cleo.py :  » Development » Leo » Leo-4.7.1-final » leo » plugins » Python Open Source

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

Cleo adds time required, progress and priority settings for nodes.
It also allows you to colour nodes.  With the @project tag a
branch can display progress and time required with dynamic updates.

Right click on a node's icon box to dislay the cleo menu.

For full documentation see:

  - http://leo.zwiki.org/Cleo 
  - http://leo.zwiki.org/cleodoc.html
'''
#@nonl
#@-node:tbrown.20060903211930:<< docstring >>
#@nl

#@@language python
#@@tabwidth -4

#@<< imports >>
#@+node:tbrown.20060903121429.2:<< imports >>
import leo.core.leoGlobals as g
import leo.core.leoPlugins as leoPlugins

Tk = g.importExtension('Tkinter',pluginName=__name__,verbose=True)
#@-node:tbrown.20060903121429.2:<< imports >>
#@nl
__version__ = "0.25.2"
#@<< version history >>
#@+node:tbrown.20060903121429.3:<< version history >>
#@@killcolor

#@+at 
#@nonl
# Use and distribute under the same terms as leo itself.
# 
# Original code by Mark Ng <z3r0.00@gmail.com>
# 
# 0.5  Priority arrows and Archetype-based colouring of headline texts.
# 0.6  Arbitary headline colouring.
# 0.7  Colouring for node types. Added "Others" type option
# 0.8  Added "Clear Priority" option
# 0.8.1  Fixed popup location
# 0.8.2  Fixed unposting
# 0.9  Automatically colour @file and @ignore nodes
# 0.10 EKR:
# - Repackaged for leoPlugins.leo.
#     - Define g.  Eliminate from x import *.
# - Made code work with 4.3 code base:
#     - Override tree.setUnselectedHeadlineColors instead of 
# tree.setUnselectedLabelState
# - Create per-commander instances of cleoController in onCreate.
# - Converted some c/java style to python style.
# - Replaced string.find(s,...) by s.find(...) & removed import string.
# - show_menu now returns 'break':  fixes the 'popup menu is not unposting 
# bug)
# 0.11 EKR:
# - hasUD and getUD now make sure that the dict is actually a dict.
# 0.12 EKR:
# - Changed 'new_c' logic to 'c' logic.
# 0.13 EKR:
# - Installed patch roughly following code at 
# http://sourceforge.net/forum/message.php?msg_id=3517080
# - custom_colours now returns None for default.
# - Added override of setDisabledHeadlineColors so that color changes in 
# headlines happen immediately.
# - Removed checkmark menu item because there is no easy way to clear it.
# 0.14 EKR: Installed further patch to clear checkmark.
# 0.15 TNB:
# - Use dictionary of lists of canvas objects to allow removal
# - Removed code for painting over things
# - Try to track which commander/frame/tree is being used when more than
#   one Leo window open.  Not quite working, redraws are missed if you change
#   Leo windows by right clicking on the icon box, but all changes occur and
#   appear when a left click on the tree occurs.
# - Added rich_ries patch to darken colour of selected auto-colored headlines
# 0.16 TNB:
# - finally seem to have resolved all issues with multiple Leo windows
# - handlers now unregister when window closes
# 0.17 TNB:
# - don't write cleo.TkPickleVars into .leo file (but handle legacies)
# - don't write empty / all default value dictionaries into .leo file
# - added menu option to strip empty / all default value dictionaries from 
# .leo file
# 0.17.1 TNB:
# - added DanR's custom colour selection idea and progress bars
# 0.18 TNB:
# - added time required and recursive time/progress calc.
#   thanks again to DanR for pointers.
# 0.19 TNB:
# - added @project nodes for automatic display updates
# - added Show time to show times on nodes
# - added Find next todo
# 0.20 EKR: applied patch by mstarzyk.
# 0.21 EKR: protect access to c.frame.tree.canvas.  It may not exist during 
# dynamic unit tests.
# 0.22 EKR: fixed crasher in custom_colours.
# 0.23 EKR: added 'c' arg to p.isVisible (v.isVisible no longer exists).
# 0.24 EKR: removed bugs section.
# 0.25 TNB:
# - added priority sort, tag all children 'todo'
# - switched to leo icons for priority display
# 0.25.1 bobjack
# - make leo play nice with rclick and standard tree popup on linux
#     - post menu with g.app.postPopupMenu
#     - destroy menu with g.app.killPopupMenu
# 0.25.2 TNB: highlight all nodes with altered background colors, minor bug 
# fixes
#@-at
#@nonl
#@-node:tbrown.20060903121429.3:<< version history >>
#@nl

ok = Tk is not None

#@+others
#@+node:tbrown.20060903121429.11:class TkPickleVar(Tk.Variable)
if ok: # Don't define this if import Tkinter failed.

    class TkPickleVar (Tk.Variable):
        "Required as target for Tk menu functions to write back into"

        def __setstate__(self,state):
            Tk.Variable.__init__(self)
            Tk.Variable.set(self,state)

        def __getstate__(self):
            p = Tk.Variable.get(self)
            # Beware of returning False!
            return p
#@-node:tbrown.20060903121429.11:class TkPickleVar(Tk.Variable)
#@+node:tbrown.20060903121429.12:init
def init():

    if Tk is None: return False

    if g.app.gui is None:
        g.app.createTkGui(__file__)

    ok = g.app.gui.guiName() == "tkinter"

    if ok:
        leoPlugins.registerHandler(('open2','new'), onCreate)
        g.plugin_signon(__name__)

    return ok
#@nonl
#@-node:tbrown.20060903121429.12:init
#@+node:tbrown.20060903121429.13:onCreate
def onCreate (tag,key):

    c = key.get('c')

    cleoController(c)
#@-node:tbrown.20060903121429.13:onCreate
#@+node:tbrown.20060903121429.14:class cleoController
class cleoController:

    '''A per-commander class that recolors outlines.'''

    #@    @+others
    #@+node:tbrown.20080304230028:priority table
    priorities = {
      1: {'long': 'Urgent',    'short': '1', 'icon': 'pri1.png'},
      2: {'long': 'Very High', 'short': '2', 'icon': 'pri2.png'},
      3: {'long': 'High',      'short': '3', 'icon': 'pri3.png'},
      4: {'long': 'Medium',    'short': '4', 'icon': 'pri4.png'},
      5: {'long': 'Low',       'short': '5', 'icon': 'pri5.png'},
      6: {'long': 'Very Low',  'short': '6', 'icon': 'pri6.png'},
      7: {'long': 'Sometime',  'short': '7', 'icon': 'pri7.png'},
      8: {'long': 'Level 8',   'short': '8', 'icon': 'pri8.png'},
      9: {'long': 'Level 9',   'short': '9', 'icon': 'pri9.png'},
     10: {'long': 'Level 0',   'short': '0', 'icon': 'pri0.png'},
     19: {'long': 'To do',     'short': 'o', 'icon': 'chkboxblk.png'},
     20: {'long': 'Bang',      'short': '!', 'icon': 'bngblk.png'},
     21: {'long': 'Cross',     'short': 'X', 'icon': 'xblk.png'},
     22: {'long': '(cross)',   'short': 'x', 'icon': 'xgry.png'},
     23: {'long': 'Query',     'short': '?', 'icon': 'qryblk.png'},
     24: {'long': 'Bullet',    'short': '-', 'icon': 'bullet.png'},
    100: {'long': 'Done',      'short': 'D', 'icon': 'chkblk.png'},
    }

    todo_priorities = 1,2,3,4,5,6,7,8,9,10,19
    #@-node:tbrown.20080304230028:priority table
    #@+node:tbrown.20060903121429.15:birth
    def __init__ (self,c):

        self.c = c
        c.cleo = self
        self.menu = None
        self.donePriority = 100
        self.smiley = None

        self.marks = []   # list of marks made on canvas

        #@    << set / read default values >>
        #@+node:tbrown.20060913151952:<< set / read default values >>
        self.progWidth = 18
        if c.config.getInt('cleo_prog_width'):
            self.progWidth = c.config.getInt('cleo_prog_width')

        self.time_init = 1.0
        if c.config.getFloat('cleo_time_init'):
            self.time_init = c.config.getFloat('cleo_time_init')

        self.scaleProg = 1
        if c.config.getInt('cleo_prog_scale'):
            self.progWidth = c.config.getInt('cleo_prog_scale')

        self.extraProg = 4
        if c.config.getFloat('cleo_prog_extra'):
            self.progWidth = c.config.getFloat('cleo_prog_extra')

        self.time_name = 'days'
        if c.config.getString('cleo_time_name'):
            self.time_name = c.config.getString('cleo_time_name')

        self.colorIgnore = c.config.getBool('cleo_color_ignore',default=True)

        self.icon_location = 'beforeIcon'
        if c.config.getString('cleo_icon_location'):
            self.icon_location = c.config.getString('cleo_icon_location')
        #@-node:tbrown.20060913151952:<< set / read default values >>
        #@nl

        # image ids should be a property of the node
        # use {marking,image id} as the kv pair.
        self.images = {}

        #@    << define colors >>
        #@+node:tbrown.20060903121429.16:<< define colors >>

        # see docstring for related @settings

        self.colours = [
            'Black',
            'Brown', 'Purple', 'Red', 'Pink',
            'Yellow', 'Orange', 'Khaki', 'Gold',
            'DarkGreen', 'Green', 'OliveDrab2',
            'Blue', 'Lightblue', 'SteelBlue2',
            'White',
        ]

        self.archetype_colours = {
            'Data' : 'Purple',
            'Thing' : 'Green3',
            'Logic' : 'Blue',
            'Interface': 'DarkOrange',
            'Moment-Interval' : 'Red',
        }

        self.node_colours = {
            'file' : 'lightgreen',
            'Major Branch' : 'SandyBrown',
            'Feature' : 'peachpuff',
            'Comments': 'lightblue',
            'Sel. File' : 'PaleGreen4',
            'Sel. Major Branch' : 'tan4',
            'Sel. Feature' : 'PeachPuff4',
            'Sel. Comments': 'LightBlue4',
        }

        self.file_nodes = ["@file", "@thin", "@nosent", "@asis", "@root"]
        if g.app.config.exists(self.c, 'cleo_color_file_node_list', 'data'):
            self.file_nodes = self.c.config.getData('cleo_color_file_node_list')

        self.priority_colours = {
            1 : 'red',
            2 : 'orange',
            3 : 'yellow',
            4 : 'green',
            5 : 'background-colour'
        }

        for pri in self.priority_colours.iterkeys():
            if self.c.config.getColor('cleo_color_pri_'+str(pri)):
                self.priority_colours[pri] = self.c.config.getColor('cleo_color_pri_'+str(pri))

        self.red, self.green = ('red', 'green')
        for i in ('red', 'green'):
            if self.c.config.getColor('cleo_color_prog_'+i):
                self.__dict__[i] = self.c.config.getColor('cleo_color_prog_'+i)

        if c.frame and c.frame.tree and c.frame.tree.canvas:
            self.background_colour = c.frame.tree.canvas.cget('background')
        else:
            self.background_color = 'black'
        #@nonl
        #@-node:tbrown.20060903121429.16:<< define colors >>
        #@nl

        self.install_drawing_overrides()

        self.handlers = [
            ("draw-outline-text-box",self.draw),
            ("redraw-entire-outline",self.clear_canvas),
            ("iconrclick1",self.show_menu),
            ("close-frame",self.close),
        ]
        for i in self.handlers:
            leoPlugins.registerHandler(i[0], i[1])

        self.loadAllIcons()
    #@+node:tbrown.20060903121429.17:install_drawing_overrides
    def install_drawing_overrides (self):

        # g.pr("Cleo plugin: installing overrides for",self.c.shortFileName())

        tree = self.c.frame.tree # NOT tkGui.leoTkinterTree

        g.funcToMethod(self.setUnselectedHeadlineColors,tree)
        g.funcToMethod(self.setDisabledHeadlineColors,tree)
    #@nonl
    #@-node:tbrown.20060903121429.17:install_drawing_overrides
    #@-node:tbrown.20060903121429.15:birth
    #@+node:tbrown.20080303214305:loadAllIcons
    def loadAllIcons(self):
        """Load icons to represent cleo state"""

        for p in self.c.all_positions():
            self.loadIcons(p)
    #@-node:tbrown.20080303214305:loadAllIcons
    #@+node:tbrown.20080303232514:loadIcons
    def loadIcons(self, p):
        com = self.c.editCommands
        allIcons = com.getIconList(p)
        icons = [i for i in allIcons if 'cleoIcon' not in i]
        pri = self.getat(p.v, 'priority')
        if pri: pri = int(pri)
        if pri in self.priorities:
            cleo_icon_path = self.c.config.getString('cleo_icon_path')
            if not cleo_icon_path:
                cleo_icon_path = 'cleo'  # relative to leo Icons dir
            iconDir = g.os_path_abspath(
              g.os_path_normpath(
                g.os_path_join(g.app.loadDir,"..","Icons")))
            com.appendImageDictToList(icons, iconDir,
                g.os_path_join(cleo_icon_path,self.priorities[pri]['icon']),
                2, on='vnode', cleoIcon='1', where=self.icon_location)
                # Icon location defaults to 'beforeIcon' unless cleo_icon_location global defined.
                # Example: @strings[beforeIcon,beforeHeadline] cleo_icon_location = beforeHeadline
                # Note: 'beforeBox' and 'afterHeadline' collide with other elements on the line.
            com.setIconList(p, icons)
        else:
            if len(allIcons) != len(icons):  # something to remove
                com.setIconList(p, icons)

    #@-node:tbrown.20080303232514:loadIcons
    #@+node:tbrown.20060903121429.18:close
    def close(self, tag, key):
        "unregister handlers on closing commander"

        if self.c != key['c']: return  # not our problem

        for i in self.handlers:
            leoPlugins.unregisterHandler(i[0], i[1])
    #@-node:tbrown.20060903121429.18:close
    #@+node:tbrown.20060903121429.19:attributes...
    #@+at
    # These methods should really be part of vnode in accordance with the 
    # principles
    # of encapsulation and information hiding.
    # 
    # annotate was the previous name of this plugin, which is why the default 
    # values
    # for several keyword args is 'annotate'.
    #@-at
    #@nonl
    #@+node:tbrown.20060903121429.20:delUD
    def delUD (self,node,udict="annotate"):

        ''' Remove our dict from the node'''

        if hasattr(node,"unknownAttributes" ) and \
               node.unknownAttributes.has_key(udict):

            del node.unknownAttributes[udict]
    #@nonl
    #@-node:tbrown.20060903121429.20:delUD
    #@+node:tbrown.20060903121429.21:hasUD
    def hasUD (self,node,udict="annotate"):

        ''' Return True if the node has an UD.'''

        # g.trace(node) # EKR: node had better not be a position!

        return (
            hasattr(node,"unknownAttributes") and
            node.unknownAttributes.has_key(udict) and
            type(node.unknownAttributes.get(udict)) == type({}) # EKR
        )
    #@nonl
    #@-node:tbrown.20060903121429.21:hasUD
    #@+node:tbrown.20060903121429.22:getUD
    #@+at 
    #@nonl
    # no longer required, use getat()
    # 
    # def getUD (self,node,udict="annotate"):
    #     ''' Create or retrive the user dict'''
    # 
    #     if not hasattr(node,'unknownAttributes'):
    #         node.unknownAttributes = {}
    #     # Create a subdictionary for the private use of my plugin.
    #     d = node.unknownAttributes.get(udict)
    # 
    #     if d is None or type(d) != type({}): # EKR
    #         node.unknownAttributes[udict] = d = {}
    #     # read legacy files that wrote TkPickleVars to the file
    #     for ky,vl in d.iteritems():
    #         if type(vl) == self.typePickle:
    #             # g.es("cleo: converting old TkPickleVar attribute")
    #             d[ky] = vl.get()
    #     return d
    #@-at
    #@nonl
    #@-node:tbrown.20060903121429.22:getUD
    #@+node:tbrown.20060903174527:getat
    def getat(self, node, attrib):
        "new attrbiute getter"

        if (not hasattr(node,'unknownAttributes') or
            not node.unknownAttributes.has_key("annotate") or
            not type(node.unknownAttributes["annotate"]) == type({}) or
            not node.unknownAttributes["annotate"].has_key(attrib)):

            if attrib == "priority":
                return 9999
            else:
                return ""

        x = node.unknownAttributes["annotate"][attrib]
        if isinstance(x, TkPickleVar):
            node.unknownAttributes["annotate"][attrib] = x.get()
            return x.get()
        else:
            return x
    #@nonl
    #@-node:tbrown.20060903174527:getat
    #@+node:tbrown.20060903204409:testDefault
    def testDefault(self, attrib, val):
        "return true if val is default val for attrib"

        # if type(val) == self.typePickle: val = val.get()
        # not needed as only dropEmpty would call with such a thing, and it checks first

        return attrib == "priority" and val == 9999 or val == ""
    #@nonl
    #@-node:tbrown.20060903204409:testDefault
    #@+node:tbrown.20060903200946:setat
    def setat(self, node, attrib, val):
        "new attrbiute setter"

        isDefault = self.testDefault(attrib, val)

        if (not hasattr(node,'unknownAttributes') or
            not node.unknownAttributes.has_key("annotate") or
            type(node.unknownAttributes["annotate"]) != type({})):
            # dictionary doesn't exist

            if isDefault:
                return  # don't create dict. for default value

            if not hasattr(node,'unknownAttributes'):  # node has no unknownAttributes
                node.unknownAttributes = {}
                node.unknownAttributes["annotate"] = {}
            else:  # our private dictionary isn't present
                if (not node.unknownAttributes.has_key("annotate") or
                    type(node.unknownAttributes["annotate"]) != type({})):
                    node.unknownAttributes["annotate"] = {}

            node.unknownAttributes["annotate"][attrib] = val

            return

        # dictionary exists

        node.unknownAttributes["annotate"][attrib] = val

        if isDefault:  # check if all default, if so drop dict.
            self.dropEmpty(node, dictOk = True)
    #@-node:tbrown.20060903200946:setat
    #@+node:tbrown.20060903204409.1:dropEmptyAll
    def dropEmptyAll(self):
        "search whole tree for empty nodes"

        cnt = 0
        for p in self.c.all_positions(): 
            if self.dropEmpty(p.v): cnt += 1

        g.es("cleo: dropped %d empty dictionaries" % cnt)
    #@-node:tbrown.20060903204409.1:dropEmptyAll
    #@+node:tbrown.20060903204409.2:dropEmpty
    def dropEmpty(self, node, dictOk = False):

        if (dictOk or
            hasattr(node,'unknownAttributes') and
            node.unknownAttributes.has_key("annotate") and
            type(node.unknownAttributes["annotate"]) == type({})):

            isDefault = True
            for ky, vl in node.unknownAttributes["annotate"].iteritems():
                if isinstance(vl, TkPickleVar):
                    node.unknownAttributes["annotate"][ky] = vl = vl.get()
                if not self.testDefault(ky, vl):
                    isDefault = False
                    break

            if isDefault:  # no non-defaults seen, drop the whole cleo dictionary
                del node.unknownAttributes["annotate"]
                self.c.setChanged(True)
                return True

        return False
    #@-node:tbrown.20060903204409.2:dropEmpty
    #@-node:tbrown.20060903121429.19:attributes...
    #@+node:tbrown.20060903121429.23:safe_del
    def safe_del(self, d, k):
        "delete a key from a dict. if present"
        if d.has_key(k): del d[k]
    #@nonl
    #@-node:tbrown.20060903121429.23:safe_del
    #@+node:tbrown.20060903121429.24:colours...
    #@+node:tbrown.20060903121429.25:remove_colours
    def remove_colours(self,v):

        self.setat(v, 'fg', '')
        self.setat(v, 'bg', '')
        self.safe_del(self.pickles, 'fg')
        self.safe_del(self.pickles, 'bg')
        self.c.redraw()
    #@nonl
    #@-node:tbrown.20060903121429.25:remove_colours
    #@+node:tbrown.20071008150126:subtree_colours
    def subtree_colours(self,p):

        fg = self.getat(p.v, 'fg')
        bg = self.getat(p.v, 'bg')
        for n in p.subtree():
            self.setat(n.v, 'fg', fg)
            self.setat(n.v, 'bg', bg)
        self.c.redraw()
    #@-node:tbrown.20071008150126:subtree_colours
    #@+node:tbrown.20060912130940:add_colour
    def add_colour(self):

        import tkColorChooser

        myColor = '#008000'
        myColor = tkColorChooser.askcolor(myColor)
        if myColor[0] == None:
            g.es("No colour selected")
            return

        myColor = "#%02x%02x%02x" % myColor[0]

        if not self.colours.count(myColor):
            self.colours.insert(0, myColor)
            g.es("Added %s to the colour list" % (myColor))
        else:
            g.es("%s already on the colour list" % (myColor))
    #@-node:tbrown.20060912130940:add_colour
    #@+node:tbrown.20060903121429.26:custom_colours
    # use return values to set the colours so no need to muck around when loading up files.

    def custom_colours(self,v,node_is_selected):

        ''' Returns the vnodes custom colours if it has them '''

        fg, bg = None, None

        # XXX This is ugly and inefficient !!
        #@    << auto headline colours >>
        #@+node:tbrown.20060903121429.27:<< auto headline colours >>
        # set bg of @file type of nodes
        h = v and v.h or ''

        for f in self.file_nodes:
            if h.startswith(f):
                bg = self.node_colours['file']

        # set bg of @ignore type of nodes
        if self.colorIgnore:
            if h.find("@ignore") == 0:
                bg = self.node_colours['Comments']
        #@nonl
        #@-node:tbrown.20060903121429.27:<< auto headline colours >>
        #@nl
        #@    << node colours >>
        #@+node:tbrown.20060903121429.28:<< node colours >>
        # Node-based colouring --- bg only
        n = self.getat(v, 'node') # d.get('node')
        if n:
            bg = self.node_colours.get(n, bg)
        #@nonl
        #@-node:tbrown.20060903121429.28:<< node colours >>
        #@nl
        #@    << archetype colours >>
        #@+node:tbrown.20060903121429.29:<< archetype colours >>
        # Archetype-based colouring --- fg only
        a = self.getat(v, 'archetype') # d.get('archetype')
        if a:
            fg = self.archetype_colours.get(a, fg)
        #@nonl
        #@-node:tbrown.20060903121429.29:<< archetype colours >>
        #@nl
        #@    << arbitary colours >>
        #@+node:tbrown.20060903121429.30:<< arbitary colours >>
        # User defined colours overrides all
        fgv = self.getat(v, 'fg') # d.get('fg')
        if fgv:
            f = fgv
            if f:
                fg = f

        bgv = self.getat(v, 'bg') # d.get('bg')
        if bgv:
            b = bgv
            if b:
                bg = b
        #@nonl
        #@-node:tbrown.20060903121429.30:<< arbitary colours >>
        #@nl

        #g.pr("> (%s,%s) %s" % (fg,bg,v.h))

        return fg,bg
    #@-node:tbrown.20060903121429.26:custom_colours
    #@-node:tbrown.20060903121429.24:colours...
    #@+node:tbrown.20060903121429.31:drawing...
    #@+node:tbrown.20060903121429.32:redraw
    def redraw(self):
        "redraw after menu used"

        g.trace(g.callers())

        # IMPORTANT ASSUMPTION: called only after menu used

        # read updates from menu choice

        # Tk seems to use menu label when '' is used as value?
        # note, keys not present if coming via clear_all
        if self.pickles.has_key('node'):
            if self.pickles['node'].get() == 'CLEO_BLANK': self.pickles['node'].set('')
        if self.pickles.has_key('archetype'):
            if self.pickles['archetype'].get() == 'CLEO_BLANK': self.pickles['archetype'].set('')

        for ky, vl in self.pickles.iteritems():
            self.setat(self.pickleV, ky, vl.get())

        self.loadIcons(self.pickleP)

        self.clear_marks(self.c.frame.tree.canvas)
        self.update_project(self.pickleP)
        c = self.c
        c.setChanged(True)
        c.redraw_now()
    #@-node:tbrown.20060903121429.32:redraw
    #@+node:tbrown.20060903121429.33:clear_all
    def clear_all(self,v):

        self.delUD(v)
        self.pickles = {}
        self.redraw()
    #@nonl
    #@-node:tbrown.20060903121429.33:clear_all
    #@+node:tbrown.20060903121429.34:clear_canvas
    def clear_canvas(self,tag,key):
        "Remove all marks placed on canvas previously"

        if key.get("c") != self.c: return  # not out problem

        self.clear_marks(self.c.frame.tree.canvas)
    #@-node:tbrown.20060903121429.34:clear_canvas
    #@+node:tbrown.20060903121429.35:clear_marks
    def clear_marks(self, canvas):
        "Remove all marks placed on canvas previously"

        for mark in self.marks:
            canvas.delete(mark)

        self.marks = []
    #@-node:tbrown.20060903121429.35:clear_marks
    #@+node:tbrown.20060903121429.36:draw box area
    #@+node:tbrown.20060903121429.37:draw
    def draw (self,tag,key):

        ''' Redraws all the indicators for the markups of v '''

        if self.c != key['c']: return  # not our problem
        # point of order, Summary of Hooks doesn't list 'c' for 'draw-outline-text-box'

        v = key['p'].v

        if not self.hasUD(v):
            # colour='white'
            # self.draw_arrow(v,colour)
            # self.draw_tick(v)
            return None

        # priority = self.getat(v, 'priority')
        # colour = self.priority_colours.get(priority,False)
        # if colour:
        #     self.draw_arrow(v,colour)
        # if priority==self.donePriority:
        #     self.draw_tick(v)

        progress = self.getat(v, 'progress')
        if self.scaleProg != 0 and progress != '': 
            progWidth = self.progWidth
            if self.scaleProg == 2 and self.getat(v, 'time_req') != '':
                progWidth += self.getat(v, 'time_req') * self.extraProg
            self.draw_prog(v, float(progress)/100., progWidth)

        # Archetype are not drawn here
        return None
    #@nonl
    #@-node:tbrown.20060903121429.37:draw
    #@+node:tbrown.20060903121429.39:draw_box <1 'hours', 100%>
    def draw_box (self,v,color,canvas):

        c = self.c
        if v.isVisible(c):
            x, y = v.iconx, v.icony
            self.marks.append(
                canvas.create_rectangle(x,y,x+10,y+10,fill=color)
                )
    #@nonl
    #@-node:tbrown.20060903121429.39:draw_box <1 'hours', 100%>
    #@+node:tbrown.20060903121429.40:draw_arrow
    # If too long can obscure +/- box

    def draw_arrow (self,v,colour='darkgreen'):

        c = self.c
        tree = c.frame.tree

        canvas = tree.canvas
        clear = colour == 'background-colour'

        if not clear:
            self.marks.append(canvas.create_line(v.iconx-10,v.icony+8,v.iconx+5,v.icony+8,
                arrow = "last", fill = colour, width = 4))
    #@nonl
    #@-node:tbrown.20060903121429.40:draw_arrow
    #@+node:tbrown.20060903121429.41:draw_tick
    def draw_tick (self,v,colour='salmon'):

        canvas = self.c.frame.tree.canvas

        # canvas.create_line(v.iconx+13-5,v.icony+8,v.iconx+13,v.icony+13,fill=colour,width=2)
        # canvas.create_line(v.iconx+13,v.icony+13,v.iconx+13+12,v.icony-2,fill=colour,width=2)

        # Define the 3 points of a check mark to allow quick adjustment.
        XpointA = v.iconx-15 + 3
        YpointA = v.icony + 8-2
        XpointB = v.iconx-7
        YpointB = v.icony + 13
        XpointC = v.iconx + 5
        YpointC = v.icony-2
        # draw the check-mark

        self.marks.append(
            canvas.create_line(XpointA,YpointA,XpointB,YpointB,fill=colour,width=2)
        )
        self.marks.append(
            canvas.create_line(XpointB,YpointB,XpointC,YpointC,fill=colour,width=2)
        )
    #@nonl
    #@-node:tbrown.20060903121429.41:draw_tick
    #@+node:tbrown.20060912215129:draw_prog
    def draw_prog (self, v, prop, progWidth):

        canvas = self.c.frame.tree.canvas

        XpointA = v.iconx+1
        YpointA = v.icony-1

        XpointB = v.iconx+1+int(prop * progWidth)
        YpointB = v.icony-1

        XpointC = v.iconx+1+progWidth
        YpointC = v.icony-1

        self.marks.append(
            canvas.create_line(XpointA,YpointA,XpointB,YpointB,fill=self.green,width=2)
        )
        self.marks.append(
            canvas.create_line(XpointB,YpointB,XpointC,YpointC,fill=self.red,width=2)
        )
    #@nonl
    #@-node:tbrown.20060912215129:draw_prog
    #@+node:tbrown.20060903121429.42:draw_invertedT
    def draw_invertedT (self,v,color,canvas):

        '''Draw the symbol for data.'''

        c = self.c
        if v.isVisible(c):

            x, y = v.iconx, v.icony ; bottom = y+13

            # Draw horizontal line.
            self.marks.append(   
                canvas.create_line(x,bottom,x+10,bottom,fill=color,width=2)
            )

            # Draw vertical line.
            self.marks.append(
                canvas.create_line(x+5,bottom-5,x+5,bottom,fill=color,width=2)
            )
    #@nonl
    #@-node:tbrown.20060903121429.42:draw_invertedT
    #@+node:tbrown.20060903121429.43:draw_topT
    def draw_topT (self,v,color,canvas):

        '''Draw the symbol for interfaces.'''

        c = self.c
        if v.isVisible(c):

            x, y = v.iconx, v.icony ; topl = y 

            # Draw the horizontal line.
            self.marks.append(
                canvas.create_line(x,topl,x+10,topl,fill=color,width=2)
            )

            # Draw the vertical line.
            self.marks.append(
                canvas.create_line(x+5,topl,x+5,topl+15,fill=color,width=2)
            )
    #@nonl
    #@-node:tbrown.20060903121429.43:draw_topT
    #@-node:tbrown.20060903121429.36:draw box area
    #@+node:tbrown.20060903121429.44:overrides of leoTkinterTree methods
    #@+node:tbrown.20060903121429.45:setUnselectedHeadlineColors
    def setUnselectedHeadlineColors (self,p):

        # unlike handlers, override commands don't need to check self.c against other c
        c = self.c

        if hasattr(p,'edit_widget'):  #temporary cvs transitional code
            w = p.edit_widget()
        else:
            w = c.edit_widget(p)

        fg, bg = self.custom_colours(p.v,node_is_selected=False)

        fg = fg or c.config.getColor("headline_text_unselected_foreground_color") or 'black'
        bg = bg or c.config.getColor("headline_text_unselected_background_color") or 'white'

        try:
            w.configure(state="disabled",highlightthickness=0,fg=fg,bg=bg)
        except:
            g.es_exception()
    #@nonl
    #@-node:tbrown.20060903121429.45:setUnselectedHeadlineColors
    #@+node:tbrown.20060903121429.46:setDisabledHeadlineColors
    def setDisabledHeadlineColors (self,p):

        c = self.c

        if hasattr(p,'edit_widget'):  #temporary cvs transitional code
            w = p.edit_widget()
        else:
            w = c.edit_widget(p)

        fg, bg = self.custom_colours(p.v,node_is_selected=True)

        fg = fg or c.config.getColor("headline_text_selected_foreground_color") or 'black'
        bg2 = c.config.getColor("headline_text_selected_background_color")
        bg = bg or bg2 or 'gray80'

        try:
            if bg != bg2:
                hl = c.config.getColor("headline_text_selected_highlight_color") or 'black'
                w.configure(state="disabled", highlightthickness=1, highlightbackground=hl, fg=fg, bg=bg)
            else:
                w.configure(state="disabled", highlightthickness=0, fg=fg, bg=bg)
        except:
            g.es_exception()
    #@nonl
    #@-node:tbrown.20060903121429.46:setDisabledHeadlineColors
    #@-node:tbrown.20060903121429.44:overrides of leoTkinterTree methods
    #@-node:tbrown.20060903121429.31:drawing...
    #@+node:tbrown.20060903121429.47:menus...
    #@+node:tbrown.20060903121429.48:prep_pickle
    def prep_pickle(self, v, pkl, default = None):
        "prepare a TkPickleVar in self.pickles for a menu write back"

        self.pickles[pkl] = TkPickleVar()
        self.pickles[pkl].set(self.getat(v, pkl))
    #@-node:tbrown.20060903121429.48:prep_pickle
    #@+node:tbrown.20060903121429.49:archetype_menu
    def archetype_menu(self,parent,p):

        self.prep_pickle(p.v, "archetype")

        menu = Tk.Menu(parent,tearoff=0,takefocus=1)

        for label,value in (
            ('Data/Description','Data'),
            ('Thing/Place','Thing'),
            ('Logic/Function','Logic'),
            ('Interface/Role','Interface'),
            ('Moment-Interval/Event Handler','Moment-Interval'),
            ('Other','CLEO_BLANK'),  # Tk seems to use menu label if '' used for value?
        ):
            menu.add_radiobutton(label=label,
                underline=0,command=self.redraw,
                variable=self.pickles['archetype'],value=value)

        parent.add_cascade(label='Code Archetypes',underline=6,menu=menu)

        return menu
    #@nonl
    #@-node:tbrown.20060903121429.49:archetype_menu
    #@+node:tbrown.20060903121429.50:colours_menu
    def colours_menu(self,parent, p):

        c = self.c
        self.prep_pickle(p.v, 'fg')
        self.prep_pickle(p.v, 'bg')

        for var in (self.pickles['fg'].get(), self.pickles['bg'].get()):
            if var and var != '' and var != 'Other' and not self.colours.count(var):
                self.colours.insert(0, var)
                g.es("Added %s to the colour list" % (var))

        for label,var in (('Foreground',self.pickles['fg']),('Background',self.pickles['bg'])):
            menu = Tk.Menu(parent,tearoff=0,takefocus=1)
            for color in self.colours:
                menu.add_radiobutton(label=color,
                    variable=var, value=color,
                    command=self.redraw)
            parent.add_cascade(label=label,underline=0,menu=menu)

        def cleoColorsMenuCallback():
            self.remove_colours(p.v)
        def cleoColorsMenuSubtree():
            self.subtree_colours(p)

        c.add_command(parent,label='Remove Colouring', underline=0,
            command=cleoColorsMenuCallback)

        c.add_command(parent,label='Colour subtree', underline=0,
            command=cleoColorsMenuSubtree)

        def cleoAddColorsMenuCallback():
            self.add_colour()

        c.add_command(parent,label='New Colour', underline=0,
            command=cleoAddColorsMenuCallback)
    #@-node:tbrown.20060903121429.50:colours_menu
    #@+node:tbrown.20060903121429.51:node menu
    def nodes_menu(self,parent,p):

        self.prep_pickle(p.v, 'node')

        menu = Tk.Menu(parent,tearoff=0,takefocus=1)

        for label,value in (
            ('@file','file'),
            ('Major Branch','Major Branch'),
            ('Feature/Concern','Feature'),
            ('Comments/@ignore','Comments'),
            ('Other','CLEO_BLANK'),  # Tk seems to use menu label if '' used for value?
        ):
            menu.add_radiobutton(label=label,underline=0,
                command=self.redraw,variable=self.pickles['node'],value=value)

        parent.add_cascade(label='Node types',underline=0,menu=menu)
    #@nonl
    #@-node:tbrown.20060903121429.51:node menu
    #@+node:tbrown.20061020145804:left_priority_menu
    def left_priority_menu(self, menu, p):
        self.prep_pickle(p.v, 'priority', default=9999)
        for pri in self.priorities:
            value,label = pri, self.priorities[pri]['short']
            s = '%s' % (label)
            menu.add_radiobutton(
                label=s,variable=self.pickles['priority'],value=value,
                command=self.redraw,underline=0)
    #@nonl
    #@-node:tbrown.20061020145804:left_priority_menu
    #@+node:tbrown.20060903121429.52:priority_menu
    def prikey(self, v):
        """key function for sorting by priority"""
        # getat returns 9999 for nodes without priority, so you'll only get -1
        # if a[1] is not a node.  Or even an object.

        try:
            pa = int(self.getat(v, 'priority'))
        except ValueError:
            pa = -1

        return pa

    def priSort(self):
        self.c.selectPosition(self.pickleP)
        self.c.sortSiblings(key=self.prikey)

    def childrenTodo(self):
        for p in self.pickleP.children():
            if self.getat(p.v, 'priority') != 9999: continue
            self.setat(p.v, 'priority', 19)
            self.loadIcons(p)
        self.c.redraw()

    def showDist(self):
        """show distribution of priority levels in subtree"""
        pris = {}
        for p in self.pickleP.subtree():
            pri = int(self.getat(p.v, 'priority'))
            if pri not in pris:
                pris[pri] = 1
            else:
                pris[pri] += 1
        pris = sorted([(k,v) for k,v in pris.iteritems()]) 
        for pri in pris:
            if pri[0] in self.priorities:
                g.es('%s\t%d\t%s' % (self.priorities[pri[0]]['short'], pri[1],
                    self.priorities[pri[0]]['long']))

    def reclassify(self):
        """change priority codes"""

        g.es('\n Current distribution:')
        self.showDist()
        dat = {}
        for end in 'from', 'to':
            x0 = g.app.gui.runAskOkCancelStringDialog(
                self.c,'Reclassify priority' ,'%s priorities (1-7,19)' % end.upper())
            try:
                x0 = [int(i) for i in x0.replace(',',' ').split()
                      if int(i) in self.todo_priorities]
            except:
                g.es('Not understood, no action')
                return
            if not x0:
                g.es('No action')
                return
            dat[end] = x0

        if len(dat['from']) != len(dat['to']):
            g.es('Unequal list lengths, no action')
            return

        cnt = 0
        for p in self.pickleP.subtree():
            pri = int(self.getat(p.v, 'priority'))
            if pri in dat['from']:
                self.setat(p.v, 'priority', dat['to'][dat['from'].index(pri)])
                self.loadIcons(p)
                cnt += 1
        g.es('\n%d priorities reclassified, new distribution:' % cnt)
        self.showDist()
        if cnt:
            self.c.redraw_now()

    def priority_menu(self,parent,p):

        # done already in left_priority menu
        # self.prep_pickle(p.v, 'priority', default=9999)

        c = self.c
        menu = Tk.Menu(parent,tearoff=0,takefocus=1)

        parent.add_cascade(label='Priority', menu=menu,underline=1)

        # Instead of just redraw, set changed too.
        for pri in self.priorities:
            value,label = pri, self.priorities[pri]['long']
            s = '%d %s' % (value,label)
            menu.add_radiobutton(
                label=s,variable=self.pickles['priority'],value=value,
                command=self.redraw,underline=0)

        menu.add_separator()

        c.add_command(menu,label='Sort',
            command=self.priSort, underline=0)
        c.add_command(menu,label='Children -> To do',
            command=self.childrenTodo, underline=0)
        c.add_command(menu,label='Show distribution',
            command=self.showDist, underline=0)
        c.add_command(menu,label='Reclassify',
            command=self.reclassify, underline=0)

        menu.add_separator()

        c.add_command(menu,label='Clear',
            command=lambda p=p:self.priority_clear(p.v),underline=0)

        return menu
    #@-node:tbrown.20060903121429.52:priority_menu
    #@+node:tbrown.20060912220630:progress_menu
    def progress_menu(self,parent,p):

        self.prep_pickle(p.v, 'progress')

        menu = Tk.Menu(parent,tearoff=0,takefocus=1)

        parent.add_cascade(label='Progress', menu=menu,underline=1)

        # Instead of just redraw, set changed too.
        for value in range(0,11):
            s = '%d%%' % (value*10)
            menu.add_radiobutton(
                label=s,variable=self.pickles['progress'],value=value*10,
                command=self.redraw,underline=0)

        menu.add_separator()

        c = self.c

        c.add_command(menu,label='Clear',
            command=lambda p=p:self.progress_clear(p.v),underline=0)

        def toggle_scaling():
            self.scaleProg = (self.scaleProg+1) % 3
            self.redraw()

        c.add_command(menu,label='Toggle progress scaling',
            underline=0,command=toggle_scaling)

        return menu
    #@nonl
    #@-node:tbrown.20060912220630:progress_menu
    #@+node:tbrown.20060913212017:time_menu
    def time_menu(self,parent,p):

        c = self.c ; v = p.v

        menu = Tk.Menu(parent,tearoff=0,takefocus=1)

        lab = 'Time'
        if self.getat(v, 'time_req') != '':
            lab += ' ('+str(self.getat(v, 'time_req'))+')'
        parent.add_cascade(label=lab, menu=menu,underline=1)

        lab = 'Set time required'
        if self.getat(v, 'time_req') != '':
            lab += ' ('+str(self.getat(v, 'time_req'))+')'
        c.add_command(menu,label=lab,
            underline=0,command=lambda:self.set_time_req(p))
        c.add_command(menu,label='Clear time required',
            underline=0,command=lambda:self.clear_time_req(p))

        c.add_command(menu,label='Show times',
            underline=0,command=lambda:self.show_times(p, show=True))
        c.add_command(menu,label='Hide times',
            underline=0,command=lambda:self.show_times(p, show=False))

        def local_recalc():
            self.recalc_time(p)
            self.pickles['progress'].set(self.getat(v, 'progress'))
            self.redraw()

        c.add_command(menu,label='Re-calc. time required',
            underline=0,command=local_recalc)

        def local_clear():
            self.recalc_time(p, clear=True)
            self.pickles['progress'].set(self.getat(v, 'progress'))
            self.redraw()

        c.add_command(menu,label='Clear derived times',
            underline=0,command=local_clear)

        return menu
    #@nonl
    #@-node:tbrown.20060913212017:time_menu
    #@+node:tbrown.20060903121429.53:show_menu
    def show_menu (self,tag,k):

        g.app.gui.killPopupMenu()

        if k['c'] != self.c: return  # not our problem

        p = k['p']
        self.c.selectPosition(p)
        v = k['p'].v ## EKR

        self.pickles = {}  # clear dict. of TkPickleVars
        self.pickleV = v
        self.pickleP = p.copy()

        # Create the menu.
        menu = Tk.Menu(None,tearoff=0,takefocus=0)

        self.left_priority_menu(menu, p)

        c = self.c

        c.add_command(menu,label='T',
            underline=0,command=lambda:self.set_time_req(p))

        c.add_command(menu,label='Find next todo', columnbreak=1,
            underline=0,command=lambda:self.find_todo(p))
        self.priority_menu(menu,p)
        self.progress_menu(menu,p)
        self.time_menu(menu,p)
        self.archetype_menu(menu, p)
        self.nodes_menu(menu,p)

        menu.add_separator()

        self.colours_menu(menu,p)
        # fonts_menu(menu,p)
        menu.add_separator()
        c.add_command(menu,label='Clear All for node',
            underline=0,command=lambda:self.clear_all(v))
        c.add_command(menu,label='Flush empty cleo attribs.',
            underline=0,command=self.dropEmptyAll)

        # Show the menu.
        event = k['event']
        g.app.gui.postPopupMenu(self.c, menu, event.x_root,event.y_root)

        return 'break' # EKR: Prevent other right clicks.
    #@-node:tbrown.20060903121429.53:show_menu
    #@-node:tbrown.20060903121429.47:menus...
    #@+node:tbrown.20060903121429.54:priority_clear
    def priority_clear(self,v):

        self.setat(v, 'priority', 9999)
        self.safe_del(self.pickles, 'priority')
        self.redraw()
    #@nonl
    #@-node:tbrown.20060903121429.54:priority_clear
    #@+node:tbrown.20060912221139:progress_clear
    def progress_clear(self,v):

        self.setat(v, 'progress', '')
        self.safe_del(self.pickles, 'progress')
        self.redraw()
    #@nonl
    #@-node:tbrown.20060912221139:progress_clear
    #@+node:tbrown.20060913153851:set_time_req
    def set_time_req(self,p):
        v = p.v
        tkSimpleDialog = g.importExtension('tkSimpleDialog',pluginName=__name__)
        initialvalue = str(self.time_init)
        if self.getat(v, 'time_req') != '':
            initialvalue = self.getat(v, 'time_req')
        prompt = '%s required' % self.time_name
        tr = tkSimpleDialog.askfloat(prompt, prompt, parent = self.c.frame.tree.canvas,
                                     initialvalue = str(initialvalue))

        if tr == None: return

        self.setat(v, 'time_req', tr)

        if self.getat(v, 'progress') == '':
            self.setat(v, 'progress', 0)
            self.pickles['progress'].set(0)

        self.redraw()
    #@nonl
    #@-node:tbrown.20060913153851:set_time_req
    #@+node:tbrown.20060913204451:show_times
    def show_times(self, p, show=False):

        import re

        def rnd(x): return re.sub('.0$', '', '%.1f' % x)

        for nd in p.self_and_subtree():
            if hasattr(nd, 'setHeadStringOrHeadline'):  # temp. cvs transition code
                nd.setHeadStringOrHeadline(re.sub(' <[^>]*>$', '', nd.h))
            else:
                self.c.setHeadString(nd, re.sub(' <[^>]*>$', '', nd.h))
                # nd.setHeadString(re.sub(' <[^>]*>$', '', nd.h))
            if show:
                tr = self.getat(nd.v, 'time_req')
                pr = self.getat(nd.v, 'progress')
                try: pr = float(pr)
                except: pr = ''
                if tr != '' or pr != '':
                    ans = ' <'
                    if tr != '':
                        if pr == '' or pr == 0 or pr == 100:
                            ans += rnd(tr) + ' ' + self.time_name
                        else:
                            ans += '%s+%s=%s %s' % (rnd(pr/100.*tr), rnd((1-pr/100.)*tr), rnd(tr), self.time_name)
                        if pr != '': ans += ', '
                    if pr != '':
                        ans += rnd(pr) + '%'  # pr may be non-integer if set by recalc_time
                    ans += '>'
                    if hasattr(nd, 'setHeadStringOrHeadline'):  # temp. cvs transition code
                        nd.setHeadStringOrHeadline(nd.h+ans)
                    else:
                        self.c.setHeadString(nd, nd.h+ans)
    #@-node:tbrown.20060913204451:show_times
    #@+node:tbrown.20060913133338:recalc_time
    def recalc_time(self, p, clear=False):
        v = p.v
        time_totl = None
        time_done = None

        # get values from children, if any
        for cn in p.children():
            ans = self.recalc_time(cn.copy(), clear)
            if time_totl == None:
                time_totl = ans[0]
            else:
                if ans[0] != None: time_totl += ans[0]

            if time_done == None:
                time_done = ans[1]
            else:
                if ans[1] != None: time_done += ans[1]

        if time_totl != None:  # some value returned

            if clear:  # then we should just clear our values
                self.setat(v, 'progress', '')
                self.setat(v, 'time_req', '')
                return (time_totl, time_done)

            if time_done != None:  # some work done
                # can't round derived progress without getting bad results form show_times
                pr = float(time_done) / float(time_totl) * 100.
                self.setat(v, 'progress', pr)
            else:
                self.setat(v, 'progress', 0)
            self.setat(v, 'time_req', time_totl)
        else:  # no values from children, use own
            tr = self.getat(v, 'time_req')
            pr = self.getat(v, 'progress')
            if tr != '':
                time_totl = tr
                if pr != '':
                    time_done = float(pr) / 100. * tr
                else:
                    self.setat(v, 'progress', 0)

        # if time_totl != None:
        #     self.setat(v, 'time_req', time_totl)
        #     if time_done != None:
        #         self.setat(v, 'progress', int(float(time_done) / float(time_totl) * 100.))
        #     else:
        #         self.setat(v, 'progress', 0)

        # import sys
        # s0, s1 = (time_totl, time_done)
        # if not s0: s0 = 0
        # if not s1: s1 = 0
        # sys.stderr.write('%s %g %g\n' % (p.h, float(s0), float(s1)))
        return (time_totl, time_done)
    #@-node:tbrown.20060913133338:recalc_time
    #@+node:tbrown.20060913104504.1:clear_time_req
    def clear_time_req(self,p):

        v = p.v
        self.setat(v, 'time_req', '')
        self.safe_del(self.pickles, 'time_req')
        self.redraw()
    #@nonl
    #@-node:tbrown.20060913104504.1:clear_time_req
    #@+node:tbrown.20060914134553.376:update_project
    def update_project(self, p):
        """Find highest parent with '@project' in headline and run recalc_time
        and maybe show_times (if headline has '@project time')"""

        project = None

        for nd in p.self_and_parents():
            if nd.h.find('@project') > -1:
                project = nd.copy()

        if project:
            self.recalc_time(project)
            if project.h.find('@project time') > -1:
                self.show_times(project, show=True)
    #@-node:tbrown.20060914134553.376:update_project
    #@+node:tbrown.20060919160306:find_todo
    def find_todo(self, p, stage = 0):
        """Recursively find the next todo"""

        # search is like XPath 'following' axis, all nodes after p in document order.
        # returning True should always propogate all the way back up to the top
        # stages: 0 - user selected start node, 1 - searching siblings, parents siblings, 2 - searching children

        if not p: return True  # not required - safety net

        # see if this node is a todo
        if stage != 0 and self.getat(p.v, 'priority') in self.todo_priorities:
            if p.getParent(): 
                self.c.selectPosition(p.getParent())
                self.c.expandNode()
            self.c.selectPosition(p)
            self.c.redraw()
            return True

        for nd in p.children():
            if self.find_todo(nd, stage = 2): return True

        if stage < 2 and p.getNext():
            if self.find_todo(p.getNext(), stage = 1): return True

        if stage < 2 and p.getParent() and p.getParent().getNext():
            if self.find_todo(p.getParent().getNext(), stage = 1): return True

        if stage == 0: g.es("None found")

        return False
    #@-node:tbrown.20060919160306:find_todo
    #@-others
#@nonl
#@-node:tbrown.20060903121429.14:class cleoController
#@-others
#@nonl
#@-node:tbrown.20060828111141:@thin cleo.py
#@-leo
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.