EditorWindow.py revision 1b3c26998e0f8b6975f7bbccf043c0afe398f151
17aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererimport sys
27aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererimport os
37aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererimport re
47aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererimport imp
57aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererfrom Tkinter import *
67aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererimport tkSimpleDialog
77aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererimport tkMessageBox
8fd182cd9d36adefba00d86542eb3134119cdc804Kurt B. Kaiser
9fd182cd9d36adefba00d86542eb3134119cdc804Kurt B. Kaiserimport webbrowser
107aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererimport idlever
117aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererimport WindowList
12c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gavaimport SearchDialog
13c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gavaimport GrepDialog
14c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gavaimport ReplaceDialog
15cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiserimport PyParse
16dc72f48e262a7aaf177a37676c79ced4ddce0fffSteven M. Gavafrom configHandler import idleConf
173b55a891a197212b34b0c077f72cb3af752468ecSteven M. Gavaimport aboutDialog, textView, configDialog
187aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
197aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer# The default tab setting for a Text widget, in average-width characters.
207aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David SchererTK_TABWIDTH_DEFAULT = 8
217aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
227aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererclass EditorWindow:
237aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    from Percolator import Percolator
247aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    from ColorDelegator import ColorDelegator
257aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    from UndoDelegator import UndoDelegator
267aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    from IOBinding import IOBinding
277aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    import Bindings
287aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    from Tkinter import Toplevel
297aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    from MultiStatusBar import MultiStatusBar
307aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
317aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    vars = {}
327aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
337aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def __init__(self, flist=None, filename=None, key=None, root=None):
34dc72f48e262a7aaf177a37676c79ced4ddce0fffSteven M. Gava        currentTheme=idleConf.CurrentTheme()
357aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.flist = flist
367aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        root = root or flist.root
377aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.root = root
387aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.menubar = Menu(root)
397aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.top = top = self.Toplevel(root, menu=self.menubar)
400c5bc8c9518fd18d41aedede536c4a9aa8dfa619Steven M. Gava        if flist:
410c5bc8c9518fd18d41aedede536c4a9aa8dfa619Steven M. Gava            self.vars = flist.vars
420c5bc8c9518fd18d41aedede536c4a9aa8dfa619Steven M. Gava            #self.top.instanceDict makes flist.inversedict avalable to
430c5bc8c9518fd18d41aedede536c4a9aa8dfa619Steven M. Gava            #configDialog.py so it can access all EditorWindow instaces
440c5bc8c9518fd18d41aedede536c4a9aa8dfa619Steven M. Gava            self.top.instanceDict=flist.inversedict
451d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava        self.recentFilesPath=os.path.join(idleConf.GetUserCfgDir(),
461d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava                'recent-files.lst')
47889f8bf259eee088d2d81e3978fbdf34585fc9aeKurt B. Kaiser        self.break_set = False
487aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.vbar = vbar = Scrollbar(top, name='vbar')
497aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.text_frame = text_frame = Frame(top)
50dc72f48e262a7aaf177a37676c79ced4ddce0fffSteven M. Gava        self.text = text = Text(text_frame, name='text', padx=5, wrap=None,
51dc72f48e262a7aaf177a37676c79ced4ddce0fffSteven M. Gava                foreground=idleConf.GetHighlight(currentTheme,
52dc72f48e262a7aaf177a37676c79ced4ddce0fffSteven M. Gava                        'normal',fgBg='fg'),
53dc72f48e262a7aaf177a37676c79ced4ddce0fffSteven M. Gava                background=idleConf.GetHighlight(currentTheme,
54dc72f48e262a7aaf177a37676c79ced4ddce0fffSteven M. Gava                        'normal',fgBg='bg'),
55dc72f48e262a7aaf177a37676c79ced4ddce0fffSteven M. Gava                highlightcolor=idleConf.GetHighlight(currentTheme,
56dc72f48e262a7aaf177a37676c79ced4ddce0fffSteven M. Gava                        'hilite',fgBg='fg'),
57dc72f48e262a7aaf177a37676c79ced4ddce0fffSteven M. Gava                highlightbackground=idleConf.GetHighlight(currentTheme,
58dc72f48e262a7aaf177a37676c79ced4ddce0fffSteven M. Gava                        'hilite',fgBg='bg'),
59dc72f48e262a7aaf177a37676c79ced4ddce0fffSteven M. Gava                insertbackground=idleConf.GetHighlight(currentTheme,
60dc72f48e262a7aaf177a37676c79ced4ddce0fffSteven M. Gava                        'cursor',fgBg='fg'),
61dc72f48e262a7aaf177a37676c79ced4ddce0fffSteven M. Gava                width=idleConf.GetOption('main','EditorWindow','width'),
62dc72f48e262a7aaf177a37676c79ced4ddce0fffSteven M. Gava                height=idleConf.GetOption('main','EditorWindow','height') )
637aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
647aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.createmenubar()
657aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.apply_bindings()
667aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
677aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.top.protocol("WM_DELETE_WINDOW", self.close)
687aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.top.bind("<<close-window>>", self.close_event)
697aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text.bind("<<center-insert>>", self.center_insert_event)
707aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text.bind("<<help>>", self.help_dialog)
717aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text.bind("<<good-advice>>", self.good_advice)
72abdfc4147d1f1334bb4dbaf5d9dd66af1db175adSteven M. Gava        text.bind("<<view-readme>>", self.view_readme)
737aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text.bind("<<python-docs>>", self.python_docs)
747aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text.bind("<<about-idle>>", self.about_dialog)
753b55a891a197212b34b0c077f72cb3af752468ecSteven M. Gava        text.bind("<<open-config-dialog>>", self.config_dialog)
767aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text.bind("<<open-module>>", self.open_module)
777aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text.bind("<<do-nothing>>", lambda event: "break")
787aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text.bind("<<select-all>>", self.select_all)
797aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text.bind("<<remove-selection>>", self.remove_selection)
80c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        text.bind("<<find>>", self.find_event)
81c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        text.bind("<<find-again>>", self.find_again_event)
82c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        text.bind("<<find-in-files>>", self.find_in_files_event)
83c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        text.bind("<<find-selection>>", self.find_selection_event)
84c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        text.bind("<<replace>>", self.replace_event)
85c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        text.bind("<<goto-line>>", self.goto_line_event)
867aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text.bind("<3>", self.right_menu_event)
87cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.bind("<<smart-backspace>>",self.smart_backspace_event)
88cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.bind("<<newline-and-indent>>",self.newline_and_indent_event)
89cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.bind("<<smart-indent>>",self.smart_indent_event)
90cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.bind("<<indent-region>>",self.indent_region_event)
91cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.bind("<<dedent-region>>",self.dedent_region_event)
92cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.bind("<<comment-region>>",self.comment_region_event)
93cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.bind("<<uncomment-region>>",self.uncomment_region_event)
94cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.bind("<<tabify-region>>",self.tabify_region_event)
95cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.bind("<<untabify-region>>",self.untabify_region_event)
96cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.bind("<<toggle-tabs>>",self.toggle_tabs_event)
97cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.bind("<<change-indentwidth>>",self.change_indentwidth_event)
98cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
997aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if flist:
1007aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            flist.inversedict[self] = key
1017aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            if key:
1027aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                flist.dict[key] = self
1037aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            text.bind("<<open-new-window>>", self.flist.new_callback)
1047aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            text.bind("<<close-all-windows>>", self.flist.close_all_callback)
1057aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            text.bind("<<open-class-browser>>", self.open_class_browser)
1067aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            text.bind("<<open-path-browser>>", self.open_path_browser)
1077aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
108898a365c276951ab2471afe2e24bbc910666d361Steven M. Gava        self.set_status_bar()
1097aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        vbar['command'] = text.yview
1107aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        vbar.pack(side=RIGHT, fill=Y)
1117aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text['yscrollcommand'] = vbar.set
112b1585417d1f7ae8984ca72b16fd5a21746949ae5Steven M. Gava        fontWeight='normal'
113b1585417d1f7ae8984ca72b16fd5a21746949ae5Steven M. Gava        if idleConf.GetOption('main','EditorWindow','font-bold',type='bool'):
114b1585417d1f7ae8984ca72b16fd5a21746949ae5Steven M. Gava            fontWeight='bold'
115dc72f48e262a7aaf177a37676c79ced4ddce0fffSteven M. Gava        text.config(font=(idleConf.GetOption('main','EditorWindow','font'),
116b1585417d1f7ae8984ca72b16fd5a21746949ae5Steven M. Gava                idleConf.GetOption('main','EditorWindow','font-size'),
117b1585417d1f7ae8984ca72b16fd5a21746949ae5Steven M. Gava                fontWeight))
1187aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text_frame.pack(side=LEFT, fill=BOTH, expand=1)
1197aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text.pack(side=TOP, fill=BOTH, expand=1)
1207aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text.focus_set()
1217aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
1227aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.per = per = self.Percolator(text)
1237aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if self.ispythonsource(filename):
124dc1e70987f49aa23bf1d07f32c476edeba0cec30Kurt B. Kaiser            self.color = color = self.ColorDelegator()
125dc1e70987f49aa23bf1d07f32c476edeba0cec30Kurt B. Kaiser            per.insertfilter(color)
1267aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            ##print "Initial colorizer"
1277aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        else:
1287aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            ##print "No initial colorizer"
1297aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.color = None
130dc1e70987f49aa23bf1d07f32c476edeba0cec30Kurt B. Kaiser
131dc1e70987f49aa23bf1d07f32c476edeba0cec30Kurt B. Kaiser        self.undo = undo = self.UndoDelegator()
132dc1e70987f49aa23bf1d07f32c476edeba0cec30Kurt B. Kaiser        per.insertfilter(undo)
133dc1e70987f49aa23bf1d07f32c476edeba0cec30Kurt B. Kaiser        text.undo_block_start = undo.undo_block_start
134dc1e70987f49aa23bf1d07f32c476edeba0cec30Kurt B. Kaiser        text.undo_block_stop = undo.undo_block_stop
135dc1e70987f49aa23bf1d07f32c476edeba0cec30Kurt B. Kaiser        undo.set_saved_change_hook(self.saved_change_hook)
136dc1e70987f49aa23bf1d07f32c476edeba0cec30Kurt B. Kaiser
137dc1e70987f49aa23bf1d07f32c476edeba0cec30Kurt B. Kaiser        # IOBinding implements file I/O and printing functionality
1387aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.io = io = self.IOBinding(self)
139dc1e70987f49aa23bf1d07f32c476edeba0cec30Kurt B. Kaiser        io.set_filename_change_hook(self.filename_change_hook)
140dc1e70987f49aa23bf1d07f32c476edeba0cec30Kurt B. Kaiser
1411d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava        #create the Recent Files submenu
1421d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava        self.menuRecentFiles=Menu(self.menubar)
1431d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava        self.menudict['file'].insert_cascade(3,label='Recent Files',
1441d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava                underline=0,menu=self.menuRecentFiles)
1451d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava        self.UpdateRecentFilesList()
1467aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
1477aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if filename:
1487aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            if os.path.exists(filename):
1497aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                io.loadfile(filename)
1507aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            else:
1517aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                io.set_filename(filename)
1527aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.saved_change_hook()
1537aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
1547aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.load_extensions()
1557aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
1567aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        menu = self.menudict.get('windows')
1577aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if menu:
1587aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            end = menu.index("end")
1597aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            if end is None:
1607aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                end = -1
1617aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            if end >= 0:
1627aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                menu.add_separator()
1637aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                end = end + 1
1647aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.wmenu_end = end
1657aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            WindowList.register_callback(self.postwindowsmenu)
1667aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
1677aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        # Some abstractions so IDLE extensions are cross-IDE
1687aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.askyesno = tkMessageBox.askyesno
1697aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.askinteger = tkSimpleDialog.askinteger
1707aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.showerror = tkMessageBox.showerror
1717aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
1727aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if self.extensions.has_key('AutoIndent'):
1737aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.extensions['AutoIndent'].set_indentation_params(
1747aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                self.ispythonsource(filename))
1750c5bc8c9518fd18d41aedede536c4a9aa8dfa619Steven M. Gava
1767aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def set_status_bar(self):
177898a365c276951ab2471afe2e24bbc910666d361Steven M. Gava        self.status_bar = self.MultiStatusBar(self.top)
1787aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.status_bar.set_label('column', 'Col: ?', side=RIGHT)
1797aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.status_bar.set_label('line', 'Ln: ?', side=RIGHT)
1807aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.status_bar.pack(side=BOTTOM, fill=X)
1817aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.text.bind('<KeyRelease>', self.set_line_and_column)
1827aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.text.bind('<ButtonRelease>', self.set_line_and_column)
1837aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.text.after_idle(self.set_line_and_column)
1847aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
1857aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def set_line_and_column(self, event=None):
1867aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        line, column = string.split(self.text.index(INSERT), '.')
1877aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.status_bar.set_label('column', 'Col: %s' % column)
1887aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.status_bar.set_label('line', 'Ln: %s' % line)
1897aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
1907aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def wakeup(self):
1917aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if self.top.wm_state() == "iconic":
1927aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.top.wm_deiconify()
1937aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        else:
1947aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.top.tkraise()
1957aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.text.focus_set()
1967aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
1977aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    menu_specs = [
1987aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        ("file", "_File"),
1997aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        ("edit", "_Edit"),
2007aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        ("format", "F_ormat"),
2017aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        ("run", "_Run"),
20282c6682bb776bb59e10a522897534af69938194fSteven M. Gava        ("settings", "_Settings"),
2037aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        ("windows", "_Windows"),
2047aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        ("help", "_Help"),
2057aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    ]
2067aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
2077aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def createmenubar(self):
2087aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        mbar = self.menubar
2097aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.menudict = menudict = {}
2107aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        for name, label in self.menu_specs:
2117aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            underline, label = prepstr(label)
2127aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            menudict[name] = menu = Menu(mbar, name=name)
2137aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            mbar.add_cascade(label=label, menu=menu, underline=underline)
2147aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.fill_menus()
2151d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava        #create the ExtraHelp menu, if required
2160c5bc8c9518fd18d41aedede536c4a9aa8dfa619Steven M. Gava        self.ResetExtraHelpMenu()
2177aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
2187aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def postwindowsmenu(self):
2197aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        # Only called when Windows menu exists
2207aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        # XXX Actually, this Just-In-Time updating interferes badly
2217aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        # XXX with the tear-off feature.  It would be better to update
2227aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        # XXX all Windows menus whenever the list of windows changes.
2237aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        menu = self.menudict['windows']
2247aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        end = menu.index("end")
2257aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if end is None:
2267aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            end = -1
2277aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if end > self.wmenu_end:
2287aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            menu.delete(self.wmenu_end+1, end)
2297aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        WindowList.add_windows_to_menu(menu)
2307aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
2317aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    rmenu = None
2327aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
2337aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def right_menu_event(self, event):
2347aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.text.tag_remove("sel", "1.0", "end")
2357aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.text.mark_set("insert", "@%d,%d" % (event.x, event.y))
2367aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if not self.rmenu:
2377aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.make_rmenu()
2387aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        rmenu = self.rmenu
2397aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.event = event
2407aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        iswin = sys.platform[:3] == 'win'
2417aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if iswin:
2427aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.text.config(cursor="arrow")
2437aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        rmenu.tk_popup(event.x_root, event.y_root)
2447aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if iswin:
2457aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.text.config(cursor="ibeam")
2467aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
2477aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    rmenu_specs = [
2487aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        # ("Label", "<<virtual-event>>"), ...
2497aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        ("Close", "<<close-window>>"), # Example
2507aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    ]
2517aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
2527aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def make_rmenu(self):
2537aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        rmenu = Menu(self.text, tearoff=0)
2547aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        for label, eventname in self.rmenu_specs:
2557aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            def command(text=self.text, eventname=eventname):
2567aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                text.event_generate(eventname)
2577aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            rmenu.add_command(label=label, command=command)
2587aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.rmenu = rmenu
2597aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
2607aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def about_dialog(self, event=None):
2617d9ed726fb6ec15dbfb8e922d35aa8816cbd0b0cSteven M. Gava        aboutDialog.AboutDialog(self.top,'About IDLEfork')
2627d9ed726fb6ec15dbfb8e922d35aa8816cbd0b0cSteven M. Gava
2633b55a891a197212b34b0c077f72cb3af752468ecSteven M. Gava    def config_dialog(self, event=None):
2643b55a891a197212b34b0c077f72cb3af752468ecSteven M. Gava        configDialog.ConfigDialog(self.top,'Settings')
2653b55a891a197212b34b0c077f72cb3af752468ecSteven M. Gava
2667aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def good_advice(self, event=None):
2677aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        tkMessageBox.showinfo('Advice', "Don't Panic!", master=self.text)
2687aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
269abdfc4147d1f1334bb4dbaf5d9dd66af1db175adSteven M. Gava    def view_readme(self, event=None):
270abdfc4147d1f1334bb4dbaf5d9dd66af1db175adSteven M. Gava        fn=os.path.join(os.path.abspath(os.path.dirname(__file__)),'README.txt')
271abdfc4147d1f1334bb4dbaf5d9dd66af1db175adSteven M. Gava        textView.TextViewer(self.top,'IDLEfork - README',fn)
272abdfc4147d1f1334bb4dbaf5d9dd66af1db175adSteven M. Gava
2737aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def help_dialog(self, event=None):
274b9d07b5a8b8b70268ffa520895e7843ab594a486Steven M. Gava        fn=os.path.join(os.path.abspath(os.path.dirname(__file__)),'help.txt')
275b9d07b5a8b8b70268ffa520895e7843ab594a486Steven M. Gava        textView.TextViewer(self.top,'Help',fn)
276b9d07b5a8b8b70268ffa520895e7843ab594a486Steven M. Gava
2777aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    help_url = "http://www.python.org/doc/current/"
278afdf71b9eefdfb5904e40602984836b28318bad0Kurt B. Kaiser    if sys.platform[:3] == "win":
279afdf71b9eefdfb5904e40602984836b28318bad0Kurt B. Kaiser        fn = os.path.dirname(__file__)
280931625dc77cfd527b059e579615d517d8d994110Steven M. Gava        fn = os.path.join(fn, os.pardir, os.pardir, "pythlp.chm")
281afdf71b9eefdfb5904e40602984836b28318bad0Kurt B. Kaiser        fn = os.path.normpath(fn)
282afdf71b9eefdfb5904e40602984836b28318bad0Kurt B. Kaiser        if os.path.isfile(fn):
283afdf71b9eefdfb5904e40602984836b28318bad0Kurt B. Kaiser            help_url = fn
284931625dc77cfd527b059e579615d517d8d994110Steven M. Gava        else:
285931625dc77cfd527b059e579615d517d8d994110Steven M. Gava            fn = os.path.dirname(__file__)
286931625dc77cfd527b059e579615d517d8d994110Steven M. Gava            fn = os.path.join(fn, os.pardir, os.pardir, "Doc", "index.html")
287931625dc77cfd527b059e579615d517d8d994110Steven M. Gava            fn = os.path.normpath(fn)
288931625dc77cfd527b059e579615d517d8d994110Steven M. Gava            if os.path.isfile(fn):
289931625dc77cfd527b059e579615d517d8d994110Steven M. Gava                help_url = fn
290afdf71b9eefdfb5904e40602984836b28318bad0Kurt B. Kaiser        del fn
291931625dc77cfd527b059e579615d517d8d994110Steven M. Gava        def python_docs(self, event=None):
292931625dc77cfd527b059e579615d517d8d994110Steven M. Gava            os.startfile(self.help_url)
293931625dc77cfd527b059e579615d517d8d994110Steven M. Gava    else:
294931625dc77cfd527b059e579615d517d8d994110Steven M. Gava        def python_docs(self, event=None):
295931625dc77cfd527b059e579615d517d8d994110Steven M. Gava            self.display_docs(self.help_url)
2960c5bc8c9518fd18d41aedede536c4a9aa8dfa619Steven M. Gava
2970c5bc8c9518fd18d41aedede536c4a9aa8dfa619Steven M. Gava    def display_docs(self, url):
2980c5bc8c9518fd18d41aedede536c4a9aa8dfa619Steven M. Gava        webbrowser.open(url)
2997aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
3007aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def select_all(self, event=None):
3017aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.text.tag_add("sel", "1.0", "end-1c")
3027aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.text.mark_set("insert", "1.0")
3037aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.text.see("insert")
3047aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        return "break"
3057aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
3067aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def remove_selection(self, event=None):
3077aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.text.tag_remove("sel", "1.0", "end")
3087aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.text.see("insert")
3097aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
310c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava    def find_event(self, event):
311c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        SearchDialog.find(self.text)
312c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        return "break"
313c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava
314c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava    def find_again_event(self, event):
315c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        SearchDialog.find_again(self.text)
316c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        return "break"
317c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava
318c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava    def find_selection_event(self, event):
319c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        SearchDialog.find_selection(self.text)
320c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        return "break"
321c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava
322c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava    def find_in_files_event(self, event):
323c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        GrepDialog.grep(self.text, self.io, self.flist)
324c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        return "break"
325c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava
326c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava    def replace_event(self, event):
327c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        ReplaceDialog.replace(self.text)
328c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        return "break"
329c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava
330c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava    def goto_line_event(self, event):
331c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        text = self.text
332c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        lineno = tkSimpleDialog.askinteger("Goto",
333c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava                "Go to line number:",parent=text)
334c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        if lineno is None:
335c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava            return "break"
336c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        if lineno <= 0:
337c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava            text.bell()
338c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava            return "break"
339c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        text.mark_set("insert", "%d.0" % lineno)
340c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        text.see("insert")
341c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava
3427aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def open_module(self, event=None):
3437aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        # XXX Shouldn't this be in IOBinding or in FileList?
3447aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        try:
3457aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            name = self.text.get("sel.first", "sel.last")
3467aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        except TclError:
3477aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            name = ""
3487aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        else:
3497aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            name = string.strip(name)
3507aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if not name:
3517aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            name = tkSimpleDialog.askstring("Module",
3527aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                     "Enter the name of a Python module\n"
3537aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                     "to search on sys.path and open:",
3547aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                     parent=self.text)
3557aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            if name:
3567aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                name = string.strip(name)
3577aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            if not name:
3587aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                return
3597aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        # XXX Ought to support package syntax
3607aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        # XXX Ought to insert current file's directory in front of path
3617aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        try:
3627aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            (f, file, (suffix, mode, type)) = imp.find_module(name)
3637aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        except (NameError, ImportError), msg:
3647aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            tkMessageBox.showerror("Import error", str(msg), parent=self.text)
3657aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return
3667aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if type != imp.PY_SOURCE:
3677aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            tkMessageBox.showerror("Unsupported type",
3687aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                "%s is not a source module" % name, parent=self.text)
3697aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return
3707aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if f:
3717aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            f.close()
3727aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if self.flist:
3737aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.flist.open(file)
3747aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        else:
3757aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.io.loadfile(file)
3767aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
3777aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def open_class_browser(self, event=None):
3787aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        filename = self.io.filename
3797aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if not filename:
3807aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            tkMessageBox.showerror(
3817aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                "No filename",
3827aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                "This buffer has no associated filename",
3837aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                master=self.text)
3847aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.text.focus_set()
3857aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return None
3867aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        head, tail = os.path.split(filename)
3877aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        base, ext = os.path.splitext(tail)
3887aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        import ClassBrowser
3897aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        ClassBrowser.ClassBrowser(self.flist, base, [head])
3907aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
3917aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def open_path_browser(self, event=None):
3927aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        import PathBrowser
3937aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        PathBrowser.PathBrowser(self.flist)
3947aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
3957aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def gotoline(self, lineno):
3967aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if lineno is not None and lineno > 0:
3977aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.text.mark_set("insert", "%d.0" % lineno)
3987aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.text.tag_remove("sel", "1.0", "end")
3997aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.text.tag_add("sel", "insert", "insert +1l")
4007aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.center()
4017aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
4027aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def ispythonsource(self, filename):
4037aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if not filename:
4047aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return 1
4057aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        base, ext = os.path.splitext(os.path.basename(filename))
4067aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if os.path.normcase(ext) in (".py", ".pyw"):
4077aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return 1
4087aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        try:
4097aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            f = open(filename)
4107aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            line = f.readline()
4117aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            f.close()
4127aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        except IOError:
4137aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return 0
4147aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        return line[:2] == '#!' and string.find(line, 'python') >= 0
4157aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
4167aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def close_hook(self):
4177aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if self.flist:
4187aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.flist.close_edit(self)
4197aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
4207aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def set_close_hook(self, close_hook):
4217aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.close_hook = close_hook
4227aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
4237aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def filename_change_hook(self):
4247aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if self.flist:
4257aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.flist.filename_changed_edit(self)
4267aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.saved_change_hook()
4277aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if self.ispythonsource(self.io.filename):
4287aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.addcolorizer()
4297aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        else:
4307aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.rmcolorizer()
4317aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
4327aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def addcolorizer(self):
4337aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if self.color:
4347aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return
4357aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        ##print "Add colorizer"
4367aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.per.removefilter(self.undo)
4377aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.color = self.ColorDelegator()
4387aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.per.insertfilter(self.color)
4397aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.per.insertfilter(self.undo)
4407aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
4417aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def rmcolorizer(self):
4427aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if not self.color:
4437aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return
4447aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        ##print "Remove colorizer"
4457aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.per.removefilter(self.undo)
4467aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.per.removefilter(self.color)
4477aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.color = None
4487aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.per.insertfilter(self.undo)
449b77d343bc846c2049a4cffb1dfd65eb49d1728b4Steven M. Gava
450b77d343bc846c2049a4cffb1dfd65eb49d1728b4Steven M. Gava    def ResetColorizer(self):
45183118c6cb36cf9a424bec1b9a2ef8c8760bae8f5Kurt B. Kaiser        "Update the colour theme if it is changed"
45283118c6cb36cf9a424bec1b9a2ef8c8760bae8f5Kurt B. Kaiser        # Called from configDialog.py
453b77d343bc846c2049a4cffb1dfd65eb49d1728b4Steven M. Gava        if self.color:
454b77d343bc846c2049a4cffb1dfd65eb49d1728b4Steven M. Gava            self.color = self.ColorDelegator()
455b77d343bc846c2049a4cffb1dfd65eb49d1728b4Steven M. Gava            self.per.insertfilter(self.color)
4567aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
457b1585417d1f7ae8984ca72b16fd5a21746949ae5Steven M. Gava    def ResetFont(self):
45883118c6cb36cf9a424bec1b9a2ef8c8760bae8f5Kurt B. Kaiser        "Update the text widgets' font if it is changed"
45983118c6cb36cf9a424bec1b9a2ef8c8760bae8f5Kurt B. Kaiser        # Called from configDialog.py
460b1585417d1f7ae8984ca72b16fd5a21746949ae5Steven M. Gava        fontWeight='normal'
461b1585417d1f7ae8984ca72b16fd5a21746949ae5Steven M. Gava        if idleConf.GetOption('main','EditorWindow','font-bold',type='bool'):
462b1585417d1f7ae8984ca72b16fd5a21746949ae5Steven M. Gava            fontWeight='bold'
463b1585417d1f7ae8984ca72b16fd5a21746949ae5Steven M. Gava        self.text.config(font=(idleConf.GetOption('main','EditorWindow','font'),
464b1585417d1f7ae8984ca72b16fd5a21746949ae5Steven M. Gava                idleConf.GetOption('main','EditorWindow','font-size'),
465b1585417d1f7ae8984ca72b16fd5a21746949ae5Steven M. Gava                fontWeight))
466b1585417d1f7ae8984ca72b16fd5a21746949ae5Steven M. Gava
467dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava    def ResetKeybindings(self):
46883118c6cb36cf9a424bec1b9a2ef8c8760bae8f5Kurt B. Kaiser        "Update the keybindings if they are changed"
46983118c6cb36cf9a424bec1b9a2ef8c8760bae8f5Kurt B. Kaiser        # Called from configDialog.py
470dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava        self.Bindings.default_keydefs=idleConf.GetCurrentKeySet()
471dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava        keydefs = self.Bindings.default_keydefs
472dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava        for event, keylist in keydefs.items():
473dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava            self.text.event_delete(event)
474dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava        self.apply_bindings()
475dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava        #update menu accelerators
476dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava        menuEventDict={}
477dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava        for menu in self.Bindings.menudefs:
478dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava            menuEventDict[menu[0]]={}
479dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava            for item in menu[1]:
480dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava                if item:
481dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava                    menuEventDict[menu[0]][prepstr(item[0])[1]]=item[1]
482dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava        for menubarItem in self.menudict.keys():
483dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava            menu=self.menudict[menubarItem]
484dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava            end=menu.index(END)+1
485dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava            for index in range(0,end):
486dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava                if menu.type(index)=='command':
487dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava                    accel=menu.entrycget(index,'accelerator')
488dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava                    if accel:
489dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava                        itemName=menu.entrycget(index,'label')
490dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava                        event=''
491dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava                        if menuEventDict.has_key(menubarItem):
492dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava                            if menuEventDict[menubarItem].has_key(itemName):
493dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava                                event=menuEventDict[menubarItem][itemName]
494dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava                        if event:
495dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava                            #print 'accel was:',accel
496dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava                            accel=get_accelerator(keydefs, event)
497dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava                            menu.entryconfig(index,accelerator=accel)
498dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava                            #print 'accel now:',accel,'\n'
499dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava
5000c5bc8c9518fd18d41aedede536c4a9aa8dfa619Steven M. Gava    def ResetExtraHelpMenu(self):
50183118c6cb36cf9a424bec1b9a2ef8c8760bae8f5Kurt B. Kaiser        "Load or update the Extra Help menu if required"
5020c5bc8c9518fd18d41aedede536c4a9aa8dfa619Steven M. Gava        menuList=idleConf.GetAllExtraHelpSourcesList()
5030c5bc8c9518fd18d41aedede536c4a9aa8dfa619Steven M. Gava        helpMenu=self.menudict['help']
5040c5bc8c9518fd18d41aedede536c4a9aa8dfa619Steven M. Gava        cascadeIndex=helpMenu.index(END)-1
5050c5bc8c9518fd18d41aedede536c4a9aa8dfa619Steven M. Gava        if menuList:
5060c5bc8c9518fd18d41aedede536c4a9aa8dfa619Steven M. Gava            if not hasattr(self,'menuExtraHelp'):
5070c5bc8c9518fd18d41aedede536c4a9aa8dfa619Steven M. Gava                self.menuExtraHelp=Menu(self.menubar)
5080c5bc8c9518fd18d41aedede536c4a9aa8dfa619Steven M. Gava                helpMenu.insert_cascade(cascadeIndex,label='Extra Help',
5090c5bc8c9518fd18d41aedede536c4a9aa8dfa619Steven M. Gava                        underline=1,menu=self.menuExtraHelp)
5100c5bc8c9518fd18d41aedede536c4a9aa8dfa619Steven M. Gava            self.menuExtraHelp.delete(1,END)
5110c5bc8c9518fd18d41aedede536c4a9aa8dfa619Steven M. Gava            for menuItem in menuList:
5120c5bc8c9518fd18d41aedede536c4a9aa8dfa619Steven M. Gava                self.menuExtraHelp.add_command(label=menuItem[0],
5131d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava                        command=self.__DisplayExtraHelpCallback(menuItem[1]))
5140c5bc8c9518fd18d41aedede536c4a9aa8dfa619Steven M. Gava        else: #no extra help items
5150c5bc8c9518fd18d41aedede536c4a9aa8dfa619Steven M. Gava            if hasattr(self,'menuExtraHelp'):
5160c5bc8c9518fd18d41aedede536c4a9aa8dfa619Steven M. Gava                helpMenu.delete(cascadeIndex-1)
5170c5bc8c9518fd18d41aedede536c4a9aa8dfa619Steven M. Gava                del(self.menuExtraHelp)
5181d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava
5191d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava    def __DisplayExtraHelpCallback(self,helpFile):
5201d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava        def DisplayExtraHelp(helpFile=helpFile):
5211d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava            self.display_docs(helpFile)
5221d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava        return DisplayExtraHelp
5230c5bc8c9518fd18d41aedede536c4a9aa8dfa619Steven M. Gava
5241d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava    def UpdateRecentFilesList(self,newFile=None):
52583118c6cb36cf9a424bec1b9a2ef8c8760bae8f5Kurt B. Kaiser        "Load or update the recent files list, and menu if required"
5261d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava        rfList=[]
5271d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava        if os.path.exists(self.recentFilesPath):
5281d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava            RFfile=open(self.recentFilesPath,'r')
5291d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava            try:
5301d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava                rfList=RFfile.readlines()
5311d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava            finally:
5321d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava                RFfile.close()
5331d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava        if newFile:
5341d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava            newFile=os.path.abspath(newFile)+'\n'
5351d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava            if newFile in rfList:
5361d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava                rfList.remove(newFile)
5371d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava            rfList.insert(0,newFile)
5381d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava        rfList=self.__CleanRecentFiles(rfList)
53983118c6cb36cf9a424bec1b9a2ef8c8760bae8f5Kurt B. Kaiser        #print self.flist.inversedict
54083118c6cb36cf9a424bec1b9a2ef8c8760bae8f5Kurt B. Kaiser        #print self.top.instanceDict
54183118c6cb36cf9a424bec1b9a2ef8c8760bae8f5Kurt B. Kaiser        #print self
5421d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava        if rfList:
5431d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava            for instance in self.top.instanceDict.keys():
5441d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava                instance.menuRecentFiles.delete(1,END)
5451d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava                for file in rfList:
5461d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava                    fileName=file[0:-1]
5471d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava                    instance.menuRecentFiles.add_command(label=fileName,
5481d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava                            command=instance.__RecentFileCallback(fileName))
5491d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava
5501d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava    def __CleanRecentFiles(self,rfList):
5511d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava        origRfList=rfList[:]
5521d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava        count=0
5531d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava        nonFiles=[]
5541d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava        for path in rfList:
5551d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava            if not os.path.exists(path[0:-1]):
5561d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava                nonFiles.append(count)
5571d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava            count=count+1
5581d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava        if nonFiles:
5591d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava            nonFiles.reverse()
5601d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava            for index in nonFiles:
5611d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava                del(rfList[index])
5621d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava        if len(rfList)>19:
5631d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava            rfList=rfList[0:19]
5641d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava        #if rfList != origRfList:
5651d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava        RFfile=open(self.recentFilesPath,'w')
5661d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava        try:
5671d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava            RFfile.writelines(rfList)
5681d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava        finally:
5691d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava            RFfile.close()
5701d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava        return rfList
5711d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava
5721d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava    def __RecentFileCallback(self,fileName):
5731d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava        def OpenRecentFile(fileName=fileName):
5741d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava            self.io.open(editFile=fileName)
5751d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava        return OpenRecentFile
5761d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava
5777aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def saved_change_hook(self):
5787aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        short = self.short_title()
5797aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        long = self.long_title()
5807aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if short and long:
5817aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            title = short + " - " + long
5827aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        elif short:
5837aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            title = short
5847aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        elif long:
5857aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            title = long
5867aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        else:
5877aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            title = "Untitled"
5887aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        icon = short or long or title
5897aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if not self.get_saved():
5907aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            title = "*%s*" % title
5917aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            icon = "*%s" % icon
592889f8bf259eee088d2d81e3978fbdf34585fc9aeKurt B. Kaiser            if self.break_set:
593889f8bf259eee088d2d81e3978fbdf34585fc9aeKurt B. Kaiser                shell = self.flist.pyshell
594889f8bf259eee088d2d81e3978fbdf34585fc9aeKurt B. Kaiser                shell.interp.debugger.clear_file_breaks(self)
5957aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.top.wm_title(title)
5967aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.top.wm_iconname(icon)
5977aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
5987aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def get_saved(self):
5997aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        return self.undo.get_saved()
6007aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
6017aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def set_saved(self, flag):
6027aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.undo.set_saved(flag)
6037aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
6047aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def reset_undo(self):
6057aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.undo.reset_undo()
6067aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
6077aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def short_title(self):
6087aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        filename = self.io.filename
6097aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if filename:
6107aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            filename = os.path.basename(filename)
6117aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        return filename
6127aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
6137aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def long_title(self):
6147aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        return self.io.filename or ""
6157aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
6167aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def center_insert_event(self, event):
6177aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.center()
6187aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
6197aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def center(self, mark="insert"):
6207aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text = self.text
6217aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        top, bot = self.getwindowlines()
6227aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        lineno = self.getlineno(mark)
6237aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        height = bot - top
6247aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        newtop = max(1, lineno - height/2)
6257aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text.yview(float(newtop))
6267aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
6277aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def getwindowlines(self):
6287aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text = self.text
6297aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        top = self.getlineno("@0,0")
6307aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        bot = self.getlineno("@0,65535")
6317aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if top == bot and text.winfo_height() == 1:
6327aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            # Geometry manager hasn't run yet
6337aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            height = int(text['height'])
6347aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            bot = top + height - 1
6357aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        return top, bot
6367aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
6377aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def getlineno(self, mark="insert"):
6387aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text = self.text
6397aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        return int(float(text.index(mark)))
6407aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
6417aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def close_event(self, event):
6427aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.close()
6437aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
6447aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def maybesave(self):
6457aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if self.io:
64667716b5f53715e57d147cde9539b8d76a5a56e11Steven M. Gava            if not self.get_saved():
64767716b5f53715e57d147cde9539b8d76a5a56e11Steven M. Gava                if self.top.state()!='normal':
64867716b5f53715e57d147cde9539b8d76a5a56e11Steven M. Gava                    self.top.deiconify()
64967716b5f53715e57d147cde9539b8d76a5a56e11Steven M. Gava                self.top.lower()
65067716b5f53715e57d147cde9539b8d76a5a56e11Steven M. Gava                self.top.lift()
6517aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return self.io.maybesave()
6527aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
6537aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def close(self):
6547aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        reply = self.maybesave()
6557aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if reply != "cancel":
6567aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self._close()
6577aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        return reply
6587aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
6597aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def _close(self):
66083118c6cb36cf9a424bec1b9a2ef8c8760bae8f5Kurt B. Kaiser        #print self.io.filename
6611d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava        if self.io.filename:
6621d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava            self.UpdateRecentFilesList(newFile=self.io.filename)
663889f8bf259eee088d2d81e3978fbdf34585fc9aeKurt B. Kaiser        if self.break_set:
664889f8bf259eee088d2d81e3978fbdf34585fc9aeKurt B. Kaiser            shell = self.flist.pyshell
66583118c6cb36cf9a424bec1b9a2ef8c8760bae8f5Kurt B. Kaiser            shell.interp.debugger.clear_file_breaks(self)
6667aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        WindowList.unregister_callback(self.postwindowsmenu)
6677aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if self.close_hook:
6687aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.close_hook()
6697aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.flist = None
6707aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        colorizing = 0
6717aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.unload_extensions()
6727aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.io.close(); self.io = None
6737aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.undo = None # XXX
6747aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if self.color:
6757aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            colorizing = self.color.colorizing
6767aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            doh = colorizing and self.top
6777aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.color.close(doh) # Cancel colorization
6787aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.text = None
6797aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.vars = None
6807aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.per.close(); self.per = None
6817aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if not colorizing:
6827aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.top.destroy()
6837aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
6847aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def load_extensions(self):
6857aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.extensions = {}
6867aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.load_standard_extensions()
6877aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
6887aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def unload_extensions(self):
6897aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        for ins in self.extensions.values():
6907aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            if hasattr(ins, "close"):
6917aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                ins.close()
6927aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.extensions = {}
6937aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
6947aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def load_standard_extensions(self):
6957aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        for name in self.get_standard_extension_names():
6967aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            try:
6977aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                self.load_extension(name)
6987aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            except:
6997aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                print "Failed to load extension", `name`
7007aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                import traceback
7017aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                traceback.print_exc()
7027aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
7037aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def get_standard_extension_names(self):
704dc72f48e262a7aaf177a37676c79ced4ddce0fffSteven M. Gava        return idleConf.GetExtensions()
7057aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
7067aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def load_extension(self, name):
7077aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        mod = __import__(name, globals(), locals(), [])
7087aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        cls = getattr(mod, name)
7097aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        ins = cls(self)
7107aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.extensions[name] = ins
71172c3bf076f785aaf54d63a7e8cae29bc8282920eSteven M. Gava        keydefs=idleConf.GetExtensionBindings(name)
7127aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if keydefs:
7137aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.apply_bindings(keydefs)
7147aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            for vevent in keydefs.keys():
7157aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                methodname = string.replace(vevent, "-", "_")
7167aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                while methodname[:1] == '<':
7177aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                    methodname = methodname[1:]
7187aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                while methodname[-1:] == '>':
7197aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                    methodname = methodname[:-1]
7207aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                methodname = methodname + "_event"
7217aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                if hasattr(ins, methodname):
7227aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                    self.text.bind(vevent, getattr(ins, methodname))
7237aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if hasattr(ins, "menudefs"):
7247aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.fill_menus(ins.menudefs, keydefs)
7257aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        return ins
7267aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
7277aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def apply_bindings(self, keydefs=None):
7287aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if keydefs is None:
7297aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            keydefs = self.Bindings.default_keydefs
7307aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text = self.text
7317aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text.keydefs = keydefs
7327aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        for event, keylist in keydefs.items():
7337aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            if keylist:
7347aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                apply(text.event_add, (event,) + tuple(keylist))
7357aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
7367aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def fill_menus(self, defs=None, keydefs=None):
73783118c6cb36cf9a424bec1b9a2ef8c8760bae8f5Kurt B. Kaiser        """Add appropriate entries to the menus and submenus
73883118c6cb36cf9a424bec1b9a2ef8c8760bae8f5Kurt B. Kaiser
73983118c6cb36cf9a424bec1b9a2ef8c8760bae8f5Kurt B. Kaiser        Menus that are absent or None in self.menudict are ignored.
74083118c6cb36cf9a424bec1b9a2ef8c8760bae8f5Kurt B. Kaiser        """
7417aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if defs is None:
7427aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            defs = self.Bindings.menudefs
7437aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if keydefs is None:
7447aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            keydefs = self.Bindings.default_keydefs
7457aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        menudict = self.menudict
7467aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text = self.text
7477aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        for mname, itemlist in defs:
7487aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            menu = menudict.get(mname)
7497aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            if not menu:
7507aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                continue
7517aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            for item in itemlist:
7527aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                if not item:
7537aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                    menu.add_separator()
7547aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                else:
7557aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                    label, event = item
7567aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                    checkbutton = (label[:1] == '!')
7577aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                    if checkbutton:
7587aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                        label = label[1:]
7597aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                    underline, label = prepstr(label)
7607aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                    accelerator = get_accelerator(keydefs, event)
7617aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                    def command(text=text, event=event):
7627aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                        text.event_generate(event)
7637aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                    if checkbutton:
7647aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                        var = self.getrawvar(event, BooleanVar)
7657aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                        menu.add_checkbutton(label=label, underline=underline,
7667aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                            command=command, accelerator=accelerator,
7677aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                            variable=var)
7687aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                    else:
7697aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                        menu.add_command(label=label, underline=underline,
7707aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                            command=command, accelerator=accelerator)
7717aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
7727aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def getvar(self, name):
7737aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        var = self.getrawvar(name)
7747aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if var:
7757aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return var.get()
7767aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
7777aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def setvar(self, name, value, vartype=None):
7787aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        var = self.getrawvar(name, vartype)
7797aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if var:
7807aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            var.set(value)
7817aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
7827aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def getrawvar(self, name, vartype=None):
7837aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        var = self.vars.get(name)
7847aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if not var and vartype:
7857aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.vars[name] = var = vartype(self.text)
7867aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        return var
7877aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
7887aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # Tk implementations of "virtual text methods" -- each platform
7897aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # reusing IDLE's support code needs to define these for its GUI's
7907aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # flavor of widget.
7917aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
7927aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # Is character at text_index in a Python string?  Return 0 for
7937aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # "guaranteed no", true for anything else.  This info is expensive
7947aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # to compute ab initio, but is probably already known by the
7957aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # platform's colorizer.
7967aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
7977aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def is_char_in_string(self, text_index):
7987aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if self.color:
7997aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            # Return true iff colorizer hasn't (re)gotten this far
8007aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            # yet, or the character is tagged as being in a string
8017aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return self.text.tag_prevrange("TODO", text_index) or \
8027aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                   "STRING" in self.text.tag_names(text_index)
8037aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        else:
8047aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            # The colorizer is missing: assume the worst
8057aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return 1
8067aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
8077aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # If a selection is defined in the text widget, return (start,
8087aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # end) as Tkinter text indices, otherwise return (None, None)
8097aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def get_selection_indices(self):
8107aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        try:
8117aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            first = self.text.index("sel.first")
8127aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            last = self.text.index("sel.last")
8137aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return first, last
8147aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        except TclError:
8157aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return None, None
8167aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
8177aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # Return the text widget's current view of what a tab stop means
8187aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # (equivalent width in spaces).
8197aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
8207aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def get_tabwidth(self):
8217aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        current = self.text['tabs'] or TK_TABWIDTH_DEFAULT
8227aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        return int(current)
8237aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
8247aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # Set the text widget's current view of what a tab stop means.
8257aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
8267aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def set_tabwidth(self, newtabwidth):
8277aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text = self.text
8287aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if self.get_tabwidth() != newtabwidth:
8297aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            pixels = text.tk.call("font", "measure", text["font"],
8307aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                                  "-displayof", text.master,
831afdf71b9eefdfb5904e40602984836b28318bad0Kurt B. Kaiser                                  "n" * newtabwidth)
8327aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            text.configure(tabs=pixels)
8337aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
834cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser### begin autoindent code ###
835cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
836cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # usetabs true  -> literal tab characters are used by indent and
837cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    #                  dedent cmds, possibly mixed with spaces if
838cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    #                  indentwidth is not a multiple of tabwidth
839cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    #         false -> tab characters are converted to spaces by indent
840cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    #                  and dedent cmds, and ditto TAB keystrokes
841cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # indentwidth is the number of characters per logical indent level.
842cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # tabwidth is the display width of a literal tab character.
843cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # CAUTION:  telling Tk to use anything other than its default
844cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # tab setting causes it to use an entirely different tabbing algorithm,
845cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # treating tab stops as fixed distances from the left margin.
846cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # Nobody expects this, so for now tabwidth should never be changed.
847cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    usetabs = 0
848cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    indentwidth = 4
849cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    tabwidth = 8    # for IDLE use, must remain 8 until Tk is fixed
850cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
851cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # If context_use_ps1 is true, parsing searches back for a ps1 line;
852cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # else searches for a popular (if, def, ...) Python stmt.
853cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    context_use_ps1 = 0
854cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
855cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # When searching backwards for a reliable place to begin parsing,
856cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # first start num_context_lines[0] lines back, then
857cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # num_context_lines[1] lines back if that didn't work, and so on.
858cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # The last value should be huge (larger than the # of lines in a
859cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # conceivable file).
860cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # Making the initial values larger slows things down more often.
861cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    num_context_lines = 50, 500, 5000000
862cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
863cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def config(self, **options):
864cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        for key, value in options.items():
865cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if key == 'usetabs':
866cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                self.usetabs = value
867cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            elif key == 'indentwidth':
868cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                self.indentwidth = value
869cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            elif key == 'tabwidth':
870cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                self.tabwidth = value
871cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            elif key == 'context_use_ps1':
872cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                self.context_use_ps1 = value
873cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            else:
874cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                raise KeyError, "bad option name: %s" % `key`
875cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
876cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # If ispythonsource and guess are true, guess a good value for
877cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # indentwidth based on file content (if possible), and if
878cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # indentwidth != tabwidth set usetabs false.
879cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # In any case, adjust the Text widget's view of what a tab
880cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # character means.
881cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
882cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def set_indentation_params(self, ispythonsource, guess=1):
883cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if guess and ispythonsource:
884cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            i = self.guess_indent()
885cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if 2 <= i <= 8:
886cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                self.indentwidth = i
887cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if self.indentwidth != self.tabwidth:
888cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                self.usetabs = 0
889cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
890cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        self.set_tabwidth(self.tabwidth)
891cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
892cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def smart_backspace_event(self, event):
893cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text = self.text
894cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        first, last = self.get_selection_indices()
895cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if first and last:
896cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            text.delete(first, last)
897cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            text.mark_set("insert", first)
898cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            return "break"
899cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        # Delete whitespace left, until hitting a real char or closest
900cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        # preceding virtual tab stop.
901cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        chars = text.get("insert linestart", "insert")
902cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if chars == '':
903cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if text.compare("insert", ">", "1.0"):
904cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                # easy: delete preceding newline
905cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                text.delete("insert-1c")
906cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            else:
907cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                text.bell()     # at start of buffer
908cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            return "break"
909cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if  chars[-1] not in " \t":
910cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            # easy: delete preceding real char
911cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            text.delete("insert-1c")
912cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            return "break"
913cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        # Ick.  It may require *inserting* spaces if we back up over a
914cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        # tab character!  This is written to be clear, not fast.
9151b3c26998e0f8b6975f7bbccf043c0afe398f151Kurt B. Kaiser        tabwidth = self.tabwidth
9161b3c26998e0f8b6975f7bbccf043c0afe398f151Kurt B. Kaiser        have = len(chars.expandtabs(tabwidth))
917cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        assert have > 0
918cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        want = ((have - 1) // self.indentwidth) * self.indentwidth
919cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        ncharsdeleted = 0
920cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        while 1:
921cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            chars = chars[:-1]
922cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            ncharsdeleted = ncharsdeleted + 1
9231b3c26998e0f8b6975f7bbccf043c0afe398f151Kurt B. Kaiser            have = len(chars.expandtabs(tabwidth))
924cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if have <= want or chars[-1] not in " \t":
925cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                break
926cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.undo_block_start()
927cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.delete("insert-%dc" % ncharsdeleted, "insert")
928cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if have < want:
929cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            text.insert("insert", ' ' * (want - have))
930cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.undo_block_stop()
931cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        return "break"
932cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
933cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def smart_indent_event(self, event):
934cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        # if intraline selection:
935cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        #     delete it
936cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        # elif multiline selection:
937cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        #     do indent-region & return
938cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        # indent one level
939cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text = self.text
940cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        first, last = self.get_selection_indices()
941cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.undo_block_start()
942cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        try:
943cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if first and last:
944cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                if index2line(first) != index2line(last):
945cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    return self.indent_region_event(event)
946cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                text.delete(first, last)
947cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                text.mark_set("insert", first)
948cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            prefix = text.get("insert linestart", "insert")
949cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            raw, effective = classifyws(prefix, self.tabwidth)
950cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if raw == len(prefix):
951cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                # only whitespace to the left
952cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                self.reindent_to(effective + self.indentwidth)
953cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            else:
954cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                if self.usetabs:
955cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    pad = '\t'
956cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                else:
9571b3c26998e0f8b6975f7bbccf043c0afe398f151Kurt B. Kaiser                    effective = len(prefix.expandtabs(self.tabwidth))
958cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    n = self.indentwidth
959cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    pad = ' ' * (n - effective % n)
960cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                text.insert("insert", pad)
961cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            text.see("insert")
962cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            return "break"
963cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        finally:
964cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            text.undo_block_stop()
965cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
966cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def newline_and_indent_event(self, event):
967cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text = self.text
968cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        first, last = self.get_selection_indices()
969cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.undo_block_start()
970cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        try:
971cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if first and last:
972cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                text.delete(first, last)
973cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                text.mark_set("insert", first)
974cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            line = text.get("insert linestart", "insert")
975cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            i, n = 0, len(line)
976cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            while i < n and line[i] in " \t":
977cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                i = i+1
978cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if i == n:
979cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                # the cursor is in or at leading indentation; just inject
980cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                # an empty line at the start
981cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                text.insert("insert linestart", '\n')
982cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                return "break"
983cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            indent = line[:i]
984cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            # strip whitespace before insert point
985cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            i = 0
986cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            while line and line[-1] in " \t":
987cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                line = line[:-1]
988cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                i = i+1
989cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if i:
990cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                text.delete("insert - %d chars" % i, "insert")
991cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            # strip whitespace after insert point
992cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            while text.get("insert") in " \t":
993cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                text.delete("insert")
994cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            # start new line
995cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            text.insert("insert", '\n')
996cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
997cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            # adjust indentation for continuations and block
998cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            # open/close first need to find the last stmt
999cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            lno = index2line(text.index('insert'))
1000cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            y = PyParse.Parser(self.indentwidth, self.tabwidth)
1001cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            for context in self.num_context_lines:
1002cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                startat = max(lno - context, 1)
1003cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                startatindex = `startat` + ".0"
1004cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                rawtext = text.get(startatindex, "insert")
1005cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                y.set_str(rawtext)
1006cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                bod = y.find_good_parse_start(
1007cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                          self.context_use_ps1,
1008cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                          self._build_char_in_string_func(startatindex))
1009cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                if bod is not None or startat == 1:
1010cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    break
1011cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            y.set_lo(bod or 0)
1012cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            c = y.get_continuation_type()
1013cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if c != PyParse.C_NONE:
1014cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                # The current stmt hasn't ended yet.
1015cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                if c == PyParse.C_STRING:
1016cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    # inside a string; just mimic the current indent
1017cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    text.insert("insert", indent)
1018cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                elif c == PyParse.C_BRACKET:
1019cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    # line up with the first (if any) element of the
1020cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    # last open bracket structure; else indent one
1021cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    # level beyond the indent of the line with the
1022cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    # last open bracket
1023cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    self.reindent_to(y.compute_bracket_indent())
1024cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                elif c == PyParse.C_BACKSLASH:
1025cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    # if more than one line in this stmt already, just
1026cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    # mimic the current indent; else if initial line
1027cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    # has a start on an assignment stmt, indent to
1028cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    # beyond leftmost =; else to beyond first chunk of
1029cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    # non-whitespace on initial line
1030cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    if y.get_num_lines_in_stmt() > 1:
1031cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                        text.insert("insert", indent)
1032cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    else:
1033cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                        self.reindent_to(y.compute_backslash_indent())
1034cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                else:
1035cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    assert 0, "bogus continuation type " + `c`
1036cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                return "break"
1037cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1038cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            # This line starts a brand new stmt; indent relative to
1039cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            # indentation of initial line of closest preceding
1040cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            # interesting stmt.
1041cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            indent = y.get_base_indent_string()
1042cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            text.insert("insert", indent)
1043cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if y.is_block_opener():
1044cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                self.smart_indent_event(event)
1045cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            elif indent and y.is_block_closer():
1046cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                self.smart_backspace_event(event)
1047cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            return "break"
1048cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        finally:
1049cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            text.see("insert")
1050cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            text.undo_block_stop()
1051cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1052cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    auto_indent = newline_and_indent_event
1053cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1054cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # Our editwin provides a is_char_in_string function that works
1055cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # with a Tk text index, but PyParse only knows about offsets into
1056cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # a string. This builds a function for PyParse that accepts an
1057cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # offset.
1058cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1059cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def _build_char_in_string_func(self, startindex):
1060cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        def inner(offset, _startindex=startindex,
1061cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                  _icis=self.is_char_in_string):
1062cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            return _icis(_startindex + "+%dc" % offset)
1063cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        return inner
1064cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1065cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def indent_region_event(self, event):
1066cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        head, tail, chars, lines = self.get_region()
1067cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        for pos in range(len(lines)):
1068cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            line = lines[pos]
1069cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if line:
1070cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                raw, effective = classifyws(line, self.tabwidth)
1071cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                effective = effective + self.indentwidth
1072cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                lines[pos] = self._make_blanks(effective) + line[raw:]
1073cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        self.set_region(head, tail, chars, lines)
1074cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        return "break"
1075cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1076cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def dedent_region_event(self, event):
1077cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        head, tail, chars, lines = self.get_region()
1078cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        for pos in range(len(lines)):
1079cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            line = lines[pos]
1080cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if line:
1081cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                raw, effective = classifyws(line, self.tabwidth)
1082cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                effective = max(effective - self.indentwidth, 0)
1083cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                lines[pos] = self._make_blanks(effective) + line[raw:]
1084cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        self.set_region(head, tail, chars, lines)
1085cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        return "break"
1086cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1087cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def comment_region_event(self, event):
1088cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        head, tail, chars, lines = self.get_region()
1089cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        for pos in range(len(lines) - 1):
1090cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            line = lines[pos]
1091cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            lines[pos] = '##' + line
1092cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        self.set_region(head, tail, chars, lines)
1093cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1094cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def uncomment_region_event(self, event):
1095cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        head, tail, chars, lines = self.get_region()
1096cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        for pos in range(len(lines)):
1097cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            line = lines[pos]
1098cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if not line:
1099cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                continue
1100cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if line[:2] == '##':
1101cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                line = line[2:]
1102cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            elif line[:1] == '#':
1103cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                line = line[1:]
1104cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            lines[pos] = line
1105cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        self.set_region(head, tail, chars, lines)
1106cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1107cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def tabify_region_event(self, event):
1108cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        head, tail, chars, lines = self.get_region()
1109cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        tabwidth = self._asktabwidth()
1110cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        for pos in range(len(lines)):
1111cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            line = lines[pos]
1112cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if line:
1113cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                raw, effective = classifyws(line, tabwidth)
1114cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                ntabs, nspaces = divmod(effective, tabwidth)
1115cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                lines[pos] = '\t' * ntabs + ' ' * nspaces + line[raw:]
1116cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        self.set_region(head, tail, chars, lines)
1117cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1118cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def untabify_region_event(self, event):
1119cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        head, tail, chars, lines = self.get_region()
1120cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        tabwidth = self._asktabwidth()
1121cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        for pos in range(len(lines)):
11221b3c26998e0f8b6975f7bbccf043c0afe398f151Kurt B. Kaiser            lines[pos] = lines[pos].expandtabs(tabwidth)
1123cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        self.set_region(head, tail, chars, lines)
1124cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1125cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def toggle_tabs_event(self, event):
1126cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if self.askyesno(
1127cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser              "Toggle tabs",
1128cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser              "Turn tabs " + ("on", "off")[self.usetabs] + "?",
1129cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser              parent=self.text):
1130cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            self.usetabs = not self.usetabs
1131cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        return "break"
1132cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1133cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # XXX this isn't bound to anything -- see class tabwidth comments
1134cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def change_tabwidth_event(self, event):
1135cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        new = self._asktabwidth()
1136cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if new != self.tabwidth:
1137cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            self.tabwidth = new
1138cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            self.set_indentation_params(0, guess=0)
1139cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        return "break"
1140cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1141cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def change_indentwidth_event(self, event):
1142cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        new = self.askinteger(
1143cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                  "Indent width",
1144cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                  "New indent width (2-16)",
1145cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                  parent=self.text,
1146cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                  initialvalue=self.indentwidth,
1147cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                  minvalue=2,
1148cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                  maxvalue=16)
1149cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if new and new != self.indentwidth:
1150cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            self.indentwidth = new
1151cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        return "break"
1152cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1153cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def get_region(self):
1154cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text = self.text
1155cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        first, last = self.get_selection_indices()
1156cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if first and last:
1157cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            head = text.index(first + " linestart")
1158cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            tail = text.index(last + "-1c lineend +1c")
1159cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        else:
1160cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            head = text.index("insert linestart")
1161cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            tail = text.index("insert lineend +1c")
1162cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        chars = text.get(head, tail)
11631b3c26998e0f8b6975f7bbccf043c0afe398f151Kurt B. Kaiser        lines = chars.split("\n")
1164cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        return head, tail, chars, lines
1165cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1166cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def set_region(self, head, tail, chars, lines):
1167cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text = self.text
11681b3c26998e0f8b6975f7bbccf043c0afe398f151Kurt B. Kaiser        newchars = "\n".join(lines)
1169cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if newchars == chars:
1170cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            text.bell()
1171cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            return
1172cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.tag_remove("sel", "1.0", "end")
1173cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.mark_set("insert", head)
1174cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.undo_block_start()
1175cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.delete(head, tail)
1176cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.insert(head, newchars)
1177cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.undo_block_stop()
1178cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.tag_add("sel", head, "insert")
1179cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1180cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # Make string that displays as n leading blanks.
1181cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1182cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def _make_blanks(self, n):
1183cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if self.usetabs:
1184cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            ntabs, nspaces = divmod(n, self.tabwidth)
1185cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            return '\t' * ntabs + ' ' * nspaces
1186cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        else:
1187cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            return ' ' * n
1188cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1189cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # Delete from beginning of line to insert point, then reinsert
1190cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # column logical (meaning use tabs if appropriate) spaces.
1191cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1192cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def reindent_to(self, column):
1193cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text = self.text
1194cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.undo_block_start()
1195cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if text.compare("insert linestart", "!=", "insert"):
1196cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            text.delete("insert linestart", "insert")
1197cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if column:
1198cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            text.insert("insert", self._make_blanks(column))
1199cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.undo_block_stop()
1200cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1201cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def _asktabwidth(self):
1202cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        return self.askinteger(
1203cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            "Tab width",
1204cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            "Spaces per tab? (2-16)",
1205cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            parent=self.text,
1206cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            initialvalue=self.indentwidth,
1207cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            minvalue=2,
1208cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            maxvalue=16) or self.tabwidth
1209cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1210cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # Guess indentwidth from text content.
1211cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # Return guessed indentwidth.  This should not be believed unless
1212cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # it's in a reasonable range (e.g., it will be 0 if no indented
1213cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # blocks are found).
1214cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1215cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def guess_indent(self):
1216cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        opener, indented = IndentSearcher(self.text, self.tabwidth).run()
1217cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if opener and indented:
1218cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            raw, indentsmall = classifyws(opener, self.tabwidth)
1219cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            raw, indentlarge = classifyws(indented, self.tabwidth)
1220cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        else:
1221cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            indentsmall = indentlarge = 0
1222cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        return indentlarge - indentsmall
1223cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1224cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser# "line.col" -> line, as an int
1225cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiserdef index2line(index):
1226cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    return int(float(index))
1227cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1228cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser# Look at the leading whitespace in s.
1229cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser# Return pair (# of leading ws characters,
1230cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser#              effective # of leading blanks after expanding
1231cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser#              tabs to width tabwidth)
1232cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1233cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiserdef classifyws(s, tabwidth):
1234cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    raw = effective = 0
1235cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    for ch in s:
1236cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if ch == ' ':
1237cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            raw = raw + 1
1238cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            effective = effective + 1
1239cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        elif ch == '\t':
1240cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            raw = raw + 1
1241cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            effective = (effective // tabwidth + 1) * tabwidth
1242cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        else:
1243cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            break
1244cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    return raw, effective
1245cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1246cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiserimport tokenize
1247cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser_tokenize = tokenize
1248cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiserdel tokenize
1249cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1250cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiserclass IndentSearcher:
1251cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1252cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # .run() chews over the Text widget, looking for a block opener
1253cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # and the stmt following it.  Returns a pair,
1254cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    #     (line containing block opener, line containing stmt)
1255cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # Either or both may be None.
1256cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1257cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def __init__(self, text, tabwidth):
1258cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        self.text = text
1259cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        self.tabwidth = tabwidth
1260cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        self.i = self.finished = 0
1261cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        self.blkopenline = self.indentedline = None
1262cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1263cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def readline(self):
1264cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if self.finished:
1265cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            return ""
1266cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        i = self.i = self.i + 1
1267cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        mark = `i` + ".0"
1268cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if self.text.compare(mark, ">=", "end"):
1269cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            return ""
1270cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        return self.text.get(mark, mark + " lineend+1c")
1271cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1272cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def tokeneater(self, type, token, start, end, line,
1273cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                   INDENT=_tokenize.INDENT,
1274cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                   NAME=_tokenize.NAME,
1275cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                   OPENERS=('class', 'def', 'for', 'if', 'try', 'while')):
1276cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if self.finished:
1277cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            pass
1278cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        elif type == NAME and token in OPENERS:
1279cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            self.blkopenline = line
1280cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        elif type == INDENT and self.blkopenline:
1281cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            self.indentedline = line
1282cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            self.finished = 1
1283cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1284cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def run(self):
1285cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        save_tabsize = _tokenize.tabsize
1286cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        _tokenize.tabsize = self.tabwidth
1287cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        try:
1288cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            try:
1289cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                _tokenize.tokenize(self.readline, self.tokeneater)
1290cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            except _tokenize.TokenError:
1291cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                # since we cut off the tokenizer early, we can trigger
1292cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                # spurious errors
1293cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                pass
1294cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        finally:
1295cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            _tokenize.tabsize = save_tabsize
1296cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        return self.blkopenline, self.indentedline
1297cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1298cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser### end autoindent code ###
1299cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
13007aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererdef prepstr(s):
13017aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # Helper to extract the underscore from a string, e.g.
13027aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # prepstr("Co_py") returns (2, "Copy").
13037aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    i = string.find(s, '_')
13047aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    if i >= 0:
13057aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        s = s[:i] + s[i+1:]
13067aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    return i, s
13077aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
13087aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
13097aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererkeynames = {
13107aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer 'bracketleft': '[',
13117aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer 'bracketright': ']',
13127aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer 'slash': '/',
13137aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer}
13147aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
13157aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererdef get_accelerator(keydefs, event):
13167aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    keylist = keydefs.get(event)
13177aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    if not keylist:
13187aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        return ""
13197aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    s = keylist[0]
13207aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    s = re.sub(r"-[a-z]\b", lambda m: string.upper(m.group()), s)
13217aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    s = re.sub(r"\b\w+\b", lambda m: keynames.get(m.group(), m.group()), s)
13227aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    s = re.sub("Key-", "", s)
13237aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    s = re.sub("Cancel","Ctrl-Break",s)   # dscherer@cmu.edu
13247aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    s = re.sub("Control-", "Ctrl-", s)
13257aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    s = re.sub("-", "+", s)
13267aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    s = re.sub("><", " ", s)
13277aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    s = re.sub("<", "", s)
13287aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    s = re.sub(">", "", s)
13297aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    return s
13307aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
13317aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
13327aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererdef fixwordbreaks(root):
13337aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # Make sure that Tk's double-click and next/previous word
13347aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # operations use our definition of a word (i.e. an identifier)
13357aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    tk = root.tk
13367aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    tk.call('tcl_wordBreakAfter', 'a b', 0) # make sure word.tcl is loaded
13377aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    tk.call('set', 'tcl_wordchars', '[a-zA-Z0-9_]')
13387aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    tk.call('set', 'tcl_nonwordchars', '[^a-zA-Z0-9_]')
13397aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
13407aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
13417aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererdef test():
13427aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    root = Tk()
13437aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    fixwordbreaks(root)
13447aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    root.withdraw()
13457aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    if sys.argv[1:]:
13467aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        filename = sys.argv[1]
13477aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    else:
13487aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        filename = None
13497aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    edit = EditorWindow(root=root, filename=filename)
13507aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    edit.set_close_hook(root.quit)
13517aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    root.mainloop()
13527aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    root.destroy()
13537aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
13547aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererif __name__ == '__main__':
13557aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    test()
1356