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 GdkRectangle *cell_area);
137 static void gtk_cell_area_editing_canceled (GtkCellArea *area,
138 GtkCellRenderer *renderer);
139 static void gtk_cell_area_editing_done (GtkCellArea *area,
140 GtkCellRenderer *renderer,
141 GtkCellEditable *editable);
142 static void gtk_cell_area_remove_editable (GtkCellArea *area,
143 GtkCellRenderer *renderer,
144 GtkCellEditable *editable);
147 /* Struct to pass data along while looping over
148 * cell renderers to apply attributes
154 gboolean is_expander;
155 gboolean is_expanded;
158 struct _GtkCellAreaPrivate
160 /* The GtkCellArea bookkeeps any connected
161 * attributes in this hash table.
163 GHashTable *cell_info;
165 /* The cell border decides how much space to reserve
166 * around each cell for the background_area
168 GtkBorder cell_border;
170 /* Current path is saved as a side-effect
171 * of gtk_cell_area_apply_attributes() */
174 /* Current cell being edited and editable widget used */
175 GtkCellEditable *edit_widget;
176 GtkCellRenderer *edited_cell;
178 /* Signal connections to the editable widget */
179 gulong editing_done_id;
180 gulong remove_widget_id;
182 /* Currently focused cell */
183 GtkCellRenderer *focus_cell;
185 /* Tracking which cells are focus siblings of focusable cells */
186 GHashTable *focus_siblings;
188 /* Detail string to pass to gtk_paint_*() functions */
194 PROP_CELL_MARGIN_LEFT,
195 PROP_CELL_MARGIN_RIGHT,
196 PROP_CELL_MARGIN_TOP,
197 PROP_CELL_MARGIN_BOTTOM,
204 SIGNAL_EDITING_STARTED,
205 SIGNAL_EDITING_CANCELED,
207 SIGNAL_REMOVE_EDITABLE,
208 SIGNAL_FOCUS_CHANGED,
212 /* Keep the paramspec pool internal, no need to deliver notifications
213 * on cells. at least no percieved need for now */
214 static GParamSpecPool *cell_property_pool = NULL;
215 static guint cell_area_signals[LAST_SIGNAL] = { 0 };
217 #define PARAM_SPEC_PARAM_ID(pspec) ((pspec)->param_id)
218 #define PARAM_SPEC_SET_PARAM_ID(pspec, id) ((pspec)->param_id = (id))
221 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GtkCellArea, gtk_cell_area, G_TYPE_INITIALLY_UNOWNED,
222 G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
223 gtk_cell_area_cell_layout_init));
226 gtk_cell_area_init (GtkCellArea *area)
228 GtkCellAreaPrivate *priv;
230 area->priv = G_TYPE_INSTANCE_GET_PRIVATE (area,
235 priv->cell_info = g_hash_table_new_full (g_direct_hash,
238 (GDestroyNotify)cell_info_free);
240 priv->focus_siblings = g_hash_table_new_full (g_direct_hash,
243 (GDestroyNotify)g_list_free);
245 priv->cell_border.left = 0;
246 priv->cell_border.right = 0;
247 priv->cell_border.top = 0;
248 priv->cell_border.bottom = 0;
250 priv->focus_cell = NULL;
251 priv->edited_cell = NULL;
252 priv->edit_widget = NULL;
254 priv->editing_done_id = 0;
255 priv->remove_widget_id = 0;
259 gtk_cell_area_class_init (GtkCellAreaClass *class)
261 GObjectClass *object_class = G_OBJECT_CLASS (class);
264 object_class->dispose = gtk_cell_area_dispose;
265 object_class->finalize = gtk_cell_area_finalize;
266 object_class->get_property = gtk_cell_area_get_property;
267 object_class->set_property = gtk_cell_area_set_property;
271 class->remove = NULL;
272 class->forall = NULL;
273 class->event = gtk_cell_area_real_event;
274 class->render = NULL;
277 class->create_iter = NULL;
278 class->get_request_mode = NULL;
279 class->get_preferred_width = NULL;
280 class->get_preferred_height = NULL;
281 class->get_preferred_height_for_width = gtk_cell_area_real_get_preferred_height_for_width;
282 class->get_preferred_width_for_height = gtk_cell_area_real_get_preferred_width_for_height;
285 class->can_focus = gtk_cell_area_real_can_focus;
287 class->activate = gtk_cell_area_real_activate;
290 cell_area_signals[SIGNAL_EDITING_STARTED] =
291 g_signal_new (I_("editing-started"),
292 G_OBJECT_CLASS_TYPE (object_class),
294 0, /* No class closure here */
296 _gtk_marshal_VOID__OBJECT_OBJECT_BOXED_STRING,
298 GTK_TYPE_CELL_RENDERER,
299 GTK_TYPE_CELL_EDITABLE,
303 cell_area_signals[SIGNAL_EDITING_CANCELED] =
304 g_signal_new (I_("editing-canceled"),
305 G_OBJECT_CLASS_TYPE (object_class),
307 0, /* No class closure here */
309 _gtk_marshal_VOID__OBJECT,
311 GTK_TYPE_CELL_RENDERER);
313 cell_area_signals[SIGNAL_EDITING_DONE] =
314 g_signal_new (I_("editing-done"),
315 G_OBJECT_CLASS_TYPE (object_class),
317 0, /* No class closure here */
319 _gtk_marshal_VOID__OBJECT_OBJECT,
321 GTK_TYPE_CELL_RENDERER,
322 GTK_TYPE_CELL_EDITABLE);
324 cell_area_signals[SIGNAL_REMOVE_EDITABLE] =
325 g_signal_new (I_("remove-editable"),
326 G_OBJECT_CLASS_TYPE (object_class),
328 0, /* No class closure here */
330 _gtk_marshal_VOID__OBJECT_OBJECT,
332 GTK_TYPE_CELL_RENDERER,
333 GTK_TYPE_CELL_EDITABLE);
335 cell_area_signals[SIGNAL_FOCUS_CHANGED] =
336 g_signal_new (I_("focus-changed"),
337 G_OBJECT_CLASS_TYPE (object_class),
339 0, /* No class closure here */
341 _gtk_marshal_VOID__OBJECT_STRING,
343 GTK_TYPE_CELL_RENDERER,
347 g_object_class_install_property (object_class,
348 PROP_CELL_MARGIN_LEFT,
351 P_("Margin on Left"),
352 P_("Pixels of extra space on the left side of each cell"),
356 GTK_PARAM_READWRITE));
358 g_object_class_install_property (object_class,
359 PROP_CELL_MARGIN_RIGHT,
361 ("cell-margin-right",
362 P_("Margin on Right"),
363 P_("Pixels of extra space on the right side of each cell"),
367 GTK_PARAM_READWRITE));
369 g_object_class_install_property (object_class,
370 PROP_CELL_MARGIN_TOP,
374 P_("Pixels of extra space on the top side of each cell"),
378 GTK_PARAM_READWRITE));
380 g_object_class_install_property (object_class,
381 PROP_CELL_MARGIN_BOTTOM,
383 ("cell-margin-bottom",
384 P_("Margin on Bottom"),
385 P_("Pixels of extra space on the bottom side of each cell"),
389 GTK_PARAM_READWRITE));
391 g_object_class_install_property (object_class,
396 P_("The cell which currently has focus"),
397 GTK_TYPE_CELL_RENDERER,
398 GTK_PARAM_READWRITE));
400 g_object_class_install_property (object_class,
405 P_("The cell which is currently being edited"),
406 GTK_TYPE_CELL_RENDERER,
407 GTK_PARAM_READWRITE));
409 g_object_class_install_property (object_class,
414 P_("The widget currently editing the edited cell"),
415 GTK_TYPE_CELL_RENDERER,
416 GTK_PARAM_READWRITE));
418 /* Pool for Cell Properties */
419 if (!cell_property_pool)
420 cell_property_pool = g_param_spec_pool_new (FALSE);
422 g_type_class_add_private (object_class, sizeof (GtkCellAreaPrivate));
425 /*************************************************************
427 *************************************************************/
429 cell_info_new (GtkCellLayoutDataFunc func,
431 GDestroyNotify destroy)
433 CellInfo *info = g_slice_new (CellInfo);
435 info->attributes = NULL;
438 info->destroy = destroy;
444 cell_info_free (CellInfo *info)
447 info->destroy (info->data);
449 g_slist_foreach (info->attributes, (GFunc)cell_attribute_free, NULL);
450 g_slist_free (info->attributes);
452 g_slice_free (CellInfo, info);
455 static CellAttribute *
456 cell_attribute_new (GtkCellRenderer *renderer,
457 const gchar *attribute,
462 /* Check if the attribute really exists and point to
463 * the property string installed on the cell renderer
464 * class (dont dup the string)
466 pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (renderer), attribute);
470 CellAttribute *cell_attribute = g_slice_new (CellAttribute);
472 cell_attribute->attribute = pspec->name;
473 cell_attribute->column = column;
475 return cell_attribute;
482 cell_attribute_free (CellAttribute *attribute)
484 g_slice_free (CellAttribute, attribute);
487 /* GCompareFunc for g_slist_find_custom() */
489 cell_attribute_find (CellAttribute *cell_attribute,
490 const gchar *attribute)
492 return g_strcmp0 (cell_attribute->attribute, attribute);
495 /*************************************************************
497 *************************************************************/
499 gtk_cell_area_finalize (GObject *object)
501 GtkCellArea *area = GTK_CELL_AREA (object);
502 GtkCellAreaPrivate *priv = area->priv;
504 /* All cell renderers should already be removed at this point,
505 * just kill our (empty) hash tables here.
507 g_hash_table_destroy (priv->cell_info);
508 g_hash_table_destroy (priv->focus_siblings);
510 g_free (priv->current_path);
512 G_OBJECT_CLASS (gtk_cell_area_parent_class)->finalize (object);
517 gtk_cell_area_dispose (GObject *object)
519 /* This removes every cell renderer that may be added to the GtkCellArea,
520 * subclasses should be breaking references to the GtkCellRenderers
523 gtk_cell_layout_clear (GTK_CELL_LAYOUT (object));
525 /* Remove any ref to a focused/edited cell */
526 gtk_cell_area_set_focus_cell (GTK_CELL_AREA (object), NULL);
527 gtk_cell_area_set_edited_cell (GTK_CELL_AREA (object), NULL);
528 gtk_cell_area_set_edit_widget (GTK_CELL_AREA (object), NULL);
530 G_OBJECT_CLASS (gtk_cell_area_parent_class)->dispose (object);
534 gtk_cell_area_set_property (GObject *object,
539 GtkCellArea *area = GTK_CELL_AREA (object);
543 case PROP_CELL_MARGIN_LEFT:
544 gtk_cell_area_set_cell_margin_left (area, g_value_get_int (value));
546 case PROP_CELL_MARGIN_RIGHT:
547 gtk_cell_area_set_cell_margin_right (area, g_value_get_int (value));
549 case PROP_CELL_MARGIN_TOP:
550 gtk_cell_area_set_cell_margin_top (area, g_value_get_int (value));
552 case PROP_CELL_MARGIN_BOTTOM:
553 gtk_cell_area_set_cell_margin_bottom (area, g_value_get_int (value));
555 case PROP_FOCUS_CELL:
556 gtk_cell_area_set_focus_cell (area, (GtkCellRenderer *)g_value_get_object (value));
558 case PROP_EDITED_CELL:
559 gtk_cell_area_set_edited_cell (area, (GtkCellRenderer *)g_value_get_object (value));
561 case PROP_EDIT_WIDGET:
562 gtk_cell_area_set_edit_widget (area, (GtkCellEditable *)g_value_get_object (value));
565 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
571 gtk_cell_area_get_property (GObject *object,
576 GtkCellArea *area = GTK_CELL_AREA (object);
577 GtkCellAreaPrivate *priv = area->priv;
581 case PROP_CELL_MARGIN_LEFT:
582 g_value_set_int (value, priv->cell_border.left);
584 case PROP_CELL_MARGIN_RIGHT:
585 g_value_set_int (value, priv->cell_border.right);
587 case PROP_CELL_MARGIN_TOP:
588 g_value_set_int (value, priv->cell_border.top);
590 case PROP_CELL_MARGIN_BOTTOM:
591 g_value_set_int (value, priv->cell_border.bottom);
593 case PROP_FOCUS_CELL:
594 g_value_set_object (value, priv->focus_cell);
596 case PROP_EDITED_CELL:
597 g_value_set_object (value, priv->edited_cell);
599 case PROP_EDIT_WIDGET:
600 g_value_set_object (value, priv->edit_widget);
603 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
608 /*************************************************************
610 *************************************************************/
612 gtk_cell_area_real_event (GtkCellArea *area,
613 GtkCellAreaIter *iter,
616 const GdkRectangle *cell_area,
617 GtkCellRendererState flags)
619 GtkCellAreaPrivate *priv = area->priv;
621 if (event->type == GDK_KEY_PRESS && (flags & GTK_CELL_RENDERER_FOCUSED) != 0)
623 GdkEventKey *key_event = (GdkEventKey *)event;
625 /* Cancel any edits in progress */
626 if (priv->edited_cell && (key_event->keyval == GDK_KEY_Escape))
628 gtk_cell_area_stop_editing (area, TRUE);
637 gtk_cell_area_real_get_preferred_height_for_width (GtkCellArea *area,
638 GtkCellAreaIter *iter,
641 gint *minimum_height,
642 gint *natural_height)
644 /* If the area doesnt do height-for-width, fallback on base preferred height */
645 GTK_CELL_AREA_GET_CLASS (area)->get_preferred_width (area, iter, widget, minimum_height, natural_height);
649 gtk_cell_area_real_get_preferred_width_for_height (GtkCellArea *area,
650 GtkCellAreaIter *iter,
656 /* If the area doesnt do width-for-height, fallback on base preferred width */
657 GTK_CELL_AREA_GET_CLASS (area)->get_preferred_width (area, iter, widget, minimum_width, natural_width);
661 get_can_focus (GtkCellRenderer *renderer,
665 if (gtk_cell_renderer_can_focus (renderer))
670 gtk_cell_area_real_can_focus (GtkCellArea *area)
672 gboolean can_focus = FALSE;
674 /* Checks if any renderer can focus for the currently applied
677 * Subclasses can override this in the case that they are also
678 * rendering widgets as well as renderers.
680 gtk_cell_area_forall (area, (GtkCellCallback)get_can_focus, &can_focus);
686 gtk_cell_area_real_activate (GtkCellArea *area,
687 GtkCellAreaIter *iter,
689 const GdkRectangle *cell_area,
690 GtkCellRendererState flags)
692 GtkCellAreaPrivate *priv = area->priv;
693 GdkRectangle background_area;
695 if (priv->focus_cell)
697 /* Get the allocation of the focused cell.
699 gtk_cell_area_get_cell_allocation (area, iter, widget, priv->focus_cell,
700 cell_area, &background_area);
702 /* Activate or Edit the currently focused cell
704 * Currently just not sending an event, renderers afaics dont use
705 * the event argument anyway, worst case is we can synthesize one.
707 if (gtk_cell_area_activate_cell (area, widget, priv->focus_cell, NULL,
708 &background_area, flags))
715 /*************************************************************
716 * GtkCellLayoutIface *
717 *************************************************************/
719 gtk_cell_area_cell_layout_init (GtkCellLayoutIface *iface)
721 iface->pack_start = gtk_cell_area_pack_default;
722 iface->pack_end = gtk_cell_area_pack_default;
723 iface->clear = gtk_cell_area_clear;
724 iface->add_attribute = gtk_cell_area_add_attribute;
725 iface->set_cell_data_func = gtk_cell_area_set_cell_data_func;
726 iface->clear_attributes = gtk_cell_area_clear_attributes;
727 iface->reorder = gtk_cell_area_reorder;
728 iface->get_cells = gtk_cell_area_get_cells;
732 gtk_cell_area_pack_default (GtkCellLayout *cell_layout,
733 GtkCellRenderer *renderer,
736 gtk_cell_area_add (GTK_CELL_AREA (cell_layout), renderer);
740 gtk_cell_area_clear (GtkCellLayout *cell_layout)
742 GtkCellArea *area = GTK_CELL_AREA (cell_layout);
744 gtk_cell_layout_get_cells (cell_layout);
746 for (l = cells; l; l = l->next)
748 GtkCellRenderer *renderer = l->data;
749 gtk_cell_area_remove (area, renderer);
756 gtk_cell_area_add_attribute (GtkCellLayout *cell_layout,
757 GtkCellRenderer *renderer,
758 const gchar *attribute,
761 gtk_cell_area_attribute_connect (GTK_CELL_AREA (cell_layout),
762 renderer, attribute, column);
766 gtk_cell_area_set_cell_data_func (GtkCellLayout *cell_layout,
767 GtkCellRenderer *renderer,
768 GtkCellLayoutDataFunc func,
770 GDestroyNotify destroy)
772 GtkCellArea *area = GTK_CELL_AREA (cell_layout);
773 GtkCellAreaPrivate *priv = area->priv;
776 info = g_hash_table_lookup (priv->cell_info, renderer);
780 if (info->destroy && info->data)
781 info->destroy (info->data);
786 info->data = func_data;
787 info->destroy = destroy;
793 info->destroy = NULL;
798 info = cell_info_new (func, func_data, destroy);
800 g_hash_table_insert (priv->cell_info, renderer, info);
805 gtk_cell_area_clear_attributes (GtkCellLayout *cell_layout,
806 GtkCellRenderer *renderer)
808 GtkCellArea *area = GTK_CELL_AREA (cell_layout);
809 GtkCellAreaPrivate *priv = area->priv;
812 info = g_hash_table_lookup (priv->cell_info, renderer);
816 g_slist_foreach (info->attributes, (GFunc)cell_attribute_free, NULL);
817 g_slist_free (info->attributes);
819 info->attributes = NULL;
824 gtk_cell_area_reorder (GtkCellLayout *cell_layout,
825 GtkCellRenderer *cell,
828 g_warning ("GtkCellLayout::reorder not implemented for `%s'",
829 g_type_name (G_TYPE_FROM_INSTANCE (cell_layout)));
833 accum_cells (GtkCellRenderer *renderer,
836 *accum = g_list_prepend (*accum, renderer);
840 gtk_cell_area_get_cells (GtkCellLayout *cell_layout)
844 gtk_cell_area_forall (GTK_CELL_AREA (cell_layout),
845 (GtkCellCallback)accum_cells,
848 return g_list_reverse (cells);
852 /*************************************************************
854 *************************************************************/
858 * @area: a #GtkCellArea
859 * @renderer: the #GtkCellRenderer to add to @area
861 * Adds @renderer to @area with the default child cell properties.
864 gtk_cell_area_add (GtkCellArea *area,
865 GtkCellRenderer *renderer)
867 GtkCellAreaClass *class;
869 g_return_if_fail (GTK_IS_CELL_AREA (area));
870 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
872 class = GTK_CELL_AREA_GET_CLASS (area);
875 class->add (area, renderer);
877 g_warning ("GtkCellAreaClass::add not implemented for `%s'",
878 g_type_name (G_TYPE_FROM_INSTANCE (area)));
882 * gtk_cell_area_remove:
883 * @area: a #GtkCellArea
884 * @renderer: the #GtkCellRenderer to add to @area
886 * Removes @renderer from @area.
889 gtk_cell_area_remove (GtkCellArea *area,
890 GtkCellRenderer *renderer)
892 GtkCellAreaClass *class;
893 GtkCellAreaPrivate *priv;
894 GList *renderers, *l;
896 g_return_if_fail (GTK_IS_CELL_AREA (area));
897 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
899 class = GTK_CELL_AREA_GET_CLASS (area);
902 /* Remove any custom attributes and custom cell data func here first */
903 g_hash_table_remove (priv->cell_info, renderer);
905 /* Remove focus siblings of this renderer */
906 g_hash_table_remove (priv->focus_siblings, renderer);
908 /* Remove this renderer from any focus renderer's sibling list */
909 renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (area));
911 for (l = renderers; l; l = l->next)
913 GtkCellRenderer *focus_renderer = l->data;
915 if (gtk_cell_area_is_focus_sibling (area, focus_renderer, renderer))
917 gtk_cell_area_remove_focus_sibling (area, focus_renderer, renderer);
922 g_list_free (renderers);
925 class->remove (area, renderer);
927 g_warning ("GtkCellAreaClass::remove not implemented for `%s'",
928 g_type_name (G_TYPE_FROM_INSTANCE (area)));
932 get_has_renderer (GtkCellRenderer *renderer,
933 HasRendererCheck *check)
935 if (renderer == check->renderer)
936 check->has_renderer = TRUE;
940 gtk_cell_area_has_renderer (GtkCellArea *area,
941 GtkCellRenderer *renderer)
943 HasRendererCheck check = { renderer, FALSE };
945 g_return_val_if_fail (GTK_IS_CELL_AREA (area), FALSE);
946 g_return_val_if_fail (GTK_IS_CELL_RENDERER (renderer), FALSE);
948 gtk_cell_area_forall (area, (GtkCellCallback)get_has_renderer, &check);
950 return check.has_renderer;
954 * gtk_cell_area_forall
955 * @area: a #GtkCellArea
956 * @callback: the #GtkCellCallback to call
957 * @callback_data: user provided data pointer
959 * Calls @callback for every #GtkCellRenderer in @area.
962 gtk_cell_area_forall (GtkCellArea *area,
963 GtkCellCallback callback,
964 gpointer callback_data)
966 GtkCellAreaClass *class;
968 g_return_if_fail (GTK_IS_CELL_AREA (area));
969 g_return_if_fail (callback != NULL);
971 class = GTK_CELL_AREA_GET_CLASS (area);
974 class->forall (area, callback, callback_data);
976 g_warning ("GtkCellAreaClass::forall not implemented for `%s'",
977 g_type_name (G_TYPE_FROM_INSTANCE (area)));
981 * gtk_cell_area_get_cell_allocation:
982 * @area: a #GtkCellArea
983 * @iter: the #GtkCellAreaIter used to hold sizes for @area.
984 * @widget: the #GtkWidget that @area is rendering on
985 * @renderer: the #GtkCellRenderer to get the allocation for
986 * @cell_area: the whole allocated area for @area in @widget
988 * @allocation: where to store the allocation for @renderer
990 * Derives the allocation of @renderer inside @area if @area
991 * were to be renderered in @cell_area.
994 gtk_cell_area_get_cell_allocation (GtkCellArea *area,
995 GtkCellAreaIter *iter,
997 GtkCellRenderer *renderer,
998 const GdkRectangle *cell_area,
999 GdkRectangle *allocation)
1001 GtkCellAreaClass *class;
1003 g_return_if_fail (GTK_IS_CELL_AREA (area));
1004 g_return_if_fail (GTK_IS_CELL_AREA_ITER (iter));
1005 g_return_if_fail (GTK_IS_WIDGET (widget));
1006 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1007 g_return_if_fail (cell_area != NULL);
1008 g_return_if_fail (allocation != NULL);
1010 class = GTK_CELL_AREA_GET_CLASS (area);
1012 if (class->get_cell_allocation)
1013 class->get_cell_allocation (area, iter, widget, renderer, cell_area, allocation);
1015 g_warning ("GtkCellAreaClass::get_cell_allocation not implemented for `%s'",
1016 g_type_name (G_TYPE_FROM_INSTANCE (area)));
1020 gtk_cell_area_event (GtkCellArea *area,
1021 GtkCellAreaIter *iter,
1024 const GdkRectangle *cell_area,
1025 GtkCellRendererState flags)
1027 GtkCellAreaClass *class;
1029 g_return_val_if_fail (GTK_IS_CELL_AREA (area), 0);
1030 g_return_val_if_fail (GTK_IS_CELL_AREA_ITER (iter), 0);
1031 g_return_val_if_fail (GTK_IS_WIDGET (widget), 0);
1032 g_return_val_if_fail (event != NULL, 0);
1033 g_return_val_if_fail (cell_area != NULL, 0);
1035 class = GTK_CELL_AREA_GET_CLASS (area);
1038 return class->event (area, iter, widget, event, cell_area, flags);
1040 g_warning ("GtkCellAreaClass::event not implemented for `%s'",
1041 g_type_name (G_TYPE_FROM_INSTANCE (area)));
1046 gtk_cell_area_render (GtkCellArea *area,
1047 GtkCellAreaIter *iter,
1050 const GdkRectangle *background_area,
1051 const GdkRectangle *cell_area,
1052 GtkCellRendererState flags,
1053 gboolean paint_focus)
1055 GtkCellAreaClass *class;
1057 g_return_if_fail (GTK_IS_CELL_AREA (area));
1058 g_return_if_fail (GTK_IS_CELL_AREA_ITER (iter));
1059 g_return_if_fail (GTK_IS_WIDGET (widget));
1060 g_return_if_fail (cr != NULL);
1061 g_return_if_fail (background_area != NULL);
1062 g_return_if_fail (cell_area != NULL);
1064 class = GTK_CELL_AREA_GET_CLASS (area);
1067 class->render (area, iter, widget, cr, background_area, cell_area, flags, paint_focus);
1069 g_warning ("GtkCellAreaClass::render not implemented for `%s'",
1070 g_type_name (G_TYPE_FROM_INSTANCE (area)));
1074 gtk_cell_area_set_style_detail (GtkCellArea *area,
1075 const gchar *detail)
1077 GtkCellAreaPrivate *priv;
1079 g_return_if_fail (GTK_IS_CELL_AREA (area));
1083 if (g_strcmp0 (priv->style_detail, detail) != 0)
1085 g_free (priv->style_detail);
1086 priv->style_detail = g_strdup (detail);
1090 G_CONST_RETURN gchar *
1091 gtk_cell_area_get_style_detail (GtkCellArea *area)
1093 GtkCellAreaPrivate *priv;
1095 g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL);
1099 return priv->style_detail;
1102 /*************************************************************
1104 *************************************************************/
1106 gtk_cell_area_create_iter (GtkCellArea *area)
1108 GtkCellAreaClass *class;
1110 g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL);
1112 class = GTK_CELL_AREA_GET_CLASS (area);
1114 if (class->create_iter)
1115 return class->create_iter (area);
1117 g_warning ("GtkCellAreaClass::create_iter not implemented for `%s'",
1118 g_type_name (G_TYPE_FROM_INSTANCE (area)));
1125 gtk_cell_area_get_request_mode (GtkCellArea *area)
1127 GtkCellAreaClass *class;
1129 g_return_val_if_fail (GTK_IS_CELL_AREA (area),
1130 GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH);
1132 class = GTK_CELL_AREA_GET_CLASS (area);
1134 if (class->get_request_mode)
1135 return class->get_request_mode (area);
1137 g_warning ("GtkCellAreaClass::get_request_mode not implemented for `%s'",
1138 g_type_name (G_TYPE_FROM_INSTANCE (area)));
1140 return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
1144 gtk_cell_area_get_preferred_width (GtkCellArea *area,
1145 GtkCellAreaIter *iter,
1150 GtkCellAreaClass *class;
1152 g_return_if_fail (GTK_IS_CELL_AREA (area));
1153 g_return_if_fail (GTK_IS_WIDGET (widget));
1155 class = GTK_CELL_AREA_GET_CLASS (area);
1157 if (class->get_preferred_width)
1158 class->get_preferred_width (area, iter, widget, minimum_size, natural_size);
1160 g_warning ("GtkCellAreaClass::get_preferred_width not implemented for `%s'",
1161 g_type_name (G_TYPE_FROM_INSTANCE (area)));
1165 gtk_cell_area_get_preferred_height_for_width (GtkCellArea *area,
1166 GtkCellAreaIter *iter,
1169 gint *minimum_height,
1170 gint *natural_height)
1172 GtkCellAreaClass *class;
1174 g_return_if_fail (GTK_IS_CELL_AREA (area));
1175 g_return_if_fail (GTK_IS_WIDGET (widget));
1177 class = GTK_CELL_AREA_GET_CLASS (area);
1178 class->get_preferred_height_for_width (area, iter, widget, width, minimum_height, natural_height);
1182 gtk_cell_area_get_preferred_height (GtkCellArea *area,
1183 GtkCellAreaIter *iter,
1188 GtkCellAreaClass *class;
1190 g_return_if_fail (GTK_IS_CELL_AREA (area));
1191 g_return_if_fail (GTK_IS_WIDGET (widget));
1193 class = GTK_CELL_AREA_GET_CLASS (area);
1195 if (class->get_preferred_height)
1196 class->get_preferred_height (area, iter, widget, minimum_size, natural_size);
1198 g_warning ("GtkCellAreaClass::get_preferred_height not implemented for `%s'",
1199 g_type_name (G_TYPE_FROM_INSTANCE (area)));
1203 gtk_cell_area_get_preferred_width_for_height (GtkCellArea *area,
1204 GtkCellAreaIter *iter,
1207 gint *minimum_width,
1208 gint *natural_width)
1210 GtkCellAreaClass *class;
1212 g_return_if_fail (GTK_IS_CELL_AREA (area));
1213 g_return_if_fail (GTK_IS_WIDGET (widget));
1215 class = GTK_CELL_AREA_GET_CLASS (area);
1216 class->get_preferred_width_for_height (area, iter, widget, height, minimum_width, natural_width);
1219 /*************************************************************
1221 *************************************************************/
1224 * gtk_cell_area_attribute_connect:
1225 * @area: a #GtkCellArea
1226 * @renderer: the #GtkCellRenderer to connect an attribute for
1227 * @attribute: the attribute name
1228 * @column: the #GtkTreeModel column to fetch attribute values from
1230 * Connects an @attribute to apply values from @column for the
1231 * #GtkTreeModel in use.
1234 gtk_cell_area_attribute_connect (GtkCellArea *area,
1235 GtkCellRenderer *renderer,
1236 const gchar *attribute,
1239 GtkCellAreaPrivate *priv;
1241 CellAttribute *cell_attribute;
1243 g_return_if_fail (GTK_IS_CELL_AREA (area));
1244 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1245 g_return_if_fail (attribute != NULL);
1246 g_return_if_fail (gtk_cell_area_has_renderer (area, renderer));
1249 info = g_hash_table_lookup (priv->cell_info, renderer);
1253 info = cell_info_new (NULL, NULL, NULL);
1255 g_hash_table_insert (priv->cell_info, renderer, info);
1261 /* Check we are not adding the same attribute twice */
1262 if ((node = g_slist_find_custom (info->attributes, attribute,
1263 (GCompareFunc)cell_attribute_find)) != NULL)
1265 cell_attribute = node->data;
1267 g_warning ("Cannot connect attribute `%s' for cell renderer class `%s' "
1268 "since `%s' is already attributed to column %d",
1270 g_type_name (G_TYPE_FROM_INSTANCE (area)),
1271 attribute, cell_attribute->column);
1276 cell_attribute = cell_attribute_new (renderer, attribute, column);
1278 if (!cell_attribute)
1280 g_warning ("Cannot connect attribute `%s' for cell renderer class `%s' "
1281 "since attribute does not exist",
1283 g_type_name (G_TYPE_FROM_INSTANCE (area)));
1287 info->attributes = g_slist_prepend (info->attributes, cell_attribute);
1291 * gtk_cell_area_attribute_disconnect:
1292 * @area: a #GtkCellArea
1293 * @renderer: the #GtkCellRenderer to disconnect an attribute for
1294 * @attribute: the attribute name
1296 * Disconnects @attribute for the @renderer in @area so that
1297 * attribute will no longer be updated with values from the
1301 gtk_cell_area_attribute_disconnect (GtkCellArea *area,
1302 GtkCellRenderer *renderer,
1303 const gchar *attribute)
1305 GtkCellAreaPrivate *priv;
1307 CellAttribute *cell_attribute;
1310 g_return_if_fail (GTK_IS_CELL_AREA (area));
1311 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1312 g_return_if_fail (attribute != NULL);
1313 g_return_if_fail (gtk_cell_area_has_renderer (area, renderer));
1316 info = g_hash_table_lookup (priv->cell_info, renderer);
1320 node = g_slist_find_custom (info->attributes, attribute,
1321 (GCompareFunc)cell_attribute_find);
1324 cell_attribute = node->data;
1326 cell_attribute_free (cell_attribute);
1328 info->attributes = g_slist_delete_link (info->attributes, node);
1334 apply_cell_attributes (GtkCellRenderer *renderer,
1336 AttributeData *data)
1338 CellAttribute *attribute;
1340 GValue value = { 0, };
1341 gboolean is_expander;
1342 gboolean is_expanded;
1344 g_object_freeze_notify (G_OBJECT (renderer));
1346 /* Whether a row expands or is presently expanded can only be
1347 * provided by the view (as these states can vary across views
1348 * accessing the same model).
1350 g_object_get (renderer, "is-expander", &is_expander, NULL);
1351 if (is_expander != data->is_expander)
1352 g_object_set (renderer, "is-expander", data->is_expander, NULL);
1354 g_object_get (renderer, "is-expanded", &is_expanded, NULL);
1355 if (is_expanded != data->is_expanded)
1356 g_object_set (renderer, "is-expanded", data->is_expanded, NULL);
1358 /* Apply the attributes directly to the renderer */
1359 for (list = info->attributes; list; list = list->next)
1361 attribute = list->data;
1363 gtk_tree_model_get_value (data->model, data->iter, attribute->column, &value);
1364 g_object_set_property (G_OBJECT (renderer), attribute->attribute, &value);
1365 g_value_unset (&value);
1368 /* Call any GtkCellLayoutDataFunc that may have been set by the user
1371 info->func (GTK_CELL_LAYOUT (data->area), renderer,
1372 data->model, data->iter, info->data);
1374 g_object_thaw_notify (G_OBJECT (renderer));
1378 * gtk_cell_area_apply_attributes
1379 * @area: a #GtkCellArea
1380 * @tree_model: a #GtkTreeModel to pull values from
1381 * @iter: the #GtkTreeIter in @tree_model to apply values for
1382 * @is_expander: whether @iter has children
1383 * @is_expanded: whether @iter is expanded in the view and
1384 * children are visible
1386 * Applies any connected attributes to the renderers in
1387 * @area by pulling the values from @tree_model.
1390 gtk_cell_area_apply_attributes (GtkCellArea *area,
1391 GtkTreeModel *tree_model,
1393 gboolean is_expander,
1394 gboolean is_expanded)
1396 GtkCellAreaPrivate *priv;
1400 g_return_if_fail (GTK_IS_CELL_AREA (area));
1401 g_return_if_fail (GTK_IS_TREE_MODEL (tree_model));
1402 g_return_if_fail (iter != NULL);
1406 /* Feed in data needed to apply to every renderer */
1408 data.model = tree_model;
1410 data.is_expander = is_expander;
1411 data.is_expanded = is_expanded;
1413 /* Go over any cells that have attributes or custom GtkCellLayoutDataFuncs and
1414 * apply the data from the treemodel */
1415 g_hash_table_foreach (priv->cell_info, (GHFunc)apply_cell_attributes, &data);
1417 /* Update the currently applied path */
1418 g_free (priv->current_path);
1419 path = gtk_tree_model_get_path (tree_model, iter);
1420 priv->current_path = gtk_tree_path_to_string (path);
1421 gtk_tree_path_free (path);
1425 * gtk_cell_area_get_current_path_string:
1426 * @area: a #GtkCellArea
1428 * Gets the current #GtkTreePath string for the currently
1429 * applied #GtkTreeIter, this is implicitly updated when
1430 * gtk_cell_area_apply_attributes() is called and can be
1431 * used to interact with renderers from #GtkCellArea
1435 gtk_cell_area_get_current_path_string (GtkCellArea *area)
1437 GtkCellAreaPrivate *priv;
1439 g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL);
1443 return priv->current_path;
1447 /*************************************************************
1448 * API: Cell Properties *
1449 *************************************************************/
1451 gtk_cell_area_class_install_cell_property (GtkCellAreaClass *aclass,
1455 g_return_if_fail (GTK_IS_CELL_AREA_CLASS (aclass));
1456 g_return_if_fail (G_IS_PARAM_SPEC (pspec));
1457 if (pspec->flags & G_PARAM_WRITABLE)
1458 g_return_if_fail (aclass->set_cell_property != NULL);
1459 if (pspec->flags & G_PARAM_READABLE)
1460 g_return_if_fail (aclass->get_cell_property != NULL);
1461 g_return_if_fail (property_id > 0);
1462 g_return_if_fail (PARAM_SPEC_PARAM_ID (pspec) == 0); /* paranoid */
1463 g_return_if_fail ((pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY)) == 0);
1465 if (g_param_spec_pool_lookup (cell_property_pool, pspec->name, G_OBJECT_CLASS_TYPE (aclass), TRUE))
1467 g_warning (G_STRLOC ": class `%s' already contains a cell property named `%s'",
1468 G_OBJECT_CLASS_NAME (aclass), pspec->name);
1471 g_param_spec_ref (pspec);
1472 g_param_spec_sink (pspec);
1473 PARAM_SPEC_SET_PARAM_ID (pspec, property_id);
1474 g_param_spec_pool_insert (cell_property_pool, pspec, G_OBJECT_CLASS_TYPE (aclass));
1478 gtk_cell_area_class_find_cell_property (GtkCellAreaClass *aclass,
1479 const gchar *property_name)
1481 g_return_val_if_fail (GTK_IS_CELL_AREA_CLASS (aclass), NULL);
1482 g_return_val_if_fail (property_name != NULL, NULL);
1484 return g_param_spec_pool_lookup (cell_property_pool,
1486 G_OBJECT_CLASS_TYPE (aclass),
1491 gtk_cell_area_class_list_cell_properties (GtkCellAreaClass *aclass,
1492 guint *n_properties)
1494 GParamSpec **pspecs;
1497 g_return_val_if_fail (GTK_IS_CELL_AREA_CLASS (aclass), NULL);
1499 pspecs = g_param_spec_pool_list (cell_property_pool,
1500 G_OBJECT_CLASS_TYPE (aclass),
1509 gtk_cell_area_add_with_properties (GtkCellArea *area,
1510 GtkCellRenderer *renderer,
1511 const gchar *first_prop_name,
1514 GtkCellAreaClass *class;
1516 g_return_if_fail (GTK_IS_CELL_AREA (area));
1517 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1519 class = GTK_CELL_AREA_GET_CLASS (area);
1525 class->add (area, renderer);
1527 va_start (var_args, first_prop_name);
1528 gtk_cell_area_cell_set_valist (area, renderer, first_prop_name, var_args);
1532 g_warning ("GtkCellAreaClass::add not implemented for `%s'",
1533 g_type_name (G_TYPE_FROM_INSTANCE (area)));
1537 gtk_cell_area_cell_set (GtkCellArea *area,
1538 GtkCellRenderer *renderer,
1539 const gchar *first_prop_name,
1544 g_return_if_fail (GTK_IS_CELL_AREA (area));
1545 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1547 va_start (var_args, first_prop_name);
1548 gtk_cell_area_cell_set_valist (area, renderer, first_prop_name, var_args);
1553 gtk_cell_area_cell_get (GtkCellArea *area,
1554 GtkCellRenderer *renderer,
1555 const gchar *first_prop_name,
1560 g_return_if_fail (GTK_IS_CELL_AREA (area));
1561 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1563 va_start (var_args, first_prop_name);
1564 gtk_cell_area_cell_get_valist (area, renderer, first_prop_name, var_args);
1569 area_get_cell_property (GtkCellArea *area,
1570 GtkCellRenderer *renderer,
1574 GtkCellAreaClass *class = g_type_class_peek (pspec->owner_type);
1576 class->get_cell_property (area, renderer, PARAM_SPEC_PARAM_ID (pspec), value, pspec);
1580 area_set_cell_property (GtkCellArea *area,
1581 GtkCellRenderer *renderer,
1583 const GValue *value)
1585 GValue tmp_value = { 0, };
1586 GtkCellAreaClass *class = g_type_class_peek (pspec->owner_type);
1588 /* provide a copy to work from, convert (if necessary) and validate */
1589 g_value_init (&tmp_value, G_PARAM_SPEC_VALUE_TYPE (pspec));
1590 if (!g_value_transform (value, &tmp_value))
1591 g_warning ("unable to set cell property `%s' of type `%s' from value of type `%s'",
1593 g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)),
1594 G_VALUE_TYPE_NAME (value));
1595 else if (g_param_value_validate (pspec, &tmp_value) && !(pspec->flags & G_PARAM_LAX_VALIDATION))
1597 gchar *contents = g_strdup_value_contents (value);
1599 g_warning ("value \"%s\" of type `%s' is invalid for property `%s' of type `%s'",
1601 G_VALUE_TYPE_NAME (value),
1603 g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)));
1608 class->set_cell_property (area, renderer, PARAM_SPEC_PARAM_ID (pspec), &tmp_value, pspec);
1610 g_value_unset (&tmp_value);
1614 gtk_cell_area_cell_set_valist (GtkCellArea *area,
1615 GtkCellRenderer *renderer,
1616 const gchar *first_property_name,
1621 g_return_if_fail (GTK_IS_CELL_AREA (area));
1622 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1624 name = first_property_name;
1627 GValue value = { 0, };
1628 gchar *error = NULL;
1630 g_param_spec_pool_lookup (cell_property_pool, name,
1631 G_OBJECT_TYPE (area), TRUE);
1634 g_warning ("%s: cell area class `%s' has no cell property named `%s'",
1635 G_STRLOC, G_OBJECT_TYPE_NAME (area), name);
1638 if (!(pspec->flags & G_PARAM_WRITABLE))
1640 g_warning ("%s: cell property `%s' of cell area class `%s' is not writable",
1641 G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (area));
1645 g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
1646 G_VALUE_COLLECT (&value, var_args, 0, &error);
1649 g_warning ("%s: %s", G_STRLOC, error);
1652 /* we purposely leak the value here, it might not be
1653 * in a sane state if an error condition occoured
1657 area_set_cell_property (area, renderer, pspec, &value);
1658 g_value_unset (&value);
1659 name = va_arg (var_args, gchar*);
1664 gtk_cell_area_cell_get_valist (GtkCellArea *area,
1665 GtkCellRenderer *renderer,
1666 const gchar *first_property_name,
1671 g_return_if_fail (GTK_IS_CELL_AREA (area));
1672 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1674 name = first_property_name;
1677 GValue value = { 0, };
1681 pspec = g_param_spec_pool_lookup (cell_property_pool, name,
1682 G_OBJECT_TYPE (area), TRUE);
1685 g_warning ("%s: cell area class `%s' has no cell property named `%s'",
1686 G_STRLOC, G_OBJECT_TYPE_NAME (area), name);
1689 if (!(pspec->flags & G_PARAM_READABLE))
1691 g_warning ("%s: cell property `%s' of cell area class `%s' is not readable",
1692 G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (area));
1696 g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
1697 area_get_cell_property (area, renderer, pspec, &value);
1698 G_VALUE_LCOPY (&value, var_args, 0, &error);
1701 g_warning ("%s: %s", G_STRLOC, error);
1703 g_value_unset (&value);
1706 g_value_unset (&value);
1707 name = va_arg (var_args, gchar*);
1712 gtk_cell_area_cell_set_property (GtkCellArea *area,
1713 GtkCellRenderer *renderer,
1714 const gchar *property_name,
1715 const GValue *value)
1719 g_return_if_fail (GTK_IS_CELL_AREA (area));
1720 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1721 g_return_if_fail (property_name != NULL);
1722 g_return_if_fail (G_IS_VALUE (value));
1724 pspec = g_param_spec_pool_lookup (cell_property_pool, property_name,
1725 G_OBJECT_TYPE (area), TRUE);
1727 g_warning ("%s: cell area class `%s' has no cell property named `%s'",
1728 G_STRLOC, G_OBJECT_TYPE_NAME (area), property_name);
1729 else if (!(pspec->flags & G_PARAM_WRITABLE))
1730 g_warning ("%s: cell property `%s' of cell area class `%s' is not writable",
1731 G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (area));
1734 area_set_cell_property (area, renderer, pspec, value);
1739 gtk_cell_area_cell_get_property (GtkCellArea *area,
1740 GtkCellRenderer *renderer,
1741 const gchar *property_name,
1746 g_return_if_fail (GTK_IS_CELL_AREA (area));
1747 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1748 g_return_if_fail (property_name != NULL);
1749 g_return_if_fail (G_IS_VALUE (value));
1751 pspec = g_param_spec_pool_lookup (cell_property_pool, property_name,
1752 G_OBJECT_TYPE (area), TRUE);
1754 g_warning ("%s: cell area class `%s' has no cell property named `%s'",
1755 G_STRLOC, G_OBJECT_TYPE_NAME (area), property_name);
1756 else if (!(pspec->flags & G_PARAM_READABLE))
1757 g_warning ("%s: cell property `%s' of cell area class `%s' is not readable",
1758 G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (area));
1761 GValue *prop_value, tmp_value = { 0, };
1763 /* auto-conversion of the callers value type
1765 if (G_VALUE_TYPE (value) == G_PARAM_SPEC_VALUE_TYPE (pspec))
1767 g_value_reset (value);
1770 else if (!g_value_type_transformable (G_PARAM_SPEC_VALUE_TYPE (pspec), G_VALUE_TYPE (value)))
1772 g_warning ("can't retrieve cell property `%s' of type `%s' as value of type `%s'",
1774 g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)),
1775 G_VALUE_TYPE_NAME (value));
1780 g_value_init (&tmp_value, G_PARAM_SPEC_VALUE_TYPE (pspec));
1781 prop_value = &tmp_value;
1784 area_get_cell_property (area, renderer, pspec, prop_value);
1786 if (prop_value != value)
1788 g_value_transform (prop_value, value);
1789 g_value_unset (&tmp_value);
1794 /*************************************************************
1796 *************************************************************/
1799 * gtk_cell_area_can_focus:
1800 * @area: a #GtkCellArea
1802 * Returns whether the area can receive keyboard focus,
1803 * after applying new attributes to @area.
1805 * Returns: whether @area can receive focus.
1808 gtk_cell_area_can_focus (GtkCellArea *area)
1810 g_return_val_if_fail (GTK_IS_CELL_AREA (area), FALSE);
1812 return GTK_CELL_AREA_GET_CLASS (area)->can_focus (area);
1816 * gtk_cell_area_focus:
1817 * @area: a #GtkCellArea
1818 * @direction: the #GtkDirectionType
1820 * This should be called by the @area's owning layout widget
1821 * when focus is to be passed to @area, or moved within @area
1822 * for a given @direction and row data.
1824 * Implementing #GtkCellArea classes should implement this
1825 * method to receive and navigate focus in it's own way particular
1826 * to how it lays out cells.
1828 * Returns: %TRUE if focus remains inside @area as a result of this call.
1831 gtk_cell_area_focus (GtkCellArea *area,
1832 GtkDirectionType direction)
1834 GtkCellAreaClass *class;
1836 g_return_val_if_fail (GTK_IS_CELL_AREA (area), FALSE);
1838 class = GTK_CELL_AREA_GET_CLASS (area);
1841 return class->focus (area, direction);
1843 g_warning ("GtkCellAreaClass::focus not implemented for `%s'",
1844 g_type_name (G_TYPE_FROM_INSTANCE (area)));
1850 * gtk_cell_area_activate:
1851 * @area: a #GtkCellArea
1852 * @iter: the #GtkCellAreaIter in context with the current row data
1853 * @widget: the #GtkWidget that @area is rendering on
1854 * @cell_area: the size and location of @area relative to @widget's allocation
1855 * @flags: the #GtkCellRendererState flags for @area for this row of data.
1857 * Activates @area, usually by activating the currently focused
1858 * cell, however some subclasses which embed widgets in the area
1859 * can also activate a widget if it currently has the focus.
1861 * Returns: Whether @area was successfully activated.
1864 gtk_cell_area_activate (GtkCellArea *area,
1865 GtkCellAreaIter *iter,
1867 const GdkRectangle *cell_area,
1868 GtkCellRendererState flags)
1870 g_return_val_if_fail (GTK_IS_CELL_AREA (area), FALSE);
1872 return GTK_CELL_AREA_GET_CLASS (area)->activate (area, iter, widget, cell_area, flags);
1877 * gtk_cell_area_set_focus_cell:
1878 * @area: a #GtkCellArea
1879 * @focus_cell: the #GtkCellRenderer to give focus to
1881 * This is generally called from #GtkCellArea implementations
1882 * either gtk_cell_area_grab_focus() or gtk_cell_area_update_focus()
1883 * is called. It's also up to the #GtkCellArea implementation
1884 * to update the focused cell when receiving events from
1885 * gtk_cell_area_event() appropriately.
1888 gtk_cell_area_set_focus_cell (GtkCellArea *area,
1889 GtkCellRenderer *renderer)
1891 GtkCellAreaPrivate *priv;
1893 g_return_if_fail (GTK_IS_CELL_AREA (area));
1894 g_return_if_fail (renderer == NULL || GTK_IS_CELL_RENDERER (renderer));
1898 if (priv->focus_cell != renderer)
1900 if (priv->focus_cell)
1901 g_object_unref (priv->focus_cell);
1903 priv->focus_cell = renderer;
1905 if (priv->focus_cell)
1906 g_object_ref (priv->focus_cell);
1908 g_object_notify (G_OBJECT (area), "focus-cell");
1911 /* Signal that the current focus renderer for this path changed
1912 * (it may be that the focus cell did not change, but the row
1913 * may have changed so we need to signal it) */
1914 g_signal_emit (area, cell_area_signals[SIGNAL_FOCUS_CHANGED], 0,
1915 priv->focus_cell, priv->current_path);
1920 * gtk_cell_area_get_focus_cell:
1921 * @area: a #GtkCellArea
1923 * Retrieves the currently focused cell for @area
1925 * Returns: the currently focused cell in @area.
1928 gtk_cell_area_get_focus_cell (GtkCellArea *area)
1930 GtkCellAreaPrivate *priv;
1932 g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL);
1936 return priv->focus_cell;
1940 /*************************************************************
1941 * API: Focus Siblings *
1942 *************************************************************/
1944 gtk_cell_area_add_focus_sibling (GtkCellArea *area,
1945 GtkCellRenderer *renderer,
1946 GtkCellRenderer *sibling)
1948 GtkCellAreaPrivate *priv;
1951 g_return_if_fail (GTK_IS_CELL_AREA (area));
1952 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1953 g_return_if_fail (GTK_IS_CELL_RENDERER (sibling));
1954 g_return_if_fail (renderer != sibling);
1955 g_return_if_fail (gtk_cell_area_has_renderer (area, renderer));
1956 g_return_if_fail (gtk_cell_area_has_renderer (area, sibling));
1957 g_return_if_fail (!gtk_cell_area_is_focus_sibling (area, renderer, sibling));
1959 /* XXX We should also check that sibling is not in any other renderer's sibling
1960 * list already, a renderer can be sibling of only one focusable renderer
1966 siblings = g_hash_table_lookup (priv->focus_siblings, renderer);
1969 siblings = g_list_append (siblings, sibling);
1972 siblings = g_list_append (siblings, sibling);
1973 g_hash_table_insert (priv->focus_siblings, renderer, siblings);
1978 gtk_cell_area_remove_focus_sibling (GtkCellArea *area,
1979 GtkCellRenderer *renderer,
1980 GtkCellRenderer *sibling)
1982 GtkCellAreaPrivate *priv;
1985 g_return_if_fail (GTK_IS_CELL_AREA (area));
1986 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1987 g_return_if_fail (GTK_IS_CELL_RENDERER (sibling));
1988 g_return_if_fail (gtk_cell_area_is_focus_sibling (area, renderer, sibling));
1992 siblings = g_hash_table_lookup (priv->focus_siblings, renderer);
1994 siblings = g_list_copy (siblings);
1995 siblings = g_list_remove (siblings, sibling);
1998 g_hash_table_remove (priv->focus_siblings, renderer);
2000 g_hash_table_insert (priv->focus_siblings, renderer, siblings);
2004 gtk_cell_area_is_focus_sibling (GtkCellArea *area,
2005 GtkCellRenderer *renderer,
2006 GtkCellRenderer *sibling)
2008 GtkCellAreaPrivate *priv;
2009 GList *siblings, *l;
2011 g_return_val_if_fail (GTK_IS_CELL_AREA (area), FALSE);
2012 g_return_val_if_fail (GTK_IS_CELL_RENDERER (renderer), FALSE);
2013 g_return_val_if_fail (GTK_IS_CELL_RENDERER (sibling), FALSE);
2017 siblings = g_hash_table_lookup (priv->focus_siblings, renderer);
2019 for (l = siblings; l; l = l->next)
2021 GtkCellRenderer *a_sibling = l->data;
2023 if (a_sibling == sibling)
2031 gtk_cell_area_get_focus_siblings (GtkCellArea *area,
2032 GtkCellRenderer *renderer)
2034 GtkCellAreaPrivate *priv;
2036 g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL);
2037 g_return_val_if_fail (GTK_IS_CELL_RENDERER (renderer), NULL);
2041 return g_hash_table_lookup (priv->focus_siblings, renderer);
2045 gtk_cell_area_get_focus_from_sibling (GtkCellArea *area,
2046 GtkCellRenderer *renderer)
2048 GtkCellRenderer *ret_renderer = NULL;
2049 GList *renderers, *l;
2051 g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL);
2052 g_return_val_if_fail (GTK_IS_CELL_RENDERER (renderer), NULL);
2054 renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (area));
2056 for (l = renderers; l; l = l->next)
2058 GtkCellRenderer *a_renderer = l->data;
2061 for (list = gtk_cell_area_get_focus_siblings (area, a_renderer);
2062 list; list = list->next)
2064 GtkCellRenderer *sibling_renderer = list->data;
2066 if (sibling_renderer == renderer)
2068 ret_renderer = a_renderer;
2073 g_list_free (renderers);
2075 return ret_renderer;
2078 /*************************************************************
2079 * API: Cell Activation/Editing *
2080 *************************************************************/
2082 gtk_cell_area_editing_started (GtkCellArea *area,
2083 GtkCellRenderer *renderer,
2084 GtkCellEditable *editable,
2085 GdkRectangle *cell_area)
2087 g_signal_emit (area, cell_area_signals[SIGNAL_EDITING_STARTED], 0,
2088 renderer, editable, cell_area, area->priv->current_path);
2092 gtk_cell_area_editing_canceled (GtkCellArea *area,
2093 GtkCellRenderer *renderer)
2095 g_signal_emit (area, cell_area_signals[SIGNAL_EDITING_CANCELED], 0, renderer);
2099 gtk_cell_area_editing_done (GtkCellArea *area,
2100 GtkCellRenderer *renderer,
2101 GtkCellEditable *editable)
2103 g_signal_emit (area, cell_area_signals[SIGNAL_EDITING_DONE], 0, renderer, editable);
2107 gtk_cell_area_remove_editable (GtkCellArea *area,
2108 GtkCellRenderer *renderer,
2109 GtkCellEditable *editable)
2111 g_signal_emit (area, cell_area_signals[SIGNAL_REMOVE_EDITABLE], 0, renderer, editable);
2115 cell_area_editing_done_cb (GtkCellEditable *editable,
2118 GtkCellAreaPrivate *priv = area->priv;
2120 g_assert (priv->edit_widget == editable);
2121 g_assert (priv->edited_cell != NULL);
2123 gtk_cell_area_editing_done (area, priv->edited_cell, priv->edit_widget);
2127 cell_area_remove_widget_cb (GtkCellEditable *editable,
2130 GtkCellAreaPrivate *priv = area->priv;
2132 g_assert (priv->edit_widget == editable);
2133 g_assert (priv->edited_cell != NULL);
2135 gtk_cell_area_remove_editable (area, priv->edited_cell, priv->edit_widget);
2137 /* Now that we're done with editing the widget and it can be removed,
2138 * remove our references to the widget and disconnect handlers */
2139 gtk_cell_area_set_edited_cell (area, NULL);
2140 gtk_cell_area_set_edit_widget (area, NULL);
2144 gtk_cell_area_set_edited_cell (GtkCellArea *area,
2145 GtkCellRenderer *renderer)
2147 GtkCellAreaPrivate *priv;
2149 g_return_if_fail (GTK_IS_CELL_AREA (area));
2150 g_return_if_fail (renderer == NULL || GTK_IS_CELL_RENDERER (renderer));
2154 if (priv->edited_cell != renderer)
2156 if (priv->edited_cell)
2157 g_object_unref (priv->edited_cell);
2159 priv->edited_cell = renderer;
2161 if (priv->edited_cell)
2162 g_object_ref (priv->edited_cell);
2164 g_object_notify (G_OBJECT (area), "edited-cell");
2169 gtk_cell_area_get_edited_cell (GtkCellArea *area)
2171 GtkCellAreaPrivate *priv;
2173 g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL);
2177 return priv->edited_cell;
2181 gtk_cell_area_set_edit_widget (GtkCellArea *area,
2182 GtkCellEditable *editable)
2184 GtkCellAreaPrivate *priv;
2186 g_return_if_fail (GTK_IS_CELL_AREA (area));
2187 g_return_if_fail (editable == NULL || GTK_IS_CELL_EDITABLE (editable));
2191 if (priv->edit_widget != editable)
2193 if (priv->edit_widget)
2195 g_signal_handler_disconnect (priv->edit_widget, priv->editing_done_id);
2196 g_signal_handler_disconnect (priv->edit_widget, priv->remove_widget_id);
2198 g_object_unref (priv->edit_widget);
2201 priv->edit_widget = editable;
2203 if (priv->edit_widget)
2205 priv->editing_done_id =
2206 g_signal_connect (priv->edit_widget, "editing-done",
2207 G_CALLBACK (cell_area_editing_done_cb), area);
2208 priv->remove_widget_id =
2209 g_signal_connect (priv->edit_widget, "remove-widget",
2210 G_CALLBACK (cell_area_remove_widget_cb), area);
2212 g_object_ref (priv->edit_widget);
2215 g_object_notify (G_OBJECT (area), "edit-widget");
2220 gtk_cell_area_get_edit_widget (GtkCellArea *area)
2222 GtkCellAreaPrivate *priv;
2224 g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL);
2228 return priv->edit_widget;
2232 gtk_cell_area_activate_cell (GtkCellArea *area,
2234 GtkCellRenderer *renderer,
2236 const GdkRectangle *cell_area,
2237 GtkCellRendererState flags)
2239 GtkCellRendererMode mode;
2240 GdkRectangle inner_area;
2241 GtkCellAreaPrivate *priv;
2243 g_return_val_if_fail (GTK_IS_CELL_AREA (area), FALSE);
2244 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
2245 g_return_val_if_fail (GTK_IS_CELL_RENDERER (renderer), FALSE);
2246 g_return_val_if_fail (cell_area != NULL, FALSE);
2250 /* Remove margins from the background area to produce the cell area.
2252 * XXX Maybe have to do some rtl mode treatment here...
2254 gtk_cell_area_inner_cell_area (area, cell_area, &inner_area);
2256 g_object_get (renderer, "mode", &mode, NULL);
2258 if (mode == GTK_CELL_RENDERER_MODE_ACTIVATABLE)
2260 if (gtk_cell_renderer_activate (renderer,
2268 else if (mode == GTK_CELL_RENDERER_MODE_EDITABLE)
2270 GtkCellEditable *editable_widget;
2273 gtk_cell_renderer_start_editing (renderer,
2280 if (editable_widget != NULL)
2282 GdkRectangle edit_area;
2284 g_return_val_if_fail (GTK_IS_CELL_EDITABLE (editable_widget), FALSE);
2286 gtk_cell_area_set_edited_cell (area, renderer);
2287 gtk_cell_area_set_edit_widget (area, editable_widget);
2289 gtk_cell_area_aligned_cell_area (area, widget, renderer, &inner_area, &edit_area);
2291 /* Signal that editing started so that callers can get
2292 * a handle on the editable_widget */
2293 gtk_cell_area_editing_started (area, priv->focus_cell, editable_widget, &edit_area);
2303 gtk_cell_area_stop_editing (GtkCellArea *area,
2306 GtkCellAreaPrivate *priv;
2308 g_return_if_fail (GTK_IS_CELL_AREA (area));
2312 if (priv->edited_cell)
2314 GtkCellEditable *edit_widget = g_object_ref (priv->edit_widget);
2315 GtkCellRenderer *edit_cell = g_object_ref (priv->edited_cell);
2317 /* Stop editing of the cell renderer */
2318 gtk_cell_renderer_stop_editing (priv->edited_cell, canceled);
2320 /* Signal that editing has been canceled */
2322 gtk_cell_area_editing_canceled (area, priv->edited_cell);
2324 /* Remove any references to the editable widget */
2325 gtk_cell_area_set_edited_cell (area, NULL);
2326 gtk_cell_area_set_edit_widget (area, NULL);
2328 /* Send the remove-widget signal explicitly (this is done after setting
2329 * the edit cell/widget NULL to avoid feedback)
2331 gtk_cell_area_remove_editable (area, edit_cell, edit_widget);
2332 g_object_unref (edit_cell);
2333 g_object_unref (edit_widget);
2337 /*************************************************************
2339 *************************************************************/
2341 gtk_cell_area_get_cell_margin_left (GtkCellArea *area)
2343 g_return_val_if_fail (GTK_IS_CELL_AREA (area), 0);
2345 return area->priv->cell_border.left;
2349 gtk_cell_area_set_cell_margin_left (GtkCellArea *area,
2352 GtkCellAreaPrivate *priv;
2354 g_return_if_fail (GTK_IS_CELL_AREA (area));
2358 if (priv->cell_border.left != margin)
2360 priv->cell_border.left = margin;
2362 g_object_notify (G_OBJECT (area), "margin-left");
2367 gtk_cell_area_get_cell_margin_right (GtkCellArea *area)
2369 g_return_val_if_fail (GTK_IS_CELL_AREA (area), 0);
2371 return area->priv->cell_border.right;
2375 gtk_cell_area_set_cell_margin_right (GtkCellArea *area,
2378 GtkCellAreaPrivate *priv;
2380 g_return_if_fail (GTK_IS_CELL_AREA (area));
2384 if (priv->cell_border.right != margin)
2386 priv->cell_border.right = margin;
2388 g_object_notify (G_OBJECT (area), "margin-right");
2393 gtk_cell_area_get_cell_margin_top (GtkCellArea *area)
2395 g_return_val_if_fail (GTK_IS_CELL_AREA (area), 0);
2397 return area->priv->cell_border.top;
2401 gtk_cell_area_set_cell_margin_top (GtkCellArea *area,
2404 GtkCellAreaPrivate *priv;
2406 g_return_if_fail (GTK_IS_CELL_AREA (area));
2410 if (priv->cell_border.top != margin)
2412 priv->cell_border.top = margin;
2414 g_object_notify (G_OBJECT (area), "margin-top");
2419 gtk_cell_area_get_cell_margin_bottom (GtkCellArea *area)
2421 g_return_val_if_fail (GTK_IS_CELL_AREA (area), 0);
2423 return area->priv->cell_border.bottom;
2427 gtk_cell_area_set_cell_margin_bottom (GtkCellArea *area,
2430 GtkCellAreaPrivate *priv;
2432 g_return_if_fail (GTK_IS_CELL_AREA (area));
2436 if (priv->cell_border.bottom != margin)
2438 priv->cell_border.bottom = margin;
2440 g_object_notify (G_OBJECT (area), "margin-bottom");
2445 gtk_cell_area_inner_cell_area (GtkCellArea *area,
2446 const GdkRectangle *cell_area,
2447 GdkRectangle *inner_area)
2449 GtkCellAreaPrivate *priv;
2451 g_return_if_fail (GTK_IS_CELL_AREA (area));
2452 g_return_if_fail (cell_area != NULL);
2453 g_return_if_fail (inner_area != NULL);
2457 *inner_area = *cell_area;
2459 inner_area->x += priv->cell_border.left;
2460 inner_area->width -= (priv->cell_border.left + priv->cell_border.right);
2461 inner_area->y += priv->cell_border.top;
2462 inner_area->height -= (priv->cell_border.top + priv->cell_border.bottom);
2466 gtk_cell_area_aligned_cell_area (GtkCellArea *area,
2468 GtkCellRenderer *renderer,
2469 const GdkRectangle *cell_area,
2470 GdkRectangle *aligned_area)
2472 GtkCellAreaPrivate *priv;
2473 gint opposite_size, x_offset, y_offset;
2475 g_return_if_fail (GTK_IS_CELL_AREA (area));
2476 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
2477 g_return_if_fail (GTK_IS_WIDGET (widget));
2478 g_return_if_fail (cell_area != NULL);
2479 g_return_if_fail (aligned_area != NULL);
2483 *aligned_area = *cell_area;
2485 /* Trim up the aligned size */
2486 if (gtk_cell_renderer_get_request_mode (renderer) == GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH)
2488 gtk_cell_renderer_get_preferred_height_for_width (renderer, widget,
2489 aligned_area->width,
2490 NULL, &opposite_size);
2492 aligned_area->height = MIN (opposite_size, aligned_area->height);
2496 gtk_cell_renderer_get_preferred_width_for_height (renderer, widget,
2497 aligned_area->height,
2498 NULL, &opposite_size);
2500 aligned_area->width = MIN (opposite_size, aligned_area->width);
2503 /* offset the cell position */
2504 _gtk_cell_renderer_calc_offset (renderer, cell_area,
2505 gtk_widget_get_direction (widget),
2506 aligned_area->width,
2507 aligned_area->height,
2508 &x_offset, &y_offset);
2510 aligned_area->x += x_offset;
2511 aligned_area->y += y_offset;
2515 gtk_cell_area_request_renderer (GtkCellArea *area,
2516 GtkCellRenderer *renderer,
2517 GtkOrientation orientation,
2523 GtkCellAreaPrivate *priv;
2525 g_return_if_fail (GTK_IS_CELL_AREA (area));
2526 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
2527 g_return_if_fail (GTK_IS_WIDGET (widget));
2528 g_return_if_fail (minimum_size != NULL);
2529 g_return_if_fail (natural_size != NULL);
2533 if (orientation == GTK_ORIENTATION_HORIZONTAL)
2536 gtk_cell_renderer_get_preferred_width (renderer, widget, minimum_size, natural_size);
2539 for_size = MAX (0, for_size - (priv->cell_border.top + priv->cell_border.bottom));
2541 gtk_cell_renderer_get_preferred_width_for_height (renderer, widget, for_size,
2542 minimum_size, natural_size);
2545 *minimum_size += (priv->cell_border.left + priv->cell_border.right);
2546 *natural_size += (priv->cell_border.left + priv->cell_border.right);
2548 else /* GTK_ORIENTATION_VERTICAL */
2551 gtk_cell_renderer_get_preferred_height (renderer, widget, minimum_size, natural_size);
2554 for_size = MAX (0, for_size - (priv->cell_border.left + priv->cell_border.right));
2556 gtk_cell_renderer_get_preferred_height_for_width (renderer, widget, for_size,
2557 minimum_size, natural_size);
2560 *minimum_size += (priv->cell_border.top + priv->cell_border.bottom);
2561 *natural_size += (priv->cell_border.top + priv->cell_border.bottom);