PyXR

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



0001 import sys
0002 import os
0003 import re
0004 import imp
0005 from itertools import count
0006 from Tkinter import *
0007 import tkSimpleDialog
0008 import tkMessageBox
0009 
0010 import webbrowser
0011 import idlever
0012 import WindowList
0013 import SearchDialog
0014 import GrepDialog
0015 import ReplaceDialog
0016 import PyParse
0017 from configHandler import idleConf
0018 import aboutDialog, textView, configDialog
0019 
0020 # The default tab setting for a Text widget, in average-width characters.
0021 TK_TABWIDTH_DEFAULT = 8
0022 
0023 def _find_module(fullname, path=None):
0024     """Version of imp.find_module() that handles hierarchical module names"""
0025 
0026     file = None
0027     for tgt in fullname.split('.'):
0028         if file is not None:
0029             file.close()            # close intermediate files
0030         (file, filename, descr) = imp.find_module(tgt, path)
0031         if descr[2] == imp.PY_SOURCE:
0032             break                   # find but not load the source file
0033         module = imp.load_module(tgt, file, filename, descr)
0034         try:
0035             path = module.__path__
0036         except AttributeError:
0037             raise ImportError, 'No source for module ' + module.__name__
0038     return file, filename, descr
0039 
0040 class EditorWindow:
0041     from Percolator import Percolator
0042     from ColorDelegator import ColorDelegator
0043     from UndoDelegator import UndoDelegator
0044     from IOBinding import IOBinding
0045     import Bindings
0046     from Tkinter import Toplevel
0047     from MultiStatusBar import MultiStatusBar
0048 
0049     help_url = None
0050 
0051     def __init__(self, flist=None, filename=None, key=None, root=None):
0052         if EditorWindow.help_url is None:
0053             dochome =  os.path.join(sys.prefix, 'Doc', 'index.html')
0054             if sys.platform.count('linux'):
0055                 # look for html docs in a couple of standard places
0056                 pyver = 'python-docs-' + '%s.%s.%s' % sys.version_info[:3]
0057                 if os.path.isdir('/var/www/html/python/'):  # "python2" rpm
0058                     dochome = '/var/www/html/python/index.html'
0059                 else:
0060                     basepath = '/usr/share/doc/'  # standard location
0061                     dochome = os.path.join(basepath, pyver,
0062                                            'Doc', 'index.html')
0063             elif sys.platform[:3] == 'win':
0064                 chmfile = os.path.join(sys.prefix, 'Doc',
0065                                        'Python%d%d.chm' % sys.version_info[:2])
0066                 if os.path.isfile(chmfile):
0067                     dochome = chmfile
0068             dochome = os.path.normpath(dochome)
0069             if os.path.isfile(dochome):
0070                 EditorWindow.help_url = dochome
0071             else:
0072                 EditorWindow.help_url = "http://www.python.org/doc/current"
0073         currentTheme=idleConf.CurrentTheme()
0074         self.flist = flist
0075         root = root or flist.root
0076         self.root = root
0077         self.menubar = Menu(root)
0078         self.top = top = WindowList.ListedToplevel(root, menu=self.menubar)
0079         if flist:
0080             self.tkinter_vars = flist.vars
0081             #self.top.instance_dict makes flist.inversedict avalable to
0082             #configDialog.py so it can access all EditorWindow instaces
0083             self.top.instance_dict=flist.inversedict
0084         else:
0085             self.tkinter_vars = {}  # keys: Tkinter event names
0086                                     # values: Tkinter variable instances
0087         self.recent_files_path=os.path.join(idleConf.GetUserCfgDir(),
0088                 'recent-files.lst')
0089         self.vbar = vbar = Scrollbar(top, name='vbar')
0090         self.text_frame = text_frame = Frame(top)
0091         self.width = idleConf.GetOption('main','EditorWindow','width')
0092         self.text = text = Text(text_frame, name='text', padx=5, wrap='none',
0093                 foreground=idleConf.GetHighlight(currentTheme,
0094                         'normal',fgBg='fg'),
0095                 background=idleConf.GetHighlight(currentTheme,
0096                         'normal',fgBg='bg'),
0097                 highlightcolor=idleConf.GetHighlight(currentTheme,
0098                         'hilite',fgBg='fg'),
0099                 highlightbackground=idleConf.GetHighlight(currentTheme,
0100                         'hilite',fgBg='bg'),
0101                 insertbackground=idleConf.GetHighlight(currentTheme,
0102                         'cursor',fgBg='fg'),
0103                 width=self.width,
0104                 height=idleConf.GetOption('main','EditorWindow','height') )
0105         self.top.focused_widget = self.text
0106 
0107         self.createmenubar()
0108         self.apply_bindings()
0109 
0110         self.top.protocol("WM_DELETE_WINDOW", self.close)
0111         self.top.bind("<<close-window>>", self.close_event)
0112         text.bind("<<cut>>", self.cut)
0113         text.bind("<<copy>>", self.copy)
0114         text.bind("<<paste>>", self.paste)
0115         text.bind("<<center-insert>>", self.center_insert_event)
0116         text.bind("<<help>>", self.help_dialog)
0117         text.bind("<<python-docs>>", self.python_docs)
0118         text.bind("<<about-idle>>", self.about_dialog)
0119         text.bind("<<open-config-dialog>>", self.config_dialog)
0120         text.bind("<<open-module>>", self.open_module)
0121         text.bind("<<do-nothing>>", lambda event: "break")
0122         text.bind("<<select-all>>", self.select_all)
0123         text.bind("<<remove-selection>>", self.remove_selection)
0124         text.bind("<<find>>", self.find_event)
0125         text.bind("<<find-again>>", self.find_again_event)
0126         text.bind("<<find-in-files>>", self.find_in_files_event)
0127         text.bind("<<find-selection>>", self.find_selection_event)
0128         text.bind("<<replace>>", self.replace_event)
0129         text.bind("<<goto-line>>", self.goto_line_event)
0130         text.bind("<3>", self.right_menu_event)
0131         text.bind("<<smart-backspace>>",self.smart_backspace_event)
0132         text.bind("<<newline-and-indent>>",self.newline_and_indent_event)
0133         text.bind("<<smart-indent>>",self.smart_indent_event)
0134         text.bind("<<indent-region>>",self.indent_region_event)
0135         text.bind("<<dedent-region>>",self.dedent_region_event)
0136         text.bind("<<comment-region>>",self.comment_region_event)
0137         text.bind("<<uncomment-region>>",self.uncomment_region_event)
0138         text.bind("<<tabify-region>>",self.tabify_region_event)
0139         text.bind("<<untabify-region>>",self.untabify_region_event)
0140         text.bind("<<toggle-tabs>>",self.toggle_tabs_event)
0141         text.bind("<<change-indentwidth>>",self.change_indentwidth_event)
0142         text.bind("<Left>", self.move_at_edge_if_selection(0))
0143         text.bind("<Right>", self.move_at_edge_if_selection(1))
0144 
0145         if flist:
0146             flist.inversedict[self] = key
0147             if key:
0148                 flist.dict[key] = self
0149             text.bind("<<open-new-window>>", self.new_callback)
0150             text.bind("<<close-all-windows>>", self.flist.close_all_callback)
0151             text.bind("<<open-class-browser>>", self.open_class_browser)
0152             text.bind("<<open-path-browser>>", self.open_path_browser)
0153 
0154         self.set_status_bar()
0155         vbar['command'] = text.yview
0156         vbar.pack(side=RIGHT, fill=Y)
0157         text['yscrollcommand'] = vbar.set
0158         fontWeight='normal'
0159         if idleConf.GetOption('main','EditorWindow','font-bold',type='bool'):
0160             fontWeight='bold'
0161         text.config(font=(idleConf.GetOption('main','EditorWindow','font'),
0162                 idleConf.GetOption('main','EditorWindow','font-size'),
0163                 fontWeight))
0164         text_frame.pack(side=LEFT, fill=BOTH, expand=1)
0165         text.pack(side=TOP, fill=BOTH, expand=1)
0166         text.focus_set()
0167 
0168         self.per = per = self.Percolator(text)
0169         if self.ispythonsource(filename):
0170             self.color = color = self.ColorDelegator()
0171             per.insertfilter(color)
0172         else:
0173             self.color = None
0174 
0175         self.undo = undo = self.UndoDelegator()
0176         per.insertfilter(undo)
0177         text.undo_block_start = undo.undo_block_start
0178         text.undo_block_stop = undo.undo_block_stop
0179         undo.set_saved_change_hook(self.saved_change_hook)
0180 
0181         # IOBinding implements file I/O and printing functionality
0182         self.io = io = self.IOBinding(self)
0183         io.set_filename_change_hook(self.filename_change_hook)
0184 
0185         # Create the recent files submenu
0186         self.recent_files_menu = Menu(self.menubar)
0187         self.menudict['file'].insert_cascade(3, label='Recent Files',
0188                                              underline=0,
0189                                              menu=self.recent_files_menu)
0190         self.update_recent_files_list()
0191 
0192         if filename:
0193             if os.path.exists(filename) and not os.path.isdir(filename):
0194                 io.loadfile(filename)
0195             else:
0196                 io.set_filename(filename)
0197         self.saved_change_hook()
0198 
0199         self.load_extensions()
0200 
0201         menu = self.menudict.get('windows')
0202         if menu:
0203             end = menu.index("end")
0204             if end is None:
0205                 end = -1
0206             if end >= 0:
0207                 menu.add_separator()
0208                 end = end + 1
0209             self.wmenu_end = end
0210             WindowList.register_callback(self.postwindowsmenu)
0211 
0212         # Some abstractions so IDLE extensions are cross-IDE
0213         self.askyesno = tkMessageBox.askyesno
0214         self.askinteger = tkSimpleDialog.askinteger
0215         self.showerror = tkMessageBox.showerror
0216 
0217         if self.extensions.has_key('AutoIndent'):
0218             self.extensions['AutoIndent'].set_indentation_params(
0219                 self.ispythonsource(filename))
0220 
0221     def new_callback(self, event):
0222         dirname, basename = self.io.defaultfilename()
0223         self.flist.new(dirname)
0224         return "break"
0225 
0226     def set_status_bar(self):
0227         self.status_bar = self.MultiStatusBar(self.top)
0228         self.status_bar.set_label('column', 'Col: ?', side=RIGHT)
0229         self.status_bar.set_label('line', 'Ln: ?', side=RIGHT)
0230         self.status_bar.pack(side=BOTTOM, fill=X)
0231         self.text.bind('<KeyRelease>', self.set_line_and_column)
0232         self.text.bind('<ButtonRelease>', self.set_line_and_column)
0233         self.text.after_idle(self.set_line_and_column)
0234 
0235     def set_line_and_column(self, event=None):
0236         line, column = self.text.index(INSERT).split('.')
0237         self.status_bar.set_label('column', 'Col: %s' % column)
0238         self.status_bar.set_label('line', 'Ln: %s' % line)
0239 
0240     menu_specs = [
0241         ("file", "_File"),
0242         ("edit", "_Edit"),
0243         ("format", "F_ormat"),
0244         ("run", "_Run"),
0245         ("options", "_Options"),
0246         ("windows", "_Windows"),
0247         ("help", "_Help"),
0248     ]
0249 
0250     def createmenubar(self):
0251         mbar = self.menubar
0252         self.menudict = menudict = {}
0253         for name, label in self.menu_specs:
0254             underline, label = prepstr(label)
0255             menudict[name] = menu = Menu(mbar, name=name)
0256             mbar.add_cascade(label=label, menu=menu, underline=underline)
0257         self.fill_menus()
0258         self.base_helpmenu_length = self.menudict['help'].index(END)
0259         self.reset_help_menu_entries()
0260 
0261     def postwindowsmenu(self):
0262         # Only called when Windows menu exists
0263         menu = self.menudict['windows']
0264         end = menu.index("end")
0265         if end is None:
0266             end = -1
0267         if end > self.wmenu_end:
0268             menu.delete(self.wmenu_end+1, end)
0269         WindowList.add_windows_to_menu(menu)
0270 
0271     rmenu = None
0272 
0273     def right_menu_event(self, event):
0274         self.text.tag_remove("sel", "1.0", "end")
0275         self.text.mark_set("insert", "@%d,%d" % (event.x, event.y))
0276         if not self.rmenu:
0277             self.make_rmenu()
0278         rmenu = self.rmenu
0279         self.event = event
0280         iswin = sys.platform[:3] == 'win'
0281         if iswin:
0282             self.text.config(cursor="arrow")
0283         rmenu.tk_popup(event.x_root, event.y_root)
0284         if iswin:
0285             self.text.config(cursor="ibeam")
0286 
0287     rmenu_specs = [
0288         # ("Label", "<<virtual-event>>"), ...
0289         ("Close", "<<close-window>>"), # Example
0290     ]
0291 
0292     def make_rmenu(self):
0293         rmenu = Menu(self.text, tearoff=0)
0294         for label, eventname in self.rmenu_specs:
0295             def command(text=self.text, eventname=eventname):
0296                 text.event_generate(eventname)
0297             rmenu.add_command(label=label, command=command)
0298         self.rmenu = rmenu
0299 
0300     def about_dialog(self, event=None):
0301         aboutDialog.AboutDialog(self.top,'About IDLE')
0302 
0303     def config_dialog(self, event=None):
0304         configDialog.ConfigDialog(self.top,'Settings')
0305 
0306     def help_dialog(self, event=None):
0307         fn=os.path.join(os.path.abspath(os.path.dirname(__file__)),'help.txt')
0308         textView.TextViewer(self.top,'Help',fn)
0309 
0310     def python_docs(self, event=None):
0311         if sys.platform[:3] == 'win':
0312             os.startfile(self.help_url)
0313         else:
0314             webbrowser.open(self.help_url)
0315         return "break"
0316 
0317     def cut(self,event):
0318         self.text.event_generate("<<Cut>>")
0319         return "break"
0320 
0321     def copy(self,event):
0322         self.text.event_generate("<<Copy>>")
0323         return "break"
0324 
0325     def paste(self,event):
0326         self.text.event_generate("<<Paste>>")
0327         return "break"
0328 
0329     def select_all(self, event=None):
0330         self.text.tag_add("sel", "1.0", "end-1c")
0331         self.text.mark_set("insert", "1.0")
0332         self.text.see("insert")
0333         return "break"
0334 
0335     def remove_selection(self, event=None):
0336         self.text.tag_remove("sel", "1.0", "end")
0337         self.text.see("insert")
0338 
0339     def move_at_edge_if_selection(self, edge_index):
0340         """Cursor move begins at start or end of selection
0341 
0342         When a left/right cursor key is pressed create and return to Tkinter a
0343         function which causes a cursor move from the associated edge of the
0344         selection.
0345 
0346         """
0347         self_text_index = self.text.index
0348         self_text_mark_set = self.text.mark_set
0349         edges_table = ("sel.first+1c", "sel.last-1c")
0350         def move_at_edge(event):
0351             if (event.state & 5) == 0: # no shift(==1) or control(==4) pressed
0352                 try:
0353                     self_text_index("sel.first")
0354                     self_text_mark_set("insert", edges_table[edge_index])
0355                 except TclError:
0356                     pass
0357         return move_at_edge
0358 
0359     def find_event(self, event):
0360         SearchDialog.find(self.text)
0361         return "break"
0362 
0363     def find_again_event(self, event):
0364         SearchDialog.find_again(self.text)
0365         return "break"
0366 
0367     def find_selection_event(self, event):
0368         SearchDialog.find_selection(self.text)
0369         return "break"
0370 
0371     def find_in_files_event(self, event):
0372         GrepDialog.grep(self.text, self.io, self.flist)
0373         return "break"
0374 
0375     def replace_event(self, event):
0376         ReplaceDialog.replace(self.text)
0377         return "break"
0378 
0379     def goto_line_event(self, event):
0380         text = self.text
0381         lineno = tkSimpleDialog.askinteger("Goto",
0382                 "Go to line number:",parent=text)
0383         if lineno is None:
0384             return "break"
0385         if lineno <= 0:
0386             text.bell()
0387             return "break"
0388         text.mark_set("insert", "%d.0" % lineno)
0389         text.see("insert")
0390 
0391     def open_module(self, event=None):
0392         # XXX Shouldn't this be in IOBinding or in FileList?
0393         try:
0394             name = self.text.get("sel.first", "sel.last")
0395         except TclError:
0396             name = ""
0397         else:
0398             name = name.strip()
0399         name = tkSimpleDialog.askstring("Module",
0400                  "Enter the name of a Python module\n"
0401                  "to search on sys.path and open:",
0402                  parent=self.text, initialvalue=name)
0403         if name:
0404             name = name.strip()
0405         if not name:
0406             return
0407         # XXX Ought to insert current file's directory in front of path
0408         try:
0409             (f, file, (suffix, mode, type)) = _find_module(name)
0410         except (NameError, ImportError), msg:
0411             tkMessageBox.showerror("Import error", str(msg), parent=self.text)
0412             return
0413         if type != imp.PY_SOURCE:
0414             tkMessageBox.showerror("Unsupported type",
0415                 "%s is not a source module" % name, parent=self.text)
0416             return
0417         if f:
0418             f.close()
0419         if self.flist:
0420             self.flist.open(file)
0421         else:
0422             self.io.loadfile(file)
0423 
0424     def open_class_browser(self, event=None):
0425         filename = self.io.filename
0426         if not filename:
0427             tkMessageBox.showerror(
0428                 "No filename",
0429                 "This buffer has no associated filename",
0430                 master=self.text)
0431             self.text.focus_set()
0432             return None
0433         head, tail = os.path.split(filename)
0434         base, ext = os.path.splitext(tail)
0435         import ClassBrowser
0436         ClassBrowser.ClassBrowser(self.flist, base, [head])
0437 
0438     def open_path_browser(self, event=None):
0439         import PathBrowser
0440         PathBrowser.PathBrowser(self.flist)
0441 
0442     def gotoline(self, lineno):
0443         if lineno is not None and lineno > 0:
0444             self.text.mark_set("insert", "%d.0" % lineno)
0445             self.text.tag_remove("sel", "1.0", "end")
0446             self.text.tag_add("sel", "insert", "insert +1l")
0447             self.center()
0448 
0449     def ispythonsource(self, filename):
0450         if not filename:
0451             return True
0452         base, ext = os.path.splitext(os.path.basename(filename))
0453         if os.path.normcase(ext) in (".py", ".pyw"):
0454             return True
0455         try:
0456             f = open(filename)
0457             line = f.readline()
0458             f.close()
0459         except IOError:
0460             return False
0461         return line.startswith('#!') and line.find('python') >= 0
0462 
0463     def close_hook(self):
0464         if self.flist:
0465             self.flist.close_edit(self)
0466 
0467     def set_close_hook(self, close_hook):
0468         self.close_hook = close_hook
0469 
0470     def filename_change_hook(self):
0471         if self.flist:
0472             self.flist.filename_changed_edit(self)
0473         self.saved_change_hook()
0474         self.top.update_windowlist_registry(self)
0475         if self.ispythonsource(self.io.filename):
0476             self.addcolorizer()
0477         else:
0478             self.rmcolorizer()
0479 
0480     def addcolorizer(self):
0481         if self.color:
0482             return
0483         self.per.removefilter(self.undo)
0484         self.color = self.ColorDelegator()
0485         self.per.insertfilter(self.color)
0486         self.per.insertfilter(self.undo)
0487 
0488     def rmcolorizer(self):
0489         if not self.color:
0490             return
0491         self.per.removefilter(self.undo)
0492         self.per.removefilter(self.color)
0493         self.color = None
0494         self.per.insertfilter(self.undo)
0495 
0496     def ResetColorizer(self):
0497         "Update the colour theme if it is changed"
0498         # Called from configDialog.py
0499         if self.color:
0500             self.color = self.ColorDelegator()
0501             self.per.insertfilter(self.color)
0502         theme = idleConf.GetOption('main','Theme','name')
0503         self.text.config(idleConf.GetHighlight(theme, "normal"))
0504 
0505     def ResetFont(self):
0506         "Update the text widgets' font if it is changed"
0507         # Called from configDialog.py
0508         fontWeight='normal'
0509         if idleConf.GetOption('main','EditorWindow','font-bold',type='bool'):
0510             fontWeight='bold'
0511         self.text.config(font=(idleConf.GetOption('main','EditorWindow','font'),
0512                 idleConf.GetOption('main','EditorWindow','font-size'),
0513                 fontWeight))
0514 
0515     def ResetKeybindings(self):
0516         "Update the keybindings if they are changed"
0517         # Called from configDialog.py
0518         self.Bindings.default_keydefs=idleConf.GetCurrentKeySet()
0519         keydefs = self.Bindings.default_keydefs
0520         for event, keylist in keydefs.items():
0521             self.text.event_delete(event)
0522         self.apply_bindings()
0523         #update menu accelerators
0524         menuEventDict={}
0525         for menu in self.Bindings.menudefs:
0526             menuEventDict[menu[0]]={}
0527             for item in menu[1]:
0528                 if item:
0529                     menuEventDict[menu[0]][prepstr(item[0])[1]]=item[1]
0530         for menubarItem in self.menudict.keys():
0531             menu=self.menudict[menubarItem]
0532             end=menu.index(END)+1
0533             for index in range(0,end):
0534                 if menu.type(index)=='command':
0535                     accel=menu.entrycget(index,'accelerator')
0536                     if accel:
0537                         itemName=menu.entrycget(index,'label')
0538                         event=''
0539                         if menuEventDict.has_key(menubarItem):
0540                             if menuEventDict[menubarItem].has_key(itemName):
0541                                 event=menuEventDict[menubarItem][itemName]
0542                         if event:
0543                             accel=get_accelerator(keydefs, event)
0544                             menu.entryconfig(index,accelerator=accel)
0545 
0546     def reset_help_menu_entries(self):
0547         "Update the additional help entries on the Help menu"
0548         help_list = idleConf.GetAllExtraHelpSourcesList()
0549         helpmenu = self.menudict['help']
0550         # first delete the extra help entries, if any
0551         helpmenu_length = helpmenu.index(END)
0552         if helpmenu_length > self.base_helpmenu_length:
0553             helpmenu.delete((self.base_helpmenu_length + 1), helpmenu_length)
0554         # then rebuild them
0555         if help_list:
0556             helpmenu.add_separator()
0557             for entry in help_list:
0558                 cmd = self.__extra_help_callback(entry[1])
0559                 helpmenu.add_command(label=entry[0], command=cmd)
0560         # and update the menu dictionary
0561         self.menudict['help'] = helpmenu
0562 
0563     def __extra_help_callback(self, helpfile):
0564         "Create a callback with the helpfile value frozen at definition time"
0565         def display_extra_help(helpfile=helpfile):
0566             if not (helpfile.startswith('www') or helpfile.startswith('http')):
0567                 url = os.path.normpath(helpfile)
0568             if sys.platform[:3] == 'win':
0569                 os.startfile(helpfile)
0570             else:
0571                 webbrowser.open(helpfile)
0572         return display_extra_help
0573 
0574     def update_recent_files_list(self, new_file=None):
0575         "Load and update the recent files list and menus"
0576         rf_list = []
0577         if os.path.exists(self.recent_files_path):
0578             rf_list_file = open(self.recent_files_path,'r')
0579             try:
0580                 rf_list = rf_list_file.readlines()
0581             finally:
0582                 rf_list_file.close()
0583         if new_file:
0584             new_file = os.path.abspath(new_file) + '\n'
0585             if new_file in rf_list:
0586                 rf_list.remove(new_file)  # move to top
0587             rf_list.insert(0, new_file)
0588         # clean and save the recent files list
0589         bad_paths = []
0590         for path in rf_list:
0591             if '\0' in path or not os.path.exists(path[0:-1]):
0592                 bad_paths.append(path)
0593         rf_list = [path for path in rf_list if path not in bad_paths]
0594         ulchars = "1234567890ABCDEFGHIJK"
0595         rf_list = rf_list[0:len(ulchars)]
0596         rf_file = open(self.recent_files_path, 'w')
0597         try:
0598             rf_file.writelines(rf_list)
0599         finally:
0600             rf_file.close()
0601         # for each edit window instance, construct the recent files menu
0602         for instance in self.top.instance_dict.keys():
0603             menu = instance.recent_files_menu
0604             menu.delete(1, END)  # clear, and rebuild:
0605             for i, file in zip(count(), rf_list):
0606                 file_name = file[0:-1]  # zap \n
0607                 callback = instance.__recent_file_callback(file_name)
0608                 menu.add_command(label=ulchars[i] + " " + file_name,
0609                                  command=callback,
0610                                  underline=0)
0611 
0612     def __recent_file_callback(self, file_name):
0613         def open_recent_file(fn_closure=file_name):
0614             self.io.open(editFile=fn_closure)
0615         return open_recent_file
0616 
0617     def saved_change_hook(self):
0618         short = self.short_title()
0619         long = self.long_title()
0620         if short and long:
0621             title = short + " - " + long
0622         elif short:
0623             title = short
0624         elif long:
0625             title = long
0626         else:
0627             title = "Untitled"
0628         icon = short or long or title
0629         if not self.get_saved():
0630             title = "*%s*" % title
0631             icon = "*%s" % icon
0632         self.top.wm_title(title)
0633         self.top.wm_iconname(icon)
0634 
0635     def get_saved(self):
0636         return self.undo.get_saved()
0637 
0638     def set_saved(self, flag):
0639         self.undo.set_saved(flag)
0640 
0641     def reset_undo(self):
0642         self.undo.reset_undo()
0643 
0644     def short_title(self):
0645         filename = self.io.filename
0646         if filename:
0647             filename = os.path.basename(filename)
0648         return filename
0649 
0650     def long_title(self):
0651         return self.io.filename or ""
0652 
0653     def center_insert_event(self, event):
0654         self.center()
0655 
0656     def center(self, mark="insert"):
0657         text = self.text
0658         top, bot = self.getwindowlines()
0659         lineno = self.getlineno(mark)
0660         height = bot - top
0661         newtop = max(1, lineno - height//2)
0662         text.yview(float(newtop))
0663 
0664     def getwindowlines(self):
0665         text = self.text
0666         top = self.getlineno("@0,0")
0667         bot = self.getlineno("@0,65535")
0668         if top == bot and text.winfo_height() == 1:
0669             # Geometry manager hasn't run yet
0670             height = int(text['height'])
0671             bot = top + height - 1
0672         return top, bot
0673 
0674     def getlineno(self, mark="insert"):
0675         text = self.text
0676         return int(float(text.index(mark)))
0677 
0678     def get_geometry(self):
0679         "Return (width, height, x, y)"
0680         geom = self.top.wm_geometry()
0681         m = re.match(r"(\d+)x(\d+)\+(-?\d+)\+(-?\d+)", geom)
0682         tuple = (map(int, m.groups()))
0683         return tuple
0684 
0685     def close_event(self, event):
0686         self.close()
0687 
0688     def maybesave(self):
0689         if self.io:
0690             if not self.get_saved():
0691                 if self.top.state()!='normal':
0692                     self.top.deiconify()
0693                 self.top.lower()
0694                 self.top.lift()
0695             return self.io.maybesave()
0696 
0697     def close(self):
0698         reply = self.maybesave()
0699         if reply != "cancel":
0700             self._close()
0701         return reply
0702 
0703     def _close(self):
0704         if self.io.filename:
0705             self.update_recent_files_list(new_file=self.io.filename)
0706         WindowList.unregister_callback(self.postwindowsmenu)
0707         if self.close_hook:
0708             self.close_hook()
0709         self.flist = None
0710         colorizing = 0
0711         self.unload_extensions()
0712         self.io.close(); self.io = None
0713         self.undo = None # XXX
0714         if self.color:
0715             colorizing = self.color.colorizing
0716             doh = colorizing and self.top
0717             self.color.close(doh) # Cancel colorization
0718         self.text = None
0719         self.tkinter_vars = None
0720         self.per.close(); self.per = None
0721         if not colorizing:
0722             self.top.destroy()
0723 
0724     def load_extensions(self):
0725         self.extensions = {}
0726         self.load_standard_extensions()
0727 
0728     def unload_extensions(self):
0729         for ins in self.extensions.values():
0730             if hasattr(ins, "close"):
0731                 ins.close()
0732         self.extensions = {}
0733 
0734     def load_standard_extensions(self):
0735         for name in self.get_standard_extension_names():
0736             try:
0737                 self.load_extension(name)
0738             except:
0739                 print "Failed to load extension", repr(name)
0740                 import traceback
0741                 traceback.print_exc()
0742 
0743     def get_standard_extension_names(self):
0744         return idleConf.GetExtensions(editor_only=True)
0745 
0746     def load_extension(self, name):
0747         mod = __import__(name, globals(), locals(), [])
0748         cls = getattr(mod, name)
0749         keydefs = idleConf.GetExtensionBindings(name)
0750         if hasattr(cls, "menudefs"):
0751             self.fill_menus(cls.menudefs, keydefs)
0752         ins = cls(self)
0753         self.extensions[name] = ins
0754         if keydefs:
0755             self.apply_bindings(keydefs)
0756             for vevent in keydefs.keys():
0757                 methodname = vevent.replace("-", "_")
0758                 while methodname[:1] == '<':
0759                     methodname = methodname[1:]
0760                 while methodname[-1:] == '>':
0761                     methodname = methodname[:-1]
0762                 methodname = methodname + "_event"
0763                 if hasattr(ins, methodname):
0764                     self.text.bind(vevent, getattr(ins, methodname))
0765         return ins
0766 
0767     def apply_bindings(self, keydefs=None):
0768         if keydefs is None:
0769             keydefs = self.Bindings.default_keydefs
0770         text = self.text
0771         text.keydefs = keydefs
0772         for event, keylist in keydefs.items():
0773             if keylist:
0774                 text.event_add(event, *keylist)
0775 
0776     def fill_menus(self, menudefs=None, keydefs=None):
0777         """Add appropriate entries to the menus and submenus
0778 
0779         Menus that are absent or None in self.menudict are ignored.
0780         """
0781         if menudefs is None:
0782             menudefs = self.Bindings.menudefs
0783         if keydefs is None:
0784             keydefs = self.Bindings.default_keydefs
0785         menudict = self.menudict
0786         text = self.text
0787         for mname, entrylist in menudefs:
0788             menu = menudict.get(mname)
0789             if not menu:
0790                 continue
0791             for entry in entrylist:
0792                 if not entry:
0793                     menu.add_separator()
0794                 else:
0795                     label, eventname = entry
0796                     checkbutton = (label[:1] == '!')
0797                     if checkbutton:
0798                         label = label[1:]
0799                     underline, label = prepstr(label)
0800                     accelerator = get_accelerator(keydefs, eventname)
0801                     def command(text=text, eventname=eventname):
0802                         text.event_generate(eventname)
0803                     if checkbutton:
0804                         var = self.get_var_obj(eventname, BooleanVar)
0805                         menu.add_checkbutton(label=label, underline=underline,
0806                             command=command, accelerator=accelerator,
0807                             variable=var)
0808                     else:
0809                         menu.add_command(label=label, underline=underline,
0810                                          command=command,
0811                                          accelerator=accelerator)
0812 
0813     def getvar(self, name):
0814         var = self.get_var_obj(name)
0815         if var:
0816             value = var.get()
0817             return value
0818         else:
0819             raise NameError, name
0820 
0821     def setvar(self, name, value, vartype=None):
0822         var = self.get_var_obj(name, vartype)
0823         if var:
0824             var.set(value)
0825         else:
0826             raise NameError, name
0827 
0828     def get_var_obj(self, name, vartype=None):
0829         var = self.tkinter_vars.get(name)
0830         if not var and vartype:
0831             # create a Tkinter variable object with self.text as master:
0832             self.tkinter_vars[name] = var = vartype(self.text)
0833         return var
0834 
0835     # Tk implementations of "virtual text methods" -- each platform
0836     # reusing IDLE's support code needs to define these for its GUI's
0837     # flavor of widget.
0838 
0839     # Is character at text_index in a Python string?  Return 0 for
0840     # "guaranteed no", true for anything else.  This info is expensive
0841     # to compute ab initio, but is probably already known by the
0842     # platform's colorizer.
0843 
0844     def is_char_in_string(self, text_index):
0845         if self.color:
0846             # Return true iff colorizer hasn't (re)gotten this far
0847             # yet, or the character is tagged as being in a string
0848             return self.text.tag_prevrange("TODO", text_index) or \
0849                    "STRING" in self.text.tag_names(text_index)
0850         else:
0851             # The colorizer is missing: assume the worst
0852             return 1
0853 
0854     # If a selection is defined in the text widget, return (start,
0855     # end) as Tkinter text indices, otherwise return (None, None)
0856     def get_selection_indices(self):
0857         try:
0858             first = self.text.index("sel.first")
0859             last = self.text.index("sel.last")
0860             return first, last
0861         except TclError:
0862             return None, None
0863 
0864     # Return the text widget's current view of what a tab stop means
0865     # (equivalent width in spaces).
0866 
0867     def get_tabwidth(self):
0868         current = self.text['tabs'] or TK_TABWIDTH_DEFAULT
0869         return int(current)
0870 
0871     # Set the text widget's current view of what a tab stop means.
0872 
0873     def set_tabwidth(self, newtabwidth):
0874         text = self.text
0875         if self.get_tabwidth() != newtabwidth:
0876             pixels = text.tk.call("font", "measure", text["font"],
0877                                   "-displayof", text.master,
0878                                   "n" * newtabwidth)
0879             text.configure(tabs=pixels)
0880 
0881 ### begin autoindent code ###
0882 
0883     # usetabs true  -> literal tab characters are used by indent and
0884     #                  dedent cmds, possibly mixed with spaces if
0885     #                  indentwidth is not a multiple of tabwidth
0886     #         false -> tab characters are converted to spaces by indent
0887     #                  and dedent cmds, and ditto TAB keystrokes
0888     # indentwidth is the number of characters per logical indent level.
0889     # tabwidth is the display width of a literal tab character.
0890     # CAUTION:  telling Tk to use anything other than its default
0891     # tab setting causes it to use an entirely different tabbing algorithm,
0892     # treating tab stops as fixed distances from the left margin.
0893     # Nobody expects this, so for now tabwidth should never be changed.
0894     usetabs = 0
0895     indentwidth = 4
0896     tabwidth = 8    # for IDLE use, must remain 8 until Tk is fixed
0897 
0898     # If context_use_ps1 is true, parsing searches back for a ps1 line;
0899     # else searches for a popular (if, def, ...) Python stmt.
0900     context_use_ps1 = 0
0901 
0902     # When searching backwards for a reliable place to begin parsing,
0903     # first start num_context_lines[0] lines back, then
0904     # num_context_lines[1] lines back if that didn't work, and so on.
0905     # The last value should be huge (larger than the # of lines in a
0906     # conceivable file).
0907     # Making the initial values larger slows things down more often.
0908     num_context_lines = 50, 500, 5000000
0909 
0910     def config(self, **options):
0911         for key, value in options.items():
0912             if key == 'usetabs':
0913                 self.usetabs = value
0914             elif key == 'indentwidth':
0915                 self.indentwidth = value
0916             elif key == 'tabwidth':
0917                 self.tabwidth = value
0918             elif key == 'context_use_ps1':
0919                 self.context_use_ps1 = value
0920             else:
0921                 raise KeyError, "bad option name: %r" % (key,)
0922 
0923     # If ispythonsource and guess are true, guess a good value for
0924     # indentwidth based on file content (if possible), and if
0925     # indentwidth != tabwidth set usetabs false.
0926     # In any case, adjust the Text widget's view of what a tab
0927     # character means.
0928 
0929     def set_indentation_params(self, ispythonsource, guess=1):
0930         if guess and ispythonsource:
0931             i = self.guess_indent()
0932             if 2 <= i <= 8:
0933                 self.indentwidth = i
0934             if self.indentwidth != self.tabwidth:
0935                 self.usetabs = 0
0936 
0937         self.set_tabwidth(self.tabwidth)
0938 
0939     def smart_backspace_event(self, event):
0940         text = self.text
0941         first, last = self.get_selection_indices()
0942         if first and last:
0943             text.delete(first, last)
0944             text.mark_set("insert", first)
0945             return "break"
0946         # Delete whitespace left, until hitting a real char or closest
0947         # preceding virtual tab stop.
0948         chars = text.get("insert linestart", "insert")
0949         if chars == '':
0950             if text.compare("insert", ">", "1.0"):
0951                 # easy: delete preceding newline
0952                 text.delete("insert-1c")
0953             else:
0954                 text.bell()     # at start of buffer
0955             return "break"
0956         if  chars[-1] not in " \t":
0957             # easy: delete preceding real char
0958             text.delete("insert-1c")
0959             return "break"
0960         # Ick.  It may require *inserting* spaces if we back up over a
0961         # tab character!  This is written to be clear, not fast.
0962         tabwidth = self.tabwidth
0963         have = len(chars.expandtabs(tabwidth))
0964         assert have > 0
0965         want = ((have - 1) // self.indentwidth) * self.indentwidth
0966         # Debug prompt is multilined....
0967         last_line_of_prompt = sys.ps1.split('\n')[-1]
0968         ncharsdeleted = 0
0969         while 1:
0970             if chars == last_line_of_prompt:
0971                 break
0972             chars = chars[:-1]
0973             ncharsdeleted = ncharsdeleted + 1
0974             have = len(chars.expandtabs(tabwidth))
0975             if have <= want or chars[-1] not in " \t":
0976                 break
0977         text.undo_block_start()
0978         text.delete("insert-%dc" % ncharsdeleted, "insert")
0979         if have < want:
0980             text.insert("insert", ' ' * (want - have))
0981         text.undo_block_stop()
0982         return "break"
0983 
0984     def smart_indent_event(self, event):
0985         # if intraline selection:
0986         #     delete it
0987         # elif multiline selection:
0988         #     do indent-region & return
0989         # indent one level
0990         text = self.text
0991         first, last = self.get_selection_indices()
0992         text.undo_block_start()
0993         try:
0994             if first and last:
0995                 if index2line(first) != index2line(last):
0996                     return self.indent_region_event(event)
0997                 text.delete(first, last)
0998                 text.mark_set("insert", first)
0999             prefix = text.get("insert linestart", "insert")
1000             raw, effective = classifyws(prefix, self.tabwidth)
1001             if raw == len(prefix):
1002                 # only whitespace to the left
1003                 self.reindent_to(effective + self.indentwidth)
1004             else:
1005                 if self.usetabs:
1006                     pad = '\t'
1007                 else:
1008                     effective = len(prefix.expandtabs(self.tabwidth))
1009                     n = self.indentwidth
1010                     pad = ' ' * (n - effective % n)
1011                 text.insert("insert", pad)
1012             text.see("insert")
1013             return "break"
1014         finally:
1015             text.undo_block_stop()
1016 
1017     def newline_and_indent_event(self, event):
1018         text = self.text
1019         first, last = self.get_selection_indices()
1020         text.undo_block_start()
1021         try:
1022             if first and last:
1023                 text.delete(first, last)
1024                 text.mark_set("insert", first)
1025             line = text.get("insert linestart", "insert")
1026             i, n = 0, len(line)
1027             while i < n and line[i] in " \t":
1028                 i = i+1
1029             if i == n:
1030                 # the cursor is in or at leading indentation in a continuation
1031                 # line; just inject an empty line at the start
1032                 text.insert("insert linestart", '\n')
1033                 return "break"
1034             indent = line[:i]
1035             # strip whitespace before insert point unless it's in the prompt
1036             i = 0
1037             last_line_of_prompt = sys.ps1.split('\n')[-1]
1038             while line and line[-1] in " \t" and line != last_line_of_prompt:
1039                 line = line[:-1]
1040                 i = i+1
1041             if i:
1042                 text.delete("insert - %d chars" % i, "insert")
1043             # strip whitespace after insert point
1044             while text.get("insert") in " \t":
1045                 text.delete("insert")
1046             # start new line
1047             text.insert("insert", '\n')
1048 
1049             # adjust indentation for continuations and block
1050             # open/close first need to find the last stmt
1051             lno = index2line(text.index('insert'))
1052             y = PyParse.Parser(self.indentwidth, self.tabwidth)
1053             for context in self.num_context_lines:
1054                 startat = max(lno - context, 1)
1055                 startatindex = repr(startat) + ".0"
1056                 rawtext = text.get(startatindex, "insert")
1057                 y.set_str(rawtext)
1058                 bod = y.find_good_parse_start(
1059                           self.context_use_ps1,
1060                           self._build_char_in_string_func(startatindex))
1061                 if bod is not None or startat == 1:
1062                     break
1063             y.set_lo(bod or 0)
1064             c = y.get_continuation_type()
1065             if c != PyParse.C_NONE:
1066                 # The current stmt hasn't ended yet.
1067                 if c == PyParse.C_STRING:
1068                     # inside a string; just mimic the current indent
1069                     text.insert("insert", indent)
1070                 elif c == PyParse.C_BRACKET:
1071                     # line up with the first (if any) element of the
1072                     # last open bracket structure; else indent one
1073                     # level beyond the indent of the line with the
1074                     # last open bracket
1075                     self.reindent_to(y.compute_bracket_indent())
1076                 elif c == PyParse.C_BACKSLASH:
1077                     # if more than one line in this stmt already, just
1078                     # mimic the current indent; else if initial line
1079                     # has a start on an assignment stmt, indent to
1080                     # beyond leftmost =; else to beyond first chunk of
1081                     # non-whitespace on initial line
1082                     if y.get_num_lines_in_stmt() > 1:
1083                         text.insert("insert", indent)
1084                     else:
1085                         self.reindent_to(y.compute_backslash_indent())
1086                 else:
1087                     assert 0, "bogus continuation type %r" % (c,)
1088                 return "break"
1089 
1090             # This line starts a brand new stmt; indent relative to
1091             # indentation of initial line of closest preceding
1092             # interesting stmt.
1093             indent = y.get_base_indent_string()
1094             text.insert("insert", indent)
1095             if y.is_block_opener():
1096                 self.smart_indent_event(event)
1097             elif indent and y.is_block_closer():
1098                 self.smart_backspace_event(event)
1099             return "break"
1100         finally:
1101             text.see("insert")
1102             text.undo_block_stop()
1103 
1104     # Our editwin provides a is_char_in_string function that works
1105     # with a Tk text index, but PyParse only knows about offsets into
1106     # a string. This builds a function for PyParse that accepts an
1107     # offset.
1108 
1109     def _build_char_in_string_func(self, startindex):
1110         def inner(offset, _startindex=startindex,
1111                   _icis=self.is_char_in_string):
1112             return _icis(_startindex + "+%dc" % offset)
1113         return inner
1114 
1115     def indent_region_event(self, event):
1116         head, tail, chars, lines = self.get_region()
1117         for pos in range(len(lines)):
1118             line = lines[pos]
1119             if line:
1120                 raw, effective = classifyws(line, self.tabwidth)
1121                 effective = effective + self.indentwidth
1122                 lines[pos] = self._make_blanks(effective) + line[raw:]
1123         self.set_region(head, tail, chars, lines)
1124         return "break"
1125 
1126     def dedent_region_event(self, event):
1127         head, tail, chars, lines = self.get_region()
1128         for pos in range(len(lines)):
1129             line = lines[pos]
1130             if line:
1131                 raw, effective = classifyws(line, self.tabwidth)
1132                 effective = max(effective - self.indentwidth, 0)
1133                 lines[pos] = self._make_blanks(effective) + line[raw:]
1134         self.set_region(head, tail, chars, lines)
1135         return "break"
1136 
1137     def comment_region_event(self, event):
1138         head, tail, chars, lines = self.get_region()
1139         for pos in range(len(lines) - 1):
1140             line = lines[pos]
1141             lines[pos] = '##' + line
1142         self.set_region(head, tail, chars, lines)
1143 
1144     def uncomment_region_event(self, event):
1145         head, tail, chars, lines = self.get_region()
1146         for pos in range(len(lines)):
1147             line = lines[pos]
1148             if not line:
1149                 continue
1150             if line[:2] == '##':
1151                 line = line[2:]
1152             elif line[:1] == '#':
1153                 line = line[1:]
1154             lines[pos] = line
1155         self.set_region(head, tail, chars, lines)
1156 
1157     def tabify_region_event(self, event):
1158         head, tail, chars, lines = self.get_region()
1159         tabwidth = self._asktabwidth()
1160         for pos in range(len(lines)):
1161             line = lines[pos]
1162             if line:
1163                 raw, effective = classifyws(line, tabwidth)
1164                 ntabs, nspaces = divmod(effective, tabwidth)
1165                 lines[pos] = '\t' * ntabs + ' ' * nspaces + line[raw:]
1166         self.set_region(head, tail, chars, lines)
1167 
1168     def untabify_region_event(self, event):
1169         head, tail, chars, lines = self.get_region()
1170         tabwidth = self._asktabwidth()
1171         for pos in range(len(lines)):
1172             lines[pos] = lines[pos].expandtabs(tabwidth)
1173         self.set_region(head, tail, chars, lines)
1174 
1175     def toggle_tabs_event(self, event):
1176         if self.askyesno(
1177               "Toggle tabs",
1178               "Turn tabs " + ("on", "off")[self.usetabs] + "?",
1179               parent=self.text):
1180             self.usetabs = not self.usetabs
1181         return "break"
1182 
1183     # XXX this isn't bound to anything -- see class tabwidth comments
1184     def change_tabwidth_event(self, event):
1185         new = self._asktabwidth()
1186         if new != self.tabwidth:
1187             self.tabwidth = new
1188             self.set_indentation_params(0, guess=0)
1189         return "break"
1190 
1191     def change_indentwidth_event(self, event):
1192         new = self.askinteger(
1193                   "Indent width",
1194                   "New indent width (2-16)",
1195                   parent=self.text,
1196                   initialvalue=self.indentwidth,
1197                   minvalue=2,
1198                   maxvalue=16)
1199         if new and new != self.indentwidth:
1200             self.indentwidth = new
1201         return "break"
1202 
1203     def get_region(self):
1204         text = self.text
1205         first, last = self.get_selection_indices()
1206         if first and last:
1207             head = text.index(first + " linestart")
1208             tail = text.index(last + "-1c lineend +1c")
1209         else:
1210             head = text.index("insert linestart")
1211             tail = text.index("insert lineend +1c")
1212         chars = text.get(head, tail)
1213         lines = chars.split("\n")
1214         return head, tail, chars, lines
1215 
1216     def set_region(self, head, tail, chars, lines):
1217         text = self.text
1218         newchars = "\n".join(lines)
1219         if newchars == chars:
1220             text.bell()
1221             return
1222         text.tag_remove("sel", "1.0", "end")
1223         text.mark_set("insert", head)
1224         text.undo_block_start()
1225         text.delete(head, tail)
1226         text.insert(head, newchars)
1227         text.undo_block_stop()
1228         text.tag_add("sel", head, "insert")
1229 
1230     # Make string that displays as n leading blanks.
1231 
1232     def _make_blanks(self, n):
1233         if self.usetabs:
1234             ntabs, nspaces = divmod(n, self.tabwidth)
1235             return '\t' * ntabs + ' ' * nspaces
1236         else:
1237             return ' ' * n
1238 
1239     # Delete from beginning of line to insert point, then reinsert
1240     # column logical (meaning use tabs if appropriate) spaces.
1241 
1242     def reindent_to(self, column):
1243         text = self.text
1244         text.undo_block_start()
1245         if text.compare("insert linestart", "!=", "insert"):
1246             text.delete("insert linestart", "insert")
1247         if column:
1248             text.insert("insert", self._make_blanks(column))
1249         text.undo_block_stop()
1250 
1251     def _asktabwidth(self):
1252         return self.askinteger(
1253             "Tab width",
1254             "Spaces per tab? (2-16)",
1255             parent=self.text,
1256             initialvalue=self.indentwidth,
1257             minvalue=2,
1258             maxvalue=16) or self.tabwidth
1259 
1260     # Guess indentwidth from text content.
1261     # Return guessed indentwidth.  This should not be believed unless
1262     # it's in a reasonable range (e.g., it will be 0 if no indented
1263     # blocks are found).
1264 
1265     def guess_indent(self):
1266         opener, indented = IndentSearcher(self.text, self.tabwidth).run()
1267         if opener and indented:
1268             raw, indentsmall = classifyws(opener, self.tabwidth)
1269             raw, indentlarge = classifyws(indented, self.tabwidth)
1270         else:
1271             indentsmall = indentlarge = 0
1272         return indentlarge - indentsmall
1273 
1274 # "line.col" -> line, as an int
1275 def index2line(index):
1276     return int(float(index))
1277 
1278 # Look at the leading whitespace in s.
1279 # Return pair (# of leading ws characters,
1280 #              effective # of leading blanks after expanding
1281 #              tabs to width tabwidth)
1282 
1283 def classifyws(s, tabwidth):
1284     raw = effective = 0
1285     for ch in s:
1286         if ch == ' ':
1287             raw = raw + 1
1288             effective = effective + 1
1289         elif ch == '\t':
1290             raw = raw + 1
1291             effective = (effective // tabwidth + 1) * tabwidth
1292         else:
1293             break
1294     return raw, effective
1295 
1296 import tokenize
1297 _tokenize = tokenize
1298 del tokenize
1299 
1300 class IndentSearcher:
1301 
1302     # .run() chews over the Text widget, looking for a block opener
1303     # and the stmt following it.  Returns a pair,
1304     #     (line containing block opener, line containing stmt)
1305     # Either or both may be None.
1306 
1307     def __init__(self, text, tabwidth):
1308         self.text = text
1309         self.tabwidth = tabwidth
1310         self.i = self.finished = 0
1311         self.blkopenline = self.indentedline = None
1312 
1313     def readline(self):
1314         if self.finished:
1315             return ""
1316         i = self.i = self.i + 1
1317         mark = repr(i) + ".0"
1318         if self.text.compare(mark, ">=", "end"):
1319             return ""
1320         return self.text.get(mark, mark + " lineend+1c")
1321 
1322     def tokeneater(self, type, token, start, end, line,
1323                    INDENT=_tokenize.INDENT,
1324                    NAME=_tokenize.NAME,
1325                    OPENERS=('class', 'def', 'for', 'if', 'try', 'while')):
1326         if self.finished:
1327             pass
1328         elif type == NAME and token in OPENERS:
1329             self.blkopenline = line
1330         elif type == INDENT and self.blkopenline:
1331             self.indentedline = line
1332             self.finished = 1
1333 
1334     def run(self):
1335         save_tabsize = _tokenize.tabsize
1336         _tokenize.tabsize = self.tabwidth
1337         try:
1338             try:
1339                 _tokenize.tokenize(self.readline, self.tokeneater)
1340             except _tokenize.TokenError:
1341                 # since we cut off the tokenizer early, we can trigger
1342                 # spurious errors
1343                 pass
1344         finally:
1345             _tokenize.tabsize = save_tabsize
1346         return self.blkopenline, self.indentedline
1347 
1348 ### end autoindent code ###
1349 
1350 def prepstr(s):
1351     # Helper to extract the underscore from a string, e.g.
1352     # prepstr("Co_py") returns (2, "Copy").
1353     i = s.find('_')
1354     if i >= 0:
1355         s = s[:i] + s[i+1:]
1356     return i, s
1357 
1358 
1359 keynames = {
1360  'bracketleft': '[',
1361  'bracketright': ']',
1362  'slash': '/',
1363 }
1364 
1365 def get_accelerator(keydefs, eventname):
1366     keylist = keydefs.get(eventname)
1367     if not keylist:
1368         return ""
1369     s = keylist[0]
1370     s = re.sub(r"-[a-z]\b", lambda m: m.group().upper(), s)
1371     s = re.sub(r"\b\w+\b", lambda m: keynames.get(m.group(), m.group()), s)
1372     s = re.sub("Key-", "", s)
1373     s = re.sub("Cancel","Ctrl-Break",s)   # dscherer@cmu.edu
1374     s = re.sub("Control-", "Ctrl-", s)
1375     s = re.sub("-", "+", s)
1376     s = re.sub("><", " ", s)
1377     s = re.sub("<", "", s)
1378     s = re.sub(">", "", s)
1379     return s
1380 
1381 
1382 def fixwordbreaks(root):
1383     # Make sure that Tk's double-click and next/previous word
1384     # operations use our definition of a word (i.e. an identifier)
1385     tk = root.tk
1386     tk.call('tcl_wordBreakAfter', 'a b', 0) # make sure word.tcl is loaded
1387     tk.call('set', 'tcl_wordchars', '[a-zA-Z0-9_]')
1388     tk.call('set', 'tcl_nonwordchars', '[^a-zA-Z0-9_]')
1389 
1390 
1391 def test():
1392     root = Tk()
1393     fixwordbreaks(root)
1394     root.withdraw()
1395     if sys.argv[1:]:
1396         filename = sys.argv[1]
1397     else:
1398         filename = None
1399     edit = EditorWindow(root=root, filename=filename)
1400     edit.set_close_hook(root.quit)
1401     root.mainloop()
1402     root.destroy()
1403 
1404 if __name__ == '__main__':
1405     test()
1406 

Generated by PyXR 0.9.4
SourceForge.net Logo