[gnome-music/wip/jfelder/core-playlists-view] playlistsview: Restore drag and drop operation
- From: Jean Felder <jfelder src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-music/wip/jfelder/core-playlists-view] playlistsview: Restore drag and drop operation
- Date: Tue, 9 Jul 2019 22:15:09 +0000 (UTC)
commit abad7574dfbb9929d3a5a730a637dafde822680c
Author: Jean Felder <jfelder src gnome org>
Date: Tue Jul 9 21:16:41 2019 +0200
playlistsview: Restore drag and drop operation
data/ui/SongWidget.ui | 24 ++++++++--
gnomemusic/grilowrappers/grltrackerplaylists.py | 43 ++++++++++++++++++
gnomemusic/views/playlistsview.py | 15 +++++--
gnomemusic/widgets/disclistboxwidget.py | 2 -
gnomemusic/widgets/songwidget.py | 58 ++++++++++++++++++++++++-
5 files changed, 133 insertions(+), 9 deletions(-)
---
diff --git a/data/ui/SongWidget.ui b/data/ui/SongWidget.ui
index 28f4675a..8e1987a6 100644
--- a/data/ui/SongWidget.ui
+++ b/data/ui/SongWidget.ui
@@ -6,11 +6,29 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<signal name="notify::selected" handler="_on_selection_changed"/>
+ <signal name="drag_data_received" handler="_on_drag_data_received"/>
<child>
<object class="GtkBox" id="box1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">3</property>
+ <child>
+ <object class="GtkEventBox" id="_dnd_eventbox">
+ <property name="visible">False</property>
+ <signal name="drag-begin" handler="_on_drag_begin"/>
+ <signal name="drag-end" handler="_on_drag_end"/>
+ <signal name="drag_data_get" handler="_on_drag_data_get"/>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="icon-name">open-menu-symbolic</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
<child>
<object class="GtkBox" id="box3">
<property name="width_request">48</property>
@@ -61,7 +79,7 @@
</child>
</object>
<packing>
- <property name="position">0</property>
+ <property name="position">1</property>
</packing>
</child>
<child>
@@ -103,7 +121,7 @@
</child>
</object>
<packing>
- <property name="position">1</property>
+ <property name="position">2</property>
</packing>
</child>
<child>
@@ -127,7 +145,7 @@
</child>
</object>
<packing>
- <property name="position">2</property>
+ <property name="position">3</property>
</packing>
</child>
<child>
diff --git a/gnomemusic/grilowrappers/grltrackerplaylists.py b/gnomemusic/grilowrappers/grltrackerplaylists.py
index 4553f5a7..94a3975d 100644
--- a/gnomemusic/grilowrappers/grltrackerplaylists.py
+++ b/gnomemusic/grilowrappers/grltrackerplaylists.py
@@ -549,6 +549,49 @@ class Playlist(GObject.GObject):
self._tracker.update_blank_async(
query, GLib.PRIORITY_LOW, None, _requery_media, coresong)
+ def reorder(self, previous_position, new_position):
+ """Changes the order of a songs in the playlist.
+
+ :param int previous_position: preivous song position
+ :param int new_position: new song position
+ """
+ def _position_changed_cb(conn, res, position):
+ # FIXME: Check for failure.
+ conn.update_finish(res)
+
+ coresong = self._model.get_item(previous_position)
+ self._model.remove(previous_position)
+ self._model.insert(new_position, coresong)
+
+ main_query = """
+ INSERT OR REPLACE {
+ ?entry
+ nfo:listPosition %(position)s
+ }
+ 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()
+
+ first_pos = min(previous_position, new_position)
+ last_pos = max(previous_position, new_position)
+
+ for position in range(first_pos, last_pos + 1):
+ coresong = self._model.get_item(position)
+ query = main_query % {
+ "playlist_id": self.props.pl_id,
+ "song_id": coresong.props.media.get_id(),
+ "position": position
+ }
+ self._tracker.update_async(
+ query, GLib.PRIORITY_LOW, None, _position_changed_cb,
+ position)
class SmartPlaylist(Playlist):
"""Base class for smart playlists"""
diff --git a/gnomemusic/views/playlistsview.py b/gnomemusic/views/playlistsview.py
index b389e8c3..bcd83a74 100644
--- a/gnomemusic/views/playlistsview.py
+++ b/gnomemusic/views/playlistsview.py
@@ -230,7 +230,8 @@ class PlaylistsView(BaseView):
if self.rename_active:
self._pl_ctrls.disable_rename_playlist()
- self._view.bind_model(playlist.props.model, self._create_song_widget)
+ self._view.bind_model(
+ playlist.props.model, self._create_song_widget, playlist)
self._current_playlist = playlist
self._pl_ctrls.props.playlist_name = playlist_name
@@ -243,11 +244,14 @@ class PlaylistsView(BaseView):
def _on_song_count_changed(self, playlist, value):
self._update_songs_count(playlist.props.count)
- def _create_song_widget(self, coresong):
- song_widget = SongWidget(coresong)
+ def _create_song_widget(self, coresong, playlist):
+ can_dnd = not playlist.props.is_smart
+ song_widget = SongWidget(coresong, can_dnd)
song_widget.props.show_song_number = False
song_widget.connect('button-release-event', self._song_activated)
+ if can_dnd is True:
+ song_widget.connect("widget_moved", self._on_song_widget_moved)
return song_widget
@@ -300,6 +304,11 @@ class PlaylistsView(BaseView):
# self.player.stop()
# self._window.set_player_visible(False)
+ def _on_song_widget_moved(self, target, source_position):
+ target_position = target.get_parent().get_index()
+ current_playlist = self._sidebar.get_selected_row().playlist
+ current_playlist.reorder(source_position, target_position)
+
@log
def _populate(self, data=None):
"""Populate sidebar.
diff --git a/gnomemusic/widgets/disclistboxwidget.py b/gnomemusic/widgets/disclistboxwidget.py
index 02c3c1a6..7de001d4 100644
--- a/gnomemusic/widgets/disclistboxwidget.py
+++ b/gnomemusic/widgets/disclistboxwidget.py
@@ -139,8 +139,6 @@ class DiscBox(Gtk.Box):
song_widget.connect('button-release-event', self._song_activated)
- song_widget.show_all()
-
return song_widget
@log
diff --git a/gnomemusic/widgets/songwidget.py b/gnomemusic/widgets/songwidget.py
index 35b59fa8..cc12a022 100644
--- a/gnomemusic/widgets/songwidget.py
+++ b/gnomemusic/widgets/songwidget.py
@@ -53,6 +53,7 @@ class SongWidget(Gtk.EventBox):
__gsignals__ = {
'selection-changed': (GObject.SignalFlags.RUN_FIRST, None, ()),
+ "widget-moved": (GObject.SignalFlags.RUN_FIRST, None, (int,))
}
coresong = GObject.Property(type=CoreSong, default=None)
@@ -63,6 +64,7 @@ class SongWidget(Gtk.EventBox):
_playlists = Playlists.get_default()
+ _dnd_eventbox = Gtk.Template.Child()
_select_button = Gtk.Template.Child()
_number_label = Gtk.Template.Child()
_title_label = Gtk.Template.Child()
@@ -82,7 +84,12 @@ class SongWidget(Gtk.EventBox):
return '<SongWidget>'
@log
- def __init__(self, coresong):
+ def __init__(self, coresong, can_dnd=False):
+ """Instanciates a SongWidget
+
+ :param Corsong coresong: song associated with the widget
+ :param bool can_dnd: allow drag and drop operations
+ """
super().__init__()
self.props.coresong = coresong
@@ -134,11 +141,60 @@ class SongWidget(Gtk.EventBox):
self._number_label.props.no_show_all = True
+ if can_dnd is True:
+ self._dnd_eventbox.props.visible = True
+ self._drag_widget = None
+ entries = [
+ Gtk.TargetEntry.new(
+ "GTK_EVENT_BOX", Gtk.TargetFlags.SAME_APP, 0)
+ ]
+ self._dnd_eventbox.drag_source_set(
+ Gdk.ModifierType.BUTTON1_MASK, entries,
+ Gdk.DragAction.MOVE)
+ self.drag_dest_set(
+ Gtk.DestDefaults.ALL, entries, Gdk.DragAction.MOVE)
+
@Gtk.Template.Callback()
@log
def _on_selection_changed(self, klass, value):
self.emit('selection-changed')
+ @Gtk.Template.Callback()
+ def _on_drag_begin(self, klass, context):
+ gdk_window = self.get_window()
+ _, x, y, _ = gdk_window.get_device_position(context.get_device())
+ allocation = self.get_allocation()
+
+ self._drag_widget = Gtk.ListBox()
+ self._drag_widget.set_size_request(allocation.width, allocation.height)
+
+ drag_row = SongWidget(self.props.coresong)
+ self._drag_widget.add(drag_row)
+ self._drag_widget.drag_highlight_row(drag_row.get_parent())
+ self._drag_widget.show_all()
+ Gtk.drag_set_icon_widget(context, self._drag_widget, x, y)
+
+ @Gtk.Template.Callback()
+ def _on_drag_end(self, klass, context):
+ self._drag_widget = None
+
+ @Gtk.Template.Callback()
+ def _on_drag_data_get(self, klass, context, selection_data, info, time_):
+ row_position = self.get_parent().get_index()
+ selection_data.set(
+ Gdk.Atom.intern("row_position", False), 0,
+ bytes(str(row_position), encoding="UTF8"))
+
+ @Gtk.Template.Callback()
+ def _on_drag_data_received(
+ self, klass, context, x, y, selection_data, info, time_):
+ source_position = int(str(selection_data.get_data(), "UTF-8"))
+ target_position = self.get_parent().get_index()
+ if source_position == target_position:
+ return
+
+ self.emit("widget-moved", source_position)
+
@Gtk.Template.Callback()
@log
def _on_star_toggle(self, widget, event):
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]