diff options
Diffstat (limited to 'Lib/idlelib/EditorWindow.py')
| -rw-r--r-- | Lib/idlelib/EditorWindow.py | 164 | 
1 files changed, 135 insertions, 29 deletions
diff --git a/Lib/idlelib/EditorWindow.py b/Lib/idlelib/EditorWindow.py index 98ec02b..16f63c5 100644 --- a/Lib/idlelib/EditorWindow.py +++ b/Lib/idlelib/EditorWindow.py @@ -3,7 +3,6 @@ import os  import re  import string  import imp -from itertools import count  from tkinter import *  import tkinter.simpledialog as tkSimpleDialog  import tkinter.messagebox as tkMessageBox @@ -51,8 +50,63 @@ def _find_module(fullname, path=None):              path = module.__path__          except AttributeError:              raise ImportError('No source for module ' + module.__name__) +    if descr[2] != imp.PY_SOURCE: +        # If all of the above fails and didn't raise an exception,fallback +        # to a straight import which can find __init__.py in a package. +        m = __import__(fullname) +        try: +            filename = m.__file__ +        except AttributeError: +            pass +        else: +            file = None +            descr = os.path.splitext(filename)[1], None, imp.PY_SOURCE      return file, filename, descr + +class HelpDialog(object): + +    def __init__(self): +        self.parent = None      # parent of help window +        self.dlg = None         # the help window iteself + +    def display(self, parent, near=None): +        """ Display the help dialog. + +            parent - parent widget for the help window + +            near - a Toplevel widget (e.g. EditorWindow or PyShell) +                   to use as a reference for placing the help window +        """ +        if self.dlg is None: +            self.show_dialog(parent) +        if near: +            self.nearwindow(near) + +    def show_dialog(self, parent): +        self.parent = parent +        fn=os.path.join(os.path.abspath(os.path.dirname(__file__)),'help.txt') +        self.dlg = dlg = textView.view_file(parent,'Help',fn, modal=False) +        dlg.bind('<Destroy>', self.destroy, '+') + +    def nearwindow(self, near): +        # Place the help dialog near the window specified by parent. +        # Note - this may not reposition the window in Metacity +        #  if "/apps/metacity/general/disable_workarounds" is enabled +        dlg = self.dlg +        geom = (near.winfo_rootx() + 10, near.winfo_rooty() + 10) +        dlg.withdraw() +        dlg.geometry("=+%d+%d" % geom) +        dlg.deiconify() +        dlg.lift() + +    def destroy(self, ev=None): +        self.dlg = None +        self.parent = None + +helpDialog = HelpDialog()  # singleton instance + +  class EditorWindow(object):      from idlelib.Percolator import Percolator      from idlelib.ColorDelegator import ColorDelegator @@ -116,13 +170,15 @@ class EditorWindow(object):                  'recent-files.lst')          self.text_frame = text_frame = Frame(top)          self.vbar = vbar = Scrollbar(text_frame, name='vbar') -        self.width = idleConf.GetOption('main','EditorWindow','width') +        self.width = idleConf.GetOption('main', 'EditorWindow', +                                        'width', type='int')          text_options = {                  'name': 'text',                  'padx': 5,                  'wrap': 'none',                  'width': self.width, -                'height': idleConf.GetOption('main', 'EditorWindow', 'height')} +                'height': idleConf.GetOption('main', 'EditorWindow', +                                             'height', type='int')}          if TkVersion >= 8.5:              # Starting with tk 8.5 we have to set the new tabstyle option              # to 'wordprocessor' to achieve the same display of tabs as in @@ -199,7 +255,8 @@ class EditorWindow(object):          if idleConf.GetOption('main', 'EditorWindow', 'font-bold', type='bool'):              fontWeight='bold'          text.config(font=(idleConf.GetOption('main', 'EditorWindow', 'font'), -                          idleConf.GetOption('main', 'EditorWindow', 'font-size'), +                          idleConf.GetOption('main', 'EditorWindow', +                                             'font-size', type='int'),                            fontWeight))          text_frame.pack(side=LEFT, fill=BOTH, expand=1)          text.pack(side=TOP, fill=BOTH, expand=1) @@ -214,7 +271,8 @@ class EditorWindow(object):          # Although use-spaces=0 can be configured manually in config-main.def,          # configuration of tabs v. spaces is not supported in the configuration          # dialog.  IDLE promotes the preferred Python indentation: use spaces! -        usespaces = idleConf.GetOption('main', 'Indent', 'use-spaces', type='bool') +        usespaces = idleConf.GetOption('main', 'Indent', +                                       'use-spaces', type='bool')          self.usetabs = not usespaces          # tabwidth is the display width of a literal tab character. @@ -328,9 +386,11 @@ class EditorWindow(object):              self.text.tag_remove("sel", "1.0", "end")          else:              if not self.text.index("sel.first"): -                self.text.mark_set("my_anchor", "insert")  # there was no previous selection +                # there was no previous selection +                self.text.mark_set("my_anchor", "insert")              else: -                if self.text.compare(self.text.index("sel.first"), "<", self.text.index("insert")): +                if self.text.compare(self.text.index("sel.first"), "<", +                                     self.text.index("insert")):                      self.text.mark_set("my_anchor", "sel.first") # extend back                  else:                      self.text.mark_set("my_anchor", "sel.last") # extend forward @@ -385,7 +445,7 @@ class EditorWindow(object):              underline, label = prepstr(label)              menudict[name] = menu = Menu(mbar, name=name)              mbar.add_cascade(label=label, menu=menu, underline=underline) -        if macosxSupport.runningAsOSXApp(): +        if macosxSupport.isCarbonAquaTk(self.root):              # Insert the application menu              menudict['application'] = menu = Menu(mbar, name='apple')              mbar.add_cascade(label='IDLE', menu=menu) @@ -410,7 +470,6 @@ class EditorWindow(object):      rmenu = None      def right_menu_event(self, event): -        self.text.tag_remove("sel", "1.0", "end")          self.text.mark_set("insert", "@%d,%d" % (event.x, event.y))          if not self.rmenu:              self.make_rmenu() @@ -419,23 +478,53 @@ class EditorWindow(object):          iswin = sys.platform[:3] == 'win'          if iswin:              self.text.config(cursor="arrow") + +        for label, eventname, verify_state in self.rmenu_specs: +            if verify_state is None: +                continue +            state = getattr(self, verify_state)() +            rmenu.entryconfigure(label, state=state) + +          rmenu.tk_popup(event.x_root, event.y_root)          if iswin:              self.text.config(cursor="ibeam")      rmenu_specs = [ -        # ("Label", "<<virtual-event>>"), ... -        ("Close", "<<close-window>>"), # Example +        # ("Label", "<<virtual-event>>", "statefuncname"), ... +        ("Close", "<<close-window>>", None), # Example      ]      def make_rmenu(self):          rmenu = Menu(self.text, tearoff=0) -        for label, eventname in self.rmenu_specs: -            def command(text=self.text, eventname=eventname): -                text.event_generate(eventname) -            rmenu.add_command(label=label, command=command) +        for label, eventname, _ in self.rmenu_specs: +            if label is not None: +                def command(text=self.text, eventname=eventname): +                    text.event_generate(eventname) +                rmenu.add_command(label=label, command=command) +            else: +                rmenu.add_separator()          self.rmenu = rmenu +    def rmenu_check_cut(self): +        return self.rmenu_check_copy() + +    def rmenu_check_copy(self): +        try: +            indx = self.text.index('sel.first') +        except TclError: +            return 'disabled' +        else: +            return 'normal' if indx else 'disabled' + +    def rmenu_check_paste(self): +        try: +            self.text.tk.call('tk::GetSelection', self.text, 'CLIPBOARD') +        except TclError: +            return 'disabled' +        else: +            return 'normal' +      def about_dialog(self, event=None):          aboutDialog.AboutDialog(self.top,'About IDLE') @@ -443,8 +532,11 @@ class EditorWindow(object):          configDialog.ConfigDialog(self.top,'Settings')      def help_dialog(self, event=None): -        fn=os.path.join(os.path.abspath(os.path.dirname(__file__)),'help.txt') -        textView.view_file(self.top,'Help',fn) +        if self.root: +            parent = self.root +        else: +            parent = self.top +        helpDialog.display(parent, near=self.top)      def python_docs(self, event=None):          if sys.platform[:3] == 'win': @@ -680,7 +772,8 @@ class EditorWindow(object):          if idleConf.GetOption('main','EditorWindow','font-bold',type='bool'):              fontWeight='bold'          self.text.config(font=(idleConf.GetOption('main','EditorWindow','font'), -                idleConf.GetOption('main','EditorWindow','font-size'), +                idleConf.GetOption('main','EditorWindow','font-size', +                                   type='int'),                  fontWeight))      def RemoveKeybindings(self): @@ -789,18 +882,23 @@ class EditorWindow(object):          rf_list = [path for path in rf_list if path not in bad_paths]          ulchars = "1234567890ABCDEFGHIJK"          rf_list = rf_list[0:len(ulchars)] -        rf_file = open(self.recent_files_path, 'w', -                        encoding='utf_8', errors='replace')          try: -            rf_file.writelines(rf_list) -        finally: -            rf_file.close() +            with open(self.recent_files_path, 'w', +                        encoding='utf_8', errors='replace') as rf_file: +                rf_file.writelines(rf_list) +        except IOError as err: +            if not getattr(self.root, "recentfilelist_error_displayed", False): +                self.root.recentfilelist_error_displayed = True +                tkMessageBox.showerror(title='IDLE Error', +                    message='Unable to update Recent Files list:\n%s' +                        % str(err), +                    parent=self.text)          # for each edit window instance, construct the recent files menu          for instance in self.top.instance_dict:              menu = instance.recent_files_menu -            menu.delete(1, END)  # clear, and rebuild: -            for i, file in zip(count(), rf_list): -                file_name = file[0:-1]  # zap \n +            menu.delete(0, END)  # clear, and rebuild: +            for i, file_name in enumerate(rf_list): +                file_name = file_name.rstrip()  # zap \n                  # make unicode string to display non-ASCII chars correctly                  ufile_name = self._filename_to_unicode(file_name)                  callback = instance.__recent_file_callback(file_name) @@ -1119,7 +1217,10 @@ class EditorWindow(object):          assert have > 0          want = ((have - 1) // self.indentwidth) * self.indentwidth          # Debug prompt is multilined.... -        last_line_of_prompt = sys.ps1.split('\n')[-1] +        if self.context_use_ps1: +            last_line_of_prompt = sys.ps1.split('\n')[-1] +        else: +            last_line_of_prompt = ''          ncharsdeleted = 0          while 1:              if chars == last_line_of_prompt: @@ -1517,7 +1618,7 @@ class IndentSearcher(object):                  tokens = _tokenize.generate_tokens(self.readline)                  for token in tokens:                      self.tokeneater(*token) -            except _tokenize.TokenError: +            except (_tokenize.TokenError, SyntaxError):                  # since we cut off the tokenizer early, we can trigger                  # spurious errors                  pass @@ -1544,7 +1645,12 @@ keynames = {  def get_accelerator(keydefs, eventname):      keylist = keydefs.get(eventname) -    if not keylist: +    # issue10940: temporary workaround to prevent hang with OS X Cocoa Tk 8.5 +    # if not keylist: +    if (not keylist) or (macosxSupport.runningAsOSXApp() and eventname in { +                            "<<open-module>>", +                            "<<goto-line>>", +                            "<<change-indentwidth>>"}):          return ""      s = keylist[0]      s = re.sub(r"-[a-z]\b", lambda m: m.group().upper(), s)  | 
