blob: 44e2b132b397cef1722f63522afaad6078a37b09 [file] [log] [blame]
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +00001import sys
2import os
3import string
Guido van Rossum07ec8961999-01-28 22:02:47 +00004import re
Guido van Rossumb3418881998-10-13 03:45:15 +00005import imp
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +00006from Tkinter import *
Guido van Rossumb3418881998-10-13 03:45:15 +00007import tkSimpleDialog
Guido van Rossum2aeeb551998-10-12 21:01:37 +00008import tkMessageBox
Guido van Rossum504b0bf1999-01-02 21:28:54 +00009import idlever
Guido van Rossumc4f752f1999-02-17 17:20:50 +000010import WindowList
Guido van Rossum504b0bf1999-01-02 21:28:54 +000011
Guido van Rossum13205601999-06-11 15:03:00 +000012# The default tab setting for a Text widget, in average-width characters.
13TK_TABWIDTH_DEFAULT = 8
14
Guido van Rossum504b0bf1999-01-02 21:28:54 +000015# File menu
16
17#$ event <<open-module>>
18#$ win <Alt-m>
19#$ unix <Control-x><Control-m>
20
21#$ event <<open-class-browser>>
22#$ win <Alt-c>
23#$ unix <Control-x><Control-b>
24
Guido van Rossumd6e87131999-03-10 05:18:02 +000025#$ event <<open-path-browser>>
26
Guido van Rossum504b0bf1999-01-02 21:28:54 +000027#$ event <<close-window>>
28#$ unix <Control-x><Control-0>
29#$ unix <Control-x><Key-0>
30#$ win <Alt-F4>
31
32# Edit menu
33
34#$ event <<Copy>>
35#$ win <Control-c>
36#$ unix <Alt-w>
37
38#$ event <<Cut>>
39#$ win <Control-x>
40#$ unix <Control-w>
41
42#$ event <<Paste>>
43#$ win <Control-v>
44#$ unix <Control-y>
45
46#$ event <<select-all>>
47#$ win <Alt-a>
48#$ unix <Alt-a>
49
50# Help menu
51
52#$ event <<help>>
53#$ win <F1>
54#$ unix <F1>
55
56#$ event <<about-idle>>
57
58# Events without menu entries
59
60#$ event <<remove-selection>>
61#$ win <Escape>
62
63#$ event <<center-insert>>
64#$ win <Control-l>
65#$ unix <Control-l>
66
67#$ event <<do-nothing>>
68#$ unix <Control-x>
69
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +000070
Guido van Rossum2aeeb551998-10-12 21:01:37 +000071about_title = "About IDLE"
72about_text = """\
Guido van Rossum504b0bf1999-01-02 21:28:54 +000073IDLE %s
Guido van Rossum2aeeb551998-10-12 21:01:37 +000074
Guido van Rossum504b0bf1999-01-02 21:28:54 +000075An Integrated DeveLopment Environment for Python
Guido van Rossum2aeeb551998-10-12 21:01:37 +000076
77by Guido van Rossum
Guido van Rossum504b0bf1999-01-02 21:28:54 +000078""" % idlever.IDLE_VERSION
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +000079
80class EditorWindow:
81
82 from Percolator import Percolator
83 from ColorDelegator import ColorDelegator
84 from UndoDelegator import UndoDelegator
85 from IOBinding import IOBinding
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +000086 import Bindings
Guido van Rossum504b0bf1999-01-02 21:28:54 +000087 from Tkinter import Toplevel
Guido van Rossumec73dc62000-02-15 18:05:15 +000088 from MultiStatusBar import MultiStatusBar
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +000089
Guido van Rossum504b0bf1999-01-02 21:28:54 +000090 about_title = about_title
91 about_text = about_text
92
Guido van Rossumb7ebb831999-01-28 22:24:30 +000093 vars = {}
94
Guido van Rossum504b0bf1999-01-02 21:28:54 +000095 def __init__(self, flist=None, filename=None, key=None, root=None):
Guido van Rossum7de69751999-04-20 15:45:30 +000096 cprefs = self.ColorDelegator.cprefs
Guido van Rossum504b0bf1999-01-02 21:28:54 +000097 self.flist = flist
98 root = root or flist.root
Guido van Rossum2aeeb551998-10-12 21:01:37 +000099 self.root = root
Guido van Rossumb7ebb831999-01-28 22:24:30 +0000100 if flist:
101 self.vars = flist.vars
Guido van Rossum2aeeb551998-10-12 21:01:37 +0000102 self.menubar = Menu(root)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000103 self.top = top = self.Toplevel(root, menu=self.menubar)
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000104 self.vbar = vbar = Scrollbar(top, name='vbar')
Guido van Rossumec73dc62000-02-15 18:05:15 +0000105 self.text_frame = text_frame = Frame(top)
106 self.text = text = Text(text_frame, name='text', padx=5,
Guido van Rossum7de69751999-04-20 15:45:30 +0000107 foreground=cprefs.CNormal[0],
Guido van Rossumdef2c961999-05-21 04:38:27 +0000108 background=cprefs.CNormal[1],
Guido van Rossum7de69751999-04-20 15:45:30 +0000109 highlightcolor=cprefs.CHilite[0],
110 highlightbackground=cprefs.CHilite[1],
111 insertbackground=cprefs.CCursor[1],
112 wrap="none")
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000113
Guido van Rossum2aeeb551998-10-12 21:01:37 +0000114 self.createmenubar()
Guido van Rossum07ec8961999-01-28 22:02:47 +0000115 self.apply_bindings()
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000116
117 self.top.protocol("WM_DELETE_WINDOW", self.close)
118 self.top.bind("<<close-window>>", self.close_event)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000119 text.bind("<<center-insert>>", self.center_insert_event)
120 text.bind("<<help>>", self.help_dialog)
Guido van Rossum416b9611999-08-26 23:06:05 +0000121 text.bind("<<python-docs>>", self.python_docs)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000122 text.bind("<<about-idle>>", self.about_dialog)
123 text.bind("<<open-module>>", self.open_module)
124 text.bind("<<do-nothing>>", lambda event: "break")
125 text.bind("<<select-all>>", self.select_all)
126 text.bind("<<remove-selection>>", self.remove_selection)
127 text.bind("<3>", self.right_menu_event)
128 if flist:
129 flist.inversedict[self] = key
130 if key:
131 flist.dict[key] = self
132 text.bind("<<open-new-window>>", self.flist.new_callback)
133 text.bind("<<close-all-windows>>", self.flist.close_all_callback)
134 text.bind("<<open-class-browser>>", self.open_class_browser)
Guido van Rossumd6e87131999-03-10 05:18:02 +0000135 text.bind("<<open-path-browser>>", self.open_path_browser)
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000136
137 vbar['command'] = text.yview
138 vbar.pack(side=RIGHT, fill=Y)
139
140 text['yscrollcommand'] = vbar.set
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000141 if sys.platform[:3] == 'win':
142 text['font'] = ("lucida console", 8)
Guido van Rossumdef2c961999-05-21 04:38:27 +0000143# text['font'] = ("courier new", 10)
Guido van Rossumec73dc62000-02-15 18:05:15 +0000144 text_frame.pack(side=LEFT, fill=BOTH, expand=1)
145 text.pack(side=TOP, fill=BOTH, expand=1)
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000146 text.focus_set()
147
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000148 self.per = per = self.Percolator(text)
149 if self.ispythonsource(filename):
150 self.color = color = self.ColorDelegator(); per.insertfilter(color)
151 ##print "Initial colorizer"
152 else:
153 ##print "No initial colorizer"
154 self.color = None
155 self.undo = undo = self.UndoDelegator(); per.insertfilter(undo)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000156 self.io = io = self.IOBinding(self)
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000157
Guido van Rossum318a70d1999-05-03 15:49:52 +0000158 text.undo_block_start = undo.undo_block_start
159 text.undo_block_stop = undo.undo_block_stop
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000160 undo.set_saved_change_hook(self.saved_change_hook)
161 io.set_filename_change_hook(self.filename_change_hook)
162
163 if filename:
164 if os.path.exists(filename):
165 io.loadfile(filename)
166 else:
167 io.set_filename(filename)
168
169 self.saved_change_hook()
170
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000171 self.load_extensions()
172
173 menu = self.menudict.get('windows')
174 if menu:
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000175 end = menu.index("end")
176 if end is None:
177 end = -1
178 if end >= 0:
179 menu.add_separator()
180 end = end + 1
181 self.wmenu_end = end
Guido van Rossumc4f752f1999-02-17 17:20:50 +0000182 WindowList.register_callback(self.postwindowsmenu)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000183
Guido van Rossumd395aee1999-06-02 11:04:29 +0000184 # Some abstractions so IDLE extensions are cross-IDE
185 self.askyesno = tkMessageBox.askyesno
186 self.askinteger = tkSimpleDialog.askinteger
187 self.showerror = tkMessageBox.showerror
188
Guido van Rossumdef2c961999-05-21 04:38:27 +0000189 if self.extensions.has_key('AutoIndent'):
190 self.extensions['AutoIndent'].set_indentation_params(
191 self.ispythonsource(filename))
Guido van Rossumec73dc62000-02-15 18:05:15 +0000192 self.set_status_bar()
193
194 def set_status_bar(self):
195 self.status_bar = self.MultiStatusBar(self.text_frame)
196 self.status_bar.set_label('column', 'Col: ?', side=RIGHT)
197 self.status_bar.set_label('line', 'Ln: ?', side=RIGHT)
198 self.status_bar.pack(side=BOTTOM, fill=X)
199 self.text.bind('<KeyRelease>', self.set_line_and_column)
200 self.text.bind('<ButtonRelease>', self.set_line_and_column)
201 self.text.after_idle(self.set_line_and_column)
202
203 def set_line_and_column(self, event=None):
204 line, column = string.split(self.text.index(INSERT), '.')
205 self.status_bar.set_label('column', 'Col: %s' % column)
206 self.status_bar.set_label('line', 'Ln: %s' % line)
Guido van Rossumdef2c961999-05-21 04:38:27 +0000207
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000208 def wakeup(self):
Guido van Rossum36911a11999-01-18 15:18:57 +0000209 if self.top.wm_state() == "iconic":
210 self.top.wm_deiconify()
211 else:
212 self.top.tkraise()
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000213 self.text.focus_set()
214
Guido van Rossume7b2e651998-10-12 23:56:08 +0000215 menu_specs = [
Guido van Rossumb5eed031998-11-27 03:19:07 +0000216 ("file", "_File"),
217 ("edit", "_Edit"),
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000218 ("windows", "_Windows"),
Guido van Rossumb5eed031998-11-27 03:19:07 +0000219 ("help", "_Help"),
Guido van Rossume7b2e651998-10-12 23:56:08 +0000220 ]
221
Guido van Rossum2aeeb551998-10-12 21:01:37 +0000222 def createmenubar(self):
223 mbar = self.menubar
Guido van Rossum07ec8961999-01-28 22:02:47 +0000224 self.menudict = menudict = {}
Guido van Rossume7b2e651998-10-12 23:56:08 +0000225 for name, label in self.menu_specs:
Guido van Rossum07ec8961999-01-28 22:02:47 +0000226 underline, label = prepstr(label)
227 menudict[name] = menu = Menu(mbar, name=name)
Guido van Rossumb5eed031998-11-27 03:19:07 +0000228 mbar.add_cascade(label=label, menu=menu, underline=underline)
Guido van Rossum07ec8961999-01-28 22:02:47 +0000229 self.fill_menus()
Guido van Rossum2aeeb551998-10-12 21:01:37 +0000230
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000231 def postwindowsmenu(self):
232 # Only called when Windows menu exists
Guido van Rossum85a36a51999-06-10 17:43:17 +0000233 # XXX Actually, this Just-In-Time updating interferes badly
234 # XXX with the tear-off feature. It would be better to update
235 # XXX all Windows menus whenever the list of windows changes.
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000236 menu = self.menudict['windows']
237 end = menu.index("end")
238 if end is None:
239 end = -1
240 if end > self.wmenu_end:
241 menu.delete(self.wmenu_end+1, end)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000242 WindowList.add_windows_to_menu(menu)
243
244 rmenu = None
245
246 def right_menu_event(self, event):
247 self.text.tag_remove("sel", "1.0", "end")
248 self.text.mark_set("insert", "@%d,%d" % (event.x, event.y))
249 if not self.rmenu:
250 self.make_rmenu()
251 rmenu = self.rmenu
252 self.event = event
253 iswin = sys.platform[:3] == 'win'
254 if iswin:
255 self.text.config(cursor="arrow")
256 rmenu.tk_popup(event.x_root, event.y_root)
257 if iswin:
258 self.text.config(cursor="ibeam")
259
260 rmenu_specs = [
261 # ("Label", "<<virtual-event>>"), ...
262 ("Close", "<<close-window>>"), # Example
263 ]
264
265 def make_rmenu(self):
266 rmenu = Menu(self.text, tearoff=0)
267 for label, eventname in self.rmenu_specs:
268 def command(text=self.text, eventname=eventname):
269 text.event_generate(eventname)
270 rmenu.add_command(label=label, command=command)
271 self.rmenu = rmenu
272
Guido van Rossume7b2e651998-10-12 23:56:08 +0000273 def about_dialog(self, event=None):
Guido van Rossum2aeeb551998-10-12 21:01:37 +0000274 tkMessageBox.showinfo(self.about_title, self.about_text,
275 master=self.text)
276
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000277 helpfile = "help.txt"
278
Guido van Rossume7b2e651998-10-12 23:56:08 +0000279 def help_dialog(self, event=None):
Guido van Rossum416b9611999-08-26 23:06:05 +0000280 try:
281 helpfile = os.path.join(os.path.dirname(__file__), self.helpfile)
282 except NameError:
283 helpfile = self.helpfile
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000284 if self.flist:
285 self.flist.open(helpfile)
286 else:
287 self.io.loadfile(helpfile)
288
Guido van Rossum416b9611999-08-26 23:06:05 +0000289 # XXX Fix these for Windows
290 help_viewer = "netscape -remote 'openurl(%(url)s)' 2>/dev/null || " \
291 "netscape %(url)s &"
292 help_url = "http://www.python.org/doc/current/"
293
294 def python_docs(self, event=None):
295 cmd = self.help_viewer % {"url": self.help_url}
296 os.system(cmd)
297
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000298 def select_all(self, event=None):
299 self.text.tag_add("sel", "1.0", "end-1c")
300 self.text.mark_set("insert", "1.0")
301 self.text.see("insert")
302 return "break"
303
304 def remove_selection(self, event=None):
305 self.text.tag_remove("sel", "1.0", "end")
306 self.text.see("insert")
307
Guido van Rossumb3418881998-10-13 03:45:15 +0000308 def open_module(self, event=None):
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000309 # XXX Shouldn't this be in IOBinding or in FileList?
Guido van Rossumb3418881998-10-13 03:45:15 +0000310 try:
311 name = self.text.get("sel.first", "sel.last")
312 except TclError:
313 name = ""
314 else:
315 name = string.strip(name)
316 if not name:
317 name = tkSimpleDialog.askstring("Module",
Guido van Rossume1dedc01998-10-16 16:09:57 +0000318 "Enter the name of a Python module\n"
319 "to search on sys.path and open:",
Guido van Rossumb3418881998-10-13 03:45:15 +0000320 parent=self.text)
321 if name:
322 name = string.strip(name)
323 if not name:
324 return
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000325 # XXX Ought to support package syntax
326 # XXX Ought to insert current file's directory in front of path
Guido van Rossumb3418881998-10-13 03:45:15 +0000327 try:
328 (f, file, (suffix, mode, type)) = imp.find_module(name)
Guido van Rossum74311b21999-06-01 18:27:14 +0000329 except (NameError, ImportError), msg:
Guido van Rossumb3418881998-10-13 03:45:15 +0000330 tkMessageBox.showerror("Import error", str(msg), parent=self.text)
331 return
332 if type != imp.PY_SOURCE:
333 tkMessageBox.showerror("Unsupported type",
334 "%s is not a source module" % name, parent=self.text)
335 return
336 if f:
337 f.close()
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000338 if self.flist:
339 self.flist.open(file)
340 else:
341 self.io.loadfile(file)
342
343 def open_class_browser(self, event=None):
344 filename = self.io.filename
345 if not filename:
346 tkMessageBox.showerror(
347 "No filename",
348 "This buffer has no associated filename",
349 master=self.text)
Guido van Rossum74311b21999-06-01 18:27:14 +0000350 self.text.focus_set()
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000351 return None
352 head, tail = os.path.split(filename)
353 base, ext = os.path.splitext(tail)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000354 import ClassBrowser
355 ClassBrowser.ClassBrowser(self.flist, base, [head])
Guido van Rossumdef2c961999-05-21 04:38:27 +0000356
Guido van Rossumd6e87131999-03-10 05:18:02 +0000357 def open_path_browser(self, event=None):
358 import PathBrowser
359 PathBrowser.PathBrowser(self.flist)
Guido van Rossum2aeeb551998-10-12 21:01:37 +0000360
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000361 def gotoline(self, lineno):
362 if lineno is not None and lineno > 0:
363 self.text.mark_set("insert", "%d.0" % lineno)
364 self.text.tag_remove("sel", "1.0", "end")
365 self.text.tag_add("sel", "insert", "insert +1l")
366 self.center()
367
368 def ispythonsource(self, filename):
369 if not filename:
370 return 1
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000371 base, ext = os.path.splitext(os.path.basename(filename))
372 if os.path.normcase(ext) in (".py", ".pyw"):
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000373 return 1
374 try:
375 f = open(filename)
376 line = f.readline()
377 f.close()
378 except IOError:
379 return 0
380 return line[:2] == '#!' and string.find(line, 'python') >= 0
381
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000382 def close_hook(self):
383 if self.flist:
384 self.flist.close_edit(self)
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000385
386 def set_close_hook(self, close_hook):
387 self.close_hook = close_hook
388
389 def filename_change_hook(self):
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000390 if self.flist:
391 self.flist.filename_changed_edit(self)
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000392 self.saved_change_hook()
393 if self.ispythonsource(self.io.filename):
394 self.addcolorizer()
395 else:
396 self.rmcolorizer()
397
398 def addcolorizer(self):
399 if self.color:
400 return
401 ##print "Add colorizer"
402 self.per.removefilter(self.undo)
403 self.color = self.ColorDelegator()
404 self.per.insertfilter(self.color)
405 self.per.insertfilter(self.undo)
406
407 def rmcolorizer(self):
408 if not self.color:
409 return
410 ##print "Remove colorizer"
411 self.per.removefilter(self.undo)
412 self.per.removefilter(self.color)
413 self.color = None
414 self.per.insertfilter(self.undo)
415
416 def saved_change_hook(self):
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000417 short = self.short_title()
418 long = self.long_title()
419 if short and long:
420 title = short + " - " + long
421 elif short:
422 title = short
423 elif long:
424 title = long
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000425 else:
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000426 title = "Untitled"
427 icon = short or long or title
428 if not self.get_saved():
429 title = "*%s*" % title
430 icon = "*%s" % icon
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000431 self.top.wm_title(title)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000432 self.top.wm_iconname(icon)
433
434 def get_saved(self):
435 return self.undo.get_saved()
436
437 def set_saved(self, flag):
438 self.undo.set_saved(flag)
439
440 def reset_undo(self):
441 self.undo.reset_undo()
442
443 def short_title(self):
444 filename = self.io.filename
445 if filename:
446 filename = os.path.basename(filename)
447 return filename
448
449 def long_title(self):
450 return self.io.filename or ""
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000451
452 def center_insert_event(self, event):
453 self.center()
454
455 def center(self, mark="insert"):
Guido van Rossum245ddc41999-01-11 14:51:32 +0000456 text = self.text
457 top, bot = self.getwindowlines()
458 lineno = self.getlineno(mark)
459 height = bot - top
460 newtop = max(1, lineno - height/2)
461 text.yview(float(newtop))
462
463 def getwindowlines(self):
464 text = self.text
465 top = self.getlineno("@0,0")
466 bot = self.getlineno("@0,65535")
Guido van Rossum5051f4f1999-01-12 22:09:57 +0000467 if top == bot and text.winfo_height() == 1:
468 # Geometry manager hasn't run yet
Guido van Rossum245ddc41999-01-11 14:51:32 +0000469 height = int(text['height'])
470 bot = top + height - 1
471 return top, bot
472
473 def getlineno(self, mark="insert"):
474 text = self.text
475 return int(float(text.index(mark)))
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000476
477 def close_event(self, event):
478 self.close()
479
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000480 def maybesave(self):
481 if self.io:
482 return self.io.maybesave()
483
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000484 def close(self):
485 self.top.wm_deiconify()
486 self.top.tkraise()
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000487 reply = self.maybesave()
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000488 if reply != "cancel":
Guido van Rossum205afb41999-06-25 16:06:29 +0000489 self._close()
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000490 return reply
491
Guido van Rossum205afb41999-06-25 16:06:29 +0000492 def _close(self):
493 WindowList.unregister_callback(self.postwindowsmenu)
494 if self.close_hook:
495 self.close_hook()
496 self.flist = None
497 colorizing = 0
498 self.unload_extensions()
499 self.io.close(); self.io = None
500 self.undo = None # XXX
501 if self.color:
502 colorizing = self.color.colorizing
503 doh = colorizing and self.top
504 self.color.close(doh) # Cancel colorization
505 self.text = None
506 self.vars = None
507 self.per.close(); self.per = None
508 if not colorizing:
509 self.top.destroy()
510
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000511 def load_extensions(self):
512 self.extensions = {}
513 self.load_standard_extensions()
514
Guido van Rossum205afb41999-06-25 16:06:29 +0000515 def unload_extensions(self):
516 for ins in self.extensions.values():
517 if hasattr(ins, "close"):
518 ins.close()
519 self.extensions = {}
520
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000521 def load_standard_extensions(self):
522 for name in self.get_standard_extension_names():
523 try:
524 self.load_extension(name)
525 except:
526 print "Failed to load extension", `name`
527 import traceback
528 traceback.print_exc()
529
530 def get_standard_extension_names(self):
531 import extend
532 return extend.standard
533
534 def load_extension(self, name):
Guido van Rossum9dd52091999-04-23 14:01:25 +0000535 mod = __import__(name, globals(), locals(), [])
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000536 cls = getattr(mod, name)
537 ins = cls(self)
538 self.extensions[name] = ins
539 kdnames = ["keydefs"]
540 if sys.platform == 'win32':
541 kdnames.append("windows_keydefs")
542 elif sys.platform == 'mac':
543 kdnames.append("mac_keydefs")
544 else:
545 kdnames.append("unix_keydefs")
546 keydefs = {}
547 for kdname in kdnames:
548 if hasattr(ins, kdname):
549 keydefs.update(getattr(ins, kdname))
550 if keydefs:
Guido van Rossum07ec8961999-01-28 22:02:47 +0000551 self.apply_bindings(keydefs)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000552 for vevent in keydefs.keys():
553 methodname = string.replace(vevent, "-", "_")
554 while methodname[:1] == '<':
555 methodname = methodname[1:]
556 while methodname[-1:] == '>':
557 methodname = methodname[:-1]
558 methodname = methodname + "_event"
559 if hasattr(ins, methodname):
560 self.text.bind(vevent, getattr(ins, methodname))
561 if hasattr(ins, "menudefs"):
Guido van Rossum07ec8961999-01-28 22:02:47 +0000562 self.fill_menus(ins.menudefs, keydefs)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000563 return ins
564
Guido van Rossum07ec8961999-01-28 22:02:47 +0000565 def apply_bindings(self, keydefs=None):
566 if keydefs is None:
567 keydefs = self.Bindings.default_keydefs
568 text = self.text
569 text.keydefs = keydefs
570 for event, keylist in keydefs.items():
571 if keylist:
572 apply(text.event_add, (event,) + tuple(keylist))
573
574 def fill_menus(self, defs=None, keydefs=None):
Guido van Rossum85a36a51999-06-10 17:43:17 +0000575 # Fill the menus. Menus that are absent or None in
576 # self.menudict are ignored.
Guido van Rossum07ec8961999-01-28 22:02:47 +0000577 if defs is None:
578 defs = self.Bindings.menudefs
579 if keydefs is None:
580 keydefs = self.Bindings.default_keydefs
581 menudict = self.menudict
582 text = self.text
583 for mname, itemlist in defs:
584 menu = menudict.get(mname)
585 if not menu:
586 continue
587 for item in itemlist:
588 if not item:
589 menu.add_separator()
590 else:
591 label, event = item
592 checkbutton = (label[:1] == '!')
593 if checkbutton:
594 label = label[1:]
595 underline, label = prepstr(label)
596 accelerator = get_accelerator(keydefs, event)
597 def command(text=text, event=event):
598 text.event_generate(event)
599 if checkbutton:
600 var = self.getrawvar(event, BooleanVar)
601 menu.add_checkbutton(label=label, underline=underline,
602 command=command, accelerator=accelerator,
603 variable=var)
604 else:
605 menu.add_command(label=label, underline=underline,
606 command=command, accelerator=accelerator)
Guido van Rossumdef2c961999-05-21 04:38:27 +0000607
Guido van Rossum07ec8961999-01-28 22:02:47 +0000608 def getvar(self, name):
609 var = self.getrawvar(name)
610 if var:
611 return var.get()
Guido van Rossumdef2c961999-05-21 04:38:27 +0000612
Guido van Rossum07ec8961999-01-28 22:02:47 +0000613 def setvar(self, name, value, vartype=None):
614 var = self.getrawvar(name, vartype)
615 if var:
616 var.set(value)
Guido van Rossumdef2c961999-05-21 04:38:27 +0000617
Guido van Rossum07ec8961999-01-28 22:02:47 +0000618 def getrawvar(self, name, vartype=None):
Guido van Rossumb7ebb831999-01-28 22:24:30 +0000619 var = self.vars.get(name)
620 if not var and vartype:
621 self.vars[name] = var = vartype(self.text)
622 return var
Guido van Rossum07ec8961999-01-28 22:02:47 +0000623
Guido van Rossumf4a15081999-06-03 14:32:16 +0000624 # Tk implementations of "virtual text methods" -- each platform
625 # reusing IDLE's support code needs to define these for its GUI's
626 # flavor of widget.
627
628 # Is character at text_index in a Python string? Return 0 for
Guido van Rossum85a36a51999-06-10 17:43:17 +0000629 # "guaranteed no", true for anything else. This info is expensive
630 # to compute ab initio, but is probably already known by the
631 # platform's colorizer.
Guido van Rossumf4a15081999-06-03 14:32:16 +0000632
633 def is_char_in_string(self, text_index):
634 if self.color:
Guido van Rossum85a36a51999-06-10 17:43:17 +0000635 # Return true iff colorizer hasn't (re)gotten this far
636 # yet, or the character is tagged as being in a string
Guido van Rossumf4a15081999-06-03 14:32:16 +0000637 return self.text.tag_prevrange("TODO", text_index) or \
638 "STRING" in self.text.tag_names(text_index)
639 else:
Guido van Rossum85a36a51999-06-10 17:43:17 +0000640 # The colorizer is missing: assume the worst
Guido van Rossumf4a15081999-06-03 14:32:16 +0000641 return 1
642
Guido van Rossum85a36a51999-06-10 17:43:17 +0000643 # If a selection is defined in the text widget, return (start,
644 # end) as Tkinter text indices, otherwise return (None, None)
Guido van Rossum13205601999-06-11 15:03:00 +0000645 def get_selection_indices(self):
Guido van Rossum85a36a51999-06-10 17:43:17 +0000646 try:
647 first = self.text.index("sel.first")
648 last = self.text.index("sel.last")
649 return first, last
650 except TclError:
651 return None, None
652
Guido van Rossum13205601999-06-11 15:03:00 +0000653 # Return the text widget's current view of what a tab stop means
654 # (equivalent width in spaces).
655
656 def get_tabwidth(self):
657 current = self.text['tabs'] or TK_TABWIDTH_DEFAULT
658 return int(current)
659
660 # Set the text widget's current view of what a tab stop means.
661
662 def set_tabwidth(self, newtabwidth):
663 text = self.text
664 if self.get_tabwidth() != newtabwidth:
665 pixels = text.tk.call("font", "measure", text["font"],
666 "-displayof", text.master,
667 "n" * newtabwith)
668 text.configure(tabs=pixels)
669
Guido van Rossum07ec8961999-01-28 22:02:47 +0000670def prepstr(s):
Guido van Rossum85a36a51999-06-10 17:43:17 +0000671 # Helper to extract the underscore from a string, e.g.
672 # prepstr("Co_py") returns (2, "Copy").
Guido van Rossum07ec8961999-01-28 22:02:47 +0000673 i = string.find(s, '_')
674 if i >= 0:
675 s = s[:i] + s[i+1:]
676 return i, s
677
678
679keynames = {
680 'bracketleft': '[',
681 'bracketright': ']',
682 'slash': '/',
683}
684
685def get_accelerator(keydefs, event):
686 keylist = keydefs.get(event)
687 if not keylist:
688 return ""
689 s = keylist[0]
690 s = re.sub(r"-[a-z]\b", lambda m: string.upper(m.group()), s)
691 s = re.sub(r"\b\w+\b", lambda m: keynames.get(m.group(), m.group()), s)
692 s = re.sub("Key-", "", s)
693 s = re.sub("Control-", "Ctrl-", s)
694 s = re.sub("-", "+", s)
695 s = re.sub("><", " ", s)
696 s = re.sub("<", "", s)
697 s = re.sub(">", "", s)
698 return s
699
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000700
701def fixwordbreaks(root):
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000702 # Make sure that Tk's double-click and next/previous word
703 # operations use our definition of a word (i.e. an identifier)
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000704 tk = root.tk
705 tk.call('tcl_wordBreakAfter', 'a b', 0) # make sure word.tcl is loaded
706 tk.call('set', 'tcl_wordchars', '[a-zA-Z0-9_]')
707 tk.call('set', 'tcl_nonwordchars', '[^a-zA-Z0-9_]')
708
709
710def test():
711 root = Tk()
712 fixwordbreaks(root)
713 root.withdraw()
714 if sys.argv[1:]:
715 filename = sys.argv[1]
716 else:
717 filename = None
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000718 edit = EditorWindow(root=root, filename=filename)
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000719 edit.set_close_hook(root.quit)
720 root.mainloop()
721 root.destroy()
722
723if __name__ == '__main__':
724 test()