/src/glib/gobject/gvalue.c
Line | Count | Source (jump to first uncovered 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 | | * SPDX-License-Identifier: LGPL-2.1-or-later |
5 | | * |
6 | | * This library is free software; you can redistribute it and/or |
7 | | * modify it under the terms of the GNU Lesser General Public |
8 | | * License as published by the Free Software Foundation; either |
9 | | * version 2.1 of the License, or (at your option) any later version. |
10 | | * |
11 | | * This library is distributed in the hope that it will be useful, |
12 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | | * Lesser General Public License for more details. |
15 | | * |
16 | | * You should have received a copy of the GNU Lesser General |
17 | | * Public License along with this library; if not, see <http://www.gnu.org/licenses/>. |
18 | | */ |
19 | | |
20 | | /* |
21 | | * FIXME: MT-safety |
22 | | */ |
23 | | |
24 | | #include "config.h" |
25 | | |
26 | | #include <string.h> |
27 | | |
28 | | #include "gvalue.h" |
29 | | #include "gvaluecollector.h" |
30 | | #include "gbsearcharray.h" |
31 | | #include "gtype-private.h" |
32 | | |
33 | | |
34 | | /* --- typedefs & structures --- */ |
35 | | typedef struct { |
36 | | GType src_type; |
37 | | GType dest_type; |
38 | | GValueTransform func; |
39 | | } TransformEntry; |
40 | | |
41 | | |
42 | | /* --- prototypes --- */ |
43 | | static gint transform_entries_cmp (gconstpointer bsearch_node1, |
44 | | gconstpointer bsearch_node2); |
45 | | |
46 | | |
47 | | /* --- variables --- */ |
48 | | static GBSearchArray *transform_array = NULL; |
49 | | static GBSearchConfig transform_bconfig = { |
50 | | sizeof (TransformEntry), |
51 | | transform_entries_cmp, |
52 | | G_BSEARCH_ARRAY_ALIGN_POWER2, |
53 | | }; |
54 | | |
55 | | |
56 | | /* --- functions --- */ |
57 | | void |
58 | | _g_value_c_init (void) |
59 | 4 | { |
60 | 4 | transform_array = g_bsearch_array_create (&transform_bconfig); |
61 | 4 | } |
62 | | |
63 | | static inline void /* keep this function in sync with gvaluecollector.h and gboxed.c */ |
64 | | value_meminit (GValue *value, |
65 | | GType value_type) |
66 | 0 | { |
67 | 0 | value->g_type = value_type; |
68 | 0 | memset (value->data, 0, sizeof (value->data)); |
69 | 0 | } |
70 | | |
71 | | /** |
72 | | * g_value_init: |
73 | | * @value: a zero-filled (cleared) [struct@GObject.Value] structure |
74 | | * @g_type: type the [struct@GObject.Value] should hold values of |
75 | | * |
76 | | * Initializes @value to store values of the given @type, and sets its value |
77 | | * to the default for @type. |
78 | | * |
79 | | * This must be called before any other methods on a [struct@GObject.Value], so |
80 | | * the value knows what type it’s meant to store. |
81 | | * |
82 | | * ```c |
83 | | * GValue value = G_VALUE_INIT; |
84 | | * |
85 | | * g_value_init (&value, SOME_G_TYPE); |
86 | | * … |
87 | | * g_value_unset (&value); |
88 | | * ``` |
89 | | * |
90 | | * Returns: (transfer none): the [struct@GObject.Value] structure that has been |
91 | | * passed in |
92 | | */ |
93 | | GValue* |
94 | | g_value_init (GValue *value, |
95 | | GType g_type) |
96 | 0 | { |
97 | 0 | GTypeValueTable *value_table; |
98 | | /* g_return_val_if_fail (G_TYPE_IS_VALUE (g_type), NULL); be more elaborate below */ |
99 | 0 | g_return_val_if_fail (value != NULL, NULL); |
100 | | /* g_return_val_if_fail (G_VALUE_TYPE (value) == 0, NULL); be more elaborate below */ |
101 | | |
102 | 0 | value_table = g_type_value_table_peek (g_type); |
103 | |
|
104 | 0 | if (value_table && G_VALUE_TYPE (value) == 0) |
105 | 0 | { |
106 | | /* setup and init */ |
107 | 0 | value_meminit (value, g_type); |
108 | 0 | value_table->value_init (value); |
109 | 0 | } |
110 | 0 | else if (G_VALUE_TYPE (value)) |
111 | 0 | g_critical ("%s: cannot initialize GValue with type '%s', the value has already been initialized as '%s'", |
112 | 0 | G_STRLOC, |
113 | 0 | g_type_name (g_type), |
114 | 0 | g_type_name (G_VALUE_TYPE (value))); |
115 | 0 | else /* !G_TYPE_IS_VALUE (g_type) */ |
116 | 0 | g_critical ("%s: cannot initialize GValue with type '%s', %s", |
117 | 0 | G_STRLOC, |
118 | 0 | g_type_name (g_type), |
119 | 0 | value_table ? "this type is abstract with regards to GValue use, use a more specific (derived) type" : "this type has no GTypeValueTable implementation"); |
120 | 0 | return value; |
121 | 0 | } |
122 | | |
123 | | /** |
124 | | * g_value_copy: |
125 | | * @src_value: an initialized [struct@GObject.Value] structure |
126 | | * @dest_value: an initialized [struct@GObject.Value] structure of the same type |
127 | | * as @src_value |
128 | | * |
129 | | * Copies the value of @src_value into @dest_value. |
130 | | */ |
131 | | void |
132 | | g_value_copy (const GValue *src_value, |
133 | | GValue *dest_value) |
134 | 0 | { |
135 | 0 | g_return_if_fail (src_value); |
136 | 0 | g_return_if_fail (dest_value); |
137 | 0 | g_return_if_fail (g_value_type_compatible (G_VALUE_TYPE (src_value), G_VALUE_TYPE (dest_value))); |
138 | | |
139 | 0 | if (src_value != dest_value) |
140 | 0 | { |
141 | 0 | GType dest_type = G_VALUE_TYPE (dest_value); |
142 | 0 | GTypeValueTable *value_table = g_type_value_table_peek (dest_type); |
143 | |
|
144 | 0 | g_return_if_fail (value_table); |
145 | | |
146 | | /* make sure dest_value's value is free()d */ |
147 | 0 | if (value_table->value_free) |
148 | 0 | value_table->value_free (dest_value); |
149 | | |
150 | | /* setup and copy */ |
151 | 0 | value_meminit (dest_value, dest_type); |
152 | 0 | value_table->value_copy (src_value, dest_value); |
153 | 0 | } |
154 | 0 | } |
155 | | |
156 | | /** |
157 | | * g_value_reset: |
158 | | * @value: an initialized [struct@GObject.Value] structure |
159 | | * |
160 | | * Clears the current value in @value and resets it to the default value |
161 | | * (as if the value had just been initialized using |
162 | | * [method@GObject.Value.init]). |
163 | | * |
164 | | * Returns: the [struct@GObject.Value] structure that has been passed in |
165 | | */ |
166 | | GValue* |
167 | | g_value_reset (GValue *value) |
168 | 0 | { |
169 | 0 | GTypeValueTable *value_table; |
170 | 0 | GType g_type; |
171 | |
|
172 | 0 | g_return_val_if_fail (value, NULL); |
173 | 0 | g_type = G_VALUE_TYPE (value); |
174 | |
|
175 | 0 | value_table = g_type_value_table_peek (g_type); |
176 | 0 | g_return_val_if_fail (value_table, NULL); |
177 | | |
178 | | /* make sure value's value is free()d */ |
179 | 0 | if (value_table->value_free) |
180 | 0 | value_table->value_free (value); |
181 | | |
182 | | /* setup and init */ |
183 | 0 | value_meminit (value, g_type); |
184 | 0 | value_table->value_init (value); |
185 | |
|
186 | 0 | return value; |
187 | 0 | } |
188 | | |
189 | | /** |
190 | | * g_value_unset: |
191 | | * @value: an initialized [struct@GObject.Value] structure |
192 | | * |
193 | | * Clears the current value in @value (if any) and ‘unsets’ the type. |
194 | | * |
195 | | * This releases all resources associated with this [struct@GObject.Value]. An |
196 | | * unset value is the same as a cleared (zero-filled) |
197 | | * [struct@GObject.Value] structure set to `G_VALUE_INIT`. |
198 | | */ |
199 | | void |
200 | | g_value_unset (GValue *value) |
201 | 0 | { |
202 | 0 | GTypeValueTable *value_table; |
203 | | |
204 | 0 | if (value->g_type == 0) |
205 | 0 | return; |
206 | | |
207 | 0 | g_return_if_fail (value); |
208 | | |
209 | 0 | value_table = g_type_value_table_peek (G_VALUE_TYPE (value)); |
210 | 0 | g_return_if_fail (value_table); |
211 | | |
212 | 0 | if (value_table->value_free) |
213 | 0 | value_table->value_free (value); |
214 | 0 | memset (value, 0, sizeof (*value)); |
215 | 0 | } |
216 | | |
217 | | /** |
218 | | * g_value_fits_pointer: |
219 | | * @value: an initialized [struct@GObject.Value] structure |
220 | | * |
221 | | * Determines if @value will fit inside the size of a pointer value. |
222 | | * |
223 | | * This is an internal function introduced mainly for C marshallers. |
224 | | * |
225 | | * Returns: true if @value will fit inside a pointer value; false otherwise |
226 | | */ |
227 | | gboolean |
228 | | g_value_fits_pointer (const GValue *value) |
229 | 0 | { |
230 | 0 | GTypeValueTable *value_table; |
231 | |
|
232 | 0 | g_return_val_if_fail (value, FALSE); |
233 | | |
234 | 0 | value_table = g_type_value_table_peek (G_VALUE_TYPE (value)); |
235 | 0 | g_return_val_if_fail (value_table, FALSE); |
236 | | |
237 | 0 | return value_table->value_peek_pointer != NULL; |
238 | 0 | } |
239 | | |
240 | | /** |
241 | | * g_value_peek_pointer: |
242 | | * @value: an initialized [struct@GObject.Value] structure |
243 | | * |
244 | | * Returns the value contents as a pointer. |
245 | | * |
246 | | * This function asserts that [method@GObject.Value.fits_pointer] returned true |
247 | | * for the passed in value. |
248 | | * |
249 | | * This is an internal function introduced mainly for C marshallers. |
250 | | * |
251 | | * Returns: (transfer none): the value contents as a pointer |
252 | | */ |
253 | | gpointer |
254 | | g_value_peek_pointer (const GValue *value) |
255 | 0 | { |
256 | 0 | GTypeValueTable *value_table; |
257 | |
|
258 | 0 | g_return_val_if_fail (value, NULL); |
259 | | |
260 | 0 | value_table = g_type_value_table_peek (G_VALUE_TYPE (value)); |
261 | 0 | g_return_val_if_fail (value_table, NULL); |
262 | | |
263 | 0 | if (!value_table->value_peek_pointer) |
264 | 0 | { |
265 | 0 | g_return_val_if_fail (g_value_fits_pointer (value) == TRUE, NULL); |
266 | 0 | return NULL; |
267 | 0 | } |
268 | | |
269 | 0 | return value_table->value_peek_pointer (value); |
270 | 0 | } |
271 | | |
272 | | /** |
273 | | * g_value_set_instance: |
274 | | * @value: an initialized [struct@GObject.Value] structure |
275 | | * @instance: (nullable): the instance |
276 | | * |
277 | | * Sets @value from an instantiatable type. |
278 | | * |
279 | | * This calls the [callback@GObject.TypeValueCollectFunc] function for the type |
280 | | * the [struct@GObject.Value] contains. |
281 | | */ |
282 | | void |
283 | | g_value_set_instance (GValue *value, |
284 | | gpointer instance) |
285 | 0 | { |
286 | 0 | GType g_type; |
287 | 0 | GTypeValueTable *value_table; |
288 | 0 | GTypeCValue cvalue; |
289 | 0 | gchar *error_msg; |
290 | |
|
291 | 0 | g_return_if_fail (value); |
292 | 0 | g_type = G_VALUE_TYPE (value); |
293 | 0 | value_table = g_type_value_table_peek (g_type); |
294 | 0 | g_return_if_fail (value_table); |
295 | | |
296 | 0 | if (instance) |
297 | 0 | { |
298 | 0 | g_return_if_fail (G_TYPE_CHECK_INSTANCE (instance)); |
299 | 0 | g_return_if_fail (g_value_type_compatible (G_TYPE_FROM_INSTANCE (instance), G_VALUE_TYPE (value))); |
300 | 0 | } |
301 | | |
302 | 0 | g_return_if_fail (strcmp (value_table->collect_format, "p") == 0); |
303 | | |
304 | 0 | memset (&cvalue, 0, sizeof (cvalue)); |
305 | 0 | cvalue.v_pointer = instance; |
306 | | |
307 | | /* make sure value's value is free()d */ |
308 | 0 | if (value_table->value_free) |
309 | 0 | value_table->value_free (value); |
310 | | |
311 | | /* setup and collect */ |
312 | 0 | value_meminit (value, g_type); |
313 | 0 | error_msg = value_table->collect_value (value, 1, &cvalue, 0); |
314 | 0 | if (error_msg) |
315 | 0 | { |
316 | 0 | g_critical ("%s: %s", G_STRLOC, error_msg); |
317 | 0 | g_free (error_msg); |
318 | | |
319 | | /* we purposely leak the value here, it might not be |
320 | | * in a correct state if an error condition occurred |
321 | | */ |
322 | 0 | value_meminit (value, g_type); |
323 | 0 | value_table->value_init (value); |
324 | 0 | } |
325 | 0 | } |
326 | | |
327 | | /** |
328 | | * g_value_init_from_instance: |
329 | | * @value: a zero-filled (cleared) [struct@GObject.Value] structure |
330 | | * @instance: (type GObject.TypeInstance): the instance |
331 | | * |
332 | | * Initializes and sets @value from an instantiatable type. |
333 | | * |
334 | | * This calls the [callback@GObject.TypeValueCollectFunc] function for the type |
335 | | * the [struct@GObject.Value] contains. |
336 | | * |
337 | | * Note: The @value will be initialised with the exact type of |
338 | | * @instance. If you wish to set the @value’s type to a different |
339 | | * [type@GObject.Type] (such as a parent class type), you need to manually call |
340 | | * [method@GObject.Value.init] and [method@GObject.Value.set_instance]. |
341 | | * |
342 | | * Since: 2.42 |
343 | | */ |
344 | | void |
345 | | g_value_init_from_instance (GValue *value, |
346 | | gpointer instance) |
347 | 0 | { |
348 | 0 | g_return_if_fail (value != NULL && G_VALUE_TYPE(value) == 0); |
349 | | |
350 | 0 | if (G_IS_OBJECT (instance)) |
351 | 0 | { |
352 | | /* Fast-path. |
353 | | * If G_IS_OBJECT() succeeds we know: |
354 | | * * that instance is present and valid |
355 | | * * that it is a GObject, and therefore we can directly |
356 | | * use the collect implementation (g_object_ref) */ |
357 | 0 | value_meminit (value, G_TYPE_FROM_INSTANCE (instance)); |
358 | 0 | value->data[0].v_pointer = g_object_ref (instance); |
359 | 0 | } |
360 | 0 | else |
361 | 0 | { |
362 | 0 | GType g_type; |
363 | 0 | GTypeValueTable *value_table; |
364 | 0 | GTypeCValue cvalue; |
365 | 0 | gchar *error_msg; |
366 | |
|
367 | 0 | g_return_if_fail (G_TYPE_CHECK_INSTANCE (instance)); |
368 | | |
369 | 0 | g_type = G_TYPE_FROM_INSTANCE (instance); |
370 | 0 | value_table = g_type_value_table_peek (g_type); |
371 | 0 | g_return_if_fail (strcmp (value_table->collect_format, "p") == 0); |
372 | | |
373 | 0 | memset (&cvalue, 0, sizeof (cvalue)); |
374 | 0 | cvalue.v_pointer = instance; |
375 | | |
376 | | /* setup and collect */ |
377 | 0 | value_meminit (value, g_type); |
378 | 0 | value_table->value_init (value); |
379 | 0 | error_msg = value_table->collect_value (value, 1, &cvalue, 0); |
380 | 0 | if (error_msg) |
381 | 0 | { |
382 | 0 | g_critical ("%s: %s", G_STRLOC, error_msg); |
383 | 0 | g_free (error_msg); |
384 | | |
385 | | /* we purposely leak the value here, it might not be |
386 | | * in a correct state if an error condition occurred |
387 | | */ |
388 | 0 | value_meminit (value, g_type); |
389 | 0 | value_table->value_init (value); |
390 | 0 | } |
391 | 0 | } |
392 | 0 | } |
393 | | |
394 | | static GType |
395 | | transform_lookup_get_parent_type (GType type) |
396 | 0 | { |
397 | 0 | if (g_type_fundamental (type) == G_TYPE_INTERFACE) |
398 | 0 | return g_type_interface_instantiatable_prerequisite (type); |
399 | | |
400 | 0 | return g_type_parent (type); |
401 | 0 | } |
402 | | |
403 | | static GValueTransform |
404 | | transform_func_lookup (GType src_type, |
405 | | GType dest_type) |
406 | 0 | { |
407 | 0 | TransformEntry entry; |
408 | |
|
409 | 0 | entry.src_type = src_type; |
410 | 0 | do |
411 | 0 | { |
412 | 0 | entry.dest_type = dest_type; |
413 | 0 | do |
414 | 0 | { |
415 | 0 | TransformEntry *e; |
416 | | |
417 | 0 | e = g_bsearch_array_lookup (transform_array, &transform_bconfig, &entry); |
418 | 0 | if (e) |
419 | 0 | { |
420 | | /* need to check that there hasn't been a change in value handling */ |
421 | 0 | if (g_type_value_table_peek (entry.dest_type) == g_type_value_table_peek (dest_type) && |
422 | 0 | g_type_value_table_peek (entry.src_type) == g_type_value_table_peek (src_type)) |
423 | 0 | return e->func; |
424 | 0 | } |
425 | 0 | entry.dest_type = transform_lookup_get_parent_type (entry.dest_type); |
426 | 0 | } |
427 | 0 | while (entry.dest_type); |
428 | | |
429 | 0 | entry.src_type = transform_lookup_get_parent_type (entry.src_type); |
430 | 0 | } |
431 | 0 | while (entry.src_type); |
432 | | |
433 | 0 | return NULL; |
434 | 0 | } |
435 | | |
436 | | static gint |
437 | | transform_entries_cmp (gconstpointer bsearch_node1, |
438 | | gconstpointer bsearch_node2) |
439 | 8.04k | { |
440 | 8.04k | const TransformEntry *e1 = bsearch_node1; |
441 | 8.04k | const TransformEntry *e2 = bsearch_node2; |
442 | 8.04k | gint cmp = G_BSEARCH_ARRAY_CMP (e1->src_type, e2->src_type); |
443 | | |
444 | 8.04k | if (cmp) |
445 | 5.40k | return cmp; |
446 | 2.64k | else |
447 | 2.64k | return G_BSEARCH_ARRAY_CMP (e1->dest_type, e2->dest_type); |
448 | 8.04k | } |
449 | | |
450 | | /** |
451 | | * g_value_register_transform_func: (skip) |
452 | | * @src_type: source type |
453 | | * @dest_type: target type |
454 | | * @transform_func: a function which transforms values of type @src_type |
455 | | * into values of type @dest_type |
456 | | * |
457 | | * Registers a value transformation function for use in |
458 | | * [method@GObject.Value.transform]. |
459 | | * |
460 | | * Any previously registered transformation function for @src_type and |
461 | | * @dest_type will be replaced. |
462 | | */ |
463 | | void |
464 | | g_value_register_transform_func (GType src_type, |
465 | | GType dest_type, |
466 | | GValueTransform transform_func) |
467 | 680 | { |
468 | 680 | TransformEntry entry; |
469 | | |
470 | | /* these checks won't pass for dynamic types. |
471 | | * g_return_if_fail (G_TYPE_HAS_VALUE_TABLE (src_type)); |
472 | | * g_return_if_fail (G_TYPE_HAS_VALUE_TABLE (dest_type)); |
473 | | */ |
474 | 680 | g_return_if_fail (transform_func != NULL); |
475 | | |
476 | 680 | entry.src_type = src_type; |
477 | 680 | entry.dest_type = dest_type; |
478 | | |
479 | | #if 0 /* let transform function replacement be a valid operation */ |
480 | | if (g_bsearch_array_lookup (transform_array, &transform_bconfig, &entry)) |
481 | | g_warning ("reregistering value transformation function (%p) for '%s' to '%s'", |
482 | | transform_func, |
483 | | g_type_name (src_type), |
484 | | g_type_name (dest_type)); |
485 | | #endif |
486 | | |
487 | 680 | entry.func = transform_func; |
488 | 680 | transform_array = g_bsearch_array_replace (transform_array, &transform_bconfig, &entry); |
489 | 680 | } |
490 | | |
491 | | /** |
492 | | * g_value_type_transformable: |
493 | | * @src_type: source type |
494 | | * @dest_type: target type |
495 | | * |
496 | | * Checks whether [method@GObject.Value.transform] is able to transform values |
497 | | * of type @src_type into values of type @dest_type. |
498 | | * |
499 | | * Note that for the types to be transformable, they must be compatible or a |
500 | | * transformation function must be registered using |
501 | | * [func@GObject.Value.register_transform_func]. |
502 | | * |
503 | | * Returns: true if the transformation is possible; false otherwise |
504 | | */ |
505 | | gboolean |
506 | | g_value_type_transformable (GType src_type, |
507 | | GType dest_type) |
508 | 0 | { |
509 | 0 | g_return_val_if_fail (src_type, FALSE); |
510 | 0 | g_return_val_if_fail (dest_type, FALSE); |
511 | | |
512 | 0 | return (g_value_type_compatible (src_type, dest_type) || |
513 | 0 | transform_func_lookup (src_type, dest_type) != NULL); |
514 | 0 | } |
515 | | |
516 | | /** |
517 | | * g_value_type_compatible: |
518 | | * @src_type: source type to be copied |
519 | | * @dest_type: destination type for copying |
520 | | * |
521 | | * Checks whether a [method@GObject.Value.copy] is able to copy values of type |
522 | | * @src_type into values of type @dest_type. |
523 | | * |
524 | | * Returns: true if the copy is possible; false otherwise |
525 | | */ |
526 | | gboolean |
527 | | g_value_type_compatible (GType src_type, |
528 | | GType dest_type) |
529 | 0 | { |
530 | 0 | g_return_val_if_fail (src_type, FALSE); |
531 | 0 | g_return_val_if_fail (dest_type, FALSE); |
532 | | |
533 | | /* Fast path */ |
534 | 0 | if (src_type == dest_type) |
535 | 0 | return TRUE; |
536 | | |
537 | 0 | return (g_type_is_a (src_type, dest_type) && |
538 | 0 | g_type_value_table_peek (dest_type) == g_type_value_table_peek (src_type)); |
539 | 0 | } |
540 | | |
541 | | /** |
542 | | * g_value_transform: |
543 | | * @src_value: source value |
544 | | * @dest_value: target value |
545 | | * |
546 | | * Tries to cast the contents of @src_value into a type appropriate |
547 | | * to store in @dest_value. |
548 | | * |
549 | | * If a transformation is not possible, @dest_value is not modified. |
550 | | * |
551 | | * For example, this could transform a `G_TYPE_INT` value into a `G_TYPE_FLOAT` |
552 | | * value. |
553 | | * |
554 | | * Performing transformations between value types might incur precision loss. |
555 | | * Especially transformations into strings might reveal seemingly arbitrary |
556 | | * results and the format of particular transformations to strings is not |
557 | | * guaranteed over time. |
558 | | * |
559 | | * Returns: true on success; false otherwise |
560 | | */ |
561 | | gboolean |
562 | | g_value_transform (const GValue *src_value, |
563 | | GValue *dest_value) |
564 | 0 | { |
565 | 0 | GType dest_type; |
566 | |
|
567 | 0 | g_return_val_if_fail (src_value, FALSE); |
568 | 0 | g_return_val_if_fail (dest_value, FALSE); |
569 | | |
570 | 0 | dest_type = G_VALUE_TYPE (dest_value); |
571 | 0 | if (g_value_type_compatible (G_VALUE_TYPE (src_value), dest_type)) |
572 | 0 | { |
573 | 0 | g_value_copy (src_value, dest_value); |
574 | | |
575 | 0 | return TRUE; |
576 | 0 | } |
577 | 0 | else |
578 | 0 | { |
579 | 0 | GValueTransform transform = transform_func_lookup (G_VALUE_TYPE (src_value), dest_type); |
580 | |
|
581 | 0 | if (transform) |
582 | 0 | { |
583 | 0 | g_value_unset (dest_value); |
584 | | |
585 | | /* setup and transform */ |
586 | 0 | value_meminit (dest_value, dest_type); |
587 | 0 | transform (src_value, dest_value); |
588 | | |
589 | 0 | return TRUE; |
590 | 0 | } |
591 | 0 | } |
592 | 0 | return FALSE; |
593 | 0 | } |