PyXR

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



0001 ### BITPIM
0002 ###
0003 ### Copyright (C) 2005 Joe Pham <djpham@bitpim.org>
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: today.py 4601 2008-03-04 22:37:48Z djpham $
0009 
0010 """
0011 Code to handle BitPim Today tab.
0012 
0013 """
0014 
0015 # standard modules
0016 
0017 # wx modules
0018 import wx
0019 import wx.lib.scrolledpanel as scrolled
0020 import wx.lib.hyperlink as hl
0021 
0022 # BitPim modules
0023 import aggregatedisplay as aggr
0024 import pubsub
0025 import widgets
0026 
0027 
0028 # Today REQUEST_TAB_CHANGED keys, types, and constants
0029 Tab_Today=0
0030 Tab_PhoneBook=1
0031 Tab_Wallpaper=2
0032 Tab_Ringers=3
0033 Tab_Calendar=4
0034 Tab_Memo=5
0035 Tab_Todo=6
0036 Tab_SMS=7
0037 Tab_CallHistory=8
0038 # TODAY_DATA keys, types, and constants
0039 Today_Section='section'
0040 Today_Groups='groups'
0041 Today_Group_Tab='tab_index'
0042 Today_Items='items'
0043 Today_Item_Name='name'
0044 Today_Item_Data='item_data'
0045 # Sections
0046 Today_Section_Today='Today'
0047 Today_Section_ThisWeek='This Week'
0048 # Groups
0049 Today_Group_Calendar='Calendar:'
0050 Today_Group_Todo='Todo List:'
0051 Today_Group_Memo='Memo:'
0052 Today_Group_IncomingSMS='SMS Inbox:'
0053 Today_Group_IncomingCalls='Incoming Calls:'
0054 Today_Group_MissedCalls='Missed Calls:'
0055 
0056 dow_initials=('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', '   ')
0057 
0058 EVT_TODAY_ITEM_SELECTED=wx.NewEventType()
0059 
0060 #-------------------------------------------------------------------------------
0061 class NotificationEvent(object):
0062     def __init__(self, evt_handler, client_data=None):
0063         self._evt_handler=evt_handler
0064         self.client_data=client_data
0065         self.data=None
0066     def send(self):
0067         self._evt_handler(self)
0068 
0069 #-------------------------------------------------------------------------------
0070 class NotificationEventHandler(object):
0071     def __init__(self):
0072         self._evt_table={}
0073 
0074     def append(self, evt_handler, group, client_data=None):
0075         evt=NotificationEvent(evt_handler, client_data)
0076         self._evt_table.setdefault(group, []).append(evt)
0077 
0078     def process(self, group, evt):
0079         for e in self._evt_table.get(group, []):
0080             e.data=evt
0081             e.send()
0082         
0083 #-------------------------------------------------------------------------------
0084 evt_handler=None
0085 def bind_notification_event(_evt_handler, group, client_data=None):
0086     global evt_handler
0087     if evt_handler is None:
0088         evt_handler=NotificationEventHandler()
0089     evt_handler.append(_evt_handler, group, client_data)
0090 
0091 #-------------------------------------------------------------------------------
0092 class BaseEvent(object):
0093     def __init__(self, section, group):
0094         self.section=section
0095         self.group=group
0096         self._items=[]
0097 
0098     def append(self, item_name, item_data=None):
0099         # append a data item to this event
0100         self._items.append({ Today_Item_Name: item_name,
0101                              Today_Item_Data: item_data })
0102 
0103     def _set_names(self, names):
0104         self._items=[{ Today_Item_Name: x, Today_Item_Data: None } \
0105                      for x in names]
0106     def _get_names(self):
0107         return [x[Today_Item_Name] for x in self._items]
0108     names=property(fget=_get_names, fset=_set_names)
0109 
0110     def _get_items(self):
0111         return self._items
0112     items=property(fget=_get_items)
0113 
0114     def broadcast(self):
0115         # broadcast this event
0116         pubsub.publish(pubsub.NEW_DATA_AVAILABLE, data=self)
0117     @classmethod
0118     def bind(_, func):
0119         pubsub.subscribe(func, pubsub.NEW_DATA_AVAILABLE)
0120 
0121 #-------------------------------------------------------------------------------
0122 class TodayCalendarEvent(BaseEvent):
0123     def __init__(self):
0124         super(TodayCalendarEvent, self).__init__(Today_Section_Today,
0125                                                  Today_Group_Calendar)
0126 class ThisWeekCalendarEvent(BaseEvent):
0127     def __init__(self):
0128         super(ThisWeekCalendarEvent, self).__init__(Today_Section_ThisWeek,
0129                                                     Today_Group_Calendar)
0130 class TodayTodoEvent(BaseEvent):
0131     def __init__(self):
0132         super(TodayTodoEvent, self).__init__(Today_Section_Today,
0133                                              Today_Group_Todo)
0134 class ThisWeekTodoEvent(BaseEvent):
0135     def __init__(self):
0136         super(ThisWeekTodoEvent, self).__init__(Today_Section_ThisWeek,
0137                                                 Today_Group_Todo)
0138 class TodayMemoEvent(BaseEvent):
0139     def __init__(self):
0140         super(TodayMemoEvent, self).__init__(Today_Section_Today,
0141                                              Today_Group_Memo)
0142 class TodaySMSEvent(BaseEvent):
0143     def __init__(self):
0144         super(TodaySMSEvent, self).__init__(Today_Section_Today,
0145                                             Today_Group_IncomingSMS)
0146 class TodayIncomingCallsEvent(BaseEvent):
0147     def __init__(self):
0148         super(TodayIncomingCallsEvent, self).__init__(Today_Section_Today,
0149                                                       Today_Group_IncomingCalls)
0150 class TodayMissedCallsEvent(BaseEvent):
0151     def __init__(self):
0152         super(TodayMissedCallsEvent, self).__init__(Today_Section_Today,
0153                                               Today_Group_MissedCalls)
0154 
0155 # Request Today Event-----------------------------------------------------------
0156 def bind_request_event(evt_handler):
0157     pubsub.subscribe(evt_handler, pubsub.REQUEST_TODAY_DATA)
0158 def send_request_event():
0159     pubsub.publish(pubsub.REQUEST_TODAY_DATA)
0160 
0161 #-------------------------------------------------------------------------------
0162 EVT_HYPERLINK_LEFT=hl.EVT_HYPERLINK_LEFT
0163 class HyperLinkCtrl(hl.HyperLinkCtrl):
0164     def __init__(self, *args, **kargs):
0165         super(HyperLinkCtrl, self).__init__(*args, **kargs)
0166         self.AutoBrowse(False)
0167         self.DoPopup(False)
0168         self.client_data=None
0169 
0170     def SetToolTip(self, tip):
0171         if isinstance(tip, (str, unicode)):
0172             super(HyperLinkCtrl, self).SetToolTip(wx.ToolTip(tip))
0173         else:
0174             super(HyperLinkCtrl, self).SetToolTip(tip)
0175 
0176     def SetLabel(self, label):
0177         super(HyperLinkCtrl, self).SetLabel(label)
0178         self.SetToolTip(label)
0179 
0180 #-------------------------------------------------------------------------------
0181 class StaticText(wx.StaticText):
0182     _max_item_len=30
0183     _postfix='...'
0184     _max_client_len=_max_item_len-len(_postfix)
0185     def __init__(self, *args, **kargs):
0186         super(StaticText, self).__init__(*args, **kargs)
0187     def SetLabel(self, label):
0188         if len(label)>self._max_item_len:
0189             label=label[:self._max_client_len]+self._postfix
0190         super(StaticText, self).SetLabel(label)
0191 
0192 #-------------------------------------------------------------------------------
0193 class ItemHyperLink(HyperLinkCtrl):
0194     _max_item_len=30
0195     _postfix='...'
0196     _max_client_len=_max_item_len-len(_postfix)
0197     def __init__(self, *args, **kargs):
0198         super(ItemHyperLink, self).__init__(*args, **kargs)
0199         self.client_data=None
0200     def SetLabel(self, label, client_data=None):
0201         # if label contains linefeed truncate
0202         if label.find('\n')>-1:
0203             label=label.split('\n')[0]+self._postfix
0204         if len(label)>self._max_item_len:
0205             label=label[:self._max_client_len]+self._postfix
0206         super(ItemHyperLink, self).SetLabel(label)
0207         self.client_data=client_data
0208     
0209 #-------------------------------------------------------------------------------
0210 class GroupWidget(wx.Panel, widgets.BitPimWidget):
0211     max_total_items=10
0212     max_items=9
0213     _title_font=None
0214     _item_font=None
0215     _last_item={ Today_Item_Name: '... more ...',
0216                  Today_Item_Data: None }
0217     def __init__(self, parent, _name):
0218         super(GroupWidget, self).__init__(parent, wx.NewId())
0219         self.name=_name.get('name', '')
0220         self.tab_index=_name.get('tab_index', None)
0221         self._data=[]
0222         self._widgets=[]
0223         if not self._title_font:
0224             self._title_font=wx.Font(12, wx.FONTFAMILY_DEFAULT,
0225                                      wx.FONTSTYLE_NORMAL,
0226                                      wx.FONTWEIGHT_BOLD)
0227             self._item_font=wx.Font(10, wx.FONTFAMILY_MODERN,
0228                                     wx.FONTSTYLE_NORMAL,
0229                                     wx.FONTWEIGHT_NORMAL)
0230         bs=wx.StaticBoxSizer(wx.StaticBox(self, -1, ''), wx.VERTICAL)
0231         title=HyperLinkCtrl(self, -1, self.name)
0232         if self._title_font.Ok():
0233             title.SetFont(self._title_font)
0234         hl.EVT_HYPERLINK_LEFT(self, title.GetId(), self.OnItemSelected)
0235         bs.Add(title, 0, wx.ALL, 5)
0236         vbs=wx.BoxSizer(wx.VERTICAL)
0237         for i in range(self.max_total_items):
0238             w=ItemHyperLink(self, -1,  '')
0239             if self._item_font.Ok():
0240                 w.SetFont(self._item_font)
0241             hl.EVT_HYPERLINK_LEFT(self, w.GetId(), self.OnItemSelected)
0242             vbs.Add(w, 0, wx.RIGHT|wx.LEFT, 5)
0243             vbs.Hide(i)
0244             self._widgets.append(w)
0245         bs.Add(vbs, 1, wx.LEFT, 10)
0246         self._vbs=vbs
0247         bs.Fit(self)
0248         self.SetSizer(bs)
0249         self.SetAutoLayout(True)
0250 
0251     def Draw(self, dc, width, height, selected):
0252         x=dc.pos[0]-dc.view_start[0]
0253         y=dc.pos[1]-dc.view_start[1]
0254         self.SetDimensions(x, y, width, height)
0255 
0256     def OnHyperlinkLeft(self, evt):
0257         if self.tab_index:
0258             pubsub.publish(pubsub.REQUEST_TAB_CHANGED,
0259                            data=self.tab_index)
0260 
0261     def OnItemSelected(self, evt):
0262         if evt_handler:
0263             self.OnHyperlinkLeft(evt)
0264             evt_handler.process(self.name, evt.GetEventObject().client_data)
0265 
0266     def Set(self, data):
0267         data_len=len(data)
0268         if data_len>self.max_total_items:
0269             self._data=data[:self.max_items]
0270             self._data.append(self._last_item)
0271             data_len=self.max_total_items
0272         else:
0273             self._data=data
0274         for i in range(data_len):
0275             self._widgets[i].SetLabel(self._data[i].get(Today_Item_Name, ''),
0276                                       self._data[i].get(Today_Item_Data, None))
0277             self._vbs.Show(i)
0278         for i in range(data_len, self.max_total_items):
0279             self._vbs.Hide(i)
0280         self._vbs.Layout()
0281         self.Fit()
0282         self.Refresh()
0283 
0284     def not_empty(self):
0285         return self._data
0286 
0287 #-------------------------------------------------------------------------------
0288 class SectionHeader(aggr.SectionHeader):
0289     default_width=10
0290     default_height=10
0291     def __init__(self, label):
0292         super(SectionHeader, self).__init__(label)
0293         self._items=[]
0294     def Add(self, item):
0295         self._items.append(item)
0296     def GetAllItems(self):
0297         return self._items
0298     def GetItems(self):
0299         l=[]
0300         for x in self._items:
0301             if x.not_empty():
0302                 l.append(x)
0303                 x.Show()
0304             else:
0305                 x.Hide()
0306         return l
0307     def GetItemSize(self):
0308         w=self.default_width
0309         h=self.default_height
0310         for e in self._items:
0311             w1,h1=e.GetSizeTuple()
0312             w=max(w, w1)
0313             h=max(h,h1)
0314         return (w,h)
0315 
0316     def not_empty(self):
0317         return self.GetItems()
0318 
0319 
0320 #-------------------------------------------------------------------------------
0321 class TodayWidget(aggr.Display, widgets.BitPimWidget):
0322     _section_names=('Today','This Week')
0323     _item_names=(({ 'name': Today_Group_Calendar, 'tab_index': Tab_Calendar },
0324                   { 'name': Today_Group_Todo, 'tab_index': Tab_Todo },
0325                   { 'name': Today_Group_Memo, 'tab_index': Tab_Memo },
0326                   { 'name': Today_Group_IncomingSMS, 'tab_index': Tab_SMS },
0327                   { 'name': Today_Group_IncomingCalls, 'tab_index': Tab_CallHistory },
0328                   { 'name': Today_Group_MissedCalls, 'tab_index': Tab_CallHistory }),
0329                  ({ 'name': Today_Group_Calendar, 'tab_index': Tab_Calendar },
0330                   { 'name': Today_Group_Todo, 'tab_index': Tab_Todo } ))
0331 
0332     def __init__(self, mainwindow, parent):
0333         self._sections=[]
0334         super(TodayWidget, self).__init__(parent, self)
0335         self._main_window=mainwindow
0336         # sections & items info
0337         self._sections=[SectionHeader(s) for s in self._section_names]
0338         for i,group in enumerate(self._item_names):
0339             for name in group:
0340                 w=GroupWidget(self, name)
0341                 self._sections[i].Add(w)
0342         # all done
0343         # populate data
0344         self.UpdateItems()
0345         # register for pubsub events
0346         BaseEvent.bind(self.OnNewData)
0347         pubsub.subscribe(self._OnMidnight, pubsub.MIDNIGHT)
0348 
0349     def GetSections(self):
0350         "Return a list of section headers"
0351         return [x for x in self._sections if x.not_empty()]
0352 
0353     def GetItemSize(self, sectionnumber, sectionheader):
0354         "Return (width, height of each item)"
0355         return sectionheader.GetItemSize()
0356 
0357     def GetItemsFromSection(self,sectionnumber,sectionheader):
0358         return sectionheader.GetItems()
0359 
0360     def _populate(self, data):
0361         section_name=data.section
0362         group_name=data.group
0363         for s in self._sections:
0364             if s.label!=section_name:
0365                 continue
0366             for group in s.GetAllItems():
0367                 if group.name==group_name:
0368                     group.Set(data.items)
0369                     break
0370             break
0371         self.UpdateItems()
0372         self.Refresh()
0373 
0374     def OnNewData(self, msg=None):
0375         if msg is None:
0376             return
0377         self._populate(msg.data)
0378 
0379     def _OnMidnight(self, _):
0380         send_request_event()
0381 

Generated by PyXR 0.9.4