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: guihelper.py 4620 2008-06-28 02:53:17Z djpham $ 0009 0010 "Various helper routines for gui based code" 0011 0012 # These routines were initially in gui.py but that led to circular imports 0013 # which confused the heck out of pychecker 0014 0015 # standard modules 0016 from __future__ import with_statement 0017 import contextlib 0018 import os 0019 import glob 0020 import sys 0021 0022 # wx modules 0023 import wx 0024 0025 # my modules 0026 import common # note we modify the common module contents 0027 0028 ### 0029 ### The various IDs we use. Code below munges the integers into sequence 0030 ### 0031 0032 # Main menu items 0033 0034 ID_FILENEW=1 0035 ID_FILEOPEN=1 0036 ID_FILESAVE=1 0037 ID_FILEIMPORT=1 0038 ID_FILEEXPORT=1 0039 ID_FILEPRINT=1 0040 ID_FILEPRINTPREVIEW=1 0041 ID_FILEEXIT=1 0042 ID_EDITADDENTRY=1 0043 ID_EDITDELETEENTRY=1 0044 ID_EDITSELECTALL=1 0045 ID_EDITSETTINGS=1 0046 ID_EDITPHONEINFO=1 0047 ID_EDITDETECT=1 0048 ID_EDITCOPY=1 0049 ID_EDITPASTE=1 0050 ID_EDITRENAME=1 0051 ID_DATAGETPHONE=1 0052 ID_DATASENDPHONE=1 0053 ID_DATAHISTORICAL=1 0054 ID_DATANEWDB=1 0055 ID_AUTOSYNCSETTINGS=1 0056 ID_AUTOSYNCEXECUTE=1 0057 ID_VIEWCOLUMNS=1 0058 ID_VIEWPREVIEW=1 0059 ID_VIEWLOGDATA=1 0060 ID_VIEWCLEARLOGS=1 0061 ID_VIEWFILESYSTEM=1 0062 ID_HELPHELP=1 0063 ID_HELPCONTENTS=1 0064 ID_HELPTOUR=1 0065 ID_HELPSUPPORT=1 0066 ID_HELP_UPDATE=1 0067 ID_HELPABOUT=1 0068 ID_HELPPHONE=1 0069 ID_HELPHOWTOS=1 0070 ID_HELPFAQ=1 0071 ID_DR_SETTINGS=1 0072 ID_DEBUG_SCRIPT=1 0073 ID_FILEVIEW_THUMBNAIL=1 0074 ID_FILEVIEW_LIST=1 0075 0076 # file/filesystem viewer context menus 0077 ID_FV_SAVE=1 0078 ID_FV_HEXVIEW=1 0079 ID_FV_OVERWRITE=1 0080 ID_FV_MOVE=1 0081 ID_FV_NEWSUBDIR=1 0082 ID_FV_NEWFILE=1 0083 ID_FV_DELETE=1 0084 ID_FV_OPEN=1 0085 ID_FV_RENAME=1 0086 ID_FV_REFRESH=1 0087 ID_FV_PROPERTIES=1 0088 ID_FV_ADD=1 0089 ID_FV_BACKUP=1 0090 ID_FV_BACKUP_TREE=1 0091 ID_FV_RESTORE=1 0092 ID_FV_PASTE=1 0093 ID_FV_TOTAL_REFRESH=1 0094 ID_FV_OFFLINEPHONE=1 0095 ID_FV_REBOOTPHONE=1 0096 ID_FV_MODEMMODE=1 0097 ID_FV_COPY=1 0098 ID_FV_REPLACE=1 0099 0100 # export/import IDs 0101 ID_EXPORT_VCARD_CONTACTS=1 0102 ID_EXPORT_GROUPWARE_CONTACTS=1 0103 ID_EXPORT_CSV_CONTACTS=1 0104 ID_EXPORT_CSV_CALENDAR=1 0105 ID_EXPORT_SMS=1 0106 ID_EXPORT_CSV_CALL_HISTORY=1 0107 ID_EXPORT_MEDIA_TO_DIR=1 0108 ID_EXPORT_MEDIA_TO_ZIP=1 0109 ID_IMPORT_CSV_CONTACTS=1 0110 ID_IMPORT_CSV_CALENDAR=1 0111 ID_IMPORT_VCARDS=1 0112 ID_IMPORT_VCALENDAR=1 0113 ID_IMPORT_ICALENDAR=1 0114 ID_IMPORT_GCALENDAR=1 0115 ID_IMPORT_OUTLOOK_CONTACTS=1 0116 ID_IMPORT_OUTLOOK_CALENDAR=1 0117 ID_IMPORT_OUTLOOK_NOTES=1 0118 ID_IMPORT_OUTLOOK_TASKS=1 0119 ID_IMPORT_EVO_CONTACTS=1 0120 ID_IMPORT_QTOPIA_CONTACTS=1 0121 ID_IMPORT_GROUPWARE_CONTACTS=1 0122 ID_CALENDAR_WIZARD=1 0123 ID_IMPORT_WPL=1 0124 ID_EXPORT_ICALENDAR=1 0125 0126 # keep map around 0127 idmap={} 0128 # Start at 2 (if anything ends up being one then this code didn't spot it 0129 for idmapname in locals().keys(): 0130 if idmapname.startswith('ID_'): 0131 idnum=wx.NewId() 0132 # locals()[idmapname]=idnum 0133 exec "%s = %d" % (idmapname, idnum ) 0134 idmap[idnum]=idmapname 0135 0136 ### 0137 ### Various functions not attached to classes 0138 ### 0139 0140 0141 # These are the mime-types as used internally in wxWidgets 0142 _wxmimemapping={ 0143 'bmp': 'image/x-bmp', 0144 'ico': 'image/x-ico', 0145 'cur': 'image/x-cur', 0146 'ani': 'image/x-ani', 0147 'gif': 'image/gif', 0148 'iff': 'image/iff', 0149 'jpg': 'image/jpeg', 0150 'jpeg': 'image/jpeg', 0151 'pcx': 'image/pcx', 0152 'png': 'image/png', 0153 'pnm': 'image/pnm', 0154 'xpm': 'image/xpm', 0155 } 0156 0157 def getwxmimetype(filename): 0158 "Returns wx's mime type for the extension of filename, or None" 0159 return _wxmimemapping.get(getextension(filename.lower()), None) 0160 0161 def BusyWrapper(method): 0162 def _busywrapper(*args, **kwargs): 0163 wx.BeginBusyCursor() 0164 try: 0165 return method(*args, **kwargs) 0166 finally: 0167 wx.EndBusyCursor() 0168 0169 setattr(_busywrapper, "__doc__", getattr(method, "__doc__")) 0170 0171 return _busywrapper 0172 0173 @contextlib.contextmanager 0174 def MWBusyWrapper(mw): 0175 mw.OnBusyStart() 0176 try: 0177 yield mw 0178 finally: 0179 mw.OnBusyEnd() 0180 0181 @contextlib.contextmanager 0182 def WXDialogWrapper(dlg, showmodal=False): 0183 """ A wrapper for the wx.Dialog class that automatically calls Destroy""" 0184 try: 0185 yield (dlg, dlg.ShowModal()) if showmodal else dlg 0186 finally: 0187 dlg.Destroy() 0188 0189 @BusyWrapper 0190 def MessageDialog(*args, **kwargs): 0191 with WXDialogWrapper(wx.MessageDialog(*args, **kwargs), 0192 True): 0193 pass 0194 0195 # Filename functions. These work on brew names which use forward slash / 0196 # as the directory delimiter. The builtin Python functions can't be used 0197 # as they are platform specific (eg they use \ on Windows) 0198 0199 def getextension(str): 0200 """Returns the extension of a filename (characters after last period) 0201 0202 An empty string is returned if the file has no extension. The period 0203 character is not returned""" 0204 str=basename(str) 0205 if str.rfind('.')>=0: 0206 return str[str.rfind('.')+1:] 0207 return "" 0208 0209 def basename(str): 0210 """Returns the last part of the name (everything after last /)""" 0211 if str.rfind('/')<0: return str 0212 return str[str.rfind('/')+1:] 0213 0214 def dirname(str): 0215 """Returns everything before the last / in the name""" 0216 if str.rfind('/')<0: return "" 0217 return str[:str.rfind('/')] 0218 0219 def IsMSWindows(): 0220 """Are we running on Windows? 0221 0222 @rtype: Bool""" 0223 return wx.Platform=='__WXMSW__' 0224 0225 def IsGtk(): 0226 """Are we running on GTK (Linux) 0227 0228 @rtype: Bool""" 0229 return wx.Platform=='__WXGTK__' 0230 0231 def IsMac(): 0232 """Are we running on Mac 0233 0234 @rtype: Bool""" 0235 return wx.Platform=='__WXMAC__' 0236 0237 0238 def getbitmap(name): 0239 """Gets a bitmap from the resource directory 0240 0241 @rtype: wxBitmap 0242 """ 0243 return getimage(name).ConvertToBitmap() 0244 0245 def getimage(name): 0246 """Gets an image from the resource directory 0247 0248 @rtype: wx.Image 0249 """ 0250 for ext in ("", ".png", ".jpg"): 0251 if os.path.exists(getresourcefile(name+ext)): 0252 return wx.Image(getresourcefile(name+ext)) 0253 print "You need to make "+name+".png" 0254 return getimage('unknown') 0255 0256 0257 def getresourcefile(filename): 0258 """Returns name of file by adding it to resource directory pathname 0259 0260 No attempt is made to verify the file exists 0261 @rtype: string 0262 """ 0263 return os.path.join(resourcedirectory, filename) 0264 0265 def gethelpfilename(): 0266 """Returns what name we use for the helpfile 0267 0268 Without trailing extension as wxBestHelpController figures that out""" 0269 0270 # we look in a help subdirectory first which is 0271 # present in the developer tree 0272 j=os.path.join 0273 paths=( (helpdirectory, True), 0274 (resourcedirectory, False) ) 0275 0276 if IsMSWindows(): 0277 name="bitpim.chm" 0278 else: 0279 name="bitpim.htb" 0280 0281 for p,mention in paths: 0282 if os.path.isfile(j(p, name)): 0283 if mention: 0284 print "Using help file from "+p 0285 return j(p, "bitpim") 0286 0287 assert False 0288 0289 def getresourcefiles(wildcard): 0290 "Returns a list of filenames matching the wildcard in the resource directory" 0291 l=glob.glob(os.path.join(resourcedirectory, wildcard)) 0292 l.sort() 0293 return l 0294 0295 # Where to find bitmaps etc 0296 resourcedirectory=os.path.join(common.get_main_dir(), 'resources') 0297 helpdirectory=os.path.join(common.get_main_dir(), 'help') 0298 0299 # See strorunicode comment in common 0300 if wx.USE_UNICODE: 0301 def strorunicode(s): 0302 if s is None: return s 0303 if isinstance(s, unicode): return s 0304 return str(s) 0305 0306 common.strorunicode=strorunicode 0307 del strorunicode 0308 0309 else: 0310 def strorunicode(s): 0311 if s is None: return s 0312 try: 0313 return str(s) 0314 except UnicodeEncodeError: 0315 return s.encode("ascii", "replace") 0316 0317 common.strorunicode=strorunicode 0318 del strorunicode 0319 0320 # mime-type stuff 0321 def GetOpenCommand(mimetypes, filename): 0322 # go through list of mime-types until we can find a command for opening filename 0323 for mt in mimetypes: 0324 ft=wx.TheMimeTypesManager.GetFileTypeFromMimeType(mt) 0325 if ft is None: 0326 continue 0327 if IsGtk(): 0328 # protect file names with spaces 0329 cmd=ft.GetOpenCommand('"%s"'%filename) 0330 else: 0331 cmd=ft.GetOpenCommand(filename) 0332 if cmd is not None and len(cmd): 0333 return cmd 0334 return None 0335 0336 # Art provider stuff 0337 0338 # Our constants 0339 _ourart={ 0340 "ART_ADD_WALLPAPER": "add_picture", 0341 "ART_DEL_WALLPAPER": "delete_picture", 0342 "ART_ARROW_UP": "arrow_up", 0343 "ART_ARROW_DOWN": "arrow_down", 0344 "ART_ARROW_LEFT": "arrow_left", 0345 "ART_ARROW_RIGHT": "arrow_right", 0346 "ART_ADD_FIELD": "add_field", 0347 "ART_DEL_FIELD": "delete_field", 0348 "ART_ADD_CONTACT": "add_contact", 0349 "ART_DEL_CONTACT": "delete_contact", 0350 "ART_ADD_RINGER": "add_ringer", 0351 "ART_DEL_RINGER": "delete_ringer", 0352 "ART_ADD_MEMO": "add_memo", 0353 "ART_DEL_MEMO": "delete_memo", 0354 "ART_ADD_TODO": "add_todo", 0355 "ART_DEL_TODO": "delete_todo", 0356 "ART_ADD_SMS": "add_sms", 0357 "ART_DEL_SMS": "delete_sms", 0358 "ART_SEL_MEDIA": "select_media", 0359 "ART_SEL_IMAGE": "select_image", 0360 "ART_SEL_VIDEO": "select_video", 0361 "ART_SEL_CAMERA": "select_camera", 0362 "ART_SEL_SOUNDS": "select_sounds", 0363 "ART_SEL_PHONEBOOK": "select_phonebook", 0364 "ART_SEL_WALLPAPER": "select_wallpaper", 0365 "ART_SEL_RINGERS": "select_ringers", 0366 "ART_SEL_CALENDAR": "select_calendar", 0367 "ART_SEL_CALLHISTORY": "select_call_history", 0368 "ART_SEL_CALLS": "select_calls", 0369 "ART_SEL_SMS": "select_sms", 0370 "ART_SEL_MESSAGE": "select_message", 0371 "ART_SEL_FILE": "select_file", 0372 "ART_SEL_LOG": "select_log", 0373 "ART_SEL_MEMO": "select_memo", 0374 "ART_SEL_TODO": "select_todo", 0375 "ART_SEL_PLAYLIST": "select_playlist", 0376 "ART_SEL_PROTOCOL": "select_protocol", 0377 "ART_SEL_CONSOLE": "select_console", 0378 "ART_SEL_ROOT_IMAGE": "select_root", 0379 "ART_SEL_PHONE_ROOT": "phone_root", 0380 "ART_SEL_PHONE": "phone_root", 0381 "ART_DATAGETPHONE": "datagetphone", 0382 "ART_DATASENDPHONE": "datasendphone", 0383 "ART_AUTOSYNCEXECUTE": "autosyncexecute", 0384 "ART_HELPHELP": "helphelp", 0385 "ART_EDITPHONEINFO": "editphoneinfo", 0386 "ART_EDITDETECT": "editdetect", 0387 "ART_EDITSETTINGS": "editsettings", 0388 "ART_DATAHISTORICAL": "data_history", 0389 "ART_MEDIA_LIST_VIEW": "media_list_view", 0390 "ART_MEDIA_THUMB_VIEW": "media_thumb_view", 0391 "ART_FOLDER_OPEN": "folder_open", 0392 "ART_FOLDER": "folder" 0393 } 0394 0395 # populate namespace 0396 for s in _ourart: globals()[s]=s 0397 0398 class ArtProvider(wx.ArtProvider): 0399 """ArtProvider manages the art for the application""" 0400 0401 def CreateBitmap(self, artid, client, size): 0402 """Loads a bitmap and returns it depending on the parameters 0403 """ 0404 if artid in _ourart: 0405 return getbitmap(_ourart[artid]) 0406 return wx.NullBitmap 0407 0408 class MultiMessageBox: 0409 0410 def __init__(self, parent, title="Bitpim", dlg_msg=""): 0411 self.__title=title 0412 self.__dlg_msg=dlg_msg 0413 self.__parent=parent 0414 self.__msgs={} 0415 0416 def AddMessage(self, msg, priority=99): 0417 """ 0418 Add a message to the list of messages to be displayed 0419 Each message appears on it's own line 0420 """ 0421 # find the insertion point for the message 0422 # the key is in the format "priority.index", this creates a unique key which is 0423 # sortable in priority and insertion order 0424 loop=0 0425 key="%d.%05d" % (priority, loop) 0426 while self.__msgs.has_key(key): 0427 loop=loop+1 0428 key="%d.%05d" % (priority, loop) 0429 self.__msgs[key]={'msg': msg} 0430 return 0431 0432 def ShowMessages(self, max_rows=0, max_columns=0): 0433 """ 0434 Displays the messages in a list in a dialog 0435 max_rows: Max visible messages, if number of messages exceed 0436 this a scroll bar will appear 0437 max_columns: Max visible width in characters 0438 returns: button pressed to exit dialog 0439 """ 0440 keys=self.__msgs.keys() 0441 keys.sort() 0442 out_list=[] 0443 for k in keys: 0444 msg=self.__msgs[k]['msg'] 0445 out_list.append(msg) 0446 #construct the dialog for display 0447 msg_dlg=wx.Dialog(self.__parent, -1, self.__title, 0448 style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER|wx.SYSTEM_MENU|wx.MAXIMIZE_BOX|wx.MINIMIZE_BOX) 0449 main_bs=wx.BoxSizer(wx.VERTICAL) 0450 main_bs.Add(wx.StaticText(msg_dlg, -1, self.__dlg_msg), 0, wx.ALL|wx.ALIGN_LEFT, 5) 0451 msgs=wx.ListBox(msg_dlg) 0452 msgs.Set(out_list) 0453 main_bs.Add(msgs, 0, wx.ALL|wx.EXPAND, 5) 0454 main_bs.Add(wx.Button(msg_dlg, wx.ID_OK, 'OK'), 0, wx.ALIGN_CENTRE|wx.ALL, 5) 0455 msg_dlg.SetSizer(main_bs) 0456 msg_dlg.SetAutoLayout(True) 0457 main_bs.Fit(msg_dlg) 0458 # show the dialog 0459 res=msg_dlg.ShowModal() 0460 print "multi "+`res` 0461 msg_dlg.Destroy() 0462 return res 0463 0464 def MsgCount(self): 0465 return len(self.__msgs) 0466 0467 def ClearMessages(self): 0468 self.__msgs.clear() 0469
Generated by PyXR 0.9.4