gparam.c 41.3 KB
Newer Older
1
/* GObject - GLib Type, Object, Parameter and Signal Library
2
 * Copyright (C) 1997-1999, 2000-2001 Tim Janik and Red Hat, Inc.
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General
 * Public License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, MA 02111-1307, USA.
 */
19 20 21 22 23 24 25 26 27 28 29 30

/*
 * MT safe
 */

#include "config.h"

#include <string.h>

#include "gparam.h"
#include "gparamspecs.h"
#include "gvaluecollector.h"
31
#include "gtype-private.h"
32

Stefan Kost's avatar
Stefan Kost committed
33 34
/**
 * SECTION:gparamspec
35 36 37 38 39
 * @short_description: Metadata for parameter specifications
 * @see_also: g_object_class_install_property(), g_object_set(),
 *     g_object_get(), g_object_set_property(), g_object_get_property(),
 *     g_value_register_transform_func()
 * @title: GParamSpec
40
 *
Stefan Kost's avatar
Stefan Kost committed
41 42
 * #GParamSpec is an object structure that encapsulates the metadata
 * required to specify parameters, such as e.g. #GObject properties.
43
 *
Stefan Kost's avatar
Stefan Kost committed
44 45 46 47 48 49
 * <para id="canonical-parameter-name">
 * Parameter names need to start with a letter (a-z or A-Z). Subsequent
 * characters can be letters, numbers or a '-'.
 * All other characters are replaced by a '-' during construction.
 * The result of this replacement is called the canonical name of the
 * parameter.
50
 * </para>
Stefan Kost's avatar
Stefan Kost committed
51
 */
52 53 54


/* --- defines --- */
55
#define PARAM_FLOATING_FLAG                     0x2
56
#define	G_PARAM_USER_MASK			(~0 << G_PARAM_USER_SHIFT)
57
#define PSPEC_APPLIES_TO_VALUE(pspec, value)	(G_TYPE_CHECK_VALUE_TYPE ((value), G_PARAM_SPEC_VALUE_TYPE (pspec)))
58 59 60 61 62 63

/* --- prototypes --- */
static void	g_param_spec_class_base_init	 (GParamSpecClass	*class);
static void	g_param_spec_class_base_finalize (GParamSpecClass	*class);
static void	g_param_spec_class_init		 (GParamSpecClass	*class,
						  gpointer               class_data);
64 65
static void	g_param_spec_init		 (GParamSpec		*pspec,
						  GParamSpecClass	*class);
66
static void	g_param_spec_finalize		 (GParamSpec		*pspec);
67 68 69 70
static void	value_param_init		(GValue		*value);
static void	value_param_free_value		(GValue		*value);
static void	value_param_copy_value		(const GValue	*src_value,
						 GValue		*dest_value);
71 72
static void	value_param_transform_value	(const GValue	*src_value,
						 GValue		*dest_value);
73 74
static gpointer	value_param_peek_pointer	(const GValue	*value);
static gchar*	value_param_collect_value	(GValue		*value,
75 76 77
						 guint           n_collect_values,
						 GTypeCValue    *collect_values,
						 guint           collect_flags);
78
static gchar*	value_param_lcopy_value		(const GValue	*value,
79 80 81
						 guint           n_collect_values,
						 GTypeCValue    *collect_values,
						 guint           collect_flags);
82 83


84 85
/* --- functions --- */
void
86
_g_param_type_init (void)
87 88 89 90 91 92 93
{
  static const GTypeFundamentalInfo finfo = {
    (G_TYPE_FLAG_CLASSED |
     G_TYPE_FLAG_INSTANTIATABLE |
     G_TYPE_FLAG_DERIVABLE |
     G_TYPE_FLAG_DEEP_DERIVABLE),
  };
94 95 96 97 98
  static const GTypeValueTable param_value_table = {
    value_param_init,           /* value_init */
    value_param_free_value,     /* value_free */
    value_param_copy_value,     /* value_copy */
    value_param_peek_pointer,   /* value_peek_pointer */
99
    "p",			/* collect_format */
100
    value_param_collect_value,  /* collect_value */
101
    "p",			/* lcopy_format */
102 103
    value_param_lcopy_value,    /* lcopy_value */
  };
104
  const GTypeInfo param_spec_info = {
105 106 107 108 109 110
    sizeof (GParamSpecClass),

    (GBaseInitFunc) g_param_spec_class_base_init,
    (GBaseFinalizeFunc) g_param_spec_class_base_finalize,
    (GClassInitFunc) g_param_spec_class_init,
    (GClassFinalizeFunc) NULL,
111
    NULL,	/* class_data */
112 113

    sizeof (GParamSpec),
114
    0,		/* n_preallocs */
115
    (GInstanceInitFunc) g_param_spec_init,
116

117
    &param_value_table,
118 119 120
  };
  GType type;

Johan Dahlin's avatar
Johan Dahlin committed
121 122
  /* This should be registred as GParamSpec instead of GParam, for
   * consistency sake, so that type name can be mapped to struct name,
Matthias Clasen's avatar
Matthias Clasen committed
123
   * However, some language bindings, most noticeable the python ones
Johan Dahlin's avatar
Johan Dahlin committed
124 125 126
   * depends on the "GParam" identifier, see #548689
   */
  type = g_type_register_fundamental (G_TYPE_PARAM, g_intern_static_string ("GParam"), &param_spec_info, &finfo, G_TYPE_FLAG_ABSTRACT);
127
  g_assert (type == G_TYPE_PARAM);
128
  g_value_register_transform_func (G_TYPE_PARAM, G_TYPE_PARAM, value_param_transform_value);
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
}

static void
g_param_spec_class_base_init (GParamSpecClass *class)
{
}

static void
g_param_spec_class_base_finalize (GParamSpecClass *class)
{
}

static void
g_param_spec_class_init (GParamSpecClass *class,
			 gpointer         class_data)
{
145
  class->value_type = G_TYPE_NONE;
146
  class->finalize = g_param_spec_finalize;
147 148 149
  class->value_set_default = NULL;
  class->value_validate = NULL;
  class->values_cmp = NULL;
150 151 152
}

static void
153 154
g_param_spec_init (GParamSpec      *pspec,
		   GParamSpecClass *class)
155 156
{
  pspec->name = NULL;
157 158
  pspec->_nick = NULL;
  pspec->_blurb = NULL;
159
  pspec->flags = 0;
160
  pspec->value_type = class->value_type;
161 162
  pspec->owner_type = 0;
  pspec->qdata = NULL;
163
  g_datalist_set_flags (&pspec->qdata, PARAM_FLOATING_FLAG);
164
  pspec->ref_count = 1;
165
  pspec->param_id = 0;
166 167 168 169 170 171
}

static void
g_param_spec_finalize (GParamSpec *pspec)
{
  g_datalist_clear (&pspec->qdata);
172 173 174 175 176 177

  if (!(pspec->flags & G_PARAM_STATIC_NICK))
    g_free (pspec->_nick);

  if (!(pspec->flags & G_PARAM_STATIC_BLURB))
    g_free (pspec->_blurb);
178 179 180 181

  g_type_free_instance ((GTypeInstance*) pspec);
}

Stefan Kost's avatar
Stefan Kost committed
182
/**
183
 * g_param_spec_ref: (skip)
Stefan Kost's avatar
Stefan Kost committed
184
 * @pspec: a valid #GParamSpec
185
 *
Stefan Kost's avatar
Stefan Kost committed
186
 * Increments the reference count of @pspec.
187
 *
Stefan Kost's avatar
Stefan Kost committed
188 189
 * Returns: the #GParamSpec that was passed into this function
 */
190 191 192 193
GParamSpec*
g_param_spec_ref (GParamSpec *pspec)
{
  g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL);
194

195
  g_atomic_int_inc ((int *)&pspec->ref_count);
196 197 198 199

  return pspec;
}

Stefan Kost's avatar
Stefan Kost committed
200
/**
201
 * g_param_spec_unref: (skip)
Stefan Kost's avatar
Stefan Kost committed
202
 * @pspec: a valid #GParamSpec
203
 *
Stefan Kost's avatar
Stefan Kost committed
204 205
 * Decrements the reference count of a @pspec.
 */
206 207 208
void
g_param_spec_unref (GParamSpec *pspec)
{
209 210
  gboolean is_zero;

211 212
  g_return_if_fail (G_IS_PARAM_SPEC (pspec));

213
  is_zero = g_atomic_int_dec_and_test ((int *)&pspec->ref_count);
214 215

  if (G_UNLIKELY (is_zero))
216
    {
217
      G_PARAM_SPEC_GET_CLASS (pspec)->finalize (pspec);
218
    }
219 220
}

Stefan Kost's avatar
Stefan Kost committed
221 222 223
/**
 * g_param_spec_sink:
 * @pspec: a valid #GParamSpec
224 225 226 227 228 229 230 231
 *
 * The initial reference count of a newly created #GParamSpec is 1,
 * even though no one has explicitly called g_param_spec_ref() on it
 * yet. So the initial reference count is flagged as "floating", until
 * someone calls <literal>g_param_spec_ref (pspec); g_param_spec_sink
 * (pspec);</literal> in sequence on it, taking over the initial
 * reference count (thus ending up with a @pspec that has a reference
 * count of 1 still, but is not flagged "floating" anymore).
Stefan Kost's avatar
Stefan Kost committed
232
 */
233 234 235
void
g_param_spec_sink (GParamSpec *pspec)
{
236
  gsize oldvalue;
237 238
  g_return_if_fail (G_IS_PARAM_SPEC (pspec));

239 240
  oldvalue = g_atomic_pointer_and (&pspec->qdata, ~(gsize)PARAM_FLOATING_FLAG);
  if (oldvalue & PARAM_FLOATING_FLAG)
241
    g_param_spec_unref (pspec);
242 243
}

Stefan Kost's avatar
Stefan Kost committed
244
/**
245
 * g_param_spec_ref_sink: (skip)
Stefan Kost's avatar
Stefan Kost committed
246
 * @pspec: a valid #GParamSpec
247
 *
Stefan Kost's avatar
Stefan Kost committed
248
 * Convenience function to ref and sink a #GParamSpec.
249
 *
Stefan Kost's avatar
Stefan Kost committed
250 251 252
 * Since: 2.10
 * Returns: the #GParamSpec that was passed into this function
 */
253 254 255
GParamSpec*
g_param_spec_ref_sink (GParamSpec *pspec)
{
256
  gsize oldvalue;
257 258
  g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL);

259 260 261 262
  oldvalue = g_atomic_pointer_and (&pspec->qdata, ~(gsize)PARAM_FLOATING_FLAG);
  if (!(oldvalue & PARAM_FLOATING_FLAG))
    g_param_spec_ref (pspec);

263 264 265
  return pspec;
}

Stefan Kost's avatar
Stefan Kost committed
266 267 268
/**
 * g_param_spec_get_name:
 * @pspec: a valid #GParamSpec
269
 *
Stefan Kost's avatar
Stefan Kost committed
270
 * Get the name of a #GParamSpec.
271
 *
272 273 274
 * The name is always an "interned" string (as per g_intern_string()).
 * This allows for pointer-value comparisons.
 *
Stefan Kost's avatar
Stefan Kost committed
275 276
 * Returns: the name of @pspec.
 */
277
const gchar *
278
g_param_spec_get_name (GParamSpec *pspec)
279 280 281 282 283 284
{
  g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL);

  return pspec->name;
}

Stefan Kost's avatar
Stefan Kost committed
285 286 287
/**
 * g_param_spec_get_nick:
 * @pspec: a valid #GParamSpec
288
 *
Stefan Kost's avatar
Stefan Kost committed
289
 * Get the nickname of a #GParamSpec.
290
 *
Stefan Kost's avatar
Stefan Kost committed
291 292
 * Returns: the nickname of @pspec.
 */
293
const gchar *
294
g_param_spec_get_nick (GParamSpec *pspec)
295 296 297
{
  g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL);

298 299 300 301 302 303 304 305 306 307 308 309
  if (pspec->_nick)
    return pspec->_nick;
  else
    {
      GParamSpec *redirect_target;

      redirect_target = g_param_spec_get_redirect_target (pspec);
      if (redirect_target && redirect_target->_nick)
	return redirect_target->_nick;
    }

  return pspec->name;
310 311
}

Stefan Kost's avatar
Stefan Kost committed
312 313 314
/**
 * g_param_spec_get_blurb:
 * @pspec: a valid #GParamSpec
315
 *
Stefan Kost's avatar
Stefan Kost committed
316
 * Get the short description of a #GParamSpec.
317
 *
Stefan Kost's avatar
Stefan Kost committed
318 319
 * Returns: the short description of @pspec.
 */
320
const gchar *
321
g_param_spec_get_blurb (GParamSpec *pspec)
322 323 324
{
  g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL);

325 326 327 328 329 330 331 332 333 334 335 336
  if (pspec->_blurb)
    return pspec->_blurb;
  else
    {
      GParamSpec *redirect_target;

      redirect_target = g_param_spec_get_redirect_target (pspec);
      if (redirect_target && redirect_target->_blurb)
	return redirect_target->_blurb;
    }

  return NULL;
337 338
}

339
static void
340
canonicalize_key (gchar *key)
341
{
342 343 344
  gchar *p;
  
  for (p = key; *p != 0; p++)
345
    {
346 347
      gchar c = *p;
      
348 349 350 351 352 353 354 355
      if (c != '-' &&
	  (c < '0' || c > '9') &&
	  (c < 'A' || c > 'Z') &&
	  (c < 'a' || c > 'z'))
	*p = '-';
    }
}

356
static gboolean
357
is_canonical (const gchar *key)
358
{
359
  const gchar *p;
360 361 362 363 364 365 366 367 368 369 370 371 372 373 374

  for (p = key; *p != 0; p++)
    {
      gchar c = *p;
      
      if (c != '-' &&
	  (c < '0' || c > '9') &&
	  (c < 'A' || c > 'Z') &&
	  (c < 'a' || c > 'z'))
	return FALSE;
    }

  return TRUE;
}

Stefan Kost's avatar
Stefan Kost committed
375
/**
376
 * g_param_spec_internal: (skip)
Stefan Kost's avatar
Stefan Kost committed
377 378 379 380 381
 * @param_type: the #GType for the property; must be derived from #G_TYPE_PARAM
 * @name: the canonical name of the property
 * @nick: the nickname of the property
 * @blurb: a short description of the property
 * @flags: a combination of #GParamFlags
382
 *
Stefan Kost's avatar
Stefan Kost committed
383
 * Creates a new #GParamSpec instance.
384
 *
Stefan Kost's avatar
Stefan Kost committed
385 386 387
 * A property name consists of segments consisting of ASCII letters and
 * digits, separated by either the '-' or '_' character. The first
 * character of a property name must be a letter. Names which violate these
388 389 390 391 392 393 394 395 396 397 398 399 400
 * rules lead to undefined behaviour.
 *
 * When creating and looking up a #GParamSpec, either separator can be
 * used, but they cannot be mixed. Using '-' is considerably more
 * efficient and in fact required when using property names as detail
 * strings for signals.
 *
 * Beyond the name, #GParamSpec<!-- -->s have two more descriptive
 * strings associated with them, the @nick, which should be suitable
 * for use as a label for the property in a property editor, and the
 * @blurb, which should be a somewhat longer description, suitable for
 * e.g. a tooltip. The @nick and @blurb should ideally be localized.
 *
Stefan Kost's avatar
Stefan Kost committed
401 402
 * Returns: a newly allocated #GParamSpec instance
 */
403 404 405 406 407 408 409 410
gpointer
g_param_spec_internal (GType        param_type,
		       const gchar *name,
		       const gchar *nick,
		       const gchar *blurb,
		       GParamFlags  flags)
{
  GParamSpec *pspec;
411
  
412 413
  g_return_val_if_fail (G_TYPE_IS_PARAM (param_type) && param_type != G_TYPE_PARAM, NULL);
  g_return_val_if_fail (name != NULL, NULL);
414
  g_return_val_if_fail ((name[0] >= 'A' && name[0] <= 'Z') || (name[0] >= 'a' && name[0] <= 'z'), NULL);
415
  g_return_val_if_fail (!(flags & G_PARAM_STATIC_NAME) || is_canonical (name), NULL);
416
  
417
  pspec = (gpointer) g_type_create_instance (param_type);
418

419 420
  if (flags & G_PARAM_STATIC_NAME)
    {
421 422
      /* pspec->name is not freed if (flags & G_PARAM_STATIC_NAME) */
      pspec->name = (gchar *) g_intern_static_string (name);
423 424 425
      if (!is_canonical (pspec->name))
        g_warning ("G_PARAM_STATIC_NAME used with non-canonical pspec name: %s", pspec->name);
    }
426 427
  else
    {
428 429 430 431 432 433 434 435 436
      if (is_canonical (name))
        pspec->name = (gchar *) g_intern_string (name);
      else
        {
          gchar *tmp = g_strdup (name);
          canonicalize_key (tmp);
          pspec->name = (gchar *) g_intern_string (tmp);
          g_free (tmp);
        }
437 438 439
    }

  if (flags & G_PARAM_STATIC_NICK)
440
    pspec->_nick = (gchar*) nick;
441 442 443 444
  else
    pspec->_nick = g_strdup (nick);

  if (flags & G_PARAM_STATIC_BLURB)
445
    pspec->_blurb = (gchar*) blurb;
446 447 448
  else
    pspec->_blurb = g_strdup (blurb);

449
  pspec->flags = (flags & G_PARAM_USER_MASK) | (flags & G_PARAM_MASK);
450
  
451 452 453
  return pspec;
}

Stefan Kost's avatar
Stefan Kost committed
454 455 456 457
/**
 * g_param_spec_get_qdata:
 * @pspec: a valid #GParamSpec
 * @quark: a #GQuark, naming the user data pointer
458
 *
Stefan Kost's avatar
Stefan Kost committed
459
 * Gets back user data pointers stored via g_param_spec_set_qdata().
460
 *
461
 * Returns: (transfer none): the user data pointer set, or %NULL
Stefan Kost's avatar
Stefan Kost committed
462
 */
463 464 465 466 467 468 469 470 471
gpointer
g_param_spec_get_qdata (GParamSpec *pspec,
			GQuark      quark)
{
  g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL);
  
  return quark ? g_datalist_id_get_data (&pspec->qdata, quark) : NULL;
}

Stefan Kost's avatar
Stefan Kost committed
472 473 474 475 476
/**
 * g_param_spec_set_qdata:
 * @pspec: the #GParamSpec to set store a user data pointer
 * @quark: a #GQuark, naming the user data pointer
 * @data: an opaque user data pointer
477 478 479 480 481 482 483
 *
 * Sets an opaque, named pointer on a #GParamSpec. The name is
 * specified through a #GQuark (retrieved e.g. via
 * g_quark_from_static_string()), and the pointer can be gotten back
 * from the @pspec with g_param_spec_get_qdata().  Setting a
 * previously set user data pointer, overrides (frees) the old pointer
 * set, using %NULL as pointer essentially removes the data stored.
Stefan Kost's avatar
Stefan Kost committed
484
 */
485 486 487 488 489 490 491 492 493 494 495
void
g_param_spec_set_qdata (GParamSpec *pspec,
			GQuark      quark,
			gpointer    data)
{
  g_return_if_fail (G_IS_PARAM_SPEC (pspec));
  g_return_if_fail (quark > 0);

  g_datalist_id_set_data (&pspec->qdata, quark, data);
}

Stefan Kost's avatar
Stefan Kost committed
496
/**
497
 * g_param_spec_set_qdata_full: (skip)
Stefan Kost's avatar
Stefan Kost committed
498 499 500 501 502
 * @pspec: the #GParamSpec to set store a user data pointer
 * @quark: a #GQuark, naming the user data pointer
 * @data: an opaque user data pointer
 * @destroy: function to invoke with @data as argument, when @data needs to
 *  be freed
503 504 505 506 507
 *
 * This function works like g_param_spec_set_qdata(), but in addition,
 * a <literal>void (*destroy) (gpointer)</literal> function may be
 * specified which is called with @data as argument when the @pspec is
 * finalized, or the data is being overwritten by a call to
Stefan Kost's avatar
Stefan Kost committed
508 509
 * g_param_spec_set_qdata() with the same @quark.
 */
510 511 512 513 514 515 516 517 518
void
g_param_spec_set_qdata_full (GParamSpec    *pspec,
			     GQuark         quark,
			     gpointer       data,
			     GDestroyNotify destroy)
{
  g_return_if_fail (G_IS_PARAM_SPEC (pspec));
  g_return_if_fail (quark > 0);

519
  g_datalist_id_set_data_full (&pspec->qdata, quark, data, data ? destroy : (GDestroyNotify) NULL);
520 521
}

Stefan Kost's avatar
Stefan Kost committed
522 523 524 525
/**
 * g_param_spec_steal_qdata:
 * @pspec: the #GParamSpec to get a stored user data pointer from
 * @quark: a #GQuark, naming the user data pointer
526 527
 *
 * Gets back user data pointers stored via g_param_spec_set_qdata()
528
 * and removes the @data from @pspec without invoking its destroy()
529 530 531
 * function (if any was set).  Usually, calling this function is only
 * required to update user data pointers with a destroy notifier.
 *
532
 * Returns: (transfer none): the user data pointer set, or %NULL
Stefan Kost's avatar
Stefan Kost committed
533
 */
534 535 536 537 538 539 540 541 542 543
gpointer
g_param_spec_steal_qdata (GParamSpec *pspec,
			  GQuark      quark)
{
  g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL);
  g_return_val_if_fail (quark > 0, NULL);
  
  return g_datalist_id_remove_no_notify (&pspec->qdata, quark);
}

Stefan Kost's avatar
Stefan Kost committed
544 545 546
/**
 * g_param_spec_get_redirect_target:
 * @pspec: a #GParamSpec
547
 *
Stefan Kost's avatar
Stefan Kost committed
548 549 550 551 552 553 554
 * If the paramspec redirects operations to another paramspec,
 * returns that paramspec. Redirect is used typically for
 * providing a new implementation of a property in a derived
 * type while preserving all the properties from the parent
 * type. Redirection is established by creating a property
 * of type #GParamSpecOverride. See g_object_class_override_property()
 * for an example of the use of this capability.
555
 *
Stefan Kost's avatar
Stefan Kost committed
556
 * Since: 2.4
557
 *
558 559
 * Returns: (transfer none): paramspec to which requests on this
 *          paramspec should be redirected, or %NULL if none.
Stefan Kost's avatar
Stefan Kost committed
560
 */
561 562 563 564 565 566 567 568 569 570 571 572 573 574 575
GParamSpec*
g_param_spec_get_redirect_target (GParamSpec *pspec)
{
  g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL);

  if (G_IS_PARAM_SPEC_OVERRIDE (pspec))
    {
      GParamSpecOverride *ospec = G_PARAM_SPEC_OVERRIDE (pspec);

      return ospec->overridden;
    }
  else
    return NULL;
}

Stefan Kost's avatar
Stefan Kost committed
576 577 578 579
/**
 * g_param_value_set_default:
 * @pspec: a valid #GParamSpec
 * @value: a #GValue of correct type for @pspec
580
 *
Stefan Kost's avatar
Stefan Kost committed
581 582
 * Sets @value to its default value as specified in @pspec.
 */
583 584 585 586 587 588
void
g_param_value_set_default (GParamSpec *pspec,
			   GValue     *value)
{
  g_return_if_fail (G_IS_PARAM_SPEC (pspec));
  g_return_if_fail (G_IS_VALUE (value));
589
  g_return_if_fail (PSPEC_APPLIES_TO_VALUE (pspec, value));
590 591 592 593 594

  g_value_reset (value);
  G_PARAM_SPEC_GET_CLASS (pspec)->value_set_default (pspec, value);
}

Stefan Kost's avatar
Stefan Kost committed
595 596 597 598
/**
 * g_param_value_defaults:
 * @pspec: a valid #GParamSpec
 * @value: a #GValue of correct type for @pspec
599
 *
Stefan Kost's avatar
Stefan Kost committed
600
 * Checks whether @value contains the default value as specified in @pspec.
601
 *
Stefan Kost's avatar
Stefan Kost committed
602 603
 * Returns: whether @value contains the canonical default for this @pspec
 */
604 605 606 607
gboolean
g_param_value_defaults (GParamSpec *pspec,
			GValue     *value)
{
608
  GValue dflt_value = G_VALUE_INIT;
609 610 611 612
  gboolean defaults;

  g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), FALSE);
  g_return_val_if_fail (G_IS_VALUE (value), FALSE);
613
  g_return_val_if_fail (PSPEC_APPLIES_TO_VALUE (pspec, value), FALSE);
614 615

  g_value_init (&dflt_value, G_PARAM_SPEC_VALUE_TYPE (pspec));
616
  G_PARAM_SPEC_GET_CLASS (pspec)->value_set_default (pspec, &dflt_value);
617 618 619 620 621 622
  defaults = G_PARAM_SPEC_GET_CLASS (pspec)->values_cmp (pspec, value, &dflt_value) == 0;
  g_value_unset (&dflt_value);

  return defaults;
}

Stefan Kost's avatar
Stefan Kost committed
623 624 625 626
/**
 * g_param_value_validate:
 * @pspec: a valid #GParamSpec
 * @value: a #GValue of correct type for @pspec
627
 *
Stefan Kost's avatar
Stefan Kost committed
628 629 630 631 632 633
 * Ensures that the contents of @value comply with the specifications
 * set out by @pspec. For example, a #GParamSpecInt might require
 * that integers stored in @value may not be smaller than -42 and not be
 * greater than +42. If @value contains an integer outside of this range,
 * it is modified accordingly, so the resulting value will fit into the
 * range -42 .. +42.
634
 *
Stefan Kost's avatar
Stefan Kost committed
635 636
 * Returns: whether modifying @value was necessary to ensure validity
 */
637 638 639 640 641 642
gboolean
g_param_value_validate (GParamSpec *pspec,
			GValue     *value)
{
  g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), FALSE);
  g_return_val_if_fail (G_IS_VALUE (value), FALSE);
643
  g_return_val_if_fail (PSPEC_APPLIES_TO_VALUE (pspec, value), FALSE);
644 645 646 647 648 649 650 651 652 653 654 655 656

  if (G_PARAM_SPEC_GET_CLASS (pspec)->value_validate)
    {
      GValue oval = *value;

      if (G_PARAM_SPEC_GET_CLASS (pspec)->value_validate (pspec, value) ||
	  memcmp (&oval.data, &value->data, sizeof (oval.data)))
	return TRUE;
    }

  return FALSE;
}

Stefan Kost's avatar
Stefan Kost committed
657 658 659 660 661
/**
 * g_param_value_convert:
 * @pspec: a valid #GParamSpec
 * @src_value: souce #GValue
 * @dest_value: destination #GValue of correct type for @pspec
662 663 664 665 666 667 668 669
 * @strict_validation: %TRUE requires @dest_value to conform to @pspec
 * without modifications
 *
 * Transforms @src_value into @dest_value if possible, and then
 * validates @dest_value, in order for it to conform to @pspec.  If
 * @strict_validation is %TRUE this function will only succeed if the
 * transformed @dest_value complied to @pspec without modifications.
 *
Stefan Kost's avatar
Stefan Kost committed
670 671
 * See also g_value_type_transformable(), g_value_transform() and
 * g_param_value_validate().
672
 *
Stefan Kost's avatar
Stefan Kost committed
673 674 675
 * Returns: %TRUE if transformation and validation were successful,
 *  %FALSE otherwise and @dest_value is left untouched.
 */
676 677 678 679 680 681
gboolean
g_param_value_convert (GParamSpec   *pspec,
		       const GValue *src_value,
		       GValue       *dest_value,
		       gboolean	     strict_validation)
{
682
  GValue tmp_value = G_VALUE_INIT;
683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709

  g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), FALSE);
  g_return_val_if_fail (G_IS_VALUE (src_value), FALSE);
  g_return_val_if_fail (G_IS_VALUE (dest_value), FALSE);
  g_return_val_if_fail (PSPEC_APPLIES_TO_VALUE (pspec, dest_value), FALSE);

  /* better leave dest_value untouched when returning FALSE */

  g_value_init (&tmp_value, G_VALUE_TYPE (dest_value));
  if (g_value_transform (src_value, &tmp_value) &&
      (!g_param_value_validate (pspec, &tmp_value) || !strict_validation))
    {
      g_value_unset (dest_value);
      
      /* values are relocatable */
      memcpy (dest_value, &tmp_value, sizeof (tmp_value));
      
      return TRUE;
    }
  else
    {
      g_value_unset (&tmp_value);
      
      return FALSE;
    }
}

Stefan Kost's avatar
Stefan Kost committed
710 711 712 713 714
/**
 * g_param_values_cmp:
 * @pspec: a valid #GParamSpec
 * @value1: a #GValue of correct type for @pspec
 * @value2: a #GValue of correct type for @pspec
715
 *
Stefan Kost's avatar
Stefan Kost committed
716
 * Compares @value1 with @value2 according to @pspec, and return -1, 0 or +1,
717
 * if @value1 is found to be less than, equal to or greater than @value2,
Stefan Kost's avatar
Stefan Kost committed
718
 * respectively.
719
 *
Stefan Kost's avatar
Stefan Kost committed
720 721
 * Returns: -1, 0 or +1, for a less than, equal to or greater than result
 */
722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737
gint
g_param_values_cmp (GParamSpec   *pspec,
		    const GValue *value1,
		    const GValue *value2)
{
  gint cmp;

  /* param_values_cmp() effectively does: value1 - value2
   * so the return values are:
   * -1)  value1 < value2
   *  0)  value1 == value2
   *  1)  value1 > value2
   */
  g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), 0);
  g_return_val_if_fail (G_IS_VALUE (value1), 0);
  g_return_val_if_fail (G_IS_VALUE (value2), 0);
738 739
  g_return_val_if_fail (PSPEC_APPLIES_TO_VALUE (pspec, value1), 0);
  g_return_val_if_fail (PSPEC_APPLIES_TO_VALUE (pspec, value2), 0);
740 741 742 743 744 745

  cmp = G_PARAM_SPEC_GET_CLASS (pspec)->values_cmp (pspec, value1, value2);

  return CLAMP (cmp, -1, 1);
}

746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762
static void
value_param_init (GValue *value)
{
  value->data[0].v_pointer = NULL;
}

static void
value_param_free_value (GValue *value)
{
  if (value->data[0].v_pointer)
    g_param_spec_unref (value->data[0].v_pointer);
}

static void
value_param_copy_value (const GValue *src_value,
			GValue       *dest_value)
{
763 764 765 766 767 768 769 770 771 772 773 774 775 776 777
  if (src_value->data[0].v_pointer)
    dest_value->data[0].v_pointer = g_param_spec_ref (src_value->data[0].v_pointer);
  else
    dest_value->data[0].v_pointer = NULL;
}

static void
value_param_transform_value (const GValue *src_value,
			     GValue       *dest_value)
{
  if (src_value->data[0].v_pointer &&
      g_type_is_a (G_PARAM_SPEC_TYPE (dest_value->data[0].v_pointer), G_VALUE_TYPE (dest_value)))
    dest_value->data[0].v_pointer = g_param_spec_ref (src_value->data[0].v_pointer);
  else
    dest_value->data[0].v_pointer = NULL;
778 779 780 781 782 783 784 785 786 787
}

static gpointer
value_param_peek_pointer (const GValue *value)
{
  return value->data[0].v_pointer;
}

static gchar*
value_param_collect_value (GValue      *value,
788 789 790
			   guint        n_collect_values,
			   GTypeCValue *collect_values,
			   guint        collect_flags)
791
{
792
  if (collect_values[0].v_pointer)
793
    {
794
      GParamSpec *param = collect_values[0].v_pointer;
795 796 797 798 799 800

      if (param->g_type_instance.g_class == NULL)
	return g_strconcat ("invalid unclassed param spec pointer for value type `",
			    G_VALUE_TYPE_NAME (value),
			    "'",
			    NULL);
801
      else if (!g_value_type_compatible (G_PARAM_SPEC_TYPE (param), G_VALUE_TYPE (value)))
802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817
	return g_strconcat ("invalid param spec type `",
			    G_PARAM_SPEC_TYPE_NAME (param),
			    "' for value type `",
			    G_VALUE_TYPE_NAME (value),
			    "'",
			    NULL);
      value->data[0].v_pointer = g_param_spec_ref (param);
    }
  else
    value->data[0].v_pointer = NULL;

  return NULL;
}

static gchar*
value_param_lcopy_value (const GValue *value,
818 819 820
			 guint         n_collect_values,
			 GTypeCValue  *collect_values,
			 guint         collect_flags)
821
{
822
  GParamSpec **param_p = collect_values[0].v_pointer;
823 824 825 826

  if (!param_p)
    return g_strdup_printf ("value location for `%s' passed as NULL", G_VALUE_TYPE_NAME (value));

827 828 829 830 831 832
  if (!value->data[0].v_pointer)
    *param_p = NULL;
  else if (collect_flags & G_VALUE_NOCOPY_CONTENTS)
    *param_p = value->data[0].v_pointer;
  else
    *param_p = g_param_spec_ref (value->data[0].v_pointer);
833 834 835 836

  return NULL;
}

837 838

/* --- param spec pool --- */
Stefan Kost's avatar
Stefan Kost committed
839 840
/**
 * GParamSpecPool:
841
 *
Stefan Kost's avatar
Stefan Kost committed
842 843 844
 * A #GParamSpecPool maintains a collection of #GParamSpec<!-- -->s which can be
 * quickly accessed by owner and name. The implementation of the #GObject property
 * system uses such a pool to store the #GParamSpecs of the properties all object
845
 * types.
Stefan Kost's avatar
Stefan Kost committed
846
 */
847 848
struct _GParamSpecPool
{
849
  GMutex       mutex;
850 851
  gboolean     type_prefixing;
  GHashTable  *hash_table;
852 853
};

854
static guint
855
param_spec_pool_hash (gconstpointer key_spec)
856 857 858 859 860 861 862 863 864 865 866
{
  const GParamSpec *key = key_spec;
  const gchar *p;
  guint h = key->owner_type;

  for (p = key->name; *p; p++)
    h = (h << 5) - h + *p;

  return h;
}

867
static gboolean
868 869
param_spec_pool_equals (gconstpointer key_spec_1,
			gconstpointer key_spec_2)
870 871 872 873 874 875 876 877
{
  const GParamSpec *key1 = key_spec_1;
  const GParamSpec *key2 = key_spec_2;

  return (key1->owner_type == key2->owner_type &&
	  strcmp (key1->name, key2->name) == 0);
}

Stefan Kost's avatar
Stefan Kost committed
878 879 880
/**
 * g_param_spec_pool_new:
 * @type_prefixing: Whether the pool will support type-prefixed property names.
881
 *
Stefan Kost's avatar
Stefan Kost committed
882
 * Creates a new #GParamSpecPool.
883
 *
Stefan Kost's avatar
Stefan Kost committed
884
 * If @type_prefixing is %TRUE, lookups in the newly created pool will
885 886 887 888
 * allow to specify the owner as a colon-separated prefix of the
 * property name, like "GtkContainer:border-width". This feature is
 * deprecated, so you should always set @type_prefixing to %FALSE.
 *
889
 * Returns: (transfer none): a newly allocated #GParamSpecPool.
Stefan Kost's avatar
Stefan Kost committed
890
 */
891 892
GParamSpecPool*
g_param_spec_pool_new (gboolean type_prefixing)
893
{
894
  static GMutex init_mutex;
895 896
  GParamSpecPool *pool = g_new (GParamSpecPool, 1);

897
  memcpy (&pool->mutex, &init_mutex, sizeof (init_mutex));
898 899 900 901
  pool->type_prefixing = type_prefixing != FALSE;
  pool->hash_table = g_hash_table_new (param_spec_pool_hash, param_spec_pool_equals);

  return pool;
902 903
}

Stefan Kost's avatar
Stefan Kost committed
904 905 906 907 908
/**
 * g_param_spec_pool_insert:
 * @pool: a #GParamSpecPool.
 * @pspec: the #GParamSpec to insert
 * @owner_type: a #GType identifying the owner of @pspec
909
 *
Stefan Kost's avatar
Stefan Kost committed
910
 * Inserts a #GParamSpec in the pool.
911 912
 */
void
913 914 915
g_param_spec_pool_insert (GParamSpecPool *pool,
			  GParamSpec     *pspec,
			  GType           owner_type)
916
{
917
  const gchar *p;
918 919
  
  if (pool && pspec && owner_type > 0 && pspec->owner_type == 0)
920
    {
921
      for (p = pspec->name; *p; p++)
922
	{
923 924 925 926 927
	  if (!strchr (G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-_", *p))
	    {
	      g_warning (G_STRLOC ": pspec name \"%s\" contains invalid characters", pspec->name);
	      return;
	    }
928
	}
929
      g_mutex_lock (&pool->mutex);
930 931 932
      pspec->owner_type = owner_type;
      g_param_spec_ref (pspec);
      g_hash_table_insert (pool->hash_table, pspec, pspec);
933
      g_mutex_unlock (&pool->mutex);
934 935 936 937 938 939 940
    }
  else
    {
      g_return_if_fail (pool != NULL);
      g_return_if_fail (pspec);
      g_return_if_fail (owner_type > 0);
      g_return_if_fail (pspec->owner_type == 0);
941 942 943
    }
}

Stefan Kost's avatar
Stefan Kost committed
944 945 946 947
/**
 * g_param_spec_pool_remove:
 * @pool: a #GParamSpecPool
 * @pspec: the #GParamSpec to remove
948
 *
Stefan Kost's avatar
Stefan Kost committed
949 950
 * Removes a #GParamSpec from the pool.
 */
951
void
952 953
g_param_spec_pool_remove (GParamSpecPool *pool,
			  GParamSpec     *pspec)
954
{
955 956
  if (pool && pspec)
    {
957
      g_mutex_lock (&pool->mutex);
958 959 960 961
      if (g_hash_table_remove (pool->hash_table, pspec))
	g_param_spec_unref (pspec);
      else
	g_warning (G_STRLOC ": attempt to remove unknown pspec `%s' from pool", pspec->name);
962
      g_mutex_unlock (&pool->mutex);
963
    }
964
  else
965 966 967 968
    {
      g_return_if_fail (pool != NULL);
      g_return_if_fail (pspec);
    }
969
}
970

971 972 973 974 975 976 977
static inline GParamSpec*
param_spec_ht_lookup (GHashTable  *hash_table,
		      const gchar *param_name,
		      GType        owner_type,
		      gboolean     walk_ancestors)
{
  GParamSpec key, *pspec;
978

979 980 981 982 983 984 985 986 987 988 989 990 991 992
  key.owner_type = owner_type;
  key.name = (gchar*) param_name;
  if (walk_ancestors)
    do
      {
	pspec = g_hash_table_lookup (hash_table, &key);
	if (pspec)
	  return pspec;
	key.owner_type = g_type_parent (key.owner_type);
      }
    while (key.owner_type);
  else
    pspec = g_hash_table_lookup (hash_table, &key);

993
  if (!pspec && !is_canonical (param_name))
994
    {
995 996 997 998 999
      gchar *canonical;

      canonical = g_strdup (key.name);
      canonicalize_key (canonical);

1000
      /* try canonicalized form */
1001
      key.name = canonical;
1002
      key.owner_type = owner_type;
1003

1004
      if (walk_ancestors)
1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015
        do
          {
            pspec = g_hash_table_lookup (hash_table, &key);
            if (pspec)
              {
                g_free (canonical);
                return pspec;
              }
            key.owner_type = g_type_parent (key.owner_type);
          }
        while (key.owner_type);
1016
      else
1017 1018 1019
        pspec = g_hash_table_lookup (hash_table, &key);

      g_free (canonical);
1020 1021 1022
    }

  return pspec;
1023 1024
}

Stefan Kost's avatar
Stefan Kost committed
1025 1026 1027 1028 1029
/**
 * g_param_spec_pool_lookup:
 * @pool: a #GParamSpecPool
 * @param_name: the name to look for
 * @owner_type: the owner to look for
1030
 * @walk_ancestors: If %TRUE, also try to find a #GParamSpec with @param_name
Stefan Kost's avatar
Stefan Kost committed
1031
 *  owned by an ancestor of @owner_type.
1032
 *
Stefan Kost's avatar
Stefan Kost committed
1033
 * Looks up a #GParamSpec in the pool.
1034
 *
1035 1036
 * Returns: (transfer none): The found #GParamSpec, or %NULL if no
 * matching #GParamSpec was found.
Stefan Kost's avatar
Stefan Kost committed
1037
 */
1038
GParamSpec*
1039 1040 1041
g_param_spec_pool_lookup (GParamSpecPool *pool,
			  const gchar    *param_name,
			  GType           owner_type,
1042
			  gboolean        walk_ancestors)
1043 1044 1045
{
  GParamSpec *pspec;
  gchar *delim;
1046

1047 1048 1049 1050 1051 1052
  if (!pool || !param_name)
    {
      g_return_val_if_fail (pool != NULL, NULL);
      g_return_val_if_fail (param_name != NULL, NULL);
    }

1053
  g_mutex_lock (&pool->mutex);
1054

1055
  delim = pool->type_prefixing ? strchr (param_name, ':') : NULL;
1056

1057
  /* try quick and away, i.e. without prefix */
1058
  if (!delim)
1059
    {
1060
      pspec = param_spec_ht_lookup (pool->hash_table, param_name, owner_type, walk_ancestors);
1061
      g_mutex_unlock (&pool->mutex);
1062 1063

      return pspec;
1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078
    }

  /* strip type prefix */
  if (pool->type_prefixing && delim[1] == ':')
    {
      guint l = delim - param_name;
      gchar stack_buffer[32], *buffer = l < 32 ? stack_buffer : g_new (gchar, l + 1);
      GType type;
      
      strncpy (buffer, param_name, delim - param_name);
      buffer[l] = 0;
      type = g_type_from_name (buffer);
      if (l >= 32)
	g_free (buffer);
      if (type)		/* type==0 isn't a valid type pefix */
1079
	{
1080 1081 1082
	  /* sanity check, these cases don't make a whole lot of sense */
	  if ((!walk_ancestors && type != owner_type) || !g_type_is_a (owner_type, type))
	    {
1083
	      g_mutex_unlock (&pool->mutex);
1084

1085 1086 1087 1088
	      return NULL;
	    }
	  owner_type = type;
	  param_name += l + 2;
1089
	  pspec = param_spec_ht_lookup (pool->hash_table, param_name, owner_type, walk_ancestors);
1090
	  g_mutex_unlock (&pool->mutex);
1091

1092
	  return pspec;
1093 1094
	}
    }
1095
  /* malformed param_name */
1096

1097
  g_mutex_unlock (&pool->mutex);
1098

1099
  return NULL;
1100
}
1101

1102 1103 1104 1105 1106 1107 1108
static void
pool_list (gpointer key,
	   gpointer value,
	   gpointer user_data)
{
  GParamSpec *pspec = value;
  gpointer *data = user_data;
1109
  GType owner_type = (GType) data[1];
1110 1111 1112 1113 1114

  if (owner_type == pspec->owner_type)
    data[0] = g_list_prepend (data[0], pspec);
}

Stefan Kost's avatar
Stefan Kost committed
1115 1116 1117 1118
/**
 * g_param_spec_pool_list_owned:
 * @pool: a #GParamSpecPool
 * @owner_type: the owner to look for
1119 1120 1121 1122
 *
 * Gets an #GList of all #GParamSpec<!-- -->s owned by @owner_type in
 * the pool.
 *
1123 1124 1125
 * Returns: (transfer container) (element-type GObject.ParamSpec): a
 *          #GList of all #GParamSpec<!-- -->s owned by @owner_type in
 *          the pool#GParamSpec<!-- -->s.
Stefan Kost's avatar
Stefan Kost committed
1126
 */
1127
GList*
1128
g_param_spec_pool_list_owned (GParamSpecPool *pool,
1129
			      GType           owner_type)
1130 1131 1132 1133 1134 1135
{
  gpointer data[2];

  g_return_val_if_fail (pool != NULL, NULL);
  g_return_val_if_fail (owner_type > 0, NULL);
  
1136
  g_mutex_lock (&pool->mutex);
1137
  data[0] = NULL;
1138
  data[1] = (gpointer) owner_type;
1139
  g_hash_table_foreach (pool->hash_table, pool_list, &data);
1140
  g_mutex_unlock (&pool->mutex);
1141 1142 1143 1144

  return data[0];
}

1145 1146 1147 1148 1149 1150
static gint
pspec_compare_id (gconstpointer a,
		  gconstpointer b)
{
  const GParamSpec *pspec1 = a, *pspec2 = b;

1151 1152 1153 1154 1155 1156 1157
  if (pspec1->param_id < pspec2->param_id)
    return -1;

  if (pspec1->param_id > pspec2->param_id)
    return 1;

  return strcmp (pspec1->name, pspec2->name);
1158 1159 1160
}

static inline GSList*
1161 1162 1163 1164
pspec_list_remove_overridden_and_redirected (GSList     *plist,
					     GHashTable *ht,
					     GType       owner_type,
					     guint      *n_p)
1165 1166 1167 1168 1169 1170 1171
{
  GSList *rlist = NULL;

  while (plist)
    {
      GSList *tmp = plist->next;
      GParamSpec *pspec = plist->data;
1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191
      GParamSpec *found;
      gboolean remove = FALSE;

      /* Remove paramspecs that are redirected, and also paramspecs
       * that have are overridden by non-redirected properties.
       * The idea is to get the single paramspec for each name that
       * best corresponds to what the application sees.
       */
      if (g_param_spec_get_redirect_target (pspec))
	remove = TRUE;
      else
	{
	  found = param_spec_ht_lookup (ht, pspec->name, owner_type, TRUE);
	  if (found != pspec)
	    {
	      GParamSpec *redirect = g_param_spec_get_redirect_target (found);
	      if (redirect != pspec)
		remove = TRUE;
	    }
	}
1192

1193 1194 1195 1196
      if (remove)
	{
	  g_slist_free_1 (plist);
	}
1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215
      else
	{
	  plist->next = rlist;
	  rlist = plist;
	  *n_p += 1;
	}
      plist = tmp;
    }
  return rlist;
}

static void
pool_depth_list (gpointer key,
		 gpointer value,
		 gpointer user_data)
{
  GParamSpec *pspec = value;
  gpointer *data = user_data;
  GSList **slists = data[0];
1216
  GType owner_type = (GType) data[1];
1217 1218 1219

  if (g_type_is_a (owner_type, pspec->owner_type))
    {
1220 1221 1222 1223 1224 1225 1226
      if (G_TYPE_IS_INTERFACE (pspec->owner_type))
	{
	  slists[0] = g_slist_prepend (slists[0], pspec);
	}
      else
	{
	  guint d = g_type_depth (pspec->owner_type);
1227

1228 1229
	  slists[d - 1] = g_slist_prepend (slists[d - 1], pspec);
	}
1230 1231 1232
    }
}

1233
/* We handle interfaces specially since we don't want to
Matthias Clasen's avatar
Matthias Clasen committed
1234
 * count interface prerequisites like normal inheritance;
1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255
 * the property comes from the direct inheritance from
 * the prerequisite class, not from the interface that
 * prerequires it.
 * 
 * also 'depth' isn't a meaningful concept for interface
 * prerequites.
 */
static void
pool_depth_list_for_interface (gpointer key,
			       gpointer value,
			       gpointer user_data)
{
  GParamSpec *pspec = value;
  gpointer *data = user_data;
  GSList **slists = data[0];
  GType owner_type = (GType) data[1];

  if (pspec->owner_type == owner_type)
    slists[0] = g_slist_prepend (slists[0], pspec);
}

Stefan Kost's avatar
Stefan Kost committed
1256 1257 1258 1259
/**
 * g_param_spec_pool_list:
 * @pool: a #GParamSpecPool
 * @owner_type: the owner to look for
1260
 * @n_pspecs_p: (out): return location for the length of the returned array
1261 1262 1263 1264
 *
 * Gets an array of all #GParamSpec<!-- -->s owned by @owner_type in
 * the pool.
 *
1265 1266 1267
 * Returns: (array length=n_pspecs_p) (transfer container): a newly
 *          allocated array containing pointers to all #GParamSpecs
 *          owned by @owner_type in the pool
Stefan Kost's avatar
Stefan Kost committed
1268 1269
 */
GParamSpec**
1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282
g_param_spec_pool_list (GParamSpecPool *pool,
			GType           owner_type,
			guint          *n_pspecs_p)
{
  GParamSpec **pspecs, **p;
  GSList **slists, *node;
  gpointer data[2];
  guint d, i;

  g_return_val_if_fail (pool != NULL, NULL);
  g_return_val_if_fail (owner_type > 0, NULL);
  g_return_val_if_fail (n_pspecs_p != NULL, NULL);
  
1283
  g_mutex_lock (&pool->mutex);
1284 1285 1286 1287
  *n_pspecs_p = 0;
  d = g_type_depth (owner_type);
  slists = g_new0 (GSList*, d);
  data[0] = slists;
1288
  data[1] = (gpointer) owner_type;
1289 1290 1291 1292 1293 1294 1295 1296 1297

  g_hash_table_foreach (pool->hash_table,
			G_TYPE_IS_INTERFACE (owner_type) ?
			   pool_depth_list_for_interface :
			   pool_depth_list,
			&data);
  
  for (i = 0; i < d; i++)
    slists[i] = pspec_list_remove_overridden_and_redirected (slists[i], pool->hash_table, owner_type, n_pspecs_p);
1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308
  pspecs = g_new (GParamSpec*, *n_pspecs_p + 1);
  p = pspecs;
  for (i = 0; i < d; i++)
    {
      slists[i] = g_slist_sort (slists[i], pspec_compare_id);
      for (node = slists[i]; node; node = node->next)
	*p++ = node->data;
      g_slist_free (slists[i]);
    }
  *p++ = NULL;
  g_free (slists);
1309
  g_mutex_unlock (&pool->mutex);
1310 1311 1312 1313

  return pspecs;
}

1314

Matthias Clasen's avatar
Matthias Clasen committed
1315
/* --- auxiliary functions --- */
1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361
typedef struct
{
  /* class portion */
  GType           value_type;
  void          (*finalize)             (GParamSpec   *pspec);
  void          (*value_set_default)    (GParamSpec   *pspec,
					 GValue       *value);
  gboolean      (*value_validate)       (GParamSpec   *pspec,
					 GValue       *value);
  gint          (*values_cmp)           (GParamSpec   *pspec,
					 const GValue *value1,
					 const GValue *value2);
} ParamSpecClassInfo;

static void
param_spec_generic_class_init (gpointer g_class,
			       gpointer class_data)
{
  GParamSpecClass *class = g_class;
  ParamSpecClassInfo *info = class_data;

  class->value_type = info->value_type;
  if (info->finalize)
    class->finalize = info->finalize;			/* optional */
  class->value_set_default = info->value_set_default;
  if (info->value_validate)
    class->value_validate = info->value_validate;	/* optional */
  class->values_cmp = info->values_cmp;
  g_free (class_data);
}

static void
default_value_set_default (GParamSpec *pspec,
			   GValue     *value)
{
  /* value is already zero initialized */
}

static gint
default_values_cmp (GParamSpec   *pspec,
		    const GValue *value1,
		    const GValue *value2)
{
  return memcmp (&value1->data, &value2->data, sizeof (value1->data));
}

Stefan Kost's avatar
Stefan Kost committed
1362 1363 1364 1365
/**
 * g_param_type_register_static:
 * @name: 0-terminated string used as the name of the new #GParamSpec type.
 * @pspec_info: The #GParamSpecTypeInfo for this #GParamSpec type.
1366
 *
Stefan Kost's avatar
Stefan Kost committed
1367
 * Registers @name as the name of a new static type derived from
1368 1369 1370 1371
 * #G_TYPE_PARAM. The type system uses the information contained in
 * the #GParamSpecTypeInfo structure pointed to by @info to manage the
 * #GParamSpec type and its instances.
 *
Stefan Kost's avatar
Stefan Kost committed
1372 1373
 * Returns: The new type identifier.
 */
1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412
GType
g_param_type_register_static (const gchar              *name,
			      const GParamSpecTypeInfo *pspec_info)
{
  GTypeInfo info = {
    sizeof (GParamSpecClass),      /* class_size */
    NULL,                          /* base_init */
    NULL,                          /* base_destroy */
    param_spec_generic_class_init, /* class_init */
    NULL,                          /* class_destroy */
    NULL,                          /* class_data */
    0,                             /* instance_size */
    16,                            /* n_preallocs */
    NULL,                          /* instance_init */
  };
  ParamSpecClassInfo *cinfo;

  g_return_val_if_fail (name != NULL, 0);
  g_return_val_if_fail (pspec_info != NULL, 0);
  g_return_val_if_fail (g_type_from_name (name) == 0, 0);
  g_return_val_if_fail (pspec_info->instance_size >= sizeof (GParamSpec), 0);
  g_return_val_if_fail (g_type_name (pspec_info->value_type) != NULL, 0);
  /* default: g_return_val_if_fail (pspec_info->value_set_default != NULL, 0); */
  /* optional: g_return_val_if_fail (pspec_info->value_validate != NULL, 0); */
  /* default: g_return_val_if_fail (pspec_info->values_cmp != NULL, 0); */

  info.instance_size = pspec_info->instance_size;
  info.n_preallocs = pspec_info->n_preallocs;
  info.instance_init = (GInstanceInitFunc) pspec_info->instance_init;
  cinfo = g_new (ParamSpecClassInfo, 1);
  cinfo->value_type = pspec_info->value_type;
  cinfo->finalize = pspec_info->finalize;
  cinfo->value_set_default = pspec_info->value_set_default ? pspec_info->value_set_default : default_value_set_default;
  cinfo->value_validate = pspec_info->value_validate;
  cinfo->values_cmp = pspec_info->values_cmp ? pspec_info->values_cmp : default_values_cmp;
  info.class_data = cinfo;

  return g_type_register_static (G_TYPE_PARAM, name, &info, 0);
}
1413

Stefan Kost's avatar
Stefan Kost committed
1414 1415 1416
/**
 * g_value_set_param:
 * @value: a valid #GValue of type %G_TYPE_PARAM
1417
 * @param: (allow-none): the #GParamSpec to be set
1418
 *
Stefan Kost's avatar
Stefan Kost committed
1419 1420
 * Set the contents of a %G_TYPE_PARAM #GValue to @param.
 */
1421 1422 1423 1424
void
g_value_set_param (GValue     *value,
		   GParamSpec *param)
{
1425
  g_return_if_fail (G_VALUE_HOLDS_PARAM (value));
1426 1427 1428 1429 1430 1431 1432 1433 1434 1435
  if (param)
    g_return_if_fail (G_IS_PARAM_SPEC (param));

  if (value->data[0].v_pointer)
    g_param_spec_unref (value->data[0].v_pointer);
  value->data[0].v_pointer = param;
  if (value->data[0].v_pointer)
    g_param_spec_ref (value->data[0].v_pointer);
}

Stefan Kost's avatar
Stefan Kost committed
1436
/**
1437
 * g_value_set_param_take_ownership: (skip)
Stefan Kost's avatar
Stefan Kost committed
1438
 * @value: a valid #GValue of type %G_TYPE_PARAM
1439
 * @param: (allow-none): the #GParamSpec to be set
1440
 *
Stefan Kost's avatar
Stefan Kost committed
1441
 * This is an internal function introduced mainly for C marshallers.
1442
 *
Stefan Kost's avatar
Stefan Kost committed
1443 1444
 * Deprecated: 2.4: Use g_value_take_param() instead.
 */
1445 1446 1447
void
g_value_set_param_take_ownership (GValue     *value,
				  GParamSpec *param)
1448 1449 1450 1451
{
  g_value_take_param (value, param);
}

Stefan Kost's avatar
Stefan Kost committed
1452
/**
1453
 * g_value_take_param: (skip)
Stefan Kost's avatar
Stefan Kost committed
1454
 * @value: a valid #GValue of type %G_TYPE_PARAM
1455
 * @param: (allow-none): the #GParamSpec to be set
1456 1457 1458 1459 1460
 *
 * Sets the contents of a %G_TYPE_PARAM #GValue to @param and takes
 * over the ownership of the callers reference to @param; the caller
 * doesn't have to unref it any more.
 *
Stefan Kost's avatar
Stefan Kost committed
1461 1462
 * Since: 2.4
 */
1463 1464 1465
void
g_value_take_param (GValue     *value,
		    GParamSpec *param)
1466 1467 1468 1469 1470 1471 1472 1473 1474 1475
{
  g_return_if_fail (G_VALUE_HOLDS_PARAM (value));
  if (param)
    g_return_if_fail (G_IS_PARAM_SPEC (param));

  if (value->data[0].v_pointer)
    g_param_spec_unref (value->data[0].v_pointer);
  value->data[0].v_pointer = param; /* we take over the reference count */
}

Stefan Kost's avatar
Stefan Kost committed
1476 1477 1478
/**
 * g_value_get_param:
 * @value: a valid #GValue whose type is derived from %G_TYPE_PARAM
1479
 *
Stefan Kost's avatar
Stefan Kost committed
1480
 * Get the contents of a %G_TYPE_PARAM #GValue.
1481
 *
1482
 * Returns: (transfer none): #GParamSpec content of @value
Stefan Kost's avatar
Stefan Kost committed
1483
 */
1484 1485 1486
GParamSpec*
g_value_get_param (const GValue *value)
{
1487
  g_return_val_if_fail (G_VALUE_HOLDS_PARAM (value), NULL);
1488 1489 1490 1491

  return value->data[0].v_pointer;
}

Stefan Kost's avatar
Stefan Kost committed
1492
/**
1493
 * g_value_dup_param: (skip)
Stefan Kost's avatar
Stefan Kost committed
1494
 * @value: a valid #GValue whose type is derived from %G_TYPE_PARAM
1495 1496 1497 1498 1499