1 /*
2 * libInstPatch
3 * Copyright (C) 1999-2014 Element Green <element@elementsofsound.org>
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public License
7 * as published by the Free Software Foundation; version 2.1
8 * of the License only.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 * 02110-1301, USA or on the web at http://www.gnu.org.
19 */
20 /**
21 * SECTION: IpatchItem
22 * @short_description: Abstract base item object
23 * @see_also:
24 * @stability: Stable
25 *
26 * The abstract base item type from which all instrument objects are derived
27 * and many other object types as well.
28 */
29 #include <stdarg.h>
30 #include <glib.h>
31 #include <glib-object.h>
32
33 #include "IpatchItem.h"
34 #include "IpatchIter.h"
35 #include "IpatchContainer.h"
36 #include "IpatchBase.h" /* IPATCH_BASE_CHANGED flag */
37 #include "IpatchParamProp.h"
38 #include "IpatchTypeProp.h"
39 #include "ipatch_priv.h"
40 #include "i18n.h"
41 #include "util.h"
42
43 enum
44 {
45 PROP_0,
46 PROP_FLAGS,
47 PROP_PARENT,
48 PROP_BASE,
49 PROP_TITLE
50 };
51
52 /* for unique property caching */
53 typedef struct
54 {
55 GParamSpec **pspecs; /* unique prop paramspecs (groups are consecutive) */
56 guint32 groups; /* 1 bit per pspec, pspecs of same group are the same */
57 } UniqueBag;
58
59 /* a private bag structure for ipatch_item_set_parent */
60 typedef struct
61 {
62 IpatchItem *base;
63 int hooks_active;
64 } SetParentBag;
65
66 /* defined in IpatchItemProp.c */
67 extern void _ipatch_item_prop_init(void);
68 extern void _ipatch_item_prop_deinit(void);
69
70 /* defined in IpatchBase.c */
71 extern GParamSpec *ipatch_base_pspec_changed;
72
73 static gboolean
74 _ipatch_item_hash_free_value(gpointer key, UniqueBag *value, gpointer user_data);
75 static void ipatch_item_base_init(IpatchItemClass *klass);
76 static void ipatch_item_class_init(IpatchItemClass *klass);
77 static void ipatch_item_set_property(GObject *object, guint property_id,
78 const GValue *value, GParamSpec *pspec);
79 static void ipatch_item_get_property(GObject *object, guint property_id,
80 GValue *value, GParamSpec *pspec);
81 static void ipatch_item_set_property_override(GObject *object,
82 guint property_id,
83 const GValue *value,
84 GParamSpec *pspec);
85 static void ipatch_item_init(IpatchItem *item);
86 static void ipatch_item_finalize(GObject *object);
87 static void ipatch_item_item_remove_full(IpatchItem *item, gboolean full);
88 static void ipatch_item_recursive_base_set(IpatchContainer *container,
89 SetParentBag *bag);
90 static void ipatch_item_recursive_base_unset(IpatchContainer *container);
91 static void ipatch_item_real_remove_full(IpatchItem *item, gboolean full);
92 static void ipatch_item_real_remove_recursive(IpatchItem *item, gboolean full);
93 static void copy_hash_to_list_GHFunc(gpointer key, gpointer value,
94 gpointer user_data);
95 static UniqueBag *item_lookup_unique_bag(GType type);
96 static gint unique_group_list_sort_func(gconstpointer a, gconstpointer b);
97
98
99 static GObjectClass *parent_class = NULL;
100
101 /* item class for fast hook function method calls */
102 static IpatchItemClass *real_item_class = NULL;
103
104 /* define the lock for the unique property cache hash */
105 G_LOCK_DEFINE_STATIC(unique_prop_cache);
106
107 /* cache of IpatchItem unique properties (hash: GType => UniqueBag) */
108 static GHashTable *unique_prop_cache = NULL;
109
110 /* store title property GParamSpec as a convenience to derived types */
111 /* Useful when libinstpatch library is used as a static library. */
112 GParamSpec *ipatch_item_pspec_title = NULL;
113
114 /* ----- Initialization/deinitialization of property change callback system -*/
115 /* Initialization */
_ipatch_item_init(void)116 void _ipatch_item_init(void)
117 {
118 /* cache of IpatchItem unique properties (hash: GType => UniqueBag) */
119 unique_prop_cache = g_hash_table_new (NULL, NULL);
120
121 /* Initialize property change callback system */
122 _ipatch_item_prop_init();
123 }
124
125 /* Free property change callback system */
_ipatch_item_deinit(void)126 void _ipatch_item_deinit(void)
127 {
128 /* free property change callback system */
129 _ipatch_item_prop_deinit();
130 g_hash_table_foreach_remove(unique_prop_cache,
131 (GHRFunc)_ipatch_item_hash_free_value,
132 NULL);
133 g_hash_table_destroy(unique_prop_cache);
134 }
135
136 /* free hash value entry */
137 static gboolean
_ipatch_item_hash_free_value(gpointer key,UniqueBag * value,gpointer user_data)138 _ipatch_item_hash_free_value(gpointer key, UniqueBag *value, gpointer user_data)
139 {
140 g_free(value->pspecs);
141 g_free(value);
142 return TRUE;
143 }
144
145 /* ----- IpatchItem object functions ---------------------------------------*/
146
147 /*
148 Getter function returning title property GParamSpec as a convenience to derived type.
149 Useful when libinstpatch library is used as a shared library linked at load time.
150 */
151 GParamSpec *
ipatch_item_get_pspec_title(void)152 ipatch_item_get_pspec_title(void)
153 {
154 return ipatch_item_pspec_title;
155 }
156
157 GType
ipatch_item_get_type(void)158 ipatch_item_get_type(void)
159 {
160 static GType item_type = 0;
161
162 if(!item_type)
163 {
164 static const GTypeInfo item_info =
165 {
166 sizeof(IpatchItemClass),
167 (GBaseInitFunc) ipatch_item_base_init, NULL,
168 (GClassInitFunc) ipatch_item_class_init, NULL, NULL,
169 sizeof(IpatchItem), 0,
170 (GInstanceInitFunc)ipatch_item_init,
171 };
172
173 item_type = g_type_register_static(G_TYPE_OBJECT, "IpatchItem",
174 &item_info, G_TYPE_FLAG_ABSTRACT);
175 }
176
177 return (item_type);
178 }
179
180 static void
ipatch_item_base_init(IpatchItemClass * klass)181 ipatch_item_base_init(IpatchItemClass *klass)
182 {
183 GObjectClass *obj_class = G_OBJECT_CLASS(klass);
184
185 /* override the set property method for all derived IpatchItem types */
186 obj_class->set_property = ipatch_item_set_property_override;
187 }
188
189 /* set_property override method that calls item_set_property method, derived
190 * classes should use the item_set_property method instead of set_property. */
191 static void
ipatch_item_set_property_override(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)192 ipatch_item_set_property_override(GObject *object, guint property_id,
193 const GValue *value, GParamSpec *pspec)
194 {
195 IpatchItemClass *klass;
196 GObjectClass *obj_class;
197 gboolean hooks_active;
198 GValue oldval = { 0 };
199 GType type, prev_type;
200
201 /* Get the class for the owning type of this parameter.
202 * NOTE: This wont work for interfaces (handled below) or overridden
203 * properties. FIXME - Not sure how to handle overridden properties. */
204 klass = g_type_class_peek(pspec->owner_type);
205
206 /* property belongs to an interface? */
207 if(!klass && G_TYPE_IS_INTERFACE(pspec->owner_type))
208 {
209 /* Unfortunately GObject doesn't give us an easy way to determine which
210 * class implements an interface in an object's derived ancestry, so we're
211 * left with this hack.. */
212
213 prev_type = G_OBJECT_TYPE(object);
214 type = prev_type;
215
216 /* find type in object's ancestry that implements the interface by searching
217 * for the type just before the first parent which doesn't conform to the
218 * interface. */
219 while((type = g_type_parent(type)))
220 {
221 if(!g_type_is_a(type, pspec->owner_type))
222 {
223 break;
224 }
225
226 prev_type = type;
227 }
228
229 if(prev_type)
230 {
231 klass = g_type_class_peek(prev_type);
232 }
233 }
234
235 g_return_if_fail(klass != NULL);
236 g_return_if_fail(klass->item_set_property != NULL);
237
238 /* hook functions can be inactive for greater performance */
239 hooks_active = (ipatch_item_get_flags(object) & IPATCH_ITEM_HOOKS_ACTIVE) != 0;
240
241 /* fetch parameter's current value to use as oldval in property notify */
242 if(hooks_active)
243 {
244 obj_class = (GObjectClass *)klass;
245 g_return_if_fail(obj_class->get_property != NULL);
246
247 g_value_init(&oldval, G_PARAM_SPEC_VALUE_TYPE(pspec));
248 obj_class->get_property(object, property_id, &oldval, pspec);
249 }
250
251 klass->item_set_property(object, property_id, value, pspec);
252
253 /* call property change */
254 if(hooks_active)
255 {
256 ipatch_item_prop_notify((IpatchItem *)object, pspec, value, &oldval);
257 g_value_unset(&oldval);
258 }
259 }
260
261 static void
ipatch_item_class_init(IpatchItemClass * klass)262 ipatch_item_class_init(IpatchItemClass *klass)
263 {
264 GObjectClass *obj_class = G_OBJECT_CLASS(klass);
265
266 real_item_class = klass;
267 parent_class = g_type_class_peek_parent(klass);
268
269 klass->item_set_property = ipatch_item_set_property;
270
271 obj_class->get_property = ipatch_item_get_property;
272 obj_class->finalize = ipatch_item_finalize;
273
274 klass->remove_full = ipatch_item_item_remove_full;
275
276 g_object_class_install_property(obj_class, PROP_FLAGS,
277 g_param_spec_uint("flags", _("Flags"), _("Flags"),
278 0, G_MAXUINT, 0,
279 G_PARAM_READWRITE | IPATCH_PARAM_HIDE
280 | IPATCH_PARAM_NO_SAVE_CHANGE
281 | IPATCH_PARAM_NO_SAVE));
282 g_object_class_install_property(obj_class, PROP_PARENT,
283 g_param_spec_object("parent", _("Parent"),
284 _("Parent"),
285 IPATCH_TYPE_ITEM,
286 G_PARAM_READWRITE
287 | IPATCH_PARAM_NO_SAVE));
288 g_object_class_install_property(obj_class, PROP_BASE,
289 g_param_spec_object("base", _("Base"), _("Base"),
290 IPATCH_TYPE_BASE,
291 G_PARAM_READABLE
292 | IPATCH_PARAM_NO_SAVE));
293 ipatch_item_pspec_title
294 = g_param_spec_string("title", _("Title"), _("Title"),
295 NULL, G_PARAM_READABLE | IPATCH_PARAM_NO_SAVE_CHANGE
296 | IPATCH_PARAM_NO_SAVE);
297 g_object_class_install_property(obj_class, PROP_TITLE,
298 ipatch_item_pspec_title);
299 }
300
301 static void
ipatch_item_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)302 ipatch_item_set_property(GObject *object, guint property_id,
303 const GValue *value, GParamSpec *pspec)
304 {
305 IpatchItem *item = IPATCH_ITEM(object);
306
307 switch(property_id)
308 {
309 case PROP_FLAGS:
310 ipatch_item_set_flags(item, g_value_get_uint(value));
311 break;
312
313 case PROP_PARENT:
314 ipatch_item_set_parent(item, IPATCH_ITEM(g_value_get_object(value)));
315 break;
316
317 default:
318 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
319 return;
320 }
321 }
322
323 static void
ipatch_item_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)324 ipatch_item_get_property(GObject *object, guint property_id,
325 GValue *value, GParamSpec *pspec)
326 {
327 IpatchItem *item = IPATCH_ITEM(object);
328 char *name;
329
330 switch(property_id)
331 {
332 case PROP_FLAGS:
333 g_value_set_uint(value, ipatch_item_get_flags(item));
334 break;
335
336 case PROP_PARENT:
337 g_value_take_object(value, ipatch_item_get_parent(item));
338 break;
339
340 case PROP_BASE:
341 g_value_take_object(value, ipatch_item_get_base(item));
342 break;
343
344 case PROP_TITLE:
345 /* see if the "name" type property is set */
346 ipatch_type_object_get((GObject *)item, "name", &name, NULL);
347
348 if(name)
349 {
350 g_value_take_string(value, name);
351 }
352 else
353 {
354 g_value_set_string(value, IPATCH_UNTITLED); /* "untitled" */
355 }
356
357 break;
358
359 default:
360 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
361 return;
362 }
363 }
364
365 static void
ipatch_item_init(IpatchItem * item)366 ipatch_item_init(IpatchItem *item)
367 {
368 /* always assign a mutex, will be freed and set to parent's mutex if the
369 class has mutex_slave set (during ipatch_item_set_parent) */
370 item->mutex = g_malloc(sizeof(GStaticRecMutex));
371 g_static_rec_mutex_init(item->mutex);
372
373 ipatch_item_set_flags(item, IPATCH_ITEM_FREE_MUTEX);
374 }
375
376 static void
ipatch_item_finalize(GObject * object)377 ipatch_item_finalize(GObject *object)
378 {
379 IpatchItem *item = IPATCH_ITEM(object);
380
381 if(ipatch_item_get_flags(item) & IPATCH_ITEM_FREE_MUTEX)
382 {
383 g_static_rec_mutex_free(item->mutex);
384 g_free(item->mutex);
385 }
386
387 item->mutex = NULL;
388
389 if(parent_class->finalize)
390 {
391 parent_class->finalize(object);
392 }
393 }
394
395 static void
ipatch_item_item_remove_full(IpatchItem * item,gboolean full)396 ipatch_item_item_remove_full(IpatchItem *item, gboolean full)
397 {
398 IpatchItem *parent;
399
400 parent = ipatch_item_get_parent(item); // ++ ref parent
401
402 if(parent)
403 {
404 ipatch_container_remove(IPATCH_CONTAINER(parent), item);
405 g_object_unref(parent); // -- unref parent
406 }
407 }
408
409 /**
410 * ipatch_item_get_flags:
411 * @item: (type Ipatch.Item): #IpatchItem to get flag field from
412 *
413 * Get the value of the flags field in a patch item.
414 *
415 * Returns: Value of flags field (some of which is user definable).
416 */
417 int
ipatch_item_get_flags(gpointer item)418 ipatch_item_get_flags(gpointer item)
419 {
420 g_return_val_if_fail(IPATCH_IS_ITEM(item), 0);
421
422 return (item ? g_atomic_int_get(&((IpatchItem *)item)->flags) : 0);
423 }
424
425 /**
426 * ipatch_item_set_flags:
427 * @item: (type Ipatch.Item): #IpatchItem to set flags in
428 * @flags: Flags to set
429 *
430 * Set flags in a patch item. All bits that are set in @flags are set in
431 * the @item flags field.
432 */
433 void
ipatch_item_set_flags(gpointer item,int flags)434 ipatch_item_set_flags(gpointer item, int flags)
435 {
436 int oldval, newval;
437
438 g_return_if_fail(IPATCH_IS_ITEM(item));
439
440 do
441 {
442 oldval = g_atomic_int_get(&((IpatchItem *)item)->flags);
443 newval = oldval | flags;
444 }
445 while(!g_atomic_int_compare_and_exchange(&((IpatchItem *)item)->flags,
446 oldval, newval));
447 }
448
449 /**
450 * ipatch_item_clear_flags:
451 * @item: (type Ipatch.Item): #IpatchItem object to clear flags in
452 * @flags: Flags to clear
453 *
454 * Clear (unset) flags in a patch item. All bits that are set in @flags are
455 * cleared in the @item flags field.
456 */
457 void
ipatch_item_clear_flags(gpointer item,int flags)458 ipatch_item_clear_flags(gpointer item, int flags)
459 {
460 int oldval, newval;
461
462 g_return_if_fail(IPATCH_IS_ITEM(item));
463
464 do
465 {
466 oldval = g_atomic_int_get(&((IpatchItem *)item)->flags);
467 newval = oldval & ~flags;
468 }
469 while(!g_atomic_int_compare_and_exchange(&((IpatchItem *)item)->flags,
470 oldval, newval));
471 }
472
473 /**
474 * ipatch_item_set_parent:
475 * @item: Item to set parent of (should not have a parent).
476 * @parent: New parent of item.
477 *
478 * Usually only used by #IpatchItem type implementations. Sets the
479 * parent of a patch item. Also recursively sets base parent and
480 * #IPATCH_ITEM_HOOKS_ACTIVE flag if set in @parent. Also assigns the item's
481 * thread mutex to that of the parent if its class has mutex_slave set.
482 * The @parent container is responsible for adding a reference to @item - this
483 * function does not do so.
484 *
485 * NOTE: If the @item has mutex_slave set in its class then the item will use
486 * @parent object's mutex. During the call to this function @item should not
487 * be accessed by any other threads, otherwise problems may occur when the
488 * mutex is changed. Normally this shouldn't be much of an issue, since new
489 * item's are often only accessed by 1 thread until being parented.
490 */
491 void
ipatch_item_set_parent(IpatchItem * item,IpatchItem * parent)492 ipatch_item_set_parent(IpatchItem *item, IpatchItem *parent)
493 {
494 IpatchItemClass *klass;
495 gboolean is_container;
496 SetParentBag bag;
497 guint depth;
498 guint i;
499
500 g_return_if_fail(IPATCH_IS_ITEM(item));
501 g_return_if_fail(IPATCH_IS_ITEM(parent));
502 g_return_if_fail(item != parent);
503
504 bag.base = ipatch_item_get_base(parent); /* ++ ref base patch item */
505 is_container = IPATCH_IS_CONTAINER(item);
506
507 /* get value of parent's hook flag */
508 bag.hooks_active = ipatch_item_get_flags(parent) & IPATCH_ITEM_HOOKS_ACTIVE;
509
510 IPATCH_ITEM_WLOCK(item);
511
512 if(log_if_fail(item->parent == NULL))
513 {
514 IPATCH_ITEM_WUNLOCK(item);
515
516 if(bag.base)
517 {
518 g_object_unref(bag.base);
519 }
520
521 return;
522 }
523
524 klass = IPATCH_ITEM_GET_CLASS(item);
525
526 if(klass->mutex_slave)
527 {
528 depth = g_static_rec_mutex_unlock_full(item->mutex);
529
530 if(ipatch_item_get_flags(item) & IPATCH_ITEM_FREE_MUTEX)
531 {
532 g_static_rec_mutex_free(item->mutex);
533 g_free(item->mutex);
534 }
535
536 item->mutex = parent->mutex;
537 ipatch_item_clear_flags(item, IPATCH_ITEM_FREE_MUTEX);
538
539 /* lock it the number of times old mutex was locked, we don't use
540 g_static_rec_mutex_lock_full because it could set depth rather than
541 add to it */
542 for(i = 0; i < depth; i++)
543 {
544 g_static_rec_mutex_lock(item->mutex);
545 }
546 }
547
548 item->parent = parent;
549
550 if(bag.base)
551 {
552 item->base = bag.base; /* inherit base parent item if set */
553 }
554
555 /* inherit active hooks flag, has no effect if not set */
556 ipatch_item_set_flags(item, bag.hooks_active);
557
558 /* if item is a container and base or hooks active flag is set, recursively
559 set children hooks active flags */
560 if(is_container && (bag.base || bag.hooks_active))
561 {
562 ipatch_item_recursive_base_set((IpatchContainer *)item, &bag);
563 }
564
565 IPATCH_ITEM_WUNLOCK(item);
566
567 if(bag.base)
568 {
569 g_object_unref(bag.base); /* -- unref base patch item */
570 }
571 }
572
573 /* recursively sets base field of a tree of items, expects container to
574 be write locked */
575 static void
ipatch_item_recursive_base_set(IpatchContainer * container,SetParentBag * bag)576 ipatch_item_recursive_base_set(IpatchContainer *container, SetParentBag *bag)
577 {
578 IpatchIter iter;
579 GObject *child;
580 const GType *types;
581
582 /* get container child list types */
583 types = ipatch_container_get_child_types(container);
584
585 while(*types) /* loop over list types */
586 {
587 /* initialize iterator to child list */
588 if(!ipatch_container_init_iter(container, &iter, *types))
589 {
590 return;
591 }
592
593 child = ipatch_iter_first(&iter);
594
595 while(child) /* loop over child list */
596 {
597 IPATCH_ITEM_WLOCK(child);
598
599 if(bag->base) /* inherit base pointer if set */
600 {
601 ((IpatchItem *)child)->base = bag->base;
602 }
603
604 /* inherit active hooks flag, has no effect if not set */
605 ipatch_item_set_flags(child, bag->hooks_active);
606
607 if(IPATCH_IS_CONTAINER(child)) /* recurse if a container */
608 {
609 ipatch_item_recursive_base_set((IpatchContainer *)child, bag);
610 }
611
612 IPATCH_ITEM_WUNLOCK(child);
613
614 child = ipatch_iter_next(&iter);
615 } /* child loop */
616
617 types++;
618 } /* child type loop */
619 }
620
621 /**
622 * ipatch_item_unparent:
623 * @item: Item to unparent
624 *
625 * Usually only used by #IpatchItem type implementations. Unparent an
626 * item. Also recursively unsets base parent and
627 * #IPATCH_ITEM_HOOKS_ACTIVE flag. Parent container object is
628 * responsible for removing reference of @item - this function does
629 * not do so.
630 */
631 void
ipatch_item_unparent(IpatchItem * item)632 ipatch_item_unparent(IpatchItem *item)
633 {
634 gboolean is_container;
635
636 g_return_if_fail(IPATCH_IS_ITEM(item));
637
638 is_container = IPATCH_IS_CONTAINER(item);
639
640 IPATCH_ITEM_WLOCK(item);
641
642 if(!item->parent)
643 {
644 IPATCH_ITEM_WUNLOCK(item);
645 return;
646 }
647
648 /* clear parent, base and active hooks flag of item */
649 item->parent = NULL;
650 item->base = NULL;
651 ipatch_item_clear_flags(item, IPATCH_ITEM_HOOKS_ACTIVE);
652
653 /* recursively unset base field and active hooks flag of all children */
654 if(is_container)
655 {
656 ipatch_item_recursive_base_unset((IpatchContainer *)item);
657 }
658
659 IPATCH_ITEM_WUNLOCK(item);
660 }
661
662 /* recursively unsets base field of a tree of items, expects container to
663 be write locked */
664 static void
ipatch_item_recursive_base_unset(IpatchContainer * container)665 ipatch_item_recursive_base_unset(IpatchContainer *container)
666 {
667 IpatchIter iter;
668 GObject *child;
669 const GType *types;
670
671 types = ipatch_container_get_child_types(container);
672
673 while(*types) /* loop over list types */
674 {
675 /* initialize iterator to child list */
676 if(!ipatch_container_init_iter(container, &iter, *types))
677 {
678 return;
679 }
680
681 child = ipatch_iter_first(&iter);
682
683 while(child) /* loop over child list */
684 {
685 IPATCH_ITEM_WLOCK(child);
686
687 /* unset base pointer and clear active hooks flag */
688 ((IpatchItem *)child)->base = NULL;
689 ipatch_item_clear_flags(child, IPATCH_ITEM_HOOKS_ACTIVE);
690
691 if(IPATCH_IS_CONTAINER(child)) /* recurse if a container */
692 {
693 ipatch_item_recursive_base_unset((IpatchContainer *)child);
694 }
695
696 IPATCH_ITEM_WUNLOCK(child);
697
698 child = ipatch_iter_next(&iter);
699 } /* child loop */
700
701 types++;
702 } /* child type loop */
703 }
704
705 /**
706 * ipatch_item_get_parent:
707 * @item: Item to get parent of
708 *
709 * Gets the parent of @item after incrementing its reference count. The
710 * caller is responsible for decrementing the reference count with
711 * g_object_unref when finished with it. This function is useful because
712 * all code paths must contain references to the items that they are working
713 * with in a multi-thread environment (there is no guarantee an item won't
714 * be destroyed unless a refcount is held).
715 *
716 * Returns: (transfer full): The parent of @item or %NULL if not parented or a root item. If
717 * not %NULL then the reference count of parent has been incremented and the
718 * caller is responsible for removing it when finished with parent.
719 */
720 IpatchItem *
ipatch_item_get_parent(IpatchItem * item)721 ipatch_item_get_parent(IpatchItem *item)
722 {
723 IpatchItem *parent;
724
725 g_return_val_if_fail(IPATCH_IS_ITEM(item), NULL);
726
727 IPATCH_ITEM_RLOCK(item);
728 parent = item->parent;
729
730 if(parent)
731 {
732 g_object_ref(parent);
733 }
734
735 IPATCH_ITEM_RUNLOCK(item);
736
737 return (parent);
738 }
739
740 /**
741 * ipatch_item_peek_parent: (skip)
742 * @item: Item to get the parent of
743 *
744 * This function is the same as ipatch_item_get_parent() (gets an
745 * item's parent) but does not increment the parent's ref
746 * count. Therefore this function should only be used when the caller
747 * already holds a reference or when only the value of the pointer
748 * will be used (not the contents), to avoid multi-thread problems.
749 *
750 * Returns: The parent of @item or %NULL if not parented or a root item.
751 */
752 IpatchItem *
ipatch_item_peek_parent(IpatchItem * item)753 ipatch_item_peek_parent(IpatchItem *item)
754 {
755 g_return_val_if_fail(IPATCH_IS_ITEM(item), NULL);
756 return (item->parent);
757 }
758
759 /**
760 * ipatch_item_get_base:
761 * @item: Item to get base parent of
762 *
763 * Gets the base parent of @item (toplevel patch file object) after
764 * incrementing its reference count. The caller is responsible for
765 * decrementing the reference count with g_object_unref when finished
766 * with it. If @item is itself a #IpatchBase object then it is returned.
767 *
768 * Returns: (transfer full): The base parent of @item or %NULL if no #IpatchBase type
769 * in parent ancestry. If not %NULL then the reference count of
770 * parent has been incremented and the caller is responsible for
771 * removing it when finished with the base parent.
772 */
773 IpatchItem *
ipatch_item_get_base(IpatchItem * item)774 ipatch_item_get_base(IpatchItem *item)
775 {
776 IpatchItem *base;
777
778 g_return_val_if_fail(IPATCH_IS_ITEM(item), NULL);
779
780 /* return the item if it is itself an #IpatchBase object */
781 if(IPATCH_IS_BASE(item))
782 {
783 return (g_object_ref(item));
784 }
785
786 IPATCH_ITEM_RLOCK(item);
787 base = item->base;
788
789 if(base)
790 {
791 g_object_ref(base);
792 }
793
794 IPATCH_ITEM_RUNLOCK(item);
795
796 return (base);
797 }
798
799 /**
800 * ipatch_item_peek_base: (skip)
801 * @item: Item to get the base parent of
802 *
803 * This function is the same as ipatch_item_get_base() (gets an
804 * item's toplevel base parent) but does not increment the parent's ref
805 * count. Therefore this function should only be used when the caller
806 * already holds a reference or when only the value of the pointer
807 * will be used (not the contents), to avoid multi-thread problems.
808 *
809 * Returns: The base parent of @item or %NULL if no #IpatchBase type
810 * in parent ancestry.
811 */
812 IpatchItem *
ipatch_item_peek_base(IpatchItem * item)813 ipatch_item_peek_base(IpatchItem *item)
814 {
815 g_return_val_if_fail(IPATCH_IS_ITEM(item), NULL);
816
817 /* return the item if it is itself an #IpatchBase object */
818 if(IPATCH_IS_BASE(item))
819 {
820 return (item);
821 }
822
823 return (item->base);
824 }
825
826 /**
827 * ipatch_item_get_ancestor_by_type:
828 * @item: Item to search for parent of a given type
829 * @ancestor_type: Type of parent in the @item object's ancestry
830 *
831 * Searches for the first parent item that is derived from
832 * @ancestor_type in the @item object's ancestry. @ancestor_type must
833 * be an #IpatchItem derived type (as well as the ancestry of
834 * @item). Of note is that @item can also match. The returned item's
835 * reference count has been incremented and it is the responsiblity of
836 * the caller to remove it with g_object_unref() when finished with
837 * it.
838 *
839 * Returns: (transfer full): The matched item (can be the @item itself) or %NULL if no
840 * item matches @ancestor_type in the ancestry. Remember to unref the
841 * matched item when done with it.
842 */
843 IpatchItem *
ipatch_item_get_ancestor_by_type(IpatchItem * item,GType ancestor_type)844 ipatch_item_get_ancestor_by_type(IpatchItem *item, GType ancestor_type)
845 {
846 #define MAX_ITEM_DEPTH 10
847 IpatchItem *p, *ancestry[MAX_ITEM_DEPTH];
848 int i, depth = -1;
849
850 g_return_val_if_fail(IPATCH_ITEM(item), NULL);
851 g_return_val_if_fail(g_type_is_a(ancestor_type, IPATCH_TYPE_ITEM), NULL);
852
853 p = item;
854
855 do
856 {
857 if(g_type_is_a(G_OBJECT_TYPE(p), ancestor_type))
858 {
859 break;
860 }
861
862 depth++;
863 g_assert(depth < MAX_ITEM_DEPTH);
864 p = ancestry[depth] = ipatch_item_get_parent(p);
865 }
866 while(p);
867
868 if(depth >= 0) /* unreference ancestry */
869 for(i = 0; i <= depth; i++)
870 if(ancestry[i] != p)
871 {
872 g_object_unref(ancestry[i]);
873 }
874
875 if(p == item)
876 {
877 g_object_ref(p);
878 }
879
880 return (p);
881 }
882
883 /**
884 * ipatch_item_peek_ancestor_by_type: (skip)
885 * @item: Item to search for parent of a given type
886 * @ancestor_type: Type of parent in the @item object's ancestry
887 *
888 * Just like ipatch_item_get_ancestor_by_type() but doesn't ref returned item.
889 * This should only be used when caller already owns a reference or only the
890 * pointer value is of importance, since otherwise there is a potential
891 * multi-thread race condition.
892 *
893 * Returns: The matched item (can be the @item itself) or %NULL if no
894 * item matches @ancestor_type in the ancestry. Remember that the returned
895 * item is not referenced.
896 */
897 IpatchItem *
ipatch_item_peek_ancestor_by_type(IpatchItem * item,GType ancestor_type)898 ipatch_item_peek_ancestor_by_type(IpatchItem *item, GType ancestor_type)
899 {
900 IpatchItem *match;
901
902 match = ipatch_item_get_ancestor_by_type(item, ancestor_type);
903
904 if(match)
905 {
906 g_object_unref(match);
907 }
908
909 return (match);
910 }
911
912 /**
913 * ipatch_item_remove:
914 * @item: Item to remove
915 *
916 * This function removes an item from its parent container and removes other
917 * references from within the same patch (e.g., a #IpatchSF2Sample
918 * will be removed from its parent #IpatchSF2 and all #IpatchSF2IZone objects
919 * referencing the sample will also be removed).
920 */
921 void
ipatch_item_remove(IpatchItem * item)922 ipatch_item_remove(IpatchItem *item)
923 {
924 g_return_if_fail(IPATCH_IS_ITEM(item));
925 ipatch_item_real_remove_full(item, FALSE);
926 }
927
928 /**
929 * ipatch_item_remove_full:
930 * @item: Item to remove
931 * @full: %TRUE removes all references to and from @item, %FALSE removes only references to @item
932 *
933 * Like ipatch_item_remove() but will also remove all references from @item (in the same #IpatchBase
934 * object) if @full is %TRUE, behaves like ipatch_item_remove() if @full is %FALSE.
935 *
936 * Since: 1.1.0
937 */
938 void
ipatch_item_remove_full(IpatchItem * item,gboolean full)939 ipatch_item_remove_full(IpatchItem *item, gboolean full)
940 {
941 g_return_if_fail(IPATCH_IS_ITEM(item));
942 ipatch_item_real_remove_full(item, full);
943 }
944
945 static void
ipatch_item_real_remove_full(IpatchItem * item,gboolean full)946 ipatch_item_real_remove_full(IpatchItem *item, gboolean full)
947 {
948 IpatchItemClass *klass;
949 IpatchItem *parent;
950
951 klass = IPATCH_ITEM_GET_CLASS(item);
952
953 if(klass->remove_full)
954 {
955 (*klass->remove_full)(item, full); /* call the remove_full class method */
956 }
957 else if(klass->remove)
958 {
959 (*klass->remove)(item); /* call the remove class method */
960
961 // If full removal specified and item only has older remove method, remove all children
962 if(full && IPATCH_IS_CONTAINER(item))
963 {
964 ipatch_container_remove_all(IPATCH_CONTAINER(item));
965 }
966 }
967 else
968 {
969 // Default remove if no class method
970 parent = ipatch_item_get_parent(item);
971
972 if(parent) // Remove item from parent
973 {
974 ipatch_container_remove(IPATCH_CONTAINER(parent), item);
975 g_object_unref(parent);
976 }
977
978 // If full removal specified, remove all children
979 if(full && IPATCH_IS_CONTAINER(item))
980 {
981 ipatch_container_remove_all(IPATCH_CONTAINER(item));
982 }
983 }
984 }
985
986 /**
987 * ipatch_item_remove_recursive:
988 * @item: Item to recursively remove
989 * @full: %TRUE removes all references to and from items, %FALSE removes only references to items
990 *
991 * This function calls ipatch_item_remove_full() on @item and all of its children recursively.
992 *
993 * Since: 1.1.0
994 */
995 void
ipatch_item_remove_recursive(IpatchItem * item,gboolean full)996 ipatch_item_remove_recursive(IpatchItem *item, gboolean full)
997 {
998 g_return_if_fail(IPATCH_IS_ITEM(item));
999 ipatch_item_real_remove_recursive(item, full);
1000 }
1001
1002 static void
ipatch_item_real_remove_recursive(IpatchItem * item,gboolean full)1003 ipatch_item_real_remove_recursive(IpatchItem *item, gboolean full)
1004 {
1005 const GType *child_types, *ptype;
1006 IpatchList *list;
1007 GList *p;
1008
1009 if(IPATCH_IS_CONTAINER(item))
1010 {
1011 child_types = ipatch_container_get_child_types((IpatchContainer *)item);
1012
1013 /* loop over child types */
1014 for(ptype = child_types; *ptype; ptype++)
1015 {
1016 list = ipatch_container_get_children((IpatchContainer *)item, *ptype); /* ++ ref new list */
1017
1018 if(g_type_is_a(*ptype, IPATCH_TYPE_CONTAINER))
1019 {
1020 for(p = list->items; p; p = p->next)
1021 {
1022 ipatch_item_real_remove_recursive((IpatchItem *)(p->data), full);
1023 }
1024 }
1025 else // Shortcut for non-container types (removes a level of unnecessary recursiveness)
1026 {
1027 for(p = list->items; p; p = p->next)
1028 {
1029 ipatch_item_real_remove_full((IpatchItem *)(p->data), full);
1030 }
1031 }
1032
1033 g_object_unref(list); /* -- unref list */
1034 }
1035 }
1036
1037 ipatch_item_real_remove_full(item, full);
1038 }
1039
1040 /**
1041 * ipatch_item_changed:
1042 * @item: Item that has changed
1043 *
1044 * This function should be called when an item's saveable state (what ends
1045 * up in a patch file) has changed. Causes the @item object's base parent
1046 * to have its changed flag set, indicating that the file has changes that
1047 * have not been saved. Note that this function is automatically called when
1048 * an ipatch_item_prop_notify() occurs for a property without the
1049 * #IPATCH_PARAM_NO_SAVE_CHANGE flag in its #GParamSpec and therefore this
1050 * function should not be called additionally.
1051 */
1052 void
ipatch_item_changed(IpatchItem * item)1053 ipatch_item_changed(IpatchItem *item)
1054 {
1055 IpatchItem *base = NULL;
1056
1057 g_return_if_fail(IPATCH_IS_ITEM(item));
1058
1059 IPATCH_ITEM_RLOCK(item);
1060
1061 if(item->base)
1062 {
1063 base = item->base;
1064 }
1065 else if(IPATCH_IS_BASE(item))
1066 {
1067 base = item;
1068 }
1069
1070 if(base && !(base->flags & IPATCH_BASE_CHANGED))
1071 {
1072 g_object_ref(base); /* ++ ref base object */
1073 ipatch_item_set_flags(base, IPATCH_BASE_CHANGED);
1074 }
1075 else
1076 {
1077 base = NULL;
1078 }
1079
1080 IPATCH_ITEM_RUNLOCK(item);
1081
1082 if(base) /* do property notify for base "changed" flag */
1083 {
1084 ipatch_item_prop_notify(base, ipatch_base_pspec_changed,
1085 ipatch_util_value_bool_true,
1086 ipatch_util_value_bool_false);
1087 g_object_unref(base); /* -- unref base parent object */
1088 }
1089 }
1090
1091 /**
1092 * ipatch_item_get_property_fast:
1093 * @item: Item to get property value from
1094 * @pspec: Parameter spec of property to get
1095 * @value: Uninitialized caller supplied value to store the current value in.
1096 * Remember to call g_value_unset when done with it.
1097 *
1098 * Usually only used by #IpatchItem implementations. A faster way to retrieve
1099 * an object property. Often used for fetching current value for property
1100 * notifies. Notice that this function currently doesn't work with interface
1101 * or class overridden properties.
1102 */
1103 void
ipatch_item_get_property_fast(IpatchItem * item,GParamSpec * pspec,GValue * value)1104 ipatch_item_get_property_fast(IpatchItem *item, GParamSpec *pspec,
1105 GValue *value)
1106 {
1107 GObjectClass *obj_class;
1108
1109 g_return_if_fail(IPATCH_IS_ITEM(item));
1110 g_return_if_fail(G_IS_PARAM_SPEC(pspec));
1111 g_return_if_fail(value != NULL);
1112
1113 obj_class = g_type_class_peek(pspec->owner_type);
1114 g_return_if_fail(obj_class != NULL);
1115 g_return_if_fail(obj_class->get_property != NULL);
1116
1117 g_value_init(value, G_PARAM_SPEC_VALUE_TYPE(pspec));
1118 obj_class->get_property((GObject *)item, IPATCH_PARAM_SPEC_ID(pspec),
1119 value, pspec);
1120 }
1121
1122 /**
1123 * ipatch_item_copy:
1124 * @dest: New destination item to copy to (should be same type or derived
1125 * from @src type)
1126 * @src: Item to copy from
1127 *
1128 * Copy an item using the item class' "copy" method. Object links are
1129 * copied as is, meaning the resulting object is a local object to the same
1130 * #IpatchBase.
1131 */
1132 void
ipatch_item_copy(IpatchItem * dest,IpatchItem * src)1133 ipatch_item_copy(IpatchItem *dest, IpatchItem *src)
1134 {
1135 IpatchItemClass *klass;
1136 GType dest_type, src_type;
1137
1138 g_return_if_fail(IPATCH_IS_ITEM(dest));
1139 g_return_if_fail(IPATCH_IS_ITEM(src));
1140 dest_type = G_OBJECT_TYPE(dest);
1141 src_type = G_OBJECT_TYPE(src);
1142 g_return_if_fail(g_type_is_a(dest_type, src_type));
1143
1144 klass = IPATCH_ITEM_GET_CLASS(src);
1145 g_return_if_fail(klass->copy != NULL);
1146
1147 klass->copy(dest, src, NULL, NULL);
1148 }
1149
1150 /**
1151 * ipatch_item_copy_link_func:
1152 * @dest: New destination item to copy to (should be same type or derived
1153 * from @src type)
1154 * @src: Item to copy from
1155 * @link_func: (scope call) (closure user_data): Function to call for each
1156 * object link pointer in @src. This function can alter the copied link if desired.
1157 * @user_data: (nullable): User defined data to pass to @link_func.
1158 *
1159 * Copy an item using the item class' "copy" method. Like ipatch_item_copy()
1160 * but takes a @link_func which can be used for handling replication of
1161 * externally linked objects (recursively deep duplicate for example, although
1162 * there is ipatch_item_duplicate_deep() specifically for that).
1163 */
1164 void
ipatch_item_copy_link_func(IpatchItem * dest,IpatchItem * src,IpatchItemCopyLinkFunc link_func,gpointer user_data)1165 ipatch_item_copy_link_func(IpatchItem *dest, IpatchItem *src,
1166 IpatchItemCopyLinkFunc link_func,
1167 gpointer user_data)
1168 {
1169 IpatchItemClass *klass;
1170 GType dest_type, src_type;
1171
1172 g_return_if_fail(IPATCH_IS_ITEM(dest));
1173 g_return_if_fail(IPATCH_IS_ITEM(src));
1174
1175 dest_type = G_OBJECT_TYPE(dest);
1176 src_type = G_OBJECT_TYPE(src);
1177 g_return_if_fail(g_type_is_a(dest_type, src_type));
1178
1179 klass = IPATCH_ITEM_GET_CLASS(src);
1180 g_return_if_fail(klass->copy != NULL);
1181
1182 klass->copy(dest, src, link_func, user_data);
1183 }
1184
1185 /**
1186 * ipatch_item_copy_replace:
1187 * @dest: New destination item to copy to (should be same type or derived
1188 * from @src type)
1189 * @src: Item to copy from
1190 * @repl_hash: (element-type IpatchItem IpatchItem): Hash of link pointer
1191 * substitutions. The original link pointer is the hash key, and the
1192 * hash value is the replacement pointer.
1193 *
1194 * Like ipatch_item_copy() but takes a link replacement hash, which can be
1195 * used for substituting different objects for object links. See
1196 * ipatch_item_copy_link_func() if even more flexibility is desired.
1197 */
1198 void
ipatch_item_copy_replace(IpatchItem * dest,IpatchItem * src,GHashTable * repl_hash)1199 ipatch_item_copy_replace(IpatchItem *dest, IpatchItem *src,
1200 GHashTable *repl_hash)
1201 {
1202 IpatchItemClass *klass;
1203 GType dest_type, src_type;
1204
1205 g_return_if_fail(IPATCH_IS_ITEM(dest));
1206 g_return_if_fail(IPATCH_IS_ITEM(src));
1207 dest_type = G_OBJECT_TYPE(dest);
1208 src_type = G_OBJECT_TYPE(src);
1209 g_return_if_fail(g_type_is_a(dest_type, src_type));
1210
1211 klass = IPATCH_ITEM_GET_CLASS(src);
1212 g_return_if_fail(klass->copy != NULL);
1213
1214 klass->copy(dest, src, ipatch_item_copy_link_func_hash, repl_hash);
1215 }
1216
1217 /**
1218 * ipatch_item_duplicate:
1219 * @item: Item to duplicate
1220 *
1221 * A convenience function to duplicate an item. Like ipatch_item_copy() but
1222 * creates a new duplicate item, rather than using an existing item. Note
1223 * that externally linked objects are not duplicated for non #IpatchBase
1224 * derived types, making the item local to the same #IpatchBase object.
1225 *
1226 * Returns: (transfer full): New duplicate item. Caller owns the reference to the new item.
1227 */
1228 IpatchItem *
ipatch_item_duplicate(IpatchItem * item)1229 ipatch_item_duplicate(IpatchItem *item)
1230 {
1231 IpatchItem *newitem;
1232
1233 g_return_val_if_fail(IPATCH_IS_ITEM(item), NULL);
1234
1235 newitem = g_object_new(G_OBJECT_TYPE(item), NULL); /* ++ ref new item */
1236 g_return_val_if_fail(newitem != NULL, NULL);
1237
1238 ipatch_item_copy(newitem, item);
1239 return (newitem); /* !! caller owns reference */
1240 }
1241
1242 /**
1243 * ipatch_item_duplicate_link_func:
1244 * @item: Item to duplicate
1245 * @link_func: (scope call) (closure user_data): Function to call for each
1246 * object link pointer in @item. This function can alter the copied link if desired.
1247 * @user_data: (nullable): User defined data to pass to @link_func.
1248 *
1249 * Like ipatch_item_copy_link_func() but duplicates the item instead of
1250 * copying to an existing item.
1251 *
1252 * Returns: (transfer full): New duplicate item. Caller owns the reference to the new item.
1253 */
1254 IpatchItem *
ipatch_item_duplicate_link_func(IpatchItem * item,IpatchItemCopyLinkFunc link_func,gpointer user_data)1255 ipatch_item_duplicate_link_func(IpatchItem *item,
1256 IpatchItemCopyLinkFunc link_func,
1257 gpointer user_data)
1258 {
1259 IpatchItem *newitem;
1260
1261 g_return_val_if_fail(IPATCH_IS_ITEM(item), NULL);
1262
1263 newitem = g_object_new(G_OBJECT_TYPE(item), NULL); /* ++ ref new item */
1264 g_return_val_if_fail(newitem != NULL, NULL);
1265
1266 ipatch_item_copy_link_func(newitem, item, link_func, user_data);
1267 return (newitem); /* !! caller owns reference */
1268 }
1269
1270 /**
1271 * ipatch_item_duplicate_replace:
1272 * @item: Item to duplicate
1273 * @repl_hash: (element-type IpatchItem IpatchItem): Hash of link pointer
1274 * substitutions. The original link pointer is the hash key, and the
1275 * hash value is the replacement pointer.
1276 *
1277 * Like ipatch_item_copy_replace() but duplicates the item instead of
1278 * copying it to an already created item.
1279 *
1280 * Returns: (transfer full): New duplicate item. Caller owns the reference to the new item.
1281 */
1282 IpatchItem *
ipatch_item_duplicate_replace(IpatchItem * item,GHashTable * repl_hash)1283 ipatch_item_duplicate_replace(IpatchItem *item, GHashTable *repl_hash)
1284 {
1285 IpatchItem *newitem;
1286
1287 g_return_val_if_fail(IPATCH_IS_ITEM(item), NULL);
1288
1289 newitem = g_object_new(G_OBJECT_TYPE(item), NULL); /* ++ ref new item */
1290 g_return_val_if_fail(newitem != NULL, NULL);
1291
1292 ipatch_item_copy_replace(newitem, item, repl_hash);
1293 return (newitem); /* !! caller owns reference */
1294 }
1295
1296 /**
1297 * ipatch_item_duplicate_deep:
1298 * @item: Item to deep duplicate
1299 *
1300 * Recursively duplicate an item (i.e., its dependencies also).
1301 *
1302 * Returns: (transfer full): List of duplicated @item and dependencies
1303 * (duplicated @item is the first in the list). List object is owned by
1304 * the caller and should be unreferenced when finished using it.
1305 */
1306 IpatchList *
ipatch_item_duplicate_deep(IpatchItem * item)1307 ipatch_item_duplicate_deep(IpatchItem *item)
1308 {
1309 IpatchItemClass *klass;
1310 IpatchItem *newitem;
1311 IpatchList *list;
1312 GHashTable *linkhash; /* link substitutions */
1313
1314 g_return_val_if_fail(IPATCH_IS_ITEM(item), NULL);
1315
1316 klass = IPATCH_ITEM_GET_CLASS(item);
1317 g_return_val_if_fail(klass->copy != NULL, NULL);
1318
1319 newitem = g_object_new(G_OBJECT_TYPE(item), NULL); /* ++ ref new item */
1320 g_return_val_if_fail(newitem != NULL, NULL);
1321
1322 /* create hash to track new duplicated items */
1323 linkhash = g_hash_table_new(NULL, NULL);
1324 g_hash_table_insert(linkhash, item, newitem);
1325
1326 /* do the copy using the deep copy link function callback */
1327 klass->copy(newitem, item, ipatch_item_copy_link_func_deep, linkhash);
1328
1329 list = ipatch_list_new(); /* ++ref new list */
1330
1331 /* remove newitem, before convert to list */
1332 g_hash_table_remove(linkhash, newitem);
1333
1334 /* convert hash to IpatchList (!! list takes over references) */
1335 g_hash_table_foreach(linkhash, copy_hash_to_list_GHFunc, list);
1336
1337 /* prepend newitem (!! List takes over creator's reference) */
1338 list->items = g_list_prepend(list->items, newitem);
1339
1340 g_hash_table_destroy(linkhash);
1341
1342 return (list);
1343 }
1344
1345 /* GHFunc to convert hash table to IpatchList */
copy_hash_to_list_GHFunc(gpointer key,gpointer value,gpointer user_data)1346 static void copy_hash_to_list_GHFunc(gpointer key, gpointer value,
1347 gpointer user_data)
1348 {
1349 IpatchList *list = (IpatchList *)user_data;
1350
1351 list->items = g_list_prepend(list->items, value);
1352 }
1353
1354 /**
1355 * ipatch_item_copy_link_func_deep: (skip)
1356 * @item: Item which is being linked (contains a reference to @link).
1357 * @link: The item being referenced (can be %NULL).
1358 * @user_data: User data supplied to copy/duplicate function.
1359 *
1360 * IpatchItemCopyLinkFunc for deep duplicating an object and dependencies.
1361 *
1362 * Returns: Pointer to item to use for link property (duplicated).
1363 */
1364 IpatchItem *
ipatch_item_copy_link_func_deep(IpatchItem * item,IpatchItem * link,gpointer user_data)1365 ipatch_item_copy_link_func_deep(IpatchItem *item, IpatchItem *link,
1366 gpointer user_data)
1367 {
1368 GHashTable *linkhash = (GHashTable *)user_data;
1369 IpatchItem *dup = NULL;
1370
1371 if(!link)
1372 {
1373 return (NULL);
1374 }
1375
1376 /* look up link item in duplicate hash. */
1377 if(linkhash)
1378 {
1379 dup = g_hash_table_lookup(linkhash, link);
1380 }
1381
1382 if(!dup) /* link not in hash? - Duplicate link and add it to hash. */
1383 {
1384 dup = g_object_new(G_OBJECT_TYPE(link), NULL); /* ++ ref new item */
1385 g_return_val_if_fail(dup != NULL, NULL);
1386
1387 /* !! hash table takes over reference */
1388 g_hash_table_insert(linkhash, link, dup);
1389 ipatch_item_copy_link_func(dup, link, ipatch_item_copy_link_func_deep,
1390 user_data);
1391 }
1392
1393 return (dup);
1394 }
1395
1396 /**
1397 * ipatch_item_copy_link_func_hash: (skip)
1398 * @item: Item which is being linked (contains a reference to @link).
1399 * @link: The item being referenced.
1400 * @user_data: (type GHashTable): User data supplied to copy/duplicate function.
1401 *
1402 * A predefined #IpatchItemCopyLinkFunc which takes a hash for the @user_data
1403 * component, which is used for replacing link pointers. @link is used as the
1404 * hash key, the hash value is used in its place if present otherwise @link is
1405 * used unchanged.
1406 *
1407 * Returns: Value in hash (@user_data) if @link is present in hash, or @link
1408 * itself if not.
1409 */
1410 IpatchItem *
ipatch_item_copy_link_func_hash(IpatchItem * item,IpatchItem * link,gpointer user_data)1411 ipatch_item_copy_link_func_hash(IpatchItem *item, IpatchItem *link,
1412 gpointer user_data)
1413 {
1414 GHashTable *hash = (GHashTable *)user_data;
1415 IpatchItem *repl = NULL;
1416
1417 if(!link)
1418 {
1419 return (NULL);
1420 }
1421
1422 if(hash)
1423 {
1424 repl = g_hash_table_lookup(hash, link);
1425 }
1426
1427 return (repl ? repl : link);
1428 }
1429
1430 /**
1431 * ipatch_item_type_can_conflict:
1432 * @item_type: Type to test
1433 *
1434 * Test if a given #IpatchItem derived type can conflict with another item
1435 * (only applies to items of the same type in the same #IpatchBase object).
1436 *
1437 * Returns: %TRUE if @item_type can conflict (has properties which should be
1438 * unique among its siblings), %FALSE otherwise.
1439 */
1440 gboolean
ipatch_item_type_can_conflict(GType item_type)1441 ipatch_item_type_can_conflict(GType item_type)
1442 {
1443 UniqueBag *unique;
1444
1445 unique = item_lookup_unique_bag(item_type);
1446 return (unique != NULL);
1447 }
1448
1449 /**
1450 * ipatch_item_type_get_unique_specs:
1451 * @item_type: Type to get unique parameter specs for
1452 * @groups: (out) (optional): Location to store an integer describing groups.
1453 * Each bit corresponds to a GParamSpec in the returned array. GParamSpecs of the
1454 * same group will have the same bit value (0 or 1) and be consecutive.
1455 * Unique properties in the same group must all match (logic AND) for a
1456 * conflict to occur. Pass %NULL to ignore.
1457 *
1458 * Get the list of unique parameter specs which can conflict for a given
1459 * item type.
1460 *
1461 * Returns: (array zero-terminated=1) (transfer none): %NULL terminated list of
1462 * parameter specs or %NULL if @item_type can never conflict. The returned
1463 * array is internal and should not be modified or freed.
1464 */
1465 GParamSpec **
ipatch_item_type_get_unique_specs(GType item_type,guint32 * groups)1466 ipatch_item_type_get_unique_specs(GType item_type, guint32 *groups)
1467 {
1468 UniqueBag *unique;
1469
1470 unique = item_lookup_unique_bag(item_type);
1471
1472 if(unique)
1473 {
1474 if(groups)
1475 {
1476 *groups = unique->groups;
1477 }
1478
1479 return (unique->pspecs);
1480 }
1481 else
1482 {
1483 if(groups)
1484 {
1485 *groups = 0;
1486 }
1487
1488 return (NULL);
1489 }
1490 }
1491
1492 /**
1493 * ipatch_item_get_unique_props:
1494 * @item: Item to get unique properties of
1495 *
1496 * Get the values of the unique properties for @item. This is useful when
1497 * doing conflict detection processing and more flexibility is desired than
1498 * with ipatch_item_test_conflict().
1499 *
1500 * Returns: GValueArray of the unique property values of @item in the same
1501 * order as the parameter specs returned by ipatch_item_type_get_unique_specs().
1502 * %NULL is returned if @item doesn't have any unique properties.
1503 * Use g_value_array_free() to free the array when finished using it.
1504 */
1505 GValueArray *
ipatch_item_get_unique_props(IpatchItem * item)1506 ipatch_item_get_unique_props(IpatchItem *item)
1507 {
1508 GParamSpec **ps;
1509 UniqueBag *unique;
1510 GValueArray *vals;
1511 GValue *value;
1512 int count, i;
1513
1514 g_return_val_if_fail(IPATCH_IS_ITEM(item), NULL);
1515
1516 /* item has any unique properties? */
1517 unique = item_lookup_unique_bag(G_OBJECT_TYPE(item));
1518
1519 if(!unique)
1520 {
1521 return (NULL);
1522 }
1523
1524 /* count param specs */
1525 for(count = 0, ps = unique->pspecs; *ps; count++, ps++);
1526
1527 vals = g_value_array_new(count);
1528
1529 for(i = 0, ps = unique->pspecs; i < count; i++, ps++)
1530 {
1531 /* NULL will append an unset value, saves a value copy operation */
1532 g_value_array_append(vals, NULL);
1533 value = g_value_array_get_nth(vals, i);
1534 ipatch_item_get_property_fast(item, *ps, value);
1535 }
1536
1537 return (vals);
1538 }
1539
1540 /**
1541 * ipatch_item_test_conflict:
1542 * @item1: First item to test
1543 * @item2: Second item to test
1544 *
1545 * Test if two items would conflict if they were siblings (doesn't actually
1546 * test if they are siblings).
1547 *
1548 * Returns: Flags of unique properties which conflict. The bit index
1549 * corresponds to the parameter index in the array returned by
1550 * ipatch_item_type_get_unique_specs(). 0 is returned if the items do not
1551 * conflict.
1552 */
1553 guint
ipatch_item_test_conflict(IpatchItem * item1,IpatchItem * item2)1554 ipatch_item_test_conflict(IpatchItem *item1, IpatchItem *item2)
1555 {
1556 GValue val1 = { 0 }, val2 = { 0 };
1557 UniqueBag *unique;
1558 GParamSpec *pspec;
1559 GType type;
1560 guint conflicts;
1561 guint mask;
1562 int i, count, toggle, groupcount;
1563
1564 g_return_val_if_fail(IPATCH_IS_ITEM(item1), 0);
1565 g_return_val_if_fail(IPATCH_IS_ITEM(item2), 0);
1566
1567 type = G_OBJECT_TYPE(item1);
1568
1569 /* can't conflict if items not the same type */
1570 if(type != G_OBJECT_TYPE(item2))
1571 {
1572 return (0);
1573 }
1574
1575 unique = item_lookup_unique_bag(type);
1576
1577 if(!unique)
1578 {
1579 return (0); /* no unique properties? */
1580 }
1581
1582 conflicts = 0;
1583
1584 for(i = 0; TRUE; i++)
1585 {
1586 pspec = unique->pspecs[i];
1587
1588 if(!pspec)
1589 {
1590 break;
1591 }
1592
1593 ipatch_item_get_property_fast(item1, pspec, &val1);
1594 ipatch_item_get_property_fast(item2, pspec, &val2);
1595
1596 if(g_param_values_cmp(pspec, &val1, &val2) == 0)
1597 {
1598 conflicts |= (1 << i);
1599 }
1600
1601 g_value_unset(&val1);
1602 g_value_unset(&val2);
1603 }
1604
1605 /* mask out any unique groups which don't have all its props conflicting */
1606
1607 count = i;
1608 mask = 1;
1609 groupcount = 1;
1610 toggle = (unique->groups & 1); /* to track group changes */
1611
1612 for(i = 1; i < count; i++)
1613 {
1614 /* same group as prev. property? */
1615 if(toggle == ((unique->groups & (1 << i)) != 0))
1616 {
1617 mask |= (1 << i); /* add another mask bit */
1618 groupcount++; /* increment group property count */
1619 }
1620 else /* group changed */
1621 {
1622 /* prev group is larger than 1 prop. and not all props conflicting? */
1623 if(groupcount > 1 && (conflicts & mask) != mask)
1624 {
1625 conflicts &= ~mask; /* clear all conflicts for prev group */
1626 }
1627
1628 toggle ^= 1;
1629 mask = (1 << i);
1630 groupcount = 1;
1631 }
1632 }
1633
1634 /* check if last gorup is larger than 1 prop. and not all props conflicting */
1635 if(groupcount > 1 && (conflicts & mask) != mask)
1636 {
1637 conflicts &= ~mask; /* clear all conflicts for last group */
1638 }
1639
1640 return (conflicts);
1641 }
1642
1643 /* lookup cached unique property info for a given type */
1644 static UniqueBag *
item_lookup_unique_bag(GType type)1645 item_lookup_unique_bag(GType type)
1646 {
1647 GParamSpec **pspecs, *ps;
1648 GList *speclist = NULL; /* sorted spec list (sorted by unique group) */
1649 UniqueBag *unique;
1650 gpointer klass;
1651 guint n_props, count;
1652 guint i;
1653
1654 G_LOCK(unique_prop_cache);
1655
1656 /* has not been cached yet? */
1657 if(! g_hash_table_lookup_extended(unique_prop_cache,
1658 GUINT_TO_POINTER (type),
1659 NULL, (gpointer *)&unique))
1660 {
1661 klass = g_type_class_ref(type); /* ++ref */
1662 g_return_val_if_fail(klass != NULL, NULL);
1663
1664 /* get property list and add unique properties to speclist */
1665 pspecs = g_object_class_list_properties(klass, &n_props); /* alloc */
1666
1667 for(i = 0, count = 0; i < n_props; i++)
1668 {
1669 ps = pspecs[i];
1670
1671 if(ps->flags & IPATCH_PARAM_UNIQUE) /* unique property? */
1672 {
1673 /* add to speclist sorted by unique group (0 = nogroup) */
1674 speclist = g_list_insert_sorted(speclist, ps,
1675 unique_group_list_sort_func);
1676 count++;
1677 }
1678 }
1679
1680 g_free(pspecs); /* free */
1681 g_type_class_unref(klass); /* --ref */
1682
1683 if(speclist) /* any unique properties? */
1684 {
1685 guint group, lastgroup = 0;
1686 int toggle = 0;
1687 GList *p;
1688
1689 /* create unique bag */
1690 unique = g_new(UniqueBag, 1);
1691 unique->pspecs = g_new(GParamSpec *, count + 1);
1692 unique->groups = 0;
1693
1694 /* add pointers to unique spec array and set group bit toggle field */
1695 for(i = 0, p = speclist; i < count; i++, p = p->next)
1696 {
1697 unique->pspecs[i] = (GParamSpec *)(p->data);
1698 ipatch_param_get((GParamSpec *)(p->data),
1699 "unique-group-id", &group, NULL);
1700
1701 if(group != lastgroup)
1702 {
1703 toggle ^= 1;
1704 }
1705
1706 if(toggle)
1707 {
1708 unique->groups |= (1 << i);
1709 }
1710
1711 lastgroup = group;
1712 }
1713
1714 unique->pspecs[count] = NULL; /* NULL terminated */
1715 }
1716 else
1717 {
1718 unique = NULL; /* indicate no unique properties */
1719 }
1720
1721 g_hash_table_insert(unique_prop_cache, GUINT_TO_POINTER(type), unique);
1722 }
1723
1724 G_UNLOCK(unique_prop_cache);
1725
1726 return (unique);
1727 }
1728
1729 /* sort GParamSpec pointers by their unique group id (0 = no group) */
1730 static gint
unique_group_list_sort_func(gconstpointer a,gconstpointer b)1731 unique_group_list_sort_func(gconstpointer a, gconstpointer b)
1732 {
1733 GParamSpec *aspec = (GParamSpec *)a, *bspec = (GParamSpec *)b;
1734 int agroup, bgroup;
1735
1736 ipatch_param_get(aspec, "unique-group-id", &agroup, NULL);
1737 ipatch_param_get(bspec, "unique-group-id", &bgroup, NULL);
1738
1739 return (agroup - bgroup);
1740 }
1741
1742 /**
1743 * ipatch_item_set_atomic:
1744 * @item: (type IpatchItem): IpatchItem derived object to set properties of
1745 * @first_property_name: Name of first property
1746 * @...: Variable list of arguments that should start with the value to
1747 * set @first_property_name to, followed by property name/value pairs. List is
1748 * terminated with a %NULL property name.
1749 *
1750 * Sets properties on a patch item atomically (i.e. item is
1751 * multi-thread locked while all properties are set). This avoids
1752 * critical parameter sync problems when multiple threads are
1753 * accessing the same item. See g_object_set() for more information on
1754 * setting properties.
1755 */
1756 void
ipatch_item_set_atomic(gpointer item,const char * first_property_name,...)1757 ipatch_item_set_atomic(gpointer item, const char *first_property_name, ...)
1758 {
1759 va_list args;
1760
1761 g_return_if_fail(IPATCH_IS_ITEM(item));
1762
1763 va_start(args, first_property_name);
1764
1765 IPATCH_ITEM_WLOCK(item);
1766 g_object_set_valist(G_OBJECT(item), first_property_name, args);
1767 IPATCH_ITEM_WUNLOCK(item);
1768
1769 va_end(args);
1770 }
1771
1772 /**
1773 * ipatch_item_get_atomic:
1774 * @item: (type IpatchItem): IpatchItem derived object to get properties from
1775 * @first_property_name: Name of first property
1776 * @...: Variable list of arguments that should start with a
1777 * pointer to store the value from @first_property_name, followed by
1778 * property name/value pointer pairs. List is terminated with a %NULL
1779 * property name.
1780 *
1781 * Gets properties from a patch item atomically (i.e. item is
1782 * multi-thread locked while all properties are retrieved). This
1783 * avoids critical parameter sync problems when multiple threads are
1784 * accessing the same item. See g_object_get() for more information on
1785 * getting properties.
1786 */
1787 void
ipatch_item_get_atomic(gpointer item,const char * first_property_name,...)1788 ipatch_item_get_atomic(gpointer item, const char *first_property_name, ...)
1789 {
1790 va_list args;
1791
1792 g_return_if_fail(IPATCH_IS_ITEM(item));
1793
1794 va_start(args, first_property_name);
1795
1796 IPATCH_ITEM_WLOCK(item);
1797 g_object_get_valist(G_OBJECT(item), first_property_name, args);
1798 IPATCH_ITEM_WUNLOCK(item);
1799
1800 va_end(args);
1801 }
1802
1803 /**
1804 * ipatch_item_first: (skip)
1805 * @iter: Patch item iterator containing #IpatchItem items
1806 *
1807 * Gets the first item in a patch item iterator. A convenience wrapper for
1808 * ipatch_iter_first().
1809 *
1810 * Returns: The first item in @iter or %NULL if empty.
1811 */
1812 IpatchItem *
ipatch_item_first(IpatchIter * iter)1813 ipatch_item_first(IpatchIter *iter)
1814 {
1815 GObject *obj;
1816 g_return_val_if_fail(iter != NULL, NULL);
1817
1818 obj = ipatch_iter_first(iter);
1819
1820 if(obj)
1821 {
1822 return (IPATCH_ITEM(obj));
1823 }
1824 else
1825 {
1826 return (NULL);
1827 }
1828 }
1829
1830 /**
1831 * ipatch_item_next: (skip)
1832 * @iter: Patch item iterator containing #IpatchItem items
1833 *
1834 * Gets the next item in a patch item iterator. A convenience wrapper for
1835 * ipatch_iter_next().
1836 *
1837 * Returns: The next item in @iter or %NULL if no more items.
1838 */
1839 IpatchItem *
ipatch_item_next(IpatchIter * iter)1840 ipatch_item_next(IpatchIter *iter)
1841 {
1842 GObject *obj;
1843 g_return_val_if_fail(iter != NULL, NULL);
1844
1845 obj = ipatch_iter_next(iter);
1846
1847 if(obj)
1848 {
1849 return (IPATCH_ITEM(obj));
1850 }
1851 else
1852 {
1853 return (NULL);
1854 }
1855 }
1856