PyXR

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



0001 ### BITPIM
0002 ###
0003 ### Copyright (C) 2003-2004 Joe Pham <djpham@netzero.com>
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: calendarentryeditor.py 4397 2007-09-13 01:26:23Z djpham $
0009 
0010 from __future__ import with_statement
0011 import calendar
0012 import copy
0013 import datetime
0014 import time
0015 
0016 import wx
0017 import wx.lib
0018 import wx.lib.masked.textctrl
0019 import wx.lib.intctrl
0020 import wx.lib.scrolledpanel as scrolled
0021 
0022 import bpcalendar
0023 import field_color
0024 import helpids
0025 import phonebookentryeditor as pb_editor
0026 import pubsub
0027 import guihelper
0028 import guiwidgets
0029 
0030 widgets_list=[]
0031 
0032 class RepeatEditor(pb_editor.DirtyUIBase):
0033     _repeat_type= {
0034         'daily': 1,
0035         'weekly': 2,
0036         'monthly': 3,
0037         'yearly': 4 }
0038     _repeat_options=('None', 'Daily', 'Weekly', 'Monthly', 'Yearly')
0039     _dow=('Sun', 'Mon', 'Tues', 'Wed', 'Thu', 'Fri', 'Sat')
0040     _monthly_nth_day=('First', 'Second', 'Third', 'Fourth', 'Last')
0041     _daily_option_index=0
0042     _weekly_option_index=1
0043     _monthly_option_index=2
0044     _weekly_wkst_str=(None, 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun')
0045     _weekly_wkst_idx=(7, 1, 2, 3, 4, 5, 6)
0046     
0047     def __init__(self, parent, _):
0048         global widgets_list
0049         pb_editor.DirtyUIBase.__init__(self, parent)
0050         # overall container
0051         self._main_bs=wx.BoxSizer(wx.VERTICAL)
0052         # vertical sizebox & checkboxes for different repreat options
0053         hbs_1=wx.BoxSizer(wx.HORIZONTAL)
0054         self._repeat_option_rb = wx.RadioBox(
0055                 self, -1, "Repeat Types:", wx.DefaultPosition, wx.DefaultSize,
0056                 self._repeat_options, 1, wx.RA_SPECIFY_COLS)
0057         widgets_list.append((self._repeat_option_rb, 'repeat'))
0058         wx.EVT_RADIOBOX(self, self._repeat_option_rb.GetId(), self.OnRepeatType)
0059         hbs_1.Add(self._repeat_option_rb, 0, wx.LEFT, 5)
0060         # daily options widgets
0061         self._option_bs=wx.BoxSizer(wx.VERTICAL)
0062         _box=wx.StaticBox(self, -1, 'Daily Options:')
0063         widgets_list.append((_box, 'repeat'))
0064         vbs=wx.StaticBoxSizer(_box, wx.VERTICAL)
0065         hbs=wx.BoxSizer(wx.HORIZONTAL)
0066         self._dl_every_nday=wx.RadioButton(self, -1, 'Every ', style=wx.RB_GROUP)
0067         self._dl_every_nday.SetValue(True)
0068         self._dl_every_wday=wx.RadioButton(self, -1, 'Every Weekday')
0069         wx.EVT_RADIOBUTTON(self, self._dl_every_nday.GetId(), self.OnDirtyUI)
0070         wx.EVT_RADIOBUTTON(self, self._dl_every_wday.GetId(), self.OnDirtyUI)
0071         hbs.Add(self._dl_every_nday, 0, wx.LEFT, 0)
0072         self._dl_interval=wx.TextCtrl(self, -1, '1')
0073         wx.EVT_TEXT(self, self._dl_interval.GetId(), self.OnDirtyUI)
0074         hbs.Add(self._dl_interval, 0, wx.LEFT, 0)
0075         hbs.Add(wx.StaticText(self, -1, ' day(s)'), 0, wx.LEFT, 0)
0076         vbs.Add(hbs, 0, wx.LEFT|wx.TOP, 10)
0077         vbs.Add(self._dl_every_wday, 0, wx.LEFT, 10)
0078         self._option_bs.Add(vbs, 0, wx.LEFT, 5)
0079         self._daily_option_bs=vbs
0080         # weekly options widgets
0081         _box=wx.StaticBox(self, -1, 'Weekly Options:')
0082         widgets_list.append((_box, 'repeat'))
0083         vbs=wx.StaticBoxSizer(_box, wx.VERTICAL)
0084         hbs=wx.BoxSizer(wx.HORIZONTAL)
0085         hbs.Add(wx.StaticText(self, -1, 'Every '),0, wx.LEFT, 0)
0086         self._wl_interval=wx.TextCtrl(self, -1, '1')
0087         wx.EVT_TEXT(self, self._wl_interval.GetId(), self.OnDirtyUI)
0088         hbs.Add(self._wl_interval, 0, wx.LEFT, 0)
0089         hbs.Add(wx.StaticText(self, -1, ' week(s)'), 0, wx.LEFT, 0)
0090         vbs.Add(hbs, 0, wx.LEFT|wx.TOP, 10)
0091         vbs.Add(wx.StaticText(self, -1, 'On:'), 0, wx.LEFT, 10)
0092         hbs=wx.GridSizer(2, 4)
0093         self._wl_dow={}
0094         for i, n in enumerate(self._dow):
0095             self._wl_dow[i]=wx.CheckBox(self, -1, n)
0096             wx.EVT_CHECKBOX(self, self._wl_dow[i].GetId(), self.OnDirtyUI)
0097             hbs.Add(self._wl_dow[i], 0, wx.LEFT|wx.TOP, 5)
0098         vbs.Add(hbs, 0, wx.LEFT, 5)
0099         hbs=wx.BoxSizer(wx.HORIZONTAL)
0100         hbs.Add(wx.StaticText(self, -1, 'Week starts on:'), 0, wx.LEFT, 10)
0101         self._wl_wkst=wx.ComboBox(self, -1, value=self._dow[0],
0102                                   choices=self._dow, style=wx.CB_READONLY)
0103         wx.EVT_COMBOBOX(self, self._wl_wkst.GetId(), self.OnDirtyUI)
0104         hbs.Add(self._wl_wkst, 0, wx.LEFT, 5)
0105         vbs.Add(hbs, 0, wx.TOP, 10)
0106         self._option_bs.Add(vbs, 0, wx.LEFT, 5)
0107         self._weekly_option_bs=vbs
0108         # monthly option widgets
0109         _box=wx.StaticBox(self, -1, 'Monthly Options:')
0110         widgets_list.append((_box, 'repeat'))
0111         vbs=wx.StaticBoxSizer(_box, wx.VERTICAL)
0112         hbs=wx.BoxSizer(wx.HORIZONTAL)
0113         hbs.Add(wx.StaticText(self, -1, 'Every '),0, wx.LEFT, 0)
0114         self._ml_interval=wx.TextCtrl(self, -1, '1')
0115         wx.EVT_TEXT(self, self._ml_interval.GetId(), self.OnDirtyUI)
0116         hbs.Add(self._ml_interval, 0, wx.LEFT, 0)
0117         hbs.Add(wx.StaticText(self, -1, ' month(s)'), 0, wx.LEFT, 0)
0118         vbs.Add(hbs, 0, wx.LEFT|wx.TOP, 10)
0119         vbs.Add(wx.StaticText(self, -1, 'On:'), 0, wx.LEFT, 10)
0120         self._ml_every_nday=wx.RadioButton(self, -1, 'Every nth day', style=wx.RB_GROUP)
0121         self._ml_every_nday.SetValue(True)
0122         self._ml_every_wday=wx.RadioButton(self, -1, 'Every ')
0123         wx.EVT_RADIOBUTTON(self, self._ml_every_nday.GetId(), self.OnDirtyUI)
0124         wx.EVT_RADIOBUTTON(self, self._ml_every_wday.GetId(), self.OnDirtyUI)
0125         vbs.Add(self._ml_every_nday, 0, wx.LEFT|wx.TOP, 10)
0126         hbs=wx.BoxSizer(wx.HORIZONTAL)
0127         hbs.Add(self._ml_every_wday, 0, wx.LEFT, 0)
0128         self._ml_nth_day=wx.ComboBox(self, -1, value=self._monthly_nth_day[0],
0129                                      choices=self._monthly_nth_day,
0130                                      style=wx.CB_READONLY)
0131         self._ml_wday=wx.ComboBox(self, -1, value=self._dow[0],
0132                                   choices=self._dow, style=wx.CB_READONLY)
0133         wx.EVT_COMBOBOX(self, self._ml_nth_day.GetId(), self.OnDirtyUI)
0134         wx.EVT_COMBOBOX(self, self._ml_wday.GetId(), self.OnDirtyUI)
0135         hbs.Add(self._ml_nth_day, 0, wx.LEFT, 5)
0136         hbs.Add(self._ml_wday, 0, wx.LEFT, 5)
0137         vbs.Add(hbs, 0, wx.LEFT|wx.TOP, 10)
0138         
0139         self._option_bs.Add(vbs, 0, wx.LEFT, 5)
0140         self._monthly_option_bs=vbs
0141         
0142         hbs_1.Add(self._option_bs, 0, wx.LEFT, 5)
0143         self._main_bs.Add(hbs_1, 0, wx.LEFT|wx.TOP, 5)
0144         # the exceptions list
0145         _box=wx.StaticBox(self, -1, 'Excluded Dates:')
0146         widgets_list.append((_box, 'repeat'))
0147         hbs=wx.StaticBoxSizer(_box, wx.HORIZONTAL)
0148         self._exception_list=wx.ListBox(self, -1)
0149         hbs.Add(self._exception_list, 1, wx.LEFT|wx.TOP|wx.EXPAND, 5)
0150         exception_del=wx.Button(self, -1, 'Include')
0151         wx.EVT_BUTTON(self, exception_del.GetId(), self.OnIncludeException)
0152         hbs.Add(exception_del, 0, wx.LEFT|wx.TOP, 5)
0153         self._main_bs.Add(hbs, 1, wx.LEFT|wx.TOP|wx.EXPAND, 5)
0154         # all done
0155         self.SetSizer(self._main_bs)
0156         self.SetAutoLayout(True)
0157         self._main_bs.Fit(self)
0158         self.OnRepeatType(None)
0159 
0160     def populate(self, data):
0161         if data is None:
0162             self._repeat_option_rb.SetSelection(0)
0163             self._exception_list.Clear()
0164             self.OnRepeatType(None)
0165             return
0166         rt=data.repeat_type
0167         if rt==data.daily:
0168             self._repeat_option_rb.SetSelection(1)
0169             if data.interval:
0170                 self._dl_every_nday.SetValue(True)
0171                 self._dl_interval.SetValue(`data.interval`)
0172             else:
0173                 self._dl_every_wday.SetValue(True)
0174                 self._dl_interval.SetValue('')
0175         elif rt==data.weekly:
0176             self._repeat_option_rb.SetSelection(2)
0177             self._wl_interval.SetValue(`data.interval`)
0178             dow_mask=data.dow
0179             for i in range(len(self._wl_dow)):
0180                 b=((1<<i)&dow_mask)!=0
0181                 self._wl_dow[i].SetValue(b)
0182             self._wl_wkst.SetValue(self._weekly_wkst_str[data.weekstart])
0183         elif rt==data.monthly:
0184             self._repeat_option_rb.SetSelection(3)
0185             self._ml_interval.SetValue(`data.interval2`)
0186             if data.dow:
0187                 # every 1st *day of the month
0188                 self._ml_every_wday.SetValue(True)
0189                 self._ml_nth_day.SetSelection(data.interval-1)
0190                 dow_mask=data.dow
0191                 for i,e in enumerate(self._dow):
0192                     if (1<<i)&dow_mask:
0193                         self._ml_wday.SetValue(e)
0194                         break
0195             else:
0196                 # every nth day of the month
0197                 self._ml_every_nday.SetValue(True)
0198         else:
0199             self._repeat_option_rb.SetSelection(4)
0200         self._exception_list.Set(data.get_suppressed_list())
0201         self.OnRepeatType(None)
0202 
0203     def Set(self, data):
0204         self.ignore_dirty=True
0205         self.populate(data)
0206         self.dirty=self.ignore_dirty=False
0207 
0208     def _get_daily_options(self, r):
0209         r.repeat_type=r.daily
0210         try:
0211             b=self._dl_every_nday.GetValue()
0212         except:
0213             b=False
0214             self._dl_every_nday.SetValue(False)
0215         if b:
0216             try:
0217                 r.interval=int(self._dl_interval.GetValue())
0218             except:
0219                 # default to 1
0220                 r.interval=1
0221         else:
0222             r.interval=0
0223     def _get_weekly_options(self, r):
0224         r.repeat_type=r.weekly
0225         try:
0226             r.interval=int(self._wl_interval.GetValue())
0227         except:
0228             # default to 1
0229             r.interval=1
0230         m=0
0231         for i in range(len(self._wl_dow)):
0232             if self._wl_dow[i].GetValue():
0233                 m=m|(1<<i)
0234         r.dow=m
0235         r.weekstart=self._weekly_wkst_idx[self._wl_wkst.GetSelection()]
0236     def _get_monthly_options(self, r):
0237         r.repeat_type=r.monthly
0238         try:
0239             r.interval2=int(self._ml_interval.GetValue())
0240         except:
0241             r.interval2=1
0242         try:
0243             b=self._ml_every_wday.GetValue()
0244         except:
0245             b=False
0246             self._ml_every_wday.SetValue(False)
0247         if b:
0248             # every 1st Monday etc.
0249             r.interval=self._ml_nth_day.GetSelection()+1
0250             r.dow=1<<self._ml_wday.GetSelection()
0251 
0252     def Get(self):
0253         self.dirty=self.ignore_dirty=False
0254         rt=self._repeat_option_rb.GetSelection()
0255         if rt==0:
0256             # No repeat
0257             return None
0258         r=bpcalendar.RepeatEntry()
0259         if rt==1:
0260             # daily
0261             self._get_daily_options(r)
0262         elif rt==2:
0263             # weekly
0264             self._get_weekly_options(r)
0265         elif rt==3:
0266             # monthly
0267             self._get_monthly_options(r)
0268         else:
0269             r.repeat_type=r.yearly
0270         # get the list of exceptions
0271         r.suppressed=[str(self._exception_list.GetString(i)) \
0272            for i in range(self._exception_list.GetCount())]
0273         # and return the result
0274         return r
0275 
0276     def OnRepeatType(self, evt):
0277         s=self._repeat_option_rb.GetSelection()
0278         self._option_bs.Hide(self._weekly_option_index)
0279         self._option_bs.Hide(self._daily_option_index)
0280         self._option_bs.Hide(self._monthly_option_index)
0281         if s==1:
0282             self._option_bs.Show(self._daily_option_index)
0283         elif s==2:
0284             self._option_bs.Show(self._weekly_option_index)
0285         elif s==3:
0286             self._option_bs.Show(self._monthly_option_index)
0287         self._option_bs.Layout()
0288         self.OnDirtyUI(evt)
0289 
0290     def OnIncludeException(self, evt):
0291         print 'OnIncludeException'
0292         s=self._exception_list.GetSelections()
0293         if not len(s):
0294             # nothing selected
0295             return
0296         self._exception_list.Delete(s[0])
0297         self.OnDirtyUI(evt)
0298         
0299 #------------------------------------------------------------------------------
0300 class GeneralEditor(pb_editor.DirtyUIBase):
0301     _dict_key_index=0
0302     _label_index=1
0303     _class_index=2
0304     _get_index=3
0305     _set_index=4
0306     _w_index=5
0307     color_field_name='general'
0308     def __init__(self, parent, _):
0309         global widgets_list
0310         # base clase
0311         pb_editor.DirtyUIBase.__init__(self, parent)
0312         # structure to hold all the widgets of this panel
0313         self._fields=[
0314             ['description', 'Summary:', DVTextControl, None, None, None],
0315             ['location', 'Location:', DVTextControl, None, None, None],
0316             ['allday', 'All-Day:', wx.CheckBox, None, None, None],
0317             ['start', 'From:', DVDateTimeControl, None, self._set_start_datetime, None],
0318             ['end', 'To:', DVDateTimeControl, None, self._set_end_datetime, None],
0319             ['priority', 'Priority:', None, self._get_priority, self._set_priority, None],
0320             ['alarm', 'Alarm:', DVIntControl, None, None, None],
0321             ['vibrate', 'Vibrate:', wx.CheckBox, None, None, None],
0322             ]
0323         # overall container
0324         vbs=wx.StaticBoxSizer(wx.StaticBox(self, -1), wx.VERTICAL)
0325         # instantiate the widgets
0326         self._w={}
0327         gs=wx.FlexGridSizer(-1,2,5,5)
0328         gs.AddGrowableCol(1)
0329         for n in self._fields:
0330             desc=n[self._label_index]
0331             t=wx.StaticText(self, -1, desc, style=wx.ALIGN_LEFT)
0332             widgets_list.append((t, n[self._dict_key_index]))
0333             gs.Add(t)
0334             if desc=='Priority:':
0335                 c=wx.ComboBox(self, -1, "", (-1, -1), (-1, -1),
0336                               ['<None>', '1 - Highest', '2', '3', '4', '5 - Normal',
0337                                '6', '7' ,'8', '9', '10 - Lowest'], wx.CB_DROPDOWN)
0338             else:
0339                 c=n[self._class_index](self, -1)
0340             gs.Add(c, 0, wx.EXPAND, 0)
0341             n[self._w_index]=self._w[n[self._dict_key_index]]=c
0342         vbs.Add(gs, 0, wx.EXPAND|wx.ALL, 5)
0343         # event handlers
0344         wx.EVT_CHECKBOX(self, self._w['allday'].GetId(), self.OnAllday)
0345         wx.EVT_CHECKBOX(self, self._w['vibrate'].GetId(), self.OnDirtyUI)
0346         wx.EVT_COMBOBOX(self, self._w['priority'].GetId(), self.OnDirtyUI)
0347         # all done
0348         self.SetSizer(vbs)
0349         self.SetAutoLayout(True)
0350         vbs.Fit(self)
0351 
0352     def OnMakeDirty(self, evt):
0353         self.OnDirtyUI(evt)
0354 
0355     def OnAllday(self, evt):
0356         v=evt.IsChecked()
0357         self._w['start'].SetAllday(v)
0358         self._w['end'].SetAllday(v)
0359         self.OnDirtyUI(evt)
0360 
0361     def IsValid(self):
0362         # validate and return T if so or F otherwise
0363         # check for valid date/time entries
0364         for w in (self._w['start'], self._w['end']):
0365             if not w.IsValid() or w.IsEmpty():
0366                 w.SetFocus()
0367                 wx.Bell()
0368                 return False
0369         # whine if end is before start
0370         start=datetime.datetime(*self._w['start'].GetValue())
0371         end=datetime.datetime(*self._w['end'].GetValue())
0372         if start>end:
0373             # scold the user
0374             guihelper.MessageDialog(self, "End date and time is before start!", "Time Travel Attempt Detected",
0375                                     wx.OK|wx.ICON_EXCLAMATION)
0376             # move focus
0377             self._w['end'].SetFocus()
0378             return False
0379         return True
0380 
0381     def Set(self, data):
0382         self.ignore_dirty=True
0383         if data is None:
0384             for n in self._fields:
0385                 n[self._w_index].Enable(False)
0386         else:
0387             for n in self._fields:
0388                 w=n[self._w_index]
0389                 w.Enable(True)
0390                 if n[self._set_index] is None:
0391                     w.SetValue(getattr(data, n[self._dict_key_index]))
0392                 else:
0393                     n[self._set_index](w, data)
0394         self.ignore_dirty=self.dirty=False
0395 
0396     def Get(self, data):
0397         self.ignore_dirty=self.dirty=False
0398         if data is None:
0399             return
0400         for n in self._fields:
0401             w=n[self._w_index]
0402             if n[self._get_index] is None:
0403                 v=w.GetValue()
0404             else:
0405                 v=n[self._get_index](w, None)
0406             setattr(data, n[self._dict_key_index], v)
0407         
0408     def _set_priority(self, w, entry):
0409         p=entry.priority
0410         if p is None:
0411             w.SetSelection(0)
0412         else:
0413             w.SetSelection(p)
0414     def _get_priority(self, w, _):
0415         s=w.GetSelection()
0416         if s:
0417             return s
0418         else:
0419             return None
0420 
0421     def _set_start_datetime(self, w, entry):
0422         w.SetAllday(entry.allday, entry.start)
0423 
0424     def _set_end_datetime(self, w, entry):
0425         w.SetAllday(entry.allday, entry.end)
0426 
0427 #------------------------------------------------------------------------------
0428 class CategoryEditor(pb_editor.DirtyUIBase):
0429 
0430     # we have to have an entry with a special string for the unnamed string
0431 
0432     unnamed="Select:"
0433 
0434     def __init__(self, parent, pos):
0435         global widgets_list
0436         
0437         pb_editor.DirtyUIBase.__init__(self, parent)
0438         self.static_box=wx.StaticBox(self, -1, "Category")
0439         hs=wx.StaticBoxSizer(self.static_box, wx.HORIZONTAL)
0440 
0441         self.categories=[]
0442         self.category=wx.ListBox(self, -1, choices=self.categories)
0443         pubsub.subscribe(self.OnUpdateCategories, pubsub.ALL_CATEGORIES)
0444         pubsub.publish(pubsub.REQUEST_CATEGORIES)
0445         # a boxsizer for the master category list
0446         vbs=wx.BoxSizer(wx.VERTICAL)
0447         vbs.Add(wx.StaticText(self, -1, 'Master Category'), 0,
0448                 wx.TOP|wx.LEFT, 5)
0449         vbs.Add(self.category, 1, wx.EXPAND|wx.ALL, 5)
0450         hs.Add(vbs, 1, wx.EXPAND|wx.ALL, 5)
0451         # a boxsizer for the buttons
0452         vbs=wx.BoxSizer(wx.VERTICAL)
0453         self.but=wx.Button(self, wx.NewId(), "Manage Categories:")
0454         add_btn=wx.Button(self, -1, 'Add ->')
0455         del_btn=wx.Button(self, -1, '<- Remove')
0456         vbs.Add(self.but, 0, wx.ALIGN_CENTRE|wx.ALL, 5)
0457         vbs.Add(add_btn, 0, wx.ALIGN_CENTRE|wx.ALL, 5)
0458         vbs.Add(del_btn, 0, wx.ALIGN_CENTRE|wx.ALL, 5)
0459         hs.Add(vbs, 0, wx.ALIGN_CENTRE|wx.ALL, 5)
0460         wx.EVT_BUTTON(self, add_btn.GetId(), self.OnAddCategory)
0461         wx.EVT_BUTTON(self, del_btn.GetId(), self.OnDelCategory)
0462         # box sizer for the selected category
0463         vbs=wx.BoxSizer(wx.VERTICAL)
0464         vbs.Add(wx.StaticText(self, -1, 'Selected Category:'), 0,
0465                 wx.TOP|wx.LEFT, 5)
0466         self._my_category=wx.ListBox(self, -1)
0467         vbs.Add(self._my_category, 1, wx.EXPAND|wx.ALL, 5)
0468         hs.Add(vbs, 1, wx.EXPAND|wx.ALL, 5)
0469         wx.EVT_BUTTON(self, self.but.GetId(), self.OnManageCategories)
0470 
0471         self.SetSizer(hs)
0472         hs.Fit(self)
0473 
0474     def OnManageCategories(self, _):
0475         with guihelper.WXDialogWrapper(pb_editor.CategoryManager(self),
0476                                        True):
0477             pass
0478 
0479     def OnUpdateCategories(self, msg):
0480         cats=msg.data[:]
0481         if self.categories!=cats:
0482             self.categories=cats
0483             sel=self.category.GetStringSelection()
0484             self.category.Clear()
0485             for i in cats:
0486                 self.category.Append(i)
0487             try:
0488                 if len(sel):
0489                     self.category.SetStringSelection(sel)
0490             except:
0491                 pass
0492 
0493     def Get(self):
0494         self.ignore_dirty=self.dirty=False
0495         r=[]
0496         count=self._my_category.GetCount()
0497         if count==0:
0498             return r
0499         for i in range(count):
0500             r.append({ 'category': self._my_category.GetString(i) })
0501         return r
0502 
0503     def Set(self, data):
0504         self.ignore_dirty=True
0505         self._my_category.Clear()
0506         if data is None or len(data)==0:
0507             # none or empty list, do nothing
0508             return
0509         for n in data:
0510             v=n.get('category', None)
0511             if v is not None:
0512                 self._my_category.Append(v)
0513         self.ignore_dirty=self.dirty=False
0514 
0515     def OnAddCategory(self, evt):
0516         v=self.category.GetStringSelection()
0517         if not len(v):
0518             # no selection made, do nothing
0519             return
0520         self.ignore_dirty=True
0521         self._my_category.Append(v)
0522         self._my_category.SetStringSelection(v)
0523         self.ignore_dirty=False
0524         self.OnDirtyUI(evt)
0525 
0526     def OnDelCategory(self, evt):
0527         v=self._my_category.GetSelection()
0528         if v==wx.NOT_FOUND:
0529             # no selection, do nothing
0530             return
0531         self.ignore_dirty=True
0532         self._my_category.Delete(v)
0533         self.ignore_dirty=False
0534         self.OnDirtyUI(evt)
0535 
0536 #------------------------------------------------------------------------------
0537 class Editor(wx.Dialog):
0538 
0539     # results on asking if the user wants to change the original (repeating) entry, just
0540     # this instance, or cancel
0541     ANSWER_ORIGINAL=1
0542     ANSWER_THIS=2
0543     ANSWER_CANCEL=3
0544     _dict_key_index=0
0545     _label_index=1
0546     _get_index=2
0547     _set_index=3
0548     _w_index=4
0549     # notebook items
0550     _general_page=0
0551     _repeat_page=1
0552     _notes_page=2
0553     _categories_page=3
0554     _wallpapers_page=4
0555     _ringtones_page=5
0556     _last_page=6
0557     _items=[
0558         ("General", None, GeneralEditor, None),
0559         ("Repeat", 'repeat', RepeatEditor, None),
0560         ("Notes", "notes", pb_editor.MemoEditor, 'memo'),
0561         ("Categories", "categories", CategoryEditor, 'category'),
0562         ("Wallpapers", "wallpapers", pb_editor.WallpaperEditor, 'wallpaper'),
0563         ("Ringtones", "ringtones", pb_editor.RingtoneEditor, 'ringtone'),
0564         ]
0565     color_field_name='calendar'
0566 
0567     def __init__(self, parent):
0568         global widgets_list
0569         wx.Dialog.__init__(self, parent, -1, 'Calendar Entry Editor',
0570                            wx.DefaultPosition,
0571                            style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
0572         # the parent is the BPCalenda widget, save it
0573         self.cw=parent  # also the BPCalendar object
0574         # Tracking of the entries in the listbox.  Each entry is a dict. Entries are just the
0575         # entries in a random order.  entrymap maps from the order in the listbox to a
0576         # specific entry
0577         self.entries=[]
0578         self.entrymap=[]
0579         self._current_entry=None
0580 
0581         # Dirty tracking.  We restrict what the user can do while editting an
0582         # entry to only be able to edit that entry.  'dirty' gets fired when
0583         # they make any updates.  Annoyingly, controls generate change events
0584         # when they are updated programmatically as well as by user interaction.
0585         # ignoredirty is set when we are programmatically updating controls
0586         self.dirty=None
0587         self.ignoredirty=True
0588         
0589         # overall container
0590         vbs=wx.BoxSizer(wx.VERTICAL)
0591         
0592         self._prev_btn=wx.BitmapButton(self, wx.NewId(), wx.ArtProvider.GetBitmap(guihelper.ART_ARROW_LEFT), name="Previous Day")
0593         self._next_btn=wx.BitmapButton(self, wx.NewId(), wx.ArtProvider.GetBitmap(guihelper.ART_ARROW_RIGHT), name="Next Day")
0594         self.title=wx.StaticText(self, -1, "Date here", style=wx.ALIGN_CENTRE|wx.ST_NO_AUTORESIZE)
0595 
0596         # top row container 
0597         hbs1=wx.BoxSizer(wx.HORIZONTAL)
0598         hbs1.Add(self._prev_btn, 0, wx.EXPAND)
0599         hbs1.Add(self.title, 1, wx.EXPAND)
0600         hbs1.Add(self._next_btn, 0, wx.EXPAND)
0601         vbs.Add(hbs1, 0, wx.TOP|wx.EXPAND, 10)
0602 
0603         # list box and two buttons below
0604         self.listbox=wx.ListBox(self, wx.NewId(), style=wx.LB_SINGLE|wx.LB_HSCROLL|wx.LB_NEEDED_SB)
0605         self._add_btn=wx.Button(self, wx.ID_NEW)
0606         hbs2=wx.BoxSizer(wx.HORIZONTAL)
0607         hbs2.Add(self._add_btn, 1, wx.ALIGN_CENTER|wx.LEFT|wx.RIGHT, border=5)
0608         
0609         # sizer for listbox
0610         lbs=wx.BoxSizer(wx.VERTICAL)
0611         lbs.Add(self.listbox, 1, wx.EXPAND|wx.BOTTOM, border=5)
0612         lbs.Add(hbs2, 0, wx.EXPAND)
0613 
0614         # right hand bit with all fields
0615         self._nb=wx.Notebook(self, -1)
0616         self._widgets=[]
0617         for i, (name, key, klass, color_name) in enumerate(self._items):
0618             if name in ('Ringtones', 'Wallpapers'):
0619                 self._widgets.append(klass(self._nb, parent, False))
0620             else:
0621                 self._widgets.append(klass(self._nb, parent))
0622             self._nb.AddPage(self._widgets[i], name)
0623             if color_name:
0624                 widgets_list.append((self._widgets[i].static_box, color_name))
0625             
0626         # buttons below fields
0627         self._delete_btn=wx.Button(self, wx.ID_DELETE)
0628         self._revert_btn=wx.Button(self, wx.ID_REVERT_TO_SAVED)
0629         self._save_btn=wx.Button(self, wx.ID_SAVE)
0630 
0631         hbs4=wx.BoxSizer(wx.HORIZONTAL)
0632         hbs4.Add(self._delete_btn, 1, wx.ALIGN_CENTRE|wx.LEFT, border=10)
0633         hbs4.Add(self._revert_btn, 1, wx.ALIGN_CENTRE|wx.LEFT|wx.RIGHT, border=10)
0634         hbs4.Add(self._save_btn, 1, wx.ALIGN_CENTRE|wx.RIGHT, border=10)
0635 
0636         # fields and buttons together
0637         vbs2=wx.BoxSizer(wx.VERTICAL)
0638         vbs2.Add(self._nb, 1, wx.EXPAND|wx.BOTTOM, border=5)
0639         vbs2.Add(hbs4, 0, wx.EXPAND|wx.ALIGN_CENTRE)
0640 
0641         # container for everything below title row
0642         hbs3=wx.BoxSizer(wx.HORIZONTAL)
0643         hbs3.Add(lbs, 1, wx.EXPAND|wx.ALL, 5)
0644         hbs3.Add(vbs2, 2, wx.EXPAND|wx.ALL, 5)
0645         vbs.Add(hbs3, 1, wx.EXPAND)
0646         # the standard buttons
0647         vbs.Add(wx.StaticLine(self, -1, style=wx.LI_HORIZONTAL), 0, wx.EXPAND|wx.ALL, 5)
0648         vbs.Add(self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.HELP), 0, wx.ALIGN_CENTRE|wx.ALL, 5)
0649 
0650         self.SetSizer(vbs)
0651         self.SetAutoLayout(True)
0652         vbs.Fit(self)
0653         # delete is disabled until an item is selected
0654         self._delete_btn.Enable(False)
0655 
0656         wx.EVT_LISTBOX(self, self.listbox.GetId(), self.OnListBoxItem)
0657         wx.EVT_LISTBOX_DCLICK(self, self.listbox.GetId(), self.OnListBoxItem)
0658         wx.EVT_BUTTON(self, wx.ID_SAVE, self.OnSaveButton)
0659         wx.EVT_BUTTON(self, wx.ID_REVERT_TO_SAVED, self.OnRevertButton)
0660         wx.EVT_BUTTON(self, wx.ID_NEW, self.OnNewButton)
0661         wx.EVT_BUTTON(self, wx.ID_DELETE, self.OnDeleteButton)
0662         wx.EVT_BUTTON(self, self._prev_btn.GetId(), self.OnPrevDayButton)
0663         wx.EVT_BUTTON(self, self._next_btn.GetId(), self.OnNextDayButton)
0664         # callbacks for the standard buttons
0665         wx.EVT_BUTTON(self, wx.ID_OK, self.OnOk)
0666         wx.EVT_BUTTON(self, wx.ID_CANCEL, self.OnCancel)
0667         wx.EVT_BUTTON(self, wx.ID_HELP, lambda _: wx.GetApp().displayhelpid(helpids.ID_EDITING_CALENDAR_EVENTS))
0668         # DIRTY UI Event handlers
0669         for w in self._widgets:
0670             pb_editor.EVT_DIRTY_UI(self, w.GetId(), self.OnMakeDirty)
0671         self.ignoredirty=False
0672         self.setdirty(False)
0673         guiwidgets.set_size("CalendarEntryEditor", self, 52, 1.0)
0674         field_color.reload_color_info(self, widgets_list)
0675         pubsub.subscribe(self.OnPhoneChanged, pubsub.PHONE_MODEL_CHANGED)
0676 
0677     def OnPhoneChanged(self, _):
0678         # just reload the color info based on the new phone
0679         field_color.reload_color_info(self, widgets_list)
0680         self.Refresh()
0681 
0682     def OnListBoxItem(self, evt=None):
0683         """Callback for when user clicks on an event in the listbox"""
0684         self._current_entry=self.getcurrententry()
0685         if self._current_entry:
0686             self.updatefields(self._current_entry)
0687         self.setdirty(False)
0688         self._delete_btn.Enable(True)
0689 
0690     def getcurrententry(self):
0691         """Returns the entry currently being viewed
0692 
0693         @Note: this returns the unedited form of the entry"""
0694         i=self.listbox.GetSelection()
0695         if i==-1:
0696             return None
0697         return self.getentry(i)
0698 
0699     def getentry(self, num):
0700         """maps from entry number in listbox to an entry in entries
0701 
0702         @type num: int
0703         @rtype: entry(dict)"""
0704         return self.entries[self.entrymap[num]]
0705 
0706     def OnSaveButton(self, evt):
0707         """Callback for when user presses save"""
0708 
0709         # check if the dates are ok
0710         if not self._widgets[self._general_page].IsValid():
0711             return
0712 
0713         # lets roll ..
0714 ##        entry=self.getcurrententry()
0715         entry=self._current_entry
0716 
0717         # is it a repeat?
0718         res=self.ANSWER_ORIGINAL
0719         if entry.repeat is not None:
0720             # ask the user
0721             res=self.AskAboutRepeatChange()
0722             if res==self.ANSWER_CANCEL:
0723                 return
0724         # where do we get newentry template from?
0725         if res==self.ANSWER_ORIGINAL:
0726             newentry=copy.copy(entry)
0727         else:
0728             newentry=self.cw.newentryfactory(*self.date)
0729 
0730         # update the fields from the general tab
0731         self._widgets[self._general_page].Get(newentry)
0732         if res==self.ANSWER_THIS:
0733             # change this event only, change the start & end date to this date
0734             newentry.start=list(self.date)+list(newentry.start[3:])
0735             newentry.end=list(self.date)+list(newentry.end[3:])
0736             # if we are changing a repeat, reset the new entry's repeat is off
0737             newentry.repeat=None
0738         else:
0739             # get data from the repeat tab
0740             newentry.repeat=self._widgets[self._repeat_page].Get()
0741         # and other tabs as well
0742         newentry.notes=self._widgets[self._notes_page].Get().get('memo', None)
0743         newentry.categories=self._widgets[self._categories_page].Get()
0744         newentry.wallpaper=self._widgets[self._wallpapers_page].Get().get('wallpaper', None)
0745         newentry.ringtone=self._widgets[self._ringtones_page].Get().get('ringtone', None)
0746         # got the data
0747         # update calendar widget
0748         if res==self.ANSWER_ORIGINAL:
0749             self.cw.ChangeEntry(entry, newentry)
0750         else:
0751             # delete the repeat and add this new entry
0752             self.cw.DeleteEntryRepeat(entry, *self.date)
0753             self.cw.AddEntry(newentry)
0754         if __debug__:
0755             print 'Editor.OnSaveButton: updated entry:'
0756             print newentry.get()
0757             print 'Equivalent DB dict:'
0758             print bpcalendar.CalendarDataObject(newentry)
0759         # tidy up
0760         self.setdirty(False)
0761         # did the user change the date on us?
0762         date=tuple(newentry.start[:3])
0763         if tuple(self.date)!=date:
0764             self.cw.showday(*date)
0765             self.cw.setselection(*date)
0766             self.setdate(*date)
0767         else:
0768             self.refreshentries()
0769         self.updatelistbox(newentry.id)
0770 
0771     def OnOk(self, evt):
0772         # save the current entry & exit
0773         guiwidgets.save_size("CalendarEntryEditor", self.GetRect())
0774         if self.dirty:
0775             self.OnSaveButton(None)
0776         self.setdirty(False)
0777         evt.Skip()
0778 
0779     def OnCancel(self, evt):
0780         # just exit
0781         guiwidgets.save_size("CalendarEntryEditor", self.GetRect())
0782         self.setdirty(False)
0783         evt.Skip()
0784     
0785     def OnRevertButton(self, evt):
0786         # We basically pretend the user has selected the item in the listbox again (which they
0787         # can't actually do as it is disabled
0788         self.listbox.Enable()
0789         self.OnListBoxItem()
0790 
0791     def OnNewButton(self, evt):
0792         entry=self.cw.newentryfactory(*self.date)
0793         self.cw.AddEntry(entry)
0794         self.refreshentries()
0795         self.updatelistbox(entry.id)
0796 
0797     def OnDeleteButton(self, evt):
0798         entry=self._current_entry
0799         if entry is None:
0800             return
0801         # is it a repeat?
0802         res=self.ANSWER_ORIGINAL
0803         if entry.repeat is not None:
0804             # ask the user
0805             res=self.AskAboutRepeatDelete()
0806             if res==self.ANSWER_CANCEL:
0807                 return
0808         enum=self.listbox.GetSelection()
0809         if enum+1<len(self.entrymap):
0810             # try and find entry after current one
0811             newpos=self.getentry(enum+1).id
0812         elif enum-1>=0:
0813             # entry before as we are deleting last entry
0814             newpos=self.getentry(enum-1).id
0815         else:
0816             newpos=None
0817         if res==self.ANSWER_ORIGINAL:
0818             self.cw.DeleteEntry(entry)
0819         else:
0820             self.cw.DeleteEntryRepeat(entry, *self.date)
0821         self.setdirty(False)
0822         self.refreshentries()
0823         self.updatelistbox(newpos)
0824 
0825     def OnPrevDayButton(self, evt):
0826         d=datetime.date(*self.date)-datetime.timedelta(1)
0827         self.setdate(d.year, d.month, d.day)
0828         self.cw.setday(d.year, d.month, d.day)
0829 
0830     def OnNextDayButton(self, evt):
0831         d=datetime.date(*self.date)+datetime.timedelta(1)
0832         self.setdate(d.year, d.month, d.day)
0833         self.cw.setday(d.year, d.month, d.day)
0834     
0835     def setdate(self, year, month, day, entry=None):
0836         """Sets the date we are editing entries for
0837 
0838         @Note: The list of entries is updated"""
0839         d=time.strftime("%A %d %B %Y", (year,month,day,0,0,0, calendar.weekday(year,month,day),1, 0))
0840         self.date=year,month,day
0841         self.title.SetLabel(d)
0842         self.refreshentries()
0843         self.updatelistbox(entry and entry.id or None)
0844         self.updatefields(entry)
0845 
0846     def refreshentries(self):
0847         """re-requests the list of entries for the currently visible date from the main calendar"""
0848         self.entries=self.cw.getentrydata(*self.date)
0849 
0850     def updatelistbox(self, entrytoselect=None):
0851         """
0852         Updates the contents of the listbox.  It will re-sort the contents.
0853 
0854         @param entrytoselect: The integer id of an entry to select.  Note that
0855                               this is an event id, not an index
0856         """
0857         self.listbox.Clear()
0858         selectitem=-1
0859         self.entrymap=[]
0860         self._current_entry=None
0861         # decorate
0862         for index, entry in enumerate(self.entries):
0863             if entry.allday:
0864                 e=( entry.start[3:5], entry.end[3:5], entry.description, index)
0865             else:
0866                 e=(None, None, entry.description, index)
0867             self.entrymap.append(e)
0868         # time ordered
0869         self.entrymap.sort()
0870         # now undecorate
0871         self.entrymap=[index for ign0, ign1, ign2, index in self.entrymap]
0872         # add listbox entries
0873         for curpos, index in enumerate(self.entrymap):
0874             e=self.entries[index]
0875             if e.id==entrytoselect:
0876                 selectitem=curpos
0877             self.listbox.Append(e.summary)
0878 
0879         # Select an item if requested
0880         if selectitem>=0:
0881             self.listbox.SetSelection(selectitem)
0882             self.OnListBoxItem() # update fields
0883         else:
0884             # disable fields since nothing is selected
0885             self.updatefields(None)
0886             
0887         # disable delete if there are no entries!
0888         if len(self.entries)==0:
0889             self._delete_btn.Enable(False)
0890 
0891     def updatefields(self, entry):
0892         self.ignoredirty=True
0893         self._widgets[self._general_page].Set(entry)
0894         # populate the other tabs with current entry data
0895         if entry is None:
0896             # clear everything out
0897             for i in range(self._repeat_page, self._last_page):
0898                 self._widgets[i].Set(None)
0899                 self._widgets[i].Enable(False)
0900             return
0901         # there's data, setting each page accordingly
0902         for i in range(self._repeat_page, self._last_page):
0903             self._widgets[i].Enable(True)
0904         self._widgets[self._repeat_page].Set(entry.repeat)
0905         self._widgets[self._notes_page].Set({ 'memo': entry.notes })
0906         self._widgets[self._categories_page].Set(entry.categories)
0907         self._widgets[self._wallpapers_page].Set( \
0908             { 'wallpaper': entry.wallpaper, 'type': 'calendar' })
0909         self._widgets[self._ringtones_page].Set( \
0910             { 'ringtone': entry.ringtone, 'type': 'calendar' })
0911         self.ignoredirty=False
0912 
0913     # called from various widget update callbacks
0914     def OnMakeDirty(self, _=None):
0915         """A public function you can call that will set the dirty flag"""
0916         if self.dirty or self.ignoredirty or not self.IsShown():
0917             # already dirty, no need to make it worse
0918             return
0919         self.setdirty(True)
0920 
0921     def setdirty(self, val):
0922         """Set the dirty flag
0923 
0924         The various buttons in the dialog are enabled/disabled as appropriate
0925         for the new state.
0926         
0927         @type  val: Bool
0928         @param val: True to mark edit fields as different from entry (ie
0929                     editing has taken place)
0930                     False to make them as the same as the entry (ie no
0931                     editing or the edits have been discarded)
0932         """
0933         if self.ignoredirty:
0934             return
0935         self.dirty=val
0936         if self.dirty:
0937             # The data has been modified, so we only allow working
0938             # with this data
0939             
0940             # enable save, revert, delete
0941             self._save_btn.Enable(True)
0942             self._revert_btn.Enable(True)
0943             # disable close, left, right, new
0944             self._prev_btn.Enable(False)
0945             self._next_btn.Enable(False)
0946             self._add_btn.Enable(False)
0947             # can't play with listbox now
0948             self.listbox.Enable(False)
0949         else:
0950             # The data is now clean and saved/reverted or deleted
0951             
0952             # disable save, revert,
0953             self._save_btn.Enable(False)
0954             self._revert_btn.Enable(False)
0955 
0956             # enable delete, close, left, right, new
0957             self._delete_btn.Enable(len(self.entries)>0) # only enable if there are entries
0958             self._prev_btn.Enable(True)
0959             self._next_btn.Enable(True)
0960             self._add_btn.Enable(True)
0961 
0962             # can choose another item in listbox
0963             self.listbox.Enable(True)
0964 
0965     def AskAboutRepeatDelete(self):
0966         """Asks the user if they wish to delete the original (repeating) entry, or this instance
0967 
0968         @return: An C{ANSWER_} constant
0969         """
0970         return self._AskAboutRecurringEvent("Delete recurring event?", "Do you want to delete all the recurring events, or just this one?", "Delete")
0971 
0972     def AskAboutRepeatChange(self):
0973         """Asks the user if they wish to change the original (repeating) entry, or this instance
0974 
0975         @return: An C{ANSWER_} constant
0976         """
0977         return self._AskAboutRecurringEvent("Change recurring event?", "Do you want to change all the recurring events, or just this one?", "Change")
0978 
0979     def _AskAboutRecurringEvent(self, caption, text, prefix):
0980         with guihelper.WXDialogWrapper(RecurringDialog(self, caption, text, prefix),
0981                                        True) as (dlg, res):
0982             if res==dlg.ID_THIS:
0983                 return self.ANSWER_THIS
0984             if res==dlg.ID_ALL:
0985                 return self.ANSWER_ORIGINAL
0986             if res==dlg.ID_CANCEL:
0987                 return self.ANSWER_CANCEL
0988             assert False
0989 #------------------------------------------------------------------------------    
0990 # We derive from wxPanel not the control directly.  If we derive from
0991 # wx.MaskedTextCtrl then all hell breaks loose as our {Get|Set}Value
0992 # methods make the control malfunction big time
0993 class DVDateTimeControl(wx.Panel):
0994     """A datetime control customised to work in the dayview editor"""
0995     def __init__(self,parent,id):
0996         self._allday=False
0997         self._datetime_format="EUDATETIMEYYYYMMDD.HHMM"
0998         self._date_format='EUDATEYYYYMMDD.'
0999         wx.Panel.__init__(self, parent, -1)
1000         self.c=wx.lib.masked.textctrl.TextCtrl(\
1001             self, id, "", autoformat=self._datetime_format,
1002             emptyInvalid=True,
1003             emptyBackgroundColour='Red',
1004             invalidBackgroundColour='Red')
1005         bs=wx.BoxSizer(wx.HORIZONTAL)
1006         bs.Add(self.c,0,wx.EXPAND)
1007         self.SetSizer(bs)
1008         self.SetAutoLayout(True)
1009         bs.Fit(self)
1010         wx.EVT_TEXT(self.c, id, parent.OnMakeDirty)
1011 
1012     def SetValue(self, v):
1013         if v is None:
1014             self.c.SetValue("")
1015             return
1016         if self._allday:
1017             str="%04d%02d%02d" % tuple(v[:3])
1018         else:
1019             ap="AM"
1020             v=list(v)
1021             if v[3]>12:
1022                 v[3]-=12
1023                 ap="PM"
1024             elif v[3]==0:
1025                 v[3]=12
1026             elif v[3]==12:
1027                 ap="PM"
1028             v=v+[ap]
1029             # we have to supply what the user would type without the punctuation
1030             # (try figuring that out from the "doc")
1031             str="%04d%02d%02d%02d%02d%s" % tuple(v)
1032         self.c.SetValue( str )
1033         self.c.Refresh()
1034 
1035     def GetValue(self):
1036         # The actual value including all punctuation is returned
1037         # GetPlainValue can get it with all digits run together
1038         str=self.c.GetValue()
1039         digits="0123456789"
1040 
1041         # turn it back into a list
1042         res=[]
1043         val=None
1044         for i in str:
1045             if i in digits:
1046                 if val is None: val=0
1047                 val*=10
1048                 val+=int(i)
1049             else:
1050                 if val is not None:
1051                     res.append(val)
1052                     val=None
1053         if val is not None:
1054             res.append(val)
1055         if len(res)==3:
1056             res += [0,0]
1057         elif len(res)==5:
1058             # fixup am/pm
1059             if str[-2]=='P' or str[-2]=='p':
1060                 if res[3]!=12: # 12pm is midday and left alone
1061                     res[3]+=12
1062             elif res[3]==12: # 12 am
1063                 res[3]=0
1064 
1065         return res
1066 
1067     def IsValid(self):
1068         return self.c.IsValid()
1069     def IsEmpty(self):
1070         return self.c.IsEmpty()
1071 
1072     def SetAllday(self, allday, v=None):
1073         if allday==self._allday and v is None:
1074             return
1075         if v is None:
1076             v=self.GetValue()
1077         if allday != self._allday:
1078             self._allday=allday
1079             if self._allday:
1080                 self.c.SetCtrlParameters(autoformat=self._date_format)
1081             else:
1082                 self.c.SetCtrlParameters(autoformat=self._datetime_format)
1083         self.SetValue(v)
1084 
1085 #------------------------------------------------------------------------------    
1086 class DVIntControl(wx.lib.intctrl.IntCtrl):
1087     # shows integer values
1088     def __init__(self, parent, id):
1089         wx.lib.intctrl.IntCtrl.__init__(self, parent, id, limited=True)
1090         wx.lib.intctrl.EVT_INT(self, id, parent.OnMakeDirty)
1091 
1092     def SetValue(self, v):
1093         if v is None:
1094             v=-1
1095         wx.lib.intctrl.IntCtrl.SetValue(self,v)
1096         
1097 #------------------------------------------------------------------------------    
1098 class DVTextControl(wx.TextCtrl):
1099     def __init__(self, parent, id, value=""):
1100         if value is None:
1101             value=""
1102         wx.TextCtrl.__init__(self, parent, id, value)
1103         wx.EVT_TEXT(self, id, parent.OnMakeDirty)
1104 
1105     def SetValue(self, v):
1106         if v is None: v=""
1107         wx.TextCtrl.SetValue(self,v)
1108 
1109 #-------------------------------------------------------------------------------
1110 ###
1111 ### Dialog box for asking the user what they want to for a recurring event.
1112 ### Used when saving changes or deleting entries in the DayViewDialog
1113 ###
1114 
1115 class RecurringDialog(wx.Dialog):
1116     """Ask the user what they want to do about a recurring event
1117 
1118     You should only use this as a modal dialog.  ShowModal() will
1119     return one of:
1120 
1121       - ID_THIS:   change just this event
1122       - ID_ALL:    change all events
1123       - ID_CANCEL: user cancelled dialog"""
1124     ID_THIS=1
1125     ID_ALL=2
1126     ID_CANCEL=3
1127     ID_HELP=4 # hide from epydoc
1128 
1129     def __init__(self, parent, caption, text, prefix):
1130         """Constructor
1131 
1132         @param parent: frame to parent this to
1133         @param caption: caption of the dialog (eg C{"Change recurring event?"})
1134         @param text: text displayed in the dialog (eg C{"This is a recurring event.  What would you like to change?"})
1135         @param prefix: text prepended to the buttons (eg the button says " this" so the prefix would be "Change" or "Delete")
1136         """
1137         wx.Dialog.__init__(self, parent, -1, caption,
1138                           style=wx.CAPTION)
1139 
1140         # eveything sits inside a vertical box sizer
1141         vbs=wx.BoxSizer(wx.VERTICAL)
1142 
1143         # the explanatory text
1144         t=wx.StaticText(self, -1, text)
1145         vbs.Add(t, 1, wx.EXPAND|wx.ALL,10)
1146 
1147         # horizontal line
1148         vbs.Add(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 3)
1149 
1150         # buttons at bottom
1151         buttonsizer=wx.BoxSizer(wx.HORIZONTAL)
1152         for id, label in (self.ID_THIS,   "%s %s" % (prefix, "this")), \
1153                          (self.ID_ALL,    "%s %s" % (prefix, "all")), \
1154                          (self.ID_CANCEL, "Cancel"), \
1155                          (self.ID_HELP,   "Help"):
1156             b=wx.Button(self, id, label)
1157             wx.EVT_BUTTON(self, id, self._onbutton)
1158             buttonsizer.Add(b, 5, wx.ALIGN_CENTER|wx.ALL, 5)
1159 
1160         # plumb in sizers
1161         vbs.Add(buttonsizer, 0, wx.EXPAND|wx.ALL,2)
1162         self.SetSizer(vbs)
1163         self.SetAutoLayout(True)
1164         vbs.Fit(self)
1165 
1166 
1167     def _onbutton(self, evt):
1168         if evt.GetId()==self.ID_HELP:
1169             pass # :::TODO::: some sort of help ..
1170         else:
1171             self.EndModal(evt.GetId())
1172 

Generated by PyXR 0.9.4