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