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 "gda-tree-manager.h"
23 #include "gda-tree-node.h"
24 #include "gda-value.h"
25 #include <string.h>
26 
27 typedef struct {
28 	gchar *att_name;
29 	GValue *value;
30 } AddedAttribute;
31 
32 struct _GdaTreeManagerPrivate {
33         GSList  *sub_managers; /* list of GdaTreeManager structures, no ref held */
34 	GSList  *ref_managers; /* list of GdaTreeManager structures, ref held */
35 	gboolean recursive;
36 
37 	GdaTreeManagerNodeFunc node_create_func;
38 	GdaTreeManagerNodesFunc update_func;
39 
40 	GSList *added_attributes; /* list of #AddedAttribute pointers, managed here */
41 };
42 
43 static void gda_tree_manager_class_init (GdaTreeManagerClass *klass);
44 static void gda_tree_manager_init       (GdaTreeManager *manager, GdaTreeManagerClass *klass);
45 static void gda_tree_manager_dispose    (GObject *object);
46 static void gda_tree_manager_set_property (GObject *object,
47 					   guint param_id,
48 					   const GValue *value,
49 					   GParamSpec *pspec);
50 static void gda_tree_manager_get_property (GObject *object,
51 					   guint param_id,
52 					   GValue *value,
53 					   GParamSpec *pspec);
54 
55 /* properties */
56 enum {
57 	PROP_0,
58 	PROP_RECURS,
59 	PROP_FUNC
60 };
61 
62 static GObjectClass *parent_class = NULL;
63 
64 /*
65  * GdaTreeManager class implementation
66  * @klass:
67  */
68 static void
gda_tree_manager_class_init(GdaTreeManagerClass * klass)69 gda_tree_manager_class_init (GdaTreeManagerClass *klass)
70 {
71 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
72 
73 	parent_class = g_type_class_peek_parent (klass);
74 
75 	/* virtual methods */
76 	klass->update_children = NULL;
77 
78 	/* Properties */
79         object_class->set_property = gda_tree_manager_set_property;
80         object_class->get_property = gda_tree_manager_get_property;
81 
82 	/**
83 	 * GdaTreeManager:recursive:
84 	 *
85 	 * This property specifies if, when initially creating nodes or updating the list of nodes,
86 	 * the tree manager shoud also request that each node it has created or updated also
87 	 * initially create or update their children.
88 	 *
89 	 * This property can typically set to FALSE if the process of creating children nodes is lenghty
90 	 * and needs to be postponed while an event occurs.
91 	 */
92 	g_object_class_install_property (object_class, PROP_RECURS,
93                                          g_param_spec_boolean ("recursive", NULL, "Recursive building/updating of children",
94                                                               TRUE,
95                                                               G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
96 /**
97 	 * GdaTreeManager:func:
98 	 *
99 	 * This property specifies the function which needs to be called when the list of #GdaTreeNode nodes
100 	 * managed has to be updated
101 	 */
102 	g_object_class_install_property (object_class, PROP_FUNC,
103                                          g_param_spec_pointer ("func", NULL, "Function called when building/updating of children",
104                                                               G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
105 	object_class->dispose = gda_tree_manager_dispose;
106 }
107 
108 static void
gda_tree_manager_init(GdaTreeManager * manager,G_GNUC_UNUSED GdaTreeManagerClass * klass)109 gda_tree_manager_init (GdaTreeManager *manager, G_GNUC_UNUSED GdaTreeManagerClass *klass)
110 {
111 	g_return_if_fail (GDA_IS_TREE_MANAGER (manager));
112 
113 	manager->priv = g_new0 (GdaTreeManagerPrivate, 1);
114 	manager->priv->sub_managers = NULL;
115 	manager->priv->ref_managers = NULL;
116 	manager->priv->added_attributes = NULL;
117 }
118 
119 static void
gda_tree_manager_dispose(GObject * object)120 gda_tree_manager_dispose (GObject *object)
121 {
122 	GdaTreeManager *manager = (GdaTreeManager *) object;
123 
124 	g_return_if_fail (GDA_IS_TREE_MANAGER (manager));
125 
126 	if (manager->priv) {
127 		if (manager->priv->sub_managers) {
128 			g_slist_free (manager->priv->sub_managers);
129 			manager->priv->sub_managers = NULL;
130 		}
131 		if (manager->priv->ref_managers) {
132 			g_slist_foreach (manager->priv->ref_managers, (GFunc) g_object_unref, NULL);
133 			g_slist_free (manager->priv->ref_managers);
134 		}
135 		if (manager->priv->added_attributes) {
136 			GSList *list;
137 			for (list = manager->priv->added_attributes; list; list = list->next) {
138 				AddedAttribute *aa = (AddedAttribute*) list->data;
139 				g_free (aa->att_name);
140 				if (aa->value)
141 					gda_value_free (aa->value);
142 				g_free (aa);
143 			}
144 			g_slist_free (manager->priv->added_attributes);
145 		}
146 		g_free (manager->priv);
147 		manager->priv = NULL;
148 	}
149 
150 	/* chain to parent class */
151 	parent_class->dispose (object);
152 }
153 
154 
155 /* module error */
gda_tree_manager_error_quark(void)156 GQuark gda_tree_manager_error_quark (void)
157 {
158         static GQuark quark;
159         if (!quark)
160                 quark = g_quark_from_static_string ("gda_tree_manager_error");
161         return quark;
162 }
163 
164 /**
165  * gda_tree_manager_get_type:
166  *
167  * Registers the #GdaTreeManager class on the GLib type system.
168  *
169  * Returns: the GType identifying the class.
170  *
171  * Since: 4.2
172  */
173 GType
gda_tree_manager_get_type(void)174 gda_tree_manager_get_type (void)
175 {
176         static GType type = 0;
177 
178         if (G_UNLIKELY (type == 0)) {
179                 static GMutex registering;
180                 static const GTypeInfo info = {
181                         sizeof (GdaTreeManagerClass),
182                         (GBaseInitFunc) NULL,
183                         (GBaseFinalizeFunc) NULL,
184                         (GClassInitFunc) gda_tree_manager_class_init,
185                         NULL,
186                         NULL,
187                         sizeof (GdaTreeManager),
188                         0,
189                         (GInstanceInitFunc) gda_tree_manager_init,
190 			0
191                 };
192 
193                 g_mutex_lock (&registering);
194                 if (type == 0)
195                         type = g_type_register_static (G_TYPE_OBJECT, "GdaTreeManager", &info, 0);
196                 g_mutex_unlock (&registering);
197         }
198         return type;
199 }
200 
201 static void
gda_tree_manager_set_property(GObject * object,guint param_id,const GValue * value,GParamSpec * pspec)202 gda_tree_manager_set_property (GObject *object,
203 			       guint param_id,
204 			       const GValue *value,
205 			       GParamSpec *pspec)
206 {
207 	GdaTreeManager *manager;
208 
209         manager = GDA_TREE_MANAGER (object);
210         if (manager->priv) {
211                 switch (param_id) {
212 		case PROP_RECURS:
213 			manager->priv->recursive = g_value_get_boolean (value);
214 			break;
215 		case PROP_FUNC:
216 			manager->priv->update_func = g_value_get_pointer (value);
217 			break;
218 		default:
219 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
220 			break;
221 		}
222 	}
223 }
224 
225 static void
gda_tree_manager_get_property(GObject * object,guint param_id,GValue * value,GParamSpec * pspec)226 gda_tree_manager_get_property (GObject *object,
227 			       guint param_id,
228 			       GValue *value,
229 			       GParamSpec *pspec)
230 {
231 	GdaTreeManager *manager;
232 
233 	manager = GDA_TREE_MANAGER (object);
234 	if (manager->priv) {
235 		switch (param_id) {
236 		case PROP_RECURS:
237 			g_value_set_boolean (value, manager->priv->recursive);
238 			break;
239 		case PROP_FUNC:
240 			g_value_set_pointer (value, manager->priv->update_func);
241 			break;
242 		default:
243 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
244 			break;
245 		}
246 	}
247 }
248 
249 /*
250  * @manager: a #GdaTreeManager object
251  * @node: (allow-none): a #GdaTreeNode object, or %NULL
252  * @children_nodes: a list of #GdaTreeNode nodes which have previously been created by a similar call and
253  * need to be updated ore moved
254  * @out_error: (allow-none): a boolean to store if there was an error (can be %NULL)
255  * @error: a place to store errors, or %NULL
256  *
257  * Creates (or updates) the list of #GdaTreeNode objects which are placed as children of @node. The returned
258  * list will completely replace the existing list of nodes managed by @manager (as children of @node).
259  *
260  * If a node is already present in @children_nodes and needs to be kept in the new list, then it should be added
261  * to the returned list and its reference count should be increased by one.
262  *
263  * Returns: a new list of #GdaTreeNode objects.
264  *
265  * Since: 4.2
266  */
267 void
_gda_tree_manager_update_children(GdaTreeManager * manager,GdaTreeNode * node,const GSList * children_nodes,gboolean * out_error,GError ** error)268 _gda_tree_manager_update_children (GdaTreeManager *manager, GdaTreeNode *node, const GSList *children_nodes,
269 				   gboolean *out_error, GError **error)
270 {
271 	GSList *nodes_list = NULL;
272 
273 	g_return_if_fail (GDA_IS_TREE_MANAGER (manager));
274 	g_return_if_fail (GDA_IS_TREE_NODE (node));
275 
276 	if (out_error)
277 		*out_error = FALSE;
278 	if (manager->priv->update_func)
279 		nodes_list = manager->priv->update_func (manager, node, children_nodes, out_error, error);
280 	else {
281 		GdaTreeManagerClass *klass;
282 		klass = (GdaTreeManagerClass*) G_OBJECT_GET_CLASS (manager);
283 		if (klass->update_children)
284 			nodes_list = klass->update_children (manager, node, children_nodes, out_error, error);
285 	}
286 	_gda_tree_node_add_children (node, manager, nodes_list);
287 
288 	/* calling sub managers for each new node */
289 	GSList *list;
290 	for (list = nodes_list; list; list = list->next) {
291 		GSList *sl;
292 		for (sl = manager->priv->sub_managers; sl; sl = sl->next) {
293 			if (manager->priv->recursive) {
294 				gboolean lout_error = FALSE;
295 				GdaTreeNode *parent = GDA_TREE_NODE (list->data);
296 				GdaTreeManager *mgr = (GdaTreeManager *) sl->data;
297 				_gda_tree_manager_update_children (mgr, parent,
298 								   _gda_tree_node_get_children_for_manager (parent, mgr),
299 								   &lout_error, error);
300 				if (lout_error) {
301 					if (out_error)
302 						*out_error = TRUE;
303 					return;
304 				}
305 			}
306 			else
307 				_gda_tree_node_add_children (GDA_TREE_NODE (list->data), (GdaTreeManager *) sl->data, NULL);
308 		}
309 	}
310 	if (nodes_list)
311 		g_slist_free (nodes_list);
312 }
313 
314 
315 /**
316  * gda_tree_manager_new_with_func:
317  * @update_func: (scope call): the function to call when the manager object is requested to create or update its list of
318  * #GdaTreeNode nodes
319  *
320  * Use this method to create a new #GdaTreeManager if it's more convenient than subclassing; all is needed
321  * is the @update_func function which is responsible for creating or updating the children nodes of a specified #GdaTreeNode.
322  *
323  * Returns: (transfer full): a new #GdaTreeManager
324  *
325  * Since: 4.2
326  */
327 GdaTreeManager *
gda_tree_manager_new_with_func(GdaTreeManagerNodesFunc update_func)328 gda_tree_manager_new_with_func (GdaTreeManagerNodesFunc update_func)
329 {
330 	g_return_val_if_fail (update_func, NULL);
331 
332 	return (GdaTreeManager*) g_object_new (GDA_TYPE_TREE_MANAGER, "func", update_func, NULL);
333 }
334 
335 /**
336  * gda_tree_manager_add_new_node_attribute:
337  * @manager: a #GdaTreeManager
338  * @attribute: an attribute name
339  * @value: (allow-none): the attribute's value, or %NULL
340  *
341  * Requests that for any new node managed (eg. created) by @manager, a new attribute will be set. This allows
342  * one to customize the attributes of new nodes created by an existing #GdaTreeManager.
343  *
344  * As a side effect, if @value is %NULL, then the corresponding attribute, if it was set, is unset.
345  *
346  * Since: 4.2
347  */
348 void
gda_tree_manager_add_new_node_attribute(GdaTreeManager * manager,const gchar * attribute,const GValue * value)349 gda_tree_manager_add_new_node_attribute (GdaTreeManager *manager, const gchar *attribute, const GValue *value)
350 {
351 	AddedAttribute *aa = NULL;
352 	GSList *list;
353 	g_return_if_fail (GDA_IS_TREE_MANAGER (manager));
354 	g_return_if_fail (attribute && *attribute);
355 
356 	for (list = manager->priv->added_attributes; list; list = list->next) {
357 		if (!strcmp (((AddedAttribute *) list->data)->att_name, attribute)) {
358 			aa = (AddedAttribute *) list->data;
359 			break;
360 		}
361 	}
362 	if (!aa) {
363 		aa = g_new0 (AddedAttribute, 1);
364 		aa->att_name = g_strdup (attribute);
365 		manager->priv->added_attributes = g_slist_append (manager->priv->added_attributes, aa);
366 	}
367 	else if (aa->value) {
368 		gda_value_free (aa->value);
369 		aa->value = NULL;
370 	}
371 	if (value)
372 		aa->value = gda_value_copy (value);
373 
374 }
375 
376 /**
377  * gda_tree_manager_create_node:
378  * @manager: a #GdaTreeManager
379  * @parent: (allow-none): the parent the new node may have, or %NULL
380  * @name: (allow-none): name given to the new node, or %NULL
381  *
382  * Requests that @manager creates a new #GdaTreeNode. The new node is not in any
383  * way linked to @manager yet, consider this method as a #GdaTreeNode factory.
384  *
385  * This method is usually used when implementing a #GdaTreeManagerNodesFunc function (to create nodes),
386  * or when subclassing the #GdaTreeManager.
387  *
388  * Returns: (transfer full): a new #GdaTreeNode
389  *
390  * Since: 4.2
391  */
392 GdaTreeNode *
gda_tree_manager_create_node(GdaTreeManager * manager,GdaTreeNode * parent,const gchar * name)393 gda_tree_manager_create_node (GdaTreeManager *manager, GdaTreeNode *parent, const gchar *name)
394 {
395 	GdaTreeNode *node;
396 	GSList *list;
397 	g_return_val_if_fail (GDA_IS_TREE_MANAGER (manager), NULL);
398 	if (manager->priv->node_create_func)
399 		node = manager->priv->node_create_func (manager, parent, name);
400 	else
401 		node = gda_tree_node_new (name);
402 
403 	for (list = manager->priv->added_attributes; list; list = list->next) {
404 		AddedAttribute *aa = (AddedAttribute*) list->data;
405 		gda_tree_node_set_node_attribute (node, aa->att_name, aa->value, NULL);
406 	}
407 
408 	return node;
409 }
410 
411 static gboolean
manager_is_sub_manager_of(GdaTreeManager * mgr,GdaTreeManager * sub)412 manager_is_sub_manager_of (GdaTreeManager *mgr, GdaTreeManager *sub)
413 {
414 	if (g_slist_find (mgr->priv->ref_managers, sub))
415 		return TRUE;
416 	GSList *list;
417 	for (list = mgr->priv->sub_managers; list; list = list->next) {
418 		if (manager_is_sub_manager_of (GDA_TREE_MANAGER (list->data), sub))
419 			return TRUE;
420 	}
421 	return FALSE;
422 }
423 
424 /**
425  * gda_tree_manager_add_manager:
426  * @manager: a #GdaTreeManager object
427  * @sub: a #GdaTreeManager object to add
428  *
429  * Adds a sub manager to @manager. Use this method to create the skeleton structure
430  * of a #GdaTree. Note that a single #GdaTreeManager can be used by several #GdaTree objects
431  * or several times in the same #GdaTree's structure.
432  *
433  * Please note that it's possible for @mgr and @sub to be the same object, but beware of the possible
434  * infinite recursive behaviour in this case when creating children nodes
435  * (depending on the actual implementation of the #GdaTreeManager).
436  *
437  * Since: 4.2
438  */
439 void
gda_tree_manager_add_manager(GdaTreeManager * manager,GdaTreeManager * sub)440 gda_tree_manager_add_manager (GdaTreeManager *manager, GdaTreeManager *sub)
441 {
442 	g_return_if_fail (GDA_IS_TREE_MANAGER (manager));
443 	g_return_if_fail (GDA_IS_TREE_MANAGER (sub));
444 
445 	manager->priv->sub_managers = g_slist_append (manager->priv->sub_managers, sub);
446 
447 	/* determine if @sub should be ref'ed or not to avoid circular dependencies */
448 	if ((sub != manager) && !manager_is_sub_manager_of (sub, manager)) {
449 		manager->priv->ref_managers = g_slist_prepend (manager->priv->ref_managers, sub);
450 		g_object_ref (sub);
451 	}
452 }
453 
454 
455 /**
456  * gda_tree_manager_get_managers:
457  * @manager: a #GdaTreeManager object
458  *
459  * Get the list of sub managers which have already been added using gda_tree_manager_add_manager()
460  *
461  * Returns: (transfer none) (element-type Gda.TreeManager): a list of #GdaTreeMenager which should not be modified.
462  *
463  * Since: 4.2
464  */
465 const GSList *
gda_tree_manager_get_managers(GdaTreeManager * manager)466 gda_tree_manager_get_managers (GdaTreeManager *manager)
467 {
468 	g_return_val_if_fail (GDA_IS_TREE_MANAGER (manager), NULL);
469 	return manager->priv->sub_managers;
470 }
471 
472 /**
473  * gda_tree_manager_set_node_create_func:
474  * @manager: a #GdaTreeManager tree manager object
475  * @func: (allow-none) (scope call): a #GdaTreeManagerNodeFunc function pointer, or %NULL
476  *
477  * Sets the function to be called when a new node is being created by @manager. If @func is %NULL
478  * then each created node will be a #GdaTreeNode object.
479  *
480  * Specifying a custom #GdaTreeManagerNodeFunc function for example allows one to use
481  * specialized sub-classed #GdaTreeNode objects.
482  *
483  * Since: 4.2
484  */
485 void
gda_tree_manager_set_node_create_func(GdaTreeManager * manager,GdaTreeManagerNodeFunc func)486 gda_tree_manager_set_node_create_func (GdaTreeManager *manager, GdaTreeManagerNodeFunc func)
487 {
488 	g_return_if_fail (GDA_IS_TREE_MANAGER (manager));
489 	manager->priv->node_create_func = func;
490 }
491 
492 /**
493  * gda_tree_manager_get_node_create_func: (skip)
494  * @manager: a #GdaTreeManager tree manager object
495  *
496  * Get the function used by @manager when creating new #GdaTreeNode nodes
497  *
498  * Returns: the #GdaTreeManagerNodeFunc function, or %NULL if the default function is used
499  *
500  * Since: 4.2
501  */
502 GdaTreeManagerNodeFunc
gda_tree_manager_get_node_create_func(GdaTreeManager * manager)503 gda_tree_manager_get_node_create_func (GdaTreeManager *manager)
504 {
505 	g_return_val_if_fail (GDA_IS_TREE_MANAGER (manager), NULL);
506 	return manager->priv->node_create_func;
507 }
508