Split the playlist in a generic songlist and playlist.
[python-gnt.git] / example / xmms / xmms_list.py
blob2da793bb116af05e3a18e423d3e29ce3d205c006
1 #!/usr/bin/env python
3 """
4 xmms-list : A generic widget for a list of songs.
6 Copyright (C) 2007 Sadrul Habib Chowdhury <sadrul@users.sourceforge.net>
8 This application is free software; you can redistribute it and/or
9 modify it under the terms of the GNU Lesser General Public
10 License as published by the Free Software Foundation; either
11 version 2.1 of the License, or (at your option) any later version.
13 This application is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public
19 License along with this application; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301
21 USA
22 """
24 import xmmsclient
25 import xmmsclient.glib as xg
26 import xmmsclient.collections as xc
27 import gobject
28 import gnt
29 import os
30 import sys
31 import itertools
33 import treerow
34 import songedit
35 import common
37 reload(sys)
38 sys.setdefaultencoding("utf-8")
40 class XList(gnt.Tree):
41 """Generic Song list."""
42 POSITION = 0
43 TITLE = 1
44 ARTIST = 2
45 ALBUM = 3
46 TIME = 4
47 COLUMNS = 5
49 __gntbindings__ = {
50 'edit-entry' : ('edit_entry', 'e'),
51 'edit-columns' : ('edit_columns', 'C'),
52 'search-column' : ('search_column', 's'),
55 def __init__(self, xmms):
56 """The xmms connection, and the name of the playlist."""
57 gnt.Tree.__init__(self)
58 self.xmms = xmms
59 self.init_tree()
60 self.needupdates = [] # A list of medialib id's which we need to poke information for
61 self.columns = 0xffff
62 self.searchc = self.TITLE
63 self.set_search_column(self.searchc)
65 def show_column(self, col):
66 """Show the 'col'th column"""
67 if self.columns & (1 << col): return
68 self.columns = self.columns | (1 << col)
69 self.set_column_visible(col, True)
70 self.draw()
72 def hide_column(self, col):
73 """Hide the 'col'th column"""
74 if not (self.columns & (1 << col)): return
75 self.columns = self.columns ^ (1 << col)
76 self.set_column_visible(col, False)
77 self.draw()
79 def toggle_column(self, col):
80 """Toggle the visibility of the 'col'th column"""
81 if self.columns & (1 << col):
82 self.hide_column(col)
83 else:
84 self.show_column(col)
86 def got_song_details(self, result):
87 """This is the callback of medialib_get_info. This updates the information about a song in the playlist."""
88 info = result.value()
89 id = info.get('id', None)
90 if not id:
91 return
93 [title, artist, album, time] = common.str_info(info)
95 for row in self.get_rows():
96 mlib = row.get_data('song-id')
97 if mlib != id: continue
98 row.set_data('song-info', info)
99 self.change_text(row, self.ARTIST, artist)
100 self.change_text(row, self.ALBUM, album)
101 self.change_text(row, self.TITLE, title)
102 self.change_text(row, self.TIME, time)
104 def update_info(self):
105 # Update information about some songs, if necessary
106 for song in self.needupdates:
107 # song is the medialib id
108 self.xmms.medialib_get_info(song, self.got_song_details)
109 self.needupdates = []
110 return False # That's it! We're done!!
112 def queue_update(self, song):
113 """Queue request to update information about a song."""
114 # Instead of immediately updating information about the song,
115 # we wait a little. This is to avoid having to request info
116 # about the song multiple times when more than one property
117 # of the song is changed.
118 if len(self.needupdates) == 0:
119 gobject.timeout_add(500, self.update_info)
120 self.needupdates.append(song)
122 def init_tree(self):
123 self.set_property('columns', self.COLUMNS)
125 self.set_show_title(True)
127 # Position
128 self.set_column_title(self.POSITION, "#")
129 self.set_column_is_right_aligned(self.POSITION, True)
130 self.set_col_width(self.POSITION, 5)
131 self.set_column_resizable(self.POSITION, False)
133 # Title
134 self.set_column_title(self.TITLE, "Title")
135 self.set_col_width(self.POSITION, 20)
137 # Artist
138 self.set_column_title(self.ARTIST, "Artist")
139 self.set_col_width(self.ARTIST, 20)
141 # Album
142 self.set_column_title(self.ALBUM, "Album")
143 self.set_col_width(self.ALBUM, 20)
145 # Time
146 self.set_column_title(self.TIME, "Time")
147 self.set_col_width(self.TIME, 5)
148 self.set_column_resizable(self.TIME, False)
150 # Make sure that if some metadata of a song changes, we update the playlist accordingly
151 def medialib_entry_changed(result):
152 # song is the medialib id of the song
153 song = result.value()
154 if song not in self.needupdates:
155 self.queue_update(song)
156 self.xmms.broadcast_medialib_entry_changed(medialib_entry_changed)
158 def edit_entry(self, null):
159 """Edit medialib information about the selected entry."""
160 sel = self.get_selection_data()
161 if not sel:
162 return
163 info = sel.get_data('song-info')
164 if not info:
165 common.show_error("Do not have any editable information about this song.")
166 return
167 edit = songedit.SongEdit(self.xmms)
168 win = edit.get_widget()
169 edit.set_info(info)
170 win.set_toplevel(True)
171 win.set_title("Edit Song Information")
172 win.show()
174 def add_entry(self, null):
175 """Add new song(s) in the playlist."""
176 fl = gnt.FileSel()
177 fl.set_multi_select(True)
178 def destroy_fl(b, dlg):
179 dlg.destroy()
180 fl.cancel_button().connect('activate', destroy_fl, fl)
181 def add_files(fl, path, files, dlg):
182 for file in dlg.get_selected_multi_files():
183 self.xmms.playlist_add_url('file://' + file, self.name)
184 dlg.destroy()
185 fl.connect('file_selected', add_files, fl)
186 fl.show()
188 def del_entry(self, null):
189 """Delete selected entry from the playlist."""
190 sel = self.get_selection_data()
191 if not sel:
192 return
193 index = self.get_rows().index(sel)
194 self.xmms.playlist_remove_entry(index, self.name)
196 def position_menu(self, menu):
197 """Position a menu so that it appears near the selected row in the tree."""
198 x, y = self.get_position()
199 width, height = self.get_size()
200 menu.set_position(x + width, y + self.get_selection_visible_line() + 3)
202 def desc_columns(self):
203 """Describe the columns in the tree."""
204 return (("Position", self.POSITION), ("Title", self.TITLE), ("Artist", self.ARTIST),
205 ("Album", self.ALBUM), ("Time", self.TIME))
207 def edit_columns(self, null):
208 """Change visibility of the columns."""
209 menu = gnt.Menu(gnt.MENU_POPUP)
210 def toggle_flag(item):
211 flag = item.get_data('column')
212 self.toggle_column(flag)
213 for text, col in self.desc_columns():
214 item = gnt.MenuItemCheck("Show " + text)
215 item.set_data('column', col)
216 if self.columns & (1 << col):
217 item.set_checked(True)
218 item.connect('activate', toggle_flag)
219 menu.add_item(item)
220 self.position_menu(menu)
221 gnt.show_menu(menu)
223 def search_column(self, null):
224 """Set the column to use for type-ahead search."""
225 def change_search(item):
226 col = item.get_data('column')
227 self.set_search_column(col)
228 self.searchc = col
229 menu = gnt.Menu(gnt.MENU_POPUP)
230 for text, col in self.desc_columns():
231 item = gnt.MenuItemCheck("Search " + text)
232 item.set_data('column', col)
233 item.connect('activate', change_search)
234 if col == self.searchc:
235 item.set_checked(True)
236 menu.add_item(item)
237 self.position_menu(menu)
238 gnt.show_menu(menu)
240 def got_song_details(self, result):
241 """This is the callback of medialib_get_info. This updates the information about a song in the playlist."""
242 info = result.value()
243 id = info.get('id', None)
244 if not id:
245 return
247 [title, artist, album, time] = common.str_info(info)
249 rows = self.get_rows()
250 if not rows:
251 return
253 for row in rows:
254 mlib = row.get_data('song-id')
255 if mlib != id: continue
256 row.set_data('song-info', info)
257 self.change_text(row, self.ARTIST, artist)
258 self.change_text(row, self.ALBUM, album)
259 self.change_text(row, self.TITLE, title)
260 self.change_text(row, self.TIME, time)
262 def add_mlib_song(self, position, mlibid):
263 position = int(position) - 1
264 def add_row_first(result):
265 row = treerow.Row()
266 row.set_data('song-id', mlibid)
267 rows = self.get_rows()
268 after = None
269 if not rows:
270 rows = []
271 if rows is not None and len(rows) >= position > 0:
272 after = rows[position - 1]
273 # Add the entry with empty information first. Then request the data
274 self.add_row_after(row, [str(position + 1), "", "", "", ""], None, after)
275 self.got_song_details(result)
276 self.xmms.medialib_get_info(mlibid, add_row_first)
278 gobject.type_register(XList)
279 gnt.register_bindings(XList)