blob: bc4edde47603d26c480c88731e5d9c15db1a3d13 [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
12# File menu
13
14#$ event <<open-module>>
15#$ win <Alt-m>
16#$ unix <Control-x><Control-m>
17
18#$ event <<open-class-browser>>
19#$ win <Alt-c>
20#$ unix <Control-x><Control-b>
21
Guido van Rossumd6e87131999-03-10 05:18:02 +000022#$ event <<open-path-browser>>
23
Guido van Rossum504b0bf1999-01-02 21:28:54 +000024#$ event <<close-window>>
25#$ unix <Control-x><Control-0>
26#$ unix <Control-x><Key-0>
27#$ win <Alt-F4>
28
29# Edit menu
30
31#$ event <<Copy>>
32#$ win <Control-c>
33#$ unix <Alt-w>
34
35#$ event <<Cut>>
36#$ win <Control-x>
37#$ unix <Control-w>
38
39#$ event <<Paste>>
40#$ win <Control-v>
41#$ unix <Control-y>
42
43#$ event <<select-all>>
44#$ win <Alt-a>
45#$ unix <Alt-a>
46
47# Help menu
48
49#$ event <<help>>
50#$ win <F1>
51#$ unix <F1>
52
53#$ event <<about-idle>>
54
55# Events without menu entries
56
57#$ event <<remove-selection>>
58#$ win <Escape>
59
60#$ event <<center-insert>>
61#$ win <Control-l>
62#$ unix <Control-l>
63
64#$ event <<do-nothing>>
65#$ unix <Control-x>
66
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +000067
Guido van Rossum2aeeb551998-10-12 21:01:37 +000068about_title = "About IDLE"
69about_text = """\
Guido van Rossum504b0bf1999-01-02 21:28:54 +000070IDLE %s
Guido van Rossum2aeeb551998-10-12 21:01:37 +000071
Guido van Rossum504b0bf1999-01-02 21:28:54 +000072An Integrated DeveLopment Environment for Python
Guido van Rossum2aeeb551998-10-12 21:01:37 +000073
74by Guido van Rossum
Guido van Rossum504b0bf1999-01-02 21:28:54 +000075""" % idlever.IDLE_VERSION
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +000076
77class EditorWindow:
78
79 from Percolator import Percolator
80 from ColorDelegator import ColorDelegator
81 from UndoDelegator import UndoDelegator
82 from IOBinding import IOBinding
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +000083 import Bindings
Guido van Rossum504b0bf1999-01-02 21:28:54 +000084 from Tkinter import Toplevel
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +000085
Guido van Rossum504b0bf1999-01-02 21:28:54 +000086 about_title = about_title
87 about_text = about_text
88
Guido van Rossumb7ebb831999-01-28 22:24:30 +000089 vars = {}
90
Guido van Rossum504b0bf1999-01-02 21:28:54 +000091 def __init__(self, flist=None, filename=None, key=None, root=None):
Guido van Rossum7de69751999-04-20 15:45:30 +000092 cprefs = self.ColorDelegator.cprefs
Guido van Rossum504b0bf1999-01-02 21:28:54 +000093 self.flist = flist
94 root = root or flist.root
Guido van Rossum2aeeb551998-10-12 21:01:37 +000095 self.root = root
Guido van Rossumb7ebb831999-01-28 22:24:30 +000096 if flist:
97 self.vars = flist.vars
Guido van Rossum2aeeb551998-10-12 21:01:37 +000098 self.menubar = Menu(root)
Guido van Rossum504b0bf1999-01-02 21:28:54 +000099 self.top = top = self.Toplevel(root, menu=self.menubar)
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000100 self.vbar = vbar = Scrollbar(top, name='vbar')
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000101 self.text = text = Text(top, name='text', padx=5,
Guido van Rossum7de69751999-04-20 15:45:30 +0000102 foreground=cprefs.CNormal[0],
Guido van Rossumdef2c961999-05-21 04:38:27 +0000103 background=cprefs.CNormal[1],
Guido van Rossum7de69751999-04-20 15:45:30 +0000104 highlightcolor=cprefs.CHilite[0],
105 highlightbackground=cprefs.CHilite[1],
106 insertbackground=cprefs.CCursor[1],
107 wrap="none")
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000108
Guido van Rossum2aeeb551998-10-12 21:01:37 +0000109 self.createmenubar()
Guido van Rossum07ec8961999-01-28 22:02:47 +0000110 self.apply_bindings()
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000111
112 self.top.protocol("WM_DELETE_WINDOW", self.close)
113 self.top.bind("<<close-window>>", self.close_event)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000114 text.bind("<<center-insert>>", self.center_insert_event)
115 text.bind("<<help>>", self.help_dialog)
116 text.bind("<<about-idle>>", self.about_dialog)
117 text.bind("<<open-module>>", self.open_module)
118 text.bind("<<do-nothing>>", lambda event: "break")
119 text.bind("<<select-all>>", self.select_all)
120 text.bind("<<remove-selection>>", self.remove_selection)
121 text.bind("<3>", self.right_menu_event)
122 if flist:
123 flist.inversedict[self] = key
124 if key:
125 flist.dict[key] = self
126 text.bind("<<open-new-window>>", self.flist.new_callback)
127 text.bind("<<close-all-windows>>", self.flist.close_all_callback)
128 text.bind("<<open-class-browser>>", self.open_class_browser)
Guido van Rossumd6e87131999-03-10 05:18:02 +0000129 text.bind("<<open-path-browser>>", self.open_path_browser)
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000130
131 vbar['command'] = text.yview
132 vbar.pack(side=RIGHT, fill=Y)
133
134 text['yscrollcommand'] = vbar.set
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000135 if sys.platform[:3] == 'win':
136 text['font'] = ("lucida console", 8)
Guido van Rossumdef2c961999-05-21 04:38:27 +0000137# text['font'] = ("courier new", 10)
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000138 text.pack(side=LEFT, fill=BOTH, expand=1)
139 text.focus_set()
140
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000141 self.per = per = self.Percolator(text)
142 if self.ispythonsource(filename):
143 self.color = color = self.ColorDelegator(); per.insertfilter(color)
144 ##print "Initial colorizer"
145 else:
146 ##print "No initial colorizer"
147 self.color = None
148 self.undo = undo = self.UndoDelegator(); per.insertfilter(undo)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000149 self.io = io = self.IOBinding(self)
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000150
Guido van Rossum318a70d1999-05-03 15:49:52 +0000151 text.undo_block_start = undo.undo_block_start
152 text.undo_block_stop = undo.undo_block_stop
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000153 undo.set_saved_change_hook(self.saved_change_hook)
154 io.set_filename_change_hook(self.filename_change_hook)
155
156 if filename:
157 if os.path.exists(filename):
158 io.loadfile(filename)
159 else:
160 io.set_filename(filename)
161
162 self.saved_change_hook()
163
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000164 self.load_extensions()
165
166 menu = self.menudict.get('windows')
167 if menu:
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000168 end = menu.index("end")
169 if end is None:
170 end = -1
171 if end >= 0:
172 menu.add_separator()
173 end = end + 1
174 self.wmenu_end = end
Guido van Rossumc4f752f1999-02-17 17:20:50 +0000175 WindowList.register_callback(self.postwindowsmenu)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000176
Guido van Rossumd395aee1999-06-02 11:04:29 +0000177 # Some abstractions so IDLE extensions are cross-IDE
178 self.askyesno = tkMessageBox.askyesno
179 self.askinteger = tkSimpleDialog.askinteger
180 self.showerror = tkMessageBox.showerror
181
Guido van Rossumdef2c961999-05-21 04:38:27 +0000182 if self.extensions.has_key('AutoIndent'):
183 self.extensions['AutoIndent'].set_indentation_params(
184 self.ispythonsource(filename))
185
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000186 def wakeup(self):
Guido van Rossum36911a11999-01-18 15:18:57 +0000187 if self.top.wm_state() == "iconic":
188 self.top.wm_deiconify()
189 else:
190 self.top.tkraise()
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000191 self.text.focus_set()
192
Guido van Rossume7b2e651998-10-12 23:56:08 +0000193 menu_specs = [
Guido van Rossumb5eed031998-11-27 03:19:07 +0000194 ("file", "_File"),
195 ("edit", "_Edit"),
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000196 ("windows", "_Windows"),
Guido van Rossumb5eed031998-11-27 03:19:07 +0000197 ("help", "_Help"),
Guido van Rossume7b2e651998-10-12 23:56:08 +0000198 ]
199
Guido van Rossum2aeeb551998-10-12 21:01:37 +0000200 def createmenubar(self):
201 mbar = self.menubar
Guido van Rossum07ec8961999-01-28 22:02:47 +0000202 self.menudict = menudict = {}
Guido van Rossume7b2e651998-10-12 23:56:08 +0000203 for name, label in self.menu_specs:
Guido van Rossum07ec8961999-01-28 22:02:47 +0000204 underline, label = prepstr(label)
205 menudict[name] = menu = Menu(mbar, name=name)
Guido van Rossumb5eed031998-11-27 03:19:07 +0000206 mbar.add_cascade(label=label, menu=menu, underline=underline)
Guido van Rossum07ec8961999-01-28 22:02:47 +0000207 self.fill_menus()
Guido van Rossum2aeeb551998-10-12 21:01:37 +0000208
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000209 def postwindowsmenu(self):
210 # Only called when Windows menu exists
Guido van Rossum85a36a51999-06-10 17:43:17 +0000211 # XXX Actually, this Just-In-Time updating interferes badly
212 # XXX with the tear-off feature. It would be better to update
213 # XXX all Windows menus whenever the list of windows changes.
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000214 menu = self.menudict['windows']
215 end = menu.index("end")
216 if end is None:
217 end = -1
218 if end > self.wmenu_end:
219 menu.delete(self.wmenu_end+1, end)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000220 WindowList.add_windows_to_menu(menu)
221
222 rmenu = None
223
224 def right_menu_event(self, event):
225 self.text.tag_remove("sel", "1.0", "end")
226 self.text.mark_set("insert", "@%d,%d" % (event.x, event.y))
227 if not self.rmenu:
228 self.make_rmenu()
229 rmenu = self.rmenu
230 self.event = event
231 iswin = sys.platform[:3] == 'win'
232 if iswin:
233 self.text.config(cursor="arrow")
234 rmenu.tk_popup(event.x_root, event.y_root)
235 if iswin:
236 self.text.config(cursor="ibeam")
237
238 rmenu_specs = [
239 # ("Label", "<<virtual-event>>"), ...
240 ("Close", "<<close-window>>"), # Example
241 ]
242
243 def make_rmenu(self):
244 rmenu = Menu(self.text, tearoff=0)
245 for label, eventname in self.rmenu_specs:
246 def command(text=self.text, eventname=eventname):
247 text.event_generate(eventname)
248 rmenu.add_command(label=label, command=command)
249 self.rmenu = rmenu
250
Guido van Rossume7b2e651998-10-12 23:56:08 +0000251 def about_dialog(self, event=None):
Guido van Rossum2aeeb551998-10-12 21:01:37 +0000252 tkMessageBox.showinfo(self.about_title, self.about_text,
253 master=self.text)
254
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000255 helpfile = "help.txt"
256
Guido van Rossume7b2e651998-10-12 23:56:08 +0000257 def help_dialog(self, event=None):
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000258 helpfile = self.helpfile
259 if not os.path.exists(helpfile):
260 base = os.path.basename(self.helpfile)
261 for dir in sys.path:
262 fullname = os.path.join(dir, base)
263 if os.path.exists(fullname):
264 helpfile = fullname
265 break
266 if self.flist:
267 self.flist.open(helpfile)
268 else:
269 self.io.loadfile(helpfile)
270
271 def select_all(self, event=None):
272 self.text.tag_add("sel", "1.0", "end-1c")
273 self.text.mark_set("insert", "1.0")
274 self.text.see("insert")
275 return "break"
276
277 def remove_selection(self, event=None):
278 self.text.tag_remove("sel", "1.0", "end")
279 self.text.see("insert")
280
Guido van Rossumb3418881998-10-13 03:45:15 +0000281 def open_module(self, event=None):
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000282 # XXX Shouldn't this be in IOBinding or in FileList?
Guido van Rossumb3418881998-10-13 03:45:15 +0000283 try:
284 name = self.text.get("sel.first", "sel.last")
285 except TclError:
286 name = ""
287 else:
288 name = string.strip(name)
289 if not name:
290 name = tkSimpleDialog.askstring("Module",
Guido van Rossume1dedc01998-10-16 16:09:57 +0000291 "Enter the name of a Python module\n"
292 "to search on sys.path and open:",
Guido van Rossumb3418881998-10-13 03:45:15 +0000293 parent=self.text)
294 if name:
295 name = string.strip(name)
296 if not name:
297 return
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000298 # XXX Ought to support package syntax
299 # XXX Ought to insert current file's directory in front of path
Guido van Rossumb3418881998-10-13 03:45:15 +0000300 try:
301 (f, file, (suffix, mode, type)) = imp.find_module(name)
Guido van Rossum74311b21999-06-01 18:27:14 +0000302 except (NameError, ImportError), msg:
Guido van Rossumb3418881998-10-13 03:45:15 +0000303 tkMessageBox.showerror("Import error", str(msg), parent=self.text)
304 return
305 if type != imp.PY_SOURCE:
306 tkMessageBox.showerror("Unsupported type",
307 "%s is not a source module" % name, parent=self.text)
308 return
309 if f:
310 f.close()
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000311 if self.flist:
312 self.flist.open(file)
313 else:
314 self.io.loadfile(file)
315
316 def open_class_browser(self, event=None):
317 filename = self.io.filename
318 if not filename:
319 tkMessageBox.showerror(
320 "No filename",
321 "This buffer has no associated filename",
322 master=self.text)
Guido van Rossum74311b21999-06-01 18:27:14 +0000323 self.text.focus_set()
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000324 return None
325 head, tail = os.path.split(filename)
326 base, ext = os.path.splitext(tail)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000327 import ClassBrowser
328 ClassBrowser.ClassBrowser(self.flist, base, [head])
Guido van Rossumdef2c961999-05-21 04:38:27 +0000329
Guido van Rossumd6e87131999-03-10 05:18:02 +0000330 def open_path_browser(self, event=None):
331 import PathBrowser
332 PathBrowser.PathBrowser(self.flist)
Guido van Rossum2aeeb551998-10-12 21:01:37 +0000333
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000334 def gotoline(self, lineno):
335 if lineno is not None and lineno > 0:
336 self.text.mark_set("insert", "%d.0" % lineno)
337 self.text.tag_remove("sel", "1.0", "end")
338 self.text.tag_add("sel", "insert", "insert +1l")
339 self.center()
340
341 def ispythonsource(self, filename):
342 if not filename:
343 return 1
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000344 base, ext = os.path.splitext(os.path.basename(filename))
345 if os.path.normcase(ext) in (".py", ".pyw"):
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000346 return 1
347 try:
348 f = open(filename)
349 line = f.readline()
350 f.close()
351 except IOError:
352 return 0
353 return line[:2] == '#!' and string.find(line, 'python') >= 0
354
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000355 def close_hook(self):
356 if self.flist:
357 self.flist.close_edit(self)
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000358
359 def set_close_hook(self, close_hook):
360 self.close_hook = close_hook
361
362 def filename_change_hook(self):
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000363 if self.flist:
364 self.flist.filename_changed_edit(self)
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000365 self.saved_change_hook()
366 if self.ispythonsource(self.io.filename):
367 self.addcolorizer()
368 else:
369 self.rmcolorizer()
370
371 def addcolorizer(self):
372 if self.color:
373 return
374 ##print "Add colorizer"
375 self.per.removefilter(self.undo)
376 self.color = self.ColorDelegator()
377 self.per.insertfilter(self.color)
378 self.per.insertfilter(self.undo)
379
380 def rmcolorizer(self):
381 if not self.color:
382 return
383 ##print "Remove colorizer"
384 self.per.removefilter(self.undo)
385 self.per.removefilter(self.color)
386 self.color = None
387 self.per.insertfilter(self.undo)
388
389 def saved_change_hook(self):
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000390 short = self.short_title()
391 long = self.long_title()
392 if short and long:
393 title = short + " - " + long
394 elif short:
395 title = short
396 elif long:
397 title = long
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000398 else:
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000399 title = "Untitled"
400 icon = short or long or title
401 if not self.get_saved():
402 title = "*%s*" % title
403 icon = "*%s" % icon
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000404 self.top.wm_title(title)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000405 self.top.wm_iconname(icon)
406
407 def get_saved(self):
408 return self.undo.get_saved()
409
410 def set_saved(self, flag):
411 self.undo.set_saved(flag)
412
413 def reset_undo(self):
414 self.undo.reset_undo()
415
416 def short_title(self):
417 filename = self.io.filename
418 if filename:
419 filename = os.path.basename(filename)
420 return filename
421
422 def long_title(self):
423 return self.io.filename or ""
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000424
425 def center_insert_event(self, event):
426 self.center()
427
428 def center(self, mark="insert"):
Guido van Rossum245ddc41999-01-11 14:51:32 +0000429 text = self.text
430 top, bot = self.getwindowlines()
431 lineno = self.getlineno(mark)
432 height = bot - top
433 newtop = max(1, lineno - height/2)
434 text.yview(float(newtop))
435
436 def getwindowlines(self):
437 text = self.text
438 top = self.getlineno("@0,0")
439 bot = self.getlineno("@0,65535")
Guido van Rossum5051f4f1999-01-12 22:09:57 +0000440 if top == bot and text.winfo_height() == 1:
441 # Geometry manager hasn't run yet
Guido van Rossum245ddc41999-01-11 14:51:32 +0000442 height = int(text['height'])
443 bot = top + height - 1
444 return top, bot
445
446 def getlineno(self, mark="insert"):
447 text = self.text
448 return int(float(text.index(mark)))
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000449
450 def close_event(self, event):
451 self.close()
452
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000453 def maybesave(self):
454 if self.io:
455 return self.io.maybesave()
456
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000457 def close(self):
458 self.top.wm_deiconify()
459 self.top.tkraise()
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000460 reply = self.maybesave()
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000461 if reply != "cancel":
Guido van Rossumc4f752f1999-02-17 17:20:50 +0000462 WindowList.unregister_callback(self.postwindowsmenu)
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000463 if self.close_hook:
464 self.close_hook()
Guido van Rossum5051f4f1999-01-12 22:09:57 +0000465 colorizing = 0
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000466 if self.color:
Guido van Rossum5051f4f1999-01-12 22:09:57 +0000467 colorizing = self.color.colorizing
468 doh = colorizing and self.top
469 self.color.close(doh) # Cancel colorization
470 if not colorizing:
471 self.top.destroy()
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000472 return reply
473
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000474 def load_extensions(self):
475 self.extensions = {}
476 self.load_standard_extensions()
477
478 def load_standard_extensions(self):
479 for name in self.get_standard_extension_names():
480 try:
481 self.load_extension(name)
482 except:
483 print "Failed to load extension", `name`
484 import traceback
485 traceback.print_exc()
486
487 def get_standard_extension_names(self):
488 import extend
489 return extend.standard
490
491 def load_extension(self, name):
Guido van Rossum9dd52091999-04-23 14:01:25 +0000492 mod = __import__(name, globals(), locals(), [])
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000493 cls = getattr(mod, name)
494 ins = cls(self)
495 self.extensions[name] = ins
496 kdnames = ["keydefs"]
497 if sys.platform == 'win32':
498 kdnames.append("windows_keydefs")
499 elif sys.platform == 'mac':
500 kdnames.append("mac_keydefs")
501 else:
502 kdnames.append("unix_keydefs")
503 keydefs = {}
504 for kdname in kdnames:
505 if hasattr(ins, kdname):
506 keydefs.update(getattr(ins, kdname))
507 if keydefs:
Guido van Rossum07ec8961999-01-28 22:02:47 +0000508 self.apply_bindings(keydefs)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000509 for vevent in keydefs.keys():
510 methodname = string.replace(vevent, "-", "_")
511 while methodname[:1] == '<':
512 methodname = methodname[1:]
513 while methodname[-1:] == '>':
514 methodname = methodname[:-1]
515 methodname = methodname + "_event"
516 if hasattr(ins, methodname):
517 self.text.bind(vevent, getattr(ins, methodname))
518 if hasattr(ins, "menudefs"):
Guido van Rossum07ec8961999-01-28 22:02:47 +0000519 self.fill_menus(ins.menudefs, keydefs)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000520 return ins
521
Guido van Rossum07ec8961999-01-28 22:02:47 +0000522 def apply_bindings(self, keydefs=None):
523 if keydefs is None:
524 keydefs = self.Bindings.default_keydefs
525 text = self.text
526 text.keydefs = keydefs
527 for event, keylist in keydefs.items():
528 if keylist:
529 apply(text.event_add, (event,) + tuple(keylist))
530
531 def fill_menus(self, defs=None, keydefs=None):
Guido van Rossum85a36a51999-06-10 17:43:17 +0000532 # Fill the menus. Menus that are absent or None in
533 # self.menudict are ignored.
Guido van Rossum07ec8961999-01-28 22:02:47 +0000534 if defs is None:
535 defs = self.Bindings.menudefs
536 if keydefs is None:
537 keydefs = self.Bindings.default_keydefs
538 menudict = self.menudict
539 text = self.text
540 for mname, itemlist in defs:
541 menu = menudict.get(mname)
542 if not menu:
543 continue
544 for item in itemlist:
545 if not item:
546 menu.add_separator()
547 else:
548 label, event = item
549 checkbutton = (label[:1] == '!')
550 if checkbutton:
551 label = label[1:]
552 underline, label = prepstr(label)
553 accelerator = get_accelerator(keydefs, event)
554 def command(text=text, event=event):
555 text.event_generate(event)
556 if checkbutton:
557 var = self.getrawvar(event, BooleanVar)
558 menu.add_checkbutton(label=label, underline=underline,
559 command=command, accelerator=accelerator,
560 variable=var)
561 else:
562 menu.add_command(label=label, underline=underline,
563 command=command, accelerator=accelerator)
Guido van Rossumdef2c961999-05-21 04:38:27 +0000564
Guido van Rossum07ec8961999-01-28 22:02:47 +0000565 def getvar(self, name):
566 var = self.getrawvar(name)
567 if var:
568 return var.get()
Guido van Rossumdef2c961999-05-21 04:38:27 +0000569
Guido van Rossum07ec8961999-01-28 22:02:47 +0000570 def setvar(self, name, value, vartype=None):
571 var = self.getrawvar(name, vartype)
572 if var:
573 var.set(value)
Guido van Rossumdef2c961999-05-21 04:38:27 +0000574
Guido van Rossum07ec8961999-01-28 22:02:47 +0000575 def getrawvar(self, name, vartype=None):
Guido van Rossumb7ebb831999-01-28 22:24:30 +0000576 var = self.vars.get(name)
577 if not var and vartype:
578 self.vars[name] = var = vartype(self.text)
579 return var
Guido van Rossum07ec8961999-01-28 22:02:47 +0000580
Guido van Rossumf4a15081999-06-03 14:32:16 +0000581 # Tk implementations of "virtual text methods" -- each platform
582 # reusing IDLE's support code needs to define these for its GUI's
583 # flavor of widget.
584
585 # Is character at text_index in a Python string? Return 0 for
Guido van Rossum85a36a51999-06-10 17:43:17 +0000586 # "guaranteed no", true for anything else. This info is expensive
587 # to compute ab initio, but is probably already known by the
588 # platform's colorizer.
Guido van Rossumf4a15081999-06-03 14:32:16 +0000589
590 def is_char_in_string(self, text_index):
591 if self.color:
Guido van Rossum85a36a51999-06-10 17:43:17 +0000592 # Return true iff colorizer hasn't (re)gotten this far
593 # yet, or the character is tagged as being in a string
Guido van Rossumf4a15081999-06-03 14:32:16 +0000594 return self.text.tag_prevrange("TODO", text_index) or \
595 "STRING" in self.text.tag_names(text_index)
596 else:
Guido van Rossum85a36a51999-06-10 17:43:17 +0000597 # The colorizer is missing: assume the worst
Guido van Rossumf4a15081999-06-03 14:32:16 +0000598 return 1
599
Guido van Rossum85a36a51999-06-10 17:43:17 +0000600 # If a selection is defined in the text widget, return (start,
601 # end) as Tkinter text indices, otherwise return (None, None)
602 def get_selection_index(self):
603 try:
604 first = self.text.index("sel.first")
605 last = self.text.index("sel.last")
606 return first, last
607 except TclError:
608 return None, None
609
Guido van Rossum07ec8961999-01-28 22:02:47 +0000610def prepstr(s):
Guido van Rossum85a36a51999-06-10 17:43:17 +0000611 # Helper to extract the underscore from a string, e.g.
612 # prepstr("Co_py") returns (2, "Copy").
Guido van Rossum07ec8961999-01-28 22:02:47 +0000613 i = string.find(s, '_')
614 if i >= 0:
615 s = s[:i] + s[i+1:]
616 return i, s
617
618
619keynames = {
620 'bracketleft': '[',
621 'bracketright': ']',
622 'slash': '/',
623}
624
625def get_accelerator(keydefs, event):
626 keylist = keydefs.get(event)
627 if not keylist:
628 return ""
629 s = keylist[0]
630 s = re.sub(r"-[a-z]\b", lambda m: string.upper(m.group()), s)
631 s = re.sub(r"\b\w+\b", lambda m: keynames.get(m.group(), m.group()), s)
632 s = re.sub("Key-", "", s)
633 s = re.sub("Control-", "Ctrl-", s)
634 s = re.sub("-", "+", s)
635 s = re.sub("><", " ", s)
636 s = re.sub("<", "", s)
637 s = re.sub(">", "", s)
638 return s
639
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000640
641def fixwordbreaks(root):
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000642 # Make sure that Tk's double-click and next/previous word
643 # operations use our definition of a word (i.e. an identifier)
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000644 tk = root.tk
645 tk.call('tcl_wordBreakAfter', 'a b', 0) # make sure word.tcl is loaded
646 tk.call('set', 'tcl_wordchars', '[a-zA-Z0-9_]')
647 tk.call('set', 'tcl_nonwordchars', '[^a-zA-Z0-9_]')
648
649
650def test():
651 root = Tk()
652 fixwordbreaks(root)
653 root.withdraw()
654 if sys.argv[1:]:
655 filename = sys.argv[1]
656 else:
657 filename = None
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000658 edit = EditorWindow(root=root, filename=filename)
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000659 edit.set_close_hook(root.quit)
660 root.mainloop()
661 root.destroy()
662
663if __name__ == '__main__':
664 test()