/* GIMP - The GNU Image Manipulation Program * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * gimpcontainertreestore.c * Copyright (C) 2010 Michael Natterer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "config.h" #include #include #include #include "widgets-types.h" #include "core/gimpcontainer.h" #include "core/gimpviewable.h" #include "gimpcellrendererviewable.h" #include "gimpcontainertreestore.h" #include "gimpcontainerview.h" #include "gimpviewrenderer.h" enum { PROP_0, PROP_CONTAINER_VIEW, PROP_USE_NAME }; typedef struct _GimpContainerTreeStorePrivate GimpContainerTreeStorePrivate; struct _GimpContainerTreeStorePrivate { GimpContainerView *container_view; GList *renderer_cells; gboolean use_name; }; #define GET_PRIVATE(store) \ ((GimpContainerTreeStorePrivate *) gimp_container_tree_store_get_instance_private ((GimpContainerTreeStore *) (store))) static void gimp_container_tree_store_constructed (GObject *object); static void gimp_container_tree_store_finalize (GObject *object); static void gimp_container_tree_store_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void gimp_container_tree_store_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void gimp_container_tree_store_set (GimpContainerTreeStore *store, GtkTreeIter *iter, GimpViewable *viewable); static void gimp_container_tree_store_renderer_update (GimpViewRenderer *renderer, GimpContainerTreeStore *store); G_DEFINE_TYPE_WITH_PRIVATE (GimpContainerTreeStore, gimp_container_tree_store, GTK_TYPE_TREE_STORE) #define parent_class gimp_container_tree_store_parent_class static void gimp_container_tree_store_class_init (GimpContainerTreeStoreClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->constructed = gimp_container_tree_store_constructed; object_class->finalize = gimp_container_tree_store_finalize; object_class->set_property = gimp_container_tree_store_set_property; object_class->get_property = gimp_container_tree_store_get_property; g_object_class_install_property (object_class, PROP_CONTAINER_VIEW, g_param_spec_object ("container-view", NULL, NULL, GIMP_TYPE_CONTAINER_VIEW, GIMP_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_USE_NAME, g_param_spec_boolean ("use-name", NULL, NULL, FALSE, GIMP_PARAM_READWRITE)); } static void gimp_container_tree_store_init (GimpContainerTreeStore *store) { } static void gimp_container_tree_store_constructed (GObject *object) { G_OBJECT_CLASS (parent_class)->constructed (object); } static void gimp_container_tree_store_finalize (GObject *object) { GimpContainerTreeStorePrivate *private = GET_PRIVATE (object); if (private->renderer_cells) { g_list_free (private->renderer_cells); private->renderer_cells = NULL; } G_OBJECT_CLASS (parent_class)->finalize (object); } static void gimp_container_tree_store_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GimpContainerTreeStorePrivate *private = GET_PRIVATE (object); switch (property_id) { case PROP_CONTAINER_VIEW: private->container_view = g_value_get_object (value); /* don't ref */ break; case PROP_USE_NAME: private->use_name = g_value_get_boolean (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gimp_container_tree_store_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GimpContainerTreeStorePrivate *private = GET_PRIVATE (object); switch (property_id) { case PROP_CONTAINER_VIEW: g_value_set_object (value, private->container_view); break; case PROP_USE_NAME: g_value_set_boolean (value, private->use_name); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } /* public functions */ GtkTreeModel * gimp_container_tree_store_new (GimpContainerView *container_view, gint n_columns, GType *types) { GimpContainerTreeStore *store; g_return_val_if_fail (GIMP_IS_CONTAINER_VIEW (container_view), NULL); g_return_val_if_fail (n_columns >= GIMP_CONTAINER_TREE_STORE_N_COLUMNS, NULL); g_return_val_if_fail (types != NULL, NULL); store = g_object_new (GIMP_TYPE_CONTAINER_TREE_STORE, "container-view", container_view, NULL); gtk_tree_store_set_column_types (GTK_TREE_STORE (store), n_columns, types); return GTK_TREE_MODEL (store); } void gimp_container_tree_store_add_renderer_cell (GimpContainerTreeStore *store, GtkCellRenderer *cell) { GimpContainerTreeStorePrivate *private; g_return_if_fail (GIMP_IS_CONTAINER_TREE_STORE (store)); g_return_if_fail (GIMP_IS_CELL_RENDERER_VIEWABLE (cell)); private = GET_PRIVATE (store); private->renderer_cells = g_list_prepend (private->renderer_cells, cell); } void gimp_container_tree_store_set_use_name (GimpContainerTreeStore *store, gboolean use_name) { GimpContainerTreeStorePrivate *private; g_return_if_fail (GIMP_IS_CONTAINER_TREE_STORE (store)); private = GET_PRIVATE (store); if (private->use_name != use_name) { private->use_name = use_name ? TRUE : FALSE; g_object_notify (G_OBJECT (store), "use-name"); } } gboolean gimp_container_tree_store_get_use_name (GimpContainerTreeStore *store) { g_return_val_if_fail (GIMP_IS_CONTAINER_TREE_STORE (store), FALSE); return GET_PRIVATE (store)->use_name; } static gboolean gimp_container_tree_store_set_context_foreach (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data) { GimpContext *context = data; GimpViewRenderer *renderer; gtk_tree_model_get (model, iter, GIMP_CONTAINER_TREE_STORE_COLUMN_RENDERER, &renderer, -1); gimp_view_renderer_set_context (renderer, context); g_object_unref (renderer); return FALSE; } void gimp_container_tree_store_set_context (GimpContainerTreeStore *store, GimpContext *context) { g_return_if_fail (GIMP_IS_CONTAINER_TREE_STORE (store)); gtk_tree_model_foreach (GTK_TREE_MODEL (store), gimp_container_tree_store_set_context_foreach, context); } GtkTreeIter * gimp_container_tree_store_insert_item (GimpContainerTreeStore *store, GimpViewable *viewable, GtkTreeIter *parent, gint index) { GtkTreeIter iter; g_return_val_if_fail (GIMP_IS_CONTAINER_TREE_STORE (store), NULL); if (index == -1) gtk_tree_store_append (GTK_TREE_STORE (store), &iter, parent); else gtk_tree_store_insert (GTK_TREE_STORE (store), &iter, parent, index); gimp_container_tree_store_set (store, &iter, viewable); return gtk_tree_iter_copy (&iter); } void gimp_container_tree_store_remove_item (GimpContainerTreeStore *store, GimpViewable *viewable, GtkTreeIter *iter) { if (iter) { GtkTreeModel *model = GTK_TREE_MODEL (store); GtkTreePath *path; /* emit a "row-changed" signal for 'iter', so that editing of * corresponding tree-view rows is canceled. otherwise, if we remove the * item while a corresponding row is being edited, bad things happen (see * bug #792991). */ path = gtk_tree_model_get_path (model, iter); gtk_tree_model_row_changed (model, path, iter); gtk_tree_path_free (path); gtk_tree_store_remove (GTK_TREE_STORE (store), iter); /* If the store is empty after this remove, clear out renderers * from all cells so they don't keep refing the viewables * (see bug #149906). */ if (! gtk_tree_model_iter_n_children (model, NULL)) { GimpContainerTreeStorePrivate *private = GET_PRIVATE (store); GList *list; for (list = private->renderer_cells; list; list = list->next) g_object_set (list->data, "renderer", NULL, NULL); } } } void gimp_container_tree_store_reorder_item (GimpContainerTreeStore *store, GimpViewable *viewable, gint new_index, GtkTreeIter *iter) { GimpContainerTreeStorePrivate *private; GimpViewable *parent; GimpContainer *container; g_return_if_fail (GIMP_IS_CONTAINER_TREE_STORE (store)); private = GET_PRIVATE (store); if (! iter) return; parent = gimp_viewable_get_parent (viewable); if (parent) container = gimp_viewable_get_children (parent); else container = gimp_container_view_get_container (private->container_view); if (new_index == -1 || new_index == gimp_container_get_n_children (container) - 1) { gtk_tree_store_move_before (GTK_TREE_STORE (store), iter, NULL); } else if (new_index == 0) { gtk_tree_store_move_after (GTK_TREE_STORE (store), iter, NULL); } else { GtkTreePath *path; GtkTreeIter place_iter; gint depth; gint *indices; gint old_index; path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), iter); indices = gtk_tree_path_get_indices (path); depth = gtk_tree_path_get_depth (path); old_index = indices[depth - 1]; if (new_index != old_index) { indices[depth - 1] = new_index; gtk_tree_model_get_iter (GTK_TREE_MODEL (store), &place_iter, path); if (new_index > old_index) gtk_tree_store_move_after (GTK_TREE_STORE (store), iter, &place_iter); else gtk_tree_store_move_before (GTK_TREE_STORE (store), iter, &place_iter); } gtk_tree_path_free (path); } } gboolean gimp_container_tree_store_rename_item (GimpContainerTreeStore *store, GimpViewable *viewable, GtkTreeIter *iter) { gboolean new_name_shorter = FALSE; g_return_val_if_fail (GIMP_IS_CONTAINER_TREE_STORE (store), FALSE); if (iter) { GimpContainerTreeStorePrivate *private = GET_PRIVATE (store); gchar *name; gchar *old_name; if (private->use_name) name = (gchar *) gimp_object_get_name (viewable); else name = gimp_viewable_get_description (viewable, NULL); gtk_tree_model_get (GTK_TREE_MODEL (store), iter, GIMP_CONTAINER_TREE_STORE_COLUMN_NAME, &old_name, -1); gtk_tree_store_set (GTK_TREE_STORE (store), iter, GIMP_CONTAINER_TREE_STORE_COLUMN_NAME, name, -1); if (name && old_name && strlen (name) < strlen (old_name)) new_name_shorter = TRUE; if (! private->use_name) g_free (name); g_free (old_name); } return new_name_shorter; } void gimp_container_tree_store_clear_items (GimpContainerTreeStore *store) { g_return_if_fail (GIMP_IS_CONTAINER_TREE_STORE (store)); gtk_tree_store_clear (GTK_TREE_STORE (store)); /* If the store is empty after this remove, clear out renderers * from all cells so they don't keep refing the viewables * (see bug #149906). */ if (! gtk_tree_model_iter_n_children (GTK_TREE_MODEL (store), NULL)) { GimpContainerTreeStorePrivate *private = GET_PRIVATE (store); GList *list; for (list = private->renderer_cells; list; list = list->next) g_object_set (list->data, "renderer", NULL, NULL); } } typedef struct { gint view_size; gint border_width; } SetSizeForeachData; static gboolean gimp_container_tree_store_set_view_size_foreach (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data) { SetSizeForeachData *size_data = data; GimpViewRenderer *renderer; gtk_tree_model_get (model, iter, GIMP_CONTAINER_TREE_STORE_COLUMN_RENDERER, &renderer, -1); gimp_view_renderer_set_size (renderer, size_data->view_size, size_data->border_width); g_object_unref (renderer); return FALSE; } void gimp_container_tree_store_set_view_size (GimpContainerTreeStore *store) { GimpContainerTreeStorePrivate *private; SetSizeForeachData size_data; g_return_if_fail (GIMP_IS_CONTAINER_TREE_STORE (store)); private = GET_PRIVATE (store); size_data.view_size = gimp_container_view_get_view_size (private->container_view, &size_data.border_width); gtk_tree_model_foreach (GTK_TREE_MODEL (store), gimp_container_tree_store_set_view_size_foreach, &size_data); } /* private functions */ void gimp_container_tree_store_columns_init (GType *types, gint *n_types) { g_return_if_fail (types != NULL); g_return_if_fail (n_types != NULL); g_return_if_fail (*n_types == 0); gimp_assert (GIMP_CONTAINER_TREE_STORE_COLUMN_RENDERER == gimp_container_tree_store_columns_add (types, n_types, GIMP_TYPE_VIEW_RENDERER)); gimp_assert (GIMP_CONTAINER_TREE_STORE_COLUMN_NAME == gimp_container_tree_store_columns_add (types, n_types, G_TYPE_STRING)); gimp_assert (GIMP_CONTAINER_TREE_STORE_COLUMN_NAME_ATTRIBUTES == gimp_container_tree_store_columns_add (types, n_types, PANGO_TYPE_ATTR_LIST)); gimp_assert (GIMP_CONTAINER_TREE_STORE_COLUMN_NAME_SENSITIVE == gimp_container_tree_store_columns_add (types, n_types, G_TYPE_BOOLEAN)); gimp_assert (GIMP_CONTAINER_TREE_STORE_COLUMN_USER_DATA == gimp_container_tree_store_columns_add (types, n_types, G_TYPE_POINTER)); } gint gimp_container_tree_store_columns_add (GType *types, gint *n_types, GType type) { g_return_val_if_fail (types != NULL, 0); g_return_val_if_fail (n_types != NULL, 0); g_return_val_if_fail (*n_types >= 0, 0); types[*n_types] = type; (*n_types)++; return *n_types - 1; } static void gimp_container_tree_store_set (GimpContainerTreeStore *store, GtkTreeIter *iter, GimpViewable *viewable) { GimpContainerTreeStorePrivate *private = GET_PRIVATE (store); GimpContext *context; GimpViewRenderer *renderer; gchar *name; gint view_size; gint border_width; context = gimp_container_view_get_context (private->container_view); view_size = gimp_container_view_get_view_size (private->container_view, &border_width); renderer = gimp_view_renderer_new (context, G_TYPE_FROM_INSTANCE (viewable), view_size, border_width, FALSE); gimp_view_renderer_set_viewable (renderer, viewable); gimp_view_renderer_remove_idle (renderer); g_signal_connect (renderer, "update", G_CALLBACK (gimp_container_tree_store_renderer_update), store); if (private->use_name) name = (gchar *) gimp_object_get_name (viewable); else name = gimp_viewable_get_description (viewable, NULL); gtk_tree_store_set (GTK_TREE_STORE (store), iter, GIMP_CONTAINER_TREE_STORE_COLUMN_RENDERER, renderer, GIMP_CONTAINER_TREE_STORE_COLUMN_NAME, name, GIMP_CONTAINER_TREE_STORE_COLUMN_NAME_SENSITIVE, TRUE, -1); if (! private->use_name) g_free (name); g_object_unref (renderer); } static void gimp_container_tree_store_renderer_update (GimpViewRenderer *renderer, GimpContainerTreeStore *store) { GimpContainerTreeStorePrivate *private = GET_PRIVATE (store); GtkTreeIter *iter; iter = gimp_container_view_lookup (private->container_view, renderer->viewable); if (iter) { GtkTreePath *path; path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), iter); gtk_tree_model_row_changed (GTK_TREE_MODEL (store), path, iter); gtk_tree_path_free (path); } }