PyXR

c:\python24\lib \ idlelib \ IOBinding.py



0001 # changes by dscherer@cmu.edu
0002 #   - IOBinding.open() replaces the current window with the opened file,
0003 #     if the current window is both unmodified and unnamed
0004 #   - IOBinding.loadfile() interprets Windows, UNIX, and Macintosh
0005 #     end-of-line conventions, instead of relying on the standard library,
0006 #     which will only understand the local convention.
0007 
0008 import os
0009 import types
0010 import sys
0011 import codecs
0012 import tempfile
0013 import tkFileDialog
0014 import tkMessageBox
0015 import re
0016 from Tkinter import *
0017 from SimpleDialog import SimpleDialog
0018 
0019 from configHandler import idleConf
0020 
0021 try:
0022     from codecs import BOM_UTF8
0023 except ImportError:
0024     # only available since Python 2.3
0025     BOM_UTF8 = '\xef\xbb\xbf'
0026 
0027 # Try setting the locale, so that we can find out
0028 # what encoding to use
0029 try:
0030     import locale
0031     locale.setlocale(locale.LC_CTYPE, "")
0032 except (ImportError, locale.Error):
0033     pass
0034 
0035 encoding = "ascii"
0036 if sys.platform == 'win32':
0037     # On Windows, we could use "mbcs". However, to give the user
0038     # a portable encoding name, we need to find the code page
0039     try:
0040         encoding = locale.getdefaultlocale()[1]
0041         codecs.lookup(encoding)
0042     except LookupError:
0043         pass
0044 else:
0045     try:
0046         # Different things can fail here: the locale module may not be
0047         # loaded, it may not offer nl_langinfo, or CODESET, or the
0048         # resulting codeset may be unknown to Python. We ignore all
0049         # these problems, falling back to ASCII
0050         encoding = locale.nl_langinfo(locale.CODESET)
0051         if encoding is None or encoding is '':
0052             # situation occurs on Mac OS X
0053             encoding = 'ascii'
0054         codecs.lookup(encoding)
0055     except (NameError, AttributeError, LookupError):
0056         # Try getdefaultlocale well: it parses environment variables,
0057         # which may give a clue. Unfortunately, getdefaultlocale has
0058         # bugs that can cause ValueError.
0059         try:
0060             encoding = locale.getdefaultlocale()[1]
0061             if encoding is None or encoding is '':
0062                 # situation occurs on Mac OS X
0063                 encoding = 'ascii'
0064             codecs.lookup(encoding)
0065         except (ValueError, LookupError):
0066             pass
0067 
0068 encoding = encoding.lower()
0069 
0070 coding_re = re.compile("coding[:=]\s*([-\w_.]+)")
0071 
0072 class EncodingMessage(SimpleDialog):
0073     "Inform user that an encoding declaration is needed."
0074     def __init__(self, master, enc):
0075         self.should_edit = False
0076 
0077         self.root = top = Toplevel(master)
0078         top.bind("<Return>", self.return_event)
0079         top.bind("<Escape>", self.do_ok)
0080         top.protocol("WM_DELETE_WINDOW", self.wm_delete_window)
0081         top.wm_title("I/O Warning")
0082         top.wm_iconname("I/O Warning")
0083         self.top = top
0084 
0085         l1 = Label(top,
0086             text="Non-ASCII found, yet no encoding declared. Add a line like")
0087         l1.pack(side=TOP, anchor=W)
0088         l2 = Entry(top, font="courier")
0089         l2.insert(0, "# -*- coding: %s -*-" % enc)
0090         # For some reason, the text is not selectable anymore if the
0091         # widget is disabled.
0092         # l2['state'] = DISABLED
0093         l2.pack(side=TOP, anchor = W, fill=X)
0094         l3 = Label(top, text="to your file\n"
0095                    "Choose OK to save this file as %s\n"
0096                    "Edit your general options to silence this warning" % enc)
0097         l3.pack(side=TOP, anchor = W)
0098 
0099         buttons = Frame(top)
0100         buttons.pack(side=TOP, fill=X)
0101         # Both return and cancel mean the same thing: do nothing
0102         self.default = self.cancel = 0
0103         b1 = Button(buttons, text="Ok", default="active",
0104                     command=self.do_ok)
0105         b1.pack(side=LEFT, fill=BOTH, expand=1)
0106         b2 = Button(buttons, text="Edit my file",
0107                     command=self.do_edit)
0108         b2.pack(side=LEFT, fill=BOTH, expand=1)
0109 
0110         self._set_transient(master)
0111 
0112     def do_ok(self):
0113         self.done(0)
0114 
0115     def do_edit(self):
0116         self.done(1)
0117 
0118 def coding_spec(str):
0119     """Return the encoding declaration according to PEP 263.
0120 
0121     Raise LookupError if the encoding is declared but unknown.
0122     """
0123     # Only consider the first two lines
0124     str = str.split("\n")[:2]
0125     str = "\n".join(str)
0126 
0127     match = coding_re.search(str)
0128     if not match:
0129         return None
0130     name = match.group(1)
0131     # Check whether the encoding is known
0132     import codecs
0133     try:
0134         codecs.lookup(name)
0135     except LookupError:
0136         # The standard encoding error does not indicate the encoding
0137         raise LookupError, "Unknown encoding "+name
0138     return name
0139 
0140 
0141 class IOBinding:
0142 
0143     def __init__(self, editwin):
0144         self.editwin = editwin
0145         self.text = editwin.text
0146         self.__id_open = self.text.bind("<<open-window-from-file>>", self.open)
0147         self.__id_save = self.text.bind("<<save-window>>", self.save)
0148         self.__id_saveas = self.text.bind("<<save-window-as-file>>",
0149                                           self.save_as)
0150         self.__id_savecopy = self.text.bind("<<save-copy-of-window-as-file>>",
0151                                             self.save_a_copy)
0152         self.fileencoding = None
0153         self.__id_print = self.text.bind("<<print-window>>", self.print_window)
0154 
0155     def close(self):
0156         # Undo command bindings
0157         self.text.unbind("<<open-window-from-file>>", self.__id_open)
0158         self.text.unbind("<<save-window>>", self.__id_save)
0159         self.text.unbind("<<save-window-as-file>>",self.__id_saveas)
0160         self.text.unbind("<<save-copy-of-window-as-file>>", self.__id_savecopy)
0161         self.text.unbind("<<print-window>>", self.__id_print)
0162         # Break cycles
0163         self.editwin = None
0164         self.text = None
0165         self.filename_change_hook = None
0166 
0167     def get_saved(self):
0168         return self.editwin.get_saved()
0169 
0170     def set_saved(self, flag):
0171         self.editwin.set_saved(flag)
0172 
0173     def reset_undo(self):
0174         self.editwin.reset_undo()
0175 
0176     filename_change_hook = None
0177 
0178     def set_filename_change_hook(self, hook):
0179         self.filename_change_hook = hook
0180 
0181     filename = None
0182     dirname = None
0183 
0184     def set_filename(self, filename):
0185         if filename and os.path.isdir(filename):
0186             self.filename = None
0187             self.dirname = filename
0188         else:
0189             self.filename = filename
0190             self.dirname = None
0191             self.set_saved(1)
0192             if self.filename_change_hook:
0193                 self.filename_change_hook()
0194 
0195     def open(self, event=None, editFile=None):
0196         if self.editwin.flist:
0197             if not editFile:
0198                 filename = self.askopenfile()
0199             else:
0200                 filename=editFile
0201             if filename:
0202                 # If the current window has no filename and hasn't been
0203                 # modified, we replace its contents (no loss).  Otherwise
0204                 # we open a new window.  But we won't replace the
0205                 # shell window (which has an interp(reter) attribute), which
0206                 # gets set to "not modified" at every new prompt.
0207                 try:
0208                     interp = self.editwin.interp
0209                 except:
0210                     interp = None
0211                 if not self.filename and self.get_saved() and not interp:
0212                     self.editwin.flist.open(filename, self.loadfile)
0213                 else:
0214                     self.editwin.flist.open(filename)
0215             else:
0216                 self.text.focus_set()
0217             return "break"
0218         #
0219         # Code for use outside IDLE:
0220         if self.get_saved():
0221             reply = self.maybesave()
0222             if reply == "cancel":
0223                 self.text.focus_set()
0224                 return "break"
0225         if not editFile:
0226             filename = self.askopenfile()
0227         else:
0228             filename=editFile
0229         if filename:
0230             self.loadfile(filename)
0231         else:
0232             self.text.focus_set()
0233         return "break"
0234 
0235     eol = r"(\r\n)|\n|\r"  # \r\n (Windows), \n (UNIX), or \r (Mac)
0236     eol_re = re.compile(eol)
0237     eol_convention = os.linesep # Default
0238 
0239     def loadfile(self, filename):
0240         try:
0241             # open the file in binary mode so that we can handle
0242             #   end-of-line convention ourselves.
0243             f = open(filename,'rb')
0244             chars = f.read()
0245             f.close()
0246         except IOError, msg:
0247             tkMessageBox.showerror("I/O Error", str(msg), master=self.text)
0248             return False
0249 
0250         chars = self.decode(chars)
0251         # We now convert all end-of-lines to '\n's
0252         firsteol = self.eol_re.search(chars)
0253         if firsteol:
0254             self.eol_convention = firsteol.group(0)
0255             if isinstance(self.eol_convention, unicode):
0256                 # Make sure it is an ASCII string
0257                 self.eol_convention = self.eol_convention.encode("ascii")
0258             chars = self.eol_re.sub(r"\n", chars)
0259 
0260         self.text.delete("1.0", "end")
0261         self.set_filename(None)
0262         self.text.insert("1.0", chars)
0263         self.reset_undo()
0264         self.set_filename(filename)
0265         self.text.mark_set("insert", "1.0")
0266         self.text.see("insert")
0267         self.updaterecentfileslist(filename)
0268         return True
0269 
0270     def decode(self, chars):
0271         """Create a Unicode string
0272 
0273         If that fails, let Tcl try its best
0274         """
0275         # Check presence of a UTF-8 signature first
0276         if chars.startswith(BOM_UTF8):
0277             try:
0278                 chars = chars[3:].decode("utf-8")
0279             except UnicodeError:
0280                 # has UTF-8 signature, but fails to decode...
0281                 return chars
0282             else:
0283                 # Indicates that this file originally had a BOM
0284                 self.fileencoding = BOM_UTF8
0285                 return chars
0286         # Next look for coding specification
0287         try:
0288             enc = coding_spec(chars)
0289         except LookupError, name:
0290             tkMessageBox.showerror(
0291                 title="Error loading the file",
0292                 message="The encoding '%s' is not known to this Python "\
0293                 "installation. The file may not display correctly" % name,
0294                 master = self.text)
0295             enc = None
0296         if enc:
0297             try:
0298                 return unicode(chars, enc)
0299             except UnicodeError:
0300                 pass
0301         # If it is ASCII, we need not to record anything
0302         try:
0303             return unicode(chars, 'ascii')
0304         except UnicodeError:
0305             pass
0306         # Finally, try the locale's encoding. This is deprecated;
0307         # the user should declare a non-ASCII encoding
0308         try:
0309             chars = unicode(chars, encoding)
0310             self.fileencoding = encoding
0311         except UnicodeError:
0312             pass
0313         return chars
0314 
0315     def maybesave(self):
0316         if self.get_saved():
0317             return "yes"
0318         message = "Do you want to save %s before closing?" % (
0319             self.filename or "this untitled document")
0320         m = tkMessageBox.Message(
0321             title="Save On Close",
0322             message=message,
0323             icon=tkMessageBox.QUESTION,
0324             type=tkMessageBox.YESNOCANCEL,
0325             master=self.text)
0326         reply = m.show()
0327         if reply == "yes":
0328             self.save(None)
0329             if not self.get_saved():
0330                 reply = "cancel"
0331         self.text.focus_set()
0332         return reply
0333 
0334     def save(self, event):
0335         if not self.filename:
0336             self.save_as(event)
0337         else:
0338             if self.writefile(self.filename):
0339                 self.set_saved(1)
0340                 try:
0341                     self.editwin.store_file_breaks()
0342                 except AttributeError:  # may be a PyShell
0343                     pass
0344         self.text.focus_set()
0345         return "break"
0346 
0347     def save_as(self, event):
0348         filename = self.asksavefile()
0349         if filename:
0350             if self.writefile(filename):
0351                 self.set_filename(filename)
0352                 self.set_saved(1)
0353                 try:
0354                     self.editwin.store_file_breaks()
0355                 except AttributeError:
0356                     pass
0357         self.text.focus_set()
0358         self.updaterecentfileslist(filename)
0359         return "break"
0360 
0361     def save_a_copy(self, event):
0362         filename = self.asksavefile()
0363         if filename:
0364             self.writefile(filename)
0365         self.text.focus_set()
0366         self.updaterecentfileslist(filename)
0367         return "break"
0368 
0369     def writefile(self, filename):
0370         self.fixlastline()
0371         chars = self.encode(self.text.get("1.0", "end-1c"))
0372         if self.eol_convention != "\n":
0373             chars = chars.replace("\n", self.eol_convention)
0374         try:
0375             f = open(filename, "wb")
0376             f.write(chars)
0377             f.close()
0378             return True
0379         except IOError, msg:
0380             tkMessageBox.showerror("I/O Error", str(msg),
0381                                    master=self.text)
0382             return False
0383 
0384     def encode(self, chars):
0385         if isinstance(chars, types.StringType):
0386             # This is either plain ASCII, or Tk was returning mixed-encoding
0387             # text to us. Don't try to guess further.
0388             return chars
0389         # See whether there is anything non-ASCII in it.
0390         # If not, no need to figure out the encoding.
0391         try:
0392             return chars.encode('ascii')
0393         except UnicodeError:
0394             pass
0395         # If there is an encoding declared, try this first.
0396         try:
0397             enc = coding_spec(chars)
0398             failed = None
0399         except LookupError, msg:
0400             failed = msg
0401             enc = None
0402         if enc:
0403             try:
0404                 return chars.encode(enc)
0405             except UnicodeError:
0406                 failed = "Invalid encoding '%s'" % enc
0407         if failed:
0408             tkMessageBox.showerror(
0409                 "I/O Error",
0410                 "%s. Saving as UTF-8" % failed,
0411                 master = self.text)
0412         # If there was a UTF-8 signature, use that. This should not fail
0413         if self.fileencoding == BOM_UTF8 or failed:
0414             return BOM_UTF8 + chars.encode("utf-8")
0415         # Try the original file encoding next, if any
0416         if self.fileencoding:
0417             try:
0418                 return chars.encode(self.fileencoding)
0419             except UnicodeError:
0420                 tkMessageBox.showerror(
0421                     "I/O Error",
0422                     "Cannot save this as '%s' anymore. Saving as UTF-8" \
0423                     % self.fileencoding,
0424                     master = self.text)
0425                 return BOM_UTF8 + chars.encode("utf-8")
0426         # Nothing was declared, and we had not determined an encoding
0427         # on loading. Recommend an encoding line.
0428         config_encoding = idleConf.GetOption("main","EditorWindow",
0429                                              "encoding")
0430         if config_encoding == 'utf-8':
0431             # User has requested that we save files as UTF-8
0432             return BOM_UTF8 + chars.encode("utf-8")
0433         ask_user = True
0434         try:
0435             chars = chars.encode(encoding)
0436             enc = encoding
0437             if config_encoding == 'locale':
0438                 ask_user = False
0439         except UnicodeError:
0440             chars = BOM_UTF8 + chars.encode("utf-8")
0441             enc = "utf-8"
0442         if not ask_user:
0443             return chars
0444         dialog = EncodingMessage(self.editwin.top, enc)
0445         dialog.go()
0446         if dialog.num == 1:
0447             # User asked us to edit the file
0448             encline = "# -*- coding: %s -*-\n" % enc
0449             firstline = self.text.get("1.0", "2.0")
0450             if firstline.startswith("#!"):
0451                 # Insert encoding after #! line
0452                 self.text.insert("2.0", encline)
0453             else:
0454                 self.text.insert("1.0", encline)
0455             return self.encode(self.text.get("1.0", "end-1c"))
0456         return chars
0457 
0458     def fixlastline(self):
0459         c = self.text.get("end-2c")
0460         if c != '\n':
0461             self.text.insert("end-1c", "\n")
0462 
0463     def print_window(self, event):
0464         tempfilename = None
0465         saved = self.get_saved()
0466         if saved:
0467             filename = self.filename
0468         # shell undo is reset after every prompt, looks saved, probably isn't
0469         if not saved or filename is None:
0470             # XXX KBK 08Jun03 Wouldn't it be better to ask the user to save?
0471             (tfd, tempfilename) = tempfile.mkstemp(prefix='IDLE_tmp_')
0472             filename = tempfilename
0473             os.close(tfd)
0474             if not self.writefile(tempfilename):
0475                 os.unlink(tempfilename)
0476                 return "break"
0477         platform=os.name
0478         printPlatform=1
0479         if platform == 'posix': #posix platform
0480             command = idleConf.GetOption('main','General',
0481                                          'print-command-posix')
0482             command = command + " 2>&1"
0483         elif platform == 'nt': #win32 platform
0484             command = idleConf.GetOption('main','General','print-command-win')
0485         else: #no printing for this platform
0486             printPlatform=0
0487         if printPlatform:  #we can try to print for this platform
0488             command = command % filename
0489             pipe = os.popen(command, "r")
0490             # things can get ugly on NT if there is no printer available.
0491             output = pipe.read().strip()
0492             status = pipe.close()
0493             if status:
0494                 output = "Printing failed (exit status 0x%x)\n" % \
0495                          status + output
0496             if output:
0497                 output = "Printing command: %s\n" % repr(command) + output
0498                 tkMessageBox.showerror("Print status", output, master=self.text)
0499         else:  #no printing for this platform
0500             message="Printing is not enabled for this platform: %s" % platform
0501             tkMessageBox.showinfo("Print status", message, master=self.text)
0502         if tempfilename:
0503             os.unlink(tempfilename)
0504         return "break"
0505 
0506     opendialog = None
0507     savedialog = None
0508 
0509     filetypes = [
0510         ("Python and text files", "*.py *.pyw *.txt", "TEXT"),
0511         ("All text files", "*", "TEXT"),
0512         ("All files", "*"),
0513         ]
0514 
0515     def askopenfile(self):
0516         dir, base = self.defaultfilename("open")
0517         if not self.opendialog:
0518             self.opendialog = tkFileDialog.Open(master=self.text,
0519                                                 filetypes=self.filetypes)
0520         return self.opendialog.show(initialdir=dir, initialfile=base)
0521 
0522     def defaultfilename(self, mode="open"):
0523         if self.filename:
0524             return os.path.split(self.filename)
0525         elif self.dirname:
0526             return self.dirname, ""
0527         else:
0528             try:
0529                 pwd = os.getcwd()
0530             except os.error:
0531                 pwd = ""
0532             return pwd, ""
0533 
0534     def asksavefile(self):
0535         dir, base = self.defaultfilename("save")
0536         if not self.savedialog:
0537             self.savedialog = tkFileDialog.SaveAs(master=self.text,
0538                                                   filetypes=self.filetypes)
0539         return self.savedialog.show(initialdir=dir, initialfile=base)
0540 
0541     def updaterecentfileslist(self,filename):
0542         "Update recent file list on all editor windows"
0543         self.editwin.update_recent_files_list(filename)
0544 
0545 def test():
0546     root = Tk()
0547     class MyEditWin:
0548         def __init__(self, text):
0549             self.text = text
0550             self.flist = None
0551             self.text.bind("<Control-o>", self.open)
0552             self.text.bind("<Control-s>", self.save)
0553             self.text.bind("<Alt-s>", self.save_as)
0554             self.text.bind("<Alt-z>", self.save_a_copy)
0555         def get_saved(self): return 0
0556         def set_saved(self, flag): pass
0557         def reset_undo(self): pass
0558         def open(self, event):
0559             self.text.event_generate("<<open-window-from-file>>")
0560         def save(self, event):
0561             self.text.event_generate("<<save-window>>")
0562         def save_as(self, event):
0563             self.text.event_generate("<<save-window-as-file>>")
0564         def save_a_copy(self, event):
0565             self.text.event_generate("<<save-copy-of-window-as-file>>")
0566     text = Text(root)
0567     text.pack()
0568     text.focus_set()
0569     editwin = MyEditWin(text)
0570     io = IOBinding(editwin)
0571     root.mainloop()
0572 
0573 if __name__ == "__main__":
0574     test()
0575 

Generated by PyXR 0.9.4
SourceForge.net Logo