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