diff options
Diffstat (limited to 'app/core/gimptaggedcontainer.c')
-rw-r--r-- | app/core/gimptaggedcontainer.c | 483 |
1 files changed, 483 insertions, 0 deletions
diff --git a/app/core/gimptaggedcontainer.c b/app/core/gimptaggedcontainer.c new file mode 100644 index 0000000..93ea58b --- /dev/null +++ b/app/core/gimptaggedcontainer.c @@ -0,0 +1,483 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis + * + * gimptaggedcontainer.c + * Copyright (C) 2008 Aurimas Juška <aurisj@svn.gnome.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 <gio/gio.h> + +#include "core-types.h" + +#include "gimp.h" +#include "gimpmarshal.h" +#include "gimptag.h" +#include "gimptagged.h" +#include "gimptaggedcontainer.h" + + +enum +{ + TAG_COUNT_CHANGED, + LAST_SIGNAL +}; + + +static void gimp_tagged_container_dispose (GObject *object); +static gint64 gimp_tagged_container_get_memsize (GimpObject *object, + gint64 *gui_size); + +static void gimp_tagged_container_clear (GimpContainer *container); + +static void gimp_tagged_container_src_add (GimpFilteredContainer *filtered_container, + GimpObject *object); +static void gimp_tagged_container_src_remove (GimpFilteredContainer *filtered_container, + GimpObject *object); +static void gimp_tagged_container_src_freeze (GimpFilteredContainer *filtered_container); +static void gimp_tagged_container_src_thaw (GimpFilteredContainer *filtered_container); + +static gboolean gimp_tagged_container_object_matches (GimpTaggedContainer *tagged_container, + GimpObject *object); + +static void gimp_tagged_container_tag_added (GimpTagged *tagged, + GimpTag *tag, + GimpTaggedContainer *tagged_container); +static void gimp_tagged_container_tag_removed (GimpTagged *tagged, + GimpTag *tag, + GimpTaggedContainer *tagged_container); +static void gimp_tagged_container_ref_tag (GimpTaggedContainer *tagged_container, + GimpTag *tag); +static void gimp_tagged_container_unref_tag (GimpTaggedContainer *tagged_container, + GimpTag *tag); +static void gimp_tagged_container_tag_count_changed (GimpTaggedContainer *tagged_container, + gint tag_count); + + +G_DEFINE_TYPE (GimpTaggedContainer, gimp_tagged_container, + GIMP_TYPE_FILTERED_CONTAINER) + +#define parent_class gimp_tagged_container_parent_class + +static guint gimp_tagged_container_signals[LAST_SIGNAL] = { 0, }; + + +static void +gimp_tagged_container_class_init (GimpTaggedContainerClass *klass) +{ + GObjectClass *g_object_class = G_OBJECT_CLASS (klass); + GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass); + GimpContainerClass *container_class = GIMP_CONTAINER_CLASS (klass); + GimpFilteredContainerClass *filtered_class = GIMP_FILTERED_CONTAINER_CLASS (klass); + + g_object_class->dispose = gimp_tagged_container_dispose; + + gimp_object_class->get_memsize = gimp_tagged_container_get_memsize; + + container_class->clear = gimp_tagged_container_clear; + + filtered_class->src_add = gimp_tagged_container_src_add; + filtered_class->src_remove = gimp_tagged_container_src_remove; + filtered_class->src_freeze = gimp_tagged_container_src_freeze; + filtered_class->src_thaw = gimp_tagged_container_src_thaw; + + klass->tag_count_changed = gimp_tagged_container_tag_count_changed; + + gimp_tagged_container_signals[TAG_COUNT_CHANGED] = + g_signal_new ("tag-count-changed", + GIMP_TYPE_TAGGED_CONTAINER, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GimpTaggedContainerClass, tag_count_changed), + NULL, NULL, + gimp_marshal_VOID__INT, + G_TYPE_NONE, 1, + G_TYPE_INT); +} + +static void +gimp_tagged_container_init (GimpTaggedContainer *tagged_container) +{ + tagged_container->tag_ref_counts = + g_hash_table_new_full ((GHashFunc) gimp_tag_get_hash, + (GEqualFunc) gimp_tag_equals, + (GDestroyNotify) g_object_unref, + (GDestroyNotify) NULL); +} + +static void +gimp_tagged_container_dispose (GObject *object) +{ + GimpTaggedContainer *tagged_container = GIMP_TAGGED_CONTAINER (object); + + if (tagged_container->filter) + { + g_list_free_full (tagged_container->filter, + (GDestroyNotify) gimp_tag_or_null_unref); + tagged_container->filter = NULL; + } + + g_clear_pointer (&tagged_container->tag_ref_counts, g_hash_table_unref); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static gint64 +gimp_tagged_container_get_memsize (GimpObject *object, + gint64 *gui_size) +{ + gint64 memsize = 0; + + /* FIXME take members into account */ + + return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object, + gui_size); +} + +static void +gimp_tagged_container_clear (GimpContainer *container) +{ + GimpFilteredContainer *filtered_container = GIMP_FILTERED_CONTAINER (container); + GimpTaggedContainer *tagged_container = GIMP_TAGGED_CONTAINER (container); + GList *list; + + for (list = GIMP_LIST (filtered_container->src_container)->queue->head; + list; + list = g_list_next (list)) + { + g_signal_handlers_disconnect_by_func (list->data, + gimp_tagged_container_tag_added, + tagged_container); + g_signal_handlers_disconnect_by_func (list->data, + gimp_tagged_container_tag_removed, + tagged_container); + } + + if (tagged_container->tag_ref_counts) + { + g_hash_table_remove_all (tagged_container->tag_ref_counts); + tagged_container->tag_count = 0; + } + + GIMP_CONTAINER_CLASS (parent_class)->clear (container); +} + +static void +gimp_tagged_container_src_add (GimpFilteredContainer *filtered_container, + GimpObject *object) +{ + GimpTaggedContainer *tagged_container = GIMP_TAGGED_CONTAINER (filtered_container); + GList *list; + + for (list = gimp_tagged_get_tags (GIMP_TAGGED (object)); + list; + list = g_list_next (list)) + { + gimp_tagged_container_ref_tag (tagged_container, list->data); + } + + g_signal_connect (object, "tag-added", + G_CALLBACK (gimp_tagged_container_tag_added), + tagged_container); + g_signal_connect (object, "tag-removed", + G_CALLBACK (gimp_tagged_container_tag_removed), + tagged_container); + + if (gimp_tagged_container_object_matches (tagged_container, object)) + { + gimp_container_add (GIMP_CONTAINER (tagged_container), object); + } +} + +static void +gimp_tagged_container_src_remove (GimpFilteredContainer *filtered_container, + GimpObject *object) +{ + GimpTaggedContainer *tagged_container = GIMP_TAGGED_CONTAINER (filtered_container); + GList *list; + + g_signal_handlers_disconnect_by_func (object, + gimp_tagged_container_tag_added, + tagged_container); + g_signal_handlers_disconnect_by_func (object, + gimp_tagged_container_tag_removed, + tagged_container); + + for (list = gimp_tagged_get_tags (GIMP_TAGGED (object)); + list; + list = g_list_next (list)) + { + gimp_tagged_container_unref_tag (tagged_container, list->data); + } + + if (gimp_tagged_container_object_matches (tagged_container, object)) + { + gimp_container_remove (GIMP_CONTAINER (tagged_container), object); + } +} + +static void +gimp_tagged_container_src_freeze (GimpFilteredContainer *filtered_container) +{ + gimp_container_clear (GIMP_CONTAINER (filtered_container)); +} + +static void +gimp_tagged_container_src_thaw (GimpFilteredContainer *filtered_container) +{ + GList *list; + + for (list = GIMP_LIST (filtered_container->src_container)->queue->head; + list; + list = g_list_next (list)) + { + gimp_tagged_container_src_add (filtered_container, list->data); + } +} + +/** + * gimp_tagged_container_new: + * @src_container: container to be filtered. + * + * Creates a new #GimpTaggedContainer object which creates filtered + * data view of #GimpTagged objects. It filters @src_container for + * objects containing all of the filtering tags. Synchronization with + * @src_container data is performed automatically. + * + * Return value: a new #GimpTaggedContainer object. + **/ +GimpContainer * +gimp_tagged_container_new (GimpContainer *src_container) +{ + GimpTaggedContainer *tagged_container; + GType children_type; + GCompareFunc sort_func; + + g_return_val_if_fail (GIMP_IS_LIST (src_container), NULL); + + children_type = gimp_container_get_children_type (src_container); + sort_func = GIMP_LIST (src_container)->sort_func; + + tagged_container = g_object_new (GIMP_TYPE_TAGGED_CONTAINER, + "sort-func", sort_func, + "children-type", children_type, + "policy", GIMP_CONTAINER_POLICY_WEAK, + "unique-names", FALSE, + "src-container", src_container, + NULL); + + return GIMP_CONTAINER (tagged_container); +} + +/** + * gimp_tagged_container_set_filter: + * @tagged_container: a #GimpTaggedContainer object. + * @tags: list of #GimpTag objects. + * + * Sets list of tags to be used for filtering. Only objects which have + * all of the tags assigned match filtering criteria. + **/ +void +gimp_tagged_container_set_filter (GimpTaggedContainer *tagged_container, + GList *tags) +{ + GList *new_filter; + + g_return_if_fail (GIMP_IS_TAGGED_CONTAINER (tagged_container)); + + if (tags) + { + GList *list; + + for (list = tags; list; list = g_list_next (list)) + g_return_if_fail (list->data == NULL || GIMP_IS_TAG (list->data)); + } + + if (! gimp_container_frozen (GIMP_FILTERED_CONTAINER (tagged_container)->src_container)) + { + gimp_tagged_container_src_freeze (GIMP_FILTERED_CONTAINER (tagged_container)); + } + + /* ref new tags first, they could be the same as the old ones */ + new_filter = g_list_copy (tags); + g_list_foreach (new_filter, (GFunc) gimp_tag_or_null_ref, NULL); + + g_list_free_full (tagged_container->filter, + (GDestroyNotify) gimp_tag_or_null_unref); + tagged_container->filter = new_filter; + + if (! gimp_container_frozen (GIMP_FILTERED_CONTAINER (tagged_container)->src_container)) + { + gimp_tagged_container_src_thaw (GIMP_FILTERED_CONTAINER (tagged_container)); + } +} + +/** + * gimp_tagged_container_get_filter: + * @tagged_container: a #GimpTaggedContainer object. + * + * Returns current tag filter. Tag filter is a list of GimpTag objects, which + * must be contained by each object matching filter criteria. + * + * Return value: a list of GimpTag objects used as filter. This value should + * not be modified or freed. + **/ +const GList * +gimp_tagged_container_get_filter (GimpTaggedContainer *tagged_container) +{ + g_return_val_if_fail (GIMP_IS_TAGGED_CONTAINER (tagged_container), NULL); + + return tagged_container->filter; +} + +static gboolean +gimp_tagged_container_object_matches (GimpTaggedContainer *tagged_container, + GimpObject *object) +{ + GList *filter_tags; + + for (filter_tags = tagged_container->filter; + filter_tags; + filter_tags = g_list_next (filter_tags)) + { + if (! filter_tags->data) + { + /* invalid tag - does not match */ + return FALSE; + } + + if (! gimp_tagged_has_tag (GIMP_TAGGED (object), + filter_tags->data)) + { + /* match for the tag was not found. + * since query is of type AND, it whole fails. + */ + return FALSE; + } + } + + return TRUE; +} + +static void +gimp_tagged_container_tag_added (GimpTagged *tagged, + GimpTag *tag, + GimpTaggedContainer *tagged_container) +{ + gimp_tagged_container_ref_tag (tagged_container, tag); + + if (gimp_tagged_container_object_matches (tagged_container, + GIMP_OBJECT (tagged)) && + ! gimp_container_have (GIMP_CONTAINER (tagged_container), + GIMP_OBJECT (tagged))) + { + gimp_container_add (GIMP_CONTAINER (tagged_container), + GIMP_OBJECT (tagged)); + } +} + +static void +gimp_tagged_container_tag_removed (GimpTagged *tagged, + GimpTag *tag, + GimpTaggedContainer *tagged_container) +{ + gimp_tagged_container_unref_tag (tagged_container, tag); + + if (! gimp_tagged_container_object_matches (tagged_container, + GIMP_OBJECT (tagged)) && + gimp_container_have (GIMP_CONTAINER (tagged_container), + GIMP_OBJECT (tagged))) + { + gimp_container_remove (GIMP_CONTAINER (tagged_container), + GIMP_OBJECT (tagged)); + } +} + +static void +gimp_tagged_container_ref_tag (GimpTaggedContainer *tagged_container, + GimpTag *tag) +{ + gint ref_count; + + ref_count = GPOINTER_TO_INT (g_hash_table_lookup (tagged_container->tag_ref_counts, + tag)); + ref_count++; + g_hash_table_insert (tagged_container->tag_ref_counts, + g_object_ref (tag), + GINT_TO_POINTER (ref_count)); + + if (ref_count == 1) + { + tagged_container->tag_count++; + g_signal_emit (tagged_container, + gimp_tagged_container_signals[TAG_COUNT_CHANGED], 0, + tagged_container->tag_count); + } +} + +static void +gimp_tagged_container_unref_tag (GimpTaggedContainer *tagged_container, + GimpTag *tag) +{ + gint ref_count; + + ref_count = GPOINTER_TO_INT (g_hash_table_lookup (tagged_container->tag_ref_counts, + tag)); + ref_count--; + + if (ref_count > 0) + { + g_hash_table_insert (tagged_container->tag_ref_counts, + g_object_ref (tag), + GINT_TO_POINTER (ref_count)); + } + else + { + if (g_hash_table_remove (tagged_container->tag_ref_counts, tag)) + { + tagged_container->tag_count--; + g_signal_emit (tagged_container, + gimp_tagged_container_signals[TAG_COUNT_CHANGED], 0, + tagged_container->tag_count); + } + } +} + +static void +gimp_tagged_container_tag_count_changed (GimpTaggedContainer *container, + gint tag_count) +{ +} + +/** + * gimp_tagged_container_get_tag_count: + * @container: a #GimpTaggedContainer object. + * + * Get number of distinct tags that are currently assigned to all + * objects in the container. The count is independent of currently + * used filter, it is provided for all available objects (ie. empty + * filter). + * + * Return value: number of distinct tags assigned to all objects in the + * container. + **/ +gint +gimp_tagged_container_get_tag_count (GimpTaggedContainer *container) +{ + g_return_val_if_fail (GIMP_IS_TAGGED_CONTAINER (container), 0); + + return container->tag_count; +} |