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
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,
00017 gobject.TYPE_STRING,
00018 gobject.TYPE_STRING,
00019 gobject.TYPE_STRING,
00020 gobject.TYPE_PYOBJECT)
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)
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)
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
00123
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
00132 self._view_view.set_model(None)
00133 for t in tracks:
00134
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)
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)
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
00208
00209 paths.reverse()
00210 for (p,) in paths:
00211
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