#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;
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. */
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;
}
/** @brief State for multidrag_begin() and its callbacks */
struct multidrag_begin_state {
GtkTreeView *view;
+ multidrag_row_predicate *predicate;
int rows;
int index;
GdkPixmap **pixmaps;
/** @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;
//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)))
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) {
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);
}
/*
#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 */
* @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,
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,
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,
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? */