1 /*
2  * Copyright (c) 2014 Red Hat, Inc.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "config.h"
19 #include <glib/gi18n-lib.h>
20 
21 #include "resource-list.h"
22 
23 #include "gtklabel.h"
24 #include "gtkstack.h"
25 #include "gtktextbuffer.h"
26 #include "gtktreestore.h"
27 #include "gtktreeselection.h"
28 #include "gtksearchbar.h"
29 #include "gtksearchentry.h"
30 #include "treewalk.h"
31 
32 enum
33 {
34   PROP_0,
35   PROP_BUTTONS
36 };
37 
38 enum
39 {
40   COLUMN_NAME,
41   COLUMN_PATH,
42   COLUMN_COUNT,
43   COLUMN_SIZE
44 };
45 
46 struct _GtkInspectorResourceListPrivate
47 {
48   GtkTreeStore *model;
49   GtkTextBuffer *buffer;
50   GtkWidget *image;
51   GtkWidget *content;
52   GtkWidget *name_label;
53   GtkWidget *type;
54   GtkWidget *type_label;
55   GtkWidget *size_label;
56   GtkWidget *info_grid;
57   GtkWidget *stack;
58   GtkWidget *tree;
59   GtkWidget *buttons;
60   GtkWidget *open_details_button;
61   GtkWidget *close_details_button;
62   GtkTreeViewColumn *path_column;
63   GtkTreeViewColumn *count_column;
64   GtkCellRenderer *count_renderer;
65   GtkTreeViewColumn *size_column;
66   GtkCellRenderer *size_renderer;
67   GtkWidget *search_bar;
68   GtkWidget *search_entry;
69   GtkTreeWalk *walk;
70   gint search_length;
71 };
72 
73 
G_DEFINE_TYPE_WITH_PRIVATE(GtkInspectorResourceList,gtk_inspector_resource_list,GTK_TYPE_BOX)74 G_DEFINE_TYPE_WITH_PRIVATE (GtkInspectorResourceList, gtk_inspector_resource_list, GTK_TYPE_BOX)
75 
76 static void
77 load_resources_recurse (GtkInspectorResourceList *sl,
78                         GtkTreeIter              *parent,
79                         const gchar              *path,
80                         gint                     *count_out,
81                         gsize                    *size_out)
82 {
83   gchar **names;
84   gint i;
85   GtkTreeIter iter;
86   guint64 stored_size;
87 
88   names = g_resources_enumerate_children (path, 0, NULL);
89   for (i = 0; names[i]; i++)
90     {
91       gint len;
92       gchar *p;
93       gboolean has_slash;
94       gint count;
95       gsize size;
96 
97       p = g_strconcat (path, names[i], NULL);
98 
99       len = strlen (names[i]);
100       has_slash = names[i][len - 1] == '/';
101 
102       if (has_slash)
103         names[i][len - 1] = '\0';
104 
105       gtk_tree_store_append (sl->priv->model, &iter, parent);
106       gtk_tree_store_set (sl->priv->model, &iter,
107                           COLUMN_NAME, names[i],
108                           COLUMN_PATH, p,
109                           -1);
110 
111       count = 0;
112       size = 0;
113 
114       if (has_slash)
115         {
116           load_resources_recurse (sl, &iter, p, &count, &size);
117           *count_out += count;
118           *size_out += size;
119         }
120       else
121         {
122           count = 0;
123           if (g_resources_get_info (p, 0, &size, NULL, NULL))
124             {
125               *count_out += 1;
126               *size_out += size;
127             }
128         }
129 
130       stored_size = size;
131       gtk_tree_store_set (sl->priv->model, &iter,
132                           COLUMN_COUNT, count,
133                           COLUMN_SIZE, stored_size,
134                           -1);
135 
136       g_free (p);
137     }
138   g_strfreev (names);
139 
140 }
141 
142 static gboolean
populate_details(GtkInspectorResourceList * rl,GtkTreePath * tree_path)143 populate_details (GtkInspectorResourceList *rl,
144                   GtkTreePath              *tree_path)
145 {
146   GtkTreeIter iter;
147   gchar *path;
148   gchar *name;
149   GBytes *bytes;
150   gchar *type;
151   gconstpointer data;
152   gint count;
153   gsize size;
154   guint64 stored_size;
155   GError *error = NULL;
156   gchar *markup;
157 
158 
159   gtk_tree_model_get_iter (GTK_TREE_MODEL (rl->priv->model), &iter, tree_path);
160 
161   gtk_tree_model_get (GTK_TREE_MODEL (rl->priv->model), &iter,
162                       COLUMN_PATH, &path,
163                       COLUMN_NAME, &name,
164                       COLUMN_COUNT, &count,
165                       COLUMN_SIZE, &stored_size,
166                       -1);
167   size = stored_size;
168 
169    if (g_str_has_suffix (path, "/"))
170      {
171        g_free (path);
172        g_free (name);
173        return FALSE;
174      }
175 
176   markup = g_strconcat ("<span face='Monospace' size='small'>", path, "</span>", NULL);
177   gtk_label_set_markup (GTK_LABEL (rl->priv->name_label), markup);
178   g_free (markup);
179 
180   bytes = g_resources_lookup_data (path, 0, &error);
181   if (bytes == NULL)
182     {
183       gtk_text_buffer_set_text (rl->priv->buffer, error->message, -1);
184       g_error_free (error);
185       gtk_stack_set_visible_child_name (GTK_STACK (rl->priv->content), "text");
186     }
187   else
188     {
189       gchar *text;
190       gchar *content_image;
191       gchar *content_text;
192 
193       content_image = g_content_type_from_mime_type ("image/*");
194       content_text = g_content_type_from_mime_type ("text/*");
195 
196       data = g_bytes_get_data (bytes, &size);
197       type = g_content_type_guess (name, data, size, NULL);
198 
199       text = g_content_type_get_description (type);
200       gtk_label_set_text (GTK_LABEL (rl->priv->type_label), text);
201       g_free (text);
202 
203       text = g_format_size (size);
204       gtk_label_set_text (GTK_LABEL (rl->priv->size_label), text);
205       g_free (text);
206 
207       if (g_content_type_is_a (type, content_text))
208         {
209           gtk_text_buffer_set_text (rl->priv->buffer, data, -1);
210           gtk_stack_set_visible_child_name (GTK_STACK (rl->priv->content), "text");
211         }
212       else if (g_content_type_is_a (type, content_image))
213         {
214           gtk_image_set_from_resource (GTK_IMAGE (rl->priv->image), path);
215           gtk_stack_set_visible_child_name (GTK_STACK (rl->priv->content), "image");
216         }
217       else
218         {
219           gtk_text_buffer_set_text (rl->priv->buffer, "", 0);
220           gtk_stack_set_visible_child_name (GTK_STACK (rl->priv->content), "text");
221         }
222 
223       g_free (type);
224       g_bytes_unref (bytes);
225 
226       g_free (content_image);
227       g_free (content_text);
228     }
229 
230   g_free (path);
231   g_free (name);
232 
233   return TRUE;
234 }
235 
236 static void
row_activated(GtkTreeView * treeview,GtkTreePath * path,GtkTreeViewColumn * column,GtkInspectorResourceList * sl)237 row_activated (GtkTreeView              *treeview,
238                GtkTreePath              *path,
239                GtkTreeViewColumn        *column,
240                GtkInspectorResourceList *sl)
241 {
242   if (!populate_details (sl, path))
243     return;
244 
245   gtk_stack_set_visible_child_name (GTK_STACK (sl->priv->stack), "details");
246   gtk_stack_set_visible_child_name (GTK_STACK (sl->priv->buttons), "details");
247 }
248 
249 static gboolean
can_show_details(GtkInspectorResourceList * rl)250 can_show_details (GtkInspectorResourceList *rl)
251 {
252   GtkTreeSelection *selection;
253   GtkTreeModel *model;
254   GtkTreeIter iter;
255   gchar *path;
256 
257   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (rl->priv->tree));
258   if (!gtk_tree_selection_get_selected (selection, &model, &iter))
259     return FALSE;
260 
261   gtk_tree_model_get (GTK_TREE_MODEL (rl->priv->model), &iter,
262                       COLUMN_PATH, &path,
263                       -1);
264 
265    if (g_str_has_suffix (path, "/"))
266      {
267        g_free (path);
268        return FALSE;
269      }
270 
271   g_free (path);
272   return TRUE;
273 }
274 
275 static void
on_selection_changed(GtkTreeSelection * selection,GtkInspectorResourceList * rl)276 on_selection_changed (GtkTreeSelection         *selection,
277                       GtkInspectorResourceList *rl)
278 {
279   GtkTreeIter iter;
280 
281   if (gtk_tree_selection_get_selected (selection, NULL, &iter))
282     gtk_tree_walk_reset (rl->priv->walk, &iter);
283   else
284     gtk_tree_walk_reset (rl->priv->walk, NULL);
285 
286   gtk_widget_set_sensitive (rl->priv->open_details_button, can_show_details (rl));
287 }
288 
289 static void
open_details(GtkWidget * button,GtkInspectorResourceList * sl)290 open_details (GtkWidget                *button,
291               GtkInspectorResourceList *sl)
292 {
293   GtkTreeSelection *selection;
294   GtkTreeModel *model;
295   GtkTreeIter iter;
296   GtkTreePath *path;
297 
298   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (sl->priv->tree));
299   if (!gtk_tree_selection_get_selected (selection, &model, &iter))
300     return;
301 
302   path = gtk_tree_model_get_path (model, &iter);
303   if (populate_details (sl, path))
304     {
305       gtk_stack_set_visible_child_name (GTK_STACK (sl->priv->stack), "details");
306       gtk_stack_set_visible_child_name (GTK_STACK (sl->priv->buttons), "details");
307     }
308 
309   gtk_tree_path_free (path);
310 }
311 
312 static void
close_details(GtkWidget * button,GtkInspectorResourceList * sl)313 close_details (GtkWidget                *button,
314                GtkInspectorResourceList *sl)
315 {
316   gtk_stack_set_visible_child_name (GTK_STACK (sl->priv->stack), "list");
317   gtk_stack_set_visible_child_name (GTK_STACK (sl->priv->buttons), "list");
318 }
319 
320 static void
load_resources(GtkInspectorResourceList * sl)321 load_resources (GtkInspectorResourceList *sl)
322 {
323   gint count = 0;
324   gsize size = 0;
325 
326   load_resources_recurse (sl, NULL, "/", &count, &size);
327 }
328 
329 static void
count_data_func(GtkTreeViewColumn * col,GtkCellRenderer * cell,GtkTreeModel * model,GtkTreeIter * iter,gpointer data)330 count_data_func (GtkTreeViewColumn *col,
331                  GtkCellRenderer   *cell,
332                  GtkTreeModel      *model,
333                  GtkTreeIter       *iter,
334                  gpointer           data)
335 {
336   gint count;
337   gchar *text;
338 
339   gtk_tree_model_get (model, iter, COLUMN_COUNT, &count, -1);
340   if (count > 0)
341     {
342       text = g_strdup_printf ("%d", count);
343       g_object_set (cell, "text", text, NULL);
344       g_free (text);
345     }
346   else
347     g_object_set (cell, "text", "", NULL);
348 }
349 
350 static void
size_data_func(GtkTreeViewColumn * col,GtkCellRenderer * cell,GtkTreeModel * model,GtkTreeIter * iter,gpointer data)351 size_data_func (GtkTreeViewColumn *col,
352                 GtkCellRenderer   *cell,
353                 GtkTreeModel      *model,
354                 GtkTreeIter       *iter,
355                 gpointer           data)
356 {
357   gsize size;
358   guint64 stored_size;
359   gchar *text;
360 
361   gtk_tree_model_get (model, iter, COLUMN_SIZE, &stored_size, -1);
362   size = stored_size;
363   text = g_format_size (size);
364   g_object_set (cell, "text", text, NULL);
365   g_free (text);
366 }
367 
368 static void
on_map(GtkWidget * widget)369 on_map (GtkWidget *widget)
370 {
371   GtkInspectorResourceList *sl = GTK_INSPECTOR_RESOURCE_LIST (widget);
372 
373   gtk_tree_view_expand_all (GTK_TREE_VIEW (sl->priv->tree));
374   gtk_stack_set_visible_child_name (GTK_STACK (sl->priv->stack), "list");
375   gtk_widget_set_sensitive (sl->priv->open_details_button, can_show_details (sl));
376 }
377 
378 static void
move_search_to_row(GtkInspectorResourceList * sl,GtkTreeIter * iter)379 move_search_to_row (GtkInspectorResourceList *sl,
380                     GtkTreeIter              *iter)
381 {
382   GtkTreeSelection *selection;
383   GtkTreePath *path;
384 
385   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (sl->priv->tree));
386   path = gtk_tree_model_get_path (GTK_TREE_MODEL (sl->priv->model), iter);
387   gtk_tree_view_expand_to_path (GTK_TREE_VIEW (sl->priv->tree), path);
388   gtk_tree_selection_select_path (selection, path);
389   gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (sl->priv->tree), path, NULL, TRUE, 0.5, 0.0);
390   gtk_tree_path_free (path);
391 }
392 
393 static gboolean
key_press_event(GtkWidget * window,GdkEvent * event,GtkInspectorResourceList * sl)394 key_press_event (GtkWidget                *window,
395                  GdkEvent                 *event,
396                  GtkInspectorResourceList *sl)
397 {
398   if (gtk_widget_get_mapped (GTK_WIDGET (sl)))
399     {
400       GdkModifierType default_accel;
401       gboolean search_started;
402 
403       search_started = gtk_search_bar_get_search_mode (GTK_SEARCH_BAR (sl->priv->search_bar));
404       default_accel = gtk_widget_get_modifier_mask (GTK_WIDGET (sl), GDK_MODIFIER_INTENT_PRIMARY_ACCELERATOR);
405 
406       if (search_started &&
407           (event->key.keyval == GDK_KEY_Return ||
408            event->key.keyval == GDK_KEY_ISO_Enter ||
409            event->key.keyval == GDK_KEY_KP_Enter))
410         {
411           GtkTreeSelection *selection;
412           GtkTreeModel *model;
413           GtkTreeIter iter;
414           GtkTreePath *path;
415 
416           selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (sl->priv->tree));
417           if (gtk_tree_selection_get_selected (selection, &model, &iter))
418             {
419               path = gtk_tree_model_get_path (model, &iter);
420               gtk_tree_view_row_activated (GTK_TREE_VIEW (sl->priv->tree),
421                                            path,
422                                            sl->priv->path_column);
423               gtk_tree_path_free (path);
424 
425               return GDK_EVENT_STOP;
426             }
427           else
428             return GDK_EVENT_PROPAGATE;
429         }
430       else if (search_started &&
431                (event->key.keyval == GDK_KEY_Escape))
432         {
433           gtk_search_bar_set_search_mode (GTK_SEARCH_BAR (sl->priv->search_bar), FALSE);
434           return GDK_EVENT_STOP;
435         }
436       else if (search_started &&
437                ((event->key.state & (default_accel | GDK_SHIFT_MASK)) == (default_accel | GDK_SHIFT_MASK)) &&
438                (event->key.keyval == GDK_KEY_g || event->key.keyval == GDK_KEY_G))
439         {
440           GtkTreeIter iter;
441           if (gtk_tree_walk_next_match (sl->priv->walk, TRUE, TRUE, &iter))
442             move_search_to_row (sl, &iter);
443           else
444             gtk_widget_error_bell (GTK_WIDGET (sl));
445 
446           return GDK_EVENT_STOP;
447         }
448       else if (search_started &&
449                ((event->key.state & (default_accel | GDK_SHIFT_MASK)) == default_accel) &&
450                (event->key.keyval == GDK_KEY_g || event->key.keyval == GDK_KEY_G))
451         {
452           GtkTreeIter iter;
453 
454           if (gtk_tree_walk_next_match (sl->priv->walk, TRUE, FALSE, &iter))
455             move_search_to_row (sl, &iter);
456           else
457             gtk_widget_error_bell (GTK_WIDGET (sl));
458 
459           return GDK_EVENT_STOP;
460         }
461 
462       return gtk_search_bar_handle_event (GTK_SEARCH_BAR (sl->priv->search_bar), event);
463     }
464   else
465     return GDK_EVENT_PROPAGATE;
466 }
467 
468 static void
on_hierarchy_changed(GtkWidget * widget,GtkWidget * previous_toplevel)469 on_hierarchy_changed (GtkWidget *widget,
470                       GtkWidget *previous_toplevel)
471 {
472   if (previous_toplevel)
473     g_signal_handlers_disconnect_by_func (previous_toplevel, key_press_event, widget);
474   g_signal_connect (gtk_widget_get_toplevel (widget), "key-press-event",
475                     G_CALLBACK (key_press_event), widget);
476 }
477 
478 static void
on_search_changed(GtkSearchEntry * entry,GtkInspectorResourceList * sl)479 on_search_changed (GtkSearchEntry           *entry,
480                    GtkInspectorResourceList *sl)
481 {
482   GtkTreeIter iter;
483   gint length;
484   gboolean backwards;
485 
486   length = strlen (gtk_entry_get_text (GTK_ENTRY (entry)));
487   backwards = length < sl->priv->search_length;
488   sl->priv->search_length = length;
489 
490   if (length == 0)
491     return;
492 
493   if (gtk_tree_walk_next_match (sl->priv->walk, backwards, backwards, &iter))
494     move_search_to_row (sl, &iter);
495   else if (!backwards)
496     gtk_widget_error_bell (GTK_WIDGET (sl));
497 }
498 
499 static gboolean
match_string(const gchar * string,const gchar * text)500 match_string (const gchar *string,
501               const gchar *text)
502 {
503   gchar *lower;
504   gboolean match = FALSE;
505 
506   if (string)
507     {
508       lower = g_ascii_strdown (string, -1);
509       match = g_str_has_prefix (lower, text);
510       g_free (lower);
511     }
512 
513   return match;
514 }
515 
516 static gboolean
match_row(GtkTreeModel * model,GtkTreeIter * iter,gpointer data)517 match_row (GtkTreeModel *model,
518            GtkTreeIter  *iter,
519            gpointer      data)
520 {
521   GtkInspectorResourceList *sl = data;
522   gchar *name, *path;
523   const gchar *text;
524   gboolean match;
525 
526   text = gtk_entry_get_text (GTK_ENTRY (sl->priv->search_entry));
527   gtk_tree_model_get (model, iter,
528                       COLUMN_NAME, &name,
529                       COLUMN_PATH, &path,
530                       -1);
531 
532   match = (match_string (name, text) ||
533            match_string (path, text));
534 
535   g_free (name);
536   g_free (path);
537 
538   return match;
539 }
540 
541 static void
search_mode_changed(GObject * search_bar,GParamSpec * pspec,GtkInspectorResourceList * sl)542 search_mode_changed (GObject                  *search_bar,
543                      GParamSpec               *pspec,
544                      GtkInspectorResourceList *sl)
545 {
546   if (!gtk_search_bar_get_search_mode (GTK_SEARCH_BAR (search_bar)))
547     {
548       gtk_tree_walk_reset (sl->priv->walk, NULL);
549       sl->priv->search_length = 0;
550     }
551 }
552 
553 static void
next_match(GtkButton * button,GtkInspectorResourceList * sl)554 next_match (GtkButton                *button,
555             GtkInspectorResourceList *sl)
556 {
557   if (gtk_search_bar_get_search_mode (GTK_SEARCH_BAR (sl->priv->search_bar)))
558     {
559       GtkTreeIter iter;
560 
561       if (gtk_tree_walk_next_match (sl->priv->walk, TRUE, FALSE, &iter))
562         move_search_to_row (sl, &iter);
563       else
564         gtk_widget_error_bell (GTK_WIDGET (sl));
565     }
566 }
567 
568 static void
previous_match(GtkButton * button,GtkInspectorResourceList * sl)569 previous_match (GtkButton                *button,
570                 GtkInspectorResourceList *sl)
571 {
572   if (gtk_search_bar_get_search_mode (GTK_SEARCH_BAR (sl->priv->search_bar)))
573     {
574       GtkTreeIter iter;
575 
576       if (gtk_tree_walk_next_match (sl->priv->walk, TRUE, TRUE, &iter))
577         move_search_to_row (sl, &iter);
578       else
579         gtk_widget_error_bell (GTK_WIDGET (sl));
580     }
581 }
582 
583 static void
gtk_inspector_resource_list_init(GtkInspectorResourceList * sl)584 gtk_inspector_resource_list_init (GtkInspectorResourceList *sl)
585 {
586   sl->priv = gtk_inspector_resource_list_get_instance_private (sl);
587 
588   gtk_widget_init_template (GTK_WIDGET (sl));
589 
590   gtk_tree_view_column_set_cell_data_func (sl->priv->count_column,
591                                            sl->priv->count_renderer,
592                                            count_data_func, sl, NULL);
593   gtk_tree_view_column_set_cell_data_func (sl->priv->size_column,
594                                            sl->priv->size_renderer,
595                                            size_data_func, sl, NULL);
596 
597   g_signal_connect (sl, "map", G_CALLBACK (on_map), NULL);
598 
599   gtk_search_bar_connect_entry (GTK_SEARCH_BAR (sl->priv->search_bar),
600                                 GTK_ENTRY (sl->priv->search_entry));
601 
602   g_signal_connect (sl->priv->search_bar, "notify::search-mode-enabled",
603                     G_CALLBACK (search_mode_changed), sl);
604   sl->priv->walk = gtk_tree_walk_new (GTK_TREE_MODEL (sl->priv->model), match_row, sl, NULL);
605 }
606 
607 static void
constructed(GObject * object)608 constructed (GObject *object)
609 {
610   GtkInspectorResourceList *rl = GTK_INSPECTOR_RESOURCE_LIST (object);
611 
612   g_signal_connect (rl->priv->open_details_button, "clicked",
613                     G_CALLBACK (open_details), rl);
614   g_signal_connect (rl->priv->close_details_button, "clicked",
615                     G_CALLBACK (close_details), rl);
616 
617   load_resources (rl);
618 }
619 
620 static void
get_property(GObject * object,guint param_id,GValue * value,GParamSpec * pspec)621 get_property (GObject    *object,
622               guint       param_id,
623               GValue     *value,
624               GParamSpec *pspec)
625 {
626   GtkInspectorResourceList *rl = GTK_INSPECTOR_RESOURCE_LIST (object);
627 
628   switch (param_id)
629     {
630     case PROP_BUTTONS:
631       g_value_take_object (value, rl->priv->buttons);
632       break;
633 
634     default:
635       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
636       break;
637     }
638 }
639 
640 static void
set_property(GObject * object,guint param_id,const GValue * value,GParamSpec * pspec)641 set_property (GObject      *object,
642               guint         param_id,
643               const GValue *value,
644               GParamSpec   *pspec)
645 {
646   GtkInspectorResourceList *rl = GTK_INSPECTOR_RESOURCE_LIST (object);
647 
648   switch (param_id)
649     {
650     case PROP_BUTTONS:
651       rl->priv->buttons = g_value_get_object (value);
652       rl->priv->open_details_button = gtk_stack_get_child_by_name (GTK_STACK (rl->priv->buttons), "list");
653       rl->priv->close_details_button = gtk_stack_get_child_by_name (GTK_STACK (rl->priv->buttons), "details");
654       break;
655 
656     default:
657       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
658       break;
659     }
660 }
661 
662 static void
finalize(GObject * object)663 finalize (GObject *object)
664 {
665   GtkInspectorResourceList *sl = GTK_INSPECTOR_RESOURCE_LIST (object);
666 
667   gtk_tree_walk_free (sl->priv->walk);
668 
669   G_OBJECT_CLASS (gtk_inspector_resource_list_parent_class)->finalize (object);
670 }
671 
672 static void
gtk_inspector_resource_list_class_init(GtkInspectorResourceListClass * klass)673 gtk_inspector_resource_list_class_init (GtkInspectorResourceListClass *klass)
674 {
675   GObjectClass *object_class = G_OBJECT_CLASS (klass);
676   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
677 
678   object_class->get_property = get_property;
679   object_class->set_property = set_property;
680   object_class->constructed = constructed;
681   object_class->finalize = finalize;
682 
683   g_object_class_install_property (object_class, PROP_BUTTONS,
684       g_param_spec_object ("buttons", NULL, NULL,
685                            GTK_TYPE_WIDGET, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
686 
687   gtk_widget_class_set_template_from_resource (widget_class, "/org/gtk/libgtk/inspector/resource-list.ui");
688   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorResourceList, model);
689   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorResourceList, buffer);
690   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorResourceList, content);
691   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorResourceList, image);
692   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorResourceList, name_label);
693   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorResourceList, type_label);
694   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorResourceList, type);
695   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorResourceList, size_label);
696   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorResourceList, info_grid);
697   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorResourceList, count_column);
698   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorResourceList, count_renderer);
699   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorResourceList, size_column);
700   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorResourceList, size_renderer);
701   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorResourceList, stack);
702   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorResourceList, tree);
703   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorResourceList, search_bar);
704   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorResourceList, search_entry);
705   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorResourceList, path_column);
706 
707   gtk_widget_class_bind_template_callback (widget_class, row_activated);
708   gtk_widget_class_bind_template_callback (widget_class, on_selection_changed);
709   gtk_widget_class_bind_template_callback (widget_class, on_hierarchy_changed);
710   gtk_widget_class_bind_template_callback (widget_class, on_search_changed);
711   gtk_widget_class_bind_template_callback (widget_class, next_match);
712   gtk_widget_class_bind_template_callback (widget_class, previous_match);
713 }
714 
715 // vim: set et sw=2 ts=2:
716