Merge lp:~linkinpark342/exaile/352683 into lp:exaile/0.3.3
- 352683
- Merge into exaile-0.3.x
Proposed by
Abhishek Mukherjee
Status: | Merged | ||||
---|---|---|---|---|---|
Merged at revision: | not available | ||||
Proposed branch: | lp:~linkinpark342/exaile/352683 | ||||
Merge into: | lp:exaile/0.3.3 | ||||
Diff against target: | None lines | ||||
To merge this branch: | bzr merge lp:~linkinpark342/exaile/352683 | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
reacocard (community) | Approve | ||
Review via email: mp+5646@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Revision history for this message
Abhishek Mukherjee (linkinpark342) wrote : | # |
Revision history for this message
reacocard (reacocard) : | # |
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'data/glade/main.glade' |
2 | --- data/glade/main.glade 2009-03-28 20:56:12 +0000 |
3 | +++ data/glade/main.glade 2009-04-13 02:38:36 +0000 |
4 | @@ -151,6 +151,23 @@ |
5 | </widget> |
6 | </child> |
7 | |
8 | + <child> |
9 | + <widget class="GtkImageMenuItem" id="queue_manager_item"> |
10 | + <property name="label" translatable="yes">_Queue Manager</property> |
11 | + <property name="visible">True</property> |
12 | + <property name="use_underline">True</property> |
13 | + <property name="use_stock">False</property> |
14 | + <signal name="activate" handler="on_queue_manager_item_activate"/> |
15 | + <child internal-child="image"> |
16 | + <widget class="GtkImage" id="queue_manager_icon"> |
17 | + <property name="visible">True</property> |
18 | + <property name="stock">gtk-add</property> |
19 | + <property name="icon-size">1</property> |
20 | + </widget> |
21 | + </child> |
22 | + </widget> |
23 | + </child> |
24 | + |
25 | <child> |
26 | <widget class="GtkImageMenuItem" id="plugins1"> |
27 | <property name="visible">True</property> |
28 | |
29 | === added file 'data/glade/queue_dialog.glade' |
30 | --- data/glade/queue_dialog.glade 1970-01-01 00:00:00 +0000 |
31 | +++ data/glade/queue_dialog.glade 2009-04-14 17:10:16 +0000 |
32 | @@ -0,0 +1,187 @@ |
33 | +<?xml version="1.0"?> |
34 | +<glade-interface> |
35 | + <!-- interface-requires gtk+ 2.16 --> |
36 | + <!-- interface-naming-policy project-wide --> |
37 | + <widget class="GtkWindow" id="QueueManagerDialog"> |
38 | + <property name="width_request">300</property> |
39 | + <property name="height_request">400</property> |
40 | + <property name="title" translatable="yes">Queue Manager - Exaile</property> |
41 | + <child> |
42 | + <widget class="GtkVBox" id="vbox"> |
43 | + <property name="visible">True</property> |
44 | + <property name="orientation">vertical</property> |
45 | + <child> |
46 | + <widget class="GtkHBox" id="hbox1"> |
47 | + <property name="visible">True</property> |
48 | + <child> |
49 | + <widget class="GtkScrolledWindow" id="scrolledwindow1"> |
50 | + <property name="visible">True</property> |
51 | + <property name="can_focus">True</property> |
52 | + <property name="hscrollbar_policy">automatic</property> |
53 | + <property name="vscrollbar_policy">automatic</property> |
54 | + <child> |
55 | + <widget class="GtkTreeView" id="queue_tree"> |
56 | + <property name="visible">True</property> |
57 | + <property name="can_focus">True</property> |
58 | + </widget> |
59 | + </child> |
60 | + </widget> |
61 | + <packing> |
62 | + <property name="padding">5</property> |
63 | + <property name="position">0</property> |
64 | + </packing> |
65 | + </child> |
66 | + <child> |
67 | + <widget class="GtkVButtonBox" id="vbuttonbox1"> |
68 | + <property name="visible">True</property> |
69 | + <property name="layout_style">spread</property> |
70 | + <child> |
71 | + <widget class="GtkButton" id="top_button"> |
72 | + <property name="label" translatable="yes">gtk-goto-top</property> |
73 | + <property name="visible">True</property> |
74 | + <property name="can_focus">True</property> |
75 | + <property name="receives_default">True</property> |
76 | + <property name="tooltip" translatable="yes">Move song to top in queue</property> |
77 | + <property name="use_stock">True</property> |
78 | + <signal name="clicked" handler="on_top_button_clicked"/> |
79 | + </widget> |
80 | + <packing> |
81 | + <property name="expand">False</property> |
82 | + <property name="fill">False</property> |
83 | + <property name="position">0</property> |
84 | + </packing> |
85 | + </child> |
86 | + <child> |
87 | + <widget class="GtkButton" id="up_button"> |
88 | + <property name="label" translatable="yes">gtk-go-up</property> |
89 | + <property name="visible">True</property> |
90 | + <property name="can_focus">True</property> |
91 | + <property name="receives_default">True</property> |
92 | + <property name="tooltip" translatable="yes">Move song up in queue</property> |
93 | + <property name="use_stock">True</property> |
94 | + <signal name="clicked" handler="on_up_button_clicked"/> |
95 | + </widget> |
96 | + <packing> |
97 | + <property name="expand">False</property> |
98 | + <property name="fill">False</property> |
99 | + <property name="position">1</property> |
100 | + </packing> |
101 | + </child> |
102 | + <child> |
103 | + <widget class="GtkButton" id="remove_button"> |
104 | + <property name="label" translatable="yes">gtk-remove</property> |
105 | + <property name="visible">True</property> |
106 | + <property name="can_focus">True</property> |
107 | + <property name="receives_default">True</property> |
108 | + <property name="tooltip" translatable="yes">Remove song from queue</property> |
109 | + <property name="use_stock">True</property> |
110 | + <signal name="clicked" handler="on_remove_button_clicked"/> |
111 | + </widget> |
112 | + <packing> |
113 | + <property name="expand">False</property> |
114 | + <property name="fill">False</property> |
115 | + <property name="position">2</property> |
116 | + </packing> |
117 | + </child> |
118 | + <child> |
119 | + <widget class="GtkButton" id="down_button"> |
120 | + <property name="label" translatable="yes">gtk-go-down</property> |
121 | + <property name="visible">True</property> |
122 | + <property name="can_focus">True</property> |
123 | + <property name="receives_default">True</property> |
124 | + <property name="tooltip" translatable="yes">Move song down in queue</property> |
125 | + <property name="use_stock">True</property> |
126 | + <signal name="clicked" handler="on_down_button_clicked"/> |
127 | + </widget> |
128 | + <packing> |
129 | + <property name="expand">False</property> |
130 | + <property name="fill">False</property> |
131 | + <property name="position">3</property> |
132 | + </packing> |
133 | + </child> |
134 | + <child> |
135 | + <widget class="GtkButton" id="bottom_button"> |
136 | + <property name="label" translatable="yes">gtk-goto-bottom</property> |
137 | + <property name="visible">True</property> |
138 | + <property name="can_focus">True</property> |
139 | + <property name="receives_default">True</property> |
140 | + <property name="tooltip" translatable="yes">Move song to bottom of queue</property> |
141 | + <property name="use_stock">True</property> |
142 | + <signal name="clicked" handler="on_bottom_button_clicked"/> |
143 | + </widget> |
144 | + <packing> |
145 | + <property name="expand">False</property> |
146 | + <property name="fill">False</property> |
147 | + <property name="position">4</property> |
148 | + </packing> |
149 | + </child> |
150 | + </widget> |
151 | + <packing> |
152 | + <property name="expand">False</property> |
153 | + <property name="padding">5</property> |
154 | + <property name="position">1</property> |
155 | + </packing> |
156 | + </child> |
157 | + </widget> |
158 | + <packing> |
159 | + <property name="padding">5</property> |
160 | + <property name="position">0</property> |
161 | + </packing> |
162 | + </child> |
163 | + <child> |
164 | + <widget class="GtkHSeparator" id="hseparator1"> |
165 | + <property name="visible">True</property> |
166 | + </widget> |
167 | + <packing> |
168 | + <property name="expand">False</property> |
169 | + <property name="padding">5</property> |
170 | + <property name="position">1</property> |
171 | + </packing> |
172 | + </child> |
173 | + <child> |
174 | + <widget class="GtkHButtonBox" id="button_box"> |
175 | + <property name="visible">True</property> |
176 | + <property name="spacing">5</property> |
177 | + <property name="layout_style">end</property> |
178 | + <child> |
179 | + <widget class="GtkButton" id="ok_button"> |
180 | + <property name="label" translatable="yes">gtk-ok</property> |
181 | + <property name="visible">True</property> |
182 | + <property name="can_focus">True</property> |
183 | + <property name="receives_default">True</property> |
184 | + <property name="tooltip" translatable="yes">Close this dialog</property> |
185 | + <property name="use_stock">True</property> |
186 | + <signal name="clicked" handler="close_dialog"/> |
187 | + </widget> |
188 | + <packing> |
189 | + <property name="expand">False</property> |
190 | + <property name="fill">False</property> |
191 | + <property name="position">1</property> |
192 | + </packing> |
193 | + </child> |
194 | + <child> |
195 | + <widget class="GtkButton" id="remove_all_button"> |
196 | + <property name="label" translatable="yes" comments="Remove all from queue">Remove _All</property> |
197 | + <property name="visible">True</property> |
198 | + <property name="can_focus">True</property> |
199 | + <property name="receives_default">True</property> |
200 | + <property name="use_underline">True</property> |
201 | + <signal name="clicked" handler="on_remove_all_button_clicked"/> |
202 | + </widget> |
203 | + <packing> |
204 | + <property name="expand">False</property> |
205 | + <property name="fill">False</property> |
206 | + <property name="position">0</property> |
207 | + </packing> |
208 | + </child> |
209 | + </widget> |
210 | + <packing> |
211 | + <property name="expand">False</property> |
212 | + <property name="padding">5</property> |
213 | + <property name="position">2</property> |
214 | + </packing> |
215 | + </child> |
216 | + </widget> |
217 | + </child> |
218 | + </widget> |
219 | +</glade-interface> |
220 | |
221 | === modified file 'xlgui/__init__.py' |
222 | --- xlgui/__init__.py 2009-03-15 04:05:49 +0000 |
223 | +++ xlgui/__init__.py 2009-04-13 02:38:36 +0000 |
224 | @@ -18,7 +18,7 @@ |
225 | import gtk, gtk.glade, gobject, logging |
226 | from xl import xdg, common, event, metadata |
227 | |
228 | -from xlgui import guiutil, prefs, plugins, cover, commondialogs |
229 | +from xlgui import guiutil, prefs, plugins, cover, commondialogs, queue |
230 | |
231 | gtk.window_set_default_icon_from_file(xdg.get_data_path("images/icon.png")) |
232 | logger = logging.getLogger(__name__) |
233 | @@ -84,6 +84,7 @@ |
234 | 'on_about_item_activate': self.show_about_dialog, |
235 | 'on_scan_collection_item_activate': self.on_rescan_collection, |
236 | 'on_collection_manager_item_activate': self.collection_manager, |
237 | + 'on_queue_manager_item_activate': self.queue_manager, |
238 | 'on_preferences_item_activate': lambda *e: self.show_preferences(), |
239 | 'on_plugins_item_activate': self.show_plugins, |
240 | 'on_album_art_item_activate': self.show_cover_manager, |
241 | @@ -199,6 +200,10 @@ |
242 | plugin_page=plugin_page) |
243 | dialog.run() |
244 | |
245 | + def queue_manager(self, *e): |
246 | + dialog = queue.QueueManager(self.exaile.queue) |
247 | + dialog.show() |
248 | + |
249 | def collection_manager(self, *e): |
250 | """ |
251 | Invokes the collection manager dialog |
252 | |
253 | === added file 'xlgui/queue.py' |
254 | --- xlgui/queue.py 1970-01-01 00:00:00 +0000 |
255 | +++ xlgui/queue.py 2009-04-16 20:54:18 +0000 |
256 | @@ -0,0 +1,200 @@ |
257 | +""" |
258 | + Queue manager dialog |
259 | +""" |
260 | + |
261 | +from xl import xdg |
262 | +from xl.nls import gettext as _ |
263 | +from operator import itemgetter |
264 | +from copy import copy |
265 | +import xl.event |
266 | +import os |
267 | +import gtk |
268 | +import gtk.glade |
269 | +import logging |
270 | + |
271 | +LOG = logging.getLogger('exaile.xlgui.queue') |
272 | + |
273 | +class QueueManager(object): |
274 | + |
275 | + """ |
276 | + GUI to manage a queue |
277 | + """ |
278 | + |
279 | + def __init__(self, queue): |
280 | + self._queue = queue |
281 | + |
282 | + self._xml = gtk.glade.XML( |
283 | + xdg.get_data_path(os.path.join('glade', 'queue_dialog.glade')), |
284 | + 'QueueManagerDialog', 'exaile') |
285 | + self._xml.get_widget('remove_all_button').set_image( |
286 | + gtk.image_new_from_stock(gtk.STOCK_REMOVE, gtk.ICON_SIZE_BUTTON)) |
287 | + |
288 | + self._dialog = self._xml.get_widget('QueueManagerDialog') |
289 | + self._dialog.connect('destroy', self.destroy) |
290 | + self._xml.signal_autoconnect({ |
291 | + 'close_dialog': self.destroy, |
292 | + 'on_top_button_clicked': self.selected_to_top, |
293 | + 'on_up_button_clicked': self.selected_up, |
294 | + 'on_remove_button_clicked': self.remove_selected, |
295 | + 'on_down_button_clicked': self.selected_down, |
296 | + 'on_bottom_button_clicked': self.selected_to_bottom, |
297 | + 'on_remove_all_button_clicked': self.remove_all, |
298 | + }) |
299 | + |
300 | + self.__setup_callbacks() |
301 | + |
302 | + self._model = gtk.ListStore(int, str, object) |
303 | + self._queue_view = self._xml.get_widget('queue_tree') |
304 | + self._queue_view.set_model(self._model) |
305 | + self.__last_tracks = [] |
306 | + self.__populate_queue() |
307 | + self.__setup_queue() |
308 | + |
309 | + def __setup_callbacks(self): |
310 | + for callback in self.__callbacks(): |
311 | + xl.event.add_callback(*callback) |
312 | + |
313 | + def __teardown_callbacks(self): |
314 | + for callback in self.__callbacks(): |
315 | + xl.event.remove_callback(*callback) |
316 | + |
317 | + def __populate_queue_cb(self, *e): |
318 | + self.__populate_queue() |
319 | + |
320 | + def __callbacks(self): |
321 | + yield (self.__populate_queue_cb, 'playback_start') |
322 | + yield (self.__populate_queue_cb, 'tracks_added', self._queue) |
323 | + |
324 | + def __setup_queue(self): |
325 | + """Adds columns to _queue_view""" |
326 | + renderer = gtk.CellRendererText() |
327 | + col = gtk.TreeViewColumn(_('#'), renderer, text=0) |
328 | + col.set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE) |
329 | + self._queue_view.append_column(col) |
330 | + |
331 | + renderer = gtk.CellRendererText() |
332 | + col = gtk.TreeViewColumn(_('Title'), renderer, text=1) |
333 | + col.set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE) |
334 | + self._queue_view.append_column(col) |
335 | + |
336 | + def __populate_queue(self): |
337 | + """Populates the _model with tracks""" |
338 | + tracks = self._queue.get_ordered_tracks() |
339 | + if tracks == self.__last_tracks: |
340 | + LOG.debug(_("Tracks did not change, no need to update")) |
341 | + return |
342 | + # Find the row that will be selected |
343 | + |
344 | + if self._queue_view is not None: |
345 | + model, iter = self._queue_view.get_selection().get_selected() |
346 | + if iter: |
347 | + target = model.get_value(iter, 2) |
348 | + try: |
349 | + new_cursor_pos = tracks.index(target) |
350 | + except ValueError: |
351 | + pass |
352 | + if 'new_cursor_pos' not in locals(): |
353 | + new_cursor_pos = None |
354 | + self.__last_tracks = copy(tracks) |
355 | + self._model.clear() |
356 | + # Add the rows |
357 | + for i, track in zip(xrange(1, len(tracks) + 1), tracks): |
358 | + self._model.append((i, unicode(track), track)) |
359 | + # Select new row |
360 | + if new_cursor_pos is not None and hasattr(self, '_queue_view'): |
361 | + self._queue_view.set_cursor((new_cursor_pos,)) |
362 | + |
363 | + def show(self): |
364 | + """ |
365 | + Displays this window |
366 | + """ |
367 | + self._dialog.show_all() |
368 | + |
369 | + def destroy(self, *e): |
370 | + """ |
371 | + Destroys this window |
372 | + """ |
373 | + self._dialog.destroy() |
374 | + self.__teardown_callbacks() |
375 | + |
376 | +# removing items |
377 | + def remove_selected(self, button, *userparams): |
378 | + model, iter = self._queue_view.get_selection().get_selected() |
379 | + if not iter: |
380 | + return |
381 | + i = model.get_value(iter, 0) - 1 |
382 | + self.remove(i) |
383 | + |
384 | + def remove_all(self, button, *userparams): |
385 | + while len(self._queue.get_ordered_tracks()): |
386 | + self.remove(0) |
387 | + |
388 | + def remove(self, i): |
389 | + """Removes the ith item from the queue, 0-indexed""" |
390 | + cur_queue = self._queue.get_ordered_tracks() |
391 | + if i < 0 or i >= len(cur_queue): |
392 | + LOG.error(_("Gave an invalid number to remove")) |
393 | + return |
394 | + cur_queue.pop(i) |
395 | + self.__populate_queue() |
396 | + |
397 | +# Moving callbacks |
398 | + def selected_to_top(self, button, *userparams): |
399 | + self.reorder(lambda x, l: 0) |
400 | + |
401 | + def selected_up(self, button, *userparams): |
402 | + self.reorder(lambda x, l: x - 1) |
403 | + |
404 | + def selected_down(self, button, *userparams): |
405 | + self.reorder(lambda x, l: x + 1) |
406 | + |
407 | + def selected_to_bottom(self, button, *userparams): |
408 | + self.reorder(lambda x, l: l - 1) |
409 | + |
410 | + def reorder(self, new_loc): |
411 | + """Reorders the tracks in the queue. |
412 | + |
413 | + new_loc is a function that takes two variables, x and l. x will be the |
414 | + the index of the currently selected item. l will be the size of the |
415 | + queue. The function need not bounds check. |
416 | + |
417 | + """ |
418 | + model, iter = self._queue_view.get_selection().get_selected() |
419 | + if not iter: |
420 | + return |
421 | + |
422 | + i = model.get_value(iter, 0) - 1 |
423 | + tracks = self._queue.get_ordered_tracks() |
424 | + if callable(new_loc): |
425 | + new_loc = new_loc(i, len(tracks)) |
426 | + if new_loc < 0 or new_loc >= len(tracks): |
427 | + new_loc = i |
428 | + |
429 | + new_order = list(zip(range(len(tracks)), tracks)) |
430 | + new_order[new_loc], new_order[i] = new_order[i], new_order[new_loc] |
431 | + |
432 | + self._queue.set_ordered_tracks(list(map(itemgetter(1), new_order))) |
433 | + self.__populate_queue() |
434 | + |
435 | +def main(): |
436 | + class Track(object): |
437 | + def __init__(self, title): |
438 | + self.tags = {'title': title} |
439 | + def __unicode__(self): |
440 | + return self.tags['title'] |
441 | + def __str(self): |
442 | + return str(unicode(self)) |
443 | + class Foo(object): |
444 | + ordered_tracks = [Track('Track Foo by bar on baz'), Track('bar')] |
445 | + get_ordered_tracks = lambda self: self.ordered_tracks |
446 | + def set_ordered_tracks(self, v): |
447 | + self.ordered_tracks = v |
448 | + dialog = QueueManager(Foo()) |
449 | + dialog.show() |
450 | + try: |
451 | + gtk.main() |
452 | + except KeyboardInterrupt: |
453 | + pass |
454 | + |
455 | +if __name__ == "__main__": |
456 | + main() |
Adds queue managing functionality as a popup. This could later be extended to include Queue Manager as a pane if enough support is gathered.