0001 ### BITPIM 0002 ### 0003 ### Copyright (C) 2003-2004 Roger Binns <rogerb@rogerbinns.com> 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: phonebookentryeditor.py 4693 2008-08-15 22:30:51Z djpham $ 0009 0010 from __future__ import with_statement 0011 import wx 0012 0013 import fixedscrolledpanel 0014 import pubsub 0015 import bphtml 0016 import database 0017 import nameparser 0018 import wallpaper 0019 import guihelper 0020 import field_color 0021 0022 """The dialog for editing a phonebook entry""" 0023 0024 # global ringone & wallpaper list 0025 _ringtone_list=None 0026 _wallpaper_list=None 0027 0028 # NavToolBar-------------------------------------------------------------------- 0029 class NavToolBar(wx.ToolBar): 0030 _id_up=wx.NewId() 0031 _id_down=wx.NewId() 0032 _id_del=wx.NewId() 0033 def __init__(self, parent, horizontal=True): 0034 self._parent=parent 0035 self._grandpa=parent.GetParent() 0036 _style=wx.TB_FLAT 0037 if horizontal: 0038 _style|=wx.TB_HORIZONTAL 0039 else: 0040 _style|=wx.TB_VERTICAL 0041 super(NavToolBar, self).__init__(parent, -1, style=_style) 0042 self.SetToolBitmapSize(wx.Size(16, 16)) 0043 sz=self.GetToolBitmapSize() 0044 self.AddLabelTool(NavToolBar._id_up, "Up", wx.ArtProvider.GetBitmap(guihelper.ART_ARROW_UP, wx.ART_TOOLBAR, sz), shortHelp="Move field up") 0045 self.AddLabelTool(NavToolBar._id_down, "Down", wx.ArtProvider.GetBitmap(guihelper.ART_ARROW_DOWN, wx.ART_TOOLBAR, sz), shortHelp="Move field down") 0046 self.AddLabelTool(NavToolBar._id_del, "Delete", wx.ArtProvider.GetBitmap(guihelper.ART_DEL_FIELD, wx.ART_TOOLBAR, sz), shortHelp="Delete field") 0047 if hasattr(self._grandpa, 'MoveField'): 0048 wx.EVT_TOOL(self, NavToolBar._id_up, self.OnMoveUp) 0049 wx.EVT_TOOL(self, NavToolBar._id_down, self.OnMoveDown) 0050 if hasattr(self._grandpa, 'DeleteField'): 0051 wx.EVT_TOOL(self, NavToolBar._id_del, self.OnDelete) 0052 self.Realize() 0053 0054 def OnMoveUp(self, _): 0055 self._grandpa.MoveField(self._parent, -1) 0056 0057 def OnMoveDown(self, _): 0058 self._grandpa.MoveField(self._parent, +1) 0059 0060 def OnDelete(self, _): 0061 self._grandpa.DeleteField(self._parent) 0062 0063 # DirtyUIBase------------------------------------------------------------------- 0064 myEVT_DIRTY_UI=wx.NewEventType() 0065 EVT_DIRTY_UI=wx.PyEventBinder(myEVT_DIRTY_UI, 1) 0066 0067 class DirtyUIBase(wx.Panel): 0068 """ Base class to add the capability to generate a DirtyUI event""" 0069 def __init__(self, parent): 0070 wx.Panel.__init__(self, parent, -1) 0071 self.dirty=self.ignore_dirty=False 0072 0073 def OnDirtyUI(self, evt): 0074 if self.dirty or self.ignore_dirty: 0075 return 0076 self.dirty=True 0077 self.GetEventHandler().ProcessEvent(\ 0078 wx.PyCommandEvent(myEVT_DIRTY_UI, self.GetId())) 0079 def Clean(self): 0080 self.dirty=False 0081 self.ignore_dirty=False 0082 def Ignore(self, ignore=True): 0083 self.ignore_dirty=ignore 0084 def Enable(self, enable=True): 0085 super(DirtyUIBase, self).Enable(enable) 0086 self.Refresh() 0087 0088 # My ListBox class-------------------------------------------------------------- 0089 class ListBox(wx.ListBox): 0090 """BitPim ListBox class that caches the selection string necessary for this 0091 implementation. 0092 """ 0093 def __init__(self, *args, **kwargs): 0094 super(ListBox, self).__init__(*args, **kwargs) 0095 self._selstr='' 0096 wx.EVT_LISTBOX(self, self.GetId(), self._OnSelected) 0097 def _OnSelected(self, evt): 0098 self._selstr=evt.GetString() 0099 evt.Skip() 0100 def GetStringSelection(self): 0101 return self._selstr 0102 def SetStringSelection(self, selection): 0103 try: 0104 super(ListBox, self).SetStringSelection(selection) 0105 self._selstr=selection 0106 except: 0107 self._selstr='' 0108 def SetSelection(self, idx): 0109 try: 0110 super(ListBox, self).SetSelection(idx) 0111 self._selstr=self.GetString(idx) 0112 except: 0113 self._selstr='' 0114 0115 # RingtoneEditor---------------------------------------------------------------- 0116 class MediaPreviewWindow(bphtml.HTMLWindow): 0117 """A subclass of BitPim HTMLWindow that launches a media item when clicked""" 0118 def OnLinkClicked(self, evt): 0119 pubsub.publish(pubsub.REQUEST_MEDIA_OPEN, 0120 (evt.GetHref(), None)) 0121 0122 class RingtoneEditor(DirtyUIBase): 0123 "Edit a ringtone" 0124 0125 # this is almost an exact clone of the wallpaper editor 0126 0127 unnamed="Select:" 0128 unknownselprefix=": " 0129 0130 choices=["call", "message", "calendar"] 0131 0132 ID_LIST=wx.NewId() 0133 0134 _bordersize=3 0135 0136 def __init__(self, parent, _, has_type=True, navtoolbar=False): 0137 DirtyUIBase.__init__(self, parent) 0138 0139 _box=field_color.build_color_field(self, wx.StaticBox, 0140 (self, -1, "Ringtone"), 'ringtone') 0141 hs=wx.StaticBoxSizer(_box, wx.HORIZONTAL) 0142 self.static_box=_box 0143 vs=wx.BoxSizer(wx.VERTICAL) 0144 0145 self.preview=MediaPreviewWindow(self, -1) 0146 self.preview.SetBorders(self._bordersize) 0147 vs.Add(self.preview, 1, wx.EXPAND|wx.ALL, 5) 0148 self.type=wx.ComboBox(self, -1, "call", choices=self.choices, style=wx.CB_READONLY) 0149 self.type.SetSelection(0) 0150 vs.Add(self.type, 0, wx.ALIGN_CENTRE|wx.ALL, 5) 0151 # Hide the 'type' combo box if not requested 0152 if not has_type: 0153 vs.Hide(1) 0154 0155 hs.Add(vs, 1, wx.EXPAND|wx.ALL, 5) 0156 0157 self.ringtone=ListBox(self, self.ID_LIST, choices=[self.unnamed], size=(-1,200)) 0158 hs.Add(self.ringtone, 1, wx.EXPAND|wx.ALL, 5) 0159 if navtoolbar: 0160 hs.Add(NavToolBar(self, False), 0, wx.EXPAND|wx.BOTTOM, 5) 0161 self.SetSizer(hs) 0162 hs.Fit(self) 0163 0164 pubsub.subscribe(self.OnRingtoneUpdates, pubsub.ALL_RINGTONES) 0165 wx.CallAfter(pubsub.publish, pubsub.REQUEST_RINGTONES) # make the call once we are onscreen 0166 pubsub.subscribe(self.OnPreviewUpdate, pubsub.RESPONSE_MEDIA_INFO) 0167 0168 wx.EVT_LISTBOX(self, self.ID_LIST, self.OnLBClicked) 0169 wx.EVT_LISTBOX_DCLICK(self, self.ID_LIST, self.OnLBClicked) 0170 0171 def __del__(self): 0172 pubsub.unsubscribe(self.OnRingtoneUpdates) 0173 pubsub.unsubscribe(self.OnPreviewUpdate) 0174 super(RingtoneEditor, self).__del__() 0175 0176 def OnRingtoneUpdates(self, msg): 0177 # wxPython/wxWidget bug: ListBox.Clear emits a wx.EVT_LISTBOX event 0178 # it shouldn't 0179 self.Ignore() 0180 tones=msg.data[:] 0181 cur=self._get() 0182 self.ringtone.Clear() 0183 self.ringtone.Append(self.unnamed) 0184 for p in tones: 0185 self.ringtone.Append(p) 0186 self._set(cur) 0187 self.Clean() 0188 0189 def OnLBClicked(self, evt=None): 0190 if self.ringtone.GetSelection()==wx.NOT_FOUND: 0191 return 0192 self.OnDirtyUI(evt) 0193 self._updaterequested=False 0194 v=self._get().get('ringtone', None) 0195 self.SetPreview(v) 0196 0197 _preview_html='<img src="bpimage:ringer.png;width=24;height=24"><P>%s' 0198 def OnPreviewUpdate(self, msg): 0199 # Media tab replies with some description about the selected media item 0200 if msg.data['client'] is self: 0201 # this one's for moi! 0202 if msg.data['canopen']: 0203 _s='<A HREF="%s">%s</A><BR>'%(msg.data['desc'][0], msg.data['desc'][0]) +\ 0204 '<BR>'.join(msg.data['desc'][1:]) 0205 else: 0206 _s='<BR>'.join(msg.data['desc']) 0207 self.preview.SetPage(self._preview_html%_s) 0208 0209 def SetPreview(self, name): 0210 if name is None or name==self.unnamed: 0211 self.preview.SetPage('') 0212 else: 0213 self.preview.SetPage(self._preview_html%name) 0214 pubsub.publish(pubsub.REQUEST_MEDIA_INFO, (self, name, None)) 0215 0216 def _set(self, data): 0217 if data is None: 0218 wp=self.unnamed 0219 type='call' 0220 else: 0221 wp=data.get("ringtone", self.unnamed) 0222 type=data.get("use", "call") 0223 0224 self.SetPreview(wp) 0225 if type=='calendar': 0226 self.type.SetSelection(2) 0227 elif type=="message": 0228 self.type.SetSelection(1) 0229 else: 0230 self.type.SetSelection(0) 0231 0232 # zero len? 0233 if len(wp)==0: 0234 self.ringtone.SetSelection(0) 0235 return 0236 0237 # try using straight forward name 0238 try: 0239 self.ringtone.SetStringSelection(wp) 0240 return 0241 except: 0242 pass 0243 0244 # ok, with unknownselprefix 0245 try: 0246 self.ringtone.SetStringSelection(self.unknownselprefix+wp) 0247 return 0248 except: 0249 pass 0250 0251 # ok, just add it 0252 self.ringtone.InsertItems([self.unknownselprefix+wp], 1) 0253 self.ringtone.SetStringSelection(self.unknownselprefix+wp) 0254 0255 def Set(self, data): 0256 self.Ignore(True) 0257 self._set(data) 0258 self.Clean() 0259 0260 def _get(self): 0261 res={} 0262 rt=self.ringtone.GetStringSelection() 0263 if rt==self.unnamed: 0264 return res 0265 if rt.startswith(self.unknownselprefix): 0266 rt=rt[len(self.unknownselprefix):] 0267 if len(rt): 0268 res['ringtone']=rt 0269 res['use']=self.type.GetStringSelection() 0270 return res 0271 0272 def Get(self): 0273 self.Clean() 0274 return self._get() 0275 0276 # WallpaperEditor--------------------------------------------------------------- 0277 class WallpaperEditor(DirtyUIBase): 0278 0279 unnamed="Select:" 0280 unknownselprefix=": " 0281 0282 choices=["call", "message", "calendar"] 0283 0284 ID_LIST=wx.NewId() 0285 0286 _bordersize=3 # border inside HTML widget 0287 0288 def __init__(self, parent, _, has_type=True, navtoolbar=False): 0289 DirtyUIBase.__init__(self, parent) 0290 0291 _box=field_color.build_color_field(self, wx.StaticBox, 0292 (self, -1, "Wallpaper"), 0293 'wallpaper') 0294 hs=wx.StaticBoxSizer(_box, wx.HORIZONTAL) 0295 self.static_box=_box 0296 0297 vs=wx.BoxSizer(wx.VERTICAL) 0298 0299 self.preview=wallpaper.WallpaperPreview(self) 0300 self.type=wx.ComboBox(self, -1, "call", choices=self.choices, style=wx.CB_READONLY) 0301 self.type.SetSelection(0) 0302 vs.Add(self.preview, 1, wx.EXPAND|wx.ALL, 5) 0303 vs.Add(self.type, 0, wx.ALIGN_CENTRE|wx.ALL, 5) 0304 # Hide the 'type' combo box if not requested 0305 if not has_type: 0306 vs.Hide(1) 0307 0308 hs.Add(vs, 1, wx.EXPAND|wx.ALL, 5) 0309 0310 self.wallpaper=ListBox(self, self.ID_LIST, choices=[self.unnamed], size=(-1,200), style=wx.LB_SINGLE) 0311 hs.Add(self.wallpaper, 1, wx.EXPAND|wx.ALL, 5) 0312 if navtoolbar: 0313 hs.Add(NavToolBar(self, False), 0, wx.EXPAND|wx.BOTTOM, 5) 0314 self.SetSizer(hs) 0315 hs.Fit(self) 0316 0317 pubsub.subscribe(self.OnWallpaperUpdates, pubsub.ALL_WALLPAPERS) 0318 wx.CallAfter(pubsub.publish, pubsub.REQUEST_WALLPAPERS) # make the call once we are onscreen 0319 0320 wx.EVT_LISTBOX(self, self.ID_LIST, self.OnLBClicked) 0321 wx.EVT_LISTBOX_DCLICK(self, self.ID_LIST, self.OnLBClicked) 0322 0323 def __del__(self): 0324 pubsub.unsubscribe(self.OnWallpaperUpdates) 0325 super(WallpaperEditor, self).__del__() 0326 0327 def OnWallpaperUpdates(self, msg): 0328 "Receives pubsub message with wallpaper list" 0329 # wxPython/wxWidget bug: ListBox.Clear emits a wx.EVT_LISTBOX event 0330 # it shouldn't 0331 self.Ignore() 0332 papers=msg.data[:] 0333 cur=self._get() 0334 self.wallpaper.Clear() 0335 self.wallpaper.Append(self.unnamed) 0336 for p in papers: 0337 self.wallpaper.Append(p) 0338 self._set(cur) 0339 self.Clean() 0340 0341 def OnLBClicked(self, evt=None): 0342 if self.wallpaper.GetSelection()==wx.NOT_FOUND: 0343 return 0344 self.OnDirtyUI(evt) 0345 v=self.Get().get('wallpaper', None) 0346 self.SetPreview(v) 0347 0348 def SetPreview(self, name): 0349 if name is None or name is self.unnamed: 0350 self.preview.SetImage(None) 0351 else: 0352 self.preview.SetImage(name) 0353 0354 def _set(self, data): 0355 if data is None: 0356 wp=self.unnamed 0357 type='call' 0358 else: 0359 wp=data.get("wallpaper", self.unnamed) 0360 type=data.get("use", "call") 0361 0362 self.SetPreview(wp) 0363 if type=="message": 0364 self.type.SetSelection(1) 0365 elif type=='calendar': 0366 self.type.SetSelection(2) 0367 else: 0368 self.type.SetSelection(0) 0369 0370 if len(wp)==0: 0371 self.wallpaper.SetSelection(0) 0372 return 0373 0374 # try using straight forward name 0375 try: 0376 self.wallpaper.SetStringSelection(wp) 0377 return 0378 except: 0379 pass 0380 0381 # ok, with unknownselprefix 0382 try: 0383 self.wallpaper.SetStringSelection(self.unknownselprefix+wp) 0384 return 0385 except: 0386 pass 0387 0388 # ok, just add it 0389 self.wallpaper.InsertItems([self.unknownselprefix+wp], 1) 0390 self.wallpaper.SetStringSelection(self.unknownselprefix+wp) 0391 0392 def Set(self, data): 0393 self.Ignore() 0394 self._set(data) 0395 self.Clean() 0396 0397 def _get(self): 0398 res={} 0399 wp=self.wallpaper.GetStringSelection() 0400 if wp==self.unnamed: 0401 return res 0402 if wp.startswith(self.unknownselprefix): 0403 wp=wp[len(self.unknownselprefix):] 0404 if len(wp): 0405 res['wallpaper']=wp 0406 res['use']=self.type.GetStringSelection() 0407 return res 0408 0409 def Get(self): 0410 self.Clean() 0411 return self._get() 0412 0413 # CategoryManager--------------------------------------------------------------- 0414 class CategoryManager(wx.Dialog): 0415 0416 def __init__(self, parent, title="Manage Categories"): 0417 wx.Dialog.__init__(self, parent, -1, title, style=wx.CAPTION|wx.SYSTEM_MENU|wx.DEFAULT_DIALOG_STYLE| 0418 wx.RESIZE_BORDER) 0419 0420 vs=wx.BoxSizer(wx.VERTICAL) 0421 hs=wx.BoxSizer(wx.HORIZONTAL) 0422 self.delbut=wx.Button(self, wx.NewId(), "Delete") 0423 self.addbut=wx.Button(self, wx.NewId(), "Add") 0424 self.add=wx.TextCtrl(self, -1) 0425 hs.Add(self.delbut,0, wx.EXPAND|wx.ALL, 5) 0426 hs.Add(self.addbut,0, wx.EXPAND|wx.ALL, 5) 0427 hs.Add(self.add, 1, wx.EXPAND|wx.ALL, 5) 0428 vs.Add(hs, 0, wx.EXPAND|wx.ALL, 5) 0429 0430 self.thelistb=wx.ListBox(self, -1, size=(100, 250), style=wx.LB_SORT) 0431 self.addlistb=wx.ListBox(self, -1, style=wx.LB_SORT) 0432 self.dellistb=wx.ListBox(self, -1, style=wx.LB_SORT) 0433 0434 hs=wx.BoxSizer(wx.HORIZONTAL) 0435 0436 vs2=wx.BoxSizer(wx.VERTICAL) 0437 vs2.Add(wx.StaticText(self, -1, " List"), 0, wx.ALL, 2) 0438 vs2.Add(self.thelistb, 1, wx.ALL|wx.EXPAND, 5) 0439 hs.Add(vs2, 1, wx.ALL|wx.EXPAND, 5) 0440 0441 vs2=wx.BoxSizer(wx.VERTICAL) 0442 vs2.Add(wx.StaticText(self, -1, " Added"), 0, wx.ALL, 2) 0443 vs2.Add(self.addlistb, 1, wx.ALL|wx.EXPAND, 5) 0444 hs.Add(vs2, 1, wx.ALL|wx.EXPAND, 5) 0445 0446 vs2=wx.BoxSizer(wx.VERTICAL) 0447 vs2.Add(wx.StaticText(self, -1, " Deleted"), 0, wx.ALL, 2) 0448 vs2.Add(self.dellistb, 1, wx.ALL|wx.EXPAND, 5) 0449 hs.Add(vs2, 1, wx.ALL|wx.EXPAND, 5) 0450 0451 vs.Add(hs, 1, wx.EXPAND|wx.ALL, 5) 0452 vs.Add(wx.StaticLine(self, -1, style=wx.LI_HORIZONTAL), 0, wx.EXPAND|wx.ALL, 5) 0453 vs.Add(self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.HELP), 0, wx.ALIGN_CENTRE|wx.ALL, 5) 0454 0455 self.SetSizer(vs) 0456 vs.Fit(self) 0457 0458 self.curlist=None 0459 self.dellist=[] 0460 self.addlist=[] 0461 0462 pubsub.subscribe(self.OnUpdateCategories, pubsub.ALL_CATEGORIES) 0463 pubsub.publish(pubsub.REQUEST_CATEGORIES) 0464 0465 wx.EVT_BUTTON(self, self.addbut.GetId(), self.OnAdd) 0466 wx.EVT_BUTTON(self, self.delbut.GetId(), self.OnDelete) 0467 wx.EVT_BUTTON(self, wx.ID_OK, self.OnOk) 0468 wx.EVT_BUTTON(self, wx.ID_CANCEL, self.OnCancel) 0469 0470 def __del__(self): 0471 pubsub.unsubscribe(self.OnUpdateCategories) 0472 super(CategoryManager, self).__del__() 0473 0474 def OnUpdateCategories(self, msg): 0475 cats=msg.data[:] 0476 if self.curlist is None: 0477 self.curlist=cats 0478 0479 # add in any new entries that may have appeared 0480 for i in cats: 0481 if i not in self.curlist and i not in self.dellist: 0482 self.curlist.append(i) 0483 self.addlist.append(i) 0484 self.curlist.sort() 0485 self.addlist.sort() 0486 self.UpdateLBs() 0487 0488 def UpdateLBs(self): 0489 for lb,l in (self.thelistb, self.curlist), (self.addlistb, self.addlist), (self.dellistb, self.dellist): 0490 lb.Clear() 0491 for i in l: 0492 lb.Append(i) 0493 0494 def OnOk(self, _): 0495 pubsub.publish(pubsub.SET_CATEGORIES, self.curlist) 0496 self.Show(False) 0497 self.Destroy() 0498 0499 def OnCancel(self, _): 0500 self.Show(False) 0501 self.Destroy() 0502 0503 def OnAdd(self, _): 0504 v=self.add.GetValue() 0505 self.add.SetValue("") 0506 self.add.SetFocus() 0507 if len(v)==0: 0508 return 0509 if v not in self.curlist: 0510 self.curlist.append(v) 0511 self.curlist.sort() 0512 if v not in self.addlist: 0513 self.addlist.append(v) 0514 self.addlist.sort() 0515 if v in self.dellist: 0516 i=self.dellist.index(v) 0517 del self.dellist[i] 0518 self.UpdateLBs() 0519 0520 def OnDelete(self,_): 0521 try: 0522 v=self.thelistb.GetStringSelection() 0523 if v is None or len(v)==0: return 0524 except: 0525 return 0526 i=self.curlist.index(v) 0527 del self.curlist[i] 0528 if v in self.addlist: 0529 i=self.addlist.index(v) 0530 del self.addlist[i] 0531 self.dellist.append(v) 0532 self.dellist.sort() 0533 self.UpdateLBs() 0534 0535 # CategoryEditor---------------------------------------------------------------- 0536 class CategoryEditor(DirtyUIBase): 0537 0538 # we have to have an entry with a special string for the unnamed string 0539 0540 unnamed="Select:" 0541 0542 def __init__(self, parent, pos, navtoolbar=False): 0543 DirtyUIBase.__init__(self, parent) 0544 _box=field_color.build_color_field(self, wx.StaticBox, 0545 (self, -1, "Category"), 0546 'category') 0547 hs=wx.StaticBoxSizer(_box, wx.HORIZONTAL) 0548 0549 self.categories=[self.unnamed] 0550 self.category=wx.ListBox(self, -1, choices=self.categories) 0551 pubsub.subscribe(self.OnUpdateCategories, pubsub.ALL_CATEGORIES) 0552 pubsub.publish(pubsub.REQUEST_CATEGORIES) 0553 hs.Add(self.category, 1, wx.EXPAND|wx.ALL, 5) 0554 0555 if pos==0: 0556 self.but=wx.Button(self, wx.NewId(), "Manage Categories") 0557 hs.Add(self.but, 2, wx.ALIGN_CENTRE|wx.ALL, 5) 0558 wx.EVT_BUTTON(self, self.but.GetId(), self.OnManageCategories) 0559 else: 0560 hs.Add(wx.StaticText(self, -1, ""), 2, wx.ALIGN_CENTRE|wx.ALL, 5) 0561 0562 wx.EVT_LISTBOX(self, self.category.GetId(), self.OnDirtyUI) 0563 wx.EVT_LISTBOX_DCLICK(self, self.category.GetId(), self.OnDirtyUI) 0564 if navtoolbar: 0565 hs.Add(NavToolBar(self, False), 0, wx.EXPAND|wx.BOTTOM, 5) 0566 self.SetSizer(hs) 0567 hs.Fit(self) 0568 0569 def __del__(self): 0570 pubsub.unsubscribe(self.OnUpdateCategories) 0571 super(CategoryEditor, self).__del__() 0572 0573 def OnManageCategories(self, _): 0574 with guihelper.WXDialogWrapper(CategoryManager(self), True): 0575 pass 0576 0577 def OnUpdateCategories(self, msg): 0578 cats=msg.data[:] 0579 cats=[self.unnamed]+cats 0580 if self.categories!=cats: 0581 self.categories=cats 0582 sel=self.category.GetStringSelection() 0583 self.category.Clear() 0584 for i in cats: 0585 self.category.Append(i) 0586 try: 0587 self.category.SetStringSelection(sel) 0588 except: 0589 # the above fails if the category we are is deleted 0590 self.category.SetStringSelection(self.unnamed) 0591 0592 def Get(self): 0593 self.Clean() 0594 v=self.category.GetStringSelection() 0595 if len(v) and v!=self.unnamed: 0596 return {'category': v} 0597 return {} 0598 0599 def Set(self, data): 0600 self.Ignore() 0601 if data is None: 0602 v=self.unnamed 0603 else: 0604 v=data.get("category", self.unnamed) 0605 try: 0606 self.category.SetStringSelection(v) 0607 except: 0608 assert v!=self.unnamed 0609 self.category.SetStringSelection(self.unnamed) 0610 self.Clean() 0611 0612 # MemoEditor-------------------------------------------------------------------- 0613 class MemoEditor(DirtyUIBase): 0614 0615 def __init__(self, parent, _, navtoolbar=False): 0616 DirtyUIBase.__init__(self, parent) 0617 0618 _box=field_color.build_color_field(self, wx.StaticBox, 0619 (self, -1, "Memo"), 0620 'memo') 0621 vs=wx.StaticBoxSizer(_box, wx.HORIZONTAL) 0622 self.static_box=_box 0623 0624 self.memo=wx.TextCtrl(self, -1, "", style=wx.TE_MULTILINE) 0625 vs.Add(self.memo, 1, wx.EXPAND|wx.ALL, 5) 0626 wx.EVT_TEXT(self, self.memo.GetId(), self.OnDirtyUI) 0627 if navtoolbar: 0628 vs.Add(NavToolBar(self, horizontal=False), 0, wx.EXPAND|wx.BOTTOM, 5) 0629 self.SetSizer(vs) 0630 vs.Fit(self) 0631 0632 def Set(self, data): 0633 self.Ignore() 0634 if data is None: 0635 s='' 0636 else: 0637 s=data.get('memo', '') 0638 self.memo.SetValue(s) 0639 self.Clean() 0640 0641 def Get(self): 0642 self.Clean() 0643 if len(self.memo.GetValue()): 0644 return {'memo': self.memo.GetValue()} 0645 return {} 0646 0647 # copy/cut/paste routines 0648 def CanCopy(self): 0649 return self.memo.CanCopy() 0650 def Copy(self): 0651 return self.memo.Copy() 0652 def CanPaste(self): 0653 return self.memo.CanPaste() 0654 def Paste(self): 0655 return self.memo.Paste() 0656 def CanCut(self): 0657 return self.memo.CanCut() 0658 def Cut(self): 0659 return self.memo.Cut() 0660 0661 # NumberEditor------------------------------------------------------------------ 0662 class NumberEditor(DirtyUIBase): 0663 0664 choices=[ ("None", "none"), ("Home", "home"), ("Office", 0665 "office"), ("Cell", "cell"), ("Fax", "fax"), ("Pager", "pager"), 0666 ("Data", "data"), ("Main", "main")] 0667 0668 _None_Value='None' 0669 0670 def __init__(self, parent, _, navtoolbar=False): 0671 0672 DirtyUIBase.__init__(self, parent) 0673 0674 _field_color_dict=field_color.build_field_info(self, 'number') 0675 0676 hs=wx.StaticBoxSizer(field_color.build_color_field(self, 0677 wx.StaticBox, 0678 (self, -1, "Number details"), 0679 'details', 0680 _field_color_dict), 0681 wx.VERTICAL) 0682 _hs_top=wx.BoxSizer(wx.HORIZONTAL) 0683 _txt=field_color.build_color_field(self, wx.StaticText, 0684 (self, -1, "Type"), 0685 'type', _field_color_dict) 0686 _hs_top.Add(_txt, 0, wx.ALIGN_CENTRE|wx.ALL, 5) 0687 0688 self.type=wx.ComboBox(self, -1, "Cell", choices=[desc for desc,name in self.choices], style=wx.CB_READONLY) 0689 _hs_top.Add(self.type, 0, wx.EXPAND|wx.ALL, 5) 0690 0691 _txt=field_color.build_color_field(self, wx.StaticText, 0692 (self, -1, "SpeedDial"), 0693 'speeddial', _field_color_dict) 0694 _hs_top.Add(_txt, 0, wx.ALIGN_CENTRE|wx.ALL, 5) 0695 self.speeddial=wx.TextCtrl(self, -1, "", size=(32,10)) 0696 _hs_top.Add(self.speeddial, 0, wx.EXPAND|wx.ALL, 5) 0697 0698 _txt=field_color.build_color_field(self, wx.StaticText, 0699 (self, -1, "Number"), 0700 'number', _field_color_dict) 0701 _hs_top.Add(_txt, 0, wx.ALIGN_CENTRE|wx.ALL, 5) 0702 self.number=wx.TextCtrl(self, -1, "") 0703 _hs_top.Add(self.number, 1, wx.EXPAND|wx.ALL, 5) 0704 0705 # add a toolbar w/ the Up/Down/Del buttons 0706 if navtoolbar: 0707 _hs_top.Add(NavToolBar(self), 0, wx.EXPAND|wx.BOTTOM, 5) 0708 hs.Add(_hs_top, 0, wx.EXPAND|wx.ALL, 0) 0709 # the bottom section 0710 _hs_bot=wx.BoxSizer(wx.HORIZONTAL) 0711 _txt=field_color.build_color_field(self, wx.StaticText, 0712 (self, -1, "Ringtone"), 0713 'ringtone', _field_color_dict) 0714 _hs_bot.Add(_txt, 0, wx.ALIGN_CENTRE|wx.ALL, 5) 0715 self.ringtone=wx.ComboBox(self, -1) 0716 _hs_bot.Add(self.ringtone, 0, wx.EXPAND|wx.ALL, 5) 0717 _txt=field_color.build_color_field(self, wx.StaticText, 0718 (self, -1, "Wallpaper"), 0719 'wallpaper', _field_color_dict) 0720 _hs_bot.Add(_txt, 0, wx.ALIGN_CENTRE|wx.ALL, 5) 0721 0722 self.wallpaper=wx.ComboBox(self, -1) 0723 _hs_bot.Add(self.wallpaper, 0, wx.EXPAND|wx.ALL, 5) 0724 0725 hs.Add(_hs_bot, 0, wx.EXPAND|wx.ALL, 0) 0726 0727 global _wallpaper_list, _ringtone_list 0728 pubsub.subscribe(self.OnWallpaperUpdates, pubsub.ALL_WALLPAPERS) 0729 if _wallpaper_list is None: 0730 pubsub.publish(pubsub.REQUEST_WALLPAPERS) 0731 else: 0732 self._populate_wallpaper() 0733 pubsub.subscribe(self.OnRingtoneUpdates, pubsub.ALL_RINGTONES) 0734 if _ringtone_list is None: 0735 pubsub.publish(pubsub.REQUEST_RINGTONES) 0736 else: 0737 self._populate_ringtone() 0738 0739 wx.EVT_TEXT(self, self.type.GetId(), self.OnDirtyUI) 0740 wx.EVT_TEXT(self, self.speeddial.GetId(), self.OnDirtyUI) 0741 wx.EVT_TEXT(self, self.number.GetId(), self.OnDirtyUI) 0742 wx.EVT_TEXT(self, self.ringtone.GetId(), self.OnDirtyUI) 0743 wx.EVT_TEXT(self, self.wallpaper.GetId(), self.OnDirtyUI) 0744 self.SetSizer(hs) 0745 hs.Fit(self) 0746 0747 def __del__(self): 0748 pubsub.unsubscribe(self.OnWallpaperUpdates) 0749 pubsub.unsubscribe(self.OnRingtoneUpdates) 0750 super(NumberEditor, self).__del__() 0751 0752 def _populate_cb(self, cb_widget, data): 0753 cb_widget.Clear() 0754 cb_widget.Append(self._None_Value) 0755 for _entry in data: 0756 cb_widget.Append(_entry) 0757 0758 def _set_cb_sel(self, cb_widget, str_sel): 0759 if str_sel: 0760 _sel=str_sel 0761 else: 0762 _sel=self._None_Value 0763 try: 0764 cb_widget.SetStringSelection(_sel) 0765 except: 0766 cb_widget.Append(sel) 0767 cb_widget.SetStringSelection(_sel) 0768 0769 def _get_cb_sel(self, cb_widget): 0770 _sel=cb_widget.GetStringSelection() 0771 if not _sel or _sel==self._None_Value: 0772 return None 0773 return _sel 0774 0775 def _populate_ringtone(self): 0776 """Populate the combo box with ringtone data""" 0777 self.Ignore() 0778 _str_sel=self.ringtone.GetStringSelection() 0779 global _ringtone_list 0780 self._populate_cb(self.ringtone, _ringtone_list) 0781 self._set_cb_sel(self.ringtone, _str_sel) 0782 self.Clean() 0783 0784 def _populate_wallpaper(self): 0785 """Ppulate the combo box with wallpaper data""" 0786 self.Ignore() 0787 _str_sel=self.wallpaper.GetStringSelection() 0788 global _wallpaper_list 0789 self._populate_cb(self.wallpaper, _wallpaper_list) 0790 self._set_cb_sel(self.wallpaper, _str_sel) 0791 self.Clean() 0792 0793 def OnWallpaperUpdates(self, msg): 0794 global _wallpaper_list 0795 _wallpaper_list=msg.data[:] 0796 self._populate_wallpaper() 0797 def OnRingtoneUpdates(self, msg): 0798 global _ringtone_list 0799 _ringtone_list=msg.data[:] 0800 self._populate_ringtone() 0801 0802 def Set(self, data): 0803 self.Ignore() 0804 sd=data.get("speeddial", "") 0805 if isinstance(sd,int): 0806 sd=`sd` 0807 self.speeddial.SetValue(sd) 0808 self.number.SetValue(data.get("number", "")) 0809 # ringtone & wallpaper 0810 self._set_cb_sel(self.ringtone, data.get('ringtone', None)) 0811 self._set_cb_sel(self.wallpaper, data.get('wallpaper', None)) 0812 # number of type 0813 v=data.get("type", "cell") 0814 for i in range(len(self.choices)): 0815 if self.choices[i][1]==v: 0816 self.type.SetSelection(i) 0817 self.Clean() 0818 return 0819 self.type.SetSelection(0) 0820 self.Clean() 0821 0822 def Get(self): 0823 self.Clean() 0824 res={} 0825 if len(self.number.GetValue())==0: 0826 return res 0827 res['number']=self.number.GetValue() 0828 if len(self.speeddial.GetValue()): 0829 res['speeddial']=self.speeddial.GetValue() 0830 try: 0831 res['speeddial']=int(res['speeddial']) 0832 except: 0833 pass 0834 res['type']=self.choices[self.type.GetSelection()][1] 0835 _sel=self._get_cb_sel(self.ringtone) 0836 if _sel: 0837 res['ringtone']=_sel 0838 _sel=self._get_cb_sel(self.wallpaper) 0839 if _sel: 0840 res['wallpaper']=_sel 0841 return res 0842 0843 # EmailEditor------------------------------------------------------------------- 0844 class EmailEditor(DirtyUIBase): 0845 0846 ID_TYPE=wx.NewId() 0847 _None_Value='None' 0848 0849 def __init__(self, parent, _, navtoolbar=False): 0850 super(EmailEditor, self).__init__(parent) 0851 0852 _field_color_dict=field_color.build_field_info(self, 'email_details') 0853 0854 _box=field_color.build_color_field(self, wx.StaticBox, 0855 (self, -1, 'Email Address'), 0856 'email') 0857 hs=wx.StaticBoxSizer(_box, wx.VERTICAL) 0858 # top section 0859 _hs_top=wx.BoxSizer(wx.HORIZONTAL) 0860 self.type=wx.ComboBox(self, self.ID_TYPE, "", choices=["", "Home", "Business"], style=wx.CB_READONLY) 0861 _hs_top.Add(self.type, 0, wx.EXPAND|wx.ALL, 5) 0862 self.email=wx.TextCtrl(self, -1, "") 0863 _hs_top.Add(self.email, 1, wx.EXPAND|wx.ALL, 5) 0864 if navtoolbar: 0865 _hs_top.Add(NavToolBar(self), 0, wx.EXPAND|wx.BOTTOM, 5) 0866 hs.Add(_hs_top, 0, wx.EXPAND|wx.ALL, 0) 0867 # bottom section 0868 _hs_bot=wx.BoxSizer(wx.HORIZONTAL) 0869 _txt=field_color.build_color_field(self, wx.StaticText, 0870 (self, -1, "SpeedDial"), 0871 'emailspeeddial', _field_color_dict) 0872 _hs_bot.Add(_txt, 0, wx.ALIGN_CENTRE|wx.ALL, 5) 0873 self.speeddial=wx.TextCtrl(self, -1, "", size=(32,10)) 0874 _hs_bot.Add(self.speeddial, 0, wx.EXPAND|wx.ALL, 5) 0875 _txt=field_color.build_color_field(self, wx.StaticText, 0876 (self, -1, "Ringtone"), 0877 'emailringtone', _field_color_dict) 0878 _hs_bot.Add(_txt, 0, wx.ALIGN_CENTRE|wx.ALL, 5) 0879 self.ringtone=wx.ComboBox(self, -1) 0880 _hs_bot.Add(self.ringtone, 0, wx.EXPAND|wx.ALL, 5) 0881 _txt=field_color.build_color_field(self, wx.StaticText, 0882 (self, -1, "Wallpaper"), 0883 'emailwallpaper', _field_color_dict) 0884 _hs_bot.Add(_txt, 0, wx.ALIGN_CENTRE|wx.ALL, 5) 0885 0886 self.wallpaper=wx.ComboBox(self, -1) 0887 _hs_bot.Add(self.wallpaper, 0, wx.EXPAND|wx.ALL, 5) 0888 0889 hs.Add(_hs_bot, 0, wx.EXPAND|wx.ALL, 0) 0890 0891 global _wallpaper_list, _ringtone_list 0892 pubsub.subscribe(self.OnWallpaperUpdates, pubsub.ALL_WALLPAPERS) 0893 if _wallpaper_list is None: 0894 pubsub.publish(pubsub.REQUEST_WALLPAPERS) 0895 else: 0896 self._populate_wallpaper() 0897 pubsub.subscribe(self.OnRingtoneUpdates, pubsub.ALL_RINGTONES) 0898 if _ringtone_list is None: 0899 pubsub.publish(pubsub.REQUEST_RINGTONES) 0900 else: 0901 self._populate_ringtone() 0902 0903 wx.EVT_TEXT(self, self.type.GetId(), self.OnDirtyUI) 0904 wx.EVT_TEXT(self, self.email.GetId(), self.OnDirtyUI) 0905 wx.EVT_TEXT(self, self.speeddial.GetId(), self.OnDirtyUI) 0906 wx.EVT_TEXT(self, self.ringtone.GetId(), self.OnDirtyUI) 0907 wx.EVT_TEXT(self, self.wallpaper.GetId(), self.OnDirtyUI) 0908 self.SetSizer(hs) 0909 hs.Fit(self) 0910 0911 def __del__(self): 0912 pubsub.unsubscribe(self.OnWallpaperUpdates) 0913 pubsub.unsubscribe(self.OnRingtoneUpdates) 0914 super(NumberEditor, self).__del__() 0915 0916 def _populate_cb(self, cb_widget, data): 0917 cb_widget.Clear() 0918 cb_widget.Append(self._None_Value) 0919 for _entry in data: 0920 cb_widget.Append(_entry) 0921 0922 def _set_cb_sel(self, cb_widget, str_sel): 0923 if str_sel: 0924 _sel=str_sel 0925 else: 0926 _sel=self._None_Value 0927 try: 0928 cb_widget.SetStringSelection(_sel) 0929 except: 0930 cb_widget.Append(sel) 0931 cb_widget.SetStringSelection(_sel) 0932 0933 def _get_cb_sel(self, cb_widget): 0934 _sel=cb_widget.GetStringSelection() 0935 if not _sel or _sel==self._None_Value: 0936 return None 0937 return _sel 0938 0939 def _populate_ringtone(self): 0940 """Populate the combo box with ringtone data""" 0941 self.Ignore() 0942 _str_sel=self.ringtone.GetStringSelection() 0943 global _ringtone_list 0944 self._populate_cb(self.ringtone, _ringtone_list) 0945 self._set_cb_sel(self.ringtone, _str_sel) 0946 self.Clean() 0947 0948 def _populate_wallpaper(self): 0949 """Ppulate the combo box with wallpaper data""" 0950 self.Ignore() 0951 _str_sel=self.wallpaper.GetStringSelection() 0952 global _wallpaper_list 0953 self._populate_cb(self.wallpaper, _wallpaper_list) 0954 self._set_cb_sel(self.wallpaper, _str_sel) 0955 self.Clean() 0956 0957 def OnWallpaperUpdates(self, msg): 0958 global _wallpaper_list 0959 _wallpaper_list=msg.data[:] 0960 self._populate_wallpaper() 0961 def OnRingtoneUpdates(self, msg): 0962 global _ringtone_list 0963 _ringtone_list=msg.data[:] 0964 self._populate_ringtone() 0965 0966 def Set(self, data): 0967 self.Ignore() 0968 self.email.SetValue(data.get("email", "")) 0969 sd=data.get("speeddial", "") 0970 if isinstance(sd, int): 0971 sd=`sd` 0972 self.speeddial.SetValue(sd) 0973 self._set_cb_sel(self.ringtone, data.get('ringtone', None)) 0974 self._set_cb_sel(self.wallpaper, data.get('wallpaper', None)) 0975 v=data.get("type", "") 0976 if v=="home": 0977 self.type.SetSelection(1) 0978 elif v=="business": 0979 self.type.SetSelection(2) 0980 else: 0981 self.type.SetSelection(0) 0982 self.Clean() 0983 0984 def Get(self): 0985 self.Clean() 0986 res={} 0987 if len(self.email.GetValue())==0: 0988 return res 0989 res['email']=self.email.GetValue() 0990 if len(self.speeddial.GetValue()): 0991 res['speeddial']=self.speeddial.GetValue() 0992 try: 0993 res['speeddial']=int(res['speeddial']) 0994 except: 0995 pass 0996 if self.type.GetSelection()==1: 0997 res['type']='home' 0998 elif self.type.GetSelection()==2: 0999 res['type']='business' 1000 _sel=self._get_cb_sel(self.ringtone) 1001 if _sel: 1002 res['ringtone']=_sel 1003 _sel=self._get_cb_sel(self.wallpaper) 1004 if _sel: 1005 res['wallpaper']=_sel 1006 return res 1007 1008 # URLEditor--------------------------------------------------------------------- 1009 class URLEditor(DirtyUIBase): 1010 1011 ID_TYPE=wx.NewId() 1012 def __init__(self, parent, _, navtoolbar=False): 1013 super(URLEditor, self).__init__(parent) 1014 1015 _box=field_color.build_color_field(self, wx.StaticBox, 1016 (self, -1, "URL"), 'url') 1017 hs=wx.StaticBoxSizer(_box, wx.HORIZONTAL) 1018 1019 self.type=wx.ComboBox(self, self.ID_TYPE, "", choices=["", "Home", "Business"], style=wx.CB_READONLY) 1020 hs.Add(self.type, 0, wx.EXPAND|wx.ALL, 5) 1021 self.url=wx.TextCtrl(self, -1, "") 1022 hs.Add(self.url, 1, wx.EXPAND|wx.ALL, 5) 1023 if navtoolbar: 1024 hs.Add(NavToolBar(self), 0, wx.EXPAND|wx.BOTTOM, 5) 1025 wx.EVT_TEXT(self, self.type.GetId(), self.OnDirtyUI) 1026 wx.EVT_TEXT(self, self.url.GetId(), self.OnDirtyUI) 1027 self.SetSizer(hs) 1028 hs.Fit(self) 1029 1030 def Set(self, data): 1031 self.Ignore() 1032 self.url.SetValue(data.get("url", "")) 1033 v=data.get("type", "") 1034 if v=="home": 1035 self.type.SetSelection(1) 1036 elif v=="business": 1037 self.type.SetSelection(2) 1038 else: 1039 self.type.SetSelection(0) 1040 self.Clean() 1041 1042 def Get(self): 1043 self.Clean() 1044 res={} 1045 if len(self.url.GetValue())==0: 1046 return res 1047 res['url']=self.url.GetValue() 1048 if self.type.GetSelection()==1: 1049 res['type']='home' 1050 elif self.type.GetSelection()==2: 1051 res['type']='business' 1052 return res 1053 1054 # AddressEditor----------------------------------------------------------------- 1055 class AddressEditor(DirtyUIBase): 1056 1057 ID_TYPE=wx.NewId() 1058 1059 fieldinfos=("street", "Street"), ("street2", "Street2"), ("city", "City"), \ 1060 ("state", "State"), ("postalcode", "Postal/Zipcode"), ("country", "Country/Region") 1061 1062 def __init__(self, parent, _, navtoolbar=False): 1063 super(AddressEditor, self).__init__(parent) 1064 1065 _fc_dict=field_color.build_field_info(self, 'address') 1066 _hs=wx.StaticBoxSizer(field_color.build_color_field(self, wx.StaticBox, 1067 (self, -1, "Address Details"), 1068 'details', _fc_dict), 1069 wx.HORIZONTAL) 1070 vs=wx.BoxSizer(wx.VERTICAL) 1071 hs=wx.BoxSizer(wx.HORIZONTAL) 1072 hs.Add(field_color.build_color_field(self, wx.StaticText, 1073 (self, -1, "Type"), 1074 'type', _fc_dict), 1075 0, wx.ALIGN_CENTRE|wx.ALL, 5) 1076 self.type=wx.ComboBox(self, self.ID_TYPE, "Home", choices=["Home", "Business"], style=wx.CB_READONLY) 1077 hs.Add(self.type, 0, wx.EXPAND|wx.ALL, 5) 1078 hs.Add(field_color.build_color_field(self, wx.StaticText, 1079 (self, -1, "Company"), 1080 'company', _fc_dict), 1081 0, wx.ALIGN_CENTRE|wx.ALL, 5) 1082 self.company=wx.TextCtrl(self, -1, "") 1083 hs.Add(self.company, 1, wx.EXPAND|wx.ALL, 5) 1084 1085 gs=wx.FlexGridSizer(6,2,2,5) 1086 1087 for name,desc in self.fieldinfos: 1088 gs.Add(field_color.build_color_field(self, wx.StaticText, 1089 (self, -1, desc), 1090 name, _fc_dict), 1091 0, wx.ALIGN_CENTRE) 1092 setattr(self, name, wx.TextCtrl(self, -1, "")) 1093 gs.Add(getattr(self,name), 1, wx.EXPAND) 1094 1095 gs.AddGrowableCol(1) 1096 1097 vs.Add(hs,0,wx.EXPAND|wx.ALL, 5) 1098 vs.Add(gs,0,wx.EXPAND|wx.ALL, 5) 1099 1100 _hs.Add(vs, 0, wx.EXPAND|wx.ALL, 5) 1101 if navtoolbar: 1102 _hs.Add(NavToolBar(self, horizontal=False), 0, wx.EXPAND|wx.BOTTOM, 5) 1103 # ::TODO:: disable company when type is home 1104 wx.EVT_TEXT(self, self.type.GetId(), self.OnDirtyUI) 1105 wx.EVT_TEXT(self, self.company.GetId(), self.OnDirtyUI) 1106 for name,_ in self.fieldinfos: 1107 wx.EVT_TEXT(self, getattr(self, name).GetId(), self.OnDirtyUI) 1108 self.SetSizer(_hs) 1109 vs.Fit(self) 1110 1111 def Set(self, data): 1112 self.Ignore() 1113 # most fields 1114 for name,ignore in self.fieldinfos: 1115 getattr(self, name).SetValue(data.get(name, "")) 1116 # special cases 1117 self.company.SetValue(data.get("company", "")) 1118 if data.get("type", "home")=="home": 1119 self.type.SetValue("Home") 1120 else: 1121 self.type.SetValue("Business") 1122 self.Clean() 1123 1124 def Get(self): 1125 self.Clean() 1126 res={} 1127 # most fields 1128 for name,ignore in self.fieldinfos: 1129 w=getattr(self, name) 1130 if len(w.GetValue()): 1131 res[name]=w.GetValue() 1132 # special cases 1133 if self.type.GetSelection()==1: 1134 if len(self.company.GetValue()): 1135 res['company']=self.company.GetValue() 1136 # only add in type field if any other type field is set 1137 if len(res): 1138 res['type']=['home', 'business'][self.type.GetSelection()] 1139 return res 1140 1141 # NameEditor-------------------------------------------------------------------- 1142 class NameEditor(DirtyUIBase): 1143 1144 def __init__(self, parent, _, navtoolbar=False): 1145 super(NameEditor, self).__init__(parent) 1146 1147 _fc_dict=field_color.build_field_info(self, 'name') 1148 _hs=wx.StaticBoxSizer(field_color.build_color_field(self, 1149 wx.StaticBox, 1150 (self, -1, 'Name Details'), 1151 'details', _fc_dict), 1152 wx.HORIZONTAL) 1153 vs=wx.BoxSizer(wx.VERTICAL) 1154 hstop=wx.BoxSizer(wx.HORIZONTAL) 1155 hsbot=wx.BoxSizer(wx.HORIZONTAL) 1156 hstop.Add(field_color.build_color_field(self, wx.StaticText, 1157 (self, -1, "First"), 1158 'first', _fc_dict), 1159 0, wx.ALIGN_CENTRE|wx.ALL,5) 1160 self.first=wx.TextCtrl(self, -1, "") 1161 hstop.Add(self.first, 1, wx.EXPAND|wx.ALL, 5) 1162 hstop.Add(field_color.build_color_field(self, wx.StaticText, 1163 (self, -1, "Middle"), 1164 'middle', _fc_dict), 1165 0, wx.ALIGN_CENTRE|wx.ALL,5) 1166 self.middle=wx.TextCtrl(self, -1, "") 1167 hstop.Add(self.middle, 1, wx.EXPAND|wx.ALL, 5) 1168 hstop.Add(field_color.build_color_field(self, wx.StaticText, 1169 (self, -1, "Last"), 1170 'last', _fc_dict), 1171 0, wx.ALIGN_CENTRE|wx.ALL,5) 1172 self.last=wx.TextCtrl(self, -1, "") 1173 hstop.Add(self.last, 1, wx.EXPAND|wx.ALL, 5) 1174 hsbot.Add(field_color.build_color_field(self, wx.StaticText, 1175 (self, -1, "Full"), 1176 'full', _fc_dict), 1177 0, wx.ALIGN_CENTRE|wx.ALL,5) 1178 self.full=wx.TextCtrl(self, -1, "") 1179 hsbot.Add(self.full, 4, wx.EXPAND|wx.ALL, 5) 1180 hsbot.Add(field_color.build_color_field(self, wx.StaticText, 1181 (self, -1, "Nickname"), 1182 'nickname', _fc_dict), 1183 0, wx.ALIGN_CENTRE|wx.ALL,5) 1184 self.nickname=wx.TextCtrl(self, -1, "") 1185 hsbot.Add(self.nickname, 1, wx.EXPAND|wx.ALL, 5) 1186 vs.Add(hstop, 0, wx.EXPAND|wx.ALL, 5) 1187 vs.Add(hsbot, 0, wx.EXPAND|wx.ALL, 5) 1188 _hs.Add(vs, 0, wx.EXPAND|wx.ALL, 5) 1189 # add a toolbar w/ the Up/Down/Del buttons 1190 if navtoolbar: 1191 _hs.Add(NavToolBar(self, horizontal=False), 0, wx.EXPAND, 0) 1192 for _name in ('first', 'middle', 'last', 'full', 'nickname'): 1193 wx.EVT_TEXT(self, getattr(self, _name).GetId(), self.OnDirtyUI) 1194 1195 # use the sizer and resize ourselves according to space needed by sizer 1196 self.SetSizer(_hs) 1197 vs.Fit(self) 1198 1199 def Set(self, data): 1200 self.Ignore() 1201 self.first.SetValue(data.get("first", "")) 1202 self.middle.SetValue(data.get("middle", "")) 1203 self.last.SetValue(data.get("last", "")) 1204 self.full.SetValue(data.get("full", "")) 1205 self.nickname.SetValue(data.get("nickname", "")) 1206 self.Clean() 1207 1208 def Get(self): 1209 self.Clean() 1210 res={} 1211 for name,widget in ( "first", self.first), ("middle", self.middle), ("last", self.last), \ 1212 ("full", self.full), ("nickname", self.nickname): 1213 if len(widget.GetValue()): 1214 res[name]=widget.GetValue() 1215 return res 1216 1217 # MiscEditor----------------------------------------------------------------- 1218 class MiscEditor(DirtyUIBase): 1219 def __init__(self, parent, _, navtoolbar=False): 1220 super(MiscEditor, self).__init__(parent) 1221 _fc_dict=field_color.build_field_info(self, 'phonebook') 1222 vs=wx.StaticBoxSizer(wx.StaticBox(self, -1, "Misc Details"), 1223 wx.VERTICAL) 1224 # storage field 1225 hs=wx.BoxSizer(wx.HORIZONTAL) 1226 hs.Add(field_color.build_color_field(self, wx.StaticText, 1227 (self, -1, "Storage Option:"), 1228 'storage', _fc_dict), 1229 0, wx.ALIGN_CENTRE|wx.ALL, 5) 1230 self._storage=wx.ComboBox(self, -1, 'Phone', choices=["Phone", "SIM"], 1231 style=wx.CB_READONLY) 1232 wx.EVT_COMBOBOX(self, self._storage.GetId(), self.OnDirtyUI) 1233 hs.Add(self._storage, 0, wx.EXPAND|wx.LEFT, 5) 1234 vs.Add(hs, 0, wx.EXPAND|wx.ALL, 5) 1235 # secret field 1236 self._secret=field_color.build_color_field(self, wx.CheckBox, 1237 (self, -1, 1238 'This entry is private/secret'), 1239 'secret', _fc_dict) 1240 1241 wx.EVT_CHECKBOX(self, self._secret.GetId(), self.OnDirtyUI) 1242 vs.Add(self._secret, 0, wx.EXPAND|wx.ALL, 5) 1243 # all done 1244 self.SetSizer(vs) 1245 vs.Fit(self) 1246 1247 def Set(self, data): 1248 self._storage.SetValue('SIM' if data.get('sim', False) else 'Phone') 1249 self._secret.SetValue(data.get('secret', False)) 1250 1251 def Get(self): 1252 _res={} 1253 if self._storage.GetValue()=='SIM': 1254 _res['sim']=True 1255 if self._secret.GetValue(): 1256 _res['secret']=True 1257 return _res 1258 1259 # ICEEditor----------------------------------------------------------------- 1260 class ICEEditor(DirtyUIBase): 1261 def __init__(self, parent, _, navtoolbar=False): 1262 super(ICEEditor, self).__init__(parent) 1263 _fc_dict=field_color.build_field_info(self, 'phonebook') 1264 vs=wx.StaticBoxSizer(field_color.build_color_field(self, 1265 wx.StaticBox, 1266 (self, -1, "ICE Details"), 1267 'ICE', _fc_dict), 1268 wx.VERTICAL) 1269 # ICE field 1270 hs=wx.BoxSizer(wx.HORIZONTAL) 1271 hs.Add(field_color.build_color_field(self, wx.StaticText, 1272 (self, -1, "Assign this contact as:"), 1273 'ICE', _fc_dict), 1274 0, wx.ALIGN_CENTRE|wx.ALL, 5) 1275 self._ice=wx.ComboBox(self, -1, 'None', 1276 choices=['None', 'ICE 1', 'ICE 2', 'ICE 3'], 1277 style=wx.CB_READONLY) 1278 wx.EVT_COMBOBOX(self, self._ice.GetId(), self.OnDirtyUI) 1279 hs.Add(self._ice, 0, wx.EXPAND|wx.LEFT, 5) 1280 vs.Add(hs, 0, wx.EXPAND|wx.ALL, 5) 1281 # all done 1282 self.SetSizer(vs) 1283 vs.Fit(self) 1284 1285 def Set(self, data): 1286 if data.has_key('iceindex'): 1287 _val=data['iceindex']+1 1288 else: 1289 _val=0 1290 self._ice.SetSelection(_val) 1291 1292 def Get(self): 1293 _res={} 1294 _val=self._ice.GetSelection() 1295 if _val: 1296 _res['iceindex']=_val-1 1297 return _res 1298 1299 # EditorManager----------------------------------------------------------------- 1300 class EditorManager(fixedscrolledpanel.wxScrolledPanel): 1301 1302 ID_DOWN=wx.NewId() 1303 ID_UP=wx.NewId() 1304 ID_ADD=wx.NewId() 1305 ID_DELETE=wx.NewId() 1306 instruction_text=""" 1307 \n\nPress Add above to add a field. Press Delete to remove the field your 1308 cursor is on. 1309 1310 You can use Up and Down to change the priority of items. For example, some 1311 phones store the first five numbers in the numbers tab, and treat the first 1312 number as the default to call. Other phones can only store one email address 1313 so only the first one would be stored. 1314 """ 1315 1316 def __init__(self, parent, childclass): 1317 """Constructor 1318 1319 @param parent: Parent window 1320 @param childclass: One of the *Editor classes which is used as a factory for making the 1321 widgets that correspond to each value""" 1322 fixedscrolledpanel.wxScrolledPanel.__init__(self, parent) 1323 self.dirty_ui_handler=getattr(parent, 'OnDirtyUI', None) 1324 self.sizer=wx.BoxSizer(wx.VERTICAL) 1325 self.SetSizer(self.sizer) 1326 self.widgets=[] 1327 self.childclass=childclass 1328 self.instructions=wx.StaticText(self, -1, EditorManager.instruction_text) 1329 self.sizer.Add(self.instructions, 0, wx.ALIGN_CENTER ) 1330 self.SetupScrolling() 1331 1332 def Get(self): 1333 """Returns a list of dicts corresponding to the values""" 1334 res=[] 1335 for i in self.widgets: 1336 g=i.Get() 1337 if len(g): 1338 res.append(g) 1339 return res 1340 1341 def Populate(self, data): 1342 """Fills in the editors according to the list of dicts in data 1343 1344 The editor widgets are created and destroyed as needed""" 1345 callsus=False 1346 while len(data)>len(self.widgets): 1347 callsus=True 1348 _w=self.childclass(self, len(self.widgets), navtoolbar=True) 1349 if self.dirty_ui_handler: 1350 EVT_DIRTY_UI(self, _w.GetId(), self.dirty_ui_handler) 1351 self.widgets.append(_w) 1352 self.sizer.Add(_w, 0, wx.EXPAND|wx.ALL, 10) 1353 while len(self.widgets)>len(data): 1354 callsus=True 1355 self.sizer.Remove(self.widgets[-1]) 1356 self.widgets[-1].Destroy() 1357 del self.widgets[-1] 1358 for num in range(len(data)): 1359 self.widgets[num].Clean() 1360 self.widgets[num].Set(data[num]) 1361 callsus=self.DoInstructionsLayout() or callsus 1362 if callsus: 1363 self.sizer.Layout() 1364 self.SetupScrolling() 1365 1366 def DoInstructionsLayout(self): 1367 "Returns True if Layout should be called" 1368 if len(self.widgets): 1369 if self.instructions.IsShown(): 1370 self.sizer.Remove(self.instructions) 1371 self.instructions.Show(False) 1372 return True 1373 else: 1374 if not self.instructions.IsShown(): 1375 self.sizer.Add(self.instructions, 0, wx.ALIGN_CENTER ) 1376 self.instructions.Show(True) 1377 return True 1378 return False 1379 1380 1381 def GetCurrentWidgetIndex(self): 1382 """Returns the index of the currently selected editor widget 1383 1384 @raise IndexError: if there is no selected one""" 1385 focuswin=wx.Window.FindFocus() 1386 win=focuswin 1387 while win is not None and win not in self.widgets: 1388 win=win.GetParent() 1389 if win is None: 1390 raise IndexError("no idea who is selected") 1391 if win not in self.widgets: 1392 raise IndexError("no idea what that thing is") 1393 pos=self.widgets.index(win) 1394 return pos 1395 1396 def Add(self): 1397 """Adds a new widget at the currently selected location""" 1398 gets=[x.Get() for x in self.widgets] 1399 try: 1400 pos=self.GetCurrentWidgetIndex() 1401 except IndexError: 1402 pos=len(gets)-1 1403 _w=self.childclass(self, len(self.widgets), navtoolbar=True) 1404 if self.dirty_ui_handler: 1405 EVT_DIRTY_UI(self, _w.GetId(), self.dirty_ui_handler) 1406 self.dirty_ui_handler(None) 1407 self.widgets.append(_w) 1408 self.sizer.Add(_w, 0, wx.EXPAND|wx.ALL, 10) 1409 self.DoInstructionsLayout() 1410 self.sizer.Layout() 1411 self.SetupScrolling() 1412 if len(self.widgets)>1: 1413 for num,value in zip( range(pos+2, len(self.widgets)), gets[pos+1:]): 1414 self.widgets[num].Set(value) 1415 self.widgets[pos+1].Set({}) 1416 self.widgets[pos+1].SetFocus() 1417 else: 1418 self.widgets[0].SetFocus() 1419 1420 def MoveField(self, field, delta): 1421 try: 1422 pos=self.widgets.index(field) 1423 except IndexError: 1424 wx.Bell() 1425 return 1426 if pos+delta<0: 1427 print "that would go off top" 1428 return 1429 if pos+delta>=len(self.widgets): 1430 print "that would go off bottom" 1431 return 1432 if self.dirty_ui_handler: 1433 self.dirty_ui_handler(None) 1434 gets=[x.Get() for x in self.widgets] 1435 # swap value 1436 path,settings=self.GetWidgetPathAndSettings(self.widgets[pos], field) 1437 self.widgets[pos+delta].Set(gets[pos]) 1438 self.widgets[pos].Set(gets[pos+delta]) 1439 self.SetWidgetPathAndSettings(self.widgets[pos+delta], path, settings) 1440 1441 def DeleteField(self, field): 1442 """Deletes the currently select widget""" 1443 # ignore if there is nothing to delete 1444 if len(self.widgets)==0: 1445 return 1446 # get the current value of all widgets 1447 gets=[x.Get() for x in self.widgets] 1448 try: 1449 pos=self.widgets.index(field) 1450 except IndexError: 1451 wx.Bell() 1452 return 1453 if self.dirty_ui_handler: 1454 self.dirty_ui_handler(None) 1455 # remove the last widget (the UI, not the value) 1456 self.sizer.Remove(self.widgets[-1]) 1457 self.widgets[-1].Destroy() 1458 del self.widgets[-1] 1459 # if we deleted last item and it had focus, move focus 1460 # to second to last item 1461 if len(self.widgets): 1462 if pos==len(self.widgets): 1463 self.widgets[pos-1].SetFocus() 1464 self.DoInstructionsLayout() 1465 self.sizer.Layout() 1466 self.SetupScrolling() 1467 1468 # update from one we deleted to end 1469 for i in range(pos, len(self.widgets)): 1470 self.widgets[i].Set(gets[i+1]) 1471 1472 if len(self.widgets): 1473 # change focus if we deleted the last widget 1474 if pos<len(self.widgets): 1475 self.widgets[pos].SetFocus() 1476 1477 def Delete(self): 1478 """Deletes the currently select widget""" 1479 # ignore if there is nothing to delete 1480 if len(self.widgets)==0: 1481 return 1482 # get the current value of all widgets 1483 gets=[x.Get() for x in self.widgets] 1484 try: 1485 pos=self.GetCurrentWidgetIndex() 1486 except IndexError: 1487 wx.Bell() 1488 return 1489 # remove the last widget (the UI, not the value) 1490 self.sizer.Remove(self.widgets[-1]) 1491 self.widgets[-1].Destroy() 1492 del self.widgets[-1] 1493 # if we deleted last item and it had focus, move focus 1494 # to second to last item 1495 if len(self.widgets): 1496 if pos==len(self.widgets): 1497 self.widgets[pos-1].SetFocus() 1498 self.DoInstructionsLayout() 1499 self.sizer.Layout() 1500 self.SetupScrolling() 1501 1502 # update from one we deleted to end 1503 for i in range(pos, len(self.widgets)): 1504 self.widgets[i].Set(gets[i+1]) 1505 1506 if len(self.widgets): 1507 # change focus if we deleted the last widget 1508 if pos<len(self.widgets): 1509 self.widgets[pos].SetFocus() 1510 1511 1512 def Move(self, delta): 1513 """Moves the currently selected widget 1514 1515 @param delta: positive to move down, negative to move up 1516 """ 1517 focuswin=wx.Window_FindFocus() 1518 try: 1519 pos=self.GetCurrentWidgetIndex() 1520 except IndexError: 1521 wx.Bell() 1522 return 1523 if pos+delta<0: 1524 print "that would go off top" 1525 return 1526 if pos+delta>=len(self.widgets): 1527 print "that would go off bottom" 1528 return 1529 gets=[x.Get() for x in self.widgets] 1530 # swap value 1531 path,settings=self.GetWidgetPathAndSettings(self.widgets[pos], focuswin) 1532 self.widgets[pos+delta].Set(gets[pos]) 1533 self.widgets[pos].Set(gets[pos+delta]) 1534 self.SetWidgetPathAndSettings(self.widgets[pos+delta], path, settings) 1535 1536 def GetWidgetPathAndSettings(self, widgetfrom, controlfrom): 1537 """Finds the specified control within the editor widgetfrom. 1538 The values are for calling L{SetWidgetPathAndSettings}. 1539 1540 Returns a tuple of (path, settings). path corresponds 1541 to the hierarchy with an editor (eg a panel contains a 1542 radiobox contains the radio button widget). settings 1543 means something to L{SetWidgetPathAndSettings}. For example, 1544 if the widget is a text widget it contains the current insertion 1545 point and selection.""" 1546 # we find where the control is in the hierarchy of widgetfrom 1547 path=[] 1548 1549 # this is the same algorithm getpwd uses on Unix 1550 win=controlfrom 1551 while win is not widgetfrom: 1552 p=win.GetParent() 1553 kiddies=p.GetChildren() 1554 found=False 1555 for kid in range(len(kiddies)): 1556 if kiddies[kid] is win: 1557 path=[kid]+path 1558 win=p 1559 found=True 1560 break 1561 if found: 1562 continue 1563 print "i don't appear to be my parent's child!!!" 1564 return 1565 1566 1567 # save some settings we know about 1568 settings=[] 1569 if isinstance(controlfrom, wx.TextCtrl): 1570 settings=[controlfrom.GetInsertionPoint(), controlfrom.GetSelection()] 1571 1572 return path,settings 1573 1574 def SetWidgetPathAndSettings(self,widgetto,path,settings): 1575 """See L{GetWidgetPathAndSettings}""" 1576 # now have the path. follow it in widgetto 1577 print path 1578 win=widgetto 1579 for p in path: 1580 kids=win.GetChildren() 1581 win=kids[p] 1582 controlto=win 1583 1584 controlto.SetFocus() 1585 1586 if isinstance(controlto, wx.TextCtrl): 1587 controlto.SetInsertionPoint(settings[0]) 1588 controlto.SetSelection(settings[1][0], settings[1][1]) 1589 1590 def SetFocusOnValue(self, index): 1591 """Sets focus to the editor widget corresponding to the supplied index""" 1592 wx.CallAfter(self.widgets[index].SetFocus) 1593 1594 # Editor------------------------------------------------------------------------ 1595 class Editor(wx.Dialog): 1596 "The Editor Dialog itself. It contains panes for the various field types." 1597 1598 ID_DOWN=wx.NewId() 1599 ID_UP=wx.NewId() 1600 ID_ADD=wx.NewId() 1601 ID_DELETE=wx.NewId() 1602 1603 color_field_name='phonebook' 1604 1605 # the tabs and classes within them 1606 tabsfactory=[ 1607 ("Names", "names", NameEditor), 1608 ("Numbers", "numbers", NumberEditor), 1609 ("Emails", "emails", EmailEditor), 1610 ("Addresses", "addresses", AddressEditor), 1611 ("URLs", "urls", URLEditor), 1612 ("Memos", "memos", MemoEditor), 1613 ("Categories", "categories", CategoryEditor), 1614 ("Wallpapers", "wallpapers", WallpaperEditor), 1615 ("Ringtones", "ringtones", RingtoneEditor), 1616 ("ICE", 'ice', ICEEditor), 1617 ("Misc", 'flags', MiscEditor), 1618 ] 1619 1620 def __init__(self, parent, data, title="Edit PhoneBook Entry", 1621 keytoopenon=None, dataindex=None, 1622 factory=database.dictdataobjectfactory, readonly=False, 1623 datakey=None, movement=False): 1624 """Constructor for phonebookentryeditor dialog 1625 1626 @param parent: parent window 1627 @param data: dict of values to edit 1628 @param title: window title 1629 @param keytoopenon: The key to open on. This is the key as stored in the data such as "names", "numbers" 1630 @param dataindex: Which value within the tab specified by keytoopenon to set focus to 1631 @param readonly: Indicates read-only data. 1632 """ 1633 global _ringtone_list, _wallpaper_list 1634 wx.Dialog.__init__(self, parent, -1, title, size=(740,580), style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER) 1635 field_color.get_color_info_from_profile(self) 1636 _ringtone_list=None 1637 _wallpaper_list=None 1638 self._data_key=datakey 1639 if movement and datakey is None and __debug__: 1640 self.log('Movement and datakey is None') 1641 raise ValueError 1642 # make a copy of the data we are going to work on 1643 self.dirty_widgets={ True: [], False: [] } 1644 self.data=factory.newdataobject(data) 1645 vs=wx.BoxSizer(wx.VERTICAL) 1646 # the title & direction button 1647 _hbs=wx.BoxSizer(wx.HORIZONTAL) 1648 self._title=wx.StaticText(self, -1, "Name here", style=wx.ALIGN_CENTRE|wx.ST_NO_AUTORESIZE) 1649 _add_btn=wx.BitmapButton(self, wx.NewId(), 1650 wx.ArtProvider.GetBitmap(guihelper.ART_ADD_FIELD), name="Prev Item") 1651 if movement: 1652 _prev_btn=wx.BitmapButton(self, wx.NewId(), wx.ArtProvider.GetBitmap(guihelper.ART_ARROW_LEFT), name="Prev Item") 1653 _next_btn=wx.BitmapButton(self, wx.NewId(), wx.ArtProvider.GetBitmap(guihelper.ART_ARROW_RIGHT), name="Next Item") 1654 self.dirty_widgets[False].append(_prev_btn) 1655 self.dirty_widgets[False].append(_next_btn) 1656 _hbs.Add(_prev_btn, 0, wx.EXPAND, 0) 1657 _hbs.Add(_add_btn, 0, wx.EXPAND|wx.LEFT, 10) 1658 _hbs.Add(self._title, 1, wx.EXPAND, 0) 1659 _hbs.Add(_next_btn, 0, wx.EXPAND, 0) 1660 wx.EVT_BUTTON(self, _prev_btn.GetId(), self.OnMovePrev) 1661 wx.EVT_BUTTON(self, _next_btn.GetId(), self.OnMoveNext) 1662 else: 1663 _hbs.Add(_add_btn, 0, wx.EXPAND|wx.LEFT, 10) 1664 _hbs.Add(self._title, 1, wx.EXPAND, 0) 1665 wx.EVT_BUTTON(self, _add_btn.GetId(), self.Add) 1666 vs.Add(_hbs, 0, wx.ALL|wx.EXPAND, 5) 1667 1668 nb=wx.Notebook(self, -1) 1669 self.nb=nb 1670 self.nb.OnDirtyUI=self.OnDirtyUI 1671 vs.Add(nb,1,wx.EXPAND|wx.ALL,5) 1672 1673 self.tabs=[] 1674 # instantiate the nb widgets 1675 for name,key,klass in self.tabsfactory: 1676 widget=EditorManager(self.nb, klass) 1677 nb.AddPage(widget,name) 1678 if key==keytoopenon or keytoopenon in key: 1679 nb.SetSelection(len(self.tabs)) 1680 self.tabs.append(widget) 1681 # populate the data 1682 self.Populate() 1683 # and focus on the right one if specified 1684 for _idx, (name,key,klass) in enumerate(self.tabsfactory): 1685 if key and key==keytoopenon and dataindex is not None: 1686 self.tabs[_idx].SetFocusOnValue(dataindex) 1687 1688 vs.Add(wx.StaticLine(self, -1, style=wx.LI_HORIZONTAL), 0, wx.EXPAND|wx.ALL, 5) 1689 1690 _btn_sizer=wx.StdDialogButtonSizer() 1691 if not readonly: 1692 _btn_sizer.AddButton(wx.Button(self, wx.ID_OK)) 1693 if self._data_key is not None: 1694 _w=wx.Button(self, wx.ID_APPLY) 1695 self.dirty_widgets[True].append(_w) 1696 _btn_sizer.AddButton(_w) 1697 wx.EVT_BUTTON(self, wx.ID_APPLY, self.OnApply) 1698 _btn_sizer.AddButton(wx.Button(self, wx.ID_CANCEL)) 1699 _w=wx.Button(self, wx.ID_REVERT_TO_SAVED) 1700 self.dirty_widgets[True].append(_w) 1701 _btn_sizer.SetNegativeButton(_w) 1702 _btn_sizer.Realize() 1703 vs.Add(_btn_sizer, 0, wx.ALIGN_CENTRE|wx.ALL, 5) 1704 1705 self.SetSizer(vs) 1706 1707 wx.EVT_BUTTON(self, wx.ID_REVERT_TO_SAVED, self.Revert) 1708 wx.EVT_TOOL(self, self.ID_UP, self.MoveUp) 1709 wx.EVT_TOOL(self, self.ID_DOWN, self.MoveDown) 1710 wx.EVT_TOOL(self, self.ID_ADD, self.Add) 1711 wx.EVT_TOOL(self, self.ID_DELETE, self.Delete) 1712 self.ignore_dirty=False 1713 self.dirty=False 1714 self.setdirty(False) 1715 1716 def Revert(self, _): 1717 # reload data 1718 self.Populate() 1719 self.setdirty(False) 1720 1721 def OnDirtyUI(self, _): 1722 self.setdirty() 1723 1724 def setdirty(self, flg=True): 1725 if self.ignore_dirty: 1726 return 1727 self.dirty=flg 1728 for w in self.dirty_widgets[self.dirty]: 1729 w.Enable(True) 1730 for w in self.dirty_widgets[not self.dirty]: 1731 w.Enable(False) 1732 1733 def OnApply(self, _): 1734 # Save the current data 1735 self.GetParent().SaveData(self.GetData(), self.GetDataKey()) 1736 self.setdirty(False) 1737 1738 def Populate(self): 1739 # populate various widget with data 1740 self._set_title() 1741 for _idx, (name,key,klass) in enumerate(self.tabsfactory): 1742 if key is None: 1743 # the fields are in data, not in data[key] 1744 self.tabs[_idx].Populate([self.data]) 1745 else: 1746 self.tabs[_idx].Populate(self.data.get(key, {})) 1747 1748 def GetData(self): 1749 res=self.data 1750 for i in range(len(self.tabsfactory)): 1751 widget=self.nb.GetPage(i) 1752 data=widget.Get() 1753 key=self.tabsfactory[i][1] 1754 if len(data): 1755 if key is None: 1756 res.update(data[0]) 1757 else: 1758 res[key]=data 1759 else: 1760 # remove the key 1761 try: 1762 if key is not None: 1763 del res[key] 1764 except KeyError: 1765 # which may not have existed ... 1766 pass 1767 return res 1768 1769 def GetDataKey(self): 1770 return self._data_key 1771 1772 def MoveUp(self, _): 1773 self.nb.GetPage(self.nb.GetSelection()).Move(-1) 1774 self.setdirty() 1775 1776 def MoveDown(self, _): 1777 self.nb.GetPage(self.nb.GetSelection()).Move(+1) 1778 self.setdirty() 1779 1780 def Add(self, _): 1781 self.nb.GetPage(self.nb.GetSelection()).Add() 1782 1783 def Delete(self, _): 1784 self.nb.GetPage(self.nb.GetSelection()).Delete() 1785 self.setdirty() 1786 1787 def _set_title(self): 1788 if hasattr(self, '_title'): 1789 self._title.SetLabel(nameparser.getfullname(self.data['names'][0])) 1790 1791 def OnMoveNext(self, _): 1792 _key,_data=self.GetParent().GetNextEntry(True) 1793 if _data: 1794 self.data=_data 1795 self._data_key=_key 1796 self.Populate() 1797 1798 def OnMovePrev(self, _): 1799 _key,_data=self.GetParent().GetNextEntry(False) 1800 if _data: 1801 self.data=_data 1802 self._data_key=_key 1803 self.Populate() 1804 1805 # SingleFieldEditor------------------------------------------------------------- 1806 class SingleFieldEditor(wx.Dialog): 1807 "Edit a single field for a groups of entries" 1808 1809 ID_DOWN=wx.NewId() 1810 ID_UP=wx.NewId() 1811 ID_ADD=wx.NewId() 1812 ID_DELETE=wx.NewId() 1813 1814 tabsfactory={ 1815 'categories': ("Categories", "categories", CategoryEditor), 1816 'wallpapers': ("Wallpapers", "wallpapers", WallpaperEditor), 1817 'ringtones': ("Ringtones", "ringtones", RingtoneEditor) } 1818 1819 color_field_name='phonebook' 1820 1821 def __init__(self, parent, key): 1822 if not self.tabsfactory.has_key(key): 1823 raise KeyError 1824 super(SingleFieldEditor, self).__init__(parent, -1, 1825 "Edit PhoneBook Entry", 1826 size=(740,580), 1827 style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER) 1828 field_color.get_color_info_from_profile(self) 1829 1830 self._key=key 1831 vs=wx.BoxSizer(wx.VERTICAL) 1832 _hbs=wx.BoxSizer(wx.HORIZONTAL) 1833 _add_btn=wx.BitmapButton(self, wx.NewId(), 1834 wx.ArtProvider.GetBitmap(guihelper.ART_ADD_FIELD), name="Prev Item") 1835 _hbs.Add(_add_btn, 0, wx.EXPAND|wx.LEFT, 10) 1836 wx.EVT_BUTTON(self, _add_btn.GetId(), self.Add) 1837 vs.Add(_hbs, 0, wx.ALL|wx.EXPAND, 5) 1838 1839 self.nb=wx.Notebook(self, -1) 1840 vs.Add(self.nb,1,wx.EXPAND|wx.ALL,5) 1841 1842 # instantiate the nb widgets 1843 name,key,klass=self.tabsfactory[key] 1844 widget=EditorManager(self.nb, klass) 1845 self.nb.AddPage(widget,name) 1846 1847 vs.Add(wx.StaticLine(self, -1, style=wx.LI_HORIZONTAL), 0, wx.EXPAND|wx.ALL, 5) 1848 _btn_sizer=self.CreateButtonSizer(wx.OK|wx.CANCEL) 1849 vs.Add(_btn_sizer, 0, wx.ALIGN_CENTRE|wx.ALL, 5) 1850 1851 self.SetSizer(vs) 1852 1853 wx.EVT_TOOL(self, self.ID_UP, self.MoveUp) 1854 wx.EVT_TOOL(self, self.ID_DOWN, self.MoveDown) 1855 wx.EVT_TOOL(self, self.ID_ADD, self.Add) 1856 wx.EVT_TOOL(self, self.ID_DELETE, self.Delete) 1857 1858 def MoveUp(self, _): 1859 self.nb.GetPage(0).Move(-1) 1860 1861 def MoveDown(self, _): 1862 self.nb.GetPage(0).Move(+1) 1863 1864 def Add(self, _): 1865 self.nb.GetPage(0).Add() 1866 1867 def Delete(self, _): 1868 self.nb.GetPage(0).Delete() 1869 1870 def GetData(self): 1871 return self.nb.GetPage(0).Get() 1872 1873 # main-------------------------------------------------------------------------- 1874 if __name__=='__main__': 1875 1876 # data to edit 1877 1878 data={ 'names': [ { 'full': 'John Smith'}, { 'nickname': 'I Love Testing'} ], 1879 'categories': [ {'category': 'business'}, {'category': 'friend' } ], 1880 # 'emails': [ {'email': 'ex1@example.com'}, {'email': 'ex2@example.net', 'type': 'home'} ], 1881 'urls': [ {'url': 'www.example.com'}, {'url': 'http://www.example.net', 'type': 'home'} ], 1882 'ringtones': [ {'ringtone': 'mi2.mid', 'use': 'call'}, {'ringtone': 'dots.mid', 'use': 'message'}], 1883 'addresses': [ {'type': 'home', 'street': '123 Main Street', 'city': 'Main Town', 'state': 'CA', 'postalcode': '12345'}, 1884 {'type': 'business', 'company': 'Acme Widgets Inc', 'street': '444 Industrial Way', 'street2': 'Square Business Park', 1885 'city': 'City Of Quality', 'state': 'Northern', 'postalcode': 'GHGJJ-12324', 'country': 'Nations United'} 1886 ], 1887 'wallpapers': [{'wallpaper': 'pic1.bmp', 'use': 'call'}, {'wallpaper': 'alert.jpg', 'use': 'message'}], 1888 'flags': [ {'secret': True}, {'wierd': 'orange'} ], 1889 'memos': [ {'memo': 'Some stuff about this person " is usually welcome'}, {'memo': 'A second note'}], 1890 'numbers': [ {'number': '123-432-2342', 'type': 'home', 'speeddial': 3}, {'number': '121=+4321/4', 'type': 'fax'}] 1891 } 1892 1893 app=wx.PySimpleApp() 1894 with guihelper.WXDialogWrapper(Editor(None,data), True): 1895 pass 1896
Generated by PyXR 0.9.4