gparam.c revision e773d7dba66cf51c7d6ad7d1973ab3635e986e2e
1/* GObject - GLib Type, Object, Parameter and Signal Library 2 * Copyright (C) 1997, 1998, 1999, 2000 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#include "gparam.h" 20 21 22#include "gvaluecollector.h" 23#include <string.h> 24 25 26 27/* --- defines --- */ 28#define G_PARAM_SPEC_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), G_TYPE_PARAM, GParamSpecClass)) 29#define PSPEC_APPLIES_TO_VALUE(pspec, value) (G_TYPE_CHECK_VALUE_TYPE ((value), G_PARAM_SPEC_VALUE_TYPE (pspec))) 30 31 32/* --- prototypes --- */ 33static void g_param_spec_class_base_init (GParamSpecClass *class); 34static void g_param_spec_class_base_finalize (GParamSpecClass *class); 35static void g_param_spec_class_init (GParamSpecClass *class, 36 gpointer class_data); 37static void g_param_spec_init (GParamSpec *pspec); 38static void g_param_spec_finalize (GParamSpec *pspec); 39static void value_param_init (GValue *value); 40static void value_param_free_value (GValue *value); 41static void value_param_copy_value (const GValue *src_value, 42 GValue *dest_value); 43static gpointer value_param_peek_pointer (const GValue *value); 44static gchar* value_param_collect_value (GValue *value, 45 guint nth_value, 46 GType *collect_type, 47 GTypeCValue *collect_value); 48static gchar* value_param_lcopy_value (const GValue *value, 49 guint nth_value, 50 GType *collect_type, 51 GTypeCValue *collect_value); 52 53 54/* --- variables --- */ 55static GQuark quark_floating = 0; 56 57 58/* --- functions --- */ 59void 60g_param_type_init (void) /* sync with gtype.c */ 61{ 62 static const GTypeFundamentalInfo finfo = { 63 (G_TYPE_FLAG_CLASSED | 64 G_TYPE_FLAG_INSTANTIATABLE | 65 G_TYPE_FLAG_DERIVABLE | 66 G_TYPE_FLAG_DEEP_DERIVABLE), 67 }; 68 static const GTypeValueTable param_value_table = { 69 value_param_init, /* value_init */ 70 value_param_free_value, /* value_free */ 71 value_param_copy_value, /* value_copy */ 72 value_param_peek_pointer, /* value_peek_pointer */ 73 G_VALUE_COLLECT_POINTER, /* collect_type */ 74 value_param_collect_value, /* collect_value */ 75 G_VALUE_COLLECT_POINTER, /* lcopy_type */ 76 value_param_lcopy_value, /* lcopy_value */ 77 }; 78 static const GTypeInfo param_spec_info = { 79 sizeof (GParamSpecClass), 80 81 (GBaseInitFunc) g_param_spec_class_base_init, 82 (GBaseFinalizeFunc) g_param_spec_class_base_finalize, 83 (GClassInitFunc) g_param_spec_class_init, 84 (GClassFinalizeFunc) NULL, 85 NULL, /* class_data */ 86 87 sizeof (GParamSpec), 88 0, /* n_preallocs */ 89 (GInstanceInitFunc) g_param_spec_init, 90 91 ¶m_value_table, 92 }; 93 GType type; 94 95 type = g_type_register_fundamental (G_TYPE_PARAM, "GParam", ¶m_spec_info, &finfo, G_TYPE_FLAG_ABSTRACT); 96 g_assert (type == G_TYPE_PARAM); 97} 98 99static void 100g_param_spec_class_base_init (GParamSpecClass *class) 101{ 102} 103 104static void 105g_param_spec_class_base_finalize (GParamSpecClass *class) 106{ 107} 108 109static void 110g_param_spec_class_init (GParamSpecClass *class, 111 gpointer class_data) 112{ 113 quark_floating = g_quark_from_static_string ("GParamSpec-floating"); 114 115 class->value_type = G_TYPE_NONE; 116 class->finalize = g_param_spec_finalize; 117 class->value_set_default = NULL; 118 class->value_validate = NULL; 119 class->values_cmp = NULL; 120} 121 122static void 123g_param_spec_init (GParamSpec *pspec) 124{ 125 pspec->name = NULL; 126 pspec->nick = NULL; 127 pspec->blurb = NULL; 128 pspec->flags = 0; 129 pspec->owner_type = 0; 130 pspec->qdata = NULL; 131 pspec->ref_count = 1; 132 g_datalist_id_set_data (&pspec->qdata, quark_floating, GUINT_TO_POINTER (TRUE)); 133} 134 135static void 136g_param_spec_finalize (GParamSpec *pspec) 137{ 138 g_datalist_clear (&pspec->qdata); 139 140 g_free (pspec->name); 141 g_free (pspec->nick); 142 g_free (pspec->blurb); 143 144 g_type_free_instance ((GTypeInstance*) pspec); 145} 146 147GParamSpec* 148g_param_spec_ref (GParamSpec *pspec) 149{ 150 g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL); 151 g_return_val_if_fail (pspec->ref_count > 0, NULL); 152 153 pspec->ref_count += 1; 154 155 return pspec; 156} 157 158void 159g_param_spec_unref (GParamSpec *pspec) 160{ 161 g_return_if_fail (G_IS_PARAM_SPEC (pspec)); 162 g_return_if_fail (pspec->ref_count > 0); 163 164 /* sync with _sink */ 165 pspec->ref_count -= 1; 166 if (pspec->ref_count == 0) 167 G_PARAM_SPEC_GET_CLASS (pspec)->finalize (pspec); 168} 169 170void 171g_param_spec_sink (GParamSpec *pspec) 172{ 173 g_return_if_fail (G_IS_PARAM_SPEC (pspec)); 174 g_return_if_fail (pspec->ref_count > 0); 175 176 if (g_datalist_id_remove_no_notify (&pspec->qdata, quark_floating)) 177 { 178 /* sync with _unref */ 179 if (pspec->ref_count > 1) 180 pspec->ref_count -= 1; 181 else 182 g_param_spec_unref (pspec); 183 } 184} 185 186gpointer 187g_param_spec_internal (GType param_type, 188 const gchar *name, 189 const gchar *nick, 190 const gchar *blurb, 191 GParamFlags flags) 192{ 193 GParamSpec *pspec; 194 195 g_return_val_if_fail (G_TYPE_IS_PARAM (param_type) && param_type != G_TYPE_PARAM, NULL); 196 g_return_val_if_fail (name != NULL, NULL); 197 198 pspec = (gpointer) g_type_create_instance (param_type); 199 pspec->name = g_strdup (name); 200 g_strcanon (pspec->name, G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-", '-'); 201 pspec->nick = g_strdup (nick ? nick : pspec->name); 202 pspec->blurb = g_strdup (blurb); 203 pspec->flags = (flags & G_PARAM_USER_MASK) | (flags & G_PARAM_MASK); 204 205 return pspec; 206} 207 208gpointer 209g_param_spec_get_qdata (GParamSpec *pspec, 210 GQuark quark) 211{ 212 g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL); 213 214 return quark ? g_datalist_id_get_data (&pspec->qdata, quark) : NULL; 215} 216 217void 218g_param_spec_set_qdata (GParamSpec *pspec, 219 GQuark quark, 220 gpointer data) 221{ 222 g_return_if_fail (G_IS_PARAM_SPEC (pspec)); 223 g_return_if_fail (quark > 0); 224 225 g_datalist_id_set_data (&pspec->qdata, quark, data); 226} 227 228void 229g_param_spec_set_qdata_full (GParamSpec *pspec, 230 GQuark quark, 231 gpointer data, 232 GDestroyNotify destroy) 233{ 234 g_return_if_fail (G_IS_PARAM_SPEC (pspec)); 235 g_return_if_fail (quark > 0); 236 237 g_datalist_id_set_data_full (&pspec->qdata, quark, data, data ? destroy : (GDestroyNotify) NULL); 238} 239 240gpointer 241g_param_spec_steal_qdata (GParamSpec *pspec, 242 GQuark quark) 243{ 244 g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL); 245 g_return_val_if_fail (quark > 0, NULL); 246 247 return g_datalist_id_remove_no_notify (&pspec->qdata, quark); 248} 249 250void 251g_param_value_set_default (GParamSpec *pspec, 252 GValue *value) 253{ 254 g_return_if_fail (G_IS_PARAM_SPEC (pspec)); 255 g_return_if_fail (G_IS_VALUE (value)); 256 g_return_if_fail (PSPEC_APPLIES_TO_VALUE (pspec, value)); 257 258 g_value_reset (value); 259 G_PARAM_SPEC_GET_CLASS (pspec)->value_set_default (pspec, value); 260} 261 262gboolean 263g_param_value_defaults (GParamSpec *pspec, 264 GValue *value) 265{ 266 GValue dflt_value = { 0, }; 267 gboolean defaults; 268 269 g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), FALSE); 270 g_return_val_if_fail (G_IS_VALUE (value), FALSE); 271 g_return_val_if_fail (PSPEC_APPLIES_TO_VALUE (pspec, value), FALSE); 272 273 g_value_init (&dflt_value, G_PARAM_SPEC_VALUE_TYPE (pspec)); 274 G_PARAM_SPEC_GET_CLASS (pspec)->value_set_default (pspec, &dflt_value); 275 defaults = G_PARAM_SPEC_GET_CLASS (pspec)->values_cmp (pspec, value, &dflt_value) == 0; 276 g_value_unset (&dflt_value); 277 278 return defaults; 279} 280 281gboolean 282g_param_value_validate (GParamSpec *pspec, 283 GValue *value) 284{ 285 g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), FALSE); 286 g_return_val_if_fail (G_IS_VALUE (value), FALSE); 287 g_return_val_if_fail (PSPEC_APPLIES_TO_VALUE (pspec, value), FALSE); 288 289 if (G_PARAM_SPEC_GET_CLASS (pspec)->value_validate) 290 { 291 GValue oval = *value; 292 293 if (G_PARAM_SPEC_GET_CLASS (pspec)->value_validate (pspec, value) || 294 memcmp (&oval.data, &value->data, sizeof (oval.data))) 295 return TRUE; 296 } 297 298 return FALSE; 299} 300 301gint 302g_param_values_cmp (GParamSpec *pspec, 303 const GValue *value1, 304 const GValue *value2) 305{ 306 gint cmp; 307 308 /* param_values_cmp() effectively does: value1 - value2 309 * so the return values are: 310 * -1) value1 < value2 311 * 0) value1 == value2 312 * 1) value1 > value2 313 */ 314 g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), 0); 315 g_return_val_if_fail (G_IS_VALUE (value1), 0); 316 g_return_val_if_fail (G_IS_VALUE (value2), 0); 317 g_return_val_if_fail (PSPEC_APPLIES_TO_VALUE (pspec, value1), 0); 318 g_return_val_if_fail (PSPEC_APPLIES_TO_VALUE (pspec, value2), 0); 319 320 cmp = G_PARAM_SPEC_GET_CLASS (pspec)->values_cmp (pspec, value1, value2); 321 322 return CLAMP (cmp, -1, 1); 323} 324 325static void 326value_param_init (GValue *value) 327{ 328 value->data[0].v_pointer = NULL; 329} 330 331static void 332value_param_free_value (GValue *value) 333{ 334 if (value->data[0].v_pointer) 335 g_param_spec_unref (value->data[0].v_pointer); 336} 337 338static void 339value_param_copy_value (const GValue *src_value, 340 GValue *dest_value) 341{ 342 dest_value->data[0].v_pointer = (src_value->data[0].v_pointer 343 ? g_param_spec_ref (src_value->data[0].v_pointer) 344 : NULL); 345} 346 347static gpointer 348value_param_peek_pointer (const GValue *value) 349{ 350 return value->data[0].v_pointer; 351} 352 353static gchar* 354value_param_collect_value (GValue *value, 355 guint nth_value, 356 GType *collect_type, 357 GTypeCValue *collect_value) 358{ 359 if (collect_value->v_pointer) 360 { 361 GParamSpec *param = collect_value->v_pointer; 362 363 if (param->g_type_instance.g_class == NULL) 364 return g_strconcat ("invalid unclassed param spec pointer for value type `", 365 G_VALUE_TYPE_NAME (value), 366 "'", 367 NULL); 368 else if (!g_type_is_a (G_PARAM_SPEC_TYPE (param), G_VALUE_TYPE (value))) 369 return g_strconcat ("invalid param spec type `", 370 G_PARAM_SPEC_TYPE_NAME (param), 371 "' for value type `", 372 G_VALUE_TYPE_NAME (value), 373 "'", 374 NULL); 375 value->data[0].v_pointer = g_param_spec_ref (param); 376 } 377 else 378 value->data[0].v_pointer = NULL; 379 380 *collect_type = 0; 381 return NULL; 382} 383 384static gchar* 385value_param_lcopy_value (const GValue *value, 386 guint nth_value, 387 GType *collect_type, 388 GTypeCValue *collect_value) 389{ 390 GParamSpec **param_p = collect_value->v_pointer; 391 392 if (!param_p) 393 return g_strdup_printf ("value location for `%s' passed as NULL", G_VALUE_TYPE_NAME (value)); 394 395 *param_p = value->data[0].v_pointer ? g_param_spec_ref (value->data[0].v_pointer) : NULL; 396 397 *collect_type = 0; 398 return NULL; 399} 400 401static guint 402param_spec_hash (gconstpointer key_spec) 403{ 404 const GParamSpec *key = key_spec; 405 const gchar *p; 406 guint h = key->owner_type; 407 408 for (p = key->name; *p; p++) 409 h = (h << 5) - h + *p; 410 411 return h; 412} 413 414static gboolean 415param_spec_equals (gconstpointer key_spec_1, 416 gconstpointer key_spec_2) 417{ 418 const GParamSpec *key1 = key_spec_1; 419 const GParamSpec *key2 = key_spec_2; 420 421 return (key1->owner_type == key2->owner_type && 422 strcmp (key1->name, key2->name) == 0); 423} 424 425GHashTable* 426g_param_spec_hash_table_new (void) 427{ 428 return g_hash_table_new (param_spec_hash, param_spec_equals); 429} 430 431void 432g_param_spec_hash_table_insert (GHashTable *hash_table, 433 GParamSpec *pspec, 434 GType owner_type) 435{ 436 g_return_if_fail (hash_table != NULL); 437 g_return_if_fail (G_IS_PARAM_SPEC (pspec)); 438 g_return_if_fail (pspec->name != NULL); 439 if (pspec->owner_type != owner_type) 440 g_return_if_fail (pspec->owner_type == 0); 441 442 if (strchr (pspec->name, ':')) 443 g_warning (G_STRLOC ": parameter name `%s' contains field-delimeter", 444 pspec->name); 445 else 446 { 447 pspec->owner_type = owner_type; 448 g_hash_table_insert (hash_table, pspec, pspec); 449 } 450} 451 452void 453g_param_spec_hash_table_remove (GHashTable *hash_table, 454 GParamSpec *pspec) 455{ 456 g_return_if_fail (hash_table != NULL); 457 g_return_if_fail (G_IS_PARAM_SPEC (pspec)); 458 459 g_assert (g_param_spec_hash_table_lookup (hash_table, pspec->name, pspec->owner_type, FALSE, NULL) != NULL); /* FIXME: paranoid */ 460 461 g_hash_table_remove (hash_table, pspec); 462 g_assert (g_param_spec_hash_table_lookup (hash_table, pspec->name, pspec->owner_type, FALSE, NULL) == NULL); /* FIXME: paranoid */ 463 pspec->owner_type = 0; 464} 465 466GParamSpec* 467g_param_spec_hash_table_lookup (GHashTable *hash_table, 468 const gchar *param_name, 469 GType owner_type, 470 gboolean try_ancestors, 471 const gchar **trailer) 472{ 473 GParamSpec *pspec; 474 GParamSpec key; 475 gchar *delim; 476 477 g_return_val_if_fail (hash_table != NULL, NULL); 478 g_return_val_if_fail (param_name != NULL, NULL); 479 480 key.owner_type = owner_type; 481 delim = strchr (param_name, ':'); 482 if (delim) 483 key.name = g_strndup (param_name, delim - param_name); 484 else 485 key.name = g_strdup (param_name); 486 g_strcanon (key.name, G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-", '-'); 487 488 if (trailer) 489 *trailer = delim; 490 491 pspec = g_hash_table_lookup (hash_table, &key); 492 if (!pspec && try_ancestors) 493 { 494 key.owner_type = g_type_parent (key.owner_type); 495 while (key.owner_type) 496 { 497 pspec = g_hash_table_lookup (hash_table, &key); 498 if (pspec) 499 break; 500 key.owner_type = g_type_parent (key.owner_type); 501 } 502 } 503 504 g_free (key.name); 505 506 return pspec; 507} 508 509 510/* --- auxillary functions --- */ 511typedef struct 512{ 513 /* class portion */ 514 GType value_type; 515 void (*finalize) (GParamSpec *pspec); 516 void (*value_set_default) (GParamSpec *pspec, 517 GValue *value); 518 gboolean (*value_validate) (GParamSpec *pspec, 519 GValue *value); 520 gint (*values_cmp) (GParamSpec *pspec, 521 const GValue *value1, 522 const GValue *value2); 523} ParamSpecClassInfo; 524 525static void 526param_spec_generic_class_init (gpointer g_class, 527 gpointer class_data) 528{ 529 GParamSpecClass *class = g_class; 530 ParamSpecClassInfo *info = class_data; 531 532 class->value_type = info->value_type; 533 if (info->finalize) 534 class->finalize = info->finalize; /* optional */ 535 class->value_set_default = info->value_set_default; 536 if (info->value_validate) 537 class->value_validate = info->value_validate; /* optional */ 538 class->values_cmp = info->values_cmp; 539 g_free (class_data); 540} 541 542static void 543default_value_set_default (GParamSpec *pspec, 544 GValue *value) 545{ 546 /* value is already zero initialized */ 547} 548 549static gint 550default_values_cmp (GParamSpec *pspec, 551 const GValue *value1, 552 const GValue *value2) 553{ 554 return memcmp (&value1->data, &value2->data, sizeof (value1->data)); 555} 556 557GType 558g_param_type_register_static (const gchar *name, 559 const GParamSpecTypeInfo *pspec_info) 560{ 561 GTypeInfo info = { 562 sizeof (GParamSpecClass), /* class_size */ 563 NULL, /* base_init */ 564 NULL, /* base_destroy */ 565 param_spec_generic_class_init, /* class_init */ 566 NULL, /* class_destroy */ 567 NULL, /* class_data */ 568 0, /* instance_size */ 569 16, /* n_preallocs */ 570 NULL, /* instance_init */ 571 }; 572 ParamSpecClassInfo *cinfo; 573 574 g_return_val_if_fail (name != NULL, 0); 575 g_return_val_if_fail (pspec_info != NULL, 0); 576 g_return_val_if_fail (g_type_from_name (name) == 0, 0); 577 g_return_val_if_fail (pspec_info->instance_size >= sizeof (GParamSpec), 0); 578 g_return_val_if_fail (g_type_name (pspec_info->value_type) != NULL, 0); 579 /* default: g_return_val_if_fail (pspec_info->value_set_default != NULL, 0); */ 580 /* optional: g_return_val_if_fail (pspec_info->value_validate != NULL, 0); */ 581 /* default: g_return_val_if_fail (pspec_info->values_cmp != NULL, 0); */ 582 583 info.instance_size = pspec_info->instance_size; 584 info.n_preallocs = pspec_info->n_preallocs; 585 info.instance_init = (GInstanceInitFunc) pspec_info->instance_init; 586 cinfo = g_new (ParamSpecClassInfo, 1); 587 cinfo->value_type = pspec_info->value_type; 588 cinfo->finalize = pspec_info->finalize; 589 cinfo->value_set_default = pspec_info->value_set_default ? pspec_info->value_set_default : default_value_set_default; 590 cinfo->value_validate = pspec_info->value_validate; 591 cinfo->values_cmp = pspec_info->values_cmp ? pspec_info->values_cmp : default_values_cmp; 592 info.class_data = cinfo; 593 594 return g_type_register_static (G_TYPE_PARAM, name, &info, 0); 595} 596 597void 598g_value_set_param (GValue *value, 599 GParamSpec *param) 600{ 601 g_return_if_fail (G_IS_VALUE_PARAM (value)); 602 if (param) 603 g_return_if_fail (G_IS_PARAM_SPEC (param)); 604 605 if (value->data[0].v_pointer) 606 g_param_spec_unref (value->data[0].v_pointer); 607 value->data[0].v_pointer = param; 608 if (value->data[0].v_pointer) 609 g_param_spec_ref (value->data[0].v_pointer); 610} 611 612GParamSpec* 613g_value_get_param (const GValue *value) 614{ 615 g_return_val_if_fail (G_IS_VALUE_PARAM (value), NULL); 616 617 return value->data[0].v_pointer; 618} 619 620GParamSpec* 621g_value_dup_param (const GValue *value) 622{ 623 g_return_val_if_fail (G_IS_VALUE_PARAM (value), NULL); 624 625 return value->data[0].v_pointer ? g_param_spec_ref (value->data[0].v_pointer) : NULL; 626} 627