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 (®istering);
254 if (type == 0)
255 type = g_type_register_static (G_TYPE_OBJECT, "GdaTree", &info, 0);
256 g_mutex_unlock (®istering);
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