+/** @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 *iter,
+ gpointer data) {
+ struct multidrag_begin_state *qdbs = data;
+
+ 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 Row predicate
+ */
+static void multidrag_drag_begin(GtkWidget *w,
+ GdkDragContext attribute((unused)) *dc,
+ 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)))
+ return; /* doesn't make sense */
+ /* Generate a pixmap for each row */
+ qdbs->pixmaps = g_new(GdkPixmap *, qdbs->rows);
+ 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) {
+ int pxw, pxh;
+ gdk_drawable_get_size(qdbs->pixmaps[n], &pxw, &pxh);
+ if(pxw > width)
+ width = pxw;
+ height += pxh;
+ }
+ if(!width || !height)
+ return; /* doesn't make sense */
+ /* Construct the icon */
+ icon = gdk_pixmap_new(qdbs->pixmaps[0], width, height, -1);
+ GdkGC *gc = gdk_gc_new(icon);
+ gdk_gc_set_colormap(gc, gtk_widget_get_colormap(w));
+ int y = 0;
+ for(int n = 0; n < qdbs->rows; ++n) {
+ int pxw, pxh;
+ gdk_drawable_get_size(qdbs->pixmaps[n], &pxw, &pxh);
+ gdk_draw_drawable(icon,
+ gc,
+ qdbs->pixmaps[n],
+ 0, 0, /* source coords */
+ 0, y, /* dest coords */
+ pxw, pxh); /* size */
+ y += pxh;
+ gdk_drawable_unref(qdbs->pixmaps[n]);
+ qdbs->pixmaps[n] = NULL;
+ }
+ g_free(qdbs->pixmaps);
+ qdbs->pixmaps = NULL;
+ // TODO scale down a bit, the resulting icons are currently a bit on the
+ // large side.
+ gtk_drag_source_set_icon(w,
+ gtk_widget_get_colormap(w),
+ icon,
+ NULL);
+}
+
+static gboolean multidrag_default_predicate(GtkTreePath attribute((unused)) *path,
+ GtkTreeIter attribute((unused)) *iter) {
+ return TRUE;
+}
+