1/* GTK - The GIMP Toolkit
2 * Copyright (C) 2014 Red Hat, Inc.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * Author(s): Carlos Garnacho <carlosg@gnome.org>
18 */
19
20/**
21 * GtkGestureClick:
22 *
23 * `GtkGestureClick` is a `GtkGesture` implementation for clicks.
24 *
25 * It is able to recognize multiple clicks on a nearby zone, which
26 * can be listened for through the [signal@Gtk.GestureClick::pressed]
27 * signal. Whenever time or distance between clicks exceed the GTK
28 * defaults, [signal@Gtk.GestureClick::stopped] is emitted, and the
29 * click counter is reset.
30 */
31
32#include "config.h"
33#include "gtkgestureprivate.h"
34#include "gtkgestureclick.h"
35#include "gtkgestureclickprivate.h"
36#include "gtkprivate.h"
37#include "gtkintl.h"
38#include "gtkmarshalers.h"
39
40typedef struct _GtkGestureClickPrivate GtkGestureClickPrivate;
41
42struct _GtkGestureClickPrivate
43{
44 GdkDevice *current_device;
45 double initial_press_x;
46 double initial_press_y;
47 guint double_click_timeout_id;
48 guint n_presses;
49 guint n_release;
50 guint current_button;
51};
52
53enum {
54 PRESSED,
55 RELEASED,
56 STOPPED,
57 UNPAIRED_RELEASE,
58 LAST_SIGNAL
59};
60
61static guint signals[LAST_SIGNAL] = { 0 };
62
63G_DEFINE_TYPE_WITH_PRIVATE (GtkGestureClick, gtk_gesture_click, GTK_TYPE_GESTURE_SINGLE)
64
65static void
66gtk_gesture_click_finalize (GObject *object)
67{
68 GtkGestureClickPrivate *priv;
69 GtkGestureClick *gesture;
70
71 gesture = GTK_GESTURE_CLICK (object);
72 priv = gtk_gesture_click_get_instance_private (self: gesture);
73
74 if (priv->double_click_timeout_id)
75 {
76 g_source_remove (tag: priv->double_click_timeout_id);
77 priv->double_click_timeout_id = 0;
78 }
79
80 G_OBJECT_CLASS (gtk_gesture_click_parent_class)->finalize (object);
81}
82
83static gboolean
84gtk_gesture_click_check (GtkGesture *gesture)
85{
86 GtkGestureClick *click;
87 GtkGestureClickPrivate *priv;
88 GList *sequences;
89 gboolean active;
90
91 click = GTK_GESTURE_CLICK (gesture);
92 priv = gtk_gesture_click_get_instance_private (self: click);
93 sequences = gtk_gesture_get_sequences (gesture);
94
95 active = g_list_length (list: sequences) == 1 || priv->double_click_timeout_id;
96 g_list_free (list: sequences);
97
98 return active;
99}
100
101static void
102_gtk_gesture_click_stop (GtkGestureClick *gesture)
103{
104 GtkGestureClickPrivate *priv;
105
106 priv = gtk_gesture_click_get_instance_private (self: gesture);
107
108 if (priv->n_presses == 0)
109 return;
110
111 priv->current_device = NULL;
112 priv->current_button = 0;
113 priv->n_presses = 0;
114 g_signal_emit (instance: gesture, signal_id: signals[STOPPED], detail: 0);
115 _gtk_gesture_check (GTK_GESTURE (gesture));
116}
117
118static gboolean
119_double_click_timeout_cb (gpointer user_data)
120{
121 GtkGestureClick *gesture = user_data;
122 GtkGestureClickPrivate *priv;
123
124 priv = gtk_gesture_click_get_instance_private (self: gesture);
125 priv->double_click_timeout_id = 0;
126 _gtk_gesture_click_stop (gesture);
127
128 return FALSE;
129}
130
131static void
132_gtk_gesture_click_update_timeout (GtkGestureClick *gesture)
133{
134 GtkGestureClickPrivate *priv;
135 guint double_click_time;
136 GtkSettings *settings;
137 GtkWidget *widget;
138
139 priv = gtk_gesture_click_get_instance_private (self: gesture);
140
141 if (priv->double_click_timeout_id)
142 g_source_remove (tag: priv->double_click_timeout_id);
143
144 widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
145 settings = gtk_widget_get_settings (widget);
146 g_object_get (object: settings, first_property_name: "gtk-double-click-time", &double_click_time, NULL);
147
148 priv->double_click_timeout_id = g_timeout_add (interval: double_click_time, function: _double_click_timeout_cb, data: gesture);
149 gdk_source_set_static_name_by_id (tag: priv->double_click_timeout_id, name: "[gtk] _double_click_timeout_cb");
150}
151
152static gboolean
153_gtk_gesture_click_check_within_threshold (GtkGestureClick *gesture,
154 double x,
155 double y)
156{
157 GtkGestureClickPrivate *priv;
158 guint double_click_distance;
159 GtkSettings *settings;
160 GtkWidget *widget;
161
162 priv = gtk_gesture_click_get_instance_private (self: gesture);
163
164 if (priv->n_presses == 0)
165 return TRUE;
166
167 widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
168 settings = gtk_widget_get_settings (widget);
169 g_object_get (object: settings,
170 first_property_name: "gtk-double-click-distance", &double_click_distance,
171 NULL);
172
173 if (ABS (priv->initial_press_x - x) < double_click_distance &&
174 ABS (priv->initial_press_y - y) < double_click_distance)
175 return TRUE;
176
177 return FALSE;
178}
179
180static void
181gtk_gesture_click_begin (GtkGesture *gesture,
182 GdkEventSequence *sequence)
183{
184 GtkGestureClick *click;
185 GtkGestureClickPrivate *priv;
186 guint n_presses, button = 1;
187 GdkEventSequence *current;
188 GdkEvent *event;
189 GdkEventType event_type;
190 GdkDevice *device;
191 double x, y;
192
193 if (!gtk_gesture_handles_sequence (gesture, sequence))
194 return;
195
196 click = GTK_GESTURE_CLICK (gesture);
197 priv = gtk_gesture_click_get_instance_private (self: click);
198 event = gtk_gesture_get_last_event (gesture, sequence);
199 current = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
200 device = gdk_event_get_device (event);
201 event_type = gdk_event_get_event_type (event);
202
203 if (event_type == GDK_BUTTON_PRESS)
204 button = gdk_button_event_get_button (event);
205 else if (event_type == GDK_TOUCH_BEGIN)
206 button = 1;
207 else
208 return;
209
210 /* Reset the gesture if the button number changes mid-recognition */
211 if (priv->n_presses > 0 &&
212 priv->current_button != button)
213 _gtk_gesture_click_stop (gesture: click);
214
215 /* Reset also if the device changed */
216 if (priv->current_device && priv->current_device != device)
217 _gtk_gesture_click_stop (gesture: click);
218
219 priv->current_device = device;
220 priv->current_button = button;
221 _gtk_gesture_click_update_timeout (gesture: click);
222 gtk_gesture_get_point (gesture, sequence: current, x: &x, y: &y);
223
224 if (!_gtk_gesture_click_check_within_threshold (gesture: click, x, y))
225 _gtk_gesture_click_stop (gesture: click);
226
227 /* Increment later the real counter, just if the gesture is
228 * reset on the pressed handler */
229 n_presses = priv->n_release = priv->n_presses + 1;
230
231 g_signal_emit (instance: gesture, signal_id: signals[PRESSED], detail: 0, n_presses, x, y);
232
233 if (priv->n_presses == 0)
234 {
235 priv->initial_press_x = x;
236 priv->initial_press_y = y;
237 }
238
239 priv->n_presses++;
240}
241
242static void
243gtk_gesture_click_update (GtkGesture *gesture,
244 GdkEventSequence *sequence)
245{
246 GtkGestureClick *click;
247 GdkEventSequence *current;
248 double x, y;
249
250 click = GTK_GESTURE_CLICK (gesture);
251 current = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
252 gtk_gesture_get_point (gesture, sequence: current, x: &x, y: &y);
253
254 if (!_gtk_gesture_click_check_within_threshold (gesture: click, x, y))
255 _gtk_gesture_click_stop (gesture: click);
256}
257
258static void
259gtk_gesture_click_end (GtkGesture *gesture,
260 GdkEventSequence *sequence)
261{
262 GtkGestureClick *click;
263 GtkGestureClickPrivate *priv;
264 GdkEventSequence *current;
265 double x, y;
266 gboolean interpreted;
267 GtkEventSequenceState state;
268
269 click = GTK_GESTURE_CLICK (gesture);
270 priv = gtk_gesture_click_get_instance_private (self: click);
271 current = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
272 interpreted = gtk_gesture_get_point (gesture, sequence: current, x: &x, y: &y);
273 state = gtk_gesture_get_sequence_state (gesture, sequence: current);
274
275 if (current == sequence && state != GTK_EVENT_SEQUENCE_DENIED && interpreted)
276 g_signal_emit (instance: gesture, signal_id: signals[RELEASED], detail: 0, priv->n_release, x, y);
277
278 priv->n_release = 0;
279}
280
281static void
282gtk_gesture_click_cancel (GtkGesture *gesture,
283 GdkEventSequence *sequence)
284{
285 _gtk_gesture_click_stop (GTK_GESTURE_CLICK (gesture));
286 GTK_GESTURE_CLASS (gtk_gesture_click_parent_class)->cancel (gesture, sequence);
287}
288
289static void
290gtk_gesture_click_reset (GtkEventController *controller)
291{
292 _gtk_gesture_click_stop (GTK_GESTURE_CLICK (controller));
293 GTK_EVENT_CONTROLLER_CLASS (gtk_gesture_click_parent_class)->reset (controller);
294}
295
296static gboolean
297gtk_gesture_click_handle_event (GtkEventController *controller,
298 GdkEvent *event,
299 double x,
300 double y)
301{
302 GtkEventControllerClass *parent_controller;
303 GtkGestureClickPrivate *priv;
304 GdkEventSequence *sequence;
305 guint button;
306
307 priv = gtk_gesture_click_get_instance_private (GTK_GESTURE_CLICK (controller));
308 parent_controller = GTK_EVENT_CONTROLLER_CLASS (gtk_gesture_click_parent_class);
309 sequence = gdk_event_get_event_sequence (event);
310
311 if (priv->n_presses == 0 &&
312 !gtk_gesture_handles_sequence (GTK_GESTURE (controller), sequence) &&
313 (gdk_event_get_event_type (event) == GDK_BUTTON_RELEASE ||
314 gdk_event_get_event_type (event) == GDK_TOUCH_END))
315 {
316 if (gdk_event_get_event_type (event) == GDK_BUTTON_RELEASE)
317 button = gdk_button_event_get_button (event);
318 else
319 button = 0;
320 g_signal_emit (instance: controller, signal_id: signals[UNPAIRED_RELEASE], detail: 0,
321 x, y, button, sequence);
322 }
323
324 return parent_controller->handle_event (controller, event, x, y);
325}
326
327static void
328gtk_gesture_click_class_init (GtkGestureClickClass *klass)
329{
330 GtkEventControllerClass *controller_class = GTK_EVENT_CONTROLLER_CLASS (klass);
331 GObjectClass *object_class = G_OBJECT_CLASS (klass);
332 GtkGestureClass *gesture_class = GTK_GESTURE_CLASS (klass);
333
334 object_class->finalize = gtk_gesture_click_finalize;
335
336 gesture_class->check = gtk_gesture_click_check;
337 gesture_class->begin = gtk_gesture_click_begin;
338 gesture_class->update = gtk_gesture_click_update;
339 gesture_class->end = gtk_gesture_click_end;
340 gesture_class->cancel = gtk_gesture_click_cancel;
341
342 controller_class->reset = gtk_gesture_click_reset;
343 controller_class->handle_event = gtk_gesture_click_handle_event;
344
345 /**
346 * GtkGestureClick::pressed:
347 * @gesture: the object which received the signal
348 * @n_press: how many touch/button presses happened with this one
349 * @x: The X coordinate, in widget allocation coordinates
350 * @y: The Y coordinate, in widget allocation coordinates
351 *
352 * Emitted whenever a button or touch press happens.
353 */
354 signals[PRESSED] =
355 g_signal_new (I_("pressed"),
356 G_TYPE_FROM_CLASS (klass),
357 signal_flags: G_SIGNAL_RUN_LAST,
358 G_STRUCT_OFFSET (GtkGestureClickClass, pressed),
359 NULL, NULL,
360 c_marshaller: _gtk_marshal_VOID__INT_DOUBLE_DOUBLE,
361 G_TYPE_NONE, n_params: 3, G_TYPE_INT,
362 G_TYPE_DOUBLE, G_TYPE_DOUBLE);
363 g_signal_set_va_marshaller (signal_id: signals[PRESSED],
364 G_TYPE_FROM_CLASS (klass),
365 va_marshaller: _gtk_marshal_VOID__INT_DOUBLE_DOUBLEv);
366
367 /**
368 * GtkGestureClick::released:
369 * @gesture: the object which received the signal
370 * @n_press: number of press that is paired with this release
371 * @x: The X coordinate, in widget allocation coordinates
372 * @y: The Y coordinate, in widget allocation coordinates
373 *
374 * Emitted when a button or touch is released.
375 *
376 * @n_press will report the number of press that is paired to
377 * this event, note that [signal@Gtk.GestureClick::stopped] may
378 * have been emitted between the press and its release, @n_press
379 * will only start over at the next press.
380 */
381 signals[RELEASED] =
382 g_signal_new (I_("released"),
383 G_TYPE_FROM_CLASS (klass),
384 signal_flags: G_SIGNAL_RUN_LAST,
385 G_STRUCT_OFFSET (GtkGestureClickClass, released),
386 NULL, NULL,
387 c_marshaller: _gtk_marshal_VOID__INT_DOUBLE_DOUBLE,
388 G_TYPE_NONE, n_params: 3, G_TYPE_INT,
389 G_TYPE_DOUBLE, G_TYPE_DOUBLE);
390 g_signal_set_va_marshaller (signal_id: signals[RELEASED],
391 G_TYPE_FROM_CLASS (klass),
392 va_marshaller: _gtk_marshal_VOID__INT_DOUBLE_DOUBLEv);
393
394 /**
395 * GtkGestureClick::stopped:
396 * @gesture: the object which received the signal
397 *
398 * Emitted whenever any time/distance threshold has been exceeded.
399 */
400 signals[STOPPED] =
401 g_signal_new (I_("stopped"),
402 G_TYPE_FROM_CLASS (klass),
403 signal_flags: G_SIGNAL_RUN_LAST,
404 G_STRUCT_OFFSET (GtkGestureClickClass, stopped),
405 NULL, NULL, NULL,
406 G_TYPE_NONE, n_params: 0);
407
408 /**
409 * GtkGestureClick::unpaired-release
410 * @gesture: the object which received the signal
411 * @x: X coordinate of the event
412 * @y: Y coordinate of the event
413 * @button: Button being released
414 * @sequence: Sequence being released
415 *
416 * Emitted whenever the gesture receives a release
417 * event that had no previous corresponding press.
418 *
419 * Due to implicit grabs, this can only happen on situations
420 * where input is grabbed elsewhere mid-press or the pressed
421 * widget voluntarily relinquishes its implicit grab.
422 */
423 signals[UNPAIRED_RELEASE] =
424 g_signal_new (I_("unpaired-release"),
425 G_TYPE_FROM_CLASS (klass),
426 signal_flags: G_SIGNAL_RUN_LAST,
427 class_offset: 0, NULL, NULL,
428 c_marshaller: _gtk_marshal_VOID__DOUBLE_DOUBLE_UINT_BOXED,
429 G_TYPE_NONE, n_params: 4,
430 G_TYPE_DOUBLE, G_TYPE_DOUBLE,
431 G_TYPE_UINT, GDK_TYPE_EVENT_SEQUENCE);
432 g_signal_set_va_marshaller (signal_id: signals[UNPAIRED_RELEASE],
433 G_TYPE_FROM_CLASS (klass),
434 va_marshaller: _gtk_marshal_VOID__DOUBLE_DOUBLE_UINT_BOXEDv);
435}
436
437static void
438gtk_gesture_click_init (GtkGestureClick *gesture)
439{
440}
441
442/**
443 * gtk_gesture_click_new:
444 *
445 * Returns a newly created `GtkGesture` that recognizes
446 * single and multiple presses.
447 *
448 * Returns: a newly created `GtkGestureClick`
449 **/
450GtkGesture *
451gtk_gesture_click_new (void)
452{
453 return g_object_new (GTK_TYPE_GESTURE_CLICK, NULL);
454}
455

source code of gtk/gtk/gtkgestureclick.c