#@+leo-ver=4-thin
#@+node:ekr.20080508083640.10:@thin key-handling-notes.txt
#@+all
#@+node:ekr.20080510121331.18:Vim notes
@nocolor

Other possible vim features:

- search-and-replace within the current selection,
- and to some extent macro recording and running.
- Support vim-specific features like '.', etc.
#@+node:ekr.20080508083640.16:Posting: New key handling
@nocolor

The "Huge Focus Aha", namely that focus never needs to be in the
outline pane, continues to clarify and guide my thinking:

1. There is no need for per-pane key bindings.  I'll be discussing
this in more detail as we go along.  This is itself a major
simplification.

2. Ville has discovered the heart of the matter: most modes should
continue until an unknown character is seen.  An **unknown character
for a mode** is a character for which no explicit binding has been
made for that mode.  Leo will enter a **main** (or default) mode when
the user types an unknown (unbound) character in a mode.

The main mode would be normal text-input mode for the emacs-like
bindings, and a "command" mode for vim-like bindings.  Leo already
allows the user to specify emacs-like or vim-like defaults via various
settings.  I forget the details, but they don't much matter in this
discussion.

There are a few complications with "stay in the mode until an unknown
character is seen".

A. Some modes, like the minibuffer, and vim input mode, will allow the
user to enter any unicode character.  Thus, we can't just list the
"known" bindings of a mode--there can be too many.  To handle this, we
need to create the distinction between **input modes** and **command
modes**.  Input modes continue until another mode is explicitly
requested.  Command modes continue until an unknown (unbound) key
sequence is seen.

B. Several modes, including the minibuffer and search modes, contain
implicit bindings for keys like <tab>, <return> and <backspace>.
Indeed, the user probably need not specify any bindings explicitly for
such modes.  This creates some interesting complications however.  Let
us distinguish between *text-editing* commands and *non-text-editing*
commands.  'beginning-of-line' is a text editing command; 'save-
outline' is not.  Commands such as sort-lines could be called text-
editing commands, but they make no sense in a single-line environment
such as the minibuffer or search/replace commands, so let us
tentatively call them non-text-editing commands, or rather **multi-
line-editing** commands.

As I write this, I realize that the distinction between text-editing
and non-text-editing commands implicitly assumes emacs-like *bindings*
for commands.  In the vim-like world of plain-key bindings, we could
imagine *temporarily* entering vim-command mode in the minibuffer, and
then *returning* to the minibuffer-insert-mode after the temporary vim-
command mode finished.  This could cause complications.  In general,
I'd rather stay away from a stack of modes, but only experimentation
will show what is needed.

C. Regardless of other details, some **basic editing commands/
bindings** surely must be supported in the minibuffer and search
commands:  cut, copy, paste, arrow-keys move the cursor, shift-arrow
keys extend the selection.  In emacs-like operation we can throw in
all text-editing bindings at essentially no cost.  In vim-like
operation, we can use the basic bindings, but more complex commands
would seem to require nested modes.

But the main point is that special-purpose modes will probably use
some reasonable, standard defaults based on settings for *other*
modes, plus some standard keys.  In other words, it may be possible to
dispense with explicit bindings for the minibuffer, etc.

That being so, we are left with a simple scheme:

1. Bindings apply **everywhere** in several senses.  They apply to all
panes, and bindings for text-editing commands apply in all text
widgets: the minibuffer, body pane, log pane, headlines, etc.

2. Binding unknown in a mode terminate the mode and return to the main
or default mode.

3. Non-text-editing commands, including multi-line-editing commands,
should terminate **single-line modes** such as minibuffer commands
unless a special case explicitly exists for them.

4. Bindings for **Leo-provided modes** will be created automatically
using the user's bindings for text-editing commands, plus special
characters like <tab>, <return> and <backspace>.  Leo provided modes
are the initial minibuffer mode and secondary minibuffer modes such
entering search patterns.

5. The only complication is possibly allowing vim-like command sub-
modes of single-line modes.

The code in k.masterKeyHandler is in the back of my mind as I write
this.  With luck, the new scheme will be considerably simpler than the
old, while being more flexible and more general.  Note that the way
bindings are actually specified is an entirely separate issue, which
is good because that logic is horrible.  Eliminating per-pane bindings
will help some, but not a lot.

We may want to allow the user to specify per-pane bindings as before,
but the bindings will be ignored.  But as I write this it seems like
this could cause a great deal of confusion.  Perhaps it will be better
to outlaw the old syntax entirely, or at least issue warnings that per-
pane bindings no longer have any effect.

This is all very preliminary.  I've written no code.  Your comments
are welcome now.
#@-node:ekr.20080508083640.16:Posting: New key handling
#@+node:ekr.20080509064108.9:Posting: More design notes
@nocolor

On May 7, 7:05 pm, "Edward K. Ream" <edream...@gmail.com> wrote:

> This post will be a "thinking out loud" discussion of the new design.

I've just spent some time drawing state diagrams that indicate transitions between various states.  It turned out to be more simple than I had feared.

> As I write this, I realize that the distinction between text-editing
> and non-text-editing commands implicitly assumes emacs-like *bindings*
> for commands.  

No.  The distinction has nothing to do with bindings, as my post earlier today shows.  In fact, after seeing the final state diagrams, my intention is to be able to support *both* emacs-like and vim-like bindings simultaneously.  Actually, Leo can already do this, or it could if @mode bindings actually worked :-)

The more useful distinction is between commands that change modes and commands that stay in the same mode.  This became clearer as I played with the diagrams.

Ok, let's see if I can summarize what the diagrams are trying to tell me without tying myself up in knots :-)

1. *All* modes are minibuffer modes.  That is, the present mode will always be indicated at the start of the minibuffer.  It would not be good to use the status area because the UNL plugin overwrites the status when changing nodes.

However, not all modes need actually *use* (alter) the minibuffer.  For example, a user-defined outline mode might provide bindings for just the arrow and shift arrow keys.  Nothing the user types would appear in the minibuffer during that mode.

2. @mode nodes define user modes.  These nodes can specify the "prompt" that will appear in the minibuffer while the node is active. For example:

@mode <mode name> @prompt = <prompt>

When a mode is active the minibuffer will contain as a **minibuffer prefix**:

<mode name> mode: <prompt>

As always, the prefix is protected: the user can not erase it by backspacing.

3. Binding are actually a completely separate topic from how Leo handles modes.  However, modes can create "virtual key sequences" that represent a command.  It would be extremely complex to generate those virtual key sequences in menus with the present code base--and probably for *any* version of Leo's config code.  To handle this, @item nodes in @settings trees could specify a "hint" that would look like a real menu shortcut.  Leo would completely ignore such hints: they would be only for cosmetic purposes.  Like this;

@item move-outline-left @hint = A-S-Left or [cmd]o,S-Left

The hint can be any string that makes sense to you.  In this case it signifies that there are two possible ways move an outline left: Alt-Shift-Left in any mode or type o followed by Shift-Left in command mode.

The advantage of this scheme is its simplicity and generality.  The disadvantage is that you, the user, must ensure that the hints make sense and are up-to-date.  I think it will turn out to be very handy.

4. With these preliminaries in place, the actual scheme for switching between modes is relatively straightforward:

A: The default (main) mode will be insert-mode for emacs-like operation and command-mode for vim-like operation.

B: In insert mode, edit-commands executed via menus or by key bindings will cause Leo to remain in insert mode.  All other commands transfer to other modes, with the default mode as the default.  So for emacs-like operation, control (focus) typically stays in the body pane, while for vim-like operation control typically stays in the minibuffer.

BTW, now that all modes can be considered minibuffer modes, we need to distinguish between modes that insert unbound keys (either in the present text pane or in the minibuffer) and those that don't.  For those modes that don't, any unbound key will transfer to the default mode.

C: In general (there may be smallish exceptions), a key stroke that would invoke an unknown command causes control to go to the default mode.

D: In user modes, all commands transfer to the default mode except for explicitly declared "stay-in-this-mode" commands.  This mechanism is already in place.

In short, key **bindings** no longer matter to k.masterKeyHandler.  All that matters is the command that is about to be executed.  This is a very nice simplification: all the old pane-specific logic should just go away.  Actually, it seems like an important Aha.

I now have hopes of putting this all in place in just a few days.

BTW, none of this is a "requirements specification".  That's not how I work :-)  What will emerge will be the result of playing with the code and seeing what works and what doesn't.

Edward
#@nonl
#@-node:ekr.20080509064108.9:Posting: More design notes
#@+node:ekr.20080509145618.4:Used by vim
@nocolor

Used by vim:

a/A (append after cursor)
c
d,dd,D (delete before cursor)
f/F/t/T
g,gf,g0,gg,ge,gE
h,j,k,l (motion commands)
i/I (insert before cursor, insert at indentation)
m,m',m` (marks)
n,N
o/O (open line) Why not just use <insert?>
r/R
x/X (delete after cursor)
y
u
v,V
w/W
ZZ,ZQ
.
:
;
+-
/ (patterns)
#@nonl
#@-node:ekr.20080509145618.4:Used by vim
#@+node:ekr.20080512125926.3:vim bindings: what I did
@nocolor

- Changed cycle-focus and cycle-all-focus commands.
- Removed Alt-T binding for focus-to-outline
- Paste in minibuffer (find/change) does not work.
  Added cut-text, copy-text, paste-text to list of single-line commands.  
- Bug: don't enter edit-headline in command mode.
- Bug: can't enter period in find.  Period is bound to auto-complete(!)
- Indicate input mode in mini-buffer.
- Ignore plain-key bindings in insert-mode.
- Enter outline mode after clone/copy-outline/Collapse all.
- Bound escape to set-command-mode.
- end-edit-headline should switch to body pane.
  Just set @bool stayInTreeAfterEditHeadline = False
- Enter command mode when editing headlines.
- Fix autocompletion.
- Don't allow unbound plain keys in command mode.
- State prompt (and Find prompt) in minibuffer is not protected.
- Tab completion wipes out full-command.
- Modes no longer put focus in minibuffer.
- Strip '-mode' from mode message in minibuffer.
- Show "In outline" in showStateAndMode.
- focus-to-body now enters default input state.
- End editing of headline leaves *body* in insert mode.
    - The actual bug was in k.setDefaultInputAction. It must use k.defaultUnboundKeyAction.
- Fixed bug: body pane changes color when headline mode changes.
- Python indentation now works.
- Ignore all unbound plain keys valid in command mode, except for plain auto-complete key.
- Insert mode does now persists in headline.
- Handled outline navigation.
- Don't honor command-mode keys if in tree.
- Reorganized and simplified leoSettings.leo.
- find-char should extend past line.
- Extended a, e commands: go further if at beginning/end of line.
- **Sometimes** the focus is both in the body pane and outline pane.
  For example, after save outline.
- Made legacy (insert mode) operation the default.
- All unit tests pass in legacy mode.
- Moving outlines with alt-shift-arrow keys messed up state reports.
- Autocompletion doesn't restore 'insert' mode when 'command' mode is the default.
- Control-g doesn't restore default input state, or doesn't recolor the body.
- Body pane is yellow initially, and prompt is "In Tree", but focus is in body!
- Initial body pane color should be yellow if outline has focus initially.
- Make sure minibuffer keeps its blue color.
- add empty bindings for clean-recent-files and sort-recent-files.
- Dot in insert mode when there are no autocompletions to suggest goes to command mode.
- Typing . at the start of a line with auto-completer enabled hangs leo(!)
  This happens in the trunk too.
- Disabled failed unit tests when command-mode is the default.
#@nonl
#@-node:ekr.20080512125926.3:vim bindings: what I did
#@-node:ekr.20080510121331.18:Vim notes
#@+node:ekr.20080514131122.2:New drawing/focus code
#@+node:ekr.20080515173014.2:What I did
What I did:

- Created new version of all c.Drawing methods when g.newDrawing is True.
- Call outerUpdate from event handlers.
- Call outerUpdate after calling k.masterKeyHandlerHelper.
- Created new versions of leoBody.color methods.
- Created and test c.bind.
- Reviewed all uses of c.redraw_now(): added calls to c.outerUpdate where needed.
- Removed new wrappers from k.masterX.
- There is a slight wiggle when opening files.  It happens with the old code.
- Add c.bind2 with 3 args.
- Added call to c.outerUpdate to g.app.closeLeoWindow.
- Fixed major bug: c.redraw and c.redraw_now must request a redraw.

Changed x.bind to c.bind or c.bind2 in the following plugins:

EditAttributes.py
UASearch.py
URLloader.py
UniversalScrolling.py
fastGotoNode.py
ironPythonGui.py
mod_scripting.py
nav_buttons.py
newButtons.py
nodebar.py
pie_menus.py
plugins_menu.py
scheduler.py
searchbox.py
toolbar.py
xcc_nodes.py
#@nonl
#@-node:ekr.20080515173014.2:What I did
#@+node:ekr.20080515170754.1:Notes
@nocolor

c.redraw_now is a bit of a problem.  Sometimes it can be replaced by c.redraw, sometimes not. I have been super conservative: I've left all calls to c.redraw_now alone, and added calls to c.outerUpdate as needed.

Later, when preliminary testing is complete, c.redraw_now() can call c.outerUpdate automatically.

c.bind(w,a,b) can call g.app.gui.bind(w,a,b) to avoid a gui-related case statement.

@color
#@nonl
#@+node:ekr.20080513174131.1:Design for simplified drawing & focus
@nocolor

- c.outerUpdate actually updates everything, using c.requestedRedraw,
  c.requestedWidget, c.requestedMinibufferPrompt. c.outerUpdate may be called
  anytime to force an immediate update. The master command handler calls
  outerUpdate, as do all event handlers that make any requests.

- g.newDrawing selects between old and new code. When g.newDrawing is False,
  c.outerUpdate does nothing. When g.newDrawing is True, many of Leo's drawing
  methods simply make requests:

    - c.beginUpdate does nothing.
    - c.endUpdate sets c.requestRedrawFlag to request a redraw.
    - c.xWantsFocus set c.requestedFocusWidget.
    - c.k.showStateAndFocus sets c.requestedMinibufferPrompt.
    - c.selectPosition sets c.requestedPosition.
    - c.recolor() sets c.frame.requestRecolorFlag.

This design might substantially simplify some very complex code. My intention is
to make *no* changes to the code base except for calls to c.outerUpdate. Thus,
it will be easy to see what has changed. The experiment can be done in the
key-handling branch.

This is a higly speculative project.  Previous attempts have failed...
#@-node:ekr.20080513174131.1:Design for simplified drawing & focus
#@-node:ekr.20080515170754.1:Notes
#@+node:ekr.20080515053412.54:regex: replace w.bind by c.bind(w...)
([ ]+)w.bind\((.*)\)(.*)$
\1c.bind(w,\2)\3

# Bind w.bind with more than two args (none found)

w.bind\(.*,.*,.*$

[-w]\.bind\(
#@nonl
#@-node:ekr.20080515053412.54:regex: replace w.bind by c.bind(w...)
#@+node:ekr.20031218072017.4017:Menus (tkBody) (May cause problems)
def bind (self,*args,**keys):

    c = self.c
    return self.bodyCtrl.bind(*args,**keys)
#@-node:ekr.20031218072017.4017:Menus (tkBody) (May cause problems)
#@+node:ekr.20080515053412.52:Changed
#@+node:ekr.20031218072017.2812:c.__init__
def __init__(self,frame,fileName,relativeFileName=None):

    c = self

    if g.newDrawing:
        self.requestedFocusWidget = None
        c.requestRecolorFlag = False
        self.requestRedrawFlag = False
        self.requestRedrawScrollFlag = False
        self.requestedIconify = '' # 'iconify','deiconify'
        g.es('Using new drawing code',color='red')

    # g.trace('Commands')
    self.exists = True # Indicate that this class exists and has not been destroyed.
        # Do this early in the startup process so we can call hooks.

    # Init ivars with self.x instead of c.x to keep Pychecker happy
    self.chapterController = None
    self.frame = frame

    self.hiddenRootNode = leoNodes.vnode(context=c)
    self.hiddenRootNode.setHeadString('<hidden root vnode>')
    self.hiddenRootNode.t.vnodeList = [self.hiddenRootNode]
    self.isZipped = False # May be set to True by g.openWithFileName.
    self.mFileName = fileName
        # Do _not_ use os_path_norm: it converts an empty path to '.' (!!)
    self.mRelativeFileName = relativeFileName

    # g.trace(c) # Do this after setting c.mFileName.
    c.initIvars()
    self.nodeHistory = nodeHistory(c)

    self.contractVisitedNodes = c.config.getBool('contractVisitedNodes')
    self.useTextMinibuffer = c.config.getBool('useTextMinibuffer')
    self.showMinibuffer = c.config.getBool('useMinibuffer')
    self.stayInTree = c.config.getBool('stayInTreeAfterSelect')
    self.fixed = c.config.getBool('fixedWindow',False)
        # New in Leo 4.5: True: Don't write window position, expansion states, marks, etc.
    self.fixedWindowPosition = c.config.getData('fixedWindowPosition')
    if self.fixedWindowPosition:
        try:
            w,h,l,t = self.fixedWindowPosition
            self.fixedWindowPosition = int(w),int(h),int(l),int(t)
        except Exception:
            g.es_print('bad @data fixedWindowPosition',repr(self.fixedWindowPosition),color='red')
    else:
        self.windowPosition = 500,700,50,50 # width,height,left,top.

    # initialize the sub-commanders.
    # c.finishCreate creates the sub-commanders for edit commands.
    self.fileCommands   = leoFileCommands.fileCommands(c)
    self.atFileCommands = leoAtFile.atFile(c)
    self.importCommands = leoImport.leoImportCommands(c)
    self.tangleCommands = leoTangle.tangleCommands(c)
    leoEditCommands.createEditCommanders(c)

    if 0:
        print ; print "*** using Null undoer ***" ; print
        self.undoer = leoUndo.nullUndoer(self)
    else:
        self.undoer = leoUndo.undoer(self)
#@-node:ekr.20031218072017.2812:c.__init__
#@+node:ekr.20031218072017.1934:run
def run(fileName=None,pymacs=None,jyLeo=False,*args,**keywords):

    """Initialize and run Leo"""

    # __pychecker__ = '--no-argsused' # keywords not used.

    # print 'leo.py:run','fileName',fileName
    if not jyLeo and not isValidPython(): return
    << import leoGlobals and leoApp >>
    g.computeStandardDirectories()
    adjustSysPath(g)
    if pymacs:
        script = windowFlag = False
    else:
        script, windowFlag = getBatchScript() # Do early so we can compute verbose next.
    verbose = script is None
    g.app.batchMode = script is not None
    g.app.silentMode = '-silent' in sys.argv or '--silent' in sys.argv
    g.app.setLeoID(verbose=verbose) # Force the user to set g.app.leoID.
    << import leoNodes and leoConfig >>
    g.app.nodeIndices = leoNodes.nodeIndices(g.app.leoID)
    g.app.config = leoConfig.configClass()
    fileName,relativeFileName = completeFileName(fileName)
    reportDirectories(verbose)
    # Read settings *after* setting g.app.config.
    # Read settings *before* opening plugins.  This means if-gui has effect only in per-file settings.
    g.app.config.readSettingsFiles(fileName,verbose)
    g.app.setEncoding()
    if pymacs:
        createNullGuiWithScript(None)
    elif jyLeo:
        import leoSwingGui
        g.app.gui = leoSwingGui.swingGui()
    elif script:
        if windowFlag:
            g.app.createTkGui() # Creates global windows.
            g.app.gui.setScript(script)
            sys.args = []
        else:
            createNullGuiWithScript(script)
        fileName = None
    # Load plugins. Plugins may create g.app.gui.
    g.doHook("start1")
    if g.app.killed: return # Support for g.app.forceShutdown.
    # Create the default gui if needed.
    if g.app.gui == None: g.app.createTkGui() # Creates global windows.
    # Initialize tracing and statistics.
    g.init_sherlock(args)
    if g.app and g.app.use_psyco: startPsyco()
    # Clear g.app.initing _before_ creating the frame.
    g.app.initing = False # "idle" hooks may now call g.app.forceShutdown.
    # Create the main frame.  Show it and all queued messages.
    c,frame = createFrame(fileName,relativeFileName)
    if not frame: return
    g.app.trace_gc          = c.config.getBool('trace_gc')
    g.app.trace_gc_calls    = c.config.getBool('trace_gc_calls')
    g.app.trace_gc_verbose  = c.config.getBool('trace_gc_verbose')
    if g.app.disableSave:
        g.es("disabling save commands",color="red")
    g.app.writeWaitingLog()
    p = c.currentPosition()
    g.doHook("start2",c=c,p=p,v=p,fileName=fileName)
    if c.config.getBool('allow_idle_time_hook'):
        g.enableIdleTimeHook()
    if not fileName:
        c.redraw_now()
    # Respect c's focus wishes if posssible.
    w = g.app.gui.get_focus(c)
    if w != c.frame.body.bodyCtrl and w != c.frame.tree.canvas:
        c.bodyWantsFocus()
        c.k.showStateAndMode(w)
    c.outerUpdate()
    g.app.gui.runMainLoop()
#@+node:ekr.20041219072112:<< import leoGlobals and leoApp >>
if jyLeo:

    print '*** starting jyLeo',sys.platform # will be something like java1.6.0_02

    ### This is a hack.
    ### The jyleo script in test.leo sets the cwd to g.app.loadDir
    ### Eventually, we will have to compute the equivalent here.

    path = os.path.join(os.getcwd()) ### ,'..','src')
    if path not in sys.path:
        print 'appending %s to sys.path' % path
        sys.path.append(path)
    if 0:
        print 'sys.path...'
        for s in sys.path: print s

# Import leoGlobals, but do NOT set g.
import leoGlobals
import leoApp

# Create the app.
leoGlobals.app = leoApp.LeoApp()

# **now** we can set g.
g = leoGlobals
assert(g.app)

if jyLeo:
    startJyleo(g)
#@-node:ekr.20041219072112:<< import leoGlobals and leoApp >>
#@+node:ekr.20041219072416.1:<< import leoNodes and leoConfig >>
import leoNodes
import leoConfig

# try:
    # import leoNodes
# except ImportError:
    # print "Error importing leoNodes.py"
    # import traceback ; traceback.print_exc()

# try:
    # import leoConfig
# except ImportError:
    # print "Error importing leoConfig.py"
    # import traceback ; traceback.print_exc()
#@-node:ekr.20041219072416.1:<< import leoNodes and leoConfig >>
#@-node:ekr.20031218072017.1934:run
#@+node:ekr.20031218072017.2817: doCommand
command_count = 0

def doCommand (self,command,label,event=None):

    """Execute the given command, invoking hooks and catching exceptions.

    The code assumes that the "command1" hook has completely handled the command if
    g.doHook("command1") returns False.
    This provides a simple mechanism for overriding commands."""

    c = self ; p = c.currentPosition()
    commandName = command and command.__name__
    c.setLog()

    self.command_count += 1
    if not g.app.unitTesting and c.config.getBool('trace_doCommand'):
        g.trace(commandName)

    # The presence of this message disables all commands.
    if c.disableCommandsMessage:
        g.es(c.disableCommandsMessage,color='blue')
        return 'break' # Inhibit all other handlers.

    if c.exists and c.inCommand and not g.unitTesting:
        # g.trace('inCommand',c)
        g.es('ignoring command: already executing a command.',color='red')
        return 'break'

    if label and event is None: # Do this only for legacy commands.
        if label == "cantredo": label = "redo"
        if label == "cantundo": label = "undo"
        g.app.commandName = label

    if not g.doHook("command1",c=c,p=p,v=p,label=label):
        try:
            c.inCommand = True
            val = command(event)
            if c and c.exists: # Be careful: the command could destroy c.
                c.inCommand = False
                c.k.funcReturn = val
            # else: print 'c no longer exists',c
        except:
            c.inCommand = False
            if g.app.unitTesting:
                raise
            else:
                g.es("exception executing command")
                print "exception executing command"
                g.es_exception(c=c)
                if c and c.exists and hasattr(c,'frame'):
                    c.redraw_now()

        if c and c.exists:
            if c.requestCloseWindow:
                g.trace('Closing window after command')
                c.requestCloseWindow = False
                g.app.closeLeoWindow(c.frame)
            else:
                c.outerUpdate()

    # Be careful: the command could destroy c.
    if c and c.exists:
        p = c.currentPosition()
        g.doHook("command2",c=c,p=p,v=p,label=label)

    return "break" # Inhibit all other handlers.
#@-node:ekr.20031218072017.2817: doCommand
#@+node:ekr.20031218072017.3677:Coloring (leoBody)
def getColorizer(self):

    return self.colorizer

def updateSyntaxColorer(self,p):

    return self.colorizer.updateSyntaxColorer(p.copy())


if g.newDrawing:

    def recolor(self,p,incremental=False):
        self.c.requestRecolorFlag = True
        self.c.incrementalRecolorFlag = incremental
else:

    def recolor(self,p,incremental=False):
        self.colorizer.colorize(p.copy(),incremental)

recolor_now = recolor


#@-node:ekr.20031218072017.3677:Coloring (leoBody)
#@+node:ekr.20041223102225:class tkIconBarClass
class tkIconBarClass:

    '''A class representing the singleton Icon bar'''

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

    self.c = c

    self.buttons = {}
    self.iconFrame = w = Tk.Frame(parentFrame,height="5m",bd=2,relief="groove")
    self.c.frame.iconFrame = self.iconFrame
    self.font = None
    self.parentFrame = parentFrame
    self.visible = False
    self.show()
#@-node:ekr.20041223102225.1: ctor
#@+node:ekr.20031218072017.3958:add
def add(self,*args,**keys):

    """Add a button containing text or a picture to the icon bar.

    Pictures take precedence over text"""

    c = self.c ; f = self.iconFrame
    text = keys.get('text')
    imagefile = keys.get('imagefile')
    image = keys.get('image')
    command = keys.get('command')
    bg = keys.get('bg')

    if not imagefile and not image and not text: return

    # First define n.
    try:
        g.app.iconWidgetCount += 1
        n = g.app.iconWidgetCount
    except:
        n = g.app.iconWidgetCount = 1

    if command:
        def commandCallBack(c=c,command=command):
            val = command()
            if c.exists:
                c.bodyWantsFocus()
                c.outerUpdate()
            return val
    else:
        def commandCallback():
            print "command for widget %s" % (n)
        command = commandCallback

    if imagefile or image:
        << create a picture >>
    elif text:
        b = Tk.Button(f,text=text,relief="groove",bd=2,command=command)
        if not self.font:
            self.font = c.config.getFontFromParams(
                "button_text_font_family", "button_text_font_size",
                "button_text_font_slant",  "button_text_font_weight",)
        b.configure(font=self.font)
        # elif sys.platform.startswith('win'):
            # width = max(6,len(text))
            # b.configure(width=width,font=('verdana',7,'bold'))
        if bg: b.configure(bg=bg)
        b.pack(side="left", fill="none")
        return b

    return None
#@+node:ekr.20031218072017.3959:<< create a picture >>
try:
    if imagefile:
        # Create the image.  Throws an exception if file not found
        imagefile = g.os_path_join(g.app.loadDir,imagefile)
        imagefile = g.os_path_normpath(imagefile)
        image = Tk.PhotoImage(master=g.app.root,file=imagefile)

        # Must keep a reference to the image!
        try:
            refs = g.app.iconImageRefs
        except:
            refs = g.app.iconImageRefs = []

        refs.append((imagefile,image),)

    if not bg:
        bg = f.cget("bg")

    b = Tk.Button(f,image=image,relief="flat",bd=0,command=command,bg=bg)
    b.pack(side="left",fill="y")
    return b

except:
    g.es_exception()
    return None
#@-node:ekr.20031218072017.3959:<< create a picture >>
#@-node:ekr.20031218072017.3958:add
#@+node:ekr.20031218072017.3956:clear
def clear(self):

    """Destroy all the widgets in the icon bar"""

    f = self.iconFrame

    for slave in f.pack_slaves():
        slave.destroy()
    self.visible = False

    f.configure(height="5m") # The default height.
    g.app.iconWidgetCount = 0
    g.app.iconImageRefs = []
#@-node:ekr.20031218072017.3956:clear
#@+node:ekr.20061213091114.1:deleteButton (new in Leo 4.4.3)
def deleteButton (self,w):

    w.pack_forget()
    self.c.bodyWantsFocus()
    self.c.outerUpdate()
#@-node:ekr.20061213091114.1:deleteButton (new in Leo 4.4.3)
#@+node:ekr.20041223114821:getFrame
def getFrame (self):

    return self.iconFrame
#@-node:ekr.20041223114821:getFrame
#@+node:ekr.20041223102225.2:pack (show)
def pack (self):

    """Show the icon bar by repacking it"""

    if not self.visible:
        self.visible = True
        self.iconFrame.pack(fill="x",pady=2)

show = pack
#@-node:ekr.20041223102225.2:pack (show)
#@+node:ekr.20061213092103:setCommandForButton (new in Leo 4.4.3)
def setCommandForButton(self,b,command):

    b.configure(command=command)
#@-node:ekr.20061213092103:setCommandForButton (new in Leo 4.4.3)
#@+node:ekr.20031218072017.3955:unpack (hide)
def unpack (self):

    """Hide the icon bar by unpacking it.

    A later call to show will repack it in a new location."""

    if self.visible:
        self.visible = False
        self.iconFrame.pack_forget()

hide = unpack
#@-node:ekr.20031218072017.3955:unpack (hide)
#@-node:ekr.20041223102225:class tkIconBarClass
#@+node:ekr.20040803072955.127:tree.editLabel
def editLabel (self,p,selectAll=False):

    """Start editing p's headline."""

    c = self.c
    trace = (False or self.trace_edit) and not g.app.unitTesting

    if p and p != self.editPosition():

        if trace:
            g.trace(p.headString(),g.choose(c.edit_widget(p),'','no edit widget'))

        c.beginUpdate()
        try:
            self.endEditLabel()
        finally:
            c.endUpdate(True)
            c.outerUpdate()

    self.setEditPosition(p) # That is, self._editPosition = p

    if trace: g.trace(c.edit_widget(p))

    w = c.edit_widget(p)
    if p and w:
        self.revertHeadline = p.headString() # New in 4.4b2: helps undo.
        self.setEditLabelState(p,selectAll=selectAll) # Sets the focus immediately.
        c.headlineWantsFocus(p) # Make sure the focus sticks.
        c.k.showStateAndMode(w)
#@-node:ekr.20040803072955.127:tree.editLabel
#@+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.'''

    # g.trace('event.keysym_num',event.keysym_num,event,dir(event))

    << define vars >>
    if keysym in special_keys: return None

    trace = (False or self.trace_masterKeyHandler) and not g.app.unitTesting
    traceGC = self.trace_masterKeyHandlerGC and not g.app.unitTesting
    if traceGC: g.printNewObjects('masterKey 1')
    if trace:
        g.trace('stroke:',repr(stroke),'keysym:',repr(event.keysym),'ch:',repr(event.char))
            # 'state.kind:',k.state.kind),'\n',g.callers())
        # if (self.master_key_count % 100) == 0: g.printGcSummary()

    # Handle keyboard-quit first.
    if k.abortAllModesKey and stroke == k.abortAllModesKey:
        # g.trace('special case')
        return k.masterCommand(event,k.keyboardQuit,stroke,'keyboard-quit')

    if k.inState():
        # This will return unless k.autoCompleterStateHandler
        # (called from k.callStateFunction) returns 'do-standard-keys'
        << handle mode bindings >>

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

    if stroke and isPlain:
        << handle special cases for plain keys >>

    << handle per-pane bindings >>

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

    return k.handleUnboundKeys(event,char,keysym,stroke)
#@+node:ekr.20061031131434.147:<< define vars >>
k = self ; c = k.c ; gui = g.app.gui

if event: event = gui.leoKeyEvent(event,c)

w = event.widget
char = event.char
keysym = event.keysym
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',
    '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 >>
#@+node:ekr.20061031131434.149:<< handle mode bindings >>
# 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 'break'

# Second, honor general modes.
if state == 'getArg':
    return k.getArg(event,stroke=stroke)
elif state == 'getFileName':
    return 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) # k.state.handler)
    val = k.callStateFunction(event) # Calls end-command.
    if val != 'do-standard-keys': return 'break'

# Third, pass keys to user modes.
else:
    d =  k.masterBindingsDict.get(state)
    if d:
        b = d.get(stroke)
        if b:
            if trace: g.trace('calling generalModeHandler')
            k.generalModeHandler (event,
                commandName=b.commandName,func=b.func,
                modeName=state,nextMode=b.nextMode)
            return 'break'
        else:
            # New in Leo 4.5: unbound keys end mode.
            k.endMode(event)
    else:
        # New in 4.4b4.
        handler = k.getStateHandler()
        if handler:
            handler(event)
        else:
            g.trace('No state handler for %s' % state)
        return 'break'
#@-node:ekr.20061031131434.149:<< handle mode bindings >>
#@+node:ekr.20080510153327.4:<< handle special cases for plain keys >>
# g.trace('plain key','state',k.unboundKeyAction,'stroke',stroke)

# 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))
                k.masterCommand(event,b.func,b.stroke,b.commandName)
                return 'break'

    if trace: g.trace('%s in %s mode' % (stroke,k.unboundKeyAction))
    k.masterCommand(event,func=None,stroke=stroke,commandName=None)
    return 'break'

# Bound   plain keys in command mode are by the per-pane logic.
# Unbound plain keys in command mode are ignored by handleUnboundKeys.

# This code ignores all command-state keys if we are not in a text widget.
elif k.unboundKeyAction == 'command':
    if not g.app.gui.isTextWidget(w):
        c.onCanvasKey(event)
        return 'break'

# elif k.unboundKeyAction == 'command':
    # # In command mode we must disallow all keys except those actually bound in command-state.
    # # That is, we cannot use general per-pane mode bindings.
    # for key,name in (
        # # Order here is similar to bindtags order.
        # ('command',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 (
            # 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,{})
            # # g.trace('key',key,'name',name,'stroke',stroke,'stroke in d.keys',stroke in d.keys())
            # if d:
                # b = d.get(stroke)
                # if b:
                    # if trace: g.trace('%s found %s = %s' % (key,repr(b.stroke),b.commandName))
                    # return k.masterCommand(event,b.func,b.stroke,b.commandName)
    # else:
        # if trace: g.trace('ignoring %s in command mode' % b.stroke)
        # return 'break' # Disallow all unbound plain keys in command mode.
#@nonl
#@-node:ekr.20080510153327.4:<< handle special cases for plain keys >>
#@+node:ekr.20061031131434.150:<< handle per-pane bindings >>
keyStatesTuple = ('command','insert','overwrite')

# g.trace('w_name',w_name,'w',w,'isTextWidget(w)',g.app.gui.isTextWidget(w))
# g.trace('button',k.masterBindingsDict.get('button'))

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,{})
        # g.trace('key',key,'name',name,'stroke',stroke,'stroke in d.keys',stroke in d.keys())
        if d:
            b = d.get(stroke)
            if b:
                if trace: g.trace('%s found %s = %s' % (key,repr(b.stroke),b.commandName))
                if traceGC: g.printNewObjects('masterKey 3')
                return k.masterCommand(event,b.func,b.stroke,b.commandName)
#@-node:ekr.20061031131434.150:<< handle per-pane bindings >>
#@+node:ekr.20061031131434.108:k.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:k.callStateFunction
#@+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','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) ### ,setDefaultUnboundKeyAction=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:handleUnboudKeys
def handleUnboundKeys (self,event,char,keysym,stroke):

    k = self ; c = k.c
    modesTuple = ('insert','overwrite')
    trace = False

    if trace:
        # if stroke: g.trace('***unexpected stroke***')
        g.trace('keysym:',repr(event.keysym),'ch:',repr(event.char))
            # 'state.kind:',k.state.kind),'\n',g.callers())
        # if (self.master_key_count % 100) == 0: g.printGcSummary()

    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 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.find('Escape') != -1:
        # Never insert escape 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:handleUnboudKeys
#@-node:ekr.20061031131434.146:masterKeyHandler & helpers
#@+node:ekr.20061031131434.153:masterClickHandler
# def masterClickHandler (self,event,func=None):
    # k = self ; c = k.c
    # val = self.masterClickHandlerHelper(event,func)
    # if c.exists: c.outerUpdate()
    # return val

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.
        c.masterFocusHandler()
        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
    # val = self.masterDoubleClickHandlerHelper(event,func)
    # if c.exists: c.outerUpdate()
    # return val

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
    # val = self.masterMenuHandlerHelper(stroke,func,commandName)
    # if c.exists: c.outerUpdate()
    # return val

def masterMenuHandler (self,stroke,func,commandName):

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

    # 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.20080515053412.52:Changed
#@+node:ekr.20031218072017.2949:Drawing Utilities (commands)
#@+node:ekr.20080514131122.4:Legacy code
if not g.newDrawing:

    @others
#@+node:ekr.20031218072017.2950:c.begin/endUpdate
@
**Important** These methods ensure that exactly zero or one (depending on the
argument to endUpdate) redraws exist within the section of code bounded by
c.beginUpdate and c.endUpdate. This greatly simplifies and clarifies the code.

Callers should ensure that every beginUpdate is matched with an endUpdate by
using the following pattern:
    c.beginUpdate()
    try:
        << whatever >>
    finally:
        c.endUpdate()
@c

def beginUpdate(self):

    '''Suppress redraws of the tree (except for explict calls to c.redraw_now)
    until the matching call to endUpdate.'''

    c = self
    if c.frame and c.frame.tree:
        c.frame.tree.beginUpdate()

def endUpdate(self,flag=True,scroll=True):

    '''Redraw the screen if flag is True.'''

    c = self
    if c.frame and c.frame.tree:
        c.frame.tree.endUpdate(flag,scroll=scroll)

BeginUpdate = beginUpdate # Compatibility with old scripts
EndUpdate = endUpdate # Compatibility with old scripts
#@-node:ekr.20031218072017.2950:c.begin/endUpdate
#@+node:ekr.20080515053412.53:c.bind (new)
def bind (self,w,pattern,func):

    w.bind(pattern,func)
#@-node:ekr.20080515053412.53:c.bind (new)
#@+node:ekr.20031218072017.2951:c.bringToFront
def bringToFront(self,set_focus=True):

    # g.trace(g.callers())

    c = self
    c.frame.deiconify()

    if set_focus:
        c.frame.body.setFocus()

BringToFront = bringToFront # Compatibility with old scripts
#@-node:ekr.20031218072017.2951:c.bringToFront
#@+node:ekr.20060205103842:c.get/request/set_focus
def get_focus (self):

    c = self
    return g.app.gui and g.app.gui.get_focus(c)

def get_requested_focus (self):

    c = self
    return c.requestedFocusWidget or c.hasFocusWidget or g.app.gui.get_focus(c)

def request_focus(self,w):

    c = self
    if w: c.requestedFocusWidget = w
    c.traceFocus(w)

def set_focus (self,w,force=False):

    c = self

    if force: # New in Leo 4.4.2: safer.
        c.hasFocusWidget = c.requestedFocusWidget = w
        g.app.gui and g.app.gui.set_focus(c,w)
        c.traceFocus(w)
    else: # An optimization.
        c.requestedFocusWidget = w
        c.masterFocusHandler()

    # Can't do this: it interferes with too much.
    # if c.k: c.k.showStateAndMode(w)
#@-node:ekr.20060205103842:c.get/request/set_focus
#@+node:ekr.20060210103358:c.invalidateFocus
def invalidateFocus (self):

    '''Indicate that the focus is in an invalid location, or is unknown.'''

    c = self
    c.requestedFocusWidget = None
    c.hasFocusWidget = None
    # g.trace(g.callers())
#@-node:ekr.20060210103358:c.invalidateFocus
#@+node:ekr.20060207140352:c.masterFocusHandler
def masterFocusHandler (self):

    c = self
    trace = False or (not g.app.unitTesting and c.config.getBool('trace_masterFocusHandler'))

    # Give priority to later requests, but default to previously set widget.
    w = c.requestedFocusWidget or c.hasFocusWidget

    if trace: print \
        'requested',c.widget_name(c.requestedFocusWidget),\
        'present',c.widget_name(c.hasFocusWidget)

    if c.hasFocusWidget and (
        not c.requestedFocusWidget or c.requestedFocusWidget == c.hasFocusWidget):
        if trace: print 'no change.',c.widget_name(w)
        c.requestedFocusWidget = None
    elif w:
        # Ignore whatever g.app.gui.get_focus might say.
        ok = g.app.gui.set_focus(c,w)
        if ok: c.hasFocusWidget = w
        c.requestedFocusWidget = None
    else:
        # This is not an error: it can arise because of a call to k.invalidateFocus.
        if trace: print '*'*20,'oops: moving to body pane.'
        c.bodyWantsFocusNow()

restoreRequestedFocus = masterFocusHandler
#@-node:ekr.20060207140352:c.masterFocusHandler
#@+node:ekr.20080514131122.21:c.outerUpdate
def outerUpdate (self):

    pass
#@-node:ekr.20080514131122.21:c.outerUpdate
#@+node:ekr.20031218072017.2953:c.recolor & requestRecolor
def recolor(self):

    c = self
    c.frame.body.recolor(c.currentPosition())

def requestRecolor (self):

    c = self
    c.frame.requestRecolorFlag = True
#@-node:ekr.20031218072017.2953:c.recolor & requestRecolor
#@+node:ekr.20051216171520:c.recolor_now
def recolor_now(self,p=None,incremental=False,interruptable=True):

    c = self
    if p is None:
        p = c.currentPosition()

    c.frame.body.colorizer.colorize(p,
        incremental=incremental,interruptable=interruptable)
#@-node:ekr.20051216171520:c.recolor_now
#@+node:ekr.20031218072017.2954:c.redraw and c.redraw_now
def redraw (self):
    c = self
    c.beginUpdate()
    c.endUpdate()

def redraw_now (self):

    c = self

    if g.app.quitting or not c.exists or not hasattr(c.frame,'top'):
        return # nullFrame's do not have a top frame.

    c.frame.tree.redraw_now()
    if 0: # Interferes with new colorizer.
        c.frame.top.update_idletasks()

    if c.frame.requestRecolorFlag:
        c.frame.requestRecolorFlag = False
        c.recolor()

# Compatibility with old scripts
force_redraw = redraw_now
#@-node:ekr.20031218072017.2954:c.redraw and c.redraw_now
#@+node:ekr.20060208143543:c.restoreFocus
def restoreFocus (self):

    '''Ensure that the focus eventually gets restored.'''

    c =self
    trace = not g.app.unitTesting and c.config.getBool('trace_focus')

    if c.requestedFocusWidget:
        c.hasFocusWidget = None # Force an update
    elif c.hasFocusWidget:
        c.requestedFocusWidget = c.hasFocusWidget
        c.hasFocusWidget = None # Force an update
    else:
        # Should not happen, except during unit testing.
        # c.masterFocusHandler sets c.hasFocusWidget,
        # so if it is not set here it is because this method cleared it.
        if not g.app.unitTesting: g.trace('oops: no requested or present widget.',g.callers())
        c.bodyWantsFocusNow()

    if c.inCommand:
        if trace: g.trace('expecting later call to c.masterFocusHandler')
        # A call to c.masterFocusHandler will surely happen.
    else:
        c.masterFocusHandler() # Do it now.
#@-node:ekr.20060208143543:c.restoreFocus
#@+node:ekr.20060207142332:c.traceFocus
trace_focus_count = 0

def traceFocus (self,w):

    c = self

    if False or (not g.app.unitTesting and c.config.getBool('trace_focus')):
        c.trace_focus_count += 1
        print '%4d' % (c.trace_focus_count),c.widget_name(w),g.callers(8)
#@-node:ekr.20060207142332:c.traceFocus
#@+node:ekr.20060205111103:c.widget_name
def widget_name (self,widget):

    c = self

    return g.app.gui and g.app.gui.widget_name(widget) or ''
#@-node:ekr.20060205111103:c.widget_name
#@+node:ekr.20050120092028:c.xWantsFocus
def bodyWantsFocus(self):
    c = self ; body = c.frame.body
    c.request_focus(body and body.bodyCtrl)
def headlineWantsFocus(self,p):
    c = self
    c.request_focus(p and c.edit_widget(p))

def logWantsFocus(self):
    c = self ; log = c.frame.log
    c.request_focus(log and log.logCtrl)

def minibufferWantsFocus(self):
    c = self ; k = c.k
    if k: k.minibufferWantsFocus()

def treeWantsFocus(self):
    c = self ; tree = c.frame.tree
    c.request_focus(tree and tree.canvas)

def widgetWantsFocus(self,w):
    c = self ; c.request_focus(w)
#@-node:ekr.20050120092028:c.xWantsFocus
#@+node:ekr.20060210102201:c.xWantsFocusNow
def bodyWantsFocusNow(self):
    c = self ; body = c.frame.body
    #g.trace(body and body.bodyCtrl)
    c.set_focus(body and body.bodyCtrl,force=True)

def headlineWantsFocusNow(self,p):
    c = self
    c.set_focus(p and c.edit_widget(p),force=True)

def logWantsFocusNow(self):
    c = self ; log = c.frame.log
    c.set_focus(log and log.logCtrl,force=True)

def minibufferWantsFocusNow(self):
    c = self ; k = c.k
    if k: k.minibufferWantsFocusNow()

def treeWantsFocusNow(self):
    c = self ; tree = c.frame.tree
    c.set_focus(tree and tree.canvas,force=True)

def widgetWantsFocusNow(self,w):
    c = self ; c.set_focus(w,force=True)
#@-node:ekr.20060210102201:c.xWantsFocusNow
#@-node:ekr.20080514131122.4:Legacy code
#@+node:ekr.20080514131122.6:New code
if g.newDrawing:

    @others

else:

    def outerUpdate (self):
        pass
#@+node:ekr.20080514131122.7:c.begin/endUpdate

def beginUpdate(self):

    '''Suppress redraws of the tree (except for explict calls to c.redraw_now)
    until the matching call to endUpdate.'''

    pass

def endUpdate(self,flag=True,scroll=True):

    '''Redraw the screen if flag is True.'''

    c = self
    if flag:
        c.requestRedrawFlag = True
        c.requestRedrawScrollFlag = scroll
        # g.trace('flag is True','scroll',scroll,c)

BeginUpdate = beginUpdate # Compatibility with old scripts
EndUpdate = endUpdate # Compatibility with old scripts
#@-node:ekr.20080514131122.7:c.begin/endUpdate
#@+node:ekr.20080515053412.1:c.bind & c.bind2
def bind (self,w,pattern,func):

    c = self

    def bindCallback(event,c=c,func=func):
        val = func(event)
        # Careful: func may destroy c.
        if c.exists: c.outerUpdate()
        return val

    w.bind(pattern,bindCallback)

def bind2 (self,w,pattern,func,extra):

    c = self

    def bindCallback(event,c=c,func=func):
        val = func(event)
        # Careful: func may destroy c.
        if c.exists: c.outerUpdate()
        return val

    w.bind(pattern,bindCallback,extra)
#@-node:ekr.20080515053412.1:c.bind & c.bind2
#@+node:ekr.20080514131122.8:c.bringToFront
def bringToFront(self,set_focus=True):

    c = self
    c.requestedIconify = 'deiconify'
    c.requestedFocusWidget = c.frame.body.bodyCtrl

BringToFront = bringToFront # Compatibility with old scripts
#@-node:ekr.20080514131122.8:c.bringToFront
#@+node:ekr.20080514131122.9:c.get/request/set_focus
def get_focus (self):

    c = self
    return g.app.gui and g.app.gui.get_focus(c)

def get_requested_focus (self):

    c = self
    return c.requestedFocusWidget

def request_focus(self,w):

    c = self
    if w: c.requestedFocusWidget = w

def set_focus (self,w,force=False):

    c = self
    if w and g.app.gui and c.requestedFocusWidget:
        g.app.gui.set_focus(c,w)

    c.requestedFocusWidget = None
#@-node:ekr.20080514131122.9:c.get/request/set_focus
#@+node:ekr.20080514131122.10:c.invalidateFocus
def invalidateFocus (self):

    '''Indicate that the focus is in an invalid location, or is unknown.'''

    # c = self
    # c.requestedFocusWidget = None
    pass
#@nonl
#@-node:ekr.20080514131122.10:c.invalidateFocus
#@+node:ekr.20080514131122.11:c.masterFocusHandler
def masterFocusHandler (self):

    pass # No longer used.

    # c = self
    # trace = False or (not g.app.unitTesting and c.config.getBool('trace_masterFocusHandler'))

    # # Give priority to later requests, but default to previously set widget.
    # w = c.requestedFocusWidget or c.hasFocusWidget

    # if trace: print \
        # 'requested',c.widget_name(c.requestedFocusWidget),\
        # 'present',c.widget_name(c.hasFocusWidget)

    # if c.hasFocusWidget and (
        # not c.requestedFocusWidget or c.requestedFocusWidget == c.hasFocusWidget):
        # if trace: print 'no change.',c.widget_name(w)
        # c.requestedFocusWidget = None
    # elif w:
        # # Ignore whatever g.app.gui.get_focus might say.
        # ok = g.app.gui.set_focus(c,w)
        # if ok: c.hasFocusWidget = w
        # c.requestedFocusWidget = None    # c = self
    # trace = False or (not g.app.unitTesting and c.config.getBool('trace_masterFocusHandler'))

    # # Give priority to later requests, but default to previously set widget.
    # w = c.requestedFocusWidget or c.hasFocusWidget

    # if trace: print \
        # 'requested',c.widget_name(c.requestedFocusWidget),\
        # 'present',c.widget_name(c.hasFocusWidget)

    # if c.hasFocusWidget and (
        # not c.requestedFocusWidget or c.requestedFocusWidget == c.hasFocusWidget):
        # if trace: print 'no change.',c.widget_name(w)
        # c.requestedFocusWidget = None
    # elif w:
        # # Ignore whatever g.app.gui.get_focus might say.
        # ok = g.app.gui.set_focus(c,w)
        # if ok: c.hasFocusWidget = w
        # c.requestedFocusWidget = None
    # else:
        # # This is not an error: it can arise because of a call to k.invalidateFocus.
        # if trace: print '*'*20,'oops: moving to body pane.'
        # c.bodyWantsFocusNow()
    # else:
        # # This is not an error: it can arise because of a call to k.invalidateFocus.
        # if trace: print '*'*20,'oops: moving to body pane.'
        # c.bodyWantsFocusNow()

restoreRequestedFocus = masterFocusHandler
#@-node:ekr.20080514131122.11:c.masterFocusHandler
#@+node:ekr.20080514131122.20:c.outerUpdate
def outerUpdate (self):

    c = self ; aList = []

    if not c.exists or not c.k:
        return

    if c.requestedIconify == 'iconify':
        aList.append('iconify')
        c.frame.iconify()

    if c.requestedIconify == 'deiconify':
        aList.append('deiconify')
        c.frame.deiconify()

    if c.requestRedrawFlag:
        aList.append('redraw') # : scroll: %s' % (c.requestRedrawScrollFlag))
        c.frame.tree.redraw_now(scroll=c.requestRedrawScrollFlag)

    if c.requestRecolorFlag:
        aList.append('%srecolor' % (
            g.choose(c.incrementalRecolorFlag,'','full ')))
        c.recolor_now(incremental=c.incrementalRecolorFlag)

    if c.requestedFocusWidget:
        aList.append('focus: %s' % (
            g.app.gui.widget_name(c.requestedFocusWidget)))
        c.set_focus(c.requestedFocusWidget)
    else:
        # We can not set the focus to the body pane:
        # That would make nested calls to c.outerUpdate significant.
        pass

    # if aList: g.trace(', '.join(aList)) # ,g.callers(5))

    c.incrementalRecolorFlag = False
    c.requestRecolorFlag = None
    c.requestRedrawFlag = False
    c.requestedFocusWidget = None
    c.requestedIconify = ''
    c.requestedRedrawScrollFlag = False
#@-node:ekr.20080514131122.20:c.outerUpdate
#@+node:ekr.20080514131122.12:c.recolor & requestRecolor
def requestRecolor (self):

    c = self
    c.frame.requestRecolorFlag = True

recolor = requestRecolor
#@-node:ekr.20080514131122.12:c.recolor & requestRecolor
#@+node:ekr.20080514131122.13:c.recolor_now
def recolor_now(self,p=None,incremental=False,interruptable=True):

    c = self
    if p is None:
        p = c.currentPosition()

    c.frame.body.colorizer.colorize(p,
        incremental=incremental,interruptable=interruptable)
#@-node:ekr.20080514131122.13:c.recolor_now
#@+node:ekr.20080514131122.14:c.redraw and c.redraw_now
def redraw (self):
    c = self
    c.requestRedrawFlag = True

def redraw_now (self):
    c = self
    c.requestRedrawFlag = True

# Compatibility with old scripts
force_redraw = redraw_now
#@-node:ekr.20080514131122.14:c.redraw and c.redraw_now
#@+node:ekr.20080514131122.15:c.restoreFocus
def restoreFocus (self):

    '''Ensure that the focus eventually gets restored.'''
    pass
    # g.trace(g.callers(5))

    # c =self
    # trace = not g.app.unitTesting and c.config.getBool('trace_focus')

    # if c.requestedFocusWidget:
        # c.hasFocusWidget = None # Force an update
    # elif c.hasFocusWidget:
        # c.requestedFocusWidget = c.hasFocusWidget
        # c.hasFocusWidget = None # Force an update
    # else:
        # # Should not happen, except during unit testing.
        # # c.masterFocusHandler sets c.hasFocusWidget,
        # # so if it is not set here it is because this method cleared it.
        # if not g.app.unitTesting: g.trace('oops: no requested or present widget.',g.callers())
        # c.bodyWantsFocusNow()

    # if c.inCommand:
        # if trace: g.trace('expecting later call to c.masterFocusHandler')
        # # A call to c.masterFocusHandler will surely happen.
    # else:
        # c.masterFocusHandler() # Do it now.
#@-node:ekr.20080514131122.15:c.restoreFocus
#@+node:ekr.20080514131122.16:c.traceFocus
trace_focus_count = 0

def traceFocus (self,w):

    c = self

    if False or (not g.app.unitTesting and c.config.getBool('trace_focus')):
        c.trace_focus_count += 1
        print '%4d' % (c.trace_focus_count),c.widget_name(w),g.callers(8)
#@-node:ekr.20080514131122.16:c.traceFocus
#@+node:ekr.20080514131122.17:c.widget_name
def widget_name (self,widget):

    c = self

    return g.app.gui and g.app.gui.widget_name(widget) or ''
#@-node:ekr.20080514131122.17:c.widget_name
#@+node:ekr.20080514131122.18:c.xWantsFocus (no change)
def bodyWantsFocus(self):
    c = self ; body = c.frame.body
    c.request_focus(body and body.bodyCtrl)

def headlineWantsFocus(self,p):
    c = self
    c.request_focus(p and c.edit_widget(p))

def logWantsFocus(self):
    c = self ; log = c.frame.log
    c.request_focus(log and log.logCtrl)

def minibufferWantsFocus(self):
    c = self ; k = c.k
    if k: k.minibufferWantsFocus()

def treeWantsFocus(self):
    c = self ; tree = c.frame.tree
    c.request_focus(tree and tree.canvas)

def widgetWantsFocus(self,w):
    c = self ; c.request_focus(w)
#@-node:ekr.20080514131122.18:c.xWantsFocus (no change)
#@+node:ekr.20080514131122.19:c.xWantsFocusNow 
# widgetWantsFocusNow does an automatic update.
def widgetWantsFocusNow(self,w):
    c = self
    c.request_focus(w)
    c.outerUpdate()
    # Re-request widget so we don't use the body by default.
    c.request_focus(w) 

# All other "Now" methods wait.
bodyWantsFocusNow = bodyWantsFocus
headlineWantsFocusNow = headlineWantsFocus
logWantsFocusNow = logWantsFocus
minibufferWantsFocusNow = minibufferWantsFocus
treeWantsFocusNow = treeWantsFocus
#@-node:ekr.20080514131122.19:c.xWantsFocusNow 
#@-node:ekr.20080514131122.6:New code
#@-node:ekr.20031218072017.2949:Drawing Utilities (commands)
#@-node:ekr.20080514131122.2:New drawing/focus code
#@+node:ekr.20080510185834.6:Projects
#@+node:ekr.20080509064108.8:Classified commands
#@+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-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-keyboard-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-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-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',
        'scroll-down-extend-selection',
        'scroll-outline-down-line',
        'scroll-outline-down-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.20080509064108.1:editing commands: single-line (appropriate for find or minibuffer)
# editCommandsClass
# change ':.*$ to nothing
# change ^[ ]*' to '

'back-to-indentation':                  self.backToIndentation,
'back-char':                            self.backCharacter,
'back-char-extend-selection':           self.backCharacterExtendSelection,
'back-word':                            self.backwardWord,
'back-word-extend-selection':           self.backwardWordExtendSelection,
'backward-delete-char':                 self.backwardDeleteCharacter,
'backward-find-character':              self.backwardFindCharacter,
'backward-find-character-extend-selection': self.backwardFindCharacterExtendSelection,
'beginning-of-line':                    self.beginningOfLine,
'beginning-of-line-extend-selection':   self.beginningOfLineExtendSelection,
'capitalize-word':                      self.capitalizeWord,
'delete-char':                          self.deleteNextChar,
'delete-indentation':                   self.deleteIndentation,
'delete-spaces':                        self.deleteSpaces,
# 'do-nothing':                           self.doNothing,
'downcase-word':                        self.downCaseWord,
'end-of-line':                          self.endOfLine,
'end-of-line-extend-selection':         self.endOfLineExtendSelection,
'escape':                               self.watchEscape,
'exchange-point-mark':                  self.exchangePointMark,
'extend-to-line':                       self.extendToLine,
'extend-to-word':                       self.extendToWord,
'find-character':                       self.findCharacter,
'find-character-extend-selection':      self.findCharacterExtendSelection,
'find-word':                            self.findWord,
'find-word-in-line':                    self.findWordInLine,
'forward-char':                         self.forwardCharacter,
'forward-char-extend-selection':        self.forwardCharacterExtendSelection,
'forward-end-word':                     self.forwardEndWord, # New in Leo 4.4.2.
'forward-end-word-extend-selection':    self.forwardEndWordExtendSelection, # New in Leo 4.4.2.
'forward-word':                         self.forwardWord,
'forward-word-extend-selection':        self.forwardWordExtendSelection,
'insert-newline':                       self.insertNewline,
'insert-parentheses':                   self.insertParentheses,
'move-past-close':                      self.movePastClose,
'move-past-close-extend-selection':     self.movePastCloseExtendSelection,
'newline-and-indent':                   self.insertNewLineAndTab,
'select-all':                           self.selectAllText,
'transpose-chars':                      self.transposeCharacters,
'transpose-words':                      self.transposeWords,
'upcase-word':                          self.upCaseWord,

# keyHandlerCommandsClass
'negative-argument':        k.negativeArgument,
'number-command':           k.numberCommand,
'number-command-0':         k.numberCommand0,
'number-command-1':         k.numberCommand1,
'number-command-2':         k.numberCommand2,
'number-command-3':         k.numberCommand3,
'number-command-4':         k.numberCommand4,
'number-command-5':         k.numberCommand5,
'number-command-6':         k.numberCommand6,
'number-command-7':         k.numberCommand7,
'number-command-8':         k.numberCommand8,
'universal-argument':       k.universalArgument,

# killBufferCommandsClass
'backward-kill-word':       self.backwardKillWord,
'kill-line':                self.killLine,
'kill-word':                self.killWord,
'kill-ws':                  self.killWs,
'yank':                     self.yank,
'yank-pop':                 self.yankPop,
'zap-to-character':         self.zapToCharacter,

# macroCommandsClass
'call-last-keyboard-macro': self.callLastKeyboardMacro,

# registerCommandsClass
'append-to-register':           self.appendToRegister,
'copy-to-register':             self.copyToRegister,
'insert-register':              self.insertRegister,
'prepend-to-register':          self.prependToRegister,

#@-node:ekr.20080509064108.1:editing commands: single-line (appropriate for find or minibuffer)
#@+node:ekr.20080509064108.2:editing commands: multi-line
# editCommandsClass
'add-space-to-lines':                   self.addSpaceToLines,
'add-tab-to-lines':                     self.addTabToLines,
'back-paragraph':                       self.backwardParagraph,
'back-paragraph-extend-selection':      self.backwardParagraphExtendSelection,
'back-sentence':                        self.backSentence,
'back-sentence-extend-selection':       self.backSentenceExtendSelection,
'backward-kill-paragraph':              self.backwardKillParagraph,
'beginning-of-buffer':                  self.beginningOfBuffer,
'beginning-of-buffer-extend-selection': self.beginningOfBufferExtendSelection,
'center-line':                          self.centerLine,
'center-region':                        self.centerRegion,
'clean-all-lines':                      self.cleanAllLines,
'clean-lines':                          self.cleanLines,
'downcase-region':                      self.downCaseRegion,
'end-of-buffer':                        self.endOfBuffer,
'end-of-buffer-extend-selection':       self.endOfBufferExtendSelection,
'extend-to-paragraph':                  self.extendToParagraph,
'extend-to-sentence':                   self.extendToSentence,
'fill-paragraph':                       self.fillParagraph,
'fill-region':                          self.fillRegion,
'fill-region-as-paragraph':             self.fillRegionAsParagraph,
'flush-lines':                          self.flushLines,
'forward-paragraph':                    self.forwardParagraph,
'forward-paragraph-extend-selection':   self.forwardParagraphExtendSelection,
'forward-sentence':                     self.forwardSentence,
'forward-sentence-extend-selection':    self.forwardSentenceExtendSelection,
'indent-relative':                      self.indentRelative,
'indent-rigidly':                       self.tabIndentRegion,
'indent-to-comment-column':             self.indentToCommentColumn,
'move-lines-down':                      self.moveLinesDown,
'move-lines-up':                        self.moveLinesUp,
'next-line':                            self.nextLine,
'next-line-extend-selection':           self.nextLineExtendSelection,
'previous-line':                        self.prevLine,
'previous-line-extend-selection':       self.prevLineExtendSelection,
'remove-blank-lines':                   self.removeBlankLines,
'remove-space-from-lines':              self.removeSpaceFromLines,
'remove-tab-from-lines':                self.removeTabFromLines,
'reverse-region':                       self.reverseRegion,
'reverse-sort-lines':                   self.reverseSortLines,
'reverse-sort-lines-ignoring-case':     self.reverseSortLinesIgnoringCase,                 
'scroll-down':                          self.scrollDown,
'scroll-down-extend-selection':         self.scrollDownExtendSelection,
'scroll-outline-down-line':             self.scrollOutlineDownLine,
'scroll-outline-down-page':             self.scrollOutlineDownPage,
'simulate-begin-drag':                  self.simulateBeginDrag,
'simulate-end-drag':                    self.simulateEndDrag,
'sort-columns':                         self.sortColumns,
'sort-fields':                          self.sortFields,
'sort-lines':                           self.sortLines,
'sort-lines-ignoring-case':             self.sortLinesIgnoringCase,
'split-line':                           self.splitLine,
'tabify':                               self.tabify,
'transpose-lines':                      self.transposeLines,
'untabify':                             self.untabify,
'upcase-region':                        self.upCaseRegion,

# keyHandlerCommandsClass
'repeat-complex-command':               k.repeatComplexCommand,

# killBufferCommandsClass
'backward-kill-sentence':   self.backwardKillSentence,
'kill-sentence':            self.killSentence,
'kill-region':              self.killRegion,
'kill-region-save':         self.killRegionSave,

# queryReplaceCommandsClass
'query-replace':        self.queryReplace,
'query-replace-regex':  self.queryReplaceRegex,

# rectangleCommandsClass
'clear-rectangle':  self.clearRectangle,
'close-rectangle':  self.closeRectangle,
'delete-rectangle': self.deleteRectangle,
'kill-rectangle':   self.killRectangle,
'open-rectangle':   self.openRectangle,
'string-rectangle': self.stringRectangle,
'yank-rectangle':   self.yankRectangle,

# registerCommandsClass
'jump-to-register':             self.jumpToRegister,
'point-to-register':            self.pointToRegister,

# searchCommandsClass
'change':                               self.findTabChange,
'change-then-find':                     self.findTabChangeThenFind,
'find-next':                            self.findTabFindNext,
'find-prev':                            self.findTabFindPrev,

#@-node:ekr.20080509064108.2:editing commands: multi-line
#@+node:ekr.20080509064108.3:non-editing commands (everything else)
'activate-cmds-menu':                   self.activateCmdsMenu,
'activate-edit-menu':                   self.activateEditMenu,
'activate-file-menu':                   self.activateFileMenu,
'activate-help-menu':                   self.activateHelpMenu,
'activate-outline-menu':                self.activateOutlineMenu,
'activate-plugins-menu':                self.activatePluginsMenu,
'activate-window-menu':                 self.activateWindowMenu,
'add-editor':                           c.frame.body and c.frame.body.addEditor,
'clear-extend-mode':                    self.clearExtendMode,
'clear-selected-text':                  self.clearSelectedText,
'click-click-box':                      self.clickClickBox,
'click-headline':                       self.clickHeadline,
'click-icon-box':                       self.clickIconBox,
'contract-body-pane':                   c.frame.contractBodyPane,
'contract-log-pane':                    c.frame.contractLogPane,
'contract-outline-pane':                c.frame.contractOutlinePane,
'contract-pane':                        c.frame.contractPane,
'count-region':                         self.countRegion,
'cycle-focus':                          self.cycleFocus,
'cycle-all-focus':                      self.cycleAllFocus,
'cycle-editor-focus':                   c.frame.body.cycleEditorFocus,
'delete-editor':                        c.frame.body.deleteEditor,
'delete-first-icon':                    self.deleteFirstIcon,
'delete-last-icon':                     self.deleteLastIcon,
'delete-node-icons':                    self.deleteNodeIcons,
'double-click-headline':                self.doubleClickHeadline,
'double-click-icon-box':                self.doubleClickIconBox,
'eval-expression':                      self.evalExpression,
'expand-body-pane':                     c.frame.expandBodyPane,
'expand-log-pane':                      c.frame.expandLogPane,
'expand-outline-pane':                  c.frame.expandOutlinePane,
'expand-pane':                          c.frame.expandPane,
'focus-to-body':                        self.focusToBody,
'focus-to-log':                         self.focusToLog,
'focus-to-minibuffer':                  self.focusToMinibuffer,
'focus-to-tree':                        self.focusToTree,
'fully-expand-body-pane':               c.frame.fullyExpandBodyPane,
'fully-expand-log-pane':                c.frame.fullyExpandLogPane,
'fully-expand-pane':                    c.frame.fullyExpandPane,
'fully-expand-outline-pane':            c.frame.fullyExpandOutlinePane,
'goto-char':                            self.gotoCharacter,
'goto-global-line':                     self.gotoGlobalLine,
'goto-line':                            self.gotoLine,
'hide-body-pane':                       c.frame.hideBodyPane,
'hide-log-pane':                        c.frame.hideLogPane,
'hide-pane':                            c.frame.hidePane,
'hide-outline-pane':                    c.frame.hideOutlinePane,
'how-many':                             self.howMany,
'insert-icon':                          self.insertIcon,
'keep-lines':                           self.keepLines,
'kill-paragraph':                       self.killParagraph,
'line-number':                          self.lineNumber,
'scroll-outline-left':                  self.scrollOutlineLeft,
'scroll-outline-right':                 self.scrollOutlineRight,
'scroll-outline-up-line':               self.scrollOutlineUpLine,
'scroll-outline-up-page':               self.scrollOutlineUpPage,
'scroll-up':                            self.scrollUp,
'scroll-up-extend-selection':           self.scrollUpExtendSelection,
'set-comment-column':                   self.setCommentColumn,
'set-extend-mode':                      self.setExtendMode,
'set-fill-column':                      self.setFillColumn,
'set-fill-prefix':                      self.setFillPrefix,
'show-colors':                          self.showColors,
'show-fonts':                           self.showFonts,
'toggle-extend-mode':                   self.toggleExtendMode,
'view-lossage':                         self.viewLossage,
'what-line':                            self.whatLine,

# registerCommandsClass
'copy-rectangle-to-register':   self.copyRectangleToRegister,
'increment-register':           self.incrementRegister,
'view-register':                self.viewRegister,

# Search commands
'clone-find-all':                       self.cloneFindAll,
'find-all':                             self.findAll,
'change-all':                           self.changeAll,
'hide-find-tab':                        self.hideFindTab,
'isearch-forward':                      self.isearchForward,
'isearch-backward':                     self.isearchBackward,
'isearch-forward-regexp':               self.isearchForwardRegexp,
'isearch-backward-regexp':              self.isearchBackwardRegexp,
'isearch-with-present-options':         self.isearchWithPresentOptions,
'open-find-tab':                        self.openFindTab,
'replace-string':                       self.replaceString,
're-search-forward':                    self.reSearchForward,
're-search-backward':                   self.reSearchBackward,
'search-again':                         self.findAgain,
'search-forward':                       self.searchForward,
'search-backward':                      self.searchBackward,
'search-with-present-options':          self.searchWithPresentOptions,
'set-find-everywhere':                  self.setFindScopeEveryWhere,
'set-find-node-only':                   self.setFindScopeNodeOnly,
'set-find-suboutline-only':             self.setFindScopeSuboutlineOnly,
'show-find-options':                    self.showFindOptions,
'word-search-forward':                  self.wordSearchForward,
'word-search-backward':                 self.wordSearchBackward,

# spelling
'open-spell-tab':           self.openSpellTab,
'spell-find':               self.find,
'spell-change':             self.change,
'spell-change-then-find':   self.changeThenFind,
'spell-ignore':             self.ignore,
'hide-spell-tab':           self.hide,
#@-node:ekr.20080509064108.3:non-editing commands (everything else)
#@-node:ekr.20080509064108.8:Classified commands
#@+node:ekr.20080510145650.1:Found: unboundKeyAction
#@+node:ekr.20031218072017.2886:c.editHeadline
def editHeadline (self,event=None):

    '''Begin editing the headline of the selected node.'''

    c = self ; k = c.k ; tree = c.frame.tree

    if g.app.batchMode:
        c.notValidInBatchMode("Edit Headline")
        return

    if k:
        k.setDefaultInputState()
        k.showStateAndMode()

    tree.editLabel(c.currentPosition())
#@-node:ekr.20031218072017.2886:c.editHeadline
#@+node:ekr.20051026083544.2:updateHead
def updateHead (self,event,w):

    '''Update a headline from an event.

    The headline officially changes only when editing ends.'''

    c = self.c ; k = c.k
    ch = event and event.char or ''
    i,j = w.getSelectionRange()
    ins = w.getInsertPoint()
    if i != j: ins = i

    # g.trace('w',w,'ch',repr(ch),g.callers())

    if ch == '\b':
        if i != j:  w.delete(i,j)
        else:       w.delete(ins-1)
        w.setSelectionRange(i-1,i-1,insert=i-1)
    elif ch and ch not in ('\n','\r'):
        if i != j:                              w.delete(i,j)
        elif k.unboundKeyAction == 'overwrite': w.delete(i,i+1)
        w.insert(ins,ch)
        w.setSelectionRange(ins+1,ins+1,insert=ins+1)

    s = w.getAllText()
    if s.endswith('\n'):
        # g.trace('can not happen: trailing newline')
        s = s[:-1]
    w.setWidth(self.headWidth(s=s))

    if ch in ('\n','\r'):
        self.endEditLabel() # Now calls self.onHeadChanged.
#@-node:ekr.20051026083544.2:updateHead
#@+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.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

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

    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'
    else:
        # Let tkinter handle the event.
        # ch = event and event.char ; g.trace('to tk:',name,repr(ch))
        return None
#@-node:ekr.20061031131434.110:k.handleDefaultChar
#@+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'
    keys = d.keys() ; keys.sort()
    c.frame.log.clearTab(tabName)
    data = [] ; n1 = 4 ; n2 = 20
    if not keys: return g.es('no bindings')
    for key in keys:
        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:
    sep = '-' * n1
    for prefix in (
        'Alt+Ctrl+Shift', 'Alt+Shift', 'Alt+Ctrl', 'Alt+Key','Alt',
        'Ctrl+Shift', 'Ctrl', 'Shift',
    ):
        data2 = []
        for item in data:
            s1,s2,s3 = item
            if s2.startswith(prefix):
                data2.append(item)
        g.es('','%s %s' % (sep, prefix),tabName=tabName)
        self.printBindingsHelper(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.
    g.es('','%s %s' % (sep, 'Plain Keys',),tabName=tabName)
    self.printBindingsHelper(data,n1,n2,prefix=None)
    state = k.unboundKeyAction 
    k.showStateAndMode()
#@+node:ekr.20061031131434.120:printBindingsHelper
def printBindingsHelper (self,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(lambda x,y: cmp(x[1],y[1]))
        for s1,s2,s3 in data:
            g.es('','%*s %*s %s' % (-n1,s1,-(min(12,n2)),s2,s3),tabName='Bindings')
#@-node:ekr.20061031131434.120:printBindingsHelper
#@-node:ekr.20061031131434.119:printBindings & helper
#@+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.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.150:<< handle per-pane bindings >>
keyStatesTuple = ('command','insert','overwrite')

# g.trace('w_name',w_name,'w',w,'isTextWidget(w)',g.app.gui.isTextWidget(w))
# g.trace('button',k.masterBindingsDict.get('button'))

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,{})
        # g.trace('key',key,'name',name,'stroke',stroke,'stroke in d.keys',stroke in d.keys())
        if d:
            b = d.get(stroke)
            if b:
                if trace: g.trace('%s found %s = %s' % (key,repr(b.stroke),b.commandName))
                if traceGC: g.printNewObjects('masterKey 3')
                return k.masterCommand(event,b.func,b.stroke,b.commandName)
#@-node:ekr.20061031131434.150:<< handle per-pane bindings >>
#@+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','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) ### ,setDefaultUnboundKeyAction=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:handleUnboudKeys
def handleUnboundKeys (self,event,char,keysym,stroke):

    k = self ; c = k.c
    modesTuple = ('insert','overwrite')
    trace = False

    if trace:
        # if stroke: g.trace('***unexpected stroke***')
        g.trace('keysym:',repr(event.keysym),'ch:',repr(event.char))
            # 'state.kind:',k.state.kind),'\n',g.callers())
        # if (self.master_key_count % 100) == 0: g.printGcSummary()

    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 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.find('Escape') != -1:
        # Never insert escape 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:handleUnboudKeys
#@+node:ekr.20061031131434.133:setInputState
def setInputState (self,state):

    k = self
    k.unboundKeyAction = state



#@-node:ekr.20061031131434.133:setInputState
#@+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)

    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.20080510145650.1:Found: unboundKeyAction
#@+node:ekr.20080510072134.5:Do tree navigation in outline mode
#@+node:ekr.20060923202156:c.onCanvasKey
def onCanvasKey (self,event):

    '''Navigate to the next headline starting with ch = event.char.
    If ch is uppercase, search all headlines; otherwise search only visible headlines.
    This is modelled on Windows explorer.'''

    # g.trace(event and event.char)

    if not event or not event.char or not event.keysym.isalnum():
        return
    c  = self ; p = c.currentPosition() ; p1 = p.copy()
    invisible = c.config.getBool('invisible_outline_navigation')
    ch = event.char
    allFlag = ch.isupper() and invisible # all is a global (!?)
    if not invisible: ch = ch.lower()
    found = False
    extend = self.navQuickKey()
    attempts = g.choose(extend,(True,False),(False,))
    for extend2 in attempts:
        p = p1.copy()
        while 1:
            if allFlag:
                p.moveToThreadNext()
            else:
                p.moveToVisNext(c)
            if not p:
                p = c.rootPosition()
            if p == p1: # Never try to match the same position.
                # g.trace('failed',extend2)
                found = False ; break
            newPrefix = c.navHelper(p,ch,extend2)
            if newPrefix:
                found = True ; break
        if found: break
    if found:
        if allFlag: c.frame.tree.expandAllAncestors(p)
        c.selectPosition(p)
        c.navTime = time.clock()
        c.navPrefix = newPrefix
        # g.trace('extend',extend,'extend2',extend2,'navPrefix',c.navPrefix,'p',p.headString())
    else:
        c.navTime = None
        c.navPrefix = ''
    c.treeWantsFocusNow()
#@+node:ekr.20061002095711.1:c.navQuickKey
def navQuickKey (self):

    '''return true if there are two quick outline navigation keys
    in quick succession.

    Returns False if @float outline_nav_extend_delay setting is 0.0 or unspecified.'''

    c = self

    deltaTime = c.config.getFloat('outline_nav_extend_delay')

    if deltaTime in (None,0.0):
        return False
    else:
        nearTime = c.navTime and time.clock() - c.navTime < deltaTime
        return nearTime
#@nonl
#@-node:ekr.20061002095711.1:c.navQuickKey
#@+node:ekr.20061002095711:c.navHelper
def navHelper (self,p,ch,extend):

    c = self ; h = p.headString().lower()

    if extend:
        prefix = c.navPrefix + ch
        return h.startswith(prefix.lower()) and prefix

    if h.startswith(ch):
        return ch

    # New feature: search for first non-blank character after @x for common x.
    if ch != '@' and h.startswith('@'):
        for s in ('button','command','file','thin','asis','nosent','noref'):
            prefix = '@'+s
            if h.startswith('@'+s):
                while 1:
                    n = len(prefix)
                    ch2 = n < len(h) and h[n] or ''
                    if ch2.isspace():
                        prefix = prefix + ch2
                    else: break
                if len(prefix) < len(h) and h.startswith(prefix + ch.lower()):
                    return prefix + ch
    return ''
#@nonl
#@-node:ekr.20061002095711:c.navHelper
#@-node:ekr.20060923202156:c.onCanvasKey
#@-node:ekr.20080510072134.5:Do tree navigation in outline mode
#@+node:ekr.20031218072017.2949:Drawing Utilities (commands)
#@+node:ekr.20080514131122.4:Legacy code
if not g.newDrawing:

    @others
#@+node:ekr.20031218072017.2950:c.begin/endUpdate
@
**Important** These methods ensure that exactly zero or one (depending on the
argument to endUpdate) redraws exist within the section of code bounded by
c.beginUpdate and c.endUpdate. This greatly simplifies and clarifies the code.

Callers should ensure that every beginUpdate is matched with an endUpdate by
using the following pattern:
    c.beginUpdate()
    try:
        << whatever >>
    finally:
        c.endUpdate()
@c

def beginUpdate(self):

    '''Suppress redraws of the tree (except for explict calls to c.redraw_now)
    until the matching call to endUpdate.'''

    c = self
    if c.frame and c.frame.tree:
        c.frame.tree.beginUpdate()

def endUpdate(self,flag=True,scroll=True):

    '''Redraw the screen if flag is True.'''

    c = self
    if c.frame and c.frame.tree:
        c.frame.tree.endUpdate(flag,scroll=scroll)

BeginUpdate = beginUpdate # Compatibility with old scripts
EndUpdate = endUpdate # Compatibility with old scripts
#@-node:ekr.20031218072017.2950:c.begin/endUpdate
#@+node:ekr.20080515053412.53:c.bind (new)
def bind (self,w,pattern,func):

    w.bind(pattern,func)
#@-node:ekr.20080515053412.53:c.bind (new)
#@+node:ekr.20031218072017.2951:c.bringToFront
def bringToFront(self,set_focus=True):

    # g.trace(g.callers())

    c = self
    c.frame.deiconify()

    if set_focus:
        c.frame.body.setFocus()

BringToFront = bringToFront # Compatibility with old scripts
#@-node:ekr.20031218072017.2951:c.bringToFront
#@+node:ekr.20060205103842:c.get/request/set_focus
def get_focus (self):

    c = self
    return g.app.gui and g.app.gui.get_focus(c)

def get_requested_focus (self):

    c = self
    return c.requestedFocusWidget or c.hasFocusWidget or g.app.gui.get_focus(c)

def request_focus(self,w):

    c = self
    if w: c.requestedFocusWidget = w
    c.traceFocus(w)

def set_focus (self,w,force=False):

    c = self

    if force: # New in Leo 4.4.2: safer.
        c.hasFocusWidget = c.requestedFocusWidget = w
        g.app.gui and g.app.gui.set_focus(c,w)
        c.traceFocus(w)
    else: # An optimization.
        c.requestedFocusWidget = w
        c.masterFocusHandler()

    # Can't do this: it interferes with too much.
    # if c.k: c.k.showStateAndMode(w)
#@-node:ekr.20060205103842:c.get/request/set_focus
#@+node:ekr.20060210103358:c.invalidateFocus
def invalidateFocus (self):

    '''Indicate that the focus is in an invalid location, or is unknown.'''

    c = self
    c.requestedFocusWidget = None
    c.hasFocusWidget = None
    # g.trace(g.callers())
#@-node:ekr.20060210103358:c.invalidateFocus
#@+node:ekr.20060207140352:c.masterFocusHandler
def masterFocusHandler (self):

    c = self
    trace = False or (not g.app.unitTesting and c.config.getBool('trace_masterFocusHandler'))

    # Give priority to later requests, but default to previously set widget.
    w = c.requestedFocusWidget or c.hasFocusWidget

    if trace: print \
        'requested',c.widget_name(c.requestedFocusWidget),\
        'present',c.widget_name(c.hasFocusWidget)

    if c.hasFocusWidget and (
        not c.requestedFocusWidget or c.requestedFocusWidget == c.hasFocusWidget):
        if trace: print 'no change.',c.widget_name(w)
        c.requestedFocusWidget = None
    elif w:
        # Ignore whatever g.app.gui.get_focus might say.
        ok = g.app.gui.set_focus(c,w)
        if ok: c.hasFocusWidget = w
        c.requestedFocusWidget = None
    else:
        # This is not an error: it can arise because of a call to k.invalidateFocus.
        if trace: print '*'*20,'oops: moving to body pane.'
        c.bodyWantsFocusNow()

restoreRequestedFocus = masterFocusHandler
#@-node:ekr.20060207140352:c.masterFocusHandler
#@+node:ekr.20080514131122.21:c.outerUpdate
def outerUpdate (self):

    pass
#@-node:ekr.20080514131122.21:c.outerUpdate
#@+node:ekr.20031218072017.2953:c.recolor & requestRecolor
def recolor(self):

    c = self
    c.frame.body.recolor(c.currentPosition())

def requestRecolor (self):

    c = self
    c.frame.requestRecolorFlag = True
#@-node:ekr.20031218072017.2953:c.recolor & requestRecolor
#@+node:ekr.20051216171520:c.recolor_now
def recolor_now(self,p=None,incremental=False,interruptable=True):

    c = self
    if p is None:
        p = c.currentPosition()

    c.frame.body.colorizer.colorize(p,
        incremental=incremental,interruptable=interruptable)
#@-node:ekr.20051216171520:c.recolor_now
#@+node:ekr.20031218072017.2954:c.redraw and c.redraw_now
def redraw (self):
    c = self
    c.beginUpdate()
    c.endUpdate()

def redraw_now (self):

    c = self

    if g.app.quitting or not c.exists or not hasattr(c.frame,'top'):
        return # nullFrame's do not have a top frame.

    c.frame.tree.redraw_now()
    if 0: # Interferes with new colorizer.
        c.frame.top.update_idletasks()

    if c.frame.requestRecolorFlag:
        c.frame.requestRecolorFlag = False
        c.recolor()

# Compatibility with old scripts
force_redraw = redraw_now
#@-node:ekr.20031218072017.2954:c.redraw and c.redraw_now
#@+node:ekr.20060208143543:c.restoreFocus
def restoreFocus (self):

    '''Ensure that the focus eventually gets restored.'''

    c =self
    trace = not g.app.unitTesting and c.config.getBool('trace_focus')

    if c.requestedFocusWidget:
        c.hasFocusWidget = None # Force an update
    elif c.hasFocusWidget:
        c.requestedFocusWidget = c.hasFocusWidget
        c.hasFocusWidget = None # Force an update
    else:
        # Should not happen, except during unit testing.
        # c.masterFocusHandler sets c.hasFocusWidget,
        # so if it is not set here it is because this method cleared it.
        if not g.app.unitTesting: g.trace('oops: no requested or present widget.',g.callers())
        c.bodyWantsFocusNow()

    if c.inCommand:
        if trace: g.trace('expecting later call to c.masterFocusHandler')
        # A call to c.masterFocusHandler will surely happen.
    else:
        c.masterFocusHandler() # Do it now.
#@-node:ekr.20060208143543:c.restoreFocus
#@+node:ekr.20060207142332:c.traceFocus
trace_focus_count = 0

def traceFocus (self,w):

    c = self

    if False or (not g.app.unitTesting and c.config.getBool('trace_focus')):
        c.trace_focus_count += 1
        print '%4d' % (c.trace_focus_count),c.widget_name(w),g.callers(8)
#@-node:ekr.20060207142332:c.traceFocus
#@+node:ekr.20060205111103:c.widget_name
def widget_name (self,widget):

    c = self

    return g.app.gui and g.app.gui.widget_name(widget) or ''
#@-node:ekr.20060205111103:c.widget_name
#@+node:ekr.20050120092028:c.xWantsFocus
def bodyWantsFocus(self):
    c = self ; body = c.frame.body
    c.request_focus(body and body.bodyCtrl)
def headlineWantsFocus(self,p):
    c = self
    c.request_focus(p and c.edit_widget(p))

def logWantsFocus(self):
    c = self ; log = c.frame.log
    c.request_focus(log and log.logCtrl)

def minibufferWantsFocus(self):
    c = self ; k = c.k
    if k: k.minibufferWantsFocus()

def treeWantsFocus(self):
    c = self ; tree = c.frame.tree
    c.request_focus(tree and tree.canvas)

def widgetWantsFocus(self,w):
    c = self ; c.request_focus(w)
#@-node:ekr.20050120092028:c.xWantsFocus
#@+node:ekr.20060210102201:c.xWantsFocusNow
def bodyWantsFocusNow(self):
    c = self ; body = c.frame.body
    #g.trace(body and body.bodyCtrl)
    c.set_focus(body and body.bodyCtrl,force=True)

def headlineWantsFocusNow(self,p):
    c = self
    c.set_focus(p and c.edit_widget(p),force=True)

def logWantsFocusNow(self):
    c = self ; log = c.frame.log
    c.set_focus(log and log.logCtrl,force=True)

def minibufferWantsFocusNow(self):
    c = self ; k = c.k
    if k: k.minibufferWantsFocusNow()

def treeWantsFocusNow(self):
    c = self ; tree = c.frame.tree
    c.set_focus(tree and tree.canvas,force=True)

def widgetWantsFocusNow(self,w):
    c = self ; c.set_focus(w,force=True)
#@-node:ekr.20060210102201:c.xWantsFocusNow
#@-node:ekr.20080514131122.4:Legacy code
#@+node:ekr.20080514131122.6:New code
if g.newDrawing:

    @others

else:

    def outerUpdate (self):
        pass
#@+node:ekr.20080514131122.7:c.begin/endUpdate

def beginUpdate(self):

    '''Suppress redraws of the tree (except for explict calls to c.redraw_now)
    until the matching call to endUpdate.'''

    pass

def endUpdate(self,flag=True,scroll=True):

    '''Redraw the screen if flag is True.'''

    c = self
    if flag:
        c.requestRedrawFlag = True
        c.requestRedrawScrollFlag = scroll
        # g.trace('flag is True','scroll',scroll,c)

BeginUpdate = beginUpdate # Compatibility with old scripts
EndUpdate = endUpdate # Compatibility with old scripts
#@-node:ekr.20080514131122.7:c.begin/endUpdate
#@+node:ekr.20080515053412.1:c.bind & c.bind2
def bind (self,w,pattern,func):

    c = self

    def bindCallback(event,c=c,func=func):
        val = func(event)
        # Careful: func may destroy c.
        if c.exists: c.outerUpdate()
        return val

    w.bind(pattern,bindCallback)

def bind2 (self,w,pattern,func,extra):

    c = self

    def bindCallback(event,c=c,func=func):
        val = func(event)
        # Careful: func may destroy c.
        if c.exists: c.outerUpdate()
        return val

    w.bind(pattern,bindCallback,extra)
#@-node:ekr.20080515053412.1:c.bind & c.bind2
#@+node:ekr.20080514131122.8:c.bringToFront
def bringToFront(self,set_focus=True):

    c = self
    c.requestedIconify = 'deiconify'
    c.requestedFocusWidget = c.frame.body.bodyCtrl

BringToFront = bringToFront # Compatibility with old scripts
#@-node:ekr.20080514131122.8:c.bringToFront
#@+node:ekr.20080514131122.9:c.get/request/set_focus
def get_focus (self):

    c = self
    return g.app.gui and g.app.gui.get_focus(c)

def get_requested_focus (self):

    c = self
    return c.requestedFocusWidget

def request_focus(self,w):

    c = self
    if w: c.requestedFocusWidget = w

def set_focus (self,w,force=False):

    c = self
    if w and g.app.gui and c.requestedFocusWidget:
        g.app.gui.set_focus(c,w)

    c.requestedFocusWidget = None
#@-node:ekr.20080514131122.9:c.get/request/set_focus
#@+node:ekr.20080514131122.10:c.invalidateFocus
def invalidateFocus (self):

    '''Indicate that the focus is in an invalid location, or is unknown.'''

    # c = self
    # c.requestedFocusWidget = None
    pass
#@nonl
#@-node:ekr.20080514131122.10:c.invalidateFocus
#@+node:ekr.20080514131122.11:c.masterFocusHandler
def masterFocusHandler (self):

    pass # No longer used.

    # c = self
    # trace = False or (not g.app.unitTesting and c.config.getBool('trace_masterFocusHandler'))

    # # Give priority to later requests, but default to previously set widget.
    # w = c.requestedFocusWidget or c.hasFocusWidget

    # if trace: print \
        # 'requested',c.widget_name(c.requestedFocusWidget),\
        # 'present',c.widget_name(c.hasFocusWidget)

    # if c.hasFocusWidget and (
        # not c.requestedFocusWidget or c.requestedFocusWidget == c.hasFocusWidget):
        # if trace: print 'no change.',c.widget_name(w)
        # c.requestedFocusWidget = None
    # elif w:
        # # Ignore whatever g.app.gui.get_focus might say.
        # ok = g.app.gui.set_focus(c,w)
        # if ok: c.hasFocusWidget = w
        # c.requestedFocusWidget = None    # c = self
    # trace = False or (not g.app.unitTesting and c.config.getBool('trace_masterFocusHandler'))

    # # Give priority to later requests, but default to previously set widget.
    # w = c.requestedFocusWidget or c.hasFocusWidget

    # if trace: print \
        # 'requested',c.widget_name(c.requestedFocusWidget),\
        # 'present',c.widget_name(c.hasFocusWidget)

    # if c.hasFocusWidget and (
        # not c.requestedFocusWidget or c.requestedFocusWidget == c.hasFocusWidget):
        # if trace: print 'no change.',c.widget_name(w)
        # c.requestedFocusWidget = None
    # elif w:
        # # Ignore whatever g.app.gui.get_focus might say.
        # ok = g.app.gui.set_focus(c,w)
        # if ok: c.hasFocusWidget = w
        # c.requestedFocusWidget = None
    # else:
        # # This is not an error: it can arise because of a call to k.invalidateFocus.
        # if trace: print '*'*20,'oops: moving to body pane.'
        # c.bodyWantsFocusNow()
    # else:
        # # This is not an error: it can arise because of a call to k.invalidateFocus.
        # if trace: print '*'*20,'oops: moving to body pane.'
        # c.bodyWantsFocusNow()

restoreRequestedFocus = masterFocusHandler
#@-node:ekr.20080514131122.11:c.masterFocusHandler
#@+node:ekr.20080514131122.20:c.outerUpdate
def outerUpdate (self):

    c = self ; aList = []

    if not c.exists or not c.k:
        return

    if c.requestedIconify == 'iconify':
        aList.append('iconify')
        c.frame.iconify()

    if c.requestedIconify == 'deiconify':
        aList.append('deiconify')
        c.frame.deiconify()

    if c.requestRedrawFlag:
        aList.append('redraw') # : scroll: %s' % (c.requestRedrawScrollFlag))
        c.frame.tree.redraw_now(scroll=c.requestRedrawScrollFlag)

    if c.requestRecolorFlag:
        aList.append('%srecolor' % (
            g.choose(c.incrementalRecolorFlag,'','full ')))
        c.recolor_now(incremental=c.incrementalRecolorFlag)

    if c.requestedFocusWidget:
        aList.append('focus: %s' % (
            g.app.gui.widget_name(c.requestedFocusWidget)))
        c.set_focus(c.requestedFocusWidget)
    else:
        # We can not set the focus to the body pane:
        # That would make nested calls to c.outerUpdate significant.
        pass

    # if aList: g.trace(', '.join(aList)) # ,g.callers(5))

    c.incrementalRecolorFlag = False
    c.requestRecolorFlag = None
    c.requestRedrawFlag = False
    c.requestedFocusWidget = None
    c.requestedIconify = ''
    c.requestedRedrawScrollFlag = False
#@-node:ekr.20080514131122.20:c.outerUpdate
#@+node:ekr.20080514131122.12:c.recolor & requestRecolor
def requestRecolor (self):

    c = self
    c.frame.requestRecolorFlag = True

recolor = requestRecolor
#@-node:ekr.20080514131122.12:c.recolor & requestRecolor
#@+node:ekr.20080514131122.13:c.recolor_now
def recolor_now(self,p=None,incremental=False,interruptable=True):

    c = self
    if p is None:
        p = c.currentPosition()

    c.frame.body.colorizer.colorize(p,
        incremental=incremental,interruptable=interruptable)
#@-node:ekr.20080514131122.13:c.recolor_now
#@+node:ekr.20080514131122.14:c.redraw and c.redraw_now
def redraw (self):
    c = self
    c.requestRedrawFlag = True

def redraw_now (self):
    c = self
    c.requestRedrawFlag = True

# Compatibility with old scripts
force_redraw = redraw_now
#@-node:ekr.20080514131122.14:c.redraw and c.redraw_now
#@+node:ekr.20080514131122.15:c.restoreFocus
def restoreFocus (self):

    '''Ensure that the focus eventually gets restored.'''
    pass
    # g.trace(g.callers(5))

    # c =self
    # trace = not g.app.unitTesting and c.config.getBool('trace_focus')

    # if c.requestedFocusWidget:
        # c.hasFocusWidget = None # Force an update
    # elif c.hasFocusWidget:
        # c.requestedFocusWidget = c.hasFocusWidget
        # c.hasFocusWidget = None # Force an update
    # else:
        # # Should not happen, except during unit testing.
        # # c.masterFocusHandler sets c.hasFocusWidget,
        # # so if it is not set here it is because this method cleared it.
        # if not g.app.unitTesting: g.trace('oops: no requested or present widget.',g.callers())
        # c.bodyWantsFocusNow()

    # if c.inCommand:
        # if trace: g.trace('expecting later call to c.masterFocusHandler')
        # # A call to c.masterFocusHandler will surely happen.
    # else:
        # c.masterFocusHandler() # Do it now.
#@-node:ekr.20080514131122.15:c.restoreFocus
#@+node:ekr.20080514131122.16:c.traceFocus
trace_focus_count = 0

def traceFocus (self,w):

    c = self

    if False or (not g.app.unitTesting and c.config.getBool('trace_focus')):
        c.trace_focus_count += 1
        print '%4d' % (c.trace_focus_count),c.widget_name(w),g.callers(8)
#@-node:ekr.20080514131122.16:c.traceFocus
#@+node:ekr.20080514131122.17:c.widget_name
def widget_name (self,widget):

    c = self

    return g.app.gui and g.app.gui.widget_name(widget) or ''
#@-node:ekr.20080514131122.17:c.widget_name
#@+node:ekr.20080514131122.18:c.xWantsFocus (no change)
def bodyWantsFocus(self):
    c = self ; body = c.frame.body
    c.request_focus(body and body.bodyCtrl)

def headlineWantsFocus(self,p):
    c = self
    c.request_focus(p and c.edit_widget(p))

def logWantsFocus(self):
    c = self ; log = c.frame.log
    c.request_focus(log and log.logCtrl)

def minibufferWantsFocus(self):
    c = self ; k = c.k
    if k: k.minibufferWantsFocus()

def treeWantsFocus(self):
    c = self ; tree = c.frame.tree
    c.request_focus(tree and tree.canvas)

def widgetWantsFocus(self,w):
    c = self ; c.request_focus(w)
#@-node:ekr.20080514131122.18:c.xWantsFocus (no change)
#@+node:ekr.20080514131122.19:c.xWantsFocusNow 
# widgetWantsFocusNow does an automatic update.
def widgetWantsFocusNow(self,w):
    c = self
    c.request_focus(w)
    c.outerUpdate()
    # Re-request widget so we don't use the body by default.
    c.request_focus(w) 

# All other "Now" methods wait.
bodyWantsFocusNow = bodyWantsFocus
headlineWantsFocusNow = headlineWantsFocus
logWantsFocusNow = logWantsFocus
minibufferWantsFocusNow = minibufferWantsFocus
treeWantsFocusNow = treeWantsFocus
#@-node:ekr.20080514131122.19:c.xWantsFocusNow 
#@-node:ekr.20080514131122.6:New code
#@-node:ekr.20031218072017.2949:Drawing Utilities (commands)
#@+node:ekr.20080509145618.1:Config...
#@+node:ekr.20080510055530.4:@strings  [ignore,insert, overwrite] top_level_unbound_key_action = insert
@nocolor

This setting determines the initial binding for otherwise-unbound keystrokes
when no mode is in effect.  Note: the keyboard-quit command exits all modes.

The valid values are::

ignore:  Leo ignores the key (like Vim).
insert:  Leo inserts the key at the cursor (like Emacs)
overwrite: Leo replaces the character at the cursor.
#@nonl
#@-node:ekr.20080510055530.4:@strings  [ignore,insert, overwrite] top_level_unbound_key_action = insert
#@+node:ekr.20060102103625:createModeCommand (parserBaseClass)
def createModeCommand (self,modeName,name,modeDict):

    modeName = 'enter-' + modeName.replace(' ','-')

    i = name.find('::')
    if i > -1:
        # The prompt is everything after the '::'
        prompt = name[i+2:].strip()
        modeDict ['*command-prompt*'] = prompt
        # g.trace('modeName',modeName,'*command-prompt*',prompt)

    # Save the info for k.finishCreate and k.makeAllBindings.
    d = g.app.config.modeCommandsDict

    # New in 4.4.1 b2: silently allow redefinitions of modes.
    d [modeName] = modeDict
#@-node:ekr.20060102103625:createModeCommand (parserBaseClass)
#@+node:ekr.20060102103625.1:doMode (ParserBaseClass)
def doMode(self,p,kind,name,val):

    '''Parse an @mode node and create the enter-<name>-mode command.'''

    # __pychecker__ = '--no-argsused' # val not used.

    c = self.c ; k = c.k ; name1 = name

    # g.trace('%20s' % (name),c.fileName())
    modeName = self.computeModeName(name)

    # Create a local shortcutsDict.
    old_d = self.shortcutsDict
    d = self.shortcutsDict = {}

    s = p.bodyString()
    lines = g.splitLines(s)
    for line in lines:
        line = line.strip()
        if line and not g.match(line,0,'#'):
            name,bunch = self.parseShortcutLine(line)
            if not name:
                # An entry command: put it in the special *entry-commands* key.
                aList = d.get('*entry-commands*',[])
                aList.append(bunch.entryCommandName)
                d ['*entry-commands*'] = aList
            elif bunch is not None:
                # A regular shortcut.
                bunch.val = k.strokeFromSetting(bunch.val)
                bunch.pane = modeName
                bunchList = d.get(name,[])
                # Important: use previous bindings if possible.
                key2,bunchList2 = c.config.getShortcut(name)
                bunchList3 = [b for b in bunchList2 if b.pane != modeName]
                if bunchList3:
                    # g.trace('inheriting',[b.val for b in bunchList3])
                    bunchList.extend(bunchList3)
                bunchList.append(bunch)
                d [name] = bunchList
                self.set(p,"shortcut",name,bunchList)
                self.setShortcut(name,bunchList)

    # Restore the global shortcutsDict.
    self.shortcutsDict = old_d

    # Create the command, but not any bindings to it.
    self.createModeCommand(modeName,name1,d)
#@-node:ekr.20060102103625.1:doMode (ParserBaseClass)
#@-node:ekr.20080509145618.1:Config...
#@+node:ekr.20080512061556.1:Modes no longer put focus in minibuffer
#@+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)

    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.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)
        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 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.

    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()',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)
            # 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.20061031170011.8:setLabel
def setLabel (self,s,protect=False):

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

    if trace: g.trace('protect',protect,s)

    w.setAllText(s)
    n = len(s)
    w.setSelectionRange(n,n,insert=n)
    c.masterFocusHandler() # Restore to the previously requested focus.

    if protect:
        k.mb_prefix = s
#@-node:ekr.20061031170011.8:setLabel
#@+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)
        #### c.minibufferWantsFocus()
#@-node:ekr.20061031131434.164:reinitMode
#@-node:ekr.20080512061556.1:Modes no longer put focus in minibuffer
#@+node:ekr.20080512091707.1:Reset input state in end-edit-headline
#@+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)

    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.20031218072017.3982:frame.endEditLabelCommand
def endEditLabelCommand (self,event=None):

    '''End editing of a headline and move focus to the body pane.'''

    frame = self ; c = frame.c ; k = c.k
    if g.app.batchMode:
        c.notValidInBatchMode("End Edit Headline")
    else:
        c.endEditing()
        # g.trace('setting focus')
        if c.config.getBool('stayInTreeAfterEditHeadline'):
            c.treeWantsFocusNow()
        else:
            c.bodyWantsFocusNow()

            if k:
                k.setDefaultInputState()
                # Recolor the *body* text, **not** the headline.
                k.showStateAndMode(w=c.frame.body.bodyCtrl)
#@-node:ekr.20031218072017.3982:frame.endEditLabelCommand
#@+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.20080511122507.4:setDefaultInputState
def setDefaultInputState (self):

    k = self
    k.setInputState(k.defaultUnboundKeyAction)
#@nonl
#@-node:ekr.20080511122507.4:setDefaultInputState
#@+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','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) ### ,setDefaultUnboundKeyAction=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.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.20080512091707.1:Reset input state in end-edit-headline
#@+node:ekr.20080511041907.3:Body colors
#@+node:ekr.20031218072017.838:tkBody.createBindings
def createBindings (self,w=None):

    '''(tkBody) Create gui-dependent bindings.
    These are *not* made in nullBody instances.'''

    frame = self.frame ; c = self.c ; k = c.k
    if not w: w = self.bodyCtrl

    c.bind(w,'<Key>', k.masterKeyHandler)

    def onFocusOut(event,c=c):
        # This interferes with inserting new nodes.
            # c.k.setDefaultInputState()
        self.setEditorColors(
            bg=c.k.unselected_body_bg_color,
            fg=c.k.unselected_body_fg_color)
        # This is required, for example, when typing Alt-Shift-anyArrow in insert mode.
        # But we suppress coloring in the widget.
        oldState = k.unboundKeyAction
        k.unboundKeyAction = k.defaultUnboundKeyAction
        c.k.showStateAndMode(w=c.frame.tree.canvas)
        k.unboundKeyAction = oldState

    def onFocusIn(event,c=c):
        # g.trace('callback')
        c.k.setDefaultInputState()

    c.bind(w,'<FocusOut>', onFocusOut)
    c.bind(w,'<FocusIn>', onFocusIn)

    table = [
        ('<Button-1>',  frame.OnBodyClick,          k.masterClickHandler),
        ('<Button-3>',  frame.OnBodyRClick,         k.masterClick3Handler),
        ('<Double-1>',  frame.OnBodyDoubleClick,    k.masterDoubleClickHandler),
        ('<Double-3>',  None,                       k.masterDoubleClick3Handler),
        ('<Button-2>',  frame.OnPaste,              k.masterClickHandler),
    ]

    table2 = (
        ('<Button-2>',  frame.OnPaste,              k.masterClickHandler),
    )

    if c.config.getBool('allow_middle_button_paste'):
        table.extend(table2)

    for kind,func,handler in table:
        def bodyClickCallback(event,handler=handler,func=func):
            return handler(event,func)

        c.bind(w,kind,bodyClickCallback)
#@-node:ekr.20031218072017.838:tkBody.createBindings
#@+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.keys():
        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.20080511041907.3:Body colors
#@+node:ekr.20080512125926.1:Fixed headline bugs
@nocolor

- Unbound keys valid in command mode.
- Insert mode does not persist.

@color
#@nonl
#@+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
    state = k.getState('getArg')
    keysym = gui.eventKeysym(event)
    trace = False or (c.config.getBool('trace_modes') and not g.app.unitTesting)
    if trace: g.trace(
        'state',state,'keysym',keysym,'stroke',stroke,'escapes',k.getArgEscapes,
        'completion', state==0 and completion or state!=0 and k.arg_completion)
    if state == 0:
        k.arg = ''
        << init altX vars >>
        # 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 == 'Tab':
        k.doTabCompletion(k.argTabList,k.arg_completion)
    elif keysym == 'BackSpace':
        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.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 >>
#@-node:ekr.20061031131434.128:getArg
#@+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

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

    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'
    else:
        # Let tkinter handle the event.
        # ch = event and event.char ; g.trace('to tk:',name,repr(ch))
        return None
#@-node:ekr.20061031131434.110:k.handleDefaultChar
#@+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.'''

    # g.trace('event.keysym_num',event.keysym_num,event,dir(event))

    << define vars >>
    if keysym in special_keys: return None

    trace = (False or self.trace_masterKeyHandler) and not g.app.unitTesting
    traceGC = self.trace_masterKeyHandlerGC and not g.app.unitTesting
    if traceGC: g.printNewObjects('masterKey 1')
    if trace:
        g.trace('stroke:',repr(stroke),'keysym:',repr(event.keysym),'ch:',repr(event.char))
            # 'state.kind:',k.state.kind),'\n',g.callers())
        # if (self.master_key_count % 100) == 0: g.printGcSummary()

    # Handle keyboard-quit first.
    if k.abortAllModesKey and stroke == k.abortAllModesKey:
        # g.trace('special case')
        return k.masterCommand(event,k.keyboardQuit,stroke,'keyboard-quit')

    if k.inState():
        # This will return unless k.autoCompleterStateHandler
        # (called from k.callStateFunction) returns 'do-standard-keys'
        << handle mode bindings >>

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

    if stroke and isPlain:
        << handle special cases for plain keys >>

    << handle per-pane bindings >>

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

    return k.handleUnboundKeys(event,char,keysym,stroke)
#@+node:ekr.20061031131434.147:<< define vars >>
k = self ; c = k.c ; gui = g.app.gui

if event: event = gui.leoKeyEvent(event,c)

w = event.widget
char = event.char
keysym = event.keysym
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',
    '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 >>
#@+node:ekr.20061031131434.149:<< handle mode bindings >>
# 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 'break'

# Second, honor general modes.
if state == 'getArg':
    return k.getArg(event,stroke=stroke)
elif state == 'getFileName':
    return 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) # k.state.handler)
    val = k.callStateFunction(event) # Calls end-command.
    if val != 'do-standard-keys': return 'break'

# Third, pass keys to user modes.
else:
    d =  k.masterBindingsDict.get(state)
    if d:
        b = d.get(stroke)
        if b:
            if trace: g.trace('calling generalModeHandler')
            k.generalModeHandler (event,
                commandName=b.commandName,func=b.func,
                modeName=state,nextMode=b.nextMode)
            return 'break'
        else:
            # New in Leo 4.5: unbound keys end mode.
            k.endMode(event)
    else:
        # New in 4.4b4.
        handler = k.getStateHandler()
        if handler:
            handler(event)
        else:
            g.trace('No state handler for %s' % state)
        return 'break'
#@-node:ekr.20061031131434.149:<< handle mode bindings >>
#@+node:ekr.20080510153327.4:<< handle special cases for plain keys >>
# g.trace('plain key','state',k.unboundKeyAction,'stroke',stroke)

# 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))
                k.masterCommand(event,b.func,b.stroke,b.commandName)
                return 'break'

    if trace: g.trace('%s in %s mode' % (stroke,k.unboundKeyAction))
    k.masterCommand(event,func=None,stroke=stroke,commandName=None)
    return 'break'

# Bound   plain keys in command mode are by the per-pane logic.
# Unbound plain keys in command mode are ignored by handleUnboundKeys.

# This code ignores all command-state keys if we are not in a text widget.
elif k.unboundKeyAction == 'command':
    if not g.app.gui.isTextWidget(w):
        c.onCanvasKey(event)
        return 'break'

# elif k.unboundKeyAction == 'command':
    # # In command mode we must disallow all keys except those actually bound in command-state.
    # # That is, we cannot use general per-pane mode bindings.
    # for key,name in (
        # # Order here is similar to bindtags order.
        # ('command',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 (
            # 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,{})
            # # g.trace('key',key,'name',name,'stroke',stroke,'stroke in d.keys',stroke in d.keys())
            # if d:
                # b = d.get(stroke)
                # if b:
                    # if trace: g.trace('%s found %s = %s' % (key,repr(b.stroke),b.commandName))
                    # return k.masterCommand(event,b.func,b.stroke,b.commandName)
    # else:
        # if trace: g.trace('ignoring %s in command mode' % b.stroke)
        # return 'break' # Disallow all unbound plain keys in command mode.
#@nonl
#@-node:ekr.20080510153327.4:<< handle special cases for plain keys >>
#@+node:ekr.20061031131434.150:<< handle per-pane bindings >>
keyStatesTuple = ('command','insert','overwrite')

# g.trace('w_name',w_name,'w',w,'isTextWidget(w)',g.app.gui.isTextWidget(w))
# g.trace('button',k.masterBindingsDict.get('button'))

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,{})
        # g.trace('key',key,'name',name,'stroke',stroke,'stroke in d.keys',stroke in d.keys())
        if d:
            b = d.get(stroke)
            if b:
                if trace: g.trace('%s found %s = %s' % (key,repr(b.stroke),b.commandName))
                if traceGC: g.printNewObjects('masterKey 3')
                return k.masterCommand(event,b.func,b.stroke,b.commandName)
#@-node:ekr.20061031131434.150:<< handle per-pane bindings >>
#@+node:ekr.20061031131434.108:k.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:k.callStateFunction
#@+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','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) ### ,setDefaultUnboundKeyAction=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:handleUnboudKeys
def handleUnboundKeys (self,event,char,keysym,stroke):

    k = self ; c = k.c
    modesTuple = ('insert','overwrite')
    trace = False

    if trace:
        # if stroke: g.trace('***unexpected stroke***')
        g.trace('keysym:',repr(event.keysym),'ch:',repr(event.char))
            # 'state.kind:',k.state.kind),'\n',g.callers())
        # if (self.master_key_count % 100) == 0: g.printGcSummary()

    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 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.find('Escape') != -1:
        # Never insert escape 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:handleUnboudKeys
#@-node:ekr.20061031131434.146:masterKeyHandler & helpers
#@-node:ekr.20080512125926.1:Fixed headline bugs
#@+node:ekr.20080512144457.1:newline does not stick
#@+node:ekr.20051125080855:selfInsertCommand, helpers
def selfInsertCommand(self,event,action='insert'):

    '''Insert a character in the body pane.
    This is the default binding for all keys in the body pane.'''

    w = self.editWidget(event)
    if not w: return 'break'
    << set local vars >>
    #g.trace('ch',repr(ch))
    if g.doHook("bodykey1",c=c,p=p,v=p,ch=ch,oldSel=oldSel,undoType=undoType):
        return "break" # The hook claims to have handled the event.
    if ch == '\t':
        self.updateTab(p,w)
    elif ch == '\b':
        # This is correct: we only come here if there no bindngs for this key. 
        self.backwardDeleteCharacter(event)
    elif ch in ('\r','\n'):
        ch = '\n'
        self.insertNewlineHelper(w,oldSel,undoType)
    elif inBrackets and self.autocompleteBrackets:
        self.updateAutomatchBracket(p,w,ch,oldSel)
    elif ch: # Null chars must not delete the selection.
        i,j = oldSel
        if i > j: i,j = j,i
        # Use raw insert/delete to retain the coloring.
        if i != j:                  w.delete(i,j)
        elif action == 'overwrite': w.delete(i)
        w.insert(i,ch)
        w.setInsertPoint(i+1)
        if inBrackets and self.flashMatchingBrackets:

            self.flashMatchingBracketsHelper(w,i,ch)               
    else:
        return 'break' # This method *always* returns 'break'

    # Set the column for up and down keys.
    spot = w.getInsertPoint()
    c.editCommands.setMoveCol(w,spot)

    # Update the text and handle undo.
    newText = w.getAllText()
    changed = newText != oldText
    # g.trace('ch',repr(ch),'changed',changed,'newText',repr(newText[-10:]))
    if changed:
        # g.trace('ins',w.getInsertPoint())
        c.frame.body.onBodyChanged(undoType=undoType,
            oldSel=oldSel,oldText=oldText,oldYview=None)

    g.doHook("bodykey2",c=c,p=p,v=p,ch=ch,oldSel=oldSel,undoType=undoType)
    return 'break'
#@+node:ekr.20061103114242:<< set local vars >>
c = self.c
p = c.currentPosition()
gui = g.app.gui
ch = gui.eventChar(event)
keysym = gui.eventKeysym(event)
if keysym == 'Return':
    ch = '\n' # This fixes the MacOS return bug.
if keysym == 'Tab': # Support for wx_alt_gui plugin.
    ch = '\t'
name = c.widget_name(w)
oldSel =  name.startswith('body') and w.getSelectionRange() or (None,None)
oldText = name.startswith('body') and p.bodyString() or ''
undoType = 'Typing'
trace = c.config.getBool('trace_masterCommand')
brackets = self.openBracketsList + self.closeBracketsList
inBrackets = ch and g.toUnicode(ch,g.app.tkEncoding) in brackets
if trace: g.trace(name,repr(ch),ch and ch in brackets)
#@nonl
#@-node:ekr.20061103114242:<< set local vars >>
#@+node:ekr.20051026171121:insertNewlineHelper
def insertNewlineHelper (self,w,oldSel,undoType):

    c = self.c ; p = c.currentPosition()
    i,j = oldSel ; ch = '\n'

    if i != j:
        # No auto-indent if there is selected text.
        w.delete(i,j)
        w.insert(i,ch)
        w.setInsertPoint(i+1)
    else:
        w.insert(i,ch)
        w.setInsertPoint(i+1)

        allow_in_nocolor = c.config.getBool('autoindent_in_nocolor_mode')
        if (
            (allow_in_nocolor or c.frame.body.colorizer.useSyntaxColoring(p)) and
            undoType != "Change"
        ):
            # No auto-indent if in @nocolor mode or after a Change command.
            self.updateAutoIndent(p,w)

    w.seeInsertPoint()
#@nonl
#@-node:ekr.20051026171121:insertNewlineHelper
#@+node:ekr.20060804095512:initBracketMatcher
def initBracketMatcher (self,c):

    self.openBracketsList  = c.config.getString('open_flash_brackets')  or '([{'
    self.closeBracketsList = c.config.getString('close_flash_brackets') or ')]}'

    if len(self.openBracketsList) != len(self.closeBracketsList):
        g.es_print('bad open/close_flash_brackets setting: using defaults')
        self.openBracketsList  = '([{'
        self.closeBracketsList = ')]}'

    # g.trace('self.openBrackets',openBrackets)
    # g.trace('self.closeBrackets',closeBrackets)
#@-node:ekr.20060804095512:initBracketMatcher
#@+node:ekr.20060627083506:flashMatchingBracketsHelper
def flashMatchingBracketsHelper (self,w,i,ch):

    d = {}
    if ch in self.openBracketsList:
        for z in xrange(len(self.openBracketsList)):
            d [self.openBracketsList[z]] = self.closeBracketsList[z]
        reverse = False # Search forward
    else:
        for z in xrange(len(self.openBracketsList)):
            d [self.closeBracketsList[z]] = self.openBracketsList[z]
        reverse = True # Search backward

    delim2 = d.get(ch)

    s = w.getAllText()
    j = g.skip_matching_python_delims(s,i,ch,delim2,reverse=reverse)
    if j != -1:
        self.flashCharacter(w,j)
#@-node:ekr.20060627083506:flashMatchingBracketsHelper
#@+node:ekr.20060627091557:flashCharacter
def flashCharacter(self,w,i):

    bg      = self.bracketsFlashBg or 'DodgerBlue1'
    fg      = self.bracketsFlashFg or 'white'
    flashes = self.bracketsFlashCount or 2
    delay   = self.bracketsFlashDelay or 75

    w.flashCharacter(i,bg,fg,flashes,delay)
#@-node:ekr.20060627091557:flashCharacter
#@+node:ekr.20051027172949:updateAutomatchBracket
def updateAutomatchBracket (self,p,w,ch,oldSel):

    # assert ch in ('(',')','[',']','{','}')

    c = self.c ; d = g.scanDirectives(c,p)
    i,j = oldSel
    language = d.get('language')
    s = w.getAllText()

    if ch in ('(','[','{',):
        automatch = language not in ('plain',)
        if automatch:
            ch = ch + {'(':')','[':']','{':'}'}.get(ch)
        if i != j: w.delete(i,j)
        w.insert(i,ch)
        if automatch:
            ins = w.getInsertPoint()
            w.setInsertPoint(ins-1)
    else:
        ins = w.getInsertPoint()
        ch2 = ins<len(s) and s[ins] or ''
        if ch2 in (')',']','}'):
            ins = w.getInsertPoint()
            w.setInsertPoint(ins+1)
        else:
            if i != j: w.delete(i,j)
            w.insert(i,ch)
            w.setInsertPoint(i+1)
#@-node:ekr.20051027172949:updateAutomatchBracket
#@+node:ekr.20051026171121.1:udpateAutoIndent
def updateAutoIndent (self,p,w):

    c = self.c ; d = g.scanDirectives(c,p)
    tab_width = d.get("tabwidth",c.tab_width)
    # Get the previous line.
    s = w.getAllText()
    ins = w.getInsertPoint()
    i = g.skip_to_start_of_line(s,ins)
    i,j = g.getLine(s,i-1)
    s = s[i:j-1]
    # g.trace(i,j,repr(s))

    # Add the leading whitespace to the present line.
    junk, width = g.skip_leading_ws_with_indent(s,0,tab_width)
    # g.trace('width',width,'tab_width',tab_width)

    if s and s [-1] == ':':
        # For Python: increase auto-indent after colons.
        if g.scanColorDirectives(c,p) == 'python':
            width += abs(tab_width)
    if self.smartAutoIndent:
        # Determine if prev line has unclosed parens/brackets/braces
        bracketWidths = [width] ; tabex = 0
        for i in range(0,len(s)):
            if s [i] == '\t':
                tabex += tab_width-1
            if s [i] in '([{':
                bracketWidths.append(i+tabex+1)
            elif s [i] in '}])' and len(bracketWidths) > 1:
                bracketWidths.pop()
        width = bracketWidths.pop()
    ws = g.computeLeadingWhitespace(width,tab_width)
    if ws:
        i = w.getInsertPoint()
        w.insert(i,ws)
        w.setInsertPoint(i+len(ws))
#@-node:ekr.20051026171121.1:udpateAutoIndent
#@+node:ekr.20051026092433:updateTab
def updateTab (self,p,w):

    c = self.c
    d = g.scanDirectives(c,p)
    tab_width = d.get("tabwidth",c.tab_width)
    i,j = w.getSelectionRange()
        # Returns insert point if no selection, with i <= j.

    if i != j:
        w.delete(i,j)

    if tab_width > 0:
        w.insert(i,'\t')
        ins = i+1
    else:
        # Get the preceeding characters.
        s = w.getAllText()
        start = g.skip_to_start_of_line(s,i)
        s2 = s[start:i]

        # Compute n, the number of spaces to insert.
        width = g.computeWidth(s2,tab_width)
        n = abs(tab_width) - (width % abs(tab_width))
        # g.trace('n',n)
        w.insert(i,' ' * n)
        ins = i+n

    w.setSelectionRange(ins,ins,insert=ins)
#@nonl
#@-node:ekr.20051026092433:updateTab
#@-node:ekr.20051125080855:selfInsertCommand, helpers
#@+node:ekr.20050920084036.138:insertNewLine (changed)
def insertNewLine (self,event):

    '''Insert a newline at the cursor.'''

    c = self.c ; k = c.k ; w = self.editWidget(event)
    if not w: return

    name = c.widget_name(w)
    oldSel =  name.startswith('body') and w.getSelectionRange() or (None,None)

    self.beginCommand(undoType='newline')

    # New in Leo 4.5: use the same logic as in selfInsertCommand.
    self.insertNewlineHelper(w=w,oldSel=oldSel,undoType=None)
    k.setInputState('insert')
    k.showStateAndMode()

    self.endCommand()

insertNewline = insertNewLine
#@-node:ekr.20050920084036.138:insertNewLine (changed)
#@+node:ekr.20050920084036.86:insertNewLineAndTab (changed)
def insertNewLineAndTab (self,event):

    '''Insert a newline and tab at the cursor.'''

    c = self.c ; k = c.k
    w = self.editWidget(event) ; p = c.currentPosition()
    if not w: return
    name = c.widget_name(w)
    if name.startswith('head'): return

    self.beginCommand(undoType='insert-newline-and-indent')

    # New in Leo 4.5: use the same logic as in selfInsertCommand.
    oldSel =  name.startswith('body') and w.getSelectionRange() or (None,None)
    self.insertNewlineHelper(w=w,oldSel=oldSel,undoType=None)
    self.updateTab(p,w)
    k.setInputState('insert')
    k.showStateAndMode()

    # i = w.getInsertPoint()
    # w.insert(i,'\n\t')
    # w.setInsertPoint(i+2)

    self.endCommand(changed=True,setLabel=False)
#@-node:ekr.20050920084036.86:insertNewLineAndTab (changed)
#@-node:ekr.20080512144457.1:newline does not stick
#@+node:ekr.20080512151204.1:Don't honor command-mode keys if not in text widget
#@+node:ekr.20060923202156:c.onCanvasKey
def onCanvasKey (self,event):

    '''Navigate to the next headline starting with ch = event.char.
    If ch is uppercase, search all headlines; otherwise search only visible headlines.
    This is modelled on Windows explorer.'''

    # g.trace(event and event.char)

    if not event or not event.char or not event.keysym.isalnum():
        return
    c  = self ; p = c.currentPosition() ; p1 = p.copy()
    invisible = c.config.getBool('invisible_outline_navigation')
    ch = event.char
    allFlag = ch.isupper() and invisible # all is a global (!?)
    if not invisible: ch = ch.lower()
    found = False
    extend = self.navQuickKey()
    attempts = g.choose(extend,(True,False),(False,))
    for extend2 in attempts:
        p = p1.copy()
        while 1:
            if allFlag:
                p.moveToThreadNext()
            else:
                p.moveToVisNext(c)
            if not p:
                p = c.rootPosition()
            if p == p1: # Never try to match the same position.
                # g.trace('failed',extend2)
                found = False ; break
            newPrefix = c.navHelper(p,ch,extend2)
            if newPrefix:
                found = True ; break
        if found: break
    if found:
        if allFlag: c.frame.tree.expandAllAncestors(p)
        c.selectPosition(p)
        c.navTime = time.clock()
        c.navPrefix = newPrefix
        # g.trace('extend',extend,'extend2',extend2,'navPrefix',c.navPrefix,'p',p.headString())
    else:
        c.navTime = None
        c.navPrefix = ''
    c.treeWantsFocusNow()
#@+node:ekr.20061002095711.1:c.navQuickKey
def navQuickKey (self):

    '''return true if there are two quick outline navigation keys
    in quick succession.

    Returns False if @float outline_nav_extend_delay setting is 0.0 or unspecified.'''

    c = self

    deltaTime = c.config.getFloat('outline_nav_extend_delay')

    if deltaTime in (None,0.0):
        return False
    else:
        nearTime = c.navTime and time.clock() - c.navTime < deltaTime
        return nearTime
#@nonl
#@-node:ekr.20061002095711.1:c.navQuickKey
#@+node:ekr.20061002095711:c.navHelper
def navHelper (self,p,ch,extend):

    c = self ; h = p.headString().lower()

    if extend:
        prefix = c.navPrefix + ch
        return h.startswith(prefix.lower()) and prefix

    if h.startswith(ch):
        return ch

    # New feature: search for first non-blank character after @x for common x.
    if ch != '@' and h.startswith('@'):
        for s in ('button','command','file','thin','asis','nosent','noref'):
            prefix = '@'+s
            if h.startswith('@'+s):
                while 1:
                    n = len(prefix)
                    ch2 = n < len(h) and h[n] or ''
                    if ch2.isspace():
                        prefix = prefix + ch2
                    else: break
                if len(prefix) < len(h) and h.startswith(prefix + ch.lower()):
                    return prefix + ch
    return ''
#@nonl
#@-node:ekr.20061002095711:c.navHelper
#@-node:ekr.20060923202156:c.onCanvasKey
#@+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.'''

    # g.trace('event.keysym_num',event.keysym_num,event,dir(event))

    << define vars >>
    if keysym in special_keys: return None

    trace = (False or self.trace_masterKeyHandler) and not g.app.unitTesting
    traceGC = self.trace_masterKeyHandlerGC and not g.app.unitTesting
    if traceGC: g.printNewObjects('masterKey 1')
    if trace:
        g.trace('stroke:',repr(stroke),'keysym:',repr(event.keysym),'ch:',repr(event.char))
            # 'state.kind:',k.state.kind),'\n',g.callers())
        # if (self.master_key_count % 100) == 0: g.printGcSummary()

    # Handle keyboard-quit first.
    if k.abortAllModesKey and stroke == k.abortAllModesKey:
        # g.trace('special case')
        return k.masterCommand(event,k.keyboardQuit,stroke,'keyboard-quit')

    if k.inState():
        # This will return unless k.autoCompleterStateHandler
        # (called from k.callStateFunction) returns 'do-standard-keys'
        << handle mode bindings >>

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

    if stroke and isPlain:
        << handle special cases for plain keys >>

    << handle per-pane bindings >>

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

    return k.handleUnboundKeys(event,char,keysym,stroke)
#@+node:ekr.20061031131434.147:<< define vars >>
k = self ; c = k.c ; gui = g.app.gui

if event: event = gui.leoKeyEvent(event,c)

w = event.widget
char = event.char
keysym = event.keysym
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',
    '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 >>
#@+node:ekr.20061031131434.149:<< handle mode bindings >>
# 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 'break'

# Second, honor general modes.
if state == 'getArg':
    return k.getArg(event,stroke=stroke)
elif state == 'getFileName':
    return 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) # k.state.handler)
    val = k.callStateFunction(event) # Calls end-command.
    if val != 'do-standard-keys': return 'break'

# Third, pass keys to user modes.
else:
    d =  k.masterBindingsDict.get(state)
    if d:
        b = d.get(stroke)
        if b:
            if trace: g.trace('calling generalModeHandler')
            k.generalModeHandler (event,
                commandName=b.commandName,func=b.func,
                modeName=state,nextMode=b.nextMode)
            return 'break'
        else:
            # New in Leo 4.5: unbound keys end mode.
            k.endMode(event)
    else:
        # New in 4.4b4.
        handler = k.getStateHandler()
        if handler:
            handler(event)
        else:
            g.trace('No state handler for %s' % state)
        return 'break'
#@-node:ekr.20061031131434.149:<< handle mode bindings >>
#@+node:ekr.20080510153327.4:<< handle special cases for plain keys >>
# g.trace('plain key','state',k.unboundKeyAction,'stroke',stroke)

# 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))
                k.masterCommand(event,b.func,b.stroke,b.commandName)
                return 'break'

    if trace: g.trace('%s in %s mode' % (stroke,k.unboundKeyAction))
    k.masterCommand(event,func=None,stroke=stroke,commandName=None)
    return 'break'

# Bound   plain keys in command mode are by the per-pane logic.
# Unbound plain keys in command mode are ignored by handleUnboundKeys.

# This code ignores all command-state keys if we are not in a text widget.
elif k.unboundKeyAction == 'command':
    if not g.app.gui.isTextWidget(w):
        c.onCanvasKey(event)
        return 'break'

# elif k.unboundKeyAction == 'command':
    # # In command mode we must disallow all keys except those actually bound in command-state.
    # # That is, we cannot use general per-pane mode bindings.
    # for key,name in (
        # # Order here is similar to bindtags order.
        # ('command',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 (
            # 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,{})
            # # g.trace('key',key,'name',name,'stroke',stroke,'stroke in d.keys',stroke in d.keys())
            # if d:
                # b = d.get(stroke)
                # if b:
                    # if trace: g.trace('%s found %s = %s' % (key,repr(b.stroke),b.commandName))
                    # return k.masterCommand(event,b.func,b.stroke,b.commandName)
    # else:
        # if trace: g.trace('ignoring %s in command mode' % b.stroke)
        # return 'break' # Disallow all unbound plain keys in command mode.
#@nonl
#@-node:ekr.20080510153327.4:<< handle special cases for plain keys >>
#@+node:ekr.20061031131434.150:<< handle per-pane bindings >>
keyStatesTuple = ('command','insert','overwrite')

# g.trace('w_name',w_name,'w',w,'isTextWidget(w)',g.app.gui.isTextWidget(w))
# g.trace('button',k.masterBindingsDict.get('button'))

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,{})
        # g.trace('key',key,'name',name,'stroke',stroke,'stroke in d.keys',stroke in d.keys())
        if d:
            b = d.get(stroke)
            if b:
                if trace: g.trace('%s found %s = %s' % (key,repr(b.stroke),b.commandName))
                if traceGC: g.printNewObjects('masterKey 3')
                return k.masterCommand(event,b.func,b.stroke,b.commandName)
#@-node:ekr.20061031131434.150:<< handle per-pane bindings >>
#@+node:ekr.20061031131434.108:k.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:k.callStateFunction
#@+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','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) ### ,setDefaultUnboundKeyAction=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:handleUnboudKeys
def handleUnboundKeys (self,event,char,keysym,stroke):

    k = self ; c = k.c
    modesTuple = ('insert','overwrite')
    trace = False

    if trace:
        # if stroke: g.trace('***unexpected stroke***')
        g.trace('keysym:',repr(event.keysym),'ch:',repr(event.char))
            # 'state.kind:',k.state.kind),'\n',g.callers())
        # if (self.master_key_count % 100) == 0: g.printGcSummary()

    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 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.find('Escape') != -1:
        # Never insert escape 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:handleUnboudKeys
#@-node:ekr.20061031131434.146:masterKeyHandler & helpers
#@-node:ekr.20080512151204.1:Don't honor command-mode keys if not in text widget
#@+node:ekr.20080512153146.2:New nodes should edit text in input mode
#@+node:ekr.20031218072017.1761:c.insertHeadline
def insertHeadline (self,event=None,op_name="Insert Node",as_child=False):

    '''Insert a node after the presently selected node.'''

    c = self ; u = c.undoer
    current = c.currentPosition()

    if not current: return

    c.beginUpdate()
    try:
        undoData = c.undoer.beforeInsertNode(current)
        # Make sure the new node is visible when hoisting.
        if (as_child or
            (current.hasChildren() and current.isExpanded()) or
            (c.hoistStack and current == c.hoistStack[-1].p)
        ):
            if c.config.getBool('insert_new_nodes_at_end'):
                p = current.insertAsLastChild()
            else:
                p = current.insertAsNthChild(0)
        else:
            p = current.insertAfter()
        dirtyVnodeList = p.setAllAncestorAtFileNodesDirty()
        c.selectPosition(p)
        c.setChanged(True)
        u.afterInsertNode(p,op_name,undoData,dirtyVnodeList=dirtyVnodeList)
    finally:
        c.endUpdate()
    c.beginUpdate()
    try:
        c.editPosition(p,selectAll=True)
    finally:
        c.endUpdate(False)

    return p # for mod_labels plugin.
#@-node:ekr.20031218072017.1761:c.insertHeadline
#@+node:ekr.20031218072017.2991:c.editPosition
# Selects v: sets the focus to p and edits p.

def editPosition(self,p,selectAll=False):

    c = self ; k = c.k

    if p:
        c.selectPosition(p)

        c.frame.tree.editLabel(p,selectAll=selectAll)

        if k:
            if selectAll:
                k.setInputState('insert')
            else:
                k.setDefaultInputState()

            k.showStateAndMode()
#@-node:ekr.20031218072017.2991:c.editPosition
#@+node:ekr.20031218072017.838:tkBody.createBindings
def createBindings (self,w=None):

    '''(tkBody) Create gui-dependent bindings.
    These are *not* made in nullBody instances.'''

    frame = self.frame ; c = self.c ; k = c.k
    if not w: w = self.bodyCtrl

    c.bind(w,'<Key>', k.masterKeyHandler)

    def onFocusOut(event,c=c):
        # This interferes with inserting new nodes.
            # c.k.setDefaultInputState()
        self.setEditorColors(
            bg=c.k.unselected_body_bg_color,
            fg=c.k.unselected_body_fg_color)
        # This is required, for example, when typing Alt-Shift-anyArrow in insert mode.
        # But we suppress coloring in the widget.
        oldState = k.unboundKeyAction
        k.unboundKeyAction = k.defaultUnboundKeyAction
        c.k.showStateAndMode(w=c.frame.tree.canvas)
        k.unboundKeyAction = oldState

    def onFocusIn(event,c=c):
        # g.trace('callback')
        c.k.setDefaultInputState()

    c.bind(w,'<FocusOut>', onFocusOut)
    c.bind(w,'<FocusIn>', onFocusIn)

    table = [
        ('<Button-1>',  frame.OnBodyClick,          k.masterClickHandler),
        ('<Button-3>',  frame.OnBodyRClick,         k.masterClick3Handler),
        ('<Double-1>',  frame.OnBodyDoubleClick,    k.masterDoubleClickHandler),
        ('<Double-3>',  None,                       k.masterDoubleClick3Handler),
        ('<Button-2>',  frame.OnPaste,              k.masterClickHandler),
    ]

    table2 = (
        ('<Button-2>',  frame.OnPaste,              k.masterClickHandler),
    )

    if c.config.getBool('allow_middle_button_paste'):
        table.extend(table2)

    for kind,func,handler in table:
        def bodyClickCallback(event,handler=handler,func=func):
            return handler(event,func)

        c.bind(w,kind,bodyClickCallback)
#@-node:ekr.20031218072017.838:tkBody.createBindings
#@+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)

    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.20080512153146.2:New nodes should edit text in input mode
#@+node:ekr.20080512091707.2:Body changes color when headline mode changes
#@+node:ekr.20060606090542:setEditorColors
def setEditorColors (self,bg,fg):

    c = self.c ; d = self.editorWidgets

    for key in d.keys():
        w2 = d.get(key)
        # g.trace(id(w2),bg,fg)
        try:
            w2.configure(bg=bg,fg=fg)
        except Exception:
            g.es_exception()
#@-node:ekr.20060606090542:setEditorColors
#@+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)

    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.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.133:setInputState
def setInputState (self,state):

    k = self
    k.unboundKeyAction = state



#@-node:ekr.20061031131434.133:setInputState
#@+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.20080512091707.2:Body changes color when headline mode changes
#@+node:ekr.20080512195927.1:Find character crosses lines
#@+node:ekr.20060925151926:backward/findCharacter & helper
def backwardFindCharacter (self,event):
    return self.findCharacterHelper(event,backward=True,extend=False)

def backwardFindCharacterExtendSelection (self,event):
    return self.findCharacterHelper(event,backward=True,extend=True)

def findCharacter (self,event):
    return self.findCharacterHelper(event,backward=False,extend=False)

def findCharacterExtendSelection (self,event):
    return self.findCharacterHelper(event,backward=False,extend=True)
#@nonl
#@+node:ekr.20060417194232.1:findCharacterHelper
def findCharacterHelper (self,event,backward,extend):

    '''Put the cursor at the next occurance of a character on a line.'''

    c = self.c ; k = c.k ; tag = 'find-char' ; state = k.getState(tag)

    if state == 0:
        w = self.editWidget(event) # Sets self.w
        if not w: return
        self.event = event
        self.backward = backward ; self.extend = extend
        self.insert = w.getInsertPoint()
        s = '%s character%s: ' % (
            g.choose(backward,'Backward find','Find'),
            g.choose(extend,' & extend',''))
        k.setLabelBlue(s,protect=True)
        # Get the arg without touching the focus.
        k.getArg(event,tag,1,self.findCharacter,oneCharacter=True,useMinibuffer=False)
    else:
        event = self.event ; w = self.w
        backward = self.backward ; extend = self.extend
        ch = k.arg ; s = w.getAllText()
        ins = w.toPythonIndex(self.insert)
        i = ins + g.choose(backward,-1,+1) # skip the present character.
        if backward:
            # start = s.rfind('\n',0,i)
            # if start == -1: start = 0
            start = 0
            j = s.rfind(ch,start,max(start,i)) # Skip the character at the cursor.
            if j > -1: self.moveToHelper(event,j,extend)
        else:
            # end = s.find('\n',i)
            # if end == -1: end = len(s)
            end = len(s)
            j = s.find(ch,min(i,end),end) # Skip the character at the cursor.
            if j > -1: self.moveToHelper(event,j,extend)
        k.resetLabel()
        k.clearState()
#@nonl
#@-node:ekr.20060417194232.1:findCharacterHelper
#@-node:ekr.20060925151926:backward/findCharacter & helper
#@-node:ekr.20080512195927.1:Find character crosses lines
#@-node:ekr.20080510185834.6:Projects
#@+node:ekr.20080513061733.4:Recent projects
#@+node:ekr.20080512053319.8:Found: outline_pane_has_initial_focus
@
Possibilities:

- Eliminate the code and the setting entirely.
- Encapsulate the code in c.setInitialFocus.
#@nonl
#@+node:ekr.20031218072017.1623:new
def new (self,event=None,gui=None):

    '''Create a new Leo window.'''

    c,frame = g.app.newLeoCommanderAndFrame(fileName=None,relativeFileName=None,gui=gui)

    # Needed for plugins.
    g.doHook("new",old_c=self,c=c,new_c=c)
    # Use the config params to set the size and location of the window.
    c.beginUpdate()
    try:
        frame.setInitialWindowGeometry()
        frame.deiconify()
        frame.lift()
        frame.resizePanesToRatio(frame.ratio,frame.secondary_ratio) # Resize the _new_ frame.
        v = leoNodes.vnode(context=c)
        p = leoNodes.position(v)
        v.initHeadString("NewHeadline")
        # New in Leo 4.5: p.moveToRoot would be wrong: the node hasn't been linked yet.
        p._linkAsRoot(oldRoot=None)
        c.setRootVnode(v) # New in Leo 4.4.2.
        c.editPosition(p)
        # New in Leo 4.4.8: create the menu as late as possible so it can use user commands.
        p = c.currentPosition()
        if not g.doHook("menu1",c=c,p=p,v=p):
            frame.menu.createMenuBar(frame)
            c.updateRecentFiles(fileName=None)
            g.doHook("menu2",c=frame.c,p=p,v=p)
            g.doHook("after-create-leo-frame",c=c)

    finally:
        c.endUpdate()
        # chapterController.finishCreate must be called after the first real redraw
        # because it requires a valid value for c.rootPosition().
        if c.config.getBool('use_chapters') and c.chapterController:
            c.chapterController.finishCreate()
            frame.c.setChanged(False) # Clear the changed flag set when creating the @chapters node.
        if c.config.getBool('outline_pane_has_initial_focus'):
            c.treeWantsFocusNow()
        else:
            c.bodyWantsFocusNow()
    return c # For unit test.
#@-node:ekr.20031218072017.1623:new
#@+node:ekr.20031218072017.2821:open
def open (self,event=None):

    '''Open a Leo window containing the contents of a .leo file.'''

    c = self
    << Set closeFlag if the only open window is empty >>

    fileName = ''.join(c.k.givenArgs) or g.app.gui.runOpenFileDialog(
        title = "Open",
        filetypes = [("Leo files","*.leo"), ("All files","*")],
        defaultextension = ".leo")
    c.bringToFront()

    ok = False
    if fileName and len(fileName) > 0:
        ok, frame = g.openWithFileName(fileName,c)
        if ok:
            g.setGlobalOpenDir(fileName)
        if ok and closeFlag:
            g.app.destroyWindow(c.frame)

    # openWithFileName sets focus if ok.
    if not ok:
        if c.config.getBool('outline_pane_has_initial_focus'):
            c.treeWantsFocusNow()
        else:
            c.bodyWantsFocusNow()
#@+node:ekr.20031218072017.2822:<< Set closeFlag if the only open window is empty >>
@ If this is the only open window was opened when the app started, and the window has never been written to or saved, then we will automatically close that window if this open command completes successfully.
@c

closeFlag = (
    c.frame.startupWindow and # The window was open on startup
    not c.changed and not c.frame.saved and # The window has never been changed
    g.app.numberOfWindows == 1) # Only one untitled window has ever been opened
#@-node:ekr.20031218072017.2822:<< Set closeFlag if the only open window is empty >>
#@-node:ekr.20031218072017.2821:open
#@+node:ekr.20031218072017.2052:g.openWithFileName
def openWithFileName(fileName,old_c,
    enableLog=True,gui=None,readAtFileNodesFlag=True):

    """Create a Leo Frame for the indicated fileName if the file exists."""

    if not fileName or len(fileName) == 0:
        return False, None

    def munge(name):
        return g.os_path_normpath(name or '').lower()

    # Create a full, normalized, Unicode path name, preserving case.
    relativeFileName = g.os_path_normpath(fileName)
    fileName = g.os_path_normpath(g.os_path_abspath(fileName))
    # g.trace(relativeFileName,'-->',fileName)

    # If the file is already open just bring its window to the front.
    theList = app.windowList
    for frame in theList:
        if munge(fileName) == munge(frame.c.mFileName):
            frame.bringToFront()
            frame.c.setLog()
            frame.c.outerUpdate()
            return True, frame
    if old_c:
        # New in 4.4: We must read the file *twice*.
        # The first time sets settings for the later call to c.finishCreate.
        # g.trace('***** prereading',fileName)
        c2 = g.app.config.openSettingsFile(fileName)
        if c2: g.app.config.updateSettings(c2,localFlag=True)
        g.doHook('open0')

    # Open the file in binary mode to allow 0x1a in bodies & headlines.
    theFile,isZipped = g.openLeoOrZipFile(fileName)
    if not theFile: return False, None
    c,frame = app.newLeoCommanderAndFrame(
        fileName=fileName,
        relativeFileName=relativeFileName,
        gui=gui)
    assert frame.c == c and c.frame == frame
    c.isZipped = isZipped
    frame.log.enable(enableLog)
    g.app.writeWaitingLog() # New in 4.3: write queued log first.
    c.beginUpdate()
    try:
        if not g.doHook("open1",old_c=old_c,c=c,new_c=c,fileName=fileName):
            c.setLog()
            app.lockLog()
            frame.c.fileCommands.open(
                theFile,fileName,
                readAtFileNodesFlag=readAtFileNodesFlag) # closes file.
            app.unlockLog()
            for z in g.app.windowList: # Bug fix: 2007/12/07: don't change frame var.
                # The recent files list has been updated by c.updateRecentFiles.
                z.c.config.setRecentFiles(g.app.config.recentFiles)
        # Bug fix in 4.4.
        frame.openDirectory = g.os_path_abspath(g.os_path_dirname(fileName))
        g.doHook("open2",old_c=old_c,c=c,new_c=c,fileName=fileName)
        p = c.currentPosition()
        # New in Leo 4.4.8: create the menu as late as possible so it can use user commands.
        if not g.doHook("menu1",c=c,p=p,v=p):
            frame.menu.createMenuBar(frame)
            c.updateRecentFiles(relativeFileName or fileName)
            g.doHook("menu2",c=frame.c,p=p,v=p)
            g.doHook("after-create-leo-frame",c=c)

    finally:
        c.endUpdate()
        assert frame.c == c and c.frame == frame
        # chapterController.finishCreate must be called after the first real redraw
        # because it requires a valid value for c.rootPosition().
        if c.chapterController:
            c.chapterController.finishCreate()
        k = c.k
        if k:
            k.setDefaultInputState()
        if c.config.getBool('outline_pane_has_initial_focus'):
            c.treeWantsFocusNow()
        else:
            c.bodyWantsFocusNow()
        if k:
            k.showStateAndMode()

    return True, frame
#@nonl
#@-node:ekr.20031218072017.2052:g.openWithFileName
#@-node:ekr.20080512053319.8:Found: outline_pane_has_initial_focus
#@+node:ekr.20080512155953.2:Return in headline should color headline neutral, color body pane blue
#@+node:ekr.20031218072017.2992:c.endEditing (calls tree.endEditLabel)
# Ends the editing in the outline.

def endEditing(self):

    c = self ; k = c.k

    c.frame.tree.endEditLabel()

    c.frame.tree.setSelectedLabelState(p=c.currentPosition())

    # The following code would be wrong; c.endEditing is a utility method.
    # if k:
        # k.setDefaultInputState()
        # # Recolor the *body* text, **not** the headline.
        # k.showStateAndMode(w=c.frame.body.bodyCtrl)
#@-node:ekr.20031218072017.2992:c.endEditing (calls tree.endEditLabel)
#@+node:ekr.20040803072955.126:endEditLabel
def endEditLabel (self):

    '''End editing of a headline and update p.headString().'''

    c = self.c ; k = c.k ; p = c.currentPosition()

    self.setEditPosition(None) # That is, self._editPosition = None

    # Important: this will redraw if necessary.
    self.onHeadChanged(p)

    if 0: # Can't call setDefaultUnboundKeyAction here: it might put us in ignore mode!
        k.setDefaultInputState()
        k.showStateAndMode()

    if 0: # This interferes with the find command and interferes with focus generally!
        c.bodyWantsFocus()
#@-node:ekr.20040803072955.126:endEditLabel
#@+node:ekr.20031218072017.3982:frame.endEditLabelCommand
def endEditLabelCommand (self,event=None):

    '''End editing of a headline and move focus to the body pane.'''

    frame = self ; c = frame.c ; k = c.k
    if g.app.batchMode:
        c.notValidInBatchMode("End Edit Headline")
    else:
        c.endEditing()
        # g.trace('setting focus')
        if c.config.getBool('stayInTreeAfterEditHeadline'):
            c.treeWantsFocusNow()
        else:
            c.bodyWantsFocusNow()

            if k:
                k.setDefaultInputState()
                # Recolor the *body* text, **not** the headline.
                k.showStateAndMode(w=c.frame.body.bodyCtrl)
#@-node:ekr.20031218072017.3982:frame.endEditLabelCommand
#@+node:ekr.20040803072955.136:setSelectedLabelState
trace_n = 0

def setSelectedLabelState (self,p): # selected, disabled

    c = self.c

    # g.trace(p,c.edit_widget(p))


    if p and c.edit_widget(p):

        if 0:
            g.trace(self.trace_n,c.edit_widget(p),p)
            # g.trace(g.callers(6))
            self.trace_n += 1

        self.setDisabledHeadlineColors(p)
#@-node:ekr.20040803072955.136:setSelectedLabelState
#@+node:ekr.20040803072955.139:setDisabledHeadlineColors
def setDisabledHeadlineColors (self,p):

    c = self.c ; w = c.edit_widget(p)

    if False or (self.trace and self.verbose):
        # if not self.redrawing:
            g.trace("%10s %d %s" % ("disabled",id(w),p.headString()))
            # import traceback ; traceback.print_stack(limit=6)

    fg = self.headline_text_selected_foreground_color or 'black'
    bg = self.headline_text_selected_background_color or 'grey80'
    selfg = self.headline_text_editing_selection_foreground_color
    selbg = self.headline_text_editing_selection_background_color

    try:
        w.configure(state="disabled",highlightthickness=0,fg=fg,bg=bg,
            selectbackground=bg,selectforeground=fg,highlightbackground=bg)
    except:
        g.es_exception()
#@-node:ekr.20040803072955.139:setDisabledHeadlineColors
#@+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)

    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.20080512155953.2:Return in headline should color headline neutral, color body pane blue
#@+node:ekr.20080513061733.1:Confusing status after save when focus is in headline
# Clicking in body doesn't change color and minibuffer prompt properly.
#@nonl
#@+node:ekr.20031218072017.2834:save (commands)
def save (self,event=None):

    '''Save a Leo outline to a file.'''

    c = self ; w = g.app.gui.get_focus(c)

    if g.app.disableSave:
        g.es("save commands disabled",color="purple")
        return

    # Make sure we never pass None to the ctor.
    if not c.mFileName:
        c.frame.title = ""
        c.mFileName = ""

    c.beginUpdate()
    try:
        if c.mFileName:
            # Calls c.setChanged(False) if no error.
            c.fileCommands.save(c.mFileName)
        else:
            fileName = ''.join(c.k.givenArgs) or g.app.gui.runSaveFileDialog(
                initialfile = c.mFileName,
                title="Save",
                filetypes=[("Leo files", "*.leo")],
                defaultextension=".leo")
            c.bringToFront()

            if fileName:
                # Don't change mFileName until the dialog has suceeded.
                c.mFileName = g.ensure_extension(fileName, ".leo")
                c.frame.title = c.mFileName
                c.frame.setTitle(g.computeWindowTitle(c.mFileName))
                c.frame.openDirectory = g.os_path_dirname(c.mFileName) # Bug fix in 4.4b2.
                c.fileCommands.save(c.mFileName)
                c.updateRecentFiles(c.mFileName)
    finally:
        c.endUpdate()
        c.widgetWantsFocusNow(w)
#@nonl
#@-node:ekr.20031218072017.2834:save (commands)
#@+node:ekr.20060205103842:c.get/request/set_focus
def get_focus (self):

    c = self
    return g.app.gui and g.app.gui.get_focus(c)

def get_requested_focus (self):

    c = self
    return c.requestedFocusWidget or c.hasFocusWidget or g.app.gui.get_focus(c)

def request_focus(self,w):

    c = self
    if w: c.requestedFocusWidget = w
    c.traceFocus(w)

def set_focus (self,w,force=False):

    c = self

    if force: # New in Leo 4.4.2: safer.
        c.hasFocusWidget = c.requestedFocusWidget = w
        g.app.gui and g.app.gui.set_focus(c,w)
        c.traceFocus(w)
    else: # An optimization.
        c.requestedFocusWidget = w
        c.masterFocusHandler()

    # Can't do this: it interferes with too much.
    # if c.k: c.k.showStateAndMode(w)
#@-node:ekr.20060205103842:c.get/request/set_focus
#@+node:ekr.20031218072017.3977:OnBodyClick, OnBodyRClick (Events)
def OnBodyClick (self,event=None):

    try:
        c = self.c ; p = c.currentPosition()
        if not g.doHook("bodyclick1",c=c,p=p,v=p,event=event):
            self.OnActivateBody(event=event)
            c.k.showStateAndMode()
        g.doHook("bodyclick2",c=c,p=p,v=p,event=event)
    except:
        g.es_event_exception("bodyclick")

def OnBodyRClick(self,event=None):

    try:
        c = self.c ; p = c.currentPosition()
        if not g.doHook("bodyrclick1",c=c,p=p,v=p,event=event):
            pass # By default Leo does nothing.
            c.k.showStateAndMode()
        g.doHook("bodyrclick2",c=c,p=p,v=p,event=event)
    except:
        g.es_event_exception("iconrclick")
#@-node:ekr.20031218072017.3977:OnBodyClick, OnBodyRClick (Events)
#@+node:ekr.20031218072017.3975:OnActivateBody (tkFrame)
def OnActivateBody (self,event=None):

    # __pychecker__ = '--no-argsused' # event not used.

    try:
        frame = self ; c = frame.c
        c.setLog()
        w = c.get_focus()
        if w != c.frame.body.bodyCtrl:
            frame.tree.OnDeactivate()
        c.bodyWantsFocusNow()
    except:
        g.es_event_exception("activate body")

    return 'break'
#@-node:ekr.20031218072017.3975:OnActivateBody (tkFrame)
#@+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)

    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.20080513061733.1:Confusing status after save when focus is in headline
#@+node:ekr.20080513061733.2:c.endEditing should *not* call showStateAndFocus
#@+node:ekr.20031218072017.2992:c.endEditing (calls tree.endEditLabel)
# Ends the editing in the outline.

def endEditing(self):

    c = self ; k = c.k

    c.frame.tree.endEditLabel()

    c.frame.tree.setSelectedLabelState(p=c.currentPosition())

    # The following code would be wrong; c.endEditing is a utility method.
    # if k:
        # k.setDefaultInputState()
        # # Recolor the *body* text, **not** the headline.
        # k.showStateAndMode(w=c.frame.body.bodyCtrl)
#@-node:ekr.20031218072017.2992:c.endEditing (calls tree.endEditLabel)
#@+node:ekr.20031218072017.3982:frame.endEditLabelCommand
def endEditLabelCommand (self,event=None):

    '''End editing of a headline and move focus to the body pane.'''

    frame = self ; c = frame.c ; k = c.k
    if g.app.batchMode:
        c.notValidInBatchMode("End Edit Headline")
    else:
        c.endEditing()
        # g.trace('setting focus')
        if c.config.getBool('stayInTreeAfterEditHeadline'):
            c.treeWantsFocusNow()
        else:
            c.bodyWantsFocusNow()

            if k:
                k.setDefaultInputState()
                # Recolor the *body* text, **not** the headline.
                k.showStateAndMode(w=c.frame.body.bodyCtrl)
#@-node:ekr.20031218072017.3982:frame.endEditLabelCommand
#@+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)

    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.20080513061733.2:c.endEditing should *not* call showStateAndFocus
#@+node:ekr.20080513061733.5:Set status report correctly on initial load
#@+node:ekr.20031218072017.1934:run
def run(fileName=None,pymacs=None,jyLeo=False,*args,**keywords):

    """Initialize and run Leo"""

    # __pychecker__ = '--no-argsused' # keywords not used.

    # print 'leo.py:run','fileName',fileName
    if not jyLeo and not isValidPython(): return
    << import leoGlobals and leoApp >>
    g.computeStandardDirectories()
    adjustSysPath(g)
    if pymacs:
        script = windowFlag = False
    else:
        script, windowFlag = getBatchScript() # Do early so we can compute verbose next.
    verbose = script is None
    g.app.batchMode = script is not None
    g.app.silentMode = '-silent' in sys.argv or '--silent' in sys.argv
    g.app.setLeoID(verbose=verbose) # Force the user to set g.app.leoID.
    << import leoNodes and leoConfig >>
    g.app.nodeIndices = leoNodes.nodeIndices(g.app.leoID)
    g.app.config = leoConfig.configClass()
    fileName,relativeFileName = completeFileName(fileName)
    reportDirectories(verbose)
    # Read settings *after* setting g.app.config.
    # Read settings *before* opening plugins.  This means if-gui has effect only in per-file settings.
    g.app.config.readSettingsFiles(fileName,verbose)
    g.app.setEncoding()
    if pymacs:
        createNullGuiWithScript(None)
    elif jyLeo:
        import leoSwingGui
        g.app.gui = leoSwingGui.swingGui()
    elif script:
        if windowFlag:
            g.app.createTkGui() # Creates global windows.
            g.app.gui.setScript(script)
            sys.args = []
        else:
            createNullGuiWithScript(script)
        fileName = None
    # Load plugins. Plugins may create g.app.gui.
    g.doHook("start1")
    if g.app.killed: return # Support for g.app.forceShutdown.
    # Create the default gui if needed.
    if g.app.gui == None: g.app.createTkGui() # Creates global windows.
    # Initialize tracing and statistics.
    g.init_sherlock(args)
    if g.app and g.app.use_psyco: startPsyco()
    # Clear g.app.initing _before_ creating the frame.
    g.app.initing = False # "idle" hooks may now call g.app.forceShutdown.
    # Create the main frame.  Show it and all queued messages.
    c,frame = createFrame(fileName,relativeFileName)
    if not frame: return
    g.app.trace_gc          = c.config.getBool('trace_gc')
    g.app.trace_gc_calls    = c.config.getBool('trace_gc_calls')
    g.app.trace_gc_verbose  = c.config.getBool('trace_gc_verbose')
    if g.app.disableSave:
        g.es("disabling save commands",color="red")
    g.app.writeWaitingLog()
    p = c.currentPosition()
    g.doHook("start2",c=c,p=p,v=p,fileName=fileName)
    if c.config.getBool('allow_idle_time_hook'):
        g.enableIdleTimeHook()
    if not fileName:
        c.redraw_now()
    # Respect c's focus wishes if posssible.
    w = g.app.gui.get_focus(c)
    if w != c.frame.body.bodyCtrl and w != c.frame.tree.canvas:
        c.bodyWantsFocus()
        c.k.showStateAndMode(w)
    c.outerUpdate()
    g.app.gui.runMainLoop()
#@+node:ekr.20041219072112:<< import leoGlobals and leoApp >>
if jyLeo:

    print '*** starting jyLeo',sys.platform # will be something like java1.6.0_02

    ### This is a hack.
    ### The jyleo script in test.leo sets the cwd to g.app.loadDir
    ### Eventually, we will have to compute the equivalent here.

    path = os.path.join(os.getcwd()) ### ,'..','src')
    if path not in sys.path:
        print 'appending %s to sys.path' % path
        sys.path.append(path)
    if 0:
        print 'sys.path...'
        for s in sys.path: print s

# Import leoGlobals, but do NOT set g.
import leoGlobals
import leoApp

# Create the app.
leoGlobals.app = leoApp.LeoApp()

# **now** we can set g.
g = leoGlobals
assert(g.app)

if jyLeo:
    startJyleo(g)
#@-node:ekr.20041219072112:<< import leoGlobals and leoApp >>
#@+node:ekr.20041219072416.1:<< import leoNodes and leoConfig >>
import leoNodes
import leoConfig

# try:
    # import leoNodes
# except ImportError:
    # print "Error importing leoNodes.py"
    # import traceback ; traceback.print_exc()

# try:
    # import leoConfig
# except ImportError:
    # print "Error importing leoConfig.py"
    # import traceback ; traceback.print_exc()
#@-node:ekr.20041219072416.1:<< import leoNodes and leoConfig >>
#@-node:ekr.20031218072017.1934:run
#@+node:ekr.20031218072017.2052:g.openWithFileName
def openWithFileName(fileName,old_c,
    enableLog=True,gui=None,readAtFileNodesFlag=True):

    """Create a Leo Frame for the indicated fileName if the file exists."""

    if not fileName or len(fileName) == 0:
        return False, None

    def munge(name):
        return g.os_path_normpath(name or '').lower()

    # Create a full, normalized, Unicode path name, preserving case.
    relativeFileName = g.os_path_normpath(fileName)
    fileName = g.os_path_normpath(g.os_path_abspath(fileName))
    # g.trace(relativeFileName,'-->',fileName)

    # If the file is already open just bring its window to the front.
    theList = app.windowList
    for frame in theList:
        if munge(fileName) == munge(frame.c.mFileName):
            frame.bringToFront()
            frame.c.setLog()
            frame.c.outerUpdate()
            return True, frame
    if old_c:
        # New in 4.4: We must read the file *twice*.
        # The first time sets settings for the later call to c.finishCreate.
        # g.trace('***** prereading',fileName)
        c2 = g.app.config.openSettingsFile(fileName)
        if c2: g.app.config.updateSettings(c2,localFlag=True)
        g.doHook('open0')

    # Open the file in binary mode to allow 0x1a in bodies & headlines.
    theFile,isZipped = g.openLeoOrZipFile(fileName)
    if not theFile: return False, None
    c,frame = app.newLeoCommanderAndFrame(
        fileName=fileName,
        relativeFileName=relativeFileName,
        gui=gui)
    assert frame.c == c and c.frame == frame
    c.isZipped = isZipped
    frame.log.enable(enableLog)
    g.app.writeWaitingLog() # New in 4.3: write queued log first.
    c.beginUpdate()
    try:
        if not g.doHook("open1",old_c=old_c,c=c,new_c=c,fileName=fileName):
            c.setLog()
            app.lockLog()
            frame.c.fileCommands.open(
                theFile,fileName,
                readAtFileNodesFlag=readAtFileNodesFlag) # closes file.
            app.unlockLog()
            for z in g.app.windowList: # Bug fix: 2007/12/07: don't change frame var.
                # The recent files list has been updated by c.updateRecentFiles.
                z.c.config.setRecentFiles(g.app.config.recentFiles)
        # Bug fix in 4.4.
        frame.openDirectory = g.os_path_abspath(g.os_path_dirname(fileName))
        g.doHook("open2",old_c=old_c,c=c,new_c=c,fileName=fileName)
        p = c.currentPosition()
        # New in Leo 4.4.8: create the menu as late as possible so it can use user commands.
        if not g.doHook("menu1",c=c,p=p,v=p):
            frame.menu.createMenuBar(frame)
            c.updateRecentFiles(relativeFileName or fileName)
            g.doHook("menu2",c=frame.c,p=p,v=p)
            g.doHook("after-create-leo-frame",c=c)

    finally:
        c.endUpdate()
        assert frame.c == c and c.frame == frame
        # chapterController.finishCreate must be called after the first real redraw
        # because it requires a valid value for c.rootPosition().
        if c.chapterController:
            c.chapterController.finishCreate()
        k = c.k
        if k:
            k.setDefaultInputState()
        if c.config.getBool('outline_pane_has_initial_focus'):
            c.treeWantsFocusNow()
        else:
            c.bodyWantsFocusNow()
        if k:
            k.showStateAndMode()

    return True, frame
#@nonl
#@-node:ekr.20031218072017.2052:g.openWithFileName
#@+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)

    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.20031218072017.838:tkBody.createBindings
def createBindings (self,w=None):

    '''(tkBody) Create gui-dependent bindings.
    These are *not* made in nullBody instances.'''

    frame = self.frame ; c = self.c ; k = c.k
    if not w: w = self.bodyCtrl

    c.bind(w,'<Key>', k.masterKeyHandler)

    def onFocusOut(event,c=c):
        # This interferes with inserting new nodes.
            # c.k.setDefaultInputState()
        self.setEditorColors(
            bg=c.k.unselected_body_bg_color,
            fg=c.k.unselected_body_fg_color)
        # This is required, for example, when typing Alt-Shift-anyArrow in insert mode.
        # But we suppress coloring in the widget.
        oldState = k.unboundKeyAction
        k.unboundKeyAction = k.defaultUnboundKeyAction
        c.k.showStateAndMode(w=c.frame.tree.canvas)
        k.unboundKeyAction = oldState

    def onFocusIn(event,c=c):
        # g.trace('callback')
        c.k.setDefaultInputState()

    c.bind(w,'<FocusOut>', onFocusOut)
    c.bind(w,'<FocusIn>', onFocusIn)

    table = [
        ('<Button-1>',  frame.OnBodyClick,          k.masterClickHandler),
        ('<Button-3>',  frame.OnBodyRClick,         k.masterClick3Handler),
        ('<Double-1>',  frame.OnBodyDoubleClick,    k.masterDoubleClickHandler),
        ('<Double-3>',  None,                       k.masterDoubleClick3Handler),
        ('<Button-2>',  frame.OnPaste,              k.masterClickHandler),
    ]

    table2 = (
        ('<Button-2>',  frame.OnPaste,              k.masterClickHandler),
    )

    if c.config.getBool('allow_middle_button_paste'):
        table.extend(table2)

    for kind,func,handler in table:
        def bodyClickCallback(event,handler=handler,func=func):
            return handler(event,func)

        c.bind(w,kind,bodyClickCallback)
#@-node:ekr.20031218072017.838:tkBody.createBindings
#@-node:ekr.20080513061733.5:Set status report correctly on initial load
#@+node:ekr.20080512155222.1:Autocompletion should stay in insert/overwrite mode
#@+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.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.object = None
#@-node:ekr.20061031131434.34:finish
#@-node:ekr.20080512155222.1:Autocompletion should stay in insert/overwrite mode
#@+node:ekr.20080513091809.1:Make sure keyboard-quit enters default mode, except during auto-completion
#@+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.20080513091809.1:Make sure keyboard-quit enters default mode, except during auto-completion
#@+node:ekr.20080513130956.1:Don't recolor the minibuffer widget
#@+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.20080513130956.1:Don't recolor the minibuffer widget
#@+node:ekr.20080513174131.2:Fixed unbounded loop in autocompleter.findAnchor
#@+node:ekr.20061031131434.32: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.20061031131434.32:findAnchor
#@-node:ekr.20080513174131.2:Fixed unbounded loop in autocompleter.findAnchor
#@+node:ekr.20080514062808.1:Color headline when editing
#@+node:ekr.20031218072017.2886:c.editHeadline
def editHeadline (self,event=None):

    '''Begin editing the headline of the selected node.'''

    c = self ; k = c.k ; tree = c.frame.tree

    if g.app.batchMode:
        c.notValidInBatchMode("Edit Headline")
        return

    if k:
        k.setDefaultInputState()
        k.showStateAndMode()

    tree.editLabel(c.currentPosition())
#@-node:ekr.20031218072017.2886:c.editHeadline
#@+node:ekr.20040803072955.127:tree.editLabel
def editLabel (self,p,selectAll=False):

    """Start editing p's headline."""

    c = self.c
    trace = (False or self.trace_edit) and not g.app.unitTesting

    if p and p != self.editPosition():

        if trace:
            g.trace(p.headString(),g.choose(c.edit_widget(p),'','no edit widget'))

        c.beginUpdate()
        try:
            self.endEditLabel()
        finally:
            c.endUpdate(True)
            c.outerUpdate()

    self.setEditPosition(p) # That is, self._editPosition = p

    if trace: g.trace(c.edit_widget(p))

    w = c.edit_widget(p)
    if p and w:
        self.revertHeadline = p.headString() # New in 4.4b2: helps undo.
        self.setEditLabelState(p,selectAll=selectAll) # Sets the focus immediately.
        c.headlineWantsFocus(p) # Make sure the focus sticks.
        c.k.showStateAndMode(w)
#@-node:ekr.20040803072955.127:tree.editLabel
#@-node:ekr.20080514062808.1:Color headline when editing
#@+node:ekr.20080514084054.2:Munged mode names to create mode prompt
@nocolor

In @mode aString, everything before the first :: is the mode name, and everything after is the prompt that appears in the minibuffer.  If there is no ::, the prompt is the same as the mode name.
#@nonl
#@+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.'''

    # __pychecker__ = '--no-argsused' # w not used (except for debugging).

    k = self ; c = k.c

    # g.trace(w,g.callers())
    # g.trace(g.dictToString(d))

    for commandName in d.keys():
        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

    c.frame.log.deleteTab('Mode')

    k.endCommand(event,k.stroke)
    k.inputModeName = None
    k.clearState()
    k.resetLabel()
    k.showStateAndMode() # Restores focus.
#@-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)
        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 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.

    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()',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)
            # 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)
        #### c.minibufferWantsFocus()
#@-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)
    keys = d.keys() ; keys.sort()

    data = [] ; n = 20
    for key in keys:
        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.20080514084054.3:changed
#@+node:ekr.20080514084054.4:computeModeName (parserBaseClass)
def computeModeName (self,name):

    s = name.strip().lower()
    j = s.find(' ')
    if j > -1: s = s[:j]
    if s.endswith('mode'):
        s = s[:-4].strip()
    if s.endswith('-'):
        s = s[:-1]

    i = s.find('::')
    if i > -1:
        # The actual mode name is everything up to the "::"
        # The prompt is everything after the prompt.
        s = s[:i]

    modeName = s + '-mode'
    return modeName
#@-node:ekr.20080514084054.4:computeModeName (parserBaseClass)
#@+node:ekr.20060102103625.1:doMode (ParserBaseClass)
def doMode(self,p,kind,name,val):

    '''Parse an @mode node and create the enter-<name>-mode command.'''

    # __pychecker__ = '--no-argsused' # val not used.

    c = self.c ; k = c.k ; name1 = name

    # g.trace('%20s' % (name),c.fileName())
    modeName = self.computeModeName(name)

    # Create a local shortcutsDict.
    old_d = self.shortcutsDict
    d = self.shortcutsDict = {}

    s = p.bodyString()
    lines = g.splitLines(s)
    for line in lines:
        line = line.strip()
        if line and not g.match(line,0,'#'):
            name,bunch = self.parseShortcutLine(line)
            if not name:
                # An entry command: put it in the special *entry-commands* key.
                aList = d.get('*entry-commands*',[])
                aList.append(bunch.entryCommandName)
                d ['*entry-commands*'] = aList
            elif bunch is not None:
                # A regular shortcut.
                bunch.val = k.strokeFromSetting(bunch.val)
                bunch.pane = modeName
                bunchList = d.get(name,[])
                # Important: use previous bindings if possible.
                key2,bunchList2 = c.config.getShortcut(name)
                bunchList3 = [b for b in bunchList2 if b.pane != modeName]
                if bunchList3:
                    # g.trace('inheriting',[b.val for b in bunchList3])
                    bunchList.extend(bunchList3)
                bunchList.append(bunch)
                d [name] = bunchList
                self.set(p,"shortcut",name,bunchList)
                self.setShortcut(name,bunchList)

    # Restore the global shortcutsDict.
    self.shortcutsDict = old_d

    # Create the command, but not any bindings to it.
    self.createModeCommand(modeName,name1,d)
#@-node:ekr.20060102103625.1:doMode (ParserBaseClass)
#@+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()',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)
            # 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.166:modeHelpHelper
def modeHelpHelper (self,d):

    k = self ; c = k.c ; tabName = 'Mode'
    c.frame.log.clearTab(tabName)
    keys = d.keys() ; keys.sort()

    data = [] ; n = 20
    for key in keys:
        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.20060102103625:createModeCommand (parserBaseClass)
def createModeCommand (self,modeName,name,modeDict):

    modeName = 'enter-' + modeName.replace(' ','-')

    i = name.find('::')
    if i > -1:
        # The prompt is everything after the '::'
        prompt = name[i+2:].strip()
        modeDict ['*command-prompt*'] = prompt
        # g.trace('modeName',modeName,'*command-prompt*',prompt)

    # Save the info for k.finishCreate and k.makeAllBindings.
    d = g.app.config.modeCommandsDict

    # New in 4.4.1 b2: silently allow redefinitions of modes.
    d [modeName] = modeDict
#@-node:ekr.20060102103625:createModeCommand (parserBaseClass)
#@+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)
        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 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.

    return 'break'
#@-node:ekr.20061031131434.162:generalModeHandler
#@+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)

    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.20080514084054.3:changed
#@-node:ekr.20080514084054.2:Munged mode names to create mode prompt
#@+node:ekr.20080514114704.1:Fixed an unusual headline click problem
@

This happens when focus is in the minibuffer and the user clicks a headline.

The fix was to add a "don't color" arg to showStateAndMode.

I also changed treeWantsFocus to treeWantsFocusNow in OnActivateHeadline.
#@nonl
#@+node:ekr.20051022141020:onTreeClick
def onTreeClick (self,event=None):

    '''Handle an event in the tree canvas, outside of any tree widget.'''

    c = self.c

    # New in Leo 4.4.2: a kludge: disable later event handling after a double-click.
    # This allows focus to stick in newly-opened files opened by double-clicking an @url node.
    if c.doubleClickFlag:
        c.doubleClickFlag = False
    else:
        c.treeWantsFocusNow()

    g.app.gui.killPopupMenu()
    c.outerUpdate()

    return 'break'
#@-node:ekr.20051022141020:onTreeClick
#@+node:ekr.20040803072955.87:onHeadlineClick
def onHeadlineClick (self,event,p=None):

    # g.trace('p',p)
    c = self.c ; w = event.widget

    if not p:
        try:
            p = w.leo_position
        except AttributeError:
            g.trace('*'*20,'oops')
    if not p: return 'break'

    # g.trace(g.app.gui.widget_name(w),p and p.headString())

    c.setLog()

    try:
        if not g.doHook("headclick1",c=c,p=p,v=p,event=event):
            returnVal = self.OnActivateHeadline(p)
        g.doHook("headclick2",c=c,p=p,v=p,event=event)
    except:
        returnVal = 'break'
        g.es_event_exception("headclick")

    # 'continue' is sometimes correct here.
    # 'break' would make it impossible to unselect the headline text.
    # g.trace('returnVal',returnVal,'stayInTree',self.stayInTree)
    return returnVal
#@-node:ekr.20040803072955.87:onHeadlineClick
#@+node:ekr.20040803072955.105:OnActivateHeadline (tkTree)
def OnActivateHeadline (self,p,event=None):

    '''Handle common process when any part of a headline is clicked.'''

    # g.trace(p.headString())

    returnVal = 'break' # Default: do nothing more.
    trace = False

    try:
        c = self.c
        c.setLog()
        << activate this window >>
    except:
        g.es_event_exception("activate tree")

    return returnVal
#@+node:ekr.20040803072955.106:<< activate this window >>
if p == c.currentPosition():

    if trace: g.trace('current','active',self.active)
    self.editLabel(p) # sets focus.
    # If we are active, pass the event along so the click gets handled.
    # Otherwise, do *not* pass the event along so the focus stays the same.
    returnVal = g.choose(self.active,'continue','break')
    self.active = True
else:
    if trace: g.trace("not current")
    self.select(p,scroll=False)
    w  = c.frame.body.bodyCtrl
    if c.frame.findPanel:
        c.frame.findPanel.handleUserClick(p)
    if p.v.t.insertSpot != None:
        spot = p.v.t.insertSpot
        w.setInsertPoint(spot)
        w.see(spot)
    else:
        w.setInsertPoint(0)
    # An important detail.
    # The *canvas* (not the headline) gets the focus so that
    # tree bindings take priority over text bindings.
    c.treeWantsFocusNow() # Now. New in Leo 4.5.
    c.outerUpdate()
    self.active = False
    returnVal = 'break'
#@nonl
#@-node:ekr.20040803072955.106:<< activate this window >>
#@-node:ekr.20040803072955.105:OnActivateHeadline (tkTree)
#@+node:ekr.20040803072955.128:leoTree.select & helper
tree_select_lockout = False

def select (self,p,updateBeadList=True,scroll=True):

    '''Select a node.  Never redraws outline, but may change coloring of individual headlines.'''

    if g.app.killed or self.tree_select_lockout: return None

    try:
        val = 'break'
        self.tree_select_lockout = True
        val = self.treeSelectHelper(p,updateBeadList,scroll)
    finally:
        self.tree_select_lockout = False

    return val  # Don't put a return in a finally clause.
#@+node:ekr.20070423101911:treeSelectHelper
#  Do **not** try to "optimize" this by returning if p==tree.currentPosition.

def treeSelectHelper (self,p,updateBeadList,scroll):

    c = self.c ; frame = c.frame
    body = w = frame.body.bodyCtrl
    old_p = c.currentPosition()

    if not p:
        # Do *not* test c.positionExists(p) here.
        # We may be in the process of changing roots.
        return None # Not an error.

    # g.trace('      ===',id(w),p and p.headString())
    if self.trace_select and not g.app.unitTesting: g.trace('tree',g.callers())

    if not g.doHook("unselect1",c=c,new_p=p,old_p=old_p,new_v=p,old_v=old_p):
        if old_p:
            << unselect the old node >>

    g.doHook("unselect2",c=c,new_p=p,old_p=old_p,new_v=p,old_v=old_p)

    if not g.doHook("select1",c=c,new_p=p,old_p=old_p,new_v=p,old_v=old_p):
        << select the new node >>
        if p and p != old_p: # Suppress duplicate call.
            try: # may fail during initialization.
                # p is NOT c.currentPosition() here!
                if 0: # Interferes with new colorizer.
                    self.canvas.update_idletasks()
                    self.scrollTo(p)
                if scroll and g.app.gui.guiName() == 'tkinter':
                    def scrollCallback(self=self,p=p):
                        self.scrollTo(p)
                    self.canvas.after(100,scrollCallback)
            except Exception: pass
        c.nodeHistory.update(p,updateBeadList) # Remember this position.
    c.setCurrentPosition(p)
    << set the current node >>
    c.frame.body.assignPositionToEditor(p) # New in Leo 4.4.1.
    c.frame.updateStatusLine() # New in Leo 4.4.1.

    g.doHook("select2",c=c,new_p=p,old_p=old_p,new_v=p,old_v=old_p)
    g.doHook("select3",c=c,new_p=p,old_p=old_p,new_v=p,old_v=old_p)

    return 'break' # Supresses unwanted selection.
#@+node:ekr.20040803072955.129:<< unselect the old node >>
# Remember the position of the scrollbar before making any changes.
if not body: g.trace('no body!','c.frame',c.frame,'old_p',old_p)

yview = body.getYScrollPosition()
insertSpot = c.frame.body.getInsertPoint()

if old_p != p:
    self.endEditLabel() # sets editPosition = None
    self.setUnselectedLabelState(old_p) # 12/17/04

if c.edit_widget(old_p):
    old_p.v.t.scrollBarSpot = yview
    old_p.v.t.insertSpot = insertSpot
#@-node:ekr.20040803072955.129:<< unselect the old node >>
#@+node:ekr.20040803072955.130:<< select the new node >>
# Bug fix: we must always set this, even if we never edit the node.
self.revertHeadline = p.headString()
frame.setWrap(p)

# Always do this.  Otherwise there can be problems with trailing newlines.

s = g.toUnicode(p.v.t._bodyString,"utf-8")
old_s = w.getAllText()

if p and p == old_p and c.frame.body.colorizer.isSameColorState() and s == old_s:
    pass
else:
    # This destroys all color tags, so do a full recolor.
    w.setAllText(s)
    self.frame.body.recolor_now(p) # recolor now uses p.copy(), so this is safe.

if p.v and p.v.t.scrollBarSpot != None:
    first,last = p.v.t.scrollBarSpot
    w.setYScrollPosition(first)

if p.v and p.v.t.insertSpot != None:
    spot = p.v.t.insertSpot
    w.setInsertPoint(spot)
    w.see(spot)
else:
    w.setInsertPoint(0)

# g.trace("select:",p.headString())
#@-node:ekr.20040803072955.130:<< select the new node >>
#@+node:ekr.20040803072955.133:<< set the current node >>
# g.trace('selecting',p,'edit_widget',c.edit_widget(p))
self.setSelectedLabelState(p)

frame.scanForTabWidth(p) #GS I believe this should also get into the select1 hook

if self.use_chapters:
    cc = c.chapterController
    theChapter = cc.getSelectedChapter()
    if theChapter:
        theChapter.p = p.copy()
        # g.trace('tkTree',theChapter.name,'v',id(p.v),p.headString())

if self.stayInTree:
    c.treeWantsFocus()
else:
    c.bodyWantsFocus()
#@-node:ekr.20040803072955.133:<< set the current node >>
#@-node:ekr.20070423101911:treeSelectHelper
#@-node:ekr.20040803072955.128:leoTree.select & helper
#@+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.20031218072017.838:tkBody.createBindings
def createBindings (self,w=None):

    '''(tkBody) Create gui-dependent bindings.
    These are *not* made in nullBody instances.'''

    frame = self.frame ; c = self.c ; k = c.k
    if not w: w = self.bodyCtrl

    c.bind(w,'<Key>', k.masterKeyHandler)

    def onFocusOut(event,c=c):
        # This interferes with inserting new nodes.
            # c.k.setDefaultInputState()
        self.setEditorColors(
            bg=c.k.unselected_body_bg_color,
            fg=c.k.unselected_body_fg_color)
        # This is required, for example, when typing Alt-Shift-anyArrow in insert mode.
        # But we suppress coloring in the widget.
        oldState = k.unboundKeyAction
        k.unboundKeyAction = k.defaultUnboundKeyAction
        c.k.showStateAndMode(w=c.frame.tree.canvas)
        k.unboundKeyAction = oldState

    def onFocusIn(event,c=c):
        # g.trace('callback')
        c.k.setDefaultInputState()

    c.bind(w,'<FocusOut>', onFocusOut)
    c.bind(w,'<FocusIn>', onFocusIn)

    table = [
        ('<Button-1>',  frame.OnBodyClick,          k.masterClickHandler),
        ('<Button-3>',  frame.OnBodyRClick,         k.masterClick3Handler),
        ('<Double-1>',  frame.OnBodyDoubleClick,    k.masterDoubleClickHandler),
        ('<Double-3>',  None,                       k.masterDoubleClick3Handler),
        ('<Button-2>',  frame.OnPaste,              k.masterClickHandler),
    ]

    table2 = (
        ('<Button-2>',  frame.OnPaste,              k.masterClickHandler),
    )

    if c.config.getBool('allow_middle_button_paste'):
        table.extend(table2)

    for kind,func,handler in table:
        def bodyClickCallback(event,handler=handler,func=func):
            return handler(event,func)

        c.bind(w,kind,bodyClickCallback)
#@-node:ekr.20031218072017.838:tkBody.createBindings
#@+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)

    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.20080514114704.1:Fixed an unusual headline click problem
#@+node:ekr.20080516102541.1:Fixed bug: clone-find-all didn't redraw the screen properly
# Oops: redraw_now and redraw must make a redraw request!
# This could cause all kinds of problems.
#@nonl
#@+node:ekr.20080514131122.14:c.redraw and c.redraw_now
def redraw (self):
    c = self
    c.requestRedrawFlag = True

def redraw_now (self):
    c = self
    c.requestRedrawFlag = True

# Compatibility with old scripts
force_redraw = redraw_now
#@-node:ekr.20080514131122.14:c.redraw and c.redraw_now
#@-node:ekr.20080516102541.1:Fixed bug: clone-find-all didn't redraw the screen properly
#@+node:ekr.20080516064950.1:Fixed Shift-Ctrl-r problem after Ctrl-f
# The fix was a special case in handleMindBindings.
#@nonl
#@+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','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) ### ,setDefaultUnboundKeyAction=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.20051023094009:Search classes
#@+node:ekr.20060123125256:class minibufferFind( (the findHandler)
class minibufferFind (baseEditCommandsClass):

    '''An adapter class that implements minibuffer find commands using the (hidden) Find Tab.'''

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

    baseEditCommandsClass.__init__(self,c) # init the base class.

    # g.trace('minibufferFind: finder',finder)

    self.c = c
    self.k = k = c.k
    self.w = None
    self.finder = finder
    self.findTextList = []
    self.changeTextList = []

    commandName = 'replace-string'
    s = k.getShortcutForCommandName(commandName)
    s = k.prettyPrintKey(s)
    s = k.shortcutFromSetting(s)
    # g.trace('replaceStringShortcut',s)
    self.replaceStringShortcut = s
#@-node:ekr.20060123125317.2: ctor (minibufferFind)
#@+node:ekr.20060124140114: Options (minibufferFind)
#@+node:ekr.20060124123133:setFindScope
def setFindScope(self,where):

    '''Set the find-scope radio buttons.

    `where` must be in ('node-only','entire-outline','suboutline-only'). '''

    h = self.finder

    if where in ('node-only','entire-outline','suboutline-only'):
        var = h.svarDict['radio-search-scope'].get()
        if var:
            h.svarDict["radio-search-scope"].set(where)
    else:
        g.trace('oops: bad `where` value: %s' % where)
#@-node:ekr.20060124123133:setFindScope
#@+node:ekr.20060124122844:get/set/toggleOption (minibufferFind)
# This redirection is required to remove gui-dependencies.

def getOption (self,ivar):          return self.finder.getOption(ivar)
def setOption (self,ivar,val):      self.finder.setOption(ivar,val)
def toggleOption (self,ivar):       self.finder.toggleOption(ivar)
#@-node:ekr.20060124122844:get/set/toggleOption (minibufferFind)
#@+node:ekr.20060125074939:showFindOptions
def showFindOptions (self):

    '''Show the present find options in the status line.'''

    frame = self.c.frame ; z = []
    # Set the scope field.
    head  = self.getOption('search_headline')
    body  = self.getOption('search_body')
    scope = self.getOption('radio-search-scope')
    d = {'entire-outline':'all','suboutline-only':'tree','node-only':'node'}
    scope = d.get(scope) or ''
    head = g.choose(head,'head','')
    body = g.choose(body,'body','')
    sep = g.choose(head and body,'+','')

    frame.clearStatusLine()
    s = '%s%s%s %s  ' % (head,sep,body,scope)
    frame.putStatusLine(s,color='blue')

    # Set the type field.
    script = self.getOption('script_search')
    regex  = self.getOption('pattern_match')
    change = self.getOption('script_change')
    if script:
        s1 = '*Script-find'
        s2 = g.choose(change,'-change*','*')
        z.append(s1+s2)
    elif regex: z.append('regex')

    table = (
        ('reverse',         'reverse'),
        ('ignore_case',     'noCase'),
        ('whole_word',      'word'),
        ('wrap',            'wrap'),
        ('mark_changes',    'markChg'),
        ('mark_finds',      'markFnd'),
    )

    for ivar,s in table:
        val = self.getOption(ivar)
        if val: z.append(s)

    frame.putStatusLine(' '.join(z))
#@-node:ekr.20060125074939:showFindOptions
#@+node:ekr.20060205105950:setupChangePattern
def setupChangePattern (self,pattern):

    h = self.finder ; w = h.change_ctrl

    s = g.toUnicode(pattern,g.app.tkEncoding)

    w.delete(0,'end')
    w.insert(0,s)

    h.update_ivars()
#@-node:ekr.20060205105950:setupChangePattern
#@+node:ekr.20060125091234:setupSearchPattern
def setupSearchPattern (self,pattern):

    h = self.finder ; w = h.find_ctrl

    # g.trace('pattern',pattern)

    s = g.toUnicode(pattern,g.app.tkEncoding)

    w.delete(0,'end')
    w.insert(0,s)

    h.update_ivars()
#@-node:ekr.20060125091234:setupSearchPattern
#@-node:ekr.20060124140114: Options (minibufferFind)
#@+node:ekr.20060210180352:addChangeStringToLabel
def addChangeStringToLabel (self,protect=True):

    c = self.c ; k = c.k ; h = self.finder ; w = h.change_ctrl

    c.frame.log.selectTab('Find')
    c.minibufferWantsFocusNow()

    s = w.getAllText()

    while s.endswith('\n') or s.endswith('\r'):
        s = s[:-1]

    k.extendLabel(s,select=True,protect=protect)
#@-node:ekr.20060210180352:addChangeStringToLabel
#@+node:ekr.20060210164421:addFindStringToLabel
def addFindStringToLabel (self,protect=True):

    c = self.c ; k = c.k ; h = self.finder ; w = h.find_ctrl

    c.frame.log.selectTab('Find')
    c.minibufferWantsFocusNow()

    s = w.getAllText()
    while s.endswith('\n') or s.endswith('\r'):
        s = s[:-1]

    k.extendLabel(s,select=True,protect=protect)
#@-node:ekr.20060210164421:addFindStringToLabel
#@+node:ekr.20070105123800:changeAll
def changeAll (self,event):

    k = self.k ; tag = 'change-all' ; state = k.getState(tag)

    if state == 0:
        w = self.editWidget(event) # sets self.w
        if not w: return
        self.setupArgs(forward=True,regexp=False,word=True)
        k.setLabelBlue('Change All From: ',protect=True)
        k.getArg(event,tag,1,self.changeAll)
    elif state == 1:
        self._sString = k.arg
        self.updateFindList(k.arg)
        s = 'Change All: %s With: ' % (self._sString)
        k.setLabelBlue(s,protect=True)
        self.addChangeStringToLabel()
        k.getArg(event,tag,2,self.changeAll,completion=False,prefix=s)
    elif state == 2:
        self.updateChangeList(k.arg)
        self.lastStateHelper()
        self.generalChangeHelper(self._sString,k.arg,changeAll=True)

#@-node:ekr.20070105123800:changeAll
#@+node:ekr.20060128080201:cloneFindAll
def cloneFindAll (self,event):

    c = self.c ; k = self.k ; tag = 'clone-find-all'
    state = k.getState(tag)

    if state == 0:
        w = self.editWidget(event) # sets self.w
        if not w: return
        self.setupArgs(forward=None,regexp=None,word=None)
        k.setLabelBlue('Clone Find All: ',protect=True)
        k.getArg(event,tag,1,self.cloneFindAll)
    else:
        k.clearState()
        k.resetLabel()
        k.showStateAndMode()
        self.generalSearchHelper(k.arg,cloneFindAll=True)
#@-node:ekr.20060128080201:cloneFindAll
#@+node:ekr.20060204120158:findAgain
def findAgain (self,event):

    f = self.finder

    f.p = self.c.currentPosition()
    f.v = self.finder.p.v

    # This handles the reverse option.
    return f.findAgainCommand()
#@-node:ekr.20060204120158:findAgain
#@+node:ekr.20060209064140:findAll
def findAll (self,event):

    k = self.k ; state = k.getState('find-all')
    if state == 0:
        w = self.editWidget(event) # sets self.w
        if not w: return
        self.setupArgs(forward=True,regexp=False,word=True)
        k.setLabelBlue('Find All: ',protect=True)
        k.getArg(event,'find-all',1,self.findAll)
    else:
        k.clearState()
        k.resetLabel()
        k.showStateAndMode()
        self.generalSearchHelper(k.arg,findAll=True)
#@-node:ekr.20060209064140:findAll
#@+node:ekr.20060205105950.1:generalChangeHelper
def generalChangeHelper (self,find_pattern,change_pattern,changeAll=False):

    # g.trace(repr(change_pattern))

    c = self.c

    self.setupSearchPattern(find_pattern)
    self.setupChangePattern(change_pattern)
    c.widgetWantsFocusNow(self.w)

    self.finder.p = self.c.currentPosition()
    self.finder.v = self.finder.p.v

    # Bug fix: 2007-12-14: remove call to self.finder.findNextCommand.
    # This was the cause of replaces not starting in the right place!

    if changeAll:
        self.finder.changeAllCommand()
    else:
        # This handles the reverse option.
        self.finder.findNextCommand()
#@-node:ekr.20060205105950.1:generalChangeHelper
#@+node:ekr.20060124181213.4:generalSearchHelper
def generalSearchHelper (self,pattern,cloneFindAll=False,findAll=False):

    c = self.c

    self.setupSearchPattern(pattern)
    c.widgetWantsFocusNow(self.w)

    self.finder.p = self.c.currentPosition()
    self.finder.v = self.finder.p.v

    if findAll:
        self.finder.findAllCommand()
    elif cloneFindAll:
        self.finder.cloneFindAllCommand()
    else:
        # This handles the reverse option.
        self.finder.findNextCommand()
#@-node:ekr.20060124181213.4:generalSearchHelper
#@+node:ekr.20060210174441:lastStateHelper
def lastStateHelper (self):

    k = self.k
    k.clearState()
    k.resetLabel()
    k.showStateAndMode()
#@-node:ekr.20060210174441:lastStateHelper
#@+node:ekr.20050920084036.113:replaceString
def replaceString (self,event):

    k = self.k ; tag = 'replace-string' ; state = k.getState(tag)
    pattern_match = self.getOption ('pattern_match')
    prompt = 'Replace ' + g.choose(pattern_match,'Regex','String')
    if state == 0:
        self.setupArgs(forward=None,regexp=None,word=None)
        prefix = '%s: ' % prompt
        self.stateZeroHelper(event,tag,prefix,self.replaceString)
    elif state == 1:
        self._sString = k.arg
        self.updateFindList(k.arg)
        s = '%s: %s With: ' % (prompt,self._sString)
        k.setLabelBlue(s,protect=True)
        self.addChangeStringToLabel()
        k.getArg(event,'replace-string',2,self.replaceString,completion=False,prefix=s)
    elif state == 2:
        self.updateChangeList(k.arg)
        self.lastStateHelper()
        self.generalChangeHelper(self._sString,k.arg)
#@-node:ekr.20050920084036.113:replaceString
#@+node:ekr.20060124140224.3:reSearchBackward/Forward
def reSearchBackward (self,event):

    k = self.k ; tag = 're-search-backward' ; state = k.getState(tag)

    if state == 0:
        self.setupArgs(forward=False,regexp=True,word=None)
        self.stateZeroHelper(
            event,tag,'Regexp Search Backward:',self.reSearchBackward,
            escapes=[self.replaceStringShortcut])
    elif k.getArgEscape:
        # Switch to the replace command.
        k.setState('replace-string',1,self.replaceString)
        self.replaceString(event=None)
    else:
        self.updateFindList(k.arg)
        self.lastStateHelper()
        self.generalSearchHelper(k.arg)

def reSearchForward (self,event):

    k = self.k ; tag = 're-search-forward' ; state = k.getState(tag)
    if state == 0:
        self.setupArgs(forward=True,regexp=True,word=None)
        self.stateZeroHelper(
            event,tag,'Regexp Search:',self.reSearchForward,
            escapes=[self.replaceStringShortcut])
    elif k.getArgEscape:
        # Switch to the replace command.
        k.setState('replace-string',1,self.replaceString)
        self.replaceString(event=None)
    else:
        self.updateFindList(k.arg)
        self.lastStateHelper()
        self.generalSearchHelper(k.arg)
#@-node:ekr.20060124140224.3:reSearchBackward/Forward
#@+node:ekr.20060124140224.1:seachForward/Backward
def searchBackward (self,event):

    k = self.k ; tag = 'search-backward' ; state = k.getState(tag)

    if state == 0:
        self.setupArgs(forward=False,regexp=False,word=False)
        self.stateZeroHelper(
            event,tag,'Search Backward: ',self.searchBackward,
            escapes=[self.replaceStringShortcut])
    elif k.getArgEscape:
        # Switch to the replace command.
        k.setState('replace-string',1,self.replaceString)
        self.replaceString(event=None)
    else:
        self.updateFindList(k.arg)
        self.lastStateHelper()
        self.generalSearchHelper(k.arg)

def searchForward (self,event):

    k = self.k ; tag = 'search-forward' ; state = k.getState(tag)

    if state == 0:
        self.setupArgs(forward=True,regexp=False,word=False)
        self.stateZeroHelper(
            event,tag,'Search: ',self.searchForward,
            escapes=[self.replaceStringShortcut])
    elif k.getArgEscape:
        # Switch to the replace command.
        k.setState('replace-string',1,self.replaceString)
        self.replaceString(event=None)
    else:
        self.updateFindList(k.arg)
        self.lastStateHelper()
        self.generalSearchHelper(k.arg)
#@-node:ekr.20060124140224.1:seachForward/Backward
#@+node:ekr.20060125093807:searchWithPresentOptions
def searchWithPresentOptions (self,event):

    k = self.k ; tag = 'search-with-present-options'
    state = k.getState(tag)

    # g.trace('state',state)
    if state == 0:
        self.setupArgs(forward=None,regexp=None,word=None)
        self.stateZeroHelper(
            event,tag,'Search: ',self.searchWithPresentOptions,
            escapes=[self.replaceStringShortcut])
    elif k.getArgEscape:
        # Switch to the replace command.
        k.setState('replace-string',1,self.replaceString)
        self.replaceString(event=None)
    else:
        self.updateFindList(k.arg)
        k.clearState()
        k.resetLabel()
        k.showStateAndMode()
        self.generalSearchHelper(k.arg)
#@-node:ekr.20060125093807:searchWithPresentOptions
#@+node:ekr.20060124134356:setupArgs
def setupArgs (self,forward=False,regexp=False,word=False):

    h = self.finder ; k = self.k

    if forward is None:
        reverse = None
    else:
        reverse = not forward

    for ivar,val,in (
        ('reverse', reverse),
        ('pattern_match',regexp),
        ('whole_word',word),
    ):
        if val is not None:
            self.setOption(ivar,val)

    h.p = p = self.c.currentPosition()
    h.v = p.v
    h.update_ivars()
    self.showFindOptions()
#@-node:ekr.20060124134356:setupArgs
#@+node:ekr.20060210173041:stateZeroHelper
def stateZeroHelper (self,event,tag,prefix,handler,escapes=None):

    k = self.k
    self.w = self.editWidget(event)
    if not self.w: return

    k.setLabelBlue(prefix,protect=True)
    self.addFindStringToLabel(protect=False)

    # g.trace(escapes,g.callers())
    if escapes is None: escapes = []
    k.getArgEscapes = escapes
    k.getArgEscape = None # k.getArg may set this.
    k.getArg(event,tag,1,handler, # enter state 1
        tabList=self.findTextList,completion=True,prefix=prefix)
#@-node:ekr.20060210173041:stateZeroHelper
#@+node:ekr.20060224171851:updateChange/FindList
def updateChangeList (self,s):

    if s not in self.changeTextList:
        self.changeTextList.append(s)

def updateFindList (self,s):

    if s not in self.findTextList:
        self.findTextList.append(s)
#@-node:ekr.20060224171851:updateChange/FindList
#@+node:ekr.20060124140224.2:wordSearchBackward/Forward
def wordSearchBackward (self,event):

    k = self.k ; tag = 'word-search-backward' ; state = k.getState(tag)

    if state == 0:
        self.setupArgs(forward=False,regexp=False,word=True)
        self.stateZeroHelper(event,tag,'Word Search Backward: ',self.wordSearchBackward)
    else:
        self.lastStateHelper()
        self.generalSearchHelper(k.arg)

def wordSearchForward (self,event):

    k = self.k ; tag = 'word-search-forward' ; state = k.getState(tag)

    if state == 0:
        self.setupArgs(forward=True,regexp=False,word=True)
        self.stateZeroHelper(event,tag,'Word Search: ',self.wordSearchForward)
    else:
        self.lastStateHelper()
        self.generalSearchHelper(k.arg)
#@-node:ekr.20060124140224.2:wordSearchBackward/Forward
#@-node:ekr.20060123125256:class minibufferFind( (the findHandler)
#@+node:ekr.20050920084036.257:class searchCommandsClass
class searchCommandsClass (baseEditCommandsClass):

    '''Implements many kinds of searches.'''

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

    # g.trace('searchCommandsClass')

    baseEditCommandsClass.__init__(self,c) # init the base class.

    self.findTabHandler = None
    self.minibufferFindHandler = None
    self.inited = False

    try:
        self.w = c.frame.body.bodyCtrl
    except AttributeError:
        self.w = None

    # For isearch commands.
    self.ifinder = leoFind.leoFind(c,title='ifinder')
    self.isearch_v = None # vnode of last isearch.
    self.isearch_stack = [] # A stack of previous matches: entries are: (sel,insert)
    self.ignoreCase = None
    self.forward = None
    self.regexp = None
#@-node:ekr.20050920084036.258: ctor (searchCommandsClass)
#@+node:ekr.20050920084036.259:getPublicCommands (searchCommandsClass)
def getPublicCommands (self):

    return {
        'clone-find-all':                       self.cloneFindAll,

        'find-all':                             self.findAll,
        'change-all':                           self.changeAll,

        # Thin wrappers on Find tab
        'change':                               self.findTabChange,
        'change-then-find':                     self.findTabChangeThenFind,
        'find-next':                            self.findTabFindNext,
        'find-prev':                            self.findTabFindPrev,

        'hide-find-tab':                        self.hideFindTab,

        'isearch-forward':                      self.isearchForward,
        'isearch-backward':                     self.isearchBackward,
        'isearch-forward-regexp':               self.isearchForwardRegexp,
        'isearch-backward-regexp':              self.isearchBackwardRegexp,
        'isearch-with-present-options':         self.isearchWithPresentOptions,

        'open-find-tab':                        self.openFindTab,

        'replace-string':                       self.replaceString,

        're-search-forward':                    self.reSearchForward,
        're-search-backward':                   self.reSearchBackward,

        'search-again':                         self.findAgain,
        # Uses existing search pattern.

        'search-forward':                       self.searchForward,
        'search-backward':                      self.searchBackward,
        'search-with-present-options':          self.searchWithPresentOptions,
        # Prompts for search pattern.

        'set-find-everywhere':                  self.setFindScopeEveryWhere,
        'set-find-node-only':                   self.setFindScopeNodeOnly,
        'set-find-suboutline-only':             self.setFindScopeSuboutlineOnly,

        'show-find-options':                    self.showFindOptions,

        'toggle-find-collapses_nodes':          self.toggleFindCollapesNodes,

        'toggle-find-ignore-case-option':       self.toggleIgnoreCaseOption,
        'toggle-find-in-body-option':           self.toggleSearchBodyOption,
        'toggle-find-in-headline-option':       self.toggleSearchHeadlineOption,
        'toggle-find-mark-changes-option':      self.toggleMarkChangesOption,
        'toggle-find-mark-finds-option':        self.toggleMarkFindsOption,
        'toggle-find-regex-option':             self.toggleRegexOption,
        'toggle-find-reverse-option':           self.toggleReverseOption,
        'toggle-find-word-option':              self.toggleWholeWordOption,
        'toggle-find-wrap-around-option':       self.toggleWrapSearchOption,

        'word-search-forward':                  self.wordSearchForward,
        'word-search-backward':                 self.wordSearchBackward,
    }
#@-node:ekr.20050920084036.259:getPublicCommands (searchCommandsClass)
#@+node:ekr.20060123131421:Top-level methods
#@+node:ekr.20051020120306:openFindTab
def openFindTab (self,event=None,show=True):

    '''Open the Find tab in the log pane.'''

    c = self.c ; log = c.frame.log ; tabName = 'Find'

    wasOpen = self.inited

    if self.inited:
        log.selectTab(tabName)
    else:
        self.inited = True
        log.selectTab(tabName,createText=False)
        f = log.frameDict.get(tabName)
        self.findTabHandler = g.app.gui.createFindTab(c,f)

    if show or wasOpen or c.config.getBool('minibufferSearchesShowFindTab'):
        pass # self.findTabHandler.bringToFront()
    else:
        log.hideTab(tabName)
#@-node:ekr.20051020120306:openFindTab
#@+node:ekr.20051022212004:Find Tab commands
# Just open the Find tab if it has never been opened.
# For minibuffer commands, it would be good to force the Find tab to be visible.
# However, this leads to unfortunate confusion when executed from a shortcut.

def findTabChange(self,event=None):
    '''Execute the 'Change' command with the settings shown in the Find tab.'''
    if self.findTabHandler:
        self.findTabHandler.changeCommand()
    else:
        self.openFindTab()

def findTabChangeThenFind(self,event=None):
    '''Execute the 'Replace, Find' command with the settings shown in the Find tab.'''
    if self.findTabHandler:
        self.findTabHandler.changeThenFindCommand()
    else:
        self.openFindTab()

def findTabFindAll(self,event=None):
    '''Execute the 'Find All' command with the settings shown in the Find tab.'''
    if self.findTabHandler:
        self.findTabHandler.findAllCommand()
    else:
        self.openFindTab()

def findTabFindNext (self,event=None):
    '''Execute the 'Find Next' command with the settings shown in the Find tab.'''
    if self.findTabHandler:
        self.findTabHandler.findNextCommand()
    else:
        self.openFindTab()

def findTabFindPrev (self,event=None):
    '''Execute the 'Find Previous' command with the settings shown in the Find tab.'''
    if self.findTabHandler:
        self.findTabHandler.findPrevCommand()
    else:
        self.openFindTab()

def hideFindTab (self,event=None):
    '''Hide the Find tab.'''
    if self.findTabHandler:
        self.c.frame.log.selectTab('Log')
#@-node:ekr.20051022212004:Find Tab commands
#@+node:ekr.20060124115801:getHandler
def getHandler(self,show=False):

    '''Return the minibuffer handler, creating it if necessary.'''

    c = self.c

    self.openFindTab(show=show)
        # sets self.findTabHandler,
        # but *not* minibufferFindHandler.

    if not self.minibufferFindHandler:
        self.minibufferFindHandler = minibufferFind(c,self.findTabHandler)

    return self.minibufferFindHandler
#@-node:ekr.20060124115801:getHandler
#@+node:ekr.20060123115459:Find options wrappers
def setFindScopeEveryWhere (self, event):
    '''Set the 'Entire Outline' radio button in the Find tab.'''
    return self.setFindScope('entire-outline')

def setFindScopeNodeOnly  (self, event):
    '''Set the 'Node Only' radio button in the Find tab.'''
    return self.setFindScope('node-only')

def setFindScopeSuboutlineOnly (self, event):
    '''Set the 'Suboutline Only' radio button in the Find tab.'''
    return self.setFindScope('suboutline-only')

def showFindOptions (self,event):
    '''Show all Find options in the minibuffer label area.'''
    self.getHandler().showFindOptions()

def toggleFindCollapesNodes(self,event):
    '''Toggle the 'Collapse Nodes' checkbox in the find tab.'''
    # return self.toggleOption('collapse_nodes')
    c = self.c ; p = c.currentPosition()
    val = c.config.getBool('collapse_nodes_during_finds')
    c.config.set(p,'collapse_nodes_during_finds',not val)
    g.es('collapse_nodes_during_finds',c.config.getBool('collapse_nodes_during_finds'))

def toggleIgnoreCaseOption     (self, event):
    '''Toggle the 'Ignore Case' checkbox in the Find tab.'''
    return self.toggleOption('ignore_case')

def toggleMarkChangesOption (self, event):
    '''Toggle the 'Mark Changes' checkbox in the Find tab.'''
    return self.toggleOption('mark_changes')
def toggleMarkFindsOption (self, event):
    '''Toggle the 'Mark Finds' checkbox in the Find tab.'''
    return self.toggleOption('mark_finds')
def toggleRegexOption (self, event):
    '''Toggle the 'Regexp' checkbox in the Find tab.'''
    return self.toggleOption('pattern_match')
def toggleReverseOption        (self, event):
    '''Toggle the 'Reverse' checkbox in the Find tab.'''
    return self.toggleOption('reverse')

def toggleSearchBodyOption (self, event):
    '''Set the 'Search Body' checkbox in the Find tab.'''
    return self.toggleOption('search_body')

def toggleSearchHeadlineOption (self, event):
    '''Toggle the 'Search Headline' checkbox in the Find tab.'''
    return self.toggleOption('search_headline')

def toggleWholeWordOption (self, event):
    '''Toggle the 'Whole Word' checkbox in the Find tab.'''
    return self.toggleOption('whole_word')

def toggleWrapSearchOption (self, event):
    '''Toggle the 'Wrap Around' checkbox in the Find tab.'''
    return self.toggleOption('wrap')

def setFindScope (self, where):  self.getHandler().setFindScope(where)
def toggleOption (self, ivar):   self.getHandler().toggleOption(ivar)
#@-node:ekr.20060123115459:Find options wrappers
#@+node:ekr.20060124093828:Find wrappers
def changeAll(self,event=None):
    '''Execute the 'Change All' command with the settings shown in the Find tab.'''
    self.getHandler().changeAll(event)

def cloneFindAll (self,event):
    '''Do search-with-present-options and print all matches in the log pane. It
    also creates a node at the beginning of the outline containing clones of all
    nodes containing the 'find' string. Only one clone is made of each node,
    regardless of how many clones the node has, or of how many matches are found
    in each node.'''
    self.getHandler().cloneFindAll(event)

def findAll            (self,event):
    '''Do search-with-present-options and print all matches in the log pane.'''
    self.getHandler().findAll(event)

def replaceString      (self,event):
    '''Prompts for a search string. Type <Return> to end the search string. The
    command will then prompt for the replacement string. Typing a second
    <Return> key will place both strings in the Find tab and executes a **find**
    command, that is, the search-with-present-options command.'''
    self.getHandler().replaceString(event)

def reSearchBackward   (self,event):
    '''Set the 'Regexp' checkbox to True and the 'Reverse' checkbox to True,
    then do search-with-present-options.'''
    self.getHandler().reSearchBackward(event)

def reSearchForward    (self,event):
    '''Set the 'Regexp' checkbox to True, then do search-with-present-options.'''
    self.getHandler().reSearchForward(event)

def searchBackward     (self,event):
    '''Set the 'Word Search' checkbox to False and the 'Reverse' checkbox to True,
    then do search-with-present-options.'''
    self.getHandler().searchBackward(event)

def searchForward      (self,event):
    '''Set the 'Word Search' checkbox to False, then do search-with-present-options.'''
    self.getHandler().searchForward(event)

def wordSearchBackward (self,event):
    '''Set the 'Word Search' checkbox to True, then do search-with-present-options.'''
    self.getHandler().wordSearchBackward(event)

def wordSearchForward  (self,event):
    '''Set the Word Search' checkbox to True and the 'Reverse' checkbox to True,
    then do search-with-present-options.'''
    self.getHandler().wordSearchForward(event)

def searchWithPresentOptions (self,event):
    '''Prompts for a search string. Typing the <Return> key puts the search
    string in the Find tab and executes a search based on all the settings in
    the Find tab. Recommended as the default search command.'''
    self.getHandler().searchWithPresentOptions(event)
#@-node:ekr.20060124093828:Find wrappers
#@+node:ekr.20060204120158.2:findAgain
def findAgain (self,event):

    '''The find-again command is the same as the find-next command
    if the search pattern in the Find tab is not '<find pattern here>'
    Otherwise, the find-again is the same as the search-with-present-options command.'''

    h = self.getHandler()

    # h.findAgain returns False if there is no search pattern.
    # In that case, we revert to search-with-present-options.
    if not h.findAgain(event):
        h.searchWithPresentOptions(event)
#@-node:ekr.20060204120158.2:findAgain
#@-node:ekr.20060123131421:Top-level methods
#@+node:ekr.20050920084036.261:incremental search...
def isearchForward (self,event):
    '''Begin a forward incremental search.'''
    self.startIncremental(event,forward=True,ignoreCase=False,regexp=False)

def isearchBackward (self,event):
    '''Begin a backward incremental search.'''
    self.startIncremental(event,forward=False,ignoreCase=False,regexp=False)

def isearchForwardRegexp (self,event):
    '''Begin a forward incremental regexp search.'''
    self.startIncremental(event,forward=True,ignoreCase=False,regexp=True)

def isearchBackwardRegexp (self,event):
    '''Begin a backard incremental regexp search.'''
    self.startIncremental(event,forward=False,ignoreCase=False,regexp=True)

def isearchWithPresentOptions (self,event):
    '''Begin an incremental regexp search using the regexp and reverse options from the find panel.'''
    self.startIncremental(event,forward=None,ignoreCase=None,regexp=None)
#@+node:ekr.20060420144640:iSearchBackspace
def iSearchBackspace (self):

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

    if not self.isearch_stack:
        ins = w.getInsertPoint()
        self.endSearch(ins,ins)
        return 

    gui.set_focus(c,w)
    pattern = k.getLabel(ignorePrompt=True)
    self.scolorizer(event=None,pattern=pattern)

    sel,ins = self.isearch_stack.pop()

    if sel:
        i,j = sel
        w.setSelectionRange(i,j,insert=ins)
    else:
        w.setInsertPoint(ins)

    w.seeInsertPoint()

    if not self.isearch_stack:
        self.endSearch(ins,ins)
#@-node:ekr.20060420144640:iSearchBackspace
#@+node:ekr.20050920084036.262:startIncremental
def startIncremental (self,event,forward,ignoreCase,regexp):

    c = self.c ; k = self.k ; w = self.w

    # None is a signal to get the option from the find tab.
    if forward is None or regexp is None:
        self.openFindTab(show=False)
        if not self.minibufferFindHandler:
            self.minibufferFindHandler = minibufferFind(c,self.findTabHandler)
        getOption = self.minibufferFindHandler.getOption
        # g.trace('reverse',getOption('reverse'))
        # g.trace('pattern',getOption('pattern_match'))
    else:
        getOption = lambda a: False # The value isn't used.

    self.event = event
    self.forward    = g.choose(forward is None,not getOption('reverse'),forward)
    self.ignoreCase = g.choose(ignoreCase is None,getOption('ignore_case'),ignoreCase)
    self.regexp     = g.choose(regexp  is None,getOption('pattern_match'),regexp)
    # Note: the word option can't be used with isearches!

    self.ins1 = ins = w.getInsertPoint()
    sel = w.getSelectionRange() or (ins,ins),
    self.isearch_stack = [(sel,ins),]

    k.setLabelBlue('Isearch%s%s%s: ' % (
            g.choose(self.forward,'',' Backward'),
            g.choose(self.regexp,' Regexp',''),
            g.choose(self.ignoreCase,' NoCase',''),
        ),protect=True)
    k.setState('isearch',1,handler=self.iSearchStateHandler)
    c.minibufferWantsFocusNow()
#@-node:ekr.20050920084036.262:startIncremental
#@+node:ekr.20050920084036.264:iSearchStateHandler
# Called when from the state manager when the state is 'isearch'

def iSearchStateHandler (self,event):

    c = self.c ; k = self.k ; w = self.w

    if not event:
        g.trace('no event',g.callers())
        return
    keysym = event.keysym
    ch = event.char
    if keysym == 'Control_L': return

    c.bodyWantsFocusNow()
    if keysym == 'Return':
        i,j = w.getSelectionRange()
        if not self.forward: i,j = j,i
        self.endSearch(i,j)
    elif keysym == 'BackSpace':
        k.updateLabel(event)
        self.iSearchBackspace()
    elif ch:
        k.updateLabel(event)
        self.iSearchHelper(event)
        self.scolorizer(event)
#@-node:ekr.20050920084036.264:iSearchStateHandler
#@+node:ekr.20050920084036.265:scolorizer LATER
def scolorizer (self,event,pattern=None):

    '''Colorizer for incremental searches.'''

    pass # not ready yet.   

    # k = self.k ; w = self.w
    # s = pattern or k.getLabel(ignorePrompt=True)
    # # g.trace(repr(s))
    # w.tag_delete('color','color1')
    # if not s: return
    # if g.app.gui.guiName() != 'tkinter':
        # return g.es('command not ready yet',color='blue')

    # ind = 0
    # index = w.getInsertPoint()
    # index2 = index + len(s)
    # # g.trace(index,index2)
    # # Colorize in the forward direction, regardless of the kind of search.
    # while ind:
        # try:
            # ind = w.search(s,ind,stopindex='end',regexp=self.regexp)
        # except Exception: break
        # if ind:
            # i, d = ind.split('.')
            # d = str(int(d)+len(s))
            # # g.trace(ind)
            # if ind in (index,index2):
                # w.tag_add('color1',ind,'%s.%s' % (i,d))
            # w.tag_add('color',ind,'%s.%s' % (i,d))
            # ind = i + '.' + d

    # w.tag_config('color',foreground='red')
    # w.tag_config('color1',background='lightblue')
#@-node:ekr.20050920084036.265:scolorizer LATER
#@+node:ekr.20050920084036.263:iSearchHelper
def iSearchHelper (self,event):

    '''Move the cursor to position that matches the pattern in the miniBuffer.
    isearches do not cross node boundaries.'''

    c = self.c ; gui = g.app.gui ; k = self.k ; w = self.w
    p = c.currentPosition()
    self.searchString = pattern = k.getLabel(ignorePrompt=True)
    if not pattern: return
    s = w.getAllText()

    if self.isearch_v != p.v:
        self.isearch_v = p.v
        self.isearch_stack = []

    sel = w.getSelectionRange()
    startindex = insert = w.getInsertPoint()

    if self.forward:
        i1 = startindex
        j1 = len(s)
    else:
        i1 = 0
        j1 = min(len(s),startindex + len(pattern))

    i,j = self.ifinder.searchHelper(s,i1,j1,pattern,
        backwards=not self.forward,
        nocase=self.ignoreCase,
        regexp=self.regexp,
        word=False, # Incremental word-matches are not possible!
        swapij=False)

    if i != -1:
        self.isearch_stack.append((sel,insert),)
        # g.trace(i1,j1,i,j,pos,newpos)
        gui.set_focus(c,w)
        w.setSelectionRange(i,j,insert=i)
#@-node:ekr.20050920084036.263:iSearchHelper
#@+node:ekr.20060203072636:endSearch
def endSearch (self,i,j):

    w = self.w
    w.tag_delete('color','color1')

    insert = g.choose(self.forward,'sel.end','sel.start')
    w.setSelectionRange(i,j,insert=insert)

    self.k.keyboardQuit(event=None)
#@nonl
#@-node:ekr.20060203072636:endSearch
#@-node:ekr.20050920084036.261:incremental search...
#@-node:ekr.20050920084036.257:class searchCommandsClass
#@-node:ekr.20051023094009:Search classes
#@+node:ekr.20060125093807:searchWithPresentOptions
def searchWithPresentOptions (self,event):

    k = self.k ; tag = 'search-with-present-options'
    state = k.getState(tag)

    # g.trace('state',state)
    if state == 0:
        self.setupArgs(forward=None,regexp=None,word=None)
        self.stateZeroHelper(
            event,tag,'Search: ',self.searchWithPresentOptions,
            escapes=[self.replaceStringShortcut])
    elif k.getArgEscape:
        # Switch to the replace command.
        k.setState('replace-string',1,self.replaceString)
        self.replaceString(event=None)
    else:
        self.updateFindList(k.arg)
        k.clearState()
        k.resetLabel()
        k.showStateAndMode()
        self.generalSearchHelper(k.arg)
#@-node:ekr.20060125093807:searchWithPresentOptions
#@+node:ekr.20080514131122.20:c.outerUpdate
def outerUpdate (self):

    c = self ; aList = []

    if not c.exists or not c.k:
        return

    if c.requestedIconify == 'iconify':
        aList.append('iconify')
        c.frame.iconify()

    if c.requestedIconify == 'deiconify':
        aList.append('deiconify')
        c.frame.deiconify()

    if c.requestRedrawFlag:
        aList.append('redraw') # : scroll: %s' % (c.requestRedrawScrollFlag))
        c.frame.tree.redraw_now(scroll=c.requestRedrawScrollFlag)

    if c.requestRecolorFlag:
        aList.append('%srecolor' % (
            g.choose(c.incrementalRecolorFlag,'','full ')))
        c.recolor_now(incremental=c.incrementalRecolorFlag)

    if c.requestedFocusWidget:
        aList.append('focus: %s' % (
            g.app.gui.widget_name(c.requestedFocusWidget)))
        c.set_focus(c.requestedFocusWidget)
    else:
        # We can not set the focus to the body pane:
        # That would make nested calls to c.outerUpdate significant.
        pass

    # if aList: g.trace(', '.join(aList)) # ,g.callers(5))

    c.incrementalRecolorFlag = False
    c.requestRecolorFlag = None
    c.requestRedrawFlag = False
    c.requestedFocusWidget = None
    c.requestedIconify = ''
    c.requestedRedrawScrollFlag = False
#@-node:ekr.20080514131122.20:c.outerUpdate
#@+node:ekr.20080516064950.2:Found: getArgEsc
#@+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
    state = k.getState('getArg')
    keysym = gui.eventKeysym(event)
    trace = False or (c.config.getBool('trace_modes') and not g.app.unitTesting)
    if trace: g.trace(
        'state',state,'keysym',keysym,'stroke',stroke,'escapes',k.getArgEscapes,
        'completion', state==0 and completion or state!=0 and k.arg_completion)
    if state == 0:
        k.arg = ''
        << init altX vars >>
        # 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 == 'Tab':
        k.doTabCompletion(k.argTabList,k.arg_completion)
    elif keysym == 'BackSpace':
        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.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 >>
#@-node:ekr.20061031131434.128:getArg
#@+node:ekr.20060124140224.3:reSearchBackward/Forward
def reSearchBackward (self,event):

    k = self.k ; tag = 're-search-backward' ; state = k.getState(tag)

    if state == 0:
        self.setupArgs(forward=False,regexp=True,word=None)
        self.stateZeroHelper(
            event,tag,'Regexp Search Backward:',self.reSearchBackward,
            escapes=[self.replaceStringShortcut])
    elif k.getArgEscape:
        # Switch to the replace command.
        k.setState('replace-string',1,self.replaceString)
        self.replaceString(event=None)
    else:
        self.updateFindList(k.arg)
        self.lastStateHelper()
        self.generalSearchHelper(k.arg)

def reSearchForward (self,event):

    k = self.k ; tag = 're-search-forward' ; state = k.getState(tag)
    if state == 0:
        self.setupArgs(forward=True,regexp=True,word=None)
        self.stateZeroHelper(
            event,tag,'Regexp Search:',self.reSearchForward,
            escapes=[self.replaceStringShortcut])
    elif k.getArgEscape:
        # Switch to the replace command.
        k.setState('replace-string',1,self.replaceString)
        self.replaceString(event=None)
    else:
        self.updateFindList(k.arg)
        self.lastStateHelper()
        self.generalSearchHelper(k.arg)
#@-node:ekr.20060124140224.3:reSearchBackward/Forward
#@+node:ekr.20060124140224.1:seachForward/Backward
def searchBackward (self,event):

    k = self.k ; tag = 'search-backward' ; state = k.getState(tag)

    if state == 0:
        self.setupArgs(forward=False,regexp=False,word=False)
        self.stateZeroHelper(
            event,tag,'Search Backward: ',self.searchBackward,
            escapes=[self.replaceStringShortcut])
    elif k.getArgEscape:
        # Switch to the replace command.
        k.setState('replace-string',1,self.replaceString)
        self.replaceString(event=None)
    else:
        self.updateFindList(k.arg)
        self.lastStateHelper()
        self.generalSearchHelper(k.arg)

def searchForward (self,event):

    k = self.k ; tag = 'search-forward' ; state = k.getState(tag)

    if state == 0:
        self.setupArgs(forward=True,regexp=False,word=False)
        self.stateZeroHelper(
            event,tag,'Search: ',self.searchForward,
            escapes=[self.replaceStringShortcut])
    elif k.getArgEscape:
        # Switch to the replace command.
        k.setState('replace-string',1,self.replaceString)
        self.replaceString(event=None)
    else:
        self.updateFindList(k.arg)
        self.lastStateHelper()
        self.generalSearchHelper(k.arg)
#@-node:ekr.20060124140224.1:seachForward/Backward
#@+node:ekr.20060125093807:searchWithPresentOptions
def searchWithPresentOptions (self,event):

    k = self.k ; tag = 'search-with-present-options'
    state = k.getState(tag)

    # g.trace('state',state)
    if state == 0:
        self.setupArgs(forward=None,regexp=None,word=None)
        self.stateZeroHelper(
            event,tag,'Search: ',self.searchWithPresentOptions,
            escapes=[self.replaceStringShortcut])
    elif k.getArgEscape:
        # Switch to the replace command.
        k.setState('replace-string',1,self.replaceString)
        self.replaceString(event=None)
    else:
        self.updateFindList(k.arg)
        k.clearState()
        k.resetLabel()
        k.showStateAndMode()
        self.generalSearchHelper(k.arg)
#@-node:ekr.20060125093807:searchWithPresentOptions
#@+node:ekr.20060210173041:stateZeroHelper
def stateZeroHelper (self,event,tag,prefix,handler,escapes=None):

    k = self.k
    self.w = self.editWidget(event)
    if not self.w: return

    k.setLabelBlue(prefix,protect=True)
    self.addFindStringToLabel(protect=False)

    # g.trace(escapes,g.callers())
    if escapes is None: escapes = []
    k.getArgEscapes = escapes
    k.getArgEscape = None # k.getArg may set this.
    k.getArg(event,tag,1,handler, # enter state 1
        tabList=self.findTextList,completion=True,prefix=prefix)
#@-node:ekr.20060210173041:stateZeroHelper
#@+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 >>
#@+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 >>
#@-node:ekr.20080516064950.2:Found: getArgEsc
#@+node:ekr.20060123125317.2: ctor (minibufferFind)
def __init__(self,c,finder):

    baseEditCommandsClass.__init__(self,c) # init the base class.

    # g.trace('minibufferFind: finder',finder)

    self.c = c
    self.k = k = c.k
    self.w = None
    self.finder = finder
    self.findTextList = []
    self.changeTextList = []

    commandName = 'replace-string'
    s = k.getShortcutForCommandName(commandName)
    s = k.prettyPrintKey(s)
    s = k.shortcutFromSetting(s)
    # g.trace('replaceStringShortcut',s)
    self.replaceStringShortcut = s
#@-node:ekr.20060123125317.2: ctor (minibufferFind)
#@+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
    state = k.getState('getArg')
    keysym = gui.eventKeysym(event)
    trace = False or (c.config.getBool('trace_modes') and not g.app.unitTesting)
    if trace: g.trace(
        'state',state,'keysym',keysym,'stroke',stroke,'escapes',k.getArgEscapes,
        'completion', state==0 and completion or state!=0 and k.arg_completion)
    if state == 0:
        k.arg = ''
        << init altX vars >>
        # 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 == 'Tab':
        k.doTabCompletion(k.argTabList,k.arg_completion)
    elif keysym == 'BackSpace':
        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.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 >>
#@-node:ekr.20061031131434.128:getArg
#@+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.'''

    # g.trace('event.keysym_num',event.keysym_num,event,dir(event))

    << define vars >>
    if keysym in special_keys: return None

    trace = (False or self.trace_masterKeyHandler) and not g.app.unitTesting
    traceGC = self.trace_masterKeyHandlerGC and not g.app.unitTesting
    if traceGC: g.printNewObjects('masterKey 1')
    if trace:
        g.trace('stroke:',repr(stroke),'keysym:',repr(event.keysym),'ch:',repr(event.char))
            # 'state.kind:',k.state.kind),'\n',g.callers())
        # if (self.master_key_count % 100) == 0: g.printGcSummary()

    # Handle keyboard-quit first.
    if k.abortAllModesKey and stroke == k.abortAllModesKey:
        # g.trace('special case')
        return k.masterCommand(event,k.keyboardQuit,stroke,'keyboard-quit')

    if k.inState():
        # This will return unless k.autoCompleterStateHandler
        # (called from k.callStateFunction) returns 'do-standard-keys'
        << handle mode bindings >>

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

    if stroke and isPlain:
        << handle special cases for plain keys >>

    << handle per-pane bindings >>

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

    return k.handleUnboundKeys(event,char,keysym,stroke)
#@+node:ekr.20061031131434.147:<< define vars >>
k = self ; c = k.c ; gui = g.app.gui

if event: event = gui.leoKeyEvent(event,c)

w = event.widget
char = event.char
keysym = event.keysym
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',
    '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 >>
#@+node:ekr.20061031131434.149:<< handle mode bindings >>
# 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 'break'

# Second, honor general modes.
if state == 'getArg':
    return k.getArg(event,stroke=stroke)
elif state == 'getFileName':
    return 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) # k.state.handler)
    val = k.callStateFunction(event) # Calls end-command.
    if val != 'do-standard-keys': return 'break'

# Third, pass keys to user modes.
else:
    d =  k.masterBindingsDict.get(state)
    if d:
        b = d.get(stroke)
        if b:
            if trace: g.trace('calling generalModeHandler')
            k.generalModeHandler (event,
                commandName=b.commandName,func=b.func,
                modeName=state,nextMode=b.nextMode)
            return 'break'
        else:
            # New in Leo 4.5: unbound keys end mode.
            k.endMode(event)
    else:
        # New in 4.4b4.
        handler = k.getStateHandler()
        if handler:
            handler(event)
        else:
            g.trace('No state handler for %s' % state)
        return 'break'
#@-node:ekr.20061031131434.149:<< handle mode bindings >>
#@+node:ekr.20080510153327.4:<< handle special cases for plain keys >>
# g.trace('plain key','state',k.unboundKeyAction,'stroke',stroke)

# 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))
                k.masterCommand(event,b.func,b.stroke,b.commandName)
                return 'break'

    if trace: g.trace('%s in %s mode' % (stroke,k.unboundKeyAction))
    k.masterCommand(event,func=None,stroke=stroke,commandName=None)
    return 'break'

# Bound   plain keys in command mode are by the per-pane logic.
# Unbound plain keys in command mode are ignored by handleUnboundKeys.

# This code ignores all command-state keys if we are not in a text widget.
elif k.unboundKeyAction == 'command':
    if not g.app.gui.isTextWidget(w):
        c.onCanvasKey(event)
        return 'break'

# elif k.unboundKeyAction == 'command':
    # # In command mode we must disallow all keys except those actually bound in command-state.
    # # That is, we cannot use general per-pane mode bindings.
    # for key,name in (
        # # Order here is similar to bindtags order.
        # ('command',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 (
            # 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,{})
            # # g.trace('key',key,'name',name,'stroke',stroke,'stroke in d.keys',stroke in d.keys())
            # if d:
                # b = d.get(stroke)
                # if b:
                    # if trace: g.trace('%s found %s = %s' % (key,repr(b.stroke),b.commandName))
                    # return k.masterCommand(event,b.func,b.stroke,b.commandName)
    # else:
        # if trace: g.trace('ignoring %s in command mode' % b.stroke)
        # return 'break' # Disallow all unbound plain keys in command mode.
#@nonl
#@-node:ekr.20080510153327.4:<< handle special cases for plain keys >>
#@+node:ekr.20061031131434.150:<< handle per-pane bindings >>
keyStatesTuple = ('command','insert','overwrite')

# g.trace('w_name',w_name,'w',w,'isTextWidget(w)',g.app.gui.isTextWidget(w))
# g.trace('button',k.masterBindingsDict.get('button'))

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,{})
        # g.trace('key',key,'name',name,'stroke',stroke,'stroke in d.keys',stroke in d.keys())
        if d:
            b = d.get(stroke)
            if b:
                if trace: g.trace('%s found %s = %s' % (key,repr(b.stroke),b.commandName))
                if traceGC: g.printNewObjects('masterKey 3')
                return k.masterCommand(event,b.func,b.stroke,b.commandName)
#@-node:ekr.20061031131434.150:<< handle per-pane bindings >>
#@+node:ekr.20061031131434.108:k.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:k.callStateFunction
#@+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','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) ### ,setDefaultUnboundKeyAction=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:handleUnboudKeys
def handleUnboundKeys (self,event,char,keysym,stroke):

    k = self ; c = k.c
    modesTuple = ('insert','overwrite')
    trace = False

    if trace:
        # if stroke: g.trace('***unexpected stroke***')
        g.trace('keysym:',repr(event.keysym),'ch:',repr(event.char))
            # 'state.kind:',k.state.kind),'\n',g.callers())
        # if (self.master_key_count % 100) == 0: g.printGcSummary()

    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 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.find('Escape') != -1:
        # Never insert escape 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:handleUnboudKeys
#@-node:ekr.20061031131434.146:masterKeyHandler & helpers
#@-node:ekr.20080516064950.1:Fixed Shift-Ctrl-r problem after Ctrl-f
#@+node:ekr.20080516113513.2:Improved end of calltips
# Added ')' at end.
#@nonl
#@+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 >>

    if not s:
        << get s using inspect >>

    << remove 'self' from s, but not from args >>
    if isStringMethod:
        << remove 's' from s *and* args >>

    # s = s.rstrip(')') # Not so convenient.
    << insert the text and set j1 and j2 >>

    # 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.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 >>
#@+node:ekr.20061031131434.22:<< get s using inspect >>
isStringMethod = self.prevObjects and type(self.prevObjects[-1]) == types.StringType

# 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 >>
#@+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 >>
#@+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 >>
#@+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 >>
#@+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 >>
#@-node:ekr.20061031131434.20:calltip
#@-node:ekr.20080516113513.2:Improved end of calltips
#@-node:ekr.20080513061733.4:Recent projects
#@-all
#@nonl
#@-node:ekr.20080508083640.10:@thin key-handling-notes.txt
#@-leo
