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
20#include "resource-list.h"
21
22#include "gtkbutton.h"
23#include "gtklabel.h"
24#include "gtksearchbar.h"
25#include "gtksearchentry.h"
26#include "gtkstack.h"
27#include "gtktextbuffer.h"
28#include "gtktreeselection.h"
29#include "gtktreestore.h"
30#include "gtkeventcontrollerkey.h"
31#include "gtkpicture.h"
32#include "gtkmediafile.h"
33#include "gtkbinlayout.h"
34#include "resource-holder.h"
35
36#include <glib/gi18n-lib.h>
37
38enum
39{
40 PROP_0,
41 PROP_BUTTONS
42};
43
44struct _GtkInspectorResourceList
45{
46 GtkWidget parent_instance;
47
48 GtkTextBuffer *buffer;
49 GtkWidget *video;
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 *buttons;
59 GtkWidget *open_details_button;
60 GtkWidget *close_details_button;
61 GtkWidget *search_bar;
62 GtkWidget *search_entry;
63
64 GtkWidget *list;
65 GtkColumnViewColumn *path;
66 GtkColumnViewColumn *count;
67 GtkColumnViewColumn *size;
68
69 GtkTreeListModel *tree_model;
70 GtkSingleSelection *selection;
71};
72
73typedef struct _GtkInspectorResourceListClass
74{
75 GtkWidgetClass parent;
76} GtkInspectorResourceListClass;
77
78
79G_DEFINE_TYPE (GtkInspectorResourceList, gtk_inspector_resource_list, GTK_TYPE_WIDGET)
80
81static GListModel *
82load_resources_recurse (const char *path,
83 int *count_out,
84 gsize *size_out)
85{
86 char **names;
87 int i;
88 GListStore *result;
89
90 result = g_list_store_new (RESOURCE_TYPE_HOLDER);
91
92 names = g_resources_enumerate_children (path, lookup_flags: 0, NULL);
93 for (i = 0; names[i]; i++)
94 {
95 int len;
96 char *p;
97 gboolean has_slash;
98 int count;
99 gsize size;
100 GListModel *children;
101 ResourceHolder *holder;
102
103 p = g_strconcat (string1: path, names[i], NULL);
104
105 len = strlen (s: names[i]);
106 has_slash = names[i][len - 1] == '/';
107
108 if (has_slash)
109 names[i][len - 1] = '\0';
110
111 count = 0;
112 size = 0;
113
114 if (has_slash)
115 {
116 children = load_resources_recurse (path: p, count_out: &count, size_out: &size);
117
118 *count_out += count;
119 *size_out += size;
120 }
121 else
122 {
123 count = 0;
124 if (g_resources_get_info (path: p, lookup_flags: 0, size: &size, NULL, NULL))
125 {
126 *count_out += 1;
127 *size_out += size;
128 }
129 children = NULL;
130 }
131
132 holder = resource_holder_new (name: names[i], path: p, count, size, children);
133 g_clear_object (&children);
134 g_list_store_append (store: result, item: holder);
135 g_object_unref (object: holder);
136
137 g_free (mem: p);
138 }
139
140 g_strfreev (str_array: names);
141
142 return G_LIST_MODEL (ptr: result);
143}
144
145static gboolean
146populate_details (GtkInspectorResourceList *rl,
147 ResourceHolder *holder)
148{
149 const char *path;
150 const char *name;
151 GBytes *bytes;
152 char *type;
153 gconstpointer data;
154 gsize size;
155 GError *error = NULL;
156 char *markup;
157
158 path = resource_holder_get_path (holder);
159 name = resource_holder_get_name (holder);
160 size = resource_holder_get_size (holder);
161
162 if (g_str_has_suffix (str: path, suffix: "/"))
163 return FALSE;
164
165 markup = g_strconcat (string1: "<span face='Monospace' size='small'>", path, "</span>", NULL);
166 gtk_label_set_markup (GTK_LABEL (rl->name_label), str: markup);
167 g_free (mem: markup);
168
169 bytes = g_resources_lookup_data (path, lookup_flags: 0, error: &error);
170 if (bytes == NULL)
171 {
172 gtk_text_buffer_set_text (buffer: rl->buffer, text: error->message, len: -1);
173 g_error_free (error);
174 gtk_stack_set_visible_child_name (GTK_STACK (rl->content), name: "text");
175 }
176 else
177 {
178 char *text;
179 char *content_image;
180 char *content_text;
181 char *content_video;
182
183 content_image = g_content_type_from_mime_type (mime_type: "image/*");
184 content_text = g_content_type_from_mime_type (mime_type: "text/*");
185 content_video = g_content_type_from_mime_type (mime_type: "video/*");
186
187 data = g_bytes_get_data (bytes, size: &size);
188 type = g_content_type_guess (filename: name, data, data_size: size, NULL);
189
190 text = g_content_type_get_description (type);
191 gtk_label_set_text (GTK_LABEL (rl->type_label), str: text);
192 g_free (mem: text);
193
194 text = g_format_size (size);
195 gtk_label_set_text (GTK_LABEL (rl->size_label), str: text);
196 g_free (mem: text);
197
198 if (g_content_type_is_a (type, supertype: content_text))
199 {
200 gtk_text_buffer_set_text (buffer: rl->buffer, text: data, len: -1);
201 gtk_stack_set_visible_child_name (GTK_STACK (rl->content), name: "text");
202 }
203 else if (g_content_type_is_a (type, supertype: content_image))
204 {
205 gtk_picture_set_resource (self: GTK_PICTURE (ptr: rl->image), resource_path: path);
206 gtk_stack_set_visible_child_name (GTK_STACK (rl->content), name: "image");
207 }
208 else if (g_content_type_is_a (type, supertype: content_video))
209 {
210 GtkMediaStream *stream;
211
212 stream = gtk_media_file_new_for_resource (resource_path: path);
213 gtk_media_stream_set_loop (self: GTK_MEDIA_STREAM (ptr: stream), TRUE);
214 gtk_picture_set_paintable (self: GTK_PICTURE (ptr: rl->image), paintable: GDK_PAINTABLE (ptr: stream));
215 gtk_stack_set_visible_child_name (GTK_STACK (rl->content), name: "image");
216 gtk_media_stream_play (self: GTK_MEDIA_STREAM (ptr: stream));
217 g_object_unref (object: stream);
218 }
219 else
220 {
221 gtk_text_buffer_set_text (buffer: rl->buffer, text: "", len: 0);
222 gtk_stack_set_visible_child_name (GTK_STACK (rl->content), name: "text");
223 }
224
225 g_free (mem: type);
226 g_bytes_unref (bytes);
227
228 g_free (mem: content_image);
229 g_free (mem: content_text);
230 }
231
232 return TRUE;
233}
234
235static void
236on_row_activated (GtkColumnView *view,
237 guint position,
238 GtkInspectorResourceList *rl)
239{
240 gpointer item;
241 ResourceHolder *holder;
242
243 item = g_list_model_get_item (list: G_LIST_MODEL (ptr: rl->selection), position);
244 holder = gtk_tree_list_row_get_item (self: item);
245 g_object_unref (object: item);
246 if (populate_details (rl, holder))
247 {
248 gtk_stack_set_visible_child_name (GTK_STACK (rl->stack), name: "details");
249 gtk_stack_set_visible_child_name (GTK_STACK (rl->buttons), name: "details");
250 }
251 g_object_unref (object: holder);
252}
253
254static gboolean
255can_show_details (GtkInspectorResourceList *rl)
256{
257 gpointer item;
258 ResourceHolder *holder;
259 const char *path;
260
261 item = gtk_single_selection_get_selected_item (self: rl->selection);
262 holder = gtk_tree_list_row_get_item (self: item);
263 if (holder == NULL)
264 return FALSE;
265 path = resource_holder_get_path (holder);
266 g_object_unref (object: holder);
267 return !g_str_has_suffix (str: path, suffix: "/");
268}
269
270static void
271on_selection_changed (GtkSelectionModel *selection,
272 guint position,
273 guint n_items,
274 GtkInspectorResourceList *rl)
275{
276 gtk_widget_set_sensitive (widget: rl->open_details_button, sensitive: can_show_details (rl));
277}
278
279static void
280open_details (GtkWidget *button,
281 GtkInspectorResourceList *rl)
282{
283 gpointer item;
284 ResourceHolder *holder;
285
286 item = gtk_single_selection_get_selected_item (self: rl->selection);
287 holder = gtk_tree_list_row_get_item (self: item);
288 if (holder == NULL)
289 return;
290 if (populate_details (rl, holder))
291 {
292 gtk_stack_set_visible_child_name (GTK_STACK (rl->stack), name: "details");
293 gtk_stack_set_visible_child_name (GTK_STACK (rl->buttons), name: "details");
294 }
295 g_object_unref (object: holder);
296}
297
298static void
299close_details (GtkWidget *button,
300 GtkInspectorResourceList *rl)
301{
302 gtk_stack_set_visible_child_name (GTK_STACK (rl->stack), name: "list");
303 gtk_stack_set_visible_child_name (GTK_STACK (rl->buttons), name: "list");
304}
305
306static GListModel *
307load_resources (void)
308{
309 int count = 0;
310 gsize size = 0;
311
312 return load_resources_recurse (path: "/", count_out: &count, size_out: &size);
313}
314
315static void
316on_map (GtkWidget *widget)
317{
318 GtkInspectorResourceList *rl = GTK_INSPECTOR_RESOURCE_LIST (widget);
319
320 gtk_stack_set_visible_child_name (GTK_STACK (rl->stack), name: "list");
321 gtk_widget_set_sensitive (widget: rl->open_details_button, sensitive: can_show_details (rl));
322}
323
324static gboolean search (GtkInspectorResourceList *rl,
325 gboolean forward,
326 gboolean force_progress);
327
328static gboolean
329key_pressed (GtkEventController *controller,
330 guint keyval,
331 guint keycode,
332 GdkModifierType state,
333 GtkInspectorResourceList *rl)
334{
335 if (gtk_widget_get_mapped (GTK_WIDGET (rl)))
336 {
337 GdkModifierType default_accel;
338 gboolean search_started;
339
340 search_started = gtk_search_bar_get_search_mode (GTK_SEARCH_BAR (rl->search_bar));
341 default_accel = GDK_CONTROL_MASK;
342
343 if (search_started &&
344 (keyval == GDK_KEY_Return ||
345 keyval == GDK_KEY_ISO_Enter ||
346 keyval == GDK_KEY_KP_Enter))
347 {
348 gtk_widget_activate (GTK_WIDGET (rl->list));
349 return GDK_EVENT_PROPAGATE;
350 }
351 else if (search_started &&
352 (keyval == GDK_KEY_Escape))
353 {
354 gtk_search_bar_set_search_mode (GTK_SEARCH_BAR (rl->search_bar), FALSE);
355 return GDK_EVENT_STOP;
356 }
357 else if (search_started &&
358 ((state & (default_accel | GDK_SHIFT_MASK)) == (default_accel | GDK_SHIFT_MASK)) &&
359 (keyval == GDK_KEY_g || keyval == GDK_KEY_G))
360 {
361 if (!search (rl, FALSE, TRUE))
362 gtk_widget_error_bell (GTK_WIDGET (rl));
363 return GDK_EVENT_STOP;
364 }
365 else if (search_started &&
366 ((state & (default_accel | GDK_SHIFT_MASK)) == default_accel) &&
367 (keyval == GDK_KEY_g || keyval == GDK_KEY_G))
368 {
369 if (!search (rl, TRUE, TRUE))
370 gtk_widget_error_bell (GTK_WIDGET (rl));
371 return GDK_EVENT_STOP;
372 }
373 }
374
375 return GDK_EVENT_PROPAGATE;
376}
377
378static void
379destroy_controller (GtkEventController *controller)
380{
381 gtk_widget_remove_controller (widget: gtk_event_controller_get_widget (controller), controller);
382}
383
384static void
385root (GtkWidget *widget)
386{
387 GtkInspectorResourceList *rl = GTK_INSPECTOR_RESOURCE_LIST (widget);
388 GtkEventController *controller;
389 GtkWidget *toplevel;
390
391 GTK_WIDGET_CLASS (gtk_inspector_resource_list_parent_class)->root (widget);
392
393 toplevel = GTK_WIDGET (gtk_widget_get_root (widget));
394
395 controller = gtk_event_controller_key_new ();
396 g_object_set_data_full (G_OBJECT (toplevel), key: "resource-controller", data: controller, destroy: (GDestroyNotify)destroy_controller);
397 g_signal_connect (controller, "key-pressed", G_CALLBACK (key_pressed), widget);
398 gtk_widget_add_controller (widget: toplevel, controller);
399
400 gtk_search_bar_set_key_capture_widget (GTK_SEARCH_BAR (rl->search_bar), widget: toplevel);
401}
402
403static void
404unroot (GtkWidget *widget)
405{
406 GtkWidget *toplevel;
407
408 toplevel = GTK_WIDGET (gtk_widget_get_root (widget));
409 g_object_set_data (G_OBJECT (toplevel), key: "resource-controller", NULL);
410
411 GTK_WIDGET_CLASS (gtk_inspector_resource_list_parent_class)->unroot (widget);
412}
413
414static gboolean
415match_string (const char *string,
416 const char *text)
417{
418 char *lower;
419 gboolean match = FALSE;
420
421 if (string)
422 {
423 lower = g_ascii_strdown (str: string, len: -1);
424 match = g_str_has_prefix (str: lower, prefix: text);
425 g_free (mem: lower);
426 }
427
428 return match;
429}
430
431static gboolean
432match_object (GObject *object,
433 const char *text)
434{
435 const char *name = resource_holder_get_name (holder: RESOURCE_HOLDER (ptr: object));
436
437 if (match_string (string: name, text))
438 return TRUE;
439
440 return FALSE;
441}
442
443static GObject *
444search_children (GObject *object,
445 const char *text,
446 gboolean forward)
447{
448 GListModel *children;
449 GObject *child, *result;
450 guint i, n;
451
452 children = resource_holder_get_children (holder: RESOURCE_HOLDER (ptr: object));
453 if (children == NULL)
454 return NULL;
455
456 n = g_list_model_get_n_items (list: children);
457 for (i = 0; i < n; i++)
458 {
459 child = g_list_model_get_item (list: children, position: forward ? i : n - i - 1);
460 if (match_object (object: child, text))
461 return child;
462
463 result = search_children (object: child, text, forward);
464 g_object_unref (object: child);
465 if (result)
466 return result;
467 }
468
469 return NULL;
470}
471
472static guint
473model_get_item_index (GListModel *model,
474 gpointer item)
475{
476 gpointer cmp;
477 guint i;
478
479 for (i = 0; (cmp = g_list_model_get_item (list: model, position: i)); i++)
480 {
481 if (cmp == item)
482 {
483 g_object_unref (object: cmp);
484 return i;
485 }
486 g_object_unref (object: cmp);
487 }
488
489 return G_MAXUINT;
490}
491
492static GtkTreeListRow *
493find_and_expand_object (GtkTreeListModel *model,
494 GObject *object)
495{
496 GtkTreeListRow *result;
497 GObject *parent;
498 guint pos;
499
500 parent = G_OBJECT (resource_holder_get_parent (RESOURCE_HOLDER (object)));
501 if (parent)
502 {
503 GtkTreeListRow *parent_row = find_and_expand_object (model, object: parent);
504 if (parent_row == NULL)
505 return NULL;
506
507 gtk_tree_list_row_set_expanded (self: parent_row, TRUE);
508 pos = model_get_item_index (model: gtk_tree_list_row_get_children (self: parent_row), item: object);
509 result = gtk_tree_list_row_get_child_row (self: parent_row, position: pos);
510 g_object_unref (object: parent_row);
511 }
512 else
513 {
514 pos = model_get_item_index (model: gtk_tree_list_model_get_model (self: model), item: object);
515 result = gtk_tree_list_model_get_child_row (self: model, position: pos);
516 }
517
518 return result;
519}
520
521static void
522select_object (GtkInspectorResourceList *rl,
523 GObject *object)
524{
525 GtkTreeListRow *row_item;
526
527 row_item = find_and_expand_object (model: rl->tree_model, object);
528 if (row_item == NULL)
529 return;
530
531 gtk_single_selection_set_selected (self: rl->selection,
532 position: gtk_tree_list_row_get_position (self: row_item));
533}
534
535static gboolean
536search (GtkInspectorResourceList *rl,
537 gboolean forward,
538 gboolean force_progress)
539{
540 GListModel *model = G_LIST_MODEL (ptr: rl->tree_model);
541 GtkTreeListRow *row_item;
542 GObject *child, *result;
543 guint i, selected, n, row;
544 const char *text;
545
546 text = gtk_editable_get_text (GTK_EDITABLE (rl->search_entry));
547 selected = gtk_single_selection_get_selected (self: rl->selection);
548 n = g_list_model_get_n_items (list: model);
549 if (selected >= n)
550 selected = 0;
551
552 for (i = 0; i < n; i++)
553 {
554 row = (selected + (forward ? i : n - i - 1)) % n;
555 row_item = g_list_model_get_item (list: model, position: row);
556 child = gtk_tree_list_row_get_item (self: row_item);
557 if (i > 0 || !force_progress)
558 {
559 if (match_object (object: child, text))
560 {
561 gtk_single_selection_set_selected (self: rl->selection, position: row);
562 g_object_unref (object: child);
563 g_object_unref (object: row_item);
564 return TRUE;
565 }
566 }
567
568 if (!gtk_tree_list_row_get_expanded (self: row_item))
569 {
570 result = search_children (object: child, text, forward);
571 if (result)
572 {
573 select_object (rl, object: result);
574 g_object_unref (object: result);
575 g_object_unref (object: child);
576 g_object_unref (object: row_item);
577 return TRUE;
578 }
579 }
580 g_object_unref (object: child);
581 g_object_unref (object: row_item);
582 }
583
584 return FALSE;
585}
586
587static void
588on_search_changed (GtkSearchEntry *entry,
589 GtkInspectorResourceList *rl)
590{
591 if (!search (rl, TRUE, FALSE))
592 gtk_widget_error_bell (GTK_WIDGET (rl));
593}
594
595static void
596next_match (GtkButton *button,
597 GtkInspectorResourceList *rl)
598{
599 if (gtk_search_bar_get_search_mode (GTK_SEARCH_BAR (rl->search_bar)))
600 {
601 if (!search (rl, TRUE, TRUE))
602 gtk_widget_error_bell (GTK_WIDGET (rl));
603 }
604}
605
606static void
607previous_match (GtkButton *button,
608 GtkInspectorResourceList *rl)
609{
610 if (gtk_search_bar_get_search_mode (GTK_SEARCH_BAR (rl->search_bar)))
611 {
612 if (!search (rl, FALSE, TRUE))
613 gtk_widget_error_bell (GTK_WIDGET (rl));
614 }
615}
616
617static void
618stop_search (GtkWidget *entry,
619 GtkInspectorResourceList *rl)
620{
621 gtk_editable_set_text (GTK_EDITABLE (rl->search_entry), text: "");
622 gtk_search_bar_set_search_mode (GTK_SEARCH_BAR (rl->search_bar), FALSE);
623}
624
625static char *
626holder_name (gpointer item)
627{
628 return g_strdup (str: resource_holder_get_name (holder: RESOURCE_HOLDER (ptr: item)));
629}
630
631static int
632holder_count (gpointer item)
633{
634 return resource_holder_get_count (holder: RESOURCE_HOLDER (ptr: item));
635}
636
637static gsize
638holder_size (gpointer item)
639{
640 return resource_holder_get_size (holder: RESOURCE_HOLDER (ptr: item));
641}
642
643static void
644gtk_inspector_resource_list_init (GtkInspectorResourceList *rl)
645{
646 GtkSorter *sorter;
647
648 gtk_widget_init_template (GTK_WIDGET (rl));
649
650 g_signal_connect (rl, "map", G_CALLBACK (on_map), NULL);
651
652 gtk_search_bar_connect_entry (GTK_SEARCH_BAR (rl->search_bar),
653 GTK_EDITABLE (rl->search_entry));
654
655 sorter = GTK_SORTER (ptr: gtk_string_sorter_new (expression: gtk_cclosure_expression_new (G_TYPE_STRING, NULL,
656 n_params: 0, NULL,
657 callback_func: (GCallback)holder_name,
658 NULL, NULL)));
659
660 gtk_column_view_column_set_sorter (self: rl->path, sorter);
661 g_object_unref (object: sorter);
662
663 sorter = GTK_SORTER (ptr: gtk_numeric_sorter_new (expression: gtk_cclosure_expression_new (G_TYPE_INT, NULL,
664 n_params: 0, NULL,
665 callback_func: (GCallback)holder_count,
666 NULL, NULL)));
667
668 gtk_column_view_column_set_sorter (self: rl->count, sorter);
669 g_object_unref (object: sorter);
670
671 sorter = GTK_SORTER (ptr: gtk_numeric_sorter_new (expression: gtk_cclosure_expression_new (G_TYPE_UINT64, NULL,
672 n_params: 0, NULL,
673 callback_func: (GCallback)holder_size,
674 NULL, NULL)));
675
676 gtk_column_view_column_set_sorter (self: rl->size, sorter);
677 g_object_unref (object: sorter);
678}
679
680static GListModel *
681create_model_for_object (gpointer item, gpointer data)
682{
683 GListModel *model = resource_holder_get_children (holder: RESOURCE_HOLDER (ptr: item));
684
685 if (model)
686 return g_object_ref (model);
687
688 return NULL;
689}
690
691static void
692constructed (GObject *object)
693{
694 GtkInspectorResourceList *rl = GTK_INSPECTOR_RESOURCE_LIST (object);
695 GListModel *sort_model;
696 GtkSorter *column_sorter;
697 GtkSorter *sorter;
698
699 g_signal_connect (rl->open_details_button, "clicked",
700 G_CALLBACK (open_details), rl);
701 g_signal_connect (rl->close_details_button, "clicked",
702 G_CALLBACK (close_details), rl);
703
704 rl->tree_model = gtk_tree_list_model_new (root: load_resources (),
705 FALSE,
706 FALSE,
707 create_func: create_model_for_object,
708 NULL,
709 NULL);
710
711 column_sorter = gtk_column_view_get_sorter (GTK_COLUMN_VIEW (rl->list));
712 sorter = GTK_SORTER (ptr: gtk_tree_list_row_sorter_new (g_object_ref (column_sorter)));
713 sort_model = G_LIST_MODEL (ptr: gtk_sort_list_model_new (g_object_ref (G_LIST_MODEL (rl->tree_model)), sorter));
714 rl->selection = gtk_single_selection_new (model: sort_model);
715
716 gtk_column_view_set_model (GTK_COLUMN_VIEW (rl->list), model: GTK_SELECTION_MODEL (ptr: rl->selection));
717
718 g_signal_connect (rl->selection, "selection-changed", G_CALLBACK (on_selection_changed), rl);
719}
720
721static void
722get_property (GObject *object,
723 guint param_id,
724 GValue *value,
725 GParamSpec *pspec)
726{
727 GtkInspectorResourceList *rl = GTK_INSPECTOR_RESOURCE_LIST (object);
728
729 switch (param_id)
730 {
731 case PROP_BUTTONS:
732 g_value_take_object (value, v_object: rl->buttons);
733 break;
734
735 default:
736 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
737 break;
738 }
739}
740
741static void
742set_property (GObject *object,
743 guint param_id,
744 const GValue *value,
745 GParamSpec *pspec)
746{
747 GtkInspectorResourceList *rl = GTK_INSPECTOR_RESOURCE_LIST (object);
748
749 switch (param_id)
750 {
751 case PROP_BUTTONS:
752 rl->buttons = g_value_get_object (value);
753 rl->open_details_button = gtk_stack_get_child_by_name (GTK_STACK (rl->buttons), name: "list");
754 rl->close_details_button = gtk_stack_get_child_by_name (GTK_STACK (rl->buttons), name: "details");
755 break;
756
757 default:
758 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
759 break;
760 }
761}
762
763static void
764dispose (GObject *object)
765{
766 GtkInspectorResourceList *rl = GTK_INSPECTOR_RESOURCE_LIST (object);
767
768 g_clear_pointer (&rl->stack, gtk_widget_unparent);
769 g_clear_object (&rl->selection);
770 g_clear_object (&rl->tree_model);
771
772 G_OBJECT_CLASS (gtk_inspector_resource_list_parent_class)->dispose (object);
773}
774
775static void
776setup_name_cb (GtkSignalListItemFactory *factory,
777 GtkListItem *list_item)
778{
779 GtkWidget *expander;
780 GtkWidget *label;
781
782 expander = gtk_tree_expander_new ();
783 gtk_list_item_set_child (self: list_item, child: expander);
784
785 label = gtk_label_new (NULL);
786 gtk_widget_set_margin_start (widget: label, margin: 5);
787 gtk_widget_set_margin_end (widget: label, margin: 5);
788 gtk_label_set_xalign (GTK_LABEL (label), xalign: 0.0);
789 gtk_tree_expander_set_child (self: GTK_TREE_EXPANDER (ptr: expander), child: label);
790}
791
792static void
793bind_name_cb (GtkSignalListItemFactory *factory,
794 GtkListItem *list_item)
795{
796 GtkTreeListRow *list_row;
797 GtkWidget *expander;
798 GtkWidget *label;
799 gpointer item;
800
801 list_row = gtk_list_item_get_item (self: list_item);
802 expander = gtk_list_item_get_child (self: list_item);
803 gtk_tree_expander_set_list_row (self: GTK_TREE_EXPANDER (ptr: expander), list_row);
804 item = gtk_tree_list_row_get_item (self: list_row);
805 label = gtk_tree_expander_get_child (self: GTK_TREE_EXPANDER (ptr: expander));
806
807 gtk_label_set_label (GTK_LABEL (label), str: resource_holder_get_name (holder: RESOURCE_HOLDER (ptr: item)));
808 g_object_unref (object: item);
809}
810
811static void
812setup_size_cb (GtkSignalListItemFactory *factory,
813 GtkListItem *list_item)
814{
815 GtkWidget *label;
816
817 label = gtk_label_new (NULL);
818 gtk_widget_set_margin_start (widget: label, margin: 5);
819 gtk_widget_set_margin_end (widget: label, margin: 5);
820 gtk_label_set_xalign (GTK_LABEL (label), xalign: 1.0);
821 gtk_list_item_set_child (self: list_item, child: label);
822}
823
824static void
825bind_size_cb (GtkSignalListItemFactory *factory,
826 GtkListItem *list_item)
827{
828 GObject *item;
829 GtkWidget *label;
830 gsize size;
831 char *text;
832
833 item = gtk_tree_list_row_get_item (self: gtk_list_item_get_item (self: list_item));
834 label = gtk_list_item_get_child (self: list_item);
835
836 size = resource_holder_get_size (holder: RESOURCE_HOLDER (ptr: item));
837 text = g_format_size (size);
838 gtk_label_set_label (GTK_LABEL (label), str: text);
839 g_free (mem: text);
840
841 g_object_unref (object: item);
842}
843
844static void
845setup_count_cb (GtkSignalListItemFactory *factory,
846 GtkListItem *list_item)
847{
848 GtkWidget *label;
849
850 label = gtk_label_new (NULL);
851 gtk_widget_set_margin_start (widget: label, margin: 5);
852 gtk_widget_set_margin_end (widget: label, margin: 5);
853 gtk_label_set_xalign (GTK_LABEL (label), xalign: 1.0);
854 gtk_list_item_set_child (self: list_item, child: label);
855}
856
857static void
858bind_count_cb (GtkSignalListItemFactory *factory,
859 GtkListItem *list_item)
860{
861 GObject *item;
862 GtkWidget *label;
863 int count;
864 char *text;
865
866 item = gtk_tree_list_row_get_item (self: gtk_list_item_get_item (self: list_item));
867 label = gtk_list_item_get_child (self: list_item);
868
869 count = resource_holder_get_count (holder: RESOURCE_HOLDER (ptr: item));
870 if (count > 0)
871 {
872 text = g_strdup_printf (format: "%d", count);
873 gtk_label_set_label (GTK_LABEL (label), str: text);
874 g_free (mem: text);
875 }
876 else
877 gtk_label_set_label (GTK_LABEL (label), str: "");
878 g_object_unref (object: item);
879}
880
881static void
882gtk_inspector_resource_list_class_init (GtkInspectorResourceListClass *klass)
883{
884 GObjectClass *object_class = G_OBJECT_CLASS (klass);
885 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
886
887 object_class->get_property = get_property;
888 object_class->set_property = set_property;
889 object_class->constructed = constructed;
890 object_class->dispose = dispose;
891
892 widget_class->root = root;
893 widget_class->unroot = unroot;
894
895 g_object_class_install_property (oclass: object_class, property_id: PROP_BUTTONS,
896 pspec: g_param_spec_object (name: "buttons", NULL, NULL,
897 GTK_TYPE_WIDGET, flags: G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
898
899 gtk_widget_class_set_template_from_resource (widget_class, resource_name: "/org/gtk/libgtk/inspector/resource-list.ui");
900 gtk_widget_class_bind_template_child (widget_class, GtkInspectorResourceList, buffer);
901 gtk_widget_class_bind_template_child (widget_class, GtkInspectorResourceList, content);
902 gtk_widget_class_bind_template_child (widget_class, GtkInspectorResourceList, image);
903 gtk_widget_class_bind_template_child (widget_class, GtkInspectorResourceList, name_label);
904 gtk_widget_class_bind_template_child (widget_class, GtkInspectorResourceList, type_label);
905 gtk_widget_class_bind_template_child (widget_class, GtkInspectorResourceList, type);
906 gtk_widget_class_bind_template_child (widget_class, GtkInspectorResourceList, size_label);
907 gtk_widget_class_bind_template_child (widget_class, GtkInspectorResourceList, info_grid);
908 gtk_widget_class_bind_template_child (widget_class, GtkInspectorResourceList, stack);
909 gtk_widget_class_bind_template_child (widget_class, GtkInspectorResourceList, search_bar);
910 gtk_widget_class_bind_template_child (widget_class, GtkInspectorResourceList, search_entry);
911 gtk_widget_class_bind_template_child (widget_class, GtkInspectorResourceList, list);
912 gtk_widget_class_bind_template_child (widget_class, GtkInspectorResourceList, path);
913 gtk_widget_class_bind_template_child (widget_class, GtkInspectorResourceList, count);
914 gtk_widget_class_bind_template_child (widget_class, GtkInspectorResourceList, size);
915
916 gtk_widget_class_bind_template_callback (widget_class, on_search_changed);
917 gtk_widget_class_bind_template_callback (widget_class, on_row_activated);
918 gtk_widget_class_bind_template_callback (widget_class, next_match);
919 gtk_widget_class_bind_template_callback (widget_class, previous_match);
920 gtk_widget_class_bind_template_callback (widget_class, stop_search);
921 gtk_widget_class_bind_template_callback (widget_class, setup_name_cb);
922 gtk_widget_class_bind_template_callback (widget_class, bind_name_cb);
923 gtk_widget_class_bind_template_callback (widget_class, setup_count_cb);
924 gtk_widget_class_bind_template_callback (widget_class, bind_count_cb);
925 gtk_widget_class_bind_template_callback (widget_class, setup_size_cb);
926 gtk_widget_class_bind_template_callback (widget_class, bind_size_cb);
927
928 gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
929}
930
931// vim: set et sw=2 ts=2:
932

source code of gtk/gtk/inspector/resource-list.c