Move some docs inline.
[platform/upstream/glib.git] / gobject / gparam.c
1 /* GObject - GLib Type, Object, Parameter and Signal Library
2  * Copyright (C) 1997-1999, 2000-2001 Tim Janik and Red Hat, Inc.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General
15  * Public License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * MT safe
22  */
23
24 #include        "gparam.h"
25
26
27 #include        "gvaluecollector.h"
28 #include        <string.h>
29
30
31
32 /* --- defines --- */
33 #define G_PARAM_USER_MASK                       (~0 << G_PARAM_USER_SHIFT)
34 #define PSPEC_APPLIES_TO_VALUE(pspec, value)    (G_TYPE_CHECK_VALUE_TYPE ((value), G_PARAM_SPEC_VALUE_TYPE (pspec)))
35 #define G_SLOCK(mutex)                          g_static_mutex_lock (mutex)
36 #define G_SUNLOCK(mutex)                        g_static_mutex_unlock (mutex)
37
38
39 /* --- prototypes --- */
40 static void     g_param_spec_class_base_init     (GParamSpecClass       *class);
41 static void     g_param_spec_class_base_finalize (GParamSpecClass       *class);
42 static void     g_param_spec_class_init          (GParamSpecClass       *class,
43                                                   gpointer               class_data);
44 static void     g_param_spec_init                (GParamSpec            *pspec,
45                                                   GParamSpecClass       *class);
46 static void     g_param_spec_finalize            (GParamSpec            *pspec);
47 static void     value_param_init                (GValue         *value);
48 static void     value_param_free_value          (GValue         *value);
49 static void     value_param_copy_value          (const GValue   *src_value,
50                                                  GValue         *dest_value);
51 static void     value_param_transform_value     (const GValue   *src_value,
52                                                  GValue         *dest_value);
53 static gpointer value_param_peek_pointer        (const GValue   *value);
54 static gchar*   value_param_collect_value       (GValue         *value,
55                                                  guint           n_collect_values,
56                                                  GTypeCValue    *collect_values,
57                                                  guint           collect_flags);
58 static gchar*   value_param_lcopy_value         (const GValue   *value,
59                                                  guint           n_collect_values,
60                                                  GTypeCValue    *collect_values,
61                                                  guint           collect_flags);
62
63
64 /* --- variables --- */
65 static GQuark quark_floating = 0;
66 G_LOCK_DEFINE_STATIC (pspec_ref_count);
67
68
69 /* --- functions --- */
70 void
71 g_param_type_init (void)        /* sync with gtype.c */
72 {
73   static const GTypeFundamentalInfo finfo = {
74     (G_TYPE_FLAG_CLASSED |
75      G_TYPE_FLAG_INSTANTIATABLE |
76      G_TYPE_FLAG_DERIVABLE |
77      G_TYPE_FLAG_DEEP_DERIVABLE),
78   };
79   static const GTypeValueTable param_value_table = {
80     value_param_init,           /* value_init */
81     value_param_free_value,     /* value_free */
82     value_param_copy_value,     /* value_copy */
83     value_param_peek_pointer,   /* value_peek_pointer */
84     "p",                        /* collect_format */
85     value_param_collect_value,  /* collect_value */
86     "p",                        /* lcopy_format */
87     value_param_lcopy_value,    /* lcopy_value */
88   };
89   static const GTypeInfo param_spec_info = {
90     sizeof (GParamSpecClass),
91
92     (GBaseInitFunc) g_param_spec_class_base_init,
93     (GBaseFinalizeFunc) g_param_spec_class_base_finalize,
94     (GClassInitFunc) g_param_spec_class_init,
95     (GClassFinalizeFunc) NULL,
96     NULL,       /* class_data */
97
98     sizeof (GParamSpec),
99     0,          /* n_preallocs */
100     (GInstanceInitFunc) g_param_spec_init,
101
102     &param_value_table,
103   };
104   GType type;
105
106   type = g_type_register_fundamental (G_TYPE_PARAM, "GParam", &param_spec_info, &finfo, G_TYPE_FLAG_ABSTRACT);
107   g_assert (type == G_TYPE_PARAM);
108   g_value_register_transform_func (G_TYPE_PARAM, G_TYPE_PARAM, value_param_transform_value);
109 }
110
111 static void
112 g_param_spec_class_base_init (GParamSpecClass *class)
113 {
114 }
115
116 static void
117 g_param_spec_class_base_finalize (GParamSpecClass *class)
118 {
119 }
120
121 static void
122 g_param_spec_class_init (GParamSpecClass *class,
123                          gpointer         class_data)
124 {
125   quark_floating = g_quark_from_static_string ("GParamSpec-floating");
126
127   class->value_type = G_TYPE_NONE;
128   class->finalize = g_param_spec_finalize;
129   class->value_set_default = NULL;
130   class->value_validate = NULL;
131   class->values_cmp = NULL;
132 }
133
134 static void
135 g_param_spec_init (GParamSpec      *pspec,
136                    GParamSpecClass *class)
137 {
138   pspec->name = NULL;
139   pspec->_nick = NULL;
140   pspec->_blurb = NULL;
141   pspec->flags = 0;
142   pspec->value_type = class->value_type;
143   pspec->owner_type = 0;
144   pspec->qdata = NULL;
145   pspec->ref_count = 1;
146   pspec->param_id = 0;
147   g_datalist_id_set_data (&pspec->qdata, quark_floating, GUINT_TO_POINTER (TRUE));
148 }
149
150 static void
151 g_param_spec_finalize (GParamSpec *pspec)
152 {
153   g_datalist_clear (&pspec->qdata);
154   
155   g_free (pspec->name);
156   g_free (pspec->_nick);
157   g_free (pspec->_blurb);
158
159   g_type_free_instance ((GTypeInstance*) pspec);
160 }
161
162 GParamSpec*
163 g_param_spec_ref (GParamSpec *pspec)
164 {
165   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL);
166
167   G_LOCK (pspec_ref_count);
168   if (pspec->ref_count > 0)
169     {
170       pspec->ref_count += 1;
171       G_UNLOCK (pspec_ref_count);
172     }
173   else
174     {
175       G_UNLOCK (pspec_ref_count);
176       g_return_val_if_fail (pspec->ref_count > 0, NULL);
177     }
178   
179   return pspec;
180 }
181
182 void
183 g_param_spec_unref (GParamSpec *pspec)
184 {
185   g_return_if_fail (G_IS_PARAM_SPEC (pspec));
186
187   G_LOCK (pspec_ref_count);
188   if (pspec->ref_count > 0)
189     {
190       gboolean need_finalize;
191
192       /* sync with _sink */
193       pspec->ref_count -= 1;
194       need_finalize = pspec->ref_count == 0;
195       G_UNLOCK (pspec_ref_count);
196       if (need_finalize)
197         G_PARAM_SPEC_GET_CLASS (pspec)->finalize (pspec);
198     }
199   else
200     {
201       G_UNLOCK (pspec_ref_count);
202       g_return_if_fail (pspec->ref_count > 0);
203     }
204 }
205
206 void
207 g_param_spec_sink (GParamSpec *pspec)
208 {
209   g_return_if_fail (G_IS_PARAM_SPEC (pspec));
210
211   G_LOCK (pspec_ref_count);
212   if (pspec->ref_count > 0)
213     {
214       if (g_datalist_id_remove_no_notify (&pspec->qdata, quark_floating))
215         {
216           /* sync with _unref */
217           if (pspec->ref_count > 1)
218             pspec->ref_count -= 1;
219           else
220             {
221               G_UNLOCK (pspec_ref_count);
222               g_param_spec_unref (pspec);
223
224               return;
225             }
226         }
227       G_UNLOCK (pspec_ref_count);
228     }
229   else
230     {
231       G_UNLOCK (pspec_ref_count);
232       g_return_if_fail (pspec->ref_count > 0);
233     }
234 }
235
236 G_CONST_RETURN gchar*
237 g_param_spec_get_name (GParamSpec *pspec)
238 {
239   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL);
240
241   return pspec->name;
242 }
243
244 G_CONST_RETURN gchar*
245 g_param_spec_get_nick (GParamSpec *pspec)
246 {
247   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL);
248
249   return pspec->_nick ? pspec->_nick : pspec->name;
250 }
251
252 G_CONST_RETURN gchar*
253 g_param_spec_get_blurb (GParamSpec *pspec)
254 {
255   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL);
256
257   return pspec->_blurb;
258 }
259
260 static void
261 canonicalize_key (gchar *key)
262 {
263   gchar *p;
264   
265   for (p = key; *p != 0; p++)
266     {
267       gchar c = *p;
268       
269       if (c != '-' &&
270           (c < '0' || c > '9') &&
271           (c < 'A' || c > 'Z') &&
272           (c < 'a' || c > 'z'))
273         *p = '-';
274     }
275 }
276
277 /**
278  * g_param_spec_internal:
279  * @param_type: the #GType for the property; must be derived from #G_TYPE_PARAM
280  * @name: the canonical name of the property
281  * @nick: the nickname of the property
282  * @blurb: a short description of the property
283  * @flags: a combination of #GParamFlags 
284  * 
285  * Creates a new #GParamSpec instance.
286  *
287  * A property name consists of segments consisting of ASCII letters and
288  * digits, separated by either the '-' or '_' character. The first
289  * character of a property name must be a letter. Names which violate these
290  * rules lead to undefined behaviour. 
291  *
292  * When creating and looking up a #GParamSpec<!-- -->s, either separator can
293  * be used, but they cannot be mixed. Using '-' is considerably more efficient
294  * and in fact <emphasis>required</emphasis> when using property names as 
295  * detail strings for signals.
296  * 
297  * Return value: a newly allocated #GParamSpec instance
298  **/
299 gpointer
300 g_param_spec_internal (GType        param_type,
301                        const gchar *name,
302                        const gchar *nick,
303                        const gchar *blurb,
304                        GParamFlags  flags)
305 {
306   GParamSpec *pspec;
307   
308   g_return_val_if_fail (G_TYPE_IS_PARAM (param_type) && param_type != G_TYPE_PARAM, NULL);
309   g_return_val_if_fail (name != NULL, NULL);
310   g_return_val_if_fail ((name[0] >= 'A' && name[0] <= 'Z') || (name[0] >= 'a' && name[0] <= 'z'), NULL);
311   
312   pspec = (gpointer) g_type_create_instance (param_type);
313   pspec->name = g_strdup (name);
314   canonicalize_key (pspec->name);
315   pspec->_nick = g_strdup (nick);
316   pspec->_blurb = g_strdup (blurb);
317   pspec->flags = (flags & G_PARAM_USER_MASK) | (flags & G_PARAM_MASK);
318   
319   return pspec;
320 }
321
322 gpointer
323 g_param_spec_get_qdata (GParamSpec *pspec,
324                         GQuark      quark)
325 {
326   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL);
327   
328   return quark ? g_datalist_id_get_data (&pspec->qdata, quark) : NULL;
329 }
330
331 void
332 g_param_spec_set_qdata (GParamSpec *pspec,
333                         GQuark      quark,
334                         gpointer    data)
335 {
336   g_return_if_fail (G_IS_PARAM_SPEC (pspec));
337   g_return_if_fail (quark > 0);
338
339   g_datalist_id_set_data (&pspec->qdata, quark, data);
340 }
341
342 void
343 g_param_spec_set_qdata_full (GParamSpec    *pspec,
344                              GQuark         quark,
345                              gpointer       data,
346                              GDestroyNotify destroy)
347 {
348   g_return_if_fail (G_IS_PARAM_SPEC (pspec));
349   g_return_if_fail (quark > 0);
350
351   g_datalist_id_set_data_full (&pspec->qdata, quark, data, data ? destroy : (GDestroyNotify) NULL);
352 }
353
354 gpointer
355 g_param_spec_steal_qdata (GParamSpec *pspec,
356                           GQuark      quark)
357 {
358   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL);
359   g_return_val_if_fail (quark > 0, NULL);
360   
361   return g_datalist_id_remove_no_notify (&pspec->qdata, quark);
362 }
363
364 void
365 g_param_value_set_default (GParamSpec *pspec,
366                            GValue     *value)
367 {
368   g_return_if_fail (G_IS_PARAM_SPEC (pspec));
369   g_return_if_fail (G_IS_VALUE (value));
370   g_return_if_fail (PSPEC_APPLIES_TO_VALUE (pspec, value));
371
372   g_value_reset (value);
373   G_PARAM_SPEC_GET_CLASS (pspec)->value_set_default (pspec, value);
374 }
375
376 gboolean
377 g_param_value_defaults (GParamSpec *pspec,
378                         GValue     *value)
379 {
380   GValue dflt_value = { 0, };
381   gboolean defaults;
382
383   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), FALSE);
384   g_return_val_if_fail (G_IS_VALUE (value), FALSE);
385   g_return_val_if_fail (PSPEC_APPLIES_TO_VALUE (pspec, value), FALSE);
386
387   g_value_init (&dflt_value, G_PARAM_SPEC_VALUE_TYPE (pspec));
388   G_PARAM_SPEC_GET_CLASS (pspec)->value_set_default (pspec, &dflt_value);
389   defaults = G_PARAM_SPEC_GET_CLASS (pspec)->values_cmp (pspec, value, &dflt_value) == 0;
390   g_value_unset (&dflt_value);
391
392   return defaults;
393 }
394
395 gboolean
396 g_param_value_validate (GParamSpec *pspec,
397                         GValue     *value)
398 {
399   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), FALSE);
400   g_return_val_if_fail (G_IS_VALUE (value), FALSE);
401   g_return_val_if_fail (PSPEC_APPLIES_TO_VALUE (pspec, value), FALSE);
402
403   if (G_PARAM_SPEC_GET_CLASS (pspec)->value_validate)
404     {
405       GValue oval = *value;
406
407       if (G_PARAM_SPEC_GET_CLASS (pspec)->value_validate (pspec, value) ||
408           memcmp (&oval.data, &value->data, sizeof (oval.data)))
409         return TRUE;
410     }
411
412   return FALSE;
413 }
414
415 gboolean
416 g_param_value_convert (GParamSpec   *pspec,
417                        const GValue *src_value,
418                        GValue       *dest_value,
419                        gboolean      strict_validation)
420 {
421   GValue tmp_value = { 0, };
422
423   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), FALSE);
424   g_return_val_if_fail (G_IS_VALUE (src_value), FALSE);
425   g_return_val_if_fail (G_IS_VALUE (dest_value), FALSE);
426   g_return_val_if_fail (PSPEC_APPLIES_TO_VALUE (pspec, dest_value), FALSE);
427
428   /* better leave dest_value untouched when returning FALSE */
429
430   g_value_init (&tmp_value, G_VALUE_TYPE (dest_value));
431   if (g_value_transform (src_value, &tmp_value) &&
432       (!g_param_value_validate (pspec, &tmp_value) || !strict_validation))
433     {
434       g_value_unset (dest_value);
435       
436       /* values are relocatable */
437       memcpy (dest_value, &tmp_value, sizeof (tmp_value));
438       
439       return TRUE;
440     }
441   else
442     {
443       g_value_unset (&tmp_value);
444       
445       return FALSE;
446     }
447 }
448
449 gint
450 g_param_values_cmp (GParamSpec   *pspec,
451                     const GValue *value1,
452                     const GValue *value2)
453 {
454   gint cmp;
455
456   /* param_values_cmp() effectively does: value1 - value2
457    * so the return values are:
458    * -1)  value1 < value2
459    *  0)  value1 == value2
460    *  1)  value1 > value2
461    */
462   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), 0);
463   g_return_val_if_fail (G_IS_VALUE (value1), 0);
464   g_return_val_if_fail (G_IS_VALUE (value2), 0);
465   g_return_val_if_fail (PSPEC_APPLIES_TO_VALUE (pspec, value1), 0);
466   g_return_val_if_fail (PSPEC_APPLIES_TO_VALUE (pspec, value2), 0);
467
468   cmp = G_PARAM_SPEC_GET_CLASS (pspec)->values_cmp (pspec, value1, value2);
469
470   return CLAMP (cmp, -1, 1);
471 }
472
473 static void
474 value_param_init (GValue *value)
475 {
476   value->data[0].v_pointer = NULL;
477 }
478
479 static void
480 value_param_free_value (GValue *value)
481 {
482   if (value->data[0].v_pointer)
483     g_param_spec_unref (value->data[0].v_pointer);
484 }
485
486 static void
487 value_param_copy_value (const GValue *src_value,
488                         GValue       *dest_value)
489 {
490   if (src_value->data[0].v_pointer)
491     dest_value->data[0].v_pointer = g_param_spec_ref (src_value->data[0].v_pointer);
492   else
493     dest_value->data[0].v_pointer = NULL;
494 }
495
496 static void
497 value_param_transform_value (const GValue *src_value,
498                              GValue       *dest_value)
499 {
500   if (src_value->data[0].v_pointer &&
501       g_type_is_a (G_PARAM_SPEC_TYPE (dest_value->data[0].v_pointer), G_VALUE_TYPE (dest_value)))
502     dest_value->data[0].v_pointer = g_param_spec_ref (src_value->data[0].v_pointer);
503   else
504     dest_value->data[0].v_pointer = NULL;
505 }
506
507 static gpointer
508 value_param_peek_pointer (const GValue *value)
509 {
510   return value->data[0].v_pointer;
511 }
512
513 static gchar*
514 value_param_collect_value (GValue      *value,
515                            guint        n_collect_values,
516                            GTypeCValue *collect_values,
517                            guint        collect_flags)
518 {
519   if (collect_values[0].v_pointer)
520     {
521       GParamSpec *param = collect_values[0].v_pointer;
522
523       if (param->g_type_instance.g_class == NULL)
524         return g_strconcat ("invalid unclassed param spec pointer for value type `",
525                             G_VALUE_TYPE_NAME (value),
526                             "'",
527                             NULL);
528       else if (!g_value_type_compatible (G_PARAM_SPEC_TYPE (param), G_VALUE_TYPE (value)))
529         return g_strconcat ("invalid param spec type `",
530                             G_PARAM_SPEC_TYPE_NAME (param),
531                             "' for value type `",
532                             G_VALUE_TYPE_NAME (value),
533                             "'",
534                             NULL);
535       value->data[0].v_pointer = g_param_spec_ref (param);
536     }
537   else
538     value->data[0].v_pointer = NULL;
539
540   return NULL;
541 }
542
543 static gchar*
544 value_param_lcopy_value (const GValue *value,
545                          guint         n_collect_values,
546                          GTypeCValue  *collect_values,
547                          guint         collect_flags)
548 {
549   GParamSpec **param_p = collect_values[0].v_pointer;
550
551   if (!param_p)
552     return g_strdup_printf ("value location for `%s' passed as NULL", G_VALUE_TYPE_NAME (value));
553
554   if (!value->data[0].v_pointer)
555     *param_p = NULL;
556   else if (collect_flags & G_VALUE_NOCOPY_CONTENTS)
557     *param_p = value->data[0].v_pointer;
558   else
559     *param_p = g_param_spec_ref (value->data[0].v_pointer);
560
561   return NULL;
562 }
563
564
565 /* --- param spec pool --- */
566 struct _GParamSpecPool
567 {
568   GStaticMutex smutex;
569   gboolean     type_prefixing;
570   GHashTable  *hash_table;
571 };
572
573 static guint
574 param_spec_pool_hash (gconstpointer key_spec)
575 {
576   const GParamSpec *key = key_spec;
577   const gchar *p;
578   guint h = key->owner_type;
579
580   for (p = key->name; *p; p++)
581     h = (h << 5) - h + *p;
582
583   return h;
584 }
585
586 static gboolean
587 param_spec_pool_equals (gconstpointer key_spec_1,
588                         gconstpointer key_spec_2)
589 {
590   const GParamSpec *key1 = key_spec_1;
591   const GParamSpec *key2 = key_spec_2;
592
593   return (key1->owner_type == key2->owner_type &&
594           strcmp (key1->name, key2->name) == 0);
595 }
596
597 GParamSpecPool*
598 g_param_spec_pool_new (gboolean type_prefixing)
599 {
600   static GStaticMutex init_smutex = G_STATIC_MUTEX_INIT;
601   GParamSpecPool *pool = g_new (GParamSpecPool, 1);
602
603   memcpy (&pool->smutex, &init_smutex, sizeof (init_smutex));
604   pool->type_prefixing = type_prefixing != FALSE;
605   pool->hash_table = g_hash_table_new (param_spec_pool_hash, param_spec_pool_equals);
606
607   return pool;
608 }
609
610 void
611 g_param_spec_pool_insert (GParamSpecPool *pool,
612                           GParamSpec     *pspec,
613                           GType           owner_type)
614 {
615   gchar *p;
616   
617   if (pool && pspec && owner_type > 0 && pspec->owner_type == 0)
618     {
619       G_SLOCK (&pool->smutex);
620       for (p = pspec->name; *p; p++)
621         {
622           if (!strchr (G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-_", *p))
623             {
624               g_warning (G_STRLOC ": pspec name \"%s\" contains invalid characters", pspec->name);
625               G_SUNLOCK (&pool->smutex);
626               return;
627             }
628         }
629       
630       pspec->owner_type = owner_type;
631       g_param_spec_ref (pspec);
632       g_hash_table_insert (pool->hash_table, pspec, pspec);
633       G_SUNLOCK (&pool->smutex);
634     }
635   else
636     {
637       g_return_if_fail (pool != NULL);
638       g_return_if_fail (pspec);
639       g_return_if_fail (owner_type > 0);
640       g_return_if_fail (pspec->owner_type == 0);
641     }
642 }
643
644 void
645 g_param_spec_pool_remove (GParamSpecPool *pool,
646                           GParamSpec     *pspec)
647 {
648   if (pool && pspec)
649     {
650       G_SLOCK (&pool->smutex);
651       if (g_hash_table_remove (pool->hash_table, pspec))
652         g_param_spec_unref (pspec);
653       else
654         g_warning (G_STRLOC ": attempt to remove unknown pspec `%s' from pool", pspec->name);
655       G_SUNLOCK (&pool->smutex);
656     }
657   else
658     {
659       g_return_if_fail (pool != NULL);
660       g_return_if_fail (pspec);
661     }
662 }
663
664 static inline GParamSpec*
665 param_spec_ht_lookup (GHashTable  *hash_table,
666                       const gchar *param_name,
667                       GType        owner_type,
668                       gboolean     walk_ancestors)
669 {
670   GParamSpec key, *pspec;
671
672   key.owner_type = owner_type;
673   key.name = (gchar*) param_name;
674   if (walk_ancestors)
675     do
676       {
677         pspec = g_hash_table_lookup (hash_table, &key);
678         if (pspec)
679           return pspec;
680         key.owner_type = g_type_parent (key.owner_type);
681       }
682     while (key.owner_type);
683   else
684     pspec = g_hash_table_lookup (hash_table, &key);
685
686   if (!pspec)
687     {
688       /* try canonicalized form */
689       key.name = g_strdup (param_name);
690       key.owner_type = owner_type;
691       
692       canonicalize_key (key.name);
693       if (walk_ancestors)
694         do
695           {
696             pspec = g_hash_table_lookup (hash_table, &key);
697             if (pspec)
698               {
699                 g_free (key.name);
700                 return pspec;
701               }
702             key.owner_type = g_type_parent (key.owner_type);
703           }
704         while (key.owner_type);
705       else
706         pspec = g_hash_table_lookup (hash_table, &key);
707       g_free (key.name);
708     }
709
710   return pspec;
711 }
712
713 GParamSpec*
714 g_param_spec_pool_lookup (GParamSpecPool *pool,
715                           const gchar    *param_name,
716                           GType           owner_type,
717                           gboolean        walk_ancestors)
718 {
719   GParamSpec *pspec;
720   gchar *delim;
721
722   if (!pool || !param_name)
723     {
724       g_return_val_if_fail (pool != NULL, NULL);
725       g_return_val_if_fail (param_name != NULL, NULL);
726     }
727
728   G_SLOCK (&pool->smutex);
729
730   delim = pool->type_prefixing ? strchr (param_name, ':') : NULL;
731
732   /* try quick and away, i.e. without prefix */
733   if (!delim)
734     {
735       pspec = param_spec_ht_lookup (pool->hash_table, param_name, owner_type, walk_ancestors);
736       G_SUNLOCK (&pool->smutex);
737
738       return pspec;
739     }
740
741   /* strip type prefix */
742   if (pool->type_prefixing && delim[1] == ':')
743     {
744       guint l = delim - param_name;
745       gchar stack_buffer[32], *buffer = l < 32 ? stack_buffer : g_new (gchar, l + 1);
746       GType type;
747       
748       strncpy (buffer, param_name, delim - param_name);
749       buffer[l] = 0;
750       type = g_type_from_name (buffer);
751       if (l >= 32)
752         g_free (buffer);
753       if (type)         /* type==0 isn't a valid type pefix */
754         {
755           /* sanity check, these cases don't make a whole lot of sense */
756           if ((!walk_ancestors && type != owner_type) || !g_type_is_a (owner_type, type))
757             {
758               G_SUNLOCK (&pool->smutex);
759
760               return NULL;
761             }
762           owner_type = type;
763           param_name += l + 2;
764           pspec = param_spec_ht_lookup (pool->hash_table, param_name, owner_type, walk_ancestors);
765           G_SUNLOCK (&pool->smutex);
766
767           return pspec;
768         }
769     }
770   /* malformed param_name */
771
772   G_SUNLOCK (&pool->smutex);
773
774   return NULL;
775 }
776
777 static void
778 pool_list (gpointer key,
779            gpointer value,
780            gpointer user_data)
781 {
782   GParamSpec *pspec = value;
783   gpointer *data = user_data;
784   GType owner_type = (GType) data[1];
785
786   if (owner_type == pspec->owner_type)
787     data[0] = g_list_prepend (data[0], pspec);
788 }
789
790 GList*
791 g_param_spec_pool_list_owned (GParamSpecPool *pool,
792                               GType           owner_type)
793 {
794   gpointer data[2];
795
796   g_return_val_if_fail (pool != NULL, NULL);
797   g_return_val_if_fail (owner_type > 0, NULL);
798   
799   G_SLOCK (&pool->smutex);
800   data[0] = NULL;
801   data[1] = (gpointer) owner_type;
802   g_hash_table_foreach (pool->hash_table, pool_list, &data);
803   G_SUNLOCK (&pool->smutex);
804
805   return data[0];
806 }
807
808 static gint
809 pspec_compare_id (gconstpointer a,
810                   gconstpointer b)
811 {
812   const GParamSpec *pspec1 = a, *pspec2 = b;
813
814   return pspec1->param_id < pspec2->param_id ? -1 : pspec1->param_id > pspec2->param_id;
815 }
816
817 static inline GSList*
818 pspec_list_remove_overridden (GSList     *plist,
819                               GHashTable *ht,
820                               GType       owner_type,
821                               guint      *n_p)
822 {
823   GSList *rlist = NULL;
824
825   while (plist)
826     {
827       GSList *tmp = plist->next;
828       GParamSpec *pspec = plist->data;
829
830       if (param_spec_ht_lookup (ht, pspec->name, owner_type, TRUE) != pspec)
831         g_slist_free_1 (plist);
832       else
833         {
834           plist->next = rlist;
835           rlist = plist;
836           *n_p += 1;
837         }
838       plist = tmp;
839     }
840   return rlist;
841 }
842
843 static void
844 pool_depth_list (gpointer key,
845                  gpointer value,
846                  gpointer user_data)
847 {
848   GParamSpec *pspec = value;
849   gpointer *data = user_data;
850   GSList **slists = data[0];
851   GType owner_type = (GType) data[1];
852
853   if (g_type_is_a (owner_type, pspec->owner_type))
854     {
855       guint d = g_type_depth (pspec->owner_type);
856
857       slists[d - 1] = g_slist_prepend (slists[d - 1], pspec);
858     }
859 }
860
861 GParamSpec** /* free result */
862 g_param_spec_pool_list (GParamSpecPool *pool,
863                         GType           owner_type,
864                         guint          *n_pspecs_p)
865 {
866   GParamSpec **pspecs, **p;
867   GSList **slists, *node;
868   gpointer data[2];
869   guint d, i;
870
871   g_return_val_if_fail (pool != NULL, NULL);
872   g_return_val_if_fail (owner_type > 0, NULL);
873   g_return_val_if_fail (n_pspecs_p != NULL, NULL);
874   
875   G_SLOCK (&pool->smutex);
876   *n_pspecs_p = 0;
877   d = g_type_depth (owner_type);
878   slists = g_new0 (GSList*, d);
879   data[0] = slists;
880   data[1] = (gpointer) owner_type;
881   g_hash_table_foreach (pool->hash_table, pool_depth_list, &data);
882   for (i = 0; i < d - 1; i++)
883     slists[i] = pspec_list_remove_overridden (slists[i], pool->hash_table, owner_type, n_pspecs_p);
884   *n_pspecs_p += g_slist_length (slists[i]);
885   pspecs = g_new (GParamSpec*, *n_pspecs_p + 1);
886   p = pspecs;
887   for (i = 0; i < d; i++)
888     {
889       slists[i] = g_slist_sort (slists[i], pspec_compare_id);
890       for (node = slists[i]; node; node = node->next)
891         *p++ = node->data;
892       g_slist_free (slists[i]);
893     }
894   *p++ = NULL;
895   g_free (slists);
896   G_SUNLOCK (&pool->smutex);
897
898   return pspecs;
899 }
900
901
902 /* --- auxillary functions --- */
903 typedef struct
904 {
905   /* class portion */
906   GType           value_type;
907   void          (*finalize)             (GParamSpec   *pspec);
908   void          (*value_set_default)    (GParamSpec   *pspec,
909                                          GValue       *value);
910   gboolean      (*value_validate)       (GParamSpec   *pspec,
911                                          GValue       *value);
912   gint          (*values_cmp)           (GParamSpec   *pspec,
913                                          const GValue *value1,
914                                          const GValue *value2);
915 } ParamSpecClassInfo;
916
917 static void
918 param_spec_generic_class_init (gpointer g_class,
919                                gpointer class_data)
920 {
921   GParamSpecClass *class = g_class;
922   ParamSpecClassInfo *info = class_data;
923
924   class->value_type = info->value_type;
925   if (info->finalize)
926     class->finalize = info->finalize;                   /* optional */
927   class->value_set_default = info->value_set_default;
928   if (info->value_validate)
929     class->value_validate = info->value_validate;       /* optional */
930   class->values_cmp = info->values_cmp;
931   g_free (class_data);
932 }
933
934 static void
935 default_value_set_default (GParamSpec *pspec,
936                            GValue     *value)
937 {
938   /* value is already zero initialized */
939 }
940
941 static gint
942 default_values_cmp (GParamSpec   *pspec,
943                     const GValue *value1,
944                     const GValue *value2)
945 {
946   return memcmp (&value1->data, &value2->data, sizeof (value1->data));
947 }
948
949 GType
950 g_param_type_register_static (const gchar              *name,
951                               const GParamSpecTypeInfo *pspec_info)
952 {
953   GTypeInfo info = {
954     sizeof (GParamSpecClass),      /* class_size */
955     NULL,                          /* base_init */
956     NULL,                          /* base_destroy */
957     param_spec_generic_class_init, /* class_init */
958     NULL,                          /* class_destroy */
959     NULL,                          /* class_data */
960     0,                             /* instance_size */
961     16,                            /* n_preallocs */
962     NULL,                          /* instance_init */
963   };
964   ParamSpecClassInfo *cinfo;
965
966   g_return_val_if_fail (name != NULL, 0);
967   g_return_val_if_fail (pspec_info != NULL, 0);
968   g_return_val_if_fail (g_type_from_name (name) == 0, 0);
969   g_return_val_if_fail (pspec_info->instance_size >= sizeof (GParamSpec), 0);
970   g_return_val_if_fail (g_type_name (pspec_info->value_type) != NULL, 0);
971   /* default: g_return_val_if_fail (pspec_info->value_set_default != NULL, 0); */
972   /* optional: g_return_val_if_fail (pspec_info->value_validate != NULL, 0); */
973   /* default: g_return_val_if_fail (pspec_info->values_cmp != NULL, 0); */
974
975   info.instance_size = pspec_info->instance_size;
976   info.n_preallocs = pspec_info->n_preallocs;
977   info.instance_init = (GInstanceInitFunc) pspec_info->instance_init;
978   cinfo = g_new (ParamSpecClassInfo, 1);
979   cinfo->value_type = pspec_info->value_type;
980   cinfo->finalize = pspec_info->finalize;
981   cinfo->value_set_default = pspec_info->value_set_default ? pspec_info->value_set_default : default_value_set_default;
982   cinfo->value_validate = pspec_info->value_validate;
983   cinfo->values_cmp = pspec_info->values_cmp ? pspec_info->values_cmp : default_values_cmp;
984   info.class_data = cinfo;
985
986   return g_type_register_static (G_TYPE_PARAM, name, &info, 0);
987 }
988
989 void
990 g_value_set_param (GValue     *value,
991                    GParamSpec *param)
992 {
993   g_return_if_fail (G_VALUE_HOLDS_PARAM (value));
994   if (param)
995     g_return_if_fail (G_IS_PARAM_SPEC (param));
996
997   if (value->data[0].v_pointer)
998     g_param_spec_unref (value->data[0].v_pointer);
999   value->data[0].v_pointer = param;
1000   if (value->data[0].v_pointer)
1001     g_param_spec_ref (value->data[0].v_pointer);
1002 }
1003
1004 void
1005 g_value_set_param_take_ownership (GValue     *value,
1006                                   GParamSpec *param)
1007 {
1008   g_return_if_fail (G_VALUE_HOLDS_PARAM (value));
1009   if (param)
1010     g_return_if_fail (G_IS_PARAM_SPEC (param));
1011
1012   if (value->data[0].v_pointer)
1013     g_param_spec_unref (value->data[0].v_pointer);
1014   value->data[0].v_pointer = param; /* we take over the reference count */
1015 }
1016
1017 GParamSpec*
1018 g_value_get_param (const GValue *value)
1019 {
1020   g_return_val_if_fail (G_VALUE_HOLDS_PARAM (value), NULL);
1021
1022   return value->data[0].v_pointer;
1023 }
1024
1025 GParamSpec*
1026 g_value_dup_param (const GValue *value)
1027 {
1028   g_return_val_if_fail (G_VALUE_HOLDS_PARAM (value), NULL);
1029
1030   return value->data[0].v_pointer ? g_param_spec_ref (value->data[0].v_pointer) : NULL;
1031 }