gparam.c revision f0cfc267ba9d4366679fd485f363f9130433ef2e
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 --- */
41static void	g_param_spec_class_base_init	 (GParamSpecClass	*class);
42static void	g_param_spec_class_base_finalize (GParamSpecClass	*class);
43static void	g_param_spec_class_init		 (GParamSpecClass	*class,
44						  gpointer               class_data);
45static void	g_param_spec_init		 (GParamSpec		*pspec,
46						  GParamSpecClass	*class);
47static void	g_param_spec_finalize		 (GParamSpec		*pspec);
48static void	value_param_init		(GValue		*value);
49static void	value_param_free_value		(GValue		*value);
50static void	value_param_copy_value		(const GValue	*src_value,
51						 GValue		*dest_value);
52static void	value_param_transform_value	(const GValue	*src_value,
53						 GValue		*dest_value);
54static gpointer	value_param_peek_pointer	(const GValue	*value);
55static gchar*	value_param_collect_value	(GValue		*value,
56						 guint           n_collect_values,
57						 GTypeCValue    *collect_values,
58						 guint           collect_flags);
59static 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 --- */
66static GQuark quark_floating = 0;
67
68
69/* --- functions --- */
70void
71g_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, "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
111static void
112g_param_spec_class_base_init (GParamSpecClass *class)
113{
114}
115
116static void
117g_param_spec_class_base_finalize (GParamSpecClass *class)
118{
119}
120
121static void
122g_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
134static void
135g_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
150static void
151g_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
167GParamSpec*
168g_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
178void
179g_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
194void
195g_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
204G_CONST_RETURN gchar*
205g_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
212G_CONST_RETURN gchar*
213g_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
231G_CONST_RETURN gchar*
232g_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
250static void
251canonicalize_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
267static gboolean
268is_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
286gpointer
287g_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
325gpointer
326g_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
334void
335g_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
345void
346g_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
357gpointer
358g_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
367GParamSpec*
368g_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
382void
383g_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
394gboolean
395g_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
413gboolean
414g_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
433gboolean
434g_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
467gint
468g_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
491static void
492value_param_init (GValue *value)
493{
494  value->data[0].v_pointer = NULL;
495}
496
497static void
498value_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
504static void
505value_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
514static void
515value_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
525static gpointer
526value_param_peek_pointer (const GValue *value)
527{
528  return value->data[0].v_pointer;
529}
530
531static gchar*
532value_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
561static gchar*
562value_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 --- */
584struct _GParamSpecPool
585{
586  GStaticMutex smutex;
587  gboolean     type_prefixing;
588  GHashTable  *hash_table;
589};
590
591static guint
592param_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
604static gboolean
605param_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
615GParamSpecPool*
616g_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
628void
629g_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
662void
663g_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
682static inline GParamSpec*
683param_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
731GParamSpec*
732g_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
795static void
796pool_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
808GList*
809g_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
826static gint
827pspec_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
835static inline GSList*
836pspec_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
883static void
884pool_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 */
917static void
918pool_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
931GParamSpec** /* free result */
932g_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 --- */
978typedef 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
992static void
993param_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
1009static void
1010default_value_set_default (GParamSpec *pspec,
1011			   GValue     *value)
1012{
1013  /* value is already zero initialized */
1014}
1015
1016static gint
1017default_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
1024GType
1025g_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
1064void
1065g_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
1079void
1080g_value_set_param_take_ownership (GValue     *value,
1081				  GParamSpec *param)
1082{
1083  g_value_take_param (value, param);
1084}
1085
1086void
1087g_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
1099GParamSpec*
1100g_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
1107GParamSpec*
1108g_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"
1117