diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:23:22 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:23:22 +0000 |
commit | e42129241681dde7adae7d20697e7b421682fbb4 (patch) | |
tree | af1fe815a5e639e68e59fabd8395ec69458b3e5e /app/core/gimplist.c | |
parent | Initial commit. (diff) | |
download | gimp-upstream.tar.xz gimp-upstream.zip |
Adding upstream version 2.10.22.upstream/2.10.22upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'app/core/gimplist.c')
-rw-r--r-- | app/core/gimplist.c | 691 |
1 files changed, 691 insertions, 0 deletions
diff --git a/app/core/gimplist.c b/app/core/gimplist.c new file mode 100644 index 0000000..0fbca1f --- /dev/null +++ b/app/core/gimplist.c @@ -0,0 +1,691 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis + * + * gimplist.c + * Copyright (C) 2001-2016 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 <stdlib.h> +#include <string.h> /* strcmp */ + +#include <glib-object.h> + +#include "core-types.h" + +#include "gimp-memsize.h" +#include "gimplist.h" + + +enum +{ + PROP_0, + PROP_UNIQUE_NAMES, + PROP_SORT_FUNC, + PROP_APPEND +}; + + +static void gimp_list_finalize (GObject *object); +static void gimp_list_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_list_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static gint64 gimp_list_get_memsize (GimpObject *object, + gint64 *gui_size); + +static void gimp_list_add (GimpContainer *container, + GimpObject *object); +static void gimp_list_remove (GimpContainer *container, + GimpObject *object); +static void gimp_list_reorder (GimpContainer *container, + GimpObject *object, + gint new_index); +static void gimp_list_clear (GimpContainer *container); +static gboolean gimp_list_have (GimpContainer *container, + GimpObject *object); +static void gimp_list_foreach (GimpContainer *container, + GFunc func, + gpointer user_data); +static GimpObject * gimp_list_search (GimpContainer *container, + GimpContainerSearchFunc func, + gpointer user_data); +static gboolean gimp_list_get_unique_names (GimpContainer *container); +static GimpObject * gimp_list_get_child_by_name (GimpContainer *container, + const gchar *name); +static GimpObject * gimp_list_get_child_by_index (GimpContainer *container, + gint index); +static gint gimp_list_get_child_index (GimpContainer *container, + GimpObject *object); + +static void gimp_list_uniquefy_name (GimpList *gimp_list, + GimpObject *object); +static void gimp_list_object_renamed (GimpObject *object, + GimpList *list); + + +G_DEFINE_TYPE (GimpList, gimp_list, GIMP_TYPE_CONTAINER) + +#define parent_class gimp_list_parent_class + + +static void +gimp_list_class_init (GimpListClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass); + GimpContainerClass *container_class = GIMP_CONTAINER_CLASS (klass); + + object_class->finalize = gimp_list_finalize; + object_class->set_property = gimp_list_set_property; + object_class->get_property = gimp_list_get_property; + + gimp_object_class->get_memsize = gimp_list_get_memsize; + + container_class->add = gimp_list_add; + container_class->remove = gimp_list_remove; + container_class->reorder = gimp_list_reorder; + container_class->clear = gimp_list_clear; + container_class->have = gimp_list_have; + container_class->foreach = gimp_list_foreach; + container_class->search = gimp_list_search; + container_class->get_unique_names = gimp_list_get_unique_names; + container_class->get_child_by_name = gimp_list_get_child_by_name; + container_class->get_child_by_index = gimp_list_get_child_by_index; + container_class->get_child_index = gimp_list_get_child_index; + + g_object_class_install_property (object_class, PROP_UNIQUE_NAMES, + g_param_spec_boolean ("unique-names", + NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, PROP_SORT_FUNC, + g_param_spec_pointer ("sort-func", + NULL, NULL, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_APPEND, + g_param_spec_boolean ("append", + NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); +} + +static void +gimp_list_init (GimpList *list) +{ + list->queue = g_queue_new (); + list->unique_names = FALSE; + list->sort_func = NULL; + list->append = FALSE; +} + +static void +gimp_list_finalize (GObject *object) +{ + GimpList *list = GIMP_LIST (object); + + if (list->queue) + { + g_queue_free (list->queue); + list->queue = NULL; + } + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_list_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpList *list = GIMP_LIST (object); + + switch (property_id) + { + case PROP_UNIQUE_NAMES: + list->unique_names = g_value_get_boolean (value); + break; + case PROP_SORT_FUNC: + gimp_list_set_sort_func (list, g_value_get_pointer (value)); + break; + case PROP_APPEND: + list->append = g_value_get_boolean (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_list_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpList *list = GIMP_LIST (object); + + switch (property_id) + { + case PROP_UNIQUE_NAMES: + g_value_set_boolean (value, list->unique_names); + break; + case PROP_SORT_FUNC: + g_value_set_pointer (value, list->sort_func); + break; + case PROP_APPEND: + g_value_set_boolean (value, list->append); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static gint64 +gimp_list_get_memsize (GimpObject *object, + gint64 *gui_size) +{ + GimpList *list = GIMP_LIST (object); + gint64 memsize = 0; + + if (gimp_container_get_policy (GIMP_CONTAINER (list)) == + GIMP_CONTAINER_POLICY_STRONG) + { + memsize += gimp_g_queue_get_memsize_foreach (list->queue, + (GimpMemsizeFunc) gimp_object_get_memsize, + gui_size); + } + else + { + memsize += gimp_g_queue_get_memsize (list->queue, 0); + } + + return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object, + gui_size); +} + +static gint +gimp_list_sort_func (gconstpointer a, + gconstpointer b, + gpointer user_data) +{ + GCompareFunc func = user_data; + + return func (a, b); +} + +static void +gimp_list_add (GimpContainer *container, + GimpObject *object) +{ + GimpList *list = GIMP_LIST (container); + + if (list->unique_names) + gimp_list_uniquefy_name (list, object); + + if (list->unique_names || list->sort_func) + g_signal_connect (object, "name-changed", + G_CALLBACK (gimp_list_object_renamed), + list); + + if (list->sort_func) + { + g_queue_insert_sorted (list->queue, object, gimp_list_sort_func, + list->sort_func); + } + else if (list->append) + { + g_queue_push_tail (list->queue, object); + } + else + { + g_queue_push_head (list->queue, object); + } + + GIMP_CONTAINER_CLASS (parent_class)->add (container, object); +} + +static void +gimp_list_remove (GimpContainer *container, + GimpObject *object) +{ + GimpList *list = GIMP_LIST (container); + + if (list->unique_names || list->sort_func) + g_signal_handlers_disconnect_by_func (object, + gimp_list_object_renamed, + list); + + g_queue_remove (list->queue, object); + + GIMP_CONTAINER_CLASS (parent_class)->remove (container, object); +} + +static void +gimp_list_reorder (GimpContainer *container, + GimpObject *object, + gint new_index) +{ + GimpList *list = GIMP_LIST (container); + + g_queue_remove (list->queue, object); + + if (new_index == gimp_container_get_n_children (container) - 1) + g_queue_push_tail (list->queue, object); + else + g_queue_push_nth (list->queue, object, new_index); +} + +static void +gimp_list_clear (GimpContainer *container) +{ + GimpList *list = GIMP_LIST (container); + + while (g_queue_peek_head (list->queue)) + gimp_container_remove (container, g_queue_peek_head (list->queue)); +} + +static gboolean +gimp_list_have (GimpContainer *container, + GimpObject *object) +{ + GimpList *list = GIMP_LIST (container); + + return g_queue_find (list->queue, object) ? TRUE : FALSE; +} + +static void +gimp_list_foreach (GimpContainer *container, + GFunc func, + gpointer user_data) +{ + GimpList *list = GIMP_LIST (container); + + g_queue_foreach (list->queue, func, user_data); +} + +static GimpObject * +gimp_list_search (GimpContainer *container, + GimpContainerSearchFunc func, + gpointer user_data) +{ + GimpList *list = GIMP_LIST (container); + GList *iter; + + iter = list->queue->head; + + while (iter) + { + GimpObject *object = iter->data; + + iter = g_list_next (iter); + + if (func (object, user_data)) + return object; + } + + return NULL; +} + +static gboolean +gimp_list_get_unique_names (GimpContainer *container) +{ + GimpList *list = GIMP_LIST (container); + + return list->unique_names; +} + +static GimpObject * +gimp_list_get_child_by_name (GimpContainer *container, + const gchar *name) +{ + GimpList *list = GIMP_LIST (container); + GList *glist; + + for (glist = list->queue->head; glist; glist = g_list_next (glist)) + { + GimpObject *object = glist->data; + + if (! strcmp (gimp_object_get_name (object), name)) + return object; + } + + return NULL; +} + +static GimpObject * +gimp_list_get_child_by_index (GimpContainer *container, + gint index) +{ + GimpList *list = GIMP_LIST (container); + + return g_queue_peek_nth (list->queue, index); +} + +static gint +gimp_list_get_child_index (GimpContainer *container, + GimpObject *object) +{ + GimpList *list = GIMP_LIST (container); + + return g_queue_index (list->queue, (gpointer) object); +} + +/** + * gimp_list_new: + * @children_type: the #GType of objects the list is going to hold + * @unique_names: if the list should ensure that all its children + * have unique names. + * + * Creates a new #GimpList object. Since #GimpList is a #GimpContainer + * implementation, it holds GimpObjects. Thus @children_type must be + * GIMP_TYPE_OBJECT or a type derived from it. + * + * The returned list has the #GIMP_CONTAINER_POLICY_STRONG. + * + * Return value: a new #GimpList object + **/ +GimpContainer * +gimp_list_new (GType children_type, + gboolean unique_names) +{ + GimpList *list; + + g_return_val_if_fail (g_type_is_a (children_type, GIMP_TYPE_OBJECT), NULL); + + list = g_object_new (GIMP_TYPE_LIST, + "children-type", children_type, + "policy", GIMP_CONTAINER_POLICY_STRONG, + "unique-names", unique_names ? TRUE : FALSE, + NULL); + + /* for debugging purposes only */ + gimp_object_set_static_name (GIMP_OBJECT (list), g_type_name (children_type)); + + return GIMP_CONTAINER (list); +} + +/** + * gimp_list_new_weak: + * @children_type: the #GType of objects the list is going to hold + * @unique_names: if the list should ensure that all its children + * have unique names. + * + * Creates a new #GimpList object. Since #GimpList is a #GimpContainer + * implementation, it holds GimpObjects. Thus @children_type must be + * GIMP_TYPE_OBJECT or a type derived from it. + * + * The returned list has the #GIMP_CONTAINER_POLICY_WEAK. + * + * Return value: a new #GimpList object + **/ +GimpContainer * +gimp_list_new_weak (GType children_type, + gboolean unique_names) +{ + GimpList *list; + + g_return_val_if_fail (g_type_is_a (children_type, GIMP_TYPE_OBJECT), NULL); + + list = g_object_new (GIMP_TYPE_LIST, + "children-type", children_type, + "policy", GIMP_CONTAINER_POLICY_WEAK, + "unique-names", unique_names ? TRUE : FALSE, + NULL); + + /* for debugging purposes only */ + gimp_object_set_static_name (GIMP_OBJECT (list), g_type_name (children_type)); + + return GIMP_CONTAINER (list); +} + +/** + * gimp_list_reverse: + * @list: a #GimpList + * + * Reverses the order of elements in a #GimpList. + **/ +void +gimp_list_reverse (GimpList *list) +{ + g_return_if_fail (GIMP_IS_LIST (list)); + + if (gimp_container_get_n_children (GIMP_CONTAINER (list)) > 1) + { + gimp_container_freeze (GIMP_CONTAINER (list)); + g_queue_reverse (list->queue); + gimp_container_thaw (GIMP_CONTAINER (list)); + } +} + +/** + * gimp_list_set_sort_func: + * @list: a #GimpList + * @sort_func: a #GCompareFunc + * + * Sorts the elements of @list using gimp_list_sort() and remembers the + * passed @sort_func in order to keep the list ordered across inserting + * or renaming children. + **/ +void +gimp_list_set_sort_func (GimpList *list, + GCompareFunc sort_func) +{ + g_return_if_fail (GIMP_IS_LIST (list)); + + if (sort_func != list->sort_func) + { + if (sort_func) + gimp_list_sort (list, sort_func); + + list->sort_func = sort_func; + g_object_notify (G_OBJECT (list), "sort-func"); + } +} + +/** + * gimp_list_get_sort_func: + * @list: a #GimpList + * + * Returns the @list's sort function, see gimp_list_set_sort_func(). + * + * Return Value: The @list's sort function. + **/ +GCompareFunc +gimp_list_get_sort_func (GimpList*list) +{ + g_return_val_if_fail (GIMP_IS_LIST (list), NULL); + + return list->sort_func; +} + +/** + * gimp_list_sort: + * @list: a #GimpList + * @sort_func: a #GCompareFunc + * + * Sorts the elements of a #GimpList according to the given @sort_func. + * See g_list_sort() for a detailed description of this function. + **/ +void +gimp_list_sort (GimpList *list, + GCompareFunc sort_func) +{ + g_return_if_fail (GIMP_IS_LIST (list)); + g_return_if_fail (sort_func != NULL); + + if (gimp_container_get_n_children (GIMP_CONTAINER (list)) > 1) + { + gimp_container_freeze (GIMP_CONTAINER (list)); + g_queue_sort (list->queue, gimp_list_sort_func, sort_func); + gimp_container_thaw (GIMP_CONTAINER (list)); + } +} + +/** + * gimp_list_sort_by_name: + * @list: a #GimpList + * + * Sorts the #GimpObject elements of a #GimpList by their names. + **/ +void +gimp_list_sort_by_name (GimpList *list) +{ + g_return_if_fail (GIMP_IS_LIST (list)); + + gimp_list_sort (list, (GCompareFunc) gimp_object_name_collate); +} + + +/* private functions */ + +static void +gimp_list_uniquefy_name (GimpList *gimp_list, + GimpObject *object) +{ + gchar *name = (gchar *) gimp_object_get_name (object); + GList *list; + + if (! name) + return; + + for (list = gimp_list->queue->head; list; list = g_list_next (list)) + { + GimpObject *object2 = list->data; + const gchar *name2 = gimp_object_get_name (object2); + + if (object != object2 && + name2 && + ! strcmp (name, name2)) + break; + } + + if (list) + { + gchar *ext; + gchar *new_name = NULL; + gint unique_ext = 0; + + name = g_strdup (name); + + ext = strrchr (name, '#'); + + if (ext) + { + gchar ext_str[8]; + + unique_ext = atoi (ext + 1); + + g_snprintf (ext_str, sizeof (ext_str), "%d", unique_ext); + + /* check if the extension really is of the form "#<n>" */ + if (! strcmp (ext_str, ext + 1)) + { + if (ext > name && *(ext - 1) == ' ') + ext--; + + *ext = '\0'; + } + else + { + unique_ext = 0; + } + } + + do + { + unique_ext++; + + g_free (new_name); + + new_name = g_strdup_printf ("%s #%d", name, unique_ext); + + for (list = gimp_list->queue->head; list; list = g_list_next (list)) + { + GimpObject *object2 = list->data; + const gchar *name2 = gimp_object_get_name (object2); + + if (object != object2 && + name2 && + ! strcmp (new_name, name2)) + break; + } + } + while (list); + + g_free (name); + + gimp_object_take_name (object, new_name); + } +} + +static void +gimp_list_object_renamed (GimpObject *object, + GimpList *list) +{ + if (list->unique_names) + { + g_signal_handlers_block_by_func (object, + gimp_list_object_renamed, + list); + + gimp_list_uniquefy_name (list, object); + + g_signal_handlers_unblock_by_func (object, + gimp_list_object_renamed, + list); + } + + if (list->sort_func) + { + GList *glist; + gint old_index; + gint new_index = 0; + + old_index = g_queue_index (list->queue, object); + + for (glist = list->queue->head; glist; glist = g_list_next (glist)) + { + GimpObject *object2 = GIMP_OBJECT (glist->data); + + if (object == object2) + continue; + + if (list->sort_func (object, object2) > 0) + new_index++; + else + break; + } + + if (new_index != old_index) + gimp_container_reorder (GIMP_CONTAINER (list), object, new_index); + } +} |