default initialize the temporary default value, instead of nuking the
[platform/upstream/glib.git] / gobject / gparam.c
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        <string.h>
23
24
25
26 /* --- defines --- */
27 #define G_PARAM_SPEC_CLASS(class)    (G_TYPE_CHECK_CLASS_CAST ((class), G_TYPE_PARAM, GParamSpecClass))
28
29
30 /* --- prototypes --- */
31 static void     g_param_spec_class_base_init     (GParamSpecClass       *class);
32 static void     g_param_spec_class_base_finalize (GParamSpecClass       *class);
33 static void     g_param_spec_class_init          (GParamSpecClass       *class,
34                                                   gpointer               class_data);
35 static void     g_param_spec_init                (GParamSpec            *pspec);
36 static void     g_param_spec_finalize            (GParamSpec            *pspec);
37
38
39 /* --- functions --- */
40 void
41 g_param_type_init (void)        /* sync with gtype.c */
42 {
43   static const GTypeFundamentalInfo finfo = {
44     (G_TYPE_FLAG_CLASSED |
45      G_TYPE_FLAG_INSTANTIATABLE |
46      G_TYPE_FLAG_DERIVABLE |
47      G_TYPE_FLAG_DEEP_DERIVABLE),
48   };
49   static const GTypeInfo param_spec_info = {
50     sizeof (GParamSpecClass),
51
52     (GBaseInitFunc) g_param_spec_class_base_init,
53     (GBaseFinalizeFunc) g_param_spec_class_base_finalize,
54     (GClassInitFunc) g_param_spec_class_init,
55     (GClassFinalizeFunc) NULL,
56     NULL,       /* class_data */
57
58     sizeof (GParamSpec),
59     0,          /* n_preallocs */
60     (GInstanceInitFunc) g_param_spec_init,
61
62     NULL,       /* value_table */
63   };
64   GType type;
65
66   type = g_type_register_fundamental (G_TYPE_PARAM, "GParam", &param_spec_info, &finfo);
67   g_assert (type == G_TYPE_PARAM);
68 }
69
70 static void
71 g_param_spec_class_base_init (GParamSpecClass *class)
72 {
73 }
74
75 static void
76 g_param_spec_class_base_finalize (GParamSpecClass *class)
77 {
78 }
79
80 static void
81 g_param_spec_class_init (GParamSpecClass *class,
82                          gpointer         class_data)
83 {
84   class->value_type = G_TYPE_NONE;
85   class->finalize = g_param_spec_finalize;
86   class->value_set_default = NULL;
87   class->value_validate = NULL;
88   class->values_cmp = NULL;
89 }
90
91 static void
92 g_param_spec_init (GParamSpec *pspec)
93 {
94   pspec->name = NULL;
95   pspec->nick = NULL;
96   pspec->blurb = NULL;
97   pspec->flags = 0;
98   pspec->owner_type = 0;
99   pspec->qdata = NULL;
100   pspec->ref_count = 1;
101 }
102
103 static void
104 g_param_spec_finalize (GParamSpec *pspec)
105 {
106   g_datalist_clear (&pspec->qdata);
107   
108   g_free (pspec->name);
109   g_free (pspec->nick);
110   g_free (pspec->blurb);
111
112   g_type_free_instance ((GTypeInstance*) pspec);
113 }
114
115 GParamSpec*
116 g_param_spec_ref (GParamSpec *pspec)
117 {
118   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL);
119   g_return_val_if_fail (pspec->ref_count > 0, NULL);
120
121   pspec->ref_count += 1;
122
123   return pspec;
124 }
125
126 void
127 g_param_spec_unref (GParamSpec *pspec)
128 {
129   g_return_if_fail (G_IS_PARAM_SPEC (pspec));
130   g_return_if_fail (pspec->ref_count > 0);
131
132   pspec->ref_count -= 1;
133   if (pspec->ref_count == 0)
134     G_PARAM_SPEC_GET_CLASS (pspec)->finalize (pspec);
135 }
136
137 gpointer
138 g_param_spec_internal (GType        param_type,
139                        const gchar *name,
140                        const gchar *nick,
141                        const gchar *blurb,
142                        GParamFlags  flags)
143 {
144   GParamSpec *pspec;
145
146   g_return_val_if_fail (G_TYPE_IS_PARAM (param_type) && param_type != G_TYPE_PARAM, NULL);
147   g_return_val_if_fail (name != NULL, NULL);
148
149   pspec = (gpointer) g_type_create_instance (param_type);
150   pspec->name = g_strdup (name);
151   g_strcanon (pspec->name, G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-", '-');
152   pspec->nick = g_strdup (nick ? nick : pspec->name);
153   pspec->blurb = g_strdup (blurb);
154   pspec->flags = (flags & G_PARAM_USER_MASK) | (flags & G_PARAM_MASK);
155
156   return pspec;
157 }
158
159 gpointer
160 g_param_spec_get_qdata (GParamSpec *pspec,
161                         GQuark      quark)
162 {
163   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL);
164   
165   return quark ? g_datalist_id_get_data (&pspec->qdata, quark) : NULL;
166 }
167
168 void
169 g_param_spec_set_qdata (GParamSpec *pspec,
170                         GQuark      quark,
171                         gpointer    data)
172 {
173   g_return_if_fail (G_IS_PARAM_SPEC (pspec));
174   g_return_if_fail (quark > 0);
175
176   g_datalist_id_set_data (&pspec->qdata, quark, data);
177 }
178
179 void
180 g_param_spec_set_qdata_full (GParamSpec    *pspec,
181                              GQuark         quark,
182                              gpointer       data,
183                              GDestroyNotify destroy)
184 {
185   g_return_if_fail (G_IS_PARAM_SPEC (pspec));
186   g_return_if_fail (quark > 0);
187
188   g_datalist_id_set_data_full (&pspec->qdata, quark, data, data ? destroy : NULL);
189 }
190
191 gpointer
192 g_param_spec_steal_qdata (GParamSpec *pspec,
193                           GQuark      quark)
194 {
195   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL);
196   g_return_val_if_fail (quark > 0, NULL);
197   
198   return g_datalist_id_remove_no_notify (&pspec->qdata, quark);
199 }
200
201 void
202 g_param_value_set_default (GParamSpec *pspec,
203                            GValue     *value)
204 {
205   g_return_if_fail (G_IS_PARAM_SPEC (pspec));
206   g_return_if_fail (G_IS_VALUE (value));
207   g_return_if_fail (G_IS_PARAM_VALUE (pspec, value));
208
209   g_value_reset (value);
210   G_PARAM_SPEC_GET_CLASS (pspec)->value_set_default (pspec, value);
211 }
212
213 gboolean
214 g_param_value_defaults (GParamSpec *pspec,
215                         GValue     *value)
216 {
217   GValue dflt_value = { 0, };
218   gboolean defaults;
219
220   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), FALSE);
221   g_return_val_if_fail (G_IS_VALUE (value), FALSE);
222   g_return_val_if_fail (G_IS_PARAM_VALUE (pspec, value), FALSE);
223
224   g_value_init (&dflt_value, G_PARAM_SPEC_VALUE_TYPE (pspec));
225   G_PARAM_SPEC_GET_CLASS (pspec)->value_set_default (pspec, &dflt_value);
226   defaults = G_PARAM_SPEC_GET_CLASS (pspec)->values_cmp (pspec, value, &dflt_value) == 0;
227   g_value_unset (&dflt_value);
228
229   return defaults;
230 }
231
232 gboolean
233 g_param_value_validate (GParamSpec *pspec,
234                         GValue     *value)
235 {
236   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), FALSE);
237   g_return_val_if_fail (G_IS_VALUE (value), FALSE);
238   g_return_val_if_fail (G_IS_PARAM_VALUE (pspec, value), FALSE);
239
240   if (G_PARAM_SPEC_GET_CLASS (pspec)->value_validate)
241     {
242       GValue oval = *value;
243
244       if (G_PARAM_SPEC_GET_CLASS (pspec)->value_validate (pspec, value) ||
245           memcmp (&oval.data, &value->data, sizeof (oval.data)))
246         return TRUE;
247     }
248
249   return FALSE;
250 }
251
252 gint
253 g_param_values_cmp (GParamSpec   *pspec,
254                     const GValue *value1,
255                     const GValue *value2)
256 {
257   gint cmp;
258
259   /* param_values_cmp() effectively does: value1 - value2
260    * so the return values are:
261    * -1)  value1 < value2
262    *  0)  value1 == value2
263    *  1)  value1 > value2
264    */
265   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), 0);
266   g_return_val_if_fail (G_IS_VALUE (value1), 0);
267   g_return_val_if_fail (G_IS_VALUE (value2), 0);
268   g_return_val_if_fail (G_IS_PARAM_VALUE (pspec, value1), 0);
269   g_return_val_if_fail (G_IS_PARAM_VALUE (pspec, value2), 0);
270
271   cmp = G_PARAM_SPEC_GET_CLASS (pspec)->values_cmp (pspec, value1, value2);
272
273   return CLAMP (cmp, -1, 1);
274 }
275
276 static guint
277 param_spec_hash (gconstpointer key_spec)
278 {
279   const GParamSpec *key = key_spec;
280   const gchar *p;
281   guint h = key->owner_type;
282
283   for (p = key->name; *p; p++)
284     h = (h << 5) - h + *p;
285
286   return h;
287 }
288
289 static gint
290 param_spec_equals (gconstpointer key_spec_1,
291                    gconstpointer key_spec_2)
292 {
293   const GParamSpec *key1 = key_spec_1;
294   const GParamSpec *key2 = key_spec_2;
295
296   return (key1->owner_type == key2->owner_type &&
297           strcmp (key1->name, key2->name) == 0);
298 }
299
300 GHashTable*
301 g_param_spec_hash_table_new (void)
302 {
303   return g_hash_table_new (param_spec_hash, param_spec_equals);
304 }
305
306 void
307 g_param_spec_hash_table_insert (GHashTable *hash_table,
308                                 GParamSpec *pspec,
309                                 GType       owner_type)
310 {
311   g_return_if_fail (hash_table != NULL);
312   g_return_if_fail (G_IS_PARAM_SPEC (pspec));
313   g_return_if_fail (pspec->name != NULL);
314   if (pspec->owner_type != owner_type)
315     g_return_if_fail (pspec->owner_type == 0);
316
317   if (strchr (pspec->name, ':'))
318     g_warning (G_STRLOC ": parameter name `%s' contains field-delimeter",
319                pspec->name);
320   else
321     {
322       pspec->owner_type = owner_type;
323       g_hash_table_insert (hash_table, pspec, pspec);
324     }
325 }
326
327 void
328 g_param_spec_hash_table_remove (GHashTable *hash_table,
329                                 GParamSpec *pspec)
330 {
331   g_return_if_fail (hash_table != NULL);
332   g_return_if_fail (G_IS_PARAM_SPEC (pspec));
333
334   g_assert (g_param_spec_hash_table_lookup (hash_table, pspec->name, pspec->owner_type, FALSE, NULL) != NULL); // FIXME: paranoid
335
336   g_hash_table_remove (hash_table, pspec);
337   g_assert (g_param_spec_hash_table_lookup (hash_table, pspec->name, pspec->owner_type, FALSE, NULL) == NULL); // FIXME: paranoid
338   pspec->owner_type = 0;
339 }
340
341 GParamSpec*
342 g_param_spec_hash_table_lookup (GHashTable   *hash_table,
343                                 const gchar  *param_name,
344                                 GType         owner_type,
345                                 gboolean      try_ancestors,
346                                 const gchar **trailer)
347 {
348   GParamSpec *pspec;
349   GParamSpec key;
350   gchar *delim;
351   
352   g_return_val_if_fail (hash_table != NULL, NULL);
353   g_return_val_if_fail (param_name != NULL, NULL);
354   
355   key.owner_type = owner_type;
356   delim = strchr (param_name, ':');
357   if (delim)
358     key.name = g_strndup (param_name, delim - param_name);
359   else
360     key.name = g_strdup (param_name);
361   g_strcanon (key.name, G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-", '-');
362
363   if (trailer)
364     *trailer = delim;
365   
366   pspec = g_hash_table_lookup (hash_table, &key);
367   if (!pspec && try_ancestors)
368     {
369       key.owner_type = g_type_parent (key.owner_type);
370       while (key.owner_type)
371         {
372           pspec = g_hash_table_lookup (hash_table, &key);
373           if (pspec)
374             break;
375           key.owner_type = g_type_parent (key.owner_type);
376         }
377     }
378   
379   g_free (key.name);
380   
381   return pspec;
382 }