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