Split the playlist in a generic songlist and playlist.
[python-gnt.git] / example / xmms / xmms-pl.py
blob26e75f57570331893e8f23f8350c65651a7fe5b1
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 __gntbindings__ = {
48 'add-entry' : ('add_entry', gnt.KEY_INS),
49 'del-entry' : ('del_entry', gnt.KEY_DEL),
50 'switch-playlist' : ('switch_playlist', 'w'),
51 'create-playlist' : ('create_playlist', 'c'),
52 'toggle-tag' : ('toggle_tag', gnt.KEY_CTRL_T),
53 'place-tagged' : ('place_tagged', gnt.KEY_CTRL_P),
56 def __init__(self, xmms, name):
57 """The xmms connection, and the name of the playlist."""
58 XList.__init__(self, xmms)
59 self.name = name
60 self.win = None
61 self.init_playlist()
62 self.connect('context-menu', self.context_menu)
64 def context_menu(self, null):
65 """Popup a context menu with the available options."""
66 def perform_action(item, data):
67 # This is a little weird, so pay attention ...
68 # The callback can either bring out a new window, or a new menu.
69 # In the first scenario, we want the new window to be given focus.
70 # So we perform the action immediately.
71 # In the latter case, we need to first let the active menu hide,
72 # then perform the action, so we call the callback in the next
73 # iteration of the mainloop.
74 action, wait = data
75 def really_perform_action():
76 action(None)
77 return False
78 if wait:
79 gobject.timeout_add(0, really_perform_action)
80 else:
81 action(None)
82 menu = gnt.Menu(gnt.MENU_POPUP)
83 for text, action, wait in (('Edit Info', self.edit_entry, False),
84 ('Remove', self.del_entry, False),
85 ('Add Files...', self.add_entry, False),
86 ('Edit Columns ...', self.edit_columns, True),
87 ('Set Search Column ...', self.search_column, True),
88 ('Switch Playlist...', self.switch_playlist, True)
90 item = gnt.MenuItem(text)
91 item.connect('activate', perform_action, [action, wait])
92 menu.add_item(item)
93 self.position_menu(menu)
94 gnt.show_menu(menu)
96 def init_playlist(self):
97 self.load_playlist()
99 # Refresh the playlist if an entry is added/removed etc.
100 def refresh_playlist(result):
101 info = result.value()
102 if info['name'] != self.name: return # This playlist didn't change
103 if info['type'] == xmmsclient.PLAYLIST_CHANGED_REMOVE:
104 # Some entry was removed
105 rows = self.get_rows()
106 row = rows[info['position']]
107 self.remove(row)
108 elif info['type'] == xmmsclient.PLAYLIST_CHANGED_ADD or info['type'] == xmmsclient.PLAYLIST_CHANGED_INSERT:
109 # Some entry was added
110 position = info['position']
111 self.add_mlib_song(int(position), info['id'])
112 elif info['type'] == xmmsclient.PLAYLIST_CHANGED_MOVE:
113 old = info['position']
114 new = info['newposition']
115 # First, remove the entry
116 rows = self.get_rows()
117 row = rows[old]
118 self.remove(row)
119 # Now, find the new entry position
120 rows = self.get_rows()
121 if new > 0:
122 after = rows[new - 1]
123 else:
124 after = None
125 info = row.get_data('song-info')
126 [title, artist, album, time] = common.str_info(info)
127 # Add it back in the new position
128 self.add_row_after(row, [str(new + 1), title, artist, album, time], None, after)
129 elif info['type'] == xmmsclient.PLAYLIST_CHANGED_CLEAR:
130 self.remove_all()
131 else:
132 sys.stderr.write("Unhandled playlist update type " + str(info['type']) + " -- Please file a bug report.\n")
133 # XXX: just go ahead and refresh the entire list
134 self.load_playlist()
135 return
136 # Make sure the entry in the 'position' column is correct for the entries
137 rows = self.get_rows()
138 num = 1
139 for row in rows:
140 self.change_text(row, self.POSITION, str(num))
141 num = num + 1
142 self.xmms.broadcast_playlist_changed(refresh_playlist)
144 def load_playlist(self):
145 """Get the entries in the list, and populate the tree."""
146 def got_list_of_songs_in_pl(result):
147 list = result.value()
148 pos = itertools.count(1)
149 for song in list:
150 self.add_mlib_song(int(pos.next()), song)
151 if self.win:
152 self.win.set_title("XMMS2 Playlist - " + str(self.name))
153 self.win.draw()
154 self.remove_all()
155 self.draw()
156 self.xmms.playlist_list_entries(self.name, got_list_of_songs_in_pl)
158 def show(self):
159 """Show the playlist inside a window."""
160 win = self.win = gnt.Box(homo = False, vert = True)
161 win.set_toplevel(True)
162 win.set_title("XMMS2 Playlist - " + str(self.name))
163 win.add_widget(self)
164 width, height = gnt.screen_size()
165 self.set_size(width, height)
166 win.show()
168 def add_entry(self, null):
169 """Add new song(s) in the playlist."""
170 fl = gnt.FileSel()
171 fl.set_multi_select(True)
172 def destroy_fl(b, dlg):
173 dlg.destroy()
174 fl.cancel_button().connect('activate', destroy_fl, fl)
175 def add_files(fl, path, files, dlg):
176 for file in dlg.get_selected_multi_files():
177 self.xmms.playlist_add_url('file://' + file, self.name)
178 dlg.destroy()
179 fl.connect('file_selected', add_files, fl)
180 fl.show()
182 def del_entry(self, null):
183 """Delete selected entry from the playlist."""
184 sel = self.get_selection_data()
185 if not sel:
186 return
187 index = self.get_rows().index(sel)
188 self.xmms.playlist_remove_entry(index, self.name)
190 def switch_playlist(self, null):
191 """Switch the playlist to show in the list."""
192 def show_list_menu(res):
193 def _load_playlist(item):
194 name = item.get_data('playlist-name')
195 self.name = name
196 self.load_playlist()
197 list = res.value()
198 menu = gnt.Menu(gnt.MENU_POPUP)
199 for pl in list:
200 name = str(pl)
201 item = gnt.MenuItemCheck(name)
202 if name == self.name:
203 item.set_checked(True)
204 item.set_data('playlist-name', name)
205 item.connect('activate', _load_playlist)
206 menu.add_item(item)
207 self.position_menu(menu)
208 gnt.show_menu(menu)
209 self.xmms.playlist_list(show_list_menu)
210 return True
212 def create_playlist(self, null):
213 """Create a new playlist."""
214 win = gnt.Window()
215 win.set_title('Create Playlist')
216 win.set_fill(False)
217 win.set_alignment(gnt.ALIGN_MID)
218 entry = gnt.Entry('')
219 button = gnt.Button('Create')
221 def create_playlist_cb(w, entry):
222 name = entry.get_text()
223 def _switch_list(res):
224 self.name = name
225 self.load_playlist()
226 self.xmms.playlist_create(name, _switch_list)
227 win.destroy()
228 entry.connect('activate', create_playlist_cb, entry)
229 button.connect('activate', create_playlist_cb, entry)
231 win.add_widget(gnt.Label('Name'))
232 win.add_widget(entry)
233 win.add_widget(button)
234 win.show()
235 return True
237 def toggle_tag(self, null):
238 """Tag/untag the selected entry in the playlist."""
239 entry = self.get_selection_data()
240 if not entry:
241 return True
242 entry.toggle_tag()
243 self.set_row_flags(entry, entry.get_row_flag())
244 self.perform_action_named('move-down')
245 return True
247 def place_tagged(self, null):
248 """Move the tagged entries immediately after the selected entry."""
249 entry = self.get_selection_data()
250 if not entry:
251 return True
252 all = self.get_rows()
253 index = all.index(entry)
254 pos = -1
255 for row in all:
256 pos = pos + 1
257 if not row.tagged:
258 continue
259 row.toggle_tag()
260 if pos > index:
261 index = index + 1
262 self.xmms.playlist_move(pos, index, self.name)
263 if pos < index:
264 pos = pos - 1
266 gobject.type_register(XPList)
267 gnt.register_bindings(XPList)
269 def setup_xmms():
270 xmms = xmmsclient.XMMS("pygnt-playlist")
271 try:
272 xmms.connect(os.getenv("XMMS_PATH"))
273 conn = xg.GLibConnector(xmms)
274 xqs = XPList(xmms, 'Default')
275 xqs.show()
276 except IOError, detail:
277 common.show_error("Connection failed: " + str(detail))
279 if __name__ == '__main__':
280 setup_xmms()
281 gnt.gnt_main()
282 gnt.gnt_quit()