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