/* PIKA - Photo and Image Kooker Application * a rebranding of The GNU Image Manipulation Program (created with heckimp) * A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio * * Original copyright, applying to most contents (license remains unchanged): * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * pikatreeproxy.c * Copyright (C) 2020 Ell * * 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 "libpikabase/pikabase.h" #include "core-types.h" #include "pikaviewable.h" #include "pikatreeproxy.h" enum { PROP_0, PROP_CONTAINER, PROP_FLAT }; struct _PikaTreeProxyPrivate { PikaContainer *container; gboolean flat; }; /* local function prototypes */ static void pika_tree_proxy_dispose (GObject *object); static void pika_tree_proxy_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void pika_tree_proxy_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void pika_tree_proxy_container_add (PikaContainer *container, PikaObject *object, PikaTreeProxy *tree_proxy); static void pika_tree_proxy_container_remove (PikaContainer *container, PikaObject *object, PikaTreeProxy *tree_proxy); static void pika_tree_proxy_container_reorder (PikaContainer *container, PikaObject *object, gint new_index, PikaTreeProxy *tree_proxy); static void pika_tree_proxy_container_freeze (PikaContainer *container, PikaTreeProxy *tree_proxy); static void pika_tree_proxy_container_thaw (PikaContainer *container, PikaTreeProxy *tree_proxy); static gint pika_tree_proxy_add_container (PikaTreeProxy *tree_proxy, PikaContainer *container, gint index); static void pika_tree_proxy_remove_container (PikaTreeProxy *tree_proxy, PikaContainer *container); static gint pika_tree_proxy_add_object (PikaTreeProxy *tree_proxy, PikaObject *object, gint index); static void pika_tree_proxy_remove_object (PikaTreeProxy *tree_proxy, PikaObject *object); static gint pika_tree_proxy_find_container (PikaTreeProxy *tree_proxy, PikaContainer *container); static gint pika_tree_proxy_find_object (PikaContainer *container, PikaObject *object); G_DEFINE_TYPE_WITH_PRIVATE (PikaTreeProxy, pika_tree_proxy, PIKA_TYPE_LIST) #define parent_class pika_tree_proxy_parent_class /* private functions */ static void pika_tree_proxy_class_init (PikaTreeProxyClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->dispose = pika_tree_proxy_dispose; object_class->set_property = pika_tree_proxy_set_property; object_class->get_property = pika_tree_proxy_get_property; g_object_class_install_property (object_class, PROP_CONTAINER, g_param_spec_object ("container", NULL, NULL, PIKA_TYPE_CONTAINER, PIKA_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_FLAT, g_param_spec_boolean ("flat", NULL, NULL, FALSE, PIKA_PARAM_READWRITE)); } static void pika_tree_proxy_init (PikaTreeProxy *tree_proxy) { tree_proxy->priv = pika_tree_proxy_get_instance_private (tree_proxy); } static void pika_tree_proxy_dispose (GObject *object) { PikaTreeProxy *tree_proxy = PIKA_TREE_PROXY (object); pika_tree_proxy_set_container (tree_proxy, NULL); G_OBJECT_CLASS (parent_class)->dispose (object);; } static void pika_tree_proxy_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { PikaTreeProxy *tree_proxy = PIKA_TREE_PROXY (object); switch (property_id) { case PROP_CONTAINER: pika_tree_proxy_set_container (tree_proxy, g_value_get_object (value)); break; case PROP_FLAT: pika_tree_proxy_set_flat (tree_proxy, g_value_get_boolean (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void pika_tree_proxy_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { PikaTreeProxy *tree_proxy = PIKA_TREE_PROXY (object); switch (property_id) { case PROP_CONTAINER: g_value_set_object (value, tree_proxy->priv->container); break; case PROP_FLAT: g_value_set_boolean (value, tree_proxy->priv->flat); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void pika_tree_proxy_container_add (PikaContainer *container, PikaObject *object, PikaTreeProxy *tree_proxy) { gint index; if (tree_proxy->priv->flat) { index = pika_tree_proxy_find_container (tree_proxy, container) + pika_tree_proxy_find_object (container, object); } else { index = pika_container_get_child_index (container, object); } pika_tree_proxy_add_object (tree_proxy, object, index); } static void pika_tree_proxy_container_remove (PikaContainer *container, PikaObject *object, PikaTreeProxy *tree_proxy) { pika_tree_proxy_remove_object (tree_proxy, object); } static void pika_tree_proxy_container_reorder (PikaContainer *container, PikaObject *object, gint new_index, PikaTreeProxy *tree_proxy) { gint index; if (tree_proxy->priv->flat) { index = pika_tree_proxy_find_container (tree_proxy, container) + pika_tree_proxy_find_object (container, object); if (pika_viewable_get_children (PIKA_VIEWABLE (object))) { pika_container_freeze (PIKA_CONTAINER (tree_proxy)); pika_tree_proxy_remove_object (tree_proxy, object); pika_tree_proxy_add_object (tree_proxy, object, index); pika_container_thaw (PIKA_CONTAINER (tree_proxy)); return; } } else { index = new_index; } pika_container_reorder (PIKA_CONTAINER (tree_proxy), object, index); } static void pika_tree_proxy_container_freeze (PikaContainer *container, PikaTreeProxy *tree_proxy) { pika_container_freeze (PIKA_CONTAINER (tree_proxy)); } static void pika_tree_proxy_container_thaw (PikaContainer *container, PikaTreeProxy *tree_proxy) { pika_container_thaw (PIKA_CONTAINER (tree_proxy)); } typedef struct { PikaTreeProxy *tree_proxy; gint index; } AddContainerData; static void pika_tree_proxy_add_container_func (PikaObject *object, AddContainerData *data) { data->index = pika_tree_proxy_add_object (data->tree_proxy, object, data->index); } static gint pika_tree_proxy_add_container (PikaTreeProxy *tree_proxy, PikaContainer *container, gint index) { AddContainerData data; g_signal_connect (container, "add", G_CALLBACK (pika_tree_proxy_container_add), tree_proxy); g_signal_connect (container, "remove", G_CALLBACK (pika_tree_proxy_container_remove), tree_proxy); g_signal_connect (container, "reorder", G_CALLBACK (pika_tree_proxy_container_reorder), tree_proxy); g_signal_connect (container, "freeze", G_CALLBACK (pika_tree_proxy_container_freeze), tree_proxy); g_signal_connect (container, "thaw", G_CALLBACK (pika_tree_proxy_container_thaw), tree_proxy); data.tree_proxy = tree_proxy; data.index = index; pika_container_freeze (PIKA_CONTAINER (tree_proxy)); pika_container_foreach (container, (GFunc) pika_tree_proxy_add_container_func, &data); pika_container_thaw (PIKA_CONTAINER (tree_proxy)); return data.index; } static void pika_tree_proxy_remove_container_func (PikaObject *object, PikaTreeProxy *tree_proxy) { pika_tree_proxy_remove_object (tree_proxy, object); } static void pika_tree_proxy_remove_container (PikaTreeProxy *tree_proxy, PikaContainer *container) { pika_container_freeze (PIKA_CONTAINER (tree_proxy)); pika_container_foreach (container, (GFunc) pika_tree_proxy_remove_container_func, tree_proxy); pika_container_thaw (PIKA_CONTAINER (tree_proxy)); g_signal_handlers_disconnect_by_func ( container, pika_tree_proxy_container_add, tree_proxy); g_signal_handlers_disconnect_by_func ( container, pika_tree_proxy_container_remove, tree_proxy); g_signal_handlers_disconnect_by_func ( container, pika_tree_proxy_container_reorder, tree_proxy); g_signal_handlers_disconnect_by_func ( container, pika_tree_proxy_container_freeze, tree_proxy); g_signal_handlers_disconnect_by_func ( container, pika_tree_proxy_container_thaw, tree_proxy); } static gint pika_tree_proxy_add_object (PikaTreeProxy *tree_proxy, PikaObject *object, gint index) { if (index == pika_container_get_n_children (PIKA_CONTAINER (tree_proxy))) index = -1; if (tree_proxy->priv->flat) { PikaContainer *children; children = pika_viewable_get_children (PIKA_VIEWABLE (object)); if (children) return pika_tree_proxy_add_container (tree_proxy, children, index); } if (index >= 0) { pika_container_insert (PIKA_CONTAINER (tree_proxy), object, index); return index + 1; } else { pika_container_add (PIKA_CONTAINER (tree_proxy), object); return index; } } static void pika_tree_proxy_remove_object (PikaTreeProxy *tree_proxy, PikaObject *object) { if (tree_proxy->priv->flat) { PikaContainer *children; children = pika_viewable_get_children (PIKA_VIEWABLE (object)); if (children) return pika_tree_proxy_remove_container (tree_proxy, children); } pika_container_remove (PIKA_CONTAINER (tree_proxy), object); } typedef struct { PikaContainer *container; gint index; } FindContainerData; static gboolean pika_tree_proxy_find_container_search_func (PikaObject *object, FindContainerData *data) { PikaContainer *children; children = pika_viewable_get_children (PIKA_VIEWABLE (object)); if (children) { if (children == data->container) return TRUE; return pika_container_search ( children, (PikaContainerSearchFunc) pika_tree_proxy_find_container_search_func, data) != NULL; } data->index++; return FALSE; } static gint pika_tree_proxy_find_container (PikaTreeProxy *tree_proxy, PikaContainer *container) { FindContainerData data; if (container == tree_proxy->priv->container) return 0; data.container = container; data.index = 0; if (pika_container_search ( tree_proxy->priv->container, (PikaContainerSearchFunc) pika_tree_proxy_find_container_search_func, &data)) { return data.index; } g_return_val_if_reached (0); } typedef struct { PikaObject *object; gint index; } FindObjectData; static gboolean pika_tree_proxy_find_object_search_func (PikaObject *object, FindObjectData *data) { PikaContainer *children; if (object == data->object) return TRUE; children = pika_viewable_get_children (PIKA_VIEWABLE (object)); if (children) { return pika_container_search ( children, (PikaContainerSearchFunc) pika_tree_proxy_find_object_search_func, data) != NULL; } data->index++; return FALSE; } static gint pika_tree_proxy_find_object (PikaContainer *container, PikaObject *object) { FindObjectData data; data.object = object; data.index = 0; if (pika_container_search ( container, (PikaContainerSearchFunc) pika_tree_proxy_find_object_search_func, &data)) { return data.index; } g_return_val_if_reached (0); } /* public functions */ PikaContainer * pika_tree_proxy_new (GType children_type) { GTypeClass *children_class; children_class = g_type_class_ref (children_type); g_return_val_if_fail (G_TYPE_CHECK_CLASS_TYPE (children_class, PIKA_TYPE_VIEWABLE), NULL); g_type_class_unref (children_class); return g_object_new (PIKA_TYPE_TREE_PROXY, "children-type", children_type, "policy", PIKA_CONTAINER_POLICY_WEAK, "append", TRUE, NULL); } PikaContainer * pika_tree_proxy_new_for_container (PikaContainer *container) { PikaTreeProxy *tree_proxy; g_return_val_if_fail (PIKA_IS_CONTAINER (container), NULL); tree_proxy = PIKA_TREE_PROXY ( pika_tree_proxy_new (pika_container_get_children_type (container))); pika_tree_proxy_set_container (tree_proxy, container); return PIKA_CONTAINER (tree_proxy); } void pika_tree_proxy_set_container (PikaTreeProxy *tree_proxy, PikaContainer *container) { g_return_if_fail (PIKA_IS_TREE_PROXY (tree_proxy)); g_return_if_fail (container == NULL || PIKA_IS_CONTAINER (container)); if (container) { GTypeClass *children_class; children_class = g_type_class_ref ( pika_container_get_children_type (container)); g_return_if_fail ( G_TYPE_CHECK_CLASS_TYPE ( children_class, pika_container_get_children_type (PIKA_CONTAINER (tree_proxy)))); g_type_class_unref (children_class); } if (container != tree_proxy->priv->container) { pika_container_freeze (PIKA_CONTAINER (tree_proxy)); if (tree_proxy->priv->container) { pika_tree_proxy_remove_container (tree_proxy, tree_proxy->priv->container); } g_set_object (&tree_proxy->priv->container, container); if (tree_proxy->priv->container) { pika_tree_proxy_add_container (tree_proxy, tree_proxy->priv->container, -1); } pika_container_thaw (PIKA_CONTAINER (tree_proxy)); g_object_notify (G_OBJECT (tree_proxy), "container"); } } PikaContainer * pika_tree_proxy_get_container (PikaTreeProxy *tree_proxy) { g_return_val_if_fail (PIKA_IS_TREE_PROXY (tree_proxy), NULL); return tree_proxy->priv->container; } void pika_tree_proxy_set_flat (PikaTreeProxy *tree_proxy, gboolean flat) { g_return_if_fail (PIKA_IS_TREE_PROXY (tree_proxy)); if (flat != tree_proxy->priv->flat) { pika_container_freeze (PIKA_CONTAINER (tree_proxy)); if (tree_proxy->priv->container) { pika_tree_proxy_remove_container (tree_proxy, tree_proxy->priv->container); } tree_proxy->priv->flat = flat; if (tree_proxy->priv->container) { pika_tree_proxy_add_container (tree_proxy, tree_proxy->priv->container, -1); } pika_container_thaw (PIKA_CONTAINER (tree_proxy)); g_object_notify (G_OBJECT (tree_proxy), "flat"); } } gboolean pika_tree_proxy_get_flat (PikaTreeProxy *tree_proxy) { g_return_val_if_fail (PIKA_IS_TREE_PROXY (tree_proxy), FALSE); return tree_proxy->priv->flat; }