chiark / gitweb /
Implement dragging from the choose tab. This adds a new parameter to
authorRichard Kettlewell <rjk@greenend.org.uk>
Sun, 15 Nov 2009 12:48:15 +0000 (12:48 +0000)
committerRichard Kettlewell <rjk@greenend.org.uk>
Sun, 15 Nov 2009 12:48:15 +0000 (12:48 +0000)
multidrag, a predicate to allow the draggable rows to be selected.  In
this case, choose uses it to suppress directories.

disobedience/choose.c
disobedience/multidrag.c
disobedience/multidrag.h
disobedience/queue-generic.c

index b9a9e7e647b71698872505752034e6f12692e4c3..9829f199fc2ebebc1d9b178dedc1d6f23ce481eb 100644 (file)
 
 #include "disobedience.h"
 #include "choose.h"
+#include "multidrag.h"
 #include <gdk/gdkkeysyms.h>
 
+/** @brief Drag types */
+static const GtkTargetEntry choose_targets[] = {
+  {
+    (char *)"text/x-disorder-playable-tracks", /* drag type */
+    GTK_TARGET_SAME_APP|GTK_TARGET_OTHER_WIDGET, /* copying between widgets */
+    1                                     /* ID value */
+  },
+};
+
 /** @brief The current selection tree */
 GtkTreeStore *choose_store;
 
@@ -538,6 +548,60 @@ static gboolean choose_key_event(GtkWidget attribute((unused)) *widget,
   return TRUE;                          /* Handled it */
 }
 
+static gboolean choose_multidrag_predicate(GtkTreePath attribute((unused)) *path,
+                                           GtkTreeIter *iter) {
+  return choose_is_file(iter);
+}
+
+
+/** @brief Callback to add selected tracks to the selection data
+ *
+ * Called from choose_drag_data_get().
+ */
+static void choose_drag_data_get_collect(GtkTreeModel attribute((unused)) *model,
+                                         GtkTreePath attribute((unused)) *path,
+                                         GtkTreeIter *iter,
+                                         gpointer data) {
+  struct dynstr *const result = data;
+
+  if(choose_is_file(iter)) {            /* no diretories */
+    dynstr_append_string(result, "");   /* no ID */
+    dynstr_append(result, '\n');
+    dynstr_append_string(result, choose_get_track(iter));
+    dynstr_append(result, '\n');
+  }
+}
+
+/** @brief Called to extract the dragged data from the choose view
+ * @param w Source widget (the tree view)
+ * @param dc Drag context
+ * @param data Where to put the answer
+ * @param info_ Target @c info parameter
+ * @param time_ Time data requested (for some reason not a @c time_t)
+ * @param user_data The queuelike
+ *
+ * Closely analogous to ql_drag_data_get(), and uses the same data format.
+ * IDs are sent as empty strings.
+ */
+static void choose_drag_data_get(GtkWidget *w,
+                                 GdkDragContext attribute((unused)) *dc,
+                                 GtkSelectionData *data,
+                                 guint attribute((unused)) info_,
+                                 guint attribute((unused)) time_,
+                                 gpointer attribute((unused)) user_data) {
+  struct dynstr result[1];
+  GtkTreeSelection *sel;
+
+  sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(w));
+  dynstr_init(result);
+  gtk_tree_selection_selected_foreach(sel,
+                                      choose_drag_data_get_collect,
+                                      result);
+  gtk_selection_data_set(data,
+                         GDK_TARGET_STRING,
+                         8, (guchar *)result->vec, result->nvec);
+}
+
 /** @brief Create the choose tab */
 GtkWidget *choose_widget(void) {
   /* Create the tree store. */
@@ -643,6 +707,17 @@ GtkWidget *choose_widget(void) {
   g_signal_connect(choose_view, "key-release-event",
                    G_CALLBACK(choose_key_event), choose_search_entry);
 
+  /* Enable dragging of tracks out */
+  gtk_drag_source_set(choose_view,
+                      GDK_BUTTON1_MASK,
+                      choose_targets,
+                      sizeof choose_targets / sizeof *choose_targets,
+                      GDK_ACTION_COPY);
+  g_signal_connect(choose_view, "drag-data-get",
+                   G_CALLBACK(choose_drag_data_get), NULL);
+  make_treeview_multidrag(choose_view,
+                          choose_multidrag_predicate);
+
   return vbox;
 }
 
index 6014e07d347e9e4f20493908126d18f7449c7b7b..2f58ae194a5d874cd6d1e89a2bcdb969100079ac 100644 (file)
@@ -138,6 +138,7 @@ static gboolean multidrag_button_release_event(GtkWidget *w,
 /** @brief State for multidrag_begin() and its callbacks */
 struct multidrag_begin_state {
   GtkTreeView *view;
+  multidrag_row_predicate *predicate;
   int rows;
   int index;
   GdkPixmap **pixmaps;
@@ -146,22 +147,24 @@ struct multidrag_begin_state {
 /** @brief Callback to construct a row pixmap */
 static void multidrag_make_row_pixmaps(GtkTreeModel attribute((unused)) *model,
                                       GtkTreePath *path,
-                                      GtkTreeIter attribute((unused)) *iter,
+                                      GtkTreeIter *iter,
                                       gpointer data) {
   struct multidrag_begin_state *qdbs = data;
 
-  qdbs->pixmaps[qdbs->index++]
-    = gtk_tree_view_create_row_drag_icon(qdbs->view, path);
+  if(qdbs->predicate(path, iter)) {
+    qdbs->pixmaps[qdbs->index++]
+      = gtk_tree_view_create_row_drag_icon(qdbs->view, path);
+  }
 }
 
 /** @brief Called when a drag operation starts
  * @param w Source widget (the tree view)
  * @param dc Drag context
- * @param user_data Not used
+ * @param user_data Row predicate
  */
 static void multidrag_drag_begin(GtkWidget *w,
                                 GdkDragContext attribute((unused)) *dc,
-                                gpointer attribute((unused)) user_data) {
+                                gpointer user_data) {
   struct multidrag_begin_state qdbs[1];
   GdkPixmap *icon;
   GtkTreeSelection *sel;
@@ -169,6 +172,7 @@ static void multidrag_drag_begin(GtkWidget *w,
   //fprintf(stderr, "drag-begin\n");
   memset(qdbs, 0, sizeof *qdbs);
   qdbs->view = GTK_TREE_VIEW(w);
+  qdbs->predicate = (multidrag_row_predicate *)user_data;
   sel = gtk_tree_view_get_selection(qdbs->view);
   /* Find out how many rows there are */
   if(!(qdbs->rows = gtk_tree_selection_count_selected_rows(sel)))
@@ -178,6 +182,8 @@ static void multidrag_drag_begin(GtkWidget *w,
   gtk_tree_selection_selected_foreach(sel,
                                       multidrag_make_row_pixmaps,
                                       qdbs);
+  /* Might not have used all rows */
+  qdbs->rows = qdbs->index;
   /* Determine the size of the final icon */
   int height = 0, width = 0;
   for(int n = 0; n < qdbs->rows; ++n) {
@@ -217,18 +223,32 @@ static void multidrag_drag_begin(GtkWidget *w,
                            NULL);
 }
 
+static gboolean multidrag_default_predicate(GtkTreePath attribute((unused)) *path,
+                                           GtkTreeIter attribute((unused)) *iter) {
+  return TRUE;
+}
+
 /** @brief Allow multi-row drag for @p w
  * @param w A GtkTreeView widget
+ * @param predicate Function called to test rows for draggability, or NULL
+ *
+ * Suppresses the restriction of selections when a drag is started, and
+ * intercepts drag-begin to construct an icon.
  *
- * Suppresses the restriction of selections when a drag is started.
+ * @p predicate should return TRUE for draggable rows and FALSE otherwise, to
+ * control what goes in the icon.  If NULL, equivalent to a function that
+ * always returns TRUE.
  */
-void make_treeview_multidrag(GtkWidget *w) {
+void make_treeview_multidrag(GtkWidget *w,
+                            multidrag_row_predicate *predicate) {
+  if(!predicate)
+    predicate = multidrag_default_predicate;
   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);
   g_signal_connect(w, "drag-begin",
-                   G_CALLBACK(multidrag_drag_begin), NULL);
+                   G_CALLBACK(multidrag_drag_begin), predicate);
 }
 
 /*
index 61015421b088b38f46dfd33abf56a8a205967155..82eafa938034210b104ad1e4d7f588013c13112d 100644 (file)
 #ifndef MULTIDRAG_H
 #define MULTIDRAG_H
 
-void make_treeview_multidrag(GtkWidget *w);
+/** @brief Predicate type for rows to drag
+ * @param path Path to row
+ * @param iter Iterator pointing at row
+ * @return TRUE if row is draggable else FALSE
+ */
+typedef gboolean multidrag_row_predicate(GtkTreePath *path,
+                                        GtkTreeIter *iter);
+
+void make_treeview_multidrag(GtkWidget *w,
+                            multidrag_row_predicate *predicate);
 
 #endif /* MULTIDRAG_H */
 
index 179998a5b17ad1a9d5d1a28cceec333d691d640b..5a2a8ead5f002728bcfd1a59ca54dc0bf5e38332 100644 (file)
@@ -534,6 +534,13 @@ static void ql_drag_data_get_collect(GtkTreeModel *model,
  * @param info_ Target @c info parameter
  * @param time_ Time data requested (for some reason not a @c time_t)
  * @param user_data The queuelike
+ *
+ * The list of tracks is converted into a single string, consisting of IDs
+ * and track names.  Each is terminated by a newline.  Including both ID and
+ * track name means that the receiver can use whichever happens to be more
+ * convenient.
+ *
+ * If there are no IDs for rows in this widget then the ID half is undefined.
  */
 static void ql_drag_data_get(GtkWidget attribute((unused)) *w,
                              GdkDragContext attribute((unused)) *dc,
@@ -544,10 +551,6 @@ static void ql_drag_data_get(GtkWidget attribute((unused)) *w,
   struct queuelike *const ql = user_data;
   struct dynstr result[1];
 
-  /* The list of tracks is converted into a single string, consisting of IDs
-   * and track names.  Each is terminated by a newline.  Including both ID and
-   * track name means that the receiver can use whichever happens to be more
-   * convenient. */
   dynstr_init(result);
   gtk_tree_selection_selected_foreach(ql->selection,
                                       ql_drag_data_get_collect,
@@ -747,7 +750,8 @@ 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);
+    make_treeview_multidrag(ql->view, NULL);
+    // TODO playing track should be refused by predicate arg
   } else {
     /* For queues that cannot accept a drop we still accept a copy out */
     gtk_drag_source_set(ql->view,
@@ -757,7 +761,7 @@ GtkWidget *init_queuelike(struct queuelike *ql) {
                         GDK_ACTION_COPY);
     g_signal_connect(ql->view, "drag-data-get",
                      G_CALLBACK(ql_drag_data_get), ql);
-    make_treeview_multidrag(ql->view);
+    make_treeview_multidrag(ql->view, NULL);
   }
   
   /* TODO style? */