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.
31 #include "gtkcelllayout.h"
32 #include "gtkcellarea.h"
33 #include "gtkcellareaiter.h"
34 #include "gtkmarshalers.h"
35 #include "gtkprivate.h"
37 #include <gobject/gvaluecollector.h>
41 static void gtk_cell_area_dispose (GObject *object);
42 static void gtk_cell_area_finalize (GObject *object);
43 static void gtk_cell_area_set_property (GObject *object,
47 static void gtk_cell_area_get_property (GObject *object,
52 /* GtkCellAreaClass */
53 static void gtk_cell_area_real_get_preferred_height_for_width (GtkCellArea *area,
54 GtkCellAreaIter *iter,
58 gint *natural_height);
59 static void gtk_cell_area_real_get_preferred_width_for_height (GtkCellArea *area,
60 GtkCellAreaIter *iter,
66 /* GtkCellLayoutIface */
67 static void gtk_cell_area_cell_layout_init (GtkCellLayoutIface *iface);
68 static void gtk_cell_area_pack_default (GtkCellLayout *cell_layout,
69 GtkCellRenderer *renderer,
71 static void gtk_cell_area_clear (GtkCellLayout *cell_layout);
72 static void gtk_cell_area_add_attribute (GtkCellLayout *cell_layout,
73 GtkCellRenderer *renderer,
74 const gchar *attribute,
76 static void gtk_cell_area_set_cell_data_func (GtkCellLayout *cell_layout,
77 GtkCellRenderer *cell,
78 GtkCellLayoutDataFunc func,
80 GDestroyNotify destroy);
81 static void gtk_cell_area_clear_attributes (GtkCellLayout *cell_layout,
82 GtkCellRenderer *renderer);
83 static void gtk_cell_area_reorder (GtkCellLayout *cell_layout,
84 GtkCellRenderer *cell,
86 static GList *gtk_cell_area_get_cells (GtkCellLayout *cell_layout);
88 /* Attribute/Cell metadata */
90 const gchar *attribute;
97 GtkCellLayoutDataFunc func;
99 GDestroyNotify destroy;
102 static CellInfo *cell_info_new (GtkCellLayoutDataFunc func,
104 GDestroyNotify destroy);
105 static void cell_info_free (CellInfo *info);
106 static CellAttribute *cell_attribute_new (GtkCellRenderer *renderer,
107 const gchar *attribute,
109 static void cell_attribute_free (CellAttribute *attribute);
110 static gint cell_attribute_find (CellAttribute *cell_attribute,
111 const gchar *attribute);
113 /* Struct to pass data along while looping over
114 * cell renderers to apply attributes
120 gboolean is_expander;
121 gboolean is_expanded;
124 struct _GtkCellAreaPrivate
126 GHashTable *cell_info;
128 GtkBorder cell_border;
130 GtkCellRenderer *focus_cell;
137 PROP_CELL_MARGIN_LEFT,
138 PROP_CELL_MARGIN_RIGHT,
139 PROP_CELL_MARGIN_TOP,
140 PROP_CELL_MARGIN_BOTTOM
148 /* Keep the paramspec pool internal, no need to deliver notifications
149 * on cells. at least no percieved need for now */
150 static GParamSpecPool *cell_property_pool = NULL;
151 static guint cell_area_signals[LAST_SIGNAL] = { 0 };
153 #define PARAM_SPEC_PARAM_ID(pspec) ((pspec)->param_id)
154 #define PARAM_SPEC_SET_PARAM_ID(pspec, id) ((pspec)->param_id = (id))
157 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GtkCellArea, gtk_cell_area, G_TYPE_INITIALLY_UNOWNED,
158 G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
159 gtk_cell_area_cell_layout_init));
162 gtk_cell_area_init (GtkCellArea *area)
164 GtkCellAreaPrivate *priv;
166 area->priv = G_TYPE_INSTANCE_GET_PRIVATE (area,
171 priv->cell_info = g_hash_table_new_full (g_direct_hash,
174 (GDestroyNotify)cell_info_free);
176 priv->cell_border.left = 0;
177 priv->cell_border.right = 0;
178 priv->cell_border.top = 0;
179 priv->cell_border.bottom = 0;
181 priv->focus_cell = NULL;
185 gtk_cell_area_class_init (GtkCellAreaClass *class)
187 GObjectClass *object_class = G_OBJECT_CLASS (class);
190 object_class->dispose = gtk_cell_area_dispose;
191 object_class->finalize = gtk_cell_area_finalize;
192 object_class->get_property = gtk_cell_area_get_property;
193 object_class->set_property = gtk_cell_area_set_property;
197 class->remove = NULL;
198 class->forall = NULL;
200 class->render = NULL;
203 class->create_iter = NULL;
204 class->get_request_mode = NULL;
205 class->get_preferred_width = NULL;
206 class->get_preferred_height = NULL;
207 class->get_preferred_height_for_width = gtk_cell_area_real_get_preferred_height_for_width;
208 class->get_preferred_width_for_height = gtk_cell_area_real_get_preferred_width_for_height;
211 class->grab_focus = NULL;
214 cell_area_signals[SIGNAL_FOCUS_LEAVE] =
215 g_signal_new (I_("focus-leave"),
216 G_TYPE_FROM_CLASS (object_class),
218 0, /* Class offset (just a notification, no class handler) */
220 _gtk_marshal_VOID__ENUM_STRING,
222 GTK_TYPE_DIRECTION_TYPE, G_TYPE_STRING);
226 g_object_class_install_property (object_class,
227 PROP_CELL_MARGIN_LEFT,
230 P_("Margin on Left"),
231 P_("Pixels of extra space on the left side of each cell"),
235 GTK_PARAM_READWRITE));
237 g_object_class_install_property (object_class,
238 PROP_CELL_MARGIN_RIGHT,
240 ("cell-margin-right",
241 P_("Margin on Right"),
242 P_("Pixels of extra space on the right side of each cell"),
246 GTK_PARAM_READWRITE));
248 g_object_class_install_property (object_class,
249 PROP_CELL_MARGIN_TOP,
253 P_("Pixels of extra space on the top side of each cell"),
257 GTK_PARAM_READWRITE));
259 g_object_class_install_property (object_class,
260 PROP_CELL_MARGIN_BOTTOM,
262 ("cell-margin-bottom",
263 P_("Margin on Bottom"),
264 P_("Pixels of extra space on the bottom side of each cell"),
268 GTK_PARAM_READWRITE));
270 /* Pool for Cell Properties */
271 if (!cell_property_pool)
272 cell_property_pool = g_param_spec_pool_new (FALSE);
274 g_type_class_add_private (object_class, sizeof (GtkCellAreaPrivate));
277 /*************************************************************
279 *************************************************************/
281 cell_info_new (GtkCellLayoutDataFunc func,
283 GDestroyNotify destroy)
285 CellInfo *info = g_slice_new (CellInfo);
287 info->attributes = NULL;
290 info->destroy = destroy;
296 cell_info_free (CellInfo *info)
299 info->destroy (info->data);
301 g_slist_foreach (info->attributes, (GFunc)cell_attribute_free, NULL);
302 g_slist_free (info->attributes);
304 g_slice_free (CellInfo, info);
307 static CellAttribute *
308 cell_attribute_new (GtkCellRenderer *renderer,
309 const gchar *attribute,
314 /* Check if the attribute really exists and point to
315 * the property string installed on the cell renderer
316 * class (dont dup the string)
318 pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (renderer), attribute);
322 CellAttribute *cell_attribute = g_slice_new (CellAttribute);
324 cell_attribute->attribute = pspec->name;
325 cell_attribute->column = column;
327 return cell_attribute;
334 cell_attribute_free (CellAttribute *attribute)
336 g_slice_free (CellAttribute, attribute);
339 /* GCompareFunc for g_slist_find_custom() */
341 cell_attribute_find (CellAttribute *cell_attribute,
342 const gchar *attribute)
344 return g_strcmp0 (cell_attribute->attribute, attribute);
347 /*************************************************************
349 *************************************************************/
351 gtk_cell_area_finalize (GObject *object)
353 GtkCellArea *area = GTK_CELL_AREA (object);
354 GtkCellAreaPrivate *priv = area->priv;
356 /* All cell renderers should already be removed at this point,
357 * just kill our hash table here.
359 g_hash_table_destroy (priv->cell_info);
361 G_OBJECT_CLASS (gtk_cell_area_parent_class)->finalize (object);
366 gtk_cell_area_dispose (GObject *object)
368 /* This removes every cell renderer that may be added to the GtkCellArea,
369 * subclasses should be breaking references to the GtkCellRenderers
372 gtk_cell_layout_clear (GTK_CELL_LAYOUT (object));
374 /* Remove any ref to a focused cell */
375 gtk_cell_area_set_focus_cell (GTK_CELL_AREA (object), NULL);
377 G_OBJECT_CLASS (gtk_cell_area_parent_class)->dispose (object);
381 gtk_cell_area_set_property (GObject *object,
386 GtkCellArea *area = GTK_CELL_AREA (object);
390 case PROP_CELL_MARGIN_LEFT:
391 gtk_cell_area_set_cell_margin_left (area, g_value_get_int (value));
393 case PROP_CELL_MARGIN_RIGHT:
394 gtk_cell_area_set_cell_margin_right (area, g_value_get_int (value));
396 case PROP_CELL_MARGIN_TOP:
397 gtk_cell_area_set_cell_margin_top (area, g_value_get_int (value));
399 case PROP_CELL_MARGIN_BOTTOM:
400 gtk_cell_area_set_cell_margin_bottom (area, g_value_get_int (value));
403 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
409 gtk_cell_area_get_property (GObject *object,
414 GtkCellArea *area = GTK_CELL_AREA (object);
415 GtkCellAreaPrivate *priv = area->priv;
419 case PROP_CELL_MARGIN_LEFT:
420 g_value_set_int (value, priv->cell_border.left);
422 case PROP_CELL_MARGIN_RIGHT:
423 g_value_set_int (value, priv->cell_border.right);
425 case PROP_CELL_MARGIN_TOP:
426 g_value_set_int (value, priv->cell_border.top);
428 case PROP_CELL_MARGIN_BOTTOM:
429 g_value_set_int (value, priv->cell_border.bottom);
432 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
437 /*************************************************************
439 *************************************************************/
441 gtk_cell_area_real_get_preferred_height_for_width (GtkCellArea *area,
442 GtkCellAreaIter *iter,
445 gint *minimum_height,
446 gint *natural_height)
448 /* If the area doesnt do height-for-width, fallback on base preferred height */
449 GTK_CELL_AREA_GET_CLASS (area)->get_preferred_width (area, iter, widget, minimum_height, natural_height);
453 gtk_cell_area_real_get_preferred_width_for_height (GtkCellArea *area,
454 GtkCellAreaIter *iter,
460 /* If the area doesnt do width-for-height, fallback on base preferred width */
461 GTK_CELL_AREA_GET_CLASS (area)->get_preferred_width (area, iter, widget, minimum_width, natural_width);
464 /*************************************************************
465 * GtkCellLayoutIface *
466 *************************************************************/
468 gtk_cell_area_cell_layout_init (GtkCellLayoutIface *iface)
470 iface->pack_start = gtk_cell_area_pack_default;
471 iface->pack_end = gtk_cell_area_pack_default;
472 iface->clear = gtk_cell_area_clear;
473 iface->add_attribute = gtk_cell_area_add_attribute;
474 iface->set_cell_data_func = gtk_cell_area_set_cell_data_func;
475 iface->clear_attributes = gtk_cell_area_clear_attributes;
476 iface->reorder = gtk_cell_area_reorder;
477 iface->get_cells = gtk_cell_area_get_cells;
481 gtk_cell_area_pack_default (GtkCellLayout *cell_layout,
482 GtkCellRenderer *renderer,
485 gtk_cell_area_add (GTK_CELL_AREA (cell_layout), renderer);
489 gtk_cell_area_clear (GtkCellLayout *cell_layout)
491 GtkCellArea *area = GTK_CELL_AREA (cell_layout);
493 gtk_cell_layout_get_cells (cell_layout);
495 for (l = cells; l; l = l->next)
497 GtkCellRenderer *renderer = l->data;
498 gtk_cell_area_remove (area, renderer);
505 gtk_cell_area_add_attribute (GtkCellLayout *cell_layout,
506 GtkCellRenderer *renderer,
507 const gchar *attribute,
510 gtk_cell_area_attribute_connect (GTK_CELL_AREA (cell_layout),
511 renderer, attribute, column);
515 gtk_cell_area_set_cell_data_func (GtkCellLayout *cell_layout,
516 GtkCellRenderer *renderer,
517 GtkCellLayoutDataFunc func,
519 GDestroyNotify destroy)
521 GtkCellArea *area = GTK_CELL_AREA (cell_layout);
522 GtkCellAreaPrivate *priv = area->priv;
525 info = g_hash_table_lookup (priv->cell_info, renderer);
529 if (info->destroy && info->data)
530 info->destroy (info->data);
535 info->data = func_data;
536 info->destroy = destroy;
542 info->destroy = NULL;
547 info = cell_info_new (func, func_data, destroy);
549 g_hash_table_insert (priv->cell_info, renderer, info);
554 gtk_cell_area_clear_attributes (GtkCellLayout *cell_layout,
555 GtkCellRenderer *renderer)
557 GtkCellArea *area = GTK_CELL_AREA (cell_layout);
558 GtkCellAreaPrivate *priv = area->priv;
561 info = g_hash_table_lookup (priv->cell_info, renderer);
565 g_slist_foreach (info->attributes, (GFunc)cell_attribute_free, NULL);
566 g_slist_free (info->attributes);
568 info->attributes = NULL;
573 gtk_cell_area_reorder (GtkCellLayout *cell_layout,
574 GtkCellRenderer *cell,
577 g_warning ("GtkCellLayout::reorder not implemented for `%s'",
578 g_type_name (G_TYPE_FROM_INSTANCE (cell_layout)));
582 accum_cells (GtkCellRenderer *renderer,
585 *accum = g_list_prepend (*accum, renderer);
589 gtk_cell_area_get_cells (GtkCellLayout *cell_layout)
593 gtk_cell_area_forall (GTK_CELL_AREA (cell_layout),
594 (GtkCellCallback)accum_cells,
597 return g_list_reverse (cells);
601 /*************************************************************
603 *************************************************************/
605 gtk_cell_area_add (GtkCellArea *area,
606 GtkCellRenderer *renderer)
608 GtkCellAreaClass *class;
610 g_return_if_fail (GTK_IS_CELL_AREA (area));
611 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
613 class = GTK_CELL_AREA_GET_CLASS (area);
616 class->add (area, renderer);
618 g_warning ("GtkCellAreaClass::add not implemented for `%s'",
619 g_type_name (G_TYPE_FROM_INSTANCE (area)));
623 gtk_cell_area_remove (GtkCellArea *area,
624 GtkCellRenderer *renderer)
626 GtkCellAreaClass *class;
627 GtkCellAreaPrivate *priv;
629 g_return_if_fail (GTK_IS_CELL_AREA (area));
630 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
632 class = GTK_CELL_AREA_GET_CLASS (area);
635 /* Remove any custom attributes and custom cell data func here first */
636 g_hash_table_remove (priv->cell_info, renderer);
639 class->remove (area, renderer);
641 g_warning ("GtkCellAreaClass::remove not implemented for `%s'",
642 g_type_name (G_TYPE_FROM_INSTANCE (area)));
646 gtk_cell_area_forall (GtkCellArea *area,
647 GtkCellCallback callback,
648 gpointer callback_data)
650 GtkCellAreaClass *class;
652 g_return_if_fail (GTK_IS_CELL_AREA (area));
653 g_return_if_fail (callback != NULL);
655 class = GTK_CELL_AREA_GET_CLASS (area);
658 class->forall (area, callback, callback_data);
660 g_warning ("GtkCellAreaClass::forall not implemented for `%s'",
661 g_type_name (G_TYPE_FROM_INSTANCE (area)));
665 gtk_cell_area_event (GtkCellArea *area,
666 GtkCellAreaIter *iter,
669 const GdkRectangle *cell_area,
670 GtkCellRendererState flags)
672 GtkCellAreaClass *class;
674 g_return_val_if_fail (GTK_IS_CELL_AREA (area), 0);
675 g_return_val_if_fail (GTK_IS_CELL_AREA_ITER (iter), 0);
676 g_return_val_if_fail (GTK_IS_WIDGET (widget), 0);
677 g_return_val_if_fail (event != NULL, 0);
678 g_return_val_if_fail (cell_area != NULL, 0);
680 class = GTK_CELL_AREA_GET_CLASS (area);
683 return class->event (area, iter, widget, event, cell_area, flags);
685 g_warning ("GtkCellAreaClass::event not implemented for `%s'",
686 g_type_name (G_TYPE_FROM_INSTANCE (area)));
691 gtk_cell_area_render (GtkCellArea *area,
692 GtkCellAreaIter *iter,
695 const GdkRectangle *cell_area,
696 GtkCellRendererState flags)
698 GtkCellAreaClass *class;
700 g_return_if_fail (GTK_IS_CELL_AREA (area));
701 g_return_if_fail (GTK_IS_CELL_AREA_ITER (iter));
702 g_return_if_fail (GTK_IS_WIDGET (widget));
703 g_return_if_fail (cr != NULL);
704 g_return_if_fail (cell_area != NULL);
706 class = GTK_CELL_AREA_GET_CLASS (area);
709 class->render (area, iter, widget, cr, cell_area, flags);
711 g_warning ("GtkCellAreaClass::render not implemented for `%s'",
712 g_type_name (G_TYPE_FROM_INSTANCE (area)));
715 /*************************************************************
717 *************************************************************/
719 gtk_cell_area_create_iter (GtkCellArea *area)
721 GtkCellAreaClass *class;
723 g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL);
725 class = GTK_CELL_AREA_GET_CLASS (area);
727 if (class->create_iter)
728 return class->create_iter (area);
730 g_warning ("GtkCellAreaClass::create_iter not implemented for `%s'",
731 g_type_name (G_TYPE_FROM_INSTANCE (area)));
738 gtk_cell_area_get_request_mode (GtkCellArea *area)
740 GtkCellAreaClass *class;
742 g_return_val_if_fail (GTK_IS_CELL_AREA (area),
743 GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH);
745 class = GTK_CELL_AREA_GET_CLASS (area);
747 if (class->get_request_mode)
748 return class->get_request_mode (area);
750 g_warning ("GtkCellAreaClass::get_request_mode not implemented for `%s'",
751 g_type_name (G_TYPE_FROM_INSTANCE (area)));
753 return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
757 gtk_cell_area_get_preferred_width (GtkCellArea *area,
758 GtkCellAreaIter *iter,
763 GtkCellAreaClass *class;
765 g_return_if_fail (GTK_IS_CELL_AREA (area));
766 g_return_if_fail (GTK_IS_WIDGET (widget));
768 class = GTK_CELL_AREA_GET_CLASS (area);
770 if (class->get_preferred_width)
771 class->get_preferred_width (area, iter, widget, minimum_size, natural_size);
773 g_warning ("GtkCellAreaClass::get_preferred_width not implemented for `%s'",
774 g_type_name (G_TYPE_FROM_INSTANCE (area)));
778 gtk_cell_area_get_preferred_height_for_width (GtkCellArea *area,
779 GtkCellAreaIter *iter,
782 gint *minimum_height,
783 gint *natural_height)
785 GtkCellAreaClass *class;
787 g_return_if_fail (GTK_IS_CELL_AREA (area));
788 g_return_if_fail (GTK_IS_WIDGET (widget));
790 class = GTK_CELL_AREA_GET_CLASS (area);
791 class->get_preferred_height_for_width (area, iter, widget, width, minimum_height, natural_height);
795 gtk_cell_area_get_preferred_height (GtkCellArea *area,
796 GtkCellAreaIter *iter,
801 GtkCellAreaClass *class;
803 g_return_if_fail (GTK_IS_CELL_AREA (area));
804 g_return_if_fail (GTK_IS_WIDGET (widget));
806 class = GTK_CELL_AREA_GET_CLASS (area);
808 if (class->get_preferred_height)
809 class->get_preferred_height (area, iter, widget, minimum_size, natural_size);
811 g_warning ("GtkCellAreaClass::get_preferred_height not implemented for `%s'",
812 g_type_name (G_TYPE_FROM_INSTANCE (area)));
816 gtk_cell_area_get_preferred_width_for_height (GtkCellArea *area,
817 GtkCellAreaIter *iter,
823 GtkCellAreaClass *class;
825 g_return_if_fail (GTK_IS_CELL_AREA (area));
826 g_return_if_fail (GTK_IS_WIDGET (widget));
828 class = GTK_CELL_AREA_GET_CLASS (area);
829 class->get_preferred_width_for_height (area, iter, widget, height, minimum_width, natural_width);
832 /*************************************************************
834 *************************************************************/
836 gtk_cell_area_attribute_connect (GtkCellArea *area,
837 GtkCellRenderer *renderer,
838 const gchar *attribute,
841 GtkCellAreaPrivate *priv;
843 CellAttribute *cell_attribute;
845 g_return_if_fail (GTK_IS_CELL_AREA (area));
846 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
847 g_return_if_fail (attribute != NULL);
850 info = g_hash_table_lookup (priv->cell_info, renderer);
854 info = cell_info_new (NULL, NULL, NULL);
856 g_hash_table_insert (priv->cell_info, renderer, info);
862 /* Check we are not adding the same attribute twice */
863 if ((node = g_slist_find_custom (info->attributes, attribute,
864 (GCompareFunc)cell_attribute_find)) != NULL)
866 cell_attribute = node->data;
868 g_warning ("Cannot connect attribute `%s' for cell renderer class `%s' "
869 "since `%s' is already attributed to column %d",
871 g_type_name (G_TYPE_FROM_INSTANCE (area)),
872 attribute, cell_attribute->column);
877 cell_attribute = cell_attribute_new (renderer, attribute, column);
881 g_warning ("Cannot connect attribute `%s' for cell renderer class `%s' "
882 "since attribute does not exist",
884 g_type_name (G_TYPE_FROM_INSTANCE (area)));
888 info->attributes = g_slist_prepend (info->attributes, cell_attribute);
892 gtk_cell_area_attribute_disconnect (GtkCellArea *area,
893 GtkCellRenderer *renderer,
894 const gchar *attribute)
896 GtkCellAreaPrivate *priv;
898 CellAttribute *cell_attribute;
901 g_return_if_fail (GTK_IS_CELL_AREA (area));
902 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
903 g_return_if_fail (attribute != NULL);
906 info = g_hash_table_lookup (priv->cell_info, renderer);
910 node = g_slist_find_custom (info->attributes, attribute,
911 (GCompareFunc)cell_attribute_find);
914 cell_attribute = node->data;
916 cell_attribute_free (cell_attribute);
918 info->attributes = g_slist_delete_link (info->attributes, node);
924 apply_cell_attributes (GtkCellRenderer *renderer,
928 CellAttribute *attribute;
930 GValue value = { 0, };
931 gboolean is_expander;
932 gboolean is_expanded;
934 g_object_freeze_notify (G_OBJECT (renderer));
936 /* Whether a row expands or is presently expanded can only be
937 * provided by the view (as these states can vary across views
938 * accessing the same model).
940 g_object_get (renderer, "is-expander", &is_expander, NULL);
941 if (is_expander != data->is_expander)
942 g_object_set (renderer, "is-expander", data->is_expander, NULL);
944 g_object_get (renderer, "is-expanded", &is_expanded, NULL);
945 if (is_expanded != data->is_expanded)
946 g_object_set (renderer, "is-expanded", data->is_expanded, NULL);
948 /* Apply the attributes directly to the renderer */
949 for (list = info->attributes; list; list = list->next)
951 attribute = list->data;
953 gtk_tree_model_get_value (data->model, data->iter, attribute->column, &value);
954 g_object_set_property (G_OBJECT (renderer), attribute->attribute, &value);
955 g_value_unset (&value);
958 /* Call any GtkCellLayoutDataFunc that may have been set by the user
961 info->func (GTK_CELL_LAYOUT (data->area), renderer,
962 data->model, data->iter, info->data);
964 g_object_thaw_notify (G_OBJECT (renderer));
968 gtk_cell_area_apply_attributes (GtkCellArea *area,
969 GtkTreeModel *tree_model,
971 gboolean is_expander,
972 gboolean is_expanded)
974 GtkCellAreaPrivate *priv;
977 g_return_if_fail (GTK_IS_CELL_AREA (area));
978 g_return_if_fail (GTK_IS_TREE_MODEL (tree_model));
979 g_return_if_fail (iter != NULL);
983 /* Feed in data needed to apply to every renderer */
985 data.model = tree_model;
987 data.is_expander = is_expander;
988 data.is_expanded = is_expanded;
990 /* Go over any cells that have attributes or custom GtkCellLayoutDataFuncs and
991 * apply the data from the treemodel */
992 g_hash_table_foreach (priv->cell_info, (GHFunc)apply_cell_attributes, &data);
995 /*************************************************************
996 * API: Cell Properties *
997 *************************************************************/
999 gtk_cell_area_class_install_cell_property (GtkCellAreaClass *aclass,
1003 g_return_if_fail (GTK_IS_CELL_AREA_CLASS (aclass));
1004 g_return_if_fail (G_IS_PARAM_SPEC (pspec));
1005 if (pspec->flags & G_PARAM_WRITABLE)
1006 g_return_if_fail (aclass->set_cell_property != NULL);
1007 if (pspec->flags & G_PARAM_READABLE)
1008 g_return_if_fail (aclass->get_cell_property != NULL);
1009 g_return_if_fail (property_id > 0);
1010 g_return_if_fail (PARAM_SPEC_PARAM_ID (pspec) == 0); /* paranoid */
1011 g_return_if_fail ((pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY)) == 0);
1013 if (g_param_spec_pool_lookup (cell_property_pool, pspec->name, G_OBJECT_CLASS_TYPE (aclass), TRUE))
1015 g_warning (G_STRLOC ": class `%s' already contains a cell property named `%s'",
1016 G_OBJECT_CLASS_NAME (aclass), pspec->name);
1019 g_param_spec_ref (pspec);
1020 g_param_spec_sink (pspec);
1021 PARAM_SPEC_SET_PARAM_ID (pspec, property_id);
1022 g_param_spec_pool_insert (cell_property_pool, pspec, G_OBJECT_CLASS_TYPE (aclass));
1026 gtk_cell_area_class_find_cell_property (GtkCellAreaClass *aclass,
1027 const gchar *property_name)
1029 g_return_val_if_fail (GTK_IS_CELL_AREA_CLASS (aclass), NULL);
1030 g_return_val_if_fail (property_name != NULL, NULL);
1032 return g_param_spec_pool_lookup (cell_property_pool,
1034 G_OBJECT_CLASS_TYPE (aclass),
1039 gtk_cell_area_class_list_cell_properties (GtkCellAreaClass *aclass,
1040 guint *n_properties)
1042 GParamSpec **pspecs;
1045 g_return_val_if_fail (GTK_IS_CELL_AREA_CLASS (aclass), NULL);
1047 pspecs = g_param_spec_pool_list (cell_property_pool,
1048 G_OBJECT_CLASS_TYPE (aclass),
1057 gtk_cell_area_add_with_properties (GtkCellArea *area,
1058 GtkCellRenderer *renderer,
1059 const gchar *first_prop_name,
1062 GtkCellAreaClass *class;
1064 g_return_if_fail (GTK_IS_CELL_AREA (area));
1065 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1067 class = GTK_CELL_AREA_GET_CLASS (area);
1073 class->add (area, renderer);
1075 va_start (var_args, first_prop_name);
1076 gtk_cell_area_cell_set_valist (area, renderer, first_prop_name, var_args);
1080 g_warning ("GtkCellAreaClass::add not implemented for `%s'",
1081 g_type_name (G_TYPE_FROM_INSTANCE (area)));
1085 gtk_cell_area_cell_set (GtkCellArea *area,
1086 GtkCellRenderer *renderer,
1087 const gchar *first_prop_name,
1092 g_return_if_fail (GTK_IS_CELL_AREA (area));
1093 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1095 va_start (var_args, first_prop_name);
1096 gtk_cell_area_cell_set_valist (area, renderer, first_prop_name, var_args);
1101 gtk_cell_area_cell_get (GtkCellArea *area,
1102 GtkCellRenderer *renderer,
1103 const gchar *first_prop_name,
1108 g_return_if_fail (GTK_IS_CELL_AREA (area));
1109 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1111 va_start (var_args, first_prop_name);
1112 gtk_cell_area_cell_get_valist (area, renderer, first_prop_name, var_args);
1117 area_get_cell_property (GtkCellArea *area,
1118 GtkCellRenderer *renderer,
1122 GtkCellAreaClass *class = g_type_class_peek (pspec->owner_type);
1124 class->get_cell_property (area, renderer, PARAM_SPEC_PARAM_ID (pspec), value, pspec);
1128 area_set_cell_property (GtkCellArea *area,
1129 GtkCellRenderer *renderer,
1131 const GValue *value)
1133 GValue tmp_value = { 0, };
1134 GtkCellAreaClass *class = g_type_class_peek (pspec->owner_type);
1136 /* provide a copy to work from, convert (if necessary) and validate */
1137 g_value_init (&tmp_value, G_PARAM_SPEC_VALUE_TYPE (pspec));
1138 if (!g_value_transform (value, &tmp_value))
1139 g_warning ("unable to set cell property `%s' of type `%s' from value of type `%s'",
1141 g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)),
1142 G_VALUE_TYPE_NAME (value));
1143 else if (g_param_value_validate (pspec, &tmp_value) && !(pspec->flags & G_PARAM_LAX_VALIDATION))
1145 gchar *contents = g_strdup_value_contents (value);
1147 g_warning ("value \"%s\" of type `%s' is invalid for property `%s' of type `%s'",
1149 G_VALUE_TYPE_NAME (value),
1151 g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)));
1156 class->set_cell_property (area, renderer, PARAM_SPEC_PARAM_ID (pspec), &tmp_value, pspec);
1158 g_value_unset (&tmp_value);
1162 gtk_cell_area_cell_set_valist (GtkCellArea *area,
1163 GtkCellRenderer *renderer,
1164 const gchar *first_property_name,
1169 g_return_if_fail (GTK_IS_CELL_AREA (area));
1170 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1172 name = first_property_name;
1175 GValue value = { 0, };
1176 gchar *error = NULL;
1178 g_param_spec_pool_lookup (cell_property_pool, name,
1179 G_OBJECT_TYPE (area), TRUE);
1182 g_warning ("%s: cell area class `%s' has no cell property named `%s'",
1183 G_STRLOC, G_OBJECT_TYPE_NAME (area), name);
1186 if (!(pspec->flags & G_PARAM_WRITABLE))
1188 g_warning ("%s: cell property `%s' of cell area class `%s' is not writable",
1189 G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (area));
1193 g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
1194 G_VALUE_COLLECT (&value, var_args, 0, &error);
1197 g_warning ("%s: %s", G_STRLOC, error);
1200 /* we purposely leak the value here, it might not be
1201 * in a sane state if an error condition occoured
1205 area_set_cell_property (area, renderer, pspec, &value);
1206 g_value_unset (&value);
1207 name = va_arg (var_args, gchar*);
1212 gtk_cell_area_cell_get_valist (GtkCellArea *area,
1213 GtkCellRenderer *renderer,
1214 const gchar *first_property_name,
1219 g_return_if_fail (GTK_IS_CELL_AREA (area));
1220 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1222 name = first_property_name;
1225 GValue value = { 0, };
1229 pspec = g_param_spec_pool_lookup (cell_property_pool, name,
1230 G_OBJECT_TYPE (area), TRUE);
1233 g_warning ("%s: cell area class `%s' has no cell property named `%s'",
1234 G_STRLOC, G_OBJECT_TYPE_NAME (area), name);
1237 if (!(pspec->flags & G_PARAM_READABLE))
1239 g_warning ("%s: cell property `%s' of cell area class `%s' is not readable",
1240 G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (area));
1244 g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
1245 area_get_cell_property (area, renderer, pspec, &value);
1246 G_VALUE_LCOPY (&value, var_args, 0, &error);
1249 g_warning ("%s: %s", G_STRLOC, error);
1251 g_value_unset (&value);
1254 g_value_unset (&value);
1255 name = va_arg (var_args, gchar*);
1260 gtk_cell_area_cell_set_property (GtkCellArea *area,
1261 GtkCellRenderer *renderer,
1262 const gchar *property_name,
1263 const GValue *value)
1267 g_return_if_fail (GTK_IS_CELL_AREA (area));
1268 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1269 g_return_if_fail (property_name != NULL);
1270 g_return_if_fail (G_IS_VALUE (value));
1272 pspec = g_param_spec_pool_lookup (cell_property_pool, property_name,
1273 G_OBJECT_TYPE (area), TRUE);
1275 g_warning ("%s: cell area class `%s' has no cell property named `%s'",
1276 G_STRLOC, G_OBJECT_TYPE_NAME (area), property_name);
1277 else if (!(pspec->flags & G_PARAM_WRITABLE))
1278 g_warning ("%s: cell property `%s' of cell area class `%s' is not writable",
1279 G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (area));
1282 area_set_cell_property (area, renderer, pspec, value);
1287 gtk_cell_area_cell_get_property (GtkCellArea *area,
1288 GtkCellRenderer *renderer,
1289 const gchar *property_name,
1294 g_return_if_fail (GTK_IS_CELL_AREA (area));
1295 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1296 g_return_if_fail (property_name != NULL);
1297 g_return_if_fail (G_IS_VALUE (value));
1299 pspec = g_param_spec_pool_lookup (cell_property_pool, property_name,
1300 G_OBJECT_TYPE (area), TRUE);
1302 g_warning ("%s: cell area class `%s' has no cell property named `%s'",
1303 G_STRLOC, G_OBJECT_TYPE_NAME (area), property_name);
1304 else if (!(pspec->flags & G_PARAM_READABLE))
1305 g_warning ("%s: cell property `%s' of cell area class `%s' is not readable",
1306 G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (area));
1309 GValue *prop_value, tmp_value = { 0, };
1311 /* auto-conversion of the callers value type
1313 if (G_VALUE_TYPE (value) == G_PARAM_SPEC_VALUE_TYPE (pspec))
1315 g_value_reset (value);
1318 else if (!g_value_type_transformable (G_PARAM_SPEC_VALUE_TYPE (pspec), G_VALUE_TYPE (value)))
1320 g_warning ("can't retrieve cell property `%s' of type `%s' as value of type `%s'",
1322 g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)),
1323 G_VALUE_TYPE_NAME (value));
1328 g_value_init (&tmp_value, G_PARAM_SPEC_VALUE_TYPE (pspec));
1329 prop_value = &tmp_value;
1332 area_get_cell_property (area, renderer, pspec, prop_value);
1334 if (prop_value != value)
1336 g_value_transform (prop_value, value);
1337 g_value_unset (&tmp_value);
1342 /*************************************************************
1344 *************************************************************/
1346 gtk_cell_area_grab_focus (GtkCellArea *area,
1347 GtkDirectionType direction)
1349 GtkCellAreaClass *class;
1351 g_return_if_fail (GTK_IS_CELL_AREA (area));
1353 class = GTK_CELL_AREA_GET_CLASS (area);
1355 if (class->grab_focus)
1356 class->grab_focus (area, direction);
1358 g_warning ("GtkCellAreaClass::grab_focus not implemented for `%s'",
1359 g_type_name (G_TYPE_FROM_INSTANCE (area)));
1363 gtk_cell_area_focus_leave (GtkCellArea *area,
1364 GtkDirectionType direction,
1367 g_return_if_fail (GTK_IS_CELL_AREA (area));
1369 g_signal_emit (area, cell_area_signals[SIGNAL_FOCUS_LEAVE], 0, direction, path);
1373 gtk_cell_area_set_can_focus (GtkCellArea *area,
1376 GtkCellAreaPrivate *priv;
1378 g_return_if_fail (GTK_IS_CELL_AREA (area));
1382 if (priv->can_focus != can_focus)
1384 priv->can_focus = can_focus;
1389 gtk_cell_area_get_can_focus (GtkCellArea *area)
1391 GtkCellAreaPrivate *priv;
1393 g_return_val_if_fail (GTK_IS_CELL_AREA (area), FALSE);
1397 return priv->can_focus;
1401 gtk_cell_area_set_focus_cell (GtkCellArea *area,
1402 GtkCellRenderer *renderer)
1404 GtkCellAreaPrivate *priv;
1406 g_return_if_fail (GTK_IS_CELL_AREA (area));
1407 g_return_if_fail (renderer == NULL || GTK_IS_CELL_RENDERER (renderer));
1411 if (priv->focus_cell != renderer)
1413 if (priv->focus_cell)
1414 g_object_unref (priv->focus_cell);
1416 priv->focus_cell = renderer;
1418 if (priv->focus_cell)
1419 g_object_ref (priv->focus_cell);
1424 gtk_cell_area_get_focus_cell (GtkCellArea *area)
1426 GtkCellAreaPrivate *priv;
1428 g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL);
1432 return priv->focus_cell;
1435 /*************************************************************
1437 *************************************************************/
1439 gtk_cell_area_get_cell_margin_left (GtkCellArea *area)
1441 g_return_val_if_fail (GTK_IS_CELL_AREA (area), 0);
1443 return area->priv->cell_border.left;
1447 gtk_cell_area_set_cell_margin_left (GtkCellArea *area,
1450 GtkCellAreaPrivate *priv;
1452 g_return_if_fail (GTK_IS_CELL_AREA (area));
1456 if (priv->cell_border.left != margin)
1458 priv->cell_border.left = margin;
1460 g_object_notify (G_OBJECT (area), "margin-left");
1465 gtk_cell_area_get_cell_margin_right (GtkCellArea *area)
1467 g_return_val_if_fail (GTK_IS_CELL_AREA (area), 0);
1469 return area->priv->cell_border.right;
1473 gtk_cell_area_set_cell_margin_right (GtkCellArea *area,
1476 GtkCellAreaPrivate *priv;
1478 g_return_if_fail (GTK_IS_CELL_AREA (area));
1482 if (priv->cell_border.right != margin)
1484 priv->cell_border.right = margin;
1486 g_object_notify (G_OBJECT (area), "margin-right");
1491 gtk_cell_area_get_cell_margin_top (GtkCellArea *area)
1493 g_return_val_if_fail (GTK_IS_CELL_AREA (area), 0);
1495 return area->priv->cell_border.top;
1499 gtk_cell_area_set_cell_margin_top (GtkCellArea *area,
1502 GtkCellAreaPrivate *priv;
1504 g_return_if_fail (GTK_IS_CELL_AREA (area));
1508 if (priv->cell_border.top != margin)
1510 priv->cell_border.top = margin;
1512 g_object_notify (G_OBJECT (area), "margin-top");
1517 gtk_cell_area_get_cell_margin_bottom (GtkCellArea *area)
1519 g_return_val_if_fail (GTK_IS_CELL_AREA (area), 0);
1521 return area->priv->cell_border.bottom;
1525 gtk_cell_area_set_cell_margin_bottom (GtkCellArea *area,
1528 GtkCellAreaPrivate *priv;
1530 g_return_if_fail (GTK_IS_CELL_AREA (area));
1534 if (priv->cell_border.bottom != margin)
1536 priv->cell_border.bottom = margin;
1538 g_object_notify (G_OBJECT (area), "margin-bottom");
1542 /* For convenience in area implementations */
1544 gtk_cell_area_inner_cell_area (GtkCellArea *area,
1545 GdkRectangle *background_area,
1546 GdkRectangle *cell_area)
1548 GtkCellAreaPrivate *priv;
1550 g_return_if_fail (GTK_IS_CELL_AREA (area));
1551 g_return_if_fail (background_area != NULL);
1552 g_return_if_fail (cell_area != NULL);
1556 *cell_area = *background_area;
1558 cell_area->x += priv->cell_border.left;
1559 cell_area->width -= (priv->cell_border.left + priv->cell_border.right);
1560 cell_area->y += priv->cell_border.top;
1561 cell_area->height -= (priv->cell_border.top + priv->cell_border.bottom);
1565 gtk_cell_area_request_renderer (GtkCellArea *area,
1566 GtkCellRenderer *renderer,
1567 GtkOrientation orientation,
1573 GtkCellAreaPrivate *priv;
1575 g_return_if_fail (GTK_IS_CELL_AREA (area));
1576 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1577 g_return_if_fail (GTK_IS_WIDGET (widget));
1578 g_return_if_fail (minimum_size != NULL);
1579 g_return_if_fail (natural_size != NULL);
1583 if (orientation == GTK_ORIENTATION_HORIZONTAL)
1586 gtk_cell_renderer_get_preferred_width (renderer, widget, minimum_size, natural_size);
1589 for_size = MAX (0, for_size - (priv->cell_border.top + priv->cell_border.bottom));
1591 gtk_cell_renderer_get_preferred_width_for_height (renderer, widget, for_size,
1592 minimum_size, natural_size);
1595 *minimum_size += (priv->cell_border.left + priv->cell_border.right);
1596 *natural_size += (priv->cell_border.left + priv->cell_border.right);
1598 else /* GTK_ORIENTATION_VERTICAL */
1601 gtk_cell_renderer_get_preferred_height (renderer, widget, minimum_size, natural_size);
1604 for_size = MAX (0, for_size - (priv->cell_border.left + priv->cell_border.right));
1606 gtk_cell_renderer_get_preferred_height_for_width (renderer, widget, for_size,
1607 minimum_size, natural_size);
1610 *minimum_size += (priv->cell_border.top + priv->cell_border.bottom);
1611 *natural_size += (priv->cell_border.top + priv->cell_border.bottom);