todo.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 » todo.py
#@+leo-ver=4-thin
#@+node:tbrown.20090119215428.2:@thin todo.py
#@<< docstring >>
#@+node:tbrown.20090119215428.3:<< docstring >>
'''todo.py  -- ToDo and simple task management for leo

(todo is the Qt version of the Tk cleo plugin)

todo adds time required, progress and priority settings for nodes.
With the @project tag a branch can display progress and time
required with dynamic hierachical updates.

For full documentation see:

  - http://leo.zwiki.org/ToDo 
  - http://leo.zwiki.org/tododoc.html
'''
#@nonl
#@-node:tbrown.20090119215428.3:<< docstring >>
#@nl

#@@language python
#@@tabwidth -4

#@<< imports >>
#@+node:tbrown.20090119215428.4:<< imports >>
import leo.core.leoGlobals as g
import re
if g.app.gui.guiName() == "qt":
    import leo.core.leoPlugins as leoPlugins
    import os

    from PyQt4 import QtCore,QtGui,uic
    Qt = QtCore.Qt
#@-node:tbrown.20090119215428.4:<< imports >>
#@nl
__version__ = "0.30"
#@<< version history >>
#@+node:tbrown.20090119215428.5:<< version history >>
#@@killcolor

#@+at 
#@nonl
# Use and distribute under the same terms as leo itself.
# 
# 0.30 TNB
#   - fork from cleo.py to todo.py
#   - Qt interface in a tab
#@-at
#@-node:tbrown.20090119215428.5:<< version history >>
#@nl

#@+others
#@+node:tbrown.20090119215428.6:init
def init():

    if g.app.gui.guiName() != "qt":
        print('todo.py plugin not loading because gui is not Qt')
        return False

    leoPlugins.registerHandler('after-create-leo-frame',onCreate)
    # can't use before-create-leo-frame because Qt dock's not ready
    g.plugin_signon(__name__)
    g.tree_popup_handlers.append(popup_entry)
    return True

#@-node:tbrown.20090119215428.6:init
#@+node:tbrown.20090119215428.7:onCreate
def onCreate (tag,key):

    c = key.get('c')

    todoController(c)
#@-node:tbrown.20090119215428.7:onCreate
#@+node:tbrown.20090630144958.5318:popup_entry
def popup_entry(c,p,menu):
    c.cleo.addPopupMenu(c,p,menu)
#@-node:tbrown.20090630144958.5318:popup_entry
#@+node:tbrown.20090119215428.8:class todoQtUI
if g.app.gui.guiName() == "qt":
    class cleoQtUI(QtGui.QWidget):

        def __init__(self, owner, logTab=True):

            self.owner = owner

            QtGui.QWidget.__init__(self)
            uiPath = g.os_path_join(g.app.leoDir, 'plugins', 'ToDo.ui')
            form_class, base_class = uic.loadUiType(uiPath)
            if logTab:
                self.owner.c.frame.log.createTab('Task', widget = self) 
            self.UI = form_class()
            self.UI.setupUi(self)

            u = self.UI
            o = self.owner

            self.menu = QtGui.QMenu()
            self.populateMenu(self.menu, o)

            u.butMenu.setMenu(self.menu)

            self.connect(u.butHelp, QtCore.SIGNAL("clicked()"), o.showHelp)

            self.connect(u.butClrProg, QtCore.SIGNAL("clicked()"),
                o.progress_clear)
            self.connect(u.butClrTime, QtCore.SIGNAL("clicked()"),
                o.clear_time_req)
            self.connect(u.butPriClr, QtCore.SIGNAL("clicked()"),
                o.priority_clear)

            # if live update is too slow change valueChanged(*) to editingFinished()
            self.connect(u.spinTime, QtCore.SIGNAL("valueChanged(double)"),
                lambda v: o.set_time_req(val=u.spinTime.value()))
            self.connect(u.spinProg, QtCore.SIGNAL("valueChanged(int)"),
                lambda v: o.set_progress(val=u.spinProg.value()))

            for but in ["butPri1", "butPri6", "butPriChk", "butPri2",
                "butPri4", "butPri5", "butPri8", "butPri9", "butPri0",
                "butPriToDo", "butPriXgry", "butPriBang", "butPriX",
                "butPriQuery", "butPriBullet", "butPri7", 
                "butPri3"]:

                w = getattr(u, but)

                # w.property() seems to give QVariant in python 2.x and int in 3.x!?
                try:
                    pri = int(w.property('priority'))
                except (TypeError, ValueError):
                    try:
                        pri, ok = w.property('priority').toInt()
                    except (TypeError, ValueError):
                        pri = -1

                def setter(pri=pri): o.setPri(pri)
                self.connect(w, QtCore.SIGNAL("clicked()"), setter)

        def setProgress(self, prgr):
            self.UI.spinProg.blockSignals(True)
            self.UI.spinProg.setValue(prgr)
            self.UI.spinProg.blockSignals(False)
        def setTime(self, timeReq):
            self.UI.spinTime.blockSignals(True)
            self.UI.spinTime.setValue(timeReq)
            self.UI.spinTime.blockSignals(False)

        @staticmethod
        def populateMenu(menu,o): 
            menu.addAction('Find next ToDo', o.find_todo)
            m = menu.addMenu("Priority")
            m.addAction('Sort', o.priSort)
            m.addAction('Mark children todo', o.childrenTodo)
            m.addAction('Show distribution', o.showDist)
            m.addAction('Redistribute', o.reclassify)
            m = menu.addMenu("Time")
            m.addAction('Show times', lambda:o.show_times(show=True))
            m.addAction('Hide times', lambda:o.show_times(show=False))
            m.addAction('Re-calc. derived times', o.local_recalc)
            m.addAction('Clear derived times', o.local_clear)
            m = menu.addMenu("Misc.")
            m.addAction('Hide all Todo icons', lambda:o.loadAllIcons(clear=True))
            m.addAction('Show all Todo icons', o.loadAllIcons)
            m.addAction('Delete Todo from node', o.clear_all)
            m.addAction('Delete Todo from subtree', lambda:o.clear_all(recurse=True))
            m.addAction('Delete Todo from all', lambda:o.clear_all(all=True))
#@-node:tbrown.20090119215428.8:class todoQtUI
#@+node:tbrown.20090119215428.9:class todoController
class todoController:

    '''A per-commander class that manages tasks.'''

    #@    @+others
    #@+node:tbrown.20090119215428.10: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.20090119215428.10:priority table
    #@+node:tbrown.20090119215428.11:__init__
    def __init__ (self,c):

        self.c = c
        c.cleo = self
        self.donePriority = 100
        self.menuicons = {}  # menu icon cache
        self.recentIcons = []
        #X self.smiley = None
        self.redrawLevels = 0

        #@    << set / read default values >>
        #@+node:tbrown.20090119215428.12:<< set / read default values >>
        self.time_name = 'days'
        if c.config.getString('todo_time_name'):
            self.time_name = c.config.getString('todo_time_name')

        self.icon_location = 'beforeHeadline'
        if c.config.getString('todo_icon_location'):
            self.icon_location = c.config.getString('todo_icon_location')

        self.prog_location = 'beforeHeadline'
        if c.config.getString('todo_prog_location'):
            self.prog_location = c.config.getString('todo_prog_location')

        self.icon_order = 'pri-first'
        if c.config.getString('todo_icon_order'):
            self.icon_order = c.config.getString('todo_icon_order')
        #@-node:tbrown.20090119215428.12:<< set / read default values >>
        #@nl

        self.handlers = [
           ("close-frame",self.close),
           ('select3', self.updateUI),
           ('save2', self.loadAllIcons),
        ]

        # chdir so the Icons can be located
        owd = os.getcwd()
        os.chdir(os.path.split(__file__)[0])
        self.ui = cleoQtUI(self)
        os.chdir(owd)

        for i in self.handlers:
            leoPlugins.registerHandler(i[0], i[1])

        self.loadAllIcons()
    #@-node:tbrown.20090119215428.11:__init__
    #@+node:tbrown.20090522142657.7894:__del__
    def __del__(self):
        for i in self.handlers:
            leoPlugins.unregisterHandler(i[0], i[1])
    #@-node:tbrown.20090522142657.7894:__del__
    #@+node:tbrown.20090630144958.5319:addPopupMenu
    def addPopupMenu(self,c,p,menu):

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

        taskmenu = menu.addMenu("Task")

        submenu = taskmenu.addMenu("Status")

        iconlist = [(menu, i) for i in self.recentIcons]
        iconlist.extend([(submenu, i) for i in self.priorities])

        for m,i in iconlist:
            icon = self.menuicon(i)
            a = m.addAction(icon, self.priorities[i]["long"])
            def func(pri=i):
                self.setPri(pri)
            a.connect(a, QtCore.SIGNAL("triggered()"), func)

        submenu = taskmenu.addMenu("Progress")
        for i in range(11):
            icon = self.menuicon(10*i, progress=True)
            a = submenu.addAction(icon, "%d%%" % (i*10))
            def func(prog=i):
                self.set_progress(val=10*prog)
            a.connect(a, QtCore.SIGNAL("triggered()"), func)

        prog = self.getat(p.v, 'progress')
        if isinstance(prog,int):
            a = taskmenu.addAction("(%d%% complete)"%prog)
            a.enabled = False

        time_ = self.getat(p.v, 'time_req')
        if isinstance(time_,float):
            if isinstance(prog,int):
                f = prog/100.
                a = taskmenu.addAction("(%s+%s=%s %s)"%(rnd(f*time_),
                    rnd((1.-f)*time_),rnd(time_), self.time_name))
            else:
                a = taskmenu.addAction("(%s %s)"%(rnd(time_), self.time_name))
            a.enabled = False


        cleoQtUI.populateMenu(taskmenu, self)
    #@-node:tbrown.20090630144958.5319:addPopupMenu
    #@+node:tbrown.20090630144958.5320:menuicon
    def menuicon(self, pri, progress=False):
        """return icon from cache, placing it there if needed"""

        if progress:
            prog = pri
            pri = 'prog-%d'%pri

        if pri not in self.menuicons:

            if progress:
                fn = 'prg%03d.png' % prog
            else:
                fn = self.priorities[pri]["icon"]

            iconDir = g.os_path_abspath(
              g.os_path_normpath(
                g.os_path_join(g.app.loadDir,"..","Icons")))

            fn = g.os_path_join(iconDir,'cleo',fn)

            self.menuicons[pri] = QtGui.QIcon(fn)

        return self.menuicons[pri]
    #@-node:tbrown.20090630144958.5320:menuicon
    #@+node:tbrown.20090119215428.13:redrawer
    def redrawer(fn):
        """decorator for methods which create the need for a redraw"""
        def new(self, *args, **kargs):
            self.redrawLevels += 1
            try:
                ans = fn(self,*args, **kargs)
            finally:
                self.redrawLevels -= 1

                if self.redrawLevels == 0:
                    self.redraw()

            return ans
        return new
    #@-node:tbrown.20090119215428.13:redrawer
    #@+node:tbrown.20090119215428.14:projectChanger
    def projectChanger(fn):
        """decorator for methods which change projects"""
        def new(self, *args, **kargs):
            ans = fn(self,*args, **kargs)
            self.update_project()
            return ans
        return new
    #@nonl
    #@-node:tbrown.20090119215428.14:projectChanger
    #@+node:tbrown.20090119215428.15:loadAllIcons
    @redrawer
    def loadAllIcons(self, tag=None, k=None, clear=None):
        """Load icons to represent cleo state"""

        for p in self.c.all_positions():
            self.loadIcons(p, clear=clear)
    #@-node:tbrown.20090119215428.15:loadAllIcons
    #@+node:tbrown.20090119215428.16:loadIcons
    @redrawer
    def loadIcons(self, p, clear=False):

        com = self.c.editCommands
        allIcons = com.getIconList(p)
        icons = [i for i in allIcons if 'cleoIcon' not in i]

        if clear:
            iterations = []
        else:
            iterations = [True, False]

        for which in iterations:

            if which == (self.icon_order == 'pri-first'):
                pri = self.getat(p.v, 'priority')
                if pri: pri = int(pri)
                if pri in self.priorities:
                    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',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
                    com.setIconList(p, icons)
            else:

                prog = self.getat(p.v, 'progress')
                if prog is not '':
                    prog = int(prog)
                    use = prog//10*10
                    use = 'prg%03d.png' % use

                    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',use),
                        2, on='vnode', cleoIcon='1', where=self.prog_location)
                    com.setIconList(p, icons)

        if len(allIcons) != len(icons):  # something to add / remove
            com.setIconList(p, icons)

    #@-node:tbrown.20090119215428.16:loadIcons
    #@+node:tbrown.20090119215428.17: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.20090119215428.17:close
    #@+node:tbrown.20090119215428.18:showHelp
    def showHelp(self):
        g.es('Check the Plugins menu Todo entry')
    #@nonl
    #@-node:tbrown.20090119215428.18:showHelp
    #@+node:tbrown.20090119215428.19:attributes...
    #@+at
    # annotate was the previous name of this plugin, which is why the default 
    # values
    # for several keyword args is 'annotate'.
    #@-at
    #@nonl
    #@+node:tbrown.20090119215428.20:delUD
    def delUD (self,node,udict="annotate"):

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

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

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

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

        return (
            hasattr(node,"unknownAttributes") and
            udict in node.unknownAttributes and
            type(node.unknownAttributes.get(udict)) == type({}) # EKR
        )
    #@nonl
    #@-node:tbrown.20090119215428.21:hasUD
    #@+node:tbrown.20090119215428.22:getat
    def getat(self, node, attrib):
        "new attrbiute getter"

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

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

        x = node.unknownAttributes["annotate"][attrib]
        return x
    #@nonl
    #@-node:tbrown.20090119215428.22:getat
    #@+node:tbrown.20090119215428.23:testDefault
    def testDefault(self, attrib, val):
        "return true if val is default val for attrib"

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

        isDefault = self.testDefault(attrib, val)

        if (not hasattr(node,'unknownAttributes') or
            "annotate" not in node.unknownAttributes 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 ("annotate" not in node.unknownAttributes 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.20090119215428.24:setat
    #@+node:tbrown.20090119215428.25:dropEmpty
    def dropEmpty(self, node, dictOk = False):

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

            isDefault = True
            for ky, vl in node.unknownAttributes["annotate"].items():

                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.20090119215428.25:dropEmpty
    #@+node:tbrown.20090119215428.26:safe_del
    def safe_del(self, d, k):
        "delete a key from a dict. if present"
        if k in d: del d[k]
    #@nonl
    #@-node:tbrown.20090119215428.26:safe_del
    #@-node:tbrown.20090119215428.19:attributes...
    #@+node:tbrown.20090119215428.27:drawing...
    #@+node:tbrown.20090119215428.28:redraw
    def redraw(self):

        self.updateUI()
        self.c.redraw_now()
    #@-node:tbrown.20090119215428.28:redraw
    #@+node:tbrown.20090119215428.29:clear_all
    @redrawer
    def clear_all(self, recurse=False, all=False):

        if all:
            what = self.c.all_positions()
        elif recurse:
            what = self.c.currentPosition().self_and_subtree()
        else:
            what = iter([self.c.currentPosition()])

        for p in what:
            self.delUD(p.v)
            self.loadIcons(p)
            self.show_times(p)

    #@-node:tbrown.20090119215428.29:clear_all
    #@-node:tbrown.20090119215428.27:drawing...
    #@+node:tbrown.20090119215428.30:Progress/time/project...
    #@+node:tbrown.20090119215428.31:progress_clear
    @redrawer
    @projectChanger
    def progress_clear(self,v=None):

        self.setat(self.c.currentPosition().v, 'progress', '')
    #@-node:tbrown.20090119215428.31:progress_clear
    #@+node:tbrown.20090119215428.32:set_progress
    @redrawer
    @projectChanger
    def set_progress(self,p=None, val=None):
        if p is None:
            p = self.c.currentPosition()
        v = p.v

        if val == None: return

        self.setat(v, 'progress', val)
    #@-node:tbrown.20090119215428.32:set_progress
    #@+node:tbrown.20090119215428.33:set_time_req
    @redrawer
    @projectChanger
    def set_time_req(self,p=None, val=None):
        if p is None:
            p = self.c.currentPosition()
        v = p.v

        if val == None: return

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

        if self.getat(v, 'progress') == '':
            self.setat(v, 'progress', 0)
    #@-node:tbrown.20090119215428.33:set_time_req
    #@+node:tbrown.20090119215428.34:show_times
    @redrawer
    def show_times(self, p=None, show=False):

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

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

        for nd in p.self_and_subtree():
            self.c.setHeadString(nd, re.sub(' <[^>]*>$', '', nd.headString()))

            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 show:
                    self.c.setHeadString(nd, nd.headString()+ans)
                self.loadIcons(nd)  # update progress icon

    #@-node:tbrown.20090119215428.34:show_times
    #@+node:tbrown.20090119215428.35:recalc_time
    def recalc_time(self, p=None, clear=False):

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

        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
                if time_totl == 0:
                    pr = 0.
                else:
                    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)

        return (time_totl, time_done)
    #@-node:tbrown.20090119215428.35:recalc_time
    #@+node:tbrown.20090119215428.36:clear_time_req
    @redrawer
    @projectChanger
    def clear_time_req(self, p=None):

        if p is None:
            p = self.c.currentPosition()
        v = p.v
        self.setat(v, 'time_req', '')
    #@-node:tbrown.20090119215428.36:clear_time_req
    #@+node:tbrown.20090119215428.37:update_project
    @redrawer
    def update_project(self, p=None):
        """Find highest parent with '@project' in headline and run recalc_time
        and maybe show_times (if headline has '@project time')"""

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

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

        if project:
            self.recalc_time(project)
            if project.headString().find('@project time') > -1:
                self.show_times(project, show=True)
            else:
                self.show_times(p, show=True)
        else:
            self.show_times(p, show=False)
    #@-node:tbrown.20090119215428.37:update_project
    #@+node:tbrown.20090119215428.38:local_recalc
    @redrawer
    def local_recalc(self, p=None):
        self.recalc_time(p)
    #@-node:tbrown.20090119215428.38:local_recalc
    #@+node:tbrown.20090119215428.39:local_clear
    @redrawer
    def local_clear(self, p=None):
        self.recalc_time(p, clear=True)
    #@-node:tbrown.20090119215428.39:local_clear
    #@-node:tbrown.20090119215428.30:Progress/time/project...
    #@+node:tbrown.20090119215428.40:ToDo icon related...
    #@+node:tbrown.20090119215428.41:childrenTodo
    @redrawer
    def childrenTodo(self, p=None):
        if p is None:
            p = self.c.currentPosition()
        for p in p.children():
            if self.getat(p.v, 'priority') != 9999: continue
            self.setat(p.v, 'priority', 19)
            self.loadIcons(p)
    #@nonl
    #@-node:tbrown.20090119215428.41:childrenTodo
    #@+node:tbrown.20090119215428.42:find_todo
    @redrawer
    def find_todo(self, p=None, 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 p is None:
            p = self.c.currentPosition()

        # 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)
            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.20090119215428.42:find_todo
    #@+node:tbrown.20090119215428.43:prikey
    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
    #@nonl
    #@-node:tbrown.20090119215428.43:prikey
    #@+node:tbrown.20090119215428.44:priority_clear
    @redrawer
    def priority_clear(self,v=None):

        if v is None:
            v = self.c.currentPosition().v
        self.setat(v, 'priority', 9999)
        self.loadIcons(self.c.currentPosition())
    #@-node:tbrown.20090119215428.44:priority_clear
    #@+node:tbrown.20090119215428.45:priSort
    @redrawer
    def priSort(self, p=None):
        if p is None:
            p = self.c.currentPosition()
        self.c.selectPosition(p)
        self.c.sortSiblings(key=self.prikey)
    #@nonl
    #@-node:tbrown.20090119215428.45:priSort
    #@+node:tbrown.20090119215428.46:reclassify
    @redrawer
    def reclassify(self, p=None):
        """change priority codes"""

        if p is None:
            p = self.c.currentPosition()
        g.es('\n Current distribution:')
        self.showDist()
        dat = {}
        for end in 'from', 'to':
            if Qt:
                x0,ok = QtGui.QInputDialog.getText(None, 'Reclassify priority' ,'%s priorities (1-9,19)'%end)
                if not ok:
                    x0 = None
                else:
                    x0 = str(x0)
            else:
                x0 = g.app.gui.runAskOkCancelStringDialog(
                    self.c,'Reclassify priority' ,'%s priorities (1-7,19)' % end.upper())
            try:
                while re.search(r'\d+-\d+', x0):
                    what = re.search(r'\d+-\d+', x0).group(0)
                    rng = [int(n) for n in what.split('-')]
                    repl = []
                    if rng[0] > rng[1]:
                        for n in range(rng[0], rng[1]-1, -1):
                            repl.append(str(n))
                    else:
                        for n in range(rng[0], rng[1]+1):
                            repl.append(str(n))
                    x0 = x0.replace(what, ','.join(repl))

                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 p.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()
    #@-node:tbrown.20090119215428.46:reclassify
    #@+node:tbrown.20090119215428.47:setPri
    @redrawer
    def setPri(self,pri):

        if pri in self.recentIcons:
            self.recentIcons.remove(pri)
        self.recentIcons.insert(0, pri)
        self.recentIcons = self.recentIcons[:3]

        p = self.c.currentPosition()
        self.setat(p.v, 'priority', pri)
        self.loadIcons(p)
    #@-node:tbrown.20090119215428.47:setPri
    #@+node:tbrown.20090119215428.48:showDist
    def showDist(self, p=None):
        """show distribution of priority levels in subtree"""
        if p is None:
            p = self.c.currentPosition()
        pris = {}
        for p in p.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.items()]) 
        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']))
    #@nonl
    #@-node:tbrown.20090119215428.48:showDist
    #@-node:tbrown.20090119215428.40:ToDo icon related...
    #@+node:tbrown.20090119215428.49:updateUI
    def updateUI(self,tag=None,k=None):

        if k and k['c'] != self.c:
            return  # wrong number

        v = self.c.currentPosition().v
        self.ui.setProgress(int(self.getat(v, 'progress') or 0 ))
        self.ui.setTime(float(self.getat(v, 'time_req') or 0 ))
    #@-node:tbrown.20090119215428.49:updateUI
    #@-others
#@-node:tbrown.20090119215428.9:class todoController
#@-others
#@nonl
#@-node:tbrown.20090119215428.2:@thin todo.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.