1
# -*- Mode: python; coding: utf-8; tab-width: 8; indent-tabs-mode: t; -*-
3
# Copyright (C) 2009 John Iacona
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)
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.
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.
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.
30
import ArtistTab as at
31
import AlbumTab as abt
32
import LyricsTab as lt
33
import LinksTab as lit
36
from gi.repository import Gtk, Gdk, Pango, Gio
37
from gi.repository import RB
38
from gi.repository import WebKit
42
<toolbar name="ToolBar">
43
<toolitem name="Context" action="ToggleContextView" />
48
class ContextView (gobject.GObject):
50
def __init__ (self, shell, plugin):
51
gobject.GObject.__init__ (self)
53
self.sp = shell.props.shell_player
54
self.db = shell.props.db
57
self.current_artist = None
58
self.current_album = None
59
self.current_song = None
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'),
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'),
75
# maybe move this into an idle handler?
76
self.info_cache.clean()
77
self.ranking_cache.clean()
82
self.connect_signals ()
83
self.load_top_five (self.ds['artist'])
85
# Set currently displayed tab
86
# TODO: make this persistent via gsettings key
87
self.current = 'artist'
88
self.tab[self.current].activate ()
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)
101
def deactivate (self, shell):
103
self.disconnect_signals ()
104
self.player_cb_ids = None
105
self.tab_cb_ids = None
113
shell.remove_widget (self.vbox, RB.ShellUILocation.RIGHT_SIDEBAR)
118
self.websettings = 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)
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)
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)))
135
def disconnect_signals (self):
136
for id in self.player_cb_ids:
137
self.sp.disconnect (id)
139
self.ds['artist'].disconnect (self.ds_cb_id)
141
for key, id in self.tab_cb_ids:
142
self.tab[key].disconnect (id)
144
def toggle_visibility (self, action):
146
self.shell.add_widget (self.vbox, RB.ShellUILocation.RIGHT_SIDEBAR, expand=True)
149
self.shell.remove_widget (self.vbox, RB.ShellUILocation.RIGHT_SIDEBAR)
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
159
def init_tabs (self):
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'])
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), ""])
184
num_tracks = len(top_tracks)
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)])
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)
197
def playing_changed_idle_cb (self):
201
playing_entry = self.sp.get_playing_entry ()
202
if playing_entry is None:
205
playing_artist = playing_entry.get_string(RB.RhythmDBPropType.ARTIST)
207
if self.current_artist != playing_artist:
208
self.current_artist = playing_artist.replace ('&', '&')
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)
213
self.tab[self.current].reload()
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)
221
return 1 # WEBKIT_NAVIGATION_RESPONSE_IGNORE
223
return 0 # WEBKIT_NAVIGATION_RESPONSE_ACCEPT
225
def style_set_cb(self, widget, prev_style):
226
self.apply_font_settings()
228
def apply_font_settings(self):
229
# FIXME apply font style
230
# style = self.webview.style
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)
241
self.vbox = Gtk.VBox()
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)
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)
254
top_five_tvc1 = Gtk.TreeViewColumn()
255
top_five_tvc2 = Gtk.TreeViewColumn()
257
top_five_view.append_column(top_five_tvc1)
258
top_five_view.append_column(top_five_tvc2)
260
crt = Gtk.CellRendererText()
262
top_five_tvc1.pack_start(crt, True)
263
top_five_tvc2.pack_start(crt, True)
265
top_five_tvc1.add_attribute(crt, 'text', 0)
266
top_five_tvc2.add_attribute(crt, 'text', 1)
268
top_five_view.set_headers_visible( False )
269
frame.add (top_five_view)
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)
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)
285
#----- set up button group -----#
287
self.buttons = Gtk.HBox()
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)
296
self.vbox.set_size_request(200, -1)
297
self.shell.add_widget (self.vbox, RB.ShellUILocation.RIGHT_SIDEBAR, True, True)