class Window(Gtk.ApplicationWindow): """ Init window objects """ def __init__(self, app): Gtk.ApplicationWindow.__init__(self, application=app, title=_("Lollypop")) self._timeout = None self._scanner = CollectionScanner() self._scanner.connect("scan-finished", self._setup_list_one, True) self._setup_window() self._setup_view() self._setup_media_keys() party_settings = Objects["settings"].get_value('party-ids') ids = [] for setting in party_settings: if isinstance(setting, int): ids.append(setting) Objects["player"].set_party_ids(ids) self.connect("destroy", self._on_destroyed_window) """ Run collection update if needed """ def setup_view(self): if Objects["tracks"].is_empty(): self._scanner.update(self._progress, False) return elif Objects["settings"].get_value('startup-scan'): self._scanner.update(self._progress, True) self._setup_list_one() self._update_view_albums(POPULARS) """ Update music database """ def update_db(self): self._list_one.widget.hide() self._list_two.widget.hide() old_view = self._stack.get_visible_child() self._loading = True view = LoadingView() self._stack.add(view) self._stack.set_visible_child(view) self._scanner.update(self._progress, False) if old_view: self._stack.remove(old_view) old_view.remove_signals() """ Update view class @param bool """ def update_view_class(self, dark): current_view = self._stack.get_visible_child() if dark: current_view.get_style_context().add_class('black') else: current_view.get_style_context().remove_class('black') ############ # Private # ############ """ Setup media player keys """ def _setup_media_keys(self): self._proxy = Gio.DBusProxy.new_sync(Gio.bus_get_sync(Gio.BusType.SESSION, None), Gio.DBusProxyFlags.NONE, None, 'org.gnome.SettingsDaemon', '/org/gnome/SettingsDaemon/MediaKeys', 'org.gnome.SettingsDaemon.MediaKeys', None) self._grab_media_player_keys() try: self._proxy.connect('g-signal', self._handle_media_keys) except GLib.GError: # We cannot grab media keys if no settings daemon is running pass """ Do key grabbing """ def _grab_media_player_keys(self): try: self._proxy.call_sync('GrabMediaPlayerKeys', GLib.Variant('(su)', ('Lollypop', 0)), Gio.DBusCallFlags.NONE, -1, None) except GLib.GError: # We cannot grab media keys if no settings daemon is running pass """ Do player actions in response to media key pressed """ def _handle_media_keys(self, proxy, sender, signal, parameters): if signal != 'MediaPlayerKeyPressed': print('Received an unexpected signal \'%s\' from media player'.format(signal)) return response = parameters.get_child_value(1).get_string() if 'Play' in response: Objects["player"].play_pause() elif 'Stop' in response: Objects["player"].stop() elif 'Next' in response: Objects["player"].next() elif 'Previous' in response: Objects["player"].prev() """ Setup window icon, position and size, callback for updating this values """ def _setup_window(self): self.set_icon_name('lollypop') size_setting = Objects["settings"].get_value('window-size') if isinstance(size_setting[0], int) and isinstance(size_setting[1], int): self.resize(size_setting[0], size_setting[1]) else: self.set_size_request(800, 600) position_setting = Objects["settings"].get_value('window-position') if len(position_setting) == 2 \ and isinstance(position_setting[0], int) \ and isinstance(position_setting[1], int): self.move(position_setting[0], position_setting[1]) if Objects["settings"].get_value('window-maximized'): self.maximize() self.connect("window-state-event", self._on_window_state_event) self.connect("configure-event", self._on_configure_event) """ Setup window main view: - genre list - artist list - main view as artist view or album view """ def _setup_view(self): self._paned_main_list = Gtk.HPaned() self._paned_list_view = Gtk.HPaned() vgrid = Gtk.Grid() vgrid.set_orientation(Gtk.Orientation.VERTICAL) self._toolbar = Toolbar() self._toolbar.header_bar.show() self._toolbar.get_view_genres_btn().connect("toggled", self._setup_list_one) self._list_one = SelectionList("Genre") self._list_two = SelectionList("Artist") self._list_one_signal = None self._list_two_signal = None self._loading = True loading_view = LoadingView() self._stack = Gtk.Stack() self._stack.add(loading_view) self._stack.set_visible_child(loading_view) self._stack.set_transition_duration(500) self._stack.set_transition_type(Gtk.StackTransitionType.CROSSFADE) self._stack.show() self._progress = Gtk.ProgressBar() vgrid.add(self._stack) vgrid.add(self._progress) vgrid.show() DESKTOP = environ.get("XDG_CURRENT_DESKTOP") if DESKTOP and ("GNOME" in DESKTOP or "Pantheon" in DESKTOP): self.set_titlebar(self._toolbar.header_bar) self._toolbar.header_bar.set_show_close_button(True) self.add(self._paned_main_list) else: hgrid = Gtk.Grid() hgrid.set_orientation(Gtk.Orientation.VERTICAL) hgrid.add(self._toolbar.header_bar) hgrid.add(self._paned_main_list) hgrid.show() self.add(hgrid) separator = Gtk.Separator() separator.show() self._paned_list_view.add1(self._list_two.widget) self._paned_list_view.add2(vgrid) self._paned_main_list.add1(self._list_one.widget) self._paned_main_list.add2(self._paned_list_view) self._paned_main_list.set_position(Objects["settings"].get_value("paned-mainlist-width").get_int32()) self._paned_list_view.set_position(Objects["settings"].get_value("paned-listview-width").get_int32()) self._paned_main_list.show() self._paned_list_view.show() self.show() """ Init the filter list @param widget as unused """ def _init_main_list(self, widget): if self._list_one.widget.is_visible(): self._update_genres() else: self._init_genres() """ Init list with genres or artist If update, only update list content @param obj as unused, bool """ def _setup_list_one(self, obj = None, update = None): if self._list_one_signal: self._list_one.disconnect(self._list_one_signal) active = self._toolbar.get_view_genres_btn().get_active() if active: items = Objects["genres"].get_ids() else: self._list_two.widget.hide() items = Objects["artists"].get_ids(ALL) if len(Objects["albums"].get_compilations(ALL)) > 0: items.insert(0, (COMPILATIONS, _("Compilations"))) items.insert(0, (ALL, _("All artists"))) items.insert(0, (POPULARS, _("Popular albums"))) if update: self._list_one.update(items, not active) else: self._list_one.populate(items, not active) # If was empty if not self._list_one_signal: self._list_one.select_first() if self._loading: self._stack.get_visible_child().hide() self._list_one.select_first() self._update_view_albums(POPULARS) self._loading = False self._list_one.widget.show() if active: self._list_one_signal = self._list_one.connect('item-selected', self._setup_list_two) else: self._list_one_signal = self._list_one.connect('item-selected', self._update_view_artists, None) """ Init list two with artist based on genre @param obj as unused, genre id as int """ def _setup_list_two(self, obj, genre_id): if self._list_two_signal: self._list_two.disconnect(self._list_two_signal) if genre_id == POPULARS: self._list_two.widget.hide() self._list_two_signal = None else: values = Objects["artists"].get_ids(genre_id) if len(Objects["albums"].get_compilations(genre_id)) > 0: values.insert(0, (COMPILATIONS, _("Compilations"))) self._list_two.populate(values, True) self._list_two.widget.show() self._list_two_signal = self._list_two.connect('item-selected', self._update_view_artists, genre_id) self._update_view_albums(genre_id) """ Update artist view @param artist id as int, genre id as int """ def _update_view_artists(self, obj, artist_id, genre_id): if artist_id == ALL or artist_id == POPULARS: self._update_view_albums(artist_id) else: old_view = self._stack.get_visible_child() view = ArtistView(artist_id, genre_id, False) self._stack.add(view) start_new_thread(view.populate, ()) self._stack.set_visible_child(view) if old_view: self._stack.remove(old_view) old_view.remove_signals() """ Update albums view @param genre id as int """ def _update_view_albums(self, genre_id): old_view = self._stack.get_visible_child() view = AlbumView(genre_id) self._stack.add(view) start_new_thread(view.populate, ()) self._stack.set_visible_child(view) if old_view: self._stack.remove(old_view) old_view.remove_signals() """ Delay event @param: widget as Gtk.Window @param: event as Gtk.Event """ def _on_configure_event(self, widget, event): if self._timeout: GLib.source_remove(self._timeout) self._timeout = GLib.timeout_add(500, self._save_size_position, widget) """ Save window state, update current view content size @param: widget as Gtk.Window """ def _save_size_position(self, widget): self._timeout = None size = widget.get_size() Objects["settings"].set_value('window-size', GLib.Variant('ai', [size[0], size[1]])) position = widget.get_position() Objects["settings"].set_value('window-position', GLib.Variant('ai', [position[0], position[1]])) """ Save maximised state """ def _on_window_state_event(self, widget, event): Objects["settings"].set_boolean('window-maximized', 'GDK_WINDOW_STATE_MAXIMIZED' in event.new_window_state.value_names) """ Save paned widget width @param widget as unused, data as unused """ def _on_destroyed_window(self, widget): Objects["settings"].set_value("paned-mainlist-width", GLib.Variant('i', self._paned_main_list.get_position())) Objects["settings"].set_value("paned-listview-width", GLib.Variant('i', self._paned_list_view.get_position()))
class Window(Gtk.ApplicationWindow): """ Init window objects """ def __init__(self, app, db, player): Gtk.ApplicationWindow.__init__(self, application=app, title=_("Lollypop")) self._db = db self._player = player self._scanner = CollectionScanner() self._settings = Gio.Settings.new('org.gnome.Lollypop') self._artist_signal_id = 0 self._setup_window() self._setup_view() self._setup_media_keys() party_settings = self._settings.get_value('party-ids') ids = [] for setting in party_settings: if isinstance(setting, int): ids.append(setting) self._player.set_party_ids(ids) self.connect("map-event", self._on_mapped_window) def edit_party(self): builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/PartyDialog.ui') self._party_dialog = builder.get_object('party_dialog') self._party_dialog.set_transient_for(self) self._party_dialog.set_title(_("Select what will be available in party mode")) party_button = builder.get_object('button1') party_button.connect("clicked", self._edit_party_close) scrolled = builder.get_object('scrolledwindow1') genres = self._db.get_all_genres() genres.insert(0, (-1, "Populars")) self._party_grid = Gtk.Grid() self._party_grid.set_orientation(Gtk.Orientation.VERTICAL) self._party_grid.set_property("column-spacing", 10) ids = self._player.get_party_ids() i = 0 x = 0 for genre_id, genre in genres: label = Gtk.Label() label.set_text(genre) switch = Gtk.Switch() if genre_id in ids: switch.set_state(True) switch.connect("state-set", self._party_switch_state, genre_id) self._party_grid.attach(label, x, i, 1, 1) self._party_grid.attach(switch, x+1, i, 1, 1) if x == 0: x += 2 else: i += 1 x = 0 scrolled.add(self._party_grid) self._party_dialog.show_all() """ Update music database Empty database if reinit True """ def update_db(self, reinit): if reinit: self._player.stop() self._player.clear_albums() self._toolbar.update_toolbar(None, None) self._db.reset() self._list_genres.widget.hide() self._list_artists.widget.hide() self._box.remove(self._view) self._view = LoadingView() self._box.add(self._view) self._scanner.update(self._update_genres) ############ # Private # ############ """ Update party ids when use change a switch in dialog """ def _party_switch_state(self, widget, state, genre_id): ids = self._player.get_party_ids() if state: try: ids.append(genre_id) except: pass else: try: ids.remove(genre_id) except: pass self._player.set_party_ids(ids) self._settings.set_value('party-ids', GLib.Variant('ai', ids)) """ Close edit party dialog """ def _edit_party_close(self, widget): self._party_dialog.hide() self._party_dialog.destroy() """ Setup media player keys """ def _setup_media_keys(self): self._proxy = Gio.DBusProxy.new_sync(Gio.bus_get_sync(Gio.BusType.SESSION, None), Gio.DBusProxyFlags.NONE, None, 'org.gnome.SettingsDaemon', '/org/gnome/SettingsDaemon/MediaKeys', 'org.gnome.SettingsDaemon.MediaKeys', None) self._grab_media_player_keys() try: self._proxy.connect('g-signal', self._handle_media_keys) except GLib.GError: # We cannot grab media keys if no settings daemon is running pass """ Do key grabbing """ def _grab_media_player_keys(self): try: self._proxy.call_sync('GrabMediaPlayerKeys', GLib.Variant('(su)', ('Lollypop', 0)), Gio.DBusCallFlags.NONE, -1, None) except GLib.GError: # We cannot grab media keys if no settings daemon is running pass """ Do player actions in response to media key pressed """ def _handle_media_keys(self, proxy, sender, signal, parameters): if signal != 'MediaPlayerKeyPressed': print('Received an unexpected signal \'%s\' from media player'.format(signal)) return response = parameters.get_child_value(1).get_string() if 'Play' in response: self._player.play_pause() elif 'Stop' in response: self._player.stop() elif 'Next' in response: self._player.next() elif 'Previous' in response: self._player.prev() """ Setup window icon, position and size, callback for updating this values """ def _setup_window(self): self.set_size_request(200, 100) self.set_icon_name('lollypop') size_setting = self._settings.get_value('window-size') if isinstance(size_setting[0], int) and isinstance(size_setting[1], int): self.resize(size_setting[0], size_setting[1]) position_setting = self._settings.get_value('window-position') if len(position_setting) == 2 \ and isinstance(position_setting[0], int) \ and isinstance(position_setting[1], int): self.move(position_setting[0], position_setting[1]) if self._settings.get_value('window-maximized'): self.maximize() self.connect("window-state-event", self._on_window_state_event) self.connect("configure-event", self._on_configure_event) """ Setup window main view: - genre list - artist list - main view as artist view or album view """ def _setup_view(self): self._box = Gtk.Grid() self._toolbar = Toolbar(self._db, self._player) self.set_titlebar(self._toolbar.header_bar) self._toolbar.header_bar.show() self._toolbar.get_infobox().connect("button-press-event", self._show_current_album) self._list_genres = SelectionList("Genre", 150) self._list_artists = SelectionList("Artist", 200) self._view = LoadingView() separator = Gtk.Separator() separator.show() self._box.add(self._list_genres.widget) self._box.add(separator) self._box.add(self._list_artists.widget) self._box.add(self._view) self.add(self._box) self._box.show() self.show() """ Run collection update on mapped window Pass _update_genre() as collection scanned callback """ def _on_mapped_window(self, obj, data): if self._db.is_empty(): self._scanner.update(self._update_genres) else: genres = self._db.get_all_genres() self._update_genres(genres) """ Update genres list with genres """ def _update_genres(self, genres): genres.insert(0, (-1, _("All genres"))) genres.insert(0, (-2, _("Populars albums"))) self._list_genres.populate(genres) self._list_genres.connect('item-selected', self._update_artists) self._list_genres.widget.show() self._list_genres.select_first() """ Update artist list for genre_id """ def _update_artists(self, obj, genre_id): if self._artist_signal_id: self._list_artists.disconnect(self._artist_signal_id) if genre_id == -1: self._list_artists.populate(self._db.get_all_artists(), True) self._update_view_albums(self, -1) self._list_artists.widget.show() elif genre_id == -2: self._update_view_populars_albums() self._list_artists.widget.hide() else: self._list_artists.populate(self._db.get_artists_by_genre_id(genre_id), True) self._update_view_albums(self, genre_id) self._list_artists.widget.show() self._artist_signal_id = self._list_artists.connect('item-selected', self._update_view_artist) self._genre_id = genre_id """ Update artist view for artist_id """ def _update_view_artist(self, obj, artist_id): self._box.remove(self._view) self._view.destroy() self._view = ArtistView(self._db, self._player, self._genre_id, artist_id) self._box.add(self._view) self._view.populate() """ Update albums view with populars albums """ def _update_view_populars_albums(self): self._box.remove(self._view) self._view.destroy() self._view = AlbumView(self._db, self._player, None) self._box.add(self._view) self._view.populate_popular() """ Update albums view for genre_id """ def _update_view_albums(self, obj, genre_id): self._box.remove(self._view) self._view.destroy() self._view = AlbumView(self._db, self._player, genre_id) self._box.add(self._view) self._view.populate() """ Save new window size/position """ def _on_configure_event(self, widget, event): size = widget.get_size() self._settings.set_value('window-size', GLib.Variant('ai', [size[0], size[1]])) position = widget.get_position() self._settings.set_value('window-position', GLib.Variant('ai', [position[0], position[1]])) """ Save maximised state """ def _on_window_state_event(self, widget, event): self._settings.set_boolean('window-maximized', 'GDK_WINDOW_STATE_MAXIMIZED' in event.new_window_state.value_names) """ Show current album context/content """ def _show_current_album(self, obj, data): track_id = self._player.get_current_track_id() if track_id != -1: self._view.current_changed(False, track_id)