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