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