summaryrefslogtreecommitdiffstats
path: root/Tools/idle/EditorWindow.py
diff options
context:
space:
mode:
Diffstat (limited to 'Tools/idle/EditorWindow.py')
-rw-r--r--Tools/idle/EditorWindow.py321
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()