Commit e489edbb authored by Jean Felder's avatar Jean Felder

playlistsview: Restore play song from the context menu

Also, move all the deletion logic into the NotificationsPopup.
parent 3f59f051
Pipeline #93791 passed with stages
in 12 minutes and 42 seconds
......@@ -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"""
......
......@@ -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"""
......
......@@ -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)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment