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