Get the name of the active playlist at startup.
[python-gnt.git] / example / xmms / xmms_pl.py
blob75a68b0758dd8b4d44f023fcc67e9c4221ae00de
1 #!/usr/bin/env python
3 """
4 xmms-pl : Playlist explorer for XMMS2.
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
36 from xmms_list import *
38 __version__ = "0.0.1alpha"
39 __author__ = "Sadrul Habib Chowdhury <sadrul@users.sourceforge.net>"
40 __copyright__ = "Copyright 2007, Sadrul Habib Chowdhury"
41 __license__ = "GPL"
43 reload(sys)
44 sys.setdefaultencoding("utf-8")
46 class XPList(XList):
47 __gproperties__ = {
48 'name' : (gobject.TYPE_STRING, 'name of the playlist',
49 'name of the playlist', None, gobject.PARAM_READWRITE)
52 __gntbindings__ = {
53 'add-entry' : ('add_entry', gnt.KEY_INS),
54 'del-entry' : ('del_entry', gnt.KEY_DEL),
55 'switch-playlist' : ('switch_playlist', 'w'),
56 'create-playlist' : ('create_playlist', 'c'),
57 'place-tagged' : ('place_tagged', gnt.KEY_CTRL_P),
58 'clear-playlist' : ('clear_playlist', gnt.KEY_CTRL_U),
61 def __init__(self, xmms, name = None):
62 """The xmms connection, and the name of the playlist."""
63 XList.__init__(self, xmms)
64 self.name = name
65 self.win = None
66 self.init_playlist()
67 self.connect('context-menu', self.context_menu)
69 def do_get_property(self, prop):
70 if prop.name == 'name':
71 return self.name
72 raise AttributeError, 'unknown property %s' % prop.name
74 def do_set_property(self, prop, value):
75 if prop.name == 'name':
76 self.name = value
77 else:
78 raise AttributeError, 'unknown property %s' % prop.name
80 def context_menu(self, null):
81 """Popup a context menu with the available options."""
82 def perform_action(item, data):
83 # This is a little weird, so pay attention ...
84 # The callback can either bring out a new window, or a new menu.
85 # In the first scenario, we want the new window to be given focus.
86 # So we perform the action immediately.
87 # In the latter case, we need to first let the active menu hide,
88 # then perform the action, so we call the callback in the next
89 # iteration of the mainloop.
90 action, wait = data
91 def really_perform_action():
92 action(None)
93 return False
94 if wait:
95 gobject.timeout_add(0, really_perform_action)
96 else:
97 action(None)
98 menu = gnt.Menu(gnt.MENU_POPUP)
99 for text, action, wait in (('Edit Info', self.edit_entry, False),
100 ('Remove', self.del_entry, False),
101 ('Add Files...', self.add_entry, False),
102 ('Edit Columns ...', self.edit_columns, True),
103 ('Set Search Column ...', self.search_column, True),
104 ('Switch Playlist...', self.switch_playlist, True)
106 item = gnt.MenuItem(text)
107 item.connect('activate', perform_action, [action, wait])
108 menu.add_item(item)
109 self.position_menu(menu)
110 gnt.show_menu(menu)
112 def init_playlist(self):
113 self.load_playlist()
115 # Refresh the playlist if an entry is added/removed etc.
116 def refresh_playlist(result):
117 info = result.value()
118 if info['name'] != self.name: return # This playlist didn't change
119 if info['type'] == xmmsclient.PLAYLIST_CHANGED_REMOVE:
120 # Some entry was removed
121 rows = self.get_rows()
122 row = rows[info['position']]
123 self.remove(row)
124 elif info['type'] == xmmsclient.PLAYLIST_CHANGED_ADD or info['type'] == xmmsclient.PLAYLIST_CHANGED_INSERT:
125 # Some entry was added
126 position = info['position']
127 self.add_mlib_song(int(position), info['id'])
128 elif info['type'] == xmmsclient.PLAYLIST_CHANGED_MOVE:
129 old = info['position']
130 new = info['newposition']
131 # First, remove the entry
132 rows = self.get_rows()
133 row = rows[old]
134 self.remove(row)
135 # Now, find the new entry position
136 rows = self.get_rows()
137 if new > 0:
138 after = rows[new - 1]
139 else:
140 after = None
141 info = row.get_data('song-info')
142 [title, artist, album, time] = common.str_info(info)
143 # Add it back in the new position
144 self.add_row_after(row, [str(new + 1), title, artist, album, time], None, after)
145 elif info['type'] == xmmsclient.PLAYLIST_CHANGED_CLEAR:
146 self.remove_all()
147 self.draw()
148 else:
149 sys.stderr.write("Unhandled playlist update type " + str(info['type']) + " -- Please file a bug report.\n")
150 # XXX: just go ahead and refresh the entire list
151 self.load_playlist()
152 return
153 # Make sure the entry in the 'position' column is correct for the entries
154 rows = self.get_rows()
155 if rows is not None:
156 num = 1
157 for row in rows:
158 self.change_text(row, self.POSITION, str(num))
159 num = num + 1
160 self.xmms.broadcast_playlist_changed(refresh_playlist)
162 def load_playlist(self):
163 """Get the entries in the list, and populate the tree."""
164 def got_list_of_songs_in_pl(result):
165 list = result.value()
166 pos = itertools.count(1)
167 for song in list:
168 self.add_mlib_song(int(pos.next()), song)
169 if self.win:
170 self.win.set_title("XMMS2 Playlist - " + str(self.name))
171 self.win.draw()
172 self.remove_all()
173 self.draw()
174 self.xmms.playlist_list_entries(self.name, got_list_of_songs_in_pl)
176 def show(self):
177 """Show the playlist inside a window."""
178 win = self.win = gnt.Box(homo = False, vert = True)
179 win.set_toplevel(True)
180 win.set_title("XMMS2 Playlist - " + str(self.name))
181 win.add_widget(self)
182 width, height = gnt.screen_size()
183 self.set_size(width, height)
184 win.show()
186 def add_entry(self, null):
187 """Add new song(s) in the playlist."""
188 fl = gnt.FileSel()
189 fl.set_multi_select(True)
190 def destroy_fl(b, dlg):
191 dlg.destroy()
192 fl.cancel_button().connect('activate', destroy_fl, fl)
193 def add_files(fl, path, files, dlg):
194 for file in dlg.get_selected_multi_files():
195 self.xmms.playlist_add_url('file://' + file, self.name)
196 dlg.destroy()
197 fl.connect('file_selected', add_files, fl)
198 fl.show()
200 def del_entry(self, null):
201 """Delete selected entry from the playlist."""
202 sel = self.get_selection_data()
203 if not sel:
204 return
205 index = self.get_rows().index(sel)
206 self.xmms.playlist_remove_entry(index, self.name)
208 def switch_playlist(self, null):
209 """Switch the playlist to show in the list."""
210 def show_list_menu(res):
211 def _load_playlist(item):
212 name = item.get_data('playlist-name')
213 self.set_property('name', name)
214 self.load_playlist()
215 list = res.value()
216 menu = gnt.Menu(gnt.MENU_POPUP)
217 for pl in list:
218 name = str(pl)
219 item = gnt.MenuItemCheck(name)
220 if name == self.name:
221 item.set_checked(True)
222 item.set_data('playlist-name', name)
223 item.connect('activate', _load_playlist)
224 menu.add_item(item)
225 self.position_menu(menu)
226 gnt.show_menu(menu)
227 self.xmms.playlist_list(show_list_menu)
228 return True
230 def create_playlist(self, null):
231 """Create a new playlist."""
232 win = gnt.Window()
233 win.set_title('Create Playlist')
234 win.set_fill(False)
235 win.set_alignment(gnt.ALIGN_MID)
236 entry = gnt.Entry('')
237 button = gnt.Button('Create')
239 def create_playlist_cb(w, entry):
240 name = entry.get_text()
241 def _switch_list(res):
242 self.name = name
243 self.load_playlist()
244 self.xmms.playlist_create(name, _switch_list)
245 win.destroy()
246 entry.connect('activate', create_playlist_cb, entry)
247 button.connect('activate', create_playlist_cb, entry)
249 win.add_widget(gnt.Label('Name'))
250 win.add_widget(entry)
251 win.add_widget(button)
252 win.show()
253 return True
255 def place_tagged(self, null):
256 """Move the tagged entries immediately after the selected entry."""
257 entry = self.get_selection_data()
258 if not entry:
259 return True
260 all = self.get_rows()
261 index = all.index(entry)
262 pos = -1
263 for row in all:
264 pos = pos + 1
265 if not row.tagged:
266 continue
267 row.toggle_tag()
268 if pos > index:
269 index = index + 1
270 self.xmms.playlist_move(pos, index, self.name)
271 if pos < index:
272 pos = pos - 1
274 def clear_playlist(self, null):
275 """Clear the playlist. Prompt first."""
276 win = gnt.Window()
277 win.set_title("Confirm")
278 win.set_property('vertical', True)
279 win.set_fill(False)
280 win.set_alignment(gnt.ALIGN_MID)
281 clr = gnt.Button("Clear")
282 def really_clear_playlist(b, window):
283 self.xmms.playlist_clear(self.name)
284 window.destroy()
285 clr.connect('activate', really_clear_playlist, win)
286 cnc = gnt.Button("Cancel")
287 def close_window(b, window):
288 window.destroy()
289 cnc.connect('activate', close_window, win)
290 win.add_widget(gnt.Label("Do you really want to remove all entries from this playlist?"))
291 win.add_widget(common.pack_widget(False, [clr, cnc]))
292 win.show()
293 return True
295 gobject.type_register(XPList)
296 gnt.register_bindings(XPList)
298 class XPListCurrent(XPList):
299 __gntbindings__ = {
300 'play-selected' : ('play_selected', gnt.KEY_ENTER),
301 'play' : ('play', 'x'),
302 'pause' : ('pause', 'c'),
303 'stop' : ('stop', 'v'),
304 'jump-to-current' : ('jump_to_current', gnt.KEY_CTRL_J),
305 'selected-next' : ('selected_next', 'q'),
308 def __init__(self, xmms):
309 XPList.__init__(self, xmms, None)
310 def _set_name(res):
311 self.name = res.value()
312 xmms.playlist_current_active(_set_name)
314 def do_set_property(self, prop, value):
315 XPList.do_set_property(self, prop, value)
316 self.xmms.playlist_load(self.name)
318 def play_selected(self, null):
319 song = self.get_selection_data()
320 if song is None:
321 return False
322 index = self.get_rows().index(song)
323 self.xmms.playback_start()
324 def cb(res):
325 self.xmms.playback_tickle()
326 self.xmms.playlist_set_next(index, cb)
327 return True
329 def play(self, null):
330 self.xmms.playback_start()
331 return True
333 def pause(self, null):
334 self.xmms.playback_pause()
335 return True
337 def stop(self, null):
338 self.xmms.playback_stop()
339 return True
341 def jump_to_current(self, null):
342 def got_current_pos(result):
343 current = result.value()
344 rows = self.get_rows()
345 self.set_selected(rows[current])
346 self.xmms.playlist_current_pos(cb = got_current_pos)
347 return True
349 def selected_next(self, null):
350 song = self.get_selection_data()
351 if song is None:
352 return False
353 index = self.get_rows().index(song)
354 self.xmms.playlist_set_next(index)
355 return True
357 gobject.type_register(XPListCurrent)
358 gnt.register_bindings(XPListCurrent)
360 def setup_xmms():
361 xmms = xmmsclient.XMMS("pygnt-playlist")
362 try:
363 xmms.connect(os.getenv("XMMS_PATH"))
364 conn = xg.GLibConnector(xmms)
365 xqs = XPListCurrent(xmms)
366 xqs.show()
367 except IOError, detail:
368 common.show_error("Connection failed: " + str(detail))
370 if __name__ == '__main__':
371 setup_xmms()
372 gnt.gnt_main()
373 gnt.gnt_quit()