leoKeys.py :  » Development » Leo » Leo-4.7.1-final » leo » core » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Development » Leo 
Leo » Leo 4.7.1 final » leo » core » leoKeys.py
#@+leo-ver=4-thin
#@+node:ekr.20061031131434:@thin leoKeys.py
"""Gui-independent keystroke handling for Leo.""" 

#@@language python
#@@tabwidth -4
#@@pagewidth 70

#@<< imports >>
#@+node:ekr.20061031131434.1:<< imports >>
import leo.core.leoGlobals as g
import leo.core.leoFrame as leoFrame # to access stringTextWidget class.

# This creates a circular dependency.
# We break this by doing the import in k.endCommand.
# import leo.core.leoEditCommands as leoEditCommands

import glob
import inspect
import os
import re
import string
import sys
import types
#@-node:ekr.20061031131434.1:<< imports >>
#@nl
#@<< about 'internal' bindings >>
#@+node:ekr.20061031131434.2:<< about 'internal' bindings >>
#@@nocolor
#@+at
# 
# Here are the rules for translating key bindings (in 
# leoSettings.leo) into keys for k.bindingsDict:
# 
# 1.  The case of plain letters is significant:  a is not A.
# 
# 2. The Shift- prefix can be applied *only* to letters. Leo will 
# ignore (with a
# warning) the shift prefix applied to any other binding, e.g., 
# Ctrl-Shift-(
# 
# 3. The case of letters prefixed by Ctrl-, Alt-, Key- or Shift- is 
# *not*
# significant. Thus, the Shift- prefix is required if you want an 
# upper-case
# letter (with the exception of 'bare' uppercase letters.)
# 
# The following table illustrates these rules. In each row, the 
# first entry is the
# key (for k.bindingsDict) and the other entries are equivalents 
# that the user may
# specify in leoSettings.leo:
# 
# a, Key-a, Key-A
# A, Shift-A
# Alt-a, Alt-A
# Alt-A, Alt-Shift-a, Alt-Shift-A
# Ctrl-a, Ctrl-A
# Ctrl-A, Ctrl-Shift-a, Ctrl-Shift-A
# !, Key-!,Key-exclam,exclam
# 
# This table is consistent with how Leo already works (because it is 
# consistent
# with Tk's key-event specifiers). It is also, I think, the least 
# confusing set of
# rules.
#@-at
#@nonl
#@-node:ekr.20061031131434.2:<< about 'internal' bindings >>
#@nl
#@<< about key dicts >>
#@+node:ekr.20061031131434.3:<< about key dicts >>
#@@nocolor
#@+at
# 
# ivars:
# 
# c.commandsDict:
#     Keys are emacs command names; values are functions f.
# 
# k.inverseCommandsDict:
#     Keys are f.__name__; values are emacs command names.
# 
# k.bindingsDict:
#     Keys are shortcuts; values are *lists* of 
# g.bunch(func,name,warningGiven)
# 
# k.masterBindingsDict:
#     Keys are scope names: 'all','text',etc. or mode names.
#     Values are dicts:  keys are strokes, values are 
# g.Bunch(commandName,func,pane,stroke)
# 
# k.masterGuiBindingsDict:
#     Keys are strokes; value is a list of widgets for which stroke 
# is bound.
# 
# k.settingsNameDict:
#     Keys are lowercase settings; values are 'real' Tk key 
# specifiers.
#     Important: this table has no inverse.
# 
# not an ivar (computed by k.computeInverseBindingDict):
# 
# inverseBindingDict
#     Keys are emacs command names; values are *lists* of shortcuts.
#@-at
#@-node:ekr.20061031131434.3:<< about key dicts >>
#@nl

#@+others
#@+node:ekr.20061031131434.4:class autoCompleterClass
class autoCompleterClass:

    '''A class that inserts autocompleted and calltip text in text widgets.
    This class shows alternatives in the tabbed log pane.

    The keyHandler class contains hooks to support these characters:
    invoke-autocompleter-character (default binding is '.')
    invoke-calltips-character (default binding is '(')
    '''

    #@    @+others
    #@+node:ekr.20061031131434.5: ctor (autocompleter)
    def __init__ (self,k):

        self.c = c = k.c
        self.k = k
        self.allClassesDict = {} # Will be completed after more classes exist.
        self.attrDictDict = {}  # Keys are languages (strings); values are anonymous attrDicts.
            # attrDicts: keys are strings; values are list of strings (attributes).
        self.calltips = {} # Keys are language, values are dicts: keys are ids, values are signatures.
        self.classScanner = self.classScannerClass(c)
        self.forgivingParser = self.forgivingParserClass(c)
        self.globalPythonFunctionsDict = {}
        self.language = None
        self.leadinWord = None
        self.membersList = None
        self.objectDict = {} # Created on first use of the autocompleter.
        self.selection = None # The selection range on entry to autocompleter or calltips.
        self.selectedText = None # The selected text on entry to autocompleter or calltips.
        self.selfClassName = None
        self.selfObjectsDict = {} # Keys are classNames, values are real proxy objects.
        self.selfVnodesDict = {} # Keys are tnodes, values are real proxy objects.
        self.prefix = None
        self.prevObjects = []
        self.tabList = []
        self.tabListIndex = -1
        self.tabName = None # The name of the main completion tab.
        self.theObject = None # The previously found object, for . chaining.
        self.trace = c.config.getBool('trace_autocompleter')
        self.useTabs = True # True: show results in autocompleter tab.
        self.verbose = False # True: print all members.
        self.watchwords = {} # Keys are ids, values are lists of ids that can follow a id dot.
        self.widget = None # The widget that should get focus after autocomplete is done.
    #@+node:ekr.20061031131434.6:defineClassesDict
    def defineClassesDict (self):

        self.allClassesDict = {}

        # gc may not exist.
        try: import gc
        except ImportError: return

        if g.isPython3:
            for z in gc.get_objects():
                try:
                    name = z.__class__.__name__
                except ReferenceError:
                    pass
                self.allClassesDict [name] = z
        else:
            for z in gc.get_objects():
                t = type(z)
                if t == types.ClassType:
                    name = z.__name__
                elif t == types.InstanceType:
                    name = z.__class__.__name__
                elif repr(t).startswith('<class'): # A wretched kludge.
                    name = z.__class__.__name__
                elif t == types.TypeType:
                    name = z.__name__
                else:
                    name = None
                if name:
                    # if name == 'position': g.trace(t,z)
                    self.allClassesDict [name] = z

        # g.printList(list(self.allClassesDict.keys()),tag='Classes',sort=True)
        # g.trace(len(list(self.allClassesDict.keys())))
        # g.trace('position:',self.allClassesDict.get('position'))
    #@-node:ekr.20061031131434.6:defineClassesDict
    #@+node:ekr.20061031131434.7:defineObjectDict
    def defineObjectDict (self,table=None):

        c = self.c ; k = c.k ; p = c.p

        # g.trace(g.callers(4))

        if table is None: table = [
            # Python globals...
            (['aList','bList'],     'python','list'),
            (['aString'],           'object','aString'), # An actual string object.
            (['cc'],                'object',c.chapterController),
            (['c','old_c','new_c'], 'object',c),            
            (['d','d1','d2'],       'python','dict'),
            (['f'],                 'object',c.frame), 
            (['g'],                 'object',g),       
            (['gui'],               'object',g.app.gui),
            (['k'],                 'object',k),
            (['p','p1','p2'],       'object',p.copy()), # 2009/12/21
            (['s','s1','s2','ch'],  'object','aString'),
            (['string'],            'object',string), # Python's string module.
            # (['t','t1','t2'],       'object',p.v.t),  
            (['v','v1','v2'],       'object',p.v),
            (['w','widget'],        'object',c.frame.body.bodyCtrl),
        ]

        if 0: # Not useful at this point.
            for key in __builtins__.keys():
                obj = __builtins__.get(key)
                if obj in (True,False,None): continue
                data = [key],'object',obj
                table.append(data)

        d = {'dict':{},'int':1,'list':[],'string':''}

        for idList,kind,nameOrObject in table:
            if kind == 'object':
                # Works, but hard to generalize for settings.
                obj = nameOrObject
            elif kind == 'python':
                className = nameOrObject
                o = d.get(className)
                obj = o is not None and o.__class__
            else:
                module = g.importModule (kind,verbose=True)
                if not module:
                    g.trace('Can not import ',nameOrObject)
                    continue
                self.appendToKnownObjects(module)
                if nameOrObject:
                    className = nameOrObject
                    obj = hasattr(module,className) and getattr(module,className) or None
                    if not obj:
                        g.trace('%s module has no class %s' % (kind,nameOrObject))
                    else:
                        self.appendToKnownObjects(getattr(module,className))
                else:
                    obj = module
            for z in idList:
                if obj:
                    self.objectDict[z]=obj
    #@-node:ekr.20061031131434.7:defineObjectDict
    #@-node:ekr.20061031131434.5: ctor (autocompleter)
    #@+node:ekr.20061031131434.8:Top level
    #@+node:ekr.20061031131434.9:autoComplete
    def autoComplete (self,event=None,force=False):

        '''An event handler called from k.masterKeyHanderlerHelper.'''

        c = self.c ; k = self.k ; gui = g.app.gui
        w = gui.eventWidget(event) or c.get_focus()

        # First, handle the invocation character as usual.
        k.masterCommand(event,func=None,stroke=None,commandName=None)

        # Don't allow autocompletion in headlines.
        if not c.widget_name(w).startswith('head'):
            self.language = g.scanForAtLanguage(c,c.p)
            if w and self.language == 'python' and (k.enable_autocompleter or force):
                self.start(event=event,w=w)

        return 'break'
    #@-node:ekr.20061031131434.9:autoComplete
    #@+node:ekr.20061031131434.10:autoCompleteForce
    def autoCompleteForce (self,event=None):

        '''Show autocompletion, even if autocompletion is not presently enabled.'''

        return self.autoComplete(event,force=True)
    #@-node:ekr.20061031131434.10:autoCompleteForce
    #@+node:ekr.20061031131434.11:autoCompleterStateHandler
    def autoCompleterStateHandler (self,event):

        trace = (False or self.trace) and not g.app.unitTesting
        c = self.c ; k = self.k ; gui = g.app.gui
        tag = 'auto-complete' ; state = k.getState(tag)
        ch = gui.eventChar(event)
        keysym = gui.eventKeysym(event)

        if trace: g.trace(repr(ch),repr(keysym),state)

        if state == 0:
            c.frame.log.clearTab(self.tabName)
            self.computeCompletionList()
            k.setState(tag,1,handler=self.autoCompleterStateHandler) 
        elif keysym in (' ','Return'):
            self.finish()
        elif keysym == 'Escape':
            self.abort()
        elif keysym == 'Tab':
            self.doTabCompletion()
        elif keysym in ('\b','BackSpace'): # Horrible hack for qt plugin.
            self.doBackSpace()
        elif keysym == '.':
            self.chain()
        elif keysym == '?':
            self.info()
        elif keysym == '!':
            # Toggle between verbose and brief listing.
            self.verbose = not self.verbose
            if type(self.theObject) == types.DictType:
                self.membersList = list(self.theObject.keys())
            elif type(self.theObject) in (type((),),type([])):
                self.membersList = self.theObject
            self.computeCompletionList(verbose=self.verbose)
        elif ch and ch in string.printable:
            self.insertNormalChar(ch,keysym)
        else:
            # if trace: g.trace('ignore',repr(ch))
            return 'do-standard-keys'
    #@-node:ekr.20061031131434.11:autoCompleterStateHandler
    #@+node:ekr.20080924032842.3:getExternalCompletions
    def getExternalCompletions (self,s,p=None,language='python'):

        '''Return the completions possible at the end of string 's'.
        Return (theObject,completions):
        - theObject is None unless the last character is 's' is a period.
        - completions is the list of valid completions.'''

        c = self.c ; k = c.k
        if not p: p = c.p

        # Use a separate widget containing just s.
        self.widget = w = leoFrame.stringTextWidget(c,'compute-completions-widget')
        w.setAllText(s)

        # A big kludge: scan back for the first period.
        i = len(s)-1
        while i > 0 and s[i] != '.':
            i -= 1
        if s[i] == '.': i += 1
        prefix = s[i:].strip()

        # Remember the prefix, but put the insert before the period.
        w.setSelectionRange(i, len(s)-1, insert=i)

        # Init the ivars...
        self.language = p and g.scanForAtLanguage(c,p) or language
        self.tabName = ''
        old_enable = c.k.enable_autocompleter

        # Get the completions.
        try:
            c.k.enable_autocompleter = True
            self.useTabs = False
            self.start(prefix=prefix)
        finally:
            c.k.enable_autocompleter = old_enable
            self.useTabs = True

        theObject,tabList = self.theObject,self.tabList
        self.abort()
        return theObject,tabList
    #@-node:ekr.20080924032842.3:getExternalCompletions
    #@+node:ekr.20061031131434.12:enable/disable/toggleAutocompleter/Calltips
    def disableAutocompleter (self,event=None):
        '''Disable the autocompleter.'''
        self.k.enable_autocompleter = False
        self.showAutocompleterStatus()

    def disableCalltips (self,event=None):
        '''Disable calltips.'''
        self.k.enable_calltips = False
        self.showCalltipsStatus()

    def enableAutocompleter (self,event=None):
        '''Enable the autocompleter.'''
        self.k.enable_autocompleter = True
        self.showAutocompleterStatus()

    def enableCalltips (self,event=None):
        '''Enable calltips.'''
        self.k.enable_calltips = True
        self.showCalltipsStatus()

    def toggleAutocompleter (self,event=None):
        '''Toggle whether the autocompleter is enabled.'''
        self.k.enable_autocompleter = not self.k.enable_autocompleter
        self.showAutocompleterStatus()

    def toggleCalltips (self,event=None):
        '''Toggle whether calltips are enabled.'''
        self.k.enable_calltips = not self.k.enable_calltips
        self.showCalltipsStatus()
    #@-node:ekr.20061031131434.12:enable/disable/toggleAutocompleter/Calltips
    #@+node:ekr.20061031131434.13:showCalltips
    def showCalltips (self,event=None,force=False):

        '''Show the calltips at the cursor.'''

        c = self.c ; k = c.k ; w = g.app.gui.eventWidget(event)
        if not w: return

        # Insert the calltip if possible, but not in headlines.
        if (k.enable_calltips or force) and not c.widget_name(w).startswith('head'):
            self.widget = w
            self.prefix = ''
            self.selection = w.getSelectionRange()
            self.selectedText = w.getSelectedText()
            self.leadinWord = self.findCalltipWord(w)
            # g.trace(self.leadinWord)
            self.theObject = None
            self.membersList = None
            self.calltip()
        else:
            # Just insert the invocation character as usual.
            k.masterCommand(event,func=None,stroke=None,commandName=None)

        return 'break'
    #@-node:ekr.20061031131434.13:showCalltips
    #@+node:ekr.20061031131434.14:showCalltipsForce
    def showCalltipsForce (self,event=None):

        '''Show the calltips at the cursor, even if calltips are not presently enabled.'''

        return self.showCalltips(event,force=True)
    #@-node:ekr.20061031131434.14:showCalltipsForce
    #@+node:ekr.20061031131434.15:showAutocompleter/CalltipsStatus
    def showAutocompleterStatus (self):
        '''Show the autocompleter status on the status line.'''

        k = self.k
        if not g.unitTesting:
            s = 'autocompleter %s' % g.choose(k.enable_autocompleter,'On','Off')
            g.es(s,color='red')

    def showCalltipsStatus (self):
        '''Show the autocompleter status on the status line.'''
        k = self.k
        if not g.unitTesting:
            s = 'calltips %s' % g.choose(k.enable_calltips,'On','Off')
            g.es(s,color='red')
    #@nonl
    #@-node:ekr.20061031131434.15:showAutocompleter/CalltipsStatus
    #@-node:ekr.20061031131434.8:Top level
    #@+node:ekr.20061031131434.16:Helpers
    #@+node:ekr.20061031131434.17:.abort & exit (autocompleter) (test)
    def abort (self):

        k = self.k
        k.keyboardQuit(event=None,setDefaultStatus=False)
            # Stay in the present input state.
        self.exit(restore=True)

    def exit (self,restore=False): # Called from keyboard-quit.

        k = self ; c = self.c 
        w = self.widget or c.frame.body.bodyCtrl
        for name in (self.tabName,'Modules','Info'):
            c.frame.log.deleteTab(name)
        c.widgetWantsFocusNow(w)
        i,j = w.getSelectionRange()
        if restore:
            if i != j: w.delete(i,j)
            w.insert(i,self.selectedText)
        w.setSelectionRange(j,j,insert=j)

        self.clear()
        self.theObject = None
    #@-node:ekr.20061031131434.17:.abort & exit (autocompleter) (test)
    #@+node:ekr.20061031131434.18:append/begin/popTabName
    def appendTabName (self,word):

        self.setTabName(self.tabName + word + '.')

    def beginTabName (self,word):

        # g.trace(word,g.callers())
        if word == 'self' and self.selfClassName:
            word = '%s (%s)' % (word,self.selfClassName)
        self.setTabName('AutoComplete ' + word + '.')

    def clearTabName (self):

        self.setTabName('AutoComplete ')

    def popTabName (self):

        s = self.tabName
        i = s.rfind('.',0,-1)
        if i > -1:
            self.setTabName(s[0:i])

    # Underscores are not valid in Pmw tab names!
    def setTabName (self,s):

        c = self.c
        if self.useTabs:
            if self.tabName:
                c.frame.log.deleteTab(self.tabName)
            self.tabName = s.replace('_','') or ''
            c.frame.log.clearTab(self.tabName)
    #@-node:ekr.20061031131434.18:append/begin/popTabName
    #@+node:ekr.20061031131434.19:appendToKnownObjects
    def appendToKnownObjects (self,obj):

        if 0:
            if type(obj) in (types.InstanceType,types.ModuleType,types):
                if hasattr(obj,'__name__'):
                    self.knownObjects[obj.__name__] = obj
                    # g.trace('adding',obj.__name__)
    #@-node:ekr.20061031131434.19:appendToKnownObjects
    #@+node:ekr.20061031131434.20:calltip
    def calltip (self,obj=None):

        c = self.c
        w = self.widget
        isStringMethod = False ; s = None
        # g.trace(self.leadinWord,obj)

        if self.leadinWord and (not obj or type(obj) == types.BuiltinFunctionType):
            #@        << try to set s from a Python global function >>
            #@+node:ekr.20061031131434.21:<< try to set s from a Python global function >>
            # The first line of the docstring is good enough, except for classes.
            f = __builtins__.get(self.leadinWord)
            doc = f and type(f) != types.ClassType and f.__doc__
            if doc:
                # g.trace(doc)
                s = g.splitLines(doc)
                s = args = s and s [0] or ''
                i = s.find('(')
                if i > -1: s = s [i:]
                else: s = '(' + s
                s = s and s.strip() or ''
            #@-node:ekr.20061031131434.21:<< try to set s from a Python global function >>
            #@nl

        if not s:
            #@        << get s using inspect >>
            #@+node:ekr.20061031131434.22:<< get s using inspect >>
            isStringMethod = (
                self.prevObjects and
                # type(self.prevObjects[-1]) == types.StringType
                g.isString(self.prevObjects[-1]))

            # g.trace(self.prevObjects)

            if isStringMethod and hasattr(string,obj.__name__):
                # A hack. String functions are builtins, and getargspec doesn't handle them.
                # Get the corresponding string function instead, and remove the s arg later.
                obj = getattr(string,obj.__name__)

            try:
                s1,s2,s3,s4 = inspect.getargspec(obj)
            except:
                self.extendSelection('(')
                self.finish()
                return # Not a function.  Just '('.

            s = args = inspect.formatargspec(s1,s2,s3,s4)
            #@-node:ekr.20061031131434.22:<< get s using inspect >>
            #@nl

        #@    << remove 'self' from s, but not from args >>
        #@+node:ekr.20061031131434.23:<< remove 'self' from s, but not from args >>
        if g.match(s,1,'self,'):
            s = s[0] + s[6:].strip()
        elif g.match_word(s,1,'self'):
            s = s[0] + s[5:].strip()
        #@-node:ekr.20061031131434.23:<< remove 'self' from s, but not from args >>
        #@nl
        if isStringMethod:
            #@        << remove 's' from s *and* args >>
            #@+node:ekr.20061031131434.24:<< remove 's' from s *and* args >>
            if g.match(s,1,'s,'):
                s = s[0] + s[3:]
                args = args[0] + args[3:]
            elif g.match_word(s,1,'s'):
                s = s[0] + s[2:]
                args = args[0] + args[2:]
            #@-node:ekr.20061031131434.24:<< remove 's' from s *and* args >>
            #@nl

        # s = s.rstrip(')') # Not so convenient.
        #@    << insert the text and set j1 and j2 >>
        #@+node:ekr.20061031131434.25:<< insert the text and set j1 and j2 >>
        junk,j = w.getSelectionRange() # Returns insert point if no selection.
        w.insert(j,s)
        c.frame.body.onBodyChanged('Typing')
        j1 = j + 1 ; j2 = j + len(s)
        #@-node:ekr.20061031131434.25:<< insert the text and set j1 and j2 >>
        #@nl

        # End autocompletion mode, putting the insertion point after the suggested calltip.
        self.finish()
        c.widgetWantsFocusNow(w)
        if 1: # Seems to be more useful.
            w.setSelectionRange(j1,j2,insert=j2)
        else:
            w.setInsertPoint(j2)
        #@    << put the status line >>
        #@+node:ekr.20061031131434.26:<< put the status line >>
        c.frame.clearStatusLine()
        if obj:
            name = hasattr(obj,'__name__') and obj.__name__ or repr(obj)
        else:
            name = self.leadinWord
        c.frame.putStatusLine('%s %s' % (name,args))
        #@-node:ekr.20061031131434.26:<< put the status line >>
        #@nl
    #@-node:ekr.20061031131434.20:calltip
    #@+node:ekr.20061031131434.27:chain
    def chain (self):

        c = self.c ; w = self.widget
        word = w.getSelectedText()
        old_obj = self.theObject

        if word and old_obj and type(old_obj) == type([]) and old_obj == sys.modules:
            obj = old_obj.get(word)
            if obj:
                self.theObject = obj
                self.clearTabName()
        elif word and old_obj and self.hasAttr(old_obj,word):
            self.push(old_obj)
            self.theObject = obj = self.getAttr(old_obj,word)
        else: obj = None

        if obj:
            self.appendToKnownObjects(obj)
            self.leadinWord = word
            self.membersList = self.getMembersList(obj)
            self.appendTabName(word)
            self.extendSelection('.')
            i = w.getInsertPoint()
            w.setSelectionRange(i,i,insert=i)
            # g.trace('chaining to',word,self.theObject)
            # Similar to start logic.
            self.prefix = ''
            self.selection = w.getSelectionRange()
            self.selectedText = w.getSelectedText()
            if self.membersList:
                # self.autoCompleterStateHandler(event=None)
                self.computeCompletionList()
                return
        self.extendSelection('.')
        self.finish()
    #@-node:ekr.20061031131434.27:chain
    #@+node:ekr.20061031131434.28:computeCompletionList
    def computeCompletionList (self,verbose=False):

        c = self.c ; w = self.widget
        c.widgetWantsFocus(w)
        s = w.getSelectedText()
        self.tabList,common_prefix = g.itemsMatchingPrefixInList(
            s,self.membersList,matchEmptyPrefix=True)

        if not common_prefix:
            if verbose or len(self.tabList) < 25 or not self.useTabs:
                self.tabList,common_prefix = g.itemsMatchingPrefixInList(
                    s,self.membersList,matchEmptyPrefix=True)
            else: # Show the possible starting letters.
                d = {}
                for z in self.tabList:
                    ch = z and z[0] or ''
                    if ch:
                        n = d.get(ch,0)
                        d[ch] = n + 1
                aList = [ch+'...%d' % (d.get(ch)) for ch in sorted(d)]
                self.tabList = aList

        if self.useTabs:
            c.frame.log.clearTab(self.tabName) # Creates the tab if necessary.
            if self.tabList:
                self.tabListIndex = -1 # The next item will be item 0.
                self.setSelection(common_prefix)
            g.es('','\n'.join(self.tabList),tabName=self.tabName)
    #@-node:ekr.20061031131434.28:computeCompletionList
    #@+node:ekr.20061031131434.29:doBackSpace (autocompleter)
    def doBackSpace (self):

        '''Cut back to previous prefix.'''

        trace = False and not g.unitTesting
        if trace: g.trace('(autocompleter)',
            self.prefix,self.theObject,self.prevObjects)

        c = self.c
        if self.prefix:
            self.prefix = self.prefix[:-1]
            self.setSelection(self.prefix)
            self.computeCompletionList()
        elif self.theObject:
            if self.prevObjects:
                obj = self.pop()
            else:
                obj = self.theObject
            # g.trace(self.theObject,obj)
            w = self.widget
            s = w.getAllText()
            i,junk = w.getSelectionRange()
            ch = 0 <= i-1 < len(s) and s[i-1] or ''
            # g.trace(ch)
            if ch == '.':
                self.theObject = obj
                w.delete(i-1)
                c.frame.body.onBodyChanged(undoType='Typing')
                i,j = g.getWord(s,i-2)
                word = s[i:j]
                # g.trace(i,j,repr(word))
                w.setSelectionRange(i,j,insert=j)
                self.prefix = word
                self.popTabName()
                self.membersList = self.getMembersList(obj)
                # g.trace(len(self.membersList))
                if self.membersList:
                    self.computeCompletionList()
                else:
                    self.abort()
            else:
                self.abort() # should not happen.
        else:
            self.abort()            
    #@-node:ekr.20061031131434.29:doBackSpace (autocompleter)
    #@+node:ekr.20061031131434.30:doTabCompletion (autocompleter)
    def doTabCompletion (self):

        '''Handle tab completion when the user hits a tab.'''

        c = self.c ; w = self.widget
        s = w.getSelectedText()

        if s.startswith(self.prefix) and self.tabList:
            # g.trace('cycle','prefix',repr(self.prefix),len(self.tabList),repr(s))
            # Set the label to the next item on the tab list.
            self.tabListIndex +=1
            if self.tabListIndex >= len(self.tabList):
               self.tabListIndex = 0
            self.setSelection(self.tabList[self.tabListIndex])
        else:
            self.computeCompletionList()

        c.widgetWantsFocusNow(w)
    #@-node:ekr.20061031131434.30:doTabCompletion (autocompleter)
    #@+node:ekr.20061031131434.31:extendSelection
    def extendSelection (self,s):

        '''Append s to the presently selected text.'''

        c = self.c ; w = self.widget
        c.widgetWantsFocusNow(w)

        i,j = w.getSelectionRange()
        w.insert(j,s)
        j += 1
        w.setSelectionRange(i,j,insert=j)
        c.frame.body.onBodyChanged('Typing')
    #@nonl
    #@-node:ekr.20061031131434.31:extendSelection
    #@+node:ekr.20061031131434.33:findCalltipWord
    def findCalltipWord (self,w):

        i = w.getInsertPoint()
        s = w.getAllText()
        if i > 0:
            i,j = g.getWord(s,i-1)
            word = s[i:j]
            return word
        else:
            return ''
    #@nonl
    #@-node:ekr.20061031131434.33:findCalltipWord
    #@+node:ekr.20080924032842.5:findAnchor
    def findAnchor (self,w):

        '''Returns (j,word) where j is a Python index.'''

        i = j = w.getInsertPoint()
        s = w.getAllText()

        # New in Leo 4.5: Fix a hanger by stopping when i <= 1.
        while i > 1 and s[i-1] == '.':
            i,j = g.getWord(s,i-2)

        word = s[i:j]
        if word == '.': word = None

        # g.trace(i,j,repr(word))
        return j,word
    #@nonl
    #@-node:ekr.20080924032842.5:findAnchor
    #@+node:ekr.20061031131434.34:finish
    def finish (self):

        c = self.c ; k = c.k

        k.keyboardQuit(event=None,setDefaultStatus=False)
            # Stay in the present input state.

        for name in (self.tabName,'Modules','Info'):
            c.frame.log.deleteTab(name)

        c.frame.body.onBodyChanged('Typing')
        c.recolor()
        self.clear()
        self.theObject = None
    #@-node:ekr.20061031131434.34:finish
    #@+node:ekr.20061031131434.35:getAttr and hasAttr
    # The values of self.attrDictDic are anonymous attrDict's.
    # attrDicts: keys are strings, values are lists of strings.

    def getAttr (self,obj,attr):

        '''Simulate getattr function, regardless of langauge.'''

        if self.language == 'python':
            return getattr(obj,attr)
        else:
            d = self.attrDictDict.get(self.language)
            aList = d.get(obj,[])
            return attr in aList and attr

    def hasAttr (self,obj,attr):

        '''Simulate hasattr function, regardless of langauge.'''

        if self.language == 'python':
            return hasattr(obj,attr)
        else:
            d = self.attrDictDict.get(self.language)
            aList = d.get(obj,[])
            return attr in aList
    #@-node:ekr.20061031131434.35:getAttr and hasAttr
    #@+node:ekr.20061031131434.36:getLeadinWord
    def getLeadinWord (self,w):

        self.verbose = False # User must explicitly ask for verbose.
        self.leadinWord = None
        start = w.getInsertPoint()
        s = w.getAllText()
        start -= 1
        i,word = self.findAnchor(w)

        if word and word.isdigit():
            self.membersList = []
            return False

        self.setObjectAndMembersList(word)
        # g.trace(word,self.theObject,len(self.membersList))

        if not word:
            self.membersList = []
            return False
        elif not self.theObject:
            self.membersList = []
            return False
        else:
            self.beginTabName(word)
            while 0 <= i < start and i <len(s):
                if s[i] != '.':
                    return False
                i,j = g.getWord(s,i+1)
                word = s[i:j]
                # g.trace(word,i,j,start)
                self.setObjectAndMembersList(word)
                if not self.theObject:
                    # g.trace('unknown',word)
                    return False
                self.appendTabName(word)
                i = j
            self.leadinWord = word
            return True
    #@-node:ekr.20061031131434.36:getLeadinWord
    #@+node:ekr.20061031131434.37:getMembersList
    def getMembersList (self,obj):

        '''Return a list of possible autocompletions for self.leadinWord.'''

        if obj:
            aList = inspect.getmembers(obj)
            members = ['%s:%s' % (a,g.prettyPrintType(b))
                for a,b in aList if not a.startswith('__')]
            members.sort()
            return members
        else:
            return []
    #@-node:ekr.20061031131434.37:getMembersList
    #@+node:ekr.20061031131434.38:info
    def info (self):

        c = self.c ; doc = None ; obj = self.theObject ; w = self.widget

        word = w.getSelectedText()

        if not word:
            # Never gets called, but __builtin__.f will work.
            word = self.findCalltipWord(w)
            if word:
                # Try to get the docstring for the Python global.
                f = __builtins__.get(self.leadinWord)
                doc = f and f.__doc__

        if not doc:
            if not self.hasAttr(obj,word):
                g.es('no docstring for',word,color='blue')
                return
            obj = self.getAttr(obj,word)
            doc = inspect.getdoc(obj)

        if doc:
            c.frame.log.clearTab('Info',wrap='word')
            g.es('',doc,tabName='Info')
        else:
            g.es('no docstring for',word,color='blue')
    #@-node:ekr.20061031131434.38:info
    #@+node:ekr.20061031131434.39:insertNormalChar
    def insertNormalChar (self,ch,keysym):

        k = self.k ; w = self.widget

        if g.isWordChar(ch):
            # Look ahead to see if the character completes any item.
            s = w.getSelectedText() + ch
            tabList,common_prefix = g.itemsMatchingPrefixInList(
                s,self.membersList,matchEmptyPrefix=True)
            if tabList:
                # Add the character.
                self.tabList = tabList
                self.extendSelection(ch)
                s = w.getSelectedText()
                if s.startswith(self.prefix):
                    self.prefix = self.prefix + ch
                self.computeCompletionList()
        else:
            word = w.getSelectedText()
            if ch == '(':
                # Similar to chain logic.
                obj = self.theObject
                # g.trace(obj,word,self.hasAttr(obj,word))
                if self.hasAttr(obj,word):
                    obj = self.getAttr(obj,word)
                    self.push(self.theObject)
                    self.theObject = obj
                    self.leadinWord = word
                    self.membersList = self.getMembersList(obj)
                    if k.enable_calltips:
                        # This calls self.finish if the '(' is valid.
                        self.calltip(obj)
                        return
            self.extendSelection(ch)
            self.finish()
    #@-node:ekr.20061031131434.39:insertNormalChar
    #@+node:ekr.20061031131434.40:push, pop, clear, stackNames
    def push (self,obj):

        if obj is not None:
            self.prevObjects.append(obj)
            # g.trace(self.stackNames())

    def pop (self):

        obj = self.prevObjects.pop()
        # g.trace(obj)
        return obj

    def clear (self):

        self.prevObjects = []
        # g.trace(g.callers())

    def stackNames (self):

        aList = []
        for z in self.prevObjects:
            if hasattr(z,'__name__'):
                aList.append(z.__name__)
            elif hasattr(z,'__class__'):
                aList.append(z.__class__.__name__)
            else:
                aList.append(str(z))
        return aList
    #@-node:ekr.20061031131434.40:push, pop, clear, stackNames
    #@+node:ekr.20061031131434.41:setObjectAndMembersList & helpers
    def setObjectAndMembersList (self,word):

        c = self.c

        if not word:
            # Leading dot shows all classes.
            self.leadinWord = None
            self.theObject = sys.modules
            self.membersList = list(sys.modules.keys())
            self.beginTabName('Modules')
        elif word in ( "'",'"'):
            word = 'aString' # This is in the objectsDict.
            self.clear()
            self.push(self.theObject)
            self.theObject = 'aString'
            self.membersList = self.getMembersList(self.theObject)
        elif self.theObject:
            self.getObjectFromAttribute(word)
        # elif word == 'self':
            # self.completeSelf()
        else:
            obj = self.objectDict.get(word) or sys.modules.get(word)
            self.completeFromObject(obj)

        # g.trace(word,self.theObject,len(self.membersList))
    #@+node:ekr.20061031131434.42:getObjectFromAttribute
    def getObjectFromAttribute (self,word):

        obj = self.theObject

        if obj and self.hasAttr(obj,word):
            self.push(self.theObject)
            self.theObject = self.getAttr(obj,word)
            self.appendToKnownObjects(self.theObject)
            self.membersList = self.getMembersList(self.theObject)
        else:
            # No special support for 'self' here.
            # Don't clear the stack here!
            self.membersList = []
            self.theObject = None
    #@-node:ekr.20061031131434.42:getObjectFromAttribute
    #@+node:ekr.20061031131434.43:completeSelf
    def completeSelf (self):

        # This scan will be fast if an instant object already exists.
        className,obj,p,s = self.classScanner.scan()
        # g.trace(className,obj,p,s and len(s))

        # First, look up the className.
        if not obj and className:
            obj = self.allClassesDict.get(className)
            # if obj: g.trace('found in allClassesDict: %s = %s' % (className,obj))

        # Second, create the object from class definition.
        if not obj and s:
            theClass = self.computeClassObjectFromString(className,s)
            if theClass:
                obj = self.createProxyObjectFromClass(className,theClass)
                if obj:
                    self.selfObjectsDict [className] = obj
                    # This prevents future rescanning, even if the node moves.
                    self.selfVnodesDict [p.v] = obj
        if obj:
            self.selfClassName = className
            self.push(self.theObject)
            self.theObject = obj
            self.membersList = self.getMembersList(obj=obj)
        else:
            # No further action possible or desirable.
            self.selfClassName = None
            self.theObject = None
            self.clear()
            self.membersList = []
    #@-node:ekr.20061031131434.43:completeSelf
    #@+node:ekr.20061031131434.44:completeFromObject
    def completeFromObject (self,obj):

        if obj:
            self.appendToKnownObjects(obj)
            self.push(self.theObject)
            self.theObject = obj
            self.membersList = self.getMembersList(obj=obj)
        else:
            self.theObject = None
            self.clear()
            self.membersList = []
    #@-node:ekr.20061031131434.44:completeFromObject
    #@-node:ekr.20061031131434.41:setObjectAndMembersList & helpers
    #@+node:ekr.20061031131434.45:setSelection
    def setSelection (self,s):

        c = self.c ; w = self.widget
        c.widgetWantsFocusNow(w)

        if w.hasSelection():
            i,j = w.getSelectionRange()
            w.delete(i,j)
        else:
            i = w.getInsertPoint()

        # Don't go past the ':' that separates the completion from the type.
        n = s.find(':')
        if n > -1: s = s[:n]

        w.insert(i,s)
        j = i + len(s)
        w.setSelectionRange(i,j,insert=j)

        # New in Leo 4.4.2: recolor immediately to preserve the new selection in the new colorizer.
        c.frame.body.recolor(c.p,incremental=True)
        # Usually this call will have no effect because the body text has not changed.
        c.frame.body.onBodyChanged('Typing')
    #@-node:ekr.20061031131434.45:setSelection
    #@+node:ekr.20061031131434.46:start
    def start (self,event=None,w=None,prefix=None):

        c = self.c
        if w: self.widget = w
        else: w = self.widget

        # We wait until now to define these dicts so that more classes and objects will exist.
        if not self.objectDict:
            self.defineClassesDict()
            self.defineObjectDict()

        self.prefix = g.choose(prefix is None,'',prefix)
        self.selection = w.getSelectionRange()
        self.selectedText = w.getSelectedText()
        flag = self.getLeadinWord(w)
        if self.membersList:
            if not flag:
                # Remove the (leading) invocation character.
                i = w.getInsertPoint()
                s = w.getAllText()
                if i > 0 and s[i-1] == '.':
                    s = g.app.gui.stringDelete(s,i-1)
                    w.setAllText(s)
                    c.frame.body.onBodyChanged('Typing')
            if self.useTabs:
                self.autoCompleterStateHandler(event)
            else:
                self.computeCompletionList()
                return self.tabList
        else:
            self.abort()
    #@-node:ekr.20061031131434.46:start
    #@-node:ekr.20061031131434.16:Helpers
    #@+node:ekr.20061031131434.47:Scanning (not used)
    if 0: # Not used at present.
        #@    @+others
        #@+node:ekr.20061031131434.48:initialScan
        # Don't call this finishCreate: the startup logic would call it too soon.

        def initialScan (self):

            g.trace(g.callers())

            self.scan(thread=True)
        #@-node:ekr.20061031131434.48:initialScan
        #@+node:ekr.20061031131434.49:scan
        def scan (self,event=None,verbose=True,thread=True):

            c = self.c
            if not c or not c.exists or c.frame.isNullFrame: return
            if g.app.unitTesting: return

            # g.trace('autocompleter')

            if 0: # thread:
                # Use a thread to do the initial scan so as not to interfere with the user.            
                def scan ():
                    #g.es("This is for testing if g.es blocks in a thread", color = 'pink' )
                    # During unit testing c gets destroyed before the scan finishes.
                    if not g.app.unitTesting:
                        self.scanOutline(verbose=True)

                t = threading.Thread(target=scan)
                t.setDaemon(True)
                t.start()
            else:
                self.scanOutline(verbose=verbose)
        #@-node:ekr.20061031131434.49:scan
        #@+node:ekr.20061031131434.50:definePatterns
        def definePatterns (self):

            self.space = r'[ \t\r\f\v ]+' # one or more whitespace characters.
            self.end = r'\w+\s*\([^)]*\)' # word (\w) ws ( any ) (can cross lines)

            # Define re patterns for various languages.
            # These patterns match method/function definitions.
            self.pats = {}
            self.pats ['python'] = re.compile(r'def\s+%s' % self.end)  # def ws word ( any ) # Can cross line boundaries.
            self.pats ['java'] = re.compile(
                r'((public\s+|private\s+|protected\s+)?(static%s|\w+%s){1,2}%s)' % (
                    self.space,self.space,self.end))
            self.pats ['perl'] = re.compile(r'sub\s+%s' % self.end)
            self.pats ['c++'] = re.compile(r'((virtual\s+)?\w+%s%s)' % (self.space,self.end))
            self.pats ['c'] = re.compile(r'\w+%s%s' % (self.space,self.end))

            # Define self.okchars for getCleaString.
            okchars = {}
            for z in string.ascii_letters:
                okchars [z] = z
            okchars ['_'] = '_'
            self.okchars = okchars 
        #@nonl
        #@-node:ekr.20061031131434.50:definePatterns
        #@+node:ekr.20061031131434.51:scanOutline
        def scanOutline (self,verbose=True):

            '''Traverse an outline and build the autocommander database.'''

            if verbose: g.es_print('scanning for auto-completer...')

            c = self.c ; k = self.k ; count = 0
            for p in c.all_positions():
                if verbose:
                    count += 1 ;
                    if (count % 200) == 0: g.es('','.',newline=False)
                language = g.scanForAtLanguage(c,p)
                # g.trace('language',language,p.h)
                s = p.b
                if k.enable_autocompleter:
                    self.scanForAutoCompleter(s)
                if k.enable_calltips:
                    self.scanForCallTip(s,language)

            if 0:
                g.trace('watchwords...\n\n')
                for key in sorted(self.watchwords):
                    aList = self.watchwords.get(key)
                    g.trace('%s:\n\n' % (key), g.listToString(aList))
            if 0:
                g.trace('calltips...\n\n')
                for key in sorted(self.calltips):
                    d = self.calltips.get(key)
                    if d:
                        g.trace('%s:\n\n' % (key), g.dictToString(d))

            if verbose:        
                g.es_print('\nauto-completer scan complete',color='blue')
        #@-node:ekr.20061031131434.51:scanOutline
        #@+node:ekr.20061031131434.52:scanForCallTip
        def scanForCallTip (self,s,language):

            '''this function scans text for calltip info'''

            d = self.calltips.get(language,{})
            pat = self.pats.get(language or 'python')

            # Set results to a list of all the function/method defintions in s.
            results = pat and pat.findall(s) or []

            for z in results:
                if isinstance(z,tuple): z = z [0]
                pieces2 = z.split('(')
                # g.trace(pieces2)
                pieces2 [0] = pieces2 [0].split() [-1]
                a, junk = pieces2 [0], pieces2 [1]
                aList = d.get(a,[])
                if str(z) not in aList:
                    aList.append(str(z))
                    d [a] = aList

            self.calltips [language] = d
        #@-node:ekr.20061031131434.52:scanForCallTip
        #@+node:ekr.20061031131434.53:scanForAutoCompleter
        def scanForAutoCompleter (self,s):

            '''This function scans text for the autocompleter database.'''

            aList = [] ; t1 = s.split('.')

            if 1: # Slightly faster.
                t1 = s.split('.') ; 
                i = 0 ; n = len(t1)-1
                while i < n:
                    self.makeAutocompletionList(t1[i],t1[i+1],aList)
                    i += 1
            else:
                if g.isPython3:
                    from functools import reduce
                reduce(lambda a,b: self.makeAutocompletionList(a,b,aList),t1)

            if aList:
                for a, b in aList:
                    z = self.watchwords.get(a,[])
                    if str(b) not in z:
                        z.append(str(b))
                        self.watchwords [a] = z
        #@+node:ekr.20061031131434.54:makeAutocompletionList
        def makeAutocompletionList (self,a,b,glist):

            '''We have seen a.b, where a and b are arbitrary strings.
            Append (a1.b1) to glist.
            To compute a1, scan backwards in a until finding whitespace.
            To compute b1, scan forwards in b until finding a char not in okchars.
            '''

            if 1: # Do everything inline.  It's a few percent faster.

                # Compute reverseFindWhitespace inline.
                i = len(a) -1
                while i >= 0:
                    if a[i].isspace() or a [i] == '.':
                        a1 = a [i+1:] ; break
                    i -= 1
                else:
                    a1 = a

                # Compute getCleanString inline.
                i = 0
                for ch in b:
                    if ch not in self.okchars:
                        b1 = b[:i] ; break
                    i += 1
                else:
                    b1 = b

                if b1:
                    glist.append((a1,b1),)

                return b # Not needed unless we are using reduce.
            else:
                a1 = self.reverseFindWhitespace(a)
                if a1:
                    b1 = self.getCleanString(b)
                    if b1:
                        glist.append((a1,b1))
                return b
        #@+node:ekr.20061031131434.55:reverseFindWhitespace
        def reverseFindWhitespace (self,s):

            '''Return the longest tail of s containing no whitespace or period.'''

            i = len(s) -1
            while i >= 0:
                if s[i].isspace() or s [i] == '.': return s [i+1:]
                i -= 1

            return s
        #@-node:ekr.20061031131434.55:reverseFindWhitespace
        #@+node:ekr.20061031131434.56:getCleanString
        def getCleanString (self,s):

            '''Return the prefix of s containing only chars in okchars.'''

            i = 0
            for ch in s:
                if ch not in self.okchars:
                    return s[:i]
                i += 1

            return s
        #@-node:ekr.20061031131434.56:getCleanString
        #@-node:ekr.20061031131434.54:makeAutocompletionList
        #@-node:ekr.20061031131434.53:scanForAutoCompleter
        #@-others
    #@-node:ekr.20061031131434.47:Scanning (not used)
    #@+node:ekr.20061031131434.57:Proxy classes and objects
    #@+node:ekr.20061031131434.58:createProxyObjectFromClass
    def createProxyObjectFromClass (self,className,theClass):

        '''Create a dummy instance object by instantiating theClass with a dummy ctor.'''

        if 0: # Calling the real ctor is way too dangerous.
            # Set args to the list of required arguments.
            args = inspect.getargs(theClass.__init__.im_func.func_code)
            args = args[0] ; n = len(args)-1
            args = [None for z in range(n)]

        def dummyCtor (self):
            pass

        try:
            obj = None
            old_init = hasattr(theClass,'__init__') and theClass.__init__
            theClass.__init__ = dummyCtor
            obj = theClass()
        finally:
            if old_init:
                theClass.__init__ = old_init
            else:
                delattr(theClass,'__init__')

        g.trace(type(theClass),obj)

        # Verify that it has all the proper attributes.
        # g.trace(g.listToString(dir(obj)))
        return obj
    #@-node:ekr.20061031131434.58:createProxyObjectFromClass
    #@+node:ekr.20061031131434.59:createClassObjectFromString
    def computeClassObjectFromString (self,className,s):

        try:
            # Add the the class definition to the present environment.
            exec(s) # Security violation!

            # Get the newly created object from the locals dict.
            theClass = locals().get(className)
            return theClass

        except Exception:
            if 1: # Could be a weird kind of user error.
                g.es_print('unexpected exception in',computeProxyObject)
                g.es_exception()
            return None
    #@-node:ekr.20061031131434.59:createClassObjectFromString
    #@-node:ekr.20061031131434.57:Proxy classes and objects
    #@+node:ekr.20061031131434.60:class forgivingParserClass
    class forgivingParserClass:

        '''A class to create a valid class instances from  import 
        a class definition that may contain syntax errors.'''

        #@    @+others
        #@+node:ekr.20061031131434.61:ctor (forgivingParserClass)
        def __init__ (self,c):

            self.c = c
            self.excludedTnodesList = []
            self.old_putBody = None # Set in parse for communication with newPutBody.
        #@-node:ekr.20061031131434.61:ctor (forgivingParserClass)
        #@+node:ekr.20061031131434.62:parse
        def parse (self,p):

            '''The top-level parser method.

            It patches c.atFileCommands.putBody, calls the forgiving parser and finally
            restores c.atFileCommands.putBody.'''

            c = self.c

            # Create an ivar for communication with newPutBody.
            self.old_putBody = c.atFileCommands.putBody

            # Override atFile.putBody.
            c.atFileCommands.putBody = self.newPutBody

            try:
                s = None
                s = self.forgivingParser(p)
            finally:
                c.atFileCommands.putBody = self.old_putBody

            return s # Don't put a return in a finally clause.


        #@-node:ekr.20061031131434.62:parse
        #@+node:ekr.20061031131434.63:forgivingParser (leoKeys)
        def forgivingParser (self,p,suppress=False):

            c = self.c ; root = p.copy()
            self.excludedTnodesList = []
            s = g.getScript(c,root,useSelectedText=False)
            while s:
                try:
                    if not g.isPython3:
                        s = g.toEncodedString(s)
                    compile(s+'\n','<string>','exec')
                    break
                except SyntaxError:
                    fileName, n = g.getLastTracebackFileAndLineNumber()
                    p = self.computeErrorNode(c,root,n,lines=g.splitLines(s))
                    if not p or p == root:
                        if not suppress:
                            g.es_print('syntax error in class node: can not continue')
                        s = None ; break
                    else:
                        # g.es_print('syntax error: deleting',p.h)
                        self.excludedTnodesList.append(p.v)
                        s = g.getScript(c,root,useSelectedText=False)
                except Exception:
                    g.trace('unexpected exception')
                    g.es_exception()
                    break
            return s or ''
        #@-node:ekr.20061031131434.63:forgivingParser (leoKeys)
        #@+node:ekr.20061031131434.64:computeErrorNode (leoKeys)
        def computeErrorNode (self,c,root,n,lines):

            '''The from c.goToLineNumberthatappliestoscripts. import 
            Unlike c.gotoLineNumberOpen, this function returns a position.'''

            if n == 1 or n >= len(lines):
                return root

            # vnodeName, junk, junk, junk, junk = c.convertLineToVnodeNameIndexLine(
                # lines, n, root, scriptFind = True)

            goto = goToLineNumber(c)
            vnodeName,junk,junk,junk = goto.findVnode(
                root,lines,n,ignoreSentinels)

            if vnodeName:
                for p in root.self_and_subtree():
                    if p.matchHeadline(vnodeName):
                        return p

            return None
        #@-node:ekr.20061031131434.64:computeErrorNode (leoKeys)
        #@+node:ekr.20061031131434.65:newPutBody
        def newPutBody (self,p,oneNodeOnly=False,fromString=''):

            if p.v in self.excludedTnodesList:
                pass
                # g.trace('ignoring',p.h)
            else:
                self.old_putBody(p,oneNodeOnly,fromString)
        #@-node:ekr.20061031131434.65:newPutBody
        #@-others
    #@-node:ekr.20061031131434.60:class forgivingParserClass
    #@+node:ekr.20061031131434.66:class classScannerClass
    class classScannerClass:

        '''A class to find class definitions in a node or its parents.'''

        #@    @+others
        #@+node:ekr.20061031131434.67:ctor
        def __init__ (self,c):

            self.c = c

            # Ignore @root for now:
            # self.start_in_doc = c.config.getBool('at_root_bodies_start_in_doc_mode')

            self.start_in_doc = False
        #@-node:ekr.20061031131434.67:ctor
        #@+node:ekr.20061031131434.68:scan
        def scan (self):

            c = self.c

            className,obj,p = self.findParentClass(c.p)
            # g.trace(className,obj,p)

            if p and not obj:
                parser = c.k.autoCompleter.forgivingParser
                s = parser.parse(p)
            else:
                s = None

            return className,obj,p,s
        #@-node:ekr.20061031131434.68:scan
        #@+node:ekr.20061031131434.69:findParentClass
        def findParentClass (self,root):

            autoCompleter = self.c.k.autoCompleter

            # First, see if any parent has already been scanned.
            for p in root.self_and_parents():
                obj = autoCompleter.selfVnodesDict.get(p.v)
                if obj:
                    # g.trace('found',obj,'in',p.h)
                    return None,obj,p

            # Next, do a much slower scan.
            # g.trace('slow scanning...')
            for p in root.self_and_parents():
                className = self.findClass(p)
                if className:
                    # g.trace('found',className,'in',p.h)
                    return className,None,p

            return None,None,None
        #@-node:ekr.20061031131434.69:findParentClass
        #@+node:ekr.20061031131434.70:findClass & helpers
        def findClass (self,p):

            lines = g.splitLines(p.b)
            inDoc = self.start_in_doc
            # g.trace(p.h)
            for s in lines:
                if inDoc:
                    if self.endsDoc(s):
                        inDoc = False
                else:
                    if self.startsDoc(s):
                        inDoc = True
                    else:
                        # Not a perfect scan: a triple-string could start with 'class',
                        # but perfection is not important.
                        className = self.startsClass(s)
                        if className: return className
            else:
                return None
        #@+node:ekr.20061031131434.71:endsDoc
        def endsDoc (self,s):

            return s.startswith('@c')
        #@-node:ekr.20061031131434.71:endsDoc
        #@+node:ekr.20061031131434.72:startsClass
        def startsClass (self,s):

            if s.startswith('class'):
                i = 5
                i = g.skip_ws(s,i)
                j = g.skip_id(s,i)
                word = s[i:j]
                # g.trace(word)
                return word
            else:
                return None
        #@-node:ekr.20061031131434.72:startsClass
        #@+node:ekr.20061031131434.73:startsDoc
        def startsDoc (self,s):

            for s2 in ('@doc','@ ','@\n', '@r', '@\t'):
                if s.startswith(s2):
                    return True
            else:
                return False
        #@-node:ekr.20061031131434.73:startsDoc
        #@-node:ekr.20061031131434.70:findClass & helpers
        #@-others
    #@-node:ekr.20061031131434.66:class classScannerClass
    #@-others
#@-node:ekr.20061031131434.4:class autoCompleterClass
#@+node:ekr.20061031131434.74:class keyHandlerClass
class keyHandlerClass:

    '''A class to support emacs-style commands.'''

    # Gui-independent class vars.

    global_killbuffer = []
        # Used only if useGlobalKillbuffer arg to Emacs ctor is True.
        # Otherwise, each Emacs instance has its own local kill buffer.

    global_registers = {}
        # Used only if useGlobalRegisters arg to Emacs ctor is True.
        # Otherwise each Emacs instance has its own set of registers.

    lossage = []
        # A case could be made for per-instance lossage, but this is not supported.

    #@    @+others
    #@+node:ekr.20061031131434.75: Birth (keyHandler)
    #@+node:ekr.20061031131434.76: ctor (keyHandler)
    def __init__ (self,c,useGlobalKillbuffer=False,useGlobalRegisters=False):

        '''Create a key handler for c.
        c.frame.miniBufferWidget is a Tk.Label.

        useGlobalRegisters and useGlobalKillbuffer indicate whether to use
        global (class vars) or per-instance (ivars) for kill buffers and registers.'''

        # g.trace('base keyHandler',g.callers())

        self.c = c
        self.dispatchEvent = None
        self.inited = False # Set at end of finishCreate.
        self.widget = c.frame.miniBufferWidget
        self.useGlobalKillbuffer = useGlobalKillbuffer
        self.useGlobalRegisters = useGlobalRegisters

        # Generalize...
        self.x_hasNumeric = ['sort-lines','sort-fields']

        self.altX_prompt = 'full-command: '

        # These must be defined here to avoid memory leaks.
        self.enable_autocompleter           = c.config.getBool('enable_autocompleter_initially')
        self.enable_calltips                = c.config.getBool('enable_calltips_initially')
        self.ignore_caps_lock               = c.config.getBool('ignore_caps_lock')
        self.ignore_unbound_non_ascii_keys  = c.config.getBool('ignore_unbound_non_ascii_keys')
        self.swap_mac_keys                  = c.config.getBool('swap_mac_keys')
        self.trace_bind_key_exceptions      = c.config.getBool('trace_bind_key_exceptions')
        self.trace_masterClickHandler       = c.config.getBool('trace_masterClickHandler')
        self.traceMasterCommand             = c.config.getBool('trace_masterCommand')
        self.trace_masterKeyHandler         = c.config.getBool('trace_masterKeyHandler')
        self.trace_masterKeyHandlerGC       = c.config.getBool('trace_masterKeyHandlerGC')
        self.trace_key_event                = c.config.getBool('trace_key_event')
        self.trace_minibuffer               = c.config.getBool('trace_minibuffer')
        self.warn_about_redefined_shortcuts = c.config.getBool('warn_about_redefined_shortcuts')
        # Has to be disabled (default) for AltGr support on Windows
        self.enable_alt_ctrl_bindings       = c.config.getBool('enable_alt_ctrl_bindings')
        #@    << define externally visible ivars >>
        #@+node:ekr.20061031131434.78:<< define externally visible ivars >>
        self.abbrevOn = False # True: abbreviations are on.
        self.arg = '' # The value returned by k.getArg.
        self.commandName = None # The name of the command being executed.
        self.funcReturn = None # For k.simulateCommand
        self.getArgEscape = None # A signal that the user escaped getArg in an unusual way.
        self.givenArgs = [] # New in Leo 4.4.8: arguments specified after the command name in k.simulateCommand.
        self.inputModeBindings = {}
        self.inputModeName = '' # The name of the input mode, or None.
        self.inverseCommandsDict = {}
            # Completed in k.finishCreate, but leoCommands.getPublicCommands adds entries first.
        self.modePrompt = '' # The mode promopt.
        self.negativeArg = False
        self.newMinibufferWidget = None # Usually the minibuffer restores focus.  This overrides this default.
        self.regx = g.bunch(iter=None,key=None)
        self.repeatCount = None
        self.previousSelection = None # A hack for middle-button paste: set by masterClickHandler, used by pasteText.
        self.state = g.bunch(kind=None,n=None,handler=None)
        #@-node:ekr.20061031131434.78:<< define externally visible ivars >>
        #@nl
        #@    << define internal ivars >>
        #@+node:ekr.20061031131434.79:<< define internal ivars >>
        self.abbreviationsDict = {} # Abbreviations created by @alias nodes.

        # Previously defined bindings.
        self.bindingsDict = {}
            # Keys are Tk key names, values are lists of g.bunch(pane,func,commandName)
        # Previously defined binding tags.
        self.bindtagsDict = {}
            # Keys are strings (the tag), values are 'True'
        self.masterBindingsDict = {}
            # Keys are scope names: 'all','text',etc. or mode names.
            # Values are dicts: keys are strokes, values are g.bunch(commandName,func,pane,stroke)
        self.masterGuiBindingsDict = {}
            # Keys are strokes; value is True;

        # Special bindings for k.fullCommand.
        self.mb_copyKey = None
        self.mb_pasteKey = None
        self.mb_cutKey = None
        self.mb_help = False

        self.abortAllModesKey = None
        self.fullCommandKey = None
        self.universalArgKey = None

        # Keepting track of the characters in the mini-buffer.
        self.arg_completion = True
        self.mb_event = None
        self.mb_history = []
        self.mb_prefix = ''
        self.mb_tabListPrefix = ''
        self.mb_tabList = []
        self.mb_tabListIndex = -1
        self.mb_prompt = ''

        self.func = None
        self.previous = []
        self.stroke = None

        # For onIdleTime
        self.idleCount = 0

        # For modes
        self.afterGetArgState = None
        self.argTabList = []
        self.getArgEscapes = []
        self.modeBindingsDict = {}
        self.modeWidget = None
        self.silentMode = False

        # The actual values are set later in k.finishCreate.
        self.command_mode_bg_color = 'white'
        self.command_mode_fg_color = 'black'
        self.insert_mode_bg_color = 'white'
        self.insert_mode_fg_color = 'black'
        self.overwrite_mode_bg_color = 'white'
        self.overwrite_mode_fg_color = 'black'
        #@-node:ekr.20061031131434.79:<< define internal ivars >>
        #@nl

        self.defineTkNames()
        self.defineSpecialKeys()
        self.defineSingleLineCommands()
        self.defineMultiLineCommands()
        self.autoCompleter = autoCompleterClass(self)
        self.setDefaultUnboundKeyAction()
    #@-node:ekr.20061031131434.76: ctor (keyHandler)
    #@+node:ekr.20061031131434.80:k.finishCreate & helpers
    def finishCreate (self):

        '''Complete the construction of the keyHandler class.
        c.commandsDict has been created when this is called.'''

        k = self ; c = k.c
        # g.trace('keyHandler')
        k.createInverseCommandsDict()

        # Important: bindings exist even if c.showMiniBuffer is False.
        k.makeAllBindings()

        # Set mode colors used by k.setInputState.
        bg = c.config.getColor('body_text_background_color') or 'white'
        fg = c.config.getColor('body_text_foreground_color') or 'black'

        k.command_mode_bg_color = c.config.getColor('command_mode_bg_color') or bg
        k.command_mode_fg_color = c.config.getColor('command_mode_fg_color') or fg
        k.insert_mode_bg_color = c.config.getColor('insert_mode_bg_color') or bg
        k.insert_mode_fg_color = c.config.getColor('insert_mode_fg_color') or fg
        k.overwrite_mode_bg_color = c.config.getColor('overwrite_mode_bg_color') or bg
        k.overwrite_mode_fg_color = c.config.getColor('overwrite_mode_fg_color') or fg
        k.unselected_body_bg_color = c.config.getColor('unselected_body_bg_color') or bg
        k.unselected_body_fg_color = c.config.getColor('unselected_body_fg_color') or bg    

        # g.trace(k.insert_mode_bg_color,k.insert_mode_fg_color)

        self.inited = True

        k.setDefaultInputState()
        k.resetLabel()
    #@+node:ekr.20061031131434.81:createInverseCommandsDict
    def createInverseCommandsDict (self):

        '''Add entries to k.inverseCommandsDict using c.commandDict.

        c.commandsDict:        keys are command names, values are funcions f.
        k.inverseCommandsDict: keys are f.__name__, values are minibuffer command names.
        '''

        k = self ; c = k.c

        for name in c.commandsDict:
            f = c.commandsDict.get(name)
            try:
                k.inverseCommandsDict [f.__name__] = name
                # g.trace('%24s = %s' % (f.__name__,name))

            except Exception:
                g.es_exception()
                g.trace(repr(name),repr(f),g.callers())
    #@-node:ekr.20061031131434.81:createInverseCommandsDict
    #@-node:ekr.20061031131434.80:k.finishCreate & helpers
    #@+node:ekr.20061031131434.82:setDefaultUnboundKeyAction
    def setDefaultUnboundKeyAction (self,allowCommandState=True):

        k = self ; c = k.c

        # g.trace(g.callers())

        defaultAction = c.config.getString('top_level_unbound_key_action') or 'insert'
        defaultAction.lower()

        if defaultAction == 'command' and not allowCommandState:
            self.unboundKeyAction = 'insert'
        elif defaultAction in ('command','insert','overwrite'):
            self.unboundKeyAction = defaultAction
        else:
            g.trace('ignoring top_level_unbound_key_action setting: %s' % (defaultAction))
            self.unboundKeyAction = 'insert'

        # g.trace(self.unboundKeyAction)

        self.defaultUnboundKeyAction = self.unboundKeyAction

        k.setInputState(self.defaultUnboundKeyAction)
    #@-node:ekr.20061031131434.82:setDefaultUnboundKeyAction
    #@+node:ekr.20070123143428:k.defineTkNames
    def defineTkNames (self):

        k = self

        # These names are used in Leo's core *regardless* of the gui actually in effect.
        # The gui is responsible for translating gui-dependent keycodes into these values.
        k.tkNamesList = (
            'BackSpace','Begin','Break',
            'Caps_Lock','Clear',
            'Delete','Down',
            'End','Escape',
            'F1','F2','F3','F4','F5','F6','F7','F8','F9','F10','F11','F12',
            'Home',
            'Left','Linefeed',
            'Next','Num_Lock',
            'Prior',
            'Return','Right',
            'Tab',
            'Up',
            # 'space',
        )

        # These keys settings that may be specied in leoSettings.leo.
        # Keys are lowercase, so that case is not significant *for these items only* in leoSettings.leo.
        k.settingsNameDict = {
            'bksp'    : 'BackSpace',
            'dnarrow' : 'Down',
            'esc'     : 'Escape',
            'ltarrow' : 'Left',
            'pageup'  : 'Prior',
            'pagedn'  : 'Next',
            'rtarrow' : 'Right',
            'uparrow' : 'Up',
        }

        # Add lowercase version of special keys.
        for s in k.tkNamesList:
            k.settingsNameDict [s.lower()] = s


    #@+at  
    #@nonl
    # The following are not translated, so what appears in the menu 
    # is the same as what is passed to Tk.  Case is significant.
    # Note: the Tk documentation states that not all of these may be 
    # available on all platforms.
    # 
    # Num_Lock, Pause, Scroll_Lock, Sys_Req,
    # KP_Add, KP_Decimal, KP_Divide, KP_Enter, KP_Equal,
    # KP_Multiply, KP_Separator,KP_Space, KP_Subtract, KP_Tab,
    # KP_F1,KP_F2,KP_F3,KP_F4,
    # KP_0,KP_1,KP_2,KP_3,KP_4,KP_5,KP_6,KP_7,KP_8,KP_9,
    # Insert
    #@-at
    #@-node:ekr.20070123143428:k.defineTkNames
    #@+node:ekr.20080509064108.6:k.defineSingleLineCommands
    def defineSingleLineCommands (self):

        k = self

        # These commands can be executed in the minibuffer.
        k.singleLineCommandList = [
            # editCommandsClass
            'back-to-indentation',
            'back-to-home', # 2010/02/01
            'back-char',
            'back-char-extend-selection',
            'back-word',
            'back-word-extend-selection',
            'backward-delete-char',
            'backward-find-character',
            'backward-find-character-extend-selection',
            'beginning-of-line',
            'beginning-of-line-extend-selection',
            'capitalize-word',
            'delete-char',
            'delete-indentation',
            'delete-spaces',
            'downcase-word',
            'end-of-line',
            'end-of-line-extend-selection',
            'escape',
            'exchange-point-mark',
            'extend-to-line',
            'extend-to-word',
            'find-character',
            'find-character-extend-selection',
            'find-word',
            'find-word-in-line',
            'forward-char',
            'forward-char-extend-selection',
            'forward-end-word',
            'forward-end-word-extend-selection',
            'forward-word',
            'forward-word-extend-selection',
            'insert-newline',
            'insert-parentheses',
            'move-past-close',
            'move-past-close-extend-selection',
            'newline-and-indent',
            'select-all',
            'transpose-chars',
            'transpose-words',
            'upcase-word',
            # keyHandlerCommandsClass
            # 'auto-complete',
            'negative-argument',
            'number-command',
            'number-command-0',
            'number-command-1',
            'number-command-2',
            'number-command-3',
            'number-command-4',
            'number-command-5',
            'number-command-6',
            'number-command-7',
            'number-command-8',
            'universal-argument',
            # killBufferCommandsClass
            'backward-kill-word',
            'kill-line',
            'kill-word',
            'kill-ws',
            'yank',
            'yank-pop',
            'zap-to-character',
            # leoCommands
            'cut-text',
            'copy-text',
            'paste-text',
            # macroCommandsClass
            'call-last-kbd-macro',
            # search commands
            # 'replace-string', # A special case so Shift-Ctrl-r will work after Ctrl-f.
            'toggle-find-collapses_nodes',
            'toggle-find-ignore-case-option',
            'toggle-find-in-body-option',
            'toggle-find-in-headline-option',
            'toggle-find-mark-changes-option',
            'toggle-find-mark-finds-option',
            'toggle-find-regex-option',
            'toggle-find-reverse-option',
            'toggle-find-word-option',
            'toggle-find-wrap-around-option',
            # registerCommandsClass
            'append-to-register',
            'copy-to-register',
            'insert-register',
            'prepend-to-register',
        ]
    #@-node:ekr.20080509064108.6:k.defineSingleLineCommands
    #@+node:ekr.20080509064108.7:k.defineMultiLineCommands
    def defineMultiLineCommands (self):

        k = self

        k.multiLineCommandList = [
            # editCommandsClass
            'add-space-to-lines',
            'add-tab-to-lines',
            'back-page',
            'back-page-extend-selection',
            'back-paragraph',
            'back-paragraph-extend-selection',
            'back-sentence',
            'back-sentence-extend-selection',
            'backward-kill-paragraph',
            'beginning-of-buffer',
            'beginning-of-buffer-extend-selection',
            'center-line',
            'center-region',
            'clean-all-lines',
            'clean-lines',
            'downcase-region',
            'end-of-buffer',
            'end-of-buffer-extend-selection',
            'extend-to-paragraph',
            'extend-to-sentence',
            'fill-paragraph',
            'fill-region',
            'fill-region-as-paragraph',
            'flush-lines',
            'forward-page',
            'forward-page-extend-selection',
            'forward-paragraph',
            'forward-paragraph-extend-selection',
            'forward-sentence',
            'forward-sentence-extend-selection',
            'indent-relative',
            'indent-rigidly',
            'indent-to-comment-column',
            'move-lines-down',
            'move-lines-up',
            'next-line',
            'next-line-extend-selection',
            'previous-line',
            'previous-line-extend-selection',
            'remove-blank-lines',
            'remove-space-from-lines',
            'remove-tab-from-lines',
            'reverse-region',
            'reverse-sort-lines',
            'reverse-sort-lines-ignoring-case',
            'scroll-down-half-page',
            'scroll-down-line',
            'scroll-down-page',
            'scroll-up-half-page',
            'scroll-up-line',
            'scroll-up-page',
            'simulate-begin-drag',
            'simulate-end-drag',
            'sort-columns',
            'sort-fields',
            'sort-lines',
            'sort-lines-ignoring-case',
            'split-line',
            'tabify',
            'transpose-lines',
            'untabify',
            'upcase-region',
            # keyHandlerCommandsClass
            'repeat-complex-command',
            # killBufferCommandsClass
            'backward-kill-sentence',
            'kill-sentence',
            'kill-region',
            'kill-region-save',
            # queryReplaceCommandsClass
            'query-replace',
            'query-replace-regex',
            # rectangleCommandsClass
            'clear-rectangle',
            'close-rectangle',
            'delete-rectangle',
            'kill-rectangle',
            'open-rectangle',
            'string-rectangle',
            'yank-rectangle',
            # registerCommandsClass
            'jump-to-register',
            'point-to-register',
            # searchCommandsClass
            'change',
            'change-then-find',
            'find-next',
            'find-prev',
        ]
    #@-node:ekr.20080509064108.7:k.defineMultiLineCommands
    #@+node:ekr.20070123085931:k.defineSpecialKeys
    def defineSpecialKeys (self):

        '''Define k.guiBindNamesDict and k.guiBindNamesInverseDict.

        Important: all gui's use these dictionaries because bindings in
        leoSettings.leo use these representations.'''

        k = self

        # g.trace('base keyHandler')

        # These are defined at http://tcl.activestate.com/man/tcl8.4/TkCmd/keysyms.htm.
        # Important: only the inverse dict is actually used in the new key binding scheme.
        # Tk may return the *values* of this dict in event.keysym fields.
        # Leo will warn if it gets a event whose keysym not in values of this table.
        k.guiBindNamesDict = {
            "&" : "ampersand",
            "^" : "asciicircum",
            "~" : "asciitilde",
            "*" : "asterisk",
            "@" : "at",
            "\\": "backslash",
            "|" : "bar",
            "{" : "braceleft",
            "}" : "braceright",
            "[" : "bracketleft",
            "]" : "bracketright",
            ":" : "colon",      # removed from code.
            "," : "comma",
            "$" : "dollar",
            "=" : "equal",
            "!" : "exclam",     # removed from code.
            ">" : "greater",
            "<" : "less",
            "-" : "minus",
            "#" : "numbersign",
            '"' : "quotedbl",
            "'" : "quoteright",
            "(" : "parenleft",
            ")" : "parenright", # removed from code.
            "%" : "percent",
            "." : "period",     # removed from code.
            "+" : "plus",
            "?" : "question",
            "`" : "quoteleft",
            ";" : "semicolon",
            "/" : "slash",
            " " : "space",      # removed from code.
            "_" : "underscore",
        }

        # No translation.
        for s in k.tkNamesList:
            k.guiBindNamesDict[s] = s

        # Create the inverse dict.
        k.guiBindNamesInverseDict = {}
        for key in k.guiBindNamesDict:
            k.guiBindNamesInverseDict [k.guiBindNamesDict.get(key)] = key

    #@-node:ekr.20070123085931:k.defineSpecialKeys
    #@+node:ekr.20061101071425:oops
    def oops (self):

        g.trace('Should be defined in subclass:',g.callers(4))
    #@-node:ekr.20061101071425:oops
    #@-node:ekr.20061031131434.75: Birth (keyHandler)
    #@+node:ekr.20061031131434.88:Binding (keyHandler)
    #@+node:ekr.20061031131434.89:bindKey
    def bindKey (self,pane,shortcut,callback,commandName,modeFlag=False):

        '''Bind the indicated shortcut (a Tk keystroke) to the callback.

        No actual gui bindings are made: only entries in k.masterBindingsDict.'''

        trace = False and not g.unitTesting
        k = self ; c = k.c

        # g.trace(pane,shortcut,commandName)
        if not shortcut:
            # g.trace('No shortcut for %s' % commandName)
            return False
        #@    << give warning and return if we try to bind to Enter or Leave >>
        #@+node:ekr.20061031131434.90:<< give warning and return if we try to bind to Enter or Leave >>
        if shortcut:
            for s in ('enter','leave'):
                if -1 != shortcut.lower().find(s):
                    g.es_print('ignoring invalid key binding:','%s = %s' % (
                        commandName,shortcut),color='blue')
                    return
        #@-node:ekr.20061031131434.90:<< give warning and return if we try to bind to Enter or Leave >>
        #@nl
        if pane.endswith('-mode'):
            g.trace('oops: ignoring mode binding',shortcut,commandName,g.callers())
            return False
        bunchList = k.bindingsDict.get(shortcut,[])
        if trace: g.trace(pane,shortcut,commandName)
        try:
            k.bindKeyToDict(pane,shortcut,callback,commandName)
            b = g.bunch(pane=pane,func=callback,commandName=commandName)
            #@        << remove previous conflicting definitions from bunchList >>
            #@+node:ekr.20061031131434.92:<< remove previous conflicting definitions from bunchList >>
            if not modeFlag and self.warn_about_redefined_shortcuts:
                redefs = [str(b2.commandName) for b2 in bunchList
                    if b2.commandName != commandName and pane in ('button','all',b2.pane)
                        and not b2.pane.endswith('-mode')]
                for z in redefs:
                    g.es_print ('redefining shortcut %30s from %s to %s in %s' % (
                        shortcut,
                        g.choose(pane=='button',z,commandName),
                        g.choose(pane=='button',commandName,z),
                        pane),color='red')

            if not modeFlag:
                bunchList = [b2 for b2 in bunchList if pane not in ('button','all',b2.pane)]
            #@-node:ekr.20061031131434.92:<< remove previous conflicting definitions from bunchList >>
            #@nl
            bunchList.append(b)
            shortcut = g.stripBrackets(shortcut.strip())
            k.bindingsDict [shortcut] = bunchList
            return True
        except Exception: # Could be a user error.
            if not g.app.menuWarningsGiven:
                g.es_print('exception binding',shortcut,'to',commandName)
                g.es_exception()
                g.app.menuWarningsGiven = True
            return False

    bindShortcut = bindKey # For compatibility
    #@-node:ekr.20061031131434.89:bindKey
    #@+node:ekr.20061031131434.93:bindKeyToDict
    def bindKeyToDict (self,pane,stroke,func,commandName):

        k = self
        d =  k.masterBindingsDict.get(pane,{})

        stroke = g.stripBrackets(stroke)

        if 0:
            g.trace('%-4s %-18s %-40s %s' % (
                pane,repr(stroke),commandName,func and func.__name__))

        # New in Leo 4.4.1: Allow redefintions.
        d [stroke] = g.Bunch(commandName=commandName,func=func,pane=pane,stroke=stroke)
        k.masterBindingsDict [pane] = d
    #@-node:ekr.20061031131434.93:bindKeyToDict
    #@+node:ekr.20061031131434.94:bindOpenWith
    def bindOpenWith (self,shortcut,name,data):

        '''Register an open-with command.'''

        k = self ; c = k.c

        # The first parameter must be event, and it must default to None.
        def openWithCallback(event=None,c=c,data=data):
            return c.openWith(data=data)

        # Use k.registerCommand to set the shortcuts in the various binding dicts.
        commandName = 'open-with-%s' % name.lower()
        k.registerCommand(commandName,shortcut,openWithCallback,pane='all',verbose=False)
    #@-node:ekr.20061031131434.94:bindOpenWith
    #@+node:ekr.20061031131434.95:checkBindings
    def checkBindings (self):

        '''Print warnings if commands do not have any @shortcut entry.
        The entry may be `None`, of course.'''

        k = self ; c = k.c

        if not c.config.getBool('warn_about_missing_settings'): return

        for name in sorted(c.commandsDict):
            abbrev = k.abbreviationsDict.get(name)
            key = c.frame.menu.canonicalizeMenuName(abbrev or name)
            key = key.replace('&','')
            if not g.app.config.exists(c,key,'shortcut'):
                if abbrev:
                     g.trace('No shortcut for abbrev %s -> %s = %s' % (
                        name,abbrev,key))
                else:
                    g.trace('No shortcut for %s = %s' % (name,key))
    #@-node:ekr.20061031131434.95:checkBindings
    #@+node:ekr.20070218130238:dumpMasterBindingsDict
    def dumpMasterBindingsDict (self):

        k = self ; d = k.masterBindingsDict

        g.pr('\nk.masterBindingsDict...\n')

        for key in sorted(d):
            g.pr(key, '-' * 40)
            d2 = d.get(key)
            for key2 in sorted(d2):
                b = d2.get(key2)
                g.pr('%20s %s' % (key2,b.commandName))
    #@-node:ekr.20070218130238:dumpMasterBindingsDict
    #@+node:ekr.20061031131434.96:k.completeAllBindingsForWidget
    def completeAllBindingsForWidget (self,w):

        k = self ; d = k.bindingsDict

        # g.trace('w',w,'Alt+Key-4' in d)

        for stroke in d:
            k.makeMasterGuiBinding(stroke,w=w)
    #@-node:ekr.20061031131434.96:k.completeAllBindingsForWidget
    #@+node:ekr.20061031131434.97:k.completeAllBindings
    def completeAllBindings (self,w=None):

        '''New in 4.4b3: make an actual binding in *all* the standard places.

        The event will go to k.masterKeyHandler as always, so nothing really changes.
        except that k.masterKeyHandler will know the proper stroke.'''

        # g.trace(w)

        k = self
        for stroke in k.bindingsDict:
            k.makeMasterGuiBinding(stroke,w=w)
    #@-node:ekr.20061031131434.97:k.completeAllBindings
    #@+node:ekr.20061031131434.98:k.makeAllBindings
    def makeAllBindings (self):

        k = self ; c = k.c

        # g.trace(c.fileName(),g.callers())

        k.bindingsDict = {}
        k.addModeCommands() 
        k.makeBindingsFromCommandsDict()
        k.initSpecialIvars()
        k.initAbbrev()
        c.frame.body.createBindings()
        c.frame.log.setTabBindings('Log')
        if c.frame.statusLine: c.frame.statusLine.setBindings()
        c.frame.tree.setBindings()
        c.frame.setMinibufferBindings()
        k.completeAllBindings()
        k.checkBindings()
    #@-node:ekr.20061031131434.98:k.makeAllBindings
    #@+node:ekr.20061031131434.99:k.initAbbrev
    def initAbbrev (self):

        k = self ; c = k.c ; d = c.config.getAbbrevDict()
        if d:
            for key in d:
                commandName = d.get(key)
                if commandName.startswith('press-') and commandName.endswith('-button'):
                    pass # Must be done later in k.registerCommand.
                else:
                    self.initOneAbbrev(commandName,key)

    def initOneAbbrev (self,commandName,key):
        k = self ; c = k.c
        if c.commandsDict.get(key):
            g.trace('ignoring duplicate abbrev: %s',key)
        else:
            func = c.commandsDict.get(commandName)
            if func:
                # g.trace(key,commandName,func.__name__)
                c.commandsDict [key] = func
                # k.inverseCommandsDict[func.__name__] = key
            else:
                g.es_print('bad abbrev:',key,'unknown command name:',commandName,color='blue')
    #@-node:ekr.20061031131434.99:k.initAbbrev
    #@+node:ekr.20061031131434.100:addModeCommands (enterModeCallback)
    def addModeCommands (self):

        '''Add commands created by @mode settings to c.commandsDict and k.inverseCommandsDict.'''

        k = self ; c = k.c
        d = g.app.config.modeCommandsDict # Keys are command names: enter-x-mode.

        # Create the callback functions and update c.commandsDict and k.inverseCommandsDict.
        for key in d:

            def enterModeCallback (event=None,name=key):
                k.enterNamedMode(event,name)

            c.commandsDict[key] = f = enterModeCallback
            k.inverseCommandsDict [f.__name__] = key
            # g.trace('leoCommands %24s = %s' % (f.__name__,key))
    #@-node:ekr.20061031131434.100:addModeCommands (enterModeCallback)
    #@+node:ekr.20061031131434.101:initSpecialIvars
    def initSpecialIvars (self):

        '''Set ivars for special keystrokes from previously-existing bindings.'''

        k = self ; c = k.c
        trace = False or c.config.getBool('trace_bindings_verbose')
        warn  = c.config.getBool('warn_about_missing_settings')

        for ivar,commandName in (
            ('fullCommandKey',  'full-command'),
            ('abortAllModesKey','keyboard-quit'),
            ('universalArgKey', 'universal-argument'),
        ):
            junk, bunchList = c.config.getShortcut(commandName)
            bunchList = bunchList or [] ; found = False
            for pane in ('text','all'):
                for bunch in bunchList:
                    if bunch.pane == pane:
                        stroke = k.strokeFromSetting(bunch.val)
                        if trace: g.trace(commandName,stroke)
                        setattr(k,ivar,stroke) ; found = True ;break
            if not found and warn:
                g.trace('no setting for %s' % commandName)
    #@-node:ekr.20061031131434.101:initSpecialIvars
    #@+node:ekr.20061031131434.102:makeBindingsFromCommandsDict
    def makeBindingsFromCommandsDict (self):

        '''Add bindings for all entries in c.commandDict.'''

        k = self ; c = k.c ; d = c.commandsDict

        for commandName in sorted(d):
            command = d.get(commandName)
            key, bunchList = c.config.getShortcut(commandName)
            # if commandName == 'keyboard-quit': g.trace(key,bunchList)
            for bunch in bunchList:
                accel = bunch.val ; pane = bunch.pane
                # if pane.endswith('-mode'): g.trace('skipping',shortcut,commandName)
                if accel and not pane.endswith('-mode'):
                    shortcut = k.shortcutFromSetting(accel)
                    k.bindKey(pane,shortcut,command,commandName)

        # g.trace(g.listToString(sorted(k.bindingsDict))
        # g.trace('Ctrl+g',k.bindingsDict.get('Ctrl+g'))
    #@-node:ekr.20061031131434.102:makeBindingsFromCommandsDict
    #@+node:ekr.20061031131434.103:k.makeMasterGuiBinding
    def makeMasterGuiBinding (self,stroke,w=None):

        '''Make a master gui binding for stroke in pane w, or in all the standard widgets.'''

        k = self ; c = k.c ; f = c.frame

        bindStroke = k.tkbindingFromStroke(stroke)

        # if stroke.lower()=='ctrl+v':
            # g.trace('stroke',repr(stroke),'bindStroke',repr(bindStroke),'w',repr(w),'\n',g.callers())

        if w:
            widgets = [w]
        else:
            # New in Leo 4.5: we *must* make the binding in the binding widget.
            bindingWidget = f.tree and hasattr(f.tree,'bindingWidget') and f.tree.bindingWidget or None
            bodyCtrl = f.body and hasattr(f.body,'bodyCtrl') and f.body.bodyCtrl or None
            canvas = f.tree and hasattr(f.tree,'canvas') and f.tree.canvas   or None
            if 0: # Canvas and bindingWidget bindings are now set in tree.setBindings.
                widgets = (c.miniBufferWidget,bodyCtrl)
            else:
                widgets = (c.miniBufferWidget,bodyCtrl,canvas,bindingWidget)

        # This is the only real key callback.
        def masterBindKeyCallback (event,k=k,stroke=stroke):
            # g.trace(stroke,event.w)
            return k.masterKeyHandler(event,stroke=stroke)

        if 0:
            if stroke.lower().endswith('+s') or stroke.lower().endswith('-s'):
                g.trace(sroke,widgets)
            if stroke in ('s','S'):
                g.trace(stroke,widgets)

        for w in widgets:
            if not w: continue
            # Make the binding only if no binding for the stroke exists in the widget.
            aList = k.masterGuiBindingsDict.get(bindStroke,[])
            if w not in aList:
                aList.append(w)
                k.masterGuiBindingsDict [bindStroke] = aList
                try:
                    c.bind(w,bindStroke,masterBindKeyCallback)
                    # g.trace(stroke,bindStroke,g.app.gui.widget_name(w))
                except Exception:
                    if self.trace_bind_key_exceptions:
                        g.es_exception()
                    g.es_print('exception binding',bindStroke,'to',c.widget_name(w),color='blue')

                    if g.app.unitTesting: raise
    #@-node:ekr.20061031131434.103:k.makeMasterGuiBinding
    #@-node:ekr.20061031131434.88:Binding (keyHandler)
    #@+node:ekr.20061031131434.104:Dispatching (keyHandler)
    #@+node:ekr.20061031131434.105:masterCommand & helpers
    def masterCommand (self,event,func,stroke,commandName=None):

        '''This is the central dispatching method.
        All commands and keystrokes pass through here.'''

        k = self ; c = k.c ; gui = g.app.gui
        trace = (False or k.traceMasterCommand) and not g.unitTesting
        verbose = False
        traceGC = False
        if traceGC: g.printNewObjects('masterCom 1')

        c.setLog()
        c.startRedrawCount = c.frame.tree.redrawCount
        k.stroke = stroke # Set this global for general use.
        keysym = gui.eventKeysym(event)
        ch = gui.eventChar(event)
        w = gui.eventWidget(event)
        state = event and hasattr(event,'state') and event.state or 0
        k.func = func
        k.funcReturn = None # For unit testing.
        commandName = commandName or func and func.__name__ or '<no function>'
        #@    << define specialKeysyms >>
        #@+node:ekr.20061031131434.106:<< define specialKeysyms >>
        specialKeysyms = (
            'Alt_L','Alt_R',
            'Meta_L','Meta_R', # Meta support.
            'Caps_Lock','Control_L','Control_R',
            'Num_Lock',
            'Shift_L','Shift_R',
        )
        #@nonl
        #@-node:ekr.20061031131434.106:<< define specialKeysyms >>
        #@nl
        special = keysym in specialKeysyms
        interesting = func is not None
        inserted = not special

        if trace: #  and interesting:
            g.trace(
                'stroke: ',stroke,'state:','%x' % state,'ch:',repr(ch),'keysym:',repr(keysym),
                'w:',w and c.widget_name(w),'func:',func and func.__name__
            )

        if inserted:
            # g.trace(stroke,keysym)
            #@        << add character to history >>
            #@+node:ekr.20061031131434.107:<< add character to history >>
            if stroke or len(ch) > 0:
                if len(keyHandlerClass.lossage) > 99:
                    keyHandlerClass.lossage.pop()

                # This looks like a memory leak, but isn't.
                keyHandlerClass.lossage.insert(0,(ch,stroke),)
            #@-node:ekr.20061031131434.107:<< add character to history >>
            #@nl

        # We *must not* interfere with the global state in the macro class.
        if c.macroCommands.recordingMacro:
            c.macroCommands.startKbdMacro(event)
            return 'break'

        # g.trace(stroke,k.abortAllModesKey)

        if k.abortAllModesKey and stroke == k.abortAllModesKey: # 'Control-g'
            k.keyboardQuit(event)
            k.endCommand(event,commandName)
            return 'break'

        if special: # Don't pass these on.
            return 'break' 

        if 0: # *** This is now handled by k.masterKeyHandler.
            if k.inState():
                val = k.callStateFunction(event) # Calls end-command.
                if val != 'do-func': return 'break'
                g.trace('Executing key outside of mode')

        if k.regx.iter:
            try:
                k.regXKey = keysym
                k.regx.iter.next() # EKR: next() may throw StopIteration.
            except StopIteration:
                pass
            return 'break'

        if k.abbrevOn:
            expanded = c.abbrevCommands.expandAbbrev(event)
            if expanded: return 'break'

        if func: # Func is an argument.
            if commandName == 'propagate-key-event':
                # Do *nothing* with the event.
                return k.propagateKeyEvent(event)
            elif commandName.startswith('specialCallback'):
                # The callback function will call c.doCommand
                if trace: g.trace('calling specialCallback for',commandName)
                # if commandName != 'repeat-complex-command': # 2010/01/11
                    # k.mb_history.insert(0,commandName)
                val = func(event)
                # k.simulateCommand uses k.funcReturn.
                k.funcReturn = k.funcReturn or val # For unit tests.
            else:
                # Call c.doCommand directly
                if trace: g.trace('calling command directly',commandName)
                # if commandName != 'repeat-complex-command': # 2010/01/11
                    # k.mb_history.insert(0,commandName)
                # if commandName == 'select-all': g.pdb()
                c.doCommand(func,commandName,event=event)
            if c.exists:
                k.endCommand(event,commandName)
                c.frame.updateStatusLine()
            if traceGC: g.printNewObjects('masterCom 2')
            return 'break'
        elif k.inState():
            return 'break' #Ignore unbound keys in a state.
        else:
            if traceGC: g.printNewObjects('masterCom 3')
            val = k.handleDefaultChar(event,stroke)
            if c.exists:
                c.frame.updateStatusLine()
            if traceGC: g.printNewObjects('masterCom 4')
            return val
    #@nonl
    #@+node:ekr.20061031131434.109:callKeystrokeFunction (not used)
    def callKeystrokeFunction (self,event):

        '''Handle a quick keystroke function.
        Return the function or None.'''

        k = self
        numberOfArgs, func = k.keystrokeFunctionDict [k.stroke]

        if func:
            func(event)
            commandName = k.inverseCommandsDict.get(func) # Get the emacs command name.
            k.endCommand(event,commandName)

        return func
    #@-node:ekr.20061031131434.109:callKeystrokeFunction (not used)
    #@+node:ekr.20061031131434.110:k.handleDefaultChar
    def handleDefaultChar(self,event,stroke):
        k = self ; c = k.c
        w = event and event.widget
        name = c.widget_name(w)
        trace = False and not g.unitTesting

        if trace: g.trace('widget_name',name,'stroke',stroke)

        if stroke and not (stroke.startswith('Alt+Ctrl') and \
            not self.enable_alt_ctrl_bindings) and \
            (stroke.find('Ctrl') > -1 or stroke.find('Alt') > -1
        ):
            if trace: g.trace('*** ignoring unbound ctrl/alt key:',stroke)
            return 'break'

        if name.startswith('body'):
            action = k.unboundKeyAction
            if action in ('insert','overwrite'):
                c.editCommands.selfInsertCommand(event,action=action)
            else: # Ignore the key
                if trace: g.trace('ignoring',stroke)
            return 'break'
        elif name.startswith('head'):
            c.frame.tree.onHeadlineKey(event)
            return 'break'
        elif name.startswith('canvas'):
            if not stroke: # Not exactly right, but it seems to be good enough.
                c.onCanvasKey(event) # New in Leo 4.4.2
            return 'break'
        elif name.startswith('log'):
            if g.app.gui.guiName() == 'tkinter':
                return None
            else:
                if trace: g.trace(w)
                i = w.logCtrl.getInsertPoint()
                if stroke.lower() == 'return': stroke = '\n'
                elif stroke.lower() == 'tab': stroke = '\t'
                elif stroke.lower() == 'backspace': stroke = '\b'
                w.logCtrl.insert(i,stroke)
                return None
        else:
            # Let the widget handle the event.
            # ch = event and event.char ; g.trace('to tk:',name,repr(stroke))
            return None
    #@nonl
    #@-node:ekr.20061031131434.110:k.handleDefaultChar
    #@-node:ekr.20061031131434.105:masterCommand & helpers
    #@+node:ekr.20061031131434.111:fullCommand (alt-x) & helper
    def fullCommand (self,event,specialStroke=None,specialFunc=None,help=False,helpHandler=None):

        '''Handle 'full-command' (alt-x) mode.'''

        k = self ; c = k.c ; gui = g.app.gui
        state = k.getState('full-command')
        helpPrompt = 'Help for command: '
        keysym = gui.eventKeysym(event) ; ch = gui.eventChar(event)
        trace = False or c.config.getBool('trace_modes') ; verbose = True
        if trace: g.trace('state',state,keysym)
        if state == 0:
            k.mb_event = event # Save the full event for later.
            k.setState('full-command',1,handler=k.fullCommand)
            prompt = g.choose(help,helpPrompt,k.altX_prompt)
            k.setLabelBlue('%s' % (prompt),protect=True)
            # Init mb_ ivars. This prevents problems with an initial backspace.
            k.mb_prompt = k.mb_tabListPrefix = k.mb_prefix = prompt
            k.mb_tabList = [] ; k.mb_tabListIndex = -1
            k.mb_help = help
            k.mb_helpHandler = helpHandler
            c.minibufferWantsFocus()
        elif keysym == 'Escape':
            k.keyboardQuit(event)
        elif keysym == 'Return':
            if trace and verbose: g.trace('***Return')
            c.frame.log.deleteTab('Completion')
            if k.mb_help:
                s = k.getLabel()
                commandName = s[len(helpPrompt):].strip()
                k.clearState()
                k.resetLabel()
                if k.mb_helpHandler: k.mb_helpHandler(commandName)
            else:
                k.callAltXFunction(k.mb_event)
        elif keysym in ('Tab','\t'):
            if trace and verbose: g.trace('***Tab')
            k.doTabCompletion(list(c.commandsDict.keys()))
            c.minibufferWantsFocus()
        elif keysym == 'BackSpace':
            if trace and verbose: g.trace('***BackSpace')
            k.doBackSpace(list(c.commandsDict.keys()))
            c.minibufferWantsFocus()
        elif k.ignore_unbound_non_ascii_keys and len(ch) > 1:
            # g.trace('non-ascii')
            if specialStroke:
                g.trace(specialStroke)
                specialFunc()
            c.minibufferWantsFocus()
        else:
            # Clear the list, any other character besides tab indicates that a new prefix is in effect.
            k.mb_tabList = []
            k.updateLabel(event)
            k.mb_tabListPrefix = k.getLabel()
            c.minibufferWantsFocus()
            # g.trace('new prefix',k.mb_tabListPrefix)

        return 'break'
    #@+node:ekr.20061031131434.112:callAltXFunction
    def callAltXFunction (self,event):

        trace = False and not g.unitTesting
        k = self ; c = k.c ; s = k.getLabel()
        k.mb_tabList = []
        commandName = s[len(k.mb_prefix):].strip()
        func = c.commandsDict.get(commandName)
        k.newMinibufferWidget = None

        # g.trace(func and func.__name__,'mb_event',event.widget.widgetName)
        if func:
            # These must be done *after* getting the command.
            k.clearState()
            k.resetLabel()
            if commandName != 'repeat-complex-command':
                k.mb_history.insert(0,commandName)
            w = event.widget
            if hasattr(w,'permanent') and not w.permanent:
                g.es('Can not execute commands from headlines')
            else:
                c.widgetWantsFocusNow(event.widget) # Important, so cut-text works, e.g.
                func(event)
            k.endCommand(event,commandName)
        else:
            if 1: # Useful.
                if trace: g.trace('*** tab completion')
                k.doTabCompletion(list(c.commandsDict.keys()))
            else: # Annoying.
                k.keyboardQuit(event)
                k.setLabel('Command does not exist: %s' % commandName)
                c.bodyWantsFocus()
    #@-node:ekr.20061031131434.112:callAltXFunction
    #@-node:ekr.20061031131434.111:fullCommand (alt-x) & helper
    #@+node:ekr.20061031131434.113:k.endCommand
    def endCommand (self,event,commandName):

        '''Make sure Leo updates the widget following a command.

        Never changes the minibuffer label: individual commands must do that.
        '''

        k = self ; c = k.c
        # The command may have closed the window.
        if g.app.quitting or not c.exists: return

        # Set the best possible undoType: prefer explicit commandName to k.commandName.
        commandName = commandName or k.commandName or ''
        k.commandName = k.commandName or commandName or ''
        if commandName:
            bodyCtrl = c.frame.body.bodyCtrl
            if not k.inState():
                k.commandName = None

                # Do the import here to break a circular dependency at the top level.
                import leo.core.leoEditCommands as leoEditCommands
                leoEditCommands.initAllEditCommanders(c)
                try:
                    bodyCtrl.tag_delete('color')
                    bodyCtrl.tag_delete('color1')
                except Exception:
                    pass
            if 0: # Do *not* call this by default.  It interferes with undo.
                c.frame.body.onBodyChanged(undoType='Typing')
            if k.newMinibufferWidget:
                c.widgetWantsFocusNow(k.newMinibufferWidget)
                # g.pr('endCommand', g.app.gui.widget_name(k.newMinibufferWidget),g.callers())
                k.newMinibufferWidget = None
    #@-node:ekr.20061031131434.113:k.endCommand
    #@-node:ekr.20061031131434.104:Dispatching (keyHandler)
    #@+node:ekr.20061031131434.114:Externally visible commands
    #@+node:ekr.20061031131434.115:digitArgument & universalArgument
    def universalArgument (self,event):

        '''Prompt for a universal argument.'''
        k = self
        k.setLabelBlue('Universal Argument: ',protect=True)
        k.universalDispatcher(event)

    def digitArgument (self,event):

        '''Prompt for a digit argument.'''
        k = self
        k.setLabelBlue('Digit Argument: ',protect=True)
        k.universalDispatcher(event)
    #@-node:ekr.20061031131434.115:digitArgument & universalArgument
    #@+node:ekr.20061031131434.116:k.show/hide/toggleMinibuffer
    def hideMinibuffer (self,event):
        '''Hide the minibuffer.'''
        k = self ; c = k.c
        c.frame.hideMinibuffer()
        g.es('minibuffer hidden',color='red')
        for commandName in ('show-mini-buffer','toggle-mini-buffer'):
            shortcut = k.getShortcutForCommandName(commandName)
            if shortcut:
                g.es('',commandName,'is bound to:',shortcut)

    def showMinibuffer (self,event):
        '''Show the minibuffer.'''
        k = self ; c = k.c
        c.frame.showMinibuffer()

    def toggleMinibuffer (self,event):
        '''Show or hide the minibuffer.'''
        k = self ; c = k.c
        if c.frame.minibufferVisible:
            k.hideMinibuffer(event)
        else:
            k.showMinibuffer(event)
    #@-node:ekr.20061031131434.116:k.show/hide/toggleMinibuffer
    #@+node:ekr.20070613133500:k.menuCommandKey
    def menuCommandKey (self,event=None):

        # This method must exist, but it never gets called.
        pass 
    #@-node:ekr.20070613133500:k.menuCommandKey
    #@+node:ekr.20070613190936:k.propagateKeyEvent
    def propagateKeyEvent (self,event):

        self.oops() # Should be overridden.
    #@nonl
    #@-node:ekr.20070613190936:k.propagateKeyEvent
    #@+node:ekr.20061031131434.117:negativeArgument (redo?)
    def negativeArgument (self,event):

        '''Prompt for a negative digit argument.'''

        k = self ; state = k.getState('neg-arg')

        if state == 0:
            k.setLabelBlue('Negative Argument: ',protect=True)
            k.setState('neg-arg',1,k.negativeArgument)
        else:
            k.clearState()
            k.resetLabel()
            func = k.negArgFunctions.get(k.stroke)
            if func:
                func(event)

        return 'break'
    #@-node:ekr.20061031131434.117:negativeArgument (redo?)
    #@+node:ekr.20061031131434.118:numberCommand
    def numberCommand (self,event,stroke,number):

        k = self ; k.stroke = stroke ; w = event.widget
        k.universalDispatcher(event)
        g.app.gui.event_generate(w,'<Key>',keysym=number)
        return 'break'

    def numberCommand0 (self,event):
        '''Execute command number 0.'''
        return self.numberCommand (event,None,0)

    def numberCommand1 (self,event):
        '''Execute command number 1.'''
        return self.numberCommand (event,None,1)

    def numberCommand2 (self,event):
        '''Execute command number 2.'''
        return self.numberCommand (event,None,2)

    def numberCommand3 (self,event):
        '''Execute command number 3.'''
        return self.numberCommand (event,None,3)

    def numberCommand4 (self,event):
        '''Execute command number 4.'''
        return self.numberCommand (event,None,4)

    def numberCommand5 (self,event):
        '''Execute command number 5.'''
        return self.numberCommand (event,None,5)

    def numberCommand6 (self,event):
        '''Execute command number 6.'''
        return self.numberCommand (event,None,6)

    def numberCommand7 (self,event):
        '''Execute command number 7.'''
        return self.numberCommand (event,None,7)

    def numberCommand8 (self,event):
        '''Execute command number 8.'''
        return self.numberCommand (event,None,8)

    def numberCommand9 (self,event):
        '''Execute command number 9.'''
        return self.numberCommand (event,None,9)
    #@-node:ekr.20061031131434.118:numberCommand
    #@+node:ekr.20061031131434.119:printBindings & helper
    def printBindings (self,event=None):

        '''Print all the bindings presently in effect.'''

        k = self ; c = k.c
        d = k.bindingsDict ; tabName = 'Bindings'
        c.frame.log.clearTab(tabName)
        data = [] ; n1 = 4 ; n2 = 20
        if not d: return g.es('no bindings')
        for key in sorted(d):
            bunchList = d.get(key,[])
            for b in bunchList:
                pane = g.choose(b.pane=='all','',' %s:' % (b.pane))
                s1 = pane
                s2 = k.prettyPrintKey(key,brief=True)
                s3 = b.commandName
                n1 = max(n1,len(s1))
                n2 = max(n2,len(s2))
                data.append((s1,s2,s3),)

        # Print keys by type:
        result = []
        sep = '-' * n1
        for prefix in (
            'Alt+Ctrl+Shift', 'Alt+Shift', 'Alt+Ctrl', 'Alt+Key','Alt',
            'Ctrl+Shift', 'Ctrl', 'Shift',
            'Meta+Ctrl+Shift', 'Meta+Shift', 'Meta+Ctrl', 'Meta+Key','Meta',
            # Meta support
        ):
            data2 = []
            for item in data:
                s1,s2,s3 = item
                if s2.startswith(prefix):
                    data2.append(item)
            # g.es('','%s %s' % (sep, prefix),tabName=tabName)
            result.append('%s %s\n' % (sep, prefix))
            self.printBindingsHelper(result,data2,n1,n2,prefix=prefix)
            # Remove all the items in data2 from data.
            # This must be done outside the iterator on data.
            for item in data2:
                data.remove(item)
        # Print all plain bindings.
        result.append('%s %s\n' % (sep, 'Plain Keys'))
        self.printBindingsHelper(result,data,n1,n2,prefix=None)
        if not g.unitTesting:
            g.es('',''.join(result),tabName=tabName)
        state = k.unboundKeyAction 
        k.showStateAndMode()
        return result # for unit test.
    #@+node:ekr.20061031131434.120:printBindingsHelper
    def printBindingsHelper (self,result,data,n1,n2,prefix):

        n = prefix and len(prefix)+1 or 0 # Add 1 for the '+' after the prefix.

        data1 = [z for z in data if z and z[1] and len(z[1][n:]) == 1]
            # The list of all items with only one character following the prefix.
        data2 = [z for z in data if z and z[1] and len(z[1][n:]) >  1]
            # The list of all other items.

        # This isn't perfect in variable-width fonts.
        for data in (data1,data2):
            data.sort(key=lambda x: x[1])
                # key is a function that extracts args.
            for s1,s2,s3 in data:
                # g.es('','%*s %*s %s' % (-n1,s1,-(min(12,n2)),s2,s3),tabName='Bindings')
                result.append('%*s %*s %s\n' % (-n1,s1,-(min(12,n2)),s2,s3))
    #@-node:ekr.20061031131434.120:printBindingsHelper
    #@-node:ekr.20061031131434.119:printBindings & helper
    #@+node:ekr.20061031131434.121:printCommands
    def printCommands (self,event=None):

        '''Print all the known commands and their bindings, if any.'''

        k = self ; c = k.c ; tabName = 'Commands'

        c.frame.log.clearTab(tabName)

        inverseBindingDict = k.computeInverseBindingDict()

        data = [] ; n1 = 4 ; n2 = 20
        for commandName in sorted(c.commandsDict):
            dataList = inverseBindingDict.get(commandName,[('',''),])
            for z in dataList:
                pane, key = z
                s1 = pane
                s2 = k.prettyPrintKey(key,brief=True)
                s3 = commandName
                n1 = max(n1,len(s1))
                n2 = max(n2,len(s2))
                data.append((s1,s2,s3),)

        # This isn't perfect in variable-width fonts.
        lines = ['%*s %*s %s\n' % (-n1,s1,-(min(12,n2)),s2,s3) for s1,s2,s3 in data]
        g.es('',''.join(lines),tabName=tabName)
    #@-node:ekr.20061031131434.121:printCommands
    #@+node:ekr.20061031131434.122:repeatComplexCommand & helper
    def repeatComplexCommand (self,event):

        '''Repeat the previously executed minibuffer command.'''
        k = self
        if k.mb_history:
            k.setState('last-full-command',1,handler=k.repeatComplexCommandHelper)
            k.setLabelBlue("Redo: %s" % str(k.mb_history[0]))
        else:
            g.es('no previous command',color='blue')
        return 'break'

    def repeatComplexCommandHelper (self,event):

        k = self ; c = k.c ; gui = g.app.gui

        keysym = gui.eventKeysym(event)
        # g.trace('state',k.state.kind,'event',repr(event),g.callers())
        if keysym == 'Return' and k.mb_history:
        # if k.mb_history:
            last = k.mb_history [0]
            k.resetLabel()
            k.clearState() # Bug fix.
            c.commandsDict [last](event)
            return 'break'
        else:
            # g.trace('oops')
            return k.keyboardQuit(event)
    #@-node:ekr.20061031131434.122:repeatComplexCommand & helper
    #@+node:ekr.20061031131434.123:set-xxx-State
    def setCommandState (self,event):
        '''Enter the 'command' editing state.'''
        # g.trace(g.callers())
        k = self
        k.setInputState('command')
        # This command is also valid in headlines.
        # k.c.bodyWantsFocusNow()
        k.showStateAndMode()

    def setInsertState (self,event):
        '''Enter the 'insert' editing state.'''
        # g.trace(g.callers())
        k = self
        k.setInputState('insert')
        # This command is also valid in headlines.
        # k.c.bodyWantsFocusNow()
        k.showStateAndMode()

    def setOverwriteState (self,event):
        '''Enter the 'overwrite' editing state.'''
        # g.trace(g.callers())
        k = self
        k.setInputState('overwrite')
        # This command is also valid in headlines.
        # k.c.bodyWantsFocusNow()
        k.showStateAndMode()
    #@-node:ekr.20061031131434.123:set-xxx-State
    #@+node:ekr.20061031131434.124:toggle-input-state
    def toggleInputState (self,event=None):

        '''The toggle-input-state command.'''

        k = self ; c = k.c
        default = c.config.getString('top_level_unbound_key_action') or 'insert'
        state = k.unboundKeyAction

        if default == 'insert':
            state = g.choose(state=='insert','command','insert')
        elif default == 'overwrite':
            state = g.choose(state=='overwrite','command','overwrite')
        else:
            state = g.choose(state=='command','insert','command') # prefer insert to overwrite.

        k.setInputState(state)
        k.showStateAndMode()
    #@-node:ekr.20061031131434.124:toggle-input-state
    #@-node:ekr.20061031131434.114:Externally visible commands
    #@+node:ekr.20061031131434.125:Externally visible helpers
    #@+node:ekr.20061031131434.126:manufactureKeyPressForCommandName
    def manufactureKeyPressForCommandName (self,w,commandName):

        '''Implement a command by passing a keypress to the gui.'''

        k = self ; c = k.c

        stroke = k.getShortcutForCommandName(commandName)

        # g.trace('stroke',repr(stroke),'commandName',commandName,'w',w,g.callers())

        if stroke and w:
            # g.trace(stroke)
            g.app.gui.set_focus(c,w)
            g.app.gui.event_generate(w,stroke)
        else:
            g.trace('no shortcut for %s' % (commandName),color='red')
    #@-node:ekr.20061031131434.126:manufactureKeyPressForCommandName
    #@+node:ekr.20061031131434.127:simulateCommand
    def simulateCommand (self,commandName):

        k = self ; c = k.c

        commandName = commandName.strip()
        if not commandName: return


        aList = commandName.split(None)
        if len(aList) == 1:
            k.givenArgs = []
        else:
            commandName = aList[0]
            k.givenArgs = aList[1:]

        # g.trace(commandName,k.givenArgs)
        func = c.commandsDict.get(commandName)

        if func:
            # g.trace(commandName,func.__name__)
            stroke = None
            if commandName.startswith('specialCallback'):
                event = None # A legacy function.
            else: # Create a dummy event as a signal.
                event = g.bunch(c=c,keysym='',char='',widget=None)
            k.masterCommand(event,func,stroke)
            if c.exists:
                return k.funcReturn
            else:
                return None
        else:
            g.trace('no command for %s' % (commandName),color='red')
            if g.app.unitTesting:
                raise AttributeError
            else:
                return None
    #@-node:ekr.20061031131434.127:simulateCommand
    #@+node:ekr.20061031131434.128:getArg
    def getArg (self,event,
        returnKind=None,returnState=None,handler=None,
        prefix=None,tabList=[],completion=True,oneCharacter=False,
        stroke=None, # New in 4.4.1.
        useMinibuffer=True # New in 4.4.1
    ):

        '''Accumulate an argument until the user hits return (or control-g).
        Enter the given return state when done.
        The prefix does not form the arg.  The prefix defaults to the k.getLabel().
        '''

        k = self ; c = k.c ; gui  = g.app.gui
        trace = (False or (c.config.getBool('trace_modes')) and not g.app.unitTesting)
        state = k.getState('getArg')
        keysym = gui.eventKeysym(event)
        if trace: g.trace(
            'state',state,'keysym',repr(keysym),'stroke',repr(stroke),
            'escapes',k.getArgEscapes,
            'completion',state==0 and completion or state!=0 and k.arg_completion)
        if state == 0:
            k.arg = ''
            #@        << init altX vars >>
            #@+node:ekr.20061031131434.129:<< init altX vars >>
            k.argTabList = tabList and tabList[:] or []
            k.arg_completion = completion
            # g.trace('completion',completion,'tabList',tabList)

            k.mb_prefix = prefix or k.getLabel()
            k.mb_prompt = prefix or ''
            k.mb_tabList = []

            # Clear the list: any non-tab indicates that a new prefix is in effect.
            k.mb_tabListPrefix = k.getLabel()
            k.oneCharacterArg = oneCharacter
            #@-node:ekr.20061031131434.129:<< init altX vars >>
            #@nl
            # Set the states.
            bodyCtrl = c.frame.body.bodyCtrl
            c.widgetWantsFocus(bodyCtrl)
            k.afterGetArgState=returnKind,returnState,handler
            k.setState('getArg',1,k.getArg)
            k.afterArgWidget = event and event.widget or c.frame.body.bodyCtrl
            if useMinibuffer: c.minibufferWantsFocusNow()
        elif keysym == 'Escape':
            k.keyboardQuit(event)
        elif keysym == 'Return' or k.oneCharacterArg or (stroke and stroke in k.getArgEscapes):
            if stroke and stroke in k.getArgEscapes: k.getArgEscape = stroke
            if k.oneCharacterArg:
                k.arg = event.char
            else:
                k.arg = k.getLabel(ignorePrompt=True)
            kind,n,handler = k.afterGetArgState
            if kind: k.setState(kind,n,handler)
            c.frame.log.deleteTab('Completion')
            trace and g.trace('kind',kind,'n',n,'handler',handler and handler.__name__)
            if handler: handler(event)
        elif keysym in('Tab','\t'):
            k.doTabCompletion(k.argTabList,k.arg_completion)
        elif keysym in('BackSpace','\b'): # 2010/02/20: Test for \b also.
            k.doBackSpace(k.argTabList,k.arg_completion)
            c.minibufferWantsFocus()
        else:
            # Clear the list, any other character besides tab indicates that a new prefix is in effect.
            k.mb_tabList = []
            k.updateLabel(event)
            k.mb_tabListPrefix = k.getLabel()
        return 'break'
    #@-node:ekr.20061031131434.128:getArg
    #@+node:ekr.20061031131434.130:keyboardQuit
    def keyboardQuit (self,event,hideTabs=True,setDefaultStatus=True):

        '''This method clears the state and the minibuffer label.

        k.endCommand handles all other end-of-command chores.'''

        k = self ; c = k.c

        if g.app.quitting:
            return

        if hideTabs:
            k.autoCompleter.exit()
            c.frame.log.deleteTab('Mode')
            c.frame.log.hideTab('Completion')

        # Completely clear the mode.
        if k.inputModeName:
            k.endMode(event)

        # Complete clear the state.
        k.state.kind = None
        k.state.n = None

        k.clearState()
        k.resetLabel()

        c.endEditing()
        c.bodyWantsFocus()

        if setDefaultStatus:
            # At present, only the auto-completer suppresses this.
            k.setDefaultInputState()
            k.showStateAndMode()
    #@-node:ekr.20061031131434.130:keyboardQuit
    #@+node:ekr.20061031131434.131:k.registerCommand
    def registerCommand (self,commandName,shortcut,func,pane='all',verbose=False, wrap=True):

        '''Make the function available as a minibuffer command,
        and optionally attempt to bind a shortcut.

        You can wrap any method in a callback function, so the
        restriction to functions is not significant.

        If wrap is True then func will be wrapped with c.universalCallback.

        '''

        trace = False and not g.unitTesting
        k = self ; c = k.c

        if wrap:
            func = c.universalCallback(func)

        f = c.commandsDict.get(commandName)
        verbose = (False or verbose) and not g.app.unitTesting
        if f and f.__name__ != 'dummyCallback' and verbose:
            g.es_print('redefining',commandName, color='red')

        c.commandsDict [commandName] = func
        k.inverseCommandsDict [func.__name__] = commandName
        if trace: g.trace('leoCommands %24s = %s' % (func.__name__,commandName))

        if shortcut:
            stroke = k.shortcutFromSetting(shortcut)
        elif commandName.lower() == 'shortcut': # Causes problems.
            stroke = None
        else:
            # Try to get a shortcut from leoSettings.leo.
            junk,bunchList = c.config.getShortcut(commandName)
            for bunch in bunchList:
                accel2 = bunch.val ; pane2 = bunch.pane
                if accel2 and not pane2.endswith('-mode'):
                    shortcut2 = accel2
                    stroke = k.shortcutFromSetting(shortcut2)
                    if stroke: break
            else: stroke = None

        if stroke:
            if trace: g.trace('stroke',stroke,'pane',pane,commandName,g.callers(4))
            ok = k.bindKey (pane,stroke,func,commandName) # Must be a stroke.
            k.makeMasterGuiBinding(stroke) # Must be a stroke.
            if verbose and ok and not g.app.silentMode:
                # g.trace(g.callers())
                g.es_print('','@command: %s = %s' % (
                    commandName,k.prettyPrintKey(stroke)),color='blue')
                if 0:
                    d = k.masterBindingsDict.get('button',{})
                    g.print_dict(d)
            c.frame.tree.setBindings()
        elif verbose and not g.app.silentMode:
            g.trace(g.callers())
            g.es_print('','@command: %s' % (commandName),color='blue')

        # Fixup any previous abbreviation to press-x-button commands.
        if commandName.startswith('press-') and commandName.endswith('-button'):
            d = c.config.getAbbrevDict()
                # Keys are full command names, values are abbreviations.
            if commandName in list(d.values()):
                for key in d:
                    if d.get(key) == commandName:
                        c.commandsDict [key] = c.commandsDict.get(commandName)
                        break
    #@-node:ekr.20061031131434.131:k.registerCommand
    #@+node:ekr.20071212104050:k.overrideCommand
    def overrideCommand (self,commandName,func):

        # Override entries in c.k.masterBindingsDict
        k = self
        d = k.masterBindingsDict
        for key in d:
            d2 = d.get(key)
            for key2 in d2:
                b = d2.get(key2)
                if b.commandName == commandName:
                    b.func=func
                    d2[key2] = b
    #@-node:ekr.20071212104050:k.overrideCommand
    #@-node:ekr.20061031131434.125:Externally visible helpers
    #@+node:ekr.20061031131434.145:Master event handlers (keyHandler)
    #@+node:ekr.20061031131434.146:masterKeyHandler & helpers
    master_key_count = 0

    def masterKeyHandler (self,event,stroke=None):

        '''This is the handler for almost all key bindings.'''

        #@    << define vars >>
        #@+node:ekr.20061031131434.147:<< define vars >>
        k = self ; c = k.c ; gui = g.app.gui

        if event:
            # This is a leoGui base class event.
            event = gui.leoKeyEvent(event,c,stroke=stroke)

        w = event.widget
        char = event.char
        keysym = event.keysym
        stroke = event.stroke ### 2010/10/18
        if stroke and not keysym:
            event.keysym = keysym = stroke

        w_name = c.widget_name(w)
        state = k.state.kind

        special_keys = (
            'Alt_L','Alt_R',
            'Caps_Lock','Control_L','Control_R',
            'Meta_L','Meta_R', # Meta support.
            'Num_Lock',
            'Shift_L','Shift_R',
            'Win_L','Win_R',
        )

        self.master_key_count += 1

        isPlain =  k.isPlainKey(stroke)
        #@nonl
        #@-node:ekr.20061031131434.147:<< define vars >>
        #@nl
        trace = False and not g.app.unitTesting # or self.trace_masterKeyHandler)
        traceGC = self.trace_masterKeyHandlerGC and not g.app.unitTesting
        verbose = True

        if keysym in special_keys:
            if trace and verbose: g.trace('keysym',keysym)
            return None
        if traceGC: g.printNewObjects('masterKey 1')
        if trace: g.trace('stroke:',repr(stroke),'keysym:',
            repr(event.keysym),'ch:',repr(event.char),'state',state)

        # Handle keyboard-quit first.
        if k.abortAllModesKey and stroke == k.abortAllModesKey:
            if c.macroCommands.recordingMacro:
                c.macroCommands.endKbdMacro()
                return 'break'
            else:
                return k.masterCommand(event,k.keyboardQuit,stroke,'keyboard-quit')

        if k.inState():
            done,val = k.doMode(event,state,stroke)
            if done: return val

        if traceGC: g.printNewObjects('masterKey 2')

        if stroke and isPlain:
            done,b = k.doPlainKey(event,stroke,w)
            if b: k.masterCommand(event,b.func,b.stroke,b.commandName)
            if done: return 'break'

        b = k.getPaneBinding(stroke,w)
        if b:
            if traceGC: g.printNewObjects('masterKey 3')
            return k.masterCommand(event,b.func,b.stroke,b.commandName)
        else:
            if traceGC: g.printNewObjects('masterKey 4')
            return k.handleUnboundKeys(event,char,keysym,stroke)
    #@+node:ekr.20061031131434.108:callStateFunction
    def callStateFunction (self,event):

        k = self ; val = None ; ch = g.app.gui.eventChar(event)

        # g.trace(k.state.kind,'ch',ch,'ignore-non-ascii',k.ignore_unbound_non_ascii_keys)

        if k.state.kind:
            if (
                k.ignore_unbound_non_ascii_keys and
                ch and ch not in ('\b','\n','\r','\t') and
                (ord(ch) < 32 or ord(ch) > 128)
            ):
                # g.trace('non-ascii',ord(ch))
                pass
            elif k.state.handler:
                val = k.state.handler(event)
                if val != 'continue':
                    k.endCommand(event,k.commandName)
            else:
                g.es_print('no state function for',k.state.kind,color='red')

        return val
    #@-node:ekr.20061031131434.108:callStateFunction
    #@+node:ekr.20091230094319.6244:doMode
    def doMode (self,event,state,stroke):

        trace = False and not g.unitTesting
        k = self

        # First, honor minibuffer bindings for all except user modes.
        if state in ('getArg','getFileName','full-command','auto-complete'):
            if k.handleMiniBindings(event,state,stroke):
                return True,'break'

        # Second, honor general modes.
        if state == 'getArg':
            return True,k.getArg(event,stroke=stroke)
        elif state == 'getFileName':
            return True,k.getFileName(event)
        elif state in ('full-command','auto-complete'):
            # Do the default state action.
            if trace: g.trace('calling state function',k.state.kind)
            val = k.callStateFunction(event) # Calls end-command.
            if val != 'do-standard-keys':
                return True,'break'

        # Third, pass keys to user modes.
        d =  k.masterBindingsDict.get(state)
        if d:
            b = d.get(stroke)
            if b:
                if trace: g.trace('calling generalModeHandler',stroke)
                k.generalModeHandler (event,
                    commandName=b.commandName,func=b.func,
                    modeName=state,nextMode=b.nextMode)
                return True,'break'
            else:
                # New in Leo 4.5: unbound keys end mode.
                if trace: g.trace('unbound key ends mode',stroke,state)
                k.endMode(event)
                return False,None
        else:
            # New in 4.4b4.
            handler = k.getStateHandler()
            if handler:
                handler(event)
            else:
                g.trace('No state handler for %s' % state)
            return True,'break'
    #@-node:ekr.20091230094319.6244:doMode
    #@+node:ekr.20091230094319.6242:doPlainKey
    def doPlainKey (self,event,stroke,w):

        '''Handle a plain key.  Return done,b.'''

        trace = False and not g.unitTesting
        k = self ; c = k.c

        # Important: only keys bound somewhere have a stroke.
        # All unbound plain keys will be handled by handleUnboundKeys.
        if k.unboundKeyAction in ('insert','overwrite'):
            for key in (k.unboundKeyAction,'body','log','text','all'):
                # Ignore bindings for all plain keys in insert/overwrite mode
                # *except* auto-complete.
                d = k.masterBindingsDict.get(key,{})
                if d:
                    b = d.get(stroke)
                    if b and b.commandName == 'auto-complete':
                        if trace: g.trace('%s: auto-complete key in %s mode' % (
                            stroke,k.unboundKeyAction))
                        return True,b

            if trace: g.trace('unbound key: %s in %s mode' % (stroke,k.unboundKeyAction))
            return True,g.bunch(func=None,stroke=stroke,commandName=None)

        # Ignore all command-state keys if we are not in a text widget.
        elif k.unboundKeyAction == 'command':
            if g.app.gui.isTextWidget(w):
                return False,None
            else:
                c.onCanvasKey(event)
                return True,None
        else:
            return False,None
    #@-node:ekr.20091230094319.6242:doPlainKey
    #@+node:ekr.20091230094319.6240:getPaneBinding
    def getPaneBinding (self,stroke,w):

        trace = False and not g.unitTesting
        verbose = False
        k = self ; w_name = k.c.widget_name(w)
        keyStatesTuple = ('command','insert','overwrite')

        if trace: g.trace('w_name',repr(w_name),'stroke',stroke,'w',w,
            'isTextWidget(w)',g.app.gui.isTextWidget(w))

        for key,name in (
            # Order here is similar to bindtags order.
            ('command',None),
            ('insert',None),
            ('overwrite',None),
            ('button',None),
            ('body','body'),
            ('text','head'), # Important: text bindings in head before tree bindings.
            ('tree','head'),
            ('tree','canvas'),
            ('log', 'log'),
            ('text','log'),
            ('text',None),
            ('all',None),
        ):
            if (
                # key in keyStatesTuple and isPlain and k.unboundKeyAction == key or
                name and w_name.startswith(name) or
                key in ('text','all') and g.app.gui.isTextWidget(w) or
                key in ('button','all')
            ):
                d = k.masterBindingsDict.get(key,{})
                if trace and verbose:
                    g.trace('key',key,'name',name,'stroke',stroke,'stroke in d.keys',stroke in d)
                    # g.trace(key,'keys',g.listToString(list(d.keys()),sort=True)) # [:5])
                if d:
                    b = d.get(stroke)
                    if b:
                        if trace: g.trace('%s found %s = %s' % (key,repr(b.stroke),b.commandName))
                        return b
    #@-node:ekr.20091230094319.6240:getPaneBinding
    #@+node:ekr.20061031131434.152:handleMiniBindings
    def handleMiniBindings (self,event,state,stroke):

        k = self ; c = k.c
        trace = False or (self.trace_masterKeyHandler and not g.app.unitTesting)

        # Special case for bindings handled in k.getArg:
        if state in ('getArg','full-command'):
            if stroke in ('BackSpace','Return','Tab','\t','Escape'):
                return False

        if not state.startswith('auto-'):
            # New in Leo 4.5: ignore plain key binding in the minibuffer.
            if not stroke or k.isPlainKey(stroke):
                if trace: g.trace('plain key',stroke)
                return False
            # New in Leo 4.5: The minibuffer inherits 'text' and 'all' bindings
            # for all single-line editing commands.
            for pane in ('mini','all','text'):
                d = k.masterBindingsDict.get(pane)
                if d:
                    b = d.get(stroke)
                    if b:
                        if b.commandName == 'replace-string' and state == 'getArg':
                            if trace: g.trace('%s binding for replace-string' % (pane),stroke)
                            return False # Let getArg handle it.
                        elif b.commandName not in k.singleLineCommandList:
                            if trace: g.trace('%s binding terminates minibuffer' % (pane),b.commandName,stroke)
                            k.keyboardQuit(event,hideTabs=True)
                        else:
                            if trace: g.trace(repr(stroke),'mini binding',b.commandName)
                            c.minibufferWantsFocusNow() # New in Leo 4.5.
                        # Pass this on for macro recording.
                        k.masterCommand(event,b.func,stroke,b.commandName)
                        # Careful: the command could exit.
                        if c.exists and not k.silentMode:
                            c.minibufferWantsFocus()
                        return True

        return False
    #@-node:ekr.20061031131434.152:handleMiniBindings
    #@+node:ekr.20080510095819.1:k.handleUnboudKeys
    def handleUnboundKeys (self,event,char,keysym,stroke):

        trace = False and not g.unitTesting
        k = self ; c = k.c
        modesTuple = ('insert','overwrite')

        if trace: g.trace('ch: %s keysym: %s stroke %s' % (
            repr(event.char),repr(event.keysym),repr(stroke)))

        if k.unboundKeyAction == 'command':
            # Ignore all unbound characters in command mode.
            w = g.app.gui.get_focus(c)
            if w and g.app.gui.widget_name(w).lower().startswith('canvas'):
                c.onCanvasKey(event)
            return 'break'

        elif k.isFKey(stroke):
            return 'break'

        elif stroke and k.isPlainKey(stroke) and k.unboundKeyAction in modesTuple:
            # insert/overwrite normal character.  <Return> is *not* a normal character.
            if trace: g.trace('plain key in insert mode',repr(stroke))
            return k.masterCommand(event,func=None,stroke=stroke,commandName=None)

        elif k.ignore_unbound_non_ascii_keys and len(char) > 1:
            # (stroke.find('Alt+') > -1 or stroke.find('Ctrl+') > -1)):
            if trace: g.trace('ignoring unbound non-ascii key')
            return 'break'

        elif (
            keysym and keysym.find('Escape') != -1 or
            stroke and stroke.find('Insert') != -1
        ):
            # Never insert escape or insert characters.
            return 'break'

        else:
            if trace: g.trace(repr(stroke),'no func')
            return k.masterCommand(event,func=None,stroke=stroke,commandName=None)
    #@-node:ekr.20080510095819.1:k.handleUnboudKeys
    #@-node:ekr.20061031131434.146:masterKeyHandler & helpers
    #@+node:ekr.20061031131434.153:masterClickHandler
    def masterClickHandler (self,event,func=None):

        g.app.gui.killPopupMenu()

        k = self ; c = k.c ; gui = g.app.gui
        if not event: return
        w = event.widget ; wname = c.widget_name(w)
        trace = not g.app.unitTesting and (False or k.trace_masterClickHandler)

        if trace: g.trace(wname,func and func.__name__)
        # c.frame.body.colorizer.interrupt() # New in 4.4.1

        # A click outside the minibuffer terminates any state.
        if k.inState() and w != c.frame.miniBufferWidget:
            if not c.widget_name(w).startswith('log'):
                k.keyboardQuit(event,hideTabs=False)
                # k.endMode(event) # Less drastic than keyboard-quit.
                w and c.widgetWantsFocusNow(w)
                if trace: g.trace('inState: break')
                return 'break'

        # Update the selection point immediately for updateStatusLine.
        k.previousSelection = None
        if wname.startswith('body'):
            c.frame.body.onClick(event) # New in Leo 4.4.2.
        elif wname.startswith('mini'):
            x,y = gui.eventXY(event)
            x = w.xyToPythonIndex(x,y)
            i,j = k.getEditableTextRange()
            if i <= x <= j:
                w.setSelectionRange(x,x,insert=x)
            else:
                if trace: g.trace('2: break')
                return 'break'
        if event and func:
            if trace: g.trace(func.__name__)
            val = func(event) # Don't even *think* of overriding this.
            if trace: g.trace('val:',val,g.callers())
            return val
        else:
            # All tree callbacks have a func, so we can't be in the tree.
            # g.trace('*'*20,'auto-deactivate tree: %s' % wname)
            c.frame.tree.OnDeactivate()
            c.widgetWantsFocusNow(w)
            if trace: g.trace('end: None')
            return None

    masterClick3Handler = masterClickHandler
    masterDoubleClick3Handler = masterClickHandler
    #@-node:ekr.20061031131434.153:masterClickHandler
    #@+node:ekr.20061031131434.154:masterDoubleClickHandler
    def masterDoubleClickHandler (self,event,func=None):

        k = self ; c = k.c ; w = event and event.widget

        if c.config.getBool('trace_masterClickHandler'):
            g.trace(c.widget_name(w),func and func.__name__)

        if event and func:
            # Don't event *think* of overriding this.
            return func(event)
        else:
            gui = g.app.gui
            x,y = gui.eventXY(event)
            i = w.xyToPythonIndex(x,y)
            s = w.getAllText()
            start,end = g.getWord(s,i)
            w.setSelectionRange(start,end)
            return 'break'
    #@-node:ekr.20061031131434.154:masterDoubleClickHandler
    #@+node:ekr.20061031131434.155:masterMenuHandler
    def masterMenuHandler (self,stroke,func,commandName):

        k = self ; c = k.c
        w = c.frame.getFocus()

        # g.trace('focus',w)
        # g.trace('stroke',stroke,'func',func and func.__name__,commandName,g.callers())

        # Create a minimal event for commands that require them.
        event = g.Bunch(c=c,char='',keysym='',widget=w)

        if stroke:
            return k.masterKeyHandler(event,stroke=stroke)
        else:
            return k.masterCommand(event,func,stroke,commandName)
    #@-node:ekr.20061031131434.155:masterMenuHandler
    #@-node:ekr.20061031131434.145:Master event handlers (keyHandler)
    #@+node:ekr.20061031170011.3:Minibuffer (keyHandler)
    # These may be overridden, but this code is now gui-independent.
    #@nonl
    #@+node:ekr.20061031131434.135:k.minibufferWantsFocus/Now
    def minibufferWantsFocus(self):

        c = self.c
        c.widgetWantsFocus(c.miniBufferWidget)


    def minibufferWantsFocusNow(self):

        c = self.c
        c.widgetWantsFocusNow(c.miniBufferWidget)
    #@-node:ekr.20061031131434.135:k.minibufferWantsFocus/Now
    #@+node:ekr.20061031170011.5:getLabel
    def getLabel (self,ignorePrompt=False):

        k = self ; w = self.widget
        if not w: return ''

        s = w.getAllText()

        if ignorePrompt:
            return s[len(k.mb_prefix):]
        else:
            return s or ''
    #@-node:ekr.20061031170011.5:getLabel
    #@+node:ekr.20080408060320.791:k.killLine
    def killLine (self,protect=True):

        k = self ; c = self.c
        w = self.widget
        s = w.getAllText()
        s = s[:len(k.mb_prefix)]

        w.setAllText(s)
        n = len(s)
        w.setSelectionRange(n,n,insert=n)

        if protect:
            k.mb_prefix = s
    #@-node:ekr.20080408060320.791:k.killLine
    #@+node:ekr.20061031170011.6:protectLabel
    def protectLabel (self):

        k = self ; w = self.widget
        if not w: return

        k.mb_prefix = w.getAllText()

    #@-node:ekr.20061031170011.6:protectLabel
    #@+node:ekr.20061031170011.7:resetLabel
    def resetLabel (self):

        k = self ; w = self.widget
        k.setLabelGrey('')
        k.mb_prefix = ''

        if w:    
            w.setSelectionRange(0,0,insert=0)
            state = k.unboundKeyAction
            k.setLabelBlue(label='%s State' % (state.capitalize()),protect=True)
    #@-node:ekr.20061031170011.7:resetLabel
    #@+node:ekr.20061031170011.8:setLabel
    def setLabel (self,s,protect=False):

        trace = (False or self.trace_minibuffer) and not g.app.unitTesting
        k = self ; c = k.c ; w = self.widget
        if not w: return

        if trace: g.trace(repr(s),g.callers(4))

        w.setAllText(s)
        n = len(s)
        w.setSelectionRange(n,n,insert=n)

        if protect:
            k.mb_prefix = s
    #@-node:ekr.20061031170011.8:setLabel
    #@+node:ekr.20061031170011.9:extendLabel
    def extendLabel(self,s,select=False,protect=False):

        k = self ; c = k.c ; w = self.widget
        if not w: return
        trace = self.trace_minibuffer and not g.app.unitTesting

        trace and g.trace('len(s)',len(s))
        if not s: return

        c.widgetWantsFocusNow(w)
        w.insert('end',s)

        if select:
            i,j = k.getEditableTextRange()
            w.setSelectionRange(i,j,insert=j)

        if protect:
            k.protectLabel()
    #@-node:ekr.20061031170011.9:extendLabel
    #@+node:ekr.20080408060320.790:selectAll
    def selectAll (self):

        '''Select all the user-editable text of the minibuffer.'''

        w = self.widget
        i,j = self.getEditableTextRange()
        w.setSelectionRange(i,j,insert=j)


    #@-node:ekr.20080408060320.790:selectAll
    #@+node:ekr.20061031170011.10:setLabelBlue
    def setLabelBlue (self,label=None,protect=False):

        k = self ; w = k.widget
        if not w: return

        w.setBackgroundColor('lightblue')

        if label is not None:
            k.setLabel(label,protect)
    #@-node:ekr.20061031170011.10:setLabelBlue
    #@+node:ekr.20061031170011.11:setLabelGrey
    def setLabelGrey (self,label=None):

        k = self ; w = self.widget
        if not w: return

        w.setBackgroundColor('lightgrey')

        if label is not None:
            k.setLabel(label)

    setLabelGray = setLabelGrey
    #@-node:ekr.20061031170011.11:setLabelGrey
    #@+node:ekr.20080510153327.2:setLabelRed
    def setLabelRed (self,label=None,protect=False):

        k = self ; w = self.widget
        if not w: return

        w.setForegroundColor('red')

        if label is not None:
            k.setLabel(label,protect)
    #@-node:ekr.20080510153327.2:setLabelRed
    #@+node:ekr.20061031170011.12:updateLabel
    def updateLabel (self,event):

        '''Mimic what would happen with the keyboard and a Text editor
        instead of plain accumalation.'''

        trace = False or self.trace_minibuffer and not g.app.unitTesting
        k = self ; c = k.c ; w = self.widget
        ch = (event and event.char) or ''
        keysym = (event and event.keysym) or ''

        trace and g.trace('ch',ch,'keysym',keysym,'k.stroke',k.stroke)
        # g.trace(g.callers())

        if ch and ch not in ('\n','\r'):
            c.widgetWantsFocusNow(w)
            i,j = w.getSelectionRange()
            ins = w.getInsertPoint()
            # g.trace(i,j,ins)
            if i != j:
                w.delete(i,j)
            if ch == '\b':
                s = w.getAllText()
                if len(s) > len(k.mb_prefix):
                    w.delete(i-1)
                    i-=1
            else:
                w.insert(ins,ch)
                i = ins+1
    #@nonl
    #@-node:ekr.20061031170011.12:updateLabel
    #@+node:ekr.20061031170011.13:getEditableTextRange
    def getEditableTextRange (self):

        k = self ; w = self.widget
        trace = self.trace_minibuffer and not g.app.unitTesting

        s = w.getAllText()
        # g.trace(len(s),repr(s))

        i = len(k.mb_prefix)
        j = len(s)

        trace and g.trace(i,j)
        return i,j
    #@-node:ekr.20061031170011.13:getEditableTextRange
    #@-node:ekr.20061031170011.3:Minibuffer (keyHandler)
    #@+node:ekr.20061031131434.156:Modes
    #@+node:ekr.20061031131434.157:badMode
    def badMode(self,modeName):

        k = self

        k.clearState()
        if modeName.endswith('-mode'): modeName = modeName[:-5]
        k.setLabelGrey('@mode %s is not defined (or is empty)' % modeName)
    #@-node:ekr.20061031131434.157:badMode
    #@+node:ekr.20061031131434.158:createModeBindings
    def createModeBindings (self,modeName,d,w):

        '''Create mode bindings for the named mode using dictionary d for w, a text widget.'''

        k = self ; c = k.c

        for commandName in d:
            if commandName in ('*entry-commands*','*command-prompt*'):
                # These are special-purpose dictionary entries.
                continue
            func = c.commandsDict.get(commandName)
            if not func:
                g.es_print('no such command:',commandName,'Referenced from',modeName)
                continue
            bunchList = d.get(commandName,[])
            for bunch in bunchList:
                stroke = bunch.val
                # Important: bunch.val is a stroke returned from k.strokeFromSetting.
                # Do not call k.strokeFromSetting again here!
                if stroke and stroke not in ('None','none',None):
                    if 0:
                        g.trace(
                            g.app.gui.widget_name(w), modeName,
                            '%10s' % (stroke),
                            '%20s' % (commandName),
                            bunch.nextMode)

                    k.makeMasterGuiBinding(stroke)

                    # Create the entry for the mode in k.masterBindingsDict.
                    # Important: this is similar, but not the same as k.bindKeyToDict.
                    # Thus, we should **not** call k.bindKey here!
                    d2 = k.masterBindingsDict.get(modeName,{})
                    d2 [stroke] = g.Bunch(
                        commandName=commandName,
                        func=func,
                        nextMode=bunch.nextMode,
                        stroke=stroke)
                    k.masterBindingsDict [ modeName ] = d2
    #@-node:ekr.20061031131434.158:createModeBindings
    #@+node:ekr.20061031131434.159:endMode
    def endMode(self,event):

        k = self ; c = k.c

        w = g.app.gui.get_focus(c)
        if w:
            c.frame.log.deleteTab('Mode') # Changes focus to the body pane

        k.endCommand(event,k.stroke)
        k.inputModeName = None
        k.clearState()
        k.resetLabel()
        k.showStateAndMode() # Restores focus.

        if w:
            c.widgetWantsFocusNow(w)
    #@-node:ekr.20061031131434.159:endMode
    #@+node:ekr.20061031131434.160:enterNamedMode
    def enterNamedMode (self,event,commandName):

        k = self ; c = k.c
        modeName = commandName[6:]
        c.inCommand = False # Allow inner commands in the mode.
        k.generalModeHandler(event,modeName=modeName)
    #@-node:ekr.20061031131434.160:enterNamedMode
    #@+node:ekr.20061031131434.161:exitNamedMode
    def exitNamedMode (self,event):

        k = self

        if k.inState():
            k.endMode(event)

        k.showStateAndMode()
    #@-node:ekr.20061031131434.161:exitNamedMode
    #@+node:ekr.20061031131434.162:generalModeHandler
    def generalModeHandler (self,event,
        commandName=None,func=None,modeName=None,nextMode=None,prompt=None):

        '''Handle a mode defined by an @mode node in leoSettings.leo.'''

        k = self ; c = k.c
        state = k.getState(modeName)
        trace = False or c.config.getBool('trace_modes')

        if trace: g.trace(modeName,'state',state)

        if state == 0:
            k.inputModeName = modeName
            k.modePrompt = prompt or modeName
            k.modeWidget = event and event.widget
            k.setState(modeName,1,handler=k.generalModeHandler)
            self.initMode(event,modeName)
            # Careful: k.initMode can execute commands that will destroy a commander.
            if g.app.quitting or not c.exists: return 'break'
            if not k.silentMode:
                if c.config.getBool('showHelpWhenEnteringModes'):
                    k.modeHelp(event)
                else:
                    c.frame.log.hideTab('Mode')
        elif not func:
            g.trace('No func: improper key binding')
            return 'break'
        else:
            if commandName == 'mode-help':
                func(event)
            else:
                savedModeName = k.inputModeName # Remember this: it may be cleared.
                self.endMode(event)
                if trace or c.config.getBool('trace_doCommand'): g.trace(func.__name__)
                # New in 4.4.1 b1: pass an event describing the original widget.
                if event:
                    event.widget = k.modeWidget
                else:
                    event = g.Bunch(widget = k.modeWidget)
                if trace: g.trace(modeName,'state',state,commandName,'nextMode',nextMode)
                func(event)
                if g.app.quitting or not c.exists:
                    return 'break'
                if nextMode in (None,'none'):
                    # Do *not* clear k.inputModeName or the focus here.
                    # func may have put us in *another* mode.
                    pass
                elif nextMode == 'same':
                    silent = k.silentMode
                    k.setState(modeName,1,handler=k.generalModeHandler)
                    self.reinitMode(modeName) # Re-enter this mode.
                    k.silentMode = silent
                else:
                    k.silentMode = False # All silent modes must do --> set-silent-mode.
                    self.initMode(event,nextMode) # Enter another mode.
                    # Careful: k.initMode can execute commands that will destroy a commander.
                    if g.app.quitting or not c.exists: return 'break'

        return 'break'
    #@-node:ekr.20061031131434.162:generalModeHandler
    #@+node:ekr.20061031131434.163:initMode
    def initMode (self,event,modeName):

        k = self ; c = k.c
        trace = c.config.getBool('trace_modes')

        if not modeName:
            g.trace('oops: no modeName')
            return

        d = g.app.config.modeCommandsDict.get('enter-'+modeName)
        if not d:
            self.badMode(modeName)
            return
        else:
            k.modeBindingsDict = d
            prompt = d.get('*command-prompt*') or modeName
            if trace: g.trace('modeName',modeName,prompt,'d.keys()',list(d.keys()))

        k.inputModeName = modeName
        k.silentMode = False

        entryCommands = d.get('*entry-commands*',[])
        if entryCommands:
            for commandName in entryCommands:
                if trace: g.trace('entry command:',commandName)
                k.simulateCommand(commandName)
                # Careful, the command can kill the commander.
                if g.app.quitting or not c.exists: return
                # New in Leo 4.5: a startup command can immediately transfer to another mode.
                if commandName.startswith('enter-'):
                    if trace: g.trace('redirect to mode',commandName)
                    return

        # Create bindings after we know whether we are in silent mode.
        w = g.choose(k.silentMode,k.modeWidget,k.widget)
        k.createModeBindings(modeName,d,w)
        k.showStateAndMode(prompt=prompt)
    #@-node:ekr.20061031131434.163:initMode
    #@+node:ekr.20061031131434.164:reinitMode
    def reinitMode (self,modeName):

        k = self ; c = k.c

        d = k.modeBindingsDict

        k.inputModeName = modeName
        w = g.choose(k.silentMode,k.modeWidget,k.widget)
        k.createModeBindings(modeName,d,w)

        if k.silentMode:
            k.showStateAndMode()
        else:
            # Do not set the status line here.
            k.setLabelBlue(modeName+': ',protect=True)

    #@-node:ekr.20061031131434.164:reinitMode
    #@+node:ekr.20061031131434.165:modeHelp & helper
    def modeHelp (self,event):

        '''The mode-help command.

        A possible convention would be to bind <Tab> to this command in most modes,
        by analogy with tab completion.'''

        k = self ; c = k.c

        c.endEditing()

        g.trace(k.inputModeName)

        if k.inputModeName:
            d = g.app.config.modeCommandsDict.get('enter-'+k.inputModeName)
            k.modeHelpHelper(d)

        if not k.silentMode:
            c.minibufferWantsFocus()

        return 'break'
    #@+node:ekr.20061031131434.166:modeHelpHelper
    def modeHelpHelper (self,d):

        k = self ; c = k.c ; tabName = 'Mode'
        c.frame.log.clearTab(tabName)
        data = [] ; n = 20
        for key in sorted(d):
            if key not in ( '*entry-commands*','*command-prompt*'):
                bunchList = d.get(key)
                for bunch in bunchList:
                    shortcut = bunch.val
                    if shortcut not in (None,'None'):
                        s1 = key ; s2 = k.prettyPrintKey(shortcut,brief=True)
                        n = max(n,len(s1))
                        data.append((s1,s2),)

        data.sort()

        modeName = k.inputModeName.replace('-',' ')
        if modeName.endswith('mode'): modeName = modeName[:-4].strip()

        g.es('','%s mode\n\n' % modeName,tabName=tabName)

        # This isn't perfect in variable-width fonts.
        for s1,s2 in data:
            g.es('','%*s %s' % (n,s1,s2),tabName=tabName)
    #@-node:ekr.20061031131434.166:modeHelpHelper
    #@-node:ekr.20061031131434.165:modeHelp & helper
    #@-node:ekr.20061031131434.156:Modes
    #@+node:ekr.20061031131434.167:Shared helpers
    #@+node:ekr.20061031131434.175:k.computeCompletionList
    # Important: this code must not change mb_tabListPrefix.  Only doBackSpace should do that.

    def computeCompletionList (self,defaultTabList,backspace):

        k = self ; c = k.c ; s = k.getLabel() ; tabName = 'Completion'
        command = s [len(k.mb_prompt):]
            # s always includes prefix, so command is well defined.

        k.mb_tabList,common_prefix = g.itemsMatchingPrefixInList(command,defaultTabList)
        c.frame.log.clearTab(tabName)

        if k.mb_tabList:
            k.mb_tabListIndex = -1 # The next item will be item 0.

            if not backspace:
                k.setLabel(k.mb_prompt + common_prefix)

            inverseBindingDict = k.computeInverseBindingDict()
            data = [] ; n1 = 20; n2 = 4
            for commandName in k.mb_tabList:
                dataList = inverseBindingDict.get(commandName,[('',''),])
                for z in dataList:
                    pane,key = z
                    s1 = commandName
                    s2 = pane
                    s3 = k.prettyPrintKey(key)
                    n1 = max(n1,len(s1))
                    n2 = max(n2,len(s2))
                    data.append((s1,s2,s3),)
            aList = '\n'.join(
                ['%*s %*s %s' % (-(min(20,n1)),s1,n2,s2,s3)
                    for s1,s2,s3 in data])
            g.es('',aList,tabName=tabName)
        c.bodyWantsFocus()
    #@-node:ekr.20061031131434.175:k.computeCompletionList
    #@+node:ekr.20061031131434.176:computeInverseBindingDict
    def computeInverseBindingDict (self):

        k = self ; d = {}

        # keys are minibuffer command names, values are shortcuts.
        for shortcut in k.bindingsDict:
            bunchList = k.bindingsDict.get(shortcut,[])
            for b in bunchList:
                shortcutList = d.get(b.commandName,[])
                bunchList = k.bindingsDict.get(shortcut,[g.Bunch(pane='all')])
                for b in bunchList:
                    #pane = g.choose(b.pane=='all','','%s:' % (b.pane))
                    pane = '%s:' % (b.pane)
                    data = (pane,shortcut)
                    if data not in shortcutList:
                        shortcutList.append(data)

                d [b.commandName] = shortcutList

        return d
    #@-node:ekr.20061031131434.176:computeInverseBindingDict
    #@+node:ekr.20061031131434.168:getFileName & helpers
    def getFileName (self,event=None,handler=None,prefix='',filterExt='.leo'):

        '''Similar to k.getArg, but uses completion to indicate files on the file system.'''

        k = self ; c = k.c ; gui = g.app.gui
        tag = 'getFileName' ; state = k.getState(tag)
        tabName = 'Completion'
        keysym = gui.eventKeysym(event)
        # g.trace('state',state,'keysym',keysym)
        if state == 0:
            k.arg = ''
            #@        << init altX vars >>
            #@+node:ekr.20061031131434.169:<< init altX vars >>
            k.filterExt = filterExt
            k.mb_prefix = (prefix or k.getLabel())
            k.mb_prompt = prefix or k.getLabel()
            k.mb_tabList = []

            # Clear the list: any non-tab indicates that a new prefix is in effect.
            theDir = g.os_path_finalize(os.curdir)
            k.extendLabel(theDir,select=False,protect=False)

            k.mb_tabListPrefix = k.getLabel()
            #@-node:ekr.20061031131434.169:<< init altX vars >>
            #@nl
            # Set the states.
            k.getFileNameHandler = handler
            k.setState(tag,1,k.getFileName)
            k.afterArgWidget = event and event.widget or c.frame.body.bodyCtrl
            c.frame.log.clearTab(tabName)
            c.minibufferWantsFocusNow()
        elif keysym == 'Return':
            k.arg = k.getLabel(ignorePrompt=True)
            handler = k.getFileNameHandler
            c.frame.log.deleteTab(tabName)
            if handler: handler(event)
        elif keysym in ('Tab','\t'):
            k.doFileNameTab()
            c.minibufferWantsFocus()
        elif keysym == 'BackSpace':
            k.doFileNameBackSpace() 
            c.minibufferWantsFocus()
        else:
            k.doFileNameChar(event)
        return 'break'
    #@+node:ekr.20061031131434.170:k.doFileNameBackSpace
    def doFileNameBackSpace (self):

        '''Cut back to previous prefix and update prefix.'''

        k = self ; c = k.c

        if 0:
            g.trace(
                len(k.mb_tabListPrefix) > len(k.mb_prefix),
                repr(k.mb_tabListPrefix),repr(k.mb_prefix))

        if len(k.mb_tabListPrefix) > len(k.mb_prefix):
            k.mb_tabListPrefix = k.mb_tabListPrefix [:-1]
            k.setLabel(k.mb_tabListPrefix)
    #@-node:ekr.20061031131434.170:k.doFileNameBackSpace
    #@+node:ekr.20061031131434.171:k.doFileNameChar
    def doFileNameChar (self,event):

        k = self

        # Clear the list, any other character besides tab indicates that a new prefix is in effect.
        k.mb_tabList = []
        k.updateLabel(event)
        k.mb_tabListPrefix = k.getLabel()

        common_prefix = k.computeFileNameCompletionList()

        if k.mb_tabList:
            k.setLabel(k.mb_prompt + common_prefix)
        else:
            # Restore everything.
            old = k.getLabel(ignorePrompt=True)[:-1]
            k.setLabel(k.mb_prompt + old)
    #@-node:ekr.20061031131434.171:k.doFileNameChar
    #@+node:ekr.20061031131434.172:k.doFileNameTab
    def doFileNameTab (self):

        k = self
        common_prefix = k.computeFileNameCompletionList()

        if k.mb_tabList:
            k.setLabel(k.mb_prompt + common_prefix)
    #@-node:ekr.20061031131434.172:k.doFileNameTab
    #@+node:ekr.20061031131434.173:k.computeFileNameCompletionList
    # This code must not change mb_tabListPrefix.
    def computeFileNameCompletionList (self):

        k = self ; c = k.c ; s = k.getLabel() ; tabName = 'Completion'
        path = k.getLabel(ignorePrompt=True)
        sep = os.path.sep
        tabList = []
        for f in glob.glob(path+'*'):
            if g.os_path_isdir(f):
                tabList.append(f + sep)
            else:
                junk,ext = g.os_path_splitext(f)
                if not ext or ext == k.filterExt:
                    tabList.append(f)
        k.mb_tabList = tabList
        junk,common_prefix = g.itemsMatchingPrefixInList(path,tabList)
        if tabList:
            c.frame.log.clearTab(tabName)
            k.showFileNameTabList()
        return common_prefix
    #@-node:ekr.20061031131434.173:k.computeFileNameCompletionList
    #@+node:ekr.20061031131434.174:k.showFileNameTabList
    def showFileNameTabList (self):

        k = self ; tabName = 'Completion'

        for path in k.mb_tabList:
            theDir,fileName = g.os_path_split(path)
            s = g.choose(path.endswith('\\'),theDir,fileName)
            s = fileName or g.os_path_basename(theDir) + '\\'
            g.es('',s,tabName=tabName)
    #@-node:ekr.20061031131434.174:k.showFileNameTabList
    #@-node:ekr.20061031131434.168:getFileName & helpers
    #@+node:ekr.20061031131434.179:getShortcutForCommand/Name (should return lists)
    def getShortcutForCommandName (self,commandName):

        k = self ; c = k.c

        command = c.commandsDict.get(commandName)

        if command:
            for key in k.bindingsDict:
                bunchList = k.bindingsDict.get(key,[])
                for b in bunchList:
                    if b.commandName == commandName:
                        return k.tkbindingFromStroke(key)
        return ''

    def getShortcutForCommand (self,command):

        k = self ; c = k.c

        if command:
            for key in k.bindingsDict:
                bunchList = k.bindingsDict.get(key,[])
                for b in bunchList:
                    if b.commandName == command.__name__:
                         return k.tkbindingFromStroke(key)
        return ''
    #@-node:ekr.20061031131434.179:getShortcutForCommand/Name (should return lists)
    #@+node:ekr.20061031131434.177:k.doBackSpace (minibuffer)
    # Used by getArg and fullCommand.

    def doBackSpace (self,defaultCompletionList,completion=True):

        '''Cut back to previous prefix and update prefix.'''

        trace = False and not g.unitTesting
        k = self ; c = k.c ; w = self.widget

        # Step 1: actually delete the character.
        ins = w.getInsertPoint()
        s = w.getAllText()

        if trace: g.trace('ins',ins,'k.mb_prefix',repr(k.mb_prefix),
            'w',w)

        if ins <= len(k.mb_prefix):
            # g.trace('at start')
            return
        i,j = w.getSelectionRange()
        if i == j:
            ins -= 1
            w.delete(ins)
            w.setSelectionRange(ins,ins,insert=ins)
        else:
            ins = i
            w.delete(i,j)
            w.setSelectionRange(i,i,insert=ins)

        # Step 2: compute completions.
        if not completion: return
        k.mb_tabListPrefix = w.getAllText()
        k.computeCompletionList(defaultCompletionList,backspace=True)
    #@-node:ekr.20061031131434.177:k.doBackSpace (minibuffer)
    #@+node:ekr.20061031131434.178:k.doTabCompletion
    # Used by getArg and fullCommand.

    def doTabCompletion (self,defaultTabList,redraw=True):

        '''Handle tab completion when the user hits a tab.'''

        k = self ; c = k.c ; s = k.getLabel().strip()

        if k.mb_tabList and s.startswith(k.mb_tabListPrefix):
            # g.trace('cycle',repr(s))
            # Set the label to the next item on the tab list.
            k.mb_tabListIndex +=1
            if k.mb_tabListIndex >= len(k.mb_tabList):
                k.mb_tabListIndex = 0
            k.setLabel(k.mb_prompt + k.mb_tabList [k.mb_tabListIndex])
        else:
            if redraw:
                k.computeCompletionList(defaultTabList,backspace=False)

        c.minibufferWantsFocusNow()
    #@-node:ekr.20061031131434.178:k.doTabCompletion
    #@+node:ekr.20061031131434.180:traceBinding
    def traceBinding (self,bunch,shortcut,w):

        k = self ; c = k.c ; gui = g.app.gui

        if not c.config.getBool('trace_bindings'): return

        theFilter = c.config.getString('trace_bindings_filter') or ''
        if theFilter and shortcut.lower().find(theFilter.lower()) == -1: return

        pane_filter = c.config.getString('trace_bindings_pane_filter')

        if not pane_filter or pane_filter.lower() == bunch.pane:
             g.trace(bunch.pane,shortcut,bunch.commandName,gui.widget_name(w))
    #@-node:ekr.20061031131434.180:traceBinding
    #@-node:ekr.20061031131434.167:Shared helpers
    #@+node:ekr.20061031131434.181:Shortcuts (keyHandler)
    #@+node:ekr.20090518072506.8494:isFKey
    def isFKey (self,shortcut):


        if not shortcut: return False

        s = shortcut.lower()

        return s.startswith('f') and len(s) <= 3 and s[1:].isdigit()
    #@-node:ekr.20090518072506.8494:isFKey
    #@+node:ekr.20061031131434.182:isPlainKey
    def isPlainKey (self,shortcut):

        '''Return true if the shortcut refers to a plain (non-Alt,non-Ctl) key.'''

        k = self ; shortcut = shortcut or ''

        # altgr combos (Alt+Ctrl) are always plain keys
        if shortcut.startswith('Alt+Ctrl+') and not self.enable_alt_ctrl_bindings:
            return True

        for s in ('Alt','Ctrl','Command','Meta'):
            if shortcut.find(s) != -1:            
                return False
        else:
            # Careful, allow bare angle brackets for unit tests.
            if shortcut.startswith('<') and shortcut.endswith('>'):
                shortcut = shortcut[1:-1]

            isPlain = (
                len(shortcut) == 1 or
                len(k.guiBindNamesInverseDict.get(shortcut,'')) == 1 or
                # A hack: allow Return to be bound to command.
                shortcut in ('Tab','\t')
            )

            # g.trace(isPlain,repr(shortcut))
            return isPlain and not self.isFKey(shortcut)
    #@-node:ekr.20061031131434.182:isPlainKey
    #@+node:ekr.20061031131434.184:shortcutFromSetting (uses k.guiBindNamesDict)
    def shortcutFromSetting (self,setting,addKey=True):

        k = self

        trace = False and not g.unitTesting
        if not setting:return None

        s = g.stripBrackets(setting.strip())
        #@    << define cmd, ctrl, alt, shift >>
        #@+node:ekr.20061031131434.185:<< define cmd, ctrl, alt, shift >>
        s2 = s.lower()

        cmd   = s2.find("cmd") >= 0     or s2.find("command") >= 0
        ctrl  = s2.find("control") >= 0 or s2.find("ctrl") >= 0
        alt   = s2.find("alt") >= 0
        shift = s2.find("shift") >= 0   or s2.find("shft") >= 0
        meta  = s2.find("meta") >= 0
        #@-node:ekr.20061031131434.185:<< define cmd, ctrl, alt, shift >>
        #@nl
        if k.swap_mac_keys and sys.platform == "darwin":
            #@        << swap cmd and ctrl keys >>
            #@+node:ekr.20061031131434.186:<< swap cmd and ctrl keys >>
            if ctrl and not cmd:
                cmd = True ; ctrl = False
            if alt and not ctrl:
                ctrl = True ; alt = False
            #@-node:ekr.20061031131434.186:<< swap cmd and ctrl keys >>
            #@nl
        #@    << convert minus signs to plus signs >>
        #@+node:ekr.20061031131434.187:<< convert minus signs to plus signs >>
        # Replace all minus signs by plus signs, except a trailing minus:
        if s.endswith('-'):
            s = s[:-1].replace('-','+') + '-'
        else:
            s = s.replace('-','+')
        #@-node:ekr.20061031131434.187:<< convert minus signs to plus signs >>
        #@nl
        #@    << compute the last field >>
        #@+node:ekr.20061031131434.188:<< compute the last field >>
        if s.endswith('+'):
            last = '+'
        else:
            fields = s.split('+') # Don't lower this field.
            last = fields and fields[-1]
            if not last:
                if not g.app.menuWarningsGiven:
                    g.pr("bad shortcut specifier:", s)
                return None

        if len(last) == 1:
            last2 = k.guiBindNamesDict.get(last) # Fix new bug introduced in 4.4b2.
            if last2:
                last = last2
            else:
                if last.isalpha():
                    if shift:
                        last = last.upper()
                        shift = False # It is Ctrl-A, not Ctrl-Shift-A.
                    else:
                        last = last.lower()
                # New in Leo 4.4.2: Alt-2 is not a key event!
                if addKey and last.isdigit():
                    last = 'Key-' + last
        else:
            # Translate from a made-up (or lowercase) name to 'official' Tk binding name.
            # This is a *one-way* translation, done only here.
            d = k.settingsNameDict
            last = d.get(last.lower(),last)
        #@-node:ekr.20061031131434.188:<< compute the last field >>
        #@nl
        #@    << compute shortcut >>
        #@+node:ekr.20061031131434.189:<< compute shortcut >>
        table = (
            (alt, 'Alt+'),
            (ctrl,'Ctrl+'),
            (cmd, 'Command+'),
            (meta,'Meta+'),
            (shift,'Shift+'),
            (True, last),
        )

        # new in 4.4b3: convert all characters to unicode first.
        shortcut = ''.join([g.toUnicode(val) for flag,val in table if flag])
        #@-node:ekr.20061031131434.189:<< compute shortcut >>
        #@nl
        if trace: g.trace('%20s %s' % (setting,shortcut))
        return shortcut

    canonicalizeShortcut = shortcutFromSetting # For compatibility.
    strokeFromSetting = shortcutFromSetting
    #@-node:ekr.20061031131434.184:shortcutFromSetting (uses k.guiBindNamesDict)
    #@+node:ekr.20061031131434.190:k.tkbindingFromStroke
    def tkbindingFromStroke (self,stroke):

        '''Convert a stroke (key to k.bindingsDict) to an actual Tk binding.'''

        stroke = g.stripBrackets(stroke)

        for a,b in (
            ('Alt+','Alt-'),
            ('Ctrl-','Control-'),
            ('Ctrl+','Control-'), # New in Leo 4.5.
            ('Meta+','Meta-'), # New in Leo 4.6
            ('Shift+','Shift-'),
            ('Command+','Command-'),
            ('DnArrow','Down'), # New in Leo 4.5.
            ('LtArrow','Left'), # New in Leo 4.5.
            ('RtArrow','Right'),# New in Leo 4.5.
            ('UpArrow','Up'),   # New in Leo 4.5.
        ):
            stroke = stroke.replace(a,b)

        # g.trace('<%s>' % stroke)
        return '<%s>' % stroke
    #@-node:ekr.20061031131434.190:k.tkbindingFromStroke
    #@+node:ekr.20061031131434.191:k.prettyPrintKey
    def prettyPrintKey (self,stroke,brief=False):

        k = self
        s = stroke and g.stripBrackets(stroke.strip())
        if not s: return ''

        shift = s.find("shift") >= 0 or s.find("shft") >= 0

        # Replace all minus signs by plus signs, except a trailing minus:
        if s.endswith('-'): s = s[:-1].replace('-','+') + '-'
        else:               s = s.replace('-','+')
        fields = s.split('+')
        last = fields and fields[-1]
        if last and len(last) == 1:
            prev = s[:-1]
            if last.isalpha():
                if last.isupper():
                    if not shift:
                        s = prev + 'Shift+' + last
                elif last.islower():
                    if not prev and not brief:
                        s = 'Key+' + last.upper()
                    else:
                        s = prev + last.upper()
        else:
            last = k.guiBindNamesInverseDict.get(last,last)
            if fields and fields[:-1]:
                s = '%s+%s' % ('+'.join(fields[:-1]),last)
            else:
                s = last
        # g.trace(stroke,s)
        return g.choose(brief,s,'<%s>' % s)
    #@-node:ekr.20061031131434.191:k.prettyPrintKey
    #@-node:ekr.20061031131434.181:Shortcuts (keyHandler)
    #@+node:ekr.20061031131434.193:States
    #@+node:ekr.20061031131434.194:clearState
    def clearState (self):

        k = self
        k.state.kind = None
        k.state.n = None
        k.state.handler = None
    #@-node:ekr.20061031131434.194:clearState
    #@+node:ekr.20061031131434.196:getState
    def getState (self,kind):

        k = self
        val = g.choose(k.state.kind == kind,k.state.n,0)
        # g.trace(state,'returns',val)
        return val
    #@-node:ekr.20061031131434.196:getState
    #@+node:ekr.20061031131434.195:getStateHandler
    def getStateHandler (self):

        return self.state.handler
    #@-node:ekr.20061031131434.195:getStateHandler
    #@+node:ekr.20061031131434.197:getStateKind
    def getStateKind (self):

        return self.state.kind
    #@-node:ekr.20061031131434.197:getStateKind
    #@+node:ekr.20061031131434.198:inState
    def inState (self,kind=None):

        k = self

        if kind:
            return k.state.kind == kind and k.state.n != None
        else:
            return k.state.kind and k.state.n != None
    #@-node:ekr.20061031131434.198:inState
    #@+node:ekr.20080511122507.4:setDefaultInputState
    def setDefaultInputState (self):

        k = self
        k.setInputState(k.defaultUnboundKeyAction)
    #@nonl
    #@-node:ekr.20080511122507.4:setDefaultInputState
    #@+node:ekr.20061031131434.133:setInputState
    def setInputState (self,state):

        k = self
        k.unboundKeyAction = state



    #@-node:ekr.20061031131434.133:setInputState
    #@+node:ekr.20061031131434.199:setState
    def setState (self,kind,n,handler=None):

        k = self
        if kind and n != None:
            k.state.kind = kind
            k.state.n = n
            if handler:
                k.state.handler = handler
        else:
            k.clearState()

        # k.showStateAndMode()
    #@-node:ekr.20061031131434.199:setState
    #@+node:ekr.20061031131434.192:showStateAndMode
    def showStateAndMode(self,w=None,prompt=None):

        k = self ; c = k.c
        state = k.unboundKeyAction
        mode = k.getStateKind()
        inOutline = False
        if not g.app.gui: return

        if not w:
            w = g.app.gui.get_focus(c)
            if not w: return

        # g.trace(w, state, mode,g.callers(5))

        # This fixes a problem with the tk gui plugin.
        if mode and mode.lower().startswith('isearch'):
            return

        wname = g.app.gui.widget_name(w).lower()

        if mode:
            if mode in ('getArg','getFileName','full-command'):
                s = None
            elif prompt:
                s = prompt
            else:
                mode = mode.strip()
                if mode.endswith('-mode'):
                    mode = mode[:-5]
                s = '%s Mode' % mode.capitalize()
        elif w and (wname.startswith('canvas') or wname.startswith('head')):
            s = 'In Outline'
            inOutline = True
        else:
            s = '%s State' % state.capitalize()

        if s:
            # g.trace(s,w,g.callers(4))
            k.setLabelBlue(label=s,protect=True)

        if w and g.app.gui.isTextWidget(w):
            k.showStateColors(inOutline,w)
    #@-node:ekr.20061031131434.192:showStateAndMode
    #@+node:ekr.20080512115455.1:showStateColors
    def showStateColors (self,inOutline,w):

        k = self ; c = k.c ; state = k.unboundKeyAction

        body = c.frame.body ; bodyCtrl = body.bodyCtrl

        if state not in ('insert','command','overwrite'):
            g.trace('bad input state',state)

        # g.trace(state,w,g.app.gui.widget_name(w),g.callers(4))

        # if inOutline and w == bodyCtrl:
            # return # Don't recolor the body.
        if w != bodyCtrl and not g.app.gui.widget_name(w).startswith('head'):
            # Don't recolor the minibuffer, log panes, etc.
            return

        if state == 'insert':
            bg = k.insert_mode_bg_color
            fg = k.insert_mode_fg_color
        elif state == 'command':
            bg = k.command_mode_bg_color
            fg = k.command_mode_fg_color
        elif state == 'overwrite':
            bg = k.overwrite_mode_bg_color
            fg = k.overwrite_mode_fg_color
        else:
            bg = fg = 'red'

        # g.trace(id(w),bg,fg,self)

        if w == bodyCtrl:
            body.setEditorColors(bg=bg,fg=fg)
        else:
            try:
                w.configure(bg=bg,fg=fg)
            except Exception:
                g.es_exception()
    #@-node:ekr.20080512115455.1:showStateColors
    #@-node:ekr.20061031131434.193:States
    #@+node:ekr.20061031131434.200:universalDispatcher & helpers
    def universalDispatcher (self,event):

        '''Handle accumulation of universal argument.'''

        #@    << about repeat counts >>
        #@+node:ekr.20061031131434.201:<< about repeat counts >>
        #@@nocolor

        #@+at  
        #@nonl
        # Any Emacs command can be given a numeric argument. Some 
        # commands interpret the
        # argument as a repetition count. For example, giving an 
        # argument of ten to the
        # key C-f (the command forward-char, move forward one 
        # character) moves forward ten
        # characters. With these commands, no argument is equivalent 
        # to an argument of
        # one. Negative arguments are allowed. Often they tell a 
        # command to move or act
        # backwards.
        # 
        # If your keyboard has a META key, the easiest way to 
        # specify a numeric argument
        # is to type digits and/or a minus sign while holding down 
        # the the META key. For
        # example,
        # 
        # M-5 C-n
        # 
        # moves down five lines. The characters Meta-1, Meta-2, and 
        # so on, as well as
        # Meta--, do this because they are keys bound to commands 
        # (digit-argument and
        # negative-argument) that are defined to contribute to an 
        # argument for the next
        # command.
        # 
        # Another way of specifying an argument is to use the C-u 
        # (universal-argument)
        # command followed by the digits of the argument. With C-u, 
        # you can type the
        # argument digits without holding down shift keys. To type a 
        # negative argument,
        # start with a minus sign. Just a minus sign normally means 
        # -1. C-u works on all
        # terminals.
        # 
        # C-u followed by a character which is neither a digit nor a 
        # minus sign has the
        # special meaning of "multiply by four". It multiplies the 
        # argument for the next
        # command by four. C-u twice multiplies it by sixteen. Thus, 
        # C-u C-u C-f moves
        # forward sixteen characters. This is a good way to move 
        # forward "fast", since it
        # moves about 1/5 of a line in the usual size screen. Other 
        # useful combinations
        # are C-u C-n, C-u C-u C-n (move down a good fraction of a 
        # screen), C-u C-u C-o
        # (make "a lot" of blank lines), and C-u C-k (kill four 
        # lines).
        # 
        # Some commands care only about whether there is an argument 
        # and not about its
        # value. For example, the command M-q (fill-paragraph) with 
        # no argument fills
        # text; with an argument, it justifies the text as well. 
        # (See section Filling
        # Text, for more information on M-q.) Just C-u is a handy 
        # way of providing an
        # argument for such commands.
        # 
        # Some commands use the value of the argument as a repeat 
        # count, but do something
        # peculiar when there is no argument. For example, the 
        # command C-k (kill-line)
        # with argument n kills n lines, including their terminating 
        # newlines. But C-k
        # with no argument is special: it kills the text up to the 
        # next newline, or, if
        # point is right at the end of the line, it kills the 
        # newline itself. Thus, two
        # C-k commands with no arguments can kill a non-blank line, 
        # just like C-k with an
        # argument of one. (See section Deletion and Killing, for 
        # more information on
        # C-k.)
        # 
        # A few commands treat a plain C-u differently from an 
        # ordinary argument. A few
        # others may treat an argument of just a minus sign 
        # differently from an argument
        # of -1. These unusual cases will be described when they 
        # come up; they are always
        # to make the individual command more convenient to use.
        #@-at
        #@-node:ekr.20061031131434.201:<< about repeat counts >>
        #@nl

        k = self ; gui = g.app.gui
        state = k.getState('u-arg')

        if state == 0:
            k.dispatchEvent = event
            # The call should set the label.
            k.setState('u-arg',1,k.universalDispatcher)
            k.repeatCount = 1
        elif state == 1:
            # stroke = k.stroke # Warning: k.stroke is always Alt-u
            keysym = gui.eventKeysym(event)
            # g.trace(state,keysym)
            if keysym == 'Escape':
                k.keyboardQuit(event)
            elif keysym == k.universalArgKey:
                k.repeatCount = k.repeatCount * 4
            elif keysym.isdigit() or keysym == '-':
                k.updateLabel(event)
            elif keysym in (
                'Alt_L','Alt_R',
                'Control_L','Control_R',
                'Meta_L','Meta_R',
                'Shift_L','Shift_R',
            ):
                 k.updateLabel(event)
            else:
                # *Anything* other than C-u, '-' or a numeral is taken to be a command.
                val = k.getLabel(ignorePrompt=True)
                try:                n = int(val) * k.repeatCount
                except ValueError:  n = 1
                k.clearState()
                event = k.dispatchEvent
                k.executeNTimes(event,n,stroke=keysym)
                k.keyboardQuit(event)
                # k.clearState()
                # k.setLabelGrey()
                if 0: # Not ready yet.
                    # This takes us to macro state.
                    # For example Control-u Control-x ( will execute the last macro and begin editing of it.
                    if stroke == '<Control-x>':
                        k.setState('uC',2,k.universalDispatcher)
                        return k.doControlU(event,stroke)
        elif state == 2:
            k.doControlU(event,stroke)

        return 'break'
    #@+node:ekr.20061031131434.202:executeNTimes
    def executeNTimes (self,event,n,stroke):

        trace = False and not g.unitTesting
        k = self

        if stroke == k.fullCommandKey:
            for z in range(n):
                k.fullCommand()
        else:
            stroke = g.stripBrackets(stroke)
            b = k.getPaneBinding(stroke,event.widget)
            if b:
                if trace: g.trace('repeat',n,'method',b.func.__name__,
                    'stroke',stroke,'widget',event.widget)
                for z in range(n):
                    # event = g.Bunch(
                        # c = self.c,
                        # widget = event.widget,
                        # keysym = event.keysym,
                        # stroke = event.stroke,
                        # char = event.char,
                    # )
                    k.masterCommand(event,b.func,'<%s>' % stroke)
            else:
                # This does nothing for Qt gui.
                w = event.widget
                for z in range(n):
                    g.app.gui.event_generate(w,'<Key>',keysym=event.keysym)
    #@-node:ekr.20061031131434.202:executeNTimes
    #@+node:ekr.20061031131434.203:doControlU
    def doControlU (self,event,stroke):

        k = self ; c = k.c
        ch = g.app.gui.eventChar(event)

        k.setLabelBlue('Control-u %s' % g.stripBrackets(stroke))

        if ch == '(':
            k.clearState()
            k.resetLabel()
            c.macroCommands.startKbdMacro(event)
            c.macroCommands.callLastKeyboardMacro(event)
    #@-node:ekr.20061031131434.203:doControlU
    #@-node:ekr.20061031131434.200:universalDispatcher & helpers
    #@-others
#@nonl
#@-node:ekr.20061031131434.74:class keyHandlerClass
#@-others
#@-node:ekr.20061031131434:@thin leoKeys.py
#@-leo
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.