PyXR

c:\projects\bitpim\src \ todo.py



0001 ### BITPIM
0002 ###
0003 ### Copyright (C) 2005 Joe Pham <djpham@netzero.net>
0004 ###
0005 ### This program is free software; you can redistribute it and/or modify
0006 ### it under the terms of the BitPim license as detailed in the LICENSE file.
0007 ###
0008 ### $Id: todo.py 4459 2007-11-24 07:55:33Z djpham $
0009 
0010 """
0011 Code to handle Todo Items
0012 
0013 The format for the Todo items is standardized.  It is a dict with the following
0014 fields:
0015 
0016 TodoEntry properties:
0017 summary - 'string subject'
0018 note - 'string note'
0019 due_date - 'YYYYMMDD'
0020 status - (None, NotStarted, InProgress, NeedActions, Completed, Cancelled)
0021 percent_complete - None, range(101)
0022 completion_date - 'YYYYMMDD'
0023 categories - [{ 'category': string }]
0024 private - True/<False|None>
0025 priority - range(1, 11) 1=Highest, 5=Normal, 10=Lowest
0026 
0027 TodoEntry Methods:
0028 get() - return a copy of the internal dict
0029 set(dict) - set the internal dict
0030 check_completion() - check the task for completion and if so set appropriate
0031                      values
0032 completion() - set the task as completed and set appropriate values
0033 
0034 To implement Todo read/write for a phone module:
0035  Add 2 entries into Profile._supportedsyncs:
0036         ...
0037         ('todo', 'read', None),     # all todo reading
0038         ('todo', 'write', 'OVERWRITE')  # all todo writing
0039 
0040 implement the following 2 methods in your Phone class:
0041     def gettodo(self, result):
0042         ...
0043         return result
0044 
0045     def savetodo(self, result, merge):
0046         ...
0047         return result
0048 
0049 The result dict key is 'todo'.
0050 
0051 """
0052 
0053 # standard modules
0054 from __future__ import with_statement
0055 import copy
0056 import datetime
0057 import time
0058 
0059 # wx modules
0060 import wx
0061 import wx.lib.calendar
0062 import wx.calendar as cal
0063 import wx.lib.scrolledpanel as scrolled
0064 
0065 # BitPim modules
0066 import calendarentryeditor as cal_editor
0067 import database
0068 import guihelper
0069 import helpids
0070 import field_color
0071 import phonebookentryeditor as pb_editor
0072 import pubsub
0073 import today
0074 import guihelper
0075 import widgets
0076 
0077 widgets_list=[]
0078 
0079 #-------------------------------------------------------------------------------
0080 class TodoDataObject(database.basedataobject):
0081     _knownproperties=['summary', 'note', 'due_date', 'status',
0082                       'percent_complete', 'completion_date', 'priority' ]
0083     _knownlistproperties=database.basedataobject._knownlistproperties.copy()
0084     _knownlistproperties.update( {'categories': ['category'],
0085                                   'flags': ['secret'] })
0086 
0087     def __init__(self, data=None):
0088         if data is None or not isinstance(data, TodoEntry):
0089             return;
0090         self.update(data.get_db_dict())
0091 todoobjectfactory=database.dataobjectfactory(TodoDataObject)
0092 
0093 #-------------------------------------------------------------------------------
0094 class TodoEntry(object):
0095     ST_NotStarted=1
0096     ST_InProgress=2
0097     ST_NeedActions=3
0098     ST_Completed=4
0099     ST_Cancelled=5
0100     ST_Last=6
0101     ST_Range=xrange(ST_NotStarted, ST_Last)
0102     ST_Names=(
0103         '<None>', 'Not Started', 'In Progess', 'Need Actions',
0104         'Completed', 'Cancelled', 'LAST')
0105     PC_Range=xrange(101)  # % Complete: 0-100%
0106     PR_Range=xrange(1, 11)
0107     _id_index=0
0108     _max_id_index=999
0109 
0110     def __init__(self):
0111         self._data={ 'serials': [] }
0112         self._create_id()
0113 
0114     def get(self):
0115         return copy.deepcopy(self._data, {})
0116     def set(self, d):
0117         self._data={}
0118         self._data.update(d)
0119 
0120     def get_db_dict(self):
0121         return self.get()
0122     def set_db_dict(self, d):
0123         self.set(d)
0124 
0125     def complete(self):
0126         # complete this task: set relevant values to indicate so
0127         if self.status != self.ST_Completed:
0128             self.status=self.ST_Completed
0129         if self.percent_complete != 100:
0130             self.percent_complete=100
0131         if not len(self.completion_date):
0132             self.completion_date=datetime.date.today().strftime('%Y%m%d')
0133 
0134     def check_completion(self):
0135         if self.status==self.ST_Completed or self.percent_complete==100 or \
0136            len(self.completion_date):
0137             self.complete()
0138 
0139     def _set_or_del(self, key, v, v_list=[]):
0140         if v is None or v in v_list:
0141             if self._data.has_key(key):
0142                 del self._data[key]
0143         else:
0144             self._data[key]=v
0145 
0146     def _create_id(self):
0147         "Create a BitPim serial for this entry"
0148         self._data.setdefault("serials", []).append(\
0149             {"sourcetype": "bitpim",
0150              "id": '%.3f%03d'%(time.time(), TodoEntry._id_index) })
0151         if TodoEntry._id_index<TodoEntry._max_id_index:
0152             TodoEntry._id_index+=1
0153         else:
0154             TodoEntry._id_index=0
0155     def _get_id(self):
0156         s=self._data.get('serials', [])
0157         for n in s:
0158             if n.get('sourcetype', None)=='bitpim':
0159                 return n.get('id', None)
0160         return None
0161     id=property(fget=_get_id)
0162 
0163     def _get_summary(self):
0164         return self._data.get('summary', '')
0165     def _set_summary(self, v):
0166         self._set_or_del('summary', v, [''])
0167     summary=property(fget=_get_summary, fset=_set_summary)
0168 
0169     def _get_note(self):
0170         return self._data.get('note', '')
0171     def _set_note(self, v):
0172         self._set_or_del('note', v, [''])
0173     note=property(fget=_get_note, fset=_set_note)
0174 
0175     def _get_due_date(self):
0176         return self._data.get('due_date', '')
0177     def _set_due_date(self, v):
0178         self._set_or_del('due_date', v, [''])
0179     due_date=property(fget=_get_due_date, fset=_set_due_date)
0180 
0181     def _get_status(self):
0182         return self._data.get('status', None)
0183     def _set_status(self, v):
0184         if v is not None and v not in self.ST_Range:
0185             raise ValueError, 'Illegal Status Value'
0186         self._set_or_del('status', v, [])
0187         if v==self.ST_Completed:
0188             self.complete()
0189     status=property(fget=_get_status, fset=_set_status)
0190     def is_active(self):
0191         _status=self.status
0192         return _status!=self.ST_Completed and _status!=self.ST_Cancelled
0193 
0194     def _get_percent_complete(self):
0195         return self._data.get('percent_complete', None)
0196     def _set_percent_complete(self, v):
0197         if v is not None and v not in self.PC_Range:
0198             raise ValueError, 'Illegal Percent Complete Value'
0199         self._set_or_del('percent_complete', v, [])
0200         if v==100:
0201             self.complete()
0202     percent_complete=property(fget=_get_percent_complete,
0203                               fset=_set_percent_complete)
0204 
0205     def _get_completion_date(self):
0206         return self._data.get('completion_date', '')
0207     def _set_completion_date(self, v):
0208         self._set_or_del('completion_date', v, [''])
0209         if v is not None and len(v):
0210             self.complete()
0211     completion_date=property(fget=_get_completion_date,
0212                              fset=_set_completion_date)
0213 
0214     def _get_priority(self):
0215         return self._data.get('priority', None)
0216     def _set_priority(self, v):
0217         if v is not None and v not in self.PR_Range:
0218             raise ValueError, 'Illegal priority value'
0219         self._set_or_del('priority', v, [])
0220     priority=property(fget=_get_priority, fset=_set_priority)
0221 
0222     def _get_categories(self):
0223         return self._data.get('categories', [])
0224     def _set_categories(self, s):
0225         self._set_or_del('categories', s,[])
0226         if not s and self._data.has_key('categories'):
0227             del self._data['categories']
0228     categories=property(fget=_get_categories, fset=_set_categories)
0229 
0230     def _get_secret(self):
0231         f=self._data.get('flags', [])
0232         for n in f:
0233             if n.has_key('secret'):
0234                 return n['secret']
0235         return False
0236     def _set_secret(self, v):
0237         f=self._data.get('flags', [])
0238         for i, n in enumerate(f):
0239             if n.has_key('secret'):
0240                 if v is None or not v:
0241                     del f[i]
0242                     if not self._data['flags']:
0243                         del self._data['flags']
0244                 else:
0245                     n['secret']=v
0246                 return
0247         if v is not None and v:
0248             self._data.setdefault('flags', []).append({'secret': v})
0249     private=property(fget=_get_secret, fset=_set_secret)
0250 
0251 #-------------------------------------------------------------------------------
0252 class StatusComboBox(wx.ComboBox):
0253     def __init__(self, parent, _=None):
0254         self._choices=[TodoEntry.ST_Names[x] for x in range(TodoEntry.ST_Last)]
0255         super(StatusComboBox, self).__init__(parent, -1,
0256                                              self._choices[0],
0257                                               (-1, -1), (-1, -1),
0258                                               self._choices, wx.CB_READONLY)
0259         wx.EVT_COMBOBOX(self, self.GetId(), parent.OnMakeDirty)
0260     def GetValue(self):
0261         s=super(StatusComboBox, self).GetValue()
0262         for v,n in enumerate(self._choices):
0263             if n==s:
0264                 break;
0265         if v:
0266             return v
0267         else:
0268             return None
0269     def SetValue(self, v):
0270         if v is None:
0271             v=0
0272         super(StatusComboBox, self).SetValue(self._choices[v])
0273 
0274 #-------------------------------------------------------------------------------
0275 class PercentCompleteBox(wx.ComboBox):
0276     def __init__(self, parent, _=None):
0277         self. _choices=['<None>', '0%', '10%', '20%', '30%', '40%',
0278                  '50%', '60%', '70%', '80%', '90%', '100%']
0279         super(PercentCompleteBox, self).__init__(parent, -1, self._choices[0],
0280                                                  (-1,-1), (-1,-1),
0281                                                  self._choices,  wx.CB_READONLY)
0282         wx.EVT_COMBOBOX(self, self.GetId(), parent.OnMakeDirty)
0283     def GetValue(self):
0284         s=super(PercentCompleteBox, self).GetValue()
0285         for v,n in enumerate(self._choices):
0286             if n==s:
0287                 break
0288         if v:
0289             return (v-1)*10
0290         else:
0291             return None
0292     def SetValue(self, v):
0293         if v is None:
0294             v=0
0295         else:
0296             v=(v/10)+1
0297         super(PercentCompleteBox, self).SetValue(self._choices[v])
0298 
0299 #-------------------------------------------------------------------------------
0300 class PriorityBox(wx.ComboBox):
0301     def __init__(self, parent, _= None):
0302         self._choices=['<None>', '1 - Highest', '2', '3', '4', '5 - Normal',
0303                  '6', '7', '8', '9', '10 - Lowest']
0304         super(PriorityBox, self).__init__(parent, -1, self._choices[0],
0305                                               (-1, -1), (-1, -1),
0306                                               self._choices, wx.CB_READONLY)
0307         wx.EVT_COMBOBOX(self, self.GetId(), parent.OnMakeDirty)
0308     def GetValue(self):
0309         s=super(PriorityBox, self).GetValue()
0310         for v,n in enumerate(self._choices):
0311             if n==s:
0312                 break
0313         if v:
0314             return v
0315         else:
0316             return None
0317     def SetValue(self, v):
0318         if v is None:
0319             v=0
0320         super(PriorityBox, self).SetValue(self._choices[v])
0321 
0322 #-------------------------------------------------------------------------------
0323 class DateControl(wx.Panel, widgets.BitPimWidget):
0324     def __init__(self, parent, _=None):
0325         super(DateControl, self).__init__(parent, -1)
0326         self._dt=None
0327         # main box sizer, a label, and a button
0328         self._hs=wx.BoxSizer(wx.HORIZONTAL)
0329         self._date_str=wx.StaticText(self, -1, '<None>')
0330         self._date_btn=wx.Button(self, -1, 'Set Date')
0331         self._hs.Add(self._date_str, 0, wx.EXPAND|wx.LEFT|wx.RIGHT, 5)
0332         self._hs.Add(self._date_btn, 0, wx.EXPAND|wx.LEFT|wx.RIGHT, 5)
0333         # events
0334         wx.EVT_BUTTON(self, self._date_btn.GetId(), self._OnSetDate)
0335         # all done
0336         self.SetSizer(self._hs)
0337         self.SetAutoLayout(True)
0338     def _refresh(self):
0339         if self._dt is None:
0340             s='<None>'
0341         else :
0342             s=self._dt.strftime('%Y-%m-%d')
0343         self._date_str.SetLabel(s)
0344         self._hs.Layout()
0345         self.GetParent().OnMakeDirty(None)
0346     def _OnSetDate(self, _):
0347         # bring up a calendar dlg
0348         if self._dt is None:
0349             dt=datetime.date.today()
0350         else:
0351             dt=self._dt
0352         with guihelper.WXDialogWrapper(wx.lib.calendar.CalenDlg(self,
0353                                                                 month=dt.month,
0354                                                                 day=dt.day,
0355                                                                 year=dt.year)) as dlg:
0356             dlg.Centre()
0357             if dlg.ShowModal() == wx.ID_OK:
0358                 self._dt=datetime.date(dlg.calend.GetYear(),
0359                                         dlg.calend.GetMonth(),
0360                                         dlg.calend.GetDay())
0361                 self._refresh()
0362     def SetValue(self, v):
0363         # set a date string from the dict
0364         if v is None or not len(v):
0365             self._dt=None
0366         else:
0367             self._dt=datetime.date(int(v[:4]), int(v[4:6]), int(v[6:]))
0368         self._refresh()
0369     def GetValue(self):
0370         # return a date string YYYYMMDD
0371         if self._dt is None:
0372             return ''
0373         return self._dt.strftime('%Y%m%d')
0374 
0375 #-------------------------------------------------------------------------------
0376 class DirtyCheckBox(wx.CheckBox):
0377     def __init__(self, parent, _=None):
0378         super(DirtyCheckBox, self).__init__(parent, -1)
0379         wx.EVT_CHECKBOX(self, self.GetId(), parent.OnMakeDirty)
0380 
0381 #-------------------------------------------------------------------------------
0382 class GeneralEditor(pb_editor.DirtyUIBase):
0383     _dict_key_index=0
0384     _label_index=1
0385     _class_index=2
0386     _get_index=3
0387     _set_index=4
0388     _w_index=5
0389     _flg_index=6
0390 
0391     def __init__(self, parent, _=None):
0392         global widgets_list
0393 
0394         super(GeneralEditor, self).__init__(parent)
0395         self._fields=[
0396             ['summary', 'Summary:', cal_editor.DVTextControl, None, None, None, wx.EXPAND],
0397             ['status', 'Status:', StatusComboBox, None, None, None, 0],
0398             ['due_date', 'Due Date:', DateControl, None, None, None, wx.EXPAND],
0399             ['percent_complete', '% Complete:', PercentCompleteBox, None, None, None, 0],
0400             ['completion_date', 'Completion Date:', DateControl, None, None, None, wx.EXPAND],
0401             ['private', 'Private:', DirtyCheckBox, None, None, None, 0],
0402             ['priority', 'Priority:', PriorityBox, None, None, None, 0]
0403             ]
0404         gs=wx.FlexGridSizer(-1, 2, 5, 5)
0405         gs.AddGrowableCol(1)
0406         for n in self._fields:
0407             _txt=wx.StaticText(self, -1, n[self._label_index],
0408                                style=wx.ALIGN_LEFT)
0409             widgets_list.append((_txt, n[self._dict_key_index]))
0410             gs.Add(_txt, 0, wx.EXPAND|wx.BOTTOM, 0)
0411             w=n[self._class_index](self, -1)
0412             gs.Add(w, 0, n[self._flg_index]|wx.BOTTOM, 5)
0413             n[self._w_index]=w
0414         # event handlers
0415         # all done
0416         self.SetSizer(gs)
0417         self.SetAutoLayout(True)
0418         gs.Fit(self)
0419 
0420     def OnMakeDirty(self, evt):
0421         self.OnDirtyUI(evt)
0422 
0423     def Set(self, data):
0424         self.ignore_dirty=True
0425         if data is None:
0426             for n in self._fields:
0427                 n[self._w_index].Enable(False)
0428         else:
0429             for n in self._fields:
0430                 w=n[self._w_index]
0431                 w.Enable(True)
0432                 w.SetValue(getattr(data, n[self._dict_key_index]))
0433         self.ignore_dirty=self.dirty=False
0434 
0435     def Get(self, data):
0436         self.ignore_dirty=self.dirty=False
0437         if data is None:
0438             return
0439         for n in self._fields:
0440             w=n[self._w_index]
0441             v=w.GetValue()
0442 ##            if v is not None:
0443             setattr(data, n[self._dict_key_index], v)
0444 
0445 #-------------------------------------------------------------------------------
0446 class TodoWidget(wx.Panel, widgets.BitPimWidget):
0447     color_field_name='todo'
0448 
0449     def __init__(self, mainwindow, parent):
0450         global widgets_list
0451 
0452         super(TodoWidget, self).__init__(parent, -1)
0453         self._main_window=mainwindow
0454         self._data=self._data_map={}
0455         # main box sizer
0456         vbs=wx.BoxSizer(wx.VERTICAL)
0457         # horizontal sizer for the listbox and tabs
0458         hbs=wx.BoxSizer(wx.HORIZONTAL)
0459         # the list box
0460         self._item_list=wx.ListBox(self, wx.NewId(),
0461                                     style=wx.LB_SINGLE|wx.LB_HSCROLL|wx.LB_NEEDED_SB)
0462         hbs.Add(self._item_list, 1, wx.EXPAND|wx.BOTTOM, border=5)
0463         # the detailed info pane as a scrolled panel
0464         scrolled_panel=scrolled.ScrolledPanel(self, -1)
0465         vbs1=wx.BoxSizer(wx.VERTICAL)
0466         self._items=(
0467             (GeneralEditor, 0, None),
0468             (cal_editor.CategoryEditor, 1, 'category'),
0469             (pb_editor.MemoEditor, 1, 'memo')
0470             )
0471         self._w=[]
0472         for n in self._items:
0473             w=n[0](scrolled_panel, -1)
0474             vbs1.Add(w, n[1], wx.EXPAND|wx.ALL, 5)
0475             self._w.append(w)
0476             if n[2]:
0477                 widgets_list.append((w.static_box, n[2]))
0478         scrolled_panel.SetSizer(vbs1)
0479         scrolled_panel.SetAutoLayout(True)
0480         vbs1.Fit(scrolled_panel)
0481         scrolled_panel.SetupScrolling()
0482         hbs.Add(scrolled_panel, 3, wx.EXPAND|wx.ALL, border=5)
0483         # save references to the widgets
0484         self._general_editor_w=self._w[0]
0485         self._cat_editor_w=self._w[1]
0486         self._memo_editor_w=self._w[2]
0487         # the bottom buttons
0488         hbs1=wx.BoxSizer(wx.HORIZONTAL)
0489         self._save_btn=wx.Button(self, wx.ID_SAVE)
0490         self._revert_btn=wx.Button(self, wx.ID_REVERT_TO_SAVED)
0491         help_btn=wx.Button(self, wx.ID_HELP)
0492         hbs1.Add(self._save_btn, 0, wx.ALIGN_CENTRE|wx.ALL, 5)
0493         hbs1.Add(help_btn, 0, wx.ALIGN_CENTRE|wx.ALL, 5)
0494         hbs1.Add(self._revert_btn, 0, wx.ALIGN_CENTRE|wx.ALL, 5)
0495         # all done
0496         vbs.Add(hbs, 1, wx.EXPAND|wx.ALL, 5)
0497         vbs.Add(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 5)
0498         vbs.Add(hbs1, 0, wx.ALIGN_CENTRE|wx.ALL, 5)
0499         self.SetSizer(vbs)
0500         self.SetAutoLayout(True)
0501         vbs.Fit(self)
0502         # event handlers
0503         wx.EVT_LISTBOX(self, self._item_list.GetId(), self._OnListBoxItem)
0504         wx.EVT_BUTTON(self, self._save_btn.GetId(), self._OnSave)
0505         wx.EVT_BUTTON(self, self._revert_btn.GetId(), self._OnRevert)
0506         wx.EVT_BUTTON(self, wx.ID_HELP,
0507                       lambda _: wx.GetApp().displayhelpid(helpids.ID_TAB_TODO))
0508         # DIRTY UI Event handlers
0509         for w in self._w:
0510             pb_editor.EVT_DIRTY_UI(self, w.GetId(), self.OnMakeDirty)
0511         # populate data
0512         self._populate()
0513         # turn on dirty flag
0514         self.ignoredirty=False
0515         self.setdirty(False)
0516         # register for Today selection
0517         today.bind_notification_event(self.OnTodaySelection,
0518                                       today.Today_Group_Todo)
0519         today.bind_request_event(self.OnTodayRequest)
0520         # color coded labels
0521         field_color.reload_color_info(self, widgets_list)
0522         pubsub.subscribe(self.OnPhoneChanged, pubsub.PHONE_MODEL_CHANGED)
0523 
0524     def OnPhoneChanged(self, _):
0525         # just reload the color info based on the new phone
0526         field_color.reload_color_info(self, widgets_list)
0527         self.Refresh()
0528 
0529     def _clear(self):
0530         self._item_list.Clear()
0531         self._clear_each()
0532 
0533     def _clear_each(self):
0534         for w in self._w:
0535             w.Set(None)
0536             w.Enable(False)
0537         self.Refresh()
0538 
0539     def _publish_today_events(self):
0540         now=datetime.datetime.now()
0541         _today='%04d%02d%02d'%(now.year, now.month, now.day)
0542         keys=self._data.keys()
0543         keys.sort()
0544         today_event=today.TodayTodoEvent()
0545         for k in keys:
0546             if self._data[k].is_active() and \
0547                (not self._data[k].due_date or \
0548                 self._data[k].due_date<=_today):
0549                 today_event.append(self._data[k].summary,
0550                                    { 'key': k,
0551                                      'index': self._data_map[k] })
0552         today_event.broadcast()
0553 
0554     def _publish_thisweek_events(self):
0555         now=datetime.datetime.now()
0556         _today='%04d%02d%02d'%(now.year, now.month, now.day)
0557         s=now+datetime.timedelta(7-now.isoweekday()%7)
0558         _sun='%04d%02d%02d'%(s.year, s.month, s.day)
0559         keys=self._data.keys()
0560         keys.sort()
0561         today_event=today.ThisWeekTodoEvent()
0562         dow_flg=[False]*7
0563         for k in keys:
0564             due_date=self._data[k].due_date
0565             if due_date>_today and due_date<_sun:
0566                 dt=datetime.datetime(int(due_date[:4]), int(due_date[4:6]),
0567                                          int(due_date[6:8]))
0568                 _dow=dt.isoweekday()%7
0569                 if dow_flg[_dow]:
0570                     _name=today.dow_initials[-1]+'   '+self._data[k].summary
0571                 else:
0572                     dow_flg[_dow]=True
0573                     _name=today.dow_initials[_dow]+' - '+self._data[k].summary
0574                 today_event.append(_name, { 'key': k,
0575                                             'index': self._data_map[k] })
0576         today_event.broadcast()
0577 
0578     def OnTodayRequest(self, _):
0579         self._publish_today_events()
0580         self._publish_thisweek_events()
0581 
0582     def OnTodaySelection(self, evt):
0583         self.ActivateSelf()
0584         if evt.data:
0585             self._item_list.SetSelection(evt.data.get('index', wx.NOT_FOUND))
0586             self._populate_each(evt.data.get('key', None))
0587 
0588     def _populate(self):
0589         # populate new data
0590         self._clear()
0591         self._data_map={}
0592         # populate the list with data
0593         keys=self._data.keys()
0594         keys.sort()
0595         for k in keys:
0596             n=self._data[k]
0597             i=self._item_list.Append(n.summary)
0598             self._item_list.SetClientData(i, k)
0599             self._data_map[k]=i
0600         self._publish_today_events()
0601         self._publish_thisweek_events()
0602 
0603     def _populate_each(self, k):
0604         # populate the detailed info of the item keyed k
0605         if k is None:
0606             # clear out all the subfields
0607             self._clear_each()
0608             return
0609         # there're data, first enable the widgets
0610         self.ignoredirty=True
0611         for w in self._w:
0612             w.Enable(True)
0613         entry=self._data[k]
0614         # set the general detail
0615         self._general_editor_w.Set(entry)
0616         self._cat_editor_w.Set(entry.categories)
0617         self._memo_editor_w.Set({ 'memo': entry.note })
0618         self.ignoredirty=False
0619         self.setdirty(False)
0620         
0621     # called from various widget update callbacks
0622     def OnMakeDirty(self, _=None):
0623         """A public function you can call that will set the dirty flag"""
0624         if self.dirty or self.ignoredirty or not self.IsShown():
0625             # already dirty, no need to make it worse
0626             return
0627         self.setdirty(True)
0628 
0629     def setdirty(self, val):
0630         """Set the dirty flag"""
0631         if self.ignoredirty:
0632             return
0633         self.dirty=val
0634         self._item_list.Enable(not self.dirty)
0635         self._save_btn.Enable(self.dirty)
0636         self._revert_btn.Enable(self.dirty)
0637 
0638     def GetDeleteInfo(self):
0639         return guihelper.ART_DEL_TODO, "Delete Todo Item"
0640 
0641     def GetAddInfo(self):
0642         return guihelper.ART_ADD_TODO, "Add Todo Item"
0643 
0644     def CanAdd(self):
0645         if self.dirty:
0646             return False
0647         return True
0648 
0649     def OnAdd(self, _):
0650         # add a new memo item
0651         if self.dirty:
0652             # busy editing, cannot add now, just return
0653             return
0654         m=TodoEntry()
0655         m.summary='New Task'
0656         self._data[m.id]=m
0657         self._populate()
0658         self._save_to_db(self._data)
0659         self._item_list.Select(self._data_map[m.id])
0660         self._populate_each(m.id)
0661 
0662     def CanDelete(self):
0663         sel_idx=self._item_list.GetSelection()
0664         if sel_idx is None or sel_idx==-1:
0665             # none selected
0666             return False
0667         return True
0668 
0669     def OnDelete(self, _):
0670         # delete the current selected item
0671         sel_idx=self._item_list.GetSelection()
0672         if sel_idx is None or sel_idx==-1:
0673             # none selected
0674             return
0675         self.ignoredirty=True
0676         k=self._item_list.GetClientData(sel_idx)
0677         self._item_list.Delete(sel_idx)
0678         self._clear_each()
0679         del self._data[k]
0680         del self._data_map[k]
0681         self._save_to_db(self._data)
0682         self.ignoredirty=False
0683         self.setdirty(False)
0684 
0685     def getdata(self,dict,want=None):
0686         dict['todo']=copy.deepcopy(self._data)
0687         return dict
0688 
0689     def populate(self, dict):
0690         self._data=dict.get('todo', {})
0691         self._populate()
0692 
0693     def _save_to_db(self, todo_dict):
0694         db_rr={}
0695         for k, e in todo_dict.items():
0696             db_rr[k]=TodoDataObject(e)
0697         database.ensurerecordtype(db_rr, todoobjectfactory)
0698         self._main_window.database.savemajordict('todo', db_rr)
0699         self._publish_today_events()
0700         self._publish_thisweek_events()
0701         
0702     def populatefs(self, dict):
0703         self._save_to_db(dict.get('todo', {}))
0704         return dict
0705 
0706     def getfromfs(self, result):
0707         # read data from the database
0708         todo_dict=self._main_window.database.\
0709                    getmajordictvalues('todo',todoobjectfactory)
0710         r={}
0711         for k,e in todo_dict.items():
0712             ce=TodoEntry()
0713             ce.set_db_dict(e)
0714             r[ce.id]=ce
0715         result.update({ 'todo': r })
0716         return result
0717 
0718     def _OnListBoxItem(self, evt):
0719         # an item was clicked on/selected
0720         self._populate_each(self._item_list.GetClientData(evt.GetInt()))
0721         self.Refresh()
0722 
0723     def _OnSave(self, evt):
0724         # save the current changes
0725         self.ignoredirty=True
0726         sel_idx=self._item_list.GetSelection()
0727         k=self._item_list.GetClientData(sel_idx)
0728         entry=self._data[k]
0729         self._general_editor_w.Get(entry)
0730         entry.note=self._memo_editor_w.Get().get('memo', None)
0731         entry.categories=self._cat_editor_w.Get()
0732         entry.check_completion()
0733         self._general_editor_w.Set(entry)
0734         self._item_list.SetString(sel_idx, entry.summary)
0735         self._save_to_db(self._data)
0736         self.ignoredirty=False
0737         self.setdirty(False)
0738 
0739     def _OnRevert(self, evt):
0740         self.ignoredirty=True
0741         self._item_list.Enable()
0742         sel_idx=self._item_list.GetSelection()
0743         if sel_idx!=wx.NOT_FOUND:
0744             k=self._item_list.GetClientData(sel_idx)
0745             self._populate_each(k)
0746         self.ignoredirty=False
0747         self.setdirty(False)
0748 
0749 #-------------------------------------------------------------------------------
0750 

Generated by PyXR 0.9.4