New year, big checkin
authorKristoffer Grönlund <kristoffer.gronlund@purplescout.se>
Fri, 1 Jan 2010 03:12:02 +0000 (04:12 +0100)
committerKristoffer Grönlund <kristoffer.gronlund@purplescout.se>
Fri, 1 Jan 2010 03:12:02 +0000 (04:12 +0100)
30 files changed:
data/about.png [new file with mode: 0644]
data/artist.png [new file with mode: 0644]
data/comments.png [new file with mode: 0644]
data/favorites.png [new file with mode: 0644]
data/icon_artist.png [new file with mode: 0644]
data/icon_download.png [new file with mode: 0644]
data/icon_favorite.png [new file with mode: 0644]
data/icon_favorited.png [new file with mode: 0644]
data/icon_license.png [new file with mode: 0644]
data/icon_logo.png [new file with mode: 0644]
data/icon_play.png [new file with mode: 0644]
data/icon_reviews.png [new file with mode: 0644]
data/inbox.png [new file with mode: 0644]
data/license.png [new file with mode: 0644]
data/profile.png [new file with mode: 0644]
data/radio.png [new file with mode: 0644]
data/user.png [new file with mode: 0644]
jamaendo/api.py
jamaui/favorites.py [new file with mode: 0644]
jamaui/featured.py [new file with mode: 0644]
jamaui/player.py
jamaui/playerwindow.py
jamaui/postoffice.py [new file with mode: 0644]
jamaui/radios.py [new file with mode: 0644]
jamaui/search.py
jamaui/settings.py [new file with mode: 0644]
jamaui/showalbum.py [new file with mode: 0644]
jamaui/showartist.py [new file with mode: 0644]
jamaui/ui.py
jamaui/util.py

diff --git a/data/about.png b/data/about.png
new file mode 100644 (file)
index 0000000..41dac70
Binary files /dev/null and b/data/about.png differ
diff --git a/data/artist.png b/data/artist.png
new file mode 100644 (file)
index 0000000..113ef26
Binary files /dev/null and b/data/artist.png differ
diff --git a/data/comments.png b/data/comments.png
new file mode 100644 (file)
index 0000000..59222b7
Binary files /dev/null and b/data/comments.png differ
diff --git a/data/favorites.png b/data/favorites.png
new file mode 100644 (file)
index 0000000..5740c1f
Binary files /dev/null and b/data/favorites.png differ
diff --git a/data/icon_artist.png b/data/icon_artist.png
new file mode 100644 (file)
index 0000000..d20bc79
Binary files /dev/null and b/data/icon_artist.png differ
diff --git a/data/icon_download.png b/data/icon_download.png
new file mode 100644 (file)
index 0000000..d8a1779
Binary files /dev/null and b/data/icon_download.png differ
diff --git a/data/icon_favorite.png b/data/icon_favorite.png
new file mode 100644 (file)
index 0000000..f20137b
Binary files /dev/null and b/data/icon_favorite.png differ
diff --git a/data/icon_favorited.png b/data/icon_favorited.png
new file mode 100644 (file)
index 0000000..dac5512
Binary files /dev/null and b/data/icon_favorited.png differ
diff --git a/data/icon_license.png b/data/icon_license.png
new file mode 100644 (file)
index 0000000..7311dd8
Binary files /dev/null and b/data/icon_license.png differ
diff --git a/data/icon_logo.png b/data/icon_logo.png
new file mode 100644 (file)
index 0000000..3b24930
Binary files /dev/null and b/data/icon_logo.png differ
diff --git a/data/icon_play.png b/data/icon_play.png
new file mode 100644 (file)
index 0000000..3b24930
Binary files /dev/null and b/data/icon_play.png differ
diff --git a/data/icon_reviews.png b/data/icon_reviews.png
new file mode 100644 (file)
index 0000000..739029e
Binary files /dev/null and b/data/icon_reviews.png differ
diff --git a/data/inbox.png b/data/inbox.png
new file mode 100644 (file)
index 0000000..db59be1
Binary files /dev/null and b/data/inbox.png differ
diff --git a/data/license.png b/data/license.png
new file mode 100644 (file)
index 0000000..41dac70
Binary files /dev/null and b/data/license.png differ
diff --git a/data/profile.png b/data/profile.png
new file mode 100644 (file)
index 0000000..dfeb7f6
Binary files /dev/null and b/data/profile.png differ
diff --git a/data/radio.png b/data/radio.png
new file mode 100644 (file)
index 0000000..8d8c2f4
Binary files /dev/null and b/data/radio.png differ
diff --git a/data/user.png b/data/user.png
new file mode 100644 (file)
index 0000000..89ff8a3
Binary files /dev/null and b/data/user.png differ
index 86058ce..a376b7d 100644 (file)
@@ -35,7 +35,7 @@ def set_cache_dir(cachedir):
 # makes a query internally to get the full story
 
 _ARTIST_FIELDS = ['id', 'name', 'image']
-_ALBUM_FIELDS = ['id', 'name', 'image', 'artist_name', 'artist_id']
+_ALBUM_FIELDS = ['id', 'name', 'image', 'artist_name', 'artist_id', 'license_url']
 _TRACK_FIELDS = ['id', 'name', 'image', 'artist_name', 'album_name', 'album_id', 'numalbum', 'duration']
 _RADIO_FIELDS = ['id', 'name', 'idstr', 'image']
 
@@ -109,15 +109,16 @@ class Album(LazyQuery):
         self.image = None
         self.artist_name = None
         self.artist_id = None
+        self.license_url = None
         self.tracks = None # None means not downloaded
         if json:
             self.set_from_json(json)
 
     def _needs_load(self):
-        return self._needs_load_impl('name', 'image', 'artist_name', 'artist_id', 'tracks')
+        return self._needs_load_impl('name', 'image', 'artist_name', 'artist_id', 'license_url', 'tracks')
 
     def _set_from(self, other):
-        return self._set_from_impl(other, 'name', 'image', 'artist_name', 'artist_id', 'tracks')
+        return self._set_from_impl(other, 'name', 'image', 'artist_name', 'artist_id', 'license_url', 'tracks')
 
 class Track(LazyQuery):
     def __init__(self, ID, json=None):
@@ -175,7 +176,7 @@ _CACHED_RADIOS = 10
 # TODO: cache queries?
 
 class Query(object):
-    rate_limit = 1.0 # max queries per second
+    rate_limit = 1.1 # seconds between queries
     last_query = time.time() - 1.5
 
     @classmethod
@@ -221,7 +222,6 @@ class CoverCache(object):
                     self._covers[(int(m.group(1)), int(m.group(2)))] = fl
 
     def fetch_cover(self, albumid, size):
-        Query._ratelimit() # ratelimit cover fetching too?
         coverdir = _COVERDIR if _COVERDIR else '/tmp'
         to = os.path.join(coverdir, '%d-%d.jpg'%(albumid, size))
         if not os.path.isfile(to):
@@ -362,7 +362,7 @@ class SearchQuery(GetQuery):
 
 class JamendoAPIException(Exception):
     def __init__(self, url):
-        Exception.__init__(url)
+        Exception.__init__(self, url)
 
 def _update_cache(cache, new_items):
     if not isinstance(new_items, list):
diff --git a/jamaui/favorites.py b/jamaui/favorites.py
new file mode 100644 (file)
index 0000000..7ba3827
--- /dev/null
@@ -0,0 +1,118 @@
+import gtk
+import hildon
+import jamaendo
+from playerwindow import open_playerwindow
+from showartist import ShowArtist
+from showalbum import ShowAlbum
+from settings import settings
+import logging
+
+log = logging.getLogger(__name__)
+
+def _alist(l, match):
+    for key, value in l:
+        if key == match:
+            return value
+    return None
+
+class FavoritesWindow(hildon.StackableWindow):
+    def __init__(self):
+        hildon.StackableWindow.__init__(self)
+        self.set_title("Favorites")
+
+        if settings.user:
+            # Results list
+            self.panarea = hildon.PannableArea()
+            self.result_store = gtk.ListStore(str, int)
+            #self.result_store.append(['red'])
+            self.result_view = gtk.TreeView(self.result_store)
+            col = gtk.TreeViewColumn('Name')
+            self.result_view.append_column(col)
+            cell = gtk.CellRendererText()
+            col.pack_start(cell, True)
+            col.add_attribute(cell, 'text', 0)
+            self.result_view.set_search_column(0)
+            col.set_sort_column_id(0)
+            self.result_view.connect('row-activated', self.row_activated)
+
+            self.panarea.add(self.result_view)
+
+            self.idmap = {}
+            try:
+                for item in jamaendo.favorite_albums(settings.user):
+                    self.idmap[item.ID] = item
+                    self.result_store.append([self.get_item_text(item), item.ID])
+            except jamaendo.JamendoAPIException, e:
+                msg = "Query failed, is the user name '%s' correct?" % (settings.user)
+                banner = hildon.hildon_banner_show_information(self, '',
+                                                               msg)
+                banner.set_timeout(3000)
+
+
+            def add_album(albumid):
+                album = jamaendo.get_album(albumid)
+                self.idmap[albumid] = album
+                self.result_store.append([self.get_item_text(album), albumid])
+
+            for item in settings.favorites:
+                try:
+                    if isinstance(item, tuple) and len(item) == 2:
+                        ftype, fid = item
+                        if ftype == 'album':
+                            add_album(fid)
+
+                except jamaendo.JamendoAPIException, e:
+                    log.exception("jamaendo.get_album(%s)"%(fid))
+
+            self.add(self.panarea)
+
+        else:
+            vbox = gtk.VBox()
+            lbl = gtk.Label()
+            lbl.set_markup("""<span size="xx-large">jamendo.com
+in the settings dialog
+enter your username</span>
+""")
+            lbl.set_single_line_mode(False)
+            vbox.pack_start(lbl, True, False)
+            self.add(vbox)
+
+    def get_item_text(self, item):
+        if isinstance(item, jamaendo.Album):
+            return "%s - %s" % (item.artist_name, item.name)
+        elif isinstance(item, jamaendo.Track):
+            return "%s - %s" % (item.artist_name, item.name)
+        else:
+            return item.name
+
+    def make_button(self, text, subtext, callback):
+        button = hildon.Button(gtk.HILDON_SIZE_FINGER_HEIGHT,
+                               hildon.BUTTON_ARRANGEMENT_VERTICAL)
+        button.set_text(text, subtext)
+
+        if callback:
+            button.connect('clicked', callback)
+
+        #image = gtk.image_new_from_stock(gtk.STOCK_INFO, gtk.ICON_SIZE_BUTTON)
+        #button.set_image(image)
+        #button.set_image_position(gtk.POS_RIGHT)
+
+        return button
+
+    def row_activated(self, treeview, path, view_column):
+        treeiter = self.result_store.get_iter(path)
+        title, _id = self.result_store.get(treeiter, 0, 1)
+        item = self.idmap[_id]
+        print _id, item
+        self.open_item(item)
+
+    def open_item(self, item):
+        if isinstance(item, jamaendo.Album):
+            wnd = ShowAlbum(item)
+            wnd.show_all()
+        elif isinstance(item, jamaendo.Artist):
+            wnd = ShowArtist(item)
+            wnd.show_all()
+        elif isinstance(item, jamaendo.Track):
+            wnd = open_playerwindow()
+            wnd.play_tracks([item])
diff --git a/jamaui/featured.py b/jamaui/featured.py
new file mode 100644 (file)
index 0000000..99a4977
--- /dev/null
@@ -0,0 +1,87 @@
+import gtk
+import hildon
+import jamaendo
+from playerwindow import open_playerwindow
+from showartist import ShowArtist
+from showalbum import ShowAlbum
+
+def _alist(l, match):
+    for key, value in l:
+        if key == match:
+            return value
+    return None
+
+class FeaturedWindow(hildon.StackableWindow):
+    features = (("Albums of the week",jamaendo.albums_of_the_week),
+                ("Tracks of the week",jamaendo.tracks_of_the_week),
+                ("New releases",jamaendo.new_releases)
+                )
+
+    def __init__(self, feature):
+        hildon.StackableWindow.__init__(self)
+        self.set_title("Featured")
+
+        self.featurefn = _alist(self.features, feature)
+
+        # Results list
+        self.panarea = hildon.PannableArea()
+        self.result_store = gtk.ListStore(str, int)
+        #self.result_store.append(['red'])
+        self.result_view = gtk.TreeView(self.result_store)
+        col = gtk.TreeViewColumn('Name')
+        self.result_view.append_column(col)
+        cell = gtk.CellRendererText()
+        col.pack_start(cell, True)
+        col.add_attribute(cell, 'text', 0)
+        self.result_view.set_search_column(0)
+        col.set_sort_column_id(0)
+        self.result_view.connect('row-activated', self.row_activated)
+
+        self.panarea.add(self.result_view)
+
+        self.idmap = {}
+        for item in self.featurefn():
+            self.idmap[item.ID] = item
+            self.result_store.append([self.get_item_text(item), item.ID])
+
+        self.add(self.panarea)
+
+    def get_item_text(self, item):
+        if isinstance(item, jamaendo.Album):
+            return "%s - %s" % (item.artist_name, item.name)
+        elif isinstance(item, jamaendo.Track):
+            return "%s - %s" % (item.artist_name, item.name)
+        else:
+            return item.name
+
+    def make_button(self, text, subtext, callback):
+        button = hildon.Button(gtk.HILDON_SIZE_FINGER_HEIGHT,
+                               hildon.BUTTON_ARRANGEMENT_VERTICAL)
+        button.set_text(text, subtext)
+
+        if callback:
+            button.connect('clicked', callback)
+
+        #image = gtk.image_new_from_stock(gtk.STOCK_INFO, gtk.ICON_SIZE_BUTTON)
+        #button.set_image(image)
+        #button.set_image_position(gtk.POS_RIGHT)
+
+        return button
+
+    def row_activated(self, treeview, path, view_column):
+        treeiter = self.result_store.get_iter(path)
+        title, _id = self.result_store.get(treeiter, 0, 1)
+        item = self.idmap[_id]
+        print _id, item
+        self.open_item(item)
+
+    def open_item(self, item):
+        if isinstance(item, jamaendo.Album):
+            wnd = ShowAlbum(item)
+            wnd.show_all()
+        elif isinstance(item, jamaendo.Artist):
+            wnd = ShowArtist(item)
+            wnd.show_all()
+        elif isinstance(item, jamaendo.Track):
+            wnd = open_playerwindow()
+            wnd.play_tracks([item])
index 13af7a6..d860ccb 100644 (file)
@@ -24,9 +24,9 @@ pygst.require('0.10')
 import gst
 import util
 import dbus
-import dbus.service
 
 import jamaendo
+from settings import settings
 
 log = logging.getLogger(__name__)
 
@@ -62,7 +62,7 @@ class GStreamer(_Player):
         self.filesrc = None
         self.filesrc_property = None
         self.volume_control = None
-        self.volume_multiplier = 1
+        self.volume_multiplier = 1.
         self.volume_property = None
         self.eos_callback = lambda: self.stop()
 
@@ -71,31 +71,25 @@ class GStreamer(_Player):
             self.player = None
             return False
 
-        log.debug("Setting up for %s : %s", filetype, uri)
-
-        # On maemo use software decoding to workaround some bugs their gst:
-        # 1. Weird volume bugs in playbin when playing ogg or wma files
-        # 2. When seeking the DSPs sometimes lie about the real position info
-        if True:
-            self._maemo_setup_playbin_player(uri)
-        elif util.platform == 'maemo':
-            if not self._maemo_setup_hardware_player(filetype):
-                self._maemo_setup_software_player()
-                log.debug( 'Using software decoding (maemo)' )
+        _first = False
+        if self.player is None:
+            _first = True
+            if False:
+                self._maemo_setup_playbin2_player(uri)
+                log.debug('Using playbin2 (maemo)')
+            elif util.platform == 'maemo':
+                self._maemo_setup_playbin_player()
+                log.debug('Using playbin (maemo)')
             else:
-                log.debug( 'Using hardware decoding (maemo)' )
-        else:
-            # This is for *ahem* "normal" versions of gstreamer
-            self._setup_playbin_player()
-            log.debug( 'Using playbin (non-maemo)' )
-
-        self._set_uri_to_be_played(uri)
+                self._setup_playbin_player()
+                log.debug( 'Using playbin (non-maemo)' )
 
-        bus = self.player.get_bus()
-        bus.add_signal_watch()
-        bus.connect('message', self._on_message)
+            bus = self.player.get_bus()
+            bus.add_signal_watch()
+            bus.connect('message', self._on_message)
+            self._set_volume_level(settings.volume)
 
-        self._set_volume_level( 1 )
+        self._set_uri_to_be_played(uri)
 
         self.play()
         return True
@@ -106,6 +100,15 @@ class GStreamer(_Player):
             return self.STATES.get(state, 'none')
         return 'none'
 
+    def _get_position_duration(self):
+        try:
+            pos_int = self.player.query_position(self.time_format, None)[0]
+            dur_int = self.player.query_duration(self.time_format, None)[0]
+        except Exception, e:
+            log.exception('Error getting position')
+            pos_int = dur_int = 0
+        return pos_int, dur_int
+
     def playing(self):
         return self.get_state() == 'playing'
 
@@ -123,7 +126,7 @@ class GStreamer(_Player):
             self.player.set_state(gst.STATE_NULL)
             self.player = None
 
-    def _maemo_setup_playbin_player(self, url):
+    def _maemo_setup_playbin2_player(self, url):
         self.player = gst.parse_launch("playbin2 uri=%s" % (url,))
         self.filesrc = self.player
         self.filesrc_property = 'uri'
@@ -131,56 +134,14 @@ class GStreamer(_Player):
         self.volume_multiplier = 1.
         self.volume_property = 'volume'
 
-    def _maemo_setup_hardware_player( self, filetype ):
-        """ Setup a hardware player for mp3 or aac audio using
-        dspaacsink or dspmp3sink """
-
-        if filetype in [ 'mp3', 'aac', 'mp4', 'm4a' ]:
-            self.player = gst.element_factory_make('playbin', 'player')
-            self.filesrc = self.player
-            self.filesrc_property = 'uri'
-            self.volume_control = self.player
-            self.volume_multiplier = 10.
-            self.volume_property = 'volume'
-            return True
-        else:
-            return False
-
-    def _maemo_setup_software_player( self ):
-        """
-        Setup a software decoding player for maemo, this is the only choice
-        for decoding wma and ogg or if audio is to be piped to a bluetooth
-        headset (this is because the audio must first be decoded only to be
-        re-encoded using sbcenc.
-        """
-
-        self.player = gst.Pipeline('player')
-        src = gst.element_factory_make('gnomevfssrc', 'src')
-        decoder = gst.element_factory_make('decodebin', 'decoder')
-        convert = gst.element_factory_make('audioconvert', 'convert')
-        resample = gst.element_factory_make('audioresample', 'resample')
-        sink = gst.element_factory_make('dsppcmsink', 'sink')
-
-        self.filesrc = src # pointer to the main source element
-        self.filesrc_property = 'location'
-        self.volume_control = sink
-        self.volume_multiplier = 1
-        self.volume_property = 'fvolume'
-
-        # Add the various elements to the player pipeline
-        self.player.add( src, decoder, convert, resample, sink )
-
-        # Link what can be linked now, the decoder->convert happens later
-        gst.element_link_many( src, decoder )
-        gst.element_link_many( convert, resample, sink )
-
-        # We can't link the two halves of the pipeline until it comes
-        # time to start playing, this singal lets us know when it's time.
-        # This is because the output from decoder can't be determined until
-        # decoder knows what it's decoding.
-        decoder.connect('pad-added',
-                        self._on_decoder_pad_added,
-                        convert.get_pad('sink') )
+    def _maemo_setup_playbin_player( self):
+        self.player = gst.element_factory_make('playbin2', 'player')
+        self.filesrc = self.player
+        self.filesrc_property = 'uri'
+        self.volume_control = self.player
+        self.volume_multiplier = 1.
+        self.volume_property = 'volume'
+        return True
 
     def _setup_playbin_player( self ):
         """ This is for situations where we have a normal (read: non-maemo)
@@ -205,7 +166,7 @@ class GStreamer(_Player):
         assert  0 <= value <= 1
 
         if self.volume_control is not None:
-            vol = value * self.volume_multiplier
+            vol = value * float(self.volume_multiplier)
             self.volume_control.set_property( self.volume_property, vol )
 
     def _set_uri_to_be_played(self, uri):
@@ -217,17 +178,16 @@ class GStreamer(_Player):
         t = message.type
 
         if t == gst.MESSAGE_EOS:
-            self.eos_callback()
             log.info("End of stream")
+            self.eos_callback()
         elif t == gst.MESSAGE_STATE_CHANGED:
-            old, new, pending = message.parse_state_changed()
-            log.info("State changed: %s -> %s -> %s", old, new, pending)
+            if (message.src == self.player and
+                message.structure['new-state'] == gst.STATE_PLAYING):
+                log.info("State changed to playing")
         elif t == gst.MESSAGE_ERROR:
             err, debug = message.parse_error()
             log.critical( 'Error: %s %s', err, debug )
             self.stop()
-        else:
-            log.info("? %s", message.type)
 
     def set_eos_callback(self, cb):
         self.eos_callback = cb
@@ -361,13 +321,12 @@ class Playlist(object):
         return self._current
 
     def size(self):
-        print type(self)
         return len(self.items)
 
     def __repr__(self):
-        return "Playlist(%s)"%(", ".join([str(item) for item in self.items]))
+        return "Playlist(%s)"%(", ".join([str(item.ID) for item in self.items]))
 
-class Player(Playlist):
+class Player(object):
     def __init__(self):
         self.backend = PlayerBackend()
         self.backend.set_eos_callback(self._on_eos)
index 23e684c..633264e 100644 (file)
@@ -1,6 +1,7 @@
 import gtk
 import hildon
 import jamaendo
+from settings import settings
 from player import Playlist, the_player
 
 class PlayerWindow(hildon.StackableWindow):
@@ -35,7 +36,7 @@ class PlayerWindow(hildon.StackableWindow):
             track = pl.current()
             self.set_labels(track.name, track.artist_name, track.album_name, pl.current_index(), pl.size())
         else:
-            self.set_labels('track name', 'artist', 'album', 0, 0)
+            self.set_labels('', '', '', 0, 0)
 
         vbox2.pack_start(self.track, True)
         vbox2.pack_start(self.artist, False)
@@ -59,6 +60,13 @@ class PlayerWindow(hildon.StackableWindow):
         self.add_stock_button(btns, gtk.STOCK_MEDIA_STOP, self.on_stop)
         self.add_stock_button(btns, gtk.STOCK_MEDIA_NEXT, self.on_next)
 
+        self.volume = hildon.VVolumebar()
+        self.volume.set_property('can-focus', False)
+        self.volume.connect('level_changed', self.volume_changed_hildon)
+        self.volume.connect('mute_toggled', self.mute_toggled)
+        #self.__gui_root.main_window.connect( 'key-press-event',
+        #                                     self.on_key_press )
+        hbox.pack_start(self.volume, False)
         self.add(vbox)
 
         print "Created player window, playlist: %s" % (self.playlist)
@@ -84,6 +92,16 @@ class PlayerWindow(hildon.StackableWindow):
         self.artist.set_markup('<span size="large">%s</span>'%(artist))
         self.album.set_markup('<span foreground="#aaaaaa">%s</span>'%(album))
 
+
+    def volume_changed_hildon(self, widget):
+        settings.volume = widget.get_level()/100.0
+
+    def mute_toggled(self, widget):
+        if widget.get_mute():
+            settings.volume = 0
+        else:
+            settings.volume = widget.get_level()/100.0
+
     def update_state(self):
         item = self.playlist.current()
         if item:
@@ -115,7 +133,7 @@ class PlayerWindow(hildon.StackableWindow):
     def on_stop(self, button):
         self.player.stop()
 
-def open_playerwindow(tracks):
+def open_playerwindow(tracks=None):
     player = PlayerWindow(tracks)
     player.show_all()
     return player
diff --git a/jamaui/postoffice.py b/jamaui/postoffice.py
new file mode 100644 (file)
index 0000000..88143fe
--- /dev/null
@@ -0,0 +1,35 @@
+# message central
+
+class PostOffice(object):
+    class Client(object):
+        def __init__(self):
+            self.tags = {}
+        def has_tag(self, tag):
+            return tag in self.tags
+        def notify(self, tags, data):
+            for tag in tags:
+                cb = self.tags.get(tag)
+                if cb:
+                    cb(data)
+        def register(self, tag, callback):
+            self.tags[tag] = callback
+
+    def __init__(self):
+        self.clients = {}
+
+    def notify(self, tags, data):
+        if not isinstance(tags, list):
+            tags = [tags]
+        for client in clients:
+            client.notify(tags, data)
+
+    def register(self, client_id, tag, callback):
+        client = self.clients.get(client_id)
+        if not client:
+            client = Client()
+            self.clients[client_id] = client
+        client.register(tag, callback)
+
+postoffice = PostOffice()
+
+
diff --git a/jamaui/radios.py b/jamaui/radios.py
new file mode 100644 (file)
index 0000000..545d0ae
--- /dev/null
@@ -0,0 +1,73 @@
+import gtk
+import hildon
+import jamaendo
+from playerwindow import open_playerwindow
+
+class RadiosWindow(hildon.StackableWindow):
+    def __init__(self):
+        hildon.StackableWindow.__init__(self)
+        self.set_title("Radios")
+
+        # Results list
+        self.panarea = hildon.PannableArea()
+        self.result_store = gtk.ListStore(str, int)
+        #self.result_store.append(['red'])
+        self.result_view = gtk.TreeView(self.result_store)
+        col = gtk.TreeViewColumn('Name')
+        self.result_view.append_column(col)
+        cell = gtk.CellRendererText()
+        col.pack_start(cell, True)
+        col.add_attribute(cell, 'text', 0)
+        self.result_view.set_search_column(0)
+        col.set_sort_column_id(0)
+        self.result_view.connect('row-activated', self.row_activated)
+
+        self.panarea.add(self.result_view)
+
+        self.radios = {}
+        hildon.hildon_gtk_window_set_progress_indicator(self, 1)
+        for item in jamaendo.starred_radios():
+            self.radios[item.ID] = item
+            self.result_store.append([self.radio_text(item), item.ID])
+        hildon.hildon_gtk_window_set_progress_indicator(self, 0)
+
+        self.add(self.panarea)
+
+    def radio_text(self, radio):
+        if radio.name and radio.idstr:
+            return "%s (%s)" % (radio.name, radio.idstr)
+        elif radio.name:
+            return radio.name
+        elif radio.idstr:
+            return radio.idstr
+        else:
+            return "Radio #%s" % (radio.ID)
+
+    def make_button(self, text, subtext, callback):
+        button = hildon.Button(gtk.HILDON_SIZE_FINGER_HEIGHT,
+                               hildon.BUTTON_ARRANGEMENT_VERTICAL)
+        button.set_text(text, subtext)
+
+        if callback:
+            button.connect('clicked', callback)
+
+        #image = gtk.image_new_from_stock(gtk.STOCK_INFO, gtk.ICON_SIZE_BUTTON)
+        #button.set_image(image)
+        #button.set_image_position(gtk.POS_RIGHT)
+
+        return button
+
+    def row_activated(self, treeview, path, view_column):
+        treeiter = self.result_store.get_iter(path)
+        title, _id = self.result_store.get(treeiter, 0, 1)
+        item = self.radios[_id]
+        print _id, item
+        self.open_item(item)
+
+    def open_item(self, item):
+        hildon.hildon_gtk_window_set_progress_indicator(self, 1)
+        tracks = jamaendo.get_radio_tracks(item.ID)
+        hildon.hildon_gtk_window_set_progress_indicator(self, 0)
+        if tracks:
+            wnd = open_playerwindow()
+            wnd.play_tracks(tracks)
index a6c818f..4efe294 100644 (file)
@@ -2,6 +2,8 @@ import gtk
 import hildon
 import jamaendo
 from playerwindow import open_playerwindow
+from showartist import ShowArtist
+from showalbum import ShowAlbum
 
 class SearchWindow(hildon.StackableWindow):
     def __init__(self):
@@ -10,51 +12,96 @@ class SearchWindow(hildon.StackableWindow):
 
         vbox = gtk.VBox(False, 0)
 
-        hbox = gtk.HBox()
+
+        # Results list
+        self.panarea = hildon.PannableArea()
+        self.result_store = gtk.ListStore(str, int)
+        #self.result_store.append(['red'])
+        self.result_view = gtk.TreeView(self.result_store)
+        col = gtk.TreeViewColumn('Name')
+        self.result_view.append_column(col)
+        cell = gtk.CellRendererText()
+        col.pack_start(cell, True)
+        col.add_attribute(cell, 'text', 0)
+        self.result_view.set_search_column(0)
+        col.set_sort_column_id(0)
+        self.result_view.connect('row-activated', self.row_activated)
+
+        self.panarea.add(self.result_view)
+        vbox.pack_start(self.panarea, True, True, 0)
+
+
+        # Create selector for search mode
+        self.mode_selector = hildon.TouchSelector(text=True)
+
+        self.mode_selector.append_text("Artists")
+        self.mode_selector.append_text("Albums")
+        self.mode_selector.append_text("Tracks")
+        self.mode = hildon.PickerButton(gtk.HILDON_SIZE_FINGER_HEIGHT,
+                                        hildon.BUTTON_ARRANGEMENT_VERTICAL)
+        self.mode.set_title("Search for")
+        self.mode.set_selector(self.mode_selector)
+        self.mode_selector.connect("changed", self.mode_changed)
+        #vbox.pack_start(self.mode, False)
+        self.mode.set_active(1)
+
+
+        # Search box
+        hbox = gtk.HBox(False, 0)
         self.entry = hildon.Entry(gtk.HILDON_SIZE_FINGER_HEIGHT)
         self.entry.set_placeholder("Search")
         self.entry.connect('activate', self.on_search)
         btn = hildon.GtkButton(gtk.HILDON_SIZE_FINGER_HEIGHT)
-        btn.set_label("Go")
+        btn.set_label(">>")
         btn.connect('clicked', self.on_search)
+        hbox.pack_start(self.mode, False)
         hbox.pack_start(self.entry, True, True, 0)
         hbox.pack_start(btn, False)
-
-        btnbox = gtk.HBox()
-        playbtn = hildon.GtkButton(gtk.HILDON_SIZE_FINGER_HEIGHT)
-        playbtn.set_label("Play selected")
-        playbtn.connect('clicked', self.play_selected)
-        btnbox.pack_start(playbtn, False)
-
-        self.results = hildon.TouchSelector(text=True)
-        self.results.connect("changed", self.selection_changed)
-        self.results.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
-
         vbox.pack_start(hbox, False)
-        vbox.pack_start(self.results, True, True, 0)
-        vbox.pack_start(btnbox, False)
+
 
         self.add(vbox)
 
         self.idmap = {}
 
-        self.pwnd = None
+    def mode_changed(self, selector, user_data):
+        current_selection = selector.get_current_text()
+        print current_selection
 
     def on_search(self, w):
+        mode = self.mode.get_active()
         txt = self.entry.get_text()
-        for album in jamaendo.search_albums(query=txt):
-            title = "%s - %s" % (album.artist_name, album.name)
-            self.idmap[title] = album
-            self.results.append_text(title)
-
-    def selection_changed(self, results, userdata):
-        pass
-
-    def play_selected(self, btn):
-        current_selection = self.results.get_current_text()
-
-        album = self.idmap[current_selection]
-        tracks = jamaendo.get_tracks(album.ID)
-        if tracks:
-            wnd = open_playerwindow(tracks)
-            wnd.on_play(None)
+        self.result_store.clear()
+        if mode == 0:
+            for artist in jamaendo.search_artists(query=txt):
+                title = artist.name
+                self.idmap[artist.ID] = artist
+                self.result_store.append([title, artist.ID])
+        elif mode == 1:
+            for album in jamaendo.search_albums(query=txt):
+                title = "%s - %s" % (album.artist_name, album.name)
+                self.idmap[album.ID] = album
+                self.result_store.append([title, album.ID])
+        elif mode == 2:
+            for track in jamaendo.search_tracks(query=txt):
+                title = "%s - %s" % (track.artist_name, track.name)
+                self.idmap[track.ID] = track
+                self.result_store.append([title, track.ID])
+
+    def row_activated(self, treeview, path, view_column):
+        treeiter = self.result_store.get_iter(path)
+        title, _id = self.result_store.get(treeiter, 0, 1)
+        item = self.idmap[_id]
+        print _id, item
+        self.open_item(item)
+
+    def open_item(self, item):
+        if isinstance(item, jamaendo.Album):
+            wnd = ShowAlbum(item)
+            wnd.show_all()
+        elif isinstance(item, jamaendo.Artist):
+            wnd = ShowArtist(item)
+            wnd.show_all()
+        elif isinstance(item, jamaendo.Track):
+            wnd = open_playerwindow()
+            wnd.play_tracks([item])
diff --git a/jamaui/settings.py b/jamaui/settings.py
new file mode 100644 (file)
index 0000000..5820310
--- /dev/null
@@ -0,0 +1,57 @@
+import cPickle, os
+import logging
+
+VERSION = 1
+log = logging.getLogger(__name__)
+
+class Settings(object):
+    defaults = {
+        'volume':0.5,
+        'user':None,
+        'favorites':set([]) # local favorites - until we can sync back
+        }
+
+    def __init__(self):
+        self.__savename = "/tmp/jaemendo_uisettings"
+        for k,v in self.defaults.iteritems():
+            setattr(self, k, v)
+
+    def set_filename(self, savename):
+        self.__savename = savename
+
+    def favorite(self, album):
+        self.favorites.add(('album', album.ID))
+        self.save()
+
+    def load(self):
+        if not os.path.isfile(self.__savename):
+            return
+        try:
+            f = open(self.__savename)
+            settings = cPickle.load(f)
+            f.close()
+
+            if settings['version'] > VERSION:
+                log.warning("Settings version %s higher than current version (%s)",
+                            settings['version'], VERSION)
+
+            for k in self.defaults.keys():
+                if k in settings:
+                    setattr(self, k, settings[k])
+        except Exception, e:
+            log.exception('failed to load settings')
+
+    def save(self):
+        try:
+            settings = {
+                'version':VERSION,
+                }
+            for k in self.defaults.keys():
+                settings[k] = getattr(self, k)
+            f = open(self.__savename, 'w')
+            cPickle.dump(settings, f)
+            f.close()
+        except Exception, e:
+            log.exception('failed to save settings')
+
+settings = Settings()
diff --git a/jamaui/showalbum.py b/jamaui/showalbum.py
new file mode 100644 (file)
index 0000000..ab3371b
--- /dev/null
@@ -0,0 +1,126 @@
+import gtk
+import hildon
+import jamaendo
+from playerwindow import open_playerwindow
+from settings import settings
+import util
+
+class ShowAlbum(hildon.StackableWindow):
+    def __init__(self, album):
+        hildon.StackableWindow.__init__(self)
+        self.set_title("Album")
+        self.album = album
+
+        top_vbox = gtk.VBox()
+        top_hbox = gtk.HBox()
+        vbox1 = gtk.VBox()
+        self.cover = gtk.Image()
+        self.bbox = gtk.HButtonBox()
+        self.bbox.set_property('layout-style', gtk.BUTTONBOX_SPREAD)
+        self.goto_artist = self.make_imagebutton('artist', self.on_goto_artist)
+        self.download = self.make_imagebutton('download', self.on_download)
+        self.favorite = self.make_imagebutton('favorite', self.on_favorite)
+        self.license = self.make_imagebutton('license', self.on_license)
+        self.playbtn = hildon.GtkButton(gtk.HILDON_SIZE_FINGER_HEIGHT)
+        self.playbtn.set_label("Play album")
+        self.playbtn.set_border_width(0)
+        self.playbtn.connect('clicked', self.on_play)
+
+        vbox2 = gtk.VBox()
+        self.albumtitle = gtk.Label()
+        self.albumtitle.set_markup('<big>%s</big>' % (album.name))
+        self.artist = gtk.Label()
+        self.artist.set_markup('<span color="#cccccc">%s</span>'%(album.artist_name))
+        self.trackarea = hildon.PannableArea()
+
+        self.album_store = gtk.ListStore(int, str, int)
+        self.album_view = gtk.TreeView(self.album_store)
+        col0 = gtk.TreeViewColumn('Num')
+        col = gtk.TreeViewColumn('Name')
+        self.album_view.append_column(col0)
+        self.album_view.append_column(col)
+        cell0 = gtk.CellRendererText()
+        col0.pack_start(cell0, True)
+        col0.add_attribute(cell0, 'text', 0)
+        cell = gtk.CellRendererText()
+        col.pack_start(cell, True)
+        col.add_attribute(cell, 'text', 1)
+        self.album_view.set_search_column(1)
+        col.set_sort_column_id(0)
+        self.album_view.connect('row-activated', self.row_activated)
+
+        for track in jamaendo.get_tracks(album.ID):
+            self.album_store.append([track.numalbum, track.name, track.ID])
+
+        top_vbox.pack_start(self.albumtitle, False)
+        top_vbox.pack_start(top_hbox)
+        top_hbox.pack_start(vbox1, False)
+        top_hbox.pack_start(vbox2, True)
+        vbox1.pack_start(self.cover, True)
+        vbox1.pack_start(self.playbtn, False)
+        vbox1.pack_start(self.bbox, False)
+        self.bbox.add(self.goto_artist)
+        self.bbox.add(self.download)
+        self.bbox.add(self.favorite)
+        self.bbox.add(self.license)
+        vbox2.pack_start(self.artist, False)
+        vbox2.pack_start(self.trackarea, True)
+        self.trackarea.add(self.album_view)
+
+        self.add(top_vbox)
+
+        coverfile = jamaendo.get_album_cover(self.album.ID, size=200)
+        self.cover.set_from_file(coverfile)
+
+        self.show_all()
+
+    def make_imagebutton(self, name, cb):
+        btn = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
+        btn.set_relief(gtk.RELIEF_NONE)
+        img = util.find_resource('icon_%s.png'%(name))
+        if img:
+            btn.set_image(gtk.image_new_from_file(img))
+        else:
+            btn.set_image(gtk.image_new_from_stock(gtk.STOCK_MEDIA_STOP, gtk.ICON_SIZE_SMALL_TOOLBAR))
+        btn.set_border_width(0)
+        btn.connect('clicked', cb)
+        return btn
+
+    def on_goto_artist(self, btn):
+        artist = jamaendo.get_artist(int(self.album.artist_id))
+        self.open_item(artist)
+
+    def on_download(self, btn):
+        banner = hildon.hildon_banner_show_information(self, '', "Downloads disabled in this version")
+        banner.set_timeout(2000)
+
+    def on_favorite(self, btn):
+        settings.favorite(self.album)
+        banner = hildon.hildon_banner_show_information(self, '', "Favorite added")
+        banner.set_timeout(2000)
+
+
+    def on_license(self, btn):
+        url = self.album.license_url
+        import webbrowser
+        webbrowser.open_new(url)
+
+    def on_play(self, btn):
+        self.open_item(self.album)
+
+    def row_activated(self, treeview, path, view_column):
+        pass
+
+    def open_item(self, item):
+        if isinstance(item, jamaendo.Album):
+            tracks = jamaendo.get_tracks(item.ID)
+            if tracks:
+                wnd = open_playerwindow()
+                wnd.play_tracks(tracks)
+        elif isinstance(item, jamaendo.Artist):
+            from showartist import ShowArtist
+            wnd = ShowArtist(item)
+            wnd.show_all()
+        elif isinstance(item, jamaendo.Track):
+            wnd = open_playerwindow()
+            wnd.play_tracks([item])
diff --git a/jamaui/showartist.py b/jamaui/showartist.py
new file mode 100644 (file)
index 0000000..005a651
--- /dev/null
@@ -0,0 +1,56 @@
+import gtk
+import hildon
+import jamaendo
+from playerwindow import open_playerwindow
+
+class ShowArtist(hildon.StackableWindow):
+    def __init__(self, artist):
+        hildon.StackableWindow.__init__(self)
+        self.set_title("Artist")
+        self.artist = artist
+
+        self.panarea = hildon.PannableArea()
+        vbox = gtk.VBox(False, 0)
+
+        name = gtk.Label()
+        name.set_markup('<big>%s</big>'%(artist.name))
+        vbox.pack_start(name, False)
+
+        self.album_store = gtk.ListStore(str, int)
+        self.album_view = gtk.TreeView(self.album_store)
+        col = gtk.TreeViewColumn('Name')
+        self.album_view.append_column(col)
+        cell = gtk.CellRendererText()
+        col.pack_start(cell, True)
+        col.add_attribute(cell, 'text', 0)
+        self.album_view.set_search_column(0)
+        col.set_sort_column_id(0)
+        self.album_view.connect('row-activated', self.row_activated)
+
+
+        self.panarea.add(self.album_view)
+        vbox.pack_start(self.panarea, True, True, 0)
+        self.add(vbox)
+
+        for album in jamaendo.get_albums(artist.ID):
+            self.album_store.append([album.name, album.ID])
+
+    def row_activated(self, treeview, path, view_column):
+        treeiter = self.album_store.get_iter(path)
+        title, _id = self.album_store.get(treeiter, 0, 1)
+        album = jamaendo.get_album(_id)
+        if isinstance(album, list):
+            album = album[0]
+        self.open_item(album)
+
+    def open_item(self, item):
+        if isinstance(item, jamaendo.Album):
+            from showalbum import ShowAlbum
+            wnd = ShowAlbum(item)
+            wnd.show_all()
+        elif isinstance(item, jamaendo.Artist):
+            wnd = ShowArtist(item)
+            wnd.show_all()
+        elif isinstance(item, jamaendo.Track):
+            wnd = open_playerwindow()
+            wnd.play_tracks([item])
index 8babaf5..be4da53 100644 (file)
@@ -3,6 +3,7 @@ import gtk
 import gobject
 import util
 import logging
+from settings import settings
 
 import ossohelper
 
@@ -10,6 +11,8 @@ gobject.threads_init()
 
 log = logging.getLogger(__name__)
 
+VERSION = '1'
+
 try:
     import hildon
 except:
@@ -24,72 +27,25 @@ from dbus.mainloop.glib import DBusGMainLoop
 
 DBusGMainLoop(set_as_default=True)
 
+import jamaendo
+
 from playerwindow import open_playerwindow
 from search import SearchWindow
-
-class ArtistWindow(hildon.StackableWindow):
-    def __init__(self):
-        hildon.StackableWindow.__init__(self)
-        self.set_title("View Artist")
-
-        label = gtk.Label("Artist information")
-        vbox = gtk.VBox(False, 0)
-        vbox.pack_start(label, True, True, 0)
-        self.add(vbox)
-
-class AlbumWindow(hildon.StackableWindow):
-    def __init__(self):
-        hildon.StackableWindow.__init__(self)
-        self.set_title("View Album")
-
-        label = gtk.Label("Album information")
-        vbox = gtk.VBox(False, 0)
-        vbox.pack_start(label, True, True, 0)
-        self.add(vbox)
-
-class RadiosWindow(hildon.StackableWindow):
-    def __init__(self):
-        hildon.StackableWindow.__init__(self)
-        self.set_title("Radios")
-
-        label = gtk.Label("Radios")
-        vbox = gtk.VBox(False, 0)
-        vbox.pack_start(label, True, True, 0)
-        self.add(vbox)
-
-class FeaturedWindow(hildon.StackableWindow):
-    def __init__(self):
-        hildon.StackableWindow.__init__(self)
-        self.set_title("Featured")
-
-        label = gtk.Label("featured")
-        vbox = gtk.VBox(False, 0)
-        vbox.pack_start(label, True, True, 0)
-        self.add(vbox)
+from featured import FeaturedWindow
+from radios import RadiosWindow
+from favorites import FavoritesWindow
 
 class PlaylistsWindow(hildon.StackableWindow):
     def __init__(self):
         hildon.StackableWindow.__init__(self)
         self.set_title("Playlists")
 
-        label = gtk.Label("playlists")
-        vbox = gtk.VBox(False, 0)
-        vbox.pack_start(label, True, True, 0)
-        self.add(vbox)
-
-class FavoritesWindow(hildon.StackableWindow):
-    def __init__(self):
-        hildon.StackableWindow.__init__(self)
-        self.set_title("Favorites")
-
-        label = gtk.Label("favorites")
+        label = gtk.Label("Playlists")
         vbox = gtk.VBox(False, 0)
         vbox.pack_start(label, True, True, 0)
         self.add(vbox)
 
 class Jamaui(object):
-    _BG = 'bg.png' # /opt/jamaendo/data/bg.png
-
     def __init__(self):
         self.app = None
         self.menu = None
@@ -103,6 +59,14 @@ class Jamaui(object):
         self.window.set_title("jamaendo")
         self.window.connect("destroy", self.destroy)
 
+        self.CONFDIR = os.path.expanduser('~/MyDocs/.jamaendo')
+        jamaendo.set_cache_dir(self.CONFDIR)
+        settings.set_filename(os.path.join(self.CONFDIR, 'ui_settings'))
+        settings.load()
+
+    def save_settings(self):
+        settings.save()
+
     def create_menu(self):
         self.menu = hildon.AppMenu()
 
@@ -121,11 +85,17 @@ class Jamaui(object):
         player.connect("clicked", self.on_favorites)
         self.menu.append(player)
 
+        #player = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
+        #player.set_label("Playlists")
+        #player.connect("clicked", self.on_playlists)
+        #self.menu.append(player)
+
         player = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
-        player.set_label("Playlists")
-        player.connect("clicked", self.on_playlists)
+        player.set_label("Settings")
+        player.connect("clicked", self.on_settings)
         self.menu.append(player)
 
+
         # Don't use localdb ATM
         #refresh = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
         #refresh.set_label("Refresh")
@@ -141,18 +111,9 @@ class Jamaui(object):
         self.menu.show_all()
         self.window.set_app_menu(self.menu)
 
-    def find_resource(self, name):
-        if os.path.isfile(os.path.join('data', name)):
-            return os.path.join('data', name)
-        elif os.path.isfile(os.path.join('/opt/jaemendo', name)):
-            return os.path.join('/opt/jaemendo', name)
-        elif os.path.isfile(os.path.join('/usr/share/jaemendo', name)):
-            return os.path.join('/usr/share/jaemendo', name)
-        else:
-            return None
 
     def setup_widgets(self):
-        bgimg = self.find_resource(self._BG)
+        bgimg = util.find_resource('bg.png')
         if bgimg:
             background, mask = gtk.gdk.pixbuf_new_from_file(bgimg).render_pixmap_and_mask()
             self.window.realize()
@@ -179,22 +140,45 @@ class Jamaui(object):
         btn.connect('clicked', callback)
         self.bbox.add(btn)
 
+    #def add_featured_button(self):
+    #    self.featured_sel = hildon.TouchSelector(text=True)
+    #    self.featured_sel.append_text("Albums of the week")
+    #    self.featured_sel.append_text("Tracks of the week")
+    #    self.featured_sel.append_text("New releases")
+    #    btn = hildon.PickerButton(gtk.HILDON_SIZE_THUMB_HEIGHT,
+    #                              hildon.BUTTON_ARRANGEMENT_VERTICAL)
+    #    btn.set_text("Featured", "Most listened to")
+    #    btn.set_property('width-request', 225)
+    #    btn.set_selector(self.featured_sel)
+    #    self.featured_btn = btn
+    #    self.bbox.add(btn)
+
     def destroy(self, widget):
         gtk.main_quit()
 
     def show_about(self, w, win):
         dialog = gtk.AboutDialog()
+        dialog.set_program_name("jamaendo")
         dialog.set_website("http://github.com/krig")
         dialog.set_website_label("http://github.com/krig")
-        dialog.set_authors(("Kristoffer Gronlund (Purple Scout AB)",))
+        dialog.set_version(VERSION)
+        dialog.set_authors(("Kristoffer Gronlund <kristoffer.gronlund@purplescout.se>",))
         dialog.set_comments("""Jamaendo plays music from the music catalog of JAMENDO.
 
 JAMENDO is an online platform that distributes musical works under Creative Commons licenses.""")
-        dialog.set_version('')
+        gtk.about_dialog_set_email_hook(self.open_link, dialog)
+        gtk.about_dialog_set_url_hook(self.open_link, dialog)
+        dialog.connect( 'response', lambda dlg, response: dlg.destroy())
+        for parent in dialog.vbox.get_children():
+            for child in parent.get_children():
+                if isinstance(child, gtk.Label):
+                    child.set_selectable(False)
+                    child.set_alignment(0.0, 0.5)
         dialog.run()
         dialog.destroy()
 
     def open_link(self, d, url, data):
+        print "url: %s" % (url)
         import webbrowser
         webbrowser.open_new(url)
 
@@ -206,8 +190,17 @@ JAMENDO is an online platform that distributes musical works under Creative Comm
     #    dialog.hide()
 
     def on_featured(self, button):
-        self.featuredwnd = FeaturedWindow()
-        self.featuredwnd.show_all()
+        dialog = hildon.PickerDialog(self.window)
+        sel = hildon.TouchSelector(text=True)
+        for feature, _ in FeaturedWindow.features:
+            sel.append_text(feature)
+        dialog.set_selector(sel)
+        dialog.set_title("Featured")
+        if dialog.run() == gtk.RESPONSE_OK:
+            txt = sel.get_current_text()
+            self.featuredwnd = FeaturedWindow(txt)
+            self.featuredwnd.show_all()
+        dialog.destroy()
 
     def on_radios(self, button):
         self.radioswnd = RadiosWindow()
@@ -221,6 +214,30 @@ JAMENDO is an online platform that distributes musical works under Creative Comm
         self.playlistswnd = PlaylistsWindow()
         self.playlistswnd.show_all()
 
+    def on_settings(self, button):
+        dialog = gtk.Dialog()
+        dialog.set_title("Settings")
+        dialog.add_button( gtk.STOCK_OK, gtk.RESPONSE_OK )
+        vbox = dialog.vbox
+        tbl = gtk.Table(1, 2)
+        tbl.attach(gtk.Label("Username:"), 0, 1, 0, 1)
+        entry = hildon.Entry(gtk.HILDON_SIZE_FINGER_HEIGHT)
+        entry.set_placeholder("jamendo.com username")
+        if settings.user:
+            entry.set_text(settings.user)
+        tbl.attach(entry, 1, 2, 0, 1)
+        vbox.pack_start(tbl, True, True, 2)
+        dialog.show_all()
+        result = dialog.run()
+        val = entry.get_text()
+        dialog.destroy()
+        print val, result
+        if val and result == gtk.RESPONSE_OK:
+            print "new user name:", val
+            settings.user = val
+            self.save_settings()
+
+
     def on_favorites(self, button):
         self.favoriteswnd = FavoritesWindow()
         self.favoriteswnd.show_all()
index 09904d7..d17492a 100644 (file)
@@ -23,3 +23,14 @@ platform = get_platform()
 
 def jsonprint(x):
     print simplejson.dumps(x, sort_keys=True, indent=4)
+
+def find_resource(name):
+    if os.path.isfile(os.path.join('data', name)):
+        return os.path.join('data', name)
+    elif os.path.isfile(os.path.join('/opt/jaemendo', name)):
+        return os.path.join('/opt/jaemendo', name)
+    elif os.path.isfile(os.path.join('/usr/share/jaemendo', name)):
+        return os.path.join('/usr/share/jaemendo', name)
+    else:
+        return None
+