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

Source Code for Module vcal_calendar

  1  ### BITPIM 
  2  ### 
  3  ### Copyright (C) 2004 Joe Pham <djpham@bitpim.org> 
  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: vcal_calendar.py 4708 2008-09-06 04:10:44Z djpham $ 
  9   
 10  "Deals with vcard calendar import stuff" 
 11   
 12  # system modules 
 13  from __future__ import with_statement 
 14  import contextlib 
 15  import copy 
 16  import datetime 
 17   
 18  # site modules 
 19  import wx 
 20   
 21  # local modules 
 22  import bpcalendar 
 23  import bptime 
 24  import common_calendar 
 25  import guihelper 
 26  import helpids 
 27  import vcard 
 28   
 29  module_debug=False 
30 31 #------------------------------------------------------------------------------- 32 -class ImportDataSource(common_calendar.ImportDataSource):
33 # how to define, and retrieve calendar import data source 34 message_str="Pick a vCal Calendar File" 35 wildcard='*.vcs;*.ics'
36
37 #------------------------------------------------------------------------------- 38 -class vCalendarFile(object):
39 - def __init__(self, file_name=None):
40 self._data=[] 41 self._file_name=file_name
42
43 - def _open(self, name):
44 return file(name, 'rt')
45
46 - def _read_block(self, vfile):
47 """Read a BEGIN/END block and return a dict of values/params 48 """ 49 global module_debug 50 d={} 51 _inblk=False 52 for n,l in vfile: 53 if n[0]=='BEGIN': 54 _blkname=l 55 _inblk=True 56 _vdata=[] 57 elif n[0]=='END' and l==_blkname: 58 d['BEGIN-END']={ 'value': l, 59 'params': self._read_block(_vdata) } 60 _inblk=False 61 elif _inblk: 62 _vdata.append((n, l)) 63 else: 64 _params={} 65 for _item in n[1:]: 66 _l=_item.split('=') 67 if len(_l)==1: 68 _params[_l[0]]=None 69 else: 70 _params[_l[0]]=_l[1] 71 # Some statement, i.e. EXDATE, may occur more than once, 72 # in such cases, place the values into the list 73 # This may break existing cases, but we'll fix them as 74 # users report them 75 _val={ 'value': l, 'params': _params } 76 if d.has_key(n[0]): 77 # multiple statements 78 if isinstance(d[n[0]], dict): 79 d[n[0]]=[d[n[0]], _val] 80 else: 81 d[n[0]].append(_val) 82 else: 83 d[n[0]]=_val 84 if module_debug: 85 print d,'\n' 86 return d
87
88 - def read(self, file_name=None):
89 self._data=[] 90 if file_name is not None: 91 self._file_name=file_name 92 if self._file_name is None: 93 # no file name specified 94 return 95 try: 96 with contextlib.closing(self._open(self._file_name)) as f: 97 vfile=vcard.VFile(f) 98 has_data=False 99 for n,l in vfile: 100 if n[0]=='BEGIN' and l=='VEVENT': 101 has_data=True 102 _vdata=[] 103 elif n[0]=='END' and l=='VEVENT': 104 has_data=False 105 self._data.append(self._read_block(_vdata)) 106 elif has_data: 107 _vdata.append((n, l)) 108 except: 109 if __debug__: 110 raise
111
112 - def _get_data(self):
113 return copy.deepcopy(self._data)
114 data=property(fget=_get_data)
115
116 #------------------------------------------------------------------------------- 117 -class VCalendarImportData(object):
118 119 _default_filter={ 120 'start': None, 121 'end': None, 122 'categories': None, 123 'rpt_events': False, 124 'no_alarm': False, 125 'ringtone': None, 126 'alarm_override':False, 127 'vibrate':False, 128 'alarm_value':0 129 } 130 _rrule_dow={ 131 'SU': 0x01, 'MO': 0x02, 'TU': 0x04, 'WE': 0x08, 'TH': 0x10, 132 'FR': 0x20, 'SA': 0x40 } 133 _rrule_weekday=_rrule_dow['MO']|_rrule_dow['TU']|\ 134 _rrule_dow['WE']|_rrule_dow['TH']|\ 135 _rrule_dow['FR'] 136 _source_data_class=vCalendarFile 137
138 - def __init__(self, file_name=None):
139 self._file_name=file_name 140 self._data=[] 141 self._filter=self._default_filter 142 self.read()
143
144 - def _accept(self, entry):
145 # start & end time within specified filter 146 if entry.get('repeat', False): 147 # repeat event 148 # need to populate to get an accurate end date 149 ce=bpcalendar.CalendarEntry() 150 self._populate_entry(entry, ce) 151 if self._filter['start'] is not None and \ 152 ce.end[:3]<self._filter['start'][:3]: 153 # event ends before our rannge 154 return False 155 if self._filter['end'] is not None and \ 156 ce.start[:3]>self._filter['end'][:3]: 157 # event starts after our range 158 return False 159 else: 160 # single event 161 if self._filter['start'] is not None and \ 162 entry['start'][:3]<self._filter['start'][:3]: 163 return False 164 if self._filter['end'] is not None and \ 165 entry['end'][:3]>self._filter['end'][:3] and \ 166 entry['end'][:3]!=common_calendar.no_end_date[:3]: 167 return False 168 # check the catefory 169 c=self._filter['categories'] 170 if c is None or not len(c): 171 # no categories specified => all catefories allowed. 172 return True 173 if len([x for x in entry['categories'] if x in c]): 174 return True 175 return False
176
177 - def _populate_repeat_entry(self, e, ce):
178 # populate repeat entry data 179 if not e.get('repeat', False) or e.get('repeat_type', None) is None: 180 # not a repeat event 181 return 182 rp=bpcalendar.RepeatEntry() 183 rp_type=e['repeat_type'] 184 rp_interval=e.get('repeat_interval', 1) 185 rp_interval2=e.get('repeat_interval2', 1) 186 rp_end=e.get('repeat_end', None) 187 rp_num=e.get('repeat_num', None) 188 rp_dow=e.get('repeat_dow', 0) 189 if rp_type==rp.daily: 190 # daily event 191 rp.repeat_type=rp.daily 192 rp.interval=rp_interval 193 elif rp_type==rp.weekly or rp_type==rp.monthly: 194 rp.repeat_type=rp_type 195 rp.interval=rp_interval 196 rp.interval2=rp_interval2 197 rp.dow=rp_dow 198 elif rp_type==rp.yearly: 199 rp.repeat_type=rp.yearly 200 else: 201 # not yet supported 202 return 203 rp.weekstart=e.get('repeat_wkst', 'MO') 204 # setting the repeat duration/end-date of this event 205 if rp_end is not None: 206 # end date specified 207 ce.end=rp_end[:3]+ce.end[3:] 208 elif rp_num: 209 # num of occurrences specified 210 _dt=ce.start[:3] 211 for i in range(rp_num-1): 212 _dt=rp.next_date(_dt) 213 ce.end=_dt[:3]+ce.end[3:] 214 else: 215 # forever duration 216 ce.end=common_calendar.no_end_date[:3]+ce.end[3:] 217 # add the list of exceptions 218 for k in e.get('exceptions', []): 219 rp.add_suppressed(*k[:3]) 220 # all done 221 ce.repeat=rp
222
223 - def _populate_entry(self, e, ce):
224 # populate an calendar entry with data 225 ce.description=e.get('description', None) 226 ce.location=e.get('location', None) 227 v=e.get('priority', None) 228 if v is not None: 229 ce.priority=v 230 if not self._filter.get('no_alarm', False) and \ 231 not self._filter.get('alarm_override', False) and \ 232 e.get('alarm', False): 233 ce.alarm=e.get('alarm_value', 0) 234 ce.ringtone=self._filter.get('ringtone', "") 235 ce.vibrate=self._filter.get('vibrate', False) 236 elif not self._filter.get('no_alarm', False) and \ 237 self._filter.get('alarm_override', False): 238 ce.alarm=self._filter.get('alarm_value', 0) 239 ce.ringtone=self._filter.get('ringtone', "") 240 ce.vibrate=self._filter.get('vibrate', False) 241 ce_start=e.get('start', None) 242 ce_end=e.get('end', None) 243 if ce_start is None and ce_end is None: 244 raise ValueError, "No start or end datetime" 245 if ce_start is not None: 246 ce.start=ce_start 247 if ce_end is not None: 248 ce.end=ce_end 249 if ce_start is None: 250 ce.start=ce.end 251 elif ce_end is None: 252 ce.end=ce.start 253 ce.notes=e.get('notes', None) 254 v=[] 255 for k in e.get('categories', []): 256 v.append({ 'category': k }) 257 ce.categories=v 258 # look at repeat 259 self._populate_repeat_entry(e, ce) 260 ce.allday=e.get('allday', False)
261
262 - def _generate_repeat_events(self, e):
263 # generate multiple single events from this repeat event 264 ce=bpcalendar.CalendarEntry() 265 self._populate_entry(e, ce) 266 l=[] 267 new_e=e.copy() 268 new_e['repeat']=False 269 for k in ('repeat_type', 'repeat_interval', 'repeat_dow'): 270 if new_e.has_key(k): 271 del new_e[k] 272 s_date=datetime.datetime(*self._filter['start']) 273 e_date=datetime.datetime(*self._filter['end']) 274 one_day=datetime.timedelta(1) 275 this_date=s_date 276 while this_date<=e_date: 277 date_l=(this_date.year, this_date.month, this_date.day) 278 if ce.is_active(*date_l): 279 new_e['start']=date_l+new_e['start'][3:] 280 new_e['end']=date_l+new_e['end'][3:] 281 l.append(new_e.copy()) 282 this_date+=one_day 283 return l
284
285 - def get(self):
286 global module_debug 287 res={} 288 single_rpt=self._filter.get('rpt_events', False) 289 for k in self._data: 290 try: 291 if self._accept(k): 292 if k.get('repeat', False) and single_rpt: 293 d=self._generate_repeat_events(k) 294 else: 295 d=[k] 296 for n in d: 297 ce=bpcalendar.CalendarEntry() 298 self._populate_entry(n, ce) 299 res[ce.id]=ce 300 except: 301 if module_debug: 302 raise 303 return res
304
305 - def get_category_list(self):
306 l=[] 307 for e in self._data: 308 l+=[x for x in e.get('categories', []) if x not in l] 309 return l
310
311 - def set_filter(self, filter):
312 self._filter=filter
313
314 - def get_filter(self):
315 return self._filter
316
317 - def _conv_cat(self, v, _):
318 return [x.strip() for x in v['value'].split(",") if len(x)]
319
320 - def _conv_alarm(self, v, dd):
321 try: 322 alarm_date=bptime.BPTime(v['value'].split(';')[0]) 323 start_date=bptime.BPTime(dd['start']) 324 if alarm_date.get()<start_date.get(): 325 dd['alarm_value']=(start_date-alarm_date).seconds/60 326 return True 327 return False 328 except: 329 return False
330
331 - def _conv_date(self, v, _):
332 return bptime.BPTime(v['value']).get()
333 - def _conv_priority(self, v, _):
334 try: 335 return int(v['value']) 336 except: 337 return None
338 - def _conv_str(self, v, _):
339 return v['value'].replace('\,', ',')
340
341 - def _process_daily_rule(self, v, dd):
342 # the rule is Dx #y or Dx YYYYMMDDTHHMM 343 s=v['value'].split(' ') 344 dd['repeat_interval']=int(s[0][1:]) 345 if len(s)==1: 346 # no duration/end date 347 return True 348 if s[1][0]=='#': 349 # duration 350 dd['repeat_num']=int(s[1][1:]) 351 else: 352 # end date 353 dd['repeat_end']=bptime.BPTime(s[1]).get() 354 dd['repeat_type']='daily' 355 return True
356
357 - def _process_weekly_rule(self, v, dd):
358 # the rule is Wx | Wx <#y|YYYYMMDDTHHMMSS> | Wx MO TU 359 s=v['value'].split(' ') 360 dd['repeat_interval']=int(s[0][1:]) 361 dow=0 362 for i in range(1, len(s)): 363 n=s[i] 364 if n[0].isdigit(): 365 dd['repeat_end']=bptime.BPTime(n).get() 366 elif n[0]=='#': 367 dd['repeat_num']=int(n[1:]) 368 else: 369 # day-of-week 370 dow=dow|self._rrule_dow.get(n, 0) 371 if dow: 372 dd['repeat_dow']=dow 373 dd['repeat_type']='weekly' 374 return True
375
376 - def _process_monthly_rule(self, v, dd):
377 global module_debug 378 try: 379 # acceptable format: MD1 <day number> <end date | #duration> 380 # or MP1 <[1-4]+ | 1-> <SU-SA> <end date | #duration> 381 s=v['value'].split(' ') 382 if s[0][:2]!='MD' and s[0][:2]!='MP': 383 return False 384 dd['repeat_interval2']=int(s[0][2:]) 385 if s[0][:2]=='MP': 386 # every nth *day of every month 387 n=s[1] 388 if n in ['1+', '2+', '3+', '4+', '1-']: 389 if n[1]=='-': 390 dd['repeat_interval']=5 391 else: 392 dd['repeat_interval']=int(n[0]) 393 else: 394 return False 395 dd['repeat_dow']=self._rrule_dow.get(s[2], 0) 396 else: 397 dd['repeat_interval']=dd['repeat_dow']=0 398 dd['repeat_type']='monthly' 399 n=s[-1] 400 if len(n)>7 and n[:8].isdigit(): 401 # end date/time specified 402 dd['repeat_end']=bptime.BPTime(n).get() 403 elif n[0]=='#': 404 dd['repeat_num']=int(n[1:]) 405 return True 406 except: 407 if module_debug: raise 408 return False
409 - def _process_yearly_rule(self, v, dd):
410 global module_debug 411 try: 412 # acceptable format YM1 <Month number> <end date | #duration> 413 s=v['value'].split(' ') 414 if s[0]!='YM1': 415 return False 416 n=s[-1] 417 if len(n)>7 and n[:8].isdigit(): 418 # end date/time specified 419 dd['repeat_end']=bptime.BPTime(n).get() 420 elif n[0]=='#': 421 dd['repeat_num']=int(n[1:]) 422 dd['repeat_type']='yearly' 423 return True 424 except: 425 if module_debug: raise 426 return False
427
428 - def _conv_repeat(self, v, dd):
429 func_dict={ 430 'D': self._process_daily_rule, 431 'W': self._process_weekly_rule, 432 'M': self._process_monthly_rule, 433 'Y': self._process_yearly_rule 434 } 435 c=v['value'][0] 436 return func_dict.get(c, lambda *arg: False)(v, dd)
437 - def _conv_exceptions(self, v, _):
438 try: 439 _val=v if isinstance(v, (list, tuple)) else [v] 440 r=[] 441 for _item in _val: 442 for n in _item['value'].split(';'): 443 r.append(bptime.BPTime(n).get()) 444 return r 445 except: 446 if __debug__: 447 raise 448 return []
449 _calendar_keys=[ 450 ('CATEGORIES', 'categories', _conv_cat), 451 ('DESCRIPTION', 'notes', _conv_str), 452 ('DTSTART', 'start', _conv_date), 453 ('DTEND', 'end', _conv_date), 454 ('LOCATION', 'location', _conv_str), 455 ('PRIORITY', 'priority', _conv_priority), 456 ('SUMMARY', 'description', _conv_str), 457 ('AALARM', 'alarm', _conv_alarm), 458 ('DALARM', 'alarm', _conv_alarm), 459 ('RRULE', 'repeat', _conv_repeat), 460 ('EXDATE', 'exceptions', _conv_exceptions), 461 ]
462 - def _convert(self, vcal, d):
463 global module_debug 464 for i in vcal: 465 try: 466 dd={'start': None, 'end': None } 467 for j in self._calendar_keys: 468 if i.has_key(j[0]): 469 k=i[j[0]] 470 if j[2] is not None: 471 dd[j[1]]=j[2](self, k, dd) 472 else: 473 dd[j[1]]=k['value'] 474 if dd['start'] is None and dd['end'] is None: 475 # no start or end, drop this one 476 continue 477 if dd['start'] is None: 478 dd['start']=dd['end'] 479 elif dd['end'] is None: 480 dd['end']=dd['start'] 481 if dd.get('allday', False) and dd['end']>dd['start']: 482 # All day event, adjust the end time as necessary 483 dd['end']=(bptime.BPTime(dd['end'])-\ 484 bptime.timedelta(days=1)).get()[:3]+(0, 0) 485 486 if module_debug: print dd 487 d.append(dd) 488 except: 489 if module_debug: raise
490
491 - def get_display_data(self):
492 cnt=0 493 res={} 494 single_rpt=self._filter.get('rpt_events', False) 495 for k in self._data: 496 if self._accept(k): 497 if k.get('repeat', False) and single_rpt: 498 d=self._generate_repeat_events(k) 499 else: 500 d=[k.copy()] 501 for n in d: 502 if self._filter.get('no_alarm', False): 503 n['alarm']=False 504 res[cnt]=n 505 cnt+=1 506 return res
507
508 - def get_file_name(self):
509 if self._file_name is not None: 510 return self._file_name 511 return ''
512
513 - def read(self, file_name=None, update_dlg=None):
514 if file_name is not None: 515 self._file_name=file_name 516 if self._file_name is None: 517 # no file name specified 518 return 519 v=self._source_data_class(self._file_name) 520 v.read() 521 self._convert(v.data, self._data)
522
523 #------------------------------------------------------------------------------- 524 -class VcalImportCalDialog(common_calendar.PreviewDialog):
525 _column_labels=[ 526 ('description', 'Description', 400, None), 527 ('start', 'Start', 150, common_calendar.bp_date_str), 528 ('end', 'End', 150, common_calendar.bp_date_str), 529 ('repeat_type', 'Repeat', 80, common_calendar.bp_repeat_str), 530 ('alarm', 'Alarm', 80, common_calendar.bp_alarm_str), 531 ('categories', 'Category', 150, common_calendar.category_str) 532 ] 533 _filetype_label="VCalendar File:" 534 _data_type='vCalendar' 535 _import_data_class=VCalendarImportData
536 - def __init__(self, parent, id, title):
537 self._oc=self._import_data_class() 538 common_calendar.PreviewDialog.__init__(self, parent, id, title, 539 self._column_labels, 540 self._oc.get_display_data(), 541 config_name='import/calendar/vcaldialog')
542
543 - def getcontrols(self, main_bs):
544 hbs=wx.BoxSizer(wx.HORIZONTAL) 545 # label 546 hbs.Add(wx.StaticText(self, -1, self._filetype_label), 0, wx.ALL|wx.ALIGN_CENTRE, 2) 547 # where the folder name goes 548 self.folderctrl=wx.TextCtrl(self, -1, "") #, style=wx.TE_READONLY) 549 self.folderctrl.SetValue(self._oc.get_file_name()) 550 hbs.Add(self.folderctrl, 1, wx.EXPAND|wx.ALL, 2) 551 # browse button 552 id_browse=wx.NewId() 553 hbs.Add(wx.Button(self, id_browse, 'Browse ...'), 0, wx.EXPAND|wx.ALL, 2) 554 main_bs.Add(hbs, 0, wx.EXPAND|wx.ALL, 5) 555 main_bs.Add(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 5) 556 wx.EVT_BUTTON(self, id_browse, self.OnBrowseFolder)
557
558 - def getpostcontrols(self, main_bs):
559 main_bs.Add(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 5) 560 hbs=wx.BoxSizer(wx.HORIZONTAL) 561 id_import=wx.NewId() 562 hbs.Add(wx.Button(self, id_import, 'Import'), 0, wx.ALIGN_CENTRE|wx.ALL, 5) 563 hbs.Add(wx.Button(self, wx.ID_OK, 'Replace All'), 0, wx.ALIGN_CENTRE|wx.ALL, 5) 564 hbs.Add(wx.Button(self, self.ID_ADD, 'Add'), 0, wx.ALIGN_CENTRE|wx.ALL, 5) 565 hbs.Add(wx.Button(self, self.ID_MERGE, 'Merge'), 0, wx.ALIGN_CENTRE|wx.ALL, 5) 566 hbs.Add(wx.Button(self, wx.ID_CANCEL, 'Cancel'), 0, wx.ALIGN_CENTRE|wx.ALL, 5) 567 id_filter=wx.NewId() 568 hbs.Add(wx.Button(self, id_filter, 'Filter'), 0, wx.ALIGN_CENTRE|wx.ALL, 5) 569 hbs.Add(wx.Button(self, wx.ID_HELP, 'Help'), 0, wx.ALIGN_CENTRE|wx.ALL, 5) 570 main_bs.Add(hbs, 0, wx.ALIGN_CENTRE|wx.ALL, 5) 571 wx.EVT_BUTTON(self, id_import, self.OnImport) 572 wx.EVT_BUTTON(self, id_filter, self.OnFilter) 573 wx.EVT_BUTTON(self, self.ID_ADD, self.OnEndModal) 574 wx.EVT_BUTTON(self, self.ID_MERGE, self.OnEndModal) 575 wx.EVT_BUTTON(self, wx.ID_HELP, lambda *_: wx.GetApp().displayhelpid(helpids.ID_DLG_CALENDAR_IMPORT))
576 577 @guihelper.BusyWrapper
578 - def OnImport(self, evt):
579 with guihelper.WXDialogWrapper(wx.ProgressDialog('%s Import'%self._data_type, 580 'Importing %s Data, please wait ...'%self._data_type, 581 parent=self)) as dlg: 582 try: 583 self._oc.read(self.folderctrl.GetValue()) 584 self.populate(self._oc.get_display_data()) 585 except (ValueError, IOError): 586 guihelper.MessageDialog(self, 'Failed to get import data', 587 'Import Error', 588 style=wx.OK|wx.ICON_ERROR) 589 except: 590 if __debug__: 591 raise
592
593 - def OnBrowseFolder(self, evt):
594 with guihelper.WXDialogWrapper(wx.FileDialog(self, "Pick a %s File"%self._data_type, 595 wildcard='*.vcs;*.ics'), 596 True) as (dlg, retcode): 597 if retcode==wx.ID_OK: 598 self.folderctrl.SetValue(dlg.GetPath())
599
600 - def OnFilter(self, evt):
601 cat_list=self._oc.get_category_list() 602 with guihelper.WXDialogWrapper(common_calendar.FilterDialog(self, -1, 'Filtering Parameters', cat_list), 603 True) as (dlg, retcode): 604 if retcode==wx.ID_OK: 605 self._oc.set_filter(dlg.get()) 606 self.populate(self._oc.get_display_data())
607
608 - def OnEndModal(self, evt):
609 self.EndModal(evt.GetId())
610
611 - def get(self):
612 return self._oc.get()
613
614 - def get_categories(self):
615 return self._oc.get_category_list()
616
617 #------------------------------------------------------------------------------- 618 -def ImportCal(folder, filters):
619 _oc=VCalendarImportData(folder) 620 _oc.set_filter(filters) 621 _oc.read() 622 res={ 'calendar':_oc.get() } 623 return res
624
625 #------------------------------------------------------------------------------- 626 -class VCalAutoConfCalDialog(wx.Dialog):
627 - def __init__(self, parent, id, title, folder, filters, 628 style=wx.CAPTION|wx.MAXIMIZE_BOX| \ 629 wx.SYSTEM_MENU|wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER):
630 self._oc=VCalendarImportData() 631 self._oc.set_filter(filters) 632 self._read=False 633 wx.Dialog.__init__(self, parent, id=id, title=title, style=style) 634 main_bs=wx.BoxSizer(wx.VERTICAL) 635 hbs=wx.BoxSizer(wx.HORIZONTAL) 636 # label 637 hbs.Add(wx.StaticText(self, -1, "VCalendar File:"), 0, wx.ALL|wx.ALIGN_CENTRE, 2) 638 # where the folder name goes 639 self.folderctrl=wx.TextCtrl(self, -1, "", style=wx.TE_READONLY) 640 self.folderctrl.SetValue(folder) 641 hbs.Add(self.folderctrl, 1, wx.EXPAND|wx.ALL, 2) 642 # browse button 643 id_browse=wx.NewId() 644 hbs.Add(wx.Button(self, id_browse, 'Browse ...'), 0, wx.EXPAND|wx.ALL, 2) 645 main_bs.Add(hbs, 0, wx.EXPAND|wx.ALL, 5) 646 main_bs.Add(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 5) 647 wx.EVT_BUTTON(self, id_browse, self.OnBrowseFolder) 648 hbs=wx.BoxSizer(wx.HORIZONTAL) 649 hbs.Add(wx.Button(self, wx.ID_OK, 'OK'), 0, wx.ALIGN_CENTRE|wx.ALL, 5) 650 hbs.Add(wx.Button(self, wx.ID_CANCEL, 'Cancel'), 0, wx.ALIGN_CENTRE|wx.ALL, 5) 651 id_filter=wx.NewId() 652 hbs.Add(wx.Button(self, id_filter, 'Filter'), 0, wx.ALIGN_CENTRE|wx.ALL, 5) 653 hbs.Add(wx.Button(self, wx.ID_HELP, 'Help'), 0, wx.ALIGN_CENTRE|wx.ALL, 5) 654 main_bs.Add(hbs, 0, wx.ALIGN_CENTRE|wx.ALL, 5) 655 wx.EVT_BUTTON(self, id_filter, self.OnFilter) 656 wx.EVT_BUTTON(self, wx.ID_HELP, lambda *_: wx.GetApp().displayhelpid(helpids.ID_DLG_CALENDAR_IMPORT)) 657 self.SetSizer(main_bs) 658 self.SetAutoLayout(True) 659 main_bs.Fit(self)
660
661 - def OnBrowseFolder(self, evt):
662 with guihelper.WXDialogWrapper(wx.FileDialog(self, "Pick a VCalendar File", wildcard='*.vcs'), 663 True) as (dlg, retcode): 664 if retcode==wx.ID_OK: 665 self.folderctrl.SetValue(dlg.GetPath()) 666 self._read=False
667
668 - def OnFilter(self, evt):
669 # read the calender to get the category list 670 if not self._read: 671 self._oc.read(self.folderctrl.GetValue()) 672 self._read=True 673 cat_list=self._oc.get_category_list() 674 with guihelper.WXDialogWrapper(common_calendar.AutoSyncFilterDialog(self, -1, 'Filtering Parameters', cat_list)) \ 675 as dlg: 676 dlg.set(self._oc.get_filter()) 677 if dlg.ShowModal()==wx.ID_OK: 678 self._oc.set_filter(dlg.get())
679
680 - def GetFolder(self):
681 return self.folderctrl.GetValue()
682
683 - def GetFilter(self):
684 return self._oc.get_filter()
685