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