PyXR

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



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