| Trees | Indices | Help |
|
|---|
|
|
1 ### BITPIM
2 ###
3 ### Copyright (C) 2005 Joe Pham <djpham@netzero.net>
4 ###
5 ### This program is free software; you can redistribute it and/or modify
6 ### it under the terms of the BitPim license as detailed in the LICENSE file.
7 ###
8 ### $Id: memo.py 4714 2008-10-21 01:50:47Z djpham $
9
10 """
11 Code to handle memo/note items
12
13 The format for the memo is standardized. It is a dict with the following
14 fields:
15
16 MemoEntry properties:
17 subject - 'string subject'
18 text - 'string text'
19 categories - [{ 'category': 'string' }]
20 secret - True/<False|None>
21 date - date time/stamp 'Mmm dd, YYYY HH:MM' (Read only)
22 id - unique id string that can be used as a dict key.
23
24 MemoEntry methods:
25 get() - return a copy of the memo dict.
26 set(dict) - set the internal dict to the new dict.
27 set_date_now() - set the date/time stamp to the current date/time
28 set_date_isostr(iso_string) - set the date/time stamp to the ISO date string
29 (YYYYMMDDTHHMMSS)
30
31 To implement memo read/write for a phone module:
32 Add 2 entries into Profile._supportedsyncs:
33 ...
34 ('memo', 'read', None), # all memo reading
35 ('memo', 'write', 'OVERWRITE') # all memo writing
36
37 implement the following 2 methods in your Phone class:
38 def getmemo(self, result):
39 ...
40 return result
41
42 def savememo(self, result, merge):
43 ...
44 return result
45
46 The result dict key is 'memo'.
47 """
48
49 # standard modules
50 from __future__ import with_statement
51 import copy
52 import datetime
53 import time
54
55 # wx modules
56 import wx
57
58 # BitPim modules
59 import bptime
60 import calendarentryeditor as cal_editor
61 import database
62 import field_color
63 import helpids
64 import phonebookentryeditor as pb_editor
65 import pubsub
66 import today
67 import guihelper
68 import guiwidgets
69 import widgets
70
71 widgets_list=[]
72 module_debug=False
73
74 #-------------------------------------------------------------------------------
76 _knownproperties=['subject', 'date']
77 _knownlistproperties=database.basedataobject._knownlistproperties.copy()
78 _knownlistproperties.update( {'categories': ['category'],
79 'flags': ['secret'],
80 'body': ['type', 'data', '*' ] })
81
86 memoobjectfactory=database.dataobjectfactory(MemoDataObject)
87
88 #-------------------------------------------------------------------------------
90 _body_subject_len=12 # the # of chars from body to fill in for subj + ...
91 _id_index=0
92 _max_id_index=999
97
99 return copy.deepcopy(self._data, {})
103
105 return self.get()
107 self.set(d)
108
110 if v is None or v in v_list:
111 if self._data.has_key(key):
112 del self._data[key]
113 else:
114 self._data[key]=v
115
117 return self._data.get('subject', '')
119 self._set_or_del('subject', v, ('',))
120 subject=property(fget=_get_subject, fset=_set_subject)
121
123 b=self._data.get('body', [])
124 for n in b:
125 if n.get('type', None)=='text':
126 return n.get('data', '')
127 return ''
129 if v is None:
130 v=''
131 if not self.subject:
132 self.subject=v[:self._body_subject_len]+'...'
133 b=self._data.get('body', [])
134 for n in b:
135 if n.get('type', None)=='text':
136 n['data']=v
137 return
138 self._data.setdefault('body', []).append(\
139 {'type': 'text', 'data': v })
140 text=property(fget=_get_text, fset=_set_text)
141
143 f=self._data.get('flags', [])
144 for n in f:
145 if n.has_key('secret'):
146 return n['secret']
147 return False
149 f=self._data.get('flags', [])
150 for i, n in enumerate(f):
151 if n.has_key('secret'):
152 if v is None or not v:
153 del f[i]
154 if not self._data['flags']:
155 del self._data['flags']
156 else:
157 n['secret']=v
158 return
159 if v is not None and v:
160 self._data.setdefault('flags', []).append({'secret': v})
161 secret=property(fget=_get_secret, fset=_set_secret)
162
164 return self._data.get('categories', [])
166 self._set_or_del('categories', v, ([],))
167 categories=property(fget=_get_categories, fset=_set_categories)
168
170 # set the date/time stamp to now
171 n=datetime.datetime.now()
172 self._data['date']=n.strftime('%b %d, %Y %H:%M')
174 n=bptime.BPTime(iso_string)
175 self._data['date']=n.date.strftime('%b %d, %Y')+n.time.strftime(' %H:%M')
177 return self._data.get('date', '')
178 date=property(fget=_get_date)
179
181 "Create a BitPim serial for this entry"
182 self._data.setdefault("serials", []).append(\
183 {"sourcetype": "bitpim",
184 "id": '%.3f%03d'%(time.time(), MemoEntry._id_index) })
185 if MemoEntry._id_index<MemoEntry._max_id_index:
186 MemoEntry._id_index+=1
187 else:
188 MemoEntry._id_index=0
190 s=self._data.get('serials', [])
191 for n in s:
192 if n.get('sourcetype', None)=='bitpim':
193 return n.get('id', None)
194 return None
195 id=property(fget=_get_id)
196
197 #-------------------------------------------------------------------------------
199 _dict_key_index=0
200 _label_index=1
201 _class_index=2
202 _get_index=3
203 _set_index=4
204 _w_index=5
206 global widgets_list
207
208 pb_editor.DirtyUIBase.__init__(self, parent)
209 self._fields=[
210 ['subject', 'Subject:', cal_editor.DVTextControl, None, None, None],
211 ['date', 'Date:', wx.StaticText, self._get_date_str, self._set_date_str, None],
212 ['secret', 'Private:', wx.CheckBox, None, None, None]
213 ]
214 gs=wx.FlexGridSizer(-1, 2, 5, 5)
215 gs.AddGrowableCol(1)
216 for n in self._fields:
217 _txt=wx.StaticText(self, -1, n[self._label_index],
218 style=wx.ALIGN_LEFT)
219 widgets_list.append((_txt, n[0]))
220 gs.Add(_txt, 0, wx.EXPAND|wx.BOTTOM, 5)
221 w=n[self._class_index](self, -1)
222 gs.Add(w, 0, wx.EXPAND|wx.BOTTOM, 5)
223 n[self._w_index]=w
224 # event handlers
225 wx.EVT_CHECKBOX(self, self._fields[2][self._w_index].GetId(),
226 self.OnMakeDirty)
227 # all done
228 self.SetSizer(gs)
229 self.SetAutoLayout(True)
230 gs.Fit(self)
231
237 self.OnDirtyUI(evt)
238
240 self.ignore_dirty=True
241 if data is None:
242 for n in self._fields:
243 n[self._w_index].Enable(False)
244 else:
245 for n in self._fields:
246 w=n[self._w_index]
247 w.Enable(True)
248 if n[self._set_index] is None:
249 w.SetValue(getattr(data, n[self._dict_key_index]))
250 else:
251 n[self._set_index](w, data)
252 self.ignore_dirty=self.dirty=False
253
255 self.ignore_dirty=self.dirty=False
256 if data is None:
257 return
258 for n in self._fields:
259 w=n[self._w_index]
260 if n[self._get_index] is None:
261 v=w.GetValue()
262 else:
263 v=n[self._get_index](w, None)
264 if v is not None:
265 setattr(data, n[self._dict_key_index], v)
266
267 #-------------------------------------------------------------------------------
269 color_field_name='memo'
270
272 wx.Panel.__init__(self, parent, -1)
273 self._main_window=mainwindow
274 self._data={}
275 self._data_map={}
276 # main box sizer
277 vbs=wx.BoxSizer(wx.VERTICAL)
278 # horizontal sizer for the listbox and tabs
279 hbs=wx.BoxSizer(wx.HORIZONTAL)
280 # the list box
281 self._item_list=wx.ListBox(self, wx.NewId(),
282 style=wx.LB_SINGLE|wx.LB_HSCROLL|wx.LB_NEEDED_SB)
283 hbs.Add(self._item_list, 1, wx.EXPAND|wx.BOTTOM, border=5)
284 # the detailed info pane
285 vbs1=wx.BoxSizer(wx.VERTICAL)
286 self._items=(
287 (GeneralEditor, 0, None),
288 (cal_editor.CategoryEditor, 1, 'category'),
289 (pb_editor.MemoEditor, 1, 'memo')
290 )
291 self._w=[]
292 for n in self._items:
293 w=n[0](self, -1)
294 vbs1.Add(w, n[1], wx.EXPAND|wx.ALL, 5)
295 self._w.append(w)
296 if n[2]:
297 widgets_list.append((w.static_box, n[2]))
298 hbs.Add(vbs1, 3, wx.EXPAND|wx.ALL, border=5)
299 self._general_editor_w=self._w[0]
300 self._cat_editor_w=self._w[1]
301 self._memo_editor_w=self._w[2]
302 # the bottom buttons
303 hbs1=wx.BoxSizer(wx.HORIZONTAL)
304 self._save_btn=wx.Button(self, wx.ID_SAVE)
305 self._revert_btn=wx.Button(self, wx.ID_REVERT_TO_SAVED)
306 help_btn=wx.Button(self, wx.ID_HELP)
307 hbs1.Add(self._save_btn, 0, wx.ALIGN_CENTRE|wx.ALL, 5)
308 hbs1.Add(help_btn, 0, wx.ALIGN_CENTRE|wx.ALL, 5)
309 hbs1.Add(self._revert_btn, 0, wx.ALIGN_CENTRE|wx.ALL, 5)
310 # all done
311 vbs.Add(hbs, 1, wx.EXPAND|wx.ALL, 5)
312 vbs.Add(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 5)
313 vbs.Add(hbs1, 0, wx.ALIGN_CENTRE|wx.ALL, 5)
314 self.SetSizer(vbs)
315 self.SetAutoLayout(True)
316 vbs.Fit(self)
317 # event handlers
318 wx.EVT_LISTBOX(self, self._item_list.GetId(), self._OnListBoxItem)
319 wx.EVT_BUTTON(self, self._save_btn.GetId(), self._OnSave)
320 wx.EVT_BUTTON(self, self._revert_btn.GetId(), self._OnRevert)
321 wx.EVT_BUTTON(self, wx.ID_HELP,
322 lambda _: wx.GetApp().displayhelpid(helpids.ID_TAB_MEMO))
323 # DIRTY UI Event handlers
324 for w in self._w:
325 pb_editor.EVT_DIRTY_UI(self, w.GetId(), self.OnMakeDirty)
326 # populate data
327 self._populate()
328 # turn on dirty flag
329 self.ignoredirty=False
330 self.setdirty(False)
331 # register for Today selection
332 today.bind_notification_event(self.OnTodaySelection,
333 today.Today_Group_Memo)
334 # color code editable labels
335 field_color.reload_color_info(self, widgets_list)
336 pubsub.subscribe(self.OnPhoneChanged, pubsub.PHONE_MODEL_CHANGED)
337
339 # just reload the color info based on the new phone
340 field_color.reload_color_info(self, widgets_list)
341 self.Refresh()
342
344 keys=self._data.keys()
345 keys.sort()
346 keys.reverse()
347 today_event=today.TodayMemoEvent()
348 for k in keys:
349 today_event.append(self._data[k].subject,
350 { 'key': k, 'index': self._data_map[k] })
351 today_event.broadcast()
352
354 self.ActivateSelf()
355 if evt.data:
356 self._item_list.SetSelection(evt.data.get('index', wx.NOT_FOUND))
357 self._populate_each(evt.data.get('key', None))
358
362
368
370 # populate new data
371 self._clear()
372 self._data_map={}
373 # populate the list with data
374 keys=self._data.keys()
375 keys.sort()
376 for k in keys:
377 n=self._data[k]
378 i=self._item_list.Append(n.subject)
379 self._item_list.SetClientData(i, k)
380 self._data_map[k]=i
381 self._send_today_data()
382
384 # populate the detailed info of the item keyed k
385 if k is None:
386 # clear out all the subfields
387 self._clear_each()
388 return
389 # there're data, first enable the widgets
390 self.ignoredirty=True
391 for w in self._w:
392 w.Enable(True)
393 entry=self._data[k]
394 # set the general detail
395 self._general_editor_w.Set(entry)
396 self._cat_editor_w.Set(entry.categories)
397 self._memo_editor_w.Set({ 'memo': entry.text })
398 self.ignoredirty=False
399 self.setdirty(False)
400
401 # called from various widget update callbacks
403 """A public function you can call that will set the dirty flag"""
404 if self.dirty or self.ignoredirty or not self.IsShown():
405 # already dirty, no need to make it worse
406 return
407 self.setdirty(True)
408
410 """Set the dirty flag"""
411 if self.ignoredirty:
412 return
413 self.dirty=val
414 self._item_list.Enable(not self.dirty)
415 self._save_btn.Enable(self.dirty)
416 self._revert_btn.Enable(self.dirty)
417
422
425
428
430 # add a new memo item
431 if self.dirty:
432 # busy editing, cannot add now, just return
433 return
434 m=MemoEntry()
435 m.subject='New Memo'
436 self._data[m.id]=m
437 self._populate()
438 self._save_to_db(self._data)
439 self._item_list.Select(self._data_map[m.id])
440 self._populate_each(m.id)
441
443 sel_idx=self._item_list.GetSelection()
444 if sel_idx is None or sel_idx==-1:
445 return False
446 return True
447
449 # delete the current selected item
450 sel_idx=self._item_list.GetSelection()
451 if sel_idx is None or sel_idx==-1:
452 # none selected
453 return
454 self.ignoredirty=True
455 k=self._item_list.GetClientData(sel_idx)
456 self._item_list.Delete(sel_idx)
457 self._clear_each()
458 del self._data[k]
459 del self._data_map[k]
460 self._save_to_db(self._data)
461 self.ignoredirty=False
462 self.setdirty(False)
463
467
469 # return a dict of selected items
470 res={}
471 for _idx in self._item_list.GetSelections():
472 _key=self._item_list.GetClientData(_idx)
473 if _key:
474 res[_key]=self._data[_key]
475 return res
476
479
483
485 db_rr={}
486 for k, e in memo_dict.items():
487 db_rr[k]=MemoDataObject(e)
488 database.ensurerecordtype(db_rr, memoobjectfactory)
489 self._main_window.database.savemajordict('memo', db_rr)
490
494
496 # read data from the database
497 memo_dict=self._main_window.database.getmajordictvalues('memo',
498 memoobjectfactory)
499 r={}
500 for k,e in memo_dict.items():
501 if __debug__ and module_debug:
502 print e
503 ce=MemoEntry()
504 ce.set_db_dict(e)
505 r[ce.id]=ce
506 result.update({ 'memo': r })
507 return result
508
510 # an item was clicked on/selected
511 self._populate_each(self._item_list.GetClientData(evt.GetInt()))
512 self.Refresh()
513
515 # save the current changes
516 self.ignoredirty=True
517 sel_idx=self._item_list.GetSelection()
518 k=self._item_list.GetClientData(sel_idx)
519 entry=self._data[k]
520 self._general_editor_w.Get(entry)
521 entry.text=self._memo_editor_w.Get().get('memo', None)
522 entry.categories=self._cat_editor_w.Get()
523 entry.set_date_now()
524 self._general_editor_w.Set(entry)
525 self._item_list.SetString(sel_idx, entry.subject)
526 self._save_to_db(self._data)
527 self._send_today_data()
528 self.ignoredirty=False
529 self.setdirty(False)
530
532 self.ignoredirty=True
533 # Enable the list to get the selection
534 self._item_list.Enable()
535 sel_idx=self._item_list.GetSelection()
536 if sel_idx!=wx.NOT_FOUND:
537 k=self._item_list.GetClientData(sel_idx)
538 self._populate_each(k)
539 self.ignoredirty=False
540 self.setdirty(False)
541
543 with guihelper.WXDialogWrapper(guiwidgets.MemoPrintDialog(self, mainwindow, config),
544 True):
545 pass
548
550 """Return the list of keys as being displayed"""
551 return [ self._item_list.GetClientData(x) \
552 for x in range(self._item_list.GetCount()) ]
553
555 """Return the list of keys of selected items being displayed"""
556 return [ self._item_list.GetClientData(x) \
557 for x in self._item_list.GetSelections() ]
558
| Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Sun Jan 24 16:22:13 2010 | http://epydoc.sourceforge.net |