Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(5)

Delta Between Two Patch Sets: Lib/idlelib/EditorWindow.py

Issue 13884: IDLE 2.6.5 Recent Files undocks
Left Patch Set: Created 8 years ago
Right Patch Set: Created 4 years, 6 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « no previous file | Lib/idlelib/help.txt » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
1 import sys 1 import sys
2 import os 2 import os
3 import platform
3 import re 4 import re
4 import string
5 import imp 5 import imp
6 from tkinter import * 6 from Tkinter import *
7 import tkinter.simpledialog as tkSimpleDialog 7 import tkSimpleDialog
8 import tkinter.messagebox as tkMessageBox 8 import tkMessageBox
9 import traceback
10 import webbrowser 9 import webbrowser
11 10
12 from idlelib.MultiCall import MultiCallCreator 11 from idlelib.MultiCall import MultiCallCreator
13 from idlelib import idlever
14 from idlelib import WindowList 12 from idlelib import WindowList
15 from idlelib import SearchDialog 13 from idlelib import SearchDialog
16 from idlelib import GrepDialog 14 from idlelib import GrepDialog
17 from idlelib import ReplaceDialog 15 from idlelib import ReplaceDialog
18 from idlelib import PyParse 16 from idlelib import PyParse
19 from idlelib.configHandler import idleConf 17 from idlelib.configHandler import idleConf
20 from idlelib import aboutDialog, textView, configDialog 18 from idlelib import aboutDialog, textView, configDialog
21 from idlelib import macosxSupport 19 from idlelib import macosxSupport
22 20
23 # The default tab setting for a Text widget, in average-width characters. 21 # The default tab setting for a Text widget, in average-width characters.
24 TK_TABWIDTH_DEFAULT = 8 22 TK_TABWIDTH_DEFAULT = 8
23
24 _py_version = ' (%s)' % platform.python_version()
25 25
26 def _sphinx_version(): 26 def _sphinx_version():
27 "Format sys.version_info to produce the Sphinx version string used to instal l the chm docs" 27 "Format sys.version_info to produce the Sphinx version string used to instal l the chm docs"
28 major, minor, micro, level, serial = sys.version_info 28 major, minor, micro, level, serial = sys.version_info
29 release = '%s%s' % (major, minor) 29 release = '%s%s' % (major, minor)
30 if micro: 30 if micro:
31 release += '%s' % (micro,) 31 release += '%s' % (micro,)
32 if level == 'candidate': 32 if level == 'candidate':
33 release += 'rc%s' % (serial,) 33 release += 'rc%s' % (serial,)
34 elif level != 'final': 34 elif level != 'final':
35 release += '%s%s' % (level[0], serial) 35 release += '%s%s' % (level[0], serial)
36 return release 36 return release
37 37
38 def _find_module(fullname, path=None): 38 def _find_module(fullname, path=None):
39 """Version of imp.find_module() that handles hierarchical module names""" 39 """Version of imp.find_module() that handles hierarchical module names"""
40 40
41 file = None 41 file = None
42 for tgt in fullname.split('.'): 42 for tgt in fullname.split('.'):
43 if file is not None: 43 if file is not None:
44 file.close() # close intermediate files 44 file.close() # close intermediate files
45 (file, filename, descr) = imp.find_module(tgt, path) 45 (file, filename, descr) = imp.find_module(tgt, path)
46 if descr[2] == imp.PY_SOURCE: 46 if descr[2] == imp.PY_SOURCE:
47 break # find but not load the source file 47 break # find but not load the source file
48 module = imp.load_module(tgt, file, filename, descr) 48 module = imp.load_module(tgt, file, filename, descr)
49 try: 49 try:
50 path = module.__path__ 50 path = module.__path__
51 except AttributeError: 51 except AttributeError:
52 raise ImportError('No source for module ' + module.__name__) 52 raise ImportError, 'No source for module ' + module.__name__
53 if descr[2] != imp.PY_SOURCE: 53 if descr[2] != imp.PY_SOURCE:
54 # If all of the above fails and didn't raise an exception,fallback 54 # If all of the above fails and didn't raise an exception,fallback
55 # to a straight import which can find __init__.py in a package. 55 # to a straight import which can find __init__.py in a package.
56 m = __import__(fullname) 56 m = __import__(fullname)
57 try: 57 try:
58 filename = m.__file__ 58 filename = m.__file__
59 except AttributeError: 59 except AttributeError:
60 pass 60 pass
61 else: 61 else:
62 file = None 62 file = None
63 descr = os.path.splitext(filename)[1], None, imp.PY_SOURCE 63 base, ext = os.path.splitext(filename)
64 if ext == '.pyc':
65 ext = '.py'
66 filename = base + ext
67 descr = filename, None, imp.PY_SOURCE
64 return file, filename, descr 68 return file, filename, descr
69
70
71 class HelpDialog(object):
72
73 def __init__(self):
74 self.parent = None # parent of help window
75 self.dlg = None # the help window iteself
76
77 def display(self, parent, near=None):
78 """ Display the help dialog.
79
80 parent - parent widget for the help window
81
82 near - a Toplevel widget (e.g. EditorWindow or PyShell)
83 to use as a reference for placing the help window
84 """
85 if self.dlg is None:
86 self.show_dialog(parent)
87 if near:
88 self.nearwindow(near)
89
90 def show_dialog(self, parent):
91 self.parent = parent
92 fn=os.path.join(os.path.abspath(os.path.dirname(__file__)),'help.txt')
93 self.dlg = dlg = textView.view_file(parent,'Help',fn, modal=False)
94 dlg.bind('<Destroy>', self.destroy, '+')
95
96 def nearwindow(self, near):
97 # Place the help dialog near the window specified by parent.
98 # Note - this may not reposition the window in Metacity
99 # if "/apps/metacity/general/disable_workarounds" is enabled
100 dlg = self.dlg
101 geom = (near.winfo_rootx() + 10, near.winfo_rooty() + 10)
102 dlg.withdraw()
103 dlg.geometry("=+%d+%d" % geom)
104 dlg.deiconify()
105 dlg.lift()
106
107 def destroy(self, ev=None):
108 self.dlg = None
109 self.parent = None
110
111 helpDialog = HelpDialog() # singleton instance
112 def _help_dialog(parent): # wrapper for htest
113 helpDialog.show_dialog(parent)
114
65 115
66 class EditorWindow(object): 116 class EditorWindow(object):
67 from idlelib.Percolator import Percolator 117 from idlelib.Percolator import Percolator
68 from idlelib.ColorDelegator import ColorDelegator 118 from idlelib.ColorDelegator import ColorDelegator
69 from idlelib.UndoDelegator import UndoDelegator 119 from idlelib.UndoDelegator import UndoDelegator
70 from idlelib.IOBinding import IOBinding, filesystemencoding, encoding 120 from idlelib.IOBinding import IOBinding, filesystemencoding, encoding
71 from idlelib import Bindings 121 from idlelib import Bindings
72 from tkinter import Toplevel 122 from Tkinter import Toplevel
73 from idlelib.MultiStatusBar import MultiStatusBar 123 from idlelib.MultiStatusBar import MultiStatusBar
74 124
75 help_url = None 125 help_url = None
76 126
77 def __init__(self, flist=None, filename=None, key=None, root=None): 127 def __init__(self, flist=None, filename=None, key=None, root=None):
78 if EditorWindow.help_url is None: 128 if EditorWindow.help_url is None:
79 dochome = os.path.join(sys.prefix, 'Doc', 'index.html') 129 dochome = os.path.join(sys.prefix, 'Doc', 'index.html')
80 if sys.platform.count('linux'): 130 if sys.platform.count('linux'):
81 # look for html docs in a couple of standard places 131 # look for html docs in a couple of standard places
82 pyver = 'python-docs-' + '%s.%s.%s' % sys.version_info[:3] 132 pyver = 'python-docs-' + '%s.%s.%s' % sys.version_info[:3]
83 if os.path.isdir('/var/www/html/python/'): # "python2" rpm 133 if os.path.isdir('/var/www/html/python/'): # "python2" rpm
84 dochome = '/var/www/html/python/index.html' 134 dochome = '/var/www/html/python/index.html'
85 else: 135 else:
86 basepath = '/usr/share/doc/' # standard location 136 basepath = '/usr/share/doc/' # standard location
87 dochome = os.path.join(basepath, pyver, 137 dochome = os.path.join(basepath, pyver,
88 'Doc', 'index.html') 138 'Doc', 'index.html')
89 elif sys.platform[:3] == 'win': 139 elif sys.platform[:3] == 'win':
90 chmfile = os.path.join(sys.prefix, 'Doc', 140 chmfile = os.path.join(sys.prefix, 'Doc',
91 'Python%s.chm' % _sphinx_version()) 141 'Python%s.chm' % _sphinx_version())
92 if os.path.isfile(chmfile): 142 if os.path.isfile(chmfile):
93 dochome = chmfile 143 dochome = chmfile
94 elif macosxSupport.runningAsOSXApp(): 144 elif sys.platform == 'darwin':
95 # documentation is stored inside the python framework 145 # documentation may be stored inside a python framework
96 dochome = os.path.join(sys.prefix, 146 dochome = os.path.join(sys.prefix,
97 'Resources/English.lproj/Documentation/index.html') 147 'Resources/English.lproj/Documentation/index.html')
98 dochome = os.path.normpath(dochome) 148 dochome = os.path.normpath(dochome)
99 if os.path.isfile(dochome): 149 if os.path.isfile(dochome):
100 EditorWindow.help_url = dochome 150 EditorWindow.help_url = dochome
101 if sys.platform == 'darwin': 151 if sys.platform == 'darwin':
102 # Safari requires real file:-URLs 152 # Safari requires real file:-URLs
103 EditorWindow.help_url = 'file://' + EditorWindow.help_url 153 EditorWindow.help_url = 'file://' + EditorWindow.help_url
104 else: 154 else:
105 EditorWindow.help_url = "http://docs.python.org/%d.%d" % sys.ver sion_info[:2] 155 EditorWindow.help_url = "https://docs.python.org/%d.%d/" % sys.v ersion_info[:2]
106 currentTheme=idleConf.CurrentTheme()
107 self.flist = flist 156 self.flist = flist
108 root = root or flist.root 157 root = root or flist.root
109 self.root = root 158 self.root = root
110 try: 159 try:
111 sys.ps1 160 sys.ps1
112 except AttributeError: 161 except AttributeError:
113 sys.ps1 = '>>> ' 162 sys.ps1 = '>>> '
114 self.menubar = Menu(root) 163 self.menubar = Menu(root)
115 self.top = top = WindowList.ListedToplevel(root, menu=self.menubar) 164 self.top = top = WindowList.ListedToplevel(root, menu=self.menubar)
116 if flist: 165 if flist:
117 self.tkinter_vars = flist.vars 166 self.tkinter_vars = flist.vars
118 #self.top.instance_dict makes flist.inversedict available to 167 #self.top.instance_dict makes flist.inversedict available to
119 #configDialog.py so it can access all EditorWindow instances 168 #configDialog.py so it can access all EditorWindow instances
120 self.top.instance_dict = flist.inversedict 169 self.top.instance_dict = flist.inversedict
121 else: 170 else:
122 self.tkinter_vars = {} # keys: Tkinter event names 171 self.tkinter_vars = {} # keys: Tkinter event names
123 # values: Tkinter variable instances 172 # values: Tkinter variable instances
124 self.top.instance_dict = {} 173 self.top.instance_dict = {}
125 self.recent_files_path = os.path.join(idleConf.GetUserCfgDir(), 174 self.recent_files_path = os.path.join(idleConf.GetUserCfgDir(),
126 'recent-files.lst') 175 'recent-files.lst')
127 self.text_frame = text_frame = Frame(top) 176 self.text_frame = text_frame = Frame(top)
128 self.vbar = vbar = Scrollbar(text_frame, name='vbar') 177 self.vbar = vbar = Scrollbar(text_frame, name='vbar')
129 self.width = idleConf.GetOption('main','EditorWindow','width') 178 self.width = idleConf.GetOption('main','EditorWindow','width', type='int ')
130 text_options = { 179 text_options = {
131 'name': 'text', 180 'name': 'text',
132 'padx': 5, 181 'padx': 5,
133 'wrap': 'none', 182 'wrap': 'none',
134 'width': self.width, 183 'width': self.width,
135 'height': idleConf.GetOption('main', 'EditorWindow', 'height')} 184 'height': idleConf.GetOption('main', 'EditorWindow', 'height', t ype='int')}
136 if TkVersion >= 8.5: 185 if TkVersion >= 8.5:
137 # Starting with tk 8.5 we have to set the new tabstyle option 186 # Starting with tk 8.5 we have to set the new tabstyle option
138 # to 'wordprocessor' to achieve the same display of tabs as in 187 # to 'wordprocessor' to achieve the same display of tabs as in
139 # older tk versions. 188 # older tk versions.
140 text_options['tabstyle'] = 'wordprocessor' 189 text_options['tabstyle'] = 'wordprocessor'
141 self.text = text = MultiCallCreator(Text)(text_frame, **text_options) 190 self.text = text = MultiCallCreator(Text)(text_frame, **text_options)
142 self.top.focused_widget = self.text 191 self.top.focused_widget = self.text
143 192
144 self.createmenubar() 193 self.createmenubar()
145 self.apply_bindings() 194 self.apply_bindings()
146 195
147 self.top.protocol("WM_DELETE_WINDOW", self.close) 196 self.top.protocol("WM_DELETE_WINDOW", self.close)
148 self.top.bind("<<close-window>>", self.close_event) 197 self.top.bind("<<close-window>>", self.close_event)
149 if macosxSupport.runningAsOSXApp(): 198 if macosxSupport.isAquaTk():
150 # Command-W on editorwindows doesn't work without this. 199 # Command-W on editorwindows doesn't work without this.
151 text.bind('<<close-window>>', self.close_event) 200 text.bind('<<close-window>>', self.close_event)
152 # Some OS X systems have only one mouse button, 201 # Some OS X systems have only one mouse button,
153 # so use control-click for pulldown menus there. 202 # so use control-click for pulldown menus there.
154 # (Note, AquaTk defines <2> as the right button if 203 # (Note, AquaTk defines <2> as the right button if
155 # present and the Tk Text widget already binds <2>.) 204 # present and the Tk Text widget already binds <2>.)
156 text.bind("<Control-Button-1>",self.right_menu_event) 205 text.bind("<Control-Button-1>",self.right_menu_event)
157 else: 206 else:
158 # Elsewhere, use right-click for pulldown menus. 207 # Elsewhere, use right-click for pulldown menus.
159 text.bind("<3>",self.right_menu_event) 208 text.bind("<3>",self.right_menu_event)
160 text.bind("<<cut>>", self.cut) 209 text.bind("<<cut>>", self.cut)
161 text.bind("<<copy>>", self.copy) 210 text.bind("<<copy>>", self.copy)
162 text.bind("<<paste>>", self.paste) 211 text.bind("<<paste>>", self.paste)
163 text.bind("<<center-insert>>", self.center_insert_event) 212 text.bind("<<center-insert>>", self.center_insert_event)
164 text.bind("<<help>>", self.help_dialog) 213 text.bind("<<help>>", self.help_dialog)
165 text.bind("<<python-docs>>", self.python_docs) 214 text.bind("<<python-docs>>", self.python_docs)
166 text.bind("<<about-idle>>", self.about_dialog) 215 text.bind("<<about-idle>>", self.about_dialog)
167 text.bind("<<open-config-dialog>>", self.config_dialog) 216 text.bind("<<open-config-dialog>>", self.config_dialog)
217 text.bind("<<open-config-extensions-dialog>>",
218 self.config_extensions_dialog)
168 text.bind("<<open-module>>", self.open_module) 219 text.bind("<<open-module>>", self.open_module)
169 text.bind("<<do-nothing>>", lambda event: "break") 220 text.bind("<<do-nothing>>", lambda event: "break")
170 text.bind("<<select-all>>", self.select_all) 221 text.bind("<<select-all>>", self.select_all)
171 text.bind("<<remove-selection>>", self.remove_selection) 222 text.bind("<<remove-selection>>", self.remove_selection)
172 text.bind("<<find>>", self.find_event) 223 text.bind("<<find>>", self.find_event)
173 text.bind("<<find-again>>", self.find_again_event) 224 text.bind("<<find-again>>", self.find_again_event)
174 text.bind("<<find-in-files>>", self.find_in_files_event) 225 text.bind("<<find-in-files>>", self.find_in_files_event)
175 text.bind("<<find-selection>>", self.find_selection_event) 226 text.bind("<<find-selection>>", self.find_selection_event)
176 text.bind("<<replace>>", self.replace_event) 227 text.bind("<<replace>>", self.replace_event)
177 text.bind("<<goto-line>>", self.goto_line_event) 228 text.bind("<<goto-line>>", self.goto_line_event)
(...skipping 24 matching lines...) Expand all
202 text.bind("<<open-path-browser>>", self.open_path_browser) 253 text.bind("<<open-path-browser>>", self.open_path_browser)
203 254
204 self.set_status_bar() 255 self.set_status_bar()
205 vbar['command'] = text.yview 256 vbar['command'] = text.yview
206 vbar.pack(side=RIGHT, fill=Y) 257 vbar.pack(side=RIGHT, fill=Y)
207 text['yscrollcommand'] = vbar.set 258 text['yscrollcommand'] = vbar.set
208 fontWeight = 'normal' 259 fontWeight = 'normal'
209 if idleConf.GetOption('main', 'EditorWindow', 'font-bold', type='bool'): 260 if idleConf.GetOption('main', 'EditorWindow', 'font-bold', type='bool'):
210 fontWeight='bold' 261 fontWeight='bold'
211 text.config(font=(idleConf.GetOption('main', 'EditorWindow', 'font'), 262 text.config(font=(idleConf.GetOption('main', 'EditorWindow', 'font'),
212 idleConf.GetOption('main', 'EditorWindow', 'font-size' ), 263 idleConf.GetOption('main', 'EditorWindow',
264 'font-size', type='int'),
213 fontWeight)) 265 fontWeight))
214 text_frame.pack(side=LEFT, fill=BOTH, expand=1) 266 text_frame.pack(side=LEFT, fill=BOTH, expand=1)
215 text.pack(side=TOP, fill=BOTH, expand=1) 267 text.pack(side=TOP, fill=BOTH, expand=1)
216 text.focus_set() 268 text.focus_set()
217 269
218 # usetabs true -> literal tab characters are used by indent and 270 # usetabs true -> literal tab characters are used by indent and
219 # dedent cmds, possibly mixed with spaces if 271 # dedent cmds, possibly mixed with spaces if
220 # indentwidth is not a multiple of tabwidth, 272 # indentwidth is not a multiple of tabwidth,
221 # which will cause Tabnanny to nag! 273 # which will cause Tabnanny to nag!
222 # false -> tab characters are converted to spaces by indent 274 # false -> tab characters are converted to spaces by indent
(...skipping 20 matching lines...) Expand all
243 # else searches for a popular (if, def, ...) Python stmt. 295 # else searches for a popular (if, def, ...) Python stmt.
244 self.context_use_ps1 = False 296 self.context_use_ps1 = False
245 297
246 # When searching backwards for a reliable place to begin parsing, 298 # When searching backwards for a reliable place to begin parsing,
247 # first start num_context_lines[0] lines back, then 299 # first start num_context_lines[0] lines back, then
248 # num_context_lines[1] lines back if that didn't work, and so on. 300 # num_context_lines[1] lines back if that didn't work, and so on.
249 # The last value should be huge (larger than the # of lines in a 301 # The last value should be huge (larger than the # of lines in a
250 # conceivable file). 302 # conceivable file).
251 # Making the initial values larger slows things down more often. 303 # Making the initial values larger slows things down more often.
252 self.num_context_lines = 50, 500, 5000000 304 self.num_context_lines = 50, 500, 5000000
305
253 self.per = per = self.Percolator(text) 306 self.per = per = self.Percolator(text)
307
254 self.undo = undo = self.UndoDelegator() 308 self.undo = undo = self.UndoDelegator()
255 per.insertfilter(undo) 309 per.insertfilter(undo)
256 text.undo_block_start = undo.undo_block_start 310 text.undo_block_start = undo.undo_block_start
257 text.undo_block_stop = undo.undo_block_stop 311 text.undo_block_stop = undo.undo_block_stop
258 undo.set_saved_change_hook(self.saved_change_hook) 312 undo.set_saved_change_hook(self.saved_change_hook)
313
259 # IOBinding implements file I/O and printing functionality 314 # IOBinding implements file I/O and printing functionality
260 self.io = io = self.IOBinding(self) 315 self.io = io = self.IOBinding(self)
261 io.set_filename_change_hook(self.filename_change_hook) 316 io.set_filename_change_hook(self.filename_change_hook)
262 self.good_load = False 317
263 self.set_indentation_params(False) 318 # Create the recent files submenu
319 self.recent_files_menu = Menu(self.menubar, tearoff=0)
320 self.menudict['file'].insert_cascade(3, label='Recent Files',
321 underline=0,
322 menu=self.recent_files_menu)
323 self.update_recent_files_list()
324
264 self.color = None # initialized below in self.ResetColorizer 325 self.color = None # initialized below in self.ResetColorizer
265 if filename: 326 if filename:
266 if os.path.exists(filename) and not os.path.isdir(filename): 327 if os.path.exists(filename) and not os.path.isdir(filename):
267 if io.loadfile(filename): 328 io.loadfile(filename)
268 self.good_load = True
269 is_py_src = self.ispythonsource(filename)
270 self.set_indentation_params(is_py_src)
271 if is_py_src:
272 self.color = color = self.ColorDelegator()
273 per.insertfilter(color)
274 else: 329 else:
275 io.set_filename(filename) 330 io.set_filename(filename)
276 self.ResetColorizer() 331 self.ResetColorizer()
277 self.saved_change_hook() 332 self.saved_change_hook()
278 self.update_recent_files_list() 333
334 self.set_indentation_params(self.ispythonsource(filename))
335
279 self.load_extensions() 336 self.load_extensions()
337
280 menu = self.menudict.get('windows') 338 menu = self.menudict.get('windows')
281 if menu: 339 if menu:
282 end = menu.index("end") 340 end = menu.index("end")
283 if end is None: 341 if end is None:
284 end = -1 342 end = -1
285 if end >= 0: 343 if end >= 0:
286 menu.add_separator() 344 menu.add_separator()
287 end = end + 1 345 end = end + 1
288 self.wmenu_end = end 346 self.wmenu_end = end
289 WindowList.register_callback(self.postwindowsmenu) 347 WindowList.register_callback(self.postwindowsmenu)
290 348
291 # Some abstractions so IDLE extensions are cross-IDE 349 # Some abstractions so IDLE extensions are cross-IDE
292 self.askyesno = tkMessageBox.askyesno 350 self.askyesno = tkMessageBox.askyesno
293 self.askinteger = tkSimpleDialog.askinteger 351 self.askinteger = tkSimpleDialog.askinteger
294 self.showerror = tkMessageBox.showerror 352 self.showerror = tkMessageBox.showerror
295 353
354 self._highlight_workaround() # Fix selection tags on Windows
355
356 def _highlight_workaround(self):
357 # On Windows, Tk removes painting of the selection
358 # tags which is different behavior than on Linux and Mac.
359 # See issue14146 for more information.
360 if not sys.platform.startswith('win'):
361 return
362
363 text = self.text
364 text.event_add("<<Highlight-FocusOut>>", "<FocusOut>")
365 text.event_add("<<Highlight-FocusIn>>", "<FocusIn>")
366 def highlight_fix(focus):
367 sel_range = text.tag_ranges("sel")
368 if sel_range:
369 if focus == 'out':
370 HILITE_CONFIG = idleConf.GetHighlight(
371 idleConf.CurrentTheme(), 'hilite')
372 text.tag_config("sel_fix", HILITE_CONFIG)
373 text.tag_raise("sel_fix")
374 text.tag_add("sel_fix", *sel_range)
375 elif focus == 'in':
376 text.tag_remove("sel_fix", "1.0", "end")
377
378 text.bind("<<Highlight-FocusOut>>",
379 lambda ev: highlight_fix("out"))
380 text.bind("<<Highlight-FocusIn>>",
381 lambda ev: highlight_fix("in"))
382
383
296 def _filename_to_unicode(self, filename): 384 def _filename_to_unicode(self, filename):
297 """convert filename to unicode in order to display it in Tk""" 385 """convert filename to unicode in order to display it in Tk"""
298 if isinstance(filename, str) or not filename: 386 if isinstance(filename, unicode) or not filename:
299 return filename 387 return filename
300 else: 388 else:
301 try: 389 try:
302 return filename.decode(self.filesystemencoding) 390 return filename.decode(self.filesystemencoding)
303 except UnicodeDecodeError: 391 except UnicodeDecodeError:
304 # XXX 392 # XXX
305 try: 393 try:
306 return filename.decode(self.encoding) 394 return filename.decode(self.encoding)
307 except UnicodeDecodeError: 395 except UnicodeDecodeError:
308 # byte-to-byte conversion 396 # byte-to-byte conversion
309 return filename.decode('iso8859-1') 397 return filename.decode('iso8859-1')
310 398
311 def new_callback(self, event): 399 def new_callback(self, event):
312 dirname, basename = self.io.defaultfilename() 400 dirname, basename = self.io.defaultfilename()
313 self.flist.new(dirname) 401 self.flist.new(dirname)
314 return "break" 402 return "break"
315 403
316 def home_callback(self, event): 404 def home_callback(self, event):
317 if (event.state & 4) != 0 and event.keysym == "Home": 405 if (event.state & 4) != 0 and event.keysym == "Home":
318 # state&4==Control. If <Control-Home>, use the Tk binding. 406 # state&4==Control. If <Control-Home>, use the Tk binding.
319 return 407 return
320 if self.text.index("iomark") and \ 408 if self.text.index("iomark") and \
321 self.text.compare("iomark", "<=", "insert lineend") and \ 409 self.text.compare("iomark", "<=", "insert lineend") and \
322 self.text.compare("insert linestart", "<=", "iomark"): 410 self.text.compare("insert linestart", "<=", "iomark"):
323 # In Shell on input line, go to just after prompt 411 # In Shell on input line, go to just after prompt
324 insertpt = int(self.text.index("iomark").split(".")[1]) 412 insertpt = int(self.text.index("iomark").split(".")[1])
325 else: 413 else:
326 line = self.text.get("insert linestart", "insert lineend") 414 line = self.text.get("insert linestart", "insert lineend")
327 for insertpt in range(len(line)): 415 for insertpt in xrange(len(line)):
328 if line[insertpt] not in (' ','\t'): 416 if line[insertpt] not in (' ','\t'):
329 break 417 break
330 else: 418 else:
331 insertpt=len(line) 419 insertpt=len(line)
332 lineat = int(self.text.index("insert").split('.')[1]) 420 lineat = int(self.text.index("insert").split('.')[1])
333 if insertpt == lineat: 421 if insertpt == lineat:
334 insertpt = 0 422 insertpt = 0
335 dest = "insert linestart+"+str(insertpt)+"c" 423 dest = "insert linestart+"+str(insertpt)+"c"
336 if (event.state&1) == 0: 424 if (event.state&1) == 0:
337 # shift was not pressed 425 # shift was not pressed
(...skipping 11 matching lines...) Expand all
349 if self.text.compare(first,">",last): 437 if self.text.compare(first,">",last):
350 first,last = last,first 438 first,last = last,first
351 self.text.tag_remove("sel", "1.0", "end") 439 self.text.tag_remove("sel", "1.0", "end")
352 self.text.tag_add("sel", first, last) 440 self.text.tag_add("sel", first, last)
353 self.text.mark_set("insert", dest) 441 self.text.mark_set("insert", dest)
354 self.text.see("insert") 442 self.text.see("insert")
355 return "break" 443 return "break"
356 444
357 def set_status_bar(self): 445 def set_status_bar(self):
358 self.status_bar = self.MultiStatusBar(self.top) 446 self.status_bar = self.MultiStatusBar(self.top)
359 if macosxSupport.runningAsOSXApp(): 447 if sys.platform == "darwin":
360 # Insert some padding to avoid obscuring some of the statusbar 448 # Insert some padding to avoid obscuring some of the statusbar
361 # by the resize widget. 449 # by the resize widget.
362 self.status_bar.set_label('_padding1', ' ', side=RIGHT) 450 self.status_bar.set_label('_padding1', ' ', side=RIGHT)
363 self.status_bar.set_label('column', 'Col: ?', side=RIGHT) 451 self.status_bar.set_label('column', 'Col: ?', side=RIGHT)
364 self.status_bar.set_label('line', 'Ln: ?', side=RIGHT) 452 self.status_bar.set_label('line', 'Ln: ?', side=RIGHT)
365 self.status_bar.pack(side=BOTTOM, fill=X) 453 self.status_bar.pack(side=BOTTOM, fill=X)
366 self.text.bind("<<set-line-and-column>>", self.set_line_and_column) 454 self.text.bind("<<set-line-and-column>>", self.set_line_and_column)
367 self.text.event_add("<<set-line-and-column>>", 455 self.text.event_add("<<set-line-and-column>>",
368 "<KeyRelease>", "<ButtonRelease>") 456 "<KeyRelease>", "<ButtonRelease>")
369 self.text.after_idle(self.set_line_and_column) 457 self.text.after_idle(self.set_line_and_column)
370 458
371 def set_line_and_column(self, event=None): 459 def set_line_and_column(self, event=None):
372 line, column = self.text.index(INSERT).split('.') 460 line, column = self.text.index(INSERT).split('.')
373 self.status_bar.set_label('column', 'Col: %s' % column) 461 self.status_bar.set_label('column', 'Col: %s' % column)
374 self.status_bar.set_label('line', 'Ln: %s' % line) 462 self.status_bar.set_label('line', 'Ln: %s' % line)
375 463
376 menu_specs = [ 464 menu_specs = [
377 ("file", "_File"), 465 ("file", "_File"),
378 ("edit", "_Edit"), 466 ("edit", "_Edit"),
379 ("format", "F_ormat"), 467 ("format", "F_ormat"),
380 ("run", "_Run"), 468 ("run", "_Run"),
381 ("options", "_Options"), 469 ("options", "_Options"),
382 ("windows", "_Windows"), 470 ("windows", "_Window"),
383 ("help", "_Help"), 471 ("help", "_Help"),
384 ] 472 ]
385
386 if macosxSupport.runningAsOSXApp():
387 del menu_specs[-3]
388 menu_specs[-2] = ("windows", "_Window")
389 473
390 474
391 def createmenubar(self): 475 def createmenubar(self):
392 mbar = self.menubar 476 mbar = self.menubar
393 self.menudict = menudict = {} 477 self.menudict = menudict = {}
394 for name, label in self.menu_specs: 478 for name, label in self.menu_specs:
395 underline, label = prepstr(label) 479 underline, label = prepstr(label)
396 menudict[name] = menu = Menu(mbar, name=name, tearoff=0) 480 menudict[name] = menu = Menu(mbar, name=name, tearoff=0)
397 mbar.add_cascade(label=label, menu=menu, underline=underline) 481 mbar.add_cascade(label=label, menu=menu, underline=underline)
398 if macosxSupport.isCarbonAquaTk(self.root): 482
483 if macosxSupport.isCarbonTk():
399 # Insert the application menu 484 # Insert the application menu
400 menudict['application'] = menu = Menu(mbar, name='apple', 485 menudict['application'] = menu = Menu(mbar, name='apple',
401 tearoff=0) 486 tearoff=0)
402 mbar.add_cascade(label='IDLE', menu=menu) 487 mbar.add_cascade(label='IDLE', menu=menu)
488
403 self.fill_menus() 489 self.fill_menus()
404 self.recent_files_menu = Menu(self.menubar, tearoff=0)
405 self.menudict['file'].insert_cascade(3, label='Recent Files',
406 underline=0,
407 menu=self.recent_files_menu)
408 self.base_helpmenu_length = self.menudict['help'].index(END) 490 self.base_helpmenu_length = self.menudict['help'].index(END)
409 self.reset_help_menu_entries() 491 self.reset_help_menu_entries()
410 492
411 def postwindowsmenu(self): 493 def postwindowsmenu(self):
412 # Only called when Windows menu exists 494 # Only called when Windows menu exists
413 menu = self.menudict['windows'] 495 menu = self.menudict['windows']
414 end = menu.index("end") 496 end = menu.index("end")
415 if end is None: 497 if end is None:
416 end = -1 498 end = -1
417 if end > self.wmenu_end: 499 if end > self.wmenu_end:
418 menu.delete(self.wmenu_end+1, end) 500 menu.delete(self.wmenu_end+1, end)
419 WindowList.add_windows_to_menu(menu) 501 WindowList.add_windows_to_menu(menu)
420 502
421 rmenu = None 503 rmenu = None
422 504
423 def right_menu_event(self, event): 505 def right_menu_event(self, event):
424 self.text.tag_remove("sel", "1.0", "end")
425 self.text.mark_set("insert", "@%d,%d" % (event.x, event.y)) 506 self.text.mark_set("insert", "@%d,%d" % (event.x, event.y))
426 if not self.rmenu: 507 if not self.rmenu:
427 self.make_rmenu() 508 self.make_rmenu()
428 rmenu = self.rmenu 509 rmenu = self.rmenu
429 self.event = event 510 self.event = event
430 iswin = sys.platform[:3] == 'win' 511 iswin = sys.platform[:3] == 'win'
431 if iswin: 512 if iswin:
432 self.text.config(cursor="arrow") 513 self.text.config(cursor="arrow")
514
515 for item in self.rmenu_specs:
516 try:
517 label, eventname, verify_state = item
518 except ValueError: # see issue1207589
519 continue
520
521 if verify_state is None:
522 continue
523 state = getattr(self, verify_state)()
524 rmenu.entryconfigure(label, state=state)
525
433 rmenu.tk_popup(event.x_root, event.y_root) 526 rmenu.tk_popup(event.x_root, event.y_root)
434 if iswin: 527 if iswin:
435 self.text.config(cursor="ibeam") 528 self.text.config(cursor="ibeam")
436 529
437 rmenu_specs = [ 530 rmenu_specs = [
438 # ("Label", "<<virtual-event>>"), ... 531 # ("Label", "<<virtual-event>>", "statefuncname"), ...
439 ("Close", "<<close-window>>"), # Example 532 ("Close", "<<close-window>>", None), # Example
440 ] 533 ]
441 534
442 def make_rmenu(self): 535 def make_rmenu(self):
443 rmenu = Menu(self.text, tearoff=0) 536 rmenu = Menu(self.text, tearoff=0)
444 for label, eventname in self.rmenu_specs: 537 for item in self.rmenu_specs:
445 def command(text=self.text, eventname=eventname): 538 label, eventname = item[0], item[1]
446 text.event_generate(eventname) 539 if label is not None:
447 rmenu.add_command(label=label, command=command) 540 def command(text=self.text, eventname=eventname):
541 text.event_generate(eventname)
542 rmenu.add_command(label=label, command=command)
543 else:
544 rmenu.add_separator()
448 self.rmenu = rmenu 545 self.rmenu = rmenu
546
547 def rmenu_check_cut(self):
548 return self.rmenu_check_copy()
549
550 def rmenu_check_copy(self):
551 try:
552 indx = self.text.index('sel.first')
553 except TclError:
554 return 'disabled'
555 else:
556 return 'normal' if indx else 'disabled'
557
558 def rmenu_check_paste(self):
559 try:
560 self.text.tk.call('tk::GetSelection', self.text, 'CLIPBOARD')
561 except TclError:
562 return 'disabled'
563 else:
564 return 'normal'
449 565
450 def about_dialog(self, event=None): 566 def about_dialog(self, event=None):
451 aboutDialog.AboutDialog(self.top,'About IDLE') 567 aboutDialog.AboutDialog(self.top,'About IDLE')
452 568
453 def config_dialog(self, event=None): 569 def config_dialog(self, event=None):
454 configDialog.ConfigDialog(self.top,'Settings') 570 configDialog.ConfigDialog(self.top,'Settings')
571 def config_extensions_dialog(self, event=None):
572 configDialog.ConfigExtensionsDialog(self.top)
455 573
456 def help_dialog(self, event=None): 574 def help_dialog(self, event=None):
457 fn=os.path.join(os.path.abspath(os.path.dirname(__file__)),'help.txt') 575 if self.root:
458 textView.view_file(self.top,'Help',fn) 576 parent = self.root
577 else:
578 parent = self.top
579 helpDialog.display(parent, near=self.top)
459 580
460 def python_docs(self, event=None): 581 def python_docs(self, event=None):
461 if sys.platform[:3] == 'win': 582 if sys.platform[:3] == 'win':
462 try: 583 try:
463 os.startfile(self.help_url) 584 os.startfile(self.help_url)
464 except WindowsError as why: 585 except WindowsError as why:
465 tkMessageBox.showerror(title='Document Start Failure', 586 tkMessageBox.showerror(title='Document Start Failure',
466 message=str(why), parent=self.text) 587 message=str(why), parent=self.text)
467 else: 588 else:
468 webbrowser.open(self.help_url) 589 webbrowser.open(self.help_url)
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after
548 "Go to line number:",parent=text) 669 "Go to line number:",parent=text)
549 if lineno is None: 670 if lineno is None:
550 return "break" 671 return "break"
551 if lineno <= 0: 672 if lineno <= 0:
552 text.bell() 673 text.bell()
553 return "break" 674 return "break"
554 text.mark_set("insert", "%d.0" % lineno) 675 text.mark_set("insert", "%d.0" % lineno)
555 text.see("insert") 676 text.see("insert")
556 677
557 def open_module(self, event=None): 678 def open_module(self, event=None):
558 # XXX Shouldn't this be in IOBinding? 679 # XXX Shouldn't this be in IOBinding or in FileList?
559 try: 680 try:
560 name = self.text.get("sel.first", "sel.last") 681 name = self.text.get("sel.first", "sel.last")
561 except TclError: 682 except TclError:
562 name = "" 683 name = ""
563 else: 684 else:
564 name = name.strip() 685 name = name.strip()
565 name = tkSimpleDialog.askstring("Module", 686 name = tkSimpleDialog.askstring("Module",
566 "Enter the name of a Python module\n" 687 "Enter the name of a Python module\n"
567 "to search on sys.path and open:", 688 "to search on sys.path and open:",
568 parent=self.text, initialvalue=name) 689 parent=self.text, initialvalue=name)
569 if name: 690 if name:
570 name = name.strip() 691 name = name.strip()
571 if not name: 692 if not name:
572 return 693 return
573 # XXX Ought to insert current file's directory in front of path 694 # XXX Ought to insert current file's directory in front of path
574 try: 695 try:
575 (f, file, (suffix, mode, type)) = _find_module(name) 696 (f, file_path, (suffix, mode, mtype)) = _find_module(name)
576 except (NameError, ImportError) as msg: 697 except (NameError, ImportError) as msg:
577 tkMessageBox.showerror("Import error", str(msg), parent=self.text) 698 tkMessageBox.showerror("Import error", str(msg), parent=self.text)
578 return 699 return
579 if type != imp.PY_SOURCE: 700 if mtype != imp.PY_SOURCE:
580 tkMessageBox.showerror("Unsupported type", 701 tkMessageBox.showerror("Unsupported type",
581 "%s is not a source module" % name, parent=self.text) 702 "%s is not a source module" % name, parent=self.text)
582 return 703 return
583 if f: 704 if f:
584 f.close() 705 f.close()
585 if self.flist: 706 if self.flist:
586 self.flist.open(file) 707 self.flist.open(file_path)
587 else: 708 else:
588 self.io.loadfile(file) 709 self.io.loadfile(file_path)
710 return file_path
589 711
590 def open_class_browser(self, event=None): 712 def open_class_browser(self, event=None):
591 filename = self.io.filename 713 filename = self.io.filename
592 if not filename: 714 if not (self.__class__.__name__ == 'PyShellEditorWindow'
593 tkMessageBox.showerror( 715 and filename):
594 "No filename", 716 filename = self.open_module()
595 "This buffer has no associated filename", 717 if filename is None:
596 master=self.text) 718 return
597 self.text.focus_set()
598 return None
599 head, tail = os.path.split(filename) 719 head, tail = os.path.split(filename)
600 base, ext = os.path.splitext(tail) 720 base, ext = os.path.splitext(tail)
601 from idlelib import ClassBrowser 721 from idlelib import ClassBrowser
602 ClassBrowser.ClassBrowser(self.flist, base, [head]) 722 ClassBrowser.ClassBrowser(self.flist, base, [head])
603 723
604 def open_path_browser(self, event=None): 724 def open_path_browser(self, event=None):
605 from idlelib import PathBrowser 725 from idlelib import PathBrowser
606 PathBrowser.PathBrowser(self.flist) 726 PathBrowser.PathBrowser(self.flist)
607 727
608 def gotoline(self, lineno): 728 def gotoline(self, lineno):
609 if lineno is not None and lineno > 0: 729 if lineno is not None and lineno > 0:
610 self.text.mark_set("insert", "%d.0" % lineno) 730 self.text.mark_set("insert", "%d.0" % lineno)
611 self.text.tag_remove("sel", "1.0", "end") 731 self.text.tag_remove("sel", "1.0", "end")
612 self.text.tag_add("sel", "insert", "insert +1l") 732 self.text.tag_add("sel", "insert", "insert +1l")
613 self.center() 733 self.center()
614 734
615 def ispythonsource(self, filename): 735 def ispythonsource(self, filename):
616 if not filename or os.path.isdir(filename): 736 if not filename or os.path.isdir(filename):
617 return True 737 return True
618 base, ext = os.path.splitext(os.path.basename(filename)) 738 base, ext = os.path.splitext(os.path.basename(filename))
619 if os.path.normcase(ext) in (".py", ".pyw"): 739 if os.path.normcase(ext) in (".py", ".pyw"):
620 return True 740 return True
621 line = self.text.get('1.0', '1.0 lineend') 741 try:
622 return line.startswith('#!') and 'python' in line 742 f = open(filename)
743 line = f.readline()
744 f.close()
745 except IOError:
746 return False
747 return line.startswith('#!') and line.find('python') >= 0
623 748
624 def close_hook(self): 749 def close_hook(self):
625 if self.flist: 750 if self.flist:
626 self.flist.unregister_maybe_terminate(self) 751 self.flist.unregister_maybe_terminate(self)
627 self.flist = None 752 self.flist = None
628 753
629 def set_close_hook(self, close_hook): 754 def set_close_hook(self, close_hook):
630 self.close_hook = close_hook 755 self.close_hook = close_hook
631 756
632 def filename_change_hook(self): 757 def filename_change_hook(self):
(...skipping 15 matching lines...) Expand all
648 self.per.insertfilter(self.undo) 773 self.per.insertfilter(self.undo)
649 774
650 def _rmcolorizer(self): 775 def _rmcolorizer(self):
651 if not self.color: 776 if not self.color:
652 return 777 return
653 self.color.removecolors() 778 self.color.removecolors()
654 self.per.removefilter(self.color) 779 self.per.removefilter(self.color)
655 self.color = None 780 self.color = None
656 781
657 def ResetColorizer(self): 782 def ResetColorizer(self):
658 "Update the colour theme" 783 "Update the color theme"
659 # Called from self.filename_change_hook and from configDialog.py 784 # Called from self.filename_change_hook and from configDialog.py
660 self._rmcolorizer() 785 self._rmcolorizer()
661 self._addcolorizer() 786 self._addcolorizer()
662 theme = idleConf.GetOption('main','Theme','name') 787 theme = idleConf.GetOption('main','Theme','name')
663 normal_colors = idleConf.GetHighlight(theme, 'normal') 788 normal_colors = idleConf.GetHighlight(theme, 'normal')
664 cursor_color = idleConf.GetHighlight(theme, 'cursor', fgBg='fg') 789 cursor_color = idleConf.GetHighlight(theme, 'cursor', fgBg='fg')
665 select_colors = idleConf.GetHighlight(theme, 'hilite') 790 select_colors = idleConf.GetHighlight(theme, 'hilite')
666 self.text.config( 791 self.text.config(
667 foreground=normal_colors['foreground'], 792 foreground=normal_colors['foreground'],
668 background=normal_colors['background'], 793 background=normal_colors['background'],
669 insertbackground=cursor_color, 794 insertbackground=cursor_color,
670 selectforeground=select_colors['foreground'], 795 selectforeground=select_colors['foreground'],
671 selectbackground=select_colors['background'], 796 selectbackground=select_colors['background'],
672 ) 797 )
673 798
674 IDENTCHARS = string.ascii_letters + string.digits + "_"
675
676 def colorize_syntax_error(self, text, pos):
677 text.tag_add("ERROR", pos)
678 char = text.get(pos)
679 if char and char in self.IDENTCHARS:
680 text.tag_add("ERROR", pos + " wordstart", pos)
681 if '\n' == text.get(pos): # error at line end
682 text.mark_set("insert", pos)
683 else:
684 text.mark_set("insert", pos + "+1c")
685 text.see(pos)
686
687 def ResetFont(self): 799 def ResetFont(self):
688 "Update the text widgets' font if it is changed" 800 "Update the text widgets' font if it is changed"
689 # Called from configDialog.py 801 # Called from configDialog.py
690 fontWeight='normal' 802 fontWeight='normal'
691 if idleConf.GetOption('main','EditorWindow','font-bold',type='bool'): 803 if idleConf.GetOption('main','EditorWindow','font-bold',type='bool'):
692 fontWeight='bold' 804 fontWeight='bold'
693 self.text.config(font=(idleConf.GetOption('main','EditorWindow','font'), 805 self.text.config(font=(idleConf.GetOption('main','EditorWindow','font'),
694 idleConf.GetOption('main','EditorWindow','font-size'), 806 idleConf.GetOption('main','EditorWindow','font-size',
807 type='int'),
695 fontWeight)) 808 fontWeight))
696 809
697 def RemoveKeybindings(self): 810 def RemoveKeybindings(self):
698 "Remove the keybindings before they are changed." 811 "Remove the keybindings before they are changed."
699 # Called from configDialog.py 812 # Called from configDialog.py
700 self.Bindings.default_keydefs = keydefs = idleConf.GetCurrentKeySet() 813 self.Bindings.default_keydefs = keydefs = idleConf.GetCurrentKeySet()
701 for event, keylist in keydefs.items(): 814 for event, keylist in keydefs.items():
702 self.text.event_delete(event, *keylist) 815 self.text.event_delete(event, *keylist)
703 for extensionName in self.get_standard_extension_names(): 816 for extensionName in self.get_standard_extension_names():
704 xkeydefs = idleConf.GetExtensionBindings(extensionName) 817 xkeydefs = idleConf.GetExtensionBindings(extensionName)
(...skipping 10 matching lines...) Expand all
715 xkeydefs = idleConf.GetExtensionBindings(extensionName) 828 xkeydefs = idleConf.GetExtensionBindings(extensionName)
716 if xkeydefs: 829 if xkeydefs:
717 self.apply_bindings(xkeydefs) 830 self.apply_bindings(xkeydefs)
718 #update menu accelerators 831 #update menu accelerators
719 menuEventDict = {} 832 menuEventDict = {}
720 for menu in self.Bindings.menudefs: 833 for menu in self.Bindings.menudefs:
721 menuEventDict[menu[0]] = {} 834 menuEventDict[menu[0]] = {}
722 for item in menu[1]: 835 for item in menu[1]:
723 if item: 836 if item:
724 menuEventDict[menu[0]][prepstr(item[0])[1]] = item[1] 837 menuEventDict[menu[0]][prepstr(item[0])[1]] = item[1]
725 for menubarItem in self.menudict: 838 for menubarItem in self.menudict.keys():
726 menu = self.menudict[menubarItem] 839 menu = self.menudict[menubarItem]
727 end = menu.index(END) + 1 840 end = menu.index(END)
841 if end is None:
842 # Skip empty menus
843 continue
844 end += 1
728 for index in range(0, end): 845 for index in range(0, end):
729 if menu.type(index) == 'command': 846 if menu.type(index) == 'command':
730 accel = menu.entrycget(index, 'accelerator') 847 accel = menu.entrycget(index, 'accelerator')
731 if accel: 848 if accel:
732 itemName = menu.entrycget(index, 'label') 849 itemName = menu.entrycget(index, 'label')
733 event = '' 850 event = ''
734 if menubarItem in menuEventDict: 851 if menubarItem in menuEventDict:
735 if itemName in menuEventDict[menubarItem]: 852 if itemName in menuEventDict[menubarItem]:
736 event = menuEventDict[menubarItem][itemName] 853 event = menuEventDict[menubarItem][itemName]
737 if event: 854 if event:
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
774 tkMessageBox.showerror(title='Document Start Failure', 891 tkMessageBox.showerror(title='Document Start Failure',
775 message=str(why), parent=self.text) 892 message=str(why), parent=self.text)
776 else: 893 else:
777 webbrowser.open(helpfile) 894 webbrowser.open(helpfile)
778 return display_extra_help 895 return display_extra_help
779 896
780 def update_recent_files_list(self, new_file=None): 897 def update_recent_files_list(self, new_file=None):
781 "Load and update the recent files list and menus" 898 "Load and update the recent files list and menus"
782 rf_list = [] 899 rf_list = []
783 if os.path.exists(self.recent_files_path): 900 if os.path.exists(self.recent_files_path):
784 rf_list_file = open(self.recent_files_path,'r', 901 with open(self.recent_files_path, 'r') as rf_list_file:
785 encoding='utf_8', errors='replace')
786 try:
787 rf_list = rf_list_file.readlines() 902 rf_list = rf_list_file.readlines()
788 finally:
789 rf_list_file.close()
790 if new_file: 903 if new_file:
791 new_file = os.path.abspath(new_file) + '\n' 904 new_file = os.path.abspath(new_file) + '\n'
792 if new_file in rf_list: 905 if new_file in rf_list:
793 rf_list.remove(new_file) # move to top 906 rf_list.remove(new_file) # move to top
794 rf_list.insert(0, new_file) 907 rf_list.insert(0, new_file)
795 # clean and save the recent files list 908 # clean and save the recent files list
796 bad_paths = [] 909 bad_paths = []
797 for path in rf_list: 910 for path in rf_list:
798 if '\0' in path or not os.path.exists(path[0:-1]): 911 if '\0' in path or not os.path.exists(path[0:-1]):
799 bad_paths.append(path) 912 bad_paths.append(path)
800 rf_list = [path for path in rf_list if path not in bad_paths] 913 rf_list = [path for path in rf_list if path not in bad_paths]
801 ulchars = "1234567890ABCDEFGHIJK" 914 ulchars = "1234567890ABCDEFGHIJK"
802 rf_list = rf_list[0:len(ulchars)] 915 rf_list = rf_list[0:len(ulchars)]
803 try: 916 try:
804 with open(self.recent_files_path, 'w', 917 with open(self.recent_files_path, 'w') as rf_file:
805 encoding='utf_8', errors='replace') as rf_file:
806 rf_file.writelines(rf_list) 918 rf_file.writelines(rf_list)
807 except IOError as err: 919 except IOError as err:
808 if not getattr(self.root, "recentfilelist_error_displayed", False): 920 if not getattr(self.root, "recentfilelist_error_displayed", False):
809 self.root.recentfilelist_error_displayed = True 921 self.root.recentfilelist_error_displayed = True
810 tkMessageBox.showerror(title='IDLE Error', 922 tkMessageBox.showerror(title='IDLE Error',
811 message='Unable to update Recent Files list:\n%s' 923 message='Unable to update Recent Files list:\n%s'
812 % str(err), 924 % str(err),
813 parent=self.text) 925 parent=self.text)
814 # for each edit window instance, construct the recent files menu 926 # for each edit window instance, construct the recent files menu
815 for instance in self.top.instance_dict: 927 for instance in self.top.instance_dict.keys():
816 menu = instance.recent_files_menu 928 menu = instance.recent_files_menu
817 menu.delete(1, END) # clear, and rebuild: 929 menu.delete(0, END) # clear, and rebuild:
818 for i, file_name in enumerate(rf_list): 930 for i, file_name in enumerate(rf_list):
819 file_name = file_name.rstrip() # zap \n 931 file_name = file_name.rstrip() # zap \n
820 # make unicode string to display non-ASCII chars correctly 932 # make unicode string to display non-ASCII chars correctly
821 ufile_name = self._filename_to_unicode(file_name) 933 ufile_name = self._filename_to_unicode(file_name)
822 callback = instance.__recent_file_callback(file_name) 934 callback = instance.__recent_file_callback(file_name)
823 menu.add_command(label=ulchars[i] + " " + ufile_name, 935 menu.add_command(label=ulchars[i] + " " + ufile_name,
824 command=callback, 936 command=callback,
825 underline=0) 937 underline=0)
826 938
827 def __recent_file_callback(self, file_name): 939 def __recent_file_callback(self, file_name):
828 def open_recent_file(fn_closure=file_name): 940 def open_recent_file(fn_closure=file_name):
829 self.io.open(editFile=fn_closure) 941 self.io.open(editFile=fn_closure)
830 return open_recent_file 942 return open_recent_file
831 943
832 def saved_change_hook(self): 944 def saved_change_hook(self):
833 short = self.short_title() 945 short = self.short_title()
834 long = self.long_title() 946 long = self.long_title()
835 if short and long: 947 if short and long:
836 title = short + " - " + long 948 title = short + " - " + long + _py_version
837 elif short: 949 elif short:
838 title = short 950 title = short
839 elif long: 951 elif long:
840 title = long 952 title = long
841 else: 953 else:
842 title = "Untitled" 954 title = "Untitled"
843 icon = short or long or title 955 icon = short or long or title
844 if not self.get_saved(): 956 if not self.get_saved():
845 title = "*%s*" % title 957 title = "*%s*" % title
846 icon = "*%s" % icon 958 icon = "*%s" % icon
847 self.top.wm_title(title) 959 self.top.wm_title(title)
848 self.top.wm_iconname(icon) 960 self.top.wm_iconname(icon)
849 961
850 def get_saved(self): 962 def get_saved(self):
851 return self.undo.get_saved() 963 return self.undo.get_saved()
852 964
853 def set_saved(self, flag): 965 def set_saved(self, flag):
854 self.undo.set_saved(flag) 966 self.undo.set_saved(flag)
855 967
856 def reset_undo(self): 968 def reset_undo(self):
857 self.undo.reset_undo() 969 self.undo.reset_undo()
858 970
859 def short_title(self): 971 def short_title(self):
860 filename = self.io.filename 972 filename = self.io.filename
861 if filename: 973 if filename:
862 filename = os.path.basename(filename) 974 filename = os.path.basename(filename)
975 else:
976 filename = "Untitled"
863 # return unicode string to display non-ASCII chars correctly 977 # return unicode string to display non-ASCII chars correctly
864 return self._filename_to_unicode(filename) 978 return self._filename_to_unicode(filename)
865 979
866 def long_title(self): 980 def long_title(self):
867 # return unicode string to display non-ASCII chars correctly 981 # return unicode string to display non-ASCII chars correctly
868 return self._filename_to_unicode(self.io.filename or "") 982 return self._filename_to_unicode(self.io.filename or "")
869 983
870 def center_insert_event(self, event): 984 def center_insert_event(self, event):
871 self.center() 985 self.center()
872 986
(...skipping 16 matching lines...) Expand all
889 return top, bot 1003 return top, bot
890 1004
891 def getlineno(self, mark="insert"): 1005 def getlineno(self, mark="insert"):
892 text = self.text 1006 text = self.text
893 return int(float(text.index(mark))) 1007 return int(float(text.index(mark)))
894 1008
895 def get_geometry(self): 1009 def get_geometry(self):
896 "Return (width, height, x, y)" 1010 "Return (width, height, x, y)"
897 geom = self.top.wm_geometry() 1011 geom = self.top.wm_geometry()
898 m = re.match(r"(\d+)x(\d+)\+(-?\d+)\+(-?\d+)", geom) 1012 m = re.match(r"(\d+)x(\d+)\+(-?\d+)\+(-?\d+)", geom)
899 return list(map(int, m.groups())) 1013 tuple = (map(int, m.groups()))
1014 return tuple
900 1015
901 def close_event(self, event): 1016 def close_event(self, event):
902 self.close() 1017 self.close()
903 1018
904 def maybesave(self): 1019 def maybesave(self):
905 if self.io: 1020 if self.io:
906 if not self.get_saved(): 1021 if not self.get_saved():
907 if self.top.state()!='normal': 1022 if self.top.state()!='normal':
908 self.top.deiconify() 1023 self.top.deiconify()
909 self.top.lower() 1024 self.top.lower()
(...skipping 24 matching lines...) Expand all
934 self.top.destroy() 1049 self.top.destroy()
935 if self.close_hook: 1050 if self.close_hook:
936 # unless override: unregister from flist, terminate if last window 1051 # unless override: unregister from flist, terminate if last window
937 self.close_hook() 1052 self.close_hook()
938 1053
939 def load_extensions(self): 1054 def load_extensions(self):
940 self.extensions = {} 1055 self.extensions = {}
941 self.load_standard_extensions() 1056 self.load_standard_extensions()
942 1057
943 def unload_extensions(self): 1058 def unload_extensions(self):
944 for ins in list(self.extensions.values()): 1059 for ins in self.extensions.values():
945 if hasattr(ins, "close"): 1060 if hasattr(ins, "close"):
946 ins.close() 1061 ins.close()
947 self.extensions = {} 1062 self.extensions = {}
948 1063
949 def load_standard_extensions(self): 1064 def load_standard_extensions(self):
950 for name in self.get_standard_extension_names(): 1065 for name in self.get_standard_extension_names():
951 try: 1066 try:
952 self.load_extension(name) 1067 self.load_extension(name)
953 except: 1068 except:
954 print("Failed to load extension", repr(name)) 1069 print "Failed to load extension", repr(name)
1070 import traceback
955 traceback.print_exc() 1071 traceback.print_exc()
956 1072
957 def get_standard_extension_names(self): 1073 def get_standard_extension_names(self):
958 return idleConf.GetExtensions(editor_only=True) 1074 return idleConf.GetExtensions(editor_only=True)
959 1075
960 def load_extension(self, name): 1076 def load_extension(self, name):
961 try: 1077 try:
962 mod = __import__(name, globals(), locals(), []) 1078 mod = __import__(name, globals(), locals(), [])
963 except ImportError: 1079 except ImportError:
964 print("\nFailed to import extension: ", name) 1080 print "\nFailed to import extension: ", name
965 raise 1081 return
966 cls = getattr(mod, name) 1082 cls = getattr(mod, name)
967 keydefs = idleConf.GetExtensionBindings(name) 1083 keydefs = idleConf.GetExtensionBindings(name)
968 if hasattr(cls, "menudefs"): 1084 if hasattr(cls, "menudefs"):
969 self.fill_menus(cls.menudefs, keydefs) 1085 self.fill_menus(cls.menudefs, keydefs)
970 ins = cls(self) 1086 ins = cls(self)
971 self.extensions[name] = ins 1087 self.extensions[name] = ins
972 if keydefs: 1088 if keydefs:
973 self.apply_bindings(keydefs) 1089 self.apply_bindings(keydefs)
974 for vevent in keydefs: 1090 for vevent in keydefs.keys():
975 methodname = vevent.replace("-", "_") 1091 methodname = vevent.replace("-", "_")
976 while methodname[:1] == '<': 1092 while methodname[:1] == '<':
977 methodname = methodname[1:] 1093 methodname = methodname[1:]
978 while methodname[-1:] == '>': 1094 while methodname[-1:] == '>':
979 methodname = methodname[:-1] 1095 methodname = methodname[:-1]
980 methodname = methodname + "_event" 1096 methodname = methodname + "_event"
981 if hasattr(ins, methodname): 1097 if hasattr(ins, methodname):
982 self.text.bind(vevent, getattr(ins, methodname)) 1098 self.text.bind(vevent, getattr(ins, methodname))
983 1099
984 def apply_bindings(self, keydefs=None): 1100 def apply_bindings(self, keydefs=None):
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
1026 menu.add_command(label=label, underline=underline, 1142 menu.add_command(label=label, underline=underline,
1027 command=command, 1143 command=command,
1028 accelerator=accelerator) 1144 accelerator=accelerator)
1029 1145
1030 def getvar(self, name): 1146 def getvar(self, name):
1031 var = self.get_var_obj(name) 1147 var = self.get_var_obj(name)
1032 if var: 1148 if var:
1033 value = var.get() 1149 value = var.get()
1034 return value 1150 return value
1035 else: 1151 else:
1036 raise NameError(name) 1152 raise NameError, name
1037 1153
1038 def setvar(self, name, value, vartype=None): 1154 def setvar(self, name, value, vartype=None):
1039 var = self.get_var_obj(name, vartype) 1155 var = self.get_var_obj(name, vartype)
1040 if var: 1156 if var:
1041 var.set(value) 1157 var.set(value)
1042 else: 1158 else:
1043 raise NameError(name) 1159 raise NameError, name
1044 1160
1045 def get_var_obj(self, name, vartype=None): 1161 def get_var_obj(self, name, vartype=None):
1046 var = self.tkinter_vars.get(name) 1162 var = self.tkinter_vars.get(name)
1047 if not var and vartype: 1163 if not var and vartype:
1048 # create a Tkinter variable object with self.text as master: 1164 # create a Tkinter variable object with self.text as master:
1049 self.tkinter_vars[name] = var = vartype(self.text) 1165 self.tkinter_vars[name] = var = vartype(self.text)
1050 return var 1166 return var
1051 1167
1052 # Tk implementations of "virtual text methods" -- each platform 1168 # Tk implementations of "virtual text methods" -- each platform
1053 # reusing IDLE's support code needs to define these for its GUI's 1169 # reusing IDLE's support code needs to define these for its GUI's
(...skipping 20 matching lines...) Expand all
1074 try: 1190 try:
1075 first = self.text.index("sel.first") 1191 first = self.text.index("sel.first")
1076 last = self.text.index("sel.last") 1192 last = self.text.index("sel.last")
1077 return first, last 1193 return first, last
1078 except TclError: 1194 except TclError:
1079 return None, None 1195 return None, None
1080 1196
1081 # Return the text widget's current view of what a tab stop means 1197 # Return the text widget's current view of what a tab stop means
1082 # (equivalent width in spaces). 1198 # (equivalent width in spaces).
1083 1199
1084 def get_tk_tabwidth(self): 1200 def get_tabwidth(self):
1085 current = self.text['tabs'] or TK_TABWIDTH_DEFAULT 1201 current = self.text['tabs'] or TK_TABWIDTH_DEFAULT
1086 return int(current) 1202 return int(current)
1087 1203
1088 # Set the text widget's current view of what a tab stop means. 1204 # Set the text widget's current view of what a tab stop means.
1089 1205
1090 def set_tk_tabwidth(self, newtabwidth): 1206 def set_tabwidth(self, newtabwidth):
1091 text = self.text 1207 text = self.text
1092 if self.get_tk_tabwidth() != newtabwidth: 1208 if self.get_tabwidth() != newtabwidth:
1093 # Set text widget tab width
1094 pixels = text.tk.call("font", "measure", text["font"], 1209 pixels = text.tk.call("font", "measure", text["font"],
1095 "-displayof", text.master, 1210 "-displayof", text.master,
1096 "n" * newtabwidth) 1211 "n" * newtabwidth)
1097 text.configure(tabs=pixels) 1212 text.configure(tabs=pixels)
1098 1213
1099 ### begin autoindent code ### (configuration was moved to beginning of class) 1214 # If ispythonsource and guess are true, guess a good value for
1100 1215 # indentwidth based on file content (if possible), and if
1101 def set_indentation_params(self, is_py_src, guess=True): 1216 # indentwidth != tabwidth set usetabs false.
1102 if is_py_src and guess: 1217 # In any case, adjust the Text widget's view of what a tab
1218 # character means.
1219
1220 def set_indentation_params(self, ispythonsource, guess=True):
1221 if guess and ispythonsource:
1103 i = self.guess_indent() 1222 i = self.guess_indent()
1104 if 2 <= i <= 8: 1223 if 2 <= i <= 8:
1105 self.indentwidth = i 1224 self.indentwidth = i
1106 if self.indentwidth != self.tabwidth: 1225 if self.indentwidth != self.tabwidth:
1107 self.usetabs = False 1226 self.usetabs = False
1108 self.set_tk_tabwidth(self.tabwidth) 1227 self.set_tabwidth(self.tabwidth)
1109 1228
1110 def smart_backspace_event(self, event): 1229 def smart_backspace_event(self, event):
1111 text = self.text 1230 text = self.text
1112 first, last = self.get_selection_indices() 1231 first, last = self.get_selection_indices()
1113 if first and last: 1232 if first and last:
1114 text.delete(first, last) 1233 text.delete(first, last)
1115 text.mark_set("insert", first) 1234 text.mark_set("insert", first)
1116 return "break" 1235 return "break"
1117 # Delete whitespace left, until hitting a real char or closest 1236 # Delete whitespace left, until hitting a real char or closest
1118 # preceding virtual tab stop. 1237 # preceding virtual tab stop.
(...skipping 222 matching lines...) Expand 10 before | Expand all | Expand 10 after
1341 if line[:2] == '##': 1460 if line[:2] == '##':
1342 line = line[2:] 1461 line = line[2:]
1343 elif line[:1] == '#': 1462 elif line[:1] == '#':
1344 line = line[1:] 1463 line = line[1:]
1345 lines[pos] = line 1464 lines[pos] = line
1346 self.set_region(head, tail, chars, lines) 1465 self.set_region(head, tail, chars, lines)
1347 1466
1348 def tabify_region_event(self, event): 1467 def tabify_region_event(self, event):
1349 head, tail, chars, lines = self.get_region() 1468 head, tail, chars, lines = self.get_region()
1350 tabwidth = self._asktabwidth() 1469 tabwidth = self._asktabwidth()
1470 if tabwidth is None: return
1351 for pos in range(len(lines)): 1471 for pos in range(len(lines)):
1352 line = lines[pos] 1472 line = lines[pos]
1353 if line: 1473 if line:
1354 raw, effective = classifyws(line, tabwidth) 1474 raw, effective = classifyws(line, tabwidth)
1355 ntabs, nspaces = divmod(effective, tabwidth) 1475 ntabs, nspaces = divmod(effective, tabwidth)
1356 lines[pos] = '\t' * ntabs + ' ' * nspaces + line[raw:] 1476 lines[pos] = '\t' * ntabs + ' ' * nspaces + line[raw:]
1357 self.set_region(head, tail, chars, lines) 1477 self.set_region(head, tail, chars, lines)
1358 1478
1359 def untabify_region_event(self, event): 1479 def untabify_region_event(self, event):
1360 head, tail, chars, lines = self.get_region() 1480 head, tail, chars, lines = self.get_region()
1361 tabwidth = self._asktabwidth() 1481 tabwidth = self._asktabwidth()
1482 if tabwidth is None: return
1362 for pos in range(len(lines)): 1483 for pos in range(len(lines)):
1363 lines[pos] = lines[pos].expandtabs(tabwidth) 1484 lines[pos] = lines[pos].expandtabs(tabwidth)
1364 self.set_region(head, tail, chars, lines) 1485 self.set_region(head, tail, chars, lines)
1365 1486
1366 def toggle_tabs_event(self, event): 1487 def toggle_tabs_event(self, event):
1367 if self.askyesno( 1488 if self.askyesno(
1368 "Toggle tabs", 1489 "Toggle tabs",
1369 "Turn tabs " + ("on", "off")[self.usetabs] + 1490 "Turn tabs " + ("on", "off")[self.usetabs] +
1370 "?\nIndent width " + 1491 "?\nIndent width " +
1371 ("will be", "remains at")[self.usetabs] + " 8." + 1492 ("will be", "remains at")[self.usetabs] + " 8." +
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after
1445 text.insert("insert", self._make_blanks(column)) 1566 text.insert("insert", self._make_blanks(column))
1446 text.undo_block_stop() 1567 text.undo_block_stop()
1447 1568
1448 def _asktabwidth(self): 1569 def _asktabwidth(self):
1449 return self.askinteger( 1570 return self.askinteger(
1450 "Tab width", 1571 "Tab width",
1451 "Columns per tab? (2-16)", 1572 "Columns per tab? (2-16)",
1452 parent=self.text, 1573 parent=self.text,
1453 initialvalue=self.indentwidth, 1574 initialvalue=self.indentwidth,
1454 minvalue=2, 1575 minvalue=2,
1455 maxvalue=16) or self.tabwidth 1576 maxvalue=16)
1456 1577
1457 # Guess indentwidth from text content. 1578 # Guess indentwidth from text content.
1458 # Return guessed indentwidth. This should not be believed unless 1579 # Return guessed indentwidth. This should not be believed unless
1459 # it's in a reasonable range (e.g., it will be 0 if no indented 1580 # it's in a reasonable range (e.g., it will be 0 if no indented
1460 # blocks are found). 1581 # blocks are found).
1461 1582
1462 def guess_indent(self): 1583 def guess_indent(self):
1463 opener, indented = IndentSearcher(self.text, self.tabwidth).run() 1584 opener, indented = IndentSearcher(self.text, self.tabwidth).run()
1464 if opener and indented: 1585 if opener and indented:
1465 raw, indentsmall = classifyws(opener, self.tabwidth) 1586 raw, indentsmall = classifyws(opener, self.tabwidth)
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
1526 self.blkopenline = line 1647 self.blkopenline = line
1527 elif type == INDENT and self.blkopenline: 1648 elif type == INDENT and self.blkopenline:
1528 self.indentedline = line 1649 self.indentedline = line
1529 self.finished = 1 1650 self.finished = 1
1530 1651
1531 def run(self): 1652 def run(self):
1532 save_tabsize = _tokenize.tabsize 1653 save_tabsize = _tokenize.tabsize
1533 _tokenize.tabsize = self.tabwidth 1654 _tokenize.tabsize = self.tabwidth
1534 try: 1655 try:
1535 try: 1656 try:
1536 tokens = _tokenize.generate_tokens(self.readline) 1657 _tokenize.tokenize(self.readline, self.tokeneater)
1537 for token in tokens: 1658 except (_tokenize.TokenError, SyntaxError):
1538 self.tokeneater(*token)
1539 except _tokenize.TokenError:
1540 # since we cut off the tokenizer early, we can trigger 1659 # since we cut off the tokenizer early, we can trigger
1541 # spurious errors 1660 # spurious errors
1542 pass 1661 pass
1543 finally: 1662 finally:
1544 _tokenize.tabsize = save_tabsize 1663 _tokenize.tabsize = save_tabsize
1545 return self.blkopenline, self.indentedline 1664 return self.blkopenline, self.indentedline
1546 1665
1547 ### end autoindent code ### 1666 ### end autoindent code ###
1548 1667
1549 def prepstr(s): 1668 def prepstr(s):
1550 # Helper to extract the underscore from a string, e.g. 1669 # Helper to extract the underscore from a string, e.g.
1551 # prepstr("Co_py") returns (2, "Copy"). 1670 # prepstr("Co_py") returns (2, "Copy").
1552 i = s.find('_') 1671 i = s.find('_')
1553 if i >= 0: 1672 if i >= 0:
1554 s = s[:i] + s[i+1:] 1673 s = s[:i] + s[i+1:]
1555 return i, s 1674 return i, s
1556 1675
1557 1676
1558 keynames = { 1677 keynames = {
1559 'bracketleft': '[', 1678 'bracketleft': '[',
1560 'bracketright': ']', 1679 'bracketright': ']',
1561 'slash': '/', 1680 'slash': '/',
1562 } 1681 }
1563 1682
1564 def get_accelerator(keydefs, eventname): 1683 def get_accelerator(keydefs, eventname):
1565 keylist = keydefs.get(eventname) 1684 keylist = keydefs.get(eventname)
1566 # issue10940: temporary workaround to prevent hang with OS X Cocoa Tk 8.5 1685 # issue10940: temporary workaround to prevent hang with OS X Cocoa Tk 8.5
1567 # if not keylist: 1686 # if not keylist:
1568 if (not keylist) or (macosxSupport.runningAsOSXApp() and eventname in { 1687 if (not keylist) or (macosxSupport.isCocoaTk() and eventname in {
1569 "<<open-module>>", 1688 "<<open-module>>",
1570 "<<goto-line>>", 1689 "<<goto-line>>",
1571 "<<change-indentwidth>>"}): 1690 "<<change-indentwidth>>"}):
1572 return "" 1691 return ""
1573 s = keylist[0] 1692 s = keylist[0]
1574 s = re.sub(r"-[a-z]\b", lambda m: m.group().upper(), s) 1693 s = re.sub(r"-[a-z]\b", lambda m: m.group().upper(), s)
1575 s = re.sub(r"\b\w+\b", lambda m: keynames.get(m.group(), m.group()), s) 1694 s = re.sub(r"\b\w+\b", lambda m: keynames.get(m.group(), m.group()), s)
1576 s = re.sub("Key-", "", s) 1695 s = re.sub("Key-", "", s)
1577 s = re.sub("Cancel","Ctrl-Break",s) # dscherer@cmu.edu 1696 s = re.sub("Cancel","Ctrl-Break",s) # dscherer@cmu.edu
1578 s = re.sub("Control-", "Ctrl-", s) 1697 s = re.sub("Control-", "Ctrl-", s)
1579 s = re.sub("-", "+", s) 1698 s = re.sub("-", "+", s)
1580 s = re.sub("><", " ", s) 1699 s = re.sub("><", " ", s)
1581 s = re.sub("<", "", s) 1700 s = re.sub("<", "", s)
1582 s = re.sub(">", "", s) 1701 s = re.sub(">", "", s)
1583 return s 1702 return s
1584 1703
1585 1704
1586 def fixwordbreaks(root): 1705 def fixwordbreaks(root):
1587 # Make sure that Tk's double-click and next/previous word 1706 # Make sure that Tk's double-click and next/previous word
1588 # operations use our definition of a word (i.e. an identifier) 1707 # operations use our definition of a word (i.e. an identifier)
1589 tk = root.tk 1708 tk = root.tk
1590 tk.call('tcl_wordBreakAfter', 'a b', 0) # make sure word.tcl is loaded 1709 tk.call('tcl_wordBreakAfter', 'a b', 0) # make sure word.tcl is loaded
1591 tk.call('set', 'tcl_wordchars', '[a-zA-Z0-9_]') 1710 tk.call('set', 'tcl_wordchars', '[a-zA-Z0-9_]')
1592 tk.call('set', 'tcl_nonwordchars', '[^a-zA-Z0-9_]') 1711 tk.call('set', 'tcl_nonwordchars', '[^a-zA-Z0-9_]')
1593 1712
1594 1713
1595 def test(): 1714 def _editor_window(parent): # htest #
1596 root = Tk() 1715 # error if close master window first - timer event, after script
1716 root = parent
1597 fixwordbreaks(root) 1717 fixwordbreaks(root)
1598 root.withdraw()
1599 if sys.argv[1:]: 1718 if sys.argv[1:]:
1600 filename = sys.argv[1] 1719 filename = sys.argv[1]
1601 else: 1720 else:
1602 filename = None 1721 filename = None
1722 macosxSupport.setupApp(root, None)
1603 edit = EditorWindow(root=root, filename=filename) 1723 edit = EditorWindow(root=root, filename=filename)
1604 edit.set_close_hook(root.quit)
1605 edit.text.bind("<<close-all-windows>>", edit.close_event) 1724 edit.text.bind("<<close-all-windows>>", edit.close_event)
1606 root.mainloop() 1725 # Does not stop error, neither does following
1607 root.destroy() 1726 # edit.text.bind("<<close-window>>", edit.close_event)
1727
1608 1728
1609 if __name__ == '__main__': 1729 if __name__ == '__main__':
1610 test() 1730 from idlelib.idle_test.htest import run
1731 run(_help_dialog, _editor_window)
LEFTRIGHT

RSS Feeds Recent Issues | This issue
This is Rietveld 894c83f36cb7+