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 gboolean gtk_cell_area_real_can_focus (GtkCellArea *area);
72 static gboolean gtk_cell_area_real_activate (GtkCellArea *area,
73 GtkCellAreaIter *iter,
75 const GdkRectangle *cell_area,
76 GtkCellRendererState flags);
78 /* GtkCellLayoutIface */
79 static void gtk_cell_area_cell_layout_init (GtkCellLayoutIface *iface);
80 static void gtk_cell_area_pack_default (GtkCellLayout *cell_layout,
81 GtkCellRenderer *renderer,
83 static void gtk_cell_area_clear (GtkCellLayout *cell_layout);
84 static void gtk_cell_area_add_attribute (GtkCellLayout *cell_layout,
85 GtkCellRenderer *renderer,
86 const gchar *attribute,
88 static void gtk_cell_area_set_cell_data_func (GtkCellLayout *cell_layout,
89 GtkCellRenderer *cell,
90 GtkCellLayoutDataFunc func,
92 GDestroyNotify destroy);
93 static void gtk_cell_area_clear_attributes (GtkCellLayout *cell_layout,
94 GtkCellRenderer *renderer);
95 static void gtk_cell_area_reorder (GtkCellLayout *cell_layout,
96 GtkCellRenderer *cell,
98 static GList *gtk_cell_area_get_cells (GtkCellLayout *cell_layout);
101 /* Used in forall loop to check if a child renderer is present */
103 GtkCellRenderer *renderer;
104 gboolean has_renderer;
107 /* Attribute/Cell metadata */
109 const gchar *attribute;
116 GtkCellLayoutDataFunc func;
118 GDestroyNotify destroy;
121 static CellInfo *cell_info_new (GtkCellLayoutDataFunc func,
123 GDestroyNotify destroy);
124 static void cell_info_free (CellInfo *info);
125 static CellAttribute *cell_attribute_new (GtkCellRenderer *renderer,
126 const gchar *attribute,
128 static void cell_attribute_free (CellAttribute *attribute);
129 static gint cell_attribute_find (CellAttribute *cell_attribute,
130 const gchar *attribute);
132 /* Internal signal emissions */
133 static void gtk_cell_area_editing_started (GtkCellArea *area,
134 GtkCellRenderer *renderer,
135 GtkCellEditable *editable);
136 static void gtk_cell_area_editing_canceled (GtkCellArea *area,
137 GtkCellRenderer *renderer);
138 static void gtk_cell_area_editing_done (GtkCellArea *area,
139 GtkCellRenderer *renderer,
140 GtkCellEditable *editable);
141 static void gtk_cell_area_remove_editable (GtkCellArea *area,
142 GtkCellRenderer *renderer,
143 GtkCellEditable *editable);
146 /* Struct to pass data along while looping over
147 * cell renderers to apply attributes
153 gboolean is_expander;
154 gboolean is_expanded;
157 struct _GtkCellAreaPrivate
159 /* The GtkCellArea bookkeeps any connected
160 * attributes in this hash table.
162 GHashTable *cell_info;
164 /* Tracking which cells are focus siblings of focusable cells */
165 GHashTable *focus_siblings;
167 /* The cell border decides how much space to reserve
168 * around each cell for the background_area
170 GtkBorder cell_border;
172 /* Current path is saved as a side-effect
173 * of gtk_cell_area_apply_attributes() */
176 /* Current cell being edited and editable widget used */
177 GtkCellEditable *edit_widget;
178 GtkCellRenderer *edited_cell;
180 /* Signal connections to the editable widget */
181 gulong editing_done_id;
182 gulong remove_widget_id;
184 /* Currently focused cell */
185 GtkCellRenderer *focus_cell;
190 PROP_CELL_MARGIN_LEFT,
191 PROP_CELL_MARGIN_RIGHT,
192 PROP_CELL_MARGIN_TOP,
193 PROP_CELL_MARGIN_BOTTOM,
200 SIGNAL_EDITING_STARTED,
201 SIGNAL_EDITING_CANCELED,
203 SIGNAL_REMOVE_EDITABLE,
207 /* Keep the paramspec pool internal, no need to deliver notifications
208 * on cells. at least no percieved need for now */
209 static GParamSpecPool *cell_property_pool = NULL;
210 static guint cell_area_signals[LAST_SIGNAL] = { 0 };
212 #define PARAM_SPEC_PARAM_ID(pspec) ((pspec)->param_id)
213 #define PARAM_SPEC_SET_PARAM_ID(pspec, id) ((pspec)->param_id = (id))
216 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GtkCellArea, gtk_cell_area, G_TYPE_INITIALLY_UNOWNED,
217 G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
218 gtk_cell_area_cell_layout_init));
221 gtk_cell_area_init (GtkCellArea *area)
223 GtkCellAreaPrivate *priv;
225 area->priv = G_TYPE_INSTANCE_GET_PRIVATE (area,
230 priv->cell_info = g_hash_table_new_full (g_direct_hash,
233 (GDestroyNotify)cell_info_free);
235 priv->focus_siblings = g_hash_table_new_full (g_direct_hash,
238 (GDestroyNotify)g_list_free);
240 priv->cell_border.left = 0;
241 priv->cell_border.right = 0;
242 priv->cell_border.top = 0;
243 priv->cell_border.bottom = 0;
245 priv->focus_cell = NULL;
246 priv->edited_cell = NULL;
247 priv->edit_widget = NULL;
249 priv->editing_done_id = 0;
250 priv->remove_widget_id = 0;
254 gtk_cell_area_class_init (GtkCellAreaClass *class)
256 GObjectClass *object_class = G_OBJECT_CLASS (class);
259 object_class->dispose = gtk_cell_area_dispose;
260 object_class->finalize = gtk_cell_area_finalize;
261 object_class->get_property = gtk_cell_area_get_property;
262 object_class->set_property = gtk_cell_area_set_property;
266 class->remove = NULL;
267 class->forall = NULL;
268 class->event = gtk_cell_area_real_event;
269 class->render = NULL;
272 class->create_iter = NULL;
273 class->get_request_mode = NULL;
274 class->get_preferred_width = NULL;
275 class->get_preferred_height = NULL;
276 class->get_preferred_height_for_width = gtk_cell_area_real_get_preferred_height_for_width;
277 class->get_preferred_width_for_height = gtk_cell_area_real_get_preferred_width_for_height;
280 class->can_focus = gtk_cell_area_real_can_focus;
282 class->activate = gtk_cell_area_real_activate;
285 cell_area_signals[SIGNAL_EDITING_STARTED] =
286 g_signal_new (I_("editing-started"),
287 G_OBJECT_CLASS_TYPE (object_class),
289 0, /* No class closure here */
291 _gtk_marshal_VOID__OBJECT_OBJECT_STRING,
293 GTK_TYPE_CELL_RENDERER,
294 GTK_TYPE_CELL_EDITABLE,
297 cell_area_signals[SIGNAL_EDITING_CANCELED] =
298 g_signal_new (I_("editing-canceled"),
299 G_OBJECT_CLASS_TYPE (object_class),
301 0, /* No class closure here */
303 _gtk_marshal_VOID__OBJECT,
305 GTK_TYPE_CELL_RENDERER);
307 cell_area_signals[SIGNAL_EDITING_DONE] =
308 g_signal_new (I_("editing-done"),
309 G_OBJECT_CLASS_TYPE (object_class),
311 0, /* No class closure here */
313 _gtk_marshal_VOID__OBJECT_OBJECT,
315 GTK_TYPE_CELL_RENDERER,
316 GTK_TYPE_CELL_EDITABLE);
318 cell_area_signals[SIGNAL_REMOVE_EDITABLE] =
319 g_signal_new (I_("remove-editable"),
320 G_OBJECT_CLASS_TYPE (object_class),
322 0, /* No class closure here */
324 _gtk_marshal_VOID__OBJECT_OBJECT,
326 GTK_TYPE_CELL_RENDERER,
327 GTK_TYPE_CELL_EDITABLE);
330 g_object_class_install_property (object_class,
331 PROP_CELL_MARGIN_LEFT,
334 P_("Margin on Left"),
335 P_("Pixels of extra space on the left side of each cell"),
339 GTK_PARAM_READWRITE));
341 g_object_class_install_property (object_class,
342 PROP_CELL_MARGIN_RIGHT,
344 ("cell-margin-right",
345 P_("Margin on Right"),
346 P_("Pixels of extra space on the right side of each cell"),
350 GTK_PARAM_READWRITE));
352 g_object_class_install_property (object_class,
353 PROP_CELL_MARGIN_TOP,
357 P_("Pixels of extra space on the top side of each cell"),
361 GTK_PARAM_READWRITE));
363 g_object_class_install_property (object_class,
364 PROP_CELL_MARGIN_BOTTOM,
366 ("cell-margin-bottom",
367 P_("Margin on Bottom"),
368 P_("Pixels of extra space on the bottom side of each cell"),
372 GTK_PARAM_READWRITE));
374 g_object_class_install_property (object_class,
379 P_("The cell which currently has focus"),
380 GTK_TYPE_CELL_RENDERER,
381 GTK_PARAM_READWRITE));
383 g_object_class_install_property (object_class,
388 P_("The cell which is currently being edited"),
389 GTK_TYPE_CELL_RENDERER,
390 GTK_PARAM_READWRITE));
392 g_object_class_install_property (object_class,
397 P_("The widget currently editing the edited cell"),
398 GTK_TYPE_CELL_RENDERER,
399 GTK_PARAM_READWRITE));
401 /* Pool for Cell Properties */
402 if (!cell_property_pool)
403 cell_property_pool = g_param_spec_pool_new (FALSE);
405 g_type_class_add_private (object_class, sizeof (GtkCellAreaPrivate));
408 /*************************************************************
410 *************************************************************/
412 cell_info_new (GtkCellLayoutDataFunc func,
414 GDestroyNotify destroy)
416 CellInfo *info = g_slice_new (CellInfo);
418 info->attributes = NULL;
421 info->destroy = destroy;
427 cell_info_free (CellInfo *info)
430 info->destroy (info->data);
432 g_slist_foreach (info->attributes, (GFunc)cell_attribute_free, NULL);
433 g_slist_free (info->attributes);
435 g_slice_free (CellInfo, info);
438 static CellAttribute *
439 cell_attribute_new (GtkCellRenderer *renderer,
440 const gchar *attribute,
445 /* Check if the attribute really exists and point to
446 * the property string installed on the cell renderer
447 * class (dont dup the string)
449 pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (renderer), attribute);
453 CellAttribute *cell_attribute = g_slice_new (CellAttribute);
455 cell_attribute->attribute = pspec->name;
456 cell_attribute->column = column;
458 return cell_attribute;
465 cell_attribute_free (CellAttribute *attribute)
467 g_slice_free (CellAttribute, attribute);
470 /* GCompareFunc for g_slist_find_custom() */
472 cell_attribute_find (CellAttribute *cell_attribute,
473 const gchar *attribute)
475 return g_strcmp0 (cell_attribute->attribute, attribute);
478 /*************************************************************
480 *************************************************************/
482 gtk_cell_area_finalize (GObject *object)
484 GtkCellArea *area = GTK_CELL_AREA (object);
485 GtkCellAreaPrivate *priv = area->priv;
487 /* All cell renderers should already be removed at this point,
488 * just kill our (empty) hash tables here.
490 g_hash_table_destroy (priv->cell_info);
491 g_hash_table_destroy (priv->focus_siblings);
493 g_free (priv->current_path);
495 G_OBJECT_CLASS (gtk_cell_area_parent_class)->finalize (object);
500 gtk_cell_area_dispose (GObject *object)
502 /* This removes every cell renderer that may be added to the GtkCellArea,
503 * subclasses should be breaking references to the GtkCellRenderers
506 gtk_cell_layout_clear (GTK_CELL_LAYOUT (object));
508 /* Remove any ref to a focused/edited cell */
509 gtk_cell_area_set_focus_cell (GTK_CELL_AREA (object), NULL);
510 gtk_cell_area_set_edited_cell (GTK_CELL_AREA (object), NULL);
511 gtk_cell_area_set_edit_widget (GTK_CELL_AREA (object), NULL);
513 G_OBJECT_CLASS (gtk_cell_area_parent_class)->dispose (object);
517 gtk_cell_area_set_property (GObject *object,
522 GtkCellArea *area = GTK_CELL_AREA (object);
526 case PROP_CELL_MARGIN_LEFT:
527 gtk_cell_area_set_cell_margin_left (area, g_value_get_int (value));
529 case PROP_CELL_MARGIN_RIGHT:
530 gtk_cell_area_set_cell_margin_right (area, g_value_get_int (value));
532 case PROP_CELL_MARGIN_TOP:
533 gtk_cell_area_set_cell_margin_top (area, g_value_get_int (value));
535 case PROP_CELL_MARGIN_BOTTOM:
536 gtk_cell_area_set_cell_margin_bottom (area, g_value_get_int (value));
538 case PROP_FOCUS_CELL:
539 gtk_cell_area_set_focus_cell (area, (GtkCellRenderer *)g_value_get_object (value));
541 case PROP_EDITED_CELL:
542 gtk_cell_area_set_edited_cell (area, (GtkCellRenderer *)g_value_get_object (value));
544 case PROP_EDIT_WIDGET:
545 gtk_cell_area_set_edit_widget (area, (GtkCellEditable *)g_value_get_object (value));
548 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
554 gtk_cell_area_get_property (GObject *object,
559 GtkCellArea *area = GTK_CELL_AREA (object);
560 GtkCellAreaPrivate *priv = area->priv;
564 case PROP_CELL_MARGIN_LEFT:
565 g_value_set_int (value, priv->cell_border.left);
567 case PROP_CELL_MARGIN_RIGHT:
568 g_value_set_int (value, priv->cell_border.right);
570 case PROP_CELL_MARGIN_TOP:
571 g_value_set_int (value, priv->cell_border.top);
573 case PROP_CELL_MARGIN_BOTTOM:
574 g_value_set_int (value, priv->cell_border.bottom);
576 case PROP_FOCUS_CELL:
577 g_value_set_object (value, priv->focus_cell);
579 case PROP_EDITED_CELL:
580 g_value_set_object (value, priv->edited_cell);
582 case PROP_EDIT_WIDGET:
583 g_value_set_object (value, priv->edit_widget);
586 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
591 /*************************************************************
593 *************************************************************/
595 gtk_cell_area_real_event (GtkCellArea *area,
596 GtkCellAreaIter *iter,
599 const GdkRectangle *cell_area,
600 GtkCellRendererState flags)
602 GtkCellAreaPrivate *priv = area->priv;
604 if (event->type == GDK_KEY_PRESS && (flags & GTK_CELL_RENDERER_FOCUSED) != 0)
606 GdkEventKey *key_event = (GdkEventKey *)event;
608 /* Cancel any edits in progress */
609 if (priv->edited_cell && (key_event->keyval == GDK_KEY_Escape))
611 gtk_cell_area_stop_editing (area, TRUE);
620 gtk_cell_area_real_get_preferred_height_for_width (GtkCellArea *area,
621 GtkCellAreaIter *iter,
624 gint *minimum_height,
625 gint *natural_height)
627 /* If the area doesnt do height-for-width, fallback on base preferred height */
628 GTK_CELL_AREA_GET_CLASS (area)->get_preferred_width (area, iter, widget, minimum_height, natural_height);
632 gtk_cell_area_real_get_preferred_width_for_height (GtkCellArea *area,
633 GtkCellAreaIter *iter,
639 /* If the area doesnt do width-for-height, fallback on base preferred width */
640 GTK_CELL_AREA_GET_CLASS (area)->get_preferred_width (area, iter, widget, minimum_width, natural_width);
644 get_can_focus (GtkCellRenderer *renderer,
648 if (gtk_cell_renderer_can_focus (renderer))
653 gtk_cell_area_real_can_focus (GtkCellArea *area)
655 gboolean can_focus = FALSE;
657 /* Checks if any renderer can focus for the currently applied
660 * Subclasses can override this in the case that they are also
661 * rendering widgets as well as renderers.
663 gtk_cell_area_forall (area, (GtkCellCallback)get_can_focus, &can_focus);
669 gtk_cell_area_real_activate (GtkCellArea *area,
670 GtkCellAreaIter *iter,
672 const GdkRectangle *cell_area,
673 GtkCellRendererState flags)
675 GtkCellAreaPrivate *priv = area->priv;
676 GdkRectangle background_area;
678 if (priv->focus_cell)
680 /* Get the allocation of the focused cell.
682 gtk_cell_area_get_cell_allocation (area, iter, widget, priv->focus_cell,
683 cell_area, &background_area);
685 /* Activate or Edit the currently focused cell
687 * Currently just not sending an event, renderers afaics dont use
688 * the event argument anyway, worst case is we can synthesize one.
690 if (gtk_cell_area_activate_cell (area, widget, priv->focus_cell, NULL,
691 &background_area, flags))
698 /*************************************************************
699 * GtkCellLayoutIface *
700 *************************************************************/
702 gtk_cell_area_cell_layout_init (GtkCellLayoutIface *iface)
704 iface->pack_start = gtk_cell_area_pack_default;
705 iface->pack_end = gtk_cell_area_pack_default;
706 iface->clear = gtk_cell_area_clear;
707 iface->add_attribute = gtk_cell_area_add_attribute;
708 iface->set_cell_data_func = gtk_cell_area_set_cell_data_func;
709 iface->clear_attributes = gtk_cell_area_clear_attributes;
710 iface->reorder = gtk_cell_area_reorder;
711 iface->get_cells = gtk_cell_area_get_cells;
715 gtk_cell_area_pack_default (GtkCellLayout *cell_layout,
716 GtkCellRenderer *renderer,
719 gtk_cell_area_add (GTK_CELL_AREA (cell_layout), renderer);
723 gtk_cell_area_clear (GtkCellLayout *cell_layout)
725 GtkCellArea *area = GTK_CELL_AREA (cell_layout);
727 gtk_cell_layout_get_cells (cell_layout);
729 for (l = cells; l; l = l->next)
731 GtkCellRenderer *renderer = l->data;
732 gtk_cell_area_remove (area, renderer);
739 gtk_cell_area_add_attribute (GtkCellLayout *cell_layout,
740 GtkCellRenderer *renderer,
741 const gchar *attribute,
744 gtk_cell_area_attribute_connect (GTK_CELL_AREA (cell_layout),
745 renderer, attribute, column);
749 gtk_cell_area_set_cell_data_func (GtkCellLayout *cell_layout,
750 GtkCellRenderer *renderer,
751 GtkCellLayoutDataFunc func,
753 GDestroyNotify destroy)
755 GtkCellArea *area = GTK_CELL_AREA (cell_layout);
756 GtkCellAreaPrivate *priv = area->priv;
759 info = g_hash_table_lookup (priv->cell_info, renderer);
763 if (info->destroy && info->data)
764 info->destroy (info->data);
769 info->data = func_data;
770 info->destroy = destroy;
776 info->destroy = NULL;
781 info = cell_info_new (func, func_data, destroy);
783 g_hash_table_insert (priv->cell_info, renderer, info);
788 gtk_cell_area_clear_attributes (GtkCellLayout *cell_layout,
789 GtkCellRenderer *renderer)
791 GtkCellArea *area = GTK_CELL_AREA (cell_layout);
792 GtkCellAreaPrivate *priv = area->priv;
795 info = g_hash_table_lookup (priv->cell_info, renderer);
799 g_slist_foreach (info->attributes, (GFunc)cell_attribute_free, NULL);
800 g_slist_free (info->attributes);
802 info->attributes = NULL;
807 gtk_cell_area_reorder (GtkCellLayout *cell_layout,
808 GtkCellRenderer *cell,
811 g_warning ("GtkCellLayout::reorder not implemented for `%s'",
812 g_type_name (G_TYPE_FROM_INSTANCE (cell_layout)));
816 accum_cells (GtkCellRenderer *renderer,
819 *accum = g_list_prepend (*accum, renderer);
823 gtk_cell_area_get_cells (GtkCellLayout *cell_layout)
827 gtk_cell_area_forall (GTK_CELL_AREA (cell_layout),
828 (GtkCellCallback)accum_cells,
831 return g_list_reverse (cells);
835 /*************************************************************
837 *************************************************************/
841 * @area: a #GtkCellArea
842 * @renderer: the #GtkCellRenderer to add to @area
844 * Adds @renderer to @area with the default child cell properties.
847 gtk_cell_area_add (GtkCellArea *area,
848 GtkCellRenderer *renderer)
850 GtkCellAreaClass *class;
852 g_return_if_fail (GTK_IS_CELL_AREA (area));
853 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
855 class = GTK_CELL_AREA_GET_CLASS (area);
858 class->add (area, renderer);
860 g_warning ("GtkCellAreaClass::add not implemented for `%s'",
861 g_type_name (G_TYPE_FROM_INSTANCE (area)));
865 * gtk_cell_area_remove:
866 * @area: a #GtkCellArea
867 * @renderer: the #GtkCellRenderer to add to @area
869 * Removes @renderer from @area.
872 gtk_cell_area_remove (GtkCellArea *area,
873 GtkCellRenderer *renderer)
875 GtkCellAreaClass *class;
876 GtkCellAreaPrivate *priv;
877 GList *renderers, *l;
879 g_return_if_fail (GTK_IS_CELL_AREA (area));
880 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
882 class = GTK_CELL_AREA_GET_CLASS (area);
885 /* Remove any custom attributes and custom cell data func here first */
886 g_hash_table_remove (priv->cell_info, renderer);
888 /* Remove focus siblings of this renderer */
889 g_hash_table_remove (priv->focus_siblings, renderer);
891 /* Remove this renderer from any focus renderer's sibling list */
892 renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (area));
894 for (l = renderers; l; l = l->next)
896 GtkCellRenderer *focus_renderer = l->data;
898 if (gtk_cell_area_is_focus_sibling (area, focus_renderer, renderer))
900 gtk_cell_area_remove_focus_sibling (area, focus_renderer, renderer);
905 g_list_free (renderers);
908 class->remove (area, renderer);
910 g_warning ("GtkCellAreaClass::remove not implemented for `%s'",
911 g_type_name (G_TYPE_FROM_INSTANCE (area)));
915 get_has_renderer (GtkCellRenderer *renderer,
916 HasRendererCheck *check)
918 if (renderer == check->renderer)
919 check->has_renderer = TRUE;
923 gtk_cell_area_has_renderer (GtkCellArea *area,
924 GtkCellRenderer *renderer)
926 HasRendererCheck check = { renderer, FALSE };
928 g_return_val_if_fail (GTK_IS_CELL_AREA (area), FALSE);
929 g_return_val_if_fail (GTK_IS_CELL_RENDERER (renderer), FALSE);
931 gtk_cell_area_forall (area, (GtkCellCallback)get_has_renderer, &check);
933 return check.has_renderer;
937 * gtk_cell_area_forall
938 * @area: a #GtkCellArea
939 * @callback: the #GtkCellCallback to call
940 * @callback_data: user provided data pointer
942 * Calls @callback for every #GtkCellRenderer in @area.
945 gtk_cell_area_forall (GtkCellArea *area,
946 GtkCellCallback callback,
947 gpointer callback_data)
949 GtkCellAreaClass *class;
951 g_return_if_fail (GTK_IS_CELL_AREA (area));
952 g_return_if_fail (callback != NULL);
954 class = GTK_CELL_AREA_GET_CLASS (area);
957 class->forall (area, callback, callback_data);
959 g_warning ("GtkCellAreaClass::forall not implemented for `%s'",
960 g_type_name (G_TYPE_FROM_INSTANCE (area)));
964 * gtk_cell_area_get_cell_allocation:
965 * @area: a #GtkCellArea
966 * @iter: the #GtkCellAreaIter used to hold sizes for @area.
967 * @widget: the #GtkWidget that @area is rendering on
968 * @renderer: the #GtkCellRenderer to get the allocation for
969 * @cell_area: the whole allocated area for @area in @widget
971 * @allocation: where to store the allocation for @renderer
973 * Derives the allocation of @renderer inside @area if @area
974 * were to be renderered in @cell_area.
977 gtk_cell_area_get_cell_allocation (GtkCellArea *area,
978 GtkCellAreaIter *iter,
980 GtkCellRenderer *renderer,
981 const GdkRectangle *cell_area,
982 GdkRectangle *allocation)
984 GtkCellAreaClass *class;
986 g_return_if_fail (GTK_IS_CELL_AREA (area));
987 g_return_if_fail (GTK_IS_CELL_AREA_ITER (iter));
988 g_return_if_fail (GTK_IS_WIDGET (widget));
989 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
990 g_return_if_fail (cell_area != NULL);
991 g_return_if_fail (allocation != NULL);
993 class = GTK_CELL_AREA_GET_CLASS (area);
995 if (class->get_cell_allocation)
996 class->get_cell_allocation (area, iter, widget, renderer, cell_area, allocation);
998 g_warning ("GtkCellAreaClass::get_cell_allocation not implemented for `%s'",
999 g_type_name (G_TYPE_FROM_INSTANCE (area)));
1003 gtk_cell_area_event (GtkCellArea *area,
1004 GtkCellAreaIter *iter,
1007 const GdkRectangle *cell_area,
1008 GtkCellRendererState flags)
1010 GtkCellAreaClass *class;
1012 g_return_val_if_fail (GTK_IS_CELL_AREA (area), 0);
1013 g_return_val_if_fail (GTK_IS_CELL_AREA_ITER (iter), 0);
1014 g_return_val_if_fail (GTK_IS_WIDGET (widget), 0);
1015 g_return_val_if_fail (event != NULL, 0);
1016 g_return_val_if_fail (cell_area != NULL, 0);
1018 class = GTK_CELL_AREA_GET_CLASS (area);
1021 return class->event (area, iter, widget, event, cell_area, flags);
1023 g_warning ("GtkCellAreaClass::event not implemented for `%s'",
1024 g_type_name (G_TYPE_FROM_INSTANCE (area)));
1029 gtk_cell_area_render (GtkCellArea *area,
1030 GtkCellAreaIter *iter,
1033 const GdkRectangle *background_area,
1034 const GdkRectangle *cell_area,
1035 GtkCellRendererState flags,
1036 gboolean paint_focus)
1038 GtkCellAreaClass *class;
1040 g_return_if_fail (GTK_IS_CELL_AREA (area));
1041 g_return_if_fail (GTK_IS_CELL_AREA_ITER (iter));
1042 g_return_if_fail (GTK_IS_WIDGET (widget));
1043 g_return_if_fail (cr != NULL);
1044 g_return_if_fail (background_area != NULL);
1045 g_return_if_fail (cell_area != NULL);
1047 class = GTK_CELL_AREA_GET_CLASS (area);
1050 class->render (area, iter, widget, cr, background_area, cell_area, flags, paint_focus);
1052 g_warning ("GtkCellAreaClass::render not implemented for `%s'",
1053 g_type_name (G_TYPE_FROM_INSTANCE (area)));
1056 /*************************************************************
1058 *************************************************************/
1060 gtk_cell_area_create_iter (GtkCellArea *area)
1062 GtkCellAreaClass *class;
1064 g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL);
1066 class = GTK_CELL_AREA_GET_CLASS (area);
1068 if (class->create_iter)
1069 return class->create_iter (area);
1071 g_warning ("GtkCellAreaClass::create_iter not implemented for `%s'",
1072 g_type_name (G_TYPE_FROM_INSTANCE (area)));
1079 gtk_cell_area_get_request_mode (GtkCellArea *area)
1081 GtkCellAreaClass *class;
1083 g_return_val_if_fail (GTK_IS_CELL_AREA (area),
1084 GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH);
1086 class = GTK_CELL_AREA_GET_CLASS (area);
1088 if (class->get_request_mode)
1089 return class->get_request_mode (area);
1091 g_warning ("GtkCellAreaClass::get_request_mode not implemented for `%s'",
1092 g_type_name (G_TYPE_FROM_INSTANCE (area)));
1094 return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
1098 gtk_cell_area_get_preferred_width (GtkCellArea *area,
1099 GtkCellAreaIter *iter,
1104 GtkCellAreaClass *class;
1106 g_return_if_fail (GTK_IS_CELL_AREA (area));
1107 g_return_if_fail (GTK_IS_WIDGET (widget));
1109 class = GTK_CELL_AREA_GET_CLASS (area);
1111 if (class->get_preferred_width)
1112 class->get_preferred_width (area, iter, widget, minimum_size, natural_size);
1114 g_warning ("GtkCellAreaClass::get_preferred_width not implemented for `%s'",
1115 g_type_name (G_TYPE_FROM_INSTANCE (area)));
1119 gtk_cell_area_get_preferred_height_for_width (GtkCellArea *area,
1120 GtkCellAreaIter *iter,
1123 gint *minimum_height,
1124 gint *natural_height)
1126 GtkCellAreaClass *class;
1128 g_return_if_fail (GTK_IS_CELL_AREA (area));
1129 g_return_if_fail (GTK_IS_WIDGET (widget));
1131 class = GTK_CELL_AREA_GET_CLASS (area);
1132 class->get_preferred_height_for_width (area, iter, widget, width, minimum_height, natural_height);
1136 gtk_cell_area_get_preferred_height (GtkCellArea *area,
1137 GtkCellAreaIter *iter,
1142 GtkCellAreaClass *class;
1144 g_return_if_fail (GTK_IS_CELL_AREA (area));
1145 g_return_if_fail (GTK_IS_WIDGET (widget));
1147 class = GTK_CELL_AREA_GET_CLASS (area);
1149 if (class->get_preferred_height)
1150 class->get_preferred_height (area, iter, widget, minimum_size, natural_size);
1152 g_warning ("GtkCellAreaClass::get_preferred_height not implemented for `%s'",
1153 g_type_name (G_TYPE_FROM_INSTANCE (area)));
1157 gtk_cell_area_get_preferred_width_for_height (GtkCellArea *area,
1158 GtkCellAreaIter *iter,
1161 gint *minimum_width,
1162 gint *natural_width)
1164 GtkCellAreaClass *class;
1166 g_return_if_fail (GTK_IS_CELL_AREA (area));
1167 g_return_if_fail (GTK_IS_WIDGET (widget));
1169 class = GTK_CELL_AREA_GET_CLASS (area);
1170 class->get_preferred_width_for_height (area, iter, widget, height, minimum_width, natural_width);
1173 /*************************************************************
1175 *************************************************************/
1178 * gtk_cell_area_attribute_connect:
1179 * @area: a #GtkCellArea
1180 * @renderer: the #GtkCellRenderer to connect an attribute for
1181 * @attribute: the attribute name
1182 * @column: the #GtkTreeModel column to fetch attribute values from
1184 * Connects an @attribute to apply values from @column for the
1185 * #GtkTreeModel in use.
1188 gtk_cell_area_attribute_connect (GtkCellArea *area,
1189 GtkCellRenderer *renderer,
1190 const gchar *attribute,
1193 GtkCellAreaPrivate *priv;
1195 CellAttribute *cell_attribute;
1197 g_return_if_fail (GTK_IS_CELL_AREA (area));
1198 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1199 g_return_if_fail (attribute != NULL);
1200 g_return_if_fail (gtk_cell_area_has_renderer (area, renderer));
1203 info = g_hash_table_lookup (priv->cell_info, renderer);
1207 info = cell_info_new (NULL, NULL, NULL);
1209 g_hash_table_insert (priv->cell_info, renderer, info);
1215 /* Check we are not adding the same attribute twice */
1216 if ((node = g_slist_find_custom (info->attributes, attribute,
1217 (GCompareFunc)cell_attribute_find)) != NULL)
1219 cell_attribute = node->data;
1221 g_warning ("Cannot connect attribute `%s' for cell renderer class `%s' "
1222 "since `%s' is already attributed to column %d",
1224 g_type_name (G_TYPE_FROM_INSTANCE (area)),
1225 attribute, cell_attribute->column);
1230 cell_attribute = cell_attribute_new (renderer, attribute, column);
1232 if (!cell_attribute)
1234 g_warning ("Cannot connect attribute `%s' for cell renderer class `%s' "
1235 "since attribute does not exist",
1237 g_type_name (G_TYPE_FROM_INSTANCE (area)));
1241 info->attributes = g_slist_prepend (info->attributes, cell_attribute);
1245 * gtk_cell_area_attribute_disconnect:
1246 * @area: a #GtkCellArea
1247 * @renderer: the #GtkCellRenderer to disconnect an attribute for
1248 * @attribute: the attribute name
1250 * Disconnects @attribute for the @renderer in @area so that
1251 * attribute will no longer be updated with values from the
1255 gtk_cell_area_attribute_disconnect (GtkCellArea *area,
1256 GtkCellRenderer *renderer,
1257 const gchar *attribute)
1259 GtkCellAreaPrivate *priv;
1261 CellAttribute *cell_attribute;
1264 g_return_if_fail (GTK_IS_CELL_AREA (area));
1265 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1266 g_return_if_fail (attribute != NULL);
1267 g_return_if_fail (gtk_cell_area_has_renderer (area, renderer));
1270 info = g_hash_table_lookup (priv->cell_info, renderer);
1274 node = g_slist_find_custom (info->attributes, attribute,
1275 (GCompareFunc)cell_attribute_find);
1278 cell_attribute = node->data;
1280 cell_attribute_free (cell_attribute);
1282 info->attributes = g_slist_delete_link (info->attributes, node);
1288 apply_cell_attributes (GtkCellRenderer *renderer,
1290 AttributeData *data)
1292 CellAttribute *attribute;
1294 GValue value = { 0, };
1295 gboolean is_expander;
1296 gboolean is_expanded;
1298 g_object_freeze_notify (G_OBJECT (renderer));
1300 /* Whether a row expands or is presently expanded can only be
1301 * provided by the view (as these states can vary across views
1302 * accessing the same model).
1304 g_object_get (renderer, "is-expander", &is_expander, NULL);
1305 if (is_expander != data->is_expander)
1306 g_object_set (renderer, "is-expander", data->is_expander, NULL);
1308 g_object_get (renderer, "is-expanded", &is_expanded, NULL);
1309 if (is_expanded != data->is_expanded)
1310 g_object_set (renderer, "is-expanded", data->is_expanded, NULL);
1312 /* Apply the attributes directly to the renderer */
1313 for (list = info->attributes; list; list = list->next)
1315 attribute = list->data;
1317 gtk_tree_model_get_value (data->model, data->iter, attribute->column, &value);
1318 g_object_set_property (G_OBJECT (renderer), attribute->attribute, &value);
1319 g_value_unset (&value);
1322 /* Call any GtkCellLayoutDataFunc that may have been set by the user
1325 info->func (GTK_CELL_LAYOUT (data->area), renderer,
1326 data->model, data->iter, info->data);
1328 g_object_thaw_notify (G_OBJECT (renderer));
1332 * gtk_cell_area_apply_attributes
1333 * @area: a #GtkCellArea
1334 * @tree_model: a #GtkTreeModel to pull values from
1335 * @iter: the #GtkTreeIter in @tree_model to apply values for
1336 * @is_expander: whether @iter has children
1337 * @is_expanded: whether @iter is expanded in the view and
1338 * children are visible
1340 * Applies any connected attributes to the renderers in
1341 * @area by pulling the values from @tree_model.
1344 gtk_cell_area_apply_attributes (GtkCellArea *area,
1345 GtkTreeModel *tree_model,
1347 gboolean is_expander,
1348 gboolean is_expanded)
1350 GtkCellAreaPrivate *priv;
1354 g_return_if_fail (GTK_IS_CELL_AREA (area));
1355 g_return_if_fail (GTK_IS_TREE_MODEL (tree_model));
1356 g_return_if_fail (iter != NULL);
1360 /* Feed in data needed to apply to every renderer */
1362 data.model = tree_model;
1364 data.is_expander = is_expander;
1365 data.is_expanded = is_expanded;
1367 /* Go over any cells that have attributes or custom GtkCellLayoutDataFuncs and
1368 * apply the data from the treemodel */
1369 g_hash_table_foreach (priv->cell_info, (GHFunc)apply_cell_attributes, &data);
1371 /* Update the currently applied path */
1372 g_free (priv->current_path);
1373 path = gtk_tree_model_get_path (tree_model, iter);
1374 priv->current_path = gtk_tree_path_to_string (path);
1375 gtk_tree_path_free (path);
1379 * gtk_cell_area_get_current_path_string:
1380 * @area: a #GtkCellArea
1382 * Gets the current #GtkTreePath string for the currently
1383 * applied #GtkTreeIter, this is implicitly updated when
1384 * gtk_cell_area_apply_attributes() is called and can be
1385 * used to interact with renderers from #GtkCellArea
1389 gtk_cell_area_get_current_path_string (GtkCellArea *area)
1391 GtkCellAreaPrivate *priv;
1393 g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL);
1397 return priv->current_path;
1401 /*************************************************************
1402 * API: Cell Properties *
1403 *************************************************************/
1405 gtk_cell_area_class_install_cell_property (GtkCellAreaClass *aclass,
1409 g_return_if_fail (GTK_IS_CELL_AREA_CLASS (aclass));
1410 g_return_if_fail (G_IS_PARAM_SPEC (pspec));
1411 if (pspec->flags & G_PARAM_WRITABLE)
1412 g_return_if_fail (aclass->set_cell_property != NULL);
1413 if (pspec->flags & G_PARAM_READABLE)
1414 g_return_if_fail (aclass->get_cell_property != NULL);
1415 g_return_if_fail (property_id > 0);
1416 g_return_if_fail (PARAM_SPEC_PARAM_ID (pspec) == 0); /* paranoid */
1417 g_return_if_fail ((pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY)) == 0);
1419 if (g_param_spec_pool_lookup (cell_property_pool, pspec->name, G_OBJECT_CLASS_TYPE (aclass), TRUE))
1421 g_warning (G_STRLOC ": class `%s' already contains a cell property named `%s'",
1422 G_OBJECT_CLASS_NAME (aclass), pspec->name);
1425 g_param_spec_ref (pspec);
1426 g_param_spec_sink (pspec);
1427 PARAM_SPEC_SET_PARAM_ID (pspec, property_id);
1428 g_param_spec_pool_insert (cell_property_pool, pspec, G_OBJECT_CLASS_TYPE (aclass));
1432 gtk_cell_area_class_find_cell_property (GtkCellAreaClass *aclass,
1433 const gchar *property_name)
1435 g_return_val_if_fail (GTK_IS_CELL_AREA_CLASS (aclass), NULL);
1436 g_return_val_if_fail (property_name != NULL, NULL);
1438 return g_param_spec_pool_lookup (cell_property_pool,
1440 G_OBJECT_CLASS_TYPE (aclass),
1445 gtk_cell_area_class_list_cell_properties (GtkCellAreaClass *aclass,
1446 guint *n_properties)
1448 GParamSpec **pspecs;
1451 g_return_val_if_fail (GTK_IS_CELL_AREA_CLASS (aclass), NULL);
1453 pspecs = g_param_spec_pool_list (cell_property_pool,
1454 G_OBJECT_CLASS_TYPE (aclass),
1463 gtk_cell_area_add_with_properties (GtkCellArea *area,
1464 GtkCellRenderer *renderer,
1465 const gchar *first_prop_name,
1468 GtkCellAreaClass *class;
1470 g_return_if_fail (GTK_IS_CELL_AREA (area));
1471 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1473 class = GTK_CELL_AREA_GET_CLASS (area);
1479 class->add (area, renderer);
1481 va_start (var_args, first_prop_name);
1482 gtk_cell_area_cell_set_valist (area, renderer, first_prop_name, var_args);
1486 g_warning ("GtkCellAreaClass::add not implemented for `%s'",
1487 g_type_name (G_TYPE_FROM_INSTANCE (area)));
1491 gtk_cell_area_cell_set (GtkCellArea *area,
1492 GtkCellRenderer *renderer,
1493 const gchar *first_prop_name,
1498 g_return_if_fail (GTK_IS_CELL_AREA (area));
1499 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1501 va_start (var_args, first_prop_name);
1502 gtk_cell_area_cell_set_valist (area, renderer, first_prop_name, var_args);
1507 gtk_cell_area_cell_get (GtkCellArea *area,
1508 GtkCellRenderer *renderer,
1509 const gchar *first_prop_name,
1514 g_return_if_fail (GTK_IS_CELL_AREA (area));
1515 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1517 va_start (var_args, first_prop_name);
1518 gtk_cell_area_cell_get_valist (area, renderer, first_prop_name, var_args);
1523 area_get_cell_property (GtkCellArea *area,
1524 GtkCellRenderer *renderer,
1528 GtkCellAreaClass *class = g_type_class_peek (pspec->owner_type);
1530 class->get_cell_property (area, renderer, PARAM_SPEC_PARAM_ID (pspec), value, pspec);
1534 area_set_cell_property (GtkCellArea *area,
1535 GtkCellRenderer *renderer,
1537 const GValue *value)
1539 GValue tmp_value = { 0, };
1540 GtkCellAreaClass *class = g_type_class_peek (pspec->owner_type);
1542 /* provide a copy to work from, convert (if necessary) and validate */
1543 g_value_init (&tmp_value, G_PARAM_SPEC_VALUE_TYPE (pspec));
1544 if (!g_value_transform (value, &tmp_value))
1545 g_warning ("unable to set cell property `%s' of type `%s' from value of type `%s'",
1547 g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)),
1548 G_VALUE_TYPE_NAME (value));
1549 else if (g_param_value_validate (pspec, &tmp_value) && !(pspec->flags & G_PARAM_LAX_VALIDATION))
1551 gchar *contents = g_strdup_value_contents (value);
1553 g_warning ("value \"%s\" of type `%s' is invalid for property `%s' of type `%s'",
1555 G_VALUE_TYPE_NAME (value),
1557 g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)));
1562 class->set_cell_property (area, renderer, PARAM_SPEC_PARAM_ID (pspec), &tmp_value, pspec);
1564 g_value_unset (&tmp_value);
1568 gtk_cell_area_cell_set_valist (GtkCellArea *area,
1569 GtkCellRenderer *renderer,
1570 const gchar *first_property_name,
1575 g_return_if_fail (GTK_IS_CELL_AREA (area));
1576 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1578 name = first_property_name;
1581 GValue value = { 0, };
1582 gchar *error = NULL;
1584 g_param_spec_pool_lookup (cell_property_pool, name,
1585 G_OBJECT_TYPE (area), TRUE);
1588 g_warning ("%s: cell area class `%s' has no cell property named `%s'",
1589 G_STRLOC, G_OBJECT_TYPE_NAME (area), name);
1592 if (!(pspec->flags & G_PARAM_WRITABLE))
1594 g_warning ("%s: cell property `%s' of cell area class `%s' is not writable",
1595 G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (area));
1599 g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
1600 G_VALUE_COLLECT (&value, var_args, 0, &error);
1603 g_warning ("%s: %s", G_STRLOC, error);
1606 /* we purposely leak the value here, it might not be
1607 * in a sane state if an error condition occoured
1611 area_set_cell_property (area, renderer, pspec, &value);
1612 g_value_unset (&value);
1613 name = va_arg (var_args, gchar*);
1618 gtk_cell_area_cell_get_valist (GtkCellArea *area,
1619 GtkCellRenderer *renderer,
1620 const gchar *first_property_name,
1625 g_return_if_fail (GTK_IS_CELL_AREA (area));
1626 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1628 name = first_property_name;
1631 GValue value = { 0, };
1635 pspec = g_param_spec_pool_lookup (cell_property_pool, name,
1636 G_OBJECT_TYPE (area), TRUE);
1639 g_warning ("%s: cell area class `%s' has no cell property named `%s'",
1640 G_STRLOC, G_OBJECT_TYPE_NAME (area), name);
1643 if (!(pspec->flags & G_PARAM_READABLE))
1645 g_warning ("%s: cell property `%s' of cell area class `%s' is not readable",
1646 G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (area));
1650 g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
1651 area_get_cell_property (area, renderer, pspec, &value);
1652 G_VALUE_LCOPY (&value, var_args, 0, &error);
1655 g_warning ("%s: %s", G_STRLOC, error);
1657 g_value_unset (&value);
1660 g_value_unset (&value);
1661 name = va_arg (var_args, gchar*);
1666 gtk_cell_area_cell_set_property (GtkCellArea *area,
1667 GtkCellRenderer *renderer,
1668 const gchar *property_name,
1669 const GValue *value)
1673 g_return_if_fail (GTK_IS_CELL_AREA (area));
1674 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1675 g_return_if_fail (property_name != NULL);
1676 g_return_if_fail (G_IS_VALUE (value));
1678 pspec = g_param_spec_pool_lookup (cell_property_pool, property_name,
1679 G_OBJECT_TYPE (area), TRUE);
1681 g_warning ("%s: cell area class `%s' has no cell property named `%s'",
1682 G_STRLOC, G_OBJECT_TYPE_NAME (area), property_name);
1683 else if (!(pspec->flags & G_PARAM_WRITABLE))
1684 g_warning ("%s: cell property `%s' of cell area class `%s' is not writable",
1685 G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (area));
1688 area_set_cell_property (area, renderer, pspec, value);
1693 gtk_cell_area_cell_get_property (GtkCellArea *area,
1694 GtkCellRenderer *renderer,
1695 const gchar *property_name,
1700 g_return_if_fail (GTK_IS_CELL_AREA (area));
1701 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1702 g_return_if_fail (property_name != NULL);
1703 g_return_if_fail (G_IS_VALUE (value));
1705 pspec = g_param_spec_pool_lookup (cell_property_pool, property_name,
1706 G_OBJECT_TYPE (area), TRUE);
1708 g_warning ("%s: cell area class `%s' has no cell property named `%s'",
1709 G_STRLOC, G_OBJECT_TYPE_NAME (area), property_name);
1710 else if (!(pspec->flags & G_PARAM_READABLE))
1711 g_warning ("%s: cell property `%s' of cell area class `%s' is not readable",
1712 G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (area));
1715 GValue *prop_value, tmp_value = { 0, };
1717 /* auto-conversion of the callers value type
1719 if (G_VALUE_TYPE (value) == G_PARAM_SPEC_VALUE_TYPE (pspec))
1721 g_value_reset (value);
1724 else if (!g_value_type_transformable (G_PARAM_SPEC_VALUE_TYPE (pspec), G_VALUE_TYPE (value)))
1726 g_warning ("can't retrieve cell property `%s' of type `%s' as value of type `%s'",
1728 g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)),
1729 G_VALUE_TYPE_NAME (value));
1734 g_value_init (&tmp_value, G_PARAM_SPEC_VALUE_TYPE (pspec));
1735 prop_value = &tmp_value;
1738 area_get_cell_property (area, renderer, pspec, prop_value);
1740 if (prop_value != value)
1742 g_value_transform (prop_value, value);
1743 g_value_unset (&tmp_value);
1748 /*************************************************************
1750 *************************************************************/
1753 * gtk_cell_area_can_focus:
1754 * @area: a #GtkCellArea
1756 * Returns whether the area can receive keyboard focus,
1757 * after applying new attributes to @area.
1759 * Returns: whether @area can receive focus.
1762 gtk_cell_area_can_focus (GtkCellArea *area)
1764 g_return_val_if_fail (GTK_IS_CELL_AREA (area), FALSE);
1766 return GTK_CELL_AREA_GET_CLASS (area)->can_focus (area);
1770 * gtk_cell_area_focus:
1771 * @area: a #GtkCellArea
1772 * @direction: the #GtkDirectionType
1774 * This should be called by the @area's owning layout widget
1775 * when focus is to be passed to @area, or moved within @area
1776 * for a given @direction and row data.
1778 * Implementing #GtkCellArea classes should implement this
1779 * method to receive and navigate focus in it's own way particular
1780 * to how it lays out cells.
1782 * Returns: %TRUE if focus remains inside @area as a result of this call.
1785 gtk_cell_area_focus (GtkCellArea *area,
1786 GtkDirectionType direction)
1788 GtkCellAreaClass *class;
1790 g_return_val_if_fail (GTK_IS_CELL_AREA (area), FALSE);
1792 class = GTK_CELL_AREA_GET_CLASS (area);
1795 return class->focus (area, direction);
1797 g_warning ("GtkCellAreaClass::focus not implemented for `%s'",
1798 g_type_name (G_TYPE_FROM_INSTANCE (area)));
1804 * gtk_cell_area_activate:
1805 * @area: a #GtkCellArea
1806 * @iter: the #GtkCellAreaIter in context with the current row data
1807 * @widget: the #GtkWidget that @area is rendering on
1808 * @cell_area: the size and location of @area relative to @widget's allocation
1809 * @flags: the #GtkCellRendererState flags for @area for this row of data.
1811 * Activates @area, usually by activating the currently focused
1812 * cell, however some subclasses which embed widgets in the area
1813 * can also activate a widget if it currently has the focus.
1815 * Returns: Whether @area was successfully activated.
1818 gtk_cell_area_activate (GtkCellArea *area,
1819 GtkCellAreaIter *iter,
1821 const GdkRectangle *cell_area,
1822 GtkCellRendererState flags)
1824 g_return_val_if_fail (GTK_IS_CELL_AREA (area), FALSE);
1826 return GTK_CELL_AREA_GET_CLASS (area)->activate (area, iter, widget, cell_area, flags);
1831 * gtk_cell_area_set_focus_cell:
1832 * @area: a #GtkCellArea
1833 * @focus_cell: the #GtkCellRenderer to give focus to
1835 * This is generally called from #GtkCellArea implementations
1836 * either gtk_cell_area_grab_focus() or gtk_cell_area_update_focus()
1837 * is called. It's also up to the #GtkCellArea implementation
1838 * to update the focused cell when receiving events from
1839 * gtk_cell_area_event() appropriately.
1842 gtk_cell_area_set_focus_cell (GtkCellArea *area,
1843 GtkCellRenderer *renderer)
1845 GtkCellAreaPrivate *priv;
1847 g_return_if_fail (GTK_IS_CELL_AREA (area));
1848 g_return_if_fail (renderer == NULL || GTK_IS_CELL_RENDERER (renderer));
1852 if (priv->focus_cell != renderer)
1854 if (priv->focus_cell)
1855 g_object_unref (priv->focus_cell);
1857 priv->focus_cell = renderer;
1859 if (priv->focus_cell)
1860 g_object_ref (priv->focus_cell);
1862 g_object_notify (G_OBJECT (area), "focus-cell");
1867 * gtk_cell_area_get_focus_cell:
1868 * @area: a #GtkCellArea
1870 * Retrieves the currently focused cell for @area
1872 * Returns: the currently focused cell in @area.
1875 gtk_cell_area_get_focus_cell (GtkCellArea *area)
1877 GtkCellAreaPrivate *priv;
1879 g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL);
1883 return priv->focus_cell;
1887 /*************************************************************
1888 * API: Focus Siblings *
1889 *************************************************************/
1891 gtk_cell_area_add_focus_sibling (GtkCellArea *area,
1892 GtkCellRenderer *renderer,
1893 GtkCellRenderer *sibling)
1895 GtkCellAreaPrivate *priv;
1898 g_return_if_fail (GTK_IS_CELL_AREA (area));
1899 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1900 g_return_if_fail (GTK_IS_CELL_RENDERER (sibling));
1901 g_return_if_fail (renderer != sibling);
1902 g_return_if_fail (gtk_cell_area_has_renderer (area, renderer));
1903 g_return_if_fail (gtk_cell_area_has_renderer (area, sibling));
1904 g_return_if_fail (!gtk_cell_area_is_focus_sibling (area, renderer, sibling));
1906 /* XXX We should also check that sibling is not in any other renderer's sibling
1907 * list already, a renderer can be sibling of only one focusable renderer
1913 siblings = g_hash_table_lookup (priv->focus_siblings, renderer);
1916 siblings = g_list_append (siblings, sibling);
1919 siblings = g_list_append (siblings, sibling);
1920 g_hash_table_insert (priv->focus_siblings, renderer, siblings);
1925 gtk_cell_area_remove_focus_sibling (GtkCellArea *area,
1926 GtkCellRenderer *renderer,
1927 GtkCellRenderer *sibling)
1929 GtkCellAreaPrivate *priv;
1932 g_return_if_fail (GTK_IS_CELL_AREA (area));
1933 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1934 g_return_if_fail (GTK_IS_CELL_RENDERER (sibling));
1935 g_return_if_fail (gtk_cell_area_is_focus_sibling (area, renderer, sibling));
1939 siblings = g_hash_table_lookup (priv->focus_siblings, renderer);
1941 siblings = g_list_copy (siblings);
1942 siblings = g_list_remove (siblings, sibling);
1945 g_hash_table_remove (priv->focus_siblings, renderer);
1947 g_hash_table_insert (priv->focus_siblings, renderer, siblings);
1951 gtk_cell_area_is_focus_sibling (GtkCellArea *area,
1952 GtkCellRenderer *renderer,
1953 GtkCellRenderer *sibling)
1955 GtkCellAreaPrivate *priv;
1956 GList *siblings, *l;
1958 g_return_val_if_fail (GTK_IS_CELL_AREA (area), FALSE);
1959 g_return_val_if_fail (GTK_IS_CELL_RENDERER (renderer), FALSE);
1960 g_return_val_if_fail (GTK_IS_CELL_RENDERER (sibling), FALSE);
1964 siblings = g_hash_table_lookup (priv->focus_siblings, renderer);
1966 for (l = siblings; l; l = l->next)
1968 GtkCellRenderer *a_sibling = l->data;
1970 if (a_sibling == sibling)
1978 gtk_cell_area_get_focus_siblings (GtkCellArea *area,
1979 GtkCellRenderer *renderer)
1981 GtkCellAreaPrivate *priv;
1983 g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL);
1984 g_return_val_if_fail (GTK_IS_CELL_RENDERER (renderer), NULL);
1988 return g_hash_table_lookup (priv->focus_siblings, renderer);
1991 /*************************************************************
1992 * API: Cell Activation/Editing *
1993 *************************************************************/
1995 gtk_cell_area_editing_started (GtkCellArea *area,
1996 GtkCellRenderer *renderer,
1997 GtkCellEditable *editable)
1999 g_signal_emit (area, cell_area_signals[SIGNAL_EDITING_STARTED], 0,
2000 renderer, editable, area->priv->current_path);
2004 gtk_cell_area_editing_canceled (GtkCellArea *area,
2005 GtkCellRenderer *renderer)
2007 g_signal_emit (area, cell_area_signals[SIGNAL_EDITING_CANCELED], 0, renderer);
2011 gtk_cell_area_editing_done (GtkCellArea *area,
2012 GtkCellRenderer *renderer,
2013 GtkCellEditable *editable)
2015 g_signal_emit (area, cell_area_signals[SIGNAL_EDITING_DONE], 0, renderer, editable);
2019 gtk_cell_area_remove_editable (GtkCellArea *area,
2020 GtkCellRenderer *renderer,
2021 GtkCellEditable *editable)
2023 g_signal_emit (area, cell_area_signals[SIGNAL_REMOVE_EDITABLE], 0, renderer, editable);
2027 cell_area_editing_done_cb (GtkCellEditable *editable,
2030 GtkCellAreaPrivate *priv = area->priv;
2032 g_assert (priv->edit_widget == editable);
2033 g_assert (priv->edited_cell != NULL);
2035 gtk_cell_area_editing_done (area, priv->edited_cell, priv->edit_widget);
2039 cell_area_remove_widget_cb (GtkCellEditable *editable,
2042 GtkCellAreaPrivate *priv = area->priv;
2044 g_assert (priv->edit_widget == editable);
2045 g_assert (priv->edited_cell != NULL);
2047 gtk_cell_area_remove_editable (area, priv->edited_cell, priv->edit_widget);
2049 /* Now that we're done with editing the widget and it can be removed,
2050 * remove our references to the widget and disconnect handlers */
2051 gtk_cell_area_set_edited_cell (area, NULL);
2052 gtk_cell_area_set_edit_widget (area, NULL);
2056 gtk_cell_area_set_edited_cell (GtkCellArea *area,
2057 GtkCellRenderer *renderer)
2059 GtkCellAreaPrivate *priv;
2061 g_return_if_fail (GTK_IS_CELL_AREA (area));
2062 g_return_if_fail (renderer == NULL || GTK_IS_CELL_RENDERER (renderer));
2066 if (priv->edited_cell != renderer)
2068 if (priv->edited_cell)
2069 g_object_unref (priv->edited_cell);
2071 priv->edited_cell = renderer;
2073 if (priv->edited_cell)
2074 g_object_ref (priv->edited_cell);
2076 g_object_notify (G_OBJECT (area), "edited-cell");
2081 gtk_cell_area_get_edited_cell (GtkCellArea *area)
2083 GtkCellAreaPrivate *priv;
2085 g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL);
2089 return priv->edited_cell;
2093 gtk_cell_area_set_edit_widget (GtkCellArea *area,
2094 GtkCellEditable *editable)
2096 GtkCellAreaPrivate *priv;
2098 g_return_if_fail (GTK_IS_CELL_AREA (area));
2099 g_return_if_fail (editable == NULL || GTK_IS_CELL_EDITABLE (editable));
2103 if (priv->edit_widget != editable)
2105 if (priv->edit_widget)
2107 g_signal_handler_disconnect (priv->edit_widget, priv->editing_done_id);
2108 g_signal_handler_disconnect (priv->edit_widget, priv->remove_widget_id);
2110 g_object_unref (priv->edit_widget);
2113 priv->edit_widget = editable;
2115 if (priv->edit_widget)
2117 priv->editing_done_id =
2118 g_signal_connect (priv->edit_widget, "editing-done",
2119 G_CALLBACK (cell_area_editing_done_cb), area);
2120 priv->remove_widget_id =
2121 g_signal_connect (priv->edit_widget, "remove-widget",
2122 G_CALLBACK (cell_area_remove_widget_cb), area);
2124 g_object_ref (priv->edit_widget);
2127 g_object_notify (G_OBJECT (area), "edit-widget");
2132 gtk_cell_area_get_edit_widget (GtkCellArea *area)
2134 GtkCellAreaPrivate *priv;
2136 g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL);
2140 return priv->edit_widget;
2144 gtk_cell_area_activate_cell (GtkCellArea *area,
2146 GtkCellRenderer *renderer,
2148 const GdkRectangle *cell_area,
2149 GtkCellRendererState flags)
2151 GtkCellRendererMode mode;
2152 GdkRectangle inner_area;
2153 GtkCellAreaPrivate *priv;
2155 g_return_val_if_fail (GTK_IS_CELL_AREA (area), FALSE);
2156 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
2157 g_return_val_if_fail (GTK_IS_CELL_RENDERER (renderer), FALSE);
2158 g_return_val_if_fail (cell_area != NULL, FALSE);
2162 /* Remove margins from the background area to produce the cell area.
2164 * XXX Maybe have to do some rtl mode treatment here...
2166 gtk_cell_area_inner_cell_area (area, cell_area, &inner_area);
2168 g_object_get (renderer, "mode", &mode, NULL);
2170 if (mode == GTK_CELL_RENDERER_MODE_ACTIVATABLE)
2172 if (gtk_cell_renderer_activate (renderer,
2180 else if (mode == GTK_CELL_RENDERER_MODE_EDITABLE)
2182 GtkCellEditable *editable_widget;
2185 gtk_cell_renderer_start_editing (renderer,
2192 if (editable_widget != NULL)
2194 g_return_val_if_fail (GTK_IS_CELL_EDITABLE (editable_widget), FALSE);
2196 gtk_cell_area_set_edited_cell (area, renderer);
2197 gtk_cell_area_set_edit_widget (area, editable_widget);
2199 /* Signal that editing started so that callers can get
2200 * a handle on the editable_widget */
2201 gtk_cell_area_editing_started (area, priv->focus_cell, editable_widget);
2211 gtk_cell_area_stop_editing (GtkCellArea *area,
2214 GtkCellAreaPrivate *priv;
2216 g_return_if_fail (GTK_IS_CELL_AREA (area));
2220 if (priv->edited_cell)
2222 /* Stop editing of the cell renderer */
2223 gtk_cell_renderer_stop_editing (priv->edited_cell, canceled);
2225 /* Signal that editing has been canceled */
2227 gtk_cell_area_editing_canceled (area, priv->edited_cell);
2229 /* Remove any references to the editable widget */
2230 gtk_cell_area_set_edited_cell (area, NULL);
2231 gtk_cell_area_set_edit_widget (area, NULL);
2235 /*************************************************************
2237 *************************************************************/
2239 gtk_cell_area_get_cell_margin_left (GtkCellArea *area)
2241 g_return_val_if_fail (GTK_IS_CELL_AREA (area), 0);
2243 return area->priv->cell_border.left;
2247 gtk_cell_area_set_cell_margin_left (GtkCellArea *area,
2250 GtkCellAreaPrivate *priv;
2252 g_return_if_fail (GTK_IS_CELL_AREA (area));
2256 if (priv->cell_border.left != margin)
2258 priv->cell_border.left = margin;
2260 g_object_notify (G_OBJECT (area), "margin-left");
2265 gtk_cell_area_get_cell_margin_right (GtkCellArea *area)
2267 g_return_val_if_fail (GTK_IS_CELL_AREA (area), 0);
2269 return area->priv->cell_border.right;
2273 gtk_cell_area_set_cell_margin_right (GtkCellArea *area,
2276 GtkCellAreaPrivate *priv;
2278 g_return_if_fail (GTK_IS_CELL_AREA (area));
2282 if (priv->cell_border.right != margin)
2284 priv->cell_border.right = margin;
2286 g_object_notify (G_OBJECT (area), "margin-right");
2291 gtk_cell_area_get_cell_margin_top (GtkCellArea *area)
2293 g_return_val_if_fail (GTK_IS_CELL_AREA (area), 0);
2295 return area->priv->cell_border.top;
2299 gtk_cell_area_set_cell_margin_top (GtkCellArea *area,
2302 GtkCellAreaPrivate *priv;
2304 g_return_if_fail (GTK_IS_CELL_AREA (area));
2308 if (priv->cell_border.top != margin)
2310 priv->cell_border.top = margin;
2312 g_object_notify (G_OBJECT (area), "margin-top");
2317 gtk_cell_area_get_cell_margin_bottom (GtkCellArea *area)
2319 g_return_val_if_fail (GTK_IS_CELL_AREA (area), 0);
2321 return area->priv->cell_border.bottom;
2325 gtk_cell_area_set_cell_margin_bottom (GtkCellArea *area,
2328 GtkCellAreaPrivate *priv;
2330 g_return_if_fail (GTK_IS_CELL_AREA (area));
2334 if (priv->cell_border.bottom != margin)
2336 priv->cell_border.bottom = margin;
2338 g_object_notify (G_OBJECT (area), "margin-bottom");
2343 gtk_cell_area_inner_cell_area (GtkCellArea *area,
2344 const GdkRectangle *background_area,
2345 GdkRectangle *cell_area)
2347 GtkCellAreaPrivate *priv;
2349 g_return_if_fail (GTK_IS_CELL_AREA (area));
2350 g_return_if_fail (background_area != NULL);
2351 g_return_if_fail (cell_area != NULL);
2355 *cell_area = *background_area;
2357 cell_area->x += priv->cell_border.left;
2358 cell_area->width -= (priv->cell_border.left + priv->cell_border.right);
2359 cell_area->y += priv->cell_border.top;
2360 cell_area->height -= (priv->cell_border.top + priv->cell_border.bottom);
2364 gtk_cell_area_request_renderer (GtkCellArea *area,
2365 GtkCellRenderer *renderer,
2366 GtkOrientation orientation,
2372 GtkCellAreaPrivate *priv;
2374 g_return_if_fail (GTK_IS_CELL_AREA (area));
2375 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
2376 g_return_if_fail (GTK_IS_WIDGET (widget));
2377 g_return_if_fail (minimum_size != NULL);
2378 g_return_if_fail (natural_size != NULL);
2382 if (orientation == GTK_ORIENTATION_HORIZONTAL)
2385 gtk_cell_renderer_get_preferred_width (renderer, widget, minimum_size, natural_size);
2388 for_size = MAX (0, for_size - (priv->cell_border.top + priv->cell_border.bottom));
2390 gtk_cell_renderer_get_preferred_width_for_height (renderer, widget, for_size,
2391 minimum_size, natural_size);
2394 *minimum_size += (priv->cell_border.left + priv->cell_border.right);
2395 *natural_size += (priv->cell_border.left + priv->cell_border.right);
2397 else /* GTK_ORIENTATION_VERTICAL */
2400 gtk_cell_renderer_get_preferred_height (renderer, widget, minimum_size, natural_size);
2403 for_size = MAX (0, for_size - (priv->cell_border.left + priv->cell_border.right));
2405 gtk_cell_renderer_get_preferred_height_for_width (renderer, widget, for_size,
2406 minimum_size, natural_size);
2409 *minimum_size += (priv->cell_border.top + priv->cell_border.bottom);
2410 *natural_size += (priv->cell_border.top + priv->cell_border.bottom);