Merge lp:~linkinpark342/exaile/352683 into lp:exaile/0.3.3

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
Reviewer Review Type Date Requested Status
reacocard (community) Approve
Review via email: mp+5646@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Abhishek Mukherjee (linkinpark342) wrote :

Adds queue managing functionality as a popup. This could later be extended to include Queue Manager as a pane if enough support is gathered.

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
=== modified file 'data/glade/main.glade'
--- data/glade/main.glade 2009-03-28 20:56:12 +0000
+++ data/glade/main.glade 2009-04-13 02:38:36 +0000
@@ -151,6 +151,23 @@
151 </widget>151 </widget>
152 </child>152 </child>
153153
154 <child>
155 <widget class="GtkImageMenuItem" id="queue_manager_item">
156 <property name="label" translatable="yes">_Queue Manager</property>
157 <property name="visible">True</property>
158 <property name="use_underline">True</property>
159 <property name="use_stock">False</property>
160 <signal name="activate" handler="on_queue_manager_item_activate"/>
161 <child internal-child="image">
162 <widget class="GtkImage" id="queue_manager_icon">
163 <property name="visible">True</property>
164 <property name="stock">gtk-add</property>
165 <property name="icon-size">1</property>
166 </widget>
167 </child>
168 </widget>
169 </child>
170
154 <child>171 <child>
155 <widget class="GtkImageMenuItem" id="plugins1">172 <widget class="GtkImageMenuItem" id="plugins1">
156 <property name="visible">True</property>173 <property name="visible">True</property>
157174
=== added file 'data/glade/queue_dialog.glade'
--- data/glade/queue_dialog.glade 1970-01-01 00:00:00 +0000
+++ data/glade/queue_dialog.glade 2009-04-14 17:10:16 +0000
@@ -0,0 +1,187 @@
1<?xml version="1.0"?>
2<glade-interface>
3 <!-- interface-requires gtk+ 2.16 -->
4 <!-- interface-naming-policy project-wide -->
5 <widget class="GtkWindow" id="QueueManagerDialog">
6 <property name="width_request">300</property>
7 <property name="height_request">400</property>
8 <property name="title" translatable="yes">Queue Manager - Exaile</property>
9 <child>
10 <widget class="GtkVBox" id="vbox">
11 <property name="visible">True</property>
12 <property name="orientation">vertical</property>
13 <child>
14 <widget class="GtkHBox" id="hbox1">
15 <property name="visible">True</property>
16 <child>
17 <widget class="GtkScrolledWindow" id="scrolledwindow1">
18 <property name="visible">True</property>
19 <property name="can_focus">True</property>
20 <property name="hscrollbar_policy">automatic</property>
21 <property name="vscrollbar_policy">automatic</property>
22 <child>
23 <widget class="GtkTreeView" id="queue_tree">
24 <property name="visible">True</property>
25 <property name="can_focus">True</property>
26 </widget>
27 </child>
28 </widget>
29 <packing>
30 <property name="padding">5</property>
31 <property name="position">0</property>
32 </packing>
33 </child>
34 <child>
35 <widget class="GtkVButtonBox" id="vbuttonbox1">
36 <property name="visible">True</property>
37 <property name="layout_style">spread</property>
38 <child>
39 <widget class="GtkButton" id="top_button">
40 <property name="label" translatable="yes">gtk-goto-top</property>
41 <property name="visible">True</property>
42 <property name="can_focus">True</property>
43 <property name="receives_default">True</property>
44 <property name="tooltip" translatable="yes">Move song to top in queue</property>
45 <property name="use_stock">True</property>
46 <signal name="clicked" handler="on_top_button_clicked"/>
47 </widget>
48 <packing>
49 <property name="expand">False</property>
50 <property name="fill">False</property>
51 <property name="position">0</property>
52 </packing>
53 </child>
54 <child>
55 <widget class="GtkButton" id="up_button">
56 <property name="label" translatable="yes">gtk-go-up</property>
57 <property name="visible">True</property>
58 <property name="can_focus">True</property>
59 <property name="receives_default">True</property>
60 <property name="tooltip" translatable="yes">Move song up in queue</property>
61 <property name="use_stock">True</property>
62 <signal name="clicked" handler="on_up_button_clicked"/>
63 </widget>
64 <packing>
65 <property name="expand">False</property>
66 <property name="fill">False</property>
67 <property name="position">1</property>
68 </packing>
69 </child>
70 <child>
71 <widget class="GtkButton" id="remove_button">
72 <property name="label" translatable="yes">gtk-remove</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">Remove song from queue</property>
77 <property name="use_stock">True</property>
78 <signal name="clicked" handler="on_remove_button_clicked"/>
79 </widget>
80 <packing>
81 <property name="expand">False</property>
82 <property name="fill">False</property>
83 <property name="position">2</property>
84 </packing>
85 </child>
86 <child>
87 <widget class="GtkButton" id="down_button">
88 <property name="label" translatable="yes">gtk-go-down</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 down in queue</property>
93 <property name="use_stock">True</property>
94 <signal name="clicked" handler="on_down_button_clicked"/>
95 </widget>
96 <packing>
97 <property name="expand">False</property>
98 <property name="fill">False</property>
99 <property name="position">3</property>
100 </packing>
101 </child>
102 <child>
103 <widget class="GtkButton" id="bottom_button">
104 <property name="label" translatable="yes">gtk-goto-bottom</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">Move song to bottom of queue</property>
109 <property name="use_stock">True</property>
110 <signal name="clicked" handler="on_bottom_button_clicked"/>
111 </widget>
112 <packing>
113 <property name="expand">False</property>
114 <property name="fill">False</property>
115 <property name="position">4</property>
116 </packing>
117 </child>
118 </widget>
119 <packing>
120 <property name="expand">False</property>
121 <property name="padding">5</property>
122 <property name="position">1</property>
123 </packing>
124 </child>
125 </widget>
126 <packing>
127 <property name="padding">5</property>
128 <property name="position">0</property>
129 </packing>
130 </child>
131 <child>
132 <widget class="GtkHSeparator" id="hseparator1">
133 <property name="visible">True</property>
134 </widget>
135 <packing>
136 <property name="expand">False</property>
137 <property name="padding">5</property>
138 <property name="position">1</property>
139 </packing>
140 </child>
141 <child>
142 <widget class="GtkHButtonBox" id="button_box">
143 <property name="visible">True</property>
144 <property name="spacing">5</property>
145 <property name="layout_style">end</property>
146 <child>
147 <widget class="GtkButton" id="ok_button">
148 <property name="label" translatable="yes">gtk-ok</property>
149 <property name="visible">True</property>
150 <property name="can_focus">True</property>
151 <property name="receives_default">True</property>
152 <property name="tooltip" translatable="yes">Close this dialog</property>
153 <property name="use_stock">True</property>
154 <signal name="clicked" handler="close_dialog"/>
155 </widget>
156 <packing>
157 <property name="expand">False</property>
158 <property name="fill">False</property>
159 <property name="position">1</property>
160 </packing>
161 </child>
162 <child>
163 <widget class="GtkButton" id="remove_all_button">
164 <property name="label" translatable="yes" comments="Remove all from queue">Remove _All</property>
165 <property name="visible">True</property>
166 <property name="can_focus">True</property>
167 <property name="receives_default">True</property>
168 <property name="use_underline">True</property>
169 <signal name="clicked" handler="on_remove_all_button_clicked"/>
170 </widget>
171 <packing>
172 <property name="expand">False</property>
173 <property name="fill">False</property>
174 <property name="position">0</property>
175 </packing>
176 </child>
177 </widget>
178 <packing>
179 <property name="expand">False</property>
180 <property name="padding">5</property>
181 <property name="position">2</property>
182 </packing>
183 </child>
184 </widget>
185 </child>
186 </widget>
187</glade-interface>
0188
=== modified file 'xlgui/__init__.py'
--- xlgui/__init__.py 2009-03-15 04:05:49 +0000
+++ xlgui/__init__.py 2009-04-13 02:38:36 +0000
@@ -18,7 +18,7 @@
18import gtk, gtk.glade, gobject, logging18import gtk, gtk.glade, gobject, logging
19from xl import xdg, common, event, metadata19from xl import xdg, common, event, metadata
2020
21from xlgui import guiutil, prefs, plugins, cover, commondialogs21from xlgui import guiutil, prefs, plugins, cover, commondialogs, queue
2222
23gtk.window_set_default_icon_from_file(xdg.get_data_path("images/icon.png"))23gtk.window_set_default_icon_from_file(xdg.get_data_path("images/icon.png"))
24logger = logging.getLogger(__name__)24logger = logging.getLogger(__name__)
@@ -84,6 +84,7 @@
84 'on_about_item_activate': self.show_about_dialog,84 'on_about_item_activate': self.show_about_dialog,
85 'on_scan_collection_item_activate': self.on_rescan_collection,85 'on_scan_collection_item_activate': self.on_rescan_collection,
86 'on_collection_manager_item_activate': self.collection_manager,86 'on_collection_manager_item_activate': self.collection_manager,
87 'on_queue_manager_item_activate': self.queue_manager,
87 'on_preferences_item_activate': lambda *e: self.show_preferences(),88 'on_preferences_item_activate': lambda *e: self.show_preferences(),
88 'on_plugins_item_activate': self.show_plugins,89 'on_plugins_item_activate': self.show_plugins,
89 'on_album_art_item_activate': self.show_cover_manager,90 'on_album_art_item_activate': self.show_cover_manager,
@@ -199,6 +200,10 @@
199 plugin_page=plugin_page)200 plugin_page=plugin_page)
200 dialog.run()201 dialog.run()
201202
203 def queue_manager(self, *e):
204 dialog = queue.QueueManager(self.exaile.queue)
205 dialog.show()
206
202 def collection_manager(self, *e):207 def collection_manager(self, *e):
203 """208 """
204 Invokes the collection manager dialog209 Invokes the collection manager dialog
205210
=== added file 'xlgui/queue.py'
--- xlgui/queue.py 1970-01-01 00:00:00 +0000
+++ xlgui/queue.py 2009-04-16 20:54:18 +0000
@@ -0,0 +1,200 @@
1"""
2 Queue manager dialog
3"""
4
5from xl import xdg
6from xl.nls import gettext as _
7from operator import itemgetter
8from copy import copy
9import xl.event
10import os
11import gtk
12import gtk.glade
13import logging
14
15LOG = logging.getLogger('exaile.xlgui.queue')
16
17class QueueManager(object):
18
19 """
20 GUI to manage a queue
21 """
22
23 def __init__(self, queue):
24 self._queue = queue
25
26 self._xml = gtk.glade.XML(
27 xdg.get_data_path(os.path.join('glade', 'queue_dialog.glade')),
28 'QueueManagerDialog', 'exaile')
29 self._xml.get_widget('remove_all_button').set_image(
30 gtk.image_new_from_stock(gtk.STOCK_REMOVE, gtk.ICON_SIZE_BUTTON))
31
32 self._dialog = self._xml.get_widget('QueueManagerDialog')
33 self._dialog.connect('destroy', self.destroy)
34 self._xml.signal_autoconnect({
35 'close_dialog': self.destroy,
36 'on_top_button_clicked': self.selected_to_top,
37 'on_up_button_clicked': self.selected_up,
38 'on_remove_button_clicked': self.remove_selected,
39 'on_down_button_clicked': self.selected_down,
40 'on_bottom_button_clicked': self.selected_to_bottom,
41 'on_remove_all_button_clicked': self.remove_all,
42 })
43
44 self.__setup_callbacks()
45
46 self._model = gtk.ListStore(int, str, object)
47 self._queue_view = self._xml.get_widget('queue_tree')
48 self._queue_view.set_model(self._model)
49 self.__last_tracks = []
50 self.__populate_queue()
51 self.__setup_queue()
52
53 def __setup_callbacks(self):
54 for callback in self.__callbacks():
55 xl.event.add_callback(*callback)
56
57 def __teardown_callbacks(self):
58 for callback in self.__callbacks():
59 xl.event.remove_callback(*callback)
60
61 def __populate_queue_cb(self, *e):
62 self.__populate_queue()
63
64 def __callbacks(self):
65 yield (self.__populate_queue_cb, 'playback_start')
66 yield (self.__populate_queue_cb, 'tracks_added', self._queue)
67
68 def __setup_queue(self):
69 """Adds columns to _queue_view"""
70 renderer = gtk.CellRendererText()
71 col = gtk.TreeViewColumn(_('#'), renderer, text=0)
72 col.set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE)
73 self._queue_view.append_column(col)
74
75 renderer = gtk.CellRendererText()
76 col = gtk.TreeViewColumn(_('Title'), renderer, text=1)
77 col.set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE)
78 self._queue_view.append_column(col)
79
80 def __populate_queue(self):
81 """Populates the _model with tracks"""
82 tracks = self._queue.get_ordered_tracks()
83 if tracks == self.__last_tracks:
84 LOG.debug(_("Tracks did not change, no need to update"))
85 return
86 # Find the row that will be selected
87
88 if self._queue_view is not None:
89 model, iter = self._queue_view.get_selection().get_selected()
90 if iter:
91 target = model.get_value(iter, 2)
92 try:
93 new_cursor_pos = tracks.index(target)
94 except ValueError:
95 pass
96 if 'new_cursor_pos' not in locals():
97 new_cursor_pos = None
98 self.__last_tracks = copy(tracks)
99 self._model.clear()
100 # Add the rows
101 for i, track in zip(xrange(1, len(tracks) + 1), tracks):
102 self._model.append((i, unicode(track), track))
103 # Select new row
104 if new_cursor_pos is not None and hasattr(self, '_queue_view'):
105 self._queue_view.set_cursor((new_cursor_pos,))
106
107 def show(self):
108 """
109 Displays this window
110 """
111 self._dialog.show_all()
112
113 def destroy(self, *e):
114 """
115 Destroys this window
116 """
117 self._dialog.destroy()
118 self.__teardown_callbacks()
119
120# removing items
121 def remove_selected(self, button, *userparams):
122 model, iter = self._queue_view.get_selection().get_selected()
123 if not iter:
124 return
125 i = model.get_value(iter, 0) - 1
126 self.remove(i)
127
128 def remove_all(self, button, *userparams):
129 while len(self._queue.get_ordered_tracks()):
130 self.remove(0)
131
132 def remove(self, i):
133 """Removes the ith item from the queue, 0-indexed"""
134 cur_queue = self._queue.get_ordered_tracks()
135 if i < 0 or i >= len(cur_queue):
136 LOG.error(_("Gave an invalid number to remove"))
137 return
138 cur_queue.pop(i)
139 self.__populate_queue()
140
141# Moving callbacks
142 def selected_to_top(self, button, *userparams):
143 self.reorder(lambda x, l: 0)
144
145 def selected_up(self, button, *userparams):
146 self.reorder(lambda x, l: x - 1)
147
148 def selected_down(self, button, *userparams):
149 self.reorder(lambda x, l: x + 1)
150
151 def selected_to_bottom(self, button, *userparams):
152 self.reorder(lambda x, l: l - 1)
153
154 def reorder(self, new_loc):
155 """Reorders the tracks in the queue.
156
157 new_loc is a function that takes two variables, x and l. x will be the
158 the index of the currently selected item. l will be the size of the
159 queue. The function need not bounds check.
160
161 """
162 model, iter = self._queue_view.get_selection().get_selected()
163 if not iter:
164 return
165
166 i = model.get_value(iter, 0) - 1
167 tracks = self._queue.get_ordered_tracks()
168 if callable(new_loc):
169 new_loc = new_loc(i, len(tracks))
170 if new_loc < 0 or new_loc >= len(tracks):
171 new_loc = i
172
173 new_order = list(zip(range(len(tracks)), tracks))
174 new_order[new_loc], new_order[i] = new_order[i], new_order[new_loc]
175
176 self._queue.set_ordered_tracks(list(map(itemgetter(1), new_order)))
177 self.__populate_queue()
178
179def main():
180 class Track(object):
181 def __init__(self, title):
182 self.tags = {'title': title}
183 def __unicode__(self):
184 return self.tags['title']
185 def __str(self):
186 return str(unicode(self))
187 class Foo(object):
188 ordered_tracks = [Track('Track Foo by bar on baz'), Track('bar')]
189 get_ordered_tracks = lambda self: self.ordered_tracks
190 def set_ordered_tracks(self, v):
191 self.ordered_tracks = v
192 dialog = QueueManager(Foo())
193 dialog.show()
194 try:
195 gtk.main()
196 except KeyboardInterrupt:
197 pass
198
199if __name__ == "__main__":
200 main()