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 Container: def __init__(self): # Index will start at -VOLUMES self._devices = {} self._devices_index = Navigation.DEVICES self._show_genres = Objects.settings.get_value('show-genres') self._stack = ViewContainer(500) self._stack.show() self._setup_view() self._setup_scanner() self._list_one_restore = Navigation.POPULARS self._list_two_restore = None if Objects.settings.get_value('save-state'): self._restore_view_state() # Volume manager self._vm = Gio.VolumeMonitor.get() self._vm.connect('mount-added', self._on_mount_added) self._vm.connect('mount-removed', self._on_mount_removed) Objects.playlists.connect("playlists-changed", self.update_lists) """ Update db at startup only if needed @param force as bool to force update (if possible) """ def update_db(self, force=False): if not self._progress.is_visible(): if force or Objects.tracks.is_empty(): self._scanner.update(False) elif Objects.settings.get_value('startup-scan') or\ Objects.settings.get_value('force-scan'): self._scanner.update(True) """ Save view state """ def save_view_state(self): Objects.settings.set_value("list-one", GLib.Variant( 'i', self._list_one.get_selected_id())) Objects.settings.set_value("list-two", GLib.Variant( 'i', self._list_two.get_selected_id())) """ Show playlist manager for object_id Current view stay present in ViewContainer @param object id as int @param genre id as int @param is_album as bool """ def show_playlist_manager(self, object_id, genre_id, is_album): old_view = self._stack.get_visible_child() view = PlaylistManageView(object_id, genre_id, is_album, self._stack.get_allocated_width()/2) view.show() self._stack.add(view) self._stack.set_visible_child(view) start_new_thread(view.populate, ()) # Keep previous view, if isinstance(old_view, PlaylistManageView): old_view.destroy() """ Show playlist editor for playlist Current view stay present in ViewContainer @param playlist name as str """ def show_playlist_editor(self, playlist_name): old_view = self._stack.get_visible_child() view = PlaylistEditView(playlist_name, self._stack.get_allocated_width()/2) view.show() self._stack.add(view) self._stack.set_visible_child(view) start_new_thread(view.populate, ()) # Keep previous view, if isinstance(old_view, PlaylistEditView): old_view.destroy() """ Update lists @param updater as GObject """ def update_lists(self, updater=None): self._update_list_one(updater) self._update_list_two(updater) """ Load external files @param files as [Gio.Files] """ def load_external(self, files): # We wait as selection list is threaded, # we don't want to insert item before populated if self._list_one_restore is None: start_new_thread(self._scanner.add, (files,)) else: GLib.timeout_add(250, self.load_external, files) """ Get main widget @return Gtk.HPaned """ def main_widget(self): return self._paned_main_list """ Stop current view from processing """ def stop_all(self): view = self._stack.get_visible_child() if view is not None: self._stack.clean_old_views(None) """ Show/Hide genres @param bool """ def show_genres(self, show): self._show_genres = show self._update_list_one(None) """ Destroy current view """ def destroy_current_view(self): view = self._stack.get_visible_child() view.hide() GLib.timeout_add(2000, view.destroy) ############ # Private # ############ """ 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._list_one = SelectionList() self._list_one.widget.show() self._list_two = SelectionList() self._list_one.connect('item-selected', self._on_list_one_selected) self._list_one.connect('populated', self._on_list_one_populated) self._list_two.connect('item-selected', self._on_list_two_selected) self._list_two.connect('populated', self._on_list_two_populated) self._progress = Gtk.ProgressBar() vgrid.add(self._stack) vgrid.add(self._progress) vgrid.show() 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() """ Restore saved view """ def _restore_view_state(self): position = Objects.settings.get_value('list-one').get_int32() if position != -1: self._list_one_restore = position else: self._list_one_restore = Navigation.POPULARS position = Objects.settings.get_value('list-two').get_int32() if position != -1: self._list_two_restore = position """ Add genre to genre list @param scanner as CollectionScanner @param genre id as int """ def _add_genre(self, scanner, genre_id): if self._show_genres: genre_name = Objects.genres.get_name(genre_id) self._list_one.add((genre_id, genre_name)) """ Add artist to artist list @param scanner as CollectionScanner @param artist id as int @param album id as int """ def _add_artist(self, scanner, artist_id, album_id): artist_name = Objects.artists.get_name(artist_id) if self._show_genres: genre_ids = Objects.albums.get_genre_ids(album_id) if self._list_one.get_selected_id() in genre_ids: self._list_two.add((artist_id, artist_name)) else: self._list_one.add((artist_id, artist_name)) """ Run collection update if needed @return True if hard scan is running """ def _setup_scanner(self): self._scanner = CollectionScanner(self._progress) self._scanner.connect("scan-finished", self._on_scan_finished) self._scanner.connect("genre-update", self._add_genre) self._scanner.connect("artist-update", self._add_artist) self._scanner.connect("add-finished", self._play_tracks) """ Update list one @param updater as GObject """ def _update_list_one(self, updater): update = updater is not None # Do not update if updater is PlaylistsManager if not isinstance(updater, PlaylistsManager): if self._show_genres: start_new_thread(self._setup_list_genres, (self._list_one, update)) else: start_new_thread(self._setup_list_artists, (self._list_one, Navigation.ALL, update)) """ Update list two @param updater as GObject """ def _update_list_two(self, updater): if self._show_genres: object_id = self._list_one.get_selected_id() if object_id == Navigation.PLAYLISTS: start_new_thread(self._setup_list_playlists, (True,)) elif isinstance(updater, CollectionScanner): start_new_thread(self._setup_list_artists, (self._list_two, object_id, True)) """ Return list one headers """ def _get_headers(self): items = [] items.append((Navigation.POPULARS, _("Popular albums"))) items.append((Navigation.PLAYLISTS, _("Playlists"))) if self._show_genres: items.append((Navigation.ALL, _("All artists"))) else: items.append((Navigation.ALL, _("All albums"))) return items """ Setup list for genres @param list as SelectionList @param update as bool, if True, just update entries @thread safe """ def _setup_list_genres(self, selection_list, update): sql = Objects.db.get_cursor() selection_list.mark_as_artists(False) items = self._get_headers() + Objects.genres.get(sql) if update: GLib.idle_add(selection_list.update, items) else: selection_list.populate(items) sql.close() """ Hide list two base on current artist list """ def _pre_setup_list_artists(self, selection_list): if selection_list == self._list_one: if self._list_two.widget.is_visible(): self._list_two.widget.hide() self._list_two_restore = None """ Setup list for artists @param list as SelectionList @param update as bool, if True, just update entries @thread safe """ def _setup_list_artists(self, selection_list, genre_id, update): GLib.idle_add(self._pre_setup_list_artists, selection_list) sql = Objects.db.get_cursor() items = [] selection_list.mark_as_artists(True) if selection_list == self._list_one: items = self._get_headers() if len(Objects.albums.get_compilations(genre_id, sql)) > 0: items.append((Navigation.COMPILATIONS, _("Compilations"))) items += Objects.artists.get(genre_id, sql) if update: GLib.idle_add(selection_list.update, items) else: selection_list.populate(items) sql.close() """ Setup list for playlists @param update as bool """ def _setup_list_playlists(self, update): playlists = Objects.playlists.get() if update: self._list_two.update(playlists) else: self._list_two.mark_as_artists(False) self._list_two.populate(playlists) GLib.idle_add(self._update_view_playlists, None) """ Update current view with device view, Use existing view if available @param object id as int """ def _update_view_device(self, object_id): device = self._devices[object_id] if device and device.view: view = device.view else: view = DeviceView(device, self._progress, self._stack.get_allocated_width()/2) device.view = view view.show() start_new_thread(view.populate, ()) self._stack.add(view) self._stack.set_visible_child(view) self._stack.clean_old_views(view) """ Update current view with artists view @param object id as int @param genre id as int """ def _update_view_artists(self, object_id, genre_id): view = ArtistView(object_id, True) self._stack.add(view) view.show() start_new_thread(view.populate, (genre_id,)) self._stack.set_visible_child(view) self._stack.clean_old_views(view) """ Update current view with albums view @param object id as int @param genre id as int """ def _update_view_albums(self, object_id, genre_id): view = AlbumView(object_id) self._stack.add(view) view.show() start_new_thread(view.populate, (genre_id,)) self._stack.set_visible_child(view) self._stack.clean_old_views(view) """ Update current view with playlist view @param playlist id as int """ def _update_view_playlists(self, playlist_id): view = None if playlist_id is not None: for (p_id, p_str) in Objects.playlists.get(): if p_id == playlist_id: view = PlaylistView(p_str, self._stack) break else: view = PlaylistManageView(-1, None, False, self._stack.get_allocated_width()/2) if view: view.show() self._stack.add(view) self._stack.set_visible_child(view) start_new_thread(view.populate, ()) self._stack.clean_old_views(view) """ Add volume to device list @param volume as Gio.Volume """ def _add_device(self, volume): if volume is None: return root = volume.get_activation_root() if root is None: return path = root.get_path() if path and path.find('mtp:') != -1: self._devices_index -= 1 dev = Device() dev.id = self._devices_index dev.name = volume.get_name() dev.path = path self._devices[self._devices_index] = dev self._list_one.add_device(dev.name, dev.id) """ Remove volume from device list @param volume as Gio.Volume """ def _remove_device(self, volume): for dev in self._devices.values(): if not os.path.exists(dev.path): self._list_one.remove(dev.id) device = self._devices[dev.id] if device.view: device.view.destroy() del self._devices[dev.id] break """ Update view based on selected object @param list as SelectionList @param object id as int """ def _on_list_one_selected(self, selection_list, object_id): if object_id == Navigation.PLAYLISTS: start_new_thread(self._setup_list_playlists, (False,)) self._list_two.widget.show() elif object_id < Navigation.DEVICES: self._list_two.widget.hide() self._update_view_device(object_id) elif object_id == Navigation.POPULARS: self._list_two.widget.hide() self._update_view_albums(object_id, None) elif selection_list.is_marked_as_artists(): self._list_two.widget.hide() if object_id == Navigation.ALL or\ object_id == Navigation.COMPILATIONS: self._update_view_albums(object_id, None) else: self._update_view_artists(object_id, None) else: start_new_thread(self._setup_list_artists, (self._list_two, object_id, False)) self._list_two.widget.show() if self._list_two_restore is None: self._update_view_albums(object_id, None) """ Restore previous state @param selection list as SelectionList """ def _on_list_one_populated(self, selection_list): if self._list_one_restore is not None: self._list_one.select_id(self._list_one_restore) self._list_one_restore = None for dev in self._devices.values(): self._list_one.add_device(dev.name, dev.id) """ Update view based on selected object @param list as SelectionList @param object id as int """ def _on_list_two_selected(self, selection_list, object_id): selected_id = self._list_one.get_selected_id() if selected_id == Navigation.PLAYLISTS: self._update_view_playlists(object_id) elif object_id == Navigation.COMPILATIONS: self._update_view_albums(object_id, selected_id) else: self._update_view_artists(object_id, selected_id) """ Restore previous state @param selection list as SelectionList """ def _on_list_two_populated(self, selection_list): if self._list_two_restore is not None: self._list_two.select_id(self._list_two_restore) self._list_two_restore = None """ Play tracks as user playlist @param scanner as collection scanner @param outdb as bool (tracks not present in db) """ def _play_tracks(self, scanner, outdb): ids = scanner.get_added() if ids: if not Objects.player.is_party(): Objects.player.set_user_playlist(ids, ids[0]) if outdb: Objects.settings.set_value('force-scan', GLib.Variant('b', True)) Objects.player.load(ids[0]) """ Mark force scan as False, update lists @param scanner as CollectionScanner """ def _on_scan_finished(self, scanner): Objects.settings.set_value('force-scan', GLib.Variant('b', False)) self.update_lists(scanner) """ On volume mounter @param vm as Gio.VolumeMonitor @param mnt as Gio.Mount """ def _on_mount_added(self, vm, mnt): self._add_device(mnt.get_volume()) """ On volume removed, clean selection list @param vm as Gio.VolumeMonitor @param mnt as Gio.Mount """ def _on_mount_removed(self, vm, mnt): self._remove_device(mnt.get_volume())
class Container: def __init__(self): # Index will start at -VOLUMES self._devices = {} self._devices_index = Navigation.DEVICES self._show_genres = Objects.settings.get_value('show-genres') self._stack = ViewContainer(500) self._stack.show() self._setup_view() self._setup_scanner() self._list_one_restore = Navigation.POPULARS self._list_two_restore = None if Objects.settings.get_value('save-state'): self._restore_view_state() # Volume manager self._vm = Gio.VolumeMonitor.get() self._vm.connect('mount-added', self._on_mount_added) self._vm.connect('mount-removed', self._on_mount_removed) Objects.playlists.connect("playlists-changed", self.update_lists) """ Update db at startup only if needed @param force as bool to force update (if possible) """ def update_db(self, force=False): if not self._progress.is_visible(): if force or Objects.tracks.is_empty(): self._scanner.update(False) elif Objects.settings.get_value('startup-scan') or\ Objects.settings.get_value('force-scan'): self._scanner.update(True) """ Save view state """ def save_view_state(self): Objects.settings.set_value( "list-one", GLib.Variant('i', self._list_one.get_selected_id())) Objects.settings.set_value( "list-two", GLib.Variant('i', self._list_two.get_selected_id())) """ Show playlist manager for object_id Current view stay present in ViewContainer @param object id as int @param genre id as int @param is_album as bool """ def show_playlist_manager(self, object_id, genre_id, is_album): old_view = self._stack.get_visible_child() view = PlaylistManageView(object_id, genre_id, is_album, self._stack.get_allocated_width() / 2) view.show() self._stack.add(view) self._stack.set_visible_child(view) start_new_thread(view.populate, ()) # Keep previous view, if isinstance(old_view, PlaylistManageView): old_view.destroy() """ Show playlist editor for playlist Current view stay present in ViewContainer @param playlist name as str """ def show_playlist_editor(self, playlist_name): old_view = self._stack.get_visible_child() view = PlaylistEditView(playlist_name, self._stack.get_allocated_width() / 2) view.show() self._stack.add(view) self._stack.set_visible_child(view) start_new_thread(view.populate, ()) # Keep previous view, if isinstance(old_view, PlaylistEditView): old_view.destroy() """ Update lists @param updater as GObject """ def update_lists(self, updater=None): self._update_list_one(updater) self._update_list_two(updater) """ Load external files @param files as [Gio.Files] """ def load_external(self, files): # We wait as selection list is threaded, # we don't want to insert item before populated if self._list_one_restore is None: start_new_thread(self._scanner.add, (files, )) else: GLib.timeout_add(250, self.load_external, files) """ Get main widget @return Gtk.HPaned """ def main_widget(self): return self._paned_main_list """ Stop current view from processing """ def stop_all(self): view = self._stack.get_visible_child() if view is not None: self._stack.clean_old_views(None) """ Show/Hide genres @param bool """ def show_genres(self, show): self._show_genres = show self._update_list_one(None) """ Destroy current view """ def destroy_current_view(self): view = self._stack.get_visible_child() view.hide() GLib.timeout_add(2000, view.destroy) ############ # Private # ############ """ 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._list_one = SelectionList() self._list_one.widget.show() self._list_two = SelectionList() self._list_one.connect('item-selected', self._on_list_one_selected) self._list_one.connect('populated', self._on_list_one_populated) self._list_two.connect('item-selected', self._on_list_two_selected) self._list_two.connect('populated', self._on_list_two_populated) self._progress = Gtk.ProgressBar() vgrid.add(self._stack) vgrid.add(self._progress) vgrid.show() 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() """ Restore saved view """ def _restore_view_state(self): position = Objects.settings.get_value('list-one').get_int32() if position != -1: self._list_one_restore = position else: self._list_one_restore = Navigation.POPULARS position = Objects.settings.get_value('list-two').get_int32() if position != -1: self._list_two_restore = position """ Add genre to genre list @param scanner as CollectionScanner @param genre id as int """ def _add_genre(self, scanner, genre_id): if self._show_genres: genre_name = Objects.genres.get_name(genre_id) self._list_one.add((genre_id, genre_name)) """ Add artist to artist list @param scanner as CollectionScanner @param artist id as int @param album id as int """ def _add_artist(self, scanner, artist_id, album_id): artist_name = Objects.artists.get_name(artist_id) if self._show_genres: genre_ids = Objects.albums.get_genre_ids(album_id) if self._list_one.get_selected_id() in genre_ids: self._list_two.add((artist_id, artist_name)) else: self._list_one.add((artist_id, artist_name)) """ Run collection update if needed @return True if hard scan is running """ def _setup_scanner(self): self._scanner = CollectionScanner(self._progress) self._scanner.connect("scan-finished", self._on_scan_finished) self._scanner.connect("genre-update", self._add_genre) self._scanner.connect("artist-update", self._add_artist) self._scanner.connect("add-finished", self._play_tracks) """ Update list one @param updater as GObject """ def _update_list_one(self, updater): update = updater is not None # Do not update if updater is PlaylistsManager if not isinstance(updater, PlaylistsManager): if self._show_genres: start_new_thread(self._setup_list_genres, (self._list_one, update)) else: start_new_thread(self._setup_list_artists, (self._list_one, Navigation.ALL, update)) """ Update list two @param updater as GObject """ def _update_list_two(self, updater): if self._show_genres: object_id = self._list_one.get_selected_id() if object_id == Navigation.PLAYLISTS: start_new_thread(self._setup_list_playlists, (True, )) elif isinstance(updater, CollectionScanner): start_new_thread(self._setup_list_artists, (self._list_two, object_id, True)) """ Return list one headers """ def _get_headers(self): items = [] items.append((Navigation.POPULARS, _("Popular albums"))) items.append((Navigation.PLAYLISTS, _("Playlists"))) if self._show_genres: items.append((Navigation.ALL, _("All artists"))) else: items.append((Navigation.ALL, _("All albums"))) return items """ Setup list for genres @param list as SelectionList @param update as bool, if True, just update entries @thread safe """ def _setup_list_genres(self, selection_list, update): sql = Objects.db.get_cursor() selection_list.mark_as_artists(False) items = self._get_headers() + Objects.genres.get(sql) if update: GLib.idle_add(selection_list.update, items) else: selection_list.populate(items) sql.close() """ Hide list two base on current artist list """ def _pre_setup_list_artists(self, selection_list): if selection_list == self._list_one: if self._list_two.widget.is_visible(): self._list_two.widget.hide() self._list_two_restore = None """ Setup list for artists @param list as SelectionList @param update as bool, if True, just update entries @thread safe """ def _setup_list_artists(self, selection_list, genre_id, update): GLib.idle_add(self._pre_setup_list_artists, selection_list) sql = Objects.db.get_cursor() items = [] selection_list.mark_as_artists(True) if selection_list == self._list_one: items = self._get_headers() if len(Objects.albums.get_compilations(genre_id, sql)) > 0: items.append((Navigation.COMPILATIONS, _("Compilations"))) items += Objects.artists.get(genre_id, sql) if update: GLib.idle_add(selection_list.update, items) else: selection_list.populate(items) sql.close() """ Setup list for playlists @param update as bool """ def _setup_list_playlists(self, update): playlists = Objects.playlists.get() if update: self._list_two.update(playlists) else: self._list_two.mark_as_artists(False) self._list_two.populate(playlists) GLib.idle_add(self._update_view_playlists, None) """ Update current view with device view, Use existing view if available @param object id as int """ def _update_view_device(self, object_id): device = self._devices[object_id] if device and device.view: view = device.view else: view = DeviceView(device, self._progress, self._stack.get_allocated_width() / 2) device.view = view view.show() start_new_thread(view.populate, ()) self._stack.add(view) self._stack.set_visible_child(view) self._stack.clean_old_views(view) """ Update current view with artists view @param object id as int @param genre id as int """ def _update_view_artists(self, object_id, genre_id): view = ArtistView(object_id, True) self._stack.add(view) view.show() start_new_thread(view.populate, (genre_id, )) self._stack.set_visible_child(view) self._stack.clean_old_views(view) """ Update current view with albums view @param object id as int @param genre id as int """ def _update_view_albums(self, object_id, genre_id): view = AlbumView(object_id) self._stack.add(view) view.show() start_new_thread(view.populate, (genre_id, )) self._stack.set_visible_child(view) self._stack.clean_old_views(view) """ Update current view with playlist view @param playlist id as int """ def _update_view_playlists(self, playlist_id): view = None if playlist_id is not None: for (p_id, p_str) in Objects.playlists.get(): if p_id == playlist_id: view = PlaylistView(p_str, self._stack) break else: view = PlaylistManageView(-1, None, False, self._stack.get_allocated_width() / 2) if view: view.show() self._stack.add(view) self._stack.set_visible_child(view) start_new_thread(view.populate, ()) self._stack.clean_old_views(view) """ Add volume to device list @param volume as Gio.Volume """ def _add_device(self, volume): if volume is None: return root = volume.get_activation_root() if root is None: return path = root.get_path() if path and path.find('mtp:') != -1: self._devices_index -= 1 dev = Device() dev.id = self._devices_index dev.name = volume.get_name() dev.path = path self._devices[self._devices_index] = dev self._list_one.add_device(dev.name, dev.id) """ Remove volume from device list @param volume as Gio.Volume """ def _remove_device(self, volume): for dev in self._devices.values(): if not os.path.exists(dev.path): self._list_one.remove(dev.id) device = self._devices[dev.id] if device.view: device.view.destroy() del self._devices[dev.id] break """ Update view based on selected object @param list as SelectionList @param object id as int """ def _on_list_one_selected(self, selection_list, object_id): if object_id == Navigation.PLAYLISTS: start_new_thread(self._setup_list_playlists, (False, )) self._list_two.widget.show() elif object_id < Navigation.DEVICES: self._list_two.widget.hide() self._update_view_device(object_id) elif object_id == Navigation.POPULARS: self._list_two.widget.hide() self._update_view_albums(object_id, None) elif selection_list.is_marked_as_artists(): self._list_two.widget.hide() if object_id == Navigation.ALL or\ object_id == Navigation.COMPILATIONS: self._update_view_albums(object_id, None) else: self._update_view_artists(object_id, None) else: start_new_thread(self._setup_list_artists, (self._list_two, object_id, False)) self._list_two.widget.show() if self._list_two_restore is None: self._update_view_albums(object_id, None) """ Restore previous state @param selection list as SelectionList """ def _on_list_one_populated(self, selection_list): if self._list_one_restore is not None: self._list_one.select_id(self._list_one_restore) self._list_one_restore = None for dev in self._devices.values(): self._list_one.add_device(dev.name, dev.id) """ Update view based on selected object @param list as SelectionList @param object id as int """ def _on_list_two_selected(self, selection_list, object_id): selected_id = self._list_one.get_selected_id() if selected_id == Navigation.PLAYLISTS: self._update_view_playlists(object_id) elif object_id == Navigation.COMPILATIONS: self._update_view_albums(object_id, selected_id) else: self._update_view_artists(object_id, selected_id) """ Restore previous state @param selection list as SelectionList """ def _on_list_two_populated(self, selection_list): if self._list_two_restore is not None: self._list_two.select_id(self._list_two_restore) self._list_two_restore = None """ Play tracks as user playlist @param scanner as collection scanner @param outdb as bool (tracks not present in db) """ def _play_tracks(self, scanner, outdb): ids = scanner.get_added() if ids: if not Objects.player.is_party(): Objects.player.set_user_playlist(ids, ids[0]) if outdb: Objects.settings.set_value('force-scan', GLib.Variant('b', True)) Objects.player.load(ids[0]) """ Mark force scan as False, update lists @param scanner as CollectionScanner """ def _on_scan_finished(self, scanner): Objects.settings.set_value('force-scan', GLib.Variant('b', False)) self.update_lists(scanner) """ On volume mounter @param vm as Gio.VolumeMonitor @param mnt as Gio.Mount """ def _on_mount_added(self, vm, mnt): self._add_device(mnt.get_volume()) """ On volume removed, clean selection list @param vm as Gio.VolumeMonitor @param mnt as Gio.Mount """ def _on_mount_removed(self, vm, mnt): self._remove_device(mnt.get_volume())