EditorWindow.py revision 6e3dbbdf39f3b4eb6f18c0165e446df17218b7dc
17aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererimport sys
27aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererimport os
37aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererimport re
47aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererimport imp
56634bf2919d855ccd821e878b8cc00c7209f1cbeGeorg Brandlfrom Tkinter import *
66634bf2919d855ccd821e878b8cc00c7209f1cbeGeorg Brandlimport tkSimpleDialog
76634bf2919d855ccd821e878b8cc00c7209f1cbeGeorg Brandlimport tkMessageBox
8b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiserfrom MultiCall import MultiCallCreator
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
1919302d927e6688e02553df16177e4867e2d0e3b3Ronald Oussorenimport macosxSupport
207aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
217aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer# The default tab setting for a Text widget, in average-width characters.
227aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David SchererTK_TABWIDTH_DEFAULT = 8
237aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
24f13447f3f75c7b56eec6a602b0b72a48fbbc5486Kurt B. Kaiserdef _sphinx_version():
25f13447f3f75c7b56eec6a602b0b72a48fbbc5486Kurt B. Kaiser    "Format sys.version_info to produce the Sphinx version string used to install the chm docs"
26f13447f3f75c7b56eec6a602b0b72a48fbbc5486Kurt B. Kaiser    major, minor, micro, level, serial = sys.version_info
27f13447f3f75c7b56eec6a602b0b72a48fbbc5486Kurt B. Kaiser    release = '%s%s' % (major, minor)
28f13447f3f75c7b56eec6a602b0b72a48fbbc5486Kurt B. Kaiser    if micro:
29fb23463139a173062031df9c258ac772b56ae08dBenjamin Peterson        release += '%s' % (micro,)
30fb23463139a173062031df9c258ac772b56ae08dBenjamin Peterson    if level == 'candidate':
31fb23463139a173062031df9c258ac772b56ae08dBenjamin Peterson        release += 'rc%s' % (serial,)
32fb23463139a173062031df9c258ac772b56ae08dBenjamin Peterson    elif level != 'final':
33f13447f3f75c7b56eec6a602b0b72a48fbbc5486Kurt B. Kaiser        release += '%s%s' % (level[0], serial)
34f13447f3f75c7b56eec6a602b0b72a48fbbc5486Kurt B. Kaiser    return release
35f13447f3f75c7b56eec6a602b0b72a48fbbc5486Kurt B. Kaiser
36220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiserdef _find_module(fullname, path=None):
37220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser    """Version of imp.find_module() that handles hierarchical module names"""
38220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser
39220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser    file = None
40220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser    for tgt in fullname.split('.'):
41220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser        if file is not None:
42220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser            file.close()            # close intermediate files
43220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser        (file, filename, descr) = imp.find_module(tgt, path)
44220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser        if descr[2] == imp.PY_SOURCE:
45220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser            break                   # find but not load the source file
46220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser        module = imp.load_module(tgt, file, filename, descr)
4769e8afcc9fcd8a41a1035893d60893d51fae62f1Kurt B. Kaiser        try:
4869e8afcc9fcd8a41a1035893d60893d51fae62f1Kurt B. Kaiser            path = module.__path__
4969e8afcc9fcd8a41a1035893d60893d51fae62f1Kurt B. Kaiser        except AttributeError:
5069e8afcc9fcd8a41a1035893d60893d51fae62f1Kurt B. Kaiser            raise ImportError, 'No source for module ' + module.__name__
51220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser    return file, filename, descr
52220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser
53dcba6622f52efafa28104a07db9d5ba2b1a8d628Kurt B. Kaiserclass EditorWindow(object):
547aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    from Percolator import Percolator
557aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    from ColorDelegator import ColorDelegator
567aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    from UndoDelegator import UndoDelegator
57307021f40b227d2bf114b536c37cc41e03fc2affMartin v. Löwis    from IOBinding import IOBinding, filesystemencoding, encoding
587aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    import Bindings
596634bf2919d855ccd821e878b8cc00c7209f1cbeGeorg Brandl    from Tkinter import Toplevel
607aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    from MultiStatusBar import MultiStatusBar
617aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
62114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser    help_url = None
637aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
647aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def __init__(self, flist=None, filename=None, key=None, root=None):
65114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser        if EditorWindow.help_url is None:
6684ef153c74decf0b036042b5ca3f5cb3cd785eddThomas Heller            dochome =  os.path.join(sys.prefix, 'Doc', 'index.html')
67114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser            if sys.platform.count('linux'):
68114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser                # look for html docs in a couple of standard places
69114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser                pyver = 'python-docs-' + '%s.%s.%s' % sys.version_info[:3]
70114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser                if os.path.isdir('/var/www/html/python/'):  # "python2" rpm
71114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser                    dochome = '/var/www/html/python/index.html'
72114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser                else:
73114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser                    basepath = '/usr/share/doc/'  # standard location
74114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser                    dochome = os.path.join(basepath, pyver,
75114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser                                           'Doc', 'index.html')
768aa23927e339ca13459dd408751546352d81fefaKurt B. Kaiser            elif sys.platform[:3] == 'win':
77090e636add33907d196b9899eb0f019654a055e8Kurt B. Kaiser                chmfile = os.path.join(sys.prefix, 'Doc',
78f13447f3f75c7b56eec6a602b0b72a48fbbc5486Kurt B. Kaiser                                       'Python%s.chm' % _sphinx_version())
7984ef153c74decf0b036042b5ca3f5cb3cd785eddThomas Heller                if os.path.isfile(chmfile):
8084ef153c74decf0b036042b5ca3f5cb3cd785eddThomas Heller                    dochome = chmfile
8119302d927e6688e02553df16177e4867e2d0e3b3Ronald Oussoren            elif macosxSupport.runningAsOSXApp():
8219302d927e6688e02553df16177e4867e2d0e3b3Ronald Oussoren                # documentation is stored inside the python framework
8319302d927e6688e02553df16177e4867e2d0e3b3Ronald Oussoren                dochome = os.path.join(sys.prefix,
8419302d927e6688e02553df16177e4867e2d0e3b3Ronald Oussoren                        'Resources/English.lproj/Documentation/index.html')
85114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser            dochome = os.path.normpath(dochome)
86114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser            if os.path.isfile(dochome):
87114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser                EditorWindow.help_url = dochome
8819302d927e6688e02553df16177e4867e2d0e3b3Ronald Oussoren                if sys.platform == 'darwin':
8919302d927e6688e02553df16177e4867e2d0e3b3Ronald Oussoren                    # Safari requires real file:-URLs
9019302d927e6688e02553df16177e4867e2d0e3b3Ronald Oussoren                    EditorWindow.help_url = 'file://' + EditorWindow.help_url
91114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser            else:
927b4c2beda6b54c28070a04467e22e03f90b43450Raymond Hettinger                EditorWindow.help_url = "http://docs.python.org/%d.%d" % sys.version_info[:2]
93dc72f48e262a7aaf177a37676c79ced4ddce0fffSteven M. Gava        currentTheme=idleConf.CurrentTheme()
947aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.flist = flist
957aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        root = root or flist.root
967aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.root = root
97b3c4d16e688d073dae92f272d66b6efa4b70a734Kurt B. Kaiser        try:
98b3c4d16e688d073dae92f272d66b6efa4b70a734Kurt B. Kaiser            sys.ps1
99b3c4d16e688d073dae92f272d66b6efa4b70a734Kurt B. Kaiser        except AttributeError:
100b3c4d16e688d073dae92f272d66b6efa4b70a734Kurt B. Kaiser            sys.ps1 = '>>> '
1017aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.menubar = Menu(root)
102183403a271977a26c0b77dbcf62e19395c007288Kurt B. Kaiser        self.top = top = WindowList.ListedToplevel(root, menu=self.menubar)
1030c5bc8c9518fd18d41aedede536c4a9aa8dfa619Steven M. Gava        if flist:
104610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser            self.tkinter_vars = flist.vars
105cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser            #self.top.instance_dict makes flist.inversedict avalable to
1060c5bc8c9518fd18d41aedede536c4a9aa8dfa619Steven M. Gava            #configDialog.py so it can access all EditorWindow instaces
107a2946a437ee7886e6192b9e02725b8a04cdc80aeKurt B. Kaiser            self.top.instance_dict = flist.inversedict
108610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser        else:
109610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser            self.tkinter_vars = {}  # keys: Tkinter event names
110610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser                                    # values: Tkinter variable instances
111a2946a437ee7886e6192b9e02725b8a04cdc80aeKurt B. Kaiser            self.top.instance_dict = {}
112a2946a437ee7886e6192b9e02725b8a04cdc80aeKurt B. Kaiser        self.recent_files_path = os.path.join(idleConf.GetUserCfgDir(),
1131d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava                'recent-files.lst')
1147aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.text_frame = text_frame = Frame(top)
1154ebbefe677f47a8e4f624737338f22e714a7e5bcMartin v. Löwis        self.vbar = vbar = Scrollbar(text_frame, name='vbar')
1161061e7270b13a0e632c5a28791a46e38b48a39a0Kurt B. Kaiser        self.width = idleConf.GetOption('main','EditorWindow','width')
117ce46511957740aa9963b95db80c764b6c346fb1dKurt B. Kaiser        text_options = {
118ce46511957740aa9963b95db80c764b6c346fb1dKurt B. Kaiser                'name': 'text',
119ce46511957740aa9963b95db80c764b6c346fb1dKurt B. Kaiser                'padx': 5,
120ce46511957740aa9963b95db80c764b6c346fb1dKurt B. Kaiser                'wrap': 'none',
121ce46511957740aa9963b95db80c764b6c346fb1dKurt B. Kaiser                'width': self.width,
122ce46511957740aa9963b95db80c764b6c346fb1dKurt B. Kaiser                'height': idleConf.GetOption('main', 'EditorWindow', 'height')}
123ce46511957740aa9963b95db80c764b6c346fb1dKurt B. Kaiser        if TkVersion >= 8.5:
124ce46511957740aa9963b95db80c764b6c346fb1dKurt B. Kaiser            # Starting with tk 8.5 we have to set the new tabstyle option
125ce46511957740aa9963b95db80c764b6c346fb1dKurt B. Kaiser            # to 'wordprocessor' to achieve the same display of tabs as in
126ce46511957740aa9963b95db80c764b6c346fb1dKurt B. Kaiser            # older tk versions.
127ce46511957740aa9963b95db80c764b6c346fb1dKurt B. Kaiser            text_options['tabstyle'] = 'wordprocessor'
128ce46511957740aa9963b95db80c764b6c346fb1dKurt B. Kaiser        self.text = text = MultiCallCreator(Text)(text_frame, **text_options)
129183403a271977a26c0b77dbcf62e19395c007288Kurt B. Kaiser        self.top.focused_widget = self.text
1307aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
1317aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.createmenubar()
1327aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.apply_bindings()
1337aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
1347aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.top.protocol("WM_DELETE_WINDOW", self.close)
1357aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.top.bind("<<close-window>>", self.close_event)
13617db495445ba1f6c9d360ade62471d49ceb05cfeRonald Oussoren        if macosxSupport.runningAsOSXApp():
13717db495445ba1f6c9d360ade62471d49ceb05cfeRonald Oussoren            # Command-W on editorwindows doesn't work without this.
1383075e16c516e3975b61e4356a6d64def9cc5110eRonald Oussoren            text.bind('<<close-window>>', self.close_event)
13984f4803f4f7ccf40cd777d1e9627528c30bc9053Kurt B. Kaiser        text.bind("<<cut>>", self.cut)
14084f4803f4f7ccf40cd777d1e9627528c30bc9053Kurt B. Kaiser        text.bind("<<copy>>", self.copy)
14184f4803f4f7ccf40cd777d1e9627528c30bc9053Kurt B. Kaiser        text.bind("<<paste>>", self.paste)
1427aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text.bind("<<center-insert>>", self.center_insert_event)
1437aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text.bind("<<help>>", self.help_dialog)
1447aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text.bind("<<python-docs>>", self.python_docs)
1457aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text.bind("<<about-idle>>", self.about_dialog)
1463b55a891a197212b34b0c077f72cb3af752468ecSteven M. Gava        text.bind("<<open-config-dialog>>", self.config_dialog)
1477aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text.bind("<<open-module>>", self.open_module)
1487aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text.bind("<<do-nothing>>", lambda event: "break")
1497aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text.bind("<<select-all>>", self.select_all)
1507aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text.bind("<<remove-selection>>", self.remove_selection)
151c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        text.bind("<<find>>", self.find_event)
152c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        text.bind("<<find-again>>", self.find_again_event)
153c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        text.bind("<<find-in-files>>", self.find_in_files_event)
154c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        text.bind("<<find-selection>>", self.find_selection_event)
155c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        text.bind("<<replace>>", self.replace_event)
156c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        text.bind("<<goto-line>>", self.goto_line_event)
1577aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text.bind("<3>", self.right_menu_event)
158cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.bind("<<smart-backspace>>",self.smart_backspace_event)
159cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.bind("<<newline-and-indent>>",self.newline_and_indent_event)
160cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.bind("<<smart-indent>>",self.smart_indent_event)
161cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.bind("<<indent-region>>",self.indent_region_event)
162cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.bind("<<dedent-region>>",self.dedent_region_event)
163cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.bind("<<comment-region>>",self.comment_region_event)
164cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.bind("<<uncomment-region>>",self.uncomment_region_event)
165cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.bind("<<tabify-region>>",self.tabify_region_event)
166cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.bind("<<untabify-region>>",self.untabify_region_event)
167cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.bind("<<toggle-tabs>>",self.toggle_tabs_event)
168cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.bind("<<change-indentwidth>>",self.change_indentwidth_event)
1695ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser        text.bind("<Left>", self.move_at_edge_if_selection(0))
1705ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser        text.bind("<Right>", self.move_at_edge_if_selection(1))
1713069dbb8ec9c287c2ff6b3a1c8bfde83af81c11bKurt B. Kaiser        text.bind("<<del-word-left>>", self.del_word_left)
1723069dbb8ec9c287c2ff6b3a1c8bfde83af81c11bKurt B. Kaiser        text.bind("<<del-word-right>>", self.del_word_right)
17393cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser        text.bind("<<beginning-of-line>>", self.home_callback)
1746655e4bc2752f1114a2e1f9a63ffd4191fa50d0dKurt B. Kaiser
1757aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if flist:
1767aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            flist.inversedict[self] = key
1777aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            if key:
1787aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                flist.dict[key] = self
179d2f4861a0b52a2af5ea3395267a5c56541352f8fKurt B. Kaiser            text.bind("<<open-new-window>>", self.new_callback)
1807aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            text.bind("<<close-all-windows>>", self.flist.close_all_callback)
1817aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            text.bind("<<open-class-browser>>", self.open_class_browser)
1827aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            text.bind("<<open-path-browser>>", self.open_path_browser)
1837aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
184898a365c276951ab2471afe2e24bbc910666d361Steven M. Gava        self.set_status_bar()
1857aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        vbar['command'] = text.yview
1867aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        vbar.pack(side=RIGHT, fill=Y)
1877aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text['yscrollcommand'] = vbar.set
188acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser        fontWeight = 'normal'
189acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser        if idleConf.GetOption('main', 'EditorWindow', 'font-bold', type='bool'):
190b1585417d1f7ae8984ca72b16fd5a21746949ae5Steven M. Gava            fontWeight='bold'
191acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser        text.config(font=(idleConf.GetOption('main', 'EditorWindow', 'font'),
192acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser                          idleConf.GetOption('main', 'EditorWindow', 'font-size'),
193acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser                          fontWeight))
1947aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text_frame.pack(side=LEFT, fill=BOTH, expand=1)
1957aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text.pack(side=TOP, fill=BOTH, expand=1)
1967aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text.focus_set()
1977aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
1986af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        # usetabs true  -> literal tab characters are used by indent and
1996af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        #                  dedent cmds, possibly mixed with spaces if
2006af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        #                  indentwidth is not a multiple of tabwidth,
2016af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        #                  which will cause Tabnanny to nag!
2026af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        #         false -> tab characters are converted to spaces by indent
2036af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        #                  and dedent cmds, and ditto TAB keystrokes
204acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser        # Although use-spaces=0 can be configured manually in config-main.def,
205acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser        # configuration of tabs v. spaces is not supported in the configuration
206acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser        # dialog.  IDLE promotes the preferred Python indentation: use spaces!
207acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser        usespaces = idleConf.GetOption('main', 'Indent', 'use-spaces', type='bool')
208acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser        self.usetabs = not usespaces
2096af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser
2106af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        # tabwidth is the display width of a literal tab character.
2116af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        # CAUTION:  telling Tk to use anything other than its default
2126af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        # tab setting causes it to use an entirely different tabbing algorithm,
2136af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        # treating tab stops as fixed distances from the left margin.
2146af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        # Nobody expects this, so for now tabwidth should never be changed.
215acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser        self.tabwidth = 8    # must remain 8 until Tk is fixed.
216acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser
217acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser        # indentwidth is the number of screen characters per indent level.
218acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser        # The recommended Python indentation is four spaces.
219acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser        self.indentwidth = self.tabwidth
220acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser        self.set_notabs_indentwidth()
2216af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser
2226af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        # If context_use_ps1 is true, parsing searches back for a ps1 line;
2236af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        # else searches for a popular (if, def, ...) Python stmt.
2246af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        self.context_use_ps1 = False
2256af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser
2266af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        # When searching backwards for a reliable place to begin parsing,
2276af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        # first start num_context_lines[0] lines back, then
2286af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        # num_context_lines[1] lines back if that didn't work, and so on.
2296af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        # The last value should be huge (larger than the # of lines in a
2306af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        # conceivable file).
2316af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        # Making the initial values larger slows things down more often.
2326af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        self.num_context_lines = 50, 500, 5000000
2336af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser
2347aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.per = per = self.Percolator(text)
235dc1e70987f49aa23bf1d07f32c476edeba0cec30Kurt B. Kaiser
236dc1e70987f49aa23bf1d07f32c476edeba0cec30Kurt B. Kaiser        self.undo = undo = self.UndoDelegator()
237dc1e70987f49aa23bf1d07f32c476edeba0cec30Kurt B. Kaiser        per.insertfilter(undo)
238dc1e70987f49aa23bf1d07f32c476edeba0cec30Kurt B. Kaiser        text.undo_block_start = undo.undo_block_start
239dc1e70987f49aa23bf1d07f32c476edeba0cec30Kurt B. Kaiser        text.undo_block_stop = undo.undo_block_stop
240dc1e70987f49aa23bf1d07f32c476edeba0cec30Kurt B. Kaiser        undo.set_saved_change_hook(self.saved_change_hook)
241dc1e70987f49aa23bf1d07f32c476edeba0cec30Kurt B. Kaiser
242dc1e70987f49aa23bf1d07f32c476edeba0cec30Kurt B. Kaiser        # IOBinding implements file I/O and printing functionality
2437aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.io = io = self.IOBinding(self)
244dc1e70987f49aa23bf1d07f32c476edeba0cec30Kurt B. Kaiser        io.set_filename_change_hook(self.filename_change_hook)
245dc1e70987f49aa23bf1d07f32c476edeba0cec30Kurt B. Kaiser
246cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        # Create the recent files submenu
247cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        self.recent_files_menu = Menu(self.menubar)
248cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        self.menudict['file'].insert_cascade(3, label='Recent Files',
249cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser                                             underline=0,
250cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser                                             menu=self.recent_files_menu)
251cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        self.update_recent_files_list()
2527aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
253f05fa33a6c42bc21ffdfe7a20226a2b9a83ac3c2Kurt B. Kaiser        self.color = None # initialized below in self.ResetColorizer
2547aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if filename:
255d2f4861a0b52a2af5ea3395267a5c56541352f8fKurt B. Kaiser            if os.path.exists(filename) and not os.path.isdir(filename):
2567aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                io.loadfile(filename)
2577aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            else:
2587aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                io.set_filename(filename)
259f05fa33a6c42bc21ffdfe7a20226a2b9a83ac3c2Kurt B. Kaiser        self.ResetColorizer()
2607aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.saved_change_hook()
2617aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
2626af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        self.set_indentation_params(self.ispythonsource(filename))
2636af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser
2647aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.load_extensions()
2657aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
2667aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        menu = self.menudict.get('windows')
2677aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if menu:
2687aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            end = menu.index("end")
2697aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            if end is None:
2707aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                end = -1
2717aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            if end >= 0:
2727aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                menu.add_separator()
2737aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                end = end + 1
2747aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.wmenu_end = end
2757aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            WindowList.register_callback(self.postwindowsmenu)
2767aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
2777aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        # Some abstractions so IDLE extensions are cross-IDE
2787aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.askyesno = tkMessageBox.askyesno
2797aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.askinteger = tkSimpleDialog.askinteger
2807aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.showerror = tkMessageBox.showerror
2817aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
282307021f40b227d2bf114b536c37cc41e03fc2affMartin v. Löwis    def _filename_to_unicode(self, filename):
283307021f40b227d2bf114b536c37cc41e03fc2affMartin v. Löwis        """convert filename to unicode in order to display it in Tk"""
284307021f40b227d2bf114b536c37cc41e03fc2affMartin v. Löwis        if isinstance(filename, unicode) or not filename:
285307021f40b227d2bf114b536c37cc41e03fc2affMartin v. Löwis            return filename
286307021f40b227d2bf114b536c37cc41e03fc2affMartin v. Löwis        else:
287307021f40b227d2bf114b536c37cc41e03fc2affMartin v. Löwis            try:
288307021f40b227d2bf114b536c37cc41e03fc2affMartin v. Löwis                return filename.decode(self.filesystemencoding)
289307021f40b227d2bf114b536c37cc41e03fc2affMartin v. Löwis            except UnicodeDecodeError:
290307021f40b227d2bf114b536c37cc41e03fc2affMartin v. Löwis                # XXX
291307021f40b227d2bf114b536c37cc41e03fc2affMartin v. Löwis                try:
292307021f40b227d2bf114b536c37cc41e03fc2affMartin v. Löwis                    return filename.decode(self.encoding)
293307021f40b227d2bf114b536c37cc41e03fc2affMartin v. Löwis                except UnicodeDecodeError:
294307021f40b227d2bf114b536c37cc41e03fc2affMartin v. Löwis                    # byte-to-byte conversion
295307021f40b227d2bf114b536c37cc41e03fc2affMartin v. Löwis                    return filename.decode('iso8859-1')
296307021f40b227d2bf114b536c37cc41e03fc2affMartin v. Löwis
297d2f4861a0b52a2af5ea3395267a5c56541352f8fKurt B. Kaiser    def new_callback(self, event):
298d2f4861a0b52a2af5ea3395267a5c56541352f8fKurt B. Kaiser        dirname, basename = self.io.defaultfilename()
299d2f4861a0b52a2af5ea3395267a5c56541352f8fKurt B. Kaiser        self.flist.new(dirname)
300d2f4861a0b52a2af5ea3395267a5c56541352f8fKurt B. Kaiser        return "break"
301d2f4861a0b52a2af5ea3395267a5c56541352f8fKurt B. Kaiser
30293cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser    def home_callback(self, event):
30393cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser        if (event.state & 12) != 0 and event.keysym == "Home":
30493cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser            # state&1==shift, state&4==control, state&8==alt
30593cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser            return # <Modifier-Home>; fall back to class binding
30693cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser
30793cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser        if self.text.index("iomark") and \
30893cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser           self.text.compare("iomark", "<=", "insert lineend") and \
30993cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser           self.text.compare("insert linestart", "<=", "iomark"):
31093cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser            insertpt = int(self.text.index("iomark").split(".")[1])
31193cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser        else:
31293cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser            line = self.text.get("insert linestart", "insert lineend")
31393cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser            for insertpt in xrange(len(line)):
31493cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser                if line[insertpt] not in (' ','\t'):
31593cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser                    break
31693cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser            else:
31793cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser                insertpt=len(line)
31893cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser
31993cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser        lineat = int(self.text.index("insert").split('.')[1])
32093cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser
32193cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser        if insertpt == lineat:
32293cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser            insertpt = 0
32393cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser
32493cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser        dest = "insert linestart+"+str(insertpt)+"c"
32593cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser
32693cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser        if (event.state&1) == 0:
32793cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser            # shift not pressed
32893cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser            self.text.tag_remove("sel", "1.0", "end")
32993cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser        else:
33093cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser            if not self.text.index("sel.first"):
33193cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser                self.text.mark_set("anchor","insert")
33293cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser
33393cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser            first = self.text.index(dest)
33493cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser            last = self.text.index("anchor")
33593cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser
33693cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser            if self.text.compare(first,">",last):
33793cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser                first,last = last,first
33893cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser
33993cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser            self.text.tag_remove("sel", "1.0", "end")
34093cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser            self.text.tag_add("sel", first, last)
34193cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser
34293cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser        self.text.mark_set("insert", dest)
34393cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser        self.text.see("insert")
34493cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser        return "break"
34593cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser
3467aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def set_status_bar(self):
347898a365c276951ab2471afe2e24bbc910666d361Steven M. Gava        self.status_bar = self.MultiStatusBar(self.top)
34819302d927e6688e02553df16177e4867e2d0e3b3Ronald Oussoren        if macosxSupport.runningAsOSXApp():
34919302d927e6688e02553df16177e4867e2d0e3b3Ronald Oussoren            # Insert some padding to avoid obscuring some of the statusbar
35019302d927e6688e02553df16177e4867e2d0e3b3Ronald Oussoren            # by the resize widget.
35119302d927e6688e02553df16177e4867e2d0e3b3Ronald Oussoren            self.status_bar.set_label('_padding1', '    ', side=RIGHT)
3527aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.status_bar.set_label('column', 'Col: ?', side=RIGHT)
3537aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.status_bar.set_label('line', 'Ln: ?', side=RIGHT)
3547aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.status_bar.pack(side=BOTTOM, fill=X)
355b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser        self.text.bind("<<set-line-and-column>>", self.set_line_and_column)
356b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser        self.text.event_add("<<set-line-and-column>>",
357b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser                            "<KeyRelease>", "<ButtonRelease>")
3587aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.text.after_idle(self.set_line_and_column)
3597aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
3607aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def set_line_and_column(self, event=None):
361220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser        line, column = self.text.index(INSERT).split('.')
3627aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.status_bar.set_label('column', 'Col: %s' % column)
3637aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.status_bar.set_label('line', 'Ln: %s' % line)
3647aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
3657aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    menu_specs = [
3667aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        ("file", "_File"),
3677aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        ("edit", "_Edit"),
3687aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        ("format", "F_ormat"),
3697aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        ("run", "_Run"),
3701061e7270b13a0e632c5a28791a46e38b48a39a0Kurt B. Kaiser        ("options", "_Options"),
3717aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        ("windows", "_Windows"),
3727aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        ("help", "_Help"),
3737aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    ]
3747aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
37519302d927e6688e02553df16177e4867e2d0e3b3Ronald Oussoren    if macosxSupport.runningAsOSXApp():
37619302d927e6688e02553df16177e4867e2d0e3b3Ronald Oussoren        del menu_specs[-3]
37719302d927e6688e02553df16177e4867e2d0e3b3Ronald Oussoren        menu_specs[-2] = ("windows", "_Window")
37819302d927e6688e02553df16177e4867e2d0e3b3Ronald Oussoren
37919302d927e6688e02553df16177e4867e2d0e3b3Ronald Oussoren
3807aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def createmenubar(self):
3817aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        mbar = self.menubar
3827aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.menudict = menudict = {}
3837aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        for name, label in self.menu_specs:
3847aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            underline, label = prepstr(label)
3857aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            menudict[name] = menu = Menu(mbar, name=name)
3867aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            mbar.add_cascade(label=label, menu=menu, underline=underline)
38719302d927e6688e02553df16177e4867e2d0e3b3Ronald Oussoren
388a97063a1086266e774586765b3c3e65ed2a4a1ebRonald Oussoren        if macosxSupport.runningAsOSXApp():
38919302d927e6688e02553df16177e4867e2d0e3b3Ronald Oussoren            # Insert the application menu
39019302d927e6688e02553df16177e4867e2d0e3b3Ronald Oussoren            menudict['application'] = menu = Menu(mbar, name='apple')
39119302d927e6688e02553df16177e4867e2d0e3b3Ronald Oussoren            mbar.add_cascade(label='IDLE', menu=menu)
39219302d927e6688e02553df16177e4867e2d0e3b3Ronald Oussoren
3937aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.fill_menus()
3948e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser        self.base_helpmenu_length = self.menudict['help'].index(END)
3958e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser        self.reset_help_menu_entries()
3967aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
3977aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def postwindowsmenu(self):
3987aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        # Only called when Windows menu exists
3997aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        menu = self.menudict['windows']
4007aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        end = menu.index("end")
4017aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if end is None:
4027aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            end = -1
4037aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if end > self.wmenu_end:
4047aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            menu.delete(self.wmenu_end+1, end)
4057aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        WindowList.add_windows_to_menu(menu)
4067aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
4077aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    rmenu = None
4087aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
4097aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def right_menu_event(self, event):
4107aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.text.tag_remove("sel", "1.0", "end")
4117aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.text.mark_set("insert", "@%d,%d" % (event.x, event.y))
4127aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if not self.rmenu:
4137aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.make_rmenu()
4147aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        rmenu = self.rmenu
4157aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.event = event
4167aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        iswin = sys.platform[:3] == 'win'
4177aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if iswin:
4187aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.text.config(cursor="arrow")
4197aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        rmenu.tk_popup(event.x_root, event.y_root)
4207aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if iswin:
4217aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.text.config(cursor="ibeam")
4227aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
4237aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    rmenu_specs = [
4247aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        # ("Label", "<<virtual-event>>"), ...
4257aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        ("Close", "<<close-window>>"), # Example
4267aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    ]
4277aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
4287aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def make_rmenu(self):
4297aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        rmenu = Menu(self.text, tearoff=0)
4307aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        for label, eventname in self.rmenu_specs:
4317aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            def command(text=self.text, eventname=eventname):
4327aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                text.event_generate(eventname)
4337aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            rmenu.add_command(label=label, command=command)
4347aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.rmenu = rmenu
4357aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
4367aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def about_dialog(self, event=None):
437d78b23025c9c9f6288e14112411d83f44bcf85a8Kurt B. Kaiser        aboutDialog.AboutDialog(self.top,'About IDLE')
4386655e4bc2752f1114a2e1f9a63ffd4191fa50d0dKurt B. Kaiser
4393b55a891a197212b34b0c077f72cb3af752468ecSteven M. Gava    def config_dialog(self, event=None):
4403b55a891a197212b34b0c077f72cb3af752468ecSteven M. Gava        configDialog.ConfigDialog(self.top,'Settings')
4416655e4bc2752f1114a2e1f9a63ffd4191fa50d0dKurt B. Kaiser
4427aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def help_dialog(self, event=None):
443b9d07b5a8b8b70268ffa520895e7843ab594a486Steven M. Gava        fn=os.path.join(os.path.abspath(os.path.dirname(__file__)),'help.txt')
444d5f4910afdd95d86f51ed53a932a4c2fd181bd43Kurt B. Kaiser        textView.view_file(self.top,'Help',fn)
4456655e4bc2752f1114a2e1f9a63ffd4191fa50d0dKurt B. Kaiser
446114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser    def python_docs(self, event=None):
4478aa23927e339ca13459dd408751546352d81fefaKurt B. Kaiser        if sys.platform[:3] == 'win':
448931625dc77cfd527b059e579615d517d8d994110Steven M. Gava            os.startfile(self.help_url)
449114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser        else:
450114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser            webbrowser.open(self.help_url)
4518aa23927e339ca13459dd408751546352d81fefaKurt B. Kaiser        return "break"
4527aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
45384f4803f4f7ccf40cd777d1e9627528c30bc9053Kurt B. Kaiser    def cut(self,event):
45484f4803f4f7ccf40cd777d1e9627528c30bc9053Kurt B. Kaiser        self.text.event_generate("<<Cut>>")
45584f4803f4f7ccf40cd777d1e9627528c30bc9053Kurt B. Kaiser        return "break"
45684f4803f4f7ccf40cd777d1e9627528c30bc9053Kurt B. Kaiser
45784f4803f4f7ccf40cd777d1e9627528c30bc9053Kurt B. Kaiser    def copy(self,event):
458b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser        if not self.text.tag_ranges("sel"):
459b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser            # There is no selection, so do nothing and maybe interrupt.
460b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser            return
46184f4803f4f7ccf40cd777d1e9627528c30bc9053Kurt B. Kaiser        self.text.event_generate("<<Copy>>")
46284f4803f4f7ccf40cd777d1e9627528c30bc9053Kurt B. Kaiser        return "break"
46384f4803f4f7ccf40cd777d1e9627528c30bc9053Kurt B. Kaiser
46484f4803f4f7ccf40cd777d1e9627528c30bc9053Kurt B. Kaiser    def paste(self,event):
46584f4803f4f7ccf40cd777d1e9627528c30bc9053Kurt B. Kaiser        self.text.event_generate("<<Paste>>")
466631fee62351397e940e4616ef48f03788962c3ebKurt B. Kaiser        self.text.see("insert")
46784f4803f4f7ccf40cd777d1e9627528c30bc9053Kurt B. Kaiser        return "break"
46884f4803f4f7ccf40cd777d1e9627528c30bc9053Kurt B. Kaiser
4697aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def select_all(self, event=None):
4707aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.text.tag_add("sel", "1.0", "end-1c")
4717aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.text.mark_set("insert", "1.0")
4727aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.text.see("insert")
4737aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        return "break"
4747aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
4757aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def remove_selection(self, event=None):
4767aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.text.tag_remove("sel", "1.0", "end")
4777aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.text.see("insert")
4787aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
4795ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser    def move_at_edge_if_selection(self, edge_index):
4805ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser        """Cursor move begins at start or end of selection
4815ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser
4825ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser        When a left/right cursor key is pressed create and return to Tkinter a
4835ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser        function which causes a cursor move from the associated edge of the
4845ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser        selection.
4855ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser
4865ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser        """
4875ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser        self_text_index = self.text.index
4885ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser        self_text_mark_set = self.text.mark_set
4895ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser        edges_table = ("sel.first+1c", "sel.last-1c")
4905ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser        def move_at_edge(event):
4915ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser            if (event.state & 5) == 0: # no shift(==1) or control(==4) pressed
4925ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser                try:
4935ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser                    self_text_index("sel.first")
4945ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser                    self_text_mark_set("insert", edges_table[edge_index])
4955ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser                except TclError:
4965ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser                    pass
4975ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser        return move_at_edge
4985ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser
4993069dbb8ec9c287c2ff6b3a1c8bfde83af81c11bKurt B. Kaiser    def del_word_left(self, event):
5003069dbb8ec9c287c2ff6b3a1c8bfde83af81c11bKurt B. Kaiser        self.text.event_generate('<Meta-Delete>')
5013069dbb8ec9c287c2ff6b3a1c8bfde83af81c11bKurt B. Kaiser        return "break"
5023069dbb8ec9c287c2ff6b3a1c8bfde83af81c11bKurt B. Kaiser
5033069dbb8ec9c287c2ff6b3a1c8bfde83af81c11bKurt B. Kaiser    def del_word_right(self, event):
5043069dbb8ec9c287c2ff6b3a1c8bfde83af81c11bKurt B. Kaiser        self.text.event_generate('<Meta-d>')
5053069dbb8ec9c287c2ff6b3a1c8bfde83af81c11bKurt B. Kaiser        return "break"
5063069dbb8ec9c287c2ff6b3a1c8bfde83af81c11bKurt B. Kaiser
507c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava    def find_event(self, event):
508c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        SearchDialog.find(self.text)
509c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        return "break"
510c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava
511c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava    def find_again_event(self, event):
512c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        SearchDialog.find_again(self.text)
513c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        return "break"
514c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava
515c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava    def find_selection_event(self, event):
516c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        SearchDialog.find_selection(self.text)
517c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        return "break"
518c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava
519c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava    def find_in_files_event(self, event):
520c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        GrepDialog.grep(self.text, self.io, self.flist)
521c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        return "break"
522c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava
523c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava    def replace_event(self, event):
524c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        ReplaceDialog.replace(self.text)
525c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        return "break"
526c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava
527c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava    def goto_line_event(self, event):
528c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        text = self.text
529c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        lineno = tkSimpleDialog.askinteger("Goto",
530c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava                "Go to line number:",parent=text)
531c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        if lineno is None:
532c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava            return "break"
533c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        if lineno <= 0:
534c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava            text.bell()
535c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava            return "break"
536c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        text.mark_set("insert", "%d.0" % lineno)
537c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        text.see("insert")
538c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava
5397aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def open_module(self, event=None):
5407aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        # XXX Shouldn't this be in IOBinding or in FileList?
5417aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        try:
5427aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            name = self.text.get("sel.first", "sel.last")
5437aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        except TclError:
5447aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            name = ""
5457aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        else:
546220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser            name = name.strip()
547852f35bbeb23d898fb0d16489effb1197ee3fe02Guido van Rossum        name = tkSimpleDialog.askstring("Module",
548852f35bbeb23d898fb0d16489effb1197ee3fe02Guido van Rossum                 "Enter the name of a Python module\n"
549852f35bbeb23d898fb0d16489effb1197ee3fe02Guido van Rossum                 "to search on sys.path and open:",
550852f35bbeb23d898fb0d16489effb1197ee3fe02Guido van Rossum                 parent=self.text, initialvalue=name)
551852f35bbeb23d898fb0d16489effb1197ee3fe02Guido van Rossum        if name:
552852f35bbeb23d898fb0d16489effb1197ee3fe02Guido van Rossum            name = name.strip()
5537aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if not name:
554852f35bbeb23d898fb0d16489effb1197ee3fe02Guido van Rossum            return
5557aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        # XXX Ought to insert current file's directory in front of path
5567aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        try:
557220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser            (f, file, (suffix, mode, type)) = _find_module(name)
5587aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        except (NameError, ImportError), msg:
5597aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            tkMessageBox.showerror("Import error", str(msg), parent=self.text)
5607aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return
5617aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if type != imp.PY_SOURCE:
5627aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            tkMessageBox.showerror("Unsupported type",
5637aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                "%s is not a source module" % name, parent=self.text)
5647aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return
5657aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if f:
5667aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            f.close()
5677aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if self.flist:
5687aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.flist.open(file)
5697aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        else:
5707aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.io.loadfile(file)
5717aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
5727aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def open_class_browser(self, event=None):
5737aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        filename = self.io.filename
5747aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if not filename:
5757aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            tkMessageBox.showerror(
5767aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                "No filename",
5777aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                "This buffer has no associated filename",
5787aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                master=self.text)
5797aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.text.focus_set()
5807aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return None
5817aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        head, tail = os.path.split(filename)
5827aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        base, ext = os.path.splitext(tail)
5837aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        import ClassBrowser
5847aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        ClassBrowser.ClassBrowser(self.flist, base, [head])
5857aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
5867aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def open_path_browser(self, event=None):
5877aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        import PathBrowser
5887aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        PathBrowser.PathBrowser(self.flist)
5897aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
5907aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def gotoline(self, lineno):
5917aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if lineno is not None and lineno > 0:
5927aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.text.mark_set("insert", "%d.0" % lineno)
5937aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.text.tag_remove("sel", "1.0", "end")
5947aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.text.tag_add("sel", "insert", "insert +1l")
5957aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.center()
5967aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
5977aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def ispythonsource(self, filename):
598df506ea98b1c9424634c6063e90a664ac9127164Kurt B. Kaiser        if not filename or os.path.isdir(filename):
599220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser            return True
6007aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        base, ext = os.path.splitext(os.path.basename(filename))
6017aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if os.path.normcase(ext) in (".py", ".pyw"):
602220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser            return True
6037aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        try:
6047aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            f = open(filename)
6057aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            line = f.readline()
6067aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            f.close()
6077aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        except IOError:
608220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser            return False
60983a3560527f9e62bce6adbc12b8ff6b57be8b1acKurt B. Kaiser        return line.startswith('#!') and line.find('python') >= 0
6107aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
6117aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def close_hook(self):
6127aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if self.flist:
6130b634efcbcd10d9364796e9b048f18cd4a08b88bKurt B. Kaiser            self.flist.unregister_maybe_terminate(self)
6140b634efcbcd10d9364796e9b048f18cd4a08b88bKurt B. Kaiser            self.flist = None
6157aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
6167aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def set_close_hook(self, close_hook):
6177aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.close_hook = close_hook
6187aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
6197aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def filename_change_hook(self):
6207aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if self.flist:
6217aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.flist.filename_changed_edit(self)
6227aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.saved_change_hook()
623260cb9034c861fa159f26fba8679ac265af47109Kurt B. Kaiser        self.top.update_windowlist_registry(self)
624f05fa33a6c42bc21ffdfe7a20226a2b9a83ac3c2Kurt B. Kaiser        self.ResetColorizer()
6257aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
626f05fa33a6c42bc21ffdfe7a20226a2b9a83ac3c2Kurt B. Kaiser    def _addcolorizer(self):
6277aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if self.color:
6287aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return
629f05fa33a6c42bc21ffdfe7a20226a2b9a83ac3c2Kurt B. Kaiser        if self.ispythonsource(self.io.filename):
630f05fa33a6c42bc21ffdfe7a20226a2b9a83ac3c2Kurt B. Kaiser            self.color = self.ColorDelegator()
631f05fa33a6c42bc21ffdfe7a20226a2b9a83ac3c2Kurt B. Kaiser        # can add more colorizers here...
632f05fa33a6c42bc21ffdfe7a20226a2b9a83ac3c2Kurt B. Kaiser        if self.color:
633f05fa33a6c42bc21ffdfe7a20226a2b9a83ac3c2Kurt B. Kaiser            self.per.removefilter(self.undo)
634f05fa33a6c42bc21ffdfe7a20226a2b9a83ac3c2Kurt B. Kaiser            self.per.insertfilter(self.color)
635f05fa33a6c42bc21ffdfe7a20226a2b9a83ac3c2Kurt B. Kaiser            self.per.insertfilter(self.undo)
6367aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
637f05fa33a6c42bc21ffdfe7a20226a2b9a83ac3c2Kurt B. Kaiser    def _rmcolorizer(self):
6387aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if not self.color:
6397aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return
640df506ea98b1c9424634c6063e90a664ac9127164Kurt B. Kaiser        self.color.removecolors()
6417aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.per.removefilter(self.color)
6427aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.color = None
6436655e4bc2752f1114a2e1f9a63ffd4191fa50d0dKurt B. Kaiser
644b77d343bc846c2049a4cffb1dfd65eb49d1728b4Steven M. Gava    def ResetColorizer(self):
645f05fa33a6c42bc21ffdfe7a20226a2b9a83ac3c2Kurt B. Kaiser        "Update the colour theme"
646f05fa33a6c42bc21ffdfe7a20226a2b9a83ac3c2Kurt B. Kaiser        # Called from self.filename_change_hook and from configDialog.py
647f05fa33a6c42bc21ffdfe7a20226a2b9a83ac3c2Kurt B. Kaiser        self._rmcolorizer()
648f05fa33a6c42bc21ffdfe7a20226a2b9a83ac3c2Kurt B. Kaiser        self._addcolorizer()
64973360a3e61274ffcc4c9fc3d09746bd6603e92a5Kurt B. Kaiser        theme = idleConf.GetOption('main','Theme','name')
650f05fa33a6c42bc21ffdfe7a20226a2b9a83ac3c2Kurt B. Kaiser        normal_colors = idleConf.GetHighlight(theme, 'normal')
651f05fa33a6c42bc21ffdfe7a20226a2b9a83ac3c2Kurt B. Kaiser        cursor_color = idleConf.GetHighlight(theme, 'cursor', fgBg='fg')
652f05fa33a6c42bc21ffdfe7a20226a2b9a83ac3c2Kurt B. Kaiser        select_colors = idleConf.GetHighlight(theme, 'hilite')
653f05fa33a6c42bc21ffdfe7a20226a2b9a83ac3c2Kurt B. Kaiser        self.text.config(
654f05fa33a6c42bc21ffdfe7a20226a2b9a83ac3c2Kurt B. Kaiser            foreground=normal_colors['foreground'],
655f05fa33a6c42bc21ffdfe7a20226a2b9a83ac3c2Kurt B. Kaiser            background=normal_colors['background'],
656f05fa33a6c42bc21ffdfe7a20226a2b9a83ac3c2Kurt B. Kaiser            insertbackground=cursor_color,
657f05fa33a6c42bc21ffdfe7a20226a2b9a83ac3c2Kurt B. Kaiser            selectforeground=select_colors['foreground'],
658f05fa33a6c42bc21ffdfe7a20226a2b9a83ac3c2Kurt B. Kaiser            selectbackground=select_colors['background'],
659f05fa33a6c42bc21ffdfe7a20226a2b9a83ac3c2Kurt B. Kaiser            )
6607aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
661b1585417d1f7ae8984ca72b16fd5a21746949ae5Steven M. Gava    def ResetFont(self):
6626655e4bc2752f1114a2e1f9a63ffd4191fa50d0dKurt B. Kaiser        "Update the text widgets' font if it is changed"
66383118c6cb36cf9a424bec1b9a2ef8c8760bae8f5Kurt B. Kaiser        # Called from configDialog.py
664b1585417d1f7ae8984ca72b16fd5a21746949ae5Steven M. Gava        fontWeight='normal'
665b1585417d1f7ae8984ca72b16fd5a21746949ae5Steven M. Gava        if idleConf.GetOption('main','EditorWindow','font-bold',type='bool'):
666b1585417d1f7ae8984ca72b16fd5a21746949ae5Steven M. Gava            fontWeight='bold'
667b1585417d1f7ae8984ca72b16fd5a21746949ae5Steven M. Gava        self.text.config(font=(idleConf.GetOption('main','EditorWindow','font'),
668b1585417d1f7ae8984ca72b16fd5a21746949ae5Steven M. Gava                idleConf.GetOption('main','EditorWindow','font-size'),
669b1585417d1f7ae8984ca72b16fd5a21746949ae5Steven M. Gava                fontWeight))
670b1585417d1f7ae8984ca72b16fd5a21746949ae5Steven M. Gava
671b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser    def RemoveKeybindings(self):
672b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser        "Remove the keybindings before they are changed."
67383118c6cb36cf9a424bec1b9a2ef8c8760bae8f5Kurt B. Kaiser        # Called from configDialog.py
6745a67f9b815fdd34617395c6ec5d9a679361b76eaKurt B. Kaiser        self.Bindings.default_keydefs = keydefs = idleConf.GetCurrentKeySet()
675dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava        for event, keylist in keydefs.items():
676b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser            self.text.event_delete(event, *keylist)
677b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser        for extensionName in self.get_standard_extension_names():
6785a67f9b815fdd34617395c6ec5d9a679361b76eaKurt B. Kaiser            xkeydefs = idleConf.GetExtensionBindings(extensionName)
6795a67f9b815fdd34617395c6ec5d9a679361b76eaKurt B. Kaiser            if xkeydefs:
6805a67f9b815fdd34617395c6ec5d9a679361b76eaKurt B. Kaiser                for event, keylist in xkeydefs.items():
681b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser                    self.text.event_delete(event, *keylist)
682b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser
683b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser    def ApplyKeybindings(self):
684b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser        "Update the keybindings after they are changed"
685b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser        # Called from configDialog.py
6865a67f9b815fdd34617395c6ec5d9a679361b76eaKurt B. Kaiser        self.Bindings.default_keydefs = keydefs = idleConf.GetCurrentKeySet()
687dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava        self.apply_bindings()
688b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser        for extensionName in self.get_standard_extension_names():
6895a67f9b815fdd34617395c6ec5d9a679361b76eaKurt B. Kaiser            xkeydefs = idleConf.GetExtensionBindings(extensionName)
6905a67f9b815fdd34617395c6ec5d9a679361b76eaKurt B. Kaiser            if xkeydefs:
6915a67f9b815fdd34617395c6ec5d9a679361b76eaKurt B. Kaiser                self.apply_bindings(xkeydefs)
692dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava        #update menu accelerators
6935a67f9b815fdd34617395c6ec5d9a679361b76eaKurt B. Kaiser        menuEventDict = {}
694dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava        for menu in self.Bindings.menudefs:
6955a67f9b815fdd34617395c6ec5d9a679361b76eaKurt B. Kaiser            menuEventDict[menu[0]] = {}
696dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava            for item in menu[1]:
697dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava                if item:
6985a67f9b815fdd34617395c6ec5d9a679361b76eaKurt B. Kaiser                    menuEventDict[menu[0]][prepstr(item[0])[1]] = item[1]
699dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava        for menubarItem in self.menudict.keys():
7005a67f9b815fdd34617395c6ec5d9a679361b76eaKurt B. Kaiser            menu = self.menudict[menubarItem]
7015a67f9b815fdd34617395c6ec5d9a679361b76eaKurt B. Kaiser            end = menu.index(END) + 1
7025a67f9b815fdd34617395c6ec5d9a679361b76eaKurt B. Kaiser            for index in range(0, end):
7035a67f9b815fdd34617395c6ec5d9a679361b76eaKurt B. Kaiser                if menu.type(index) == 'command':
7045a67f9b815fdd34617395c6ec5d9a679361b76eaKurt B. Kaiser                    accel = menu.entrycget(index, 'accelerator')
705dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava                    if accel:
7065a67f9b815fdd34617395c6ec5d9a679361b76eaKurt B. Kaiser                        itemName = menu.entrycget(index, 'label')
7075a67f9b815fdd34617395c6ec5d9a679361b76eaKurt B. Kaiser                        event = ''
7086e3dbbdf39f3b4eb6f18c0165e446df17218b7dcBenjamin Peterson                        if menubarItem in menuEventDict:
7096e3dbbdf39f3b4eb6f18c0165e446df17218b7dcBenjamin Peterson                            if itemName in menuEventDict[menubarItem]:
7105a67f9b815fdd34617395c6ec5d9a679361b76eaKurt B. Kaiser                                event = menuEventDict[menubarItem][itemName]
711dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava                        if event:
7125a67f9b815fdd34617395c6ec5d9a679361b76eaKurt B. Kaiser                            accel = get_accelerator(keydefs, event)
7135a67f9b815fdd34617395c6ec5d9a679361b76eaKurt B. Kaiser                            menu.entryconfig(index, accelerator=accel)
714dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava
715acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser    def set_notabs_indentwidth(self):
716acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser        "Update the indentwidth if changed and not using tabs in this window"
717acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser        # Called from configDialog.py
718acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser        if not self.usetabs:
719acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser            self.indentwidth = idleConf.GetOption('main', 'Indent','num-spaces',
720acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser                                                  type='int')
721acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser
7228e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser    def reset_help_menu_entries(self):
7238e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser        "Update the additional help entries on the Help menu"
7248e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser        help_list = idleConf.GetAllExtraHelpSourcesList()
7258e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser        helpmenu = self.menudict['help']
7268e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser        # first delete the extra help entries, if any
7278e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser        helpmenu_length = helpmenu.index(END)
7288e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser        if helpmenu_length > self.base_helpmenu_length:
7298e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser            helpmenu.delete((self.base_helpmenu_length + 1), helpmenu_length)
7308e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser        # then rebuild them
7318e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser        if help_list:
7328e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser            helpmenu.add_separator()
7338e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser            for entry in help_list:
7348e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser                cmd = self.__extra_help_callback(entry[1])
7358e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser                helpmenu.add_command(label=entry[0], command=cmd)
7368e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser        # and update the menu dictionary
7378e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser        self.menudict['help'] = helpmenu
7388e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser
7398e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser    def __extra_help_callback(self, helpfile):
7408e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser        "Create a callback with the helpfile value frozen at definition time"
7418e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser        def display_extra_help(helpfile=helpfile):
742b2afe855e5d75a570707d6bf0e32206e4b7b1c4dGeorg Brandl            if not helpfile.startswith(('www', 'http')):
7438aa23927e339ca13459dd408751546352d81fefaKurt B. Kaiser                url = os.path.normpath(helpfile)
7448aa23927e339ca13459dd408751546352d81fefaKurt B. Kaiser            if sys.platform[:3] == 'win':
7458aa23927e339ca13459dd408751546352d81fefaKurt B. Kaiser                os.startfile(helpfile)
7468aa23927e339ca13459dd408751546352d81fefaKurt B. Kaiser            else:
7478aa23927e339ca13459dd408751546352d81fefaKurt B. Kaiser                webbrowser.open(helpfile)
7488e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser        return display_extra_help
7496655e4bc2752f1114a2e1f9a63ffd4191fa50d0dKurt B. Kaiser
750cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser    def update_recent_files_list(self, new_file=None):
751cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        "Load and update the recent files list and menus"
752cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        rf_list = []
753cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        if os.path.exists(self.recent_files_path):
754cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser            rf_list_file = open(self.recent_files_path,'r')
7551d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava            try:
756cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser                rf_list = rf_list_file.readlines()
7571d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava            finally:
758cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser                rf_list_file.close()
759cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        if new_file:
760cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser            new_file = os.path.abspath(new_file) + '\n'
761cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser            if new_file in rf_list:
762cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser                rf_list.remove(new_file)  # move to top
763cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser            rf_list.insert(0, new_file)
764cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        # clean and save the recent files list
765cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        bad_paths = []
766cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        for path in rf_list:
767cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser            if '\0' in path or not os.path.exists(path[0:-1]):
768cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser                bad_paths.append(path)
769cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        rf_list = [path for path in rf_list if path not in bad_paths]
770cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        ulchars = "1234567890ABCDEFGHIJK"
771cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        rf_list = rf_list[0:len(ulchars)]
772cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        rf_file = open(self.recent_files_path, 'w')
7731d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava        try:
774cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser            rf_file.writelines(rf_list)
7751d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava        finally:
776cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser            rf_file.close()
777cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        # for each edit window instance, construct the recent files menu
778cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        for instance in self.top.instance_dict.keys():
779cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser            menu = instance.recent_files_menu
780cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser            menu.delete(1, END)  # clear, and rebuild:
78186b882f3a6978cf359dfedcf1193bd930f024780Guilherme Polo            for i, file_name in enumerate(rf_list):
78286b882f3a6978cf359dfedcf1193bd930f024780Guilherme Polo                file_name = file_name.rstrip()  # zap \n
783307021f40b227d2bf114b536c37cc41e03fc2affMartin v. Löwis                # make unicode string to display non-ASCII chars correctly
784307021f40b227d2bf114b536c37cc41e03fc2affMartin v. Löwis                ufile_name = self._filename_to_unicode(file_name)
785cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser                callback = instance.__recent_file_callback(file_name)
786307021f40b227d2bf114b536c37cc41e03fc2affMartin v. Löwis                menu.add_command(label=ulchars[i] + " " + ufile_name,
787cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser                                 command=callback,
788cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser                                 underline=0)
789cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser
790cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser    def __recent_file_callback(self, file_name):
791cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        def open_recent_file(fn_closure=file_name):
792cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser            self.io.open(editFile=fn_closure)
793cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        return open_recent_file
7946655e4bc2752f1114a2e1f9a63ffd4191fa50d0dKurt B. Kaiser
7957aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def saved_change_hook(self):
7967aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        short = self.short_title()
7977aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        long = self.long_title()
7987aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if short and long:
7997aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            title = short + " - " + long
8007aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        elif short:
8017aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            title = short
8027aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        elif long:
8037aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            title = long
8047aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        else:
8057aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            title = "Untitled"
8067aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        icon = short or long or title
8077aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if not self.get_saved():
8087aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            title = "*%s*" % title
8097aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            icon = "*%s" % icon
8107aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.top.wm_title(title)
8117aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.top.wm_iconname(icon)
8127aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
8137aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def get_saved(self):
8147aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        return self.undo.get_saved()
8157aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
8167aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def set_saved(self, flag):
8177aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.undo.set_saved(flag)
8187aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
8197aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def reset_undo(self):
8207aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.undo.reset_undo()
8217aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
8227aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def short_title(self):
8237aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        filename = self.io.filename
8247aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if filename:
8257aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            filename = os.path.basename(filename)
826307021f40b227d2bf114b536c37cc41e03fc2affMartin v. Löwis        # return unicode string to display non-ASCII chars correctly
827307021f40b227d2bf114b536c37cc41e03fc2affMartin v. Löwis        return self._filename_to_unicode(filename)
8287aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
8297aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def long_title(self):
830307021f40b227d2bf114b536c37cc41e03fc2affMartin v. Löwis        # return unicode string to display non-ASCII chars correctly
831307021f40b227d2bf114b536c37cc41e03fc2affMartin v. Löwis        return self._filename_to_unicode(self.io.filename or "")
8327aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
8337aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def center_insert_event(self, event):
8347aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.center()
8357aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
8367aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def center(self, mark="insert"):
8377aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text = self.text
8387aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        top, bot = self.getwindowlines()
8397aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        lineno = self.getlineno(mark)
8407aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        height = bot - top
841220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser        newtop = max(1, lineno - height//2)
8427aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text.yview(float(newtop))
8437aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
8447aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def getwindowlines(self):
8457aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text = self.text
8467aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        top = self.getlineno("@0,0")
8477aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        bot = self.getlineno("@0,65535")
8487aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if top == bot and text.winfo_height() == 1:
8497aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            # Geometry manager hasn't run yet
8507aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            height = int(text['height'])
8517aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            bot = top + height - 1
8527aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        return top, bot
8537aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
8547aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def getlineno(self, mark="insert"):
8557aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text = self.text
8567aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        return int(float(text.index(mark)))
8577aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
8581061e7270b13a0e632c5a28791a46e38b48a39a0Kurt B. Kaiser    def get_geometry(self):
8591061e7270b13a0e632c5a28791a46e38b48a39a0Kurt B. Kaiser        "Return (width, height, x, y)"
8601061e7270b13a0e632c5a28791a46e38b48a39a0Kurt B. Kaiser        geom = self.top.wm_geometry()
8611061e7270b13a0e632c5a28791a46e38b48a39a0Kurt B. Kaiser        m = re.match(r"(\d+)x(\d+)\+(-?\d+)\+(-?\d+)", geom)
8621061e7270b13a0e632c5a28791a46e38b48a39a0Kurt B. Kaiser        tuple = (map(int, m.groups()))
8631061e7270b13a0e632c5a28791a46e38b48a39a0Kurt B. Kaiser        return tuple
8641061e7270b13a0e632c5a28791a46e38b48a39a0Kurt B. Kaiser
8657aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def close_event(self, event):
8667aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.close()
8677aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
8687aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def maybesave(self):
8697aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if self.io:
87067716b5f53715e57d147cde9539b8d76a5a56e11Steven M. Gava            if not self.get_saved():
8716655e4bc2752f1114a2e1f9a63ffd4191fa50d0dKurt B. Kaiser                if self.top.state()!='normal':
87267716b5f53715e57d147cde9539b8d76a5a56e11Steven M. Gava                    self.top.deiconify()
87367716b5f53715e57d147cde9539b8d76a5a56e11Steven M. Gava                self.top.lower()
87467716b5f53715e57d147cde9539b8d76a5a56e11Steven M. Gava                self.top.lift()
8757aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return self.io.maybesave()
8767aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
8777aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def close(self):
8787aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        reply = self.maybesave()
879a398e2d0592464b6594bac0e4ee3ef091cce5159Matthias Klose        if str(reply) != "cancel":
8807aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self._close()
8817aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        return reply
8827aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
8837aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def _close(self):
8841d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava        if self.io.filename:
885cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser            self.update_recent_files_list(new_file=self.io.filename)
8867aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        WindowList.unregister_callback(self.postwindowsmenu)
8877aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.unload_extensions()
8880b634efcbcd10d9364796e9b048f18cd4a08b88bKurt B. Kaiser        self.io.close()
8890b634efcbcd10d9364796e9b048f18cd4a08b88bKurt B. Kaiser        self.io = None
8900b634efcbcd10d9364796e9b048f18cd4a08b88bKurt B. Kaiser        self.undo = None
8917aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if self.color:
8920b634efcbcd10d9364796e9b048f18cd4a08b88bKurt B. Kaiser            self.color.close(False)
8930b634efcbcd10d9364796e9b048f18cd4a08b88bKurt B. Kaiser            self.color = None
8947aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.text = None
895610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser        self.tkinter_vars = None
8960b634efcbcd10d9364796e9b048f18cd4a08b88bKurt B. Kaiser        self.per.close()
8970b634efcbcd10d9364796e9b048f18cd4a08b88bKurt B. Kaiser        self.per = None
8980b634efcbcd10d9364796e9b048f18cd4a08b88bKurt B. Kaiser        self.top.destroy()
8990b634efcbcd10d9364796e9b048f18cd4a08b88bKurt B. Kaiser        if self.close_hook:
9000b634efcbcd10d9364796e9b048f18cd4a08b88bKurt B. Kaiser            # unless override: unregister from flist, terminate if last window
9010b634efcbcd10d9364796e9b048f18cd4a08b88bKurt B. Kaiser            self.close_hook()
9027aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
9037aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def load_extensions(self):
9047aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.extensions = {}
9057aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.load_standard_extensions()
9067aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
9077aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def unload_extensions(self):
9087aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        for ins in self.extensions.values():
9097aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            if hasattr(ins, "close"):
9107aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                ins.close()
9117aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.extensions = {}
9127aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
9137aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def load_standard_extensions(self):
9147aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        for name in self.get_standard_extension_names():
9157aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            try:
9167aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                self.load_extension(name)
9177aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            except:
91870a6b49821a3226f55e9716f32d802d06640cb89Walter Dörwald                print "Failed to load extension", repr(name)
9197aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                import traceback
9207aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                traceback.print_exc()
9217aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
9227aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def get_standard_extension_names(self):
9234d5bc6031ca883201f87e0e3c94e5746f9f91439Kurt B. Kaiser        return idleConf.GetExtensions(editor_only=True)
9247aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
9257aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def load_extension(self, name):
926b00e89faab0d69feb040118bbc5ea6149e91867fKurt B. Kaiser        try:
927b00e89faab0d69feb040118bbc5ea6149e91867fKurt B. Kaiser            mod = __import__(name, globals(), locals(), [])
928b00e89faab0d69feb040118bbc5ea6149e91867fKurt B. Kaiser        except ImportError:
929b00e89faab0d69feb040118bbc5ea6149e91867fKurt B. Kaiser            print "\nFailed to import extension: ", name
930b00e89faab0d69feb040118bbc5ea6149e91867fKurt B. Kaiser            return
9317aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        cls = getattr(mod, name)
9324d5bc6031ca883201f87e0e3c94e5746f9f91439Kurt B. Kaiser        keydefs = idleConf.GetExtensionBindings(name)
9334d5bc6031ca883201f87e0e3c94e5746f9f91439Kurt B. Kaiser        if hasattr(cls, "menudefs"):
9344d5bc6031ca883201f87e0e3c94e5746f9f91439Kurt B. Kaiser            self.fill_menus(cls.menudefs, keydefs)
9357aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        ins = cls(self)
9367aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.extensions[name] = ins
9377aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if keydefs:
9387aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.apply_bindings(keydefs)
9397aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            for vevent in keydefs.keys():
940220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser                methodname = vevent.replace("-", "_")
9417aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                while methodname[:1] == '<':
9427aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                    methodname = methodname[1:]
9437aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                while methodname[-1:] == '>':
9447aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                    methodname = methodname[:-1]
9457aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                methodname = methodname + "_event"
9467aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                if hasattr(ins, methodname):
9477aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                    self.text.bind(vevent, getattr(ins, methodname))
9487aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
9497aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def apply_bindings(self, keydefs=None):
9507aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if keydefs is None:
9517aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            keydefs = self.Bindings.default_keydefs
9527aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text = self.text
9537aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text.keydefs = keydefs
9547aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        for event, keylist in keydefs.items():
9557aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            if keylist:
956931237e2e66975c54e2ac6c5e503ee2992a22bcfRaymond Hettinger                text.event_add(event, *keylist)
9577aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
958610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser    def fill_menus(self, menudefs=None, keydefs=None):
95983118c6cb36cf9a424bec1b9a2ef8c8760bae8f5Kurt B. Kaiser        """Add appropriate entries to the menus and submenus
96083118c6cb36cf9a424bec1b9a2ef8c8760bae8f5Kurt B. Kaiser
96183118c6cb36cf9a424bec1b9a2ef8c8760bae8f5Kurt B. Kaiser        Menus that are absent or None in self.menudict are ignored.
96283118c6cb36cf9a424bec1b9a2ef8c8760bae8f5Kurt B. Kaiser        """
963610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser        if menudefs is None:
964610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser            menudefs = self.Bindings.menudefs
9657aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if keydefs is None:
9667aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            keydefs = self.Bindings.default_keydefs
9677aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        menudict = self.menudict
9687aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text = self.text
969610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser        for mname, entrylist in menudefs:
9707aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            menu = menudict.get(mname)
9717aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            if not menu:
9727aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                continue
973610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser            for entry in entrylist:
974610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser                if not entry:
9757aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                    menu.add_separator()
9767aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                else:
977610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser                    label, eventname = entry
9787aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                    checkbutton = (label[:1] == '!')
9797aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                    if checkbutton:
9807aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                        label = label[1:]
9817aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                    underline, label = prepstr(label)
982610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser                    accelerator = get_accelerator(keydefs, eventname)
983610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser                    def command(text=text, eventname=eventname):
984610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser                        text.event_generate(eventname)
9857aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                    if checkbutton:
986610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser                        var = self.get_var_obj(eventname, BooleanVar)
9877aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                        menu.add_checkbutton(label=label, underline=underline,
9887aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                            command=command, accelerator=accelerator,
9897aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                            variable=var)
9907aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                    else:
9917aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                        menu.add_command(label=label, underline=underline,
99284f4803f4f7ccf40cd777d1e9627528c30bc9053Kurt B. Kaiser                                         command=command,
99384f4803f4f7ccf40cd777d1e9627528c30bc9053Kurt B. Kaiser                                         accelerator=accelerator)
9947aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
9957aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def getvar(self, name):
996610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser        var = self.get_var_obj(name)
9977aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if var:
998610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser            value = var.get()
999610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser            return value
1000610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser        else:
1001610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser            raise NameError, name
10027aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
10037aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def setvar(self, name, value, vartype=None):
1004610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser        var = self.get_var_obj(name, vartype)
10057aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if var:
10067aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            var.set(value)
1007610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser        else:
1008610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser            raise NameError, name
10097aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
1010610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser    def get_var_obj(self, name, vartype=None):
1011610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser        var = self.tkinter_vars.get(name)
10127aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if not var and vartype:
1013610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser            # create a Tkinter variable object with self.text as master:
1014610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser            self.tkinter_vars[name] = var = vartype(self.text)
10157aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        return var
10167aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
10177aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # Tk implementations of "virtual text methods" -- each platform
10187aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # reusing IDLE's support code needs to define these for its GUI's
10197aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # flavor of widget.
10207aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
10217aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # Is character at text_index in a Python string?  Return 0 for
10227aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # "guaranteed no", true for anything else.  This info is expensive
10237aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # to compute ab initio, but is probably already known by the
10247aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # platform's colorizer.
10257aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
10267aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def is_char_in_string(self, text_index):
10277aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if self.color:
10287aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            # Return true iff colorizer hasn't (re)gotten this far
10297aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            # yet, or the character is tagged as being in a string
10307aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return self.text.tag_prevrange("TODO", text_index) or \
10317aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                   "STRING" in self.text.tag_names(text_index)
10327aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        else:
10337aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            # The colorizer is missing: assume the worst
10347aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return 1
10357aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
10367aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # If a selection is defined in the text widget, return (start,
10377aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # end) as Tkinter text indices, otherwise return (None, None)
10387aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def get_selection_indices(self):
10397aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        try:
10407aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            first = self.text.index("sel.first")
10417aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            last = self.text.index("sel.last")
10427aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return first, last
10437aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        except TclError:
10447aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return None, None
10457aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
10467aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # Return the text widget's current view of what a tab stop means
10477aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # (equivalent width in spaces).
10487aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
10497aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def get_tabwidth(self):
10507aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        current = self.text['tabs'] or TK_TABWIDTH_DEFAULT
10517aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        return int(current)
10527aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
10537aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # Set the text widget's current view of what a tab stop means.
10547aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
10557aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def set_tabwidth(self, newtabwidth):
10567aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text = self.text
10577aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if self.get_tabwidth() != newtabwidth:
10587aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            pixels = text.tk.call("font", "measure", text["font"],
10597aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                                  "-displayof", text.master,
1060afdf71b9eefdfb5904e40602984836b28318bad0Kurt B. Kaiser                                  "n" * newtabwidth)
10617aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            text.configure(tabs=pixels)
10627aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
1063cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # If ispythonsource and guess are true, guess a good value for
1064cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # indentwidth based on file content (if possible), and if
1065cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # indentwidth != tabwidth set usetabs false.
1066cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # In any case, adjust the Text widget's view of what a tab
1067cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # character means.
1068cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
10696af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser    def set_indentation_params(self, ispythonsource, guess=True):
1070cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if guess and ispythonsource:
1071cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            i = self.guess_indent()
1072cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if 2 <= i <= 8:
1073cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                self.indentwidth = i
1074cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if self.indentwidth != self.tabwidth:
10756af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser                self.usetabs = False
1076cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        self.set_tabwidth(self.tabwidth)
1077cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1078cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def smart_backspace_event(self, event):
1079cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text = self.text
1080cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        first, last = self.get_selection_indices()
1081cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if first and last:
1082cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            text.delete(first, last)
1083cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            text.mark_set("insert", first)
1084cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            return "break"
1085cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        # Delete whitespace left, until hitting a real char or closest
1086cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        # preceding virtual tab stop.
1087cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        chars = text.get("insert linestart", "insert")
1088cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if chars == '':
1089cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if text.compare("insert", ">", "1.0"):
1090cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                # easy: delete preceding newline
1091cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                text.delete("insert-1c")
1092cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            else:
1093cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                text.bell()     # at start of buffer
1094cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            return "break"
1095cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if  chars[-1] not in " \t":
1096cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            # easy: delete preceding real char
1097cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            text.delete("insert-1c")
1098cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            return "break"
1099cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        # Ick.  It may require *inserting* spaces if we back up over a
1100cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        # tab character!  This is written to be clear, not fast.
11011b3c26998e0f8b6975f7bbccf043c0afe398f151Kurt B. Kaiser        tabwidth = self.tabwidth
11021b3c26998e0f8b6975f7bbccf043c0afe398f151Kurt B. Kaiser        have = len(chars.expandtabs(tabwidth))
1103cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        assert have > 0
1104cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        want = ((have - 1) // self.indentwidth) * self.indentwidth
11054ada7ad3bc9ab269189ed20e57943d9721664debKurt B. Kaiser        # Debug prompt is multilined....
11064ada7ad3bc9ab269189ed20e57943d9721664debKurt B. Kaiser        last_line_of_prompt = sys.ps1.split('\n')[-1]
1107cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        ncharsdeleted = 0
1108cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        while 1:
11094ada7ad3bc9ab269189ed20e57943d9721664debKurt B. Kaiser            if chars == last_line_of_prompt:
11101bdca5e051a5424aaaaf7968c710d31132a6c335Kurt B. Kaiser                break
1111cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            chars = chars[:-1]
1112cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            ncharsdeleted = ncharsdeleted + 1
11131b3c26998e0f8b6975f7bbccf043c0afe398f151Kurt B. Kaiser            have = len(chars.expandtabs(tabwidth))
1114cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if have <= want or chars[-1] not in " \t":
1115cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                break
1116cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.undo_block_start()
1117cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.delete("insert-%dc" % ncharsdeleted, "insert")
1118cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if have < want:
1119cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            text.insert("insert", ' ' * (want - have))
1120cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.undo_block_stop()
1121cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        return "break"
1122cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1123cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def smart_indent_event(self, event):
1124cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        # if intraline selection:
1125cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        #     delete it
1126cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        # elif multiline selection:
11276af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        #     do indent-region
11286af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        # else:
11296af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        #     indent one level
1130cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text = self.text
1131cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        first, last = self.get_selection_indices()
1132cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.undo_block_start()
1133cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        try:
1134cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if first and last:
1135cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                if index2line(first) != index2line(last):
1136cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    return self.indent_region_event(event)
1137cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                text.delete(first, last)
1138cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                text.mark_set("insert", first)
1139cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            prefix = text.get("insert linestart", "insert")
1140cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            raw, effective = classifyws(prefix, self.tabwidth)
1141cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if raw == len(prefix):
1142cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                # only whitespace to the left
1143cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                self.reindent_to(effective + self.indentwidth)
1144cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            else:
11456af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser                # tab to the next 'stop' within or to right of line's text:
1146cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                if self.usetabs:
1147cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    pad = '\t'
1148cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                else:
11491b3c26998e0f8b6975f7bbccf043c0afe398f151Kurt B. Kaiser                    effective = len(prefix.expandtabs(self.tabwidth))
1150cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    n = self.indentwidth
1151cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    pad = ' ' * (n - effective % n)
1152cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                text.insert("insert", pad)
1153cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            text.see("insert")
1154cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            return "break"
1155cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        finally:
1156cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            text.undo_block_stop()
1157cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1158cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def newline_and_indent_event(self, event):
1159cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text = self.text
1160cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        first, last = self.get_selection_indices()
1161cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.undo_block_start()
1162cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        try:
1163cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if first and last:
1164cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                text.delete(first, last)
1165cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                text.mark_set("insert", first)
1166cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            line = text.get("insert linestart", "insert")
1167cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            i, n = 0, len(line)
1168cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            while i < n and line[i] in " \t":
1169cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                i = i+1
1170cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if i == n:
11714ada7ad3bc9ab269189ed20e57943d9721664debKurt B. Kaiser                # the cursor is in or at leading indentation in a continuation
11724ada7ad3bc9ab269189ed20e57943d9721664debKurt B. Kaiser                # line; just inject an empty line at the start
1173cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                text.insert("insert linestart", '\n')
1174cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                return "break"
1175cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            indent = line[:i]
11764ada7ad3bc9ab269189ed20e57943d9721664debKurt B. Kaiser            # strip whitespace before insert point unless it's in the prompt
1177cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            i = 0
11784ada7ad3bc9ab269189ed20e57943d9721664debKurt B. Kaiser            last_line_of_prompt = sys.ps1.split('\n')[-1]
11794ada7ad3bc9ab269189ed20e57943d9721664debKurt B. Kaiser            while line and line[-1] in " \t" and line != last_line_of_prompt:
1180cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                line = line[:-1]
1181cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                i = i+1
1182cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if i:
1183cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                text.delete("insert - %d chars" % i, "insert")
1184cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            # strip whitespace after insert point
1185cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            while text.get("insert") in " \t":
1186cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                text.delete("insert")
1187cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            # start new line
1188cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            text.insert("insert", '\n')
1189cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1190cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            # adjust indentation for continuations and block
1191cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            # open/close first need to find the last stmt
1192cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            lno = index2line(text.index('insert'))
1193cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            y = PyParse.Parser(self.indentwidth, self.tabwidth)
1194b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser            if not self.context_use_ps1:
1195b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser                for context in self.num_context_lines:
1196b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser                    startat = max(lno - context, 1)
1197b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser                    startatindex = `startat` + ".0"
1198b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser                    rawtext = text.get(startatindex, "insert")
1199b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser                    y.set_str(rawtext)
1200b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser                    bod = y.find_good_parse_start(
1201b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser                              self.context_use_ps1,
1202b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser                              self._build_char_in_string_func(startatindex))
1203b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser                    if bod is not None or startat == 1:
1204b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser                        break
1205b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser                y.set_lo(bod or 0)
1206b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser            else:
1207b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser                r = text.tag_prevrange("console", "insert")
1208b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser                if r:
1209b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser                    startatindex = r[1]
1210b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser                else:
1211b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser                    startatindex = "1.0"
1212cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                rawtext = text.get(startatindex, "insert")
1213cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                y.set_str(rawtext)
1214b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser                y.set_lo(0)
1215b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser
1216cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            c = y.get_continuation_type()
1217cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if c != PyParse.C_NONE:
1218cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                # The current stmt hasn't ended yet.
1219b61602c96821997884e7de08d56404904baa034bKurt B. Kaiser                if c == PyParse.C_STRING_FIRST_LINE:
1220b61602c96821997884e7de08d56404904baa034bKurt B. Kaiser                    # after the first line of a string; do not indent at all
1221b61602c96821997884e7de08d56404904baa034bKurt B. Kaiser                    pass
1222b61602c96821997884e7de08d56404904baa034bKurt B. Kaiser                elif c == PyParse.C_STRING_NEXT_LINES:
1223b61602c96821997884e7de08d56404904baa034bKurt B. Kaiser                    # inside a string which started before this line;
1224b61602c96821997884e7de08d56404904baa034bKurt B. Kaiser                    # just mimic the current indent
1225cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    text.insert("insert", indent)
1226cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                elif c == PyParse.C_BRACKET:
1227cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    # line up with the first (if any) element of the
1228cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    # last open bracket structure; else indent one
1229cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    # level beyond the indent of the line with the
1230cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    # last open bracket
1231cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    self.reindent_to(y.compute_bracket_indent())
1232cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                elif c == PyParse.C_BACKSLASH:
1233cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    # if more than one line in this stmt already, just
1234cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    # mimic the current indent; else if initial line
1235cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    # has a start on an assignment stmt, indent to
1236cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    # beyond leftmost =; else to beyond first chunk of
1237cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    # non-whitespace on initial line
1238cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    if y.get_num_lines_in_stmt() > 1:
1239cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                        text.insert("insert", indent)
1240cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    else:
1241cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                        self.reindent_to(y.compute_backslash_indent())
1242cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                else:
124370a6b49821a3226f55e9716f32d802d06640cb89Walter Dörwald                    assert 0, "bogus continuation type %r" % (c,)
1244cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                return "break"
1245cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1246cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            # This line starts a brand new stmt; indent relative to
1247cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            # indentation of initial line of closest preceding
1248cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            # interesting stmt.
1249cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            indent = y.get_base_indent_string()
1250cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            text.insert("insert", indent)
1251cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if y.is_block_opener():
1252cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                self.smart_indent_event(event)
1253cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            elif indent and y.is_block_closer():
1254cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                self.smart_backspace_event(event)
1255cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            return "break"
1256cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        finally:
1257cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            text.see("insert")
1258cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            text.undo_block_stop()
1259cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1260cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # Our editwin provides a is_char_in_string function that works
1261cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # with a Tk text index, but PyParse only knows about offsets into
1262cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # a string. This builds a function for PyParse that accepts an
1263cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # offset.
1264cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1265cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def _build_char_in_string_func(self, startindex):
1266cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        def inner(offset, _startindex=startindex,
1267cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                  _icis=self.is_char_in_string):
1268cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            return _icis(_startindex + "+%dc" % offset)
1269cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        return inner
1270cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1271cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def indent_region_event(self, event):
1272cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        head, tail, chars, lines = self.get_region()
1273cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        for pos in range(len(lines)):
1274cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            line = lines[pos]
1275cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if line:
1276cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                raw, effective = classifyws(line, self.tabwidth)
1277cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                effective = effective + self.indentwidth
1278cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                lines[pos] = self._make_blanks(effective) + line[raw:]
1279cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        self.set_region(head, tail, chars, lines)
1280cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        return "break"
1281cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1282cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def dedent_region_event(self, event):
1283cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        head, tail, chars, lines = self.get_region()
1284cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        for pos in range(len(lines)):
1285cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            line = lines[pos]
1286cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if line:
1287cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                raw, effective = classifyws(line, self.tabwidth)
1288cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                effective = max(effective - self.indentwidth, 0)
1289cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                lines[pos] = self._make_blanks(effective) + line[raw:]
1290cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        self.set_region(head, tail, chars, lines)
1291cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        return "break"
1292cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1293cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def comment_region_event(self, event):
1294cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        head, tail, chars, lines = self.get_region()
1295cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        for pos in range(len(lines) - 1):
1296cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            line = lines[pos]
1297cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            lines[pos] = '##' + line
1298cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        self.set_region(head, tail, chars, lines)
1299cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1300cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def uncomment_region_event(self, event):
1301cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        head, tail, chars, lines = self.get_region()
1302cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        for pos in range(len(lines)):
1303cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            line = lines[pos]
1304cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if not line:
1305cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                continue
1306cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if line[:2] == '##':
1307cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                line = line[2:]
1308cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            elif line[:1] == '#':
1309cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                line = line[1:]
1310cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            lines[pos] = line
1311cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        self.set_region(head, tail, chars, lines)
1312cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1313cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def tabify_region_event(self, event):
1314cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        head, tail, chars, lines = self.get_region()
1315cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        tabwidth = self._asktabwidth()
1316cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        for pos in range(len(lines)):
1317cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            line = lines[pos]
1318cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if line:
1319cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                raw, effective = classifyws(line, tabwidth)
1320cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                ntabs, nspaces = divmod(effective, tabwidth)
1321cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                lines[pos] = '\t' * ntabs + ' ' * nspaces + line[raw:]
1322cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        self.set_region(head, tail, chars, lines)
1323cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1324cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def untabify_region_event(self, event):
1325cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        head, tail, chars, lines = self.get_region()
1326cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        tabwidth = self._asktabwidth()
1327cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        for pos in range(len(lines)):
13281b3c26998e0f8b6975f7bbccf043c0afe398f151Kurt B. Kaiser            lines[pos] = lines[pos].expandtabs(tabwidth)
1329cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        self.set_region(head, tail, chars, lines)
1330cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1331cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def toggle_tabs_event(self, event):
1332cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if self.askyesno(
1333cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser              "Toggle tabs",
13346af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser              "Turn tabs " + ("on", "off")[self.usetabs] +
13356af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser              "?\nIndent width " +
133653f2b5fab2f525342dbb69f6f2d5eed978c7883fKurt B. Kaiser              ("will be", "remains at")[self.usetabs] + " 8." +
133753f2b5fab2f525342dbb69f6f2d5eed978c7883fKurt B. Kaiser              "\n Note: a tab is always 8 columns",
1338cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser              parent=self.text):
1339cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            self.usetabs = not self.usetabs
134053f2b5fab2f525342dbb69f6f2d5eed978c7883fKurt B. Kaiser            # Try to prevent inconsistent indentation.
134153f2b5fab2f525342dbb69f6f2d5eed978c7883fKurt B. Kaiser            # User must change indent width manually after using tabs.
134253f2b5fab2f525342dbb69f6f2d5eed978c7883fKurt B. Kaiser            self.indentwidth = 8
1343cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        return "break"
1344cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
13456af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser    # XXX this isn't bound to anything -- see tabwidth comments
13466af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser##     def change_tabwidth_event(self, event):
13476af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser##         new = self._asktabwidth()
13486af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser##         if new != self.tabwidth:
13496af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser##             self.tabwidth = new
13506af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser##             self.set_indentation_params(0, guess=0)
13516af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser##         return "break"
1352cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1353cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def change_indentwidth_event(self, event):
1354cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        new = self.askinteger(
1355cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                  "Indent width",
13566af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser                  "New indent width (2-16)\n(Always use 8 when using tabs)",
1357cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                  parent=self.text,
1358cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                  initialvalue=self.indentwidth,
1359cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                  minvalue=2,
1360cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                  maxvalue=16)
13616af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        if new and new != self.indentwidth and not self.usetabs:
1362cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            self.indentwidth = new
1363cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        return "break"
1364cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1365cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def get_region(self):
1366cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text = self.text
1367cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        first, last = self.get_selection_indices()
1368cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if first and last:
1369cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            head = text.index(first + " linestart")
1370cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            tail = text.index(last + "-1c lineend +1c")
1371cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        else:
1372cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            head = text.index("insert linestart")
1373cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            tail = text.index("insert lineend +1c")
1374cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        chars = text.get(head, tail)
13751b3c26998e0f8b6975f7bbccf043c0afe398f151Kurt B. Kaiser        lines = chars.split("\n")
1376cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        return head, tail, chars, lines
1377cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1378cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def set_region(self, head, tail, chars, lines):
1379cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text = self.text
13801b3c26998e0f8b6975f7bbccf043c0afe398f151Kurt B. Kaiser        newchars = "\n".join(lines)
1381cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if newchars == chars:
1382cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            text.bell()
1383cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            return
1384cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.tag_remove("sel", "1.0", "end")
1385cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.mark_set("insert", head)
1386cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.undo_block_start()
1387cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.delete(head, tail)
1388cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.insert(head, newchars)
1389cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.undo_block_stop()
1390cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.tag_add("sel", head, "insert")
1391cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1392cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # Make string that displays as n leading blanks.
1393cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1394cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def _make_blanks(self, n):
1395cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if self.usetabs:
1396cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            ntabs, nspaces = divmod(n, self.tabwidth)
1397cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            return '\t' * ntabs + ' ' * nspaces
1398cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        else:
1399cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            return ' ' * n
1400cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1401cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # Delete from beginning of line to insert point, then reinsert
1402cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # column logical (meaning use tabs if appropriate) spaces.
1403cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1404cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def reindent_to(self, column):
1405cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text = self.text
1406cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.undo_block_start()
1407cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if text.compare("insert linestart", "!=", "insert"):
1408cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            text.delete("insert linestart", "insert")
1409cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if column:
1410cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            text.insert("insert", self._make_blanks(column))
1411cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.undo_block_stop()
1412cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1413cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def _asktabwidth(self):
1414cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        return self.askinteger(
1415cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            "Tab width",
1416ca7329c9c124080b817ceb67b4e3ee7963b81d41Kurt B. Kaiser            "Columns per tab? (2-16)",
1417cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            parent=self.text,
1418cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            initialvalue=self.indentwidth,
1419cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            minvalue=2,
1420cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            maxvalue=16) or self.tabwidth
1421cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1422cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # Guess indentwidth from text content.
1423cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # Return guessed indentwidth.  This should not be believed unless
1424cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # it's in a reasonable range (e.g., it will be 0 if no indented
1425cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # blocks are found).
1426cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1427cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def guess_indent(self):
1428cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        opener, indented = IndentSearcher(self.text, self.tabwidth).run()
1429cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if opener and indented:
1430cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            raw, indentsmall = classifyws(opener, self.tabwidth)
1431cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            raw, indentlarge = classifyws(indented, self.tabwidth)
1432cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        else:
1433cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            indentsmall = indentlarge = 0
1434cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        return indentlarge - indentsmall
1435cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1436cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser# "line.col" -> line, as an int
1437cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiserdef index2line(index):
1438cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    return int(float(index))
1439cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1440cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser# Look at the leading whitespace in s.
1441cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser# Return pair (# of leading ws characters,
1442cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser#              effective # of leading blanks after expanding
1443cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser#              tabs to width tabwidth)
1444cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1445cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiserdef classifyws(s, tabwidth):
1446cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    raw = effective = 0
1447cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    for ch in s:
1448cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if ch == ' ':
1449cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            raw = raw + 1
1450cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            effective = effective + 1
1451cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        elif ch == '\t':
1452cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            raw = raw + 1
1453cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            effective = (effective // tabwidth + 1) * tabwidth
1454cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        else:
1455cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            break
1456cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    return raw, effective
1457cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1458cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiserimport tokenize
1459cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser_tokenize = tokenize
1460cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiserdel tokenize
1461cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1462dcba6622f52efafa28104a07db9d5ba2b1a8d628Kurt B. Kaiserclass IndentSearcher(object):
1463cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1464cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # .run() chews over the Text widget, looking for a block opener
1465cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # and the stmt following it.  Returns a pair,
1466cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    #     (line containing block opener, line containing stmt)
1467cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # Either or both may be None.
1468cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1469cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def __init__(self, text, tabwidth):
1470cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        self.text = text
1471cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        self.tabwidth = tabwidth
1472cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        self.i = self.finished = 0
1473cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        self.blkopenline = self.indentedline = None
1474cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1475cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def readline(self):
1476cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if self.finished:
1477cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            return ""
1478cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        i = self.i = self.i + 1
147970a6b49821a3226f55e9716f32d802d06640cb89Walter Dörwald        mark = repr(i) + ".0"
1480cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if self.text.compare(mark, ">=", "end"):
1481cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            return ""
1482cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        return self.text.get(mark, mark + " lineend+1c")
1483cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1484cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def tokeneater(self, type, token, start, end, line,
1485cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                   INDENT=_tokenize.INDENT,
1486cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                   NAME=_tokenize.NAME,
1487cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                   OPENERS=('class', 'def', 'for', 'if', 'try', 'while')):
1488cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if self.finished:
1489cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            pass
1490cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        elif type == NAME and token in OPENERS:
1491cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            self.blkopenline = line
1492cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        elif type == INDENT and self.blkopenline:
1493cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            self.indentedline = line
1494cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            self.finished = 1
1495cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1496cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def run(self):
1497cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        save_tabsize = _tokenize.tabsize
1498cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        _tokenize.tabsize = self.tabwidth
1499cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        try:
1500cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            try:
1501cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                _tokenize.tokenize(self.readline, self.tokeneater)
1502cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            except _tokenize.TokenError:
1503cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                # since we cut off the tokenizer early, we can trigger
1504cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                # spurious errors
1505cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                pass
1506cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        finally:
1507cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            _tokenize.tabsize = save_tabsize
1508cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        return self.blkopenline, self.indentedline
1509cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1510cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser### end autoindent code ###
1511cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
15127aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererdef prepstr(s):
15137aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # Helper to extract the underscore from a string, e.g.
15147aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # prepstr("Co_py") returns (2, "Copy").
1515220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser    i = s.find('_')
15167aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    if i >= 0:
15177aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        s = s[:i] + s[i+1:]
15187aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    return i, s
15197aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
15207aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
15217aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererkeynames = {
15227aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer 'bracketleft': '[',
15237aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer 'bracketright': ']',
15247aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer 'slash': '/',
15257aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer}
15267aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
1527610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiserdef get_accelerator(keydefs, eventname):
1528610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser    keylist = keydefs.get(eventname)
15297aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    if not keylist:
15307aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        return ""
15317aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    s = keylist[0]
1532220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser    s = re.sub(r"-[a-z]\b", lambda m: m.group().upper(), s)
15337aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    s = re.sub(r"\b\w+\b", lambda m: keynames.get(m.group(), m.group()), s)
15347aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    s = re.sub("Key-", "", s)
15357aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    s = re.sub("Cancel","Ctrl-Break",s)   # dscherer@cmu.edu
15367aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    s = re.sub("Control-", "Ctrl-", s)
15377aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    s = re.sub("-", "+", s)
15387aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    s = re.sub("><", " ", s)
15397aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    s = re.sub("<", "", s)
15407aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    s = re.sub(">", "", s)
15417aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    return s
15427aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
15437aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
15447aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererdef fixwordbreaks(root):
15457aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # Make sure that Tk's double-click and next/previous word
15467aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # operations use our definition of a word (i.e. an identifier)
15477aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    tk = root.tk
15487aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    tk.call('tcl_wordBreakAfter', 'a b', 0) # make sure word.tcl is loaded
15497aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    tk.call('set', 'tcl_wordchars', '[a-zA-Z0-9_]')
15507aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    tk.call('set', 'tcl_nonwordchars', '[^a-zA-Z0-9_]')
15517aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
15527aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
15537aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererdef test():
15547aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    root = Tk()
15557aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    fixwordbreaks(root)
15567aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    root.withdraw()
15577aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    if sys.argv[1:]:
15587aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        filename = sys.argv[1]
15597aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    else:
15607aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        filename = None
15617aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    edit = EditorWindow(root=root, filename=filename)
15627aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    edit.set_close_hook(root.quit)
15630b634efcbcd10d9364796e9b048f18cd4a08b88bKurt B. Kaiser    edit.text.bind("<<close-all-windows>>", edit.close_event)
15647aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    root.mainloop()
15657aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    root.destroy()
15667aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
15677aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererif __name__ == '__main__':
15687aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    test()
1569