1/* GTK - The GIMP Toolkit
2 * Copyright (C) 2012, One Laptop Per Child.
3 * Copyright (C) 2014, Red Hat, Inc.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library 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 GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * Author(s): Carlos Garnacho <carlosg@gnome.org>
19 */
20
21/**
22 * GtkGestureLongPress:
23 *
24 * `GtkGestureLongPress` is a `GtkGesture` for long presses.
25 *
26 * This gesture is also known as “Press and Hold”.
27 *
28 * When the timeout is exceeded, the gesture is triggering the
29 * [signal@Gtk.GestureLongPress::pressed] signal.
30 *
31 * If the touchpoint is lifted before the timeout passes, or if
32 * it drifts too far of the initial press point, the
33 * [signal@Gtk.GestureLongPress::cancelled] signal will be emitted.
34 *
35 * How long the timeout is before the ::pressed signal gets emitted is
36 * determined by the [property@Gtk.Settings:gtk-long-press-time] setting.
37 * It can be modified by the [property@Gtk.GestureLongPress:delay-factor]
38 * property.
39 */
40
41#include "config.h"
42#include "gtkgesturelongpress.h"
43#include "gtkgesturelongpressprivate.h"
44#include "gtkgestureprivate.h"
45#include "gtkmarshalers.h"
46#include "gtkdragsourceprivate.h"
47#include "gtkprivate.h"
48#include "gtkintl.h"
49#include "gtkmarshalers.h"
50
51typedef struct _GtkGestureLongPressPrivate GtkGestureLongPressPrivate;
52
53enum {
54 PRESSED,
55 CANCELLED,
56 N_SIGNALS
57};
58
59enum {
60 PROP_DELAY_FACTOR = 1,
61 LAST_PROP
62};
63
64struct _GtkGestureLongPressPrivate
65{
66 double initial_x;
67 double initial_y;
68
69 double delay_factor;
70 guint timeout_id;
71 guint delay;
72 guint cancelled : 1;
73 guint triggered : 1;
74};
75
76static guint signals[N_SIGNALS] = { 0 };
77static GParamSpec *props[LAST_PROP] = { NULL, };
78
79G_DEFINE_TYPE_WITH_PRIVATE (GtkGestureLongPress, gtk_gesture_long_press, GTK_TYPE_GESTURE_SINGLE)
80
81static void
82gtk_gesture_long_press_init (GtkGestureLongPress *gesture)
83{
84 GtkGestureLongPressPrivate *priv = gtk_gesture_long_press_get_instance_private (self: gesture);
85 priv->delay_factor = 1.0;
86}
87
88static gboolean
89gtk_gesture_long_press_check (GtkGesture *gesture)
90{
91 GtkGestureLongPressPrivate *priv;
92
93 priv = gtk_gesture_long_press_get_instance_private (GTK_GESTURE_LONG_PRESS (gesture));
94
95 if (priv->cancelled)
96 return FALSE;
97
98 return GTK_GESTURE_CLASS (gtk_gesture_long_press_parent_class)->check (gesture);
99}
100
101static gboolean
102_gtk_gesture_long_press_timeout (gpointer user_data)
103{
104 GtkGestureLongPress *gesture = user_data;
105 GtkGestureLongPressPrivate *priv;
106 GdkEventSequence *sequence;
107 double x, y;
108
109 priv = gtk_gesture_long_press_get_instance_private (self: gesture);
110 sequence = gtk_gesture_get_last_updated_sequence (GTK_GESTURE (gesture));
111 gtk_gesture_get_point (GTK_GESTURE (gesture), sequence, x: &x, y: &y);
112
113 priv->timeout_id = 0;
114 priv->triggered = TRUE;
115 g_signal_emit (instance: gesture, signal_id: signals[PRESSED], detail: 0, x, y);
116
117 return G_SOURCE_REMOVE;
118}
119
120static void
121gtk_gesture_long_press_begin (GtkGesture *gesture,
122 GdkEventSequence *sequence)
123{
124 GtkGestureLongPressPrivate *priv;
125 GdkEvent *event;
126 GdkEventType event_type;
127 GtkWidget *widget;
128 int delay;
129
130 priv = gtk_gesture_long_press_get_instance_private (GTK_GESTURE_LONG_PRESS (gesture));
131 sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
132 event = gtk_gesture_get_last_event (gesture, sequence);
133
134 if (!event)
135 return;
136
137 event_type = gdk_event_get_event_type (event);
138
139 if (event_type != GDK_BUTTON_PRESS &&
140 event_type != GDK_TOUCH_BEGIN)
141 return;
142
143 widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
144 g_object_get (object: gtk_widget_get_settings (widget),
145 first_property_name: "gtk-long-press-time", &delay,
146 NULL);
147
148 delay = (int)(priv->delay_factor * delay);
149
150 gtk_gesture_get_point (gesture, sequence,
151 x: &priv->initial_x, y: &priv->initial_y);
152 priv->timeout_id = g_timeout_add (interval: delay, function: _gtk_gesture_long_press_timeout, data: gesture);
153 gdk_source_set_static_name_by_id (tag: priv->timeout_id, name: "[gtk] _gtk_gesture_long_press_timeout");
154}
155
156static void
157gtk_gesture_long_press_update (GtkGesture *gesture,
158 GdkEventSequence *sequence)
159{
160 GtkGestureLongPressPrivate *priv;
161 GtkWidget *widget;
162 double x, y;
163
164 widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
165 priv = gtk_gesture_long_press_get_instance_private (GTK_GESTURE_LONG_PRESS (gesture));
166 gtk_gesture_get_point (gesture, sequence, x: &x, y: &y);
167
168 if (gtk_drag_check_threshold_double (widget, start_x: priv->initial_x, start_y: priv->initial_y, current_x: x, current_y: y))
169 {
170 if (priv->timeout_id)
171 {
172 g_source_remove (tag: priv->timeout_id);
173 priv->timeout_id = 0;
174 g_signal_emit (instance: gesture, signal_id: signals[CANCELLED], detail: 0);
175 }
176
177 priv->cancelled = TRUE;
178 _gtk_gesture_check (gesture);
179 }
180}
181
182static void
183gtk_gesture_long_press_end (GtkGesture *gesture,
184 GdkEventSequence *sequence)
185{
186 GtkGestureLongPressPrivate *priv;
187
188 priv = gtk_gesture_long_press_get_instance_private (GTK_GESTURE_LONG_PRESS (gesture));
189
190 if (priv->timeout_id)
191 {
192 g_source_remove (tag: priv->timeout_id);
193 priv->timeout_id = 0;
194 g_signal_emit (instance: gesture, signal_id: signals[CANCELLED], detail: 0);
195 }
196
197 priv->cancelled = priv->triggered = FALSE;
198}
199
200static void
201gtk_gesture_long_press_cancel (GtkGesture *gesture,
202 GdkEventSequence *sequence)
203{
204 gtk_gesture_long_press_end (gesture, sequence);
205 GTK_GESTURE_CLASS (gtk_gesture_long_press_parent_class)->cancel (gesture, sequence);
206}
207
208static void
209gtk_gesture_long_press_sequence_state_changed (GtkGesture *gesture,
210 GdkEventSequence *sequence,
211 GtkEventSequenceState state)
212{
213 if (state == GTK_EVENT_SEQUENCE_DENIED)
214 gtk_gesture_long_press_end (gesture, sequence);
215}
216
217static void
218gtk_gesture_long_press_finalize (GObject *object)
219{
220 GtkGestureLongPressPrivate *priv;
221
222 priv = gtk_gesture_long_press_get_instance_private (GTK_GESTURE_LONG_PRESS (object));
223
224 if (priv->timeout_id)
225 g_source_remove (tag: priv->timeout_id);
226
227 G_OBJECT_CLASS (gtk_gesture_long_press_parent_class)->finalize (object);
228}
229
230static void
231gtk_gesture_long_press_get_property (GObject *object,
232 guint property_id,
233 GValue *value,
234 GParamSpec *pspec)
235{
236 switch (property_id)
237 {
238 case PROP_DELAY_FACTOR:
239 g_value_set_double (value, v_double: gtk_gesture_long_press_get_delay_factor (GTK_GESTURE_LONG_PRESS (object)));
240 break;
241
242 default:
243 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
244 break;
245 }
246}
247
248static void
249gtk_gesture_long_press_set_property (GObject *object,
250 guint property_id,
251 const GValue *value,
252 GParamSpec *pspec)
253{
254 switch (property_id)
255 {
256 case PROP_DELAY_FACTOR:
257 gtk_gesture_long_press_set_delay_factor (GTK_GESTURE_LONG_PRESS (object),
258 delay_factor: g_value_get_double (value));
259 break;
260
261 default:
262 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
263 break;
264 }
265}
266
267static void
268gtk_gesture_long_press_class_init (GtkGestureLongPressClass *klass)
269{
270 GObjectClass *object_class = G_OBJECT_CLASS (klass);
271 GtkGestureClass *gesture_class = GTK_GESTURE_CLASS (klass);
272
273 object_class->finalize = gtk_gesture_long_press_finalize;
274 object_class->get_property = gtk_gesture_long_press_get_property;
275 object_class->set_property = gtk_gesture_long_press_set_property;
276
277 gesture_class->check = gtk_gesture_long_press_check;
278 gesture_class->begin = gtk_gesture_long_press_begin;
279 gesture_class->update = gtk_gesture_long_press_update;
280 gesture_class->end = gtk_gesture_long_press_end;
281 gesture_class->cancel = gtk_gesture_long_press_cancel;
282 gesture_class->sequence_state_changed = gtk_gesture_long_press_sequence_state_changed;
283
284 /**
285 * GtkGestureLongPress:delay-factor: (attributes org.gtk.Property.get=gtk_gesture_long_press_get_delay_factor org.gtk.Property.set=gtk_gesture_long_press_set_delay_factor)
286 *
287 * Factor by which to modify the default timeout.
288 */
289 props[PROP_DELAY_FACTOR] =
290 g_param_spec_double (name: "delay-factor",
291 P_("Delay factor"),
292 P_("Factor by which to modify the default timeout"),
293 minimum: 0.5, maximum: 2.0, default_value: 1.0,
294 flags: G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
295
296 g_object_class_install_properties (oclass: object_class, n_pspecs: LAST_PROP, pspecs: props);
297
298 /**
299 * GtkGestureLongPress::pressed:
300 * @gesture: the object which received the signal
301 * @x: the X coordinate where the press happened, relative to the widget allocation
302 * @y: the Y coordinate where the press happened, relative to the widget allocation
303 *
304 * Emitted whenever a press goes unmoved/unreleased longer than
305 * what the GTK defaults tell.
306 */
307 signals[PRESSED] =
308 g_signal_new (I_("pressed"),
309 G_TYPE_FROM_CLASS (klass),
310 signal_flags: G_SIGNAL_RUN_LAST,
311 G_STRUCT_OFFSET (GtkGestureLongPressClass, pressed),
312 NULL, NULL,
313 c_marshaller: _gtk_marshal_VOID__DOUBLE_DOUBLE,
314 G_TYPE_NONE, n_params: 2, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
315 g_signal_set_va_marshaller (signal_id: signals[PRESSED],
316 G_TYPE_FROM_CLASS (klass),
317 va_marshaller: _gtk_marshal_VOID__DOUBLE_DOUBLEv);
318
319 /**
320 * GtkGestureLongPress::cancelled:
321 * @gesture: the object which received the signal
322 *
323 * Emitted whenever a press moved too far, or was released
324 * before [signal@Gtk.GestureLongPress::pressed] happened.
325 */
326 signals[CANCELLED] =
327 g_signal_new (I_("cancelled"),
328 G_TYPE_FROM_CLASS (klass),
329 signal_flags: G_SIGNAL_RUN_LAST,
330 G_STRUCT_OFFSET (GtkGestureLongPressClass, cancelled),
331 NULL, NULL, NULL,
332 G_TYPE_NONE, n_params: 0);
333}
334
335/**
336 * gtk_gesture_long_press_new:
337 *
338 * Returns a newly created `GtkGesture` that recognizes long presses.
339 *
340 * Returns: a newly created `GtkGestureLongPress`.
341 */
342GtkGesture *
343gtk_gesture_long_press_new (void)
344{
345 return g_object_new (GTK_TYPE_GESTURE_LONG_PRESS,
346 NULL);
347}
348
349/**
350 * gtk_gesture_long_press_set_delay_factor: (attributes org.gtk.Method.set_property=delay-factor)
351 * @gesture: A `GtkGestureLongPress`
352 * @delay_factor: The delay factor to apply
353 *
354 * Applies the given delay factor.
355 *
356 * The default long press time will be multiplied by this value.
357 * Valid values are in the range [0.5..2.0].
358 */
359void
360gtk_gesture_long_press_set_delay_factor (GtkGestureLongPress *gesture,
361 double delay_factor)
362{
363 GtkGestureLongPressPrivate *priv = gtk_gesture_long_press_get_instance_private (self: gesture);
364
365 g_return_if_fail (GTK_IS_GESTURE_LONG_PRESS (gesture));
366 g_return_if_fail (delay_factor >= 0.5);
367 g_return_if_fail (delay_factor <= 2.0);
368
369 if (delay_factor == priv->delay_factor)
370 return;
371
372 priv->delay_factor = delay_factor;
373
374 g_object_notify_by_pspec (G_OBJECT (gesture), pspec: props[PROP_DELAY_FACTOR]);
375}
376
377/**
378 * gtk_gesture_long_press_get_delay_factor: (attributes org.gtk.Method.get_property=delay-factor)
379 * @gesture: A `GtkGestureLongPress`
380 *
381 * Returns the delay factor.
382 *
383 * Returns: the delay factor
384 */
385double
386gtk_gesture_long_press_get_delay_factor (GtkGestureLongPress *gesture)
387{
388 GtkGestureLongPressPrivate *priv = gtk_gesture_long_press_get_instance_private (self: gesture);
389
390 g_return_val_if_fail (GTK_IS_GESTURE_LONG_PRESS (gesture), 0);
391
392 return priv->delay_factor;
393}
394

source code of gtk/gtk/gtkgesturelongpress.c