diff options
Diffstat (limited to 'Tools/idle/EditorWindow.py')
-rw-r--r-- | Tools/idle/EditorWindow.py | 321 |
1 files changed, 287 insertions, 34 deletions
diff --git a/Tools/idle/EditorWindow.py b/Tools/idle/EditorWindow.py index 24b62dc..b4c9156 100644 --- a/Tools/idle/EditorWindow.py +++ b/Tools/idle/EditorWindow.py @@ -5,15 +5,70 @@ import imp from Tkinter import * import tkSimpleDialog import tkMessageBox +import idlever + +# File menu + +#$ event <<open-module>> +#$ win <Alt-m> +#$ unix <Control-x><Control-m> + +#$ event <<open-class-browser>> +#$ win <Alt-c> +#$ unix <Control-x><Control-b> + +#$ event <<close-window>> +#$ unix <Control-x><Control-0> +#$ unix <Control-x><Key-0> +#$ win <Alt-F4> + +# Edit menu + +#$ event <<Copy>> +#$ win <Control-c> +#$ unix <Alt-w> + +#$ event <<Cut>> +#$ win <Control-x> +#$ unix <Control-w> + +#$ event <<Paste>> +#$ win <Control-v> +#$ unix <Control-y> + +#$ event <<select-all>> +#$ win <Alt-a> +#$ unix <Alt-a> + +# Help menu + +#$ event <<help>> +#$ win <F1> +#$ unix <F1> + +#$ event <<about-idle>> + +# Events without menu entries + +#$ event <<remove-selection>> +#$ win <Escape> + +#$ event <<center-insert>> +#$ win <Control-l> +#$ unix <Control-l> + +#$ event <<do-nothing>> +#$ unix <Control-x> + about_title = "About IDLE" about_text = """\ -IDLE 0.1 +IDLE %s -A not totally unintegrated development environment for Python +An Integrated DeveLopment Environment for Python by Guido van Rossum -""" +""" % idlever.IDLE_VERSION class EditorWindow: @@ -21,44 +76,52 @@ class EditorWindow: from ColorDelegator import ColorDelegator from UndoDelegator import UndoDelegator from IOBinding import IOBinding - from SearchBinding import SearchBinding - from AutoIndent import AutoIndent - from AutoExpand import AutoExpand import Bindings - + from Tkinter import Toplevel + about_title = about_title - about_text = about_text + about_text = about_text - def __init__(self, root, filename=None): + def __init__(self, flist=None, filename=None, key=None, root=None): + self.flist = flist + root = root or flist.root self.root = root self.menubar = Menu(root) - self.top = top = Toplevel(root, menu=self.menubar) + self.top = top = self.Toplevel(root, menu=self.menubar) self.vbar = vbar = Scrollbar(top, name='vbar') - self.text = text = Text(top, name='text') + self.text = text = Text(top, name='text', padx=5, + background="white", wrap="none") self.createmenubar() self.Bindings.apply_bindings(text) self.top.protocol("WM_DELETE_WINDOW", self.close) self.top.bind("<<close-window>>", self.close_event) - self.text.bind("<<center-insert>>", self.center_insert_event) - self.text.bind("<<help>>", self.help_dialog) - self.text.bind("<<about-idle>>", self.about_dialog) - self.text.bind("<<open-module>>", self.open_module) - self.text.bind("<<do-nothing>>", lambda event: "break") + text.bind("<<center-insert>>", self.center_insert_event) + text.bind("<<help>>", self.help_dialog) + text.bind("<<about-idle>>", self.about_dialog) + text.bind("<<open-module>>", self.open_module) + text.bind("<<do-nothing>>", lambda event: "break") + text.bind("<<select-all>>", self.select_all) + text.bind("<<remove-selection>>", self.remove_selection) + text.bind("<3>", self.right_menu_event) + if flist: + flist.inversedict[self] = key + if key: + flist.dict[key] = self + text.bind("<<open-new-window>>", self.flist.new_callback) + text.bind("<<close-all-windows>>", self.flist.close_all_callback) + text.bind("<<open-class-browser>>", self.open_class_browser) vbar['command'] = text.yview vbar.pack(side=RIGHT, fill=Y) text['yscrollcommand'] = vbar.set - text['background'] = 'white' if sys.platform[:3] == 'win': text['font'] = ("lucida console", 8) text.pack(side=LEFT, fill=BOTH, expand=1) text.focus_set() - self.auto = auto = self.AutoIndent(text) - self.autoex = self.AutoExpand(text) self.per = per = self.Percolator(text) if self.ispythonsource(filename): self.color = color = self.ColorDelegator(); per.insertfilter(color) @@ -67,8 +130,7 @@ class EditorWindow: ##print "No initial colorizer" self.color = None self.undo = undo = self.UndoDelegator(); per.insertfilter(undo) - self.search = search = self.SearchBinding(undo) - self.io = io = self.IOBinding(undo) + self.io = io = self.IOBinding(self) undo.set_saved_change_hook(self.saved_change_hook) io.set_filename_change_hook(self.filename_change_hook) @@ -81,9 +143,29 @@ class EditorWindow: self.saved_change_hook() + self.load_extensions() + + menu = self.menudict.get('windows') + if menu: + menu.configure(tearoff=0) + end = menu.index("end") + if end is None: + end = -1 + if end >= 0: + menu.add_separator() + end = end + 1 + self.wmenu_end = end + menu.configure(postcommand=self.postwindowsmenu) + + def wakeup(self): + self.top.tkraise() + self.top.wm_deiconify() + self.text.focus_set() + menu_specs = [ ("file", "_File"), ("edit", "_Edit"), + ("windows", "_Windows"), ("help", "_Help"), ] @@ -96,15 +178,78 @@ class EditorWindow: mbar.add_cascade(label=label, menu=menu, underline=underline) self.Bindings.fill_menus(self.text, mdict) + def postwindowsmenu(self): + # Only called when Windows menu exists + menu = self.menudict['windows'] + end = menu.index("end") + if end is None: + end = -1 + if end > self.wmenu_end: + menu.delete(self.wmenu_end+1, end) + import WindowList + WindowList.add_windows_to_menu(menu) + + rmenu = None + + def right_menu_event(self, event): + self.text.tag_remove("sel", "1.0", "end") + self.text.mark_set("insert", "@%d,%d" % (event.x, event.y)) + if not self.rmenu: + self.make_rmenu() + rmenu = self.rmenu + self.event = event + iswin = sys.platform[:3] == 'win' + if iswin: + self.text.config(cursor="arrow") + rmenu.tk_popup(event.x_root, event.y_root) + if iswin: + self.text.config(cursor="ibeam") + + rmenu_specs = [ + # ("Label", "<<virtual-event>>"), ... + ("Close", "<<close-window>>"), # Example + ] + + def make_rmenu(self): + rmenu = Menu(self.text, tearoff=0) + for label, eventname in self.rmenu_specs: + def command(text=self.text, eventname=eventname): + text.event_generate(eventname) + rmenu.add_command(label=label, command=command) + self.rmenu = rmenu + def about_dialog(self, event=None): tkMessageBox.showinfo(self.about_title, self.about_text, master=self.text) + helpfile = "help.txt" + def help_dialog(self, event=None): - from HelpWindow import HelpWindow - HelpWindow(root=self.root) - + helpfile = self.helpfile + if not os.path.exists(helpfile): + base = os.path.basename(self.helpfile) + for dir in sys.path: + fullname = os.path.join(dir, base) + if os.path.exists(fullname): + helpfile = fullname + break + if self.flist: + self.flist.open(helpfile) + else: + self.io.loadfile(helpfile) + + def select_all(self, event=None): + self.text.tag_add("sel", "1.0", "end-1c") + self.text.mark_set("insert", "1.0") + self.text.see("insert") + return "break" + + def remove_selection(self, event=None): + self.text.tag_remove("sel", "1.0", "end") + self.text.see("insert") + def open_module(self, event=None): + # XXX Shouldn't this be in IOBinding or in FileList? try: name = self.text.get("sel.first", "sel.last") except TclError: @@ -120,6 +265,8 @@ class EditorWindow: name = string.strip(name) if not name: return + # XXX Ought to support package syntax + # XXX Ought to insert current file's directory in front of path try: (f, file, (suffix, mode, type)) = imp.find_module(name) except ImportError, msg: @@ -131,7 +278,26 @@ class EditorWindow: return if f: f.close() - self.flist.open(file, self) + if self.flist: + self.flist.open(file) + else: + self.io.loadfile(file) + + def open_class_browser(self, event=None): + filename = self.io.filename + if not filename: + tkMessageBox.showerror( + "No filename", + "This buffer has no associated filename", + master=self.text) + return None + head, tail = os.path.split(filename) + base, ext = os.path.splitext(tail) + import pyclbr + if pyclbr._modules.has_key(base): + del pyclbr._modules[base] + import ClassBrowser + ClassBrowser.ClassBrowser(self.flist, base, [head]) def gotoline(self, lineno): if lineno is not None and lineno > 0: @@ -143,7 +309,8 @@ class EditorWindow: def ispythonsource(self, filename): if not filename: return 1 - if os.path.normcase(filename[-3:]) == ".py": + base, ext = os.path.splitext(os.path.basename(filename)) + if os.path.normcase(ext) in (".py", ".pyw"): return 1 try: f = open(filename) @@ -153,12 +320,16 @@ class EditorWindow: return 0 return line[:2] == '#!' and string.find(line, 'python') >= 0 - close_hook = None + def close_hook(self): + if self.flist: + self.flist.close_edit(self) def set_close_hook(self, close_hook): self.close_hook = close_hook def filename_change_hook(self): + if self.flist: + self.flist.filename_changed_edit(self) self.saved_change_hook() if self.ispythonsource(self.io.filename): self.addcolorizer() @@ -184,13 +355,40 @@ class EditorWindow: self.per.insertfilter(self.undo) def saved_change_hook(self): - if self.io.filename: - title = self.io.filename + short = self.short_title() + long = self.long_title() + if short and long: + title = short + " - " + long + elif short: + title = short + elif long: + title = long else: - title = "(Untitled)" - if not self.undo.get_saved(): - title = title + " *" + title = "Untitled" + icon = short or long or title + if not self.get_saved(): + title = "*%s*" % title + icon = "*%s" % icon self.top.wm_title(title) + self.top.wm_iconname(icon) + + def get_saved(self): + return self.undo.get_saved() + + def set_saved(self, flag): + self.undo.set_saved(flag) + + def reset_undo(self): + self.undo.reset_undo() + + def short_title(self): + filename = self.io.filename + if filename: + filename = os.path.basename(filename) + return filename + + def long_title(self): + return self.io.filename or "" def center_insert_event(self, event): self.center() @@ -207,10 +405,14 @@ class EditorWindow: def close_event(self, event): self.close() + def maybesave(self): + if self.io: + return self.io.maybesave() + def close(self): self.top.wm_deiconify() self.top.tkraise() - reply = self.io.maybesave() + reply = self.maybesave() if reply != "cancel": if self.color and self.color.colorizing: self.color.close() @@ -223,8 +425,59 @@ class EditorWindow: self.top.destroy() return reply + def load_extensions(self): + self.extensions = {} + self.load_standard_extensions() + + def load_standard_extensions(self): + for name in self.get_standard_extension_names(): + try: + self.load_extension(name) + except: + print "Failed to load extension", `name` + import traceback + traceback.print_exc() + + def get_standard_extension_names(self): + import extend + return extend.standard + + def load_extension(self, name): + mod = __import__(name) + cls = getattr(mod, name) + ins = cls(self) + self.extensions[name] = ins + kdnames = ["keydefs"] + if sys.platform == 'win32': + kdnames.append("windows_keydefs") + elif sys.platform == 'mac': + kdnames.append("mac_keydefs") + else: + kdnames.append("unix_keydefs") + keydefs = {} + for kdname in kdnames: + if hasattr(ins, kdname): + keydefs.update(getattr(ins, kdname)) + if keydefs: + self.Bindings.apply_bindings(self.text, keydefs) + for vevent in keydefs.keys(): + methodname = string.replace(vevent, "-", "_") + while methodname[:1] == '<': + methodname = methodname[1:] + while methodname[-1:] == '>': + methodname = methodname[:-1] + methodname = methodname + "_event" + if hasattr(ins, methodname): + self.text.bind(vevent, getattr(ins, methodname)) + if hasattr(ins, "menudefs"): + self.Bindings.fill_menus(self.text, self. menudict, + ins.menudefs, keydefs) + return ins + def fixwordbreaks(root): + # Make sure that Tk's double-click and next/previous word + # operations use our definition of a word (i.e. an identifier) tk = root.tk tk.call('tcl_wordBreakAfter', 'a b', 0) # make sure word.tcl is loaded tk.call('set', 'tcl_wordchars', '[a-zA-Z0-9_]') @@ -239,7 +492,7 @@ def test(): filename = sys.argv[1] else: filename = None - edit = EditorWindow(root, filename) + edit = EditorWindow(root=root, filename=filename) edit.set_close_hook(root.quit) root.mainloop() root.destroy() |