~ubuntu-branches/ubuntu/oneiric/rhythmbox/oneiric

« back to all changes in this revision

Viewing changes to plugins/context/ContextView.py

  • Committer: Bazaar Package Importer
  • Author(s): Rico Tzschichholz
  • Date: 2011-07-29 16:41:38 UTC
  • mto: This revision was merged to the branch mainline in revision 191.
  • Revision ID: james.westby@ubuntu.com-20110729164138-wwicy8nqalm18ck7
Tags: upstream-2.90.1~20110802
ImportĀ upstreamĀ versionĀ 2.90.1~20110802

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- Mode: python; coding: utf-8; tab-width: 8; indent-tabs-mode: t; -*-
 
2
#
 
3
# Copyright (C) 2009 John Iacona
 
4
#
 
5
# This program is free software; you can redistribute it and/or modify
 
6
# it under the terms of the GNU General Public License as published by
 
7
# the Free Software Foundation; either version 2, or (at your option)
 
8
# any later version.
 
9
#
 
10
# The Rhythmbox authors hereby grant permission for non-GPL compatible
 
11
# GStreamer plugins to be used and distributed together with GStreamer
 
12
# and Rhythmbox. This permission is above and beyond the permissions granted
 
13
# by the GPL license by which Rhythmbox is covered. If you modify this code
 
14
# you may extend this exception to your version of the code, but you are not
 
15
# obligated to do so. If you do not wish to do so, delete this exception
 
16
# statement from your version.
 
17
#
 
18
# This program is distributed in the hope that it will be useful,
 
19
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
20
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
21
# GNU General Public License for more details.
 
22
#
 
23
# You should have received a copy of the GNU General Public License
 
24
# along with this program; if not, write to the Free Software
 
25
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
 
26
 
 
27
import gobject
 
28
import os
 
29
 
 
30
import ArtistTab as at
 
31
import AlbumTab as abt
 
32
import LyricsTab as lt
 
33
import LinksTab as lit
 
34
 
 
35
import rb
 
36
from gi.repository import Gtk, Gdk, Pango, Gio
 
37
from gi.repository import RB
 
38
from gi.repository import WebKit
 
39
 
 
40
context_ui = """
 
41
<ui>
 
42
    <toolbar name="ToolBar">
 
43
        <toolitem name="Context" action="ToggleContextView" />
 
44
    </toolbar>
 
45
</ui>
 
46
"""
 
47
 
 
48
class ContextView (gobject.GObject):
 
49
 
 
50
    def __init__ (self, shell, plugin):
 
51
        gobject.GObject.__init__ (self)
 
52
        self.shell = shell
 
53
        self.sp = shell.props.shell_player
 
54
        self.db = shell.props.db
 
55
        self.plugin = plugin
 
56
        
 
57
        self.current_artist = None
 
58
        self.current_album = None
 
59
        self.current_song = None
 
60
        self.visible = True
 
61
 
 
62
        # cache for artist/album information: valid for a month, can be used indefinitely
 
63
        # if offline, discarded if unused for six months
 
64
        self.info_cache = rb.URLCache(name = 'info',
 
65
                                      path = os.path.join('context-pane', 'info'),
 
66
                                      refresh = 30,
 
67
                                      discard = 180)
 
68
        # cache for rankings (artist top tracks and top albums): valid for a week,
 
69
        # can be used for a month if offline
 
70
        self.ranking_cache = rb.URLCache(name = 'ranking',
 
71
                                         path = os.path.join('context-pane', 'ranking'),
 
72
                                         refresh = 7,
 
73
                                         lifetime = 30)
 
74
 
 
75
        # maybe move this into an idle handler?
 
76
        self.info_cache.clean()
 
77
        self.ranking_cache.clean()
 
78
 
 
79
        self.init_gui ()
 
80
        self.init_tabs()
 
81
 
 
82
        self.connect_signals ()
 
83
        self.load_top_five (self.ds['artist'])
 
84
 
 
85
        # Set currently displayed tab
 
86
        # TODO: make this persistent via gsettings key
 
87
        self.current = 'artist'
 
88
        self.tab[self.current].activate ()
 
89
 
 
90
        # Add button to toggle visibility of pane
 
91
        self.action = ('ToggleContextView','gtk-info', _('Toggle Conte_xt Pane'),
 
92
                        None, _('Change the visibility of the context pane'),
 
93
                        self.toggle_visibility, True)
 
94
        self.action_group = Gtk.ActionGroup(name='ContextPluginActions')
 
95
        self.action_group.add_toggle_actions([self.action])
 
96
        uim = self.shell.get_ui_manager()
 
97
        uim.insert_action_group (self.action_group, 0)
 
98
        self.ui_id = uim.add_ui_from_string(context_ui)
 
99
        uim.ensure_update()
 
100
 
 
101
    def deactivate (self, shell):
 
102
        self.shell = None
 
103
        self.disconnect_signals ()
 
104
        self.player_cb_ids = None
 
105
        self.tab_cb_ids = None
 
106
        self.sp = None
 
107
        self.db = None
 
108
        self.plugin = None
 
109
        self.tab = None
 
110
        self.ds = None
 
111
        self.view = None
 
112
        if self.visible:
 
113
            shell.remove_widget (self.vbox, RB.ShellUILocation.RIGHT_SIDEBAR)
 
114
            self.visible = False
 
115
        self.vbox = None
 
116
        self.label = None
 
117
        self.webview = None
 
118
        self.websettings = None
 
119
        self.buttons = None
 
120
        self.top_five_list = None
 
121
        uim = shell.get_ui_manager ()
 
122
        uim.remove_ui (self.ui_id)
 
123
        uim.remove_action_group (self.action_group)
 
124
 
 
125
    def connect_signals(self):
 
126
        self.player_cb_ids = ( self.sp.connect ('playing-changed', self.playing_changed_cb),
 
127
            self.sp.connect ('playing-song-changed', self.playing_changed_cb))
 
128
        self.ds_cb_id = self.ds['artist'].connect ('artist-top-tracks-ready', self.load_top_five)
 
129
        self.tab_cb_ids = []
 
130
 
 
131
        # Listen for switch-tab signal from each tab
 
132
        for key, value in self.tab.items():
 
133
            self.tab_cb_ids.append((key, self.tab[key].connect ('switch-tab', self.change_tab)))
 
134
 
 
135
    def disconnect_signals (self):
 
136
        for id in self.player_cb_ids:
 
137
            self.sp.disconnect (id)
 
138
 
 
139
        self.ds['artist'].disconnect (self.ds_cb_id)
 
140
 
 
141
        for key, id in self.tab_cb_ids:
 
142
            self.tab[key].disconnect (id)
 
143
 
 
144
    def toggle_visibility (self, action):
 
145
        if not self.visible:
 
146
            self.shell.add_widget (self.vbox, RB.ShellUILocation.RIGHT_SIDEBAR, expand=True)
 
147
            self.visible = True
 
148
        else:
 
149
            self.shell.remove_widget (self.vbox, RB.ShellUILocation.RIGHT_SIDEBAR)
 
150
            self.visible = False
 
151
 
 
152
    def change_tab (self, tab, newtab):
 
153
        print "swapping tab from %s to %s" % (self.current, newtab)
 
154
        if (self.current != newtab):
 
155
            self.tab[self.current].deactivate()
 
156
            self.tab[newtab].activate()
 
157
            self.current = newtab
 
158
 
 
159
    def init_tabs (self):
 
160
        self.tab = {}
 
161
        self.ds = {}
 
162
        self.view = {}
 
163
 
 
164
        self.ds['artist'] = at.ArtistDataSource (self.info_cache, self.ranking_cache)
 
165
        self.view['artist'] = at.ArtistView (self.shell, self.plugin, self.webview, self.ds['artist'])
 
166
        self.tab['artist']  = at.ArtistTab (self.shell, self.buttons, self.ds['artist'], self.view['artist'])
 
167
        self.ds['album']    = abt.AlbumDataSource(self.info_cache, self.ranking_cache)
 
168
        self.view['album']  = abt.AlbumView(self.shell, self.plugin, self.webview, self.ds['album'])
 
169
        self.tab['album']   = abt.AlbumTab(self.shell, self.buttons, self.ds['album'], self.view['album'])
 
170
        self.ds['lyrics']   = lt.LyricsDataSource (self.db)
 
171
        self.view['lyrics'] = lt.LyricsView (self.shell, self.plugin, self.webview, self.ds['lyrics'])
 
172
        self.tab['lyrics']  = lt.LyricsTab (self.shell, self.buttons, self.ds['lyrics'], self.view['lyrics'])
 
173
        self.ds['links']   = lit.LinksDataSource (self.db)
 
174
        self.view['links'] = lit.LinksView (self.shell, self.plugin, self.webview)
 
175
        self.tab['links']  = lit.LinksTab (self.shell, self.buttons, self.ds['links'], self.view['links'])
 
176
 
 
177
    def load_top_five (self, ds):
 
178
        top_tracks = ds.get_top_tracks ()
 
179
        ## populate liststore
 
180
        if top_tracks is None:
 
181
            for i in range (0, 5):
 
182
                self.top_five_list.append(["%d. " % (i+1), ""])
 
183
        else:
 
184
            num_tracks = len(top_tracks)
 
185
            ## empty liststore
 
186
            self.top_five_list.clear()
 
187
            for i in range (0, 5):
 
188
                if i >= num_tracks : track = ""
 
189
                else : track = top_tracks[i]
 
190
                self.top_five_list.append(["%d. " % (i+1), str(track)])
 
191
 
 
192
    def playing_changed_cb (self, playing, user_data):
 
193
        # this sometimes happens on a streaming thread, so we need to
 
194
        # move it to the main thread
 
195
        gobject.idle_add (self.playing_changed_idle_cb)
 
196
 
 
197
    def playing_changed_idle_cb (self):
 
198
        if self.sp is None:
 
199
            return
 
200
 
 
201
        playing_entry = self.sp.get_playing_entry ()
 
202
        if playing_entry is None:
 
203
            return
 
204
 
 
205
        playing_artist = playing_entry.get_string(RB.RhythmDBPropType.ARTIST)
 
206
 
 
207
        if self.current_artist != playing_artist:
 
208
            self.current_artist = playing_artist.replace ('&', '&amp;')
 
209
            # Translators: 'top' here means 'most popular'.  %s is replaced by the artist name.
 
210
            self.label.set_markup(_('Top songs by %s') % ('<i>' + self.current_artist + '</i>'))
 
211
            self.ds['artist'].fetch_top_tracks (self.current_artist)
 
212
 
 
213
        self.tab[self.current].reload()
 
214
 
 
215
    def navigation_request_cb(self, view, frame, request):
 
216
        # open HTTP URIs externally.  this isn't a web browser.
 
217
        if request.get_uri().startswith('http'):
 
218
            print "opening uri %s" % request.get_uri()
 
219
            Gtk.show_uri(self.shell.props.window.get_screen(), request.get_uri(), Gdk.CURRENT_TIME)
 
220
 
 
221
            return 1        # WEBKIT_NAVIGATION_RESPONSE_IGNORE
 
222
        else:
 
223
            return 0        # WEBKIT_NAVIGATION_RESPONSE_ACCEPT
 
224
 
 
225
    def style_set_cb(self, widget, prev_style):
 
226
        self.apply_font_settings()
 
227
 
 
228
    def apply_font_settings(self):
 
229
        # FIXME apply font style
 
230
        # style = self.webview.style
 
231
        return
 
232
 
 
233
        font_size = style.font_desc.get_size()
 
234
        if style.font_desc.get_size_is_absolute() is False:
 
235
            font_size /= Pango.SCALE
 
236
        self.websettings.props.default_font_size = font_size
 
237
        self.websettings.props.default_font_family = style.font_desc.get_family()
 
238
        print "web view font settings: %s, %d" % (style.font_desc.get_family(), font_size)
 
239
 
 
240
    def init_gui(self):
 
241
        self.vbox = Gtk.VBox()
 
242
        frame = Gtk.Frame()
 
243
        self.label = Gtk.Label(_('Nothing Playing'))
 
244
        frame.set_shadow_type(Gtk.ShadowType.IN)
 
245
        frame.set_label_align(0.0,0.0)
 
246
        frame.set_label_widget(self.label)
 
247
        self.label.set_use_markup(True)
 
248
        self.label.set_padding(0,4)
 
249
 
 
250
        #----- set up top 5 tree view -----#
 
251
        self.top_five_list = Gtk.ListStore (gobject.TYPE_STRING, gobject.TYPE_STRING)
 
252
        top_five_view = Gtk.TreeView.new_with_model(self.top_five_list)
 
253
 
 
254
        top_five_tvc1 = Gtk.TreeViewColumn()
 
255
        top_five_tvc2 = Gtk.TreeViewColumn()
 
256
 
 
257
        top_five_view.append_column(top_five_tvc1)
 
258
        top_five_view.append_column(top_five_tvc2)
 
259
 
 
260
        crt = Gtk.CellRendererText()
 
261
 
 
262
        top_five_tvc1.pack_start(crt, True)
 
263
        top_five_tvc2.pack_start(crt, True)
 
264
 
 
265
        top_five_tvc1.add_attribute(crt, 'text', 0)
 
266
        top_five_tvc2.add_attribute(crt, 'text', 1)
 
267
        
 
268
        top_five_view.set_headers_visible( False )
 
269
        frame.add (top_five_view)
 
270
 
 
271
        #---- set up webkit pane -----#
 
272
        self.webview = WebKit.WebView()
 
273
        self.webview.connect("navigation-requested", self.navigation_request_cb)
 
274
        scroll = Gtk.ScrolledWindow()
 
275
        scroll.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
 
276
        scroll.set_shadow_type(Gtk.ShadowType.IN)
 
277
        scroll.add (self.webview)
 
278
 
 
279
        # set up webkit settings to match gtk font settings
 
280
        self.websettings = WebKit.WebSettings()
 
281
        self.webview.set_settings(self.websettings)
 
282
        self.apply_font_settings()
 
283
        self.webview.connect("style-set", self.style_set_cb)
 
284
 
 
285
        #----- set up button group -----#
 
286
        vbox2 = Gtk.VBox()
 
287
        self.buttons = Gtk.HBox()
 
288
 
 
289
        #---- pack everything into side pane ----#
 
290
        self.vbox.pack_start  (frame, False, True, 0)
 
291
        vbox2.pack_start (self.buttons, False, True, 0)
 
292
        vbox2.pack_start (scroll, True, True, 0)
 
293
        self.vbox.pack_start (vbox2, True, True, 0)
 
294
 
 
295
        self.vbox.show_all()
 
296
        self.vbox.set_size_request(200, -1)
 
297
        self.shell.add_widget (self.vbox, RB.ShellUILocation.RIGHT_SIDEBAR, True, True)