logo

oasis-root

Compiled tree of Oasis Linux based on own branch at <https://hacktivis.me/git/oasis/> git clone https://anongit.hacktivis.me/git/oasis-root.git

iomenu.py (15732B)


  1. import io
  2. import os
  3. import shlex
  4. import sys
  5. import tempfile
  6. import tokenize
  7. from tkinter import filedialog
  8. from tkinter import messagebox
  9. from tkinter.simpledialog import askstring
  10. import idlelib
  11. from idlelib.config import idleConf
  12. encoding = 'utf-8'
  13. if sys.platform == 'win32':
  14. errors = 'surrogatepass'
  15. else:
  16. errors = 'surrogateescape'
  17. class IOBinding:
  18. # One instance per editor Window so methods know which to save, close.
  19. # Open returns focus to self.editwin if aborted.
  20. # EditorWindow.open_module, others, belong here.
  21. def __init__(self, editwin):
  22. self.editwin = editwin
  23. self.text = editwin.text
  24. self.__id_open = self.text.bind("<<open-window-from-file>>", self.open)
  25. self.__id_save = self.text.bind("<<save-window>>", self.save)
  26. self.__id_saveas = self.text.bind("<<save-window-as-file>>",
  27. self.save_as)
  28. self.__id_savecopy = self.text.bind("<<save-copy-of-window-as-file>>",
  29. self.save_a_copy)
  30. self.fileencoding = 'utf-8'
  31. self.__id_print = self.text.bind("<<print-window>>", self.print_window)
  32. def close(self):
  33. # Undo command bindings
  34. self.text.unbind("<<open-window-from-file>>", self.__id_open)
  35. self.text.unbind("<<save-window>>", self.__id_save)
  36. self.text.unbind("<<save-window-as-file>>",self.__id_saveas)
  37. self.text.unbind("<<save-copy-of-window-as-file>>", self.__id_savecopy)
  38. self.text.unbind("<<print-window>>", self.__id_print)
  39. # Break cycles
  40. self.editwin = None
  41. self.text = None
  42. self.filename_change_hook = None
  43. def get_saved(self):
  44. return self.editwin.get_saved()
  45. def set_saved(self, flag):
  46. self.editwin.set_saved(flag)
  47. def reset_undo(self):
  48. self.editwin.reset_undo()
  49. filename_change_hook = None
  50. def set_filename_change_hook(self, hook):
  51. self.filename_change_hook = hook
  52. filename = None
  53. dirname = None
  54. def set_filename(self, filename):
  55. if filename and os.path.isdir(filename):
  56. self.filename = None
  57. self.dirname = filename
  58. else:
  59. self.filename = filename
  60. self.dirname = None
  61. self.set_saved(1)
  62. if self.filename_change_hook:
  63. self.filename_change_hook()
  64. def open(self, event=None, editFile=None):
  65. flist = self.editwin.flist
  66. # Save in case parent window is closed (ie, during askopenfile()).
  67. if flist:
  68. if not editFile:
  69. filename = self.askopenfile()
  70. else:
  71. filename=editFile
  72. if filename:
  73. # If editFile is valid and already open, flist.open will
  74. # shift focus to its existing window.
  75. # If the current window exists and is a fresh unnamed,
  76. # unmodified editor window (not an interpreter shell),
  77. # pass self.loadfile to flist.open so it will load the file
  78. # in the current window (if the file is not already open)
  79. # instead of a new window.
  80. if (self.editwin and
  81. not getattr(self.editwin, 'interp', None) and
  82. not self.filename and
  83. self.get_saved()):
  84. flist.open(filename, self.loadfile)
  85. else:
  86. flist.open(filename)
  87. else:
  88. if self.text:
  89. self.text.focus_set()
  90. return "break"
  91. # Code for use outside IDLE:
  92. if self.get_saved():
  93. reply = self.maybesave()
  94. if reply == "cancel":
  95. self.text.focus_set()
  96. return "break"
  97. if not editFile:
  98. filename = self.askopenfile()
  99. else:
  100. filename=editFile
  101. if filename:
  102. self.loadfile(filename)
  103. else:
  104. self.text.focus_set()
  105. return "break"
  106. eol_convention = os.linesep # default
  107. def loadfile(self, filename):
  108. try:
  109. try:
  110. with tokenize.open(filename) as f:
  111. chars = f.read()
  112. fileencoding = f.encoding
  113. eol_convention = f.newlines
  114. converted = False
  115. except (UnicodeDecodeError, SyntaxError):
  116. # Wait for the editor window to appear
  117. self.editwin.text.update()
  118. enc = askstring(
  119. "Specify file encoding",
  120. "The file's encoding is invalid for Python 3.x.\n"
  121. "IDLE will convert it to UTF-8.\n"
  122. "What is the current encoding of the file?",
  123. initialvalue='utf-8',
  124. parent=self.editwin.text)
  125. with open(filename, encoding=enc) as f:
  126. chars = f.read()
  127. fileencoding = f.encoding
  128. eol_convention = f.newlines
  129. converted = True
  130. except OSError as err:
  131. messagebox.showerror("I/O Error", str(err), parent=self.text)
  132. return False
  133. except UnicodeDecodeError:
  134. messagebox.showerror("Decoding Error",
  135. "File %s\nFailed to Decode" % filename,
  136. parent=self.text)
  137. return False
  138. if not isinstance(eol_convention, str):
  139. # If the file does not contain line separators, it is None.
  140. # If the file contains mixed line separators, it is a tuple.
  141. if eol_convention is not None:
  142. messagebox.showwarning("Mixed Newlines",
  143. "Mixed newlines detected.\n"
  144. "The file will be changed on save.",
  145. parent=self.text)
  146. converted = True
  147. eol_convention = os.linesep # default
  148. self.text.delete("1.0", "end")
  149. self.set_filename(None)
  150. self.fileencoding = fileencoding
  151. self.eol_convention = eol_convention
  152. self.text.insert("1.0", chars)
  153. self.reset_undo()
  154. self.set_filename(filename)
  155. if converted:
  156. # We need to save the conversion results first
  157. # before being able to execute the code
  158. self.set_saved(False)
  159. self.text.mark_set("insert", "1.0")
  160. self.text.yview("insert")
  161. self.updaterecentfileslist(filename)
  162. return True
  163. def maybesave(self):
  164. if self.get_saved():
  165. return "yes"
  166. message = "Do you want to save %s before closing?" % (
  167. self.filename or "this untitled document")
  168. confirm = messagebox.askyesnocancel(
  169. title="Save On Close",
  170. message=message,
  171. default=messagebox.YES,
  172. parent=self.text)
  173. if confirm:
  174. reply = "yes"
  175. self.save(None)
  176. if not self.get_saved():
  177. reply = "cancel"
  178. elif confirm is None:
  179. reply = "cancel"
  180. else:
  181. reply = "no"
  182. self.text.focus_set()
  183. return reply
  184. def save(self, event):
  185. if not self.filename:
  186. self.save_as(event)
  187. else:
  188. if self.writefile(self.filename):
  189. self.set_saved(True)
  190. try:
  191. self.editwin.store_file_breaks()
  192. except AttributeError: # may be a PyShell
  193. pass
  194. self.text.focus_set()
  195. return "break"
  196. def save_as(self, event):
  197. filename = self.asksavefile()
  198. if filename:
  199. if self.writefile(filename):
  200. self.set_filename(filename)
  201. self.set_saved(1)
  202. try:
  203. self.editwin.store_file_breaks()
  204. except AttributeError:
  205. pass
  206. self.text.focus_set()
  207. self.updaterecentfileslist(filename)
  208. return "break"
  209. def save_a_copy(self, event):
  210. filename = self.asksavefile()
  211. if filename:
  212. self.writefile(filename)
  213. self.text.focus_set()
  214. self.updaterecentfileslist(filename)
  215. return "break"
  216. def writefile(self, filename):
  217. text = self.fixnewlines()
  218. chars = self.encode(text)
  219. try:
  220. with open(filename, "wb") as f:
  221. f.write(chars)
  222. f.flush()
  223. os.fsync(f.fileno())
  224. return True
  225. except OSError as msg:
  226. messagebox.showerror("I/O Error", str(msg),
  227. parent=self.text)
  228. return False
  229. def fixnewlines(self):
  230. "Return text with final \n if needed and os eols."
  231. if (self.text.get("end-2c") != '\n'
  232. and not hasattr(self.editwin, "interp")): # Not shell.
  233. self.text.insert("end-1c", "\n")
  234. text = self.text.get("1.0", "end-1c")
  235. if self.eol_convention != "\n":
  236. text = text.replace("\n", self.eol_convention)
  237. return text
  238. def encode(self, chars):
  239. if isinstance(chars, bytes):
  240. # This is either plain ASCII, or Tk was returning mixed-encoding
  241. # text to us. Don't try to guess further.
  242. return chars
  243. # Preserve a BOM that might have been present on opening
  244. if self.fileencoding == 'utf-8-sig':
  245. return chars.encode('utf-8-sig')
  246. # See whether there is anything non-ASCII in it.
  247. # If not, no need to figure out the encoding.
  248. try:
  249. return chars.encode('ascii')
  250. except UnicodeEncodeError:
  251. pass
  252. # Check if there is an encoding declared
  253. try:
  254. encoded = chars.encode('ascii', 'replace')
  255. enc, _ = tokenize.detect_encoding(io.BytesIO(encoded).readline)
  256. return chars.encode(enc)
  257. except SyntaxError as err:
  258. failed = str(err)
  259. except UnicodeEncodeError:
  260. failed = "Invalid encoding '%s'" % enc
  261. messagebox.showerror(
  262. "I/O Error",
  263. "%s.\nSaving as UTF-8" % failed,
  264. parent=self.text)
  265. # Fallback: save as UTF-8, with BOM - ignoring the incorrect
  266. # declared encoding
  267. return chars.encode('utf-8-sig')
  268. def print_window(self, event):
  269. confirm = messagebox.askokcancel(
  270. title="Print",
  271. message="Print to Default Printer",
  272. default=messagebox.OK,
  273. parent=self.text)
  274. if not confirm:
  275. self.text.focus_set()
  276. return "break"
  277. tempfilename = None
  278. saved = self.get_saved()
  279. if saved:
  280. filename = self.filename
  281. # shell undo is reset after every prompt, looks saved, probably isn't
  282. if not saved or filename is None:
  283. (tfd, tempfilename) = tempfile.mkstemp(prefix='IDLE_tmp_')
  284. filename = tempfilename
  285. os.close(tfd)
  286. if not self.writefile(tempfilename):
  287. os.unlink(tempfilename)
  288. return "break"
  289. platform = os.name
  290. printPlatform = True
  291. if platform == 'posix': #posix platform
  292. command = idleConf.GetOption('main','General',
  293. 'print-command-posix')
  294. command = command + " 2>&1"
  295. elif platform == 'nt': #win32 platform
  296. command = idleConf.GetOption('main','General','print-command-win')
  297. else: #no printing for this platform
  298. printPlatform = False
  299. if printPlatform: #we can try to print for this platform
  300. command = command % shlex.quote(filename)
  301. pipe = os.popen(command, "r")
  302. # things can get ugly on NT if there is no printer available.
  303. output = pipe.read().strip()
  304. status = pipe.close()
  305. if status:
  306. output = "Printing failed (exit status 0x%x)\n" % \
  307. status + output
  308. if output:
  309. output = "Printing command: %s\n" % repr(command) + output
  310. messagebox.showerror("Print status", output, parent=self.text)
  311. else: #no printing for this platform
  312. message = "Printing is not enabled for this platform: %s" % platform
  313. messagebox.showinfo("Print status", message, parent=self.text)
  314. if tempfilename:
  315. os.unlink(tempfilename)
  316. return "break"
  317. opendialog = None
  318. savedialog = None
  319. filetypes = (
  320. ("Python files", "*.py *.pyw", "TEXT"),
  321. ("Text files", "*.txt", "TEXT"),
  322. ("All files", "*"),
  323. )
  324. defaultextension = '.py' if sys.platform == 'darwin' else ''
  325. def askopenfile(self):
  326. dir, base = self.defaultfilename("open")
  327. if not self.opendialog:
  328. self.opendialog = filedialog.Open(parent=self.text,
  329. filetypes=self.filetypes)
  330. filename = self.opendialog.show(initialdir=dir, initialfile=base)
  331. return filename
  332. def defaultfilename(self, mode="open"):
  333. if self.filename:
  334. return os.path.split(self.filename)
  335. elif self.dirname:
  336. return self.dirname, ""
  337. else:
  338. try:
  339. pwd = os.getcwd()
  340. except OSError:
  341. pwd = ""
  342. return pwd, ""
  343. def asksavefile(self):
  344. dir, base = self.defaultfilename("save")
  345. if not self.savedialog:
  346. self.savedialog = filedialog.SaveAs(
  347. parent=self.text,
  348. filetypes=self.filetypes,
  349. defaultextension=self.defaultextension)
  350. filename = self.savedialog.show(initialdir=dir, initialfile=base)
  351. return filename
  352. def updaterecentfileslist(self,filename):
  353. "Update recent file list on all editor windows"
  354. if self.editwin.flist:
  355. self.editwin.update_recent_files_list(filename)
  356. def _io_binding(parent): # htest #
  357. from tkinter import Toplevel, Text
  358. root = Toplevel(parent)
  359. root.title("Test IOBinding")
  360. x, y = map(int, parent.geometry().split('+')[1:])
  361. root.geometry("+%d+%d" % (x, y + 175))
  362. class MyEditWin:
  363. def __init__(self, text):
  364. self.text = text
  365. self.flist = None
  366. self.text.bind("<Control-o>", self.open)
  367. self.text.bind('<Control-p>', self.print)
  368. self.text.bind("<Control-s>", self.save)
  369. self.text.bind("<Alt-s>", self.saveas)
  370. self.text.bind('<Control-c>', self.savecopy)
  371. def get_saved(self): return 0
  372. def set_saved(self, flag): pass
  373. def reset_undo(self): pass
  374. def open(self, event):
  375. self.text.event_generate("<<open-window-from-file>>")
  376. def print(self, event):
  377. self.text.event_generate("<<print-window>>")
  378. def save(self, event):
  379. self.text.event_generate("<<save-window>>")
  380. def saveas(self, event):
  381. self.text.event_generate("<<save-window-as-file>>")
  382. def savecopy(self, event):
  383. self.text.event_generate("<<save-copy-of-window-as-file>>")
  384. text = Text(root)
  385. text.pack()
  386. text.focus_set()
  387. editwin = MyEditWin(text)
  388. IOBinding(editwin)
  389. if __name__ == "__main__":
  390. from unittest import main
  391. main('idlelib.idle_test.test_iomenu', verbosity=2, exit=False)
  392. from idlelib.idle_test.htest import run
  393. run(_io_binding)