def __init__(self, playlist_ids, editable=True): """ Init PlaylistView @parma playlist ids as [int] @param editable as bool """ View.__init__(self) self.__tracks = [] self.__playlist_ids = playlist_ids self.__signal_id1 = Lp().playlists.connect('playlist-add', self.__on_playlist_add) self.__signal_id2 = Lp().playlists.connect('playlist-del', self.__on_playlist_del) builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/PlaylistView.ui') builder.connect_signals(self) builder.get_object('title').set_label( ", ".join(Lp().playlists.get_names(playlist_ids))) self.__edit_button = builder.get_object('edit-button') self.__jump_button = builder.get_object('jump-button') if len(playlist_ids) > 1 or ( playlist_ids[0] < 0 and playlist_ids[0] != Type.LOVED) or\ not editable: self.__edit_button.hide() self.__playlists_widget = PlaylistsWidget(playlist_ids) self.__playlists_widget.show() self.add(builder.get_object('widget')) self._viewport.add(self.__playlists_widget) self._scrolled.set_property('expand', True) self.add(self._scrolled)
def __init__(self, playlist_ids, editable=True): """ Init PlaylistView @parma playlist ids as [int] @param editable as bool """ View.__init__(self, True) self.__tracks = [] self.__playlist_ids = playlist_ids self.__signal_id1 = Lp().playlists.connect("playlist-add", self.__on_playlist_add) self.__signal_id2 = Lp().playlists.connect("playlist-del", self.__on_playlist_del) builder = Gtk.Builder() builder.add_from_resource("/org/gnome/Lollypop/PlaylistView.ui") self.__duration_label = builder.get_object("duration") builder.get_object("title").set_label(", ".join( Lp().playlists.get_names(playlist_ids))) self.__edit_button = builder.get_object("edit-button") self.__jump_button = builder.get_object("jump-button") split_button = builder.get_object("split-button") if editable: split_button.set_active(not Lp().settings.get_value("split-view")) else: split_button.hide() if len(playlist_ids) > 1 or ( playlist_ids[0] < 0 and playlist_ids[0] not in [Type.LOVED, Type.NOPARTY]) or\ not editable: self.__edit_button.hide() self.__playlists_widget = PlaylistsWidget(playlist_ids) self.__playlists_widget.set_filter_func(self._filter_func) self.__playlists_widget.show() self.add(builder.get_object("widget")) self._viewport.add(self.__playlists_widget) self._scrolled.set_property("expand", True) self.add(self._scrolled) # Connect signals after ui init # "split-button" will emit a signal otherwise builder.connect_signals(self) # No duration for non user playlists # FIXME if playlist_ids[0] > 0: self.__set_duration()
def __init__(self, playlist_ids, editable=True): """ Init PlaylistView @parma playlist ids as [int] @param editable as bool """ View.__init__(self) self._tracks = [] self._playlist_ids = playlist_ids self._signal_id1 = Lp().playlists.connect('playlist-add', self._on_playlist_add) self._signal_id2 = Lp().playlists.connect('playlist-del', self._on_playlist_del) builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/PlaylistView.ui') builder.connect_signals(self) builder.get_object('title').set_label( ", ".join(Lp().playlists.get_names(playlist_ids))) self._edit_button = builder.get_object('edit-button') self._jump_button = builder.get_object('jump-button') if len(playlist_ids) > 1 or ( playlist_ids[0] < 0 and playlist_ids[0] != Type.LOVED) or\ not editable: self._edit_button.hide() self._playlists_widget = PlaylistsWidget(playlist_ids) self._playlists_widget.show() self.add(builder.get_object('widget')) self._viewport.add(self._playlists_widget) self._scrolled.set_property('expand', True) self.add(self._scrolled)
def __init__(self, playlist_ids, editable=True): """ Init PlaylistView @parma playlist ids as [int] @param editable as bool """ View.__init__(self, True) self.__tracks = [] self.__playlist_ids = playlist_ids self.__signal_id1 = Lp().playlists.connect('playlist-add', self.__on_playlist_add) self.__signal_id2 = Lp().playlists.connect('playlist-del', self.__on_playlist_del) builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/PlaylistView.ui') builder.get_object('title').set_label(", ".join( Lp().playlists.get_names(playlist_ids))) self.__edit_button = builder.get_object('edit-button') self.__jump_button = builder.get_object('jump-button') split_button = builder.get_object('split-button') if editable: split_button.set_active(not Lp().settings.get_value('split-view')) else: split_button.hide() if len(playlist_ids) > 1 or ( playlist_ids[0] < 0 and playlist_ids[0] not in [Type.LOVED, Type.NOPARTY]) or\ not editable: self.__edit_button.hide() self.__playlists_widget = PlaylistsWidget(playlist_ids) self.__playlists_widget.set_filter_func(self._filter_func) self.__playlists_widget.show() self.add(builder.get_object('widget')) self._viewport.add(self.__playlists_widget) self._scrolled.set_property('expand', True) self.add(self._scrolled) # Connect signals after ui init # 'split-button' will emit a signal otherwise builder.connect_signals(self)
def __init__(self, playlist_ids, editable=True): """ Init PlaylistView @parma playlist ids as [int] @param editable as bool """ View.__init__(self) self._tracks = [] self._playlist_ids = playlist_ids self._signal_id1 = Lp().playlists.connect('playlist-add', self._on_playlist_add) self._signal_id2 = Lp().playlists.connect('playlist-del', self._on_playlist_del) builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/PlaylistView.ui') builder.connect_signals(self) name = "" for playlist_id in playlist_ids: if playlist_id == Type.POPULARS: name += _("Popular tracks")+", " elif playlist_id == Type.RECENTS: name += _("Recently played")+", " elif playlist_id == Type.NEVER: name += _("Never played")+", " elif playlist_id == Type.RANDOMS: name += _("Random tracks")+", " elif playlist_id == Type.SEARCH: name += _("Search")+", " else: name += Lp().playlists.get_name(playlist_id)+", " builder.get_object('title').set_label(name[:-2]) self._edit_button = builder.get_object('edit-button') self._jump_button = builder.get_object('jump-button') if len(playlist_ids) > 1 or ( playlist_ids[0] < 0 and playlist_ids[0] != Type.LOVED) or\ not editable: self._edit_button.hide() self._title = builder.get_object('title') self._playlists_widget = PlaylistsWidget(playlist_ids) self._playlists_widget.connect('populated', self._on_populated) self._playlists_widget.show() self.add(builder.get_object('widget')) self._viewport.add(self._playlists_widget) self._scrolled.set_property('expand', True) self.add(self._scrolled)
class PlaylistsView(View): """ Show playlist tracks """ def __init__(self, playlist_ids, editable=True): """ Init PlaylistView @parma playlist ids as [int] @param editable as bool """ View.__init__(self) self.__tracks = [] self.__playlist_ids = playlist_ids self.__signal_id1 = Lp().playlists.connect('playlist-add', self.__on_playlist_add) self.__signal_id2 = Lp().playlists.connect('playlist-del', self.__on_playlist_del) builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/PlaylistView.ui') builder.get_object('title').set_label(", ".join( Lp().playlists.get_names(playlist_ids))) self.__edit_button = builder.get_object('edit-button') self.__jump_button = builder.get_object('jump-button') split_button = builder.get_object('split-button') if editable: split_button.set_active(not Lp().settings.get_value('split-view')) else: split_button.hide() if len(playlist_ids) > 1 or ( playlist_ids[0] < 0 and playlist_ids[0] != Type.LOVED) or\ not editable: self.__edit_button.hide() self.__playlists_widget = PlaylistsWidget(playlist_ids) self.__playlists_widget.show() self.add(builder.get_object('widget')) self._viewport.add(self.__playlists_widget) self._scrolled.set_property('expand', True) self.add(self._scrolled) # Connect signals after ui init # 'split-button' will emit a signal otherwise builder.connect_signals(self) def populate(self, tracks): """ Populate view with tracks from playlist Thread safe """ # We are looking for middle # Ponderate with this: # Tracks with cover == 2 # Tracks without cover == 1 prev_album_id = None heights = {} total = 0 idx = 0 for track_id in tracks: track = Track(track_id) if track.album_id != prev_album_id: heights[idx] = 2 total += 2 else: heights[idx] = 1 total += 1 prev_album_id = track.album_id idx += 1 half = int(total / 2 + 0.5) mid_tracks = 1 count = 0 for height in heights.values(): count += height if count >= half: break mid_tracks += 1 self.__tracks = tracks self.__update_jump_button() self.__playlists_widget.populate_list_left(tracks[:mid_tracks], 1) self.__playlists_widget.populate_list_right(tracks[mid_tracks:], mid_tracks + 1) def get_ids(self): """ Return playlist ids @return id as [int] """ return self.__playlist_ids def stop(self): """ Stop populating """ self.__playlists_widget.stop() ####################### # PROTECTED # ####################### def _get_children(self): """ Return view children """ return [self.__playlists_widget] def _on_destroy(self, widget): """ Disconnect signals @param widget as Gtk.Widget """ View._on_destroy(self, widget) if self.__signal_id1: Lp().playlists.disconnect(self.__signal_id1) self.__signal_id1 = None if self.__signal_id2: Lp().playlists.disconnect(self.__signal_id2) self.__signal_id2 = None def _on_split_button_toggled(self, button): """ Split/Unsplit view """ Lp().settings.set_value('split-view', GLib.Variant('b', not button.get_active())) self.__playlists_widget.update_allocation() def _on_jump_button_clicked(self, button): """ Scroll to current track @param button as Gtk.Button """ y = self.__playlists_widget.get_current_ordinate() if y is not None: self._scrolled.get_vadjustment().set_value(y) def _on_edit_button_clicked(self, button): """ Edit playlist @param button as Gtk.Button """ Lp().window.show_playlist_editor(self.__playlist_ids[0]) def _on_current_changed(self, player): """ Current song changed, update playing button @param player as Player """ View._on_current_changed(self, player) self.__update_jump_button() ####################### # PRIVATE # ####################### def __update_jump_button(self): """ Update jump button status """ if Lp().player.current_track.id in self.__tracks: self.__jump_button.set_sensitive(True) artists = ", ".join(Lp().player.current_track.artists) self.__jump_button.set_tooltip_markup( "<b>%s</b>\n%s" % (GLib.markup_escape_text(artists), GLib.markup_escape_text(Lp().player.current_track.name))) else: self.__jump_button.set_sensitive(False) self.__jump_button.set_tooltip_text('') def __on_playlist_add(self, manager, playlist_id, track_id): """ Update tracks widgets @param manager as PlaylistsManager @param playlist id as int @param track id as int """ if playlist_id in self.__playlist_ids: self.__playlists_widget.append(track_id) def __on_playlist_del(self, manager, playlist_id, track_id): """ Update tracks widgets @param manager as PlaylistsManager @param playlist id as int @param track id as int """ if playlist_id in self.__playlist_ids: self.__playlists_widget.remove(track_id)
def __init__(self, playlist_ids, view_type): """ Init PlaylistView @parma playlist ids as [int] @param view_type as ViewType """ LazyLoadingView.__init__(self) ViewController.__init__(self, ViewControllerType.ALBUM) self.__view_type = view_type self.__playlist_ids = playlist_ids self.__signal_id1 = App().playlists.connect( "playlist-track-added", self.__on_playlist_track_added) self.__signal_id2 = App().playlists.connect( "playlist-track-removed", self.__on_playlist_track_removed) self.__signal_id3 = App().settings.connect( "changed::split-view", self.__on_split_view_changed) builder = Gtk.Builder() builder.add_from_resource("/org/gnome/Lollypop/PlaylistView.ui") self.__title_label = builder.get_object("title") self.__duration_label = builder.get_object("duration") self.__play_button = builder.get_object("play_button") self.__shuffle_button = builder.get_object("shuffle_button") self.__jump_button = builder.get_object("jump_button") self.__menu_button = builder.get_object("menu_button") self.__buttons = builder.get_object("box-buttons") self.__widget = builder.get_object("widget") self.__playlists_widget = PlaylistsWidget(playlist_ids, view_type) self.__playlists_widget.set_filter_func(self._filter_func) self.__playlists_widget.connect("populated", self.__on_populated) self.__playlists_widget.show() self._viewport.add(self.__playlists_widget) self.__title_label.set_margin_start(MARGIN) self.__buttons.set_margin_end(MARGIN) if self.__view_type & (ViewType.POPOVER | ViewType.FULLSCREEN): self.__title_label.get_style_context().add_class("dim-label") self.__duration_label.get_style_context().add_class("dim-label") self.__widget.add(self.__title_label) self.__jump_button = Gtk.Button.new_from_icon_name( "go-jump-symbolic", Gtk.IconSize.BUTTON) self.__jump_button.connect("clicked", self._on_jump_button_clicked) self.__jump_button.set_relief(Gtk.ReliefStyle.NONE) self.__jump_button.show() self.__jump_button.set_margin_end(MARGIN_SMALL) self.__widget.add(self.__duration_label) self.__widget.add(self.__jump_button) self.__widget.set_margin_bottom(MARGIN_SMALL) self.add(self.__widget) self.add(self._scrolled) else: self.__duration_label.set_margin_start(MARGIN) self._overlay = Gtk.Overlay.new() self._overlay.add(self._scrolled) self._overlay.show() self.__widget.attach(self.__title_label, 0, 0, 1, 1) self.__widget.attach(self.__duration_label, 0, 1, 1, 1) self.__widget.attach(self.__buttons, 1, 0, 1, 2) self.__widget.set_vexpand(True) self.__title_label.set_vexpand(True) self.__duration_label.set_vexpand(True) if App().window.is_adaptive: self.__title_label.get_style_context().add_class( "text-x-large") self.__duration_label.get_style_context().add_class( "text-large") else: self.__title_label.get_style_context().add_class( "text-xx-large") self.__duration_label.get_style_context().add_class( "text-x-large") self.__title_label.set_property("valign", Gtk.Align.END) self.__duration_label.set_property("valign", Gtk.Align.START) self.__banner = PlaylistBannerWidget(playlist_ids[0]) self.__banner.show() self._overlay.add_overlay(self.__banner) self.__banner.add_overlay(self.__widget) self.__playlists_widget.set_margin_top( self.__banner.default_height + 15) self.add(self._overlay) self.__title_label.set_label(", ".join( App().playlists.get_names(playlist_ids))) self._scrolled.set_property("expand", True) builder.connect_signals(self) if len(playlist_ids) > 1: self.__menu_button.hide() # In DB duration calculation if playlist_ids[0] > 0 and\ not App().playlists.get_smart(playlist_ids[0]): duration = 0 for playlist_id in self.__playlist_ids: duration += App().playlists.get_duration(playlist_id) self.__set_duration(duration) # Ask widget after populated else: self.__playlists_widget.connect("populated", self.__on_playlist_populated)
class PlaylistsView(LazyLoadingView, ViewController): """ Show playlist tracks """ def __init__(self, playlist_ids, view_type): """ Init PlaylistView @parma playlist ids as [int] @param view_type as ViewType """ LazyLoadingView.__init__(self) ViewController.__init__(self, ViewControllerType.ALBUM) self.__view_type = view_type self.__playlist_ids = playlist_ids self.__signal_id1 = App().playlists.connect( "playlist-track-added", self.__on_playlist_track_added) self.__signal_id2 = App().playlists.connect( "playlist-track-removed", self.__on_playlist_track_removed) self.__signal_id3 = App().settings.connect( "changed::split-view", self.__on_split_view_changed) builder = Gtk.Builder() builder.add_from_resource("/org/gnome/Lollypop/PlaylistView.ui") self.__title_label = builder.get_object("title") self.__duration_label = builder.get_object("duration") self.__play_button = builder.get_object("play_button") self.__shuffle_button = builder.get_object("shuffle_button") self.__jump_button = builder.get_object("jump_button") self.__menu_button = builder.get_object("menu_button") self.__buttons = builder.get_object("box-buttons") self.__widget = builder.get_object("widget") self.__playlists_widget = PlaylistsWidget(playlist_ids, view_type) self.__playlists_widget.set_filter_func(self._filter_func) self.__playlists_widget.connect("populated", self.__on_populated) self.__playlists_widget.show() self._viewport.add(self.__playlists_widget) self.__title_label.set_margin_start(MARGIN) self.__buttons.set_margin_end(MARGIN) if self.__view_type & (ViewType.POPOVER | ViewType.FULLSCREEN): self.__title_label.get_style_context().add_class("dim-label") self.__duration_label.get_style_context().add_class("dim-label") self.__widget.add(self.__title_label) self.__jump_button = Gtk.Button.new_from_icon_name( "go-jump-symbolic", Gtk.IconSize.BUTTON) self.__jump_button.connect("clicked", self._on_jump_button_clicked) self.__jump_button.set_relief(Gtk.ReliefStyle.NONE) self.__jump_button.show() self.__jump_button.set_margin_end(MARGIN_SMALL) self.__widget.add(self.__duration_label) self.__widget.add(self.__jump_button) self.__widget.set_margin_bottom(MARGIN_SMALL) self.add(self.__widget) self.add(self._scrolled) else: self.__duration_label.set_margin_start(MARGIN) self._overlay = Gtk.Overlay.new() self._overlay.add(self._scrolled) self._overlay.show() self.__widget.attach(self.__title_label, 0, 0, 1, 1) self.__widget.attach(self.__duration_label, 0, 1, 1, 1) self.__widget.attach(self.__buttons, 1, 0, 1, 2) self.__widget.set_vexpand(True) self.__title_label.set_vexpand(True) self.__duration_label.set_vexpand(True) if App().window.is_adaptive: self.__title_label.get_style_context().add_class( "text-x-large") self.__duration_label.get_style_context().add_class( "text-large") else: self.__title_label.get_style_context().add_class( "text-xx-large") self.__duration_label.get_style_context().add_class( "text-x-large") self.__title_label.set_property("valign", Gtk.Align.END) self.__duration_label.set_property("valign", Gtk.Align.START) self.__banner = PlaylistBannerWidget(playlist_ids[0]) self.__banner.show() self._overlay.add_overlay(self.__banner) self.__banner.add_overlay(self.__widget) self.__playlists_widget.set_margin_top( self.__banner.default_height + 15) self.add(self._overlay) self.__title_label.set_label(", ".join( App().playlists.get_names(playlist_ids))) self._scrolled.set_property("expand", True) builder.connect_signals(self) if len(playlist_ids) > 1: self.__menu_button.hide() # In DB duration calculation if playlist_ids[0] > 0 and\ not App().playlists.get_smart(playlist_ids[0]): duration = 0 for playlist_id in self.__playlist_ids: duration += App().playlists.get_duration(playlist_id) self.__set_duration(duration) # Ask widget after populated else: self.__playlists_widget.connect("populated", self.__on_playlist_populated) def populate(self, tracks): """ Populate view with tracks from playlist @param tracks as [track] """ self.__playlists_widget.populate(tracks) self.__update_jump_button() def stop(self): """ Stop populating """ self.__playlists_widget.stop() @property def playlist_ids(self): """ Return playlist ids @return id as [int] """ return self.__playlist_ids ####################### # PROTECTED # ####################### def _on_value_changed(self, adj): """ Adapt widget to current scroll value @param adj as Gtk.Adjustment """ LazyLoadingView._on_value_changed(self, adj) if not self.__view_type & (ViewType.POPOVER | ViewType.FULLSCREEN): title_style_context = self.__title_label.get_style_context() if adj.get_value() == adj.get_lower(): height = self.__banner.default_height self.__duration_label.show() self.__title_label.set_property("valign", Gtk.Align.END) if not App().window.is_adaptive: title_style_context.remove_class("text-x-large") title_style_context.add_class("text-xx-large") else: self.__duration_label.hide() title_style_context.remove_class("text-xx-large") title_style_context.add_class("text-x-large") self.__title_label.set_property("valign", Gtk.Align.CENTER) height = self.__banner.default_height // 3 # Make grid cover artwork # No idea why... self.__banner.set_height(height) self.__widget.set_size_request(-1, height + 1) def _on_current_changed(self, player): """ Update children state @param player as Player """ self.__update_jump_button() self.__playlists_widget.set_playing_indicator() def _on_search_changed(self, entry): """ Update filter @param entry as Gtk.Entry """ self._filter = entry.get_text() for box in self.__playlists_widget.boxes: box.invalidate_filter() def _on_destroy(self, widget): """ Disconnect signals @param widget as Gtk.Widget """ LazyLoadingView._on_destroy(self, widget) if self.__signal_id1: App().playlists.disconnect(self.__signal_id1) self.__signal_id1 = None if self.__signal_id2: App().playlists.disconnect(self.__signal_id2) self.__signal_id2 = None def _on_jump_button_clicked(self, button): """ Scroll to current track @param button as Gtk.Button """ y = self.__playlists_widget.get_current_ordinate() if y is not None: self._scrolled.get_vadjustment().set_value(y) def _on_play_button_clicked(self, button): """ Play playlist @param button as Gtk.Button """ tracks = [] for child in self.__playlists_widget.children: tracks.append(child.track) if tracks: App().player.populate_playlist_by_tracks(tracks, self.__playlist_ids, tracks[0]) def _on_shuffle_button_clicked(self, button): """ Play playlist shuffled @param button as Gtk.Button """ tracks = [] for child in self.__playlists_widget.children: tracks.append(child.track) if tracks: shuffle(tracks) App().player.populate_playlist_by_tracks(tracks, self.__playlist_ids, tracks[0]) def _on_menu_button_clicked(self, button): """ Show playlist menu @param button as Gtk.Button """ from lollypop.menu_playlist import PlaylistMenu menu = PlaylistMenu(self.__playlist_ids[0]) popover = Gtk.Popover.new_from_model(button, menu) popover.popup() def _on_map(self, widget): """ Set active ids """ sidebar_content = App().settings.get_enum("sidebar-content") if sidebar_content != SidebarContent.GENRES: App().window.emit("show-can-go-back", True) App().window.emit("can-go-back-changed", True) App().settings.set_value("state-one-ids", GLib.Variant("ai", [Type.PLAYLISTS])) App().settings.set_value("state-two-ids", GLib.Variant("ai", self.__playlist_ids)) App().settings.set_value("state-three-ids", GLib.Variant("ai", [])) ####################### # PRIVATE # ####################### def __set_duration(self, duration): """ Set playlist duration @param duration as int (seconds) """ self.__duration_label.set_text(get_human_duration(duration)) def __update_jump_button(self): """ Update jump button status """ track_ids = [ child.track.id for child in self.__playlists_widget.children ] if App().player.current_track.id in track_ids: self.__jump_button.set_sensitive(True) else: self.__jump_button.set_sensitive(False) def __on_populated(self, playlists_widget): """ Update jump button on populated @param playlists_widget as PlaylistsWidget """ self.__update_jump_button() def __on_playlist_track_added(self, playlists, playlist_id, uri, pos): """ Update tracks widgets @param playlists as Playlists @param playlist_id as int @param uri as str @param pos as int """ if len(self.__playlist_ids) == 1 and\ playlist_id in self.__playlist_ids: track_id = App().tracks.get_id_by_uri(uri) self.__playlists_widget.append(track_id) def __on_playlist_track_removed(self, playlists, playlist_id, uri, pos): """ Update tracks widgets @param playlists as Playlists @param playlist_id as int @param uri as str @param pos as int """ if len(self.__playlist_ids) == 1 and\ playlist_id in self.__playlist_ids: track_id = App().tracks.get_id_by_uri(uri) self.__playlists_widget.remove(track_id, pos) def __on_playlist_populated(self, widget): """ Set duration on populated @param widget as PlaylistsWidget """ self.__set_duration(widget.duration) def __on_split_view_changed(self, settings, value): """ Split/Unsplit view @param settings as Gio.Settings @param value as GLib.Variant """ self.__playlists_widget.update_allocation()
def __init__(self, playlist_ids, list_type, editable=True): """ Init PlaylistView @parma playlist ids as [int] @param list_type as RowListType @param editable as bool """ View.__init__(self, True) ViewController.__init__(self, ViewControllerType.ALBUM) self.__list_type = list_type self.__playlist_ids = playlist_ids self.__signal_id1 = App().playlists.connect( "playlist-track-added", self.__on_playlist_track_added) self.__signal_id2 = App().playlists.connect( "playlist-track-removed", self.__on_playlist_track_removed) builder = Gtk.Builder() builder.add_from_resource("/org/gnome/Lollypop/PlaylistView.ui") self.__header = builder.get_object("header") self.__play_button = builder.get_object("play_button") self.__shuffle_button = builder.get_object("shuffle_button") if App().player.is_locked: self.__play_button.set_sensitive(False) self.__shuffle_button.set_sensitive(False) self.__duration_label = builder.get_object("duration") builder.get_object("title").set_label(", ".join( App().playlists.get_names(playlist_ids))) self.__jump_button = builder.get_object("jump_button") self.__split_button = builder.get_object("split_button") smart_button = builder.get_object("smart_button") if editable: self.__split_button.set_active( not App().settings.get_value("split-view")) else: self.__jump_button.set_hexpand(True) self.__split_button.hide() self.__play_button.hide() self.__shuffle_button.hide() if not editable or len(playlist_ids) > 1 or playlist_ids[0] < 0: smart_button.hide() self.__playlists_widget = PlaylistsWidget(playlist_ids, list_type) self.__playlists_widget.set_filter_func(self._filter_func) self.__playlists_widget.connect("populated", self.__on_populated) self.__playlists_widget.show() self.add(builder.get_object("widget")) self._viewport.add(self.__playlists_widget) self._scrolled.set_property("expand", True) self.add(self._scrolled) # Connect signals after ui init # "split-button" will emit a signal otherwise builder.connect_signals(self) # In DB duration calculation if playlist_ids[0] > 0: duration = 0 for playlist_id in self.__playlist_ids: duration += App().playlists.get_duration(playlist_id) self.__set_duration(duration) # Ask widget after populated else: self.__playlists_widget.connect("populated", self.__on_playlist_populated) self.__playlists_widget.connect("orientation-changed", self.__on_orientation_changed)
class PlaylistsView(View, ViewController): """ Show playlist tracks """ def __init__(self, playlist_ids, list_type, editable=True): """ Init PlaylistView @parma playlist ids as [int] @param list_type as RowListType @param editable as bool """ View.__init__(self, True) ViewController.__init__(self, ViewControllerType.ALBUM) self.__list_type = list_type self.__playlist_ids = playlist_ids self.__signal_id1 = App().playlists.connect( "playlist-track-added", self.__on_playlist_track_added) self.__signal_id2 = App().playlists.connect( "playlist-track-removed", self.__on_playlist_track_removed) builder = Gtk.Builder() builder.add_from_resource("/org/gnome/Lollypop/PlaylistView.ui") self.__header = builder.get_object("header") self.__play_button = builder.get_object("play_button") self.__shuffle_button = builder.get_object("shuffle_button") if App().player.is_locked: self.__play_button.set_sensitive(False) self.__shuffle_button.set_sensitive(False) self.__duration_label = builder.get_object("duration") builder.get_object("title").set_label(", ".join( App().playlists.get_names(playlist_ids))) self.__jump_button = builder.get_object("jump_button") self.__split_button = builder.get_object("split_button") smart_button = builder.get_object("smart_button") if editable: self.__split_button.set_active( not App().settings.get_value("split-view")) else: self.__jump_button.set_hexpand(True) self.__split_button.hide() self.__play_button.hide() self.__shuffle_button.hide() if not editable or len(playlist_ids) > 1 or playlist_ids[0] < 0: smart_button.hide() self.__playlists_widget = PlaylistsWidget(playlist_ids, list_type) self.__playlists_widget.set_filter_func(self._filter_func) self.__playlists_widget.connect("populated", self.__on_populated) self.__playlists_widget.show() self.add(builder.get_object("widget")) self._viewport.add(self.__playlists_widget) self._scrolled.set_property("expand", True) self.add(self._scrolled) # Connect signals after ui init # "split-button" will emit a signal otherwise builder.connect_signals(self) # In DB duration calculation if playlist_ids[0] > 0: duration = 0 for playlist_id in self.__playlist_ids: duration += App().playlists.get_duration(playlist_id) self.__set_duration(duration) # Ask widget after populated else: self.__playlists_widget.connect("populated", self.__on_playlist_populated) self.__playlists_widget.connect("orientation-changed", self.__on_orientation_changed) def populate(self, tracks): """ Populate view with tracks from playlist @param tracks as [track] """ self.__playlists_widget.populate(tracks) self.__update_jump_button() def stop(self): """ Stop populating """ self.__playlists_widget.stop() @property def playlist_ids(self): """ Return playlist ids @return id as [int] """ return self.__playlist_ids ####################### # PROTECTED # ####################### def _on_current_changed(self, player): """ Update children state @param player as Player """ self.__update_jump_button() self.__playlists_widget.set_playing_indicator() def _on_search_changed(self, entry): """ Update filter @param entry as Gtk.Entry """ self._filter = entry.get_text() for box in self.__playlists_widget.boxes: box.invalidate_filter() def _on_destroy(self, widget): """ Disconnect signals @param widget as Gtk.Widget """ View._on_destroy(self, widget) if self.__signal_id1: App().playlists.disconnect(self.__signal_id1) self.__signal_id1 = None if self.__signal_id2: App().playlists.disconnect(self.__signal_id2) self.__signal_id2 = None def _on_split_button_toggled(self, button): """ Split/Unsplit view """ App().settings.set_value("split-view", GLib.Variant("b", not button.get_active())) self.__playlists_widget.update_allocation() def _on_jump_button_clicked(self, button): """ Scroll to current track @param button as Gtk.Button """ y = self.__playlists_widget.get_current_ordinate() if y is not None: self._scrolled.get_vadjustment().set_value(y) def _on_save_button_clicked(self, button): """ Save playlist as file @param button as Gtk.Button """ filechooser = Gtk.FileChooserNative.new(_("Save playlist"), App().window, Gtk.FileChooserAction.SAVE, _("Save"), _("Cancel")) filter = Gtk.FileFilter.new() filter.set_name("audio/x-mpegurl") filter.add_mime_type("audio/x-mpegurl") filechooser.add_filter(filter) filechooser.set_do_overwrite_confirmation(True) name = ", ".join(App().playlists.get_names(self.__playlist_ids)) filechooser.set_current_name("%s.m3u" % name) filechooser.connect("response", self.__on_save_response) filechooser.run() def _on_play_button_clicked(self, button): """ Play playlist @param button as Gtk.Button """ tracks = [] for child in self.__playlists_widget.children: tracks.append(child.track) if tracks: App().player.load(tracks[0]) App().player.populate_playlist_by_tracks(tracks, self.__playlist_ids) def _on_shuffle_button_clicked(self, button): """ Play playlist shuffled @param button as Gtk.Button """ tracks = [] for child in self.__playlists_widget.children: tracks.append(child.track) if tracks: shuffle(tracks) App().player.load(tracks[0]) App().player.populate_playlist_by_tracks(tracks, self.__playlist_ids) def _on_smart_button_clicked(self, button): """ Edit smart playlist @param button as Gtk.Button """ App().window.container.show_smart_playlist_editor( self.__playlist_ids[0]) def _on_map(self, widget): """ Set active ids """ App().settings.set_value("state-one-ids", GLib.Variant("ai", [Type.PLAYLISTS])) App().settings.set_value("state-two-ids", GLib.Variant("ai", self.__playlist_ids)) ####################### # PRIVATE # ####################### def __set_duration(self, duration): """ Set playlist duration @param duration as int (seconds) """ hours = int(duration / 3600) mins = int(duration / 60) if hours > 0: mins -= hours * 60 if mins > 0: # Duration hour minute self.__duration_label.set_text(_("%s h %s m") % (hours, mins)) else: # Duration hour minute self.__duration_label.set_text(_("%s h") % hours) else: # Duration hour minute self.__duration_label.set_text(_("%s m") % mins) def __update_jump_button(self): """ Update jump button status """ track_ids = [ child.track.id for child in self.__playlists_widget.children ] if App().player.current_track.id in track_ids: self.__jump_button.set_sensitive(True) else: self.__jump_button.set_sensitive(False) def __on_save_response(self, dialog, response_id): """ Save playlist @param dialog as Gtk.NativeDialog @param response_id as int """ try: if response_id == Gtk.ResponseType.ACCEPT: uris = [] for box in self.__playlists_widget.boxes: for child in box.get_children(): uris.append(child.track.uri) stream = dialog.get_file().replace( None, False, Gio.FileCreateFlags.REPLACE_DESTINATION, None) stream.write("#EXTM3U\n".encode("utf-8")) for uri in uris: string = "%s\n" % uri stream.write(string.encode("utf-8")) stream.close() except: pass def __on_populated(self, playlists_widget): """ Update jump button on populated @param playlists_widget as PlaylistsWidget """ self.__update_jump_button() def __on_playlist_track_added(self, playlists, playlist_id, uri, pos): """ Update tracks widgets @param playlists as Playlists @param playlist id as int @param uri as str @param pos as int """ if len(self.__playlist_ids) == 1 and\ playlist_id in self.__playlist_ids: track_id = App().tracks.get_id_by_uri(uri) self.__playlists_widget.append(track_id) def __on_playlist_track_removed(self, playlists, playlist_id, uri, pos): """ Update tracks widgets @param playlists as Playlists @param playlist id as int @param uri as str @param pos as int """ if len(self.__playlist_ids) == 1 and\ playlist_id in self.__playlist_ids: track_id = App().tracks.get_id_by_uri(uri) self.__playlists_widget.remove(track_id, pos) def __on_playlist_populated(self, widget): """ Set duration on populated @param widget as PlaylistsWidget """ self.__set_duration(widget.duration) def __on_orientation_changed(self, widget, orientation): """ Show/Hide split button @param widget as Gtk.Widget @param orientation as Gtk.Orientation """ if orientation == Gtk.Orientation.VERTICAL and ( App().window.is_adaptive or self.__list_type & RowListType.POPOVER): self.__split_button.hide() else: self.__split_button.show()
class PlaylistsView(View): """ Show playlist tracks """ def __init__(self, playlist_ids, editable=True): """ Init PlaylistView @parma playlist ids as [int] @param editable as bool """ View.__init__(self, True) self.__tracks = [] self.__playlist_ids = playlist_ids self.__signal_id1 = Lp().playlists.connect("playlist-add", self.__on_playlist_add) self.__signal_id2 = Lp().playlists.connect("playlist-del", self.__on_playlist_del) builder = Gtk.Builder() builder.add_from_resource("/org/gnome/Lollypop/PlaylistView.ui") self.__duration_label = builder.get_object("duration") builder.get_object("title").set_label(", ".join( Lp().playlists.get_names(playlist_ids))) self.__edit_button = builder.get_object("edit-button") self.__jump_button = builder.get_object("jump-button") split_button = builder.get_object("split-button") if editable: split_button.set_active(not Lp().settings.get_value("split-view")) else: split_button.hide() if len(playlist_ids) > 1 or ( playlist_ids[0] < 0 and playlist_ids[0] not in [Type.LOVED, Type.NOPARTY]) or\ not editable: self.__edit_button.hide() self.__playlists_widget = PlaylistsWidget(playlist_ids) self.__playlists_widget.set_filter_func(self._filter_func) self.__playlists_widget.show() self.add(builder.get_object("widget")) self._viewport.add(self.__playlists_widget) self._scrolled.set_property("expand", True) self.add(self._scrolled) # Connect signals after ui init # "split-button" will emit a signal otherwise builder.connect_signals(self) # No duration for non user playlists # FIXME if playlist_ids[0] > 0: self.__set_duration() def populate(self, tracks): """ Populate view with tracks from playlist Thread safe """ # We are looking for middle # Ponderate with this: # Tracks with cover == 2 # Tracks without cover == 1 prev_album_id = None heights = {} total = 0 idx = 0 for track_id in tracks: track = Track(track_id) if track.album_id != prev_album_id: heights[idx] = 2 total += 2 else: heights[idx] = 1 total += 1 prev_album_id = track.album_id idx += 1 half = int(total / 2 + 0.5) mid_tracks = 1 count = 0 for height in heights.values(): count += height if count >= half: break mid_tracks += 1 self.__tracks = tracks self.__update_jump_button() self.__playlists_widget.populate_list_left(tracks[:mid_tracks], 1) self.__playlists_widget.populate_list_right(tracks[mid_tracks:], mid_tracks + 1) def get_ids(self): """ Return playlist ids @return id as [int] """ return self.__playlist_ids def stop(self): """ Stop populating """ self.__playlists_widget.stop() ####################### # PROTECTED # ####################### def _get_children(self): """ Return view children """ return [self.__playlists_widget] def _on_search_changed(self, entry): """ Update filter @param entry as Gtk.Entry """ self._filter = entry.get_text() for box in self.__playlists_widget.boxes: box.invalidate_filter() def _on_destroy(self, widget): """ Disconnect signals @param widget as Gtk.Widget """ View._on_destroy(self, widget) if self.__signal_id1: Lp().playlists.disconnect(self.__signal_id1) self.__signal_id1 = None if self.__signal_id2: Lp().playlists.disconnect(self.__signal_id2) self.__signal_id2 = None def _on_split_button_toggled(self, button): """ Split/Unsplit view """ Lp().settings.set_value("split-view", GLib.Variant("b", not button.get_active())) self.__playlists_widget.update_allocation() def _on_jump_button_clicked(self, button): """ Scroll to current track @param button as Gtk.Button """ y = self.__playlists_widget.get_current_ordinate() if y is not None: self._scrolled.get_vadjustment().set_value(y) def _on_edit_button_clicked(self, button): """ Edit playlist @param button as Gtk.Button """ Lp().window.show_playlist_editor(self.__playlist_ids[0]) def _on_current_changed(self, player): """ Current song changed, update playing button @param player as Player """ View._on_current_changed(self, player) self.__update_jump_button() ####################### # PRIVATE # ####################### def __set_duration(self): """ Set playlist duration """ duration = 0 for playlist_id in self.__playlist_ids: duration += Lp().playlists.get_duration(playlist_id) hours = int(duration / 3600) mins = int(duration / 60) if hours > 0: mins -= hours * 60 if mins > 0: self.__duration_label.set_text(_("%s h %s m") % (hours, mins)) else: self.__duration_label.set_text(_("%s h") % hours) else: self.__duration_label.set_text(_("%s m") % mins) def __update_jump_button(self): """ Update jump button status """ if Lp().player.current_track.id in self.__tracks: self.__jump_button.set_sensitive(True) artists = ", ".join(Lp().player.current_track.artists) self.__jump_button.set_tooltip_markup( "<b>%s</b>\n%s" % (GLib.markup_escape_text(artists), GLib.markup_escape_text(Lp().player.current_track.name))) else: self.__jump_button.set_sensitive(False) self.__jump_button.set_tooltip_text("") def __on_playlist_add(self, manager, playlist_id, track_id, pos): """ Update tracks widgets @param manager as PlaylistsManager @param playlist id as int @param track id as int """ if playlist_id in self.__playlist_ids: self.__playlists_widget.insert(track_id, pos) def __on_playlist_del(self, manager, playlist_id, track_id): """ Update tracks widgets @param manager as PlaylistsManager @param playlist id as int @param track id as int """ if playlist_id in self.__playlist_ids: self.__playlists_widget.remove(track_id)
class PlaylistsView(View): """ Show playlist tracks """ def __init__(self, playlist_ids, editable=True): """ Init PlaylistView @parma playlist ids as [int] @param editable as bool """ View.__init__(self) self._tracks = [] self._playlist_ids = playlist_ids self._signal_id1 = Lp().playlists.connect('playlist-add', self._on_playlist_add) self._signal_id2 = Lp().playlists.connect('playlist-del', self._on_playlist_del) builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/PlaylistView.ui') builder.connect_signals(self) name = "" for playlist_id in playlist_ids: if playlist_id == Type.POPULARS: name += _("Popular tracks")+", " elif playlist_id == Type.RECENTS: name += _("Recently played")+", " elif playlist_id == Type.NEVER: name += _("Never played")+", " elif playlist_id == Type.RANDOMS: name += _("Random tracks")+", " elif playlist_id == Type.SEARCH: name += _("Search")+", " else: name += Lp().playlists.get_name(playlist_id)+", " builder.get_object('title').set_label(name[:-2]) self._edit_button = builder.get_object('edit-button') self._jump_button = builder.get_object('jump-button') if len(playlist_ids) > 1 or ( playlist_ids[0] < 0 and playlist_ids[0] != Type.LOVED) or\ not editable: self._edit_button.hide() self._title = builder.get_object('title') self._playlists_widget = PlaylistsWidget(playlist_ids) self._playlists_widget.connect('populated', self._on_populated) self._playlists_widget.show() self.add(builder.get_object('widget')) self._viewport.add(self._playlists_widget) self._scrolled.set_property('expand', True) self.add(self._scrolled) def populate(self, tracks): """ Populate view with tracks from playlist Thread safe """ self._tracks = tracks mid_tracks = int(0.5+len(tracks)/2) self._playlists_widget.populate_list_left(tracks[:mid_tracks], 1) self._playlists_widget.populate_list_right(tracks[mid_tracks:], mid_tracks + 1) def get_ids(self): """ Return playlist ids @return id as [int] """ return self._playlist_ids def stop(self): """ Stop populating """ self._playlists_widget.stop() ####################### # PRIVATE # ####################### def _get_children(self): """ Return view children """ return [self._playlists_widget] def _update_jump_button(self): """ Update jump button status """ if Lp().player.current_track.id in self._tracks: self._jump_button.set_sensitive(True) self._jump_button.set_tooltip_markup( "<b>%s</b>\n%s" % (escape(Lp().player.current_track.artist_names), escape(Lp().player.current_track.name))) else: self._jump_button.set_sensitive(False) self._jump_button.set_tooltip_text('') def _on_playlist_add(self, manager, playlist_id, track_id): """ Update tracks widgets @param manager as PlaylistsManager @param playlist id as int @param track id as int """ if playlist_id in self._playlist_ids: self._playlists_widget.append(track_id) def _on_playlist_del(self, manager, playlist_id, track_id): """ Update tracks widgets @param manager as PlaylistsManager @param playlist id as int @param track id as int """ if playlist_id in self._playlist_ids: self._playlists_widget.remove(track_id) def _on_populated(self, widget): """ Show current track @param widget as Gtk.Widget """ self._update_jump_button() def _on_destroy(self, widget): """ Disconnect signals @param widget as Gtk.Widget """ View._on_destroy(self, widget) if self._signal_id1: Lp().playlists.disconnect(self._signal_id1) self._signal_id1 = None if self._signal_id2: Lp().playlists.disconnect(self._signal_id2) self._signal_id2 = None def _on_jump_button_clicked(self, button): """ Scroll to current track @param button as Gtk.Button """ y = self._playlists_widget.get_current_ordinate() if y is not None: self._scrolled.get_vadjustment().set_value(y) def _on_edit_button_clicked(self, button): """ Edit playlist @param button as Gtk.Button """ Lp().window.show_playlist_editor(self._playlist_ids[0]) def _on_current_changed(self, player): """ Current song changed, update playing button @param player as Player """ View._on_current_changed(self, player) self._update_jump_button()
class PlaylistsView(View): """ Show playlist tracks """ def __init__(self, playlist_ids, editable=True): """ Init PlaylistView @parma playlist ids as [int] @param editable as bool """ View.__init__(self) self._tracks = [] self._playlist_ids = playlist_ids self._signal_id1 = Lp().playlists.connect('playlist-add', self._on_playlist_add) self._signal_id2 = Lp().playlists.connect('playlist-del', self._on_playlist_del) builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/PlaylistView.ui') builder.connect_signals(self) builder.get_object('title').set_label( ", ".join(Lp().playlists.get_names(playlist_ids))) self._edit_button = builder.get_object('edit-button') self._jump_button = builder.get_object('jump-button') if len(playlist_ids) > 1 or ( playlist_ids[0] < 0 and playlist_ids[0] != Type.LOVED) or\ not editable: self._edit_button.hide() self._playlists_widget = PlaylistsWidget(playlist_ids) self._playlists_widget.show() self.add(builder.get_object('widget')) self._viewport.add(self._playlists_widget) self._scrolled.set_property('expand', True) self.add(self._scrolled) def populate(self, tracks): """ Populate view with tracks from playlist Thread safe """ self._tracks = tracks self._update_jump_button() mid_tracks = int(0.5+len(tracks)/2) self._playlists_widget.populate_list_left(tracks[:mid_tracks], 1) self._playlists_widget.populate_list_right(tracks[mid_tracks:], mid_tracks + 1) def get_ids(self): """ Return playlist ids @return id as [int] """ return self._playlist_ids def stop(self): """ Stop populating """ self._playlists_widget.stop() ####################### # PRIVATE # ####################### def _get_children(self): """ Return view children """ return [self._playlists_widget] def _update_jump_button(self): """ Update jump button status """ if Lp().player.current_track.id in self._tracks: self._jump_button.set_sensitive(True) self._jump_button.set_tooltip_markup( "<b>%s</b>\n%s" % (escape(Lp().player.current_track.artists), escape(Lp().player.current_track.name))) else: self._jump_button.set_sensitive(False) self._jump_button.set_tooltip_text('') def _on_playlist_add(self, manager, playlist_id, track_id): """ Update tracks widgets @param manager as PlaylistsManager @param playlist id as int @param track id as int """ if playlist_id in self._playlist_ids: self._playlists_widget.append(track_id) def _on_playlist_del(self, manager, playlist_id, track_id): """ Update tracks widgets @param manager as PlaylistsManager @param playlist id as int @param track id as int """ if playlist_id in self._playlist_ids: self._playlists_widget.remove(track_id) def _on_populated(self, widget): """ Show current track @param widget as LazyLoadingView """ self._update_jump_button() def _on_destroy(self, widget): """ Disconnect signals @param widget as Gtk.Widget """ View._on_destroy(self, widget) if self._signal_id1: Lp().playlists.disconnect(self._signal_id1) self._signal_id1 = None if self._signal_id2: Lp().playlists.disconnect(self._signal_id2) self._signal_id2 = None def _on_jump_button_clicked(self, button): """ Scroll to current track @param button as Gtk.Button """ y = self._playlists_widget.get_current_ordinate() if y is not None: self._scrolled.get_vadjustment().set_value(y) def _on_edit_button_clicked(self, button): """ Edit playlist @param button as Gtk.Button """ Lp().window.show_playlist_editor(self._playlist_ids[0]) def _on_current_changed(self, player): """ Current song changed, update playing button @param player as Player """ View._on_current_changed(self, player) self._update_jump_button()
class PlaylistsView(View): """ Show playlist tracks """ def __init__(self, playlist_ids, editable=True): """ Init PlaylistView @parma playlist ids as [int] @param editable as bool """ View.__init__(self) self._tracks = [] self._playlist_ids = playlist_ids self._signal_id1 = Lp().playlists.connect("playlist-add", self._on_playlist_add) self._signal_id2 = Lp().playlists.connect("playlist-del", self._on_playlist_del) builder = Gtk.Builder() builder.add_from_resource("/org/gnome/Lollypop/PlaylistView.ui") builder.connect_signals(self) builder.get_object("title").set_label(", ".join(Lp().playlists.get_names(playlist_ids))) self._edit_button = builder.get_object("edit-button") self._jump_button = builder.get_object("jump-button") if len(playlist_ids) > 1 or (playlist_ids[0] < 0 and playlist_ids[0] != Type.LOVED) or not editable: self._edit_button.hide() self._playlists_widget = PlaylistsWidget(playlist_ids) self._playlists_widget.show() self.add(builder.get_object("widget")) self._viewport.add(self._playlists_widget) self._scrolled.set_property("expand", True) self.add(self._scrolled) def populate(self, tracks): """ Populate view with tracks from playlist Thread safe """ # We are looking for middle # Ponderate with this: # Tracks with cover == 2 # Tracks without cover == 1 prev_album_id = None heights = {} total = 0 idx = 0 for track_id in tracks: track = Track(track_id) if track.album_id != prev_album_id: heights[idx] = 2 total += 2 else: heights[idx] = 1 total += 1 prev_album_id = track.album_id idx += 1 half = int(total / 2 + 0.5) mid_tracks = 1 count = 0 for height in heights.values(): count += height if count >= half: break mid_tracks += 1 self._tracks = tracks self._update_jump_button() self._playlists_widget.populate_list_left(tracks[:mid_tracks], 1) self._playlists_widget.populate_list_right(tracks[mid_tracks:], mid_tracks + 1) def get_ids(self): """ Return playlist ids @return id as [int] """ return self._playlist_ids def stop(self): """ Stop populating """ self._playlists_widget.stop() ####################### # PRIVATE # ####################### def _get_children(self): """ Return view children """ return [self._playlists_widget] def _update_jump_button(self): """ Update jump button status """ if Lp().player.current_track.id in self._tracks: self._jump_button.set_sensitive(True) artists = ", ".join(Lp().player.current_track.artists) self._jump_button.set_tooltip_markup( "<b>%s</b>\n%s" % (escape(artists), escape(Lp().player.current_track.name)) ) else: self._jump_button.set_sensitive(False) self._jump_button.set_tooltip_text("") def _on_playlist_add(self, manager, playlist_id, track_id): """ Update tracks widgets @param manager as PlaylistsManager @param playlist id as int @param track id as int """ if playlist_id in self._playlist_ids: self._playlists_widget.append(track_id) def _on_playlist_del(self, manager, playlist_id, track_id): """ Update tracks widgets @param manager as PlaylistsManager @param playlist id as int @param track id as int """ if playlist_id in self._playlist_ids: self._playlists_widget.remove(track_id) def _on_destroy(self, widget): """ Disconnect signals @param widget as Gtk.Widget """ View._on_destroy(self, widget) if self._signal_id1: Lp().playlists.disconnect(self._signal_id1) self._signal_id1 = None if self._signal_id2: Lp().playlists.disconnect(self._signal_id2) self._signal_id2 = None def _on_jump_button_clicked(self, button): """ Scroll to current track @param button as Gtk.Button """ y = self._playlists_widget.get_current_ordinate() if y is not None: self._scrolled.get_vadjustment().set_value(y) def _on_edit_button_clicked(self, button): """ Edit playlist @param button as Gtk.Button """ Lp().window.show_playlist_editor(self._playlist_ids[0]) def _on_current_changed(self, player): """ Current song changed, update playing button @param player as Player """ View._on_current_changed(self, player) self._update_jump_button()
class PlaylistsView(View): """ Show playlist tracks """ def __init__(self, playlist_ids, editable=True): """ Init PlaylistView @parma playlist ids as [int] @param editable as bool """ View.__init__(self, True) self.__tracks = [] self.__playlist_ids = playlist_ids self.__signal_id1 = Lp().playlists.connect('playlist-add', self.__on_playlist_add) self.__signal_id2 = Lp().playlists.connect('playlist-del', self.__on_playlist_del) builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/PlaylistView.ui') builder.get_object('title').set_label( ", ".join(Lp().playlists.get_names(playlist_ids))) self.__edit_button = builder.get_object('edit-button') self.__jump_button = builder.get_object('jump-button') split_button = builder.get_object('split-button') if editable: split_button.set_active(not Lp().settings.get_value('split-view')) else: split_button.hide() if len(playlist_ids) > 1 or ( playlist_ids[0] < 0 and playlist_ids[0] != Type.LOVED) or\ not editable: self.__edit_button.hide() self.__playlists_widget = PlaylistsWidget(playlist_ids) self.__playlists_widget.set_filter_func(self._filter_func) self.__playlists_widget.show() self.add(builder.get_object('widget')) self._viewport.add(self.__playlists_widget) self._scrolled.set_property('expand', True) self.add(self._scrolled) # Connect signals after ui init # 'split-button' will emit a signal otherwise builder.connect_signals(self) def populate(self, tracks): """ Populate view with tracks from playlist Thread safe """ # We are looking for middle # Ponderate with this: # Tracks with cover == 2 # Tracks without cover == 1 prev_album_id = None heights = {} total = 0 idx = 0 for track_id in tracks: track = Track(track_id) if track.album_id != prev_album_id: heights[idx] = 2 total += 2 else: heights[idx] = 1 total += 1 prev_album_id = track.album_id idx += 1 half = int(total / 2 + 0.5) mid_tracks = 1 count = 0 for height in heights.values(): count += height if count >= half: break mid_tracks += 1 self.__tracks = tracks self.__update_jump_button() self.__playlists_widget.populate_list_left(tracks[:mid_tracks], 1) self.__playlists_widget.populate_list_right(tracks[mid_tracks:], mid_tracks + 1) def get_ids(self): """ Return playlist ids @return id as [int] """ return self.__playlist_ids def stop(self): """ Stop populating """ self.__playlists_widget.stop() ####################### # PROTECTED # ####################### def _get_children(self): """ Return view children """ return [self.__playlists_widget] def _on_search_changed(self, entry): """ Update filter @param entry as Gtk.Entry """ self._filter = entry.get_text() for box in self.__playlists_widget.boxes: box.invalidate_filter() def _on_destroy(self, widget): """ Disconnect signals @param widget as Gtk.Widget """ View._on_destroy(self, widget) if self.__signal_id1: Lp().playlists.disconnect(self.__signal_id1) self.__signal_id1 = None if self.__signal_id2: Lp().playlists.disconnect(self.__signal_id2) self.__signal_id2 = None def _on_split_button_toggled(self, button): """ Split/Unsplit view """ Lp().settings.set_value('split-view', GLib.Variant('b', not button.get_active())) self.__playlists_widget.update_allocation() def _on_jump_button_clicked(self, button): """ Scroll to current track @param button as Gtk.Button """ y = self.__playlists_widget.get_current_ordinate() if y is not None: self._scrolled.get_vadjustment().set_value(y) def _on_edit_button_clicked(self, button): """ Edit playlist @param button as Gtk.Button """ Lp().window.show_playlist_editor(self.__playlist_ids[0]) def _on_current_changed(self, player): """ Current song changed, update playing button @param player as Player """ View._on_current_changed(self, player) self.__update_jump_button() ####################### # PRIVATE # ####################### def __update_jump_button(self): """ Update jump button status """ if Lp().player.current_track.id in self.__tracks: self.__jump_button.set_sensitive(True) artists = ", ".join(Lp().player.current_track.artists) self.__jump_button.set_tooltip_markup( "<b>%s</b>\n%s" % (GLib.markup_escape_text(artists), GLib.markup_escape_text( Lp().player.current_track.name))) else: self.__jump_button.set_sensitive(False) self.__jump_button.set_tooltip_text('') def __on_playlist_add(self, manager, playlist_id, track_id, pos): """ Update tracks widgets @param manager as PlaylistsManager @param playlist id as int @param track id as int """ if playlist_id in self.__playlist_ids: self.__playlists_widget.insert(track_id, pos) def __on_playlist_del(self, manager, playlist_id, track_id): """ Update tracks widgets @param manager as PlaylistsManager @param playlist id as int @param track id as int """ if playlist_id in self.__playlist_ids: self.__playlists_widget.remove(track_id)