blob: 74a4749551f571751c207e92512a959e182dd191 [file] [log] [blame]
Brett Cannonaef82d32012-04-14 20:44:23 -04001import importlib
Brett Cannon50793b42013-06-07 13:17:48 -04002import importlib.abc
Eric Snow6029e082014-01-25 15:32:46 -07003import importlib.util
David Scherer7aced172000-08-15 01:13:23 +00004import os
Terry Jan Reedy0726ddf2014-08-14 21:54:43 -04005import platform
David Scherer7aced172000-08-15 01:13:23 +00006import re
Guido van Rossum33d26892007-08-05 15:29:28 +00007import string
Brett Cannonaef82d32012-04-14 20:44:23 -04008import sys
Georg Brandl14fc4272008-05-17 18:39:55 +00009from tkinter import *
10import tkinter.simpledialog as tkSimpleDialog
11import tkinter.messagebox as tkMessageBox
Guido van Rossum36e0a922007-07-20 04:05:57 +000012import traceback
Kurt B. Kaiserfd182cd2001-07-14 03:58:25 +000013import webbrowser
Guido van Rossum36e0a922007-07-20 04:05:57 +000014
Kurt B. Kaiser2d7f6a02007-08-22 23:01:33 +000015from idlelib.MultiCall import MultiCallCreator
Kurt B. Kaiser2d7f6a02007-08-22 23:01:33 +000016from idlelib import WindowList
17from idlelib import SearchDialog
18from idlelib import GrepDialog
19from idlelib import ReplaceDialog
20from idlelib import PyParse
21from idlelib.configHandler import idleConf
22from idlelib import aboutDialog, textView, configDialog
23from idlelib import macosxSupport
Terry Jan Reedy5d46ab12015-09-20 19:57:13 -040024from idlelib import help
David Scherer7aced172000-08-15 01:13:23 +000025
26# The default tab setting for a Text widget, in average-width characters.
27TK_TABWIDTH_DEFAULT = 8
28
Terry Jan Reedy0726ddf2014-08-14 21:54:43 -040029_py_version = ' (%s)' % platform.python_version()
30
Kurt B. Kaiserc34ed8e2009-04-26 01:33:55 +000031def _sphinx_version():
32 "Format sys.version_info to produce the Sphinx version string used to install the chm docs"
33 major, minor, micro, level, serial = sys.version_info
34 release = '%s%s' % (major, minor)
Martin v. Löwis7f9d1812012-05-01 16:31:18 +020035 release += '%s' % (micro,)
Benjamin Petersonb48f6342009-06-22 19:36:31 +000036 if level == 'candidate':
37 release += 'rc%s' % (serial,)
38 elif level != 'final':
Kurt B. Kaiserc34ed8e2009-04-26 01:33:55 +000039 release += '%s%s' % (level[0], serial)
40 return release
41
Terry Jan Reedye91e7632012-02-05 15:14:20 -050042
43class HelpDialog(object):
44
45 def __init__(self):
46 self.parent = None # parent of help window
47 self.dlg = None # the help window iteself
48
49 def display(self, parent, near=None):
50 """ Display the help dialog.
51
52 parent - parent widget for the help window
53
54 near - a Toplevel widget (e.g. EditorWindow or PyShell)
55 to use as a reference for placing the help window
56 """
Terry Jan Reedya0ae7892015-09-22 22:59:40 -040057 import warnings as w
58 w.warn("EditorWindow.HelpDialog is no longer used by Idle.\n"
59 "It will be removed in 3.6 or later.\n"
60 "It has been replaced by private help.HelpWindow\n",
61 DeprecationWarning, stacklevel=2)
Terry Jan Reedye91e7632012-02-05 15:14:20 -050062 if self.dlg is None:
63 self.show_dialog(parent)
64 if near:
65 self.nearwindow(near)
66
67 def show_dialog(self, parent):
68 self.parent = parent
69 fn=os.path.join(os.path.abspath(os.path.dirname(__file__)),'help.txt')
70 self.dlg = dlg = textView.view_file(parent,'Help',fn, modal=False)
71 dlg.bind('<Destroy>', self.destroy, '+')
72
73 def nearwindow(self, near):
74 # Place the help dialog near the window specified by parent.
75 # Note - this may not reposition the window in Metacity
76 # if "/apps/metacity/general/disable_workarounds" is enabled
77 dlg = self.dlg
78 geom = (near.winfo_rootx() + 10, near.winfo_rooty() + 10)
79 dlg.withdraw()
80 dlg.geometry("=+%d+%d" % geom)
81 dlg.deiconify()
82 dlg.lift()
83
84 def destroy(self, ev=None):
85 self.dlg = None
86 self.parent = None
87
Terry Jan Reedy96f802a2015-09-20 23:05:25 -040088helpDialog = HelpDialog() # singleton instance, no longer used
Terry Jan Reedye91e7632012-02-05 15:14:20 -050089
90
Kurt B. Kaiserdcba6622004-12-21 22:10:32 +000091class EditorWindow(object):
Kurt B. Kaiser2d7f6a02007-08-22 23:01:33 +000092 from idlelib.Percolator import Percolator
93 from idlelib.ColorDelegator import ColorDelegator
94 from idlelib.UndoDelegator import UndoDelegator
95 from idlelib.IOBinding import IOBinding, filesystemencoding, encoding
96 from idlelib import Bindings
Guilherme Polo5424b0a2008-05-25 15:26:44 +000097 from tkinter import Toplevel
Kurt B. Kaiser2d7f6a02007-08-22 23:01:33 +000098 from idlelib.MultiStatusBar import MultiStatusBar
David Scherer7aced172000-08-15 01:13:23 +000099
Kurt B. Kaiser114713d2003-01-10 05:07:24 +0000100 help_url = None
David Scherer7aced172000-08-15 01:13:23 +0000101
102 def __init__(self, flist=None, filename=None, key=None, root=None):
Kurt B. Kaiser114713d2003-01-10 05:07:24 +0000103 if EditorWindow.help_url is None:
Vinay Sajip7ded1f02012-05-26 03:45:29 +0100104 dochome = os.path.join(sys.base_prefix, 'Doc', 'index.html')
Kurt B. Kaiser114713d2003-01-10 05:07:24 +0000105 if sys.platform.count('linux'):
106 # look for html docs in a couple of standard places
107 pyver = 'python-docs-' + '%s.%s.%s' % sys.version_info[:3]
108 if os.path.isdir('/var/www/html/python/'): # "python2" rpm
109 dochome = '/var/www/html/python/index.html'
110 else:
111 basepath = '/usr/share/doc/' # standard location
112 dochome = os.path.join(basepath, pyver,
113 'Doc', 'index.html')
Kurt B. Kaiser8aa23922004-07-15 04:54:57 +0000114 elif sys.platform[:3] == 'win':
Vinay Sajip7ded1f02012-05-26 03:45:29 +0100115 chmfile = os.path.join(sys.base_prefix, 'Doc',
Kurt B. Kaiserc34ed8e2009-04-26 01:33:55 +0000116 'Python%s.chm' % _sphinx_version())
Thomas Heller84ef1532003-09-23 20:53:10 +0000117 if os.path.isfile(chmfile):
118 dochome = chmfile
Ned Deilyb7601672014-03-27 20:49:14 -0700119 elif sys.platform == 'darwin':
120 # documentation may be stored inside a python framework
Vinay Sajip7ded1f02012-05-26 03:45:29 +0100121 dochome = os.path.join(sys.base_prefix,
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000122 'Resources/English.lproj/Documentation/index.html')
Kurt B. Kaiser114713d2003-01-10 05:07:24 +0000123 dochome = os.path.normpath(dochome)
124 if os.path.isfile(dochome):
125 EditorWindow.help_url = dochome
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000126 if sys.platform == 'darwin':
127 # Safari requires real file:-URLs
128 EditorWindow.help_url = 'file://' + EditorWindow.help_url
Kurt B. Kaiser114713d2003-01-10 05:07:24 +0000129 else:
Terry Jan Reedyb6e17782014-09-19 22:54:15 -0400130 EditorWindow.help_url = "https://docs.python.org/%d.%d/" % sys.version_info[:2]
David Scherer7aced172000-08-15 01:13:23 +0000131 self.flist = flist
132 root = root or flist.root
133 self.root = root
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000134 try:
135 sys.ps1
136 except AttributeError:
137 sys.ps1 = '>>> '
David Scherer7aced172000-08-15 01:13:23 +0000138 self.menubar = Menu(root)
Kurt B. Kaiser183403a2004-08-22 05:14:32 +0000139 self.top = top = WindowList.ListedToplevel(root, menu=self.menubar)
Steven M. Gava0c5bc8c2002-03-27 02:25:44 +0000140 if flist:
Kurt B. Kaiser610c7e02004-04-24 03:01:48 +0000141 self.tkinter_vars = flist.vars
Ezio Melotti42da6632011-03-15 05:18:48 +0200142 #self.top.instance_dict makes flist.inversedict available to
143 #configDialog.py so it can access all EditorWindow instances
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000144 self.top.instance_dict = flist.inversedict
Kurt B. Kaiser610c7e02004-04-24 03:01:48 +0000145 else:
146 self.tkinter_vars = {} # keys: Tkinter event names
147 # values: Tkinter variable instances
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000148 self.top.instance_dict = {}
149 self.recent_files_path = os.path.join(idleConf.GetUserCfgDir(),
Steven M. Gava1d46e402002-03-27 08:40:46 +0000150 'recent-files.lst')
David Scherer7aced172000-08-15 01:13:23 +0000151 self.text_frame = text_frame = Frame(top)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000152 self.vbar = vbar = Scrollbar(text_frame, name='vbar')
Andrew Svetlov8a495a42012-12-24 13:15:43 +0200153 self.width = idleConf.GetOption('main', 'EditorWindow',
154 'width', type='int')
Kurt B. Kaiser113f0e82009-04-04 20:38:52 +0000155 text_options = {
156 'name': 'text',
157 'padx': 5,
158 'wrap': 'none',
159 'width': self.width,
Andrew Svetlov8a495a42012-12-24 13:15:43 +0200160 'height': idleConf.GetOption('main', 'EditorWindow',
161 'height', type='int')}
Kurt B. Kaiser113f0e82009-04-04 20:38:52 +0000162 if TkVersion >= 8.5:
163 # Starting with tk 8.5 we have to set the new tabstyle option
164 # to 'wordprocessor' to achieve the same display of tabs as in
165 # older tk versions.
166 text_options['tabstyle'] = 'wordprocessor'
167 self.text = text = MultiCallCreator(Text)(text_frame, **text_options)
Kurt B. Kaiser183403a2004-08-22 05:14:32 +0000168 self.top.focused_widget = self.text
David Scherer7aced172000-08-15 01:13:23 +0000169
170 self.createmenubar()
171 self.apply_bindings()
172
173 self.top.protocol("WM_DELETE_WINDOW", self.close)
174 self.top.bind("<<close-window>>", self.close_event)
Ned Deilyb7601672014-03-27 20:49:14 -0700175 if macosxSupport.isAquaTk():
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000176 # Command-W on editorwindows doesn't work without this.
177 text.bind('<<close-window>>', self.close_event)
Terry Jan Reedy3c7eccd2015-09-22 21:10:27 -0400178 # Some OS X systems have only one mouse button, so use
179 # control-click for popup context menus there. For two
180 # buttons, AquaTk defines <2> as the right button, not <3>.
R. David Murrayb68a7bc2010-12-18 17:19:10 +0000181 text.bind("<Control-Button-1>",self.right_menu_event)
Terry Jan Reedy3c7eccd2015-09-22 21:10:27 -0400182 text.bind("<2>", self.right_menu_event)
R. David Murrayb68a7bc2010-12-18 17:19:10 +0000183 else:
Terry Jan Reedy3c7eccd2015-09-22 21:10:27 -0400184 # Elsewhere, use right-click for popup menus.
R. David Murrayb68a7bc2010-12-18 17:19:10 +0000185 text.bind("<3>",self.right_menu_event)
Kurt B. Kaiser84f48032002-09-26 22:13:22 +0000186 text.bind("<<cut>>", self.cut)
187 text.bind("<<copy>>", self.copy)
188 text.bind("<<paste>>", self.paste)
David Scherer7aced172000-08-15 01:13:23 +0000189 text.bind("<<center-insert>>", self.center_insert_event)
190 text.bind("<<help>>", self.help_dialog)
David Scherer7aced172000-08-15 01:13:23 +0000191 text.bind("<<python-docs>>", self.python_docs)
192 text.bind("<<about-idle>>", self.about_dialog)
Steven M. Gava3b55a892001-11-21 05:56:26 +0000193 text.bind("<<open-config-dialog>>", self.config_dialog)
Terry Jan Reedya9421fb2014-10-22 20:15:18 -0400194 text.bind("<<open-config-extensions-dialog>>",
195 self.config_extensions_dialog)
David Scherer7aced172000-08-15 01:13:23 +0000196 text.bind("<<open-module>>", self.open_module)
197 text.bind("<<do-nothing>>", lambda event: "break")
198 text.bind("<<select-all>>", self.select_all)
199 text.bind("<<remove-selection>>", self.remove_selection)
Steven M. Gavac5976402002-01-04 03:06:08 +0000200 text.bind("<<find>>", self.find_event)
201 text.bind("<<find-again>>", self.find_again_event)
202 text.bind("<<find-in-files>>", self.find_in_files_event)
203 text.bind("<<find-selection>>", self.find_selection_event)
204 text.bind("<<replace>>", self.replace_event)
205 text.bind("<<goto-line>>", self.goto_line_event)
Kurt B. Kaisercb7a3832002-09-14 02:34:23 +0000206 text.bind("<<smart-backspace>>",self.smart_backspace_event)
207 text.bind("<<newline-and-indent>>",self.newline_and_indent_event)
208 text.bind("<<smart-indent>>",self.smart_indent_event)
209 text.bind("<<indent-region>>",self.indent_region_event)
210 text.bind("<<dedent-region>>",self.dedent_region_event)
211 text.bind("<<comment-region>>",self.comment_region_event)
212 text.bind("<<uncomment-region>>",self.uncomment_region_event)
213 text.bind("<<tabify-region>>",self.tabify_region_event)
214 text.bind("<<untabify-region>>",self.untabify_region_event)
215 text.bind("<<toggle-tabs>>",self.toggle_tabs_event)
216 text.bind("<<change-indentwidth>>",self.change_indentwidth_event)
Kurt B. Kaiser5ec186b2003-01-17 04:04:06 +0000217 text.bind("<Left>", self.move_at_edge_if_selection(0))
218 text.bind("<Right>", self.move_at_edge_if_selection(1))
Kurt B. Kaiser3069dbb2005-01-28 00:16:16 +0000219 text.bind("<<del-word-left>>", self.del_word_left)
220 text.bind("<<del-word-right>>", self.del_word_right)
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000221 text.bind("<<beginning-of-line>>", self.home_callback)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000222
David Scherer7aced172000-08-15 01:13:23 +0000223 if flist:
224 flist.inversedict[self] = key
225 if key:
226 flist.dict[key] = self
Kurt B. Kaiserd2f48612003-06-05 02:34:04 +0000227 text.bind("<<open-new-window>>", self.new_callback)
David Scherer7aced172000-08-15 01:13:23 +0000228 text.bind("<<close-all-windows>>", self.flist.close_all_callback)
229 text.bind("<<open-class-browser>>", self.open_class_browser)
230 text.bind("<<open-path-browser>>", self.open_path_browser)
Terry Jan Reedy7e55db22014-07-28 22:23:59 -0400231 text.bind("<<open-turtle-demo>>", self.open_turtle_demo)
David Scherer7aced172000-08-15 01:13:23 +0000232
Steven M. Gava898a3652001-10-07 11:10:44 +0000233 self.set_status_bar()
David Scherer7aced172000-08-15 01:13:23 +0000234 vbar['command'] = text.yview
235 vbar.pack(side=RIGHT, fill=Y)
David Scherer7aced172000-08-15 01:13:23 +0000236 text['yscrollcommand'] = vbar.set
Terry Jan Reedyd87d1682015-08-01 18:57:33 -0400237 text['font'] = idleConf.GetFont(self.root, 'main', 'EditorWindow')
David Scherer7aced172000-08-15 01:13:23 +0000238 text_frame.pack(side=LEFT, fill=BOTH, expand=1)
239 text.pack(side=TOP, fill=BOTH, expand=1)
240 text.focus_set()
241
Kurt B. Kaiser6af44982005-01-19 00:22:59 +0000242 # usetabs true -> literal tab characters are used by indent and
243 # dedent cmds, possibly mixed with spaces if
244 # indentwidth is not a multiple of tabwidth,
245 # which will cause Tabnanny to nag!
246 # false -> tab characters are converted to spaces by indent
247 # and dedent cmds, and ditto TAB keystrokes
Kurt B. Kaiseracdef852005-01-31 03:34:26 +0000248 # Although use-spaces=0 can be configured manually in config-main.def,
249 # configuration of tabs v. spaces is not supported in the configuration
250 # dialog. IDLE promotes the preferred Python indentation: use spaces!
Andrew Svetlov8a495a42012-12-24 13:15:43 +0200251 usespaces = idleConf.GetOption('main', 'Indent',
252 'use-spaces', type='bool')
Kurt B. Kaiseracdef852005-01-31 03:34:26 +0000253 self.usetabs = not usespaces
Kurt B. Kaiser6af44982005-01-19 00:22:59 +0000254
255 # tabwidth is the display width of a literal tab character.
256 # CAUTION: telling Tk to use anything other than its default
257 # tab setting causes it to use an entirely different tabbing algorithm,
258 # treating tab stops as fixed distances from the left margin.
259 # Nobody expects this, so for now tabwidth should never be changed.
Kurt B. Kaiseracdef852005-01-31 03:34:26 +0000260 self.tabwidth = 8 # must remain 8 until Tk is fixed.
261
262 # indentwidth is the number of screen characters per indent level.
263 # The recommended Python indentation is four spaces.
264 self.indentwidth = self.tabwidth
265 self.set_notabs_indentwidth()
Kurt B. Kaiser6af44982005-01-19 00:22:59 +0000266
267 # If context_use_ps1 is true, parsing searches back for a ps1 line;
268 # else searches for a popular (if, def, ...) Python stmt.
269 self.context_use_ps1 = False
270
271 # When searching backwards for a reliable place to begin parsing,
272 # first start num_context_lines[0] lines back, then
273 # num_context_lines[1] lines back if that didn't work, and so on.
274 # The last value should be huge (larger than the # of lines in a
275 # conceivable file).
276 # Making the initial values larger slows things down more often.
277 self.num_context_lines = 50, 500, 5000000
David Scherer7aced172000-08-15 01:13:23 +0000278 self.per = per = self.Percolator(text)
Kurt B. Kaiserdc1e7092002-07-11 04:33:41 +0000279 self.undo = undo = self.UndoDelegator()
280 per.insertfilter(undo)
281 text.undo_block_start = undo.undo_block_start
282 text.undo_block_stop = undo.undo_block_stop
283 undo.set_saved_change_hook(self.saved_change_hook)
Kurt B. Kaiserdc1e7092002-07-11 04:33:41 +0000284 # IOBinding implements file I/O and printing functionality
David Scherer7aced172000-08-15 01:13:23 +0000285 self.io = io = self.IOBinding(self)
Kurt B. Kaiserdc1e7092002-07-11 04:33:41 +0000286 io.set_filename_change_hook(self.filename_change_hook)
Kurt B. Kaiser105f60e2007-09-06 04:03:04 +0000287 self.good_load = False
288 self.set_indentation_params(False)
Christian Heimesa156e092008-02-16 07:38:31 +0000289 self.color = None # initialized below in self.ResetColorizer
David Scherer7aced172000-08-15 01:13:23 +0000290 if filename:
Kurt B. Kaiserd2f48612003-06-05 02:34:04 +0000291 if os.path.exists(filename) and not os.path.isdir(filename):
Kurt B. Kaiser105f60e2007-09-06 04:03:04 +0000292 if io.loadfile(filename):
293 self.good_load = True
294 is_py_src = self.ispythonsource(filename)
295 self.set_indentation_params(is_py_src)
David Scherer7aced172000-08-15 01:13:23 +0000296 else:
297 io.set_filename(filename)
Roger Serwy5b1ab242013-05-05 11:34:21 -0500298 self.good_load = True
299
Christian Heimesa156e092008-02-16 07:38:31 +0000300 self.ResetColorizer()
David Scherer7aced172000-08-15 01:13:23 +0000301 self.saved_change_hook()
Kurt B. Kaiser105f60e2007-09-06 04:03:04 +0000302 self.update_recent_files_list()
David Scherer7aced172000-08-15 01:13:23 +0000303 self.load_extensions()
David Scherer7aced172000-08-15 01:13:23 +0000304 menu = self.menudict.get('windows')
305 if menu:
306 end = menu.index("end")
307 if end is None:
308 end = -1
309 if end >= 0:
310 menu.add_separator()
311 end = end + 1
312 self.wmenu_end = end
313 WindowList.register_callback(self.postwindowsmenu)
314
315 # Some abstractions so IDLE extensions are cross-IDE
316 self.askyesno = tkMessageBox.askyesno
317 self.askinteger = tkSimpleDialog.askinteger
318 self.showerror = tkMessageBox.showerror
319
Terry Jan Reedy2122b622015-09-27 22:50:54 -0400320 self._highlight_workaround() # Fix selection tags on Windows
321
322 def _highlight_workaround(self):
323 # On Windows, Tk removes painting of the selection
324 # tags which is different behavior than on Linux and Mac.
325 # See issue14146 for more information.
326 if not sys.platform.startswith('win'):
327 return
328
329 text = self.text
330 text.event_add("<<Highlight-FocusOut>>", "<FocusOut>")
331 text.event_add("<<Highlight-FocusIn>>", "<FocusIn>")
332 def highlight_fix(focus):
333 sel_range = text.tag_ranges("sel")
334 if sel_range:
335 if focus == 'out':
336 HILITE_CONFIG = idleConf.GetHighlight(
337 idleConf.CurrentTheme(), 'hilite')
338 text.tag_config("sel_fix", HILITE_CONFIG)
339 text.tag_raise("sel_fix")
340 text.tag_add("sel_fix", *sel_range)
341 elif focus == 'in':
342 text.tag_remove("sel_fix", "1.0", "end")
343
344 text.bind("<<Highlight-FocusOut>>",
345 lambda ev: highlight_fix("out"))
346 text.bind("<<Highlight-FocusIn>>",
347 lambda ev: highlight_fix("in"))
348
349
Martin v. Löwis307021f2005-11-27 16:59:04 +0000350 def _filename_to_unicode(self, filename):
Terry Jan Reedy5c28e9f2015-08-06 00:54:07 -0400351 """Return filename as BMP unicode so diplayable in Tk."""
352 # Decode bytes to unicode.
353 if isinstance(filename, bytes):
Martin v. Löwis307021f2005-11-27 16:59:04 +0000354 try:
Terry Jan Reedy5c28e9f2015-08-06 00:54:07 -0400355 filename = filename.decode(self.filesystemencoding)
Martin v. Löwis307021f2005-11-27 16:59:04 +0000356 except UnicodeDecodeError:
Martin v. Löwis307021f2005-11-27 16:59:04 +0000357 try:
Terry Jan Reedy5c28e9f2015-08-06 00:54:07 -0400358 filename = filename.decode(self.encoding)
Martin v. Löwis307021f2005-11-27 16:59:04 +0000359 except UnicodeDecodeError:
360 # byte-to-byte conversion
Terry Jan Reedy5c28e9f2015-08-06 00:54:07 -0400361 filename = filename.decode('iso8859-1')
362 # Replace non-BMP char with diamond questionmark.
363 return re.sub('[\U00010000-\U0010FFFF]', '\ufffd', filename)
Martin v. Löwis307021f2005-11-27 16:59:04 +0000364
Kurt B. Kaiserd2f48612003-06-05 02:34:04 +0000365 def new_callback(self, event):
366 dirname, basename = self.io.defaultfilename()
367 self.flist.new(dirname)
368 return "break"
369
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000370 def home_callback(self, event):
Kurt B. Kaiser75fc5662011-03-21 02:13:42 -0400371 if (event.state & 4) != 0 and event.keysym == "Home":
372 # state&4==Control. If <Control-Home>, use the Tk binding.
373 return
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000374 if self.text.index("iomark") and \
375 self.text.compare("iomark", "<=", "insert lineend") and \
376 self.text.compare("insert linestart", "<=", "iomark"):
Kurt B. Kaiser946f1722011-03-25 20:29:13 -0400377 # In Shell on input line, go to just after prompt
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000378 insertpt = int(self.text.index("iomark").split(".")[1])
379 else:
380 line = self.text.get("insert linestart", "insert lineend")
Georg Brandl7d4f39a2008-07-19 13:53:58 +0000381 for insertpt in range(len(line)):
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000382 if line[insertpt] not in (' ','\t'):
383 break
384 else:
385 insertpt=len(line)
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000386 lineat = int(self.text.index("insert").split('.')[1])
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000387 if insertpt == lineat:
388 insertpt = 0
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000389 dest = "insert linestart+"+str(insertpt)+"c"
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000390 if (event.state&1) == 0:
Kurt B. Kaiser946f1722011-03-25 20:29:13 -0400391 # shift was not pressed
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000392 self.text.tag_remove("sel", "1.0", "end")
393 else:
394 if not self.text.index("sel.first"):
Andrew Svetlov8a495a42012-12-24 13:15:43 +0200395 # there was no previous selection
396 self.text.mark_set("my_anchor", "insert")
Kurt B. Kaiser946f1722011-03-25 20:29:13 -0400397 else:
Andrew Svetlov8a495a42012-12-24 13:15:43 +0200398 if self.text.compare(self.text.index("sel.first"), "<",
399 self.text.index("insert")):
Kurt B. Kaiser946f1722011-03-25 20:29:13 -0400400 self.text.mark_set("my_anchor", "sel.first") # extend back
401 else:
402 self.text.mark_set("my_anchor", "sel.last") # extend forward
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000403 first = self.text.index(dest)
Kurt B. Kaiser946f1722011-03-25 20:29:13 -0400404 last = self.text.index("my_anchor")
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000405 if self.text.compare(first,">",last):
406 first,last = last,first
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000407 self.text.tag_remove("sel", "1.0", "end")
408 self.text.tag_add("sel", first, last)
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000409 self.text.mark_set("insert", dest)
410 self.text.see("insert")
411 return "break"
412
David Scherer7aced172000-08-15 01:13:23 +0000413 def set_status_bar(self):
Steven M. Gava898a3652001-10-07 11:10:44 +0000414 self.status_bar = self.MultiStatusBar(self.top)
Ned Deilyb7601672014-03-27 20:49:14 -0700415 if sys.platform == "darwin":
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000416 # Insert some padding to avoid obscuring some of the statusbar
417 # by the resize widget.
418 self.status_bar.set_label('_padding1', ' ', side=RIGHT)
David Scherer7aced172000-08-15 01:13:23 +0000419 self.status_bar.set_label('column', 'Col: ?', side=RIGHT)
420 self.status_bar.set_label('line', 'Ln: ?', side=RIGHT)
421 self.status_bar.pack(side=BOTTOM, fill=X)
Kurt B. Kaiserb1754452005-11-18 22:05:48 +0000422 self.text.bind("<<set-line-and-column>>", self.set_line_and_column)
423 self.text.event_add("<<set-line-and-column>>",
424 "<KeyRelease>", "<ButtonRelease>")
David Scherer7aced172000-08-15 01:13:23 +0000425 self.text.after_idle(self.set_line_and_column)
426
427 def set_line_and_column(self, event=None):
Kurt B. Kaiser220ecbc2002-09-16 02:13:15 +0000428 line, column = self.text.index(INSERT).split('.')
David Scherer7aced172000-08-15 01:13:23 +0000429 self.status_bar.set_label('column', 'Col: %s' % column)
430 self.status_bar.set_label('line', 'Ln: %s' % line)
431
David Scherer7aced172000-08-15 01:13:23 +0000432 menu_specs = [
433 ("file", "_File"),
434 ("edit", "_Edit"),
435 ("format", "F_ormat"),
436 ("run", "_Run"),
Kurt B. Kaiser1061e722003-01-04 01:43:53 +0000437 ("options", "_Options"),
Ned Deilyccb416f2015-01-17 21:06:27 -0800438 ("windows", "_Window"),
David Scherer7aced172000-08-15 01:13:23 +0000439 ("help", "_Help"),
440 ]
441
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000442
David Scherer7aced172000-08-15 01:13:23 +0000443 def createmenubar(self):
444 mbar = self.menubar
445 self.menudict = menudict = {}
446 for name, label in self.menu_specs:
447 underline, label = prepstr(label)
Terry Jan Reedy30f1f672015-07-30 16:44:22 -0400448 menudict[name] = menu = Menu(mbar, name=name, tearoff=0)
David Scherer7aced172000-08-15 01:13:23 +0000449 mbar.add_cascade(label=label, menu=menu, underline=underline)
Ned Deilyb7601672014-03-27 20:49:14 -0700450 if macosxSupport.isCarbonTk():
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000451 # Insert the application menu
Terry Jan Reedy30f1f672015-07-30 16:44:22 -0400452 menudict['application'] = menu = Menu(mbar, name='apple',
453 tearoff=0)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000454 mbar.add_cascade(label='IDLE', menu=menu)
David Scherer7aced172000-08-15 01:13:23 +0000455 self.fill_menus()
Terry Jan Reedy30f1f672015-07-30 16:44:22 -0400456 self.recent_files_menu = Menu(self.menubar, tearoff=0)
Kurt B. Kaiser105f60e2007-09-06 04:03:04 +0000457 self.menudict['file'].insert_cascade(3, label='Recent Files',
458 underline=0,
459 menu=self.recent_files_menu)
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000460 self.base_helpmenu_length = self.menudict['help'].index(END)
461 self.reset_help_menu_entries()
David Scherer7aced172000-08-15 01:13:23 +0000462
463 def postwindowsmenu(self):
464 # Only called when Windows menu exists
David Scherer7aced172000-08-15 01:13:23 +0000465 menu = self.menudict['windows']
466 end = menu.index("end")
467 if end is None:
468 end = -1
469 if end > self.wmenu_end:
470 menu.delete(self.wmenu_end+1, end)
471 WindowList.add_windows_to_menu(menu)
472
473 rmenu = None
474
475 def right_menu_event(self, event):
David Scherer7aced172000-08-15 01:13:23 +0000476 self.text.mark_set("insert", "@%d,%d" % (event.x, event.y))
477 if not self.rmenu:
478 self.make_rmenu()
479 rmenu = self.rmenu
480 self.event = event
481 iswin = sys.platform[:3] == 'win'
482 if iswin:
483 self.text.config(cursor="arrow")
Andrew Svetlovd1837672012-11-01 22:41:19 +0200484
Roger Serwy6b2918a2013-04-07 12:15:52 -0500485 for item in self.rmenu_specs:
486 try:
487 label, eventname, verify_state = item
488 except ValueError: # see issue1207589
489 continue
490
Andrew Svetlovd1837672012-11-01 22:41:19 +0200491 if verify_state is None:
492 continue
493 state = getattr(self, verify_state)()
494 rmenu.entryconfigure(label, state=state)
495
496
David Scherer7aced172000-08-15 01:13:23 +0000497 rmenu.tk_popup(event.x_root, event.y_root)
498 if iswin:
499 self.text.config(cursor="ibeam")
500
501 rmenu_specs = [
Andrew Svetlovd1837672012-11-01 22:41:19 +0200502 # ("Label", "<<virtual-event>>", "statefuncname"), ...
503 ("Close", "<<close-window>>", None), # Example
David Scherer7aced172000-08-15 01:13:23 +0000504 ]
505
506 def make_rmenu(self):
507 rmenu = Menu(self.text, tearoff=0)
Roger Serwy6b2918a2013-04-07 12:15:52 -0500508 for item in self.rmenu_specs:
509 label, eventname = item[0], item[1]
Andrew Svetlovd1837672012-11-01 22:41:19 +0200510 if label is not None:
511 def command(text=self.text, eventname=eventname):
512 text.event_generate(eventname)
513 rmenu.add_command(label=label, command=command)
514 else:
515 rmenu.add_separator()
David Scherer7aced172000-08-15 01:13:23 +0000516 self.rmenu = rmenu
517
Andrew Svetlovd1837672012-11-01 22:41:19 +0200518 def rmenu_check_cut(self):
519 return self.rmenu_check_copy()
520
521 def rmenu_check_copy(self):
522 try:
523 indx = self.text.index('sel.first')
524 except TclError:
525 return 'disabled'
526 else:
527 return 'normal' if indx else 'disabled'
528
529 def rmenu_check_paste(self):
530 try:
531 self.text.tk.call('tk::GetSelection', self.text, 'CLIPBOARD')
532 except TclError:
533 return 'disabled'
534 else:
535 return 'normal'
536
David Scherer7aced172000-08-15 01:13:23 +0000537 def about_dialog(self, event=None):
Terry Jan Reedyb50c6372015-09-20 22:55:39 -0400538 "Handle Help 'About IDLE' event."
539 # Synchronize with macosxSupport.overrideRootMenu.about_dialog.
Kurt B. Kaiserd78b2302003-06-12 04:03:49 +0000540 aboutDialog.AboutDialog(self.top,'About IDLE')
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000541
Steven M. Gava3b55a892001-11-21 05:56:26 +0000542 def config_dialog(self, event=None):
Terry Jan Reedyb50c6372015-09-20 22:55:39 -0400543 "Handle Options 'Configure IDLE' event."
544 # Synchronize with macosxSupport.overrideRootMenu.config_dialog.
Steven M. Gava3b55a892001-11-21 05:56:26 +0000545 configDialog.ConfigDialog(self.top,'Settings')
Terry Jan Reedyb50c6372015-09-20 22:55:39 -0400546
Terry Jan Reedya9421fb2014-10-22 20:15:18 -0400547 def config_extensions_dialog(self, event=None):
Terry Jan Reedyb50c6372015-09-20 22:55:39 -0400548 "Handle Options 'Configure Extensions' event."
Terry Jan Reedya9421fb2014-10-22 20:15:18 -0400549 configDialog.ConfigExtensionsDialog(self.top)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000550
David Scherer7aced172000-08-15 01:13:23 +0000551 def help_dialog(self, event=None):
Terry Jan Reedyb50c6372015-09-20 22:55:39 -0400552 "Handle Help 'IDLE Help' event."
553 # Synchronize with macosxSupport.overrideRootMenu.help_dialog.
Terry Jan Reedye91e7632012-02-05 15:14:20 -0500554 if self.root:
555 parent = self.root
556 else:
557 parent = self.top
Terry Jan Reedy5d46ab12015-09-20 19:57:13 -0400558 help.show_idlehelp(parent)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000559
Kurt B. Kaiser114713d2003-01-10 05:07:24 +0000560 def python_docs(self, event=None):
Kurt B. Kaiser8aa23922004-07-15 04:54:57 +0000561 if sys.platform[:3] == 'win':
Terry Reedy6739cc02011-01-01 02:25:36 +0000562 try:
563 os.startfile(self.help_url)
Andrew Svetlov2606a6f2012-12-19 14:33:35 +0200564 except OSError as why:
Terry Reedy6739cc02011-01-01 02:25:36 +0000565 tkMessageBox.showerror(title='Document Start Failure',
566 message=str(why), parent=self.text)
Kurt B. Kaiser114713d2003-01-10 05:07:24 +0000567 else:
568 webbrowser.open(self.help_url)
Kurt B. Kaiser8aa23922004-07-15 04:54:57 +0000569 return "break"
David Scherer7aced172000-08-15 01:13:23 +0000570
Kurt B. Kaiser84f48032002-09-26 22:13:22 +0000571 def cut(self,event):
572 self.text.event_generate("<<Cut>>")
573 return "break"
574
575 def copy(self,event):
Kurt B. Kaiserb1754452005-11-18 22:05:48 +0000576 if not self.text.tag_ranges("sel"):
577 # There is no selection, so do nothing and maybe interrupt.
578 return
Kurt B. Kaiser84f48032002-09-26 22:13:22 +0000579 self.text.event_generate("<<Copy>>")
580 return "break"
581
582 def paste(self,event):
583 self.text.event_generate("<<Paste>>")
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000584 self.text.see("insert")
Kurt B. Kaiser84f48032002-09-26 22:13:22 +0000585 return "break"
586
David Scherer7aced172000-08-15 01:13:23 +0000587 def select_all(self, event=None):
588 self.text.tag_add("sel", "1.0", "end-1c")
589 self.text.mark_set("insert", "1.0")
590 self.text.see("insert")
591 return "break"
592
593 def remove_selection(self, event=None):
594 self.text.tag_remove("sel", "1.0", "end")
595 self.text.see("insert")
596
Kurt B. Kaiser5ec186b2003-01-17 04:04:06 +0000597 def move_at_edge_if_selection(self, edge_index):
598 """Cursor move begins at start or end of selection
599
600 When a left/right cursor key is pressed create and return to Tkinter a
601 function which causes a cursor move from the associated edge of the
602 selection.
603
604 """
605 self_text_index = self.text.index
606 self_text_mark_set = self.text.mark_set
607 edges_table = ("sel.first+1c", "sel.last-1c")
608 def move_at_edge(event):
609 if (event.state & 5) == 0: # no shift(==1) or control(==4) pressed
610 try:
611 self_text_index("sel.first")
612 self_text_mark_set("insert", edges_table[edge_index])
613 except TclError:
614 pass
615 return move_at_edge
616
Kurt B. Kaiser3069dbb2005-01-28 00:16:16 +0000617 def del_word_left(self, event):
618 self.text.event_generate('<Meta-Delete>')
619 return "break"
620
621 def del_word_right(self, event):
622 self.text.event_generate('<Meta-d>')
623 return "break"
624
Steven M. Gavac5976402002-01-04 03:06:08 +0000625 def find_event(self, event):
626 SearchDialog.find(self.text)
627 return "break"
628
629 def find_again_event(self, event):
630 SearchDialog.find_again(self.text)
631 return "break"
632
633 def find_selection_event(self, event):
634 SearchDialog.find_selection(self.text)
635 return "break"
636
637 def find_in_files_event(self, event):
638 GrepDialog.grep(self.text, self.io, self.flist)
639 return "break"
640
641 def replace_event(self, event):
642 ReplaceDialog.replace(self.text)
643 return "break"
644
645 def goto_line_event(self, event):
646 text = self.text
647 lineno = tkSimpleDialog.askinteger("Goto",
648 "Go to line number:",parent=text)
649 if lineno is None:
650 return "break"
651 if lineno <= 0:
652 text.bell()
653 return "break"
654 text.mark_set("insert", "%d.0" % lineno)
655 text.see("insert")
656
David Scherer7aced172000-08-15 01:13:23 +0000657 def open_module(self, event=None):
Kurt B. Kaiser105f60e2007-09-06 04:03:04 +0000658 # XXX Shouldn't this be in IOBinding?
David Scherer7aced172000-08-15 01:13:23 +0000659 try:
660 name = self.text.get("sel.first", "sel.last")
661 except TclError:
662 name = ""
663 else:
Kurt B. Kaiser220ecbc2002-09-16 02:13:15 +0000664 name = name.strip()
Guido van Rossum852f35b2003-06-05 11:36:55 +0000665 name = tkSimpleDialog.askstring("Module",
666 "Enter the name of a Python module\n"
667 "to search on sys.path and open:",
668 parent=self.text, initialvalue=name)
669 if name:
670 name = name.strip()
David Scherer7aced172000-08-15 01:13:23 +0000671 if not name:
Guido van Rossum852f35b2003-06-05 11:36:55 +0000672 return
David Scherer7aced172000-08-15 01:13:23 +0000673 # XXX Ought to insert current file's directory in front of path
674 try:
Eric Snow6029e082014-01-25 15:32:46 -0700675 spec = importlib.util.find_spec(name)
Brett Cannon50793b42013-06-07 13:17:48 -0400676 except (ValueError, ImportError) as msg:
David Scherer7aced172000-08-15 01:13:23 +0000677 tkMessageBox.showerror("Import error", str(msg), parent=self.text)
678 return
Eric Snow02b9f9d2014-01-06 20:42:59 -0700679 if spec is None:
Brett Cannon50793b42013-06-07 13:17:48 -0400680 tkMessageBox.showerror("Import error", "module not found",
681 parent=self.text)
David Scherer7aced172000-08-15 01:13:23 +0000682 return
Eric Snow02b9f9d2014-01-06 20:42:59 -0700683 if not isinstance(spec.loader, importlib.abc.SourceLoader):
Brett Cannon50793b42013-06-07 13:17:48 -0400684 tkMessageBox.showerror("Import error", "not a source-based module",
685 parent=self.text)
686 return
687 try:
Eric Snow02b9f9d2014-01-06 20:42:59 -0700688 file_path = spec.loader.get_filename(name)
Brett Cannon50793b42013-06-07 13:17:48 -0400689 except AttributeError:
690 tkMessageBox.showerror("Import error",
691 "loader does not support get_filename",
692 parent=self.text)
693 return
David Scherer7aced172000-08-15 01:13:23 +0000694 if self.flist:
Brett Cannon50793b42013-06-07 13:17:48 -0400695 self.flist.open(file_path)
David Scherer7aced172000-08-15 01:13:23 +0000696 else:
Brett Cannon50793b42013-06-07 13:17:48 -0400697 self.io.loadfile(file_path)
Terry Jan Reedy380ec632014-10-15 22:01:31 -0400698 return file_path
David Scherer7aced172000-08-15 01:13:23 +0000699
700 def open_class_browser(self, event=None):
701 filename = self.io.filename
Terry Jan Reedy380ec632014-10-15 22:01:31 -0400702 if not (self.__class__.__name__ == 'PyShellEditorWindow'
703 and filename):
704 filename = self.open_module()
705 if filename is None:
706 return
David Scherer7aced172000-08-15 01:13:23 +0000707 head, tail = os.path.split(filename)
708 base, ext = os.path.splitext(tail)
Kurt B. Kaiser2d7f6a02007-08-22 23:01:33 +0000709 from idlelib import ClassBrowser
David Scherer7aced172000-08-15 01:13:23 +0000710 ClassBrowser.ClassBrowser(self.flist, base, [head])
711
712 def open_path_browser(self, event=None):
Kurt B. Kaiser2d7f6a02007-08-22 23:01:33 +0000713 from idlelib import PathBrowser
David Scherer7aced172000-08-15 01:13:23 +0000714 PathBrowser.PathBrowser(self.flist)
715
Terry Jan Reedy7e55db22014-07-28 22:23:59 -0400716 def open_turtle_demo(self, event = None):
717 import subprocess
718
719 cmd = [sys.executable,
720 '-c',
721 'from turtledemo.__main__ import main; main()']
Terry Jan Reedy038c16b2015-05-15 23:03:17 -0400722 subprocess.Popen(cmd, shell=False)
Terry Jan Reedy7e55db22014-07-28 22:23:59 -0400723
David Scherer7aced172000-08-15 01:13:23 +0000724 def gotoline(self, lineno):
725 if lineno is not None and lineno > 0:
726 self.text.mark_set("insert", "%d.0" % lineno)
727 self.text.tag_remove("sel", "1.0", "end")
728 self.text.tag_add("sel", "insert", "insert +1l")
729 self.center()
730
731 def ispythonsource(self, filename):
Kurt B. Kaiserdf506ea2005-06-12 04:33:30 +0000732 if not filename or os.path.isdir(filename):
Kurt B. Kaiser220ecbc2002-09-16 02:13:15 +0000733 return True
David Scherer7aced172000-08-15 01:13:23 +0000734 base, ext = os.path.splitext(os.path.basename(filename))
735 if os.path.normcase(ext) in (".py", ".pyw"):
Kurt B. Kaiser220ecbc2002-09-16 02:13:15 +0000736 return True
Kurt B. Kaiser105f60e2007-09-06 04:03:04 +0000737 line = self.text.get('1.0', '1.0 lineend')
738 return line.startswith('#!') and 'python' in line
David Scherer7aced172000-08-15 01:13:23 +0000739
740 def close_hook(self):
741 if self.flist:
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000742 self.flist.unregister_maybe_terminate(self)
743 self.flist = None
David Scherer7aced172000-08-15 01:13:23 +0000744
745 def set_close_hook(self, close_hook):
746 self.close_hook = close_hook
747
748 def filename_change_hook(self):
749 if self.flist:
750 self.flist.filename_changed_edit(self)
751 self.saved_change_hook()
Kurt B. Kaiser260cb902003-06-06 21:58:38 +0000752 self.top.update_windowlist_registry(self)
Christian Heimesa156e092008-02-16 07:38:31 +0000753 self.ResetColorizer()
David Scherer7aced172000-08-15 01:13:23 +0000754
Christian Heimesa156e092008-02-16 07:38:31 +0000755 def _addcolorizer(self):
David Scherer7aced172000-08-15 01:13:23 +0000756 if self.color:
757 return
Christian Heimesa156e092008-02-16 07:38:31 +0000758 if self.ispythonsource(self.io.filename):
759 self.color = self.ColorDelegator()
760 # can add more colorizers here...
761 if self.color:
762 self.per.removefilter(self.undo)
763 self.per.insertfilter(self.color)
764 self.per.insertfilter(self.undo)
David Scherer7aced172000-08-15 01:13:23 +0000765
Christian Heimesa156e092008-02-16 07:38:31 +0000766 def _rmcolorizer(self):
David Scherer7aced172000-08-15 01:13:23 +0000767 if not self.color:
768 return
Kurt B. Kaiserdf506ea2005-06-12 04:33:30 +0000769 self.color.removecolors()
David Scherer7aced172000-08-15 01:13:23 +0000770 self.per.removefilter(self.color)
771 self.color = None
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000772
Steven M. Gavab77d3432002-03-02 07:16:21 +0000773 def ResetColorizer(self):
Terry Jan Reedy86757992014-10-09 18:44:32 -0400774 "Update the color theme"
Christian Heimesa156e092008-02-16 07:38:31 +0000775 # Called from self.filename_change_hook and from configDialog.py
776 self._rmcolorizer()
777 self._addcolorizer()
Kurt B. Kaiser73360a32004-03-08 18:15:31 +0000778 theme = idleConf.GetOption('main','Theme','name')
Christian Heimesa156e092008-02-16 07:38:31 +0000779 normal_colors = idleConf.GetHighlight(theme, 'normal')
780 cursor_color = idleConf.GetHighlight(theme, 'cursor', fgBg='fg')
781 select_colors = idleConf.GetHighlight(theme, 'hilite')
782 self.text.config(
783 foreground=normal_colors['foreground'],
784 background=normal_colors['background'],
785 insertbackground=cursor_color,
786 selectforeground=select_colors['foreground'],
787 selectbackground=select_colors['background'],
788 )
David Scherer7aced172000-08-15 01:13:23 +0000789
Guido van Rossum33d26892007-08-05 15:29:28 +0000790 IDENTCHARS = string.ascii_letters + string.digits + "_"
791
792 def colorize_syntax_error(self, text, pos):
793 text.tag_add("ERROR", pos)
794 char = text.get(pos)
795 if char and char in self.IDENTCHARS:
796 text.tag_add("ERROR", pos + " wordstart", pos)
797 if '\n' == text.get(pos): # error at line end
798 text.mark_set("insert", pos)
799 else:
800 text.mark_set("insert", pos + "+1c")
801 text.see(pos)
802
Steven M. Gavab1585412002-03-12 00:21:56 +0000803 def ResetFont(self):
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000804 "Update the text widgets' font if it is changed"
Kurt B. Kaiser83118c62002-06-24 17:03:37 +0000805 # Called from configDialog.py
Terry Jan Reedyd87d1682015-08-01 18:57:33 -0400806
807 self.text['font'] = idleConf.GetFont(self.root, 'main','EditorWindow')
Steven M. Gavab1585412002-03-12 00:21:56 +0000808
Kurt B. Kaiserb1754452005-11-18 22:05:48 +0000809 def RemoveKeybindings(self):
810 "Remove the keybindings before they are changed."
Kurt B. Kaiser83118c62002-06-24 17:03:37 +0000811 # Called from configDialog.py
Kurt B. Kaiser5a67f9b2005-11-22 01:47:14 +0000812 self.Bindings.default_keydefs = keydefs = idleConf.GetCurrentKeySet()
Steven M. Gavadbfe92c2002-03-18 02:38:44 +0000813 for event, keylist in keydefs.items():
Kurt B. Kaiserb1754452005-11-18 22:05:48 +0000814 self.text.event_delete(event, *keylist)
815 for extensionName in self.get_standard_extension_names():
Kurt B. Kaiser5a67f9b2005-11-22 01:47:14 +0000816 xkeydefs = idleConf.GetExtensionBindings(extensionName)
817 if xkeydefs:
818 for event, keylist in xkeydefs.items():
Kurt B. Kaiserb1754452005-11-18 22:05:48 +0000819 self.text.event_delete(event, *keylist)
820
821 def ApplyKeybindings(self):
822 "Update the keybindings after they are changed"
823 # Called from configDialog.py
Kurt B. Kaiser5a67f9b2005-11-22 01:47:14 +0000824 self.Bindings.default_keydefs = keydefs = idleConf.GetCurrentKeySet()
Steven M. Gavadbfe92c2002-03-18 02:38:44 +0000825 self.apply_bindings()
Kurt B. Kaiserb1754452005-11-18 22:05:48 +0000826 for extensionName in self.get_standard_extension_names():
Kurt B. Kaiser5a67f9b2005-11-22 01:47:14 +0000827 xkeydefs = idleConf.GetExtensionBindings(extensionName)
828 if xkeydefs:
829 self.apply_bindings(xkeydefs)
Steven M. Gavadbfe92c2002-03-18 02:38:44 +0000830 #update menu accelerators
Kurt B. Kaiser5a67f9b2005-11-22 01:47:14 +0000831 menuEventDict = {}
Steven M. Gavadbfe92c2002-03-18 02:38:44 +0000832 for menu in self.Bindings.menudefs:
Kurt B. Kaiser5a67f9b2005-11-22 01:47:14 +0000833 menuEventDict[menu[0]] = {}
Steven M. Gavadbfe92c2002-03-18 02:38:44 +0000834 for item in menu[1]:
835 if item:
Kurt B. Kaiser5a67f9b2005-11-22 01:47:14 +0000836 menuEventDict[menu[0]][prepstr(item[0])[1]] = item[1]
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000837 for menubarItem in self.menudict:
Kurt B. Kaiser5a67f9b2005-11-22 01:47:14 +0000838 menu = self.menudict[menubarItem]
Ned Deily8e8b9ba2013-07-20 15:06:26 -0700839 end = menu.index(END)
840 if end is None:
841 # Skip empty menus
842 continue
843 end += 1
Kurt B. Kaiser5a67f9b2005-11-22 01:47:14 +0000844 for index in range(0, end):
845 if menu.type(index) == 'command':
846 accel = menu.entrycget(index, 'accelerator')
Steven M. Gavadbfe92c2002-03-18 02:38:44 +0000847 if accel:
Kurt B. Kaiser5a67f9b2005-11-22 01:47:14 +0000848 itemName = menu.entrycget(index, 'label')
849 event = ''
Guido van Rossum811c4e02006-08-22 15:45:46 +0000850 if menubarItem in menuEventDict:
851 if itemName in menuEventDict[menubarItem]:
Kurt B. Kaiser5a67f9b2005-11-22 01:47:14 +0000852 event = menuEventDict[menubarItem][itemName]
Steven M. Gavadbfe92c2002-03-18 02:38:44 +0000853 if event:
Kurt B. Kaiser5a67f9b2005-11-22 01:47:14 +0000854 accel = get_accelerator(keydefs, event)
855 menu.entryconfig(index, accelerator=accel)
Steven M. Gavadbfe92c2002-03-18 02:38:44 +0000856
Kurt B. Kaiseracdef852005-01-31 03:34:26 +0000857 def set_notabs_indentwidth(self):
858 "Update the indentwidth if changed and not using tabs in this window"
859 # Called from configDialog.py
860 if not self.usetabs:
861 self.indentwidth = idleConf.GetOption('main', 'Indent','num-spaces',
862 type='int')
863
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000864 def reset_help_menu_entries(self):
865 "Update the additional help entries on the Help menu"
866 help_list = idleConf.GetAllExtraHelpSourcesList()
867 helpmenu = self.menudict['help']
868 # first delete the extra help entries, if any
869 helpmenu_length = helpmenu.index(END)
870 if helpmenu_length > self.base_helpmenu_length:
871 helpmenu.delete((self.base_helpmenu_length + 1), helpmenu_length)
872 # then rebuild them
873 if help_list:
874 helpmenu.add_separator()
875 for entry in help_list:
876 cmd = self.__extra_help_callback(entry[1])
877 helpmenu.add_command(label=entry[0], command=cmd)
878 # and update the menu dictionary
879 self.menudict['help'] = helpmenu
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000880
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000881 def __extra_help_callback(self, helpfile):
882 "Create a callback with the helpfile value frozen at definition time"
883 def display_extra_help(helpfile=helpfile):
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000884 if not helpfile.startswith(('www', 'http')):
Terry Reedy6739cc02011-01-01 02:25:36 +0000885 helpfile = os.path.normpath(helpfile)
Kurt B. Kaiser8aa23922004-07-15 04:54:57 +0000886 if sys.platform[:3] == 'win':
Terry Reedy6739cc02011-01-01 02:25:36 +0000887 try:
888 os.startfile(helpfile)
Andrew Svetlov2606a6f2012-12-19 14:33:35 +0200889 except OSError as why:
Terry Reedy6739cc02011-01-01 02:25:36 +0000890 tkMessageBox.showerror(title='Document Start Failure',
891 message=str(why), parent=self.text)
Kurt B. Kaiser8aa23922004-07-15 04:54:57 +0000892 else:
893 webbrowser.open(helpfile)
Kurt B. Kaiser8e92bf72003-01-14 22:03:31 +0000894 return display_extra_help
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000895
Kurt B. Kaisercf6f1b62004-04-11 03:16:07 +0000896 def update_recent_files_list(self, new_file=None):
897 "Load and update the recent files list and menus"
898 rf_list = []
899 if os.path.exists(self.recent_files_path):
Terry Jan Reedy95f34ab2013-08-04 15:39:03 -0400900 with open(self.recent_files_path, 'r',
901 encoding='utf_8', errors='replace') as rf_list_file:
Kurt B. Kaisercf6f1b62004-04-11 03:16:07 +0000902 rf_list = rf_list_file.readlines()
Kurt B. Kaisercf6f1b62004-04-11 03:16:07 +0000903 if new_file:
904 new_file = os.path.abspath(new_file) + '\n'
905 if new_file in rf_list:
906 rf_list.remove(new_file) # move to top
907 rf_list.insert(0, new_file)
908 # clean and save the recent files list
909 bad_paths = []
910 for path in rf_list:
911 if '\0' in path or not os.path.exists(path[0:-1]):
912 bad_paths.append(path)
913 rf_list = [path for path in rf_list if path not in bad_paths]
914 ulchars = "1234567890ABCDEFGHIJK"
915 rf_list = rf_list[0:len(ulchars)]
Steven M. Gava1d46e402002-03-27 08:40:46 +0000916 try:
Ned Deilyf505b742011-12-14 14:58:24 -0800917 with open(self.recent_files_path, 'w',
918 encoding='utf_8', errors='replace') as rf_file:
919 rf_file.writelines(rf_list)
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200920 except OSError as err:
Ned Deilyf505b742011-12-14 14:58:24 -0800921 if not getattr(self.root, "recentfilelist_error_displayed", False):
922 self.root.recentfilelist_error_displayed = True
923 tkMessageBox.showerror(title='IDLE Error',
924 message='Unable to update Recent Files list:\n%s'
925 % str(err),
926 parent=self.text)
Kurt B. Kaisercf6f1b62004-04-11 03:16:07 +0000927 # for each edit window instance, construct the recent files menu
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000928 for instance in self.top.instance_dict:
Kurt B. Kaisercf6f1b62004-04-11 03:16:07 +0000929 menu = instance.recent_files_menu
Ned Deily3aee9412012-05-29 10:43:36 -0700930 menu.delete(0, END) # clear, and rebuild:
Guilherme Polo1fff0082009-08-14 15:05:30 +0000931 for i, file_name in enumerate(rf_list):
932 file_name = file_name.rstrip() # zap \n
Martin v. Löwis307021f2005-11-27 16:59:04 +0000933 # make unicode string to display non-ASCII chars correctly
934 ufile_name = self._filename_to_unicode(file_name)
Kurt B. Kaisercf6f1b62004-04-11 03:16:07 +0000935 callback = instance.__recent_file_callback(file_name)
Martin v. Löwis307021f2005-11-27 16:59:04 +0000936 menu.add_command(label=ulchars[i] + " " + ufile_name,
Kurt B. Kaisercf6f1b62004-04-11 03:16:07 +0000937 command=callback,
938 underline=0)
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000939
Kurt B. Kaisercf6f1b62004-04-11 03:16:07 +0000940 def __recent_file_callback(self, file_name):
941 def open_recent_file(fn_closure=file_name):
942 self.io.open(editFile=fn_closure)
943 return open_recent_file
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000944
David Scherer7aced172000-08-15 01:13:23 +0000945 def saved_change_hook(self):
946 short = self.short_title()
947 long = self.long_title()
948 if short and long:
Terry Jan Reedy0726ddf2014-08-14 21:54:43 -0400949 title = short + " - " + long + _py_version
David Scherer7aced172000-08-15 01:13:23 +0000950 elif short:
951 title = short
952 elif long:
953 title = long
954 else:
955 title = "Untitled"
956 icon = short or long or title
957 if not self.get_saved():
958 title = "*%s*" % title
959 icon = "*%s" % icon
960 self.top.wm_title(title)
961 self.top.wm_iconname(icon)
962
963 def get_saved(self):
964 return self.undo.get_saved()
965
966 def set_saved(self, flag):
967 self.undo.set_saved(flag)
968
969 def reset_undo(self):
970 self.undo.reset_undo()
971
972 def short_title(self):
973 filename = self.io.filename
974 if filename:
975 filename = os.path.basename(filename)
Terry Jan Reedy94338de2014-01-23 00:36:46 -0500976 else:
977 filename = "Untitled"
Martin v. Löwis307021f2005-11-27 16:59:04 +0000978 # return unicode string to display non-ASCII chars correctly
Terry Jan Reedy0726ddf2014-08-14 21:54:43 -0400979 return self._filename_to_unicode(filename)
David Scherer7aced172000-08-15 01:13:23 +0000980
981 def long_title(self):
Martin v. Löwis307021f2005-11-27 16:59:04 +0000982 # return unicode string to display non-ASCII chars correctly
983 return self._filename_to_unicode(self.io.filename or "")
David Scherer7aced172000-08-15 01:13:23 +0000984
985 def center_insert_event(self, event):
986 self.center()
987
988 def center(self, mark="insert"):
989 text = self.text
990 top, bot = self.getwindowlines()
991 lineno = self.getlineno(mark)
992 height = bot - top
Kurt B. Kaiser220ecbc2002-09-16 02:13:15 +0000993 newtop = max(1, lineno - height//2)
David Scherer7aced172000-08-15 01:13:23 +0000994 text.yview(float(newtop))
995
996 def getwindowlines(self):
997 text = self.text
998 top = self.getlineno("@0,0")
999 bot = self.getlineno("@0,65535")
1000 if top == bot and text.winfo_height() == 1:
1001 # Geometry manager hasn't run yet
1002 height = int(text['height'])
1003 bot = top + height - 1
1004 return top, bot
1005
1006 def getlineno(self, mark="insert"):
1007 text = self.text
1008 return int(float(text.index(mark)))
1009
Kurt B. Kaiser1061e722003-01-04 01:43:53 +00001010 def get_geometry(self):
1011 "Return (width, height, x, y)"
1012 geom = self.top.wm_geometry()
1013 m = re.match(r"(\d+)x(\d+)\+(-?\d+)\+(-?\d+)", geom)
Kurt B. Kaiser66aaf742007-08-09 18:00:23 +00001014 return list(map(int, m.groups()))
Kurt B. Kaiser1061e722003-01-04 01:43:53 +00001015
David Scherer7aced172000-08-15 01:13:23 +00001016 def close_event(self, event):
1017 self.close()
1018
1019 def maybesave(self):
1020 if self.io:
Steven M. Gava67716b52002-02-26 02:31:03 +00001021 if not self.get_saved():
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +00001022 if self.top.state()!='normal':
Steven M. Gava67716b52002-02-26 02:31:03 +00001023 self.top.deiconify()
1024 self.top.lower()
1025 self.top.lift()
David Scherer7aced172000-08-15 01:13:23 +00001026 return self.io.maybesave()
1027
1028 def close(self):
David Scherer7aced172000-08-15 01:13:23 +00001029 reply = self.maybesave()
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +00001030 if str(reply) != "cancel":
David Scherer7aced172000-08-15 01:13:23 +00001031 self._close()
1032 return reply
1033
1034 def _close(self):
Steven M. Gava1d46e402002-03-27 08:40:46 +00001035 if self.io.filename:
Kurt B. Kaisercf6f1b62004-04-11 03:16:07 +00001036 self.update_recent_files_list(new_file=self.io.filename)
David Scherer7aced172000-08-15 01:13:23 +00001037 WindowList.unregister_callback(self.postwindowsmenu)
David Scherer7aced172000-08-15 01:13:23 +00001038 self.unload_extensions()
Guido van Rossum8ce8a782007-11-01 19:42:39 +00001039 self.io.close()
1040 self.io = None
1041 self.undo = None
David Scherer7aced172000-08-15 01:13:23 +00001042 if self.color:
Guido van Rossum8ce8a782007-11-01 19:42:39 +00001043 self.color.close(False)
1044 self.color = None
David Scherer7aced172000-08-15 01:13:23 +00001045 self.text = None
Kurt B. Kaiser610c7e02004-04-24 03:01:48 +00001046 self.tkinter_vars = None
Guido van Rossum8ce8a782007-11-01 19:42:39 +00001047 self.per.close()
1048 self.per = None
1049 self.top.destroy()
1050 if self.close_hook:
1051 # unless override: unregister from flist, terminate if last window
1052 self.close_hook()
David Scherer7aced172000-08-15 01:13:23 +00001053
1054 def load_extensions(self):
1055 self.extensions = {}
1056 self.load_standard_extensions()
1057
1058 def unload_extensions(self):
Kurt B. Kaisere0712772007-08-23 05:25:55 +00001059 for ins in list(self.extensions.values()):
David Scherer7aced172000-08-15 01:13:23 +00001060 if hasattr(ins, "close"):
1061 ins.close()
1062 self.extensions = {}
1063
1064 def load_standard_extensions(self):
1065 for name in self.get_standard_extension_names():
1066 try:
1067 self.load_extension(name)
1068 except:
Guido van Rossumbe19ed72007-02-09 05:37:30 +00001069 print("Failed to load extension", repr(name))
David Scherer7aced172000-08-15 01:13:23 +00001070 traceback.print_exc()
1071
1072 def get_standard_extension_names(self):
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +00001073 return idleConf.GetExtensions(editor_only=True)
David Scherer7aced172000-08-15 01:13:23 +00001074
1075 def load_extension(self, name):
Kurt B. Kaiserb00e89f2005-01-18 00:54:58 +00001076 try:
Brett Cannonaef82d32012-04-14 20:44:23 -04001077 try:
1078 mod = importlib.import_module('.' + name, package=__package__)
Terry Jan Reedy06313b72014-05-11 23:32:32 -04001079 except (ImportError, TypeError):
Brett Cannonaef82d32012-04-14 20:44:23 -04001080 mod = importlib.import_module(name)
Kurt B. Kaiserb00e89f2005-01-18 00:54:58 +00001081 except ImportError:
Guido van Rossumbe19ed72007-02-09 05:37:30 +00001082 print("\nFailed to import extension: ", name)
Guido van Rossum36e0a922007-07-20 04:05:57 +00001083 raise
David Scherer7aced172000-08-15 01:13:23 +00001084 cls = getattr(mod, name)
Kurt B. Kaiser4d5bc602004-06-06 01:29:22 +00001085 keydefs = idleConf.GetExtensionBindings(name)
1086 if hasattr(cls, "menudefs"):
1087 self.fill_menus(cls.menudefs, keydefs)
David Scherer7aced172000-08-15 01:13:23 +00001088 ins = cls(self)
1089 self.extensions[name] = ins
David Scherer7aced172000-08-15 01:13:23 +00001090 if keydefs:
1091 self.apply_bindings(keydefs)
Kurt B. Kaisere0712772007-08-23 05:25:55 +00001092 for vevent in keydefs:
Kurt B. Kaiser220ecbc2002-09-16 02:13:15 +00001093 methodname = vevent.replace("-", "_")
David Scherer7aced172000-08-15 01:13:23 +00001094 while methodname[:1] == '<':
1095 methodname = methodname[1:]
1096 while methodname[-1:] == '>':
1097 methodname = methodname[:-1]
1098 methodname = methodname + "_event"
1099 if hasattr(ins, methodname):
1100 self.text.bind(vevent, getattr(ins, methodname))
David Scherer7aced172000-08-15 01:13:23 +00001101
1102 def apply_bindings(self, keydefs=None):
1103 if keydefs is None:
1104 keydefs = self.Bindings.default_keydefs
1105 text = self.text
1106 text.keydefs = keydefs
1107 for event, keylist in keydefs.items():
1108 if keylist:
Raymond Hettinger931237e2003-07-09 18:48:24 +00001109 text.event_add(event, *keylist)
David Scherer7aced172000-08-15 01:13:23 +00001110
Kurt B. Kaiser610c7e02004-04-24 03:01:48 +00001111 def fill_menus(self, menudefs=None, keydefs=None):
Kurt B. Kaiser83118c62002-06-24 17:03:37 +00001112 """Add appropriate entries to the menus and submenus
1113
1114 Menus that are absent or None in self.menudict are ignored.
1115 """
Kurt B. Kaiser610c7e02004-04-24 03:01:48 +00001116 if menudefs is None:
1117 menudefs = self.Bindings.menudefs
David Scherer7aced172000-08-15 01:13:23 +00001118 if keydefs is None:
1119 keydefs = self.Bindings.default_keydefs
1120 menudict = self.menudict
1121 text = self.text
Kurt B. Kaiser610c7e02004-04-24 03:01:48 +00001122 for mname, entrylist in menudefs:
David Scherer7aced172000-08-15 01:13:23 +00001123 menu = menudict.get(mname)
1124 if not menu:
1125 continue
Kurt B. Kaiser610c7e02004-04-24 03:01:48 +00001126 for entry in entrylist:
1127 if not entry:
David Scherer7aced172000-08-15 01:13:23 +00001128 menu.add_separator()
1129 else:
Kurt B. Kaiser610c7e02004-04-24 03:01:48 +00001130 label, eventname = entry
David Scherer7aced172000-08-15 01:13:23 +00001131 checkbutton = (label[:1] == '!')
1132 if checkbutton:
1133 label = label[1:]
1134 underline, label = prepstr(label)
Kurt B. Kaiser610c7e02004-04-24 03:01:48 +00001135 accelerator = get_accelerator(keydefs, eventname)
1136 def command(text=text, eventname=eventname):
1137 text.event_generate(eventname)
David Scherer7aced172000-08-15 01:13:23 +00001138 if checkbutton:
Kurt B. Kaiser610c7e02004-04-24 03:01:48 +00001139 var = self.get_var_obj(eventname, BooleanVar)
David Scherer7aced172000-08-15 01:13:23 +00001140 menu.add_checkbutton(label=label, underline=underline,
1141 command=command, accelerator=accelerator,
1142 variable=var)
1143 else:
1144 menu.add_command(label=label, underline=underline,
Kurt B. Kaiser84f48032002-09-26 22:13:22 +00001145 command=command,
1146 accelerator=accelerator)
David Scherer7aced172000-08-15 01:13:23 +00001147
1148 def getvar(self, name):
Kurt B. Kaiser610c7e02004-04-24 03:01:48 +00001149 var = self.get_var_obj(name)
David Scherer7aced172000-08-15 01:13:23 +00001150 if var:
Kurt B. Kaiser610c7e02004-04-24 03:01:48 +00001151 value = var.get()
1152 return value
1153 else:
Kurt B. Kaiserad667422007-08-23 01:06:15 +00001154 raise NameError(name)
David Scherer7aced172000-08-15 01:13:23 +00001155
1156 def setvar(self, name, value, vartype=None):
Kurt B. Kaiser610c7e02004-04-24 03:01:48 +00001157 var = self.get_var_obj(name, vartype)
David Scherer7aced172000-08-15 01:13:23 +00001158 if var:
1159 var.set(value)
Kurt B. Kaiser610c7e02004-04-24 03:01:48 +00001160 else:
Kurt B. Kaiserad667422007-08-23 01:06:15 +00001161 raise NameError(name)
David Scherer7aced172000-08-15 01:13:23 +00001162
Kurt B. Kaiser610c7e02004-04-24 03:01:48 +00001163 def get_var_obj(self, name, vartype=None):
1164 var = self.tkinter_vars.get(name)
David Scherer7aced172000-08-15 01:13:23 +00001165 if not var and vartype:
Kurt B. Kaiser610c7e02004-04-24 03:01:48 +00001166 # create a Tkinter variable object with self.text as master:
1167 self.tkinter_vars[name] = var = vartype(self.text)
David Scherer7aced172000-08-15 01:13:23 +00001168 return var
1169
1170 # Tk implementations of "virtual text methods" -- each platform
1171 # reusing IDLE's support code needs to define these for its GUI's
1172 # flavor of widget.
1173
1174 # Is character at text_index in a Python string? Return 0 for
1175 # "guaranteed no", true for anything else. This info is expensive
1176 # to compute ab initio, but is probably already known by the
1177 # platform's colorizer.
1178
1179 def is_char_in_string(self, text_index):
1180 if self.color:
1181 # Return true iff colorizer hasn't (re)gotten this far
1182 # yet, or the character is tagged as being in a string
1183 return self.text.tag_prevrange("TODO", text_index) or \
1184 "STRING" in self.text.tag_names(text_index)
1185 else:
1186 # The colorizer is missing: assume the worst
1187 return 1
1188
1189 # If a selection is defined in the text widget, return (start,
1190 # end) as Tkinter text indices, otherwise return (None, None)
1191 def get_selection_indices(self):
1192 try:
1193 first = self.text.index("sel.first")
1194 last = self.text.index("sel.last")
1195 return first, last
1196 except TclError:
1197 return None, None
1198
1199 # Return the text widget's current view of what a tab stop means
1200 # (equivalent width in spaces).
1201
Kurt B. Kaiser105f60e2007-09-06 04:03:04 +00001202 def get_tk_tabwidth(self):
David Scherer7aced172000-08-15 01:13:23 +00001203 current = self.text['tabs'] or TK_TABWIDTH_DEFAULT
1204 return int(current)
1205
1206 # Set the text widget's current view of what a tab stop means.
1207
Kurt B. Kaiser105f60e2007-09-06 04:03:04 +00001208 def set_tk_tabwidth(self, newtabwidth):
David Scherer7aced172000-08-15 01:13:23 +00001209 text = self.text
Kurt B. Kaiser105f60e2007-09-06 04:03:04 +00001210 if self.get_tk_tabwidth() != newtabwidth:
1211 # Set text widget tab width
David Scherer7aced172000-08-15 01:13:23 +00001212 pixels = text.tk.call("font", "measure", text["font"],
1213 "-displayof", text.master,
Kurt B. Kaiserafdf71b2001-07-13 03:35:32 +00001214 "n" * newtabwidth)
David Scherer7aced172000-08-15 01:13:23 +00001215 text.configure(tabs=pixels)
1216
Guido van Rossum33d26892007-08-05 15:29:28 +00001217### begin autoindent code ### (configuration was moved to beginning of class)
1218
Kurt B. Kaiser105f60e2007-09-06 04:03:04 +00001219 def set_indentation_params(self, is_py_src, guess=True):
1220 if is_py_src and guess:
Kurt B. Kaisercb7a3832002-09-14 02:34:23 +00001221 i = self.guess_indent()
1222 if 2 <= i <= 8:
1223 self.indentwidth = i
1224 if self.indentwidth != self.tabwidth:
Kurt B. Kaiser6af44982005-01-19 00:22:59 +00001225 self.usetabs = False
Kurt B. Kaiser105f60e2007-09-06 04:03:04 +00001226 self.set_tk_tabwidth(self.tabwidth)
Kurt B. Kaisercb7a3832002-09-14 02:34:23 +00001227
1228 def smart_backspace_event(self, event):
1229 text = self.text
1230 first, last = self.get_selection_indices()
1231 if first and last:
1232 text.delete(first, last)
1233 text.mark_set("insert", first)
1234 return "break"
1235 # Delete whitespace left, until hitting a real char or closest
1236 # preceding virtual tab stop.
1237 chars = text.get("insert linestart", "insert")
1238 if chars == '':
1239 if text.compare("insert", ">", "1.0"):
1240 # easy: delete preceding newline
1241 text.delete("insert-1c")
1242 else:
1243 text.bell() # at start of buffer
1244 return "break"
1245 if chars[-1] not in " \t":
1246 # easy: delete preceding real char
1247 text.delete("insert-1c")
1248 return "break"
1249 # Ick. It may require *inserting* spaces if we back up over a
1250 # tab character! This is written to be clear, not fast.
Kurt B. Kaiser1b3c2692002-09-15 21:31:30 +00001251 tabwidth = self.tabwidth
1252 have = len(chars.expandtabs(tabwidth))
Kurt B. Kaisercb7a3832002-09-14 02:34:23 +00001253 assert have > 0
1254 want = ((have - 1) // self.indentwidth) * self.indentwidth
Kurt B. Kaiser4ada7ad2002-12-29 22:03:38 +00001255 # Debug prompt is multilined....
Terry Jan Reedy7f53aea2012-01-15 19:03:23 -05001256 if self.context_use_ps1:
1257 last_line_of_prompt = sys.ps1.split('\n')[-1]
1258 else:
1259 last_line_of_prompt = ''
Kurt B. Kaisercb7a3832002-09-14 02:34:23 +00001260 ncharsdeleted = 0
1261 while 1:
Kurt B. Kaiser4ada7ad2002-12-29 22:03:38 +00001262 if chars == last_line_of_prompt:
Kurt B. Kaiser1bdca5e2002-12-16 22:25:10 +00001263 break
Kurt B. Kaisercb7a3832002-09-14 02:34:23 +00001264 chars = chars[:-1]
1265 ncharsdeleted = ncharsdeleted + 1
Kurt B. Kaiser1b3c2692002-09-15 21:31:30 +00001266 have = len(chars.expandtabs(tabwidth))
Kurt B. Kaisercb7a3832002-09-14 02:34:23 +00001267 if have <= want or chars[-1] not in " \t":
1268 break
1269 text.undo_block_start()
1270 text.delete("insert-%dc" % ncharsdeleted, "insert")
1271 if have < want:
1272 text.insert("insert", ' ' * (want - have))
1273 text.undo_block_stop()
1274 return "break"
1275
1276 def smart_indent_event(self, event):
1277 # if intraline selection:
1278 # delete it
1279 # elif multiline selection:
Kurt B. Kaiser6af44982005-01-19 00:22:59 +00001280 # do indent-region
1281 # else:
1282 # indent one level
Kurt B. Kaisercb7a3832002-09-14 02:34:23 +00001283 text = self.text
1284 first, last = self.get_selection_indices()
1285 text.undo_block_start()
1286 try:
1287 if first and last:
1288 if index2line(first) != index2line(last):
1289 return self.indent_region_event(event)
1290 text.delete(first, last)
1291 text.mark_set("insert", first)
1292 prefix = text.get("insert linestart", "insert")
1293 raw, effective = classifyws(prefix, self.tabwidth)
1294 if raw == len(prefix):
1295 # only whitespace to the left
1296 self.reindent_to(effective + self.indentwidth)
1297 else:
Kurt B. Kaiser6af44982005-01-19 00:22:59 +00001298 # tab to the next 'stop' within or to right of line's text:
Kurt B. Kaisercb7a3832002-09-14 02:34:23 +00001299 if self.usetabs:
1300 pad = '\t'
1301 else:
Kurt B. Kaiser1b3c2692002-09-15 21:31:30 +00001302 effective = len(prefix.expandtabs(self.tabwidth))
Kurt B. Kaisercb7a3832002-09-14 02:34:23 +00001303 n = self.indentwidth
1304 pad = ' ' * (n - effective % n)
1305 text.insert("insert", pad)
1306 text.see("insert")
1307 return "break"
1308 finally:
1309 text.undo_block_stop()
1310
1311 def newline_and_indent_event(self, event):
1312 text = self.text
1313 first, last = self.get_selection_indices()
1314 text.undo_block_start()
1315 try:
1316 if first and last:
1317 text.delete(first, last)
1318 text.mark_set("insert", first)
1319 line = text.get("insert linestart", "insert")
1320 i, n = 0, len(line)
1321 while i < n and line[i] in " \t":
1322 i = i+1
1323 if i == n:
Kurt B. Kaiser4ada7ad2002-12-29 22:03:38 +00001324 # the cursor is in or at leading indentation in a continuation
1325 # line; just inject an empty line at the start
Kurt B. Kaisercb7a3832002-09-14 02:34:23 +00001326 text.insert("insert linestart", '\n')
1327 return "break"
1328 indent = line[:i]
Kurt B. Kaiser4ada7ad2002-12-29 22:03:38 +00001329 # strip whitespace before insert point unless it's in the prompt
Kurt B. Kaisercb7a3832002-09-14 02:34:23 +00001330 i = 0
Kurt B. Kaiser4ada7ad2002-12-29 22:03:38 +00001331 last_line_of_prompt = sys.ps1.split('\n')[-1]
1332 while line and line[-1] in " \t" and line != last_line_of_prompt:
Kurt B. Kaisercb7a3832002-09-14 02:34:23 +00001333 line = line[:-1]
1334 i = i+1
1335 if i:
1336 text.delete("insert - %d chars" % i, "insert")
1337 # strip whitespace after insert point
1338 while text.get("insert") in " \t":
1339 text.delete("insert")
1340 # start new line
1341 text.insert("insert", '\n')
1342
1343 # adjust indentation for continuations and block
1344 # open/close first need to find the last stmt
1345 lno = index2line(text.index('insert'))
1346 y = PyParse.Parser(self.indentwidth, self.tabwidth)
Kurt B. Kaiserb1754452005-11-18 22:05:48 +00001347 if not self.context_use_ps1:
1348 for context in self.num_context_lines:
1349 startat = max(lno - context, 1)
Brett Cannon0b70cca2006-08-25 02:59:59 +00001350 startatindex = repr(startat) + ".0"
Kurt B. Kaiserb1754452005-11-18 22:05:48 +00001351 rawtext = text.get(startatindex, "insert")
1352 y.set_str(rawtext)
1353 bod = y.find_good_parse_start(
1354 self.context_use_ps1,
1355 self._build_char_in_string_func(startatindex))
1356 if bod is not None or startat == 1:
1357 break
1358 y.set_lo(bod or 0)
1359 else:
1360 r = text.tag_prevrange("console", "insert")
1361 if r:
1362 startatindex = r[1]
1363 else:
1364 startatindex = "1.0"
Kurt B. Kaisercb7a3832002-09-14 02:34:23 +00001365 rawtext = text.get(startatindex, "insert")
1366 y.set_str(rawtext)
Kurt B. Kaiserb1754452005-11-18 22:05:48 +00001367 y.set_lo(0)
1368
Kurt B. Kaisercb7a3832002-09-14 02:34:23 +00001369 c = y.get_continuation_type()
1370 if c != PyParse.C_NONE:
1371 # The current stmt hasn't ended yet.
Kurt B. Kaiserb61602c2005-11-15 07:20:06 +00001372 if c == PyParse.C_STRING_FIRST_LINE:
1373 # after the first line of a string; do not indent at all
1374 pass
1375 elif c == PyParse.C_STRING_NEXT_LINES:
1376 # inside a string which started before this line;
1377 # just mimic the current indent
Kurt B. Kaisercb7a3832002-09-14 02:34:23 +00001378 text.insert("insert", indent)
1379 elif c == PyParse.C_BRACKET:
1380 # line up with the first (if any) element of the
1381 # last open bracket structure; else indent one
1382 # level beyond the indent of the line with the
1383 # last open bracket
1384 self.reindent_to(y.compute_bracket_indent())
1385 elif c == PyParse.C_BACKSLASH:
1386 # if more than one line in this stmt already, just
1387 # mimic the current indent; else if initial line
1388 # has a start on an assignment stmt, indent to
1389 # beyond leftmost =; else to beyond first chunk of
1390 # non-whitespace on initial line
1391 if y.get_num_lines_in_stmt() > 1:
1392 text.insert("insert", indent)
1393 else:
1394 self.reindent_to(y.compute_backslash_indent())
1395 else:
Walter Dörwald70a6b492004-02-12 17:35:32 +00001396 assert 0, "bogus continuation type %r" % (c,)
Kurt B. Kaisercb7a3832002-09-14 02:34:23 +00001397 return "break"
1398
1399 # This line starts a brand new stmt; indent relative to
1400 # indentation of initial line of closest preceding
1401 # interesting stmt.
1402 indent = y.get_base_indent_string()
1403 text.insert("insert", indent)
1404 if y.is_block_opener():
1405 self.smart_indent_event(event)
1406 elif indent and y.is_block_closer():
1407 self.smart_backspace_event(event)
1408 return "break"
1409 finally:
1410 text.see("insert")
1411 text.undo_block_stop()
1412
Kurt B. Kaisercb7a3832002-09-14 02:34:23 +00001413 # Our editwin provides a is_char_in_string function that works
1414 # with a Tk text index, but PyParse only knows about offsets into
1415 # a string. This builds a function for PyParse that accepts an
1416 # offset.
1417
1418 def _build_char_in_string_func(self, startindex):
1419 def inner(offset, _startindex=startindex,
1420 _icis=self.is_char_in_string):
1421 return _icis(_startindex + "+%dc" % offset)
1422 return inner
1423
1424 def indent_region_event(self, event):
1425 head, tail, chars, lines = self.get_region()
1426 for pos in range(len(lines)):
1427 line = lines[pos]
1428 if line:
1429 raw, effective = classifyws(line, self.tabwidth)
1430 effective = effective + self.indentwidth
1431 lines[pos] = self._make_blanks(effective) + line[raw:]
1432 self.set_region(head, tail, chars, lines)
1433 return "break"
1434
1435 def dedent_region_event(self, event):
1436 head, tail, chars, lines = self.get_region()
1437 for pos in range(len(lines)):
1438 line = lines[pos]
1439 if line:
1440 raw, effective = classifyws(line, self.tabwidth)
1441 effective = max(effective - self.indentwidth, 0)
1442 lines[pos] = self._make_blanks(effective) + line[raw:]
1443 self.set_region(head, tail, chars, lines)
1444 return "break"
1445
1446 def comment_region_event(self, event):
1447 head, tail, chars, lines = self.get_region()
1448 for pos in range(len(lines) - 1):
1449 line = lines[pos]
1450 lines[pos] = '##' + line
1451 self.set_region(head, tail, chars, lines)
1452
1453 def uncomment_region_event(self, event):
1454 head, tail, chars, lines = self.get_region()
1455 for pos in range(len(lines)):
1456 line = lines[pos]
1457 if not line:
1458 continue
1459 if line[:2] == '##':
1460 line = line[2:]
1461 elif line[:1] == '#':
1462 line = line[1:]
1463 lines[pos] = line
1464 self.set_region(head, tail, chars, lines)
1465
1466 def tabify_region_event(self, event):
1467 head, tail, chars, lines = self.get_region()
1468 tabwidth = self._asktabwidth()
Roger Serwy0ef392c2013-04-06 20:26:53 -05001469 if tabwidth is None: return
Kurt B. Kaisercb7a3832002-09-14 02:34:23 +00001470 for pos in range(len(lines)):
1471 line = lines[pos]
1472 if line:
1473 raw, effective = classifyws(line, tabwidth)
1474 ntabs, nspaces = divmod(effective, tabwidth)
1475 lines[pos] = '\t' * ntabs + ' ' * nspaces + line[raw:]
1476 self.set_region(head, tail, chars, lines)
1477
1478 def untabify_region_event(self, event):
1479 head, tail, chars, lines = self.get_region()
1480 tabwidth = self._asktabwidth()
Roger Serwy0ef392c2013-04-06 20:26:53 -05001481 if tabwidth is None: return
Kurt B. Kaisercb7a3832002-09-14 02:34:23 +00001482 for pos in range(len(lines)):
Kurt B. Kaiser1b3c2692002-09-15 21:31:30 +00001483 lines[pos] = lines[pos].expandtabs(tabwidth)
Kurt B. Kaisercb7a3832002-09-14 02:34:23 +00001484 self.set_region(head, tail, chars, lines)
1485
1486 def toggle_tabs_event(self, event):
1487 if self.askyesno(
1488 "Toggle tabs",
Kurt B. Kaiser6af44982005-01-19 00:22:59 +00001489 "Turn tabs " + ("on", "off")[self.usetabs] +
1490 "?\nIndent width " +
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001491 ("will be", "remains at")[self.usetabs] + " 8." +
1492 "\n Note: a tab is always 8 columns",
Kurt B. Kaisercb7a3832002-09-14 02:34:23 +00001493 parent=self.text):
1494 self.usetabs = not self.usetabs
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001495 # Try to prevent inconsistent indentation.
1496 # User must change indent width manually after using tabs.
1497 self.indentwidth = 8
Kurt B. Kaisercb7a3832002-09-14 02:34:23 +00001498 return "break"
1499
Kurt B. Kaiser6af44982005-01-19 00:22:59 +00001500 # XXX this isn't bound to anything -- see tabwidth comments
1501## def change_tabwidth_event(self, event):
1502## new = self._asktabwidth()
1503## if new != self.tabwidth:
1504## self.tabwidth = new
1505## self.set_indentation_params(0, guess=0)
1506## return "break"
Kurt B. Kaisercb7a3832002-09-14 02:34:23 +00001507
1508 def change_indentwidth_event(self, event):
1509 new = self.askinteger(
1510 "Indent width",
Kurt B. Kaiser6af44982005-01-19 00:22:59 +00001511 "New indent width (2-16)\n(Always use 8 when using tabs)",
Kurt B. Kaisercb7a3832002-09-14 02:34:23 +00001512 parent=self.text,
1513 initialvalue=self.indentwidth,
1514 minvalue=2,
1515 maxvalue=16)
Kurt B. Kaiser6af44982005-01-19 00:22:59 +00001516 if new and new != self.indentwidth and not self.usetabs:
Kurt B. Kaisercb7a3832002-09-14 02:34:23 +00001517 self.indentwidth = new
1518 return "break"
1519
1520 def get_region(self):
1521 text = self.text
1522 first, last = self.get_selection_indices()
1523 if first and last:
1524 head = text.index(first + " linestart")
1525 tail = text.index(last + "-1c lineend +1c")
1526 else:
1527 head = text.index("insert linestart")
1528 tail = text.index("insert lineend +1c")
1529 chars = text.get(head, tail)
Kurt B. Kaiser1b3c2692002-09-15 21:31:30 +00001530 lines = chars.split("\n")
Kurt B. Kaisercb7a3832002-09-14 02:34:23 +00001531 return head, tail, chars, lines
1532
1533 def set_region(self, head, tail, chars, lines):
1534 text = self.text
Kurt B. Kaiser1b3c2692002-09-15 21:31:30 +00001535 newchars = "\n".join(lines)
Kurt B. Kaisercb7a3832002-09-14 02:34:23 +00001536 if newchars == chars:
1537 text.bell()
1538 return
1539 text.tag_remove("sel", "1.0", "end")
1540 text.mark_set("insert", head)
1541 text.undo_block_start()
1542 text.delete(head, tail)
1543 text.insert(head, newchars)
1544 text.undo_block_stop()
1545 text.tag_add("sel", head, "insert")
1546
1547 # Make string that displays as n leading blanks.
1548
1549 def _make_blanks(self, n):
1550 if self.usetabs:
1551 ntabs, nspaces = divmod(n, self.tabwidth)
1552 return '\t' * ntabs + ' ' * nspaces
1553 else:
1554 return ' ' * n
1555
1556 # Delete from beginning of line to insert point, then reinsert
1557 # column logical (meaning use tabs if appropriate) spaces.
1558
1559 def reindent_to(self, column):
1560 text = self.text
1561 text.undo_block_start()
1562 if text.compare("insert linestart", "!=", "insert"):
1563 text.delete("insert linestart", "insert")
1564 if column:
1565 text.insert("insert", self._make_blanks(column))
1566 text.undo_block_stop()
1567
1568 def _asktabwidth(self):
1569 return self.askinteger(
1570 "Tab width",
Kurt B. Kaiserca7329c2005-06-12 05:19:23 +00001571 "Columns per tab? (2-16)",
Kurt B. Kaisercb7a3832002-09-14 02:34:23 +00001572 parent=self.text,
1573 initialvalue=self.indentwidth,
1574 minvalue=2,
Roger Serwy0ef392c2013-04-06 20:26:53 -05001575 maxvalue=16)
Kurt B. Kaisercb7a3832002-09-14 02:34:23 +00001576
1577 # Guess indentwidth from text content.
1578 # Return guessed indentwidth. This should not be believed unless
1579 # it's in a reasonable range (e.g., it will be 0 if no indented
1580 # blocks are found).
1581
1582 def guess_indent(self):
1583 opener, indented = IndentSearcher(self.text, self.tabwidth).run()
1584 if opener and indented:
1585 raw, indentsmall = classifyws(opener, self.tabwidth)
1586 raw, indentlarge = classifyws(indented, self.tabwidth)
1587 else:
1588 indentsmall = indentlarge = 0
1589 return indentlarge - indentsmall
1590
1591# "line.col" -> line, as an int
1592def index2line(index):
1593 return int(float(index))
1594
1595# Look at the leading whitespace in s.
1596# Return pair (# of leading ws characters,
1597# effective # of leading blanks after expanding
1598# tabs to width tabwidth)
1599
1600def classifyws(s, tabwidth):
1601 raw = effective = 0
1602 for ch in s:
1603 if ch == ' ':
1604 raw = raw + 1
1605 effective = effective + 1
1606 elif ch == '\t':
1607 raw = raw + 1
1608 effective = (effective // tabwidth + 1) * tabwidth
1609 else:
1610 break
1611 return raw, effective
1612
1613import tokenize
1614_tokenize = tokenize
1615del tokenize
1616
Kurt B. Kaiserdcba6622004-12-21 22:10:32 +00001617class IndentSearcher(object):
Kurt B. Kaisercb7a3832002-09-14 02:34:23 +00001618
1619 # .run() chews over the Text widget, looking for a block opener
1620 # and the stmt following it. Returns a pair,
1621 # (line containing block opener, line containing stmt)
1622 # Either or both may be None.
1623
1624 def __init__(self, text, tabwidth):
1625 self.text = text
1626 self.tabwidth = tabwidth
1627 self.i = self.finished = 0
1628 self.blkopenline = self.indentedline = None
1629
1630 def readline(self):
1631 if self.finished:
1632 return ""
1633 i = self.i = self.i + 1
Walter Dörwald70a6b492004-02-12 17:35:32 +00001634 mark = repr(i) + ".0"
Kurt B. Kaisercb7a3832002-09-14 02:34:23 +00001635 if self.text.compare(mark, ">=", "end"):
1636 return ""
1637 return self.text.get(mark, mark + " lineend+1c")
1638
1639 def tokeneater(self, type, token, start, end, line,
1640 INDENT=_tokenize.INDENT,
1641 NAME=_tokenize.NAME,
1642 OPENERS=('class', 'def', 'for', 'if', 'try', 'while')):
1643 if self.finished:
1644 pass
1645 elif type == NAME and token in OPENERS:
1646 self.blkopenline = line
1647 elif type == INDENT and self.blkopenline:
1648 self.indentedline = line
1649 self.finished = 1
1650
1651 def run(self):
1652 save_tabsize = _tokenize.tabsize
1653 _tokenize.tabsize = self.tabwidth
1654 try:
1655 try:
Trent Nelson428de652008-03-18 22:41:35 +00001656 tokens = _tokenize.generate_tokens(self.readline)
1657 for token in tokens:
1658 self.tokeneater(*token)
Serhiy Storchaka07e0e062012-12-27 21:38:04 +02001659 except (_tokenize.TokenError, SyntaxError):
Kurt B. Kaisercb7a3832002-09-14 02:34:23 +00001660 # since we cut off the tokenizer early, we can trigger
1661 # spurious errors
1662 pass
1663 finally:
1664 _tokenize.tabsize = save_tabsize
1665 return self.blkopenline, self.indentedline
1666
1667### end autoindent code ###
1668
David Scherer7aced172000-08-15 01:13:23 +00001669def prepstr(s):
1670 # Helper to extract the underscore from a string, e.g.
1671 # prepstr("Co_py") returns (2, "Copy").
Kurt B. Kaiser220ecbc2002-09-16 02:13:15 +00001672 i = s.find('_')
David Scherer7aced172000-08-15 01:13:23 +00001673 if i >= 0:
1674 s = s[:i] + s[i+1:]
1675 return i, s
1676
1677
1678keynames = {
1679 'bracketleft': '[',
1680 'bracketright': ']',
1681 'slash': '/',
1682}
1683
Kurt B. Kaiser610c7e02004-04-24 03:01:48 +00001684def get_accelerator(keydefs, eventname):
1685 keylist = keydefs.get(eventname)
Ned Deily70063932011-01-29 18:29:01 +00001686 # issue10940: temporary workaround to prevent hang with OS X Cocoa Tk 8.5
1687 # if not keylist:
Ned Deilyb7601672014-03-27 20:49:14 -07001688 if (not keylist) or (macosxSupport.isCocoaTk() and eventname in {
Ned Deily70063932011-01-29 18:29:01 +00001689 "<<open-module>>",
1690 "<<goto-line>>",
1691 "<<change-indentwidth>>"}):
David Scherer7aced172000-08-15 01:13:23 +00001692 return ""
1693 s = keylist[0]
Kurt B. Kaiser220ecbc2002-09-16 02:13:15 +00001694 s = re.sub(r"-[a-z]\b", lambda m: m.group().upper(), s)
David Scherer7aced172000-08-15 01:13:23 +00001695 s = re.sub(r"\b\w+\b", lambda m: keynames.get(m.group(), m.group()), s)
1696 s = re.sub("Key-", "", s)
1697 s = re.sub("Cancel","Ctrl-Break",s) # dscherer@cmu.edu
1698 s = re.sub("Control-", "Ctrl-", s)
1699 s = re.sub("-", "+", s)
1700 s = re.sub("><", " ", s)
1701 s = re.sub("<", "", s)
1702 s = re.sub(">", "", s)
1703 return s
1704
1705
1706def fixwordbreaks(root):
1707 # Make sure that Tk's double-click and next/previous word
1708 # operations use our definition of a word (i.e. an identifier)
1709 tk = root.tk
1710 tk.call('tcl_wordBreakAfter', 'a b', 0) # make sure word.tcl is loaded
1711 tk.call('set', 'tcl_wordchars', '[a-zA-Z0-9_]')
1712 tk.call('set', 'tcl_nonwordchars', '[^a-zA-Z0-9_]')
1713
1714
Terry Jan Reedycd567362014-10-17 01:31:35 -04001715def _editor_window(parent): # htest #
1716 # error if close master window first - timer event, after script
Terry Jan Reedy06313b72014-05-11 23:32:32 -04001717 root = parent
David Scherer7aced172000-08-15 01:13:23 +00001718 fixwordbreaks(root)
David Scherer7aced172000-08-15 01:13:23 +00001719 if sys.argv[1:]:
1720 filename = sys.argv[1]
1721 else:
1722 filename = None
Terry Jan Reedy06313b72014-05-11 23:32:32 -04001723 macosxSupport.setupApp(root, None)
David Scherer7aced172000-08-15 01:13:23 +00001724 edit = EditorWindow(root=root, filename=filename)
Terry Jan Reedya2fc99e2014-05-25 18:44:05 -04001725 edit.text.bind("<<close-all-windows>>", edit.close_event)
Terry Jan Reedycd567362014-10-17 01:31:35 -04001726 # Does not stop error, neither does following
1727 # edit.text.bind("<<close-window>>", edit.close_event)
David Scherer7aced172000-08-15 01:13:23 +00001728
1729if __name__ == '__main__':
Terry Jan Reedy06313b72014-05-11 23:32:32 -04001730 from idlelib.idle_test.htest import run
Terry Jan Reedy5d46ab12015-09-20 19:57:13 -04001731 run(_editor_window)