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