]> Pileus Git - ~andy/gtk/blob - gtk/gtkpressandhold.c
stylecontext: Do invalidation on first resize container
[~andy/gtk] / gtk / gtkpressandhold.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 2012 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
18 #include "config.h"
19
20 #include "gdk.h"
21 #include "gtkpressandholdprivate.h"
22 #include "gtkintl.h"
23 #include "gtkprivate.h"
24
25 struct _GtkPressAndHoldPrivate
26 {
27   gint hold_time;
28   gint drag_threshold;
29
30   GdkEventSequence *sequence;
31   guint timeout;
32   gint start_x;
33   gint start_y;
34   gint x;
35   gint y;
36 };
37
38 enum
39 {
40   PROP_ZERO,
41   PROP_HOLD_TIME,
42   PROP_DRAG_THRESHOLD
43 };
44
45 enum
46 {
47   HOLD,
48   TAP,
49   LAST_SIGNAL
50 };
51
52 static guint signals[LAST_SIGNAL];
53
54 G_DEFINE_TYPE (GtkPressAndHold, gtk_press_and_hold, G_TYPE_OBJECT)
55
56 static void
57 gtk_press_and_hold_init (GtkPressAndHold *pah)
58 {
59   pah->priv = G_TYPE_INSTANCE_GET_PRIVATE (pah,
60                                            GTK_TYPE_PRESS_AND_HOLD,
61                                            GtkPressAndHoldPrivate);
62
63   pah->priv->hold_time = 1000;
64   pah->priv->drag_threshold = 8;
65 }
66
67 static void
68 press_and_hold_finalize (GObject *object)
69 {
70   GtkPressAndHold *pah = GTK_PRESS_AND_HOLD (object);
71
72   if (pah->priv->timeout)
73     g_source_remove (pah->priv->timeout);
74
75   G_OBJECT_CLASS (gtk_press_and_hold_parent_class)->finalize (object);
76 }
77
78 static void
79 press_and_hold_get_property (GObject    *object,
80                              guint       prop_id,
81                              GValue     *value,
82                              GParamSpec *pspec)
83 {
84   GtkPressAndHold *pah = GTK_PRESS_AND_HOLD (object);
85
86   switch (prop_id)
87     {
88     case PROP_HOLD_TIME:
89       g_value_set_int (value, pah->priv->hold_time);
90       break;
91     case PROP_DRAG_THRESHOLD:
92       g_value_set_int (value, pah->priv->drag_threshold);
93       break;
94     default:
95       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
96       break;
97     }
98 }
99
100 static void
101 press_and_hold_set_property (GObject      *object,
102                              guint         prop_id,
103                              const GValue *value,
104                              GParamSpec   *pspec)
105 {
106   GtkPressAndHold *pah = GTK_PRESS_AND_HOLD (object);
107
108   switch (prop_id)
109     {
110     case PROP_HOLD_TIME:
111       pah->priv->hold_time = g_value_get_int (value);
112       break;
113     case PROP_DRAG_THRESHOLD:
114       pah->priv->hold_time = g_value_get_int (value);
115       break;
116     default:
117       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
118       break;
119     }
120 }
121
122 static void
123 gtk_press_and_hold_class_init (GtkPressAndHoldClass *class)
124 {
125   GObjectClass *object_class = (GObjectClass *)class;
126
127   object_class->get_property = press_and_hold_get_property;
128   object_class->set_property = press_and_hold_set_property;
129   object_class->finalize = press_and_hold_finalize;
130
131   signals[HOLD] =
132     g_signal_new ("hold",
133                   GTK_TYPE_PRESS_AND_HOLD,
134                   G_SIGNAL_RUN_FIRST,
135                   G_STRUCT_OFFSET (GtkPressAndHoldClass, hold),
136                   NULL, NULL, NULL,
137                   G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);
138
139   signals[TAP] =
140     g_signal_new ("tap",
141                   GTK_TYPE_PRESS_AND_HOLD,
142                   G_SIGNAL_RUN_FIRST,
143                   G_STRUCT_OFFSET (GtkPressAndHoldClass, tap),
144                   NULL, NULL, NULL,
145                   G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);
146
147   g_object_class_install_property (object_class, PROP_HOLD_TIME,
148       g_param_spec_int ("hold-time", P_("Hold Time"), P_("Hold Time (in milliseconds)"),
149                         0, G_MAXINT, 1000, GTK_PARAM_READWRITE));
150
151   g_object_class_install_property (object_class, PROP_DRAG_THRESHOLD,
152       g_param_spec_int ("drag-threshold", P_("Drag Threshold"), P_("Drag Threshold (in pixels)"),
153                         1, G_MAXINT, 8, GTK_PARAM_READWRITE));
154
155   g_type_class_add_private (object_class, sizeof (GtkPressAndHoldPrivate));
156 }
157
158 static void
159 press_and_hold_cancel (GtkPressAndHold *pah)
160 {
161   GtkPressAndHoldPrivate *priv = pah->priv;
162
163   if (priv->timeout)
164     g_source_remove (priv->timeout);
165
166   priv->timeout = 0;
167   priv->sequence = NULL;
168 }
169
170 static gboolean
171 hold_action (gpointer data)
172 {
173   GtkPressAndHold *pah = data;
174   GtkPressAndHoldPrivate *priv = pah->priv;
175
176   press_and_hold_cancel (pah);
177
178   g_signal_emit (pah, signals[HOLD], 0, priv->x, priv->y);
179
180   return G_SOURCE_REMOVE;
181 }
182
183 void
184 gtk_press_and_hold_process_event (GtkPressAndHold *pah,
185                                   GdkEvent        *event)
186 {
187   GtkPressAndHoldPrivate *priv = pah->priv;
188
189   /* We're already tracking a different touch, ignore */
190   if ((event->type == GDK_TOUCH_BEGIN && priv->sequence != NULL) ||
191       (event->type != GDK_TOUCH_BEGIN && priv->sequence != event->touch.sequence))
192     return;
193
194   priv->x = event->touch.x;
195   priv->y = event->touch.y;
196
197   if (event->type == GDK_TOUCH_BEGIN)
198     {
199       priv->sequence = event->touch.sequence;
200       priv->start_x = priv->x;
201       priv->start_y = priv->y;
202       priv->timeout =
203           gdk_threads_add_timeout (priv->hold_time, hold_action, pah);
204     }
205   else if (event->type == GDK_TOUCH_UPDATE)
206     {
207       if (ABS (priv->x - priv->start_x) > priv->drag_threshold ||
208           ABS (priv->y - priv->start_y) > priv->drag_threshold)
209         press_and_hold_cancel (pah);
210     }
211   else if (event->type == GDK_TOUCH_END)
212     {
213       press_and_hold_cancel (pah);
214       g_signal_emit (pah, signals[TAP], 0, priv->x, priv->y);
215     }
216   else if (event->type == GDK_TOUCH_CANCEL)
217     {
218       press_and_hold_cancel (pah);
219     }
220 }
221
222 GtkPressAndHold *
223 gtk_press_and_hold_new (void)
224 {
225   return (GtkPressAndHold *) g_object_new (GTK_TYPE_PRESS_AND_HOLD, NULL);
226 }