dataprovider: search_pattern added to dbus signal
[mswl2010:jamp.git] / ui / jampui / jampui / gui.py
1 #
2 # Copyright (C) 2010 Igalia S.L.
3 #
4 # Contact: mswl-dm-2009@igalia.com
5 #
6 # This library is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU Lesser General Public License
8 # as published by the Free Software Foundation; version 2.1 of
9 # the License, or (at your option) any later version.
10 #
11 # This library is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 # Lesser General Public License for more details.
15 #
16 # You should have received a copy of the GNU Lesser General Public
17 # License along with this library; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
19 # 02110-1301 USA
20 #
21 #
22
23 from jampui.util import constants
24 from jampui.playlist import playlist
25
26 import sys
27 import pygtk
28 pygtk.require("2.0")
29 import gtk
30 import gobject
31 import dbus
32 import dbus.mainloop.glib
33
34 PLAYING_STATE_STOP = 0
35 PLAYING_STATE_START = 1
36 PLAYING_STATE_PAUSED = 2
37
38
39
40 class Jamp:
41     MAIN_TAB = 0
42     SEARCH_TAB = 1
43
44     def tick_signal_handler(self, position, duration):
45         self._songAdjustment.value = position
46         self._songAdjustment.upper = duration
47
48         self._songPosition.set_text(self.time_on_nanoseconds_to_string(position))
49         self._songDuration.set_text(self.time_on_nanoseconds_to_string(duration))
50
51     def dataprovider_signal_handler(self, search_pattern, n_tracks, tracks):
52         self._searchLabel.set_text(str(n_tracks) + " tracks found")
53
54         self._query_search_tree_view = QuerySearchTreeView()
55         self._query_search_tree_view.connect("row_activated", self.query_search_activated_cb)
56
57         for trackpath in tracks:
58             proxy = self.bus.get_object("org.mswl.jamp", trackpath)
59
60             name = proxy.GetName(dbus_interface='org.mswl.jamp.track')
61             duration = proxy.GetDuration(dbus_interface='org.mswl.jamp.track')
62             id = proxy.GetId(dbus_interface='org.mswl.jamp.track')
63             stream = proxy.GetStream(dbus_interface='org.mswl.jamp.track')
64             uri = proxy.GetUrl(dbus_interface='org.mswl.jamp.track')
65
66             track = {'name' : name,
67                      'duration' : self.time_on_seconds_to_string(duration),
68                      'id' : id,
69                      'stream' : stream,
70                      'uri' : uri,
71                      }
72
73             self._query_search_tree_view.append_track([track, track['name'], track['duration']])
74
75         search_window = gtk.ScrolledWindow()
76         search_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
77         search_window.add(self._query_search_tree_view)
78         search_window.show()
79
80         label = gtk.Label('search')
81         self._notebook.append_page(search_window, label)
82         #self._search_box.add(self._query_search_tree_view)
83         self._notebook.set_current_page(2)
84         self.window.show_all()
85
86     def __init__(self):
87
88         self._reproduction_state = PLAYING_STATE_STOP
89
90         # builder
91         self._builder = gtk.Builder()
92         self._builder.add_from_file(constants.UI_XML_FILE)
93         self._builder.connect_signals(self)
94
95         # d-bus
96         dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
97         self.bus = dbus.SessionBus()
98
99         self.jmp_mplayer = self.bus.get_object("org.mswl.jamp", "/Player")
100         self.jmp_mplayer.connect_to_signal('tick',
101                                            self.tick_signal_handler,
102                                            'org.mswl.jamp')
103
104         self.jmp_dataprovider = self.bus.get_object("org.mswl.jamp", "/DataProvider")
105         self.jmp_dataprovider.connect_to_signal('response_received',
106                                            self.dataprovider_signal_handler,
107                                            'org.mswl.jamp.dataprovider')
108
109         # playlist
110         self._playlist_manager = playlist.PlaylistManager()
111
112         # widgets
113         self.window = self._builder.get_object("mainWindow")
114         self._playlist_box = self._builder.get_object("playListScrolledWindow")
115 #        self._notebook = self._builder.get_object("mainNotebook")
116         self._mainHBox = self._builder.get_object("mainHBox")
117         self._playButton = self._builder.get_object("playButton")
118         self._search_entry = self._builder.get_object("searchEntry")
119         self._songPercent = self._builder.get_object("songPercent")
120         self._songAdjustment = self._builder.get_object("songAdjustment")
121         self._volumeAdjustment = self._builder.get_object("volumeAdjustment")
122         self._songPosition = self._builder.get_object("songPosition")
123         self._songDuration = self._builder.get_object("songDuration")
124         self._searchLabel = self._builder.get_object("searchLabel")
125
126         self._playlist_tree_view = PlaylistTreeView()
127         self._playlist_box.add(self._playlist_tree_view)
128         self._playlist_tree_view.add_playlists(self._playlist_manager.playlist_list)
129         self._playlist_tree_view.connect("row_activated", self.playlist_activated_cb)
130
131         self._search_tree_view = SearchTreeView()
132         self._search_tree_view.connect("row_activated", self.track_activated_cb)
133
134         self._notebook = gtk.Notebook()
135         self._mainHBox.add(self._notebook)
136         self._query_tracks = []
137 #        self._search_box.add(self._search_tree_view)
138
139         self.window.show_all()
140
141
142     def run(self):
143         gtk.main()
144
145     def search_entry_activate_cb(self, entry):
146         print "SEARCH ON ACTIVATE"
147         self.search()
148
149     def search_entry_icon_press_cb(self, entry, icon_pos, event):
150         print "SEARCH ON ICON"
151         self.search()
152
153     def next_button_clicked_cb(self, button):
154         print "NEXT_BUTTON"
155         if self._search_tree_view.inc_cursor():
156             self.stop()
157             self.play_selected_song()
158
159     def prev_button_clicked_cb(self, button):
160         print "PREV_BUTTON"
161         if self._search_tree_view.dec_cursor():
162             self.stop()
163             self.play_selected_song()
164
165     def play_selected_song(self):
166         model, list = self._search_tree_view.get_selection().get_selected_rows()
167         if list == []:
168             return
169         iter = model.get_iter(list[len(list) - 1])
170         self.set_uri(model.get_value(iter, 0).location)
171         self.play()
172
173     def play_button_clicked_cb(self, button):
174         print "PLAY_BUTTON"
175         model, iter = self._search_tree_view.get_selection().get_selected()
176         if self._reproduction_state == PLAYING_STATE_START:
177             self.pause()
178         else:
179             if self._reproduction_state == PLAYING_STATE_PAUSED:
180                 self.play()
181             else:
182                 if iter != None:
183                     self.play_selected_song()
184
185     def set_uri(self, uri):
186         self.jmp_mplayer.SetUri(uri, dbus_interface='org.mswl.jamp')
187         self.sync_volume(100)
188
189     def play(self):
190         self._reproduction_state = PLAYING_STATE_START
191         self._playButton.set_stock_id("gtk-media-pause")
192         self.jmp_mplayer.Play(dbus_interface='org.mswl.jamp')
193
194     def stop(self):
195         if self._reproduction_state != PLAYING_STATE_STOP:
196             self._reproduction_state = PLAYING_STATE_STOP
197             self._playButton.set_stock_id("gtk-media-play")
198             self.jmp_mplayer.Stop(dbus_interface='org.mswl.jamp')
199
200     def pause(self):
201         self._reproduction_state = PLAYING_STATE_PAUSED
202         self._playButton.set_stock_id("gtk-media-play")
203         self.jmp_mplayer.Pause(dbus_interface='org.mswl.jamp')
204
205     def playlist_activated_cb(self, treeview, path, view_column):
206         model, iter = treeview.get_selection().get_selected()
207         self._search_tree_view.set_content(model.get_value(iter, 0).track_list)
208
209         playlist_window = gtk.ScrolledWindow()
210         playlist_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
211         playlist_window.add(self._search_tree_view)
212
213         label = gtk.Label('playlist')
214         self._notebook.append_page(playlist_window, label)
215         self._notebook.show_all()
216         self._notebook.set_current_page(self.SEARCH_TAB)
217         print "PLAYLIST CLICKED"
218
219     def track_activated_cb(self, treeview, path, view_column):
220         model, iter = treeview.get_selection().get_selected()
221         print model.get_value(iter, 0).title
222         if self._reproduction_state != PLAYING_STATE_STOP:
223             self.stop()
224         self.set_uri(model.get_value(iter, 0).location)
225         self.play()
226         print "TRACK ACTIVATED"
227
228     def query_search_activated_cb(self, treeview, path, view_column):
229         model, iter = treeview.get_selection().get_selected()
230         if self._reproduction_state != PLAYING_STATE_STOP:
231             self.stop()
232         self.set_uri(model.get_value(iter, 0)['stream'])
233         self.play()
234         print "TRACK IN SEARCH ACTIVATED"
235
236     def on_menuitem_new_activate(self, menuitem):
237         playlist_dialog = self._builder.get_object("newPlaylistDialog")
238         print playlist_dialog
239         playlist_title_entry = self._builder.get_object("titleEntry")
240         playlist_creator_entry = self._builder.get_object("creatorEntry")
241         playlist_info_entry = self._builder.get_object("infoEntry")
242
243         result = playlist_dialog.run()
244         if result == 1:
245             created_playlist = self._playlist_manager.new(playlist_title_entry.get_text(),
246                                                           playlist_creator_entry.get_text(),
247                                                           playlist_info_entry.get_text())
248             self._playlist_tree_view.add_playlist(created_playlist)
249         playlist_dialog.hide()
250
251     def main_window_destroy_cb(self, widget):
252         print "EXIT"
253         if self._reproduction_state != PLAYING_STATE_STOP:
254             self.stop()
255         self._playlist_manager.save()
256         gtk.main_quit()
257
258     def search(self):
259         query = self._search_entry.get_text()
260         if (len(query)):
261             self.jmp_dataprovider.QueryTracks(query, 20, 0)
262
263
264     def on_menuitem_play_activate(self, widget):
265         print "PLAY_MENU"
266
267     def on_menuitem_pause_activate(self, widget):
268         print "PAUSE_MENU"
269
270     def on_menuitem_next_activate(self, widget):
271         print "NEXT_MENU"
272
273     def on_menuitem_prev_activate(self, widget):
274         print "PREV_MENU"
275
276     def on_menuitem_quit_activate(self, widget):
277         print "QUIT_MENU"
278         if self._reproduction_state != PLAYING_STATE_STOP:
279             self.stop()
280         self._playlist_manager.save()
281         gtk.main_quit()
282
283     def on_songPercent_change_value(self, range, scroll, value):
284         self._songPosition.set_text(self.time_on_nanoseconds_to_string(int(value)))
285         self.jmp_mplayer.Seek(value, dbus_interface='org.mswl.jamp')
286
287     def sync_volume(self, volume):
288         self.jmp_mplayer.SetVolume(volume / 100, dbus_interface='org.mswl.jamp')
289
290     def on_volumeButton_value_changed(self, scalebutton, value):
291         self.sync_volume(value)
292
293     def time_on_seconds_to_string(self, total_seconds):
294         seconds_are_less_than_ten = ""
295
296         seconds = total_seconds % 60
297         minutes = total_seconds / 60
298
299         if seconds < 10:
300             seconds_are_less_than_ten = "0"
301
302         return str(minutes) + ":" + seconds_are_less_than_ten + str(seconds)
303
304     def time_on_nanoseconds_to_string(self, nanoseconds):
305
306         seconds = nanoseconds / 1000000000
307
308         return self.time_on_seconds_to_string(seconds)
309
310 class QuerySearchTreeView(gtk.TreeView):
311     def __init__(self):
312         gtk.TreeView.__init__(self)
313         self._search_table_model = gtk.ListStore(gobject.TYPE_PYOBJECT,
314                                                    gobject.TYPE_STRING,
315                                                    gobject.TYPE_STRING)
316         self.set_model(self._search_table_model)
317
318         column_render = gtk.CellRendererText()
319
320         column = gtk.TreeViewColumn("Song", column_render, text=1)
321         column.set_sort_column_id(1)
322         self.append_column(column)
323
324         column = gtk.TreeViewColumn("Duration", column_render, text=2)
325         column.set_sort_column_id(2)
326         self.append_column(column)
327
328     def append_track(self, track):
329         self._search_table_model.append(track)
330
331
332 class PlaylistTreeView(gtk.TreeView):
333     def __init__(self):
334         gtk.TreeView.__init__(self)
335         self._playlist_table_model = gtk.ListStore(gobject.TYPE_PYOBJECT,
336                                                    gobject.TYPE_STRING)
337         self.set_model(self._playlist_table_model)
338         name_column_render = gtk.CellRendererText()
339         name_column = gtk.TreeViewColumn ("Playlist", name_column_render, text=1)
340         self.append_column (name_column)
341
342         self._playlist_table_model.clear()
343
344     def add_playlist(self, playlist):
345         self._playlist_table_model.append([playlist, playlist.title])
346
347     def add_playlists(self, playlist):
348         for item in playlist:
349             self._playlist_table_model.append([item, item.title])
350
351 class SearchTreeView(gtk.TreeView):
352     def __init__(self):
353
354         gtk.TreeView.__init__(self)
355         self._song_table_model = gtk.ListStore(gobject.TYPE_PYOBJECT,
356                                                gobject.TYPE_STRING,
357                                                gobject.TYPE_STRING,
358                                                gobject.TYPE_STRING)
359         self.set_model(self._song_table_model)
360
361         column_render = gtk.CellRendererText()
362
363         column = gtk.TreeViewColumn("Title", column_render, text=1)
364         column.set_sort_column_id(1)
365         self.append_column(column)
366
367         column = gtk.TreeViewColumn("Artist", column_render, text=2)
368         column.set_sort_column_id(2)
369         self.append_column(column)
370
371         column = gtk.TreeViewColumn("Album", column_render, text=3)
372         column.set_sort_column_id(3)
373         self.append_column(column)
374
375     def inc_cursor(self):
376         model, iter = self.get_selection().get_selected()
377         self.get_selection().unselect_all()
378         if iter == None:
379             return False
380         if self.get_model().iter_next(iter) == None:
381             iter = model.get_iter_first()
382             path = model.get_path(iter)
383             self.set_cursor(path, None, False)
384             return True
385         iter_next = model.iter_next(iter)
386         path = model.get_path(iter_next)
387         self.set_cursor(path, None, False)
388         return True
389
390     def dec_cursor(self):
391         model, iter = self.get_selection().get_selected()
392         self.get_selection().unselect_all()
393         if iter:
394             path = model.get_path(iter)
395             if path[0] == 0:
396                 self.set_cursor(self.get_model().iter_n_children(None) - 1,
397                                 None,
398                                 False)
399                 return True
400             self.set_cursor(path[0] - 1, None, False)
401             return True
402         return False
403
404     def set_content(self, track_list):
405         self._song_table_model.clear()
406         for track in track_list:
407             self._song_table_model.append([track, track.title, track.creator, track.album])
408
409 if __name__ == "__main__":
410     jamp = Jamp()
411     jamp.run()