EditorWindow.py revision 00b0bd55b4d615740e06ac4d445221a176da30f3
17aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererimport sys
27aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererimport os
3592436552816f23b585e0a8e25284eeb1d25a184Terry Jan Reedyfrom platform import python_version
47aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererimport re
57aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererimport imp
66634bf2919d855ccd821e878b8cc00c7209f1cbeGeorg Brandlfrom Tkinter import *
76634bf2919d855ccd821e878b8cc00c7209f1cbeGeorg Brandlimport tkSimpleDialog
86634bf2919d855ccd821e878b8cc00c7209f1cbeGeorg Brandlimport tkMessageBox
9fd182cd9d36adefba00d86542eb3134119cdc804Kurt B. Kaiserimport webbrowser
10d630c04ab1ab35e2ec6eeeaba9bdcb9f8e730e78Florent Xicluna
11d630c04ab1ab35e2ec6eeeaba9bdcb9f8e730e78Florent Xiclunafrom idlelib.MultiCall import MultiCallCreator
12d630c04ab1ab35e2ec6eeeaba9bdcb9f8e730e78Florent Xiclunafrom idlelib import idlever
13d630c04ab1ab35e2ec6eeeaba9bdcb9f8e730e78Florent Xiclunafrom idlelib import WindowList
14d630c04ab1ab35e2ec6eeeaba9bdcb9f8e730e78Florent Xiclunafrom idlelib import SearchDialog
15d630c04ab1ab35e2ec6eeeaba9bdcb9f8e730e78Florent Xiclunafrom idlelib import GrepDialog
16d630c04ab1ab35e2ec6eeeaba9bdcb9f8e730e78Florent Xiclunafrom idlelib import ReplaceDialog
17d630c04ab1ab35e2ec6eeeaba9bdcb9f8e730e78Florent Xiclunafrom idlelib import PyParse
18d630c04ab1ab35e2ec6eeeaba9bdcb9f8e730e78Florent Xiclunafrom idlelib.configHandler import idleConf
19d630c04ab1ab35e2ec6eeeaba9bdcb9f8e730e78Florent Xiclunafrom idlelib import aboutDialog, textView, configDialog
20d630c04ab1ab35e2ec6eeeaba9bdcb9f8e730e78Florent Xiclunafrom idlelib import macosxSupport
217aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
227aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer# The default tab setting for a Text widget, in average-width characters.
237aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David SchererTK_TABWIDTH_DEFAULT = 8
247aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
25f13447f3f75c7b56eec6a602b0b72a48fbbc5486Kurt B. Kaiserdef _sphinx_version():
26f13447f3f75c7b56eec6a602b0b72a48fbbc5486Kurt B. Kaiser    "Format sys.version_info to produce the Sphinx version string used to install the chm docs"
27f13447f3f75c7b56eec6a602b0b72a48fbbc5486Kurt B. Kaiser    major, minor, micro, level, serial = sys.version_info
28f13447f3f75c7b56eec6a602b0b72a48fbbc5486Kurt B. Kaiser    release = '%s%s' % (major, minor)
29f13447f3f75c7b56eec6a602b0b72a48fbbc5486Kurt B. Kaiser    if micro:
30fb23463139a173062031df9c258ac772b56ae08dBenjamin Peterson        release += '%s' % (micro,)
31fb23463139a173062031df9c258ac772b56ae08dBenjamin Peterson    if level == 'candidate':
32fb23463139a173062031df9c258ac772b56ae08dBenjamin Peterson        release += 'rc%s' % (serial,)
33fb23463139a173062031df9c258ac772b56ae08dBenjamin Peterson    elif level != 'final':
34f13447f3f75c7b56eec6a602b0b72a48fbbc5486Kurt B. Kaiser        release += '%s%s' % (level[0], serial)
35f13447f3f75c7b56eec6a602b0b72a48fbbc5486Kurt B. Kaiser    return release
36f13447f3f75c7b56eec6a602b0b72a48fbbc5486Kurt B. Kaiser
37220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiserdef _find_module(fullname, path=None):
38220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser    """Version of imp.find_module() that handles hierarchical module names"""
39220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser
40220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser    file = None
41220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser    for tgt in fullname.split('.'):
42220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser        if file is not None:
43220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser            file.close()            # close intermediate files
44220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser        (file, filename, descr) = imp.find_module(tgt, path)
45220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser        if descr[2] == imp.PY_SOURCE:
46220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser            break                   # find but not load the source file
47220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser        module = imp.load_module(tgt, file, filename, descr)
4869e8afcc9fcd8a41a1035893d60893d51fae62f1Kurt B. Kaiser        try:
4969e8afcc9fcd8a41a1035893d60893d51fae62f1Kurt B. Kaiser            path = module.__path__
5069e8afcc9fcd8a41a1035893d60893d51fae62f1Kurt B. Kaiser        except AttributeError:
5169e8afcc9fcd8a41a1035893d60893d51fae62f1Kurt B. Kaiser            raise ImportError, 'No source for module ' + module.__name__
52179816df59744313693d21525046d1d38af9119cRaymond Hettinger    if descr[2] != imp.PY_SOURCE:
53179816df59744313693d21525046d1d38af9119cRaymond Hettinger        # If all of the above fails and didn't raise an exception,fallback
54179816df59744313693d21525046d1d38af9119cRaymond Hettinger        # to a straight import which can find __init__.py in a package.
55179816df59744313693d21525046d1d38af9119cRaymond Hettinger        m = __import__(fullname)
56179816df59744313693d21525046d1d38af9119cRaymond Hettinger        try:
57179816df59744313693d21525046d1d38af9119cRaymond Hettinger            filename = m.__file__
58179816df59744313693d21525046d1d38af9119cRaymond Hettinger        except AttributeError:
59179816df59744313693d21525046d1d38af9119cRaymond Hettinger            pass
60179816df59744313693d21525046d1d38af9119cRaymond Hettinger        else:
61179816df59744313693d21525046d1d38af9119cRaymond Hettinger            file = None
62179816df59744313693d21525046d1d38af9119cRaymond Hettinger            base, ext = os.path.splitext(filename)
63179816df59744313693d21525046d1d38af9119cRaymond Hettinger            if ext == '.pyc':
64179816df59744313693d21525046d1d38af9119cRaymond Hettinger                ext = '.py'
65179816df59744313693d21525046d1d38af9119cRaymond Hettinger            filename = base + ext
66179816df59744313693d21525046d1d38af9119cRaymond Hettinger            descr = filename, None, imp.PY_SOURCE
67220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser    return file, filename, descr
68220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser
69adb87e2677caad4cd167e4915763dda105bc982cTerry Jan Reedy
70adb87e2677caad4cd167e4915763dda105bc982cTerry Jan Reedyclass HelpDialog(object):
71adb87e2677caad4cd167e4915763dda105bc982cTerry Jan Reedy
72adb87e2677caad4cd167e4915763dda105bc982cTerry Jan Reedy    def __init__(self):
73adb87e2677caad4cd167e4915763dda105bc982cTerry Jan Reedy        self.parent = None      # parent of help window
74adb87e2677caad4cd167e4915763dda105bc982cTerry Jan Reedy        self.dlg = None         # the help window iteself
75adb87e2677caad4cd167e4915763dda105bc982cTerry Jan Reedy
76adb87e2677caad4cd167e4915763dda105bc982cTerry Jan Reedy    def display(self, parent, near=None):
77adb87e2677caad4cd167e4915763dda105bc982cTerry Jan Reedy        """ Display the help dialog.
78adb87e2677caad4cd167e4915763dda105bc982cTerry Jan Reedy
79adb87e2677caad4cd167e4915763dda105bc982cTerry Jan Reedy            parent - parent widget for the help window
80adb87e2677caad4cd167e4915763dda105bc982cTerry Jan Reedy
81adb87e2677caad4cd167e4915763dda105bc982cTerry Jan Reedy            near - a Toplevel widget (e.g. EditorWindow or PyShell)
82adb87e2677caad4cd167e4915763dda105bc982cTerry Jan Reedy                   to use as a reference for placing the help window
83adb87e2677caad4cd167e4915763dda105bc982cTerry Jan Reedy        """
84adb87e2677caad4cd167e4915763dda105bc982cTerry Jan Reedy        if self.dlg is None:
85adb87e2677caad4cd167e4915763dda105bc982cTerry Jan Reedy            self.show_dialog(parent)
86adb87e2677caad4cd167e4915763dda105bc982cTerry Jan Reedy        if near:
87adb87e2677caad4cd167e4915763dda105bc982cTerry Jan Reedy            self.nearwindow(near)
88adb87e2677caad4cd167e4915763dda105bc982cTerry Jan Reedy
89adb87e2677caad4cd167e4915763dda105bc982cTerry Jan Reedy    def show_dialog(self, parent):
90adb87e2677caad4cd167e4915763dda105bc982cTerry Jan Reedy        self.parent = parent
91adb87e2677caad4cd167e4915763dda105bc982cTerry Jan Reedy        fn=os.path.join(os.path.abspath(os.path.dirname(__file__)),'help.txt')
92adb87e2677caad4cd167e4915763dda105bc982cTerry Jan Reedy        self.dlg = dlg = textView.view_file(parent,'Help',fn, modal=False)
93adb87e2677caad4cd167e4915763dda105bc982cTerry Jan Reedy        dlg.bind('<Destroy>', self.destroy, '+')
94adb87e2677caad4cd167e4915763dda105bc982cTerry Jan Reedy
95adb87e2677caad4cd167e4915763dda105bc982cTerry Jan Reedy    def nearwindow(self, near):
96adb87e2677caad4cd167e4915763dda105bc982cTerry Jan Reedy        # Place the help dialog near the window specified by parent.
97adb87e2677caad4cd167e4915763dda105bc982cTerry Jan Reedy        # Note - this may not reposition the window in Metacity
98adb87e2677caad4cd167e4915763dda105bc982cTerry Jan Reedy        #  if "/apps/metacity/general/disable_workarounds" is enabled
99adb87e2677caad4cd167e4915763dda105bc982cTerry Jan Reedy        dlg = self.dlg
100adb87e2677caad4cd167e4915763dda105bc982cTerry Jan Reedy        geom = (near.winfo_rootx() + 10, near.winfo_rooty() + 10)
101adb87e2677caad4cd167e4915763dda105bc982cTerry Jan Reedy        dlg.withdraw()
102adb87e2677caad4cd167e4915763dda105bc982cTerry Jan Reedy        dlg.geometry("=+%d+%d" % geom)
103adb87e2677caad4cd167e4915763dda105bc982cTerry Jan Reedy        dlg.deiconify()
104adb87e2677caad4cd167e4915763dda105bc982cTerry Jan Reedy        dlg.lift()
105adb87e2677caad4cd167e4915763dda105bc982cTerry Jan Reedy
106adb87e2677caad4cd167e4915763dda105bc982cTerry Jan Reedy    def destroy(self, ev=None):
107adb87e2677caad4cd167e4915763dda105bc982cTerry Jan Reedy        self.dlg = None
108adb87e2677caad4cd167e4915763dda105bc982cTerry Jan Reedy        self.parent = None
109adb87e2677caad4cd167e4915763dda105bc982cTerry Jan Reedy
110adb87e2677caad4cd167e4915763dda105bc982cTerry Jan ReedyhelpDialog = HelpDialog()  # singleton instance
11100b0bd55b4d615740e06ac4d445221a176da30f3Terry Jan Reedydef _Help_dialog(parent):  # wrapper for htest
11200b0bd55b4d615740e06ac4d445221a176da30f3Terry Jan Reedy    helpDialog.show_dialog(parent)
113adb87e2677caad4cd167e4915763dda105bc982cTerry Jan Reedy
114adb87e2677caad4cd167e4915763dda105bc982cTerry Jan Reedy
115dcba6622f52efafa28104a07db9d5ba2b1a8d628Kurt B. Kaiserclass EditorWindow(object):
116d630c04ab1ab35e2ec6eeeaba9bdcb9f8e730e78Florent Xicluna    from idlelib.Percolator import Percolator
117d630c04ab1ab35e2ec6eeeaba9bdcb9f8e730e78Florent Xicluna    from idlelib.ColorDelegator import ColorDelegator
118d630c04ab1ab35e2ec6eeeaba9bdcb9f8e730e78Florent Xicluna    from idlelib.UndoDelegator import UndoDelegator
119d630c04ab1ab35e2ec6eeeaba9bdcb9f8e730e78Florent Xicluna    from idlelib.IOBinding import IOBinding, filesystemencoding, encoding
120d630c04ab1ab35e2ec6eeeaba9bdcb9f8e730e78Florent Xicluna    from idlelib import Bindings
1216634bf2919d855ccd821e878b8cc00c7209f1cbeGeorg Brandl    from Tkinter import Toplevel
122d630c04ab1ab35e2ec6eeeaba9bdcb9f8e730e78Florent Xicluna    from idlelib.MultiStatusBar import MultiStatusBar
1237aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
124114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser    help_url = None
1257aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
1267aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def __init__(self, flist=None, filename=None, key=None, root=None):
127114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser        if EditorWindow.help_url is None:
12884ef153c74decf0b036042b5ca3f5cb3cd785eddThomas Heller            dochome =  os.path.join(sys.prefix, 'Doc', 'index.html')
129114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser            if sys.platform.count('linux'):
130114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser                # look for html docs in a couple of standard places
131114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser                pyver = 'python-docs-' + '%s.%s.%s' % sys.version_info[:3]
132114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser                if os.path.isdir('/var/www/html/python/'):  # "python2" rpm
133114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser                    dochome = '/var/www/html/python/index.html'
134114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser                else:
135114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser                    basepath = '/usr/share/doc/'  # standard location
136114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser                    dochome = os.path.join(basepath, pyver,
137114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser                                           'Doc', 'index.html')
1388aa23927e339ca13459dd408751546352d81fefaKurt B. Kaiser            elif sys.platform[:3] == 'win':
139090e636add33907d196b9899eb0f019654a055e8Kurt B. Kaiser                chmfile = os.path.join(sys.prefix, 'Doc',
140f13447f3f75c7b56eec6a602b0b72a48fbbc5486Kurt B. Kaiser                                       'Python%s.chm' % _sphinx_version())
14184ef153c74decf0b036042b5ca3f5cb3cd785eddThomas Heller                if os.path.isfile(chmfile):
14284ef153c74decf0b036042b5ca3f5cb3cd785eddThomas Heller                    dochome = chmfile
14357847df4e59015c141e645b47112829cf41b582aNed Deily            elif sys.platform == 'darwin':
14457847df4e59015c141e645b47112829cf41b582aNed Deily                # documentation may be stored inside a python framework
14519302d927e6688e02553df16177e4867e2d0e3b3Ronald Oussoren                dochome = os.path.join(sys.prefix,
14619302d927e6688e02553df16177e4867e2d0e3b3Ronald Oussoren                        'Resources/English.lproj/Documentation/index.html')
147114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser            dochome = os.path.normpath(dochome)
148114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser            if os.path.isfile(dochome):
149114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser                EditorWindow.help_url = dochome
15019302d927e6688e02553df16177e4867e2d0e3b3Ronald Oussoren                if sys.platform == 'darwin':
15119302d927e6688e02553df16177e4867e2d0e3b3Ronald Oussoren                    # Safari requires real file:-URLs
15219302d927e6688e02553df16177e4867e2d0e3b3Ronald Oussoren                    EditorWindow.help_url = 'file://' + EditorWindow.help_url
153114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser            else:
1547b4c2beda6b54c28070a04467e22e03f90b43450Raymond Hettinger                EditorWindow.help_url = "http://docs.python.org/%d.%d" % sys.version_info[:2]
155dc72f48e262a7aaf177a37676c79ced4ddce0fffSteven M. Gava        currentTheme=idleConf.CurrentTheme()
1567aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.flist = flist
1577aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        root = root or flist.root
1587aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.root = root
159b3c4d16e688d073dae92f272d66b6efa4b70a734Kurt B. Kaiser        try:
160b3c4d16e688d073dae92f272d66b6efa4b70a734Kurt B. Kaiser            sys.ps1
161b3c4d16e688d073dae92f272d66b6efa4b70a734Kurt B. Kaiser        except AttributeError:
162b3c4d16e688d073dae92f272d66b6efa4b70a734Kurt B. Kaiser            sys.ps1 = '>>> '
1637aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.menubar = Menu(root)
164183403a271977a26c0b77dbcf62e19395c007288Kurt B. Kaiser        self.top = top = WindowList.ListedToplevel(root, menu=self.menubar)
1650c5bc8c9518fd18d41aedede536c4a9aa8dfa619Steven M. Gava        if flist:
166610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser            self.tkinter_vars = flist.vars
16724b07bcba350bb86c4d6ca446e1564647a199868Ezio Melotti            #self.top.instance_dict makes flist.inversedict available to
16824b07bcba350bb86c4d6ca446e1564647a199868Ezio Melotti            #configDialog.py so it can access all EditorWindow instances
169a2946a437ee7886e6192b9e02725b8a04cdc80aeKurt B. Kaiser            self.top.instance_dict = flist.inversedict
170610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser        else:
171610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser            self.tkinter_vars = {}  # keys: Tkinter event names
172610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser                                    # values: Tkinter variable instances
173a2946a437ee7886e6192b9e02725b8a04cdc80aeKurt B. Kaiser            self.top.instance_dict = {}
174a2946a437ee7886e6192b9e02725b8a04cdc80aeKurt B. Kaiser        self.recent_files_path = os.path.join(idleConf.GetUserCfgDir(),
1751d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava                'recent-files.lst')
1767aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.text_frame = text_frame = Frame(top)
1774ebbefe677f47a8e4f624737338f22e714a7e5bcMartin v. Löwis        self.vbar = vbar = Scrollbar(text_frame, name='vbar')
178d8590ff209369f45a5ed417c79ce255a33f70a1dAndrew Svetlov        self.width = idleConf.GetOption('main','EditorWindow','width', type='int')
179ce46511957740aa9963b95db80c764b6c346fb1dKurt B. Kaiser        text_options = {
180ce46511957740aa9963b95db80c764b6c346fb1dKurt B. Kaiser                'name': 'text',
181ce46511957740aa9963b95db80c764b6c346fb1dKurt B. Kaiser                'padx': 5,
182ce46511957740aa9963b95db80c764b6c346fb1dKurt B. Kaiser                'wrap': 'none',
183ce46511957740aa9963b95db80c764b6c346fb1dKurt B. Kaiser                'width': self.width,
184d8590ff209369f45a5ed417c79ce255a33f70a1dAndrew Svetlov                'height': idleConf.GetOption('main', 'EditorWindow', 'height', type='int')}
185ce46511957740aa9963b95db80c764b6c346fb1dKurt B. Kaiser        if TkVersion >= 8.5:
186ce46511957740aa9963b95db80c764b6c346fb1dKurt B. Kaiser            # Starting with tk 8.5 we have to set the new tabstyle option
187ce46511957740aa9963b95db80c764b6c346fb1dKurt B. Kaiser            # to 'wordprocessor' to achieve the same display of tabs as in
188ce46511957740aa9963b95db80c764b6c346fb1dKurt B. Kaiser            # older tk versions.
189ce46511957740aa9963b95db80c764b6c346fb1dKurt B. Kaiser            text_options['tabstyle'] = 'wordprocessor'
190ce46511957740aa9963b95db80c764b6c346fb1dKurt B. Kaiser        self.text = text = MultiCallCreator(Text)(text_frame, **text_options)
191183403a271977a26c0b77dbcf62e19395c007288Kurt B. Kaiser        self.top.focused_widget = self.text
1927aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
1937aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.createmenubar()
1947aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.apply_bindings()
1957aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
1967aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.top.protocol("WM_DELETE_WINDOW", self.close)
1977aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.top.bind("<<close-window>>", self.close_event)
19857847df4e59015c141e645b47112829cf41b582aNed Deily        if macosxSupport.isAquaTk():
19917db495445ba1f6c9d360ade62471d49ceb05cfeRonald Oussoren            # Command-W on editorwindows doesn't work without this.
2003075e16c516e3975b61e4356a6d64def9cc5110eRonald Oussoren            text.bind('<<close-window>>', self.close_event)
2013f752ab22e369bd664bbce395099f263e3cff784R. David Murray            # Some OS X systems have only one mouse button,
2023f752ab22e369bd664bbce395099f263e3cff784R. David Murray            # so use control-click for pulldown menus there.
2033f752ab22e369bd664bbce395099f263e3cff784R. David Murray            #  (Note, AquaTk defines <2> as the right button if
2043f752ab22e369bd664bbce395099f263e3cff784R. David Murray            #   present and the Tk Text widget already binds <2>.)
2053f752ab22e369bd664bbce395099f263e3cff784R. David Murray            text.bind("<Control-Button-1>",self.right_menu_event)
2063f752ab22e369bd664bbce395099f263e3cff784R. David Murray        else:
2073f752ab22e369bd664bbce395099f263e3cff784R. David Murray            # Elsewhere, use right-click for pulldown menus.
2083f752ab22e369bd664bbce395099f263e3cff784R. David Murray            text.bind("<3>",self.right_menu_event)
20984f4803f4f7ccf40cd777d1e9627528c30bc9053Kurt B. Kaiser        text.bind("<<cut>>", self.cut)
21084f4803f4f7ccf40cd777d1e9627528c30bc9053Kurt B. Kaiser        text.bind("<<copy>>", self.copy)
21184f4803f4f7ccf40cd777d1e9627528c30bc9053Kurt B. Kaiser        text.bind("<<paste>>", self.paste)
2127aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text.bind("<<center-insert>>", self.center_insert_event)
2137aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text.bind("<<help>>", self.help_dialog)
2147aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text.bind("<<python-docs>>", self.python_docs)
2157aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text.bind("<<about-idle>>", self.about_dialog)
2163b55a891a197212b34b0c077f72cb3af752468ecSteven M. Gava        text.bind("<<open-config-dialog>>", self.config_dialog)
2177aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text.bind("<<open-module>>", self.open_module)
2187aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text.bind("<<do-nothing>>", lambda event: "break")
2197aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text.bind("<<select-all>>", self.select_all)
2207aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text.bind("<<remove-selection>>", self.remove_selection)
221c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        text.bind("<<find>>", self.find_event)
222c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        text.bind("<<find-again>>", self.find_again_event)
223c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        text.bind("<<find-in-files>>", self.find_in_files_event)
224c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        text.bind("<<find-selection>>", self.find_selection_event)
225c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        text.bind("<<replace>>", self.replace_event)
226c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        text.bind("<<goto-line>>", self.goto_line_event)
227cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.bind("<<smart-backspace>>",self.smart_backspace_event)
228cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.bind("<<newline-and-indent>>",self.newline_and_indent_event)
229cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.bind("<<smart-indent>>",self.smart_indent_event)
230cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.bind("<<indent-region>>",self.indent_region_event)
231cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.bind("<<dedent-region>>",self.dedent_region_event)
232cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.bind("<<comment-region>>",self.comment_region_event)
233cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.bind("<<uncomment-region>>",self.uncomment_region_event)
234cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.bind("<<tabify-region>>",self.tabify_region_event)
235cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.bind("<<untabify-region>>",self.untabify_region_event)
236cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.bind("<<toggle-tabs>>",self.toggle_tabs_event)
237cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.bind("<<change-indentwidth>>",self.change_indentwidth_event)
2385ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser        text.bind("<Left>", self.move_at_edge_if_selection(0))
2395ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser        text.bind("<Right>", self.move_at_edge_if_selection(1))
2403069dbb8ec9c287c2ff6b3a1c8bfde83af81c11bKurt B. Kaiser        text.bind("<<del-word-left>>", self.del_word_left)
2413069dbb8ec9c287c2ff6b3a1c8bfde83af81c11bKurt B. Kaiser        text.bind("<<del-word-right>>", self.del_word_right)
24293cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser        text.bind("<<beginning-of-line>>", self.home_callback)
2436655e4bc2752f1114a2e1f9a63ffd4191fa50d0dKurt B. Kaiser
2447aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if flist:
2457aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            flist.inversedict[self] = key
2467aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            if key:
2477aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                flist.dict[key] = self
248d2f4861a0b52a2af5ea3395267a5c56541352f8fKurt B. Kaiser            text.bind("<<open-new-window>>", self.new_callback)
2497aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            text.bind("<<close-all-windows>>", self.flist.close_all_callback)
2507aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            text.bind("<<open-class-browser>>", self.open_class_browser)
2517aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            text.bind("<<open-path-browser>>", self.open_path_browser)
2527aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
253898a365c276951ab2471afe2e24bbc910666d361Steven M. Gava        self.set_status_bar()
2547aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        vbar['command'] = text.yview
2557aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        vbar.pack(side=RIGHT, fill=Y)
2567aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text['yscrollcommand'] = vbar.set
257acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser        fontWeight = 'normal'
258acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser        if idleConf.GetOption('main', 'EditorWindow', 'font-bold', type='bool'):
259b1585417d1f7ae8984ca72b16fd5a21746949ae5Steven M. Gava            fontWeight='bold'
260acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser        text.config(font=(idleConf.GetOption('main', 'EditorWindow', 'font'),
261d8590ff209369f45a5ed417c79ce255a33f70a1dAndrew Svetlov                          idleConf.GetOption('main', 'EditorWindow',
262d8590ff209369f45a5ed417c79ce255a33f70a1dAndrew Svetlov                                             'font-size', type='int'),
263acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser                          fontWeight))
2647aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text_frame.pack(side=LEFT, fill=BOTH, expand=1)
2657aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text.pack(side=TOP, fill=BOTH, expand=1)
2667aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text.focus_set()
2677aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
2686af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        # usetabs true  -> literal tab characters are used by indent and
2696af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        #                  dedent cmds, possibly mixed with spaces if
2706af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        #                  indentwidth is not a multiple of tabwidth,
2716af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        #                  which will cause Tabnanny to nag!
2726af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        #         false -> tab characters are converted to spaces by indent
2736af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        #                  and dedent cmds, and ditto TAB keystrokes
274acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser        # Although use-spaces=0 can be configured manually in config-main.def,
275acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser        # configuration of tabs v. spaces is not supported in the configuration
276acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser        # dialog.  IDLE promotes the preferred Python indentation: use spaces!
277acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser        usespaces = idleConf.GetOption('main', 'Indent', 'use-spaces', type='bool')
278acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser        self.usetabs = not usespaces
2796af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser
2806af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        # tabwidth is the display width of a literal tab character.
2816af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        # CAUTION:  telling Tk to use anything other than its default
2826af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        # tab setting causes it to use an entirely different tabbing algorithm,
2836af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        # treating tab stops as fixed distances from the left margin.
2846af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        # Nobody expects this, so for now tabwidth should never be changed.
285acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser        self.tabwidth = 8    # must remain 8 until Tk is fixed.
286acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser
287acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser        # indentwidth is the number of screen characters per indent level.
288acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser        # The recommended Python indentation is four spaces.
289acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser        self.indentwidth = self.tabwidth
290acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser        self.set_notabs_indentwidth()
2916af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser
2926af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        # If context_use_ps1 is true, parsing searches back for a ps1 line;
2936af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        # else searches for a popular (if, def, ...) Python stmt.
2946af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        self.context_use_ps1 = False
2956af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser
2966af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        # When searching backwards for a reliable place to begin parsing,
2976af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        # first start num_context_lines[0] lines back, then
2986af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        # num_context_lines[1] lines back if that didn't work, and so on.
2996af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        # The last value should be huge (larger than the # of lines in a
3006af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        # conceivable file).
3016af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        # Making the initial values larger slows things down more often.
3026af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        self.num_context_lines = 50, 500, 5000000
3036af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser
3047aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.per = per = self.Percolator(text)
305dc1e70987f49aa23bf1d07f32c476edeba0cec30Kurt B. Kaiser
306dc1e70987f49aa23bf1d07f32c476edeba0cec30Kurt B. Kaiser        self.undo = undo = self.UndoDelegator()
307dc1e70987f49aa23bf1d07f32c476edeba0cec30Kurt B. Kaiser        per.insertfilter(undo)
308dc1e70987f49aa23bf1d07f32c476edeba0cec30Kurt B. Kaiser        text.undo_block_start = undo.undo_block_start
309dc1e70987f49aa23bf1d07f32c476edeba0cec30Kurt B. Kaiser        text.undo_block_stop = undo.undo_block_stop
310dc1e70987f49aa23bf1d07f32c476edeba0cec30Kurt B. Kaiser        undo.set_saved_change_hook(self.saved_change_hook)
311dc1e70987f49aa23bf1d07f32c476edeba0cec30Kurt B. Kaiser
312dc1e70987f49aa23bf1d07f32c476edeba0cec30Kurt B. Kaiser        # IOBinding implements file I/O and printing functionality
3137aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.io = io = self.IOBinding(self)
314dc1e70987f49aa23bf1d07f32c476edeba0cec30Kurt B. Kaiser        io.set_filename_change_hook(self.filename_change_hook)
315dc1e70987f49aa23bf1d07f32c476edeba0cec30Kurt B. Kaiser
316cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        # Create the recent files submenu
317cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        self.recent_files_menu = Menu(self.menubar)
318cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        self.menudict['file'].insert_cascade(3, label='Recent Files',
319cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser                                             underline=0,
320cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser                                             menu=self.recent_files_menu)
321cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        self.update_recent_files_list()
3227aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
323f05fa33a6c42bc21ffdfe7a20226a2b9a83ac3c2Kurt B. Kaiser        self.color = None # initialized below in self.ResetColorizer
3247aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if filename:
325d2f4861a0b52a2af5ea3395267a5c56541352f8fKurt B. Kaiser            if os.path.exists(filename) and not os.path.isdir(filename):
3267aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                io.loadfile(filename)
3277aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            else:
3287aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                io.set_filename(filename)
329f05fa33a6c42bc21ffdfe7a20226a2b9a83ac3c2Kurt B. Kaiser        self.ResetColorizer()
3307aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.saved_change_hook()
3317aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
3326af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        self.set_indentation_params(self.ispythonsource(filename))
3336af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser
3347aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.load_extensions()
3357aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
3367aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        menu = self.menudict.get('windows')
3377aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if menu:
3387aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            end = menu.index("end")
3397aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            if end is None:
3407aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                end = -1
3417aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            if end >= 0:
3427aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                menu.add_separator()
3437aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                end = end + 1
3447aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.wmenu_end = end
3457aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            WindowList.register_callback(self.postwindowsmenu)
3467aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
3477aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        # Some abstractions so IDLE extensions are cross-IDE
3487aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.askyesno = tkMessageBox.askyesno
3497aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.askinteger = tkSimpleDialog.askinteger
3507aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.showerror = tkMessageBox.showerror
3517aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
35202c0ed06123d62dcf4c686e0f5bc48bac55676ccRoger Serwy        self._highlight_workaround()  # Fix selection tags on Windows
35302c0ed06123d62dcf4c686e0f5bc48bac55676ccRoger Serwy
35402c0ed06123d62dcf4c686e0f5bc48bac55676ccRoger Serwy    def _highlight_workaround(self):
35502c0ed06123d62dcf4c686e0f5bc48bac55676ccRoger Serwy        # On Windows, Tk removes painting of the selection
35602c0ed06123d62dcf4c686e0f5bc48bac55676ccRoger Serwy        # tags which is different behavior than on Linux and Mac.
35702c0ed06123d62dcf4c686e0f5bc48bac55676ccRoger Serwy        # See issue14146 for more information.
35802c0ed06123d62dcf4c686e0f5bc48bac55676ccRoger Serwy        if not sys.platform.startswith('win'):
35902c0ed06123d62dcf4c686e0f5bc48bac55676ccRoger Serwy            return
36002c0ed06123d62dcf4c686e0f5bc48bac55676ccRoger Serwy
36102c0ed06123d62dcf4c686e0f5bc48bac55676ccRoger Serwy        text = self.text
36202c0ed06123d62dcf4c686e0f5bc48bac55676ccRoger Serwy        text.event_add("<<Highlight-FocusOut>>", "<FocusOut>")
36302c0ed06123d62dcf4c686e0f5bc48bac55676ccRoger Serwy        text.event_add("<<Highlight-FocusIn>>", "<FocusIn>")
36402c0ed06123d62dcf4c686e0f5bc48bac55676ccRoger Serwy        def highlight_fix(focus):
36502c0ed06123d62dcf4c686e0f5bc48bac55676ccRoger Serwy            sel_range = text.tag_ranges("sel")
36602c0ed06123d62dcf4c686e0f5bc48bac55676ccRoger Serwy            if sel_range:
36702c0ed06123d62dcf4c686e0f5bc48bac55676ccRoger Serwy                if focus == 'out':
36802c0ed06123d62dcf4c686e0f5bc48bac55676ccRoger Serwy                    HILITE_CONFIG = idleConf.GetHighlight(
36902c0ed06123d62dcf4c686e0f5bc48bac55676ccRoger Serwy                            idleConf.CurrentTheme(), 'hilite')
37002c0ed06123d62dcf4c686e0f5bc48bac55676ccRoger Serwy                    text.tag_config("sel_fix", HILITE_CONFIG)
37102c0ed06123d62dcf4c686e0f5bc48bac55676ccRoger Serwy                    text.tag_raise("sel_fix")
37202c0ed06123d62dcf4c686e0f5bc48bac55676ccRoger Serwy                    text.tag_add("sel_fix", *sel_range)
37302c0ed06123d62dcf4c686e0f5bc48bac55676ccRoger Serwy                elif focus == 'in':
37402c0ed06123d62dcf4c686e0f5bc48bac55676ccRoger Serwy                    text.tag_remove("sel_fix", "1.0", "end")
37502c0ed06123d62dcf4c686e0f5bc48bac55676ccRoger Serwy
37602c0ed06123d62dcf4c686e0f5bc48bac55676ccRoger Serwy        text.bind("<<Highlight-FocusOut>>",
37702c0ed06123d62dcf4c686e0f5bc48bac55676ccRoger Serwy                lambda ev: highlight_fix("out"))
37802c0ed06123d62dcf4c686e0f5bc48bac55676ccRoger Serwy        text.bind("<<Highlight-FocusIn>>",
37902c0ed06123d62dcf4c686e0f5bc48bac55676ccRoger Serwy                lambda ev: highlight_fix("in"))
38002c0ed06123d62dcf4c686e0f5bc48bac55676ccRoger Serwy
38102c0ed06123d62dcf4c686e0f5bc48bac55676ccRoger Serwy
382307021f40b227d2bf114b536c37cc41e03fc2affMartin v. Löwis    def _filename_to_unicode(self, filename):
383307021f40b227d2bf114b536c37cc41e03fc2affMartin v. Löwis        """convert filename to unicode in order to display it in Tk"""
384307021f40b227d2bf114b536c37cc41e03fc2affMartin v. Löwis        if isinstance(filename, unicode) or not filename:
385307021f40b227d2bf114b536c37cc41e03fc2affMartin v. Löwis            return filename
386307021f40b227d2bf114b536c37cc41e03fc2affMartin v. Löwis        else:
387307021f40b227d2bf114b536c37cc41e03fc2affMartin v. Löwis            try:
388307021f40b227d2bf114b536c37cc41e03fc2affMartin v. Löwis                return filename.decode(self.filesystemencoding)
389307021f40b227d2bf114b536c37cc41e03fc2affMartin v. Löwis            except UnicodeDecodeError:
390307021f40b227d2bf114b536c37cc41e03fc2affMartin v. Löwis                # XXX
391307021f40b227d2bf114b536c37cc41e03fc2affMartin v. Löwis                try:
392307021f40b227d2bf114b536c37cc41e03fc2affMartin v. Löwis                    return filename.decode(self.encoding)
393307021f40b227d2bf114b536c37cc41e03fc2affMartin v. Löwis                except UnicodeDecodeError:
394307021f40b227d2bf114b536c37cc41e03fc2affMartin v. Löwis                    # byte-to-byte conversion
395307021f40b227d2bf114b536c37cc41e03fc2affMartin v. Löwis                    return filename.decode('iso8859-1')
396307021f40b227d2bf114b536c37cc41e03fc2affMartin v. Löwis
397d2f4861a0b52a2af5ea3395267a5c56541352f8fKurt B. Kaiser    def new_callback(self, event):
398d2f4861a0b52a2af5ea3395267a5c56541352f8fKurt B. Kaiser        dirname, basename = self.io.defaultfilename()
399d2f4861a0b52a2af5ea3395267a5c56541352f8fKurt B. Kaiser        self.flist.new(dirname)
400d2f4861a0b52a2af5ea3395267a5c56541352f8fKurt B. Kaiser        return "break"
401d2f4861a0b52a2af5ea3395267a5c56541352f8fKurt B. Kaiser
40293cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser    def home_callback(self, event):
403020d3d985698aac55383eb1a309fd6d281f58a97Kurt B. Kaiser        if (event.state & 4) != 0 and event.keysym == "Home":
404020d3d985698aac55383eb1a309fd6d281f58a97Kurt B. Kaiser            # state&4==Control. If <Control-Home>, use the Tk binding.
405020d3d985698aac55383eb1a309fd6d281f58a97Kurt B. Kaiser            return
40693cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser        if self.text.index("iomark") and \
40793cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser           self.text.compare("iomark", "<=", "insert lineend") and \
40893cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser           self.text.compare("insert linestart", "<=", "iomark"):
4097548bce0ee08a7eb1c2e88ee88799e5982ca21e9Kurt B. Kaiser            # In Shell on input line, go to just after prompt
41093cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser            insertpt = int(self.text.index("iomark").split(".")[1])
41193cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser        else:
41293cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser            line = self.text.get("insert linestart", "insert lineend")
41393cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser            for insertpt in xrange(len(line)):
41493cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser                if line[insertpt] not in (' ','\t'):
41593cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser                    break
41693cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser            else:
41793cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser                insertpt=len(line)
41893cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser        lineat = int(self.text.index("insert").split('.')[1])
41993cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser        if insertpt == lineat:
42093cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser            insertpt = 0
42193cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser        dest = "insert linestart+"+str(insertpt)+"c"
42293cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser        if (event.state&1) == 0:
4237548bce0ee08a7eb1c2e88ee88799e5982ca21e9Kurt B. Kaiser            # shift was not pressed
42493cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser            self.text.tag_remove("sel", "1.0", "end")
42593cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser        else:
42693cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser            if not self.text.index("sel.first"):
4277548bce0ee08a7eb1c2e88ee88799e5982ca21e9Kurt B. Kaiser                self.text.mark_set("my_anchor", "insert")  # there was no previous selection
4287548bce0ee08a7eb1c2e88ee88799e5982ca21e9Kurt B. Kaiser            else:
4297548bce0ee08a7eb1c2e88ee88799e5982ca21e9Kurt B. Kaiser                if self.text.compare(self.text.index("sel.first"), "<", self.text.index("insert")):
4307548bce0ee08a7eb1c2e88ee88799e5982ca21e9Kurt B. Kaiser                    self.text.mark_set("my_anchor", "sel.first") # extend back
4317548bce0ee08a7eb1c2e88ee88799e5982ca21e9Kurt B. Kaiser                else:
4327548bce0ee08a7eb1c2e88ee88799e5982ca21e9Kurt B. Kaiser                    self.text.mark_set("my_anchor", "sel.last") # extend forward
43393cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser            first = self.text.index(dest)
4347548bce0ee08a7eb1c2e88ee88799e5982ca21e9Kurt B. Kaiser            last = self.text.index("my_anchor")
43593cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser            if self.text.compare(first,">",last):
43693cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser                first,last = last,first
43793cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser            self.text.tag_remove("sel", "1.0", "end")
43893cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser            self.text.tag_add("sel", first, last)
43993cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser        self.text.mark_set("insert", dest)
44093cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser        self.text.see("insert")
44193cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser        return "break"
44293cdae5f814292da17c38374d0cfa314b96fd3beKurt B. Kaiser
4437aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def set_status_bar(self):
444898a365c276951ab2471afe2e24bbc910666d361Steven M. Gava        self.status_bar = self.MultiStatusBar(self.top)
44557847df4e59015c141e645b47112829cf41b582aNed Deily        if sys.platform == "darwin":
44619302d927e6688e02553df16177e4867e2d0e3b3Ronald Oussoren            # Insert some padding to avoid obscuring some of the statusbar
44719302d927e6688e02553df16177e4867e2d0e3b3Ronald Oussoren            # by the resize widget.
44819302d927e6688e02553df16177e4867e2d0e3b3Ronald Oussoren            self.status_bar.set_label('_padding1', '    ', side=RIGHT)
4497aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.status_bar.set_label('column', 'Col: ?', side=RIGHT)
4507aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.status_bar.set_label('line', 'Ln: ?', side=RIGHT)
4517aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.status_bar.pack(side=BOTTOM, fill=X)
452b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser        self.text.bind("<<set-line-and-column>>", self.set_line_and_column)
453b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser        self.text.event_add("<<set-line-and-column>>",
454b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser                            "<KeyRelease>", "<ButtonRelease>")
4557aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.text.after_idle(self.set_line_and_column)
4567aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
4577aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def set_line_and_column(self, event=None):
458220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser        line, column = self.text.index(INSERT).split('.')
4597aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.status_bar.set_label('column', 'Col: %s' % column)
4607aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.status_bar.set_label('line', 'Ln: %s' % line)
4617aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
4627aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    menu_specs = [
4637aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        ("file", "_File"),
4647aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        ("edit", "_Edit"),
4657aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        ("format", "F_ormat"),
4667aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        ("run", "_Run"),
4671061e7270b13a0e632c5a28791a46e38b48a39a0Kurt B. Kaiser        ("options", "_Options"),
4687aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        ("windows", "_Windows"),
4697aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        ("help", "_Help"),
4707aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    ]
4717aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
47257847df4e59015c141e645b47112829cf41b582aNed Deily    if sys.platform == "darwin":
47319302d927e6688e02553df16177e4867e2d0e3b3Ronald Oussoren        menu_specs[-2] = ("windows", "_Window")
47419302d927e6688e02553df16177e4867e2d0e3b3Ronald Oussoren
47519302d927e6688e02553df16177e4867e2d0e3b3Ronald Oussoren
4767aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def createmenubar(self):
4777aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        mbar = self.menubar
4787aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.menudict = menudict = {}
4797aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        for name, label in self.menu_specs:
4807aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            underline, label = prepstr(label)
4817aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            menudict[name] = menu = Menu(mbar, name=name)
4827aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            mbar.add_cascade(label=label, menu=menu, underline=underline)
48319302d927e6688e02553df16177e4867e2d0e3b3Ronald Oussoren
48457847df4e59015c141e645b47112829cf41b582aNed Deily        if macosxSupport.isCarbonTk():
48519302d927e6688e02553df16177e4867e2d0e3b3Ronald Oussoren            # Insert the application menu
48619302d927e6688e02553df16177e4867e2d0e3b3Ronald Oussoren            menudict['application'] = menu = Menu(mbar, name='apple')
48719302d927e6688e02553df16177e4867e2d0e3b3Ronald Oussoren            mbar.add_cascade(label='IDLE', menu=menu)
48819302d927e6688e02553df16177e4867e2d0e3b3Ronald Oussoren
4897aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.fill_menus()
4908e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser        self.base_helpmenu_length = self.menudict['help'].index(END)
4918e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser        self.reset_help_menu_entries()
4927aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
4937aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def postwindowsmenu(self):
4947aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        # Only called when Windows menu exists
4957aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        menu = self.menudict['windows']
4967aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        end = menu.index("end")
4977aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if end is None:
4987aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            end = -1
4997aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if end > self.wmenu_end:
5007aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            menu.delete(self.wmenu_end+1, end)
5017aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        WindowList.add_windows_to_menu(menu)
5027aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
5037aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    rmenu = None
5047aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
5057aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def right_menu_event(self, event):
5067aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.text.mark_set("insert", "@%d,%d" % (event.x, event.y))
5077aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if not self.rmenu:
5087aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.make_rmenu()
5097aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        rmenu = self.rmenu
5107aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.event = event
5117aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        iswin = sys.platform[:3] == 'win'
5127aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if iswin:
5137aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.text.config(cursor="arrow")
5145018db76aa433430e1bdb0826086df1c025a1f70Andrew Svetlov
515231a8fd22dcb60b2090e05029a536df73ef44373Roger Serwy        for item in self.rmenu_specs:
516231a8fd22dcb60b2090e05029a536df73ef44373Roger Serwy            try:
517231a8fd22dcb60b2090e05029a536df73ef44373Roger Serwy                label, eventname, verify_state = item
518231a8fd22dcb60b2090e05029a536df73ef44373Roger Serwy            except ValueError: # see issue1207589
519231a8fd22dcb60b2090e05029a536df73ef44373Roger Serwy                continue
520231a8fd22dcb60b2090e05029a536df73ef44373Roger Serwy
5215018db76aa433430e1bdb0826086df1c025a1f70Andrew Svetlov            if verify_state is None:
5225018db76aa433430e1bdb0826086df1c025a1f70Andrew Svetlov                continue
5235018db76aa433430e1bdb0826086df1c025a1f70Andrew Svetlov            state = getattr(self, verify_state)()
5245018db76aa433430e1bdb0826086df1c025a1f70Andrew Svetlov            rmenu.entryconfigure(label, state=state)
5255018db76aa433430e1bdb0826086df1c025a1f70Andrew Svetlov
5267aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        rmenu.tk_popup(event.x_root, event.y_root)
5277aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if iswin:
5287aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.text.config(cursor="ibeam")
5297aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
5307aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    rmenu_specs = [
5315018db76aa433430e1bdb0826086df1c025a1f70Andrew Svetlov        # ("Label", "<<virtual-event>>", "statefuncname"), ...
5325018db76aa433430e1bdb0826086df1c025a1f70Andrew Svetlov        ("Close", "<<close-window>>", None), # Example
5337aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    ]
5347aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
5357aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def make_rmenu(self):
5367aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        rmenu = Menu(self.text, tearoff=0)
537231a8fd22dcb60b2090e05029a536df73ef44373Roger Serwy        for item in self.rmenu_specs:
538231a8fd22dcb60b2090e05029a536df73ef44373Roger Serwy            label, eventname = item[0], item[1]
5395018db76aa433430e1bdb0826086df1c025a1f70Andrew Svetlov            if label is not None:
5405018db76aa433430e1bdb0826086df1c025a1f70Andrew Svetlov                def command(text=self.text, eventname=eventname):
5415018db76aa433430e1bdb0826086df1c025a1f70Andrew Svetlov                    text.event_generate(eventname)
5425018db76aa433430e1bdb0826086df1c025a1f70Andrew Svetlov                rmenu.add_command(label=label, command=command)
5435018db76aa433430e1bdb0826086df1c025a1f70Andrew Svetlov            else:
5445018db76aa433430e1bdb0826086df1c025a1f70Andrew Svetlov                rmenu.add_separator()
5457aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.rmenu = rmenu
5467aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
5475018db76aa433430e1bdb0826086df1c025a1f70Andrew Svetlov    def rmenu_check_cut(self):
5485018db76aa433430e1bdb0826086df1c025a1f70Andrew Svetlov        return self.rmenu_check_copy()
5495018db76aa433430e1bdb0826086df1c025a1f70Andrew Svetlov
5505018db76aa433430e1bdb0826086df1c025a1f70Andrew Svetlov    def rmenu_check_copy(self):
5515018db76aa433430e1bdb0826086df1c025a1f70Andrew Svetlov        try:
5525018db76aa433430e1bdb0826086df1c025a1f70Andrew Svetlov            indx = self.text.index('sel.first')
5535018db76aa433430e1bdb0826086df1c025a1f70Andrew Svetlov        except TclError:
5545018db76aa433430e1bdb0826086df1c025a1f70Andrew Svetlov            return 'disabled'
5555018db76aa433430e1bdb0826086df1c025a1f70Andrew Svetlov        else:
5565018db76aa433430e1bdb0826086df1c025a1f70Andrew Svetlov            return 'normal' if indx else 'disabled'
5575018db76aa433430e1bdb0826086df1c025a1f70Andrew Svetlov
5585018db76aa433430e1bdb0826086df1c025a1f70Andrew Svetlov    def rmenu_check_paste(self):
5595018db76aa433430e1bdb0826086df1c025a1f70Andrew Svetlov        try:
5605018db76aa433430e1bdb0826086df1c025a1f70Andrew Svetlov            self.text.tk.call('tk::GetSelection', self.text, 'CLIPBOARD')
5615018db76aa433430e1bdb0826086df1c025a1f70Andrew Svetlov        except TclError:
5625018db76aa433430e1bdb0826086df1c025a1f70Andrew Svetlov            return 'disabled'
5635018db76aa433430e1bdb0826086df1c025a1f70Andrew Svetlov        else:
5645018db76aa433430e1bdb0826086df1c025a1f70Andrew Svetlov            return 'normal'
5655018db76aa433430e1bdb0826086df1c025a1f70Andrew Svetlov
5667aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def about_dialog(self, event=None):
567d78b23025c9c9f6288e14112411d83f44bcf85a8Kurt B. Kaiser        aboutDialog.AboutDialog(self.top,'About IDLE')
5686655e4bc2752f1114a2e1f9a63ffd4191fa50d0dKurt B. Kaiser
5693b55a891a197212b34b0c077f72cb3af752468ecSteven M. Gava    def config_dialog(self, event=None):
5703b55a891a197212b34b0c077f72cb3af752468ecSteven M. Gava        configDialog.ConfigDialog(self.top,'Settings')
5716655e4bc2752f1114a2e1f9a63ffd4191fa50d0dKurt B. Kaiser
5727aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def help_dialog(self, event=None):
573adb87e2677caad4cd167e4915763dda105bc982cTerry Jan Reedy        if self.root:
574adb87e2677caad4cd167e4915763dda105bc982cTerry Jan Reedy            parent = self.root
575adb87e2677caad4cd167e4915763dda105bc982cTerry Jan Reedy        else:
576adb87e2677caad4cd167e4915763dda105bc982cTerry Jan Reedy            parent = self.top
577adb87e2677caad4cd167e4915763dda105bc982cTerry Jan Reedy        helpDialog.display(parent, near=self.top)
5786655e4bc2752f1114a2e1f9a63ffd4191fa50d0dKurt B. Kaiser
579114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser    def python_docs(self, event=None):
5808aa23927e339ca13459dd408751546352d81fefaKurt B. Kaiser        if sys.platform[:3] == 'win':
581247327a27e622a344787464fea404dd73bc71c50Terry Reedy            try:
582247327a27e622a344787464fea404dd73bc71c50Terry Reedy                os.startfile(self.help_url)
583247327a27e622a344787464fea404dd73bc71c50Terry Reedy            except WindowsError as why:
584247327a27e622a344787464fea404dd73bc71c50Terry Reedy                tkMessageBox.showerror(title='Document Start Failure',
585247327a27e622a344787464fea404dd73bc71c50Terry Reedy                    message=str(why), parent=self.text)
586114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser        else:
587114713d19451e893dc7dabea9b96d396baf7ecafKurt B. Kaiser            webbrowser.open(self.help_url)
5888aa23927e339ca13459dd408751546352d81fefaKurt B. Kaiser        return "break"
5897aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
59084f4803f4f7ccf40cd777d1e9627528c30bc9053Kurt B. Kaiser    def cut(self,event):
59184f4803f4f7ccf40cd777d1e9627528c30bc9053Kurt B. Kaiser        self.text.event_generate("<<Cut>>")
59284f4803f4f7ccf40cd777d1e9627528c30bc9053Kurt B. Kaiser        return "break"
59384f4803f4f7ccf40cd777d1e9627528c30bc9053Kurt B. Kaiser
59484f4803f4f7ccf40cd777d1e9627528c30bc9053Kurt B. Kaiser    def copy(self,event):
595b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser        if not self.text.tag_ranges("sel"):
596b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser            # There is no selection, so do nothing and maybe interrupt.
597b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser            return
59884f4803f4f7ccf40cd777d1e9627528c30bc9053Kurt B. Kaiser        self.text.event_generate("<<Copy>>")
59984f4803f4f7ccf40cd777d1e9627528c30bc9053Kurt B. Kaiser        return "break"
60084f4803f4f7ccf40cd777d1e9627528c30bc9053Kurt B. Kaiser
60184f4803f4f7ccf40cd777d1e9627528c30bc9053Kurt B. Kaiser    def paste(self,event):
60284f4803f4f7ccf40cd777d1e9627528c30bc9053Kurt B. Kaiser        self.text.event_generate("<<Paste>>")
603631fee62351397e940e4616ef48f03788962c3ebKurt B. Kaiser        self.text.see("insert")
60484f4803f4f7ccf40cd777d1e9627528c30bc9053Kurt B. Kaiser        return "break"
60584f4803f4f7ccf40cd777d1e9627528c30bc9053Kurt B. Kaiser
6067aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def select_all(self, event=None):
6077aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.text.tag_add("sel", "1.0", "end-1c")
6087aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.text.mark_set("insert", "1.0")
6097aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.text.see("insert")
6107aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        return "break"
6117aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
6127aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def remove_selection(self, event=None):
6137aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.text.tag_remove("sel", "1.0", "end")
6147aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.text.see("insert")
6157aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
6165ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser    def move_at_edge_if_selection(self, edge_index):
6175ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser        """Cursor move begins at start or end of selection
6185ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser
6195ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser        When a left/right cursor key is pressed create and return to Tkinter a
6205ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser        function which causes a cursor move from the associated edge of the
6215ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser        selection.
6225ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser
6235ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser        """
6245ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser        self_text_index = self.text.index
6255ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser        self_text_mark_set = self.text.mark_set
6265ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser        edges_table = ("sel.first+1c", "sel.last-1c")
6275ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser        def move_at_edge(event):
6285ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser            if (event.state & 5) == 0: # no shift(==1) or control(==4) pressed
6295ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser                try:
6305ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser                    self_text_index("sel.first")
6315ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser                    self_text_mark_set("insert", edges_table[edge_index])
6325ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser                except TclError:
6335ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser                    pass
6345ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser        return move_at_edge
6355ec186b1cbf06cf66262882fb5b6f54a893e48a5Kurt B. Kaiser
6363069dbb8ec9c287c2ff6b3a1c8bfde83af81c11bKurt B. Kaiser    def del_word_left(self, event):
6373069dbb8ec9c287c2ff6b3a1c8bfde83af81c11bKurt B. Kaiser        self.text.event_generate('<Meta-Delete>')
6383069dbb8ec9c287c2ff6b3a1c8bfde83af81c11bKurt B. Kaiser        return "break"
6393069dbb8ec9c287c2ff6b3a1c8bfde83af81c11bKurt B. Kaiser
6403069dbb8ec9c287c2ff6b3a1c8bfde83af81c11bKurt B. Kaiser    def del_word_right(self, event):
6413069dbb8ec9c287c2ff6b3a1c8bfde83af81c11bKurt B. Kaiser        self.text.event_generate('<Meta-d>')
6423069dbb8ec9c287c2ff6b3a1c8bfde83af81c11bKurt B. Kaiser        return "break"
6433069dbb8ec9c287c2ff6b3a1c8bfde83af81c11bKurt B. Kaiser
644c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava    def find_event(self, event):
645c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        SearchDialog.find(self.text)
646c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        return "break"
647c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava
648c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava    def find_again_event(self, event):
649c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        SearchDialog.find_again(self.text)
650c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        return "break"
651c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava
652c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava    def find_selection_event(self, event):
653c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        SearchDialog.find_selection(self.text)
654c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        return "break"
655c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava
656c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava    def find_in_files_event(self, event):
657c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        GrepDialog.grep(self.text, self.io, self.flist)
658c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        return "break"
659c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava
660c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava    def replace_event(self, event):
661c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        ReplaceDialog.replace(self.text)
662c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        return "break"
663c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava
664c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava    def goto_line_event(self, event):
665c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        text = self.text
666c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        lineno = tkSimpleDialog.askinteger("Goto",
667c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava                "Go to line number:",parent=text)
668c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        if lineno is None:
669c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava            return "break"
670c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        if lineno <= 0:
671c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava            text.bell()
672c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava            return "break"
673c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        text.mark_set("insert", "%d.0" % lineno)
674c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava        text.see("insert")
675c597640515a9ca3aa1807cc633b8d7bf2aa4a4e6Steven M. Gava
6767aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def open_module(self, event=None):
6777aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        # XXX Shouldn't this be in IOBinding or in FileList?
6787aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        try:
6797aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            name = self.text.get("sel.first", "sel.last")
6807aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        except TclError:
6817aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            name = ""
6827aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        else:
683220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser            name = name.strip()
684852f35bbeb23d898fb0d16489effb1197ee3fe02Guido van Rossum        name = tkSimpleDialog.askstring("Module",
685852f35bbeb23d898fb0d16489effb1197ee3fe02Guido van Rossum                 "Enter the name of a Python module\n"
686852f35bbeb23d898fb0d16489effb1197ee3fe02Guido van Rossum                 "to search on sys.path and open:",
687852f35bbeb23d898fb0d16489effb1197ee3fe02Guido van Rossum                 parent=self.text, initialvalue=name)
688852f35bbeb23d898fb0d16489effb1197ee3fe02Guido van Rossum        if name:
689852f35bbeb23d898fb0d16489effb1197ee3fe02Guido van Rossum            name = name.strip()
6907aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if not name:
691852f35bbeb23d898fb0d16489effb1197ee3fe02Guido van Rossum            return
6927aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        # XXX Ought to insert current file's directory in front of path
6937aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        try:
694220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser            (f, file, (suffix, mode, type)) = _find_module(name)
6952b149860a052606500cc4293db29cde5c85bf288Terry Jan Reedy        except (NameError, ImportError) as msg:
6967aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            tkMessageBox.showerror("Import error", str(msg), parent=self.text)
6977aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return
6987aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if type != imp.PY_SOURCE:
6997aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            tkMessageBox.showerror("Unsupported type",
7007aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                "%s is not a source module" % name, parent=self.text)
7017aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return
7027aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if f:
7037aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            f.close()
7047aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if self.flist:
7057aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.flist.open(file)
7067aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        else:
7077aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.io.loadfile(file)
7087aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
7097aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def open_class_browser(self, event=None):
7107aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        filename = self.io.filename
7117aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if not filename:
7127aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            tkMessageBox.showerror(
7137aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                "No filename",
7147aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                "This buffer has no associated filename",
7157aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                master=self.text)
7167aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.text.focus_set()
7177aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return None
7187aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        head, tail = os.path.split(filename)
7197aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        base, ext = os.path.splitext(tail)
720d630c04ab1ab35e2ec6eeeaba9bdcb9f8e730e78Florent Xicluna        from idlelib import ClassBrowser
7217aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        ClassBrowser.ClassBrowser(self.flist, base, [head])
7227aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
7237aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def open_path_browser(self, event=None):
724d630c04ab1ab35e2ec6eeeaba9bdcb9f8e730e78Florent Xicluna        from idlelib import PathBrowser
7257aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        PathBrowser.PathBrowser(self.flist)
7267aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
7277aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def gotoline(self, lineno):
7287aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if lineno is not None and lineno > 0:
7297aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.text.mark_set("insert", "%d.0" % lineno)
7307aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.text.tag_remove("sel", "1.0", "end")
7317aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.text.tag_add("sel", "insert", "insert +1l")
7327aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.center()
7337aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
7347aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def ispythonsource(self, filename):
735df506ea98b1c9424634c6063e90a664ac9127164Kurt B. Kaiser        if not filename or os.path.isdir(filename):
736220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser            return True
7377aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        base, ext = os.path.splitext(os.path.basename(filename))
7387aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if os.path.normcase(ext) in (".py", ".pyw"):
739220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser            return True
7407aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        try:
7417aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            f = open(filename)
7427aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            line = f.readline()
7437aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            f.close()
7447aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        except IOError:
745220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser            return False
74683a3560527f9e62bce6adbc12b8ff6b57be8b1acKurt B. Kaiser        return line.startswith('#!') and line.find('python') >= 0
7477aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
7487aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def close_hook(self):
7497aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if self.flist:
7500b634efcbcd10d9364796e9b048f18cd4a08b88bKurt B. Kaiser            self.flist.unregister_maybe_terminate(self)
7510b634efcbcd10d9364796e9b048f18cd4a08b88bKurt B. Kaiser            self.flist = None
7527aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
7537aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def set_close_hook(self, close_hook):
7547aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.close_hook = close_hook
7557aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
7567aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def filename_change_hook(self):
7577aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if self.flist:
7587aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.flist.filename_changed_edit(self)
7597aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.saved_change_hook()
760260cb9034c861fa159f26fba8679ac265af47109Kurt B. Kaiser        self.top.update_windowlist_registry(self)
761f05fa33a6c42bc21ffdfe7a20226a2b9a83ac3c2Kurt B. Kaiser        self.ResetColorizer()
7627aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
763f05fa33a6c42bc21ffdfe7a20226a2b9a83ac3c2Kurt B. Kaiser    def _addcolorizer(self):
7647aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if self.color:
7657aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return
766f05fa33a6c42bc21ffdfe7a20226a2b9a83ac3c2Kurt B. Kaiser        if self.ispythonsource(self.io.filename):
767f05fa33a6c42bc21ffdfe7a20226a2b9a83ac3c2Kurt B. Kaiser            self.color = self.ColorDelegator()
768f05fa33a6c42bc21ffdfe7a20226a2b9a83ac3c2Kurt B. Kaiser        # can add more colorizers here...
769f05fa33a6c42bc21ffdfe7a20226a2b9a83ac3c2Kurt B. Kaiser        if self.color:
770f05fa33a6c42bc21ffdfe7a20226a2b9a83ac3c2Kurt B. Kaiser            self.per.removefilter(self.undo)
771f05fa33a6c42bc21ffdfe7a20226a2b9a83ac3c2Kurt B. Kaiser            self.per.insertfilter(self.color)
772f05fa33a6c42bc21ffdfe7a20226a2b9a83ac3c2Kurt B. Kaiser            self.per.insertfilter(self.undo)
7737aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
774f05fa33a6c42bc21ffdfe7a20226a2b9a83ac3c2Kurt B. Kaiser    def _rmcolorizer(self):
7757aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if not self.color:
7767aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return
777df506ea98b1c9424634c6063e90a664ac9127164Kurt B. Kaiser        self.color.removecolors()
7787aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.per.removefilter(self.color)
7797aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.color = None
7806655e4bc2752f1114a2e1f9a63ffd4191fa50d0dKurt B. Kaiser
781b77d343bc846c2049a4cffb1dfd65eb49d1728b4Steven M. Gava    def ResetColorizer(self):
782f05fa33a6c42bc21ffdfe7a20226a2b9a83ac3c2Kurt B. Kaiser        "Update the colour theme"
783f05fa33a6c42bc21ffdfe7a20226a2b9a83ac3c2Kurt B. Kaiser        # Called from self.filename_change_hook and from configDialog.py
784f05fa33a6c42bc21ffdfe7a20226a2b9a83ac3c2Kurt B. Kaiser        self._rmcolorizer()
785f05fa33a6c42bc21ffdfe7a20226a2b9a83ac3c2Kurt B. Kaiser        self._addcolorizer()
78673360a3e61274ffcc4c9fc3d09746bd6603e92a5Kurt B. Kaiser        theme = idleConf.GetOption('main','Theme','name')
787f05fa33a6c42bc21ffdfe7a20226a2b9a83ac3c2Kurt B. Kaiser        normal_colors = idleConf.GetHighlight(theme, 'normal')
788f05fa33a6c42bc21ffdfe7a20226a2b9a83ac3c2Kurt B. Kaiser        cursor_color = idleConf.GetHighlight(theme, 'cursor', fgBg='fg')
789f05fa33a6c42bc21ffdfe7a20226a2b9a83ac3c2Kurt B. Kaiser        select_colors = idleConf.GetHighlight(theme, 'hilite')
790f05fa33a6c42bc21ffdfe7a20226a2b9a83ac3c2Kurt B. Kaiser        self.text.config(
791f05fa33a6c42bc21ffdfe7a20226a2b9a83ac3c2Kurt B. Kaiser            foreground=normal_colors['foreground'],
792f05fa33a6c42bc21ffdfe7a20226a2b9a83ac3c2Kurt B. Kaiser            background=normal_colors['background'],
793f05fa33a6c42bc21ffdfe7a20226a2b9a83ac3c2Kurt B. Kaiser            insertbackground=cursor_color,
794f05fa33a6c42bc21ffdfe7a20226a2b9a83ac3c2Kurt B. Kaiser            selectforeground=select_colors['foreground'],
795f05fa33a6c42bc21ffdfe7a20226a2b9a83ac3c2Kurt B. Kaiser            selectbackground=select_colors['background'],
796f05fa33a6c42bc21ffdfe7a20226a2b9a83ac3c2Kurt B. Kaiser            )
7977aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
798b1585417d1f7ae8984ca72b16fd5a21746949ae5Steven M. Gava    def ResetFont(self):
7996655e4bc2752f1114a2e1f9a63ffd4191fa50d0dKurt B. Kaiser        "Update the text widgets' font if it is changed"
80083118c6cb36cf9a424bec1b9a2ef8c8760bae8f5Kurt B. Kaiser        # Called from configDialog.py
801b1585417d1f7ae8984ca72b16fd5a21746949ae5Steven M. Gava        fontWeight='normal'
802b1585417d1f7ae8984ca72b16fd5a21746949ae5Steven M. Gava        if idleConf.GetOption('main','EditorWindow','font-bold',type='bool'):
803b1585417d1f7ae8984ca72b16fd5a21746949ae5Steven M. Gava            fontWeight='bold'
804b1585417d1f7ae8984ca72b16fd5a21746949ae5Steven M. Gava        self.text.config(font=(idleConf.GetOption('main','EditorWindow','font'),
805d8590ff209369f45a5ed417c79ce255a33f70a1dAndrew Svetlov                idleConf.GetOption('main','EditorWindow','font-size',
806d8590ff209369f45a5ed417c79ce255a33f70a1dAndrew Svetlov                                   type='int'),
807b1585417d1f7ae8984ca72b16fd5a21746949ae5Steven M. Gava                fontWeight))
808b1585417d1f7ae8984ca72b16fd5a21746949ae5Steven M. Gava
809b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser    def RemoveKeybindings(self):
810b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser        "Remove the keybindings before they are changed."
81183118c6cb36cf9a424bec1b9a2ef8c8760bae8f5Kurt B. Kaiser        # Called from configDialog.py
8125a67f9b815fdd34617395c6ec5d9a679361b76eaKurt B. Kaiser        self.Bindings.default_keydefs = keydefs = idleConf.GetCurrentKeySet()
813dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava        for event, keylist in keydefs.items():
814b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser            self.text.event_delete(event, *keylist)
815b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser        for extensionName in self.get_standard_extension_names():
8165a67f9b815fdd34617395c6ec5d9a679361b76eaKurt B. Kaiser            xkeydefs = idleConf.GetExtensionBindings(extensionName)
8175a67f9b815fdd34617395c6ec5d9a679361b76eaKurt B. Kaiser            if xkeydefs:
8185a67f9b815fdd34617395c6ec5d9a679361b76eaKurt B. Kaiser                for event, keylist in xkeydefs.items():
819b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser                    self.text.event_delete(event, *keylist)
820b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser
821b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser    def ApplyKeybindings(self):
822b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser        "Update the keybindings after they are changed"
823b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser        # Called from configDialog.py
8245a67f9b815fdd34617395c6ec5d9a679361b76eaKurt B. Kaiser        self.Bindings.default_keydefs = keydefs = idleConf.GetCurrentKeySet()
825dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava        self.apply_bindings()
826b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser        for extensionName in self.get_standard_extension_names():
8275a67f9b815fdd34617395c6ec5d9a679361b76eaKurt B. Kaiser            xkeydefs = idleConf.GetExtensionBindings(extensionName)
8285a67f9b815fdd34617395c6ec5d9a679361b76eaKurt B. Kaiser            if xkeydefs:
8295a67f9b815fdd34617395c6ec5d9a679361b76eaKurt B. Kaiser                self.apply_bindings(xkeydefs)
830dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava        #update menu accelerators
8315a67f9b815fdd34617395c6ec5d9a679361b76eaKurt B. Kaiser        menuEventDict = {}
832dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava        for menu in self.Bindings.menudefs:
8335a67f9b815fdd34617395c6ec5d9a679361b76eaKurt B. Kaiser            menuEventDict[menu[0]] = {}
834dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava            for item in menu[1]:
835dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava                if item:
8365a67f9b815fdd34617395c6ec5d9a679361b76eaKurt B. Kaiser                    menuEventDict[menu[0]][prepstr(item[0])[1]] = item[1]
837dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava        for menubarItem in self.menudict.keys():
8385a67f9b815fdd34617395c6ec5d9a679361b76eaKurt B. Kaiser            menu = self.menudict[menubarItem]
83914ef0c8d6bb1794910e815fb3c61d22cc966a49eNed Deily            end = menu.index(END)
84014ef0c8d6bb1794910e815fb3c61d22cc966a49eNed Deily            if end is None:
84114ef0c8d6bb1794910e815fb3c61d22cc966a49eNed Deily                # Skip empty menus
84214ef0c8d6bb1794910e815fb3c61d22cc966a49eNed Deily                continue
84314ef0c8d6bb1794910e815fb3c61d22cc966a49eNed Deily            end += 1
8445a67f9b815fdd34617395c6ec5d9a679361b76eaKurt B. Kaiser            for index in range(0, end):
8455a67f9b815fdd34617395c6ec5d9a679361b76eaKurt B. Kaiser                if menu.type(index) == 'command':
8465a67f9b815fdd34617395c6ec5d9a679361b76eaKurt B. Kaiser                    accel = menu.entrycget(index, 'accelerator')
847dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava                    if accel:
8485a67f9b815fdd34617395c6ec5d9a679361b76eaKurt B. Kaiser                        itemName = menu.entrycget(index, 'label')
8495a67f9b815fdd34617395c6ec5d9a679361b76eaKurt B. Kaiser                        event = ''
8506e3dbbdf39f3b4eb6f18c0165e446df17218b7dcBenjamin Peterson                        if menubarItem in menuEventDict:
8516e3dbbdf39f3b4eb6f18c0165e446df17218b7dcBenjamin Peterson                            if itemName in menuEventDict[menubarItem]:
8525a67f9b815fdd34617395c6ec5d9a679361b76eaKurt B. Kaiser                                event = menuEventDict[menubarItem][itemName]
853dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava                        if event:
8545a67f9b815fdd34617395c6ec5d9a679361b76eaKurt B. Kaiser                            accel = get_accelerator(keydefs, event)
8555a67f9b815fdd34617395c6ec5d9a679361b76eaKurt B. Kaiser                            menu.entryconfig(index, accelerator=accel)
856dbfe92cd27f08d38cc8a2882c147c4289b6b5db1Steven M. Gava
857acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser    def set_notabs_indentwidth(self):
858acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser        "Update the indentwidth if changed and not using tabs in this window"
859acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser        # Called from configDialog.py
860acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser        if not self.usetabs:
861acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser            self.indentwidth = idleConf.GetOption('main', 'Indent','num-spaces',
862acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser                                                  type='int')
863acdef858a51f32d2b2c82f3d47025cb26c2a63eeKurt B. Kaiser
8648e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser    def reset_help_menu_entries(self):
8658e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser        "Update the additional help entries on the Help menu"
8668e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser        help_list = idleConf.GetAllExtraHelpSourcesList()
8678e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser        helpmenu = self.menudict['help']
8688e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser        # first delete the extra help entries, if any
8698e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser        helpmenu_length = helpmenu.index(END)
8708e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser        if helpmenu_length > self.base_helpmenu_length:
8718e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser            helpmenu.delete((self.base_helpmenu_length + 1), helpmenu_length)
8728e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser        # then rebuild them
8738e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser        if help_list:
8748e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser            helpmenu.add_separator()
8758e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser            for entry in help_list:
8768e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser                cmd = self.__extra_help_callback(entry[1])
8778e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser                helpmenu.add_command(label=entry[0], command=cmd)
8788e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser        # and update the menu dictionary
8798e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser        self.menudict['help'] = helpmenu
8808e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser
8818e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser    def __extra_help_callback(self, helpfile):
8828e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser        "Create a callback with the helpfile value frozen at definition time"
8838e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser        def display_extra_help(helpfile=helpfile):
884b2afe855e5d75a570707d6bf0e32206e4b7b1c4dGeorg Brandl            if not helpfile.startswith(('www', 'http')):
885247327a27e622a344787464fea404dd73bc71c50Terry Reedy                helpfile = os.path.normpath(helpfile)
8868aa23927e339ca13459dd408751546352d81fefaKurt B. Kaiser            if sys.platform[:3] == 'win':
887247327a27e622a344787464fea404dd73bc71c50Terry Reedy                try:
888247327a27e622a344787464fea404dd73bc71c50Terry Reedy                    os.startfile(helpfile)
889247327a27e622a344787464fea404dd73bc71c50Terry Reedy                except WindowsError as why:
890247327a27e622a344787464fea404dd73bc71c50Terry Reedy                    tkMessageBox.showerror(title='Document Start Failure',
891247327a27e622a344787464fea404dd73bc71c50Terry Reedy                        message=str(why), parent=self.text)
8928aa23927e339ca13459dd408751546352d81fefaKurt B. Kaiser            else:
8938aa23927e339ca13459dd408751546352d81fefaKurt B. Kaiser                webbrowser.open(helpfile)
8948e92bf7699ac3617d3a0a9a55a38f642f5a6ecc6Kurt B. Kaiser        return display_extra_help
8956655e4bc2752f1114a2e1f9a63ffd4191fa50d0dKurt B. Kaiser
896cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser    def update_recent_files_list(self, new_file=None):
897cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        "Load and update the recent files list and menus"
898cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        rf_list = []
899cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        if os.path.exists(self.recent_files_path):
900f9489436041b319ffab52eb1769d11849d68129dTerry Jan Reedy            with  open(self.recent_files_path, 'r') as rf_list_file:
901cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser                rf_list = rf_list_file.readlines()
902cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        if new_file:
903cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser            new_file = os.path.abspath(new_file) + '\n'
904cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser            if new_file in rf_list:
905cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser                rf_list.remove(new_file)  # move to top
906cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser            rf_list.insert(0, new_file)
907cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        # clean and save the recent files list
908cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        bad_paths = []
909cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        for path in rf_list:
910cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser            if '\0' in path or not os.path.exists(path[0:-1]):
911cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser                bad_paths.append(path)
912cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        rf_list = [path for path in rf_list if path not in bad_paths]
913cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        ulchars = "1234567890ABCDEFGHIJK"
914cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        rf_list = rf_list[0:len(ulchars)]
9151d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava        try:
91640ad04171d7f3773ed29a3ff13a1d58eefab57c2Ned Deily            with open(self.recent_files_path, 'w') as rf_file:
91740ad04171d7f3773ed29a3ff13a1d58eefab57c2Ned Deily                rf_file.writelines(rf_list)
91840ad04171d7f3773ed29a3ff13a1d58eefab57c2Ned Deily        except IOError as err:
91940ad04171d7f3773ed29a3ff13a1d58eefab57c2Ned Deily            if not getattr(self.root, "recentfilelist_error_displayed", False):
92040ad04171d7f3773ed29a3ff13a1d58eefab57c2Ned Deily                self.root.recentfilelist_error_displayed = True
92140ad04171d7f3773ed29a3ff13a1d58eefab57c2Ned Deily                tkMessageBox.showerror(title='IDLE Error',
92240ad04171d7f3773ed29a3ff13a1d58eefab57c2Ned Deily                    message='Unable to update Recent Files list:\n%s'
92340ad04171d7f3773ed29a3ff13a1d58eefab57c2Ned Deily                        % str(err),
92440ad04171d7f3773ed29a3ff13a1d58eefab57c2Ned Deily                    parent=self.text)
925cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        # for each edit window instance, construct the recent files menu
926cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        for instance in self.top.instance_dict.keys():
927cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser            menu = instance.recent_files_menu
9287148984d61b5677af1ee090e52c1cba08299c9f9Ned Deily            menu.delete(0, END)  # clear, and rebuild:
92986b882f3a6978cf359dfedcf1193bd930f024780Guilherme Polo            for i, file_name in enumerate(rf_list):
93086b882f3a6978cf359dfedcf1193bd930f024780Guilherme Polo                file_name = file_name.rstrip()  # zap \n
931307021f40b227d2bf114b536c37cc41e03fc2affMartin v. Löwis                # make unicode string to display non-ASCII chars correctly
932307021f40b227d2bf114b536c37cc41e03fc2affMartin v. Löwis                ufile_name = self._filename_to_unicode(file_name)
933cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser                callback = instance.__recent_file_callback(file_name)
934307021f40b227d2bf114b536c37cc41e03fc2affMartin v. Löwis                menu.add_command(label=ulchars[i] + " " + ufile_name,
935cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser                                 command=callback,
936cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser                                 underline=0)
937cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser
938cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser    def __recent_file_callback(self, file_name):
939cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        def open_recent_file(fn_closure=file_name):
940cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser            self.io.open(editFile=fn_closure)
941cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser        return open_recent_file
9426655e4bc2752f1114a2e1f9a63ffd4191fa50d0dKurt B. Kaiser
9437aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def saved_change_hook(self):
9447aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        short = self.short_title()
9457aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        long = self.long_title()
9467aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if short and long:
9477aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            title = short + " - " + long
9487aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        elif short:
9497aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            title = short
9507aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        elif long:
9517aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            title = long
9527aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        else:
9537aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            title = "Untitled"
9547aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        icon = short or long or title
9557aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if not self.get_saved():
9567aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            title = "*%s*" % title
9577aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            icon = "*%s" % icon
9587aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.top.wm_title(title)
9597aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.top.wm_iconname(icon)
9607aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
9617aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def get_saved(self):
9627aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        return self.undo.get_saved()
9637aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
9647aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def set_saved(self, flag):
9657aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.undo.set_saved(flag)
9667aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
9677aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def reset_undo(self):
9687aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.undo.reset_undo()
9697aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
9707aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def short_title(self):
971592436552816f23b585e0a8e25284eeb1d25a184Terry Jan Reedy        pyversion = "Python " + python_version() + ": "
9727aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        filename = self.io.filename
9737aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if filename:
9747aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            filename = os.path.basename(filename)
975592436552816f23b585e0a8e25284eeb1d25a184Terry Jan Reedy        else:
976592436552816f23b585e0a8e25284eeb1d25a184Terry Jan Reedy            filename = "Untitled"
977307021f40b227d2bf114b536c37cc41e03fc2affMartin v. Löwis        # return unicode string to display non-ASCII chars correctly
978592436552816f23b585e0a8e25284eeb1d25a184Terry Jan Reedy        return pyversion + self._filename_to_unicode(filename)
9797aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
9807aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def long_title(self):
981307021f40b227d2bf114b536c37cc41e03fc2affMartin v. Löwis        # return unicode string to display non-ASCII chars correctly
982307021f40b227d2bf114b536c37cc41e03fc2affMartin v. Löwis        return self._filename_to_unicode(self.io.filename or "")
9837aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
9847aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def center_insert_event(self, event):
9857aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.center()
9867aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
9877aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def center(self, mark="insert"):
9887aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text = self.text
9897aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        top, bot = self.getwindowlines()
9907aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        lineno = self.getlineno(mark)
9917aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        height = bot - top
992220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser        newtop = max(1, lineno - height//2)
9937aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text.yview(float(newtop))
9947aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
9957aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def getwindowlines(self):
9967aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text = self.text
9977aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        top = self.getlineno("@0,0")
9987aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        bot = self.getlineno("@0,65535")
9997aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if top == bot and text.winfo_height() == 1:
10007aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            # Geometry manager hasn't run yet
10017aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            height = int(text['height'])
10027aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            bot = top + height - 1
10037aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        return top, bot
10047aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
10057aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def getlineno(self, mark="insert"):
10067aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text = self.text
10077aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        return int(float(text.index(mark)))
10087aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
10091061e7270b13a0e632c5a28791a46e38b48a39a0Kurt B. Kaiser    def get_geometry(self):
10101061e7270b13a0e632c5a28791a46e38b48a39a0Kurt B. Kaiser        "Return (width, height, x, y)"
10111061e7270b13a0e632c5a28791a46e38b48a39a0Kurt B. Kaiser        geom = self.top.wm_geometry()
10121061e7270b13a0e632c5a28791a46e38b48a39a0Kurt B. Kaiser        m = re.match(r"(\d+)x(\d+)\+(-?\d+)\+(-?\d+)", geom)
10131061e7270b13a0e632c5a28791a46e38b48a39a0Kurt B. Kaiser        tuple = (map(int, m.groups()))
10141061e7270b13a0e632c5a28791a46e38b48a39a0Kurt B. Kaiser        return tuple
10151061e7270b13a0e632c5a28791a46e38b48a39a0Kurt B. Kaiser
10167aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def close_event(self, event):
10177aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.close()
10187aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
10197aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def maybesave(self):
10207aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if self.io:
102167716b5f53715e57d147cde9539b8d76a5a56e11Steven M. Gava            if not self.get_saved():
10226655e4bc2752f1114a2e1f9a63ffd4191fa50d0dKurt B. Kaiser                if self.top.state()!='normal':
102367716b5f53715e57d147cde9539b8d76a5a56e11Steven M. Gava                    self.top.deiconify()
102467716b5f53715e57d147cde9539b8d76a5a56e11Steven M. Gava                self.top.lower()
102567716b5f53715e57d147cde9539b8d76a5a56e11Steven M. Gava                self.top.lift()
10267aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return self.io.maybesave()
10277aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
10287aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def close(self):
10297aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        reply = self.maybesave()
1030a398e2d0592464b6594bac0e4ee3ef091cce5159Matthias Klose        if str(reply) != "cancel":
10317aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self._close()
10327aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        return reply
10337aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
10347aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def _close(self):
10351d46e40f58b21dd6c30e21a841d65e9bcbc899b1Steven M. Gava        if self.io.filename:
1036cf6f1b69eb5f491dd3cba6c5c90bcb344d4b3a96Kurt B. Kaiser            self.update_recent_files_list(new_file=self.io.filename)
10377aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        WindowList.unregister_callback(self.postwindowsmenu)
10387aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.unload_extensions()
10390b634efcbcd10d9364796e9b048f18cd4a08b88bKurt B. Kaiser        self.io.close()
10400b634efcbcd10d9364796e9b048f18cd4a08b88bKurt B. Kaiser        self.io = None
10410b634efcbcd10d9364796e9b048f18cd4a08b88bKurt B. Kaiser        self.undo = None
10427aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if self.color:
10430b634efcbcd10d9364796e9b048f18cd4a08b88bKurt B. Kaiser            self.color.close(False)
10440b634efcbcd10d9364796e9b048f18cd4a08b88bKurt B. Kaiser            self.color = None
10457aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.text = None
1046610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser        self.tkinter_vars = None
10470b634efcbcd10d9364796e9b048f18cd4a08b88bKurt B. Kaiser        self.per.close()
10480b634efcbcd10d9364796e9b048f18cd4a08b88bKurt B. Kaiser        self.per = None
10490b634efcbcd10d9364796e9b048f18cd4a08b88bKurt B. Kaiser        self.top.destroy()
10500b634efcbcd10d9364796e9b048f18cd4a08b88bKurt B. Kaiser        if self.close_hook:
10510b634efcbcd10d9364796e9b048f18cd4a08b88bKurt B. Kaiser            # unless override: unregister from flist, terminate if last window
10520b634efcbcd10d9364796e9b048f18cd4a08b88bKurt B. Kaiser            self.close_hook()
10537aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
10547aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def load_extensions(self):
10557aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.extensions = {}
10567aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.load_standard_extensions()
10577aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
10587aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def unload_extensions(self):
10597aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        for ins in self.extensions.values():
10607aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            if hasattr(ins, "close"):
10617aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                ins.close()
10627aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.extensions = {}
10637aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
10647aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def load_standard_extensions(self):
10657aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        for name in self.get_standard_extension_names():
10667aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            try:
10677aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                self.load_extension(name)
10687aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            except:
106970a6b49821a3226f55e9716f32d802d06640cb89Walter Dörwald                print "Failed to load extension", repr(name)
10707aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                import traceback
10717aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                traceback.print_exc()
10727aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
10737aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def get_standard_extension_names(self):
10744d5bc6031ca883201f87e0e3c94e5746f9f91439Kurt B. Kaiser        return idleConf.GetExtensions(editor_only=True)
10757aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
10767aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def load_extension(self, name):
1077b00e89faab0d69feb040118bbc5ea6149e91867fKurt B. Kaiser        try:
1078b00e89faab0d69feb040118bbc5ea6149e91867fKurt B. Kaiser            mod = __import__(name, globals(), locals(), [])
1079b00e89faab0d69feb040118bbc5ea6149e91867fKurt B. Kaiser        except ImportError:
1080b00e89faab0d69feb040118bbc5ea6149e91867fKurt B. Kaiser            print "\nFailed to import extension: ", name
1081b00e89faab0d69feb040118bbc5ea6149e91867fKurt B. Kaiser            return
10827aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        cls = getattr(mod, name)
10834d5bc6031ca883201f87e0e3c94e5746f9f91439Kurt B. Kaiser        keydefs = idleConf.GetExtensionBindings(name)
10844d5bc6031ca883201f87e0e3c94e5746f9f91439Kurt B. Kaiser        if hasattr(cls, "menudefs"):
10854d5bc6031ca883201f87e0e3c94e5746f9f91439Kurt B. Kaiser            self.fill_menus(cls.menudefs, keydefs)
10867aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        ins = cls(self)
10877aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        self.extensions[name] = ins
10887aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if keydefs:
10897aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            self.apply_bindings(keydefs)
10907aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            for vevent in keydefs.keys():
1091220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser                methodname = vevent.replace("-", "_")
10927aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                while methodname[:1] == '<':
10937aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                    methodname = methodname[1:]
10947aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                while methodname[-1:] == '>':
10957aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                    methodname = methodname[:-1]
10967aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                methodname = methodname + "_event"
10977aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                if hasattr(ins, methodname):
10987aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                    self.text.bind(vevent, getattr(ins, methodname))
10997aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
11007aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def apply_bindings(self, keydefs=None):
11017aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if keydefs is None:
11027aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            keydefs = self.Bindings.default_keydefs
11037aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text = self.text
11047aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text.keydefs = keydefs
11057aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        for event, keylist in keydefs.items():
11067aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            if keylist:
1107931237e2e66975c54e2ac6c5e503ee2992a22bcfRaymond Hettinger                text.event_add(event, *keylist)
11087aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
1109610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser    def fill_menus(self, menudefs=None, keydefs=None):
111083118c6cb36cf9a424bec1b9a2ef8c8760bae8f5Kurt B. Kaiser        """Add appropriate entries to the menus and submenus
111183118c6cb36cf9a424bec1b9a2ef8c8760bae8f5Kurt B. Kaiser
111283118c6cb36cf9a424bec1b9a2ef8c8760bae8f5Kurt B. Kaiser        Menus that are absent or None in self.menudict are ignored.
111383118c6cb36cf9a424bec1b9a2ef8c8760bae8f5Kurt B. Kaiser        """
1114610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser        if menudefs is None:
1115610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser            menudefs = self.Bindings.menudefs
11167aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if keydefs is None:
11177aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            keydefs = self.Bindings.default_keydefs
11187aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        menudict = self.menudict
11197aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text = self.text
1120610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser        for mname, entrylist in menudefs:
11217aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            menu = menudict.get(mname)
11227aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            if not menu:
11237aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                continue
1124610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser            for entry in entrylist:
1125610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser                if not entry:
11267aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                    menu.add_separator()
11277aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                else:
1128610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser                    label, eventname = entry
11297aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                    checkbutton = (label[:1] == '!')
11307aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                    if checkbutton:
11317aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                        label = label[1:]
11327aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                    underline, label = prepstr(label)
1133610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser                    accelerator = get_accelerator(keydefs, eventname)
1134610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser                    def command(text=text, eventname=eventname):
1135610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser                        text.event_generate(eventname)
11367aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                    if checkbutton:
1137610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser                        var = self.get_var_obj(eventname, BooleanVar)
11387aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                        menu.add_checkbutton(label=label, underline=underline,
11397aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                            command=command, accelerator=accelerator,
11407aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                            variable=var)
11417aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                    else:
11427aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                        menu.add_command(label=label, underline=underline,
114384f4803f4f7ccf40cd777d1e9627528c30bc9053Kurt B. Kaiser                                         command=command,
114484f4803f4f7ccf40cd777d1e9627528c30bc9053Kurt B. Kaiser                                         accelerator=accelerator)
11457aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
11467aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def getvar(self, name):
1147610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser        var = self.get_var_obj(name)
11487aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if var:
1149610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser            value = var.get()
1150610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser            return value
1151610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser        else:
1152610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser            raise NameError, name
11537aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
11547aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def setvar(self, name, value, vartype=None):
1155610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser        var = self.get_var_obj(name, vartype)
11567aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if var:
11577aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            var.set(value)
1158610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser        else:
1159610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser            raise NameError, name
11607aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
1161610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser    def get_var_obj(self, name, vartype=None):
1162610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser        var = self.tkinter_vars.get(name)
11637aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if not var and vartype:
1164610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser            # create a Tkinter variable object with self.text as master:
1165610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser            self.tkinter_vars[name] = var = vartype(self.text)
11667aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        return var
11677aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
11687aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # Tk implementations of "virtual text methods" -- each platform
11697aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # reusing IDLE's support code needs to define these for its GUI's
11707aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # flavor of widget.
11717aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
11727aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # Is character at text_index in a Python string?  Return 0 for
11737aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # "guaranteed no", true for anything else.  This info is expensive
11747aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # to compute ab initio, but is probably already known by the
11757aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # platform's colorizer.
11767aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
11777aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def is_char_in_string(self, text_index):
11787aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if self.color:
11797aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            # Return true iff colorizer hasn't (re)gotten this far
11807aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            # yet, or the character is tagged as being in a string
11817aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return self.text.tag_prevrange("TODO", text_index) or \
11827aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                   "STRING" in self.text.tag_names(text_index)
11837aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        else:
11847aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            # The colorizer is missing: assume the worst
11857aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return 1
11867aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
11877aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # If a selection is defined in the text widget, return (start,
11887aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # end) as Tkinter text indices, otherwise return (None, None)
11897aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def get_selection_indices(self):
11907aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        try:
11917aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            first = self.text.index("sel.first")
11927aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            last = self.text.index("sel.last")
11937aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return first, last
11947aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        except TclError:
11957aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            return None, None
11967aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
11977aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # Return the text widget's current view of what a tab stop means
11987aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # (equivalent width in spaces).
11997aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
12007aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def get_tabwidth(self):
12017aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        current = self.text['tabs'] or TK_TABWIDTH_DEFAULT
12027aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        return int(current)
12037aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
12047aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # Set the text widget's current view of what a tab stop means.
12057aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
12067aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    def set_tabwidth(self, newtabwidth):
12077aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        text = self.text
12087aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        if self.get_tabwidth() != newtabwidth:
12097aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            pixels = text.tk.call("font", "measure", text["font"],
12107aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer                                  "-displayof", text.master,
1211afdf71b9eefdfb5904e40602984836b28318bad0Kurt B. Kaiser                                  "n" * newtabwidth)
12127aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer            text.configure(tabs=pixels)
12137aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
1214cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # If ispythonsource and guess are true, guess a good value for
1215cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # indentwidth based on file content (if possible), and if
1216cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # indentwidth != tabwidth set usetabs false.
1217cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # In any case, adjust the Text widget's view of what a tab
1218cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # character means.
1219cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
12206af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser    def set_indentation_params(self, ispythonsource, guess=True):
1221cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if guess and ispythonsource:
1222cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            i = self.guess_indent()
1223cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if 2 <= i <= 8:
1224cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                self.indentwidth = i
1225cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if self.indentwidth != self.tabwidth:
12266af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser                self.usetabs = False
1227cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        self.set_tabwidth(self.tabwidth)
1228cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1229cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def smart_backspace_event(self, event):
1230cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text = self.text
1231cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        first, last = self.get_selection_indices()
1232cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if first and last:
1233cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            text.delete(first, last)
1234cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            text.mark_set("insert", first)
1235cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            return "break"
1236cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        # Delete whitespace left, until hitting a real char or closest
1237cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        # preceding virtual tab stop.
1238cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        chars = text.get("insert linestart", "insert")
1239cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if chars == '':
1240cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if text.compare("insert", ">", "1.0"):
1241cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                # easy: delete preceding newline
1242cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                text.delete("insert-1c")
1243cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            else:
1244cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                text.bell()     # at start of buffer
1245cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            return "break"
1246cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if  chars[-1] not in " \t":
1247cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            # easy: delete preceding real char
1248cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            text.delete("insert-1c")
1249cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            return "break"
1250cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        # Ick.  It may require *inserting* spaces if we back up over a
1251cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        # tab character!  This is written to be clear, not fast.
12521b3c26998e0f8b6975f7bbccf043c0afe398f151Kurt B. Kaiser        tabwidth = self.tabwidth
12531b3c26998e0f8b6975f7bbccf043c0afe398f151Kurt B. Kaiser        have = len(chars.expandtabs(tabwidth))
1254cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        assert have > 0
1255cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        want = ((have - 1) // self.indentwidth) * self.indentwidth
12564ada7ad3bc9ab269189ed20e57943d9721664debKurt B. Kaiser        # Debug prompt is multilined....
12578ef4a70a48daa451fca6d59a6129f49389e5b0feTerry Jan Reedy        if self.context_use_ps1:
12588ef4a70a48daa451fca6d59a6129f49389e5b0feTerry Jan Reedy            last_line_of_prompt = sys.ps1.split('\n')[-1]
12598ef4a70a48daa451fca6d59a6129f49389e5b0feTerry Jan Reedy        else:
12608ef4a70a48daa451fca6d59a6129f49389e5b0feTerry Jan Reedy            last_line_of_prompt = ''
1261cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        ncharsdeleted = 0
1262cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        while 1:
12634ada7ad3bc9ab269189ed20e57943d9721664debKurt B. Kaiser            if chars == last_line_of_prompt:
12641bdca5e051a5424aaaaf7968c710d31132a6c335Kurt B. Kaiser                break
1265cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            chars = chars[:-1]
1266cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            ncharsdeleted = ncharsdeleted + 1
12671b3c26998e0f8b6975f7bbccf043c0afe398f151Kurt B. Kaiser            have = len(chars.expandtabs(tabwidth))
1268cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if have <= want or chars[-1] not in " \t":
1269cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                break
1270cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.undo_block_start()
1271cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.delete("insert-%dc" % ncharsdeleted, "insert")
1272cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if have < want:
1273cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            text.insert("insert", ' ' * (want - have))
1274cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.undo_block_stop()
1275cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        return "break"
1276cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1277cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def smart_indent_event(self, event):
1278cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        # if intraline selection:
1279cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        #     delete it
1280cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        # elif multiline selection:
12816af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        #     do indent-region
12826af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        # else:
12836af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        #     indent one level
1284cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text = self.text
1285cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        first, last = self.get_selection_indices()
1286cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.undo_block_start()
1287cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        try:
1288cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if first and last:
1289cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                if index2line(first) != index2line(last):
1290cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    return self.indent_region_event(event)
1291cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                text.delete(first, last)
1292cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                text.mark_set("insert", first)
1293cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            prefix = text.get("insert linestart", "insert")
1294cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            raw, effective = classifyws(prefix, self.tabwidth)
1295cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if raw == len(prefix):
1296cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                # only whitespace to the left
1297cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                self.reindent_to(effective + self.indentwidth)
1298cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            else:
12996af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser                # tab to the next 'stop' within or to right of line's text:
1300cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                if self.usetabs:
1301cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    pad = '\t'
1302cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                else:
13031b3c26998e0f8b6975f7bbccf043c0afe398f151Kurt B. Kaiser                    effective = len(prefix.expandtabs(self.tabwidth))
1304cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    n = self.indentwidth
1305cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    pad = ' ' * (n - effective % n)
1306cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                text.insert("insert", pad)
1307cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            text.see("insert")
1308cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            return "break"
1309cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        finally:
1310cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            text.undo_block_stop()
1311cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1312cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def newline_and_indent_event(self, event):
1313cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text = self.text
1314cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        first, last = self.get_selection_indices()
1315cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.undo_block_start()
1316cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        try:
1317cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if first and last:
1318cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                text.delete(first, last)
1319cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                text.mark_set("insert", first)
1320cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            line = text.get("insert linestart", "insert")
1321cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            i, n = 0, len(line)
1322cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            while i < n and line[i] in " \t":
1323cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                i = i+1
1324cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if i == n:
13254ada7ad3bc9ab269189ed20e57943d9721664debKurt B. Kaiser                # the cursor is in or at leading indentation in a continuation
13264ada7ad3bc9ab269189ed20e57943d9721664debKurt B. Kaiser                # line; just inject an empty line at the start
1327cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                text.insert("insert linestart", '\n')
1328cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                return "break"
1329cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            indent = line[:i]
13304ada7ad3bc9ab269189ed20e57943d9721664debKurt B. Kaiser            # strip whitespace before insert point unless it's in the prompt
1331cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            i = 0
13324ada7ad3bc9ab269189ed20e57943d9721664debKurt B. Kaiser            last_line_of_prompt = sys.ps1.split('\n')[-1]
13334ada7ad3bc9ab269189ed20e57943d9721664debKurt B. Kaiser            while line and line[-1] in " \t" and line != last_line_of_prompt:
1334cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                line = line[:-1]
1335cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                i = i+1
1336cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if i:
1337cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                text.delete("insert - %d chars" % i, "insert")
1338cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            # strip whitespace after insert point
1339cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            while text.get("insert") in " \t":
1340cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                text.delete("insert")
1341cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            # start new line
1342cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            text.insert("insert", '\n')
1343cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1344cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            # adjust indentation for continuations and block
1345cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            # open/close first need to find the last stmt
1346cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            lno = index2line(text.index('insert'))
1347cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            y = PyParse.Parser(self.indentwidth, self.tabwidth)
1348b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser            if not self.context_use_ps1:
1349b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser                for context in self.num_context_lines:
1350b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser                    startat = max(lno - context, 1)
1351dfd3618422bc19ae48047e74c6320746ee6b610eFlorent Xicluna                    startatindex = repr(startat) + ".0"
1352b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser                    rawtext = text.get(startatindex, "insert")
1353b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser                    y.set_str(rawtext)
1354b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser                    bod = y.find_good_parse_start(
1355b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser                              self.context_use_ps1,
1356b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser                              self._build_char_in_string_func(startatindex))
1357b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser                    if bod is not None or startat == 1:
1358b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser                        break
1359b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser                y.set_lo(bod or 0)
1360b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser            else:
1361b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser                r = text.tag_prevrange("console", "insert")
1362b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser                if r:
1363b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser                    startatindex = r[1]
1364b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser                else:
1365b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser                    startatindex = "1.0"
1366cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                rawtext = text.get(startatindex, "insert")
1367cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                y.set_str(rawtext)
1368b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser                y.set_lo(0)
1369b17544551fc8dfd1304d5679c6e444cad4d34d97Kurt B. Kaiser
1370cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            c = y.get_continuation_type()
1371cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if c != PyParse.C_NONE:
1372cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                # The current stmt hasn't ended yet.
1373b61602c96821997884e7de08d56404904baa034bKurt B. Kaiser                if c == PyParse.C_STRING_FIRST_LINE:
1374b61602c96821997884e7de08d56404904baa034bKurt B. Kaiser                    # after the first line of a string; do not indent at all
1375b61602c96821997884e7de08d56404904baa034bKurt B. Kaiser                    pass
1376b61602c96821997884e7de08d56404904baa034bKurt B. Kaiser                elif c == PyParse.C_STRING_NEXT_LINES:
1377b61602c96821997884e7de08d56404904baa034bKurt B. Kaiser                    # inside a string which started before this line;
1378b61602c96821997884e7de08d56404904baa034bKurt B. Kaiser                    # just mimic the current indent
1379cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    text.insert("insert", indent)
1380cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                elif c == PyParse.C_BRACKET:
1381cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    # line up with the first (if any) element of the
1382cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    # last open bracket structure; else indent one
1383cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    # level beyond the indent of the line with the
1384cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    # last open bracket
1385cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    self.reindent_to(y.compute_bracket_indent())
1386cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                elif c == PyParse.C_BACKSLASH:
1387cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    # if more than one line in this stmt already, just
1388cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    # mimic the current indent; else if initial line
1389cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    # has a start on an assignment stmt, indent to
1390cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    # beyond leftmost =; else to beyond first chunk of
1391cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    # non-whitespace on initial line
1392cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    if y.get_num_lines_in_stmt() > 1:
1393cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                        text.insert("insert", indent)
1394cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                    else:
1395cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                        self.reindent_to(y.compute_backslash_indent())
1396cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                else:
139770a6b49821a3226f55e9716f32d802d06640cb89Walter Dörwald                    assert 0, "bogus continuation type %r" % (c,)
1398cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                return "break"
1399cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1400cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            # This line starts a brand new stmt; indent relative to
1401cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            # indentation of initial line of closest preceding
1402cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            # interesting stmt.
1403cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            indent = y.get_base_indent_string()
1404cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            text.insert("insert", indent)
1405cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if y.is_block_opener():
1406cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                self.smart_indent_event(event)
1407cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            elif indent and y.is_block_closer():
1408cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                self.smart_backspace_event(event)
1409cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            return "break"
1410cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        finally:
1411cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            text.see("insert")
1412cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            text.undo_block_stop()
1413cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1414cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # Our editwin provides a is_char_in_string function that works
1415cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # with a Tk text index, but PyParse only knows about offsets into
1416cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # a string. This builds a function for PyParse that accepts an
1417cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # offset.
1418cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1419cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def _build_char_in_string_func(self, startindex):
1420cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        def inner(offset, _startindex=startindex,
1421cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                  _icis=self.is_char_in_string):
1422cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            return _icis(_startindex + "+%dc" % offset)
1423cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        return inner
1424cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1425cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def indent_region_event(self, event):
1426cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        head, tail, chars, lines = self.get_region()
1427cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        for pos in range(len(lines)):
1428cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            line = lines[pos]
1429cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if line:
1430cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                raw, effective = classifyws(line, self.tabwidth)
1431cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                effective = effective + self.indentwidth
1432cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                lines[pos] = self._make_blanks(effective) + line[raw:]
1433cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        self.set_region(head, tail, chars, lines)
1434cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        return "break"
1435cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1436cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def dedent_region_event(self, event):
1437cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        head, tail, chars, lines = self.get_region()
1438cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        for pos in range(len(lines)):
1439cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            line = lines[pos]
1440cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if line:
1441cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                raw, effective = classifyws(line, self.tabwidth)
1442cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                effective = max(effective - self.indentwidth, 0)
1443cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                lines[pos] = self._make_blanks(effective) + line[raw:]
1444cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        self.set_region(head, tail, chars, lines)
1445cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        return "break"
1446cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1447cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def comment_region_event(self, event):
1448cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        head, tail, chars, lines = self.get_region()
1449cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        for pos in range(len(lines) - 1):
1450cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            line = lines[pos]
1451cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            lines[pos] = '##' + line
1452cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        self.set_region(head, tail, chars, lines)
1453cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1454cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def uncomment_region_event(self, event):
1455cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        head, tail, chars, lines = self.get_region()
1456cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        for pos in range(len(lines)):
1457cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            line = lines[pos]
1458cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if not line:
1459cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                continue
1460cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if line[:2] == '##':
1461cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                line = line[2:]
1462cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            elif line[:1] == '#':
1463cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                line = line[1:]
1464cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            lines[pos] = line
1465cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        self.set_region(head, tail, chars, lines)
1466cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1467cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def tabify_region_event(self, event):
1468cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        head, tail, chars, lines = self.get_region()
1469cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        tabwidth = self._asktabwidth()
147075b249c914561166602bb42fe748b6e00211cc91Roger Serwy        if tabwidth is None: return
1471cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        for pos in range(len(lines)):
1472cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            line = lines[pos]
1473cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            if line:
1474cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                raw, effective = classifyws(line, tabwidth)
1475cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                ntabs, nspaces = divmod(effective, tabwidth)
1476cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                lines[pos] = '\t' * ntabs + ' ' * nspaces + line[raw:]
1477cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        self.set_region(head, tail, chars, lines)
1478cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1479cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def untabify_region_event(self, event):
1480cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        head, tail, chars, lines = self.get_region()
1481cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        tabwidth = self._asktabwidth()
148275b249c914561166602bb42fe748b6e00211cc91Roger Serwy        if tabwidth is None: return
1483cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        for pos in range(len(lines)):
14841b3c26998e0f8b6975f7bbccf043c0afe398f151Kurt B. Kaiser            lines[pos] = lines[pos].expandtabs(tabwidth)
1485cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        self.set_region(head, tail, chars, lines)
1486cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1487cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def toggle_tabs_event(self, event):
1488cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if self.askyesno(
1489cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser              "Toggle tabs",
14906af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser              "Turn tabs " + ("on", "off")[self.usetabs] +
14916af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser              "?\nIndent width " +
149253f2b5fab2f525342dbb69f6f2d5eed978c7883fKurt B. Kaiser              ("will be", "remains at")[self.usetabs] + " 8." +
149353f2b5fab2f525342dbb69f6f2d5eed978c7883fKurt B. Kaiser              "\n Note: a tab is always 8 columns",
1494cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser              parent=self.text):
1495cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            self.usetabs = not self.usetabs
149653f2b5fab2f525342dbb69f6f2d5eed978c7883fKurt B. Kaiser            # Try to prevent inconsistent indentation.
149753f2b5fab2f525342dbb69f6f2d5eed978c7883fKurt B. Kaiser            # User must change indent width manually after using tabs.
149853f2b5fab2f525342dbb69f6f2d5eed978c7883fKurt B. Kaiser            self.indentwidth = 8
1499cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        return "break"
1500cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
15016af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser    # XXX this isn't bound to anything -- see tabwidth comments
15026af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser##     def change_tabwidth_event(self, event):
15036af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser##         new = self._asktabwidth()
15046af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser##         if new != self.tabwidth:
15056af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser##             self.tabwidth = new
15066af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser##             self.set_indentation_params(0, guess=0)
15076af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser##         return "break"
1508cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1509cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def change_indentwidth_event(self, event):
1510cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        new = self.askinteger(
1511cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                  "Indent width",
15126af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser                  "New indent width (2-16)\n(Always use 8 when using tabs)",
1513cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                  parent=self.text,
1514cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                  initialvalue=self.indentwidth,
1515cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                  minvalue=2,
1516cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                  maxvalue=16)
15176af44986029c84c4c5df62a64c60a6ed978a3693Kurt B. Kaiser        if new and new != self.indentwidth and not self.usetabs:
1518cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            self.indentwidth = new
1519cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        return "break"
1520cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1521cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def get_region(self):
1522cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text = self.text
1523cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        first, last = self.get_selection_indices()
1524cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if first and last:
1525cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            head = text.index(first + " linestart")
1526cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            tail = text.index(last + "-1c lineend +1c")
1527cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        else:
1528cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            head = text.index("insert linestart")
1529cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            tail = text.index("insert lineend +1c")
1530cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        chars = text.get(head, tail)
15311b3c26998e0f8b6975f7bbccf043c0afe398f151Kurt B. Kaiser        lines = chars.split("\n")
1532cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        return head, tail, chars, lines
1533cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1534cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def set_region(self, head, tail, chars, lines):
1535cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text = self.text
15361b3c26998e0f8b6975f7bbccf043c0afe398f151Kurt B. Kaiser        newchars = "\n".join(lines)
1537cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if newchars == chars:
1538cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            text.bell()
1539cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            return
1540cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.tag_remove("sel", "1.0", "end")
1541cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.mark_set("insert", head)
1542cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.undo_block_start()
1543cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.delete(head, tail)
1544cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.insert(head, newchars)
1545cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.undo_block_stop()
1546cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.tag_add("sel", head, "insert")
1547cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1548cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # Make string that displays as n leading blanks.
1549cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1550cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def _make_blanks(self, n):
1551cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if self.usetabs:
1552cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            ntabs, nspaces = divmod(n, self.tabwidth)
1553cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            return '\t' * ntabs + ' ' * nspaces
1554cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        else:
1555cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            return ' ' * n
1556cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1557cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # Delete from beginning of line to insert point, then reinsert
1558cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # column logical (meaning use tabs if appropriate) spaces.
1559cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1560cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def reindent_to(self, column):
1561cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text = self.text
1562cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.undo_block_start()
1563cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if text.compare("insert linestart", "!=", "insert"):
1564cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            text.delete("insert linestart", "insert")
1565cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if column:
1566cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            text.insert("insert", self._make_blanks(column))
1567cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        text.undo_block_stop()
1568cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1569cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def _asktabwidth(self):
1570cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        return self.askinteger(
1571cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            "Tab width",
1572ca7329c9c124080b817ceb67b4e3ee7963b81d41Kurt B. Kaiser            "Columns per tab? (2-16)",
1573cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            parent=self.text,
1574cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            initialvalue=self.indentwidth,
1575cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            minvalue=2,
157675b249c914561166602bb42fe748b6e00211cc91Roger Serwy            maxvalue=16)
1577cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1578cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # Guess indentwidth from text content.
1579cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # Return guessed indentwidth.  This should not be believed unless
1580cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # it's in a reasonable range (e.g., it will be 0 if no indented
1581cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # blocks are found).
1582cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1583cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def guess_indent(self):
1584cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        opener, indented = IndentSearcher(self.text, self.tabwidth).run()
1585cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if opener and indented:
1586cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            raw, indentsmall = classifyws(opener, self.tabwidth)
1587cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            raw, indentlarge = classifyws(indented, self.tabwidth)
1588cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        else:
1589cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            indentsmall = indentlarge = 0
1590cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        return indentlarge - indentsmall
1591cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1592cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser# "line.col" -> line, as an int
1593cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiserdef index2line(index):
1594cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    return int(float(index))
1595cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1596cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser# Look at the leading whitespace in s.
1597cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser# Return pair (# of leading ws characters,
1598cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser#              effective # of leading blanks after expanding
1599cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser#              tabs to width tabwidth)
1600cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1601cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiserdef classifyws(s, tabwidth):
1602cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    raw = effective = 0
1603cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    for ch in s:
1604cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if ch == ' ':
1605cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            raw = raw + 1
1606cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            effective = effective + 1
1607cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        elif ch == '\t':
1608cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            raw = raw + 1
1609cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            effective = (effective // tabwidth + 1) * tabwidth
1610cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        else:
1611cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            break
1612cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    return raw, effective
1613cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1614cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiserimport tokenize
1615cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser_tokenize = tokenize
1616cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiserdel tokenize
1617cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1618dcba6622f52efafa28104a07db9d5ba2b1a8d628Kurt B. Kaiserclass IndentSearcher(object):
1619cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1620cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # .run() chews over the Text widget, looking for a block opener
1621cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # and the stmt following it.  Returns a pair,
1622cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    #     (line containing block opener, line containing stmt)
1623cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    # Either or both may be None.
1624cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1625cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def __init__(self, text, tabwidth):
1626cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        self.text = text
1627cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        self.tabwidth = tabwidth
1628cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        self.i = self.finished = 0
1629cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        self.blkopenline = self.indentedline = None
1630cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1631cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def readline(self):
1632cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if self.finished:
1633cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            return ""
1634cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        i = self.i = self.i + 1
163570a6b49821a3226f55e9716f32d802d06640cb89Walter Dörwald        mark = repr(i) + ".0"
1636cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if self.text.compare(mark, ">=", "end"):
1637cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            return ""
1638cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        return self.text.get(mark, mark + " lineend+1c")
1639cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1640cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def tokeneater(self, type, token, start, end, line,
1641cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                   INDENT=_tokenize.INDENT,
1642cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                   NAME=_tokenize.NAME,
1643cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                   OPENERS=('class', 'def', 'for', 'if', 'try', 'while')):
1644cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        if self.finished:
1645cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            pass
1646cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        elif type == NAME and token in OPENERS:
1647cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            self.blkopenline = line
1648cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        elif type == INDENT and self.blkopenline:
1649cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            self.indentedline = line
1650cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            self.finished = 1
1651cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1652cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser    def run(self):
1653cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        save_tabsize = _tokenize.tabsize
1654cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        _tokenize.tabsize = self.tabwidth
1655cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        try:
1656cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            try:
1657cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                _tokenize.tokenize(self.readline, self.tokeneater)
165861006a211339f592dcb60f20ee7e478ad012556dSerhiy Storchaka            except (_tokenize.TokenError, SyntaxError):
1659cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                # since we cut off the tokenizer early, we can trigger
1660cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                # spurious errors
1661cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser                pass
1662cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        finally:
1663cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser            _tokenize.tabsize = save_tabsize
1664cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser        return self.blkopenline, self.indentedline
1665cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
1666cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser### end autoindent code ###
1667cb7a383bad7e7085f1bd6067865516aa9244cf23Kurt B. Kaiser
16687aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererdef prepstr(s):
16697aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # Helper to extract the underscore from a string, e.g.
16707aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # prepstr("Co_py") returns (2, "Copy").
1671220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser    i = s.find('_')
16727aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    if i >= 0:
16737aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        s = s[:i] + s[i+1:]
16747aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    return i, s
16757aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
16767aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
16777aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererkeynames = {
16787aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer 'bracketleft': '[',
16797aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer 'bracketright': ']',
16807aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer 'slash': '/',
16817aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer}
16827aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
1683610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiserdef get_accelerator(keydefs, eventname):
1684610c7e07f3e41e1f4baf7303edc8124269c01250Kurt B. Kaiser    keylist = keydefs.get(eventname)
168560651535c997bd3b55afb3b5718680ec2a404f69Ned Deily    # issue10940: temporary workaround to prevent hang with OS X Cocoa Tk 8.5
168660651535c997bd3b55afb3b5718680ec2a404f69Ned Deily    # if not keylist:
168757847df4e59015c141e645b47112829cf41b582aNed Deily    if (not keylist) or (macosxSupport.isCocoaTk() and eventname in {
168860651535c997bd3b55afb3b5718680ec2a404f69Ned Deily                            "<<open-module>>",
168960651535c997bd3b55afb3b5718680ec2a404f69Ned Deily                            "<<goto-line>>",
169060651535c997bd3b55afb3b5718680ec2a404f69Ned Deily                            "<<change-indentwidth>>"}):
16917aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        return ""
16927aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    s = keylist[0]
1693220ecbc731bf600fe977e1721f2f5c1aec7b033cKurt B. Kaiser    s = re.sub(r"-[a-z]\b", lambda m: m.group().upper(), s)
16947aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    s = re.sub(r"\b\w+\b", lambda m: keynames.get(m.group(), m.group()), s)
16957aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    s = re.sub("Key-", "", s)
16967aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    s = re.sub("Cancel","Ctrl-Break",s)   # dscherer@cmu.edu
16977aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    s = re.sub("Control-", "Ctrl-", s)
16987aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    s = re.sub("-", "+", s)
16997aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    s = re.sub("><", " ", s)
17007aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    s = re.sub("<", "", s)
17017aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    s = re.sub(">", "", s)
17027aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    return s
17037aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
17047aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
17057aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererdef fixwordbreaks(root):
17067aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # Make sure that Tk's double-click and next/previous word
17077aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    # operations use our definition of a word (i.e. an identifier)
17087aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    tk = root.tk
17097aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    tk.call('tcl_wordBreakAfter', 'a b', 0) # make sure word.tcl is loaded
17107aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    tk.call('set', 'tcl_wordchars', '[a-zA-Z0-9_]')
17117aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    tk.call('set', 'tcl_nonwordchars', '[^a-zA-Z0-9_]')
17127aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
17137aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
171400b0bd55b4d615740e06ac4d445221a176da30f3Terry Jan Reedydef _Editor_window(parent):
171500b0bd55b4d615740e06ac4d445221a176da30f3Terry Jan Reedy    root = parent
17167aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    fixwordbreaks(root)
17177aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    root.withdraw()
17187aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    if sys.argv[1:]:
17197aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        filename = sys.argv[1]
17207aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    else:
17217aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer        filename = None
172200b0bd55b4d615740e06ac4d445221a176da30f3Terry Jan Reedy    macosxSupport.setupApp(root, None)
17237aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    edit = EditorWindow(root=root, filename=filename)
17247aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer    edit.set_close_hook(root.quit)
17250b634efcbcd10d9364796e9b048f18cd4a08b88bKurt B. Kaiser    edit.text.bind("<<close-all-windows>>", edit.close_event)
17267aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Scherer
17277aced17437a6b05bc4b0b5ff93aa6a5d3a374d68David Schererif __name__ == '__main__':
172800b0bd55b4d615740e06ac4d445221a176da30f3Terry Jan Reedy    from idlelib.idle_test.htest import run
172900b0bd55b4d615740e06ac4d445221a176da30f3Terry Jan Reedy    if len(sys.argv) <= 1:
173000b0bd55b4d615740e06ac4d445221a176da30f3Terry Jan Reedy        run(_Help_dialog)
173100b0bd55b4d615740e06ac4d445221a176da30f3Terry Jan Reedy    run(_Editor_window)
1732