blob: 53f2520545d4eba4839e94e8adeb90fe3c3977e1 [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
Fred Drake6cd948e2000-05-10 17:28:42 +00009import BrowserControl
Guido van Rossum504b0bf1999-01-02 21:28:54 +000010import idlever
Guido van Rossumc4f752f1999-02-17 17:20:50 +000011import WindowList
Jeremy Hyltonae1f3bd2000-03-07 17:56:27 +000012from IdleConf import idleconf
Guido van Rossum99aabe32000-02-17 16:14:16 +000013
Guido van Rossum13205601999-06-11 15:03:00 +000014# The default tab setting for a Text widget, in average-width characters.
15TK_TABWIDTH_DEFAULT = 8
16
Guido van Rossum504b0bf1999-01-02 21:28:54 +000017# File menu
18
19#$ event <<open-module>>
20#$ win <Alt-m>
21#$ unix <Control-x><Control-m>
22
23#$ event <<open-class-browser>>
24#$ win <Alt-c>
25#$ unix <Control-x><Control-b>
26
Guido van Rossumd6e87131999-03-10 05:18:02 +000027#$ event <<open-path-browser>>
28
Guido van Rossum504b0bf1999-01-02 21:28:54 +000029#$ event <<close-window>>
30#$ unix <Control-x><Control-0>
31#$ unix <Control-x><Key-0>
32#$ win <Alt-F4>
33
34# Edit menu
35
36#$ event <<Copy>>
37#$ win <Control-c>
38#$ unix <Alt-w>
39
40#$ event <<Cut>>
41#$ win <Control-x>
42#$ unix <Control-w>
43
44#$ event <<Paste>>
45#$ win <Control-v>
46#$ unix <Control-y>
47
48#$ event <<select-all>>
49#$ win <Alt-a>
50#$ unix <Alt-a>
51
52# Help menu
53
54#$ event <<help>>
55#$ win <F1>
56#$ unix <F1>
57
58#$ event <<about-idle>>
59
60# Events without menu entries
61
62#$ event <<remove-selection>>
63#$ win <Escape>
64
65#$ event <<center-insert>>
66#$ win <Control-l>
67#$ unix <Control-l>
68
69#$ event <<do-nothing>>
70#$ unix <Control-x>
71
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +000072
Guido van Rossum2aeeb551998-10-12 21:01:37 +000073about_title = "About IDLE"
74about_text = """\
Guido van Rossum504b0bf1999-01-02 21:28:54 +000075IDLE %s
Guido van Rossum2aeeb551998-10-12 21:01:37 +000076
Guido van Rossum504b0bf1999-01-02 21:28:54 +000077An Integrated DeveLopment Environment for Python
Guido van Rossum2aeeb551998-10-12 21:01:37 +000078
79by Guido van Rossum
Guido van Rossum504b0bf1999-01-02 21:28:54 +000080""" % idlever.IDLE_VERSION
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +000081
82class EditorWindow:
83
84 from Percolator import Percolator
85 from ColorDelegator import ColorDelegator
86 from UndoDelegator import UndoDelegator
87 from IOBinding import IOBinding
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +000088 import Bindings
Guido van Rossum504b0bf1999-01-02 21:28:54 +000089 from Tkinter import Toplevel
Guido van Rossumec73dc62000-02-15 18:05:15 +000090 from MultiStatusBar import MultiStatusBar
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +000091
Guido van Rossum504b0bf1999-01-02 21:28:54 +000092 about_title = about_title
93 about_text = about_text
94
Guido van Rossumb7ebb831999-01-28 22:24:30 +000095 vars = {}
96
Guido van Rossum504b0bf1999-01-02 21:28:54 +000097 def __init__(self, flist=None, filename=None, key=None, root=None):
Jeremy Hyltonae1f3bd2000-03-07 17:56:27 +000098 edconf = idleconf.getsection('EditorWindow')
99 coconf = idleconf.getsection('Colors')
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000100 self.flist = flist
101 root = root or flist.root
Guido van Rossum2aeeb551998-10-12 21:01:37 +0000102 self.root = root
Guido van Rossumb7ebb831999-01-28 22:24:30 +0000103 if flist:
104 self.vars = flist.vars
Guido van Rossum2aeeb551998-10-12 21:01:37 +0000105 self.menubar = Menu(root)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000106 self.top = top = self.Toplevel(root, menu=self.menubar)
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000107 self.vbar = vbar = Scrollbar(top, name='vbar')
Guido van Rossumec73dc62000-02-15 18:05:15 +0000108 self.text_frame = text_frame = Frame(top)
109 self.text = text = Text(text_frame, name='text', padx=5,
Jeremy Hyltone81f28b2000-03-03 23:06:45 +0000110 foreground=coconf.getdef('normal-foreground'),
111 background=coconf.getdef('normal-background'),
112 highlightcolor=coconf.getdef('hilite-foreground'),
113 highlightbackground=coconf.getdef('hilite-background'),
114 insertbackground=coconf.getdef('cursor-background'),
115 width=edconf.getint('width'),
116 height=edconf.getint('height'),
117 wrap="none")
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000118
Guido van Rossum2aeeb551998-10-12 21:01:37 +0000119 self.createmenubar()
Guido van Rossum07ec8961999-01-28 22:02:47 +0000120 self.apply_bindings()
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000121
122 self.top.protocol("WM_DELETE_WINDOW", self.close)
123 self.top.bind("<<close-window>>", self.close_event)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000124 text.bind("<<center-insert>>", self.center_insert_event)
125 text.bind("<<help>>", self.help_dialog)
Guido van Rossum416b9611999-08-26 23:06:05 +0000126 text.bind("<<python-docs>>", self.python_docs)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000127 text.bind("<<about-idle>>", self.about_dialog)
128 text.bind("<<open-module>>", self.open_module)
129 text.bind("<<do-nothing>>", lambda event: "break")
130 text.bind("<<select-all>>", self.select_all)
131 text.bind("<<remove-selection>>", self.remove_selection)
132 text.bind("<3>", self.right_menu_event)
133 if flist:
134 flist.inversedict[self] = key
135 if key:
136 flist.dict[key] = self
137 text.bind("<<open-new-window>>", self.flist.new_callback)
138 text.bind("<<close-all-windows>>", self.flist.close_all_callback)
139 text.bind("<<open-class-browser>>", self.open_class_browser)
Guido van Rossumd6e87131999-03-10 05:18:02 +0000140 text.bind("<<open-path-browser>>", self.open_path_browser)
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000141
142 vbar['command'] = text.yview
143 vbar.pack(side=RIGHT, fill=Y)
144
145 text['yscrollcommand'] = vbar.set
Jeremy Hyltone81f28b2000-03-03 23:06:45 +0000146 text['font'] = edconf.get('font-name'), edconf.get('font-size')
Guido van Rossumec73dc62000-02-15 18:05:15 +0000147 text_frame.pack(side=LEFT, fill=BOTH, expand=1)
148 text.pack(side=TOP, fill=BOTH, expand=1)
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000149 text.focus_set()
150
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000151 self.per = per = self.Percolator(text)
152 if self.ispythonsource(filename):
153 self.color = color = self.ColorDelegator(); per.insertfilter(color)
154 ##print "Initial colorizer"
155 else:
156 ##print "No initial colorizer"
157 self.color = None
158 self.undo = undo = self.UndoDelegator(); per.insertfilter(undo)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000159 self.io = io = self.IOBinding(self)
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000160
Guido van Rossum318a70d1999-05-03 15:49:52 +0000161 text.undo_block_start = undo.undo_block_start
162 text.undo_block_stop = undo.undo_block_stop
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000163 undo.set_saved_change_hook(self.saved_change_hook)
164 io.set_filename_change_hook(self.filename_change_hook)
165
166 if filename:
167 if os.path.exists(filename):
168 io.loadfile(filename)
169 else:
170 io.set_filename(filename)
171
172 self.saved_change_hook()
173
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000174 self.load_extensions()
175
176 menu = self.menudict.get('windows')
177 if menu:
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000178 end = menu.index("end")
179 if end is None:
180 end = -1
181 if end >= 0:
182 menu.add_separator()
183 end = end + 1
184 self.wmenu_end = end
Guido van Rossumc4f752f1999-02-17 17:20:50 +0000185 WindowList.register_callback(self.postwindowsmenu)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000186
Guido van Rossumd395aee1999-06-02 11:04:29 +0000187 # Some abstractions so IDLE extensions are cross-IDE
188 self.askyesno = tkMessageBox.askyesno
189 self.askinteger = tkSimpleDialog.askinteger
190 self.showerror = tkMessageBox.showerror
191
Guido van Rossumdef2c961999-05-21 04:38:27 +0000192 if self.extensions.has_key('AutoIndent'):
193 self.extensions['AutoIndent'].set_indentation_params(
194 self.ispythonsource(filename))
Guido van Rossumec73dc62000-02-15 18:05:15 +0000195 self.set_status_bar()
196
197 def set_status_bar(self):
198 self.status_bar = self.MultiStatusBar(self.text_frame)
199 self.status_bar.set_label('column', 'Col: ?', side=RIGHT)
200 self.status_bar.set_label('line', 'Ln: ?', side=RIGHT)
201 self.status_bar.pack(side=BOTTOM, fill=X)
202 self.text.bind('<KeyRelease>', self.set_line_and_column)
203 self.text.bind('<ButtonRelease>', self.set_line_and_column)
204 self.text.after_idle(self.set_line_and_column)
205
206 def set_line_and_column(self, event=None):
207 line, column = string.split(self.text.index(INSERT), '.')
208 self.status_bar.set_label('column', 'Col: %s' % column)
209 self.status_bar.set_label('line', 'Ln: %s' % line)
Guido van Rossumdef2c961999-05-21 04:38:27 +0000210
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000211 def wakeup(self):
Guido van Rossum36911a11999-01-18 15:18:57 +0000212 if self.top.wm_state() == "iconic":
213 self.top.wm_deiconify()
214 else:
215 self.top.tkraise()
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000216 self.text.focus_set()
217
Guido van Rossume7b2e651998-10-12 23:56:08 +0000218 menu_specs = [
Guido van Rossumb5eed031998-11-27 03:19:07 +0000219 ("file", "_File"),
220 ("edit", "_Edit"),
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000221 ("windows", "_Windows"),
Guido van Rossumb5eed031998-11-27 03:19:07 +0000222 ("help", "_Help"),
Guido van Rossume7b2e651998-10-12 23:56:08 +0000223 ]
224
Guido van Rossum2aeeb551998-10-12 21:01:37 +0000225 def createmenubar(self):
226 mbar = self.menubar
Guido van Rossum07ec8961999-01-28 22:02:47 +0000227 self.menudict = menudict = {}
Guido van Rossume7b2e651998-10-12 23:56:08 +0000228 for name, label in self.menu_specs:
Guido van Rossum07ec8961999-01-28 22:02:47 +0000229 underline, label = prepstr(label)
230 menudict[name] = menu = Menu(mbar, name=name)
Guido van Rossumb5eed031998-11-27 03:19:07 +0000231 mbar.add_cascade(label=label, menu=menu, underline=underline)
Guido van Rossum07ec8961999-01-28 22:02:47 +0000232 self.fill_menus()
Guido van Rossum2aeeb551998-10-12 21:01:37 +0000233
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000234 def postwindowsmenu(self):
235 # Only called when Windows menu exists
Guido van Rossum85a36a51999-06-10 17:43:17 +0000236 # XXX Actually, this Just-In-Time updating interferes badly
237 # XXX with the tear-off feature. It would be better to update
238 # XXX all Windows menus whenever the list of windows changes.
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000239 menu = self.menudict['windows']
240 end = menu.index("end")
241 if end is None:
242 end = -1
243 if end > self.wmenu_end:
244 menu.delete(self.wmenu_end+1, end)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000245 WindowList.add_windows_to_menu(menu)
246
247 rmenu = None
248
249 def right_menu_event(self, event):
250 self.text.tag_remove("sel", "1.0", "end")
251 self.text.mark_set("insert", "@%d,%d" % (event.x, event.y))
252 if not self.rmenu:
253 self.make_rmenu()
254 rmenu = self.rmenu
255 self.event = event
256 iswin = sys.platform[:3] == 'win'
257 if iswin:
258 self.text.config(cursor="arrow")
259 rmenu.tk_popup(event.x_root, event.y_root)
260 if iswin:
261 self.text.config(cursor="ibeam")
262
263 rmenu_specs = [
264 # ("Label", "<<virtual-event>>"), ...
265 ("Close", "<<close-window>>"), # Example
266 ]
267
268 def make_rmenu(self):
269 rmenu = Menu(self.text, tearoff=0)
270 for label, eventname in self.rmenu_specs:
271 def command(text=self.text, eventname=eventname):
272 text.event_generate(eventname)
273 rmenu.add_command(label=label, command=command)
274 self.rmenu = rmenu
275
Guido van Rossume7b2e651998-10-12 23:56:08 +0000276 def about_dialog(self, event=None):
Guido van Rossum2aeeb551998-10-12 21:01:37 +0000277 tkMessageBox.showinfo(self.about_title, self.about_text,
278 master=self.text)
279
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000280 helpfile = "help.txt"
281
Guido van Rossume7b2e651998-10-12 23:56:08 +0000282 def help_dialog(self, event=None):
Guido van Rossum416b9611999-08-26 23:06:05 +0000283 try:
284 helpfile = os.path.join(os.path.dirname(__file__), self.helpfile)
285 except NameError:
286 helpfile = self.helpfile
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000287 if self.flist:
288 self.flist.open(helpfile)
289 else:
290 self.io.loadfile(helpfile)
291
Guido van Rossum416b9611999-08-26 23:06:05 +0000292 help_url = "http://www.python.org/doc/current/"
Fred Drake8638ace2000-05-10 16:50:07 +0000293 if sys.platform[:3] == "win":
294 fn = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
295 fn = os.path.join(fn, "Doc", "index.html")
296 if os.path.isfile(fn):
297 help_url = fn
298 del fn
Guido van Rossum416b9611999-08-26 23:06:05 +0000299
300 def python_docs(self, event=None):
Fred Drake6cd948e2000-05-10 17:28:42 +0000301 BrowserControl.open(self.help_url)
Guido van Rossum416b9611999-08-26 23:06:05 +0000302
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000303 def select_all(self, event=None):
304 self.text.tag_add("sel", "1.0", "end-1c")
305 self.text.mark_set("insert", "1.0")
306 self.text.see("insert")
307 return "break"
308
309 def remove_selection(self, event=None):
310 self.text.tag_remove("sel", "1.0", "end")
311 self.text.see("insert")
312
Guido van Rossumb3418881998-10-13 03:45:15 +0000313 def open_module(self, event=None):
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000314 # XXX Shouldn't this be in IOBinding or in FileList?
Guido van Rossumb3418881998-10-13 03:45:15 +0000315 try:
316 name = self.text.get("sel.first", "sel.last")
317 except TclError:
318 name = ""
319 else:
320 name = string.strip(name)
321 if not name:
322 name = tkSimpleDialog.askstring("Module",
Guido van Rossume1dedc01998-10-16 16:09:57 +0000323 "Enter the name of a Python module\n"
324 "to search on sys.path and open:",
Guido van Rossumb3418881998-10-13 03:45:15 +0000325 parent=self.text)
326 if name:
327 name = string.strip(name)
328 if not name:
329 return
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000330 # XXX Ought to support package syntax
331 # XXX Ought to insert current file's directory in front of path
Guido van Rossumb3418881998-10-13 03:45:15 +0000332 try:
333 (f, file, (suffix, mode, type)) = imp.find_module(name)
Guido van Rossum74311b21999-06-01 18:27:14 +0000334 except (NameError, ImportError), msg:
Guido van Rossumb3418881998-10-13 03:45:15 +0000335 tkMessageBox.showerror("Import error", str(msg), parent=self.text)
336 return
337 if type != imp.PY_SOURCE:
338 tkMessageBox.showerror("Unsupported type",
339 "%s is not a source module" % name, parent=self.text)
340 return
341 if f:
342 f.close()
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000343 if self.flist:
344 self.flist.open(file)
345 else:
346 self.io.loadfile(file)
347
348 def open_class_browser(self, event=None):
349 filename = self.io.filename
350 if not filename:
351 tkMessageBox.showerror(
352 "No filename",
353 "This buffer has no associated filename",
354 master=self.text)
Guido van Rossum74311b21999-06-01 18:27:14 +0000355 self.text.focus_set()
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000356 return None
357 head, tail = os.path.split(filename)
358 base, ext = os.path.splitext(tail)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000359 import ClassBrowser
360 ClassBrowser.ClassBrowser(self.flist, base, [head])
Guido van Rossumdef2c961999-05-21 04:38:27 +0000361
Guido van Rossumd6e87131999-03-10 05:18:02 +0000362 def open_path_browser(self, event=None):
363 import PathBrowser
364 PathBrowser.PathBrowser(self.flist)
Guido van Rossum2aeeb551998-10-12 21:01:37 +0000365
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000366 def gotoline(self, lineno):
367 if lineno is not None and lineno > 0:
368 self.text.mark_set("insert", "%d.0" % lineno)
369 self.text.tag_remove("sel", "1.0", "end")
370 self.text.tag_add("sel", "insert", "insert +1l")
371 self.center()
372
373 def ispythonsource(self, filename):
374 if not filename:
375 return 1
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000376 base, ext = os.path.splitext(os.path.basename(filename))
377 if os.path.normcase(ext) in (".py", ".pyw"):
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000378 return 1
379 try:
380 f = open(filename)
381 line = f.readline()
382 f.close()
383 except IOError:
384 return 0
385 return line[:2] == '#!' and string.find(line, 'python') >= 0
386
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000387 def close_hook(self):
388 if self.flist:
389 self.flist.close_edit(self)
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000390
391 def set_close_hook(self, close_hook):
392 self.close_hook = close_hook
393
394 def filename_change_hook(self):
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000395 if self.flist:
396 self.flist.filename_changed_edit(self)
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000397 self.saved_change_hook()
398 if self.ispythonsource(self.io.filename):
399 self.addcolorizer()
400 else:
401 self.rmcolorizer()
402
403 def addcolorizer(self):
404 if self.color:
405 return
406 ##print "Add colorizer"
407 self.per.removefilter(self.undo)
408 self.color = self.ColorDelegator()
409 self.per.insertfilter(self.color)
410 self.per.insertfilter(self.undo)
411
412 def rmcolorizer(self):
413 if not self.color:
414 return
415 ##print "Remove colorizer"
416 self.per.removefilter(self.undo)
417 self.per.removefilter(self.color)
418 self.color = None
419 self.per.insertfilter(self.undo)
420
421 def saved_change_hook(self):
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000422 short = self.short_title()
423 long = self.long_title()
424 if short and long:
425 title = short + " - " + long
426 elif short:
427 title = short
428 elif long:
429 title = long
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000430 else:
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000431 title = "Untitled"
432 icon = short or long or title
433 if not self.get_saved():
434 title = "*%s*" % title
435 icon = "*%s" % icon
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000436 self.top.wm_title(title)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000437 self.top.wm_iconname(icon)
438
439 def get_saved(self):
440 return self.undo.get_saved()
441
442 def set_saved(self, flag):
443 self.undo.set_saved(flag)
444
445 def reset_undo(self):
446 self.undo.reset_undo()
447
448 def short_title(self):
449 filename = self.io.filename
450 if filename:
451 filename = os.path.basename(filename)
452 return filename
453
454 def long_title(self):
455 return self.io.filename or ""
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000456
457 def center_insert_event(self, event):
458 self.center()
459
460 def center(self, mark="insert"):
Guido van Rossum245ddc41999-01-11 14:51:32 +0000461 text = self.text
462 top, bot = self.getwindowlines()
463 lineno = self.getlineno(mark)
464 height = bot - top
465 newtop = max(1, lineno - height/2)
466 text.yview(float(newtop))
467
468 def getwindowlines(self):
469 text = self.text
470 top = self.getlineno("@0,0")
471 bot = self.getlineno("@0,65535")
Guido van Rossum5051f4f1999-01-12 22:09:57 +0000472 if top == bot and text.winfo_height() == 1:
473 # Geometry manager hasn't run yet
Guido van Rossum245ddc41999-01-11 14:51:32 +0000474 height = int(text['height'])
475 bot = top + height - 1
476 return top, bot
477
478 def getlineno(self, mark="insert"):
479 text = self.text
480 return int(float(text.index(mark)))
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000481
482 def close_event(self, event):
483 self.close()
484
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000485 def maybesave(self):
486 if self.io:
487 return self.io.maybesave()
488
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000489 def close(self):
490 self.top.wm_deiconify()
491 self.top.tkraise()
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000492 reply = self.maybesave()
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000493 if reply != "cancel":
Guido van Rossum205afb41999-06-25 16:06:29 +0000494 self._close()
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000495 return reply
496
Guido van Rossum205afb41999-06-25 16:06:29 +0000497 def _close(self):
498 WindowList.unregister_callback(self.postwindowsmenu)
499 if self.close_hook:
500 self.close_hook()
501 self.flist = None
502 colorizing = 0
503 self.unload_extensions()
504 self.io.close(); self.io = None
505 self.undo = None # XXX
506 if self.color:
507 colorizing = self.color.colorizing
508 doh = colorizing and self.top
509 self.color.close(doh) # Cancel colorization
510 self.text = None
511 self.vars = None
512 self.per.close(); self.per = None
513 if not colorizing:
514 self.top.destroy()
515
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000516 def load_extensions(self):
517 self.extensions = {}
518 self.load_standard_extensions()
519
Guido van Rossum205afb41999-06-25 16:06:29 +0000520 def unload_extensions(self):
521 for ins in self.extensions.values():
522 if hasattr(ins, "close"):
523 ins.close()
524 self.extensions = {}
525
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000526 def load_standard_extensions(self):
527 for name in self.get_standard_extension_names():
528 try:
529 self.load_extension(name)
530 except:
531 print "Failed to load extension", `name`
532 import traceback
533 traceback.print_exc()
534
535 def get_standard_extension_names(self):
Jeremy Hyltonae1f3bd2000-03-07 17:56:27 +0000536 return idleconf.getextensions()
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000537
538 def load_extension(self, name):
Guido van Rossum9dd52091999-04-23 14:01:25 +0000539 mod = __import__(name, globals(), locals(), [])
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000540 cls = getattr(mod, name)
541 ins = cls(self)
542 self.extensions[name] = ins
543 kdnames = ["keydefs"]
544 if sys.platform == 'win32':
545 kdnames.append("windows_keydefs")
546 elif sys.platform == 'mac':
547 kdnames.append("mac_keydefs")
548 else:
549 kdnames.append("unix_keydefs")
550 keydefs = {}
551 for kdname in kdnames:
552 if hasattr(ins, kdname):
553 keydefs.update(getattr(ins, kdname))
554 if keydefs:
Guido van Rossum07ec8961999-01-28 22:02:47 +0000555 self.apply_bindings(keydefs)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000556 for vevent in keydefs.keys():
557 methodname = string.replace(vevent, "-", "_")
558 while methodname[:1] == '<':
559 methodname = methodname[1:]
560 while methodname[-1:] == '>':
561 methodname = methodname[:-1]
562 methodname = methodname + "_event"
563 if hasattr(ins, methodname):
564 self.text.bind(vevent, getattr(ins, methodname))
565 if hasattr(ins, "menudefs"):
Guido van Rossum07ec8961999-01-28 22:02:47 +0000566 self.fill_menus(ins.menudefs, keydefs)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000567 return ins
568
Guido van Rossum07ec8961999-01-28 22:02:47 +0000569 def apply_bindings(self, keydefs=None):
570 if keydefs is None:
571 keydefs = self.Bindings.default_keydefs
572 text = self.text
573 text.keydefs = keydefs
574 for event, keylist in keydefs.items():
575 if keylist:
576 apply(text.event_add, (event,) + tuple(keylist))
577
578 def fill_menus(self, defs=None, keydefs=None):
Guido van Rossum85a36a51999-06-10 17:43:17 +0000579 # Fill the menus. Menus that are absent or None in
580 # self.menudict are ignored.
Guido van Rossum07ec8961999-01-28 22:02:47 +0000581 if defs is None:
582 defs = self.Bindings.menudefs
583 if keydefs is None:
584 keydefs = self.Bindings.default_keydefs
585 menudict = self.menudict
586 text = self.text
587 for mname, itemlist in defs:
588 menu = menudict.get(mname)
589 if not menu:
590 continue
591 for item in itemlist:
592 if not item:
593 menu.add_separator()
594 else:
595 label, event = item
596 checkbutton = (label[:1] == '!')
597 if checkbutton:
598 label = label[1:]
599 underline, label = prepstr(label)
600 accelerator = get_accelerator(keydefs, event)
601 def command(text=text, event=event):
602 text.event_generate(event)
603 if checkbutton:
604 var = self.getrawvar(event, BooleanVar)
605 menu.add_checkbutton(label=label, underline=underline,
606 command=command, accelerator=accelerator,
607 variable=var)
608 else:
609 menu.add_command(label=label, underline=underline,
610 command=command, accelerator=accelerator)
Guido van Rossumdef2c961999-05-21 04:38:27 +0000611
Guido van Rossum07ec8961999-01-28 22:02:47 +0000612 def getvar(self, name):
613 var = self.getrawvar(name)
614 if var:
615 return var.get()
Guido van Rossumdef2c961999-05-21 04:38:27 +0000616
Guido van Rossum07ec8961999-01-28 22:02:47 +0000617 def setvar(self, name, value, vartype=None):
618 var = self.getrawvar(name, vartype)
619 if var:
620 var.set(value)
Guido van Rossumdef2c961999-05-21 04:38:27 +0000621
Guido van Rossum07ec8961999-01-28 22:02:47 +0000622 def getrawvar(self, name, vartype=None):
Guido van Rossumb7ebb831999-01-28 22:24:30 +0000623 var = self.vars.get(name)
624 if not var and vartype:
625 self.vars[name] = var = vartype(self.text)
626 return var
Guido van Rossum07ec8961999-01-28 22:02:47 +0000627
Guido van Rossumf4a15081999-06-03 14:32:16 +0000628 # Tk implementations of "virtual text methods" -- each platform
629 # reusing IDLE's support code needs to define these for its GUI's
630 # flavor of widget.
631
632 # Is character at text_index in a Python string? Return 0 for
Guido van Rossum85a36a51999-06-10 17:43:17 +0000633 # "guaranteed no", true for anything else. This info is expensive
634 # to compute ab initio, but is probably already known by the
635 # platform's colorizer.
Guido van Rossumf4a15081999-06-03 14:32:16 +0000636
637 def is_char_in_string(self, text_index):
638 if self.color:
Guido van Rossum85a36a51999-06-10 17:43:17 +0000639 # Return true iff colorizer hasn't (re)gotten this far
640 # yet, or the character is tagged as being in a string
Guido van Rossumf4a15081999-06-03 14:32:16 +0000641 return self.text.tag_prevrange("TODO", text_index) or \
642 "STRING" in self.text.tag_names(text_index)
643 else:
Guido van Rossum85a36a51999-06-10 17:43:17 +0000644 # The colorizer is missing: assume the worst
Guido van Rossumf4a15081999-06-03 14:32:16 +0000645 return 1
646
Guido van Rossum85a36a51999-06-10 17:43:17 +0000647 # If a selection is defined in the text widget, return (start,
648 # end) as Tkinter text indices, otherwise return (None, None)
Guido van Rossum13205601999-06-11 15:03:00 +0000649 def get_selection_indices(self):
Guido van Rossum85a36a51999-06-10 17:43:17 +0000650 try:
651 first = self.text.index("sel.first")
652 last = self.text.index("sel.last")
653 return first, last
654 except TclError:
655 return None, None
656
Guido van Rossum13205601999-06-11 15:03:00 +0000657 # Return the text widget's current view of what a tab stop means
658 # (equivalent width in spaces).
659
660 def get_tabwidth(self):
661 current = self.text['tabs'] or TK_TABWIDTH_DEFAULT
662 return int(current)
663
664 # Set the text widget's current view of what a tab stop means.
665
666 def set_tabwidth(self, newtabwidth):
667 text = self.text
668 if self.get_tabwidth() != newtabwidth:
669 pixels = text.tk.call("font", "measure", text["font"],
670 "-displayof", text.master,
671 "n" * newtabwith)
672 text.configure(tabs=pixels)
673
Guido van Rossum07ec8961999-01-28 22:02:47 +0000674def prepstr(s):
Guido van Rossum85a36a51999-06-10 17:43:17 +0000675 # Helper to extract the underscore from a string, e.g.
676 # prepstr("Co_py") returns (2, "Copy").
Guido van Rossum07ec8961999-01-28 22:02:47 +0000677 i = string.find(s, '_')
678 if i >= 0:
679 s = s[:i] + s[i+1:]
680 return i, s
681
682
683keynames = {
684 'bracketleft': '[',
685 'bracketright': ']',
686 'slash': '/',
687}
688
689def get_accelerator(keydefs, event):
690 keylist = keydefs.get(event)
691 if not keylist:
692 return ""
693 s = keylist[0]
694 s = re.sub(r"-[a-z]\b", lambda m: string.upper(m.group()), s)
695 s = re.sub(r"\b\w+\b", lambda m: keynames.get(m.group(), m.group()), s)
696 s = re.sub("Key-", "", s)
697 s = re.sub("Control-", "Ctrl-", s)
698 s = re.sub("-", "+", s)
699 s = re.sub("><", " ", s)
700 s = re.sub("<", "", s)
701 s = re.sub(">", "", s)
702 return s
703
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000704
705def fixwordbreaks(root):
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000706 # Make sure that Tk's double-click and next/previous word
707 # operations use our definition of a word (i.e. an identifier)
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000708 tk = root.tk
709 tk.call('tcl_wordBreakAfter', 'a b', 0) # make sure word.tcl is loaded
710 tk.call('set', 'tcl_wordchars', '[a-zA-Z0-9_]')
711 tk.call('set', 'tcl_nonwordchars', '[^a-zA-Z0-9_]')
712
713
714def test():
715 root = Tk()
716 fixwordbreaks(root)
717 root.withdraw()
718 if sys.argv[1:]:
719 filename = sys.argv[1]
720 else:
721 filename = None
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000722 edit = EditorWindow(root=root, filename=filename)
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000723 edit.set_close_hook(root.quit)
724 root.mainloop()
725 root.destroy()
726
727if __name__ == '__main__':
728 test()