def __init__(self, *args, **kwargs): ''' Initializes the button. ''' Gtk.RadioButton.__init__(self, *args, **kwargs) OptionsWidget.__init__(self, *args, **kwargs) gs = GSetting() setting = gs.get_setting(gs.Path.PLUGIN) setting.bind(gs.PluginKey.BUTTON_RELIEF, self, 'button_relief', Gio.SettingsBindFlags.GET) self.connect('notify::button-relief', self.on_notify_button_relief) # initialise some variables self.image_display = False self.initialised = False #ensure button appearance rather than standard radio toggle self.set_mode(False) #label colours self._not_active_colour = None self._active_colour = None
def __init__(self, plugin, viewmgr): super(ArtistSortPopupController, self).__init__() self._viewmgr = viewmgr self.plugin = plugin # sorts dictionary cl = CoverLocale() cl.switch_locale(cl.Locale.LOCALE_DOMAIN) self.values = OrderedDict([(_('Sort by album name'), 'name_artist'), (_('Sort by year'), 'year_artist'), (_('Sort by rating'), 'rating_artist')]) self.options = list(self.values.keys()) # get the current sort key and initialise the superclass gs = GSetting() source_settings = gs.get_setting(gs.Path.PLUGIN) value = source_settings[gs.PluginKey.SORT_BY_ARTIST] if value not in list(self.values.values()): print ("here") value = 'name_artist' source_settings[gs.PluginKey.SORT_BY_ARTIST]=value self._spritesheet = None self.update_images(False) self.current_key = list(self.values.keys())[ list(self.values.values()).index(value)] print (self.current_key)
def sort(self): albums = SortedCollection(key=lambda album: getattr(album, 'name')) gs = GSetting() source_settings = gs.get_setting(gs.Path.PLUGIN) key = source_settings[gs.PluginKey.SORT_BY_ARTIST] order = source_settings[gs.PluginKey.SORT_ORDER_ARTIST] sort_keys = { 'name_artist': ('album_sort', 'album_sort'), 'year_artist': ('real_year', 'calc_year_sort'), 'rating_artist': ('rating', 'album_sort') } props = sort_keys[key] def key_function(album): keys = [getattr(album, prop) for prop in props] return keys # remember the current sort then remove the sort order # because sorting will only work in unsorted lists sortSettings = self.store.get_sort_column_id() self.store.set_sort_column_id(-1, Gtk.SortType.ASCENDING) for artist in self._iters: albums.clear() albums.key = key_function if 'album' in self._iters[artist] and len( self._iters[artist]['album']) > 1: # we only need to sort an artists albums if there is more than one album # sort all the artists albums for album_iter in self._iters[artist]['album']: albums.insert(self._tree_store[album_iter][ self.columns['artist_album']]) if not order: albums = reversed(albums) # now we iterate through the sorted artist albums. Look and swap iters # according to where they are in the tree store artist_iter = self._iters[artist]['iter'] next_iter = self._tree_store.iter_children(artist_iter) for album in albums: if self._tree_store[next_iter][ self.columns['artist_album']] != album: self._tree_store.swap(next_iter, self._albumiters[album]['iter']) next_iter = self._albumiters[album]['iter'] next_iter = self._tree_store.iter_next(next_iter) # now we have finished sorting, reapply the sort if sortSettings[0]: self.store.set_sort_column_id(*sortSettings)
def __init__(self, plugin, album_model): super(SortPopupController, self).__init__() self._album_model = album_model self.plugin = plugin # sorts dictionary cl = CoverLocale() cl.switch_locale(cl.Locale.LOCALE_DOMAIN) self.values = OrderedDict([(_('Sort by album name'), 'name'), (_('Sort by album artist'), 'artist'), (_('Sort by year'), 'year'), (_('Sort by rating'), 'rating')]) self.options = self.values.keys() # get the current sort key and initialise the superclass gs = GSetting() source_settings = gs.get_setting(gs.Path.PLUGIN) value = source_settings[gs.PluginKey.SORT_BY] self._spritesheet = None self.update_images(False) self.current_key = self.values.keys()[self.values.values().index( value)]
def initialise(self, plugin, shell, callback): ''' extend the default initialise function because we need to also resize the picture associated with the sort button as well as find the saved sort order ''' if self.is_initialised: return self._spritesheet = ConfiguredSpriteSheet(plugin, 'sort') gs = GSetting() source_settings = gs.get_setting(gs.Path.PLUGIN) source_settings.bind(gs.PluginKey.SORT_BY, self, 'sort_by', Gio.SettingsBindFlags.DEFAULT) super(SortPopupButton, self).initialise(shell, callback, self.sorts[self.sort_by]) # create the pop up menu for key, text in sorted(self.sorts.iteritems()): self.add_menuitem(text, self._sort_changed, key) self._sort_changed(None, self.sort_by)
def display_columns(self): self.col_map = OrderedDict([ ('track-number', RB.EntryViewColumn.TRACK_NUMBER), ('title', RB.EntryViewColumn.TITLE), ('genre', RB.EntryViewColumn.GENRE), ('artist', RB.EntryViewColumn.ARTIST), ('album', RB.EntryViewColumn.ALBUM), ('composer', RB.EntryViewColumn.COMPOSER), ('date', RB.EntryViewColumn.YEAR), ('duration', RB.EntryViewColumn.DURATION), ('bitrate', RB.EntryViewColumn.QUALITY), ('play-count', RB.EntryViewColumn.PLAY_COUNT), ('beats-per-minute', RB.EntryViewColumn.BPM), ('comment', RB.EntryViewColumn.COMMENT), ('location', RB.EntryViewColumn.LOCATION), ('rating', RB.EntryViewColumn.RATING), ('last-played', RB.EntryViewColumn.LAST_PLAYED), ('first-seen', RB.EntryViewColumn.FIRST_SEEN) ]) for entry in self.col_map: visible = True if entry == 'title' else False self.append_column(self.col_map[entry], visible) # connect the visible-columns global setting to update our entryview gs = GSetting() rhythm_settings = gs.get_setting(gs.Path.RBSOURCE) rhythm_settings.connect('changed::visible-columns', self.on_visible_columns_changed) self.on_visible_columns_changed(rhythm_settings, 'visible-columns')
def __init__(self, plugin, viewmgr): super(SortPopupController, self).__init__() self._viewmgr = viewmgr self.plugin = plugin # sorts dictionary cl = CoverLocale() cl.switch_locale(cl.Locale.LOCALE_DOMAIN) self.values = OrderedDict( [ (_("Sort by album name"), "name"), (_("Sort by album artist"), "artist"), (_("Sort by year"), "year"), (_("Sort by rating"), "rating"), ] ) self.options = list(self.values.keys()) # get the current sort key and initialise the superclass gs = GSetting() source_settings = gs.get_setting(gs.Path.PLUGIN) value = source_settings[gs.PluginKey.SORT_BY] self._spritesheet = None self.update_images(False) self.current_key = list(self.values.keys())[list(self.values.values()).index(value)]
def on_notify_view_name(self, *args): if self._lastview and self.view_name != self._lastview: selected = self._views[self._lastview].get_selected_objects() current_album = None if len(selected) > 0: current_album = self._views[self._lastview].get_selected_objects()[0] self.window.remove(self._views[self._lastview].view) self.window.add(self._views[self.view_name].view) self.window.show_all() self.click_count = 0 self._views[self._lastview].panedposition = self.source.paned.get_expansion_status() self._views[self.view_name].switch_to_view(self.source, current_album) self.source.paned.expand(self._views[self.view_name].panedposition) self._lastview = self.view_name self.current_view.set_popup_menu(self.source.popup_menu) self.source.album_manager.current_view = self.current_view gs = GSetting() setting = gs.get_setting(gs.Path.PLUGIN) setting[gs.PluginKey.VIEW_NAME] = self.view_name self.emit('new-view')
def __init__(self, plugin, viewmgr): super(ArtistSortPopupController, self).__init__() self._viewmgr = viewmgr self.plugin = plugin # sorts dictionary cl = CoverLocale() cl.switch_locale(cl.Locale.LOCALE_DOMAIN) self.values = OrderedDict([(_('Sort by album name'), 'name_artist'), (_('Sort by year'), 'year_artist'), (_('Sort by rating'), 'rating_artist')]) self.options = list(self.values.keys()) # get the current sort key and initialise the superclass gs = GSetting() source_settings = gs.get_setting(gs.Path.PLUGIN) value = source_settings[gs.PluginKey.SORT_BY_ARTIST] if value not in list(self.values.values()): print("here") value = 'name_artist' source_settings[gs.PluginKey.SORT_BY_ARTIST] = value self._spritesheet = None self.update_images(False) self.current_key = list(self.values.keys())[ list(self.values.values()).index(value)] print(self.current_key)
def __init__(self, *args, **kwargs): ''' Initializes the button. ''' Gtk.RadioButton.__init__(self, *args, **kwargs) OptionsWidget.__init__(self, *args, **kwargs) gs = GSetting() setting = gs.get_setting(gs.Path.PLUGIN) setting.bind(gs.PluginKey.BUTTON_RELIEF, self, 'button_relief', Gio.SettingsBindFlags.GET) self.connect('notify::button-relief', self.on_notify_button_relief) # initialise some variables self.image_display = False self.initialised = False # ensure button appearance rather than standard radio toggle self.set_mode(False) #label colours self._not_active_colour = None self._active_colour = None
def __init__(self, plugin, album_model): super(SortPopupController, self).__init__() self._album_model = album_model self.plugin = plugin # sorts dictionary cl = CoverLocale() cl.switch_locale(cl.Locale.LOCALE_DOMAIN) self.values = OrderedDict([(_('Sort by album name'), 'name'), (_('Sort by album artist'), 'artist'), (_('Sort by year'), 'year'), (_('Sort by rating'), 'rating')]) self.options = self.values.keys() # get the current sort key and initialise the superclass gs = GSetting() source_settings = gs.get_setting(gs.Path.PLUGIN) value = source_settings[gs.PluginKey.SORT_BY] self._spritesheet = None self.update_images(False) self.current_key = self.values.keys()[ self.values.values().index(value)]
def _connect_properties(self): gs = GSetting() setting = gs.get_setting(gs.Path.PLUGIN) setting.bind( gs.PluginKey.USE_FAVOURITES, self, 'favourites', Gio.SettingsBindFlags.DEFAULT)
def do_action(self): sort = self.values[self.current_key] gs = GSetting() settings = gs.get_setting(gs.Path.PLUGIN) settings[gs.PluginKey.SORT_BY] = sort self._album_model.sort(sort)
def __init__(self, *args, **kwargs): super(EnhancedButton, self).__init__(*args, **kwargs) gs = GSetting() setting = gs.get_setting(gs.Path.PLUGIN) setting.bind(gs.PluginKey.BUTTON_RELIEF, self, "button_relief", Gio.SettingsBindFlags.GET) self.connect("notify::button-relief", self.on_notify_button_relief)
def do_action(self): sort = self.values[self.current_key] gs = GSetting() settings = gs.get_setting(gs.Path.PLUGIN) settings[gs.PluginKey.SORT_BY_ARTIST] = sort self._viewmgr.current_view.get_default_manager().emit('sort', "artist")
def __init__(self, *args, **kwargs): super(EnhancedButton, self).__init__(*args, **kwargs) gs = GSetting() setting = gs.get_setting(gs.Path.PLUGIN) setting.bind(gs.PluginKey.BUTTON_RELIEF, self, 'button_relief', Gio.SettingsBindFlags.GET) self.connect('notify::button-relief', self.on_notify_button_relief)
def play_random_album_menu_item_callback(self, favourites=False): print('''CoverArtBrowser DEBUG - play_random_album_menu_item_callback''') query_model = RB.RhythmDBQueryModel.new_empty(self.shell.props.db) num_albums = len(self.album_manager.model.store) #random_list = [] selected_albums = [] gs = GSetting() settings = gs.get_setting(gs.Path.PLUGIN) to_queue = settings[gs.PluginKey.RANDOM] if num_albums <= to_queue: dialog = Gtk.MessageDialog(None, Gtk.DialogFlags.MODAL, Gtk.MessageType.INFO, Gtk.ButtonsType.OK, _("The number of albums to randomly play is less than that displayed.")) dialog.run() dialog.destroy() return album_col = self.album_manager.model.columns['album'] chosen = {} # now loop through finding unique random albums # i.e. ensure we dont queue the same album twice for loop in range(0, to_queue): while True: pos = random.randint(0, num_albums - 1) if pos not in chosen: chosen[pos] = None selected_albums.append(self.album_manager.model.store[pos][album_col]) break threshold = self.rating_threshold if favourites else 0 for album in selected_albums: # Retrieve and sort the entries of the album tracks = album.get_tracks(threshold) # Add the songs to the play queue for track in tracks: query_model.add_entry(track.entry, -1) self.props.query_model = query_model # Start the music player = self.shell.props.shell_player player.play_entry(query_model[0][0], self) print("CoverArtBrowser DEBUG - end play_selected_album")
def sort(self): albums = SortedCollection(key=lambda album: getattr(album, 'name')) gs = GSetting() source_settings = gs.get_setting(gs.Path.PLUGIN) key = source_settings[gs.PluginKey.SORT_BY_ARTIST] order = source_settings[gs.PluginKey.SORT_ORDER_ARTIST] sort_keys = { 'name_artist': ('album_sort', 'album_sort'), 'year_artist': ('real_year', 'calc_year_sort'), 'rating_artist': ('rating', 'album_sort') } props = sort_keys[key] def key_function(album): keys = [getattr(album, prop) for prop in props] return keys # remember the current sort then remove the sort order # because sorting will only work in unsorted lists sortSettings = self.store.get_sort_column_id() self.store.set_sort_column_id(-1, Gtk.SortType.ASCENDING) for artist in self._iters: albums.clear() albums.key = key_function if 'album' in self._iters[artist] and len(self._iters[artist]['album']) > 1: # we only need to sort an artists albums if there is more than one album # sort all the artists albums for album_iter in self._iters[artist]['album']: albums.insert(self._tree_store[album_iter][self.columns['artist_album']]) if not order: albums = reversed(albums) # now we iterate through the sorted artist albums. Look and swap iters # according to where they are in the tree store artist_iter = self._iters[artist]['iter'] next_iter = self._tree_store.iter_children(artist_iter) for album in albums: if self._tree_store[next_iter][self.columns['artist_album']] != album: self._tree_store.swap(next_iter, self._albumiters[album]['iter']) next_iter = self._albumiters[album]['iter'] next_iter = self._tree_store.iter_next(next_iter) # now we have finished sorting, reapply the sort if sortSettings[0]: self.store.set_sort_column_id(*sortSettings)
def load_complete(self, *args, **kwargs): ''' Called by Rhythmbox when it has completed loading all data Used to automatically switch to the browser if the user has set in the preferences ''' gs = GSetting() setting = gs.get_setting(gs.Path.PLUGIN) if setting[gs.PluginKey.AUTOSTART]: GLib.idle_add(self.shell.props.display_page_tree.select, self.source)
def load_complete(self, *args, **kwargs): ''' Called by Rhythmbox when it has completed loading all data Used to automatically switch to the browser if the user has set in the preferences ''' gs = GSetting() setting = gs.get_setting(gs.Path.PLUGIN) if setting[gs.PluginKey.AUTOSTART]: self._externalmenu.autostart_source()
def _connect_properties(self): gs = GSetting() setting = gs.get_setting(gs.Path.PLUGIN) setting.bind(gs.PluginKey.COVER_SIZE, self, 'cover-size', Gio.SettingsBindFlags.GET) setting.bind(gs.PluginKey.DISPLAY_TEXT_POS, self, 'display-text-pos', Gio.SettingsBindFlags.GET) setting.bind(gs.PluginKey.DISPLAY_TEXT, self, 'display-text', Gio.SettingsBindFlags.GET)
def view_change_cb(self, action, current): ''' Called when the view state on a page is changed. Sets the new state. ''' action.set_state(current) view_name = self._views.get_view_name_for_action(current) if view_name != ListView.name: gs = GSetting() setting = gs.get_setting(gs.Path.PLUGIN) setting[gs.PluginKey.VIEW_NAME] = view_name GLib.idle_add(self.shell.props.display_page_tree.select, self.source)
def _connect_properties(self): gs = GSetting() setting = gs.get_setting(gs.Path.PLUGIN) setting.bind( gs.PluginKey.USE_FAVOURITES, self, 'favourites', Gio.SettingsBindFlags.DEFAULT) setting.bind( gs.PluginKey.FOLLOWING, self, 'follow', Gio.SettingsBindFlags.DEFAULT)
class _impl(GObject.Object): """ Implementation of the singleton interface """ # properties theme = GObject.property(type=str, default="standard") # signals ''' changed = signal emitted when a theme has changed ''' __gsignals__ = { 'theme_changed': (GObject.SIGNAL_RUN_LAST, None, ()) } # below public variables and methods that can be called for Theme def __init__(self, plugin): ''' Initializes the singleton interface, assigning all the constants used to access the plugin's settings. ''' super(Theme._impl, self).__init__() self.plugin = plugin popups = rb.find_plugin_file(plugin, 'img/popups.xml') root = ET.parse(open(popups)).getroot() base = 'theme/theme' self.themes = [] for elem in root.xpath(base): self.themes.append(elem.attrib['folder_name']) self.gs = GSetting() self.setting = self.gs.get_setting(self.gs.Path.PLUGIN) # connect properties and signals self._connect_properties() self._connect_signals() @property def current(self): return self.setting[self.gs.PluginKey.THEME] def _connect_properties(self): self.setting.bind(self.gs.PluginKey.THEME, self, 'theme', Gio.SettingsBindFlags.GET) def _connect_signals(self): self.connect('notify::theme', self._on_theme_changed, None) def _on_theme_changed(self, *args): self.emit('theme_changed')
class SortOrderToggleController(OptionsController): toolbar_type = "album" def __init__(self, plugin, viewmgr): super(SortOrderToggleController, self).__init__() self._viewmgr = viewmgr self.plugin = plugin # options self.values = OrderedDict([(_('Sort in descending order'), False), (_('Sort in ascending order'), True)]) self.options = list(self.values.keys()) self._images = [] # set the current key self.gs = GSetting() self.settings = self.gs.get_setting(self.gs.Path.PLUGIN) self.key = self.get_key() sort_order = self.settings[self.key] self.current_key = list(self.values.keys())[list( self.values.values()).index(sort_order)] self.update_images(False) def get_key(self): return self.gs.PluginKey.SORT_ORDER def update_images(self, *args): # initialize images if len(self._images) > 0: del self._images[:] self._images.append( self.create_button_image(self.plugin, None, 'arrow_down.png')) self._images.append( self.create_button_image(self.plugin, None, 'arrow_up.png')) if args[-1]: self.update_image = True def do_action(self): sort_order = self.values[self.current_key] self.settings[self.key] = sort_order self._viewmgr.current_view.get_default_manager().emit( 'sort', self.toolbar_type) def get_current_image(self): return self._images[self.get_current_key_index()]
class _impl(GObject.Object): """ Implementation of the singleton interface """ # properties theme = GObject.property(type=str, default="standard") # signals ''' changed = signal emitted when a theme has changed ''' __gsignals__ = {'theme_changed': (GObject.SIGNAL_RUN_LAST, None, ())} # below public variables and methods that can be called for Theme def __init__(self, plugin): ''' Initializes the singleton interface, assigning all the constants used to access the plugin's settings. ''' super(Theme._impl, self).__init__() self.plugin = plugin popups = rb.find_plugin_file(plugin, 'img/popups.xml') root = ET.parse(open(popups)).getroot() base = 'theme/theme' self.themes = [] for elem in root.xpath(base): self.themes.append(elem.attrib['folder_name']) self.gs = GSetting() self.setting = self.gs.get_setting(self.gs.Path.PLUGIN) # connect properties and signals self._connect_properties() self._connect_signals() @property def current(self): return self.setting[self.gs.PluginKey.THEME] def _connect_properties(self): self.setting.bind(self.gs.PluginKey.THEME, self, 'theme', Gio.SettingsBindFlags.GET) def _connect_signals(self): self.connect('notify::theme', self._on_theme_changed, None) def _on_theme_changed(self, *args): self.emit('theme_changed')
class SortOrderToggleController(OptionsController): toolbar_type = "album" def __init__(self, plugin, viewmgr): super(SortOrderToggleController, self).__init__() self._viewmgr = viewmgr self.plugin = plugin # options self.values = OrderedDict([(_('Sort in descending order'), False), (_('Sort in ascending order'), True)]) self.options = list(self.values.keys()) self._images = [] # set the current key self.gs = GSetting() self.settings = self.gs.get_setting(self.gs.Path.PLUGIN) self.key = self.get_key() sort_order = self.settings[self.key] self.current_key = list(self.values.keys())[ list(self.values.values()).index(sort_order)] self.update_images(False) def get_key(self): return self.gs.PluginKey.SORT_ORDER def update_images(self, *args): # initialize images if len(self._images) > 0: del self._images[:] self._images.append(self.create_button_image( self.plugin, None, 'arrow_down.png')) self._images.append(self.create_button_image( self.plugin, None, 'arrow_up.png')) if args[-1]: self.update_image = True def do_action(self): sort_order = self.values[self.current_key] self.settings[self.key] = sort_order self._viewmgr.current_view.get_default_manager().emit('sort', self.toolbar_type) def get_current_image(self): return self._images[self.get_current_key_index()]
def _sort_changed(self, menu, sort): ''' called when sort popup menu item chosen ''' if not menu or menu.get_active(): self.set_popup_value(self.sorts[sort]) #self.sort_by = sort print sort gs = GSetting() settings = gs.get_setting(gs.Path.PLUGIN) settings[gs.PluginKey.SORT_BY] = sort self.resize_button_image(self._spritesheet[sort]) self.callback(sort)
def album_art_requested(self, store, key, last_time): searches = [] gs = GSetting() setting = gs.get_setting(gs.Path.PLUGIN) if setting[gs.PluginKey.EMBEDDED_SEARCH]: searches.append(CoverAlbumSearch()) if setting[gs.PluginKey.DISCOGS_SEARCH]: searches.append(DiscogsSearch()) print "about to search" s = CoverSearch(store, key, last_time, searches) print "finished about to return" return s.next_search()
def on_notify_view_name(self, *args): if self._lastview and self.view_name != self._lastview: selected = self._views[self._lastview].get_selected_objects() current_album = None if len(selected) > 0: current_album = self._views[ self._lastview].get_selected_objects()[0] if self._views[self.view_name].use_plugin_window: child = self.window.get_child() if child: self.window.remove(child) self.window.add(self._views[self.view_name].view) self.window.show_all() self.click_count = 0 self._views[ self. _lastview].panedposition = self.source.paned.get_expansion_status( ) self._views[self.view_name].switch_to_view(self.source, current_album) self._views[self.view_name].emit('update-toolbar') self._views[self.view_name].get_default_manager().emit( 'sort', None) if self._views[self.view_name].use_plugin_window: self.source.paned.expand( self._views[self.view_name].panedposition) self.current_view.set_popup_menu(self.source.popup_menu) self.source.album_manager.current_view = self.current_view if self._views[self.view_name].use_plugin_window: # we only ever save plugin views not external views saved_view = self.view_name else: saved_view = self._lastview self._lastview = self.view_name gs = GSetting() setting = gs.get_setting(gs.Path.PLUGIN) setting[gs.PluginKey.VIEW_NAME] = saved_view self.emit('new-view', self.view_name)
class SortOrderToggleController(OptionsController): def __init__(self, plugin, album_model): super(SortOrderToggleController, self).__init__() self._album_model = album_model self.plugin = plugin # options self.values = OrderedDict([(_('Sort in descending order'), False), (_('Sort in ascending order'), True)]) self.options = self.values.keys() self._images = [] # set the current key self.gs = GSetting() self.settings = self.gs.get_setting(self.gs.Path.PLUGIN) sort_order = self.settings[self.gs.PluginKey.SORT_ORDER] self.current_key = self.values.keys()[ self.values.values().index(sort_order)] self.update_images(False) def update_images(self, *args): # initialize images if len(self._images) > 0: del self._images[:] self._images.append(self.create_button_image( self.plugin, None, 'arrow_down.png')) self._images.append(self.create_button_image( self.plugin, None, 'arrow_up.png')) if args[-1]: self.update_image = True def do_action(self): sort_order = self.values[self.current_key] if not sort_order or\ sort_order != self.settings[self.gs.PluginKey.SORT_ORDER]: self._album_model.sort(reverse=True) self.settings[self.gs.PluginKey.SORT_ORDER] = sort_order def get_current_image(self): return self._images[self.get_current_key_index()]
def _connect_properties(self): gs = GSetting() settings = gs.get_setting(gs.Path.PLUGIN) settings.bind(gs.PluginKey.FLOW_APPEARANCE, self, 'flow_appearance', Gio.SettingsBindFlags.GET) settings.bind(gs.PluginKey.FLOW_HIDE_CAPTION, self, 'flow_hide', Gio.SettingsBindFlags.GET) settings.bind(gs.PluginKey.FLOW_SCALE, self, 'flow_scale', Gio.SettingsBindFlags.GET) settings.bind(gs.PluginKey.FLOW_AUTOMATIC, self, 'flow_automatic', Gio.SettingsBindFlags.GET) settings.bind(gs.PluginKey.FLOW_BACKGROUND_COLOUR, self, 'flow_background', Gio.SettingsBindFlags.GET) settings.bind(gs.PluginKey.FLOW_WIDTH, self, 'flow_width', Gio.SettingsBindFlags.GET) settings.bind(gs.PluginKey.FLOW_MAX, self, 'flow_max', Gio.SettingsBindFlags.GET)
class SortOrderToggleController(OptionsController): def __init__(self, plugin, album_model): super(SortOrderToggleController, self).__init__() self._album_model = album_model self.plugin = plugin # options self.values = OrderedDict([(_('Sort in descending order'), False), (_('Sort in ascending order'), True)]) self.options = self.values.keys() self._images = [] # set the current key self.gs = GSetting() self.settings = self.gs.get_setting(self.gs.Path.PLUGIN) sort_order = self.settings[self.gs.PluginKey.SORT_ORDER] self.current_key = self.values.keys()[self.values.values().index( sort_order)] self.update_images(False) def update_images(self, *args): # initialize images if len(self._images) > 0: del self._images[:] self._images.append( self.create_button_image(self.plugin, None, 'arrow_down.png')) self._images.append( self.create_button_image(self.plugin, None, 'arrow_up.png')) if args[-1]: self.update_image = True def do_action(self): sort_order = self.values[self.current_key] if not sort_order or\ sort_order != self.settings[self.gs.PluginKey.SORT_ORDER]: self._album_model.sort(reverse=True) self.settings[self.gs.PluginKey.SORT_ORDER] = sort_order def get_current_image(self): return self._images[self.get_current_key_index()]
def __init__(self, plugin): super(ExternalPluginMenu, self).__init__() self.plugin = plugin self.shell = plugin.shell self.source = plugin.source self.app_id = None self.locations = [ 'library-toolbar', 'queue-toolbar', 'playsource-toolbar' ] from coverart_browser_source import Views self._views = Views(self.shell) self._use_standard_control = True self.plugin.using_alternative_toolbar = hasattr( self.shell, 'alternative_toolbar') if self.plugin.using_alternative_toolbar: from alttoolbar_type import AltToolbarHeaderBar self.plugin.using_headerbar = isinstance( self.shell.alternative_toolbar.toolbar_type, AltToolbarHeaderBar) if self.plugin.using_headerbar: self._use_standard_control = False # register with headerbar to complete the setup for coverart-browser print("registering") self.shell.alternative_toolbar.toolbar_type.setup_completed_async( self._headerbar_toolbar_completed) if self._use_standard_control: # ... otherwise just use the standard menubutton approach self.source.props.visibility = True # make the source visible gs = GSetting() setting = gs.get_setting(gs.Path.PLUGIN) setting.bind(gs.PluginKey.TOOLBAR_POS, self, 'toolbar_pos', Gio.SettingsBindFlags.GET) self.connect('notify::toolbar-pos', self._on_notify_toolbar_pos) self.shell.props.display_page_tree.connect("selected", self.on_page_change) self._create_menu()
def _connect_properties(self): gs = GSetting() setting = gs.get_setting(gs.Path.PLUGIN) setting.bind(gs.PluginKey.COVER_SIZE, self, 'cover-size', Gio.SettingsBindFlags.GET) setting.bind(gs.PluginKey.DISPLAY_TEXT_POS, self, 'display-text-pos', Gio.SettingsBindFlags.GET) setting.bind(gs.PluginKey.DISPLAY_TEXT, self, 'display-text', Gio.SettingsBindFlags.GET) setting.bind(gs.PluginKey.ADD_SHADOW, self, 'add-shadow', Gio.SettingsBindFlags.GET) setting.bind(gs.PluginKey.TEXT_ALIGNMENT, self, 'text-alignment', Gio.SettingsBindFlags.GET)
def on_notify_view_name(self, *args): if self._lastview and self.view_name != self._lastview: selected = self._views[self._lastview].get_selected_objects() current_album = None if len(selected) > 0: current_album = self._views[self._lastview].get_selected_objects()[0] if self._views[self.view_name].use_plugin_window: child = self.window.get_child() if child: self.window.remove(child) self.window.add(self._views[self.view_name].view) self.window.show_all() self.click_count = 0 self._views[self._lastview].panedposition = self.source.paned.get_expansion_status() self._views[self.view_name].switch_to_view(self.source, current_album) self._views[self.view_name].emit('update-toolbar') self._views[self.view_name].get_default_manager().emit('sort', None) if self._views[self.view_name].use_plugin_window: self.source.paned.expand(self._views[self.view_name].panedposition) self.current_view.set_popup_menu(self.source.popup_menu) self.source.album_manager.current_view = self.current_view if self._views[self.view_name].use_plugin_window: # we only ever save plugin views not external views saved_view = self.view_name else: saved_view = self._lastview self._lastview = self.view_name gs = GSetting() setting = gs.get_setting(gs.Path.PLUGIN) setting[gs.PluginKey.VIEW_NAME] = saved_view self.emit('new-view', self.view_name)
def __init__(self, plugin): super(ExternalPluginMenu, self).__init__() self.plugin = plugin self.shell = plugin.shell self.source = plugin.source self.app_id = None self.locations = ['library-toolbar', 'queue-toolbar', 'playsource-toolbar'] from coverart_browser_source import Views self._views = Views(self.shell) self._use_standard_control = True self.plugin.using_alternative_toolbar = hasattr(self.shell, 'alternative_toolbar') if self.plugin.using_alternative_toolbar: from alttoolbar_type import AltToolbarHeaderBar self.plugin.using_headerbar = isinstance(self.shell.alternative_toolbar.toolbar_type, AltToolbarHeaderBar) if self.plugin.using_headerbar: self._use_standard_control = False # register with headerbar to complete the setup for coverart-browser print ("registering") self.shell.alternative_toolbar.toolbar_type.setup_completed_async(self._headerbar_toolbar_completed) if self._use_standard_control: # ... otherwise just use the standard menubutton approach self.source.props.visibility = True # make the source visible gs = GSetting() setting = gs.get_setting(gs.Path.PLUGIN) setting.bind(gs.PluginKey.TOOLBAR_POS, self, 'toolbar_pos', Gio.SettingsBindFlags.GET) self.connect('notify::toolbar-pos', self._on_notify_toolbar_pos) self.shell.props.display_page_tree.connect( "selected", self.on_page_change ) self._create_menu()
def _select_view(self, view_name): ''' with the view_name decide which view to be displayed or if view_name is None then use the last remembered view_name return view_name ''' if not self.shell.props.display_page_tree: return print("_select_view") print(view_name) if view_name != ListView.name and \ view_name != QueueView.name and \ view_name != PlaySourceView.name: gs = GSetting() setting = gs.get_setting(gs.Path.PLUGIN) if view_name: setting[gs.PluginKey.VIEW_NAME] = view_name else: view_name = setting[gs.PluginKey.VIEW_NAME] player = self.shell.props.shell_player player.set_selected_source(self.source) #.playlist_source) GLib.idle_add(self.shell.props.display_page_tree.select, self.source) elif view_name == ListView.name: GLib.idle_add(self.shell.props.display_page_tree.select, self.shell.props.library_source) elif view_name == QueueView.name: GLib.idle_add(self.shell.props.display_page_tree.select, self.shell.props.queue_source) elif view_name == PlaySourceView.name: GLib.idle_add(self.shell.props.display_page_tree.select, self.plugin.playlist_source) return view_name
def _select_view(self, view_name): ''' with the view_name decide which view to be displayed or if view_name is None then use the last remembered view_name return view_name ''' if not self.shell.props.display_page_tree: return print ("_select_view") print (view_name) if view_name != ListView.name and \ view_name != QueueView.name and \ view_name != PlaySourceView.name: gs = GSetting() setting = gs.get_setting(gs.Path.PLUGIN) if view_name: setting[gs.PluginKey.VIEW_NAME] = view_name else: view_name = setting[gs.PluginKey.VIEW_NAME] player = self.shell.props.shell_player player.set_selected_source(self.source) #.playlist_source) GLib.idle_add(self.shell.props.display_page_tree.select, self.source) elif view_name == ListView.name: GLib.idle_add(self.shell.props.display_page_tree.select, self.shell.props.library_source) elif view_name == QueueView.name: GLib.idle_add(self.shell.props.display_page_tree.select, self.shell.props.queue_source) elif view_name == PlaySourceView.name: GLib.idle_add(self.shell.props.display_page_tree.select, self.plugin.playlist_source) return view_name
def _connect_properties(self): gs = GSetting() setting = gs.get_setting(gs.Path.PLUGIN) setting.bind(gs.PluginKey.TOOLBAR_POS, self, 'toolbar_pos', Gio.SettingsBindFlags.GET)
class EntryViewPane(object): ''' encapulates all of the Track Pane objects ''' def __init__(self, shell, plugin, source, entry_view_grid, viewmgr): self.gs = GSetting() self.entry_view_grid = entry_view_grid self.shell = shell self.viewmgr = viewmgr self.plugin = plugin self.source = source # setup entry-view objects and widgets self.stack = Gtk.Stack() self.stack.set_transition_type( Gtk.StackTransitionType.SLIDE_LEFT_RIGHT) self.stack.set_transition_duration(750) # create entry views. Don't allow to reorder until the load is finished self.entry_view_compact = CoverArtCompactEntryView( self.shell, self.source) self.entry_view_full = CoverArtEntryView(self.shell, self.source) self.entry_view = self.entry_view_compact self.shell.props.library_source.get_entry_view().set_columns_clickable( False) self.entry_view_results = ResultsGrid() self.entry_view_results.initialise(self.entry_view_grid, source) self.stack.add_titled(self.entry_view_results, "notebook_tracks", _("Tracks")) self.entry_view_grid.attach(self.stack, 0, 0, 3, 1) def setup_source(self): colour = self.viewmgr.get_selection_colour() self.cover_search_pane = CoverSearchPane(self.plugin, colour) self.stack.add_titled(self.cover_search_pane, "notebook_covers", _("Covers")) # define entry-view toolbar self.stars = ReactiveStar() self.stars.set_rating(0) self.stars.connect('changed', self.rating_changed_callback) self.stars.props.valign = Gtk.Align.CENTER self.entry_view_grid.attach(self.stars, 1, 1, 1, 1) stack_switcher = Gtk.StackSwitcher() stack_switcher.set_stack(self.stack) self.entry_view_grid.attach(stack_switcher, 0, 1, 1, 1) viewtoggle = PixbufButton() viewtoggle.set_image(create_button_image(self.plugin, "entryview.png")) self.viewtoggle_id = None setting = self.gs.get_setting(self.gs.Path.PLUGIN) viewtoggle.set_active(not setting[self.gs.PluginKey.ENTRY_VIEW_MODE]) self.entry_view_toggled(viewtoggle, True) viewtoggle.connect('toggled', self.entry_view_toggled) smallwindowbutton = PixbufButton() smallwindowbutton.set_image( create_button_image(self.plugin, "view-restore.png")) smallwindowbutton.connect('toggled', self.smallwindowbutton_callback) self.smallwindowext = ExternalPlugin() self.smallwindowext.appendattribute('plugin_name', 'smallwindow') self.smallwindowext.appendattribute('action_group_name', 'small window actions') self.smallwindowext.appendattribute('action_name', 'SmallWindow') self.smallwindowext.appendattribute('action_type', 'app') whatsplayingtoggle = PixbufButton() whatsplayingtoggle.set_image( create_button_image(self.plugin, "whatsplaying.png")) whatsplayingtoggle.connect('toggled', self.whatsplayingtoggle_callback) rightgrid = Gtk.Grid() rightgrid.props.halign = Gtk.Align.END # rightgrid.attach(whatsplayingtoggle, 0, 0, 1, 1) rightgrid.attach(viewtoggle, 1, 0, 1, 1) rightgrid.attach(smallwindowbutton, 2, 0, 1, 1) self.entry_view_grid.attach_next_to(rightgrid, self.stars, Gtk.PositionType.RIGHT, 1, 1) self.stack.set_visible_child(self.entry_view_results) self.stack.connect('notify::visible-child-name', self.notebook_switch_page_callback) self.entry_view_grid.show_all() smallwindowbutton.set_visible(self.smallwindowext.is_activated()) def whatsplayingtoggle_callback(self, widget): self.entry_view_results.emit('whats-playing', widget.get_active()) def smallwindowbutton_callback(self, widget): if widget.get_active(): self.smallwindowext.activate(self.shell) widget.emit('clicked') def entry_view_toggled(self, widget, initialised=False): print("DEBUG - entry_view_toggled") if widget.get_active(): next_view = self.entry_view_full show_coverart = False if self.viewtoggle_id: self.shell.props.window.disconnect(self.viewtoggle_id) self.viewtoggle_id = None else: next_view = self.entry_view_compact show_coverart = True self.viewtoggle_id = self.shell.props.window.connect( 'check_resize', self.entry_view_results.window_resize) setting = self.gs.get_setting(self.gs.Path.PLUGIN) setting[self.gs.PluginKey.ENTRY_VIEW_MODE] = not widget.get_active() self.entry_view_results.change_view(next_view, show_coverart) self.entry_view = next_view if not initialised: self.source.update_with_selection() def notebook_switch_page_callback(self, *args): ''' Callback called when the notebook page gets switched. It initiates the cover search when the cover search pane's page is selected. ''' print("CoverArtBrowser DEBUG - notebook_switch_page_callback") if self.stack.get_visible_child_name() == 'notebook_covers': self.viewmgr.current_view.switch_to_coverpane( self.cover_search_pane) else: entries = self.entry_view.get_selected_entries() if entries and len(entries) > 0: self.entry_view_results.emit('update-cover', self.source, entries[0]) else: selected = self.viewmgr.current_view.get_selected_objects() tracks = selected[0].get_tracks() self.entry_view_results.emit('update-cover', self.source, tracks[0].entry) print("CoverArtBrowser DEBUG - end notebook_switch_page_callback") def rating_changed_callback(self, widget): ''' Callback called when the Rating stars is changed ''' print("CoverArtBrowser DEBUG - rating_changed_callback") rating = widget.get_rating() for album in self.viewmgr.current_view.get_selected_objects(): album.rating = rating print("CoverArtBrowser DEBUG - end rating_changed_callback") def get_entry_view(self): return self.entry_view def update_cover(self, album_artist, manager): if not self.stack.get_visible_child_name() == "notebook_covers": return self.cover_search_pane.clear() self.cover_search(album_artist, manager) def cover_search(self, album_artist, manager): self.cover_search_pane.do_search(album_artist, manager.cover_man.update_cover) def update_selection(self, last_selected_album, click_count): ''' Update the source view when an item gets selected. ''' print("DEBUG - update_with_selection") selected = self.viewmgr.current_view.get_selected_objects() # clear the entry view self.entry_view.clear() cover_search_pane_visible = self.stack.get_visible_child_name( ) == "notebook_covers" if not selected: # clean cover tab if selected if cover_search_pane_visible: self.cover_search_pane.clear() self.entry_view_results.emit('update-cover', self.source, None) return last_selected_album, click_count elif len(selected) == 1: self.stars.set_rating(selected[0].rating) if selected[0] is not last_selected_album: # when the selection changes we've to take into account two # things if not click_count: # we may be using the arrows, so if there is no mouse # involved, we should change the last selected last_selected_album = selected[0] else: # we may've doing a fast change after a valid second click, # so it shouldn't be considered a double click click_count -= 1 else: self.stars.set_rating(0) if len(selected) == 1: self.source.artist_info.emit('selected', selected[0].artist, selected[0].name) self.entry_view.set_sorting_order('track-number', Gtk.SortType.ASCENDING) for album in selected: # add the album to the entry_view self.entry_view.add_album(album) if len(selected) > 0: def cover_update(*args): print("emitting") self.entry_view_results.emit('update-cover', self.source, selected[0].get_tracks()[0].entry) # add a short delay to give the entry-pane time to expand etc. Gdk.threads_add_timeout(GLib.PRIORITY_DEFAULT_IDLE, 250, cover_update, None) # update the cover search pane with the first selected album if cover_search_pane_visible: self.cover_search_pane.do_search( selected[0], self.source.album_manager.cover_man.update_cover) return last_selected_album, click_count
class ArtistInfoPane(GObject.GObject): __gsignals__ = { 'selected': (GObject.SIGNAL_RUN_LAST, None, (GObject.TYPE_STRING, GObject.TYPE_STRING)) } artist_info_paned_pos = GObject.property(type=str) min_paned_pos = 100 def __init__(self, button_box, scroll_window, info_paned, source): GObject.GObject.__init__(self) self.tab = {} self.ds = {} self.view = {} self.buttons = button_box self.source = source self.plugin = source.plugin self.shell = source.shell self.info_scrolled_window = scroll_window self.info_paned = info_paned self.current_artist = None self.current_album_title = None self.webview = WebKit.WebView() self.webview.connect("navigation-requested", self.navigation_request_cb) self.webview.connect("notify::title", self.view_title_change) self.info_scrolled_window.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) self.info_scrolled_window.add(self.webview) self.info_scrolled_window.show_all() # cache for artist/album information: valid for a month, can be used indefinitely # if offline, discarded if unused for six months self.info_cache = rb.URLCache(name='info', path=os.path.join( 'coverart_browser', 'info'), refresh=30, discard=180) # cache for rankings (artist top tracks and top albums): valid for a week, # can be used for a month if offline self.ranking_cache = rb.URLCache(name='ranking', path=os.path.join( 'coverart_browser', 'ranking'), refresh=7, lifetime=30) self.info_cache.clean() self.ranking_cache.clean() self.ds['link'] = LinksDataSource() self.ds['artist'] = ArtistDataSource(self.info_cache, self.ranking_cache) self.view['artist'] = ArtistInfoView(self.shell, self.plugin, self.webview, self.ds['artist'], self.ds['link']) self.tab['artist'] = ArtistInfoTab(self.plugin, self.shell, self.buttons, self.ds['artist'], self.view['artist']) self.ds['album'] = AlbumDataSource(self.info_cache, self.ranking_cache) self.view['album'] = AlbumInfoView(self.shell, self.plugin, self.webview, self.ds['album']) self.tab['album'] = AlbumInfoTab(self.plugin, self.shell, self.buttons, self.ds['album'], self.view['album']) self.ds['echoartist'] = EchoArtistDataSource(self.info_cache, self.ranking_cache) self.view['echoartist'] = EchoArtistInfoView(self.shell, self.plugin, self.webview, self.ds['echoartist'], self.ds['link']) self.tab['echoartist'] = EchoArtistInfoTab(self.plugin, self.shell, self.buttons, self.ds['echoartist'], self.view['echoartist']) self.gs = GSetting() self.connect_properties() self.connect_signals() Gdk.threads_add_timeout(GLib.PRIORITY_DEFAULT_IDLE, 50, self._change_paned_pos, self.source.viewmgr.view_name) self.current = 'artist' self.tab[self.current].activate() def connect_properties(self): ''' Connects the source properties to the saved preferences. ''' setting = self.gs.get_setting(self.gs.Path.PLUGIN) setting.bind(self.gs.PluginKey.ARTIST_INFO_PANED_POSITION, self, 'artist-info-paned-pos', Gio.SettingsBindFlags.DEFAULT) def connect_signals(self): self.tab_cb_ids = [] # Listen for switch-tab signal from each tab for key, value in self.tab.items(): self.tab_cb_ids.append( (key, self.tab[key].connect('switch-tab', self.change_tab))) # Listen for selected signal from the views self.connect('selected', self.select_artist) # lets remember info paned click self.info_paned.connect('button-release-event', self.artist_info_paned_button_release_callback) # lets also listen for changes to the view to set the paned position self.source.viewmgr.connect('new-view', self.on_view_changed) def view_title_change(self, webview, param): title = webview.get_title() if title: args = json.loads(title) artist = args['artist'] if args['toggle']: self.source.album_manager.model.replace_filter( 'similar_artist', artist) else: self.source.album_manager.model.remove_filter('similar_artist') else: self.source.album_manager.model.remove_filter('similar_artist') def on_view_changed(self, widget, view_name): self._change_paned_pos(view_name) def _change_paned_pos(self, view_name): paned_positions = eval(self.artist_info_paned_pos) found = None for viewpos in paned_positions: if view_name in viewpos: found = viewpos break if not found: return child_width = int(found.split(":")[1]) calc_pos = self.source.page.get_allocated_width() - child_width self.info_paned.set_position(calc_pos) self.info_paned.set_visible(True) def _get_child_width(self): child = self.info_paned.get_child2() return child.get_allocated_width() def artist_info_paned_button_release_callback(self, *args): ''' Callback when the artist paned handle is released from its mouse click. ''' child_width = self._get_child_width() paned_positions = eval(self.artist_info_paned_pos) found = None for viewpos in paned_positions: if self.source.viewmgr.view_name in viewpos: found = viewpos break if not found: return paned_positions.remove(found) if child_width <= self.min_paned_pos: child_width = 0 self.info_paned.set_position( self.source.page.get_allocated_width()) paned_positions.append(self.source.viewmgr.view_name + ":" + str(child_width)) self.artist_info_paned_pos = repr(paned_positions) def select_artist(self, widget, artist, album_title): if self._get_child_width() > self.min_paned_pos: self.tab[self.current].reload(artist, album_title) else: self.tab[self.current].view.blank_view() self.current_album_title = album_title self.current_artist = artist def change_tab(self, tab, newtab): print("swapping tab from %s to %s" % (self.current, newtab)) if (self.current != newtab): self.tab[self.current].deactivate() if self._get_child_width() > self.min_paned_pos: self.tab[newtab].activate(self.current_artist, self.current_album_title) else: self.tab[newtab].view.blank_view() self.current = newtab def navigation_request_cb(self, view, frame, request): # open HTTP URIs externally. this isn't a web browser. if request.get_uri().startswith('http'): print("opening uri %s" % request.get_uri()) Gtk.show_uri(self.shell.props.window.get_screen(), request.get_uri(), Gdk.CURRENT_TIME) return 1 # WEBKIT_NAVIGATION_RESPONSE_IGNORE else: return 0 # WEBKIT_NAVIGATION_RESPONSE_ACCEPT
def play_random_album_menu_item_callback(self, favourites=False): print( '''CoverArtBrowser DEBUG - play_random_album_menu_item_callback''') query_model = RB.RhythmDBQueryModel.new_empty(self.shell.props.db) num_albums = len(self.album_manager.model.store) #random_list = [] selected_albums = [] gs = GSetting() settings = gs.get_setting(gs.Path.PLUGIN) to_queue = settings[gs.PluginKey.RANDOM] if num_albums <= to_queue: dialog = Gtk.MessageDialog( None, Gtk.DialogFlags.MODAL, Gtk.MessageType.INFO, Gtk.ButtonsType.OK, _("The number of albums to randomly play is less than that displayed." )) dialog.run() dialog.destroy() return album_col = self.album_manager.model.columns['album'] chosen = {} # now loop through finding unique random albums # i.e. ensure we dont queue the same album twice for loop in range(0, to_queue): while True: pos = random.randint(0, num_albums - 1) if pos not in chosen: chosen[pos] = None selected_albums.append( self.album_manager.model.store[pos][album_col]) break threshold = self.rating_threshold if favourites else 0 total = 0 for album in selected_albums: # Retrieve and sort the entries of the album tracks = album.get_tracks(threshold) total = total + len(tracks) # Add the songs to the play queue for track in tracks: query_model.add_entry(track.entry, -1) if total == 0 and threshold: dialog = Gtk.MessageDialog( None, Gtk.DialogFlags.MODAL, Gtk.MessageType.INFO, Gtk.ButtonsType.OK, _("No tracks have been added because no tracks meet the favourite rating threshold" )) dialog.run() dialog.destroy() self.props.query_model = query_model # Start the music player = self.shell.props.shell_player player.play_entry(query_model[0][0], self) print("CoverArtBrowser DEBUG - end play_selected_album")
def _connect_properties(self): gs = GSetting() setting = gs.get_setting(gs.Path.PLUGIN) setting.bind(gs.PluginKey.NEW_GENRE_ICON, self, 'new_genre_icon', Gio.SettingsBindFlags.GET)
class ArtistView(Gtk.TreeView, AbstractView): __gtype_name__ = "ArtistView" name = 'artistview' icon_automatic = GObject.property(type=bool, default=True) panedposition = PanedCollapsible.Paned.COLLAPSE __gsignals__ = { 'update-toolbar': (GObject.SIGNAL_RUN_LAST, None, ()) } def __init__(self, *args, **kwargs): super(ArtistView, self).__init__(*args, **kwargs) self._external_plugins = None self.gs = GSetting() self.show_policy = ArtistShowingPolicy(self) self.view = self self._has_initialised = False self._last_row_was_artist = False def initialise(self, source): if self._has_initialised: return self._has_initialised = True self.view_name = "artist_view" super(ArtistView, self).initialise(source) self.album_manager = source.album_manager self.shell = source.shell self.props.has_tooltip = True self.set_enable_tree_lines(True) col = Gtk.TreeViewColumn(' ', Gtk.CellRendererText(), text=6) self.append_column(col) pixbuf = Gtk.CellRendererPixbuf() col = Gtk.TreeViewColumn(_('Covers'), pixbuf, pixbuf=1) self.append_column(col) col = Gtk.TreeViewColumn(_('Artist'), Gtk.CellRendererText(), markup=5) self._artist_col = col col.set_clickable(True) col.set_sort_column_id(0) col.set_sort_indicator(True) col.connect('clicked', self._artist_sort_clicked) self.append_column(col) col = Gtk.TreeViewColumn('', Gtk.CellRendererText(), text=4) self.append_column(col) # dummy column to expand horizontally self.artist_manager = self.album_manager.artist_man self.artist_manager.model.store.set_sort_column_id(0, Gtk.SortType.ASCENDING) self.set_model(self.artist_manager.model.store) # setup iconview drag&drop support # first drag and drop on the coverart view to receive coverart self.enable_model_drag_dest([], Gdk.DragAction.COPY) self.drag_dest_add_image_targets() self.drag_dest_add_text_targets() self.connect('drag-drop', self.on_drag_drop) self.connect('drag-data-received', self.on_drag_data_received) # lastly support drag-drop from coverart to devices/nautilus etc # n.b. enabling of drag-source is controlled by the selection-changed to ensure # we dont allow drag from artists self.connect('drag-begin', self.on_drag_begin) self._targets = Gtk.TargetList.new([Gtk.TargetEntry.new("text/uri-list", 0, 0) ]) # N.B. values taken from rhythmbox v2.97 widgets/rb_entry_view.c self._targets.add_uri_targets(1) self.connect("drag-data-get", self.on_drag_data_get) # define artist specific popup menu self.artist_popup_menu = Menu(self.plugin, self.shell) self.artist_popup_menu.load_from_file('ui/coverart_artist_pop_rb2.ui', 'ui/coverart_artist_pop_rb3.ui') signals = \ { 'play_album_menu_item': self.source.play_album_menu_item_callback, 'queue_album_menu_item': self.source.queue_album_menu_item_callback, 'new_playlist': self.source.add_playlist_menu_item_callback, 'artist_cover_search_menu_item': self.cover_search_menu_item_callback } self.artist_popup_menu.connect_signals(signals) self.artist_popup_menu.connect('pre-popup', self.add_external_menu) # connect properties and signals self._connect_properties() self._connect_signals() def _connect_properties(self): setting = self.gs.get_setting(self.gs.Path.PLUGIN) setting.bind(self.gs.PluginKey.ICON_AUTOMATIC, self, 'icon_automatic', Gio.SettingsBindFlags.GET) def _connect_signals(self): self.connect('row-activated', self._row_activated) self.connect('row-expanded', self._row_expanded) self.connect('button-press-event', self._row_click) self.get_selection().connect('changed', self._selection_changed) self.connect('query-tooltip', self._query_tooltip) def _artist_sort_clicked(self, *args): # in the absence of an apparent way to remove the unsorted default_sort_func # find out if we are now in an unsorted state - if we are # throw another clicked event so that we remain sorted. value, order = self.artist_manager.model.store.get_sort_column_id() if order == None: self._artist_col.emit('clicked') def cover_search_menu_item_callback(self, *args): self.artist_manager.cover_man.search_covers(self.get_selected_objects(just_artist=True), callback=self.source.update_request_status_bar) def _query_tooltip( self, widget, x, y, key, tooltip ): try: winx, winy = self.convert_widget_to_bin_window_coords(x, y) treepath, treecolumn, cellx, celly = self.get_path_at_pos(winx, winy) active_object = self.artist_manager.model.get_from_path(treepath) #active_object=self.artist_manager.model.store[treepath][self.artist_manager.model.columns['artist_album']] if isinstance(active_object, Artist) and \ treecolumn.get_title() == _('Covers') and \ active_object.cover.original != self.artist_manager.cover_man.unknown_cover.original: # we display the tooltip if the row is an artist and the column # is actually the artist cover itself pixbuf = GdkPixbuf.Pixbuf.new_from_file(active_object.cover.original) src_width = pixbuf.get_width() src_height = pixbuf.get_height() factor = min(float(256) / float(src_width), float(256) / float(src_height)) new_width = int(src_width * factor + 0.5) new_height = int(src_height * factor + 0.5) pixbuf = create_pixbuf_from_file_at_size( active_object.cover.original, new_width, new_height) tooltip.set_icon( pixbuf ) return True else: return False except: pass def _row_expanded(self, treeview, treeiter, treepath): ''' event called when clicking the expand icon on the treeview ''' self._row_activated(treeview, treepath, _) def _row_activated(self, treeview, treepath, treeviewcolumn): ''' event called when double clicking on the tree-view or by keyboard ENTER ''' active_object = self.artist_manager.model.get_from_path(treepath) if isinstance(active_object, Artist): self.artist_manager.model.emit('update-path', treepath) else: #we need to play this album self.source.play_selected_album(self.source.favourites) def add_external_menu(self, *args): ''' callback when artist popup menu is about to be displayed ''' self.source.playlist_menu_item_callback() def _row_click(self, widget, event): ''' event called when clicking on a row ''' try: treepath, treecolumn, cellx, celly = self.get_path_at_pos(event.x, event.y) except: return active_object = self.artist_manager.model.get_from_path(treepath) if not isinstance(active_object, Album): self.source.artist_info.emit('selected', active_object.name, None) if self.icon_automatic: # reset counter so that we get correct double click action for albums self.source.click_count = 0 if treecolumn != self.get_expander_column(): if self.row_expanded(treepath) and event.button == 1 and self._last_row_was_artist: self.collapse_row(treepath) else: self.expand_row(treepath, False) self._last_row_was_artist = True if event.button ==3: # on right click # display popup self.artist_popup_menu.popup(self.source, 'popup_menu', 3, Gtk.get_current_event_time()) return if event.button == 1: # on click # to expand the entry view ctrl = event.state & Gdk.ModifierType.CONTROL_MASK shift = event.state & Gdk.ModifierType.SHIFT_MASK if self.icon_automatic and not self._last_row_was_artist: self.source.click_count += 1 if not ctrl and not shift else 0 if self.source.click_count == 1: Gdk.threads_add_timeout(GLib.PRIORITY_DEFAULT_IDLE, 250, self.source.show_hide_pane, active_object) elif event.button ==3: # on right click # display popup self.popup.popup(self.source, 'popup_menu', 3, Gtk.get_current_event_time()) self._last_row_was_artist = False def get_view_icon_name(self): return "artistview.png" def _selection_changed(self, *args): selected = self.get_selected_objects(just_artist=True) print (selected) if len(selected) == 0: self.source.entry_view.clear() return if isinstance(selected[0], Artist): print ("selected artist") self.unset_rows_drag_source() # turn off drag-drop for artists # clear the entry view self.source.entry_view.clear() cover_search_pane_visible = self.source.notebook.get_current_page() == \ self.source.notebook.page_num(self.source.cover_search_pane) # update the cover search pane with the first selected artist if cover_search_pane_visible: print ("update coversearch for artist") print (selected[0]) self.source.cover_search_pane.do_search(selected[0], self.artist_manager.cover_man.update_cover) else: print ("selected album") self.source.update_with_selection() # now turnon drag-drop for album. self.enable_model_drag_source(Gdk.ModifierType.BUTTON1_MASK, [], Gdk.DragAction.COPY) self.drag_source_set_target_list(self._targets) def switch_to_coverpane(self, cover_search_pane): ''' called from the source to update the coverpane when it is switched from the track pane This overrides the base method ''' selected = self.get_selected_objects(just_artist=True) if selected: manager = self.get_default_manager() cover_search_pane.do_search(selected[0], manager.cover_man.update_cover) def get_selected_objects(self, just_artist=False): ''' finds what has been selected returns an array of `Album` ''' selection = self.get_selection() model, treeiter = selection.get_selected() if treeiter: active_object = model.get_value(treeiter,ArtistsModel.columns['artist_album']) if isinstance(active_object, Album): # have chosen an album then just return that album return [active_object] else: # must have chosen an artist - return all albums for the artist by default # or just the artist itself if not just_artist: return self.artist_manager.model.get_albums(active_object.name) else: return [active_object] return [] def switch_to_view(self, source, album): self.initialise(source) self.show_policy.initialise(source.album_manager) self.scroll_to_album(album) def scroll_to_album(self, album): if album: print ("switch to artist view") print (album) artist = self.artist_manager.model.get(album.artist) path = self.artist_manager.model.get_path(artist) print (artist) print (path) path = self.artist_manager.model.store.convert_child_path_to_path(path) print (path) if path: self.scroll_to_cell(path, self._artist_col) self.expand_row(path, False) self.set_cursor(path) def do_update_toolbar(self, *args): self.source.toolbar_manager.set_enabled(False, ToolbarObject.SORT_BY) self.source.toolbar_manager.set_enabled(False, ToolbarObject.SORT_ORDER) self.source.toolbar_manager.set_enabled(True, ToolbarObject.SORT_BY_ARTIST) self.source.toolbar_manager.set_enabled(True, ToolbarObject.SORT_ORDER_ARTIST) def on_drag_drop(self, widget, context, x, y, time): ''' Callback called when a drag operation finishes over the view of the source. It decides if the dropped item can be processed as an image to use as a cover. ''' # stop the propagation of the signal (deactivates superclass callback) if rb3compat.is_rb3(self.shell): widget.stop_emission_by_name('drag-drop') else: widget.stop_emission('drag-drop') # obtain the path of the icon over which the drag operation finished drop_info = self.get_dest_row_at_pos(x, y) path = None if drop_info: path, position = drop_info result = path is not None if result: target = self.drag_dest_find_target(context, None) widget.drag_get_data(context, target, time) return result def on_drag_data_received(self, widget, drag_context, x, y, data, info, time): ''' Callback called when the drag source has prepared the data (pixbuf) for us to use. ''' # stop the propagation of the signal (deactivates superclass callback) if rb3compat.is_rb3(self.shell): widget.stop_emission_by_name('drag-data-received') else: widget.stop_emission('drag-data-received') # get the artist and the info and ask the loader to update the cover path, position = self.get_dest_row_at_pos(x, y) artist_album = widget.get_model()[path][2] pixbuf = data.get_pixbuf() if isinstance(artist_album, Album): manager = self.album_manager else: manager = self.artist_manager if pixbuf: manager.cover_man.update_cover(artist_album, pixbuf) else: uri = data.get_text() manager.cover_man.update_cover(artist_album, uri=uri) # call the context drag_finished to inform the source about it drag_context.finish(True, False, time) def on_drag_data_get(self, widget, drag_context, data, info, time): ''' Callback called when the drag destination (playlist) has requested what album (icon) has been dragged ''' uris = [] for album in widget.get_selected_objects(): for track in album.get_tracks(): uris.append(track.location) data.set_uris(uris) # stop the propagation of the signal (deactivates superclass callback) if rb3compat.is_rb3(self.shell): widget.stop_emission_by_name('drag-data-get') else: widget.stop_emission('drag-data-get') def on_drag_begin(self, widget, context): ''' Callback called when the drag-drop from coverview has started Changes the drag icon as appropriate ''' album_number = len(widget.get_selected_objects()) if album_number == 1: item = Gtk.STOCK_DND else: item = Gtk.STOCK_DND_MULTIPLE widget.drag_source_set_icon_stock(item) if rb3compat.is_rb3(self.shell): widget.stop_emission_by_name('drag-begin') else: widget.stop_emission('drag-begin') def get_default_manager(self): ''' the default manager for this view is the artist_manager ''' return self.artist_manager
def _connect_properties(self): gs = GSetting() settings = gs.get_setting(gs.Path.PLUGIN) settings.bind(gs.PluginKey.CUSTOM_STATUSBAR, self, 'custom_statusbar_enabled', Gio.SettingsBindFlags.GET)
def __init__(self, shell, source): ''' Initializes the entryview. ''' self.shell = shell self.source = source self.plugin = self.source.props.plugin super(RB.EntryView, self).__init__(db=shell.props.db, shell_player=shell.props.shell_player, is_drag_source=True, visible_columns=[]) cl = CoverLocale() cl.switch_locale(cl.Locale.RB) self.append_column(RB.EntryViewColumn.TRACK_NUMBER, False) self.append_column(RB.EntryViewColumn.TITLE, True) # always shown self.append_column(RB.EntryViewColumn.GENRE, False) self.append_column(RB.EntryViewColumn.ARTIST, False) self.append_column(RB.EntryViewColumn.ALBUM, False) self.append_column(RB.EntryViewColumn.DURATION, False) self.append_column(RB.EntryViewColumn.COMMENT, False) self.append_column(RB.EntryViewColumn.RATING, False) self.append_column(RB.EntryViewColumn.QUALITY, False) self.append_column(RB.EntryViewColumn.PLAY_COUNT, False) self.append_column(RB.EntryViewColumn.LAST_PLAYED, False) self.append_column(RB.EntryViewColumn.YEAR, False) self.append_column(RB.EntryViewColumn.FIRST_SEEN, False) self.append_column(RB.EntryViewColumn.LOCATION, False) self.append_column(RB.EntryViewColumn.BPM, False) cl.switch_locale(cl.Locale.LOCALE_DOMAIN) # UI elements need to be imported. ui = Gtk.Builder() ui.set_translation_domain(cl.Locale.LOCALE_DOMAIN) ui.add_from_file(rb.find_plugin_file(self.plugin, 'ui/coverart_entryview.ui')) ui.connect_signals(self) self.popup_menu = ui.get_object('entryview_popup_menu') # connect signals to the shell to know when the playing state changes self.shell.props.shell_player.connect('playing-song-changed', self.playing_song_changed) self.shell.props.shell_player.connect('playing-changed', self.playing_changed) self.playlist_sub_menu_item = ui.get_object('playlist_sub_menu_item') self.actiongroup = Gtk.ActionGroup('coverentryplaylist_submenu') uim = self.shell.props.ui_manager uim.insert_action_group(self.actiongroup) self.external_plugins = \ CreateExternalPluginMenu("ca_entryview", self.shell) # connect the visible-columns global setting to update our entryview gs = GSetting() rhythm_settings = gs.get_setting(gs.Path.RBSOURCE) rhythm_settings.connect('changed::visible-columns', self.on_visible_columns_changed) self.on_visible_columns_changed(rhythm_settings, 'visible-columns') self.qm = RB.RhythmDBQueryModel.new_empty(self.shell.props.db) self.set_model(self.qm) # connect the sort-order to the library source sort library_view = self.shell.props.library_source.get_entry_view() library_view.connect('notify::sort-order', self._on_library_sorting_changed) self._on_library_sorting_changed(library_view, library_view.props.sort_order) # connect to the sort-order property self.connect('notify::sort-order', self._notify_sort_order, library_view)
class CoverIconView(EnhancedIconView, AbstractView): __gtype_name__ = "CoverIconView" icon_spacing = GObject.property(type=int, default=0) icon_padding = GObject.property(type=int, default=0) icon_automatic = GObject.property(type=bool, default=True) display_text_enabled = GObject.property(type=bool, default=False) display_text_pos = GObject.property(type=bool, default=False) name = 'coverview' panedposition = PanedCollapsible.Paned.COLLAPSE text_alignment = GObject.property(type=int, default=1) __gsignals__ = { 'update-toolbar': (GObject.SIGNAL_RUN_LAST, None, ()) } def __init__(self, *args, **kwargs): super(CoverIconView, self).__init__(cell_area=AlbumArtCellArea(), *args, **kwargs) self.gs = GSetting() # custom text renderer self._text_renderer = None self.show_policy = AlbumShowingPolicy(self) self.view = self self._has_initialised = False self._last_path = None self._calc_motion_step = 0 self.set_selection_mode(Gtk.SelectionMode.MULTIPLE) self.object_column = AlbumsModel.columns['album'] def initialise(self, source): if self._has_initialised: return self._has_initialised = True self.view_name = "covers_view" super(CoverIconView, self).initialise(source) self.shell = source.shell self.album_manager = source.album_manager # setup iconview drag&drop support # first drag and drop on the coverart view to receive coverart self.enable_model_drag_dest([], Gdk.DragAction.COPY) self.drag_dest_add_image_targets() self.drag_dest_add_text_targets() self.connect('drag-drop', self.on_drag_drop) self.connect('drag-data-received', self.on_drag_data_received) self.source.paned.connect("expanded", self.bottom_expander_expanded_callback) # lastly support drag-drop from coverart to devices/nautilus etc self.connect('drag-begin', self.on_drag_begin) self.enable_model_drag_source(Gdk.ModifierType.BUTTON1_MASK, [], Gdk.DragAction.COPY) # targets = Gtk.TargetList.new([Gtk.TargetEntry.new("application/x-rhythmbox-entry", 0, 0), # Gtk.TargetEntry.new("text/uri-list", 0, 1) ]) targets = Gtk.TargetList.new([Gtk.TargetEntry.new("text/uri-list", 0, 0)]) # N.B. values taken from rhythmbox v2.97 widgets/rb_entry_view.c targets.add_uri_targets(1) self.drag_source_set_target_list(targets) self.connect("drag-data-get", self.on_drag_data_get) # set the model to the view # self.set_pixbuf_column(AlbumsModel.columns['pixbuf']) self.set_model(self.album_manager.model.store) # setup view to monitor mouse movements self.add_events(Gdk.EventMask.POINTER_MOTION_MASK) self.hover_pixbufs = { 'button_play': None, 'button_play_hover': None, 'button_playpause': None, 'button_playpause_hover': None, 'button_queue': None, 'button_queue_hover': None, } for pixbuf_type in self.hover_pixbufs: filename = 'img/' + pixbuf_type + '.png' filename = rb.find_plugin_file(self.plugin, filename) self.hover_pixbufs[pixbuf_type] = GdkPixbuf.Pixbuf.new_from_file_at_size(filename, PLAY_SIZE_X, PLAY_SIZE_Y) self._connect_properties() self._connect_signals() self._activate_markup() self.on_notify_icon_padding() self.on_notify_icon_spacing() def _connect_properties(self): setting = self.gs.get_setting(self.gs.Path.PLUGIN) setting.bind( self.gs.PluginKey.ICON_SPACING, self, 'icon_spacing', Gio.SettingsBindFlags.GET) setting.bind( self.gs.PluginKey.ICON_PADDING, self, 'icon_padding', Gio.SettingsBindFlags.GET) setting.bind(self.gs.PluginKey.DISPLAY_TEXT, self, 'display_text_enabled', Gio.SettingsBindFlags.GET) setting.bind(self.gs.PluginKey.ICON_AUTOMATIC, self, 'icon_automatic', Gio.SettingsBindFlags.GET) setting.bind(self.gs.PluginKey.DISPLAY_TEXT_POS, self, 'display-text-pos', Gio.SettingsBindFlags.GET) setting.bind(self.gs.PluginKey.TEXT_ALIGNMENT, self, 'text-alignment', Gio.SettingsBindFlags.GET) def _connect_signals(self): self.connect("item-clicked", self.item_clicked_callback) self.connect("selection-changed", self.selectionchanged_callback) self.connect("item-activated", self.item_activated_callback) self.connect('notify::icon-spacing', self.on_notify_icon_spacing) self.connect('notify::icon-padding', self.on_notify_icon_padding) self.connect('notify::display-text-enabled', self._activate_markup) self.connect('notify::display-text-pos', self._activate_markup) self.connect('notify::text-alignment', self._create_and_configure_renderer) self.connect("motion-notify-event", self.on_pointer_motion) def get_view_icon_name(self): return "iconview.png" def resize_icon(self, cover_size): ''' Callback called when to resize the icon [common to all views] ''' self.set_item_width(cover_size) def on_drag_drop(self, widget, context, x, y, time): ''' Callback called when a drag operation finishes over the cover view of the source. It decides if the dropped item can be processed as an image to use as a cover. ''' # stop the propagation of the signal (deactivates superclass callback) widget.stop_emission_by_name('drag-drop') # obtain the path of the icon over which the drag operation finished path, pos = widget.get_dest_item_at_pos(x, y) result = path is not None if result: target = self.drag_dest_find_target(context, None) widget.drag_get_data(context, target, time) return result def on_drag_data_received(self, widget, drag_context, x, y, data, info, time): ''' Callback called when the drag source has prepared the data (pixbuf) for us to use. ''' # stop the propagation of the signal (deactivates superclass callback) widget.stop_emission_by_name('drag-data-received') # get the album and the info and ask the loader to update the cover path, pos = widget.get_dest_item_at_pos(x, y) album = widget.get_model()[path][2] pixbuf = data.get_pixbuf() if pixbuf: self.album_manager.cover_man.update_cover(album, pixbuf) else: uri = data.get_text() self.album_manager.cover_man.update_cover(album, uri=uri) # call the context drag_finished to inform the source about it drag_context.finish(True, False, time) def on_drag_data_get(self, widget, drag_context, data, info, time): ''' Callback called when the drag destination (playlist) has requested what album (icon) has been dragged ''' uris = [] for album in widget.get_selected_objects(): for track in album.get_tracks(): uris.append(track.location) sel = data.set_uris(uris) # stop the propagation of the signal (deactivates superclass callback) widget.stop_emission_by_name('drag-data-get') def on_drag_begin(self, widget, context): ''' Callback called when the drag-drop from coverview has started Changes the drag icon as appropriate ''' album_number = len(widget.get_selected_objects()) if album_number == 1: item = Gtk.STOCK_DND else: item = Gtk.STOCK_DND_MULTIPLE widget.drag_source_set_icon_stock(item) widget.stop_emission_by_name('drag-begin') def _cover_play_hotspot(self, path, in_vacinity=False): if path: valid, rect = self.get_cell_rect(path, None) # rect of widget coords cursor_x, cursor_y = self.get_pointer() # returns widget coords c_x = cursor_x - rect.x - (self.icon_padding / 2) - (self.icon_spacing / 2) c_y = cursor_y - rect.y - (self.icon_padding / 2) - (self.icon_spacing / 2) sizing = (rect.width / 3) if in_vacinity else 0 full, x_offset, y_offset = self.props.cell_area.calc_play_icon_offset(0, 0) if full and c_y > y_offset: return False y_offset = y_offset - PLAY_SIZE_Y if (y_offset - PLAY_SIZE_Y) < 0: return False if c_x < (PLAY_SIZE_X + sizing + x_offset) and \ c_y < (PLAY_SIZE_Y + sizing + y_offset) and \ c_x > x_offset and \ c_y > (y_offset - sizing): return True # c_y 0 value at top - largest at bottom of the cover return False def on_pointer_motion(self, widget, event): self._current_mouse_x = event.x self._current_mouse_y = event.y if self._calc_motion_step == 0: self._calc_motion_step = 1 Gdk.threads_add_timeout(GLib.PRIORITY_DEFAULT_IDLE, 100, self._calculate_hotspot) else: path = self.get_path_at_pos(self._current_mouse_x, self._current_mouse_y) if not self._last_path or self._last_path != path: self._display_icon(None, self._last_path) def _display_icon(self, icon, path): self.props.cell_area.hover_pixbuf = icon if path and self.props.window: valid, rect = self.get_cell_rect(path, None) self.props.window.invalidate_rect(rect, True) self.queue_draw() def _calculate_hotspot(self, *args): path = self.get_path_at_pos(self._current_mouse_x, self._current_mouse_y) # if the current path was not the same as the last path then # reset the counter if not self._last_path or self._last_path != path: self._display_icon(None, self._last_path) self._last_path = path self._calc_motion_step = 0 return False self._calc_motion_step = self._calc_motion_step + 1 # if havent yet reached the requisite number of steps then # let the thread roll to the next increment if self._calc_motion_step < 8: return True if not self._cover_play_hotspot(path, in_vacinity=True): # we are not near the hot-spot so decrement the counter # hoping next time around we are near self._calc_motion_step = self._calc_motion_step - 1 self._display_icon(None, self._last_path) return True # from here on in, we are going to display a hotspot icon # so lets decide which one (_, playing) = self.shell.props.shell_player.get_playing() calc_path = -1 if playing: entry = self.shell.props.shell_player.get_playing_entry() album = self.album_manager.model.get_from_dbentry(entry) calc_path = self.album_manager.model.get_path(album) if playing and calc_path == path: icon = 'button_playpause' elif playing: icon = 'button_queue' else: icon = 'button_play' # now we've got the icon - lets double check that we are # actually hovering exactly on the hotspot because the icon will visually change exact_hotspot = self._cover_play_hotspot(path) if exact_hotspot: icon = icon + '_hover' hover = self.hover_pixbufs[icon] self._display_icon(hover, path) self._calc_motion_step = self._calc_motion_step - 1 return True def item_clicked_callback(self, iconview, event, path): ''' Callback called when the user clicks somewhere on the cover_view. Along with source "show_hide_pane", takes care of showing/hiding the bottom pane after a second click on a selected album. ''' # first test if we've clicked on the cover-play icon if self._cover_play_hotspot(path): (_, playing) = self.shell.props.shell_player.get_playing() # first see if anything is playing... if playing: entry = self.shell.props.shell_player.get_playing_entry() album = self.album_manager.model.get_from_dbentry(entry) # if the current playing entry corresponds to the album # we are hovering over then we are requesting to pause if self.album_manager.model.get_from_path(path) == album: self._last_path = path self.shell.props.shell_player.pause() self.on_pointer_motion(self, event) return # this must be a new album so we are asking just # to play this new album ... just need a short interval # for the selection event to kick in first def delay(*args): if playing: # if we are playing then queue up the next album self.source.queue_selected_album(None, self.source.favourites) album = self.get_selected_objects()[0] cl = CoverLocale() cl.switch_locale(cl.Locale.LOCALE_DOMAIN) message = gettext.gettext('Album has added to list of playing albums') self.display_notification(album.name, message, album.cover.original) else: # otherwise just play it self._last_path = path self.source.play_selected_album(self.source.favourites) icon = 'button_play_hover' self.props.cell_area.hover_pixbuf = \ self.hover_pixbufs[icon] Gdk.threads_add_timeout(GLib.PRIORITY_DEFAULT_IDLE, 250, delay, None) return # to expand the entry view ctrl = event.state & Gdk.ModifierType.CONTROL_MASK shift = event.state & Gdk.ModifierType.SHIFT_MASK if self.icon_automatic: self.source.click_count += 1 if not ctrl and not shift else 0 if self.source.click_count == 1: album = self.album_manager.model.get_from_path(path) \ if path else None Gdk.threads_add_timeout(GLib.PRIORITY_DEFAULT_IDLE, 250, self.source.show_hide_pane, album) def item_activated_callback(self, iconview, path): ''' Callback called when the cover view is double clicked or space-bar is pressed. It plays the selected album ''' self.source.play_selected_album(self.source.favourites) return True def on_notify_icon_padding(self, *args): ''' Callback called when the icon-padding gsetting value is changed ''' self.set_item_padding(self.icon_padding) def on_notify_icon_spacing(self, *args): ''' Callback called when the icon-spacing gsetting value is changed ''' self.set_row_spacing(self.icon_spacing) self.set_column_spacing(self.icon_spacing) def _create_and_configure_renderer(self, *args): if not self._text_renderer: # Add own cellrenderer self._text_renderer = Gtk.CellRendererText() self._text_renderer.props.alignment = self.text_alignment self._text_renderer.props.wrap_mode = Pango.WrapMode.WORD if self.text_alignment == 1: self._text_renderer.props.xalign = 0.5 elif self.text_alignment == 0: self._text_renderer.props.xalign = 0 else: self._text_renderer.props.xalign = 1 self._text_renderer.props.yalign = 0 self._text_renderer.props.width = \ self.album_manager.cover_man.cover_size self._text_renderer.props.wrap_width = \ self.album_manager.cover_man.cover_size def _activate_markup(self, *args): ''' Utility method to activate/deactivate the markup text on the cover view. ''' if self.display_text_enabled and self.display_text_pos: if not self._text_renderer: # create and configure the custom cell renderer self._create_and_configure_renderer() # set the renderer self.pack_end(self._text_renderer, False) self.add_attribute(self._text_renderer, 'markup', AlbumsModel.columns['markup']) elif self._text_renderer: # remove the cell renderer self.props.cell_area.remove(self._text_renderer) if self.display_text_enabled: self.set_tooltip_column(-1) # turnoff tooltips else: self.set_tooltip_column(AlbumsModel.columns['tooltip']) def bottom_expander_expanded_callback(self, paned, expand): ''' Callback connected to expanded signal of the paned GtkExpander ''' if expand: # accommodate the viewport if there's an album selected if self.source.last_selected_album: def scroll_to_album(*args): # accommodate the viewport if there's an album selected path = self.album_manager.model.get_path( self.source.last_selected_album) self.scroll_to_path(path, False, 0, 0) return False Gdk.threads_add_idle(GObject.PRIORITY_DEFAULT_IDLE, scroll_to_album, None) def switch_to_view(self, source, album): self.initialise(source) self.show_policy.initialise(source.album_manager) self.scroll_to_album(album) def grab_focus(self): super(EnhancedIconView, self).grab_focus()
class ArtistInfoPane(GObject.GObject): __gsignals__ = { 'selected': (GObject.SIGNAL_RUN_LAST, None, (GObject.TYPE_STRING, GObject.TYPE_STRING)) } paned_pos = GObject.property(type=str) min_paned_pos = 100 def __init__(self, button_box, stack, info_paned, source): GObject.GObject.__init__(self) self.ds = {} self.view = {} # self.buttons = button_box self.source = source self.plugin = source.plugin self.shell = source.shell self.info_paned = info_paned self.current_artist = None self.current_album_title = None self.current = 'artist' self._from_paned_handle = 0 self.stack = stack self.stack.set_transition_type( Gtk.StackTransitionType.SLIDE_LEFT_RIGHT) stack_switcher = Gtk.StackSwitcher() stack_switcher.set_stack(self.stack) self.stack.connect('notify::visible-child-name', self.change_stack) button_box.pack_start(stack_switcher, False, False, 0) button_box.show_all() # cache for artist/album information: valid for a month, can be used indefinitely # if offline, discarded if unused for six months self.info_cache = rb.URLCache(name='info', path=os.path.join( 'coverart_browser', 'info'), refresh=30, discard=180) # cache for rankings (artist top tracks and top albums): valid for a week, # can be used for a month if offline self.ranking_cache = rb.URLCache(name='ranking', path=os.path.join( 'coverart_browser', 'ranking'), refresh=7, lifetime=30) self.info_cache.clean() self.ranking_cache.clean() self.ds['link'] = LinksDataSource() self.ds['artist'] = ArtistDataSource(self.info_cache, self.ranking_cache) self.view['artist'] = ArtistInfoView() self.view['artist'].initialise(self.source, self.shell, self.plugin, self.stack, self.ds['artist'], self.ds['link']) self.ds['album'] = AlbumDataSource(self.info_cache, self.ranking_cache) self.view['album'] = AlbumInfoView() self.view['album'].initialise(self.source, self.shell, self.plugin, self.stack, self.ds['album']) self.ds['echoartist'] = EchoArtistDataSource(self.info_cache, self.ranking_cache) self.view['echoartist'] = EchoArtistInfoView() self.view['echoartist'].initialise(self.source, self.shell, self.plugin, self.stack, self.ds['echoartist'], self.ds['link']) self.gs = GSetting() self.connect_properties() self.connect_signals() Gdk.threads_add_timeout(GLib.PRIORITY_DEFAULT_IDLE, 50, self._change_paned_pos, self.source.viewmgr.view_name) self.view[self.current].activate() def connect_properties(self): ''' Connects the source properties to the saved preferences. ''' setting = self.gs.get_setting(self.gs.Path.PLUGIN) setting.bind(self.gs.PluginKey.ARTIST_INFO_PANED_POSITION, self, 'paned-pos', Gio.SettingsBindFlags.DEFAULT) def connect_signals(self): self.tab_cb_ids = [] # Listen for switch-tab signal from each tab ''' for key, value in self.tab.items(): self.tab_cb_ids.append(( key, self.tab[key].connect ('switch-tab', self.change_tab) )) ''' # Listen for selected signal from the views self.connect('selected', self.select_artist) # lets remember info paned click self.info_paned.connect('button_press_event', self.paned_button_press_callback) self.info_paned.connect('button-release-event', self.paned_button_release_callback) # lets also listen for changes to the view to set the paned position self.source.viewmgr.connect('new-view', self.on_view_changed) def on_view_changed(self, widget, view_name): self._change_paned_pos(view_name) def _change_paned_pos(self, view_name): print(self.paned_pos) paned_positions = eval(self.paned_pos) found = None for viewpos in paned_positions: if view_name in viewpos: found = viewpos break if not found: return values = found.split(":") child_width = int(values[1]) open_type = "closed" if len(values) > 2: open_type = values[2] elif child_width > 0: open_type = "opened" if open_type == "closed": child_width = 0 calc_pos = self.source.page.get_allocated_width() - child_width self.info_paned.set_position(calc_pos) self.info_paned.set_visible(True) def _get_child_width(self): child = self.info_paned.get_child2() return child.get_allocated_width() def paned_button_press_callback(self, widget, event): print('paned_button_press_callback') self._from_paned_handle = 1 if event.type == Gdk.EventType._2BUTTON_PRESS: self._from_paned_handle = 2 def paned_button_release_callback(self, *args): ''' Callback when the artist paned handle is released from its mouse click. ''' if self._from_paned_handle == 0: return False print("paned_button_release_callback") paned_positions = eval(self.paned_pos) found = None for viewpos in paned_positions: if self.source.viewmgr.view_name in viewpos: found = viewpos break if not found: print("cannot find") return True values = found.split(':') child_width = self.source.page.get_allocated_width( ) - self.info_paned.get_position() print(child_width) open_type = "closed" print(values) if len(values) > 2: open_type = values[2] if child_width <= self.min_paned_pos and \ self._from_paned_handle == 1 and \ open_type == "closed": # we are dealing with a situation where the pane is already closed # or almost closed - just shut the door print("we are closed") calc_pos = self.source.page.get_allocated_width() self.info_paned.set_position(calc_pos) return False open_type = "closed" paned_positions.remove(found) if self._from_paned_handle == 2: # we are dealing with a double click situation new_width = child_width if new_width <= self.min_paned_pos: if int(values[1]) == 0: new_width = self.min_paned_pos + 1 else: new_width = int(values[1]) open_type = "opened" child_width = new_width else: new_width = 0 calc_pos = self.source.page.get_allocated_width() - new_width self.info_paned.set_position(calc_pos) if child_width <= self.min_paned_pos and self._from_paned_handle == 1: if int(values[1]) == 0: child_width = self.min_paned_pos + 1 open_type = "opened" else: child_width = 0 calc_pos = self.source.page.get_allocated_width() - child_width self.info_paned.set_position(calc_pos) if self._from_paned_handle == 1 and child_width != 0: open_type = "opened" paned_positions.append(self.source.viewmgr.view_name + \ ":" + \ str(child_width) + \ ":" + \ open_type) self.paned_pos = repr(paned_positions) self._from_paned_handle = 0 print("End artist_info_paned_button_release_callback") def select_artist(self, widget, artist, album_title): print("artist %s title %s" % (artist, album_title)) if self._get_child_width() > self.min_paned_pos: self.view[self.current].reload(artist, album_title) else: self.view[self.current].blank_view() self.current_album_title = album_title self.current_artist = artist def change_stack(self, widget, value): child_name = self.stack.get_visible_child_name() if child_name and self.current != child_name: self.view[self.current].deactivate() if self._get_child_width() > self.min_paned_pos: self.view[child_name].activate(self.current_artist, self.current_album_title) else: self.view[child_name].blank_view() self.current = child_name