0001 ### BITPIM 0002 ### 0003 ### Copyright (C) 2004 Joe Pham <djpham@bitpim.org> 0004 ### 0005 ### This program is free software; you can redistribute it and/or modify 0006 ### it under the terms of the BitPim license as detailed in the LICENSE file. 0007 ### 0008 ### $Id: vcal_calendar.py 4396 2007-09-12 21:44:00Z djpham $ 0009 0010 "Deals with vcard calendar import stuff" 0011 0012 # system modules 0013 from __future__ import with_statement 0014 import contextlib 0015 import copy 0016 import datetime 0017 0018 # site modules 0019 import wx 0020 0021 # local modules 0022 import bpcalendar 0023 import bptime 0024 import common_calendar 0025 import guihelper 0026 import helpids 0027 import vcard 0028 0029 module_debug=False 0030 0031 #------------------------------------------------------------------------------- 0032 class ImportDataSource(common_calendar.ImportDataSource): 0033 # how to define, and retrieve calendar import data source 0034 message_str="Pick a vCal Calendar File" 0035 wildcard='*.vcs;*.ics' 0036 0037 #------------------------------------------------------------------------------- 0038 class vCalendarFile(object): 0039 def __init__(self, file_name=None): 0040 self._data=[] 0041 self._file_name=file_name 0042 0043 def _open(self, name): 0044 return file(name, 'rt') 0045 0046 def _read_block(self, vfile): 0047 """Read a BEGIN/END block and return a dict of values/params 0048 """ 0049 global module_debug 0050 d={} 0051 _inblk=False 0052 for n,l in vfile: 0053 if n[0]=='BEGIN': 0054 _blkname=l 0055 _inblk=True 0056 _vdata=[] 0057 elif n[0]=='END' and l==_blkname: 0058 d['BEGIN-END']={ 'value': l, 0059 'params': self._read_block(_vdata) } 0060 _inblk=False 0061 elif _inblk: 0062 _vdata.append((n, l)) 0063 else: 0064 _params={} 0065 for _item in n[1:]: 0066 _l=_item.split('=') 0067 if len(_l)==1: 0068 _params[_l[0]]=None 0069 else: 0070 _params[_l[0]]=_l[1] 0071 d[n[0]]={ 'value': l, 0072 'params': _params } 0073 if module_debug: 0074 print d,'\n' 0075 return d 0076 0077 def read(self, file_name=None): 0078 self._data=[] 0079 if file_name is not None: 0080 self._file_name=file_name 0081 if self._file_name is None: 0082 # no file name specified 0083 return 0084 try: 0085 with contextlib.closing(self._open(self._file_name)) as f: 0086 vfile=vcard.VFile(f) 0087 has_data=False 0088 for n,l in vfile: 0089 if n[0]=='BEGIN' and l=='VEVENT': 0090 has_data=True 0091 _vdata=[] 0092 elif n[0]=='END' and l=='VEVENT': 0093 has_data=False 0094 self._data.append(self._read_block(_vdata)) 0095 elif has_data: 0096 _vdata.append((n, l)) 0097 except: 0098 if __debug__: 0099 raise 0100 0101 def _get_data(self): 0102 return copy.deepcopy(self._data) 0103 data=property(fget=_get_data) 0104 0105 #------------------------------------------------------------------------------- 0106 class VCalendarImportData(object): 0107 0108 _default_filter={ 0109 'start': None, 0110 'end': None, 0111 'categories': None, 0112 'rpt_events': False, 0113 'no_alarm': False, 0114 'ringtone': None, 0115 'alarm_override':False, 0116 'vibrate':False, 0117 'alarm_value':0 0118 } 0119 _rrule_dow={ 0120 'SU': 0x01, 'MO': 0x02, 'TU': 0x04, 'WE': 0x08, 'TH': 0x10, 0121 'FR': 0x20, 'SA': 0x40 } 0122 _rrule_weekday=_rrule_dow['MO']|_rrule_dow['TU']|\ 0123 _rrule_dow['WE']|_rrule_dow['TH']|\ 0124 _rrule_dow['FR'] 0125 _source_data_class=vCalendarFile 0126 0127 def __init__(self, file_name=None): 0128 self._file_name=file_name 0129 self._data=[] 0130 self._filter=self._default_filter 0131 self.read() 0132 0133 def _accept(self, entry): 0134 # start & end time within specified filter 0135 if entry.get('repeat', False): 0136 # repeat event 0137 # need to populate to get an accurate end date 0138 ce=bpcalendar.CalendarEntry() 0139 self._populate_entry(entry, ce) 0140 if self._filter['start'] is not None and \ 0141 ce.end[:3]<self._filter['start'][:3]: 0142 # event ends before our rannge 0143 return False 0144 if self._filter['end'] is not None and \ 0145 ce.start[:3]>self._filter['end'][:3]: 0146 # event starts after our range 0147 return False 0148 else: 0149 # single event 0150 if self._filter['start'] is not None and \ 0151 entry['start'][:3]<self._filter['start'][:3]: 0152 return False 0153 if self._filter['end'] is not None and \ 0154 entry['end'][:3]>self._filter['end'][:3] and \ 0155 entry['end'][:3]!=common_calendar.no_end_date[:3]: 0156 return False 0157 # check the catefory 0158 c=self._filter['categories'] 0159 if c is None or not len(c): 0160 # no categories specified => all catefories allowed. 0161 return True 0162 if len([x for x in entry['categories'] if x in c]): 0163 return True 0164 return False 0165 0166 def _populate_repeat_entry(self, e, ce): 0167 # populate repeat entry data 0168 if not e.get('repeat', False) or e.get('repeat_type', None) is None: 0169 # not a repeat event 0170 return 0171 rp=bpcalendar.RepeatEntry() 0172 rp_type=e['repeat_type'] 0173 rp_interval=e.get('repeat_interval', 1) 0174 rp_interval2=e.get('repeat_interval2', 1) 0175 rp_end=e.get('repeat_end', None) 0176 rp_num=e.get('repeat_num', None) 0177 rp_dow=e.get('repeat_dow', 0) 0178 if rp_type==rp.daily: 0179 # daily event 0180 rp.repeat_type=rp.daily 0181 rp.interval=rp_interval 0182 elif rp_type==rp.weekly or rp_type==rp.monthly: 0183 rp.repeat_type=rp_type 0184 rp.interval=rp_interval 0185 rp.interval2=rp_interval2 0186 if rp_dow: 0187 rp.dow=rp_dow 0188 else: 0189 rp.dow=ce.start 0190 elif rp_type==rp.yearly: 0191 rp.repeat_type=rp.yearly 0192 else: 0193 # not yet supported 0194 return 0195 rp.weekstart=e.get('repeat_wkst', 'MO') 0196 # setting the repeat duration/end-date of this event 0197 if rp_end is not None: 0198 # end date specified 0199 ce.end=rp_end[:3]+ce.end[3:] 0200 elif rp_num: 0201 # num of occurrences specified 0202 _dt=ce.start[:3] 0203 for i in range(rp_num-1): 0204 _dt=rp.next_date(_dt) 0205 ce.end=_dt[:3]+ce.end[3:] 0206 else: 0207 # forever duration 0208 ce.end=common_calendar.no_end_date[:3]+ce.end[3:] 0209 # add the list of exceptions 0210 for k in e.get('exceptions', []): 0211 rp.add_suppressed(*k[:3]) 0212 # all done 0213 ce.repeat=rp 0214 0215 def _populate_entry(self, e, ce): 0216 # populate an calendar entry with data 0217 ce.description=e.get('description', None) 0218 ce.location=e.get('location', None) 0219 v=e.get('priority', None) 0220 if v is not None: 0221 ce.priority=v 0222 if not self._filter.get('no_alarm', False) and \ 0223 not self._filter.get('alarm_override', False) and \ 0224 e.get('alarm', False): 0225 ce.alarm=e.get('alarm_value', 0) 0226 ce.ringtone=self._filter.get('ringtone', "") 0227 ce.vibrate=self._filter.get('vibrate', False) 0228 elif not self._filter.get('no_alarm', False) and \ 0229 self._filter.get('alarm_override', False): 0230 ce.alarm=self._filter.get('alarm_value', 0) 0231 ce.ringtone=self._filter.get('ringtone', "") 0232 ce.vibrate=self._filter.get('vibrate', False) 0233 ce_start=e.get('start', None) 0234 ce_end=e.get('end', None) 0235 if ce_start is None and ce_end is None: 0236 raise ValueError, "No start or end datetime" 0237 if ce_start is not None: 0238 ce.start=ce_start 0239 if ce_end is not None: 0240 ce.end=ce_end 0241 if ce_start is None: 0242 ce.start=ce.end 0243 elif ce_end is None: 0244 ce.end=ce.start 0245 ce.notes=e.get('notes', None) 0246 v=[] 0247 for k in e.get('categories', []): 0248 v.append({ 'category': k }) 0249 ce.categories=v 0250 # look at repeat 0251 self._populate_repeat_entry(e, ce) 0252 ce.allday=e.get('allday', False) 0253 0254 def _generate_repeat_events(self, e): 0255 # generate multiple single events from this repeat event 0256 ce=bpcalendar.CalendarEntry() 0257 self._populate_entry(e, ce) 0258 l=[] 0259 new_e=e.copy() 0260 new_e['repeat']=False 0261 for k in ('repeat_type', 'repeat_interval', 'repeat_dow'): 0262 if new_e.has_key(k): 0263 del new_e[k] 0264 s_date=datetime.datetime(*self._filter['start']) 0265 e_date=datetime.datetime(*self._filter['end']) 0266 one_day=datetime.timedelta(1) 0267 this_date=s_date 0268 while this_date<=e_date: 0269 date_l=(this_date.year, this_date.month, this_date.day) 0270 if ce.is_active(*date_l): 0271 new_e['start']=date_l+new_e['start'][3:] 0272 new_e['end']=date_l+new_e['end'][3:] 0273 l.append(new_e.copy()) 0274 this_date+=one_day 0275 return l 0276 0277 def get(self): 0278 global module_debug 0279 res={} 0280 single_rpt=self._filter.get('rpt_events', False) 0281 for k in self._data: 0282 try: 0283 if self._accept(k): 0284 if k.get('repeat', False) and single_rpt: 0285 d=self._generate_repeat_events(k) 0286 else: 0287 d=[k] 0288 for n in d: 0289 ce=bpcalendar.CalendarEntry() 0290 self._populate_entry(n, ce) 0291 res[ce.id]=ce 0292 except: 0293 if module_debug: 0294 raise 0295 return res 0296 0297 def get_category_list(self): 0298 l=[] 0299 for e in self._data: 0300 l+=[x for x in e.get('categories', []) if x not in l] 0301 return l 0302 0303 def set_filter(self, filter): 0304 self._filter=filter 0305 0306 def get_filter(self): 0307 return self._filter 0308 0309 def _conv_cat(self, v, _): 0310 return [x.strip() for x in v['value'].split(",") if len(x)] 0311 0312 def _conv_alarm(self, v, dd): 0313 try: 0314 alarm_date=bptime.BPTime(v['value'].split(';')[0]) 0315 start_date=bptime.BPTime(dd['start']) 0316 if alarm_date.get()<start_date.get(): 0317 dd['alarm_value']=(start_date-alarm_date).seconds/60 0318 return True 0319 return False 0320 except: 0321 return False 0322 0323 def _conv_date(self, v, _): 0324 return bptime.BPTime(v['value']).get() 0325 def _conv_priority(self, v, _): 0326 try: 0327 return int(v['value']) 0328 except: 0329 return None 0330 def _conv_str(self, v, _): 0331 return v['value'].replace('\,', ',') 0332 0333 def _process_daily_rule(self, v, dd): 0334 # the rule is Dx #y or Dx YYYYMMDDTHHMM 0335 s=v['value'].split(' ') 0336 dd['repeat_interval']=int(s[0][1:]) 0337 if len(s)==1: 0338 # no duration/end date 0339 return True 0340 if s[1][0]=='#': 0341 # duration 0342 dd['repeat_num']=int(s[1][1:]) 0343 else: 0344 # end date 0345 dd['repeat_end']=bptime.BPTime(s[1]).get() 0346 dd['repeat_type']='daily' 0347 return True 0348 0349 def _process_weekly_rule(self, v, dd): 0350 # the rule is Wx | Wx <#y|YYYYMMDDTHHMMSS> | Wx MO TU 0351 s=v['value'].split(' ') 0352 dd['repeat_interval']=int(s[0][1:]) 0353 dow=0 0354 for i in range(1, len(s)): 0355 n=s[i] 0356 if n[0].isdigit(): 0357 dd['repeat_end']=bptime.BPTime(n).get() 0358 elif n[0]=='#': 0359 dd['repeat_num']=int(n[1:]) 0360 else: 0361 # day-of-week 0362 dow=dow|self._rrule_dow.get(n, 0) 0363 if dow: 0364 dd['repeat_dow']=dow 0365 dd['repeat_type']='weekly' 0366 return True 0367 0368 def _process_monthly_rule(self, v, dd): 0369 global module_debug 0370 try: 0371 # acceptable format: MD1 <day number> <end date | #duration> 0372 # or MP1 <[1-4]+ | 1-> <SU-SA> <end date | #duration> 0373 s=v['value'].split(' ') 0374 if s[0][:2]!='MD' and s[0][:2]!='MP': 0375 return False 0376 dd['repeat_interval2']=int(s[0][2:]) 0377 if s[0][:2]=='MP': 0378 # every nth *day of every month 0379 n=s[1] 0380 if n in ['1+', '2+', '3+', '4+', '1-']: 0381 if n[1]=='-': 0382 dd['repeat_interval']=5 0383 else: 0384 dd['repeat_interval']=int(n[0]) 0385 else: 0386 return False 0387 dd['repeat_dow']=self._rrule_dow.get(s[2], 0) 0388 else: 0389 dd['repeat_interval']=dd['repeat_dow']=0 0390 dd['repeat_type']='monthly' 0391 n=s[-1] 0392 if len(n)>7 and n[:8].isdigit(): 0393 # end date/time specified 0394 dd['repeat_end']=bptime.BPTime(n).get() 0395 elif n[0]=='#': 0396 dd['repeat_num']=int(n[1:]) 0397 return True 0398 except: 0399 if module_debug: raise 0400 return False 0401 def _process_yearly_rule(self, v, dd): 0402 global module_debug 0403 try: 0404 # acceptable format YM1 <Month number> <end date | #duration> 0405 s=v['value'].split(' ') 0406 if s[0]!='YM1': 0407 return False 0408 n=s[-1] 0409 if len(n)>7 and n[:8].isdigit(): 0410 # end date/time specified 0411 dd['repeat_end']=bptime.BPTime(n).get() 0412 elif n[0]=='#': 0413 dd['repeat_num']=int(n[1:]) 0414 dd['repeat_type']='yearly' 0415 return True 0416 except: 0417 if module_debug: raise 0418 return False 0419 0420 def _conv_repeat(self, v, dd): 0421 func_dict={ 0422 'D': self._process_daily_rule, 0423 'W': self._process_weekly_rule, 0424 'M': self._process_monthly_rule, 0425 'Y': self._process_yearly_rule 0426 } 0427 c=v['value'][0] 0428 return func_dict.get(c, lambda *arg: False)(v, dd) 0429 def _conv_exceptions(self, v, _): 0430 try: 0431 l=v['value'].split(';') 0432 r=[] 0433 for n in l: 0434 r.append(bptime.BPTime(n).get()) 0435 return r 0436 except: 0437 if __debug__: 0438 raise 0439 return [] 0440 _calendar_keys=[ 0441 ('CATEGORIES', 'categories', _conv_cat), 0442 ('DESCRIPTION', 'notes', _conv_str), 0443 ('DTSTART', 'start', _conv_date), 0444 ('DTEND', 'end', _conv_date), 0445 ('LOCATION', 'location', _conv_str), 0446 ('PRIORITY', 'priority', _conv_priority), 0447 ('SUMMARY', 'description', _conv_str), 0448 ('AALARM', 'alarm', _conv_alarm), 0449 ('DALARM', 'alarm', _conv_alarm), 0450 ('RRULE', 'repeat', _conv_repeat), 0451 ('EXDATE', 'exceptions', _conv_exceptions), 0452 ] 0453 def _convert(self, vcal, d): 0454 global module_debug 0455 for i in vcal: 0456 try: 0457 dd={'start': None, 'end': None } 0458 for j in self._calendar_keys: 0459 if i.has_key(j[0]): 0460 k=i[j[0]] 0461 if j[2] is not None: 0462 dd[j[1]]=j[2](self, k, dd) 0463 else: 0464 dd[j[1]]=k['value'] 0465 if dd['start'] is None and dd['end'] is None: 0466 # no start or end, drop this one 0467 continue 0468 if dd['start'] is None: 0469 dd['start']=dd['end'] 0470 elif dd['end'] is None: 0471 dd['end']=dd['start'] 0472 if module_debug: print dd 0473 d.append(dd) 0474 except: 0475 if module_debug: raise 0476 0477 def get_display_data(self): 0478 cnt=0 0479 res={} 0480 single_rpt=self._filter.get('rpt_events', False) 0481 for k in self._data: 0482 if self._accept(k): 0483 if k.get('repeat', False) and single_rpt: 0484 d=self._generate_repeat_events(k) 0485 else: 0486 d=[k.copy()] 0487 for n in d: 0488 if self._filter.get('no_alarm', False): 0489 n['alarm']=False 0490 res[cnt]=n 0491 cnt+=1 0492 return res 0493 0494 def get_file_name(self): 0495 if self._file_name is not None: 0496 return self._file_name 0497 return '' 0498 0499 def read(self, file_name=None, update_dlg=None): 0500 if file_name is not None: 0501 self._file_name=file_name 0502 if self._file_name is None: 0503 # no file name specified 0504 return 0505 v=self._source_data_class(self._file_name) 0506 v.read() 0507 self._convert(v.data, self._data) 0508 0509 #------------------------------------------------------------------------------- 0510 class VcalImportCalDialog(common_calendar.PreviewDialog): 0511 _column_labels=[ 0512 ('description', 'Description', 400, None), 0513 ('start', 'Start', 150, common_calendar.bp_date_str), 0514 ('end', 'End', 150, common_calendar.bp_date_str), 0515 ('repeat_type', 'Repeat', 80, common_calendar.bp_repeat_str), 0516 ('alarm', 'Alarm', 80, common_calendar.bp_alarm_str), 0517 ('categories', 'Category', 150, common_calendar.category_str) 0518 ] 0519 _filetype_label="VCalendar File:" 0520 _data_type='vCalendar' 0521 _import_data_class=VCalendarImportData 0522 def __init__(self, parent, id, title): 0523 self._oc=self._import_data_class() 0524 common_calendar.PreviewDialog.__init__(self, parent, id, title, 0525 self._column_labels, 0526 self._oc.get_display_data(), 0527 config_name='import/calendar/vcaldialog') 0528 0529 def getcontrols(self, main_bs): 0530 hbs=wx.BoxSizer(wx.HORIZONTAL) 0531 # label 0532 hbs.Add(wx.StaticText(self, -1, self._filetype_label), 0, wx.ALL|wx.ALIGN_CENTRE, 2) 0533 # where the folder name goes 0534 self.folderctrl=wx.TextCtrl(self, -1, "") #, style=wx.TE_READONLY) 0535 self.folderctrl.SetValue(self._oc.get_file_name()) 0536 hbs.Add(self.folderctrl, 1, wx.EXPAND|wx.ALL, 2) 0537 # browse button 0538 id_browse=wx.NewId() 0539 hbs.Add(wx.Button(self, id_browse, 'Browse ...'), 0, wx.EXPAND|wx.ALL, 2) 0540 main_bs.Add(hbs, 0, wx.EXPAND|wx.ALL, 5) 0541 main_bs.Add(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 5) 0542 wx.EVT_BUTTON(self, id_browse, self.OnBrowseFolder) 0543 0544 def getpostcontrols(self, main_bs): 0545 main_bs.Add(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 5) 0546 hbs=wx.BoxSizer(wx.HORIZONTAL) 0547 id_import=wx.NewId() 0548 hbs.Add(wx.Button(self, id_import, 'Import'), 0, wx.ALIGN_CENTRE|wx.ALL, 5) 0549 hbs.Add(wx.Button(self, wx.ID_OK, 'Replace All'), 0, wx.ALIGN_CENTRE|wx.ALL, 5) 0550 hbs.Add(wx.Button(self, self.ID_ADD, 'Add'), 0, wx.ALIGN_CENTRE|wx.ALL, 5) 0551 hbs.Add(wx.Button(self, self.ID_MERGE, 'Merge'), 0, wx.ALIGN_CENTRE|wx.ALL, 5) 0552 hbs.Add(wx.Button(self, wx.ID_CANCEL, 'Cancel'), 0, wx.ALIGN_CENTRE|wx.ALL, 5) 0553 id_filter=wx.NewId() 0554 hbs.Add(wx.Button(self, id_filter, 'Filter'), 0, wx.ALIGN_CENTRE|wx.ALL, 5) 0555 hbs.Add(wx.Button(self, wx.ID_HELP, 'Help'), 0, wx.ALIGN_CENTRE|wx.ALL, 5) 0556 main_bs.Add(hbs, 0, wx.ALIGN_CENTRE|wx.ALL, 5) 0557 wx.EVT_BUTTON(self, id_import, self.OnImport) 0558 wx.EVT_BUTTON(self, id_filter, self.OnFilter) 0559 wx.EVT_BUTTON(self, self.ID_ADD, self.OnEndModal) 0560 wx.EVT_BUTTON(self, self.ID_MERGE, self.OnEndModal) 0561 wx.EVT_BUTTON(self, wx.ID_HELP, lambda *_: wx.GetApp().displayhelpid(helpids.ID_DLG_CALENDAR_IMPORT)) 0562 0563 @guihelper.BusyWrapper 0564 def OnImport(self, evt): 0565 with guihelper.WXDialogWrapper(wx.ProgressDialog('%s Import'%self._data_type, 0566 'Importing %s Data, please wait ...'%self._data_type, 0567 parent=self)) as dlg: 0568 try: 0569 self._oc.read(self.folderctrl.GetValue()) 0570 self.populate(self._oc.get_display_data()) 0571 except (ValueError, IOError): 0572 guihelper.MessageDialog(self, 'Failed to get import data', 0573 'Import Error', 0574 style=wx.OK|wx.ICON_ERROR) 0575 except: 0576 if __debug__: 0577 raise 0578 0579 def OnBrowseFolder(self, evt): 0580 with guihelper.WXDialogWrapper(wx.FileDialog(self, "Pick a %s File"%self._data_type, 0581 wildcard='*.vcs;*.ics'), 0582 True) as (dlg, retcode): 0583 if retcode==wx.ID_OK: 0584 self.folderctrl.SetValue(dlg.GetPath()) 0585 0586 def OnFilter(self, evt): 0587 cat_list=self._oc.get_category_list() 0588 with guihelper.WXDialogWrapper(common_calendar.FilterDialog(self, -1, 'Filtering Parameters', cat_list), 0589 True) as (dlg, retcode): 0590 if retcode==wx.ID_OK: 0591 self._oc.set_filter(dlg.get()) 0592 self.populate(self._oc.get_display_data()) 0593 0594 def OnEndModal(self, evt): 0595 self.EndModal(evt.GetId()) 0596 0597 def get(self): 0598 return self._oc.get() 0599 0600 def get_categories(self): 0601 return self._oc.get_category_list() 0602 0603 #------------------------------------------------------------------------------- 0604 def ImportCal(folder, filters): 0605 _oc=VCalendarImportData(folder) 0606 _oc.set_filter(filters) 0607 _oc.read() 0608 res={ 'calendar':_oc.get() } 0609 return res 0610 0611 #------------------------------------------------------------------------------- 0612 class VCalAutoConfCalDialog(wx.Dialog): 0613 def __init__(self, parent, id, title, folder, filters, 0614 style=wx.CAPTION|wx.MAXIMIZE_BOX| \ 0615 wx.SYSTEM_MENU|wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER): 0616 self._oc=VCalendarImportData() 0617 self._oc.set_filter(filters) 0618 self._read=False 0619 wx.Dialog.__init__(self, parent, id=id, title=title, style=style) 0620 main_bs=wx.BoxSizer(wx.VERTICAL) 0621 hbs=wx.BoxSizer(wx.HORIZONTAL) 0622 # label 0623 hbs.Add(wx.StaticText(self, -1, "VCalendar File:"), 0, wx.ALL|wx.ALIGN_CENTRE, 2) 0624 # where the folder name goes 0625 self.folderctrl=wx.TextCtrl(self, -1, "", style=wx.TE_READONLY) 0626 self.folderctrl.SetValue(folder) 0627 hbs.Add(self.folderctrl, 1, wx.EXPAND|wx.ALL, 2) 0628 # browse button 0629 id_browse=wx.NewId() 0630 hbs.Add(wx.Button(self, id_browse, 'Browse ...'), 0, wx.EXPAND|wx.ALL, 2) 0631 main_bs.Add(hbs, 0, wx.EXPAND|wx.ALL, 5) 0632 main_bs.Add(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 5) 0633 wx.EVT_BUTTON(self, id_browse, self.OnBrowseFolder) 0634 hbs=wx.BoxSizer(wx.HORIZONTAL) 0635 hbs.Add(wx.Button(self, wx.ID_OK, 'OK'), 0, wx.ALIGN_CENTRE|wx.ALL, 5) 0636 hbs.Add(wx.Button(self, wx.ID_CANCEL, 'Cancel'), 0, wx.ALIGN_CENTRE|wx.ALL, 5) 0637 id_filter=wx.NewId() 0638 hbs.Add(wx.Button(self, id_filter, 'Filter'), 0, wx.ALIGN_CENTRE|wx.ALL, 5) 0639 hbs.Add(wx.Button(self, wx.ID_HELP, 'Help'), 0, wx.ALIGN_CENTRE|wx.ALL, 5) 0640 main_bs.Add(hbs, 0, wx.ALIGN_CENTRE|wx.ALL, 5) 0641 wx.EVT_BUTTON(self, id_filter, self.OnFilter) 0642 wx.EVT_BUTTON(self, wx.ID_HELP, lambda *_: wx.GetApp().displayhelpid(helpids.ID_DLG_CALENDAR_IMPORT)) 0643 self.SetSizer(main_bs) 0644 self.SetAutoLayout(True) 0645 main_bs.Fit(self) 0646 0647 def OnBrowseFolder(self, evt): 0648 with guihelper.WXDialogWrapper(wx.FileDialog(self, "Pick a VCalendar File", wildcard='*.vcs'), 0649 True) as (dlg, retcode): 0650 if retcode==wx.ID_OK: 0651 self.folderctrl.SetValue(dlg.GetPath()) 0652 self._read=False 0653 0654 def OnFilter(self, evt): 0655 # read the calender to get the category list 0656 if not self._read: 0657 self._oc.read(self.folderctrl.GetValue()) 0658 self._read=True 0659 cat_list=self._oc.get_category_list() 0660 with guihelper.WXDialogWrapper(common_calendar.AutoSyncFilterDialog(self, -1, 'Filtering Parameters', cat_list)) \ 0661 as dlg: 0662 dlg.set(self._oc.get_filter()) 0663 if dlg.ShowModal()==wx.ID_OK: 0664 self._oc.set_filter(dlg.get()) 0665 0666 def GetFolder(self): 0667 return self.folderctrl.GetValue() 0668 0669 def GetFilter(self): 0670 return self._oc.get_filter() 0671
Generated by PyXR 0.9.4