/* * Copyright (C) 2009 - 2011 Vivien Malerba * Copyright (C) 2010 David King * Copyright (C) 2010 Jonh Wendell * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include "gda-tree.h" #include "gda-tree-manager.h" #include "gda-tree-node.h" #include struct _GdaTreePrivate { GSList *managers; /* list of GdaTreeManager */ GdaTreeNode *root; }; static void gda_tree_class_init (GdaTreeClass *klass); static void gda_tree_init (GdaTree *tree, GdaTreeClass *klass); static void gda_tree_dispose (GObject *object); static void gda_tree_set_property (GObject *object, guint param_id, const GValue *value, GParamSpec *pspec); static void gda_tree_get_property (GObject *object, guint param_id, GValue *value, GParamSpec *pspec); static gboolean create_or_update_children (GSList *mgrlist, GdaTreeNode *parent, gboolean disable_recurs, GError **error); static void node_changed_cb (GdaTreeNode *reporting, GdaTreeNode *node, GdaTree *tree); static void node_inserted_cb (GdaTreeNode *reporting, GdaTreeNode *node, GdaTree *tree); static void node_has_child_toggled_cb (GdaTreeNode *reporting, GdaTreeNode *node, GdaTree *tree); static void node_deleted_cb (GdaTreeNode *reporting, const gchar *relative_path, GdaTree *tree); static void take_root_node (GdaTree *tree, GdaTreeNode *root); static void unset_root_node (GdaTree *tree); enum { NODE_CHANGED, NODE_INSERTED, NODE_HAS_CHILD_TOGGLED, NODE_DELETED, LAST_SIGNAL }; static gint gda_tree_signals[LAST_SIGNAL] = { 0, 0, 0, 0 }; extern GdaAttributesManager *_gda_tree_node_attributes_manager; /* properties */ enum { PROP_0, PROP_IS_LIST }; static GObjectClass *parent_class = NULL; /* * GdaTree class implementation * @klass: */ static void gda_tree_class_init (GdaTreeClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); parent_class = g_type_class_peek_parent (klass); /* signals */ /** * GdaTree::node-changed: * @tree: the #GdaTree * @node: the #GdaTreeNode which has changed * * Gets emitted when a @node has changed in @tree * * Since: 4.2 */ gda_tree_signals[NODE_CHANGED] = g_signal_new ("node_changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GdaTreeClass, node_changed), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GDA_TYPE_TREE_NODE); /** * GdaTree::node-inserted: * @tree: the #GdaTree * @node: the #GdaTreeNode which has inserted * * Gets emitted when a @node has been inserted in @tree * * Since: 4.2 */ gda_tree_signals[NODE_INSERTED] = g_signal_new ("node_inserted", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GdaTreeClass, node_inserted), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GDA_TYPE_TREE_NODE); /** * GdaTree::node-has-child-toggled: * @tree: the #GdaTree * @node: the #GdaTreeNode which changed from having children to being a * leaf or the other way around * * Gets emitted when a @node has has a child when it did not have any or when it * does not have a ny children anymore when it had some * * Since: 4.2 */ gda_tree_signals[NODE_HAS_CHILD_TOGGLED] = g_signal_new ("node_has-child-toggled", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GdaTreeClass, node_has_child_toggled), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GDA_TYPE_TREE_NODE); /** * GdaTree::node-deleted: * @tree: the #GdaTree * @node_path: the position the node held in @tree as a tree path * * Gets emitted when a @node has been removed from @tree * * Since: 4.2 */ gda_tree_signals[NODE_DELETED] = g_signal_new ("node_deleted", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GdaTreeClass, node_deleted), NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); klass->node_changed = NULL; klass->node_inserted = NULL; klass->node_has_child_toggled = NULL; klass->node_deleted = NULL; /* Properties */ object_class->set_property = gda_tree_set_property; object_class->get_property = gda_tree_get_property; /** * GdaTree:is-list: * * Tells if the GdaTree is a list or a tree. */ g_object_class_install_property (object_class, PROP_IS_LIST, g_param_spec_boolean ("is-list", _("Tells if the GdaTree is a list or a tree"), NULL, FALSE, G_PARAM_READABLE)); object_class->dispose = gda_tree_dispose; } static void gda_tree_init (GdaTree *tree, G_GNUC_UNUSED GdaTreeClass *klass) { g_return_if_fail (GDA_IS_TREE (tree)); tree->priv = g_new0 (GdaTreePrivate, 1); tree->priv->managers = NULL; take_root_node (tree, gda_tree_node_new (NULL)); } static void gda_tree_dispose (GObject *object) { GdaTree *tree = (GdaTree *) object; g_return_if_fail (GDA_IS_TREE (tree)); if (tree->priv) { if (tree->priv->root) unset_root_node (tree); if (tree->priv->managers) { g_slist_foreach (tree->priv->managers, (GFunc) g_object_unref, NULL); g_slist_free (tree->priv->managers); } g_free (tree->priv); tree->priv = NULL; } /* chain to parent class */ parent_class->dispose (object); } /* module error */ GQuark gda_tree_error_quark (void) { static GQuark quark; if (!quark) quark = g_quark_from_static_string ("gda_tree_error"); return quark; } /** * gda_tree_get_type: * * Registers the #GdaTree class on the GLib type system. * * Returns: the GType identifying the class. * * Since: 4.2 */ GType gda_tree_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) { static GMutex registering; static const GTypeInfo info = { sizeof (GdaTreeClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) gda_tree_class_init, NULL, NULL, sizeof (GdaTree), 0, (GInstanceInitFunc) gda_tree_init, 0 }; g_mutex_lock (®istering); if (type == 0) type = g_type_register_static (G_TYPE_OBJECT, "GdaTree", &info, 0); g_mutex_unlock (®istering); } return type; } static void gda_tree_set_property (GObject *object, guint param_id, G_GNUC_UNUSED const GValue *value, GParamSpec *pspec) { GdaTree *tree; tree = GDA_TREE (object); if (tree->priv) { switch (param_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); break; } } } static void gda_tree_get_property (GObject *object, guint param_id, GValue *value, GParamSpec *pspec) { GdaTree *tree; tree = GDA_TREE (object); if (tree->priv) { switch (param_id) { case PROP_IS_LIST: { GSList *list; gboolean is_list = TRUE; for (list = tree->priv->managers; list; list = list->next) { if (gda_tree_manager_get_managers ((GdaTreeManager*) list->data)) { is_list = FALSE; break; } } g_value_set_boolean (value, is_list); break; } default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); break; } } } /** * gda_tree_new: * * Creates a new #GdaTree object * * Returns: (transfer full): a new #GdaTree object * * Since: 4.2 */ GdaTree* gda_tree_new (void) { return (GdaTree*) g_object_new (GDA_TYPE_TREE, NULL); } /** * gda_tree_add_manager: * @tree: a #GdaTree object * @manager: (transfer none): a #GdaTreeManager object * * Sets @manager as a top #GdaTreeManager object, which will be responsible for creating top level nodes in @tree. * * Since: 4.2 */ void gda_tree_add_manager (GdaTree *tree, GdaTreeManager *manager) { g_return_if_fail (GDA_IS_TREE (tree)); g_return_if_fail (GDA_IS_TREE_MANAGER (manager)); tree->priv->managers = g_slist_append (tree->priv->managers, manager); g_object_ref (manager); } #ifdef GDA_DEBUG_NO static void dump_attr_foreach_func (const gchar *att_name, const GValue *value, gpointer data) { g_print ("%s ==> %p\n", att_name, value); } static void dump_root_attributes (GdaTreeNode *root) { g_print ("DUMPING attributes for %p\n", root); gda_attributes_manager_foreach (_gda_tree_node_attributes_manager, root, (GdaAttributesManagerFunc) dump_attr_foreach_func, NULL); } #endif /** * gda_tree_clean: * @tree: a #GdaTree object * * Removes any node in @tree * * Since: 4.2 */ void gda_tree_clean (GdaTree *tree) { GdaTreeNode *new_root; g_return_if_fail (GDA_IS_TREE (tree)); TO_IMPLEMENT; /* signal changes */ new_root = gda_tree_node_new (NULL); gda_attributes_manager_copy (_gda_tree_node_attributes_manager, (gpointer) tree->priv->root, _gda_tree_node_attributes_manager, (gpointer) new_root); take_root_node (tree, new_root); } /** * gda_tree_update_all: * @tree: a #GdaTree object * @error: (allow-none): a place to store errors, or %NULL * * Requests that @tree be populated with nodes. If an error occurs, then @tree's contents is left * unchanged, and otherwise @tree's previous contents is completely replaced by the new one. * * Returns: TRUE if no error occurred. * * Since: 4.2 */ gboolean gda_tree_update_all (GdaTree *tree, GError **error) { g_return_val_if_fail (GDA_IS_TREE (tree), FALSE); return create_or_update_children (tree->priv->managers, tree->priv->root, FALSE, error); } /** * gda_tree_update_part: * @tree: a #GdaTree object * @node: a #GdaTreeNode node in @tree * @error: (allow-none): a place to store errors, or %NULL * * Requests that @tree be populated with nodes, starting from @node * * Returns: TRUE if no error occurred. * * Since: 4.2 */ gboolean gda_tree_update_part (GdaTree *tree, GdaTreeNode *node, GError **error) { GSList *mgrlist; GdaTreeManager *mgr; GdaTreeNode *top; g_return_val_if_fail (GDA_IS_TREE (tree), FALSE); g_return_val_if_fail (GDA_IS_TREE_NODE (node), FALSE); top = gda_tree_node_get_parent (node); if (!top) top = tree->priv->root; mgr = _gda_tree_node_get_manager_for_child (top, node); mgrlist = (GSList*) gda_tree_manager_get_managers (mgr); if (mgrlist) { gboolean res; res = create_or_update_children (mgrlist, node, FALSE, error); return res; } return TRUE; } /** * gda_tree_update_children: * @tree: a #GdaTree object * @node: (allow-none): a #GdaTreeNode node in @tree, or %NULL * @error: (allow-none): a place to store errors, or %NULL * * Update the children of @node in @tree (not recursively, to update recursively, use * gda_tree_update_part()). If @node is %NULL then the top level nodes are updated. * * Returns: TRUE if no error occurred. * * Since: 4.2.8 */ gboolean gda_tree_update_children (GdaTree *tree, GdaTreeNode *node, GError **error) { GSList *mgrlist; GdaTreeManager *mgr; GdaTreeNode *top; g_return_val_if_fail (GDA_IS_TREE (tree), FALSE); g_return_val_if_fail (! node || GDA_IS_TREE_NODE (node), FALSE); if (node) { top = gda_tree_node_get_parent (node); if (!top) top = tree->priv->root; mgr = _gda_tree_node_get_manager_for_child (top, node); mgrlist = (GSList*) gda_tree_manager_get_managers (mgr); if (mgrlist) { gboolean res; res = create_or_update_children (mgrlist, node, TRUE, error); return res; } } else { /* update top level nodes */ create_or_update_children (tree->priv->managers, tree->priv->root, TRUE, error); } return TRUE; } /** * gda_tree_dump: * @tree: a #GdaTree * @node: (allow-none): a #GdaTreeNode to start the dump from, or %NULL for a full dump * @stream: (allow-none): a stream to send the dump to, or %NULL for STDOUT * * Dumps the contents of @tree to @stream, using a hierarchical view. * * Since: 4.2 */ void gda_tree_dump (GdaTree *tree, GdaTreeNode *node, FILE *stream) { GdaTreeNodeClass *klass; GString *string; g_return_if_fail (GDA_IS_TREE (tree)); if (!node) node = tree->priv->root; string = g_string_new (".\n"); klass = (GdaTreeNodeClass*) G_OBJECT_GET_CLASS (node); klass->dump_children (node, "", string); g_fprintf (stream ? stream : stdout, "%s", string->str); g_string_free (string, TRUE); } static GSList *real_gda_tree_get_nodes_in_path (GdaTree *tree, GSList *segments, gboolean use_names, GdaTreeNode **out_last_node); static GSList *decompose_path_as_segments (const gchar *path, gboolean use_names); /** * gda_tree_get_nodes_in_path: * @tree: a #GdaTree object * @tree_path: (allow-none): full path to the required nodes (if @use_names is %TRUE, then it must start with '/'), or %NULL * @use_names: if %TRUE, then @tree_path will be interpreted as a unix style path, and if %FALSE, * then @tree_path will be interpreted similarly to the #GtkTreePath's string representation. * * The returned list is a list of all the #GdaTreeNode nodes below the node * at the specified path. * * As a corner case if @tree_path is %NULL, then the returned list contains all the top level nodes. * * Returns: (transfer container) (element-type GdaTreeNode): a new list of #GdaTreeNode pointers, free it with g_slist_free() * * Since: 4.2 */ GSList * gda_tree_get_nodes_in_path (GdaTree *tree, const gchar *tree_path, gboolean use_names) { GSList *segments, *nodes; g_return_val_if_fail (GDA_IS_TREE (tree), NULL); if (tree_path) { segments = decompose_path_as_segments (tree_path, use_names); nodes = real_gda_tree_get_nodes_in_path (tree, segments, use_names, NULL); if (segments) { g_slist_foreach (segments, (GFunc) g_free, NULL); g_slist_free (segments); } } else { nodes = gda_tree_node_get_children (tree->priv->root); #ifdef GDA_DEBUG_NO GSList *list; g_print ("Top nodes:\n"); for (list = nodes; list; list = list->next) { g_print ("Node %s(%p)\n", gda_value_stringify (gda_tree_node_fetch_attribute (GDA_TREE_NODE (list->data), GDA_ATTRIBUTE_NAME)), list->data); } #endif } return nodes; } /* * if @out_last_node is NULL, then it returns the children of the node pointed by @segments; * if @out_last_node is NOT NULL, then it returns NULL and sets @out_last_node to point to the last node encountered * in @segments * */ static GSList * real_gda_tree_get_nodes_in_path (GdaTree *tree, GSList *segments, gboolean use_names, GdaTreeNode **out_last_node) { if (out_last_node) *out_last_node = NULL; /* handle the case where no segment (path) is specified */ if (!segments) { if (out_last_node) return NULL; else return gda_tree_node_get_children (tree->priv->root); } /* get the GdatreeNode for @tree_path */ GSList *seglist; GdaTreeNode *node; GdaTreeNode *parent; for (seglist = segments, parent = tree->priv->root; seglist; seglist = seglist->next, parent = node) { if (use_names) node = gda_tree_node_get_child_name (parent, (gchar *) seglist->data); else node = gda_tree_node_get_child_index (parent, atoi ((gchar *) seglist->data)); /* Flawfinder: ignore */ if (!node) return NULL; } if (out_last_node) { *out_last_node = node; return NULL; } else return gda_tree_node_get_children (node); } static gboolean build_node_path (GdaTree *tree, GdaTreeNode *node, GArray *array); /** * gda_tree_get_node_path: * @tree: a #GdaTree * @node: a #GdaTreeNode node in @tree * * Get the path associated to @node in @tree. * * Returns: (transfer full): a new string, or %NULL if @node is not in @tree * * Since: 4.2 */ gchar * gda_tree_get_node_path (GdaTree *tree, GdaTreeNode *node) { GArray *array; gchar *str = NULL; g_return_val_if_fail (GDA_IS_TREE (tree), NULL); g_return_val_if_fail (GDA_IS_TREE_NODE (node), NULL); if (!tree->priv->root) return NULL; array = g_array_new (TRUE, FALSE, sizeof (gchar*)); if (build_node_path (tree, node, array)) str = g_strjoinv (":", (gchar **) array->data); gsize i; for (i = 0; i < array->len; i++) g_free (g_array_index (array, gchar *, i)); g_array_free (array, TRUE); return str; } static gboolean build_node_path (GdaTree *tree, GdaTreeNode *node, GArray *array) { GdaTreeNode *parent; GSList *list; gint i; parent = gda_tree_node_get_parent (node); if (parent) list = gda_tree_node_get_children (parent); else list = gda_tree_node_get_children (tree->priv->root); i = g_slist_index (list, node); g_slist_free (list); if (i < 0) return FALSE; else { gchar *tmp; tmp = g_strdup_printf ("%d", i); g_array_prepend_val (array, tmp); if (parent) return build_node_path (tree, parent, array); else return TRUE; } } /** * gda_tree_get_node: * @tree: a #GdaTree object * @tree_path: full path to the required nodes (if @use_names is %TRUE, then it must start with '/') * @use_names: if %TRUE, then @tree_path will be interpreted as a unix style path, and if %FALSE, * then @tree_path will be interpreted similarly to the #GtkTreePath's string representation. * * Locates a #GdaTreeNode using the @tree_path path. * * Returns: (transfer none) (allow-none): the requested #GdaTreeNode pointer, or %NULL if not found * * Since: 4.2 */ GdaTreeNode * gda_tree_get_node (GdaTree *tree, const gchar *tree_path, gboolean use_names) { GSList *segments; GdaTreeNode *node = NULL; g_return_val_if_fail (GDA_IS_TREE (tree), NULL); segments = decompose_path_as_segments (tree_path, use_names); if (!segments) return NULL; g_assert (real_gda_tree_get_nodes_in_path (tree, segments, use_names, &node) == NULL); if (segments) { g_slist_foreach (segments, (GFunc) g_free, NULL); g_slist_free (segments); } return node; } /** * gda_tree_get_node_manager: * @tree: a #GdaTree * @node: a #GdaTreeNode present in @tree * * Get the #GdaTreeManager which created @node in @tree * * Returns: (transfer none): the #GdaTreeManager, or %NULL if @node is not present in @tree * * Since: 4.2 */ GdaTreeManager * gda_tree_get_node_manager (GdaTree *tree, GdaTreeNode *node) { GdaTreeNode *parent; g_return_val_if_fail (GDA_IS_TREE (tree), NULL); g_return_val_if_fail (GDA_IS_TREE_NODE (node), NULL); parent = gda_tree_node_get_parent (node); return _gda_tree_node_get_manager_for_child (parent ? parent : tree->priv->root, node); } static gboolean create_or_update_children (GSList *mgrlist, GdaTreeNode *parent, gboolean disable_recurs, GError **error) { GSList *list; for (list = mgrlist; list; list = list->next) { GdaTreeManager *manager = GDA_TREE_MANAGER (list->data); gboolean recurs = FALSE; if (disable_recurs) { g_object_get (G_OBJECT (manager), "recursive", &recurs, NULL); if (recurs) g_object_set (G_OBJECT (manager), "recursive", FALSE, NULL); } gboolean has_error = FALSE; _gda_tree_manager_update_children (manager, parent, _gda_tree_node_get_children_for_manager (parent, manager), &has_error, error); if (has_error) return FALSE; if (disable_recurs && recurs) g_object_set (G_OBJECT (manager), "recursive", TRUE, NULL); } return TRUE; } static GSList *split_absolute_path (const gchar *path, gboolean *out_error); static GSList *split_indexed_path (const gchar *path, gboolean *out_error); /** * decompose_path_as_segments: * @path: a path using '/' * @use_names: * * Returns: a new list of allocated strings (one for each segment of @path), or %NULL */ static GSList * decompose_path_as_segments (const gchar *path, gboolean use_names) { GSList *segments; gboolean path_error; if (!path) return NULL; if (use_names) { if (*path != '/') { g_warning (_("Path format error: %s"), path); return NULL; } segments = split_absolute_path (path, &path_error); } else segments = split_indexed_path (path, &path_error); if (path_error) { g_warning (_("Path format error: %s"), path); return NULL; } return segments; } /* * Splits @path into a list of path segments, avoiding empty ("") segments * @path is expected to be a unix style path * FIXME: check for errors * Returns: a new list of allocated strings (one for each segment of @path) */ static GSList * split_absolute_path (const gchar *path, gboolean *out_error) { GSList *list = NULL; gchar *copy; gchar *start, *end; *out_error = FALSE; copy = g_strdup (path); start = copy; for (;;) { for (end = start; *end; end++) { if (*end == '/') break; } if (*end == '/') { *end = 0; if (start != end) list = g_slist_prepend (list, g_strdup (start)); start = end + 1; } else { if (start != end) list = g_slist_prepend (list, g_strdup (start)); break; } } g_free (copy); list = g_slist_reverse (list); #ifdef GDA_DEBUG_NO GSList *l; for (l = list; l; l = l->next) g_print ("Part: #%s#\n", (gchar*) l->data); #endif return list; } /* * Splits @path into a list of path segments, avoiding empty ("") segments * @path is expected to be a GtkTreePath's string representation path (ex: "3:2") * Returns: a new list of allocated strings (one for each segment of @path) */ static GSList * split_indexed_path (const gchar *path, gboolean *out_error) { GSList *list = NULL; gchar *copy; gchar *start, *end; *out_error = FALSE; copy = g_strdup (path); start = copy; for (;;) { for (end = start; *end; end++) { if (*end == ':') break; if ((*end < '0') || (*end > '9')) { /* error */ *out_error = TRUE; g_slist_foreach (list, (GFunc) g_free, NULL); g_slist_free (list); g_free (copy); return NULL; } } if (*end == ':') { *end = 0; if (start != end) list = g_slist_prepend (list, g_strdup (start)); start = end + 1; } else { if (start != end) list = g_slist_prepend (list, g_strdup (start)); break; } } g_free (copy); list = g_slist_reverse (list); #ifdef GDA_DEBUG_NO GSList *l; g_print ("Splitting path '%s' into: ", path); for (l = list; l; l = l->next) g_print ("%s ", (gchar*) l->data); g_print ("\n"); #endif return list; } /** * gda_tree_set_attribute: * @tree: a #GdaTree object * @attribute: attribute name * @value: a #GValue, or %NULL * @destroy: a function to be called when @attribute is not needed anymore, or %NULL * * Sets an attribute to @tree, which will be accessible to any node in it. * * Since: 4.2 */ void gda_tree_set_attribute (GdaTree *tree, const gchar *attribute, const GValue *value, GDestroyNotify destroy) { g_return_if_fail (GDA_IS_TREE (tree)); gda_tree_node_set_node_attribute (tree->priv->root, attribute, value, destroy); } static void take_root_node (GdaTree *tree, GdaTreeNode *root) { if (tree->priv->root) unset_root_node (tree); tree->priv->root = root; g_signal_connect (tree->priv->root, "node-changed", G_CALLBACK (node_changed_cb), tree); g_signal_connect (tree->priv->root, "node-inserted", G_CALLBACK (node_inserted_cb), tree); g_signal_connect (tree->priv->root, "node-has-child-toggled", G_CALLBACK (node_has_child_toggled_cb), tree); g_signal_connect (tree->priv->root, "node-deleted", G_CALLBACK (node_deleted_cb), tree); } static void unset_root_node (GdaTree *tree) { GSList *list; for (list = tree->priv->managers; list; list = list->next) { GdaTreeManager *manager = GDA_TREE_MANAGER (list->data); _gda_tree_node_add_children (tree->priv->root, manager, NULL); } g_signal_handlers_disconnect_by_func (tree->priv->root, G_CALLBACK (node_changed_cb), tree); g_signal_handlers_disconnect_by_func (tree->priv->root, G_CALLBACK (node_inserted_cb), tree); g_signal_handlers_disconnect_by_func (tree->priv->root, G_CALLBACK (node_has_child_toggled_cb), tree); g_signal_handlers_disconnect_by_func (tree->priv->root, G_CALLBACK (node_deleted_cb), tree); g_object_unref (tree->priv->root); } static void node_changed_cb (GdaTreeNode *reporting, GdaTreeNode *node, GdaTree *tree) { if ((reporting != node) || (reporting != tree->priv->root)) g_signal_emit (tree, gda_tree_signals [NODE_CHANGED], 0, node); } static void node_inserted_cb (GdaTreeNode *reporting, GdaTreeNode *node, GdaTree *tree) { if ((reporting != node) || (reporting != tree->priv->root)) g_signal_emit (tree, gda_tree_signals [NODE_INSERTED], 0, node); } static void node_has_child_toggled_cb (GdaTreeNode *reporting, GdaTreeNode *node, GdaTree *tree) { if ((reporting != node) || (reporting != tree->priv->root)) g_signal_emit (tree, gda_tree_signals [NODE_HAS_CHILD_TOGGLED], 0, node); } static void node_deleted_cb (G_GNUC_UNUSED GdaTreeNode *reporting, const gchar *relative_path, GdaTree *tree) { g_signal_emit (tree, gda_tree_signals [NODE_DELETED], 0, relative_path); }