added floating reference count. g_object_is_floating():
[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 #include        "gparamspecs.h"
26
27 #include        "gvaluecollector.h"
28 #include        "gobjectalias.h"
29 #include        <string.h>
30
31
32
33 /* --- defines --- */
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
68
69 /* --- functions --- */
70 void
71 g_param_type_init (void)
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, g_intern_static_string ("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   if (!(pspec->flags & G_PARAM_STATIC_NAME))
156     g_free (pspec->name);
157   
158   if (!(pspec->flags & G_PARAM_STATIC_NICK))
159     g_free (pspec->_nick);
160
161   if (!(pspec->flags & G_PARAM_STATIC_BLURB))
162     g_free (pspec->_blurb);
163
164   g_type_free_instance ((GTypeInstance*) pspec);
165 }
166
167 GParamSpec*
168 g_param_spec_ref (GParamSpec *pspec)
169 {
170   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL);
171   g_return_val_if_fail (pspec->ref_count > 0, NULL);
172
173   g_atomic_int_inc (&pspec->ref_count);
174
175   return pspec;
176 }
177
178 void
179 g_param_spec_unref (GParamSpec *pspec)
180 {
181   gboolean is_zero;
182
183   g_return_if_fail (G_IS_PARAM_SPEC (pspec));
184   g_return_if_fail (pspec->ref_count > 0);
185
186   is_zero = g_atomic_int_dec_and_test (&pspec->ref_count);
187
188   if (G_UNLIKELY (is_zero))
189     {
190       G_PARAM_SPEC_GET_CLASS (pspec)->finalize (pspec);
191     }
192 }
193
194 void
195 g_param_spec_sink (GParamSpec *pspec)
196 {
197   g_return_if_fail (G_IS_PARAM_SPEC (pspec));
198   g_return_if_fail (pspec->ref_count > 0);
199
200   if (g_datalist_id_remove_no_notify (&pspec->qdata, quark_floating))
201     g_param_spec_unref (pspec);
202 }
203
204 GParamSpec*
205 g_param_spec_ref_sink (GParamSpec *pspec)
206 {
207   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL);
208   g_return_val_if_fail (pspec->ref_count > 0, NULL);
209
210   g_param_spec_ref (pspec);
211   g_param_spec_sink (pspec);
212   return pspec;
213 }
214
215 G_CONST_RETURN gchar*
216 g_param_spec_get_name (GParamSpec *pspec)
217 {
218   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL);
219
220   return pspec->name;
221 }
222
223 G_CONST_RETURN gchar*
224 g_param_spec_get_nick (GParamSpec *pspec)
225 {
226   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL);
227
228   if (pspec->_nick)
229     return pspec->_nick;
230   else
231     {
232       GParamSpec *redirect_target;
233
234       redirect_target = g_param_spec_get_redirect_target (pspec);
235       if (redirect_target && redirect_target->_nick)
236         return redirect_target->_nick;
237     }
238
239   return pspec->name;
240 }
241
242 G_CONST_RETURN gchar*
243 g_param_spec_get_blurb (GParamSpec *pspec)
244 {
245   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL);
246
247   if (pspec->_blurb)
248     return pspec->_blurb;
249   else
250     {
251       GParamSpec *redirect_target;
252
253       redirect_target = g_param_spec_get_redirect_target (pspec);
254       if (redirect_target && redirect_target->_blurb)
255         return redirect_target->_blurb;
256     }
257
258   return NULL;
259 }
260
261 static void
262 canonicalize_key (gchar *key)
263 {
264   gchar *p;
265   
266   for (p = key; *p != 0; p++)
267     {
268       gchar c = *p;
269       
270       if (c != '-' &&
271           (c < '0' || c > '9') &&
272           (c < 'A' || c > 'Z') &&
273           (c < 'a' || c > 'z'))
274         *p = '-';
275     }
276 }
277
278 static gboolean
279 is_canonical (const gchar *key)
280 {
281   const gchar *p;
282
283   for (p = key; *p != 0; p++)
284     {
285       gchar c = *p;
286       
287       if (c != '-' &&
288           (c < '0' || c > '9') &&
289           (c < 'A' || c > 'Z') &&
290           (c < 'a' || c > 'z'))
291         return FALSE;
292     }
293
294   return TRUE;
295 }
296
297 gpointer
298 g_param_spec_internal (GType        param_type,
299                        const gchar *name,
300                        const gchar *nick,
301                        const gchar *blurb,
302                        GParamFlags  flags)
303 {
304   GParamSpec *pspec;
305   
306   g_return_val_if_fail (G_TYPE_IS_PARAM (param_type) && param_type != G_TYPE_PARAM, NULL);
307   g_return_val_if_fail (name != NULL, NULL);
308   g_return_val_if_fail ((name[0] >= 'A' && name[0] <= 'Z') || (name[0] >= 'a' && name[0] <= 'z'), NULL);
309   g_return_val_if_fail (!(flags & G_PARAM_STATIC_NAME) || is_canonical (name), NULL);
310   
311   pspec = (gpointer) g_type_create_instance (param_type);
312
313   if (flags & G_PARAM_STATIC_NAME)
314     {
315       pspec->name = g_intern_static_string (name);
316       if (!is_canonical (pspec->name))
317         g_warning ("G_PARAM_STATIC_NAME used with non-canonical pspec name: %s", pspec->name);
318     }
319   else
320     {
321       pspec->name = g_strdup (name);
322       canonicalize_key (pspec->name);
323       g_intern_string (pspec->name);
324     }
325
326   if (flags & G_PARAM_STATIC_NICK)
327     pspec->_nick = (gchar*) nick;
328   else
329     pspec->_nick = g_strdup (nick);
330
331   if (flags & G_PARAM_STATIC_BLURB)
332     pspec->_blurb = (gchar*) blurb;
333   else
334     pspec->_blurb = g_strdup (blurb);
335
336   pspec->flags = (flags & G_PARAM_USER_MASK) | (flags & G_PARAM_MASK);
337   
338   return pspec;
339 }
340
341 gpointer
342 g_param_spec_get_qdata (GParamSpec *pspec,
343                         GQuark      quark)
344 {
345   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL);
346   
347   return quark ? g_datalist_id_get_data (&pspec->qdata, quark) : NULL;
348 }
349
350 void
351 g_param_spec_set_qdata (GParamSpec *pspec,
352                         GQuark      quark,
353                         gpointer    data)
354 {
355   g_return_if_fail (G_IS_PARAM_SPEC (pspec));
356   g_return_if_fail (quark > 0);
357
358   g_datalist_id_set_data (&pspec->qdata, quark, data);
359 }
360
361 void
362 g_param_spec_set_qdata_full (GParamSpec    *pspec,
363                              GQuark         quark,
364                              gpointer       data,
365                              GDestroyNotify destroy)
366 {
367   g_return_if_fail (G_IS_PARAM_SPEC (pspec));
368   g_return_if_fail (quark > 0);
369
370   g_datalist_id_set_data_full (&pspec->qdata, quark, data, data ? destroy : (GDestroyNotify) NULL);
371 }
372
373 gpointer
374 g_param_spec_steal_qdata (GParamSpec *pspec,
375                           GQuark      quark)
376 {
377   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL);
378   g_return_val_if_fail (quark > 0, NULL);
379   
380   return g_datalist_id_remove_no_notify (&pspec->qdata, quark);
381 }
382
383 GParamSpec*
384 g_param_spec_get_redirect_target (GParamSpec *pspec)
385 {
386   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL);
387
388   if (G_IS_PARAM_SPEC_OVERRIDE (pspec))
389     {
390       GParamSpecOverride *ospec = G_PARAM_SPEC_OVERRIDE (pspec);
391
392       return ospec->overridden;
393     }
394   else
395     return NULL;
396 }
397
398 void
399 g_param_value_set_default (GParamSpec *pspec,
400                            GValue     *value)
401 {
402   g_return_if_fail (G_IS_PARAM_SPEC (pspec));
403   g_return_if_fail (G_IS_VALUE (value));
404   g_return_if_fail (PSPEC_APPLIES_TO_VALUE (pspec, value));
405
406   g_value_reset (value);
407   G_PARAM_SPEC_GET_CLASS (pspec)->value_set_default (pspec, value);
408 }
409
410 gboolean
411 g_param_value_defaults (GParamSpec *pspec,
412                         GValue     *value)
413 {
414   GValue dflt_value = { 0, };
415   gboolean defaults;
416
417   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), FALSE);
418   g_return_val_if_fail (G_IS_VALUE (value), FALSE);
419   g_return_val_if_fail (PSPEC_APPLIES_TO_VALUE (pspec, value), FALSE);
420
421   g_value_init (&dflt_value, G_PARAM_SPEC_VALUE_TYPE (pspec));
422   G_PARAM_SPEC_GET_CLASS (pspec)->value_set_default (pspec, &dflt_value);
423   defaults = G_PARAM_SPEC_GET_CLASS (pspec)->values_cmp (pspec, value, &dflt_value) == 0;
424   g_value_unset (&dflt_value);
425
426   return defaults;
427 }
428
429 gboolean
430 g_param_value_validate (GParamSpec *pspec,
431                         GValue     *value)
432 {
433   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), FALSE);
434   g_return_val_if_fail (G_IS_VALUE (value), FALSE);
435   g_return_val_if_fail (PSPEC_APPLIES_TO_VALUE (pspec, value), FALSE);
436
437   if (G_PARAM_SPEC_GET_CLASS (pspec)->value_validate)
438     {
439       GValue oval = *value;
440
441       if (G_PARAM_SPEC_GET_CLASS (pspec)->value_validate (pspec, value) ||
442           memcmp (&oval.data, &value->data, sizeof (oval.data)))
443         return TRUE;
444     }
445
446   return FALSE;
447 }
448
449 gboolean
450 g_param_value_convert (GParamSpec   *pspec,
451                        const GValue *src_value,
452                        GValue       *dest_value,
453                        gboolean      strict_validation)
454 {
455   GValue tmp_value = { 0, };
456
457   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), FALSE);
458   g_return_val_if_fail (G_IS_VALUE (src_value), FALSE);
459   g_return_val_if_fail (G_IS_VALUE (dest_value), FALSE);
460   g_return_val_if_fail (PSPEC_APPLIES_TO_VALUE (pspec, dest_value), FALSE);
461
462   /* better leave dest_value untouched when returning FALSE */
463
464   g_value_init (&tmp_value, G_VALUE_TYPE (dest_value));
465   if (g_value_transform (src_value, &tmp_value) &&
466       (!g_param_value_validate (pspec, &tmp_value) || !strict_validation))
467     {
468       g_value_unset (dest_value);
469       
470       /* values are relocatable */
471       memcpy (dest_value, &tmp_value, sizeof (tmp_value));
472       
473       return TRUE;
474     }
475   else
476     {
477       g_value_unset (&tmp_value);
478       
479       return FALSE;
480     }
481 }
482
483 gint
484 g_param_values_cmp (GParamSpec   *pspec,
485                     const GValue *value1,
486                     const GValue *value2)
487 {
488   gint cmp;
489
490   /* param_values_cmp() effectively does: value1 - value2
491    * so the return values are:
492    * -1)  value1 < value2
493    *  0)  value1 == value2
494    *  1)  value1 > value2
495    */
496   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), 0);
497   g_return_val_if_fail (G_IS_VALUE (value1), 0);
498   g_return_val_if_fail (G_IS_VALUE (value2), 0);
499   g_return_val_if_fail (PSPEC_APPLIES_TO_VALUE (pspec, value1), 0);
500   g_return_val_if_fail (PSPEC_APPLIES_TO_VALUE (pspec, value2), 0);
501
502   cmp = G_PARAM_SPEC_GET_CLASS (pspec)->values_cmp (pspec, value1, value2);
503
504   return CLAMP (cmp, -1, 1);
505 }
506
507 static void
508 value_param_init (GValue *value)
509 {
510   value->data[0].v_pointer = NULL;
511 }
512
513 static void
514 value_param_free_value (GValue *value)
515 {
516   if (value->data[0].v_pointer)
517     g_param_spec_unref (value->data[0].v_pointer);
518 }
519
520 static void
521 value_param_copy_value (const GValue *src_value,
522                         GValue       *dest_value)
523 {
524   if (src_value->data[0].v_pointer)
525     dest_value->data[0].v_pointer = g_param_spec_ref (src_value->data[0].v_pointer);
526   else
527     dest_value->data[0].v_pointer = NULL;
528 }
529
530 static void
531 value_param_transform_value (const GValue *src_value,
532                              GValue       *dest_value)
533 {
534   if (src_value->data[0].v_pointer &&
535       g_type_is_a (G_PARAM_SPEC_TYPE (dest_value->data[0].v_pointer), G_VALUE_TYPE (dest_value)))
536     dest_value->data[0].v_pointer = g_param_spec_ref (src_value->data[0].v_pointer);
537   else
538     dest_value->data[0].v_pointer = NULL;
539 }
540
541 static gpointer
542 value_param_peek_pointer (const GValue *value)
543 {
544   return value->data[0].v_pointer;
545 }
546
547 static gchar*
548 value_param_collect_value (GValue      *value,
549                            guint        n_collect_values,
550                            GTypeCValue *collect_values,
551                            guint        collect_flags)
552 {
553   if (collect_values[0].v_pointer)
554     {
555       GParamSpec *param = collect_values[0].v_pointer;
556
557       if (param->g_type_instance.g_class == NULL)
558         return g_strconcat ("invalid unclassed param spec pointer for value type `",
559                             G_VALUE_TYPE_NAME (value),
560                             "'",
561                             NULL);
562       else if (!g_value_type_compatible (G_PARAM_SPEC_TYPE (param), G_VALUE_TYPE (value)))
563         return g_strconcat ("invalid param spec type `",
564                             G_PARAM_SPEC_TYPE_NAME (param),
565                             "' for value type `",
566                             G_VALUE_TYPE_NAME (value),
567                             "'",
568                             NULL);
569       value->data[0].v_pointer = g_param_spec_ref (param);
570     }
571   else
572     value->data[0].v_pointer = NULL;
573
574   return NULL;
575 }
576
577 static gchar*
578 value_param_lcopy_value (const GValue *value,
579                          guint         n_collect_values,
580                          GTypeCValue  *collect_values,
581                          guint         collect_flags)
582 {
583   GParamSpec **param_p = collect_values[0].v_pointer;
584
585   if (!param_p)
586     return g_strdup_printf ("value location for `%s' passed as NULL", G_VALUE_TYPE_NAME (value));
587
588   if (!value->data[0].v_pointer)
589     *param_p = NULL;
590   else if (collect_flags & G_VALUE_NOCOPY_CONTENTS)
591     *param_p = value->data[0].v_pointer;
592   else
593     *param_p = g_param_spec_ref (value->data[0].v_pointer);
594
595   return NULL;
596 }
597
598
599 /* --- param spec pool --- */
600 struct _GParamSpecPool
601 {
602   GStaticMutex smutex;
603   gboolean     type_prefixing;
604   GHashTable  *hash_table;
605 };
606
607 static guint
608 param_spec_pool_hash (gconstpointer key_spec)
609 {
610   const GParamSpec *key = key_spec;
611   const gchar *p;
612   guint h = key->owner_type;
613
614   for (p = key->name; *p; p++)
615     h = (h << 5) - h + *p;
616
617   return h;
618 }
619
620 static gboolean
621 param_spec_pool_equals (gconstpointer key_spec_1,
622                         gconstpointer key_spec_2)
623 {
624   const GParamSpec *key1 = key_spec_1;
625   const GParamSpec *key2 = key_spec_2;
626
627   return (key1->owner_type == key2->owner_type &&
628           strcmp (key1->name, key2->name) == 0);
629 }
630
631 GParamSpecPool*
632 g_param_spec_pool_new (gboolean type_prefixing)
633 {
634   static GStaticMutex init_smutex = G_STATIC_MUTEX_INIT;
635   GParamSpecPool *pool = g_new (GParamSpecPool, 1);
636
637   memcpy (&pool->smutex, &init_smutex, sizeof (init_smutex));
638   pool->type_prefixing = type_prefixing != FALSE;
639   pool->hash_table = g_hash_table_new (param_spec_pool_hash, param_spec_pool_equals);
640
641   return pool;
642 }
643
644 void
645 g_param_spec_pool_insert (GParamSpecPool *pool,
646                           GParamSpec     *pspec,
647                           GType           owner_type)
648 {
649   gchar *p;
650   
651   if (pool && pspec && owner_type > 0 && pspec->owner_type == 0)
652     {
653       G_SLOCK (&pool->smutex);
654       for (p = pspec->name; *p; p++)
655         {
656           if (!strchr (G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-_", *p))
657             {
658               g_warning (G_STRLOC ": pspec name \"%s\" contains invalid characters", pspec->name);
659               G_SUNLOCK (&pool->smutex);
660               return;
661             }
662         }
663       
664       pspec->owner_type = owner_type;
665       g_param_spec_ref (pspec);
666       g_hash_table_insert (pool->hash_table, pspec, pspec);
667       G_SUNLOCK (&pool->smutex);
668     }
669   else
670     {
671       g_return_if_fail (pool != NULL);
672       g_return_if_fail (pspec);
673       g_return_if_fail (owner_type > 0);
674       g_return_if_fail (pspec->owner_type == 0);
675     }
676 }
677
678 void
679 g_param_spec_pool_remove (GParamSpecPool *pool,
680                           GParamSpec     *pspec)
681 {
682   if (pool && pspec)
683     {
684       G_SLOCK (&pool->smutex);
685       if (g_hash_table_remove (pool->hash_table, pspec))
686         g_param_spec_unref (pspec);
687       else
688         g_warning (G_STRLOC ": attempt to remove unknown pspec `%s' from pool", pspec->name);
689       G_SUNLOCK (&pool->smutex);
690     }
691   else
692     {
693       g_return_if_fail (pool != NULL);
694       g_return_if_fail (pspec);
695     }
696 }
697
698 static inline GParamSpec*
699 param_spec_ht_lookup (GHashTable  *hash_table,
700                       const gchar *param_name,
701                       GType        owner_type,
702                       gboolean     walk_ancestors)
703 {
704   GParamSpec key, *pspec;
705
706   key.owner_type = owner_type;
707   key.name = (gchar*) param_name;
708   if (walk_ancestors)
709     do
710       {
711         pspec = g_hash_table_lookup (hash_table, &key);
712         if (pspec)
713           return pspec;
714         key.owner_type = g_type_parent (key.owner_type);
715       }
716     while (key.owner_type);
717   else
718     pspec = g_hash_table_lookup (hash_table, &key);
719
720   if (!pspec && !is_canonical (param_name))
721     {
722       /* try canonicalized form */
723       key.name = g_strdup (param_name);
724       key.owner_type = owner_type;
725       
726       canonicalize_key (key.name);
727       if (walk_ancestors)
728         do
729           {
730             pspec = g_hash_table_lookup (hash_table, &key);
731             if (pspec)
732               {
733                 g_free (key.name);
734                 return pspec;
735               }
736             key.owner_type = g_type_parent (key.owner_type);
737           }
738         while (key.owner_type);
739       else
740         pspec = g_hash_table_lookup (hash_table, &key);
741       g_free (key.name);
742     }
743
744   return pspec;
745 }
746
747 GParamSpec*
748 g_param_spec_pool_lookup (GParamSpecPool *pool,
749                           const gchar    *param_name,
750                           GType           owner_type,
751                           gboolean        walk_ancestors)
752 {
753   GParamSpec *pspec;
754   gchar *delim;
755
756   if (!pool || !param_name)
757     {
758       g_return_val_if_fail (pool != NULL, NULL);
759       g_return_val_if_fail (param_name != NULL, NULL);
760     }
761
762   G_SLOCK (&pool->smutex);
763
764   delim = pool->type_prefixing ? strchr (param_name, ':') : NULL;
765
766   /* try quick and away, i.e. without prefix */
767   if (!delim)
768     {
769       pspec = param_spec_ht_lookup (pool->hash_table, param_name, owner_type, walk_ancestors);
770       G_SUNLOCK (&pool->smutex);
771
772       return pspec;
773     }
774
775   /* strip type prefix */
776   if (pool->type_prefixing && delim[1] == ':')
777     {
778       guint l = delim - param_name;
779       gchar stack_buffer[32], *buffer = l < 32 ? stack_buffer : g_new (gchar, l + 1);
780       GType type;
781       
782       strncpy (buffer, param_name, delim - param_name);
783       buffer[l] = 0;
784       type = g_type_from_name (buffer);
785       if (l >= 32)
786         g_free (buffer);
787       if (type)         /* type==0 isn't a valid type pefix */
788         {
789           /* sanity check, these cases don't make a whole lot of sense */
790           if ((!walk_ancestors && type != owner_type) || !g_type_is_a (owner_type, type))
791             {
792               G_SUNLOCK (&pool->smutex);
793
794               return NULL;
795             }
796           owner_type = type;
797           param_name += l + 2;
798           pspec = param_spec_ht_lookup (pool->hash_table, param_name, owner_type, walk_ancestors);
799           G_SUNLOCK (&pool->smutex);
800
801           return pspec;
802         }
803     }
804   /* malformed param_name */
805
806   G_SUNLOCK (&pool->smutex);
807
808   return NULL;
809 }
810
811 static void
812 pool_list (gpointer key,
813            gpointer value,
814            gpointer user_data)
815 {
816   GParamSpec *pspec = value;
817   gpointer *data = user_data;
818   GType owner_type = (GType) data[1];
819
820   if (owner_type == pspec->owner_type)
821     data[0] = g_list_prepend (data[0], pspec);
822 }
823
824 GList*
825 g_param_spec_pool_list_owned (GParamSpecPool *pool,
826                               GType           owner_type)
827 {
828   gpointer data[2];
829
830   g_return_val_if_fail (pool != NULL, NULL);
831   g_return_val_if_fail (owner_type > 0, NULL);
832   
833   G_SLOCK (&pool->smutex);
834   data[0] = NULL;
835   data[1] = (gpointer) owner_type;
836   g_hash_table_foreach (pool->hash_table, pool_list, &data);
837   G_SUNLOCK (&pool->smutex);
838
839   return data[0];
840 }
841
842 static gint
843 pspec_compare_id (gconstpointer a,
844                   gconstpointer b)
845 {
846   const GParamSpec *pspec1 = a, *pspec2 = b;
847
848   return pspec1->param_id < pspec2->param_id ? -1 : pspec1->param_id > pspec2->param_id;
849 }
850
851 static inline GSList*
852 pspec_list_remove_overridden_and_redirected (GSList     *plist,
853                                              GHashTable *ht,
854                                              GType       owner_type,
855                                              guint      *n_p)
856 {
857   GSList *rlist = NULL;
858
859   while (plist)
860     {
861       GSList *tmp = plist->next;
862       GParamSpec *pspec = plist->data;
863       GParamSpec *found;
864       gboolean remove = FALSE;
865
866       /* Remove paramspecs that are redirected, and also paramspecs
867        * that have are overridden by non-redirected properties.
868        * The idea is to get the single paramspec for each name that
869        * best corresponds to what the application sees.
870        */
871       if (g_param_spec_get_redirect_target (pspec))
872         remove = TRUE;
873       else
874         {
875           found = param_spec_ht_lookup (ht, pspec->name, owner_type, TRUE);
876           if (found != pspec)
877             {
878               GParamSpec *redirect = g_param_spec_get_redirect_target (found);
879               if (redirect != pspec)
880                 remove = TRUE;
881             }
882         }
883
884       if (remove)
885         {
886           g_slist_free_1 (plist);
887         }
888       else
889         {
890           plist->next = rlist;
891           rlist = plist;
892           *n_p += 1;
893         }
894       plist = tmp;
895     }
896   return rlist;
897 }
898
899 static void
900 pool_depth_list (gpointer key,
901                  gpointer value,
902                  gpointer user_data)
903 {
904   GParamSpec *pspec = value;
905   gpointer *data = user_data;
906   GSList **slists = data[0];
907   GType owner_type = (GType) data[1];
908
909   if (g_type_is_a (owner_type, pspec->owner_type))
910     {
911       if (G_TYPE_IS_INTERFACE (pspec->owner_type))
912         {
913           slists[0] = g_slist_prepend (slists[0], pspec);
914         }
915       else
916         {
917           guint d = g_type_depth (pspec->owner_type);
918
919           slists[d - 1] = g_slist_prepend (slists[d - 1], pspec);
920         }
921     }
922 }
923
924 /* We handle interfaces specially since we don't want to
925  * count interface prerequsites like normal inheritance;
926  * the property comes from the direct inheritance from
927  * the prerequisite class, not from the interface that
928  * prerequires it.
929  * 
930  * also 'depth' isn't a meaningful concept for interface
931  * prerequites.
932  */
933 static void
934 pool_depth_list_for_interface (gpointer key,
935                                gpointer value,
936                                gpointer user_data)
937 {
938   GParamSpec *pspec = value;
939   gpointer *data = user_data;
940   GSList **slists = data[0];
941   GType owner_type = (GType) data[1];
942
943   if (pspec->owner_type == owner_type)
944     slists[0] = g_slist_prepend (slists[0], pspec);
945 }
946
947 GParamSpec** /* free result */
948 g_param_spec_pool_list (GParamSpecPool *pool,
949                         GType           owner_type,
950                         guint          *n_pspecs_p)
951 {
952   GParamSpec **pspecs, **p;
953   GSList **slists, *node;
954   gpointer data[2];
955   guint d, i;
956
957   g_return_val_if_fail (pool != NULL, NULL);
958   g_return_val_if_fail (owner_type > 0, NULL);
959   g_return_val_if_fail (n_pspecs_p != NULL, NULL);
960   
961   G_SLOCK (&pool->smutex);
962   *n_pspecs_p = 0;
963   d = g_type_depth (owner_type);
964   slists = g_new0 (GSList*, d);
965   data[0] = slists;
966   data[1] = (gpointer) owner_type;
967
968   g_hash_table_foreach (pool->hash_table,
969                         G_TYPE_IS_INTERFACE (owner_type) ?
970                            pool_depth_list_for_interface :
971                            pool_depth_list,
972                         &data);
973   
974   for (i = 0; i < d; i++)
975     slists[i] = pspec_list_remove_overridden_and_redirected (slists[i], pool->hash_table, owner_type, n_pspecs_p);
976   pspecs = g_new (GParamSpec*, *n_pspecs_p + 1);
977   p = pspecs;
978   for (i = 0; i < d; i++)
979     {
980       slists[i] = g_slist_sort (slists[i], pspec_compare_id);
981       for (node = slists[i]; node; node = node->next)
982         *p++ = node->data;
983       g_slist_free (slists[i]);
984     }
985   *p++ = NULL;
986   g_free (slists);
987   G_SUNLOCK (&pool->smutex);
988
989   return pspecs;
990 }
991
992
993 /* --- auxillary functions --- */
994 typedef struct
995 {
996   /* class portion */
997   GType           value_type;
998   void          (*finalize)             (GParamSpec   *pspec);
999   void          (*value_set_default)    (GParamSpec   *pspec,
1000                                          GValue       *value);
1001   gboolean      (*value_validate)       (GParamSpec   *pspec,
1002                                          GValue       *value);
1003   gint          (*values_cmp)           (GParamSpec   *pspec,
1004                                          const GValue *value1,
1005                                          const GValue *value2);
1006 } ParamSpecClassInfo;
1007
1008 static void
1009 param_spec_generic_class_init (gpointer g_class,
1010                                gpointer class_data)
1011 {
1012   GParamSpecClass *class = g_class;
1013   ParamSpecClassInfo *info = class_data;
1014
1015   class->value_type = info->value_type;
1016   if (info->finalize)
1017     class->finalize = info->finalize;                   /* optional */
1018   class->value_set_default = info->value_set_default;
1019   if (info->value_validate)
1020     class->value_validate = info->value_validate;       /* optional */
1021   class->values_cmp = info->values_cmp;
1022   g_free (class_data);
1023 }
1024
1025 static void
1026 default_value_set_default (GParamSpec *pspec,
1027                            GValue     *value)
1028 {
1029   /* value is already zero initialized */
1030 }
1031
1032 static gint
1033 default_values_cmp (GParamSpec   *pspec,
1034                     const GValue *value1,
1035                     const GValue *value2)
1036 {
1037   return memcmp (&value1->data, &value2->data, sizeof (value1->data));
1038 }
1039
1040 GType
1041 g_param_type_register_static (const gchar              *name,
1042                               const GParamSpecTypeInfo *pspec_info)
1043 {
1044   GTypeInfo info = {
1045     sizeof (GParamSpecClass),      /* class_size */
1046     NULL,                          /* base_init */
1047     NULL,                          /* base_destroy */
1048     param_spec_generic_class_init, /* class_init */
1049     NULL,                          /* class_destroy */
1050     NULL,                          /* class_data */
1051     0,                             /* instance_size */
1052     16,                            /* n_preallocs */
1053     NULL,                          /* instance_init */
1054   };
1055   ParamSpecClassInfo *cinfo;
1056
1057   g_return_val_if_fail (name != NULL, 0);
1058   g_return_val_if_fail (pspec_info != NULL, 0);
1059   g_return_val_if_fail (g_type_from_name (name) == 0, 0);
1060   g_return_val_if_fail (pspec_info->instance_size >= sizeof (GParamSpec), 0);
1061   g_return_val_if_fail (g_type_name (pspec_info->value_type) != NULL, 0);
1062   /* default: g_return_val_if_fail (pspec_info->value_set_default != NULL, 0); */
1063   /* optional: g_return_val_if_fail (pspec_info->value_validate != NULL, 0); */
1064   /* default: g_return_val_if_fail (pspec_info->values_cmp != NULL, 0); */
1065
1066   info.instance_size = pspec_info->instance_size;
1067   info.n_preallocs = pspec_info->n_preallocs;
1068   info.instance_init = (GInstanceInitFunc) pspec_info->instance_init;
1069   cinfo = g_new (ParamSpecClassInfo, 1);
1070   cinfo->value_type = pspec_info->value_type;
1071   cinfo->finalize = pspec_info->finalize;
1072   cinfo->value_set_default = pspec_info->value_set_default ? pspec_info->value_set_default : default_value_set_default;
1073   cinfo->value_validate = pspec_info->value_validate;
1074   cinfo->values_cmp = pspec_info->values_cmp ? pspec_info->values_cmp : default_values_cmp;
1075   info.class_data = cinfo;
1076
1077   return g_type_register_static (G_TYPE_PARAM, name, &info, 0);
1078 }
1079
1080 void
1081 g_value_set_param (GValue     *value,
1082                    GParamSpec *param)
1083 {
1084   g_return_if_fail (G_VALUE_HOLDS_PARAM (value));
1085   if (param)
1086     g_return_if_fail (G_IS_PARAM_SPEC (param));
1087
1088   if (value->data[0].v_pointer)
1089     g_param_spec_unref (value->data[0].v_pointer);
1090   value->data[0].v_pointer = param;
1091   if (value->data[0].v_pointer)
1092     g_param_spec_ref (value->data[0].v_pointer);
1093 }
1094
1095 void
1096 g_value_set_param_take_ownership (GValue     *value,
1097                                   GParamSpec *param)
1098 {
1099   g_value_take_param (value, param);
1100 }
1101
1102 void
1103 g_value_take_param (GValue     *value,
1104                     GParamSpec *param)
1105 {
1106   g_return_if_fail (G_VALUE_HOLDS_PARAM (value));
1107   if (param)
1108     g_return_if_fail (G_IS_PARAM_SPEC (param));
1109
1110   if (value->data[0].v_pointer)
1111     g_param_spec_unref (value->data[0].v_pointer);
1112   value->data[0].v_pointer = param; /* we take over the reference count */
1113 }
1114
1115 GParamSpec*
1116 g_value_get_param (const GValue *value)
1117 {
1118   g_return_val_if_fail (G_VALUE_HOLDS_PARAM (value), NULL);
1119
1120   return value->data[0].v_pointer;
1121 }
1122
1123 GParamSpec*
1124 g_value_dup_param (const GValue *value)
1125 {
1126   g_return_val_if_fail (G_VALUE_HOLDS_PARAM (value), NULL);
1127
1128   return value->data[0].v_pointer ? g_param_spec_ref (value->data[0].v_pointer) : NULL;
1129 }
1130
1131 #define __G_PARAM_C__
1132 #include "gobjectaliasdef.c"