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 (®istering);
194 if (type == 0)
195 type = g_type_register_static (G_TYPE_OBJECT, "GdaTreeManager", &info, 0);
196 g_mutex_unlock (®istering);
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