blob: ef40715d2254288ae2f6ae76a247bdde71cf20fe [file] [log] [blame]
David Scherer7aced172000-08-15 01:13:23 +00001# changes by dscherer@cmu.edu
2# - IOBinding.open() replaces the current window with the opened file,
3# if the current window is both unmodified and unnamed
4# - IOBinding.loadfile() interprets Windows, UNIX, and Macintosh
5# end-of-line conventions, instead of relying on the standard library,
6# which will only understand the local convention.
7
Terry Jan Reedyfaaf16b2015-10-30 02:47:01 -04008import codecs
9from codecs import BOM_UTF8
David Scherer7aced172000-08-15 01:13:23 +000010import os
Serhiy Storchakaeebcb5f2013-01-12 18:12:27 +020011import pipes
Terry Jan Reedyfaaf16b2015-10-30 02:47:01 -040012import re
Kurt B. Kaiser01166da2002-09-16 22:03:37 +000013import sys
Steven M. Gava7981ce52002-06-11 04:45:34 +000014import tempfile
Terry Jan Reedyfaaf16b2015-10-30 02:47:01 -040015
Georg Brandl6634bf22008-05-20 07:13:37 +000016import tkFileDialog
17import tkMessageBox
Georg Brandl6634bf22008-05-20 07:13:37 +000018from SimpleDialog import SimpleDialog
Kurt B. Kaiserbfed3462002-12-14 04:38:51 +000019
Terry Jan Reedya944ac32016-01-27 11:51:45 -050020from idlelib.configHandler import idleConf
21
Kurt B. Kaiser01166da2002-09-16 22:03:37 +000022# Try setting the locale, so that we can find out
23# what encoding to use
24try:
25 import locale
26 locale.setlocale(locale.LC_CTYPE, "")
Kurt B. Kaiser188e25f2003-11-25 05:01:00 +000027except (ImportError, locale.Error):
Kurt B. Kaiser01166da2002-09-16 22:03:37 +000028 pass
29
Martin v. Löwis307021f2005-11-27 16:59:04 +000030# Encoding for file names
31filesystemencoding = sys.getfilesystemencoding()
32
Kurt B. Kaiser01166da2002-09-16 22:03:37 +000033encoding = "ascii"
34if sys.platform == 'win32':
35 # On Windows, we could use "mbcs". However, to give the user
36 # a portable encoding name, we need to find the code page
37 try:
38 encoding = locale.getdefaultlocale()[1]
39 codecs.lookup(encoding)
40 except LookupError:
41 pass
42else:
43 try:
44 # Different things can fail here: the locale module may not be
45 # loaded, it may not offer nl_langinfo, or CODESET, or the
46 # resulting codeset may be unknown to Python. We ignore all
47 # these problems, falling back to ASCII
48 encoding = locale.nl_langinfo(locale.CODESET)
Martin v. Löwisa9170c72004-08-12 13:14:52 +000049 if encoding is None or encoding is '':
Tony Lowndse555fc72002-09-23 01:01:20 +000050 # situation occurs on Mac OS X
51 encoding = 'ascii'
Kurt B. Kaiser01166da2002-09-16 22:03:37 +000052 codecs.lookup(encoding)
53 except (NameError, AttributeError, LookupError):
54 # Try getdefaultlocale well: it parses environment variables,
55 # which may give a clue. Unfortunately, getdefaultlocale has
56 # bugs that can cause ValueError.
57 try:
58 encoding = locale.getdefaultlocale()[1]
Martin v. Löwisa9170c72004-08-12 13:14:52 +000059 if encoding is None or encoding is '':
Tony Lowndse555fc72002-09-23 01:01:20 +000060 # situation occurs on Mac OS X
61 encoding = 'ascii'
Kurt B. Kaiser01166da2002-09-16 22:03:37 +000062 codecs.lookup(encoding)
63 except (ValueError, LookupError):
64 pass
65
66encoding = encoding.lower()
67
Serhiy Storchakae4818f62013-09-17 10:09:08 +030068coding_re = re.compile(r'^[ \t\f]*#.*coding[:=][ \t]*([-\w.]+)')
Serhiy Storchaka3eb554f2014-09-05 10:22:05 +030069blank_re = re.compile(r'^[ \t\f]*(?:[#\r\n]|$)')
Kurt B. Kaiserbfed3462002-12-14 04:38:51 +000070
Kurt B. Kaisera053f332003-05-10 00:49:56 +000071class EncodingMessage(SimpleDialog):
72 "Inform user that an encoding declaration is needed."
73 def __init__(self, master, enc):
74 self.should_edit = False
Kurt B. Kaiser47674012003-05-18 02:24:32 +000075
Kurt B. Kaisera053f332003-05-10 00:49:56 +000076 self.root = top = Toplevel(master)
77 top.bind("<Return>", self.return_event)
78 top.bind("<Escape>", self.do_ok)
79 top.protocol("WM_DELETE_WINDOW", self.wm_delete_window)
80 top.wm_title("I/O Warning")
81 top.wm_iconname("I/O Warning")
82 self.top = top
83
84 l1 = Label(top,
85 text="Non-ASCII found, yet no encoding declared. Add a line like")
86 l1.pack(side=TOP, anchor=W)
87 l2 = Entry(top, font="courier")
88 l2.insert(0, "# -*- coding: %s -*-" % enc)
89 # For some reason, the text is not selectable anymore if the
90 # widget is disabled.
91 # l2['state'] = DISABLED
92 l2.pack(side=TOP, anchor = W, fill=X)
93 l3 = Label(top, text="to your file\n"
94 "Choose OK to save this file as %s\n"
95 "Edit your general options to silence this warning" % enc)
96 l3.pack(side=TOP, anchor = W)
97
98 buttons = Frame(top)
99 buttons.pack(side=TOP, fill=X)
100 # Both return and cancel mean the same thing: do nothing
101 self.default = self.cancel = 0
102 b1 = Button(buttons, text="Ok", default="active",
103 command=self.do_ok)
104 b1.pack(side=LEFT, fill=BOTH, expand=1)
105 b2 = Button(buttons, text="Edit my file",
106 command=self.do_edit)
107 b2.pack(side=LEFT, fill=BOTH, expand=1)
Kurt B. Kaiser47674012003-05-18 02:24:32 +0000108
Kurt B. Kaisera053f332003-05-10 00:49:56 +0000109 self._set_transient(master)
110
111 def do_ok(self):
112 self.done(0)
113
114 def do_edit(self):
115 self.done(1)
116
Kurt B. Kaiser01166da2002-09-16 22:03:37 +0000117def coding_spec(str):
Kurt B. Kaiser01166da2002-09-16 22:03:37 +0000118 """Return the encoding declaration according to PEP 263.
Kurt B. Kaiser01166da2002-09-16 22:03:37 +0000119
Kurt B. Kaiserbfed3462002-12-14 04:38:51 +0000120 Raise LookupError if the encoding is declared but unknown.
121 """
Kurt B. Kaiser01166da2002-09-16 22:03:37 +0000122 # Only consider the first two lines
Serhiy Storchaka1c760ca2013-10-29 10:15:09 +0200123 lst = str.split("\n", 2)[:2]
Serhiy Storchakae787bce2013-09-17 00:00:46 +0300124 for line in lst:
125 match = coding_re.match(line)
126 if match is not None:
127 break
Serhiy Storchaka3eb554f2014-09-05 10:22:05 +0300128 if not blank_re.match(line):
129 return None
Serhiy Storchakae787bce2013-09-17 00:00:46 +0300130 else:
Kurt B. Kaiser01166da2002-09-16 22:03:37 +0000131 return None
132 name = match.group(1)
133 # Check whether the encoding is known
134 import codecs
135 try:
136 codecs.lookup(name)
137 except LookupError:
138 # The standard encoding error does not indicate the encoding
139 raise LookupError, "Unknown encoding "+name
140 return name
David Scherer7aced172000-08-15 01:13:23 +0000141
142class IOBinding:
143
144 def __init__(self, editwin):
145 self.editwin = editwin
146 self.text = editwin.text
147 self.__id_open = self.text.bind("<<open-window-from-file>>", self.open)
148 self.__id_save = self.text.bind("<<save-window>>", self.save)
149 self.__id_saveas = self.text.bind("<<save-window-as-file>>",
150 self.save_as)
151 self.__id_savecopy = self.text.bind("<<save-copy-of-window-as-file>>",
152 self.save_a_copy)
Kurt B. Kaiser01166da2002-09-16 22:03:37 +0000153 self.fileencoding = None
Steven M. Gava7981ce52002-06-11 04:45:34 +0000154 self.__id_print = self.text.bind("<<print-window>>", self.print_window)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000155
David Scherer7aced172000-08-15 01:13:23 +0000156 def close(self):
157 # Undo command bindings
158 self.text.unbind("<<open-window-from-file>>", self.__id_open)
159 self.text.unbind("<<save-window>>", self.__id_save)
160 self.text.unbind("<<save-window-as-file>>",self.__id_saveas)
161 self.text.unbind("<<save-copy-of-window-as-file>>", self.__id_savecopy)
Steven M. Gava7981ce52002-06-11 04:45:34 +0000162 self.text.unbind("<<print-window>>", self.__id_print)
David Scherer7aced172000-08-15 01:13:23 +0000163 # Break cycles
164 self.editwin = None
165 self.text = None
166 self.filename_change_hook = None
167
168 def get_saved(self):
169 return self.editwin.get_saved()
170
171 def set_saved(self, flag):
172 self.editwin.set_saved(flag)
173
174 def reset_undo(self):
175 self.editwin.reset_undo()
176
177 filename_change_hook = None
178
179 def set_filename_change_hook(self, hook):
180 self.filename_change_hook = hook
181
182 filename = None
Kurt B. Kaiserd2f48612003-06-05 02:34:04 +0000183 dirname = None
David Scherer7aced172000-08-15 01:13:23 +0000184
185 def set_filename(self, filename):
Kurt B. Kaiserd2f48612003-06-05 02:34:04 +0000186 if filename and os.path.isdir(filename):
187 self.filename = None
188 self.dirname = filename
189 else:
190 self.filename = filename
191 self.dirname = None
192 self.set_saved(1)
193 if self.filename_change_hook:
194 self.filename_change_hook()
David Scherer7aced172000-08-15 01:13:23 +0000195
Steven M. Gava1d46e402002-03-27 08:40:46 +0000196 def open(self, event=None, editFile=None):
Terry Jan Reedyeaa7e782012-05-26 20:33:32 -0400197 flist = self.editwin.flist
Terry Jan Reedyf0775132012-06-02 20:22:35 -0400198 # Save in case parent window is closed (ie, during askopenfile()).
Terry Jan Reedyeaa7e782012-05-26 20:33:32 -0400199 if flist:
Steven M. Gava1d46e402002-03-27 08:40:46 +0000200 if not editFile:
201 filename = self.askopenfile()
202 else:
203 filename=editFile
David Scherer7aced172000-08-15 01:13:23 +0000204 if filename:
Terry Jan Reedyf0775132012-06-02 20:22:35 -0400205 # If editFile is valid and already open, flist.open will
206 # shift focus to its existing window.
207 # If the current window exists and is a fresh unnamed,
208 # unmodified editor window (not an interpreter shell),
209 # pass self.loadfile to flist.open so it will load the file
210 # in the current window (if the file is not already open)
211 # instead of a new window.
212 if (self.editwin and
213 not getattr(self.editwin, 'interp', None) and
214 not self.filename and
215 self.get_saved()):
Terry Jan Reedyeaa7e782012-05-26 20:33:32 -0400216 flist.open(filename, self.loadfile)
David Scherer7aced172000-08-15 01:13:23 +0000217 else:
Terry Jan Reedyeaa7e782012-05-26 20:33:32 -0400218 flist.open(filename)
David Scherer7aced172000-08-15 01:13:23 +0000219 else:
Terry Jan Reedyeaa7e782012-05-26 20:33:32 -0400220 if self.text:
221 self.text.focus_set()
David Scherer7aced172000-08-15 01:13:23 +0000222 return "break"
Terry Jan Reedyf0775132012-06-02 20:22:35 -0400223
David Scherer7aced172000-08-15 01:13:23 +0000224 # Code for use outside IDLE:
225 if self.get_saved():
226 reply = self.maybesave()
227 if reply == "cancel":
228 self.text.focus_set()
229 return "break"
Steven M. Gava1d46e402002-03-27 08:40:46 +0000230 if not editFile:
231 filename = self.askopenfile()
232 else:
233 filename=editFile
David Scherer7aced172000-08-15 01:13:23 +0000234 if filename:
235 self.loadfile(filename)
236 else:
237 self.text.focus_set()
238 return "break"
239
Guido van Rossumc2f77dd2003-04-25 18:36:31 +0000240 eol = r"(\r\n)|\n|\r" # \r\n (Windows), \n (UNIX), or \r (Mac)
241 eol_re = re.compile(eol)
242 eol_convention = os.linesep # Default
243
David Scherer7aced172000-08-15 01:13:23 +0000244 def loadfile(self, filename):
245 try:
246 # open the file in binary mode so that we can handle
247 # end-of-line convention ourselves.
Terry Jan Reedyf9489432013-08-04 15:39:56 -0400248 with open(filename, 'rb') as f:
249 chars = f.read()
Terry Jan Reedy2b149862013-06-29 00:59:34 -0400250 except IOError as msg:
Terry Jan Reedy8bfacc72015-09-25 22:22:48 -0400251 tkMessageBox.showerror("I/O Error", str(msg), parent=self.text)
Kurt B. Kaiser01166da2002-09-16 22:03:37 +0000252 return False
David Scherer7aced172000-08-15 01:13:23 +0000253
Kurt B. Kaiser01166da2002-09-16 22:03:37 +0000254 chars = self.decode(chars)
David Scherer7aced172000-08-15 01:13:23 +0000255 # We now convert all end-of-lines to '\n's
Guido van Rossumc2f77dd2003-04-25 18:36:31 +0000256 firsteol = self.eol_re.search(chars)
257 if firsteol:
258 self.eol_convention = firsteol.group(0)
Martin v. Löwis249d50a2003-08-05 05:51:20 +0000259 if isinstance(self.eol_convention, unicode):
260 # Make sure it is an ASCII string
261 self.eol_convention = self.eol_convention.encode("ascii")
Guido van Rossumc2f77dd2003-04-25 18:36:31 +0000262 chars = self.eol_re.sub(r"\n", chars)
David Scherer7aced172000-08-15 01:13:23 +0000263
264 self.text.delete("1.0", "end")
265 self.set_filename(None)
266 self.text.insert("1.0", chars)
267 self.reset_undo()
268 self.set_filename(filename)
269 self.text.mark_set("insert", "1.0")
Ned Deilyd8b17232011-07-26 18:16:08 -0700270 self.text.yview("insert")
Chui Tey993e81a2002-11-04 03:11:10 +0000271 self.updaterecentfileslist(filename)
Kurt B. Kaiser01166da2002-09-16 22:03:37 +0000272 return True
273
274 def decode(self, chars):
Kurt B. Kaiserbfed3462002-12-14 04:38:51 +0000275 """Create a Unicode string
Kurt B. Kaiser01166da2002-09-16 22:03:37 +0000276
Kurt B. Kaiserbfed3462002-12-14 04:38:51 +0000277 If that fails, let Tcl try its best
278 """
Kurt B. Kaiser01166da2002-09-16 22:03:37 +0000279 # Check presence of a UTF-8 signature first
280 if chars.startswith(BOM_UTF8):
281 try:
282 chars = chars[3:].decode("utf-8")
283 except UnicodeError:
284 # has UTF-8 signature, but fails to decode...
285 return chars
286 else:
287 # Indicates that this file originally had a BOM
288 self.fileencoding = BOM_UTF8
289 return chars
Kurt B. Kaiser01166da2002-09-16 22:03:37 +0000290 # Next look for coding specification
291 try:
292 enc = coding_spec(chars)
Terry Jan Reedy2b149862013-06-29 00:59:34 -0400293 except LookupError as name:
Kurt B. Kaiser01166da2002-09-16 22:03:37 +0000294 tkMessageBox.showerror(
295 title="Error loading the file",
296 message="The encoding '%s' is not known to this Python "\
297 "installation. The file may not display correctly" % name,
Terry Jan Reedy8bfacc72015-09-25 22:22:48 -0400298 parent = self.text)
Kurt B. Kaiser01166da2002-09-16 22:03:37 +0000299 enc = None
Kurt B. Kaiser01166da2002-09-16 22:03:37 +0000300 if enc:
301 try:
302 return unicode(chars, enc)
303 except UnicodeError:
304 pass
Kurt B. Kaiser01166da2002-09-16 22:03:37 +0000305 # If it is ASCII, we need not to record anything
306 try:
307 return unicode(chars, 'ascii')
308 except UnicodeError:
309 pass
Kurt B. Kaiser01166da2002-09-16 22:03:37 +0000310 # Finally, try the locale's encoding. This is deprecated;
311 # the user should declare a non-ASCII encoding
312 try:
313 chars = unicode(chars, encoding)
314 self.fileencoding = encoding
315 except UnicodeError:
316 pass
317 return chars
David Scherer7aced172000-08-15 01:13:23 +0000318
319 def maybesave(self):
320 if self.get_saved():
321 return "yes"
322 message = "Do you want to save %s before closing?" % (
323 self.filename or "this untitled document")
Kurt B. Kaiserd82a8872011-05-12 21:18:47 -0400324 confirm = tkMessageBox.askyesnocancel(
325 title="Save On Close",
326 message=message,
327 default=tkMessageBox.YES,
Terry Jan Reedy8bfacc72015-09-25 22:22:48 -0400328 parent=self.text)
Kurt B. Kaiserd82a8872011-05-12 21:18:47 -0400329 if confirm:
330 reply = "yes"
David Scherer7aced172000-08-15 01:13:23 +0000331 self.save(None)
332 if not self.get_saved():
333 reply = "cancel"
Kurt B. Kaiserd82a8872011-05-12 21:18:47 -0400334 elif confirm is None:
335 reply = "cancel"
336 else:
337 reply = "no"
David Scherer7aced172000-08-15 01:13:23 +0000338 self.text.focus_set()
339 return reply
340
341 def save(self, event):
342 if not self.filename:
343 self.save_as(event)
344 else:
345 if self.writefile(self.filename):
Kurt B. Kaiserd82a8872011-05-12 21:18:47 -0400346 self.set_saved(True)
Kurt B. Kaiserddeaf112003-03-04 04:42:04 +0000347 try:
348 self.editwin.store_file_breaks()
349 except AttributeError: # may be a PyShell
350 pass
David Scherer7aced172000-08-15 01:13:23 +0000351 self.text.focus_set()
352 return "break"
353
354 def save_as(self, event):
355 filename = self.asksavefile()
356 if filename:
357 if self.writefile(filename):
358 self.set_filename(filename)
359 self.set_saved(1)
Kurt B. Kaiserddeaf112003-03-04 04:42:04 +0000360 try:
361 self.editwin.store_file_breaks()
362 except AttributeError:
363 pass
David Scherer7aced172000-08-15 01:13:23 +0000364 self.text.focus_set()
Chui Tey993e81a2002-11-04 03:11:10 +0000365 self.updaterecentfileslist(filename)
David Scherer7aced172000-08-15 01:13:23 +0000366 return "break"
367
368 def save_a_copy(self, event):
369 filename = self.asksavefile()
370 if filename:
371 self.writefile(filename)
372 self.text.focus_set()
Chui Tey993e81a2002-11-04 03:11:10 +0000373 self.updaterecentfileslist(filename)
David Scherer7aced172000-08-15 01:13:23 +0000374 return "break"
375
376 def writefile(self, filename):
377 self.fixlastline()
Kurt B. Kaiser01166da2002-09-16 22:03:37 +0000378 chars = self.encode(self.text.get("1.0", "end-1c"))
Guido van Rossumc2f77dd2003-04-25 18:36:31 +0000379 if self.eol_convention != "\n":
380 chars = chars.replace("\n", self.eol_convention)
David Scherer7aced172000-08-15 01:13:23 +0000381 try:
Terry Jan Reedyf9489432013-08-04 15:39:56 -0400382 with open(filename, "wb") as f:
383 f.write(chars)
Kurt B. Kaiser01166da2002-09-16 22:03:37 +0000384 return True
Terry Jan Reedy2b149862013-06-29 00:59:34 -0400385 except IOError as msg:
David Scherer7aced172000-08-15 01:13:23 +0000386 tkMessageBox.showerror("I/O Error", str(msg),
Terry Jan Reedy8bfacc72015-09-25 22:22:48 -0400387 parent=self.text)
Kurt B. Kaiser01166da2002-09-16 22:03:37 +0000388 return False
389
390 def encode(self, chars):
Terry Jan Reedyd8dc7492015-05-14 18:10:30 -0400391 if isinstance(chars, str):
Kurt B. Kaiser01166da2002-09-16 22:03:37 +0000392 # This is either plain ASCII, or Tk was returning mixed-encoding
393 # text to us. Don't try to guess further.
394 return chars
Kurt B. Kaiser01166da2002-09-16 22:03:37 +0000395 # See whether there is anything non-ASCII in it.
396 # If not, no need to figure out the encoding.
397 try:
398 return chars.encode('ascii')
399 except UnicodeError:
400 pass
Kurt B. Kaiser01166da2002-09-16 22:03:37 +0000401 # If there is an encoding declared, try this first.
402 try:
403 enc = coding_spec(chars)
404 failed = None
Terry Jan Reedy2b149862013-06-29 00:59:34 -0400405 except LookupError as msg:
Kurt B. Kaiser01166da2002-09-16 22:03:37 +0000406 failed = msg
407 enc = None
408 if enc:
409 try:
410 return chars.encode(enc)
411 except UnicodeError:
412 failed = "Invalid encoding '%s'" % enc
Kurt B. Kaiser01166da2002-09-16 22:03:37 +0000413 if failed:
414 tkMessageBox.showerror(
415 "I/O Error",
416 "%s. Saving as UTF-8" % failed,
Terry Jan Reedy8bfacc72015-09-25 22:22:48 -0400417 parent = self.text)
Kurt B. Kaiser01166da2002-09-16 22:03:37 +0000418 # If there was a UTF-8 signature, use that. This should not fail
419 if self.fileencoding == BOM_UTF8 or failed:
420 return BOM_UTF8 + chars.encode("utf-8")
Kurt B. Kaiser01166da2002-09-16 22:03:37 +0000421 # Try the original file encoding next, if any
422 if self.fileencoding:
423 try:
424 return chars.encode(self.fileencoding)
425 except UnicodeError:
426 tkMessageBox.showerror(
427 "I/O Error",
428 "Cannot save this as '%s' anymore. Saving as UTF-8" \
429 % self.fileencoding,
Terry Jan Reedy8bfacc72015-09-25 22:22:48 -0400430 parent = self.text)
Kurt B. Kaiser01166da2002-09-16 22:03:37 +0000431 return BOM_UTF8 + chars.encode("utf-8")
Kurt B. Kaiser01166da2002-09-16 22:03:37 +0000432 # Nothing was declared, and we had not determined an encoding
433 # on loading. Recommend an encoding line.
Kurt B. Kaiser47674012003-05-18 02:24:32 +0000434 config_encoding = idleConf.GetOption("main","EditorWindow",
435 "encoding")
436 if config_encoding == 'utf-8':
437 # User has requested that we save files as UTF-8
438 return BOM_UTF8 + chars.encode("utf-8")
439 ask_user = True
440 try:
441 chars = chars.encode(encoding)
442 enc = encoding
443 if config_encoding == 'locale':
Kurt B. Kaisera053f332003-05-10 00:49:56 +0000444 ask_user = False
Kurt B. Kaiser47674012003-05-18 02:24:32 +0000445 except UnicodeError:
446 chars = BOM_UTF8 + chars.encode("utf-8")
447 enc = "utf-8"
448 if not ask_user:
Kurt B. Kaisera053f332003-05-10 00:49:56 +0000449 return chars
Kurt B. Kaiser47674012003-05-18 02:24:32 +0000450 dialog = EncodingMessage(self.editwin.top, enc)
451 dialog.go()
452 if dialog.num == 1:
453 # User asked us to edit the file
454 encline = "# -*- coding: %s -*-\n" % enc
455 firstline = self.text.get("1.0", "2.0")
456 if firstline.startswith("#!"):
457 # Insert encoding after #! line
458 self.text.insert("2.0", encline)
459 else:
460 self.text.insert("1.0", encline)
461 return self.encode(self.text.get("1.0", "end-1c"))
462 return chars
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000463
Kurt B. Kaiser01166da2002-09-16 22:03:37 +0000464 def fixlastline(self):
465 c = self.text.get("end-2c")
466 if c != '\n':
467 self.text.insert("end-1c", "\n")
468
Steven M. Gava7981ce52002-06-11 04:45:34 +0000469 def print_window(self, event):
Kurt B. Kaiserd82a8872011-05-12 21:18:47 -0400470 confirm = tkMessageBox.askokcancel(
471 title="Print",
472 message="Print to Default Printer",
473 default=tkMessageBox.OK,
Terry Jan Reedy8bfacc72015-09-25 22:22:48 -0400474 parent=self.text)
Kurt B. Kaiserd82a8872011-05-12 21:18:47 -0400475 if not confirm:
Kurt B. Kaiser60d58402007-10-28 19:03:59 +0000476 self.text.focus_set()
477 return "break"
Steven M. Gava7981ce52002-06-11 04:45:34 +0000478 tempfilename = None
Kurt B. Kaiser9067c8d2003-06-09 03:12:42 +0000479 saved = self.get_saved()
480 if saved:
Steven M. Gava7981ce52002-06-11 04:45:34 +0000481 filename = self.filename
Kurt B. Kaiser9067c8d2003-06-09 03:12:42 +0000482 # shell undo is reset after every prompt, looks saved, probably isn't
483 if not saved or filename is None:
Kurt B. Kaiser61e2c9a2003-06-14 17:56:25 +0000484 (tfd, tempfilename) = tempfile.mkstemp(prefix='IDLE_tmp_')
485 filename = tempfilename
486 os.close(tfd)
487 if not self.writefile(tempfilename):
Steven M. Gava7981ce52002-06-11 04:45:34 +0000488 os.unlink(tempfilename)
489 return "break"
Kurt B. Kaiserd82a8872011-05-12 21:18:47 -0400490 platform = os.name
491 printPlatform = True
Steven M. Gava7981ce52002-06-11 04:45:34 +0000492 if platform == 'posix': #posix platform
Kurt B. Kaiser01166da2002-09-16 22:03:37 +0000493 command = idleConf.GetOption('main','General',
494 'print-command-posix')
Steven M. Gava7981ce52002-06-11 04:45:34 +0000495 command = command + " 2>&1"
496 elif platform == 'nt': #win32 platform
497 command = idleConf.GetOption('main','General','print-command-win')
498 else: #no printing for this platform
Kurt B. Kaiserd82a8872011-05-12 21:18:47 -0400499 printPlatform = False
Steven M. Gava7981ce52002-06-11 04:45:34 +0000500 if printPlatform: #we can try to print for this platform
Serhiy Storchakaeebcb5f2013-01-12 18:12:27 +0200501 command = command % pipes.quote(filename)
Steven M. Gava7981ce52002-06-11 04:45:34 +0000502 pipe = os.popen(command, "r")
Kurt B. Kaiser9067c8d2003-06-09 03:12:42 +0000503 # things can get ugly on NT if there is no printer available.
Steven M. Gava7981ce52002-06-11 04:45:34 +0000504 output = pipe.read().strip()
505 status = pipe.close()
506 if status:
Kurt B. Kaiser01166da2002-09-16 22:03:37 +0000507 output = "Printing failed (exit status 0x%x)\n" % \
508 status + output
Steven M. Gava7981ce52002-06-11 04:45:34 +0000509 if output:
510 output = "Printing command: %s\n" % repr(command) + output
Terry Jan Reedy8bfacc72015-09-25 22:22:48 -0400511 tkMessageBox.showerror("Print status", output, parent=self.text)
Steven M. Gava7981ce52002-06-11 04:45:34 +0000512 else: #no printing for this platform
Kurt B. Kaiserd82a8872011-05-12 21:18:47 -0400513 message = "Printing is not enabled for this platform: %s" % platform
Terry Jan Reedy8bfacc72015-09-25 22:22:48 -0400514 tkMessageBox.showinfo("Print status", message, parent=self.text)
Kurt B. Kaiser61e2c9a2003-06-14 17:56:25 +0000515 if tempfilename:
516 os.unlink(tempfilename)
Steven M. Gava7981ce52002-06-11 04:45:34 +0000517 return "break"
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000518
David Scherer7aced172000-08-15 01:13:23 +0000519 opendialog = None
520 savedialog = None
521
522 filetypes = [
Terry Reedy277896a2010-11-23 06:44:05 +0000523 ("Python files", "*.py *.pyw", "TEXT"),
524 ("Text files", "*.txt", "TEXT"),
David Scherer7aced172000-08-15 01:13:23 +0000525 ("All files", "*"),
526 ]
527
Terry Jan Reedy57ac6232014-10-06 00:13:51 -0400528 defaultextension = '.py' if sys.platform == 'darwin' else ''
529
David Scherer7aced172000-08-15 01:13:23 +0000530 def askopenfile(self):
531 dir, base = self.defaultfilename("open")
532 if not self.opendialog:
Terry Jan Reedy8bfacc72015-09-25 22:22:48 -0400533 self.opendialog = tkFileDialog.Open(parent=self.text,
David Scherer7aced172000-08-15 01:13:23 +0000534 filetypes=self.filetypes)
Martin v. Löwis307021f2005-11-27 16:59:04 +0000535 filename = self.opendialog.show(initialdir=dir, initialfile=base)
536 if isinstance(filename, unicode):
537 filename = filename.encode(filesystemencoding)
538 return filename
David Scherer7aced172000-08-15 01:13:23 +0000539
540 def defaultfilename(self, mode="open"):
541 if self.filename:
542 return os.path.split(self.filename)
Kurt B. Kaiserd2f48612003-06-05 02:34:04 +0000543 elif self.dirname:
544 return self.dirname, ""
David Scherer7aced172000-08-15 01:13:23 +0000545 else:
546 try:
547 pwd = os.getcwd()
548 except os.error:
549 pwd = ""
550 return pwd, ""
551
552 def asksavefile(self):
553 dir, base = self.defaultfilename("save")
554 if not self.savedialog:
Terry Jan Reedy57ac6232014-10-06 00:13:51 -0400555 self.savedialog = tkFileDialog.SaveAs(
Terry Jan Reedy8bfacc72015-09-25 22:22:48 -0400556 parent=self.text,
Terry Jan Reedy57ac6232014-10-06 00:13:51 -0400557 filetypes=self.filetypes,
558 defaultextension=self.defaultextension)
Martin v. Löwis307021f2005-11-27 16:59:04 +0000559 filename = self.savedialog.show(initialdir=dir, initialfile=base)
560 if isinstance(filename, unicode):
561 filename = filename.encode(filesystemencoding)
562 return filename
David Scherer7aced172000-08-15 01:13:23 +0000563
Chui Tey993e81a2002-11-04 03:11:10 +0000564 def updaterecentfileslist(self,filename):
Kurt B. Kaiserbfed3462002-12-14 04:38:51 +0000565 "Update recent file list on all editor windows"
Kurt B. Kaisercf6f1b62004-04-11 03:16:07 +0000566 self.editwin.update_recent_files_list(filename)
Chui Tey993e81a2002-11-04 03:11:10 +0000567
Terry Jan Reedyfaaf16b2015-10-30 02:47:01 -0400568
Terry Jan Reedyd8dc7492015-05-14 18:10:30 -0400569def _io_binding(parent): # htest #
Terry Jan Reedyfaaf16b2015-10-30 02:47:01 -0400570 from Tkinter import Toplevel, Text
Terry Jan Reedyfaaf16b2015-10-30 02:47:01 -0400571
572 root = Toplevel(parent)
Terry Jan Reedy62012fc2014-05-24 18:48:03 -0400573 root.title("Test IOBinding")
574 width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
575 root.geometry("+%d+%d"%(x, y + 150))
David Scherer7aced172000-08-15 01:13:23 +0000576 class MyEditWin:
577 def __init__(self, text):
578 self.text = text
579 self.flist = None
580 self.text.bind("<Control-o>", self.open)
Terry Jan Reedya944ac32016-01-27 11:51:45 -0500581 self.text.bind('<Control-p>', self.printer)
David Scherer7aced172000-08-15 01:13:23 +0000582 self.text.bind("<Control-s>", self.save)
Terry Jan Reedya944ac32016-01-27 11:51:45 -0500583 self.text.bind("<Alt-s>", self.saveas)
584 self.text.bind('<Control-c>', self.savecopy)
David Scherer7aced172000-08-15 01:13:23 +0000585 def get_saved(self): return 0
586 def set_saved(self, flag): pass
587 def reset_undo(self): pass
Terry Jan Reedya944ac32016-01-27 11:51:45 -0500588 def update_recent_files_list(self, filename): pass
David Scherer7aced172000-08-15 01:13:23 +0000589 def open(self, event):
590 self.text.event_generate("<<open-window-from-file>>")
Terry Jan Reedya944ac32016-01-27 11:51:45 -0500591 def printer(self, event):
592 self.text.event_generate("<<print-window>>")
David Scherer7aced172000-08-15 01:13:23 +0000593 def save(self, event):
594 self.text.event_generate("<<save-window>>")
Terry Jan Reedya944ac32016-01-27 11:51:45 -0500595 def saveas(self, event):
596 self.text.event_generate("<<save-window-as-file>>")
597 def savecopy(self, event):
598 self.text.event_generate("<<save-copy-of-window-as-file>>")
Terry Jan Reedy62012fc2014-05-24 18:48:03 -0400599
David Scherer7aced172000-08-15 01:13:23 +0000600 text = Text(root)
601 text.pack()
602 text.focus_set()
603 editwin = MyEditWin(text)
Terry Jan Reedyd8dc7492015-05-14 18:10:30 -0400604 IOBinding(editwin)
David Scherer7aced172000-08-15 01:13:23 +0000605
606if __name__ == "__main__":
Terry Jan Reedy62012fc2014-05-24 18:48:03 -0400607 from idlelib.idle_test.htest import run
608 run(_io_binding)