Example #1
0
    def __init__(self):
        """
            Init toolbar
        """
        Gtk.Bin.__init__(self)
        self.connect('show', self.__on_show)
        self.connect('hide', self.__on_hide)
        self.set_hexpand(True)
        self.__next_popover = NextPopover()
        self.__search = None
        self.__next_was_inhibited = False
        self.__timeout_id = None
        builder = Gtk.Builder()
        builder.add_from_resource('/org/gnome/Lollypop/ToolbarEnd.ui')
        builder.connect_signals(self)

        self.add(builder.get_object('end'))

        self.__grid_next = builder.get_object('grid-next')

        self.__shuffle_button = builder.get_object('shuffle-button')
        self.__shuffle_image = builder.get_object('shuffle-button-image')
        shuffleAction = Gio.SimpleAction.new('shuffle-button', None)
        shuffleAction.connect('activate', self.__activate_shuffle_button)
        Lp().add_action(shuffleAction)
        Lp().set_accels_for_action("app.shuffle-button", ["<Control>r"])
        Lp().settings.connect('changed::shuffle', self.__on_playback_changed)
        Lp().settings.connect('changed::playback', self.__on_playback_changed)

        self.__party_button = builder.get_object('party-button')
        party_action = Gio.SimpleAction.new('party', None)
        party_action.connect('activate', self.__activate_party_button)
        Lp().add_action(party_action)
        Lp().set_accels_for_action("app.party", ["<Control>p"])

        self.__search_button = builder.get_object('search-button')
        helper = TouchHelper(self.__search_button, "search", "<Control>f")
        helper.set_long_func(self.__on_search_long)
        helper.set_short_func(self.__on_search_short)

        self.__settings_button = builder.get_object('settings-button')

        self.__list_button = builder.get_object('list-button')
        self.__list_button.set_property('has-tooltip', True)
        self.__list_button.connect('query-tooltip',
                                   self.__on_list_button_query_tooltip)
        self.__list_popover = None
        Lp().player.connect('party-changed', self.__on_party_changed)
        Lp().player.connect('lock-changed', self.__on_lock_changed)
    def __init__(self):
        """
            Init toolbar
        """
        Gtk.Bin.__init__(self)
        InfosController.__init__(self, ArtSize.SMALL)
        builder = Gtk.Builder()
        builder.add_from_resource('/org/gnome/Lollypop/ToolbarInfo.ui')
        builder.connect_signals(self)
        self.__pop_tunein = None
        self.__pop_info = None
        self.__timeout_id = None
        self.__width = 0

        self._infobox = builder.get_object('info')
        self.add(self._infobox)

        helper = TouchHelper(self._infobox, None, None)
        helper.set_long_func(self.__on_info_long)
        helper.set_short_func(self.__on_info_short)

        self._spinner = builder.get_object('spinner')

        self.__labels = builder.get_object('nowplaying_labels')
        self.__labels.connect('query-tooltip', self.__on_query_tooltip)
        self.__labels.set_property('has-tooltip', True)

        self._title_label = builder.get_object('title')
        self._artist_label = builder.get_object('artist')
        self._cover = builder.get_object('cover')
        self._cover.set_property('has-tooltip', True)
        # Since GTK 3.20, we can set cover full height
        if Gtk.get_minor_version() > 18:
            self._cover.get_style_context().add_class('toolbar-cover-frame')
        else:
            self._cover.get_style_context().add_class('small-cover-frame')

        self.connect('realize', self.__on_realize)
        Lp().player.connect('loading-changed', self.__on_loading_changed)
        Lp().art.connect('album-artwork-changed', self.__update_cover)
        Lp().art.connect('radio-artwork-changed', self.__update_logo)
Example #3
0
    def __init__(self):
        """
            Init toolbar
        """
        Gtk.Bin.__init__(self)
        InfosController.__init__(self, ArtSize.SMALL)
        builder = Gtk.Builder()
        builder.add_from_resource('/org/gnome/Lollypop/ToolbarInfo.ui')
        builder.connect_signals(self)
        self.__pop_tunein = None
        self.__pop_info = None
        self.__timeout_id = None
        self.__width = 0

        self._infobox = builder.get_object('info')
        self.add(self._infobox)

        helper = TouchHelper(self._infobox, None, None)
        helper.set_long_func(self.__on_info_long)
        helper.set_short_func(self.__on_info_short)

        self._spinner = builder.get_object('spinner')

        self.__labels = builder.get_object('nowplaying_labels')
        self.__labels.connect('query-tooltip', self.__on_query_tooltip)
        self.__labels.set_property('has-tooltip', True)

        self._title_label = builder.get_object('title')
        self._artist_label = builder.get_object('artist')
        self._cover = builder.get_object('cover')
        self._cover.set_property('has-tooltip', True)
        # Since GTK 3.20, we can set cover full height
        if Gtk.get_minor_version() > 18:
            self._cover.get_style_context().add_class('toolbar-cover-frame')
        else:
            self._cover.get_style_context().add_class('small-cover-frame')

        self.connect('realize', self.__on_realize)
        Lp().player.connect('loading-changed', self.__on_loading_changed)
        Lp().art.connect('album-artwork-changed', self.__update_cover)
        Lp().art.connect('radio-artwork-changed', self.__update_logo)
 def populate(self):
     """
         Init widget content
     """
     self.get_style_context().remove_class('loading')
     self._widget = Gtk.EventBox()
     helper = TouchHelper(self._widget, None, None)
     helper.set_long_func(self.__on_click_long)
     helper.set_short_func(self.__on_click_short)
     self._widget.connect('enter-notify-event', self._on_enter_notify)
     self._widget.connect('leave-notify-event', self._on_leave_notify)
     self._cover = Gtk.Image()
     self._cover.set_property('halign', Gtk.Align.CENTER)
     self._cover.set_size_request(ArtSize.BIG, ArtSize.BIG)
     self.__title_label = Gtk.Label()
     self.__title_label.set_ellipsize(Pango.EllipsizeMode.END)
     self.__title_label.set_property('halign', Gtk.Align.CENTER)
     self.__title_label.set_text(self.__name)
     self.__title_label.set_property('has-tooltip', True)
     self.__title_label.connect('query-tooltip', self._on_query_tooltip)
     self._overlay = Gtk.Overlay()
     frame = Gtk.Frame()
     frame.get_style_context().add_class('cover-frame')
     frame.add(self._cover)
     self._overlay.add(frame)
     grid = Gtk.Grid()
     grid.set_orientation(Gtk.Orientation.VERTICAL)
     self._overlay.get_style_context().add_class('white')
     grid.add(self._overlay)
     grid.add(self.__title_label)
     self._widget.add(grid)
     self.set_property('halign', Gtk.Align.CENTER)
     self.set_property('valign', Gtk.Align.CENTER)
     self.add(self._widget)
     self.set_cover()
     self.update_state()
     self.show_all()
Example #5
0
 def populate(self):
     """
         Init widget content
     """
     self.get_style_context().remove_class('loading')
     self._widget = Gtk.EventBox()
     helper = TouchHelper(self._widget, None, None)
     helper.set_long_func(self.__on_click_long)
     helper.set_short_func(self.__on_click_short)
     self._widget.connect('enter-notify-event', self._on_enter_notify)
     self._widget.connect('leave-notify-event', self._on_leave_notify)
     self._cover = Gtk.Image()
     self._cover.set_property('halign', Gtk.Align.CENTER)
     self._cover.set_size_request(ArtSize.BIG, ArtSize.BIG)
     self.__title_label = Gtk.Label()
     self.__title_label.set_ellipsize(Pango.EllipsizeMode.END)
     self.__title_label.set_property('halign', Gtk.Align.CENTER)
     self.__title_label.set_text(self.__name)
     self.__title_label.set_property('has-tooltip', True)
     self.__title_label.connect('query-tooltip', self._on_query_tooltip)
     self._overlay = Gtk.Overlay()
     frame = Gtk.Frame()
     frame.get_style_context().add_class('cover-frame')
     frame.add(self._cover)
     self._overlay.add(frame)
     grid = Gtk.Grid()
     grid.set_orientation(Gtk.Orientation.VERTICAL)
     self._overlay.get_style_context().add_class('white')
     grid.add(self._overlay)
     grid.add(self.__title_label)
     self._widget.add(grid)
     self.set_property('halign', Gtk.Align.CENTER)
     self.set_property('valign', Gtk.Align.CENTER)
     self.add(self._widget)
     self.set_cover()
     self.update_state()
     self.show_all()
Example #6
0
class SettingsDialog:
    """
        Dialog showing lollypop options
    """
    def __init__(self):
        """
            Init dialog
        """
        self.__choosers = []
        self.__cover_tid = None
        self.__mix_tid = None
        self.__popover = None

        cs_api_key = Lp().settings.get_value('cs-api-key').get_string()
        default_cs_api_key = Lp().settings.get_default_value(
            'cs-api-key').get_string()
        if (not cs_api_key or
            cs_api_key == default_cs_api_key) and\
                get_network_available() and\
                Lp().notify is not None:
            Lp().notify.send(
                _("Google Web Services need a custom API key"),
                _("Lollypop needs this to search artwork and music."))

        builder = Gtk.Builder()
        builder.add_from_resource('/org/gnome/Lollypop/SettingsDialog.ui')
        self.__progress = builder.get_object('progress')
        self.__infobar = builder.get_object('infobar')
        self.__reset_button = builder.get_object('reset_button')
        if Lp().lastfm is None or Lp().lastfm.is_goa:
            builder.get_object('lastfm_grid').hide()
        if Lp().scanner.is_locked():
            builder.get_object('reset_button').set_sensitive(False)
        artists = Lp().artists.count()
        albums = Lp().albums.count()
        tracks = Lp().tracks.count()
        builder.get_object('artists').set_text(
            ngettext("%d artist", "%d artists", artists) % artists)
        builder.get_object('albums').set_text(
            ngettext("%d album", "%d albums", albums) % albums)
        builder.get_object('tracks').set_text(
            ngettext("%d track", "%d tracks", tracks) % tracks)

        self.__popover_content = builder.get_object('popover')
        duration = builder.get_object('duration')
        duration.set_range(1, 20)
        duration.set_value(Lp().settings.get_value('mix-duration').get_int32())

        self.__settings_dialog = builder.get_object('settings_dialog')
        self.__settings_dialog.set_transient_for(Lp().window)

        if Lp().settings.get_value('disable-csd'):
            self.__settings_dialog.set_title(_("Preferences"))
        else:
            headerbar = builder.get_object('header_bar')
            headerbar.set_title(_("Preferences"))
            self.__settings_dialog.set_titlebar(headerbar)

        switch_scan = builder.get_object('switch_scan')
        switch_scan.set_state(Lp().settings.get_value('auto-update'))

        switch_view = builder.get_object('switch_dark')
        if Lp().gtk_application_prefer_dark_theme:
            switch_view.set_sensitive(False)
        else:
            switch_view.set_state(Lp().settings.get_value('dark-ui'))

        switch_background = builder.get_object('switch_background')
        switch_background.set_state(Lp().settings.get_value('background-mode'))

        switch_state = builder.get_object('switch_state')
        switch_state.set_state(Lp().settings.get_value('save-state'))

        switch_mix = builder.get_object('switch_mix')
        switch_mix.set_state(Lp().settings.get_value('mix'))
        self.__helper = TouchHelper(switch_mix, None, None)
        self.__helper.set_long_func(self.__mix_long_func, switch_mix)
        self.__helper.set_short_func(self.__mix_short_func, switch_mix)

        switch_mix_party = builder.get_object('switch_mix_party')
        switch_mix_party.set_state(Lp().settings.get_value('party-mix'))

        switch_librefm = builder.get_object('switch_librefm')
        switch_librefm.set_state(Lp().settings.get_value('use-librefm'))

        switch_artwork_tags = builder.get_object('switch_artwork_tags')
        # Check portal for kid3-cli
        can_set_cover = False
        try:
            bus = Gio.bus_get_sync(Gio.BusType.SESSION, None)
            proxy = Gio.DBusProxy.new_sync(bus, Gio.DBusProxyFlags.NONE, None,
                                           'org.gnome.Lollypop.Portal',
                                           '/org/gnome/LollypopPortal',
                                           'org.gnome.Lollypop.Portal', None)
            can_set_cover = proxy.call_sync('CanSetCover', None,
                                            Gio.DBusCallFlags.NO_AUTO_START,
                                            500, None)[0]
        except Exception as e:
            print(
                "You are missing lollypop-portal: "
                "https://github.com/gnumdk/lollypop-portal", e)
        if not can_set_cover:
            grid = builder.get_object('grid_behaviour')
            h = grid.child_get_property(switch_artwork_tags, 'height')
            w = grid.child_get_property(switch_artwork_tags, 'width')
            l = grid.child_get_property(switch_artwork_tags, 'left-attach')
            t = grid.child_get_property(switch_artwork_tags, 'top-attach')
            switch_artwork_tags.destroy()
            label = Gtk.Label.new(_("You need to install kid3-cli"))
            label.get_style_context().add_class('dim-label')
            label.set_property('halign', Gtk.Align.END)
            label.show()
            grid.attach(label, l, t, w, h)
        else:
            switch_artwork_tags.set_state(
                Lp().settings.get_value('save-to-tags'))

        if GLib.find_program_in_path("youtube-dl") is None:
            builder.get_object('charts_grid').hide()
        else:
            switch_charts = builder.get_object('switch_charts')
            switch_charts.set_state(Lp().settings.get_value('show-charts'))

        switch_genres = builder.get_object('switch_genres')
        switch_genres.set_state(Lp().settings.get_value('show-genres'))

        switch_compilations = builder.get_object('switch_compilations')
        switch_compilations.set_state(
            Lp().settings.get_value('show-compilations'))

        switch_artwork = builder.get_object('switch_artwork')
        switch_artwork.set_state(Lp().settings.get_value('artist-artwork'))

        switch_spotify = builder.get_object('switch_spotify')
        switch_spotify.set_state(Lp().settings.get_value('search-spotify'))

        switch_itunes = builder.get_object('switch_itunes')
        switch_itunes.set_state(Lp().settings.get_value('search-itunes'))

        if GLib.find_program_in_path("youtube-dl") is None:
            builder.get_object('yt-dl').show()

        combo_orderby = builder.get_object('combo_orderby')
        combo_orderby.set_active(Lp().settings.get_enum(('orderby')))

        combo_preview = builder.get_object('combo_preview')

        scale_coversize = builder.get_object('scale_coversize')
        scale_coversize.set_range(150, 300)
        scale_coversize.set_value(
            Lp().settings.get_value('cover-size').get_int32())
        self.__settings_dialog.connect('destroy', self.__edit_settings_close)

        builder.connect_signals(self)

        main_chooser_box = builder.get_object('main_chooser_box')
        self.__chooser_box = builder.get_object('chooser_box')

        self.__set_outputs(combo_preview)

        #
        # Music tab
        #
        dirs = []
        for directory in Lp().settings.get_value('music-uris'):
            dirs.append(directory)

        # Main chooser
        self.__main_chooser = ChooserWidget()
        image = Gtk.Image.new_from_icon_name("list-add-symbolic",
                                             Gtk.IconSize.MENU)
        self.__main_chooser.set_icon(image)
        self.__main_chooser.set_action(self.__add_chooser)
        main_chooser_box.pack_start(self.__main_chooser, False, True, 0)
        if len(dirs) > 0:
            uri = dirs.pop(0)
        else:
            filename = GLib.get_user_special_dir(
                GLib.UserDirectory.DIRECTORY_MUSIC)
            if filename:
                uri = GLib.filename_to_uri(filename)
            else:
                uri = ""

        self.__main_chooser.set_dir(uri)

        # Others choosers
        for directory in dirs:
            self.__add_chooser(directory)

        #
        # Google tab
        #
        builder.get_object('cs-entry').set_text(
            Lp().settings.get_value('cs-api-key').get_string())
        #
        # Last.fm tab
        #
        if Lp().lastfm is not None and Secret is not None:
            self.__test_img = builder.get_object('test_img')
            self.__login = builder.get_object('login')
            self.__password = builder.get_object('password')
            schema = Secret.Schema.new("org.gnome.Lollypop",
                                       Secret.SchemaFlags.NONE, SecretSchema)
            Secret.password_lookup(schema, SecretAttributes, None,
                                   self.__on_password_lookup)
            builder.get_object('lastfm_grid').set_sensitive(True)
            builder.get_object('lastfm_error').hide()
            self.__login.set_text(
                Lp().settings.get_value('lastfm-login').get_string())

    def show(self):
        """
            Show dialog
        """
        self.__settings_dialog.show()

#######################
# PROTECTED           #
#######################

    def _update_coversize(self, widget):
        """
            Delayed update cover size
            @param widget as Gtk.Range
        """
        if self.__cover_tid is not None:
            GLib.source_remove(self.__cover_tid)
            self.__cover_tid = None
        self.__cover_tid = GLib.timeout_add(500,
                                            self.__really_update_coversize,
                                            widget)

    def _update_ui_setting(self, widget, state):
        """
            Update view setting
            @param widget as Gtk.Switch
            @param state as bool
        """
        Lp().settings.set_value('dark-ui', GLib.Variant('b', state))
        if not Lp().player.is_party:
            settings = Gtk.Settings.get_default()
            settings.set_property("gtk-application-prefer-dark-theme", state)

    def _update_scan_setting(self, widget, state):
        """
            Update scan setting
            @param widget as Gtk.Switch
            @param state as bool
        """
        Lp().settings.set_value('auto-update', GLib.Variant('b', state))

    def _update_background_setting(self, widget, state):
        """
            Update background mode setting
            @param widget as Gtk.Switch
            @param state as bool
        """
        Lp().settings.set_value('background-mode', GLib.Variant('b', state))

    def _update_state_setting(self, widget, state):
        """
            Update save state setting
            @param widget as Gtk.Switch
            @param state as bool
        """
        Lp().settings.set_value('save-state', GLib.Variant('b', state))

    def _update_genres_setting(self, widget, state):
        """
            Update show genre setting
            @param widget as Gtk.Switch
            @param state as bool
        """
        Lp().window.show_genres(state)
        Lp().settings.set_value('show-genres', GLib.Variant('b', state))

    def _update_charts_setting(self, widget, state):
        """
            Update show charts setting
            @param widget as Gtk.Switch
            @param state as bool
        """
        if Lp().settings.get_value('network-access'):
            GLib.idle_add(Lp().window.add_remove_from,
                          (Type.CHARTS, _("The charts"), ""), True, state)
        if state:
            if Lp().charts is None:
                from lollypop.charts import Charts
                Lp().charts = Charts()
            if get_network_available():
                Lp().charts.start()
            elif Lp().notify is not None:
                Lp().notify.send(_("The charts"), _("Network access disabled"))
        else:
            Lp().charts.stop()
            Lp().scanner.clean_charts()
        Lp().settings.set_value('show-charts', GLib.Variant('b', state))

    def _update_mix_setting(self, widget, state):
        """
            Update mix setting
            @param widget as Gtk.Switch
            @param state as bool
        """
        Lp().settings.set_value('mix', GLib.Variant('b', state))
        Lp().player.update_crossfading()
        if state:
            if self.__popover is None:
                self.__popover = Gtk.Popover.new(widget)
                self.__popover.set_modal(False)
                self.__popover.add(self.__popover_content)
            self.__popover.show_all()
        elif self.__popover is not None:
            self.__popover.hide()

    def _update_party_mix_setting(self, widget, state):
        """
            Update party mix setting
            @param widget as Gtk.Range
        """
        Lp().settings.set_value('party-mix', GLib.Variant('b', state))
        Lp().player.update_crossfading()

    def _update_librefm_setting(self, widget, state):
        """
            Update librefm setting
            @param widget as Gtk.Range
        """
        from lollypop.lastfm import LastFM
        Lp().settings.set_value('use-librefm', GLib.Variant('b', state))
        # Reset lastfm object
        Lp().lastfm = LastFM()

    def _update_mix_duration_setting(self, widget):
        """
            Update mix duration setting
            @param widget as Gtk.Range
        """
        value = widget.get_value()
        Lp().settings.set_value('mix-duration', GLib.Variant('i', value))

    def _update_artwork_tags_setting(self, widget, state):
        """
            Update artwork in tags setting
            @param widget as Gtk.Switch
            @param state as bool
        """
        Lp().settings.set_value('save-to-tags', GLib.Variant('b', state))

    def _update_compilations_setting(self, widget, state):
        """
            Update compilations setting
            @param widget as Gtk.Switch
            @param state as bool
        """
        Lp().settings.set_value('show-compilations', GLib.Variant('b', state))

    def _update_artwork_setting(self, widget, state):
        """
            Update artist artwork setting
            @param widget as Gtk.Switch
            @param state as bool
        """
        Lp().settings.set_value('artist-artwork', GLib.Variant('b', state))
        Lp().window.reload_view()
        if state:
            Lp().art.cache_artists_info()

    def _update_orderby_setting(self, widget):
        """
            Update orderby setting
            @param widget as Gtk.ComboBoxText
        """
        Lp().settings.set_enum('orderby', widget.get_active())

    def _update_spotify_setting(self, widget, state):
        """
            Update search on spotify setting
            @param widget as Gtk.Switch
            @param state as bool
        """
        Lp().settings.set_value('search-spotify', GLib.Variant('b', state))

    def _update_itunes_setting(self, widget, state):
        """
            Update search on spotify setting
            @param widget as Gtk.Switch
            @param state as bool
        """
        Lp().settings.set_value('search-itunes', GLib.Variant('b', state))

    def _update_lastfm_settings(self, sync=False):
        """
            Update lastfm settings
            @param sync as bool
        """
        try:
            if Lp().lastfm is not None and Secret is not None:
                schema = Secret.Schema.new("org.gnome.Lollypop",
                                           Secret.SchemaFlags.NONE,
                                           SecretSchema)
                Secret.password_store_sync(
                    schema, SecretAttributes, Secret.COLLECTION_DEFAULT,
                    "org.gnome.Lollypop"
                    ".lastfm.login %s" % self.__login.get_text(),
                    self.__password.get_text(), None)
                Lp().settings.set_value(
                    'lastfm-login', GLib.Variant('s', self.__login.get_text()))
                if sync:
                    Lp().lastfm.connect_sync(self.__password.get_text())
                else:
                    Lp().lastfm.connect(self.__password.get_text())
        except Exception as e:
            print("Settings::_update_lastfm_settings(): %s" % e)

    def _on_cs_api_changed(self, entry):
        """
            Save key
            @param entry as Gtk.Entry
        """
        value = entry.get_text().strip()
        Lp().settings.set_value('cs-api-key', GLib.Variant('s', value))

    def _on_preview_changed(self, combo):
        """
            Update preview setting
            @param combo as Gtk.ComboBoxText
        """
        Lp().settings.set_value('preview-output',
                                GLib.Variant('s', combo.get_active_id()))
        Lp().player.set_preview_output()

    def _on_preview_query_tooltip(self, combo, x, y, keyboard, tooltip):
        """
            Show tooltip if needed
            @param combo as Gtk.ComboBoxText
            @param x as int
            @param y as int
            @param keyboard as bool
            @param tooltip as Gtk.Tooltip
        """
        combo.set_tooltip_text(combo.get_active_text())

    def _on_key_release_event(self, widget, event):
        """
            Destroy window if Esc
            @param widget as Gtk.Widget
            @param event as Gdk.event
        """
        if event.keyval == Gdk.KEY_Escape:
            self.__settings_dialog.destroy()

    def _on_test_btn_clicked(self, button):
        """
            Test lastfm connection
            @param button as Gtk.Button
        """
        self._update_lastfm_settings(True)
        if not get_network_available():
            self.__test_img.set_from_icon_name('computer-fail-symbolic',
                                               Gtk.IconSize.MENU)
            return
        t = Thread(target=self.__test_lastfm_connection)
        t.daemon = True
        t.start()

    def _hide_popover(self, widget):
        """
            Hide popover
            @param widget as Gtk.Widget
        """
        self.__popover.hide()

    def _on_response(self, infobar, response_id):
        """
            Hide infobar
            @param widget as Gtk.Infobar
            @param reponse id as int
        """
        if response_id == Gtk.ResponseType.CLOSE:
            infobar.hide()

    def _on_confirm_button_clicked(self, button):
        """
            Reset database
            @param button as Gtk.Button
        """
        try:
            Lp().player.stop()
            Lp().player.reset_pcn()
            Lp().player.emit('current-changed')
            Lp().player.emit('prev-changed')
            Lp().player.emit('next-changed')
            Lp().cursors = {}
            track_ids = Lp().tracks.get_ids()
            self.__progress.show()
            history = History()
            self.__reset_button.get_toplevel().set_deletable(False)
            self.__reset_button.set_sensitive(False)
            self.__infobar.hide()
            if Lp().charts is not None:
                Lp().charts.stop()
            self.__reset_database(track_ids, len(track_ids), history)
        except Exception as e:
            print("Application::_on_confirm_button_clicked():", e)

    def _on_reset_button_clicked(self, widget):
        """
            Show infobar
            @param widget as Gtk.Widget
        """
        self.__infobar.show()
        # GTK 3.20 https://bugzilla.gnome.org/show_bug.cgi?id=710888
        self.__infobar.queue_resize()


#######################
# PRIVATE             #
#######################

    def __mix_long_func(self, args):
        """
            Show popover
            @param args as []
        """
        if Lp().settings.get_value('mix'):
            if self.__popover is None:
                self.__popover = Gtk.Popover.new(args[0])
                self.__popover.set_modal(False)
                self.__popover.add(self.__popover_content)
            self.__popover.show_all()

    def __mix_short_func(self, args):
        """
            Activate switch
            @param args as []
        """
        args[0].set_active(not args[0].get_active())
        return True

    def __get_pa_outputs(self):
        """
            Get PulseAudio outputs
            @return name/device as [(str, str)]
        """
        ret = []
        try:
            bus = Gio.bus_get_sync(Gio.BusType.SESSION, None)
            proxy = Gio.DBusProxy.new_sync(bus, Gio.DBusProxyFlags.NONE, None,
                                           'org.gnome.Lollypop.Portal',
                                           '/org/gnome/LollypopPortal',
                                           'org.gnome.Lollypop.Portal', None)
            ret = proxy.call_sync('PaListSinks', None,
                                  Gio.DBusCallFlags.NO_AUTO_START, 500, None)
            return ret[0]
        except Exception as e:
            print(
                "You are missing lollypop-portal: "
                "https://github.com/gnumdk/lollypop-portal", e)
        return ret

    def __set_outputs(self, combo):
        """
            Set outputs in combo
            @parma combo as Gtk.ComboxBoxText
        """
        current = Lp().settings.get_value('preview-output').get_string()
        renderer = combo.get_cells()[0]
        renderer.set_property('ellipsize', Pango.EllipsizeMode.END)
        renderer.set_property('max-width-chars', 60)
        outputs = self.__get_pa_outputs()
        if outputs:
            for output in outputs:
                combo.append(output[1], output[0])
                if output[1] == current:
                    combo.set_active_id(output[1])
        else:
            combo.set_sensitive(False)

    def __add_chooser(self, directory=None):
        """
            Add a new chooser widget
            @param directory uri as string
        """
        chooser = ChooserWidget()
        image = Gtk.Image.new_from_icon_name("list-remove-symbolic",
                                             Gtk.IconSize.MENU)
        chooser.set_icon(image)
        if directory:
            chooser.set_dir(directory)
        self.__chooser_box.add(chooser)

    def __really_update_coversize(self, widget):
        """
            Update cover size
            @param widget as Gtk.Range
        """
        self.__cover_tid = None
        value = widget.get_value()
        Lp().settings.set_value('cover-size', GLib.Variant('i', value))
        Lp().art.update_art_size()
        for suffix in ["lastfm", "wikipedia", "spotify"]:
            for artist in Lp().artists.get([]):
                InfoCache.uncache_artwork(artist[1], suffix,
                                          widget.get_scale_factor())
                Lp().art.emit('artist-artwork-changed', artist[1])
        Lp().window.reload_view()

    def __edit_settings_close(self, widget):
        """
            Close edit party dialog
            @param widget as Gtk.Window
        """
        # Music uris
        uris = []
        default = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_MUSIC)
        if default is not None:
            default_uri = GLib.filename_to_uri(default)
        else:
            default_uri = None
        main_uri = self.__main_chooser.get_dir()
        choosers = self.__chooser_box.get_children()
        if main_uri != default_uri or choosers:
            uris.append(main_uri)
            for chooser in choosers:
                uri = chooser.get_dir()
                if uri is not None and uri not in uris:
                    uris.append(uri)

        previous = Lp().settings.get_value('music-uris')
        Lp().settings.set_value('music-uris', GLib.Variant('as', uris))

        # Last.fm
        try:
            if not Lp().lastfm.is_goa:
                self._update_lastfm_settings()
        except:
            pass

        self.__settings_dialog.hide()
        self.__settings_dialog.destroy()
        if set(previous) != set(uris):
            Lp().window.update_db()
        if Lp().window.view is not None:
            Lp().window.view.update_children()

    def __test_lastfm_connection(self):
        """
            Test lastfm connection
            @thread safe
        """
        if Lp().lastfm.session_key:
            GLib.idle_add(self.__test_img.set_from_icon_name,
                          'object-select-symbolic', Gtk.IconSize.MENU)
        else:
            GLib.idle_add(self.__test_img.set_from_icon_name,
                          'computer-fail-symbolic', Gtk.IconSize.MENU)

    def __on_password_lookup(self, source, result):
        """
            Set password entry
            @param source as GObject.Object
            @param result Gio.AsyncResult
        """
        try:
            password = None
            if result is not None:
                password = Secret.password_lookup_finish(result)
            if password is not None:
                self.__password.set_text(password)
        except:
            pass

    def __reset_database(self, track_ids, count, history):
        """
            Backup database and reset
            @param track ids as [int]
            @param count as int
            @param history as History
        """
        if track_ids:
            track_id = track_ids.pop(0)
            uri = Lp().tracks.get_uri(track_id)
            f = Lio.File.new_for_uri(uri)
            name = f.get_basename()
            album_id = Lp().tracks.get_album_id(track_id)
            popularity = Lp().tracks.get_popularity(track_id)
            rate = Lp().tracks.get_rate(track_id)
            ltime = Lp().tracks.get_ltime(track_id)
            mtime = Lp().albums.get_mtime(album_id)
            duration = Lp().tracks.get_duration(track_id)
            loved = Lp().albums.get_loved(album_id)
            album_popularity = Lp().albums.get_popularity(album_id)
            album_rate = Lp().albums.get_rate(album_id)
            history.add(name, duration, popularity, rate, ltime, mtime, loved,
                        album_popularity, album_rate)
            self.__progress.set_fraction((count - len(track_ids)) / count)
            GLib.idle_add(self.__reset_database, track_ids, count, history)
        else:
            self.__progress.hide()
            Lp().player.stop()
            Lp().db.drop_db()
            Lp().db = Database()
            Lp().window.show_genres(Lp().settings.get_value('show-genres'))
            Lp().window.update_db()
            self.__progress.get_toplevel().set_deletable(True)
            if Lp().charts is not None and get_network_available():
                Lp().charts.start()
Example #7
0
class SettingsDialog:
    """
        Dialog showing lollypop options
    """
    def __init__(self):
        """
            Init dialog
        """
        self.__choosers = []
        self.__cover_tid = None
        self.__mix_tid = None
        self.__popover = None

        builder = Gtk.Builder()
        builder.add_from_resource("/org/gnome/Lollypop/SettingsDialog.ui")
        self.__progress = builder.get_object("progress")
        self.__infobar = builder.get_object("infobar")
        self.__reset_button = builder.get_object("reset_button")
        if Lp().lastfm is None or Lp().lastfm.is_goa:
            builder.get_object("lastfm_grid").hide()
        if Lp().scanner.is_locked():
            builder.get_object("reset_button").set_sensitive(False)
        artists = Lp().artists.count()
        albums = Lp().albums.count()
        tracks = Lp().tracks.count()
        builder.get_object("artists").set_text(
            ngettext("%d artist", "%d artists", artists) % artists)
        builder.get_object("albums").set_text(
            ngettext("%d album", "%d albums", albums) % albums)
        builder.get_object("tracks").set_text(
            ngettext("%d track", "%d tracks", tracks) % tracks)

        self.__popover_content = builder.get_object("popover")
        duration = builder.get_object("duration")
        duration.set_range(1, 20)
        duration.set_value(Lp().settings.get_value("mix-duration").get_int32())

        self.__settings_dialog = builder.get_object("settings_dialog")
        self.__settings_dialog.set_transient_for(Lp().window)

        if Lp().settings.get_value("disable-csd"):
            self.__settings_dialog.set_title(_("Preferences"))
        else:
            headerbar = builder.get_object("header_bar")
            headerbar.set_title(_("Preferences"))
            self.__settings_dialog.set_titlebar(headerbar)

        switch_scan = builder.get_object("switch_scan")
        switch_scan.set_state(Lp().settings.get_value("auto-update"))

        switch_view = builder.get_object("switch_dark")
        if Lp().gtk_application_prefer_dark_theme:
            switch_view.set_sensitive(False)
        else:
            switch_view.set_state(Lp().settings.get_value("dark-ui"))

        switch_background = builder.get_object("switch_background")
        switch_background.set_state(Lp().settings.get_value("background-mode"))

        switch_state = builder.get_object("switch_state")
        switch_state.set_state(Lp().settings.get_value("save-state"))

        switch_mix = builder.get_object("switch_mix")
        switch_mix.set_state(Lp().settings.get_value("mix"))

        self._switch_song_notifications = builder.get_object(
            "switch_song_notifications", )

        self._switch_song_notifications.set_state(
            not Lp().settings.get_value("disable-song-notifications"), )

        self._switch_song_notifications.set_sensitive(
            not Lp().settings.get_value("disable-notifications"), )

        Lp().settings.connect(
            "changed::disable-notifications",
            self._on_notifications_setting_changed,
        )

        self.__helper = TouchHelper(switch_mix, None, None)
        self.__helper.set_long_func(self.__mix_long_func, switch_mix)
        self.__helper.set_short_func(self.__mix_short_func, switch_mix)

        switch_mix_party = builder.get_object("switch_mix_party")
        switch_mix_party.set_state(Lp().settings.get_value("party-mix"))

        switch_artwork_tags = builder.get_object("switch_artwork_tags")
        grid_behaviour = builder.get_object("grid_behaviour")
        # Check portal for kid3-cli
        dbus_helper = DBusHelper()
        dbus_helper.call("CanSetCover", None, self.__on_can_set_cover,
                         (switch_artwork_tags, grid_behaviour))

        if GLib.find_program_in_path("youtube-dl") is None or\
                not Lp().settings.get_value("network-access"):
            builder.get_object("charts_grid").hide()
        else:
            switch_charts = builder.get_object("switch_charts")
            switch_charts.set_state(Lp().settings.get_value("show-charts"))

        switch_genres = builder.get_object("switch_genres")
        switch_genres.set_state(Lp().settings.get_value("show-genres"))

        switch_compilations = builder.get_object("switch_compilations")
        switch_compilations.set_state(
            Lp().settings.get_value("show-compilations"))

        switch_artwork = builder.get_object("switch_artwork")
        switch_artwork.set_state(Lp().settings.get_value("artist-artwork"))

        switch_spotify = builder.get_object("switch_spotify")
        switch_spotify.set_state(Lp().settings.get_value("search-spotify"))

        switch_itunes = builder.get_object("switch_itunes")
        switch_itunes.set_state(Lp().settings.get_value("search-itunes"))

        if GLib.find_program_in_path("youtube-dl") is None:
            builder.get_object("yt-dl").show()

        combo_orderby = builder.get_object("combo_orderby")
        combo_orderby.set_active(Lp().settings.get_enum(("orderby")))

        combo_preview = builder.get_object("combo_preview")

        scale_coversize = builder.get_object("scale_coversize")
        scale_coversize.set_range(150, 300)
        scale_coversize.set_value(
            Lp().settings.get_value("cover-size").get_int32())
        self.__settings_dialog.connect("destroy", self.__edit_settings_close)

        builder.connect_signals(self)

        main_chooser_box = builder.get_object("main_chooser_box")
        self.__chooser_box = builder.get_object("chooser_box")

        self.__set_outputs(combo_preview)

        #
        # Music tab
        #
        dirs = []
        for directory in Lp().settings.get_value("music-uris"):
            dirs.append(directory)

        # Main chooser
        self.__main_chooser = ChooserWidget()
        image = Gtk.Image.new_from_icon_name("list-add-symbolic",
                                             Gtk.IconSize.MENU)
        self.__main_chooser.set_icon(image)
        self.__main_chooser.set_action(self.__add_chooser)
        main_chooser_box.pack_start(self.__main_chooser, False, True, 0)
        if len(dirs) > 0:
            uri = dirs.pop(0)
        else:
            filename = GLib.get_user_special_dir(
                GLib.UserDirectory.DIRECTORY_MUSIC)
            if filename:
                uri = GLib.filename_to_uri(filename)
            else:
                uri = ""

        self.__main_chooser.set_dir(uri)

        # Others choosers
        for directory in dirs:
            self.__add_chooser(directory)

        #
        # Google tab
        #
        key = Lp().settings.get_value("cs-api-key").get_string() or\
            Lp().settings.get_default_value("cs-api-key").get_string()
        builder.get_object("cs-entry").set_text(key)

        from lollypop.helper_passwords import PasswordsHelper
        helper = PasswordsHelper()
        #
        # Last.fm tab
        #
        if Lp().lastfm is not None:
            self.__lastfm_test_image = builder.get_object("lastfm_test_image")
            self.__lastfm_login = builder.get_object("lastfm_login")
            self.__lastfm_password = builder.get_object("lastfm_password")
            helper.get("lastfm", self.__on_get_password)
            builder.get_object("lastfm_grid").set_sensitive(True)
            builder.get_object("lastfm_error_label").hide()
        #
        # Libre.fm tab
        #
        if Lp().librefm is not None:
            self.__librefm_test_image = builder.get_object(
                "librefm_test_image")
            self.__librefm_login = builder.get_object("librefm_login")
            self.__librefm_password = builder.get_object("librefm_password")
            helper.get("librefm", self.__on_get_password)
            builder.get_object("librefm_grid").set_sensitive(True)
            builder.get_object("librefm_error_label").hide()

    def show(self):
        """
            Show dialog
        """
        self.__settings_dialog.show()

#######################
# PROTECTED           #
#######################

    def _update_coversize(self, widget):
        """
            Delayed update cover size
            @param widget as Gtk.Range
        """
        if self.__cover_tid is not None:
            GLib.source_remove(self.__cover_tid)
            self.__cover_tid = None
        self.__cover_tid = GLib.timeout_add(500,
                                            self.__really_update_coversize,
                                            widget)

    def _update_ui_setting(self, widget, state):
        """
            Update view setting
            @param widget as Gtk.Switch
            @param state as bool
        """
        Lp().settings.set_value("dark-ui", GLib.Variant("b", state))
        if not Lp().player.is_party:
            settings = Gtk.Settings.get_default()
            settings.set_property("gtk-application-prefer-dark-theme", state)

    def _update_scan_setting(self, widget, state):
        """
            Update scan setting
            @param widget as Gtk.Switch
            @param state as bool
        """
        Lp().settings.set_value("auto-update", GLib.Variant("b", state))

    def _update_background_setting(self, widget, state):
        """
            Update background mode setting
            @param widget as Gtk.Switch
            @param state as bool
        """
        Lp().settings.set_value("background-mode", GLib.Variant("b", state))

    def _update_state_setting(self, widget, state):
        """
            Update save state setting
            @param widget as Gtk.Switch
            @param state as bool
        """
        Lp().settings.set_value("save-state", GLib.Variant("b", state))

    def _update_genres_setting(self, widget, state):
        """
            Update show genre setting
            @param widget as Gtk.Switch
            @param state as bool
        """
        Lp().window.show_genres(state)
        Lp().settings.set_value("show-genres", GLib.Variant("b", state))

    def _update_charts_setting(self, widget, state):
        """
            Update show charts setting
            @param widget as Gtk.Switch
            @param state as bool
        """
        if Lp().settings.get_value("network-access"):
            GLib.idle_add(Lp().window.add_remove_from,
                          (Type.CHARTS, _("The charts"), ""), True, state)
        if state:
            if Lp().charts is None:
                from lollypop.charts import Charts
                Lp().charts = Charts()
            if get_network_available():
                Lp().charts.start()
            elif Lp().notify is not None:
                Lp().notify.send(_("The charts"), _("Network access disabled"))
        else:
            Lp().charts.stop()
            Lp().scanner.clean_charts()
        Lp().settings.set_value("show-charts", GLib.Variant("b", state))

    def _update_mix_setting(self, widget, state):
        """
            Update mix setting
            @param widget as Gtk.Switch
            @param state as bool
        """
        Lp().settings.set_value("mix", GLib.Variant("b", state))
        Lp().player.update_crossfading()
        if state:
            if self.__popover is None:
                self.__popover = Gtk.Popover.new(widget)
                self.__popover.set_modal(False)
                self.__popover.add(self.__popover_content)
            self.__popover.show_all()
        elif self.__popover is not None:
            self.__popover.hide()

    def _update_song_notifications_setting(self, widget, state):
        """
            Update notifications setting
            @param widget as Gtk.Switch
            @param state as bool
        """
        Lp().settings.set_value(
            "disable-song-notifications",
            GLib.Variant("b", not state),
        )

    def _on_notifications_setting_changed(self, *ignore):
        """
            Update switch_song_notifications
        """
        self._switch_song_notifications.set_sensitive(
            not Lp().settings.get_value("disable-notifications"), )

    def _update_party_mix_setting(self, widget, state):
        """
            Update party mix setting
            @param widget as Gtk.Range
        """
        Lp().settings.set_value("party-mix", GLib.Variant("b", state))
        Lp().player.update_crossfading()

    def _update_mix_duration_setting(self, widget):
        """
            Update mix duration setting
            @param widget as Gtk.Range
        """
        value = widget.get_value()
        Lp().settings.set_value("mix-duration", GLib.Variant("i", value))

    def _update_artwork_tags_setting(self, widget, state):
        """
            Update artwork in tags setting
            @param widget as Gtk.Switch
            @param state as bool
        """
        Lp().settings.set_value("save-to-tags", GLib.Variant("b", state))

    def _update_compilations_setting(self, widget, state):
        """
            Update compilations setting
            @param widget as Gtk.Switch
            @param state as bool
        """
        Lp().settings.set_value("show-compilations", GLib.Variant("b", state))

    def _update_artwork_setting(self, widget, state):
        """
            Update artist artwork setting
            @param widget as Gtk.Switch
            @param state as bool
        """
        Lp().settings.set_value("artist-artwork", GLib.Variant("b", state))
        Lp().window.reload_view()
        if state:
            Lp().art.cache_artists_info()

    def _update_orderby_setting(self, widget):
        """
            Update orderby setting
            @param widget as Gtk.ComboBoxText
        """
        Lp().settings.set_enum("orderby", widget.get_active())

    def _update_spotify_setting(self, widget, state):
        """
            Update search on spotify setting
            @param widget as Gtk.Switch
            @param state as bool
        """
        Lp().settings.set_value("search-spotify", GLib.Variant("b", state))

    def _update_itunes_setting(self, widget, state):
        """
            Update search on spotify setting
            @param widget as Gtk.Switch
            @param state as bool
        """
        Lp().settings.set_value("search-itunes", GLib.Variant("b", state))

    def _update_fm_settings(self, name):
        """
            Update lastfm settings
            @param name as str (librefm/lastfm)
        """
        fm = None
        if name == "librefm" and Lp().librefm is not None:
            fm = Lp().librefm
            callback = self.__test_librefm_connection
            login = self.__librefm_login.get_text()
            password = self.__librefm_password.get_text()
        elif Lp().lastfm is not None:
            fm = Lp().lastfm
            callback = self.__test_lastfm_connection
            login = self.__lastfm_login.get_text()
            password = self.__lastfm_password.get_text()
        try:
            if fm is not None and login and password:
                from lollypop.helper_passwords import PasswordsHelper
                helper = PasswordsHelper()
                helper.clear(name, helper.store, name, login, password,
                             self.__on_password_store, fm, False, callback)
        except Exception as e:
            print("Settings::_update_fm_settings(): %s" % e)

    def _on_cs_api_changed(self, entry):
        """
            Save key
            @param entry as Gtk.Entry
        """
        value = entry.get_text().strip()
        Lp().settings.set_value("cs-api-key", GLib.Variant("s", value))

    def _on_preview_changed(self, combo):
        """
            Update preview setting
            @param combo as Gtk.ComboBoxText
        """
        Lp().settings.set_value("preview-output",
                                GLib.Variant("s", combo.get_active_id()))
        Lp().player.set_preview_output()

    def _on_preview_query_tooltip(self, combo, x, y, keyboard, tooltip):
        """
            Show tooltip if needed
            @param combo as Gtk.ComboBoxText
            @param x as int
            @param y as int
            @param keyboard as bool
            @param tooltip as Gtk.Tooltip
        """
        combo.set_tooltip_text(combo.get_active_text())

    def _on_key_release_event(self, widget, event):
        """
            Destroy window if Esc
            @param widget as Gtk.Widget
            @param event as Gdk.event
        """
        if event.keyval == Gdk.KEY_Escape:
            self.__settings_dialog.destroy()

    def _on_lastfm_test_btn_clicked(self, button):
        """
            Test lastfm connection
            @param button as Gtk.Button
        """
        self._update_fm_settings("lastfm")
        if not get_network_available():
            self.__lastfm_test_image.set_from_icon_name(
                "computer-fail-symbolic", Gtk.IconSize.MENU)
            return

    def _on_librefm_test_btn_clicked(self, button):
        """
            Test librefm connection
            @param button as Gtk.Button
        """
        self._update_fm_settings("librefm")
        if not get_network_available():
            self.__librefm_test_image.set_from_icon_name(
                "computer-fail-symbolic", Gtk.IconSize.MENU)
            return

    def _hide_popover(self, widget):
        """
            Hide popover
            @param widget as Gtk.Widget
        """
        self.__popover.hide()

    def _on_response(self, infobar, response_id):
        """
            Hide infobar
            @param widget as Gtk.Infobar
            @param reponse id as int
        """
        if response_id == Gtk.ResponseType.CLOSE:
            infobar.hide()

    def _on_confirm_button_clicked(self, button):
        """
            Reset database
            @param button as Gtk.Button
        """
        try:
            Lp().player.stop()
            Lp().player.reset_pcn()
            Lp().player.emit("current-changed")
            Lp().player.emit("prev-changed")
            Lp().player.emit("next-changed")
            Lp().cursors = {}
            track_ids = Lp().tracks.get_ids()
            self.__progress.show()
            history = History()
            self.__reset_button.get_toplevel().set_deletable(False)
            self.__reset_button.set_sensitive(False)
            self.__infobar.hide()
            if Lp().charts is not None:
                Lp().charts.stop()
            self.__reset_database(track_ids, len(track_ids), history)
        except Exception as e:
            print("Application::_on_confirm_button_clicked():", e)

    def _on_reset_button_clicked(self, widget):
        """
            Show infobar
            @param widget as Gtk.Widget
        """
        self.__infobar.show()
        # GTK 3.20 https://bugzilla.gnome.org/show_bug.cgi?id=710888
        self.__infobar.queue_resize()


#######################
# PRIVATE             #
#######################

    def __mix_long_func(self, args):
        """
            Show popover
            @param args as []
        """
        if Lp().settings.get_value("mix"):
            if self.__popover is None:
                self.__popover = Gtk.Popover.new(args[0])
                self.__popover.set_modal(False)
                self.__popover.add(self.__popover_content)
            self.__popover.show_all()

    def __mix_short_func(self, args):
        """
            Activate switch
            @param args as []
        """
        args[0].set_active(not args[0].get_active())
        return True

    def __set_outputs(self, combo):
        """
            Set outputs in combo
            @parma combo as Gtk.ComboxBoxText
        """
        renderer = combo.get_cells()[0]
        renderer.set_property("ellipsize", Pango.EllipsizeMode.END)
        renderer.set_property("max-width-chars", 60)
        dbus_helper = DBusHelper()
        dbus_helper.call("PaListSinks", None, self.__on_pa_list_sinks, combo)

    def __add_chooser(self, directory=None):
        """
            Add a new chooser widget
            @param directory uri as string
        """
        chooser = ChooserWidget()
        image = Gtk.Image.new_from_icon_name("list-remove-symbolic",
                                             Gtk.IconSize.MENU)
        chooser.set_icon(image)
        if directory:
            chooser.set_dir(directory)
        self.__chooser_box.add(chooser)

    def __really_update_coversize(self, widget):
        """
            Update cover size
            @param widget as Gtk.Range
        """
        self.__cover_tid = None
        value = widget.get_value()
        Lp().settings.set_value("cover-size", GLib.Variant("i", value))
        Lp().art.update_art_size()
        for suffix in ["lastfm", "wikipedia", "spotify"]:
            for artist in Lp().artists.get([]):
                InfoCache.uncache_artwork(artist[1], suffix,
                                          widget.get_scale_factor())
                Lp().art.emit("artist-artwork-changed", artist[1])
        Lp().window.reload_view()

    def __edit_settings_close(self, widget):
        """
            Close edit party dialog
            @param widget as Gtk.Window
        """
        # Music uris
        uris = []
        default = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_MUSIC)
        if default is not None:
            default_uri = GLib.filename_to_uri(default)
        else:
            default_uri = None
        main_uri = self.__main_chooser.get_dir()
        choosers = self.__chooser_box.get_children()
        if main_uri != default_uri or choosers:
            uris.append(main_uri)
            for chooser in choosers:
                uri = chooser.get_dir()
                if uri is not None and uri not in uris:
                    uris.append(uri)

        previous = Lp().settings.get_value("music-uris")
        Lp().settings.set_value("music-uris", GLib.Variant("as", uris))

        self.__settings_dialog.hide()
        self.__settings_dialog.destroy()
        if set(previous) != set(uris):
            Lp().window.update_db()
        if Lp().window.view is not None:
            Lp().window.view.update_children()

    def __test_lastfm_connection(self, result):
        """
            Test lastfm connection
            @param result as None
        """
        if Lp().lastfm.session_key:
            self.__lastfm_test_image.set_from_icon_name(
                "object-select-symbolic", Gtk.IconSize.MENU)
        else:
            self.__lastfm_test_image.set_from_icon_name(
                "computer-fail-symbolic", Gtk.IconSize.MENU)

    def __test_librefm_connection(self, result):
        """
            Test librefm connection
            @param result as None
        """
        if Lp().librefm.session_key:
            self.__librefm_test_image.set_from_icon_name(
                "object-select-symbolic", Gtk.IconSize.MENU)
        else:
            self.__librefm_test_image.set_from_icon_name(
                "computer-fail-symbolic", Gtk.IconSize.MENU)

    def __on_password_store(self, source, result, fm, full_sync, callback):
        """
            Connect service
            @param source as GObject.Object
            @param result as Gio.AsyncResult
            @param fm as LastFM
            @param full_sync as bool
        """
        fm.connect(full_sync, callback)

    def __on_pa_list_sinks(self, source, result, combo):
        """
            Populate combo
            @param source as GObject.Object
            @param result as Gio.AsyncResult
            @param combo as Gtk.ComboBoxText
        """
        current = Lp().settings.get_value("preview-output").get_string()
        try:
            outputs = source.call_finish(result)[0]
        except:
            outputs = []
        if outputs:
            for output in outputs:
                combo.append(output[1], output[0])
                if output[1] == current:
                    combo.set_active_id(output[1])
        else:
            combo.set_sensitive(False)

    def __on_can_set_cover(self, source, result, data):
        """
            Update grid/switch based on result
            @param source as GObject.Object
            @param result as Gio.AsyncResult
            @param data as (Gtk.Switch, Gtk.Grid)
        """
        try:
            can_set_cover = source.call_finish(result)
        except:
            can_set_cover = False
        switch_artwork_tags = data[0]
        if not can_set_cover:
            grid = data[1]
            h = grid.child_get_property(switch_artwork_tags, "height")
            w = grid.child_get_property(switch_artwork_tags, "width")
            l = grid.child_get_property(switch_artwork_tags, "left-attach")
            t = grid.child_get_property(switch_artwork_tags, "top-attach")
            switch_artwork_tags.destroy()
            label = Gtk.Label.new(_("You need to install kid3-cli"))
            label.get_style_context().add_class("dim-label")
            label.set_property("halign", Gtk.Align.END)
            label.show()
            grid.attach(label, l, t, w, h)
        else:
            switch_artwork_tags.set_state(
                Lp().settings.get_value("save-to-tags"))

    def __on_get_password(self, attributes, password, name):
        """
             Set password label
             @param attributes as {}
             @param password as str
             @param name as str
        """
        if attributes is None:
            return
        if name == "librefm":
            self.__librefm_login.set_text(attributes["login"])
            self.__librefm_password.set_text(password)
        else:
            self.__lastfm_login.set_text(attributes["login"])
            self.__lastfm_password.set_text(password)

    def __reset_database(self, track_ids, count, history):
        """
            Backup database and reset
            @param track ids as [int]
            @param count as int
            @param history as History
        """
        if track_ids:
            track_id = track_ids.pop(0)
            uri = Lp().tracks.get_uri(track_id)
            f = Lio.File.new_for_uri(uri)
            name = f.get_basename()
            album_id = Lp().tracks.get_album_id(track_id)
            popularity = Lp().tracks.get_popularity(track_id)
            rate = Lp().tracks.get_rate(track_id)
            ltime = Lp().tracks.get_ltime(track_id)
            mtime = Lp().albums.get_mtime(album_id)
            duration = Lp().tracks.get_duration(track_id)
            loved = Lp().albums.get_loved(album_id)
            album_popularity = Lp().albums.get_popularity(album_id)
            album_rate = Lp().albums.get_rate(album_id)
            history.add(name, duration, popularity, rate, ltime, mtime, loved,
                        album_popularity, album_rate)
            self.__progress.set_fraction((count - len(track_ids)) / count)
            GLib.idle_add(self.__reset_database, track_ids, count, history)
        else:
            self.__progress.hide()
            Lp().player.stop()
            Lp().db.drop_db()
            Lp().db = Database()
            Lp().window.show_genres(Lp().settings.get_value("show-genres"))
            Lp().window.update_db()
            self.__progress.get_toplevel().set_deletable(True)
            if Lp().charts is not None and get_network_available():
                Lp().charts.start()
Example #8
0
class ToolbarInfo(Gtk.Bin, InfoController):
    """
        Informations toolbar
    """

    def __init__(self):
        """
            Init toolbar
        """
        Gtk.Bin.__init__(self)
        InfoController.__init__(self, ArtSize.SMALL)
        builder = Gtk.Builder()
        builder.add_from_resource("/org/gnome/Lollypop/ToolbarInfo.ui")
        builder.connect_signals(self)
        self.__pop_tunein = None
        self.__pop_info = None
        self.__timeout_id = None
        self.__width = 0

        self._infobox = builder.get_object("info")
        self.add(self._infobox)

        self.__helper = TouchHelper(self._infobox, None, None)
        self.__helper.set_long_func(self.__on_info_long)
        self.__helper.set_short_func(self.__on_info_short)

        self._spinner = builder.get_object("spinner")

        self.__labels = builder.get_object("nowplaying_labels")
        self.__labels.connect("query-tooltip", self.__on_query_tooltip)
        self.__labels.set_property("has-tooltip", True)

        self._title_label = builder.get_object("title")
        self._artist_label = builder.get_object("artist")
        self._cover = builder.get_object("cover")
        self._cover.set_property("has-tooltip", True)
        # Since GTK 3.20, we can set cover full height
        if Gtk.get_minor_version() > 18:
            self._cover.get_style_context().add_class("toolbar-cover-frame")
        else:
            self._cover.get_style_context().add_class("small-cover-frame")

        self.connect("realize", self.__on_realize)
        Lp().player.connect("loading-changed", self.__on_loading_changed)
        Lp().art.connect("album-artwork-changed", self.__update_cover)
        Lp().art.connect("radio-artwork-changed", self.__update_logo)

    def do_get_preferred_width(self):
        """
            We force preferred width
            @return (int, int)
        """
        return (self.__width, self.__width)

    def get_preferred_height(self):
        """
            Return preferred height
            @return (int, int)
        """
        return self.__labels.get_preferred_height()

    def set_width(self, width):
        """
            Set widget width
            @param width as int
        """
        self.__width = width
        self.set_property("width-request", width)

#######################
# PROTECTED           #
#######################
    def _on_eventbox_realize(self, eventbox):
        """
            Show hand cursor over
        """
        eventbox.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.HAND2))

#######################
# PRIVATE             #
#######################
    def __update_cover(self, art, album_id):
        """
            Update cover for album_id
            @param art as Art
            @param album id as int
        """
        if Lp().player.current_track.album.id == album_id:
            surface = Lp().art.get_album_artwork(
                                       Lp().player.current_track.album,
                                       self._artsize,
                                       self._cover.get_scale_factor())
            self._cover.set_from_surface(surface)

    def __update_logo(self, art, name):
        """
            Update logo for name
            @param art as Art
            @param name as str
        """
        if Lp().player.current_track.album_artist == name:
            pixbuf = Lp().art.get_radio_artwork(name, self._artsize)
            self._cover.set_from_surface(pixbuf)

    def __on_info_long(self, args):
        """
            Show current track menu
            @param args as []
        """
        if Lp().player.current_track.id >= 0:
            from lollypop.pop_menu import PlaylistsMenu
            from lollypop.pop_menu import TrackMenuPopover
            popover = TrackMenuPopover(
                        Lp().player.current_track,
                        PlaylistsMenu(Lp().player.current_track))
            popover.set_relative_to(self._infobox)
            popover.show()

    def __on_info_short(self, args):
        """
            Show track information popover
            @param args as []
        """
        if Lp().player.current_track.id == Type.EXTERNALS:
            from lollypop.pop_externals import ExternalsPopover
            expopover = ExternalsPopover()
            expopover.set_relative_to(self._infobox)
            expopover.populate()
            expopover.show()
        elif Lp().player.current_track.id is not None:
            if self.__pop_info is None:
                from lollypop.pop_info import InfoPopover
                self.__pop_info = InfoPopover([])
                self.__pop_info.set_relative_to(self._infobox)
            if Lp().player.current_track.id == Type.RADIOS:
                view_type = Type.RADIOS
            else:
                view_type = Type.ALBUMS
            self.__pop_info.set_view_type(view_type)
            self.__pop_info.show()

    def __on_loading_changed(self, player, show):
        """
            Show spinner based on loading status
            @param player as player
            @param show as bool
        """
        if show:
            self._title_label.hide()
            self._artist_label.hide()
            self._cover.hide()
            self._spinner.show()
            self._spinner.start()
            self._infobox.show()
        else:
            self._spinner.hide()
            self._spinner.stop()

    def __on_realize(self, toolbar):
        """
            Calculate art size
            @param toolbar as ToolbarInfos
        """
        style = self.get_style_context()
        padding = style.get_padding(style.get_state())
        self._artsize = self.get_allocated_height()\
            - padding.top - padding.bottom
        # Since GTK 3.20, we can set cover full height
        if Gtk.get_minor_version() < 20:
            self._artsize -= 2

    def __on_query_tooltip(self, widget, x, y, keyboard, tooltip):
        """
            Show tooltip if needed
            @param widget as Gtk.Widget
            @param x as int
            @param y as int
            @param keyboard as bool
            @param tooltip as Gtk.Tooltip
        """
        layout_title = self._title_label.get_layout()
        layout_artist = self._artist_label.get_layout()
        if layout_title.is_ellipsized() or layout_artist.is_ellipsized():
            artist = GLib.markup_escape_text(self._artist_label.get_text())
            title = GLib.markup_escape_text(self._title_label.get_text())
            tooltip.set_markup("<b>%s</b> - %s" % (artist, title))
        else:
            return False
        return True
Example #9
0
class SettingsDialog:
    """
        Dialog showing lollypop options
    """

    def __init__(self):
        """
            Init dialog
        """
        self.__choosers = []
        self.__cover_tid = None
        self.__mix_tid = None
        self.__popover = None

        cs_api_key = Lp().settings.get_value('cs-api-key').get_string()
        default_cs_api_key = Lp().settings.get_default_value(
                                                     'cs-api-key').get_string()
        if (not cs_api_key or
            cs_api_key == default_cs_api_key) and\
                get_network_available() and\
                Lp().notify is not None:
            Lp().notify.send(
                         _("Google Web Services need a custom API key"),
                         _("Lollypop needs this to search artwork and music."))

        builder = Gtk.Builder()
        builder.add_from_resource('/org/gnome/Lollypop/SettingsDialog.ui')
        self.__progress = builder.get_object('progress')
        self.__infobar = builder.get_object('infobar')
        self.__reset_button = builder.get_object('reset_button')
        if Lp().lastfm is None or Lp().lastfm.is_goa:
            builder.get_object('lastfm_grid').hide()
        if Lp().scanner.is_locked():
            builder.get_object('reset_button').set_sensitive(False)
        artists = Lp().artists.count()
        albums = Lp().albums.count()
        tracks = Lp().tracks.count()
        builder.get_object('artists').set_text(
                        ngettext("%d artist", "%d artists", artists) % artists)
        builder.get_object('albums').set_text(
                            ngettext("%d album", "%d albums", albums) % albums)
        builder.get_object('tracks').set_text(
                            ngettext("%d track", "%d tracks", tracks) % tracks)

        self.__popover_content = builder.get_object('popover')
        duration = builder.get_object('duration')
        duration.set_range(1, 20)
        duration.set_value(Lp().settings.get_value('mix-duration').get_int32())

        self.__settings_dialog = builder.get_object('settings_dialog')
        self.__settings_dialog.set_transient_for(Lp().window)

        if Lp().settings.get_value('disable-csd'):
            self.__settings_dialog.set_title(_("Preferences"))
        else:
            headerbar = builder.get_object('header_bar')
            headerbar.set_title(_("Preferences"))
            self.__settings_dialog.set_titlebar(headerbar)

        switch_scan = builder.get_object('switch_scan')
        switch_scan.set_state(Lp().settings.get_value('auto-update'))

        switch_view = builder.get_object('switch_dark')
        switch_view.set_state(Lp().settings.get_value('dark-ui'))

        switch_background = builder.get_object('switch_background')
        switch_background.set_state(Lp().settings.get_value('background-mode'))

        switch_state = builder.get_object('switch_state')
        switch_state.set_state(Lp().settings.get_value('save-state'))

        switch_mix = builder.get_object('switch_mix')
        switch_mix.set_state(Lp().settings.get_value('mix'))
        self.__helper = TouchHelper(switch_mix, None, None)
        self.__helper.set_long_func(self.__mix_long_func, switch_mix)
        self.__helper.set_short_func(self.__mix_short_func, switch_mix)

        switch_mix_party = builder.get_object('switch_mix_party')
        switch_mix_party.set_state(Lp().settings.get_value('party-mix'))

        switch_librefm = builder.get_object('switch_librefm')
        switch_librefm.set_state(Lp().settings.get_value('use-librefm'))

        switch_artwork_tags = builder.get_object('switch_artwork_tags')
        # Check portal for kid3-cli
        can_set_cover = False
        try:
            bus = Gio.bus_get_sync(Gio.BusType.SESSION, None)
            proxy = Gio.DBusProxy.new_sync(bus, Gio.DBusProxyFlags.NONE, None,
                                           'org.gnome.Lollypop.Portal',
                                           '/org/gnome/LollypopPortal',
                                           'org.gnome.Lollypop.Portal', None)
            can_set_cover = proxy.call_sync('CanSetCover', None,
                                            Gio.DBusCallFlags.NO_AUTO_START,
                                            500, None)[0]
        except Exception as e:
            print("You are missing lollypop-portal: "
                  "https://github.com/gnumdk/lollypop-portal", e)
        if not can_set_cover:
            grid = builder.get_object('grid_behaviour')
            h = grid.child_get_property(switch_artwork_tags, 'height')
            w = grid.child_get_property(switch_artwork_tags, 'width')
            l = grid.child_get_property(switch_artwork_tags, 'left-attach')
            t = grid.child_get_property(switch_artwork_tags, 'top-attach')
            switch_artwork_tags.destroy()
            label = Gtk.Label.new(_("You need to install kid3-cli"))
            label.get_style_context().add_class('dim-label')
            label.set_property('halign', Gtk.Align.END)
            label.show()
            grid.attach(label, l, t, w, h)
        else:
            switch_artwork_tags.set_state(
                                      Lp().settings.get_value('save-to-tags'))

        if GLib.find_program_in_path("youtube-dl") is None:
            builder.get_object('charts_grid').hide()
        else:
            switch_charts = builder.get_object('switch_charts')
            switch_charts.set_state(Lp().settings.get_value('show-charts'))

        switch_genres = builder.get_object('switch_genres')
        switch_genres.set_state(Lp().settings.get_value('show-genres'))

        switch_compilations = builder.get_object('switch_compilations')
        switch_compilations.set_state(
            Lp().settings.get_value('show-compilations'))

        switch_artwork = builder.get_object('switch_artwork')
        switch_artwork.set_state(Lp().settings.get_value('artist-artwork'))

        switch_spotify = builder.get_object('switch_spotify')
        switch_spotify.set_state(Lp().settings.get_value('search-spotify'))

        switch_itunes = builder.get_object('switch_itunes')
        switch_itunes.set_state(Lp().settings.get_value('search-itunes'))

        if GLib.find_program_in_path("youtube-dl") is None:
            builder.get_object('yt-dl').show()

        combo_orderby = builder.get_object('combo_orderby')
        combo_orderby.set_active(Lp().settings.get_enum(('orderby')))

        combo_preview = builder.get_object('combo_preview')

        scale_coversize = builder.get_object('scale_coversize')
        scale_coversize.set_range(150, 300)
        scale_coversize.set_value(
                            Lp().settings.get_value('cover-size').get_int32())
        self.__settings_dialog.connect('destroy', self.__edit_settings_close)

        builder.connect_signals(self)

        main_chooser_box = builder.get_object('main_chooser_box')
        self.__chooser_box = builder.get_object('chooser_box')

        self.__set_outputs(combo_preview)

        #
        # Music tab
        #
        dirs = []
        for directory in Lp().settings.get_value('music-uris'):
            dirs.append(directory)

        # Main chooser
        self.__main_chooser = ChooserWidget()
        image = Gtk.Image.new_from_icon_name("list-add-symbolic",
                                             Gtk.IconSize.MENU)
        self.__main_chooser.set_icon(image)
        self.__main_chooser.set_action(self.__add_chooser)
        main_chooser_box.pack_start(self.__main_chooser, False, True, 0)
        if len(dirs) > 0:
            uri = dirs.pop(0)
        else:
            filename = GLib.get_user_special_dir(
                                            GLib.UserDirectory.DIRECTORY_MUSIC)
            if filename:
                uri = GLib.filename_to_uri(filename)
            else:
                uri = ""

        self.__main_chooser.set_dir(uri)

        # Others choosers
        for directory in dirs:
            self.__add_chooser(directory)

        #
        # Google tab
        #
        builder.get_object('cs-entry').set_text(
                            Lp().settings.get_value('cs-api-key').get_string())
        #
        # Last.fm tab
        #
        if Lp().lastfm is not None and Secret is not None:
            self.__test_img = builder.get_object('test_img')
            self.__login = builder.get_object('login')
            self.__password = builder.get_object('password')
            schema = Secret.Schema.new("org.gnome.Lollypop",
                                       Secret.SchemaFlags.NONE,
                                       SecretSchema)
            Secret.password_lookup(schema, SecretAttributes, None,
                                   self.__on_password_lookup)
            builder.get_object('lastfm_grid').set_sensitive(True)
            builder.get_object('lastfm_error').hide()
            self.__login.set_text(
                Lp().settings.get_value('lastfm-login').get_string())

    def show(self):
        """
            Show dialog
        """
        self.__settings_dialog.show()

#######################
# PROTECTED           #
#######################
    def _update_coversize(self, widget):
        """
            Delayed update cover size
            @param widget as Gtk.Range
        """
        if self.__cover_tid is not None:
            GLib.source_remove(self.__cover_tid)
            self.__cover_tid = None
        self.__cover_tid = GLib.timeout_add(500,
                                            self.__really_update_coversize,
                                            widget)

    def _update_ui_setting(self, widget, state):
        """
            Update view setting
            @param widget as Gtk.Switch
            @param state as bool
        """
        Lp().settings.set_value('dark-ui', GLib.Variant('b', state))
        if not Lp().player.is_party:
            settings = Gtk.Settings.get_default()
            settings.set_property("gtk-application-prefer-dark-theme", state)

    def _update_scan_setting(self, widget, state):
        """
            Update scan setting
            @param widget as Gtk.Switch
            @param state as bool
        """
        Lp().settings.set_value('auto-update',
                                GLib.Variant('b', state))

    def _update_background_setting(self, widget, state):
        """
            Update background mode setting
            @param widget as Gtk.Switch
            @param state as bool
        """
        Lp().settings.set_value('background-mode',
                                GLib.Variant('b', state))

    def _update_state_setting(self, widget, state):
        """
            Update save state setting
            @param widget as Gtk.Switch
            @param state as bool
        """
        Lp().settings.set_value('save-state',
                                GLib.Variant('b', state))

    def _update_genres_setting(self, widget, state):
        """
            Update show genre setting
            @param widget as Gtk.Switch
            @param state as bool
        """
        Lp().window.show_genres(state)
        Lp().settings.set_value('show-genres',
                                GLib.Variant('b', state))

    def _update_charts_setting(self, widget, state):
        """
            Update show charts setting
            @param widget as Gtk.Switch
            @param state as bool
        """
        if Lp().settings.get_value('network-access'):
            GLib.idle_add(Lp().window.add_remove_from,
                          (Type.CHARTS, _("The charts"), ""),
                          True,
                          state)
        if state:
            if Lp().charts is None:
                from lollypop.charts import Charts
                Lp().charts = Charts()
            if get_network_available():
                Lp().charts.start()
            elif Lp().notify is not None:
                Lp().notify.send(_("The charts"),
                                 _("Network access disabled"))
        else:
            Lp().charts.stop()
            Lp().scanner.clean_charts()
        Lp().settings.set_value('show-charts',
                                GLib.Variant('b', state))

    def _update_mix_setting(self, widget, state):
        """
            Update mix setting
            @param widget as Gtk.Switch
            @param state as bool
        """
        Lp().settings.set_value('mix', GLib.Variant('b', state))
        Lp().player.update_crossfading()
        if state:
            if self.__popover is None:
                self.__popover = Gtk.Popover.new(widget)
                self.__popover.set_modal(False)
                self.__popover.add(self.__popover_content)
            self.__popover.show_all()
        elif self.__popover is not None:
            self.__popover.hide()

    def _update_party_mix_setting(self, widget, state):
        """
            Update party mix setting
            @param widget as Gtk.Range
        """
        Lp().settings.set_value('party-mix', GLib.Variant('b', state))
        Lp().player.update_crossfading()

    def _update_librefm_setting(self, widget, state):
        """
            Update librefm setting
            @param widget as Gtk.Range
        """
        from lollypop.lastfm import LastFM
        Lp().settings.set_value('use-librefm', GLib.Variant('b', state))
        # Reset lastfm object
        Lp().lastfm = LastFM()

    def _update_mix_duration_setting(self, widget):
        """
            Update mix duration setting
            @param widget as Gtk.Range
        """
        value = widget.get_value()
        Lp().settings.set_value('mix-duration', GLib.Variant('i', value))

    def _update_artwork_tags_setting(self, widget, state):
        """
            Update artwork in tags setting
            @param widget as Gtk.Switch
            @param state as bool
        """
        Lp().settings.set_value('save-to-tags', GLib.Variant('b', state))

    def _update_compilations_setting(self, widget, state):
        """
            Update compilations setting
            @param widget as Gtk.Switch
            @param state as bool
        """
        Lp().settings.set_value('show-compilations',
                                GLib.Variant('b', state))

    def _update_artwork_setting(self, widget, state):
        """
            Update artist artwork setting
            @param widget as Gtk.Switch
            @param state as bool
        """
        Lp().settings.set_value('artist-artwork',
                                GLib.Variant('b', state))
        Lp().window.reload_view()
        if state:
            Lp().art.cache_artists_info()

    def _update_orderby_setting(self, widget):
        """
            Update orderby setting
            @param widget as Gtk.ComboBoxText
        """
        Lp().settings.set_enum('orderby', widget.get_active())

    def _update_spotify_setting(self, widget, state):
        """
            Update search on spotify setting
            @param widget as Gtk.Switch
            @param state as bool
        """
        Lp().settings.set_value('search-spotify', GLib.Variant('b', state))

    def _update_itunes_setting(self, widget, state):
        """
            Update search on spotify setting
            @param widget as Gtk.Switch
            @param state as bool
        """
        Lp().settings.set_value('search-itunes', GLib.Variant('b', state))

    def _update_lastfm_settings(self, sync=False):
        """
            Update lastfm settings
            @param sync as bool
        """
        try:
            if Lp().lastfm is not None and Secret is not None:
                schema = Secret.Schema.new("org.gnome.Lollypop",
                                           Secret.SchemaFlags.NONE,
                                           SecretSchema)
                Secret.password_store_sync(schema, SecretAttributes,
                                           Secret.COLLECTION_DEFAULT,
                                           "org.gnome.Lollypop"
                                           ".lastfm.login %s" %
                                           self.__login.get_text(),
                                           self.__password.get_text(),
                                           None)
                Lp().settings.set_value('lastfm-login',
                                        GLib.Variant('s',
                                                     self.__login.get_text()))
                if sync:
                    Lp().lastfm.connect_sync(self.__password.get_text())
                else:
                    Lp().lastfm.connect(self.__password.get_text())
        except Exception as e:
            print("Settings::_update_lastfm_settings(): %s" % e)

    def _on_cs_api_changed(self, entry):
        """
            Save key
            @param entry as Gtk.Entry
        """
        value = entry.get_text().strip()
        Lp().settings.set_value('cs-api-key', GLib.Variant('s', value))

    def _on_preview_changed(self, combo):
        """
            Update preview setting
            @param combo as Gtk.ComboBoxText
        """
        Lp().settings.set_value('preview-output',
                                GLib.Variant('s', combo.get_active_id()))
        Lp().player.set_preview_output()

    def _on_preview_query_tooltip(self, combo, x, y, keyboard, tooltip):
        """
            Show tooltip if needed
            @param combo as Gtk.ComboBoxText
            @param x as int
            @param y as int
            @param keyboard as bool
            @param tooltip as Gtk.Tooltip
        """
        combo.set_tooltip_text(combo.get_active_text())

    def _on_key_release_event(self, widget, event):
        """
            Destroy window if Esc
            @param widget as Gtk.Widget
            @param event as Gdk.event
        """
        if event.keyval == Gdk.KEY_Escape:
            self.__settings_dialog.destroy()

    def _on_test_btn_clicked(self, button):
        """
            Test lastfm connection
            @param button as Gtk.Button
        """
        self._update_lastfm_settings(True)
        if not get_network_available():
            self.__test_img.set_from_icon_name('computer-fail-symbolic',
                                               Gtk.IconSize.MENU)
            return
        t = Thread(target=self.__test_lastfm_connection)
        t.daemon = True
        t.start()

    def _hide_popover(self, widget):
        """
            Hide popover
            @param widget as Gtk.Widget
        """
        self.__popover.hide()

    def _on_response(self, infobar, response_id):
        """
            Hide infobar
            @param widget as Gtk.Infobar
            @param reponse id as int
        """
        if response_id == Gtk.ResponseType.CLOSE:
            infobar.hide()

    def _on_confirm_button_clicked(self, button):
        """
            Reset database
            @param button as Gtk.Button
        """
        try:
            Lp().player.stop()
            Lp().player.reset_pcn()
            Lp().player.emit('current-changed')
            Lp().player.emit('prev-changed')
            Lp().player.emit('next-changed')
            Lp().cursors = {}
            track_ids = Lp().tracks.get_ids()
            self.__progress.show()
            history = History()
            self.__reset_button.get_toplevel().set_deletable(False)
            self.__reset_button.set_sensitive(False)
            self.__infobar.hide()
            if Lp().charts is not None:
                Lp().charts.stop()
            self.__reset_database(track_ids, len(track_ids), history)
        except Exception as e:
            print("Application::_on_confirm_button_clicked():", e)

    def _on_reset_button_clicked(self, widget):
        """
            Show infobar
            @param widget as Gtk.Widget
        """
        self.__infobar.show()
        # GTK 3.20 https://bugzilla.gnome.org/show_bug.cgi?id=710888
        self.__infobar.queue_resize()

#######################
# PRIVATE             #
#######################
    def __mix_long_func(self, args):
        """
            Show popover
            @param args as []
        """
        if Lp().settings.get_value('mix'):
            if self.__popover is None:
                self.__popover = Gtk.Popover.new(args[0])
                self.__popover.set_modal(False)
                self.__popover.add(self.__popover_content)
            self.__popover.show_all()

    def __mix_short_func(self, args):
        """
            Activate switch
            @param args as []
        """
        args[0].set_active(not args[0].get_active())
        return True

    def __get_pa_outputs(self):
        """
            Get PulseAudio outputs
            @return name/device as [(str, str)]
        """
        ret = []
        try:
            bus = Gio.bus_get_sync(Gio.BusType.SESSION, None)
            proxy = Gio.DBusProxy.new_sync(bus, Gio.DBusProxyFlags.NONE, None,
                                           'org.gnome.Lollypop.Portal',
                                           '/org/gnome/LollypopPortal',
                                           'org.gnome.Lollypop.Portal', None)
            ret = proxy.call_sync('PaListSinks', None,
                                  Gio.DBusCallFlags.NO_AUTO_START,
                                  500, None)
            return ret[0]
        except Exception as e:
            print("You are missing lollypop-portal: "
                  "https://github.com/gnumdk/lollypop-portal", e)
        return ret

    def __set_outputs(self, combo):
        """
            Set outputs in combo
            @parma combo as Gtk.ComboxBoxText
        """
        current = Lp().settings.get_value('preview-output').get_string()
        renderer = combo.get_cells()[0]
        renderer.set_property('ellipsize', Pango.EllipsizeMode.END)
        renderer.set_property('max-width-chars', 60)
        outputs = self.__get_pa_outputs()
        if outputs:
            for output in outputs:
                combo.append(output[1], output[0])
                if output[1] == current:
                    combo.set_active_id(output[1])
        else:
            combo.set_sensitive(False)

    def __add_chooser(self, directory=None):
        """
            Add a new chooser widget
            @param directory uri as string
        """
        chooser = ChooserWidget()
        image = Gtk.Image.new_from_icon_name("list-remove-symbolic",
                                             Gtk.IconSize.MENU)
        chooser.set_icon(image)
        if directory:
            chooser.set_dir(directory)
        self.__chooser_box.add(chooser)

    def __really_update_coversize(self, widget):
        """
            Update cover size
            @param widget as Gtk.Range
        """
        self.__cover_tid = None
        value = widget.get_value()
        Lp().settings.set_value('cover-size', GLib.Variant('i', value))
        Lp().art.update_art_size()
        for suffix in ["lastfm", "wikipedia", "spotify"]:
            for artist in Lp().artists.get([]):
                InfoCache.uncache_artwork(artist[1], suffix,
                                          widget.get_scale_factor())
                Lp().art.emit('artist-artwork-changed', artist[1])
        Lp().window.reload_view()

    def __edit_settings_close(self, widget):
        """
            Close edit party dialog
            @param widget as Gtk.Window
        """
        # Music uris
        uris = []
        default = GLib.get_user_special_dir(
                                            GLib.UserDirectory.DIRECTORY_MUSIC)
        if default is not None:
            default_uri = GLib.filename_to_uri(default)
        else:
            default_uri = None
        main_uri = self.__main_chooser.get_dir()
        choosers = self.__chooser_box.get_children()
        if main_uri != default_uri or choosers:
            uris.append(main_uri)
            for chooser in choosers:
                uri = chooser.get_dir()
                if uri is not None and uri not in uris:
                    uris.append(uri)

        previous = Lp().settings.get_value('music-uris')
        Lp().settings.set_value('music-uris', GLib.Variant('as', uris))

        # Last.fm
        try:
            if not Lp().lastfm.is_goa:
                self._update_lastfm_settings()
        except:
            pass

        self.__settings_dialog.hide()
        self.__settings_dialog.destroy()
        if set(previous) != set(uris):
            Lp().window.update_db()
        if Lp().window.view is not None:
            Lp().window.view.update_children()

    def __test_lastfm_connection(self):
        """
            Test lastfm connection
            @thread safe
        """
        if Lp().lastfm.session_key:
            GLib.idle_add(self.__test_img.set_from_icon_name,
                          'object-select-symbolic',
                          Gtk.IconSize.MENU)
        else:
            GLib.idle_add(self.__test_img.set_from_icon_name,
                          'computer-fail-symbolic',
                          Gtk.IconSize.MENU)

    def __on_password_lookup(self, source, result):
        """
            Set password entry
            @param source as GObject.Object
            @param result Gio.AsyncResult
        """
        try:
            password = None
            if result is not None:
                password = Secret.password_lookup_finish(result)
            if password is not None:
                self.__password.set_text(password)
        except:
            pass

    def __reset_database(self, track_ids, count, history):
        """
            Backup database and reset
            @param track ids as [int]
            @param count as int
            @param history as History
        """
        if track_ids:
            track_id = track_ids.pop(0)
            uri = Lp().tracks.get_uri(track_id)
            f = Lio.File.new_for_uri(uri)
            name = f.get_basename()
            album_id = Lp().tracks.get_album_id(track_id)
            popularity = Lp().tracks.get_popularity(track_id)
            rate = Lp().tracks.get_rate(track_id)
            ltime = Lp().tracks.get_ltime(track_id)
            mtime = Lp().albums.get_mtime(album_id)
            duration = Lp().tracks.get_duration(track_id)
            loved = Lp().albums.get_loved(album_id)
            album_popularity = Lp().albums.get_popularity(album_id)
            album_rate = Lp().albums.get_rate(album_id)
            history.add(name, duration, popularity, rate,
                        ltime, mtime, loved, album_popularity, album_rate)
            self.__progress.set_fraction((count - len(track_ids))/count)
            GLib.idle_add(self.__reset_database, track_ids,
                          count, history)
        else:
            self.__progress.hide()
            Lp().player.stop()
            Lp().db.drop_db()
            Lp().db = Database()
            Lp().window.show_genres(Lp().settings.get_value('show-genres'))
            Lp().window.update_db()
            self.__progress.get_toplevel().set_deletable(True)
            if Lp().charts is not None and get_network_available():
                Lp().charts.start()
Example #10
0
class RadioWidget(Gtk.FlowBoxChild, BaseWidget):
    """
        Widget with radio cover and title
    """
    __gsignals__ = {
        'overlayed': (GObject.SignalFlags.RUN_FIRST, None, (bool, ))
    }

    def __init__(self, name, radios_manager):
        """
            Init radio widget
            @param name as string
            @param radios_manager as RadiosManager
        """
        Gtk.FlowBoxChild.__init__(self)
        BaseWidget.__init__(self)
        self.get_style_context().add_class('loading')
        self.__name = name
        self.__radios_manager = radios_manager

    def populate(self):
        """
            Init widget content
        """
        self.get_style_context().remove_class('loading')
        self._widget = Gtk.EventBox()
        self.__helper = TouchHelper(self._widget, None, None)
        self.__helper.set_long_func(self.__on_click_long)
        self.__helper.set_short_func(self.__on_click_short)
        self._widget.connect('enter-notify-event', self._on_enter_notify)
        self._widget.connect('leave-notify-event', self._on_leave_notify)
        self._cover = Gtk.Image()
        self._cover.set_property('halign', Gtk.Align.CENTER)
        self._cover.set_size_request(ArtSize.BIG, ArtSize.BIG)
        self.__title_label = Gtk.Label()
        self.__title_label.set_ellipsize(Pango.EllipsizeMode.END)
        self.__title_label.set_property('halign', Gtk.Align.CENTER)
        self.__title_label.set_text(self.__name)
        self.__title_label.set_property('has-tooltip', True)
        self.__title_label.connect('query-tooltip', self._on_query_tooltip)
        self._overlay = Gtk.Overlay()
        frame = Gtk.Frame()
        frame.get_style_context().add_class('cover-frame')
        frame.add(self._cover)
        self._overlay.add(frame)
        grid = Gtk.Grid()
        grid.set_orientation(Gtk.Orientation.VERTICAL)
        self._overlay.get_style_context().add_class('white')
        grid.add(self._overlay)
        grid.add(self.__title_label)
        self._widget.add(grid)
        self.set_property('halign', Gtk.Align.CENTER)
        self.set_property('valign', Gtk.Align.CENTER)
        self.add(self._widget)
        self.set_cover()
        self.update_state()
        self.show_all()
        self._lock_overlay = False

    def set_sensitive(self, b):
        """
            Ignore set sensitive
        """
        pass

    @property
    def id(self):
        """
            Return widget id (same value for all radio widgets)
        """
        return Type.RADIOS

    @property
    def filter(self):
        """
            @Return filter as str
        """
        return self.__name

    @property
    def title(self):
        """
            @Return title as str
        """
        return self.__name

    def do_get_preferred_width(self):
        """
            Return preferred width
            @return (int, int)
        """
        # Padding: 3px, border: 1px + spacing
        width = ArtSize.BIG + 16
        return (width, width)

    def set_name(self, name):
        """
            Set radio name
            @param name as string
        """
        self.__name = name
        self.__title_label.set_label(name)

    def set_cover(self):
        """
            Set cover for album if state changed
        """
        if self._cover is None:
            return
        surface = Lp().art.get_radio_artwork(self.__name, ArtSize.BIG,
                                             self._cover.get_scale_factor())
        self._cover.set_from_surface(surface)
        del surface

    def update_cover(self):
        """
            Update cover for radio
        """
        if self._cover is None:
            return
        surface = Lp().art.get_radio_artwork(self.__name, ArtSize.BIG,
                                             self._cover.get_scale_factor())
        self._cover.set_from_surface(surface)
        del surface

    def update_state(self):
        """
            Update widget state
        """
        if self._cover is None:
            return
        selected = Lp().player.current_track.id == Type.RADIOS and\
            self.__name == Lp().player.current_track.album_artists[0]
        if selected:
            self._overlay.get_style_context().add_class('cover-frame-selected')
        else:
            self._overlay.get_style_context().remove_class(
                'cover-frame-selected')

#######################
# PROTECTED           #
#######################

    def _show_overlay_func(self, set):
        """
            Set overlay
            @param set as bool
        """
        if self._lock_overlay or\
           self._show_overlay == set:
            return
        if set:
            # Play button
            self._play_event = Gtk.EventBox()
            self._play_event.set_property('has-tooltip', True)
            self._play_event.set_tooltip_text(_("Play"))
            self._play_event.set_hexpand(True)
            self._play_event.set_property('valign', Gtk.Align.CENTER)
            self._play_event.set_property('halign', Gtk.Align.CENTER)
            self._play_event.connect('realize', self._on_eventbox_realize)
            self._play_event.connect('button-press-event',
                                     self._on_play_press_event)
            self._play_button = Gtk.Image.new_from_icon_name(
                'media-playback-start-symbolic', Gtk.IconSize.DND)
            self._play_button.set_opacity(0)
            # Edit button
            self._artwork_event = Gtk.EventBox()
            self._artwork_event.set_margin_bottom(5)
            self._artwork_event.set_margin_end(5)
            self._artwork_event.set_property('has-tooltip', True)
            self._artwork_event.set_tooltip_text(_("Modify radio"))
            self._artwork_event.set_property('halign', Gtk.Align.END)
            self._artwork_event.connect('realize', self._on_eventbox_realize)
            self._artwork_event.connect('button-press-event',
                                        self._on_edit_press_event)
            self._artwork_event.set_property('valign', Gtk.Align.END)
            self._artwork_event.set_property('halign', Gtk.Align.END)
            self._artwork_button = Gtk.Image.new_from_icon_name(
                'document-properties-symbolic', Gtk.IconSize.BUTTON)
            self._artwork_button.set_opacity(0)
            self._play_event.add(self._play_button)
            self._artwork_event.add(self._artwork_button)
            self._overlay.add_overlay(self._play_event)
            self._overlay.add_overlay(self._artwork_event)
            self._overlay.show_all()
            BaseWidget._show_overlay_func(self, True)
        else:
            BaseWidget._show_overlay_func(self, False)
            self._play_event.destroy()
            self._play_event = None
            self._play_button.destroy()
            self._play_button = None
            self._artwork_event.destroy()
            self._artwork_event = None
            self._artwork_button.destroy()
            self._artwork_button = None

#######################
# PRIVATE             #
#######################

    def __on_click_long(self, args):
        """
            Show overlay
            @param args as []
        """
        self._show_overlay_func(True)

    def __on_click_short(self, args):
        """
            Show overlay
            @param args as []
        """
        self._show_overlay_func(True)

    def _on_query_tooltip(self, widget, x, y, keyboard, tooltip):
        """
            Show tooltip if needed
            @param widget as Gtk.Widget
            @param x as int
            @param y as int
            @param keyboard as bool
            @param tooltip as Gtk.Tooltip
        """
        layout = widget.get_layout()
        if layout.is_ellipsized():
            widget.set_tooltip_text(widget.get_text())
        else:
            widget.set_tooltip_text('')

    def _on_play_press_event(self, widget, event):
        """
            Play radio
            @param: widget as Gtk.EventBox
            @param: event as Gdk.Event
        """
        if Lp().player.locked:
            return True
        url = self.__radios_manager.get_url(self.__name)
        if url:
            track = Track()
            track.set_radio(self.__name, url)
            Lp().player.load(track)

    def _on_edit_press_event(self, widget, event):
        """
            Edit radio
            @param: widget as Gtk.EventBox
            @param: event as Gdk.Event
        """
        popover = RadioPopover(self.__name, self.__radios_manager)
        popover.set_relative_to(widget)
        popover.connect('closed', self._on_pop_cover_closed)
        self._lock_overlay = True
        popover.show()
Example #11
0
class ToolbarEnd(Gtk.Bin):
    """
        Toolbar end
    """
    def __init__(self):
        """
            Init toolbar
        """
        Gtk.Bin.__init__(self)
        self.connect('show', self.__on_show)
        self.connect('hide', self.__on_hide)
        self.set_hexpand(True)
        self.__next_popover = NextPopover()
        self.__search = None
        self.__next_was_inhibited = False
        self.__timeout_id = None
        builder = Gtk.Builder()
        builder.add_from_resource('/org/gnome/Lollypop/ToolbarEnd.ui')
        builder.connect_signals(self)

        self.add(builder.get_object('end'))

        self.__grid_next = builder.get_object('grid-next')

        self.__shuffle_button = builder.get_object('shuffle-button')
        self.__shuffle_image = builder.get_object('shuffle-button-image')
        shuffleAction = Gio.SimpleAction.new('shuffle-button', None)
        shuffleAction.connect('activate', self.__activate_shuffle_button)
        Lp().add_action(shuffleAction)
        Lp().set_accels_for_action("app.shuffle-button", ["<Control>r"])
        Lp().settings.connect('changed::shuffle', self.__on_playback_changed)
        Lp().settings.connect('changed::playback', self.__on_playback_changed)

        self.__party_button = builder.get_object('party-button')
        party_action = Gio.SimpleAction.new('party', None)
        party_action.connect('activate', self.__activate_party_button)
        Lp().add_action(party_action)
        Lp().set_accels_for_action("app.party", ["<Control>p"])

        self.__search_button = builder.get_object('search-button')
        self.__helper = TouchHelper(self.__search_button, "search",
                                    "<Control>f")
        self.__helper.set_long_func(self.__on_search_long)
        self.__helper.set_short_func(self.__on_search_short)

        self.__settings_button = builder.get_object('settings-button')

        self.__list_button = builder.get_object('list-button')
        self.__list_button.set_property('has-tooltip', True)
        self.__list_button.connect('query-tooltip',
                                   self.__on_list_button_query_tooltip)
        self.__list_popover = None
        Lp().player.connect('party-changed', self.__on_party_changed)
        Lp().player.connect('lock-changed', self.__on_lock_changed)

    def setup_menu(self, menu):
        """
            Add an application menu to menu button
            @parma: menu as Gio.Menu
        """
        self.__settings_button.show()
        self.__settings_button.set_menu_model(menu)

    def on_status_changed(self, player):
        """
            Update buttons on status changed
            @param player as Player
        """
        if player.is_playing:
            # Party mode can be activated
            # via Fullscreen class, so check button state
            self.__party_button.set_active(player.is_party)

    def on_next_changed(self, player, force=False):
        """
            Show next popover
            @param player as Player
            @param force to show the popover
        """
        # Do not show popover is this menu is active
        # or if we are hidden
        if self.__shuffle_button.get_active() or\
           not self.__grid_next.is_visible():
            return
        if self.__next_popover.should_be_shown() or force:
            if self.__next_popover.is_visible():
                self.__next_popover.update()
            else:
                self.__next_popover.set_relative_to(self.__grid_next)
                self.__next_popover.show()
        else:
            self.__next_popover.hide()

    def search(self, search):
        """
            Search item
            @param search as str
        """
        self.__on_search_short([])
        self.__search.set_text(search)

#######################
# PROTECTED           #
#######################

    def _on_shuffle_button_toggled(self, button):
        """
            Hide next popover
            @param button as Gtk.Button
        """
        if button.get_active():
            self.__next_popover.hide()
            self.__next_popover.inhibit(True)
        else:
            self.__next_popover.inhibit(False)
            if self.__next_popover.should_be_shown():
                self.__next_popover.show()

    def _on_party_button_toggled(self, button):
        """
            Set party mode on if party button active
            @param obj as Gtk.button
        """
        active = self.__party_button.get_active()
        self.__shuffle_button.set_sensitive(not active)
        if not Lp().settings.get_value('dark-ui'):
            settings = Gtk.Settings.get_default()
            settings.set_property("gtk-application-prefer-dark-theme", active)
        Lp().player.set_party(active)
        self.on_next_changed(Lp().player)

    def _on_party_press_event(self, eventbox, event):
        """
            Show party popover
            @param eventbox as Gtk.EventBox
            @param event as Gdk.Event
        """
        if event.button == 3:
            popover = PartyPopover()
            popover.set_relative_to(eventbox)
            self.__next_popover.hide()
            popover.connect('closed', self.__on_popover_closed)
            self.__next_popover.inhibit(True)
            popover.show()
            return True

    def _on_list_button_clicked(self, widget, unused=None):
        """
            Show current playback context popover
            @param widget as Gtk.Widget
        """
        if self.__list_popover is not None:
            return
        self.__next_was_inhibited = self.__next_popover.inhibited
        self.__next_popover.hide()
        self.__next_popover.inhibit(True)
        if Lp().player.current_track.id == Type.EXTERNALS:
            from lollypop.pop_externals import ExternalsPopover
            self.__list_popover = ExternalsPopover()
            self.__list_popover.set_relative_to(self.__list_button)
            self.__list_popover.populate()
            self.__list_popover.show()
        elif Lp().player.get_queue():
            from lollypop.pop_queue import QueuePopover
            self.__list_popover = QueuePopover()
            self.__list_popover.set_relative_to(self.__list_button)
            self.__list_popover.show()
        elif Lp().player.get_user_playlist_ids():
            from lollypop.pop_playlists import PlaylistsPopover
            self.__list_popover = PlaylistsPopover()
            self.__list_popover.set_relative_to(self.__list_button)
            self.__list_popover.show()
        else:
            from lollypop.pop_albums import AlbumsPopover
            self.__list_popover = AlbumsPopover()
            self.__list_popover.set_relative_to(self.__list_button)
            self.__list_popover.show()
        self.__list_popover.connect('closed', self.__on_list_popover_closed)
        return True

#######################
# PRIVATE             #
#######################

    def __on_search_long(self, args):
        """
            Show current track menu
            @param args as []
        """
        if Lp().window.view is not None:
            Lp().window.view.set_search_mode()

    def __on_search_short(self, args):
        """
            Show search popover
            @param args as []
        """
        self.__next_was_inhibited = self.__next_popover.inhibited
        self.__next_popover.hide()
        self.__next_popover.inhibit(True)
        if self.__search is None:
            from lollypop.pop_search import SearchPopover
            self.__search = SearchPopover()
            self.__search.connect('closed', self.__on_popover_closed)
        self.__search.set_relative_to(self.__search_button)
        self.__search.show()

    def __set_icon(self):
        """
            Set shuffle icon
        """
        shuffle = Lp().settings.get_enum('shuffle')
        repeat = Lp().settings.get_enum('playback')
        if repeat == NextContext.REPEAT_TRACK:
            self.__shuffle_image.get_style_context().remove_class('selected')
            self.__shuffle_image.set_from_icon_name(
                "media-playlist-repeat-song-symbolic",
                Gtk.IconSize.SMALL_TOOLBAR)
        elif shuffle == Shuffle.NONE:
            self.__shuffle_image.get_style_context().remove_class('selected')
            if repeat == NextContext.NONE:
                self.__shuffle_image.set_from_icon_name(
                    "media-playlist-repeat-symbolic",
                    Gtk.IconSize.SMALL_TOOLBAR)
            else:
                self.__shuffle_image.set_from_icon_name(
                    "media-playlist-consecutive-symbolic",
                    Gtk.IconSize.SMALL_TOOLBAR)
        else:
            self.__shuffle_image.set_from_icon_name(
                "media-playlist-shuffle-symbolic", Gtk.IconSize.SMALL_TOOLBAR)
            if shuffle == Shuffle.TRACKS:
                self.__shuffle_image.get_style_context().add_class('selected')
            else:
                self.__shuffle_image.get_style_context().remove_class(
                    'selected')

    def __activate_party_button(self, action=None, param=None):
        """
            Activate party button
            @param action as Gio.SimpleAction
            @param param as GLib.Variant
        """
        self.__party_button.set_active(not self.__party_button.get_active())
        Lp().window.responsive_design()

    def __activate_shuffle_button(self, action=None, param=None):
        """
            Activate shuffle button
            @param action as Gio.SimpleAction
            @param param as GLib.Variant
        """
        self.__shuffle_button.set_active(
            not self.__shuffle_button.get_active())

    def __on_lock_changed(self, player):
        """
            Lock toolbar
            @param player as Player
        """
        self.__party_button.set_sensitive(not player.locked)
        self.__list_button.set_sensitive(not player.locked)
        self.__shuffle_button.set_sensitive(not player.locked)

    def __on_playback_changed(self, settings, value):
        """
            Update shuffle icon
            @param settings as Gio.Settings, value as str
        """
        self.__set_icon()
        self.__next_popover.hide()

    def __on_party_changed(self, player, is_party):
        """
            On party change, sync toolbar
            @param player as Player
            @param is party as bool
        """
        if self.__party_button.get_active() != is_party:
            self.__activate_party_button()

    def __on_list_popover_closed(self, popover):
        """
            Reset variable
            @param popover as Gtk.Popover
        """
        self.__list_popover = None
        self.__on_popover_closed(popover)

    def __on_popover_closed(self, popover):
        """
            Restore next popover if needed
            @param popover as Gtk.Popover
        """
        if not self.__next_was_inhibited:
            self.__next_popover.inhibit(False)
        if self.__next_popover.should_be_shown():
            self.__next_popover.set_relative_to(self.__grid_next)
            self.__next_popover.show()

    def __on_show(self, widget):
        """
            Show popover if needed
            @param widget as Gtk.Widget
        """
        self.__set_icon()

    def __on_hide(self, widget):
        """
            Hide popover
            @param widget as Gtk.Widget
        """
        self.__next_popover.hide()

    def __on_list_button_query_tooltip(self, widget, x, y, keyboard, tooltip):
        """
            Show tooltip
            @param widget as Gtk.Widget
            @param x as int
            @param y as int
            @param keyboard as bool
            @param tooltip as Gtk.Tooltip
        """
        if Lp().player.current_track.id == Type.EXTERNALS:
            widget.set_tooltip_text(_("External tracks"))
        elif Lp().player.get_queue():
            widget.set_tooltip_text(_("Queue"))
        elif Lp().player.get_user_playlist_ids():
            widget.set_tooltip_text(_("Playing playlists"))
        else:
            widget.set_tooltip_text(_("Playing albums"))
Example #12
0
class RadioWidget(Gtk.FlowBoxChild, BaseWidget):
    """
        Widget with radio cover and title
    """
    __gsignals__ = {
        'overlayed': (GObject.SignalFlags.RUN_FIRST, None, (bool,))
    }

    def __init__(self, name, radios_manager):
        """
            Init radio widget
            @param name as string
            @param radios_manager as RadiosManager
        """
        Gtk.FlowBoxChild.__init__(self)
        BaseWidget.__init__(self)
        self.get_style_context().add_class('loading')
        self.__name = name
        self.__radios_manager = radios_manager

    def populate(self):
        """
            Init widget content
        """
        self.get_style_context().remove_class('loading')
        self._widget = Gtk.EventBox()
        self.__helper = TouchHelper(self._widget, None, None)
        self.__helper.set_long_func(self.__on_click_long)
        self.__helper.set_short_func(self.__on_click_short)
        self._widget.connect('enter-notify-event', self._on_enter_notify)
        self._widget.connect('leave-notify-event', self._on_leave_notify)
        self._cover = Gtk.Image()
        self._cover.set_property('halign', Gtk.Align.CENTER)
        self._cover.set_size_request(ArtSize.BIG, ArtSize.BIG)
        self.__title_label = Gtk.Label()
        self.__title_label.set_ellipsize(Pango.EllipsizeMode.END)
        self.__title_label.set_property('halign', Gtk.Align.CENTER)
        self.__title_label.set_text(self.__name)
        self.__title_label.set_property('has-tooltip', True)
        self.__title_label.connect('query-tooltip', self._on_query_tooltip)
        self._overlay = Gtk.Overlay()
        frame = Gtk.Frame()
        frame.get_style_context().add_class('cover-frame')
        frame.add(self._cover)
        self._overlay.add(frame)
        grid = Gtk.Grid()
        grid.set_orientation(Gtk.Orientation.VERTICAL)
        self._overlay.get_style_context().add_class('white')
        grid.add(self._overlay)
        grid.add(self.__title_label)
        self._widget.add(grid)
        self.set_property('halign', Gtk.Align.CENTER)
        self.set_property('valign', Gtk.Align.CENTER)
        self.add(self._widget)
        self.set_cover()
        self.update_state()
        self.show_all()

    def set_sensitive(self, b):
        """
            Ignore set sensitive
        """
        pass

    @property
    def id(self):
        """
            Return widget id (same value for all radio widgets)
        """
        return Type.RADIOS

    @property
    def filter(self):
        """
            @Return filter as str
        """
        return self.__name

    @property
    def title(self):
        """
            @Return title as str
        """
        return self.__name

    def do_get_preferred_width(self):
        """
            Return preferred width
            @return (int, int)
        """
        # Padding: 3px, border: 1px + spacing
        width = ArtSize.BIG + 16
        return (width, width)

    def set_name(self, name):
        """
            Set radio name
            @param name as string
        """
        self.__name = name
        self.__title_label.set_label(name)

    def set_cover(self):
        """
            Set cover for album if state changed
        """
        if self._cover is None:
            return
        surface = Lp().art.get_radio_artwork(
                    self.__name,
                    ArtSize.BIG,
                    self._cover.get_scale_factor())
        self._cover.set_from_surface(surface)
        del surface

    def update_cover(self):
        """
            Update cover for radio
        """
        if self._cover is None:
            return
        surface = Lp().art.get_radio_artwork(
                    self.__name,
                    ArtSize.BIG,
                    self._cover.get_scale_factor())
        self._cover.set_from_surface(surface)
        del surface

    def update_state(self):
        """
            Update widget state
        """
        if self._cover is None:
            return
        selected = Lp().player.current_track.id == Type.RADIOS and\
            self.__name == Lp().player.current_track.album_artists[0]
        if selected:
            self._overlay.get_style_context().add_class(
                                                    'cover-frame-selected')
        else:
            self._overlay.get_style_context().remove_class(
                                                    'cover-frame-selected')

#######################
# PROTECTED           #
#######################
    def _show_overlay_func(self, set):
        """
            Set overlay
            @param set as bool
        """
        if self._lock_overlay or\
           self._show_overlay == set:
            return
        if set:
            # Play button
            self._play_event = Gtk.EventBox()
            self._play_event.set_property('has-tooltip', True)
            self._play_event.set_tooltip_text(_("Play"))
            self._play_event.set_hexpand(True)
            self._play_event.set_property('valign', Gtk.Align.CENTER)
            self._play_event.set_property('halign', Gtk.Align.CENTER)
            self._play_event.connect('realize', self._on_eventbox_realize)
            self._play_event.connect('button-press-event',
                                     self._on_play_press_event)
            self._play_button = Gtk.Image.new_from_icon_name(
                                               'media-playback-start-symbolic',
                                               Gtk.IconSize.DND)
            self._play_button.set_opacity(0)
            # Edit button
            self._artwork_event = Gtk.EventBox()
            self._artwork_event.set_margin_bottom(5)
            self._artwork_event.set_margin_end(5)
            self._artwork_event.set_property('has-tooltip', True)
            self._artwork_event.set_tooltip_text(_("Modify radio"))
            self._artwork_event.set_property('halign', Gtk.Align.END)
            self._artwork_event.connect('realize', self._on_eventbox_realize)
            self._artwork_event.connect('button-press-event',
                                        self._on_edit_press_event)
            self._artwork_event.set_property('valign', Gtk.Align.END)
            self._artwork_event.set_property('halign', Gtk.Align.END)
            self._artwork_button = Gtk.Image.new_from_icon_name(
                                               'document-properties-symbolic',
                                               Gtk.IconSize.BUTTON)
            self._artwork_button.set_opacity(0)
            self._play_event.add(self._play_button)
            self._artwork_event.add(self._artwork_button)
            self._overlay.add_overlay(self._play_event)
            self._overlay.add_overlay(self._artwork_event)
            self._overlay.show_all()
            BaseWidget._show_overlay_func(self, True)
        else:
            BaseWidget._show_overlay_func(self, False)
            self._play_event.destroy()
            self._play_event = None
            self._play_button.destroy()
            self._play_button = None
            self._artwork_event.destroy()
            self._artwork_event = None
            self._artwork_button.destroy()
            self._artwork_button = None

#######################
# PRIVATE             #
#######################
    def __on_click_long(self, args):
        """
            Show overlay
            @param args as []
        """
        self._show_overlay_func(True)

    def __on_click_short(self, args):
        """
            Show overlay
            @param args as []
        """
        self._show_overlay_func(True)

    def _on_query_tooltip(self, widget, x, y, keyboard, tooltip):
        """
            Show tooltip if needed
            @param widget as Gtk.Widget
            @param x as int
            @param y as int
            @param keyboard as bool
            @param tooltip as Gtk.Tooltip
        """
        layout = widget.get_layout()
        if layout.is_ellipsized():
            widget.set_tooltip_text(widget.get_text())
        else:
            widget.set_tooltip_text('')

    def _on_play_press_event(self, widget, event):
        """
            Play radio
            @param: widget as Gtk.EventBox
            @param: event as Gdk.Event
        """
        if Lp().player.locked:
            return True
        url = self.__radios_manager.get_url(self.__name)
        if url:
            track = Track()
            track.set_radio(self.__name, url)
            Lp().player.load(track)

    def _on_edit_press_event(self, widget, event):
        """
            Edit radio
            @param: widget as Gtk.EventBox
            @param: event as Gdk.Event
        """
        popover = RadioPopover(self.__name, self.__radios_manager)
        popover.set_relative_to(widget)
        popover.connect('closed', self._on_pop_cover_closed)
        self._lock_overlay = True
        popover.show()
Example #13
0
    def __init__(self):
        """
            Init dialog
        """
        self.__choosers = []
        self.__cover_tid = None
        self.__mix_tid = None
        self.__popover = None

        cs_api_key = Lp().settings.get_value('cs-api-key').get_string()
        default_cs_api_key = Lp().settings.get_default_value(
                                                     'cs-api-key').get_string()
        if (not cs_api_key or
            cs_api_key == default_cs_api_key) and\
                get_network_available() and\
                Lp().notify is not None:
            Lp().notify.send(
                         _("Google Web Services need a custom API key"),
                         _("Lollypop needs this to search artwork and music."))

        builder = Gtk.Builder()
        builder.add_from_resource('/org/gnome/Lollypop/SettingsDialog.ui')
        if Lp().lastfm is None or Lp().lastfm.is_goa:
            builder.get_object('lastfm_grid').hide()
        if Lp().scanner.is_locked():
            builder.get_object('button').set_sensitive(False)
        builder.get_object('button').connect('clicked',
                                             self.__on_reset_clicked,
                                             builder.get_object('progress'))
        artists = Lp().artists.count()
        albums = Lp().albums.count()
        tracks = Lp().tracks.count()
        builder.get_object('artists').set_text(
                        ngettext("%d artist", "%d artists", artists) % artists)
        builder.get_object('albums').set_text(
                            ngettext("%d album", "%d albums", albums) % albums)
        builder.get_object('tracks').set_text(
                            ngettext("%d track", "%d tracks", tracks) % tracks)

        self.__popover_content = builder.get_object('popover')
        duration = builder.get_object('duration')
        duration.set_range(1, 20)
        duration.set_value(Lp().settings.get_value('mix-duration').get_int32())

        self.__settings_dialog = builder.get_object('settings_dialog')
        self.__settings_dialog.set_transient_for(Lp().window)

        if Lp().settings.get_value('disable-csd'):
            self.__settings_dialog.set_title(_("Preferences"))
        else:
            headerbar = builder.get_object('header_bar')
            headerbar.set_title(_("Preferences"))
            self.__settings_dialog.set_titlebar(headerbar)

        switch_scan = builder.get_object('switch_scan')
        switch_scan.set_state(Lp().settings.get_value('auto-update'))

        switch_view = builder.get_object('switch_dark')
        switch_view.set_state(Lp().settings.get_value('dark-ui'))

        switch_background = builder.get_object('switch_background')
        switch_background.set_state(Lp().settings.get_value('background-mode'))

        switch_state = builder.get_object('switch_state')
        switch_state.set_state(Lp().settings.get_value('save-state'))

        switch_mix = builder.get_object('switch_mix')
        switch_mix.set_state(Lp().settings.get_value('mix'))
        helper = TouchHelper(switch_mix, None, None)
        helper.set_long_func(self.__mix_long_func, switch_mix)
        helper.set_short_func(self.__mix_short_func, switch_mix)

        switch_mix_party = builder.get_object('switch_mix_party')
        switch_mix_party.set_state(Lp().settings.get_value('party-mix'))

        switch_librefm = builder.get_object('switch_librefm')
        switch_librefm.set_state(Lp().settings.get_value('use-librefm'))

        switch_artwork_tags = builder.get_object('switch_artwork_tags')
        # Check portal for kid3-cli
        can_set_cover = False
        try:
            bus = Gio.bus_get_sync(Gio.BusType.SESSION, None)
            proxy = Gio.DBusProxy.new_sync(bus, Gio.DBusProxyFlags.NONE, None,
                                           'org.gnome.Lollypop.Portal',
                                           '/org/gnome/LollypopPortal',
                                           'org.gnome.Lollypop.Portal', None)
            can_set_cover = proxy.call_sync('CanSetCover', None,
                                            Gio.DBusCallFlags.NO_AUTO_START,
                                            500, None)[0]
        except Exception as e:
            print("SettingsDialog::__init__():", e)
        if not can_set_cover:
            grid = builder.get_object('grid_behaviour')
            h = grid.child_get_property(switch_artwork_tags, 'height')
            w = grid.child_get_property(switch_artwork_tags, 'width')
            l = grid.child_get_property(switch_artwork_tags, 'left-attach')
            t = grid.child_get_property(switch_artwork_tags, 'top-attach')
            switch_artwork_tags.destroy()
            label = Gtk.Label.new(_("You need to install kid3-cli"))
            label.get_style_context().add_class('dim-label')
            label.set_property('halign', Gtk.Align.END)
            label.show()
            grid.attach(label, l, t, w, h)
        else:
            switch_artwork_tags.set_state(
                                      Lp().settings.get_value('save-to-tags'))

        if GLib.find_program_in_path("youtube-dl") is None:
            builder.get_object('charts_grid').hide()
        else:
            switch_charts = builder.get_object('switch_charts')
            switch_charts.set_state(Lp().settings.get_value('show-charts'))

        switch_genres = builder.get_object('switch_genres')
        switch_genres.set_state(Lp().settings.get_value('show-genres'))

        switch_compilations = builder.get_object('switch_compilations')
        switch_compilations.set_state(
            Lp().settings.get_value('show-compilations'))

        switch_artwork = builder.get_object('switch_artwork')
        switch_artwork.set_state(Lp().settings.get_value('artist-artwork'))

        combo_orderby = builder.get_object('combo_orderby')
        combo_orderby.set_active(Lp().settings.get_enum(('orderby')))

        combo_preview = builder.get_object('combo_preview')

        scale_coversize = builder.get_object('scale_coversize')
        scale_coversize.set_range(150, 300)
        scale_coversize.set_value(
                            Lp().settings.get_value('cover-size').get_int32())
        self.__settings_dialog.connect('destroy', self.__edit_settings_close)

        builder.connect_signals(self)

        main_chooser_box = builder.get_object('main_chooser_box')
        self.__chooser_box = builder.get_object('chooser_box')

        self.__set_outputs(combo_preview)

        #
        # Music tab
        #
        dirs = []
        for directory in Lp().settings.get_value('music-uris'):
            dirs.append(directory)

        # Main chooser
        self.__main_chooser = ChooserWidget()
        image = Gtk.Image.new_from_icon_name("list-add-symbolic",
                                             Gtk.IconSize.MENU)
        self.__main_chooser.set_icon(image)
        self.__main_chooser.set_action(self.__add_chooser)
        main_chooser_box.pack_start(self.__main_chooser, False, True, 0)
        if len(dirs) > 0:
            uri = dirs.pop(0)
        else:
            filename = GLib.get_user_special_dir(
                                            GLib.UserDirectory.DIRECTORY_MUSIC)
            if filename:
                uri = GLib.filename_to_uri(filename)
            else:
                uri = ""

        self.__main_chooser.set_dir(uri)

        # Others choosers
        for directory in dirs:
            self.__add_chooser(directory)

        #
        # Google tab
        #
        builder.get_object('cs-entry').set_text(
                            Lp().settings.get_value('cs-api-key').get_string())
        #
        # Last.fm tab
        #
        if Lp().lastfm is not None and Secret is not None:
            self.__test_img = builder.get_object('test_img')
            self.__login = builder.get_object('login')
            self.__password = builder.get_object('password')
            schema = Secret.Schema.new("org.gnome.Lollypop",
                                       Secret.SchemaFlags.NONE,
                                       SecretSchema)
            Secret.password_lookup(schema, SecretAttributes, None,
                                   self.__on_password_lookup)
            builder.get_object('lastfm_grid').set_sensitive(True)
            builder.get_object('lastfm_error').hide()
            self.__login.set_text(
                Lp().settings.get_value('lastfm-login').get_string())
Example #14
0
class ToolbarInfo(Gtk.Bin, InfosController):
    """
        Informations toolbar
    """

    def __init__(self):
        """
            Init toolbar
        """
        Gtk.Bin.__init__(self)
        InfosController.__init__(self, ArtSize.SMALL)
        builder = Gtk.Builder()
        builder.add_from_resource('/org/gnome/Lollypop/ToolbarInfo.ui')
        builder.connect_signals(self)
        self.__pop_tunein = None
        self.__pop_info = None
        self.__timeout_id = None
        self.__width = 0

        self._infobox = builder.get_object('info')
        self.add(self._infobox)

        self.__helper = TouchHelper(self._infobox, None, None)
        self.__helper.set_long_func(self.__on_info_long)
        self.__helper.set_short_func(self.__on_info_short)

        self._spinner = builder.get_object('spinner')

        self.__labels = builder.get_object('nowplaying_labels')
        self.__labels.connect('query-tooltip', self.__on_query_tooltip)
        self.__labels.set_property('has-tooltip', True)

        self._title_label = builder.get_object('title')
        self._artist_label = builder.get_object('artist')
        self._cover = builder.get_object('cover')
        self._cover.set_property('has-tooltip', True)
        # Since GTK 3.20, we can set cover full height
        if Gtk.get_minor_version() > 18:
            self._cover.get_style_context().add_class('toolbar-cover-frame')
        else:
            self._cover.get_style_context().add_class('small-cover-frame')

        self.connect('realize', self.__on_realize)
        Lp().player.connect('loading-changed', self.__on_loading_changed)
        Lp().art.connect('album-artwork-changed', self.__update_cover)
        Lp().art.connect('radio-artwork-changed', self.__update_logo)

    def do_get_preferred_width(self):
        """
            We force preferred width
            @return (int, int)
        """
        return (self.__width, self.__width)

    def get_preferred_height(self):
        """
            Return preferred height
            @return (int, int)
        """
        return self.__labels.get_preferred_height()

    def set_width(self, width):
        """
            Set widget width
            @param width as int
        """
        self.__width = width
        self.set_property('width-request', width)

#######################
# PROTECTED           #
#######################
    def _on_eventbox_realize(self, eventbox):
        """
            Show hand cursor over
        """
        eventbox.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.HAND2))

#######################
# PRIVATE             #
#######################
    def __update_cover(self, art, album_id):
        """
            Update cover for album_id
            @param art as Art
            @param album id as int
        """
        if Lp().player.current_track.album.id == album_id:
            surface = Lp().art.get_album_artwork(
                                       Lp().player.current_track.album,
                                       self._artsize,
                                       self._cover.get_scale_factor())
            self._cover.set_from_surface(surface)
            del surface

    def __update_logo(self, art, name):
        """
            Update logo for name
            @param art as Art
            @param name as str
        """
        if Lp().player.current_track.album_artist == name:
            pixbuf = Lp().art.get_radio_artwork(name, self._artsize)
            self._cover.set_from_surface(pixbuf)
            del pixbuf

    def __on_info_long(self, args):
        """
            Show current track menu
            @param args as []
        """
        if Lp().player.current_track.id >= 0:
            from lollypop.pop_menu import PlaylistsMenu
            from lollypop.pop_menu import TrackMenuPopover
            popover = TrackMenuPopover(
                        Lp().player.current_track,
                        PlaylistsMenu(Lp().player.current_track))
            popover.set_relative_to(self._infobox)
            popover.show()

    def __on_info_short(self, args):
        """
            Show track information popover
            @param args as []
        """
        if Lp().player.current_track.id == Type.EXTERNALS:
            from lollypop.pop_externals import ExternalsPopover
            expopover = ExternalsPopover()
            expopover.set_relative_to(self._infobox)
            expopover.populate()
            expopover.show()
        elif Lp().player.current_track.id is not None:
            if Lp().player.current_track.id == Type.RADIOS:
                if self.__pop_tunein is None:
                    from lollypop.pop_tunein import TuneinPopover
                    self.__pop_tunein = TuneinPopover()
                    self.__pop_tunein.populate()
                    self.__pop_tunein.set_relative_to(self._infobox)
                self.__pop_tunein.show()
            else:
                if self.__pop_info is None:
                    from lollypop.pop_info import InfoPopover
                    self.__pop_info = InfoPopover()
                    self.__pop_info.set_relative_to(self._infobox)
                self.__pop_info.show()

    def __on_loading_changed(self, player, show):
        """
            Show spinner based on loading status
            @param player as player
            @param show as bool
        """
        if show:
            self._title_label.hide()
            self._artist_label.hide()
            self._cover.hide()
            self._spinner.show()
            self._spinner.start()
            self._infobox.show()
        else:
            self._spinner.hide()
            self._spinner.stop()

    def __on_realize(self, toolbar):
        """
            Calculate art size
            @param toolbar as ToolbarInfos
        """
        style = self.get_style_context()
        padding = style.get_padding(style.get_state())
        self._artsize = self.get_allocated_height()\
            - padding.top - padding.bottom
        # Since GTK 3.20, we can set cover full height
        if Gtk.get_minor_version() < 20:
            self._artsize -= 2

    def __on_query_tooltip(self, widget, x, y, keyboard, tooltip):
        """
            Show tooltip if needed
            @param widget as Gtk.Widget
            @param x as int
            @param y as int
            @param keyboard as bool
            @param tooltip as Gtk.Tooltip
        """
        layout_title = self._title_label.get_layout()
        layout_artist = self._artist_label.get_layout()
        if layout_title.is_ellipsized() or layout_artist.is_ellipsized():
            artist = GLib.markup_escape_text(self._artist_label.get_text())
            title = GLib.markup_escape_text(self._title_label.get_text())
            tooltip.set_markup("<b>%s</b> - %s" % (artist, title))
        else:
            return False
        return True
Example #15
0
    def __init__(self):
        """
            Init dialog
        """
        self.__choosers = []
        self.__cover_tid = None
        self.__mix_tid = None
        self.__popover = None

        cs_api_key = Lp().settings.get_value('cs-api-key').get_string()
        default_cs_api_key = Lp().settings.get_default_value(
                                                     'cs-api-key').get_string()
        if (not cs_api_key or
            cs_api_key == default_cs_api_key) and\
                get_network_available() and\
                Lp().notify is not None:
            Lp().notify.send(
                         _("Google Web Services need a custom API key"),
                         _("Lollypop needs this to search artwork and music."))

        builder = Gtk.Builder()
        builder.add_from_resource('/org/gnome/Lollypop/SettingsDialog.ui')
        if Lp().lastfm is None or Lp().lastfm.is_goa:
            builder.get_object('lastfm_grid').hide()
        if Lp().scanner.is_locked():
            builder.get_object('button').set_sensitive(False)
        builder.get_object('button').connect('clicked',
                                             self.__on_reset_clicked,
                                             builder.get_object('progress'))
        artists = Lp().artists.count()
        albums = Lp().albums.count()
        tracks = Lp().tracks.count()
        builder.get_object('artists').set_text(
                        ngettext("%d artist", "%d artists", artists) % artists)
        builder.get_object('albums').set_text(
                            ngettext("%d album", "%d albums", albums) % albums)
        builder.get_object('tracks').set_text(
                            ngettext("%d track", "%d tracks", tracks) % tracks)

        self.__popover_content = builder.get_object('popover')
        duration = builder.get_object('duration')
        duration.set_range(1, 20)
        duration.set_value(Lp().settings.get_value('mix-duration').get_int32())

        self.__settings_dialog = builder.get_object('settings_dialog')
        self.__settings_dialog.set_transient_for(Lp().window)

        if Lp().settings.get_value('disable-csd'):
            self.__settings_dialog.set_title(_("Preferences"))
        else:
            headerbar = builder.get_object('header_bar')
            headerbar.set_title(_("Preferences"))
            self.__settings_dialog.set_titlebar(headerbar)

        switch_scan = builder.get_object('switch_scan')
        switch_scan.set_state(Lp().settings.get_value('auto-update'))

        switch_view = builder.get_object('switch_dark')
        switch_view.set_state(Lp().settings.get_value('dark-ui'))

        switch_background = builder.get_object('switch_background')
        switch_background.set_state(Lp().settings.get_value('background-mode'))

        switch_state = builder.get_object('switch_state')
        switch_state.set_state(Lp().settings.get_value('save-state'))

        switch_mix = builder.get_object('switch_mix')
        switch_mix.set_state(Lp().settings.get_value('mix'))
        helper = TouchHelper(switch_mix, None, None)
        helper.set_long_func(self.__mix_long_func, switch_mix)
        helper.set_short_func(self.__mix_short_func, switch_mix)

        switch_mix_party = builder.get_object('switch_mix_party')
        switch_mix_party.set_state(Lp().settings.get_value('party-mix'))

        switch_librefm = builder.get_object('switch_librefm')
        switch_librefm.set_state(Lp().settings.get_value('use-librefm'))

        switch_artwork_tags = builder.get_object('switch_artwork_tags')
        # Check portal for kid3-cli
        can_set_cover = False
        try:
            bus = Gio.bus_get_sync(Gio.BusType.SESSION, None)
            proxy = Gio.DBusProxy.new_sync(bus, Gio.DBusProxyFlags.NONE, None,
                                           'org.gnome.Lollypop.Portal',
                                           '/org/gnome/LollypopPortal',
                                           'org.gnome.Lollypop.Portal', None)
            can_set_cover = proxy.call_sync('CanSetCover', None,
                                            Gio.DBusCallFlags.NO_AUTO_START,
                                            500, None)[0]
        except Exception as e:
            print("You are missing lollypop-portal: "
                  "https://github.com/gnumdk/lollypop-portal", e)
        if not can_set_cover:
            grid = builder.get_object('grid_behaviour')
            h = grid.child_get_property(switch_artwork_tags, 'height')
            w = grid.child_get_property(switch_artwork_tags, 'width')
            l = grid.child_get_property(switch_artwork_tags, 'left-attach')
            t = grid.child_get_property(switch_artwork_tags, 'top-attach')
            switch_artwork_tags.destroy()
            label = Gtk.Label.new(_("You need to install kid3-cli"))
            label.get_style_context().add_class('dim-label')
            label.set_property('halign', Gtk.Align.END)
            label.show()
            grid.attach(label, l, t, w, h)
        else:
            switch_artwork_tags.set_state(
                                      Lp().settings.get_value('save-to-tags'))

        if GLib.find_program_in_path("youtube-dl") is None:
            builder.get_object('charts_grid').hide()
        else:
            switch_charts = builder.get_object('switch_charts')
            switch_charts.set_state(Lp().settings.get_value('show-charts'))

        switch_genres = builder.get_object('switch_genres')
        switch_genres.set_state(Lp().settings.get_value('show-genres'))

        switch_compilations = builder.get_object('switch_compilations')
        switch_compilations.set_state(
            Lp().settings.get_value('show-compilations'))

        switch_artwork = builder.get_object('switch_artwork')
        switch_artwork.set_state(Lp().settings.get_value('artist-artwork'))

        combo_orderby = builder.get_object('combo_orderby')
        combo_orderby.set_active(Lp().settings.get_enum(('orderby')))

        combo_preview = builder.get_object('combo_preview')

        scale_coversize = builder.get_object('scale_coversize')
        scale_coversize.set_range(150, 300)
        scale_coversize.set_value(
                            Lp().settings.get_value('cover-size').get_int32())
        self.__settings_dialog.connect('destroy', self.__edit_settings_close)

        builder.connect_signals(self)

        main_chooser_box = builder.get_object('main_chooser_box')
        self.__chooser_box = builder.get_object('chooser_box')

        self.__set_outputs(combo_preview)

        #
        # Music tab
        #
        dirs = []
        for directory in Lp().settings.get_value('music-uris'):
            dirs.append(directory)

        # Main chooser
        self.__main_chooser = ChooserWidget()
        image = Gtk.Image.new_from_icon_name("list-add-symbolic",
                                             Gtk.IconSize.MENU)
        self.__main_chooser.set_icon(image)
        self.__main_chooser.set_action(self.__add_chooser)
        main_chooser_box.pack_start(self.__main_chooser, False, True, 0)
        if len(dirs) > 0:
            uri = dirs.pop(0)
        else:
            filename = GLib.get_user_special_dir(
                                            GLib.UserDirectory.DIRECTORY_MUSIC)
            if filename:
                uri = GLib.filename_to_uri(filename)
            else:
                uri = ""

        self.__main_chooser.set_dir(uri)

        # Others choosers
        for directory in dirs:
            self.__add_chooser(directory)

        #
        # Google tab
        #
        builder.get_object('cs-entry').set_text(
                            Lp().settings.get_value('cs-api-key').get_string())
        #
        # Last.fm tab
        #
        if Lp().lastfm is not None and Secret is not None:
            self.__test_img = builder.get_object('test_img')
            self.__login = builder.get_object('login')
            self.__password = builder.get_object('password')
            schema = Secret.Schema.new("org.gnome.Lollypop",
                                       Secret.SchemaFlags.NONE,
                                       SecretSchema)
            Secret.password_lookup(schema, SecretAttributes, None,
                                   self.__on_password_lookup)
            builder.get_object('lastfm_grid').set_sensitive(True)
            builder.get_object('lastfm_error').hide()
            self.__login.set_text(
                Lp().settings.get_value('lastfm-login').get_string())