5732ee7c75f3a715fe0679d8bb24ba70cf4baa96
[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 (probably) only works in maemo')
51         import helldon as hildon
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         gtk.gdk.threads_enter()
174         postoffice.notify('album-cover', albumid, size, cover)
175         gtk.gdk.threads_leave()
176
177     def got_images(self, images):
178         gtk.gdk.threads_enter()
179         postoffice.notify('images', images)
180         gtk.gdk.threads_leave()
181
182     #def add_featured_button(self):
183     #    self.featured_sel = hildon.TouchSelector(text=True)
184     #    self.featured_sel.append_text("Albums of the week")
185     #    self.featured_sel.append_text("Tracks of the week")
186     #    self.featured_sel.append_text("New releases")
187     #    btn = hildon.PickerButton(gtk.HILDON_SIZE_THUMB_HEIGHT,
188     #                              hildon.BUTTON_ARRANGEMENT_VERTICAL)
189     #    btn.set_text("Featured", "Most listened to")
190     #    btn.set_property('width-request', 225)
191     #    btn.set_selector(self.featured_sel)
192     #    self.featured_btn = btn
193     #    self.bbox.add(btn)
194
195     def destroy(self, widget):
196         postoffice.disconnect(['request-album-cover', 'request-images'], self)
197         settings.save()
198         from player import the_player
199         if the_player:
200             the_player.stop()
201         gtk.main_quit()
202
203     def show_about(self, w, win):
204         dialog = gtk.AboutDialog()
205         dialog.set_program_name("jamaendo")
206         dialog.set_website("http://jamaendo.garage.maemo.org/")
207         dialog.set_website_label("http://jamaendo.garage.maemo.org/")
208         dialog.set_version(VERSION)
209         dialog.set_license("""Copyright (c) 2010, Kristoffer Gronlund
210 All rights reserved.
211
212 Redistribution and use in source and binary forms, with or without
213 modification, are permitted provided that the following conditions are met:
214      * Redistributions of source code must retain the above copyright
215        notice, this list of conditions and the following disclaimer.
216      * Redistributions in binary form must reproduce the above copyright
217        notice, this list of conditions and the following disclaimer in the
218        documentation and/or other materials provided with the distribution.
219      * Neither the name of Jamaendo nor the
220        names of its contributors may be used to endorse or promote products
221        derived from this software without specific prior written permission.
222
223 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
224 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
225 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
226 DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
227 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
228 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
229 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
230 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
231 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
232 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
233 """)
234         dialog.set_authors(("Kristoffer Gronlund <kristoffer.gronlund@purplescout.se>",
235                             "Based on Panucci, written by Thomas Perl <thpinfo.com>"))
236         dialog.set_comments("""Jamaendo plays music from the music catalog of JAMENDO.
237
238 JAMENDO is an online platform that distributes musical works under Creative Commons licenses.""")
239         gtk.about_dialog_set_email_hook(self.open_link, dialog)
240         gtk.about_dialog_set_url_hook(self.open_link, dialog)
241         dialog.connect( 'response', lambda dlg, response: dlg.destroy())
242         for parent in dialog.vbox.get_children():
243             for child in parent.get_children():
244                 if isinstance(child, gtk.Label):
245                     child.set_selectable(False)
246                     child.set_alignment(0.0, 0.5)
247         dialog.run()
248         dialog.destroy()
249
250     def open_link(self, d, url, data):
251         #print "url: %s" % (url)
252         import webbrowser
253         webbrowser.open_new(url)
254
255
256     #def on_refresh(self, button):
257     #    dialog = RefreshDialog()
258     #    dialog.show_all()
259     #    dialog.run()
260     #    dialog.hide()
261
262     def on_featured(self, button):
263         dialog = ButtonListDialog('Featured', self.window)
264         def fn(btn, feature):
265             self.featuredwnd = FeaturedWindow(feature)
266             self.featuredwnd.show_all()
267             dialog.response(gtk.RESPONSE_OK)
268         for feature, _ in FeaturedWindow.features:
269             dialog.add_button(feature, fn, feature)
270         dialog.show_all()
271         dialog.run()
272         dialog.destroy()
273
274     def on_radios(self, button):
275         self.radioswnd = RadiosWindow()
276         self.radioswnd.show_all()
277
278     def on_search(self, button):
279         self.searchwnd = SearchWindow()
280         self.searchwnd.show_all()
281
282     def on_playlists(self, button):
283         self.playlistswnd = PlaylistsWindow()
284         self.playlistswnd.show_all()
285
286     def on_settings(self, button):
287         dialog = gtk.Dialog()
288         dialog.set_title("Settings")
289         dialog.add_button( gtk.STOCK_OK, gtk.RESPONSE_OK )
290         vbox = dialog.vbox
291         tbl = gtk.Table(1, 2)
292         tbl.attach(gtk.Label("Username:"), 0, 1, 0, 1)
293         entry = hildon.Entry(gtk.HILDON_SIZE_FINGER_HEIGHT)
294         entry.set_placeholder("jamendo.com username")
295         if settings.user:
296             entry.set_text(settings.user)
297         tbl.attach(entry, 1, 2, 0, 1)
298         vbox.pack_start(tbl, True, True, 2)
299         dialog.show_all()
300         result = dialog.run()
301         val = entry.get_text()
302         dialog.destroy()
303         #print val, result
304         if val and result == gtk.RESPONSE_OK:
305             #print "new user name:", val
306             settings.user = val
307             settings.save()
308
309
310     def on_favorites(self, button):
311         self.favoriteswnd = FavoritesWindow()
312         self.favoriteswnd.show_all()
313
314     def on_player(self, button):
315         open_playerwindow()
316
317     '''
318     def on_search(self, button):
319         if self.searchbar:
320             self.searchbar.show()
321         else:
322             self.searchstore = gtk.ListStore(gobject.TYPE_STRING)
323             iter = self.searchstore.append()
324             self.searchstore.set(iter, 0, "Test1")
325             iter = self.searchstore.append()
326             self.searchstore.set(iter, 0, "Test2")
327             self.searchbar = hildon.FindToolbar("Search", self.searchstore, 0)
328             self.searchbar.set_active(0)
329             self.window.add_toolbar(self.searchbar)
330             self.searchbar.show()
331     '''
332
333     def run(self):
334         ossohelper.application_init('org.jamaendo', '0.1')
335         self.create_window()
336         self.create_menu()
337         self.setup_widgets()
338         self.window.show_all()
339         gtk.main()
340         ossohelper.application_exit()
341
342 if __name__=="__main__":
343     ui = Jamaui()
344     ui.run()
345