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 gint gtk_cell_area_real_event (GtkCellArea *area,
54 GtkCellAreaIter *iter,
57 const GdkRectangle *cell_area,
58 GtkCellRendererState flags);
59 static void gtk_cell_area_real_get_preferred_height_for_width (GtkCellArea *area,
60 GtkCellAreaIter *iter,
64 gint *natural_height);
65 static void gtk_cell_area_real_get_preferred_width_for_height (GtkCellArea *area,
66 GtkCellAreaIter *iter,
71 static void gtk_cell_area_real_update_focus (GtkCellArea *area);
73 /* GtkCellLayoutIface */
74 static void gtk_cell_area_cell_layout_init (GtkCellLayoutIface *iface);
75 static void gtk_cell_area_pack_default (GtkCellLayout *cell_layout,
76 GtkCellRenderer *renderer,
78 static void gtk_cell_area_clear (GtkCellLayout *cell_layout);
79 static void gtk_cell_area_add_attribute (GtkCellLayout *cell_layout,
80 GtkCellRenderer *renderer,
81 const gchar *attribute,
83 static void gtk_cell_area_set_cell_data_func (GtkCellLayout *cell_layout,
84 GtkCellRenderer *cell,
85 GtkCellLayoutDataFunc func,
87 GDestroyNotify destroy);
88 static void gtk_cell_area_clear_attributes (GtkCellLayout *cell_layout,
89 GtkCellRenderer *renderer);
90 static void gtk_cell_area_reorder (GtkCellLayout *cell_layout,
91 GtkCellRenderer *cell,
93 static GList *gtk_cell_area_get_cells (GtkCellLayout *cell_layout);
95 /* Attribute/Cell metadata */
97 const gchar *attribute;
104 GtkCellLayoutDataFunc func;
106 GDestroyNotify destroy;
109 static CellInfo *cell_info_new (GtkCellLayoutDataFunc func,
111 GDestroyNotify destroy);
112 static void cell_info_free (CellInfo *info);
113 static CellAttribute *cell_attribute_new (GtkCellRenderer *renderer,
114 const gchar *attribute,
116 static void cell_attribute_free (CellAttribute *attribute);
117 static gint cell_attribute_find (CellAttribute *cell_attribute,
118 const gchar *attribute);
120 /* Struct to pass data along while looping over
121 * cell renderers to apply attributes
127 gboolean is_expander;
128 gboolean is_expanded;
131 struct _GtkCellAreaPrivate
133 GHashTable *cell_info;
135 GtkBorder cell_border;
137 GtkCellRenderer *focus_cell;
144 PROP_CELL_MARGIN_LEFT,
145 PROP_CELL_MARGIN_RIGHT,
146 PROP_CELL_MARGIN_TOP,
147 PROP_CELL_MARGIN_BOTTOM
155 /* Keep the paramspec pool internal, no need to deliver notifications
156 * on cells. at least no percieved need for now */
157 static GParamSpecPool *cell_property_pool = NULL;
158 static guint cell_area_signals[LAST_SIGNAL] = { 0 };
160 #define PARAM_SPEC_PARAM_ID(pspec) ((pspec)->param_id)
161 #define PARAM_SPEC_SET_PARAM_ID(pspec, id) ((pspec)->param_id = (id))
164 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GtkCellArea, gtk_cell_area, G_TYPE_INITIALLY_UNOWNED,
165 G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
166 gtk_cell_area_cell_layout_init));
169 gtk_cell_area_init (GtkCellArea *area)
171 GtkCellAreaPrivate *priv;
173 area->priv = G_TYPE_INSTANCE_GET_PRIVATE (area,
178 priv->cell_info = g_hash_table_new_full (g_direct_hash,
181 (GDestroyNotify)cell_info_free);
183 priv->cell_border.left = 0;
184 priv->cell_border.right = 0;
185 priv->cell_border.top = 0;
186 priv->cell_border.bottom = 0;
188 priv->focus_cell = NULL;
192 gtk_cell_area_class_init (GtkCellAreaClass *class)
194 GObjectClass *object_class = G_OBJECT_CLASS (class);
197 object_class->dispose = gtk_cell_area_dispose;
198 object_class->finalize = gtk_cell_area_finalize;
199 object_class->get_property = gtk_cell_area_get_property;
200 object_class->set_property = gtk_cell_area_set_property;
204 class->remove = NULL;
205 class->forall = NULL;
206 class->event = gtk_cell_area_real_event;
207 class->render = NULL;
210 class->create_iter = NULL;
211 class->get_request_mode = NULL;
212 class->get_preferred_width = NULL;
213 class->get_preferred_height = NULL;
214 class->get_preferred_height_for_width = gtk_cell_area_real_get_preferred_height_for_width;
215 class->get_preferred_width_for_height = gtk_cell_area_real_get_preferred_width_for_height;
218 class->grab_focus = NULL;
219 class->update_focus = gtk_cell_area_real_update_focus;
222 cell_area_signals[SIGNAL_FOCUS_LEAVE] =
223 g_signal_new (I_("focus-leave"),
224 G_TYPE_FROM_CLASS (object_class),
226 0, /* Class offset (just a notification, no class handler) */
228 _gtk_marshal_VOID__ENUM_STRING,
230 GTK_TYPE_DIRECTION_TYPE, G_TYPE_STRING);
234 g_object_class_install_property (object_class,
235 PROP_CELL_MARGIN_LEFT,
238 P_("Margin on Left"),
239 P_("Pixels of extra space on the left side of each cell"),
243 GTK_PARAM_READWRITE));
245 g_object_class_install_property (object_class,
246 PROP_CELL_MARGIN_RIGHT,
248 ("cell-margin-right",
249 P_("Margin on Right"),
250 P_("Pixels of extra space on the right side of each cell"),
254 GTK_PARAM_READWRITE));
256 g_object_class_install_property (object_class,
257 PROP_CELL_MARGIN_TOP,
261 P_("Pixels of extra space on the top side of each cell"),
265 GTK_PARAM_READWRITE));
267 g_object_class_install_property (object_class,
268 PROP_CELL_MARGIN_BOTTOM,
270 ("cell-margin-bottom",
271 P_("Margin on Bottom"),
272 P_("Pixels of extra space on the bottom side of each cell"),
276 GTK_PARAM_READWRITE));
278 /* Pool for Cell Properties */
279 if (!cell_property_pool)
280 cell_property_pool = g_param_spec_pool_new (FALSE);
282 g_type_class_add_private (object_class, sizeof (GtkCellAreaPrivate));
285 /*************************************************************
287 *************************************************************/
289 cell_info_new (GtkCellLayoutDataFunc func,
291 GDestroyNotify destroy)
293 CellInfo *info = g_slice_new (CellInfo);
295 info->attributes = NULL;
298 info->destroy = destroy;
304 cell_info_free (CellInfo *info)
307 info->destroy (info->data);
309 g_slist_foreach (info->attributes, (GFunc)cell_attribute_free, NULL);
310 g_slist_free (info->attributes);
312 g_slice_free (CellInfo, info);
315 static CellAttribute *
316 cell_attribute_new (GtkCellRenderer *renderer,
317 const gchar *attribute,
322 /* Check if the attribute really exists and point to
323 * the property string installed on the cell renderer
324 * class (dont dup the string)
326 pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (renderer), attribute);
330 CellAttribute *cell_attribute = g_slice_new (CellAttribute);
332 cell_attribute->attribute = pspec->name;
333 cell_attribute->column = column;
335 return cell_attribute;
342 cell_attribute_free (CellAttribute *attribute)
344 g_slice_free (CellAttribute, attribute);
347 /* GCompareFunc for g_slist_find_custom() */
349 cell_attribute_find (CellAttribute *cell_attribute,
350 const gchar *attribute)
352 return g_strcmp0 (cell_attribute->attribute, attribute);
355 /*************************************************************
357 *************************************************************/
359 gtk_cell_area_finalize (GObject *object)
361 GtkCellArea *area = GTK_CELL_AREA (object);
362 GtkCellAreaPrivate *priv = area->priv;
364 /* All cell renderers should already be removed at this point,
365 * just kill our hash table here.
367 g_hash_table_destroy (priv->cell_info);
369 G_OBJECT_CLASS (gtk_cell_area_parent_class)->finalize (object);
374 gtk_cell_area_dispose (GObject *object)
376 /* This removes every cell renderer that may be added to the GtkCellArea,
377 * subclasses should be breaking references to the GtkCellRenderers
380 gtk_cell_layout_clear (GTK_CELL_LAYOUT (object));
382 /* Remove any ref to a focused cell */
383 gtk_cell_area_set_focus_cell (GTK_CELL_AREA (object), NULL);
385 G_OBJECT_CLASS (gtk_cell_area_parent_class)->dispose (object);
389 gtk_cell_area_set_property (GObject *object,
394 GtkCellArea *area = GTK_CELL_AREA (object);
398 case PROP_CELL_MARGIN_LEFT:
399 gtk_cell_area_set_cell_margin_left (area, g_value_get_int (value));
401 case PROP_CELL_MARGIN_RIGHT:
402 gtk_cell_area_set_cell_margin_right (area, g_value_get_int (value));
404 case PROP_CELL_MARGIN_TOP:
405 gtk_cell_area_set_cell_margin_top (area, g_value_get_int (value));
407 case PROP_CELL_MARGIN_BOTTOM:
408 gtk_cell_area_set_cell_margin_bottom (area, g_value_get_int (value));
411 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
417 gtk_cell_area_get_property (GObject *object,
422 GtkCellArea *area = GTK_CELL_AREA (object);
423 GtkCellAreaPrivate *priv = area->priv;
427 case PROP_CELL_MARGIN_LEFT:
428 g_value_set_int (value, priv->cell_border.left);
430 case PROP_CELL_MARGIN_RIGHT:
431 g_value_set_int (value, priv->cell_border.right);
433 case PROP_CELL_MARGIN_TOP:
434 g_value_set_int (value, priv->cell_border.top);
436 case PROP_CELL_MARGIN_BOTTOM:
437 g_value_set_int (value, priv->cell_border.bottom);
440 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
445 /*************************************************************
447 *************************************************************/
449 gtk_cell_area_real_event (GtkCellArea *area,
450 GtkCellAreaIter *iter,
453 const GdkRectangle *cell_area,
454 GtkCellRendererState flags)
456 if (event->type == GDK_KEY_PRESS)
458 GtkCellAreaPrivate *priv = area->priv;
460 if (priv->focus_cell)
462 /* Activate of Edit the currently focused cell */
473 gtk_cell_area_real_get_preferred_height_for_width (GtkCellArea *area,
474 GtkCellAreaIter *iter,
477 gint *minimum_height,
478 gint *natural_height)
480 /* If the area doesnt do height-for-width, fallback on base preferred height */
481 GTK_CELL_AREA_GET_CLASS (area)->get_preferred_width (area, iter, widget, minimum_height, natural_height);
485 gtk_cell_area_real_get_preferred_width_for_height (GtkCellArea *area,
486 GtkCellAreaIter *iter,
492 /* If the area doesnt do width-for-height, fallback on base preferred width */
493 GTK_CELL_AREA_GET_CLASS (area)->get_preferred_width (area, iter, widget, minimum_width, natural_width);
497 update_can_focus (GtkCellRenderer *renderer,
501 if (gtk_cell_renderer_can_focus (renderer))
506 gtk_cell_area_real_update_focus (GtkCellArea *area)
508 gboolean can_focus = FALSE;
510 /* Update the area's can focus flag, if any of the renderers can
511 * focus then the area can focus.
513 * Subclasses can override this in the case that they are also
514 * rendering widgets as well as renderers.
516 gtk_cell_area_forall (area, (GtkCellCallback)update_can_focus, &can_focus);
517 gtk_cell_area_set_can_focus (area, can_focus);
519 /* Unset the currently focused cell if the area can not receive
520 * focus for the given row data */
522 gtk_cell_area_set_focus_cell (area, NULL);
525 /*************************************************************
526 * GtkCellLayoutIface *
527 *************************************************************/
529 gtk_cell_area_cell_layout_init (GtkCellLayoutIface *iface)
531 iface->pack_start = gtk_cell_area_pack_default;
532 iface->pack_end = gtk_cell_area_pack_default;
533 iface->clear = gtk_cell_area_clear;
534 iface->add_attribute = gtk_cell_area_add_attribute;
535 iface->set_cell_data_func = gtk_cell_area_set_cell_data_func;
536 iface->clear_attributes = gtk_cell_area_clear_attributes;
537 iface->reorder = gtk_cell_area_reorder;
538 iface->get_cells = gtk_cell_area_get_cells;
542 gtk_cell_area_pack_default (GtkCellLayout *cell_layout,
543 GtkCellRenderer *renderer,
546 gtk_cell_area_add (GTK_CELL_AREA (cell_layout), renderer);
550 gtk_cell_area_clear (GtkCellLayout *cell_layout)
552 GtkCellArea *area = GTK_CELL_AREA (cell_layout);
554 gtk_cell_layout_get_cells (cell_layout);
556 for (l = cells; l; l = l->next)
558 GtkCellRenderer *renderer = l->data;
559 gtk_cell_area_remove (area, renderer);
566 gtk_cell_area_add_attribute (GtkCellLayout *cell_layout,
567 GtkCellRenderer *renderer,
568 const gchar *attribute,
571 gtk_cell_area_attribute_connect (GTK_CELL_AREA (cell_layout),
572 renderer, attribute, column);
576 gtk_cell_area_set_cell_data_func (GtkCellLayout *cell_layout,
577 GtkCellRenderer *renderer,
578 GtkCellLayoutDataFunc func,
580 GDestroyNotify destroy)
582 GtkCellArea *area = GTK_CELL_AREA (cell_layout);
583 GtkCellAreaPrivate *priv = area->priv;
586 info = g_hash_table_lookup (priv->cell_info, renderer);
590 if (info->destroy && info->data)
591 info->destroy (info->data);
596 info->data = func_data;
597 info->destroy = destroy;
603 info->destroy = NULL;
608 info = cell_info_new (func, func_data, destroy);
610 g_hash_table_insert (priv->cell_info, renderer, info);
615 gtk_cell_area_clear_attributes (GtkCellLayout *cell_layout,
616 GtkCellRenderer *renderer)
618 GtkCellArea *area = GTK_CELL_AREA (cell_layout);
619 GtkCellAreaPrivate *priv = area->priv;
622 info = g_hash_table_lookup (priv->cell_info, renderer);
626 g_slist_foreach (info->attributes, (GFunc)cell_attribute_free, NULL);
627 g_slist_free (info->attributes);
629 info->attributes = NULL;
634 gtk_cell_area_reorder (GtkCellLayout *cell_layout,
635 GtkCellRenderer *cell,
638 g_warning ("GtkCellLayout::reorder not implemented for `%s'",
639 g_type_name (G_TYPE_FROM_INSTANCE (cell_layout)));
643 accum_cells (GtkCellRenderer *renderer,
646 *accum = g_list_prepend (*accum, renderer);
650 gtk_cell_area_get_cells (GtkCellLayout *cell_layout)
654 gtk_cell_area_forall (GTK_CELL_AREA (cell_layout),
655 (GtkCellCallback)accum_cells,
658 return g_list_reverse (cells);
662 /*************************************************************
664 *************************************************************/
666 gtk_cell_area_add (GtkCellArea *area,
667 GtkCellRenderer *renderer)
669 GtkCellAreaClass *class;
671 g_return_if_fail (GTK_IS_CELL_AREA (area));
672 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
674 class = GTK_CELL_AREA_GET_CLASS (area);
677 class->add (area, renderer);
679 g_warning ("GtkCellAreaClass::add not implemented for `%s'",
680 g_type_name (G_TYPE_FROM_INSTANCE (area)));
684 gtk_cell_area_remove (GtkCellArea *area,
685 GtkCellRenderer *renderer)
687 GtkCellAreaClass *class;
688 GtkCellAreaPrivate *priv;
690 g_return_if_fail (GTK_IS_CELL_AREA (area));
691 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
693 class = GTK_CELL_AREA_GET_CLASS (area);
696 /* Remove any custom attributes and custom cell data func here first */
697 g_hash_table_remove (priv->cell_info, renderer);
700 class->remove (area, renderer);
702 g_warning ("GtkCellAreaClass::remove not implemented for `%s'",
703 g_type_name (G_TYPE_FROM_INSTANCE (area)));
707 gtk_cell_area_forall (GtkCellArea *area,
708 GtkCellCallback callback,
709 gpointer callback_data)
711 GtkCellAreaClass *class;
713 g_return_if_fail (GTK_IS_CELL_AREA (area));
714 g_return_if_fail (callback != NULL);
716 class = GTK_CELL_AREA_GET_CLASS (area);
719 class->forall (area, callback, callback_data);
721 g_warning ("GtkCellAreaClass::forall not implemented for `%s'",
722 g_type_name (G_TYPE_FROM_INSTANCE (area)));
726 gtk_cell_area_event (GtkCellArea *area,
727 GtkCellAreaIter *iter,
730 const GdkRectangle *cell_area,
731 GtkCellRendererState flags)
733 GtkCellAreaClass *class;
735 g_return_val_if_fail (GTK_IS_CELL_AREA (area), 0);
736 g_return_val_if_fail (GTK_IS_CELL_AREA_ITER (iter), 0);
737 g_return_val_if_fail (GTK_IS_WIDGET (widget), 0);
738 g_return_val_if_fail (event != NULL, 0);
739 g_return_val_if_fail (cell_area != NULL, 0);
741 class = GTK_CELL_AREA_GET_CLASS (area);
744 return class->event (area, iter, widget, event, cell_area, flags);
746 g_warning ("GtkCellAreaClass::event not implemented for `%s'",
747 g_type_name (G_TYPE_FROM_INSTANCE (area)));
752 gtk_cell_area_render (GtkCellArea *area,
753 GtkCellAreaIter *iter,
756 const GdkRectangle *cell_area,
757 GtkCellRendererState flags)
759 GtkCellAreaClass *class;
761 g_return_if_fail (GTK_IS_CELL_AREA (area));
762 g_return_if_fail (GTK_IS_CELL_AREA_ITER (iter));
763 g_return_if_fail (GTK_IS_WIDGET (widget));
764 g_return_if_fail (cr != NULL);
765 g_return_if_fail (cell_area != NULL);
767 class = GTK_CELL_AREA_GET_CLASS (area);
770 class->render (area, iter, widget, cr, cell_area, flags);
772 g_warning ("GtkCellAreaClass::render not implemented for `%s'",
773 g_type_name (G_TYPE_FROM_INSTANCE (area)));
776 /*************************************************************
778 *************************************************************/
780 gtk_cell_area_create_iter (GtkCellArea *area)
782 GtkCellAreaClass *class;
784 g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL);
786 class = GTK_CELL_AREA_GET_CLASS (area);
788 if (class->create_iter)
789 return class->create_iter (area);
791 g_warning ("GtkCellAreaClass::create_iter not implemented for `%s'",
792 g_type_name (G_TYPE_FROM_INSTANCE (area)));
799 gtk_cell_area_get_request_mode (GtkCellArea *area)
801 GtkCellAreaClass *class;
803 g_return_val_if_fail (GTK_IS_CELL_AREA (area),
804 GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH);
806 class = GTK_CELL_AREA_GET_CLASS (area);
808 if (class->get_request_mode)
809 return class->get_request_mode (area);
811 g_warning ("GtkCellAreaClass::get_request_mode not implemented for `%s'",
812 g_type_name (G_TYPE_FROM_INSTANCE (area)));
814 return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
818 gtk_cell_area_get_preferred_width (GtkCellArea *area,
819 GtkCellAreaIter *iter,
824 GtkCellAreaClass *class;
826 g_return_if_fail (GTK_IS_CELL_AREA (area));
827 g_return_if_fail (GTK_IS_WIDGET (widget));
829 class = GTK_CELL_AREA_GET_CLASS (area);
831 if (class->get_preferred_width)
832 class->get_preferred_width (area, iter, widget, minimum_size, natural_size);
834 g_warning ("GtkCellAreaClass::get_preferred_width not implemented for `%s'",
835 g_type_name (G_TYPE_FROM_INSTANCE (area)));
839 gtk_cell_area_get_preferred_height_for_width (GtkCellArea *area,
840 GtkCellAreaIter *iter,
843 gint *minimum_height,
844 gint *natural_height)
846 GtkCellAreaClass *class;
848 g_return_if_fail (GTK_IS_CELL_AREA (area));
849 g_return_if_fail (GTK_IS_WIDGET (widget));
851 class = GTK_CELL_AREA_GET_CLASS (area);
852 class->get_preferred_height_for_width (area, iter, widget, width, minimum_height, natural_height);
856 gtk_cell_area_get_preferred_height (GtkCellArea *area,
857 GtkCellAreaIter *iter,
862 GtkCellAreaClass *class;
864 g_return_if_fail (GTK_IS_CELL_AREA (area));
865 g_return_if_fail (GTK_IS_WIDGET (widget));
867 class = GTK_CELL_AREA_GET_CLASS (area);
869 if (class->get_preferred_height)
870 class->get_preferred_height (area, iter, widget, minimum_size, natural_size);
872 g_warning ("GtkCellAreaClass::get_preferred_height not implemented for `%s'",
873 g_type_name (G_TYPE_FROM_INSTANCE (area)));
877 gtk_cell_area_get_preferred_width_for_height (GtkCellArea *area,
878 GtkCellAreaIter *iter,
884 GtkCellAreaClass *class;
886 g_return_if_fail (GTK_IS_CELL_AREA (area));
887 g_return_if_fail (GTK_IS_WIDGET (widget));
889 class = GTK_CELL_AREA_GET_CLASS (area);
890 class->get_preferred_width_for_height (area, iter, widget, height, minimum_width, natural_width);
893 /*************************************************************
895 *************************************************************/
897 gtk_cell_area_attribute_connect (GtkCellArea *area,
898 GtkCellRenderer *renderer,
899 const gchar *attribute,
902 GtkCellAreaPrivate *priv;
904 CellAttribute *cell_attribute;
906 g_return_if_fail (GTK_IS_CELL_AREA (area));
907 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
908 g_return_if_fail (attribute != NULL);
911 info = g_hash_table_lookup (priv->cell_info, renderer);
915 info = cell_info_new (NULL, NULL, NULL);
917 g_hash_table_insert (priv->cell_info, renderer, info);
923 /* Check we are not adding the same attribute twice */
924 if ((node = g_slist_find_custom (info->attributes, attribute,
925 (GCompareFunc)cell_attribute_find)) != NULL)
927 cell_attribute = node->data;
929 g_warning ("Cannot connect attribute `%s' for cell renderer class `%s' "
930 "since `%s' is already attributed to column %d",
932 g_type_name (G_TYPE_FROM_INSTANCE (area)),
933 attribute, cell_attribute->column);
938 cell_attribute = cell_attribute_new (renderer, attribute, column);
942 g_warning ("Cannot connect attribute `%s' for cell renderer class `%s' "
943 "since attribute does not exist",
945 g_type_name (G_TYPE_FROM_INSTANCE (area)));
949 info->attributes = g_slist_prepend (info->attributes, cell_attribute);
953 gtk_cell_area_attribute_disconnect (GtkCellArea *area,
954 GtkCellRenderer *renderer,
955 const gchar *attribute)
957 GtkCellAreaPrivate *priv;
959 CellAttribute *cell_attribute;
962 g_return_if_fail (GTK_IS_CELL_AREA (area));
963 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
964 g_return_if_fail (attribute != NULL);
967 info = g_hash_table_lookup (priv->cell_info, renderer);
971 node = g_slist_find_custom (info->attributes, attribute,
972 (GCompareFunc)cell_attribute_find);
975 cell_attribute = node->data;
977 cell_attribute_free (cell_attribute);
979 info->attributes = g_slist_delete_link (info->attributes, node);
985 apply_cell_attributes (GtkCellRenderer *renderer,
989 CellAttribute *attribute;
991 GValue value = { 0, };
992 gboolean is_expander;
993 gboolean is_expanded;
995 g_object_freeze_notify (G_OBJECT (renderer));
997 /* Whether a row expands or is presently expanded can only be
998 * provided by the view (as these states can vary across views
999 * accessing the same model).
1001 g_object_get (renderer, "is-expander", &is_expander, NULL);
1002 if (is_expander != data->is_expander)
1003 g_object_set (renderer, "is-expander", data->is_expander, NULL);
1005 g_object_get (renderer, "is-expanded", &is_expanded, NULL);
1006 if (is_expanded != data->is_expanded)
1007 g_object_set (renderer, "is-expanded", data->is_expanded, NULL);
1009 /* Apply the attributes directly to the renderer */
1010 for (list = info->attributes; list; list = list->next)
1012 attribute = list->data;
1014 gtk_tree_model_get_value (data->model, data->iter, attribute->column, &value);
1015 g_object_set_property (G_OBJECT (renderer), attribute->attribute, &value);
1016 g_value_unset (&value);
1019 /* Call any GtkCellLayoutDataFunc that may have been set by the user
1022 info->func (GTK_CELL_LAYOUT (data->area), renderer,
1023 data->model, data->iter, info->data);
1025 g_object_thaw_notify (G_OBJECT (renderer));
1029 gtk_cell_area_apply_attributes (GtkCellArea *area,
1030 GtkTreeModel *tree_model,
1032 gboolean is_expander,
1033 gboolean is_expanded)
1035 GtkCellAreaPrivate *priv;
1038 g_return_if_fail (GTK_IS_CELL_AREA (area));
1039 g_return_if_fail (GTK_IS_TREE_MODEL (tree_model));
1040 g_return_if_fail (iter != NULL);
1044 /* Feed in data needed to apply to every renderer */
1046 data.model = tree_model;
1048 data.is_expander = is_expander;
1049 data.is_expanded = is_expanded;
1051 /* Go over any cells that have attributes or custom GtkCellLayoutDataFuncs and
1052 * apply the data from the treemodel */
1053 g_hash_table_foreach (priv->cell_info, (GHFunc)apply_cell_attributes, &data);
1056 /*************************************************************
1057 * API: Cell Properties *
1058 *************************************************************/
1060 gtk_cell_area_class_install_cell_property (GtkCellAreaClass *aclass,
1064 g_return_if_fail (GTK_IS_CELL_AREA_CLASS (aclass));
1065 g_return_if_fail (G_IS_PARAM_SPEC (pspec));
1066 if (pspec->flags & G_PARAM_WRITABLE)
1067 g_return_if_fail (aclass->set_cell_property != NULL);
1068 if (pspec->flags & G_PARAM_READABLE)
1069 g_return_if_fail (aclass->get_cell_property != NULL);
1070 g_return_if_fail (property_id > 0);
1071 g_return_if_fail (PARAM_SPEC_PARAM_ID (pspec) == 0); /* paranoid */
1072 g_return_if_fail ((pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY)) == 0);
1074 if (g_param_spec_pool_lookup (cell_property_pool, pspec->name, G_OBJECT_CLASS_TYPE (aclass), TRUE))
1076 g_warning (G_STRLOC ": class `%s' already contains a cell property named `%s'",
1077 G_OBJECT_CLASS_NAME (aclass), pspec->name);
1080 g_param_spec_ref (pspec);
1081 g_param_spec_sink (pspec);
1082 PARAM_SPEC_SET_PARAM_ID (pspec, property_id);
1083 g_param_spec_pool_insert (cell_property_pool, pspec, G_OBJECT_CLASS_TYPE (aclass));
1087 gtk_cell_area_class_find_cell_property (GtkCellAreaClass *aclass,
1088 const gchar *property_name)
1090 g_return_val_if_fail (GTK_IS_CELL_AREA_CLASS (aclass), NULL);
1091 g_return_val_if_fail (property_name != NULL, NULL);
1093 return g_param_spec_pool_lookup (cell_property_pool,
1095 G_OBJECT_CLASS_TYPE (aclass),
1100 gtk_cell_area_class_list_cell_properties (GtkCellAreaClass *aclass,
1101 guint *n_properties)
1103 GParamSpec **pspecs;
1106 g_return_val_if_fail (GTK_IS_CELL_AREA_CLASS (aclass), NULL);
1108 pspecs = g_param_spec_pool_list (cell_property_pool,
1109 G_OBJECT_CLASS_TYPE (aclass),
1118 gtk_cell_area_add_with_properties (GtkCellArea *area,
1119 GtkCellRenderer *renderer,
1120 const gchar *first_prop_name,
1123 GtkCellAreaClass *class;
1125 g_return_if_fail (GTK_IS_CELL_AREA (area));
1126 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1128 class = GTK_CELL_AREA_GET_CLASS (area);
1134 class->add (area, renderer);
1136 va_start (var_args, first_prop_name);
1137 gtk_cell_area_cell_set_valist (area, renderer, first_prop_name, var_args);
1141 g_warning ("GtkCellAreaClass::add not implemented for `%s'",
1142 g_type_name (G_TYPE_FROM_INSTANCE (area)));
1146 gtk_cell_area_cell_set (GtkCellArea *area,
1147 GtkCellRenderer *renderer,
1148 const gchar *first_prop_name,
1153 g_return_if_fail (GTK_IS_CELL_AREA (area));
1154 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1156 va_start (var_args, first_prop_name);
1157 gtk_cell_area_cell_set_valist (area, renderer, first_prop_name, var_args);
1162 gtk_cell_area_cell_get (GtkCellArea *area,
1163 GtkCellRenderer *renderer,
1164 const gchar *first_prop_name,
1169 g_return_if_fail (GTK_IS_CELL_AREA (area));
1170 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1172 va_start (var_args, first_prop_name);
1173 gtk_cell_area_cell_get_valist (area, renderer, first_prop_name, var_args);
1178 area_get_cell_property (GtkCellArea *area,
1179 GtkCellRenderer *renderer,
1183 GtkCellAreaClass *class = g_type_class_peek (pspec->owner_type);
1185 class->get_cell_property (area, renderer, PARAM_SPEC_PARAM_ID (pspec), value, pspec);
1189 area_set_cell_property (GtkCellArea *area,
1190 GtkCellRenderer *renderer,
1192 const GValue *value)
1194 GValue tmp_value = { 0, };
1195 GtkCellAreaClass *class = g_type_class_peek (pspec->owner_type);
1197 /* provide a copy to work from, convert (if necessary) and validate */
1198 g_value_init (&tmp_value, G_PARAM_SPEC_VALUE_TYPE (pspec));
1199 if (!g_value_transform (value, &tmp_value))
1200 g_warning ("unable to set cell property `%s' of type `%s' from value of type `%s'",
1202 g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)),
1203 G_VALUE_TYPE_NAME (value));
1204 else if (g_param_value_validate (pspec, &tmp_value) && !(pspec->flags & G_PARAM_LAX_VALIDATION))
1206 gchar *contents = g_strdup_value_contents (value);
1208 g_warning ("value \"%s\" of type `%s' is invalid for property `%s' of type `%s'",
1210 G_VALUE_TYPE_NAME (value),
1212 g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)));
1217 class->set_cell_property (area, renderer, PARAM_SPEC_PARAM_ID (pspec), &tmp_value, pspec);
1219 g_value_unset (&tmp_value);
1223 gtk_cell_area_cell_set_valist (GtkCellArea *area,
1224 GtkCellRenderer *renderer,
1225 const gchar *first_property_name,
1230 g_return_if_fail (GTK_IS_CELL_AREA (area));
1231 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1233 name = first_property_name;
1236 GValue value = { 0, };
1237 gchar *error = NULL;
1239 g_param_spec_pool_lookup (cell_property_pool, name,
1240 G_OBJECT_TYPE (area), TRUE);
1243 g_warning ("%s: cell area class `%s' has no cell property named `%s'",
1244 G_STRLOC, G_OBJECT_TYPE_NAME (area), name);
1247 if (!(pspec->flags & G_PARAM_WRITABLE))
1249 g_warning ("%s: cell property `%s' of cell area class `%s' is not writable",
1250 G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (area));
1254 g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
1255 G_VALUE_COLLECT (&value, var_args, 0, &error);
1258 g_warning ("%s: %s", G_STRLOC, error);
1261 /* we purposely leak the value here, it might not be
1262 * in a sane state if an error condition occoured
1266 area_set_cell_property (area, renderer, pspec, &value);
1267 g_value_unset (&value);
1268 name = va_arg (var_args, gchar*);
1273 gtk_cell_area_cell_get_valist (GtkCellArea *area,
1274 GtkCellRenderer *renderer,
1275 const gchar *first_property_name,
1280 g_return_if_fail (GTK_IS_CELL_AREA (area));
1281 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1283 name = first_property_name;
1286 GValue value = { 0, };
1290 pspec = g_param_spec_pool_lookup (cell_property_pool, name,
1291 G_OBJECT_TYPE (area), TRUE);
1294 g_warning ("%s: cell area class `%s' has no cell property named `%s'",
1295 G_STRLOC, G_OBJECT_TYPE_NAME (area), name);
1298 if (!(pspec->flags & G_PARAM_READABLE))
1300 g_warning ("%s: cell property `%s' of cell area class `%s' is not readable",
1301 G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (area));
1305 g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
1306 area_get_cell_property (area, renderer, pspec, &value);
1307 G_VALUE_LCOPY (&value, var_args, 0, &error);
1310 g_warning ("%s: %s", G_STRLOC, error);
1312 g_value_unset (&value);
1315 g_value_unset (&value);
1316 name = va_arg (var_args, gchar*);
1321 gtk_cell_area_cell_set_property (GtkCellArea *area,
1322 GtkCellRenderer *renderer,
1323 const gchar *property_name,
1324 const GValue *value)
1328 g_return_if_fail (GTK_IS_CELL_AREA (area));
1329 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1330 g_return_if_fail (property_name != NULL);
1331 g_return_if_fail (G_IS_VALUE (value));
1333 pspec = g_param_spec_pool_lookup (cell_property_pool, property_name,
1334 G_OBJECT_TYPE (area), TRUE);
1336 g_warning ("%s: cell area class `%s' has no cell property named `%s'",
1337 G_STRLOC, G_OBJECT_TYPE_NAME (area), property_name);
1338 else if (!(pspec->flags & G_PARAM_WRITABLE))
1339 g_warning ("%s: cell property `%s' of cell area class `%s' is not writable",
1340 G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (area));
1343 area_set_cell_property (area, renderer, pspec, value);
1348 gtk_cell_area_cell_get_property (GtkCellArea *area,
1349 GtkCellRenderer *renderer,
1350 const gchar *property_name,
1355 g_return_if_fail (GTK_IS_CELL_AREA (area));
1356 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1357 g_return_if_fail (property_name != NULL);
1358 g_return_if_fail (G_IS_VALUE (value));
1360 pspec = g_param_spec_pool_lookup (cell_property_pool, property_name,
1361 G_OBJECT_TYPE (area), TRUE);
1363 g_warning ("%s: cell area class `%s' has no cell property named `%s'",
1364 G_STRLOC, G_OBJECT_TYPE_NAME (area), property_name);
1365 else if (!(pspec->flags & G_PARAM_READABLE))
1366 g_warning ("%s: cell property `%s' of cell area class `%s' is not readable",
1367 G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (area));
1370 GValue *prop_value, tmp_value = { 0, };
1372 /* auto-conversion of the callers value type
1374 if (G_VALUE_TYPE (value) == G_PARAM_SPEC_VALUE_TYPE (pspec))
1376 g_value_reset (value);
1379 else if (!g_value_type_transformable (G_PARAM_SPEC_VALUE_TYPE (pspec), G_VALUE_TYPE (value)))
1381 g_warning ("can't retrieve cell property `%s' of type `%s' as value of type `%s'",
1383 g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)),
1384 G_VALUE_TYPE_NAME (value));
1389 g_value_init (&tmp_value, G_PARAM_SPEC_VALUE_TYPE (pspec));
1390 prop_value = &tmp_value;
1393 area_get_cell_property (area, renderer, pspec, prop_value);
1395 if (prop_value != value)
1397 g_value_transform (prop_value, value);
1398 g_value_unset (&tmp_value);
1403 /*************************************************************
1405 *************************************************************/
1408 * gtk_cell_area_grab_focus:
1409 * @area: a #GtkCellArea
1410 * @direction: the #GtkDirectionType from which focus came
1412 * This should be called by the @area's owning layout widget
1413 * when focus should be passed to @area for a given row data.
1415 * Note that after applying new attributes for @area that
1416 * gtk_cell_area_update_focus() should be called and
1417 * gtk_cell_area_can_focus() should be checked before trying
1418 * to pass focus to @area.
1420 * Implementing #GtkCellArea classes should implement this
1421 * method to receive focus in it's own way particular to
1422 * how it lays out cells.
1425 gtk_cell_area_grab_focus (GtkCellArea *area,
1426 GtkDirectionType direction)
1428 GtkCellAreaClass *class;
1430 g_return_if_fail (GTK_IS_CELL_AREA (area));
1432 class = GTK_CELL_AREA_GET_CLASS (area);
1434 if (class->grab_focus)
1435 class->grab_focus (area, direction);
1437 g_warning ("GtkCellAreaClass::grab_focus not implemented for `%s'",
1438 g_type_name (G_TYPE_FROM_INSTANCE (area)));
1442 * gtk_cell_area_focus_leave:
1443 * @area: a #GtkCellArea
1444 * @direction: the #GtkDirectionType in which focus
1446 * @path: the current #GtkTreePath string for the
1447 * event which was handled by @area
1449 * Notifies that focus is to leave @area in the
1452 * This is called by #GtkCellArea implementations upon
1453 * handling a key event that caused focus to leave the
1454 * cell. The resulting signal can be handled by the
1455 * owning layouting widget to decide which new @area
1456 * to pass focus to and from what @direction. Or to
1457 * pass focus along to an entirely new data row.
1460 gtk_cell_area_focus_leave (GtkCellArea *area,
1461 GtkDirectionType direction,
1464 g_return_if_fail (GTK_IS_CELL_AREA (area));
1466 g_signal_emit (area, cell_area_signals[SIGNAL_FOCUS_LEAVE], 0, direction, path);
1470 * gtk_cell_area_update_focus:
1471 * @area: a #GtkCellArea
1473 * Updates focus information on @area for a given
1476 * After calling gtk_cell_area_apply_attributes() to
1477 * the @area this method should be called to update
1478 * information about whether the @area can focus and
1479 * which is the cell currently in focus.
1482 gtk_cell_area_update_focus (GtkCellArea *area)
1484 g_return_if_fail (GTK_IS_CELL_AREA (area));
1486 GTK_CELL_AREA_GET_CLASS (area)->update_focus (area);
1490 * gtk_cell_area_set_can_focus:
1491 * @area: a #GtkCellArea
1492 * @can_focus: whether @area can receive focus
1494 * This is generally called from GtkCellArea::update_focus()
1495 * implementations to update if the @area can focus after
1496 * applying new row data attributes.
1499 gtk_cell_area_set_can_focus (GtkCellArea *area,
1502 GtkCellAreaPrivate *priv;
1504 g_return_if_fail (GTK_IS_CELL_AREA (area));
1508 if (priv->can_focus != can_focus)
1510 priv->can_focus = can_focus;
1515 * gtk_cell_area_get_can_focus:
1516 * @area: a #GtkCellArea
1518 * Returns whether the area can receive keyboard focus,
1519 * after applying new attributes to @area,
1520 * gtk_cell_area_update_focus() needs to be called before
1521 * calling this method.
1523 * Returns: whether @area can receive focus.
1526 gtk_cell_area_get_can_focus (GtkCellArea *area)
1528 GtkCellAreaPrivate *priv;
1530 g_return_val_if_fail (GTK_IS_CELL_AREA (area), FALSE);
1534 return priv->can_focus;
1539 * gtk_cell_area_set_focus_cell:
1540 * @area: a #GtkCellArea
1541 * @focus_cell: the #GtkCellRenderer to give focus to
1543 * This is generally called from #GtkCellArea implementations
1544 * either gtk_cell_area_grab_focus() or gtk_cell_area_update_focus()
1545 * is called. It's also up to the #GtkCellArea implementation
1546 * to update the focused cell when receiving events from
1547 * gtk_cell_area_event() appropriately.
1550 gtk_cell_area_set_focus_cell (GtkCellArea *area,
1551 GtkCellRenderer *renderer)
1553 GtkCellAreaPrivate *priv;
1555 g_return_if_fail (GTK_IS_CELL_AREA (area));
1556 g_return_if_fail (renderer == NULL || GTK_IS_CELL_RENDERER (renderer));
1560 if (priv->focus_cell != renderer)
1562 if (priv->focus_cell)
1563 g_object_unref (priv->focus_cell);
1565 priv->focus_cell = renderer;
1567 if (priv->focus_cell)
1568 g_object_ref (priv->focus_cell);
1573 * gtk_cell_area_get_focus_cell:
1574 * @area: a #GtkCellArea
1576 * Retrieves the currently focused cell for @area
1578 * Returns: the currently focused cell in @area.
1581 gtk_cell_area_get_focus_cell (GtkCellArea *area)
1583 GtkCellAreaPrivate *priv;
1585 g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL);
1589 return priv->focus_cell;
1592 /*************************************************************
1594 *************************************************************/
1596 gtk_cell_area_get_cell_margin_left (GtkCellArea *area)
1598 g_return_val_if_fail (GTK_IS_CELL_AREA (area), 0);
1600 return area->priv->cell_border.left;
1604 gtk_cell_area_set_cell_margin_left (GtkCellArea *area,
1607 GtkCellAreaPrivate *priv;
1609 g_return_if_fail (GTK_IS_CELL_AREA (area));
1613 if (priv->cell_border.left != margin)
1615 priv->cell_border.left = margin;
1617 g_object_notify (G_OBJECT (area), "margin-left");
1622 gtk_cell_area_get_cell_margin_right (GtkCellArea *area)
1624 g_return_val_if_fail (GTK_IS_CELL_AREA (area), 0);
1626 return area->priv->cell_border.right;
1630 gtk_cell_area_set_cell_margin_right (GtkCellArea *area,
1633 GtkCellAreaPrivate *priv;
1635 g_return_if_fail (GTK_IS_CELL_AREA (area));
1639 if (priv->cell_border.right != margin)
1641 priv->cell_border.right = margin;
1643 g_object_notify (G_OBJECT (area), "margin-right");
1648 gtk_cell_area_get_cell_margin_top (GtkCellArea *area)
1650 g_return_val_if_fail (GTK_IS_CELL_AREA (area), 0);
1652 return area->priv->cell_border.top;
1656 gtk_cell_area_set_cell_margin_top (GtkCellArea *area,
1659 GtkCellAreaPrivate *priv;
1661 g_return_if_fail (GTK_IS_CELL_AREA (area));
1665 if (priv->cell_border.top != margin)
1667 priv->cell_border.top = margin;
1669 g_object_notify (G_OBJECT (area), "margin-top");
1674 gtk_cell_area_get_cell_margin_bottom (GtkCellArea *area)
1676 g_return_val_if_fail (GTK_IS_CELL_AREA (area), 0);
1678 return area->priv->cell_border.bottom;
1682 gtk_cell_area_set_cell_margin_bottom (GtkCellArea *area,
1685 GtkCellAreaPrivate *priv;
1687 g_return_if_fail (GTK_IS_CELL_AREA (area));
1691 if (priv->cell_border.bottom != margin)
1693 priv->cell_border.bottom = margin;
1695 g_object_notify (G_OBJECT (area), "margin-bottom");
1699 /* For convenience in area implementations */
1701 gtk_cell_area_inner_cell_area (GtkCellArea *area,
1702 GdkRectangle *background_area,
1703 GdkRectangle *cell_area)
1705 GtkCellAreaPrivate *priv;
1707 g_return_if_fail (GTK_IS_CELL_AREA (area));
1708 g_return_if_fail (background_area != NULL);
1709 g_return_if_fail (cell_area != NULL);
1713 *cell_area = *background_area;
1715 cell_area->x += priv->cell_border.left;
1716 cell_area->width -= (priv->cell_border.left + priv->cell_border.right);
1717 cell_area->y += priv->cell_border.top;
1718 cell_area->height -= (priv->cell_border.top + priv->cell_border.bottom);
1722 gtk_cell_area_request_renderer (GtkCellArea *area,
1723 GtkCellRenderer *renderer,
1724 GtkOrientation orientation,
1730 GtkCellAreaPrivate *priv;
1732 g_return_if_fail (GTK_IS_CELL_AREA (area));
1733 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1734 g_return_if_fail (GTK_IS_WIDGET (widget));
1735 g_return_if_fail (minimum_size != NULL);
1736 g_return_if_fail (natural_size != NULL);
1740 if (orientation == GTK_ORIENTATION_HORIZONTAL)
1743 gtk_cell_renderer_get_preferred_width (renderer, widget, minimum_size, natural_size);
1746 for_size = MAX (0, for_size - (priv->cell_border.top + priv->cell_border.bottom));
1748 gtk_cell_renderer_get_preferred_width_for_height (renderer, widget, for_size,
1749 minimum_size, natural_size);
1752 *minimum_size += (priv->cell_border.left + priv->cell_border.right);
1753 *natural_size += (priv->cell_border.left + priv->cell_border.right);
1755 else /* GTK_ORIENTATION_VERTICAL */
1758 gtk_cell_renderer_get_preferred_height (renderer, widget, minimum_size, natural_size);
1761 for_size = MAX (0, for_size - (priv->cell_border.left + priv->cell_border.right));
1763 gtk_cell_renderer_get_preferred_height_for_width (renderer, widget, for_size,
1764 minimum_size, natural_size);
1767 *minimum_size += (priv->cell_border.top + priv->cell_border.bottom);
1768 *natural_size += (priv->cell_border.top + priv->cell_border.bottom);