chiark / gitweb /
Multiple-track drag+drop queue rearrangement.
authorRichard Kettlewell <rjk@greenend.org.uk>
Fri, 13 Nov 2009 19:45:53 +0000 (19:45 +0000)
committerRichard Kettlewell <rjk@greenend.org.uk>
Fri, 13 Nov 2009 19:45:53 +0000 (19:45 +0000)
CHANGES.html
disobedience/Makefile.am
disobedience/disobedience.h
disobedience/multidrag.c [new file with mode: 0644]
disobedience/queue-generic.c

index 8b0b28f..38f14e9 100644 (file)
@@ -82,6 +82,15 @@ span.command {
       
     </div>
       
+    <h3>Disobedience</h3>
+
+    <div class=section>
+
+      <p>Multiple tracks can now be dragged in the queue in a single
+      operation.</p>
+
+    </div>
+
     <h3>Web Interface</h3>
  
     <div class=section>
index f8bdb14..353b145 100644 (file)
@@ -28,7 +28,7 @@ disobedience_SOURCES=disobedience.h disobedience.c client.c queue.c   \
        choose.c choose-menu.c choose-search.c popup.c misc.c           \
        control.c properties.c menu.c log.c progress.c login.c rtp.c    \
        help.c ../lib/memgc.c settings.c users.c lookup.c choose.h      \
-       popup.h playlists.c
+       popup.h playlists.c multidrag.c
 disobedience_LDADD=../lib/libdisorder.a $(LIBPCRE) $(LIBGC) $(LIBGCRYPT) \
        $(LIBASOUND) $(COREAUDIO) $(LIBDB) $(LIBICONV)
 disobedience_LDFLAGS=$(GTK_LIBS)
index f4678c7..404358a 100644 (file)
@@ -266,6 +266,8 @@ extern GtkWidget *playlists_menu;
 extern GtkWidget *editplaylists_widget;
 #endif
 
+void make_treeview_multidrag(GtkWidget *w);
+
 #endif /* DISOBEDIENCE_H */
 
 /*
diff --git a/disobedience/multidrag.c b/disobedience/multidrag.c
new file mode 100644 (file)
index 0000000..95a133a
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * This file is part of DisOrder
+ * Copyright (C) 2009 Richard Kettlewell
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+/** @file disobedience/multidrag.c
+ * @brief Drag multiple rows of a GtkTreeView
+ */
+#include "disobedience.h"
+
+static gboolean multidrag_selection_block(GtkTreeSelection attribute((unused)) *selection,
+                                         GtkTreeModel attribute((unused)) *model,
+                                         GtkTreePath attribute((unused)) *path,
+                                         gboolean attribute((unused)) path_currently_selected,
+                                         gpointer data) {
+  return *(gboolean *)data;
+}
+
+static void block_selection(GtkWidget *w, gboolean block,
+                           int x, int y) {
+  static gboolean which[] = { FALSE, TRUE };
+  GtkTreeSelection *s = gtk_tree_view_get_selection(GTK_TREE_VIEW(w));
+  gtk_tree_selection_set_select_function(s,
+                                        multidrag_selection_block,
+                                        &which[!!block],
+                                        NULL);
+  // Remember the pointer location
+  int *where = g_object_get_data(G_OBJECT(w), "multidrag-where");
+  if(!where) {
+    where = g_malloc(2 * sizeof (int));
+    g_object_set_data(G_OBJECT(w), "multidrag-where", where);
+  }
+  where[0] = x;
+  where[1] = y;
+}
+
+static gboolean multidrag_button_press_event(GtkWidget *w,
+                                            GdkEventButton *event,
+                                            gpointer attribute((unused)) user_data) {
+  /* By default we assume that anything this button press does should
+   * act as normal */
+  block_selection(w, TRUE, -1, -1);
+  /* We are only interested in left-button behavior */
+  if(event->button != 1)
+    return FALSE;
+  /* We are only interested in unmodified clicks (not SHIFT etc) */
+  if(event->state & GDK_MODIFIER_MASK)
+    return FALSE;
+  /* We are only interested if a well-defined path is clicked */
+  GtkTreePath *path;
+  if(!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(w),
+                                   event->x, event->y,
+                                   &path,
+                                   NULL,
+                                   NULL, NULL))
+    return FALSE;
+  //gtk_widget_grab_focus(w);    // TODO why??
+  /* We are only interested if a selected row is clicked */
+  GtkTreeSelection *s = gtk_tree_view_get_selection(GTK_TREE_VIEW(w));
+  if(!gtk_tree_selection_path_is_selected(s, path))
+    return FALSE;
+  /* We block subsequent selection changes and remember where the
+   * click was */
+  block_selection(w, FALSE, event->x, event->y);
+  return FALSE;                        /* propagate */
+}
+
+static gboolean multidrag_button_release_event(GtkWidget *w,
+                                              GdkEventButton *event,
+                                              gpointer attribute((unused)) user_data) {
+  int *where = g_object_get_data(G_OBJECT(w), "multidrag-where");
+
+  if(where && where[0] != -1) {
+    // Remember where the down-click was
+    const int x = where[0], y = where[1];
+    // Re-allow selections
+    block_selection(w, TRUE, -1, -1);
+    if(x == event->x && y == event->y) {
+      // If the up-click is at the same location as the down-click,
+      // it's not a drag.
+      GtkTreePath *path;
+      GtkTreeViewColumn *col;
+      if(gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(w),
+                                      event->x, event->y,
+                                      &path,
+                                      &col,
+                                      NULL, NULL)) {
+       gtk_tree_view_set_cursor(GTK_TREE_VIEW(w), path, col, FALSE);
+      }
+    }
+  }
+  return FALSE;                        /* propagate */
+}
+
+void make_treeview_multidrag(GtkWidget *w) {
+  g_signal_connect(w, "button-press-event",
+                  G_CALLBACK(multidrag_button_press_event), NULL);
+  g_signal_connect(w, "button-release-event",
+                  G_CALLBACK(multidrag_button_release_event), NULL);
+}
+
+/*
+Local Variables:
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/
index 913d9d3..2db2e1d 100644 (file)
@@ -798,6 +798,7 @@ GtkWidget *init_queuelike(struct queuelike *ql) {
                      G_CALLBACK(ql_drag_data_get), ql);
     g_signal_connect(ql->view, "drag-data-received",
                      G_CALLBACK(ql_drag_data_received), ql);
+    make_treeview_multidrag(ql->view);
   } else {
     /* TODO: support copy-dragging out of non-rearrangeable queues.  Will need
      * to support copy dropping into the rearrangeable ones. */