diff options
Diffstat (limited to 'app/widgets/gimpcontainertreestore.c')
-rw-r--r-- | app/widgets/gimpcontainertreestore.c | 612 |
1 files changed, 612 insertions, 0 deletions
diff --git a/app/widgets/gimpcontainertreestore.c b/app/widgets/gimpcontainertreestore.c new file mode 100644 index 0000000..643368d --- /dev/null +++ b/app/widgets/gimpcontainertreestore.c @@ -0,0 +1,612 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcontainertreestore.c + * Copyright (C) 2010 Michael Natterer <mitch@gimp.org> + * + * 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 <https://www.gnu.org/licenses/>. + */ + +#include "config.h" + +#include <string.h> + +#include <gegl.h> +#include <gtk/gtk.h> + +#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); + } +} |