PyXR

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



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