Module calendarentryeditor
[hide private]
[frames] | no frames]

Source Code for Module calendarentryeditor

   1  ### BITPIM 
   2  ### 
   3  ### Copyright (C) 2003-2004 Joe Pham <djpham@netzero.com> 
   4  ### 
   5  ### This program is free software; you can redistribute it and/or modify 
   6  ### it under the terms of the BitPim license as detailed in the LICENSE file. 
   7  ### 
   8  ### $Id: calendarentryeditor.py 4708 2008-09-06 04:10:44Z djpham $ 
   9   
  10  from __future__ import with_statement 
  11  import calendar 
  12  import copy 
  13  import datetime 
  14  import time 
  15   
  16  import wx 
  17  import wx.lib 
  18  import wx.lib.masked.textctrl 
  19  import wx.lib.intctrl 
  20  import wx.lib.scrolledpanel as scrolled 
  21   
  22  import bpcalendar 
  23  import field_color 
  24  import helpids 
  25  import phonebookentryeditor as pb_editor 
  26  import pubsub 
  27  import guihelper 
  28  import guiwidgets 
  29   
  30  widgets_list=[] 
  31   
32 -class RepeatEditor(pb_editor.DirtyUIBase):
33 _repeat_type= { 34 'daily': 1, 35 'weekly': 2, 36 'monthly': 3, 37 'yearly': 4 } 38 _repeat_options=('None', 'Daily', 'Weekly', 'Monthly', 'Yearly') 39 _dow=('Sun', 'Mon', 'Tues', 'Wed', 'Thu', 'Fri', 'Sat') 40 _monthly_nth_day=('First', 'Second', 'Third', 'Fourth', 'Last') 41 _daily_option_index=0 42 _weekly_option_index=1 43 _monthly_option_index=2 44 _weekly_wkst_str=(None, 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun') 45 _weekly_wkst_idx=(7, 1, 2, 3, 4, 5, 6) 46
47 - def __init__(self, parent, _):
48 global widgets_list 49 pb_editor.DirtyUIBase.__init__(self, parent) 50 # overall container 51 self._main_bs=wx.BoxSizer(wx.VERTICAL) 52 # vertical sizebox & checkboxes for different repreat options 53 hbs_1=wx.BoxSizer(wx.HORIZONTAL) 54 self._repeat_option_rb = wx.RadioBox( 55 self, -1, "Repeat Types:", wx.DefaultPosition, wx.DefaultSize, 56 self._repeat_options, 1, wx.RA_SPECIFY_COLS) 57 widgets_list.append((self._repeat_option_rb, 'repeat')) 58 wx.EVT_RADIOBOX(self, self._repeat_option_rb.GetId(), self.OnRepeatType) 59 hbs_1.Add(self._repeat_option_rb, 0, wx.LEFT, 5) 60 # daily options widgets 61 self._option_bs=wx.BoxSizer(wx.VERTICAL) 62 _box=wx.StaticBox(self, -1, 'Daily Options:') 63 widgets_list.append((_box, 'repeat')) 64 vbs=wx.StaticBoxSizer(_box, wx.VERTICAL) 65 hbs=wx.BoxSizer(wx.HORIZONTAL) 66 self._dl_every_nday=wx.RadioButton(self, -1, 'Every ', style=wx.RB_GROUP) 67 self._dl_every_nday.SetValue(True) 68 self._dl_every_wday=wx.RadioButton(self, -1, 'Every Weekday') 69 wx.EVT_RADIOBUTTON(self, self._dl_every_nday.GetId(), self.OnDirtyUI) 70 wx.EVT_RADIOBUTTON(self, self._dl_every_wday.GetId(), self.OnDirtyUI) 71 hbs.Add(self._dl_every_nday, 0, wx.LEFT, 0) 72 self._dl_interval=wx.TextCtrl(self, -1, '1') 73 wx.EVT_TEXT(self, self._dl_interval.GetId(), self.OnDirtyUI) 74 hbs.Add(self._dl_interval, 0, wx.LEFT, 0) 75 hbs.Add(wx.StaticText(self, -1, ' day(s)'), 0, wx.LEFT, 0) 76 vbs.Add(hbs, 0, wx.LEFT|wx.TOP, 10) 77 vbs.Add(self._dl_every_wday, 0, wx.LEFT, 10) 78 self._option_bs.Add(vbs, 0, wx.LEFT, 5) 79 self._daily_option_bs=vbs 80 # weekly options widgets 81 _box=wx.StaticBox(self, -1, 'Weekly Options:') 82 widgets_list.append((_box, 'repeat')) 83 vbs=wx.StaticBoxSizer(_box, wx.VERTICAL) 84 hbs=wx.BoxSizer(wx.HORIZONTAL) 85 hbs.Add(wx.StaticText(self, -1, 'Every '),0, wx.LEFT, 0) 86 self._wl_interval=wx.TextCtrl(self, -1, '1') 87 wx.EVT_TEXT(self, self._wl_interval.GetId(), self.OnDirtyUI) 88 hbs.Add(self._wl_interval, 0, wx.LEFT, 0) 89 hbs.Add(wx.StaticText(self, -1, ' week(s)'), 0, wx.LEFT, 0) 90 vbs.Add(hbs, 0, wx.LEFT|wx.TOP, 10) 91 vbs.Add(wx.StaticText(self, -1, 'On:'), 0, wx.LEFT, 10) 92 hbs=wx.GridSizer(2, 4) 93 self._wl_dow={} 94 for i, n in enumerate(self._dow): 95 self._wl_dow[i]=wx.CheckBox(self, -1, n) 96 wx.EVT_CHECKBOX(self, self._wl_dow[i].GetId(), self.OnDirtyUI) 97 hbs.Add(self._wl_dow[i], 0, wx.LEFT|wx.TOP, 5) 98 vbs.Add(hbs, 0, wx.LEFT, 5) 99 hbs=wx.BoxSizer(wx.HORIZONTAL) 100 hbs.Add(wx.StaticText(self, -1, 'Week starts on:'), 0, wx.LEFT, 10) 101 self._wl_wkst=wx.ComboBox(self, -1, value=self._dow[0], 102 choices=self._dow, style=wx.CB_READONLY) 103 wx.EVT_COMBOBOX(self, self._wl_wkst.GetId(), self.OnDirtyUI) 104 hbs.Add(self._wl_wkst, 0, wx.LEFT, 5) 105 vbs.Add(hbs, 0, wx.TOP, 10) 106 self._option_bs.Add(vbs, 0, wx.LEFT, 5) 107 self._weekly_option_bs=vbs 108 # monthly option widgets 109 _box=wx.StaticBox(self, -1, 'Monthly Options:') 110 widgets_list.append((_box, 'repeat')) 111 vbs=wx.StaticBoxSizer(_box, wx.VERTICAL) 112 hbs=wx.BoxSizer(wx.HORIZONTAL) 113 hbs.Add(wx.StaticText(self, -1, 'Every '),0, wx.LEFT, 0) 114 self._ml_interval=wx.TextCtrl(self, -1, '1') 115 wx.EVT_TEXT(self, self._ml_interval.GetId(), self.OnDirtyUI) 116 hbs.Add(self._ml_interval, 0, wx.LEFT, 0) 117 hbs.Add(wx.StaticText(self, -1, ' month(s)'), 0, wx.LEFT, 0) 118 vbs.Add(hbs, 0, wx.LEFT|wx.TOP, 10) 119 vbs.Add(wx.StaticText(self, -1, 'On:'), 0, wx.LEFT, 10) 120 self._ml_every_nday=wx.RadioButton(self, -1, 'Every nth day', style=wx.RB_GROUP) 121 self._ml_every_nday.SetValue(True) 122 self._ml_every_wday=wx.RadioButton(self, -1, 'Every ') 123 wx.EVT_RADIOBUTTON(self, self._ml_every_nday.GetId(), self.OnDirtyUI) 124 wx.EVT_RADIOBUTTON(self, self._ml_every_wday.GetId(), self.OnDirtyUI) 125 vbs.Add(self._ml_every_nday, 0, wx.LEFT|wx.TOP, 10) 126 hbs=wx.BoxSizer(wx.HORIZONTAL) 127 hbs.Add(self._ml_every_wday, 0, wx.LEFT, 0) 128 self._ml_nth_day=wx.ComboBox(self, -1, value=self._monthly_nth_day[0], 129 choices=self._monthly_nth_day, 130 style=wx.CB_READONLY) 131 self._ml_wday=wx.ComboBox(self, -1, value=self._dow[0], 132 choices=self._dow, style=wx.CB_READONLY) 133 wx.EVT_COMBOBOX(self, self._ml_nth_day.GetId(), self.OnDirtyUI) 134 wx.EVT_COMBOBOX(self, self._ml_wday.GetId(), self.OnDirtyUI) 135 hbs.Add(self._ml_nth_day, 0, wx.LEFT, 5) 136 hbs.Add(self._ml_wday, 0, wx.LEFT, 5) 137 vbs.Add(hbs, 0, wx.LEFT|wx.TOP, 10) 138 139 self._option_bs.Add(vbs, 0, wx.LEFT, 5) 140 self._monthly_option_bs=vbs 141 142 hbs_1.Add(self._option_bs, 0, wx.LEFT, 5) 143 self._main_bs.Add(hbs_1, 0, wx.LEFT|wx.TOP, 5) 144 # the exceptions list 145 _box=wx.StaticBox(self, -1, 'Excluded Dates:') 146 widgets_list.append((_box, 'repeat')) 147 hbs=wx.StaticBoxSizer(_box, wx.HORIZONTAL) 148 self._exception_list=wx.ListBox(self, -1) 149 hbs.Add(self._exception_list, 1, wx.LEFT|wx.TOP|wx.EXPAND, 5) 150 exception_del=wx.Button(self, -1, 'Include') 151 wx.EVT_BUTTON(self, exception_del.GetId(), self.OnIncludeException) 152 hbs.Add(exception_del, 0, wx.LEFT|wx.TOP, 5) 153 self._main_bs.Add(hbs, 1, wx.LEFT|wx.TOP|wx.EXPAND, 5) 154 # all done 155 self.SetSizer(self._main_bs) 156 self.SetAutoLayout(True) 157 self._main_bs.Fit(self) 158 self.OnRepeatType(None)
159
160 - def populate(self, data):
161 if data is None: 162 self._repeat_option_rb.SetSelection(0) 163 self._exception_list.Clear() 164 self.OnRepeatType(None) 165 return 166 rt=data.repeat_type 167 if rt==data.daily: 168 self._repeat_option_rb.SetSelection(1) 169 if data.interval: 170 self._dl_every_nday.SetValue(True) 171 self._dl_interval.SetValue(`data.interval`) 172 else: 173 self._dl_every_wday.SetValue(True) 174 self._dl_interval.SetValue('') 175 elif rt==data.weekly: 176 self._repeat_option_rb.SetSelection(2) 177 self._wl_interval.SetValue(`data.interval`) 178 dow_mask=data.dow 179 for i in range(len(self._wl_dow)): 180 b=((1<<i)&dow_mask)!=0 181 self._wl_dow[i].SetValue(b) 182 self._wl_wkst.SetValue(self._weekly_wkst_str[data.weekstart]) 183 elif rt==data.monthly: 184 self._repeat_option_rb.SetSelection(3) 185 self._ml_interval.SetValue(`data.interval2`) 186 if data.dow: 187 # every 1st *day of the month 188 self._ml_every_wday.SetValue(True) 189 self._ml_nth_day.SetSelection(data.interval-1) 190 dow_mask=data.dow 191 for i,e in enumerate(self._dow): 192 if (1<<i)&dow_mask: 193 self._ml_wday.SetValue(e) 194 break 195 else: 196 # every nth day of the month 197 self._ml_every_nday.SetValue(True) 198 else: 199 self._repeat_option_rb.SetSelection(4) 200 self._exception_list.Set(data.get_suppressed_list()) 201 self.OnRepeatType(None)
202
203 - def Set(self, data):
204 self.ignore_dirty=True 205 self.populate(data) 206 self.dirty=self.ignore_dirty=False
207
208 - def _get_daily_options(self, r):
209 r.repeat_type=r.daily 210 try: 211 b=self._dl_every_nday.GetValue() 212 except: 213 b=False 214 self._dl_every_nday.SetValue(False) 215 if b: 216 try: 217 r.interval=int(self._dl_interval.GetValue()) 218 except: 219 # default to 1 220 r.interval=1 221 else: 222 r.interval=0
223 - def _get_weekly_options(self, r):
224 r.repeat_type=r.weekly 225 try: 226 r.interval=int(self._wl_interval.GetValue()) 227 except: 228 # default to 1 229 r.interval=1 230 m=0 231 for i in range(len(self._wl_dow)): 232 if self._wl_dow[i].GetValue(): 233 m=m|(1<<i) 234 r.dow=m 235 r.weekstart=self._weekly_wkst_idx[self._wl_wkst.GetSelection()]
236 - def _get_monthly_options(self, r):
237 r.repeat_type=r.monthly 238 try: 239 r.interval2=int(self._ml_interval.GetValue()) 240 except: 241 r.interval2=1 242 try: 243 b=self._ml_every_wday.GetValue() 244 except: 245 b=False 246 self._ml_every_wday.SetValue(False) 247 if b: 248 # every 1st Monday etc. 249 r.interval=self._ml_nth_day.GetSelection()+1 250 r.dow=1<<self._ml_wday.GetSelection()
251
252 - def Get(self):
253 self.dirty=self.ignore_dirty=False 254 rt=self._repeat_option_rb.GetSelection() 255 if rt==0: 256 # No repeat 257 return None 258 r=bpcalendar.RepeatEntry() 259 if rt==1: 260 # daily 261 self._get_daily_options(r) 262 elif rt==2: 263 # weekly 264 self._get_weekly_options(r) 265 elif rt==3: 266 # monthly 267 self._get_monthly_options(r) 268 else: 269 r.repeat_type=r.yearly 270 # get the list of exceptions 271 r.suppressed=[str(self._exception_list.GetString(i)) \ 272 for i in range(self._exception_list.GetCount())] 273 # and return the result 274 return r
275
276 - def OnRepeatType(self, evt):
277 s=self._repeat_option_rb.GetSelection() 278 self._option_bs.Hide(self._weekly_option_index) 279 self._option_bs.Hide(self._daily_option_index) 280 self._option_bs.Hide(self._monthly_option_index) 281 if s==1: 282 self._option_bs.Show(self._daily_option_index) 283 elif s==2: 284 self._option_bs.Show(self._weekly_option_index) 285 elif s==3: 286 self._option_bs.Show(self._monthly_option_index) 287 self._option_bs.Layout() 288 self.OnDirtyUI(evt)
289
290 - def OnIncludeException(self, evt):
291 print 'OnIncludeException' 292 s=self._exception_list.GetSelections() 293 if not len(s): 294 # nothing selected 295 return 296 self._exception_list.Delete(s[0]) 297 self.OnDirtyUI(evt)
298 299 #------------------------------------------------------------------------------
300 -class GeneralEditor(pb_editor.DirtyUIBase):
301 _dict_key_index=0 302 _label_index=1 303 _class_index=2 304 _get_index=3 305 _set_index=4 306 _w_index=5 307 color_field_name='general'
308 - def __init__(self, parent, _):
309 global widgets_list 310 # base clase 311 pb_editor.DirtyUIBase.__init__(self, parent) 312 # structure to hold all the widgets of this panel 313 self._fields=[ 314 ['description', 'Summary:', DVTextControl, None, None, None], 315 ['location', 'Location:', DVTextControl, None, None, None], 316 ['allday', 'All-Day:', wx.CheckBox, None, None, None], 317 ['start', 'From:', DVDateTimeControl, None, self._set_start_datetime, None], 318 ['end', 'To:', DVDateTimeControl, None, self._set_end_datetime, None], 319 ['priority', 'Priority:', None, self._get_priority, self._set_priority, None], 320 ['alarm', 'Alarm:', DVIntControl, None, None, None], 321 ['vibrate', 'Vibrate:', wx.CheckBox, None, None, None], 322 ] 323 # overall container 324 vbs=wx.StaticBoxSizer(wx.StaticBox(self, -1), wx.VERTICAL) 325 # instantiate the widgets 326 self._w={} 327 gs=wx.FlexGridSizer(-1,2,5,5) 328 gs.AddGrowableCol(1) 329 for n in self._fields: 330 desc=n[self._label_index] 331 t=wx.StaticText(self, -1, desc, style=wx.ALIGN_LEFT) 332 widgets_list.append((t, n[self._dict_key_index])) 333 gs.Add(t) 334 if desc=='Priority:': 335 c=wx.ComboBox(self, -1, "", (-1, -1), (-1, -1), 336 ['<None>', '1 - Highest', '2', '3', '4', '5 - Normal', 337 '6', '7' ,'8', '9', '10 - Lowest'], wx.CB_DROPDOWN) 338 else: 339 c=n[self._class_index](self, -1) 340 gs.Add(c, 0, wx.EXPAND, 0) 341 n[self._w_index]=self._w[n[self._dict_key_index]]=c 342 vbs.Add(gs, 0, wx.EXPAND|wx.ALL, 5) 343 # event handlers 344 wx.EVT_CHECKBOX(self, self._w['allday'].GetId(), self.OnAllday) 345 wx.EVT_CHECKBOX(self, self._w['vibrate'].GetId(), self.OnDirtyUI) 346 wx.EVT_COMBOBOX(self, self._w['priority'].GetId(), self.OnDirtyUI) 347 # all done 348 self.SetSizer(vbs) 349 self.SetAutoLayout(True) 350 vbs.Fit(self)
351
352 - def OnMakeDirty(self, evt):
353 self.OnDirtyUI(evt)
354
355 - def OnAllday(self, evt):
356 v=evt.IsChecked() 357 self._w['start'].SetAllday(v) 358 self._w['end'].SetAllday(v) 359 self.OnDirtyUI(evt)
360
361 - def IsValid(self):
362 # validate and return T if so or F otherwise 363 # check for valid date/time entries 364 for w in (self._w['start'], self._w['end']): 365 if not w.IsValid() or w.IsEmpty(): 366 w.SetFocus() 367 wx.Bell() 368 return False 369 # whine if end is before start 370 start=datetime.datetime(*self._w['start'].GetValue()) 371 end=datetime.datetime(*self._w['end'].GetValue()) 372 if start>end: 373 # scold the user 374 guihelper.MessageDialog(self, "End date and time is before start!", "Time Travel Attempt Detected", 375 wx.OK|wx.ICON_EXCLAMATION) 376 # move focus 377 self._w['end'].SetFocus() 378 return False 379 return True
380
381 - def Set(self, data):
382 self.ignore_dirty=True 383 if data is None: 384 for n in self._fields: 385 n[self._w_index].Enable(False) 386 else: 387 for n in self._fields: 388 w=n[self._w_index] 389 w.Enable(True) 390 if n[self._set_index] is None: 391 w.SetValue(getattr(data, n[self._dict_key_index])) 392 else: 393 n[self._set_index](w, data) 394 self.ignore_dirty=self.dirty=False
395
396 - def Get(self, data):
397 self.ignore_dirty=self.dirty=False 398 if data is None: 399 return 400 for n in self._fields: 401 w=n[self._w_index] 402 if n[self._get_index] is None: 403 v=w.GetValue() 404 else: 405 v=n[self._get_index](w, None) 406 setattr(data, n[self._dict_key_index], v)
407
408 - def _set_priority(self, w, entry):
409 p=entry.priority 410 if p is None: 411 w.SetSelection(0) 412 else: 413 w.SetSelection(p)
414 - def _get_priority(self, w, _):
415 s=w.GetSelection() 416 if s: 417 return s 418 else: 419 return None
420
421 - def _set_start_datetime(self, w, entry):
422 w.SetAllday(entry.allday, entry.start)
423
424 - def _set_end_datetime(self, w, entry):
425 w.SetAllday(entry.allday, entry.end)
426 427 #------------------------------------------------------------------------------
428 -class CategoryEditor(pb_editor.DirtyUIBase):
429 430 # we have to have an entry with a special string for the unnamed string 431 432 unnamed="Select:" 433
434 - def __init__(self, parent, pos):
435 global widgets_list 436 437 pb_editor.DirtyUIBase.__init__(self, parent) 438 self.static_box=wx.StaticBox(self, -1, "Category") 439 hs=wx.StaticBoxSizer(self.static_box, wx.HORIZONTAL) 440 441 self.categories=[] 442 self.category=wx.ListBox(self, -1, choices=self.categories) 443 pubsub.subscribe(self.OnUpdateCategories, pubsub.ALL_CATEGORIES) 444 pubsub.publish(pubsub.REQUEST_CATEGORIES) 445 # a boxsizer for the master category list 446 vbs=wx.BoxSizer(wx.VERTICAL) 447 vbs.Add(wx.StaticText(self, -1, 'Master Category'), 0, 448 wx.TOP|wx.LEFT, 5) 449 vbs.Add(self.category, 1, wx.EXPAND|wx.ALL, 5) 450 hs.Add(vbs, 1, wx.EXPAND|wx.ALL, 5) 451 # a boxsizer for the buttons 452 vbs=wx.BoxSizer(wx.VERTICAL) 453 self.but=wx.Button(self, wx.NewId(), "Manage Categories:") 454 add_btn=wx.Button(self, -1, 'Add ->') 455 del_btn=wx.Button(self, -1, '<- Remove') 456 vbs.Add(self.but, 0, wx.ALIGN_CENTRE|wx.ALL, 5) 457 vbs.Add(add_btn, 0, wx.ALIGN_CENTRE|wx.ALL, 5) 458 vbs.Add(del_btn, 0, wx.ALIGN_CENTRE|wx.ALL, 5) 459 hs.Add(vbs, 0, wx.ALIGN_CENTRE|wx.ALL, 5) 460 wx.EVT_BUTTON(self, add_btn.GetId(), self.OnAddCategory) 461 wx.EVT_BUTTON(self, del_btn.GetId(), self.OnDelCategory) 462 # box sizer for the selected category 463 vbs=wx.BoxSizer(wx.VERTICAL) 464 vbs.Add(wx.StaticText(self, -1, 'Selected Category:'), 0, 465 wx.TOP|wx.LEFT, 5) 466 self._my_category=wx.ListBox(self, -1) 467 vbs.Add(self._my_category, 1, wx.EXPAND|wx.ALL, 5) 468 hs.Add(vbs, 1, wx.EXPAND|wx.ALL, 5) 469 wx.EVT_BUTTON(self, self.but.GetId(), self.OnManageCategories) 470 471 self.SetSizer(hs) 472 hs.Fit(self)
473
474 - def OnManageCategories(self, _):
475 with guihelper.WXDialogWrapper(pb_editor.CategoryManager(self), 476 True): 477 pass
478
479 - def OnUpdateCategories(self, msg):
480 cats=msg.data[:] 481 if self.categories!=cats: 482 self.categories=cats 483 sel=self.category.GetStringSelection() 484 self.category.Clear() 485 for i in cats: 486 self.category.Append(i) 487 try: 488 if len(sel): 489 self.category.SetStringSelection(sel) 490 except: 491 pass
492
493 - def Get(self):
494 self.ignore_dirty=self.dirty=False 495 r=[] 496 count=self._my_category.GetCount() 497 if count==0: 498 return r 499 for i in range(count): 500 r.append({ 'category': self._my_category.GetString(i) }) 501 return r
502
503 - def Set(self, data):
504 self.ignore_dirty=True 505 self._my_category.Clear() 506 if data is None or len(data)==0: 507 # none or empty list, do nothing 508 return 509 for n in data: 510 v=n.get('category', None) 511 if v is not None: 512 self._my_category.Append(v) 513 self.ignore_dirty=self.dirty=False
514
515 - def OnAddCategory(self, evt):
516 v=self.category.GetStringSelection() 517 if not len(v): 518 # no selection made, do nothing 519 return 520 self.ignore_dirty=True 521 self._my_category.Append(v) 522 self._my_category.SetStringSelection(v) 523 self.ignore_dirty=False 524 self.OnDirtyUI(evt)
525
526 - def OnDelCategory(self, evt):
527 v=self._my_category.GetSelection() 528 if v==wx.NOT_FOUND: 529 # no selection, do nothing 530 return 531 self.ignore_dirty=True 532 self._my_category.Delete(v) 533 self.ignore_dirty=False 534 self.OnDirtyUI(evt)
535 536 #------------------------------------------------------------------------------
537 -class Editor(wx.Dialog):
538 539 # results on asking if the user wants to change the original (repeating) entry, just 540 # this instance, or cancel 541 ANSWER_ORIGINAL=1 542 ANSWER_THIS=2 543 ANSWER_CANCEL=3 544 _dict_key_index=0 545 _label_index=1 546 _get_index=2 547 _set_index=3 548 _w_index=4 549 # notebook items 550 _general_page=0 551 _repeat_page=1 552 _notes_page=2 553 _categories_page=3 554 _wallpapers_page=4 555 _ringtones_page=5 556 _last_page=6 557 _items=[ 558 ("General", None, GeneralEditor, None), 559 ("Repeat", 'repeat', RepeatEditor, None), 560 ("Notes", "notes", pb_editor.MemoEditor, 'memo'), 561 ("Categories", "categories", CategoryEditor, 'category'), 562 ("Wallpapers", "wallpapers", pb_editor.WallpaperEditor, 'wallpaper'), 563 ("Ringtones", "ringtones", pb_editor.RingtoneEditor, 'ringtone'), 564 ] 565 color_field_name='calendar' 566
567 - def __init__(self, parent):
568 global widgets_list 569 wx.Dialog.__init__(self, parent, -1, 'Calendar Entry Editor', 570 wx.DefaultPosition, 571 style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER) 572 # the parent is the BPCalenda widget, save it 573 self.cw=parent # also the BPCalendar object 574 # Tracking of the entries in the listbox. Each entry is a dict. Entries are just the 575 # entries in a random order. entrymap maps from the order in the listbox to a 576 # specific entry 577 self.entries=[] 578 self.entrymap=[] 579 self._current_entry=None 580 581 # Dirty tracking. We restrict what the user can do while editting an 582 # entry to only be able to edit that entry. 'dirty' gets fired when 583 # they make any updates. Annoyingly, controls generate change events 584 # when they are updated programmatically as well as by user interaction. 585 # ignoredirty is set when we are programmatically updating controls 586 self.dirty=None 587 self.ignoredirty=True 588 589 # overall container 590 vbs=wx.BoxSizer(wx.VERTICAL) 591 592 self._prev_btn=wx.BitmapButton(self, wx.NewId(), wx.ArtProvider.GetBitmap(guihelper.ART_ARROW_LEFT), name="Previous Day") 593 self._next_btn=wx.BitmapButton(self, wx.NewId(), wx.ArtProvider.GetBitmap(guihelper.ART_ARROW_RIGHT), name="Next Day") 594 self.title=wx.StaticText(self, -1, "Date here", style=wx.ALIGN_CENTRE|wx.ST_NO_AUTORESIZE) 595 596 # top row container 597 hbs1=wx.BoxSizer(wx.HORIZONTAL) 598 hbs1.Add(self._prev_btn, 0, wx.EXPAND) 599 hbs1.Add(self.title, 1, wx.EXPAND) 600 hbs1.Add(self._next_btn, 0, wx.EXPAND) 601 vbs.Add(hbs1, 0, wx.TOP|wx.EXPAND, 10) 602 603 # list box and two buttons below 604 self.listbox=wx.ListBox(self, wx.NewId(), style=wx.LB_SINGLE|wx.LB_HSCROLL|wx.LB_NEEDED_SB) 605 self._add_btn=wx.Button(self, wx.ID_NEW) 606 hbs2=wx.BoxSizer(wx.HORIZONTAL) 607 hbs2.Add(self._add_btn, 1, wx.ALIGN_CENTER|wx.LEFT|wx.RIGHT, border=5) 608 609 # sizer for listbox 610 lbs=wx.BoxSizer(wx.VERTICAL) 611 lbs.Add(self.listbox, 1, wx.EXPAND|wx.BOTTOM, border=5) 612 lbs.Add(hbs2, 0, wx.EXPAND) 613 614 # right hand bit with all fields 615 self._nb=wx.Notebook(self, -1) 616 self._widgets=[] 617 for i, (name, key, klass, color_name) in enumerate(self._items): 618 if name in ('Ringtones', 'Wallpapers'): 619 self._widgets.append(klass(self._nb, parent, False)) 620 else: 621 self._widgets.append(klass(self._nb, parent)) 622 self._nb.AddPage(self._widgets[i], name) 623 if color_name: 624 widgets_list.append((self._widgets[i].static_box, color_name)) 625 626 # buttons below fields 627 self._delete_btn=wx.Button(self, wx.ID_DELETE) 628 self._revert_btn=wx.Button(self, wx.ID_REVERT_TO_SAVED) 629 self._save_btn=wx.Button(self, wx.ID_SAVE) 630 631 hbs4=wx.BoxSizer(wx.HORIZONTAL) 632 hbs4.Add(self._delete_btn, 1, wx.ALIGN_CENTRE|wx.LEFT, border=10) 633 hbs4.Add(self._revert_btn, 1, wx.ALIGN_CENTRE|wx.LEFT|wx.RIGHT, border=10) 634 hbs4.Add(self._save_btn, 1, wx.ALIGN_CENTRE|wx.RIGHT, border=10) 635 636 # fields and buttons together 637 vbs2=wx.BoxSizer(wx.VERTICAL) 638 vbs2.Add(self._nb, 1, wx.EXPAND|wx.BOTTOM, border=5) 639 vbs2.Add(hbs4, 0, wx.EXPAND|wx.ALIGN_CENTRE) 640 641 # container for everything below title row 642 hbs3=wx.BoxSizer(wx.HORIZONTAL) 643 hbs3.Add(lbs, 1, wx.EXPAND|wx.ALL, 5) 644 hbs3.Add(vbs2, 2, wx.EXPAND|wx.ALL, 5) 645 vbs.Add(hbs3, 1, wx.EXPAND) 646 # the standard buttons 647 vbs.Add(wx.StaticLine(self, -1, style=wx.LI_HORIZONTAL), 0, wx.EXPAND|wx.ALL, 5) 648 vbs.Add(self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.HELP), 0, wx.ALIGN_CENTRE|wx.ALL, 5) 649 650 self.SetSizer(vbs) 651 self.SetAutoLayout(True) 652 vbs.Fit(self) 653 # delete is disabled until an item is selected 654 self._delete_btn.Enable(False) 655 656 wx.EVT_LISTBOX(self, self.listbox.GetId(), self.OnListBoxItem) 657 wx.EVT_LISTBOX_DCLICK(self, self.listbox.GetId(), self.OnListBoxItem) 658 wx.EVT_BUTTON(self, wx.ID_SAVE, self.OnSaveButton) 659 wx.EVT_BUTTON(self, wx.ID_REVERT_TO_SAVED, self.OnRevertButton) 660 wx.EVT_BUTTON(self, wx.ID_NEW, self.OnNewButton) 661 wx.EVT_BUTTON(self, wx.ID_DELETE, self.OnDeleteButton) 662 wx.EVT_BUTTON(self, self._prev_btn.GetId(), self.OnPrevDayButton) 663 wx.EVT_BUTTON(self, self._next_btn.GetId(), self.OnNextDayButton) 664 # callbacks for the standard buttons 665 wx.EVT_BUTTON(self, wx.ID_OK, self.OnOk) 666 wx.EVT_BUTTON(self, wx.ID_CANCEL, self.OnCancel) 667 wx.EVT_BUTTON(self, wx.ID_HELP, lambda _: wx.GetApp().displayhelpid(helpids.ID_EDITING_CALENDAR_EVENTS)) 668 # DIRTY UI Event handlers 669 for w in self._widgets: 670 pb_editor.EVT_DIRTY_UI(self, w.GetId(), self.OnMakeDirty) 671 self.ignoredirty=False 672 self.setdirty(False) 673 guiwidgets.set_size("CalendarEntryEditor", self, 52, 1.0) 674 field_color.reload_color_info(self, widgets_list) 675 pubsub.subscribe(self.OnPhoneChanged, pubsub.PHONE_MODEL_CHANGED)
676
677 - def OnPhoneChanged(self, _):
678 # just reload the color info based on the new phone 679 field_color.reload_color_info(self, widgets_list) 680 self.Refresh()
681
682 - def OnListBoxItem(self, evt=None):
683 """Callback for when user clicks on an event in the listbox""" 684 self._current_entry=self.getcurrententry() 685 if self._current_entry: 686 self.updatefields(self._current_entry) 687 self.setdirty(False) 688 self._delete_btn.Enable(True)
689
690 - def getcurrententry(self):
691 """Returns the entry currently being viewed 692 693 @Note: this returns the unedited form of the entry""" 694 i=self.listbox.GetSelection() 695 if i==-1: 696 return None 697 return self.getentry(i)
698
699 - def getentry(self, num):
700 """maps from entry number in listbox to an entry in entries 701 702 @type num: int 703 @rtype: entry(dict)""" 704 return self.entries[self.entrymap[num]]
705
706 - def OnSaveButton(self, evt):
707 """Callback for when user presses save""" 708 709 # check if the dates are ok 710 if not self._widgets[self._general_page].IsValid(): 711 return 712 713 # lets roll .. 714 ## entry=self.getcurrententry() 715 entry=self._current_entry 716 717 # is it a repeat? 718 res=self.ANSWER_ORIGINAL 719 if entry.repeat is not None: 720 # ask the user 721 res=self.AskAboutRepeatChange() 722 if res==self.ANSWER_CANCEL: 723 return 724 # where do we get newentry template from? 725 if res==self.ANSWER_ORIGINAL: 726 newentry=copy.copy(entry) 727 else: 728 newentry=self.cw.newentryfactory(*self.date) 729 730 # update the fields from the general tab 731 self._widgets[self._general_page].Get(newentry) 732 if res==self.ANSWER_THIS: 733 # change this event only, change the start & end date to this date 734 newentry.start=list(self.date)+list(newentry.start[3:]) 735 newentry.end=list(self.date)+list(newentry.end[3:]) 736 # if we are changing a repeat, reset the new entry's repeat is off 737 newentry.repeat=None 738 else: 739 # get data from the repeat tab 740 newentry.repeat=self._widgets[self._repeat_page].Get() 741 # and other tabs as well 742 newentry.notes=self._widgets[self._notes_page].Get().get('memo', None) 743 newentry.categories=self._widgets[self._categories_page].Get() 744 newentry.wallpaper=self._widgets[self._wallpapers_page].Get().get('wallpaper', None) 745 newentry.ringtone=self._widgets[self._ringtones_page].Get().get('ringtone', None) 746 # got the data 747 # update calendar widget 748 if res==self.ANSWER_ORIGINAL: 749 self.cw.ChangeEntry(entry, newentry) 750 else: 751 # delete the repeat and add this new entry 752 self.cw.DeleteEntryRepeat(entry, *self.date) 753 self.cw.AddEntry(newentry) 754 if __debug__: 755 print 'Editor.OnSaveButton: updated entry:' 756 print newentry.get() 757 print 'Equivalent DB dict:' 758 print bpcalendar.CalendarDataObject(newentry) 759 # tidy up 760 self.setdirty(False) 761 # did the user change the date on us? 762 date=tuple(newentry.start[:3]) 763 if tuple(self.date)!=date: 764 self.cw.showday(*date) 765 self.cw.setselection(*date) 766 self.setdate(*date) 767 else: 768 self.refreshentries() 769 self.updatelistbox(newentry.id)
770
771 - def OnOk(self, evt):
772 # save the current entry & exit 773 guiwidgets.save_size("CalendarEntryEditor", self.GetRect()) 774 if self.dirty: 775 self.OnSaveButton(None) 776 self.setdirty(False) 777 evt.Skip()
778
779 - def OnCancel(self, evt):
780 # just exit 781 guiwidgets.save_size("CalendarEntryEditor", self.GetRect()) 782 self.setdirty(False) 783 evt.Skip()
784
785 - def OnRevertButton(self, evt):
786 # We basically pretend the user has selected the item in the listbox again (which they 787 # can't actually do as it is disabled 788 self.listbox.Enable() 789 self.OnListBoxItem()
790
791 - def OnNewButton(self, evt):
792 entry=self.cw.newentryfactory(*self.date) 793 self.cw.AddEntry(entry) 794 self.refreshentries() 795 self.updatelistbox(entry.id)
796
797 - def OnDeleteButton(self, evt):
798 entry=self._current_entry 799 if entry is None: 800 return 801 # is it a repeat? 802 res=self.ANSWER_ORIGINAL 803 if entry.repeat is not None: 804 # ask the user 805 res=self.AskAboutRepeatDelete() 806 if res==self.ANSWER_CANCEL: 807 return 808 enum=self.listbox.GetSelection() 809 if enum+1<len(self.entrymap): 810 # try and find entry after current one 811 newpos=self.getentry(enum+1).id 812 elif enum-1>=0: 813 # entry before as we are deleting last entry 814 newpos=self.getentry(enum-1).id 815 else: 816 newpos=None 817 if res==self.ANSWER_ORIGINAL: 818 self.cw.DeleteEntry(entry) 819 else: 820 self.cw.DeleteEntryRepeat(entry, *self.date) 821 self.setdirty(False) 822 self.refreshentries() 823 self.updatelistbox(newpos)
824
825 - def OnPrevDayButton(self, evt):
826 d=datetime.date(*self.date)-datetime.timedelta(1) 827 self.setdate(d.year, d.month, d.day) 828 self.cw.setday(d.year, d.month, d.day)
829
830 - def OnNextDayButton(self, evt):
831 d=datetime.date(*self.date)+datetime.timedelta(1) 832 self.setdate(d.year, d.month, d.day) 833 self.cw.setday(d.year, d.month, d.day)
834
835 - def setdate(self, year, month, day, entry=None):
836 """Sets the date we are editing entries for 837 838 @Note: The list of entries is updated""" 839 d=time.strftime("%A %d %B %Y", (year,month,day,0,0,0, calendar.weekday(year,month,day),1, 0)) 840 self.date=year,month,day 841 self.title.SetLabel(d) 842 self.refreshentries() 843 self.updatelistbox(entry and entry.id or None) 844 self.updatefields(entry)
845
846 - def refreshentries(self):
847 """re-requests the list of entries for the currently visible date from the main calendar""" 848 self.entries=self.cw.getentrydata(*self.date)
849
850 - def updatelistbox(self, entrytoselect=None):
851 """ 852 Updates the contents of the listbox. It will re-sort the contents. 853 854 @param entrytoselect: The integer id of an entry to select. Note that 855 this is an event id, not an index 856 """ 857 self.listbox.Clear() 858 selectitem=-1 859 self.entrymap=[] 860 self._current_entry=None 861 # decorate 862 for index, entry in enumerate(self.entries): 863 if entry.allday: 864 e=( entry.start[3:5], entry.end[3:5], entry.description, index) 865 else: 866 e=(None, None, entry.description, index) 867 self.entrymap.append(e) 868 # time ordered 869 self.entrymap.sort() 870 # now undecorate 871 self.entrymap=[index for ign0, ign1, ign2, index in self.entrymap] 872 # add listbox entries 873 for curpos, index in enumerate(self.entrymap): 874 e=self.entries[index] 875 if e.id==entrytoselect: 876 selectitem=curpos 877 self.listbox.Append(e.summary) 878 879 # Select an item if requested 880 if selectitem>=0: 881 self.listbox.SetSelection(selectitem) 882 self.OnListBoxItem() # update fields 883 else: 884 # disable fields since nothing is selected 885 self.updatefields(None) 886 887 # disable delete if there are no entries! 888 if len(self.entries)==0: 889 self._delete_btn.Enable(False)
890
891 - def updatefields(self, entry):
892 self.ignoredirty=True 893 self._widgets[self._general_page].Set(entry) 894 # populate the other tabs with current entry data 895 if entry is None: 896 # clear everything out 897 for i in range(self._repeat_page, self._last_page): 898 self._widgets[i].Set(None) 899 self._widgets[i].Enable(False) 900 return 901 # there's data, setting each page accordingly 902 for i in range(self._repeat_page, self._last_page): 903 self._widgets[i].Enable(True) 904 self._widgets[self._repeat_page].Set(entry.repeat) 905 self._widgets[self._notes_page].Set({ 'memo': entry.notes }) 906 self._widgets[self._categories_page].Set(entry.categories) 907 self._widgets[self._wallpapers_page].Set( \ 908 { 'wallpaper': entry.wallpaper, 'type': 'calendar' }) 909 self._widgets[self._ringtones_page].Set( \ 910 { 'ringtone': entry.ringtone, 'type': 'calendar' }) 911 self.ignoredirty=False
912 913 # called from various widget update callbacks
914 - def OnMakeDirty(self, _=None):
915 """A public function you can call that will set the dirty flag""" 916 if self.dirty or self.ignoredirty or not self.IsShown(): 917 # already dirty, no need to make it worse 918 return 919 self.setdirty(True)
920
921 - def setdirty(self, val):
922 """Set the dirty flag 923 924 The various buttons in the dialog are enabled/disabled as appropriate 925 for the new state. 926 927 @type val: Bool 928 @param val: True to mark edit fields as different from entry (ie 929 editing has taken place) 930 False to make them as the same as the entry (ie no 931 editing or the edits have been discarded) 932 """ 933 if self.ignoredirty: 934 return 935 self.dirty=val 936 if self.dirty: 937 # The data has been modified, so we only allow working 938 # with this data 939 940 # enable save, revert, delete 941 self._save_btn.Enable(True) 942 self._revert_btn.Enable(True) 943 # disable close, left, right, new 944 self._prev_btn.Enable(False) 945 self._next_btn.Enable(False) 946 self._add_btn.Enable(False) 947 # can't play with listbox now 948 self.listbox.Enable(False) 949 else: 950 # The data is now clean and saved/reverted or deleted 951 952 # disable save, revert, 953 self._save_btn.Enable(False) 954 self._revert_btn.Enable(False) 955 956 # enable delete, close, left, right, new 957 self._delete_btn.Enable(len(self.entries)>0) # only enable if there are entries 958 self._prev_btn.Enable(True) 959 self._next_btn.Enable(True) 960 self._add_btn.Enable(True) 961 962 # can choose another item in listbox 963 self.listbox.Enable(True)
964
965 - def AskAboutRepeatDelete(self):
966 """Asks the user if they wish to delete the original (repeating) entry, or this instance 967 968 @return: An C{ANSWER_} constant 969 """ 970 return self._AskAboutRecurringEvent("Delete recurring event?", "Do you want to delete all the recurring events, or just this one?", "Delete")
971
972 - def AskAboutRepeatChange(self):
973 """Asks the user if they wish to change the original (repeating) entry, or this instance 974 975 @return: An C{ANSWER_} constant 976 """ 977 return self._AskAboutRecurringEvent("Change recurring event?", "Do you want to change all the recurring events, or just this one?", "Change")
978
979 - def _AskAboutRecurringEvent(self, caption, text, prefix):
980 with guihelper.WXDialogWrapper(RecurringDialog(self, caption, text, prefix), 981 True) as (dlg, res): 982 if res==dlg.ID_THIS: 983 return self.ANSWER_THIS 984 if res==dlg.ID_ALL: 985 return self.ANSWER_ORIGINAL 986 if res==dlg.ID_CANCEL: 987 return self.ANSWER_CANCEL 988 assert False
989 #------------------------------------------------------------------------------ 990 # We derive from wxPanel not the control directly. If we derive from 991 # wx.MaskedTextCtrl then all hell breaks loose as our {Get|Set}Value 992 # methods make the control malfunction big time
993 -class DVDateTimeControl(wx.Panel):
994 """A datetime control customised to work in the dayview editor"""
995 - def __init__(self,parent,id):
996 self._allday=False 997 self._datetime_format="EUDATETIMEYYYYMMDD.HHMM" 998 self._date_format='EUDATEYYYYMMDD.' 999 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,int(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