1/*
2 * Copyright (c) 2008-2009 Christian Hammond
3 * Copyright (c) 2008-2009 David Trowbridge
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included
13 * in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 * THE SOFTWARE.
22 */
23
24#include "config.h"
25#include <glib/gi18n-lib.h>
26
27#include "window.h"
28
29#include "highlightoverlay.h"
30#include "object-tree.h"
31
32#include "gtkstack.h"
33#include "gtkmain.h"
34#include "gtkwidgetprivate.h"
35#include "gtkeventcontrollermotion.h"
36#include "gtkeventcontrollerkey.h"
37#include "gtknative.h"
38#include "gtkwindowprivate.h"
39
40static GtkWidget *
41find_widget_at_pointer (GdkDevice *device)
42{
43 GtkWidget *widget = NULL;
44 GdkSurface *pointer_surface;
45
46 pointer_surface = gdk_device_get_surface_at_position (device, NULL, NULL);
47
48 if (pointer_surface)
49 widget = GTK_WIDGET (gtk_native_get_for_surface (pointer_surface));
50
51 if (widget)
52 {
53 double x, y;
54 double nx, ny;
55
56 gdk_surface_get_device_position (surface: gtk_native_get_surface (self: GTK_NATIVE (ptr: widget)),
57 device, x: &x, y: &y, NULL);
58 gtk_native_get_surface_transform (self: GTK_NATIVE (ptr: widget), x: &nx, y: &ny);
59 x -= nx;
60 y -= ny;
61
62 widget = gtk_widget_pick (widget, x, y, flags: GTK_PICK_INSENSITIVE|GTK_PICK_NON_TARGETABLE);
63 }
64
65 return widget;
66}
67
68static void
69clear_flash (GtkInspectorWindow *iw)
70{
71 if (iw->flash_overlay)
72 {
73 gtk_inspector_window_remove_overlay (iw, overlay: iw->flash_overlay);
74 g_clear_object (&iw->flash_overlay);
75 }
76}
77
78static void
79start_flash (GtkInspectorWindow *iw,
80 GtkWidget *widget)
81{
82 clear_flash (iw);
83
84 iw->flash_count = 1;
85 iw->flash_overlay = gtk_highlight_overlay_new (widget);
86 gtk_inspector_window_add_overlay (iw, overlay: iw->flash_overlay);
87}
88
89static void
90select_widget (GtkInspectorWindow *iw,
91 GtkWidget *widget)
92{
93 GtkInspectorObjectTree *wt = GTK_INSPECTOR_OBJECT_TREE (iw->object_tree);
94
95 gtk_inspector_object_tree_activate_object (wt, G_OBJECT (widget));
96}
97
98static void
99on_inspect_widget (GtkInspectorWindow *iw,
100 GdkEvent *event)
101{
102 GtkWidget *widget;
103
104 gtk_window_present (GTK_WINDOW (iw));
105
106 clear_flash (iw);
107
108 widget = find_widget_at_pointer (device: gdk_event_get_device (event));
109
110 if (widget)
111 select_widget (iw, widget);
112}
113
114static void
115reemphasize_window (GtkWidget *window)
116{
117 gtk_window_present (GTK_WINDOW (window));
118}
119
120static gboolean handle_event (GtkInspectorWindow *iw, GdkEvent *event);
121
122static gboolean
123handle_event (GtkInspectorWindow *iw, GdkEvent *event)
124{
125 switch ((int)gdk_event_get_event_type (event))
126 {
127 case GDK_KEY_PRESS:
128 case GDK_KEY_RELEASE:
129 {
130 guint keyval = 0;
131
132 keyval = gdk_key_event_get_keyval (event);
133 if (keyval == GDK_KEY_Escape)
134 {
135 g_signal_handlers_disconnect_by_func (iw, handle_event, NULL);
136 reemphasize_window (GTK_WIDGET (iw));
137 clear_flash (iw);
138 }
139 }
140 break;
141
142 case GDK_MOTION_NOTIFY:
143 {
144 GtkWidget *widget = find_widget_at_pointer (device: gdk_event_get_device (event));
145
146 if (widget == NULL)
147 {
148 /* This window isn't in-process. Ignore it. */
149 break;
150 }
151
152 if (gtk_widget_get_root (widget) == GTK_ROOT (ptr: iw))
153 {
154 /* Don't highlight things in the inspector window */
155 break;
156 }
157
158 if (iw->flash_overlay &&
159 gtk_highlight_overlay_get_widget (self: GTK_HIGHLIGHT_OVERLAY (ptr: iw->flash_overlay)) == widget)
160 {
161 /* Already selected */
162 break;
163 }
164
165 start_flash (iw, widget);
166 }
167 break;
168
169 case GDK_BUTTON_PRESS:
170 case GDK_BUTTON_RELEASE:
171 g_signal_handlers_disconnect_by_func (iw, handle_event, NULL);
172 reemphasize_window (GTK_WIDGET (iw));
173 on_inspect_widget (iw, event);
174 break;
175
176 default:;
177 }
178
179 return TRUE;
180}
181
182void
183gtk_inspector_on_inspect (GtkWidget *button,
184 GtkInspectorWindow *iw)
185{
186 gtk_widget_hide (GTK_WIDGET (iw));
187
188 g_signal_connect (iw, "event", G_CALLBACK (handle_event), NULL);
189}
190
191static gboolean
192on_flash_timeout (GtkInspectorWindow *iw)
193{
194 iw->flash_count++;
195
196 gtk_highlight_overlay_set_color (self: GTK_HIGHLIGHT_OVERLAY (ptr: iw->flash_overlay),
197 color: &(GdkRGBA) {
198 0.0, 0.0, 1.0,
199 (iw && iw->flash_count % 2 == 0) ? 0.0 : 0.2
200 });
201
202 if (iw->flash_count == 6)
203 {
204 clear_flash (iw);
205 iw->flash_cnx = 0;
206
207 return G_SOURCE_REMOVE;
208 }
209
210 return G_SOURCE_CONTINUE;
211}
212
213void
214gtk_inspector_flash_widget (GtkInspectorWindow *iw,
215 GtkWidget *widget)
216{
217 if (!gtk_widget_get_visible (widget) || !gtk_widget_get_mapped (widget))
218 return;
219
220 if (iw->flash_cnx != 0)
221 {
222 g_source_remove (tag: iw->flash_cnx);
223 iw->flash_cnx = 0;
224 }
225
226 start_flash (iw, widget);
227 iw->flash_cnx = g_timeout_add (interval: 150, function: (GSourceFunc) on_flash_timeout, data: iw);
228}
229
230void
231gtk_inspector_window_select_widget_under_pointer (GtkInspectorWindow *iw)
232{
233 GdkDisplay *display;
234 GdkSeat *seat;
235 GdkDevice *device;
236 GtkWidget *widget;
237
238 display = gtk_inspector_window_get_inspected_display (iw);
239 seat = gdk_display_get_default_seat (display);
240 if (!seat)
241 return;
242
243 device = gdk_seat_get_pointer (seat);
244
245 widget = find_widget_at_pointer (device);
246
247 if (widget)
248 select_widget (iw, widget);
249}
250
251/* vim: set et sw=2 ts=2: */
252

source code of gtk/gtk/inspector/inspect-button.c