1/*
2 * Copyright (c) 2008-2009 Christian Hammond
3 * Copyright (c) 2008-2009 David Trowbridge
4 * Copyright (c) 2013 Intel Corporation
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included
14 * in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24
25#include "config.h"
26#include <glib/gi18n-lib.h>
27
28#include <stdlib.h>
29
30#include "init.h"
31#include "window.h"
32#include "prop-list.h"
33#include "clipboard.h"
34#include "controllers.h"
35#include "css-editor.h"
36#include "css-node-tree.h"
37#include "object-tree.h"
38#include "size-groups.h"
39#include "a11y.h"
40#include "actions.h"
41#include "shortcuts.h"
42#include "list-data.h"
43#include "menu.h"
44#include "misc-info.h"
45#include "magnifier.h"
46#include "recorder.h"
47#include "tree-data.h"
48#include "visual.h"
49#include "general.h"
50#include "logs.h"
51
52#include "gdkdebug.h"
53#include "gdkmarshalers.h"
54#include "gskrendererprivate.h"
55#include "gtkbutton.h"
56#include "gtkcsswidgetnodeprivate.h"
57#include "gtklabel.h"
58#include "gtkmodulesprivate.h"
59#include "gtkprivate.h"
60#include "gtknative.h"
61#include "gtkstack.h"
62#include "gtktreeviewcolumn.h"
63#include "gtkwindowgroup.h"
64#include "gtkrevealer.h"
65#include "gtklayoutmanager.h"
66#include "gtkcssprovider.h"
67#include "gtkstylecontext.h"
68#include "gtkwidgetprivate.h"
69
70
71enum {
72 PROP_INSPECTED_DISPLAY = 1,
73 NUM_PROPERTIES
74};
75
76static GParamSpec *properties[NUM_PROPERTIES];
77
78enum {
79 EVENT,
80 LAST_SIGNAL
81};
82
83static guint signals[LAST_SIGNAL];
84
85
86G_DEFINE_TYPE (GtkInspectorWindow, gtk_inspector_window, GTK_TYPE_WINDOW)
87
88
89/* Fast way of knowing that further checks are necessary because at least
90 * one inspector window has been constructed. */
91static gboolean any_inspector_window_constructed = FALSE;
92
93
94static gboolean
95set_selected_object (GtkInspectorWindow *iw,
96 GObject *selected)
97{
98 GList *l;
99 char *title;
100
101 if (!gtk_inspector_prop_list_set_object (GTK_INSPECTOR_PROP_LIST (iw->prop_list), object: selected))
102 return FALSE;
103
104 title = gtk_inspector_get_object_title (object: selected);
105 gtk_label_set_label (GTK_LABEL (iw->object_title), str: title);
106 g_free (mem: title);
107
108 gtk_inspector_prop_list_set_layout_child (GTK_INSPECTOR_PROP_LIST (iw->layout_prop_list), object: selected);
109 gtk_inspector_misc_info_set_object (GTK_INSPECTOR_MISC_INFO (iw->misc_info), object: selected);
110 gtk_inspector_css_node_tree_set_object (GTK_INSPECTOR_CSS_NODE_TREE (iw->widget_css_node_tree), object: selected);
111 gtk_inspector_size_groups_set_object (GTK_INSPECTOR_SIZE_GROUPS (iw->size_groups), object: selected);
112 gtk_inspector_tree_data_set_object (GTK_INSPECTOR_TREE_DATA (iw->tree_data), object: selected);
113 gtk_inspector_list_data_set_object (sl: GTK_INSPECTOR_LIST_DATA (ptr: iw->list_data), object: selected);
114 gtk_inspector_actions_set_object (GTK_INSPECTOR_ACTIONS (iw->actions), object: selected);
115 gtk_inspector_shortcuts_set_object (sl: GTK_INSPECTOR_SHORTCUTS (ptr: iw->shortcuts), object: selected);
116 gtk_inspector_menu_set_object (GTK_INSPECTOR_MENU (iw->menu), object: selected);
117 gtk_inspector_controllers_set_object (sl: GTK_INSPECTOR_CONTROLLERS (ptr: iw->controllers), object: selected);
118 gtk_inspector_magnifier_set_object (GTK_INSPECTOR_MAGNIFIER (iw->magnifier), object: selected);
119 gtk_inspector_a11y_set_object (GTK_INSPECTOR_A11Y (iw->a11y), object: selected);
120
121 for (l = iw->extra_pages; l != NULL; l = l->next)
122 g_object_set (object: l->data, first_property_name: "object", selected, NULL);
123
124 return TRUE;
125}
126
127static void
128on_object_activated (GtkInspectorObjectTree *wt,
129 GObject *selected,
130 GtkInspectorWindow *iw)
131{
132 if (GTK_IS_WIDGET (selected))
133 gtk_inspector_window_set_object (iw, object: selected, kind: CHILD_KIND_WIDGET, position: 0);
134 else
135 gtk_inspector_window_set_object (iw, object: selected, kind: CHILD_KIND_OTHER, position: 0);
136
137 gtk_stack_set_visible_child_name (GTK_STACK (iw->object_stack), name: "object-details");
138 gtk_stack_set_visible_child_name (GTK_STACK (iw->object_buttons), name: "details");
139}
140
141static void
142on_object_selected (GtkInspectorObjectTree *wt,
143 GObject *selected,
144 GtkInspectorWindow *iw)
145{
146 gtk_widget_set_sensitive (widget: iw->object_details_button, sensitive: selected != NULL);
147 if (GTK_IS_WIDGET (selected))
148 gtk_inspector_flash_widget (iw, GTK_WIDGET (selected));
149}
150
151static void
152notify_node (GtkInspectorCssNodeTree *cnt,
153 GParamSpec *pspec,
154 GtkInspectorWindow *iw)
155{
156 GtkCssNode *node;
157 GtkWidget *widget = NULL;
158
159 for (node = gtk_inspector_css_node_tree_get_node (cnt);
160 node != NULL;
161 node = gtk_css_node_get_parent (cssnode: node))
162 {
163 if (!GTK_IS_CSS_WIDGET_NODE (node))
164 continue;
165
166 widget = gtk_css_widget_node_get_widget (GTK_CSS_WIDGET_NODE (node));
167 if (widget != NULL)
168 break;
169 }
170 if (widget)
171 gtk_inspector_flash_widget (iw, widget);
172}
173
174static void
175close_object_details (GtkWidget *button, GtkInspectorWindow *iw)
176{
177 gtk_stack_set_visible_child_name (GTK_STACK (iw->object_stack), name: "object-tree");
178 gtk_stack_set_visible_child_name (GTK_STACK (iw->object_buttons), name: "list");
179}
180
181static void
182open_object_details (GtkWidget *button, GtkInspectorWindow *iw)
183{
184 GObject *selected;
185
186 selected = gtk_inspector_object_tree_get_selected (GTK_INSPECTOR_OBJECT_TREE (iw->object_tree));
187
188 gtk_inspector_window_set_object (iw, object: selected, kind: CHILD_KIND_WIDGET, position: 0);
189
190 gtk_stack_set_visible_child_name (GTK_STACK (iw->object_stack), name: "object-details");
191 gtk_stack_set_visible_child_name (GTK_STACK (iw->object_buttons), name: "details");
192}
193
194static gboolean
195translate_visible_child_name (GBinding *binding,
196 const GValue *from,
197 GValue *to,
198 gpointer user_data)
199{
200 GtkInspectorWindow *iw = user_data;
201 const char *name;
202
203 name = g_value_get_string (value: from);
204
205 if (gtk_stack_get_child_by_name (GTK_STACK (iw->object_start_stack), name))
206 g_value_set_string (value: to, v_string: name);
207 else
208 g_value_set_string (value: to, v_string: "empty");
209
210 return TRUE;
211}
212
213typedef struct
214{
215 GObject *object;
216 ChildKind kind;
217 guint position;
218} ChildData;
219
220static void
221gtk_inspector_window_init (GtkInspectorWindow *iw)
222{
223 GIOExtensionPoint *extension_point;
224 GList *l, *extensions;
225
226 iw->objects = g_array_new (FALSE, FALSE, element_size: sizeof (ChildData));
227
228 gtk_widget_init_template (GTK_WIDGET (iw));
229
230 g_object_bind_property_full (source: iw->object_details, source_property: "visible-child-name",
231 target: iw->object_start_stack, target_property: "visible-child-name",
232 flags: G_BINDING_SYNC_CREATE,
233 transform_to: translate_visible_child_name,
234 NULL,
235 user_data: iw,
236 NULL);
237
238 gtk_window_group_add_window (window_group: gtk_window_group_new (), GTK_WINDOW (iw));
239
240 extension_point = g_io_extension_point_lookup (name: "gtk-inspector-page");
241 extensions = g_io_extension_point_get_extensions (extension_point);
242
243 for (l = extensions; l != NULL; l = l->next)
244 {
245 GIOExtension *extension = l->data;
246 GType type;
247 GtkWidget *widget;
248 const char *name;
249 char *title;
250 GtkWidget *button;
251 gboolean use_picker;
252
253 type = g_io_extension_get_type (extension);
254
255 widget = g_object_new (object_type: type, NULL);
256
257 iw->extra_pages = g_list_prepend (list: iw->extra_pages, data: widget);
258
259 name = g_io_extension_get_name (extension);
260 g_object_get (object: widget, first_property_name: "title", &title, NULL);
261
262 if (g_object_class_find_property (G_OBJECT_GET_CLASS (widget), property_name: "use-picker"))
263 g_object_get (object: widget, first_property_name: "use-picker", &use_picker, NULL);
264 else
265 use_picker = FALSE;
266
267 if (use_picker)
268 {
269 button = gtk_button_new_from_icon_name (icon_name: "find-location-symbolic");
270 gtk_widget_set_focus_on_click (widget: button, FALSE);
271 gtk_widget_set_halign (widget: button, align: GTK_ALIGN_START);
272 gtk_widget_set_valign (widget: button, align: GTK_ALIGN_CENTER);
273 g_signal_connect (button, "clicked",
274 G_CALLBACK (gtk_inspector_on_inspect), iw);
275 }
276 else
277 button = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 0);
278
279 gtk_stack_add_titled (GTK_STACK (iw->top_stack), child: widget, name, title);
280 gtk_stack_add_named (GTK_STACK (iw->button_stack), child: button, name);
281 gtk_widget_show (widget);
282 gtk_widget_show (widget: button);
283
284 g_free (mem: title);
285 }
286}
287
288static void
289gtk_inspector_window_constructed (GObject *object)
290{
291 GtkInspectorWindow *iw = GTK_INSPECTOR_WINDOW (object);
292
293 G_OBJECT_CLASS (gtk_inspector_window_parent_class)->constructed (object);
294
295 g_object_set_data (G_OBJECT (iw->inspected_display), key: "-gtk-inspector", data: iw);
296 any_inspector_window_constructed = TRUE;
297
298 gtk_inspector_object_tree_set_display (GTK_INSPECTOR_OBJECT_TREE (iw->object_tree), display: iw->inspected_display);
299 gtk_inspector_css_editor_set_display (GTK_INSPECTOR_CSS_EDITOR (iw->css_editor), display: iw->inspected_display);
300 gtk_inspector_visual_set_display (GTK_INSPECTOR_VISUAL (iw->visual), display: iw->inspected_display);
301 gtk_inspector_general_set_display (GTK_INSPECTOR_GENERAL (iw->general), display: iw->inspected_display);
302 gtk_inspector_clipboard_set_display (GTK_INSPECTOR_CLIPBOARD (iw->clipboard), display: iw->inspected_display);
303 gtk_inspector_logs_set_display (GTK_INSPECTOR_LOGS (iw->logs), display: iw->inspected_display);
304 gtk_inspector_css_node_tree_set_display (GTK_INSPECTOR_CSS_NODE_TREE (iw->widget_css_node_tree), display: iw->inspected_display);
305}
306
307static void
308gtk_inspector_window_dispose (GObject *object)
309{
310 GtkInspectorWindow *iw = GTK_INSPECTOR_WINDOW (object);
311
312 g_object_set_data (G_OBJECT (iw->inspected_display), key: "-gtk-inspector", NULL);
313
314 g_clear_pointer (&iw->top_stack, gtk_widget_unparent);
315 g_clear_object (&iw->flash_overlay);
316 g_clear_pointer (&iw->objects, g_array_unref);
317
318 G_OBJECT_CLASS (gtk_inspector_window_parent_class)->dispose (object);
319}
320
321static void
322object_details_changed (GtkWidget *combo,
323 GParamSpec *pspec,
324 GtkInspectorWindow *iw)
325{
326 gtk_stack_set_visible_child_name (GTK_STACK (iw->object_center_stack), name: "title");
327}
328
329static void
330go_up_cb (GtkWidget *button,
331 GtkInspectorWindow *iw)
332{
333 if (iw->objects->len > 1)
334 {
335 gtk_inspector_window_pop_object (iw);
336 return;
337 }
338 else if (iw->objects->len > 0)
339 {
340 ChildData *data = &g_array_index (iw->objects, ChildData, 0);
341 GtkWidget *widget = (GtkWidget *)data->object;
342 if (GTK_IS_WIDGET (widget) && gtk_widget_get_parent (widget))
343 {
344 GObject *obj = G_OBJECT (gtk_widget_get_parent (widget));
345 gtk_inspector_window_replace_object (iw, object: obj, kind: CHILD_KIND_WIDGET, position: 0);
346 return;
347 }
348 }
349
350 gtk_widget_error_bell (GTK_WIDGET (iw));
351}
352
353static void
354go_down_cb (GtkWidget *button,
355 GtkInspectorWindow *iw)
356{
357 ChildData *data;
358 GObject *object;
359
360 if (iw->objects->len < 1)
361 {
362 gtk_widget_error_bell (GTK_WIDGET (iw));
363 return;
364 }
365
366 data = &g_array_index (iw->objects, ChildData, iw->objects->len - 1);
367 object = data->object;
368
369 if (GTK_IS_WIDGET (object))
370 {
371 GtkWidget *child = gtk_widget_get_first_child (GTK_WIDGET (object));
372
373 if (child)
374 {
375 gtk_inspector_window_push_object (iw, G_OBJECT (child), kind: CHILD_KIND_WIDGET, position: 0);
376 return;
377 }
378 }
379 else if (G_IS_LIST_MODEL (ptr: object))
380 {
381 GObject *item = g_list_model_get_item (list: G_LIST_MODEL (ptr: object), position: 0);
382 if (item)
383 {
384 gtk_inspector_window_push_object (iw, object: item, kind: CHILD_KIND_LISTITEM, position: 0);
385 g_object_unref (object: item);
386 return;
387 }
388 }
389
390 gtk_widget_error_bell (GTK_WIDGET (iw));
391}
392
393static void
394go_previous_cb (GtkWidget *button,
395 GtkInspectorWindow *iw)
396{
397 ChildData *data;
398 GObject *object;
399 GObject *parent;
400
401 if (iw->objects->len < 1)
402 {
403 gtk_widget_error_bell (GTK_WIDGET (iw));
404 return;
405 }
406
407 if (iw->objects->len > 1)
408 {
409 data = &g_array_index (iw->objects, ChildData, iw->objects->len - 2);
410 parent = data->object;
411 }
412 else
413 parent = NULL;
414
415 data = &g_array_index (iw->objects, ChildData, iw->objects->len - 1);
416 object = data->object;
417
418 switch (data->kind)
419 {
420 case CHILD_KIND_WIDGET:
421 {
422 GtkWidget *sibling = gtk_widget_get_prev_sibling (GTK_WIDGET (object));
423 if (sibling)
424 {
425 gtk_inspector_window_replace_object (iw, object: (GObject*)sibling, kind: CHILD_KIND_WIDGET, position: 0);
426 return;
427 }
428 }
429 break;
430
431 case CHILD_KIND_LISTITEM:
432 {
433 GObject *item;
434
435 if (parent && data->position > 0)
436 item = g_list_model_get_item (list: G_LIST_MODEL (ptr: parent), position: data->position - 1);
437 else
438 item = NULL;
439
440 if (item)
441 {
442 gtk_inspector_window_replace_object (iw, object: item, kind: CHILD_KIND_LISTITEM, position: data->position - 1);
443 g_object_unref (object: item);
444 return;
445 }
446 }
447 break;
448
449 case CHILD_KIND_CONTROLLER:
450 case CHILD_KIND_PROPERTY:
451 case CHILD_KIND_OTHER:
452 default: ;
453 }
454
455 gtk_widget_error_bell (GTK_WIDGET (iw));
456}
457
458static void
459go_next_cb (GtkWidget *button,
460 GtkInspectorWindow *iw)
461{
462 ChildData *data;
463 GObject *object;
464 GObject *parent;
465
466 if (iw->objects->len < 1)
467 {
468 gtk_widget_error_bell (GTK_WIDGET (iw));
469 return;
470 }
471
472 if (iw->objects->len > 1)
473 {
474 data = &g_array_index (iw->objects, ChildData, iw->objects->len - 2);
475 parent = data->object;
476 }
477 else
478 parent = NULL;
479
480 data = &g_array_index (iw->objects, ChildData, iw->objects->len - 1);
481 object = data->object;
482
483 switch (data->kind)
484 {
485 case CHILD_KIND_WIDGET:
486 {
487 GtkWidget *sibling = gtk_widget_get_next_sibling (GTK_WIDGET (object));
488 if (sibling)
489 {
490 gtk_inspector_window_replace_object (iw, object: (GObject*)sibling, kind: CHILD_KIND_WIDGET, position: 0);
491 return;
492 }
493 }
494 break;
495
496 case CHILD_KIND_LISTITEM:
497 {
498 GObject *item;
499
500 if (parent &&
501 data->position + 1 < g_list_model_get_n_items (list: G_LIST_MODEL (ptr: parent)))
502 item = g_list_model_get_item (list: G_LIST_MODEL (ptr: parent), position: data->position + 1);
503 else
504 item = NULL;
505
506 if (item)
507 {
508 gtk_inspector_window_replace_object (iw, object: item, kind: CHILD_KIND_LISTITEM, position: data->position + 1);
509 g_object_unref (object: item);
510 return;
511 }
512 }
513 break;
514
515 case CHILD_KIND_CONTROLLER:
516 case CHILD_KIND_PROPERTY:
517 case CHILD_KIND_OTHER:
518 default: ;
519 }
520
521 gtk_widget_error_bell (GTK_WIDGET (iw));
522}
523
524static void
525gtk_inspector_window_realize (GtkWidget *widget)
526{
527 GskRenderer *renderer;
528 GtkCssProvider *provider;
529
530 GTK_WIDGET_CLASS (gtk_inspector_window_parent_class)->realize (widget);
531
532 renderer = gtk_native_get_renderer (self: GTK_NATIVE (ptr: widget));
533 gsk_renderer_set_debug_flags (renderer, flags: 0);
534
535 provider = gtk_css_provider_new ();
536 gtk_css_provider_load_from_resource (css_provider: provider, resource_path: "/org/gtk/libgtk/inspector/inspector.css");
537 gtk_style_context_add_provider_for_display (display: gtk_widget_get_display (widget),
538 GTK_STYLE_PROVIDER (provider),
539 priority: 800);
540 g_object_unref (object: provider);
541}
542
543static void
544gtk_inspector_window_set_property (GObject *object,
545 guint prop_id,
546 const GValue *value,
547 GParamSpec *pspec)
548{
549 GtkInspectorWindow *iw = GTK_INSPECTOR_WINDOW (object);
550
551 switch (prop_id)
552 {
553 case PROP_INSPECTED_DISPLAY:
554 iw->inspected_display = g_value_get_object (value);
555 break;
556
557 default:
558 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
559 break;
560 }
561}
562
563static void
564gtk_inspector_window_get_property (GObject *object,
565 guint prop_id,
566 GValue *value,
567 GParamSpec *pspec)
568{
569 GtkInspectorWindow *iw = GTK_INSPECTOR_WINDOW (object);
570
571 switch (prop_id)
572 {
573 case PROP_INSPECTED_DISPLAY:
574 g_value_set_object (value, v_object: iw->inspected_display);
575 break;
576
577 default:
578 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
579 break;
580 }
581}
582
583static gboolean
584gtk_inspector_window_enable_debugging (GtkWindow *window,
585 gboolean toggle)
586{
587 return FALSE;
588}
589
590static void
591gtk_inspector_window_class_init (GtkInspectorWindowClass *klass)
592{
593 GObjectClass *object_class = G_OBJECT_CLASS (klass);
594 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
595 GtkWindowClass *window_class = GTK_WINDOW_CLASS (klass);
596
597 object_class->constructed = gtk_inspector_window_constructed;
598 object_class->dispose = gtk_inspector_window_dispose;
599 object_class->set_property = gtk_inspector_window_set_property;
600 object_class->get_property = gtk_inspector_window_get_property;
601
602 widget_class->realize = gtk_inspector_window_realize;
603
604 window_class->enable_debugging = gtk_inspector_window_enable_debugging;
605
606 properties[PROP_INSPECTED_DISPLAY] =
607 g_param_spec_object (name: "inspected-display", nick: "Inspected display", blurb: "Inspected display",
608 GDK_TYPE_DISPLAY,
609 flags: G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
610 g_object_class_install_properties (oclass: object_class, n_pspecs: NUM_PROPERTIES, pspecs: properties);
611
612 signals[EVENT] = g_signal_new (signal_name: g_intern_static_string (string: "event"),
613 G_OBJECT_CLASS_TYPE (object_class),
614 signal_flags: G_SIGNAL_RUN_LAST,
615 class_offset: 0,
616 accumulator: g_signal_accumulator_true_handled,
617 NULL,
618 c_marshaller: _gdk_marshal_BOOLEAN__POINTER,
619 G_TYPE_BOOLEAN,
620 n_params: 1,
621 GDK_TYPE_EVENT);
622 g_signal_set_va_marshaller (signal_id: signals[EVENT],
623 G_OBJECT_CLASS_TYPE (object_class),
624 va_marshaller: _gdk_marshal_BOOLEAN__POINTERv);
625
626 gtk_widget_class_set_template_from_resource (widget_class, resource_name: "/org/gtk/libgtk/inspector/window.ui");
627
628 gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, top_stack);
629 gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, button_stack);
630 gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, object_stack);
631 gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, object_tree);
632 gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, object_details);
633 gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, object_start_stack);
634 gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, object_center_stack);
635 gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, object_buttons);
636 gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, object_details_button);
637 gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, select_object);
638 gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, prop_list);
639 gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, layout_prop_list);
640 gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, widget_css_node_tree);
641 gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, widget_recorder);
642 gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, object_title);
643 gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, size_groups);
644 gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, tree_data);
645 gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, list_data);
646 gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, actions);
647 gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, shortcuts);
648 gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, menu);
649 gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, misc_info);
650 gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, controllers);
651 gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, magnifier);
652 gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, a11y);
653 gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, sidebar_revealer);
654 gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, css_editor);
655 gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, visual);
656 gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, general);
657 gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, clipboard);
658 gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, logs);
659
660 gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, go_up_button);
661 gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, go_down_button);
662 gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, go_previous_button);
663 gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, list_position_label);
664 gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, go_next_button);
665
666 gtk_widget_class_bind_template_callback (widget_class, gtk_inspector_on_inspect);
667 gtk_widget_class_bind_template_callback (widget_class, on_object_activated);
668 gtk_widget_class_bind_template_callback (widget_class, on_object_selected);
669 gtk_widget_class_bind_template_callback (widget_class, open_object_details);
670 gtk_widget_class_bind_template_callback (widget_class, close_object_details);
671 gtk_widget_class_bind_template_callback (widget_class, object_details_changed);
672 gtk_widget_class_bind_template_callback (widget_class, notify_node);
673 gtk_widget_class_bind_template_callback (widget_class, go_previous_cb);
674 gtk_widget_class_bind_template_callback (widget_class, go_up_cb);
675 gtk_widget_class_bind_template_callback (widget_class, go_down_cb);
676 gtk_widget_class_bind_template_callback (widget_class, go_next_cb);
677}
678
679static GdkDisplay *
680get_inspector_display (void)
681{
682 static GdkDisplay *display = NULL;
683
684 if (display == NULL)
685 {
686 const char *name;
687
688 name = g_getenv (variable: "GTK_INSPECTOR_DISPLAY");
689 display = gdk_display_open (display_name: name);
690
691 if (display)
692 g_debug ("Using display %s for GtkInspector", name);
693 else
694 g_message ("Failed to open display %s", name);
695 }
696
697 if (!display)
698 {
699 display = gdk_display_open (NULL);
700 if (display)
701 g_debug ("Using default display for GtkInspector");
702 else
703 g_message ("Failed to separate connection to default display");
704 }
705
706
707 if (display)
708 {
709 const char *name;
710 GdkDebugFlags flags;
711
712 name = g_getenv (variable: "GTK_INSPECTOR_RENDERER");
713
714 g_object_set_data_full (G_OBJECT (display), key: "gsk-renderer",
715 data: g_strdup (str: name), destroy: g_free);
716
717 flags = gdk_display_get_debug_flags (display: gdk_display_get_default ());
718
719 gdk_display_set_debug_flags (display, flags: flags & (GDK_DEBUG_GL_GLES | GDK_DEBUG_GL_GLX));
720 gtk_set_display_debug_flags (display, flags: 0);
721 }
722
723 if (!display)
724 display = gdk_display_get_default ();
725
726 if (display == gdk_display_get_default ())
727 g_message ("Using default display for GtkInspector; expect some spillover");
728
729 return display;
730}
731
732static GtkInspectorWindow *
733gtk_inspector_window_new (GdkDisplay *display)
734{
735 GtkInspectorWindow *iw;
736
737 iw = g_object_new (GTK_TYPE_INSPECTOR_WINDOW,
738 first_property_name: "display", get_inspector_display (),
739 "inspected-display", display,
740 NULL);
741
742 return iw;
743}
744
745GtkWidget *
746gtk_inspector_window_get (GdkDisplay *display)
747{
748 GtkWidget *iw;
749
750 gtk_inspector_init ();
751
752 iw = GTK_WIDGET (g_object_get_data (G_OBJECT (display), "-gtk-inspector"));
753
754 if (!iw)
755 iw = GTK_WIDGET (gtk_inspector_window_new (display));
756
757 return iw;
758}
759
760void
761gtk_inspector_window_add_overlay (GtkInspectorWindow *iw,
762 GtkInspectorOverlay *overlay)
763{
764 iw->overlays = g_list_prepend (list: iw->overlays, g_object_ref (overlay));
765
766 gtk_inspector_overlay_queue_draw (self: overlay);
767}
768
769void
770gtk_inspector_window_remove_overlay (GtkInspectorWindow *iw,
771 GtkInspectorOverlay *overlay)
772{
773 GList *item;
774
775 item = g_list_find (list: iw->overlays, data: overlay);
776 if (item == NULL)
777 return;
778
779 gtk_inspector_overlay_queue_draw (self: overlay);
780
781 iw->overlays = g_list_delete_link (list: iw->overlays, link_: item);
782 g_object_unref (object: overlay);
783}
784
785static GtkInspectorWindow *
786gtk_inspector_window_get_for_display (GdkDisplay *display)
787{
788 return g_object_get_data (G_OBJECT (display), key: "-gtk-inspector");
789}
790
791GskRenderNode *
792gtk_inspector_prepare_render (GtkWidget *widget,
793 GskRenderer *renderer,
794 GdkSurface *surface,
795 const cairo_region_t *region,
796 GskRenderNode *root,
797 GskRenderNode *widget_node)
798{
799 GtkInspectorWindow *iw;
800
801 iw = gtk_inspector_window_get_for_display (display: gtk_widget_get_display (widget));
802 if (iw == NULL)
803 return root;
804
805 /* sanity check for single-display GDK backends */
806 if (GTK_WIDGET (iw) == widget)
807 return root;
808
809 gtk_inspector_recorder_record_render (GTK_INSPECTOR_RECORDER (iw->widget_recorder),
810 widget,
811 renderer,
812 surface,
813 region,
814 node: root);
815
816 if (iw->overlays)
817 {
818 GtkSnapshot *snapshot;
819 GList *l;
820 double native_x, native_y;
821
822 snapshot = gtk_snapshot_new ();
823 gtk_snapshot_append_node (snapshot, node: root);
824
825 gtk_native_get_surface_transform (self: GTK_NATIVE (ptr: widget), x: &native_x, y: &native_y);
826
827 gtk_snapshot_save (snapshot);
828 gtk_snapshot_translate (snapshot, point: &(graphene_point_t) { native_x, native_y });
829
830 for (l = iw->overlays; l; l = l->next)
831 {
832 gtk_inspector_overlay_snapshot (self: l->data, snapshot, node: widget_node, widget);
833 }
834
835 gtk_snapshot_restore (snapshot);
836
837 gsk_render_node_unref (node: root);
838 root = gtk_snapshot_free_to_node (snapshot);
839 }
840
841 return root;
842}
843
844gboolean
845gtk_inspector_is_recording (GtkWidget *widget)
846{
847 GtkInspectorWindow *iw;
848
849 if (!any_inspector_window_constructed)
850 return FALSE;
851
852 iw = gtk_inspector_window_get_for_display (display: gtk_widget_get_display (widget));
853 if (iw == NULL)
854 return FALSE;
855
856 /* sanity check for single-display GDK backends */
857 if (GTK_WIDGET (iw) == widget)
858 return FALSE;
859
860 return gtk_inspector_recorder_is_recording (GTK_INSPECTOR_RECORDER (iw->widget_recorder));
861}
862
863gboolean
864gtk_inspector_handle_event (GdkEvent *event)
865{
866 GtkInspectorWindow *iw;
867 gboolean handled = FALSE;
868
869 if (!any_inspector_window_constructed)
870 return FALSE;
871
872 iw = gtk_inspector_window_get_for_display (display: gdk_event_get_display (event));
873 if (iw == NULL)
874 return FALSE;
875
876 gtk_inspector_recorder_record_event (GTK_INSPECTOR_RECORDER (iw->widget_recorder),
877 widget: gtk_get_event_widget (event),
878 event);
879
880 g_signal_emit (instance: iw, signal_id: signals[EVENT], detail: 0, event, &handled);
881
882 return handled;
883}
884
885GdkDisplay *
886gtk_inspector_window_get_inspected_display (GtkInspectorWindow *iw)
887{
888 return iw->inspected_display;
889}
890
891static void
892update_go_button (GtkWidget *button,
893 gboolean enabled,
894 const char *tooltip)
895{
896 gtk_widget_set_sensitive (widget: button, sensitive: enabled);
897 gtk_widget_set_tooltip_text (widget: button, text: tooltip);
898}
899
900static void
901update_go_buttons (GtkInspectorWindow *iw)
902{
903 GObject *parent;
904 GObject *object;
905 ChildKind kind;
906 guint position;
907
908 if (iw->objects->len > 1)
909 {
910 ChildData *data = &g_array_index (iw->objects, ChildData, iw->objects->len - 2);
911 parent = data->object;
912 }
913 else
914 {
915 parent = NULL;
916 }
917
918 if (iw->objects->len > 0)
919 {
920 ChildData *data = &g_array_index (iw->objects, ChildData, iw->objects->len - 1);
921 object = data->object;
922 kind = data->kind;
923 position = data->position;
924 }
925 else
926 {
927 object = NULL;
928 kind = CHILD_KIND_OTHER;
929 position = 0;
930 }
931
932 if (parent)
933 {
934 char *text;
935 text = g_strdup_printf (format: "Go to %s", G_OBJECT_TYPE_NAME (parent));
936 update_go_button (button: iw->go_up_button, TRUE, tooltip: text);
937 g_free (mem: text);
938 }
939 else
940 {
941 update_go_button (button: iw->go_up_button, GTK_IS_WIDGET (object) && !GTK_IS_ROOT (ptr: object), tooltip: "Parent widget");
942 }
943
944 switch (kind)
945 {
946 case CHILD_KIND_WIDGET:
947 update_go_button (button: iw->go_down_button,
948 GTK_IS_WIDGET (object) &&gtk_widget_get_first_child (GTK_WIDGET (object)) != NULL,
949 tooltip: "First child");
950 update_go_button (button: iw->go_previous_button,
951 GTK_IS_WIDGET (object) && gtk_widget_get_prev_sibling (GTK_WIDGET (object)) != NULL,
952 tooltip: "Previous sibling");
953 update_go_button (button: iw->go_next_button,
954 GTK_IS_WIDGET (object) && gtk_widget_get_next_sibling (GTK_WIDGET (object)) != NULL,
955 tooltip: "Next sibling");
956 gtk_widget_hide (widget: iw->list_position_label);
957 break;
958 case CHILD_KIND_LISTITEM:
959 update_go_button (button: iw->go_down_button, FALSE, NULL);
960 update_go_button (button: iw->go_previous_button, enabled: position > 0, tooltip: "Previous list item");
961 update_go_button (button: iw->go_next_button, enabled: position + 1 < g_list_model_get_n_items (list: G_LIST_MODEL (ptr: parent)), tooltip: "Next list item");
962 {
963 char *text = g_strdup_printf (format: "%u", position);
964 gtk_label_set_label (GTK_LABEL (iw->list_position_label), str: text);
965 g_free (mem: text);
966 gtk_widget_show (widget: iw->list_position_label);
967 }
968 break;
969 case CHILD_KIND_PROPERTY:
970 case CHILD_KIND_CONTROLLER:
971 case CHILD_KIND_OTHER:
972 update_go_button (button: iw->go_down_button, FALSE, NULL);
973 update_go_button (button: iw->go_previous_button, FALSE, NULL);
974 update_go_button (button: iw->go_next_button, FALSE, NULL);
975 gtk_widget_hide (widget: iw->list_position_label);
976 break;
977 default:
978 g_assert_not_reached ();
979 break;
980 }
981}
982
983static void
984show_object_details (GtkInspectorWindow *iw,
985 GObject *object,
986 const char *tab)
987{
988 set_selected_object (iw, selected: object);
989
990 if (tab)
991 gtk_stack_set_visible_child_name (GTK_STACK (iw->object_details), name: tab);
992 if (!gtk_stack_get_visible_child_name (GTK_STACK (iw->object_details)))
993 gtk_stack_set_visible_child_name (GTK_STACK (iw->object_details), name: "properties");
994
995 gtk_stack_set_visible_child_name (GTK_STACK (iw->object_stack), name: "object-details");
996 gtk_stack_set_visible_child_name (GTK_STACK (iw->object_buttons), name: "details");
997}
998
999void
1000gtk_inspector_window_push_object (GtkInspectorWindow *iw,
1001 GObject *object,
1002 ChildKind kind,
1003 guint position)
1004{
1005 ChildData data;
1006
1007 data.kind = kind;
1008 data.object = object;
1009 data.position = position;
1010 g_array_append_val (iw->objects, data);
1011 show_object_details (iw, object, tab: "properties");
1012 update_go_buttons (iw);
1013}
1014
1015void
1016gtk_inspector_window_pop_object (GtkInspectorWindow *iw)
1017{
1018 ChildData *data;
1019 const char *tabs[] = {
1020 "properties",
1021 "controllers",
1022 "properties",
1023 "list-data",
1024 "misc",
1025 };
1026 const char *tab;
1027
1028 if (iw->objects->len < 2)
1029 {
1030 gtk_widget_error_bell (GTK_WIDGET (iw));
1031 return;
1032 }
1033
1034 data = &g_array_index (iw->objects, ChildData, iw->objects->len - 1);
1035 tab = tabs[data->kind];
1036 g_array_remove_index (array: iw->objects, index_: iw->objects->len - 1);
1037 data = &g_array_index (iw->objects, ChildData, iw->objects->len - 1);
1038 show_object_details (iw, object: data->object, tab);
1039 update_go_buttons (iw);
1040}
1041
1042void
1043gtk_inspector_window_replace_object (GtkInspectorWindow *iw,
1044 GObject *object,
1045 ChildKind kind,
1046 guint position)
1047{
1048 ChildData *data;
1049
1050 data = &g_array_index (iw->objects, ChildData, iw->objects->len - 1);
1051 g_assert (data->kind == kind);
1052 data->object = object;
1053 data->position = position;
1054 show_object_details (iw, object, NULL);
1055 update_go_buttons (iw);
1056}
1057
1058void
1059gtk_inspector_window_set_object (GtkInspectorWindow *iw,
1060 GObject *object,
1061 ChildKind kind,
1062 guint position)
1063{
1064 g_array_set_size (array: iw->objects, length: 0);
1065 gtk_inspector_window_push_object (iw, object, kind, position);
1066 update_go_buttons (iw);
1067}
1068
1069// vim: set et sw=2 ts=2:
1070
1071

source code of gtk/gtk/inspector/window.c