playlist.py

Go to the documentation of this file.
00001 import gtk,gobject,pango,gtk.glade
00002 import random,pickle
00003 
00004 from plai.track import Track
00005 from plai import config
00006 from plai import popup
00007 from plai import utility
00008 
00009 _trackcol=4             # where to look for the track object in the treestore
00010                 
00011 class Playlist:
00012         """One of the playlists in tabs on the right of the window."""
00013         def __init__(self,widget):
00014                 """Pass me with a gtk.ListView to be in."""
00015                 self._view_view=widget
00016                 self._store_store=gtk.ListStore(gobject.TYPE_STRING,     #artist
00017                                                                   gobject.TYPE_STRING,   #title
00018                                                                   gobject.TYPE_STRING,   #album
00019                                                                   gobject.TYPE_STRING,   #length (string so we can render it pretty)
00020                                                                   gobject.TYPE_PYOBJECT) #track
00021                 self._view_view.set_model(self._store_store)
00022                 self._view_view.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
00023                 renderer=gtk.CellRendererText()
00024                 renderer.set_property("ellipsize",pango.ELLIPSIZE_END)
00025                 titles=['Artist','Title','Album','Length']
00026                 for t in range(0,len(titles)):
00027                         column=gtk.TreeViewColumn(titles[t],renderer,text=t)
00028                         column.set_cell_data_func(renderer,self._FillAndHighlight_FillAndHighlight,0);
00029                         if t!=3: column.set_expand(True)
00030                         self._view_view.append_column(column)
00031                         
00032                 self._curtrackref_curtrackref=None
00033                 
00034                 targets=[('foo',gtk.TARGET_SAME_APP,42)]
00035                 self._view_view.enable_model_drag_dest(targets,gtk.gdk.ACTION_COPY|gtk.gdk.ACTION_MOVE)
00036                 self._view_view.connect('drag-data-received',self._DragReceive_DragReceive)
00037                 
00038                 self._view_view.enable_model_drag_source(gtk.gdk.BUTTON1_MASK,targets,gtk.gdk.ACTION_MOVE)
00039                 self._view_view.connect('drag-data-get',self._DragData_DragData)
00040                 
00041                 self._view_view.connect('button-press-event',self._onClick_onClick)
00042                                 
00043                         
00044         def GetCurrentTrack(self):
00045                 """Returns the current (highlighted) Track object if it
00046                 exists, None otherwise."""
00047                 if self._curtrackref_curtrackref==None or not self._curtrackref_curtrackref.valid(): return None
00048                 return self._store_store.get_value(self._store_store.get_iter(self._curtrackref_curtrackref.get_path()),_trackcol)
00049                 
00050                 
00051         def SetCurrentTrack(self,it):
00052                 """Sets the current track to the one pointed to by the gtk iterator 'it'.
00053                 'it' may be None."""
00054                 if self.GetCurrentTrackGetCurrentTrack(): self.GetCurrentTrackGetCurrentTrack().highlight=False
00055                 
00056                 if it==None: 
00057                         self._curtrackref_curtrackref=None
00058                 else:
00059                         self._curtrackref_curtrackref=gtk.TreeRowReference(self._store_store,self._store_store.get_path(it))
00060                         self.GetCurrentTrackGetCurrentTrack().highlight=True
00061                 self._Refresh_Refresh()
00062                 
00063                 
00064         def Shuffle(self):
00065                 """Removes everything from the playlist and puts it back in a random order."""
00066                 tracks=[]
00067                 for row in self._store_store:
00068                         tracks.append(row[_trackcol])
00069                 random.shuffle(tracks)
00070                 self._view_view.set_model(None) #for speed
00071                 self._store_store.clear()
00072                 for t in tracks:
00073                         it=self._store_store.append()
00074                         self._store_store.set(it,_trackcol,t)
00075                 self._view_view.set_model(self._store_store) #undo speed hack
00076                 
00077                 
00078         def _Refresh(self):
00079                 """Forces a redraw (GTK doesn't always know we need one)."""
00080                 self._view_view.queue_draw()
00081                 
00082                 
00083         def GotoNextTrack(self):
00084                 """Moves onto the next track in the playlist if possible.
00085                 Returns a boolean indicating success."""
00086                 if self._curtrackref_curtrackref==None or not self._curtrackref_curtrackref.valid(): return False
00087                 curit=self._store_store.get_iter(self._curtrackref_curtrackref.get_path())
00088                 nextit=self._store_store.iter_next(curit)
00089                 if nextit:
00090                         self.SetCurrentTrackSetCurrentTrack(nextit)
00091                         return True
00092                 else:
00093                         return False
00094         
00095                 
00096         def _FillAndHighlight(self,col,renderer,model,it,data):
00097                 """Private, for use by the renderer to extract text from the Track object
00098                 and draw the current track highlighted."""
00099                 (track,)=model.get(it,_trackcol)
00100         
00101                 coltitle=col.get_title()
00102 
00103                 if coltitle=='Title':   renderer.set_property('text',track.Title())
00104                 if coltitle=='Album':   renderer.set_property('text',track.Album())
00105                 if coltitle=='Artist':  renderer.set_property('text',track.Artist())
00106                 if coltitle=='Length':
00107                         l=track.Length()
00108                         txt=utility.PrettyPrintTime(l) if l!=-999 else ''
00109                         renderer.set_property('text',txt)
00110 
00111                 if track.highlight:
00112                         renderer.set_property('foreground','red')                       
00113                         renderer.set_property('weight',800)                     
00114                 else:
00115                         renderer.set_property('foreground',None)
00116                         renderer.set_property('weight',400)     
00117                         
00118                                         
00119         def ScrollToCurrent(self):
00120                 """Scroll the playlist until the current track is 1/3 of the way down."""
00121                 self._view_view.scroll_to_cell(self._curtrackref_curtrackref.get_path(),None,True,0.3)
00122 #               """Do the minimum amount of scrolling to make the current track visible."""
00123 #               self._view.scroll_to_cell(self._curtrackref.get_path()) #lazy scrolling
00124         
00125         
00126         def AppendTracks(self,tracks):  
00127                 """Appends 'tracks' to the playlist. Returns an iterator to
00128                 the first Track appended."""
00129                                 
00130                 retit=None
00131                 #while this may well speed up the track appending, it makes it look less cool
00132                 self._view_view.set_model(None) #for speed
00133                 for t in tracks:
00134 #                       while gtk.events_pending(): gtk.main_iteration() #try not to block the GUI
00135                         
00136                         it=self._store_store.append()
00137                         if retit==None: retit=it
00138                         self._store_store.set(it,_trackcol,t)
00139                 self._view_view.set_model(self._store_store) #undo speed hack
00140                 return retit
00141                 
00142                 
00143         def _DragReceive(self,widget,context,x,y,sel,info,time):
00144                 """Callback for drag and drop. Expects drag data to be a pickled
00145                 list of Track objects, as provided by CollectionTree.DragData()
00146                 or by self.DragData()."""
00147                 data=pickle.loads(sel.data)
00148                 pathpos=widget.get_dest_row_at_pos(x,y)
00149                 if pathpos:
00150                         (path,pos)=pathpos
00151                         it=self._store_store.get_iter(path)
00152                         if (pos==gtk.TREE_VIEW_DROP_BEFORE 
00153                          or pos==gtk.TREE_VIEW_DROP_INTO_OR_BEFORE):
00154                                 firstit=self._store_store.insert_before(it)
00155                         else:
00156                                 firstit=self._store_store.insert_after(it)
00157                 else:
00158                         firstit=self._store_store.append()
00159                         
00160                 self._store_store.set(firstit,_trackcol,data[0])
00161                 nextit=firstit
00162                 if len(data)>1:
00163                         for n in range(1,len(data)):
00164                                 nextit=self._store_store.insert_after(nextit)
00165                                 self._store_store.set(nextit,_trackcol,data[n])
00166                                 
00167                 if context.action==gtk.gdk.ACTION_MOVE:
00168                         context.finish(True,True,time)                  
00169         
00170                                 
00171         def GetSelectedTracks(self):
00172                 """Returns a list of Track objects representing the selected tracks."""
00173                 (junk,paths)=self._view_view.get_selection().get_selected_rows()
00174                 its=[self._store_store.get_iter(p) for p in paths]
00175                 return [self._store_store.get_value(it,_trackcol) for it in its]
00176                 
00177                 
00178         def _DragData(self,widget,context,sel,info,time):
00179                 """Callback for drag and drop. Provides a pickled list of Track objects."""
00180                 sel.set(sel.target,8,pickle.dumps(self.GetSelectedTracksGetSelectedTracks(),pickle.HIGHEST_PROTOCOL))
00181                 
00182         
00183         def GetState(self):
00184                 """Returns a representation of the playlist's state.
00185                 Form is currently ([tracks],currentpath)."""
00186                 tracks=[row[_trackcol] for row in self._store_store]
00187                 path=self._curtrackref_curtrackref.get_path() if self._curtrackref_curtrackref else None
00188                 return (tracks,path)
00189                 
00190                 
00191         def SetState(self,state):
00192                 """Initialises playlist to the state returned by GetState()."""
00193                 (tracks,path)=state
00194                 self.AppendTracksAppendTracks(tracks)
00195                 if path:
00196                         self.SetCurrentTrackSetCurrentTrack(self._store_store.get_iter(path))
00197                         self.ScrollToCurrentScrollToCurrent()
00198                         
00199                 self._view_view.set_sensitive(True)     # now fully initialised
00200         
00201         
00202         def RemoveSelected(self):
00203                 """Removes the currently selected tracks from the playlist.
00204                 Returns whether the currently playing track was removed."""
00205                 wascurrent=False
00206                 (junk,paths)=self._view_view.get_selection().get_selected_rows()
00207         # This assumes paths are in order.  Although it works there is no such
00208         # guarantee in Gtk+ API docs, so maybe a sort is necessary.
00209                 paths.reverse()
00210                 for (p,) in paths:
00211                         #TODO: find a neater way to test for this case
00212                         if self.GetCurrentTrackGetCurrentTrack() and (p,)==self._curtrackref_curtrackref.get_path():
00213                                 wascurrent=True
00214                         self._store_store.remove(self._store_store.get_iter(p))
00215                         
00216                 return wascurrent
00217                 
00218                 
00219         def _onClick(self,widget,event):
00220                 """Handler for clicks on playlist. Responds to right-click by
00221                 popping-up the context menu."""
00222                 if event.button==3:
00223                         p=self._view_view.get_path_at_pos(int(event.x),int(event.y))
00224                         if p:
00225                                 (path,col,x,y)=p
00226                                 track=self._store_store.get_value(self._store_store.get_iter(path),_trackcol)
00227                                 popup.ShowPopup(event,track)
00228                 

Generated on Mon Aug 6 21:24:20 2007 for plai by  doxygen 1.5.1