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