3 * Copyright (C) 2010 Openismus GmbH
6 * Tristan Van Berkom <tristanvb@openismus.com>
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
24 #include "gtkcelllayout.h"
25 #include "gtkcellarea.h"
28 static void gtk_cell_area_dispose (GObject *object);
29 static void gtk_cell_area_finalize (GObject *object);
31 /* GtkCellAreaClass */
32 static void gtk_cell_area_real_get_preferred_height_for_width (GtkCellArea *area,
36 gint *natural_height);
37 static void gtk_cell_area_real_get_preferred_width_for_height (GtkCellArea *area,
43 /* GtkCellLayoutIface */
44 static void gtk_cell_area_cell_layout_init (GtkCellLayoutIface *iface);
45 static void gtk_cell_area_pack_default (GtkCellLayout *cell_layout,
46 GtkCellRenderer *renderer,
48 static void gtk_cell_area_clear (GtkCellLayout *cell_layout);
49 static void gtk_cell_area_add_attribute (GtkCellLayout *cell_layout,
50 GtkCellRenderer *renderer,
51 const gchar *attribute,
53 static void gtk_cell_area_set_cell_data_func (GtkCellLayout *cell_layout,
54 GtkCellRenderer *cell,
55 GtkCellLayoutDataFunc func,
57 GDestroyNotify destroy);
58 static void gtk_cell_area_clear_attributes (GtkCellLayout *cell_layout,
59 GtkCellRenderer *renderer);
60 static void gtk_cell_area_reorder (GtkCellLayout *cell_layout,
61 GtkCellRenderer *cell,
63 static GList *gtk_cell_area_get_cells (GtkCellLayout *cell_layout);
65 /* Attribute/Cell metadata */
67 const gchar *attribute;
74 GtkCellLayoutDataFunc func;
76 GDestroyNotify destroy;
79 static CellInfo *cell_info_new (GtkCellLayoutDataFunc func,
81 GDestroyNotify destroy);
82 static void cell_info_free (CellInfo *info);
83 static CellAttribute *cell_attribute_new (GtkCellRenderer *renderer,
84 const gchar *attribute,
86 static void cell_attribute_free (CellAttribute *attribute);
87 static gint cell_attribute_find (CellAttribute *cell_attribute,
88 const gchar *attribute);
90 /* Struct to pass data along while looping over
91 * cell renderers to apply attributes
99 struct _GtkCellAreaPrivate
101 GHashTable *cell_info;
104 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GtkCellArea, gtk_cell_area, G_TYPE_INITIALLY_UNOWNED,
105 G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
106 gtk_cell_area_cell_layout_init));
109 gtk_cell_area_init (GtkCellArea *area)
111 GtkCellAreaPrivate *priv;
113 area->priv = G_TYPE_INSTANCE_GET_PRIVATE (area,
118 priv->cell_info = g_hash_table_new_full (g_direct_hash,
121 (GDestroyNotify)cell_info_free);
125 gtk_cell_area_class_init (GtkCellAreaClass *class)
127 GObjectClass *object_class = G_OBJECT_CLASS (class);
130 object_class->dispose = gtk_cell_area_dispose;
131 object_class->finalize = gtk_cell_area_finalize;
135 class->remove = NULL;
136 class->forall = NULL;
138 class->render = NULL;
141 class->get_request_mode = NULL;
142 class->get_preferred_width = NULL;
143 class->get_preferred_height = NULL;
144 class->get_preferred_height_for_width = gtk_cell_area_real_get_preferred_height_for_width;
145 class->get_preferred_width_for_height = gtk_cell_area_real_get_preferred_width_for_height;
147 g_type_class_add_private (object_class, sizeof (GtkCellAreaPrivate));
150 /*************************************************************
152 *************************************************************/
154 cell_info_new (GtkCellLayoutDataFunc func,
156 GDestroyNotify destroy)
158 CellInfo *info = g_slice_new (CellInfo);
160 info->attributes = NULL;
163 info->destroy = destroy;
169 cell_info_free (CellInfo *info)
172 info->destroy (info->data);
174 g_slist_foreach (info->attributes, (GFunc)cell_attribute_free, NULL);
175 g_slist_free (info->attributes);
177 g_slice_free (CellInfo, info);
180 static CellAttribute *
181 cell_attribute_new (GtkCellRenderer *renderer,
182 const gchar *attribute,
187 /* Check if the attribute really exists and point to
188 * the property string installed on the cell renderer
189 * class (dont dup the string)
191 pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (renderer), attribute);
195 CellAttribute *cell_attribute = g_slice_new (CellAttribute);
197 cell_attribute->attribute = pspec->name;
198 cell_attribute->column = column;
200 return cell_attribute;
207 cell_attribute_free (CellAttribute *attribute)
209 g_slice_free (CellAttribute, attribute);
212 /* GCompareFunc for g_slist_find_custom() */
214 cell_attribute_find (CellAttribute *cell_attribute,
215 const gchar *attribute)
217 return g_strcmp0 (cell_attribute->attribute, attribute);
220 /*************************************************************
222 *************************************************************/
224 gtk_cell_area_finalize (GObject *object)
226 GtkCellArea *area = GTK_CELL_AREA (object);
227 GtkCellAreaPrivate *priv = area->priv;
229 /* All cell renderers should already be removed at this point,
230 * just kill our hash table here.
232 g_hash_table_destroy (priv->cell_info);
234 G_OBJECT_CLASS (gtk_cell_area_parent_class)->finalize (object);
239 gtk_cell_area_dispose (GObject *object)
241 /* This removes every cell renderer that may be added to the GtkCellArea,
242 * subclasses should be breaking references to the GtkCellRenderers
245 gtk_cell_layout_clear (GTK_CELL_LAYOUT (object));
247 G_OBJECT_CLASS (gtk_cell_area_parent_class)->dispose (object);
251 /*************************************************************
253 *************************************************************/
255 gtk_cell_area_real_get_preferred_height_for_width (GtkCellArea *area,
258 gint *minimum_height,
259 gint *natural_height)
261 /* If the area doesnt do height-for-width, fallback on base preferred height */
262 GTK_CELL_AREA_GET_CLASS (area)->get_preferred_width (area, widget, minimum_height, natural_height);
266 gtk_cell_area_real_get_preferred_width_for_height (GtkCellArea *area,
272 /* If the area doesnt do width-for-height, fallback on base preferred width */
273 GTK_CELL_AREA_GET_CLASS (area)->get_preferred_width (area, widget, minimum_width, natural_width);
276 /*************************************************************
277 * GtkCellLayoutIface *
278 *************************************************************/
280 gtk_cell_area_cell_layout_init (GtkCellLayoutIface *iface)
282 iface->pack_start = gtk_cell_area_pack_default;
283 iface->pack_end = gtk_cell_area_pack_default;
284 iface->clear = gtk_cell_area_clear;
285 iface->add_attribute = gtk_cell_area_add_attribute;
286 iface->set_cell_data_func = gtk_cell_area_set_cell_data_func;
287 iface->clear_attributes = gtk_cell_area_clear_attributes;
288 iface->reorder = gtk_cell_area_reorder;
289 iface->get_cells = gtk_cell_area_get_cells;
293 gtk_cell_area_pack_default (GtkCellLayout *cell_layout,
294 GtkCellRenderer *renderer,
297 gtk_cell_area_add (GTK_CELL_AREA (cell_layout), renderer);
301 gtk_cell_area_clear (GtkCellLayout *cell_layout)
303 GtkCellArea *area = GTK_CELL_AREA (cell_layout);
305 gtk_cell_layout_get_cells (cell_layout);
307 for (l = cells; l; l = l->next)
309 GtkCellRenderer *renderer = l->data;
310 gtk_cell_area_remove (area, renderer);
317 gtk_cell_area_add_attribute (GtkCellLayout *cell_layout,
318 GtkCellRenderer *renderer,
319 const gchar *attribute,
322 gtk_cell_area_attribute_connect (GTK_CELL_AREA (cell_layout),
323 renderer, attribute, column);
327 gtk_cell_area_set_cell_data_func (GtkCellLayout *cell_layout,
328 GtkCellRenderer *renderer,
329 GtkCellLayoutDataFunc func,
331 GDestroyNotify destroy)
333 GtkCellArea *area = GTK_CELL_AREA (cell_layout);
334 GtkCellAreaPrivate *priv = area->priv;
337 info = g_hash_table_lookup (priv->cell_info, renderer);
341 if (info->destroy && info->data)
342 info->destroy (info->data);
347 info->data = func_data;
348 info->destroy = destroy;
354 info->destroy = NULL;
359 info = cell_info_new (func, func_data, destroy);
361 g_hash_table_insert (priv->cell_info, renderer, info);
366 gtk_cell_area_clear_attributes (GtkCellLayout *cell_layout,
367 GtkCellRenderer *renderer)
369 GtkCellArea *area = GTK_CELL_AREA (cell_layout);
370 GtkCellAreaPrivate *priv = area->priv;
373 info = g_hash_table_lookup (priv->cell_info, renderer);
377 g_slist_foreach (info->attributes, (GFunc)cell_attribute_free, NULL);
378 g_slist_free (info->attributes);
380 info->attributes = NULL;
385 gtk_cell_area_reorder (GtkCellLayout *cell_layout,
386 GtkCellRenderer *cell,
389 g_warning ("GtkCellLayout::reorder not implemented for `%s'",
390 g_type_name (G_TYPE_FROM_INSTANCE (cell_layout)));
394 accum_cells (GtkCellRenderer *renderer,
397 *accum = g_list_prepend (*accum, renderer);
401 gtk_cell_area_get_cells (GtkCellLayout *cell_layout)
405 gtk_cell_area_forall (GTK_CELL_AREA (cell_layout),
406 (GtkCellCallback)accum_cells,
409 return g_list_reverse (cells);
413 /*************************************************************
415 *************************************************************/
418 gtk_cell_area_add (GtkCellArea *area,
419 GtkCellRenderer *renderer)
421 GtkCellAreaClass *class;
423 g_return_if_fail (GTK_IS_CELL_AREA (area));
424 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
426 class = GTK_CELL_AREA_GET_CLASS (area);
429 class->add (area, renderer);
431 g_warning ("GtkCellAreaClass::add not implemented for `%s'",
432 g_type_name (G_TYPE_FROM_INSTANCE (area)));
436 gtk_cell_area_remove (GtkCellArea *area,
437 GtkCellRenderer *renderer)
439 GtkCellAreaClass *class;
440 GtkCellAreaPrivate *priv;
442 g_return_if_fail (GTK_IS_CELL_AREA (area));
443 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
445 class = GTK_CELL_AREA_GET_CLASS (area);
448 /* Remove any custom attributes and custom cell data func here first */
449 g_hash_table_remove (priv->cell_info, renderer);
452 class->remove (area, renderer);
454 g_warning ("GtkCellAreaClass::remove not implemented for `%s'",
455 g_type_name (G_TYPE_FROM_INSTANCE (area)));
459 gtk_cell_area_forall (GtkCellArea *area,
460 GtkCellCallback callback,
461 gpointer callback_data)
463 GtkCellAreaClass *class;
465 g_return_if_fail (GTK_IS_CELL_AREA (area));
466 g_return_if_fail (callback != NULL);
468 class = GTK_CELL_AREA_GET_CLASS (area);
471 class->forall (area, callback, callback_data);
473 g_warning ("GtkCellAreaClass::forall not implemented for `%s'",
474 g_type_name (G_TYPE_FROM_INSTANCE (area)));
478 gtk_cell_area_event (GtkCellArea *area,
481 const GdkRectangle *cell_area)
483 GtkCellAreaClass *class;
485 g_return_val_if_fail (GTK_IS_CELL_AREA (area), 0);
486 g_return_val_if_fail (GTK_IS_WIDGET (widget), 0);
487 g_return_val_if_fail (event != NULL, 0);
488 g_return_val_if_fail (cell_area != NULL, 0);
490 class = GTK_CELL_AREA_GET_CLASS (area);
493 return class->event (area, widget, event, cell_area);
495 g_warning ("GtkCellAreaClass::event not implemented for `%s'",
496 g_type_name (G_TYPE_FROM_INSTANCE (area)));
501 gtk_cell_area_render (GtkCellArea *area,
504 const GdkRectangle *cell_area)
506 GtkCellAreaClass *class;
508 g_return_if_fail (GTK_IS_CELL_AREA (area));
509 g_return_if_fail (cr != NULL);
510 g_return_if_fail (GTK_IS_WIDGET (widget));
511 g_return_if_fail (cell_area != NULL);
513 class = GTK_CELL_AREA_GET_CLASS (area);
516 class->render (area, cr, widget, cell_area);
518 g_warning ("GtkCellAreaClass::render not implemented for `%s'",
519 g_type_name (G_TYPE_FROM_INSTANCE (area)));
524 gtk_cell_area_get_request_mode (GtkCellArea *area)
526 GtkCellAreaClass *class;
528 g_return_val_if_fail (GTK_IS_CELL_AREA (area),
529 GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH);
531 class = GTK_CELL_AREA_GET_CLASS (area);
533 if (class->get_request_mode)
534 return class->get_request_mode (area);
536 g_warning ("GtkCellAreaClass::get_request_mode not implemented for `%s'",
537 g_type_name (G_TYPE_FROM_INSTANCE (area)));
539 return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
543 gtk_cell_area_get_preferred_width (GtkCellArea *area,
548 GtkCellAreaClass *class;
550 g_return_if_fail (GTK_IS_CELL_AREA (area));
551 g_return_if_fail (GTK_IS_WIDGET (widget));
553 class = GTK_CELL_AREA_GET_CLASS (area);
555 if (class->get_preferred_width)
556 class->get_preferred_width (area, widget, minimum_size, natural_size);
558 g_warning ("GtkCellAreaClass::get_preferred_width not implemented for `%s'",
559 g_type_name (G_TYPE_FROM_INSTANCE (area)));
563 gtk_cell_area_get_preferred_height_for_width (GtkCellArea *area,
566 gint *minimum_height,
567 gint *natural_height)
569 GtkCellAreaClass *class;
571 g_return_if_fail (GTK_IS_CELL_AREA (area));
572 g_return_if_fail (GTK_IS_WIDGET (widget));
574 class = GTK_CELL_AREA_GET_CLASS (area);
575 class->get_preferred_height_for_width (area, widget, width, minimum_height, natural_height);
579 gtk_cell_area_get_preferred_height (GtkCellArea *area,
584 GtkCellAreaClass *class;
586 g_return_if_fail (GTK_IS_CELL_AREA (area));
587 g_return_if_fail (GTK_IS_WIDGET (widget));
589 class = GTK_CELL_AREA_GET_CLASS (area);
591 if (class->get_preferred_height)
592 class->get_preferred_height (area, widget, minimum_size, natural_size);
594 g_warning ("GtkCellAreaClass::get_preferred_height not implemented for `%s'",
595 g_type_name (G_TYPE_FROM_INSTANCE (area)));
599 gtk_cell_area_get_preferred_width_for_height (GtkCellArea *area,
605 GtkCellAreaClass *class;
607 g_return_if_fail (GTK_IS_CELL_AREA (area));
608 g_return_if_fail (GTK_IS_WIDGET (widget));
610 class = GTK_CELL_AREA_GET_CLASS (area);
611 class->get_preferred_width_for_height (area, widget, height, minimum_width, natural_width);
615 gtk_cell_area_attribute_connect (GtkCellArea *area,
616 GtkCellRenderer *renderer,
617 const gchar *attribute,
620 GtkCellAreaPrivate *priv;
622 CellAttribute *cell_attribute;
624 g_return_if_fail (GTK_IS_CELL_AREA (area));
625 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
626 g_return_if_fail (attribute != NULL);
629 info = g_hash_table_lookup (priv->cell_info, renderer);
633 info = cell_info_new (NULL, NULL, NULL);
635 g_hash_table_insert (priv->cell_info, renderer, info);
641 /* Check we are not adding the same attribute twice */
642 if ((node = g_slist_find_custom (info->attributes, attribute,
643 (GCompareFunc)cell_attribute_find)) != NULL)
645 cell_attribute = node->data;
647 g_warning ("Cannot connect attribute `%s' for cell renderer class `%s' "
648 "since `%s' is already attributed to column %d",
650 g_type_name (G_TYPE_FROM_INSTANCE (area)),
651 attribute, cell_attribute->column);
656 cell_attribute = cell_attribute_new (renderer, attribute, column);
660 g_warning ("Cannot connect attribute `%s' for cell renderer class `%s' "
661 "since attribute does not exist",
663 g_type_name (G_TYPE_FROM_INSTANCE (area)));
667 info->attributes = g_slist_prepend (info->attributes, cell_attribute);
671 gtk_cell_area_attribute_disconnect (GtkCellArea *area,
672 GtkCellRenderer *renderer,
673 const gchar *attribute)
675 GtkCellAreaPrivate *priv;
677 CellAttribute *cell_attribute;
680 g_return_if_fail (GTK_IS_CELL_AREA (area));
681 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
682 g_return_if_fail (attribute != NULL);
685 info = g_hash_table_lookup (priv->cell_info, renderer);
689 node = g_slist_find_custom (info->attributes, attribute,
690 (GCompareFunc)cell_attribute_find);
693 cell_attribute = node->data;
695 cell_attribute_free (cell_attribute);
697 info->attributes = g_slist_delete_link (info->attributes, node);
703 apply_cell_attributes (GtkCellRenderer *renderer,
707 CellAttribute *attribute;
709 GValue value = { 0, };
711 /* Apply the attributes directly to the renderer */
712 for (list = info->attributes; list; list = list->next)
714 attribute = list->data;
716 gtk_tree_model_get_value (data->model, data->iter, attribute->column, &value);
717 g_object_set_property (G_OBJECT (renderer), attribute->attribute, &value);
718 g_value_unset (&value);
721 /* Call any GtkCellLayoutDataFunc that may have been set by the user
724 info->func (GTK_CELL_LAYOUT (data->area), renderer,
725 data->model, data->iter, info->data);
729 gtk_cell_area_apply_attributes (GtkCellArea *area,
730 GtkTreeModel *tree_model,
733 GtkCellAreaPrivate *priv;
736 g_return_if_fail (GTK_IS_CELL_AREA (area));
737 g_return_if_fail (GTK_IS_TREE_MODEL (tree_model));
738 g_return_if_fail (iter != NULL);
742 /* Go over any cells that have attributes or custom GtkCellLayoutDataFuncs and
743 * apply the data from the treemodel */
744 g_hash_table_foreach (priv->cell_info, (GHFunc)apply_cell_attributes, &data);