Updated website
[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         player = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
97         player.set_label("Open player")
98         player.connect("clicked", self.on_player)
99         self.menu.append(player)
100
101         player = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
102         player.set_label("Favorites")
103         player.connect("clicked", self.on_favorites)
104         self.menu.append(player)
105
106         player = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
107         player.set_label("Playlists")
108         player.connect("clicked", self.on_playlists)
109         self.menu.append(player)
110
111         player = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
112         player.set_label("Settings")
113         player.connect("clicked", self.on_settings)
114         self.menu.append(player)
115
116         menu_about = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
117         menu_about.set_label("About")
118         menu_about.connect("clicked", self.show_about, self.window)
119         self.menu.append(menu_about)
120         gtk.about_dialog_set_url_hook(self.open_link, None)
121
122         self.menu.show_all()
123         self.window.set_app_menu(self.menu)
124
125
126     def setup_widgets(self):
127         bgimg = util.find_resource('bg.png')
128         if bgimg:
129             background, mask = gtk.gdk.pixbuf_new_from_file(bgimg).render_pixmap_and_mask()
130             self.window.realize()
131             self.window.window.set_back_pixmap(background, False)
132
133         bbox = gtk.HButtonBox()
134         alignment = gtk.Alignment(xalign=0.2, yalign=0.4, xscale=1.0)
135         alignment.add(bbox)
136         bbox.set_property('layout-style', gtk.BUTTONBOX_SPREAD)
137         self.bbox = bbox
138         self.window.add(alignment)
139
140         self.add_mainscreen_button("Featured", "Most listened to", self.on_featured)
141         self.add_mainscreen_button("Radios", "The best in free music", self.on_radios)
142         self.add_mainscreen_button("Search", "Search for artists/albums", self.on_search)
143
144         self.window.show_all()
145
146     def add_mainscreen_button(self, title, subtitle, callback):
147         btn = hildon.Button(gtk.HILDON_SIZE_THUMB_HEIGHT,
148                             hildon.BUTTON_ARRANGEMENT_VERTICAL)
149         btn.set_text(title, subtitle)
150         btn.set_property('width-request', 225)
151         btn.connect('clicked', callback)
152         self.bbox.add(btn)
153
154     def on_request_cover(self, albumid, size):
155         jamaendo.get_album_cover_async(self.got_album_cover, int(albumid), size)
156
157     def on_request_images(self, urls):
158         jamaendo.get_images_async(self.got_images, urls)
159
160     def got_album_cover(self, albumid, size, cover):
161         gtk.gdk.threads_enter()
162         postoffice.notify('album-cover', albumid, size, cover)
163         gtk.gdk.threads_leave()
164
165     def got_images(self, images):
166         gtk.gdk.threads_enter()
167         postoffice.notify('images', images)
168         gtk.gdk.threads_leave()
169
170     def destroy(self, widget):
171         postoffice.disconnect(['request-album-cover', 'request-images'], self)
172         settings.save()
173         from player import the_player
174         if the_player:
175             the_player.stop()
176         gtk.main_quit()
177
178     def show_about(self, w, win):
179         dialog = gtk.AboutDialog()
180         dialog.set_program_name("jamaendo")
181         dialog.set_website("http://jamaendo.garage.maemo.org/")
182         dialog.set_website_label("http://jamaendo.garage.maemo.org/")
183         dialog.set_version(VERSION)
184         dialog.set_license("""Copyright (c) 2010, Kristoffer Gronlund
185 All rights reserved.
186
187 Redistribution and use in source and binary forms, with or without
188 modification, are permitted provided that the following conditions are met:
189      * Redistributions of source code must retain the above copyright
190        notice, this list of conditions and the following disclaimer.
191      * Redistributions in binary form must reproduce the above copyright
192        notice, this list of conditions and the following disclaimer in the
193        documentation and/or other materials provided with the distribution.
194      * Neither the name of Jamaendo nor the
195        names of its contributors may be used to endorse or promote products
196        derived from this software without specific prior written permission.
197
198 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
199 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
200 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
201 DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
202 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
203 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
204 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
205 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
206 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
207 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
208 """)
209         dialog.set_authors(("Kristoffer Gronlund <kristoffer.gronlund@purplescout.se>",
210                             "Based on Panucci, written by Thomas Perl <thpinfo.com>",
211                             "Icons by Joseph Wain <http://glyphish.com/>"))
212         dialog.set_comments("""Jamaendo plays music from the music catalog of JAMENDO.
213
214 JAMENDO is an online platform that distributes musical works under Creative Commons licenses.""")
215         gtk.about_dialog_set_email_hook(self.open_link, dialog)
216         gtk.about_dialog_set_url_hook(self.open_link, dialog)
217         dialog.connect( 'response', lambda dlg, response: dlg.destroy())
218         for parent in dialog.vbox.get_children():
219             for child in parent.get_children():
220                 if isinstance(child, gtk.Label):
221                     child.set_selectable(False)
222                     child.set_alignment(0.0, 0.5)
223         dialog.run()
224         dialog.destroy()
225
226     def open_link(self, d, url, data):
227         import webbrowser
228         webbrowser.open_new(url)
229
230     def on_featured(self, button):
231         dialog = ButtonListDialog('Featured', self.window)
232         def fn(btn, feature):
233             self.featuredwnd = FeaturedWindow(feature)
234             self.featuredwnd.show_all()
235             dialog.response(gtk.RESPONSE_OK)
236         for feature, _ in FeaturedWindow.features:
237             dialog.add_button(feature, fn, feature)
238         dialog.show_all()
239         dialog.run()
240         dialog.destroy()
241
242     def on_radios(self, button):
243         self.radioswnd = RadiosWindow()
244         self.radioswnd.show_all()
245
246     def on_search(self, button):
247         self.searchwnd = SearchWindow()
248         self.searchwnd.show_all()
249
250     def on_playlists(self, button):
251         self.playlistswnd = PlaylistsWindow()
252         self.playlistswnd.show_all()
253
254     def on_settings(self, button):
255         dialog = gtk.Dialog()
256         dialog.set_title("Settings")
257         dialog.add_button( gtk.STOCK_OK, gtk.RESPONSE_OK )
258         vbox = dialog.vbox
259         hboxinner = gtk.HBox()
260         hboxinner.pack_start(gtk.Label("Username:"), False, False, 0)
261         entry = hildon.Entry(gtk.HILDON_SIZE_FINGER_HEIGHT)
262         entry.set_placeholder("jamendo.com username")
263         if settings.user:
264             entry.set_text(settings.user)
265         hboxinner.pack_start(entry, True, True, 0)
266         vbox.pack_start(hboxinner, True, True, 0)
267         dialog.show_all()
268         result = dialog.run()
269         val = entry.get_text()
270         dialog.destroy()
271         if val and result == gtk.RESPONSE_OK:
272             settings.user = val
273             settings.save()
274
275
276     def on_favorites(self, button):
277         self.favoriteswnd = FavoritesWindow()
278         self.favoriteswnd.show_all()
279
280     def on_player(self, button):
281         open_playerwindow()
282
283     def run(self):
284         ossohelper.application_init('org.jamaendo', '0.1')
285         self.create_window()
286         self.create_menu()
287         self.setup_widgets()
288         self.window.show_all()
289         gtk.gdk.threads_enter()
290         gtk.main()
291         gtk.gdk.threads_leave()
292         ossohelper.application_exit()
293
294 if __name__=="__main__":
295     ui = Jamaui()
296     ui.run()
297