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  * SECTION:gtkgesturedrag
22  * @Short_description: Drag gesture
23  * @Title: GtkGestureDrag
24  * @See_also: #GtkGestureSwipe
25  *
26  * #GtkGestureDrag is a #GtkGesture implementation that recognizes drag
27  * operations. The drag operation itself can be tracked throught the
28  * #GtkGestureDrag::drag-begin, #GtkGestureDrag::drag-update and
29  * #GtkGestureDrag::drag-end signals, or the relevant coordinates be
30  * extracted through gtk_gesture_drag_get_offset() and
31  * gtk_gesture_drag_get_start_point().
32  */
33 #include "config.h"
34 #include "gtkgesturedrag.h"
35 #include "gtkgesturedragprivate.h"
36 #include "gtkintl.h"
37 #include "gtkmarshalers.h"
38 
39 typedef struct _GtkGestureDragPrivate GtkGestureDragPrivate;
40 typedef struct _EventData EventData;
41 
42 struct _GtkGestureDragPrivate
43 {
44   gdouble start_x;
45   gdouble start_y;
46   gdouble last_x;
47   gdouble last_y;
48 };
49 
50 enum {
51   DRAG_BEGIN,
52   DRAG_UPDATE,
53   DRAG_END,
54   N_SIGNALS
55 };
56 
57 static guint signals[N_SIGNALS] = { 0 };
58 
G_DEFINE_TYPE_WITH_PRIVATE(GtkGestureDrag,gtk_gesture_drag,GTK_TYPE_GESTURE_SINGLE)59 G_DEFINE_TYPE_WITH_PRIVATE (GtkGestureDrag, gtk_gesture_drag, GTK_TYPE_GESTURE_SINGLE)
60 
61 static gboolean
62 gtk_gesture_drag_filter_event (GtkEventController *controller,
63                                const GdkEvent     *event)
64 {
65   /* Let touchpad swipe events go through, only if they match n-points  */
66   if (event->type == GDK_TOUCHPAD_SWIPE)
67     {
68       guint n_points;
69 
70       g_object_get (G_OBJECT (controller), "n-points", &n_points, NULL);
71 
72       if (event->touchpad_swipe.n_fingers == n_points)
73         return FALSE;
74       else
75         return TRUE;
76     }
77 
78   return GTK_EVENT_CONTROLLER_CLASS (gtk_gesture_drag_parent_class)->filter_event (controller, event);
79 }
80 
81 static void
gtk_gesture_drag_begin(GtkGesture * gesture,GdkEventSequence * sequence)82 gtk_gesture_drag_begin (GtkGesture       *gesture,
83                         GdkEventSequence *sequence)
84 {
85   GtkGestureDragPrivate *priv;
86   GdkEventSequence *current;
87 
88   current = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
89 
90   priv = gtk_gesture_drag_get_instance_private (GTK_GESTURE_DRAG (gesture));
91   gtk_gesture_get_point (gesture, current, &priv->start_x, &priv->start_y);
92   priv->last_x = priv->start_x;
93   priv->last_y = priv->start_y;
94 
95   g_signal_emit (gesture, signals[DRAG_BEGIN], 0, priv->start_x, priv->start_y);
96 }
97 
98 static void
gtk_gesture_drag_update(GtkGesture * gesture,GdkEventSequence * sequence)99 gtk_gesture_drag_update (GtkGesture       *gesture,
100                          GdkEventSequence *sequence)
101 {
102   GtkGestureDragPrivate *priv;
103   gdouble x, y;
104 
105   priv = gtk_gesture_drag_get_instance_private (GTK_GESTURE_DRAG (gesture));
106   gtk_gesture_get_point (gesture, sequence, &priv->last_x, &priv->last_y);
107   x = priv->last_x - priv->start_x;
108   y = priv->last_y - priv->start_y;
109 
110   g_signal_emit (gesture, signals[DRAG_UPDATE], 0, x, y);
111 }
112 
113 static void
gtk_gesture_drag_end(GtkGesture * gesture,GdkEventSequence * sequence)114 gtk_gesture_drag_end (GtkGesture       *gesture,
115                       GdkEventSequence *sequence)
116 {
117   GtkGestureDragPrivate *priv;
118   GdkEventSequence *current;
119   gdouble x, y;
120 
121   current = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
122 
123   priv = gtk_gesture_drag_get_instance_private (GTK_GESTURE_DRAG (gesture));
124   gtk_gesture_get_point (gesture, current, &priv->last_x, &priv->last_y);
125   x = priv->last_x - priv->start_x;
126   y = priv->last_y - priv->start_y;
127 
128   g_signal_emit (gesture, signals[DRAG_END], 0, x, y);
129 }
130 
131 static void
gtk_gesture_drag_class_init(GtkGestureDragClass * klass)132 gtk_gesture_drag_class_init (GtkGestureDragClass *klass)
133 {
134   GtkGestureClass *gesture_class = GTK_GESTURE_CLASS (klass);
135   GtkEventControllerClass *event_controller_class = GTK_EVENT_CONTROLLER_CLASS (klass);
136 
137   event_controller_class->filter_event = gtk_gesture_drag_filter_event;
138 
139   gesture_class->begin = gtk_gesture_drag_begin;
140   gesture_class->update = gtk_gesture_drag_update;
141   gesture_class->end = gtk_gesture_drag_end;
142 
143   /**
144    * GtkGestureDrag::drag-begin:
145    * @gesture: the object which received the signal
146    * @start_x: X coordinate, relative to the widget allocation
147    * @start_y: Y coordinate, relative to the widget allocation
148    *
149    * This signal is emitted whenever dragging starts.
150    *
151    * Since: 3.14
152    */
153   signals[DRAG_BEGIN] =
154     g_signal_new (I_("drag-begin"),
155                   G_TYPE_FROM_CLASS (klass),
156                   G_SIGNAL_RUN_LAST,
157                   G_STRUCT_OFFSET (GtkGestureDragClass, drag_begin),
158                   NULL, NULL,
159                   _gtk_marshal_VOID__DOUBLE_DOUBLE,
160                   G_TYPE_NONE, 2, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
161   g_signal_set_va_marshaller (signals[DRAG_BEGIN],
162                               G_TYPE_FROM_CLASS (klass),
163                               _gtk_marshal_VOID__DOUBLE_DOUBLEv);
164   /**
165    * GtkGestureDrag::drag-update:
166    * @gesture: the object which received the signal
167    * @offset_x: X offset, relative to the start point
168    * @offset_y: Y offset, relative to the start point
169    *
170    * This signal is emitted whenever the dragging point moves.
171    *
172    * Since: 3.14
173    */
174   signals[DRAG_UPDATE] =
175     g_signal_new (I_("drag-update"),
176                   G_TYPE_FROM_CLASS (klass),
177                   G_SIGNAL_RUN_LAST,
178                   G_STRUCT_OFFSET (GtkGestureDragClass, drag_update),
179                   NULL, NULL,
180                   _gtk_marshal_VOID__DOUBLE_DOUBLE,
181                   G_TYPE_NONE, 2, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
182   g_signal_set_va_marshaller (signals[DRAG_UPDATE],
183                               G_TYPE_FROM_CLASS (klass),
184                               _gtk_marshal_VOID__DOUBLE_DOUBLEv);
185   /**
186    * GtkGestureDrag::drag-end:
187    * @gesture: the object which received the signal
188    * @offset_x: X offset, relative to the start point
189    * @offset_y: Y offset, relative to the start point
190    *
191    * This signal is emitted whenever the dragging is finished.
192    *
193    * Since: 3.14
194    */
195   signals[DRAG_END] =
196     g_signal_new (I_("drag-end"),
197                   G_TYPE_FROM_CLASS (klass),
198                   G_SIGNAL_RUN_LAST,
199                   G_STRUCT_OFFSET (GtkGestureDragClass, drag_end),
200                   NULL, NULL,
201                   _gtk_marshal_VOID__DOUBLE_DOUBLE,
202                   G_TYPE_NONE, 2, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
203   g_signal_set_va_marshaller (signals[DRAG_END],
204                               G_TYPE_FROM_CLASS (klass),
205                               _gtk_marshal_VOID__DOUBLE_DOUBLEv);
206 }
207 
208 static void
gtk_gesture_drag_init(GtkGestureDrag * gesture)209 gtk_gesture_drag_init (GtkGestureDrag *gesture)
210 {
211 }
212 
213 /**
214  * gtk_gesture_drag_new:
215  * @widget: a #GtkWidget
216  *
217  * Returns a newly created #GtkGesture that recognizes drags.
218  *
219  * Returns: a newly created #GtkGestureDrag
220  *
221  * Since: 3.14
222  **/
223 GtkGesture *
gtk_gesture_drag_new(GtkWidget * widget)224 gtk_gesture_drag_new (GtkWidget *widget)
225 {
226   g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
227 
228   return g_object_new (GTK_TYPE_GESTURE_DRAG,
229                        "widget", widget,
230                        NULL);
231 }
232 
233 /**
234  * gtk_gesture_drag_get_start_point:
235  * @gesture: a #GtkGesture
236  * @x: (out) (nullable): X coordinate for the drag start point
237  * @y: (out) (nullable): Y coordinate for the drag start point
238  *
239  * If the @gesture is active, this function returns %TRUE
240  * and fills in @x and @y with the drag start coordinates,
241  * in window-relative coordinates.
242  *
243  * Returns: %TRUE if the gesture is active
244  *
245  * Since: 3.14
246  **/
247 gboolean
gtk_gesture_drag_get_start_point(GtkGestureDrag * gesture,gdouble * x,gdouble * y)248 gtk_gesture_drag_get_start_point (GtkGestureDrag *gesture,
249                                   gdouble        *x,
250                                   gdouble        *y)
251 {
252   GtkGestureDragPrivate *priv;
253   GdkEventSequence *sequence;
254 
255   g_return_val_if_fail (GTK_IS_GESTURE_DRAG (gesture), FALSE);
256 
257   sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
258 
259   if (!gtk_gesture_handles_sequence (GTK_GESTURE (gesture), sequence))
260     return FALSE;
261 
262   priv = gtk_gesture_drag_get_instance_private (gesture);
263 
264   if (x)
265     *x = priv->start_x;
266 
267   if (y)
268     *y = priv->start_y;
269 
270   return TRUE;
271 }
272 
273 /**
274  * gtk_gesture_drag_get_offset:
275  * @gesture: a #GtkGesture
276  * @x: (out) (nullable): X offset for the current point
277  * @y: (out) (nullable): Y offset for the current point
278  *
279  * If the @gesture is active, this function returns %TRUE and
280  * fills in @x and @y with the coordinates of the current point,
281  * as an offset to the starting drag point.
282  *
283  * Returns: %TRUE if the gesture is active
284  *
285  * Since: 3.14
286  **/
287 gboolean
gtk_gesture_drag_get_offset(GtkGestureDrag * gesture,gdouble * x,gdouble * y)288 gtk_gesture_drag_get_offset (GtkGestureDrag *gesture,
289                              gdouble        *x,
290                              gdouble        *y)
291 {
292   GtkGestureDragPrivate *priv;
293   GdkEventSequence *sequence;
294 
295   g_return_val_if_fail (GTK_IS_GESTURE_DRAG (gesture), FALSE);
296 
297   sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
298 
299   if (!gtk_gesture_handles_sequence (GTK_GESTURE (gesture), sequence))
300     return FALSE;
301 
302   priv = gtk_gesture_drag_get_instance_private (gesture);
303 
304   if (x)
305     *x = priv->last_x - priv->start_x;
306 
307   if (y)
308     *y = priv->last_y - priv->start_y;
309 
310   return TRUE;
311 }
312