CoverArt Browser  v2.0
Browse your cover-art albums in Rhythmbox
/home/foss/Downloads/coverart-browser/coverart_entryview.py
00001 # -*- Mode: python; coding: utf-8; tab-width: 4; indent-tabs-mode: nil; -*-
00002 #
00003 # Copyright (C) 2012 - fossfreedom
00004 # Copyright (C) 2012 - Agustin Carrasco
00005 #
00006 # This program is free software; you can redistribute it and/or modify
00007 # it under the terms of the GNU General Public License as published by
00008 # the Free Software Foundation; either version 2, or (at your option)
00009 # any later version.
00010 #
00011 # This program is distributed in the hope that it will be useful,
00012 # but WITHOUT ANY WARRANTY; without even the implied warranty of
00013 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014 # GNU General Public License for more details.
00015 #
00016 # You should have received a copy of the GNU General Public License
00017 # along with this program; if not, write to the Free Software
00018 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
00019 
00020 from collections import OrderedDict
00021 
00022 from gi.repository import RB
00023 from gi.repository import Gtk
00024 from gi.repository import GObject
00025 from gi.repository import GdkPixbuf
00026 from gi.repository import Gdk
00027 from gi.repository import GLib
00028 
00029 from coverart_rb3compat import Menu
00030 from coverart_rb3compat import ActionGroup
00031 from coverart_browser_prefs import GSetting
00032 from coverart_browser_prefs import CoverLocale
00033 from coverart_external_plugins import CreateExternalPluginMenu
00034 from coverart_playlists import LastFMTrackPlaylist
00035 from coverart_playlists import EchoNestPlaylist
00036 from coverart_playlists import EchoNestGenrePlaylist
00037 from coverart_utils import create_button_image
00038 from coverart_external_plugins import ExternalPlugin
00039 from stars import ReactiveStar
00040 from coverart_search import CoverSearchPane
00041 from coverart_widgets import PixbufButton
00042 
00043 
00044 MIN_IMAGE_SIZE = 100
00045 
00046 
00047 class EntryViewPane(object):
00048     '''
00049         encapulates all of the Track Pane objects
00050     '''
00051 
00052     def __init__(self, shell, plugin, source, entry_view_grid, viewmgr):
00053         self.gs = GSetting()
00054 
00055         self.entry_view_grid = entry_view_grid
00056         self.shell = shell
00057         self.viewmgr = viewmgr
00058         self.plugin = plugin
00059         self.source = source
00060 
00061         # setup entry-view objects and widgets
00062         self.stack = Gtk.Stack()
00063         self.stack.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT_RIGHT)
00064         self.stack.set_transition_duration(750)
00065 
00066         # create entry views. Don't allow to reorder until the load is finished
00067         self.entry_view_compact = CoverArtCompactEntryView(self.shell, self.source)
00068         self.entry_view_full = CoverArtEntryView(self.shell, self.source)
00069         self.entry_view = self.entry_view_compact
00070         self.shell.props.library_source.get_entry_view().set_columns_clickable(
00071             False)
00072 
00073         self.entry_view_results = ResultsGrid()
00074         self.entry_view_results.initialise()
00075 
00076         self.stack.add_titled(self.entry_view_results, "notebook_tracks", _("Tracks"))
00077         self.entry_view_grid.attach(self.stack, 0, 0, 3, 1)
00078 
00079     def setup_source(self):
00080 
00081         colour = self.viewmgr.get_selection_colour()
00082         self.cover_search_pane = CoverSearchPane(self.plugin, colour)
00083 
00084         self.stack.add_titled(self.cover_search_pane, "notebook_covers", _("Covers"))
00085 
00086         # define entry-view toolbar
00087         self.stars = ReactiveStar()
00088         self.stars.set_rating(0)
00089         self.stars.connect('changed', self.rating_changed_callback)
00090         self.stars.props.valign = Gtk.Align.CENTER
00091         self.entry_view_grid.attach(self.stars, 1, 1, 1, 1)
00092         stack_switcher = Gtk.StackSwitcher()
00093         stack_switcher.set_stack(self.stack)
00094         self.entry_view_grid.attach(stack_switcher, 0, 1, 1, 1)
00095         viewtoggle = PixbufButton()
00096         viewtoggle.set_image(create_button_image(self.plugin, "entryview.png"))
00097         self.viewtoggle_id = None
00098 
00099         setting = self.gs.get_setting(self.gs.Path.PLUGIN)
00100         viewtoggle.set_active(not setting[self.gs.PluginKey.ENTRY_VIEW_MODE])
00101         self.entry_view_toggled(viewtoggle, True)
00102         viewtoggle.connect('toggled', self.entry_view_toggled)
00103 
00104         smallwindowbutton = PixbufButton()
00105         smallwindowbutton.set_image(create_button_image(self.plugin, "view-restore.png"))
00106         smallwindowbutton.connect('toggled', self.smallwindowbutton_callback)
00107 
00108         self.smallwindowext = ExternalPlugin()
00109         self.smallwindowext.appendattribute('plugin_name', 'smallwindow')
00110         self.smallwindowext.appendattribute('action_group_name', 'small window actions')
00111         self.smallwindowext.appendattribute('action_name', 'SmallWindow')
00112         self.smallwindowext.appendattribute('action_type', 'app')
00113 
00114         whatsplayingtoggle = PixbufButton()
00115         whatsplayingtoggle.set_image(create_button_image(self.plugin, "whatsplaying.png"))
00116         whatsplayingtoggle.connect('toggled', self.whatsplayingtoggle_callback)
00117 
00118         rightgrid = Gtk.Grid()
00119         rightgrid.props.halign = Gtk.Align.END
00120 
00121         #rightgrid.attach(whatsplayingtoggle, 0, 0, 1, 1)
00122         rightgrid.attach(viewtoggle, 1, 0, 1, 1)
00123         rightgrid.attach(smallwindowbutton, 2, 0, 1, 1)
00124 
00125         self.entry_view_grid.attach_next_to(rightgrid, self.stars, Gtk.PositionType.RIGHT, 1, 1)
00126         self.stack.set_visible_child(self.entry_view_results)
00127         self.stack.connect('notify::visible-child-name', self.notebook_switch_page_callback)
00128 
00129         self.entry_view_grid.show_all()
00130         smallwindowbutton.set_visible(self.smallwindowext.is_activated())
00131 
00132     def whatsplayingtoggle_callback(self, widget):
00133         self.entry_view_results.emit('whats-playing', widget.get_active())
00134 
00135 
00136     def smallwindowbutton_callback(self, widget):
00137         if widget.get_active():
00138             self.smallwindowext.activate(self.shell)
00139             widget.emit('clicked')
00140 
00141     def entry_view_toggled(self, widget, initialised=False):
00142         print("DEBUG - entry_view_toggled")
00143         if widget.get_active():
00144             next_view = self.entry_view_full
00145             show_coverart = False
00146             if self.viewtoggle_id:
00147                 self.shell.props.window.disconnect(self.viewtoggle_id)
00148                 self.viewtoggle_id = None
00149         else:
00150             next_view = self.entry_view_compact
00151             show_coverart = True
00152             self.viewtoggle_id = self.shell.props.window.connect('check_resize', self.entry_view_results.window_resize)
00153 
00154         setting = self.gs.get_setting(self.gs.Path.PLUGIN)
00155         setting[self.gs.PluginKey.ENTRY_VIEW_MODE] = not widget.get_active()
00156 
00157         self.entry_view_results.change_view(next_view, show_coverart)
00158         self.entry_view = next_view
00159         if not initialised:
00160             self.source.update_with_selection()
00161 
00162     def notebook_switch_page_callback(self, *args):
00163         '''
00164         Callback called when the notebook page gets switched. It initiates
00165         the cover search when the cover search pane's page is selected.
00166         '''
00167         print("CoverArtBrowser DEBUG - notebook_switch_page_callback")
00168 
00169         if self.stack.get_visible_child_name() == 'notebook_covers':
00170             self.viewmgr.current_view.switch_to_coverpane(self.cover_search_pane)
00171         else:
00172             entries = self.entry_view.get_selected_entries()
00173             if entries and len(entries) > 0:
00174                 self.entry_view_results.emit('update-cover', self.source, entries[0])
00175             else:
00176                 selected = self.viewmgr.current_view.get_selected_objects()
00177                 tracks = selected[0].get_tracks()
00178                 self.entry_view_results.emit('update-cover', self.source, tracks[0].entry)
00179 
00180         print("CoverArtBrowser DEBUG - end notebook_switch_page_callback")
00181 
00182     def rating_changed_callback(self, widget):
00183         '''
00184         Callback called when the Rating stars is changed
00185         '''
00186         print("CoverArtBrowser DEBUG - rating_changed_callback")
00187 
00188         rating = widget.get_rating()
00189 
00190         for album in self.viewmgr.current_view.get_selected_objects():
00191             album.rating = rating
00192 
00193         print("CoverArtBrowser DEBUG - end rating_changed_callback")
00194 
00195     def get_entry_view(self):
00196         return self.entry_view
00197 
00198     def update_cover(self, album_artist, manager):
00199         if not self.stack.get_visible_child_name() == "notebook_covers":
00200             return
00201 
00202         self.cover_search_pane.clear()
00203         self.cover_search(album_artist, manager)
00204 
00205     def cover_search(self, album_artist, manager):
00206         self.cover_search_pane.do_search(album_artist,
00207                                          manager.cover_man.update_cover)
00208 
00209     def update_selection(self, last_selected_album, click_count):
00210         '''
00211         Update the source view when an item gets selected.
00212         '''
00213         print("DEBUG - update_with_selection")
00214         selected = self.viewmgr.current_view.get_selected_objects()
00215 
00216         # clear the entry view
00217         self.entry_view.clear()
00218 
00219         cover_search_pane_visible = self.stack.get_visible_child_name() == "notebook_covers"
00220 
00221         if not selected:
00222             # clean cover tab if selected
00223             if cover_search_pane_visible:
00224                 self.cover_search_pane.clear()
00225 
00226             self.entry_view_results.emit('update-cover', self.source, None)
00227             return last_selected_album, click_count
00228         elif len(selected) == 1:
00229             self.stars.set_rating(selected[0].rating)
00230 
00231             if selected[0] is not last_selected_album:
00232                 # when the selection changes we've to take into account two
00233                 # things
00234                 if not click_count:
00235                     # we may be using the arrows, so if there is no mouse
00236                     # involved, we should change the last selected
00237                     last_selected_album = selected[0]
00238                 else:
00239                     # we may've doing a fast change after a valid second click,
00240                     # so it shouldn't be considered a double click
00241                     click_count -= 1
00242         else:
00243             self.stars.set_rating(0)
00244 
00245         if len(selected) == 1:
00246             self.source.artist_info.emit('selected',
00247                                          selected[0].artist,
00248                                          selected[0].name)
00249 
00250         self.entry_view.set_sorting_order('track-number', Gtk.SortType.ASCENDING)
00251 
00252         for album in selected:
00253             # add the album to the entry_view
00254             self.entry_view.add_album(album)
00255 
00256         if len(selected) > 0:
00257 
00258             def cover_update(*args):
00259                 print ("emitting")
00260                 self.entry_view_results.emit('update-cover',
00261                                          self.source,
00262                                          selected[0].get_tracks()[0].entry)
00263 
00264             # add a short delay to give the entry-pane time to expand etc.
00265             Gdk.threads_add_timeout(GLib.PRIORITY_DEFAULT_IDLE, 250, cover_update, None)
00266 
00267         # update the cover search pane with the first selected album
00268         if cover_search_pane_visible:
00269             self.cover_search_pane.do_search(selected[0],
00270                                              self.source.album_manager.cover_man.update_cover)
00271 
00272         return last_selected_album, click_count
00273 
00274 
00275 class ResultsGrid(Gtk.Grid):
00276     # signals
00277     __gsignals__ = {
00278         'update-cover': (GObject.SIGNAL_RUN_LAST, None, (GObject.Object, RB.RhythmDBEntry)),
00279         'whats-playing': (GObject.SIGNAL_RUN_LAST, None, (bool,))
00280     }
00281     image_width = 0
00282 
00283     def __init__(self, *args, **kwargs):
00284         super(ResultsGrid, self).__init__(*args, **kwargs)
00285 
00286     def initialise(self):
00287         self.pixbuf = None
00288 
00289         self.oldval = 0
00290         self.stack = Gtk.Stack()
00291         self.stack.set_transition_type(Gtk.StackTransitionType.CROSSFADE)
00292         self.stack.set_transition_duration(350)
00293 
00294         self.image1 = Gtk.Image()
00295         self.image1.props.hexpand = True
00296         self.image1.props.vexpand = True
00297         self.stack.add_named(self.image1, "image1")
00298 
00299         self.image2 = Gtk.Image()
00300         self.image2.props.hexpand = True
00301         self.image2.props.vexpand = True
00302         self.stack.add_named(self.image2, "image2")
00303 
00304         self.frame = Gtk.AspectFrame.new("", 0.5, 0.5, 1, False)
00305         self.update_cover(None, None, None)
00306         scroll = Gtk.ScrolledWindow()
00307         scroll.add_with_viewport(self.stack)
00308         scroll.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.NEVER)
00309         scroll.set_resize_mode(Gtk.ResizeMode.QUEUE)
00310 
00311         self.frame.add(scroll)
00312         self._signal_connected = None
00313 
00314         self.attach(self.frame, 6, 0, 1, 1)
00315         self.connect('update-cover', self.update_cover)
00316         self.connect('whats-playing', self.display_whats_playing)
00317 
00318         #lets fix the situation where some-themes background colour is incorrectly defined
00319         #in these cases the background colour is black
00320         context = self.get_style_context()
00321         bg_colour = context.get_background_color(Gtk.StateFlags.NORMAL)
00322         if bg_colour == Gdk.RGBA(0, 0, 0, 0):
00323             color = context.get_color(Gtk.StateFlags.NORMAL)
00324             self.override_background_color(Gtk.StateType.NORMAL, color)
00325 
00326     def update_cover(self, widget, source, entry):
00327 
00328         print ('update_cover')
00329         self.oldval = 0  # force a redraw
00330         if entry:
00331             print ('entry')
00332             album = source.album_manager.model.get_from_dbentry(entry)
00333             self.pixbuf = GdkPixbuf.Pixbuf().new_from_file(album.cover.original)
00334             self.window_resize(None)
00335             self.frame.set_shadow_type(Gtk.ShadowType.NONE)
00336         else:
00337             print ('no pixbuf')
00338             self.pixbuf = None
00339             self.frame.set_shadow_type(Gtk.ShadowType.ETCHED_OUT)
00340 
00341         if self.stack.get_visible_child_name() == "image1":
00342             self.image1.queue_draw()
00343         else:
00344             self.image2.queue_draw()
00345 
00346     def display_whats_playing(self, show_playing):
00347         view = self.get_child_at(0, 0)
00348 
00349         view.display_playing_tracks(show_playing)
00350 
00351     def window_resize(self, widget):
00352         alloc = self.get_allocation()
00353         if alloc.height < 10:
00354             print ('less than')
00355             return
00356 
00357         if (alloc.width / 3) <= (MIN_IMAGE_SIZE + 30) or \
00358                         (alloc.height) <= (MIN_IMAGE_SIZE + 30):
00359             self.frame.props.visible = False
00360         else:
00361             self.frame.props.visible = True
00362 
00363         framealloc = self.frame.get_allocation()
00364         minval = min(framealloc.width - 30, framealloc.height - 30)
00365         if self.oldval == minval:
00366             print (self.oldval)
00367             return
00368         print("resizing")
00369         print (self.pixbuf)
00370         print (minval)
00371         self.oldval = minval
00372         if self.pixbuf:
00373             p = self.pixbuf.scale_simple(minval, minval, GdkPixbuf.InterpType.BILINEAR)
00374         else:
00375             p = None
00376 
00377         if self.stack.get_visible_child_name() == "image1":
00378             self.image2.set_from_pixbuf(p)
00379             self.stack.set_visible_child_name("image2")
00380         else:
00381             self.image1.set_from_pixbuf(p)
00382             self.stack.set_visible_child_name("image1")
00383 
00384     def change_view(self, entry_view, show_coverart):
00385         print("debug - change_view")
00386         widget = self.get_child_at(0, 0)
00387         if widget:
00388             self.remove(widget)
00389 
00390         if not show_coverart:
00391             widget = self.get_child_at(6, 0)
00392             if widget:
00393                 self.remove(widget)
00394 
00395         entry_view.props.hexpand = True
00396         entry_view.props.vexpand = True
00397         self.attach(entry_view, 0, 0, 3, 1)
00398 
00399         if show_coverart:
00400             self.attach(self.frame, 6, 0, 1, 1)
00401 
00402         self.show_all()
00403 
00404 
00405 class BaseView(RB.EntryView):
00406 
00407     def __init__(self, shell, source):
00408         '''
00409         Initializes the entryview.
00410         '''
00411         self.shell = shell
00412         self.source = source
00413         self.plugin = self.source.props.plugin
00414 
00415         super(RB.EntryView, self).__init__(db=shell.props.db,
00416                                            shell_player=shell.props.shell_player, is_drag_source=True,
00417                                            visible_columns=[])
00418 
00419         cl = CoverLocale()
00420         cl.switch_locale(cl.Locale.RB)
00421 
00422         self.display_columns()
00423 
00424         cl.switch_locale(cl.Locale.LOCALE_DOMAIN)
00425 
00426         self.define_menu()
00427 
00428         # connect signals to the shell to know when the playing state changes
00429         self.shell.props.shell_player.connect('playing-song-changed',
00430                                               self.playing_song_changed)
00431         self.shell.props.shell_player.connect('playing-changed',
00432                                               self.playing_changed)
00433 
00434         self.actiongroup = ActionGroup(self.shell, 'coverentryplaylist_submenu')
00435 
00436         self.external_plugins = None
00437 
00438         self.source_query_model = self.source.source_query_model  # RB.RhythmDBQueryModel.new_empty(self.shell.props.db)
00439         self.qm = RB.RhythmDBQueryModel.new_empty(self.shell.props.db)
00440         self.set_model(self.qm)
00441 
00442         self.connect_library_signals()
00443         self.echonest_similar_playlist = None
00444         self.echonest_similar_genre_playlist = None
00445         self.lastfm_similar_playlist = None
00446 
00447         self.connect('selection-changed', self.selection_changed)
00448 
00449         self.artists = ""
00450 
00451         print ("end constructor")
00452 
00453     def __del__(self):
00454         del self.action_group
00455         del self.play_action
00456         del self.queue_action
00457 
00458     def connect_library_signals(self):
00459         # connect the sort-order to the library source sort
00460         library_view = self.shell.props.library_source.get_entry_view()
00461         library_view.connect('notify::sort-order',
00462                              self._on_library_sorting_changed)
00463         self._on_library_sorting_changed(library_view,
00464                                          library_view.props.sort_order)
00465 
00466         # connect to the sort-order property
00467         self.connect('notify::sort-order', self._notify_sort_order,
00468                      library_view)
00469 
00470         self.set_columns_clickable(False)
00471 
00472 
00473     def display_playing_tracks(self, show_playing):
00474         pass
00475 
00476     def define_menu(self):
00477         pass
00478 
00479     def display_columns(self):
00480         pass
00481 
00482     def selection_changed(self, entry_view):
00483         entries = entry_view.get_selected_entries()
00484         if entries and len(entries) > 0:
00485             self.source.entryviewpane.entry_view_results.emit('update-cover', self.source, entries[0])
00486 
00487     def add_album(self, album):
00488         print("CoverArtBrowser DEBUG - add_album()")
00489         tracks = album.get_tracks()
00490 
00491         for track in tracks:
00492             self.qm.add_entry(track.entry, -1)
00493 
00494         (_, playing) = self.shell.props.shell_player.get_playing()
00495         self.playing_changed(self.shell.props.shell_player, playing)
00496 
00497         artists = album.artists.split(', ')
00498         if self.artists == "":
00499             self.artists = artists
00500         else:
00501             self.artists = list(set(self.artists + artists))
00502 
00503         print("CoverArtBrowser DEBUG - add_album()")
00504 
00505     def clear(self):
00506         print("CoverArtBrowser DEBUG - clear()")
00507 
00508         for row in self.qm:
00509             self.qm.remove_entry(row[0])
00510 
00511         self.artists = ""
00512 
00513         print("CoverArtBrowser DEBUG - clear()")
00514 
00515     def do_entry_activated(self, entry):
00516         print("CoverArtBrowser DEBUG - do_entry_activated()")
00517         self.select_entry(entry)
00518         self.play_track_menu_item_callback(entry)
00519         print("CoverArtBrowser DEBUG - do_entry_activated()")
00520         return True
00521 
00522     def pre_popup_menu_callback(self, *args):
00523         pass
00524 
00525     def do_show_popup(self, over_entry):
00526         if over_entry:
00527             print("CoverArtBrowser DEBUG - do_show_popup()")
00528 
00529             self.popup.popup(self.source,
00530                              'entryview_popup_menu', 0, Gtk.get_current_event_time())
00531 
00532         return over_entry
00533 
00534     def play_similar_artist_menu_item_callback(self, *args):
00535         if not self.echonest_similar_playlist:
00536             self.echonest_similar_playlist = \
00537                 EchoNestPlaylist(self.shell,
00538                                  self.shell.props.queue_source)
00539 
00540         selected = self.get_selected_entries()
00541         entry = selected[0]
00542         self.echonest_similar_playlist.start(entry, reinitialise=True)
00543 
00544     def play_similar_genre_menu_item_callback(self, *args):
00545         if not self.echonest_similar_genre_playlist:
00546             self.echonest_similar_genre_playlist = \
00547                 EchoNestGenrePlaylist(self.shell,
00548                                       self.shell.props.queue_source)
00549 
00550         selected = self.get_selected_entries()
00551         entry = selected[0]
00552         self.echonest_similar_genre_playlist.start(entry, reinitialise=True)
00553 
00554     def play_similar_track_menu_item_callback(self, *args):
00555         if not self.lastfm_similar_playlist:
00556             self.lastfm_similar_playlist = \
00557                 LastFMTrackPlaylist(self.shell,
00558                                     self.shell.props.queue_source)
00559 
00560         selected = self.get_selected_entries()
00561         entry = selected[0]
00562         self.lastfm_similar_playlist.start(entry, reinitialise=True)
00563 
00564 
00565     def play_track_menu_item_callback(self, *args):
00566         print("CoverArtBrowser DEBUG - play_track_menu_item_callback()")
00567 
00568         for row in self.source_query_model:
00569             self.source_query_model.remove_entry(row[0])
00570 
00571         selected = self.get_selected_entries()
00572         entry = selected[0]
00573 
00574         if len(selected) == 1:
00575             self.source_query_model.copy_contents(self.qm)
00576         else:
00577             self.add_tracks_to_source(self.source_query_model)
00578 
00579         self.source.props.query_model = self.source_query_model
00580 
00581         #library_view = self.shell.props.library_source.get_entry_view()
00582         #library_view.set_sorting_order('track-number', Gtk.SortType.ASCENDING)
00583         #self.set_sorting_order('track-number', Gtk.SortType.ASCENDING)
00584 
00585         # Start the music
00586         player = self.shell.props.shell_player
00587         player.play_entry(entry, self.source)
00588 
00589         print("CoverArtBrowser DEBUG - play_track_menu_item_callback()")
00590 
00591     def queue_track_menu_item_callback(self, *args):
00592         print("CoverArtBrowser DEBUG - queue_track_menu_item_callback()")
00593 
00594         self.add_tracks_to_source(self.shell.props.queue_source)
00595 
00596     def add_to_playing_menu_item_callback(self, *args):
00597         print("CoverArtBrowser DEBUG - add_to_playing_menu_item_callback()")
00598         self.add_tracks_to_source(None)
00599 
00600     def add_tracks_to_source(self, source):
00601 
00602         if source == None:
00603             source = self.source_query_model
00604 
00605         selected = self.get_selected_entries()
00606         selected.reverse()
00607 
00608         selected = sorted(selected,
00609                           key=lambda song: song.get_ulong(RB.RhythmDBPropType.TRACK_NUMBER))
00610 
00611         for entry in selected:
00612             source.add_entry(entry, -1)
00613 
00614         print("CoverArtBrowser DEBUG - queue_track_menu_item_callback()")
00615 
00616     def love_track(self, rating):
00617         '''
00618         utility function to set the rating for selected tracks
00619         '''
00620         selected = self.get_selected_entries()
00621 
00622         for entry in selected:
00623             self.shell.props.db.entry_set(entry, RB.RhythmDBPropType.RATING,
00624                                           rating)
00625 
00626         self.shell.props.db.commit()
00627 
00628     def show_properties_menu_item_callback(self, *args):
00629         print("CoverArtBrowser DEBUG - show_properties_menu_item_callback()")
00630 
00631         info_dialog = RB.SongInfo(source=self.source, entry_view=self)
00632         info_dialog.show_all()
00633 
00634         print("CoverArtBrowser DEBUG - show_properties_menu_item_callback()")
00635 
00636     def playing_song_changed(self, shell_player, entry):
00637         print("CoverArtBrowser DEBUG - playing_song_changed()")
00638 
00639         if entry is not None and self.get_entry_contained(entry):
00640             self.set_state(RB.EntryViewState.PLAYING)
00641         else:
00642             self.set_state(RB.EntryViewState.NOT_PLAYING)
00643 
00644         print("CoverArtBrowser DEBUG - playing_song_changed()")
00645 
00646     def playing_changed(self, shell_player, playing):
00647         print("CoverArtBrowser DEBUG - playing_changed()")
00648         entry = shell_player.get_playing_entry()
00649 
00650         if entry is not None and self.get_entry_contained(entry):
00651             if playing:
00652                 self.set_state(RB.EntryViewState.PLAYING)
00653             else:
00654                 self.set_state(RB.EntryViewState.PAUSED)
00655         else:
00656             self.set_state(RB.EntryViewState.NOT_PLAYING)
00657 
00658         print("CoverArtBrowser DEBUG - playing_changed()")
00659 
00660     def add_playlist_menu_item_callback(self, *args):
00661         print("CoverArtBrowser DEBUG - add_playlist_menu_item_callback")
00662         playlist_manager = self.shell.props.playlist_manager
00663         playlist = playlist_manager.new_playlist(_('New Playlist'), False)
00664 
00665         self.add_tracks_to_source(playlist)
00666 
00667     def playlist_menu_item_callback(self, *args):
00668         pass
00669 
00670     def add_to_static_playlist_menu_item_callback(self, action, param, args):
00671         print("CoverArtBrowser DEBUG - " + \
00672               "add_to_static_playlist_menu_item_callback")
00673 
00674         playlist = args['playlist']
00675         self.add_tracks_to_source(playlist)
00676 
00677     def _on_library_sorting_changed(self, view, _):
00678         self._old_sort_order = self.props.sort_order
00679 
00680         self.set_sorting_type(view.props.sort_order)
00681 
00682     def _notify_sort_order(self, view, _, library_view):
00683         if self.props.sort_order != self._old_sort_order:
00684             self.resort_model()
00685 
00686             # update library source's view direction
00687             library_view.set_sorting_type(self.props.sort_order)
00688 
00689 
00690 class CoverArtCompactEntryView(BaseView):
00691     __hash__ = GObject.__hash__
00692 
00693     def __init__(self, shell, source):
00694         '''
00695         Initializes the entryview.
00696         '''
00697         super(CoverArtCompactEntryView, self).__init__(shell, source)
00698 
00699     def display_columns(self):
00700 
00701         self.col_map = OrderedDict([
00702             ('track-number', RB.EntryViewColumn.TRACK_NUMBER),
00703             ('title', RB.EntryViewColumn.TITLE),
00704             ('artist', RB.EntryViewColumn.ARTIST),
00705             ('rating', RB.EntryViewColumn.RATING),
00706             ('duration', RB.EntryViewColumn.DURATION)
00707         ])
00708 
00709         for entry in self.col_map:
00710             visible = False if entry == 'artist' else True
00711             self.append_column(self.col_map[entry], visible)
00712 
00713     def add_album(self, album):
00714         super(CoverArtCompactEntryView, self).add_album(album)
00715 
00716         if len(self.artists) > 1:
00717             self.get_column(RB.EntryViewColumn.ARTIST).set_visible(True)
00718         else:
00719             self.get_column(RB.EntryViewColumn.ARTIST).set_visible(False)
00720 
00721     def define_menu(self):
00722         popup = Menu(self.plugin, self.shell)
00723         popup.load_from_file('N/A',
00724                              'ui/coverart_entryview_compact_pop_rb3.ui')
00725         signals = {
00726             'ev_compact_play_track_menu_item': self.play_track_menu_item_callback,
00727             'ev_compact_queue_track_menu_item': self.queue_track_menu_item_callback,
00728             'ev_compact_add_to_playing_menu_item': self.add_to_playing_menu_item_callback,
00729             'ev_compact_new_playlist': self.add_playlist_menu_item_callback,
00730             'ev_compact_show_properties_menu_item': self.show_properties_menu_item_callback,
00731             'ev_compact_similar_track_menu_item': self.play_similar_track_menu_item_callback,
00732             'ev_compact_similar_artist_menu_item': self.play_similar_artist_menu_item_callback,
00733             'ev_compact_similar_genre_menu_item': self.play_similar_genre_menu_item_callback}
00734 
00735         popup.connect_signals(signals)
00736         popup.connect('pre-popup', self.pre_popup_menu_callback)
00737         self.popup = popup
00738 
00739     def playlist_menu_item_callback(self, *args):
00740         print("CoverArtBrowser DEBUG - playlist_menu_item_callback")
00741 
00742         self.source.playlist_fillmenu(self.popup, 'ev_compact_playlist_sub_menu_item', 'ev_compact_playlist_section',
00743                                       self.actiongroup, self.add_to_static_playlist_menu_item_callback)
00744 
00745     def pre_popup_menu_callback(self, *args):
00746         '''
00747         Callback when the popup menu is about to be displayed
00748         '''
00749 
00750         state, sensitive = self.shell.props.shell_player.get_playing()
00751         if not state:
00752             sensitive = False
00753 
00754         self.popup.set_sensitive('ev_compact_add_to_playing_menu_item', sensitive)
00755 
00756         if not self.external_plugins:
00757             self.external_plugins = \
00758                 CreateExternalPluginMenu("ev_compact_entryview", 5, self.popup)
00759             self.external_plugins.create_menu('entryview_compact_popup_menu')
00760 
00761         self.playlist_menu_item_callback()
00762 
00763 
00764 class CoverArtEntryView(BaseView):
00765     __hash__ = GObject.__hash__
00766 
00767     def __init__(self, shell, source):
00768         '''
00769         Initializes the entryview.
00770         '''
00771         super(CoverArtEntryView, self).__init__(shell, source)
00772 
00773     def display_columns(self):
00774 
00775         self.col_map = OrderedDict([
00776             ('track-number', RB.EntryViewColumn.TRACK_NUMBER),
00777             ('title', RB.EntryViewColumn.TITLE),
00778             ('genre', RB.EntryViewColumn.GENRE),
00779             ('artist', RB.EntryViewColumn.ARTIST),
00780             ('album', RB.EntryViewColumn.ALBUM),
00781             ('composer', RB.EntryViewColumn.COMPOSER),
00782             ('date', RB.EntryViewColumn.YEAR),
00783             ('duration', RB.EntryViewColumn.DURATION),
00784             ('bitrate', RB.EntryViewColumn.QUALITY),
00785             ('play-count', RB.EntryViewColumn.PLAY_COUNT),
00786             ('beats-per-minute', RB.EntryViewColumn.BPM),
00787             ('comment', RB.EntryViewColumn.COMMENT),
00788             ('location', RB.EntryViewColumn.LOCATION),
00789             ('rating', RB.EntryViewColumn.RATING),
00790             ('last-played', RB.EntryViewColumn.LAST_PLAYED),
00791             ('first-seen', RB.EntryViewColumn.FIRST_SEEN)
00792         ])
00793 
00794         for entry in self.col_map:
00795             visible = True if entry == 'title' else False
00796             self.append_column(self.col_map[entry], visible)
00797 
00798         # connect the visible-columns global setting to update our entryview
00799         gs = GSetting()
00800         rhythm_settings = gs.get_setting(gs.Path.RBSOURCE)
00801         rhythm_settings.connect('changed::visible-columns',
00802                                 self.on_visible_columns_changed)
00803         self.on_visible_columns_changed(rhythm_settings, 'visible-columns')
00804 
00805     def on_visible_columns_changed(self, settings, key):
00806         print("CoverArtBrowser DEBUG - on_visible_columns_changed()")
00807         # reset current columns
00808         print("CoverArtBrowser DEBUG - end on_visible_columns_changed()")
00809         for entry in self.col_map:
00810             col = self.get_column(self.col_map[entry])
00811             if entry in settings[key]:
00812                 col.set_visible(True)
00813             else:
00814                 if entry != 'title':
00815                     col.set_visible(False)
00816 
00817         print("CoverArtBrowser DEBUG - end on_visible_columns_changed()")
00818 
00819     def define_menu(self):
00820         popup = Menu(self.plugin, self.shell)
00821         popup.load_from_file('N/A',
00822                              'ui/coverart_entryview_full_pop_rb3.ui')
00823         signals = {
00824             'ev_full_play_track_menu_item': self.play_track_menu_item_callback,
00825             'ev_full_queue_track_menu_item': self.queue_track_menu_item_callback,
00826             'ev_full_add_to_playing_menu_item': self.add_to_playing_menu_item_callback,
00827             'ev_full_new_playlist': self.add_playlist_menu_item_callback,
00828             'ev_full_show_properties_menu_item': self.show_properties_menu_item_callback,
00829             'ev_full_similar_track_menu_item': self.play_similar_track_menu_item_callback,
00830             'ev_full_similar_artist_menu_item': self.play_similar_artist_menu_item_callback,
00831             'ev_full_similar_genre_menu_item': self.play_similar_genre_menu_item_callback}
00832 
00833         popup.connect_signals(signals)
00834         popup.connect('pre-popup', self.pre_popup_menu_callback)
00835         self.popup = popup
00836 
00837     def playlist_menu_item_callback(self, *args):
00838         print("CoverArtBrowser DEBUG - playlist_menu_item_callback")
00839 
00840         self.source.playlist_fillmenu(self.popup, 'ev_full_playlist_sub_menu_item', 'ev_full_playlist_section',
00841                                       self.actiongroup, self.add_to_static_playlist_menu_item_callback)
00842 
00843     def pre_popup_menu_callback(self, *args):
00844         '''
00845         Callback when the popup menu is about to be displayed
00846         '''
00847 
00848         state, sensitive = self.shell.props.shell_player.get_playing()
00849         if not state:
00850             sensitive = False
00851 
00852         self.popup.set_sensitive('ev_full_add_to_playing_menu_item', sensitive)
00853 
00854         if not self.external_plugins:
00855             self.external_plugins = \
00856                 CreateExternalPluginMenu("ev_full_entryview", 5, self.popup)
00857             self.external_plugins.create_menu('entryview_full_popup_menu')
00858 
00859         self.playlist_menu_item_callback()
00860 
00861 
00862 GObject.type_register(CoverArtEntryView)
00863 GObject.type_register(CoverArtCompactEntryView)
00864 
 All Classes Functions