chiark / gitweb /
Make multidrag a bit more independent of Disobedience
[disorder] / disobedience / multidrag.c
CommitLineData
6a7eb118
RK
1/*
2 * This file is part of DisOrder
3 * Copyright (C) 2009 Richard Kettlewell
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18/** @file disobedience/multidrag.c
19 * @brief Drag multiple rows of a GtkTreeView
30b358a3
RK
20 *
21 * Normally when you start a drag, GtkTreeView sets the selection to just row
22 * you dragged from (because it can't cope with dragging more than one row at a
23 * time).
24 *
25 * Disobedience needs more. To implement this it intercepts button-press-event
26 * and button-release event and for clicks that might be the start of drags,
27 * suppresses changes to the selection. A consequence of this is that it needs
28 * to intercept button-release-event too, to restore the effect of the click,
29 * if it turns out not to be drag after all.
30 *
31 * The location of the initial click is stored in object data called @c
32 * multidrag-where.
33 *
34 * Inspired by similar code in <a
35 * href="http://code.google.com/p/quodlibet/">Quodlibet</a> (another software
36 * jukebox, albeit as far as I can see a single-user one).
6a7eb118 37 */
3ae36d41
RK
38#include <config.h>
39#include <glib.h>
40#include <gtk/gtk.h>
41
42#include "multidrag.h"
6a7eb118
RK
43
44static gboolean multidrag_selection_block(GtkTreeSelection attribute((unused)) *selection,
45 GtkTreeModel attribute((unused)) *model,
46 GtkTreePath attribute((unused)) *path,
47 gboolean attribute((unused)) path_currently_selected,
48 gpointer data) {
30b358a3 49 return *(const gboolean *)data;
6a7eb118
RK
50}
51
52static void block_selection(GtkWidget *w, gboolean block,
53 int x, int y) {
30b358a3 54 static const gboolean which[] = { FALSE, TRUE };
6a7eb118
RK
55 GtkTreeSelection *s = gtk_tree_view_get_selection(GTK_TREE_VIEW(w));
56 gtk_tree_selection_set_select_function(s,
57 multidrag_selection_block,
30b358a3 58 (gboolean *)&which[!!block],
6a7eb118
RK
59 NULL);
60 // Remember the pointer location
61 int *where = g_object_get_data(G_OBJECT(w), "multidrag-where");
62 if(!where) {
63 where = g_malloc(2 * sizeof (int));
64 g_object_set_data(G_OBJECT(w), "multidrag-where", where);
65 }
66 where[0] = x;
67 where[1] = y;
30b358a3 68 // TODO release 'where' when object is destroyed
6a7eb118
RK
69}
70
71static gboolean multidrag_button_press_event(GtkWidget *w,
72 GdkEventButton *event,
73 gpointer attribute((unused)) user_data) {
74 /* By default we assume that anything this button press does should
75 * act as normal */
76 block_selection(w, TRUE, -1, -1);
77 /* We are only interested in left-button behavior */
78 if(event->button != 1)
79 return FALSE;
80 /* We are only interested in unmodified clicks (not SHIFT etc) */
81 if(event->state & GDK_MODIFIER_MASK)
82 return FALSE;
83 /* We are only interested if a well-defined path is clicked */
84 GtkTreePath *path;
85 if(!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(w),
86 event->x, event->y,
87 &path,
88 NULL,
89 NULL, NULL))
90 return FALSE;
91 //gtk_widget_grab_focus(w); // TODO why??
92 /* We are only interested if a selected row is clicked */
93 GtkTreeSelection *s = gtk_tree_view_get_selection(GTK_TREE_VIEW(w));
94 if(!gtk_tree_selection_path_is_selected(s, path))
95 return FALSE;
96 /* We block subsequent selection changes and remember where the
97 * click was */
98 block_selection(w, FALSE, event->x, event->y);
99 return FALSE; /* propagate */
100}
101
102static gboolean multidrag_button_release_event(GtkWidget *w,
103 GdkEventButton *event,
104 gpointer attribute((unused)) user_data) {
105 int *where = g_object_get_data(G_OBJECT(w), "multidrag-where");
106
30b358a3
RK
107 /* Did button-press-event do anything? We just check the outcome rather than
108 * going through all the conditions it tests. */
6a7eb118
RK
109 if(where && where[0] != -1) {
110 // Remember where the down-click was
111 const int x = where[0], y = where[1];
112 // Re-allow selections
113 block_selection(w, TRUE, -1, -1);
114 if(x == event->x && y == event->y) {
115 // If the up-click is at the same location as the down-click,
116 // it's not a drag.
117 GtkTreePath *path;
118 GtkTreeViewColumn *col;
119 if(gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(w),
120 event->x, event->y,
121 &path,
122 &col,
123 NULL, NULL)) {
124 gtk_tree_view_set_cursor(GTK_TREE_VIEW(w), path, col, FALSE);
125 }
126 }
127 }
128 return FALSE; /* propagate */
129}
130
30b358a3
RK
131/** @brief Allow multi-row drag for @p w
132 * @param w A GtkTreeView widget
133 *
134 * Suppresses the restriction of selections when a drag is started.
135 */
6a7eb118
RK
136void make_treeview_multidrag(GtkWidget *w) {
137 g_signal_connect(w, "button-press-event",
138 G_CALLBACK(multidrag_button_press_event), NULL);
139 g_signal_connect(w, "button-release-event",
140 G_CALLBACK(multidrag_button_release_event), NULL);
141}
142
143/*
144Local Variables:
145c-basic-offset:2
146comment-column:40
147fill-column:79
148indent-tabs-mode:nil
149End:
150*/