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