blob: ed90d271a595e95346ab264825737a88ebb00726 [file] [log] [blame]
David Scherer7aced172000-08-15 01:13:23 +00001# changes by dscherer@cmu.edu
2# - created format and run menus
3# - added silly advice dialog (apologies to Douglas Adams)
4# - made Python Documentation work on Windows (requires win32api to
5# do a ShellExecute(); other ways of starting a web browser are awkward)
6
7import sys
8import os
9import string
10import re
11import imp
12from Tkinter import *
13import tkSimpleDialog
14import tkMessageBox
Kurt B. Kaiserfd182cd2001-07-14 03:58:25 +000015
16import webbrowser
David Scherer7aced172000-08-15 01:13:23 +000017import idlever
18import WindowList
19from IdleConf import idleconf
Steven M. Gavab9d07b52001-07-31 11:11:38 +000020import aboutDialog, textView
David Scherer7aced172000-08-15 01:13:23 +000021
22# The default tab setting for a Text widget, in average-width characters.
23TK_TABWIDTH_DEFAULT = 8
24
25# File menu
26
27#$ event <<open-module>>
28#$ win <Alt-m>
29#$ unix <Control-x><Control-m>
30
31#$ event <<open-class-browser>>
32#$ win <Alt-c>
33#$ unix <Control-x><Control-b>
34
35#$ event <<open-path-browser>>
36
37#$ event <<close-window>>
Kurt B. Kaiserafdf71b2001-07-13 03:35:32 +000038
David Scherer7aced172000-08-15 01:13:23 +000039#$ unix <Control-x><Control-0>
40#$ unix <Control-x><Key-0>
41#$ win <Alt-F4>
42
43# Edit menu
44
45#$ event <<Copy>>
46#$ win <Control-c>
47#$ unix <Alt-w>
48
49#$ event <<Cut>>
50#$ win <Control-x>
51#$ unix <Control-w>
52
53#$ event <<Paste>>
54#$ win <Control-v>
55#$ unix <Control-y>
56
57#$ event <<select-all>>
58#$ win <Alt-a>
59#$ unix <Alt-a>
60
61# Help menu
62
63#$ event <<help>>
64#$ win <F1>
65#$ unix <F1>
66
67#$ event <<about-idle>>
68
69# Events without menu entries
70
71#$ event <<remove-selection>>
72#$ win <Escape>
73
74#$ event <<center-insert>>
75#$ win <Control-l>
76#$ unix <Control-l>
77
78#$ event <<do-nothing>>
79#$ unix <Control-x>
80
David Scherer7aced172000-08-15 01:13:23 +000081class EditorWindow:
82
83 from Percolator import Percolator
84 from ColorDelegator import ColorDelegator
85 from UndoDelegator import UndoDelegator
86 from IOBinding import IOBinding
87 import Bindings
88 from Tkinter import Toplevel
89 from MultiStatusBar import MultiStatusBar
90
David Scherer7aced172000-08-15 01:13:23 +000091 vars = {}
92
93 def __init__(self, flist=None, filename=None, key=None, root=None):
94 edconf = idleconf.getsection('EditorWindow')
95 coconf = idleconf.getsection('Colors')
96 self.flist = flist
97 root = root or flist.root
98 self.root = root
99 if flist:
100 self.vars = flist.vars
101 self.menubar = Menu(root)
102 self.top = top = self.Toplevel(root, menu=self.menubar)
103 self.vbar = vbar = Scrollbar(top, name='vbar')
104 self.text_frame = text_frame = Frame(top)
105 self.text = text = Text(text_frame, name='text', padx=5,
106 foreground=coconf.getdef('normal-foreground'),
107 background=coconf.getdef('normal-background'),
108 highlightcolor=coconf.getdef('hilite-foreground'),
109 highlightbackground=coconf.getdef('hilite-background'),
110 insertbackground=coconf.getdef('cursor-background'),
111 width=edconf.getint('width'),
112 height=edconf.getint('height'),
113 wrap="none")
114
115 self.createmenubar()
116 self.apply_bindings()
117
118 self.top.protocol("WM_DELETE_WINDOW", self.close)
119 self.top.bind("<<close-window>>", self.close_event)
120 text.bind("<<center-insert>>", self.center_insert_event)
121 text.bind("<<help>>", self.help_dialog)
122 text.bind("<<good-advice>>", self.good_advice)
Steven M. Gavaabdfc412001-08-11 07:46:26 +0000123 text.bind("<<view-readme>>", self.view_readme)
David Scherer7aced172000-08-15 01:13:23 +0000124 text.bind("<<python-docs>>", self.python_docs)
125 text.bind("<<about-idle>>", self.about_dialog)
126 text.bind("<<open-module>>", self.open_module)
127 text.bind("<<do-nothing>>", lambda event: "break")
128 text.bind("<<select-all>>", self.select_all)
129 text.bind("<<remove-selection>>", self.remove_selection)
130 text.bind("<3>", self.right_menu_event)
131 if flist:
132 flist.inversedict[self] = key
133 if key:
134 flist.dict[key] = self
135 text.bind("<<open-new-window>>", self.flist.new_callback)
136 text.bind("<<close-all-windows>>", self.flist.close_all_callback)
137 text.bind("<<open-class-browser>>", self.open_class_browser)
138 text.bind("<<open-path-browser>>", self.open_path_browser)
139
Steven M. Gava898a3652001-10-07 11:10:44 +0000140 self.set_status_bar()
141
David Scherer7aced172000-08-15 01:13:23 +0000142 vbar['command'] = text.yview
143 vbar.pack(side=RIGHT, fill=Y)
144
145 text['yscrollcommand'] = vbar.set
146 text['font'] = edconf.get('font-name'), edconf.get('font-size')
147 text_frame.pack(side=LEFT, fill=BOTH, expand=1)
148 text.pack(side=TOP, fill=BOTH, expand=1)
149 text.focus_set()
150
151 self.per = per = self.Percolator(text)
152 if self.ispythonsource(filename):
153 self.color = color = self.ColorDelegator(); per.insertfilter(color)
154 ##print "Initial colorizer"
155 else:
156 ##print "No initial colorizer"
157 self.color = None
158 self.undo = undo = self.UndoDelegator(); per.insertfilter(undo)
159 self.io = io = self.IOBinding(self)
160
161 text.undo_block_start = undo.undo_block_start
162 text.undo_block_stop = undo.undo_block_stop
163 undo.set_saved_change_hook(self.saved_change_hook)
164 io.set_filename_change_hook(self.filename_change_hook)
165
166 if filename:
167 if os.path.exists(filename):
168 io.loadfile(filename)
169 else:
170 io.set_filename(filename)
171
172 self.saved_change_hook()
173
174 self.load_extensions()
175
176 menu = self.menudict.get('windows')
177 if menu:
178 end = menu.index("end")
179 if end is None:
180 end = -1
181 if end >= 0:
182 menu.add_separator()
183 end = end + 1
184 self.wmenu_end = end
185 WindowList.register_callback(self.postwindowsmenu)
186
187 # Some abstractions so IDLE extensions are cross-IDE
188 self.askyesno = tkMessageBox.askyesno
189 self.askinteger = tkSimpleDialog.askinteger
190 self.showerror = tkMessageBox.showerror
191
192 if self.extensions.has_key('AutoIndent'):
193 self.extensions['AutoIndent'].set_indentation_params(
194 self.ispythonsource(filename))
Steven M. Gava898a3652001-10-07 11:10:44 +0000195
David Scherer7aced172000-08-15 01:13:23 +0000196
197 def set_status_bar(self):
Steven M. Gava898a3652001-10-07 11:10:44 +0000198 self.status_bar = self.MultiStatusBar(self.top)
David Scherer7aced172000-08-15 01:13:23 +0000199 self.status_bar.set_label('column', 'Col: ?', side=RIGHT)
200 self.status_bar.set_label('line', 'Ln: ?', side=RIGHT)
201 self.status_bar.pack(side=BOTTOM, fill=X)
202 self.text.bind('<KeyRelease>', self.set_line_and_column)
203 self.text.bind('<ButtonRelease>', self.set_line_and_column)
204 self.text.after_idle(self.set_line_and_column)
205
206 def set_line_and_column(self, event=None):
207 line, column = string.split(self.text.index(INSERT), '.')
208 self.status_bar.set_label('column', 'Col: %s' % column)
209 self.status_bar.set_label('line', 'Ln: %s' % line)
210
211 def wakeup(self):
212 if self.top.wm_state() == "iconic":
213 self.top.wm_deiconify()
214 else:
215 self.top.tkraise()
216 self.text.focus_set()
217
218 menu_specs = [
219 ("file", "_File"),
220 ("edit", "_Edit"),
221 ("format", "F_ormat"),
222 ("run", "_Run"),
223 ("windows", "_Windows"),
224 ("help", "_Help"),
225 ]
226
227 def createmenubar(self):
228 mbar = self.menubar
229 self.menudict = menudict = {}
230 for name, label in self.menu_specs:
231 underline, label = prepstr(label)
232 menudict[name] = menu = Menu(mbar, name=name)
233 mbar.add_cascade(label=label, menu=menu, underline=underline)
234 self.fill_menus()
235
236 def postwindowsmenu(self):
237 # Only called when Windows menu exists
238 # XXX Actually, this Just-In-Time updating interferes badly
239 # XXX with the tear-off feature. It would be better to update
240 # XXX all Windows menus whenever the list of windows changes.
241 menu = self.menudict['windows']
242 end = menu.index("end")
243 if end is None:
244 end = -1
245 if end > self.wmenu_end:
246 menu.delete(self.wmenu_end+1, end)
247 WindowList.add_windows_to_menu(menu)
248
249 rmenu = None
250
251 def right_menu_event(self, event):
252 self.text.tag_remove("sel", "1.0", "end")
253 self.text.mark_set("insert", "@%d,%d" % (event.x, event.y))
254 if not self.rmenu:
255 self.make_rmenu()
256 rmenu = self.rmenu
257 self.event = event
258 iswin = sys.platform[:3] == 'win'
259 if iswin:
260 self.text.config(cursor="arrow")
261 rmenu.tk_popup(event.x_root, event.y_root)
262 if iswin:
263 self.text.config(cursor="ibeam")
264
265 rmenu_specs = [
266 # ("Label", "<<virtual-event>>"), ...
267 ("Close", "<<close-window>>"), # Example
268 ]
269
270 def make_rmenu(self):
271 rmenu = Menu(self.text, tearoff=0)
272 for label, eventname in self.rmenu_specs:
273 def command(text=self.text, eventname=eventname):
274 text.event_generate(eventname)
275 rmenu.add_command(label=label, command=command)
276 self.rmenu = rmenu
277
278 def about_dialog(self, event=None):
Steven M. Gava7d9ed722001-07-31 07:01:47 +0000279 aboutDialog.AboutDialog(self.top,'About IDLEfork')
280
David Scherer7aced172000-08-15 01:13:23 +0000281 def good_advice(self, event=None):
282 tkMessageBox.showinfo('Advice', "Don't Panic!", master=self.text)
283
Steven M. Gavaabdfc412001-08-11 07:46:26 +0000284 def view_readme(self, event=None):
285 fn=os.path.join(os.path.abspath(os.path.dirname(__file__)),'README.txt')
286 textView.TextViewer(self.top,'IDLEfork - README',fn)
287
David Scherer7aced172000-08-15 01:13:23 +0000288 def help_dialog(self, event=None):
Steven M. Gavab9d07b52001-07-31 11:11:38 +0000289 fn=os.path.join(os.path.abspath(os.path.dirname(__file__)),'help.txt')
290 textView.TextViewer(self.top,'Help',fn)
291
David Scherer7aced172000-08-15 01:13:23 +0000292 help_url = "http://www.python.org/doc/current/"
Kurt B. Kaiserafdf71b2001-07-13 03:35:32 +0000293 if sys.platform[:3] == "win":
294 fn = os.path.dirname(__file__)
Kurt B. Kaiserfd182cd2001-07-14 03:58:25 +0000295 fn = os.path.join(fn, os.pardir, os.pardir, "Doc", "index.html")
Kurt B. Kaiserafdf71b2001-07-13 03:35:32 +0000296 fn = os.path.normpath(fn)
297 if os.path.isfile(fn):
298 help_url = fn
299 del fn
David Scherer7aced172000-08-15 01:13:23 +0000300
301 def python_docs(self, event=None):
Kurt B. Kaiserafdf71b2001-07-13 03:35:32 +0000302 webbrowser.open(self.help_url)
David Scherer7aced172000-08-15 01:13:23 +0000303
304 def select_all(self, event=None):
305 self.text.tag_add("sel", "1.0", "end-1c")
306 self.text.mark_set("insert", "1.0")
307 self.text.see("insert")
308 return "break"
309
310 def remove_selection(self, event=None):
311 self.text.tag_remove("sel", "1.0", "end")
312 self.text.see("insert")
313
314 def open_module(self, event=None):
315 # XXX Shouldn't this be in IOBinding or in FileList?
316 try:
317 name = self.text.get("sel.first", "sel.last")
318 except TclError:
319 name = ""
320 else:
321 name = string.strip(name)
322 if not name:
323 name = tkSimpleDialog.askstring("Module",
324 "Enter the name of a Python module\n"
325 "to search on sys.path and open:",
326 parent=self.text)
327 if name:
328 name = string.strip(name)
329 if not name:
330 return
331 # XXX Ought to support package syntax
332 # XXX Ought to insert current file's directory in front of path
333 try:
334 (f, file, (suffix, mode, type)) = imp.find_module(name)
335 except (NameError, ImportError), msg:
336 tkMessageBox.showerror("Import error", str(msg), parent=self.text)
337 return
338 if type != imp.PY_SOURCE:
339 tkMessageBox.showerror("Unsupported type",
340 "%s is not a source module" % name, parent=self.text)
341 return
342 if f:
343 f.close()
344 if self.flist:
345 self.flist.open(file)
346 else:
347 self.io.loadfile(file)
348
349 def open_class_browser(self, event=None):
350 filename = self.io.filename
351 if not filename:
352 tkMessageBox.showerror(
353 "No filename",
354 "This buffer has no associated filename",
355 master=self.text)
356 self.text.focus_set()
357 return None
358 head, tail = os.path.split(filename)
359 base, ext = os.path.splitext(tail)
360 import ClassBrowser
361 ClassBrowser.ClassBrowser(self.flist, base, [head])
362
363 def open_path_browser(self, event=None):
364 import PathBrowser
365 PathBrowser.PathBrowser(self.flist)
366
367 def gotoline(self, lineno):
368 if lineno is not None and lineno > 0:
369 self.text.mark_set("insert", "%d.0" % lineno)
370 self.text.tag_remove("sel", "1.0", "end")
371 self.text.tag_add("sel", "insert", "insert +1l")
372 self.center()
373
374 def ispythonsource(self, filename):
375 if not filename:
376 return 1
377 base, ext = os.path.splitext(os.path.basename(filename))
378 if os.path.normcase(ext) in (".py", ".pyw"):
379 return 1
380 try:
381 f = open(filename)
382 line = f.readline()
383 f.close()
384 except IOError:
385 return 0
386 return line[:2] == '#!' and string.find(line, 'python') >= 0
387
388 def close_hook(self):
389 if self.flist:
390 self.flist.close_edit(self)
391
392 def set_close_hook(self, close_hook):
393 self.close_hook = close_hook
394
395 def filename_change_hook(self):
396 if self.flist:
397 self.flist.filename_changed_edit(self)
398 self.saved_change_hook()
399 if self.ispythonsource(self.io.filename):
400 self.addcolorizer()
401 else:
402 self.rmcolorizer()
403
404 def addcolorizer(self):
405 if self.color:
406 return
407 ##print "Add colorizer"
408 self.per.removefilter(self.undo)
409 self.color = self.ColorDelegator()
410 self.per.insertfilter(self.color)
411 self.per.insertfilter(self.undo)
412
413 def rmcolorizer(self):
414 if not self.color:
415 return
416 ##print "Remove colorizer"
417 self.per.removefilter(self.undo)
418 self.per.removefilter(self.color)
419 self.color = None
420 self.per.insertfilter(self.undo)
421
422 def saved_change_hook(self):
423 short = self.short_title()
424 long = self.long_title()
425 if short and long:
426 title = short + " - " + long
427 elif short:
428 title = short
429 elif long:
430 title = long
431 else:
432 title = "Untitled"
433 icon = short or long or title
434 if not self.get_saved():
435 title = "*%s*" % title
436 icon = "*%s" % icon
437 self.top.wm_title(title)
438 self.top.wm_iconname(icon)
439
440 def get_saved(self):
441 return self.undo.get_saved()
442
443 def set_saved(self, flag):
444 self.undo.set_saved(flag)
445
446 def reset_undo(self):
447 self.undo.reset_undo()
448
449 def short_title(self):
450 filename = self.io.filename
451 if filename:
452 filename = os.path.basename(filename)
453 return filename
454
455 def long_title(self):
456 return self.io.filename or ""
457
458 def center_insert_event(self, event):
459 self.center()
460
461 def center(self, mark="insert"):
462 text = self.text
463 top, bot = self.getwindowlines()
464 lineno = self.getlineno(mark)
465 height = bot - top
466 newtop = max(1, lineno - height/2)
467 text.yview(float(newtop))
468
469 def getwindowlines(self):
470 text = self.text
471 top = self.getlineno("@0,0")
472 bot = self.getlineno("@0,65535")
473 if top == bot and text.winfo_height() == 1:
474 # Geometry manager hasn't run yet
475 height = int(text['height'])
476 bot = top + height - 1
477 return top, bot
478
479 def getlineno(self, mark="insert"):
480 text = self.text
481 return int(float(text.index(mark)))
482
483 def close_event(self, event):
484 self.close()
485
486 def maybesave(self):
487 if self.io:
488 return self.io.maybesave()
489
490 def close(self):
491 self.top.wm_deiconify()
492 self.top.tkraise()
493 reply = self.maybesave()
494 if reply != "cancel":
495 self._close()
496 return reply
497
498 def _close(self):
499 WindowList.unregister_callback(self.postwindowsmenu)
500 if self.close_hook:
501 self.close_hook()
502 self.flist = None
503 colorizing = 0
504 self.unload_extensions()
505 self.io.close(); self.io = None
506 self.undo = None # XXX
507 if self.color:
508 colorizing = self.color.colorizing
509 doh = colorizing and self.top
510 self.color.close(doh) # Cancel colorization
511 self.text = None
512 self.vars = None
513 self.per.close(); self.per = None
514 if not colorizing:
515 self.top.destroy()
516
517 def load_extensions(self):
518 self.extensions = {}
519 self.load_standard_extensions()
520
521 def unload_extensions(self):
522 for ins in self.extensions.values():
523 if hasattr(ins, "close"):
524 ins.close()
525 self.extensions = {}
526
527 def load_standard_extensions(self):
528 for name in self.get_standard_extension_names():
529 try:
530 self.load_extension(name)
531 except:
532 print "Failed to load extension", `name`
533 import traceback
534 traceback.print_exc()
535
536 def get_standard_extension_names(self):
537 return idleconf.getextensions()
538
539 def load_extension(self, name):
540 mod = __import__(name, globals(), locals(), [])
541 cls = getattr(mod, name)
542 ins = cls(self)
543 self.extensions[name] = ins
544 kdnames = ["keydefs"]
545 if sys.platform == 'win32':
546 kdnames.append("windows_keydefs")
547 elif sys.platform == 'mac':
548 kdnames.append("mac_keydefs")
549 else:
550 kdnames.append("unix_keydefs")
551 keydefs = {}
552 for kdname in kdnames:
553 if hasattr(ins, kdname):
554 keydefs.update(getattr(ins, kdname))
555 if keydefs:
556 self.apply_bindings(keydefs)
557 for vevent in keydefs.keys():
558 methodname = string.replace(vevent, "-", "_")
559 while methodname[:1] == '<':
560 methodname = methodname[1:]
561 while methodname[-1:] == '>':
562 methodname = methodname[:-1]
563 methodname = methodname + "_event"
564 if hasattr(ins, methodname):
565 self.text.bind(vevent, getattr(ins, methodname))
566 if hasattr(ins, "menudefs"):
567 self.fill_menus(ins.menudefs, keydefs)
568 return ins
569
570 def apply_bindings(self, keydefs=None):
571 if keydefs is None:
572 keydefs = self.Bindings.default_keydefs
573 text = self.text
574 text.keydefs = keydefs
575 for event, keylist in keydefs.items():
576 if keylist:
577 apply(text.event_add, (event,) + tuple(keylist))
578
579 def fill_menus(self, defs=None, keydefs=None):
580 # Fill the menus. Menus that are absent or None in
581 # self.menudict are ignored.
582 if defs is None:
583 defs = self.Bindings.menudefs
584 if keydefs is None:
585 keydefs = self.Bindings.default_keydefs
586 menudict = self.menudict
587 text = self.text
588 for mname, itemlist in defs:
589 menu = menudict.get(mname)
590 if not menu:
591 continue
592 for item in itemlist:
593 if not item:
594 menu.add_separator()
595 else:
596 label, event = item
597 checkbutton = (label[:1] == '!')
598 if checkbutton:
599 label = label[1:]
600 underline, label = prepstr(label)
601 accelerator = get_accelerator(keydefs, event)
602 def command(text=text, event=event):
603 text.event_generate(event)
604 if checkbutton:
605 var = self.getrawvar(event, BooleanVar)
606 menu.add_checkbutton(label=label, underline=underline,
607 command=command, accelerator=accelerator,
608 variable=var)
609 else:
610 menu.add_command(label=label, underline=underline,
611 command=command, accelerator=accelerator)
612
613 def getvar(self, name):
614 var = self.getrawvar(name)
615 if var:
616 return var.get()
617
618 def setvar(self, name, value, vartype=None):
619 var = self.getrawvar(name, vartype)
620 if var:
621 var.set(value)
622
623 def getrawvar(self, name, vartype=None):
624 var = self.vars.get(name)
625 if not var and vartype:
626 self.vars[name] = var = vartype(self.text)
627 return var
628
629 # Tk implementations of "virtual text methods" -- each platform
630 # reusing IDLE's support code needs to define these for its GUI's
631 # flavor of widget.
632
633 # Is character at text_index in a Python string? Return 0 for
634 # "guaranteed no", true for anything else. This info is expensive
635 # to compute ab initio, but is probably already known by the
636 # platform's colorizer.
637
638 def is_char_in_string(self, text_index):
639 if self.color:
640 # Return true iff colorizer hasn't (re)gotten this far
641 # yet, or the character is tagged as being in a string
642 return self.text.tag_prevrange("TODO", text_index) or \
643 "STRING" in self.text.tag_names(text_index)
644 else:
645 # The colorizer is missing: assume the worst
646 return 1
647
648 # If a selection is defined in the text widget, return (start,
649 # end) as Tkinter text indices, otherwise return (None, None)
650 def get_selection_indices(self):
651 try:
652 first = self.text.index("sel.first")
653 last = self.text.index("sel.last")
654 return first, last
655 except TclError:
656 return None, None
657
658 # Return the text widget's current view of what a tab stop means
659 # (equivalent width in spaces).
660
661 def get_tabwidth(self):
662 current = self.text['tabs'] or TK_TABWIDTH_DEFAULT
663 return int(current)
664
665 # Set the text widget's current view of what a tab stop means.
666
667 def set_tabwidth(self, newtabwidth):
668 text = self.text
669 if self.get_tabwidth() != newtabwidth:
670 pixels = text.tk.call("font", "measure", text["font"],
671 "-displayof", text.master,
Kurt B. Kaiserafdf71b2001-07-13 03:35:32 +0000672 "n" * newtabwidth)
David Scherer7aced172000-08-15 01:13:23 +0000673 text.configure(tabs=pixels)
674
675def prepstr(s):
676 # Helper to extract the underscore from a string, e.g.
677 # prepstr("Co_py") returns (2, "Copy").
678 i = string.find(s, '_')
679 if i >= 0:
680 s = s[:i] + s[i+1:]
681 return i, s
682
683
684keynames = {
685 'bracketleft': '[',
686 'bracketright': ']',
687 'slash': '/',
688}
689
690def get_accelerator(keydefs, event):
691 keylist = keydefs.get(event)
692 if not keylist:
693 return ""
694 s = keylist[0]
695 s = re.sub(r"-[a-z]\b", lambda m: string.upper(m.group()), s)
696 s = re.sub(r"\b\w+\b", lambda m: keynames.get(m.group(), m.group()), s)
697 s = re.sub("Key-", "", s)
698 s = re.sub("Cancel","Ctrl-Break",s) # dscherer@cmu.edu
699 s = re.sub("Control-", "Ctrl-", s)
700 s = re.sub("-", "+", s)
701 s = re.sub("><", " ", s)
702 s = re.sub("<", "", s)
703 s = re.sub(">", "", s)
704 return s
705
706
707def fixwordbreaks(root):
708 # Make sure that Tk's double-click and next/previous word
709 # operations use our definition of a word (i.e. an identifier)
710 tk = root.tk
711 tk.call('tcl_wordBreakAfter', 'a b', 0) # make sure word.tcl is loaded
712 tk.call('set', 'tcl_wordchars', '[a-zA-Z0-9_]')
713 tk.call('set', 'tcl_nonwordchars', '[^a-zA-Z0-9_]')
714
715
716def test():
717 root = Tk()
718 fixwordbreaks(root)
719 root.withdraw()
720 if sys.argv[1:]:
721 filename = sys.argv[1]
722 else:
723 filename = None
724 edit = EditorWindow(root=root, filename=filename)
725 edit.set_close_hook(root.quit)
726 root.mainloop()
727 root.destroy()
728
729if __name__ == '__main__':
730 test()