/* * Copyright (c) 2014 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "config.h" #include #include "resource-list.h" #include "gtklabel.h" #include "gtkstack.h" #include "gtktextbuffer.h" #include "gtktreestore.h" #include "gtktreeselection.h" enum { PROP_0, PROP_BUTTONS }; enum { COLUMN_NAME, COLUMN_PATH, COLUMN_COUNT, COLUMN_SIZE }; struct _GtkInspectorResourceListPrivate { GtkTreeStore *model; GtkTextBuffer *buffer; GtkWidget *image; GtkWidget *content; GtkWidget *name_label; GtkWidget *type; GtkWidget *type_label; GtkWidget *size_label; GtkWidget *info_grid; GtkWidget *stack; GtkWidget *tree; GtkWidget *buttons; GtkWidget *open_details_button; GtkWidget *close_details_button; GtkTreeViewColumn *count_column; GtkCellRenderer *count_renderer; GtkTreeViewColumn *size_column; GtkCellRenderer *size_renderer; }; G_DEFINE_TYPE_WITH_PRIVATE (GtkInspectorResourceList, gtk_inspector_resource_list, GTK_TYPE_BOX) static void load_resources_recurse (GtkInspectorResourceList *sl, GtkTreeIter *parent, const gchar *path, gint *count_out, gsize *size_out) { gchar **names; gint i; GtkTreeIter iter; names = g_resources_enumerate_children (path, 0, NULL); for (i = 0; names[i]; i++) { gint len; gchar *p; gboolean has_slash; gint count; gsize size; p = g_strconcat (path, names[i], NULL); len = strlen (names[i]); has_slash = names[i][len - 1] == '/'; if (has_slash) names[i][len - 1] = '\0'; gtk_tree_store_append (sl->priv->model, &iter, parent); gtk_tree_store_set (sl->priv->model, &iter, COLUMN_NAME, names[i], COLUMN_PATH, p, -1); count = 0; size = 0; if (has_slash) { load_resources_recurse (sl, &iter, p, &count, &size); *count_out += count; *size_out += size; } else { count = 0; g_resources_get_info (p, 0, &size, NULL, NULL); *count_out += 1; *size_out += size; } gtk_tree_store_set (sl->priv->model, &iter, COLUMN_COUNT, count, COLUMN_SIZE, size, -1); g_free (p); } g_strfreev (names); } static gboolean populate_details (GtkInspectorResourceList *rl, GtkTreePath *tree_path) { GtkTreeIter iter; gchar *path; gchar *name; GBytes *bytes; gchar *type; gconstpointer data; gint count; gsize size; GError *error = NULL; gchar *markup; gtk_tree_model_get_iter (GTK_TREE_MODEL (rl->priv->model), &iter, tree_path); gtk_tree_model_get (GTK_TREE_MODEL (rl->priv->model), &iter, COLUMN_PATH, &path, COLUMN_NAME, &name, COLUMN_COUNT, &count, COLUMN_SIZE, &size, -1); if (g_str_has_suffix (path, "/")) { g_free (path); g_free (name); return FALSE; } markup = g_strconcat ("", path, "", NULL); gtk_label_set_markup (GTK_LABEL (rl->priv->name_label), markup); g_free (markup); bytes = g_resources_lookup_data (path, 0, &error); if (bytes == NULL) { gtk_text_buffer_set_text (rl->priv->buffer, error->message, -1); g_error_free (error); gtk_stack_set_visible_child_name (GTK_STACK (rl->priv->content), "text"); } else { gchar *text; data = g_bytes_get_data (bytes, &size); type = g_content_type_guess (name, data, size, NULL); text = g_content_type_get_description (type); gtk_label_set_text (GTK_LABEL (rl->priv->type_label), text); g_free (text); text = g_format_size (size); gtk_label_set_text (GTK_LABEL (rl->priv->size_label), text); g_free (text); if (g_content_type_is_a (type, "text/*")) { gtk_text_buffer_set_text (rl->priv->buffer, data, -1); gtk_stack_set_visible_child_name (GTK_STACK (rl->priv->content), "text"); } else if (g_content_type_is_a (type, "image/*")) { gtk_image_set_from_resource (GTK_IMAGE (rl->priv->image), path); gtk_stack_set_visible_child_name (GTK_STACK (rl->priv->content), "image"); } else { gtk_text_buffer_set_text (rl->priv->buffer, "", 0); gtk_stack_set_visible_child_name (GTK_STACK (rl->priv->content), "text"); } g_free (type); g_bytes_unref (bytes); } g_free (path); g_free (name); return TRUE; } static void row_activated (GtkTreeView *treeview, GtkTreePath *path, GtkTreeViewColumn *column, GtkInspectorResourceList *sl) { if (!populate_details (sl, path)) return; gtk_stack_set_visible_child_name (GTK_STACK (sl->priv->stack), "details"); gtk_stack_set_visible_child_name (GTK_STACK (sl->priv->buttons), "details"); } static gboolean can_show_details (GtkInspectorResourceList *rl) { GtkTreeSelection *selection; GtkTreeModel *model; GtkTreeIter iter; gchar *path; selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (rl->priv->tree)); if (!gtk_tree_selection_get_selected (selection, &model, &iter)) return FALSE; gtk_tree_model_get (GTK_TREE_MODEL (rl->priv->model), &iter, COLUMN_PATH, &path, -1); if (g_str_has_suffix (path, "/")) { g_free (path); return FALSE; } g_free (path); return TRUE; } static void on_selection_changed (GtkTreeSelection *selection, GtkInspectorResourceList *rl) { gtk_widget_set_sensitive (rl->priv->open_details_button, can_show_details (rl)); } static void open_details (GtkWidget *button, GtkInspectorResourceList *sl) { GtkTreeSelection *selection; GtkTreeModel *model; GtkTreeIter iter; GtkTreePath *path; selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (sl->priv->tree)); if (!gtk_tree_selection_get_selected (selection, &model, &iter)) return; path = gtk_tree_model_get_path (model, &iter); if (populate_details (sl, path)) { gtk_stack_set_visible_child_name (GTK_STACK (sl->priv->stack), "details"); gtk_stack_set_visible_child_name (GTK_STACK (sl->priv->buttons), "details"); } gtk_tree_path_free (path); } static void close_details (GtkWidget *button, GtkInspectorResourceList *sl) { gtk_stack_set_visible_child_name (GTK_STACK (sl->priv->stack), "list"); gtk_stack_set_visible_child_name (GTK_STACK (sl->priv->buttons), "list"); } static void visible_child_name_changed (GObject *obj, GParamSpec *pspec, GtkInspectorResourceList *sl) { const gchar *child; gboolean resources_visible; child = gtk_stack_get_visible_child_name (GTK_STACK (gtk_widget_get_parent (GTK_WIDGET (sl)))); resources_visible = g_strcmp0 (child, "resources") == 0; gtk_widget_set_visible (sl->priv->buttons, resources_visible); gtk_widget_set_sensitive (sl->priv->open_details_button, can_show_details (sl)); } static void load_resources (GtkInspectorResourceList *sl) { gint count = 0; gsize size = 0; load_resources_recurse (sl, NULL, "/", &count, &size); } static void count_data_func (GtkTreeViewColumn *col, GtkCellRenderer *cell, GtkTreeModel *model, GtkTreeIter *iter, gpointer data) { gint count; gchar *text; gtk_tree_model_get (model, iter, COLUMN_COUNT, &count, -1); if (count > 0) { text = g_strdup_printf ("%d", count); g_object_set (cell, "text", text, NULL); g_free (text); } else g_object_set (cell, "text", "", NULL); } static void size_data_func (GtkTreeViewColumn *col, GtkCellRenderer *cell, GtkTreeModel *model, GtkTreeIter *iter, gpointer data) { gint size; gchar *text; gtk_tree_model_get (model, iter, COLUMN_SIZE, &size, -1); text = g_format_size (size); g_object_set (cell, "text", text, NULL); g_free (text); } static void on_map (GtkWidget *widget) { GtkInspectorResourceList *sl = GTK_INSPECTOR_RESOURCE_LIST (widget); gtk_tree_view_expand_all (GTK_TREE_VIEW (sl->priv->tree)); gtk_stack_set_visible_child_name (GTK_STACK (sl->priv->stack), "list"); } static void parent_set (GtkWidget *widget, GtkWidget *old_parent) { if (old_parent) g_signal_handlers_disconnect_by_func (old_parent, visible_child_name_changed, widget); g_signal_connect (gtk_widget_get_parent (widget), "notify::visible-child-name", G_CALLBACK (visible_child_name_changed), widget); } static void gtk_inspector_resource_list_init (GtkInspectorResourceList *sl) { sl->priv = gtk_inspector_resource_list_get_instance_private (sl); gtk_widget_init_template (GTK_WIDGET (sl)); gtk_tree_view_column_set_cell_data_func (sl->priv->count_column, sl->priv->count_renderer, count_data_func, sl, NULL); gtk_tree_view_column_set_cell_data_func (sl->priv->size_column, sl->priv->size_renderer, size_data_func, sl, NULL); g_signal_connect (sl, "map", G_CALLBACK (on_map), NULL); g_signal_connect (sl->priv->stack, "notify::visible-child-name", G_CALLBACK (visible_child_name_changed), sl); } static void constructed (GObject *object) { GtkInspectorResourceList *rl = GTK_INSPECTOR_RESOURCE_LIST (object); g_signal_connect (rl->priv->open_details_button, "clicked", G_CALLBACK (open_details), rl); g_signal_connect (rl->priv->close_details_button, "clicked", G_CALLBACK (close_details), rl); load_resources (rl); } static void get_property (GObject *object, guint param_id, GValue *value, GParamSpec *pspec) { GtkInspectorResourceList *rl = GTK_INSPECTOR_RESOURCE_LIST (object); switch (param_id) { case PROP_BUTTONS: g_value_take_object (value, rl->priv->buttons); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); break; } } static void set_property (GObject *object, guint param_id, const GValue *value, GParamSpec *pspec) { GtkInspectorResourceList *rl = GTK_INSPECTOR_RESOURCE_LIST (object); switch (param_id) { case PROP_BUTTONS: rl->priv->buttons = g_value_get_object (value); rl->priv->open_details_button = gtk_stack_get_child_by_name (GTK_STACK (rl->priv->buttons), "list"); rl->priv->close_details_button = gtk_stack_get_child_by_name (GTK_STACK (rl->priv->buttons), "details"); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); break; } } static void gtk_inspector_resource_list_class_init (GtkInspectorResourceListClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); object_class->get_property = get_property; object_class->set_property = set_property; object_class->constructed = constructed; widget_class->parent_set = parent_set; g_object_class_install_property (object_class, PROP_BUTTONS, g_param_spec_object ("buttons", NULL, NULL, GTK_TYPE_WIDGET, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); gtk_widget_class_set_template_from_resource (widget_class, "/org/gtk/inspector/resource-list.ui"); gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorResourceList, model); gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorResourceList, buffer); gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorResourceList, content); gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorResourceList, image); gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorResourceList, name_label); gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorResourceList, type_label); gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorResourceList, type); gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorResourceList, size_label); gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorResourceList, info_grid); gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorResourceList, count_column); gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorResourceList, count_renderer); gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorResourceList, size_column); gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorResourceList, size_renderer); gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorResourceList, stack); gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorResourceList, tree); gtk_widget_class_bind_template_callback (widget_class, row_activated); gtk_widget_class_bind_template_callback (widget_class, on_selection_changed); } // vim: set et sw=2 ts=2: