Initial playlist support
[jamaendo] / jamaui / ui.py
1 #!/usr/bin/env python
2 #
3 # This file is part of Jamaendo.
4 # Copyright (c) 2010 Kristoffer Gronlund
5 #
6 # Jamaendo is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
10 #
11 # Jamaendo is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with Jamaendo.  If not, see <http://www.gnu.org/licenses/>.
18 #
19 # Player code heavily based on http://thpinfo.com/2008/panucci/:
20 #  A resuming media player for Podcasts and Audiobooks
21 #  Copyright (c) 2008-05-26 Thomas Perl <thpinfo.com>
22 #  (based on http://pygstdocs.berlios.de/pygst-tutorial/seeking.html)
23 #
24 # Jamaendo jamendo.com API wrapper licensed under the New BSD License;
25 # see module for details.
26 #
27
28 import os, sys
29 import gtk
30 import gobject
31 import util
32 import logging
33 from settings import settings
34
35 import ossohelper
36
37 gobject.threads_init()
38
39 log = logging.getLogger(__name__)
40
41 VERSION = '0.2'
42
43 try:
44     import hildon
45 except:
46     if util.platform == 'maemo':
47         log.critical('Using GTK widgets, install "python2.5-hildon" '
48             'for this to work properly.')
49     else:
50         log.critical('This ui only works in maemo')
51         sys.exit(1)
52
53 from dbus.mainloop.glib import DBusGMainLoop
54
55 DBusGMainLoop(set_as_default=True)
56
57 import jamaendo
58
59 from postoffice import postoffice
60 from playerwindow import open_playerwindow
61 from search import SearchWindow
62 from featured import FeaturedWindow
63 from radios import RadiosWindow
64 from favorites import FavoritesWindow
65 from playlists import PlaylistsWindow
66 from listbox import ButtonListDialog
67
68 class Jamaui(object):
69     def __init__(self):
70         self.app = None
71         self.menu = None
72         self.window = None
73
74     def create_window(self):
75         log.debug("Creating main window...")
76         self.app = hildon.Program()
77         self.window = hildon.StackableWindow()
78         self.app.add_window(self.window)
79
80         self.window.set_title("jamaendo")
81
82         self.window.connect("destroy", self.destroy)
83
84         self.CONFDIR = os.path.expanduser('~/MyDocs/.jamaendo')
85         jamaendo.set_cache_dir(self.CONFDIR)
86         settings.set_filename(os.path.join(self.CONFDIR, 'ui_settings'))
87         settings.load()
88
89         postoffice.connect('request-album-cover', self, self.on_request_cover)
90         postoffice.connect('request-images', self, self.on_request_images)
91         log.debug("Created main window.")
92
93     def create_menu(self):
94         self.menu = hildon.AppMenu()
95
96         #search = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
97         #search.set_label("Search")
98         #search.connect("clicked", self.on_search)
99         #self.menu.append(search)
100
101         player = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
102         player.set_label("Open player")
103         player.connect("clicked", self.on_player)
104         self.menu.append(player)
105
106         player = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
107         player.set_label("Favorites")
108         player.connect("clicked", self.on_favorites)
109         self.menu.append(player)
110
111         player = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
112         player.set_label("Playlists")
113         player.connect("clicked", self.on_playlists)
114         self.menu.append(player)
115
116         player = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
117         player.set_label("Settings")
118         player.connect("clicked", self.on_settings)
119         self.menu.append(player)
120
121
122         # Don't use localdb ATM
123         #refresh = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
124         #refresh.set_label("Refresh")
125         #refresh.connect("clicked", self.on_refresh)
126         #self.menu.append(refresh)
127
128         menu_about = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
129         menu_about.set_label("About")
130         menu_about.connect("clicked", self.show_about, self.window)
131         self.menu.append(menu_about)
132         gtk.about_dialog_set_url_hook(self.open_link, None)
133
134         self.menu.show_all()
135         self.window.set_app_menu(self.menu)
136
137
138     def setup_widgets(self):
139         bgimg = util.find_resource('bg.png')
140         if bgimg:
141             background, mask = gtk.gdk.pixbuf_new_from_file(bgimg).render_pixmap_and_mask()
142             self.window.realize()
143             self.window.window.set_back_pixmap(background, False)
144
145         bbox = gtk.HButtonBox()
146         alignment = gtk.Alignment(xalign=0.2, yalign=0.925, xscale=1.0)
147         alignment.add(bbox)
148         bbox.set_property('layout-style', gtk.BUTTONBOX_SPREAD)
149         self.bbox = bbox
150         self.window.add(alignment)
151
152         self.add_mainscreen_button("Featured", "Most listened to", self.on_featured)
153         self.add_mainscreen_button("Radios", "The best in free music", self.on_radios)
154         self.add_mainscreen_button("Search", "Search for artists/albums", self.on_search)
155
156         self.window.show_all()
157
158     def add_mainscreen_button(self, title, subtitle, callback):
159         btn = hildon.Button(gtk.HILDON_SIZE_THUMB_HEIGHT,
160                             hildon.BUTTON_ARRANGEMENT_VERTICAL)
161         btn.set_text(title, subtitle)
162         btn.set_property('width-request', 225)
163         btn.connect('clicked', callback)
164         self.bbox.add(btn)
165
166     def on_request_cover(self, albumid, size):
167         jamaendo.get_album_cover_async(self.got_album_cover, int(albumid), size)
168
169     def on_request_images(self, urls):
170         jamaendo.get_images_async(self.got_images, urls)
171
172     def got_album_cover(self, albumid, size, cover):
173         postoffice.notify('album-cover', albumid, size, cover)
174
175     def got_images(self, images):
176         postoffice.notify('images', images)
177
178     #def add_featured_button(self):
179     #    self.featured_sel = hildon.TouchSelector(text=True)
180     #    self.featured_sel.append_text("Albums of the week")
181     #    self.featured_sel.append_text("Tracks of the week")
182     #    self.featured_sel.append_text("New releases")
183     #    btn = hildon.PickerButton(gtk.HILDON_SIZE_THUMB_HEIGHT,
184     #                              hildon.BUTTON_ARRANGEMENT_VERTICAL)
185     #    btn.set_text("Featured", "Most listened to")
186     #    btn.set_property('width-request', 225)
187     #    btn.set_selector(self.featured_sel)
188     #    self.featured_btn = btn
189     #    self.bbox.add(btn)
190
191     def destroy(self, widget):
192         postoffice.disconnect(['request-album-cover', 'request-images'], self)
193         settings.save()
194         from player import the_player
195         if the_player:
196             the_player.stop()
197         gtk.main_quit()
198
199     def show_about(self, w, win):
200         dialog = gtk.AboutDialog()
201         dialog.set_program_name("jamaendo")
202         dialog.set_website("http://jamaendo.garage.maemo.org/")
203         dialog.set_website_label("http://jamaendo.garage.maemo.org/")
204         dialog.set_version(VERSION)
205         dialog.set_license("""Copyright (c) 2010, Kristoffer Gronlund
206 All rights reserved.
207
208 Redistribution and use in source and binary forms, with or without
209 modification, are permitted provided that the following conditions are met:
210      * Redistributions of source code must retain the above copyright
211        notice, this list of conditions and the following disclaimer.
212      * Redistributions in binary form must reproduce the above copyright
213        notice, this list of conditions and the following disclaimer in the
214        documentation and/or other materials provided with the distribution.
215      * Neither the name of Jamaendo nor the
216        names of its contributors may be used to endorse or promote products
217        derived from this software without specific prior written permission.
218
219 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
220 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
221 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
222 DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
223 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
224 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
225 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
226 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
227 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
228 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
229 """)
230         dialog.set_authors(("Kristoffer Gronlund <kristoffer.gronlund@purplescout.se>",
231                             "Based on Panucci, written by Thomas Perl <thpinfo.com>"))
232         dialog.set_comments("""Jamaendo plays music from the music catalog of JAMENDO.
233
234 JAMENDO is an online platform that distributes musical works under Creative Commons licenses.""")
235         gtk.about_dialog_set_email_hook(self.open_link, dialog)
236         gtk.about_dialog_set_url_hook(self.open_link, dialog)
237         dialog.connect( 'response', lambda dlg, response: dlg.destroy())
238         for parent in dialog.vbox.get_children():
239             for child in parent.get_children():
240                 if isinstance(child, gtk.Label):
241                     child.set_selectable(False)
242                     child.set_alignment(0.0, 0.5)
243         dialog.run()
244         dialog.destroy()
245
246     def open_link(self, d, url, data):
247         #print "url: %s" % (url)
248         import webbrowser
249         webbrowser.open_new(url)
250
251
252     #def on_refresh(self, button):
253     #    dialog = RefreshDialog()
254     #    dialog.show_all()
255     #    dialog.run()
256     #    dialog.hide()
257
258     def on_featured(self, button):
259         dialog = ButtonListDialog('Featured', self.window)
260         def fn(btn, feature):
261             self.featuredwnd = FeaturedWindow(feature)
262             self.featuredwnd.show_all()
263             dialog.response(gtk.RESPONSE_OK)
264         for feature, _ in FeaturedWindow.features:
265             dialog.add_button(feature, fn, feature)
266         dialog.show_all()
267         dialog.run()
268         dialog.destroy()
269
270     def on_radios(self, button):
271         self.radioswnd = RadiosWindow()
272         self.radioswnd.show_all()
273
274     def on_search(self, button):
275         self.searchwnd = SearchWindow()
276         self.searchwnd.show_all()
277
278     def on_playlists(self, button):
279         self.playlistswnd = PlaylistsWindow()
280         self.playlistswnd.show_all()
281
282     def on_settings(self, button):
283         dialog = gtk.Dialog()
284         dialog.set_title("Settings")
285         dialog.add_button( gtk.STOCK_OK, gtk.RESPONSE_OK )
286         vbox = dialog.vbox
287         tbl = gtk.Table(1, 2)
288         tbl.attach(gtk.Label("Username:"), 0, 1, 0, 1)
289         entry = hildon.Entry(gtk.HILDON_SIZE_FINGER_HEIGHT)
290         entry.set_placeholder("jamendo.com username")
291         if settings.user:
292             entry.set_text(settings.user)
293         tbl.attach(entry, 1, 2, 0, 1)
294         vbox.pack_start(tbl, True, True, 2)
295         dialog.show_all()
296         result = dialog.run()
297         val = entry.get_text()
298         dialog.destroy()
299         #print val, result
300         if val and result == gtk.RESPONSE_OK:
301             #print "new user name:", val
302             settings.user = val
303             settings.save()
304
305
306     def on_favorites(self, button):
307         self.favoriteswnd = FavoritesWindow()
308         self.favoriteswnd.show_all()
309
310     def on_player(self, button):
311         open_playerwindow()
312
313     '''
314     def on_search(self, button):
315         if self.searchbar:
316             self.searchbar.show()
317         else:
318             self.searchstore = gtk.ListStore(gobject.TYPE_STRING)
319             iter = self.searchstore.append()
320             self.searchstore.set(iter, 0, "Test1")
321             iter = self.searchstore.append()
322             self.searchstore.set(iter, 0, "Test2")
323             self.searchbar = hildon.FindToolbar("Search", self.searchstore, 0)
324             self.searchbar.set_active(0)
325             self.window.add_toolbar(self.searchbar)
326             self.searchbar.show()
327     '''
328
329     def run(self):
330         ossohelper.application_init('org.jamaendo', '0.1')
331         self.create_window()
332         self.create_menu()
333         self.setup_widgets()
334         self.window.show_all()
335         gtk.main()
336         ossohelper.application_exit()
337
338 if __name__=="__main__":
339     ui = Jamaui()
340     ui.run()
341