new Auto Sync feature for calendar (Part 1 of 2)
"Simon C" <[email protected]>
| Newsgroups | gmane.comp.mobile.bitpim.devel |
|---|---|
| Message-ID | <000f01c5be78$1864a850$6400a8c0@HOME> |
New autosync feature for bitpim. The purpose is to make bitpim automatically sync up with your phone with your outlook calendar every time you connect it to the PC or at a preset interval if it is left connected, if the phone supports bluetooth you just have to be close to the PC for it to sync up as long as bitpim is running. There is a new menu option "autosync" to configure and run the feature. The implementation stores the Auto sync folder and filter settings in the bitpim database, it uses the existing calendar code to import the calendar from the PC and to send the data to the phone. The existing import calendar feature still worksas it used to. You can use it with vcal, csv and outlook calendars. To avoid that annoying "permission" dialog that pops up with outlook, the autosync feature does not import the body of the calendar events from outlook, so you only get the event title on the phone, not an issue with LG phones as they don't support a memo for events anyway. I and one other person have been testing for a couple of weeks with outlook and it seems to be working OK. Tested on 4400, 6100 & 8100 although the code is not phone specific. It only works in one direction, i.e. outlook calendar copied into phone, events added to the phone will be lost, a future enhancement would be to push these back into outlook or at least not loose them. Another improvement would a way to minimise bitpim and hide it, like bitfling does, this would make leaving bitpim running easier, also auto starting with user login so it is completely automatic. I enhanced the alarm options in the calendar filter so you can choose the ringtone, vibrate and force the alarms to on for all events, this affects the existing import feature as well because the filter code is shared. I included some 8100 calendar enhancements that are independent from the rest of the code and should be comitted regardless of whether this feature is. If you decide to add this I can write up a help page for it. One new source (auto_sync.py) and 8 edits with diffs attached. It is merged with the latest code from CVS. Some of the code changes in Part 2, message too big for sourceforge if I attach all at the same time. Simon
auto_sync.py
(text/plain, 17.1 KB)
### BITPIM ### ### Copyright (C) 2004 Simon Capper <[email protected]> ### ### This program is free software; you can redistribute it and/or modify ### it under the terms of the BitPim license as detailed in the LICENSE file. ### """ Auto synchronization of calender This module provides functionality to automatically sync the calender on your PC (outlook etc.) with your phone. It will do so at a regular interval if the phone is connected and BitPim is running This feature works with all BitPim calender types and phones that support the writing of calenders """ # standard modules import copy import sha import time import re # wx modules import wx # BitPim modules import database import guiwidgets import common_calendar import guiwidgets import gui _data_key='auto_sync_settings' _filter_keys=['start_offset', 'end_offset', 'no_alarm', 'rpt_events', "alarm_value", 'ringtone', 'vibrate', 'categories', 'alarm_override'] def _getsettings(mw): settings=AutoSyncSettingsEntry() dict=mw.database.getmajordictvalues(_data_key, autosyncsettingsobjectfactory) for k,e in dict.items(): settings.set_db_dict(e) # only expecting one entry break return settings def UpdateOnConnect(mw): settings=_getsettings(mw) return settings.sync_on_connect class SyncSchedule(object): def __init__(self, log=None): # get standard commport parameters self.__log=log self.__data={} def log(self, log_str): if self.__log is None: print log_str else: self.__log.log(log_str) def logdata(self, log_str, log_data): if self.__log is None: print log_str,log_data else: self.__log.logdata(log_str, log_data) def importcalenderdata(self): res=0 for entry in self.mw.calenders: if entry[0]==self.settings.caltype: filter={} for setting in _filter_keys: if self.settings._data.has_key(setting) and self.settings._data[setting]!=None: # need to convert start and end from scalable values if setting=='start_offset': tm=time.gmtime(time.time()-(self.settings._data[setting]*24*60*60)) date=(tm.tm_year, tm.tm_mon, tm.tm_mday) filter['start']=date elif setting=='end_offset': tm=time.gmtime(time.time()+(self.settings._data[setting]*24*60*60)) date=(tm.tm_year, tm.tm_mon, tm.tm_mday) filter['end']=date elif setting=="categories": # convert categories into list filter[setting]=self.settings._data[setting].split("||") else: filter[setting]=self.settings._data[setting] else: if setting=='start_offset': filter['start']=None if setting=='end_offset': filter['end']=None res=entry[2](self.mw, self.settings.calender_id, filter) if res==1: # imported calender OK!!! self.log("Auto Sync: Imported calender OK") if not res: self.log("Auto Sync: Failed to import calender") return res def sendcalendertophone(self): res=1 data={} todo=[] data['calendar_version']=self.mw.phoneprofile.BP_Calendar_Version self.mw.calendarwidget.getdata(data) todo.append( (self.mw.wt.writecalendar, "Calendar", False) ) todo.append((self.mw.wt.rebootcheck, "Phone Reboot")) self.mw.MakeCall(gui.Request(self.mw.wt.getfundamentals), gui.Callback(self.OnDataSendPhoneGotFundamentals, data, todo)) return res def OnDataSendPhoneGotFundamentals(self, data, todo, exception, results): if exception!=None: if not self.silent: self.mw.HandleException(exception) self.log("Auto Sync: Failed, Exception getting phone fundementals") self.mw.OnBusyEnd() return data.update(results) # Now scribble to phone self.log("Auto Sync: Sending results to phone") self.mw.MakeCall(gui.Request(self.mw.wt.senddata, data, todo), gui.Callback(self.OnDataSendCalenderResults)) def OnDataSendCalenderResults(self, exception, results): if exception!=None: if not self.silent: self.mw.HandleException(exception) self.log("Auto Sync: Failed, Exception writing calender to phone") self.mw.OnBusyEnd() return if self.silent==0: wx.MessageBox('Phone Synchronized OK', 'Synchronize Complete', wx.OK) self.log("Auto Sync: Synchronize Completed OK") self.mw.OnBusyEnd() def sync(self, mw, silent): self.silent=silent # start the autosync process # import the calender, find the entry point for the import function self.mw=mw if mw.config.ReadInt("SafeMode", False): self.log("Auto Sync: Disabled, BitPim in safe mode") return 0 if wx.IsBusy(): self.log("Auto Sync: Failed, BitPim busy") return 0 self.log("Auto Sync: Starting (silent mode=%d)..." % (silent)) self.mw.OnBusyStart() self.mw.GetStatusBar().progressminor(0, 100, 'AutoSync in progress ...') # retrieve the configuration self.settings=_getsettings(mw) # update BitPims calender res=self.importcalenderdata() if res==1: # send updated calender to the phone res=self.sendcalendertophone() else: self.mw.OnBusyEnd() if silent==0: wx.MessageBox('Unable to synchronize phone schedule', 'Synchronize failed', wx.OK) self.log("Auto Sync: Failed, Unable to synchronize phone schedule") return res #------------------------------------------------------------------------------- class AutoSyncSettingsobject(database.basedataobject): _knownproperties=['caltype', 'calender_id', 'sync_on_connect', 'sync_frequency', \ 'start_offset', 'end_offset', 'no_alarm', 'rpt_events', 'categories', \ 'ringtone', 'vibrate', 'alarm_override', 'alarm_value'] _knownlistproperties=database.basedataobject._knownlistproperties.copy() def __init__(self, data=None): if data is None or not isinstance(data, AutoSyncSettingsEntry): return; self.update(data.get_db_dict()) autosyncsettingsobjectfactory=database.dataobjectfactory(AutoSyncSettingsobject) #------------------------------------------------------------------------------- class AutoSyncSettingsEntry(object): _caltype_key='caltype' _calender_id_key='calender_id' _sync_on_connect_key='sync_on_connect' _sync_frequency_key='sync_frequency' #_start_offset_key='start_offset' #_end_offset_key='end_offset' #_no_alarm_key='no_alarm' #_categories_key='categories' #_rpt_event_key='rpt_event' #_vibrate_key='vibrate' def __init__(self): self._data={ 'serials': [] } # we only expect one record, so the ID is fixed self._set_id(99) def __eq__(self, rhs): return self.caltype==rhs.caltype and self.calender_id==rhs.calender_id and\ self.sync_frequency==rhs.sync_frequency and self.sync_on_connect==rhs.sync_on_connect def __ne__(self, rhs): return (not __eq__(rhs)) def get(self): return copy.deepcopy(self._data, {}) def set(self, d): self._data={} self._data.update(d) def get_db_dict(self): return self.get() def set_db_dict(self, d): self.set(d) def _get_id(self): s=self._data.get('serials', []) for n in s: if n.get('sourcetype', None)=='bitpim': return n.get('id', None) return None def _set_id(self, id): s=self._data.get('serials', []) for n in s: if n.get('sourcetype', None)=='bitpim': n['id']=id return self._data['serials'].append({'sourcetype': 'bitpim', 'id': id } ) id=property(fget=_get_id, fset=_set_id) def _set_or_del(self, key, v, v_list=[]): if v is None or v in v_list: if self._data.has_key(key): del self._data[key] else: self._data[key]=v def _get_caltype(self): return self._data.get(self._caltype_key, 'None') def _set_caltype(self, v): self._set_or_del(self._caltype_key, v, ['']) caltype=property(fget=_get_caltype, fset=_set_caltype) def _get_calender_id(self): return self._data.get(self._calender_id_key, '') def _set_calender_id(self, v): self._set_or_del(self._calender_id_key, v, ['']) calender_id=property(fget=_get_calender_id, fset=_set_calender_id) def _get_sync_on_connect(self): return self._data.get(self._sync_on_connect_key, False) def _set_sync_on_connect(self, v): self._set_or_del(self._sync_on_connect_key, v, ['']) sync_on_connect=property(fget=_get_sync_on_connect, fset=_set_sync_on_connect) def _get_sync_frequency(self): return self._data.get(self._sync_frequency_key, 0) def _set_sync_frequency(self, v): self._set_or_del(self._sync_frequency_key, v, ['']) sync_frequency=property(fget=_get_sync_frequency, fset=_set_sync_frequency) ### ### The autosync settings dialog ### class AutoSyncSettingsDialog(wx.Dialog): ID_CALSETTINGS=wx.NewId() def __init__(self, mainwindow, frame, title="Schedule Auto Sync Settings", id=-1): wx.Dialog.__init__(self, frame, id, title, style=wx.CAPTION|wx.SYSTEM_MENU|wx.DEFAULT_DIALOG_STYLE) self.mw=mainwindow gs=wx.GridBagSizer(10, 10) gs.AddGrowableCol(1) # calender type gs.Add( wx.StaticText(self, -1, "Calender Type"), pos=(0,0), flag=wx.ALIGN_CENTER_VERTICAL) # get a list of the possible calender types supported calendertype=('None',) # build a list of calender types for the user to select from for entry in self.mw.calenders: calendertype+=(entry[0], ) self.caltype=wx.ComboBox(self, -1, calendertype[0], style=wx.CB_DROPDOWN|wx.CB_READONLY,choices=calendertype) gs.Add( self.caltype, pos=(0,1), flag=wx.ALIGN_CENTER_VERTICAL) gs.Add( wx.Button(self, self.ID_CALSETTINGS, "Calender Settings..."), pos=(0,2), flag=wx.ALIGN_CENTER_VERTICAL) # on connect gs.Add( wx.StaticText(self, -1, "Update when phone connected"), pos=(1,0), flag=wx.ALIGN_CENTER_VERTICAL) self.sync_on_connect=wx.CheckBox(self, wx.NewId(), "") gs.Add( self.sync_on_connect, pos=(1,1), flag=wx.ALIGN_CENTER_VERTICAL) # frequency gs.Add( wx.StaticText(self, -1, "Update Frequency (mins) 0=never"), pos=(2,0), flag=wx.ALIGN_CENTER_VERTICAL) self.sync_frequency=wx.lib.intctrl.IntCtrl(self, -1, value=0, min=0, max=1440) gs.Add( self.sync_frequency, pos=(2,1), flag=wx.ALIGN_CENTER_VERTICAL) # crud at the bottom bs=wx.BoxSizer(wx.VERTICAL) bs.Add(gs, 0, wx.EXPAND|wx.ALL, 10) bs.Add(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 7) but=self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.HELP) bs.Add(but, 0, wx.CENTER|wx.ALL, 10) wx.EVT_BUTTON(self, wx.ID_HELP, self.OnHelp) wx.EVT_BUTTON(self, wx.ID_CANCEL, self.OnCancel) wx.EVT_BUTTON(self, wx.ID_OK, self.OnOK) wx.EVT_BUTTON(self, self.ID_CALSETTINGS, self.OnConfigCalender) wx.EVT_COMBOBOX(self, self.caltype.GetId(), self.OnCaltypeChange) self.settings=AutoSyncSettingsEntry() self.SetSizer(bs) self.SetAutoLayout(True) bs.Fit(self) # read initial values from database self.getfromfs() self.auto_sync_timer_id = wx.NewId() self.auto_sync_timer = wx.Timer(self, self.auto_sync_timer_id) # start the timer self.SetAutoSyncTimer() # Retrieve saved settings... (we only care about position) guiwidgets.set_size("AutoSyncSettingsDialog", self, screenpct=-1, aspect=3.5) wx.EVT_CLOSE(self, self.OnClose) def OnCaltypeChange(self, _): #see if the value has changed, if so automatically fire up the configuration for the calender if self.settings.caltype!=self.caltype.GetValue(): self.OnConfigCalender() return def OnConfigCalender(self, _=None): old_folder=self.settings.calender_id for entry in self.mw.calenders: if entry[0]==self.caltype.GetValue(): # if the calender type is changing blank out the folder name if self.settings.caltype!=self.caltype.GetValue(): self.settings.calender_id='' filter={} for setting in _filter_keys: if self.settings._data.has_key(setting) and self.settings._data[setting]!=None: if setting=="categories": # convert categories into list filter[setting]=self.settings._data[setting].split("||") else: filter[setting]=self.settings._data[setting] res, temp=entry[1](self.mw, self.settings.calender_id, filter) if res==wx.ID_OK: # temp is a tuple of the calender_id and the filter settings self.settings.calender_id=temp[0] for setting in _filter_keys: if(temp[1].has_key(setting) and temp[1][setting]!=None): if setting=="categories": # convert categories into storable type cat_str="" for cat in temp[1][setting]: #use a || to separate individual categories if len(cat_str): cat_str=cat_str+"||"+cat else: cat_str=cat self.settings._data[setting]=cat_str else: self.settings._data[setting]=temp[1][setting] else: if self.settings._data.has_key(setting): del self.settings._data[setting] self.settings.caltype=self.caltype.GetValue() else: # cancel pressed #revert back to previous value self.caltype.SetValue(self.settings.caltype) self.settings.calender_id=old_folder return return def OnCancel(self, _): self.saveSize() self.EndModal(wx.ID_CANCEL) return def OnOK(self, _): self.saveSize() self.EndModal(wx.ID_OK) return def OnHelp(self, _): wx.GetApp().displayhelpid(helpids.ID_SETTINGS_DIALOG) return def OnClose(self, evt): self.saveSize() # Don't destroy the dialong, just put it away... self.EndModal(wx.ID_CANCEL) return def _save_to_db(self): db_rr={} self.settings.caltype=self.caltype.GetValue() self.settings.sync_on_connect=self.sync_on_connect.GetValue() self.settings.sync_frequency=self.sync_frequency.GetValue() db_rr[self.settings.id]=AutoSyncSettingsobject(self.settings) database.ensurerecordtype(db_rr, autosyncsettingsobjectfactory) self.mw.database.savemajordict(_data_key, db_rr) def getfromfs(self): self.settings=_getsettings(self.mw) self.caltype.SetValue(self.settings.caltype) self.sync_on_connect.SetValue(int(self.settings.sync_on_connect)) self.sync_frequency.SetValue(int(self.settings.sync_frequency)) return def updatevariables(self): self.mw.auto_save_dict=self.settings def ShowModal(self): self.getfromfs() ec=wx.Dialog.ShowModal(self) if ec==wx.ID_OK: self._save_to_db() self.updatevariables() self.SetAutoSyncTimer() return ec def saveSize(self): guiwidgets.save_size("AutoSyncSettingsDialog", self.GetRect()) def SetAutoSyncTimer(self): # stop the previous timer (if any) self.auto_sync_timer.Stop() oneShot = True timeout=self.settings.sync_frequency*60000 # convert msecs if timeout: self.auto_sync_timer.Start(timeout, oneShot) self.Bind(wx.EVT_TIMER, self.OnTimer, self.auto_sync_timer) def OnTimer(self, event): self.mw.log("Auto Sync: Timed update") SyncSchedule(self.mw).sync(self.mw, silent=1) self.SetAutoSyncTimer()
com_lgvx8100.py
(text/plain, 18.6 KB)
### BITPIM ### ### Copyright (C) 2003-2005 Roger Binns <[email protected]> ### Copyright (C) 2005 Simon Capper <[email protected]> ### ### This program is free software; you can redistribute it and/or modify ### it under the terms of the BitPim license as detailed in the LICENSE file. ### """Communicate with the LG VX8100 cell phone The VX8100 is substantially similar to the VX7000 but also supports video. """ # standard modules import re import time import cStringIO import sha # my modules import common import commport import copy import com_lgvx4400 import p_brew import p_lgvx8100 import com_lgvx7000 import com_brew import com_phone import com_lg import prototypes import bpcalendar import call_history import sms import memo from prototypes import * class Phone(com_lgvx7000.Phone): "Talk to the LG VX8100 cell phone" desc="LG-VX8100" protocolclass=p_lgvx8100 serialsname='lgvx8100' builtinringtones= ('Low Beep Once', 'Low Beeps', 'Loud Beep Once', 'Loud Beeps', 'VZW Default Ringtone') + \ tuple(['Ringtone '+`n` for n in range(1,11)]) + \ ('No Ring',) ringtonelocations= ( # type index-file size-file directory-to-use lowest-index-to-use maximum-entries type-major ( 'ringers', 'dload/my_ringtone.dat', 'dload/my_ringtonesize.dat', 'brew/16452/lk/mr', 100, 150, 1), ) builtinwallpapers = () # none wallpaperlocations= ( ( 'images', 'dload/image.dat', 'dload/imagesize.dat', 'brew/16452/mp', 100, 50, 0), ) def __init__(self, logtarget, commport): com_lgvx4400.Phone.__init__(self, logtarget, commport) self.mode=self.MODENONE def getfundamentals(self, results): """Gets information fundamental to interopating with the phone and UI. Currently this is: - 'uniqueserial' a unique serial number representing the phone - 'groups' the phonebook groups - 'wallpaper-index' map index numbers to names - 'ringtone-index' map index numbers to ringtone names This method is called before we read the phonebook data or before we write phonebook data. """ # use a hash of ESN and other stuff (being paranoid) self.log("Retrieving fundamental phone information") self.log("Phone serial number") results['uniqueserial']=sha.new(self.getfilecontents("nvm/$SYS.ESN")).hexdigest() # now read groups self.log("Reading group information") buf=prototypes.buffer(self.getfilecontents("pim/pbgroup.dat")) g=self.protocolclass.pbgroups() g.readfrombuffer(buf) self.logdata("Groups read", buf.getdata(), g) groups={} for i in range(len(g.groups)): if len(g.groups[i].name): # sometimes have zero length names groups[i]={'name': g.groups[i].name } results['groups']=groups self.getwallpaperindices(results) self.getringtoneindices(results) self.log("Fundamentals retrieved") return results def savegroups(self, data): groups=data['groups'] keys=groups.keys() keys.sort() g=self.protocolclass.pbgroups() for k in keys: e=self.protocolclass.pbgroup() e.name=groups[k]['name'] g.groups.append(e) buffer=prototypes.buffer() g.writetobuffer(buffer) self.logdata("New group file", buffer.getvalue(), g) self.writefile("pim/pbgroup.dat", buffer.getvalue()) def getmemo(self, result): # read the memo file try: buf=prototypes.buffer(self.getfilecontents("sch/neomemo.dat")) text_memo=self.protocolclass.textmemofile() text_memo.readfrombuffer(buf) res={} for m in text_memo.items: entry=memo.MemoEntry() entry.text=m.text entry.set_date_isostr("%d%02d%02dT%02d%02d00" % ((m.memotime))) res[entry.id]=entry except com_brew.BrewNoSuchFileException: res={} result['memo']=res return result def savememo(self, result, merge): text_memo=self.protocolclass.textmemofile() memo_dict=result.get('memo', {}) keys=memo_dict.keys() keys.sort() text_memo.itemcount=len(keys) for k in keys: entry=self.protocolclass.textmemo() entry.text=memo_dict[k].text t=time.strptime(memo_dict[k].date, '%b %d, %Y %H:%M') entry.memotime=(t.tm_year, t.tm_mon, t.tm_mday, t.tm_hour, t.tm_min) text_memo.items.append(entry) buf=prototypes.buffer() text_memo.writetobuffer(buf) self.writefile("sch/neomemo.dat", buf.getvalue()) return result def getcalendar(self,result): res={} # Read exceptions file first try: buf=prototypes.buffer(self.getfilecontents("sch/newschexception.dat")) ex=self.protocolclass.scheduleexceptionfile() ex.readfrombuffer(buf) self.logdata("Calendar exceptions", buf.getdata(), ex) exceptions={} for i in ex.items: try: exceptions[i.pos].append( (i.year,i.month,i.day) ) except KeyError: exceptions[i.pos]=[ (i.year,i.month,i.day) ] except com_brew.BrewNoSuchFileException: exceptions={} # Now read schedule try: buf=prototypes.buffer(self.getfilecontents("sch/newschedule.dat")) if len(buf.getdata())<3: # file is empty, and hence same as non-existent raise com_brew.BrewNoSuchFileException() sc=self.protocolclass.schedulefile() self.logdata("Calendar", buf.getdata(), sc) sc.readfrombuffer(buf) for event in sc.events: # the vx8100 has a bad entry when the calender is empty # stop processing the calender when we hit this record if event.pos==0: #invalid entry break entry=bpcalendar.CalendarEntry() entry.description=event.description entry.start=event.start entry.end=event.end entry.vibrate=~(event.alarmindex_vibrate&0x1) # vibarate bit is inverted in phone 0=on, 1=off entry.repeat = self.makerepeat(event.repeat) min=event.alarmminutes hour=event.alarmhours if min==0x64 or hour==0x64: entry.alarm=None # no alarm set else: entry.alarm=hour*60+min entry.ringtone=result['ringtone-index'][event.ringtone]['name'] entry.snoozedelay=0 # check for exceptions and remove them if event.repeat[3] and exceptions.has_key(event.pos): for year, month, day in exceptions[event.pos]: entry.suppress_repeat_entry(year, month, day) res[entry.id]=entry assert sc.numactiveitems==len(res) except com_brew.BrewNoSuchFileException: pass # do nothing if file doesn't exist result['calendar']=res return result def makerepeat(self, repeat): # get all the variables out of the repeat tuple # and convert into a bpcalender RepeatEntry type,dow,interval,exceptions=repeat if type==0: repeat_entry=None else: repeat_entry=bpcalendar.RepeatEntry() if type==1: #daily repeat_entry.repeat_type=repeat_entry.daily repeat_entry.interval=interval elif type==5: #'monfri' repeat_entry.repeat_type=repeat_entry.daily repeat_entry.interval=0 elif type==2: #'weekly' repeat_entry.repeat_type=repeat_entry.weekly repeat_entry.dow=dow repeat_entry.interval=interval elif type==3: #'monthly' repeat_entry.repeat_type=repeat_entry.monthly repeat_entry.interval=interval repeat_entry.dow=0 elif type==6: #'monthly' #Xth Y day (e.g. 2nd friday each month) repeat_entry.repeat_type=repeat_entry.monthly repeat_entry.interval=interval #X repeat_entry.dow=dow #Y else: # =4 'yearly' repeat_entry.repeat_type=repeat_entry.yearly return repeat_entry def savecalendar(self, dict, merge): # ::TODO:: # what will be written to the files eventsf=self.protocolclass.schedulefile() exceptionsf=self.protocolclass.scheduleexceptionfile() # what are we working with cal=dict['calendar'] newcal={} #sort into start order, makes it possible to see if the calendar has changed keys=[(x.start, k) for k,x in cal.items()] keys.sort() # number of entries eventsf.numactiveitems=len(keys) pos=1 contains_alarms=False # play with each entry for (_,k) in keys: # entry is what we will return to user entry=cal[k] data=self.protocolclass.scheduleevent() data.pos=eventsf.packetsize() data.description=entry.description data.start=entry.start data.end=entry.end self.setalarm(entry, data) data.ringtone=0 for i in dict['ringtone-index']: if dict['ringtone-index'][i]['name']==entry.ringtone: data.ringtone=i # check for exceptions and add them to the exceptions list exceptions=0 if entry.repeat!=None: if data.end[:3]==entry.no_end_date: data.end=(2100, 12, 31)+data.end[3:] for i in entry.repeat.suppressed: de=self.protocolclass.scheduleexception() de.pos=data.pos de.day=i.date.day de.month=i.date.month de.year=i.date.year exceptions=1 exceptionsf.items.append(de) if entry.repeat != None: data.repeat=(self.getrepeattype(entry, exceptions)) else: data.repeat=((0,0,0,0)) data.unknown1=0 data.unknown2=0 if data.alarmindex_vibrate!=1: # if alarm set contains_alarms=True # put entry in nice shiny new dict we are building entry=copy.copy(entry) newcal[data.pos]=entry eventsf.events.append(data) buf=prototypes.buffer() eventsf.writetobuffer(buf) # We check the existing calender as changes require a reboot for the alarms # to work properly, also no point writing the file if it is not changing if buf.getvalue()!=self.getfilecontents("sch/newschedule.dat"): self.logdata("Writing calendar", buf.getvalue(), eventsf) self.writefile("sch/newschedule.dat", buf.getvalue()) if contains_alarms: self.log("Your phone has to be rebooted due to the calendar changing") dict["rebootphone"]=True else: self.log("Phone calendar unchanged, no update required") buf=prototypes.buffer() exceptionsf.writetobuffer(buf) self.logdata("Writing calendar exceptions", buf.getvalue(), exceptionsf) self.writefile("sch/newschexception.dat", buf.getvalue()) # fix passed in dict dict['calendar']=newcal return dict def getrepeattype(self, entry, exceptions): #convert the bpcalender type into vx8100 type repeat_entry=bpcalendar.RepeatEntry() if entry.repeat.repeat_type==repeat_entry.monthly: dow=entry.repeat.dow if entry.repeat.dow==0: # set interval for month type 4 to start day of month, (required by vx8100) interval=entry.start[2] type=3 else: interval=entry.repeat.interval type=6 elif entry.repeat.repeat_type==repeat_entry.daily: dow=entry.repeat.dow interval=entry.repeat.interval if entry.repeat.interval==0: type=5 else: type=1 elif entry.repeat.repeat_type==repeat_entry.weekly: dow=entry.repeat.dow interval=entry.repeat.interval type=2 elif entry.repeat.repeat_type==repeat_entry.yearly: # set interval to start day of month, (required by vx8100) interval=entry.start[2] # set dow to start month, (required by vx8100) dow=entry.start[1] type=4 return (type, dow, interval, exceptions) def setalarm(self, entry, data): # vx8100 only allows certain repeat intervals, adjust to fit, it also stores an index to the interval if entry.alarm>=2880: entry.alarm=2880 data.alarmminutes=0 data.alarmhours=48 data.alarmindex_vibrate=0x10 elif entry.alarm>=1440: entry.alarm=1440 data.alarmminutes=0 data.alarmhours=24 data.alarmindex_vibrate=0xe elif entry.alarm>=120: entry.alarm=120 data.alarmminutes=0 data.alarmhours=2 data.alarmindex_vibrate=0xc elif entry.alarm>=60: entry.alarm=60 data.alarmminutes=0 data.alarmhours=1 data.alarmindex_vibrate=0xa elif entry.alarm>=15: entry.alarm=15 data.alarmminutes=15 data.alarmhours=0 data.alarmindex_vibrate=0x8 elif entry.alarm>=10: entry.alarm=10 data.alarmminutes=10 data.alarmhours=0 data.alarmindex_vibrate=0x6 elif entry.alarm>=5: entry.alarm=5 data.alarmminutes=5 data.alarmhours=0 data.alarmindex_vibrate=0x4 elif entry.alarm>=0: entry.alarm=0 data.alarmminutes=0 data.alarmhours=0 data.alarmindex_vibrate=0x2 else: # no alarm data.alarmminutes=0x64 data.alarmhours=0x64 data.alarmindex_vibrate=1 # set the vibrate bit if data.alarmindex_vibrate > 1 and entry.vibrate==0: data.alarmindex_vibrate+=1 return my_model='VX8100' def getphoneinfo(self, phone_info): self.log('Getting Phone Info') try: s=self.getfilecontents('brew/version.txt') if s[:6]=='VX8100': phone_info.append('Model:', "VX8100") req=p_brew.firmwarerequest() res=self.sendbrewcommand(req, self.protocolclass.firmwareresponse) phone_info.append('Firmware Version:', res.firmware) s=self.getfilecontents("nvm/$SYS.ESN")[85:89] txt='%02X%02X%02X%02X'%(ord(s[3]), ord(s[2]), ord(s[1]), ord(s[0])) phone_info.append('ESN:', txt) txt=self.getfilecontents("nvm/nvm/nvm_cdma")[180:190] phone_info.append('Phone Number:', txt) except: pass return parentprofile=com_lgvx7000.Profile class Profile(parentprofile): protocolclass=Phone.protocolclass serialsname=Phone.serialsname BP_Calendar_Version=3 phone_manufacturer='LG Electronics Inc' phone_model='VX8100' WALLPAPER_WIDTH=176 WALLPAPER_HEIGHT=184 MAX_WALLPAPER_BASENAME_LENGTH=32 WALLPAPER_FILENAME_CHARS="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 ." WALLPAPER_CONVERT_FORMAT="jpg" MAX_RINGTONE_BASENAME_LENGTH=32 RINGTONE_FILENAME_CHARS="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 ." # there is an origin named 'aod' - no idea what it is for except maybe # 'all other downloads' # the vx8100 supports bluetooth for connectivity to the PC, define the "bluetooth_mgd_id" # to enable bluetooth discovery during phone detection # the bluetooth address starts with LG's the three-octet OUI, all LG phone # addresses start with this, it provides a way to identify LG bluetooth devices # during phone discovery # OUI=Organizationally Unique Identifier # see http://standards.ieee.org/regauth/oui/index.shtml for more info bluetooth_mfg_id="001256" # the 8100 doesn't have seperate origins - they are all dumped in "images" imageorigins={} imageorigins.update(common.getkv(parentprofile.stockimageorigins, "images")) def GetImageOrigins(self): return self.imageorigins # our targets are the same for all origins imagetargets={} imagetargets.update(common.getkv(parentprofile.stockimagetargets, "wallpaper", {'width': 176, 'height': 184, 'format': "JPEG"})) imagetargets.update(common.getkv(parentprofile.stockimagetargets, "pictureid", {'width': 176, 'height': 184, 'format': "JPEG"})) imagetargets.update(common.getkv(parentprofile.stockimagetargets, "outsidelcd", {'width': 96, 'height': 80, 'format': "JPEG"})) imagetargets.update(common.getkv(parentprofile.stockimagetargets, "fullscreen", {'width': 176, 'height': 220, 'format': "JPEG"})) def GetTargetsForImageOrigin(self, origin): return self.imagetargets def __init__(self): parentprofile.__init__(self) _supportedsyncs=( ('phonebook', 'read', None), # all phonebook reading ('calendar', 'read', None), # all calendar reading ('wallpaper', 'read', None), # all wallpaper reading ('ringtone', 'read', None), # all ringtone reading ('call_history', 'read', None),# all call history list reading ('sms', 'read', None), # all SMS list reading ('memo', 'read', None), # all memo list reading ('phonebook', 'write', 'OVERWRITE'), # only overwriting phonebook ('calendar', 'write', 'OVERWRITE'), # only overwriting calendar ('wallpaper', 'write', 'MERGE'), # merge and overwrite wallpaper ('wallpaper', 'write', 'OVERWRITE'), ('ringtone', 'write', 'MERGE'), # merge and overwrite ringtone ('ringtone', 'write', 'OVERWRITE'), ('sms', 'write', 'OVERWRITE'), # all SMS list writing ('memo', 'write', 'OVERWRITE'), # all memo list writing )
com_lgvx8100.py.diff
(application/octet-stream, 3 KB)
Index: com_lgvx8100.py
===================================================================
RCS file: /cvsroot/bitpim/bitpim/com_lgvx8100.py,v
retrieving revision 1.6
diff -u -r1.6 com_lgvx8100.py
--- com_lgvx8100.py 13 Sep 2005 02:07:37 -0000 1.6
+++ com_lgvx8100.py 16 Sep 2005 08:46:51 -0000
@@ -242,19 +242,18 @@
# what will be written to the files
eventsf=self.protocolclass.schedulefile()
exceptionsf=self.protocolclass.scheduleexceptionfile()
-
# what are we working with
cal=dict['calendar']
newcal={}
- keys=cal.keys()
+ #sort into start order, makes it possible to see if the calendar has changed
+ keys=[(x.start, k) for k,x in cal.items()]
keys.sort()
- pos=1
-
# number of entries
eventsf.numactiveitems=len(keys)
-
+ pos=1
+ contains_alarms=False
# play with each entry
- for k in keys:
+ for (_,k) in keys:
# entry is what we will return to user
entry=cal[k]
data=self.protocolclass.scheduleevent()
@@ -270,6 +269,8 @@
# check for exceptions and add them to the exceptions list
exceptions=0
if entry.repeat!=None:
+ if data.end[:3]==entry.no_end_date:
+ data.end=(2100, 12, 31)+data.end[3:]
for i in entry.repeat.suppressed:
de=self.protocolclass.scheduleexception()
de.pos=data.pos
@@ -282,20 +283,28 @@
data.repeat=(self.getrepeattype(entry, exceptions))
else:
data.repeat=((0,0,0,0))
-
data.unknown1=0
data.unknown2=0
-
+ if data.alarmindex_vibrate!=1: # if alarm set
+ contains_alarms=True
# put entry in nice shiny new dict we are building
entry=copy.copy(entry)
newcal[data.pos]=entry
eventsf.events.append(data)
- # scribble everything out
buf=prototypes.buffer()
eventsf.writetobuffer(buf)
- self.logdata("Writing calendar", buf.getvalue(), eventsf)
- self.writefile("sch/newschedule.dat", buf.getvalue())
+ # We check the existing calender as changes require a reboot for the alarms
+ # to work properly, also no point writing the file if it is not changing
+ if buf.getvalue()!=self.getfilecontents("sch/newschedule.dat"):
+ self.logdata("Writing calendar", buf.getvalue(), eventsf)
+ self.writefile("sch/newschedule.dat", buf.getvalue())
+ if contains_alarms:
+ self.log("Your phone has to be rebooted due to the calendar changing")
+ dict["rebootphone"]=True
+ else:
+ self.log("Phone calendar unchanged, no update required")
+
buf=prototypes.buffer()
exceptionsf.writetobuffer(buf)
self.logdata("Writing calendar exceptions", buf.getvalue(), exceptionsf)
common_calendar.py
(text/plain, 20.5 KB)
### BITPIM ### ### Copyright (C) 2004 Joe Pham <[email protected]> ### ### This program is free software; you can redistribute it and/or modify ### it under the terms of the BitPim license as detailed in the LICENSE file. ### ### $Id: common_calendar.py,v 1.10 2005/05/05 04:24:22 djpham Exp $ "Common stuff for the Calendar Import functions" # system modules import sys # wxPython modules import wx import wx.calendar import wx.lib.mixins.listctrl as listmix # local modules import guiwidgets import pubsub no_end_date=(4000, 1, 1, 0, 0) def bp_repeat_str(dict, v): if v is None: return '' return v def bp_date_str(dict, v): try: if v[0]>=no_end_date[0]: # no-end date, don't display it if dict.get('allday', False): return '' else: return '%02d:%02d'%v[3:] if dict.get('allday', False): return '%04d-%02d-%02d'%v[:3] else: return '%04d-%02d-%02d %02d:%02d'% v except (ValueError, TypeError): return '' except: if __debug__: raise return '' def bp_alarm_str(dict, v): try: if dict.get('alarm', False): v=dict.get('alarm_value', 0) if v: return '-%d min'%v else: return 'Ontime' else: return '' except (ValueError, TypeError): return '' except: if __debug__: raise return '' def category_str(dict, v): try: s='' for d in v: if len(d): if len(s): s+=', '+d else: s=d return s except (ValueError, TypeError): return '' except: if __debug__: raise return '' #------------------------------------------------------------------------------- class PreviewDialog(wx.Dialog, listmix.ColumnSorterMixin): def __init__(self, parent, id, title, col_labels, data={}, config_name=None, style=wx.CAPTION|wx.MAXIMIZE_BOX| \ wx.SYSTEM_MENU|wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER): wx.Dialog.__init__(self, parent, id=id, title=title, style=style) self.__col_labels=col_labels self.__config_name=config_name self.itemDataMap={} # main boxsizer main_bs=wx.BoxSizer(wx.VERTICAL) # add custom controls here self.getcontrols(main_bs) # create a data preview list with supplied column labels self.__list=wx.ListView(self, wx.NewId()) self.__image_list=wx.ImageList(16, 16) self.__ig_up=self.__image_list.Add(wx.ArtProvider_GetBitmap(wx.ART_GO_UP, wx.ART_OTHER, (16, 16))) self.__ig_dn=self.__image_list.Add(wx.ArtProvider_GetBitmap(wx.ART_GO_DOWN, wx.ART_OTHER, (16, 16))) self.__list.SetImageList(self.__image_list, wx.IMAGE_LIST_SMALL) li=wx.ListItem() li.m_mask=wx.LIST_MASK_TEXT | wx.LIST_MASK_IMAGE li.m_image=-1 for i, d in enumerate(self.__col_labels): # insert a column with specified name and width li.m_text=d[1] self.__list.InsertColumnInfo(i, li) self.__list.SetColumnWidth(i, d[2]) main_bs.Add(self.__list, 1, wx.EXPAND, 0) self.populate(data) # the Mixin sorter listmix.ColumnSorterMixin.__init__(self, len(col_labels)) # now the buttons self.getpostcontrols(main_bs) # handle events # all done self.SetSizer(main_bs) self.SetAutoLayout(True) main_bs.Fit(self) # save my own size, if specified if config_name is not None: guiwidgets.set_size(config_name, self) wx.EVT_SIZE(self, self.__save_size) def getcontrols(self, main_bs): # controls to be placed above the preview pane # by default, put nothing. pass def getpostcontrols(self, main_bs): # control to be placed below the preview pane # by default, just add the OK & CANCEL button main_bs.Add(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 5) main_bs.Add(self.CreateButtonSizer(wx.OK|wx.CANCEL), 0, wx.ALIGN_CENTRE|wx.ALL, 5) def populate(self, data): self.__list.DeleteAllItems() m={} m_count=0 for k in data: try: d=data[k] col_idx=None mm={} for i, l in enumerate(self.__col_labels): entry=d.get(l[0], None) s='' if l[3] is None: s=str(entry) else: s=l[3](d, entry) mm[i]=s if i: self.__list.SetStringItem(col_idx, i, s) else: col_idx=self.__list.InsertImageStringItem(sys.maxint, s, -1) self.__list.SetItemData(col_idx, m_count) m[m_count]=mm m_count += 1 except: # something wrong happened, drop this event if __debug__: raise self.itemDataMap=m def GetListCtrl(self): return self.__list def GetSortImages(self): return (self.__ig_dn, self.__ig_up) def __save_size(self, evt): if self.__config_name is not None: guiwidgets.save_size(self.__config_name, self.GetRect()) evt.Skip() #------------------------------------------------------------------------------- class FilterDialogBase(wx.Dialog): unnamed="Select:" def __init__(self, parent, id, caption, categories, style=wx.DEFAULT_DIALOG_STYLE): wx.Dialog.__init__(self, parent, id, title=caption, style=style) # the main box sizer bs=wx.BoxSizer(wx.VERTICAL) # the flex grid sizers for the editable items main_fgs=wx.FlexGridSizer(0, 1, 0, 0) fgs=wx.FlexGridSizer(3, 2, 0, 5) fgs1=wx.FlexGridSizer(0, 1, 0, 0) fgs2=wx.FlexGridSizer(0, 2, 0, 5) # set the date options self.SetDateControls(fgs, fgs1) # new repeat to single events option self.__rpt_chkbox=wx.CheckBox(self, id=wx.NewId(), label='Repeat Events:', style=wx.ALIGN_RIGHT) self.__rpt_chkbox.Disable() fgs.Add(self.__rpt_chkbox, 0, wx.ALIGN_RIGHT|wx.TOP|wx.BOTTOM, 5) self.__rpt_chkbox_text=wx.StaticText(self, -1, 'Import as multi-single events.') fgs.Add(self.__rpt_chkbox_text, 0, wx.ALIGN_LEFT|wx.ALIGN_CENTRE, 0) self.__rpt_chkbox_text.Disable() # alarm option choices=('Disable All Alarms', 'Use Alarm Settings From Calender', 'Set Alarm On All Events') self.__alarm_setting = wx.RadioBox(self, id=wx.NewId(), label="Select Alarm Settings For Imported Events", choices=choices, majorDimension=1, size=(280,-1)) fgs1.Add(self.__alarm_setting, 0, wx.ALIGN_CENTRE|wx.TOP|wx.BOTTOM, 5) #alarm vibrate self.__vibrate=wx.CheckBox(self, id=wx.NewId(), label='Alarm Vibrate:', style=wx.ALIGN_RIGHT) fgs2.Add(self.__vibrate, 0, wx.ALIGN_RIGHT|wx.TOP|wx.BOTTOM, 5) self.__vibrate_text=wx.StaticText(self, -1, 'Enable vibrate for alarms.') fgs2.Add(self.__vibrate_text, 0, wx.ALIGN_LEFT|wx.TOP|wx.BOTTOM, 5) # alarm settings self.__ringtone_text=wx.StaticText(self, -1, 'Alarm Ringtone:') fgs2.Add(self.__ringtone_text, 0, wx.ALIGN_RIGHT|wx.TOP|wx.BOTTOM, 5) self.__ringtone=wx.ComboBox(self, id=wx.NewId(), style=wx.CB_DROPDOWN|wx.CB_READONLY, choices=[self.unnamed], size=(160,-1)) fgs2.Add(self.__ringtone, 0, wx.ALIGN_LEFT|wx.TOP|wx.BOTTOM, 2) # alarm value self.__alarm_value_text=wx.StaticText(self, -1, 'Alert before (mins):') fgs2.Add(self.__alarm_value_text, 0, wx.ALIGN_RIGHT|wx.TOP|wx.BOTTOM, 5) self.__alarm_value=wx.lib.intctrl.IntCtrl(self, id=wx.NewId(), size=(50,-1), value=0, min=0, max=1000) fgs2.Add( self.__alarm_value, 0, wx.ALIGN_LEFT|wx.TOP|wx.BOTTOM, 2) # category option self.__cat_chkbox=wx.CheckBox(self, id=wx.NewId(), label='Categories:', style=wx.ALIGN_RIGHT) fgs2.Add(self.__cat_chkbox, 0, wx.ALIGN_RIGHT|wx.TOP|wx.BOTTOM, 5) for i,c in enumerate(categories): if not len(c): categories[i]='<None>' self.__cats=wx.CheckListBox(self, choices=categories, size=(160, 50)) self.__cats.Disable() fgs2.Add(self.__cats, 0, wx.ALIGN_LEFT, 0) main_fgs.Add(fgs, 1, wx.EXPAND|wx.ALL, 0) main_fgs.Add(fgs1, 1, wx.EXPAND|wx.ALL, 0) main_fgs.Add(fgs2, 1, wx.EXPAND|wx.ALL, 0) bs.Add(main_fgs, 1, wx.EXPAND|wx.ALL, 5) # the buttons bs.Add(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 5) bs.Add(self.CreateButtonSizer(wx.OK|wx.CANCEL), 0, wx.ALIGN_CENTRE|wx.ALL, 5) # event handles wx.EVT_CHECKBOX(self, self._start_date_chkbox.GetId(), self.OnCheckBox) wx.EVT_CHECKBOX(self, self._end_date_chkbox.GetId(), self.OnCheckBox) wx.EVT_CHECKBOX(self, self.__cat_chkbox.GetId(), self.OnCheckBox) wx.EVT_RADIOBOX(self, self.__alarm_setting.GetId(), self.OnAlarmSetting) # all done self.SetSizer(bs) self.SetAutoLayout(True) bs.Fit(self) def ShowModal(self): # request ringtones from pubsub.subscribe(self.OnRingtoneUpdates, pubsub.ALL_RINGTONES) wx.CallAfter(pubsub.publish, pubsub.REQUEST_RINGTONES) # make the call once we are onscreen return wx.Dialog.ShowModal(self) def OnRingtoneUpdates(self, msg): "Receives pubsub message with ringtone list" tones=msg.data[:] #cur=self.Get() try: self.__ringtone.Clear() self.__ringtone.Append(self.unnamed) for p in tones: self.__ringtone.Append(p) rt=self.__ringtone.SetStringSelection(self.ringtone) except: self.ringtone=self.unnamed def __set_cats(self, chk_box, c, data): if data is None: chk_box.SetValue(False) c.Disable() else: chk_box.SetValue(True) c.Enable() for i,d in enumerate(data): if not len(d): data[i]='<None>' for i in range(c.GetCount()): c.Check(i, c.GetString(i) in data) def __set_rpt(self, data): if self._start_date_chkbox.GetValue() and\ self._end_date_chkbox.GetValue(): self.__rpt_chkbox.Enable() self.__rpt_chkbox_text.Enable() self.__rpt_chkbox.SetValue(data) else: self.__rpt_chkbox.SetValue(False) self.__rpt_chkbox.Disable() self.__rpt_chkbox_text.Disable() def __set_alarm_fields(self, value): if value==0: self.__vibrate.Disable() self.__alarm_value.Disable() self.__ringtone.Disable() self.__vibrate_text.Disable() self.__alarm_value_text.Disable() self.__ringtone_text.Disable() elif value==1: self.__vibrate.Enable() self.__alarm_value.Disable() self.__ringtone.Enable() self.__vibrate_text.Enable() self.__alarm_value_text.Disable() self.__ringtone_text.Enable() else: self.__vibrate.Enable() self.__alarm_value.Enable() self.__ringtone.Enable() self.__vibrate_text.Enable() self.__alarm_value_text.Enable() self.__ringtone_text.Enable() def set_base(self, data): self.__set_rpt(data.get('rpt_events', False)) no_alarm=data.get('no_alarm', False) alarm_override=data.get('alarm_override', False) if no_alarm: value=0 elif alarm_override: value=2 else: value=1 self.__set_alarm_fields(value) self.__alarm_setting.SetSelection(value) self.ringtone=data.get('ringtone', self.unnamed) try: self.__ringtone.SetStringSelection(ringtone) except: self.__ringtone.SetStringSelection(self.unnamed) value=data.get('vibrate', False); self.__vibrate.SetValue(value) self.__alarm_value.SetValue(data.get('alarm_value', 0)) self.__set_cats(self.__cat_chkbox, self.__cats, data.get('categories', None)) def get_base(self, r): r['rpt_events']=self.__rpt_chkbox.GetValue() value=self.__alarm_setting.GetSelection() if value==0: r['no_alarm']=True r['alarm_override']=False elif value==1: r['no_alarm']=False r['alarm_override']=False else: r['no_alarm']=False r['alarm_override']=True r['ringtone']=self.__ringtone.GetStringSelection() r['vibrate']=self.__vibrate.GetValue() r['alarm_value']=self.__alarm_value.GetValue() if self.__cat_chkbox.GetValue(): c=[] for i in range(self.__cats.GetCount()): if self.__cats.IsChecked(i): s=self.__cats.GetString(i) if s=='<None>': c.append('') else: c.append(s) r['categories']=c else: r['categories']=None return def OnAlarmSetting(self, _): self.__set_alarm_fields(self.__alarm_setting.GetSelection()) def OnCheckBox(self, evt): evt_id=evt.GetId() if evt_id==self._start_date_chkbox.GetId(): w1,w2=self._start_date_chkbox, self._start_date elif evt_id==self._end_date_chkbox.GetId(): w1,w2=self._end_date_chkbox, self._end_date else: w1,w2=self.__cat_chkbox, self.__cats if w1.GetValue(): w2.Enable() else: w2.Disable() # turn on the repeat event option of both start date and end date # are specified. if self._start_date_chkbox.GetValue() and \ self._end_date_chkbox.GetValue(): self.__rpt_chkbox.Enable() self.__rpt_chkbox_text.Enable() else: self.__rpt_chkbox.SetValue(False) self.__rpt_chkbox.Disable() self.__rpt_chkbox_text.Disable() #------------------------------------------------------------------------------- class FilterDialog(FilterDialogBase): def __init__(self, parent, id, caption, categories, style=wx.DEFAULT_DIALOG_STYLE): FilterDialogBase.__init__(self, parent, id, caption, categories, style) def SetDateControls(self, fgs, fgs1): self._start_date_chkbox=wx.CheckBox(self, id=wx.NewId(), label='Start Date:', style=wx.ALIGN_RIGHT) fgs.Add(self._start_date_chkbox, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTRE_VERTICAL, 0) self._start_date=wx.calendar.CalendarCtrl(self, -1, wx.DateTime_Now(), style = wx.calendar.CAL_SUNDAY_FIRST | wx.calendar.CAL_SEQUENTIAL_MONTH_SELECTION) self._start_date.Disable() fgs.Add(self._start_date, 1, wx.ALIGN_LEFT, 5) self._end_date_chkbox=wx.CheckBox(self, id=wx.NewId(), label='End Date:', style=wx.ALIGN_RIGHT) fgs.Add(self._end_date_chkbox, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTRE_VERTICAL, 0) self._end_date=wx.calendar.CalendarCtrl(self, -1, wx.DateTime_Now(), style = wx.calendar.CAL_SUNDAY_FIRST | wx.calendar.CAL_SEQUENTIAL_MONTH_SELECTION) self._end_date.Disable() fgs.Add(self._end_date, 1, wx.ALIGN_LEFT, 5) def __set_date(self, chk_box, cal, d): if d is None: chk_box.SetValue(False) cal.Disable() else: chk_box.SetValue(True) cal.Enable() dt=wx.DateTime() dt.Set(d[2], year=d[0], month=d[1]-1) cal.SetDate(dt) def set(self, data): self.__set_date(self._start_date_chkbox, self._start_date, data.get('start', None)) self.__set_date(self._end_date_chkbox, self._end_date, data.get('end', None)) self.set_base(data) def get(self): r={} if self._start_date_chkbox.GetValue(): dt=self._start_date.GetDate() r['start']=(dt.GetYear(), dt.GetMonth()+1, dt.GetDay()) else: r['start']=None if self._end_date_chkbox.GetValue(): dt=self._end_date.GetDate() r['end']=(dt.GetYear(), dt.GetMonth()+1, dt.GetDay()) else: r['end']=None self.get_base(r) return r #------------------------------------------------------------------------------- class AutoSyncFilterDialog(FilterDialogBase): def __init__(self, parent, id, caption, categories, style=wx.DEFAULT_DIALOG_STYLE): FilterDialogBase.__init__(self, parent, id, caption, categories, style) def SetDateControls(self, fgs, fgs1): #start_offset self._start_date_chkbox=wx.CheckBox(self, id=wx.NewId(), label='Start Offset (days):', style=wx.ALIGN_RIGHT) fgs.Add(self._start_date_chkbox, 0, wx.ALIGN_RIGHT|wx.TOP|wx.BOTTOM, 5) self._start_date=wx.lib.intctrl.IntCtrl(self, id=wx.NewId(), size=(50,-1), value=0, min=0, max=1000) self._start_date.Disable() fgs.Add( self._start_date, 0, wx.ALIGN_LEFT|wx.TOP|wx.BOTTOM, 2) #end_offset self._end_date_chkbox=wx.CheckBox(self, id=wx.NewId(), label='End Offset (days):', style=wx.ALIGN_RIGHT) fgs.Add(self._end_date_chkbox, 0, wx.ALIGN_RIGHT|wx.TOP|wx.BOTTOM, 5) self._end_date=wx.lib.intctrl.IntCtrl(self, id=wx.NewId(), size=(50,-1), value=0, min=0, max=1000) self._end_date.Disable() fgs.Add( self._end_date, 0, wx.ALIGN_LEFT|wx.TOP|wx.BOTTOM, 2) fgs1.Add(wx.StaticText(self, -1, 'Note: The start offset is the number of days' + ' in the past, and the end offset is the number of days' + ' in the future imported from the calender into your phone. If' + ' disabled, all past and/or future events are imported.', size=(270,55)), 0, wx.ALIGN_LEFT|wx.TOP|wx.BOTTOM, 5) def __set_start_date(self, d): if d is None: self._start_date_chkbox.SetValue(False) self._start_date.Disable() else: self._start_date_chkbox.SetValue(True) self._start_date.Enable() self._start_date.SetValue(d) def __set_end_date(self, d): if d is None: self._end_date_chkbox.SetValue(False) self._end_date.Disable() else: self._end_date_chkbox.SetValue(True) self._end_date.Enable() self._end_date.SetValue(d) def set(self, data): self.__set_start_date(data.get('start_offset', None)) self.__set_end_date(data.get('end_offset', None)) self.set_base(data) def get(self): r={} if self._start_date_chkbox.GetValue(): r['start_offset']=self._start_date.GetValue() else: r['start_offset']=None if self._end_date_chkbox.GetValue(): r['end_offset']=self._end_date.GetValue() else: r['end_offset']=None self.get_base(r) return r
common_calendar.py.diff
(application/octet-stream, 19.1 KB)
Index: common_calendar.py
===================================================================
RCS file: /cvsroot/bitpim/bitpim/common_calendar.py,v
retrieving revision 1.10
diff -u -r1.10 common_calendar.py
--- common_calendar.py 5 May 2005 04:24:22 -0000 1.10
+++ common_calendar.py 16 Sep 2005 10:21:32 -0000
@@ -19,6 +19,7 @@
# local modules
import guiwidgets
+import pubsub
no_end_date=(4000, 1, 1, 0, 0)
@@ -180,76 +181,101 @@
evt.Skip()
#-------------------------------------------------------------------------------
-class FilterDialog(wx.Dialog):
+class FilterDialogBase(wx.Dialog):
+ unnamed="Select:"
def __init__(self, parent, id, caption, categories, style=wx.DEFAULT_DIALOG_STYLE):
wx.Dialog.__init__(self, parent, id,
title=caption, style=style)
# the main box sizer
bs=wx.BoxSizer(wx.VERTICAL)
# the flex grid sizers for the editable items
- fgs=wx.FlexGridSizer(3, 3, 0, 5)
- fgs.Add(wx.StaticText(self, -1, 'Start Date:'), 0, wx.ALIGN_CENTRE, 0)
- self.__start_date_chkbox=wx.CheckBox(self, id=wx.NewId())
- fgs.Add(self.__start_date_chkbox, 0, wx.ALIGN_CENTRE, 0)
- self.__start_date=wx.calendar.CalendarCtrl(self, -1, wx.DateTime_Now(),
- style = wx.calendar.CAL_SUNDAY_FIRST
- | wx.calendar.CAL_SEQUENTIAL_MONTH_SELECTION)
- self.__start_date.Disable()
- fgs.Add(self.__start_date, 1, wx.ALIGN_LEFT, 5)
- fgs.Add(wx.StaticText(self, -1, 'End Date:'), 0, wx.ALIGN_LEFT|wx.ALIGN_CENTRE, 0)
- self.__end_date_chkbox=wx.CheckBox(self, id=wx.NewId())
- fgs.Add(self.__end_date_chkbox, 0, wx.ALIGN_CENTRE, 0)
- self.__end_date=wx.calendar.CalendarCtrl(self, -1, wx.DateTime_Now(),
- style = wx.calendar.CAL_SUNDAY_FIRST
- | wx.calendar.CAL_SEQUENTIAL_MONTH_SELECTION)
- self.__end_date.Disable()
- fgs.Add(self.__end_date, 1, wx.ALIGN_LEFT, 5)
+ main_fgs=wx.FlexGridSizer(0, 1, 0, 0)
+ fgs=wx.FlexGridSizer(3, 2, 0, 5)
+ fgs1=wx.FlexGridSizer(0, 1, 0, 0)
+ fgs2=wx.FlexGridSizer(0, 2, 0, 5)
+ # set the date options
+ self.SetDateControls(fgs, fgs1)
# new repeat to single events option
- fgs.Add(wx.StaticText(self, -1, 'Repeat Events:'), 0, wx.ALIGN_LEFT|wx.ALIGN_CENTRE, 0)
- self.__rpt_chkbox=wx.CheckBox(self, id=wx.NewId())
+ self.__rpt_chkbox=wx.CheckBox(self, id=wx.NewId(), label='Repeat Events:',
+ style=wx.ALIGN_RIGHT)
self.__rpt_chkbox.Disable()
- fgs.Add(self.__rpt_chkbox, 0, wx.ALIGN_CENTRE|wx.TOP|wx.BOTTOM, 5)
- fgs.Add(wx.StaticText(self, -1, 'Import as multi-single events.'),
- 0, wx.ALIGN_LEFT|wx.ALIGN_CENTRE, 0)
+ fgs.Add(self.__rpt_chkbox, 0, wx.ALIGN_RIGHT|wx.TOP|wx.BOTTOM, 5)
+ self.__rpt_chkbox_text=wx.StaticText(self, -1, 'Import as multi-single events.')
+ fgs.Add(self.__rpt_chkbox_text, 0, wx.ALIGN_LEFT|wx.ALIGN_CENTRE, 0)
+ self.__rpt_chkbox_text.Disable()
# alarm option
- fgs.Add(wx.StaticText(self, -1, 'No Alarm'), 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER, 0)
- self.__no_alarm_chkbox=wx.CheckBox(self, id=wx.NewId())
- fgs.Add(self.__no_alarm_chkbox, 0, wx.ALIGN_CENTRE|wx.TOP|wx.BOTTOM, 5)
- fgs.Add(wx.StaticText(self, -1, 'Turn off all events alarms.'),
- 0, wx.ALIGN_LEFT|wx.ALIGN_CENTRE, 0)
+ choices=('Disable All Alarms', 'Use Alarm Settings From Calender',
+ 'Set Alarm On All Events')
+ self.__alarm_setting = wx.RadioBox(self, id=wx.NewId(),
+ label="Select Alarm Settings For Imported Events",
+ choices=choices,
+ majorDimension=1,
+ size=(280,-1))
+ fgs1.Add(self.__alarm_setting, 0, wx.ALIGN_CENTRE|wx.TOP|wx.BOTTOM, 5)
+ #alarm vibrate
+ self.__vibrate=wx.CheckBox(self, id=wx.NewId(), label='Alarm Vibrate:',
+ style=wx.ALIGN_RIGHT)
+ fgs2.Add(self.__vibrate, 0, wx.ALIGN_RIGHT|wx.TOP|wx.BOTTOM, 5)
+ self.__vibrate_text=wx.StaticText(self, -1, 'Enable vibrate for alarms.')
+ fgs2.Add(self.__vibrate_text, 0, wx.ALIGN_LEFT|wx.TOP|wx.BOTTOM, 5)
+ # alarm settings
+ self.__ringtone_text=wx.StaticText(self, -1, 'Alarm Ringtone:')
+ fgs2.Add(self.__ringtone_text, 0, wx.ALIGN_RIGHT|wx.TOP|wx.BOTTOM, 5)
+ self.__ringtone=wx.ComboBox(self, id=wx.NewId(),
+ style=wx.CB_DROPDOWN|wx.CB_READONLY,
+ choices=[self.unnamed], size=(160,-1))
+ fgs2.Add(self.__ringtone, 0, wx.ALIGN_LEFT|wx.TOP|wx.BOTTOM, 2)
+ # alarm value
+ self.__alarm_value_text=wx.StaticText(self, -1, 'Alert before (mins):')
+ fgs2.Add(self.__alarm_value_text, 0, wx.ALIGN_RIGHT|wx.TOP|wx.BOTTOM, 5)
+ self.__alarm_value=wx.lib.intctrl.IntCtrl(self, id=wx.NewId(), size=(50,-1),
+ value=0, min=0, max=1000)
+ fgs2.Add( self.__alarm_value, 0, wx.ALIGN_LEFT|wx.TOP|wx.BOTTOM, 2)
# category option
- fgs.Add(wx.StaticText(self, -1, 'Categories:'), 0, wx.ALIGN_LEFT|wx.ALIGN_CENTRE, 0)
- self.__cat_chkbox=wx.CheckBox(self, id=wx.NewId())
- fgs.Add(self.__cat_chkbox, 0, wx.ALIGN_CENTRE, 0)
+ self.__cat_chkbox=wx.CheckBox(self, id=wx.NewId(), label='Categories:',
+ style=wx.ALIGN_RIGHT)
+ fgs2.Add(self.__cat_chkbox, 0, wx.ALIGN_RIGHT|wx.TOP|wx.BOTTOM, 5)
for i,c in enumerate(categories):
if not len(c):
categories[i]='<None>'
- self.__cats=wx.CheckListBox(self, choices=categories, size=(180, 50))
+ self.__cats=wx.CheckListBox(self, choices=categories, size=(160, 50))
self.__cats.Disable()
- fgs.Add(self.__cats, 0, wx.ALIGN_LEFT, 5)
- bs.Add(fgs, 1, wx.EXPAND|wx.ALL, 5)
+ fgs2.Add(self.__cats, 0, wx.ALIGN_LEFT, 0)
+ main_fgs.Add(fgs, 1, wx.EXPAND|wx.ALL, 0)
+ main_fgs.Add(fgs1, 1, wx.EXPAND|wx.ALL, 0)
+ main_fgs.Add(fgs2, 1, wx.EXPAND|wx.ALL, 0)
+ bs.Add(main_fgs, 1, wx.EXPAND|wx.ALL, 5)
# the buttons
bs.Add(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 5)
bs.Add(self.CreateButtonSizer(wx.OK|wx.CANCEL), 0, wx.ALIGN_CENTRE|wx.ALL, 5)
# event handles
- wx.EVT_CHECKBOX(self, self.__start_date_chkbox.GetId(), self.OnCheckBox)
- wx.EVT_CHECKBOX(self, self.__end_date_chkbox.GetId(), self.OnCheckBox)
+ wx.EVT_CHECKBOX(self, self._start_date_chkbox.GetId(), self.OnCheckBox)
+ wx.EVT_CHECKBOX(self, self._end_date_chkbox.GetId(), self.OnCheckBox)
wx.EVT_CHECKBOX(self, self.__cat_chkbox.GetId(), self.OnCheckBox)
+ wx.EVT_RADIOBOX(self, self.__alarm_setting.GetId(), self.OnAlarmSetting)
# all done
self.SetSizer(bs)
self.SetAutoLayout(True)
bs.Fit(self)
- def __set_date(self, chk_box, cal, d):
- if d is None:
- chk_box.SetValue(False)
- cal.Disable()
- else:
- chk_box.SetValue(True)
- cal.Enable()
- dt=wx.DateTime()
- dt.Set(d[2], year=d[0], month=d[1]-1)
- cal.SetDate(dt)
+ def ShowModal(self):
+ # request ringtones from
+ pubsub.subscribe(self.OnRingtoneUpdates, pubsub.ALL_RINGTONES)
+ wx.CallAfter(pubsub.publish, pubsub.REQUEST_RINGTONES) # make the call once we are onscreen
+ return wx.Dialog.ShowModal(self)
+
+ def OnRingtoneUpdates(self, msg):
+ "Receives pubsub message with ringtone list"
+ tones=msg.data[:]
+ #cur=self.Get()
+ try:
+ self.__ringtone.Clear()
+ self.__ringtone.Append(self.unnamed)
+ for p in tones:
+ self.__ringtone.Append(p)
+ rt=self.__ringtone.SetStringSelection(self.ringtone)
+ except:
+ self.ringtone=self.unnamed
def __set_cats(self, chk_box, c, data):
if data is None:
@@ -265,37 +291,76 @@
c.Check(i, c.GetString(i) in data)
def __set_rpt(self, data):
- if self.__start_date_chkbox.GetValue() and\
- self.__end_date_chkbox.GetValue():
+ if self._start_date_chkbox.GetValue() and\
+ self._end_date_chkbox.GetValue():
self.__rpt_chkbox.Enable()
+ self.__rpt_chkbox_text.Enable()
self.__rpt_chkbox.SetValue(data)
else:
self.__rpt_chkbox.SetValue(False)
self.__rpt_chkbox.Disable()
+ self.__rpt_chkbox_text.Disable()
- def set(self, data):
- self.__set_date(self.__start_date_chkbox, self.__start_date,
- data.get('start', None))
- self.__set_date(self.__end_date_chkbox, self.__end_date,
- data.get('end', None))
+ def __set_alarm_fields(self, value):
+ if value==0:
+ self.__vibrate.Disable()
+ self.__alarm_value.Disable()
+ self.__ringtone.Disable()
+ self.__vibrate_text.Disable()
+ self.__alarm_value_text.Disable()
+ self.__ringtone_text.Disable()
+ elif value==1:
+ self.__vibrate.Enable()
+ self.__alarm_value.Disable()
+ self.__ringtone.Enable()
+ self.__vibrate_text.Enable()
+ self.__alarm_value_text.Disable()
+ self.__ringtone_text.Enable()
+ else:
+ self.__vibrate.Enable()
+ self.__alarm_value.Enable()
+ self.__ringtone.Enable()
+ self.__vibrate_text.Enable()
+ self.__alarm_value_text.Enable()
+ self.__ringtone_text.Enable()
+
+ def set_base(self, data):
self.__set_rpt(data.get('rpt_events', False))
- self.__no_alarm_chkbox.SetValue(data.get('no_alarm', False))
+ no_alarm=data.get('no_alarm', False)
+ alarm_override=data.get('alarm_override', False)
+ if no_alarm:
+ value=0
+ elif alarm_override:
+ value=2
+ else:
+ value=1
+ self.__set_alarm_fields(value)
+ self.__alarm_setting.SetSelection(value)
+ self.ringtone=data.get('ringtone', self.unnamed)
+ try:
+ self.__ringtone.SetStringSelection(ringtone)
+ except:
+ self.__ringtone.SetStringSelection(self.unnamed)
+ value=data.get('vibrate', False);
+ self.__vibrate.SetValue(value)
+ self.__alarm_value.SetValue(data.get('alarm_value', 0))
self.__set_cats(self.__cat_chkbox, self.__cats, data.get('categories', None))
- def get(self):
- r={}
- if self.__start_date_chkbox.GetValue():
- dt=self.__start_date.GetDate()
- r['start']=(dt.GetYear(), dt.GetMonth()+1, dt.GetDay())
- else:
- r['start']=None
- if self.__end_date_chkbox.GetValue():
- dt=self.__end_date.GetDate()
- r['end']=(dt.GetYear(), dt.GetMonth()+1, dt.GetDay())
- else:
- r['end']=None
+ def get_base(self, r):
r['rpt_events']=self.__rpt_chkbox.GetValue()
- r['no_alarm']=self.__no_alarm_chkbox.GetValue()
+ value=self.__alarm_setting.GetSelection()
+ if value==0:
+ r['no_alarm']=True
+ r['alarm_override']=False
+ elif value==1:
+ r['no_alarm']=False
+ r['alarm_override']=False
+ else:
+ r['no_alarm']=False
+ r['alarm_override']=True
+ r['ringtone']=self.__ringtone.GetStringSelection()
+ r['vibrate']=self.__vibrate.GetValue()
+ r['alarm_value']=self.__alarm_value.GetValue()
if self.__cat_chkbox.GetValue():
c=[]
for i in range(self.__cats.GetCount()):
@@ -308,14 +373,17 @@
r['categories']=c
else:
r['categories']=None
- return r
+ return
+ def OnAlarmSetting(self, _):
+ self.__set_alarm_fields(self.__alarm_setting.GetSelection())
+
def OnCheckBox(self, evt):
evt_id=evt.GetId()
- if evt_id==self.__start_date_chkbox.GetId():
- w1,w2=self.__start_date_chkbox, self.__start_date
- elif evt_id==self.__end_date_chkbox.GetId():
- w1,w2=self.__end_date_chkbox, self.__end_date
+ if evt_id==self._start_date_chkbox.GetId():
+ w1,w2=self._start_date_chkbox, self._start_date
+ elif evt_id==self._end_date_chkbox.GetId():
+ w1,w2=self._end_date_chkbox, self._end_date
else:
w1,w2=self.__cat_chkbox, self.__cats
if w1.GetValue():
@@ -324,10 +392,137 @@
w2.Disable()
# turn on the repeat event option of both start date and end date
# are specified.
- if self.__start_date_chkbox.GetValue() and \
- self.__end_date_chkbox.GetValue():
+ if self._start_date_chkbox.GetValue() and \
+ self._end_date_chkbox.GetValue():
self.__rpt_chkbox.Enable()
+ self.__rpt_chkbox_text.Enable()
else:
self.__rpt_chkbox.SetValue(False)
self.__rpt_chkbox.Disable()
+ self.__rpt_chkbox_text.Disable()
+
+#-------------------------------------------------------------------------------
+class FilterDialog(FilterDialogBase):
+ def __init__(self, parent, id, caption, categories, style=wx.DEFAULT_DIALOG_STYLE):
+ FilterDialogBase.__init__(self, parent, id, caption, categories, style)
+
+ def SetDateControls(self, fgs, fgs1):
+ self._start_date_chkbox=wx.CheckBox(self, id=wx.NewId(),
+ label='Start Date:',
+ style=wx.ALIGN_RIGHT)
+ fgs.Add(self._start_date_chkbox, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTRE_VERTICAL, 0)
+ self._start_date=wx.calendar.CalendarCtrl(self, -1, wx.DateTime_Now(),
+ style = wx.calendar.CAL_SUNDAY_FIRST
+ | wx.calendar.CAL_SEQUENTIAL_MONTH_SELECTION)
+ self._start_date.Disable()
+ fgs.Add(self._start_date, 1, wx.ALIGN_LEFT, 5)
+ self._end_date_chkbox=wx.CheckBox(self, id=wx.NewId(),
+ label='End Date:',
+ style=wx.ALIGN_RIGHT)
+ fgs.Add(self._end_date_chkbox, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTRE_VERTICAL, 0)
+ self._end_date=wx.calendar.CalendarCtrl(self, -1, wx.DateTime_Now(),
+ style = wx.calendar.CAL_SUNDAY_FIRST
+ | wx.calendar.CAL_SEQUENTIAL_MONTH_SELECTION)
+ self._end_date.Disable()
+ fgs.Add(self._end_date, 1, wx.ALIGN_LEFT, 5)
+ def __set_date(self, chk_box, cal, d):
+ if d is None:
+ chk_box.SetValue(False)
+ cal.Disable()
+ else:
+ chk_box.SetValue(True)
+ cal.Enable()
+ dt=wx.DateTime()
+ dt.Set(d[2], year=d[0], month=d[1]-1)
+ cal.SetDate(dt)
+
+ def set(self, data):
+ self.__set_date(self._start_date_chkbox, self._start_date,
+ data.get('start', None))
+ self.__set_date(self._end_date_chkbox, self._end_date,
+ data.get('end', None))
+ self.set_base(data)
+
+ def get(self):
+ r={}
+ if self._start_date_chkbox.GetValue():
+ dt=self._start_date.GetDate()
+ r['start']=(dt.GetYear(), dt.GetMonth()+1, dt.GetDay())
+ else:
+ r['start']=None
+ if self._end_date_chkbox.GetValue():
+ dt=self._end_date.GetDate()
+ r['end']=(dt.GetYear(), dt.GetMonth()+1, dt.GetDay())
+ else:
+ r['end']=None
+ self.get_base(r)
+ return r
+
+#-------------------------------------------------------------------------------
+class AutoSyncFilterDialog(FilterDialogBase):
+ def __init__(self, parent, id, caption, categories, style=wx.DEFAULT_DIALOG_STYLE):
+ FilterDialogBase.__init__(self, parent, id, caption, categories, style)
+
+ def SetDateControls(self, fgs, fgs1):
+ #start_offset
+ self._start_date_chkbox=wx.CheckBox(self, id=wx.NewId(),
+ label='Start Offset (days):',
+ style=wx.ALIGN_RIGHT)
+ fgs.Add(self._start_date_chkbox, 0, wx.ALIGN_RIGHT|wx.TOP|wx.BOTTOM, 5)
+ self._start_date=wx.lib.intctrl.IntCtrl(self, id=wx.NewId(), size=(50,-1),
+ value=0, min=0, max=1000)
+ self._start_date.Disable()
+ fgs.Add( self._start_date, 0, wx.ALIGN_LEFT|wx.TOP|wx.BOTTOM, 2)
+ #end_offset
+ self._end_date_chkbox=wx.CheckBox(self, id=wx.NewId(),
+ label='End Offset (days):',
+ style=wx.ALIGN_RIGHT)
+ fgs.Add(self._end_date_chkbox, 0, wx.ALIGN_RIGHT|wx.TOP|wx.BOTTOM, 5)
+ self._end_date=wx.lib.intctrl.IntCtrl(self, id=wx.NewId(), size=(50,-1),
+ value=0, min=0, max=1000)
+ self._end_date.Disable()
+ fgs.Add( self._end_date, 0, wx.ALIGN_LEFT|wx.TOP|wx.BOTTOM, 2)
+ fgs1.Add(wx.StaticText(self, -1, 'Note: The start offset is the number of days' +
+ ' in the past, and the end offset is the number of days' +
+ ' in the future imported from the calender into your phone. If' +
+ ' disabled, all past and/or future events are imported.',
+ size=(270,55)),
+ 0, wx.ALIGN_LEFT|wx.TOP|wx.BOTTOM, 5)
+
+
+ def __set_start_date(self, d):
+ if d is None:
+ self._start_date_chkbox.SetValue(False)
+ self._start_date.Disable()
+ else:
+ self._start_date_chkbox.SetValue(True)
+ self._start_date.Enable()
+ self._start_date.SetValue(d)
+
+ def __set_end_date(self, d):
+ if d is None:
+ self._end_date_chkbox.SetValue(False)
+ self._end_date.Disable()
+ else:
+ self._end_date_chkbox.SetValue(True)
+ self._end_date.Enable()
+ self._end_date.SetValue(d)
+
+ def set(self, data):
+ self.__set_start_date(data.get('start_offset', None))
+ self.__set_end_date(data.get('end_offset', None))
+ self.set_base(data)
+
+ def get(self):
+ r={}
+ if self._start_date_chkbox.GetValue():
+ r['start_offset']=self._start_date.GetValue()
+ else:
+ r['start_offset']=None
+ if self._end_date_chkbox.GetValue():
+ r['end_offset']=self._end_date.GetValue()
+ else:
+ r['end_offset']=None
+ self.get_base(r)
+ return r
csv_calendar.py.diff
(application/octet-stream, 5.4 KB)
Index: csv_calendar.py
===================================================================
RCS file: /cvsroot/bitpim/bitpim/csv_calendar.py,v
retrieving revision 1.2
diff -u -r1.2 csv_calendar.py
--- csv_calendar.py 22 Jun 2005 00:07:43 -0000 1.2
+++ csv_calendar.py 19 Sep 2005 15:28:31 -0000
@@ -157,7 +157,11 @@
'end': None,
'categories': None,
'rpt_events': False,
- 'no_alarm': False
+ 'no_alarm': False,
+ 'ringtone': None,
+ 'alarm_override':False,
+ 'vibrate':False,
+ 'alarm_value':0
}
def __init__(self, file_name=None):
self.__calendar_keys=(
@@ -259,7 +263,10 @@
if self.__file_name is None:
# no file name specified
return
- csv_file=file(self.__file_name, 'rb')
+ try:
+ csv_file=file(self.__file_name, 'rb')
+ except:
+ return
reader=csv.reader(csv_file)
# retrieve the header and build the header keys
h=reader.next()
@@ -321,8 +328,17 @@
v=e.get('priority', None)
if v is not None:
ce.priority=v
- if not self.__filter.get('no_alarm', False) and e.get('alarm', False):
+ if not self._filter.get('no_alarm', False) and \
+ not self._filter.get('alarm_override', False) and \
+ e.get('alarm', False):
ce.alarm=e.get('alarm_value', 0)
+ ce.ringtone=self._filter.get('ringtone', "")
+ ce.vibrate=self._filter.get('vibrate', False)
+ elif not self._filter.get('no_alarm', False) and \
+ self._filter.get('alarm_override', False):
+ ce.alarm=self._filter.get('alarm_value', 0)
+ ce.ringtone=self._filter.get('ringtone', "")
+ ce.vibrate=self._filter.get('vibrate', False)
ce.allday=e.get('allday', False)
ce_start=e.get('start', None)
ce_end=e.get('end', None)
@@ -479,7 +495,7 @@
wx.EndBusyCursor()
def OnBrowseFolder(self, evt):
- dlg=wx.FileDialog(self, "Pick a CSV Calendar 4File", wildcard='*.csv')
+ dlg=wx.FileDialog(self, "Pick a CSV Calendar File", wildcard='*.csv')
id=dlg.ShowModal()
if id==wx.ID_CANCEL:
dlg.Destroy()
@@ -505,3 +521,72 @@
return self.__oc.get_category_list()
#-------------------------------------------------------------------------------
+def ImportCal(folder, filters):
+ _oc=CSVCalendarImportData(folder)
+ _oc.set_filter(filters)
+ _oc.read()
+ res={ 'calendar':_oc.get() }
+ return res
+
+#-------------------------------------------------------------------------------
+class CSVAutoConfCalDialog(wx.Dialog):
+ def __init__(self, parent, id, title, folder, filters,
+ style=wx.CAPTION|wx.MAXIMIZE_BOX| \
+ wx.SYSTEM_MENU|wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER):
+ self._oc=CSVCalendarImportData()
+ self._oc.set_filter(filters)
+ self.__read=False
+ wx.Dialog.__init__(self, parent, id=id, title=title, style=style)
+ main_bs=wx.BoxSizer(wx.VERTICAL)
+ hbs=wx.BoxSizer(wx.HORIZONTAL)
+ # label
+ hbs.Add(wx.StaticText(self, -1, "CSV Calendar File:"), 0, wx.ALL|wx.ALIGN_CENTRE, 2)
+ # where the folder name goes
+ self.folderctrl=wx.TextCtrl(self, -1, "", style=wx.TE_READONLY)
+ self.folderctrl.SetValue(folder)
+ hbs.Add(self.folderctrl, 1, wx.EXPAND|wx.ALL, 2)
+ # browse button
+ id_browse=wx.NewId()
+ hbs.Add(wx.Button(self, id_browse, 'Browse ...'), 0, wx.EXPAND|wx.ALL, 2)
+ main_bs.Add(hbs, 0, wx.EXPAND|wx.ALL, 5)
+ main_bs.Add(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 5)
+ wx.EVT_BUTTON(self, id_browse, self.OnBrowseFolder)
+ hbs=wx.BoxSizer(wx.HORIZONTAL)
+ hbs.Add(wx.Button(self, wx.ID_OK, 'OK'), 0, wx.ALIGN_CENTRE|wx.ALL, 5)
+ hbs.Add(wx.Button(self, wx.ID_CANCEL, 'Cancel'), 0, wx.ALIGN_CENTRE|wx.ALL, 5)
+ id_filter=wx.NewId()
+ hbs.Add(wx.Button(self, id_filter, 'Filter'), 0, wx.ALIGN_CENTRE|wx.ALL, 5)
+ hbs.Add(wx.Button(self, wx.ID_HELP, 'Help'), 0, wx.ALIGN_CENTRE|wx.ALL, 5)
+ main_bs.Add(hbs, 0, wx.ALIGN_CENTRE|wx.ALL, 5)
+ wx.EVT_BUTTON(self, id_filter, self.OnFilter)
+ wx.EVT_BUTTON(self, wx.ID_HELP, lambda *_: wx.GetApp().displayhelpid(helpids.ID_DLG_CALENDAR_IMPORT))
+ self.SetSizer(main_bs)
+ self.SetAutoLayout(True)
+ main_bs.Fit(self)
+
+ def OnBrowseFolder(self, evt):
+ dlg=wx.FileDialog(self, "Pick a CSV Calendar File", wildcard='*.csv')
+ id=dlg.ShowModal()
+ if id==wx.ID_CANCEL:
+ dlg.Destroy()
+ return
+ self.folderctrl.SetValue(dlg.GetPath())
+ self.__read=False
+ dlg.Destroy()
+
+ def OnFilter(self, evt):
+ # read the calender to get the category list
+ if not self.__read:
+ self._oc.read(self.folderctrl.GetValue())
+ self.__read=True
+ cat_list=self._oc.get_category_list()
+ dlg=common_calendar.AutoSyncFilterDialog(self, -1, 'Filtering Parameters', cat_list)
+ dlg.set(self._oc.get_filter())
+ if dlg.ShowModal()==wx.ID_OK:
+ self._oc.set_filter(dlg.get())
+
+ def GetFolder(self):
+ return self.folderctrl.GetValue()
+
+ def GetFilter(self):
+ return self._oc.get_filter()
csv_calendar.py
(text/plain, 22.3 KB)
### BITPIM ### ### Copyright (C) 2004 Joe Pham <[email protected]> ### ### This program is free software; you can redistribute it and/or modify ### it under the terms of the BitPim license as detailed in the LICENSE file. ### ### $Id: csv_calendar.py,v 1.2 2005/06/22 00:07:43 djpham Exp $ "Deals with CSV calendar import/export stuff" # System modules import csv import datetime # wxPython modules import wx # Others # My modules import bpcalendar import common_calendar import helpids #------------------------------------------------------------------------------ class ExportCSVDialog(wx.Dialog): def __init__(self, parent, title): super(ExportCSVDialog, self).__init__(parent, -1, title) # make the ui vbs=wx.BoxSizer(wx.VERTICAL) hbs=wx.BoxSizer(wx.HORIZONTAL) hbs.Add(wx.StaticText(self, -1, "File"), 0, wx.ALL|wx.ALIGN_CENTRE, 5) self.filenamectrl=wx.TextCtrl(self, -1, "calendar.csv") hbs.Add(self.filenamectrl, 1, wx.ALL|wx.EXPAND, 5) self.browsectrl=wx.Button(self, wx.NewId(), "Browse...") hbs.Add(self.browsectrl, 0, wx.ALL|wx.EXPAND, 5) vbs.Add(hbs, 0, wx.EXPAND|wx.ALL, 5) # selection GUI vbs.Add(self.GetSelectionGui(self), 5, wx.EXPAND|wx.ALL, 5) # the buttons vbs.Add(wx.StaticLine(self, -1, style=wx.LI_HORIZONTAL), 0, wx.EXPAND|wx.ALL,5) vbs.Add(self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.HELP), 0, wx.ALIGN_CENTER|wx.ALL, 5) # event handlers wx.EVT_BUTTON(self, self.browsectrl.GetId(), self.OnBrowse) wx.EVT_BUTTON(self, wx.ID_OK, self.OnOk) # all done self.SetSizer(vbs) self.SetAutoLayout(True) vbs.Fit(self) def GetSelectionGui(self, parent): hbs=wx.BoxSizer(wx.HORIZONTAL) self.__selection=wx.RadioBox(parent, wx.NewId(), 'Events Selection', choices=['All', 'Date Range'], style=wx.RA_SPECIFY_ROWS) hbs.Add(self.__selection, 0, wx.EXPAND|wx.ALL, 5) sbs=wx.StaticBoxSizer(wx.StaticBox(parent, -1, 'Date Range'), wx.VERTICAL) gs=wx.FlexGridSizer(-1, 2, 5, 5) gs.AddGrowableCol(1) gs.Add(wx.StaticText(self, -1, 'Start:'), 0, wx.ALL, 0) self.__start_date=wx.DatePickerCtrl(self, style=wx.DP_DROPDOWN | wx.DP_SHOWCENTURY) gs.Add(self.__start_date, 0, wx.ALL, 0) gs.Add(wx.StaticText(self, -1, 'End:'), 0, wx.ALL, 0) self.__end_date=wx.DatePickerCtrl(self, style=wx.DP_DROPDOWN | wx.DP_SHOWCENTURY) gs.Add(self.__end_date, 0, wx.ALL, 0) sbs.Add(gs, 1, wx.EXPAND|wx.ALL, 5) hbs.Add(sbs, 0, wx.EXPAND|wx.ALL, 5) return hbs def OnBrowse(self, _): dlg=wx.FileDialog(self, defaultFile=self.filenamectrl.GetValue(), wildcard="CSV files (*.cvs)|*.csv", style=wx.SAVE|wx.CHANGE_DIR) if dlg.ShowModal()==wx.ID_OK: self.filenamectrl.SetValue(dlg.GetPath()) dlg.Destroy() def __get_str(self, entry, field): s=getattr(entry, field, '') if s is None: s='' if isinstance(s, unicode): return s.encode('ascii', 'ignore') else: return str(s) def OnOk(self, _): # do export filename=self.filenamectrl.GetValue() csv_event_template=( ('Start', 'start_str', None), ('End', 'end_str', None), ('Description', 'description', None), ('Location', 'location', None), ('Priority', 'priority', None), ('Alarm', 'alarm', None), ('All-Day', 'allday', None), ('Notes', 'notes', None), ('Categories', 'categories_str', None), ('Ringtone', 'ringtone', None), ('Wallpaper', 'wallpaper', None)) csv_repeat_template=( ('Repeat Type', 'repeat_type', None), ('Repeat Interval', 'interval', None), ('Day-of-Week', 'dow_str', None), ('Excluded Dates', 'suppressed_str', None)) try: f=file(filename, 'wt') except: f=None if f is None: dlg=wx.MessageDialog(self, 'Failed to open file ['+filename+']', 'Export Error') dlg.ShowModal() dlg.Destroy() self.EndModal(wx.ID_OK) s=['"'+x[0]+'"' for x in csv_event_template]+\ ['"'+x[0]+'"' for x in csv_repeat_template] f.write(','.join(s)+'\n') all_items=self.__selection.GetSelection()==0 dt=self.__start_date.GetValue() range_start=(dt.GetYear(), dt.GetMonth()+1, dt.GetDay()) dt=self.__end_date.GetValue() range_end=(dt.GetYear(), dt.GetMonth()+1, dt.GetDay()) #--- def __write_rec(f, cal_dict): for k,e in cal_dict.items(): if not all_items and \ (e.end < range_start or e.start>range_end): continue l=[] for field in csv_event_template: if field[2] is None: s=self.__get_str(e, field[1]) else: s=field[2](e, field[1]) l+=['"'+s.replace('"', '')+'"'] rpt=e.repeat if rpt is None: l+=['']*len(csv_repeat_template) else: for field in csv_repeat_template: if field[2] is None: s=self.__get_str(rpt, field[1]) else: s=field[2](rpt, field[1]) l+=['"'+s.replace('"', '')+'"'] f.write(','.join(l)+'\n') #--- cal_dict=self.GetParent().GetCalendarData() __write_rec(f, cal_dict) f.close() self.EndModal(wx.ID_OK) #------------------------------------------------------------------------------ class CSVCalendarImportData(object): __default_filter={ 'start': None, 'end': None, 'categories': None, 'rpt_events': False, 'no_alarm': False, 'ringtone': None, 'alarm_override':False, 'vibrate':False, 'alarm_value':0 } def __init__(self, file_name=None): self.__calendar_keys=( ('Start', 'start', self.__set_datetime), ('End', 'end', self.__set_datetime), ('Description', 'description', self.__set_str), ('Location', 'location', self.__set_str), ('Priority', 'priority', self.__set_priority), ('Alarm', 'alarm_value',self.__set_alarm), ('All-Day', 'allday', self.__set_bool), ('Notes', 'notes', self.__set_str), ('Categories', 'categories', self.__set_categories), ('Ringtone', 'ringtone', self.__set_str), ('Wallpaper', 'wallpaper', self.__set_str), ('Repeat Type', 'repeat_type', self.__set_repeat_type), ('Repeat Interval', 'repeat_interval', self.__set_int), ('Day-of-Week', 'repeat_dow', self.__set_dow), ('Excluded Dates', 'exceptions', self.__set_exceptions) ) self.__file_name=file_name self.__data=[] self.__filter=self.__default_filter self.read() def __accept(self, entry): # start & end time within specified filter if self.__filter['start'] is not None and \ entry['start'][:3]<self.__filter['start'][:3]: return False if self.__filter['end'] is not None and \ entry['end'][:3]>self.__filter['end'][:3] and \ entry['end'][:3]!=common_calendar.no_end_date[:3]: return False # check the catefory c=self.__filter['categories'] if c is None or not len(c): # no categories specified => all catefories allowed. return True if len([x for x in entry['categories'] if x in c]): return True return False def get(self): res={} single_rpt=self.__filter.get('rpt_events', False) for k in self.__data: try: if self.__accept(k): if k.get('repeat', False) and single_rpt: d=self.__generate_repeat_events(k) else: d=[k] for n in d: ce=bpcalendar.CalendarEntry() self.__populate_entry(n, ce) res[ce.id]=ce except: if module_debug: raise return res def get_category_list(self): l=[] for e in self.__data: l+=[x for x in e.get('categories', []) if x not in l] return l def set_filter(self, filter): self.__filter=filter def get_filter(self): return self.__filter def get_display_data(self): cnt=0 res={} single_rpt=self.__filter.get('rpt_events', False) for k in self.__data: if self.__accept(k): if k.get('repeat', False) and single_rpt: d=self.__generate_repeat_events(k) else: d=[k.copy()] for n in d: if self.__filter.get('no_alarm', False): n['alarm']=False res[cnt]=n cnt+=1 return res def get_file_name(self): if self.__file_name is not None: return self.__file_name return '' def read(self, file_name=None): if file_name is not None: self.__file_name=file_name if self.__file_name is None: # no file name specified return try: csv_file=file(self.__file_name, 'rb') except: return reader=csv.reader(csv_file) # retrieve the header and build the header keys h=reader.next() header_keys=[] for e in h: k=None for x in self.__calendar_keys: if e==x[0]: k=x break header_keys.append(k) # loop through the file, read each line, and parse it self.__data=[] for row in reader: d={} for i,e in enumerate(row): if header_keys[i] is None: continue elif header_keys[i][2] is None: self.__set_str(e, d, header_keys[i][1]) else: header_keys[i][2](e, d, header_keys[i][1]) self.__data.append(d) csv_file.close() def __populate_repeat_entry(self, e, ce): # populate repeat entry data if not e.get('repeat', False) or e.get('repeat_type', None) is None: # not a repeat event return rp=bpcalendar.RepeatEntry() rp_type=e['repeat_type'] rp_interval=e.get('repeat_interval', 1) rp_dow=e.get('repeat_dow', 0) if rp_type==rp.daily: # daily event rp.repeat_type=rp.daily rp.interval=rp_interval elif rp_type==rp.weekly or rp_type==rp.monthly: rp.repeat_type=rp_type rp.interval=rp_interval rp.dow=rp_dow elif rp_type==rp.yearly: rp.repeat_type=rp.yearly else: # not yet supported return # add the list of exceptions for k in e.get('exceptions', []): rp.add_suppressed(*k[:3]) # all done ce.repeat=rp def __populate_entry(self, e, ce): # populate an calendar entry with data ce.description=e.get('description', None) ce.location=e.get('location', None) v=e.get('priority', None) if v is not None: ce.priority=v if not self._filter.get('no_alarm', False) and \ not self._filter.get('alarm_override', False) and \ e.get('alarm', False): ce.alarm=e.get('alarm_value', 0) ce.ringtone=self._filter.get('ringtone', "") ce.vibrate=self._filter.get('vibrate', False) elif not self._filter.get('no_alarm', False) and \ self._filter.get('alarm_override', False): ce.alarm=self._filter.get('alarm_value', 0) ce.ringtone=self._filter.get('ringtone', "") ce.vibrate=self._filter.get('vibrate', False) ce.allday=e.get('allday', False) ce_start=e.get('start', None) ce_end=e.get('end', None) if ce_start is None and ce_end is None: raise ValueError, "No start or end datetime" if ce_start is not None: ce.start=ce_start if ce_end is not None: ce.end=ce_end if ce_start is None: ce.start=ce.end elif ce_end is None: ce.end=ce.start ce.notes=e.get('notes', None) v=[] for k in e.get('categories', []): v.append({ 'category': k }) ce.categories=v # look at repeat self.__populate_repeat_entry(e, ce) def __generate_repeat_events(self, e): # generate multiple single events from this repeat event ce=bpcalendar.CalendarEntry() self.__populate_entry(e, ce) l=[] new_e=e.copy() new_e['repeat']=False for k in ('repeat_type', 'repeat_interval', 'repeat_dow'): if new_e.has_key(k): del new_e[k] s_date=datetime.datetime(*self.__filter['start']) e_date=datetime.datetime(*self.__filter['end']) one_day=datetime.timedelta(1) this_date=s_date while this_date<=e_date: date_l=(this_date.year, this_date.month, this_date.day) if ce.is_active(*date_l): new_e['start']=date_l+new_e['start'][3:] new_e['end']=date_l+new_e['end'][3:] l.append(new_e.copy()) this_date+=one_day return l def __set_str(self, v, d, key): d[key]=str(v) def __set_datetime(self, v, d, key): # the date time should be in this format: YYYY-MM-DD hh:mm # quick check for the format if v[4]!='-' or v[7]!='-' or v[10]!= ' ' or v[13]!=':': return d[key]=(int(v[:4]), int(v[5:7]), int(v[8:10]), int(v[11:13]), int(v[14:16])) def __set_priority(self, v, d, key): if len(v): d[key]=int(v) else: d[key]=None def __set_alarm(self, v, d, key): if len(v): d[key]=int(v) d['alarm']=d[key]!=-1 else: d[key]=None d['alarm']=False def __set_int(self, v, d, key): if not len(v): d[key]=None else: d[key]=int(v) def __set_bool(self, v, d, key): d[key]=v.upper()=='TRUE' def __set_categories(self, v, d, key): if v is None or not len(v): d[key]=[] else: d[key]=v.split(';') def __set_repeat_type(self, v, d, key): if len(v): d[key]=str(v) d['repeat']=True else: d['repeat']=False def __set_dow(self, v, d, key): dow=0 for e in v.split(';'): dow|=bpcalendar.RepeatEntry.dow_names.get(e, 0) d[key]=dow def __set_exceptions(self, v, d, key): l=[] for e in v.split(';'): if len(e): if e[4]=='-' and e[7]=='-': l.append( (int(e[:4]), int(e[5:7]), int(e[8:10])) ) d[key]=l #------------------------------------------------------------------------------ class CSVImportDialog(common_calendar.PreviewDialog): __column_labels=[ ('description', 'Description', 400, None), ('start', 'Start', 150, common_calendar.bp_date_str), ('end', 'End', 150, common_calendar.bp_date_str), ('repeat_type', 'Repeat', 80, common_calendar.bp_repeat_str), ('alarm', 'Alarm', 80, common_calendar.bp_alarm_str), ('categories', 'Category', 150, common_calendar.category_str) ] ID_ADD=wx.NewId() def __init__(self, parent, id, title): self.__oc=CSVCalendarImportData() common_calendar.PreviewDialog.__init__(self, parent, id, title, self.__column_labels, self.__oc.get_display_data(), config_name='import/calendar/csvdialog') def getcontrols(self, main_bs): hbs=wx.BoxSizer(wx.HORIZONTAL) # label hbs.Add(wx.StaticText(self, -1, "CSV File:"), 0, wx.ALL|wx.ALIGN_CENTRE, 2) # where the folder name goes self.folderctrl=wx.TextCtrl(self, -1, "", style=wx.TE_READONLY) self.folderctrl.SetValue(self.__oc.get_file_name()) hbs.Add(self.folderctrl, 1, wx.EXPAND|wx.ALL, 2) # browse button id_browse=wx.NewId() hbs.Add(wx.Button(self, id_browse, 'Browse ...'), 0, wx.EXPAND|wx.ALL, 2) main_bs.Add(hbs, 0, wx.EXPAND|wx.ALL, 5) main_bs.Add(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 5) wx.EVT_BUTTON(self, id_browse, self.OnBrowseFolder) def getpostcontrols(self, main_bs): main_bs.Add(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 5) hbs=wx.BoxSizer(wx.HORIZONTAL) id_import=wx.NewId() hbs.Add(wx.Button(self, id_import, 'Import'), 0, wx.ALIGN_CENTRE|wx.ALL, 5) hbs.Add(wx.Button(self, wx.ID_OK, 'Replace All'), 0, wx.ALIGN_CENTRE|wx.ALL, 5) hbs.Add(wx.Button(self, self.ID_ADD, 'Add'), 0, wx.ALIGN_CENTRE|wx.ALL, 5) hbs.Add(wx.Button(self, wx.ID_CANCEL, 'Cancel'), 0, wx.ALIGN_CENTRE|wx.ALL, 5) id_filter=wx.NewId() hbs.Add(wx.Button(self, id_filter, 'Filter'), 0, wx.ALIGN_CENTRE|wx.ALL, 5) hbs.Add(wx.Button(self, wx.ID_HELP, 'Help'), 0, wx.ALIGN_CENTRE|wx.ALL, 5) main_bs.Add(hbs, 0, wx.ALIGN_CENTRE|wx.ALL, 5) wx.EVT_BUTTON(self, id_import, self.OnImport) wx.EVT_BUTTON(self, id_filter, self.OnFilter) wx.EVT_BUTTON(self, self.ID_ADD, self.OnAdd) wx.EVT_BUTTON(self, wx.ID_HELP, lambda *_: wx.GetApp().displayhelpid(helpids.ID_DLG_CALENDAR_IMPORT)) def OnImport(self, evt): wx.BeginBusyCursor() dlg=wx.ProgressDialog('CSV Calendar Import', 'Importing CSV Calendar Data, please wait ...', parent=self) self.__oc.read(self.folderctrl.GetValue()) self.populate(self.__oc.get_display_data()) dlg.Destroy() wx.EndBusyCursor() def OnBrowseFolder(self, evt): dlg=wx.FileDialog(self, "Pick a CSV Calendar File", wildcard='*.csv') id=dlg.ShowModal() if id==wx.ID_CANCEL: dlg.Destroy() return self.folderctrl.SetValue(dlg.GetPath()) dlg.Destroy() def OnFilter(self, evt): cat_list=self.__oc.get_category_list() dlg=common_calendar.FilterDialog(self, -1, 'Filtering Parameters', cat_list) dlg.set(self.__oc.get_filter()) if dlg.ShowModal()==wx.ID_OK: self.__oc.set_filter(dlg.get()) self.populate(self.__oc.get_display_data()) def OnAdd(self, evt): self.EndModal(self.ID_ADD) def get(self): return self.__oc.get() def get_categories(self): return self.__oc.get_category_list() #------------------------------------------------------------------------------- def ImportCal(folder, filters): _oc=CSVCalendarImportData(folder) _oc.set_filter(filters) _oc.read() res={ 'calendar':_oc.get() } return res #------------------------------------------------------------------------------- class CSVAutoConfCalDialog(wx.Dialog): def __init__(self, parent, id, title, folder, filters, style=wx.CAPTION|wx.MAXIMIZE_BOX| \ wx.SYSTEM_MENU|wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER): self._oc=CSVCalendarImportData() self._oc.set_filter(filters) self.__read=False wx.Dialog.__init__(self, parent, id=id, title=title, style=style) main_bs=wx.BoxSizer(wx.VERTICAL) hbs=wx.BoxSizer(wx.HORIZONTAL) # label hbs.Add(wx.StaticText(self, -1, "CSV Calendar File:"), 0, wx.ALL|wx.ALIGN_CENTRE, 2) # where the folder name goes self.folderctrl=wx.TextCtrl(self, -1, "", style=wx.TE_READONLY) self.folderctrl.SetValue(folder) hbs.Add(self.folderctrl, 1, wx.EXPAND|wx.ALL, 2) # browse button id_browse=wx.NewId() hbs.Add(wx.Button(self, id_browse, 'Browse ...'), 0, wx.EXPAND|wx.ALL, 2) main_bs.Add(hbs, 0, wx.EXPAND|wx.ALL, 5) main_bs.Add(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 5) wx.EVT_BUTTON(self, id_browse, self.OnBrowseFolder) hbs=wx.BoxSizer(wx.HORIZONTAL) hbs.Add(wx.Button(self, wx.ID_OK, 'OK'), 0, wx.ALIGN_CENTRE|wx.ALL, 5) hbs.Add(wx.Button(self, wx.ID_CANCEL, 'Cancel'), 0, wx.ALIGN_CENTRE|wx.ALL, 5) id_filter=wx.NewId() hbs.Add(wx.Button(self, id_filter, 'Filter'), 0, wx.ALIGN_CENTRE|wx.ALL, 5) hbs.Add(wx.Button(self, wx.ID_HELP, 'Help'), 0, wx.ALIGN_CENTRE|wx.ALL, 5) main_bs.Add(hbs, 0, wx.ALIGN_CENTRE|wx.ALL, 5) wx.EVT_BUTTON(self, id_filter, self.OnFilter) wx.EVT_BUTTON(self, wx.ID_HELP, lambda *_: wx.GetApp().displayhelpid(helpids.ID_DLG_CALENDAR_IMPORT)) self.SetSizer(main_bs) self.SetAutoLayout(True) main_bs.Fit(self) def OnBrowseFolder(self, evt): dlg=wx.FileDialog(self, "Pick a CSV Calendar File", wildcard='*.csv') id=dlg.ShowModal() if id==wx.ID_CANCEL: dlg.Destroy() return self.folderctrl.SetValue(dlg.GetPath()) self.__read=False dlg.Destroy() def OnFilter(self, evt): # read the calender to get the category list if not self.__read: self._oc.read(self.folderctrl.GetValue()) self.__read=True cat_list=self._oc.get_category_list() dlg=common_calendar.AutoSyncFilterDialog(self, -1, 'Filtering Parameters', cat_list) dlg.set(self._oc.get_filter()) if dlg.ShowModal()==wx.ID_OK: self._oc.set_filter(dlg.get()) def GetFolder(self): return self.folderctrl.GetValue() def GetFilter(self): return self._oc.get_filter()