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 <string.h>
29
30 #include "object-tree.h"
31 #include "prop-list.h"
32
33 #include "gtkbuildable.h"
34 #include "gtkbutton.h"
35 #include "gtkcelllayout.h"
36 #include "gtkcomboboxprivate.h"
37 #include "gtkiconview.h"
38 #include "gtklabel.h"
39 #include "gtkmenuitem.h"
40 #include "gtksettings.h"
41 #include "gtktextview.h"
42 #include "gtktreeview.h"
43 #include "gtktreeselection.h"
44 #include "gtktreestore.h"
45 #include "gtktreemodelsort.h"
46 #include "gtktreemodelfilter.h"
47 #include "gtkwidgetprivate.h"
48 #include "gtkstylecontext.h"
49 #include "gtksearchbar.h"
50 #include "gtksearchentry.h"
51 #include "treewalk.h"
52
53 enum
54 {
55 OBJECT,
56 OBJECT_TYPE,
57 OBJECT_NAME,
58 OBJECT_LABEL,
59 OBJECT_CLASSES,
60 SENSITIVE
61 };
62
63
64 enum
65 {
66 OBJECT_SELECTED,
67 OBJECT_ACTIVATED,
68 LAST_SIGNAL
69 };
70
71
72 struct _GtkInspectorObjectTreePrivate
73 {
74 GtkTreeView *tree;
75 GtkTreeStore *model;
76 gulong map_hook;
77 gulong unmap_hook;
78 GtkTreeViewColumn *object_column;
79 GtkWidget *search_bar;
80 GtkWidget *search_entry;
81 GtkTreeWalk *walk;
82 gint search_length;
83 };
84
85 typedef struct _ObjectTreeClassFuncs ObjectTreeClassFuncs;
86 typedef void (* ObjectTreeForallFunc) (GObject *object,
87 const char *name,
88 gpointer data);
89
90 struct _ObjectTreeClassFuncs {
91 GType (* get_type) (void);
92 GObject * (* get_parent) (GObject *object);
93 void (* forall) (GObject *object,
94 ObjectTreeForallFunc forall_func,
95 gpointer forall_data);
96 gboolean (* get_sensitive) (GObject *object);
97 };
98
99 static guint signals[LAST_SIGNAL] = { 0 };
100
G_DEFINE_TYPE_WITH_PRIVATE(GtkInspectorObjectTree,gtk_inspector_object_tree,GTK_TYPE_BOX)101 G_DEFINE_TYPE_WITH_PRIVATE (GtkInspectorObjectTree, gtk_inspector_object_tree, GTK_TYPE_BOX)
102
103 static GObject *
104 object_tree_get_parent_default (GObject *object)
105 {
106 return g_object_get_data (object, "inspector-object-tree-parent");
107 }
108
109 static void
object_tree_forall_default(GObject * object,ObjectTreeForallFunc forall_func,gpointer forall_data)110 object_tree_forall_default (GObject *object,
111 ObjectTreeForallFunc forall_func,
112 gpointer forall_data)
113 {
114 }
115
116 static gboolean
object_tree_get_sensitive_default(GObject * object)117 object_tree_get_sensitive_default (GObject *object)
118 {
119 return TRUE;
120 }
121
122 static GObject *
object_tree_widget_get_parent(GObject * object)123 object_tree_widget_get_parent (GObject *object)
124 {
125 return G_OBJECT (gtk_widget_get_parent (GTK_WIDGET (object)));
126 }
127
128 static void
object_tree_widget_forall(GObject * object,ObjectTreeForallFunc forall_func,gpointer forall_data)129 object_tree_widget_forall (GObject *object,
130 ObjectTreeForallFunc forall_func,
131 gpointer forall_data)
132 {
133 struct {
134 GtkPropagationPhase phase;
135 const gchar *name;
136 } phases[] = {
137 { GTK_PHASE_CAPTURE, "capture" },
138 { GTK_PHASE_TARGET, "target" },
139 { GTK_PHASE_BUBBLE, "bubble" },
140 { GTK_PHASE_NONE, "" }
141 };
142 gint i;
143
144 for (i = 0; i < G_N_ELEMENTS (phases); i++)
145 {
146 GList *list, *l;
147
148 list = _gtk_widget_list_controllers (GTK_WIDGET (object), phases[i].phase);
149 for (l = list; l; l = l->next)
150 {
151 GObject *controller = l->data;
152 forall_func (controller, phases[i].name, forall_data);
153 }
154 g_list_free (list);
155 }
156
157 if (gtk_widget_is_toplevel (GTK_WIDGET (object)))
158 {
159 GObject *clock;
160
161 clock = G_OBJECT (gtk_widget_get_frame_clock (GTK_WIDGET (object)));
162 if (clock)
163 forall_func (clock, "frame-clock", forall_data);
164 }
165 }
166
167 static gboolean
object_tree_widget_get_sensitive(GObject * object)168 object_tree_widget_get_sensitive (GObject *object)
169 {
170 return gtk_widget_get_mapped (GTK_WIDGET (object));
171 }
172
173 typedef struct {
174 ObjectTreeForallFunc forall_func;
175 gpointer forall_data;
176 } ForallData;
177
178 static void
container_children_callback(GtkWidget * widget,gpointer client_data)179 container_children_callback (GtkWidget *widget,
180 gpointer client_data)
181 {
182 ForallData *forall_data = client_data;
183
184 forall_data->forall_func (G_OBJECT (widget), NULL, forall_data->forall_data);
185 }
186
187 static void
object_tree_container_forall(GObject * object,ObjectTreeForallFunc forall_func,gpointer forall_data)188 object_tree_container_forall (GObject *object,
189 ObjectTreeForallFunc forall_func,
190 gpointer forall_data)
191 {
192 ForallData data = {
193 forall_func,
194 forall_data
195 };
196
197 gtk_container_forall (GTK_CONTAINER (object),
198 container_children_callback,
199 &data);
200 }
201
202 static void
object_tree_tree_model_sort_forall(GObject * object,ObjectTreeForallFunc forall_func,gpointer forall_data)203 object_tree_tree_model_sort_forall (GObject *object,
204 ObjectTreeForallFunc forall_func,
205 gpointer forall_data)
206 {
207 GObject *child = G_OBJECT (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (object)));
208
209 if (child)
210 forall_func (child, "model", forall_data);
211 }
212
213 static void
object_tree_tree_model_filter_forall(GObject * object,ObjectTreeForallFunc forall_func,gpointer forall_data)214 object_tree_tree_model_filter_forall (GObject *object,
215 ObjectTreeForallFunc forall_func,
216 gpointer forall_data)
217 {
218 GObject *child = G_OBJECT (gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (object)));
219
220 if (child)
221 forall_func (child, "model", forall_data);
222 }
223
224 static void
object_tree_menu_item_forall(GObject * object,ObjectTreeForallFunc forall_func,gpointer forall_data)225 object_tree_menu_item_forall (GObject *object,
226 ObjectTreeForallFunc forall_func,
227 gpointer forall_data)
228 {
229 GtkWidget *submenu;
230
231 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (object));
232 if (submenu)
233 forall_func (G_OBJECT (submenu), "submenu", forall_data);
234 }
235
236 static void
object_tree_combo_box_forall(GObject * object,ObjectTreeForallFunc forall_func,gpointer forall_data)237 object_tree_combo_box_forall (GObject *object,
238 ObjectTreeForallFunc forall_func,
239 gpointer forall_data)
240 {
241 GtkWidget *popup;
242 GObject *child;
243
244 popup = gtk_combo_box_get_popup (GTK_COMBO_BOX (object));
245 if (popup)
246 forall_func (G_OBJECT (popup), "popup", forall_data);
247
248 child = G_OBJECT (gtk_combo_box_get_model (GTK_COMBO_BOX (object)));
249 if (child)
250 forall_func (child, "model", forall_data);
251 }
252
253 static void
object_tree_tree_view_forall(GObject * object,ObjectTreeForallFunc forall_func,gpointer forall_data)254 object_tree_tree_view_forall (GObject *object,
255 ObjectTreeForallFunc forall_func,
256 gpointer forall_data)
257 {
258 gint n_columns, i;
259 GObject *child;
260
261 child = G_OBJECT (gtk_tree_view_get_model (GTK_TREE_VIEW (object)));
262 if (child)
263 forall_func (child, "model", forall_data);
264
265 child = G_OBJECT (gtk_tree_view_get_selection (GTK_TREE_VIEW (object)));
266 if (child)
267 forall_func (child, "selection", forall_data);
268
269 n_columns = gtk_tree_view_get_n_columns (GTK_TREE_VIEW (object));
270 for (i = 0; i < n_columns; i++)
271 {
272 child = G_OBJECT (gtk_tree_view_get_column (GTK_TREE_VIEW (object), i));
273 forall_func (child, NULL, forall_data);
274 }
275 }
276
277 static void
object_tree_icon_view_forall(GObject * object,ObjectTreeForallFunc forall_func,gpointer forall_data)278 object_tree_icon_view_forall (GObject *object,
279 ObjectTreeForallFunc forall_func,
280 gpointer forall_data)
281 {
282 GObject *child;
283
284 child = G_OBJECT (gtk_icon_view_get_model (GTK_ICON_VIEW (object)));
285 if (child)
286 forall_func (child, "model", forall_data);
287 }
288
289 typedef struct {
290 ObjectTreeForallFunc forall_func;
291 gpointer forall_data;
292 GObject *parent;
293 } ParentForallData;
294
295 static gboolean
cell_callback(GtkCellRenderer * renderer,gpointer data)296 cell_callback (GtkCellRenderer *renderer,
297 gpointer data)
298 {
299 ParentForallData *d = data;
300 gpointer cell_layout;
301
302 cell_layout = g_object_get_data (d->parent, "gtk-inspector-cell-layout");
303 g_object_set_data (G_OBJECT (renderer), "gtk-inspector-cell-layout", cell_layout);
304 d->forall_func (G_OBJECT (renderer), NULL, d->forall_data);
305
306 return FALSE;
307 }
308
309 static void
object_tree_cell_area_forall(GObject * object,ObjectTreeForallFunc forall_func,gpointer forall_data)310 object_tree_cell_area_forall (GObject *object,
311 ObjectTreeForallFunc forall_func,
312 gpointer forall_data)
313 {
314 ParentForallData data = {
315 forall_func,
316 forall_data,
317 object
318 };
319
320 gtk_cell_area_foreach (GTK_CELL_AREA (object), cell_callback, &data);
321 }
322
323 static void
object_tree_cell_layout_forall(GObject * object,ObjectTreeForallFunc forall_func,gpointer forall_data)324 object_tree_cell_layout_forall (GObject *object,
325 ObjectTreeForallFunc forall_func,
326 gpointer forall_data)
327 {
328 GtkCellArea *area;
329
330 /* cell areas handle their own stuff */
331 if (GTK_IS_CELL_AREA (object))
332 return;
333
334 area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (object));
335 if (!area)
336 return;
337
338 g_object_set_data (G_OBJECT (area), "gtk-inspector-cell-layout", object);
339 forall_func (G_OBJECT (area), "cell-area", forall_data);
340 }
341
342 static void
object_tree_text_view_forall(GObject * object,ObjectTreeForallFunc forall_func,gpointer forall_data)343 object_tree_text_view_forall (GObject *object,
344 ObjectTreeForallFunc forall_func,
345 gpointer forall_data)
346 {
347 GtkTextBuffer *buffer;
348
349 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (object));
350 forall_func (G_OBJECT (buffer), "buffer", forall_data);
351 }
352
353 static void
object_tree_text_buffer_forall(GObject * object,ObjectTreeForallFunc forall_func,gpointer forall_data)354 object_tree_text_buffer_forall (GObject *object,
355 ObjectTreeForallFunc forall_func,
356 gpointer forall_data)
357 {
358 GtkTextTagTable *tags;
359
360 tags = gtk_text_buffer_get_tag_table (GTK_TEXT_BUFFER (object));
361 forall_func (G_OBJECT (tags), "tag-table", forall_data);
362 }
363
364 static void
tag_callback(GtkTextTag * tag,gpointer data)365 tag_callback (GtkTextTag *tag,
366 gpointer data)
367 {
368 ForallData *d = data;
369 gchar *name;
370
371 g_object_get (tag, "name", &name, NULL);
372 d->forall_func (G_OBJECT (tag), name, d->forall_data);
373 g_free (name);
374 }
375
376 static void
object_tree_text_tag_table_forall(GObject * object,ObjectTreeForallFunc forall_func,gpointer forall_data)377 object_tree_text_tag_table_forall (GObject *object,
378 ObjectTreeForallFunc forall_func,
379 gpointer forall_data)
380 {
381 ForallData data = {
382 forall_func,
383 forall_data
384 };
385
386 gtk_text_tag_table_foreach (GTK_TEXT_TAG_TABLE (object), tag_callback, &data);
387 }
388
389 static void
object_tree_application_forall(GObject * object,ObjectTreeForallFunc forall_func,gpointer forall_data)390 object_tree_application_forall (GObject *object,
391 ObjectTreeForallFunc forall_func,
392 gpointer forall_data)
393 {
394 GObject *menu;
395
396 menu = (GObject *)gtk_application_get_app_menu (GTK_APPLICATION (object));
397 if (menu)
398 forall_func (menu, "app-menu", forall_data);
399
400 menu = (GObject *)gtk_application_get_menubar (GTK_APPLICATION (object));
401 if (menu)
402 forall_func (menu, "menubar", forall_data);
403 }
404
405 /* Note:
406 * This tree must be sorted with the most specific types first.
407 * We iterate over it top to bottom and return the first match
408 * using g_type_is_a ()
409 */
410 static const ObjectTreeClassFuncs object_tree_class_funcs[] = {
411 {
412 gtk_application_get_type,
413 object_tree_get_parent_default,
414 object_tree_application_forall,
415 object_tree_get_sensitive_default
416 },
417 {
418 gtk_text_tag_table_get_type,
419 object_tree_get_parent_default,
420 object_tree_text_tag_table_forall,
421 object_tree_get_sensitive_default
422 },
423 {
424 gtk_text_buffer_get_type,
425 object_tree_get_parent_default,
426 object_tree_text_buffer_forall,
427 object_tree_get_sensitive_default
428 },
429 {
430 gtk_text_view_get_type,
431 object_tree_widget_get_parent,
432 object_tree_text_view_forall,
433 object_tree_widget_get_sensitive
434 },
435 {
436 gtk_icon_view_get_type,
437 object_tree_widget_get_parent,
438 object_tree_icon_view_forall,
439 object_tree_widget_get_sensitive
440 },
441 {
442 gtk_tree_view_get_type,
443 object_tree_widget_get_parent,
444 object_tree_tree_view_forall,
445 object_tree_widget_get_sensitive
446 },
447 {
448 gtk_combo_box_get_type,
449 object_tree_widget_get_parent,
450 object_tree_combo_box_forall,
451 object_tree_widget_get_sensitive
452 },
453 {
454 gtk_menu_item_get_type,
455 object_tree_widget_get_parent,
456 object_tree_menu_item_forall,
457 object_tree_widget_get_sensitive
458 },
459 {
460 gtk_container_get_type,
461 object_tree_widget_get_parent,
462 object_tree_container_forall,
463 object_tree_widget_get_sensitive
464 },
465 {
466 gtk_widget_get_type,
467 object_tree_widget_get_parent,
468 object_tree_widget_forall,
469 object_tree_widget_get_sensitive
470 },
471 {
472 gtk_tree_model_filter_get_type,
473 object_tree_get_parent_default,
474 object_tree_tree_model_filter_forall,
475 object_tree_get_sensitive_default
476 },
477 {
478 gtk_tree_model_sort_get_type,
479 object_tree_get_parent_default,
480 object_tree_tree_model_sort_forall,
481 object_tree_get_sensitive_default
482 },
483 {
484 gtk_cell_area_get_type,
485 object_tree_get_parent_default,
486 object_tree_cell_area_forall,
487 object_tree_get_sensitive_default
488 },
489 {
490 gtk_cell_layout_get_type,
491 object_tree_get_parent_default,
492 object_tree_cell_layout_forall,
493 object_tree_get_sensitive_default
494 },
495 {
496 g_object_get_type,
497 object_tree_get_parent_default,
498 object_tree_forall_default,
499 object_tree_get_sensitive_default
500 },
501 };
502
503 static const ObjectTreeClassFuncs *
find_class_funcs(GObject * object)504 find_class_funcs (GObject *object)
505 {
506 GType object_type;
507 guint i;
508
509 object_type = G_OBJECT_TYPE (object);
510
511 for (i = 0; i < G_N_ELEMENTS (object_tree_class_funcs); i++)
512 {
513 if (g_type_is_a (object_type, object_tree_class_funcs[i].get_type ()))
514 return &object_tree_class_funcs[i];
515 }
516
517 g_assert_not_reached ();
518
519 return NULL;
520 }
521
522 static GObject *
object_get_parent(GObject * object)523 object_get_parent (GObject *object)
524 {
525 const ObjectTreeClassFuncs *funcs;
526
527 funcs = find_class_funcs (object);
528
529 return funcs->get_parent (object);
530 }
531
532 static void
object_forall(GObject * object,ObjectTreeForallFunc forall_func,gpointer forall_data)533 object_forall (GObject *object,
534 ObjectTreeForallFunc forall_func,
535 gpointer forall_data)
536 {
537 GType object_type;
538 guint i;
539
540 object_type = G_OBJECT_TYPE (object);
541
542 for (i = 0; i < G_N_ELEMENTS (object_tree_class_funcs); i++)
543 {
544 if (g_type_is_a (object_type, object_tree_class_funcs[i].get_type ()))
545 object_tree_class_funcs[i].forall (object, forall_func, forall_data);
546 }
547 }
548
549 static gboolean
object_get_sensitive(GObject * object)550 object_get_sensitive (GObject *object)
551 {
552 const ObjectTreeClassFuncs *funcs;
553
554 funcs = find_class_funcs (object);
555
556 return funcs->get_sensitive (object);
557 }
558
559 static void
on_row_activated(GtkTreeView * tree,GtkTreePath * path,GtkTreeViewColumn * col,GtkInspectorObjectTree * wt)560 on_row_activated (GtkTreeView *tree,
561 GtkTreePath *path,
562 GtkTreeViewColumn *col,
563 GtkInspectorObjectTree *wt)
564 {
565 GtkTreeIter iter;
566 GObject *object;
567 gchar *name;
568
569 gtk_tree_model_get_iter (GTK_TREE_MODEL (wt->priv->model), &iter, path);
570 gtk_tree_model_get (GTK_TREE_MODEL (wt->priv->model), &iter,
571 OBJECT, &object,
572 OBJECT_NAME, &name,
573 -1);
574
575 g_signal_emit (wt, signals[OBJECT_ACTIVATED], 0, object, name);
576
577 g_free (name);
578 }
579
580 GObject *
gtk_inspector_object_tree_get_selected(GtkInspectorObjectTree * wt)581 gtk_inspector_object_tree_get_selected (GtkInspectorObjectTree *wt)
582 {
583 GObject *object;
584 GtkTreeIter iter;
585 GtkTreeSelection *sel;
586 GtkTreeModel *model;
587
588 object = NULL;
589 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (wt->priv->tree));
590 if (gtk_tree_selection_get_selected (sel, &model, &iter))
591 gtk_tree_model_get (model, &iter,
592 OBJECT, &object,
593 -1);
594
595 return object;
596 }
597
598 static void
on_selection_changed(GtkTreeSelection * selection,GtkInspectorObjectTree * wt)599 on_selection_changed (GtkTreeSelection *selection,
600 GtkInspectorObjectTree *wt)
601 {
602 GObject *object;
603 GtkTreeIter iter;
604
605 if (gtk_tree_selection_get_selected (selection, NULL, &iter))
606 gtk_tree_walk_reset (wt->priv->walk, &iter);
607 else
608 gtk_tree_walk_reset (wt->priv->walk, NULL);
609 object = gtk_inspector_object_tree_get_selected (wt);
610 g_signal_emit (wt, signals[OBJECT_SELECTED], 0, object);
611 }
612
613 typedef struct {
614 GObject *dead_object;
615 GtkTreeWalk *walk;
616 GtkTreePath *walk_pos;
617 } RemoveData;
618
619 static gboolean
remove_cb(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)620 remove_cb (GtkTreeModel *model,
621 GtkTreePath *path,
622 GtkTreeIter *iter,
623 gpointer data)
624 {
625 RemoveData *remove_data = data;
626 GObject *lookup;
627
628 gtk_tree_model_get (model, iter, OBJECT, &lookup, -1);
629
630 if (lookup == remove_data->dead_object)
631 {
632 if (remove_data->walk_pos != NULL &&
633 gtk_tree_path_compare (path, remove_data->walk_pos) == 0)
634 gtk_tree_walk_reset (remove_data->walk, NULL);
635
636 gtk_tree_store_remove (GTK_TREE_STORE (model), iter);
637
638 return TRUE;
639 }
640
641 return FALSE;
642 }
643
644 static void
gtk_object_tree_remove_dead_object(gpointer data,GObject * dead_object)645 gtk_object_tree_remove_dead_object (gpointer data, GObject *dead_object)
646 {
647 GtkInspectorObjectTree *wt = data;
648 GtkTreeIter iter;
649 RemoveData remove_data;
650
651 remove_data.dead_object = dead_object;
652 remove_data.walk = wt->priv->walk;
653 if (gtk_tree_walk_get_position (wt->priv->walk, &iter))
654 remove_data.walk_pos = gtk_tree_model_get_path (GTK_TREE_MODEL (wt->priv->model), &iter);
655 else
656 remove_data.walk_pos = NULL;
657
658 gtk_tree_model_foreach (GTK_TREE_MODEL (wt->priv->model), remove_cb, &remove_data);
659
660 if (remove_data.walk_pos)
661 gtk_tree_path_free (remove_data.walk_pos);
662 }
663
664 static gboolean
weak_unref_cb(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)665 weak_unref_cb (GtkTreeModel *model,
666 GtkTreePath *path,
667 GtkTreeIter *iter,
668 gpointer data)
669 {
670 GtkInspectorObjectTree *wt = data;
671 GObject *object;
672
673 gtk_tree_model_get (model, iter, OBJECT, &object, -1);
674
675 g_object_weak_unref (object, gtk_object_tree_remove_dead_object, wt);
676
677 return FALSE;
678 }
679
680 static void
clear_store(GtkInspectorObjectTree * wt)681 clear_store (GtkInspectorObjectTree *wt)
682 {
683 if (wt->priv->model)
684 {
685 gtk_tree_model_foreach (GTK_TREE_MODEL (wt->priv->model), weak_unref_cb, wt);
686 gtk_tree_store_clear (wt->priv->model);
687 gtk_tree_walk_reset (wt->priv->walk, NULL);
688 }
689 }
690
691 static gboolean
map_or_unmap(GSignalInvocationHint * ihint,guint n_params,const GValue * params,gpointer data)692 map_or_unmap (GSignalInvocationHint *ihint,
693 guint n_params,
694 const GValue *params,
695 gpointer data)
696 {
697 GtkInspectorObjectTree *wt = data;
698 GtkWidget *widget;
699 GtkTreeIter iter;
700
701 widget = g_value_get_object (params);
702 if (gtk_inspector_object_tree_find_object (wt, G_OBJECT (widget), &iter))
703 gtk_tree_store_set (wt->priv->model, &iter,
704 SENSITIVE, gtk_widget_get_mapped (widget),
705 -1);
706 return TRUE;
707 }
708
709 static void
move_search_to_row(GtkInspectorObjectTree * wt,GtkTreeIter * iter)710 move_search_to_row (GtkInspectorObjectTree *wt,
711 GtkTreeIter *iter)
712 {
713 GtkTreeSelection *selection;
714 GtkTreePath *path;
715
716 selection = gtk_tree_view_get_selection (wt->priv->tree);
717 path = gtk_tree_model_get_path (GTK_TREE_MODEL (wt->priv->model), iter);
718 gtk_tree_view_expand_to_path (wt->priv->tree, path);
719 gtk_tree_selection_select_path (selection, path);
720 gtk_tree_view_scroll_to_cell (wt->priv->tree, path, NULL, TRUE, 0.5, 0.0);
721 gtk_tree_path_free (path);
722 }
723
724 static gboolean
key_press_event(GtkWidget * window,GdkEvent * event,GtkInspectorObjectTree * wt)725 key_press_event (GtkWidget *window,
726 GdkEvent *event,
727 GtkInspectorObjectTree *wt)
728 {
729 if (gtk_widget_get_mapped (GTK_WIDGET (wt)))
730 {
731 GdkModifierType default_accel;
732 gboolean search_started;
733
734 search_started = gtk_search_bar_get_search_mode (GTK_SEARCH_BAR (wt->priv->search_bar));
735 default_accel = gtk_widget_get_modifier_mask (GTK_WIDGET (wt), GDK_MODIFIER_INTENT_PRIMARY_ACCELERATOR);
736
737 if (search_started &&
738 (event->key.keyval == GDK_KEY_Return ||
739 event->key.keyval == GDK_KEY_ISO_Enter ||
740 event->key.keyval == GDK_KEY_KP_Enter))
741 {
742 GtkTreeSelection *selection;
743 GtkTreeModel *model;
744 GtkTreeIter iter;
745 GtkTreePath *path;
746
747 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (wt->priv->tree));
748 if (gtk_tree_selection_get_selected (selection, &model, &iter))
749 {
750 path = gtk_tree_model_get_path (model, &iter);
751 gtk_tree_view_row_activated (GTK_TREE_VIEW (wt->priv->tree),
752 path,
753 wt->priv->object_column);
754 gtk_tree_path_free (path);
755
756 return GDK_EVENT_STOP;
757 }
758 else
759 return GDK_EVENT_PROPAGATE;
760 }
761 else if (search_started &&
762 (event->key.keyval == GDK_KEY_Escape))
763 {
764 gtk_search_bar_set_search_mode (GTK_SEARCH_BAR (wt->priv->search_bar), FALSE);
765 return GDK_EVENT_STOP;
766 }
767 else if (search_started &&
768 ((event->key.state & (default_accel | GDK_SHIFT_MASK)) == (default_accel | GDK_SHIFT_MASK)) &&
769 (event->key.keyval == GDK_KEY_g || event->key.keyval == GDK_KEY_G))
770 {
771 GtkTreeIter iter;
772 if (gtk_tree_walk_next_match (wt->priv->walk, TRUE, TRUE, &iter))
773 move_search_to_row (wt, &iter);
774 else
775 gtk_widget_error_bell (GTK_WIDGET (wt));
776
777 return GDK_EVENT_STOP;
778 }
779 else if (search_started &&
780 ((event->key.state & (default_accel | GDK_SHIFT_MASK)) == default_accel) &&
781 (event->key.keyval == GDK_KEY_g || event->key.keyval == GDK_KEY_G))
782 {
783 GtkTreeIter iter;
784
785 if (gtk_tree_walk_next_match (wt->priv->walk, TRUE, FALSE, &iter))
786 move_search_to_row (wt, &iter);
787 else
788 gtk_widget_error_bell (GTK_WIDGET (wt));
789
790 return GDK_EVENT_STOP;
791 }
792
793 return gtk_search_bar_handle_event (GTK_SEARCH_BAR (wt->priv->search_bar), event);
794 }
795 else
796 return GDK_EVENT_PROPAGATE;
797 }
798
799 static void
on_hierarchy_changed(GtkWidget * widget,GtkWidget * previous_toplevel)800 on_hierarchy_changed (GtkWidget *widget,
801 GtkWidget *previous_toplevel)
802 {
803 if (previous_toplevel)
804 g_signal_handlers_disconnect_by_func (previous_toplevel, key_press_event, widget);
805 g_signal_connect (gtk_widget_get_toplevel (widget), "key-press-event",
806 G_CALLBACK (key_press_event), widget);
807 }
808
809 static void
on_search_changed(GtkSearchEntry * entry,GtkInspectorObjectTree * wt)810 on_search_changed (GtkSearchEntry *entry,
811 GtkInspectorObjectTree *wt)
812 {
813 GtkTreeIter iter;
814 gint length;
815 gboolean backwards;
816
817 length = strlen (gtk_entry_get_text (GTK_ENTRY (entry)));
818 backwards = length < wt->priv->search_length;
819 wt->priv->search_length = length;
820
821 if (length == 0)
822 return;
823
824 if (gtk_tree_walk_next_match (wt->priv->walk, backwards, backwards, &iter))
825 move_search_to_row (wt, &iter);
826 else if (!backwards)
827 gtk_widget_error_bell (GTK_WIDGET (wt));
828 }
829
830 static gboolean
match_string(const gchar * string,const gchar * text)831 match_string (const gchar *string,
832 const gchar *text)
833 {
834 gchar *lower;
835 gboolean match = FALSE;
836
837 if (string)
838 {
839 lower = g_ascii_strdown (string, -1);
840 match = g_str_has_prefix (lower, text);
841 g_free (lower);
842 }
843
844 return match;
845 }
846
847 static gboolean
match_row(GtkTreeModel * model,GtkTreeIter * iter,gpointer data)848 match_row (GtkTreeModel *model,
849 GtkTreeIter *iter,
850 gpointer data)
851 {
852 GtkInspectorObjectTree *wt = data;
853 gchar *type, *name, *label;
854 const gchar *text;
855 gboolean match;
856
857 text = gtk_entry_get_text (GTK_ENTRY (wt->priv->search_entry));
858 gtk_tree_model_get (model, iter,
859 OBJECT_TYPE, &type,
860 OBJECT_NAME, &name,
861 OBJECT_LABEL, &label,
862 -1);
863
864 match = (match_string (type, text) ||
865 match_string (name, text) ||
866 match_string (label, text));
867
868 g_free (type);
869 g_free (name);
870 g_free (label);
871
872 return match;
873 }
874
875 static void
search_mode_changed(GObject * search_bar,GParamSpec * pspec,GtkInspectorObjectTree * wt)876 search_mode_changed (GObject *search_bar,
877 GParamSpec *pspec,
878 GtkInspectorObjectTree *wt)
879 {
880 if (!gtk_search_bar_get_search_mode (GTK_SEARCH_BAR (search_bar)))
881 {
882 gtk_tree_walk_reset (wt->priv->walk, NULL);
883 wt->priv->search_length = 0;
884 }
885 }
886
887 static void
next_match(GtkButton * button,GtkInspectorObjectTree * wt)888 next_match (GtkButton *button,
889 GtkInspectorObjectTree *wt)
890 {
891 if (gtk_search_bar_get_search_mode (GTK_SEARCH_BAR (wt->priv->search_bar)))
892 {
893 GtkTreeIter iter;
894
895 if (gtk_tree_walk_next_match (wt->priv->walk, TRUE, FALSE, &iter))
896 move_search_to_row (wt, &iter);
897 else
898 gtk_widget_error_bell (GTK_WIDGET (wt));
899 }
900 }
901
902 static void
previous_match(GtkButton * button,GtkInspectorObjectTree * wt)903 previous_match (GtkButton *button,
904 GtkInspectorObjectTree *wt)
905 {
906 if (gtk_search_bar_get_search_mode (GTK_SEARCH_BAR (wt->priv->search_bar)))
907 {
908 GtkTreeIter iter;
909
910 if (gtk_tree_walk_next_match (wt->priv->walk, TRUE, TRUE, &iter))
911 move_search_to_row (wt, &iter);
912 else
913 gtk_widget_error_bell (GTK_WIDGET (wt));
914 }
915 }
916
917 static void
stop_search(GtkWidget * entry,GtkInspectorObjectTree * wt)918 stop_search (GtkWidget *entry,
919 GtkInspectorObjectTree *wt)
920 {
921 gtk_entry_set_text (GTK_ENTRY (wt->priv->search_entry), "");
922 gtk_search_bar_set_search_mode (GTK_SEARCH_BAR (wt->priv->search_bar), FALSE);
923 }
924
925 static void
gtk_inspector_object_tree_init(GtkInspectorObjectTree * wt)926 gtk_inspector_object_tree_init (GtkInspectorObjectTree *wt)
927 {
928 guint signal_id;
929
930 wt->priv = gtk_inspector_object_tree_get_instance_private (wt);
931 gtk_widget_init_template (GTK_WIDGET (wt));
932
933 gtk_search_bar_connect_entry (GTK_SEARCH_BAR (wt->priv->search_bar),
934 GTK_ENTRY (wt->priv->search_entry));
935
936 g_signal_connect (wt->priv->search_bar, "notify::search-mode-enabled",
937 G_CALLBACK (search_mode_changed), wt);
938 wt->priv->walk = gtk_tree_walk_new (GTK_TREE_MODEL (wt->priv->model), match_row, wt, NULL);
939
940 signal_id = g_signal_lookup ("map", GTK_TYPE_WIDGET);
941 wt->priv->map_hook = g_signal_add_emission_hook (signal_id, 0,
942 map_or_unmap, wt, NULL);
943 signal_id = g_signal_lookup ("unmap", GTK_TYPE_WIDGET);
944 wt->priv->unmap_hook = g_signal_add_emission_hook (signal_id, 0,
945 map_or_unmap, wt, NULL);
946
947 gtk_inspector_object_tree_append_object (wt, G_OBJECT (gtk_settings_get_default ()), NULL, NULL);
948 }
949
950 static void
gtk_inspector_object_tree_dispose(GObject * object)951 gtk_inspector_object_tree_dispose (GObject *object)
952 {
953 GtkInspectorObjectTree *wt = GTK_INSPECTOR_OBJECT_TREE (object);
954
955 clear_store (wt);
956
957 G_OBJECT_CLASS (gtk_inspector_object_tree_parent_class)->dispose (object);
958 }
959
960 static void
gtk_inspector_object_tree_finalize(GObject * object)961 gtk_inspector_object_tree_finalize (GObject *object)
962 {
963 GtkInspectorObjectTree *wt = GTK_INSPECTOR_OBJECT_TREE (object);
964 guint signal_id;
965
966 signal_id = g_signal_lookup ("map", GTK_TYPE_WIDGET);
967 g_signal_remove_emission_hook (signal_id, wt->priv->map_hook);
968 signal_id = g_signal_lookup ("unmap", GTK_TYPE_WIDGET);
969 g_signal_remove_emission_hook (signal_id, wt->priv->unmap_hook);
970
971 gtk_tree_walk_free (wt->priv->walk);
972
973 G_OBJECT_CLASS (gtk_inspector_object_tree_parent_class)->finalize (object);
974 }
975
976 static void
gtk_inspector_object_tree_class_init(GtkInspectorObjectTreeClass * klass)977 gtk_inspector_object_tree_class_init (GtkInspectorObjectTreeClass *klass)
978 {
979 GObjectClass *object_class = G_OBJECT_CLASS (klass);
980 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
981
982 object_class->finalize = gtk_inspector_object_tree_finalize;
983 object_class->dispose = gtk_inspector_object_tree_dispose;
984
985 signals[OBJECT_ACTIVATED] =
986 g_signal_new ("object-activated",
987 G_OBJECT_CLASS_TYPE (klass),
988 G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE,
989 G_STRUCT_OFFSET (GtkInspectorObjectTreeClass, object_activated),
990 NULL, NULL,
991 NULL,
992 G_TYPE_NONE, 2, G_TYPE_OBJECT, G_TYPE_STRING);
993
994 signals[OBJECT_SELECTED] =
995 g_signal_new ("object-selected",
996 G_OBJECT_CLASS_TYPE (klass),
997 G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE,
998 G_STRUCT_OFFSET (GtkInspectorObjectTreeClass, object_selected),
999 NULL, NULL,
1000 NULL,
1001 G_TYPE_NONE, 1, G_TYPE_OBJECT);
1002
1003 gtk_widget_class_set_template_from_resource (widget_class, "/org/gtk/libgtk/inspector/object-tree.ui");
1004 gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorObjectTree, model);
1005 gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorObjectTree, tree);
1006 gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorObjectTree, object_column);
1007 gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorObjectTree, search_bar);
1008 gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorObjectTree, search_entry);
1009 gtk_widget_class_bind_template_callback (widget_class, on_selection_changed);
1010 gtk_widget_class_bind_template_callback (widget_class, on_row_activated);
1011 gtk_widget_class_bind_template_callback (widget_class, on_hierarchy_changed);
1012 gtk_widget_class_bind_template_callback (widget_class, on_search_changed);
1013 gtk_widget_class_bind_template_callback (widget_class, next_match);
1014 gtk_widget_class_bind_template_callback (widget_class, previous_match);
1015 gtk_widget_class_bind_template_callback (widget_class, stop_search);
1016 }
1017
1018 typedef struct
1019 {
1020 GtkInspectorObjectTree *wt;
1021 GtkTreeIter *iter;
1022 GObject *parent;
1023 } FindAllData;
1024
1025 static void
child_callback(GObject * object,const char * name,gpointer data)1026 child_callback (GObject *object,
1027 const char *name,
1028 gpointer data)
1029 {
1030 FindAllData *d = data;
1031
1032 gtk_inspector_object_tree_append_object (d->wt, object, d->iter, NULL);
1033 }
1034
1035 void
gtk_inspector_object_tree_append_object(GtkInspectorObjectTree * wt,GObject * object,GtkTreeIter * parent_iter,const gchar * name)1036 gtk_inspector_object_tree_append_object (GtkInspectorObjectTree *wt,
1037 GObject *object,
1038 GtkTreeIter *parent_iter,
1039 const gchar *name)
1040 {
1041 GtkTreeIter iter;
1042 const gchar *class_name;
1043 gchar *classes;
1044 const gchar *label;
1045 FindAllData data;
1046
1047 class_name = G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (object));
1048
1049 if (GTK_IS_WIDGET (object))
1050 {
1051 const gchar *id;
1052 GtkStyleContext *context;
1053 GList *list, *l;
1054 GString *string;
1055
1056 id = gtk_widget_get_name (GTK_WIDGET (object));
1057 if (name == NULL && id != NULL && g_strcmp0 (id, class_name) != 0)
1058 name = id;
1059
1060 context = gtk_widget_get_style_context (GTK_WIDGET (object));
1061 string = g_string_new ("");
1062 list = gtk_style_context_list_classes (context);
1063 for (l = list; l; l = l->next)
1064 {
1065 if (string->len > 0)
1066 g_string_append_c (string, ' ');
1067 g_string_append (string, (gchar *)l->data);
1068 }
1069 classes = g_string_free (string, FALSE);
1070 g_list_free (list);
1071 }
1072 else
1073 {
1074 if (parent_iter)
1075 {
1076 GObject *parent;
1077
1078 gtk_tree_model_get (GTK_TREE_MODEL (wt->priv->model), parent_iter,
1079 OBJECT, &parent,
1080 -1);
1081 g_object_set_data (object, "inspector-object-tree-parent", parent);
1082 }
1083 classes = g_strdup ("");
1084 }
1085
1086 if (GTK_IS_BUILDABLE (object))
1087 {
1088 const gchar *id;
1089 id = gtk_buildable_get_name (GTK_BUILDABLE (object));
1090 if (name == NULL && id != NULL && !g_str_has_prefix (id, "___object_"))
1091 name = id;
1092 }
1093
1094 if (name == NULL)
1095 name = "";
1096
1097 if (GTK_IS_LABEL (object))
1098 label = gtk_label_get_text (GTK_LABEL (object));
1099 else if (GTK_IS_BUTTON (object))
1100 label = gtk_button_get_label (GTK_BUTTON (object));
1101 else if (GTK_IS_WINDOW (object))
1102 label = gtk_window_get_title (GTK_WINDOW (object));
1103 else if (GTK_IS_TREE_VIEW_COLUMN (object))
1104 label = gtk_tree_view_column_get_title (GTK_TREE_VIEW_COLUMN (object));
1105 else
1106 label = "";
1107
1108 gtk_tree_store_append (wt->priv->model, &iter, parent_iter);
1109 gtk_tree_store_set (wt->priv->model, &iter,
1110 OBJECT, object,
1111 OBJECT_TYPE, class_name,
1112 OBJECT_NAME, name,
1113 OBJECT_LABEL, label,
1114 OBJECT_CLASSES, classes,
1115 SENSITIVE, object_get_sensitive (object),
1116 -1);
1117
1118 if (name && *name)
1119 {
1120 gchar *title;
1121 title = g_strconcat (class_name, " — ", name, NULL);
1122 g_object_set_data_full (object, "gtk-inspector-object-title", title, g_free);
1123 }
1124 else
1125 {
1126 g_object_set_data (object, "gtk-inspector-object-title", (gpointer)class_name);
1127 }
1128
1129 g_free (classes);
1130
1131 g_object_weak_ref (object, gtk_object_tree_remove_dead_object, wt);
1132
1133 data.wt = wt;
1134 data.iter = &iter;
1135 data.parent = object;
1136
1137 object_forall (object, child_callback, &data);
1138 }
1139
1140 static void
block_selection_changed(GtkInspectorObjectTree * wt)1141 block_selection_changed (GtkInspectorObjectTree *wt)
1142 {
1143 GtkTreeSelection *selection;
1144
1145 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (wt->priv->tree));
1146 g_signal_handlers_block_by_func (selection, on_selection_changed, wt);
1147 }
1148
1149 static void
unblock_selection_changed(GtkInspectorObjectTree * wt)1150 unblock_selection_changed (GtkInspectorObjectTree *wt)
1151 {
1152 GtkTreeSelection *selection;
1153
1154 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (wt->priv->tree));
1155 g_signal_handlers_unblock_by_func (selection, on_selection_changed, wt);
1156 }
1157
1158 gboolean
select_object_internal(GtkInspectorObjectTree * wt,GObject * object,gboolean activate)1159 select_object_internal (GtkInspectorObjectTree *wt,
1160 GObject *object,
1161 gboolean activate)
1162 {
1163 GtkTreeIter iter;
1164
1165 if (gtk_inspector_object_tree_find_object (wt, object, &iter))
1166 {
1167 GtkTreePath *path;
1168 GtkTreeSelection *selection;
1169
1170 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (wt->priv->tree));
1171 path = gtk_tree_model_get_path (GTK_TREE_MODEL (wt->priv->model), &iter);
1172 gtk_tree_view_expand_to_path (GTK_TREE_VIEW (wt->priv->tree), path);
1173 if (!activate)
1174 block_selection_changed (wt);
1175 gtk_tree_selection_select_iter (selection, &iter);
1176 if (!activate)
1177 unblock_selection_changed (wt);
1178
1179 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (wt->priv->tree), path, NULL, TRUE, 0.5, 0);
1180 if (activate)
1181 gtk_tree_view_row_activated (GTK_TREE_VIEW (wt->priv->tree), path, NULL);
1182 gtk_tree_path_free (path);
1183
1184 return TRUE;
1185 }
1186
1187 return FALSE;
1188 }
1189
1190 gboolean
gtk_inspector_object_tree_select_object(GtkInspectorObjectTree * wt,GObject * object)1191 gtk_inspector_object_tree_select_object (GtkInspectorObjectTree *wt,
1192 GObject *object)
1193 {
1194 return select_object_internal (wt, object, TRUE);
1195 }
1196
1197 void
gtk_inspector_object_tree_scan(GtkInspectorObjectTree * wt,GtkWidget * window)1198 gtk_inspector_object_tree_scan (GtkInspectorObjectTree *wt,
1199 GtkWidget *window)
1200 {
1201 GtkWidget *inspector_win;
1202 GList *toplevels, *l;
1203 GdkScreen *screen;
1204 GObject *selected;
1205
1206 block_selection_changed (wt);
1207
1208 selected = gtk_inspector_object_tree_get_selected (wt);
1209
1210 clear_store (wt);
1211 gtk_inspector_object_tree_append_object (wt, G_OBJECT (gtk_settings_get_default ()), NULL, NULL);
1212 if (g_application_get_default ())
1213 gtk_inspector_object_tree_append_object (wt, G_OBJECT (g_application_get_default ()), NULL, NULL);
1214
1215 if (window)
1216 gtk_inspector_object_tree_append_object (wt, G_OBJECT (window), NULL, NULL);
1217
1218 screen = gdk_screen_get_default ();
1219
1220 inspector_win = gtk_widget_get_toplevel (GTK_WIDGET (wt));
1221 toplevels = gtk_window_list_toplevels ();
1222 for (l = toplevels; l; l = l->next)
1223 {
1224 if (GTK_IS_WINDOW (l->data) &&
1225 gtk_window_get_window_type (l->data) == GTK_WINDOW_TOPLEVEL &&
1226 gtk_widget_get_screen (l->data) == screen &&
1227 l->data != window &&
1228 l->data != inspector_win)
1229 gtk_inspector_object_tree_append_object (wt, G_OBJECT (l->data), NULL, NULL);
1230 }
1231 g_list_free (toplevels);
1232
1233 gtk_tree_view_columns_autosize (GTK_TREE_VIEW (wt->priv->tree));
1234
1235 if (selected)
1236 select_object_internal (wt, selected, FALSE);
1237
1238 unblock_selection_changed (wt);
1239 }
1240
1241 static gboolean
gtk_inspector_object_tree_find_object_at_parent_iter(GtkTreeModel * model,GObject * object,GtkTreeIter * parent,GtkTreeIter * iter)1242 gtk_inspector_object_tree_find_object_at_parent_iter (GtkTreeModel *model,
1243 GObject *object,
1244 GtkTreeIter *parent,
1245 GtkTreeIter *iter)
1246 {
1247 if (!gtk_tree_model_iter_children (model, iter, parent))
1248 return FALSE;
1249
1250 do {
1251 GObject *lookup;
1252
1253 gtk_tree_model_get (model, iter, OBJECT, &lookup, -1);
1254
1255 if (lookup == object)
1256 return TRUE;
1257
1258 } while (gtk_tree_model_iter_next (model, iter));
1259
1260 return FALSE;
1261 }
1262
1263 gboolean
gtk_inspector_object_tree_find_object(GtkInspectorObjectTree * wt,GObject * object,GtkTreeIter * iter)1264 gtk_inspector_object_tree_find_object (GtkInspectorObjectTree *wt,
1265 GObject *object,
1266 GtkTreeIter *iter)
1267 {
1268 GtkTreeIter parent_iter;
1269 GObject *parent;
1270
1271 parent = object_get_parent (object);
1272 if (parent)
1273 {
1274 if (!gtk_inspector_object_tree_find_object (wt, parent, &parent_iter))
1275 return FALSE;
1276
1277 return gtk_inspector_object_tree_find_object_at_parent_iter (GTK_TREE_MODEL (wt->priv->model),
1278 object,
1279 &parent_iter,
1280 iter);
1281 }
1282 else
1283 {
1284 return gtk_inspector_object_tree_find_object_at_parent_iter (GTK_TREE_MODEL (wt->priv->model),
1285 object,
1286 NULL,
1287 iter);
1288 }
1289 }
1290
1291
1292 // vim: set et sw=2 ts=2:
1293