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: IpatchTypeProp
22  * @short_description: GObject style properties for GTypes
23  * @see_also:
24  * @stability: Stable
25  *
26  * Provides a registry system for adding GObject style properties to GTypes.
27  * This is used to describe certain properties of different objects, such as
28  * "category".
29  */
30 #include <stdio.h>
31 #include <string.h>
32 #include <glib.h>
33 #include <glib-object.h>
34 #include <gobject/gvaluecollector.h>
35 
36 #include "IpatchTypeProp.h"
37 #include "IpatchVirtualContainer.h"
38 #include "builtin_enums.h"
39 #include "ipatch_priv.h"
40 #include "i18n.h"
41 
42 /* key used for hash of GType property values */
43 typedef struct
44 {
45     GType type;		/* type this property is bound to */
46     GParamSpec *spec;	/* the parameter spec of the property */
47 } TypePropValueKey;
48 
49 typedef struct
50 {
51     GValue value;			/* a static assigned value (or NULL) */
52     IpatchTypePropGetFunc func;	/* a dynamic prop function (or NULL) */
53     GDestroyNotify notify_func;
54     gpointer user_data;
55 } TypePropValueVal;
56 
57 static guint type_prop_value_GHashFunc(gconstpointer key);
58 static gboolean type_prop_value_GEqualFunc(gconstpointer a, gconstpointer b);
59 static void type_prop_value_destroy(gpointer user_data);
60 
61 static void type_list_properties_GHFunc(gpointer key, gpointer value,
62                                         gpointer user_data);
63 static void
64 type_set_property(GType type, GParamSpec *prop_spec, const GValue *value,
65                   IpatchTypePropGetFunc func, GDestroyNotify notify_func,
66                   gpointer user_data);
67 static void type_get_property(GType type, GParamSpec *prop_spec,
68                               GValue *value, GObject *object);
69 
70 
71 G_LOCK_DEFINE_STATIC(type_prop_hash);
72 G_LOCK_DEFINE_STATIC(type_prop_value_hash);
73 
74 /* GType GParamSpec property hash (PropNameQuark -> GParamSpec) */
75 static GHashTable *type_prop_hash = NULL;
76 
77 /* hash of all GType property values (GType:GParamSpec -> GValue) */
78 static GHashTable *type_prop_value_hash = NULL;
79 
80 /*-----------------------------------------------------------------------------
81   Initialization/deinitialization of GType property system
82  ----------------------------------------------------------------------------*/
83 /**
84  * _ipatch_type_prop_init: (skip)
85  *
86  * Initialize GType property system
87  */
88 void
_ipatch_type_prop_init(void)89 _ipatch_type_prop_init(void)
90 {
91     type_prop_hash = g_hash_table_new(NULL, NULL);
92 
93     type_prop_value_hash =
94         g_hash_table_new_full(type_prop_value_GHashFunc,
95                               type_prop_value_GEqualFunc,
96                               (GDestroyNotify)g_free,
97                               (GDestroyNotify)type_prop_value_destroy);
98 
99     /* install some default type properties */
100 
101     /* a user friendly type name */
102     ipatch_type_install_property
103     (g_param_spec_string("name", "Name", "Name",
104                          NULL, G_PARAM_READWRITE));
105 
106     /* title of the object (usually dynamically created from obj instance) */
107     ipatch_type_install_property
108     (g_param_spec_string("title", "Title", "Title",
109                          NULL, G_PARAM_READWRITE));
110 
111     /* a user friendly type detail name */
112     ipatch_type_install_property
113     (g_param_spec_string("blurb", "Blurb", "Blurb",
114                          NULL, G_PARAM_READWRITE));
115 
116     /* type classes (see ipatch_type_prop_register_category) */
117     ipatch_type_install_property
118     (g_param_spec_int("category", "Category", "Type category",
119                       G_MININT, G_MAXINT, IPATCH_CATEGORY_NONE,
120                       G_PARAM_READWRITE));
121 
122     /* virtual parent container type (defined for children of
123      * IpatchVirtualContainer types) */
124     ipatch_type_install_property
125     (g_param_spec_gtype("virtual-parent-type", "Virtual parent type",
126                         "Virtual parent type", G_TYPE_NONE,
127                         G_PARAM_READWRITE));
128 
129     /* virtual container child type (defined for IpatchVirtualContainer types) */
130     ipatch_type_install_property
131     (g_param_spec_gtype("virtual-child-type", "Virtual child type",
132                         "Virtual child type", G_TYPE_NONE,
133                         G_PARAM_READWRITE));
134 
135     /* link item type (type of object referenced/linked by another) */
136     ipatch_type_install_property
137     (g_param_spec_gtype("link-type", "Link type", "Link type", G_TYPE_NONE,
138                         G_PARAM_READWRITE));
139 
140     /* virtual container conform function (function pointer used for making
141      * a child object conform to the virtual container's criteria, the "percussion"
142      * property for example) See IpatchVirtualContainerConformFunc. */
143     ipatch_type_install_property(g_param_spec_pointer
144                                  ("virtual-child-conform-func",
145                                   "IpatchVirtualContainerConformFunc",
146                                   "IpatchVirtualContainerConformFunc", G_PARAM_READWRITE));
147 
148     /* sort a container's children in user interfaces? */
149     ipatch_type_install_property
150     (g_param_spec_boolean("sort-children", "Sort children",
151                           "Sort children", FALSE, G_PARAM_READWRITE));
152 
153     /* splits type property (for note and velocity splits) */
154     ipatch_type_install_property
155     (g_param_spec_enum("splits-type", "Splits type",
156                        "Splits type", IPATCH_TYPE_SPLITS_TYPE,
157                        IPATCH_SPLITS_NONE, G_PARAM_READWRITE));
158 
159     /* mime type for IpatchFile derived types */
160     ipatch_type_install_property
161     (g_param_spec_string("mime-type", "Mime type", "Mime type",
162                          NULL, G_PARAM_READWRITE));
163 }
164 
165 /* hash function for GType property value hash */
166 static guint
type_prop_value_GHashFunc(gconstpointer key)167 type_prop_value_GHashFunc(gconstpointer key)
168 {
169     TypePropValueKey *valkey = (TypePropValueKey *)key;
170     return ((guint)(valkey->type) + G_PARAM_SPEC_TYPE(valkey->spec));
171 }
172 
173 /* key equal function for GType property value hash */
174 static gboolean
type_prop_value_GEqualFunc(gconstpointer a,gconstpointer b)175 type_prop_value_GEqualFunc(gconstpointer a, gconstpointer b)
176 {
177     TypePropValueKey *akey = (TypePropValueKey *)a;
178     TypePropValueKey *bkey = (TypePropValueKey *)b;
179     return (akey->type == bkey->type && akey->spec == bkey->spec);
180 }
181 
182 /* destroy notify for GType property values */
183 static void
type_prop_value_destroy(gpointer user_data)184 type_prop_value_destroy(gpointer user_data)
185 {
186     TypePropValueVal *val = (TypePropValueVal *)user_data;
187 
188     if(G_IS_VALUE(&val->value))
189     {
190         g_value_unset(&val->value);
191     }
192 
193     if(val->notify_func)
194     {
195         val->notify_func(val->user_data);
196     }
197 
198     g_slice_free(TypePropValueVal, val);
199 }
200 
201 /**
202  * _ipatch_type_prop_deinit
203  *
204  * Free GType property system
205  */
206 void
_ipatch_type_prop_deinit(void)207 _ipatch_type_prop_deinit (void)
208 {
209     g_hash_table_destroy(type_prop_hash);
210     g_hash_table_destroy(type_prop_value_hash);
211 }
212 
213 /**
214  * ipatch_type_install_property:
215  * @prop_spec: (transfer full): Specification for the new #GType property.  Ownership
216  *   of the GParamSpec is taken over by this function.  The name field of
217  *   the GParamSpec should be a static string and is used as the property's
218  *   ID.
219  *
220  * Install a new #GType property which can be used to associate arbitrary
221  * information to GTypes.
222  */
223 void
ipatch_type_install_property(GParamSpec * prop_spec)224 ipatch_type_install_property(GParamSpec *prop_spec)
225 {
226     GQuark quark;
227 
228     g_return_if_fail(G_IS_PARAM_SPEC(prop_spec));
229     g_return_if_fail(prop_spec->name != NULL);
230 
231     /* take ownership of the parameter spec */
232     g_param_spec_ref(prop_spec);
233     g_param_spec_sink(prop_spec);
234 
235     quark = g_quark_from_static_string(prop_spec->name);
236 
237     G_LOCK(type_prop_hash);
238     g_hash_table_insert(type_prop_hash, GUINT_TO_POINTER(quark), prop_spec);
239     G_UNLOCK(type_prop_hash);
240 }
241 
242 /**
243  * ipatch_type_find_property:
244  * @name: Name of GType property
245  *
246  * Lookup a GType property by name.
247  *
248  * Returns: (transfer none): The matching GParamSpec or %NULL if not found.  The GParamSpec is
249  *   internal and should NOT be modified or freed.
250  */
251 GParamSpec *
ipatch_type_find_property(const char * name)252 ipatch_type_find_property(const char *name)
253 {
254     GParamSpec *spec;
255     GQuark quark;
256 
257     g_return_val_if_fail(name != NULL, NULL);
258 
259     quark = g_quark_try_string(name);
260 
261     if(!quark)
262     {
263         return (NULL);
264     }
265 
266     G_LOCK(type_prop_hash);
267     spec = g_hash_table_lookup(type_prop_hash, GUINT_TO_POINTER(quark));
268     G_UNLOCK(type_prop_hash);
269 
270     return (spec);
271 }
272 
273 /**
274  * ipatch_type_list_properties:
275  * @n_properties: (out): Length of returned array
276  *
277  * Get a list of all registered GType properties.
278  *
279  * Returns: (array length=n_properties) (transfer container): An array of GParamSpecs
280  *   which should be freed when finished (only the array, not the GParamSpecs themselves)
281  */
282 GParamSpec **
ipatch_type_list_properties(guint * n_properties)283 ipatch_type_list_properties(guint *n_properties)
284 {
285     GParamSpec **specs, **specp;
286 
287     g_return_val_if_fail(n_properties != NULL, NULL);
288 
289     G_LOCK(type_prop_hash);
290     specs = g_new(GParamSpec *, g_hash_table_size(type_prop_hash));
291     specp = specs;
292     g_hash_table_foreach(type_prop_hash, type_list_properties_GHFunc, &specp);
293     G_UNLOCK(type_prop_hash);
294 
295     return (specs);
296 }
297 
298 /* fills an array with GParamSpecs in the type_prop_hash */
299 static void
type_list_properties_GHFunc(gpointer key,gpointer value,gpointer user_data)300 type_list_properties_GHFunc(gpointer key, gpointer value, gpointer user_data)
301 {
302     GParamSpec ***specs = user_data;
303     **specs = (GParamSpec *)value;
304     *specs = *specs + 1;
305 }
306 
307 /**
308  * ipatch_type_find_types_with_property:
309  * @name: Name of type property
310  * @value: (nullable): Optional value to match to type property values
311  *   (%NULL to match any value)
312  * @n_types: (out) (optional): Location to store count of types in returned
313  *   array or %NULL to ignore
314  *
315  * Get an array of types which have the given type property assigned and match
316  * @value (optional, %NULL matches any value).
317  *
318  * Returns: (array length=n_types): Newly allocated 0 terminated array of types
319  *   which have the named property set or %NULL if type property not found.
320  */
321 GType *
ipatch_type_find_types_with_property(const char * name,const GValue * value,guint * n_types)322 ipatch_type_find_types_with_property(const char *name, const GValue *value,
323                                      guint *n_types)
324 {
325     TypePropValueKey *key;
326     GParamSpec *pspec;
327     GList *keys, *p, *temp;
328     GType *types;
329     int count = 0;
330     int i;
331 
332     g_return_val_if_fail(name != NULL, NULL);
333 
334     pspec = ipatch_type_find_property(name);
335     g_return_val_if_fail(pspec != NULL, NULL);
336 
337     G_LOCK(type_prop_value_hash);
338 
339     keys = g_hash_table_get_keys(type_prop_value_hash);   /* ++ alloc keys list */
340 
341     /* Convert keys list to list of matching GTypes */
342     for(p = keys; p;)
343     {
344         key = p->data;
345 
346         if(key->spec == pspec)
347         {
348             /* Replace list data TypePropValueKey pointer to GType */
349             p->data = GSIZE_TO_POINTER(key->type);
350             p = p->next;
351         }
352         else        /* Doesn't match GParamSpec - remove from list */
353         {
354             if(p->prev)
355             {
356                 p->prev->next = p->next;
357             }
358             else
359             {
360                 keys = p->next;
361             }
362 
363             if(p->next)
364             {
365                 p->next->prev = p->prev;
366             }
367 
368             temp = p;
369             p = p->next;
370             g_list_free1(temp);
371         }
372     }
373 
374     G_UNLOCK(type_prop_value_hash);
375 
376     /* Compare values if @value was supplied */
377     if(value)
378     {
379         GValue cmp_value = { 0 };
380         GType type;
381 
382         for(p = keys; p;)
383         {
384             type = GPOINTER_TO_SIZE(p->data);
385             g_value_init(&cmp_value, G_PARAM_SPEC_VALUE_TYPE(pspec));
386             ipatch_type_get_property(type, pspec->name, &cmp_value);
387 
388             if(g_param_values_cmp(pspec, value, &cmp_value) != 0)
389             {
390                 if(p->prev)
391                 {
392                     p->prev->next = p->next;
393                 }
394                 else
395                 {
396                     keys = p->next;
397                 }
398 
399                 if(p->next)
400                 {
401                     p->next->prev = p->prev;
402                 }
403 
404                 temp = p;
405                 p = p->next;
406                 g_list_free1(temp);
407             }
408             else
409             {
410                 p = p->next;
411             }
412 
413             g_value_unset(&cmp_value);
414         }
415     }
416 
417     count = g_list_length(keys);
418     types = g_new(GType, count + 1);      /* ++ alloc types */
419 
420     /* Copy GType list to type array and delete the list */
421     for(p = keys, i = 0; p; p = g_list_delete_link(p, p), i++)    /* -- free keys list */
422     {
423         types[i] = GPOINTER_TO_SIZE(p->data);
424     }
425 
426     types[i] = 0;
427 
428     if(n_types)
429     {
430         *n_types = count;
431     }
432 
433     return (types);     /* !! caller takes over alloc */
434 }
435 
436 /**
437  * ipatch_type_set:
438  * @type: GType to set properties of
439  * @first_property_name: Name of first property to set
440  * @...: Value of first property to set and optionally followed by more
441  *   property name/value pairs, terminated with %NULL name.
442  *
443  * Set GType properties.  GType properties are used to associate arbitrary
444  * information with GTypes.
445  */
446 void
ipatch_type_set(GType type,const char * first_property_name,...)447 ipatch_type_set(GType type, const char *first_property_name, ...)
448 {
449     va_list args;
450 
451     va_start(args, first_property_name);
452     ipatch_type_set_valist(type, first_property_name, args);
453     va_end(args);
454 }
455 
456 /**
457  * ipatch_type_set_valist:
458  * @type: GType to set properties of
459  * @first_property_name: Name of first property to set
460  * @args: Value of first property to set and optionally followed by more
461  *   property name/value pairs, terminated with %NULL name.
462  *
463  * Like ipatch_type_set() but uses a va_list.
464  */
465 void
ipatch_type_set_valist(GType type,const char * first_property_name,va_list args)466 ipatch_type_set_valist(GType type, const char *first_property_name,
467                        va_list args)
468 {
469     const char *name;
470     GValue value = { 0 };
471     gchar *error = NULL;
472     GParamSpec *prop_spec;
473 
474     g_return_if_fail(type != 0);
475     g_return_if_fail(first_property_name != NULL);
476 
477     name = first_property_name;
478 
479     while(name)
480     {
481         prop_spec = ipatch_type_find_property(name);
482 
483         if(!prop_spec)
484         {
485             g_warning("%s: no type property named `%s'",
486                       G_STRLOC, name);
487             break;
488         }
489 
490         if(!(prop_spec->flags & G_PARAM_WRITABLE))
491         {
492             g_warning("%s: type property `%s' is not writable",
493                       G_STRLOC, prop_spec->name);
494             break;
495         }
496 
497         g_value_init(&value, G_PARAM_SPEC_VALUE_TYPE(prop_spec));
498         G_VALUE_COLLECT(&value, args, 0, &error);
499 
500         if(error)
501         {
502             g_warning("%s: %s", G_STRLOC, error);
503             g_free(error);
504 
505             /* we purposely leak the GValue contents here, it might not be
506              * in a sane state if an error condition occured */
507             break;
508         }
509 
510         type_set_property(type, prop_spec, &value, NULL, NULL, NULL);
511 
512         g_value_unset(&value);
513         name = va_arg(args, char *);
514     }
515 }
516 
517 /**
518  * ipatch_type_set_property:
519  * @type: GType to set property of
520  * @property_name: Name of property to set
521  * @value: Value to set the the property to. The value's
522  *   type must be the same as the GType property's type.
523  *
524  * Set a single property of a #GType.
525  */
526 void
ipatch_type_set_property(GType type,const char * property_name,const GValue * value)527 ipatch_type_set_property(GType type, const char *property_name,
528                          const GValue *value)
529 {
530     GParamSpec *prop_spec;
531 
532     g_return_if_fail(type != 0);
533     g_return_if_fail(property_name != NULL);
534     g_return_if_fail(G_IS_VALUE(value));
535 
536     prop_spec = ipatch_type_find_property(property_name);
537 
538     if(!prop_spec)
539     {
540         g_warning("%s: no type property named `%s'",
541                   G_STRLOC, property_name);
542         return;
543     }
544 
545     if(!(prop_spec->flags & G_PARAM_WRITABLE))
546     {
547         g_warning("%s: type property `%s' is not writable",
548                   G_STRLOC, property_name);
549         return;
550     }
551 
552     if(G_VALUE_TYPE(value) == G_PARAM_SPEC_VALUE_TYPE(prop_spec))
553     {
554         g_warning("%s: value type should be '%s' but is '%s'",
555                   G_STRLOC, g_type_name(G_PARAM_SPEC_VALUE_TYPE(prop_spec)),
556                   G_VALUE_TYPE_NAME(value));
557         return;
558     }
559 
560     type_set_property(type, prop_spec, value, NULL, NULL, NULL);
561 }
562 
563 /* does the actual setting of a GType property, note that the value is
564    is copied and not used directly */
565 static void
type_set_property(GType type,GParamSpec * prop_spec,const GValue * value,IpatchTypePropGetFunc func,GDestroyNotify notify_func,gpointer user_data)566 type_set_property(GType type, GParamSpec *prop_spec, const GValue *value,
567                   IpatchTypePropGetFunc func, GDestroyNotify notify_func,
568                   gpointer user_data)
569 {
570     TypePropValueKey *key;
571     TypePropValueVal *val;
572 
573     key = g_new(TypePropValueKey, 1);
574     key->type = type;
575     key->spec = prop_spec;
576 
577     val = g_slice_new0(TypePropValueVal);
578 
579     if(value)
580     {
581         g_value_init(&val->value, G_VALUE_TYPE(value));
582         g_value_copy(value, &val->value);
583     }
584 
585     val->func = func;
586     val->notify_func = notify_func;
587     val->user_data = user_data;
588 
589     /* value is taken over by the hash table */
590     G_LOCK(type_prop_value_hash);
591     g_hash_table_insert(type_prop_value_hash, key, val);
592     G_UNLOCK(type_prop_value_hash);
593 }
594 
595 /**
596  * ipatch_type_unset_property:
597  * @type: GType to unset a property of
598  * @property_name: The property to unset
599  *
600  * Unsets the value or dynamic function of a type property.
601  *
602  * Since: 1.1.0
603  */
604 void
ipatch_type_unset_property(GType type,const char * property_name)605 ipatch_type_unset_property(GType type, const char *property_name)
606 {
607     GParamSpec *prop_spec;
608     TypePropValueKey key;
609 
610     g_return_if_fail(type != 0);
611     g_return_if_fail(property_name != NULL);
612 
613     prop_spec = ipatch_type_find_property(property_name);
614 
615     if(!prop_spec)
616     {
617         g_warning("%s: no type property named `%s'",
618                   G_STRLOC, property_name);
619         return;
620     }
621 
622     if(!(prop_spec->flags & G_PARAM_WRITABLE))
623     {
624         g_warning("%s: type property `%s' is not writable",
625                   G_STRLOC, property_name);
626         return;
627     }
628 
629     key.type = type;
630     key.spec = prop_spec;
631 
632     G_LOCK(type_prop_value_hash);
633     g_hash_table_remove(type_prop_value_hash, &key);
634     G_UNLOCK(type_prop_value_hash);
635 }
636 
637 /**
638  * ipatch_type_get:
639  * @type: GType to get properties from
640  * @first_property_name: Name of first property to get
641  * @...: Pointer to store first property value and optionally followed
642  *   by more property name/value pairs, terminated with %NULL name.
643  *
644  * Get GType property values.
645  */
646 void
ipatch_type_get(GType type,const char * first_property_name,...)647 ipatch_type_get(GType type, const char *first_property_name, ...)
648 {
649     va_list args;
650 
651     va_start(args, first_property_name);
652     ipatch_type_get_valist(type, first_property_name, args);
653     va_end(args);
654 }
655 
656 /**
657  * ipatch_type_get_valist:
658  * @type: GType to get properties from
659  * @first_property_name: Name of first property to get
660  * @args: Pointer to store first property value and optionally followed
661  *   by more property name/value pairs, terminated with %NULL name.
662  *
663  * Like ipatch_type_get() but uses a va_list.
664  */
665 void
ipatch_type_get_valist(GType type,const char * first_property_name,va_list args)666 ipatch_type_get_valist(GType type, const char *first_property_name,
667                        va_list args)
668 {
669     const char *name;
670 
671     g_return_if_fail(type != 0);
672     g_return_if_fail(first_property_name != NULL);
673 
674     name = first_property_name;
675 
676     while(name)
677     {
678         GValue value = { 0, };
679         GParamSpec *prop_spec;
680         char *error;
681 
682         prop_spec = ipatch_type_find_property(name);
683 
684         if(!prop_spec)
685         {
686             g_warning("%s: no type property named `%s'",
687                       G_STRLOC, name);
688             break;
689         }
690 
691         if(!(prop_spec->flags & G_PARAM_READABLE))
692         {
693             g_warning("%s: type property `%s' is not readable",
694                       G_STRLOC, prop_spec->name);
695             break;
696         }
697 
698         g_value_init(&value, G_PARAM_SPEC_VALUE_TYPE(prop_spec));
699 
700         type_get_property(type, prop_spec, &value, NULL);
701         G_VALUE_LCOPY(&value, args, 0, &error);
702 
703         if(error)
704         {
705             g_warning("%s: %s", G_STRLOC, error);
706             g_free(error);
707             g_value_unset(&value);
708             break;
709         }
710 
711         g_value_unset(&value);
712         name = va_arg(args, char *);
713     }
714 }
715 
716 /**
717  * ipatch_type_get_property:
718  * @type: GType to get property from
719  * @property_name: Name of property to get
720  * @value: (out): An initialized value to store the property value in. The value's
721  *   type must be a type that the property can be transformed to.
722  *
723  * Get a single property from a #GType.
724  */
725 void
ipatch_type_get_property(GType type,const char * property_name,GValue * value)726 ipatch_type_get_property(GType type, const char *property_name,
727                          GValue *value)
728 {
729     GParamSpec *prop_spec;
730 
731     g_return_if_fail(type != 0);
732     g_return_if_fail(property_name != NULL);
733     g_return_if_fail(G_IS_VALUE(value));
734 
735     prop_spec = ipatch_type_find_property(property_name);
736 
737     if(!prop_spec)
738         g_warning("%s: no type property named `%s'",
739                   G_STRLOC, property_name);
740     else if(!(prop_spec->flags & G_PARAM_READABLE))
741         g_warning("%s: type property `%s' is not readable",
742                   G_STRLOC, prop_spec->name);
743     else
744     {
745         GValue *prop_value, tmp_value = { 0, };
746 
747         /* auto-conversion of the callers value type */
748         if(G_VALUE_TYPE(value) == G_PARAM_SPEC_VALUE_TYPE(prop_spec))
749         {
750             g_value_reset(value);
751             prop_value = value;
752         }
753         else if(!g_value_type_transformable(G_PARAM_SPEC_VALUE_TYPE(prop_spec),
754                                             G_VALUE_TYPE(value)))
755         {
756             g_warning("can't retrieve type property `%s' of type"
757                       " `%s' as value of type `%s'", prop_spec->name,
758                       g_type_name(G_PARAM_SPEC_VALUE_TYPE(prop_spec)),
759                       G_VALUE_TYPE_NAME(value));
760             return;
761         }
762         else
763         {
764             g_value_init(&tmp_value, G_PARAM_SPEC_VALUE_TYPE(prop_spec));
765             prop_value = &tmp_value;
766         }
767 
768         type_get_property(type, prop_spec, prop_value, NULL);
769 
770         if(prop_value != value)
771         {
772             g_value_transform(prop_value, value);
773             g_value_unset(&tmp_value);
774         }
775     }
776 }
777 
778 /* does the actual getting of a GType property */
779 static void
type_get_property(GType type,GParamSpec * prop_spec,GValue * value,GObject * object)780 type_get_property(GType type, GParamSpec *prop_spec, GValue *value,
781                   GObject *object)
782 {
783     TypePropValueKey key;
784     TypePropValueVal *val;
785 
786     key.type = type;
787     key.spec = prop_spec;
788 
789     G_LOCK(type_prop_value_hash);
790     val = g_hash_table_lookup(type_prop_value_hash, &key);
791     G_UNLOCK(type_prop_value_hash);
792 
793     if(val)
794     {
795         if(val->func)
796         {
797             val->func(type, prop_spec, value, object);
798         }
799         else
800         {
801             g_value_copy(&val->value, value);
802         }
803     }
804     else
805     {
806         g_param_value_set_default(prop_spec, value);
807     }
808 }
809 
810 /**
811  * ipatch_type_object_get:
812  * @object: Object instance to get type property from
813  * @first_property_name: Name of first property to get
814  * @...: Pointer to store first property value and optionally followed
815  *   by more property name/value pairs, terminated with %NULL name.
816  *
817  * Get GType property values.  Like ipatch_type_get() but takes an object
818  * instance which can be used by any registered dynamic type property
819  * functions.
820  */
821 void
ipatch_type_object_get(GObject * object,const char * first_property_name,...)822 ipatch_type_object_get(GObject *object, const char *first_property_name, ...)
823 {
824     va_list args;
825 
826     va_start(args, first_property_name);
827     ipatch_type_object_get_valist(object, first_property_name, args);
828     va_end(args);
829 }
830 
831 /*
832  * ipatch_type_object_get_valist:
833  * @object: Object instance to get type property from
834  * @first_property_name: Name of first property to get
835  * @args: Pointer to store first property value and optionally followed
836  *   by more property name/value pairs, terminated with %NULL name.
837  *
838  * Like ipatch_type_object_get() but uses a va_list.
839  */
840 void
ipatch_type_object_get_valist(GObject * object,const char * first_property_name,va_list args)841 ipatch_type_object_get_valist(GObject *object, const char *first_property_name,
842                               va_list args)
843 {
844     GType type;
845     const char *name;
846 
847     g_return_if_fail(G_IS_OBJECT(object));
848     g_return_if_fail(first_property_name != NULL);
849 
850     type = G_OBJECT_TYPE(object);
851     g_return_if_fail(type != 0);
852 
853     name = first_property_name;
854 
855     while(name)
856     {
857         GValue value = { 0, };
858         GParamSpec *prop_spec;
859         char *error;
860 
861         prop_spec = ipatch_type_find_property(name);
862 
863         if(!prop_spec)
864         {
865             g_warning("%s: no type property named `%s'",
866                       G_STRLOC, name);
867             break;
868         }
869 
870         if(!(prop_spec->flags & G_PARAM_READABLE))
871         {
872             g_warning("%s: type property `%s' is not readable",
873                       G_STRLOC, prop_spec->name);
874             break;
875         }
876 
877         g_value_init(&value, G_PARAM_SPEC_VALUE_TYPE(prop_spec));
878 
879         type_get_property(type, prop_spec, &value, object);
880         G_VALUE_LCOPY(&value, args, 0, &error);
881 
882         if(error)
883         {
884             g_warning("%s: %s", G_STRLOC, error);
885             g_free(error);
886             g_value_unset(&value);
887             break;
888         }
889 
890         g_value_unset(&value);
891         name = va_arg(args, char *);
892     }
893 }
894 
895 /**
896  * ipatch_type_object_get_property:
897  * @object: Object instance to get type property from
898  * @property_name: Name of property to get
899  * @value: (out): An initialized value to store the property value in. The value's
900  *   type must be a type that the property can be transformed to.
901  *
902  * Get a single type property from an @object instance.
903  */
904 void
ipatch_type_object_get_property(GObject * object,const char * property_name,GValue * value)905 ipatch_type_object_get_property(GObject *object, const char *property_name,
906                                 GValue *value)
907 {
908     GParamSpec *prop_spec;
909     GType type;
910 
911     g_return_if_fail(G_IS_OBJECT(object));
912     g_return_if_fail(property_name != NULL);
913     g_return_if_fail(G_IS_VALUE(value));
914 
915     type = G_OBJECT_TYPE(object);
916     g_return_if_fail(type != 0);
917 
918     prop_spec = ipatch_type_find_property(property_name);
919 
920     if(!prop_spec)
921         g_warning("%s: no type property named `%s'",
922                   G_STRLOC, property_name);
923     else if(!(prop_spec->flags & G_PARAM_READABLE))
924         g_warning("%s: type property `%s' is not readable",
925                   G_STRLOC, prop_spec->name);
926     else
927     {
928         GValue *prop_value, tmp_value = { 0, };
929 
930         /* auto-conversion of the callers value type */
931         if(G_VALUE_TYPE(value) == G_PARAM_SPEC_VALUE_TYPE(prop_spec))
932         {
933             g_value_reset(value);
934             prop_value = value;
935         }
936         else if(!g_value_type_transformable(G_PARAM_SPEC_VALUE_TYPE(prop_spec),
937                                             G_VALUE_TYPE(value)))
938         {
939             g_warning("can't retrieve type property `%s' of type"
940                       " `%s' as value of type `%s'", prop_spec->name,
941                       g_type_name(G_PARAM_SPEC_VALUE_TYPE(prop_spec)),
942                       G_VALUE_TYPE_NAME(value));
943             return;
944         }
945         else
946         {
947             g_value_init(&tmp_value, G_PARAM_SPEC_VALUE_TYPE(prop_spec));
948             prop_value = &tmp_value;
949         }
950 
951         type_get_property(type, prop_spec, prop_value, object);
952 
953         if(prop_value != value)
954         {
955             g_value_transform(prop_value, value);
956             g_value_unset(&tmp_value);
957         }
958     }
959 }
960 
961 /**
962  * ipatch_type_set_dynamic_func: (skip)
963  * @type: GType of the type property
964  * @property_name: Name of a previously registered type property
965  * @func: Callback function used for getting values for this type property
966  *
967  * Registers a callback function for dynamically getting the value of a
968  * type property.
969  *
970  * Example: A dynamic property is created for SoundFont presets to return a
971  * different "virtual-parent-type" depending on if its a percussion or
972  * melodic preset (determined from a preset's bank number).
973  */
974 void
ipatch_type_set_dynamic_func(GType type,const char * property_name,IpatchTypePropGetFunc func)975 ipatch_type_set_dynamic_func(GType type, const char *property_name,
976                              IpatchTypePropGetFunc func)
977 {
978     ipatch_type_set_dynamic_func_full(type, property_name, func, NULL, NULL);
979 }
980 
981 /**
982  * ipatch_type_set_dynamic_func_full: (rename-to ipatch_type_set_dynamic_func)
983  * @type: GType of the type property
984  * @property_name: Name of a previously registered type property
985  * @func: Callback function used for getting values for this type property
986  * @notify_func: (nullable) (scope async) (closure user_data): Destroy function
987  *   callback when @func is removed
988  * @user_data: (nullable): Data passed to @notify_func
989  *
990  * Registers a callback function for dynamically getting the value of a
991  * type property.  Like ipatch_type_set_dynamic_func() but more GObject Introspection
992  * friendly.
993  *
994  * Example: A dynamic property is created for SoundFont presets to return a
995  * different "virtual-parent-type" depending on if its a percussion or
996  * melodic preset (determined from a preset's bank number).
997  *
998  * Since: 1.1.0
999  */
1000 void
ipatch_type_set_dynamic_func_full(GType type,const char * property_name,IpatchTypePropGetFunc func,GDestroyNotify notify_func,gpointer user_data)1001 ipatch_type_set_dynamic_func_full(GType type, const char *property_name,
1002                                   IpatchTypePropGetFunc func,
1003                                   GDestroyNotify notify_func, gpointer user_data)
1004 {
1005     GParamSpec *prop_spec;
1006 
1007     g_return_if_fail(type != 0);
1008     g_return_if_fail(property_name != NULL);
1009 
1010     prop_spec = ipatch_type_find_property(property_name);
1011 
1012     if(!prop_spec)
1013     {
1014         g_warning("%s: no type property named `%s'",
1015                   G_STRLOC, property_name);
1016         return;
1017     }
1018 
1019     type_set_property(type, prop_spec, NULL, func, notify_func, user_data);
1020 }
1021 
1022 /**
1023  * ipatch_type_get_dynamic_func: (skip)
1024  * @type: GType of the type property
1025  * @property_name: Name of a previously registered type property
1026  *
1027  * Get a the dynamic function registered for a given @type and @property_name
1028  * with ipatch_type_set_dynamic_func().  Also can be used as an indicator of
1029  * whether a type property is dynamic or not.
1030  *
1031  * Returns: Pointer to the registered function or %NULL if no function
1032  *   registered (not a dynamic type property).
1033  */
1034 IpatchTypePropGetFunc
ipatch_type_get_dynamic_func(GType type,const char * property_name)1035 ipatch_type_get_dynamic_func(GType type, const char *property_name)
1036 {
1037     GParamSpec *type_prop_pspec;
1038     TypePropValueKey key;
1039     TypePropValueVal *val;
1040 
1041     type_prop_pspec = ipatch_type_find_property(property_name);
1042     g_return_val_if_fail(type_prop_pspec != NULL, NULL);
1043 
1044     key.type = type;
1045     key.spec = type_prop_pspec;
1046 
1047     G_LOCK(type_prop_value_hash);
1048     val = g_hash_table_lookup(type_prop_value_hash, &key);
1049     G_UNLOCK(type_prop_value_hash);
1050 
1051     return (val ? val->func : NULL);
1052 }
1053