4d67292353804edc3f1b0ccacdfd13807644e670
# changes by dscherer@cmu.edu
# - IOBinding.open() replaces the current window with the opened file,
# if the current window is both unmodified and unnamed
# - IOBinding.loadfile() interprets Windows, UNIX, and Macintosh
# end-of-line conventions, instead of relying on the standard library,
# which will only understand the local convention.
from SimpleDialog
import SimpleDialog
from configHandler
import idleConf
from codecs
import BOM_UTF8
# only available since Python 2.3
BOM_UTF8
= '\xef\xbb\xbf'
# Try setting the locale, so that we can find out
locale
.setlocale(locale
.LC_CTYPE
, "")
except (ImportError, locale
.Error
):
if sys
.platform
== 'win32':
# On Windows, we could use "mbcs". However, to give the user
# a portable encoding name, we need to find the code page
encoding
= locale
.getdefaultlocale()[1]
# Different things can fail here: the locale module may not be
# loaded, it may not offer nl_langinfo, or CODESET, or the
# resulting codeset may be unknown to Python. We ignore all
# these problems, falling back to ASCII
encoding
= locale
.nl_langinfo(locale
.CODESET
)
if encoding
is None or encoding
is '':
# situation occurs on Mac OS X
except (NameError, AttributeError, LookupError):
# Try getdefaultlocale well: it parses environment variables,
# which may give a clue. Unfortunately, getdefaultlocale has
# bugs that can cause ValueError.
encoding
= locale
.getdefaultlocale()[1]
if encoding
is None or encoding
is '':
# situation occurs on Mac OS X
except (ValueError, LookupError):
encoding
= encoding
.lower()
coding_re
= re
.compile("coding[:=]\s*([-\w_.]+)")
class EncodingMessage(SimpleDialog
):
"Inform user that an encoding declaration is needed."
def __init__(self
, master
, enc
):
self
.root
= top
= Toplevel(master
)
top
.bind("<Return>", self
.return_event
)
top
.bind("<Escape>", self
.do_ok
)
top
.protocol("WM_DELETE_WINDOW", self
.wm_delete_window
)
top
.wm_title("I/O Warning")
top
.wm_iconname("I/O Warning")
text
="Non-ASCII found, yet no encoding declared. Add a line like")
l1
.pack(side
=TOP
, anchor
=W
)
l2
= Entry(top
, font
="courier")
l2
.insert(0, "# -*- coding: %s -*-" % enc
)
# For some reason, the text is not selectable anymore if the
l2
.pack(side
=TOP
, anchor
= W
, fill
=X
)
l3
= Label(top
, text
="to your file\n"
"Choose OK to save this file as %s\n"
"Edit your general options to silence this warning" % enc
)
l3
.pack(side
=TOP
, anchor
= W
)
buttons
.pack(side
=TOP
, fill
=X
)
# Both return and cancel mean the same thing: do nothing
self
.default
= self
.cancel
= 0
b1
= Button(buttons
, text
="Ok", default
="active",
b1
.pack(side
=LEFT
, fill
=BOTH
, expand
=1)
b2
= Button(buttons
, text
="Edit my file",
b2
.pack(side
=LEFT
, fill
=BOTH
, expand
=1)
self
._set
_transient
(master
)
"""Return the encoding declaration according to PEP 263.
Raise LookupError if the encoding is declared but unknown.
# Only consider the first two lines
str = str.split("\n")[:2]
match
= coding_re
.search(str)
# Check whether the encoding is known
# The standard encoding error does not indicate the encoding
raise LookupError, "Unknown encoding "+name
def __init__(self
, editwin
):
self
.__id
_open
= self
.text
.bind("<<open-window-from-file>>", self
.open)
self
.__id
_save
= self
.text
.bind("<<save-window>>", self
.save
)
self
.__id
_saveas
= self
.text
.bind("<<save-window-as-file>>",
self
.__id
_savecopy
= self
.text
.bind("<<save-copy-of-window-as-file>>",
self
.__id
_print
= self
.text
.bind("<<print-window>>", self
.print_window
)
self
.text
.unbind("<<open-window-from-file>>", self
.__id
_open
)
self
.text
.unbind("<<save-window>>", self
.__id
_save
)
self
.text
.unbind("<<save-window-as-file>>",self
.__id
_saveas
)
self
.text
.unbind("<<save-copy-of-window-as-file>>", self
.__id
_savecopy
)
self
.text
.unbind("<<print-window>>", self
.__id
_print
)
self
.filename_change_hook
= None
return self
.editwin
.get_saved()
def set_saved(self
, flag
):
self
.editwin
.set_saved(flag
)
self
.editwin
.reset_undo()
filename_change_hook
= None
def set_filename_change_hook(self
, hook
):
self
.filename_change_hook
= hook
def set_filename(self
, filename
):
if filename
and os
.path
.isdir(filename
):
if self
.filename_change_hook
:
self
.filename_change_hook()
def open(self
, event
=None, editFile
=None):
filename
= self
.askopenfile()
# If the current window has no filename and hasn't been
# modified, we replace its contents (no loss). Otherwise
# we open a new window. But we won't replace the
# shell window (which has an interp(reter) attribute), which
# gets set to "not modified" at every new prompt.
interp
= self
.editwin
.interp
if not self
.filename
and self
.get_saved() and not interp
:
self
.editwin
.flist
.open(filename
, self
.loadfile
)
self
.editwin
.flist
.open(filename
)
# Code for use outside IDLE:
filename
= self
.askopenfile()
eol
= r
"(\r\n)|\n|\r" # \r\n (Windows), \n (UNIX), or \r (Mac)
eol_convention
= os
.linesep
# Default
def loadfile(self
, filename
):
# open the file in binary mode so that we can handle
# end-of-line convention ourselves.
tkMessageBox
.showerror("I/O Error", str(msg
), master
=self
.text
)
chars
= self
.decode(chars
)
# We now convert all end-of-lines to '\n's
firsteol
= self
.eol_re
.search(chars
)
self
.eol_convention
= firsteol
.group(0)
if isinstance(self
.eol_convention
, unicode):
# Make sure it is an ASCII string
self
.eol_convention
= self
.eol_convention
.encode("ascii")
chars
= self
.eol_re
.sub(r
"\n", chars
)
self
.text
.delete("1.0", "end")
self
.text
.insert("1.0", chars
)
self
.set_filename(filename
)
self
.text
.mark_set("insert", "1.0")
self
.updaterecentfileslist(filename
)
"""Create a Unicode string
If that fails, let Tcl try its best
# Check presence of a UTF-8 signature first
if chars
.startswith(BOM_UTF8
):
chars
= chars
[3:].decode("utf-8")
# has UTF-8 signature, but fails to decode...
# Indicates that this file originally had a BOM
self
.fileencoding
= BOM_UTF8
# Next look for coding specification
except LookupError, name
:
title
="Error loading the file",
message
="The encoding '%s' is not known to this Python "\
"installation. The file may not display correctly" % name
,
return unicode(chars
, enc
)
# If it is ASCII, we need not to record anything
return unicode(chars
, 'ascii')
# Finally, try the locale's encoding. This is deprecated;
# the user should declare a non-ASCII encoding
chars
= unicode(chars
, encoding
)
self
.fileencoding
= encoding
message
= "Do you want to save %s before closing?" % (
self
.filename
or "this untitled document")
m
= tkMessageBox
.Message(
icon
=tkMessageBox
.QUESTION
,
type=tkMessageBox
.YESNOCANCEL
,
if self
.writefile(self
.filename
):
self
.editwin
.store_file_breaks()
except AttributeError: # may be a PyShell
def save_as(self
, event
):
filename
= self
.asksavefile()
if self
.writefile(filename
):
self
.set_filename(filename
)
self
.editwin
.store_file_breaks()
self
.updaterecentfileslist(filename
)
def save_a_copy(self
, event
):
filename
= self
.asksavefile()
self
.updaterecentfileslist(filename
)
def writefile(self
, filename
):
chars
= self
.encode(self
.text
.get("1.0", "end-1c"))
if self
.eol_convention
!= "\n":
chars
= chars
.replace("\n", self
.eol_convention
)
tkMessageBox
.showerror("I/O Error", str(msg
),
if isinstance(chars
, types
.StringType
):
# This is either plain ASCII, or Tk was returning mixed-encoding
# text to us. Don't try to guess further.
# See whether there is anything non-ASCII in it.
# If not, no need to figure out the encoding.
return chars
.encode('ascii')
# If there is an encoding declared, try this first.
failed
= "Invalid encoding '%s'" % enc
"%s. Saving as UTF-8" % failed
,
# If there was a UTF-8 signature, use that. This should not fail
if self
.fileencoding
== BOM_UTF8
or failed
:
return BOM_UTF8
+ chars
.encode("utf-8")
# Try the original file encoding next, if any
return chars
.encode(self
.fileencoding
)
"Cannot save this as '%s' anymore. Saving as UTF-8" \
return BOM_UTF8
+ chars
.encode("utf-8")
# Nothing was declared, and we had not determined an encoding
# on loading. Recommend an encoding line.
config_encoding
= idleConf
.GetOption("main","EditorWindow",
if config_encoding
== 'utf-8':
# User has requested that we save files as UTF-8
return BOM_UTF8
+ chars
.encode("utf-8")
chars
= chars
.encode(encoding
)
if config_encoding
== 'locale':
chars
= BOM_UTF8
+ chars
.encode("utf-8")
dialog
= EncodingMessage(self
.editwin
.top
, enc
)
# User asked us to edit the file
encline
= "# -*- coding: %s -*-\n" % enc
firstline
= self
.text
.get("1.0", "2.0")
if firstline
.startswith("#!"):
# Insert encoding after #! line
self
.text
.insert("2.0", encline
)
self
.text
.insert("1.0", encline
)
return self
.encode(self
.text
.get("1.0", "end-1c"))
c
= self
.text
.get("end-2c")
self
.text
.insert("end-1c", "\n")
def print_window(self
, event
):
# shell undo is reset after every prompt, looks saved, probably isn't
if not saved
or filename
is None:
# XXX KBK 08Jun03 Wouldn't it be better to ask the user to save?
(tfd
, tempfilename
) = tempfile
.mkstemp(prefix
='IDLE_tmp_')
if not self
.writefile(tempfilename
):
if platform
== 'posix': #posix platform
command
= idleConf
.GetOption('main','General',
command
= command
+ " 2>&1"
elif platform
== 'nt': #win32 platform
command
= idleConf
.GetOption('main','General','print-command-win')
else: #no printing for this platform
if printPlatform
: #we can try to print for this platform
command
= command
% filename
pipe
= os
.popen(command
, "r")
# things can get ugly on NT if there is no printer available.
output
= pipe
.read().strip()
output
= "Printing failed (exit status 0x%x)\n" % \
output
= "Printing command: %s\n" % repr(command
) + output
tkMessageBox
.showerror("Print status", output
, master
=self
.text
)
else: #no printing for this platform
message
="Printing is not enabled for this platform: %s" % platform
tkMessageBox
.showinfo("Print status", message
, master
=self
.text
)
("Python and text files", "*.py *.pyw *.txt", "TEXT"),
("All text files", "*", "TEXT"),
dir, base
= self
.defaultfilename("open")
self
.opendialog
= tkFileDialog
.Open(master
=self
.text
,
filetypes
=self
.filetypes
)
return self
.opendialog
.show(initialdir
=dir, initialfile
=base
)
def defaultfilename(self
, mode
="open"):
return os
.path
.split(self
.filename
)
dir, base
= self
.defaultfilename("save")
self
.savedialog
= tkFileDialog
.SaveAs(master
=self
.text
,
filetypes
=self
.filetypes
)
return self
.savedialog
.show(initialdir
=dir, initialfile
=base
)
def updaterecentfileslist(self
,filename
):
"Update recent file list on all editor windows"
self
.editwin
.update_recent_files_list(filename
)
def __init__(self
, text
):
self
.text
.bind("<Control-o>", self
.open)
self
.text
.bind("<Control-s>", self
.save
)
self
.text
.bind("<Alt-s>", self
.save_as
)
self
.text
.bind("<Alt-z>", self
.save_a_copy
)
def get_saved(self
): return 0
def set_saved(self
, flag
): pass
def reset_undo(self
): pass
self
.text
.event_generate("<<open-window-from-file>>")
self
.text
.event_generate("<<save-window>>")
def save_as(self
, event
):
self
.text
.event_generate("<<save-window-as-file>>")
def save_a_copy(self
, event
):
self
.text
.event_generate("<<save-copy-of-window-as-file>>")
editwin
= MyEditWin(text
)
if __name__
== "__main__":