def post_initialise(self):
        super(AltToolbarShared, self).post_initialise()
        self.volume_button.props.value = self.shell.props.shell_player.props.volume
        self.volume_button.bind_property("value", self.shell.props.shell_player, "volume",
                                         Gio.SettingsBindFlags.DEFAULT)
        self.volume_button.set_visible(self.plugin.volume_control)
        self.volume_button.set_relief(Gtk.ReliefStyle.NORMAL)
        child = self.volume_button.get_child()
        child.set_margin_left(5)
        child.set_margin_right(5)

        if self.plugin.inline_label:
            self.song_box.remove(self.song_button_label)

        if self.plugin.compact_progressbar:
            self.song_progress = SmallProgressBar()
        else:
            self.song_progress = SmallScale()

        self.song_progress.connect('control', self._sh_progress_control)
        self.song_progress.show_all()
        self.song_progress_box.pack_start(self.song_progress, False, True, 1)

        # Bring Builtin Actions to plugin
        for (a, b) in ((self.play_button, "play"),
                       (self.prev_button, "play-previous"),
                       (self.next_button, "play-next"),
                       (self.repeat_toggle, "play-repeat"),
                       (self.shuffle_toggle, "play-shuffle")):
            a.set_action_name("app." + b)
            if b == "play-repeat" or b == "play-shuffle":
                # for some distros you need to set the target_value
                # for others this would actually disable the action
                # so work around this by testing if the action is disabled
                # then reset the action
                a.set_action_target_value(GLib.Variant("b", True))
                print(a.get_sensitive())
                if not a.get_sensitive():
                    a.set_detailed_action_name("app." + b)

        # The Play-Repeat button is subject to the plugins Repeat All/one song capability
        self._repeat = Repeat(self.shell, self.repeat_toggle)

        if gtk_version() >= 3.12:
            self.cover_popover = Gtk.Popover.new(self.album_cover)
            image = Gtk.Image.new()
            self.cover_popover.add(image)

            self._popover_inprogress = 0
            self.cover_popover.set_modal(False)
            self.cover_popover.connect('leave-notify-event', self._on_cover_popover_mouse_over)
            self.cover_popover.connect('enter-notify-event', self._on_cover_popover_mouse_over)
            # detect when mouse moves out of the cover image (it has a parent eventbox)
            self.album_cover_eventbox.connect('leave-notify-event', self._on_cover_popover_mouse_over)
            self.album_cover_eventbox.connect('enter-notify-event', self._on_cover_popover_mouse_over)
Example #2
0
    def post_initialise(self):
        self.volume_button.bind_property("value",
                                         self.shell.props.shell_player,
                                         "volume",
                                         Gio.SettingsBindFlags.DEFAULT)
        self.volume_button.props.value = self.shell.props.shell_player.props.volume
        self.volume_button.set_visible(self.plugin.volume_control)
        self.volume_button.set_relief(Gtk.ReliefStyle.NORMAL)
        child = self.volume_button.get_child()
        child.set_margin_left(5)
        child.set_margin_right(5)

        if self.plugin.inline_label:
            self.song_box.remove(self.song_button_label)

        if self.plugin.compact_progressbar:
            self.song_progress = SmallProgressBar()
        else:
            self.song_progress = SmallScale()

        self.song_progress.connect('control', self._sh_progress_control)
        self.song_progress.show_all()
        self.song_progress_box.pack_start(self.song_progress, False, True, 1)

        # Bring Builtin Actions to plugin
        for (a, b) in ((self.play_button, "play"), (self.prev_button,
                                                    "play-previous"),
                       (self.next_button, "play-next"), (self.repeat_toggle,
                                                         "play-repeat"),
                       (self.shuffle_toggle, "play-shuffle")):
            a.set_action_name("app." + b)
            if b == "play-repeat" or b == "play-shuffle":
                # for some distros you need to set the target_value
                # for others this would actually disable the action
                # so work around this by testing if the action is disabled
                # then reset the action
                a.set_action_target_value(GLib.Variant("b", True))
                print(a.get_sensitive())
                if not a.get_sensitive():
                    a.set_detailed_action_name("app." + b)

        if gtk_version() >= 3.12:
            self.cover_popover = Gtk.Popover.new(self.album_cover)
            image = Gtk.Image.new()
            self.cover_popover.add(image)
Example #3
0
class AltToolbarShared(AltToolbarBase):
    '''
    shared components for the compact and headerbar toolbar types
    '''
    def __init__(self):
        '''
        Initialises the object.
        '''
        super(AltToolbarShared, self).__init__()

        # Prepare Album Art Displaying
        self.album_art_db = GObject.new(RB.ExtDB, name="album-art")

        what, width, height = Gtk.icon_size_lookup(Gtk.IconSize.SMALL_TOOLBAR)
        self.icon_width = width
        self.cover_pixbuf = None

    def initialise(self, plugin):
        super(AltToolbarShared, self).initialise(plugin)

        ui = rb.find_plugin_file(plugin, 'ui/alttoolbar.ui')

        builder = Gtk.Builder()
        builder.add_from_file(ui)

        self.load_builder_content(builder)
        self.connect_builder_content(builder)

    def post_initialise(self):
        self.volume_button.bind_property("value",
                                         self.shell.props.shell_player,
                                         "volume",
                                         Gio.SettingsBindFlags.DEFAULT)
        self.volume_button.props.value = self.shell.props.shell_player.props.volume
        self.volume_button.set_visible(self.plugin.volume_control)
        self.volume_button.set_relief(Gtk.ReliefStyle.NORMAL)
        child = self.volume_button.get_child()
        child.set_margin_left(5)
        child.set_margin_right(5)

        if self.plugin.inline_label:
            self.song_box.remove(self.song_button_label)

        if self.plugin.compact_progressbar:
            self.song_progress = SmallProgressBar()
        else:
            self.song_progress = SmallScale()

        self.song_progress.connect('control', self._sh_progress_control)
        self.song_progress.show_all()
        self.song_progress_box.pack_start(self.song_progress, False, True, 1)

        # Bring Builtin Actions to plugin
        for (a, b) in ((self.play_button, "play"), (self.prev_button,
                                                    "play-previous"),
                       (self.next_button, "play-next"), (self.repeat_toggle,
                                                         "play-repeat"),
                       (self.shuffle_toggle, "play-shuffle")):
            a.set_action_name("app." + b)
            if b == "play-repeat" or b == "play-shuffle":
                # for some distros you need to set the target_value
                # for others this would actually disable the action
                # so work around this by testing if the action is disabled
                # then reset the action
                a.set_action_target_value(GLib.Variant("b", True))
                print(a.get_sensitive())
                if not a.get_sensitive():
                    a.set_detailed_action_name("app." + b)

        if gtk_version() >= 3.12:
            self.cover_popover = Gtk.Popover.new(self.album_cover)
            image = Gtk.Image.new()
            self.cover_popover.add(image)

    def show_cover_tooltip(self, tooltip):
        if (self.cover_pixbuf is not None):
            if gtk_version() >= 3.12:
                if self.cover_popover.get_visible():
                    return False
                image = self.cover_popover.get_child()
                image.set_from_pixbuf(
                    self.cover_pixbuf.scale_simple(300, 300,
                                                   GdkPixbuf.InterpType.HYPER))
                self.cover_popover.show_all()
            else:
                tooltip.set_icon(
                    self.cover_pixbuf.scale_simple(300, 300,
                                                   GdkPixbuf.InterpType.HYPER))
            return True
        else:
            return False

    def show_slider(self, visibility):
        self.song_box.set_visible(visibility)

    def display_song(self, entry):
        self.entry = entry

        self.cover_pixbuf = None
        self.album_cover.clear()

        if self.plugin.inline_label:
            ret = self._inline_progress_label(entry)
        else:
            ret = self._combined_progress_label(entry)

        if ret:
            key = entry.create_ext_db_key(RB.RhythmDBPropType.ALBUM)
            self.album_art_db.request(key,
                                      self.display_song_album_art_callback,
                                      entry)

    def _inline_progress_label(self, entry):

        if (entry is None):
            # self.song_button_label.set_text("")
            self.inline_box.set_visible(False)
            return False

        self.inline_box.set_visible(True)

        stream_title = self.shell.props.db.entry_request_extra_metadata(
            entry, RB.RHYTHMDB_PROP_STREAM_SONG_TITLE)
        stream_artist = self.shell.props.db.entry_request_extra_metadata(
            entry, RB.RHYTHMDB_PROP_STREAM_SONG_ARTIST)

        def set_labels(title, artist):
            for child in self.inline_box:
                self.inline_box.remove(child)

            self.song_title = Gtk.Label()
            self.song_title.set_markup(title)
            self.song_title.set_ellipsize(Pango.EllipsizeMode.END)
            self.song_title.show()
            self.inline_box.pack_start(self.song_title, False, True, 0)
            print(artist)
            if artist != "" or artist:
                print("adding artist")
                self.song_artist = Gtk.Label()
                self.song_artist.set_markup(artist)
                self.song_artist.set_ellipsize(Pango.EllipsizeMode.END)
                self.song_artist.show()
                self.inline_box.pack_start(self.song_artist, False, True, 1)

        if stream_title:
            print("stream_title")
            if stream_artist:
                artist_markup = "<small>{artist}</small>".format(
                    artist=GLib.markup_escape_text(stream_artist))
            else:
                artist_markup = ""

            title_markup = "<b>{title}</b>".format(
                title=GLib.markup_escape_text(stream_title))

            set_labels(title_markup, artist_markup)

            return True

        album = entry.get_string(RB.RhythmDBPropType.ALBUM)
        if not album or album == "":
            print("album")
            title_markup = "<b>{title}</b>".format(
                title=GLib.markup_escape_text(
                    entry.get_string(RB.RhythmDBPropType.TITLE)))

            artist = entry.get_string(RB.RhythmDBPropType.ARTIST)
            if artist and artist != "":
                artist_markup = "<small>{artist}</small>".format(
                    artist=GLib.markup_escape_text(
                        entry.get_string(RB.RhythmDBPropType.ARTIST)))
            else:
                artist_markup = ""

            set_labels(title_markup, artist_markup)

            return True

        if self.plugin.playing_label:
            print("playing_label")
            year = entry.get_ulong(RB.RhythmDBPropType.DATE)
            if year == 0:
                year = date.today().year
            else:
                year = datetime.fromordinal(year).year

            title_markup = "<b>{album}</b>".format(
                album=GLib.markup_escape_text(
                    entry.get_string(RB.RhythmDBPropType.ALBUM)))
            artist_markup = "<small>{genre} - {year}</small>".format(
                genre=GLib.markup_escape_text(
                    entry.get_string(RB.RhythmDBPropType.GENRE)),
                year=GLib.markup_escape_text(str(year)))

            set_labels(title_markup, artist_markup)
        else:
            print("not playing_label")
            title_markup = "<b>{title}</b>".format(
                title=GLib.markup_escape_text(
                    entry.get_string(RB.RhythmDBPropType.TITLE)))

            artist_markup = "<small>{artist}</small>".format(
                artist=GLib.markup_escape_text(
                    entry.get_string(RB.RhythmDBPropType.ARTIST)))

            set_labels(title_markup, artist_markup)

        return True

    def _combined_progress_label(self, entry):
        '''
           utility function to calculate the label to be used when a progress bar has the label above it
           :param RBEntry
        '''

        if (entry is None):
            self.song_button_label.set_text("")
            return False

        stream_title = self.shell.props.db.entry_request_extra_metadata(
            entry, RB.RHYTHMDB_PROP_STREAM_SONG_TITLE)
        stream_artist = self.shell.props.db.entry_request_extra_metadata(
            entry, RB.RHYTHMDB_PROP_STREAM_SONG_ARTIST)

        if stream_title:
            if stream_artist:
                markup = "<small><b>{title}</b> {artist}</small>".format(
                    title=GLib.markup_escape_text(stream_title),
                    artist=GLib.markup_escape_text(stream_artist))
            else:
                markup = "<small><b>{title}</b></small>".format(
                    title=GLib.markup_escape_text(stream_title))
            self.song_button_label.set_markup(markup)
            return True

        album = entry.get_string(RB.RhythmDBPropType.ALBUM)
        if not album or album == "":
            self.song_button_label.set_markup(
                "<small><b>{title}</b></small>".format(
                    title=GLib.markup_escape_text(
                        entry.get_string(RB.RhythmDBPropType.TITLE))))
            return True

        if self.plugin.playing_label:
            year = entry.get_ulong(RB.RhythmDBPropType.DATE)
            if year == 0:
                year = date.today().year
            else:
                year = datetime.fromordinal(year).year

            self.song_button_label.set_markup(
                "<small>{album} - {genre} - {year}</small>".format(
                    album=GLib.markup_escape_text(
                        entry.get_string(RB.RhythmDBPropType.ALBUM)),
                    genre=GLib.markup_escape_text(
                        entry.get_string(RB.RhythmDBPropType.GENRE)),
                    year=GLib.markup_escape_text(str(year))))
        else:
            self.song_button_label.set_markup(
                "<small><b>{title}</b> {album} - {artist}</small>".format(
                    title=GLib.markup_escape_text(
                        entry.get_string(RB.RhythmDBPropType.TITLE)),
                    album=GLib.markup_escape_text(
                        entry.get_string(RB.RhythmDBPropType.ALBUM)),
                    artist=GLib.markup_escape_text(
                        entry.get_string(RB.RhythmDBPropType.ARTIST))))

        return True

    def display_song_album_art_callback(self,
                                        *args):  # key, filename, data, entry):
        '''
          RBExtDB signal callback to display the album-art
        '''
        # rhythmbox 3.2 breaks the API - need to find the parameter with the pixbuf
        data = None
        for data in args:
            if isinstance(data, GdkPixbuf.Pixbuf):
                break

        if ((data is not None) and (isinstance(data, GdkPixbuf.Pixbuf))):
            self.cover_pixbuf = data
            scale_cover = self.cover_pixbuf.scale_simple(
                34, 34, GdkPixbuf.InterpType.HYPER)

            self.album_cover.set_from_pixbuf(scale_cover)
        else:
            self.cover_pixbuf = None
            self.album_cover.clear()

        self.album_cover.trigger_tooltip_query()

    def show_cover(self, visibility):
        self.album_cover.set_visible(self.plugin.show_album_art)

    def show_small_bar(self):
        self.small_bar.show_all()
        self.inline_box.set_visible(False)

    def play_control_change(self, player, playing):
        image = self.play_button.get_child()
        if (playing):
            if player.get_active_source().can_pause():
                icon_name = "media-playback-pause-symbolic"
            else:
                icon_name = "media-playback-stop-symbolic"

        else:
            icon_name = "media-playback-start-symbolic"

        image.set_from_icon_name(icon_name, image.props.icon_size)

    # Builder related utility functions... ####################################

    def load_builder_content(self, builder):
        if (not hasattr(self, "__builder_obj_names")):
            self.__builder_obj_names = list()

        for obj in builder.get_objects():
            if (isinstance(obj, Gtk.Buildable)):
                name = Gtk.Buildable.get_name(obj).replace(' ', '_')
                self.__dict__[name] = obj
                self.__builder_obj_names.append(name)

    def connect_builder_content(self, builder):
        builder.connect_signals_full(self.connect_builder_content_func, self)

    def connect_builder_content_func(self, builder, object, sig_name,
                                     handler_name, conn_object, flags, target):
        handler = None

        h_name_internal = "_sh_" + handler_name.replace(" ", "_")

        if (hasattr(target, h_name_internal)):
            handler = getattr(target, h_name_internal)
        else:
            handler = eval(handler_name)

        object.connect(sig_name, handler)

    def purge_builder_content(self):
        for name in self.__builder_obj_names:
            o = self.__dict__[name]
            if (isinstance(o, Gtk.Widget)):
                o.destroy()
            del self.__dict__[name]

        del self.__builder_obj_names

    # Signal Handlers ##########################################################

    def _sh_progress_control(self, progress, fraction):
        #if not hasattr(self, 'song_duration'):
        #    return

        if (self.plugin.song_duration != 0):
            self.shell.props.shell_player.set_playing_time(
                self.plugin.song_duration * fraction)

    def _sh_bigger_cover(self, cover, x, y, key, tooltip):
        return self.show_cover_tooltip(tooltip)
class AltToolbarShared(AltToolbarBase):
    """
    shared components for the compact and headerbar toolbar types
    """

    def __init__(self):
        """
        Initialises the object.
        """
        super(AltToolbarShared, self).__init__()

        # Prepare Album Art Displaying
        self.album_art_db = GObject.new(RB.ExtDB, name="album-art")

        what, width, height = Gtk.icon_size_lookup(Gtk.IconSize.SMALL_TOOLBAR)
        self.icon_width = width
        self.cover_pixbuf = None
        self._controllers = {}
        self._tooltip_exceptions = ['album_cover']
        self._moved_controls = []

    def initialise(self, plugin):
        super(AltToolbarShared, self).initialise(plugin)

        ui = rb.find_plugin_file(plugin, 'ui/alttoolbar.ui')

        builder = Gtk.Builder()
        builder.add_from_file(ui)

        self.load_builder_content(builder)
        self.connect_builder_content(builder)

        self._controllers['generic'] = AltGenericController(self)
        # every potential source should have its own controller - we use this to
        # categorise the source and provide specific capability for inherited classes
        # where a controller is not specified then a generic controller is used
        # i.e. use add_controller method to add a controller
        self.add_controller(AltMusicLibraryController(self))
        self.add_controller(AltSoundCloudController(self))
        self.add_controller(AltCoverArtBrowserController(self))
        self.add_controller(AltCoverArtPlaySourceController(self))
        self.add_controller(AltQueueController(self))
        self.add_controller(AltStandardOnlineController(self))
        self.add_controller(AltStandardLocalController(self))
        self.add_controller(AltRadioController(self))
        self.add_controller(AltLastFMController(self))
        self.add_controller(AltPlaylistController(self))
        self.add_controller(AltErrorsController(self))
        self.add_controller(AltPodcastController(self))
        self.add_controller(AltAndroidController(self))

        # support RTL
        for control, icon_name in [(self.prev_button, 'media-skip-backward-symbolic'),
                                   (self.play_button, 'media-playback-start-symbolic'),
                                   (self.next_button, 'media-skip-forward-symbolic')]:
            image = control.get_child()
            icon_name = self.request_rtl_icon(control, icon_name)
            image.set_from_icon_name(icon_name, image.props.icon_size)

        # now move current RBDisplayPageTree to listview stack
        display_tree = self.shell.props.display_page_tree
        self.display_tree_parent = display_tree.get_parent()
        self.display_tree_parent.remove(display_tree)
        self.stack = Gtk.Stack()
        self.stack.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT_RIGHT)
        self.stack.set_transition_duration(1000)

        image_name = 'view-list-symbolic'

        box_listview = Gtk.Box()
        box_listview.pack_start(display_tree, True, True, 0)
        # box_listview.show_all()
        self.stack.add_named(box_listview, "listview")
        self.stack.child_set_property(box_listview, "icon-name", image_name)
        self.stack.show_all()

        self.display_tree_parent.pack1(self.stack, True, True)

        #if 1==2: #self.plugin.enhanced_sidebar:
        toolbar = self.find(display_tree, 'GtkToolbar', 'by_name')
        #context = toolbar.get_style_context()
        #context.add_class('toolbar')
        box = self.find(toolbar, 'GtkBox', 'by_name')
        #box.props.margin_top = 2
        #box.props.margin_bottom = 0
        #box.props.margin_left = 5
        context = box.get_style_context()
        context.add_class('linked')
        #parent = box.get_parent()
        #parent.remove(box)
        #parent_toolbar = toolbar.get_parent()
        #parent_toolbar.remove(toolbar)
        #display_tree.attach(box, 0, 10, 1 ,1 )

        # child, new-parent, old-parent
        #self._moved_controls.append((box, display_tree, parent))
        #self._moved_controls.append((toolbar, None, parent_toolbar))

        # find the actual GtkTreeView in the RBDisplayTree and remove it
        self.rbtree = self.find(display_tree, 'GtkTreeView', 'by_name')
        self.rbtreeparent = self.rbtree.get_parent()
        self.rbtreeparent.remove(self.rbtree)

        self.sidebar = None

    def post_initialise(self):
        super(AltToolbarShared, self).post_initialise()
        self.volume_button.props.value = self.shell.props.shell_player.props.volume
        self.volume_button.bind_property("value", self.shell.props.shell_player, "volume",
                                         Gio.SettingsBindFlags.DEFAULT)
        self.volume_button.set_visible(self.plugin.volume_control)
        self.volume_button.set_relief(Gtk.ReliefStyle.NORMAL)
        child = self.volume_button.get_child()
        child.set_margin_left(5)
        child.set_margin_right(5)

        if self.plugin.inline_label:
            self.song_box.remove(self.song_button_label)

        if self.plugin.compact_progressbar:
            self.song_progress = SmallProgressBar()
        else:
            self.song_progress = SmallScale()

        self.song_progress.connect('control', self._sh_progress_control)
        self.song_progress.show_all()
        self.song_progress_box.pack_start(self.song_progress, False, True, 1)

        # Bring Builtin Actions to plugin
        for (a, b) in ((self.play_button, "play"),
                       (self.prev_button, "play-previous"),
                       (self.next_button, "play-next"),
                       (self.repeat_toggle, "play-repeat"),
                       (self.shuffle_toggle, "play-shuffle")):
            a.set_action_name("app." + b)
            if b == "play-repeat" or b == "play-shuffle":
                # for some distros you need to set the target_value
                # for others this would actually disable the action
                # so work around this by testing if the action is disabled
                # then reset the action
                a.set_action_target_value(GLib.Variant("b", True))
                print(a.get_sensitive())
                if not a.get_sensitive():
                    a.set_detailed_action_name("app." + b)

        # The Play-Repeat button is subject to the plugins Repeat All/one song capability
        self._repeat = Repeat(self.shell, self.repeat_toggle)

        if gtk_version() >= 3.12:
            self.cover_popover = Gtk.Popover.new(self.album_cover)
            image = Gtk.Image.new()
            self.cover_popover.add(image)

            self._popover_inprogress = 0
            self.cover_popover.set_modal(False)
            self.cover_popover.connect('leave-notify-event', self._on_cover_popover_mouse_over)
            self.cover_popover.connect('enter-notify-event', self._on_cover_popover_mouse_over)
            # detect when mouse moves out of the cover image (it has a parent eventbox)
            self.album_cover_eventbox.connect('leave-notify-event', self._on_cover_popover_mouse_over)
            self.album_cover_eventbox.connect('enter-notify-event', self._on_cover_popover_mouse_over)

    def on_startup(self, *args):
        super(AltToolbarShared, self).on_startup(*args)

        if self.plugin.enhanced_sidebar:
            self.sidebar = AltToolbarSidebar(self, self.rbtree)
            self.sidebar.show_all()
            self.rbtreeparent.add(self.sidebar)
        else:
            self.rbtreeparent.add(self.rbtree)

            # self.shell.add_widget(self.rbtree, RB.ShellUILocation.SIDEBAR, expand=True, fill=True)

    def register_moved_control(self, child, old_parent, new_parent=None):
        """
           convenience function to save the GTK child & parents when they are moved.
           we use this info to cleanup when quitting RB - we need to move stuff back because
           otherwise there are random crashes due to memory deallocation issues

        :param child: GTK Widget
        :param old_parent: original GTK container that the child was moved from
        :param new_parent: new GTK container that the child was added to (may just have removed without moving)
        :return:
        """

        # store as a tuple: child, new-parent, old-parent
        self._moved_controls.append((child, new_parent, old_parent))

    def cleanup(self):
        """
          extend
        :return:
        """

        super(AltToolbarShared, self).cleanup()

        if self.sidebar:
            self.sidebar.cleanup()

        self.display_tree_parent.remove(self.stack)
        self.display_tree_parent.pack1(self.shell.props.display_page_tree)
        if self.sidebar:
            self.rbtreeparent.remove(self.sidebar)  # remove our sidebar
            self.rbtreeparent.add(self.rbtree)  # add the original GtkTree view

        print ("####")
        # child, new-parent, old-parent
        for child, new_parent, old_parent in reversed(self._moved_controls):
            if new_parent:
                new_parent.remove(child)
            print (child)
            print (new_parent)
            print (old_parent)
            if isinstance(old_parent, Gtk.Grid):
                print ("attaching to grid")
                old_parent.attach(child, 0, 0, 1, 1)
            else:
                print ("adding to parent")
                old_parent.add(child)

    def add_controller(self, controller):
        """
          register a new controller
        """
        if not controller in self._controllers:
            self._controllers[controller] = controller

    def is_controlled(self, source):
        """
          determine if the source has a controller
          return bool, controller
             if no specific controller (False) then the generic controller returned
        """

        if source in self._controllers:
            return True, self._controllers[source]

        # loop through controllers to find one that is most applicable
        for controller_type in self._controllers:
            if self._controllers[controller_type].valid_source(source):
                return True, self._controllers[controller_type]

        return False, self._controllers['generic']

    def show_cover_tooltip(self, tooltip):
        if ( self.cover_pixbuf is not None ):
            if gtk_version() >= 3.12:
                if self.cover_popover.get_visible():
                    return False
                image = self.cover_popover.get_child()
                image.set_from_pixbuf(self.cover_pixbuf.scale_simple(300, 300,
                                                                     GdkPixbuf.InterpType.HYPER))
                self.cover_popover.show_all()
            else:
                tooltip.set_icon(self.cover_pixbuf.scale_simple(300, 300,
                                                                GdkPixbuf.InterpType.HYPER))
            return True
        else:
            return False

    def _on_cover_popover_mouse_over(self, widget, eventcrossing):
        if eventcrossing.type == Gdk.EventType.ENTER_NOTIFY:
            if self._popover_inprogress == 0:
                self._popover_inprogress = 1
            else:
                self._popover_inprogress = 2

            self._popover_inprogress_count = 0
            print ("enter")
        else:
            print ("exit")
            self._popover_inprogress = 3

        # print (eventcrossing.type)

        def delayed(*args):
            if self._popover_inprogress == 3:
                self._popover_inprogress_count += 1
                if self._popover_inprogress_count < 5:
                    return True

                self.cover_popover.hide()
                self._popover_inprogress = 0
                return False
            else:
                return True

        if self._popover_inprogress == 1:
            print ("addding timeout")
            self._popover_inprogress = 2
            GLib.timeout_add(100, delayed)

    def show_slider(self, visibility):
        self.song_box.set_visible(visibility)

    def display_song(self, entry):
        self.entry = entry

        self.cover_pixbuf = None
        self.album_cover.clear()

        if self.plugin.inline_label:
            ret = self._inline_progress_label(entry)
        else:
            ret = self._combined_progress_label(entry)

        if ret:
            key = entry.create_ext_db_key(RB.RhythmDBPropType.ALBUM)
            self.album_art_db.request(key,
                                      self.display_song_album_art_callback,
                                      entry)

    def _inline_progress_label(self, entry):

        if ( entry is None ):
            # self.song_button_label.set_text("")
            self.inline_box.set_visible(False)
            return False

        self.inline_box.set_visible(True)

        stream_title = self.shell.props.db.entry_request_extra_metadata(entry, RB.RHYTHMDB_PROP_STREAM_SONG_TITLE)
        stream_artist = self.shell.props.db.entry_request_extra_metadata(entry, RB.RHYTHMDB_PROP_STREAM_SONG_ARTIST)

        def set_labels(title, artist):
            for child in self.inline_box:
                self.inline_box.remove(child)

            self.song_title = Gtk.Label()
            self.song_title.set_markup(title)
            self.song_title.set_ellipsize(Pango.EllipsizeMode.END)
            self.song_title.show()
            self.inline_box.pack_start(self.song_title, False, True, 0)
            print (artist)
            if artist != "" or artist:
                print ("adding artist")
                self.song_artist = Gtk.Label()
                self.song_artist.set_markup(artist)
                self.song_artist.set_ellipsize(Pango.EllipsizeMode.END)
                self.song_artist.show()
                self.inline_box.pack_start(self.song_artist, False, True, 1)

        if stream_title:
            print ("stream_title")
            if stream_artist:
                artist_markup = "<small>{artist}</small>".format(
                    artist=GLib.markup_escape_text(stream_artist))
            else:
                artist_markup = ""

            title_markup = "<b>{title}</b>".format(
                title=GLib.markup_escape_text(stream_title))

            set_labels(title_markup, artist_markup)

            return True

        album = entry.get_string(RB.RhythmDBPropType.ALBUM)
        if not album or album == "":
            print ("album")
            title_markup = "<b>{title}</b>".format(
                title=GLib.markup_escape_text(entry.get_string(RB.RhythmDBPropType.TITLE)))

            artist = entry.get_string(RB.RhythmDBPropType.ARTIST)
            if artist and artist != "":
                artist_markup = "<small>{artist}</small>".format(
                    artist=GLib.markup_escape_text(entry.get_string(RB.RhythmDBPropType.ARTIST)))
            else:
                artist_markup = ""

            set_labels(title_markup, artist_markup)

            return True

        if self.plugin.playing_label:
            print ("playing_label")
            year = entry.get_ulong(RB.RhythmDBPropType.DATE)
            if year == 0:
                year = date.today().year
            else:
                year = datetime.fromordinal(year).year

            title_markup = "<b>{album}</b>".format(
                album=GLib.markup_escape_text(entry.get_string(RB.RhythmDBPropType.ALBUM)))
            artist_markup = "<small>{genre} - {year}</small>".format(
                genre=GLib.markup_escape_text(entry.get_string(RB.RhythmDBPropType.GENRE)),
                year=GLib.markup_escape_text(str(year)))

            set_labels(title_markup, artist_markup)
        else:
            print ("not playing_label")
            title_markup = "<b>{title}</b>".format(
                title=GLib.markup_escape_text(entry.get_string(RB.RhythmDBPropType.TITLE)))

            artist_markup = "<small>{artist}</small>".format(
                artist=GLib.markup_escape_text(entry.get_string(RB.RhythmDBPropType.ARTIST)))

            set_labels(title_markup, artist_markup)

        return True


    def _combined_progress_label(self, entry):
        """
           utility function to calculate the label to be used when a progress bar has the label above it
           :param RBEntry
        """

        if ( entry is None ):
            self.song_button_label.set_label("")
            return False

        stream_title = self.shell.props.db.entry_request_extra_metadata(entry, RB.RHYTHMDB_PROP_STREAM_SONG_TITLE)
        stream_artist = self.shell.props.db.entry_request_extra_metadata(entry, RB.RHYTHMDB_PROP_STREAM_SONG_ARTIST)

        if stream_title:
            if stream_artist:
                markup = "<small><b>{title}</b> {artist}</small>".format(
                    title=GLib.markup_escape_text(stream_title),
                    artist=GLib.markup_escape_text(stream_artist))
            else:
                markup = "<small><b>{title}</b></small>".format(
                    title=GLib.markup_escape_text(stream_title))
            self.song_button_label.set_markup(markup)
            return True

        album = entry.get_string(RB.RhythmDBPropType.ALBUM)
        if not album or album == "":
            self.song_button_label.set_markup("<small><b>{title}</b></small>".format(
                title=GLib.markup_escape_text(entry.get_string(RB.RhythmDBPropType.TITLE))))
            return True

        if self.plugin.playing_label:
            year = entry.get_ulong(RB.RhythmDBPropType.DATE)
            if year == 0:
                year = date.today().year
            else:
                year = datetime.fromordinal(year).year

            self.song_button_label.set_markup(
                "<small>{album} - {genre} - {year}</small>".format(
                    album=GLib.markup_escape_text(entry.get_string(RB.RhythmDBPropType.ALBUM)),
                    genre=GLib.markup_escape_text(entry.get_string(RB.RhythmDBPropType.GENRE)),
                    year=GLib.markup_escape_text(str(year))))
        else:
            self.song_button_label.set_markup(
                "<small><b>{title}</b> {album} - {artist}</small>".format(
                    title=GLib.markup_escape_text(entry.get_string(RB.RhythmDBPropType.TITLE)),
                    album=GLib.markup_escape_text(entry.get_string(RB.RhythmDBPropType.ALBUM)),
                    artist=GLib.markup_escape_text(entry.get_string(RB.RhythmDBPropType.ARTIST))))

        return True

    def display_song_album_art_callback(self, *args):  # key, filename, data, entry):
        """
          RBExtDB signal callback to display the album-art
        """
        # rhythmbox 3.2 breaks the API - need to find the parameter with the pixbuf
        data = None
        for data in args:
            if isinstance(data, GdkPixbuf.Pixbuf):
                break

        if ( ( data is not None ) and ( isinstance(data, GdkPixbuf.Pixbuf) ) ):
            self.cover_pixbuf = data
            scale_cover = self.cover_pixbuf.scale_simple(34, 34,
                                                         GdkPixbuf.InterpType.HYPER)

            self.album_cover.set_from_pixbuf(scale_cover)
        else:
            self.cover_pixbuf = None
            self.album_cover.clear()

        self.album_cover.trigger_tooltip_query()


    def show_cover(self, visibility):
        self.album_cover.set_visible(self.plugin.show_album_art)

    def show_small_bar(self):
        self.small_bar.show_all()
        self.inline_box.set_visible(False)

    def request_rtl_icon(self, control, icon_name):

        if gtk_version() < 3.12:
            if control.get_direction() == Gtk.TextDirection.RTL:
                rtl_name = {"media-playback-start-symbolic":"media-playback-start-rtl-symbolic",
                            "media-skip-forward-symbolic":"media-skip-forward-rtl-symbolic",
                            "media-skip-backward-symbolic":"media-skip-backward-rtl-symbolic"}
                icon_name = rtl_name[icon_name]

        return icon_name

    def play_control_change(self, player, playing):
        image = self.play_button.get_child()
        if (playing):
            if player.get_active_source().can_pause():
                icon_name = "media-playback-pause-symbolic"
            else:
                icon_name = "media-playback-stop-symbolic"

        else:
            icon_name = self.request_rtl_icon(self.play_button, "media-playback-start-symbolic")

        image.set_from_icon_name(icon_name, image.props.icon_size)

    # Builder related utility functions... ####################################

    def load_builder_content(self, builder):
        if ( not hasattr(self, "__builder_obj_names") ):
            self.__builder_obj_names = list()

        for obj in builder.get_objects():
            if ( isinstance(obj, Gtk.Buildable) ):
                name = Gtk.Buildable.get_name(obj).replace(' ', '_')
                self.__dict__[name] = obj
                self.__builder_obj_names.append(name)

                if not self.plugin.show_tooltips and obj.get_has_tooltip():
                    if not name in self._tooltip_exceptions:
                        obj.set_has_tooltip(False)

    def connect_builder_content(self, builder):
        builder.connect_signals_full(self.connect_builder_content_func, self)

    def connect_builder_content_func(self,
                                     builder,
                                     object,
                                     sig_name,
                                     handler_name,
                                     conn_object,
                                     flags,
                                     target):
        handler = None

        h_name_internal = "_sh_" + handler_name.replace(" ", "_")

        if ( hasattr(target, h_name_internal) ):
            handler = getattr(target, h_name_internal)
        else:
            handler = eval(handler_name)

        object.connect(sig_name, handler)

    def purge_builder_content(self):
        for name in self.__builder_obj_names:
            o = self.__dict__[name]
            if ( isinstance(o, Gtk.Widget) ):
                o.destroy()
            del self.__dict__[name]

        del self.__builder_obj_names

    # Signal Handlers ##########################################################

    def _sh_progress_control(self, progress, fraction):
        # if not hasattr(self, 'song_duration'):
        #    return

        if ( self.plugin.song_duration != 0 ):
            self.shell.props.shell_player.set_playing_time(self.plugin.song_duration * fraction)

    def _sh_bigger_cover(self, cover, x, y, key, tooltip):
        return self.show_cover_tooltip(tooltip)
Example #5
0
class AltToolbarShared(AltToolbarBase):
    """
    shared components for the compact and headerbar toolbar types
    """
    def __init__(self):
        """
        Initialises the object.
        """
        super(AltToolbarShared, self).__init__()

        # Prepare Album Art Displaying
        self.album_art_db = GObject.new(RB.ExtDB, name="album-art")

        what, width, height = Gtk.icon_size_lookup(Gtk.IconSize.SMALL_TOOLBAR)
        self.icon_width = width
        self.cover_pixbuf = None
        self._controllers = {}
        self._tooltip_exceptions = ['album_cover']
        self._moved_controls = []

    def initialise(self, plugin):
        super(AltToolbarShared, self).initialise(plugin)

        ui = rb.find_plugin_file(plugin, 'ui/alttoolbar.ui')

        builder = Gtk.Builder()
        builder.add_from_file(ui)

        self.load_builder_content(builder)
        self.connect_builder_content(builder)

        self._controllers['generic'] = AltGenericController(self)
        # every potential source should have its own controller - we use this to
        # categorise the source and provide specific capability for inherited classes
        # where a controller is not specified then a generic controller is used
        # i.e. use add_controller method to add a controller
        self.add_controller(AltMusicLibraryController(self))
        self.add_controller(AltSoundCloudController(self))
        self.add_controller(AltCoverArtBrowserController(self))
        self.add_controller(AltCoverArtPlaySourceController(self))
        self.add_controller(AltQueueController(self))
        self.add_controller(AltStandardOnlineController(self))
        self.add_controller(AltStandardLocalController(self))
        self.add_controller(AltRadioController(self))
        self.add_controller(AltLastFMController(self))
        self.add_controller(AltPlaylistController(self))
        self.add_controller(AltErrorsController(self))
        self.add_controller(AltPodcastController(self))
        self.add_controller(AltAndroidController(self))

        # support RTL
        for control, icon_name in [
            (self.prev_button, 'media-skip-backward-symbolic'),
            (self.play_button, 'media-playback-start-symbolic'),
            (self.next_button, 'media-skip-forward-symbolic')
        ]:
            image = control.get_child()
            icon_name = self.request_rtl_icon(control, icon_name)
            image.set_from_icon_name(icon_name, image.props.icon_size)

        # now move current RBDisplayPageTree to listview stack
        display_tree = self.shell.props.display_page_tree
        self.display_tree_parent = display_tree.get_parent()
        self.display_tree_parent.remove(display_tree)
        self.stack = Gtk.Stack()
        self.stack.set_transition_type(
            Gtk.StackTransitionType.SLIDE_LEFT_RIGHT)
        self.stack.set_transition_duration(1000)

        image_name = 'view-list-symbolic'

        box_listview = Gtk.Box()
        box_listview.pack_start(display_tree, True, True, 0)
        # box_listview.show_all()
        self.stack.add_named(box_listview, "listview")
        self.stack.child_set_property(box_listview, "icon-name", image_name)
        self.stack.show_all()

        self.display_tree_parent.pack1(self.stack, True, True)

        #if 1==2: #self.plugin.enhanced_sidebar:
        toolbar = self.find(display_tree, 'GtkToolbar', 'by_name')
        #context = toolbar.get_style_context()
        #context.add_class('toolbar')
        box = self.find(toolbar, 'GtkBox', 'by_name')
        #box.props.margin_top = 2
        #box.props.margin_bottom = 0
        #box.props.margin_left = 5
        context = box.get_style_context()
        context.add_class('linked')
        #parent = box.get_parent()
        #parent.remove(box)
        #parent_toolbar = toolbar.get_parent()
        #parent_toolbar.remove(toolbar)
        #display_tree.attach(box, 0, 10, 1 ,1 )

        # child, new-parent, old-parent
        #self._moved_controls.append((box, display_tree, parent))
        #self._moved_controls.append((toolbar, None, parent_toolbar))

        # find the actual GtkTreeView in the RBDisplayTree and remove it
        self.rbtree = self.find(display_tree, 'GtkTreeView', 'by_name')
        self.rbtreeparent = self.rbtree.get_parent()
        self.rbtreeparent.remove(self.rbtree)

        self.sidebar = None

    def post_initialise(self):
        super(AltToolbarShared, self).post_initialise()
        self.volume_button.props.value = self.shell.props.shell_player.props.volume
        self.volume_button.bind_property("value",
                                         self.shell.props.shell_player,
                                         "volume",
                                         Gio.SettingsBindFlags.DEFAULT)
        self.volume_button.set_visible(self.plugin.volume_control)
        self.volume_button.set_relief(Gtk.ReliefStyle.NORMAL)
        child = self.volume_button.get_child()
        child.set_margin_left(5)
        child.set_margin_right(5)

        if self.plugin.inline_label:
            self.song_box.remove(self.song_button_label)

        if self.plugin.compact_progressbar:
            self.song_progress = SmallProgressBar()
        else:
            self.song_progress = SmallScale()

        self.song_progress.connect('control', self._sh_progress_control)
        self.song_progress.show_all()
        self.song_progress_box.pack_start(self.song_progress, False, True, 1)

        # Bring Builtin Actions to plugin
        for (a, b) in ((self.play_button, "play"), (self.prev_button,
                                                    "play-previous"),
                       (self.next_button, "play-next"), (self.repeat_toggle,
                                                         "play-repeat"),
                       (self.shuffle_toggle, "play-shuffle")):
            a.set_action_name("app." + b)
            if b == "play-repeat" or b == "play-shuffle":
                # for some distros you need to set the target_value
                # for others this would actually disable the action
                # so work around this by testing if the action is disabled
                # then reset the action
                a.set_action_target_value(GLib.Variant("b", True))
                print(a.get_sensitive())
                if not a.get_sensitive():
                    a.set_detailed_action_name("app." + b)

        # The Play-Repeat button is subject to the plugins Repeat All/one song capability
        self._repeat = Repeat(self.shell, self.repeat_toggle)

        if gtk_version() >= 3.12:
            self.cover_popover = Gtk.Popover.new(self.album_cover)
            image = Gtk.Image.new()
            self.cover_popover.add(image)

            self._popover_inprogress = 0
            self.cover_popover.set_modal(False)
            self.cover_popover.connect('leave-notify-event',
                                       self._on_cover_popover_mouse_over)
            self.cover_popover.connect('enter-notify-event',
                                       self._on_cover_popover_mouse_over)
            # detect when mouse moves out of the cover image (it has a parent eventbox)
            self.album_cover_eventbox.connect(
                'leave-notify-event', self._on_cover_popover_mouse_over)
            self.album_cover_eventbox.connect(
                'enter-notify-event', self._on_cover_popover_mouse_over)

    def on_startup(self, *args):
        super(AltToolbarShared, self).on_startup(*args)

        if self.plugin.enhanced_sidebar:
            self.sidebar = AltToolbarSidebar(self, self.rbtree)
            self.sidebar.show_all()
            self.rbtreeparent.add(self.sidebar)
        else:
            self.rbtreeparent.add(self.rbtree)

            # self.shell.add_widget(self.rbtree, RB.ShellUILocation.SIDEBAR, expand=True, fill=True)

    def register_moved_control(self, child, old_parent, new_parent=None):
        """
           convenience function to save the GTK child & parents when they are moved.
           we use this info to cleanup when quitting RB - we need to move stuff back because
           otherwise there are random crashes due to memory deallocation issues

        :param child: GTK Widget
        :param old_parent: original GTK container that the child was moved from
        :param new_parent: new GTK container that the child was added to (may just have removed without moving)
        :return:
        """

        # store as a tuple: child, new-parent, old-parent
        self._moved_controls.append((child, new_parent, old_parent))

    def cleanup(self):
        """
          extend
        :return:
        """

        super(AltToolbarShared, self).cleanup()

        if self.sidebar:
            self.sidebar.cleanup()

        self.display_tree_parent.remove(self.stack)
        self.display_tree_parent.pack1(self.shell.props.display_page_tree)
        if self.sidebar:
            self.rbtreeparent.remove(self.sidebar)  # remove our sidebar
            self.rbtreeparent.add(self.rbtree)  # add the original GtkTree view

        print("####")
        # child, new-parent, old-parent
        for child, new_parent, old_parent in reversed(self._moved_controls):
            if new_parent:
                new_parent.remove(child)
            print(child)
            print(new_parent)
            print(old_parent)
            if isinstance(old_parent, Gtk.Grid):
                print("attaching to grid")
                old_parent.attach(child, 0, 0, 1, 1)
            else:
                print("adding to parent")
                old_parent.add(child)

    def add_controller(self, controller):
        """
          register a new controller
        """
        if not controller in self._controllers:
            self._controllers[controller] = controller

    def is_controlled(self, source):
        """
          determine if the source has a controller
          return bool, controller
             if no specific controller (False) then the generic controller returned
        """

        if source in self._controllers:
            return True, self._controllers[source]

        # loop through controllers to find one that is most applicable
        for controller_type in self._controllers:
            if self._controllers[controller_type].valid_source(source):
                return True, self._controllers[controller_type]

        return False, self._controllers['generic']

    def show_cover_tooltip(self, tooltip):
        if (self.cover_pixbuf is not None):
            if gtk_version() >= 3.12:
                if self.cover_popover.get_visible():
                    return False
                image = self.cover_popover.get_child()
                image.set_from_pixbuf(
                    self.cover_pixbuf.scale_simple(300, 300,
                                                   GdkPixbuf.InterpType.HYPER))
                self.cover_popover.show_all()
            else:
                tooltip.set_icon(
                    self.cover_pixbuf.scale_simple(300, 300,
                                                   GdkPixbuf.InterpType.HYPER))
            return True
        else:
            return False

    def _on_cover_popover_mouse_over(self, widget, eventcrossing):
        if eventcrossing.type == Gdk.EventType.ENTER_NOTIFY:
            if self._popover_inprogress == 0:
                self._popover_inprogress = 1
            else:
                self._popover_inprogress = 2

            self._popover_inprogress_count = 0
            print("enter")
        else:
            print("exit")
            self._popover_inprogress = 3

        # print (eventcrossing.type)

        def delayed(*args):
            if self._popover_inprogress == 3:
                self._popover_inprogress_count += 1
                if self._popover_inprogress_count < 5:
                    return True

                self.cover_popover.hide()
                self._popover_inprogress = 0
                return False
            else:
                return True

        if self._popover_inprogress == 1:
            print("addding timeout")
            self._popover_inprogress = 2
            GLib.timeout_add(100, delayed)

    def show_slider(self, visibility):
        self.song_box.set_visible(visibility)

    def display_song(self, entry):
        self.entry = entry

        self.cover_pixbuf = None
        self.album_cover.clear()

        if self.plugin.inline_label:
            ret = self._inline_progress_label(entry)
        else:
            ret = self._combined_progress_label(entry)

        if ret:
            key = entry.create_ext_db_key(RB.RhythmDBPropType.ALBUM)
            self.album_art_db.request(key,
                                      self.display_song_album_art_callback,
                                      entry)

    def _inline_progress_label(self, entry):

        if (entry is None):
            # self.song_button_label.set_text("")
            self.inline_box.set_visible(False)
            return False

        self.inline_box.set_visible(True)

        stream_title = self.shell.props.db.entry_request_extra_metadata(
            entry, RB.RHYTHMDB_PROP_STREAM_SONG_TITLE)
        stream_artist = self.shell.props.db.entry_request_extra_metadata(
            entry, RB.RHYTHMDB_PROP_STREAM_SONG_ARTIST)

        def set_labels(title, artist):
            for child in self.inline_box:
                self.inline_box.remove(child)

            self.song_title = Gtk.Label()
            self.song_title.set_markup(title)
            self.song_title.set_ellipsize(Pango.EllipsizeMode.END)
            self.song_title.show()
            self.inline_box.pack_start(self.song_title, False, True, 0)
            print(artist)
            if artist != "" or artist:
                print("adding artist")
                self.song_artist = Gtk.Label()
                self.song_artist.set_markup(artist)
                self.song_artist.set_ellipsize(Pango.EllipsizeMode.END)
                self.song_artist.show()
                self.inline_box.pack_start(self.song_artist, False, True, 1)

        if stream_title:
            print("stream_title")
            if stream_artist:
                artist_markup = "<small>{artist}</small>".format(
                    artist=GLib.markup_escape_text(stream_artist))
            else:
                artist_markup = ""

            title_markup = "<b>{title}</b>".format(
                title=GLib.markup_escape_text(stream_title))

            set_labels(title_markup, artist_markup)

            return True

        album = entry.get_string(RB.RhythmDBPropType.ALBUM)
        if not album or album == "":
            print("album")
            title_markup = "<b>{title}</b>".format(
                title=GLib.markup_escape_text(
                    entry.get_string(RB.RhythmDBPropType.TITLE)))

            artist = entry.get_string(RB.RhythmDBPropType.ARTIST)
            if artist and artist != "":
                artist_markup = "<small>{artist}</small>".format(
                    artist=GLib.markup_escape_text(
                        entry.get_string(RB.RhythmDBPropType.ARTIST)))
            else:
                artist_markup = ""

            set_labels(title_markup, artist_markup)

            return True

        if self.plugin.playing_label:
            print("playing_label")
            year = entry.get_ulong(RB.RhythmDBPropType.DATE)
            if year == 0:
                year = date.today().year
            else:
                year = datetime.fromordinal(year).year

            title_markup = "<b>{album}</b>".format(
                album=GLib.markup_escape_text(
                    entry.get_string(RB.RhythmDBPropType.ALBUM)))
            artist_markup = "<small>{genre} - {year}</small>".format(
                genre=GLib.markup_escape_text(
                    entry.get_string(RB.RhythmDBPropType.GENRE)),
                year=GLib.markup_escape_text(str(year)))

            set_labels(title_markup, artist_markup)
        else:
            print("not playing_label")
            title_markup = "<b>{title}</b>".format(
                title=GLib.markup_escape_text(
                    entry.get_string(RB.RhythmDBPropType.TITLE)))

            artist_markup = "<small>{artist}</small>".format(
                artist=GLib.markup_escape_text(
                    entry.get_string(RB.RhythmDBPropType.ARTIST)))

            set_labels(title_markup, artist_markup)

        return True

    def _combined_progress_label(self, entry):
        """
           utility function to calculate the label to be used when a progress bar has the label above it
           :param RBEntry
        """

        if (entry is None):
            self.song_button_label.set_label("")
            return False

        stream_title = self.shell.props.db.entry_request_extra_metadata(
            entry, RB.RHYTHMDB_PROP_STREAM_SONG_TITLE)
        stream_artist = self.shell.props.db.entry_request_extra_metadata(
            entry, RB.RHYTHMDB_PROP_STREAM_SONG_ARTIST)

        if stream_title:
            if stream_artist:
                markup = "<small><b>{title}</b> {artist}</small>".format(
                    title=GLib.markup_escape_text(stream_title),
                    artist=GLib.markup_escape_text(stream_artist))
            else:
                markup = "<small><b>{title}</b></small>".format(
                    title=GLib.markup_escape_text(stream_title))
            self.song_button_label.set_markup(markup)
            return True

        album = entry.get_string(RB.RhythmDBPropType.ALBUM)
        if not album or album == "":
            self.song_button_label.set_markup(
                "<small><b>{title}</b></small>".format(
                    title=GLib.markup_escape_text(
                        entry.get_string(RB.RhythmDBPropType.TITLE))))
            return True

        if self.plugin.playing_label:
            year = entry.get_ulong(RB.RhythmDBPropType.DATE)
            if year == 0:
                year = date.today().year
            else:
                year = datetime.fromordinal(year).year

            self.song_button_label.set_markup(
                "<small>{album} - {genre} - {year}</small>".format(
                    album=GLib.markup_escape_text(
                        entry.get_string(RB.RhythmDBPropType.ALBUM)),
                    genre=GLib.markup_escape_text(
                        entry.get_string(RB.RhythmDBPropType.GENRE)),
                    year=GLib.markup_escape_text(str(year))))
        else:
            self.song_button_label.set_markup(
                "<small><b>{title}</b> {album} - {artist}</small>".format(
                    title=GLib.markup_escape_text(
                        entry.get_string(RB.RhythmDBPropType.TITLE)),
                    album=GLib.markup_escape_text(
                        entry.get_string(RB.RhythmDBPropType.ALBUM)),
                    artist=GLib.markup_escape_text(
                        entry.get_string(RB.RhythmDBPropType.ARTIST))))

        return True

    def display_song_album_art_callback(self,
                                        *args):  # key, filename, data, entry):
        """
          RBExtDB signal callback to display the album-art
        """
        # rhythmbox 3.2 breaks the API - need to find the parameter with the pixbuf
        data = None
        for data in args:
            if isinstance(data, GdkPixbuf.Pixbuf):
                break

        if ((data is not None) and (isinstance(data, GdkPixbuf.Pixbuf))):
            self.cover_pixbuf = data
            scale_cover = self.cover_pixbuf.scale_simple(
                34, 34, GdkPixbuf.InterpType.HYPER)

            self.album_cover.set_from_pixbuf(scale_cover)
        else:
            self.cover_pixbuf = None
            self.album_cover.clear()

        self.album_cover.trigger_tooltip_query()

    def show_cover(self, visibility):
        self.album_cover.set_visible(self.plugin.show_album_art)

    def show_small_bar(self):
        self.small_bar.show_all()
        self.inline_box.set_visible(False)

    def request_rtl_icon(self, control, icon_name):

        if gtk_version() < 3.12:
            if control.get_direction() == Gtk.TextDirection.RTL:
                rtl_name = {
                    "media-playback-start-symbolic":
                    "media-playback-start-rtl-symbolic",
                    "media-skip-forward-symbolic":
                    "media-skip-forward-rtl-symbolic",
                    "media-skip-backward-symbolic":
                    "media-skip-backward-rtl-symbolic"
                }
                icon_name = rtl_name[icon_name]

        return icon_name

    def play_control_change(self, player, playing):
        image = self.play_button.get_child()
        if (playing):
            if player.get_active_source().can_pause():
                icon_name = "media-playback-pause-symbolic"
            else:
                icon_name = "media-playback-stop-symbolic"

        else:
            icon_name = self.request_rtl_icon(self.play_button,
                                              "media-playback-start-symbolic")

        image.set_from_icon_name(icon_name, image.props.icon_size)

    # Builder related utility functions... ####################################

    def load_builder_content(self, builder):
        if (not hasattr(self, "__builder_obj_names")):
            self.__builder_obj_names = list()

        for obj in builder.get_objects():
            if (isinstance(obj, Gtk.Buildable)):
                name = Gtk.Buildable.get_name(obj).replace(' ', '_')
                self.__dict__[name] = obj
                self.__builder_obj_names.append(name)

                if not self.plugin.show_tooltips and obj.get_has_tooltip():
                    if not name in self._tooltip_exceptions:
                        obj.set_has_tooltip(False)

    def connect_builder_content(self, builder):
        builder.connect_signals_full(self.connect_builder_content_func, self)

    def connect_builder_content_func(self, builder, object, sig_name,
                                     handler_name, conn_object, flags, target):
        handler = None

        h_name_internal = "_sh_" + handler_name.replace(" ", "_")

        if (hasattr(target, h_name_internal)):
            handler = getattr(target, h_name_internal)
        else:
            handler = eval(handler_name)

        object.connect(sig_name, handler)

    def purge_builder_content(self):
        for name in self.__builder_obj_names:
            o = self.__dict__[name]
            if (isinstance(o, Gtk.Widget)):
                o.destroy()
            del self.__dict__[name]

        del self.__builder_obj_names

    # Signal Handlers ##########################################################

    def _sh_progress_control(self, progress, fraction):
        # if not hasattr(self, 'song_duration'):
        #    return

        if (self.plugin.song_duration != 0):
            self.shell.props.shell_player.set_playing_time(
                self.plugin.song_duration * fraction)

    def _sh_bigger_cover(self, cover, x, y, key, tooltip):
        return self.show_cover_tooltip(tooltip)
class AltToolbarShared(AltToolbarBase):
    '''
    shared components for the compact and headerbar toolbar types
    '''

    def __init__(self):
        '''
        Initialises the object.
        '''
        super(AltToolbarShared, self).__init__()

        # Prepare Album Art Displaying
        self.album_art_db = GObject.new(RB.ExtDB, name="album-art")

        what, width, height = Gtk.icon_size_lookup(Gtk.IconSize.SMALL_TOOLBAR)
        self.icon_width = width
        self.cover_pixbuf = None

    def initialise(self, plugin):
        super(AltToolbarShared, self).initialise(plugin)

        ui = rb.find_plugin_file(plugin, 'ui/alttoolbar.ui')

        builder = Gtk.Builder()
        builder.add_from_file(ui)

        self.load_builder_content(builder)
        self.connect_builder_content(builder)

    def post_initialise(self):
        self.volume_button.bind_property("value", self.shell.props.shell_player, "volume",
                                         Gio.SettingsBindFlags.DEFAULT)
        self.volume_button.props.value = self.shell.props.shell_player.props.volume
        self.volume_button.set_visible(self.plugin.volume_control)
        self.volume_button.set_relief(Gtk.ReliefStyle.NORMAL)
        child = self.volume_button.get_child()
        child.set_margin_left(5)
        child.set_margin_right(5)

        if self.plugin.inline_label:
            self.song_box.remove(self.song_button_label)

        if self.plugin.compact_progressbar:
            self.song_progress = SmallProgressBar()
        else:
            self.song_progress = SmallScale()

        self.song_progress.connect('control', self._sh_progress_control)
        self.song_progress.show_all()
        self.song_progress_box.pack_start(self.song_progress, False, True, 1)

        # Bring Builtin Actions to plugin
        for (a, b) in ((self.play_button, "play"),
                       (self.prev_button, "play-previous"),
                       (self.next_button, "play-next"),
                       (self.repeat_toggle, "play-repeat"),
                       (self.shuffle_toggle, "play-shuffle")):
            a.set_action_name("app." + b)
            if b == "play-repeat" or b == "play-shuffle":
                # for some distros you need to set the target_value
                # for others this would actually disable the action
                # so work around this by testing if the action is disabled
                # then reset the action
                a.set_action_target_value(GLib.Variant("b", True))
                print(a.get_sensitive())
                if not a.get_sensitive():
                    a.set_detailed_action_name("app." + b)
                    
 
        if gtk_version() >= 3.12:
            self.cover_popover = Gtk.Popover.new(self.album_cover)
            image = Gtk.Image.new()
            self.cover_popover.add(image)
        
    def show_cover_tooltip(self, tooltip):
        if ( self.cover_pixbuf is not None ):
            if gtk_version() >= 3.12:
                if self.cover_popover.get_visible():
                    return False
                image = self.cover_popover.get_child()
                image.set_from_pixbuf(self.cover_pixbuf.scale_simple(300, 300,
                                                                GdkPixbuf.InterpType.HYPER))
                self.cover_popover.show_all()
            else:
                tooltip.set_icon(self.cover_pixbuf.scale_simple(300, 300,
                                                            GdkPixbuf.InterpType.HYPER))
            return True
        else:
            return False

    def show_slider(self, visibility):
        self.song_box.set_visible(visibility)

    def display_song(self, entry):
        self.entry = entry

        self.cover_pixbuf = None
        self.album_cover.clear()

        if self.plugin.inline_label:
            ret = self._inline_progress_label(entry)
        else:
            ret = self._combined_progress_label(entry)

        if ret:
            key = entry.create_ext_db_key(RB.RhythmDBPropType.ALBUM)
            self.album_art_db.request(key,
                                      self.display_song_album_art_callback,
                                      entry)

    def _inline_progress_label(self, entry):

        if ( entry is None ):
            # self.song_button_label.set_text("")
            self.inline_box.set_visible(False)
            return False

        self.inline_box.set_visible(True)

        stream_title = self.shell.props.db.entry_request_extra_metadata(entry, RB.RHYTHMDB_PROP_STREAM_SONG_TITLE)
        stream_artist = self.shell.props.db.entry_request_extra_metadata(entry, RB.RHYTHMDB_PROP_STREAM_SONG_ARTIST)

        def set_labels(title, artist):
            for child  in self.inline_box:
                self.inline_box.remove(child)

            self.song_title = Gtk.Label()
            self.song_title.set_markup(title)
            self.song_title.set_ellipsize(Pango.EllipsizeMode.END)
            self.song_title.show()
            self.inline_box.pack_start(self.song_title, False, True, 0)
            print (artist)
            if artist != "" or artist:
                print ("adding artist")
                self.song_artist = Gtk.Label()
                self.song_artist.set_markup(artist)
                self.song_artist.set_ellipsize(Pango.EllipsizeMode.END)
                self.song_artist.show()
                self.inline_box.pack_start(self.song_artist, False, True, 1)

        if stream_title:
            print ("stream_title")
            if stream_artist:
                artist_markup = "<small>{artist}</small>".format(
                    artist=GLib.markup_escape_text(stream_artist))
            else:
                artist_markup = ""

            title_markup = "<b>{title}</b>".format(
                title=GLib.markup_escape_text(stream_title))

                
            set_labels(title_markup, artist_markup)
            
            return True

        album = entry.get_string(RB.RhythmDBPropType.ALBUM)
        if not album or album == "":
            print ("album")
            title_markup = "<b>{title}</b>".format(
                title=GLib.markup_escape_text(entry.get_string(RB.RhythmDBPropType.TITLE)))
               
            artist = entry.get_string(RB.RhythmDBPropType.ARTIST)
            if artist and artist != "":
                artist_markup= "<small>{artist}</small>".format(
                        artist=GLib.markup_escape_text(entry.get_string(RB.RhythmDBPropType.ARTIST)))
            else:
                artist_markup = ""
                
            set_labels(title_markup, artist_markup)
            
            return True

        if self.plugin.playing_label:
            print ("playing_label")
            year = entry.get_ulong(RB.RhythmDBPropType.DATE)
            if year == 0:
                year = date.today().year
            else:
                year = datetime.fromordinal(year).year

            title_markup = "<b>{album}</b>".format(
                    album=GLib.markup_escape_text(entry.get_string(RB.RhythmDBPropType.ALBUM)))
            artist_markup = "<small>{genre} - {year}</small>".format(
                    genre=GLib.markup_escape_text(entry.get_string(RB.RhythmDBPropType.GENRE)),
                    year=GLib.markup_escape_text(str(year)))
                    
            set_labels(title_markup, artist_markup)
        else:
            print ("not playing_label")
            title_markup = "<b>{title}</b>".format(
                    title=GLib.markup_escape_text(entry.get_string(RB.RhythmDBPropType.TITLE)))

            artist_markup= "<small>{artist}</small>".format(
                    artist=GLib.markup_escape_text(entry.get_string(RB.RhythmDBPropType.ARTIST)))
            
            set_labels(title_markup, artist_markup)

        return True


    def _combined_progress_label(self, entry):
        '''
           utility function to calculate the label to be used when a progress bar has the label above it
           :param RBEntry
        '''

        if ( entry is None ):
            self.song_button_label.set_text("")
            return False

        stream_title = self.shell.props.db.entry_request_extra_metadata(entry, RB.RHYTHMDB_PROP_STREAM_SONG_TITLE)
        stream_artist = self.shell.props.db.entry_request_extra_metadata(entry, RB.RHYTHMDB_PROP_STREAM_SONG_ARTIST)

        if stream_title:
            if stream_artist:
                markup = "<small><b>{title}</b> {artist}</small>".format(
                    title=GLib.markup_escape_text(stream_title),
                    artist=GLib.markup_escape_text(stream_artist))
            else:
                markup = "<small><b>{title}</b></small>".format(
                    title=GLib.markup_escape_text(stream_title))
            self.song_button_label.set_markup(markup)
            return True

        album = entry.get_string(RB.RhythmDBPropType.ALBUM)
        if not album or album == "":
            self.song_button_label.set_markup("<small><b>{title}</b></small>".format(
                title=GLib.markup_escape_text(entry.get_string(RB.RhythmDBPropType.TITLE))))
            return True

        if self.plugin.playing_label:
            year = entry.get_ulong(RB.RhythmDBPropType.DATE)
            if year == 0:
                year = date.today().year
            else:
                year = datetime.fromordinal(year).year

            self.song_button_label.set_markup(
                "<small>{album} - {genre} - {year}</small>".format(
                    album=GLib.markup_escape_text(entry.get_string(RB.RhythmDBPropType.ALBUM)),
                    genre=GLib.markup_escape_text(entry.get_string(RB.RhythmDBPropType.GENRE)),
                    year=GLib.markup_escape_text(str(year))))
        else:
            self.song_button_label.set_markup(
                "<small><b>{title}</b> {album} - {artist}</small>".format(
                    title=GLib.markup_escape_text(entry.get_string(RB.RhythmDBPropType.TITLE)),
                    album=GLib.markup_escape_text(entry.get_string(RB.RhythmDBPropType.ALBUM)),
                    artist=GLib.markup_escape_text(entry.get_string(RB.RhythmDBPropType.ARTIST))))

        return True

    def display_song_album_art_callback(self, *args):  # key, filename, data, entry):
        '''
          RBExtDB signal callback to display the album-art
        '''
        # rhythmbox 3.2 breaks the API - need to find the parameter with the pixbuf
        data = None
        for data in args:
            if isinstance(data, GdkPixbuf.Pixbuf):
                break

        if ( ( data is not None ) and ( isinstance(data, GdkPixbuf.Pixbuf) ) ):
            self.cover_pixbuf = data
            scale_cover = self.cover_pixbuf.scale_simple(34, 34,
                                                         GdkPixbuf.InterpType.HYPER)

            self.album_cover.set_from_pixbuf(scale_cover)
        else:
            self.cover_pixbuf = None
            self.album_cover.clear()

        self.album_cover.trigger_tooltip_query()


    def show_cover(self, visibility):
        self.album_cover.set_visible(self.plugin.show_album_art)

    def show_small_bar(self):
        self.small_bar.show_all()
        self.inline_box.set_visible(False)


    def play_control_change(self, player, playing):
        image = self.play_button.get_child()
        if (playing):
            if player.get_active_source().can_pause():
                icon_name = "media-playback-pause-symbolic"
            else:
                icon_name = "media-playback-stop-symbolic"

        else:
            icon_name = "media-playback-start-symbolic"

        image.set_from_icon_name(icon_name, image.props.icon_size)

    # Builder related utility functions... ####################################

    def load_builder_content(self, builder):
        if ( not hasattr(self, "__builder_obj_names") ):
            self.__builder_obj_names = list()

        for obj in builder.get_objects():
            if ( isinstance(obj, Gtk.Buildable) ):
                name = Gtk.Buildable.get_name(obj).replace(' ', '_')
                self.__dict__[name] = obj
                self.__builder_obj_names.append(name)

    def connect_builder_content(self, builder):
        builder.connect_signals_full(self.connect_builder_content_func, self)

    def connect_builder_content_func(self,
                                     builder,
                                     object,
                                     sig_name,
                                     handler_name,
                                     conn_object,
                                     flags,
                                     target):
        handler = None

        h_name_internal = "_sh_" + handler_name.replace(" ", "_")

        if ( hasattr(target, h_name_internal) ):
            handler = getattr(target, h_name_internal)
        else:
            handler = eval(handler_name)

        object.connect(sig_name, handler)

    def purge_builder_content(self):
        for name in self.__builder_obj_names:
            o = self.__dict__[name]
            if ( isinstance(o, Gtk.Widget) ):
                o.destroy()
            del self.__dict__[name]

        del self.__builder_obj_names

    # Signal Handlers ##########################################################

    def _sh_progress_control(self, progress, fraction):
        #if not hasattr(self, 'song_duration'):
        #    return

        if ( self.plugin.song_duration != 0 ):
            self.shell.props.shell_player.set_playing_time(self.plugin.song_duration * fraction)

    def _sh_bigger_cover(self, cover, x, y, key, tooltip):
        return self.show_cover_tooltip(tooltip)