1 # 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.
17 from configHandler
import idleConf
20 from codecs
import BOM_UTF8
22 # only available since Python 2.3
23 BOM_UTF8
= '\xef\xbb\xbf'
25 # Try setting the locale, so that we can find out
26 # what encoding to use
29 locale
.setlocale(locale
.LC_CTYPE
, "")
34 if 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
38 encoding
= locale
.getdefaultlocale()[1]
39 codecs
.lookup(encoding
)
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
)
50 # situation occurs on Mac OS X
52 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.
58 encoding
= locale
.getdefaultlocale()[1]
60 # situation occurs on Mac OS X
62 codecs
.lookup(encoding
)
63 except (ValueError, LookupError):
66 encoding
= encoding
.lower()
68 coding_re
= re
.compile("coding[:=]\s*([-\w_.]+)")
71 """Return the encoding declaration according to PEP 263.
73 Raise LookupError if the encoding is declared but unknown.
75 # Only consider the first two lines
76 str = str.split("\n")[:2]
79 match
= coding_re
.search(str)
83 # Check whether the encoding is known
88 # The standard encoding error does not indicate the encoding
89 raise LookupError, "Unknown encoding "+name
95 def __init__(self
, editwin
):
96 self
.editwin
= editwin
97 self
.text
= editwin
.text
98 self
.__id
_open
= self
.text
.bind("<<open-window-from-file>>", self
.open)
99 self
.__id
_save
= self
.text
.bind("<<save-window>>", self
.save
)
100 self
.__id
_saveas
= self
.text
.bind("<<save-window-as-file>>",
102 self
.__id
_savecopy
= self
.text
.bind("<<save-copy-of-window-as-file>>",
104 self
.fileencoding
= None
105 self
.__id
_print
= self
.text
.bind("<<print-window>>", self
.print_window
)
108 # Undo command bindings
109 self
.text
.unbind("<<open-window-from-file>>", self
.__id
_open
)
110 self
.text
.unbind("<<save-window>>", self
.__id
_save
)
111 self
.text
.unbind("<<save-window-as-file>>",self
.__id
_saveas
)
112 self
.text
.unbind("<<save-copy-of-window-as-file>>", self
.__id
_savecopy
)
113 self
.text
.unbind("<<print-window>>", self
.__id
_print
)
117 self
.filename_change_hook
= None
120 return self
.editwin
.get_saved()
122 def set_saved(self
, flag
):
123 self
.editwin
.set_saved(flag
)
125 def reset_undo(self
):
126 self
.editwin
.reset_undo()
128 filename_change_hook
= None
130 def set_filename_change_hook(self
, hook
):
131 self
.filename_change_hook
= hook
135 def set_filename(self
, filename
):
136 self
.filename
= filename
138 if self
.filename_change_hook
:
139 self
.filename_change_hook()
141 def open(self
, event
=None, editFile
=None):
142 if self
.editwin
.flist
:
144 filename
= self
.askopenfile()
148 # If the current window has no filename and hasn't been
149 # modified, we replace its contents (no loss). Otherwise
150 # we open a new window. But we won't replace the
151 # shell window (which has an interp(reter) attribute), which
152 # gets set to "not modified" at every new prompt.
154 interp
= self
.editwin
.interp
157 if not self
.filename
and self
.get_saved() and not interp
:
158 self
.editwin
.flist
.open(filename
, self
.loadfile
)
160 self
.editwin
.flist
.open(filename
)
162 self
.text
.focus_set()
165 # Code for use outside IDLE:
167 reply
= self
.maybesave()
168 if reply
== "cancel":
169 self
.text
.focus_set()
172 filename
= self
.askopenfile()
176 self
.loadfile(filename
)
178 self
.text
.focus_set()
181 def loadfile(self
, filename
):
183 # open the file in binary mode so that we can handle
184 # end-of-line convention ourselves.
185 f
= open(filename
,'rb')
189 tkMessageBox
.showerror("I/O Error", str(msg
), master
=self
.text
)
192 chars
= self
.decode(chars
)
193 # We now convert all end-of-lines to '\n's
194 eol
= r
"(\r\n)|\n|\r" # \r\n (Windows), \n (UNIX), or \r (Mac)
195 chars
= re
.compile( eol
).sub( r
"\n", chars
)
197 self
.text
.delete("1.0", "end")
198 self
.set_filename(None)
199 self
.text
.insert("1.0", chars
)
201 self
.set_filename(filename
)
202 self
.text
.mark_set("insert", "1.0")
203 self
.text
.see("insert")
204 self
.updaterecentfileslist(filename
)
207 def decode(self
, chars
):
208 """Create a Unicode string
210 If that fails, let Tcl try its best
212 # Check presence of a UTF-8 signature first
213 if chars
.startswith(BOM_UTF8
):
215 chars
= chars
[3:].decode("utf-8")
217 # has UTF-8 signature, but fails to decode...
220 # Indicates that this file originally had a BOM
221 self
.fileencoding
= BOM_UTF8
223 # Next look for coding specification
225 enc
= coding_spec(chars
)
226 except LookupError, name
:
227 tkMessageBox
.showerror(
228 title
="Error loading the file",
229 message
="The encoding '%s' is not known to this Python "\
230 "installation. The file may not display correctly" % name
,
235 return unicode(chars
, enc
)
238 # If it is ASCII, we need not to record anything
240 return unicode(chars
, 'ascii')
243 # Finally, try the locale's encoding. This is deprecated;
244 # the user should declare a non-ASCII encoding
246 chars
= unicode(chars
, encoding
)
247 self
.fileencoding
= encoding
255 message
= "Do you want to save %s before closing?" % (
256 self
.filename
or "this untitled document")
257 m
= tkMessageBox
.Message(
258 title
="Save On Close",
260 icon
=tkMessageBox
.QUESTION
,
261 type=tkMessageBox
.YESNOCANCEL
,
266 if not self
.get_saved():
268 self
.text
.focus_set()
271 def save(self
, event
):
272 if not self
.filename
:
275 if self
.writefile(self
.filename
):
277 self
.editwin
.store_file_breaks()
278 self
.text
.focus_set()
281 def save_as(self
, event
):
282 filename
= self
.asksavefile()
284 if self
.writefile(filename
):
285 self
.set_filename(filename
)
287 self
.editwin
.store_file_breaks()
288 self
.text
.focus_set()
289 self
.updaterecentfileslist(filename
)
292 def save_a_copy(self
, event
):
293 filename
= self
.asksavefile()
295 self
.writefile(filename
)
296 self
.text
.focus_set()
297 self
.updaterecentfileslist(filename
)
300 def writefile(self
, filename
):
302 chars
= self
.encode(self
.text
.get("1.0", "end-1c"))
304 f
= open(filename
, "w")
309 tkMessageBox
.showerror("I/O Error", str(msg
),
313 def encode(self
, chars
):
314 if isinstance(chars
, types
.StringType
):
315 # This is either plain ASCII, or Tk was returning mixed-encoding
316 # text to us. Don't try to guess further.
318 # See whether there is anything non-ASCII in it.
319 # If not, no need to figure out the encoding.
321 return chars
.encode('ascii')
324 # If there is an encoding declared, try this first.
326 enc
= coding_spec(chars
)
328 except LookupError, msg
:
333 return chars
.encode(enc
)
335 failed
= "Invalid encoding '%s'" % enc
337 tkMessageBox
.showerror(
339 "%s. Saving as UTF-8" % failed
,
341 # If there was a UTF-8 signature, use that. This should not fail
342 if self
.fileencoding
== BOM_UTF8
or failed
:
343 return BOM_UTF8
+ chars
.encode("utf-8")
344 # Try the original file encoding next, if any
345 if self
.fileencoding
:
347 return chars
.encode(self
.fileencoding
)
349 tkMessageBox
.showerror(
351 "Cannot save this as '%s' anymore. Saving as UTF-8" \
354 return BOM_UTF8
+ chars
.encode("utf-8")
355 # Nothing was declared, and we had not determined an encoding
356 # on loading. Recommend an encoding line.
358 chars
= chars
.encode(encoding
)
361 chars
= BOM_UTF8
+ chars
.encode("utf-8")
363 tkMessageBox
.showerror(
365 "Non-ASCII found, yet no encoding declared. Add a line like\n"
366 "# -*- coding: %s -*- \nto your file" % enc
,
370 def fixlastline(self
):
371 c
= self
.text
.get("end-2c")
373 self
.text
.insert("end-1c", "\n")
375 def print_window(self
, event
):
378 filename
= self
.filename
380 filename
= tempfilename
= tempfile
.mktemp()
381 if not self
.writefile(filename
):
382 os
.unlink(tempfilename
)
386 if platform
== 'posix': #posix platform
387 command
= idleConf
.GetOption('main','General',
388 'print-command-posix')
389 command
= command
+ " 2>&1"
390 elif platform
== 'nt': #win32 platform
391 command
= idleConf
.GetOption('main','General','print-command-win')
392 else: #no printing for this platform
394 if printPlatform
: #we can try to print for this platform
395 command
= command
% filename
396 pipe
= os
.popen(command
, "r")
397 output
= pipe
.read().strip()
398 status
= pipe
.close()
400 output
= "Printing failed (exit status 0x%x)\n" % \
403 output
= "Printing command: %s\n" % repr(command
) + output
404 tkMessageBox
.showerror("Print status", output
, master
=self
.text
)
405 else: #no printing for this platform
406 message
="Printing is not enabled for this platform: %s" % platform
407 tkMessageBox
.showinfo("Print status", message
, master
=self
.text
)
414 ("Python and text files", "*.py *.pyw *.txt", "TEXT"),
415 ("All text files", "*", "TEXT"),
419 def askopenfile(self
):
420 dir, base
= self
.defaultfilename("open")
421 if not self
.opendialog
:
422 self
.opendialog
= tkFileDialog
.Open(master
=self
.text
,
423 filetypes
=self
.filetypes
)
424 return self
.opendialog
.show(initialdir
=dir, initialfile
=base
)
426 def defaultfilename(self
, mode
="open"):
428 return os
.path
.split(self
.filename
)
436 def asksavefile(self
):
437 dir, base
= self
.defaultfilename("save")
438 if not self
.savedialog
:
439 self
.savedialog
= tkFileDialog
.SaveAs(master
=self
.text
,
440 filetypes
=self
.filetypes
)
441 return self
.savedialog
.show(initialdir
=dir, initialfile
=base
)
443 def updaterecentfileslist(self
,filename
):
444 "Update recent file list on all editor windows"
445 self
.editwin
.UpdateRecentFilesList(filename
)
450 def __init__(self
, text
):
453 self
.text
.bind("<Control-o>", self
.open)
454 self
.text
.bind("<Control-s>", self
.save
)
455 self
.text
.bind("<Alt-s>", self
.save_as
)
456 self
.text
.bind("<Alt-z>", self
.save_a_copy
)
457 def get_saved(self
): return 0
458 def set_saved(self
, flag
): pass
459 def reset_undo(self
): pass
460 def open(self
, event
):
461 self
.text
.event_generate("<<open-window-from-file>>")
462 def save(self
, event
):
463 self
.text
.event_generate("<<save-window>>")
464 def save_as(self
, event
):
465 self
.text
.event_generate("<<save-window-as-file>>")
466 def save_a_copy(self
, event
):
467 self
.text
.event_generate("<<save-copy-of-window-as-file>>")
471 editwin
= MyEditWin(text
)
472 io
= IOBinding(editwin
)
475 if __name__
== "__main__":
476 from Tkinter
import *