1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 2017, 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): Matthias Clasen <mclasen@redhat.com>
18  */
19 
20 /**
21  * SECTION:gtkeventcontrollermotion
22  * @Short_description: Event controller for motion events
23  * @Title: GtkEventControllerMotion
24  * @See_also: #GtkEventController
25  *
26  * #GtkEventControllerMotion is an event controller meant for situations
27  * where you need to track the position of the pointer.
28  *
29  * This object was added in 3.24.
30  **/
31 #include "config.h"
32 
33 #include "gtkeventcontrollermotion.h"
34 
35 #if GTK_CHECK_VERSION(3, 24, 0)
36 # error "Cannot use this in conjunction with 3.24"
37 #endif
38 
39 #define I_(s) (g_intern_static_string(s))
40 
41 struct _GtkEventControllerMotion
42 {
43   /* EventController adds no new instance fields up to 3.24 */
44   /* GtkEventController parent_instance; */
45   GObject parent_instance;
46 };
47 
48 struct _GtkEventControllerClass
49 {
50   GObjectClass parent_class;
51 
52   gboolean (* handle_event) (GtkEventController *controller,
53                              const GdkEvent     *event);
54   void     (* reset)        (GtkEventController *controller);
55 
56   /*<private>*/
57 
58   /* Tells whether the event is filtered out, %TRUE makes
59    * the event unseen by the handle_event vfunc.
60    */
61   gboolean (* filter_event) (GtkEventController *controller,
62                              const GdkEvent     *event);
63   gpointer padding[10];
64 };
65 
66 struct _GtkEventControllerMotionClass
67 {
68   GtkEventControllerClass parent_class;
69 };
70 
71 enum {
72   ENTER,
73   LEAVE,
74   MOTION,
75   N_SIGNALS
76 };
77 
78 static guint signals[N_SIGNALS] = { 0 };
79 
G_DEFINE_TYPE(GtkEventControllerMotion,gtk_event_controller_motion,GTK_TYPE_EVENT_CONTROLLER)80 G_DEFINE_TYPE (GtkEventControllerMotion, gtk_event_controller_motion, GTK_TYPE_EVENT_CONTROLLER)
81 
82 static void
83 get_coords (GtkWidget      *widget,
84             const GdkEvent *event,
85             double         *x,
86             double         *y)
87 {
88   GdkWindow *window, *ancestor;
89   GtkAllocation alloc;
90 
91   gtk_widget_get_allocation (widget, &alloc);
92   gdk_event_get_coords (event, x, y);
93 
94   ancestor = gtk_widget_get_window (widget);
95   window = gdk_event_get_window (event);
96 
97   while (window && ancestor && (window != ancestor))
98     {
99       gdk_window_coords_to_parent (window, *x, *y, x, y);
100       window = gdk_window_get_parent (window);
101     }
102 
103   if (!gtk_widget_get_has_window (widget))
104     {
105       *x -= alloc.x;
106       *y -= alloc.y;
107     }
108 }
109 
110 static gboolean
gtk_event_controller_motion_handle_event(GtkEventController * controller,const GdkEvent * event)111 gtk_event_controller_motion_handle_event (GtkEventController *controller,
112                                           const GdkEvent     *event)
113 {
114   GtkEventControllerClass *parent_class;
115   GtkWidget *widget;
116   GdkEventType type;
117 
118   widget = gtk_event_controller_get_widget (controller);
119 
120   type = gdk_event_get_event_type (event);
121   if (type == GDK_ENTER_NOTIFY)
122     {
123       double x, y;
124       get_coords (widget, event, &x, &y);
125       g_signal_emit (controller, signals[ENTER], 0, x, y);
126     }
127   else if (type == GDK_LEAVE_NOTIFY)
128     {
129       g_signal_emit (controller, signals[LEAVE], 0);
130     }
131   else if (type == GDK_MOTION_NOTIFY)
132     {
133       double x, y;
134       get_coords (widget, event, &x, &y);
135       g_signal_emit (controller, signals[MOTION], 0, x, y);
136     }
137 
138   parent_class = GTK_EVENT_CONTROLLER_CLASS (gtk_event_controller_motion_parent_class);
139 
140   return parent_class->handle_event (controller, event);
141 }
142 
143 static void
gtk_event_controller_motion_class_init(GtkEventControllerMotionClass * klass)144 gtk_event_controller_motion_class_init (GtkEventControllerMotionClass *klass)
145 {
146   GtkEventControllerClass *controller_class = GTK_EVENT_CONTROLLER_CLASS (klass);
147 
148   controller_class->handle_event = gtk_event_controller_motion_handle_event;
149 
150   /**
151    * GtkEventControllerMotion::enter:
152    * @controller: The object that received the signal
153    * @x: the x coordinate
154    * @y: the y coordinate
155    *
156    * Signals that the pointer has entered the widget.
157    */
158   signals[ENTER] =
159     g_signal_new (I_("enter"),
160                   GTK_TYPE_EVENT_CONTROLLER_MOTION,
161                   G_SIGNAL_RUN_FIRST,
162                   0, NULL, NULL,
163                   NULL,
164                   G_TYPE_NONE, 2, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
165 
166   /**
167    * GtkEventControllerMotion::leave:
168    * @controller: The object that received the signal
169    *
170    * Signals that pointer has left the widget.
171    */
172   signals[LEAVE] =
173     g_signal_new (I_("leave"),
174                   GTK_TYPE_EVENT_CONTROLLER_MOTION,
175                   G_SIGNAL_RUN_FIRST,
176                   0, NULL, NULL,
177         	  NULL,
178                   G_TYPE_NONE, 0);
179 
180   /**
181    * GtkEventControllerMotion::motion:
182    * @controller: The object that received the signal
183    * @x: the x coordinate
184    * @y: the y coordinate
185    *
186    * Emitted when the pointer moves inside the widget.
187    */
188   signals[MOTION] =
189     g_signal_new (I_("motion"),
190                   GTK_TYPE_EVENT_CONTROLLER_MOTION,
191                   G_SIGNAL_RUN_FIRST,
192                   0, NULL, NULL,
193                   NULL,
194                   G_TYPE_NONE, 2, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
195 }
196 
197 static void
gtk_event_controller_motion_init(GtkEventControllerMotion * motion)198 gtk_event_controller_motion_init (GtkEventControllerMotion *motion)
199 {
200 }
201 
202 /**
203  * gtk_event_controller_motion_new:
204  * @widget: a #GtkWidget
205  *
206  * Creates a new event controller that will handle motion events
207  * for the given @widget.
208  *
209  * Returns: a new #GtkEventControllerMotion
210  *
211  * Since: 3.24
212  **/
213 GtkEventController *
gtk_event_controller_motion_new(GtkWidget * widget)214 gtk_event_controller_motion_new (GtkWidget *widget)
215 {
216   g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
217 
218   return g_object_new (GTK_TYPE_EVENT_CONTROLLER_MOTION,
219                        "widget", widget,
220                        NULL);
221 }
222