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