blob: d744a2e386cfb90befdae429e04c763104f0abad [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 Drake36377992000-07-09 19:10:19 +00009try:
10 import webbrowser
11except ImportError:
12 import BrowserControl
13 webbrowser = BrowserControl
14 del BrowserControl
Guido van Rossum504b0bf1999-01-02 21:28:54 +000015import idlever
Guido van Rossumc4f752f1999-02-17 17:20:50 +000016import WindowList
Jeremy Hyltonae1f3bd2000-03-07 17:56:27 +000017from IdleConf import idleconf
Guido van Rossum99aabe32000-02-17 16:14:16 +000018
Guido van Rossum13205601999-06-11 15:03:00 +000019# The default tab setting for a Text widget, in average-width characters.
20TK_TABWIDTH_DEFAULT = 8
21
Guido van Rossum504b0bf1999-01-02 21:28:54 +000022# File menu
23
24#$ event <<open-module>>
25#$ win <Alt-m>
26#$ unix <Control-x><Control-m>
27
28#$ event <<open-class-browser>>
29#$ win <Alt-c>
30#$ unix <Control-x><Control-b>
31
Guido van Rossumd6e87131999-03-10 05:18:02 +000032#$ event <<open-path-browser>>
33
Guido van Rossum504b0bf1999-01-02 21:28:54 +000034#$ event <<close-window>>
Fred Drake36377992000-07-09 19:10:19 +000035
Guido van Rossum504b0bf1999-01-02 21:28:54 +000036#$ unix <Control-x><Control-0>
37#$ unix <Control-x><Key-0>
38#$ win <Alt-F4>
39
40# Edit menu
41
42#$ event <<Copy>>
43#$ win <Control-c>
44#$ unix <Alt-w>
45
46#$ event <<Cut>>
47#$ win <Control-x>
48#$ unix <Control-w>
49
50#$ event <<Paste>>
51#$ win <Control-v>
52#$ unix <Control-y>
53
54#$ event <<select-all>>
55#$ win <Alt-a>
56#$ unix <Alt-a>
57
58# Help menu
59
60#$ event <<help>>
61#$ win <F1>
62#$ unix <F1>
63
64#$ event <<about-idle>>
65
66# Events without menu entries
67
68#$ event <<remove-selection>>
69#$ win <Escape>
70
71#$ event <<center-insert>>
72#$ win <Control-l>
73#$ unix <Control-l>
74
75#$ event <<do-nothing>>
76#$ unix <Control-x>
77
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +000078
Guido van Rossum2aeeb551998-10-12 21:01:37 +000079about_title = "About IDLE"
80about_text = """\
Guido van Rossum504b0bf1999-01-02 21:28:54 +000081IDLE %s
Guido van Rossum2aeeb551998-10-12 21:01:37 +000082
Guido van Rossum504b0bf1999-01-02 21:28:54 +000083An Integrated DeveLopment Environment for Python
Guido van Rossum2aeeb551998-10-12 21:01:37 +000084
85by Guido van Rossum
Guido van Rossum504b0bf1999-01-02 21:28:54 +000086""" % idlever.IDLE_VERSION
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +000087
88class EditorWindow:
89
90 from Percolator import Percolator
91 from ColorDelegator import ColorDelegator
92 from UndoDelegator import UndoDelegator
93 from IOBinding import IOBinding
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +000094 import Bindings
Guido van Rossum504b0bf1999-01-02 21:28:54 +000095 from Tkinter import Toplevel
Guido van Rossumec73dc62000-02-15 18:05:15 +000096 from MultiStatusBar import MultiStatusBar
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +000097
Guido van Rossum504b0bf1999-01-02 21:28:54 +000098 about_title = about_title
99 about_text = about_text
100
Guido van Rossumb7ebb831999-01-28 22:24:30 +0000101 vars = {}
102
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000103 def __init__(self, flist=None, filename=None, key=None, root=None):
Jeremy Hyltonae1f3bd2000-03-07 17:56:27 +0000104 edconf = idleconf.getsection('EditorWindow')
105 coconf = idleconf.getsection('Colors')
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000106 self.flist = flist
107 root = root or flist.root
Guido van Rossum2aeeb551998-10-12 21:01:37 +0000108 self.root = root
Guido van Rossumb7ebb831999-01-28 22:24:30 +0000109 if flist:
110 self.vars = flist.vars
Guido van Rossum2aeeb551998-10-12 21:01:37 +0000111 self.menubar = Menu(root)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000112 self.top = top = self.Toplevel(root, menu=self.menubar)
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000113 self.vbar = vbar = Scrollbar(top, name='vbar')
Guido van Rossumec73dc62000-02-15 18:05:15 +0000114 self.text_frame = text_frame = Frame(top)
115 self.text = text = Text(text_frame, name='text', padx=5,
Jeremy Hyltone81f28b2000-03-03 23:06:45 +0000116 foreground=coconf.getdef('normal-foreground'),
117 background=coconf.getdef('normal-background'),
118 highlightcolor=coconf.getdef('hilite-foreground'),
119 highlightbackground=coconf.getdef('hilite-background'),
120 insertbackground=coconf.getdef('cursor-background'),
121 width=edconf.getint('width'),
122 height=edconf.getint('height'),
123 wrap="none")
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000124
Guido van Rossum2aeeb551998-10-12 21:01:37 +0000125 self.createmenubar()
Guido van Rossum07ec8961999-01-28 22:02:47 +0000126 self.apply_bindings()
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000127
128 self.top.protocol("WM_DELETE_WINDOW", self.close)
129 self.top.bind("<<close-window>>", self.close_event)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000130 text.bind("<<center-insert>>", self.center_insert_event)
131 text.bind("<<help>>", self.help_dialog)
Guido van Rossum416b9611999-08-26 23:06:05 +0000132 text.bind("<<python-docs>>", self.python_docs)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000133 text.bind("<<about-idle>>", self.about_dialog)
134 text.bind("<<open-module>>", self.open_module)
135 text.bind("<<do-nothing>>", lambda event: "break")
136 text.bind("<<select-all>>", self.select_all)
137 text.bind("<<remove-selection>>", self.remove_selection)
138 text.bind("<3>", self.right_menu_event)
139 if flist:
140 flist.inversedict[self] = key
141 if key:
142 flist.dict[key] = self
143 text.bind("<<open-new-window>>", self.flist.new_callback)
144 text.bind("<<close-all-windows>>", self.flist.close_all_callback)
145 text.bind("<<open-class-browser>>", self.open_class_browser)
Guido van Rossumd6e87131999-03-10 05:18:02 +0000146 text.bind("<<open-path-browser>>", self.open_path_browser)
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000147
148 vbar['command'] = text.yview
149 vbar.pack(side=RIGHT, fill=Y)
150
151 text['yscrollcommand'] = vbar.set
Jeremy Hyltone81f28b2000-03-03 23:06:45 +0000152 text['font'] = edconf.get('font-name'), edconf.get('font-size')
Guido van Rossumec73dc62000-02-15 18:05:15 +0000153 text_frame.pack(side=LEFT, fill=BOTH, expand=1)
154 text.pack(side=TOP, fill=BOTH, expand=1)
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000155 text.focus_set()
156
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000157 self.per = per = self.Percolator(text)
158 if self.ispythonsource(filename):
159 self.color = color = self.ColorDelegator(); per.insertfilter(color)
160 ##print "Initial colorizer"
161 else:
162 ##print "No initial colorizer"
163 self.color = None
164 self.undo = undo = self.UndoDelegator(); per.insertfilter(undo)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000165 self.io = io = self.IOBinding(self)
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000166
Guido van Rossum318a70d1999-05-03 15:49:52 +0000167 text.undo_block_start = undo.undo_block_start
168 text.undo_block_stop = undo.undo_block_stop
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000169 undo.set_saved_change_hook(self.saved_change_hook)
170 io.set_filename_change_hook(self.filename_change_hook)
171
172 if filename:
173 if os.path.exists(filename):
174 io.loadfile(filename)
175 else:
176 io.set_filename(filename)
177
178 self.saved_change_hook()
179
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000180 self.load_extensions()
181
182 menu = self.menudict.get('windows')
183 if menu:
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000184 end = menu.index("end")
185 if end is None:
186 end = -1
187 if end >= 0:
188 menu.add_separator()
189 end = end + 1
190 self.wmenu_end = end
Guido van Rossumc4f752f1999-02-17 17:20:50 +0000191 WindowList.register_callback(self.postwindowsmenu)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000192
Guido van Rossumd395aee1999-06-02 11:04:29 +0000193 # Some abstractions so IDLE extensions are cross-IDE
194 self.askyesno = tkMessageBox.askyesno
195 self.askinteger = tkSimpleDialog.askinteger
196 self.showerror = tkMessageBox.showerror
197
Guido van Rossumdef2c961999-05-21 04:38:27 +0000198 if self.extensions.has_key('AutoIndent'):
199 self.extensions['AutoIndent'].set_indentation_params(
200 self.ispythonsource(filename))
Guido van Rossumec73dc62000-02-15 18:05:15 +0000201 self.set_status_bar()
202
203 def set_status_bar(self):
204 self.status_bar = self.MultiStatusBar(self.text_frame)
205 self.status_bar.set_label('column', 'Col: ?', side=RIGHT)
206 self.status_bar.set_label('line', 'Ln: ?', side=RIGHT)
207 self.status_bar.pack(side=BOTTOM, fill=X)
208 self.text.bind('<KeyRelease>', self.set_line_and_column)
209 self.text.bind('<ButtonRelease>', self.set_line_and_column)
210 self.text.after_idle(self.set_line_and_column)
211
212 def set_line_and_column(self, event=None):
213 line, column = string.split(self.text.index(INSERT), '.')
214 self.status_bar.set_label('column', 'Col: %s' % column)
215 self.status_bar.set_label('line', 'Ln: %s' % line)
Guido van Rossumdef2c961999-05-21 04:38:27 +0000216
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000217 def wakeup(self):
Guido van Rossum36911a11999-01-18 15:18:57 +0000218 if self.top.wm_state() == "iconic":
219 self.top.wm_deiconify()
220 else:
221 self.top.tkraise()
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000222 self.text.focus_set()
223
Guido van Rossume7b2e651998-10-12 23:56:08 +0000224 menu_specs = [
Guido van Rossumb5eed031998-11-27 03:19:07 +0000225 ("file", "_File"),
226 ("edit", "_Edit"),
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000227 ("windows", "_Windows"),
Guido van Rossumb5eed031998-11-27 03:19:07 +0000228 ("help", "_Help"),
Guido van Rossume7b2e651998-10-12 23:56:08 +0000229 ]
230
Guido van Rossum2aeeb551998-10-12 21:01:37 +0000231 def createmenubar(self):
232 mbar = self.menubar
Guido van Rossum07ec8961999-01-28 22:02:47 +0000233 self.menudict = menudict = {}
Guido van Rossume7b2e651998-10-12 23:56:08 +0000234 for name, label in self.menu_specs:
Guido van Rossum07ec8961999-01-28 22:02:47 +0000235 underline, label = prepstr(label)
236 menudict[name] = menu = Menu(mbar, name=name)
Guido van Rossumb5eed031998-11-27 03:19:07 +0000237 mbar.add_cascade(label=label, menu=menu, underline=underline)
Guido van Rossum07ec8961999-01-28 22:02:47 +0000238 self.fill_menus()
Guido van Rossum2aeeb551998-10-12 21:01:37 +0000239
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000240 def postwindowsmenu(self):
241 # Only called when Windows menu exists
Guido van Rossum85a36a51999-06-10 17:43:17 +0000242 # XXX Actually, this Just-In-Time updating interferes badly
243 # XXX with the tear-off feature. It would be better to update
244 # XXX all Windows menus whenever the list of windows changes.
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000245 menu = self.menudict['windows']
246 end = menu.index("end")
247 if end is None:
248 end = -1
249 if end > self.wmenu_end:
250 menu.delete(self.wmenu_end+1, end)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000251 WindowList.add_windows_to_menu(menu)
252
253 rmenu = None
254
255 def right_menu_event(self, event):
256 self.text.tag_remove("sel", "1.0", "end")
257 self.text.mark_set("insert", "@%d,%d" % (event.x, event.y))
258 if not self.rmenu:
259 self.make_rmenu()
260 rmenu = self.rmenu
261 self.event = event
262 iswin = sys.platform[:3] == 'win'
263 if iswin:
264 self.text.config(cursor="arrow")
265 rmenu.tk_popup(event.x_root, event.y_root)
266 if iswin:
267 self.text.config(cursor="ibeam")
268
269 rmenu_specs = [
270 # ("Label", "<<virtual-event>>"), ...
271 ("Close", "<<close-window>>"), # Example
272 ]
273
274 def make_rmenu(self):
275 rmenu = Menu(self.text, tearoff=0)
276 for label, eventname in self.rmenu_specs:
277 def command(text=self.text, eventname=eventname):
278 text.event_generate(eventname)
279 rmenu.add_command(label=label, command=command)
280 self.rmenu = rmenu
281
Guido van Rossume7b2e651998-10-12 23:56:08 +0000282 def about_dialog(self, event=None):
Guido van Rossum2aeeb551998-10-12 21:01:37 +0000283 tkMessageBox.showinfo(self.about_title, self.about_text,
284 master=self.text)
285
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000286 helpfile = "help.txt"
287
Guido van Rossume7b2e651998-10-12 23:56:08 +0000288 def help_dialog(self, event=None):
Guido van Rossum416b9611999-08-26 23:06:05 +0000289 try:
290 helpfile = os.path.join(os.path.dirname(__file__), self.helpfile)
291 except NameError:
292 helpfile = self.helpfile
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000293 if self.flist:
294 self.flist.open(helpfile)
295 else:
296 self.io.loadfile(helpfile)
297
Guido van Rossum416b9611999-08-26 23:06:05 +0000298 help_url = "http://www.python.org/doc/current/"
Fred Drake8638ace2000-05-10 16:50:07 +0000299 if sys.platform[:3] == "win":
300 fn = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
301 fn = os.path.join(fn, "Doc", "index.html")
302 if os.path.isfile(fn):
303 help_url = fn
304 del fn
Guido van Rossum416b9611999-08-26 23:06:05 +0000305
306 def python_docs(self, event=None):
Fred Drake36377992000-07-09 19:10:19 +0000307 webbrowser.open(self.help_url)
Guido van Rossum416b9611999-08-26 23:06:05 +0000308
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000309 def select_all(self, event=None):
310 self.text.tag_add("sel", "1.0", "end-1c")
311 self.text.mark_set("insert", "1.0")
312 self.text.see("insert")
313 return "break"
314
315 def remove_selection(self, event=None):
316 self.text.tag_remove("sel", "1.0", "end")
317 self.text.see("insert")
318
Guido van Rossumb3418881998-10-13 03:45:15 +0000319 def open_module(self, event=None):
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000320 # XXX Shouldn't this be in IOBinding or in FileList?
Guido van Rossumb3418881998-10-13 03:45:15 +0000321 try:
322 name = self.text.get("sel.first", "sel.last")
323 except TclError:
324 name = ""
325 else:
326 name = string.strip(name)
327 if not name:
328 name = tkSimpleDialog.askstring("Module",
Guido van Rossume1dedc01998-10-16 16:09:57 +0000329 "Enter the name of a Python module\n"
330 "to search on sys.path and open:",
Guido van Rossumb3418881998-10-13 03:45:15 +0000331 parent=self.text)
332 if name:
333 name = string.strip(name)
334 if not name:
335 return
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000336 # XXX Ought to support package syntax
337 # XXX Ought to insert current file's directory in front of path
Guido van Rossumb3418881998-10-13 03:45:15 +0000338 try:
339 (f, file, (suffix, mode, type)) = imp.find_module(name)
Guido van Rossum74311b21999-06-01 18:27:14 +0000340 except (NameError, ImportError), msg:
Guido van Rossumb3418881998-10-13 03:45:15 +0000341 tkMessageBox.showerror("Import error", str(msg), parent=self.text)
342 return
343 if type != imp.PY_SOURCE:
344 tkMessageBox.showerror("Unsupported type",
345 "%s is not a source module" % name, parent=self.text)
346 return
347 if f:
348 f.close()
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000349 if self.flist:
350 self.flist.open(file)
351 else:
352 self.io.loadfile(file)
353
354 def open_class_browser(self, event=None):
355 filename = self.io.filename
356 if not filename:
357 tkMessageBox.showerror(
358 "No filename",
359 "This buffer has no associated filename",
360 master=self.text)
Guido van Rossum74311b21999-06-01 18:27:14 +0000361 self.text.focus_set()
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000362 return None
363 head, tail = os.path.split(filename)
364 base, ext = os.path.splitext(tail)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000365 import ClassBrowser
366 ClassBrowser.ClassBrowser(self.flist, base, [head])
Guido van Rossumdef2c961999-05-21 04:38:27 +0000367
Guido van Rossumd6e87131999-03-10 05:18:02 +0000368 def open_path_browser(self, event=None):
369 import PathBrowser
370 PathBrowser.PathBrowser(self.flist)
Guido van Rossum2aeeb551998-10-12 21:01:37 +0000371
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000372 def gotoline(self, lineno):
373 if lineno is not None and lineno > 0:
374 self.text.mark_set("insert", "%d.0" % lineno)
375 self.text.tag_remove("sel", "1.0", "end")
376 self.text.tag_add("sel", "insert", "insert +1l")
377 self.center()
378
379 def ispythonsource(self, filename):
380 if not filename:
381 return 1
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000382 base, ext = os.path.splitext(os.path.basename(filename))
383 if os.path.normcase(ext) in (".py", ".pyw"):
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000384 return 1
385 try:
386 f = open(filename)
387 line = f.readline()
388 f.close()
389 except IOError:
390 return 0
391 return line[:2] == '#!' and string.find(line, 'python') >= 0
392
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000393 def close_hook(self):
394 if self.flist:
395 self.flist.close_edit(self)
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000396
397 def set_close_hook(self, close_hook):
398 self.close_hook = close_hook
399
400 def filename_change_hook(self):
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000401 if self.flist:
402 self.flist.filename_changed_edit(self)
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000403 self.saved_change_hook()
404 if self.ispythonsource(self.io.filename):
405 self.addcolorizer()
406 else:
407 self.rmcolorizer()
408
409 def addcolorizer(self):
410 if self.color:
411 return
412 ##print "Add colorizer"
413 self.per.removefilter(self.undo)
414 self.color = self.ColorDelegator()
415 self.per.insertfilter(self.color)
416 self.per.insertfilter(self.undo)
417
418 def rmcolorizer(self):
419 if not self.color:
420 return
421 ##print "Remove colorizer"
422 self.per.removefilter(self.undo)
423 self.per.removefilter(self.color)
424 self.color = None
425 self.per.insertfilter(self.undo)
426
427 def saved_change_hook(self):
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000428 short = self.short_title()
429 long = self.long_title()
430 if short and long:
431 title = short + " - " + long
432 elif short:
433 title = short
434 elif long:
435 title = long
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000436 else:
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000437 title = "Untitled"
438 icon = short or long or title
439 if not self.get_saved():
440 title = "*%s*" % title
441 icon = "*%s" % icon
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000442 self.top.wm_title(title)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000443 self.top.wm_iconname(icon)
444
445 def get_saved(self):
446 return self.undo.get_saved()
447
448 def set_saved(self, flag):
449 self.undo.set_saved(flag)
450
451 def reset_undo(self):
452 self.undo.reset_undo()
453
454 def short_title(self):
455 filename = self.io.filename
456 if filename:
457 filename = os.path.basename(filename)
458 return filename
459
460 def long_title(self):
461 return self.io.filename or ""
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000462
463 def center_insert_event(self, event):
464 self.center()
465
466 def center(self, mark="insert"):
Guido van Rossum245ddc41999-01-11 14:51:32 +0000467 text = self.text
468 top, bot = self.getwindowlines()
469 lineno = self.getlineno(mark)
470 height = bot - top
471 newtop = max(1, lineno - height/2)
472 text.yview(float(newtop))
473
474 def getwindowlines(self):
475 text = self.text
476 top = self.getlineno("@0,0")
477 bot = self.getlineno("@0,65535")
Guido van Rossum5051f4f1999-01-12 22:09:57 +0000478 if top == bot and text.winfo_height() == 1:
479 # Geometry manager hasn't run yet
Guido van Rossum245ddc41999-01-11 14:51:32 +0000480 height = int(text['height'])
481 bot = top + height - 1
482 return top, bot
483
484 def getlineno(self, mark="insert"):
485 text = self.text
486 return int(float(text.index(mark)))
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000487
488 def close_event(self, event):
489 self.close()
490
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000491 def maybesave(self):
492 if self.io:
493 return self.io.maybesave()
494
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000495 def close(self):
496 self.top.wm_deiconify()
497 self.top.tkraise()
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000498 reply = self.maybesave()
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000499 if reply != "cancel":
Guido van Rossum205afb41999-06-25 16:06:29 +0000500 self._close()
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000501 return reply
502
Guido van Rossum205afb41999-06-25 16:06:29 +0000503 def _close(self):
504 WindowList.unregister_callback(self.postwindowsmenu)
505 if self.close_hook:
506 self.close_hook()
507 self.flist = None
508 colorizing = 0
509 self.unload_extensions()
510 self.io.close(); self.io = None
511 self.undo = None # XXX
512 if self.color:
513 colorizing = self.color.colorizing
514 doh = colorizing and self.top
515 self.color.close(doh) # Cancel colorization
516 self.text = None
517 self.vars = None
518 self.per.close(); self.per = None
519 if not colorizing:
520 self.top.destroy()
521
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000522 def load_extensions(self):
523 self.extensions = {}
524 self.load_standard_extensions()
525
Guido van Rossum205afb41999-06-25 16:06:29 +0000526 def unload_extensions(self):
527 for ins in self.extensions.values():
528 if hasattr(ins, "close"):
529 ins.close()
530 self.extensions = {}
531
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000532 def load_standard_extensions(self):
533 for name in self.get_standard_extension_names():
534 try:
535 self.load_extension(name)
536 except:
537 print "Failed to load extension", `name`
538 import traceback
539 traceback.print_exc()
540
541 def get_standard_extension_names(self):
Jeremy Hyltonae1f3bd2000-03-07 17:56:27 +0000542 return idleconf.getextensions()
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000543
544 def load_extension(self, name):
Guido van Rossum9dd52091999-04-23 14:01:25 +0000545 mod = __import__(name, globals(), locals(), [])
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000546 cls = getattr(mod, name)
547 ins = cls(self)
548 self.extensions[name] = ins
549 kdnames = ["keydefs"]
550 if sys.platform == 'win32':
551 kdnames.append("windows_keydefs")
552 elif sys.platform == 'mac':
553 kdnames.append("mac_keydefs")
554 else:
555 kdnames.append("unix_keydefs")
556 keydefs = {}
557 for kdname in kdnames:
558 if hasattr(ins, kdname):
559 keydefs.update(getattr(ins, kdname))
560 if keydefs:
Guido van Rossum07ec8961999-01-28 22:02:47 +0000561 self.apply_bindings(keydefs)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000562 for vevent in keydefs.keys():
563 methodname = string.replace(vevent, "-", "_")
564 while methodname[:1] == '<':
565 methodname = methodname[1:]
566 while methodname[-1:] == '>':
567 methodname = methodname[:-1]
568 methodname = methodname + "_event"
569 if hasattr(ins, methodname):
570 self.text.bind(vevent, getattr(ins, methodname))
571 if hasattr(ins, "menudefs"):
Guido van Rossum07ec8961999-01-28 22:02:47 +0000572 self.fill_menus(ins.menudefs, keydefs)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000573 return ins
574
Guido van Rossum07ec8961999-01-28 22:02:47 +0000575 def apply_bindings(self, keydefs=None):
576 if keydefs is None:
577 keydefs = self.Bindings.default_keydefs
578 text = self.text
579 text.keydefs = keydefs
580 for event, keylist in keydefs.items():
581 if keylist:
582 apply(text.event_add, (event,) + tuple(keylist))
583
584 def fill_menus(self, defs=None, keydefs=None):
Guido van Rossum85a36a51999-06-10 17:43:17 +0000585 # Fill the menus. Menus that are absent or None in
586 # self.menudict are ignored.
Guido van Rossum07ec8961999-01-28 22:02:47 +0000587 if defs is None:
588 defs = self.Bindings.menudefs
589 if keydefs is None:
590 keydefs = self.Bindings.default_keydefs
591 menudict = self.menudict
592 text = self.text
593 for mname, itemlist in defs:
594 menu = menudict.get(mname)
595 if not menu:
596 continue
597 for item in itemlist:
598 if not item:
599 menu.add_separator()
600 else:
601 label, event = item
602 checkbutton = (label[:1] == '!')
603 if checkbutton:
604 label = label[1:]
605 underline, label = prepstr(label)
606 accelerator = get_accelerator(keydefs, event)
607 def command(text=text, event=event):
608 text.event_generate(event)
609 if checkbutton:
610 var = self.getrawvar(event, BooleanVar)
611 menu.add_checkbutton(label=label, underline=underline,
612 command=command, accelerator=accelerator,
613 variable=var)
614 else:
615 menu.add_command(label=label, underline=underline,
616 command=command, accelerator=accelerator)
Guido van Rossumdef2c961999-05-21 04:38:27 +0000617
Guido van Rossum07ec8961999-01-28 22:02:47 +0000618 def getvar(self, name):
619 var = self.getrawvar(name)
620 if var:
621 return var.get()
Guido van Rossumdef2c961999-05-21 04:38:27 +0000622
Guido van Rossum07ec8961999-01-28 22:02:47 +0000623 def setvar(self, name, value, vartype=None):
624 var = self.getrawvar(name, vartype)
625 if var:
626 var.set(value)
Guido van Rossumdef2c961999-05-21 04:38:27 +0000627
Guido van Rossum07ec8961999-01-28 22:02:47 +0000628 def getrawvar(self, name, vartype=None):
Guido van Rossumb7ebb831999-01-28 22:24:30 +0000629 var = self.vars.get(name)
630 if not var and vartype:
631 self.vars[name] = var = vartype(self.text)
632 return var
Guido van Rossum07ec8961999-01-28 22:02:47 +0000633
Guido van Rossumf4a15081999-06-03 14:32:16 +0000634 # Tk implementations of "virtual text methods" -- each platform
635 # reusing IDLE's support code needs to define these for its GUI's
636 # flavor of widget.
637
638 # Is character at text_index in a Python string? Return 0 for
Guido van Rossum85a36a51999-06-10 17:43:17 +0000639 # "guaranteed no", true for anything else. This info is expensive
640 # to compute ab initio, but is probably already known by the
641 # platform's colorizer.
Guido van Rossumf4a15081999-06-03 14:32:16 +0000642
643 def is_char_in_string(self, text_index):
644 if self.color:
Guido van Rossum85a36a51999-06-10 17:43:17 +0000645 # Return true iff colorizer hasn't (re)gotten this far
646 # yet, or the character is tagged as being in a string
Guido van Rossumf4a15081999-06-03 14:32:16 +0000647 return self.text.tag_prevrange("TODO", text_index) or \
648 "STRING" in self.text.tag_names(text_index)
649 else:
Guido van Rossum85a36a51999-06-10 17:43:17 +0000650 # The colorizer is missing: assume the worst
Guido van Rossumf4a15081999-06-03 14:32:16 +0000651 return 1
652
Guido van Rossum85a36a51999-06-10 17:43:17 +0000653 # If a selection is defined in the text widget, return (start,
654 # end) as Tkinter text indices, otherwise return (None, None)
Guido van Rossum13205601999-06-11 15:03:00 +0000655 def get_selection_indices(self):
Guido van Rossum85a36a51999-06-10 17:43:17 +0000656 try:
657 first = self.text.index("sel.first")
658 last = self.text.index("sel.last")
659 return first, last
660 except TclError:
661 return None, None
662
Guido van Rossum13205601999-06-11 15:03:00 +0000663 # Return the text widget's current view of what a tab stop means
664 # (equivalent width in spaces).
665
666 def get_tabwidth(self):
667 current = self.text['tabs'] or TK_TABWIDTH_DEFAULT
668 return int(current)
669
670 # Set the text widget's current view of what a tab stop means.
671
672 def set_tabwidth(self, newtabwidth):
673 text = self.text
674 if self.get_tabwidth() != newtabwidth:
675 pixels = text.tk.call("font", "measure", text["font"],
676 "-displayof", text.master,
677 "n" * newtabwith)
678 text.configure(tabs=pixels)
679
Guido van Rossum07ec8961999-01-28 22:02:47 +0000680def prepstr(s):
Guido van Rossum85a36a51999-06-10 17:43:17 +0000681 # Helper to extract the underscore from a string, e.g.
682 # prepstr("Co_py") returns (2, "Copy").
Guido van Rossum07ec8961999-01-28 22:02:47 +0000683 i = string.find(s, '_')
684 if i >= 0:
685 s = s[:i] + s[i+1:]
686 return i, s
687
688
689keynames = {
690 'bracketleft': '[',
691 'bracketright': ']',
692 'slash': '/',
693}
694
695def get_accelerator(keydefs, event):
696 keylist = keydefs.get(event)
697 if not keylist:
698 return ""
699 s = keylist[0]
700 s = re.sub(r"-[a-z]\b", lambda m: string.upper(m.group()), s)
701 s = re.sub(r"\b\w+\b", lambda m: keynames.get(m.group(), m.group()), s)
702 s = re.sub("Key-", "", s)
703 s = re.sub("Control-", "Ctrl-", s)
704 s = re.sub("-", "+", s)
705 s = re.sub("><", " ", s)
706 s = re.sub("<", "", s)
707 s = re.sub(">", "", s)
708 return s
709
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000710
711def fixwordbreaks(root):
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000712 # Make sure that Tk's double-click and next/previous word
713 # operations use our definition of a word (i.e. an identifier)
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000714 tk = root.tk
715 tk.call('tcl_wordBreakAfter', 'a b', 0) # make sure word.tcl is loaded
716 tk.call('set', 'tcl_wordchars', '[a-zA-Z0-9_]')
717 tk.call('set', 'tcl_nonwordchars', '[^a-zA-Z0-9_]')
718
719
720def test():
721 root = Tk()
722 fixwordbreaks(root)
723 root.withdraw()
724 if sys.argv[1:]:
725 filename = sys.argv[1]
726 else:
727 filename = None
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000728 edit = EditorWindow(root=root, filename=filename)
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000729 edit.set_close_hook(root.quit)
730 root.mainloop()
731 root.destroy()
732
733if __name__ == '__main__':
734 test()