adg: "transfer none" on get_cairo_path()
[adg.git] / src / adg / adg-table.c
blob314f1ed0f543cd9794a97942034b459b704c6a92
1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007,2008,2009,2010,2011,2012,2013 Nicola Fontana <ntd at entidi.it>
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.
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.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
21 /**
22 * SECTION:adg-table
23 * @short_description: A tabular entity
25 * The #AdgTable is the entity to be used for rendering data arranged
26 * in tabular evironments.
28 * To define a table, you should add to it a serie of one or more
29 * #AdgTableRow by using the #AdgTableRow specific APIs.
31 * <note><para>
32 * By default, the #AdgText:local-mix property is set to
33 * #ADG_MIX_DISABLED on #AdgTable entities.
34 * </para></note>
36 * Since: 1.0
37 **/
39 /**
40 * AdgTable:
42 * All fields are private and should not be used directly.
43 * Use its public methods instead.
45 * Since: 1.0
46 **/
49 #include "adg-internal.h"
51 #include "adg-model.h"
52 #include "adg-trail.h"
53 #include "adg-style.h"
54 #include "adg-table-style.h"
55 #include "adg-path.h"
56 #include "adg-stroke.h"
57 #include "adg-container.h"
58 #include "adg-alignment.h"
59 #include "adg-entity-private.h"
60 #include "adg-dress.h"
61 #include "adg-param-dress.h"
63 #include "adg-table.h"
64 #include "adg-table-private.h"
65 #include "adg-table-row.h"
66 #include "adg-table-cell.h"
69 #define _ADG_OLD_OBJECT_CLASS ((GObjectClass *) adg_table_parent_class)
70 #define _ADG_OLD_ENTITY_CLASS ((AdgEntityClass *) adg_table_parent_class)
73 G_DEFINE_TYPE(AdgTable, adg_table, ADG_TYPE_ENTITY)
75 enum {
76 PROP_0,
77 PROP_TABLE_DRESS,
78 PROP_HAS_FRAME
81 typedef struct {
82 GCallback callback;
83 gpointer user_data;
84 } AdgClosure;
88 static void _adg_dispose (GObject *object);
89 static void _adg_finalize (GObject *object);
90 static void _adg_get_property (GObject *object,
91 guint param_id,
92 GValue *value,
93 GParamSpec *pspec);
94 static void _adg_set_property (GObject *object,
95 guint param_id,
96 const GValue *value,
97 GParamSpec *pspec);
98 static void _adg_destroy (AdgEntity *entity);
99 static void _adg_global_changed (AdgEntity *entity);
100 static void _adg_local_changed (AdgEntity *entity);
101 static void _adg_invalidate (AdgEntity *entity);
102 static void _adg_arrange (AdgEntity *entity);
103 static void _adg_arrange_grid (AdgEntity *entity);
104 static void _adg_arrange_frame (AdgEntity *entity,
105 const CpmlExtents *extents);
106 static void _adg_render (AdgEntity *entity,
107 cairo_t *cr);
108 static void _adg_propagate (AdgTable *table,
109 const gchar *detailed_signal,
110 ...);
111 static void _adg_foreach_row (AdgTableRow *table_row,
112 const AdgClosure *closure);
113 static void _adg_append_frame (AdgTableCell *table_cell,
114 AdgPath *path);
115 static void _adg_proxy_signal (AdgTableCell *table_cell,
116 AdgProxyData *proxy_data);
117 static gboolean _adg_value_match (gpointer key,
118 gpointer value,
119 gpointer user_data);
122 static void
123 adg_table_class_init(AdgTableClass *klass)
125 GObjectClass *gobject_class;
126 AdgEntityClass *entity_class;
127 GParamSpec *param;
129 gobject_class = (GObjectClass *) klass;
130 entity_class = (AdgEntityClass *) klass;
132 g_type_class_add_private(klass, sizeof(AdgTablePrivate));
134 gobject_class->dispose = _adg_dispose;
135 gobject_class->finalize = _adg_finalize;
136 gobject_class->get_property = _adg_get_property;
137 gobject_class->set_property = _adg_set_property;
139 entity_class->destroy = _adg_destroy;
140 entity_class->global_changed = _adg_global_changed;
141 entity_class->local_changed = _adg_local_changed;
142 entity_class->invalidate = _adg_invalidate;
143 entity_class->arrange = _adg_arrange;
144 entity_class->render = _adg_render;
146 param = adg_param_spec_dress("table-dress",
147 P_("Table Dress"),
148 P_("The dress to use for stroking this entity"),
149 ADG_DRESS_TABLE,
150 G_PARAM_READWRITE);
151 g_object_class_install_property(gobject_class, PROP_TABLE_DRESS, param);
153 param = g_param_spec_boolean("has-frame",
154 P_("Has Frame Flag"),
155 P_("If enabled, a frame using the proper dress found in this table style will be drawn around the table extents"),
156 TRUE,
157 G_PARAM_READWRITE);
158 g_object_class_install_property(gobject_class, PROP_HAS_FRAME, param);
161 static void
162 adg_table_init(AdgTable *table)
164 AdgTablePrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(table,
165 ADG_TYPE_TABLE,
166 AdgTablePrivate);
167 AdgEntityPrivate *entity_data = ((AdgEntity *) table)->data;
169 data->table_dress = ADG_DRESS_TABLE;
170 data->has_frame = TRUE;
172 data->table_style = NULL;
173 data->grid = NULL;
174 data->frame = NULL;
175 data->rows = NULL;
176 data->cell_names = NULL;
178 table->data = data;
180 /* Initialize to custom default some AdgEntity field by directly
181 * accessing the private struct to avoid notify signal emissions
183 entity_data->local_mix = ADG_MIX_DISABLED;
186 static void
187 _adg_dispose(GObject *object)
189 AdgTable *table = (AdgTable *) object;
190 AdgTablePrivate *data = table->data;
192 adg_table_invalidate_grid(table);
194 if (data->frame) {
195 g_object_unref(data->frame);
196 data->frame = NULL;
199 if (data->rows) {
200 adg_table_foreach_cell(table,
201 (GCallback) adg_table_cell_dispose, NULL);
202 data->rows = NULL;
205 if (_ADG_OLD_OBJECT_CLASS->dispose)
206 _ADG_OLD_OBJECT_CLASS->dispose(object);
209 static void
210 _adg_finalize(GObject *object)
212 AdgTable *table;
213 AdgTablePrivate *data;
215 table = (AdgTable *) object;
216 data = table->data;
218 if (data->rows) {
219 g_slist_foreach(data->rows, (GFunc) adg_table_row_free, NULL);
220 g_slist_free(data->rows);
223 if (data->cell_names)
224 g_hash_table_destroy(data->cell_names);
226 if (_ADG_OLD_OBJECT_CLASS->finalize)
227 _ADG_OLD_OBJECT_CLASS->finalize(object);
230 static void
231 _adg_get_property(GObject *object, guint prop_id,
232 GValue *value, GParamSpec *pspec)
234 AdgTablePrivate *data = ((AdgTable *) object)->data;
236 switch (prop_id) {
237 case PROP_TABLE_DRESS:
238 g_value_set_enum(value, data->table_dress);
239 break;
240 case PROP_HAS_FRAME:
241 g_value_set_boolean(value, data->has_frame);
242 break;
243 default:
244 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
245 break;
249 static void
250 _adg_set_property(GObject *object, guint prop_id,
251 const GValue *value, GParamSpec *pspec)
253 AdgTablePrivate *data = ((AdgTable *) object)->data;
255 switch (prop_id) {
256 case PROP_TABLE_DRESS:
257 data->table_dress = g_value_get_enum(value);
258 break;
259 case PROP_HAS_FRAME:
260 data->has_frame = g_value_get_boolean(value);
261 break;
262 default:
263 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
264 break;
270 * adg_table_new:
272 * Creates a new empty table entity.
274 * Returns: the newly created table entity
276 * Since: 1.0
278 AdgTable *
279 adg_table_new(void)
281 return g_object_new(ADG_TYPE_TABLE, NULL);
285 * adg_table_insert:
286 * @table: an #AdgTable
287 * @table_row: a valid #AdgTableRow
288 * @before_row: (allow-none): an #AdgTableRow or %NULL
290 * Inserts @table_row inside the rows list of @table. If @before_row
291 * is specified, @table_row is inserted before it.
293 * Since: 1.0
295 void
296 adg_table_insert(AdgTable *table, AdgTableRow *table_row,
297 AdgTableRow *before_row)
299 AdgTablePrivate *data;
301 g_return_if_fail(ADG_IS_TABLE(table));
302 g_return_if_fail(table_row != NULL);
304 data = table->data;
306 if (before_row == NULL) {
307 data->rows = g_slist_append(data->rows, table_row);
308 } else {
309 GSList *before = g_slist_find(data->rows, before_row);
311 /* This MUST be present, otherwise something really bad happened */
312 g_return_if_fail(before != NULL);
314 data->rows = g_slist_insert_before(data->rows, before, table_row);
319 * adg_table_remove:
320 * @table: an #AdgTable
321 * @table_row: a valid #AdgTableRow
323 * Removes @table_row from list of rows of @table.
325 * Since: 1.0
327 void
328 adg_table_remove(AdgTable *table, AdgTableRow *table_row)
330 AdgTablePrivate *data;
332 g_return_if_fail(ADG_IS_TABLE(table));
333 g_return_if_fail(table_row != NULL);
335 data = table->data;
336 data->rows = g_slist_remove(data->rows, table_row);
340 * adg_table_foreach:
341 * @table: an #AdgTable
342 * @callback: (scope call): a callback
343 * @user_data: callback user data
345 * Invokes @callback on each row of @table.
346 * The callback should be declared as:
348 * |[
349 * void callback(AdgTableRow *table_row, gpointer user_data);
350 * ]|
352 * Since: 1.0
354 void
355 adg_table_foreach(AdgTable *table, GCallback callback, gpointer user_data)
357 AdgTablePrivate *data = table->data;
359 g_return_if_fail(table != NULL);
360 g_return_if_fail(callback != NULL);
362 g_slist_foreach(data->rows, (GFunc) callback, user_data);
366 * adg_table_foreach_cell:
367 * @table: an #AdgTable
368 * @callback: (scope call): a callback
369 * @user_data: callback user data
371 * Invokes @callback on each cell of @table.
372 * The callback should be declared as:
374 * |[
375 * void callback(AdgTableCell *table_cell, gpointer user_data);
376 * ]|
378 * Since: 1.0
380 void
381 adg_table_foreach_cell(AdgTable *table,
382 GCallback callback, gpointer user_data)
384 AdgClosure closure = { callback, user_data };
385 adg_table_foreach(table, (GCallback) _adg_foreach_row, &closure);
389 * adg_table_set_cell:
390 * @table: an #AdgTable
391 * @name: the name of the cell
392 * @table_cell: the named cell
394 * Binds @table_cell to @name, so it can be accessed by name later
395 * with adg_table_get_cell(). Internally the binding is handled with
396 * an hash table, so accessing the cell this way is O(1).
398 * If @name is %NULL, any binding to @ŧable_cell will be removed.
399 * This is quite inefficient because the whole hash table must be scanned.
401 * If @table_cell is %NULL, the key with @name in the hash table will
402 * be removed.
404 * Both @name and @table_cell cannot be %NULL at the same time.
406 * Since: 1.0
408 void
409 adg_table_set_cell(AdgTable *table, const gchar *name,
410 AdgTableCell *table_cell)
412 AdgTablePrivate *data;
414 g_return_if_fail(ADG_IS_TABLE(table));
415 g_return_if_fail(name != NULL || table_cell != NULL);
417 data = table->data;
419 if (data->cell_names == NULL) {
420 /* Check if trying to remove from an empty hash table */
421 if (name == NULL || table_cell == NULL)
422 return;
424 data->cell_names = g_hash_table_new_full(g_str_hash, g_str_equal,
425 g_free, NULL);
428 if (name == NULL) {
429 /* _adg_value_match() will return the key in user_data[1] */
430 gpointer user_data[] = { table_cell, NULL };
431 g_hash_table_find(data->cell_names, _adg_value_match, user_data);
432 g_hash_table_remove(data->cell_names, user_data[1]);
433 } else if (table_cell == NULL) {
434 g_hash_table_remove(data->cell_names, name);
435 } else {
436 g_hash_table_insert(data->cell_names, g_strdup(name), table_cell);
441 * adg_table_get_table_style:
442 * @table: an #AdgTable
444 * Gets the #AdgTableStyle explicitely set on @table. This is a kind
445 * of accessor function: for rendering purpose use adg_entity_style()
446 * instead. The returned object is owned by @table and should not be
447 * freed or modified.
449 * Returns: (transfer none): the requested style or %NULL on errors.
451 * Since: 1.0
453 AdgStyle *
454 adg_table_get_table_style(AdgTable *table)
456 AdgTablePrivate *data;
458 g_return_val_if_fail(ADG_IS_TABLE(table), NULL);
460 data = table->data;
461 return (AdgStyle *) data->table_style;
465 * adg_table_get_cell:
466 * @table: an #AdgTable
467 * @name: the name of a cell
469 * Gets the cell named @name inside @table. Only named cells
470 * can be retrieved by this method.
472 * The returned cell is owned by @ŧable and must not be
473 * modified or freed.
475 * Returns: (transfer none): the requested cell or %NULL if not found.
477 * Since: 1.0
479 AdgTableCell *
480 adg_table_get_cell(AdgTable *table, const gchar *name)
482 AdgTablePrivate *data;
484 g_return_val_if_fail(ADG_IS_TABLE(table), NULL);
486 data = table->data;
488 if (data->cell_names == NULL)
489 return NULL;
491 return g_hash_table_lookup(data->cell_names, name);
495 * adg_table_set_table_dress:
496 * @table: an #AdgTable
497 * @dress: the new #AdgDress to use
499 * Sets a new table dress for rendering @table. The new dress
500 * must be related to the original dress for this property:
501 * you cannot set a dress used for line styles to a dress
502 * managing fonts.
504 * The check is done by calling adg_dress_are_related() with
505 * @dress and the previous dress as arguments. Check out its
506 * documentation for details on what is a related dress.
508 * Since: 1.0
510 void
511 adg_table_set_table_dress(AdgTable *table, AdgDress dress)
513 g_return_if_fail(ADG_IS_TABLE(table));
514 g_object_set(table, "table-dress", dress, NULL);
518 * adg_table_get_table_dress:
519 * @table: an #AdgTable
521 * Gets the table dress to be used in rendering @table.
523 * Returns: (transfer none): the current table dress.
525 * Since: 1.0
527 AdgDress
528 adg_table_get_table_dress(AdgTable *table)
530 AdgTablePrivate *data;
532 g_return_val_if_fail(ADG_IS_TABLE(table), ADG_DRESS_UNDEFINED);
534 data = table->data;
536 return data->table_dress;
540 * adg_table_switch_frame:
541 * @table: an #AdgTable
542 * @new_state: the new state of the frame
544 * Sets the #AdgTable:has-frame property: %TRUE will draw a
545 * frame around the whole table using the #AdgTableStyle:frame-dress
546 * dress of the table style.
548 * Since: 1.0
550 void
551 adg_table_switch_frame(AdgTable *table, gboolean new_state)
553 g_return_if_fail(ADG_IS_TABLE(table));
554 g_object_set(table, "has-frame", new_state, NULL);
558 * adg_table_has_frame:
559 * @table: an #AdgTable
561 * Returns the state of the #AdgTable:has-frame property.
563 * Returns: the current state.
565 * Since: 1.0
567 gboolean
568 adg_table_has_frame(AdgTable *table)
570 AdgTablePrivate *data;
572 g_return_val_if_fail(ADG_IS_TABLE(table), FALSE);
574 data = table->data;
576 return data->has_frame;
580 * adg_table_invalidate_grid:
581 * @table: an #AdgTable
583 * <note><para>
584 * This method is only useful in table children implementation.
585 * </para></note>
587 * Clears the internal grid cache, effectively forcing its
588 * regeneration next time the #AdgEntity::arrange signal is emitted.
590 void
591 adg_table_invalidate_grid(AdgTable *table)
593 AdgTablePrivate *data;
595 g_return_if_fail(ADG_IS_TABLE(table));
597 data = table->data;
599 if (data->grid) {
600 g_object_unref(data->grid);
601 data->grid = NULL;
606 static void
607 _adg_destroy(AdgEntity *entity)
609 _adg_propagate((AdgTable *) entity, "destroy");
611 if (_ADG_OLD_ENTITY_CLASS->destroy)
612 _ADG_OLD_ENTITY_CLASS->destroy(entity);
615 static void
616 _adg_global_changed(AdgEntity *entity)
618 if (_ADG_OLD_ENTITY_CLASS->global_changed)
619 _ADG_OLD_ENTITY_CLASS->global_changed(entity);
621 _adg_propagate((AdgTable *) entity, "global-changed");
624 static void
625 _adg_local_changed(AdgEntity *entity)
627 if (_ADG_OLD_ENTITY_CLASS->local_changed)
628 _ADG_OLD_ENTITY_CLASS->local_changed(entity);
630 _adg_propagate((AdgTable *) entity, "local-changed");
633 static void
634 _adg_invalidate(AdgEntity *entity)
636 _adg_propagate((AdgTable *) entity, "invalidate");
639 static void
640 _adg_arrange(AdgEntity *entity)
642 AdgTable *table;
643 AdgTablePrivate *data;
644 CpmlExtents extents = { 0 };
645 CpmlExtents row_layout = { 0 };
646 const CpmlExtents *row_extents;
647 const CpmlPair *spacing;
648 const CpmlPair *size;
649 GSList *row_node;
650 AdgTableRow *row;
652 table = (AdgTable *) entity;
653 data = table->data;
655 /* Resolve the table style */
656 if (data->table_style == NULL)
657 data->table_style = (AdgTableStyle *)
658 adg_entity_style(entity, data->table_dress);
660 spacing = adg_table_style_get_cell_spacing(data->table_style);
662 /* Compute the size of the table */
663 for (row_node = data->rows; row_node; row_node = row_node->next) {
664 row = row_node->data;
665 size = adg_table_row_size_request(row);
667 if (size->x > extents.size.x)
668 extents.size.x = size->x;
669 extents.size.y += size->y;
672 /* Arrange the layout of the table components */
673 row_layout.org.x = extents.org.x;
674 row_layout.org.y = extents.org.y + spacing->y;
675 row_layout.size.x = extents.size.x;
676 row_layout.size.y = -1;
677 for (row_node = data->rows; row_node; row_node = row_node->next) {
678 row = row_node->data;
679 row_extents = adg_table_row_arrange(row, &row_layout);
680 row_layout.org.y += row_extents->size.y + spacing->y;
683 _adg_arrange_grid(entity);
684 _adg_arrange_frame(entity, &extents);
686 extents.is_defined = TRUE;
687 cpml_extents_transform(&extents, adg_entity_get_global_matrix(entity));
688 cpml_extents_transform(&extents, adg_entity_get_local_matrix(entity));
689 adg_entity_set_extents(entity, &extents);
692 static void
693 _adg_arrange_grid(AdgEntity *entity)
695 AdgTable *table;
696 AdgTablePrivate *data;
697 AdgPath *path;
698 AdgTrail *trail;
699 AdgDress dress;
701 table = (AdgTable *) entity;
702 data = table->data;
704 if (data->grid)
705 return;
707 path = adg_path_new();
708 trail = (AdgTrail *) path;
710 adg_table_foreach_cell(table, (GCallback) _adg_append_frame, path);
712 if (!adg_trail_get_extents(trail)->is_defined)
713 return;
715 dress = adg_table_style_get_grid_dress(data->table_style);
716 data->grid = g_object_new(ADG_TYPE_STROKE,
717 "line-dress", dress,
718 "trail", trail,
719 "parent", entity,
720 NULL);
721 adg_entity_arrange((AdgEntity *) data->grid);
724 static void
725 _adg_arrange_frame(AdgEntity *entity, const CpmlExtents *extents)
727 AdgTablePrivate *data;
728 AdgPath *path;
729 AdgTrail *trail;
730 CpmlPair pair;
731 AdgDress dress;
733 data = ((AdgTable *) entity)->data;
735 if (data->frame || !data->has_frame)
736 return;
738 path = adg_path_new();
739 trail = (AdgTrail *) path;
741 cpml_pair_copy(&pair, &extents->org);
742 adg_path_move_to(path, &pair);
743 pair.x += extents->size.x;
744 adg_path_line_to(path, &pair);
745 pair.y += extents->size.y;
746 adg_path_line_to(path, &pair);
747 pair.x -= extents->size.x;
748 adg_path_line_to(path, &pair);
749 adg_path_close(path);
751 dress = adg_table_style_get_frame_dress(data->table_style);
752 data->frame = g_object_new(ADG_TYPE_STROKE,
753 "line-dress", dress,
754 "trail", trail,
755 "parent", entity,
756 NULL);
757 adg_entity_arrange((AdgEntity *) data->frame);
760 static void
761 _adg_render(AdgEntity *entity, cairo_t *cr)
763 AdgTablePrivate *data = ((AdgTable *) entity)->data;
765 adg_style_apply((AdgStyle *) data->table_style, entity, cr);
767 _adg_propagate((AdgTable *) entity, "render", cr);
770 static void
771 _adg_propagate(AdgTable *table, const gchar *detailed_signal, ...)
773 va_list var_copy;
774 AdgTablePrivate *data;
775 AdgProxyData proxy_data;
777 if (!g_signal_parse_name(detailed_signal, G_TYPE_FROM_INSTANCE(table),
778 &proxy_data.signal_id, &proxy_data.detail, FALSE)) {
779 g_return_if_reached();
782 va_start(proxy_data.var_args, detailed_signal);
783 data = table->data;
785 if (data->frame) {
786 G_VA_COPY(var_copy, proxy_data.var_args);
787 g_signal_emit_valist(data->frame, proxy_data.signal_id,
788 proxy_data.detail, var_copy);
791 if (data->grid) {
792 G_VA_COPY(var_copy, proxy_data.var_args);
793 g_signal_emit_valist(data->grid, proxy_data.signal_id,
794 proxy_data.detail, var_copy);
797 adg_table_foreach_cell(table, (GCallback) _adg_proxy_signal, &proxy_data);
798 va_end(proxy_data.var_args);
801 static void
802 _adg_foreach_row(AdgTableRow *table_row, const AdgClosure *closure)
804 adg_table_row_foreach(table_row, closure->callback, closure->user_data);
807 static void
808 _adg_append_frame(AdgTableCell *table_cell, AdgPath *path)
810 CpmlPair pair;
811 const CpmlExtents *extents;
813 if (! adg_table_cell_has_frame(table_cell))
814 return;
816 extents = adg_table_cell_get_extents(table_cell);
818 cpml_pair_copy(&pair, &extents->org);
819 adg_path_move_to(path, &pair);
820 pair.x += extents->size.x;
821 adg_path_line_to(path, &pair);
822 pair.y += extents->size.y;
823 adg_path_line_to(path, &pair);
824 pair.x -= extents->size.x;
825 adg_path_line_to(path, &pair);
826 adg_path_close(path);
829 static void
830 _adg_proxy_signal(AdgTableCell *table_cell, AdgProxyData *proxy_data)
832 AdgEntity *entity;
833 AdgAlignment *alignment;
834 va_list var_copy;
836 entity = adg_table_cell_title(table_cell);
837 if (entity) {
838 alignment = (AdgAlignment *) adg_entity_get_parent(entity);
839 G_VA_COPY(var_copy, proxy_data->var_args);
840 g_signal_emit_valist(alignment, proxy_data->signal_id,
841 proxy_data->detail, var_copy);
844 entity = adg_table_cell_value(table_cell);
845 if (entity) {
846 alignment = (AdgAlignment *) adg_entity_get_parent(entity);
847 G_VA_COPY(var_copy, proxy_data->var_args);
848 g_signal_emit_valist(alignment, proxy_data->signal_id,
849 proxy_data->detail, var_copy);
853 static gboolean
854 _adg_value_match(gpointer key, gpointer value, gpointer user_data)
856 gpointer *array = user_data;
858 if (value == array[0]) {
859 array[1] = key;
860 return TRUE;
862 return FALSE;