EditorWindow.py revision df506ea98b1c9424634c6063e90a664ac9127164
17aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererimport sys
27aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererimport os
37aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererimport re
47aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererimport imp
5cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiserfrom itertools import count
67aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererfrom Tkinter import *
77aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererimport tkSimpleDialog
87aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererimport tkMessageBox
9fd182cd9d36adefba00d86542eb3134119cdc804Kurt B. Kaiser
10fd182cd9d36adefba00d86542eb3134119cdc804Kurt B. Kaiserimport webbrowser
117aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererimport idlever
127aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererimport WindowList
13c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gavaimport SearchDialog
14c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gavaimport GrepDialog
15c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gavaimport ReplaceDialog
16cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiserimport PyParse
17dc72f48e262a7aaf177a37676c79ced4ddce0fffSteven M. Gavafrom configHandler import idleConf
183b55a891a197212b34b0c077f72cb3af752468ecSteven M. Gavaimport aboutDialog, textView, configDialog
197aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
207aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer# The default tab setting for a Text widget, in average-width characters.
217aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David SchererTK_TABWIDTH_DEFAULT = 8
227aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
23220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiserdef _find_module(fullname, path=None):
24220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser    """Version of imp.find_module() that handles hierarchical module names"""
25220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser
26220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser    file = None
27220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser    for tgt in fullname.split('.'):
28220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser        if file is not None:
29220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser            file.close()            # close intermediate files
30220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser        (file, filename, descr) = imp.find_module(tgt, path)
31220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser        if descr[2] == imp.PY_SOURCE:
32220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser            break                   # find but not load the source file
33220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser        module = imp.load_module(tgt, file, filename, descr)
3469e8afcc9fcd8a41a1035893d60893d51fae62f1Kurt B. Kaiser        try:
3569e8afcc9fcd8a41a1035893d60893d51fae62f1Kurt B. Kaiser            path = module.__path__
3669e8afcc9fcd8a41a1035893d60893d51fae62f1Kurt B. Kaiser        except AttributeError:
3769e8afcc9fcd8a41a1035893d60893d51fae62f1Kurt B. Kaiser            raise ImportError, 'No source for module ' + module.__name__
38220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser    return file, filename, descr
39220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser
40dcba6622f52efafa28104a07db9d5ba2b1a8d628Kurt B. Kaiserclass EditorWindow(object):
417aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    from Percolator import Percolator
427aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    from ColorDelegator import ColorDelegator
437aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    from UndoDelegator import UndoDelegator
447aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    from IOBinding import IOBinding
457aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    import Bindings
467aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    from Tkinter import Toplevel
477aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    from MultiStatusBar import MultiStatusBar
487aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
49114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser    help_url = None
507aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
517aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def __init__(self, flist=None, filename=None, key=None, root=None):
52114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser        if EditorWindow.help_url is None:
5384ef153c74decf0b036042b5ca3f5cb3cd785eddThomas Heller            dochome =  os.path.join(sys.prefix, 'Doc', 'index.html')
54114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser            if sys.platform.count('linux'):
55114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser                # look for html docs in a couple of standard places
56114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser                pyver = 'python-docs-' + '%s.%s.%s' % sys.version_info[:3]
57114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser                if os.path.isdir('/var/www/html/python/'):  # "python2" rpm
58114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser                    dochome = '/var/www/html/python/index.html'
59114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser                else:
60114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser                    basepath = '/usr/share/doc/'  # standard location
61114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser                    dochome = os.path.join(basepath, pyver,
62114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser                                           'Doc', 'index.html')
638aa23927e339ca13459dd408751546352d81fefaKurt B. Kaiser            elif sys.platform[:3] == 'win':
64090e636add33907d196b9899eb0f019654a055e8Kurt B. Kaiser                chmfile = os.path.join(sys.prefix, 'Doc',
65090e636add33907d196b9899eb0f019654a055e8Kurt B. Kaiser                                       'Python%d%d.chm' % sys.version_info[:2])
6684ef153c74decf0b036042b5ca3f5cb3cd785eddThomas Heller                if os.path.isfile(chmfile):
6784ef153c74decf0b036042b5ca3f5cb3cd785eddThomas Heller                    dochome = chmfile
68114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser            dochome = os.path.normpath(dochome)
69114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser            if os.path.isfile(dochome):
70114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser                EditorWindow.help_url = dochome
71114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser            else:
72114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser                EditorWindow.help_url = "http://www.python.org/doc/current"
73dc72f48e262a7aaf177a37676c79ced4ddce0fffSteven M. Gava        currentTheme=idleConf.CurrentTheme()
747aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.flist = flist
757aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        root = root or flist.root
767aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.root = root
777aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.menubar = Menu(root)
78183403a271977a26c0b77dbcf62e19395c007288Kurt B. Kaiser        self.top = top = WindowList.ListedToplevel(root, menu=self.menubar)
790c5bc8c9518fd18d41aedede536c4a9aa8dfa619Steven M. Gava        if flist:
80610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser            self.tkinter_vars = flist.vars
81cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser            #self.top.instance_dict makes flist.inversedict avalable to
820c5bc8c9518fd18d41aedede536c4a9aa8dfa619Steven M. Gava            #configDialog.py so it can access all EditorWindow instaces
83cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser            self.top.instance_dict=flist.inversedict
84610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser        else:
85610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser            self.tkinter_vars = {}  # keys: Tkinter event names
86610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser                                    # values: Tkinter variable instances
87cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        self.recent_files_path=os.path.join(idleConf.GetUserCfgDir(),
881d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava                'recent-files.lst')
897aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.vbar = vbar = Scrollbar(top, name='vbar')
907aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.text_frame = text_frame = Frame(top)
911061e7270b13a0e632c5a28791a46e38b48a39a0Kurt B. Kaiser        self.width = idleConf.GetOption('main','EditorWindow','width')
92a1dee069831c5551dc28d90d495ab5de967c17d5Kurt B. Kaiser        self.text = text = Text(text_frame, name='text', padx=5, wrap='none',
93dc72f48e262a7aaf177a37676c79ced4ddce0fffSteven M. Gava                foreground=idleConf.GetHighlight(currentTheme,
94dc72f48e262a7aaf177a37676c79ced4ddce0fffSteven M. Gava                        'normal',fgBg='fg'),
95dc72f48e262a7aaf177a37676c79ced4ddce0fffSteven M. Gava                background=idleConf.GetHighlight(currentTheme,
96dc72f48e262a7aaf177a37676c79ced4ddce0fffSteven M. Gava                        'normal',fgBg='bg'),
97dc72f48e262a7aaf177a37676c79ced4ddce0fffSteven M. Gava                highlightcolor=idleConf.GetHighlight(currentTheme,
98dc72f48e262a7aaf177a37676c79ced4ddce0fffSteven M. Gava                        'hilite',fgBg='fg'),
99dc72f48e262a7aaf177a37676c79ced4ddce0fffSteven M. Gava                highlightbackground=idleConf.GetHighlight(currentTheme,
100dc72f48e262a7aaf177a37676c79ced4ddce0fffSteven M. Gava                        'hilite',fgBg='bg'),
101dc72f48e262a7aaf177a37676c79ced4ddce0fffSteven M. Gava                insertbackground=idleConf.GetHighlight(currentTheme,
102dc72f48e262a7aaf177a37676c79ced4ddce0fffSteven M. Gava                        'cursor',fgBg='fg'),
1031061e7270b13a0e632c5a28791a46e38b48a39a0Kurt B. Kaiser                width=self.width,
104dc72f48e262a7aaf177a37676c79ced4ddce0fffSteven M. Gava                height=idleConf.GetOption('main','EditorWindow','height') )
105183403a271977a26c0b77dbcf62e19395c007288Kurt B. Kaiser        self.top.focused_widget = self.text
1067aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
1077aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.createmenubar()
1087aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.apply_bindings()
1097aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
1107aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.top.protocol("WM_DELETE_WINDOW", self.close)
1117aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.top.bind("<<close-window>>", self.close_event)
11284f4803f4f7ccf40cd777d1e9627528c30bc9053Kurt B. Kaiser        text.bind("<<cut>>", self.cut)
11384f4803f4f7ccf40cd777d1e9627528c30bc9053Kurt B. Kaiser        text.bind("<<copy>>", self.copy)
11484f4803f4f7ccf40cd777d1e9627528c30bc9053Kurt B. Kaiser        text.bind("<<paste>>", self.paste)
1157aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text.bind("<<center-insert>>", self.center_insert_event)
1167aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text.bind("<<help>>", self.help_dialog)
1177aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text.bind("<<python-docs>>", self.python_docs)
1187aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text.bind("<<about-idle>>", self.about_dialog)
1193b55a891a197212b34b0c077f72cb3af752468ecSteven M. Gava        text.bind("<<open-config-dialog>>", self.config_dialog)
1207aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text.bind("<<open-module>>", self.open_module)
1217aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text.bind("<<do-nothing>>", lambda event: "break")
1227aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text.bind("<<select-all>>", self.select_all)
1237aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text.bind("<<remove-selection>>", self.remove_selection)
124c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        text.bind("<<find>>", self.find_event)
125c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        text.bind("<<find-again>>", self.find_again_event)
126c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        text.bind("<<find-in-files>>", self.find_in_files_event)
127c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        text.bind("<<find-selection>>", self.find_selection_event)
128c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        text.bind("<<replace>>", self.replace_event)
129c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        text.bind("<<goto-line>>", self.goto_line_event)
1307aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text.bind("<3>", self.right_menu_event)
131cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.bind("<<smart-backspace>>",self.smart_backspace_event)
132cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.bind("<<newline-and-indent>>",self.newline_and_indent_event)
133cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.bind("<<smart-indent>>",self.smart_indent_event)
134cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.bind("<<indent-region>>",self.indent_region_event)
135cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.bind("<<dedent-region>>",self.dedent_region_event)
136cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.bind("<<comment-region>>",self.comment_region_event)
137cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.bind("<<uncomment-region>>",self.uncomment_region_event)
138cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.bind("<<tabify-region>>",self.tabify_region_event)
139cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.bind("<<untabify-region>>",self.untabify_region_event)
140cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.bind("<<toggle-tabs>>",self.toggle_tabs_event)
141cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.bind("<<change-indentwidth>>",self.change_indentwidth_event)
1425ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser        text.bind("<Left>", self.move_at_edge_if_selection(0))
1435ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser        text.bind("<Right>", self.move_at_edge_if_selection(1))
1443069dbb8ec9c287c2ff6b3a1c8bfde83af81c11bKurt B. Kaiser        text.bind("<<del-word-left>>", self.del_word_left)
1453069dbb8ec9c287c2ff6b3a1c8bfde83af81c11bKurt B. Kaiser        text.bind("<<del-word-right>>", self.del_word_right)
1466655e4bc2752f1114a2e1f9a63ffd4191fa50d0dKurt B. Kaiser
1477aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if flist:
1487aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            flist.inversedict[self] = key
1497aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            if key:
1507aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                flist.dict[key] = self
151d2f4861a0b52a2af5ea3395267a5c56541352f8fKurt B. Kaiser            text.bind("<<open-new-window>>", self.new_callback)
1527aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            text.bind("<<close-all-windows>>", self.flist.close_all_callback)
1537aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            text.bind("<<open-class-browser>>", self.open_class_browser)
1547aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            text.bind("<<open-path-browser>>", self.open_path_browser)
1557aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
156898a365c276951ab2471afe2e24bbc910666d361Steven M. Gava        self.set_status_bar()
1577aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        vbar['command'] = text.yview
1587aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        vbar.pack(side=RIGHT, fill=Y)
1597aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text['yscrollcommand'] = vbar.set
160acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser        fontWeight = 'normal'
161acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser        if idleConf.GetOption('main', 'EditorWindow', 'font-bold', type='bool'):
162b1585417d1f7ae8984ca72b16fd5a21746949ae5Steven M. Gava            fontWeight='bold'
163acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser        text.config(font=(idleConf.GetOption('main', 'EditorWindow', 'font'),
164acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser                          idleConf.GetOption('main', 'EditorWindow', 'font-size'),
165acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser                          fontWeight))
1667aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text_frame.pack(side=LEFT, fill=BOTH, expand=1)
1677aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text.pack(side=TOP, fill=BOTH, expand=1)
1687aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text.focus_set()
1697aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
1706af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        # usetabs true  -> literal tab characters are used by indent and
1716af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        #                  dedent cmds, possibly mixed with spaces if
1726af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        #                  indentwidth is not a multiple of tabwidth,
1736af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        #                  which will cause Tabnanny to nag!
1746af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        #         false -> tab characters are converted to spaces by indent
1756af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        #                  and dedent cmds, and ditto TAB keystrokes
176acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser        # Although use-spaces=0 can be configured manually in config-main.def,
177acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser        # configuration of tabs v. spaces is not supported in the configuration
178acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser        # dialog.  IDLE promotes the preferred Python indentation: use spaces!
179acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser        usespaces = idleConf.GetOption('main', 'Indent', 'use-spaces', type='bool')
180acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser        self.usetabs = not usespaces
1816af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser
1826af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        # tabwidth is the display width of a literal tab character.
1836af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        # CAUTION:  telling Tk to use anything other than its default
1846af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        # tab setting causes it to use an entirely different tabbing algorithm,
1856af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        # treating tab stops as fixed distances from the left margin.
1866af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        # Nobody expects this, so for now tabwidth should never be changed.
187acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser        self.tabwidth = 8    # must remain 8 until Tk is fixed.
188acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser
189acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser        # indentwidth is the number of screen characters per indent level.
190acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser        # The recommended Python indentation is four spaces.
191acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser        self.indentwidth = self.tabwidth
192acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser        self.set_notabs_indentwidth()
1936af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser
1946af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        # If context_use_ps1 is true, parsing searches back for a ps1 line;
1956af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        # else searches for a popular (if, def, ...) Python stmt.
1966af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        self.context_use_ps1 = False
1976af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser
1986af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        # When searching backwards for a reliable place to begin parsing,
1996af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        # first start num_context_lines[0] lines back, then
2006af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        # num_context_lines[1] lines back if that didn't work, and so on.
2016af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        # The last value should be huge (larger than the # of lines in a
2026af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        # conceivable file).
2036af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        # Making the initial values larger slows things down more often.
2046af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        self.num_context_lines = 50, 500, 5000000
2056af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser
2067aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.per = per = self.Percolator(text)
2077aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if self.ispythonsource(filename):
208dc1e70987f49aa23bf1d07f32c476edeba0cec30Kurt B. Kaiser            self.color = color = self.ColorDelegator()
209dc1e70987f49aa23bf1d07f32c476edeba0cec30Kurt B. Kaiser            per.insertfilter(color)
2107aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        else:
2117aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.color = None
212dc1e70987f49aa23bf1d07f32c476edeba0cec30Kurt B. Kaiser
213dc1e70987f49aa23bf1d07f32c476edeba0cec30Kurt B. Kaiser        self.undo = undo = self.UndoDelegator()
214dc1e70987f49aa23bf1d07f32c476edeba0cec30Kurt B. Kaiser        per.insertfilter(undo)
215dc1e70987f49aa23bf1d07f32c476edeba0cec30Kurt B. Kaiser        text.undo_block_start = undo.undo_block_start
216dc1e70987f49aa23bf1d07f32c476edeba0cec30Kurt B. Kaiser        text.undo_block_stop = undo.undo_block_stop
217dc1e70987f49aa23bf1d07f32c476edeba0cec30Kurt B. Kaiser        undo.set_saved_change_hook(self.saved_change_hook)
218dc1e70987f49aa23bf1d07f32c476edeba0cec30Kurt B. Kaiser
219dc1e70987f49aa23bf1d07f32c476edeba0cec30Kurt B. Kaiser        # IOBinding implements file I/O and printing functionality
2207aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.io = io = self.IOBinding(self)
221dc1e70987f49aa23bf1d07f32c476edeba0cec30Kurt B. Kaiser        io.set_filename_change_hook(self.filename_change_hook)
222dc1e70987f49aa23bf1d07f32c476edeba0cec30Kurt B. Kaiser
223cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        # Create the recent files submenu
224cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        self.recent_files_menu = Menu(self.menubar)
225cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        self.menudict['file'].insert_cascade(3, label='Recent Files',
226cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser                                             underline=0,
227cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser                                             menu=self.recent_files_menu)
228cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        self.update_recent_files_list()
2297aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
2307aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if filename:
231d2f4861a0b52a2af5ea3395267a5c56541352f8fKurt B. Kaiser            if os.path.exists(filename) and not os.path.isdir(filename):
2327aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                io.loadfile(filename)
2337aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            else:
2347aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                io.set_filename(filename)
2357aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.saved_change_hook()
2367aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
2376af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        self.set_indentation_params(self.ispythonsource(filename))
2386af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser
2397aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.load_extensions()
2407aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
2417aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        menu = self.menudict.get('windows')
2427aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if menu:
2437aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            end = menu.index("end")
2447aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            if end is None:
2457aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                end = -1
2467aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            if end >= 0:
2477aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                menu.add_separator()
2487aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                end = end + 1
2497aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.wmenu_end = end
2507aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            WindowList.register_callback(self.postwindowsmenu)
2517aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
2527aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        # Some abstractions so IDLE extensions are cross-IDE
2537aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.askyesno = tkMessageBox.askyesno
2547aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.askinteger = tkSimpleDialog.askinteger
2557aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.showerror = tkMessageBox.showerror
2567aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
257d2f4861a0b52a2af5ea3395267a5c56541352f8fKurt B. Kaiser    def new_callback(self, event):
258d2f4861a0b52a2af5ea3395267a5c56541352f8fKurt B. Kaiser        dirname, basename = self.io.defaultfilename()
259d2f4861a0b52a2af5ea3395267a5c56541352f8fKurt B. Kaiser        self.flist.new(dirname)
260d2f4861a0b52a2af5ea3395267a5c56541352f8fKurt B. Kaiser        return "break"
261d2f4861a0b52a2af5ea3395267a5c56541352f8fKurt B. Kaiser
2627aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def set_status_bar(self):
263898a365c276951ab2471afe2e24bbc910666d361Steven M. Gava        self.status_bar = self.MultiStatusBar(self.top)
2647aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.status_bar.set_label('column', 'Col: ?', side=RIGHT)
2657aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.status_bar.set_label('line', 'Ln: ?', side=RIGHT)
2667aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.status_bar.pack(side=BOTTOM, fill=X)
2677aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.text.bind('<KeyRelease>', self.set_line_and_column)
2687aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.text.bind('<ButtonRelease>', self.set_line_and_column)
2697aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.text.after_idle(self.set_line_and_column)
2707aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
2717aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def set_line_and_column(self, event=None):
272220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser        line, column = self.text.index(INSERT).split('.')
2737aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.status_bar.set_label('column', 'Col: %s' % column)
2747aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.status_bar.set_label('line', 'Ln: %s' % line)
2757aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
2767aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    menu_specs = [
2777aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        ("file", "_File"),
2787aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        ("edit", "_Edit"),
2797aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        ("format", "F_ormat"),
2807aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        ("run", "_Run"),
2811061e7270b13a0e632c5a28791a46e38b48a39a0Kurt B. Kaiser        ("options", "_Options"),
2827aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        ("windows", "_Windows"),
2837aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        ("help", "_Help"),
2847aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    ]
2857aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
2867aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def createmenubar(self):
2877aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        mbar = self.menubar
2887aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.menudict = menudict = {}
2897aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        for name, label in self.menu_specs:
2907aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            underline, label = prepstr(label)
2917aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            menudict[name] = menu = Menu(mbar, name=name)
2927aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            mbar.add_cascade(label=label, menu=menu, underline=underline)
2937aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.fill_menus()
2948e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser        self.base_helpmenu_length = self.menudict['help'].index(END)
2958e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser        self.reset_help_menu_entries()
2967aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
2977aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def postwindowsmenu(self):
2987aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        # Only called when Windows menu exists
2997aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        menu = self.menudict['windows']
3007aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        end = menu.index("end")
3017aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if end is None:
3027aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            end = -1
3037aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if end > self.wmenu_end:
3047aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            menu.delete(self.wmenu_end+1, end)
3057aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        WindowList.add_windows_to_menu(menu)
3067aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
3077aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    rmenu = None
3087aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
3097aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def right_menu_event(self, event):
3107aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.text.tag_remove("sel", "1.0", "end")
3117aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.text.mark_set("insert", "@%d,%d" % (event.x, event.y))
3127aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if not self.rmenu:
3137aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.make_rmenu()
3147aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        rmenu = self.rmenu
3157aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.event = event
3167aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        iswin = sys.platform[:3] == 'win'
3177aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if iswin:
3187aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.text.config(cursor="arrow")
3197aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        rmenu.tk_popup(event.x_root, event.y_root)
3207aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if iswin:
3217aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.text.config(cursor="ibeam")
3227aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
3237aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    rmenu_specs = [
3247aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        # ("Label", "<<virtual-event>>"), ...
3257aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        ("Close", "<<close-window>>"), # Example
3267aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    ]
3277aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
3287aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def make_rmenu(self):
3297aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        rmenu = Menu(self.text, tearoff=0)
3307aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        for label, eventname in self.rmenu_specs:
3317aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            def command(text=self.text, eventname=eventname):
3327aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                text.event_generate(eventname)
3337aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            rmenu.add_command(label=label, command=command)
3347aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.rmenu = rmenu
3357aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
3367aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def about_dialog(self, event=None):
337d78b23025c9c9f6288e14112411d83f44bcf85a8Kurt B. Kaiser        aboutDialog.AboutDialog(self.top,'About IDLE')
3386655e4bc2752f1114a2e1f9a63ffd4191fa50d0dKurt B. Kaiser
3393b55a891a197212b34b0c077f72cb3af752468ecSteven M. Gava    def config_dialog(self, event=None):
3403b55a891a197212b34b0c077f72cb3af752468ecSteven M. Gava        configDialog.ConfigDialog(self.top,'Settings')
3416655e4bc2752f1114a2e1f9a63ffd4191fa50d0dKurt B. Kaiser
3427aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def help_dialog(self, event=None):
343b9d07b5a8b8b70268ffa520895e7843ab594a486Steven M. Gava        fn=os.path.join(os.path.abspath(os.path.dirname(__file__)),'help.txt')
3446655e4bc2752f1114a2e1f9a63ffd4191fa50d0dKurt B. Kaiser        textView.TextViewer(self.top,'Help',fn)
3456655e4bc2752f1114a2e1f9a63ffd4191fa50d0dKurt B. Kaiser
346114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser    def python_docs(self, event=None):
3478aa23927e339ca13459dd408751546352d81fefaKurt B. Kaiser        if sys.platform[:3] == 'win':
348931625dc77cfd527b059e579615d517d8d994110Steven M. Gava            os.startfile(self.help_url)
349114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser        else:
350114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser            webbrowser.open(self.help_url)
3518aa23927e339ca13459dd408751546352d81fefaKurt B. Kaiser        return "break"
3527aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
35384f4803f4f7ccf40cd777d1e9627528c30bc9053Kurt B. Kaiser    def cut(self,event):
35484f4803f4f7ccf40cd777d1e9627528c30bc9053Kurt B. Kaiser        self.text.event_generate("<<Cut>>")
35584f4803f4f7ccf40cd777d1e9627528c30bc9053Kurt B. Kaiser        return "break"
35684f4803f4f7ccf40cd777d1e9627528c30bc9053Kurt B. Kaiser
35784f4803f4f7ccf40cd777d1e9627528c30bc9053Kurt B. Kaiser    def copy(self,event):
35884f4803f4f7ccf40cd777d1e9627528c30bc9053Kurt B. Kaiser        self.text.event_generate("<<Copy>>")
35984f4803f4f7ccf40cd777d1e9627528c30bc9053Kurt B. Kaiser        return "break"
36084f4803f4f7ccf40cd777d1e9627528c30bc9053Kurt B. Kaiser
36184f4803f4f7ccf40cd777d1e9627528c30bc9053Kurt B. Kaiser    def paste(self,event):
36284f4803f4f7ccf40cd777d1e9627528c30bc9053Kurt B. Kaiser        self.text.event_generate("<<Paste>>")
36384f4803f4f7ccf40cd777d1e9627528c30bc9053Kurt B. Kaiser        return "break"
36484f4803f4f7ccf40cd777d1e9627528c30bc9053Kurt B. Kaiser
3657aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def select_all(self, event=None):
3667aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.text.tag_add("sel", "1.0", "end-1c")
3677aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.text.mark_set("insert", "1.0")
3687aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.text.see("insert")
3697aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        return "break"
3707aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
3717aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def remove_selection(self, event=None):
3727aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.text.tag_remove("sel", "1.0", "end")
3737aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.text.see("insert")
3747aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
3755ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser    def move_at_edge_if_selection(self, edge_index):
3765ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser        """Cursor move begins at start or end of selection
3775ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser
3785ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser        When a left/right cursor key is pressed create and return to Tkinter a
3795ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser        function which causes a cursor move from the associated edge of the
3805ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser        selection.
3815ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser
3825ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser        """
3835ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser        self_text_index = self.text.index
3845ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser        self_text_mark_set = self.text.mark_set
3855ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser        edges_table = ("sel.first+1c", "sel.last-1c")
3865ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser        def move_at_edge(event):
3875ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser            if (event.state & 5) == 0: # no shift(==1) or control(==4) pressed
3885ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser                try:
3895ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser                    self_text_index("sel.first")
3905ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser                    self_text_mark_set("insert", edges_table[edge_index])
3915ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser                except TclError:
3925ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser                    pass
3935ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser        return move_at_edge
3945ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser
3953069dbb8ec9c287c2ff6b3a1c8bfde83af81c11bKurt B. Kaiser    def del_word_left(self, event):
3963069dbb8ec9c287c2ff6b3a1c8bfde83af81c11bKurt B. Kaiser        self.text.event_generate('<Meta-Delete>')
3973069dbb8ec9c287c2ff6b3a1c8bfde83af81c11bKurt B. Kaiser        return "break"
3983069dbb8ec9c287c2ff6b3a1c8bfde83af81c11bKurt B. Kaiser
3993069dbb8ec9c287c2ff6b3a1c8bfde83af81c11bKurt B. Kaiser    def del_word_right(self, event):
4003069dbb8ec9c287c2ff6b3a1c8bfde83af81c11bKurt B. Kaiser        self.text.event_generate('<Meta-d>')
4013069dbb8ec9c287c2ff6b3a1c8bfde83af81c11bKurt B. Kaiser        return "break"
4023069dbb8ec9c287c2ff6b3a1c8bfde83af81c11bKurt B. Kaiser
403c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava    def find_event(self, event):
404c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        SearchDialog.find(self.text)
405c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        return "break"
406c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava
407c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava    def find_again_event(self, event):
408c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        SearchDialog.find_again(self.text)
409c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        return "break"
410c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava
411c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava    def find_selection_event(self, event):
412c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        SearchDialog.find_selection(self.text)
413c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        return "break"
414c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava
415c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava    def find_in_files_event(self, event):
416c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        GrepDialog.grep(self.text, self.io, self.flist)
417c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        return "break"
418c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava
419c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava    def replace_event(self, event):
420c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        ReplaceDialog.replace(self.text)
421c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        return "break"
422c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava
423c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava    def goto_line_event(self, event):
424c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        text = self.text
425c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        lineno = tkSimpleDialog.askinteger("Goto",
426c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava                "Go to line number:",parent=text)
427c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        if lineno is None:
428c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava            return "break"
429c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        if lineno <= 0:
430c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava            text.bell()
431c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava            return "break"
432c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        text.mark_set("insert", "%d.0" % lineno)
433c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        text.see("insert")
434c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava
4357aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def open_module(self, event=None):
4367aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        # XXX Shouldn't this be in IOBinding or in FileList?
4377aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        try:
4387aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            name = self.text.get("sel.first", "sel.last")
4397aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        except TclError:
4407aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            name = ""
4417aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        else:
442220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser            name = name.strip()
443852f35bbeb23d898fb0d16489effb1197ee3fe02Guido van Rossum        name = tkSimpleDialog.askstring("Module",
444852f35bbeb23d898fb0d16489effb1197ee3fe02Guido van Rossum                 "Enter the name of a Python module\n"
445852f35bbeb23d898fb0d16489effb1197ee3fe02Guido van Rossum                 "to search on sys.path and open:",
446852f35bbeb23d898fb0d16489effb1197ee3fe02Guido van Rossum                 parent=self.text, initialvalue=name)
447852f35bbeb23d898fb0d16489effb1197ee3fe02Guido van Rossum        if name:
448852f35bbeb23d898fb0d16489effb1197ee3fe02Guido van Rossum            name = name.strip()
4497aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if not name:
450852f35bbeb23d898fb0d16489effb1197ee3fe02Guido van Rossum            return
4517aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        # XXX Ought to insert current file's directory in front of path
4527aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        try:
453220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser            (f, file, (suffix, mode, type)) = _find_module(name)
4547aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        except (NameError, ImportError), msg:
4557aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            tkMessageBox.showerror("Import error", str(msg), parent=self.text)
4567aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return
4577aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if type != imp.PY_SOURCE:
4587aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            tkMessageBox.showerror("Unsupported type",
4597aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                "%s is not a source module" % name, parent=self.text)
4607aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return
4617aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if f:
4627aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            f.close()
4637aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if self.flist:
4647aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.flist.open(file)
4657aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        else:
4667aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.io.loadfile(file)
4677aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
4687aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def open_class_browser(self, event=None):
4697aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        filename = self.io.filename
4707aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if not filename:
4717aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            tkMessageBox.showerror(
4727aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                "No filename",
4737aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                "This buffer has no associated filename",
4747aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                master=self.text)
4757aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.text.focus_set()
4767aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return None
4777aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        head, tail = os.path.split(filename)
4787aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        base, ext = os.path.splitext(tail)
4797aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        import ClassBrowser
4807aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        ClassBrowser.ClassBrowser(self.flist, base, [head])
4817aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
4827aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def open_path_browser(self, event=None):
4837aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        import PathBrowser
4847aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        PathBrowser.PathBrowser(self.flist)
4857aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
4867aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def gotoline(self, lineno):
4877aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if lineno is not None and lineno > 0:
4887aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.text.mark_set("insert", "%d.0" % lineno)
4897aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.text.tag_remove("sel", "1.0", "end")
4907aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.text.tag_add("sel", "insert", "insert +1l")
4917aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.center()
4927aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
4937aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def ispythonsource(self, filename):
494df506ea98b1c9424634c6063e90a664ac9127164Kurt B. Kaiser        if not filename or os.path.isdir(filename):
495220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser            return True
4967aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        base, ext = os.path.splitext(os.path.basename(filename))
4977aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if os.path.normcase(ext) in (".py", ".pyw"):
498220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser            return True
4997aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        try:
5007aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            f = open(filename)
5017aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            line = f.readline()
5027aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            f.close()
5037aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        except IOError:
504220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser            return False
50583a3560527f9e62bce6adbc12b8ff6b57be8b1acKurt B. Kaiser        return line.startswith('#!') and line.find('python') >= 0
5067aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
5077aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def close_hook(self):
5087aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if self.flist:
5097aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.flist.close_edit(self)
5107aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
5117aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def set_close_hook(self, close_hook):
5127aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.close_hook = close_hook
5137aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
5147aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def filename_change_hook(self):
5157aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if self.flist:
5167aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.flist.filename_changed_edit(self)
5177aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.saved_change_hook()
518260cb9034c861fa159f26fba8679ac265af47109Kurt B. Kaiser        self.top.update_windowlist_registry(self)
5197aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if self.ispythonsource(self.io.filename):
5207aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.addcolorizer()
5217aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        else:
5227aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.rmcolorizer()
5237aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
5247aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def addcolorizer(self):
5257aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if self.color:
5267aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return
5277aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.per.removefilter(self.undo)
5287aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.color = self.ColorDelegator()
5297aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.per.insertfilter(self.color)
5307aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.per.insertfilter(self.undo)
5317aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
5327aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def rmcolorizer(self):
5337aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if not self.color:
5347aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return
535df506ea98b1c9424634c6063e90a664ac9127164Kurt B. Kaiser        self.color.removecolors()
5367aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.per.removefilter(self.undo)
5377aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.per.removefilter(self.color)
5387aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.color = None
5397aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.per.insertfilter(self.undo)
5406655e4bc2752f1114a2e1f9a63ffd4191fa50d0dKurt B. Kaiser
541b77d343bc846c2049a4cffb1dfd65eb49d1728b4Steven M. Gava    def ResetColorizer(self):
54283118c6cb36cf9a424bec1b9a2ef8c8760bae8f5Kurt B. Kaiser        "Update the colour theme if it is changed"
54383118c6cb36cf9a424bec1b9a2ef8c8760bae8f5Kurt B. Kaiser        # Called from configDialog.py
544b77d343bc846c2049a4cffb1dfd65eb49d1728b4Steven M. Gava        if self.color:
545b77d343bc846c2049a4cffb1dfd65eb49d1728b4Steven M. Gava            self.color = self.ColorDelegator()
546b77d343bc846c2049a4cffb1dfd65eb49d1728b4Steven M. Gava            self.per.insertfilter(self.color)
54773360a3e61274ffcc4c9fc3d09746bd6603e92a5Kurt B. Kaiser        theme = idleConf.GetOption('main','Theme','name')
54873360a3e61274ffcc4c9fc3d09746bd6603e92a5Kurt B. Kaiser        self.text.config(idleConf.GetHighlight(theme, "normal"))
5497aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
550b1585417d1f7ae8984ca72b16fd5a21746949ae5Steven M. Gava    def ResetFont(self):
5516655e4bc2752f1114a2e1f9a63ffd4191fa50d0dKurt B. Kaiser        "Update the text widgets' font if it is changed"
55283118c6cb36cf9a424bec1b9a2ef8c8760bae8f5Kurt B. Kaiser        # Called from configDialog.py
553b1585417d1f7ae8984ca72b16fd5a21746949ae5Steven M. Gava        fontWeight='normal'
554b1585417d1f7ae8984ca72b16fd5a21746949ae5Steven M. Gava        if idleConf.GetOption('main','EditorWindow','font-bold',type='bool'):
555b1585417d1f7ae8984ca72b16fd5a21746949ae5Steven M. Gava            fontWeight='bold'
556b1585417d1f7ae8984ca72b16fd5a21746949ae5Steven M. Gava        self.text.config(font=(idleConf.GetOption('main','EditorWindow','font'),
557b1585417d1f7ae8984ca72b16fd5a21746949ae5Steven M. Gava                idleConf.GetOption('main','EditorWindow','font-size'),
558b1585417d1f7ae8984ca72b16fd5a21746949ae5Steven M. Gava                fontWeight))
559b1585417d1f7ae8984ca72b16fd5a21746949ae5Steven M. Gava
560dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava    def ResetKeybindings(self):
56183118c6cb36cf9a424bec1b9a2ef8c8760bae8f5Kurt B. Kaiser        "Update the keybindings if they are changed"
56283118c6cb36cf9a424bec1b9a2ef8c8760bae8f5Kurt B. Kaiser        # Called from configDialog.py
563dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava        self.Bindings.default_keydefs=idleConf.GetCurrentKeySet()
564dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava        keydefs = self.Bindings.default_keydefs
565dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava        for event, keylist in keydefs.items():
566dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava            self.text.event_delete(event)
567dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava        self.apply_bindings()
568dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava        #update menu accelerators
569dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava        menuEventDict={}
570dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava        for menu in self.Bindings.menudefs:
571dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava            menuEventDict[menu[0]]={}
572dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava            for item in menu[1]:
573dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava                if item:
574dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava                    menuEventDict[menu[0]][prepstr(item[0])[1]]=item[1]
575dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava        for menubarItem in self.menudict.keys():
576dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava            menu=self.menudict[menubarItem]
577dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava            end=menu.index(END)+1
578dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava            for index in range(0,end):
579dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava                if menu.type(index)=='command':
580dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava                    accel=menu.entrycget(index,'accelerator')
581dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava                    if accel:
582dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava                        itemName=menu.entrycget(index,'label')
583dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava                        event=''
584dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava                        if menuEventDict.has_key(menubarItem):
585dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava                            if menuEventDict[menubarItem].has_key(itemName):
586dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava                                event=menuEventDict[menubarItem][itemName]
587dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava                        if event:
588dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava                            accel=get_accelerator(keydefs, event)
589dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava                            menu.entryconfig(index,accelerator=accel)
590dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava
591acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser    def set_notabs_indentwidth(self):
592acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser        "Update the indentwidth if changed and not using tabs in this window"
593acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser        # Called from configDialog.py
594acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser        if not self.usetabs:
595acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser            self.indentwidth = idleConf.GetOption('main', 'Indent','num-spaces',
596acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser                                                  type='int')
597acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser
5988e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser    def reset_help_menu_entries(self):
5998e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser        "Update the additional help entries on the Help menu"
6008e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser        help_list = idleConf.GetAllExtraHelpSourcesList()
6018e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser        helpmenu = self.menudict['help']
6028e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser        # first delete the extra help entries, if any
6038e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser        helpmenu_length = helpmenu.index(END)
6048e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser        if helpmenu_length > self.base_helpmenu_length:
6058e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser            helpmenu.delete((self.base_helpmenu_length + 1), helpmenu_length)
6068e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser        # then rebuild them
6078e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser        if help_list:
6088e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser            helpmenu.add_separator()
6098e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser            for entry in help_list:
6108e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser                cmd = self.__extra_help_callback(entry[1])
6118e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser                helpmenu.add_command(label=entry[0], command=cmd)
6128e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser        # and update the menu dictionary
6138e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser        self.menudict['help'] = helpmenu
6148e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser
6158e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser    def __extra_help_callback(self, helpfile):
6168e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser        "Create a callback with the helpfile value frozen at definition time"
6178e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser        def display_extra_help(helpfile=helpfile):
6188aa23927e339ca13459dd408751546352d81fefaKurt B. Kaiser            if not (helpfile.startswith('www') or helpfile.startswith('http')):
6198aa23927e339ca13459dd408751546352d81fefaKurt B. Kaiser                url = os.path.normpath(helpfile)
6208aa23927e339ca13459dd408751546352d81fefaKurt B. Kaiser            if sys.platform[:3] == 'win':
6218aa23927e339ca13459dd408751546352d81fefaKurt B. Kaiser                os.startfile(helpfile)
6228aa23927e339ca13459dd408751546352d81fefaKurt B. Kaiser            else:
6238aa23927e339ca13459dd408751546352d81fefaKurt B. Kaiser                webbrowser.open(helpfile)
6248e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser        return display_extra_help
6256655e4bc2752f1114a2e1f9a63ffd4191fa50d0dKurt B. Kaiser
626cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser    def update_recent_files_list(self, new_file=None):
627cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        "Load and update the recent files list and menus"
628cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        rf_list = []
629cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        if os.path.exists(self.recent_files_path):
630cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser            rf_list_file = open(self.recent_files_path,'r')
6311d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava            try:
632cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser                rf_list = rf_list_file.readlines()
6331d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava            finally:
634cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser                rf_list_file.close()
635cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        if new_file:
636cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser            new_file = os.path.abspath(new_file) + '\n'
637cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser            if new_file in rf_list:
638cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser                rf_list.remove(new_file)  # move to top
639cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser            rf_list.insert(0, new_file)
640cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        # clean and save the recent files list
641cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        bad_paths = []
642cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        for path in rf_list:
643cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser            if '\0' in path or not os.path.exists(path[0:-1]):
644cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser                bad_paths.append(path)
645cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        rf_list = [path for path in rf_list if path not in bad_paths]
646cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        ulchars = "1234567890ABCDEFGHIJK"
647cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        rf_list = rf_list[0:len(ulchars)]
648cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        rf_file = open(self.recent_files_path, 'w')
6491d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava        try:
650cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser            rf_file.writelines(rf_list)
6511d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava        finally:
652cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser            rf_file.close()
653cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        # for each edit window instance, construct the recent files menu
654cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        for instance in self.top.instance_dict.keys():
655cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser            menu = instance.recent_files_menu
656cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser            menu.delete(1, END)  # clear, and rebuild:
657cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser            for i, file in zip(count(), rf_list):
658cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser                file_name = file[0:-1]  # zap \n
659cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser                callback = instance.__recent_file_callback(file_name)
660cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser                menu.add_command(label=ulchars[i] + " " + file_name,
661cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser                                 command=callback,
662cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser                                 underline=0)
663cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser
664cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser    def __recent_file_callback(self, file_name):
665cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        def open_recent_file(fn_closure=file_name):
666cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser            self.io.open(editFile=fn_closure)
667cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        return open_recent_file
6686655e4bc2752f1114a2e1f9a63ffd4191fa50d0dKurt B. Kaiser
6697aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def saved_change_hook(self):
6707aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        short = self.short_title()
6717aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        long = self.long_title()
6727aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if short and long:
6737aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            title = short + " - " + long
6747aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        elif short:
6757aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            title = short
6767aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        elif long:
6777aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            title = long
6787aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        else:
6797aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            title = "Untitled"
6807aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        icon = short or long or title
6817aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if not self.get_saved():
6827aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            title = "*%s*" % title
6837aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            icon = "*%s" % icon
6847aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.top.wm_title(title)
6857aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.top.wm_iconname(icon)
6867aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
6877aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def get_saved(self):
6887aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        return self.undo.get_saved()
6897aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
6907aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def set_saved(self, flag):
6917aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.undo.set_saved(flag)
6927aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
6937aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def reset_undo(self):
6947aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.undo.reset_undo()
6957aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
6967aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def short_title(self):
6977aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        filename = self.io.filename
6987aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if filename:
6997aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            filename = os.path.basename(filename)
7007aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        return filename
7017aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
7027aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def long_title(self):
7037aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        return self.io.filename or ""
7047aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
7057aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def center_insert_event(self, event):
7067aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.center()
7077aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
7087aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def center(self, mark="insert"):
7097aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text = self.text
7107aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        top, bot = self.getwindowlines()
7117aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        lineno = self.getlineno(mark)
7127aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        height = bot - top
713220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser        newtop = max(1, lineno - height//2)
7147aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text.yview(float(newtop))
7157aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
7167aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def getwindowlines(self):
7177aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text = self.text
7187aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        top = self.getlineno("@0,0")
7197aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        bot = self.getlineno("@0,65535")
7207aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if top == bot and text.winfo_height() == 1:
7217aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            # Geometry manager hasn't run yet
7227aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            height = int(text['height'])
7237aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            bot = top + height - 1
7247aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        return top, bot
7257aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
7267aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def getlineno(self, mark="insert"):
7277aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text = self.text
7287aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        return int(float(text.index(mark)))
7297aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
7301061e7270b13a0e632c5a28791a46e38b48a39a0Kurt B. Kaiser    def get_geometry(self):
7311061e7270b13a0e632c5a28791a46e38b48a39a0Kurt B. Kaiser        "Return (width, height, x, y)"
7321061e7270b13a0e632c5a28791a46e38b48a39a0Kurt B. Kaiser        geom = self.top.wm_geometry()
7331061e7270b13a0e632c5a28791a46e38b48a39a0Kurt B. Kaiser        m = re.match(r"(\d+)x(\d+)\+(-?\d+)\+(-?\d+)", geom)
7341061e7270b13a0e632c5a28791a46e38b48a39a0Kurt B. Kaiser        tuple = (map(int, m.groups()))
7351061e7270b13a0e632c5a28791a46e38b48a39a0Kurt B. Kaiser        return tuple
7361061e7270b13a0e632c5a28791a46e38b48a39a0Kurt B. Kaiser
7377aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def close_event(self, event):
7387aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.close()
7397aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
7407aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def maybesave(self):
7417aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if self.io:
74267716b5f53715e57d147cde9539b8d76a5a56e11Steven M. Gava            if not self.get_saved():
7436655e4bc2752f1114a2e1f9a63ffd4191fa50d0dKurt B. Kaiser                if self.top.state()!='normal':
74467716b5f53715e57d147cde9539b8d76a5a56e11Steven M. Gava                    self.top.deiconify()
74567716b5f53715e57d147cde9539b8d76a5a56e11Steven M. Gava                self.top.lower()
74667716b5f53715e57d147cde9539b8d76a5a56e11Steven M. Gava                self.top.lift()
7477aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return self.io.maybesave()
7487aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
7497aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def close(self):
7507aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        reply = self.maybesave()
7517aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if reply != "cancel":
7527aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self._close()
7537aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        return reply
7547aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
7557aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def _close(self):
7561d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava        if self.io.filename:
757cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser            self.update_recent_files_list(new_file=self.io.filename)
7587aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        WindowList.unregister_callback(self.postwindowsmenu)
7597aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if self.close_hook:
7607aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.close_hook()
7617aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.flist = None
7627aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        colorizing = 0
7637aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.unload_extensions()
7647aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.io.close(); self.io = None
7657aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.undo = None # XXX
7667aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if self.color:
7677aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            colorizing = self.color.colorizing
7687aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            doh = colorizing and self.top
7697aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.color.close(doh) # Cancel colorization
7707aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.text = None
771610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser        self.tkinter_vars = None
7727aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.per.close(); self.per = None
7737aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if not colorizing:
7747aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.top.destroy()
7757aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
7767aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def load_extensions(self):
7777aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.extensions = {}
7787aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.load_standard_extensions()
7797aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
7807aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def unload_extensions(self):
7817aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        for ins in self.extensions.values():
7827aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            if hasattr(ins, "close"):
7837aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                ins.close()
7847aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.extensions = {}
7857aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
7867aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def load_standard_extensions(self):
7877aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        for name in self.get_standard_extension_names():
7887aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            try:
7897aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                self.load_extension(name)
7907aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            except:
79170a6b49821a3226f55e9716f32d802d06640cb89Walter Dörwald                print "Failed to load extension", repr(name)
7927aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                import traceback
7937aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                traceback.print_exc()
7947aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
7957aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def get_standard_extension_names(self):
7964d5bc6031ca883201f87e0e3c94e5746f9f91439Kurt B. Kaiser        return idleConf.GetExtensions(editor_only=True)
7977aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
7987aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def load_extension(self, name):
799b00e89faab0d69feb040118bbc5ea6149e91867fKurt B. Kaiser        try:
800b00e89faab0d69feb040118bbc5ea6149e91867fKurt B. Kaiser            mod = __import__(name, globals(), locals(), [])
801b00e89faab0d69feb040118bbc5ea6149e91867fKurt B. Kaiser        except ImportError:
802b00e89faab0d69feb040118bbc5ea6149e91867fKurt B. Kaiser            print "\nFailed to import extension: ", name
803b00e89faab0d69feb040118bbc5ea6149e91867fKurt B. Kaiser            return
8047aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        cls = getattr(mod, name)
8054d5bc6031ca883201f87e0e3c94e5746f9f91439Kurt B. Kaiser        keydefs = idleConf.GetExtensionBindings(name)
8064d5bc6031ca883201f87e0e3c94e5746f9f91439Kurt B. Kaiser        if hasattr(cls, "menudefs"):
8074d5bc6031ca883201f87e0e3c94e5746f9f91439Kurt B. Kaiser            self.fill_menus(cls.menudefs, keydefs)
8087aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        ins = cls(self)
8097aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.extensions[name] = ins
8107aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if keydefs:
8117aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.apply_bindings(keydefs)
8127aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            for vevent in keydefs.keys():
813220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser                methodname = vevent.replace("-", "_")
8147aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                while methodname[:1] == '<':
8157aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                    methodname = methodname[1:]
8167aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                while methodname[-1:] == '>':
8177aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                    methodname = methodname[:-1]
8187aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                methodname = methodname + "_event"
8197aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                if hasattr(ins, methodname):
8207aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                    self.text.bind(vevent, getattr(ins, methodname))
8217aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
8227aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def apply_bindings(self, keydefs=None):
8237aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if keydefs is None:
8247aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            keydefs = self.Bindings.default_keydefs
8257aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text = self.text
8267aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text.keydefs = keydefs
8277aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        for event, keylist in keydefs.items():
8287aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            if keylist:
829931237e2e66975c54e2ac6c5e503ee2992a22bcfRaymond Hettinger                text.event_add(event, *keylist)
8307aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
831610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser    def fill_menus(self, menudefs=None, keydefs=None):
83283118c6cb36cf9a424bec1b9a2ef8c8760bae8f5Kurt B. Kaiser        """Add appropriate entries to the menus and submenus
83383118c6cb36cf9a424bec1b9a2ef8c8760bae8f5Kurt B. Kaiser
83483118c6cb36cf9a424bec1b9a2ef8c8760bae8f5Kurt B. Kaiser        Menus that are absent or None in self.menudict are ignored.
83583118c6cb36cf9a424bec1b9a2ef8c8760bae8f5Kurt B. Kaiser        """
836610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser        if menudefs is None:
837610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser            menudefs = self.Bindings.menudefs
8387aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if keydefs is None:
8397aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            keydefs = self.Bindings.default_keydefs
8407aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        menudict = self.menudict
8417aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text = self.text
842610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser        for mname, entrylist in menudefs:
8437aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            menu = menudict.get(mname)
8447aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            if not menu:
8457aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                continue
846610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser            for entry in entrylist:
847610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser                if not entry:
8487aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                    menu.add_separator()
8497aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                else:
850610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser                    label, eventname = entry
8517aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                    checkbutton = (label[:1] == '!')
8527aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                    if checkbutton:
8537aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                        label = label[1:]
8547aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                    underline, label = prepstr(label)
855610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser                    accelerator = get_accelerator(keydefs, eventname)
856610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser                    def command(text=text, eventname=eventname):
857610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser                        text.event_generate(eventname)
8587aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                    if checkbutton:
859610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser                        var = self.get_var_obj(eventname, BooleanVar)
8607aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                        menu.add_checkbutton(label=label, underline=underline,
8617aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                            command=command, accelerator=accelerator,
8627aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                            variable=var)
8637aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                    else:
8647aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                        menu.add_command(label=label, underline=underline,
86584f4803f4f7ccf40cd777d1e9627528c30bc9053Kurt B. Kaiser                                         command=command,
86684f4803f4f7ccf40cd777d1e9627528c30bc9053Kurt B. Kaiser                                         accelerator=accelerator)
8677aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
8687aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def getvar(self, name):
869610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser        var = self.get_var_obj(name)
8707aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if var:
871610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser            value = var.get()
872610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser            return value
873610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser        else:
874610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser            raise NameError, name
8757aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
8767aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def setvar(self, name, value, vartype=None):
877610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser        var = self.get_var_obj(name, vartype)
8787aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if var:
8797aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            var.set(value)
880610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser        else:
881610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser            raise NameError, name
8827aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
883610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser    def get_var_obj(self, name, vartype=None):
884610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser        var = self.tkinter_vars.get(name)
8857aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if not var and vartype:
886610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser            # create a Tkinter variable object with self.text as master:
887610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser            self.tkinter_vars[name] = var = vartype(self.text)
8887aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        return var
8897aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
8907aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # Tk implementations of "virtual text methods" -- each platform
8917aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # reusing IDLE's support code needs to define these for its GUI's
8927aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # flavor of widget.
8937aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
8947aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # Is character at text_index in a Python string?  Return 0 for
8957aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # "guaranteed no", true for anything else.  This info is expensive
8967aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # to compute ab initio, but is probably already known by the
8977aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # platform's colorizer.
8987aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
8997aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def is_char_in_string(self, text_index):
9007aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if self.color:
9017aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            # Return true iff colorizer hasn't (re)gotten this far
9027aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            # yet, or the character is tagged as being in a string
9037aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return self.text.tag_prevrange("TODO", text_index) or \
9047aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                   "STRING" in self.text.tag_names(text_index)
9057aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        else:
9067aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            # The colorizer is missing: assume the worst
9077aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return 1
9087aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
9097aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # If a selection is defined in the text widget, return (start,
9107aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # end) as Tkinter text indices, otherwise return (None, None)
9117aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def get_selection_indices(self):
9127aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        try:
9137aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            first = self.text.index("sel.first")
9147aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            last = self.text.index("sel.last")
9157aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return first, last
9167aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        except TclError:
9177aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return None, None
9187aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
9197aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # Return the text widget's current view of what a tab stop means
9207aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # (equivalent width in spaces).
9217aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
9227aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def get_tabwidth(self):
9237aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        current = self.text['tabs'] or TK_TABWIDTH_DEFAULT
9247aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        return int(current)
9257aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
9267aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # Set the text widget's current view of what a tab stop means.
9277aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
9287aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def set_tabwidth(self, newtabwidth):
9297aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text = self.text
9307aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if self.get_tabwidth() != newtabwidth:
9317aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            pixels = text.tk.call("font", "measure", text["font"],
9327aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                                  "-displayof", text.master,
933afdf71b9eefdfb5904e40602984836b28318bad0Kurt B. Kaiser                                  "n" * newtabwidth)
9347aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            text.configure(tabs=pixels)
9357aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
936cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # If ispythonsource and guess are true, guess a good value for
937cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # indentwidth based on file content (if possible), and if
938cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # indentwidth != tabwidth set usetabs false.
939cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # In any case, adjust the Text widget's view of what a tab
940cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # character means.
941cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
9426af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser    def set_indentation_params(self, ispythonsource, guess=True):
943cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if guess and ispythonsource:
944cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            i = self.guess_indent()
945cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if 2 <= i <= 8:
946cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                self.indentwidth = i
947cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if self.indentwidth != self.tabwidth:
9486af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser                self.usetabs = False
949cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        self.set_tabwidth(self.tabwidth)
950cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
951cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def smart_backspace_event(self, event):
952cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text = self.text
953cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        first, last = self.get_selection_indices()
954cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if first and last:
955cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            text.delete(first, last)
956cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            text.mark_set("insert", first)
957cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            return "break"
958cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        # Delete whitespace left, until hitting a real char or closest
959cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        # preceding virtual tab stop.
960cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        chars = text.get("insert linestart", "insert")
961cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if chars == '':
962cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if text.compare("insert", ">", "1.0"):
963cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                # easy: delete preceding newline
964cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                text.delete("insert-1c")
965cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            else:
966cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                text.bell()     # at start of buffer
967cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            return "break"
968cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if  chars[-1] not in " \t":
969cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            # easy: delete preceding real char
970cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            text.delete("insert-1c")
971cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            return "break"
972cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        # Ick.  It may require *inserting* spaces if we back up over a
973cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        # tab character!  This is written to be clear, not fast.
9741b3c26998e0f8b6975f7bbccf043c0afe398f151Kurt B. Kaiser        tabwidth = self.tabwidth
9751b3c26998e0f8b6975f7bbccf043c0afe398f151Kurt B. Kaiser        have = len(chars.expandtabs(tabwidth))
976cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        assert have > 0
977cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        want = ((have - 1) // self.indentwidth) * self.indentwidth
9784ada7ad3bc9ab269189ed20e57943d9721664debKurt B. Kaiser        # Debug prompt is multilined....
9794ada7ad3bc9ab269189ed20e57943d9721664debKurt B. Kaiser        last_line_of_prompt = sys.ps1.split('\n')[-1]
980cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        ncharsdeleted = 0
981cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        while 1:
9824ada7ad3bc9ab269189ed20e57943d9721664debKurt B. Kaiser            if chars == last_line_of_prompt:
9831bdca5e051a5424aaaaf7968c710d31132a6c335Kurt B. Kaiser                break
984cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            chars = chars[:-1]
985cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            ncharsdeleted = ncharsdeleted + 1
9861b3c26998e0f8b6975f7bbccf043c0afe398f151Kurt B. Kaiser            have = len(chars.expandtabs(tabwidth))
987cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if have <= want or chars[-1] not in " \t":
988cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                break
989cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.undo_block_start()
990cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.delete("insert-%dc" % ncharsdeleted, "insert")
991cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if have < want:
992cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            text.insert("insert", ' ' * (want - have))
993cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.undo_block_stop()
994cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        return "break"
995cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
996cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def smart_indent_event(self, event):
997cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        # if intraline selection:
998cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        #     delete it
999cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        # elif multiline selection:
10006af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        #     do indent-region
10016af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        # else:
10026af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        #     indent one level
1003cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text = self.text
1004cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        first, last = self.get_selection_indices()
1005cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.undo_block_start()
1006cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        try:
1007cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if first and last:
1008cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                if index2line(first) != index2line(last):
1009cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    return self.indent_region_event(event)
1010cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                text.delete(first, last)
1011cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                text.mark_set("insert", first)
1012cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            prefix = text.get("insert linestart", "insert")
1013cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            raw, effective = classifyws(prefix, self.tabwidth)
1014cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if raw == len(prefix):
1015cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                # only whitespace to the left
1016cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                self.reindent_to(effective + self.indentwidth)
1017cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            else:
10186af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser                # tab to the next 'stop' within or to right of line's text:
1019cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                if self.usetabs:
1020cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    pad = '\t'
1021cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                else:
10221b3c26998e0f8b6975f7bbccf043c0afe398f151Kurt B. Kaiser                    effective = len(prefix.expandtabs(self.tabwidth))
1023cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    n = self.indentwidth
1024cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    pad = ' ' * (n - effective % n)
1025cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                text.insert("insert", pad)
1026cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            text.see("insert")
1027cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            return "break"
1028cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        finally:
1029cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            text.undo_block_stop()
1030cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1031cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def newline_and_indent_event(self, event):
1032cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text = self.text
1033cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        first, last = self.get_selection_indices()
1034cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.undo_block_start()
1035cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        try:
1036cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if first and last:
1037cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                text.delete(first, last)
1038cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                text.mark_set("insert", first)
1039cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            line = text.get("insert linestart", "insert")
1040cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            i, n = 0, len(line)
1041cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            while i < n and line[i] in " \t":
1042cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                i = i+1
1043cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if i == n:
10444ada7ad3bc9ab269189ed20e57943d9721664debKurt B. Kaiser                # the cursor is in or at leading indentation in a continuation
10454ada7ad3bc9ab269189ed20e57943d9721664debKurt B. Kaiser                # line; just inject an empty line at the start
1046cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                text.insert("insert linestart", '\n')
1047cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                return "break"
1048cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            indent = line[:i]
10494ada7ad3bc9ab269189ed20e57943d9721664debKurt B. Kaiser            # strip whitespace before insert point unless it's in the prompt
1050cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            i = 0
10514ada7ad3bc9ab269189ed20e57943d9721664debKurt B. Kaiser            last_line_of_prompt = sys.ps1.split('\n')[-1]
10524ada7ad3bc9ab269189ed20e57943d9721664debKurt B. Kaiser            while line and line[-1] in " \t" and line != last_line_of_prompt:
1053cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                line = line[:-1]
1054cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                i = i+1
1055cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if i:
1056cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                text.delete("insert - %d chars" % i, "insert")
1057cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            # strip whitespace after insert point
1058cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            while text.get("insert") in " \t":
1059cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                text.delete("insert")
1060cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            # start new line
1061cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            text.insert("insert", '\n')
1062cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1063cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            # adjust indentation for continuations and block
1064cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            # open/close first need to find the last stmt
1065cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            lno = index2line(text.index('insert'))
1066cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            y = PyParse.Parser(self.indentwidth, self.tabwidth)
1067cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            for context in self.num_context_lines:
1068cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                startat = max(lno - context, 1)
106970a6b49821a3226f55e9716f32d802d06640cb89Walter Dörwald                startatindex = repr(startat) + ".0"
1070cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                rawtext = text.get(startatindex, "insert")
1071cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                y.set_str(rawtext)
1072cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                bod = y.find_good_parse_start(
1073cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                          self.context_use_ps1,
1074cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                          self._build_char_in_string_func(startatindex))
1075cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                if bod is not None or startat == 1:
1076cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    break
1077cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            y.set_lo(bod or 0)
1078cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            c = y.get_continuation_type()
1079cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if c != PyParse.C_NONE:
1080cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                # The current stmt hasn't ended yet.
1081cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                if c == PyParse.C_STRING:
1082cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    # inside a string; just mimic the current indent
1083cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    text.insert("insert", indent)
1084cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                elif c == PyParse.C_BRACKET:
1085cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    # line up with the first (if any) element of the
1086cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    # last open bracket structure; else indent one
1087cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    # level beyond the indent of the line with the
1088cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    # last open bracket
1089cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    self.reindent_to(y.compute_bracket_indent())
1090cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                elif c == PyParse.C_BACKSLASH:
1091cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    # if more than one line in this stmt already, just
1092cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    # mimic the current indent; else if initial line
1093cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    # has a start on an assignment stmt, indent to
1094cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    # beyond leftmost =; else to beyond first chunk of
1095cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    # non-whitespace on initial line
1096cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    if y.get_num_lines_in_stmt() > 1:
1097cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                        text.insert("insert", indent)
1098cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    else:
1099cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                        self.reindent_to(y.compute_backslash_indent())
1100cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                else:
110170a6b49821a3226f55e9716f32d802d06640cb89Walter Dörwald                    assert 0, "bogus continuation type %r" % (c,)
1102cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                return "break"
1103cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1104cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            # This line starts a brand new stmt; indent relative to
1105cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            # indentation of initial line of closest preceding
1106cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            # interesting stmt.
1107cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            indent = y.get_base_indent_string()
1108cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            text.insert("insert", indent)
1109cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if y.is_block_opener():
1110cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                self.smart_indent_event(event)
1111cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            elif indent and y.is_block_closer():
1112cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                self.smart_backspace_event(event)
1113cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            return "break"
1114cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        finally:
1115cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            text.see("insert")
1116cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            text.undo_block_stop()
1117cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1118cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # Our editwin provides a is_char_in_string function that works
1119cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # with a Tk text index, but PyParse only knows about offsets into
1120cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # a string. This builds a function for PyParse that accepts an
1121cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # offset.
1122cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1123cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def _build_char_in_string_func(self, startindex):
1124cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        def inner(offset, _startindex=startindex,
1125cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                  _icis=self.is_char_in_string):
1126cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            return _icis(_startindex + "+%dc" % offset)
1127cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        return inner
1128cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1129cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def indent_region_event(self, event):
1130cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        head, tail, chars, lines = self.get_region()
1131cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        for pos in range(len(lines)):
1132cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            line = lines[pos]
1133cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if line:
1134cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                raw, effective = classifyws(line, self.tabwidth)
1135cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                effective = effective + self.indentwidth
1136cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                lines[pos] = self._make_blanks(effective) + line[raw:]
1137cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        self.set_region(head, tail, chars, lines)
1138cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        return "break"
1139cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1140cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def dedent_region_event(self, event):
1141cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        head, tail, chars, lines = self.get_region()
1142cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        for pos in range(len(lines)):
1143cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            line = lines[pos]
1144cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if line:
1145cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                raw, effective = classifyws(line, self.tabwidth)
1146cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                effective = max(effective - self.indentwidth, 0)
1147cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                lines[pos] = self._make_blanks(effective) + line[raw:]
1148cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        self.set_region(head, tail, chars, lines)
1149cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        return "break"
1150cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1151cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def comment_region_event(self, event):
1152cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        head, tail, chars, lines = self.get_region()
1153cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        for pos in range(len(lines) - 1):
1154cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            line = lines[pos]
1155cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            lines[pos] = '##' + line
1156cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        self.set_region(head, tail, chars, lines)
1157cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1158cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def uncomment_region_event(self, event):
1159cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        head, tail, chars, lines = self.get_region()
1160cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        for pos in range(len(lines)):
1161cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            line = lines[pos]
1162cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if not line:
1163cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                continue
1164cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if line[:2] == '##':
1165cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                line = line[2:]
1166cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            elif line[:1] == '#':
1167cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                line = line[1:]
1168cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            lines[pos] = line
1169cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        self.set_region(head, tail, chars, lines)
1170cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1171cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def tabify_region_event(self, event):
1172cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        head, tail, chars, lines = self.get_region()
1173cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        tabwidth = self._asktabwidth()
1174cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        for pos in range(len(lines)):
1175cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            line = lines[pos]
1176cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if line:
1177cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                raw, effective = classifyws(line, tabwidth)
1178cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                ntabs, nspaces = divmod(effective, tabwidth)
1179cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                lines[pos] = '\t' * ntabs + ' ' * nspaces + line[raw:]
1180cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        self.set_region(head, tail, chars, lines)
1181cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1182cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def untabify_region_event(self, event):
1183cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        head, tail, chars, lines = self.get_region()
1184cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        tabwidth = self._asktabwidth()
1185cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        for pos in range(len(lines)):
11861b3c26998e0f8b6975f7bbccf043c0afe398f151Kurt B. Kaiser            lines[pos] = lines[pos].expandtabs(tabwidth)
1187cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        self.set_region(head, tail, chars, lines)
1188cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1189cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def toggle_tabs_event(self, event):
1190cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if self.askyesno(
1191cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser              "Toggle tabs",
11926af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser              "Turn tabs " + ("on", "off")[self.usetabs] +
11936af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser              "?\nIndent width " +
11946af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser              ("will be", "remains at")[self.usetabs] + " 8.",
1195cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser              parent=self.text):
1196cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            self.usetabs = not self.usetabs
11976af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        # Try to prevent mixed tabs/spaces.
11986af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        # User must reset indent width manually after using tabs
11996af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        #      if he insists on getting into trouble.
12006af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        self.indentwidth = 8
1201cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        return "break"
1202cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
12036af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser    # XXX this isn't bound to anything -- see tabwidth comments
12046af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser##     def change_tabwidth_event(self, event):
12056af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser##         new = self._asktabwidth()
12066af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser##         if new != self.tabwidth:
12076af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser##             self.tabwidth = new
12086af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser##             self.set_indentation_params(0, guess=0)
12096af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser##         return "break"
1210cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1211cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def change_indentwidth_event(self, event):
1212cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        new = self.askinteger(
1213cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                  "Indent width",
12146af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser                  "New indent width (2-16)\n(Always use 8 when using tabs)",
1215cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                  parent=self.text,
1216cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                  initialvalue=self.indentwidth,
1217cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                  minvalue=2,
1218cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                  maxvalue=16)
12196af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        if new and new != self.indentwidth and not self.usetabs:
1220cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            self.indentwidth = new
1221cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        return "break"
1222cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1223cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def get_region(self):
1224cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text = self.text
1225cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        first, last = self.get_selection_indices()
1226cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if first and last:
1227cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            head = text.index(first + " linestart")
1228cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            tail = text.index(last + "-1c lineend +1c")
1229cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        else:
1230cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            head = text.index("insert linestart")
1231cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            tail = text.index("insert lineend +1c")
1232cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        chars = text.get(head, tail)
12331b3c26998e0f8b6975f7bbccf043c0afe398f151Kurt B. Kaiser        lines = chars.split("\n")
1234cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        return head, tail, chars, lines
1235cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1236cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def set_region(self, head, tail, chars, lines):
1237cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text = self.text
12381b3c26998e0f8b6975f7bbccf043c0afe398f151Kurt B. Kaiser        newchars = "\n".join(lines)
1239cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if newchars == chars:
1240cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            text.bell()
1241cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            return
1242cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.tag_remove("sel", "1.0", "end")
1243cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.mark_set("insert", head)
1244cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.undo_block_start()
1245cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.delete(head, tail)
1246cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.insert(head, newchars)
1247cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.undo_block_stop()
1248cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.tag_add("sel", head, "insert")
1249cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1250cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # Make string that displays as n leading blanks.
1251cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1252cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def _make_blanks(self, n):
1253cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if self.usetabs:
1254cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            ntabs, nspaces = divmod(n, self.tabwidth)
1255cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            return '\t' * ntabs + ' ' * nspaces
1256cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        else:
1257cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            return ' ' * n
1258cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1259cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # Delete from beginning of line to insert point, then reinsert
1260cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # column logical (meaning use tabs if appropriate) spaces.
1261cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1262cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def reindent_to(self, column):
1263cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text = self.text
1264cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.undo_block_start()
1265cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if text.compare("insert linestart", "!=", "insert"):
1266cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            text.delete("insert linestart", "insert")
1267cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if column:
1268cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            text.insert("insert", self._make_blanks(column))
1269cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.undo_block_stop()
1270cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1271cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def _asktabwidth(self):
1272cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        return self.askinteger(
1273cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            "Tab width",
1274cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            "Spaces per tab? (2-16)",
1275cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            parent=self.text,
1276cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            initialvalue=self.indentwidth,
1277cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            minvalue=2,
1278cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            maxvalue=16) or self.tabwidth
1279cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1280cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # Guess indentwidth from text content.
1281cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # Return guessed indentwidth.  This should not be believed unless
1282cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # it's in a reasonable range (e.g., it will be 0 if no indented
1283cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # blocks are found).
1284cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1285cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def guess_indent(self):
1286cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        opener, indented = IndentSearcher(self.text, self.tabwidth).run()
1287cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if opener and indented:
1288cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            raw, indentsmall = classifyws(opener, self.tabwidth)
1289cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            raw, indentlarge = classifyws(indented, self.tabwidth)
1290cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        else:
1291cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            indentsmall = indentlarge = 0
1292cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        return indentlarge - indentsmall
1293cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1294cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser# "line.col" -> line, as an int
1295cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiserdef index2line(index):
1296cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    return int(float(index))
1297cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1298cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser# Look at the leading whitespace in s.
1299cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser# Return pair (# of leading ws characters,
1300cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser#              effective # of leading blanks after expanding
1301cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser#              tabs to width tabwidth)
1302cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1303cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiserdef classifyws(s, tabwidth):
1304cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    raw = effective = 0
1305cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    for ch in s:
1306cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if ch == ' ':
1307cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            raw = raw + 1
1308cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            effective = effective + 1
1309cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        elif ch == '\t':
1310cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            raw = raw + 1
1311cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            effective = (effective // tabwidth + 1) * tabwidth
1312cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        else:
1313cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            break
1314cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    return raw, effective
1315cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1316cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiserimport tokenize
1317cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser_tokenize = tokenize
1318cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiserdel tokenize
1319cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1320dcba6622f52efafa28104a07db9d5ba2b1a8d628Kurt B. Kaiserclass IndentSearcher(object):
1321cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1322cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # .run() chews over the Text widget, looking for a block opener
1323cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # and the stmt following it.  Returns a pair,
1324cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    #     (line containing block opener, line containing stmt)
1325cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # Either or both may be None.
1326cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1327cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def __init__(self, text, tabwidth):
1328cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        self.text = text
1329cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        self.tabwidth = tabwidth
1330cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        self.i = self.finished = 0
1331cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        self.blkopenline = self.indentedline = None
1332cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1333cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def readline(self):
1334cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if self.finished:
1335cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            return ""
1336cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        i = self.i = self.i + 1
133770a6b49821a3226f55e9716f32d802d06640cb89Walter Dörwald        mark = repr(i) + ".0"
1338cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if self.text.compare(mark, ">=", "end"):
1339cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            return ""
1340cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        return self.text.get(mark, mark + " lineend+1c")
1341cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1342cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def tokeneater(self, type, token, start, end, line,
1343cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                   INDENT=_tokenize.INDENT,
1344cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                   NAME=_tokenize.NAME,
1345cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                   OPENERS=('class', 'def', 'for', 'if', 'try', 'while')):
1346cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if self.finished:
1347cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            pass
1348cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        elif type == NAME and token in OPENERS:
1349cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            self.blkopenline = line
1350cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        elif type == INDENT and self.blkopenline:
1351cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            self.indentedline = line
1352cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            self.finished = 1
1353cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1354cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def run(self):
1355cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        save_tabsize = _tokenize.tabsize
1356cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        _tokenize.tabsize = self.tabwidth
1357cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        try:
1358cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            try:
1359cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                _tokenize.tokenize(self.readline, self.tokeneater)
1360cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            except _tokenize.TokenError:
1361cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                # since we cut off the tokenizer early, we can trigger
1362cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                # spurious errors
1363cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                pass
1364cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        finally:
1365cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            _tokenize.tabsize = save_tabsize
1366cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        return self.blkopenline, self.indentedline
1367cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1368cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser### end autoindent code ###
1369cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
13707aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererdef prepstr(s):
13717aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # Helper to extract the underscore from a string, e.g.
13727aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # prepstr("Co_py") returns (2, "Copy").
1373220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser    i = s.find('_')
13747aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    if i >= 0:
13757aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        s = s[:i] + s[i+1:]
13767aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    return i, s
13777aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
13787aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
13797aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererkeynames = {
13807aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer 'bracketleft': '[',
13817aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer 'bracketright': ']',
13827aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer 'slash': '/',
13837aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer}
13847aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
1385610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiserdef get_accelerator(keydefs, eventname):
1386610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser    keylist = keydefs.get(eventname)
13877aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    if not keylist:
13887aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        return ""
13897aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    s = keylist[0]
1390220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser    s = re.sub(r"-[a-z]\b", lambda m: m.group().upper(), s)
13917aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    s = re.sub(r"\b\w+\b", lambda m: keynames.get(m.group(), m.group()), s)
13927aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    s = re.sub("Key-", "", s)
13937aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    s = re.sub("Cancel","Ctrl-Break",s)   # dscherer@cmu.edu
13947aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    s = re.sub("Control-", "Ctrl-", s)
13957aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    s = re.sub("-", "+", s)
13967aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    s = re.sub("><", " ", s)
13977aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    s = re.sub("<", "", s)
13987aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    s = re.sub(">", "", s)
13997aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    return s
14007aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
14017aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
14027aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererdef fixwordbreaks(root):
14037aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # Make sure that Tk's double-click and next/previous word
14047aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # operations use our definition of a word (i.e. an identifier)
14057aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    tk = root.tk
14067aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    tk.call('tcl_wordBreakAfter', 'a b', 0) # make sure word.tcl is loaded
14077aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    tk.call('set', 'tcl_wordchars', '[a-zA-Z0-9_]')
14087aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    tk.call('set', 'tcl_nonwordchars', '[^a-zA-Z0-9_]')
14097aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
14107aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
14117aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererdef test():
14127aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    root = Tk()
14137aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    fixwordbreaks(root)
14147aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    root.withdraw()
14157aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    if sys.argv[1:]:
14167aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        filename = sys.argv[1]
14177aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    else:
14187aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        filename = None
14197aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    edit = EditorWindow(root=root, filename=filename)
14207aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    edit.set_close_hook(root.quit)
14217aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    root.mainloop()
14227aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    root.destroy()
14237aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
14247aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererif __name__ == '__main__':
14257aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    test()
1426