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