blob: ee3b9c34d4a5b7f3d11c6ba9b7e0b237c08f84c9 [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
Jeremy Hyltone81f28b2000-03-03 23:06:45 +000011from IdleConf import IdleConf
Guido van Rossum99aabe32000-02-17 16:14:16 +000012
Guido van Rossum13205601999-06-11 15:03:00 +000013# The default tab setting for a Text widget, in average-width characters.
14TK_TABWIDTH_DEFAULT = 8
15
Guido van Rossum504b0bf1999-01-02 21:28:54 +000016# File menu
17
18#$ event <<open-module>>
19#$ win <Alt-m>
20#$ unix <Control-x><Control-m>
21
22#$ event <<open-class-browser>>
23#$ win <Alt-c>
24#$ unix <Control-x><Control-b>
25
Guido van Rossumd6e87131999-03-10 05:18:02 +000026#$ event <<open-path-browser>>
27
Guido van Rossum504b0bf1999-01-02 21:28:54 +000028#$ event <<close-window>>
29#$ unix <Control-x><Control-0>
30#$ unix <Control-x><Key-0>
31#$ win <Alt-F4>
32
33# Edit menu
34
35#$ event <<Copy>>
36#$ win <Control-c>
37#$ unix <Alt-w>
38
39#$ event <<Cut>>
40#$ win <Control-x>
41#$ unix <Control-w>
42
43#$ event <<Paste>>
44#$ win <Control-v>
45#$ unix <Control-y>
46
47#$ event <<select-all>>
48#$ win <Alt-a>
49#$ unix <Alt-a>
50
51# Help menu
52
53#$ event <<help>>
54#$ win <F1>
55#$ unix <F1>
56
57#$ event <<about-idle>>
58
59# Events without menu entries
60
61#$ event <<remove-selection>>
62#$ win <Escape>
63
64#$ event <<center-insert>>
65#$ win <Control-l>
66#$ unix <Control-l>
67
68#$ event <<do-nothing>>
69#$ unix <Control-x>
70
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +000071
Guido van Rossum2aeeb551998-10-12 21:01:37 +000072about_title = "About IDLE"
73about_text = """\
Guido van Rossum504b0bf1999-01-02 21:28:54 +000074IDLE %s
Guido van Rossum2aeeb551998-10-12 21:01:37 +000075
Guido van Rossum504b0bf1999-01-02 21:28:54 +000076An Integrated DeveLopment Environment for Python
Guido van Rossum2aeeb551998-10-12 21:01:37 +000077
78by Guido van Rossum
Guido van Rossum504b0bf1999-01-02 21:28:54 +000079""" % idlever.IDLE_VERSION
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +000080
81class EditorWindow:
82
83 from Percolator import Percolator
84 from ColorDelegator import ColorDelegator
85 from UndoDelegator import UndoDelegator
86 from IOBinding import IOBinding
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +000087 import Bindings
Guido van Rossum504b0bf1999-01-02 21:28:54 +000088 from Tkinter import Toplevel
Guido van Rossumec73dc62000-02-15 18:05:15 +000089 from MultiStatusBar import MultiStatusBar
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +000090
Guido van Rossum504b0bf1999-01-02 21:28:54 +000091 about_title = about_title
92 about_text = about_text
93
Guido van Rossumb7ebb831999-01-28 22:24:30 +000094 vars = {}
95
Guido van Rossum504b0bf1999-01-02 21:28:54 +000096 def __init__(self, flist=None, filename=None, key=None, root=None):
Jeremy Hyltone81f28b2000-03-03 23:06:45 +000097 edconf = IdleConf.getsection('EditorWindow')
98 coconf = IdleConf.getsection('Colors')
Guido van Rossum504b0bf1999-01-02 21:28:54 +000099 self.flist = flist
100 root = root or flist.root
Guido van Rossum2aeeb551998-10-12 21:01:37 +0000101 self.root = root
Guido van Rossumb7ebb831999-01-28 22:24:30 +0000102 if flist:
103 self.vars = flist.vars
Guido van Rossum2aeeb551998-10-12 21:01:37 +0000104 self.menubar = Menu(root)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000105 self.top = top = self.Toplevel(root, menu=self.menubar)
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000106 self.vbar = vbar = Scrollbar(top, name='vbar')
Guido van Rossumec73dc62000-02-15 18:05:15 +0000107 self.text_frame = text_frame = Frame(top)
108 self.text = text = Text(text_frame, name='text', padx=5,
Jeremy Hyltone81f28b2000-03-03 23:06:45 +0000109 foreground=coconf.getdef('normal-foreground'),
110 background=coconf.getdef('normal-background'),
111 highlightcolor=coconf.getdef('hilite-foreground'),
112 highlightbackground=coconf.getdef('hilite-background'),
113 insertbackground=coconf.getdef('cursor-background'),
114 width=edconf.getint('width'),
115 height=edconf.getint('height'),
116 wrap="none")
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000117
Guido van Rossum2aeeb551998-10-12 21:01:37 +0000118 self.createmenubar()
Guido van Rossum07ec8961999-01-28 22:02:47 +0000119 self.apply_bindings()
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000120
121 self.top.protocol("WM_DELETE_WINDOW", self.close)
122 self.top.bind("<<close-window>>", self.close_event)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000123 text.bind("<<center-insert>>", self.center_insert_event)
124 text.bind("<<help>>", self.help_dialog)
Guido van Rossum416b9611999-08-26 23:06:05 +0000125 text.bind("<<python-docs>>", self.python_docs)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000126 text.bind("<<about-idle>>", self.about_dialog)
127 text.bind("<<open-module>>", self.open_module)
128 text.bind("<<do-nothing>>", lambda event: "break")
129 text.bind("<<select-all>>", self.select_all)
130 text.bind("<<remove-selection>>", self.remove_selection)
131 text.bind("<3>", self.right_menu_event)
132 if flist:
133 flist.inversedict[self] = key
134 if key:
135 flist.dict[key] = self
136 text.bind("<<open-new-window>>", self.flist.new_callback)
137 text.bind("<<close-all-windows>>", self.flist.close_all_callback)
138 text.bind("<<open-class-browser>>", self.open_class_browser)
Guido van Rossumd6e87131999-03-10 05:18:02 +0000139 text.bind("<<open-path-browser>>", self.open_path_browser)
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000140
141 vbar['command'] = text.yview
142 vbar.pack(side=RIGHT, fill=Y)
143
144 text['yscrollcommand'] = vbar.set
Jeremy Hyltone81f28b2000-03-03 23:06:45 +0000145 text['font'] = edconf.get('font-name'), edconf.get('font-size')
Guido van Rossumec73dc62000-02-15 18:05:15 +0000146 text_frame.pack(side=LEFT, fill=BOTH, expand=1)
147 text.pack(side=TOP, fill=BOTH, expand=1)
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000148 text.focus_set()
149
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000150 self.per = per = self.Percolator(text)
151 if self.ispythonsource(filename):
152 self.color = color = self.ColorDelegator(); per.insertfilter(color)
153 ##print "Initial colorizer"
154 else:
155 ##print "No initial colorizer"
156 self.color = None
157 self.undo = undo = self.UndoDelegator(); per.insertfilter(undo)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000158 self.io = io = self.IOBinding(self)
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000159
Guido van Rossum318a70d1999-05-03 15:49:52 +0000160 text.undo_block_start = undo.undo_block_start
161 text.undo_block_stop = undo.undo_block_stop
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000162 undo.set_saved_change_hook(self.saved_change_hook)
163 io.set_filename_change_hook(self.filename_change_hook)
164
165 if filename:
166 if os.path.exists(filename):
167 io.loadfile(filename)
168 else:
169 io.set_filename(filename)
170
171 self.saved_change_hook()
172
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000173 self.load_extensions()
174
175 menu = self.menudict.get('windows')
176 if menu:
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000177 end = menu.index("end")
178 if end is None:
179 end = -1
180 if end >= 0:
181 menu.add_separator()
182 end = end + 1
183 self.wmenu_end = end
Guido van Rossumc4f752f1999-02-17 17:20:50 +0000184 WindowList.register_callback(self.postwindowsmenu)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000185
Guido van Rossumd395aee1999-06-02 11:04:29 +0000186 # Some abstractions so IDLE extensions are cross-IDE
187 self.askyesno = tkMessageBox.askyesno
188 self.askinteger = tkSimpleDialog.askinteger
189 self.showerror = tkMessageBox.showerror
190
Guido van Rossumdef2c961999-05-21 04:38:27 +0000191 if self.extensions.has_key('AutoIndent'):
192 self.extensions['AutoIndent'].set_indentation_params(
193 self.ispythonsource(filename))
Guido van Rossumec73dc62000-02-15 18:05:15 +0000194 self.set_status_bar()
195
196 def set_status_bar(self):
197 self.status_bar = self.MultiStatusBar(self.text_frame)
198 self.status_bar.set_label('column', 'Col: ?', side=RIGHT)
199 self.status_bar.set_label('line', 'Ln: ?', side=RIGHT)
200 self.status_bar.pack(side=BOTTOM, fill=X)
201 self.text.bind('<KeyRelease>', self.set_line_and_column)
202 self.text.bind('<ButtonRelease>', self.set_line_and_column)
203 self.text.after_idle(self.set_line_and_column)
204
205 def set_line_and_column(self, event=None):
206 line, column = string.split(self.text.index(INSERT), '.')
207 self.status_bar.set_label('column', 'Col: %s' % column)
208 self.status_bar.set_label('line', 'Ln: %s' % line)
Guido van Rossumdef2c961999-05-21 04:38:27 +0000209
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000210 def wakeup(self):
Guido van Rossum36911a11999-01-18 15:18:57 +0000211 if self.top.wm_state() == "iconic":
212 self.top.wm_deiconify()
213 else:
214 self.top.tkraise()
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000215 self.text.focus_set()
216
Guido van Rossume7b2e651998-10-12 23:56:08 +0000217 menu_specs = [
Guido van Rossumb5eed031998-11-27 03:19:07 +0000218 ("file", "_File"),
219 ("edit", "_Edit"),
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000220 ("windows", "_Windows"),
Guido van Rossumb5eed031998-11-27 03:19:07 +0000221 ("help", "_Help"),
Guido van Rossume7b2e651998-10-12 23:56:08 +0000222 ]
223
Guido van Rossum2aeeb551998-10-12 21:01:37 +0000224 def createmenubar(self):
225 mbar = self.menubar
Guido van Rossum07ec8961999-01-28 22:02:47 +0000226 self.menudict = menudict = {}
Guido van Rossume7b2e651998-10-12 23:56:08 +0000227 for name, label in self.menu_specs:
Guido van Rossum07ec8961999-01-28 22:02:47 +0000228 underline, label = prepstr(label)
229 menudict[name] = menu = Menu(mbar, name=name)
Guido van Rossumb5eed031998-11-27 03:19:07 +0000230 mbar.add_cascade(label=label, menu=menu, underline=underline)
Guido van Rossum07ec8961999-01-28 22:02:47 +0000231 self.fill_menus()
Guido van Rossum2aeeb551998-10-12 21:01:37 +0000232
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000233 def postwindowsmenu(self):
234 # Only called when Windows menu exists
Guido van Rossum85a36a51999-06-10 17:43:17 +0000235 # XXX Actually, this Just-In-Time updating interferes badly
236 # XXX with the tear-off feature. It would be better to update
237 # XXX all Windows menus whenever the list of windows changes.
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000238 menu = self.menudict['windows']
239 end = menu.index("end")
240 if end is None:
241 end = -1
242 if end > self.wmenu_end:
243 menu.delete(self.wmenu_end+1, end)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000244 WindowList.add_windows_to_menu(menu)
245
246 rmenu = None
247
248 def right_menu_event(self, event):
249 self.text.tag_remove("sel", "1.0", "end")
250 self.text.mark_set("insert", "@%d,%d" % (event.x, event.y))
251 if not self.rmenu:
252 self.make_rmenu()
253 rmenu = self.rmenu
254 self.event = event
255 iswin = sys.platform[:3] == 'win'
256 if iswin:
257 self.text.config(cursor="arrow")
258 rmenu.tk_popup(event.x_root, event.y_root)
259 if iswin:
260 self.text.config(cursor="ibeam")
261
262 rmenu_specs = [
263 # ("Label", "<<virtual-event>>"), ...
264 ("Close", "<<close-window>>"), # Example
265 ]
266
267 def make_rmenu(self):
268 rmenu = Menu(self.text, tearoff=0)
269 for label, eventname in self.rmenu_specs:
270 def command(text=self.text, eventname=eventname):
271 text.event_generate(eventname)
272 rmenu.add_command(label=label, command=command)
273 self.rmenu = rmenu
274
Guido van Rossume7b2e651998-10-12 23:56:08 +0000275 def about_dialog(self, event=None):
Guido van Rossum2aeeb551998-10-12 21:01:37 +0000276 tkMessageBox.showinfo(self.about_title, self.about_text,
277 master=self.text)
278
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000279 helpfile = "help.txt"
280
Guido van Rossume7b2e651998-10-12 23:56:08 +0000281 def help_dialog(self, event=None):
Guido van Rossum416b9611999-08-26 23:06:05 +0000282 try:
283 helpfile = os.path.join(os.path.dirname(__file__), self.helpfile)
284 except NameError:
285 helpfile = self.helpfile
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000286 if self.flist:
287 self.flist.open(helpfile)
288 else:
289 self.io.loadfile(helpfile)
290
Guido van Rossum416b9611999-08-26 23:06:05 +0000291 # XXX Fix these for Windows
292 help_viewer = "netscape -remote 'openurl(%(url)s)' 2>/dev/null || " \
293 "netscape %(url)s &"
294 help_url = "http://www.python.org/doc/current/"
295
296 def python_docs(self, event=None):
297 cmd = self.help_viewer % {"url": self.help_url}
298 os.system(cmd)
299
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000300 def select_all(self, event=None):
301 self.text.tag_add("sel", "1.0", "end-1c")
302 self.text.mark_set("insert", "1.0")
303 self.text.see("insert")
304 return "break"
305
306 def remove_selection(self, event=None):
307 self.text.tag_remove("sel", "1.0", "end")
308 self.text.see("insert")
309
Guido van Rossumb3418881998-10-13 03:45:15 +0000310 def open_module(self, event=None):
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000311 # XXX Shouldn't this be in IOBinding or in FileList?
Guido van Rossumb3418881998-10-13 03:45:15 +0000312 try:
313 name = self.text.get("sel.first", "sel.last")
314 except TclError:
315 name = ""
316 else:
317 name = string.strip(name)
318 if not name:
319 name = tkSimpleDialog.askstring("Module",
Guido van Rossume1dedc01998-10-16 16:09:57 +0000320 "Enter the name of a Python module\n"
321 "to search on sys.path and open:",
Guido van Rossumb3418881998-10-13 03:45:15 +0000322 parent=self.text)
323 if name:
324 name = string.strip(name)
325 if not name:
326 return
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000327 # XXX Ought to support package syntax
328 # XXX Ought to insert current file's directory in front of path
Guido van Rossumb3418881998-10-13 03:45:15 +0000329 try:
330 (f, file, (suffix, mode, type)) = imp.find_module(name)
Guido van Rossum74311b21999-06-01 18:27:14 +0000331 except (NameError, ImportError), msg:
Guido van Rossumb3418881998-10-13 03:45:15 +0000332 tkMessageBox.showerror("Import error", str(msg), parent=self.text)
333 return
334 if type != imp.PY_SOURCE:
335 tkMessageBox.showerror("Unsupported type",
336 "%s is not a source module" % name, parent=self.text)
337 return
338 if f:
339 f.close()
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000340 if self.flist:
341 self.flist.open(file)
342 else:
343 self.io.loadfile(file)
344
345 def open_class_browser(self, event=None):
346 filename = self.io.filename
347 if not filename:
348 tkMessageBox.showerror(
349 "No filename",
350 "This buffer has no associated filename",
351 master=self.text)
Guido van Rossum74311b21999-06-01 18:27:14 +0000352 self.text.focus_set()
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000353 return None
354 head, tail = os.path.split(filename)
355 base, ext = os.path.splitext(tail)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000356 import ClassBrowser
357 ClassBrowser.ClassBrowser(self.flist, base, [head])
Guido van Rossumdef2c961999-05-21 04:38:27 +0000358
Guido van Rossumd6e87131999-03-10 05:18:02 +0000359 def open_path_browser(self, event=None):
360 import PathBrowser
361 PathBrowser.PathBrowser(self.flist)
Guido van Rossum2aeeb551998-10-12 21:01:37 +0000362
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000363 def gotoline(self, lineno):
364 if lineno is not None and lineno > 0:
365 self.text.mark_set("insert", "%d.0" % lineno)
366 self.text.tag_remove("sel", "1.0", "end")
367 self.text.tag_add("sel", "insert", "insert +1l")
368 self.center()
369
370 def ispythonsource(self, filename):
371 if not filename:
372 return 1
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000373 base, ext = os.path.splitext(os.path.basename(filename))
374 if os.path.normcase(ext) in (".py", ".pyw"):
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000375 return 1
376 try:
377 f = open(filename)
378 line = f.readline()
379 f.close()
380 except IOError:
381 return 0
382 return line[:2] == '#!' and string.find(line, 'python') >= 0
383
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000384 def close_hook(self):
385 if self.flist:
386 self.flist.close_edit(self)
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000387
388 def set_close_hook(self, close_hook):
389 self.close_hook = close_hook
390
391 def filename_change_hook(self):
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000392 if self.flist:
393 self.flist.filename_changed_edit(self)
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000394 self.saved_change_hook()
395 if self.ispythonsource(self.io.filename):
396 self.addcolorizer()
397 else:
398 self.rmcolorizer()
399
400 def addcolorizer(self):
401 if self.color:
402 return
403 ##print "Add colorizer"
404 self.per.removefilter(self.undo)
405 self.color = self.ColorDelegator()
406 self.per.insertfilter(self.color)
407 self.per.insertfilter(self.undo)
408
409 def rmcolorizer(self):
410 if not self.color:
411 return
412 ##print "Remove colorizer"
413 self.per.removefilter(self.undo)
414 self.per.removefilter(self.color)
415 self.color = None
416 self.per.insertfilter(self.undo)
417
418 def saved_change_hook(self):
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000419 short = self.short_title()
420 long = self.long_title()
421 if short and long:
422 title = short + " - " + long
423 elif short:
424 title = short
425 elif long:
426 title = long
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000427 else:
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000428 title = "Untitled"
429 icon = short or long or title
430 if not self.get_saved():
431 title = "*%s*" % title
432 icon = "*%s" % icon
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000433 self.top.wm_title(title)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000434 self.top.wm_iconname(icon)
435
436 def get_saved(self):
437 return self.undo.get_saved()
438
439 def set_saved(self, flag):
440 self.undo.set_saved(flag)
441
442 def reset_undo(self):
443 self.undo.reset_undo()
444
445 def short_title(self):
446 filename = self.io.filename
447 if filename:
448 filename = os.path.basename(filename)
449 return filename
450
451 def long_title(self):
452 return self.io.filename or ""
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000453
454 def center_insert_event(self, event):
455 self.center()
456
457 def center(self, mark="insert"):
Guido van Rossum245ddc41999-01-11 14:51:32 +0000458 text = self.text
459 top, bot = self.getwindowlines()
460 lineno = self.getlineno(mark)
461 height = bot - top
462 newtop = max(1, lineno - height/2)
463 text.yview(float(newtop))
464
465 def getwindowlines(self):
466 text = self.text
467 top = self.getlineno("@0,0")
468 bot = self.getlineno("@0,65535")
Guido van Rossum5051f4f1999-01-12 22:09:57 +0000469 if top == bot and text.winfo_height() == 1:
470 # Geometry manager hasn't run yet
Guido van Rossum245ddc41999-01-11 14:51:32 +0000471 height = int(text['height'])
472 bot = top + height - 1
473 return top, bot
474
475 def getlineno(self, mark="insert"):
476 text = self.text
477 return int(float(text.index(mark)))
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000478
479 def close_event(self, event):
480 self.close()
481
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000482 def maybesave(self):
483 if self.io:
484 return self.io.maybesave()
485
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000486 def close(self):
487 self.top.wm_deiconify()
488 self.top.tkraise()
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000489 reply = self.maybesave()
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000490 if reply != "cancel":
Guido van Rossum205afb41999-06-25 16:06:29 +0000491 self._close()
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000492 return reply
493
Guido van Rossum205afb41999-06-25 16:06:29 +0000494 def _close(self):
495 WindowList.unregister_callback(self.postwindowsmenu)
496 if self.close_hook:
497 self.close_hook()
498 self.flist = None
499 colorizing = 0
500 self.unload_extensions()
501 self.io.close(); self.io = None
502 self.undo = None # XXX
503 if self.color:
504 colorizing = self.color.colorizing
505 doh = colorizing and self.top
506 self.color.close(doh) # Cancel colorization
507 self.text = None
508 self.vars = None
509 self.per.close(); self.per = None
510 if not colorizing:
511 self.top.destroy()
512
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000513 def load_extensions(self):
514 self.extensions = {}
515 self.load_standard_extensions()
516
Guido van Rossum205afb41999-06-25 16:06:29 +0000517 def unload_extensions(self):
518 for ins in self.extensions.values():
519 if hasattr(ins, "close"):
520 ins.close()
521 self.extensions = {}
522
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000523 def load_standard_extensions(self):
524 for name in self.get_standard_extension_names():
525 try:
526 self.load_extension(name)
527 except:
528 print "Failed to load extension", `name`
529 import traceback
530 traceback.print_exc()
531
532 def get_standard_extension_names(self):
Jeremy Hyltone81f28b2000-03-03 23:06:45 +0000533 return IdleConf.getextensions()
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000534
535 def load_extension(self, name):
Guido van Rossum9dd52091999-04-23 14:01:25 +0000536 mod = __import__(name, globals(), locals(), [])
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000537 cls = getattr(mod, name)
538 ins = cls(self)
539 self.extensions[name] = ins
540 kdnames = ["keydefs"]
541 if sys.platform == 'win32':
542 kdnames.append("windows_keydefs")
543 elif sys.platform == 'mac':
544 kdnames.append("mac_keydefs")
545 else:
546 kdnames.append("unix_keydefs")
547 keydefs = {}
548 for kdname in kdnames:
549 if hasattr(ins, kdname):
550 keydefs.update(getattr(ins, kdname))
551 if keydefs:
Guido van Rossum07ec8961999-01-28 22:02:47 +0000552 self.apply_bindings(keydefs)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000553 for vevent in keydefs.keys():
554 methodname = string.replace(vevent, "-", "_")
555 while methodname[:1] == '<':
556 methodname = methodname[1:]
557 while methodname[-1:] == '>':
558 methodname = methodname[:-1]
559 methodname = methodname + "_event"
560 if hasattr(ins, methodname):
561 self.text.bind(vevent, getattr(ins, methodname))
562 if hasattr(ins, "menudefs"):
Guido van Rossum07ec8961999-01-28 22:02:47 +0000563 self.fill_menus(ins.menudefs, keydefs)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000564 return ins
565
Guido van Rossum07ec8961999-01-28 22:02:47 +0000566 def apply_bindings(self, keydefs=None):
567 if keydefs is None:
568 keydefs = self.Bindings.default_keydefs
569 text = self.text
570 text.keydefs = keydefs
571 for event, keylist in keydefs.items():
572 if keylist:
573 apply(text.event_add, (event,) + tuple(keylist))
574
575 def fill_menus(self, defs=None, keydefs=None):
Guido van Rossum85a36a51999-06-10 17:43:17 +0000576 # Fill the menus. Menus that are absent or None in
577 # self.menudict are ignored.
Guido van Rossum07ec8961999-01-28 22:02:47 +0000578 if defs is None:
579 defs = self.Bindings.menudefs
580 if keydefs is None:
581 keydefs = self.Bindings.default_keydefs
582 menudict = self.menudict
583 text = self.text
584 for mname, itemlist in defs:
585 menu = menudict.get(mname)
586 if not menu:
587 continue
588 for item in itemlist:
589 if not item:
590 menu.add_separator()
591 else:
592 label, event = item
593 checkbutton = (label[:1] == '!')
594 if checkbutton:
595 label = label[1:]
596 underline, label = prepstr(label)
597 accelerator = get_accelerator(keydefs, event)
598 def command(text=text, event=event):
599 text.event_generate(event)
600 if checkbutton:
601 var = self.getrawvar(event, BooleanVar)
602 menu.add_checkbutton(label=label, underline=underline,
603 command=command, accelerator=accelerator,
604 variable=var)
605 else:
606 menu.add_command(label=label, underline=underline,
607 command=command, accelerator=accelerator)
Guido van Rossumdef2c961999-05-21 04:38:27 +0000608
Guido van Rossum07ec8961999-01-28 22:02:47 +0000609 def getvar(self, name):
610 var = self.getrawvar(name)
611 if var:
612 return var.get()
Guido van Rossumdef2c961999-05-21 04:38:27 +0000613
Guido van Rossum07ec8961999-01-28 22:02:47 +0000614 def setvar(self, name, value, vartype=None):
615 var = self.getrawvar(name, vartype)
616 if var:
617 var.set(value)
Guido van Rossumdef2c961999-05-21 04:38:27 +0000618
Guido van Rossum07ec8961999-01-28 22:02:47 +0000619 def getrawvar(self, name, vartype=None):
Guido van Rossumb7ebb831999-01-28 22:24:30 +0000620 var = self.vars.get(name)
621 if not var and vartype:
622 self.vars[name] = var = vartype(self.text)
623 return var
Guido van Rossum07ec8961999-01-28 22:02:47 +0000624
Guido van Rossumf4a15081999-06-03 14:32:16 +0000625 # Tk implementations of "virtual text methods" -- each platform
626 # reusing IDLE's support code needs to define these for its GUI's
627 # flavor of widget.
628
629 # Is character at text_index in a Python string? Return 0 for
Guido van Rossum85a36a51999-06-10 17:43:17 +0000630 # "guaranteed no", true for anything else. This info is expensive
631 # to compute ab initio, but is probably already known by the
632 # platform's colorizer.
Guido van Rossumf4a15081999-06-03 14:32:16 +0000633
634 def is_char_in_string(self, text_index):
635 if self.color:
Guido van Rossum85a36a51999-06-10 17:43:17 +0000636 # Return true iff colorizer hasn't (re)gotten this far
637 # yet, or the character is tagged as being in a string
Guido van Rossumf4a15081999-06-03 14:32:16 +0000638 return self.text.tag_prevrange("TODO", text_index) or \
639 "STRING" in self.text.tag_names(text_index)
640 else:
Guido van Rossum85a36a51999-06-10 17:43:17 +0000641 # The colorizer is missing: assume the worst
Guido van Rossumf4a15081999-06-03 14:32:16 +0000642 return 1
643
Guido van Rossum85a36a51999-06-10 17:43:17 +0000644 # If a selection is defined in the text widget, return (start,
645 # end) as Tkinter text indices, otherwise return (None, None)
Guido van Rossum13205601999-06-11 15:03:00 +0000646 def get_selection_indices(self):
Guido van Rossum85a36a51999-06-10 17:43:17 +0000647 try:
648 first = self.text.index("sel.first")
649 last = self.text.index("sel.last")
650 return first, last
651 except TclError:
652 return None, None
653
Guido van Rossum13205601999-06-11 15:03:00 +0000654 # Return the text widget's current view of what a tab stop means
655 # (equivalent width in spaces).
656
657 def get_tabwidth(self):
658 current = self.text['tabs'] or TK_TABWIDTH_DEFAULT
659 return int(current)
660
661 # Set the text widget's current view of what a tab stop means.
662
663 def set_tabwidth(self, newtabwidth):
664 text = self.text
665 if self.get_tabwidth() != newtabwidth:
666 pixels = text.tk.call("font", "measure", text["font"],
667 "-displayof", text.master,
668 "n" * newtabwith)
669 text.configure(tabs=pixels)
670
Guido van Rossum07ec8961999-01-28 22:02:47 +0000671def prepstr(s):
Guido van Rossum85a36a51999-06-10 17:43:17 +0000672 # Helper to extract the underscore from a string, e.g.
673 # prepstr("Co_py") returns (2, "Copy").
Guido van Rossum07ec8961999-01-28 22:02:47 +0000674 i = string.find(s, '_')
675 if i >= 0:
676 s = s[:i] + s[i+1:]
677 return i, s
678
679
680keynames = {
681 'bracketleft': '[',
682 'bracketright': ']',
683 'slash': '/',
684}
685
686def get_accelerator(keydefs, event):
687 keylist = keydefs.get(event)
688 if not keylist:
689 return ""
690 s = keylist[0]
691 s = re.sub(r"-[a-z]\b", lambda m: string.upper(m.group()), s)
692 s = re.sub(r"\b\w+\b", lambda m: keynames.get(m.group(), m.group()), s)
693 s = re.sub("Key-", "", s)
694 s = re.sub("Control-", "Ctrl-", s)
695 s = re.sub("-", "+", s)
696 s = re.sub("><", " ", s)
697 s = re.sub("<", "", s)
698 s = re.sub(">", "", s)
699 return s
700
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000701
702def fixwordbreaks(root):
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000703 # Make sure that Tk's double-click and next/previous word
704 # operations use our definition of a word (i.e. an identifier)
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000705 tk = root.tk
706 tk.call('tcl_wordBreakAfter', 'a b', 0) # make sure word.tcl is loaded
707 tk.call('set', 'tcl_wordchars', '[a-zA-Z0-9_]')
708 tk.call('set', 'tcl_nonwordchars', '[^a-zA-Z0-9_]')
709
710
711def test():
712 root = Tk()
713 fixwordbreaks(root)
714 root.withdraw()
715 if sys.argv[1:]:
716 filename = sys.argv[1]
717 else:
718 filename = None
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000719 edit = EditorWindow(root=root, filename=filename)
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000720 edit.set_close_hook(root.quit)
721 root.mainloop()
722 root.destroy()
723
724if __name__ == '__main__':
725 test()