Move menu/key binding code from Bindings.py to EditorWindow.py,

with changed APIs -- it makes much more sense there.
Also add a new feature: if the first character of a menu label is
a '!', it gets a checkbox.  Checkboxes are bound to Boolean Tcl variables
that can be accessed through the new getvar/setvar/getrawvar API;
the variable is named after the event to which the menu is bound.
This commit is contained in:
Guido van Rossum 1999-01-28 22:02:47 +00:00
parent 85ef9dce9f
commit 07ec896707
3 changed files with 127 additions and 79 deletions

View File

@ -7,7 +7,6 @@
import sys import sys
import string import string
import re
from keydefs import * from keydefs import *
menudefs = [ menudefs = [
@ -42,9 +41,9 @@ menudefs = [
]), ]),
('debug', [ ('debug', [
('_Go to file/line', '<<goto-file-line>>'), ('_Go to file/line', '<<goto-file-line>>'),
('_Open stack viewer', '<<open-stack-viewer>>'), ('_Stack viewer', '<<open-stack-viewer>>'),
('_Debugger toggle', '<<toggle-debugger>>'), ('!_Debugger', '<<toggle-debugger>>'),
('_JIT Stack viewer toggle', '<<toggle-jit-stack-viewer>>' ), ('!_Auto-open stack viewer', '<<toggle-jit-stack-viewer>>' ),
]), ]),
('help', [ ('help', [
('_Help...', '<<help>>'), ('_Help...', '<<help>>'),
@ -53,62 +52,7 @@ menudefs = [
]), ]),
] ]
def prepstr(s):
# Helper to extract the underscore from a string,
# e.g. prepstr("Co_py") returns (2, "Copy").
i = string.find(s, '_')
if i >= 0:
s = s[:i] + s[i+1:]
return i, s
keynames = {
'bracketleft': '[',
'bracketright': ']',
'slash': '/',
}
def get_accelerator(keydefs, event):
keylist = keydefs.get(event)
if not keylist:
return ""
s = keylist[0]
s = re.sub(r"-[a-z]\b", lambda m: string.upper(m.group()), s)
s = re.sub(r"\b\w+\b", lambda m: keynames.get(m.group(), m.group()), s)
s = re.sub("Key-", "", s)
s = re.sub("Control-", "Ctrl-", s)
s = re.sub("-", "+", s)
s = re.sub("><", " ", s)
s = re.sub("<", "", s)
s = re.sub(">", "", s)
return s
if sys.platform == 'win32': if sys.platform == 'win32':
default_keydefs = windows_keydefs default_keydefs = windows_keydefs
else: else:
default_keydefs = unix_keydefs default_keydefs = unix_keydefs
def apply_bindings(text, keydefs=default_keydefs):
text.keydefs = keydefs
for event, keylist in keydefs.items():
if keylist:
apply(text.event_add, (event,) + tuple(keylist))
def fill_menus(text, menudict, defs=menudefs, keydefs=default_keydefs):
# Fill the menus for the given text widget. The menudict argument is
# a dictionary containing the menus, keyed by their lowercased name.
# Menus that are absent or None are ignored.
for mname, itemlist in defs:
menu = menudict.get(mname)
if not menu:
continue
for item in itemlist:
if not item:
menu.add_separator()
else:
label, event = item
underline, label = prepstr(label)
accelerator = get_accelerator(keydefs, event)
def command(text=text, event=event):
text.event_generate(event)
menu.add_command(label=label, underline=underline,
command=command, accelerator=accelerator)

View File

@ -1,6 +1,7 @@
import sys import sys
import os import os
import string import string
import re
import imp import imp
from Tkinter import * from Tkinter import *
import tkSimpleDialog import tkSimpleDialog
@ -93,7 +94,7 @@ class EditorWindow:
background="white", wrap="none") background="white", wrap="none")
self.createmenubar() self.createmenubar()
self.Bindings.apply_bindings(text) self.apply_bindings()
self.top.protocol("WM_DELETE_WINDOW", self.close) self.top.protocol("WM_DELETE_WINDOW", self.close)
self.top.bind("<<close-window>>", self.close_event) self.top.bind("<<close-window>>", self.close_event)
@ -172,15 +173,19 @@ class EditorWindow:
def createmenubar(self): def createmenubar(self):
mbar = self.menubar mbar = self.menubar
self.menudict = mdict = {} self.menudict = menudict = {}
for name, label in self.menu_specs: for name, label in self.menu_specs:
underline, label = self.Bindings.prepstr(label) underline, label = prepstr(label)
mdict[name] = menu = Menu(mbar, name=name) menudict[name] = menu = Menu(mbar, name=name)
mbar.add_cascade(label=label, menu=menu, underline=underline) mbar.add_cascade(label=label, menu=menu, underline=underline)
self.Bindings.fill_menus(self.text, mdict) self.fill_menus()
def postwindowsmenu(self): def postwindowsmenu(self):
# Only called when Windows menu exists # Only called when Windows menu exists
# XXX Actually, this Just-In-Time updating interferes
# XXX badly with the tear-off feature. It would be better
# XXX to update all Windows menus whenever the list of windows
# XXX changes.
menu = self.menudict['windows'] menu = self.menudict['windows']
end = menu.index("end") end = menu.index("end")
if end is None: if end is None:
@ -477,7 +482,7 @@ class EditorWindow:
if hasattr(ins, kdname): if hasattr(ins, kdname):
keydefs.update(getattr(ins, kdname)) keydefs.update(getattr(ins, kdname))
if keydefs: if keydefs:
self.Bindings.apply_bindings(self.text, keydefs) self.apply_bindings(keydefs)
for vevent in keydefs.keys(): for vevent in keydefs.keys():
methodname = string.replace(vevent, "-", "_") methodname = string.replace(vevent, "-", "_")
while methodname[:1] == '<': while methodname[:1] == '<':
@ -488,10 +493,104 @@ class EditorWindow:
if hasattr(ins, methodname): if hasattr(ins, methodname):
self.text.bind(vevent, getattr(ins, methodname)) self.text.bind(vevent, getattr(ins, methodname))
if hasattr(ins, "menudefs"): if hasattr(ins, "menudefs"):
self.Bindings.fill_menus(self.text, self. menudict, self.fill_menus(ins.menudefs, keydefs)
ins.menudefs, keydefs)
return ins return ins
def apply_bindings(self, keydefs=None):
if keydefs is None:
keydefs = self.Bindings.default_keydefs
text = self.text
text.keydefs = keydefs
for event, keylist in keydefs.items():
if keylist:
apply(text.event_add, (event,) + tuple(keylist))
def fill_menus(self, defs=None, keydefs=None):
# Fill the menus.
# Menus that are absent or None in self.menudict are ignored.
if defs is None:
defs = self.Bindings.menudefs
if keydefs is None:
keydefs = self.Bindings.default_keydefs
menudict = self.menudict
text = self.text
for mname, itemlist in defs:
menu = menudict.get(mname)
if not menu:
continue
for item in itemlist:
if not item:
menu.add_separator()
else:
label, event = item
checkbutton = (label[:1] == '!')
if checkbutton:
label = label[1:]
underline, label = prepstr(label)
accelerator = get_accelerator(keydefs, event)
def command(text=text, event=event):
text.event_generate(event)
if checkbutton:
var = self.getrawvar(event, BooleanVar)
menu.add_checkbutton(label=label, underline=underline,
command=command, accelerator=accelerator,
variable=var)
else:
menu.add_command(label=label, underline=underline,
command=command, accelerator=accelerator)
def getvar(self, name):
var = self.getrawvar(name)
if var:
return var.get()
def setvar(self, name, value, vartype=None):
var = self.getrawvar(name, vartype)
if var:
var.set(value)
def getrawvar(self, name, vartype=None):
key = ".VARS."
vars = self.menudict.get(key)
if not vars and vartype:
self.menudict[key] = vars = {}
if vars is not None:
var = vars.get(name)
if not var and vartype:
vars[name] = var = vartype(self.text)
return var
def prepstr(s):
# Helper to extract the underscore from a string,
# e.g. prepstr("Co_py") returns (2, "Copy").
i = string.find(s, '_')
if i >= 0:
s = s[:i] + s[i+1:]
return i, s
keynames = {
'bracketleft': '[',
'bracketright': ']',
'slash': '/',
}
def get_accelerator(keydefs, event):
keylist = keydefs.get(event)
if not keylist:
return ""
s = keylist[0]
s = re.sub(r"-[a-z]\b", lambda m: string.upper(m.group()), s)
s = re.sub(r"\b\w+\b", lambda m: keynames.get(m.group(), m.group()), s)
s = re.sub("Key-", "", s)
s = re.sub("Control-", "Ctrl-", s)
s = re.sub("-", "+", s)
s = re.sub("><", " ", s)
s = re.sub("<", "", s)
s = re.sub(">", "", s)
return s
def fixwordbreaks(root): def fixwordbreaks(root):
# Make sure that Tk's double-click and next/previous word # Make sure that Tk's double-click and next/previous word

View File

@ -217,11 +217,11 @@ class ModifiedInterpreter(InteractiveInterpreter):
raise raise
else: else:
self.showtraceback() self.showtraceback()
if self.tkconsole.jit_stack_view: if self.tkconsole.getvar("<<toggle-jit-stack-viewer>>"):
self.tkconsole.open_stack_viewer() self.tkconsole.open_stack_viewer()
except: except:
self.showtraceback() self.showtraceback()
if self.tkconsole.jit_stack_view: if self.tkconsole.getvar("<<toggle-jit-stack-viewer>>"):
self.tkconsole.open_stack_viewer() self.tkconsole.open_stack_viewer()
finally: finally:
@ -289,17 +289,20 @@ class PyShell(OutputWindow):
tkMessageBox.showerror("Don't debug now", tkMessageBox.showerror("Don't debug now",
"You can only toggle the debugger when idle", "You can only toggle the debugger when idle",
master=self.text) master=self.text)
self.set_debugger_indicator()
return "break" return "break"
db = self.interp.getdebugger()
if db:
self.close_debugger()
else: else:
self.open_debugger() db = self.interp.getdebugger()
if db:
jit_stack_view = 0 self.close_debugger()
else:
self.open_debugger()
def set_debugger_indicator(self):
db = self.interp.getdebugger()
self.setvar("<<toggle-debugger>>", not not db)
def toggle_jit_stack_viewer( self, event=None): def toggle_jit_stack_viewer( self, event=None):
self.jit_stack_view = not self.jit_stack_view pass # All we need is the variable
def close_debugger(self): def close_debugger(self):
db = self.interp.getdebugger() db = self.interp.getdebugger()
@ -310,12 +313,14 @@ class PyShell(OutputWindow):
self.console.write("[DEBUG OFF]\n") self.console.write("[DEBUG OFF]\n")
sys.ps1 = ">>> " sys.ps1 = ">>> "
self.showprompt() self.showprompt()
self.set_debugger_indicator()
def open_debugger(self): def open_debugger(self):
import Debugger import Debugger
self.interp.setdebugger(Debugger.Debugger(self)) self.interp.setdebugger(Debugger.Debugger(self))
sys.ps1 = "[DEBUG ON]\n>>> " sys.ps1 = "[DEBUG ON]\n>>> "
self.showprompt() self.showprompt()
self.set_debugger_indicator()
def beginexecuting(self): def beginexecuting(self):
# Helper for ModifiedInterpreter # Helper for ModifiedInterpreter
@ -335,8 +340,8 @@ class PyShell(OutputWindow):
if self.executing: if self.executing:
# XXX Need to ask a question here # XXX Need to ask a question here
if not tkMessageBox.askokcancel( if not tkMessageBox.askokcancel(
"Cancel?", "Kill?",
"The program is still running; do you want to cancel it?", "The program is still running; do you want to kill it?",
default="ok", default="ok",
master=self.text): master=self.text):
return "cancel" return "cancel"