source: trunk/third/glib2/gobject/gparam.c @ 20721

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