0001 #!/usr/bin/env python 0002 0003 ### BITPIM 0004 ### 0005 ### Copyright (C) 2003-2006 Roger Binns <rogerb@rogerbinns.com> 0006 ### 0007 ### This program is free software; you can redistribute it and/or modify 0008 ### it under the terms of the BitPim license as detailed in the LICENSE file. 0009 ### 0010 ### $Id: fileview.py 4412 2007-09-28 01:28:56Z djpham $ 0011 0012 0013 ### 0014 ### File viewer 0015 ### 0016 from __future__ import with_statement 0017 import os 0018 import copy 0019 import cStringIO 0020 import time 0021 import base64 0022 import phone_media_codec 0023 import wx 0024 import guihelper 0025 import aggregatedisplay 0026 import pubsub 0027 import common 0028 import widgets 0029 import guiwidgets 0030 import shutil 0031 import database 0032 import helpids 0033 import tempfile 0034 0035 0036 basename=common.basename 0037 stripext=common.stripext 0038 getext=common.getext 0039 0040 0041 #------------------------------------------------------------------------------- 0042 class MediaDataObject(database.basedataobject): 0043 # modified_datatime is unix time 0044 _knownproperties=['name', 'origin', 'index', 'timestamp'] 0045 _knownlistproperties=database.basedataobject._knownlistproperties.copy() 0046 _knownlistproperties.update( { 'mediadata': ['data'] }) 0047 def __init__(self, data=None): 0048 if data is None or not isinstance(data, MediaEntry): 0049 return; 0050 self.update(data.get_db_dict()) 0051 mediaobjectfactory=database.dataobjectfactory(MediaDataObject) 0052 0053 #------------------------------------------------------------------------------- 0054 class MediaEntry(object): 0055 _id_index=0 0056 _max_id_index=999 0057 def __init__(self): 0058 self._data={ 'serials': [] } 0059 self._create_id() 0060 0061 def get(self): 0062 res=copy.deepcopy(self._data, None, {}) 0063 # account for the medidadata field 0064 if res.has_key('mediadata'): 0065 if res['mediadata'] is not None: 0066 res['mediadata']=[{'data': buffer(res['mediadata']) }] 0067 else: 0068 del res['mediadata'] 0069 return res 0070 def set(self, d): 0071 self._data={} 0072 self._data.update(d) 0073 # check for mediadata 0074 if d.get('mediadata', None) is not None: 0075 self._data['mediadata']=str(d['mediadata'][0]['data']) 0076 0077 def get_db_dict(self): 0078 return self.get() 0079 def set_db_dict(self, d): 0080 self.set(d) 0081 0082 def _create_id(self): 0083 "Create a BitPim serial for this entry" 0084 self._data.setdefault("serials", []).append(\ 0085 {"sourcetype": "bitpim", 0086 "id": '%.3f%03d'%(time.time(), MediaEntry._id_index) }) 0087 if MediaEntry._id_index<MediaEntry._max_id_index: 0088 MediaEntry._id_index+=1 0089 else: 0090 MediaEntry._id_index=0 0091 def _get_id(self): 0092 s=self._data.get('serials', []) 0093 for n in s: 0094 if n.get('sourcetype', None)=='bitpim': 0095 return n.get('id', None) 0096 return None 0097 def _set_id(self, id): 0098 s=self._data.get('serials', []) 0099 for n in s: 0100 if n.get('sourcetype', None)=='bitpim': 0101 n['id']=id 0102 return 0103 self._data['serials'].append({'sourcetype': 'bitpim', 'id': id } ) 0104 id=property(fget=_get_id, fset=_set_id) 0105 0106 def _set_or_del(self, key, v, v_list=[]): 0107 if v is None or v in v_list: 0108 if self._data.has_key(key): 0109 del self._data[key] 0110 else: 0111 self._data[key]=v 0112 0113 def _get_origin(self): 0114 return self._data.get('origin', '') 0115 def _set_origin(self, v): 0116 if v is None: 0117 if self._data.has_key('origin'): 0118 del self._data['origin'] 0119 return 0120 if not isinstance(v, (str, unicode)): 0121 raise TypeError,'not a string or unicode type' 0122 self._data['origin']=v 0123 origin=property(fget=_get_origin, fset=_set_origin) 0124 0125 def _get_mediadata(self): 0126 return self._data.get('mediadata', None) 0127 def _set_mediadata(self, v): 0128 if v is not None: 0129 self._set_or_del('mediadata', v, []) 0130 mediadata=property(fget=_get_mediadata, fset=_set_mediadata) 0131 0132 def _get_name(self): 0133 return self._data.get('name', '') 0134 def _set_name(self, v): 0135 self._set_or_del('name', v, ('',)) 0136 name=property(fget=_get_name, fset=_set_name) 0137 0138 def _get_index(self): 0139 return self._data.get('index', -1) 0140 def _set_index(self, v): 0141 self._set_or_del('index', v, ('',)) 0142 index=property(fget=_get_index, fset=_set_index) 0143 0144 def _get_timestamp(self): 0145 return self._data.get('timestamp', None) 0146 def _set_timestamp(self, v): 0147 if v is not None: 0148 if not isinstance(v, (int, float)): 0149 raise TypeError('timestamp property is an int arg') 0150 v=int(v) 0151 self._set_or_del('timestamp', v) 0152 timestamp=property(fget=_get_timestamp, fset=_set_timestamp) 0153 0154 def DrawTextWithLimit(dc, x, y, text, widthavailable, guardspace, term="..."): 0155 """Draws text and if it will overflow the width available, truncates and puts ... at the end 0156 0157 @param x: start position for text 0158 @param y: start position for text 0159 @param text: the string to draw 0160 @param widthavailable: the total amount of space available 0161 @param guardspace: if the text is longer than widthavailable then this amount of space is 0162 reclaimed from the right handside and term put there instead. Consequently 0163 this value should be at least the width of term 0164 @param term: the string that is placed in the guardspace if it gets truncated. Make sure guardspace 0165 is at least the width of this string! 0166 @returns: The extent of the text that was drawn in the end as a tuple of (width, height) 0167 """ 0168 w,h=dc.GetTextExtent(text) 0169 if w<widthavailable: 0170 dc.DrawText(text,x,y) 0171 return w,h 0172 extents=dc.GetPartialTextExtents(text) 0173 limit=widthavailable-guardspace 0174 # find out how many chars in we have to go before hitting limit 0175 for i,offset in enumerate(extents): 0176 if offset>limit: 0177 break 0178 # back off 1 in case the new text's a tad long 0179 if i: 0180 i-=1 0181 text=text[:i]+term 0182 w,h=dc.GetTextExtent(text) 0183 assert w<=widthavailable 0184 dc.DrawText(text, x, y) 0185 return w,h 0186 0187 media_codec=phone_media_codec.codec_name 0188 class MyFileDropTarget(wx.FileDropTarget): 0189 def __init__(self, target, drag_over=False, enter_leave=False): 0190 wx.FileDropTarget.__init__(self) 0191 self.target=target 0192 self.drag_over=drag_over 0193 self.enter_leave=enter_leave 0194 0195 def OnDropFiles(self, x, y, filenames): 0196 return self.target.OnDropFiles(x,y,filenames) 0197 0198 def OnDragOver(self, x, y, d): 0199 if self.drag_over: 0200 return self.target.OnDragOver(x,y,d) 0201 return wx.FileDropTarget.OnDragOver(self, x, y, d) 0202 0203 def OnEnter(self, x, y, d): 0204 if self.enter_leave: 0205 return self.target.OnEnter(x,y,d) 0206 return wx.FileDropTarget.OnEnter(self, x, y, d) 0207 0208 def OnLeave(self): 0209 if self.enter_leave: 0210 return self.target.OnLeave() 0211 return wx.FileDropTarget.OnLeave(self) 0212 0213 class FileView(wx.Panel, widgets.BitPimWidget): 0214 0215 # Various DC objects used for drawing the items. We have to calculate them in the constructor as 0216 # the app object hasn't been constructed when this file is imported. 0217 item_selection_brush=None 0218 item_selection_pen=None 0219 item_line_font=None 0220 item_term="..." 0221 item_guardspace=None 0222 # Files we should ignore 0223 skiplist= ( 'desktop.ini', 'thumbs.db', 'zbthumbnail.info' ) 0224 database_key="" 0225 0226 # how much data do we want in call to getdata 0227 NONE=0 0228 SELECTED=1 0229 ALL=2 0230 0231 # maximum length of a filename 0232 maxlen=-1 # set via phone profile 0233 # acceptable characters in a filename 0234 filenamechars=None # set via phone profile 0235 # Default Help page, children can override 0236 helpid=helpids.ID_TAB_MEDIA 0237 0238 def __init__(self, mainwindow, parent, media_root, watermark=None): 0239 wx.Panel.__init__(self,parent,style=wx.CLIP_CHILDREN) 0240 # adjust the DB to accommodate the new schema if necessary 0241 self._fixupdb(mainwindow.database) 0242 # item attributes 0243 if self.item_selection_brush is None: 0244 self.item_selection_brush=wx.TheBrushList.FindOrCreateBrush("MEDIUMPURPLE2", wx.SOLID) 0245 self.item_selection_pen=wx.ThePenList.FindOrCreatePen("MEDIUMPURPLE2", 1, wx.SOLID) 0246 f1=wx.TheFontList.FindOrCreateFont(10, wx.SWISS, wx.NORMAL, wx.BOLD) 0247 f2=wx.TheFontList.FindOrCreateFont(10, wx.SWISS, wx.NORMAL, wx.NORMAL) 0248 self.item_line_font=[f1, f2, f2, f2] 0249 dc=wx.MemoryDC() 0250 dc.SelectObject(wx.EmptyBitmap(100,100)) 0251 self.item_guardspace=dc.GetTextExtent(self.item_term)[0] 0252 del dc 0253 0254 # no redraw ickiness 0255 # wx.EVT_ERASE_BACKGROUND(self, lambda evt: None) 0256 0257 self.parent=parent 0258 self.mainwindow=mainwindow 0259 self.thedir=None 0260 self.wildcard="I forgot to set wildcard in derived class|*" 0261 self.__dragging=False 0262 self._in_context_menu=False 0263 self.media_root=media_root 0264 self.show_thumbnail=True 0265 self.active_section="" 0266 # origins that should not be used for phonebook 0267 self.excluded_origins=() 0268 0269 # use the aggregatedisplay to do the actual item display 0270 self.aggdisp=aggregatedisplay.Display(self, self, watermark) # we are our own datasource 0271 self.vbs=wx.BoxSizer(wx.VERTICAL) 0272 0273 ### toolbar 0274 self.tb=wx.ToolBar(self, -1, style=wx.TB_3DBUTTONS|wx.TB_HORIZONTAL) 0275 self.tb.SetToolBitmapSize(wx.Size(18,18)) 0276 sz=self.tb.GetToolBitmapSize() 0277 0278 # list and thumbnail tools 0279 self.tb.AddRadioLabelTool(guihelper.ID_FILEVIEW_THUMBNAIL, "Thumbnail", 0280 wx.ArtProvider.GetBitmap(guihelper.ART_MEDIA_THUMB_VIEW, wx.ART_TOOLBAR, sz), 0281 wx.ArtProvider.GetBitmap(guihelper.ART_MEDIA_THUMB_VIEW, wx.ART_TOOLBAR, sz), 0282 "Show Thumbnails", "Show items as thumbnails") 0283 self.tb.AddRadioLabelTool(guihelper.ID_FILEVIEW_LIST, "List", 0284 wx.ArtProvider.GetBitmap(guihelper.ART_MEDIA_LIST_VIEW, wx.ART_TOOLBAR, sz), 0285 wx.ArtProvider.GetBitmap(guihelper.ART_MEDIA_LIST_VIEW, wx.ART_TOOLBAR, sz), 0286 "Show List", "Show items in a list") 0287 self.vbs.Add(self.tb, 0, wx.EXPAND|wx.ALL, 1) 0288 self.aggr_sizer=self.vbs.Add(self.aggdisp, 1, wx.EXPAND|wx.ALL, 2) 0289 0290 # main list 0291 column_info=self.GetColumnNames() 0292 self.item_list=guiwidgets.BitPimListCtrl(self, column_info) 0293 self.nodes={} 0294 self.nodes_keys={} 0295 self.item_list.ResetView(self.nodes, self.nodes_keys) 0296 self.item_sizer=self.vbs.Add(self.item_list, 1, wx.EXPAND|wx.ALL, 2) 0297 self.item_sizer.Show(False) 0298 self.note=self.vbs.Add(wx.StaticText(self, -1, ' Note: Click column headings to sort data'), 0, wx.ALIGN_CENTRE|wx.BOTTOM, 10) 0299 self.note.Show(False) 0300 self.SetSizer(self.vbs) 0301 timerid=wx.NewId() 0302 self.thetimer=wx.Timer(self, timerid) 0303 wx.EVT_TIMER(self, timerid, self.OnTooltipTimer) 0304 self.motionpos=None 0305 wx.EVT_MOUSE_EVENTS(self.aggdisp, self.OnMouseEvent) 0306 self.tipwindow=None 0307 if True: # guihelper.IsMSWindows() or guihelper.IsGtk(): 0308 # turn on drag-and-drag for all platforms 0309 wx.EVT_MOTION(self.aggdisp, self.OnStartDrag) 0310 0311 # Menus 0312 0313 self.itemmenu=wx.Menu() 0314 self.itemmenu.Append(guihelper.ID_FV_OPEN, "Open") 0315 self.itemmenu.Append(guihelper.ID_FV_SAVE, "Save ...") 0316 self.itemmenu.AppendSeparator() 0317 if guihelper.IsMSWindows(): 0318 self.itemmenu.Append(guihelper.ID_FV_COPY, "Copy") 0319 self.itemmenu.Append(guihelper.ID_FV_DELETE, "Delete") 0320 self.itemmenu.Append(guihelper.ID_FV_RENAME, "Rename") 0321 self.movemenu=wx.Menu() 0322 self.itemmenu.AppendMenu(guihelper.ID_FV_MOVE, "Move to", self.movemenu) 0323 self.itemmenu.AppendSeparator() 0324 self.itemmenu.Append(guihelper.ID_FV_REPLACE, "Replace") 0325 # self.itemmenu.Append(guihelper.ID_FV_RENAME, "Rename") 0326 self.itemmenu.Append(guihelper.ID_FV_REFRESH, "Refresh") 0327 0328 self.bgmenu=wx.Menu() 0329 self.bgmenu.Append(guihelper.ID_FV_ADD, "Add ...") 0330 self.bgmenu.Append(guihelper.ID_FV_PASTE, "Paste") 0331 self.bgmenu.Append(guihelper.ID_FV_REFRESH, "Refresh") 0332 0333 wx.EVT_MENU(self.tb, guihelper.ID_FILEVIEW_THUMBNAIL, self.OnThumbnailView) 0334 wx.EVT_MENU(self.tb, guihelper.ID_FILEVIEW_LIST, self.OnListView) 0335 0336 0337 wx.EVT_MENU(self.itemmenu, guihelper.ID_FV_OPEN, self.OnLaunch) 0338 wx.EVT_MENU(self.itemmenu, guihelper.ID_FV_SAVE, self.OnSave) 0339 if guihelper.IsMSWindows(): 0340 wx.EVT_MENU(self.itemmenu, guihelper.ID_FV_COPY, self.OnCopy) 0341 wx.EVT_MENU(self.itemmenu, guihelper.ID_FV_DELETE, self.OnDelete) 0342 wx.EVT_MENU(self.itemmenu, guihelper.ID_FV_RENAME, self.OnRename) 0343 wx.EVT_MENU(self.itemmenu, guihelper.ID_FV_REPLACE, self.OnReplace) 0344 wx.EVT_MENU(self.itemmenu, guihelper.ID_FV_REFRESH, lambda evt: self.OnRefresh()) 0345 wx.EVT_MENU(self.bgmenu, guihelper.ID_FV_ADD, self.OnAdd) 0346 wx.EVT_MENU(self.bgmenu, guihelper.ID_FV_PASTE, self.OnPaste) 0347 wx.EVT_MENU(self.bgmenu, guihelper.ID_FV_REFRESH, lambda evt: self.OnRefresh) 0348 0349 wx.EVT_RIGHT_UP(self.aggdisp, self.OnRightClick) 0350 wx.EVT_LIST_ITEM_RIGHT_CLICK(self.item_list, self.item_list.GetId(), self.OnRightClick) 0351 aggregatedisplay.EVT_ACTIVATE(self.aggdisp, self.aggdisp.GetId(), self.OnLaunch) 0352 wx.EVT_LIST_ITEM_ACTIVATED(self.item_list, self.item_list.GetId(), self.OnLaunch) 0353 0354 self.droptarget=MyFileDropTarget(self) 0355 self.SetDropTarget(self.droptarget) 0356 wx.EVT_SIZE(self, self.OnSize) 0357 wx.EVT_IDLE(self, self.OnIdle) 0358 wx.EVT_KEY_DOWN(self.aggdisp, self.OnKeyDown) 0359 wx.EVT_KEY_UP(self.aggdisp, self.OnKeyUp) 0360 self.tb.Realize() 0361 pubsub.subscribe(self.OnMediaInfo, pubsub.REQUEST_MEDIA_INFO) 0362 pubsub.subscribe(self.OnMediaOpen, pubsub.REQUEST_MEDIA_OPEN) 0363 0364 def OnIdle(self, _): 0365 "Save out changed data" 0366 if self.modified: 0367 self.modified=False 0368 self._populatefs(self._data) 0369 self.OnListRequest() # broadcast changes 0370 0371 def OnKeyDown(self, evt): 0372 if guihelper.IsGtk(): 0373 if evt.GetKeyCode()==wx.WXK_SHIFT: 0374 self._shift_down=True 0375 else: 0376 self._shift_down=evt.ShiftDown() 0377 evt.Skip() 0378 0379 def OnKeyUp(self, evt): 0380 if guihelper.IsGtk(): 0381 if evt.GetKeyCode()==wx.WXK_SHIFT: 0382 self._shift_down=False 0383 else: 0384 self._shift_down=evt.ShiftDown() 0385 evt.Skip() 0386 0387 def OnThumbnailView(self, _): 0388 self.thetimer.Stop() 0389 self.show_thumbnail=True 0390 self.item_sizer.Show(False) 0391 self.note.Show(False) 0392 self.aggr_sizer.Show(True) 0393 self.aggdisp.SetFocus() 0394 self.vbs.Layout() 0395 0396 def OnListView(self, _): 0397 self.thetimer.Stop() 0398 self.show_thumbnail=False 0399 self.aggr_sizer.Show(False) 0400 # resize to hide the thumbnails otherwise it still gets the mouse scroll events. 0401 self.aggdisp.SetSize((1,1)) 0402 self.item_sizer.Show(True) 0403 self.item_list.SetFocus() 0404 self.note.Show(True) 0405 self.vbs.Layout() 0406 0407 def OnSelected(self, node): 0408 self.active_section=self.media_root.GetNodeName(self, node) 0409 self.aggdisp.SetActiveSection(self.active_section) 0410 self.MakeMoveMenu() 0411 self.OnRefreshList() 0412 0413 def GetRightClickMenuItems(self, node): 0414 # we set these values so that the event hander knows only to save 0415 # this origin rather than all media, we clear these values after 0416 # the menu is dismissed (see OnRightClickMenuExit below) 0417 self.media_root.widget_to_save=self 0418 self.media_root.origin_to_save=self.active_section 0419 result=[] 0420 result.append((widgets.BitPimWidget.MENU_NORMAL, guihelper.ID_EDITADDENTRY, "Add to %s" % self.active_section, "Add a new media items")) 0421 result.append((widgets.BitPimWidget.MENU_NORMAL, guihelper.ID_EDITDELETEENTRY, "Delete Selected", "Delete Selected Items")) 0422 result.append((widgets.BitPimWidget.MENU_NORMAL, guihelper.ID_EDITSELECTALL, "Select All", "Select All Items")) 0423 if guihelper.IsMSWindows(): 0424 result.append((widgets.BitPimWidget.MENU_NORMAL, guihelper.ID_EDITCOPY, "Copy", "Copy Selected Items")) 0425 result.append((widgets.BitPimWidget.MENU_SPACER, 0, "", "")) 0426 result.append((widgets.BitPimWidget.MENU_NORMAL, guihelper.ID_EXPORT_MEDIA_TO_DIR, "Export %s to Folder ..." % self.active_section, "Export the media to a folder on your hard drive")) 0427 result.append((widgets.BitPimWidget.MENU_NORMAL, guihelper.ID_EXPORT_MEDIA_TO_ZIP, "Export %s to Zip File ..." % self.active_section, "Export the media to a zip file")) 0428 return result 0429 0430 def OnRightClickMenuExit(self): 0431 # clear these values now that the event has been processed 0432 self.media_root.widget_to_save=None 0433 self.media_root.origin_to_save="" 0434 0435 def MakeMoveMenu(self): 0436 # redo the move menu 0437 menuItems = self.movemenu.GetMenuItems() 0438 for i, menuItem in enumerate(menuItems): 0439 self.Unbind(wx.EVT_MENU, id=menuItem.GetId()) 0440 self.movemenu.DeleteItem(menuItem) 0441 0442 # get the list of origins 0443 origins=self.media_root.GetNodeList(self) 0444 origins.remove(self.active_section) 0445 if len(origins): 0446 for origin in origins: 0447 mid=wx.NewId() 0448 self.movemenu.Append(mid, origin) 0449 wx.EVT_MENU(self, mid, self.OnMoveItem) 0450 0451 def GetColumnNames(self): 0452 columns=[] 0453 columns.append(("Name", 120, False)) 0454 columns.append(("Size/bytes", 80, True)) 0455 columns.append(("Date Modified", 120, False)) 0456 columns.append(("File Details", 380, False)) 0457 return columns 0458 0459 def OnSize(self, evt): 0460 # stop the tool tip from poping up when we're resizing! 0461 if self.thetimer.IsRunning(): 0462 self.thetimer.Stop() 0463 evt.Skip() 0464 0465 def OnRightClick(self, evt): 0466 """Popup the right click context menu 0467 0468 @param widget: which widget to popup in 0469 @param position: position in widget 0470 @param onitem: True if the context menu is for an item 0471 """ 0472 if len(self.GetSelectedItems()): 0473 menu=self.itemmenu 0474 item=self.GetSelectedItems()[0] 0475 single=len(self.GetSelectedItems())==1 0476 menu.Enable(guihelper.ID_FV_RENAME, single) 0477 # we always launch on mac 0478 if not guihelper.IsMac(): 0479 menu.FindItemById(guihelper.ID_FV_OPEN).Enable(guihelper.GetOpenCommand(item.mimetypes, item.name) is not None) 0480 else: 0481 menu=self.bgmenu 0482 menu.Enable(guihelper.ID_FV_PASTE, self.CanPaste()) 0483 if menu is None: 0484 return 0485 # we're putting up the context menu, quit the tool tip timer. 0486 self._in_context_menu=True 0487 self.aggdisp.PopupMenu(menu, evt.GetPosition()) 0488 self._in_context_menu=False 0489 0490 def OnMoveItem(self, evt): 0491 new_origin=None 0492 items=self.GetSelectedItems() 0493 new_origin=self.movemenu.FindItemById(evt.GetId()).GetLabel() 0494 for item in items: 0495 if new_origin!=None and new_origin in self.media_root.GetNodeList(self): 0496 # make sure this name is not already used 0497 for i in self._data[self.database_key]: 0498 if self._data[self.database_key][i].origin==new_origin and \ 0499 self._data[self.database_key][i].name==item.name: 0500 wx.MessageBox("A file with the same name already exists in %s!" % new_origin, "Move Error", wx.OK|wx.ICON_EXCLAMATION) 0501 return 0502 wx.BeginBusyCursor() 0503 item.ChangeOriginInIndex(new_origin) 0504 self.OnRefresh() 0505 wx.EndBusyCursor() 0506 0507 def _launch(self, item): 0508 # Open/Launch the specified item 0509 me=self._data[self.database_key][item.key] 0510 fname=self._gettempfile(me) 0511 if guihelper.IsMac(): 0512 import findertools 0513 findertools.launch(fname) 0514 return 0515 cmd=guihelper.GetOpenCommand(item.mimetypes, fname) 0516 if cmd is None: 0517 wx.Bell() 0518 else: 0519 wx.Execute(cmd, wx.EXEC_ASYNC) 0520 0521 @guihelper.BusyWrapper 0522 def OnLaunch(self, _): 0523 self._launch(self.GetSelectedItems()[0]) 0524 0525 if True: # guihelper.IsMSWindows() or guihelper.IsGtk(): 0526 # drag-and-drop files should work on all platforms 0527 def OnStartDrag(self, evt): 0528 evt.Skip() 0529 if not evt.LeftIsDown(): 0530 return 0531 items=self.GetSelectedItems() 0532 if not len(items): 0533 return 0534 drag_source=wx.DropSource(self) 0535 file_names=wx.FileDataObject() 0536 for item in items: 0537 me=self._data[self.database_key][item.key] 0538 fname=self._gettempfile(me) 0539 if not os.path.isfile(fname): 0540 continue 0541 file_names.AddFile(fname) 0542 drag_source.SetData(file_names) 0543 self.__dragging=True 0544 res=drag_source.DoDragDrop(wx.Drag_AllowMove) 0545 self.__dragging=False 0546 # check of any of the files have been removed, 0547 # can't trust result returned by DoDragDrop 0548 for item in items: 0549 me=self._data[self.database_key][item.key] 0550 fname=self._gettempfile(me) 0551 if not os.path.isfile(fname): 0552 item.RemoveFromIndex() 0553 0554 def OnMouseEvent(self, evt): 0555 self.motionpos=evt.GetPosition() 0556 # on windows if we quickly move the mouse out of bitpim window we never get an event and we will pop up 0557 # the tooltip when we should not, so we check the position after the timeout and see if it has moved. 0558 self.abs_mouse_pos=wx.GetMousePosition() 0559 evt.Skip() 0560 self.thetimer.Stop() 0561 if evt.AltDown() or evt.MetaDown() or evt.ControlDown() or \ 0562 evt.ShiftDown() or evt.Dragging() or evt.IsButton() or \ 0563 self._in_context_menu or not self.show_thumbnail: 0564 return 0565 self.thetimer.Start(1750, wx.TIMER_ONE_SHOT) 0566 0567 def OnTooltipTimer(self, _): 0568 if self._in_context_menu or not self.show_thumbnail or \ 0569 wx.GetApp().critical.isSet(): 0570 # we're putting up a context menu or main app is busy, forget this 0571 return 0572 # see if we have moved 0573 if self.abs_mouse_pos!=wx.GetMousePosition(): 0574 return 0575 x,y=self.aggdisp.CalcUnscrolledPosition(*self.motionpos) 0576 res=self.aggdisp.HitTest(x,y) 0577 if res.item is not None: 0578 try: self.tipwindow.Destroy() 0579 except: pass 0580 self.tipwindow=res.item.DisplayTooltip(self.aggdisp, res.itemrectscrolled) 0581 0582 def OnRefresh(self): 0583 # update aggregate view 0584 self.aggdisp.UpdateItems() 0585 self.OnRefreshList() 0586 self.media_root.DoMediaSummary() 0587 0588 def OnRefreshList(self): 0589 # update list view 0590 self.nodes={} 0591 self.nodes_keys={} 0592 index=0 0593 for k,e in self.sections: 0594 if self.active_section==None or k.label==self.active_section: 0595 for item in e: 0596 # replace linefeeds in description 0597 dlist=item.long.splitlines() 0598 d="" 0599 for l in dlist: 0600 if len(d): 0601 d+=" - " 0602 d+=l 0603 self.nodes[index]=(item.name, str(item.size), item.timestamp, d) 0604 self.nodes_keys[index]=item 0605 index+=1 0606 self.item_list.ResetView(self.nodes, self.nodes_keys) 0607 0608 def GetSelectedItems(self): 0609 if self.show_thumbnail: 0610 return [item for _,_,_,item in self.aggdisp.GetSelection()] 0611 res=[] 0612 sel=self.item_list.GetSelections() 0613 for sel_idx in sel: 0614 res.append(self.item_list.GetItemData(sel[sel_idx])) 0615 return res 0616 0617 def GetAllItems(self): 0618 return [item for _,_,_,item in self.aggdisp.GetAllItems()] 0619 0620 def CanSelectAll(self): 0621 return self.item_list.GetItemCount() > 0 0622 0623 def OnSelectAll(self, _): 0624 self.aggdisp.SelectAll() 0625 self.item_list.SelectAll() 0626 0627 def OnSave(self, _): 0628 # If one item is selected we ask for a filename to save. If 0629 # multiple then we ask for a directory, and users don't get 0630 # the choice to affect the names of files. Note that we don't 0631 # allow users to select a different format for the file - we 0632 # just copy it as is. 0633 items=self.GetSelectedItems() 0634 if len(items)==1: 0635 ext=getext(items[0].name) 0636 if ext=="": ext="*" 0637 else: ext="*."+ext 0638 with guihelper.WXDialogWrapper(wx.FileDialog(self, "Save item", wildcard=ext, defaultFile=items[0].name, style=wx.SAVE|wx.OVERWRITE_PROMPT|wx.CHANGE_DIR), 0639 True) as (dlg, retcode): 0640 if retcode==wx.ID_OK: 0641 file(dlg.GetPath(), "wb").write(self._data[items[0].datakey][items[0].key].mediadata) 0642 if self._data[items[0].datakey][items[0].key].timestamp!=None: 0643 os.utime(dlg.GetPath(), (self._data[items[0].datakey][items[0].key].timestamp, 0644 self._data[items[0].datakey][items[0].key].timestamp)) 0645 else: 0646 with guihelper.WXDialogWrapper(wx.DirDialog(self, "Save items to", style=wx.DD_DEFAULT_STYLE|wx.DD_NEW_DIR_BUTTON), 0647 True) as (dlg, retcode): 0648 if retcode==wx.ID_OK: 0649 for item in items: 0650 fname=item.name.encode(media_codec) 0651 fname=os.path.join(dlg.GetPath(), basename(fname)) 0652 file(fname, 'wb').write(self._data[item.datakey][item.key].mediadata) 0653 if self._data[item.datakey][item.key].timestamp!=None: 0654 os.utime(fname, (self._data[item.datakey][item.key].timestamp, 0655 self._data[item.datakey][item.key].timestamp)) 0656 0657 if guihelper.IsMSWindows(): 0658 def OnCopy(self, _): 0659 items=self.GetSelectedItems() 0660 if not len(items): 0661 # nothing selected 0662 return 0663 file_names=wx.FileDataObject() 0664 for item in items: 0665 me=self._data[self.database_key][item.key] 0666 fname=self._gettempfile(me) 0667 if not os.path.isfile(fname): 0668 continue 0669 file_names.AddFile(fname) 0670 if wx.TheClipboard.Open(): 0671 wx.TheClipboard.SetData(file_names) 0672 wx.TheClipboard.Close() 0673 def CanCopy(self): 0674 return len(self.GetSelectedItems()) 0675 0676 if guihelper.IsGtk(): 0677 # Gtk just pastes the file names as text onto the Clipboard 0678 def OnPaste(self, _=None): 0679 if not wx.TheClipboard.Open(): 0680 # can't access the clipboard 0681 return 0682 if wx.TheClipboard.IsSupported(wx.DataFormat(wx.DF_UNICODETEXT)): 0683 file_names=wx.TextDataObject() 0684 has_data=wx.TheClipboard.GetData(file_names) 0685 else: 0686 has_data=False 0687 wx.TheClipboard.Close() 0688 if has_data: 0689 # collect file names if any. 0690 _names=[x for x in file_names.GetText().split('\n') \ 0691 if os.path.isfile(x) ] 0692 if _names: 0693 self.OnAddFiles(_names) 0694 def CanPaste(self): 0695 """ Return True if can accept clipboard data, False otherwise 0696 """ 0697 if not wx.TheClipboard.Open(): 0698 return False 0699 r=wx.TheClipboard.IsSupported(wx.DataFormat(wx.DF_UNICODETEXT)) 0700 if r: 0701 file_names=wx.TextDataObject() 0702 r=wx.TheClipboard.GetData(file_names) 0703 if r: 0704 for _name in file_names.GetText().split('\n'): 0705 if not os.path.isfile(_name): 0706 r=False 0707 break 0708 wx.TheClipboard.Close() 0709 return r 0710 else: 0711 def OnPaste(self, _=None): 0712 if not wx.TheClipboard.Open(): 0713 # can't access the clipboard 0714 return 0715 if wx.TheClipboard.IsSupported(wx.DataFormat(wx.DF_FILENAME)): 0716 file_names=wx.FileDataObject() 0717 has_data=wx.TheClipboard.GetData(file_names) 0718 else: 0719 has_data=False 0720 wx.TheClipboard.Close() 0721 if has_data: 0722 self.OnAddFiles(file_names.GetFilenames()) 0723 0724 def CanPaste(self): 0725 """ Return True if can accept clipboard data, False otherwise 0726 """ 0727 if not wx.TheClipboard.Open(): 0728 return False 0729 r=wx.TheClipboard.IsSupported(wx.DataFormat(wx.DF_FILENAME)) 0730 wx.TheClipboard.Close() 0731 return r 0732 0733 def CanDelete(self): 0734 if len(self.GetSelectedItems()): 0735 return True 0736 return False 0737 0738 def OnDelete(self,_): 0739 items=self.GetSelectedItems() 0740 for item in items: 0741 item.RemoveFromIndex() 0742 self.OnRefresh() 0743 0744 def AddToIndex(self, file, origin, data, dict, timestamp=None, index=-1): 0745 # see if it is already in the origin 0746 if dict.has_key(self.database_key): 0747 for i in dict[self.database_key]: 0748 # see if the media is already in the database 0749 # if so update the existing entry, the display objects have references to the keys, if 0750 # we delete items the keys go bad and we require a refresh 0751 if dict[self.database_key][i].name==file and dict[self.database_key][i].origin==origin: 0752 # update the index 0753 if index>=0: 0754 dict[self.database_key][i].index=index 0755 # update the timestamp 0756 if timestamp!=None: 0757 dict[self.database_key][i].timestamp=timestamp 0758 # if there is no data dont update the dict 0759 # when indexes are updates the data is not provided 0760 if data!=None and data!='': 0761 dict[self.database_key][i].mediadata=data 0762 return 0763 else: 0764 dict[self.database_key]={} 0765 entry=MediaEntry() 0766 entry.name=file 0767 entry.origin=origin 0768 entry.mediadata=data 0769 entry.index=index 0770 entry.timestamp=timestamp 0771 dict[self.database_key][entry.id]=entry 0772 self.modified=True 0773 0774 def _fixupdb(self, db): 0775 # fixup the database to accommodate the new schema 0776 adjustflg=False 0777 for _,_name,_type in db.getcolumns(self.database_key): 0778 if _name=='mediadata' and _type!='indirectBLOB': 0779 # old schema, need to adjust 0780 adjustflg=True 0781 break 0782 # adjust the table: replace the mediadata field 0783 if adjustflg: 0784 db._altertable(self.database_key, [('mediadata', 'indirectBLOB')], 0785 ['mediadata'], 1) 0786 0787 def _gettempfile(self, item): 0788 # store the the media data in a temporary file and return the full file 0789 # path 0790 filename=os.path.join(tempfile.gettempdir(), 0791 item.name.encode(media_codec)) 0792 file(filename, 'wb').write(item.mediadata) 0793 return filename 0794 0795 def _filename(self, item): 0796 # return the filename associated with a media file 0797 if hasattr(item, 'origin'): 0798 _origin=item.origin 0799 _name=item.name 0800 else: 0801 _origin=item.get('origin', '') 0802 _name=item.get('name', '') 0803 relative_name=os.path.join(_origin, 0804 _name.encode(media_codec)) 0805 return os.path.join(self.mainwindow.blob_path, relative_name) 0806 0807 def _save_to_db(self, dict): 0808 db_rr={} 0809 for k,e in dict.items(): 0810 db_rr[k]=MediaDataObject(e) 0811 # the media data does not go into the actual database, we store it 0812 # in a regular file to minimise the database size, it 0813 # gets very big if the data is stored in it and starts to get slow. 0814 database.ensurerecordtype(db_rr, mediaobjectfactory) 0815 self.mainwindow.database.savemajordict(self.database_key, db_rr) 0816 0817 def _load_from_db(self, result): 0818 dict=self.mainwindow.database.\ 0819 getmajordictvalues(self.database_key, 0820 mediaobjectfactory) 0821 r={} 0822 for k,e in dict.items(): 0823 ce=MediaEntry() 0824 ce.set_db_dict(e) 0825 if ce.mediadata is None: 0826 # try reading the data from the "old" way 0827 try: 0828 ce.mediadata=file(self._filename(ce), 'rb').read() 0829 except: 0830 # abandon data that has no file it probably means that 0831 # blobs directory is corrupted, maybe the user deleted 0832 # the file themselves 0833 pass 0834 r[ce.id]=ce 0835 result.update({ self.database_key: r}) 0836 return result 0837 0838 def convert_to_dict(self, result, res=None): 0839 if res==None: 0840 res={} 0841 for rec in result[self.database_key]: 0842 fname=result[self.database_key][rec]['name'] 0843 # legacy format does not always contain origin 0844 if result[self.database_key][rec].has_key('origin'): 0845 origin=result[self.database_key][rec]['origin'] 0846 else: 0847 origin=self.default_origin 0848 data, timestamp=self.get_media_data(result, fname, origin) 0849 # None and '' are treated differently by viewer. data=='' means we could not read from 0850 # the phone but we still want to display, 0851 # None means we do not want to display like the builtin media 0852 if data=='': # no data read, see if we have this file in the dict already and use its data, 0853 # provides a workaround for phones that don't let us read the ringtones back 0854 # which we wrote in the first place 0855 for i in self._data[self.database_key]: 0856 if self._data[self.database_key][i].name==result[self.database_key][rec]['name'] \ 0857 and self._data[self.database_key][i].origin==result[self.database_key][rec]['origin'] \ 0858 and self._data[self.database_key][i].mediadata!=None: 0859 data=self._data[self.database_key][i].mediadata 0860 self.AddToIndex(result[self.database_key][rec]['name'], origin, data, res, timestamp, rec) 0861 return res 0862 0863 def get_media_data(self, result, name, origin): 0864 data=None 0865 timestamp=None # unix time 0866 if result.has_key(self.media_key): 0867 if result[self.media_key].has_key("new_media_version"): 0868 if result[self.media_key].has_key(origin): 0869 if result[self.media_key][origin].has_key(name): 0870 data=result[self.media_key][origin][name]['data'] 0871 if result[self.media_key][origin][name].has_key('timestamp'): 0872 timestamp=result[self.media_key][origin][name]['timestamp'] 0873 pass 0874 # old style data with no origin info, means that the filenames have 0875 # to be unique across all the origins for the widget 0876 elif result[self.media_key].has_key(name): 0877 data=result[self.media_key][name] 0878 return data, timestamp 0879 0880 def updateindex(self, index): 0881 self._data=self.convert_to_dict(index, self._data) 0882 # delete unused medias 0883 del_list=[] 0884 for i in self._data[self.database_key]: 0885 found=False 0886 for rec in index[self.database_key]: 0887 if self._data[self.database_key][i].name==index[self.database_key][rec]['name'] \ 0888 and self._data[self.database_key][i].origin==index[self.database_key][rec]['origin']: 0889 found=True 0890 break 0891 if not found: 0892 del_list.append(i) 0893 for i in del_list: 0894 del self._data[self.database_key][i] 0895 self.modified=True 0896 0897 def populatefs(self, dict): 0898 res={} 0899 dict=self.convert_to_dict(dict) 0900 return self._populatefs(dict) 0901 0902 def _populatefs(self, dict): 0903 self._save_to_db(dict.get(self.database_key, {})) 0904 return dict 0905 0906 def populate(self, dict): 0907 if not dict.has_key('media_from_db'): 0908 # update the existing dict (the key are referenced from the display objects, doing a refresh causes 0909 # a long delay 0910 self._data=self.convert_to_dict(dict, self._data) 0911 # delete unused medias 0912 del_list=[] 0913 for i in self._data[self.database_key]: 0914 found=False 0915 for rec in dict[self.database_key]: 0916 if self._data[self.database_key][i].name==dict[self.database_key][rec]['name'] \ 0917 and self._data[self.database_key][i].origin==dict[self.database_key][rec]['origin']: 0918 found=True 0919 break 0920 if not found: 0921 del_list.append(i) 0922 for i in del_list: 0923 del self._data[self.database_key][i] 0924 0925 self.modified=True 0926 self.OnRefresh() 0927 else: 0928 if dict[self.database_key]!=self._data[self.database_key]: 0929 self._data[self.database_key]=dict[self.database_key].copy() 0930 self.modified=True 0931 self.OnRefresh() 0932 0933 def getfromfs(self, result): 0934 if self.mainwindow.database.doestableexist(self.database_key): 0935 result=self._load_from_db(result) 0936 else: # if there is no data in the database then try to read the legacy media 0937 res={} 0938 res=self.legacygetfromfs(res, self.media_key, self.database_key, self.CURRENTFILEVERSION) 0939 # if we got anything save into the database and delete the old index file 0940 if res.has_key(self.database_key) and len(res[self.database_key])!=0: 0941 result.update(self.convert_to_dict(res)) 0942 self._populatefs(result) 0943 self.delete_old_media() 0944 else: 0945 result=self._load_from_db(result) 0946 result['media_from_db']=1 0947 return result 0948 0949 def legacygetfromfs(self, result, key, indexkey, currentversion): 0950 dict={} 0951 index_found=False 0952 if os.path.isdir(self.thedir): 0953 for file in os.listdir(self.thedir): 0954 if file=='index.idx': 0955 d={} 0956 d['result']={} 0957 common.readversionedindexfile(os.path.join(self.thedir, file), d, self.versionupgrade, currentversion) 0958 result.update(d['result']) 0959 index_found=True 0960 elif file.lower() in self.skiplist: 0961 # ignore windows detritus 0962 continue 0963 elif key is not None: 0964 dict[file.decode(media_codec)]=open(os.path.join(self.thedir, file), "rb").read() 0965 if index_found: 0966 if key is not None: 0967 result[key]=dict 0968 if indexkey not in result: 0969 result[indexkey]={} 0970 return result 0971 0972 def delete_old_media(self): 0973 # No longer do this since it messes up the virtual tables setup! 0974 pass 0975 0976 def OnDropFiles(self, _, dummy, filenames): 0977 # There is a bug in that the most recently created tab 0978 # in the notebook that accepts filedrop receives these 0979 # files, not the most visible one. We find the currently 0980 # viewed tab in the notebook and send the files there 0981 if self.__dragging: 0982 # I'm the drag source, forget 'bout it ! 0983 return 0984 target=self # fallback 0985 t=self._tree.mw.GetCurrentActiveWidget() 0986 if isinstance(t, FileView): 0987 # changing target in dragndrop 0988 target=t 0989 target.OnAddFiles(filenames) 0990 0991 def CanAdd(self): 0992 return True 0993 0994 def OnAdd(self, _=None): 0995 with guihelper.WXDialogWrapper(wx.FileDialog(self, "Choose files", style=wx.OPEN|wx.MULTIPLE, wildcard=self.wildcard), 0996 True) as (dlg, retcode): 0997 if retcode==wx.ID_OK: 0998 self.OnAddFiles(dlg.GetPaths()) 0999 1000 def CanRename(self): 1001 return len(self.GetSelectedItems())==1 1002 # subclass needs to define this 1003 media_notification_type=None 1004 def OnRename(self, _=None): 1005 items=self.GetSelectedItems() 1006 if len(items)!=1: 1007 # either none or more than 1 items selected 1008 return 1009 old_name=items[0].name 1010 with guihelper.WXDialogWrapper(wx.TextEntryDialog(self, "Enter a new name:", "Item Rename", 1011 old_name), 1012 True) as (dlg, retcode): 1013 if retcode==wx.ID_OK: 1014 new_name=dlg.GetValue() 1015 if len(new_name) and new_name!=old_name: 1016 items[0].name=new_name 1017 items[0].RenameInIndex(new_name) 1018 pubsub.publish(pubsub.MEDIA_NAME_CHANGED, 1019 data={ pubsub.media_change_type: self.media_notification_type, 1020 pubsub.media_old_name: old_name, 1021 pubsub.media_new_name: new_name }) 1022 1023 def OnAddFiles(self,_): 1024 raise NotImplementedError 1025 1026 def OnReplace(self, _=None): 1027 items=self.GetSelectedItems() 1028 if len(items)!=1: 1029 # either none or more than 1 items selected 1030 return 1031 with guihelper.WXDialogWrapper(wx.FileDialog(self, "Choose file", 1032 style=wx.OPEN, wildcard=self.wildcard), 1033 True) as (dlg, retcode): 1034 if retcode==wx.ID_OK: 1035 self.ReplaceContents(items[0].name, items[0].origin, dlg.GetPath()) 1036 items[0].Refresh() 1037 1038 def get_media_name_from_filename(self, filename, newext=''): 1039 path,filename=os.path.split(filename) 1040 # degrade to ascii 1041 degraded_fname=common.encode_with_degrade(filename, 'ascii', 'ignore') 1042 # decode with media codec in case it contains escape characters 1043 degraded_fname=degraded_fname.decode(media_codec) 1044 if not 'A' in self.filenamechars: 1045 degraded_fname=degraded_fname.lower() 1046 if not 'a' in self.filenamechars: 1047 degraded_fname=degraded_fname.upper() 1048 if len(newext): 1049 degraded_fname=stripext(degraded_fname) 1050 media_name="".join([x for x in degraded_fname if x in self.filenamechars]) 1051 media_name=media_name.replace(" "," ").replace(" ", " ") # remove double spaces 1052 if len(newext): 1053 media_name+='.'+newext 1054 if len(media_name)>self.maxlen: 1055 chop=len(media_name)-self.maxlen 1056 media_name=stripext(media_name)[:-chop].strip()+'.'+getext(media_name) 1057 return media_name 1058 1059 def getdata(self,dict,want=NONE): 1060 items=None 1061 media_index={} 1062 media_data={} 1063 old_dict={} 1064 data_key=0 1065 1066 if want==self.SELECTED: 1067 items=self.GetSelectedItems() 1068 if len(items)==0: 1069 want=self.ALL 1070 1071 if want==self.SELECTED: 1072 if items is not None: 1073 media_data={} 1074 i=0 1075 for item in items: 1076 me=self._data[item.datakey][item.key] 1077 if me.mediadata!=None: 1078 media_data[data_key]={'name': me.name, 'data': me.mediadata, 'origin': me.origin} 1079 data_key+=1 1080 1081 # convert into the old format 1082 index_cnt=-1 1083 for i in self._data[self.database_key]: 1084 me=self._data[self.database_key][i] 1085 # make sure the index is unique 1086 if me.index in media_index: 1087 while index_cnt in media_index: 1088 index_cnt-=1 1089 index=index_cnt 1090 else: 1091 index=me.index 1092 media_index[index]={'name': me.name, 'origin': me.origin} 1093 if want==self.ALL and me.mediadata!=None: 1094 media_data[data_key]={'name': me.name, 'data': me.mediadata, 'origin': me.origin} 1095 data_key+=1 1096 old_dict[self.database_key]=media_index 1097 dict.update(old_dict) 1098 dict[self.media_key]=media_data 1099 return dict 1100 1101 def CompareItems(self, a, b): 1102 s1=a.name.lower() 1103 s2=b.name.lower() 1104 if s1<s2: 1105 return -1 1106 if s1==s2: 1107 return 0 1108 return 1 1109 1110 def log(self, log_str): 1111 self.mainwindow.log(log_str) 1112 1113 def GetHelpID(self): 1114 return self.helpid 1115 1116 def OnMediaInfo(self, msg): 1117 # return the list of strings (lines) describing this item 1118 client, name, origin=msg.data 1119 for _item in self.GetAllItems(): 1120 if (origin is None or _item.origin==origin) and \ 1121 _item.name==name: 1122 pubsub.publish(pubsub.RESPONSE_MEDIA_INFO, 1123 { 'client': client, 1124 'canopen': bool(guihelper.GetOpenCommand(_item.mimetypes, _item.name)), 1125 'desc': _item.lines }) 1126 def OnMediaOpen(self, msg): 1127 # Launch the specified item name 1128 name, origin=msg.data 1129 for _item in self.GetAllItems(): 1130 if (origin is None or _item.origin==origin) and \ 1131 _item.name==name: 1132 return self._launch(_item) 1133 1134 class FileViewDisplayItem(object): 1135 1136 datakey="Someone forgot to set me" 1137 PADDING=3 1138 1139 def __init__(self, view, key): 1140 self.view=view 1141 self.key=key 1142 self.thumbsize=10,10 1143 self.setvals() 1144 self.lastw=None 1145 1146 def setvals(self): 1147 me=self.view._data[self.datakey][self.key] 1148 self.name=me.name 1149 self.origin=me.origin 1150 self.mimetypes='' 1151 self.short='' 1152 self.long='' 1153 self.timestamp='' 1154 self.thumb=None 1155 if me.mediadata!=None: 1156 self.size=len(me.mediadata) 1157 self.no_data=False 1158 if me.timestamp!=None and me.timestamp!=0: 1159 try: 1160 self.timestamp=time.strftime("%x %X", time.localtime(me.timestamp)) 1161 except: # unexplained errors sometimes, so skip timestamp if this fails 1162 self.timestamp='' 1163 fileinfo=self.view.GetFileInfoString(me.mediadata) 1164 if fileinfo!=None: 1165 self.short=fileinfo.shortdescription() 1166 self.long=fileinfo.longdescription() 1167 self.mimetypes=fileinfo.mimetypes 1168 self.fileinfo=fileinfo 1169 else: 1170 self.size=0 1171 self.no_data=True 1172 self.selbbox=None 1173 self.lines=[self.name, self.short, 1174 '%.1f kb' % (self.size/1024.0,)] 1175 1176 def Draw(self, dc, width, height, selected): 1177 if self.thumb==None: 1178 try: 1179 if self.size: 1180 me=self.view._data[self.datakey][self.key] 1181 self.thumb=self.view.GetItemThumbnail(me.mediadata, self.thumbnailsize[0], self.thumbnailsize[1], self.fileinfo) 1182 else: 1183 self.thumb=self.view.GetItemThumbnail(None, self.thumbnailsize[0], self.thumbnailsize[1]) 1184 except: 1185 self.thumb=self.view.GetItemThumbnail(None, self.thumbnailsize[0], self.thumbnailsize[1]) 1186 redrawbbox=False 1187 if selected: 1188 if self.lastw!=width or self.selbbox is None: 1189 redrawbbox=True 1190 else: 1191 oldb=dc.GetBrush() 1192 oldp=dc.GetPen() 1193 dc.SetBrush(self.view.item_selection_brush) 1194 dc.SetPen(self.view.item_selection_pen) 1195 dc.DrawRectangle(*self.selbbox) 1196 dc.SetBrush(oldb) 1197 dc.SetPen(oldp) 1198 dc.DrawBitmap(self.thumb, self.PADDING+self.thumbnailsize[0]/2-self.thumb.GetWidth()/2, self.PADDING, True) 1199 xoff=self.PADDING+self.thumbnailsize[0]+self.PADDING 1200 yoff=self.PADDING*2 1201 widthavailable=width-xoff-self.PADDING 1202 maxw=0 1203 old=dc.GetFont() 1204 for i,line in enumerate(self.lines): 1205 dc.SetFont(self.view.item_line_font[i]) 1206 w,h=DrawTextWithLimit(dc, xoff, yoff, line, widthavailable, self.view.item_guardspace, self.view.item_term) 1207 maxw=max(maxw,w) 1208 yoff+=h 1209 dc.SetFont(old) 1210 self.lastw=width 1211 self.selbbox=(0,0,xoff+maxw+self.PADDING,max(yoff+self.PADDING,self.thumb.GetHeight()+self.PADDING*2)) 1212 if redrawbbox: 1213 return self.Draw(dc, width, height, selected) 1214 return self.selbbox 1215 1216 def DisplayTooltip(self, parent, rect): 1217 res=["Name: "+self.name, "Origin: "+(self.origin, "default")[self.origin is None], 1218 'File size: %.1f kb (%d bytes)' % (self.size/1024.0, self.size), "\n"+self.datatype+" information:\n", self.long] 1219 # tipwindow takes screen coordinates so we have to transform 1220 x,y=parent.ClientToScreen(rect[0:2]) 1221 return wx.TipWindow(parent, "\n".join(res), 1024, wx.Rect(x,y,rect[2], rect[3])) 1222 1223 def RemoveFromIndex(self): 1224 del self.view._data[self.datakey][self.key] 1225 self.view.modified=True 1226 self.view.OnRefresh() 1227 1228 def RenameInIndex(self, new_name): 1229 self.view._data[self.datakey][self.key].name=new_name 1230 self.view.modified=True 1231 self.view.OnRefresh() 1232 1233 def ChangeOriginInIndex(self, new_origin): 1234 self.view._data[self.datakey][self.key].origin=new_origin 1235 self.view._data[self.datakey][self.key].index=-1 1236 self.view.modified=True 1237 self.view.OnRefresh() 1238 1239 def Refresh(self): 1240 self.setvals() 1241 self.view.modified=True 1242 self.view.OnRefresh() 1243
Generated by PyXR 0.9.4