Major list upgrade - now images in most places, and when selecting a featured track...
[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
66 class PlaylistsWindow(hildon.StackableWindow):
67     def __init__(self):
68         hildon.StackableWindow.__init__(self)
69         self.set_title("Playlists")
70
71         label = gtk.Label("Playlists")
72         vbox = gtk.VBox(False, 0)
73         vbox.pack_start(label, True, True, 0)
74         self.add(vbox)
75
76 class Jamaui(object):
77     def __init__(self):
78         self.app = None
79         self.menu = None
80         self.window = None
81
82     def create_window(self):
83         log.debug("Creating main window...")
84         self.app = hildon.Program()
85         self.window = hildon.StackableWindow()
86         self.app.add_window(self.window)
87
88         self.window.set_title("jamaendo")
89
90         self.window.connect("destroy", self.destroy)
91
92         self.CONFDIR = os.path.expanduser('~/MyDocs/.jamaendo')
93         jamaendo.set_cache_dir(self.CONFDIR)
94         settings.set_filename(os.path.join(self.CONFDIR, 'ui_settings'))
95         settings.load()
96
97         postoffice.connect('request-album-cover', self, self.on_request_cover)
98         postoffice.connect('request-images', self, self.on_request_images)
99         log.debug("Created main window.")
100
101     def save_settings(self):
102         settings.save()
103
104     def create_menu(self):
105         self.menu = hildon.AppMenu()
106
107         #search = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
108         #search.set_label("Search")
109         #search.connect("clicked", self.on_search)
110         #self.menu.append(search)
111
112         player = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
113         player.set_label("Open player")
114         player.connect("clicked", self.on_player)
115         self.menu.append(player)
116
117         player = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
118         player.set_label("Favorites")
119         player.connect("clicked", self.on_favorites)
120         self.menu.append(player)
121
122         #player = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
123         #player.set_label("Playlists")
124         #player.connect("clicked", self.on_playlists)
125         #self.menu.append(player)
126
127         player = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
128         player.set_label("Settings")
129         player.connect("clicked", self.on_settings)
130         self.menu.append(player)
131
132
133         # Don't use localdb ATM
134         #refresh = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
135         #refresh.set_label("Refresh")
136         #refresh.connect("clicked", self.on_refresh)
137         #self.menu.append(refresh)
138
139         menu_about = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
140         menu_about.set_label("About")
141         menu_about.connect("clicked", self.show_about, self.window)
142         self.menu.append(menu_about)
143         gtk.about_dialog_set_url_hook(self.open_link, None)
144
145         self.menu.show_all()
146         self.window.set_app_menu(self.menu)
147
148
149     def setup_widgets(self):
150         bgimg = util.find_resource('bg.png')
151         if bgimg:
152             background, mask = gtk.gdk.pixbuf_new_from_file(bgimg).render_pixmap_and_mask()
153             self.window.realize()
154             self.window.window.set_back_pixmap(background, False)
155
156         bbox = gtk.HButtonBox()
157         alignment = gtk.Alignment(xalign=0.2, yalign=0.925, xscale=1.0)
158         alignment.add(bbox)
159         bbox.set_property('layout-style', gtk.BUTTONBOX_SPREAD)
160         self.bbox = bbox
161         self.window.add(alignment)
162
163         self.add_mainscreen_button("Featured", "Most listened to", self.on_featured)
164         self.add_mainscreen_button("Radios", "The best in free music", self.on_radios)
165         self.add_mainscreen_button("Search", "Search for artists/albums", self.on_search)
166
167         self.window.show_all()
168
169     def add_mainscreen_button(self, title, subtitle, callback):
170         btn = hildon.Button(gtk.HILDON_SIZE_THUMB_HEIGHT,
171                             hildon.BUTTON_ARRANGEMENT_VERTICAL)
172         btn.set_text(title, subtitle)
173         btn.set_property('width-request', 225)
174         btn.connect('clicked', callback)
175         self.bbox.add(btn)
176
177     def on_request_cover(self, albumid, size):
178         jamaendo.get_album_cover_async(self.got_album_cover, int(albumid), size)
179
180     def on_request_images(self, urls):
181         jamaendo.get_images_async(self.got_images, urls)
182
183     def got_album_cover(self, albumid, size, cover):
184         postoffice.notify('album-cover', albumid, size, cover)
185
186     def got_images(self, images):
187         postoffice.notify('images', images)
188
189     #def add_featured_button(self):
190     #    self.featured_sel = hildon.TouchSelector(text=True)
191     #    self.featured_sel.append_text("Albums of the week")
192     #    self.featured_sel.append_text("Tracks of the week")
193     #    self.featured_sel.append_text("New releases")
194     #    btn = hildon.PickerButton(gtk.HILDON_SIZE_THUMB_HEIGHT,
195     #                              hildon.BUTTON_ARRANGEMENT_VERTICAL)
196     #    btn.set_text("Featured", "Most listened to")
197     #    btn.set_property('width-request', 225)
198     #    btn.set_selector(self.featured_sel)
199     #    self.featured_btn = btn
200     #    self.bbox.add(btn)
201
202     def destroy(self, widget):
203         postoffice.disconnect(['request-album-cover', 'request-images'], self)
204         self.save_settings()
205         from player import the_player
206         if the_player:
207             the_player.stop()
208         gtk.main_quit()
209
210     def show_about(self, w, win):
211         dialog = gtk.AboutDialog()
212         dialog.set_program_name("jamaendo")
213         dialog.set_website("http://jamaendo.garage.maemo.org/")
214         dialog.set_website_label("http://jamaendo.garage.maemo.org/")
215         dialog.set_version(VERSION)
216         dialog.set_license("""Copyright (c) 2010, Kristoffer Gronlund
217 All rights reserved.
218
219 Redistribution and use in source and binary forms, with or without
220 modification, are permitted provided that the following conditions are met:
221      * Redistributions of source code must retain the above copyright
222        notice, this list of conditions and the following disclaimer.
223      * Redistributions in binary form must reproduce the above copyright
224        notice, this list of conditions and the following disclaimer in the
225        documentation and/or other materials provided with the distribution.
226      * Neither the name of Jamaendo nor the
227        names of its contributors may be used to endorse or promote products
228        derived from this software without specific prior written permission.
229
230 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
231 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
232 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
233 DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
234 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
235 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
236 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
237 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
238 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
239 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
240 """)
241         dialog.set_authors(("Kristoffer Gronlund <kristoffer.gronlund@purplescout.se>",
242                             "Based on Panucci, written by Thomas Perl <thpinfo.com>"))
243         dialog.set_comments("""Jamaendo plays music from the music catalog of JAMENDO.
244
245 JAMENDO is an online platform that distributes musical works under Creative Commons licenses.""")
246         gtk.about_dialog_set_email_hook(self.open_link, dialog)
247         gtk.about_dialog_set_url_hook(self.open_link, dialog)
248         dialog.connect( 'response', lambda dlg, response: dlg.destroy())
249         for parent in dialog.vbox.get_children():
250             for child in parent.get_children():
251                 if isinstance(child, gtk.Label):
252                     child.set_selectable(False)
253                     child.set_alignment(0.0, 0.5)
254         dialog.run()
255         dialog.destroy()
256
257     def open_link(self, d, url, data):
258         #print "url: %s" % (url)
259         import webbrowser
260         webbrowser.open_new(url)
261
262
263     #def on_refresh(self, button):
264     #    dialog = RefreshDialog()
265     #    dialog.show_all()
266     #    dialog.run()
267     #    dialog.hide()
268
269     def on_featured(self, button):
270         dialog = hildon.PickerDialog(self.window)
271         sel = hildon.TouchSelectorEntry(text=True)
272         for feature, _ in FeaturedWindow.features:
273             sel.append_text(feature)
274         dialog.set_selector(sel)
275         dialog.set_title("Featured")
276         sel.unselect_all(0)
277         if dialog.run() == gtk.RESPONSE_OK:
278             txt = sel.get_current_text()
279             self.featuredwnd = FeaturedWindow(txt)
280             self.featuredwnd.show_all()
281         dialog.destroy()
282
283     def on_radios(self, button):
284         self.radioswnd = RadiosWindow()
285         self.radioswnd.show_all()
286
287     def on_search(self, button):
288         self.searchwnd = SearchWindow()
289         self.searchwnd.show_all()
290
291     def on_playlists(self, button):
292         self.playlistswnd = PlaylistsWindow()
293         self.playlistswnd.show_all()
294
295     def on_settings(self, button):
296         dialog = gtk.Dialog()
297         dialog.set_title("Settings")
298         dialog.add_button( gtk.STOCK_OK, gtk.RESPONSE_OK )
299         vbox = dialog.vbox
300         tbl = gtk.Table(1, 2)
301         tbl.attach(gtk.Label("Username:"), 0, 1, 0, 1)
302         entry = hildon.Entry(gtk.HILDON_SIZE_FINGER_HEIGHT)
303         entry.set_placeholder("jamendo.com username")
304         if settings.user:
305             entry.set_text(settings.user)
306         tbl.attach(entry, 1, 2, 0, 1)
307         vbox.pack_start(tbl, True, True, 2)
308         dialog.show_all()
309         result = dialog.run()
310         val = entry.get_text()
311         dialog.destroy()
312         #print val, result
313         if val and result == gtk.RESPONSE_OK:
314             #print "new user name:", val
315             settings.user = val
316             self.save_settings()
317
318
319     def on_favorites(self, button):
320         self.favoriteswnd = FavoritesWindow()
321         self.favoriteswnd.show_all()
322
323     def on_player(self, button):
324         open_playerwindow()
325
326     '''
327     def on_search(self, button):
328         if self.searchbar:
329             self.searchbar.show()
330         else:
331             self.searchstore = gtk.ListStore(gobject.TYPE_STRING)
332             iter = self.searchstore.append()
333             self.searchstore.set(iter, 0, "Test1")
334             iter = self.searchstore.append()
335             self.searchstore.set(iter, 0, "Test2")
336             self.searchbar = hildon.FindToolbar("Search", self.searchstore, 0)
337             self.searchbar.set_active(0)
338             self.window.add_toolbar(self.searchbar)
339             self.searchbar.show()
340     '''
341
342     def run(self):
343         ossohelper.application_init('org.jamaendo', '0.1')
344         self.create_window()
345         self.create_menu()
346         self.setup_widgets()
347         self.window.show_all()
348         gtk.main()
349         ossohelper.application_exit()
350
351 if __name__=="__main__":
352     ui = Jamaui()
353     ui.run()
354