#@+leo-ver=4-thin
#@+node:ekr.20040117181936:@thin ../doc/leoToDo.txt
#@+all
#@+node:ekr.20080323093939.3:Update Leo's intro pages
#@-node:ekr.20080323093939.3:Update Leo's intro pages
#@+node:ekr.20080311135649.1:4.4.8 to-do
#@+node:ekr.20080313032655.1:One message during import test on Linux
#@+node:ekr.20051104075904.77:Import/Export test code (leoTest.py)
#@+node:ekr.20051104075904.78:makeImportExportSuite
def makeImportExportSuite(c,parentHeadline,doImport):

    """Create an Import/Export test for every descendant of testParentHeadline.."""

    u = testUtils(c)
    parent = u.findNodeAnywhere(parentHeadline)
    assert(parent)
    temp = u.findNodeInTree(parent,"tempNode")
    assert(temp)

    # Create the suite and add all test cases.
    suite = unittest.makeSuite(unittest.TestCase)

    for p in parent.children_iter(copy=True):
        if p == temp: continue
        dialog = u.findNodeInTree(p,"dialog")
        assert(dialog)
        test = importExportTestCase(c,p,dialog,temp,doImport)
        suite.addTest(test)

    return suite
#@-node:ekr.20051104075904.78:makeImportExportSuite
#@+node:ekr.20051104075904.79:class importExportTestCase
class importExportTestCase(unittest.TestCase):

    """Data-driven unit tests for Leo's edit body commands."""

    @others
#@+node:ekr.20051104075904.80:__init__
def __init__ (self,c,p,dialog,temp_p,doImport):

    # Init the base class.
    unittest.TestCase.__init__(self)

    self.c = c
    self.dialog = dialog
    self.p = p.copy()
    self.temp_p = temp_p.copy()

    self.gui = None
    self.oldGui = None
    self.wasChanged = c.changed
    self.fileName = ""
    self.doImport = doImport

    self.old_p = c.currentPosition()
#@-node:ekr.20051104075904.80:__init__
#@+node:ekr.20051104075904.81: fail
def fail (self,msg=None):

    """Mark a unit test as having failed."""

    # __pychecker__ = '--no-argsused'
        #  msg needed so signature matches base class.

    import leoGlobals as g

    g.app.unitTestDict["fail"] = g.callers()
#@-node:ekr.20051104075904.81: fail
#@+node:ekr.20051104075904.82:importExport
def importExport (self):

    c = self.c ; p = self.p

    g.app.unitTestDict = {'c':c,'g':g,'p':p and p.copy()}

    commandName = p.headString()
    command = getattr(c,commandName) # Will fail if command does not exist.
    command(event=None)

    failedMethod = g.app.unitTestDict.get("fail")
    self.failIf(failedMethod,failedMethod)
#@-node:ekr.20051104075904.82:importExport
#@+node:ekr.20051104075904.83:runTest
def runTest(self):

    # """Import Export Test Case"""

    self.importExport()
#@-node:ekr.20051104075904.83:runTest
#@+node:ekr.20051104075904.84:setUp
def setUp(self):

    c = self.c ; temp_p = self.temp_p ; d = self.dialog

    temp_p.setTnodeText('',g.app.tkEncoding)

    # Create a node under temp_p.
    child = temp_p.insertAsLastChild()
    assert(child)
    c.setHeadString(child,"import test: " + self.p.headString())
    c.selectVnode(child)

    assert(d)
    s = d.bodyString()
    lines = s.split('\n')
    name = lines[0]
    fileName = lines[1]

    # Replace '\\' by os.path.sep in fileName
    try:
        # os.path.sep does not exist in Python 2.2.x.
        sep = os.path.sep
        fileName = fileName.replace('\\',sep)
    except AttributeError:
        fileName = g.os_path_normpath(fileName)

    self.fileName = fileName = g.os_path_join(g.app.loadDir,"..",fileName)

    if self.doImport:
        theDict = {name: [fileName]}
    else:
        theDict = {name: fileName}

    self.oldGui = g.app.gui
    self.gui = leoGui.unitTestGui(theDict,trace=False)
#@-node:ekr.20051104075904.84:setUp
#@+node:ekr.20051104075904.85:shortDescription
def shortDescription (self):

    try:
        return "ImportExportTestCase: %s %s" % (self.p.headString(),self.fileName)
    except:
        return "ImportExportTestCase"
#@-node:ekr.20051104075904.85:shortDescription
#@+node:ekr.20051104075904.86:tearDown
def tearDown (self):

    c = self.c ; temp_p = self.temp_p

    if self.gui:
        self.gui.destroySelf()
        self.gui = None

    temp_p.setTnodeText("",g.app.tkEncoding)
    temp_p.clearDirty()

    if not self.wasChanged:
        c.setChanged (False)

    if 1: # Delete all children of temp node.
        while temp_p.firstChild():
            temp_p.firstChild().doDelete()

    g.app.gui = self.oldGui
    c.selectPosition(self.old_p)
#@-node:ekr.20051104075904.86:tearDown
#@-node:ekr.20051104075904.79:class importExportTestCase
#@-node:ekr.20051104075904.77:Import/Export test code (leoTest.py)
#@+node:ekr.20031218072017.2853:importAtFile
def importAtFile (self,event=None):

    '''Import one or more external files, creating @file trees.'''

    c = self

    types = [
        ("All files","*"),
        ("C/C++ files","*.c"),
        ("C/C++ files","*.cpp"),
        ("C/C++ files","*.h"),
        ("C/C++ files","*.hpp"),
        ("Java files","*.java"),
        ("Lua files", "*.lua"),
        ("Pascal files","*.pas"),
        ("Python files","*.py") ]

    names = g.app.gui.runOpenFileDialog(
        title="Import To @file",
        filetypes=types,
        defaultextension=".py",
        multiple=True)
    c.bringToFront()

    if names:
        c.importCommands.importFilesCommand(names,"@file")
#@-node:ekr.20031218072017.2853:importAtFile
#@+node:ekr.20031218072017.3212:importFilesCommand
def importFilesCommand (self,files=None,treeType=None):
    # Not a command.  It must *not* have an event arg.

    c = self.c
    if c == None: return
    v = current = c.currentVnode()
    if current == None: return
    if len(files) < 1: return
    self.tab_width = self.getTabWidth() # New in 4.3.
    self.treeType = treeType
    c.beginUpdate()
    try: # range of update...
        if len(files) == 2:
            << Create a parent for two files having a common prefix >>
        for fileName in files:
            g.setGlobalOpenDir(fileName)
            v = self.createOutline(fileName,current)
            if v: # createOutline may fail.
                if not g.unitTesting:
                    g.es("imported",fileName,color="blue")
                v.contract()
                v.setDirty()
                c.setChanged(True)
        c.validateOutline()
        current.expand()
    finally:
        c.endUpdate()
    c.selectVnode(current)
#@+node:ekr.20031218072017.3213:<< Create a parent for two files having a common prefix >>
@ The two filenames have a common prefix everything before the last period is the same.  For example, x.h and x.cpp.
@c

name0 = files[0]
name1 = files[1]
prefix0, junk = g.os_path_splitext(name0)
prefix1, junk = g.os_path_splitext(name1)
if len(prefix0) > 0 and prefix0 == prefix1:
    current = current.insertAsLastChild()
    # junk, nameExt = g.os_path_split(prefix1)
    name,junk = g.os_path_splitext(prefix1)
    current.initHeadString(name)
#@-node:ekr.20031218072017.3213:<< Create a parent for two files having a common prefix >>
#@-node:ekr.20031218072017.3212:importFilesCommand
#@-node:ekr.20080313032655.1:One message during import test on Linux
#@-node:ekr.20080311135649.1:4.4.8 to-do
#@+node:ekr.20071113093057:4.5 cleanups & vim bindings
#@+node:ekr.20080403100132.1:First
#@+node:ekr.20080404074921.6:Create branch for "make leo a package" stuff
#@-node:ekr.20080404074921.6:Create branch for "make leo a package" stuff
#@+node:ekr.20080404074921.2:Start Komodo question
#@-node:ekr.20080404074921.2:Start Komodo question
#@+node:ekr.20080403102730.1:Explore ipython stuff
@nocolor

>  > %run leo.py  (in leo/leo/src)
>  >
>  > When leo is installable as a package, this will be much simpler
>  > (because we don't need to know where leo.py is).
>
>  I'm having trouble with this because I don't know where leo.py is :-)
>  What directory do I have to be in for this to work?

leo/leo/src. See the documentation in leodocs.

>  Also, I'd like to use ipython for all my Python work, but I'm not sure
>  whether what I am doing makes sense.  Formerly, in the XP Folder

I think it's better to request IPython explicitly (using ip.runlines
if you want ipython input handling or ip.ex if you don't), rather than
having ipython as default for everything.

>  c:\python25\python.exe C:\Python25\scripts\ipython c:\leo.repo\leo-
>  editor\trunk\leo\src\leo.py "%1"
>
>  This works, kind of.  The .leo file starts up, but I get the *python*
>  version number in Leo's log.  When I quit Leo, I do indeed drop into

Yes, IPython runs the startup script using normal python execfile.

>  IPython. Is this how you set things up, Ville?

Pretty normally; I start leo.py as normal python program.
#@nonl
#@-node:ekr.20080403102730.1:Explore ipython stuff
#@+node:ekr.20080402153826.2:Make Leo a package
# The start of this project is in leo.repo/leo-working.
#@nonl
#@-node:ekr.20080402153826.2:Make Leo a package
#@+node:ekr.20080330085343.4:Add new iterators from branch thrysus
#@-node:ekr.20080330085343.4:Add new iterators from branch thrysus
#@-node:ekr.20080403100132.1:First
#@+node:ekr.20080330085343.2:Bugs
#@+node:ekr.20080315083057.6:Fix spell bug: words containing unicode characters aren't saved properly
#@-node:ekr.20080315083057.6:Fix spell bug: words containing unicode characters aren't saved properly
#@+node:ekr.20080406075855.1:Fix find completion bug
@nocolor

Trying to edit the minibuffer, when I backspace to a character and
press delete the cursor moves to the end of the buffer and deletes the
last character instead.
#@nonl
#@-node:ekr.20080406075855.1:Fix find completion bug
#@+node:ekr.20080330085343.3:Fix bug in atFile.write
The code should save/restore, just as in the clean_file function (from scripts.leo)
#@nonl
#@+node:ekr.20080330085343.1:clean_file
def clean_file(p):

    at = c.atFileCommands

    if hasattr(p.v.t,'tnodeList'):
        has_list = True
        old_list =  p.v.t.tnodeList[:]
    else:
        has_list = False

    at.write(
        root=p,nosentinels=True,
        thinFile=False,
        scriptWrite=True,
        toString=True,
        write_strips_blank_lines=False)

    if has_list:
        p.v.t.tnodeList = old_list

    fileName = g.os_path_normpath(g.os_path_join(
        at.default_directory,
        'clean',
        g.os_path_basename(p.anyAtFileNodeName())))

    # g.trace(p.headString(),len(at.stringOutput),fileName)

    # Adapted from at.openFileForWritingHelper
    path = g.os_path_dirname(fileName)
    if not g.os_path_exists(path):
        g.es('clean directory does not exist',path)
        return

    try:
        f = file(fileName,'w')
        f.write(at.stringOutput)
        f.close()
        g.es_print('wrote',fileName)
    except IOError:
        g.es_print('can not write',fileName,color='red')
        g.es_exception()
#@-node:ekr.20080330085343.1:clean_file
#@-node:ekr.20080330085343.3:Fix bug in atFile.write
#@+node:ekr.20080405074410.39:Fix new @auto bug
@nocolor

Leo's running, so I create a new outline with File->New

Then I save as in a location where foo.py exists.

The I create a node '@auto foo.py'

Then I use File->Read/Write->Read @auto (with that node selected)

Leo says it can't read it.

I close the leo file, re-open in from the recent files menu of the
outline that spawned it, and the @auto file is read no problems.


#@-node:ekr.20080405074410.39:Fix new @auto bug
#@+node:ekr.20080406075855.2:Fix problem with already-existing .leo.bak files
@nocolor

I also unhappily discovered that any file named somedoc.leo.bak will
get deleted whenever you save a file named somedoc.leo.  Once I saw my
nodes were overwritten unitTest.leo, I copied somedoc.leo to
somedoc.leo.bak because I wanted to do a save to see what would
happen.  The somedoc.leo file ended up with the contents of
unitTest.leo as I suspected.  To my surprise, I also lost
somedoc.leo.bak!  The file disappeared!  Fortunately, I had another
backup copy to revert to.  If leo is making use of a .bak file during
saving, then should it give a warning if that file already exists?

To duplicate:

1. Open a leo file that you don't care about losing.
2. From the file->open menu open leo/test/unitTest.leo
3. Select a unit test and run it with Alt-4 (I chose the Position
tests described above)
4. Switch back to the original leo document and you should see the
contents of unitTest.leo instead of the original document contents.
#@nonl
#@-node:ekr.20080406075855.2:Fix problem with already-existing .leo.bak files
#@+node:ekr.20080406075855.3:Fix problem with running unitTest.leo when it is opened from another .leo file
@nocolor

I was playing around with this and I discovered that if I open
unitTest.leo from the File->Open menu pick of another leo outline and
then I run a unit test, then all the nodes from unitTest.leo appear in
the original document.  If I execute a save on the original document
(now with the unitTest.leo nodes), then the leo file gets overwritten
with the contents unitTest.leo.

#@-node:ekr.20080406075855.3:Fix problem with running unitTest.leo when it is opened from another .leo file
#@-node:ekr.20080330085343.2:Bugs
#@+node:ekr.20080124083629.1:Improve @path handling
@nocolor

[quote]
nested @paths don't do what I expected: 

For a leo file in directory foo: 

@path src 
....@path tmpl

should refer to foo/src and foo/src/tmpl

using @path src/tmpl works fine though.
[end quote]
#@-node:ekr.20080124083629.1:Improve @path handling
#@+node:ekr.20080218110223.1:Files
#@+node:ekr.20080318081653.1:Support 'fixed Leo file' option
@nocolor

We should avoid conflicts here in the first place. We really need that
'toggle-fixed' thing (that would prevent writing window positions and node
expansion statuses to .leo file) I suggested a while back, to make collaboration
with .leo files possible...
#@-node:ekr.20080318081653.1:Support 'fixed Leo file' option
#@+node:ekr.20080311135649.2:Allow different .leo formats
@nocolor

On Tue, Mar 11, 2008 at 7:03 AM, Kent Tenney <kten...@gmail.com> wrote:

> On 3/11/08, derwisch <johannes.hues...@med.uni-heidelberg.de> wrote:

> >  On 11 Mrz., 08:03, "Ville M. Vainio" <vivai...@gmail.com> wrote:
> >  > It could also be argued that

> >  > - Referring to previous cloned vnodes explicitly in XML does not
> >  > necessarily obscure DAG - it follows the "do not repeat yourself"
> rule
> >  > - It will speed up reading
> >  > - Wouldn't it be better for preserving the integrity of the XML file?

> > I would lean towards this line of argumentation. A couple of days I
> >  had my Leo extension destroy the Leo ODM file (which was still valid
> >  according to Leo, but unreadable wrt the extension and broken uAs). I
> >  resorted to editing the Leo file with Emacs, and was quite surprised
> >  to see that the headStrings were attributes of vnodes.

> I'll chime in with my pet peeve re: .leo file structure::

> I think that putting the headstrings on vnodes and body strings on tnodes
> obscures the informational content of the .leo file, and makes the .leo
> file
> format less attractive as a generalized solution to the problem of how to
> manage head/body pairs which live in a hierarchal structure.

> Thanks,
> Kent

> >  I think that
> >  editing the file might have been a bit easier if there had been no
> >  such redundancy. But this is more a feeling rather than a qualified
> >  opinion.

Thanks for all these comments.  I'll respond to them all here.

Clearly, we should be using a standard xml parser to read .leo files.

My present thoughts:

- I personally like human-readable headlines in <v> elements.

- I am open to putting headlines in <t> elements, as an indication that
tnodes do indeed contain headlines and body text.

- I am willing to consider only writing shared subtrees once.

Oh! (An Aha)  All these are preferences.  We can allow any combination of
these provided that headlines appear somewhere.

So that's clean.  This will happen in Leo 4.5. 
#@nonl
#@-node:ekr.20080311135649.2:Allow different .leo formats
#@+node:ekr.20071031113657:Always use sax parser to read .leo files
# Are there proper unit tests?
# Do global settings and find-panel settings get set properly.
#@nonl
#@-node:ekr.20071031113657:Always use sax parser to read .leo files
#@+node:ekr.20071003104917:Make http://edreamleo.org/namespaces/leo-python-editor/1.1 Leo's official namespace
xmlns:leo="http://edreamleo.org/namespaces/leo-python-editor/1.1"
#@nonl
#@-node:ekr.20071003104917:Make http://edreamleo.org/namespaces/leo-python-editor/1.1 Leo's official namespace
#@+node:ekr.20080225104355:@bool fixed-leo-file
@nocolor

http://mail.google.com/mail/#inbox/118504bef91bb8ce

I think it might be handy to have a 'fixed' flag in .leo file, to
minimize cvs conflicts. When a leo document has that flag, nothing
about frame positions etc. will ever be written to the .leo file on
save.

Most leo documents where people are supposed to collaborate should
have this flag (e.g. the .leo documents in Leo cvs).

@bool fixed-leo-file = True
#@-node:ekr.20080225104355:@bool fixed-leo-file
#@-node:ekr.20080218110223.1:Files
#@+node:ekr.20071211142600.2:Config stuff
#@+node:ekr.20080311135649.5:List the special cases in the config code
#@-node:ekr.20080311135649.5:List the special cases in the config code
#@+node:ekr.20080225103954:Add 'else' clause to if-platform
@nocolor

**OR** put 'else' (default) first, and have @ifplatform only override parts.

@ifplatform isn't very useful without @else or some other kind of default.
#@nonl
#@-node:ekr.20080225103954:Add 'else' clause to if-platform
#@+node:ekr.20080219080209:Create myLeoSettings.leo if it does not exist
@nocolor

http://mail.google.com/mail/#label/Leo/1182d78133aa0ecf

Create a simple template myLeoSettings.leo in $HOME when you try to
open it and it doesn't exist
#@nonl
#@-node:ekr.20080219080209:Create myLeoSettings.leo if it does not exist
#@+node:ekr.20071211142600.3:Clean up config mess
#@+node:ekr.20080313032655.2:Once a plugin is enabled, it is always enabled
#@-node:ekr.20080313032655.2:Once a plugin is enabled, it is always enabled
#@+node:ekr.20080129103436:Solve Kent's @menu problem
@nocolor

http://sourceforge.net/forum/forum.php?thread_id=1858451&forum_id=10228

I'm interested in using @settings to create application 
specific mini-editors. I think there's great promise in  
@settings nodes which create an environment with buttons 
and menu choices to work with specific file types, or other 
niche applications. The menus and buttons will expose a 
set of capabilities, and unneeded capabilities will be hidden. 

However, if I create a @settings->@menus->@menu node in a file, open 
the file, then open another Leo file, the next file opens with the 
redefined menu. 
#@nonl
#@-node:ekr.20080129103436:Solve Kent's @menu problem
#@+node:ekr.20061014050154.2:Binding to None clears all bindings
#@-node:ekr.20061014050154.2:Binding to None clears all bindings
#@+node:ekr.20060927173836.6:Don't abort mode if there are problems with bindings
#@-node:ekr.20060927173836.6:Don't abort mode if there are problems with bindings
#@-node:ekr.20071211142600.3:Clean up config mess
#@+node:ekr.20071010162003:Add @rclick-menus trees to specify rclick popup menus
@nocolor

http://sourceforge.net/forum/message.php?msg_id=4562648
By: rogererens

Maybe I may use this thread to make another suggestion for Leo 4.4.5, related
to @menus: have the menu-options for right-clicking in the various panes also
in the @settings-tree.

Regards, Roger
#@nonl
#@-node:ekr.20071010162003:Add @rclick-menus trees to specify rclick popup menus
#@-node:ekr.20071211142600.2:Config stuff
#@+node:ekr.20070623144302:vim-like bindings
#@+node:ekr.20071105160956:*** auto-change panes
@nocolor

The idea: if a key binding is only in effect for one pane, and we get the
binding, maybe we should change to the pane first!

Examples:

copy-node: auto-select tree pane.
copy_text (when not editing a headline): auto-select body text.
#@nonl
#@-node:ekr.20071105160956:*** auto-change panes
#@+node:ekr.20070824094005:Requests
@nocolor

> Some of the features I've grown to like with VIM are search-and-replace within the current selection, easy keyboard navigation, and to some extent macro recording and running.

Thanks for these comments.  I'll keep them in mind as I design the vim-like code.

Edward
#@nonl
#@-node:ekr.20070824094005:Requests
#@+node:ekr.20070814083528.1:Design post
@nocolor

http://sourceforge.net/forum/forum.php?thread_id=1800079&forum_id=10226

Recent posts have discussed @menu trees and resolving cvs conflicts. The third of my recent ruminations concerns adding support for vim-like key bindings. 

Earlier work involving input modes (@mode nodes in settings file) probably is not completely sufficient to implement plain-key key bindings in a way that would truly be vim-like. 

Most of the issues are implementation issues: Leo's key-handling code is very complicated. The trick is to handle both vim and emacs styles compatibly. 

The user must be able to specify vim-like key bindings without using @mode nodes. That is, top-level vim key bindings must always be available. Vim has an enormous number of key bindings: the user must be able to specify which are in effect. 

My strategy will be as follows: 

1. Create enter-vim-mode and exit-vim-mode commands, and create the setting: 

@bool vim-mode-by-default = False 

This will allow the user to specify vim mode as the default mode if desired. 

2. Create **vim-edit** commands as needed in order to handler user interaction. For example, dd deletes the present line while d5 deletes 5 lines. Rather than trying to simulate this effect using the present mechanisms, the vim-delete command will prompt for the number of characters or lines to delete. 

Give the complex nature of vims c and d commands (and others) it will probably be necessary to create a vim-edit class in leoEditCommands.py. This class will handle all the ways of specifying a range of characters to which the various commands will apply. It's not clear exactly how much work will be needed, but my guess is that it will, in fact, be easiest to handle vim-like interactions with new code. 

3. Vim's ':' command will simply invoke Leo's minibuffer. This isn't precisely the same as Vim's status line: it is significantly better. Otoh, we will want a way to simulate the actual spelling of vim's ':' commands. Possible @abbreviation nodes can be pressed into service. 

4. Internally, it will be quite a trick to allow top-level vim-like plain-key bindings in all text widgets. I'm not sure how to do this, but it is essential. 

I am quite eager to get something truly vim-like working. It promises to increase my editing speed substantially. It also should appeal strongly to present vim users. 
#@nonl
#@-node:ekr.20070814083528.1:Design post
#@+node:ekr.20071026140857:Posting re vim bindings
@nocolor

> I'm looking forward to this.

Excellent.  You can help with the testing :-)

Leo looks like a perfect match for the vim approach.  Indeed, vim typically uses a 1 or 2 level concatenation scheme (preceded by a repeat count).  This is kinda like a utf-8 encoding :-)  That is, it is a variable-length encoding of the command.

This is a very powerful approach, one that vim doesn't really exploit (because it doesn't need to). But Leo absolutely needs this power of some extra levels.  Something like this:

i input mode

f focus commands
..fb focus to body
..fo focus to outline
..fl focus to log
..ft toggle body pane focus

o outline commands
..om outline move, i.e., actually move the nodes.
..og outline go, i.e., select nodes without changing the outline
..od outline delete
..oh outline edit headline

s search commands
..so search options
...soi toggle ignore-case checkbox
...sow toggle word checkbox
...etc!
..st search using search tab
..sf forward incremental search
..sb backward incremental search

x minibuffer commands

w file commands
..ws save outline
..wq quit

escape: universal escape, like ctrl-g now.

Do you see?  Just one extra level allows 10+ times the number of key bindings.  This is a perfect match for Leo's complex environment.

Naturally, Leo will allow the user to specify these bindings using nodes in the @settings tree. Indeed, this is truly an exciting prospect.

Edward

P.S.  I've glossed over the details of which single characters (like d for delete character) should be reserved for common editing and cursor-movement commands.  That's the beauty of creating these bindings with user settings: no need to argue :-)  And that's the beauty of arbitrarily deep key binding trees: there are plenty of keys to go around.

EKR
#@-node:ekr.20071026140857:Posting re vim bindings
#@+node:ekr.20070613181147:(maybe) Improve key handling
# The idea is to use a single <Key binding> and
# use w.event_generate(key) to create a table
# that associates Tk key numbers with bindings:

    if event.char:
        if 0:
            import Tkinter as Tk
            w = Tk.Text()
            def foo (event):
                g.trace(event)
            w.bind('<Key>',foo)
            for prefix in ('Alt-Key-','Shift-Key-'):
                key = '<%s%s>' % (prefix,event.char.strip())
                g.trace(key)
                w.event_generate('a')
        if 0:
            for z in ('char','keysym','keycode','keysym_num','state',):
                a = getattr(event,z)
                print z,repr(a)
#@-node:ekr.20070613181147:(maybe) Improve key handling
#@-node:ekr.20070623144302:vim-like bindings
#@+node:ekr.20071029082025:Improve distribution
#@+node:ekr.20071211093323:more screen shots on Leo's web site
#@-node:ekr.20071211093323:more screen shots on Leo's web site
#@+node:ekr.20070929125944:Emulate Orange's download philosophy
@nocolor

http://sourceforge.net/forum/message.php?msg_id=4543089
By: billp9619

from the download page:

If it's the first time you hear about Python, this is the installation for you.
The packages includes complete Orange, Python, Windows Extensions for Python
(PythonWin), Numeric Python, Qt 2.2 non-commercial, PyQt, PyQwt and GraphViz.

Leo should copy this download philosophy.



#@-node:ekr.20070929125944:Emulate Orange's download philosophy
#@+node:ekr.20070615063616.1:Create Debian package
#@-node:ekr.20070615063616.1:Create Debian package
#@-node:ekr.20071029082025:Improve distribution
#@+node:ekr.20071215101531:Reorg drawing/focus code?
@nocolor

** Dangerous!  Complex!
    * This could be a very bad idea.
    - Make *sure* everything works before making massive code changes!
    - Make a separate bzr branch for this.

Changing position
    - Affects both redraw and focus.

Drawing
    - Most redraws happen at end-of-command.
    - Must allow immediate redraws: make list of exceptions.
    ** Don't eliminate begin/endUpdate pairs until *sure* everything works.
        - Experiment by making begin/endUpdate do nothing.

Focus
    - Most focus changes happen at end-of-command.
    - Must allow immediate focus changes: make list of exceptions.
    ** The present focus code seems too complex, **but it works**.
    - Clean up focus code: use treeFocusHelper everywhere

@color
#@nonl
#@+node:ekr.20071215114036:Possible code
@color

@ notes:

- Calls to request_x emphasize that the action is not to be taken immediately.
- Calls to redraw, set_focus and setCurrentPosition emphasize that the action will happen eventually.
- Calls to actual_x are for special situations.
@c

Add c ivars: requested_draw, requested_position, requested_focus.

c methods:

def update (self):
    '''Honor all requests for drawing, focus and position.'''
    c = self
    if c.requested_position(): # First.
        c.actual_selectPosition(c.requested_positiion)
        c.requested_position = None
    if c.requested_draw: # Second.
        c.actual_draw()
        c.requested_draw = False
    if c.requested_focus: # Third.
        c.actual_set_focus()
        c.requested_focus = None

def request_draw (self,flag = True):
    self.requested_draw = flag
draw = redraw = request_redraw

def begin_update(self):
    pass
end_update = request_redraw

def actual_draw (self):
    << do the actual redraw >>
redraw_now = actual_draw

def request_currentPosition (self,p):
    self.requested_draw = p.copy()
setCurrentPosition = request_currentPosition

def actual_setCurrentPosition (self,p):
    << select position p >>

def request_focus (self,w):
    self.requested_focus = w
set_focus = request_focus

def actual_set_focus (self,w):
    << set the focus to w >>
set_focus_now = actual_set_focus
#@nonl
#@-node:ekr.20071215114036:Possible code
#@-node:ekr.20071215101531:Reorg drawing/focus code?
#@-node:ekr.20071113093057:4.5 cleanups & vim bindings
#@+node:ekr.20071211093011:4.6  autocompletion
#@+node:ekr.20080113165010:About auto completion
@nocolor

Summary: inspect when possible, ask for types otherwise.

    os.? gives a list of all instances of os module.
    x.? gives a list of all known classes.
    x.# gives list of all known functions.
    self.? gives all known methods and ivars of the enclosing class.
    The user will be able to specify type conventsions, like Leo's c,g,p,t,v vars.

Completed (mostly): user interface stuff.

Performance isn't too hard:
    - Do all scanning in separate threads.
    - Associate node info with tnodes.
    - Update node info when deselecting a node (in tree base class)

Parsing:
    - Forgiving parser is essentially complete.
    - It's easy to parse python def lines.
#@nonl
#@-node:ekr.20080113165010:About auto completion
#@+node:ekr.20080110082845:pyxides: code completion
@nocolor

Python code completion module


From: "Tal Einat" <talei...@gmail.com>
Date: Wed, 6 Jun 2007 20:57:18 +0300

I've been developing IDLE over the past 2 years or so. Even before
that, I helped a friend of mine, Noam Raphael, write IDLE's
auto-completion, which is included in recent versions of IDLE.

Noam wrote the original completion code from scratch, and AFAIK every
Python IDE which features code completion has done the same. Surely
there is -some- functionality which could be useful cross-IDE?
Retrieving possible completions from the namespace, for example. And
we should be learning from each-others' ideas and experiences.

So how about we design a generic Python completion module, that
each IDE could extend, and use for the completion logic?



From: "Ali Afshar" <aafs...@gmail.com>
Date: Wed, 6 Jun 2007 19:06:01 +0100

I am very keen for this. I will help where it is required. PIDA
currently has no code completion (outside what vim/emacs provide),



From: "phil jones" <inters...@gmail.com>
Date: Wed, 6 Jun 2007 11:07:33 -0700

What functions would we ask for a code completion module?

Presumably recognition of the beginnings of
- a) python keywords
- b) classes and functions defined earlier in this file?
- c) in scope variables?

As python is dynamically typed, I guess we can't expect to know the
names of methods of objects?



From: "Ali Afshar" <aafs...@gmail.com>
Date: Wed, 6 Jun 2007 19:13:10 +0100

> Presumably recognition of the beginnings of
> - a) python keywords
> - b) classes and functions defined earlier in this file?
> - c) in scope variables?

does c) include: d) imported modules



From: Nicolas Chauvat <nicolas.chau...@logilab.fr>
Date: Wed, 6 Jun 2007 20:17:30 +0200

> >Presumably recognition of the beginnings of
> >- a) python keywords
> >- b) classes and functions defined earlier in this file?
> >- c) in scope variables?

> does c) include: d) imported modules

For code-completion, I suppose astng[1] could be useful.

1: http://www.logilab.org/project/eid/856



From: Stani's Python Editor <spe.stani...@gmail.com>
Date: Wed, 06 Jun 2007 20:48:41 +0200

A good point. I think we all have been thinking about this. Important
issues for the design is the extraction method and the sources.

*the method*
Importing is a lazy, but accurate way of importing, but is security wise
not such a good idea. Parsing throught an AST compiler is better,
however more difficult. Here are two options.

From version 2.5 the standard Python compiler converts internally the
source code to an abstract syntax tree (AST) before producing the
bytecode. So probably that is a good way to go as every python
distribution has this battery included.

As Nicolas suggested earlier on this mailing list, there is another
option: the AST compiler in python or PyPy:

On Mar 14 2006, 12:16 am, Nicolas Chauvat <nicolas.chau...@logilab.fr>
wrote:

> > WingIDE use anASTgenerator written in C (but cross-platform),
> > lightningly quick, and open sourced. This could be a potential
> > starting point.

> > Additionally isn't Python2.5 planned to have a C-written compiler?

> PyPy also produced an improved parser/compiler.

> http://codespeak.net/pypy/dist/pypy/doc/index.html
> http://codespeak.net/pypy/dist/pypy/module/recparser/

But if it could be done with the standard one it is one dependency less.

*the sources*
In the design we could define first the sources:
1 external imported modules from the pythonpath
2 local modules relative to the current file or context dependent
(Blender, Gimp, ...)
3 inner code

For 1:
It might be a good idea to have a function which scans all the modules
from the pythonpath or one specific module to cache all autocompletion
and calltip information of all classes, methods and doc strings. Why?
Modules in the pythonpath don't change so often. With some criteria
(file name, time stamp, size, ...) you could check if updates are
necessary at startup. Having a readymade 'database' (could be python
dictionary or sqlite database) for autocompletion/call tips would speed
up things (and is also more secure if you are importing rather than
parsing. For example trying to provide gtk autocompletion in a wxPython
by importing is problematic).

For 2:
Here you load the parser on demand. Autocompletion/calltip information
can be added to the database.

For 3:
A different kind of parser needs to be used here as per definition code
you edit contains errors while typing. External modules are retrieved
from 1 and 2, for internal code you can scan all the words and add them
to the autocomplete database. As a refinement you can give special
attention to 'self'. Also for calltips you can inherit when there are
assignments, eg
frame = Frame()
than frame inherits autocomplete & calltip information from Frame.

So autocompletion & calltips deals with two steps: extraction and
'database'. If someone has a good parser already, we could use it.
Otherwise we can define an API for the extraction and maybe lazily
implement it first with importing and concentrate first on the
'database'. When the database is ready we can implement the parsing. You
could also implement the parsing first, but than it takes longer before
you have results. Of course the library is GUI independent, it only
works with strings or lists.

What concerns SPE, it uses importing for autocompletion (1+2) and does
internal code analysis for local code (however without the inheriting).

Tal, how does IDLE's autocompletion works?

Stani



From: Stani's Python Editor <spe.stani...@gmail.com>
Date: Wed, 06 Jun 2007 20:53:10 +0200

Nicolas Chauvat wrote:
> On Wed, Jun 06, 2007 at 07:13:10PM +0100, Ali Afshar wrote:
>>> Presumably recognition of the beginnings of
>>> - a) python keywords
>>> - b) classes and functions defined earlier in this file?
>>> - c) in scope variables?
>> does c) include: d) imported modules

> For code-completion, I suppose astng[1] could be useful.

> 1: http://www.logilab.org/project/eid/856

How dependent/independent is this from the standard AST compiler or
PyPy? Is it more IDE friendly? Is it based on it or a total independent
implementation?



From: "Ali Afshar" <aafs...@gmail.com>
Date: Wed, 6 Jun 2007 19:59:13 +0100

> A good point. I think we all have been thinking about this. Important
> issues for the design is the extraction method and the sources.

> *the method*
> Importing is a lazy, but accurate way of importing, but is security wise
> not such a good idea. Parsing throught an AST compiler is better,
> however more difficult. Here are two options.

> From version 2.5 the standard Python compiler converts internally the
> source code to an abstract syntax tree (AST) before producing the
> bytecode. So probably that is a good way to go as every python
> distribution has this battery included.

> As Nicolas suggested earlier on this mailing list, there is another
> option: the AST compiler in python or PyPy:

What concerns me about these is whether they would work in a module
which has a syntax error.

I believe Wing's compiler bit of their code completion is open source.
I remember having seen the code.



From: Stani <spe.stani...@gmail.com>
Date: Wed, 06 Jun 2007 12:08:00 -0700

> What concerns me about these is whether they would work in a module
> which has a syntax error.

> I believe Wing's compiler bit of their code completion is open source.
> I remember having seen the code.

It is indeed, but is implemented in C, which means an extra dependency
and not a 100% python solution. Normally modules (especially in the
pythonpath) which you import don't have syntax errors. Maybe logilabs
implementation handles syntax errors well as it is developed for
PyLint. Nicolas?



From: "Tal Einat" <talei...@gmail.com>
Date: Wed, 6 Jun 2007 22:34:41 +0300

> As python is dynamically typed, I guess we can't expect to know the
> names of methods of objects?

Well, the dir() builtin does just that, though there can be attributes
which won't be included therein. However, the builtin dir() can be
overridden... and ignoring it can break libraries like RPyC which
define a custom dir() function just for this purpose.

This issue has already been run in to by RPyC (an Python RPC lib). The
main developr went ahead and suggested adding a __dir__ method which
will return a list of attributes, and IIRC he has already implemented
a patch for this, and it will likely enter Python2.6.

Until then, I guess we're going to have to rely on dir for this.



From: "Josiah Carlson" <josiah.carl...@gmail.com>
Date: Wed, 6 Jun 2007 12:42:01 -0700

For reference, PyPE auto-parses source code in the background, generating
(among other things) a function/class/method hierarchy.  Its autocomplete
generally sticks to global functions and keywords, but when doing
self.method lookups, it checks the current source code line, looks up in its
index of classes/methods, and trims the results based on known methods in
the current class in the current source file.

It certainly isn't complete (it should try to check base classes of the
class in the same file, it could certainly pay attention to names assigned
in the current scope, the global scope, imports, types of objects as per
WingIDE's assert isinstance(obj, type), etc.), but it also makes the
computation fairly straightforward, fast, and only in reference to the
current document.



From: "Tal Einat" <talei...@gmail.com>
Date: Wed, 6 Jun 2007 22:52:08 +0300

> Tal, how does IDLE's autocompletion works?

Much like Stani said, since Python is interpreted, collection of
possible completions splits into two methods:
1) source code analysis
2) dynamic introspection

Of course, we could do either or a combination of both.

IDLE just uses introspection: since IDLE always has a python shell
running, it just completes according to the shell's state (plus
built-in keywords and modules). This is a very simple method,
obviously lacking. It does allow the user some control of the
completion, though - just import whatever you want to be completable
in the shell. However, introspection is all that is needed in a Python
shell, which is the major reason this is the method used in IDLE.



From: Nicolas Chauvat <nicolas.chau...@logilab.fr>
Date: Wed, 6 Jun 2007 23:59:32 +0200


> How dependent/independent is this from the standard AST compiler or
> PyPy? Is it more IDE friendly? Is it based on it or a total independent
> implementation?

It is independent from PyPy.

The above web page says:

"""
Python Abstract Syntax Tree New Generation

The aim of this module is to provide a common base representation of
python source code for projects such as pychecker, pyreverse,
pylint... Well, actually the development of this library is essentialy
governed by pylint's needs.

It extends class defined in the compiler.ast [1] module with some
additional methods and attributes. Instance attributes are added by a
builder object, which can either generate extended ast (let's call
them astng ;) by visiting an existant ast tree or by inspecting living
object. Methods are added by monkey patching ast classes.Python
Abstract Syntax Tree New Generation

The aim of this module is to provide a common base representation of
python source code for projects such as pychecker, pyreverse,
pylint... Well, actually the development of this library is essentialy
governed by pylint's needs.

It extends class defined in the compiler.ast [1] module with some
additional methods and attributes. Instance attributes are added by a
builder object, which can either generate extended ast (let's call
them astng ;) by visiting an existant ast tree or by inspecting living
object. Methods are added by monkey patching ast classes.
"""

From: "Sylvain Thénault" <thena...@gmail.com>
Date: Wed, 13 Jun 2007 10:51:04 +0200

> Please let me involve Sylvain in the discussion. As the main author of
> pylint and astng, he will provide better answers.

well logilab-astng is basically a big monkey patching of the compiler
package from the stdlib, so you can't get an astng representation from a
module with syntax errors in. However inference and most others
navigation methods (which are basically the value added by astng) are
"syntax error resilient" : if a dependency module (direct or indirect)
contains a syntax error, you don't get any exception, though since some
information is missing you can miss some results you'ld get if the
faulting module were parseable.



From: "Tal Einat" <talei...@gmail.com>
Date: Tue, 31 Jul 2007 10:33:33 +0300

Since astng already does some inference (which we definitely want!)
and is based on the standard Python AST compiler, it sounds like our
#1 candidate. I think we should give the code a serious once-over and
see how well it fits our requirements, and if it can be adapted to
better handle errors. Any volunteers?

Also, has anyone used astng for completion, calltips, or something
similar? Or the standard AST compiler, for that matter?



From: "Tal Einat" <talei...@gmail.com>
Date: Tue, 31 Jul 2007 10:40:11 +0300

How does PyPE parse code? Home-rolled, standard AST compiler, something else?

It seems to me we should try to come up with an algorithm for parsing,
before getting to the code. All of the details you mentioned -
noticing assignments, using base-class methods, etc. - could be better
defined and organized this way. Perhaps we could brainstorm on this in
a wiki?



From: "Tal Einat" <talei...@gmail.com>
Date: Tue, 31 Jul 2007 11:38:40 +0300

Sorry for being away for such a long time. I hope we can get this
conversation rolling again, and get started with the actual work.

I'll try to sum up what has been said so far, and how I see things.

== Top Priorities ==
* Can we implement a parser based on the standard Python AST compiler
(or astng)? For example, can syntax errors be handled well?
* Is importing reasonable security-wise? If not, can it be made secure?

== General issues ==
* Do we aim for just completion, or also calltips? Perhaps also other
meta-data, e.g. place defined, source code, ... (see IPython's '??')
* Dependencies - do we want to allow C-extensions, or are we going for
a Python-only solution? (IDLE would only use such a Python-only tool.)
It seems that we want to pre-process most of the data in the
background, so I don't see why we would want to do this in C for
efficiency reasons.

== Completion sources ==
1) Importing "external" modules
2) Importing/Parsing "local" modules
3) Parsing the current file
4) Using objects/modules from the shell (e.g. IDLE has both editor
windows and a Python shell)

== Importing ==
* Stani mentioned that importing is problematic from a security point
of view. What are the security issues? Are they really an issue for an
IDE? If so, perhaps we could overcome this by importing in some kind
of "sandbox"?
* What are the pros and cons of Importing vs. Parsing?
* If importing is always preferable to parsing unless there's a syntax
error, perhaps try to import and parse on failure?

== Parsing ==
* This is going to be the most complex method - I think we should have
a general idea of how this should work before starting an
implementation. I suggest hashing ideas out on a wiki, since there a
lot of details to consider.
* Can a parser based on the standard AST compiler (or astng) work? Is
there a way to deal with errors? (HIGH PRIORITY!)
* There are other existing, open-source implementations out there -
WingIDE, PyPE have been mentioned. Any others? We should collect these
so we can use the code for learning, and perhaps direct use (if
possible license-wise).

== Shell ==
This is relatively straight-forward - just use dir(). This should be
optional, for use by IDEs which have a shell (support multiple
shells?).

Some known issues from IDLE and PyCrust:
* Handle object proxies such as RPC proxies (e.g. RPyC)
* Handle ZODB "ghost" objects
* Watch out for circular references
* Watch out for objects with special __getattr__/__hasattr__
implementations (for example xmlrpc, soap)

== Persistence ==
* Stani mentioned a 'database'. I feel Sqlite should be at most
optional, to reduce dependencies.
* Do we really want to have the data persistent (between IDE
sessiosns)? If so, we need to support simultaneous instances of the
IDE so they don't corrupt the data. Any other issues? (I have a
feeling this would better be left for later stages of development.)



From: "Tal Einat" <talei...@gmail.com>
Date: Tue, 31 Jul 2007 12:22:59 +0300

One more note: We should distinguish between completion in an editor
and completion in a shell. The conversation up until now has focused
on editors, which is reasonable since that is the problematic scene. I
think a generic Python completion library should support completion in
both contexts, especially if it uses can use a shell's namespace for
completion in the editor.



From: "Ali Afshar" <aafs...@gmail.com>
Date: Tue, 31 Jul 2007 11:20:19 +0100

I have just implemented a completion mockup using Rope (which is a
refactoring library). It works quite nicely, and definitely worth a
look.

http://rope.sourceforge.net/

It even achieves this kind of completion:

class Banana(object):
    def do_something(self):
         return

def foo():
    return [Banana(), Banana()]

foo()[0].<complete> includes do_something

Which seems pretty impressive to me.



From: "Tal Einat" <talei...@gmail.com>
Date: Tue, 31 Jul 2007 20:12:50 +0300

Wow, Rope does look very impressive! A quick look at the code tells me
that a lot of work has been invested in it.

So we have one existing Python-only solution. We should evaluate it -
see what it can and can't do, and perhaps take a look at the overall
design.

I'm CC-ing Rope's developer, Ali. Hopefully Ali can help us quickly
understand Rope's code analysis capabilities.

Ali, could you elaborate a bit on what kinds of completion Rope can
do, and the methods it uses? We would especially like to know how your
static and dynamic inference work, what they can accomplish, and what
their limitations are.



From: "Ali Afshar" <aafs...@gmail.com>
Date: Tue, 31 Jul 2007 19:45:15 +0100

> Ali, could you elaborate a bit on what kinds of completion Rope can
> do, and the methods it uses? We would especially like to know how your
> static and dynamic inference work, what they can accomplish, and what
> their limitations are.

Well, I haven't really looked at the code. But I can tell you this:

from rope.ide.codeassist import PythonCodeAssist
from rope.base.project import Project
for compl in PythonCodeAssist(Project(package_root)).assist(buffer,
offset).completions:
    print compl

And that is as far as I really got. I expect to get a better look at
it later in the week though...


From: "Josiah Carlson" <josiah.carl...@gmail.com>
Date: Wed, 1 Aug 2007 00:26:14 -0700

> How does PyPE parse code? Home-rolled, standard AST compiler, something else?

The compiler for syntactically correct Python, a line-based compiler
for broken Python.  TO generate a method list for self.methods, using
the current line number, I discover the enclosing class, check the
listing of methods for that class (generated by the compiler or
line-based parsers), and return a valid list for the specified prefix.
 It doesn't walk the inheritance tree, it doesn't do imports, etc.

> It seems to me we should try to come up with an algorithm for parsing,
> before getting to the code. All of the details you mentioned -
> noticing assignments, using base-class methods, etc. - could be better
> defined and organized this way. Perhaps we could brainstorm on this in
> a wiki?

A wiki would be fine, the one for this mailing list would likely be
best (if it is still up and working).  Then again, Rope looks quite
nifty.  I may have to borrow some of that source ;)


Discussion subject changed to "Fwd: Python code completion module" by Tal Einat

From: Ali Gholami Rudi <aligr...@gmail.com>
Date: Aug 1, 2007 5:50 PM

First of all I should note that rope's main goal was being a
refactoring tool and a refactoring tool needs to know a lot about
python modules.  `rope.base` package provides information about python
modules.

Actually what ropeide provides as auto-completion is defined in
`rope.ide.codeassist` module.  This module almost does nothing but use
`rope.base`.  Since `rope.ide` package is not included in the rope
library (which has been separated from ropeide since 0.6m4) it lacks
good documentation and the API might not be easy to use (most of it is
written in the first months of rope's birth).

> ..., could you elaborate a bit on what kinds of completion Rope can
> do, ...

I don't know what to say here.  Well, actually it tries to use the
source code as much as possible and infer things from it.  So I can
say that it can complete any obvious thing that can be inferred by a
human.  Like this is the first parameter of a method and after dots
its attributes can appear or these modules are imported so their names
and contents are available or this is an instance of some known type
and we know its attributes and ... .  Try ropeide (it uses emacs-like
keybinding, C-/ for completion; see ~/.rope if you want to change
that); it completes common cases (and sometimes completes things you
don't expect it to!).

> ..., and the methods it uses?

Rope analyzes python source code and AST.  Rope used to use the
`compiler` module till 0.5 and now it uses `_ast` module.

> We would especially like to know how your
> static and dynamic inference work, what they can accomplish

There are a few examples in docs/overview.txt.  Unit-test modules like
`ropetest.base.objectinfertest` and `advanced_oi_test` might help,
too.  Also have a look at `rope.base.oi.__init__` pydoc for an
overview of how they work; (I'm afraid it is a bit out of date and
carelessly written.)  The idea behind rope's object inference is to
guess what references (names in source-code) hold.  They collect
information about code when they can and use them later.

>..., and what their limitations are.

Many things in rope are approximations that might be exact if some
conditions hold.  For instance rope might assume that every normal
reference in module scope holds only one kind of object.  Apart from
these assumptions both SOI and DOI have their own disadvantages; For
instance SOI fails when dynamic code is evaluated while DOI does not.
Or DOI is slower than SOI.  (Well, after recent enhancements to rope's
SOI I rarely use DOI).

I tried to answer as short as possible.  If there are questions on
specific parts of rope, I'll be happy to answer.

By the way, I tried to reply this mail to the group, but it seems that
your group requires subscription for posting, so I've sent it to you,
instead.
#@nonl
#@-node:ekr.20080110082845:pyxides: code completion
#@+node:ekr.20060927173836.1:Make calltips and autocompleter 'stateless'
@nocolor

Disabled these binding:

auto-complete-force = None # This command needs work before it is useful. Ctrl-period
show-calltips-force = None # This command needs work before it is useful. Alt-parenleft

The problem is that autocompletion depends on state: self.leadinWord,
prevObjects, etc. Thus, it's not presently possible to start the proces
anywhere. Similar remarks apply to calltips, which relies on autocompleter
state.

This is a complex problem, and not very serious now that there is an easy way of
toggling autocompleter and calltips on and off.

@color
#@nonl
#@-node:ekr.20060927173836.1:Make calltips and autocompleter 'stateless'
#@+node:ekr.20071106083149:Recent post
@killcolor

In general, autocompletion is a tricky problem. Consider:

- There may be no 'clean' version of the source code that you want to
auto-complete: you may be creating a new node, or a new file, and the source
code, being incomplete, will not parse correctly.

- Except in special circumstances, there is no 'real' object corresponding to s,
so there is no way to use Python's inspect module on s. Modules are an
exception: the autocompleter can handle existing modules fairly well. Try "os."
or "os.path." for example.


It might be possible to generalize c.k.defineObjectDict so that the user
could specify autocompleter conventions, say in an @autocompleter node in an
@settings tree.
#@nonl
#@-node:ekr.20071106083149:Recent post
#@-node:ekr.20071211093011:4.6  autocompletion
#@+node:ekr.20071211091355:Other
#@+node:ekr.20071110071126.2:Better isearch
#@-node:ekr.20071110071126.2:Better isearch
#@+node:ekr.20071128094234:Finish scolorizer
#@+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.20071128094234:Finish scolorizer
#@+node:ekr.20071001052501:Versioning for nodes
@nocolor

One feature I have not seen in SCS system is something which might be called
"history compression": I might be interested in having both version 5 and 6
in my source tree, when the current version is 7, but I am not really interested
in the 2000 steps which transformed 5 into 6 (just suggested this feature to
the bazaar people). This happens actually quite often to me, since I use the
SCS as a back-up system, saving many (uninteresting) intermediate steps while
implementing a new feature.
#@nonl
#@-node:ekr.20071001052501:Versioning for nodes
#@+node:ekr.20071021125124:Revise plugins manager plugin
Remove all references to pluginsManager.txt.
#@nonl
#@-node:ekr.20071021125124:Revise plugins manager plugin
#@+node:ekr.20061127170002.1:Allow multiple lines on the button bar
@nocolor

http://sourceforge.net/forum/message.php?msg_id=3993176
By: rich_ries

How can the button bar be enlarged to take a second (or third!) row of icons? Or maybe the Scripting buttons can have
their own section, like the NodeBar plugin did/does?

add-new-icon-row command?
#@nonl
#@-node:ekr.20061127170002.1:Allow multiple lines on the button bar
#@+node:ekr.20070220082124:Make sure Leo can generate Latex
#@-node:ekr.20070220082124:Make sure Leo can generate Latex
#@+node:ekr.20070307082503:Make Leo an IPython notebook
#@+node:ekr.20071128071822:Do ipython demo
@nocolor

> The simple Python prompt is interesting in its own right and maybe an established use of it within Leo would act as a demonstration. The advancement to IPython would be obvious once there was an attractive integration of the normal prompt.

Good point.

> It seems that many of the user stories you responded to on the wiki could be done with the simple Python prompt integration(somehow using the shell prompt instead of how you might have explained current execute script in your answers). So, of course, the Leo integration and demonstration of a simple Python prompt would assist these users - but require giving up IPython convenience in those situations.

Exactly. 

> But then there would be more of a dialog on how to get to IPython since the integration of the shell - in terms of Leo - is a then a concrete model.

Yes, it would be feasible to do a 'toy' implementation using python's cmd and readline modules. Presumably, this would sidestep all the difficult implementation issues that make ipython valuable :-)

So this is all good.  We have a start of a user-level design, and a way of turning that design into working code *relatively* easily.

Originally, I was thinking that the demo would be a set of diagrams illustrating how to integrate ipython into Leo.  The original post on this thread might claim to be such a 'diagram'.  Here are the bones of the design as described in the first post:

1. Put the interactive shell in a Shell tab in the log pane.

2. If the present node is an @history node, all of the work on the interactive prompt is captured (appended) in that body pane. Such nodes might be grouped into chapters, or by @shell-histories trees.

3. Buttons or commands would play back captured sessions one step at a time.

4. The open-shell-tab command could (under control of user options) rearrange Leo's panes to make them easier to use for entering shell commands.  The close-shell-tab command would undo whatever rearrangement took place.

5. Leo users would see leo outlines as conveniently arranged saved sessions which are available for review and replay. 

I think this is a pretty decent demo!

My original idea was to use the minibuffer for input and use the 'shell' tab (or body pane for @shell nodes) only for output. But this would make it impossible to play back captured sessions.

Actually, it may be easiest to do the full simulation in the Shell tab.  The Shell tab must have bindings that emulate the bindings (say) in idle's main console window.  I strongly suspect that there are more details than meet the eye.  Still, python's cmd and readline modules are a good start.  

In short, it looks like we have a clear roadmap for an ipython demo in Leo: put an 'idle-like' console window in a Shell tab in Leo's log pane.

I'll create commands to open and close the Shell tab soon--maybe today.  Is anyone interested in actually creating an 'idle-like' command interpreter in the Shell pane?
#@nonl
#@-node:ekr.20071128071822:Do ipython demo
#@+node:ekr.20071127103206:Post from BillP
@nocolor

http://sourceforge.net/forum/message.php?msg_id=4640591
By: billp9619

It seems to me that the python interactive prompt is such a prominent feature
of the language that it should be integrated better into leo.

If the shell was there as a standard tab (similar to "Find" panel) then I think
this would make Leo more enticing as a teaching tool.

I could envision an arrangement such that if the selected outline node is of
a certain kind - maybe @interactive - then all of the work on the interactive
prompt is captured(appended) in that body pane. Another button or command would
play back the captured session one step at a time.

The window action "Toggle Split Direction" places the Log pane at the bottom
where the body is normally. So when using the prompt it would be below the outline
- and the body pane hidden by dragging left edge of window flush to the right.
Such a user would see leo outlines as conveniently arranged saved sessions which
are available for replay(and reviewable history as saved in corresponding body
texts).
#@nonl
#@-node:ekr.20071127103206:Post from BillP
#@+node:ekr.20071117082144:@url http://projects.scipy.org/ipython/ipython/wiki/NoteBook/ProjectDescription
#@-node:ekr.20071117082144:@url http://projects.scipy.org/ipython/ipython/wiki/NoteBook/ProjectDescription
#@+node:ekr.20071118051101:@url http://leo.zwiki.org/ResponsesToIPythonsUserStories
#@-node:ekr.20071118051101:@url http://leo.zwiki.org/ResponsesToIPythonsUserStories
#@+node:ekr.20060603090445.7:Ipython stuff
#@+node:ekr.20060603085719.1:Use IPython autocompleter?
#@-node:ekr.20060603085719.1:Use IPython autocompleter?
#@+node:ekr.20060603090445.8:Add newline history (like Iptyon history?)
#@-node:ekr.20060603090445.8:Add newline history (like Iptyon history?)
#@+node:ekr.20060601150940.1:Investigate Ipython support in Scripting plugin
#@-node:ekr.20060601150940.1:Investigate Ipython support in Scripting plugin
#@+node:ekr.20060603090445.9:Script button that saves environment between runs
This would allow IPython-like operation.
#@nonl
#@-node:ekr.20060603090445.9:Script button that saves environment between runs
#@-node:ekr.20060603090445.7:Ipython stuff
#@+node:ekr.20070521100324.1:Using Leo as a notebook
@nocolor
https://sourceforge.net/forum/message.php?msg_id=4314394
By: duanekaufman

I am starting a kinda 'stream-of-thought' document, one in which I can put down
a paragraph of thought (perhaps in a pretty fashion, using ReST markup), develop
some code, to do some calculations, run the code and display the results...rinse,
repeat.

The document, in pseudo-form, would look like:

<Development of thought #1>
<Code to calculate something>
<Results from code execution>
<Discussion of results>

Each of the above would probably be a node, and I have already put together
things that look like the above, all except for the <results> part. I can run
the code, but how can I get the output automatically in my node? I can cut-and-paste
from the underlying command window (I run under Linux and Windows XP), but I
would like o automate it as much as possible.

Does anyone have any ideas on how to accomodate this workflow?

------ Reply

A most interesting and important question--with such a capability one could make Leo into a full-fledged engineering notebook, e.g., as envisaged by the IPython Notebook project: 

http://projects.scipy.org/ipython/ipython/wiki/NbshellManual 

I can think of several possible approaches: 

1. Create a custom version of the execute script command that would put script output in a particular node. Perhaps the headline of the node could be an argument to the enhanced command. 

2. Have your script write to some 'standard place', and create a command to put that script in the presently selected node. Something like insert-script-output. 

3. Create a script button that executes the script in the presently selected node and puts the result in the node *following* the selected node. You can then either move the node by hand, or use another script button to move the node to a 'standard place', say the last child of the <<results>> node. 

As I write this I think the script-button approach is likely to be the simplest, most flexible and most powerful. I'll look into this myself soon... 

Edward
#@nonl
#@-node:ekr.20070521100324.1:Using Leo as a notebook
#@+node:ekr.20071116093143.1:Execute script using Ipython
# It should be possible to use Leo as an IPython notebook *now*.
#@nonl
#@+node:ekr.20071118114756:IPython notes
@nocolor

shell.start has a user_ns keyword.  It might be interesting to set this to Leo global dict.

shell_class is IPython.iplib.InteractiveShell

InteractiveShell creates self.logger and self.jobs

self.compile is codeop.CommandCompiler

__builtin__.quit = Quitter('quit')
__builtin__.exit = Quitter('exit')
self.meta = Struct()

Show Me videos:

discusses all the magic commands.  Some are cool.
#@nonl
#@-node:ekr.20071118114756:IPython notes
#@-node:ekr.20071116093143.1:Execute script using Ipython
#@-node:ekr.20070307082503:Make Leo an IPython notebook
#@+node:ekr.20070703111455.3:Minor
# some of these can wait till much later.
#@nonl
#@+node:ekr.20070521105645:Improve api docs with epidoc?
@nocolor
http://sourceforge.net/forum/message.php?msg_id=4319363
By: ktenney

I think there is room for improvement in documenting Leo's
API, making it easier to write these kind of scripts.
I'm not sure of the best way to do that.

Epydoc seems to be the most active project in this realm.
http://epydoc.sourceforge.net/epydoc.html
#@-node:ekr.20070521105645:Improve api docs with epidoc?
#@+node:ekr.20061116054917.6:Remove blanks in calltips
#@-node:ekr.20061116054917.6:Remove blanks in calltips
#@+node:ekr.20061002093442:Add opml support to new,open, save commands
#@+node:ekr.20031218072017.2820:top level (file menu)
#@+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.
        t = leoNodes.tnode()
        v = leoNodes.vnode(t)
        p = leoNodes.position(v,[])
        v.initHeadString("NewHeadline")
        v.moveToRoot(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.2823:openWith and allies
def openWith(self,event=None,data=None):

    """This routine handles the items in the Open With... menu.

    These items can only be created by createOpenWithMenuFromTable().
    Typically this would be done from the "open2" hook.

    New in 4.3: The "os.spawnv" now works. You may specify arguments to spawnv
    using a list, e.g.:

    openWith("os.spawnv", ["c:/prog.exe","--parm1","frog","--switch2"], None)
    """

    c = self ; p = c.currentPosition()
    n = data and len(data) or 0
    if n != 3:
        g.trace('bad data, length must be 3, got %d' % n)
        return
    try:
        openType,arg,ext=data
        if not g.doHook("openwith1",c=c,p=p,v=p.v,openType=openType,arg=arg,ext=ext):
            g.enableIdleTimeHook(idleTimeDelay=100)
            << set ext based on the present language >>
            << create or reopen temp file, testing for conflicting changes >>
            << execute a command to open path in external editor >>
        g.doHook("openwith2",c=c,p=p,v=p.v,openType=openType,arg=arg,ext=ext)
    except Exception:
        g.es("unexpected exception in c.openWith")
        g.es_exception()

    return "break"
#@+node:ekr.20031218072017.2824:<< set ext based on the present language >>
if not ext:
    theDict = g.scanDirectives(c)
    language = theDict.get("language")
    ext = g.app.language_extension_dict.get(language)
    # print language,ext
    if ext == None:
        ext = "txt"

if ext[0] != ".":
    ext = "."+ext

# print "ext",ext
#@-node:ekr.20031218072017.2824:<< set ext based on the present language >>
#@+node:ekr.20031218072017.2825:<< create or reopen temp file, testing for conflicting changes >>
theDict = None ; path = None
<< set dict and path if a temp file already refers to p.v.t >>
if path:
    << create or recreate temp file as needed >>
else:
    path = c.createOpenWithTempFile(p,ext)

if not path:
    return # An error has occured.
#@+node:ekr.20031218072017.2826:<<set dict and path if a temp file already refers to p.v.t >>
searchPath = c.openWithTempFilePath(p,ext)

if g.os_path_exists(searchPath):
    for theDict in g.app.openWithFiles:
        if p.v == theDict.get('v') and searchPath == theDict.get("path"):
            path = searchPath
            break
#@-node:ekr.20031218072017.2826:<<set dict and path if a temp file already refers to p.v.t >>
#@+node:ekr.20031218072017.2827:<< create or recreate temp file as needed >>
@ We test for changes in both p and the temp file:

- If only p's body text has changed, we recreate the temp file.
- If only the temp file has changed, do nothing here.
- If both have changed we must prompt the user to see which code to use.
@c

encoding = theDict.get("encoding")
old_body = theDict.get("body")
new_body = p.bodyString()
new_body = g.toEncodedString(new_body,encoding,reportErrors=True)

old_time = theDict.get("time")
try:
    new_time = g.os_path_getmtime(path)
except:
    new_time = None

body_changed = old_body != new_body
temp_changed = old_time != new_time

if body_changed and temp_changed:
    << Raise dialog about conflict and set result >>
    if result == "cancel": return
    rewrite = result == "outline"
else:
    rewrite = body_changed

if rewrite:
    path = c.createOpenWithTempFile(p,ext)
else:
    g.es("reopening:",g.shortFileName(path),color="blue")
#@+node:ekr.20031218072017.2828:<< Raise dialog about conflict and set result >>
message = (
    "Conflicting changes in outline and temp file\n\n" +
    "Do you want to use the code in the outline or the temp file?\n\n")

result = g.app.gui.runAskYesNoCancelDialog(c,
    "Conflict!", message,
    yesMessage = "Outline",
    noMessage = "File",
    defaultButton = "Cancel")
#@-node:ekr.20031218072017.2828:<< Raise dialog about conflict and set result >>
#@-node:ekr.20031218072017.2827:<< create or recreate temp file as needed >>
#@-node:ekr.20031218072017.2825:<< create or reopen temp file, testing for conflicting changes >>
#@+node:ekr.20031218072017.2829:<< execute a command to open path in external editor >>
try:
    if arg == None: arg = ""
    shortPath = path # g.shortFileName(path)
    if openType == "os.system":
        if 1:
            # This works, _provided_ that arg does not contain blanks.  Sheesh.
            command = 'os.system(%s)' % (arg+shortPath)
            os.system(arg+shortPath)
        else:
            # XP does not like this format!
            command = 'os.system("%s" "%s")' % (arg,shortPath)
            os.system('"%s" "%s"' % (arg,shortPath))
    elif openType == "os.startfile":
        command = "os.startfile(%s)" % (arg+shortPath)
        os.startfile(arg+path)
    elif openType == "exec":
        command = "exec(%s)" % (arg+shortPath)
        exec arg+path in {}
    elif openType == "os.spawnl":
        filename = g.os_path_basename(arg)
        command = "os.spawnl(%s,%s,%s)" % (arg,filename,path)
        apply(os.spawnl,(os.P_NOWAIT,arg,filename,path))
    elif openType == "os.spawnv":
        filename = os.path.basename(arg[0]) 
        vtuple = arg[1:]
        vtuple.insert(0, filename)
            # add the name of the program as the first argument.
            # Change suggested by Jim Sizelove.
        vtuple.append(path)
        command = "os.spawnv(%s,%s)" % (arg[0],repr(vtuple))
        apply(os.spawnv,(os.P_NOWAIT,arg[0],vtuple))
    # This clause by Jim Sizelove.
    elif openType == "subprocess.Popen":
        if isinstance(arg, basestring):
            vtuple = arg + " " + path
        elif isinstance(arg, (list, tuple)):
            vtuple = arg[:]
            vtuple.append(path)
        command = "subprocess.Popen(%s)" % repr(vtuple)
        if subprocess:
            subprocess.Popen(vtuple)
        else:
            g.trace('Can not import subprocess.  Skipping: "%s"' % command)
    else:
        command="bad command:"+str(openType)
        g.trace(command)
except Exception:
    g.es("exception executing:",command)
    g.es_exception()
#@-node:ekr.20031218072017.2829:<< execute a command to open path in external editor >>
#@+node:ekr.20031218072017.2830:createOpenWithTempFile
def createOpenWithTempFile (self,p,ext):

    c = self
    path = c.openWithTempFilePath(p,ext)
    try:
        if g.os_path_exists(path):
            g.es("recreating:  ",g.shortFileName(path),color="red")
        else:
            g.es("creating:  ",g.shortFileName(path),color="blue")
        theFile = open(path,"w")
        # Convert s to whatever encoding is in effect.
        s = p.bodyString()
        theDict = g.scanDirectives(c,p=p)
        encoding = theDict.get("encoding",None)
        if encoding == None:
            encoding = c.config.default_derived_file_encoding
        s = g.toEncodedString(s,encoding,reportErrors=True) 
        theFile.write(s)
        theFile.flush()
        theFile.close()
        try:    time = g.os_path_getmtime(path)
        except: time = None
        # g.es("time: " + str(time))
        # New in 4.3: theDict now contains both 'p' and 'v' entries, of the expected type.
        theDict = {
            "body":s, "c":c, "encoding":encoding,
            "f":theFile, "path":path, "time":time,
            "p":p, "v":p.v }
        << remove previous entry from app.openWithFiles if it exists >>
        g.app.openWithFiles.append(theDict)
        return path
    except:
        if theFile:
            theFile.close()
        theFile = None
        g.es("exception creating temp file",color="red")
        g.es_exception()
        return None
#@+node:ekr.20031218072017.2831:<< remove previous entry from app.openWithFiles if it exists >>
for d in g.app.openWithFiles[:]:
    p2 = d.get("p")
    if p.v.t == p2.v.t:
        # print "removing previous entry in g.app.openWithFiles for",p.headString()
        g.app.openWithFiles.remove(d)
#@-node:ekr.20031218072017.2831:<< remove previous entry from app.openWithFiles if it exists >>
#@-node:ekr.20031218072017.2830:createOpenWithTempFile
#@+node:ekr.20031218072017.2832:c.openWithTempFilePath
def openWithTempFilePath (self,p,ext):

    """Return the path to the temp file corresponding to p and ext."""

    name = "LeoTemp_%s_%s%s" % (
        str(id(p.v.t)),
        g.sanitize_filename(p.headString()),
        ext)

    name = g.toUnicode(name,g.app.tkEncoding)

    td = g.os_path_abspath(tempfile.gettempdir())

    path = g.os_path_join(td,name)

    return path
#@-node:ekr.20031218072017.2832:c.openWithTempFilePath
#@-node:ekr.20031218072017.2823:openWith and allies
#@+node:ekr.20031218072017.2833:close
def close (self,event=None):

    '''Close the Leo window, prompting to save it if it has been changed.'''

    g.app.closeLeoWindow(self.frame)
#@-node:ekr.20031218072017.2833:close
#@+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.widgetWantsFocus(w)
#@nonl
#@-node:ekr.20031218072017.2834:save (commands)
#@+node:ekr.20031218072017.2835:saveAs
def saveAs (self,event=None):

    '''Save a Leo outline to a file with a new filename.'''

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

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

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

        fileName = ''.join(c.k.givenArgs) or g.app.gui.runSaveFileDialog(
            initialfile = c.mFileName,
            title="Save As",
            filetypes=[("Leo files", "*.leo")],
            defaultextension=".leo")
        c.bringToFront()

        if fileName:
            g.trace(fileName)
            # 7/2/02: 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.
            # Calls c.setChanged(False) if no error.
            c.fileCommands.saveAs(c.mFileName)
            c.updateRecentFiles(c.mFileName)
    finally:
        c.endUpdate()
        c.widgetWantsFocus(w)
#@-node:ekr.20031218072017.2835:saveAs
#@+node:ekr.20070413045221:saveAsUnzipped & saveAsZipped
def saveAsUnzipped (self,event=None):

    '''Save a Leo outline to a file with a new filename,
    ensuring that the file is not compressed.'''
    self.saveAsZippedHelper(False)

def saveAsZipped (self,event=None):

    '''Save a Leo outline to a file with a new filename,
    ensuring that the file is compressed.'''
    self.saveAsZippedHelper(True)

def saveAsZippedHelper (self,isZipped):

    c = self
    oldZipped = c.isZipped
    c.isZipped = isZipped
    try:
        c.saveAs()
    finally:
        c.isZipped = oldZipped
#@-node:ekr.20070413045221:saveAsUnzipped & saveAsZipped
#@+node:ekr.20031218072017.2836:saveTo
def saveTo (self,event=None):

    '''Save a Leo outline to a file, leaving the file associated with the Leo outline unchanged.'''

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

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

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

        # set local fileName, _not_ c.mFileName
        fileName = ''.join(c.k.givenArgs) or g.app.gui.runSaveFileDialog(
            initialfile = c.mFileName,
            title="Save To",
            filetypes=[("Leo files", "*.leo")],
            defaultextension=".leo")
        c.bringToFront()

        if fileName:
            fileName = g.ensure_extension(fileName, ".leo")
            c.fileCommands.saveTo(fileName)
            c.updateRecentFiles(fileName)

    finally:
        c.endUpdate()
        c.widgetWantsFocus(w)
#@-node:ekr.20031218072017.2836:saveTo
#@+node:ekr.20031218072017.2837:revert
def revert (self,event=None):

    '''Revert the contents of a Leo outline to last saved contents.'''

    c = self

    # Make sure the user wants to Revert.
    if not c.mFileName:
        return

    reply = g.app.gui.runAskYesNoDialog(c,"Revert",
        "Revert to previous version of " + c.mFileName + "?")
    c.bringToFront()

    if reply=="no":
        return

    # Kludge: rename this frame so openWithFileName won't think it is open.
    fileName = c.mFileName ; c.mFileName = ""

    # Create a new frame before deleting this frame.
    ok, frame = g.openWithFileName(fileName,c)
    if ok:
        frame.deiconify()
        g.app.destroyWindow(c.frame)
    else:
        c.mFileName = fileName
#@-node:ekr.20031218072017.2837:revert
#@-node:ekr.20031218072017.2820:top level (file menu)
#@-node:ekr.20061002093442:Add opml support to new,open, save commands
#@+node:ekr.20071004120359.2:Do expand-region-abbrevs from
See: regionalExpandAbbrev.
#@nonl
#@-node:ekr.20071004120359.2:Do expand-region-abbrevs from
#@+node:ekr.20070605114358:Improve how recent files works?
@nocolor

http://sourceforge.net/forum/message.php?msg_id=4347116
By: terry_n_brown

This is just a nit I guess, but I move between two machines all the time, and
Leo keeps changing the paths I open from /home/tbrown/project/project.leo to
/media/hda8/project/project.leo, so that when I switch to the other machine,
where /home/tbrown/project/project.leo is actually /media/hdb2/project/project.leo,
the recent files link is wrong.

If it would be trivial for Leo to not do this, I'd like that feature.  If it's
a big deal, then not to worry, I can always navigate to the file of course.
#@-node:ekr.20070605114358:Improve how recent files works?
#@+node:ekr.20070624135822:Templates for common code fragments
#@-node:ekr.20070624135822:Templates for common code fragments
#@-node:ekr.20070703111455.3:Minor
#@-node:ekr.20071211091355:Other
#@+node:ekr.20060306194040:Leo Video
@nocolor
http://sourceforge.net/forum/message.php?msg_id=3615177
By: ktenney

High on my ToLearn list is vnc2swf
http://www.unixuser.org/~euske/vnc2swf/


http://sourceforge.net/forum/message.php?msg_id=3615278
By: James

A lot of people are now using Wink for demonstrations
(http://www.debugmode.com/wink/), it's is free and seems to work well.

Check out http://murl.se/11332
At the bottom they talk about tools and techniques.
http://showmedo.com seems like it would be a good
place to host vids also.

I've listened/watched a fair number of things like this;
my recomendation is to get a good microphone and
pre-amp to record your voice, and prepare the audio
track carefully. It is so aggravating when
it's hard to discern the words being spoken.

Thanks,
Kent
#@nonl
#@+node:ekr.20060531134434:Tutorials
http://sourceforge.net/forum/message.php?msg_id=3758271

From: Rich

Tutorials would be great. I use Liberty BASIC, (http://libertybasic.conforums.com)
and it has a very good tutorial -- leads the beginner by the hand through much
of the language. Also the help file has working code snippets
to cut-n-paste-n-play-with. I'd like to see something like:

[Buttons]
...[What are Buttons good for?]
...[How do I make my own buttons?]
......[Some commands you can use with buttons]
......[Where to find button commands]
#@nonl
#@-node:ekr.20060531134434:Tutorials
#@+node:ekr.20060531134434.1:Screencasts
@nocolor
http://sourceforge.net/forum/message.php?msg_id=3758303
By: ktenney

My sense is that documentation/screencasts has the
greatest potential for expanding Leo's mindshare.

I really like those produced by the good folks
at Dabo;
http://leafe.com/screencasts/
http://leafe.com/screencasts/populategrid.html

The TurboGears people have taken this to the extreme;
http://www.turbogears.org/ultimate.html

Leo is different enough that it warrants a 
demonstration of it's advantages.

-------------------

https://sourceforge.net/forum/message.php?msg_id=4396251
By: ktenney

2 good screencasts on making screencasts;

http://murl.se/26296
#@-node:ekr.20060531134434.1:Screencasts
#@+node:ekr.20060829103523:Render Leo slideshows
@nocolor

http://sourceforge.net/forum/message.php?msg_id=3889246
By: terry_n_brown

Three packages that might be candidates for "rendering" slides authored in Leo:

MagicPoint: http://member.wide.ad.jp/wg/mgp/

  uses a text file format that leo could produce

Slidy: http://www.w3.org/Talks/Tools/Slidy/

  uses XHTML / canned Java script

S5: http://meyerweb.com/eric/tools/s5/

  Similar to Slidy I think, haven't looked at it
#@nonl
#@-node:ekr.20060829103523:Render Leo slideshows
#@-node:ekr.20060306194040:Leo Video
#@+node:ekr.20071211093011.1:Other guis
#@+node:ekr.20071030191227:Emacs/Pymacs gui
#@-node:ekr.20071030191227:Emacs/Pymacs gui
#@+node:ekr.20071117150547:pyGtk
Wing IDE uses this: it looks great.
#@nonl
#@+node:ekr.20080112145409.479:class leoKeyEvent (gtkGui) (to do)
class leoKeyEvent:

    '''A gui-independent wrapper for gui events.'''

    def __init__ (self,event,c):

        # g.trace('leoKeyEvent(gtkGui)')
        self.actualEvent = event
        self.c      = c # Required to access c.k tables.
        self.char   = hasattr(event,'char') and event.char or ''
        self.keysym = hasattr(event,'keysym') and event.keysym or ''
        self.w      = hasattr(event,'widget') and event.widget or None
        self.x      = hasattr(event,'x') and event.x or 0
        self.y      = hasattr(event,'y') and event.y or 0
        # Support for fastGotoNode plugin
        self.x_root = hasattr(event,'x_root') and event.x_root or 0
        self.y_root = hasattr(event,'y_root') and event.y_root or 0

        if self.keysym and c.k:
            # Translate keysyms for ascii characters to the character itself.
            self.keysym = c.k.guiBindNamesInverseDict.get(self.keysym,self.keysym)

        self.widget = self.w

    def __repr__ (self):

        return 'gtkGui.leoKeyEvent: char: %s, keysym: %s' % (repr(self.char),repr(self.keysym))
#@nonl
#@-node:ekr.20080112145409.479:class leoKeyEvent (gtkGui) (to do)
#@-node:ekr.20071117150547:pyGtk
#@+node:ekr.20070703111913:Swing gui
#@+node:ekr.20070930105601:Gui Swing classes
#@+node:ekr.20071001092721:@thin leoSwingDialog.py
@language python
@tabwidth -4
@pagewidth 80

import leoGlobals as g
import string

@others
#@+node:ekr.20071001092721.1: class leoSwingDialog
class leoSwingDialog:
    """The base class for all Leo swing dialogs"""
    @others
#@+node:ekr.20071001092721.2:__init__ (tkDialog)
def __init__(self,c,title="",resizeable=True,canClose=True,show=True):

    """Constructor for the leoSwingDialog class."""

    self.answer = None # Value returned from run()
    self.c = c # For use by delayed focus methods in c.frame.
    self.resizeable = resizeable
    self.title = title
    self.modal = None

    self.buttonsFrame = None # Frame to hold typical dialog buttons.
    self.defaultButtonCommand = None  # Command to call when user closes the window by clicking the close box.
    self.frame = None # The outermost frame.
    self.root = None # g.app.root
    self.showFlag = show
    self.top = None # The toplevel Tk widget.
    self.focus_widget = None # The widget to get the first focus.
    self.canClose = canClose
#@-node:ekr.20071001092721.2:__init__ (tkDialog)
#@+node:ekr.20071001092721.3:cancelButton, noButton, okButton, yesButton
def cancelButton(self):

    """Do default click action in cancel button."""

    self.answer="cancel"
    self.top.destroy()

def noButton(self):

    """Do default click action in no button."""

    self.answer="no"
    self.top.destroy()

def okButton(self):

    """Do default click action in ok button."""

    self.answer="ok"
    self.top.destroy()

def yesButton(self):

    """Do default click action in yes button."""

    self.answer="yes"
    self.top.destroy()
#@-node:ekr.20071001092721.3:cancelButton, noButton, okButton, yesButton
#@+node:ekr.20071001092721.4:center
def center(self):

    """Center any leoSwingDialog."""

    g.app.gui.center_dialog(self.top)
#@-node:ekr.20071001092721.4:center
#@+node:ekr.20071001092721.5:createButtons
def createButtons (self,buttons):

    """Create a row of buttons.

    buttons is a list of dictionaries containing the properties of each button."""

    assert(self.frame)
    self.buttonsFrame = f = Tk.Frame(self.top)
    f.pack(side="top",padx=30)

    # Buttons is a list of dictionaries, with an empty dictionary at the end if there is only one entry.
    buttonList = []
    for d in buttons:
        text = d.get("text","<missing button name>")
        isDefault = d.get("default",False)
        underline = d.get("underline",0)
        command = d.get("command",None)
        bd = g.choose(isDefault,4,2)

        b = Tk.Button(f,width=6,text=text,bd=bd,underline=underline,command=command)
        b.pack(side="left",padx=5,pady=10)
        buttonList.append(b)

        if isDefault and command:
            self.defaultButtonCommand = command

    return buttonList
#@-node:ekr.20071001092721.5:createButtons
#@+node:ekr.20071001092721.6:createMessageFrame
def createMessageFrame (self,message):

    """Create a frame containing a Tk.Label widget."""

    label = Tk.Label(self.frame,text=message)
    label.pack(pady=10)
#@-node:ekr.20071001092721.6:createMessageFrame
#@+node:ekr.20071001092721.7:createTopFrame
def createTopFrame(self):

    """Create the Tk.Toplevel widget for a leoSwingDialog."""

    if g.app.unitTesting: return

    self.root = g.app.root
    # g.trace("leoSwingDialog",'root',self.root)

    self.top = Tk.Toplevel(self.root)
    self.top.title(self.title)

    if not self.resizeable:
        self.top.resizable(0,0) # neither height or width is resizable.

    self.frame = Tk.Frame(self.top)
    self.frame.pack(side="top",expand=1,fill="both")

    if not self.canClose:
        self.top.protocol("WM_DELETE_WINDOW", self.onClose)

    # Do this at idle time.
    def attachIconCallback(top=self.top):
        g.app.gui.attachLeoIcon(top)

    ### self.top.after_idle(attachIconCallback)
#@-node:ekr.20071001092721.7:createTopFrame
#@+node:ekr.20071001092721.8:onClose
def onClose (self):

    """Disable all attempts to close this frame with the close box."""

    pass
#@-node:ekr.20071001092721.8:onClose
#@+node:ekr.20071001092721.9:run (tkDialog)
def run (self,modal):

    """Run a leoSwingDialog."""

    if g.app.unitTesting: return None

    c = self.c ; self.modal = modal

    self.center() # Do this after all packing complete.
    if self.showFlag:
        self.top.lift()
    else:
        self.top.withdraw()

    # Get all keystrokes.
    if self.modal:
        self.top.grab_set() # Make the dialog a modal dialog.

    if self.focus_widget == None:
        self.focus_widget = self.top

    c and c.widgetWantsFocusNow(self.focus_widget)

    self.root.wait_window(self.top)

    if self.modal:
        return self.answer
    else:
        return None
#@-node:ekr.20071001092721.9:run (tkDialog)
#@-node:ekr.20071001092721.1: class leoSwingDialog
#@+node:ekr.20071001092721.10:class swingAboutLeo
class swingAboutLeo (leoSwingDialog):

    """A class that creates the swing About Leo dialog."""

    @others
#@+node:ekr.20071001092721.11:swingAboutLeo.__init__
def __init__ (self,c,version,theCopyright,url,email):

    """Create a swing About Leo dialog."""

    leoSwingDialog.__init__(self,c,"About Leo",resizeable=True) # Initialize the base class.

    if g.app.unitTesting: return

    self.copyright = theCopyright
    self.email = email
    self.url = url
    self.version = version

    c.inCommand = False # Allow the app to close immediately.

    self.createTopFrame()
    self.createFrame()
#@-node:ekr.20071001092721.11:swingAboutLeo.__init__
#@+node:ekr.20071001092721.12:swingAboutLeo.createFrame
def createFrame (self):

    """Create the frame for an About Leo dialog."""

    if g.app.unitTesting: return

    frame = self.frame
    theCopyright = self.copyright ; email = self.email
    url = self.url ; version = self.version

    # Calculate the approximate height & width. (There are bugs in Tk here.)
    lines = string.split(theCopyright,'\n')
    height = len(lines) + 8 # Add lines for version,url,email,spacing.
    width = 0
    for line in lines:
        width = max(width,len(line))
    width = max(width,len(url))
    width += 10 # 9/9/02

    frame.pack(padx=6,pady=4)

    self.text = w = g.app.gui.plainTextWidget(
        frame,height=height,width=width,bd=0,bg=frame.cget("background"))
    w.pack(pady=10)

    try:
        bitmap_name = g.os_path_join(g.app.loadDir,"..","Icons","Leoapp.GIF") # 5/12/03
        image = Tk.PhotoImage(file=bitmap_name)
        w.image_create("1.0",image=image,padx=10)
    except Exception:
        pass # This can sometimes happen for mysterious reasons.

    w.insert("end",version) #,tag="version")
    w.tag_add('version','end-%dc' %(len(version)+1),'end-1c')
    w.insert("end",theCopyright) #,tag="copyright")
    w.tag_add('copyright','end-%dc' %(len(theCopyright)+1),'end-1c')
    w.insert("end",'\n')
    w.insert("end",url)
    w.tag_add('url','end-%dc' %(len(url)+1),'end-1c')
    w.insert("end",'\n')
    w.insert("end",email)
    w.tag_add('url','end-%dc' %(len(email)+1),'end-1c')

    w.tag_config("version",justify="center")
    w.tag_config("copyright",justify="center",spacing1="3")
    w.tag_config("url",underline=1,justify="center",spacing1="10")

    w.tag_bind("url","<Button-1>",self.onAboutLeoUrl)
    w.tag_bind("url","<Enter>",self.setArrowCursor)
    w.tag_bind("url","<Leave>",self.setDefaultCursor)

    w.tag_config("email",underline=1,justify="center",spacing1="10")
    w.tag_bind("email","<Button-1>",self.onAboutLeoEmail)
    w.tag_bind("email","<Enter>",self.setArrowCursor)
    w.tag_bind("email","<Leave>",self.setDefaultCursor)

    w.configure(state="disabled")
#@-node:ekr.20071001092721.12:swingAboutLeo.createFrame
#@+node:ekr.20071001092721.13:swingAboutLeo.onAboutLeoEmail
def onAboutLeoEmail(self,event=None):

    """Handle clicks in the email link in an About Leo dialog."""

    # __pychecker__ = '--no-argsused' # the event param must be present.

    try:
        import webbrowser
        webbrowser.open("mailto:" + self.email)
    except:
        g.es("not found: " + self.email)
#@-node:ekr.20071001092721.13:swingAboutLeo.onAboutLeoEmail
#@+node:ekr.20071001092721.14:swingAboutLeo.onAboutLeoUrl
def onAboutLeoUrl(self,event=None):

    """Handle clicks in the url link in an About Leo dialog."""

    # __pychecker__ = '--no-argsused' # the event param must be present.

    try:
        import webbrowser
        webbrowser.open(self.url)
    except:
        g.es("not found: " + self.url)
#@-node:ekr.20071001092721.14:swingAboutLeo.onAboutLeoUrl
#@+node:ekr.20071001092721.15:swingAboutLeo: setArrowCursor, setDefaultCursor
def setArrowCursor (self,event=None):

    """Set the cursor to an arrow in an About Leo dialog."""

    # __pychecker__ = '--no-argsused' # the event param must be present.

    self.text.configure(cursor="arrow")

def setDefaultCursor (self,event=None):

    """Set the cursor to the default cursor in an About Leo dialog."""

    # __pychecker__ = '--no-argsused' # the event param must be present.

    self.text.configure(cursor="xterm")
#@-node:ekr.20071001092721.15:swingAboutLeo: setArrowCursor, setDefaultCursor
#@-node:ekr.20071001092721.10:class swingAboutLeo
#@+node:ekr.20071001092721.16:class swingAskLeoID
class swingAskLeoID (leoSwingDialog):

    """A class that creates the swing About Leo dialog."""

    @others
#@+node:ekr.20071001092721.17:swingAskLeoID.__init__
def __init__(self,c=None):

    """Create the Leo Id dialog."""

    # Initialize the base class: prevent clicks in the close box from closing.
    leoSwingDialog.__init__(self,c,"Enter unique id",resizeable=False,canClose=False)

    if g.app.unitTesting: return

    self.id_entry = None
    self.answer = None

    self.createTopFrame()
    self.top.bind("<Key>", self.onKey)

    message = (
        "leoID.txt not found\n\n" +
        "Please enter an id that identifies you uniquely.\n" +
        "Your cvs login name is a good choice.\n\n" +
        "Your id must contain only letters and numbers\n" +
        "and must be at least 3 characters in length.")
    self.createFrame(message)
    self.focus_widget = self.id_entry

    buttons = {"text":"OK","command":self.onButton,"default":True}, # Singleton tuple.
    buttonList = self.createButtons(buttons)
    self.ok_button = buttonList[0]
    self.ok_button.configure(state="disabled")
#@-node:ekr.20071001092721.17:swingAskLeoID.__init__
#@+node:ekr.20071001092721.18:swingAskLeoID.createFrame
def createFrame(self,message):

    """Create the frame for the Leo Id dialog."""

    if g.app.unitTesting: return

    f = self.frame

    label = Tk.Label(f,text=message)
    label.pack(pady=10)

    self.id_entry = text = Tk.Entry(f,width=20)
    text.pack()
#@-node:ekr.20071001092721.18:swingAskLeoID.createFrame
#@+node:ekr.20071001092721.19:swingAskLeoID.onButton
def onButton(self):

    """Handle clicks in the Leo Id close button."""

    s = self.id_entry.get().strip()
    if len(s) < 3:  # Require at least 3 characters in an id.
        return

    self.answer = g.app.leoID = s

    self.top.destroy() # terminates wait_window
    self.top = None
#@-node:ekr.20071001092721.19:swingAskLeoID.onButton
#@+node:ekr.20071001092721.20:swingAskLeoID.onKey
def onKey(self,event):

    """Handle keystrokes in the Leo Id dialog."""

    << eliminate invalid characters >>
    << enable the ok button if there are 3 or more valid characters >>

    ch = event.char.lower()
    if ch in ('\n','\r'):
        self.onButton()
    return "break"
#@+node:ekr.20071001092721.21:<< eliminate invalid characters >>
e = self.id_entry
s = e.get().strip()
i = 0 ; ok = True
while i < len(s):
    ch = s[i]
    if not ch.isalnum():
        e.delete(str(i))
        s = e.get()
        ok = False
    else:
        i += 1
if not ok: return
#@-node:ekr.20071001092721.21:<< eliminate invalid characters >>
#@+node:ekr.20071001092721.22:<< enable the ok button if there are 3 or more valid characters >>
e = self.id_entry
b = self.ok_button

if len(e.get().strip()) >= 3:
    b.configure(state="normal")
else:
    b.configure(state="disabled")
#@-node:ekr.20071001092721.22:<< enable the ok button if there are 3 or more valid characters >>
#@-node:ekr.20071001092721.20:swingAskLeoID.onKey
#@-node:ekr.20071001092721.16:class swingAskLeoID
#@+node:ekr.20071001092721.23:class swingAskOk
class swingAskOk(leoSwingDialog):

    """A class that creates a swing dialog with a single OK button."""

    @others
#@+node:ekr.20071001092721.24:class swingAskOk.__init__
def __init__ (self,c,title,message=None,text="Ok",resizeable=False):

    """Create a dialog with one button"""

    leoSwingDialog.__init__(self,c,title,resizeable) # Initialize the base class.

    if g.app.unitTesting: return

    self.text = text
    self.createTopFrame()
    self.top.bind("<Key>", self.onKey)

    if message:
        self.createMessageFrame(message)

    buttons = {"text":text,"command":self.okButton,"default":True}, # Singleton tuple.
    self.createButtons(buttons)
#@-node:ekr.20071001092721.24:class swingAskOk.__init__
#@+node:ekr.20071001092721.25:class swingAskOk.onKey
def onKey(self,event):

    """Handle Key events in askOk dialogs."""

    ch = event.char.lower()

    if ch in (self.text[0].lower(),'\n','\r'):
        self.okButton()

    return "break"
#@-node:ekr.20071001092721.25:class swingAskOk.onKey
#@-node:ekr.20071001092721.23:class swingAskOk
#@+node:ekr.20071001092721.26:class swingAskOkCancelNumber
class  swingAskOkCancelNumber (leoSwingDialog):

    """Create and run a modal swing dialog to get a number."""

    @others
#@+node:ekr.20071001092721.27:swingAskOKCancelNumber.__init__
def __init__ (self,c,title,message):

    """Create a number dialog"""

    leoSwingDialog.__init__(self,c,title,resizeable=False) # Initialize the base class.

    if g.app.unitTesting: return

    self.answer = -1
    self.number_entry = None

    self.createTopFrame()
    self.top.bind("<Key>", self.onKey)

    self.createFrame(message)
    self.focus_widget = self.number_entry

    buttons = (
            {"text":"Ok",    "command":self.okButton,     "default":True},
            {"text":"Cancel","command":self.cancelButton} )
    buttonList = self.createButtons(buttons)
    self.ok_button = buttonList[0] # Override the default kind of Ok button.
#@-node:ekr.20071001092721.27:swingAskOKCancelNumber.__init__
#@+node:ekr.20071001092721.28:swingAskOKCancelNumber.createFrame
def createFrame (self,message):

    """Create the frame for a number dialog."""

    if g.app.unitTesting: return

    c = self.c

    lab = Tk.Label(self.frame,text=message)
    lab.pack(pady=10,side="left")

    self.number_entry = w = Tk.Entry(self.frame,width=20)
    w.pack(side="left")

    c.set_focus(w)
#@-node:ekr.20071001092721.28:swingAskOKCancelNumber.createFrame
#@+node:ekr.20071001092721.29:swingAskOKCancelNumber.okButton, cancelButton
def okButton(self):

    """Handle clicks in the ok button of a number dialog."""

    s = self.number_entry.get().strip()

    try:
        self.answer=int(s)
    except:
        self.answer=-1 # Cancel the operation.

    self.top.destroy()

def cancelButton(self):

    """Handle clicks in the cancel button of a number dialog."""

    self.answer=-1
    self.top.destroy()
#@-node:ekr.20071001092721.29:swingAskOKCancelNumber.okButton, cancelButton
#@+node:ekr.20071001092721.30:swingAskOKCancelNumber.onKey
def onKey (self,event):

    << eliminate non-numbers >>

    ch = event.char.lower()

    if ch in ('o','\n','\r'):
        self.okButton()
    elif ch == 'c':
        self.cancelButton()

    return "break"
#@+node:ekr.20071001092721.31:<< eliminate non-numbers >>
e = self.number_entry
s = e.get().strip()

i = 0
while i < len(s):
    ch = s[i]
    if not ch.isdigit():
        e.delete(str(i))
        s = e.get()
    else:
        i += 1
#@-node:ekr.20071001092721.31:<< eliminate non-numbers >>
#@-node:ekr.20071001092721.30:swingAskOKCancelNumber.onKey
#@-node:ekr.20071001092721.26:class swingAskOkCancelNumber
#@+node:ekr.20071001092721.32:class swingAskOkCancelString
class  swingAskOkCancelString (leoSwingDialog):

    """Create and run a modal swing dialog to get a string."""

    @others
#@+node:ekr.20071001092721.33:swingAskOKCancelString.__init__
def __init__ (self,c,title,message):

    """Create a number dialog"""

    leoSwingDialog.__init__(self,c,title,resizeable=False) # Initialize the base class.

    if g.app.unitTesting: return

    self.answer = -1
    self.number_entry = None

    self.createTopFrame()
    self.top.bind("<Key>", self.onKey)

    self.createFrame(message)
    self.focus_widget = self.number_entry

    buttons = (
            {"text":"Ok",    "command":self.okButton,     "default":True},
            {"text":"Cancel","command":self.cancelButton} )
    buttonList = self.createButtons(buttons)
    self.ok_button = buttonList[0] # Override the default kind of Ok button.
#@-node:ekr.20071001092721.33:swingAskOKCancelString.__init__
#@+node:ekr.20071001092721.34:swingAskOkCancelString.createFrame
def createFrame (self,message):

    """Create the frame for a number dialog."""

    if g.app.unitTesting: return

    c = self.c

    lab = Tk.Label(self.frame,text=message)
    lab.pack(pady=10,side="left")

    self.number_entry = w = Tk.Entry(self.frame,width=20)
    w.pack(side="left")

    c.set_focus(w)
#@-node:ekr.20071001092721.34:swingAskOkCancelString.createFrame
#@+node:ekr.20071001092721.35:swingAskOkCancelString.okButton, cancelButton
def okButton(self):

    """Handle clicks in the ok button of a string dialog."""

    self.answer = self.number_entry.get().strip()
    self.top.destroy()

def cancelButton(self):

    """Handle clicks in the cancel button of a string dialog."""

    self.answer=''
    self.top.destroy()
#@-node:ekr.20071001092721.35:swingAskOkCancelString.okButton, cancelButton
#@+node:ekr.20071001092721.36:swingAskOkCancelString.onKey
def onKey (self,event):

    ch = event.char.lower()

    if ch in ('o','\n','\r'):
        self.okButton()
    elif ch == 'c':
        self.cancelButton()

    return "break"
#@-node:ekr.20071001092721.36:swingAskOkCancelString.onKey
#@-node:ekr.20071001092721.32:class swingAskOkCancelString
#@+node:ekr.20071001092721.37:class swingAskYesNo
class swingAskYesNo (leoSwingDialog):

    """A class that creates a swing dialog with two buttons: Yes and No."""

    @others
#@+node:ekr.20071001092721.38:swingAskYesNo.__init__
def __init__ (self,c,title,message=None,resizeable=False):

    """Create a dialog having yes and no buttons."""

    leoSwingDialog.__init__(self,c,title,resizeable) # Initialize the base class.

    if g.app.unitTesting: return

    self.createTopFrame()
    self.top.bind("<Key>",self.onKey)

    if message:
        self.createMessageFrame(message)

    buttons = (
        {"text":"Yes","command":self.yesButton,  "default":True},
        {"text":"No", "command":self.noButton} )
    self.createButtons(buttons)
#@-node:ekr.20071001092721.38:swingAskYesNo.__init__
#@+node:ekr.20071001092721.39:swingAskYesNo.onKey
def onKey(self,event):

    """Handle keystroke events in dialogs having yes and no buttons."""

    ch = event.char.lower()

    if ch in ('y','\n','\r'):
        self.yesButton()
    elif ch == 'n':
        self.noButton()

    return "break"
#@-node:ekr.20071001092721.39:swingAskYesNo.onKey
#@-node:ekr.20071001092721.37:class swingAskYesNo
#@+node:ekr.20071001092721.40:class swingAskYesNoCancel
class swingAskYesNoCancel(leoSwingDialog):

    """A class to create and run swing dialogs having three buttons.

    By default, these buttons are labeled Yes, No and Cancel."""

    @others
#@+node:ekr.20071001092721.41:askYesNoCancel.__init__
def __init__ (self,c,title,
    message=None,
    yesMessage="Yes",
    noMessage="No",
    defaultButton="Yes",
    resizeable=False):

    """Create a dialog having three buttons."""

    leoSwingDialog.__init__(self,c,title,resizeable,canClose=False) # Initialize the base class.

    if g.app.unitTesting: return

    self.yesMessage,self.noMessage = yesMessage,noMessage
    self.defaultButton = defaultButton

    self.createTopFrame()
    self.top.bind("<Key>",self.onKey)

    if message:
        self.createMessageFrame(message)

    buttons = (
        {"text":yesMessage,"command":self.yesButton,   "default":yesMessage==defaultButton},
        {"text":noMessage, "command":self.noButton,    "default":noMessage==defaultButton},
        {"text":"Cancel",  "command":self.cancelButton,"default":"Cancel"==defaultButton} )
    self.createButtons(buttons)
#@-node:ekr.20071001092721.41:askYesNoCancel.__init__
#@+node:ekr.20071001092721.42:askYesNoCancel.onKey
def onKey(self,event):

    """Handle keystrokes in dialogs with three buttons."""

    ch = event.char.lower()

    if ch in ('\n','\r'):
        ch = self.defaultButton[0].lower()

    if ch == self.yesMessage[0].lower():
        self.yesButton()
    elif ch == self.noMessage[0].lower():
        self.noButton()
    elif ch == 'c':
        self.cancelButton()

    return "break"
#@-node:ekr.20071001092721.42:askYesNoCancel.onKey
#@+node:ekr.20071001092721.43:askYesNoCancel.noButton & yesButton
def noButton(self):

    """Handle clicks in the 'no' (second) button in a dialog with three buttons."""

    self.answer=self.noMessage.lower()
    self.top.destroy()

def yesButton(self):

    """Handle clicks in the 'yes' (first) button in a dialog with three buttons."""

    self.answer=self.yesMessage.lower()
    self.top.destroy()
#@-node:ekr.20071001092721.43:askYesNoCancel.noButton & yesButton
#@-node:ekr.20071001092721.40:class swingAskYesNoCancel
#@+node:ekr.20071001092721.44:class swingListboxDialog
class swingListBoxDialog (leoSwingDialog):

    """A base class for swing dialogs containing a Tk Listbox"""

    @others
#@+node:ekr.20071001092721.45:swingListboxDialog.__init__
def __init__ (self,c,title,label):

    """Constructor for the base listboxDialog class."""

    leoSwingDialog.__init__(self,c,title,resizeable=True) # Initialize the base class.

    if g.app.unitTesting: return

    self.createTopFrame()
    self.top.protocol("WM_DELETE_WINDOW", self.destroy)

    # Initialize common ivars.
    self.label = label
    self.positionList = []
    self.buttonFrame = None

    # Fill in the frame.
    self.createFrame()
    self.fillbox()

    # Make the common bindings after creating self.box.

    self.box.bind("<Double-Button-1>",self.go)
#@-node:ekr.20071001092721.45:swingListboxDialog.__init__
#@+node:ekr.20071001092721.46:addStdButtons
def addStdButtons (self,frame):

    """Add stanadard buttons to a listBox dialog."""

    # Create the ok and cancel buttons.
    self.ok = ok = Tk.Button(frame,text="Go",width=6,command=self.go)
    self.hide = hide = Tk.Button(frame,text="Hide",width=6,command=self.hide)

    ok.pack(side="left",pady=2,padx=5)
    hide.pack(side="left",pady=2,padx=5)
#@-node:ekr.20071001092721.46:addStdButtons
#@+node:ekr.20071001092721.47:createFrame
def createFrame(self):

    """Create the essentials of a listBoxDialog frame

    Subclasses will add buttons to self.buttonFrame"""

    if g.app.unitTesting: return

    self.outerFrame = f = Tk.Frame(self.frame)
    f.pack(expand=1,fill="both")

    if self.label:
        labf = Tk.Frame(f)
        labf.pack(pady=2)
        lab = Tk.Label(labf,text=self.label)
        lab.pack()

    f2 = Tk.Frame(f)
    f2.pack(expand=1,fill="both")

    self.box = box = Tk.Listbox(f2,height=20,width=30)
    box.pack(side="left",expand=1,fill="both")

    bar = Tk.Scrollbar(f2)
    bar.pack(side="left", fill="y")

    bar.config(command=box.yview)
    box.config(yscrollcommand=bar.set)
#@-node:ekr.20071001092721.47:createFrame
#@+node:ekr.20071001092721.48:destroy
def destroy (self,event=None):

    """Hide, do not destroy, a listboxDialog window

    subclasses may override to really destroy the window"""

    # __pychecker__ = '--no-argsused' # event not used, but must be present.

    self.top.withdraw() # Don't allow this window to be destroyed.
#@-node:ekr.20071001092721.48:destroy
#@+node:ekr.20071001092721.49:hide
def hide (self):

    """Hide a list box dialog."""

    self.top.withdraw()
#@-node:ekr.20071001092721.49:hide
#@+node:ekr.20071001092721.50:fillbox
def fillbox(self,event=None):

    """Fill a listbox from information.

    Overridden by subclasses"""

    # __pychecker__ = '--no-argsused' # the event param must be present.

    pass
#@-node:ekr.20071001092721.50:fillbox
#@+node:ekr.20071001092721.51:go
def go(self,event=None):

    """Handle clicks in the "go" button in a list box dialog."""

    # __pychecker__ = '--no-argsused' # the event param must be present.

    c = self.c ; box = self.box

    # Work around an old Python bug.  Convert strings to ints.
    items = box.curselection()
    try:
        items = map(int, items)
    except ValueError: pass

    if items:
        n = items[0]
        p = self.positionList[n]
        c.beginUpdate()
        try:
            c.frame.tree.expandAllAncestors(p)
            c.selectPosition(p,updateBeadList=True)
                # A case could be made for updateBeadList=False
        finally:
            c.endUpdate()
#@-node:ekr.20071001092721.51:go
#@-node:ekr.20071001092721.44:class swingListboxDialog
#@-node:ekr.20071001092721:@thin leoSwingDialog.py
#@+node:ekr.20070930105125:@thin leoSwingFrame.py
@first # -*- coding: utf-8 -*-

@language python
@tabwidth -4
@pagewidth 80

<< imports >>

@others
#@+node:ekr.20070930105601.1:<< imports >>
import leoGlobals as g

import leoChapters
import leoColor
import leoFrame
import leoKeys
import leoMenu
import leoNodes

import javax.swing as swing
import java.awt as awt
import java.lang

import os
import string
import sys

# # The following imports _are_ used.
# __pychecker__ = '--no-import'
# import threading
# import time
#@-node:ekr.20070930105601.1:<< imports >>
#@+node:ekr.20070930105601.2:class leoSwingFrame
class leoSwingFrame (leoFrame.leoFrame):

    @others
#@+node:ekr.20071001091231.24: Birth & Death (swingFrame)
#@+node:ekr.20071001091231.25:__init__ (swingFrame)
def __init__(self,title,gui):

    g.trace('swingFrame',g.callers(20))

    # Init the base class.
    leoFrame.leoFrame.__init__(self,gui)

    self.use_chapters = False ###

    self.title = title

    leoSwingFrame.instances += 1

    self.c = None # Set in finishCreate.
    self.iconBarClass = self.swingIconBarClass
    self.statusLineClass = self.swingStatusLineClass
    self.iconBar = None

    self.trace_status_line = None # Set in finishCreate.

    << set the leoSwingFrame ivars >>
#@+node:ekr.20071001091231.26:<< set the leoSwingFrame ivars >> (removed frame.bodyCtrl ivar)
# "Official ivars created in createLeoFrame and its allies.
self.bar1 = None
self.bar2 = None
self.body = None
self.f1 = self.f2 = None
self.findPanel = None # Inited when first opened.
self.iconBarComponentName = 'iconBar'
self.iconFrame = None 
self.log = None
self.canvas = None
self.outerFrame = None
self.statusFrame = None
self.statusLineComponentName = 'statusLine'
self.statusText = None 
self.statusLabel = None 
self.top = None
self.tree = None
# self.treeBar = None # Replaced by injected frame.canvas.leo_treeBar.

# Used by event handlers...
self.controlKeyIsDown = False # For control-drags
self.draggedItem = None
self.isActive = True
self.redrawCount = 0
self.wantedWidget = None
self.wantedCallbackScheduled = False
self.scrollWay = None
#@-node:ekr.20071001091231.26:<< set the leoSwingFrame ivars >> (removed frame.bodyCtrl ivar)
#@-node:ekr.20071001091231.25:__init__ (swingFrame)
#@+node:ekr.20071001091231.27:__repr__ (swingFrame)
def __repr__ (self):

    return "<leoSwingFrame: %s>" % self.title
#@-node:ekr.20071001091231.27:__repr__ (swingFrame)
#@+node:ekr.20071001091231.28:swingFrame.finishCreate & helpers
def finishCreate (self,c):

    f = self ; f.c = c
    g.trace('swingFrame')

    self.trace_status_line = c.config.getBool('trace_status_line')
    self.use_chapters      = False and c.config.getBool('use_chapters') ###
    self.use_chapter_tabs  = False and c.config.getBool('use_chapter_tabs') ###

    # This must be done after creating the commander.
    f.splitVerticalFlag,f.ratio,f.secondary_ratio = f.initialRatios()

    f.createOuterFrames()

    ### f.createIconBar()

    f.createSplitterComponents()

    ### f.createStatusLine()
    f.createFirstTreeNode()
    f.menu = leoSwingMenu(f)
        # c.finishCreate calls f.createMenuBar later.
    c.setLog()
    g.app.windowList.append(f)
    c.initVersion()
    c.signOnWithVersion()
    f.miniBufferWidget = f.createMiniBufferWidget()
    c.bodyWantsFocusNow()
#@+node:ekr.20071001091231.29:createOuterFrames
def createOuterFrames (self):


    f = self ; c = f.c
    ### f.top = top = Tk.Toplevel()
    ### g.app.gui.attachLeoIcon(top)
    ### top.title(f.title)
    ### top.minsize(30,10) # In grid units.

    def exit(event):
        java.lang.System.exit(0)

    # def onButtonPressed(event):
        # field.text=quotes[event.source.text]

    # def createButton(name):
        # return swing.JButton(name,preferredSize=(100,20),
            # actionPerformed=onButtonPressed)

    f.top = w = swing.JFrame('jyLeo!',size=(700,700),windowClosing=exit)
    w.contentPane.layout = awt.FlowLayout()

    # if g.os_path_exists(g.app.user_xresources_path):
        # f.top.option_readfile(g.app.user_xresources_path)

    # f.top.protocol("WM_DELETE_WINDOW", f.OnCloseLeoEvent)
    # f.top.bind("<Button-1>", f.OnActivateLeoEvent)

    # f.top.bind("<Control-KeyPress>",f.OnControlKeyDown)
    # f.top.bind("<Control-KeyRelease>",f.OnControlKeyUp)

    # These don't work on Windows. Because of bugs in window managers,
    # there is NO WAY to know which window is on top!
    # f.top.bind("<Activate>",f.OnActivateLeoEvent)
    # f.top.bind("<Deactivate>",f.OnDeactivateLeoEvent)

    # Create the outer frame, the 'hull' component.
    # f.outerFrame = Tk.Frame(top)
    # f.outerFrame.pack(expand=1,fill="both")
#@-node:ekr.20071001091231.29:createOuterFrames
#@+node:ekr.20071001091231.30:createSplitterComponents (removed frame.bodyCtrl ivar)
def createSplitterComponents (self):

    f = self ; c = f.c

    g.trace()

    f.createLeoSplitters(f.outerFrame)

    if 0:
        # Create the canvas, tree, log and body.
        if f.use_chapters:
            c.chapterController = cc = leoChapters.chapterController(c)

        if self.use_chapters and self.use_chapter_tabs:
            cc.tt = leoSwingTreeTab(c,f.split2Pane1,cc)

        f.canvas = f.createCanvas(f.split2Pane1)
        f.tree   = leoSwingTree.leoSwingTree(c,f,f.canvas)
        f.log    = leoSwingLog(f,f.split2Pane2)
        f.body   = leoSwingBody(f,f.split1Pane2)

    f.body = leoSwingBody(f,f.top)
    f.tree = leoSwingTree(c,f,f.top)
    f.log  = leoSwingLog(f,f.top)

    # Configure.
    f.setTabWidth(c.tab_width)
    f.reconfigurePanes()
    f.body.setFontFromConfig()
    f.body.setColorFromConfig()
#@-node:ekr.20071001091231.30:createSplitterComponents (removed frame.bodyCtrl ivar)
#@+node:ekr.20071001091231.31:createFirstTreeNode
def createFirstTreeNode (self):

    f = self ; c = f.c

    t = leoNodes.tnode()
    v = leoNodes.vnode(t)
    p = leoNodes.position(v,[])
    v.initHeadString("NewHeadline")
    p.moveToRoot(oldRoot=None)
    c.setRootPosition(p) # New in 4.4.2.
    c.editPosition(p)
#@-node:ekr.20071001091231.31:createFirstTreeNode
#@-node:ekr.20071001091231.28:swingFrame.finishCreate & helpers
#@+node:ekr.20071001091231.33:swingFrame.createCanvas & helpers
def createCanvas (self,parentFrame,pack=True):

    c = self.c

    scrolls = c.config.getBool('outline_pane_scrolls_horizontally')
    scrolls = g.choose(scrolls,1,0)
    canvas = self.createTkTreeCanvas(parentFrame,scrolls,pack)
    self.setCanvasColorFromConfig(canvas)

    return canvas
#@nonl
#@+node:ekr.20071001091231.34:f.createTkTreeCanvas & callbacks
def createTkTreeCanvas (self,parentFrame,scrolls,pack):

    frame = self

    canvas = Tk.Canvas(parentFrame,name="canvas",
        bd=0,bg="white",relief="flat")

    treeBar = Tk.Scrollbar(parentFrame,name="treeBar")

    # New in Leo 4.4.3 b1: inject the ivar into the canvas.
    canvas.leo_treeBar = treeBar

    # Bind mouse wheel event to canvas
    if sys.platform != "win32": # Works on 98, crashes on XP.
        canvas.bind("<MouseWheel>", frame.OnMouseWheel)
        if 1: # New in 4.3.
            << workaround for mouse-wheel problems >>

    canvas['yscrollcommand'] = self.setCallback
    treeBar['command']     = self.yviewCallback
    treeBar.pack(side="right", fill="y")
    if scrolls: 
        treeXBar = Tk.Scrollbar( 
            parentFrame,name='treeXBar',orient="horizontal") 
        canvas['xscrollcommand'] = treeXBar.set 
        treeXBar['command'] = canvas.xview 
        treeXBar.pack(side="bottom", fill="x")

    if pack:
        canvas.pack(expand=1,fill="both")

    canvas.bind("<Button-1>", frame.OnActivateTree)

    # Handle mouse wheel in the outline pane.
    if sys.platform == "linux2": # This crashes tcl83.dll
        canvas.bind("<MouseWheel>", frame.OnMouseWheel)
    if 0:
        << do scrolling by hand in a separate thread >>

    # g.print_bindings("canvas",canvas)
    return canvas
#@+node:ekr.20071001091231.35:<< workaround for mouse-wheel problems >>
# Handle mapping of mouse-wheel to buttons 4 and 5.

def mapWheel(e):
    if e.num == 4: # Button 4
        e.delta = 120
        return frame.OnMouseWheel(e)
    elif e.num == 5: # Button 5
        e.delta = -120
        return frame.OnMouseWheel(e)

canvas.bind("<ButtonPress>",mapWheel,add=1)
#@-node:ekr.20071001091231.35:<< workaround for mouse-wheel problems >>
#@+node:ekr.20071001091231.36:<< do scrolling by hand in a separate thread >>
# New in 4.3: replaced global way with scrollWay ivar.
ev = threading.Event()

def run(self=self,canvas=canvas,ev=ev):

    while 1:
        ev.wait()
        if self.scrollWay =='Down': canvas.yview("scroll", 1,"units")
        else:                       canvas.yview("scroll",-1,"units")
        time.sleep(.1)

t = threading.Thread(target = run)
t.setDaemon(True)
t.start()

def scrollUp(event): scrollUpOrDown(event,'Down')
def scrollDn(event): scrollUpOrDown(event,'Up')

def scrollUpOrDown(event,theWay):
    if event.widget!=canvas: return
    if 0: # This seems to interfere with scrolling.
        if canvas.find_overlapping(event.x,event.y,event.x,event.y): return
    ev.set()
    self.scrollWay = theWay

def off(event,ev=ev,canvas=canvas):
    if event.widget!=canvas: return
    ev.clear()

if 1: # Use shift-click
    # Shift-button-1 scrolls up, Shift-button-2 scrolls down
    canvas.bind_all('<Shift Button-3>',scrollDn)
    canvas.bind_all('<Shift Button-1>',scrollUp)
    canvas.bind_all('<Shift ButtonRelease-1>',off)
    canvas.bind_all('<Shift ButtonRelease-3>',off)
else: # Use plain click.
    canvas.bind_all( '<Button-3>',scrollDn)
    canvas.bind_all( '<Button-1>',scrollUp)
    canvas.bind_all( '<ButtonRelease-1>',off)
    canvas.bind_all( '<ButtonRelease-3>',off)
#@-node:ekr.20071001091231.36:<< do scrolling by hand in a separate thread >>
#@+node:ekr.20071001091231.37:Scrolling callbacks (swingFrame)
def setCallback (self,*args,**keys):

    """Callback to adjust the scrollbar.

    Args is a tuple of two floats describing the fraction of the visible area."""

    #g.trace(self.tree.redrawCount,args,g.callers())

    apply(self.canvas.leo_treeBar.set,args,keys)

    if self.tree.allocateOnlyVisibleNodes:
        self.tree.setVisibleArea(args)

def yviewCallback (self,*args,**keys):

    """Tell the canvas to scroll"""

    #g.trace(vyiewCallback,args,keys,g.callers())

    if self.tree.allocateOnlyVisibleNodes:
        self.tree.allocateNodesBeforeScrolling(args)

    apply(self.canvas.yview,args,keys)
#@nonl
#@-node:ekr.20071001091231.37:Scrolling callbacks (swingFrame)
#@-node:ekr.20071001091231.34:f.createTkTreeCanvas & callbacks
#@+node:ekr.20071001091231.38:f.setCanvasColorFromConfig
def setCanvasColorFromConfig (self,canvas):

    c = self.c

    bg = c.config.getColor("outline_pane_background_color") or 'white'

    try:
        canvas.configure(bg=bg)
    except:
        g.es("exception setting outline pane background color")
        g.es_exception()
#@-node:ekr.20071001091231.38:f.setCanvasColorFromConfig
#@-node:ekr.20071001091231.33:swingFrame.createCanvas & helpers
#@+node:ekr.20071001091231.39:swingFrame.createLeoSplitters & helpers
@ The key invariants used throughout this code:

1. self.splitVerticalFlag tells the alignment of the main splitter and
2. not self.splitVerticalFlag tells the alignment of the secondary splitter.

Only the general-purpose divideAnySplitter routine doesn't know about these
invariants. So most of this code is specialized for Leo's window. OTOH, creating
a single splitter window would be much easier than this code.
@c

def createLeoSplitters (self,parentFrame):

    # Splitter 1 is the main splitter containing splitter2 and the body pane.
    f1,bar1,split1Pane1,split1Pane2 = self.createLeoSwingSplitter(
        parentFrame,self.splitVerticalFlag,'splitter1')

    self.f1,self.bar1 = f1,bar1
    self.split1Pane1,self.split1Pane2 = split1Pane1,split1Pane2

    # Splitter 2 is the secondary splitter containing the tree and log panes.
    f2,bar2,split2Pane1,split2Pane2 = self.createLeoSwingSplitter(
        split1Pane1,not self.splitVerticalFlag,'splitter2')

    self.f2,self.bar2 = f2,bar2
    self.split2Pane1,self.split2Pane2 = split2Pane1,split2Pane2
#@nonl
#@+node:ekr.20071001091231.40:createLeoSwingSplitter
def createLeoSwingSplitter (self,parent,verticalFlag,componentName):

    c = self.c

    return None,None,None,None ###

    # # Create the frames.
    # f = Tk.Frame(parent,bd=0,relief="flat")
    # f.pack(expand=1,fill="both",pady=1)

    # f1 = Tk.Frame(f)
    # f2 = Tk.Frame(f)
    # bar = Tk.Frame(f,bd=2,relief="raised",bg="LightSteelBlue2")

    # # Configure and place the frames.
    # self.configureBar(bar,verticalFlag)
    # self.bindBar(bar,verticalFlag)
    # self.placeSplitter(bar,f1,f2,verticalFlag)

    # return f, bar, f1, f2
#@-node:ekr.20071001091231.40:createLeoSwingSplitter
#@+node:ekr.20071001091231.41:bindBar
def bindBar (self, bar, verticalFlag):

    if verticalFlag == self.splitVerticalFlag:
        bar.bind("<B1-Motion>", self.onDragMainSplitBar)

    else:
        bar.bind("<B1-Motion>", self.onDragSecondarySplitBar)
#@-node:ekr.20071001091231.41:bindBar
#@+node:ekr.20071001091231.42:divideAnySplitter
# This is the general-purpose placer for splitters.
# It is the only general-purpose splitter code in Leo.

def divideAnySplitter (self, frac, verticalFlag, bar, pane1, pane2):

    pass ###

    # if verticalFlag:
        # # Panes arranged vertically; horizontal splitter bar
        # bar.place(rely=frac)
        # pane1.place(relheight=frac)
        # pane2.place(relheight=1-frac)
    # else:
        # # Panes arranged horizontally; vertical splitter bar
        # bar.place(relx=frac)
        # pane1.place(relwidth=frac)
        # pane2.place(relwidth=1-frac)
#@-node:ekr.20071001091231.42:divideAnySplitter
#@+node:ekr.20071001091231.43:divideLeoSplitter
# Divides the main or secondary splitter, using the key invariant.
def divideLeoSplitter (self, verticalFlag, frac):

    if self.splitVerticalFlag == verticalFlag:
        self.divideLeoSplitter1(frac,verticalFlag)
        self.ratio = frac # Ratio of body pane to tree pane.
    else:
        self.divideLeoSplitter2(frac,verticalFlag)
        self.secondary_ratio = frac # Ratio of tree pane to log pane.

# Divides the main splitter.
def divideLeoSplitter1 (self, frac, verticalFlag): 
    self.divideAnySplitter(frac, verticalFlag,
        self.bar1, self.split1Pane1, self.split1Pane2)

# Divides the secondary splitter.
def divideLeoSplitter2 (self, frac, verticalFlag): 
    self.divideAnySplitter (frac, verticalFlag,
        self.bar2, self.split2Pane1, self.split2Pane2)
#@-node:ekr.20071001091231.43:divideLeoSplitter
#@+node:ekr.20071001091231.44:onDrag...
def onDragMainSplitBar (self, event):
    self.onDragSplitterBar(event,self.splitVerticalFlag)

def onDragSecondarySplitBar (self, event):
    self.onDragSplitterBar(event,not self.splitVerticalFlag)

def onDragSplitterBar (self, event, verticalFlag):

    # x and y are the coordinates of the cursor relative to the bar, not the main window.
    bar = event.widget
    x = event.x
    y = event.y
    top = bar.winfo_toplevel()

    if verticalFlag:
        # Panes arranged vertically; horizontal splitter bar
        wRoot = top.winfo_rooty()
        barRoot = bar.winfo_rooty()
        wMax = top.winfo_height()
        offset = float(barRoot) + y - wRoot
    else:
        # Panes arranged horizontally; vertical splitter bar
        wRoot = top.winfo_rootx()
        barRoot = bar.winfo_rootx()
        wMax = top.winfo_width()
        offset = float(barRoot) + x - wRoot

    # Adjust the pixels, not the frac.
    if offset < 3: offset = 3
    if offset > wMax - 2: offset = wMax - 2
    # Redraw the splitter as the drag is occuring.
    frac = float(offset) / wMax
    # g.trace(frac)
    self.divideLeoSplitter(verticalFlag, frac)
#@-node:ekr.20071001091231.44:onDrag...
#@+node:ekr.20071001091231.45:placeSplitter
def placeSplitter (self,bar,pane1,pane2,verticalFlag):

    if verticalFlag:
        # Panes arranged vertically; horizontal splitter bar
        pane1.place(relx=0.5, rely =   0, anchor="n", relwidth=1.0, relheight=0.5)
        pane2.place(relx=0.5, rely = 1.0, anchor="s", relwidth=1.0, relheight=0.5)
        bar.place  (relx=0.5, rely = 0.5, anchor="c", relwidth=1.0)
    else:
        # Panes arranged horizontally; vertical splitter bar
        # adj gives tree pane more room when tiling vertically.
        adj = g.choose(verticalFlag != self.splitVerticalFlag,0.65,0.5)
        pane1.place(rely=0.5, relx =   0, anchor="w", relheight=1.0, relwidth=adj)
        pane2.place(rely=0.5, relx = 1.0, anchor="e", relheight=1.0, relwidth=1.0-adj)
        bar.place  (rely=0.5, relx = adj, anchor="c", relheight=1.0)
#@-node:ekr.20071001091231.45:placeSplitter
#@-node:ekr.20071001091231.39:swingFrame.createLeoSplitters & helpers
#@+node:ekr.20071001091231.46:Destroying the swingFrame
#@+node:ekr.20071001091231.47:destroyAllObjects
def destroyAllObjects (self):

    """Clear all links to objects in a Leo window."""

    frame = self ; c = self.c ; tree = frame.tree ; body = self.body

    # g.printGcAll()

    # Do this first.
    << clear all vnodes and tnodes in the tree >>

    # Destroy all ivars in subcommanders.
    g.clearAllIvars(c.atFileCommands)
    if c.chapterController: # New in Leo 4.4.3 b1.
        g.clearAllIvars(c.chapterController)
    g.clearAllIvars(c.fileCommands)
    g.clearAllIvars(c.keyHandler) # New in Leo 4.4.3 b1.
    g.clearAllIvars(c.importCommands)
    g.clearAllIvars(c.tangleCommands)
    g.clearAllIvars(c.undoer)

    g.clearAllIvars(c)
    g.clearAllIvars(body.colorizer)
    g.clearAllIvars(body)
    g.clearAllIvars(tree)

    # This must be done last.
    frame.destroyAllPanels()
    g.clearAllIvars(frame)

#@+node:ekr.20071001091231.48:<< clear all vnodes and tnodes in the tree>>
# Using a dict here is essential for adequate speed.
vList = [] ; tDict = {}

for p in c.allNodes_iter():
    vList.append(p.v)
    if p.v.t:
        key = id(p.v.t)
        if not tDict.has_key(key):
            tDict[key] = p.v.t

for key in tDict.keys():
    g.clearAllIvars(tDict[key])

for v in vList:
    g.clearAllIvars(v)

vList = [] ; tDict = {} # Remove these references immediately.
#@-node:ekr.20071001091231.48:<< clear all vnodes and tnodes in the tree>>
#@-node:ekr.20071001091231.47:destroyAllObjects
#@+node:ekr.20071001091231.49:destroyAllPanels
def destroyAllPanels (self):

    """Destroy all panels attached to this frame."""

    panels = (self.comparePanel, self.colorPanel, self.findPanel, self.fontPanel, self.prefsPanel)

    for panel in panels:
        if panel:
            panel.top.destroy()
#@-node:ekr.20071001091231.49:destroyAllPanels
#@+node:ekr.20071001091231.50:destroySelf (swingFrame)
def destroySelf (self):

    # Remember these: we are about to destroy all of our ivars!
    top = self.top 
    c = self.c

    # Indicate that the commander is no longer valid.
    c.exists = False 

    # g.trace(self)

    # Important: this destroys all the objects of the commander too.
    self.destroyAllObjects()

    c.exists = False # Make sure this one ivar has not been destroyed.

    top.destroy()
#@-node:ekr.20071001091231.50:destroySelf (swingFrame)
#@-node:ekr.20071001091231.46:Destroying the swingFrame
#@-node:ekr.20071001091231.24: Birth & Death (swingFrame)
#@+node:ekr.20071001091231.51:class swingStatusLineClass
class swingStatusLineClass:

    '''A class representing the status line.'''

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

    self.c = c
    self.colorTags = [] # list of color names used as tags.
    self.enabled = False
    self.isVisible = False
    self.lastRow = self.lastCol = 0
    self.log = c.frame.log
    #if 'black' not in self.log.colorTags:
    #    self.log.colorTags.append("black")
    self.parentFrame = parentFrame
    self.statusFrame = Tk.Frame(parentFrame,bd=2)
    text = "line 0, col 0"
    width = len(text) + 4
    self.labelWidget = Tk.Label(self.statusFrame,text=text,width=width,anchor="w")
    self.labelWidget.pack(side="left",padx=1)

    bg = self.statusFrame.cget("background")
    self.textWidget = w = g.app.gui.bodyTextWidget(
        self.statusFrame,
        height=1,state="disabled",bg=bg,relief="groove",name='status-line')
    self.textWidget.pack(side="left",expand=1,fill="x")
    w.bind("<Button-1>", self.onActivate)
    self.show()

    c.frame.statusFrame = self.statusFrame
    c.frame.statusLabel = self.labelWidget
    c.frame.statusText  = self.textWidget
#@-node:ekr.20071001091231.52: ctor
#@+node:ekr.20071001091231.53:clear
def clear (self):

    w = self.textWidget
    if not w: return

    w.configure(state="normal")
    w.delete(0,"end")
    w.configure(state="disabled")
#@-node:ekr.20071001091231.53:clear
#@+node:ekr.20071001091231.54:enable, disable & isEnabled
def disable (self,background=None):

    c = self.c ; w = self.textWidget
    if w:
        if not background:
            background = self.statusFrame.cget("background")
        w.configure(state="disabled",background=background)
    self.enabled = False
    c.bodyWantsFocus()

def enable (self,background="white"):

    # g.trace()
    c = self.c ; w = self.textWidget
    if w:
        w.configure(state="normal",background=background)
        c.widgetWantsFocus(w)
    self.enabled = True

def isEnabled(self):
    return self.enabled
#@nonl
#@-node:ekr.20071001091231.54:enable, disable & isEnabled
#@+node:ekr.20071001091231.55:get
def get (self):

    w = self.textWidget
    if w:
        return w.getAllText()
    else:
        return ""
#@-node:ekr.20071001091231.55:get
#@+node:ekr.20071001091231.56:getFrame
def getFrame (self):

    return self.statusFrame
#@-node:ekr.20071001091231.56:getFrame
#@+node:ekr.20071001091231.57:onActivate
def onActivate (self,event=None):

    # Don't change background as the result of simple mouse clicks.
    background = self.statusFrame.cget("background")
    self.enable(background=background)
#@-node:ekr.20071001091231.57:onActivate
#@+node:ekr.20071001091231.58:pack & show
def pack (self):

    if not self.isVisible:
        self.isVisible = True
        self.statusFrame.pack(fill="x",pady=1)

show = pack
#@-node:ekr.20071001091231.58:pack & show
#@+node:ekr.20071001091231.59:put (leoSwingFrame:statusLineClass)
def put(self,s,color=None):

    # g.trace('swingStatusLine',self.textWidget,s)

    w = self.textWidget
    if not w:
        g.trace('swingStatusLine','***** disabled')
        return

    w.configure(state="normal")
    w.insert("end",s)

    if color:
        if color not in self.colorTags:
            self.colorTags.append(color)
            w.tag_config(color,foreground=color)
        w.tag_add(color,"end-%dc" % (len(s)+1),"end-1c")
        w.tag_config("black",foreground="black")
        w.tag_add("black","end")

    w.configure(state="disabled")
#@-node:ekr.20071001091231.59:put (leoSwingFrame:statusLineClass)
#@+node:ekr.20071001091231.60:unpack & hide
def unpack (self):

    if self.isVisible:
        self.isVisible = False
        self.statusFrame.pack_forget()

hide = unpack
#@-node:ekr.20071001091231.60:unpack & hide
#@+node:ekr.20071001091231.61:update (statusLine)
def update (self):

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

    if g.app.killed or not self.isVisible:
        return

    s = bodyCtrl.getAllText()    
    index = bodyCtrl.getInsertPoint()
    row,col = g.convertPythonIndexToRowCol(s,index)
    if col > 0:
        s2 = s[index-col:index]
        s2 = g.toUnicode(s2,g.app.tkEncoding)
        col = g.computeWidth (s2,c.tab_width)

    # Important: this does not change the focus because labels never get focus.
    self.labelWidget.configure(text="line %d, col %d" % (row,col))
    self.lastRow = row
    self.lastCol = col
#@-node:ekr.20071001091231.61:update (statusLine)
#@-node:ekr.20071001091231.51:class swingStatusLineClass
#@+node:ekr.20071001091231.62:class swingIconBarClass
class swingIconBarClass:

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

    @others
#@+node:ekr.20071001091231.63: 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.20071001091231.63: ctor
#@+node:ekr.20071001091231.64: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 not command:
        def command():
            print "command for widget %s" % (n)

    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.20071001091231.65:<< 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.20071001091231.65:<< create a picture >>
#@-node:ekr.20071001091231.64:add
#@+node:ekr.20071001091231.66: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.20071001091231.66:clear
#@+node:ekr.20071001091231.67:deleteButton (new in Leo 4.4.3)
def deleteButton (self,w):

    w.pack_forget()
#@-node:ekr.20071001091231.67:deleteButton (new in Leo 4.4.3)
#@+node:ekr.20071001091231.68:getFrame
def getFrame (self):

    return self.iconFrame
#@-node:ekr.20071001091231.68:getFrame
#@+node:ekr.20071001091231.69: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.20071001091231.69:pack (show)
#@+node:ekr.20071001091231.70:setCommandForButton (new in Leo 4.4.3)
def setCommandForButton(self,b,command):

    b.configure(command=command)
#@-node:ekr.20071001091231.70:setCommandForButton (new in Leo 4.4.3)
#@+node:ekr.20071001091231.71: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.20071001091231.71:unpack (hide)
#@-node:ekr.20071001091231.62:class swingIconBarClass
#@+node:ekr.20071001091231.72:Minibuffer methods
#@+node:ekr.20071001091231.73:showMinibuffer
def showMinibuffer (self):

    '''Make the minibuffer visible.'''

    frame = self

    if not frame.minibufferVisible:
        frame.minibufferFrame.pack(side='bottom',fill='x')
        frame.minibufferVisible = True
#@-node:ekr.20071001091231.73:showMinibuffer
#@+node:ekr.20071001091231.74:hideMinibuffer
def hideMinibuffer (self):

    '''Hide the minibuffer.'''

    frame = self
    if frame.minibufferVisible:
        frame.minibufferFrame.pack_forget()
        frame.minibufferVisible = False
#@-node:ekr.20071001091231.74:hideMinibuffer
#@+node:ekr.20071001091231.75:f.createMiniBufferWidget
def createMiniBufferWidget (self):

    '''Create the minbuffer below the status line.'''

    frame = self ; c = frame.c

    # frame.minibufferFrame = f = Tk.Frame(frame.outerFrame,relief='flat',borderwidth=0)
    # if c.showMinibuffer:
        # f.pack(side='bottom',fill='x')

    # lab = Tk.Label(f,text='mini-buffer',justify='left',anchor='nw',foreground='blue')
    # lab.pack(side='left')

    # if c.useTextMinibuffer:
        # label = g.app.gui.plainTextWidget(
            # f,height=1,relief='groove',background='lightgrey',name='minibuffer')
        # label.pack(side='left',fill='x',expand=1,padx=2,pady=1)
    # else:
        # label = Tk.Label(f,relief='groove',justify='left',anchor='w',name='minibuffer')
        # label.pack(side='left',fill='both',expand=1,padx=2,pady=1)

    # frame.minibufferVisible = c.showMinibuffer

    # return label
#@-node:ekr.20071001091231.75:f.createMiniBufferWidget
#@+node:ekr.20071001091231.76:f.setMinibufferBindings
def setMinibufferBindings (self):

    '''Create bindings for the minibuffer..'''

    f = self ; c = f.c ; k = c.k ; w = f.miniBufferWidget

    if not c.useTextMinibuffer: return

    # for kind,callback in (
        # ('<Key>',           k.masterKeyHandler),
        # ('<Button-1>',      k.masterClickHandler),
        # ('<Button-3>',      k.masterClick3Handler),
        # ('<Double-1>',      k.masterDoubleClickHandler),
        # ('<Double-3>',      k.masterDoubleClick3Handler),
    # ):
        # w.bind(kind,callback)

    # if 0:
        # if sys.platform.startswith('win'):
            # # Support Linux middle-button paste easter egg.
            # w.bind("<Button-2>",frame.OnPaste)
#@-node:ekr.20071001091231.76:f.setMinibufferBindings
#@-node:ekr.20071001091231.72:Minibuffer methods
#@+node:ekr.20071001091231.77:Configuration (swingFrame)
#@+node:ekr.20071001091231.78:configureBar (swingFrame)
def configureBar (self,bar,verticalFlag):

    c = self.c

    # Get configuration settings.
    w = c.config.getInt("split_bar_width")
    if not w or w < 1: w = 7
    relief = c.config.get("split_bar_relief","relief")
    if not relief: relief = "flat"
    color = c.config.getColor("split_bar_color")
    if not color: color = "LightSteelBlue2"

    try:
        if verticalFlag:
            # Panes arranged vertically; horizontal splitter bar
            bar.configure(relief=relief,height=w,bg=color,cursor="sb_v_double_arrow")
        else:
            # Panes arranged horizontally; vertical splitter bar
            bar.configure(relief=relief,width=w,bg=color,cursor="sb_h_double_arrow")
    except: # Could be a user error. Use all defaults
        g.es("exception in user configuration for splitbar")
        g.es_exception()
        if verticalFlag:
            # Panes arranged vertically; horizontal splitter bar
            bar.configure(height=7,cursor="sb_v_double_arrow")
        else:
            # Panes arranged horizontally; vertical splitter bar
            bar.configure(width=7,cursor="sb_h_double_arrow")
#@-node:ekr.20071001091231.78:configureBar (swingFrame)
#@+node:ekr.20071001091231.79:configureBarsFromConfig (swingFrame)
def configureBarsFromConfig (self):

    c = self.c

    w = c.config.getInt("split_bar_width")
    if not w or w < 1: w = 7

    relief = c.config.get("split_bar_relief","relief")
    if not relief or relief == "": relief = "flat"

    color = c.config.getColor("split_bar_color")
    if not color or color == "": color = "LightSteelBlue2"

    if self.splitVerticalFlag:
        bar1,bar2=self.bar1,self.bar2
    else:
        bar1,bar2=self.bar2,self.bar1

    try:
        bar1.configure(relief=relief,height=w,bg=color)
        bar2.configure(relief=relief,width=w,bg=color)
    except: # Could be a user error.
        g.es("exception in user configuration for splitbar")
        g.es_exception()
#@-node:ekr.20071001091231.79:configureBarsFromConfig (swingFrame)
#@+node:ekr.20071001091231.80:reconfigureFromConfig (swingFrame)
def reconfigureFromConfig (self):

    frame = self ; c = frame.c

    frame.tree.setFontFromConfig()
    ### frame.tree.setColorFromConfig()

    frame.configureBarsFromConfig()

    frame.body.setFontFromConfig()
    frame.body.setColorFromConfigt()

    frame.setTabWidth(c.tab_width)
    frame.log.setFontFromConfig()
    frame.log.setColorFromConfig()

    c.redraw_now()
#@-node:ekr.20071001091231.80:reconfigureFromConfig (swingFrame)
#@+node:ekr.20071001091231.81:setInitialWindowGeometry (swingFrame)
def setInitialWindowGeometry(self):

    """Set the position and size of the frame to config params."""

    c = self.c

    h = c.config.getInt("initial_window_height") or 500
    w = c.config.getInt("initial_window_width") or 600
    x = c.config.getInt("initial_window_left") or 10
    y = c.config.getInt("initial_window_top") or 10

    if h and w and x and y:
        pass ### self.setTopGeometry(w,h,x,y)
#@-node:ekr.20071001091231.81:setInitialWindowGeometry (swingFrame)
#@+node:ekr.20071001091231.82:setTabWidth (swingFrame)
def setTabWidth (self, w):

    pass

    # try: # This can fail when called from scripts
        # # Use the present font for computations.
        # font = self.bodyCtrl.cget("font")
        # root = g.app.root # 4/3/03: must specify root so idle window will work properly.
        # font = swingFont.Font(root=root,font=font)
        # tabw = font.measure(" " * abs(w)) # 7/2/02
        # self.bodyCtrl.configure(tabs=tabw)
        # self.tab_width = w
        # # g.trace(w,tabw)
    # except:
        # g.es_exception()
        # pass
#@-node:ekr.20071001091231.82:setTabWidth (swingFrame)
#@+node:ekr.20071001091231.83:setWrap (swingFrame)
def setWrap (self,p):

    c = self.c ; w = c.frame.body.bodyCtrl

    theDict = g.scanDirectives(c,p)
    if not theDict: return

    wrap = theDict.get("wrap")

    ### if self.body.wrapState == wrap: return

    self.body.wrapState = wrap
    # g.trace(wrap)

    ### Rewrite for swing.
#@nonl
#@-node:ekr.20071001091231.83:setWrap (swingFrame)
#@+node:ekr.20071001091231.84:setTopGeometry (swingFrame)
def setTopGeometry(self,w,h,x,y,adjustSize=True):

    # Put the top-left corner on the screen.
    x = max(10,x) ; y = max(10,y)

    if adjustSize:
        top = self.top
        sw = top.winfo_screenwidth()
        sh = top.winfo_screenheight()

        # Adjust the size so the whole window fits on the screen.
        w = min(sw-10,w)
        h = min(sh-10,h)

        # Adjust position so the whole window fits on the screen.
        if x + w > sw: x = 10
        if y + h > sh: y = 10

    geom = "%dx%d%+d%+d" % (w,h,x,y)

    self.top.geometry(geom)
#@-node:ekr.20071001091231.84:setTopGeometry (swingFrame)
#@+node:ekr.20071001091231.85:reconfigurePanes (use config bar_width) (swingFrame)
def reconfigurePanes (self):

    c = self.c

    border = c.config.getInt('additional_body_text_border')
    if border == None: border = 0

    # The body pane needs a _much_ bigger border when tiling horizontally.
    border = g.choose(self.splitVerticalFlag,2+border,6+border)
    ### self.bodyCtrl.configure(bd=border)

    # The log pane needs a slightly bigger border when tiling vertically.
    border = g.choose(self.splitVerticalFlag,4,2) 
    ### self.log.configureBorder(border)
#@-node:ekr.20071001091231.85:reconfigurePanes (use config bar_width) (swingFrame)
#@+node:ekr.20071001091231.86:resizePanesToRatio (swingFrame)
def resizePanesToRatio(self,ratio,ratio2):

    # g.trace(ratio,ratio2,g.callers())

    self.divideLeoSplitter(self.splitVerticalFlag,ratio)
    self.divideLeoSplitter(not self.splitVerticalFlag,ratio2)
#@nonl
#@-node:ekr.20071001091231.86:resizePanesToRatio (swingFrame)
#@-node:ekr.20071001091231.77:Configuration (swingFrame)
#@+node:ekr.20071001091231.87:Event handlers (swingFrame)
#@+node:ekr.20071001091231.88:frame.OnCloseLeoEvent
# Called from quit logic and when user closes the window.
# Returns True if the close happened.

def OnCloseLeoEvent(self):

    f = self ; c = f.c

    if c.inCommand:
        # g.trace('requesting window close')
        c.requestCloseWindow = True
    else:
        g.app.closeLeoWindow(self)
#@-node:ekr.20071001091231.88:frame.OnCloseLeoEvent
#@+node:ekr.20071001091231.89:frame.OnControlKeyUp/Down
def OnControlKeyDown (self,event=None):

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

    self.controlKeyIsDown = True

def OnControlKeyUp (self,event=None):

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

    self.controlKeyIsDown = False
#@-node:ekr.20071001091231.89:frame.OnControlKeyUp/Down
#@+node:ekr.20071001091231.90:OnActivateBody (swingFrame)
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.bodyWantsFocus()
    except:
        g.es_event_exception("activate body")

    return 'break'
#@-node:ekr.20071001091231.90:OnActivateBody (swingFrame)
#@+node:ekr.20071001091231.91:OnActivateLeoEvent, OnDeactivateLeoEvent
def OnActivateLeoEvent(self,event=None):

    '''Handle a click anywhere in the Leo window.'''

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

    self.c.setLog()

def OnDeactivateLeoEvent(self,event=None):

    pass # This causes problems on the Mac.
#@-node:ekr.20071001091231.91:OnActivateLeoEvent, OnDeactivateLeoEvent
#@+node:ekr.20071001091231.92:OnActivateTree
def OnActivateTree (self,event=None):

    try:
        frame = self ; c = frame.c
        c.setLog()

        if 0: # Do NOT do this here!
            # OnActivateTree can get called when the tree gets DE-activated!!
            c.bodyWantsFocus()

    except:
        g.es_event_exception("activate tree")
#@-node:ekr.20071001091231.92:OnActivateTree
#@+node:ekr.20071001091231.93: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)
        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.
        g.doHook("bodyrclick2",c=c,p=p,v=p,event=event)
    except:
        g.es_event_exception("iconrclick")
#@-node:ekr.20071001091231.93:OnBodyClick, OnBodyRClick (Events)
#@+node:ekr.20071001091231.94:OnBodyDoubleClick (Events)
def OnBodyDoubleClick (self,event=None):

    try:
        c = self.c ; p = c.currentPosition()
        if event and not g.doHook("bodydclick1",c=c,p=p,v=p,event=event):
            c.editCommands.extendToWord(event) # Handles unicode properly.
        g.doHook("bodydclick2",c=c,p=p,v=p,event=event)
    except:
        g.es_event_exception("bodydclick")

    return "break" # Restore this to handle proper double-click logic.
#@-node:ekr.20071001091231.94:OnBodyDoubleClick (Events)
#@+node:ekr.20071001091231.95:OnMouseWheel (Tomaz Ficko)
# Contributed by Tomaz Ficko.  This works on some systems.
# On XP it causes a crash in tcl83.dll.  Clearly a Tk bug.

def OnMouseWheel(self, event=None):

    try:
        if event.delta < 1:
            self.canvas.yview(Tk.SCROLL, 1, Tk.UNITS)
        else:
            self.canvas.yview(Tk.SCROLL, -1, Tk.UNITS)
    except:
        g.es_event_exception("scroll wheel")

    return "break"
#@-node:ekr.20071001091231.95:OnMouseWheel (Tomaz Ficko)
#@-node:ekr.20071001091231.87:Event handlers (swingFrame)
#@+node:ekr.20071001091231.96:Gui-dependent commands
#@+node:ekr.20071001091231.97:Minibuffer commands... (swingFrame)

#@+node:ekr.20071001091231.98:contractPane
def contractPane (self,event=None):

    '''Contract the selected pane.'''

    f = self ; c = f.c
    w = c.get_requested_focus()
    wname = c.widget_name(w)

    # g.trace(wname)
    if not w: return

    if wname.startswith('body'):
        f.contractBodyPane()
    elif wname.startswith('log'):
        f.contractLogPane()
    elif wname.startswith('head') or wname.startswith('canvas'):
        f.contractOutlinePane()
#@-node:ekr.20071001091231.98:contractPane
#@+node:ekr.20071001091231.99:expandPane
def expandPane (self,event=None):

    '''Expand the selected pane.'''

    f = self ; c = f.c

    w = c.get_requested_focus()
    wname = c.widget_name(w)

    # g.trace(wname)
    if not w: return

    if wname.startswith('body'):
        f.expandBodyPane()
    elif wname.startswith('log'):
        f.expandLogPane()
    elif wname.startswith('head') or wname.startswith('canvas'):
        f.expandOutlinePane()
#@-node:ekr.20071001091231.99:expandPane
#@+node:ekr.20071001091231.100:fullyExpandPane
def fullyExpandPane (self,event=None):

    '''Fully expand the selected pane.'''

    f = self ; c = f.c

    w = c.get_requested_focus()
    wname = c.widget_name(w)

    # g.trace(wname)
    if not w: return

    if wname.startswith('body'):
        f.fullyExpandBodyPane()
    elif wname.startswith('log'):
        f.fullyExpandLogPane()
    elif wname.startswith('head') or wname.startswith('canvas'):
        f.fullyExpandOutlinePane()
#@-node:ekr.20071001091231.100:fullyExpandPane
#@+node:ekr.20071001091231.101:hidePane
def hidePane (self,event=None):

    '''Completely contract the selected pane.'''

    f = self ; c = f.c

    w = c.get_requested_focus()
    wname = c.widget_name(w)

    g.trace(wname)
    if not w: return

    if wname.startswith('body'):
        f.hideBodyPane()
        c.treeWantsFocusNow()
    elif wname.startswith('log'):
        f.hideLogPane()
        c.bodyWantsFocusNow()
    elif wname.startswith('head') or wname.startswith('canvas'):
        f.hideOutlinePane()
        c.bodyWantsFocusNow()
#@-node:ekr.20071001091231.101:hidePane
#@+node:ekr.20071001091231.102:expand/contract/hide...Pane
@ The first arg to divideLeoSplitter means the following:

    f.splitVerticalFlag: use the primary   (tree/body) ratio.
not f.splitVerticalFlag: use the secondary (tree/log) ratio.
@c

def contractBodyPane (self,event=None):
    '''Contract the body pane.'''
    f = self ; r = min(1.0,f.ratio+0.1)
    f.divideLeoSplitter(f.splitVerticalFlag,r)

def contractLogPane (self,event=None):
    '''Contract the log pane.'''
    f = self ; r = min(1.0,f.ratio+0.1)
    f.divideLeoSplitter(not f.splitVerticalFlag,r)

def contractOutlinePane (self,event=None):
    '''Contract the outline pane.'''
    f = self ; r = max(0.0,f.ratio-0.1)
    f.divideLeoSplitter(f.splitVerticalFlag,r)

def expandBodyPane (self,event=None):
    '''Expand the body pane.'''
    self.contractOutlinePane()

def expandLogPane(self,event=None):
    '''Expand the log pane.'''
    f = self ; r = max(0.0,f.ratio-0.1)
    f.divideLeoSplitter(not f.splitVerticalFlag,r)

def expandOutlinePane (self,event=None):
    '''Expand the outline pane.'''
    self.contractBodyPane()
#@-node:ekr.20071001091231.102:expand/contract/hide...Pane
#@+node:ekr.20071001091231.103:fullyExpand/hide...Pane
def fullyExpandBodyPane (self,event=None):
    '''Fully expand the body pane.'''
    f = self ; f.divideLeoSplitter(f.splitVerticalFlag,0.0)

def fullyExpandLogPane (self,event=None):
    '''Fully expand the log pane.'''
    f = self ; f.divideLeoSplitter(not f.splitVerticalFlag,0.0)

def fullyExpandOutlinePane (self,event=None):
    '''Fully expand the outline pane.'''
    f = self ; f.divideLeoSplitter(f.splitVerticalFlag,1.0)

def hideBodyPane (self,event=None):
    '''Completely contract the body pane.'''
    f = self ; f.divideLeoSplitter(f.splitVerticalFlag,1.0)

def hideLogPane (self,event=None):
    '''Completely contract the log pane.'''
    f = self ; f.divideLeoSplitter(not f.splitVerticalFlag,1.0)

def hideOutlinePane (self,event=None):
    '''Completely contract the outline pane.'''
    f = self ; f.divideLeoSplitter(f.splitVerticalFlag,0.0)
#@-node:ekr.20071001091231.103:fullyExpand/hide...Pane
#@-node:ekr.20071001091231.97:Minibuffer commands... (swingFrame)
#@+node:ekr.20071001091231.104:Window Menu...
#@+node:ekr.20071001091231.105:toggleActivePane
def toggleActivePane (self,event=None):

    '''Toggle the focus between the outline and body panes.'''

    frame = self ; c = frame.c

    if c.get_focus() == frame.body.bodyCtrl: # 2007:10/25
        c.treeWantsFocusNow()
    else:
        c.endEditing()
        c.bodyWantsFocusNow()
#@-node:ekr.20071001091231.105:toggleActivePane
#@+node:ekr.20071001091231.106:cascade
def cascade (self,event=None):

    '''Cascade all Leo windows.'''

    x,y,delta = 10,10,10
    for frame in g.app.windowList:
        top = frame.top

        # Compute w,h
        top.update_idletasks() # Required to get proper info.
        geom = top.geometry() # geom = "WidthxHeight+XOffset+YOffset"
        dim,junkx,junky = string.split(geom,'+')
        w,h = string.split(dim,'x')
        w,h = int(w),int(h)

        # Set new x,y and old w,h
        frame.setTopGeometry(w,h,x,y,adjustSize=False)

        # Compute the new offsets.
        x += 30 ; y += 30
        if x > 200:
            x = 10 + delta ; y = 40 + delta
            delta += 10
#@-node:ekr.20071001091231.106:cascade
#@+node:ekr.20071001091231.107:equalSizedPanes
def equalSizedPanes (self,event=None):

    '''Make the outline and body panes have the same size.'''

    frame = self
    frame.resizePanesToRatio(0.5,frame.secondary_ratio)
#@-node:ekr.20071001091231.107:equalSizedPanes
#@+node:ekr.20071001091231.108:hideLogWindow
def hideLogWindow (self,event=None):

    frame = self
    frame.divideLeoSplitter2(0.99, not frame.splitVerticalFlag)
#@-node:ekr.20071001091231.108:hideLogWindow
#@+node:ekr.20071001091231.109:minimizeAll
def minimizeAll (self,event=None):

    '''Minimize all Leo's windows.'''

    self.minimize(g.app.pythonFrame)
    for frame in g.app.windowList:
        self.minimize(frame)
        self.minimize(frame.findPanel)

def minimize(self,frame):

    if frame and frame.top.state() == "normal":
        frame.top.iconify()
#@-node:ekr.20071001091231.109:minimizeAll
#@+node:ekr.20071001091231.110:toggleSplitDirection (swingFrame)
# The key invariant: self.splitVerticalFlag tells the alignment of the main splitter.

def toggleSplitDirection (self,event=None):

    '''Toggle the split direction in the present Leo window.'''

    # Switch directions.
    c = self.c
    self.splitVerticalFlag = not self.splitVerticalFlag
    orientation = g.choose(self.splitVerticalFlag,"vertical","horizontal")
    c.config.set("initial_splitter_orientation","string",orientation)

    self.toggleTkSplitDirection(self.splitVerticalFlag)
#@+node:ekr.20071001091231.111:toggleTkSplitDirection
def toggleTkSplitDirection (self,verticalFlag):

    # Abbreviations.
    frame = self
    bar1 = self.bar1 ; bar2 = self.bar2
    split1Pane1,split1Pane2 = self.split1Pane1,self.split1Pane2
    split2Pane1,split2Pane2 = self.split2Pane1,self.split2Pane2
    # Reconfigure the bars.
    bar1.place_forget()
    bar2.place_forget()
    self.configureBar(bar1,verticalFlag)
    self.configureBar(bar2,not verticalFlag)
    # Make the initial placements again.
    self.placeSplitter(bar1,split1Pane1,split1Pane2,verticalFlag)
    self.placeSplitter(bar2,split2Pane1,split2Pane2,not verticalFlag)
    # Adjust the log and body panes to give more room around the bars.
    self.reconfigurePanes()
    # Redraw with an appropriate ratio.
    vflag,ratio,secondary_ratio = frame.initialRatios()
    self.resizePanesToRatio(ratio,secondary_ratio)
#@-node:ekr.20071001091231.111:toggleTkSplitDirection
#@-node:ekr.20071001091231.110:toggleSplitDirection (swingFrame)
#@+node:ekr.20071001091231.112:resizeToScreen
def resizeToScreen (self,event=None):

    '''Resize the Leo window so it fill the entire screen.'''

    top = self.top

    w = top.winfo_screenwidth()
    h = top.winfo_screenheight()

    if sys.platform.startswith('win'):
        top.state('zoomed')
    elif sys.platform == 'darwin':
        # Must leave room to get at very small resizing area.
        geom = "%dx%d%+d%+d" % (w-20,h-55,10,25)
        top.geometry(geom)
    else:
        # Fill almost the entire screen.
        # Works on Windows. YMMV for other platforms.
        geom = "%dx%d%+d%+d" % (w-8,h-46,0,0)
        top.geometry(geom)
#@-node:ekr.20071001091231.112:resizeToScreen
#@-node:ekr.20071001091231.104:Window Menu...
#@+node:ekr.20071001091231.113:Help Menu...
#@+node:ekr.20071001091231.114:leoHelp
def leoHelp (self,event=None):

    '''Open Leo's offline tutorial.'''

    frame = self ; c = frame.c

    theFile = g.os_path_join(g.app.loadDir,"..","doc","sbooks.chm")

    if g.os_path_exists(theFile):
        os.startfile(theFile)
    else:
        answer = g.app.gui.runAskYesNoDialog(c,
            "Download Tutorial?",
            "Download tutorial (sbooks.chm) from SourceForge?")

        if answer == "yes":
            try:
                if 0: # Download directly.  (showProgressBar needs a lot of work)
                    url = "http://umn.dl.sourceforge.net/sourceforge/leo/sbooks.chm"
                    import urllib
                    self.scale = None
                    urllib.urlretrieve(url,theFile,self.showProgressBar)
                    if self.scale:
                        self.scale.destroy()
                        self.scale = None
                else:
                    url = "http://prdownloads.sourceforge.net/leo/sbooks.chm?download"
                    import webbrowser
                    os.chdir(g.app.loadDir)
                    webbrowser.open_new(url)
            except:
                g.es("exception dowloading sbooks.chm")
                g.es_exception()
#@+node:ekr.20071001091231.115:showProgressBar
def showProgressBar (self,count,size,total):

    # g.trace("count,size,total:",count,size,total)
    if self.scale == None:
        << create the scale widget >>
    self.scale.set(count*size)
    self.scale.update_idletasks()
#@+node:ekr.20071001091231.116:<< create the scale widget >>
top = Tk.Toplevel()
top.title("Download progress")
self.scale = scale = Tk.Scale(top,state="normal",orient="horizontal",from_=0,to=total)
scale.pack()
top.lift()
#@-node:ekr.20071001091231.116:<< create the scale widget >>
#@-node:ekr.20071001091231.115:showProgressBar
#@-node:ekr.20071001091231.114:leoHelp
#@-node:ekr.20071001091231.113:Help Menu...
#@-node:ekr.20071001091231.96:Gui-dependent commands
#@+node:ekr.20071001091231.117:Delayed Focus (swingFrame)
@ New in 4.3. The proper way to change focus is to call c.frame.xWantsFocus.

Important: This code never calls select, so there can be no race condition here
that alters text improperly.
#@-node:ekr.20071001091231.117:Delayed Focus (swingFrame)
#@+node:ekr.20071001091231.118:Tk bindings... (swingFrame)
def bringToFront (self):
    # g.trace(g.callers())
    self.top.deiconify()
    self.top.lift()

def getFocus(self):
    """Returns the widget that has focus, or body if None."""
    try:
        # This method is unreliable while focus is changing.
        # The call to update_idletasks may help.  Or not.
        self.top.update_idletasks()
        f = self.top.focus_displayof()
    except Exception:
        f = None
    if f:
        return f
    else:
        return self.body.bodyCtrl

def getTitle (self):
    return self.top.title()

def setTitle (self,title):
    return self.top.title(title)

def get_window_info(self):
    return g.app.gui.get_window_info(self.top)

def iconify(self):
    self.top.iconify()

def deiconify (self):
    self.top.deiconify()

def lift (self):
    self.top.lift()

def update (self):
    self.top.update()
#@-node:ekr.20071001091231.118:Tk bindings... (swingFrame)
#@-node:ekr.20070930105601.2:class leoSwingFrame
#@+node:ekr.20070930110535:class leoSwingBody
class leoSwingBody (leoFrame.leoBody):

    ###

    # def __init__ (self,frame,parentFrame):
        # # g.trace('leoSwingBody')
        # leoFrame.leoBody.__init__(self,frame,parentFrame) # Init the base class.

    # # Birth, death & config...
    # def createBindings (self,w=None):         pass
    # def createControl (self,parentFrame,p):   pass
    # def setColorFromConfig (self,w=None):     pass
    # def setFontFromConfig (self,w=None):      pass

    # # Editor...
    # def createEditorLabel (self,pane):  pass
    # def setEditorColors (self,bg,fg):   pass

    # # Events...
    # def scheduleIdleTimeRoutine (self,function,*args,**keys): pass

    @others
#@+node:ekr.20071001091231.3: Birth & death
#@+node:ekr.20071001091231.4:swingBody. __init__
def __init__ (self,frame,parentFrame):

    g.trace('leoSwingBody')

    # Call the base class constructor.
    leoFrame.leoBody.__init__(self,frame,parentFrame)

    c = self.c ; p = c.currentPosition()
    self.editor_name = None
    self.editor_v = None

    self.trace_onBodyChanged = c.config.getBool('trace_onBodyChanged')
    self.bodyCtrl = self.createControl(parentFrame,p)
    self.colorizer = leoColor.colorizer(c)
#@-node:ekr.20071001091231.4:swingBody. __init__
#@+node:ekr.20071001091231.5:swingBody.createBindings
def createBindings (self,w=None):

    '''(swingBody) 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

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

    # for kind,func,handler in (
        # ('<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),
    # ):
        # def bodyClickCallback(event,handler=handler,func=func):
            # return handler(event,func)

        # w.bind(kind,bodyClickCallback)
#@nonl
#@-node:ekr.20071001091231.5:swingBody.createBindings
#@+node:ekr.20071001091231.6:swingBody.createControl
def createControl (self,parentFrame,p):

    c = self.c

    g.trace('swingBody')

    # New in 4.4.1: make the parent frame a PanedWidget.
    self.numberOfEditors = 1 ; name = '1'
    self.totalNumberOfEditors = 1

    orient = c.config.getString('editor_orientation') or 'horizontal'
    if orient not in ('horizontal','vertical'): orient = 'horizontal'

    # self.pb = pb = Pmw.PanedWidget(parentFrame,orient=orient)
    # parentFrame = pb.add(name)
    # pb.pack(expand=1,fill='both') # Must be done after the first page created.

    w = self.createTextWidget(parentFrame,p,name)
    self.editorWidgets[name] = w

    return w
#@-node:ekr.20071001091231.6:swingBody.createControl
#@+node:ekr.20071001091231.7:swingBody.createTextWidget
def createTextWidget (self,parentFrame,p,name):

    c = self.c

    # parentFrame.configure(bg='LightSteelBlue1')

    wrap = c.config.getBool('body_pane_wraps')
    wrap = g.choose(wrap,"word","none")

    # # Setgrid=1 cause severe problems with the font panel.
    body = w = leoSwingTextWidget (parentFrame,name='body-pane',
        bd=2,bg="white",relief="flat",setgrid=0,wrap=wrap)

    # bodyBar = Tk.Scrollbar(parentFrame,name='bodyBar')

    # def yscrollCallback(x,y,bodyBar=bodyBar,w=w):
        # # g.trace(x,y,g.callers())
        # if hasattr(w,'leo_scrollBarSpot'):
            # w.leo_scrollBarSpot = (x,y)
        # return bodyBar.set(x,y)

    # body['yscrollcommand'] = yscrollCallback # bodyBar.set

    # bodyBar['command'] =  body.yview
    # bodyBar.pack(side="right", fill="y")

    # # Always create the horizontal bar.
    # bodyXBar = Tk.Scrollbar(
        # parentFrame,name='bodyXBar',orient="horizontal")
    # body['xscrollcommand'] = bodyXBar.set
    # bodyXBar['command'] = body.xview

    # if wrap == "none":
        # # g.trace(parentFrame)
        # bodyXBar.pack(side="bottom", fill="x")

    # body.pack(expand=1,fill="both")

    # self.wrapState = wrap

    # if 0: # Causes the cursor not to blink.
        # body.configure(insertofftime=0)

    # # Inject ivars
    if name == '1':
        w.leo_p = w.leo_v = None # Will be set when the second editor is created.
    else:
        w.leo_p = p.copy()
        w.leo_v = w.leo_p.v
            # pychecker complains body.leo_p does not exist.
    w.leo_active = True
    w.leo_bodyBar = bodyBar
    w.leo_bodyXBar = bodyXBar
    w.leo_chapter = None
    w.leo_frame = parentFrame
    w.leo_name = name
    w.leo_label = None
    w.leo_label_s = None
    w.leo_scrollBarSpot = None
    w.leo_insertSpot = None
    w.leo_selection = None

    return w
#@-node:ekr.20071001091231.7:swingBody.createTextWidget
#@-node:ekr.20071001091231.3: Birth & death
#@+node:ekr.20071001091231.8:swingBody.setColorFromConfig
def setColorFromConfig (self,w=None):

    c = self.c
    if w is None: w = self.bodyCtrl

    return ###

    bg = c.config.getColor("body_text_background_color") or 'white'
    # g.trace(id(w),bg)

    try: w.configure(bg=bg)
    except:
        g.es("exception setting body text background color")
        g.es_exception()

    fg = c.config.getColor("body_text_foreground_color") or 'black'
    try: w.configure(fg=fg)
    except:
        g.es("exception setting body textforeground color")
        g.es_exception()

    bg = c.config.getColor("body_insertion_cursor_color")
    if bg:
        try: w.configure(insertbackground=bg)
        except:
            g.es("exception setting body pane cursor color")
            g.es_exception()

    sel_bg = c.config.getColor('body_text_selection_background_color') or 'Gray80'
    try: w.configure(selectbackground=sel_bg)
    except Exception:
        g.es("exception setting body pane text selection background color")
        g.es_exception()

    sel_fg = c.config.getColor('body_text_selection_foreground_color') or 'white'
    try: w.configure(selectforeground=sel_fg)
    except Exception:
        g.es("exception setting body pane text selection foreground color")
        g.es_exception()

    if sys.platform != "win32": # Maybe a Windows bug.
        fg = c.config.getColor("body_cursor_foreground_color")
        bg = c.config.getColor("body_cursor_background_color")
        if fg and bg:
            cursor="xterm" + " " + fg + " " + bg
            try: w.configure(cursor=cursor)
            except:
                import traceback ; traceback.print_exc()
#@-node:ekr.20071001091231.8:swingBody.setColorFromConfig
#@+node:ekr.20071001091231.9:swingBody.setFontFromConfig
def setFontFromConfig (self,w=None):

    c = self.c

    if not w: w = self.bodyCtrl

    font = c.config.getFontFromParams(
        "body_text_font_family", "body_text_font_size",
        "body_text_font_slant",  "body_text_font_weight",
        c.config.defaultBodyFontSize)

    self.fontRef = font # ESSENTIAL: retain a link to font.
    ### w.configure(font=font)

    # g.trace("BODY",body.cget("font"),font.cget("family"),font.cget("weight"))
#@-node:ekr.20071001091231.9:swingBody.setFontFromConfig
#@+node:ekr.20071001091231.10:Focus (swingBody)
def hasFocus (self):

    return self.bodyCtrl == self.frame.top.focus_displayof()

def setFocus (self):

    self.c.widgetWantsFocus(self.bodyCtrl)
#@-node:ekr.20071001091231.10:Focus (swingBody)
#@+node:ekr.20071001091231.11:forceRecolor
def forceFullRecolor (self):

    self.forceFullRecolorFlag = True
#@-node:ekr.20071001091231.11:forceRecolor
#@+node:ekr.20071001091231.12:Tk bindings (swingBbody)
#@+node:ekr.20071001193426:bind (new)
def bind (self,*args,**keys):

    pass
#@-node:ekr.20071001193426:bind (new)
#@+node:ekr.20071001091231.13:Tags (Tk spelling) (swingBody)
def tag_add (self,tagName,index1,index2):
    self.bodyCtrl.tag_add(tagName,index1,index2)

def tag_bind (self,tagName,event,callback):
    self.bodyCtrl.tag_bind(tagName,event,callback)

def tag_configure (self,colorName,**keys):
    self.bodyCtrl.tag_configure(colorName,keys)

def tag_delete(self,tagName):
    self.bodyCtrl.tag_delete(tagName)

def tag_names(self,*args): # New in Leo 4.4.1.
    return self.bodyCtrl.tag_names(*args)

def tag_remove (self,tagName,index1,index2):
    return self.bodyCtrl.tag_remove(tagName,index1,index2)
#@-node:ekr.20071001091231.13:Tags (Tk spelling) (swingBody)
#@+node:ekr.20071001091231.14:Configuration (Tk spelling) (swingBody)
def cget(self,*args,**keys):

    body = self ; w = self.bodyCtrl
    val = w.cget(*args,**keys)

    if g.app.trace:
        g.trace(val,args,keys)

    return val

def configure (self,*args,**keys):

    # g.trace(args,keys)

    body = self ; w = body.bodyCtrl
    return w.configure(*args,**keys)
#@-node:ekr.20071001091231.14:Configuration (Tk spelling) (swingBody)
#@+node:ekr.20071001091231.15:Height & width (swingBody)
def getBodyPaneHeight (self):

    return self.bodyCtrl.winfo_height()

def getBodyPaneWidth (self):

    return self.bodyCtrl.winfo_width()
#@-node:ekr.20071001091231.15:Height & width (swingBody)
#@+node:ekr.20071001091231.16:Idle time... (swingBody)
def scheduleIdleTimeRoutine (self,function,*args,**keys):

    pass ### self.bodyCtrl.after_idle(function,*args,**keys)
#@-node:ekr.20071001091231.16:Idle time... (swingBody)
#@+node:ekr.20071001091231.17:Menus (swingBody)
def bind (self,*args,**keys):

    pass ### return self.bodyCtrl.bind(*args,**keys)
#@-node:ekr.20071001091231.17:Menus (swingBody)
#@+node:ekr.20071001091231.18:Text (now in base class) (swingBody)
# def getAllText (self):              return self.bodyCtrl.getAllText()
# def getInsertPoint(self):           return self.bodyCtrl.getInsertPoint()
# def getSelectedText (self):         return self.bodyCtrl.getSelectedText()
# def getSelectionRange (self,sort=True): return self.bodyCtrl.getSelectionRange(sort)
# def hasTextSelection (self):        return self.bodyCtrl.hasSelection()
# # def scrollDown (self):            g.app.gui.yscroll(self.bodyCtrl,1,'units')
# # def scrollUp (self):              g.app.gui.yscroll(self.bodyCtrl,-1,'units')
# def see (self,index):               self.bodyCtrl.see(index)
# def seeInsertPoint (self):          self.bodyCtrl.seeInsertPoint()
# def selectAllText (self,event=None):
    # w = g.app.gui.eventWidget(event) or self.bodyCtrl
    # return w.selectAllText()
# def setInsertPoint (self,pos):      return self.bodyCtrl.getInsertPoint(pos)
# def setSelectionRange (self,sel):
    # i,j = sel
    # self.bodyCtrl.setSelectionRange(i,j)
#@nonl
#@-node:ekr.20071001091231.18:Text (now in base class) (swingBody)
#@-node:ekr.20071001091231.12:Tk bindings (swingBbody)
#@+node:ekr.20071001091231.19:Editors (swingBody)
#@+node:ekr.20071001091231.20:createEditorFrame
def createEditorFrame (self,pane):

    f = Tk.Frame(pane)
    f.pack(side='top',expand=1,fill='both')
    return f
#@-node:ekr.20071001091231.20:createEditorFrame
#@+node:ekr.20071001091231.21:packEditorLabelWidget
def packEditorLabelWidget (self,w):

    '''Create a Tk label widget.'''

    if not hasattr(w,'leo_label') or not w.leo_label:
        # g.trace('w.leo_frame',id(w.leo_frame))
        w.pack_forget()
        w.leo_label = Tk.Label(w.leo_frame)
        w.leo_label.pack(side='top')
        w.pack(expand=1,fill='both')
#@nonl
#@-node:ekr.20071001091231.21:packEditorLabelWidget
#@+node:ekr.20071001091231.22: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()
            # pass
#@-node:ekr.20071001091231.22:setEditorColors
#@-node:ekr.20071001091231.19:Editors (swingBody)
#@-node:ekr.20070930110535:class leoSwingBody
#@+node:ekr.20070930111347:class leoSwingKeys
class swingKeyHandlerClass (leoKeys.keyHandlerClass):

    '''swing overrides of base keyHandlerClass.'''

    def __init__(self,c,useGlobalKillbuffer=False,useGlobalRegisters=False):

        # g.trace('swingKeyHandlerClass',c)

        # Init the base class.
        leoKeys.keyHandlerClass.__init__(self,c,useGlobalKillbuffer,useGlobalRegisters)
#@-node:ekr.20070930111347:class leoSwingKeys
#@+node:ekr.20071001173734:class leoSwingMenu
class leoSwingMenu( leoMenu.leoMenu ):

    @others
#@nonl
#@+node:ekr.20071001173734.1: leoSwingMenu.__init__
def __init__ (self,frame):

    if 0:
        ld = io.File( g.app.loadDir )
        ijcl.addToSearchPath( ld )
        ijcl.beginLoading()
        self.font = frame.top.getFont()
        self.executor = java.util.concurrent.Executors.newCachedThreadPool()
        self.queue = java.util.concurrent.LinkedBlockingQueue()
        self.menu_changer = self.MenuChanger( self.queue )
        self.names_and_commands = {}
        self.keystrokes_and_actions = {}

    leoMenu.leoMenu.__init__( self, frame )

    #self.createLeoSwingPrint()
    #self.defineLeoSwingPrintTable()
    #self.addCommanderSupplemental()








#@-node:ekr.20071001173734.1: leoSwingMenu.__init__
#@+node:ekr.20071001173943:not ready yet
if 0:
    @others
#@nonl
#@+node:ekr.20071001173734.2:class MenuChanger
class MenuChanger( java.lang.Runnable, java.util.concurrent.Callable ):

    def __init__( self, queue ):
        self.queue = queue

    def run( self ):

        ft = java.util.concurrent.FutureTask( self )
        java.awt.EventQueue.invokeLater( ft )


    def call( self ):

        menu , name , label, enabled = self.queue.take() 
        target = None
        for z in menu.getMenuComponents():
            if hasattr( z, "getText" ) and z.getText() == name:
                target = z
                break


        if target:
            target.setText( label )
            target.setEnabled( enabled )
#@-node:ekr.20071001173734.2:class MenuChanger
#@+node:ekr.20071001173734.3:print menu stuff...

#@+node:ekr.20071001173734.4:defineLeoSwingPrintTable
def defineLeoSwingPrintTable( self ):

    self.printNodeTable= (

    ( "Print Current Node" , None, lambda event: self.lsp.printNode() ),
    ( "Print Current Node as HTML", None, lambda event: self.lsp.printNode( type = "HTML" ) ),
    ( "Print Marked Nodes", None, lambda event:  self.lsp.printMarkedNodes() ),
    ( "Print Marked Nodes as HTML", None, lambda event: self.lsp.printNode( type ="HTML" ) ),

    )

    for z in self.printNodeTable:
        self.names_and_commands[ z[ 0 ] ] = z[ 2 ]
#@-node:ekr.20071001173734.4:defineLeoSwingPrintTable
#@+node:ekr.20071001173734.5:createLeoSwingPrintMenu
def createLeoSwingPrintMenu( self ):

    fmenu = self.getMenu( "File" )

    components = fmenu.getMenuComponents()

    x = 0
    for z in components:

        if hasattr( z, 'getText' ) and z.getText() == "Recent Files...":
            break
        x += 1


    spot = x + 1

    pmenu = swing.JMenu( "Printing" )

    pnodes = swing.JMenu( "Print Nodes" )
    pmenu.add( pnodes )
    for z in self.printNodeTable:
        item = swing.JMenuItem( z[ 0 ] )
        item.actionPerformed = z[ 2 ]
        pnodes.add( item )

    sep = swing.JSeparator()
    fmenu.add( sep, spot  )
    fmenu.add( pmenu, spot + 1 )

    print_tree = swing.JMenuItem( "Print Tree As Is" )
    print_tree.actionPerformed = self.lsp.printTreeAsIs
    pmenu.add( print_tree )
    self.names_and_commands[ "Print Tree As Is" ] = self.lsp.printTreeAsIs
    print_as_more = swing.JMenuItem( "Print Outline in More Format" )
    print_as_more.actionPerformed = self.lsp.printOutlineAsMore
    self.names_and_commands[ "Print Outline in More Formet" ] = self.lsp.printOutlineAsMore
    pmenu.add( print_as_more )











#@-node:ekr.20071001173734.5:createLeoSwingPrintMenu
#@+node:ekr.20071001173734.6:createLeoSwingPrint
def createLeoSwingPrint( self ):

    c = self.c
    import leoSwingPrint
    lsp = leoSwingPrint.leoSwingPrint( c )
    menu = lsp.getAsMenu()

    fmenu = self.getMenu( "File" )

    components = fmenu.getMenuComponents()

    x = 0
    for z in components:

        if hasattr( z, 'getText' ) and z.getText() == "Recent Files...":
            break
        x += 1


    spot = x + 1


    sep = swing.JSeparator()
    fmenu.add( sep, spot  )
    fmenu.add( menu, spot + 1 )


#@-node:ekr.20071001173734.6:createLeoSwingPrint
#@-node:ekr.20071001173734.3:print menu stuff...
#@+node:ekr.20071001173734.7:plugin menu stuff...
#@+node:ekr.20071001173734.8:createPluginMenu
def createPluginMenu( self ):

    top = self.getMenu( 'top' )
    oline = self.getMenu( 'Outline' )
    ind = top.getComponentIndex( oline ) + 1
    import leoSwingPluginManager
    self.plugin_menu = pmenu = leoSwingPluginManager.createPluginsMenu()
    #self.plugin_menu = pmenu = swing.JMenu( "Plugins" )
    top.add( pmenu, ind )
    #cpm = swing.JMenuItem( "Plugin Manager" )
    #cpm.actionPerformed = self.createPluginManager
    #pmenu.add( cpm )
    #pmenu.addSeparator()


    #self.names_and_commands[ "Plugin Manager" ] = self.createPluginManager


#@-node:ekr.20071001173734.8:createPluginMenu
#@+node:ekr.20071001173734.9:createPluginManager
def createPluginManager( self, event ):

    import leoSwingPluginManager as lspm
    lspm.topLevelMenu()

#@-node:ekr.20071001173734.9:createPluginManager
#@+node:ekr.20071001173734.10:getPluginMenu
def getPluginMenu( self ):

    return self.plugin_menu
#@-node:ekr.20071001173734.10:getPluginMenu
#@-node:ekr.20071001173734.7:plugin menu stuff...
#@+node:ekr.20071001173734.11:JythonShell stuff

#@+node:ekr.20071001173734.12:openJythonShell
def openJythonShell( self ):

    js = ijcl.getJythonShell()
    jd = js.getDelegate()
    config = g.app.config
    c = self.c

    import leoSwingFrame
    getColorInstance = leoSwingFrame.getColorInstance 

    colorconfig = js.getColorConfiguration()
    color = config.getColor( c, "jyshell_background" )
    colorconfig.setBackgroundColor( getColorInstance( color, awt.Color.WHITE ) )

    color = config.getColor( c, "jyshell_foreground" )
    colorconfig.setForegroundColor( getColorInstance( color, awt.Color.GRAY ) )

    color = config.getColor( c, "jyshell_keyword" )
    colorconfig.setKeywordColor( getColorInstance( color, awt.Color.GREEN ) )

    color = config.getColor( c, "jyshell_local" )
    colorconfig.setLocalColor( getColorInstance( color, awt.Color.ORANGE ) )

    color = config.getColor( c, "jyshell_ps1color" )
    colorconfig.setPromptOneColor( getColorInstance( color, awt.Color.BLUE ) )

    color = config.getColor( c, "jyshell_ps2color" )
    colorconfig.setPromptTwoColor( getColorInstance( color, awt.Color.GREEN ) )

    color = config.getColor( c, "jyshell_syntax" )
    colorconfig.setSyntaxColor( getColorInstance( color, awt.Color.RED ) )

    color = config.getColor( c, "jyshell_output" )
    colorconfig.setOutColor( getColorInstance( color, awt.Color.GRAY ) )

    color = config.getColor( c, "jyshell_error" )
    colorconfig.setErrColor( getColorInstance( color, awt.Color.RED ) )

    family = config.get( c, "jyshell_text_font_family", "family" )
    size = config.get( c, "jyshell_text_font_size", "size" )
    weight = config.get( c, "jyshell_text_font_weight", "weight" )
    slant = None
    font = config.getFontFromParams( c, "jyshell_text_font_family", "jyshell_text_font_size", None, "jyshell_text_font_weight")

    use_bgimage = g.app.config.getBool( c, "jyshell_background_image" )
    if use_bgimage:

        image_location = g.app.config.getString( c, "jyshell_image_location@as-filedialog" )
        test_if_exists = java.io.File( image_location )
        if test_if_exists.exists():
            ii = swing.ImageIcon( image_location )
            alpha = g.app.config.getFloat( c, "jyshell_background_alpha" )
            js.setBackgroundImage( ii.getImage(), float( alpha ) )

    if font:
        js.setFont( font )

    js.setVisible( True )
    widget = js.getWidget()
    log = self.c.frame.log    
    self.addMenuToJythonShell( js )
    log.addTab( "JythonShell", widget )
    log.selectTab( widget )


#@-node:ekr.20071001173734.12:openJythonShell
#@+node:ekr.20071001173734.13:addMenuToJythonShell
def addMenuToJythonShell( self, js ):

    c = self.c
    jd = js.getDelegate()
    jmenu = swing.JMenu( "Leo" )
    jd.addToMenu( jmenu )

    e = swing.JMenuItem( "Execute Node As Script" )  
    e.actionPerformed = lambda event, jd = jd: self.fireNodeAsScript( event, jd )
    jmenu.add( e )

    p = swing.JMenuItem( "Run Node in Pdb" )
    p.actionPerformed = self.getRunNodeInPdb( c, jd )
    jmenu.add( p )

    captext = "Capture Shell Input In Node"
    totext = "Turn Off Shell Input Capture"
    sc = swing.JMenuItem( captext )
    import org.leo.JTextComponentOutputStream as jtcos
    class logcontrol:
        def __init__( self, menu ):
            self.menu = menu
            self.loging = False
            self.ostream = jtcos( c.frame.body.editor.editor )

        def __call__( self, event ):  
            menu = self.menu
            loging = self.loging
            if not loging:
                js.addLogger( self.ostream )
                menu.setText( totext )
                self.loging = True
            else:
                js.removeLogger( self.ostream )
                menu.setText( captext )
                self.loging = False

    sc.actionPerformed = logcontrol( sc )           
    jmenu.add( sc )

    d = swing.JMenuItem( "Detach Shell" )
    class detacher( java.util.concurrent.Callable ):

        def __init__( self, menu ):
            self.menu = menu
            self.embeded = True
            js.setCloser( self )

        def call( self ):

            if self.embeded:
                log = c.frame.log
                widget = js.getWidget()
                log.removeTab( widget )
            else:
                widget = js.getWidget()
                parent = widget.getTopLevelAncestor()
                parent.dispose();

        def __call__( self, event ):
            d = self.menu
            text = d.getText()
            if( text == "Detach Shell" ):
                d.setText( "Retach Shell" )
                jf = swing.JFrame( "JythonShell" )
                widget = js.getWidget()
                log = c.frame.log 
                log.removeTab( widget )
                jf.add( widget )
                jf.setSize( 500, 500 )
                jf.visible = 1
                self.embeded = False
            else:
                d.setText( "Detach Shell" )
                widget = js.getWidget()
                parent = widget.getTopLevelAncestor()
                parent.dispose();
                log = c.frame.log
                log.addTab( "JythonShell", widget  )
                log.selectTab( widget ) 
                self.embeded = True

    d.actionPerformed = detacher( d )
    jmenu.add( d )    


#@-node:ekr.20071001173734.13:addMenuToJythonShell
#@+node:ekr.20071001173734.14:getInsertNodeIntoShell
def getInsertNodeIntoShell( self, c, jd ):

    jm = swing.JMenuItem( "Write Node Into Shell as Reference" )
    def writeNode( event ):

        cp = c.currentPosition()
        at = c.atFileCommands

        at.write(cp.copy(),nosentinels=True,toString=True,scriptWrite=True)
        data = at.stringOutput

        jtf = self._GetReferenceName( jd, data )
        jtf.rmv_spot = jd.insertWidget( jtf )
        jtf.requestFocusInWindow()

    jm.actionPerformed = writeNode
    return jm
#@-node:ekr.20071001173734.14:getInsertNodeIntoShell
#@+node:ekr.20071001173734.15:getInsertReferenceIntoLeo
def getInsertReferenceIntoLeo( self, jd ):

    jmi = swing.JMenuItem( "Insert Reference As Node" )

    def action( event ):

        jtf = self._GetReferenceAsObject( jd, self.c )
        jtf.rmv_spot = jd.insertWidget( jtf )
        jtf.requestFocusInWindow()

    jmi.actionPerformed = action
    return jmi
#@nonl
#@-node:ekr.20071001173734.15:getInsertReferenceIntoLeo
#@+node:ekr.20071001173734.16:getRunNodeInPdb
def getRunNodeInPdb( self, c, jd ):

    def runInPdb( event ):

        cp = c.currentPosition()
        name = cp.headString()
        name = name.split()[ 0 ]
        at = c.atFileCommands

        at.write(cp.copy(),nosentinels=True,toString=True,scriptWrite=True)
        data = at.stringOutput

        f = java.io.File.createTempFile( "leopdbrun", None )
        pw = java.io.PrintWriter( f )
        pw.println( "import pdb" )
        pw.println( "pdb.set_trace()" )
        for z in data.split( "\n" ):
            pw.println( z )            
        pw.close()
        f.deleteOnExit()       
        l = java.util.Vector()
        l.add( "execfile( '%s', globals(), locals())" % f.getAbsolutePath() )
        jd.processAsScript( l )


    return runInPdb      
#@-node:ekr.20071001173734.16:getRunNodeInPdb
#@+node:ekr.20071001173734.17:fireNodeAsScript
def fireNodeAsScript( self, event, jd ):

    c = self.c        
    cp = c.currentPosition()    
    at = c.atFileCommands 

    at.write(cp.copy(),nosentinels=True,toString=True,scriptWrite=True)
    data = at.stringOutput.split( '\n' ) 


    l = java.util.Vector()
    for z in data:
        l.add( java.lang.String( z ) )

    jd.processAsScript( l )
#@nonl
#@-node:ekr.20071001173734.17:fireNodeAsScript
#@+node:ekr.20071001173734.18:class _GetReferenceName
class _GetReferenceName( swing.JTextField, aevent.KeyListener ):


    def __init__( self, jd, data ):
        swing.JTextField.__init__( self )
        self.jd = jd
        self.data = data
        border = self.getBorder()
        tborder = sborder.TitledBorder( border )
        tborder.setTitle( "Choose Reference Name:" )
        self.setBorder( tborder )
        self.addKeyListener( self )
        self.rmv_spot = None

    def keyPressed( self, event ):

        kc = event.getKeyChar();
        if kc == '\n':
            self.execute()
        elif java.lang.Character.isWhitespace( kc ):
            event.consume

    def execute( self ):

        self.jd.setReference( self.getText(), self.data )
        if self.rmv_spot:
            self.jd.remove( self.rmv_spot)
        self.jd.requestFocusInWindow()

    def keyTyped( self, event ):

        kc = event.getKeyChar()
        if kc == '\n': return
        elif java.lang.Character.isWhitespace( kc ):
            event.consume()

    def keyReleased( self, event ):

        kc = event.getKeyChar()
        if kc == '\n': return
        elif java.lang.Character.isWhitespace( kc ):
            event.consume()


class _GetReferenceAsObject( _GetReferenceName ):

    def __init__( self, jd, c ):
        leoSwingMenu._GetReferenceName.__init__( self, jd, None )
        self.c = c
        border = self.getBorder()
        border.setTitle( "Which Reference To Insert:" )


    def execute( self ):

        ref = self.jd.getReference( self.getText() )
        if ref:
            self.c.beginUpdate()
            pos = self.c.currentPosition()
            npos = pos.insertAfter()
            npos.setHeadString( "Reference: %s" % self.getText() )
            npos.setTnodeText( str( ref ) )
            self.c.endUpdate()
        if self.rmv_spot:
            self.jd.remove( self.rmv_spot )
#@-node:ekr.20071001173734.18:class _GetReferenceName
#@-node:ekr.20071001173734.11:JythonShell stuff
#@+node:ekr.20071001173734.19:addUserGuide
def addUserGuide( self ):

    help = self.getMenu( 'Help' )
    c = self.c
    help.addSeparator()
    jmi = swing.JCheckBoxMenuItem( "View User Guide" )
    widgets = []
    def showUserGuide( event ):
        if jmi.getState() and not widgets:
            import leoSwingLeoTutorial
            lswlt = leoSwingLeoTutorial.leoSwingLeoTutorial()
            widget = lswlt.getWidget()
            widgets.append( widget )
            c.frame.body.addTab( "User Guide", widget )
        elif jmi.getState() and widgets:
            widget = widgets[ 0 ]
            c.frame.body.addTab( "User Guide", widget )
        else:
            widget = widgets[ 0 ]
            c.frame.body.removeTab( widget )


    jmi.actionPerformed = showUserGuide
    help.add( jmi )
#@-node:ekr.20071001173734.19:addUserGuide
#@+node:ekr.20071001173734.20:createRecentFilesMenuItems (leoMenu)
def createRecentFilesMenuItems (self):

    c = self.c ; frame = c.frame
    recentFilesMenu = self.getMenu("Recent Files...")

    # Delete all previous entries.
    if len( recentFilesMenu.getMenuComponents() ) != 0:
        deferable = lambda :self.delete_range(recentFilesMenu,0,len(c.recentFiles)+2)
        if not swing.SwingUtilities.isEventDispatchThread():
            dc = DefCallable( deferable )
            ft = dc.wrappedAsFutureTask()
            swing.SwingUtilities.invokeAndWait( ft )
        else:
            deferable()
    # Create the first two entries.
    table = (
        ("Clear Recent Files",None,c.clearRecentFiles),
        ("-",None,None))
    self.createMenuEntries(recentFilesMenu,table,init=True)

    # Create all the other entries.
    i = 3
    for name in c.recentFiles:
        def callback (event=None,c=c,name=name): # 12/9/03
            c.openRecentFile(name)
        label = "%d %s" % (i-2,g.computeWindowTitle(name))
        self.add_command(recentFilesMenu,label=label,command=callback,underline=0)
        i += 1
#@nonl
#@-node:ekr.20071001173734.20:createRecentFilesMenuItems (leoMenu)
#@+node:ekr.20071001173734.21:oops
def oops (self):

    print "leoMenu oops:", g.callerName(2), "should be overridden in subclass"
#@nonl
#@-node:ekr.20071001173734.21:oops
#@+node:ekr.20071001173734.22:Must be overridden in menu subclasses
#@+node:ekr.20071001173734.23:9 Routines with Tk spellings
def add_cascade (self,parent,label,menu,underline):

    menu.setText( label )

def add_command (self,menu,**keys):

    if keys[ 'label' ] == "Open Python Window":
        keys[ 'command' ] = self.openJythonShell

    self.names_and_commands[ keys[ 'label' ] ] = keys[ 'command' ]

    action = self.MenuRunnable( keys[ 'label' ], keys[ 'command' ], self.c, self.executor )
    jmenu = swing.JMenuItem( action )
    if keys.has_key( 'accelerator' ) and keys[ 'accelerator' ]:
        accel = keys[ 'accelerator' ]
        acc_list = accel.split( '+' )
        changeTo = { 'Alt': 'alt', 'Shift':'shift', #translation table
                     'Ctrl':'ctrl', 'UpArrow':'UP', 'DnArrow':'DOWN',
                     '-':'MINUS', '+':'PLUS', '=':'EQUALS',
                     '[':'typed [', ']':'typed ]', '{':'typed {',
                     '}':'typed }', 'Esc':'ESCAPE', '.':'typed .',
                      "`":"typed `", "BkSp":"BACK_SPACE"} #SEE java.awt.event.KeyEvent for further translations
        chg_list = []
        for z in acc_list:
            if z in changeTo:
                chg_list.append( changeTo[ z ] )
            else:
                chg_list.append( z )
        accelerator = " ".join( chg_list )
        ks = swing.KeyStroke.getKeyStroke( accelerator )
        if ks:
            self.keystrokes_and_actions[ ks ] = action
            jmenu.setAccelerator( ks )
        else:
            pass
    menu.add( jmenu )
    label = keys[ 'label' ]
    return jmenu

def add_separator(self,menu):
    menu.addSeparator()

def bind (self,bind_shortcut,callback):
    #self.oops() 
    pass

def delete (self,menu,realItemName):
    self.oops()

def delete_range (self,menu,n1,n2):


    items = menu.getMenuComponents()
    n3 = n1
    components = []
    while 1:
        if n3 == n2:
            break
        item = menu.getMenuComponent( n3 )
        components.append( item )
        n3 += 1

    for z in components:
        menu.remove( z )


def destroy (self,menu):
    self.oops()

def insert_cascade (self,parent,index,label,menu,underline):
    self.oops()

def new_menu(self,parent,tearoff=0):
    jm = swing.JMenu( "1" )
    #jm = self.LeoMenu( "1" )
    parent.add( jm )
    #jm.setFont( self.font)
    return jm
#@nonl
#@-node:ekr.20071001173734.23:9 Routines with Tk spellings
#@+node:ekr.20071001173734.24:7 Routines with new spellings
def createMenuBar (self,frame):

    top = frame.top
    self.defineMenuTables()
    topMenu = swing.JMenuBar()
    top.setJMenuBar( topMenu )
    topMenu.setFont( self.font )
    # Do gui-independent stuff.
    self.setMenu("top",topMenu)
    self.createMenusFromTables()
    self.createLeoSwingPrint()
    self.createPluginMenu()
    self.addUserGuide()

def createOpenWithMenuFromTable (self,table):
    self.oops()

def defineMenuCallback(self,command,name):
    return command

def defineOpenWithMenuCallback(self,command):
    self.oops()

def disableMenu (self,menu,name):
    for z in menu.getMenuComponents():
        if hasattr( z, "getText" ) and z.getText() == name:
            z.setEnabled( False )

def enableMenu (self,menu,name,val):
    for z in menu.getMenuComponents():
        if hasattr( z, "getText" ) and z.getText() == name:
            z.setEnabled( bool( val ) )

def setMenuLabel (self,menu,name,label,underline=-1, enabled = 1):

    item = ( menu, name, label, enabled )
    self.queue.offer( item )
    self.executor.submit( self.menu_changer )
#@-node:ekr.20071001173734.24:7 Routines with new spellings
#@+node:ekr.20071001173734.25:class MenuRunnable
class MenuRunnable( swing.AbstractAction, java.lang.Runnable): 

    def __init__( self, name, command, c , executor):
        swing.AbstractAction.__init__( self, name )
        self.command = command
        self.c = c
        self.name = name
        self.executor = executor

    def run( self ):
        self.c.doCommand( self.command, self.name ) #command()

    def actionPerformed( self, aE ):

        #print self.command
        #if self.name == 'Save':
        self.executor.submit( self )

        #else:        
        #    se
#@nonl
#@-node:ekr.20071001173734.25:class MenuRunnable
#@+node:ekr.20071001173734.26:class MenuExecuteOnSelect
class MenuExecuteOnSelect( sevent.MenuListener ):

    def __init__( self, method ):
        self.method = method

    def menuSelected( self, me ):
        self.method()

    def menuCanceled( self, me ):
        pass

    def menuDeselected( self, me ):
        pass
#@nonl
#@-node:ekr.20071001173734.26:class MenuExecuteOnSelect
#@+node:ekr.20071001173734.27:class LeoMenu
class LeoMenu( swing.JMenu ):

    def __init__( self, *args ):
        swing.JMenu.__init__( self, *args )

    def add( self, *items ):
        if hasattr( items[ 0 ], "setFont" ):
            items[ 0 ].setFont( self.getFont() )
        return self.super__add( *items )

#@-node:ekr.20071001173734.27:class LeoMenu
#@-node:ekr.20071001173734.22:Must be overridden in menu subclasses
#@-node:ekr.20071001173943:not ready yet
#@-node:ekr.20071001173734:class leoSwingMenu
#@+node:ekr.20070930184746.8:class leoSplash (java.lang.Runnable)
class leoSplash ( java.lang.Runnable ):

    @others
#@+node:ekr.20070930185331:run (leoSplash)
def run (self):

    g.trace(g.callers())

    self.splash = splash = swing.JWindow()
    splash.setAlwaysOnTop(1)
    cpane = splash.getContentPane()
    rp = splash.getRootPane()
    tb = swing.border.TitledBorder('Leo')
    tb.setTitleJustification(tb.CENTER)
    rp.setBorder(tb)
    splash.setBackground(awt.Color.ORANGE)
    dimension = awt.Dimension(400,400)
    splash.setPreferredSize(dimension)
    splash.setSize(400,400)

    sicon = g.os_path_join(g.app.loadDir,"..","Icons","Leosplash.GIF")
    ii = swing.ImageIcon(sicon)
    image = swing.JLabel(ii)
    image.setBackground(awt.Color.ORANGE)
    cpane.add(image)
    self.splashlabel = splashlabel = swing.JLabel("Leo is starting....")
    splashlabel.setBackground(awt.Color.ORANGE)
    splashlabel.setForeground(awt.Color.BLUE)
    cpane.add(splashlabel,awt.BorderLayout.SOUTH)
    w, h = self._calculateCenteredPosition(splash)
    splash.setLocation(w,h)
    splash.visible = True
#@-node:ekr.20070930185331:run (leoSplash)
#@+node:ekr.20070930185331.1:utils
def _calculateCenteredPosition( self, widget ):

    size = widget.getPreferredSize()
    height = size.height/2
    width = size.width/2
    h,w = self._getScreenPositionForDialog()
    height = h - height
    width = w - width
    return width, height

def _getScreenPositionForDialog( self ):

    tk = awt.Toolkit.getDefaultToolkit()
    dim = tk.getScreenSize()
    h = dim.height/2
    w = dim.width/2
    return h, w   

def setText( self, text ):  
    self.splashlabel.setText( text )

def hide( self ):
    self.splash.visible = 0

def toBack( self ):
    if self.splash.visible:
        self.splash.toBack()

def toFront( self ):
    if self.splash.visible:
        self.splash.setAlwaysOnTop( 1 )
        self.splash.toFront()

def isVisible( self ):
    return self.splash.visible
#@-node:ekr.20070930185331.1:utils
#@-node:ekr.20070930184746.8:class leoSplash (java.lang.Runnable)
#@+node:ekr.20071001091231.119:class leoSwingLog (REWRITE)
class leoSwingLog (leoFrame.leoLog):

    """A class that represents the log pane of a swing window."""

    @others
#@+node:ekr.20071001091231.120:swingLog Birth
#@+node:ekr.20071001091231.121:swingLog.__init__
def __init__ (self,frame,parentFrame):

    # g.trace("leoSwingLog")

    # Call the base class constructor and calls createControl.
    leoFrame.leoLog.__init__(self,frame,parentFrame)

    self.c = c = frame.c # Also set in the base constructor, but we need it here.

    self.colorTags = []
        # The list of color names used as tags in present tab.
        # This gest switched by selectTab.

    self.wrap = g.choose(c.config.getBool('log_pane_wraps'),"word","none")

    # New in 4.4a2: The log pane is a Pmw.Notebook...

    self.nb = None      # The Pmw.Notebook that holds all the tabs.
    self.colorTagsDict = {} # Keys are page names.  Values are saved colorTags lists.
    self.menu = None # A menu that pops up on right clicks in the hull or in tabs.

    self.logCtrl = self.createControl(parentFrame)
    self.setFontFromConfig()
    self.setColorFromConfig()



#@-node:ekr.20071001091231.121:swingLog.__init__
#@+node:ekr.20071001091231.122:swingLog.createControl
def createControl (self,parentFrame):

    c = self.c

    return self ### self.logCtrl

    # self.nb = Pmw.NoteBook(parentFrame,
        # borderwidth = 1, pagemargin = 0,
        # raisecommand = self.raiseTab,
        # lowercommand = self.lowerTab,
        # arrownavigation = 0,
    # )

    # menu = self.makeTabMenu(tabName=None)

    # def hullMenuCallback(event):
        # return self.onRightClick(event,menu)

    # self.nb.bind('<Button-3>',hullMenuCallback)

    # self.nb.pack(fill='both',expand=1)
    # self.selectTab('Log') # Create and activate the default tabs.

    # return self.logCtrl
#@-node:ekr.20071001091231.122:swingLog.createControl
#@+node:ekr.20071001091231.123:swingLog.finishCreate
def finishCreate (self):

    # g.trace('swingLog')

    c = self.c ; log = self

    c.searchCommands.openFindTab(show=False)
    c.spellCommands.openSpellTab()
    log.selectTab('Log')
#@-node:ekr.20071001091231.123:swingLog.finishCreate
#@+node:ekr.20071001091231.124:swingLog.createTextWidget
def createTextWidget (self,parentFrame):

    self.logNumber += 1

    log = g.app.gui.plainTextWidget(
        parentFrame,name="log-%d" % self.logNumber,
        setgrid=0,wrap=self.wrap,bd=2,bg="white",relief="flat")

    # logBar = Tk.Scrollbar(parentFrame,name="logBar")

    # log['yscrollcommand'] = logBar.set
    # logBar['command'] = log.yview

    # logBar.pack(side="right", fill="y")
    # # rr 8/14/02 added horizontal elevator 
    # if self.wrap == "none": 
        # logXBar = Tk.Scrollbar( 
            # parentFrame,name='logXBar',orient="horizontal") 
        # log['xscrollcommand'] = logXBar.set 
        # logXBar['command'] = log.xview 
        # logXBar.pack(side="bottom", fill="x")
    # log.pack(expand=1, fill="both")

    return log
#@-node:ekr.20071001091231.124:swingLog.createTextWidget
#@+node:ekr.20071001091231.125:swingLog.makeTabMenu
def makeTabMenu (self,tabName=None):

    '''Create a tab popup menu.'''

    # g.trace(tabName,g.callers())

    c = self.c
    # hull = self.nb.component('hull') # A Tk.Canvas.

    # menu = Tk.Menu(hull,tearoff=0)
    # menu.add_command(label='New Tab',command=self.newTabFromMenu)

    # if tabName:
        # # Important: tabName is the name when the tab is created.
        # # It is not affected by renaming, so we don't have to keep
        # # track of the correspondence between this name and what is in the label.
        # def deleteTabCallback():
            # return self.deleteTab(tabName)

        # label = g.choose(
            # tabName in ('Find','Spell'),'Hide This Tab','Delete This Tab')
        # menu.add_command(label=label,command=deleteTabCallback)

        # def renameTabCallback():
            # return self.renameTabFromMenu(tabName)

        # menu.add_command(label='Rename This Tab',command=renameTabCallback)

    # return menu
#@-node:ekr.20071001091231.125:swingLog.makeTabMenu
#@-node:ekr.20071001091231.120:swingLog Birth
#@+node:ekr.20071001091231.126:Config & get/saveState
#@+node:ekr.20071001091231.127:swingLog.configureBorder & configureFont
def configureBorder(self,border):

    self.logCtrl.configure(bd=border)

def configureFont(self,font):

    self.logCtrl.configure(font=font)
#@-node:ekr.20071001091231.127:swingLog.configureBorder & configureFont
#@+node:ekr.20071001091231.128:swingLog.getFontConfig
def getFontConfig (self):

    font = self.logCtrl.cget("font")
    # g.trace(font)
    return font
#@-node:ekr.20071001091231.128:swingLog.getFontConfig
#@+node:ekr.20071001091231.129:swingLog.restoreAllState
def restoreAllState (self,d):

    '''Restore the log from a dict created by saveAllState.'''

    logCtrl = self.logCtrl

    # Restore the text.
    text = d.get('text')
    logCtrl.insert('end',text)

    # Restore all colors.
    colors = d.get('colors')
    for color in colors.keys():
        if color not in self.colorTags:
            self.colorTags.append(color)
            logCtrl.tag_config(color,foreground=color)
        items = list(colors.get(color))
        while items:
            start,stop = items[0],items[1]
            items = items[2:]
            logCtrl.tag_add(color,start,stop)
#@-node:ekr.20071001091231.129:swingLog.restoreAllState
#@+node:ekr.20071001091231.130:swingLog.saveAllState
def saveAllState (self):

    '''Return a dict containing all data needed to recreate the log in another widget.'''

    logCtrl = self.logCtrl ; colors = {}

    # Save the text
    text = logCtrl.getAllText()

    # Save color tags.
    tag_names = logCtrl.tag_names()
    for tag in tag_names:
        if tag in self.colorTags:
            colors[tag] = logCtrl.tag_ranges(tag)

    d = {'text':text,'colors': colors}
    # g.trace('\n',g.dictToString(d))
    return d
#@-node:ekr.20071001091231.130:swingLog.saveAllState
#@+node:ekr.20071001091231.131:swingLog.setColorFromConfig
def setColorFromConfig (self):

    c = self.c

    bg = c.config.getColor("log_pane_background_color") or 'white'

    try:
        self.logCtrl.configure(bg=bg)
    except:
        g.es("exception setting log pane background color")
        g.es_exception()
#@-node:ekr.20071001091231.131:swingLog.setColorFromConfig
#@+node:ekr.20071001091231.132:swingLog.setFontFromConfig
def SetWidgetFontFromConfig (self,logCtrl=None):

    c = self.c

    if not logCtrl: logCtrl = self.logCtrl

    font = c.config.getFontFromParams(
        "log_text_font_family", "log_text_font_size",
        "log_text_font_slant", "log_text_font_weight",
        c.config.defaultLogFontSize)

    self.fontRef = font # ESSENTIAL: retain a link to font.
    ### logCtrl.configure(font=font)

    # g.trace("LOG",logCtrl.cget("font"),font.cget("family"),font.cget("weight"))

    bg = c.config.getColor("log_text_background_color")
    if bg:
        try: logCtrl.configure(bg=bg)
        except: pass

    fg = c.config.getColor("log_text_foreground_color")
    if fg:
        try: logCtrl.configure(fg=fg)
        except: pass

setFontFromConfig = SetWidgetFontFromConfig # Renaming supresses a pychecker warning.
#@-node:ekr.20071001091231.132:swingLog.setFontFromConfig
#@-node:ekr.20071001091231.126:Config & get/saveState
#@+node:ekr.20071001091231.133:Focus & update (swingLog)
#@+node:ekr.20071001091231.134:swingLog.onActivateLog
def onActivateLog (self,event=None):

    try:
        self.c.setLog()
        self.frame.tree.OnDeactivate()
        self.c.logWantsFocus()
    except:
        g.es_event_exception("activate log")
#@-node:ekr.20071001091231.134:swingLog.onActivateLog
#@+node:ekr.20071001091231.135:swingLog.hasFocus
def hasFocus (self):

    return self.c.get_focus() == self.logCtrl
#@-node:ekr.20071001091231.135:swingLog.hasFocus
#@+node:ekr.20071001091231.136:forceLogUpdate
def forceLogUpdate (self,s):

    if sys.platform == "darwin": # Does not work on MacOS X.
        try:
            print s, # Don't add a newline.
        except UnicodeError:
            # g.app may not be inited during scripts!
            print g.toEncodedString(s,'utf-8')
    else:
        self.logCtrl.update_idletasks()
#@-node:ekr.20071001091231.136:forceLogUpdate
#@-node:ekr.20071001091231.133:Focus & update (swingLog)
#@+node:ekr.20071001091231.137:put & putnl (swingLog)
@ Printing uses self.logCtrl, so this code need not concern itself
with which tab is active.

Also, selectTab switches the contents of colorTags, so that is not concern.
It may be that Pmw will allow us to dispense with the colorTags logic...
#@+node:ekr.20071001091231.138:put
# All output to the log stream eventually comes here.
def put (self,s,color=None,tabName='Log'):

    c = self.c

    # print 'swingLog.put',self.c.shortFileName(),tabName,g.callers()

    if g.app.quitting or not c or not c.exists:
        return

    if tabName:
        self.selectTab(tabName)

    # if self.logCtrl:
        # << put s to log control >>
        # self.logCtrl.update_idletasks()
    # else:
        # << put s to logWaiting and print s >>
#@+node:ekr.20071001091231.139:<< put s to log control >>
# if color:
    # if color not in self.colorTags:
        # self.colorTags.append(color)
        # self.logCtrl.tag_config(color,foreground=color)
    # self.logCtrl.insert("end",s)
    # self.logCtrl.tag_add(color,"end-%dc" % (len(s)+1),"end-1c")
    # self.logCtrl.tag_add("black","end")
# else:
    # self.logCtrl.insert("end",s)

# self.logCtrl.see('end')
# self.forceLogUpdate(s)
#@-node:ekr.20071001091231.139:<< put s to log control >>
#@+node:ekr.20071001091231.140:<< put s to logWaiting and print s >>
# g.app.logWaiting.append((s,color),)

# print "Null swing log"

# if type(s) == type(u""):
    # s = g.toEncodedString(s,"ascii")

# print s
#@-node:ekr.20071001091231.140:<< put s to logWaiting and print s >>
#@-node:ekr.20071001091231.138:put
#@+node:ekr.20071001091231.141:putnl
def putnl (self,tabName='Log'):

    if g.app.quitting:
        return
    if tabName:
        self.selectTab(tabName)

    # if self.logCtrl:
        # self.logCtrl.insert("end",'\n')
        # self.logCtrl.see('end')
        # self.forceLogUpdate('\n')
    # else:
        # # Put a newline to logWaiting and print newline
        # g.app.logWaiting.append(('\n',"black"),)
        # print "Null swing log"
        # print
#@-node:ekr.20071001091231.141:putnl
#@-node:ekr.20071001091231.137:put & putnl (swingLog)
#@+node:ekr.20071001091231.142:Tab (TkLog)
#@+node:ekr.20071001091231.143:clearTab
def clearTab (self,tabName,wrap='none'):

    self.selectTab(tabName,wrap=wrap)
    w = self.logCtrl
    w and w.delete(0,'end')
#@-node:ekr.20071001091231.143:clearTab
#@+node:ekr.20071001091231.144:createTab
def createTab (self,tabName,createText=True,wrap='none'):

    # g.trace(tabName,wrap)

    c = self.c ; k = c.k

    # tabFrame = self.nb.add(tabName)
    # self.menu = self.makeTabMenu(tabName)
    # if createText:
        # << Create the tab's text widget >>
        # if tabName != 'Log':
            # # c.k doesn't exist when the log pane is created.
            # # k.makeAllBindings will call setTabBindings('Log')
            # self.setTabBindings(tabName)
    # else:
        # self.textDict [tabName] = None
        # self.frameDict [tabName] = tabFrame
#@+node:ekr.20071001091231.145:<< Create the tab's text widget >>
# w = self.createTextWidget(tabFrame)

# # Set the background color.
# configName = 'log_pane_%s_tab_background_color' % tabName
# bg = c.config.getColor(configName) or 'MistyRose1'

# if wrap not in ('none','char','word'): wrap = 'none'
# try: w.configure(bg=bg,wrap=wrap)
# except Exception: pass # Could be a user error.

# self.SetWidgetFontFromConfig(logCtrl=w)

# self.frameDict [tabName] = tabFrame
# self.textDict [tabName] = w

# # Switch to a new colorTags list.
# if self.tabName:
    # self.colorTagsDict [self.tabName] = self.colorTags [:]

# self.colorTags = ['black']
# self.colorTagsDict [tabName] = self.colorTags
#@-node:ekr.20071001091231.145:<< Create the tab's text widget >>
#@-node:ekr.20071001091231.144:createTab
#@+node:ekr.20071001091231.146:cycleTabFocus
def cycleTabFocus (self,event=None,stop_w = None):

    '''Cycle keyboard focus between the tabs in the log pane.'''

    c = self.c ; d = self.frameDict # Keys are page names. Values are Tk.Frames.
    w = d.get(self.tabName)
    # g.trace(self.tabName,w)
    values = d.values()
    if self.numberOfVisibleTabs() > 1:
        i = i2 = values.index(w) + 1
        if i == len(values): i = 0
        tabName = d.keys()[i]
        self.selectTab(tabName)
        return 
#@nonl
#@-node:ekr.20071001091231.146:cycleTabFocus
#@+node:ekr.20071001091231.147:deleteTab
def deleteTab (self,tabName,force=False):

    if tabName == 'Log':
        pass

    elif tabName in ('Find','Spell') and not force:
        self.selectTab('Log')

    # elif tabName in self.nb.pagenames():
        # # g.trace(tabName,force)
        # self.nb.delete(tabName)
        # self.colorTagsDict [tabName] = []
        # self.textDict [tabName] = None
        # self.frameDict [tabName] = None
        # self.tabName = None
        # self.selectTab('Log')

    # New in Leo 4.4b1.
    self.c.invalidateFocus()
    self.c.bodyWantsFocus()
#@-node:ekr.20071001091231.147:deleteTab
#@+node:ekr.20071001091231.148:hideTab
def hideTab (self,tabName):

    # __pychecker__ = '--no-argsused' # tabName

    self.selectTab('Log')
#@-node:ekr.20071001091231.148:hideTab
#@+node:ekr.20071001091231.149:getSelectedTab
def getSelectedTab (self):

    return self.tabName
#@-node:ekr.20071001091231.149:getSelectedTab
#@+node:ekr.20071001091231.150:lower/raiseTab
def lowerTab (self,tabName):

    # if tabName:
        # b = self.nb.tab(tabName) # b is a Tk.Button.
        # b.config(bg='grey80')
    self.c.invalidateFocus()
    self.c.bodyWantsFocus()

def raiseTab (self,tabName):

    # if tabName:
        # b = self.nb.tab(tabName) # b is a Tk.Button.
        # b.config(bg='LightSteelBlue1')
    self.c.invalidateFocus()
    self.c.bodyWantsFocus()
#@-node:ekr.20071001091231.150:lower/raiseTab
#@+node:ekr.20071001091231.151:numberOfVisibleTabs
def numberOfVisibleTabs (self):

    return len([val for val in self.frameDict.values() if val != None])
#@-node:ekr.20071001091231.151:numberOfVisibleTabs
#@+node:ekr.20071001091231.152:renameTab
def renameTab (self,oldName,newName):

    # g.trace('newName',newName)

    # label = self.nb.tab(oldName)
    # label.configure(text=newName)

    pass
#@-node:ekr.20071001091231.152:renameTab
#@+node:ekr.20071001091231.153:selectTab
def selectTab (self,tabName,createText=True,wrap='none'):

    '''Create the tab if necessary and make it active.'''

    c = self.c

    # tabFrame = self.frameDict.get(tabName)
    # logCtrl = self.textDict.get(tabName)

    # if tabFrame and logCtrl:
        # # Switch to a new colorTags list.
        # newColorTags = self.colorTagsDict.get(tabName)
        # self.colorTagsDict [self.tabName] = self.colorTags [:]
        # self.colorTags = newColorTags
    # elif not tabFrame:
        # self.createTab(tabName,createText=createText,wrap=wrap)

    # self.nb.selectpage(tabName)
    # # Update the status vars.
    # self.tabName = tabName
    # self.logCtrl = self.textDict.get(tabName)
    # self.tabFrame = self.frameDict.get(tabName)

    # if 0: # Absolutely do not do this here!  It is a cause of the 'sticky focus' problem.
        # c.widgetWantsFocusNow(self.logCtrl)
    # return tabFrame
#@-node:ekr.20071001091231.153:selectTab
#@+node:ekr.20071001091231.154:setTabBindings
def setTabBindings (self,tabName):

    c = self.c ; k = c.k
    # tab = self.nb.tab(tabName)
    # w = self.textDict.get(tabName)

    # # Send all event in the text area to the master handlers.
    # for kind,handler in (
        # ('<Key>',       k.masterKeyHandler),
        # ('<Button-1>',  k.masterClickHandler),
        # ('<Button-3>',  k.masterClick3Handler),
    # ):
        # w.bind(kind,handler)

    # # Clicks in the tab area are harmless: use the old code.
    # def tabMenuRightClickCallback(event,menu=self.menu):
        # return self.onRightClick(event,menu)

    # def tabMenuClickCallback(event,tabName=tabName):
        # return self.onClick(event,tabName)

    # tab.bind('<Button-1>',tabMenuClickCallback)
    # tab.bind('<Button-3>',tabMenuRightClickCallback)

    # k.completeAllBindingsForWidget(w)
#@-node:ekr.20071001091231.154:setTabBindings
#@+node:ekr.20071001091231.155:Tab menu callbacks & helpers
#@+node:ekr.20071001091231.156:onRightClick & onClick
def onRightClick (self,event,menu):

    c = self.c
    menu.post(event.x_root,event.y_root)


def onClick (self,event,tabName):

    self.selectTab(tabName)
#@-node:ekr.20071001091231.156:onRightClick & onClick
#@+node:ekr.20071001091231.157:newTabFromMenu
def newTabFromMenu (self,tabName='Log'):

    self.selectTab(tabName)

    # This is called by getTabName.
    def selectTabCallback (newName):
        return self.selectTab(newName)

    self.getTabName(selectTabCallback)
#@-node:ekr.20071001091231.157:newTabFromMenu
#@+node:ekr.20071001091231.158:renameTabFromMenu
def renameTabFromMenu (self,tabName):

    if tabName in ('Log','Completions'):
        g.es('can not rename %s tab' % (tabName),color='blue')
    else:
        def renameTabCallback (newName):
            return self.renameTab(tabName,newName)

        self.getTabName(renameTabCallback)
#@-node:ekr.20071001091231.158:renameTabFromMenu
#@+node:ekr.20071001091231.159:getTabName
def getTabName (self,exitCallback):

    canvas = self.nb.component('hull')

    # Overlay what is there!
    c = self.c
    f = Tk.Frame(canvas)
    f.pack(side='top',fill='both',expand=1)

    row1 = Tk.Frame(f)
    row1.pack(side='top',expand=0,fill='x',pady=10)
    row2 = Tk.Frame(f)
    row2.pack(side='top',expand=0,fill='x')

    Tk.Label(row1,text='Tab name').pack(side='left')

    e = Tk.Entry(row1,background='white')
    e.pack(side='left')

    def getNameCallback (event=None):
        s = e.get().strip()
        f.pack_forget()
        if s: exitCallback(s)

    def closeTabNameCallback (event=None):
        f.pack_forget()

    b = Tk.Button(row2,text='Ok',width=6,command=getNameCallback)
    b.pack(side='left',padx=10)

    b = Tk.Button(row2,text='Cancel',width=6,command=closeTabNameCallback)
    b.pack(side='left')

    g.app.gui.set_focus(c,e)
    e.bind('<Return>',getNameCallback)
#@-node:ekr.20071001091231.159:getTabName
#@-node:ekr.20071001091231.155:Tab menu callbacks & helpers
#@-node:ekr.20071001091231.142:Tab (TkLog)
#@+node:ekr.20071001091231.160:swingLog color tab stuff
def createColorPicker (self,tabName):

    log = self

    << define colors >>

    parent = log.frameDict.get(tabName)
    w = log.textDict.get(tabName)
    w.pack_forget()

    colors = list(colors)
    bg = parent.cget('background')

    outer = Tk.Frame(parent,background=bg)
    outer.pack(side='top',fill='both',expand=1,pady=10)

    f = Tk.Frame(outer)
    f.pack(side='top',expand=0,fill='x')
    f1 = Tk.Frame(f) ; f1.pack(side='top',expand=0,fill='x')
    f2 = Tk.Frame(f) ; f2.pack(side='top',expand=1,fill='x')
    f3 = Tk.Frame(f) ; f3.pack(side='top',expand=1,fill='x')

    label = g.app.gui.plainTextWidget(f1,height=1,width=20)
    label.insert('1.0','Color name or value...')
    label.pack(side='left',pady=6)

    << create optionMenu and callback >>
    << create picker button and callback >>
#@+node:ekr.20071001091231.161:<< define colors >>
colors = (
    "gray60", "gray70", "gray80", "gray85", "gray90", "gray95",
    "snow1", "snow2", "snow3", "snow4", "seashell1", "seashell2",
    "seashell3", "seashell4", "AntiqueWhite1", "AntiqueWhite2", "AntiqueWhite3",
    "AntiqueWhite4", "bisque1", "bisque2", "bisque3", "bisque4", "PeachPuff1",
    "PeachPuff2", "PeachPuff3", "PeachPuff4", "NavajoWhite1", "NavajoWhite2",
    "NavajoWhite3", "NavajoWhite4", "LemonChiffon1", "LemonChiffon2",
    "LemonChiffon3", "LemonChiffon4", "cornsilk1", "cornsilk2", "cornsilk3",
    "cornsilk4", "ivory1", "ivory2", "ivory3", "ivory4", "honeydew1", "honeydew2",
    "honeydew3", "honeydew4", "LavenderBlush1", "LavenderBlush2",
    "LavenderBlush3", "LavenderBlush4", "MistyRose1", "MistyRose2",
    "MistyRose3", "MistyRose4", "azure1", "azure2", "azure3", "azure4",
    "SlateBlue1", "SlateBlue2", "SlateBlue3", "SlateBlue4", "RoyalBlue1",
    "RoyalBlue2", "RoyalBlue3", "RoyalBlue4", "blue1", "blue2", "blue3", "blue4",
    "DodgerBlue1", "DodgerBlue2", "DodgerBlue3", "DodgerBlue4", "SteelBlue1",
    "SteelBlue2", "SteelBlue3", "SteelBlue4", "DeepSkyBlue1", "DeepSkyBlue2",
    "DeepSkyBlue3", "DeepSkyBlue4", "SkyBlue1", "SkyBlue2", "SkyBlue3",
    "SkyBlue4", "LightSkyBlue1", "LightSkyBlue2", "LightSkyBlue3",
    "LightSkyBlue4", "SlateGray1", "SlateGray2", "SlateGray3", "SlateGray4",
    "LightSteelBlue1", "LightSteelBlue2", "LightSteelBlue3",
    "LightSteelBlue4", "LightBlue1", "LightBlue2", "LightBlue3",
    "LightBlue4", "LightCyan1", "LightCyan2", "LightCyan3", "LightCyan4",
    "PaleTurquoise1", "PaleTurquoise2", "PaleTurquoise3", "PaleTurquoise4",
    "CadetBlue1", "CadetBlue2", "CadetBlue3", "CadetBlue4", "turquoise1",
    "turquoise2", "turquoise3", "turquoise4", "cyan1", "cyan2", "cyan3", "cyan4",
    "DarkSlateGray1", "DarkSlateGray2", "DarkSlateGray3",
    "DarkSlateGray4", "aquamarine1", "aquamarine2", "aquamarine3",
    "aquamarine4", "DarkSeaGreen1", "DarkSeaGreen2", "DarkSeaGreen3",
    "DarkSeaGreen4", "SeaGreen1", "SeaGreen2", "SeaGreen3", "SeaGreen4",
    "PaleGreen1", "PaleGreen2", "PaleGreen3", "PaleGreen4", "SpringGreen1",
    "SpringGreen2", "SpringGreen3", "SpringGreen4", "green1", "green2",
    "green3", "green4", "chartreuse1", "chartreuse2", "chartreuse3",
    "chartreuse4", "OliveDrab1", "OliveDrab2", "OliveDrab3", "OliveDrab4",
    "DarkOliveGreen1", "DarkOliveGreen2", "DarkOliveGreen3",
    "DarkOliveGreen4", "khaki1", "khaki2", "khaki3", "khaki4",
    "LightGoldenrod1", "LightGoldenrod2", "LightGoldenrod3",
    "LightGoldenrod4", "LightYellow1", "LightYellow2", "LightYellow3",
    "LightYellow4", "yellow1", "yellow2", "yellow3", "yellow4", "gold1", "gold2",
    "gold3", "gold4", "goldenrod1", "goldenrod2", "goldenrod3", "goldenrod4",
    "DarkGoldenrod1", "DarkGoldenrod2", "DarkGoldenrod3", "DarkGoldenrod4",
    "RosyBrown1", "RosyBrown2", "RosyBrown3", "RosyBrown4", "IndianRed1",
    "IndianRed2", "IndianRed3", "IndianRed4", "sienna1", "sienna2", "sienna3",
    "sienna4", "burlywood1", "burlywood2", "burlywood3", "burlywood4", "wheat1",
    "wheat2", "wheat3", "wheat4", "tan1", "tan2", "tan3", "tan4", "chocolate1",
    "chocolate2", "chocolate3", "chocolate4", "firebrick1", "firebrick2",
    "firebrick3", "firebrick4", "brown1", "brown2", "brown3", "brown4", "salmon1",
    "salmon2", "salmon3", "salmon4", "LightSalmon1", "LightSalmon2",
    "LightSalmon3", "LightSalmon4", "orange1", "orange2", "orange3", "orange4",
    "DarkOrange1", "DarkOrange2", "DarkOrange3", "DarkOrange4", "coral1",
    "coral2", "coral3", "coral4", "tomato1", "tomato2", "tomato3", "tomato4",
    "OrangeRed1", "OrangeRed2", "OrangeRed3", "OrangeRed4", "red1", "red2", "red3",
    "red4", "DeepPink1", "DeepPink2", "DeepPink3", "DeepPink4", "HotPink1",
    "HotPink2", "HotPink3", "HotPink4", "pink1", "pink2", "pink3", "pink4",
    "LightPink1", "LightPink2", "LightPink3", "LightPink4", "PaleVioletRed1",
    "PaleVioletRed2", "PaleVioletRed3", "PaleVioletRed4", "maroon1",
    "maroon2", "maroon3", "maroon4", "VioletRed1", "VioletRed2", "VioletRed3",
    "VioletRed4", "magenta1", "magenta2", "magenta3", "magenta4", "orchid1",
    "orchid2", "orchid3", "orchid4", "plum1", "plum2", "plum3", "plum4",
    "MediumOrchid1", "MediumOrchid2", "MediumOrchid3", "MediumOrchid4",
    "DarkOrchid1", "DarkOrchid2", "DarkOrchid3", "DarkOrchid4", "purple1",
    "purple2", "purple3", "purple4", "MediumPurple1", "MediumPurple2",
    "MediumPurple3", "MediumPurple4", "thistle1", "thistle2", "thistle3",
    "thistle4" )
#@-node:ekr.20071001091231.161:<< define colors >>
#@+node:ekr.20071001091231.162:<< create optionMenu and callback >>
colorBox = Pmw.ComboBox(f2,scrolledlist_items=colors)
colorBox.pack(side='left',pady=4)

def colorCallback (newName): 
    label.delete('1.0','end')
    label.insert('1.0',newName)
    try:
        for theFrame in (parent,outer,f,f1,f2,f3):
            theFrame.configure(background=newName)
    except: pass # Ignore invalid names.

colorBox.configure(selectioncommand=colorCallback)
#@-node:ekr.20071001091231.162:<< create optionMenu and callback >>
#@+node:ekr.20071001091231.163:<< create picker button and callback >>
def pickerCallback ():
    rgb,val = swingColorChooser.askcolor(parent=parent,initialcolor=f.cget('background'))
    if rgb or val:
        # label.configure(text=val)
        label.delete('1.0','end')
        label.insert('1.0',val)
        for theFrame in (parent,outer,f,f1,f2,f3):
            theFrame.configure(background=val)

b = Tk.Button(f3,text="Color Picker...",
    command=pickerCallback,background=bg)
b.pack(side='left',pady=4)
#@-node:ekr.20071001091231.163:<< create picker button and callback >>
#@-node:ekr.20071001091231.160:swingLog color tab stuff
#@+node:ekr.20071001091231.164:swingLog font tab stuff
#@+node:ekr.20071001091231.165:createFontPicker
def createFontPicker (self,tabName):

    log = self
    parent = log.frameDict.get(tabName)
    w = log.textDict.get(tabName)
    w.pack_forget()

    bg = parent.cget('background')
    font = self.getFont()
    << create the frames >>
    << create the family combo box >>
    << create the size entry >>
    << create the weight combo box >>
    << create the slant combo box >>
    << create the sample text widget >>
    << create and bind the callbacks >>
    self.createBindings()
#@+node:ekr.20071001091231.166:<< create the frames >>
f = Tk.Frame(parent,background=bg) ; f.pack (side='top',expand=0,fill='both')
f1 = Tk.Frame(f,background=bg)     ; f1.pack(side='top',expand=1,fill='x')
f2 = Tk.Frame(f,background=bg)     ; f2.pack(side='top',expand=1,fill='x')
f3 = Tk.Frame(f,background=bg)     ; f3.pack(side='top',expand=1,fill='x')
f4 = Tk.Frame(f,background=bg)     ; f4.pack(side='top',expand=1,fill='x')
#@-node:ekr.20071001091231.166:<< create the frames >>
#@+node:ekr.20071001091231.167:<< create the family combo box >>
names = swingFont.families()
names = list(names)
names.sort()
names.insert(0,'<None>')

self.familyBox = familyBox = Pmw.ComboBox(f1,
    labelpos="we",label_text='Family:',label_width=10,
    label_background=bg,
    arrowbutton_background=bg,
    scrolledlist_items=names)

familyBox.selectitem(0)
familyBox.pack(side="left",padx=2,pady=2)
#@-node:ekr.20071001091231.167:<< create the family combo box >>
#@+node:ekr.20071001091231.168:<< create the size entry >>
Tk.Label(f2,text="Size:",width=10,background=bg).pack(side="left")

sizeEntry = Tk.Entry(f2,width=4)
sizeEntry.insert(0,'12')
sizeEntry.pack(side="left",padx=2,pady=2)
#@-node:ekr.20071001091231.168:<< create the size entry >>
#@+node:ekr.20071001091231.169:<< create the weight combo box >>
weightBox = Pmw.ComboBox(f3,
    labelpos="we",label_text="Weight:",label_width=10,
    label_background=bg,
    arrowbutton_background=bg,
    scrolledlist_items=['normal','bold'])

weightBox.selectitem(0)
weightBox.pack(side="left",padx=2,pady=2)
#@-node:ekr.20071001091231.169:<< create the weight combo box >>
#@+node:ekr.20071001091231.170:<< create the slant combo box>>
slantBox = Pmw.ComboBox(f4,
    labelpos="we",label_text="Slant:",label_width=10,
    label_background=bg,
    arrowbutton_background=bg,
    scrolledlist_items=['roman','italic'])

slantBox.selectitem(0)
slantBox.pack(side="left",padx=2,pady=2)
#@-node:ekr.20071001091231.170:<< create the slant combo box>>
#@+node:ekr.20071001091231.171:<< create the sample text widget >>
self.sampleWidget = sample = g.app.gui.plainTextWidget(f,height=20,width=80,font=font)
sample.pack(side='left')

s = 'The quick brown fox\njumped over the lazy dog.\n0123456789'
sample.insert(0,s)
#@-node:ekr.20071001091231.171:<< create the sample text widget >>
#@+node:ekr.20071001091231.172:<< create and bind the callbacks >>
def fontCallback(event=None):
    self.setFont(familyBox,sizeEntry,slantBox,weightBox,sample)

for w in (familyBox,slantBox,weightBox):
    w.configure(selectioncommand=fontCallback)

sizeEntry.bind('<Return>',fontCallback)
#@-node:ekr.20071001091231.172:<< create and bind the callbacks >>
#@-node:ekr.20071001091231.165:createFontPicker
#@+node:ekr.20071001091231.173:createBindings (fontPicker)
def createBindings (self):

    c = self.c ; k = c.k

    table = (
        ('<Button-1>',  k.masterClickHandler),
        ('<Double-1>',  k.masterClickHandler),
        ('<Button-3>',  k.masterClickHandler),
        ('<Double-3>',  k.masterClickHandler),
        ('<Key>',       k.masterKeyHandler),
        ("<Escape>",    self.hideFontTab),
    )

    w = self.sampleWidget
    for event, callback in table:
        w.bind(event,callback)

    k.completeAllBindingsForWidget(w)
#@-node:ekr.20071001091231.173:createBindings (fontPicker)
#@+node:ekr.20071001091231.174:getFont
def getFont(self,family=None,size=12,slant='roman',weight='normal'):

    try:
        return swingFont.Font(family=family,size=size,slant=slant,weight=weight)
    except Exception:
        g.es("exception setting font")
        g.es("family,size,slant,weight:",family,size,slant,weight)
        # g.es_exception() # This just confuses people.
        return g.app.config.defaultFont
#@-node:ekr.20071001091231.174:getFont
#@+node:ekr.20071001091231.175:setFont
def setFont(self,familyBox,sizeEntry,slantBox,weightBox,label):

    d = {}
    for box,key in (
        (familyBox, 'family'),
        (None,      'size'),
        (slantBox,  'slant'),
        (weightBox, 'weight'),
    ):
        if box: val = box.get()
        else:
            val = sizeEntry.get().strip() or ''
            try: int(val)
            except ValueError: val = None
        if val and val.lower() not in ('none','<none>',):
            d[key] = val

    family=d.get('family',None)
    size=d.get('size',12)
    weight=d.get('weight','normal')
    slant=d.get('slant','roman')
    font = self.getFont(family,size,slant,weight)
    label.configure(font=font)
#@-node:ekr.20071001091231.175:setFont
#@+node:ekr.20071001091231.176:hideFontTab
def hideFontTab (self,event=None):

    c = self.c
    c.frame.log.selectTab('Log')
    c.bodyWantsFocus()
#@-node:ekr.20071001091231.176:hideFontTab
#@-node:ekr.20071001091231.164:swingLog font tab stuff
#@-node:ekr.20071001091231.119:class leoSwingLog (REWRITE)
#@+node:ekr.20071001091231.177:class leoSwingTreeTab (REWRITE)
class leoSwingTreeTab (leoFrame.leoTreeTab):

    '''A class representing a tabbed outline pane drawn with swing.'''

    @others
#@nonl
#@+node:ekr.20071001091231.178: Birth & death
#@+node:ekr.20071001091231.179: ctor (leoTreeTab)
def __init__ (self,c,parentFrame,chapterController):

    leoFrame.leoTreeTab.__init__ (self,c,chapterController,parentFrame)
        # Init the base class.  Sets self.c, self.cc and self.parentFrame.

    self.tabNames = [] # The list of tab names.  Changes when tabs are renamed.

    self.createControl()
#@-node:ekr.20071001091231.179: ctor (leoTreeTab)
#@+node:ekr.20071001091231.180:tt.createControl
def createControl (self):

    tt = self ; c = tt.c

    # Create the main container.
    tt.frame = Tk.Frame(c.frame.iconFrame)
    tt.frame.pack(side="left")

    # Create the chapter menu.
    self.chapterVar = var = Tk.StringVar()
    var.set('main')

    tt.chapterMenu = menu = Pmw.OptionMenu(tt.frame,
        labelpos = 'w', label_text = 'chapter',
        menubutton_textvariable = var,
        items = [],
        command = tt.selectTab,
    )
    menu.pack(side='left',padx=5)
#@nonl
#@-node:ekr.20071001091231.180:tt.createControl
#@-node:ekr.20071001091231.178: Birth & death
#@+node:ekr.20071001091231.181:Tabs...
#@+node:ekr.20071001091231.182:tt.createTab
def createTab (self,tabName,select=True):

    tt = self

    if tabName not in tt.tabNames:
        tt.tabNames.append(tabName)
        tt.setNames()
#@-node:ekr.20071001091231.182:tt.createTab
#@+node:ekr.20071001091231.183:tt.destroyTab
def destroyTab (self,tabName):

    tt = self

    if tabName in tt.tabNames:
        tt.tabNames.remove(tabName)
        tt.setNames()
#@-node:ekr.20071001091231.183:tt.destroyTab
#@+node:ekr.20071001091231.184:tt.selectTab
def selectTab (self,tabName):

    tt = self

    if tabName not in self.tabNames:
        tt.createTab(tabName)

    tt.cc.selectChapterByName(tabName)
#@-node:ekr.20071001091231.184:tt.selectTab
#@+node:ekr.20071001091231.185:tt.setTabLabel
def setTabLabel (self,tabName):

    tt = self
    tt.chapterVar.set(tabName)
#@-node:ekr.20071001091231.185:tt.setTabLabel
#@+node:ekr.20071001091231.186:tt.setNames
def setNames (self):

    '''Recreate the list of items.'''

    tt = self
    names = tt.tabNames[:]
    if 'main' in names: names.remove('main')
    names.sort()
    names.insert(0,'main')
    tt.chapterMenu.setitems(names)
#@-node:ekr.20071001091231.186:tt.setNames
#@-node:ekr.20071001091231.181:Tabs...
#@-node:ekr.20071001091231.177:class leoSwingTreeTab (REWRITE)
#@+node:ekr.20071001091231.187:class leoSwingTextWidget (revise)
class leoSwingTextWidget: ### (leoFrame.baseTextWidget):

    '''A class to wrap the Tk.Text widget.
    Translates Python (integer) indices to and from Tk (string) indices.

    This class inherits almost all swingText methods: you call use them as usual.'''

    # The signatures of tag_add and insert are different from the Tk.Text signatures.
    # __pychecker__ = '--no-override' # suppress warning about changed signature.

    def __repr__(self):
        name = hasattr(self,'_name') and self._name or '<no name>'
        return 'swingTextWidget id: %s name: %s' % (id(self),name)

    @others
#@nonl
#@+node:ekr.20071001091231.188:swingTextWidget.__init__

def __init__ (self,parentFrame,*args,**keys):

    # Create the actual gui widget.
    ### self.widget = Tk.Text(*args,**keys)

    ### To do: probably need to subclass JTextField so we can inject ivars.

    self.widget = w = swing.JTextField() ###preferredSize=(200,20))
    parentFrame.contentPane.add(w)

    ### Probably should be somewhere else.
    parentFrame.pack()
    parentFrame.show()

    # Init the base class.
    # name = keys.get('name') or '<unknown swingTextWidget>'
    # leoFrame.baseTextWidget.__init__(self,c=c,
        # baseClassName='swingTextWidget',name=name,widget=self.widget)

    # self.defaultFont = font = wx.Font(pointSize=10,
        # family = wx.FONTFAMILY_TELETYPE, # wx.FONTFAMILY_ROMAN,
        # style  = wx.FONTSTYLE_NORMAL,
        # weight = wx.FONTWEIGHT_NORMAL,)
#@-node:ekr.20071001091231.188:swingTextWidget.__init__
#@+node:ekr.20071001091231.189:bindings (not used)
# Specify the names of widget-specific methods.
# These particular names are the names of wx.TextCtrl methods.

# def _appendText(self,s):            return self.widget.insert(s)
# def _get(self,i,j):                 return self.widget.get(i,j)
# def _getAllText(self):              return self.widget.get('1.0','end')
# def _getFocus(self):                return self.widget.focus_get()
# def _getInsertPoint(self):          return self.widget.index('insert')
# def _getLastPosition(self):         return self.widget.index('end')
# def _getSelectedText(self):         return self.widget.get('sel.start','sel.end')
# def _getSelectionRange(self):       return self.widget.index('sel.start'),self.widget.index('sel.end')
# def _hitTest(self,pos):             pass ###
# def _insertText(self,i,s):          return self.widget.insert(i,s)
# def _scrollLines(self,n):           pass ###
# def _see(self,i):                   return self.widget.see(i)
# def _setAllText(self,s):            self.widget.delete('1.0','end') ; self.widget.insert('1.0',s)
# def _setBackgroundColor(self,color): return self.widget.configure(background=color)
# def _setFocus(self):                return self.widget.focus_set()
# def _setInsertPoint(self,i):        return self.widget.mark_set('insert',i)
# # def _setSelectionRange(self,i,j):   return self.widget.SetSelection(i,j)
#@-node:ekr.20071001091231.189:bindings (not used)
#@+node:ekr.20071001091231.190:Index conversion (swingTextWidget)
#@+node:ekr.20071001091231.191:w.toGuiIndex
def toGuiIndex (self,i,s=None):
    '''Convert a Python index to a Tk index as needed.'''
    w = self
    if i is None:
        g.trace('can not happen: i is None',g.callers())
        return '1.0'
    elif type(i) == type(99):
        # The 's' arg supports the threaded colorizer.
        if s is None:
            # This *must* be 'end-1c', even if other code must change.
            s = '' ### s = Tk.Text.get(w,'1.0','end-1c')
        row,col = g.convertPythonIndexToRowCol(s,i)
        i = '%s.%s' % (row+1,col)
        # g.trace(len(s),i,repr(s))
    else:
        try:
            i = 0 ### i = Tk.Text.index(w,i)
        except Exception:
            # g.es_exception()
            g.trace('Tk.Text.index failed:',repr(i),g.callers())
            i = '1.0'
    return i
#@nonl
#@-node:ekr.20071001091231.191:w.toGuiIndex
#@+node:ekr.20071001091231.192:w.toPythonIndex
def toPythonIndex (self,i):
    '''Convert a Tk index to a Python index as needed.'''
    w =self
    if i is None:
        g.trace('can not happen: i is None')
        return 0
    elif type(i) in (type('a'),type(u'a')):
        s = '' ### s = Tk.Text.get(w,'1.0','end') # end-1c does not work.
        i = '1.0' ### i = Tk.Text.index(w,i) # Convert to row/column form.
        row,col = i.split('.')
        row,col = int(row),int(col)
        row -= 1
        i = g.convertRowColToPythonIndex(s,row,col)
        #g.es_print(i)
    return i
#@-node:ekr.20071001091231.192:w.toPythonIndex
#@+node:ekr.20071001091231.193:w.rowColToGuiIndex
# This method is called only from the colorizer.
# It provides a huge speedup over naive code.

def rowColToGuiIndex (self,s,row,col):

    return '%s.%s' % (row+1,col)
#@nonl
#@-node:ekr.20071001091231.193:w.rowColToGuiIndex
#@-node:ekr.20071001091231.190:Index conversion (swingTextWidget)
#@+node:ekr.20071001091231.194:getName (Tk.Text)
def getName (self):

    w = self
    return hasattr(w,'_name') and w._name or repr(w)
#@nonl
#@-node:ekr.20071001091231.194:getName (Tk.Text)
#@+node:ekr.20071001091231.195:_setSelectionRange
if 0:
    def _setSelectionRange (self,i,j,insert=None):

        w = self.widget

        i,j = w.toGuiIndex(i),w.toGuiIndex(j)

        # g.trace('i,j,insert',repr(i),repr(j),repr(insert),g.callers())

        # g.trace('i,j,insert',i,j,repr(insert))
        if w.compare(w,i, ">", j): i,j = j,i
        w.tag_remove(w,"sel","1.0",i)
        w.tag_add(w,"sel",i,j)
        w.tag_remove(w,"sel",j,"end")

        if insert is not None:
            w.setInsertPoint(insert)
#@-node:ekr.20071001091231.195:_setSelectionRange
#@+node:ekr.20071001091231.196:Wrapper methods (swingTextWidget)
#@+node:ekr.20071001185205:after_idle (new)
def after_idle(self,*args,**keys):

    pass
#@-node:ekr.20071001185205:after_idle (new)
#@+node:ekr.20071001193522:bind (new)
def bind (self,*args,**keys):

    pass
#@-node:ekr.20071001193522:bind (new)
#@+node:ekr.20071001091231.197:delete
def delete(self,i,j=None):

    w = self
    i = w.toGuiIndex(i)

    if j is None:
        pass ### Tk.Text.delete(w,i)
    else:
        j = w.toGuiIndex(j)
        pass ### Tk.Text.delete(w,i,j)
#@-node:ekr.20071001091231.197:delete
#@+node:ekr.20071001091231.198:flashCharacter
def flashCharacter(self,i,bg='white',fg='red',flashes=3,delay=75): # swingTextWidget.

    w = self

    # def addFlashCallback(w,count,index):
        # # g.trace(count,index)
        # i,j = w.toGuiIndex(index),w.toGuiIndex(index+1)
        # Tk.Text.tag_add(w,'flash',i,j)
        # Tk.Text.after(w,delay,removeFlashCallback,w,count-1,index)

    # def removeFlashCallback(w,count,index):
        # # g.trace(count,index)
        # Tk.Text.tag_remove(w,'flash','1.0','end')
        # if count > 0:
            # Tk.Text.after(w,delay,addFlashCallback,w,count,index)

    # try:
        # Tk.Text.tag_configure(w,'flash',foreground=fg,background=bg)
        # addFlashCallback(w,flashes,i)
    # except Exception:
        # pass ; g.es_exception()
#@nonl
#@-node:ekr.20071001091231.198:flashCharacter
#@+node:ekr.20071001091231.199:get
def get(self,i,j=None):

    w = self
    i = w.toGuiIndex(i)

    if j is None:
        return '' ### return Tk.Text.get(w,i)
    else:
        j = w.toGuiIndex(j)
        return ### return Tk.Text.get(w,i,j)
#@-node:ekr.20071001091231.199:get
#@+node:ekr.20071001091231.200:getAllText
def getAllText (self): # swingTextWidget.

    """Return all the text of Tk.Text widget w converted to unicode."""

    w = self
    ### s = Tk.Text.get(w,"1.0","end-1c") # New in 4.4.1: use end-1c.
    s = '' ###

    if s is None:
        return u""
    else:
        return g.toUnicode(s,g.app.tkEncoding)
#@-node:ekr.20071001091231.200:getAllText
#@+node:ekr.20071001091231.201:getInsertPoint
def getInsertPoint(self): # swingTextWidget.

    w = self
    i = 0 ### i = Tk.Text.index(w,'insert')
    i = w.toPythonIndex(i)
    return i
#@-node:ekr.20071001091231.201:getInsertPoint
#@+node:ekr.20071001091231.202:getSelectedText
def getSelectedText (self): # swingTextWidget.

    w = self
    i,j = w.getSelectionRange()
    if i != j:
        i,j = w.toGuiIndex(i),w.toGuiIndex(j)
        s = '' ### s = Tk.Text.get(w,i,j)
        return g.toUnicode(s,g.app.tkEncoding)
    else:
        return u""
#@-node:ekr.20071001091231.202:getSelectedText
#@+node:ekr.20071001091231.203:getSelectionRange
def getSelectionRange (self,sort=True): # swingTextWidget.

    """Return a tuple representing the selected range.

    Return a tuple giving the insertion point if no range of text is selected."""

    w = self
    sel = 0,0 ### sel = Tk.Text.tag_ranges(w,"sel")
    if len(sel) == 2:
        i,j = sel
    else:
        i = j = 0 ### i = j = Tk.Text.index(w,"insert")

    i,j = w.toPythonIndex(i),w.toPythonIndex(j)  
    if sort and i > j: i,j = j,i
    return i,j
#@nonl
#@-node:ekr.20071001091231.203:getSelectionRange
#@+node:ekr.20071001091231.204:getYScrollPosition
def getYScrollPosition (self):

     w = self
     return 0 ### return w.yview()
#@-node:ekr.20071001091231.204:getYScrollPosition
#@+node:ekr.20071001091231.205:getWidth
def getWidth (self):

    '''Return the width of the widget.
    This is only called for headline widgets,
    and gui's may choose not to do anything here.'''

    w = self
    return 0 ### return w.cget('width')
#@-node:ekr.20071001091231.205:getWidth
#@+node:ekr.20071001091231.206:hasSelection
def hasSelection (self):

    w = self
    i,j = w.getSelectionRange()
    return i != j
#@-node:ekr.20071001091231.206:hasSelection
#@+node:ekr.20071001091231.207:insert
# The signature is more restrictive than the Tk.Text.insert method.

def insert(self,i,s):

    w = self
    i = w.toGuiIndex(i)
    ### Tk.Text.insert(w,i,s)

#@-node:ekr.20071001091231.207:insert
#@+node:ekr.20071001091231.208:indexIsVisible
def indexIsVisible (self,i):

    w = self

    return True ### return w.dlineinfo(i)
#@nonl
#@-node:ekr.20071001091231.208:indexIsVisible
#@+node:ekr.20071001091231.209:mark_set NO LONGER USED
# def mark_set(self,markName,i):

    # w = self
    # i = w.toGuiIndex(i)
    # Tk.Text.mark_set(w,markName,i)
#@-node:ekr.20071001091231.209:mark_set NO LONGER USED
#@+node:ekr.20071001091231.210:replace
def replace (self,i,j,s): # swingTextWidget

    w = self
    i,j = w.toGuiIndex(i),w.toGuiIndex(j)

    ### Tk.Text.delete(w,i,j)
    ### Tk.Text.insert(w,i,s)
#@-node:ekr.20071001091231.210:replace
#@+node:ekr.20071001091231.211:see
def see (self,i): # swingTextWidget.

    w = self
    i = w.toGuiIndex(i)
    ### Tk.Text.see(w,i)
#@-node:ekr.20071001091231.211:see
#@+node:ekr.20071001091231.212:seeInsertPoint
def seeInsertPoint (self): # swingTextWidget.

    w = self
    ### Tk.Text.see(w,'insert')
#@-node:ekr.20071001091231.212:seeInsertPoint
#@+node:ekr.20071001091231.213:selectAllText
def selectAllText (self,insert=None): # swingTextWidget

    '''Select all text of the widget, *not* including the extra newline.'''

    w = self ; s = w.getAllText()
    if insert is None: insert = len(s)
    w.setSelectionRange(0,len(s),insert=insert)
#@-node:ekr.20071001091231.213:selectAllText
#@+node:ekr.20071001091231.214:setAllText
def setAllText (self,s): # swingTextWidget

    w = self

    # state = Tk.Text.cget(w,"state")
    # Tk.Text.configure(w,state="normal")

    # Tk.Text.delete(w,'1.0','end')
    # Tk.Text.insert(w,'1.0',s)

    # Tk.Text.configure(w,state=state)
#@-node:ekr.20071001091231.214:setAllText
#@+node:ekr.20071001091231.215:setBackgroundColor
def setBackgroundColor (self,color):

    w = self
    w.configure(background=color)
#@nonl
#@-node:ekr.20071001091231.215:setBackgroundColor
#@+node:ekr.20071001091231.216:setInsertPoint
def setInsertPoint (self,i): # swingTextWidget.

    w = self
    i = w.toGuiIndex(i)
    # g.trace(i,g.callers())
    ### Tk.Text.mark_set(w,'insert',i)
#@-node:ekr.20071001091231.216:setInsertPoint
#@+node:ekr.20071001091231.217:setSelectionRange
def setSelectionRange (self,i,j,insert=None): # swingTextWidget

    w = self

    i,j = w.toGuiIndex(i),w.toGuiIndex(j)

    # g.trace('i,j,insert',repr(i),repr(j),repr(insert),g.callers())

    # g.trace('i,j,insert',i,j,repr(insert))

    ###
    # if Tk.Text.compare(w,i, ">", j): i,j = j,i
    # Tk.Text.tag_remove(w,"sel","1.0",i)
    # Tk.Text.tag_add(w,"sel",i,j)
    # Tk.Text.tag_remove(w,"sel",j,"end")

    # if insert is not None:
        # w.setInsertPoint(insert)
#@-node:ekr.20071001091231.217:setSelectionRange
#@+node:ekr.20071001091231.218:setYScrollPosition
def setYScrollPosition (self,i):

     w = self
     w.yview('moveto',i)
#@nonl
#@-node:ekr.20071001091231.218:setYScrollPosition
#@+node:ekr.20071001091231.219:setWidth
def setWidth (self,width):

    '''Set the width of the widget.
    This is only called for headline widgets,
    and gui's may choose not to do anything here.'''

    w = self
    w.configure(width=width)
#@-node:ekr.20071001091231.219:setWidth
#@+node:ekr.20071001091231.220:tag_add
# The signature is slightly different than the Tk.Text.insert method.

def tag_add(self,tagName,i,j=None,*args):

    w = self
    i = w.toGuiIndex(i)

    # if j is None:
        # Tk.Text.tag_add(w,tagName,i,*args)
    # else:
        # j = w.toGuiIndex(j)
        # Tk.Text.tag_add(w,tagName,i,j,*args)

#@-node:ekr.20071001091231.220:tag_add
#@+node:ekr.20071001184404:tag_configure (NEW)
def tag_configure (self,*args,**keys):

    pass

tag_config = tag_configure
#@-node:ekr.20071001184404:tag_configure (NEW)
#@+node:ekr.20071001091231.221:tag_ranges
def tag_ranges(self,tagName):

    w = self
    aList = [] ### aList = Tk.Text.tag_ranges(w,tagName)
    aList = [w.toPythonIndex(z) for z in aList]
    return tuple(aList)
#@-node:ekr.20071001091231.221:tag_ranges
#@+node:ekr.20071001091231.222:tag_remove
def tag_remove (self,tagName,i,j=None,*args):

    w = self
    i = w.toGuiIndex(i)

    if j is None:
        pass ### Tk.Text.tag_remove(w,tagName,i,*args)
    else:
        j = w.toGuiIndex(j)
        ### Tk.Text.tag_remove(w,tagName,i,j,*args)


#@-node:ekr.20071001091231.222:tag_remove
#@+node:ekr.20071001091231.223:w.deleteTextSelection
def deleteTextSelection (self): # swingTextWidget

    w = self
    # sel = Tk.Text.tag_ranges(w,"sel")
    # if len(sel) == 2:
        # start,end = sel
        # if Tk.Text.compare(w,start,"!=",end):
            # Tk.Text.delete(w,start,end)
#@-node:ekr.20071001091231.223:w.deleteTextSelection
#@+node:ekr.20071001091231.224:xyToGui/PythonIndex
def xyToGuiIndex (self,x,y): # swingTextWidget

    w = self
    return 0 ### return Tk.Text.index(w,"@%d,%d" % (x,y))

def xyToPythonIndex(self,x,y): # swingTextWidget

    w = self
    i = 0 ### i = Tk.Text.index(w,"@%d,%d" % (x,y))
    i = w.toPythonIndex(i)
    return i
#@-node:ekr.20071001091231.224:xyToGui/PythonIndex
#@-node:ekr.20071001091231.196:Wrapper methods (swingTextWidget)
#@-node:ekr.20071001091231.187:class leoSwingTextWidget (revise)
#@+node:ekr.20071001092453:class leoSwingTree (REWRITE)
class leoSwingTree (leoFrame.leoTree):

    callbacksInjected = False

    """Leo swing tree class."""

    @others
#@+node:ekr.20071001092453.3:  Notes
@killcolor
#@+node:ekr.20071001092453.4:Changes made since first update
@

- disabled drawing of user icons.  They weren't being hidden, which messed up scrolling.

- Expanded clickBox so all clicks fall inside it.

- Added binding for plugBox so it doesn't interfere with the clickBox.  Another weirdness.

- Re-enabled code in drawText that sets the headline state.

- eventToPosition now returns p.copy, which means that nobody can change the list.

- Likewise, clear self.iconIds so old icon id's don't confuse findVnodeWithIconId.

- All drawing methods must do p = p.copy() at the beginning if they make any changes to p.
    - This ensures neither they nor their allies can change the caller's position.
    - In fact, though, only drawTree changes position.  It makes a copy before calling drawNode.
    *** Therefore, all positions in the drawing code are immutable!

- Fixed the race conditions that caused drawing sometimes to fail.  The essential idea is that we must not call w.config if we are about to do a redraw.  For full details, see the Notes node in the Race Conditions section.
#@-node:ekr.20071001092453.4:Changes made since first update
#@+node:ekr.20071001092453.5:Changes made since second update
@

- Removed duplicate code in tree.select.  The following code was being called twice (!!):
    self.endEditLabel()
    self.setUnselectedLabelState(old_p)

- Add p.copy() instead of p when inserting nodes into data structures in select.

- Fixed a _major_ bug in Leo's core.  c.setCurrentPosition must COPY the position given to it!  It's _not_ enough to return a copy of position: it may already have changed!!

- Fixed a another (lesser??) bug in Leo's core.  handleUserClick should also make a copy.

- Fixed bug in mod_scripting.py.  The callback was failing if the script was empty.

- Put in the self.recycle ivar AND THE CODE STILL FAILS.
    It seems to me that this shows there is a bug in my code somewhere, but where ???????????????????
#@-node:ekr.20071001092453.5:Changes made since second update
#@+node:ekr.20071001092453.6:Most recent changes
@

- Added generation count.
    - Incremented on each redraw.
    - Potentially a barrior to race conditions, but it never seemed to do anything.
    - This code is a candidate for elimination.

- Used vnodes rather than positions in several places.
    - I actually don't think this was involved in the real problem, and it doesn't hurt.

- Added much better traces: the beginning of the end for the bugs :-)
    - Added self.verbose option.
    - Added align keyword option to g.trace.
    - Separate each set of traces by a blank line.
        - This makes clear the grouping of id's.

- Defensive code: Disable dragging at start of redraw code.
    - This protects against race conditions.

- Fixed blunder 1: Fixed a number of bugs in the dragging code.
    - I had never looked at this code!
    - Eliminating false drags greatly simplifies matters.

- Fixed blunder 2: Added the following to eventToPosition:
        x = canvas.canvasx(x)
        y = canvas.canvasy(y)
    - Apparently this was the cause of false associations between icons and id's.
    - It's amazing that the code didn't fail earlier without these!

- Converted all module-level constants to ivars.

- Lines no longer interfere with eventToPosition.
    - The problem was that find_nearest or find_overlapping don't depend on stacking order!
    - Added p param to horizontal lines, but not vertical lines.
    - EventToPosition adds 1 to the x coordinate of vertical lines, then recomputes the id.

- Compute indentation only in forceDrawNode.  Removed child_indent constant.

- Simplified drawTree to use indentation returned from forceDrawNode.

- setHeadlineText now ensures that state is "normal" before attempting to set the text.
    - This is the robust way.

7/31/04: newText must call setHeadlineText for all nodes allocated, even if p matches.
#@-node:ekr.20071001092453.6:Most recent changes
#@-node:ekr.20071001092453.3:  Notes
#@+node:ekr.20071001092453.7: Birth... (swingTree)
#@+node:ekr.20071001092453.8:__init__ (swingTree)
def __init__(self,c,frame,canvas):

    # Init the base class.
    leoFrame.leoTree.__init__(self,frame)

    # Configuration and debugging settings.
    # These must be defined here to eliminate memory leaks.
    self.allow_clone_drags          = c.config.getBool('allow_clone_drags')
    self.center_selected_tree_node  = c.config.getBool('center_selected_tree_node')
    self.enable_drag_messages       = c.config.getBool("enable_drag_messages")
    self.expanded_click_area        = c.config.getBool('expanded_click_area')
    self.gc_before_redraw           = c.config.getBool('gc_before_redraw')

    self.headline_text_editing_foreground_color = c.config.getColor(
        'headline_text_editing_foreground_color')
    self.headline_text_editing_background_color = c.config.getColor(
        'headline_text_editing_background_color')
    self.headline_text_editing_selection_foreground_color = c.config.getColor(
        'headline_text_editing_selection_foreground_color')
    self.headline_text_editing_selection_background_color = c.config.getColor(
        'headline_text_editing_selection_background_color')
    self.headline_text_selected_foreground_color = c.config.getColor(
        "headline_text_selected_foreground_color")
    self.headline_text_selected_background_color = c.config.getColor(
        "headline_text_selected_background_color")
    self.headline_text_editing_selection_foreground_color = c.config.getColor(
        "headline_text_editing_selection_foreground_color")
    self.headline_text_editing_selection_background_color = c.config.getColor(
        "headline_text_editing_selection_background_color")
    self.headline_text_unselected_foreground_color = c.config.getColor(
        'headline_text_unselected_foreground_color')
    self.headline_text_unselected_background_color = c.config.getColor(
        'headline_text_unselected_background_color')

    self.idle_redraw = c.config.getBool('idle_redraw')
    self.initialClickExpandsOrContractsNode = c.config.getBool(
        'initialClickExpandsOrContractsNode')
    self.look_for_control_drag_on_mouse_down = c.config.getBool(
        'look_for_control_drag_on_mouse_down')
    self.select_all_text_when_editing_headlines = c.config.getBool(
        'select_all_text_when_editing_headlines')

    self.stayInTree     = c.config.getBool('stayInTreeAfterSelect')
    self.trace          = c.config.getBool('trace_tree')
    self.trace_alloc    = c.config.getBool('trace_tree_alloc')
    self.trace_chapters = c.config.getBool('trace_chapters')
    self.trace_edit     = c.config.getBool('trace_tree_edit')
    self.trace_gc       = c.config.getBool('trace_tree_gc')
    self.trace_redraw   = c.config.getBool('trace_tree_redraw')
    self.trace_select   = c.config.getBool('trace_select')
    self.trace_stats    = c.config.getBool('show_tree_stats')
    self.use_chapters   = False and c.config.getBool('use_chapters') ###

    # Objects associated with this tree.
    self.canvas = canvas

    << define drawing constants >>
    << old ivars >>
    << inject callbacks into the position class >>

    self.dragging = False
    self.generation = 0
    self.prevPositions = 0
    self.redrawing = False # Used only to disable traces.
    self.redrawCount = 0 # Count for debugging.
    self.revertHeadline = None # Previous headline text for abortEditLabel.

    # New in 4.4: We should stay in the tree to use per-pane bindings.
    self.textBindings = [] # Set in setBindings.
    self.textNumber = 0 # To make names unique.
    self.updateCount = 0 # Drawing is enabled only if self.updateCount <= 0
    self.verbose = True

    self.setEditPosition(None) # Set positions returned by leoTree.editPosition()

    # Keys are id's, values are positions...
    self.ids = {}
    self.iconIds = {}

    # Lists of visible (in-use) widgets...
    self.visibleBoxes = []
    self.visibleClickBoxes = []
    self.visibleIcons = []
    self.visibleLines = []
    self.visibleText  = {}
        # Pre 4.4b2: Keys are vnodes, values are Tk.Text widgets.
        #     4.4b2: Keys are p.key(), values are Tk.Text widgets.
    self.visibleUserIcons = []

    # Lists of free, hidden widgets...
    self.freeBoxes = []
    self.freeClickBoxes = []
    self.freeIcons = []
    self.freeLines = []
    self.freeText = [] # New in 4.4b2: a list of free Tk.Text widgets

    self.freeUserIcons = []
#@+node:ekr.20071001092453.9:<< define drawing constants >>
self.box_padding = 5 # extra padding between box and icon
self.box_width = 9 + self.box_padding
self.icon_width = 20
self.text_indent = 4 # extra padding between icon and tex

self.hline_y = 7 # Vertical offset of horizontal line
self.root_left = 7 + self.box_width
self.root_top = 2

self.default_line_height = 17 + 2 # default if can't set line_height from font.
self.line_height = self.default_line_height
#@-node:ekr.20071001092453.9:<< define drawing constants >>
#@+node:ekr.20071001092453.10:<< old ivars >>
# Miscellaneous info.
self.iconimages = {} # Image cache set by getIconImage().
self.active = False # True if present headline is active
self._editPosition = None # Returned by leoTree.editPosition()
self.lineyoffset = 0 # y offset for this headline.
self.lastClickFrameId = None # id of last entered clickBox.
self.lastColoredText = None # last colored text widget.

# Set self.font and self.fontName.
self.setFontFromConfig()

# Drag and drop
self.drag_p = None
self.controlDrag = False # True: control was down when drag started.

# Keep track of popup menu so we can handle behavior better on Linux Context menu
self.popupMenu = None

# Incremental redraws:
self.allocateOnlyVisibleNodes = False # True: enable incremental redraws.
self.prevMoveToFrac = 0.0
self.visibleArea = None
self.expandedVisibleArea = None

###
# if self.allocateOnlyVisibleNodes:
    # self.frame.bar1.bind("<B1-ButtonRelease>", self.redraw_now)
#@-node:ekr.20071001092453.10:<< old ivars >>
#@+node:ekr.20071001092453.11:<< inject callbacks into the position class >>
# The new code injects 3 callbacks for the colorizer.

if not leoSwingTree.callbacksInjected: # Class var.
    leoSwingTree.callbacksInjected = True
    self.injectCallbacks()
#@-node:ekr.20071001092453.11:<< inject callbacks into the position class >>
#@-node:ekr.20071001092453.8:__init__ (swingTree)
#@+node:ekr.20071001092453.12:swingTtree.setBindings
def setBindings (self,):

    '''Create master bindings for all headlines.'''

    tree = self ; k = self.c.k ; canvas = self.canvas

    if 0:

        # g.trace('self',self,'canvas',canvas)

        << make bindings for a common binding widget >>

        tree.setCanvasBindings(canvas)

        k.completeAllBindingsForWidget(canvas)

        k.completeAllBindingsForWidget(self.bindingWidget)

#@+node:ekr.20071001092453.13:<< make bindings for a common binding widget >>
self.bindingWidget = w = g.app.gui.plainTextWidget(
    self.canvas,name='bindingWidget')

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

table = (
    ('<Button-1>',       k.masterClickHandler,          tree.onHeadlineClick),
    ('<Button-3>',       k.masterClick3Handler,         tree.onHeadlineRightClick),
    ('<Double-Button-1>',k.masterDoubleClickHandler,    tree.onHeadlineClick),
    ('<Double-Button-3>',k.masterDoubleClick3Handler,   tree.onHeadlineRightClick),
)

for a,handler,func in table:
    def treeBindingCallback(event,handler=handler,func=func):
        # g.trace('func',func)
        return handler(event,func)
    w.bind(a,treeBindingCallback)

### self.textBindings = w.bindtags()
#@-node:ekr.20071001092453.13:<< make bindings for a common binding widget >>
#@-node:ekr.20071001092453.12:swingTtree.setBindings
#@+node:ekr.20071001092453.14:swingTree.setCanvasBindings
def setCanvasBindings (self,canvas):

    k = self.c.k

    if 0: ###

        canvas.bind('<Key>',k.masterKeyHandler)
        canvas.bind('<Button-1>',self.onTreeClick)

        << make bindings for tagged items on the canvas >>
        << create baloon bindings for tagged items on the canvas >>
#@+node:ekr.20071001092453.15:<< make bindings for tagged items on the canvas >>
where = g.choose(self.expanded_click_area,'clickBox','plusBox')

###
# table = (
    # (where,    '<Button-1>',self.onClickBoxClick),
    # ('iconBox','<Button-1>',self.onIconBoxClick),
    # ('iconBox','<Double-1>',self.onIconBoxDoubleClick),
    # ('iconBox','<Button-3>',self.onIconBoxRightClick),
    # ('iconBox','<Double-3>',self.onIconBoxRightClick),
    # ('iconBox','<B1-Motion>',self.onDrag),
    # ('iconBox','<Any-ButtonRelease-1>',self.onEndDrag),
# )
# for tag,event,callback in table:
    # canvas.tag_bind(tag,event,callback)
#@-node:ekr.20071001092453.15:<< make bindings for tagged items on the canvas >>
#@+node:ekr.20071001092453.16:<< create baloon bindings for tagged items on the canvas >>
if 0: # I find these very irritating.
    for tag,text in (
        # ('plusBox','plusBox'),
        ('iconBox','Icon Box'),
        ('selectBox','Click to select'),
        ('clickBox','Click to expand or contract'),
        # ('textBox','Headline'),
    ):
        # A fairly long wait is best.
        balloon = Pmw.Balloon(self.canvas,initwait=700)
        balloon.tagbind(self.canvas,tag,balloonHelp=text)
#@-node:ekr.20071001092453.16:<< create baloon bindings for tagged items on the canvas >>
#@-node:ekr.20071001092453.14:swingTree.setCanvasBindings
#@-node:ekr.20071001092453.7: Birth... (swingTree)
#@+node:ekr.20071001092453.17:Allocation...
#@+node:ekr.20071001092453.18:newBox
def newBox (self,p,x,y,image):

    canvas = self.canvas ; tag = "plusBox"

    if self.freeBoxes:
        theId = self.freeBoxes.pop(0)
        canvas.coords(theId,x,y)
        canvas.itemconfigure(theId,image=image)
    else:
        theId = canvas.create_image(x,y,image=image,tag=tag)
        if self.trace_alloc: g.trace("%3d %s" % (theId,p and p.headString()),align=-20)

    if theId not in self.visibleBoxes: 
        self.visibleBoxes.append(theId)

    if p:
        self.ids[theId] = p

    return theId
#@-node:ekr.20071001092453.18:newBox
#@+node:ekr.20071001092453.19:newClickBox
def newClickBox (self,p,x1,y1,x2,y2):

    canvas = self.canvas ; defaultColor = ""
    tag = g.choose(p.hasChildren(),'clickBox','selectBox')

    if self.freeClickBoxes:
        theId = self.freeClickBoxes.pop(0)
        canvas.coords(theId,x1,y1,x2,y2)
        canvas.itemconfig(theId,tag=tag)
    else:
        theId = self.canvas.create_rectangle(x1,y1,x2,y2,tag=tag)
        canvas.itemconfig(theId,fill=defaultColor,outline=defaultColor)
        if self.trace_alloc: g.trace("%3d %s" % (theId,p and p.headString()),align=-20)

    if theId not in self.visibleClickBoxes:
        self.visibleClickBoxes.append(theId)
    if p:
        self.ids[theId] = p

    return theId
#@-node:ekr.20071001092453.19:newClickBox
#@+node:ekr.20071001092453.20:newIcon
def newIcon (self,p,x,y,image):

    canvas = self.canvas ; tag = "iconBox"

    if self.freeIcons:
        theId = self.freeIcons.pop(0)
        canvas.itemconfigure(theId,image=image)
        canvas.coords(theId,x,y)
    else:
        theId = canvas.create_image(x,y,image=image,anchor="nw",tag=tag)
        if self.trace_alloc: g.trace("%3d %s" % (theId,p and p.headString()),align=-20)

    if theId not in self.visibleIcons:
        self.visibleIcons.append(theId)

    if p:
        data = p,self.generation
        self.iconIds[theId] = data # Remember which vnode belongs to the icon.
        self.ids[theId] = p

    return theId
#@-node:ekr.20071001092453.20:newIcon
#@+node:ekr.20071001092453.21:newLine
def newLine (self,p,x1,y1,x2,y2):

    canvas = self.canvas

    if self.freeLines:
        theId = self.freeLines.pop(0)
        canvas.coords(theId,x1,y1,x2,y2)
    else:
        theId = canvas.create_line(x1,y1,x2,y2,tag="lines",fill="gray50") # stipple="gray25")
        if self.trace_alloc: g.trace("%3d %s" % (theId,p and p.headString()),align=-20)

    if p:
        self.ids[theId] = p

    if theId not in self.visibleLines:
        self.visibleLines.append(theId)

    return theId
#@-node:ekr.20071001092453.21:newLine
#@+node:ekr.20071001092453.22:newText (swingTree) and helper
def newText (self,p,x,y):

    canvas = self.canvas ; tag = "textBox"
    c = self.c ;  k = c.k
    if self.freeText:
        w,theId = self.freeText.pop()
        canvas.coords(theId,x,y) # Make the window visible again.
            # theId is the id of the *window* not the text.
    else:
        # Tags are not valid in Tk.Text widgets.
        self.textNumber += 1
        w = g.app.gui.plainTextWidget(
            canvas,name='head-%d' % self.textNumber,
            state="normal",font=self.font,bd=0,relief="flat",height=1)
        ### w.bindtags(self.textBindings) # Set the bindings for this widget.

        if 0: # Crashes on XP.
            << patch by Maciej Kalisiak to handle scroll-wheel events >>

        theId = canvas.create_window(x,y,anchor="nw",window=w,tag=tag)
        w.leo_window_id = theId # Never changes.

        if self.trace_alloc: g.trace('%3d %6s' % (theId,id(w)),align=-20)

    # Common configuration.
    if 0: # Doesn't seem to work.
        balloon = Pmw.Balloon(canvas,initwait=700)
        balloon.tagbind(canvas,theId,balloonHelp='Headline')

    if p:
        self.ids[theId] = p # Add the id of the *window*
        self.setHeadlineText(theId,w,p.headString())
        w.configure(width=self.headWidth(p=p))
        w.leo_position = p # This p never changes.
            # *Required*: onHeadlineClick uses w.leo_position to get p.

        # Keys are p.key().  Entries are (w,theId)
        self.visibleText [p.key()] = w,theId
    else:
        g.trace('**** can not happen.  No p')

    return w
#@+node:ekr.20071001092453.23:<< patch by Maciej Kalisiak  to handle scroll-wheel events >>
def PropagateButton4(e):
    canvas.event_generate("<Button-4>")
    return "break"

def PropagateButton5(e):
    canvas.event_generate("<Button-5>")
    return "break"

def PropagateMouseWheel(e):
    canvas.event_generate("<MouseWheel>")
    return "break"

### 
# instance_tag = w.bindtags()[0]
# w.bind_class(instance_tag, "<Button-4>", PropagateButton4)
# w.bind_class(instance_tag, "<Button-5>", PropagateButton5)
# w.bind_class(instance_tag, "<MouseWheel>",PropagateMouseWheel)
#@-node:ekr.20071001092453.23:<< patch by Maciej Kalisiak  to handle scroll-wheel events >>
#@+node:ekr.20071001092453.24:tree.setHeadlineText
def setHeadlineText (self,theId,w,s):

    """All changes to text widgets should come here."""

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

    # if self.trace_alloc: g.trace('%4d %6s %s' % (theId,self.textAddr(w),s),align=-20)

    state = w.cget("state")
    if state != "normal":
        w.configure(state="normal")
    w.delete(0,"end")
    # Important: do not allow newlines in headlines.
    while s.endswith('\n') or s.endswith('\r'):
        s = s[:-1]
    w.insert("end",s)
    # g.trace(repr(s))
    if state != "normal":
        w.configure(state=state)
#@-node:ekr.20071001092453.24:tree.setHeadlineText
#@-node:ekr.20071001092453.22:newText (swingTree) and helper
#@+node:ekr.20071001092453.25:recycleWidgets
def recycleWidgets (self):

    canvas = self.canvas

    for theId in self.visibleBoxes:
        if theId not in self.freeBoxes:
            self.freeBoxes.append(theId)
        canvas.coords(theId,-100,-100)
    self.visibleBoxes = []

    for theId in self.visibleClickBoxes:
        if theId not in self.freeClickBoxes:
            self.freeClickBoxes.append(theId)
        canvas.coords(theId,-100,-100,-100,-100)
    self.visibleClickBoxes = []

    for theId in self.visibleIcons:
        if theId not in self.freeIcons:
            self.freeIcons.append(theId)
        canvas.coords(theId,-100,-100)
    self.visibleIcons = []

    for theId in self.visibleLines:
        if theId not in self.freeLines:
            self.freeLines.append(theId)
        canvas.coords(theId,-100,-100,-100,-100)
    self.visibleLines = []

    aList = self.visibleText.values()
    for data in aList:
        w,theId = data
        # assert theId == w.leo_window_id
        canvas.coords(theId,-100,-100)
        w.leo_position = None # Allow the position to be freed.
        if data not in self.freeText:
            self.freeText.append(data)
    self.visibleText = {}

    for theId in self.visibleUserIcons:
        # The present code does not recycle user Icons.
        self.canvas.delete(theId)
    self.visibleUserIcons = []
#@-node:ekr.20071001092453.25:recycleWidgets
#@+node:ekr.20071001092453.26:destroyWidgets
def destroyWidgets (self):

    self.ids = {}

    self.visibleBoxes = []
    self.visibleClickBoxes = []
    self.visibleIcons = []
    self.visibleLines = []
    self.visibleUserIcons = []

    self.visibleText = {}

    self.freeText = []
    self.freeBoxes = []
    self.freeClickBoxes = []
    self.freeIcons = []
    self.freeLines = []

    self.canvas.delete("all")
#@-node:ekr.20071001092453.26:destroyWidgets
#@+node:ekr.20071001092453.27:showStats
def showStats (self):

    z = []
    for kind,a,b in (
        ('boxes',self.visibleBoxes,self.freeBoxes),
        ('clickBoxes',self.visibleClickBoxes,self.freeClickBoxes),
        ('icons',self.visibleIcons,self.freeIcons),
        ('lines',self.visibleLines,self.freeLines),
        ('tesxt',self.visibleText.values(),self.freeText),
    ):
        z.append('%10s used: %4d free: %4d' % (kind,len(a),len(b)))

    g.es_print('\n' + '\n'.join(z))
#@-node:ekr.20071001092453.27:showStats
#@-node:ekr.20071001092453.17:Allocation...
#@+node:ekr.20071001092453.28:Config & Measuring...
#@+node:ekr.20071001092453.29:tree.getFont,setFont,setFontFromConfig
def getFont (self):

    return self.font

def setFont (self,font=None, fontName=None):

    # ESSENTIAL: retain a link to font.
    if fontName:
        self.fontName = fontName
        self.font = swingFont.Font(font=fontName)
    else:
        self.fontName = None
        self.font = font

    self.setLineHeight(self.font)

# Called by ctor and when config params are reloaded.
def setFontFromConfig (self):
    c = self.c
    # g.trace()
    font = c.config.getFontFromParams(
        "headline_text_font_family", "headline_text_font_size",
        "headline_text_font_slant",  "headline_text_font_weight",
        c.config.defaultTreeFontSize)

    self.setFont(font)
#@-node:ekr.20071001092453.29:tree.getFont,setFont,setFontFromConfig
#@+node:ekr.20071001092453.30:headWidth & widthInPixels
def headWidth(self,p=None,s=''):

    """Returns the proper width of the entry widget for the headline."""

    if p: s = p.headString()

    return self.font.measure(s)/self.font.measure('0')+1


def widthInPixels(self,s):

    s = g.toEncodedString(s,g.app.tkEncoding)

    return self.font.measure(s)
#@-node:ekr.20071001092453.30:headWidth & widthInPixels
#@+node:ekr.20071001092453.31:setLineHeight
def setLineHeight (self,font):

    pass ###

    # try:
        # metrics = font.metrics()
        # linespace = metrics ["linespace"]
        # self.line_height = linespace + 5 # Same as before for the default font on Windows.
        # # print metrics
    # except:
        # self.line_height = self.default_line_height
        # g.es("exception setting outline line height")
        # g.es_exception()
#@-node:ekr.20071001092453.31:setLineHeight
#@-node:ekr.20071001092453.28:Config & Measuring...
#@+node:ekr.20071001092453.32:Debugging...
#@+node:ekr.20071001092453.33:textAddr
def textAddr(self,w):

    """Return the address part of repr(Tk.Text)."""

    return repr(w)[-9:-1].lower()
#@-node:ekr.20071001092453.33:textAddr
#@+node:ekr.20071001092453.34:traceIds (Not used)
# Verbose tracing is much more useful than this because we can see the recent past.

def traceIds (self,full=False):

    tree = self

    for theDict,tag,flag in ((tree.ids,"ids",True),(tree.iconIds,"icon ids",False)):
        print '=' * 60
        print ; print "%s..." % tag
        keys = theDict.keys()
        keys.sort()
        for key in keys:
            p = tree.ids.get(key)
            if p is None: # For lines.
                print "%3d None" % key
            else:
                print "%3d" % key,p.headString()
        if flag and full:
            print '-' * 40
            values = theDict.values()
            values.sort()
            seenValues = []
            for value in values:
                if value not in seenValues:
                    seenValues.append(value)
                    for item in theDict.items():
                        key,val = item
                        if val and val == value:
                            print "%3d" % key,val.headString()
#@-node:ekr.20071001092453.34:traceIds (Not used)
#@-node:ekr.20071001092453.32:Debugging...
#@+node:ekr.20071001092453.35:Drawing... (swingTree)
#@+node:ekr.20071001092453.36:tree.begin/endUpdate
def beginUpdate (self):

    self.updateCount += 1
    # g.trace('tree',id(self),self.updateCount,g.callers())

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

    self.updateCount -= 1
    # g.trace(self.updateCount,'scroll',scroll,g.callers())

    if self.updateCount <= 0:
        if flag:
            self.redraw_now(scroll=scroll)
        if self.updateCount < 0:
            g.trace("Can't happen: negative updateCount",g.callers())
#@-node:ekr.20071001092453.36:tree.begin/endUpdate
#@+node:ekr.20071001092453.37:tree.redraw_now & helper
# New in 4.4b2: suppress scrolling by default.

def redraw_now (self,scroll=False):

    '''Redraw immediately: used by Find so a redraw doesn't mess up selections in headlines.'''

    if g.app.quitting or self.drag_p or self.frame not in g.app.windowList:
        return

    c = self.c

    # g.trace(g.callers())

    if not g.app.unitTesting:
        if self.gc_before_redraw:
            g.collectGarbage()
        if g.app.trace_gc_verbose:
            if (self.redrawCount % 5) == 0:
                g.printGcSummary()
        if self.trace_redraw or self.trace_alloc:
            # g.trace(self.redrawCount,g.callers())
            # g.trace(c.rootPosition().headString(),'canvas:',id(self.canvas),g.callers())
            if self.trace_stats:
                g.print_stats()
                g.clear_stats()

    # New in 4.4b2: Call endEditLabel, but suppress the redraw.
    self.beginUpdate()
    try:
        self.endEditLabel()
    finally:
        self.endUpdate(False)

    # Do the actual redraw.
    self.expandAllAncestors(c.currentPosition())
    if self.idle_redraw:
        def idleRedrawCallback(event=None,self=self,scroll=scroll):
            self.redrawHelper(scroll=scroll)
        ### self.canvas.after_idle(idleRedrawCallback)
    else:
        self.redrawHelper(scroll=scroll)
    if g.app.unitTesting:
        self.canvas.update_idletasks() # Important for unit tests.
    c.masterFocusHandler()

redraw = redraw_now # Compatibility
#@+node:ekr.20071001092453.38:redrawHelper
def redrawHelper (self,scroll=True):

    c = self.c

    ###

    # oldcursor = self.canvas['cursor']
    # self.canvas['cursor'] = "watch"

    # if not g.doHook("redraw-entire-outline",c=c):
        # c.setTopVnode(None)
        # self.setVisibleAreaToFullCanvas()
        # self.drawTopTree()
        # # Set up the scroll region after the tree has been redrawn.
        # bbox = self.canvas.bbox('all')
        # # g.trace('canvas',self.canvas,'bbox',bbox)
        # if bbox is None:
            # x0,y0,x1,y1 = 0,0,100,100
        # else:
            # x0, y0, x1, y1 = bbox
        # self.canvas.configure(scrollregion=(0, 0, x1, y1))
        # if scroll:
            # self.canvas.update_idletasks() # Essential.
            # self.scrollTo()

    g.doHook("after-redraw-outline",c=c)

    ### self.canvas['cursor'] = oldcursor
#@-node:ekr.20071001092453.38:redrawHelper
#@-node:ekr.20071001092453.37:tree.redraw_now & helper
#@+node:ekr.20071001092453.39:idle_second_redraw
def idle_second_redraw (self):

    c = self.c

    # Erase and redraw the entire tree the SECOND time.
    # This ensures that all visible nodes are allocated.
    c.setTopVnode(None)
    args = self.canvas.yview()
    self.setVisibleArea(args)

    if 0:
        self.deleteBindings()
        self.canvas.delete("all")

    self.drawTopTree()

    if self.trace:
        g.trace(self.redrawCount)
#@-node:ekr.20071001092453.39:idle_second_redraw
#@+node:ekr.20071001092453.40:drawX...
#@+node:ekr.20071001092453.41:drawBox
def drawBox (self,p,x,y):

    tree = self ; c = self.c
    y += 7 # draw the box at x, y+7

    theId = g.doHook("draw-outline-box",tree=tree,c=c,p=p,v=p,x=x,y=y)

    if theId is None:
        # if self.trace_gc: g.printNewObjects(tag='box 1')
        iconname = g.choose(p.isExpanded(),"minusnode.gif", "plusnode.gif")
        image = self.getIconImage(iconname)
        theId = self.newBox(p,x,y+self.lineyoffset,image)
        # if self.trace_gc: g.printNewObjects(tag='box 2')
        return theId
    else:
        return theId
#@-node:ekr.20071001092453.41:drawBox
#@+node:ekr.20071001092453.42:drawClickBox
def drawClickBox (self,p,y):

    h = self.line_height

    # Define a slighly larger rect to catch clicks.
    if self.expanded_click_area:
        self.newClickBox(p,0,y,1000,y+h-2)
#@-node:ekr.20071001092453.42:drawClickBox
#@+node:ekr.20071001092453.43:drawIcon
def drawIcon(self,p,x=None,y=None):

    """Draws icon for position p at x,y, or at p.v.iconx,p.v.icony if x,y = None,None"""

    # if self.trace_gc: g.printNewObjects(tag='icon 1')

    c = self.c ; v = p.v
    << compute x,y and iconVal >>
    v.iconVal = val

    if not g.doHook("draw-outline-icon",tree=self,c=c,p=p,v=p,x=x,y=y):

        # Get the image.
        imagename = "box%02d.GIF" % val
        image = self.getIconImage(imagename)
        self.newIcon(p,x,y+self.lineyoffset,image)

    return 0,self.icon_width # dummy icon height,width
#@+node:ekr.20071001092453.44:<< compute x,y and iconVal >>
if x is None and y is None:
    try:
        x,y = v.iconx, v.icony
    except:
        # Inject the ivars.
        x,y = v.iconx, v.icony = 0,0
else:
    # Inject the ivars.
    v.iconx, v.icony = x,y

y += 2 # draw icon at y + 2

# Always recompute v.iconVal.
# This is an important drawing optimization.
val = v.computeIcon()
assert(0 <= val <= 15)
# g.trace(v,val)
#@nonl
#@-node:ekr.20071001092453.44:<< compute x,y and iconVal >>
#@-node:ekr.20071001092453.43:drawIcon
#@+node:ekr.20071001092453.45:drawLine
def drawLine (self,p,x1,y1,x2,y2):

    theId = self.newLine(p,x1,y1,x2,y2)

    return theId
#@-node:ekr.20071001092453.45:drawLine
#@+node:ekr.20071001092453.46:drawNode & force_draw_node (good trace)
def drawNode(self,p,x,y):

    c = self.c

    # g.trace(x,y,p,id(self.canvas))

    data = g.doHook("draw-outline-node",tree=self,c=c,p=p,v=p,x=x,y=y)
    if data is not None: return data

    if 1:
        self.lineyoffset = 0
    else:
        if hasattr(p.v.t,"unknownAttributes"):
            self.lineyoffset = p.v.t.unknownAttributes.get("lineYOffset",0)
        else:
            self.lineyoffset = 0

    # Draw the horizontal line.
    self.drawLine(p,
        x,y+7+self.lineyoffset,
        x+self.box_width,y+7+self.lineyoffset)

    if self.inVisibleArea(y):
        return self.force_draw_node(p,x,y)
    else:
        return self.line_height,0
#@+node:ekr.20071001092453.47:force_draw_node
def force_draw_node(self,p,x,y):

    h = 0 # The total height of the line.
    indent = 0 # The amount to indent this line.

    h2,w2 = self.drawUserIcons(p,"beforeBox",x,y)
    h = max(h,h2) ; x += w2 ; indent += w2

    if p.hasChildren():
        self.drawBox(p,x,y)

    indent += self.box_width
    x += self.box_width # even if box isn't drawn.

    h2,w2 = self.drawUserIcons(p,"beforeIcon",x,y)
    h = max(h,h2) ; x += w2 ; indent += w2

    h2,w2 = self.drawIcon(p,x,y)
    h = max(h,h2) ; x += w2 ; indent += w2/2

    # Nothing after here affects indentation.
    h2,w2 = self.drawUserIcons(p,"beforeHeadline",x,y)
    h = max(h,h2) ; x += w2

    h2 = self.drawText(p,x,y)
    h = max(h,h2)
    x += self.widthInPixels(p.headString())

    h2,w2 = self.drawUserIcons(p,"afterHeadline",x,y)
    h = max(h,h2)

    self.drawClickBox(p,y)

    return h,indent
#@-node:ekr.20071001092453.47:force_draw_node
#@-node:ekr.20071001092453.46:drawNode & force_draw_node (good trace)
#@+node:ekr.20071001092453.48:drawText
def drawText(self,p,x,y):

    """draw text for position p at nominal coordinates x,y."""

    assert(p)

    c = self.c
    x += self.text_indent

    data = g.doHook("draw-outline-text-box",tree=self,c=c,p=p,v=p,x=x,y=y)
    if data is not None: return data

    self.newText(p,x,y+self.lineyoffset)

    self.configureTextState(p)

    return self.line_height
#@-node:ekr.20071001092453.48:drawText
#@+node:ekr.20071001092453.49:drawUserIcons
def drawUserIcons(self,p,where,x,y):

    """Draw any icons specified by p.v.t.unknownAttributes["icons"]."""

    h,w = 0,0 ; t = p.v.t

    if not hasattr(t,"unknownAttributes"):
        return h,w

    iconsList = t.unknownAttributes.get("icons")
    if not iconsList:
        return h,w

    try:
        for theDict in iconsList:
            h2,w2 = self.drawUserIcon(p,where,x,y,w,theDict)
            h = max(h,h2) ; w += w2
    except:
        g.es_exception()

    # g.trace(where,h,w)

    return h,w
#@-node:ekr.20071001092453.49:drawUserIcons
#@+node:ekr.20071001092453.50:drawUserIcon
def drawUserIcon (self,p,where,x,y,w2,theDict):

    h,w = 0,0

    if where != theDict.get("where","beforeHeadline"):
        return h,w

    # if self.trace_gc: g.printNewObjects(tag='userIcon 1')

    # g.trace(where,x,y,theDict)

    << set offsets and pads >>
    theType = theDict.get("type")
    if theType == "icon":
        if 0: # not ready yet.
            s = theDict.get("icon")
            << draw the icon in string s >>
    elif theType == "file":
        theFile = theDict.get("file")
        << draw the icon at file >>
    elif theType == "url":
        ## url = theDict.get("url")
        << draw the icon at url >>

    # Allow user to specify height, width explicitly.
    h = theDict.get("height",h)
    w = theDict.get("width",w)

    # if self.trace_gc: g.printNewObjects(tag='userIcon 2')

    return h,w
#@+node:ekr.20071001092453.51:<< set offsets and pads >>
xoffset = theDict.get("xoffset")
try:    xoffset = int(xoffset)
except: xoffset = 0

yoffset = theDict.get("yoffset")
try:    yoffset = int(yoffset)
except: yoffset = 0

xpad = theDict.get("xpad")
try:    xpad = int(xpad)
except: xpad = 0

ypad = theDict.get("ypad")
try:    ypad = int(ypad)
except: ypad = 0
#@-node:ekr.20071001092453.51:<< set offsets and pads >>
#@+node:ekr.20071001092453.52:<< draw the icon in string s >>
pass
#@-node:ekr.20071001092453.52:<< draw the icon in string s >>
#@+node:ekr.20071001092453.53:<< draw the icon at file >>
try:
    image = self.iconimages[theFile]
    # Get the image from the cache if possible.
except KeyError:
    try:
        fullname = g.os_path_join(g.app.loadDir,"..","Icons",theFile)
        fullname = g.os_path_normpath(fullname)
        image = Tk.PhotoImage(master=self.canvas,file=fullname)
        self.iconimages[fullname] = image
    except:
        #g.es("Exception loading: " + fullname)
        #g.es_exception()
        image = None

if image:
    theId = self.canvas.create_image(
        x+xoffset+w2,y+yoffset,
        anchor="nw",image=image,tag="userIcon")
    self.ids[theId] = p
    # assert(theId not in self.visibleIcons)
    self.visibleUserIcons.append(theId)

    h = image.height() + yoffset + ypad
    w = image.width()  + xoffset + xpad
#@-node:ekr.20071001092453.53:<< draw the icon at file >>
#@+node:ekr.20071001092453.54:<< draw the icon at url >>
pass
#@-node:ekr.20071001092453.54:<< draw the icon at url >>
#@-node:ekr.20071001092453.50:drawUserIcon
#@+node:ekr.20071001092453.55:drawTopTree
def drawTopTree (self):

    """Draws the top-level tree, taking into account the hoist state."""

    c = self.c ; canvas = self.canvas
    trace = False or self.trace or self.trace_redraw

    self.redrawing = True

    # Recycle all widgets and clear all widget lists.
    self.recycleWidgets()
    # Clear all ids so invisible id's don't confuse eventToPosition & findPositionWithIconId
    self.ids = {}
    self.iconIds = {}
    self.generation += 1
    self.redrawCount += 1
    self.drag_p = None # Disable drags across redraws.
    self.dragging = False
    if trace:
        g.trace('redrawCount',self.redrawCount,g.callers()) # 'len(c.hoistStack)',len(c.hoistStack))
        if 0:
            delta = g.app.positions - self.prevPositions
            g.trace("**** gen: %-3d positions: %5d +%4d" % (
                self.generation,g.app.positions,delta),g.callers())

    self.prevPositions = g.app.positions
    if self.trace_gc: g.printNewObjects(tag='top 1')

    hoistFlag = c.hoistStack
    if c.hoistStack:
        bunch = c.hoistStack[-1] ; p = bunch.p
        h = p.headString()
        if len(c.hoistStack) == 1 and h.startswith('@chapter') and p.hasChildren():
            p = p.firstChild()
            hoistFlag = False
    else:
        p = c.rootPosition()

    self.drawTree(p,self.root_left,self.root_top,0,0,hoistFlag=hoistFlag)

    if self.trace_gc: g.printNewObjects(tag='top 2')
    if self.trace_stats: self.showStats()

    canvas.lower("lines")  # Lowest.
    canvas.lift("textBox") # Not the Tk.Text widget: it should be low.
    canvas.lift("userIcon")
    canvas.lift("plusBox")
    canvas.lift("clickBox")
    canvas.lift("clickExpandBox")
    canvas.lift("iconBox") # Higest.

    self.redrawing = False
#@-node:ekr.20071001092453.55:drawTopTree
#@+node:ekr.20071001092453.56:drawTree
def drawTree(self,p,x,y,h,level,hoistFlag=False):

    tree = self ; c = self.c
    yfirst = ylast = y ; h1 = None
    data = g.doHook("draw-sub-outline",tree=tree,
        c=c,p=p,v=p,x=x,y=y,h=h,level=level,hoistFlag=hoistFlag)
    if data is not None: return data

    while p: # Do not use iterator.
        # This is the ONLY copy of p that needs to be made;
        # no other drawing routine calls any p.moveTo method.
        const_p = p.copy()
        h,indent = self.drawNode(const_p,x,y)
        if h1 is None: h1 = h # Set h1 *after* calling drawNode.
        y += h ; ylast = y
        if p.isExpanded() and p.hasFirstChild():
            # Must make an additional copy here by calling firstChild.
            y = self.drawTree(p.firstChild(),x+indent,y,h,level+1)
        if hoistFlag: break
        else:         p = p.next()
    # Draw the vertical line.
    if h1 is None: h1 = h
    y2 = g.choose(level==0,yfirst+(h1-1)/2,yfirst-h1/2-1)
    self.drawLine(None,x,y2,x,ylast+self.hline_y-h)
    return y
#@-node:ekr.20071001092453.56:drawTree
#@-node:ekr.20071001092453.40:drawX...
#@+node:ekr.20071001092453.57:Helpers...
#@+node:ekr.20071001092453.58:getIconImage
def getIconImage (self, name):

    # Return the image from the cache if possible.
    if self.iconimages.has_key(name):
        return self.iconimages[name]

    # g.trace(name)

    try:
        fullname = g.os_path_join(g.app.loadDir,"..","Icons",name)
        fullname = g.os_path_normpath(fullname)
        image = Tk.PhotoImage(master=self.canvas,file=fullname)
        self.iconimages[name] = image
        return image
    except:
        g.es("Exception loading: " + fullname)
        g.es_exception()
        return None
#@-node:ekr.20071001092453.58:getIconImage
#@+node:ekr.20071001092453.59:inVisibleArea & inExpandedVisibleArea
def inVisibleArea (self,y1):

    if self.allocateOnlyVisibleNodes:
        if self.visibleArea:
            vis1,vis2 = self.visibleArea
            y2 = y1 + self.line_height
            return y2 >= vis1 and y1 <= vis2
        else: return False
    else:
        return True # This forces all nodes to be allocated on all redraws.

def inExpandedVisibleArea (self,y1):

    if self.expandedVisibleArea:
        vis1,vis2 = self.expandedVisibleArea
        y2 = y1 + self.line_height
        return y2 >= vis1 and y1 <= vis2
    else:
        return False
#@-node:ekr.20071001092453.59:inVisibleArea & inExpandedVisibleArea
#@+node:ekr.20071001092453.60:numberOfVisibleNodes
def numberOfVisibleNodes(self):

    c = self.c

    n = 0 ; p = self.c.rootPosition()
    while p:
        n += 1
        p.moveToVisNext(c)
    return n
#@-node:ekr.20071001092453.60:numberOfVisibleNodes
#@+node:ekr.20071001092453.61:scrollTo (swingTree)
def scrollTo(self,p=None):

    """Scrolls the canvas so that p is in view."""

    # __pychecker__ = '--no-argsused' # event not used.
    # __pychecker__ = '--no-intdivide' # suppress warning about integer division.

    c = self.c ; frame = c.frame ; trace = True
    if not p or not c.positionExists(p):
        p = c.currentPosition()
    if not p or not c.positionExists(p):
        if trace: g.trace('current p does not exist',p)
        p = c.rootPosition()
    if not p or not c.positionExists(p):
        if trace: g.trace('no root position')
        return
    try:
        h1 = self.yoffset(p)
        if self.center_selected_tree_node: # New in Leo 4.4.3.
            << compute frac0 >>
            delta = abs(self.prevMoveToFrac-frac0)
            # g.trace(delta)
            if delta > 0.0:
                self.prevMoveToFrac = frac0
                self.canvas.yview("moveto",frac0)
                if trace: g.trace("frac0 %1.2f %3d %3d %3d" % (frac0,h1,htot,wtot))
        else:
            last = c.lastVisible()
            nextToLast = last.visBack(c)
            h2 = self.yoffset(last)
            << compute approximate line height >>
            << Compute the fractions to scroll down/up >>
            if frac <= lo: # frac is for scrolling down.
                if self.prevMoveToFrac != frac:
                    self.prevMoveToFrac = frac
                    self.canvas.yview("moveto",frac)
                    if trace: g.trace("frac  %1.2f %3d %3d %1.2f %1.2f" % (frac, h1,h2,lo,hi))
            elif frac2 + (hi - lo) >= hi: # frac2 is for scrolling up.
                if self.prevMoveToFrac != frac2:
                    self.prevMoveToFrac = frac2
                    self.canvas.yview("moveto",frac2)
                    if trace: g.trace("frac2 1.2f %3d %3d %1.2f %1.2f" % (frac2,h1,h2,lo,hi))

        if self.allocateOnlyVisibleNodes:
            pass ### self.canvas.after_idle(self.idle_second_redraw)

        c.setTopVnode(p) # 1/30/04: remember a pseudo "top" node.

    except:
        g.es_exception()

idle_scrollTo = scrollTo # For compatibility.
#@nonl
#@+node:ekr.20071001092453.62:<< compute frac0 >>
# frac0 attempt to put the 
scrollRegion = self.canvas.cget('scrollregion')
geom = self.canvas.winfo_geometry()

if scrollRegion and geom:
    scrollRegion = scrollRegion.split(' ')
    # g.trace('scrollRegion',repr(scrollRegion))
    htot = int(scrollRegion[3])
    wh,junk,junk = geom.split('+')
    junk,h = wh.split('x')
    if h: wtot = int(h)
    else: wtot = 500
    # g.trace('geom',geom,'wtot',wtot)
    if htot > 0.1:
        frac0 = float(h1-wtot/2)/float(htot)
        frac0 = max(min(frac0,1.0),0.0)
    else:
        frac0 = 0.0
else:
    frac0 = 0.0 ; htot = wtot = 0
#@-node:ekr.20071001092453.62:<< compute frac0 >>
#@+node:ekr.20071001092453.63:<< compute approximate line height >>
if nextToLast: # 2/2/03: compute approximate line height.
    lineHeight = h2 - self.yoffset(nextToLast)
else:
    lineHeight = 20 # A reasonable default.
#@-node:ekr.20071001092453.63:<< compute approximate line height >>
#@+node:ekr.20071001092453.64:<< Compute the fractions to scroll down/up >>
data = frame.canvas.leo_treeBar.get() # Get the previous values of the scrollbar.
try: lo, hi = data
except: lo,hi = 0.0,1.0

# h1 and h2 are the y offsets of the present and last nodes.
if h2 > 0.1:
    frac = float(h1)/float(h2) # For scrolling down.
    frac2 = float(h1+lineHeight/2)/float(h2) # For scrolling up.
    frac2 = frac2 - (hi - lo)
else:
    frac = frac2 = 0.0 # probably any value would work here.

frac =  max(min(frac,1.0),0.0)
frac2 = max(min(frac2,1.0),0.0)
#@nonl
#@-node:ekr.20071001092453.64:<< Compute the fractions to scroll down/up >>
#@-node:ekr.20071001092453.61:scrollTo (swingTree)
#@+node:ekr.20071001092453.65:yoffset (swingTree)
@ We can't just return icony because the tree hasn't been redrawn yet.
For the same reason we can't rely on any TK canvas methods here.
@c

def yoffset(self,p1):
    # if not p1.isVisible(): print "yoffset not visible:",p1
    if not p1: return 0
    if c.hoistStack:
        bunch = c.hoistStack[-1]
        root = bunch.p.copy()
    else:
        root = self.c.rootPosition()
    if root:
        h,flag = self.yoffsetTree(root,p1)
        # flag can be False during initialization.
        # if not flag: print "yoffset fails:",h,v1
        return h
    else:
        return 0

def yoffsetTree(self,p,p1):
    h = 0 ; trace = False
    if not self.c.positionExists(p):
        if trace: g.trace('does not exist',p.headString())
        return h,False # An extra precaution.
    p = p.copy()
    for p2 in p.self_and_siblings_iter():  # was p.siblings_iter
        print "yoffsetTree:", p2
        if p2 == p1:
            if trace: g.trace(p.headString(),p1.headString(),h)
            return h, True
        h += self.line_height
        if p2.isExpanded() and p2.hasChildren():
            child = p2.firstChild()
            h2, flag = self.yoffsetTree(child,p1)
            h += h2
            if flag:
                if trace: g.trace(p.headString(),p1.headString(),h)
                return h, True

    if trace: g.trace('not found',p.headString(),p1.headString())
    return h, False
#@-node:ekr.20071001092453.65:yoffset (swingTree)
#@-node:ekr.20071001092453.57:Helpers...
#@-node:ekr.20071001092453.35:Drawing... (swingTree)
#@+node:ekr.20071001092453.66:Event handlers (swingTree)
#@+node:ekr.20071001092453.67:Helpers
#@+node:ekr.20071001092453.68:checkWidgetList
def checkWidgetList (self,tag):

    return True # This will fail when the headline actually changes!

    for w in self.visibleText:

        p = w.leo_position
        if p:
            s = w.getAllText().strip()
            h = p.headString().strip()

            if h != s:
                self.dumpWidgetList(tag)
                return False
        else:
            self.dumpWidgetList(tag)
            return False

    return True
#@-node:ekr.20071001092453.68:checkWidgetList
#@+node:ekr.20071001092453.69:dumpWidgetList
def dumpWidgetList (self,tag):

    print
    print "checkWidgetList: %s" % tag

    for w in self.visibleText:

        p = w.leo_position
        if p:
            s = w.getAllText().strip()
            h = p.headString().strip()

            addr = self.textAddr(w)
            print "p:",addr,h
            if h != s:
                print "w:",'*' * len(addr),s
        else:
            print "w.leo_position == None",w
#@-node:ekr.20071001092453.69:dumpWidgetList
#@+node:ekr.20071001092453.70:tree.edit_widget
def edit_widget (self,p):

    """Returns the Tk.Edit widget for position p."""

    return self.findEditWidget(p)
#@nonl
#@-node:ekr.20071001092453.70:tree.edit_widget
#@+node:ekr.20071001092453.71:eventToPosition
def eventToPosition (self,event):

    canvas = self.canvas
    x,y = event.x,event.y
    x = canvas.canvasx(x) 
    y = canvas.canvasy(y)
    if self.trace: g.trace(x,y)
    item = canvas.find_overlapping(x,y,x,y)
    if not item: return None

    # Item may be a tuple, possibly empty.
    try:    theId = item[0]
    except: theId = item
    if not theId: return None

    p = self.ids.get(theId)

    # A kludge: p will be None for vertical lines.
    if not p:
        item = canvas.find_overlapping(x+1,y,x+1,y)
        try:    theId = item[0]
        except: theId = item
        if not theId:
            g.es_print('oops: eventToPosition failed')
            return None
        p = self.ids.get(theId)
        # g.trace("was vertical line",p)

    if self.trace and self.verbose:
        if p:
            w = self.findEditWidget(p)
            g.trace("%3d %3d %3d %d" % (theId,x,y,id(w)),p.headString())
        else:
            g.trace("%3d %3d %3d" % (theId,x,y),None)

    # defensive programming: this copy is not needed.
    if p: return p.copy() # Make _sure_ nobody changes this table!
    else: return None
#@-node:ekr.20071001092453.71:eventToPosition
#@+node:ekr.20071001092453.72:findEditWidget
def findEditWidget (self,p):

    """Return the Tk.Text item corresponding to p."""

    c = self.c

    if p and c:
        aTuple = self.visibleText.get(p.key())
        if aTuple:
            w,theId = aTuple
            # g.trace('%4d' % (theId),self.textAddr(w),p.headString())
            return w
        else:
            # g.trace('oops: not found',p)
            return None

    # g.trace(not found',p.headString())
    return None
#@-node:ekr.20071001092453.72:findEditWidget
#@+node:ekr.20071001092453.73:findVnodeWithIconId
def findPositionWithIconId (self,theId):

    # Due to an old bug, theId may be a tuple.
    try:
        data = self.iconIds.get(theId[0])
    except:
        data = self.iconIds.get(theId)

    if data:
        p,generation = data
        if generation==self.generation:
            if self.trace and self.verbose:
                g.trace(theId,p.headString())
            return p
        else:
            if self.trace and self.verbose:
                g.trace("*** wrong generation: %d ***" % theId)
            return None
    else:
        if self.trace and self.verbose: g.trace(theId,None)
        return None
#@-node:ekr.20071001092453.73:findVnodeWithIconId
#@-node:ekr.20071001092453.67:Helpers
#@+node:ekr.20071001092453.74:Click Box...
#@+node:ekr.20071001092453.75:onClickBoxClick
def onClickBoxClick (self,event,p=None):

    c = self.c ; p1 = c.currentPosition()

    if not p: p = self.eventToPosition(event)
    if not p: return

    c.setLog()

    c.beginUpdate()
    try:
        if p and not g.doHook("boxclick1",c=c,p=p,v=p,event=event):
            c.endEditing()
            if p == p1 or self.initialClickExpandsOrContractsNode:
                if p.isExpanded(): p.contract()
                else:              p.expand()
            self.select(p)
            if c.frame.findPanel:
                c.frame.findPanel.handleUserClick(p)
            if self.stayInTree:
                c.treeWantsFocus()
            else:
                c.bodyWantsFocus()
        g.doHook("boxclick2",c=c,p=p,v=p,event=event)
    finally:
        c.endUpdate()
#@-node:ekr.20071001092453.75:onClickBoxClick
#@-node:ekr.20071001092453.74:Click Box...
#@+node:ekr.20071001092453.76:Dragging (swingTree)
#@+node:ekr.20071001092453.77:endDrag
def endDrag (self,event):

    """The official helper of the onEndDrag event handler."""

    c = self.c ; p = self.drag_p
    c.setLog()
    canvas = self.canvas
    if not event: return

    c.beginUpdate()
    try:
        << set vdrag, childFlag >>
        if self.allow_clone_drags:
            if not self.look_for_control_drag_on_mouse_down:
                self.controlDrag = c.frame.controlKeyIsDown

        redrawFlag = vdrag and vdrag.v.t != p.v.t
        if redrawFlag: # Disallow drag to joined node.
            << drag p to vdrag >>
        elif self.trace and self.verbose:
            g.trace("Cancel drag")

        # Reset the old cursor by brute force.
        self.canvas['cursor'] = "arrow"
        self.dragging = False
        self.drag_p = None
    finally:
        # Must set self.drag_p = None first.
        c.endUpdate(redrawFlag)
        c.recolor_now() # Dragging can affect coloring.
#@+node:ekr.20071001092453.78:<< set vdrag, childFlag >>
x,y = event.x,event.y
canvas_x = canvas.canvasx(x)
canvas_y = canvas.canvasy(y)

theId = self.canvas.find_closest(canvas_x,canvas_y)
# theId = self.canvas.find_overlapping(canvas_x,canvas_y,canvas_x,canvas_y)

vdrag = self.findPositionWithIconId(theId)
childFlag = vdrag and vdrag.hasChildren() and vdrag.isExpanded()
#@-node:ekr.20071001092453.78:<< set vdrag, childFlag >>
#@+node:ekr.20071001092453.79:<< drag p to vdrag >>
# g.trace("*** end drag   ***",theId,x,y,p.headString(),vdrag.headString())

if self.controlDrag: # Clone p and move the clone.
    if childFlag:
        c.dragCloneToNthChildOf(p,vdrag,0)
    else:
        c.dragCloneAfter(p,vdrag)
else: # Just drag p.
    if childFlag:
        c.dragToNthChildOf(p,vdrag,0)
    else:
        c.dragAfter(p,vdrag)
#@-node:ekr.20071001092453.79:<< drag p to vdrag >>
#@-node:ekr.20071001092453.77:endDrag
#@+node:ekr.20071001092453.80:startDrag
# This precomputes numberOfVisibleNodes(), a significant optimization.
# We also indicate where findPositionWithIconId() should start looking for tree id's.

def startDrag (self,event,p=None):

    """The official helper of the onDrag event handler."""

    c = self.c ; canvas = self.canvas

    if not p:
        assert(not self.drag_p)
        x = canvas.canvasx(event.x)
        y = canvas.canvasy(event.y)
        theId = canvas.find_closest(x,y)
        # theId = canvas.find_overlapping(canvas_x,canvas_y,canvas_x,canvas_y)
        if theId is None: return
        try: theId = theId[0]
        except: pass
        p = self.ids.get(theId)
    if not p: return
    c.setLog()
    self.drag_p = p.copy() # defensive programming: not needed.
    self.dragging = True
    # g.trace("*** start drag ***",theId,self.drag_p.headString())
    # Only do this once: greatly speeds drags.
    self.savedNumberOfVisibleNodes = self.numberOfVisibleNodes()
    # g.trace('self.controlDrag',self.controlDrag)
    if self.allow_clone_drags:
        self.controlDrag = c.frame.controlKeyIsDown
        if self.look_for_control_drag_on_mouse_down:
            if self.enable_drag_messages:
                if self.controlDrag:
                    g.es("dragged node will be cloned")
                else:
                    g.es("dragged node will be moved")
    else: self.controlDrag = False
    self.canvas['cursor'] = "hand2" # "center_ptr"
#@-node:ekr.20071001092453.80:startDrag
#@+node:ekr.20071001092453.81:onContinueDrag
def onContinueDrag(self,event):

    p = self.drag_p
    if not p: return

    try:
        canvas = self.canvas ; frame = self.c.frame
        if event:
            x,y = event.x,event.y
        else:
            x,y = frame.top.winfo_pointerx(),frame.top.winfo_pointery()
            # Stop the scrolling if we go outside the entire window.
            if x == -1 or y == -1: return 
        if self.dragging: # This gets cleared by onEndDrag()
            << scroll the canvas as needed >>
    except:
        g.es_event_exception("continue drag")
#@+node:ekr.20071001092453.82:<< scroll the canvas as needed >>
# Scroll the screen up or down one line if the cursor (y) is outside the canvas.
h = canvas.winfo_height()

if y < 0 or y > h:
    lo, hi = frame.canvas.leo_treeBar.get()
    n = self.savedNumberOfVisibleNodes
    line_frac = 1.0 / float(n)
    frac = g.choose(y < 0, lo - line_frac, lo + line_frac)
    frac = min(frac,1.0)
    frac = max(frac,0.0)
    # g.es("lo,hi,frac:",lo,hi,frac)
    canvas.yview("moveto", frac)

    # Queue up another event to keep scrolling while the cursor is outside the canvas.
    lo, hi = frame.canvas.leo_treeBar.get()
    if (y < 0 and lo > 0.1) or (y > h and hi < 0.9):
        pass ### canvas.after_idle(self.onContinueDrag,None) # Don't propagate the event.
#@-node:ekr.20071001092453.82:<< scroll the canvas as needed >>
#@-node:ekr.20071001092453.81:onContinueDrag
#@+node:ekr.20071001092453.83:onDrag
def onDrag(self,event):

    c = self.c ; p = self.drag_p
    if not event: return

    c.setLog()

    if not self.dragging:
        if not g.doHook("drag1",c=c,p=p,v=p,event=event):
            self.startDrag(event)
        g.doHook("drag2",c=c,p=p,v=p,event=event)

    if not g.doHook("dragging1",c=c,p=p,v=p,event=event):
        self.onContinueDrag(event)
    g.doHook("dragging2",c=c,p=p,v=p,event=event)
#@-node:ekr.20071001092453.83:onDrag
#@+node:ekr.20071001092453.84:onEndDrag
def onEndDrag(self,event):

    """Tree end-of-drag handler called from vnode event handler."""

    c = self.c ; p = self.drag_p
    if not p: return

    c.setLog()

    if not g.doHook("enddrag1",c=c,p=p,v=p,event=event):
        self.endDrag(event)
    g.doHook("enddrag2",c=c,p=p,v=p,event=event)
#@-node:ekr.20071001092453.84:onEndDrag
#@-node:ekr.20071001092453.76:Dragging (swingTree)
#@+node:ekr.20071001092453.85:Icon Box...
#@+node:ekr.20071001092453.86:onIconBoxClick
def onIconBoxClick (self,event,p=None):

    c = self.c ; tree = self

    if not p: p = self.eventToPosition(event)
    if not p: return

    c.setLog()

    if self.trace and self.verbose: g.trace()

    if not g.doHook("iconclick1",c=c,p=p,v=p,event=event):
        if event:
            self.onDrag(event)
        tree.endEditLabel()
        tree.select(p,scroll=False)
        if c.frame.findPanel:
            c.frame.findPanel.handleUserClick(p)
    g.doHook("iconclick2",c=c,p=p,v=p,event=event)

    return "break" # disable expanded box handling.
#@-node:ekr.20071001092453.86:onIconBoxClick
#@+node:ekr.20071001092453.87:onIconBoxRightClick
def onIconBoxRightClick (self,event,p=None):

    """Handle a right click in any outline widget."""

    c = self.c

    if not p: p = self.eventToPosition(event)
    if not p: return

    c.setLog()

    try:
        if not g.doHook("iconrclick1",c=c,p=p,v=p,event=event):
            self.OnActivateHeadline(p)
            self.endEditLabel()
            self.OnPopup(p,event)
        g.doHook("iconrclick2",c=c,p=p,v=p,event=event)
    except:
        g.es_event_exception("iconrclick")

    return 'break'
#@-node:ekr.20071001092453.87:onIconBoxRightClick
#@+node:ekr.20071001092453.88:onIconBoxDoubleClick
def onIconBoxDoubleClick (self,event,p=None):

    c = self.c

    if not p: p = self.eventToPosition(event)
    if not p: return

    c.setLog()

    if self.trace and self.verbose: g.trace()

    try:
        if not g.doHook("icondclick1",c=c,p=p,v=p,event=event):
            self.endEditLabel() # Bug fix: 11/30/05
            self.OnIconDoubleClick(p) # Call the method in the base class.
        g.doHook("icondclick2",c=c,p=p,v=p,event=event)
    except:
        g.es_event_exception("icondclick")

    return 'break' # 11/19/06
#@-node:ekr.20071001092453.88:onIconBoxDoubleClick
#@-node:ekr.20071001092453.85:Icon Box...
#@+node:ekr.20071001092453.89:OnActivateHeadline (swingTree)
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.20071001092453.90:<< 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.treeWantsFocus()
    self.active = False
    returnVal = 'break'
#@nonl
#@-node:ekr.20071001092453.90:<< activate this window >>
#@-node:ekr.20071001092453.89:OnActivateHeadline (swingTree)
#@+node:ekr.20071001092453.91:Text Box...
#@+node:ekr.20071001092453.92:configureTextState
def configureTextState (self,p):

    c = self.c

    if not p: return

    # g.trace(p.headString(),self.c._currentPosition)

    if c.isCurrentPosition(p):
        if p == self.editPosition():
            self.setEditLabelState(p) # selected, editing.
        else:
            self.setSelectedLabelState(p) # selected, not editing.
    else:
        self.setUnselectedLabelState(p) # unselected
#@-node:ekr.20071001092453.92:configureTextState
#@+node:ekr.20071001092453.93:onCtontrolT
# This works around an apparent Tk bug.

def onControlT (self,event=None):

    # If we don't inhibit further processing the Tx.Text widget switches characters!
    return "break"
#@-node:ekr.20071001092453.93:onCtontrolT
#@+node:ekr.20071001092453.94: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.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.20071001092453.94:onHeadlineClick
#@+node:ekr.20071001092453.95:onHeadlineRightClick
def onHeadlineRightClick (self,event):

    """Handle a right click in any outline widget."""

    c = self.c ; w = event.widget

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

    c.setLog()

    try:
        if not g.doHook("headrclick1",c=c,p=p,v=p,event=event):
            self.OnActivateHeadline(p)
            self.endEditLabel()
            self.OnPopup(p,event)
        g.doHook("headrclick2",c=c,p=p,v=p,event=event)
    except:
        g.es_event_exception("headrclick")

    # 'continue' *is* correct here.
    # 'break' would make it impossible to unselect the headline text.
    return 'continue'
#@-node:ekr.20071001092453.95:onHeadlineRightClick
#@-node:ekr.20071001092453.91:Text Box...
#@+node:ekr.20071001092453.96:tree.OnDeactivate
def OnDeactivate (self,event=None):

    """Deactivate the tree pane, dimming any headline being edited."""

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

    tree = self ; c = self.c

    # g.trace(g.callers())

    c.beginUpdate()
    try:
        tree.endEditLabel()
        tree.dimEditLabel()
    finally:
        c.endUpdate(False)
#@-node:ekr.20071001092453.96:tree.OnDeactivate
#@+node:ekr.20071001092453.97:tree.OnPopup & allies
def OnPopup (self,p,event):

    """Handle right-clicks in the outline.

    This is *not* an event handler: it is called from other event handlers."""

    # Note: "headrclick" hooks handled by vnode callback routine.

    if event != None:
        c = self.c
        c.setLog()

        if not g.doHook("create-popup-menu",c=c,p=p,v=p,event=event):
            self.createPopupMenu(event)
        if not g.doHook("enable-popup-menu-items",c=c,p=p,v=p,event=event):
            self.enablePopupMenuItems(p,event)
        if not g.doHook("show-popup-menu",c=c,p=p,v=p,event=event):
            self.showPopupMenu(event)

    return "break"
#@+node:ekr.20071001092453.98:OnPopupFocusLost
@ On Linux we must do something special to make the popup menu "unpost" if the mouse is clicked elsewhere.  So we have to catch the <FocusOut> event and explicitly unpost.  In order to process the <FocusOut> event, we need to be able to find the reference to the popup window again, so this needs to be an attribute of the tree object; hence, "self.popupMenu".

Aside: though Tk tries to be muli-platform, the interaction with different window managers does cause small differences that will need to be compensated by system specific application code. :-(
@c

# 20-SEP-2002 DTHEIN: This event handler is only needed for Linux.

def OnPopupFocusLost(self,event=None):

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

    self.popupMenu.unpost()
#@-node:ekr.20071001092453.98:OnPopupFocusLost
#@+node:ekr.20071001092453.99:createPopupMenu
def createPopupMenu (self,event):

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

    c = self.c ; frame = c.frame

    # If we are going to recreate it, we had better destroy it.
    if self.popupMenu:
        self.popupMenu.destroy()
        self.popupMenu = None

    self.popupMenu = menu = Tk.Menu(g.app.root, tearoff=0)

    # Add the Open With entries if they exist.
    if g.app.openWithTable:
        frame.menu.createOpenWithMenuItemsFromTable(menu,g.app.openWithTable)
        table = (("-",None,None),)
        frame.menu.createMenuEntries(menu,table)

    << Create the menu table >>

    # New in 4.4.  There is no need for a dontBind argument because
    # Bindings from tables are ignored.
    frame.menu.createMenuEntries(menu,table)
#@+node:ekr.20071001092453.100:<< Create the menu table >>
table = (
    ("&Read @file Nodes",c.readAtFileNodes),
    ("&Write @file Nodes",c.fileCommands.writeAtFileNodes),
    ("-",None),
    ("&Tangle",c.tangle),
    ("&Untangle",c.untangle),
    ("-",None),
    ("Toggle Angle &Brackets",c.toggleAngleBrackets),
    ("-",None),
    ("Cut Node",c.cutOutline),
    ("Copy Node",c.copyOutline),
    ("&Paste Node",c.pasteOutline),
    ("&Delete Node",c.deleteOutline),
    ("-",None),
    ("&Insert Node",c.insertHeadline),
    ("&Clone Node",c.clone),
    ("Sort C&hildren",c.sortChildren),
    ("&Sort Siblings",c.sortSiblings),
    ("-",None),
    ("Contract Parent",c.contractParent),
)
#@-node:ekr.20071001092453.100:<< Create the menu table >>
#@-node:ekr.20071001092453.99:createPopupMenu
#@+node:ekr.20071001092453.101:enablePopupMenuItems
def enablePopupMenuItems (self,v,event):

    """Enable and disable items in the popup menu."""

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

    c = self.c ; menu = self.popupMenu

    << set isAtRoot and isAtFile if v's tree contains @root or @file nodes >>
    isAtFile = g.choose(isAtFile,1,0)
    isAtRoot = g.choose(isAtRoot,1,0)
    canContract = v.parent() != None
    canContract = g.choose(canContract,1,0)

    enable = self.frame.menu.enableMenu

    for name in ("Read @file Nodes", "Write @file Nodes"):
        enable(menu,name,isAtFile)
    for name in ("Tangle", "Untangle"):
        enable(menu,name,isAtRoot)

    enable(menu,"Cut Node",c.canCutOutline())
    enable(menu,"Delete Node",c.canDeleteHeadline())
    enable(menu,"Paste Node",c.canPasteOutline())
    enable(menu,"Sort Children",c.canSortChildren())
    enable(menu,"Sort Siblings",c.canSortSiblings())
    enable(menu,"Contract Parent",c.canContractParent())
#@+node:ekr.20071001092453.102:<< set isAtRoot and isAtFile if v's tree contains @root or @file nodes >>
isAtFile = False
isAtRoot = False

for v2 in v.self_and_subtree_iter():
    if isAtFile and isAtRoot:
        break
    if (v2.isAtFileNode() or
        v2.isAtNorefFileNode() or
        v2.isAtAsisFileNode() or
        v2.isAtNoSentFileNode()
    ):
        isAtFile = True

    isRoot,junk = g.is_special(v2.bodyString(),0,"@root")
    if isRoot:
        isAtRoot = True
#@-node:ekr.20071001092453.102:<< set isAtRoot and isAtFile if v's tree contains @root or @file nodes >>
#@-node:ekr.20071001092453.101:enablePopupMenuItems
#@+node:ekr.20071001092453.103:showPopupMenu
def showPopupMenu (self,event):

    """Show a popup menu."""

    c = self.c ; menu = self.popupMenu

    ###

    # if sys.platform == "linux2": # 20-SEP-2002 DTHEIN: not needed for Windows
        # menu.bind("<FocusOut>",self.OnPopupFocusLost)

    # menu.post(event.x_root, event.y_root)

    # # Set the focus immediately so we know when we lose it.
    # c.widgetWantsFocus(menu)
#@-node:ekr.20071001092453.103:showPopupMenu
#@-node:ekr.20071001092453.97:tree.OnPopup & allies
#@+node:ekr.20071001092453.104: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()

    return 'break'
#@-node:ekr.20071001092453.104:onTreeClick
#@-node:ekr.20071001092453.66:Event handlers (swingTree)
#@+node:ekr.20071001092453.105:Incremental drawing...
#@+node:ekr.20071001092453.106:allocateNodes
def allocateNodes(self,where,lines):

    """Allocate Tk widgets in nodes that will become visible as the result of an upcoming scroll"""

    assert(where in ("above","below"))

    # print "allocateNodes: %d lines %s visible area" % (lines,where)

    # Expand the visible area: a little extra delta is safer.
    delta = lines * (self.line_height + 4)
    y1,y2 = self.visibleArea

    if where == "below":
        y2 += delta
    else:
        y1 = max(0.0,y1-delta)

    self.expandedVisibleArea=y1,y2
    # print "expandedArea:   %5.1f %5.1f" % (y1,y2)

    # Allocate all nodes in expanded visible area.
    self.updatedNodeCount = 0
    self.updateTree(self.c.rootPosition(),self.root_left,self.root_top,0,0)
    # if self.updatedNodeCount: print "updatedNodeCount:", self.updatedNodeCount
#@-node:ekr.20071001092453.106:allocateNodes
#@+node:ekr.20071001092453.107:allocateNodesBeforeScrolling
def allocateNodesBeforeScrolling (self, args):

    """Calculate the nodes that will become visible as the result of an upcoming scroll.

    args is the tuple passed to the Tk.Canvas.yview method"""

    if not self.allocateOnlyVisibleNodes: return

    # print "allocateNodesBeforeScrolling:",self.redrawCount,args

    assert(self.visibleArea)
    assert(len(args)==2 or len(args)==3)
    kind = args[0] ; n = args[1]
    lines = 2 # Update by 2 lines to account for rounding.
    if len(args) == 2:
        assert(kind=="moveto")
        frac1,frac2 = args
        if float(n) != frac1:
            where = g.choose(n<frac1,"above","below")
            self.allocateNodes(where=where,lines=lines)
    else:
        assert(kind=="scroll")
        linesPerPage = self.canvas.winfo_height()/self.line_height + 2
        n = int(n) ; assert(abs(n)==1)
        where = g.choose(n == 1,"below","above")
        lines = g.choose(args[2] == "pages",linesPerPage,lines)
        self.allocateNodes(where=where,lines=lines)
#@-node:ekr.20071001092453.107:allocateNodesBeforeScrolling
#@+node:ekr.20071001092453.108:updateNode
def updateNode (self,p,x,y):

    """Draw a node that may have become visible as a result of a scrolling operation"""

    c = self.c

    if self.inExpandedVisibleArea(y):
        # This check is a major optimization.
        if not c.edit_widget(p):
            return self.force_draw_node(p,x,y)
        else:
            return self.line_height

    return self.line_height
#@-node:ekr.20071001092453.108:updateNode
#@+node:ekr.20071001092453.109:setVisibleAreaToFullCanvas
def setVisibleAreaToFullCanvas(self):

    if self.visibleArea:
        y1,y2 = self.visibleArea
        y2 = max(y2,y1 + self.canvas.winfo_height())
        self.visibleArea = y1,y2
#@-node:ekr.20071001092453.109:setVisibleAreaToFullCanvas
#@+node:ekr.20071001092453.110:setVisibleArea
def setVisibleArea (self,args):

    r1,r2 = args
    r1,r2 = float(r1),float(r2)
    # print "scroll ratios:",r1,r2

    try:
        s = self.canvas.cget("scrollregion")
        x1,y1,x2,y2 = g.scanf(s,"%d %d %d %d")
        x1,y1,x2,y2 = int(x1),int(y1),int(x2),int(y2)
    except:
        self.visibleArea = None
        return

    scroll_h = y2-y1
    # print "height of scrollregion:", scroll_h

    vy1 = y1 + (scroll_h*r1)
    vy2 = y1 + (scroll_h*r2)
    self.visibleArea = vy1,vy2
    # print "setVisibleArea: %5.1f %5.1f" % (vy1,vy2)
#@-node:ekr.20071001092453.110:setVisibleArea
#@+node:ekr.20071001092453.111:tree.updateTree
def updateTree (self,v,x,y,h,level):

    yfirst = y
    if level==0: yfirst += 10
    while v:
        # g.trace(x,y,v)
        h,indent = self.updateNode(v,x,y)
        y += h
        if v.isExpanded() and v.firstChild():
            y = self.updateTree(v.firstChild(),x+indent,y,h,level+1)
        v = v.next()
    return y
#@-node:ekr.20071001092453.111:tree.updateTree
#@-node:ekr.20071001092453.105:Incremental drawing...
#@+node:ekr.20071001092453.112:Selecting & editing... (swingTree)
#@+node:ekr.20071001092453.113:dimEditLabel, undimEditLabel
# Convenience methods so the caller doesn't have to know the present edit node.

def dimEditLabel (self):

    p = self.c.currentPosition()
    self.setSelectedLabelState(p)

def undimEditLabel (self):

    p = self.c.currentPosition()
    self.setSelectedLabelState(p)
#@-node:ekr.20071001092453.113:dimEditLabel, undimEditLabel
#@+node:ekr.20071001092453.114:tree.editLabel
def editLabel (self,p,selectAll=False):

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

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

    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)

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

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

    if p and c.edit_widget(p):
        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.
#@-node:ekr.20071001092453.114:tree.editLabel
#@+node:ekr.20071001092453.115:tree.set...LabelState
#@+node:ekr.20071001092453.116:setEditLabelState
def setEditLabelState (self,p,selectAll=False): # selected, editing

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

    if p and w:
        # g.trace('*****',g.callers())
        c.widgetWantsFocusNow(w)
        self.setEditHeadlineColors(p)
        selectAll = selectAll or self.select_all_text_when_editing_headlines
        if selectAll:
            w.setSelectionRange(0,'end',insert='end')
        else:
            w.setInsertPoint('end') # Clears insert point.
    else:
        g.trace('no edit_widget')

setNormalLabelState = setEditLabelState # For compatibility.
#@-node:ekr.20071001092453.116:setEditLabelState
#@+node:ekr.20071001092453.117:setSelectedLabelState
def setSelectedLabelState (self,p): # selected, disabled

    # g.trace(p.headString(),g.callers())

    c = self.c

    if p and c.edit_widget(p):
        self.setDisabledHeadlineColors(p)
#@-node:ekr.20071001092453.117:setSelectedLabelState
#@+node:ekr.20071001092453.118:setUnselectedLabelState
def setUnselectedLabelState (self,p): # not selected.

    c = self.c

    if p and c.edit_widget(p):
        self.setUnselectedHeadlineColors(p)
#@-node:ekr.20071001092453.118:setUnselectedLabelState
#@+node:ekr.20071001092453.119:setDisabledHeadlineColors
def setDisabledHeadlineColors (self,p):

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

    if 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.20071001092453.119:setDisabledHeadlineColors
#@+node:ekr.20071001092453.120:setEditHeadlineColors
def setEditHeadlineColors (self,p):

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

    if self.trace and self.verbose:
        if not self.redrawing:
            print "%10s %d %s" % ("edit",id(2),p.headString())

    fg    = self.headline_text_editing_foreground_color or 'black'
    bg    = self.headline_text_editing_background_color or 'white'
    selfg = self.headline_text_editing_selection_foreground_color or 'white'
    selbg = self.headline_text_editing_selection_background_color or 'black'

    try: # Use system defaults for selection foreground/background
        w.configure(state="normal",highlightthickness=1,
        fg=fg,bg=bg,selectforeground=selfg,selectbackground=selbg)
    except:
        g.es_exception()
#@-node:ekr.20071001092453.120:setEditHeadlineColors
#@+node:ekr.20071001092453.121:setUnselectedHeadlineColors
def setUnselectedHeadlineColors (self,p):

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

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

    fg = self.headline_text_unselected_foreground_color or 'black'
    bg = self.headline_text_unselected_background_color or 'white'

    try:
        w.configure(state="disabled",highlightthickness=0,fg=fg,bg=bg,
            selectbackground=bg,selectforeground=fg,highlightbackground=bg)
    except:
        g.es_exception()
#@-node:ekr.20071001092453.121:setUnselectedHeadlineColors
#@-node:ekr.20071001092453.115:tree.set...LabelState
#@+node:ekr.20071001092453.122:tree.setHeadline (swingTree)
def setHeadline (self,p,s):

    '''Set the actual text of the headline widget.

    This is called from the undo/redo logic to change the text before redrawing.'''

    w = self.edit_widget(p)
    if w:
        w.configure(state='normal')
        w.delete(0,'end')
        if s.endswith('\n') or s.endswith('\r'):
            s = s[:-1]
        w.insert(0,s)
        self.revertHeadline = s
        # g.trace(repr(s),w.getAllText())
    else:
        g.trace('-'*20,'oops')
#@-node:ekr.20071001092453.122:tree.setHeadline (swingTree)
#@-node:ekr.20071001092453.112:Selecting & editing... (swingTree)
#@-node:ekr.20071001092453:class leoSwingTree (REWRITE)
#@-node:ekr.20070930105125:@thin leoSwingFrame.py
#@+node:ekr.20070930102053.1:@thin leoSwingGui.py
@first # -*- coding: utf-8 -*-

'''Leo's Swing Gui module.'''

@language python
@tabwidth -4
@pagewidth 80

<< imports >>

class swingGui(leoGui.leoGui):

    """A class encapulating all calls to swing."""

    @others
#@+node:ekr.20070930102228.3:<< imports >>
import leoGlobals as g
import leoGui

import os
import string
import sys

import leoFrame
import leoSwingFrame

# import leoTkinterComparePanel
# import leoTkinterDialog
# import leoTkinterFind
# import tkFont
# import tkFileDialog
#@-node:ekr.20070930102228.3:<< imports >>
#@+node:ekr.20070930102228.4:swingGui birth & death
#@+node:ekr.20070930102228.5: swingGui.__init__
def __init__ (self):

    # Initialize the base class.
    leoGui.leoGui.__init__(self,'swing')

    self.root = None

    self.bodyTextWidget  = leoSwingFrame.leoSwingTextWidget
    self.plainTextWidget = leoSwingFrame.leoSwingTextWidget

    self.bitmap_name = None
    self.bitmap = None

    self.defaultFont = None
    self.defaultFontFamily = None

    # self.win32clipboard = None 
#@nonl
#@-node:ekr.20070930102228.5: swingGui.__init__
#@+node:ekr.20070930102228.6:createKeyHandlerClass (swingGui)
def createKeyHandlerClass (self,c,useGlobalKillbuffer=True,useGlobalRegisters=True):

    import leoSwingFrame # Do this here to break any circular dependency.

    return leoSwingFrame.swingKeyHandlerClass(c,useGlobalKillbuffer,useGlobalRegisters)
#@-node:ekr.20070930102228.6:createKeyHandlerClass (swingGui)
#@+node:ekr.20070930102228.14:runMainLoop (swingGui)
def runMainLoop(self):

    '''Start the swing main loop.'''

    if self.script:
        log = g.app.log
        if log:
            print 'Start of batch script...\n'
            log.c.executeScript(script=self.script)
            print 'End of batch script'
        else:
            print 'no log, no commander for executeScript in swingGui.runMainLoop'
    else:
        pass # no need to invoke a swing main loop.
#@-node:ekr.20070930102228.14:runMainLoop (swingGui)
#@+node:ekr.20070930125059:Not used
def createRootWindow(self):
    pass

def destroySelf (self):
    pass

def killGui(self,exitFlag=True):
    """Destroy a gui and terminate Leo if exitFlag is True."""
    pass

def recreateRootWindow(self):
    """A do-nothing base class to create the hidden root window of a gui
    after a previous gui has terminated with killGui(False)."""
    pass

if 0:
    @others
#@+node:ekr.20070930102228.9:swingGui.setDefaultIcon
def setDefaultIcon(self):

    """Set the icon to be used in all Leo windows.

    This code does nothing for Tk versions before 8.4.3."""

    gui = self

    try:
        version = gui.root.getvar("tk_patchLevel")
        # g.trace(repr(version),g.CheckVersion(version,"8.4.3"))
        if g.CheckVersion(version,"8.4.3") and sys.platform == "win32":

            # tk 8.4.3 or greater: load a 16 by 16 icon.
            path = g.os_path_join(g.app.loadDir,"..","Icons")
            if g.os_path_exists(path):
                theFile = g.os_path_join(path,"LeoApp16.ico")
                if g.os_path_exists(path):
                    self.bitmap = Tk.BitmapImage(theFile)
                else:
                    g.es("LeoApp16.ico not in Icons directory", color="red")
            else:
                g.es("Icons directory not found: "+path, color="red")
    except:
        print "exception setting bitmap"
        import traceback ; traceback.print_exc()
#@-node:ekr.20070930102228.9:swingGui.setDefaultIcon
#@+node:ekr.20070930102228.10:swingGui.getDefaultConfigFont
def getDefaultConfigFont(self,config):

    """Get the default font from a new text widget."""

    if not self.defaultFontFamily:
        # WARNING: retain NO references to widgets or fonts here!
        w = g.app.gui.plainTextWidget()
        fn = w.cget("font")
        font = swingFont.Font(font=fn) 
        family = font.cget("family")
        self.defaultFontFamily = family[:]
        # print '***** getDefaultConfigFont',repr(family)

    config.defaultFont = None
    config.defaultFontFamily = self.defaultFontFamily
#@-node:ekr.20070930102228.10:swingGui.getDefaultConfigFont
#@-node:ekr.20070930125059:Not used
#@-node:ekr.20070930102228.4:swingGui birth & death
#@+node:ekr.20070930102228.15:swingGui dialogs & panels
def runAboutLeoDialog(self,c,version,theCopyright,url,email):
    """Create and run a swing About Leo dialog."""
    d = leoSwingDialog.swingAboutLeo(c,version,theCopyright,url,email)
    return d.run(modal=False)

def runAskLeoIDDialog(self):
    """Create and run a dialog to get g.app.LeoID."""
    d = leoSwingDialog.swingAskLeoID()
    return d.run(modal=True)

def runAskOkDialog(self,c,title,message=None,text="Ok"):
    """Create and run a swing an askOK dialog ."""
    d = leoSwingDialog.swingAskOk(c,title,message,text)
    return d.run(modal=True)

def runAskOkCancelNumberDialog(self,c,title,message):
    """Create and run askOkCancelNumber dialog ."""
    d = leoSwingDialog.swingAskOkCancelNumber(c,title,message)
    return d.run(modal=True)

def runAskOkCancelStringDialog(self,c,title,message):
    """Create and run askOkCancelString dialog ."""
    d = leoSwingDialog.swingAskOkCancelString(c,title,message)
    return d.run(modal=True)

def runAskYesNoDialog(self,c,title,message=None):
    """Create and run an askYesNo dialog."""
    d = leoSwingDialog.swingAskYesNo(c,title,message)
    return d.run(modal=True)

def runAskYesNoCancelDialog(self,c,title,
    message=None,yesMessage="Yes",noMessage="No",defaultButton="Yes"):
    """Create and run an askYesNoCancel dialog ."""
    d = leoSwingDialog.swingAskYesNoCancel(
        c,title,message,yesMessage,noMessage,defaultButton)
    return d.run(modal=True)

# The compare panel has no run dialog.

# def runCompareDialog(self,c):
    # """Create and run an askYesNo dialog."""
    # if not g.app.unitTesting:
        # leoSwingCompareDialog(c)
#@+node:ekr.20070930102228.16:swingGui.createSpellTab
def createSpellTab(self,c,spellHandler,tabName):

    ### return leoSwingFind.swingSpellTab(c,spellHandler,tabName)

    pass
#@-node:ekr.20070930102228.16:swingGui.createSpellTab
#@+node:ekr.20070930102228.17:swingGui file dialogs
# We no longer specify default extensions so that we can open and save files without extensions.
#@+node:ekr.20070930102228.18:runOpenFileDialog
def runOpenFileDialog(self,title,filetypes,defaultextension,multiple=False):

    """Create and run an swing open file dialog ."""

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

    initialdir = g.app.globalOpenDir or g.os_path_abspath(os.getcwd())

    if multiple:
        # askopenfilenames requires Python 2.3 and Tk 8.4.
        version = '.'.join([str(sys.version_info[i]) for i in (0,1,2)])
        if (
            g.CheckVersion(version,"2.3") and
            g.CheckVersion(self.root.getvar("tk_patchLevel"),"8.4")
        ):
            files = swingFileDialog.askopenfilenames(
                title=title,filetypes=filetypes,initialdir=initialdir)
            # g.trace(files)
            return list(files)
        else:
            # Get one file and return it as a list.
            theFile = swingFileDialog.askopenfilename(
                title=title,filetypes=filetypes,initialdir=initialdir)
            return [theFile]
    else:
        # Return a single file name as a string.
        return swingFileDialog.askopenfilename(
            title=title,filetypes=filetypes,initialdir=initialdir)
#@-node:ekr.20070930102228.18:runOpenFileDialog
#@+node:ekr.20070930102228.19:runSaveFileDialog
def runSaveFileDialog(self,initialfile,title,filetypes,defaultextension):

    """Create and run an swing save file dialog ."""

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

    initialdir=g.app.globalOpenDir or g.os_path_abspath(os.getcwd()),

    return swingFileDialog.asksaveasfilename(
        initialdir=initialdir,initialfile=initialfile,
        title=title,filetypes=filetypes)
#@-node:ekr.20070930102228.19:runSaveFileDialog
#@-node:ekr.20070930102228.17:swingGui file dialogs
#@+node:ekr.20070930102228.20:swingGui panels
def createComparePanel(self,c):
    """Create a swing color picker panel."""
    ### return leoSwingComparePanel.leoSwingComparePanel(c)

def createFindPanel(self,c):
    """Create a hidden swing find panel."""
    ### 
    # panel = leoSwingFind.leoSwingFind(c)
    # panel.top.withdraw()
    # return panel

def createFindTab (self,c,parentFrame):
    """Create a swing find tab in the indicated frame."""
    ### return leoSwingFind.swingFindTab(c,parentFrame)

def createLeoFrame(self,title):
    """Create a new Leo frame."""
    gui = self
    return leoSwingFrame.leoSwingFrame(title,gui)
#@-node:ekr.20070930102228.20:swingGui panels
#@-node:ekr.20070930102228.15:swingGui dialogs & panels
#@+node:ekr.20070930102228.21:swingGui utils (TO DO)
#@+node:ekr.20070930102228.22:Clipboard (swingGui)
#@+node:ekr.20070930102228.23:replaceClipboardWith
def replaceClipboardWith (self,s):

    # g.app.gui.win32clipboard is always None.
    wcb = g.app.gui.win32clipboard

    if wcb:
        try:
            wcb.OpenClipboard(0)
            wcb.EmptyClipboard()
            wcb.SetClipboardText(s)
            wcb.CloseClipboard()
        except:
            g.es_exception()
    else:
        self.root.clipboard_clear()
        self.root.clipboard_append(s)
#@-node:ekr.20070930102228.23:replaceClipboardWith
#@+node:ekr.20070930102228.24:getTextFromClipboard
def getTextFromClipboard (self):

    # g.app.gui.win32clipboard is always None.
    wcb = g.app.gui.win32clipboard

    if wcb:
        try:
            wcb.OpenClipboard(0)
            data = wcb.GetClipboardData()
            wcb.CloseClipboard()
            # g.trace(data)
            return data
        except TypeError:
            # g.trace(None)
            return None
        except:
            g.es_exception()
            return None
    else:
        try:
            s = self.root.selection_get(selection="CLIPBOARD")
            return s
        except:
            return None
#@-node:ekr.20070930102228.24:getTextFromClipboard
#@-node:ekr.20070930102228.22:Clipboard (swingGui)
#@+node:ekr.20070930102228.25:color
# g.es calls gui.color to do the translation,
# so most code in Leo's core can simply use Tk color names.

def color (self,color):
    '''Return the gui-specific color corresponding to the Tk color name.'''
    return color

#@-node:ekr.20070930102228.25:color
#@+node:ekr.20070930102228.26:Dialog
#@+node:ekr.20070930102228.27:get_window_info
# WARNING: Call this routine _after_ creating a dialog.
# (This routine inhibits the grid and pack geometry managers.)

def get_window_info (self,top):

    top.update_idletasks() # Required to get proper info.

    # Get the information about top and the screen.
    geom = top.geometry() # geom = "WidthxHeight+XOffset+YOffset"
    dim,x,y = string.split(geom,'+')
    w,h = string.split(dim,'x')
    w,h,x,y = int(w),int(h),int(x),int(y)

    return w,h,x,y
#@-node:ekr.20070930102228.27:get_window_info
#@+node:ekr.20070930102228.28:center_dialog
def center_dialog(self,top):

    """Center the dialog on the screen.

    WARNING: Call this routine _after_ creating a dialog.
    (This routine inhibits the grid and pack geometry managers.)"""

    sw = top.winfo_screenwidth()
    sh = top.winfo_screenheight()
    w,h,x,y = self.get_window_info(top)

    # Set the new window coordinates, leaving w and h unchanged.
    x = (sw - w)/2
    y = (sh - h)/2
    top.geometry("%dx%d%+d%+d" % (w,h,x,y))

    return w,h,x,y
#@-node:ekr.20070930102228.28:center_dialog
#@+node:ekr.20070930102228.29:create_labeled_frame
# Returns frames w and f.
# Typically the caller would pack w into other frames, and pack content into f.

def create_labeled_frame (self,parent,
    caption=None,relief="groove",bd=2,padx=0,pady=0):

    # Create w, the master frame.
    w = Tk.Frame(parent)
    w.grid(sticky="news")

    # Configure w as a grid with 5 rows and columns.
    # The middle of this grid will contain f, the expandable content area.
    w.columnconfigure(1,minsize=bd)
    w.columnconfigure(2,minsize=padx)
    w.columnconfigure(3,weight=1)
    w.columnconfigure(4,minsize=padx)
    w.columnconfigure(5,minsize=bd)

    w.rowconfigure(1,minsize=bd)
    w.rowconfigure(2,minsize=pady)
    w.rowconfigure(3,weight=1)
    w.rowconfigure(4,minsize=pady)
    w.rowconfigure(5,minsize=bd)

    # Create the border spanning all rows and columns.
    border = Tk.Frame(w,bd=bd,relief=relief) # padx=padx,pady=pady)
    border.grid(row=1,column=1,rowspan=5,columnspan=5,sticky="news")

    # Create the content frame, f, in the center of the grid.
    f = Tk.Frame(w,bd=bd)
    f.grid(row=3,column=3,sticky="news")

    # Add the caption.
    if caption and len(caption) > 0:
        caption = Tk.Label(parent,text=caption,highlightthickness=0,bd=0)
        # caption.tkraise(w)
        caption.grid(in_=w,row=0,column=2,rowspan=2,columnspan=3,padx=4,sticky="w")

    return w,f
#@-node:ekr.20070930102228.29:create_labeled_frame
#@-node:ekr.20070930102228.26:Dialog
#@+node:ekr.20070930102228.30:Events (swingGui)
def event_generate(self,w,kind,*args,**keys):
    '''Generate an event.'''
    return w.event_generate(kind,*args,**keys)

def eventChar (self,event,c=None):
    '''Return the char field of an event.'''
    return event and event.char or ''

def eventKeysym (self,event,c=None):
    '''Return the keysym value of an event.'''
    return event and event.keysym

def eventWidget (self,event,c=None):
    '''Return the widget field of an event.'''   
    return event and event.widget

def eventXY (self,event,c=None):
    if event:
        return event.x,event.y
    else:
        return 0,0
#@nonl
#@-node:ekr.20070930102228.30:Events (swingGui)
#@+node:ekr.20070930102228.31:Focus
#@+node:ekr.20070930102228.32:swingGui.get_focus
def get_focus(self,c):

    """Returns the widget that has focus, or body if None."""

    return c.frame.top.focus_displayof()
#@-node:ekr.20070930102228.32:swingGui.get_focus
#@+node:ekr.20070930102228.33:swing.Gui.set_focus
set_focus_count = 0

def set_focus(self,c,w):

    # __pychecker__ = '--no-argsused' # c not used at present.

    """Put the focus on the widget."""

    if not g.app.unitTesting and c and c.config.getBool('trace_g.app.gui.set_focus'):
        self.set_focus_count += 1
        # Do not call trace here: that might affect focus!
        print 'gui.set_focus: %4d %10s %s' % (
            self.set_focus_count,c and c.shortFileName(),
            c and c.widget_name(w)), g.callers(5)

    if w:
        try:
            if 0: # No longer needed.
                # A call to findTab.bringToFront caused
                # the focus problems with Pmw.Notebook.
                w.update()

            # It's possible that the widget doesn't exist now.
            w.focus_set()
            return True
        except Exception:
            # g.es_exception()
            return False
#@-node:ekr.20070930102228.33:swing.Gui.set_focus
#@-node:ekr.20070930102228.31:Focus
#@+node:ekr.20070930102228.34:Font
#@+node:ekr.20070930102228.35:swingGui.getFontFromParams
def getFontFromParams(self,family,size,slant,weight,defaultSize=12):

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

    family_name = family

    try:
        font = swingFont.Font(family=family,size=size,slant=slant,weight=weight)
        # if g.app.trace: g.trace(font)
        return font
    except:
        g.es("exception setting font from ",family_name)
        g.es("family,size,slant,weight:",family,size,slant,weight)
        # g.es_exception() # This just confuses people.
        return g.app.config.defaultFont
#@-node:ekr.20070930102228.35:swingGui.getFontFromParams
#@-node:ekr.20070930102228.34:Font
#@+node:ekr.20070930102228.36:getFullVersion
def getFullVersion (self,c):

    swingLevel = '<swingLevel>' ### c.frame.top.getvar("tk_patchLevel")

    return 'swing %s' % (swingLevel)
#@-node:ekr.20070930102228.36:getFullVersion
#@+node:ekr.20070930102228.37:Icons
#@+node:ekr.20070930102228.38:attachLeoIcon & createLeoIcon
def attachLeoIcon (self,w):

    """Try to attach a Leo icon to the Leo Window.

    Use tk's wm_iconbitmap function if available (tk 8.3.4 or greater).
    Otherwise, try to use the Python Imaging Library and the tkIcon package."""

    if self.bitmap != None:
        # We don't need PIL or tkicon: this is tk 8.3.4 or greater.
        try:
            w.wm_iconbitmap(self.bitmap)
        except:
            self.bitmap = None

    if self.bitmap == None:
        try:
            << try to use the PIL and tkIcon packages to draw the icon >>
        except:
            # import traceback ; traceback.print_exc()
            # g.es_exception()
            self.leoIcon = None
#@+node:ekr.20070930102228.39:<< try to use the PIL and tkIcon packages to draw the icon >>
@ This code requires Fredrik Lundh's PIL and tkIcon packages:

Download PIL    from http://www.pythonware.com/downloads/index.htm#pil
Download tkIcon from http://www.effbot.org/downloads/#tkIcon

Many thanks to Jonathan M. Gilligan for suggesting this code.
@c

import Image
import tkIcon # pychecker complains, but this *is* used.

# Wait until the window has been drawn once before attaching the icon in OnVisiblity.
def visibilityCallback(event,self=self,w=w):
    try: self.leoIcon.attach(w.winfo_id())
    except: pass
w.bind("<Visibility>",visibilityCallback)

if not self.leoIcon:
    # Load a 16 by 16 gif.  Using .gif rather than an .ico allows us to specify transparency.
    icon_file_name = g.os_path_join(g.app.loadDir,'..','Icons','LeoWin.gif')
    icon_file_name = g.os_path_normpath(icon_file_name)
    icon_image = Image.open(icon_file_name)
    if 1: # Doesn't resize.
        self.leoIcon = self.createLeoIcon(icon_image)
    else: # Assumes 64x64
        self.leoIcon = tkIcon.Icon(icon_image)
#@-node:ekr.20070930102228.39:<< try to use the PIL and tkIcon packages to draw the icon >>
#@+node:ekr.20070930102228.40:createLeoIcon
# This code is adapted from tkIcon.__init__
# Unlike the tkIcon code, this code does _not_ resize the icon file.

def createLeoIcon (self,icon):

    try:
        import Image,_tkicon

        i = icon ; m = None
        # create transparency mask
        if i.mode == "P":
            try:
                t = i.info["transparency"]
                m = i.point(lambda i, t=t: i==t, "1")
            except KeyError: pass
        elif i.mode == "RGBA":
            # get transparency layer
            m = i.split()[3].point(lambda i: i == 0, "1")
        if not m:
            m = Image.new("1", i.size, 0) # opaque
        # clear unused parts of the original image
        i = i.convert("RGB")
        i.paste((0, 0, 0), (0, 0), m)
        # create icon
        m = m.tostring("raw", ("1", 0, 1))
        c = i.tostring("raw", ("BGRX", 0, -1))
        return _tkicon.new(i.size, c, m)
    except:
        return None
#@-node:ekr.20070930102228.40:createLeoIcon
#@-node:ekr.20070930102228.38:attachLeoIcon & createLeoIcon
#@-node:ekr.20070930102228.37:Icons
#@+node:ekr.20070930102228.41:Idle Time
#@+node:ekr.20070930102228.42:swingGui.setIdleTimeHook
def setIdleTimeHook (self,idleTimeHookHandler):

    # if self.root:
        # self.root.after_idle(idleTimeHookHandler)

    pass
#@nonl
#@-node:ekr.20070930102228.42:swingGui.setIdleTimeHook
#@+node:ekr.20070930102228.43:setIdleTimeHookAfterDelay
def setIdleTimeHookAfterDelay (self,idleTimeHookHandler):

    if self.root:
        g.app.root.after(g.app.idleTimeDelay,idleTimeHookHandler)
#@-node:ekr.20070930102228.43:setIdleTimeHookAfterDelay
#@-node:ekr.20070930102228.41:Idle Time
#@+node:ekr.20070930102228.44:isTextWidget
def isTextWidget (self,w):

    '''Return True if w is a Text widget suitable for text-oriented commands.'''

    return w and isinstance(w,leoFrame.stringTextWidget) ### Tk.Text)
#@-node:ekr.20070930102228.44:isTextWidget
#@+node:ekr.20070930102228.45:makeScriptButton
def makeScriptButton (self,c,
    p=None, # A node containing the script.
    script=None, # The script itself.
    buttonText=None,
    balloonText='Script Button',
    shortcut=None,bg='LightSteelBlue1',
    define_g=True,define_name='__main__',silent=False, # Passed on to c.executeScript.
):

    '''Create a script button for the script in node p.
    The button's text defaults to p.headString'''

    k = c.k
    if p and not buttonText: buttonText = p.headString().strip()
    if not buttonText: buttonText = 'Unnamed Script Button'
    << create the button b >>
    << define the callbacks for b >>
    b.configure(command=executeScriptCallback)
    b.bind('<3>',deleteButtonCallback)
    if shortcut:
        << bind the shortcut to executeScriptCallback >>
    << create press-buttonText-button command >>
#@+node:ekr.20070930102228.46:<< create the button b >>
iconBar = c.frame.getIconBarObject()
b = iconBar.add(text=buttonText)

# if balloonText and balloonText != buttonText:
    # Pmw = g.importExtension('Pmw',pluginName='gui.makeScriptButton',verbose=False)
    # if Pmw:
        # balloon = Pmw.Balloon(b,initwait=100)
        # balloon.bind(b,balloonText)

# if sys.platform == "win32":
    # width = int(len(buttonText) * 0.9)
    # b.configure(width=width,font=('verdana',7,'bold'),bg=bg)
#@-node:ekr.20070930102228.46:<< create the button b >>
#@+node:ekr.20070930102228.47:<< define the callbacks for b >>
def deleteButtonCallback(event=None,b=b,c=c):
    if b: b.pack_forget()
    c.bodyWantsFocus()

def executeScriptCallback (event=None,
    b=b,c=c,buttonText=buttonText,p=p and p.copy(),script=script):

    if c.disableCommandsMessage:
        g.es(c.disableCommandsMessage,color='blue')
    else:
        g.app.scriptDict = {}
        c.executeScript(p=p,script=script,
        define_g= define_g,define_name=define_name,silent=silent)
        # Remove the button if the script asks to be removed.
        if g.app.scriptDict.get('removeMe'):
            g.es("Removing '%s' button at its request" % buttonText)
            b.pack_forget()
    # Do not assume the script will want to remain in this commander.
#@-node:ekr.20070930102228.47:<< define the callbacks for b >>
#@+node:ekr.20070930102228.48:<< bind the shortcut to executeScriptCallback >>
func = executeScriptCallback
shortcut = k.canonicalizeShortcut(shortcut)
ok = k.bindKey ('button', shortcut,func,buttonText)
if ok:
    g.es_print('Bound @button %s to %s' % (buttonText,shortcut),color='blue')
#@-node:ekr.20070930102228.48:<< bind the shortcut to executeScriptCallback >>
#@+node:ekr.20070930102228.49:<< create press-buttonText-button command >>
aList = [g.choose(ch.isalnum(),ch,'-') for ch in buttonText]

buttonCommandName = ''.join(aList)
buttonCommandName = buttonCommandName.replace('--','-')
buttonCommandName = 'press-%s-button' % buttonCommandName.lower()

# This will use any shortcut defined in an @shortcuts node.
k.registerCommand(buttonCommandName,None,executeScriptCallback,pane='button',verbose=False)
#@-node:ekr.20070930102228.49:<< create press-buttonText-button command >>
#@-node:ekr.20070930102228.45:makeScriptButton
#@-node:ekr.20070930102228.21:swingGui utils (TO DO)
#@+node:ekr.20070930102228.50:class leoKeyEvent (swingGui)
class leoKeyEvent:

    '''A gui-independent wrapper for gui events.'''

    def __init__ (self,event,c):

        # g.trace('leoKeyEvent(swingGui)')
        self.actualEvent = event
        self.c      = c # Required to access c.k tables.
        self.char   = hasattr(event,'char') and event.char or ''
        self.keysym = hasattr(event,'keysym') and event.keysym or ''
        self.w      = hasattr(event,'widget') and event.widget or None
        self.x      = hasattr(event,'x') and event.x or 0
        self.y      = hasattr(event,'y') and event.y or 0
        # Support for fastGotoNode plugin
        self.x_root = hasattr(event,'x_root') and event.x_root or 0
        self.y_root = hasattr(event,'y_root') and event.y_root or 0

        if self.keysym and c.k:
            # Translate keysyms for ascii characters to the character itself.
            self.keysym = c.k.guiBindNamesInverseDict.get(self.keysym,self.keysym)

        self.widget = self.w

    def __repr__ (self):

        return 'swingGui.leoKeyEvent: char: %s, keysym: %s' % (repr(self.char),repr(self.keysym))
#@nonl
#@-node:ekr.20070930102228.50:class leoKeyEvent (swingGui)
#@-node:ekr.20070930102053.1:@thin leoSwingGui.py
#@+node:ekr.20071001081358:@thin leoSwingUtils.py
@first # -*- coding: utf-8 -*-

'''Utility class and functions for Leo's swing gui.'''

@language python
@tabwidth -4
@pagewidth 80

<< imports >>

@others
#@nonl
#@+node:ekr.20071001081358.1:<< imports >>
import leoGlobals as g

# import javax.swing as swing
# import java.awt as awt
import java.lang

# import os
# import string
# import sys
#@-node:ekr.20071001081358.1:<< imports >>
#@+node:ekr.20070930184746.373:class GCEveryOneMinute
class GCEveryOneMinute(java.lang.Thread):

    def __init__ (self):
        java.lang.Thread.__init__(self)

    def run (self):
        while 1:
            java.lang.System.gc()
            self.sleep(60000)
#@-node:ekr.20070930184746.373:class GCEveryOneMinute
#@-node:ekr.20071001081358:@thin leoSwingUtils.py
#@-node:ekr.20070930105601:Gui Swing classes
#@-node:ekr.20070703111913:Swing gui
#@+node:ekr.20070824093611:Improve wxGui plugin
@nocolor
http://sourceforge.net/forum/message.php?msg_id=4480477
By: plumloco

Sorry to here about your injury, Edward. Hope its not RSI from working too hard
on Leo.  Get well soon.

When your fit and well again you might like to have a look at this.

I have been playing with custom tree widgets for leo, first with pyGTK then
with wxPython.  I had thought about doing a  pyGTK plugin until I realized how
much work was involved :)

I have adapted the __wx_gui.py plugin to work with my custom widget and called
it __wx_alt_gui.py. You, or anyone else who is interested, can download the
plugin from http://plumloco.co.uk/scratch/__wx_alt_gui.py.gz.

The great benefit of using this custom widget is that it has no secondary data
structures to keep in synch, this makes it faster and more efficient in terms
of resources. It also eliminates the problem of complicated glue code.

All that is needed is to call update whenever a change is made and the widget
will get all its data directly from the leo outline itself.  A good test for
the widget is to load a large outline and do an expand-all-nodes on it (try
the same with __wx_gui and tkLeo).

The widget still needs a lot of work but it works well enough for its usefulness
and viability to be tested.

I would be interested to know your intentions towards wxLeo.  My impression
is that you do not plan on being very active in its development, seeing as how
you are talking about doing a java swing version.

If you have no objections, I would like to do some work on wxLeo myself, partly
for its own sake and partly to gain experience for a possible pyGTK port.  Obviously
I do not want to be treading on your toes, so please tell me if I am intruding.


#@-node:ekr.20070824093611:Improve wxGui plugin
#@+node:ekr.20080118175128:Create emacs-gui for use by leoPymacs module?
@nocolor

This would draw into Emacs Windows(!)

- Text buffers: easy?
- Outline buffer: harder?
- Minibuffer: can we use the Emacs minibuffer?
#@nonl
#@-node:ekr.20080118175128:Create emacs-gui for use by leoPymacs module?
#@-node:ekr.20071211093011.1:Other guis
#@-all
#@nonl
#@-node:ekr.20040117181936:@thin ../doc/leoToDo.txt
#@-leo
