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
25 import xmmsclient
.glib
as xg
26 import xmmsclient
.collections
as xc
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"
44 sys
.setdefaultencoding("utf-8")
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
)
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.
75 def really_perform_action():
79 gobject
.timeout_add(0, really_perform_action
)
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
])
93 self
.position_menu(menu
)
96 def init_playlist(self
):
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']]
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()
119 # Now, find the new entry position
120 rows
= self
.get_rows()
122 after
= rows
[new
- 1]
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
:
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
136 # Make sure the entry in the 'position' column is correct for the entries
137 rows
= self
.get_rows()
140 self
.change_text(row
, self
.POSITION
, str(num
))
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)
150 self
.add_mlib_song(int(pos
.next()), song
)
152 self
.win
.set_title("XMMS2 Playlist - " + str(self
.name
))
156 self
.xmms
.playlist_list_entries(self
.name
, got_list_of_songs_in_pl
)
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
))
164 width
, height
= gnt
.screen_size()
165 self
.set_size(width
, height
)
168 def add_entry(self
, null
):
169 """Add new song(s) in the playlist."""
171 fl
.set_multi_select(True)
172 def destroy_fl(b
, dlg
):
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
)
179 fl
.connect('file_selected', add_files
, fl
)
182 def del_entry(self
, null
):
183 """Delete selected entry from the playlist."""
184 sel
= self
.get_selection_data()
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')
198 menu
= gnt
.Menu(gnt
.MENU_POPUP
)
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
)
207 self
.position_menu(menu
)
209 self
.xmms
.playlist_list(show_list_menu
)
212 def create_playlist(self
, null
):
213 """Create a new playlist."""
215 win
.set_title('Create Playlist')
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
):
226 self
.xmms
.playlist_create(name
, _switch_list
)
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
)
237 def toggle_tag(self
, null
):
238 """Tag/untag the selected entry in the playlist."""
239 entry
= self
.get_selection_data()
243 self
.set_row_flags(entry
, entry
.get_row_flag())
244 self
.perform_action_named('move-down')
247 def place_tagged(self
, null
):
248 """Move the tagged entries immediately after the selected entry."""
249 entry
= self
.get_selection_data()
252 all
= self
.get_rows()
253 index
= all
.index(entry
)
262 self
.xmms
.playlist_move(pos
, index
, self
.name
)
266 gobject
.type_register(XPList
)
267 gnt
.register_bindings(XPList
)
270 xmms
= xmmsclient
.XMMS("pygnt-playlist")
272 xmms
.connect(os
.getenv("XMMS_PATH"))
273 conn
= xg
.GLibConnector(xmms
)
274 xqs
= XPList(xmms
, 'Default')
276 except IOError, detail
:
277 common
.show_error("Connection failed: " + str(detail
))
279 if __name__
== '__main__':