[gnome-music/wip/mschraal/core] playlistsview: Restore play song from the context menu
- From: Jean Felder <jfelder src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-music/wip/mschraal/core] playlistsview: Restore play song from the context menu
- Date: Mon, 8 Jul 2019 18:07:51 +0000 (UTC)
commit e489edbb1adc197540d167527195a65bfa5344e8
Author: Jean Felder <jfelder src gnome org>
Date: Mon Jul 8 18:57:07 2019 +0200
playlistsview: Restore play song from the context menu
Also, move all the deletion logic into the NotificationsPopup.
gnomemusic/grilowrappers/grltrackerplaylists.py | 100 +++++++++++++++++++++++-
gnomemusic/views/playlistsview.py | 22 +++++-
gnomemusic/widgets/notificationspopup.py | 57 ++++++++++----
3 files changed, 157 insertions(+), 22 deletions(-)
---
diff --git a/gnomemusic/grilowrappers/grltrackerplaylists.py b/gnomemusic/grilowrappers/grltrackerplaylists.py
index 198c3cdd..b8d74888 100644
--- a/gnomemusic/grilowrappers/grltrackerplaylists.py
+++ b/gnomemusic/grilowrappers/grltrackerplaylists.py
@@ -160,6 +160,8 @@ class Playlist(GObject.GObject):
self._fast_options.set_resolution_flags(
Grl.ResolutionFlags.FAST_ONLY | Grl.ResolutionFlags.IDLE_RELAY)
+ self._songs_todelete = []
+
@GObject.Property(type=Gio.ListStore, default=None)
def model(self):
if self._model is None:
@@ -220,7 +222,8 @@ class Playlist(GObject.GObject):
return
coresong = CoreSong(media, self._coreselection, self._grilo)
- self._model.append(coresong)
+ if coresong not in self._songs_todelete:
+ self._model.append(coresong)
options = Grl.OperationOptions()
options.set_resolution_flags(
@@ -262,6 +265,101 @@ class Playlist(GObject.GObject):
self._tracker.update_async(
query, GLib.PRIORITY_LOW, None, update_cb, None)
+ def stage_deletion(self, coresong, index):
+ """Adds a song to the list of songs to delete
+
+ :param CoreSong coresong: song to delete
+ :param int position: Song position in the playlist
+ """
+ self._songs_todelete.append(coresong)
+ self._model.remove(index)
+ self.props.count -= 1
+
+ def undo_pending_deletion(self, coresong, position):
+ """Adds a song to the list of songs to delete
+
+ :param CoreSong coresong: song to delete
+ :param int position: Song position in the playlist
+ """
+ self._songs_todelete.remove(coresong)
+ self._model.insert(position, coresong)
+ self.props.count += 1
+
+ def finish_deletion(self, coresong):
+ """Removes a song from the playlist
+
+ :param CoreSong coresong: song to remove
+ """
+
+ def update_cb(conn, res, data):
+ # FIXME: Check for failure.
+ conn.update_finish(res)
+
+ query = """
+ INSERT OR REPLACE {
+ ?entry nfo:listPosition ?position .
+
+ }
+ WHERE {
+ SELECT ?entry
+ (?old_position - 1) AS ?position
+ WHERE {
+ ?entry a nfo:MediaFileListEntry ;
+ nfo:listPosition ?old_position .
+ ?playlist nfo:hasMediaFileListEntry ?entry .
+ FILTER (?old_position > ?removed_position)
+ {
+ SELECT ?playlist
+ ?removed_position
+ WHERE {
+ ?playlist a nmm:Playlist ;
+ a nfo:MediaList ;
+ nfo:hasMediaFileListEntry ?removed_entry .
+ ?removed_entry nfo:listPosition ?removed_position .
+ FILTER (
+ tracker:id(?playlist) = %(playlist_id)s &&
+ tracker:id(?removed_entry) = %(song_id)s
+ )
+ }
+ }
+ }
+ }
+ INSERT OR REPLACE {
+ ?playlist nfo:entryCounter ?new_counter .
+ }
+ WHERE {
+ SELECT ?playlist
+ (?counter - 1) AS ?new_counter
+ WHERE {
+ ?playlist a nmm:Playlist ;
+ a nfo:MediaList ;
+ nfo:entryCounter ?counter .
+ FILTER (
+ tracker:id(?playlist) = %(playlist_id)s
+ )
+ }
+ }
+ DELETE {
+ ?playlist nfo:hasMediaFileListEntry ?entry .
+ ?entry a rdfs:Resource .
+ }
+ WHERE {
+ ?playlist a nmm:Playlist ;
+ a nfo:MediaList ;
+ nfo:hasMediaFileListEntry ?entry .
+ FILTER (
+ tracker:id(?playlist) = %(playlist_id)s &&
+ tracker:id(?entry) = %(song_id)s
+ )
+ }
+ """.replace("\n", " ").strip() % {
+ "playlist_id": self.props.pl_id,
+ "song_id": coresong.props.media.get_id()
+ }
+
+ self._tracker.update_async(
+ query, GLib.PRIORITY_LOW, None, update_cb, None)
+
class SmartPlaylist(Playlist):
"""Base class for smart playlists"""
diff --git a/gnomemusic/views/playlistsview.py b/gnomemusic/views/playlistsview.py
index df46ffc8..d1ee4880 100644
--- a/gnomemusic/views/playlistsview.py
+++ b/gnomemusic/views/playlistsview.py
@@ -29,6 +29,7 @@ from gi.repository import Gdk, GObject, Gio, Gtk
from gnomemusic import log
from gnomemusic.player import PlayerPlaylist
from gnomemusic.views.baseview import BaseView
+from gnomemusic.widgets.notificationspopup import PlaylistNotification
from gnomemusic.widgets.playlistcontextmenu import PlaylistContextMenu
from gnomemusic.widgets.playlistcontrols import PlaylistControls
from gnomemusic.widgets.sidebarrow import SidebarRow
@@ -78,10 +79,10 @@ class PlaylistsView(BaseView):
# add_song_to_playlist.connect('activate', self._add_song_to_playlist)
# self._window.add_action(add_song_to_playlist)
- # self._remove_song_action = Gio.SimpleAction.new('remove_song', None)
- # self._remove_song_action.connect(
- # 'activate', self._stage_song_for_deletion)
- # self._window.add_action(self._remove_song_action)
+ self._remove_song_action = Gio.SimpleAction.new('remove_song', None)
+ self._remove_song_action.connect(
+ 'activate', self._stage_song_for_deletion)
+ self._window.add_action(self._remove_song_action)
# playlist_play_action = Gio.SimpleAction.new('playlist_play', None)
# playlist_play_action.connect('activate', self._on_play_activate)
@@ -180,6 +181,19 @@ class PlaylistsView(BaseView):
self._view.unselect_all()
self._song_activated(song_widget, None)
+ @log
+ def _stage_song_for_deletion(self, menuitem, data=None):
+ selected_row = self._view.get_selected_row()
+ position = selected_row.get_index()
+ song_widget = selected_row.get_child()
+ coresong = song_widget.props.coresong
+
+ selected_playlist = self._sidebar.get_selected_row().playlist
+
+ notification = PlaylistNotification( # noqa: F841
+ self._window.notifications_popup, PlaylistNotification.Type.SONG,
+ selected_playlist, coresong, position)
+
@log
def _on_playlist_activated(self, sidebar, row, data=None):
"""Update view with content from selected playlist"""
diff --git a/gnomemusic/widgets/notificationspopup.py b/gnomemusic/widgets/notificationspopup.py
index 08b2750a..061a811e 100644
--- a/gnomemusic/widgets/notificationspopup.py
+++ b/gnomemusic/widgets/notificationspopup.py
@@ -123,14 +123,12 @@ class NotificationsPopup(Gtk.Revealer):
self.set_reveal_child(True)
@log
- def remove_notification(self, notification, signal):
- """Remove notification and emit a signal.
+ def remove_notification(self, notification):
+ """Removes notification.
:param notification: notification to remove
- :param signal: signal to emit: deletion or undo action
"""
self._set_visibility(notification, True)
- notification.emit(signal)
@log
def terminate_pending(self):
@@ -138,7 +136,7 @@ class NotificationsPopup(Gtk.Revealer):
children = self._grid.get_children()
if len(children) > 1:
for notification in children[:-1]:
- self.remove_notification(notification, 'finish-deletion')
+ notification._finish_deletion()
class LoadingNotification(Gtk.Grid):
@@ -211,41 +209,66 @@ class PlaylistNotification(Gtk.Grid):
PLAYLIST = 0
SONG = 1
- __gsignals__ = {
- 'undo-deletion': (GObject.SignalFlags.RUN_FIRST, None, ()),
- 'finish-deletion': (GObject.SignalFlags.RUN_FIRST, None, ())
- }
-
def __repr__(self):
return '<PlaylistNotification>'
@log
- def __init__(self, notifications_popup, type_, message, data):
+ def __init__(self, notifications_popup, type_, playlist, data, position):
+ """Creates a playlist deletion notification popup (song or playlist)
+
+ :param GtkRevealer notifications_popup: the popup object
+ :param type_: NotificationType (song or playlist)
+ :param Playlist playlist: playlist
+ :param object data: Data associated with the deletion
+ :param int position: position of the object to delete
+ """
super().__init__(column_spacing=18)
self._notifications_popup = notifications_popup
self.type_ = type_
+ self._playlist = playlist
self.data = data
+ self._position = position
+ message = self._create_notification_message()
self._label = Gtk.Label(
label=message, halign=Gtk.Align.START, hexpand=True)
self.add(self._label)
undo_button = Gtk.Button.new_with_mnemonic(_("_Undo"))
- undo_button.connect("clicked", self._undo_clicked)
+ undo_button.connect("clicked", self._undo_deletion)
self.add(undo_button)
self.show_all()
- self._timeout_id = GLib.timeout_add_seconds(
- 5, self._notifications_popup.remove_notification, self,
- 'finish-deletion')
+ if self.type_ == PlaylistNotification.Type.SONG:
+ playlist.stage_deletion(self.data, position)
+ self._timeout_id = GLib.timeout_add_seconds(5, self._finish_deletion)
self._notifications_popup.add_notification(self)
+ def _create_notification_message(self):
+ if self.type_ == PlaylistNotification.Type.PLAYLIST:
+ return None
+ # pl_todelete = data
+ # msg = _("Playlist {} removed".format(pl_todelete.props.title))
+
+ else:
+ playlist_title = self._playlist.props.title
+ coresong = self.data
+ song_title = coresong.props.title
+ msg = _("{} removed from {}".format(
+ song_title, playlist_title))
+ return msg
+
@log
- def _undo_clicked(self, widget_):
+ def _undo_deletion(self, widget_):
"""Undo deletion and remove notification"""
if self._timeout_id > 0:
GLib.source_remove(self._timeout_id)
self._timeout_id = 0
- self._notifications_popup.remove_notification(self, 'undo-deletion')
+ self._notifications_popup.remove_notification(self)
+ self._playlist.undo_pending_deletion(self.data, self._position)
+
+ def _finish_deletion(self):
+ self._notifications_popup.remove_notification(self)
+ self._playlist.finish_deletion(self.data)
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]