symbian-qemu-0.9.1-12/python-2.6.1/Lib/idlelib/IOBinding.py
changeset 1 2fb8b9db1c86
equal deleted inserted replaced
0:ffa851df0825 1:2fb8b9db1c86
       
     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.
       
     7 
       
     8 import os
       
     9 import types
       
    10 import sys
       
    11 import codecs
       
    12 import tempfile
       
    13 import tkFileDialog
       
    14 import tkMessageBox
       
    15 import re
       
    16 from Tkinter import *
       
    17 from SimpleDialog import SimpleDialog
       
    18 
       
    19 from configHandler import idleConf
       
    20 
       
    21 try:
       
    22     from codecs import BOM_UTF8
       
    23 except ImportError:
       
    24     # only available since Python 2.3
       
    25     BOM_UTF8 = '\xef\xbb\xbf'
       
    26 
       
    27 # Try setting the locale, so that we can find out
       
    28 # what encoding to use
       
    29 try:
       
    30     import locale
       
    31     locale.setlocale(locale.LC_CTYPE, "")
       
    32 except (ImportError, locale.Error):
       
    33     pass
       
    34 
       
    35 # Encoding for file names
       
    36 filesystemencoding = sys.getfilesystemencoding()
       
    37 
       
    38 encoding = "ascii"
       
    39 if sys.platform == 'win32':
       
    40     # On Windows, we could use "mbcs". However, to give the user
       
    41     # a portable encoding name, we need to find the code page
       
    42     try:
       
    43         encoding = locale.getdefaultlocale()[1]
       
    44         codecs.lookup(encoding)
       
    45     except LookupError:
       
    46         pass
       
    47 else:
       
    48     try:
       
    49         # Different things can fail here: the locale module may not be
       
    50         # loaded, it may not offer nl_langinfo, or CODESET, or the
       
    51         # resulting codeset may be unknown to Python. We ignore all
       
    52         # these problems, falling back to ASCII
       
    53         encoding = locale.nl_langinfo(locale.CODESET)
       
    54         if encoding is None or encoding is '':
       
    55             # situation occurs on Mac OS X
       
    56             encoding = 'ascii'
       
    57         codecs.lookup(encoding)
       
    58     except (NameError, AttributeError, LookupError):
       
    59         # Try getdefaultlocale well: it parses environment variables,
       
    60         # which may give a clue. Unfortunately, getdefaultlocale has
       
    61         # bugs that can cause ValueError.
       
    62         try:
       
    63             encoding = locale.getdefaultlocale()[1]
       
    64             if encoding is None or encoding is '':
       
    65                 # situation occurs on Mac OS X
       
    66                 encoding = 'ascii'
       
    67             codecs.lookup(encoding)
       
    68         except (ValueError, LookupError):
       
    69             pass
       
    70 
       
    71 encoding = encoding.lower()
       
    72 
       
    73 coding_re = re.compile("coding[:=]\s*([-\w_.]+)")
       
    74 
       
    75 class EncodingMessage(SimpleDialog):
       
    76     "Inform user that an encoding declaration is needed."
       
    77     def __init__(self, master, enc):
       
    78         self.should_edit = False
       
    79 
       
    80         self.root = top = Toplevel(master)
       
    81         top.bind("<Return>", self.return_event)
       
    82         top.bind("<Escape>", self.do_ok)
       
    83         top.protocol("WM_DELETE_WINDOW", self.wm_delete_window)
       
    84         top.wm_title("I/O Warning")
       
    85         top.wm_iconname("I/O Warning")
       
    86         self.top = top
       
    87 
       
    88         l1 = Label(top,
       
    89             text="Non-ASCII found, yet no encoding declared. Add a line like")
       
    90         l1.pack(side=TOP, anchor=W)
       
    91         l2 = Entry(top, font="courier")
       
    92         l2.insert(0, "# -*- coding: %s -*-" % enc)
       
    93         # For some reason, the text is not selectable anymore if the
       
    94         # widget is disabled.
       
    95         # l2['state'] = DISABLED
       
    96         l2.pack(side=TOP, anchor = W, fill=X)
       
    97         l3 = Label(top, text="to your file\n"
       
    98                    "Choose OK to save this file as %s\n"
       
    99                    "Edit your general options to silence this warning" % enc)
       
   100         l3.pack(side=TOP, anchor = W)
       
   101 
       
   102         buttons = Frame(top)
       
   103         buttons.pack(side=TOP, fill=X)
       
   104         # Both return and cancel mean the same thing: do nothing
       
   105         self.default = self.cancel = 0
       
   106         b1 = Button(buttons, text="Ok", default="active",
       
   107                     command=self.do_ok)
       
   108         b1.pack(side=LEFT, fill=BOTH, expand=1)
       
   109         b2 = Button(buttons, text="Edit my file",
       
   110                     command=self.do_edit)
       
   111         b2.pack(side=LEFT, fill=BOTH, expand=1)
       
   112 
       
   113         self._set_transient(master)
       
   114 
       
   115     def do_ok(self):
       
   116         self.done(0)
       
   117 
       
   118     def do_edit(self):
       
   119         self.done(1)
       
   120 
       
   121 def coding_spec(str):
       
   122     """Return the encoding declaration according to PEP 263.
       
   123 
       
   124     Raise LookupError if the encoding is declared but unknown.
       
   125     """
       
   126     # Only consider the first two lines
       
   127     str = str.split("\n")[:2]
       
   128     str = "\n".join(str)
       
   129 
       
   130     match = coding_re.search(str)
       
   131     if not match:
       
   132         return None
       
   133     name = match.group(1)
       
   134     # Check whether the encoding is known
       
   135     import codecs
       
   136     try:
       
   137         codecs.lookup(name)
       
   138     except LookupError:
       
   139         # The standard encoding error does not indicate the encoding
       
   140         raise LookupError, "Unknown encoding "+name
       
   141     return name
       
   142 
       
   143 
       
   144 class IOBinding:
       
   145 
       
   146     def __init__(self, editwin):
       
   147         self.editwin = editwin
       
   148         self.text = editwin.text
       
   149         self.__id_open = self.text.bind("<<open-window-from-file>>", self.open)
       
   150         self.__id_save = self.text.bind("<<save-window>>", self.save)
       
   151         self.__id_saveas = self.text.bind("<<save-window-as-file>>",
       
   152                                           self.save_as)
       
   153         self.__id_savecopy = self.text.bind("<<save-copy-of-window-as-file>>",
       
   154                                             self.save_a_copy)
       
   155         self.fileencoding = None
       
   156         self.__id_print = self.text.bind("<<print-window>>", self.print_window)
       
   157 
       
   158     def close(self):
       
   159         # Undo command bindings
       
   160         self.text.unbind("<<open-window-from-file>>", self.__id_open)
       
   161         self.text.unbind("<<save-window>>", self.__id_save)
       
   162         self.text.unbind("<<save-window-as-file>>",self.__id_saveas)
       
   163         self.text.unbind("<<save-copy-of-window-as-file>>", self.__id_savecopy)
       
   164         self.text.unbind("<<print-window>>", self.__id_print)
       
   165         # Break cycles
       
   166         self.editwin = None
       
   167         self.text = None
       
   168         self.filename_change_hook = None
       
   169 
       
   170     def get_saved(self):
       
   171         return self.editwin.get_saved()
       
   172 
       
   173     def set_saved(self, flag):
       
   174         self.editwin.set_saved(flag)
       
   175 
       
   176     def reset_undo(self):
       
   177         self.editwin.reset_undo()
       
   178 
       
   179     filename_change_hook = None
       
   180 
       
   181     def set_filename_change_hook(self, hook):
       
   182         self.filename_change_hook = hook
       
   183 
       
   184     filename = None
       
   185     dirname = None
       
   186 
       
   187     def set_filename(self, filename):
       
   188         if filename and os.path.isdir(filename):
       
   189             self.filename = None
       
   190             self.dirname = filename
       
   191         else:
       
   192             self.filename = filename
       
   193             self.dirname = None
       
   194             self.set_saved(1)
       
   195             if self.filename_change_hook:
       
   196                 self.filename_change_hook()
       
   197 
       
   198     def open(self, event=None, editFile=None):
       
   199         if self.editwin.flist:
       
   200             if not editFile:
       
   201                 filename = self.askopenfile()
       
   202             else:
       
   203                 filename=editFile
       
   204             if filename:
       
   205                 # If the current window has no filename and hasn't been
       
   206                 # modified, we replace its contents (no loss).  Otherwise
       
   207                 # we open a new window.  But we won't replace the
       
   208                 # shell window (which has an interp(reter) attribute), which
       
   209                 # gets set to "not modified" at every new prompt.
       
   210                 try:
       
   211                     interp = self.editwin.interp
       
   212                 except AttributeError:
       
   213                     interp = None
       
   214                 if not self.filename and self.get_saved() and not interp:
       
   215                     self.editwin.flist.open(filename, self.loadfile)
       
   216                 else:
       
   217                     self.editwin.flist.open(filename)
       
   218             else:
       
   219                 self.text.focus_set()
       
   220             return "break"
       
   221         #
       
   222         # Code for use outside IDLE:
       
   223         if self.get_saved():
       
   224             reply = self.maybesave()
       
   225             if reply == "cancel":
       
   226                 self.text.focus_set()
       
   227                 return "break"
       
   228         if not editFile:
       
   229             filename = self.askopenfile()
       
   230         else:
       
   231             filename=editFile
       
   232         if filename:
       
   233             self.loadfile(filename)
       
   234         else:
       
   235             self.text.focus_set()
       
   236         return "break"
       
   237 
       
   238     eol = r"(\r\n)|\n|\r"  # \r\n (Windows), \n (UNIX), or \r (Mac)
       
   239     eol_re = re.compile(eol)
       
   240     eol_convention = os.linesep # Default
       
   241 
       
   242     def loadfile(self, filename):
       
   243         try:
       
   244             # open the file in binary mode so that we can handle
       
   245             #   end-of-line convention ourselves.
       
   246             f = open(filename,'rb')
       
   247             chars = f.read()
       
   248             f.close()
       
   249         except IOError, msg:
       
   250             tkMessageBox.showerror("I/O Error", str(msg), master=self.text)
       
   251             return False
       
   252 
       
   253         chars = self.decode(chars)
       
   254         # We now convert all end-of-lines to '\n's
       
   255         firsteol = self.eol_re.search(chars)
       
   256         if firsteol:
       
   257             self.eol_convention = firsteol.group(0)
       
   258             if isinstance(self.eol_convention, unicode):
       
   259                 # Make sure it is an ASCII string
       
   260                 self.eol_convention = self.eol_convention.encode("ascii")
       
   261             chars = self.eol_re.sub(r"\n", chars)
       
   262 
       
   263         self.text.delete("1.0", "end")
       
   264         self.set_filename(None)
       
   265         self.text.insert("1.0", chars)
       
   266         self.reset_undo()
       
   267         self.set_filename(filename)
       
   268         self.text.mark_set("insert", "1.0")
       
   269         self.text.see("insert")
       
   270         self.updaterecentfileslist(filename)
       
   271         return True
       
   272 
       
   273     def decode(self, chars):
       
   274         """Create a Unicode string
       
   275 
       
   276         If that fails, let Tcl try its best
       
   277         """
       
   278         # Check presence of a UTF-8 signature first
       
   279         if chars.startswith(BOM_UTF8):
       
   280             try:
       
   281                 chars = chars[3:].decode("utf-8")
       
   282             except UnicodeError:
       
   283                 # has UTF-8 signature, but fails to decode...
       
   284                 return chars
       
   285             else:
       
   286                 # Indicates that this file originally had a BOM
       
   287                 self.fileencoding = BOM_UTF8
       
   288                 return chars
       
   289         # Next look for coding specification
       
   290         try:
       
   291             enc = coding_spec(chars)
       
   292         except LookupError, name:
       
   293             tkMessageBox.showerror(
       
   294                 title="Error loading the file",
       
   295                 message="The encoding '%s' is not known to this Python "\
       
   296                 "installation. The file may not display correctly" % name,
       
   297                 master = self.text)
       
   298             enc = None
       
   299         if enc:
       
   300             try:
       
   301                 return unicode(chars, enc)
       
   302             except UnicodeError:
       
   303                 pass
       
   304         # If it is ASCII, we need not to record anything
       
   305         try:
       
   306             return unicode(chars, 'ascii')
       
   307         except UnicodeError:
       
   308             pass
       
   309         # Finally, try the locale's encoding. This is deprecated;
       
   310         # the user should declare a non-ASCII encoding
       
   311         try:
       
   312             chars = unicode(chars, encoding)
       
   313             self.fileencoding = encoding
       
   314         except UnicodeError:
       
   315             pass
       
   316         return chars
       
   317 
       
   318     def maybesave(self):
       
   319         if self.get_saved():
       
   320             return "yes"
       
   321         message = "Do you want to save %s before closing?" % (
       
   322             self.filename or "this untitled document")
       
   323         m = tkMessageBox.Message(
       
   324             title="Save On Close",
       
   325             message=message,
       
   326             icon=tkMessageBox.QUESTION,
       
   327             type=tkMessageBox.YESNOCANCEL,
       
   328             master=self.text)
       
   329         reply = m.show()
       
   330         if reply == "yes":
       
   331             self.save(None)
       
   332             if not self.get_saved():
       
   333                 reply = "cancel"
       
   334         self.text.focus_set()
       
   335         return reply
       
   336 
       
   337     def save(self, event):
       
   338         if not self.filename:
       
   339             self.save_as(event)
       
   340         else:
       
   341             if self.writefile(self.filename):
       
   342                 self.set_saved(1)
       
   343                 try:
       
   344                     self.editwin.store_file_breaks()
       
   345                 except AttributeError:  # may be a PyShell
       
   346                     pass
       
   347         self.text.focus_set()
       
   348         return "break"
       
   349 
       
   350     def save_as(self, event):
       
   351         filename = self.asksavefile()
       
   352         if filename:
       
   353             if self.writefile(filename):
       
   354                 self.set_filename(filename)
       
   355                 self.set_saved(1)
       
   356                 try:
       
   357                     self.editwin.store_file_breaks()
       
   358                 except AttributeError:
       
   359                     pass
       
   360         self.text.focus_set()
       
   361         self.updaterecentfileslist(filename)
       
   362         return "break"
       
   363 
       
   364     def save_a_copy(self, event):
       
   365         filename = self.asksavefile()
       
   366         if filename:
       
   367             self.writefile(filename)
       
   368         self.text.focus_set()
       
   369         self.updaterecentfileslist(filename)
       
   370         return "break"
       
   371 
       
   372     def writefile(self, filename):
       
   373         self.fixlastline()
       
   374         chars = self.encode(self.text.get("1.0", "end-1c"))
       
   375         if self.eol_convention != "\n":
       
   376             chars = chars.replace("\n", self.eol_convention)
       
   377         try:
       
   378             f = open(filename, "wb")
       
   379             f.write(chars)
       
   380             f.flush()
       
   381             f.close()
       
   382             return True
       
   383         except IOError, msg:
       
   384             tkMessageBox.showerror("I/O Error", str(msg),
       
   385                                    master=self.text)
       
   386             return False
       
   387 
       
   388     def encode(self, chars):
       
   389         if isinstance(chars, types.StringType):
       
   390             # This is either plain ASCII, or Tk was returning mixed-encoding
       
   391             # text to us. Don't try to guess further.
       
   392             return chars
       
   393         # See whether there is anything non-ASCII in it.
       
   394         # If not, no need to figure out the encoding.
       
   395         try:
       
   396             return chars.encode('ascii')
       
   397         except UnicodeError:
       
   398             pass
       
   399         # If there is an encoding declared, try this first.
       
   400         try:
       
   401             enc = coding_spec(chars)
       
   402             failed = None
       
   403         except LookupError, msg:
       
   404             failed = msg
       
   405             enc = None
       
   406         if enc:
       
   407             try:
       
   408                 return chars.encode(enc)
       
   409             except UnicodeError:
       
   410                 failed = "Invalid encoding '%s'" % enc
       
   411         if failed:
       
   412             tkMessageBox.showerror(
       
   413                 "I/O Error",
       
   414                 "%s. Saving as UTF-8" % failed,
       
   415                 master = self.text)
       
   416         # If there was a UTF-8 signature, use that. This should not fail
       
   417         if self.fileencoding == BOM_UTF8 or failed:
       
   418             return BOM_UTF8 + chars.encode("utf-8")
       
   419         # Try the original file encoding next, if any
       
   420         if self.fileencoding:
       
   421             try:
       
   422                 return chars.encode(self.fileencoding)
       
   423             except UnicodeError:
       
   424                 tkMessageBox.showerror(
       
   425                     "I/O Error",
       
   426                     "Cannot save this as '%s' anymore. Saving as UTF-8" \
       
   427                     % self.fileencoding,
       
   428                     master = self.text)
       
   429                 return BOM_UTF8 + chars.encode("utf-8")
       
   430         # Nothing was declared, and we had not determined an encoding
       
   431         # on loading. Recommend an encoding line.
       
   432         config_encoding = idleConf.GetOption("main","EditorWindow",
       
   433                                              "encoding")
       
   434         if config_encoding == 'utf-8':
       
   435             # User has requested that we save files as UTF-8
       
   436             return BOM_UTF8 + chars.encode("utf-8")
       
   437         ask_user = True
       
   438         try:
       
   439             chars = chars.encode(encoding)
       
   440             enc = encoding
       
   441             if config_encoding == 'locale':
       
   442                 ask_user = False
       
   443         except UnicodeError:
       
   444             chars = BOM_UTF8 + chars.encode("utf-8")
       
   445             enc = "utf-8"
       
   446         if not ask_user:
       
   447             return chars
       
   448         dialog = EncodingMessage(self.editwin.top, enc)
       
   449         dialog.go()
       
   450         if dialog.num == 1:
       
   451             # User asked us to edit the file
       
   452             encline = "# -*- coding: %s -*-\n" % enc
       
   453             firstline = self.text.get("1.0", "2.0")
       
   454             if firstline.startswith("#!"):
       
   455                 # Insert encoding after #! line
       
   456                 self.text.insert("2.0", encline)
       
   457             else:
       
   458                 self.text.insert("1.0", encline)
       
   459             return self.encode(self.text.get("1.0", "end-1c"))
       
   460         return chars
       
   461 
       
   462     def fixlastline(self):
       
   463         c = self.text.get("end-2c")
       
   464         if c != '\n':
       
   465             self.text.insert("end-1c", "\n")
       
   466 
       
   467     def print_window(self, event):
       
   468         m = tkMessageBox.Message(
       
   469             title="Print",
       
   470             message="Print to Default Printer",
       
   471             icon=tkMessageBox.QUESTION,
       
   472             type=tkMessageBox.OKCANCEL,
       
   473             default=tkMessageBox.OK,
       
   474             master=self.text)
       
   475         reply = m.show()
       
   476         if reply != tkMessageBox.OK:
       
   477             self.text.focus_set()
       
   478             return "break"
       
   479         tempfilename = None
       
   480         saved = self.get_saved()
       
   481         if saved:
       
   482             filename = self.filename
       
   483         # shell undo is reset after every prompt, looks saved, probably isn't
       
   484         if not saved or filename is None:
       
   485             (tfd, tempfilename) = tempfile.mkstemp(prefix='IDLE_tmp_')
       
   486             filename = tempfilename
       
   487             os.close(tfd)
       
   488             if not self.writefile(tempfilename):
       
   489                 os.unlink(tempfilename)
       
   490                 return "break"
       
   491         platform=os.name
       
   492         printPlatform=1
       
   493         if platform == 'posix': #posix platform
       
   494             command = idleConf.GetOption('main','General',
       
   495                                          'print-command-posix')
       
   496             command = command + " 2>&1"
       
   497         elif platform == 'nt': #win32 platform
       
   498             command = idleConf.GetOption('main','General','print-command-win')
       
   499         else: #no printing for this platform
       
   500             printPlatform=0
       
   501         if printPlatform:  #we can try to print for this platform
       
   502             command = command % filename
       
   503             pipe = os.popen(command, "r")
       
   504             # things can get ugly on NT if there is no printer available.
       
   505             output = pipe.read().strip()
       
   506             status = pipe.close()
       
   507             if status:
       
   508                 output = "Printing failed (exit status 0x%x)\n" % \
       
   509                          status + output
       
   510             if output:
       
   511                 output = "Printing command: %s\n" % repr(command) + output
       
   512                 tkMessageBox.showerror("Print status", output, master=self.text)
       
   513         else:  #no printing for this platform
       
   514             message="Printing is not enabled for this platform: %s" % platform
       
   515             tkMessageBox.showinfo("Print status", message, master=self.text)
       
   516         if tempfilename:
       
   517             os.unlink(tempfilename)
       
   518         return "break"
       
   519 
       
   520     opendialog = None
       
   521     savedialog = None
       
   522 
       
   523     filetypes = [
       
   524         ("Python and text files", "*.py *.pyw *.txt", "TEXT"),
       
   525         ("All text files", "*", "TEXT"),
       
   526         ("All files", "*"),
       
   527         ]
       
   528 
       
   529     def askopenfile(self):
       
   530         dir, base = self.defaultfilename("open")
       
   531         if not self.opendialog:
       
   532             self.opendialog = tkFileDialog.Open(master=self.text,
       
   533                                                 filetypes=self.filetypes)
       
   534         filename = self.opendialog.show(initialdir=dir, initialfile=base)
       
   535         if isinstance(filename, unicode):
       
   536             filename = filename.encode(filesystemencoding)
       
   537         return filename
       
   538 
       
   539     def defaultfilename(self, mode="open"):
       
   540         if self.filename:
       
   541             return os.path.split(self.filename)
       
   542         elif self.dirname:
       
   543             return self.dirname, ""
       
   544         else:
       
   545             try:
       
   546                 pwd = os.getcwd()
       
   547             except os.error:
       
   548                 pwd = ""
       
   549             return pwd, ""
       
   550 
       
   551     def asksavefile(self):
       
   552         dir, base = self.defaultfilename("save")
       
   553         if not self.savedialog:
       
   554             self.savedialog = tkFileDialog.SaveAs(master=self.text,
       
   555                                                   filetypes=self.filetypes)
       
   556         filename = self.savedialog.show(initialdir=dir, initialfile=base)
       
   557         if isinstance(filename, unicode):
       
   558             filename = filename.encode(filesystemencoding)
       
   559         return filename
       
   560 
       
   561     def updaterecentfileslist(self,filename):
       
   562         "Update recent file list on all editor windows"
       
   563         self.editwin.update_recent_files_list(filename)
       
   564 
       
   565 def test():
       
   566     root = Tk()
       
   567     class MyEditWin:
       
   568         def __init__(self, text):
       
   569             self.text = text
       
   570             self.flist = None
       
   571             self.text.bind("<Control-o>", self.open)
       
   572             self.text.bind("<Control-s>", self.save)
       
   573             self.text.bind("<Alt-s>", self.save_as)
       
   574             self.text.bind("<Alt-z>", self.save_a_copy)
       
   575         def get_saved(self): return 0
       
   576         def set_saved(self, flag): pass
       
   577         def reset_undo(self): pass
       
   578         def open(self, event):
       
   579             self.text.event_generate("<<open-window-from-file>>")
       
   580         def save(self, event):
       
   581             self.text.event_generate("<<save-window>>")
       
   582         def save_as(self, event):
       
   583             self.text.event_generate("<<save-window-as-file>>")
       
   584         def save_a_copy(self, event):
       
   585             self.text.event_generate("<<save-copy-of-window-as-file>>")
       
   586     text = Text(root)
       
   587     text.pack()
       
   588     text.focus_set()
       
   589     editwin = MyEditWin(text)
       
   590     io = IOBinding(editwin)
       
   591     root.mainloop()
       
   592 
       
   593 if __name__ == "__main__":
       
   594     test()