00001 from __future__ import with_statement
00002
00003 import gtk,gtk.glade,gobject
00004 import pickle
00005
00006 import sqlite3,time,sys
00007
00008 from plai import config
00009 from plai.track import Track
00010 from plai import popup
00011
00012
00013 class CollectionTree:
00014 """The tree on the left side of the window.
00015 Contains tracks grouped by artist then album."""
00016 def __init__(self,widget):
00017 """Pass me a gtk.TreeView to be in."""
00018 self._view_view=widget
00019
00020 self._view_view.set_headers_visible(False)
00021
00022 self._view_view.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
00023
00024 self._store_store=gtk.TreeStore(gobject.TYPE_STRING,gobject.TYPE_PYOBJECT)
00025 self._view_view.set_model(self._store_store)
00026
00027 renderer=gtk.CellRendererText()
00028 column=gtk.TreeViewColumn('',renderer,text=0)
00029
00030 self._view_view.append_column(column)
00031
00032 targets=[('foo',gtk.TARGET_SAME_APP,42)]
00033 self._view_view.enable_model_drag_source(gtk.gdk.BUTTON1_MASK,targets,gtk.gdk.ACTION_COPY)
00034 self._view_view.connect('drag-data-get',self._DragData_DragData)
00035
00036
00037
00038
00039
00040
00041
00042
00043 self._querypattern_querypattern='%'
00044 self.RunQueryRunQuery('')
00045
00046 self._view_view.connect('button-press-event',self._onClick_onClick)
00047 self._view_view.connect('row-expanded',self._onExpand_onExpand)
00048
00049
00050 def _executeSQL(self,query,params):
00051 """Run the SQL 'query' with dictionary 'params' substituted in.
00052 Returns the database object, which you can iterate over."""
00053 con=sqlite3.Connection(config.sqlfname)
00054
00055 con.text_factory = lambda x: unicode(x,'latin1')
00056 db=con.cursor()
00057 db.row_factory=sqlite3.Row
00058 db.execute(query,params)
00059 return db
00060
00061
00062 def RunQuery(self,text):
00063 """Query the database for tracks matching text and display the results."""
00064
00065 self._querypattern_querypattern='%'+text+'%'
00066
00067 self._store_store.clear()
00068
00069 for onesong in [True,False]:
00070
00071 query='select distinct sortartist,artist from collection where '\
00072 '(artist like :pattern or album like :pattern or title like :pattern) '\
00073 'and rowid '+('' if onesong else 'not ')+'in (select track from onesongs) '\
00074 'order by sortartist,length(artist) desc'
00075
00076 params={'pattern':self._querypattern_querypattern}
00077
00078
00079
00080 results=[x for x in self._executeSQL_executeSQL(query,params)]
00081
00082 parent=None
00083 if onesong and results!=[]:
00084 parent=self._store_store.append(None,('(one song)','onesong'))
00085
00086 lastsortartist=None
00087
00088 for result in results:
00089 if result['sortartist']!=lastsortartist:
00090 lastsortartist=result['sortartist']
00091 it=self._store_store.append(parent,(result['artist'],'artist'))
00092
00093 self._store_store.append(it,(None,result['sortartist']))
00094
00095
00096 def _appendexpanded(self,its,ret):
00097 """Helper function called from GetSelectedTracks. Recurses through the tree."""
00098 for it in its:
00099 if self._store_store.iter_n_children(it)==0:
00100 ret.append(it)
00101 else:
00102 self._FillInTreeBelow_FillInTreeBelow(it)
00103 newits=[]
00104 for n in range(0,self._store_store.iter_n_children(it)):
00105 newits.append(self._store_store.iter_nth_child(it,n))
00106 self._appendexpanded_appendexpanded(newits,ret)
00107
00108
00109 def GetSelectedTracks(self):
00110 """Returns a list of track objects representing what is selected."""
00111 (junk,paths)=self._view_view.get_selection().get_selected_rows()
00112 its=[self._store_store.get_iter(p) for p in paths]
00113
00114 exp=[]
00115 self._appendexpanded_appendexpanded(its,exp)
00116
00117 return [self._store_store.get_value(it,1) for it in exp]
00118
00119
00120 def _DragData(self,widget,context,sel,info,time):
00121 """Callback for drag and drop. Represents the drag data as a pickled
00122 list of Track objects."""
00123 sel.set(sel.target,8,pickle.dumps(self.GetSelectedTracksGetSelectedTracks(),pickle.HIGHEST_PROTOCOL))
00124
00125
00126 def _onClick(self,widget,event):
00127 """Handler for clicks on collection. Responds to right-click by
00128 popping-up the context menu."""
00129 if event.button==3:
00130 p=self._view_view.get_path_at_pos(int(event.x),int(event.y))
00131 if p:
00132 (path,col,x,y)=p
00133 it=self._store_store.get_iter(path)
00134 istrack=self._store_store.iter_n_children(it)==0
00135 if istrack:
00136 track=self._store_store.get_value(it,1)
00137 popup.ShowPopup(event,track)
00138
00139
00140 def _onExpand(self,view,it,path):
00141 """Callback for expanding a row in the tree."""
00142 self._FillInTreeBelow_FillInTreeBelow(it)
00143
00144
00145 def _FillInTreeBelow(self,it):
00146 """Call me if you want to be sure that that the contents of the store
00147 beneath 'it' exist."""
00148
00149
00150 if self._store_store.get_value(self._store_store.iter_children(it),0): return
00151
00152 nodetype=self._store_store.get_value(it,1)
00153
00154
00155 sortartist=self._store_store.get_value(self._store_store.iter_children(it),1)
00156 if nodetype=='album': album=self._store_store.get_value(it,0)
00157
00158 if nodetype=='artist':
00159 query='select distinct album from collection where \
00160 sortartist==:sortartist and (artist like :pattern or album like :pattern or title like :pattern)\
00161 order by album'
00162 params={'sortartist':sortartist,'pattern':self._querypattern_querypattern}
00163 else:
00164 query='select fname,title,artist,length from collection where \
00165 sortartist==:sortartist and album==:album and (artist like :pattern or album like :pattern or title like :pattern)\
00166 order by fname'
00167 params={'album':album,'sortartist':sortartist,'pattern':self._querypattern_querypattern}
00168
00169 for result in self._executeSQL_executeSQL(query,params):
00170 if nodetype=='artist':
00171 child=self._store_store.append(it,(result['album'],'album'))
00172 self._store_store.append(child,(None,sortartist))
00173 else:
00174 self._store_store.append(it,(result['title'],Track(result['fname'],result['title'],result['artist'],album,result['length'])))
00175
00176
00177 self._store_store.remove(self._store_store.iter_children(it))
00178
00179
00180