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 __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 __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() 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 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()
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())
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()
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()
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()
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
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())
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()
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()
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"))
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()
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