blob: 0e0e0dcda9197c37232445e7ef4559f1e1530075 [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 Rossum99aabe32000-02-17 16:14:16 +000012# Customization of default window size and font
13# standard
14WIDTH = 80
15HEIGHT = 24
16if sys.platform[:3] == 'win':
17 FONT = ("courier new", 10)
18else:
19 FONT = ("courier", 10)
20if 0:
21 # for demos (on Windows)
22 WIDTH = 70
23 HEIGHT = 16
24 FONT = ("lucida console", 14)
25if 0:
26 # for Windows 98
27 FONT = ("lucida console", 8)
28
Guido van Rossum13205601999-06-11 15:03:00 +000029# The default tab setting for a Text widget, in average-width characters.
30TK_TABWIDTH_DEFAULT = 8
31
Guido van Rossum504b0bf1999-01-02 21:28:54 +000032# File menu
33
34#$ event <<open-module>>
35#$ win <Alt-m>
36#$ unix <Control-x><Control-m>
37
38#$ event <<open-class-browser>>
39#$ win <Alt-c>
40#$ unix <Control-x><Control-b>
41
Guido van Rossumd6e87131999-03-10 05:18:02 +000042#$ event <<open-path-browser>>
43
Guido van Rossum504b0bf1999-01-02 21:28:54 +000044#$ event <<close-window>>
45#$ unix <Control-x><Control-0>
46#$ unix <Control-x><Key-0>
47#$ win <Alt-F4>
48
49# Edit menu
50
51#$ event <<Copy>>
52#$ win <Control-c>
53#$ unix <Alt-w>
54
55#$ event <<Cut>>
56#$ win <Control-x>
57#$ unix <Control-w>
58
59#$ event <<Paste>>
60#$ win <Control-v>
61#$ unix <Control-y>
62
63#$ event <<select-all>>
64#$ win <Alt-a>
65#$ unix <Alt-a>
66
67# Help menu
68
69#$ event <<help>>
70#$ win <F1>
71#$ unix <F1>
72
73#$ event <<about-idle>>
74
75# Events without menu entries
76
77#$ event <<remove-selection>>
78#$ win <Escape>
79
80#$ event <<center-insert>>
81#$ win <Control-l>
82#$ unix <Control-l>
83
84#$ event <<do-nothing>>
85#$ unix <Control-x>
86
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +000087
Guido van Rossum2aeeb551998-10-12 21:01:37 +000088about_title = "About IDLE"
89about_text = """\
Guido van Rossum504b0bf1999-01-02 21:28:54 +000090IDLE %s
Guido van Rossum2aeeb551998-10-12 21:01:37 +000091
Guido van Rossum504b0bf1999-01-02 21:28:54 +000092An Integrated DeveLopment Environment for Python
Guido van Rossum2aeeb551998-10-12 21:01:37 +000093
94by Guido van Rossum
Guido van Rossum504b0bf1999-01-02 21:28:54 +000095""" % idlever.IDLE_VERSION
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +000096
97class EditorWindow:
98
99 from Percolator import Percolator
100 from ColorDelegator import ColorDelegator
101 from UndoDelegator import UndoDelegator
102 from IOBinding import IOBinding
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000103 import Bindings
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000104 from Tkinter import Toplevel
Guido van Rossumec73dc62000-02-15 18:05:15 +0000105 from MultiStatusBar import MultiStatusBar
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000106
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000107 about_title = about_title
108 about_text = about_text
109
Guido van Rossumb7ebb831999-01-28 22:24:30 +0000110 vars = {}
111
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000112 def __init__(self, flist=None, filename=None, key=None, root=None):
Guido van Rossum7de69751999-04-20 15:45:30 +0000113 cprefs = self.ColorDelegator.cprefs
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000114 self.flist = flist
115 root = root or flist.root
Guido van Rossum2aeeb551998-10-12 21:01:37 +0000116 self.root = root
Guido van Rossumb7ebb831999-01-28 22:24:30 +0000117 if flist:
118 self.vars = flist.vars
Guido van Rossum2aeeb551998-10-12 21:01:37 +0000119 self.menubar = Menu(root)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000120 self.top = top = self.Toplevel(root, menu=self.menubar)
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000121 self.vbar = vbar = Scrollbar(top, name='vbar')
Guido van Rossumec73dc62000-02-15 18:05:15 +0000122 self.text_frame = text_frame = Frame(top)
123 self.text = text = Text(text_frame, name='text', padx=5,
Guido van Rossum7de69751999-04-20 15:45:30 +0000124 foreground=cprefs.CNormal[0],
Guido van Rossumdef2c961999-05-21 04:38:27 +0000125 background=cprefs.CNormal[1],
Guido van Rossum7de69751999-04-20 15:45:30 +0000126 highlightcolor=cprefs.CHilite[0],
127 highlightbackground=cprefs.CHilite[1],
128 insertbackground=cprefs.CCursor[1],
Guido van Rossum99aabe32000-02-17 16:14:16 +0000129 width=WIDTH, height=HEIGHT,
Guido van Rossum7de69751999-04-20 15:45:30 +0000130 wrap="none")
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000131
Guido van Rossum2aeeb551998-10-12 21:01:37 +0000132 self.createmenubar()
Guido van Rossum07ec8961999-01-28 22:02:47 +0000133 self.apply_bindings()
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000134
135 self.top.protocol("WM_DELETE_WINDOW", self.close)
136 self.top.bind("<<close-window>>", self.close_event)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000137 text.bind("<<center-insert>>", self.center_insert_event)
138 text.bind("<<help>>", self.help_dialog)
Guido van Rossum416b9611999-08-26 23:06:05 +0000139 text.bind("<<python-docs>>", self.python_docs)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000140 text.bind("<<about-idle>>", self.about_dialog)
141 text.bind("<<open-module>>", self.open_module)
142 text.bind("<<do-nothing>>", lambda event: "break")
143 text.bind("<<select-all>>", self.select_all)
144 text.bind("<<remove-selection>>", self.remove_selection)
145 text.bind("<3>", self.right_menu_event)
146 if flist:
147 flist.inversedict[self] = key
148 if key:
149 flist.dict[key] = self
150 text.bind("<<open-new-window>>", self.flist.new_callback)
151 text.bind("<<close-all-windows>>", self.flist.close_all_callback)
152 text.bind("<<open-class-browser>>", self.open_class_browser)
Guido van Rossumd6e87131999-03-10 05:18:02 +0000153 text.bind("<<open-path-browser>>", self.open_path_browser)
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000154
155 vbar['command'] = text.yview
156 vbar.pack(side=RIGHT, fill=Y)
157
158 text['yscrollcommand'] = vbar.set
Guido van Rossum99aabe32000-02-17 16:14:16 +0000159 text['font'] = FONT
Guido van Rossumec73dc62000-02-15 18:05:15 +0000160 text_frame.pack(side=LEFT, fill=BOTH, expand=1)
161 text.pack(side=TOP, fill=BOTH, expand=1)
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000162 text.focus_set()
163
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000164 self.per = per = self.Percolator(text)
165 if self.ispythonsource(filename):
166 self.color = color = self.ColorDelegator(); per.insertfilter(color)
167 ##print "Initial colorizer"
168 else:
169 ##print "No initial colorizer"
170 self.color = None
171 self.undo = undo = self.UndoDelegator(); per.insertfilter(undo)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000172 self.io = io = self.IOBinding(self)
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000173
Guido van Rossum318a70d1999-05-03 15:49:52 +0000174 text.undo_block_start = undo.undo_block_start
175 text.undo_block_stop = undo.undo_block_stop
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000176 undo.set_saved_change_hook(self.saved_change_hook)
177 io.set_filename_change_hook(self.filename_change_hook)
178
179 if filename:
180 if os.path.exists(filename):
181 io.loadfile(filename)
182 else:
183 io.set_filename(filename)
184
185 self.saved_change_hook()
186
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000187 self.load_extensions()
188
189 menu = self.menudict.get('windows')
190 if menu:
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000191 end = menu.index("end")
192 if end is None:
193 end = -1
194 if end >= 0:
195 menu.add_separator()
196 end = end + 1
197 self.wmenu_end = end
Guido van Rossumc4f752f1999-02-17 17:20:50 +0000198 WindowList.register_callback(self.postwindowsmenu)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000199
Guido van Rossumd395aee1999-06-02 11:04:29 +0000200 # Some abstractions so IDLE extensions are cross-IDE
201 self.askyesno = tkMessageBox.askyesno
202 self.askinteger = tkSimpleDialog.askinteger
203 self.showerror = tkMessageBox.showerror
204
Guido van Rossumdef2c961999-05-21 04:38:27 +0000205 if self.extensions.has_key('AutoIndent'):
206 self.extensions['AutoIndent'].set_indentation_params(
207 self.ispythonsource(filename))
Guido van Rossumec73dc62000-02-15 18:05:15 +0000208 self.set_status_bar()
209
210 def set_status_bar(self):
211 self.status_bar = self.MultiStatusBar(self.text_frame)
212 self.status_bar.set_label('column', 'Col: ?', side=RIGHT)
213 self.status_bar.set_label('line', 'Ln: ?', side=RIGHT)
214 self.status_bar.pack(side=BOTTOM, fill=X)
215 self.text.bind('<KeyRelease>', self.set_line_and_column)
216 self.text.bind('<ButtonRelease>', self.set_line_and_column)
217 self.text.after_idle(self.set_line_and_column)
218
219 def set_line_and_column(self, event=None):
220 line, column = string.split(self.text.index(INSERT), '.')
221 self.status_bar.set_label('column', 'Col: %s' % column)
222 self.status_bar.set_label('line', 'Ln: %s' % line)
Guido van Rossumdef2c961999-05-21 04:38:27 +0000223
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000224 def wakeup(self):
Guido van Rossum36911a11999-01-18 15:18:57 +0000225 if self.top.wm_state() == "iconic":
226 self.top.wm_deiconify()
227 else:
228 self.top.tkraise()
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000229 self.text.focus_set()
230
Guido van Rossume7b2e651998-10-12 23:56:08 +0000231 menu_specs = [
Guido van Rossumb5eed031998-11-27 03:19:07 +0000232 ("file", "_File"),
233 ("edit", "_Edit"),
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000234 ("windows", "_Windows"),
Guido van Rossumb5eed031998-11-27 03:19:07 +0000235 ("help", "_Help"),
Guido van Rossume7b2e651998-10-12 23:56:08 +0000236 ]
237
Guido van Rossum2aeeb551998-10-12 21:01:37 +0000238 def createmenubar(self):
239 mbar = self.menubar
Guido van Rossum07ec8961999-01-28 22:02:47 +0000240 self.menudict = menudict = {}
Guido van Rossume7b2e651998-10-12 23:56:08 +0000241 for name, label in self.menu_specs:
Guido van Rossum07ec8961999-01-28 22:02:47 +0000242 underline, label = prepstr(label)
243 menudict[name] = menu = Menu(mbar, name=name)
Guido van Rossumb5eed031998-11-27 03:19:07 +0000244 mbar.add_cascade(label=label, menu=menu, underline=underline)
Guido van Rossum07ec8961999-01-28 22:02:47 +0000245 self.fill_menus()
Guido van Rossum2aeeb551998-10-12 21:01:37 +0000246
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000247 def postwindowsmenu(self):
248 # Only called when Windows menu exists
Guido van Rossum85a36a51999-06-10 17:43:17 +0000249 # XXX Actually, this Just-In-Time updating interferes badly
250 # XXX with the tear-off feature. It would be better to update
251 # XXX all Windows menus whenever the list of windows changes.
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000252 menu = self.menudict['windows']
253 end = menu.index("end")
254 if end is None:
255 end = -1
256 if end > self.wmenu_end:
257 menu.delete(self.wmenu_end+1, end)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000258 WindowList.add_windows_to_menu(menu)
259
260 rmenu = None
261
262 def right_menu_event(self, event):
263 self.text.tag_remove("sel", "1.0", "end")
264 self.text.mark_set("insert", "@%d,%d" % (event.x, event.y))
265 if not self.rmenu:
266 self.make_rmenu()
267 rmenu = self.rmenu
268 self.event = event
269 iswin = sys.platform[:3] == 'win'
270 if iswin:
271 self.text.config(cursor="arrow")
272 rmenu.tk_popup(event.x_root, event.y_root)
273 if iswin:
274 self.text.config(cursor="ibeam")
275
276 rmenu_specs = [
277 # ("Label", "<<virtual-event>>"), ...
278 ("Close", "<<close-window>>"), # Example
279 ]
280
281 def make_rmenu(self):
282 rmenu = Menu(self.text, tearoff=0)
283 for label, eventname in self.rmenu_specs:
284 def command(text=self.text, eventname=eventname):
285 text.event_generate(eventname)
286 rmenu.add_command(label=label, command=command)
287 self.rmenu = rmenu
288
Guido van Rossume7b2e651998-10-12 23:56:08 +0000289 def about_dialog(self, event=None):
Guido van Rossum2aeeb551998-10-12 21:01:37 +0000290 tkMessageBox.showinfo(self.about_title, self.about_text,
291 master=self.text)
292
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000293 helpfile = "help.txt"
294
Guido van Rossume7b2e651998-10-12 23:56:08 +0000295 def help_dialog(self, event=None):
Guido van Rossum416b9611999-08-26 23:06:05 +0000296 try:
297 helpfile = os.path.join(os.path.dirname(__file__), self.helpfile)
298 except NameError:
299 helpfile = self.helpfile
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000300 if self.flist:
301 self.flist.open(helpfile)
302 else:
303 self.io.loadfile(helpfile)
304
Guido van Rossum416b9611999-08-26 23:06:05 +0000305 # XXX Fix these for Windows
306 help_viewer = "netscape -remote 'openurl(%(url)s)' 2>/dev/null || " \
307 "netscape %(url)s &"
308 help_url = "http://www.python.org/doc/current/"
309
310 def python_docs(self, event=None):
311 cmd = self.help_viewer % {"url": self.help_url}
312 os.system(cmd)
313
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000314 def select_all(self, event=None):
315 self.text.tag_add("sel", "1.0", "end-1c")
316 self.text.mark_set("insert", "1.0")
317 self.text.see("insert")
318 return "break"
319
320 def remove_selection(self, event=None):
321 self.text.tag_remove("sel", "1.0", "end")
322 self.text.see("insert")
323
Guido van Rossumb3418881998-10-13 03:45:15 +0000324 def open_module(self, event=None):
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000325 # XXX Shouldn't this be in IOBinding or in FileList?
Guido van Rossumb3418881998-10-13 03:45:15 +0000326 try:
327 name = self.text.get("sel.first", "sel.last")
328 except TclError:
329 name = ""
330 else:
331 name = string.strip(name)
332 if not name:
333 name = tkSimpleDialog.askstring("Module",
Guido van Rossume1dedc01998-10-16 16:09:57 +0000334 "Enter the name of a Python module\n"
335 "to search on sys.path and open:",
Guido van Rossumb3418881998-10-13 03:45:15 +0000336 parent=self.text)
337 if name:
338 name = string.strip(name)
339 if not name:
340 return
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000341 # XXX Ought to support package syntax
342 # XXX Ought to insert current file's directory in front of path
Guido van Rossumb3418881998-10-13 03:45:15 +0000343 try:
344 (f, file, (suffix, mode, type)) = imp.find_module(name)
Guido van Rossum74311b21999-06-01 18:27:14 +0000345 except (NameError, ImportError), msg:
Guido van Rossumb3418881998-10-13 03:45:15 +0000346 tkMessageBox.showerror("Import error", str(msg), parent=self.text)
347 return
348 if type != imp.PY_SOURCE:
349 tkMessageBox.showerror("Unsupported type",
350 "%s is not a source module" % name, parent=self.text)
351 return
352 if f:
353 f.close()
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000354 if self.flist:
355 self.flist.open(file)
356 else:
357 self.io.loadfile(file)
358
359 def open_class_browser(self, event=None):
360 filename = self.io.filename
361 if not filename:
362 tkMessageBox.showerror(
363 "No filename",
364 "This buffer has no associated filename",
365 master=self.text)
Guido van Rossum74311b21999-06-01 18:27:14 +0000366 self.text.focus_set()
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000367 return None
368 head, tail = os.path.split(filename)
369 base, ext = os.path.splitext(tail)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000370 import ClassBrowser
371 ClassBrowser.ClassBrowser(self.flist, base, [head])
Guido van Rossumdef2c961999-05-21 04:38:27 +0000372
Guido van Rossumd6e87131999-03-10 05:18:02 +0000373 def open_path_browser(self, event=None):
374 import PathBrowser
375 PathBrowser.PathBrowser(self.flist)
Guido van Rossum2aeeb551998-10-12 21:01:37 +0000376
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000377 def gotoline(self, lineno):
378 if lineno is not None and lineno > 0:
379 self.text.mark_set("insert", "%d.0" % lineno)
380 self.text.tag_remove("sel", "1.0", "end")
381 self.text.tag_add("sel", "insert", "insert +1l")
382 self.center()
383
384 def ispythonsource(self, filename):
385 if not filename:
386 return 1
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000387 base, ext = os.path.splitext(os.path.basename(filename))
388 if os.path.normcase(ext) in (".py", ".pyw"):
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000389 return 1
390 try:
391 f = open(filename)
392 line = f.readline()
393 f.close()
394 except IOError:
395 return 0
396 return line[:2] == '#!' and string.find(line, 'python') >= 0
397
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000398 def close_hook(self):
399 if self.flist:
400 self.flist.close_edit(self)
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000401
402 def set_close_hook(self, close_hook):
403 self.close_hook = close_hook
404
405 def filename_change_hook(self):
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000406 if self.flist:
407 self.flist.filename_changed_edit(self)
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000408 self.saved_change_hook()
409 if self.ispythonsource(self.io.filename):
410 self.addcolorizer()
411 else:
412 self.rmcolorizer()
413
414 def addcolorizer(self):
415 if self.color:
416 return
417 ##print "Add colorizer"
418 self.per.removefilter(self.undo)
419 self.color = self.ColorDelegator()
420 self.per.insertfilter(self.color)
421 self.per.insertfilter(self.undo)
422
423 def rmcolorizer(self):
424 if not self.color:
425 return
426 ##print "Remove colorizer"
427 self.per.removefilter(self.undo)
428 self.per.removefilter(self.color)
429 self.color = None
430 self.per.insertfilter(self.undo)
431
432 def saved_change_hook(self):
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000433 short = self.short_title()
434 long = self.long_title()
435 if short and long:
436 title = short + " - " + long
437 elif short:
438 title = short
439 elif long:
440 title = long
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000441 else:
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000442 title = "Untitled"
443 icon = short or long or title
444 if not self.get_saved():
445 title = "*%s*" % title
446 icon = "*%s" % icon
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000447 self.top.wm_title(title)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000448 self.top.wm_iconname(icon)
449
450 def get_saved(self):
451 return self.undo.get_saved()
452
453 def set_saved(self, flag):
454 self.undo.set_saved(flag)
455
456 def reset_undo(self):
457 self.undo.reset_undo()
458
459 def short_title(self):
460 filename = self.io.filename
461 if filename:
462 filename = os.path.basename(filename)
463 return filename
464
465 def long_title(self):
466 return self.io.filename or ""
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000467
468 def center_insert_event(self, event):
469 self.center()
470
471 def center(self, mark="insert"):
Guido van Rossum245ddc41999-01-11 14:51:32 +0000472 text = self.text
473 top, bot = self.getwindowlines()
474 lineno = self.getlineno(mark)
475 height = bot - top
476 newtop = max(1, lineno - height/2)
477 text.yview(float(newtop))
478
479 def getwindowlines(self):
480 text = self.text
481 top = self.getlineno("@0,0")
482 bot = self.getlineno("@0,65535")
Guido van Rossum5051f4f1999-01-12 22:09:57 +0000483 if top == bot and text.winfo_height() == 1:
484 # Geometry manager hasn't run yet
Guido van Rossum245ddc41999-01-11 14:51:32 +0000485 height = int(text['height'])
486 bot = top + height - 1
487 return top, bot
488
489 def getlineno(self, mark="insert"):
490 text = self.text
491 return int(float(text.index(mark)))
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000492
493 def close_event(self, event):
494 self.close()
495
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000496 def maybesave(self):
497 if self.io:
498 return self.io.maybesave()
499
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000500 def close(self):
501 self.top.wm_deiconify()
502 self.top.tkraise()
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000503 reply = self.maybesave()
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000504 if reply != "cancel":
Guido van Rossum205afb41999-06-25 16:06:29 +0000505 self._close()
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000506 return reply
507
Guido van Rossum205afb41999-06-25 16:06:29 +0000508 def _close(self):
509 WindowList.unregister_callback(self.postwindowsmenu)
510 if self.close_hook:
511 self.close_hook()
512 self.flist = None
513 colorizing = 0
514 self.unload_extensions()
515 self.io.close(); self.io = None
516 self.undo = None # XXX
517 if self.color:
518 colorizing = self.color.colorizing
519 doh = colorizing and self.top
520 self.color.close(doh) # Cancel colorization
521 self.text = None
522 self.vars = None
523 self.per.close(); self.per = None
524 if not colorizing:
525 self.top.destroy()
526
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000527 def load_extensions(self):
528 self.extensions = {}
529 self.load_standard_extensions()
530
Guido van Rossum205afb41999-06-25 16:06:29 +0000531 def unload_extensions(self):
532 for ins in self.extensions.values():
533 if hasattr(ins, "close"):
534 ins.close()
535 self.extensions = {}
536
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000537 def load_standard_extensions(self):
538 for name in self.get_standard_extension_names():
539 try:
540 self.load_extension(name)
541 except:
542 print "Failed to load extension", `name`
543 import traceback
544 traceback.print_exc()
545
546 def get_standard_extension_names(self):
547 import extend
548 return extend.standard
549
550 def load_extension(self, name):
Guido van Rossum9dd52091999-04-23 14:01:25 +0000551 mod = __import__(name, globals(), locals(), [])
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000552 cls = getattr(mod, name)
553 ins = cls(self)
554 self.extensions[name] = ins
555 kdnames = ["keydefs"]
556 if sys.platform == 'win32':
557 kdnames.append("windows_keydefs")
558 elif sys.platform == 'mac':
559 kdnames.append("mac_keydefs")
560 else:
561 kdnames.append("unix_keydefs")
562 keydefs = {}
563 for kdname in kdnames:
564 if hasattr(ins, kdname):
565 keydefs.update(getattr(ins, kdname))
566 if keydefs:
Guido van Rossum07ec8961999-01-28 22:02:47 +0000567 self.apply_bindings(keydefs)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000568 for vevent in keydefs.keys():
569 methodname = string.replace(vevent, "-", "_")
570 while methodname[:1] == '<':
571 methodname = methodname[1:]
572 while methodname[-1:] == '>':
573 methodname = methodname[:-1]
574 methodname = methodname + "_event"
575 if hasattr(ins, methodname):
576 self.text.bind(vevent, getattr(ins, methodname))
577 if hasattr(ins, "menudefs"):
Guido van Rossum07ec8961999-01-28 22:02:47 +0000578 self.fill_menus(ins.menudefs, keydefs)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000579 return ins
580
Guido van Rossum07ec8961999-01-28 22:02:47 +0000581 def apply_bindings(self, keydefs=None):
582 if keydefs is None:
583 keydefs = self.Bindings.default_keydefs
584 text = self.text
585 text.keydefs = keydefs
586 for event, keylist in keydefs.items():
587 if keylist:
588 apply(text.event_add, (event,) + tuple(keylist))
589
590 def fill_menus(self, defs=None, keydefs=None):
Guido van Rossum85a36a51999-06-10 17:43:17 +0000591 # Fill the menus. Menus that are absent or None in
592 # self.menudict are ignored.
Guido van Rossum07ec8961999-01-28 22:02:47 +0000593 if defs is None:
594 defs = self.Bindings.menudefs
595 if keydefs is None:
596 keydefs = self.Bindings.default_keydefs
597 menudict = self.menudict
598 text = self.text
599 for mname, itemlist in defs:
600 menu = menudict.get(mname)
601 if not menu:
602 continue
603 for item in itemlist:
604 if not item:
605 menu.add_separator()
606 else:
607 label, event = item
608 checkbutton = (label[:1] == '!')
609 if checkbutton:
610 label = label[1:]
611 underline, label = prepstr(label)
612 accelerator = get_accelerator(keydefs, event)
613 def command(text=text, event=event):
614 text.event_generate(event)
615 if checkbutton:
616 var = self.getrawvar(event, BooleanVar)
617 menu.add_checkbutton(label=label, underline=underline,
618 command=command, accelerator=accelerator,
619 variable=var)
620 else:
621 menu.add_command(label=label, underline=underline,
622 command=command, accelerator=accelerator)
Guido van Rossumdef2c961999-05-21 04:38:27 +0000623
Guido van Rossum07ec8961999-01-28 22:02:47 +0000624 def getvar(self, name):
625 var = self.getrawvar(name)
626 if var:
627 return var.get()
Guido van Rossumdef2c961999-05-21 04:38:27 +0000628
Guido van Rossum07ec8961999-01-28 22:02:47 +0000629 def setvar(self, name, value, vartype=None):
630 var = self.getrawvar(name, vartype)
631 if var:
632 var.set(value)
Guido van Rossumdef2c961999-05-21 04:38:27 +0000633
Guido van Rossum07ec8961999-01-28 22:02:47 +0000634 def getrawvar(self, name, vartype=None):
Guido van Rossumb7ebb831999-01-28 22:24:30 +0000635 var = self.vars.get(name)
636 if not var and vartype:
637 self.vars[name] = var = vartype(self.text)
638 return var
Guido van Rossum07ec8961999-01-28 22:02:47 +0000639
Guido van Rossumf4a15081999-06-03 14:32:16 +0000640 # Tk implementations of "virtual text methods" -- each platform
641 # reusing IDLE's support code needs to define these for its GUI's
642 # flavor of widget.
643
644 # Is character at text_index in a Python string? Return 0 for
Guido van Rossum85a36a51999-06-10 17:43:17 +0000645 # "guaranteed no", true for anything else. This info is expensive
646 # to compute ab initio, but is probably already known by the
647 # platform's colorizer.
Guido van Rossumf4a15081999-06-03 14:32:16 +0000648
649 def is_char_in_string(self, text_index):
650 if self.color:
Guido van Rossum85a36a51999-06-10 17:43:17 +0000651 # Return true iff colorizer hasn't (re)gotten this far
652 # yet, or the character is tagged as being in a string
Guido van Rossumf4a15081999-06-03 14:32:16 +0000653 return self.text.tag_prevrange("TODO", text_index) or \
654 "STRING" in self.text.tag_names(text_index)
655 else:
Guido van Rossum85a36a51999-06-10 17:43:17 +0000656 # The colorizer is missing: assume the worst
Guido van Rossumf4a15081999-06-03 14:32:16 +0000657 return 1
658
Guido van Rossum85a36a51999-06-10 17:43:17 +0000659 # If a selection is defined in the text widget, return (start,
660 # end) as Tkinter text indices, otherwise return (None, None)
Guido van Rossum13205601999-06-11 15:03:00 +0000661 def get_selection_indices(self):
Guido van Rossum85a36a51999-06-10 17:43:17 +0000662 try:
663 first = self.text.index("sel.first")
664 last = self.text.index("sel.last")
665 return first, last
666 except TclError:
667 return None, None
668
Guido van Rossum13205601999-06-11 15:03:00 +0000669 # Return the text widget's current view of what a tab stop means
670 # (equivalent width in spaces).
671
672 def get_tabwidth(self):
673 current = self.text['tabs'] or TK_TABWIDTH_DEFAULT
674 return int(current)
675
676 # Set the text widget's current view of what a tab stop means.
677
678 def set_tabwidth(self, newtabwidth):
679 text = self.text
680 if self.get_tabwidth() != newtabwidth:
681 pixels = text.tk.call("font", "measure", text["font"],
682 "-displayof", text.master,
683 "n" * newtabwith)
684 text.configure(tabs=pixels)
685
Guido van Rossum07ec8961999-01-28 22:02:47 +0000686def prepstr(s):
Guido van Rossum85a36a51999-06-10 17:43:17 +0000687 # Helper to extract the underscore from a string, e.g.
688 # prepstr("Co_py") returns (2, "Copy").
Guido van Rossum07ec8961999-01-28 22:02:47 +0000689 i = string.find(s, '_')
690 if i >= 0:
691 s = s[:i] + s[i+1:]
692 return i, s
693
694
695keynames = {
696 'bracketleft': '[',
697 'bracketright': ']',
698 'slash': '/',
699}
700
701def get_accelerator(keydefs, event):
702 keylist = keydefs.get(event)
703 if not keylist:
704 return ""
705 s = keylist[0]
706 s = re.sub(r"-[a-z]\b", lambda m: string.upper(m.group()), s)
707 s = re.sub(r"\b\w+\b", lambda m: keynames.get(m.group(), m.group()), s)
708 s = re.sub("Key-", "", s)
709 s = re.sub("Control-", "Ctrl-", s)
710 s = re.sub("-", "+", s)
711 s = re.sub("><", " ", s)
712 s = re.sub("<", "", s)
713 s = re.sub(">", "", s)
714 return s
715
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000716
717def fixwordbreaks(root):
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000718 # Make sure that Tk's double-click and next/previous word
719 # operations use our definition of a word (i.e. an identifier)
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000720 tk = root.tk
721 tk.call('tcl_wordBreakAfter', 'a b', 0) # make sure word.tcl is loaded
722 tk.call('set', 'tcl_wordchars', '[a-zA-Z0-9_]')
723 tk.call('set', 'tcl_nonwordchars', '[^a-zA-Z0-9_]')
724
725
726def test():
727 root = Tk()
728 fixwordbreaks(root)
729 root.withdraw()
730 if sys.argv[1:]:
731 filename = sys.argv[1]
732 else:
733 filename = None
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000734 edit = EditorWindow(root=root, filename=filename)
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000735 edit.set_close_hook(root.quit)
736 root.mainloop()
737 root.destroy()
738
739if __name__ == '__main__':
740 test()