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 "gtkintl.h"
34 #include "gtkmarshalers.h"
35 #include "gtkwidget.h"
36 #include "gtkeventcontrollerprivate.h"
37 #include "gtkeventcontrollermotion.h"
38 #include "gtktypebuiltins.h"
39 #include "gtkmarshalers.h"
40 
41 struct _GtkEventControllerMotion
42 {
43   GtkEventController parent_instance;
44 };
45 
46 struct _GtkEventControllerMotionClass
47 {
48   GtkEventControllerClass parent_class;
49 };
50 
51 enum {
52   ENTER,
53   LEAVE,
54   MOTION,
55   N_SIGNALS
56 };
57 
58 static guint signals[N_SIGNALS] = { 0 };
59 
G_DEFINE_TYPE(GtkEventControllerMotion,gtk_event_controller_motion,GTK_TYPE_EVENT_CONTROLLER)60 G_DEFINE_TYPE (GtkEventControllerMotion, gtk_event_controller_motion, GTK_TYPE_EVENT_CONTROLLER)
61 
62 static void
63 get_coords (GtkWidget      *widget,
64             const GdkEvent *event,
65             double         *x,
66             double         *y)
67 {
68   GdkWindow *window, *ancestor;
69   GtkAllocation alloc;
70 
71   gtk_widget_get_allocation (widget, &alloc);
72   gdk_event_get_coords (event, x, y);
73 
74   ancestor = gtk_widget_get_window (widget);
75   window = gdk_event_get_window (event);
76 
77   while (window && ancestor && (window != ancestor))
78     {
79       gdk_window_coords_to_parent (window, *x, *y, x, y);
80       window = gdk_window_get_parent (window);
81     }
82 
83   if (!gtk_widget_get_has_window (widget))
84     {
85       *x -= alloc.x;
86       *y -= alloc.y;
87     }
88 }
89 
90 static gboolean
gtk_event_controller_motion_handle_event(GtkEventController * controller,const GdkEvent * event)91 gtk_event_controller_motion_handle_event (GtkEventController *controller,
92                                           const GdkEvent     *event)
93 {
94   GtkEventControllerClass *parent_class;
95   GtkWidget *widget;
96   GdkEventType type;
97 
98   widget = gtk_event_controller_get_widget (controller);
99 
100   type = gdk_event_get_event_type (event);
101   if (type == GDK_ENTER_NOTIFY)
102     {
103       double x, y;
104       get_coords (widget, event, &x, &y);
105       g_signal_emit (controller, signals[ENTER], 0, x, y);
106     }
107   else if (type == GDK_LEAVE_NOTIFY)
108     {
109       g_signal_emit (controller, signals[LEAVE], 0);
110     }
111   else if (type == GDK_MOTION_NOTIFY)
112     {
113       double x, y;
114       get_coords (widget, event, &x, &y);
115       g_signal_emit (controller, signals[MOTION], 0, x, y);
116     }
117 
118   parent_class = GTK_EVENT_CONTROLLER_CLASS (gtk_event_controller_motion_parent_class);
119 
120   return parent_class->handle_event (controller, event);
121 }
122 
123 static void
gtk_event_controller_motion_class_init(GtkEventControllerMotionClass * klass)124 gtk_event_controller_motion_class_init (GtkEventControllerMotionClass *klass)
125 {
126   GtkEventControllerClass *controller_class = GTK_EVENT_CONTROLLER_CLASS (klass);
127 
128   controller_class->handle_event = gtk_event_controller_motion_handle_event;
129 
130   /**
131    * GtkEventControllerMotion::enter:
132    * @controller: The object that received the signal
133    * @x: the x coordinate
134    * @y: the y coordinate
135    *
136    * Signals that the pointer has entered the widget.
137    */
138   signals[ENTER] =
139     g_signal_new (I_("enter"),
140                   GTK_TYPE_EVENT_CONTROLLER_MOTION,
141                   G_SIGNAL_RUN_FIRST,
142                   0, NULL, NULL,
143                   _gtk_marshal_VOID__DOUBLE_DOUBLE,
144                   G_TYPE_NONE, 2, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
145   g_signal_set_va_marshaller (signals[ENTER],
146                               G_TYPE_FROM_CLASS (klass),
147                               _gtk_marshal_VOID__DOUBLE_DOUBLEv);
148 
149   /**
150    * GtkEventControllerMotion::leave:
151    * @controller: The object that received the signal
152    *
153    * Signals that pointer has left the widget.
154    */
155   signals[LEAVE] =
156     g_signal_new (I_("leave"),
157                   GTK_TYPE_EVENT_CONTROLLER_MOTION,
158                   G_SIGNAL_RUN_FIRST,
159                   0, NULL, NULL,
160                   NULL,
161                   G_TYPE_NONE, 0);
162 
163   /**
164    * GtkEventControllerMotion::motion:
165    * @controller: The object that received the signal
166    * @x: the x coordinate
167    * @y: the y coordinate
168    *
169    * Emitted when the pointer moves inside the widget.
170    */
171   signals[MOTION] =
172     g_signal_new (I_("motion"),
173                   GTK_TYPE_EVENT_CONTROLLER_MOTION,
174                   G_SIGNAL_RUN_FIRST,
175                   0, NULL, NULL,
176                   _gtk_marshal_VOID__DOUBLE_DOUBLE,
177                   G_TYPE_NONE, 2, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
178   g_signal_set_va_marshaller (signals[MOTION],
179                               G_TYPE_FROM_CLASS (klass),
180                               _gtk_marshal_VOID__DOUBLE_DOUBLEv);
181 }
182 
183 static void
gtk_event_controller_motion_init(GtkEventControllerMotion * motion)184 gtk_event_controller_motion_init (GtkEventControllerMotion *motion)
185 {
186 }
187 
188 /**
189  * gtk_event_controller_motion_new:
190  * @widget: a #GtkWidget
191  *
192  * Creates a new event controller that will handle motion events
193  * for the given @widget.
194  *
195  * Returns: a new #GtkEventControllerMotion
196  *
197  * Since: 3.24
198  **/
199 GtkEventController *
gtk_event_controller_motion_new(GtkWidget * widget)200 gtk_event_controller_motion_new (GtkWidget *widget)
201 {
202   g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
203 
204   return g_object_new (GTK_TYPE_EVENT_CONTROLLER_MOTION,
205                        "widget", widget,
206                        NULL);
207 }
208