1 /*
2  * Copyright (C) 2009 - 2011 Vivien Malerba <malerba@gnome-db.org>
3  * Copyright (C) 2010 David King <davidk@openismus.com>
4  * Copyright (C) 2010 Jonh Wendell <jwendell@gnome.org>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  * Boston, MA  02110-1301, USA.
20  */
21 
22 #include <string.h>
23 #include <stdlib.h>
24 #include <glib/gprintf.h>
25 #include <glib/gi18n-lib.h>
26 #include <libgda/gda-attributes-manager.h>
27 #include "gda-tree.h"
28 #include "gda-tree-manager.h"
29 #include "gda-tree-node.h"
30 #include <libgda/gda-debug-macros.h>
31 
32 struct _GdaTreePrivate {
33 	GSList      *managers; /* list of GdaTreeManager */
34 	GdaTreeNode *root;
35 };
36 
37 static void gda_tree_class_init (GdaTreeClass *klass);
38 static void gda_tree_init       (GdaTree *tree, GdaTreeClass *klass);
39 static void gda_tree_dispose    (GObject *object);
40 static void gda_tree_set_property (GObject *object,
41 				   guint param_id,
42 				   const GValue *value,
43 				   GParamSpec *pspec);
44 static void gda_tree_get_property (GObject *object,
45 				   guint param_id,
46 				   GValue *value,
47 				   GParamSpec *pspec);
48 
49 static gboolean create_or_update_children (GSList *mgrlist, GdaTreeNode *parent, gboolean disable_recurs, GError **error);
50 static void node_changed_cb (GdaTreeNode *reporting, GdaTreeNode *node, GdaTree *tree);
51 static void node_inserted_cb (GdaTreeNode *reporting, GdaTreeNode *node, GdaTree *tree);
52 static void node_has_child_toggled_cb (GdaTreeNode *reporting, GdaTreeNode *node, GdaTree *tree);
53 static void node_deleted_cb (GdaTreeNode *reporting, const gchar *relative_path, GdaTree *tree);
54 
55 static void take_root_node (GdaTree *tree, GdaTreeNode *root);
56 static void unset_root_node (GdaTree *tree);
57 
58 enum {
59 	NODE_CHANGED,
60 	NODE_INSERTED,
61 	NODE_HAS_CHILD_TOGGLED,
62 	NODE_DELETED,
63 	LAST_SIGNAL
64 };
65 
66 static gint gda_tree_signals[LAST_SIGNAL] = { 0, 0, 0, 0 };
67 extern GdaAttributesManager *_gda_tree_node_attributes_manager;
68 
69 /* properties */
70 enum {
71 	PROP_0,
72 	PROP_IS_LIST
73 };
74 
75 static GObjectClass *parent_class = NULL;
76 
77 /*
78  * GdaTree class implementation
79  * @klass:
80  */
81 static void
gda_tree_class_init(GdaTreeClass * klass)82 gda_tree_class_init (GdaTreeClass *klass)
83 {
84 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
85 
86 	parent_class = g_type_class_peek_parent (klass);
87 
88 	/* signals */
89 	/**
90 	 * GdaTree::node-changed:
91 	 * @tree: the #GdaTree
92 	 * @node: the #GdaTreeNode which has changed
93 	 *
94 	 * Gets emitted when a @node has changed in @tree
95 	 *
96 	 * Since: 4.2
97 	 */
98 	gda_tree_signals[NODE_CHANGED] =
99 		g_signal_new ("node_changed",
100 			      G_TYPE_FROM_CLASS (klass),
101 			      G_SIGNAL_RUN_LAST,
102 			      G_STRUCT_OFFSET (GdaTreeClass, node_changed),
103 			      NULL, NULL,
104 			      g_cclosure_marshal_VOID__OBJECT,
105 			      G_TYPE_NONE, 1, GDA_TYPE_TREE_NODE);
106 	/**
107 	 * GdaTree::node-inserted:
108 	 * @tree: the #GdaTree
109 	 * @node: the #GdaTreeNode which has inserted
110 	 *
111 	 * Gets emitted when a @node has been inserted in @tree
112 	 *
113 	 * Since: 4.2
114 	 */
115 	gda_tree_signals[NODE_INSERTED] =
116 		g_signal_new ("node_inserted",
117 			      G_TYPE_FROM_CLASS (klass),
118 			      G_SIGNAL_RUN_LAST,
119 			      G_STRUCT_OFFSET (GdaTreeClass, node_inserted),
120 			      NULL, NULL,
121 			      g_cclosure_marshal_VOID__OBJECT,
122 			      G_TYPE_NONE, 1, GDA_TYPE_TREE_NODE);
123 	/**
124 	 * GdaTree::node-has-child-toggled:
125 	 * @tree: the #GdaTree
126 	 * @node: the #GdaTreeNode which changed from having children to being a
127 	 *        leaf or the other way around
128 	 *
129 	 * Gets emitted when a @node has has a child when it did not have any or when it
130 	 * does not have a ny children anymore when it had some
131 	 *
132 	 * Since: 4.2
133 	 */
134 	gda_tree_signals[NODE_HAS_CHILD_TOGGLED] =
135 		g_signal_new ("node_has-child-toggled",
136 			      G_TYPE_FROM_CLASS (klass),
137 			      G_SIGNAL_RUN_LAST,
138 			      G_STRUCT_OFFSET (GdaTreeClass, node_has_child_toggled),
139 			      NULL, NULL,
140 			      g_cclosure_marshal_VOID__OBJECT,
141 			      G_TYPE_NONE, 1, GDA_TYPE_TREE_NODE);
142 	/**
143 	 * GdaTree::node-deleted:
144 	 * @tree: the #GdaTree
145 	 * @node_path: the position the node held in @tree as a tree path
146 	 *
147 	 * Gets emitted when a @node has been removed from @tree
148 	 *
149 	 * Since: 4.2
150 	 */
151 	gda_tree_signals[NODE_DELETED] =
152 		g_signal_new ("node_deleted",
153 			      G_TYPE_FROM_CLASS (klass),
154 			      G_SIGNAL_RUN_LAST,
155 			      G_STRUCT_OFFSET (GdaTreeClass, node_deleted),
156 			      NULL, NULL,
157 			      g_cclosure_marshal_VOID__STRING,
158 			      G_TYPE_NONE, 1, G_TYPE_STRING);
159 
160 	klass->node_changed = NULL;
161 	klass->node_inserted = NULL;
162 	klass->node_has_child_toggled = NULL;
163 	klass->node_deleted = NULL;
164 
165 	/* Properties */
166         object_class->set_property = gda_tree_set_property;
167         object_class->get_property = gda_tree_get_property;
168 
169 	/**
170 	 * GdaTree:is-list:
171 	 *
172 	 * Tells if the GdaTree is a list or a tree.
173 	 */
174 	g_object_class_install_property (object_class, PROP_IS_LIST,
175                                          g_param_spec_boolean ("is-list", _("Tells if the GdaTree is a list or a tree"), NULL,
176 							       FALSE, G_PARAM_READABLE));
177 
178 	object_class->dispose = gda_tree_dispose;
179 }
180 
181 static void
gda_tree_init(GdaTree * tree,G_GNUC_UNUSED GdaTreeClass * klass)182 gda_tree_init (GdaTree *tree, G_GNUC_UNUSED GdaTreeClass *klass)
183 {
184 	g_return_if_fail (GDA_IS_TREE (tree));
185 
186 	tree->priv = g_new0 (GdaTreePrivate, 1);
187 	tree->priv->managers = NULL;
188 
189 	take_root_node (tree, gda_tree_node_new (NULL));
190 }
191 
192 static void
gda_tree_dispose(GObject * object)193 gda_tree_dispose (GObject *object)
194 {
195 	GdaTree *tree = (GdaTree *) object;
196 
197 	g_return_if_fail (GDA_IS_TREE (tree));
198 
199 	if (tree->priv) {
200 		if (tree->priv->root)
201 			unset_root_node (tree);
202 		if (tree->priv->managers) {
203 			g_slist_foreach (tree->priv->managers, (GFunc) g_object_unref, NULL);
204 			g_slist_free (tree->priv->managers);
205 		}
206 		g_free (tree->priv);
207 		tree->priv = NULL;
208 	}
209 
210 	/* chain to parent class */
211 	parent_class->dispose (object);
212 }
213 
214 
215 /* module error */
gda_tree_error_quark(void)216 GQuark gda_tree_error_quark (void)
217 {
218         static GQuark quark;
219         if (!quark)
220                 quark = g_quark_from_static_string ("gda_tree_error");
221         return quark;
222 }
223 
224 /**
225  * gda_tree_get_type:
226  *
227  * Registers the #GdaTree class on the GLib type system.
228  *
229  * Returns: the GType identifying the class.
230  *
231  * Since: 4.2
232  */
233 GType
gda_tree_get_type(void)234 gda_tree_get_type (void)
235 {
236         static GType type = 0;
237 
238         if (G_UNLIKELY (type == 0)) {
239                 static GMutex registering;
240                 static const GTypeInfo info = {
241                         sizeof (GdaTreeClass),
242                         (GBaseInitFunc) NULL,
243                         (GBaseFinalizeFunc) NULL,
244                         (GClassInitFunc) gda_tree_class_init,
245                         NULL,
246                         NULL,
247                         sizeof (GdaTree),
248                         0,
249                         (GInstanceInitFunc) gda_tree_init,
250 			0
251                 };
252 
253                 g_mutex_lock (&registering);
254                 if (type == 0)
255                         type = g_type_register_static (G_TYPE_OBJECT, "GdaTree", &info, 0);
256                 g_mutex_unlock (&registering);
257         }
258         return type;
259 }
260 
261 static void
gda_tree_set_property(GObject * object,guint param_id,G_GNUC_UNUSED const GValue * value,GParamSpec * pspec)262 gda_tree_set_property (GObject *object,
263 			 guint param_id,
264 			 G_GNUC_UNUSED const GValue *value,
265 			 GParamSpec *pspec)
266 {
267 	GdaTree *tree;
268 
269         tree = GDA_TREE (object);
270         if (tree->priv) {
271                 switch (param_id) {
272 		default:
273 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
274 			break;
275 		}
276 	}
277 }
278 
279 static void
gda_tree_get_property(GObject * object,guint param_id,GValue * value,GParamSpec * pspec)280 gda_tree_get_property (GObject *object,
281 			 guint param_id,
282 			 GValue *value,
283 			 GParamSpec *pspec)
284 {
285 	GdaTree *tree;
286 
287 	tree = GDA_TREE (object);
288 	if (tree->priv) {
289 		switch (param_id) {
290 		case PROP_IS_LIST: {
291 			GSList *list;
292 			gboolean is_list = TRUE;
293 			for (list = tree->priv->managers; list; list = list->next) {
294 				if (gda_tree_manager_get_managers ((GdaTreeManager*) list->data)) {
295 					is_list = FALSE;
296 					break;
297 				}
298 			}
299 			g_value_set_boolean (value, is_list);
300 			break;
301 		}
302 		default:
303 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
304 			break;
305 		}
306 	}
307 }
308 
309 /**
310  * gda_tree_new:
311  *
312  * Creates a new #GdaTree object
313  *
314  * Returns: (transfer full): a new #GdaTree object
315  *
316  * Since: 4.2
317  */
318 GdaTree*
gda_tree_new(void)319 gda_tree_new (void)
320 {
321 	return (GdaTree*) g_object_new (GDA_TYPE_TREE, NULL);
322 }
323 
324 
325 /**
326  * gda_tree_add_manager:
327  * @tree: a #GdaTree object
328  * @manager: (transfer none): a #GdaTreeManager object
329  *
330  * Sets @manager as a top #GdaTreeManager object, which will be responsible for creating top level nodes in @tree.
331  *
332  * Since: 4.2
333  */
334 void
gda_tree_add_manager(GdaTree * tree,GdaTreeManager * manager)335 gda_tree_add_manager (GdaTree *tree, GdaTreeManager *manager)
336 {
337 	g_return_if_fail (GDA_IS_TREE (tree));
338 	g_return_if_fail (GDA_IS_TREE_MANAGER (manager));
339 
340 	tree->priv->managers = g_slist_append (tree->priv->managers, manager);
341 	g_object_ref (manager);
342 }
343 
344 #ifdef GDA_DEBUG_NO
345 static void
dump_attr_foreach_func(const gchar * att_name,const GValue * value,gpointer data)346 dump_attr_foreach_func (const gchar *att_name, const GValue *value, gpointer data)
347 {
348 	g_print ("%s ==> %p\n", att_name, value);
349 }
350 static void
dump_root_attributes(GdaTreeNode * root)351 dump_root_attributes (GdaTreeNode *root)
352 {
353 	g_print ("DUMPING attributes for %p\n", root);
354 	gda_attributes_manager_foreach (_gda_tree_node_attributes_manager, root,
355 					(GdaAttributesManagerFunc) dump_attr_foreach_func, NULL);
356 }
357 #endif
358 
359 /**
360  * gda_tree_clean:
361  * @tree: a #GdaTree object
362  *
363  * Removes any node in @tree
364  *
365  * Since: 4.2
366  */
367 void
gda_tree_clean(GdaTree * tree)368 gda_tree_clean (GdaTree *tree)
369 {
370 	GdaTreeNode *new_root;
371 
372 	g_return_if_fail (GDA_IS_TREE (tree));
373 	TO_IMPLEMENT; /* signal changes */
374 
375 	new_root = gda_tree_node_new (NULL);
376 
377 	gda_attributes_manager_copy (_gda_tree_node_attributes_manager, (gpointer) tree->priv->root,
378 				     _gda_tree_node_attributes_manager, (gpointer) new_root);
379 
380 	take_root_node (tree, new_root);
381 }
382 
383 /**
384  * gda_tree_update_all:
385  * @tree: a #GdaTree object
386  * @error: (allow-none): a place to store errors, or %NULL
387  *
388  * Requests that @tree be populated with nodes. If an error occurs, then @tree's contents is left
389  * unchanged, and otherwise @tree's previous contents is completely replaced by the new one.
390  *
391  * Returns: TRUE if no error occurred.
392  *
393  * Since: 4.2
394  */
395 gboolean
gda_tree_update_all(GdaTree * tree,GError ** error)396 gda_tree_update_all (GdaTree *tree, GError **error)
397 {
398 	g_return_val_if_fail (GDA_IS_TREE (tree), FALSE);
399 	return create_or_update_children (tree->priv->managers, tree->priv->root, FALSE, error);
400 }
401 
402 /**
403  * gda_tree_update_part:
404  * @tree: a #GdaTree object
405  * @node: a #GdaTreeNode node in @tree
406  * @error: (allow-none): a place to store errors, or %NULL
407  *
408  * Requests that @tree be populated with nodes, starting from @node
409  *
410  * Returns: TRUE if no error occurred.
411  *
412  * Since: 4.2
413  */
414 gboolean
gda_tree_update_part(GdaTree * tree,GdaTreeNode * node,GError ** error)415 gda_tree_update_part (GdaTree *tree, GdaTreeNode *node, GError **error)
416 {
417 	GSList *mgrlist;
418 	GdaTreeManager *mgr;
419 	GdaTreeNode *top;
420 
421 	g_return_val_if_fail (GDA_IS_TREE (tree), FALSE);
422 	g_return_val_if_fail (GDA_IS_TREE_NODE (node), FALSE);
423 
424 	top = gda_tree_node_get_parent (node);
425 	if (!top)
426 		top = tree->priv->root;
427 	mgr = _gda_tree_node_get_manager_for_child (top, node);
428 	mgrlist = (GSList*) gda_tree_manager_get_managers (mgr);
429 
430 	if (mgrlist) {
431 		gboolean res;
432 		res = create_or_update_children (mgrlist, node, FALSE, error);
433 		return res;
434 	}
435 	return TRUE;
436 }
437 
438 /**
439  * gda_tree_update_children:
440  * @tree: a #GdaTree object
441  * @node: (allow-none): a #GdaTreeNode node in @tree, or %NULL
442  * @error: (allow-none): a place to store errors, or %NULL
443  *
444  * Update the children of @node in @tree (not recursively, to update recursively, use
445  * gda_tree_update_part()). If @node is %NULL then the top level nodes are updated.
446  *
447  * Returns: TRUE if no error occurred.
448  *
449  * Since: 4.2.8
450  */
451 gboolean
gda_tree_update_children(GdaTree * tree,GdaTreeNode * node,GError ** error)452 gda_tree_update_children (GdaTree *tree, GdaTreeNode *node, GError **error)
453 {
454 	GSList *mgrlist;
455 	GdaTreeManager *mgr;
456 	GdaTreeNode *top;
457 
458 	g_return_val_if_fail (GDA_IS_TREE (tree), FALSE);
459 	g_return_val_if_fail (! node || GDA_IS_TREE_NODE (node), FALSE);
460 
461 	if (node) {
462 		top = gda_tree_node_get_parent (node);
463 		if (!top)
464 			top = tree->priv->root;
465 		mgr = _gda_tree_node_get_manager_for_child (top, node);
466 		mgrlist = (GSList*) gda_tree_manager_get_managers (mgr);
467 
468 		if (mgrlist) {
469 			gboolean res;
470 			res = create_or_update_children (mgrlist, node, TRUE, error);
471 			return res;
472 		}
473 	}
474 	else {
475 		/* update top level nodes */
476 		create_or_update_children (tree->priv->managers, tree->priv->root, TRUE, error);
477 	}
478 
479 	return TRUE;
480 }
481 
482 /**
483  * gda_tree_dump:
484  * @tree: a #GdaTree
485  * @node: (allow-none): a #GdaTreeNode to start the dump from, or %NULL for a full dump
486  * @stream: (allow-none): a stream to send the dump to, or %NULL for STDOUT
487  *
488  * Dumps the contents of @tree to @stream, using a hierarchical view.
489  *
490  * Since: 4.2
491  */
492 void
gda_tree_dump(GdaTree * tree,GdaTreeNode * node,FILE * stream)493 gda_tree_dump (GdaTree *tree, GdaTreeNode *node, FILE *stream)
494 {
495 	GdaTreeNodeClass *klass;
496 	GString *string;
497 
498 	g_return_if_fail (GDA_IS_TREE (tree));
499 
500 	if (!node)
501 		node = tree->priv->root;
502 
503 	string = g_string_new (".\n");
504 	klass = (GdaTreeNodeClass*) G_OBJECT_GET_CLASS (node);
505 	klass->dump_children (node, "", string);
506 	g_fprintf (stream ? stream : stdout, "%s", string->str);
507 	g_string_free (string, TRUE);
508 }
509 
510 static GSList *real_gda_tree_get_nodes_in_path (GdaTree *tree, GSList *segments, gboolean use_names,
511 						GdaTreeNode **out_last_node);
512 static GSList *decompose_path_as_segments (const gchar *path, gboolean use_names);
513 
514 /**
515  * gda_tree_get_nodes_in_path:
516  * @tree: a #GdaTree object
517  * @tree_path: (allow-none): full path to the required nodes (if @use_names is %TRUE, then it must start with '/'), or %NULL
518  * @use_names: if %TRUE, then @tree_path will be interpreted as a unix style path, and if %FALSE,
519  *             then @tree_path will be interpreted similarly to the #GtkTreePath's string representation.
520  *
521  * The returned list is a list of all the #GdaTreeNode nodes <emphasis>below</emphasis> the node
522  * at the specified path.
523  *
524  * As a corner case if @tree_path is %NULL, then the returned list contains all the top level nodes.
525  *
526  * Returns: (transfer container) (element-type GdaTreeNode): a new list of #GdaTreeNode pointers, free it with g_slist_free()
527  *
528  * Since: 4.2
529  */
530 GSList *
gda_tree_get_nodes_in_path(GdaTree * tree,const gchar * tree_path,gboolean use_names)531 gda_tree_get_nodes_in_path (GdaTree *tree, const gchar *tree_path, gboolean use_names)
532 {
533 	GSList *segments, *nodes;
534 
535 	g_return_val_if_fail (GDA_IS_TREE (tree), NULL);
536 
537 	if (tree_path) {
538 		segments = decompose_path_as_segments (tree_path, use_names);
539 		nodes = real_gda_tree_get_nodes_in_path (tree, segments, use_names, NULL);
540 		if (segments) {
541 			g_slist_foreach (segments, (GFunc) g_free, NULL);
542 			g_slist_free (segments);
543 		}
544 	}
545 	else {
546 		nodes = gda_tree_node_get_children (tree->priv->root);
547 #ifdef GDA_DEBUG_NO
548 		GSList *list;
549 		g_print ("Top nodes:\n");
550 		for (list = nodes; list; list = list->next) {
551 			g_print ("Node %s(%p)\n",
552 				 gda_value_stringify (gda_tree_node_fetch_attribute (GDA_TREE_NODE (list->data), GDA_ATTRIBUTE_NAME)),
553 				 list->data);
554 		}
555 #endif
556 	}
557 	return nodes;
558 }
559 
560 /*
561  * if @out_last_node is NULL, then it returns the children of the node pointed by @segments;
562  * if @out_last_node is NOT NULL, then it returns NULL and sets @out_last_node to point to the last node encountered
563  *    in @segments
564  *
565  */
566 static GSList *
real_gda_tree_get_nodes_in_path(GdaTree * tree,GSList * segments,gboolean use_names,GdaTreeNode ** out_last_node)567 real_gda_tree_get_nodes_in_path (GdaTree *tree, GSList *segments, gboolean use_names, GdaTreeNode **out_last_node)
568 {
569 	if (out_last_node)
570 		*out_last_node = NULL;
571 
572 	/* handle the case where no segment (path) is specified */
573 	if (!segments) {
574 		if (out_last_node)
575 			return NULL;
576 		else
577 			return gda_tree_node_get_children (tree->priv->root);
578 	}
579 
580 	/* get the GdatreeNode for @tree_path */
581 	GSList *seglist;
582 	GdaTreeNode *node;
583 	GdaTreeNode *parent;
584 	for (seglist = segments, parent = tree->priv->root;
585 	     seglist;
586 	     seglist = seglist->next, parent = node) {
587 		if (use_names)
588 			node = gda_tree_node_get_child_name (parent, (gchar *) seglist->data);
589 		else
590 			node = gda_tree_node_get_child_index (parent, atoi ((gchar *) seglist->data)); /* Flawfinder: ignore */
591 		if (!node)
592 			return NULL;
593 	}
594 
595 	if (out_last_node) {
596 		*out_last_node = node;
597 		return NULL;
598 	}
599 	else
600 		return gda_tree_node_get_children (node);
601 }
602 
603 static gboolean build_node_path (GdaTree *tree, GdaTreeNode *node, GArray *array);
604 
605 /**
606  * gda_tree_get_node_path:
607  * @tree: a #GdaTree
608  * @node: a #GdaTreeNode node in @tree
609  *
610  * Get the path associated to @node in @tree.
611  *
612  * Returns: (transfer full): a new string, or %NULL if @node is not in @tree
613  *
614  * Since: 4.2
615  */
616 gchar *
gda_tree_get_node_path(GdaTree * tree,GdaTreeNode * node)617 gda_tree_get_node_path (GdaTree *tree, GdaTreeNode *node)
618 {
619 	GArray *array;
620 	gchar *str = NULL;
621 	g_return_val_if_fail (GDA_IS_TREE (tree), NULL);
622 	g_return_val_if_fail (GDA_IS_TREE_NODE (node), NULL);
623 
624 	if (!tree->priv->root)
625 		return NULL;
626 
627 	array = g_array_new (TRUE, FALSE, sizeof (gchar*));
628 	if (build_node_path (tree, node, array))
629 		str = g_strjoinv (":", (gchar **) array->data);
630 
631 	gsize i;
632 	for (i = 0; i < array->len; i++)
633 		g_free (g_array_index (array, gchar *, i));
634 	g_array_free (array, TRUE);
635 	return str;
636 }
637 
638 static gboolean
build_node_path(GdaTree * tree,GdaTreeNode * node,GArray * array)639 build_node_path (GdaTree *tree, GdaTreeNode *node, GArray *array)
640 {
641 	GdaTreeNode *parent;
642 	GSList *list;
643 	gint i;
644 	parent = gda_tree_node_get_parent (node);
645 	if (parent)
646 		list = gda_tree_node_get_children (parent);
647 	else
648 		list = gda_tree_node_get_children (tree->priv->root);
649 
650 	i = g_slist_index (list, node);
651 	g_slist_free (list);
652 
653 	if (i < 0)
654 		return FALSE;
655 	else {
656 		gchar *tmp;
657 		tmp = g_strdup_printf ("%d", i);
658 		g_array_prepend_val (array, tmp);
659 		if (parent)
660 			return build_node_path (tree, parent, array);
661 		else
662 			return TRUE;
663 	}
664 }
665 
666 /**
667  * gda_tree_get_node:
668  * @tree: a #GdaTree object
669  * @tree_path: full path to the required nodes (if @use_names is %TRUE, then it must start with '/')
670  * @use_names: if %TRUE, then @tree_path will be interpreted as a unix style path, and if %FALSE,
671  *             then @tree_path will be interpreted similarly to the #GtkTreePath's string representation.
672  *
673  * Locates a #GdaTreeNode using the @tree_path path.
674  *
675  * Returns: (transfer none) (allow-none): the requested #GdaTreeNode pointer, or %NULL if not found
676  *
677  * Since: 4.2
678  */
679 GdaTreeNode *
gda_tree_get_node(GdaTree * tree,const gchar * tree_path,gboolean use_names)680 gda_tree_get_node (GdaTree *tree, const gchar *tree_path, gboolean use_names)
681 {
682 	GSList *segments;
683 	GdaTreeNode *node = NULL;
684 
685 	g_return_val_if_fail (GDA_IS_TREE (tree), NULL);
686 
687 	segments = decompose_path_as_segments (tree_path, use_names);
688 	if (!segments)
689 		return NULL;
690 
691 	g_assert (real_gda_tree_get_nodes_in_path (tree, segments, use_names, &node) == NULL);
692 
693 	if (segments) {
694 		g_slist_foreach (segments, (GFunc) g_free, NULL);
695 		g_slist_free (segments);
696 	}
697 
698 	return node;
699 }
700 
701 /**
702  * gda_tree_get_node_manager:
703  * @tree: a #GdaTree
704  * @node: a #GdaTreeNode present in @tree
705  *
706  * Get the #GdaTreeManager which created @node in @tree
707  *
708  * Returns: (transfer none): the #GdaTreeManager, or %NULL if @node is not present in @tree
709  *
710  * Since: 4.2
711  */
712 GdaTreeManager *
gda_tree_get_node_manager(GdaTree * tree,GdaTreeNode * node)713 gda_tree_get_node_manager (GdaTree *tree, GdaTreeNode *node)
714 {
715 	GdaTreeNode *parent;
716 	g_return_val_if_fail (GDA_IS_TREE (tree), NULL);
717 	g_return_val_if_fail (GDA_IS_TREE_NODE (node), NULL);
718 
719 	parent = gda_tree_node_get_parent (node);
720 	return _gda_tree_node_get_manager_for_child (parent ? parent : tree->priv->root, node);
721 }
722 
723 static gboolean
create_or_update_children(GSList * mgrlist,GdaTreeNode * parent,gboolean disable_recurs,GError ** error)724 create_or_update_children (GSList *mgrlist, GdaTreeNode *parent, gboolean disable_recurs, GError **error)
725 {
726 	GSList *list;
727 	for (list = mgrlist; list; list = list->next) {
728 		GdaTreeManager *manager = GDA_TREE_MANAGER (list->data);
729 		gboolean recurs = FALSE;
730 		if (disable_recurs) {
731 			g_object_get (G_OBJECT (manager), "recursive", &recurs, NULL);
732 			if (recurs)
733 				g_object_set (G_OBJECT (manager), "recursive", FALSE, NULL);
734 		}
735 
736 		gboolean has_error = FALSE;
737 		_gda_tree_manager_update_children (manager, parent,
738 						   _gda_tree_node_get_children_for_manager (parent,
739 											    manager),
740 						   &has_error, error);
741 		if (has_error)
742 			return FALSE;
743 		if (disable_recurs && recurs)
744 			g_object_set (G_OBJECT (manager), "recursive", TRUE, NULL);
745 	}
746 	return TRUE;
747 }
748 
749 static GSList *split_absolute_path (const gchar *path, gboolean *out_error);
750 static GSList *split_indexed_path (const gchar *path, gboolean *out_error);
751 
752 /**
753  * decompose_path_as_segments:
754  * @path: a path using '/'
755  * @use_names:
756  *
757  * Returns: a new list of allocated strings (one for each segment of @path), or %NULL
758  */
759 static GSList *
decompose_path_as_segments(const gchar * path,gboolean use_names)760 decompose_path_as_segments (const gchar *path, gboolean use_names)
761 {
762 	GSList *segments;
763 	gboolean path_error;
764 	if (!path)
765 		return NULL;
766 
767 	if (use_names) {
768 		if (*path != '/') {
769 			g_warning (_("Path format error: %s"), path);
770 			return NULL;
771 		}
772 		segments = split_absolute_path (path, &path_error);
773 	}
774 	else
775 		segments = split_indexed_path (path, &path_error);
776 	if (path_error) {
777 		g_warning (_("Path format error: %s"), path);
778 		return NULL;
779 	}
780 	return segments;
781 }
782 
783 
784 /*
785  * Splits @path into a list of path segments, avoiding empty ("") segments
786  * @path is expected to be a unix style path
787  * FIXME: check for errors
788  * Returns: a new list of allocated strings (one for each segment of @path)
789  */
790 static GSList *
split_absolute_path(const gchar * path,gboolean * out_error)791 split_absolute_path (const gchar *path, gboolean *out_error)
792 {
793 	GSList *list = NULL;
794 	gchar *copy;
795 	gchar *start, *end;
796 
797 	*out_error = FALSE;
798 	copy = g_strdup (path);
799 	start = copy;
800 	for (;;) {
801 		for (end = start; *end; end++) {
802 			if (*end == '/')
803 				break;
804 		}
805 		if (*end == '/') {
806 			*end = 0;
807 			if (start != end)
808 				list = g_slist_prepend (list, g_strdup (start));
809 			start = end + 1;
810 		}
811 		else {
812 			if (start != end)
813 				list = g_slist_prepend (list, g_strdup (start));
814 			break;
815 		}
816 	}
817 	g_free (copy);
818 
819 	list = g_slist_reverse (list);
820 
821 #ifdef GDA_DEBUG_NO
822 	GSList *l;
823 	for (l = list; l; l = l->next)
824 		g_print ("Part: #%s#\n", (gchar*) l->data);
825 #endif
826 
827 	return list;
828 }
829 
830 /*
831  * Splits @path into a list of path segments, avoiding empty ("") segments
832  * @path is expected to be a GtkTreePath's string representation path (ex: "3:2")
833  * Returns: a new list of allocated strings (one for each segment of @path)
834  */
835 static GSList *
split_indexed_path(const gchar * path,gboolean * out_error)836 split_indexed_path (const gchar *path, gboolean *out_error)
837 {
838 	GSList *list = NULL;
839 	gchar *copy;
840 	gchar *start, *end;
841 
842 	*out_error = FALSE;
843 	copy = g_strdup (path);
844 	start = copy;
845 	for (;;) {
846 		for (end = start; *end; end++) {
847 			if (*end == ':')
848 				break;
849 			if ((*end < '0') || (*end > '9')) {
850 				/* error */
851 				*out_error = TRUE;
852 				g_slist_foreach (list, (GFunc) g_free, NULL);
853 				g_slist_free (list);
854 				g_free (copy);
855 				return NULL;
856 			}
857 		}
858 		if (*end == ':') {
859 			*end = 0;
860 			if (start != end)
861 				list = g_slist_prepend (list, g_strdup (start));
862 			start = end + 1;
863 		}
864 		else {
865 			if (start != end)
866 				list = g_slist_prepend (list, g_strdup (start));
867 			break;
868 		}
869 	}
870 	g_free (copy);
871 
872 	list = g_slist_reverse (list);
873 
874 #ifdef GDA_DEBUG_NO
875 	GSList *l;
876 	g_print ("Splitting path '%s' into: ", path);
877 	for (l = list; l; l = l->next)
878 		g_print ("%s ", (gchar*) l->data);
879 	g_print ("\n");
880 #endif
881 
882 	return list;
883 }
884 
885 /**
886  * gda_tree_set_attribute:
887  * @tree: a #GdaTree object
888  * @attribute: attribute name
889  * @value: a #GValue, or %NULL
890  * @destroy: a function to be called when @attribute is not needed anymore, or %NULL
891  *
892  * Sets an attribute to @tree, which will be accessible to any node in it.
893  *
894  * Since: 4.2
895  */
896 void
gda_tree_set_attribute(GdaTree * tree,const gchar * attribute,const GValue * value,GDestroyNotify destroy)897 gda_tree_set_attribute (GdaTree *tree, const gchar *attribute, const GValue *value,
898 			GDestroyNotify destroy)
899 {
900 	g_return_if_fail (GDA_IS_TREE (tree));
901 	gda_tree_node_set_node_attribute (tree->priv->root, attribute, value, destroy);
902 }
903 
904 static void
take_root_node(GdaTree * tree,GdaTreeNode * root)905 take_root_node (GdaTree *tree, GdaTreeNode *root)
906 {
907 	if (tree->priv->root)
908 		unset_root_node (tree);
909 	tree->priv->root = root;
910 	g_signal_connect (tree->priv->root, "node-changed",
911 			  G_CALLBACK (node_changed_cb), tree);
912 	g_signal_connect (tree->priv->root, "node-inserted",
913 			  G_CALLBACK (node_inserted_cb), tree);
914 	g_signal_connect (tree->priv->root, "node-has-child-toggled",
915 			  G_CALLBACK (node_has_child_toggled_cb), tree);
916 	g_signal_connect (tree->priv->root, "node-deleted",
917 			  G_CALLBACK (node_deleted_cb), tree);
918 }
919 
920 static void
unset_root_node(GdaTree * tree)921 unset_root_node (GdaTree *tree)
922 {
923 	GSList *list;
924 	for (list = tree->priv->managers; list; list = list->next) {
925 		GdaTreeManager *manager = GDA_TREE_MANAGER (list->data);
926 
927 		_gda_tree_node_add_children (tree->priv->root, manager, NULL);
928 	}
929 
930 	g_signal_handlers_disconnect_by_func (tree->priv->root,
931 					      G_CALLBACK (node_changed_cb), tree);
932 	g_signal_handlers_disconnect_by_func (tree->priv->root,
933 					      G_CALLBACK (node_inserted_cb), tree);
934 	g_signal_handlers_disconnect_by_func (tree->priv->root,
935 					      G_CALLBACK (node_has_child_toggled_cb), tree);
936 	g_signal_handlers_disconnect_by_func (tree->priv->root,
937 					      G_CALLBACK (node_deleted_cb), tree);
938 
939 	g_object_unref (tree->priv->root);
940 }
941 
942 static void
node_changed_cb(GdaTreeNode * reporting,GdaTreeNode * node,GdaTree * tree)943 node_changed_cb (GdaTreeNode *reporting, GdaTreeNode *node, GdaTree *tree)
944 {
945 	if ((reporting != node) || (reporting != tree->priv->root))
946 		g_signal_emit (tree, gda_tree_signals [NODE_CHANGED], 0, node);
947 }
948 
949 static void
node_inserted_cb(GdaTreeNode * reporting,GdaTreeNode * node,GdaTree * tree)950 node_inserted_cb (GdaTreeNode *reporting, GdaTreeNode *node, GdaTree *tree)
951 {
952 	if ((reporting != node) || (reporting != tree->priv->root))
953 		g_signal_emit (tree, gda_tree_signals [NODE_INSERTED], 0, node);
954 }
955 
956 static void
node_has_child_toggled_cb(GdaTreeNode * reporting,GdaTreeNode * node,GdaTree * tree)957 node_has_child_toggled_cb (GdaTreeNode *reporting, GdaTreeNode *node, GdaTree *tree)
958 {
959 	if ((reporting != node) || (reporting != tree->priv->root))
960 		g_signal_emit (tree, gda_tree_signals [NODE_HAS_CHILD_TOGGLED], 0, node);
961 }
962 
963 static void
node_deleted_cb(G_GNUC_UNUSED GdaTreeNode * reporting,const gchar * relative_path,GdaTree * tree)964 node_deleted_cb (G_GNUC_UNUSED GdaTreeNode *reporting, const gchar *relative_path, GdaTree *tree)
965 {
966 	g_signal_emit (tree, gda_tree_signals [NODE_DELETED], 0, relative_path);
967 }
968