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
25 import xmmsclient
.glib
as xg
26 import xmmsclient
.collections
as xc
38 sys
.setdefaultencoding("utf-8")
40 class XList(gnt
.Tree
):
41 """Generic Song list."""
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
)
60 self
.needupdates
= [] # A list of medialib id's which we need to poke information for
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)
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)
79 def toggle_column(self
, col
):
80 """Toggle the visibility of the 'col'th column"""
81 if self
.columns
& (1 << 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."""
89 id = info
.get('id', None)
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
)
123 self
.set_property('columns', self
.COLUMNS
)
125 self
.set_show_title(True)
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)
134 self
.set_column_title(self
.TITLE
, "Title")
135 self
.set_col_width(self
.POSITION
, 20)
138 self
.set_column_title(self
.ARTIST
, "Artist")
139 self
.set_col_width(self
.ARTIST
, 20)
142 self
.set_column_title(self
.ALBUM
, "Album")
143 self
.set_col_width(self
.ALBUM
, 20)
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()
163 info
= sel
.get_data('song-info')
165 common
.show_error("Do not have any editable information about this song.")
167 edit
= songedit
.SongEdit(self
.xmms
)
168 win
= edit
.get_widget()
170 win
.set_toplevel(True)
171 win
.set_title("Edit Song Information")
174 def add_entry(self
, null
):
175 """Add new song(s) in the playlist."""
177 fl
.set_multi_select(True)
178 def destroy_fl(b
, dlg
):
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
)
185 fl
.connect('file_selected', add_files
, fl
)
188 def del_entry(self
, null
):
189 """Delete selected entry from the playlist."""
190 sel
= self
.get_selection_data()
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
)
220 self
.position_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
)
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)
237 self
.position_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)
247 [title
, artist
, album
, time
] = common
.str_info(info
)
249 rows
= self
.get_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
):
266 row
.set_data('song-id', mlibid
)
267 rows
= self
.get_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
)