adg: inherit AdgDress from GEnum
[adg.git] / src / adg / adg-table.c
blob98698863363af478d8b3abb0427bec9c1fd40be8
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-dress.h"
54 #include "adg-style.h"
55 #include "adg-table-style.h"
56 #include "adg-path.h"
57 #include "adg-stroke.h"
58 #include "adg-container.h"
59 #include "adg-alignment.h"
60 #include "adg-entity-private.h"
62 #include "adg-table.h"
63 #include "adg-table-private.h"
64 #include "adg-table-row.h"
65 #include "adg-table-cell.h"
68 #define _ADG_OLD_OBJECT_CLASS ((GObjectClass *) adg_table_parent_class)
69 #define _ADG_OLD_ENTITY_CLASS ((AdgEntityClass *) adg_table_parent_class)
72 G_DEFINE_TYPE(AdgTable, adg_table, ADG_TYPE_ENTITY)
74 enum {
75 PROP_0,
76 PROP_TABLE_DRESS,
77 PROP_HAS_FRAME
80 typedef struct {
81 GCallback callback;
82 gpointer user_data;
83 } AdgClosure;
87 static void _adg_dispose (GObject *object);
88 static void _adg_finalize (GObject *object);
89 static void _adg_get_property (GObject *object,
90 guint param_id,
91 GValue *value,
92 GParamSpec *pspec);
93 static void _adg_set_property (GObject *object,
94 guint param_id,
95 const GValue *value,
96 GParamSpec *pspec);
97 static void _adg_destroy (AdgEntity *entity);
98 static void _adg_global_changed (AdgEntity *entity);
99 static void _adg_local_changed (AdgEntity *entity);
100 static void _adg_invalidate (AdgEntity *entity);
101 static void _adg_arrange (AdgEntity *entity);
102 static void _adg_arrange_grid (AdgEntity *entity);
103 static void _adg_arrange_frame (AdgEntity *entity,
104 const CpmlExtents *extents);
105 static void _adg_render (AdgEntity *entity,
106 cairo_t *cr);
107 static void _adg_propagate (AdgTable *table,
108 const gchar *detailed_signal,
109 ...);
110 static void _adg_foreach_row (AdgTableRow *table_row,
111 const AdgClosure *closure);
112 static void _adg_append_frame (AdgTableCell *table_cell,
113 AdgPath *path);
114 static void _adg_proxy_signal (AdgTableCell *table_cell,
115 AdgProxyData *proxy_data);
116 static gboolean _adg_value_match (gpointer key,
117 gpointer value,
118 gpointer user_data);
121 static void
122 adg_table_class_init(AdgTableClass *klass)
124 GObjectClass *gobject_class;
125 AdgEntityClass *entity_class;
126 GParamSpec *param;
128 gobject_class = (GObjectClass *) klass;
129 entity_class = (AdgEntityClass *) klass;
131 g_type_class_add_private(klass, sizeof(AdgTablePrivate));
133 gobject_class->dispose = _adg_dispose;
134 gobject_class->finalize = _adg_finalize;
135 gobject_class->get_property = _adg_get_property;
136 gobject_class->set_property = _adg_set_property;
138 entity_class->destroy = _adg_destroy;
139 entity_class->global_changed = _adg_global_changed;
140 entity_class->local_changed = _adg_local_changed;
141 entity_class->invalidate = _adg_invalidate;
142 entity_class->arrange = _adg_arrange;
143 entity_class->render = _adg_render;
145 param = adg_param_spec_dress("table-dress",
146 P_("Table Dress"),
147 P_("The dress to use for stroking this entity"),
148 ADG_DRESS_TABLE,
149 G_PARAM_READWRITE);
150 g_object_class_install_property(gobject_class, PROP_TABLE_DRESS, param);
152 param = g_param_spec_boolean("has-frame",
153 P_("Has Frame Flag"),
154 P_("If enabled, a frame using the proper dress found in this table style will be drawn around the table extents"),
155 TRUE,
156 G_PARAM_READWRITE);
157 g_object_class_install_property(gobject_class, PROP_HAS_FRAME, param);
160 static void
161 adg_table_init(AdgTable *table)
163 AdgTablePrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(table,
164 ADG_TYPE_TABLE,
165 AdgTablePrivate);
166 AdgEntityPrivate *entity_data = ((AdgEntity *) table)->data;
168 data->table_dress = ADG_DRESS_TABLE;
169 data->has_frame = TRUE;
171 data->table_style = NULL;
172 data->grid = NULL;
173 data->frame = NULL;
174 data->rows = NULL;
175 data->cell_names = NULL;
177 table->data = data;
179 /* Initialize to custom default some AdgEntity field by directly
180 * accessing the private struct to avoid notify signal emissions
182 entity_data->local_mix = ADG_MIX_DISABLED;
185 static void
186 _adg_dispose(GObject *object)
188 AdgTable *table = (AdgTable *) object;
189 AdgTablePrivate *data = table->data;
191 adg_table_invalidate_grid(table);
193 if (data->frame) {
194 g_object_unref(data->frame);
195 data->frame = NULL;
198 if (data->rows) {
199 adg_table_foreach_cell(table,
200 (GCallback) adg_table_cell_dispose, NULL);
201 data->rows = NULL;
204 if (_ADG_OLD_OBJECT_CLASS->dispose)
205 _ADG_OLD_OBJECT_CLASS->dispose(object);
208 static void
209 _adg_finalize(GObject *object)
211 AdgTable *table;
212 AdgTablePrivate *data;
214 table = (AdgTable *) object;
215 data = table->data;
217 if (data->rows) {
218 g_slist_foreach(data->rows, (GFunc) adg_table_row_free, NULL);
219 g_slist_free(data->rows);
222 if (data->cell_names)
223 g_hash_table_destroy(data->cell_names);
225 if (_ADG_OLD_OBJECT_CLASS->finalize)
226 _ADG_OLD_OBJECT_CLASS->finalize(object);
229 static void
230 _adg_get_property(GObject *object, guint prop_id,
231 GValue *value, GParamSpec *pspec)
233 AdgTablePrivate *data = ((AdgTable *) object)->data;
235 switch (prop_id) {
236 case PROP_TABLE_DRESS:
237 g_value_set_enum(value, data->table_dress);
238 break;
239 case PROP_HAS_FRAME:
240 g_value_set_boolean(value, data->has_frame);
241 break;
242 default:
243 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
244 break;
248 static void
249 _adg_set_property(GObject *object, guint prop_id,
250 const GValue *value, GParamSpec *pspec)
252 AdgTablePrivate *data = ((AdgTable *) object)->data;
254 switch (prop_id) {
255 case PROP_TABLE_DRESS:
256 data->table_dress = g_value_get_enum(value);
257 break;
258 case PROP_HAS_FRAME:
259 data->has_frame = g_value_get_boolean(value);
260 break;
261 default:
262 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
263 break;
269 * adg_table_new:
271 * Creates a new empty table entity.
273 * Returns: the newly created table entity
275 * Since: 1.0
277 AdgTable *
278 adg_table_new(void)
280 return g_object_new(ADG_TYPE_TABLE, NULL);
284 * adg_table_insert:
285 * @table: an #AdgTable
286 * @table_row: a valid #AdgTableRow
287 * @before_row: (allow-none): an #AdgTableRow or %NULL
289 * Inserts @table_row inside the rows list of @table. If @before_row
290 * is specified, @table_row is inserted before it.
292 * Since: 1.0
294 void
295 adg_table_insert(AdgTable *table, AdgTableRow *table_row,
296 AdgTableRow *before_row)
298 AdgTablePrivate *data;
300 g_return_if_fail(ADG_IS_TABLE(table));
301 g_return_if_fail(table_row != NULL);
303 data = table->data;
305 if (before_row == NULL) {
306 data->rows = g_slist_append(data->rows, table_row);
307 } else {
308 GSList *before = g_slist_find(data->rows, before_row);
310 /* This MUST be present, otherwise something really bad happened */
311 g_return_if_fail(before != NULL);
313 data->rows = g_slist_insert_before(data->rows, before, table_row);
318 * adg_table_remove:
319 * @table: an #AdgTable
320 * @table_row: a valid #AdgTableRow
322 * Removes @table_row from list of rows of @table.
324 * Since: 1.0
326 void
327 adg_table_remove(AdgTable *table, AdgTableRow *table_row)
329 AdgTablePrivate *data;
331 g_return_if_fail(ADG_IS_TABLE(table));
332 g_return_if_fail(table_row != NULL);
334 data = table->data;
335 data->rows = g_slist_remove(data->rows, table_row);
339 * adg_table_foreach:
340 * @table: an #AdgTable
341 * @callback: (scope call): a callback
342 * @user_data: callback user data
344 * Invokes @callback on each row of @table.
345 * The callback should be declared as:
347 * |[
348 * void callback(AdgTableRow *table_row, gpointer user_data);
349 * ]|
351 * Since: 1.0
353 void
354 adg_table_foreach(AdgTable *table, GCallback callback, gpointer user_data)
356 AdgTablePrivate *data = table->data;
358 g_return_if_fail(table != NULL);
359 g_return_if_fail(callback != NULL);
361 g_slist_foreach(data->rows, (GFunc) callback, user_data);
365 * adg_table_foreach_cell:
366 * @table: an #AdgTable
367 * @callback: (scope call): a callback
368 * @user_data: callback user data
370 * Invokes @callback on each cell of @table.
371 * The callback should be declared as:
373 * |[
374 * void callback(AdgTableCell *table_cell, gpointer user_data);
375 * ]|
377 * Since: 1.0
379 void
380 adg_table_foreach_cell(AdgTable *table,
381 GCallback callback, gpointer user_data)
383 AdgClosure closure = { callback, user_data };
384 adg_table_foreach(table, (GCallback) _adg_foreach_row, &closure);
388 * adg_table_set_cell:
389 * @table: an #AdgTable
390 * @name: the name of the cell
391 * @table_cell: the named cell
393 * Binds @table_cell to @name, so it can be accessed by name later
394 * with adg_table_get_cell(). Internally the binding is handled with
395 * an hash table, so accessing the cell this way is O(1).
397 * If @name is %NULL, any binding to @ŧable_cell will be removed.
398 * This is quite inefficient because the whole hash table must be scanned.
400 * If @table_cell is %NULL, the key with @name in the hash table will
401 * be removed.
403 * Both @name and @table_cell cannot be %NULL at the same time.
405 * Since: 1.0
407 void
408 adg_table_set_cell(AdgTable *table, const gchar *name,
409 AdgTableCell *table_cell)
411 AdgTablePrivate *data;
413 g_return_if_fail(ADG_IS_TABLE(table));
414 g_return_if_fail(name != NULL || table_cell != NULL);
416 data = table->data;
418 if (data->cell_names == NULL) {
419 /* Check if trying to remove from an empty hash table */
420 if (name == NULL || table_cell == NULL)
421 return;
423 data->cell_names = g_hash_table_new_full(g_str_hash, g_str_equal,
424 g_free, NULL);
427 if (name == NULL) {
428 /* _adg_value_match() will return the key in user_data[1] */
429 gpointer user_data[] = { table_cell, NULL };
430 g_hash_table_find(data->cell_names, _adg_value_match, user_data);
431 g_hash_table_remove(data->cell_names, user_data[1]);
432 } else if (table_cell == NULL) {
433 g_hash_table_remove(data->cell_names, name);
434 } else {
435 g_hash_table_insert(data->cell_names, g_strdup(name), table_cell);
440 * adg_table_get_table_style:
441 * @table: an #AdgTable
443 * Gets the #AdgTableStyle explicitely set on @table. This is a kind
444 * of accessor function: for rendering purpose use adg_entity_style()
445 * instead. The returned object is owned by @table and should not be
446 * freed or modified.
448 * Returns: (transfer none): the requested style or %NULL on errors.
450 * Since: 1.0
452 AdgStyle *
453 adg_table_get_table_style(AdgTable *table)
455 AdgTablePrivate *data;
457 g_return_val_if_fail(ADG_IS_TABLE(table), NULL);
459 data = table->data;
460 return (AdgStyle *) data->table_style;
464 * adg_table_get_cell:
465 * @table: an #AdgTable
466 * @name: the name of a cell
468 * Gets the cell named @name inside @table. Only named cells
469 * can be retrieved by this method.
471 * The returned cell is owned by @ŧable and must not be
472 * modified or freed.
474 * Returns: (transfer none): the requested cell or %NULL if not found.
476 * Since: 1.0
478 AdgTableCell *
479 adg_table_get_cell(AdgTable *table, const gchar *name)
481 AdgTablePrivate *data;
483 g_return_val_if_fail(ADG_IS_TABLE(table), NULL);
485 data = table->data;
487 if (data->cell_names == NULL)
488 return NULL;
490 return g_hash_table_lookup(data->cell_names, name);
494 * adg_table_set_table_dress:
495 * @table: an #AdgTable
496 * @dress: the new #AdgDress to use
498 * Sets a new table dress for rendering @table. The new dress
499 * must be related to the original dress for this property:
500 * you cannot set a dress used for line styles to a dress
501 * managing fonts.
503 * The check is done by calling adg_dress_are_related() with
504 * @dress and the previous dress as arguments. Check out its
505 * documentation for details on what is a related dress.
507 * Since: 1.0
509 void
510 adg_table_set_table_dress(AdgTable *table, AdgDress dress)
512 g_return_if_fail(ADG_IS_TABLE(table));
513 g_object_set(table, "table-dress", dress, NULL);
517 * adg_table_get_table_dress:
518 * @table: an #AdgTable
520 * Gets the table dress to be used in rendering @table.
522 * Returns: (transfer none): the current table dress.
524 * Since: 1.0
526 AdgDress
527 adg_table_get_table_dress(AdgTable *table)
529 AdgTablePrivate *data;
531 g_return_val_if_fail(ADG_IS_TABLE(table), ADG_DRESS_UNDEFINED);
533 data = table->data;
535 return data->table_dress;
539 * adg_table_switch_frame:
540 * @table: an #AdgTable
541 * @new_state: the new state of the frame
543 * Sets the #AdgTable:has-frame property: %TRUE will draw a
544 * frame around the whole table using the #AdgTableStyle:frame-dress
545 * dress of the table style.
547 * Since: 1.0
549 void
550 adg_table_switch_frame(AdgTable *table, gboolean new_state)
552 g_return_if_fail(ADG_IS_TABLE(table));
553 g_object_set(table, "has-frame", new_state, NULL);
557 * adg_table_has_frame:
558 * @table: an #AdgTable
560 * Returns the state of the #AdgTable:has-frame property.
562 * Returns: the current state.
564 * Since: 1.0
566 gboolean
567 adg_table_has_frame(AdgTable *table)
569 AdgTablePrivate *data;
571 g_return_val_if_fail(ADG_IS_TABLE(table), FALSE);
573 data = table->data;
575 return data->has_frame;
579 * adg_table_invalidate_grid:
580 * @table: an #AdgTable
582 * <note><para>
583 * This method is only useful in table children implementation.
584 * </para></note>
586 * Clears the internal grid cache, effectively forcing its
587 * regeneration next time the #AdgEntity::arrange signal is emitted.
589 void
590 adg_table_invalidate_grid(AdgTable *table)
592 AdgTablePrivate *data;
594 g_return_if_fail(ADG_IS_TABLE(table));
596 data = table->data;
598 if (data->grid) {
599 g_object_unref(data->grid);
600 data->grid = NULL;
605 static void
606 _adg_destroy(AdgEntity *entity)
608 _adg_propagate((AdgTable *) entity, "destroy");
610 if (_ADG_OLD_ENTITY_CLASS->destroy)
611 _ADG_OLD_ENTITY_CLASS->destroy(entity);
614 static void
615 _adg_global_changed(AdgEntity *entity)
617 if (_ADG_OLD_ENTITY_CLASS->global_changed)
618 _ADG_OLD_ENTITY_CLASS->global_changed(entity);
620 _adg_propagate((AdgTable *) entity, "global-changed");
623 static void
624 _adg_local_changed(AdgEntity *entity)
626 if (_ADG_OLD_ENTITY_CLASS->local_changed)
627 _ADG_OLD_ENTITY_CLASS->local_changed(entity);
629 _adg_propagate((AdgTable *) entity, "local-changed");
632 static void
633 _adg_invalidate(AdgEntity *entity)
635 _adg_propagate((AdgTable *) entity, "invalidate");
638 static void
639 _adg_arrange(AdgEntity *entity)
641 AdgTable *table;
642 AdgTablePrivate *data;
643 CpmlExtents extents = { 0 };
644 CpmlExtents row_layout = { 0 };
645 const CpmlExtents *row_extents;
646 const CpmlPair *spacing;
647 const CpmlPair *size;
648 GSList *row_node;
649 AdgTableRow *row;
651 table = (AdgTable *) entity;
652 data = table->data;
654 /* Resolve the table style */
655 if (data->table_style == NULL)
656 data->table_style = (AdgTableStyle *)
657 adg_entity_style(entity, data->table_dress);
659 spacing = adg_table_style_get_cell_spacing(data->table_style);
661 /* Compute the size of the table */
662 for (row_node = data->rows; row_node; row_node = row_node->next) {
663 row = row_node->data;
664 size = adg_table_row_size_request(row);
666 if (size->x > extents.size.x)
667 extents.size.x = size->x;
668 extents.size.y += size->y;
671 /* Arrange the layout of the table components */
672 row_layout.org.x = extents.org.x;
673 row_layout.org.y = extents.org.y + spacing->y;
674 row_layout.size.x = extents.size.x;
675 row_layout.size.y = -1;
676 for (row_node = data->rows; row_node; row_node = row_node->next) {
677 row = row_node->data;
678 row_extents = adg_table_row_arrange(row, &row_layout);
679 row_layout.org.y += row_extents->size.y + spacing->y;
682 _adg_arrange_grid(entity);
683 _adg_arrange_frame(entity, &extents);
685 extents.is_defined = TRUE;
686 cpml_extents_transform(&extents, adg_entity_get_global_matrix(entity));
687 cpml_extents_transform(&extents, adg_entity_get_local_matrix(entity));
688 adg_entity_set_extents(entity, &extents);
691 static void
692 _adg_arrange_grid(AdgEntity *entity)
694 AdgTable *table;
695 AdgTablePrivate *data;
696 AdgPath *path;
697 AdgTrail *trail;
698 AdgDress dress;
700 table = (AdgTable *) entity;
701 data = table->data;
703 if (data->grid)
704 return;
706 path = adg_path_new();
707 trail = (AdgTrail *) path;
709 adg_table_foreach_cell(table, (GCallback) _adg_append_frame, path);
711 if (!adg_trail_get_extents(trail)->is_defined)
712 return;
714 dress = adg_table_style_get_grid_dress(data->table_style);
715 data->grid = g_object_new(ADG_TYPE_STROKE,
716 "line-dress", dress,
717 "trail", trail,
718 "parent", entity,
719 NULL);
720 adg_entity_arrange((AdgEntity *) data->grid);
723 static void
724 _adg_arrange_frame(AdgEntity *entity, const CpmlExtents *extents)
726 AdgTablePrivate *data;
727 AdgPath *path;
728 AdgTrail *trail;
729 CpmlPair pair;
730 AdgDress dress;
732 data = ((AdgTable *) entity)->data;
734 if (data->frame || !data->has_frame)
735 return;
737 path = adg_path_new();
738 trail = (AdgTrail *) path;
740 cpml_pair_copy(&pair, &extents->org);
741 adg_path_move_to(path, &pair);
742 pair.x += extents->size.x;
743 adg_path_line_to(path, &pair);
744 pair.y += extents->size.y;
745 adg_path_line_to(path, &pair);
746 pair.x -= extents->size.x;
747 adg_path_line_to(path, &pair);
748 adg_path_close(path);
750 dress = adg_table_style_get_frame_dress(data->table_style);
751 data->frame = g_object_new(ADG_TYPE_STROKE,
752 "line-dress", dress,
753 "trail", trail,
754 "parent", entity,
755 NULL);
756 adg_entity_arrange((AdgEntity *) data->frame);
759 static void
760 _adg_render(AdgEntity *entity, cairo_t *cr)
762 AdgTablePrivate *data = ((AdgTable *) entity)->data;
764 adg_style_apply((AdgStyle *) data->table_style, entity, cr);
766 _adg_propagate((AdgTable *) entity, "render", cr);
769 static void
770 _adg_propagate(AdgTable *table, const gchar *detailed_signal, ...)
772 va_list var_copy;
773 AdgTablePrivate *data;
774 AdgProxyData proxy_data;
776 if (!g_signal_parse_name(detailed_signal, G_TYPE_FROM_INSTANCE(table),
777 &proxy_data.signal_id, &proxy_data.detail, FALSE)) {
778 g_return_if_reached();
781 va_start(proxy_data.var_args, detailed_signal);
782 data = table->data;
784 if (data->frame) {
785 G_VA_COPY(var_copy, proxy_data.var_args);
786 g_signal_emit_valist(data->frame, proxy_data.signal_id,
787 proxy_data.detail, var_copy);
790 if (data->grid) {
791 G_VA_COPY(var_copy, proxy_data.var_args);
792 g_signal_emit_valist(data->grid, proxy_data.signal_id,
793 proxy_data.detail, var_copy);
796 adg_table_foreach_cell(table, (GCallback) _adg_proxy_signal, &proxy_data);
797 va_end(proxy_data.var_args);
800 static void
801 _adg_foreach_row(AdgTableRow *table_row, const AdgClosure *closure)
803 adg_table_row_foreach(table_row, closure->callback, closure->user_data);
806 static void
807 _adg_append_frame(AdgTableCell *table_cell, AdgPath *path)
809 CpmlPair pair;
810 const CpmlExtents *extents;
812 if (! adg_table_cell_has_frame(table_cell))
813 return;
815 extents = adg_table_cell_get_extents(table_cell);
817 cpml_pair_copy(&pair, &extents->org);
818 adg_path_move_to(path, &pair);
819 pair.x += extents->size.x;
820 adg_path_line_to(path, &pair);
821 pair.y += extents->size.y;
822 adg_path_line_to(path, &pair);
823 pair.x -= extents->size.x;
824 adg_path_line_to(path, &pair);
825 adg_path_close(path);
828 static void
829 _adg_proxy_signal(AdgTableCell *table_cell, AdgProxyData *proxy_data)
831 AdgEntity *entity;
832 AdgAlignment *alignment;
833 va_list var_copy;
835 entity = adg_table_cell_title(table_cell);
836 if (entity) {
837 alignment = (AdgAlignment *) adg_entity_get_parent(entity);
838 G_VA_COPY(var_copy, proxy_data->var_args);
839 g_signal_emit_valist(alignment, proxy_data->signal_id,
840 proxy_data->detail, var_copy);
843 entity = adg_table_cell_value(table_cell);
844 if (entity) {
845 alignment = (AdgAlignment *) adg_entity_get_parent(entity);
846 G_VA_COPY(var_copy, proxy_data->var_args);
847 g_signal_emit_valist(alignment, proxy_data->signal_id,
848 proxy_data->detail, var_copy);
852 static gboolean
853 _adg_value_match(gpointer key, gpointer value, gpointer user_data)
855 gpointer *array = user_data;
857 if (value == array[0]) {
858 array[1] = key;
859 return TRUE;
861 return FALSE;