3 * Copyright (C) 2010 Openismus GmbH
6 * Tristan Van Berkom <tristanvb@openismus.com>
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
31 #include "gtkcelllayout.h"
32 #include "gtkcellarea.h"
33 #include "gtkcellareaiter.h"
34 #include "gtkmarshalers.h"
35 #include "gtkprivate.h"
37 #include <gobject/gvaluecollector.h>
41 static void gtk_cell_area_dispose (GObject *object);
42 static void gtk_cell_area_finalize (GObject *object);
43 static void gtk_cell_area_set_property (GObject *object,
47 static void gtk_cell_area_get_property (GObject *object,
52 /* GtkCellAreaClass */
53 static gint gtk_cell_area_real_event (GtkCellArea *area,
54 GtkCellAreaIter *iter,
57 const GdkRectangle *cell_area,
58 GtkCellRendererState flags);
59 static void gtk_cell_area_real_get_preferred_height_for_width (GtkCellArea *area,
60 GtkCellAreaIter *iter,
64 gint *natural_height);
65 static void gtk_cell_area_real_get_preferred_width_for_height (GtkCellArea *area,
66 GtkCellAreaIter *iter,
71 static void gtk_cell_area_real_update_focus (GtkCellArea *area);
73 /* GtkCellLayoutIface */
74 static void gtk_cell_area_cell_layout_init (GtkCellLayoutIface *iface);
75 static void gtk_cell_area_pack_default (GtkCellLayout *cell_layout,
76 GtkCellRenderer *renderer,
78 static void gtk_cell_area_clear (GtkCellLayout *cell_layout);
79 static void gtk_cell_area_add_attribute (GtkCellLayout *cell_layout,
80 GtkCellRenderer *renderer,
81 const gchar *attribute,
83 static void gtk_cell_area_set_cell_data_func (GtkCellLayout *cell_layout,
84 GtkCellRenderer *cell,
85 GtkCellLayoutDataFunc func,
87 GDestroyNotify destroy);
88 static void gtk_cell_area_clear_attributes (GtkCellLayout *cell_layout,
89 GtkCellRenderer *renderer);
90 static void gtk_cell_area_reorder (GtkCellLayout *cell_layout,
91 GtkCellRenderer *cell,
93 static GList *gtk_cell_area_get_cells (GtkCellLayout *cell_layout);
95 /* Attribute/Cell metadata */
97 const gchar *attribute;
104 GtkCellLayoutDataFunc func;
106 GDestroyNotify destroy;
109 static CellInfo *cell_info_new (GtkCellLayoutDataFunc func,
111 GDestroyNotify destroy);
112 static void cell_info_free (CellInfo *info);
113 static CellAttribute *cell_attribute_new (GtkCellRenderer *renderer,
114 const gchar *attribute,
116 static void cell_attribute_free (CellAttribute *attribute);
117 static gint cell_attribute_find (CellAttribute *cell_attribute,
118 const gchar *attribute);
120 /* Internal signal emissions */
121 static void gtk_cell_area_editing_started (GtkCellArea *area,
122 GtkCellRenderer *renderer,
123 GtkCellEditable *editable);
124 static void gtk_cell_area_editing_canceled (GtkCellArea *area,
125 GtkCellRenderer *renderer);
126 static void gtk_cell_area_editing_done (GtkCellArea *area,
127 GtkCellRenderer *renderer,
128 GtkCellEditable *editable);
129 static void gtk_cell_area_remove_editable (GtkCellArea *area,
130 GtkCellRenderer *renderer,
131 GtkCellEditable *editable);
134 /* Struct to pass data along while looping over
135 * cell renderers to apply attributes
141 gboolean is_expander;
142 gboolean is_expanded;
145 struct _GtkCellAreaPrivate
147 /* The GtkCellArea bookkeeps any connected
148 * attributes in this hash table.
150 GHashTable *cell_info;
152 /* The cell border decides how much space to reserve
153 * around each cell for the background_area
155 GtkBorder cell_border;
157 /* Current path is saved as a side-effect
158 * of gtk_cell_area_apply_attributes() */
161 /* Current cell being edited and editable widget used */
162 GtkCellEditable *edit_widget;
163 GtkCellRenderer *edited_cell;
165 /* Signal connections to the editable widget */
166 gulong editing_done_id;
167 gulong remove_widget_id;
169 /* Currently focused cell */
170 GtkCellRenderer *focus_cell;
177 PROP_CELL_MARGIN_LEFT,
178 PROP_CELL_MARGIN_RIGHT,
179 PROP_CELL_MARGIN_TOP,
180 PROP_CELL_MARGIN_BOTTOM,
188 SIGNAL_EDITING_STARTED,
189 SIGNAL_EDITING_CANCELED,
191 SIGNAL_REMOVE_EDITABLE,
195 /* Keep the paramspec pool internal, no need to deliver notifications
196 * on cells. at least no percieved need for now */
197 static GParamSpecPool *cell_property_pool = NULL;
198 static guint cell_area_signals[LAST_SIGNAL] = { 0 };
200 #define PARAM_SPEC_PARAM_ID(pspec) ((pspec)->param_id)
201 #define PARAM_SPEC_SET_PARAM_ID(pspec, id) ((pspec)->param_id = (id))
204 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GtkCellArea, gtk_cell_area, G_TYPE_INITIALLY_UNOWNED,
205 G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
206 gtk_cell_area_cell_layout_init));
209 gtk_cell_area_init (GtkCellArea *area)
211 GtkCellAreaPrivate *priv;
213 area->priv = G_TYPE_INSTANCE_GET_PRIVATE (area,
218 priv->cell_info = g_hash_table_new_full (g_direct_hash,
221 (GDestroyNotify)cell_info_free);
223 priv->cell_border.left = 0;
224 priv->cell_border.right = 0;
225 priv->cell_border.top = 0;
226 priv->cell_border.bottom = 0;
228 priv->focus_cell = NULL;
229 priv->edited_cell = NULL;
230 priv->edit_widget = NULL;
231 priv->can_focus = FALSE;
233 priv->editing_done_id = 0;
234 priv->remove_widget_id = 0;
238 gtk_cell_area_class_init (GtkCellAreaClass *class)
240 GObjectClass *object_class = G_OBJECT_CLASS (class);
243 object_class->dispose = gtk_cell_area_dispose;
244 object_class->finalize = gtk_cell_area_finalize;
245 object_class->get_property = gtk_cell_area_get_property;
246 object_class->set_property = gtk_cell_area_set_property;
250 class->remove = NULL;
251 class->forall = NULL;
252 class->event = gtk_cell_area_real_event;
253 class->render = NULL;
256 class->create_iter = NULL;
257 class->get_request_mode = NULL;
258 class->get_preferred_width = NULL;
259 class->get_preferred_height = NULL;
260 class->get_preferred_height_for_width = gtk_cell_area_real_get_preferred_height_for_width;
261 class->get_preferred_width_for_height = gtk_cell_area_real_get_preferred_width_for_height;
264 class->grab_focus = NULL;
265 class->update_focus = gtk_cell_area_real_update_focus;
268 cell_area_signals[SIGNAL_FOCUS_LEAVE] =
269 g_signal_new (I_("focus-leave"),
270 G_TYPE_FROM_CLASS (object_class),
272 0, /* Class offset (just a notification, no class handler) */
274 _gtk_marshal_VOID__ENUM_STRING,
276 GTK_TYPE_DIRECTION_TYPE, G_TYPE_STRING);
278 cell_area_signals[SIGNAL_EDITING_STARTED] =
279 g_signal_new (I_("editing-started"),
280 G_OBJECT_CLASS_TYPE (object_class),
282 0, /* No class closure here */
284 _gtk_marshal_VOID__OBJECT_OBJECT_STRING,
286 GTK_TYPE_CELL_RENDERER,
287 GTK_TYPE_CELL_EDITABLE,
290 cell_area_signals[SIGNAL_EDITING_CANCELED] =
291 g_signal_new (I_("editing-canceled"),
292 G_OBJECT_CLASS_TYPE (object_class),
294 0, /* No class closure here */
296 _gtk_marshal_VOID__OBJECT,
298 GTK_TYPE_CELL_RENDERER);
300 cell_area_signals[SIGNAL_EDITING_DONE] =
301 g_signal_new (I_("editing-done"),
302 G_OBJECT_CLASS_TYPE (object_class),
304 0, /* No class closure here */
306 _gtk_marshal_VOID__OBJECT_OBJECT,
308 GTK_TYPE_CELL_RENDERER,
309 GTK_TYPE_CELL_EDITABLE);
311 cell_area_signals[SIGNAL_REMOVE_EDITABLE] =
312 g_signal_new (I_("remove-editable"),
313 G_OBJECT_CLASS_TYPE (object_class),
315 0, /* No class closure here */
317 _gtk_marshal_VOID__OBJECT_OBJECT,
319 GTK_TYPE_CELL_RENDERER,
320 GTK_TYPE_CELL_EDITABLE);
323 g_object_class_install_property (object_class,
324 PROP_CELL_MARGIN_LEFT,
327 P_("Margin on Left"),
328 P_("Pixels of extra space on the left side of each cell"),
332 GTK_PARAM_READWRITE));
334 g_object_class_install_property (object_class,
335 PROP_CELL_MARGIN_RIGHT,
337 ("cell-margin-right",
338 P_("Margin on Right"),
339 P_("Pixels of extra space on the right side of each cell"),
343 GTK_PARAM_READWRITE));
345 g_object_class_install_property (object_class,
346 PROP_CELL_MARGIN_TOP,
350 P_("Pixels of extra space on the top side of each cell"),
354 GTK_PARAM_READWRITE));
356 g_object_class_install_property (object_class,
357 PROP_CELL_MARGIN_BOTTOM,
359 ("cell-margin-bottom",
360 P_("Margin on Bottom"),
361 P_("Pixels of extra space on the bottom side of each cell"),
365 GTK_PARAM_READWRITE));
367 g_object_class_install_property (object_class,
372 P_("The cell which currently has focus"),
373 GTK_TYPE_CELL_RENDERER,
374 GTK_PARAM_READWRITE));
376 g_object_class_install_property (object_class,
381 P_("The cell which is currently being edited"),
382 GTK_TYPE_CELL_RENDERER,
383 GTK_PARAM_READWRITE));
385 g_object_class_install_property (object_class,
390 P_("The widget currently editing the edited cell"),
391 GTK_TYPE_CELL_RENDERER,
392 GTK_PARAM_READWRITE));
394 /* Pool for Cell Properties */
395 if (!cell_property_pool)
396 cell_property_pool = g_param_spec_pool_new (FALSE);
398 g_type_class_add_private (object_class, sizeof (GtkCellAreaPrivate));
401 /*************************************************************
403 *************************************************************/
405 cell_info_new (GtkCellLayoutDataFunc func,
407 GDestroyNotify destroy)
409 CellInfo *info = g_slice_new (CellInfo);
411 info->attributes = NULL;
414 info->destroy = destroy;
420 cell_info_free (CellInfo *info)
423 info->destroy (info->data);
425 g_slist_foreach (info->attributes, (GFunc)cell_attribute_free, NULL);
426 g_slist_free (info->attributes);
428 g_slice_free (CellInfo, info);
431 static CellAttribute *
432 cell_attribute_new (GtkCellRenderer *renderer,
433 const gchar *attribute,
438 /* Check if the attribute really exists and point to
439 * the property string installed on the cell renderer
440 * class (dont dup the string)
442 pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (renderer), attribute);
446 CellAttribute *cell_attribute = g_slice_new (CellAttribute);
448 cell_attribute->attribute = pspec->name;
449 cell_attribute->column = column;
451 return cell_attribute;
458 cell_attribute_free (CellAttribute *attribute)
460 g_slice_free (CellAttribute, attribute);
463 /* GCompareFunc for g_slist_find_custom() */
465 cell_attribute_find (CellAttribute *cell_attribute,
466 const gchar *attribute)
468 return g_strcmp0 (cell_attribute->attribute, attribute);
471 /*************************************************************
473 *************************************************************/
475 gtk_cell_area_finalize (GObject *object)
477 GtkCellArea *area = GTK_CELL_AREA (object);
478 GtkCellAreaPrivate *priv = area->priv;
480 /* All cell renderers should already be removed at this point,
481 * just kill our hash table here.
483 g_hash_table_destroy (priv->cell_info);
485 g_free (priv->current_path);
487 G_OBJECT_CLASS (gtk_cell_area_parent_class)->finalize (object);
492 gtk_cell_area_dispose (GObject *object)
494 /* This removes every cell renderer that may be added to the GtkCellArea,
495 * subclasses should be breaking references to the GtkCellRenderers
498 gtk_cell_layout_clear (GTK_CELL_LAYOUT (object));
500 /* Remove any ref to a focused/edited cell */
501 gtk_cell_area_set_focus_cell (GTK_CELL_AREA (object), NULL);
502 gtk_cell_area_set_edited_cell (GTK_CELL_AREA (object), NULL);
503 gtk_cell_area_set_edit_widget (GTK_CELL_AREA (object), NULL);
505 G_OBJECT_CLASS (gtk_cell_area_parent_class)->dispose (object);
509 gtk_cell_area_set_property (GObject *object,
514 GtkCellArea *area = GTK_CELL_AREA (object);
518 case PROP_CELL_MARGIN_LEFT:
519 gtk_cell_area_set_cell_margin_left (area, g_value_get_int (value));
521 case PROP_CELL_MARGIN_RIGHT:
522 gtk_cell_area_set_cell_margin_right (area, g_value_get_int (value));
524 case PROP_CELL_MARGIN_TOP:
525 gtk_cell_area_set_cell_margin_top (area, g_value_get_int (value));
527 case PROP_CELL_MARGIN_BOTTOM:
528 gtk_cell_area_set_cell_margin_bottom (area, g_value_get_int (value));
530 case PROP_FOCUS_CELL:
531 gtk_cell_area_set_focus_cell (area, (GtkCellRenderer *)g_value_get_object (value));
533 case PROP_EDITED_CELL:
534 gtk_cell_area_set_edited_cell (area, (GtkCellRenderer *)g_value_get_object (value));
536 case PROP_EDIT_WIDGET:
537 gtk_cell_area_set_edit_widget (area, (GtkCellEditable *)g_value_get_object (value));
540 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
546 gtk_cell_area_get_property (GObject *object,
551 GtkCellArea *area = GTK_CELL_AREA (object);
552 GtkCellAreaPrivate *priv = area->priv;
556 case PROP_CELL_MARGIN_LEFT:
557 g_value_set_int (value, priv->cell_border.left);
559 case PROP_CELL_MARGIN_RIGHT:
560 g_value_set_int (value, priv->cell_border.right);
562 case PROP_CELL_MARGIN_TOP:
563 g_value_set_int (value, priv->cell_border.top);
565 case PROP_CELL_MARGIN_BOTTOM:
566 g_value_set_int (value, priv->cell_border.bottom);
568 case PROP_FOCUS_CELL:
569 g_value_set_object (value, priv->focus_cell);
571 case PROP_EDITED_CELL:
572 g_value_set_object (value, priv->edited_cell);
574 case PROP_EDIT_WIDGET:
575 g_value_set_object (value, priv->edit_widget);
578 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
583 /*************************************************************
585 *************************************************************/
587 gtk_cell_area_real_event (GtkCellArea *area,
588 GtkCellAreaIter *iter,
591 const GdkRectangle *cell_area,
592 GtkCellRendererState flags)
594 if (event->type == GDK_KEY_PRESS && (flags & GTK_CELL_RENDERER_FOCUSED) != 0)
596 GdkEventKey *key_event = (GdkEventKey *)event;
597 GtkCellAreaPrivate *priv = area->priv;
599 if (priv->focus_cell &&
600 (key_event->keyval == GDK_KEY_space ||
601 key_event->keyval == GDK_KEY_KP_Space ||
602 key_event->keyval == GDK_KEY_Return ||
603 key_event->keyval == GDK_KEY_ISO_Enter ||
604 key_event->keyval == GDK_KEY_KP_Enter))
606 GdkRectangle background_area;
608 /* Get the allocation of the focused cell.
610 gtk_cell_area_get_cell_allocation (area, iter, widget, priv->focus_cell,
611 cell_area, &background_area);
613 /* Activate or Edit the currently focused cell */
614 if (gtk_cell_area_activate_cell (area, widget, priv->focus_cell, event,
615 &background_area, flags))
618 else if (priv->edited_cell &&
619 (key_event->keyval == GDK_KEY_Escape))
621 gtk_cell_area_stop_editing (area, TRUE);
630 gtk_cell_area_real_get_preferred_height_for_width (GtkCellArea *area,
631 GtkCellAreaIter *iter,
634 gint *minimum_height,
635 gint *natural_height)
637 /* If the area doesnt do height-for-width, fallback on base preferred height */
638 GTK_CELL_AREA_GET_CLASS (area)->get_preferred_width (area, iter, widget, minimum_height, natural_height);
642 gtk_cell_area_real_get_preferred_width_for_height (GtkCellArea *area,
643 GtkCellAreaIter *iter,
649 /* If the area doesnt do width-for-height, fallback on base preferred width */
650 GTK_CELL_AREA_GET_CLASS (area)->get_preferred_width (area, iter, widget, minimum_width, natural_width);
654 update_can_focus (GtkCellRenderer *renderer,
658 if (gtk_cell_renderer_can_focus (renderer))
663 gtk_cell_area_real_update_focus (GtkCellArea *area)
665 gboolean can_focus = FALSE;
667 /* Update the area's can focus flag, if any of the renderers can
668 * focus then the area can focus.
670 * Subclasses can override this in the case that they are also
671 * rendering widgets as well as renderers.
673 gtk_cell_area_forall (area, (GtkCellCallback)update_can_focus, &can_focus);
674 gtk_cell_area_set_can_focus (area, can_focus);
676 /* Unset the currently focused cell if the area can not receive
677 * focus for the given row data */
679 gtk_cell_area_set_focus_cell (area, NULL);
682 /*************************************************************
683 * GtkCellLayoutIface *
684 *************************************************************/
686 gtk_cell_area_cell_layout_init (GtkCellLayoutIface *iface)
688 iface->pack_start = gtk_cell_area_pack_default;
689 iface->pack_end = gtk_cell_area_pack_default;
690 iface->clear = gtk_cell_area_clear;
691 iface->add_attribute = gtk_cell_area_add_attribute;
692 iface->set_cell_data_func = gtk_cell_area_set_cell_data_func;
693 iface->clear_attributes = gtk_cell_area_clear_attributes;
694 iface->reorder = gtk_cell_area_reorder;
695 iface->get_cells = gtk_cell_area_get_cells;
699 gtk_cell_area_pack_default (GtkCellLayout *cell_layout,
700 GtkCellRenderer *renderer,
703 gtk_cell_area_add (GTK_CELL_AREA (cell_layout), renderer);
707 gtk_cell_area_clear (GtkCellLayout *cell_layout)
709 GtkCellArea *area = GTK_CELL_AREA (cell_layout);
711 gtk_cell_layout_get_cells (cell_layout);
713 for (l = cells; l; l = l->next)
715 GtkCellRenderer *renderer = l->data;
716 gtk_cell_area_remove (area, renderer);
723 gtk_cell_area_add_attribute (GtkCellLayout *cell_layout,
724 GtkCellRenderer *renderer,
725 const gchar *attribute,
728 gtk_cell_area_attribute_connect (GTK_CELL_AREA (cell_layout),
729 renderer, attribute, column);
733 gtk_cell_area_set_cell_data_func (GtkCellLayout *cell_layout,
734 GtkCellRenderer *renderer,
735 GtkCellLayoutDataFunc func,
737 GDestroyNotify destroy)
739 GtkCellArea *area = GTK_CELL_AREA (cell_layout);
740 GtkCellAreaPrivate *priv = area->priv;
743 info = g_hash_table_lookup (priv->cell_info, renderer);
747 if (info->destroy && info->data)
748 info->destroy (info->data);
753 info->data = func_data;
754 info->destroy = destroy;
760 info->destroy = NULL;
765 info = cell_info_new (func, func_data, destroy);
767 g_hash_table_insert (priv->cell_info, renderer, info);
772 gtk_cell_area_clear_attributes (GtkCellLayout *cell_layout,
773 GtkCellRenderer *renderer)
775 GtkCellArea *area = GTK_CELL_AREA (cell_layout);
776 GtkCellAreaPrivate *priv = area->priv;
779 info = g_hash_table_lookup (priv->cell_info, renderer);
783 g_slist_foreach (info->attributes, (GFunc)cell_attribute_free, NULL);
784 g_slist_free (info->attributes);
786 info->attributes = NULL;
791 gtk_cell_area_reorder (GtkCellLayout *cell_layout,
792 GtkCellRenderer *cell,
795 g_warning ("GtkCellLayout::reorder not implemented for `%s'",
796 g_type_name (G_TYPE_FROM_INSTANCE (cell_layout)));
800 accum_cells (GtkCellRenderer *renderer,
803 *accum = g_list_prepend (*accum, renderer);
807 gtk_cell_area_get_cells (GtkCellLayout *cell_layout)
811 gtk_cell_area_forall (GTK_CELL_AREA (cell_layout),
812 (GtkCellCallback)accum_cells,
815 return g_list_reverse (cells);
819 /*************************************************************
821 *************************************************************/
825 * @area: a #GtkCellArea
826 * @renderer: the #GtkCellRenderer to add to @area
828 * Adds @renderer to @area with the default child cell properties.
831 gtk_cell_area_add (GtkCellArea *area,
832 GtkCellRenderer *renderer)
834 GtkCellAreaClass *class;
836 g_return_if_fail (GTK_IS_CELL_AREA (area));
837 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
839 class = GTK_CELL_AREA_GET_CLASS (area);
842 class->add (area, renderer);
844 g_warning ("GtkCellAreaClass::add not implemented for `%s'",
845 g_type_name (G_TYPE_FROM_INSTANCE (area)));
849 * gtk_cell_area_remove:
850 * @area: a #GtkCellArea
851 * @renderer: the #GtkCellRenderer to add to @area
853 * Removes @renderer from @area.
856 gtk_cell_area_remove (GtkCellArea *area,
857 GtkCellRenderer *renderer)
859 GtkCellAreaClass *class;
860 GtkCellAreaPrivate *priv;
862 g_return_if_fail (GTK_IS_CELL_AREA (area));
863 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
865 class = GTK_CELL_AREA_GET_CLASS (area);
868 /* Remove any custom attributes and custom cell data func here first */
869 g_hash_table_remove (priv->cell_info, renderer);
872 class->remove (area, renderer);
874 g_warning ("GtkCellAreaClass::remove not implemented for `%s'",
875 g_type_name (G_TYPE_FROM_INSTANCE (area)));
879 * gtk_cell_area_forall
880 * @area: a #GtkCellArea
881 * @callback: the #GtkCellCallback to call
882 * @callback_data: user provided data pointer
884 * Calls @callback for every #GtkCellRenderer in @area.
887 gtk_cell_area_forall (GtkCellArea *area,
888 GtkCellCallback callback,
889 gpointer callback_data)
891 GtkCellAreaClass *class;
893 g_return_if_fail (GTK_IS_CELL_AREA (area));
894 g_return_if_fail (callback != NULL);
896 class = GTK_CELL_AREA_GET_CLASS (area);
899 class->forall (area, callback, callback_data);
901 g_warning ("GtkCellAreaClass::forall not implemented for `%s'",
902 g_type_name (G_TYPE_FROM_INSTANCE (area)));
906 * gtk_cell_area_get_cell_allocation:
907 * @area: a #GtkCellArea
908 * @iter: the #GtkCellAreaIter used to hold sizes for @area.
909 * @widget: the #GtkWidget that @area is rendering on
910 * @renderer: the #GtkCellRenderer to get the allocation for
911 * @cell_area: the whole allocated area for @area in @widget
913 * @allocation: where to store the allocation for @renderer
915 * Derives the allocation of @renderer inside @area if @area
916 * were to be renderered in @cell_area.
919 gtk_cell_area_get_cell_allocation (GtkCellArea *area,
920 GtkCellAreaIter *iter,
922 GtkCellRenderer *renderer,
923 const GdkRectangle *cell_area,
924 GdkRectangle *allocation)
926 GtkCellAreaClass *class;
928 g_return_if_fail (GTK_IS_CELL_AREA (area));
929 g_return_if_fail (GTK_IS_CELL_AREA_ITER (iter));
930 g_return_if_fail (GTK_IS_WIDGET (widget));
931 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
932 g_return_if_fail (cell_area != NULL);
933 g_return_if_fail (allocation != NULL);
935 class = GTK_CELL_AREA_GET_CLASS (area);
937 if (class->get_cell_allocation)
938 class->get_cell_allocation (area, iter, widget, renderer, cell_area, allocation);
940 g_warning ("GtkCellAreaClass::get_cell_allocation not implemented for `%s'",
941 g_type_name (G_TYPE_FROM_INSTANCE (area)));
945 gtk_cell_area_event (GtkCellArea *area,
946 GtkCellAreaIter *iter,
949 const GdkRectangle *cell_area,
950 GtkCellRendererState flags)
952 GtkCellAreaClass *class;
954 g_return_val_if_fail (GTK_IS_CELL_AREA (area), 0);
955 g_return_val_if_fail (GTK_IS_CELL_AREA_ITER (iter), 0);
956 g_return_val_if_fail (GTK_IS_WIDGET (widget), 0);
957 g_return_val_if_fail (event != NULL, 0);
958 g_return_val_if_fail (cell_area != NULL, 0);
960 class = GTK_CELL_AREA_GET_CLASS (area);
963 return class->event (area, iter, widget, event, cell_area, flags);
965 g_warning ("GtkCellAreaClass::event not implemented for `%s'",
966 g_type_name (G_TYPE_FROM_INSTANCE (area)));
971 gtk_cell_area_render (GtkCellArea *area,
972 GtkCellAreaIter *iter,
975 const GdkRectangle *cell_area,
976 GtkCellRendererState flags)
978 GtkCellAreaClass *class;
980 g_return_if_fail (GTK_IS_CELL_AREA (area));
981 g_return_if_fail (GTK_IS_CELL_AREA_ITER (iter));
982 g_return_if_fail (GTK_IS_WIDGET (widget));
983 g_return_if_fail (cr != NULL);
984 g_return_if_fail (cell_area != NULL);
986 class = GTK_CELL_AREA_GET_CLASS (area);
989 class->render (area, iter, widget, cr, cell_area, flags);
991 g_warning ("GtkCellAreaClass::render not implemented for `%s'",
992 g_type_name (G_TYPE_FROM_INSTANCE (area)));
995 /*************************************************************
997 *************************************************************/
999 gtk_cell_area_create_iter (GtkCellArea *area)
1001 GtkCellAreaClass *class;
1003 g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL);
1005 class = GTK_CELL_AREA_GET_CLASS (area);
1007 if (class->create_iter)
1008 return class->create_iter (area);
1010 g_warning ("GtkCellAreaClass::create_iter not implemented for `%s'",
1011 g_type_name (G_TYPE_FROM_INSTANCE (area)));
1018 gtk_cell_area_get_request_mode (GtkCellArea *area)
1020 GtkCellAreaClass *class;
1022 g_return_val_if_fail (GTK_IS_CELL_AREA (area),
1023 GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH);
1025 class = GTK_CELL_AREA_GET_CLASS (area);
1027 if (class->get_request_mode)
1028 return class->get_request_mode (area);
1030 g_warning ("GtkCellAreaClass::get_request_mode not implemented for `%s'",
1031 g_type_name (G_TYPE_FROM_INSTANCE (area)));
1033 return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
1037 gtk_cell_area_get_preferred_width (GtkCellArea *area,
1038 GtkCellAreaIter *iter,
1043 GtkCellAreaClass *class;
1045 g_return_if_fail (GTK_IS_CELL_AREA (area));
1046 g_return_if_fail (GTK_IS_WIDGET (widget));
1048 class = GTK_CELL_AREA_GET_CLASS (area);
1050 if (class->get_preferred_width)
1051 class->get_preferred_width (area, iter, widget, minimum_size, natural_size);
1053 g_warning ("GtkCellAreaClass::get_preferred_width not implemented for `%s'",
1054 g_type_name (G_TYPE_FROM_INSTANCE (area)));
1058 gtk_cell_area_get_preferred_height_for_width (GtkCellArea *area,
1059 GtkCellAreaIter *iter,
1062 gint *minimum_height,
1063 gint *natural_height)
1065 GtkCellAreaClass *class;
1067 g_return_if_fail (GTK_IS_CELL_AREA (area));
1068 g_return_if_fail (GTK_IS_WIDGET (widget));
1070 class = GTK_CELL_AREA_GET_CLASS (area);
1071 class->get_preferred_height_for_width (area, iter, widget, width, minimum_height, natural_height);
1075 gtk_cell_area_get_preferred_height (GtkCellArea *area,
1076 GtkCellAreaIter *iter,
1081 GtkCellAreaClass *class;
1083 g_return_if_fail (GTK_IS_CELL_AREA (area));
1084 g_return_if_fail (GTK_IS_WIDGET (widget));
1086 class = GTK_CELL_AREA_GET_CLASS (area);
1088 if (class->get_preferred_height)
1089 class->get_preferred_height (area, iter, widget, minimum_size, natural_size);
1091 g_warning ("GtkCellAreaClass::get_preferred_height not implemented for `%s'",
1092 g_type_name (G_TYPE_FROM_INSTANCE (area)));
1096 gtk_cell_area_get_preferred_width_for_height (GtkCellArea *area,
1097 GtkCellAreaIter *iter,
1100 gint *minimum_width,
1101 gint *natural_width)
1103 GtkCellAreaClass *class;
1105 g_return_if_fail (GTK_IS_CELL_AREA (area));
1106 g_return_if_fail (GTK_IS_WIDGET (widget));
1108 class = GTK_CELL_AREA_GET_CLASS (area);
1109 class->get_preferred_width_for_height (area, iter, widget, height, minimum_width, natural_width);
1112 /*************************************************************
1114 *************************************************************/
1117 * gtk_cell_area_attribute_connect:
1118 * @area: a #GtkCellArea
1119 * @renderer: the #GtkCellRenderer to connect an attribute for
1120 * @attribute: the attribute name
1121 * @column: the #GtkTreeModel column to fetch attribute values from
1123 * Connects an @attribute to apply values from @column for the
1124 * #GtkTreeModel in use.
1127 gtk_cell_area_attribute_connect (GtkCellArea *area,
1128 GtkCellRenderer *renderer,
1129 const gchar *attribute,
1132 GtkCellAreaPrivate *priv;
1134 CellAttribute *cell_attribute;
1136 g_return_if_fail (GTK_IS_CELL_AREA (area));
1137 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1138 g_return_if_fail (attribute != NULL);
1141 info = g_hash_table_lookup (priv->cell_info, renderer);
1145 info = cell_info_new (NULL, NULL, NULL);
1147 g_hash_table_insert (priv->cell_info, renderer, info);
1153 /* Check we are not adding the same attribute twice */
1154 if ((node = g_slist_find_custom (info->attributes, attribute,
1155 (GCompareFunc)cell_attribute_find)) != NULL)
1157 cell_attribute = node->data;
1159 g_warning ("Cannot connect attribute `%s' for cell renderer class `%s' "
1160 "since `%s' is already attributed to column %d",
1162 g_type_name (G_TYPE_FROM_INSTANCE (area)),
1163 attribute, cell_attribute->column);
1168 cell_attribute = cell_attribute_new (renderer, attribute, column);
1170 if (!cell_attribute)
1172 g_warning ("Cannot connect attribute `%s' for cell renderer class `%s' "
1173 "since attribute does not exist",
1175 g_type_name (G_TYPE_FROM_INSTANCE (area)));
1179 info->attributes = g_slist_prepend (info->attributes, cell_attribute);
1183 * gtk_cell_area_attribute_disconnect:
1184 * @area: a #GtkCellArea
1185 * @renderer: the #GtkCellRenderer to disconnect an attribute for
1186 * @attribute: the attribute name
1188 * Disconnects @attribute for the @renderer in @area so that
1189 * attribute will no longer be updated with values from the
1193 gtk_cell_area_attribute_disconnect (GtkCellArea *area,
1194 GtkCellRenderer *renderer,
1195 const gchar *attribute)
1197 GtkCellAreaPrivate *priv;
1199 CellAttribute *cell_attribute;
1202 g_return_if_fail (GTK_IS_CELL_AREA (area));
1203 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1204 g_return_if_fail (attribute != NULL);
1207 info = g_hash_table_lookup (priv->cell_info, renderer);
1211 node = g_slist_find_custom (info->attributes, attribute,
1212 (GCompareFunc)cell_attribute_find);
1215 cell_attribute = node->data;
1217 cell_attribute_free (cell_attribute);
1219 info->attributes = g_slist_delete_link (info->attributes, node);
1225 apply_cell_attributes (GtkCellRenderer *renderer,
1227 AttributeData *data)
1229 CellAttribute *attribute;
1231 GValue value = { 0, };
1232 gboolean is_expander;
1233 gboolean is_expanded;
1235 g_object_freeze_notify (G_OBJECT (renderer));
1237 /* Whether a row expands or is presently expanded can only be
1238 * provided by the view (as these states can vary across views
1239 * accessing the same model).
1241 g_object_get (renderer, "is-expander", &is_expander, NULL);
1242 if (is_expander != data->is_expander)
1243 g_object_set (renderer, "is-expander", data->is_expander, NULL);
1245 g_object_get (renderer, "is-expanded", &is_expanded, NULL);
1246 if (is_expanded != data->is_expanded)
1247 g_object_set (renderer, "is-expanded", data->is_expanded, NULL);
1249 /* Apply the attributes directly to the renderer */
1250 for (list = info->attributes; list; list = list->next)
1252 attribute = list->data;
1254 gtk_tree_model_get_value (data->model, data->iter, attribute->column, &value);
1255 g_object_set_property (G_OBJECT (renderer), attribute->attribute, &value);
1256 g_value_unset (&value);
1259 /* Call any GtkCellLayoutDataFunc that may have been set by the user
1262 info->func (GTK_CELL_LAYOUT (data->area), renderer,
1263 data->model, data->iter, info->data);
1265 g_object_thaw_notify (G_OBJECT (renderer));
1269 * gtk_cell_area_apply_attributes
1270 * @area: a #GtkCellArea
1271 * @tree_model: a #GtkTreeModel to pull values from
1272 * @iter: the #GtkTreeIter in @tree_model to apply values for
1273 * @is_expander: whether @iter has children
1274 * @is_expanded: whether @iter is expanded in the view and
1275 * children are visible
1277 * Applies any connected attributes to the renderers in
1278 * @area by pulling the values from @tree_model.
1281 gtk_cell_area_apply_attributes (GtkCellArea *area,
1282 GtkTreeModel *tree_model,
1284 gboolean is_expander,
1285 gboolean is_expanded)
1287 GtkCellAreaPrivate *priv;
1291 g_return_if_fail (GTK_IS_CELL_AREA (area));
1292 g_return_if_fail (GTK_IS_TREE_MODEL (tree_model));
1293 g_return_if_fail (iter != NULL);
1297 /* Feed in data needed to apply to every renderer */
1299 data.model = tree_model;
1301 data.is_expander = is_expander;
1302 data.is_expanded = is_expanded;
1304 /* Go over any cells that have attributes or custom GtkCellLayoutDataFuncs and
1305 * apply the data from the treemodel */
1306 g_hash_table_foreach (priv->cell_info, (GHFunc)apply_cell_attributes, &data);
1308 /* Update the currently applied path */
1309 g_free (priv->current_path);
1310 path = gtk_tree_model_get_path (tree_model, iter);
1311 priv->current_path = gtk_tree_path_to_string (path);
1312 gtk_tree_path_free (path);
1316 * gtk_cell_area_get_current_path_string:
1317 * @area: a #GtkCellArea
1319 * Gets the current #GtkTreePath string for the currently
1320 * applied #GtkTreeIter, this is implicitly updated when
1321 * gtk_cell_area_apply_attributes() is called and can be
1322 * used to interact with renderers from #GtkCellArea
1326 gtk_cell_area_get_current_path_string (GtkCellArea *area)
1328 GtkCellAreaPrivate *priv;
1330 g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL);
1334 return priv->current_path;
1338 /*************************************************************
1339 * API: Cell Properties *
1340 *************************************************************/
1342 gtk_cell_area_class_install_cell_property (GtkCellAreaClass *aclass,
1346 g_return_if_fail (GTK_IS_CELL_AREA_CLASS (aclass));
1347 g_return_if_fail (G_IS_PARAM_SPEC (pspec));
1348 if (pspec->flags & G_PARAM_WRITABLE)
1349 g_return_if_fail (aclass->set_cell_property != NULL);
1350 if (pspec->flags & G_PARAM_READABLE)
1351 g_return_if_fail (aclass->get_cell_property != NULL);
1352 g_return_if_fail (property_id > 0);
1353 g_return_if_fail (PARAM_SPEC_PARAM_ID (pspec) == 0); /* paranoid */
1354 g_return_if_fail ((pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY)) == 0);
1356 if (g_param_spec_pool_lookup (cell_property_pool, pspec->name, G_OBJECT_CLASS_TYPE (aclass), TRUE))
1358 g_warning (G_STRLOC ": class `%s' already contains a cell property named `%s'",
1359 G_OBJECT_CLASS_NAME (aclass), pspec->name);
1362 g_param_spec_ref (pspec);
1363 g_param_spec_sink (pspec);
1364 PARAM_SPEC_SET_PARAM_ID (pspec, property_id);
1365 g_param_spec_pool_insert (cell_property_pool, pspec, G_OBJECT_CLASS_TYPE (aclass));
1369 gtk_cell_area_class_find_cell_property (GtkCellAreaClass *aclass,
1370 const gchar *property_name)
1372 g_return_val_if_fail (GTK_IS_CELL_AREA_CLASS (aclass), NULL);
1373 g_return_val_if_fail (property_name != NULL, NULL);
1375 return g_param_spec_pool_lookup (cell_property_pool,
1377 G_OBJECT_CLASS_TYPE (aclass),
1382 gtk_cell_area_class_list_cell_properties (GtkCellAreaClass *aclass,
1383 guint *n_properties)
1385 GParamSpec **pspecs;
1388 g_return_val_if_fail (GTK_IS_CELL_AREA_CLASS (aclass), NULL);
1390 pspecs = g_param_spec_pool_list (cell_property_pool,
1391 G_OBJECT_CLASS_TYPE (aclass),
1400 gtk_cell_area_add_with_properties (GtkCellArea *area,
1401 GtkCellRenderer *renderer,
1402 const gchar *first_prop_name,
1405 GtkCellAreaClass *class;
1407 g_return_if_fail (GTK_IS_CELL_AREA (area));
1408 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1410 class = GTK_CELL_AREA_GET_CLASS (area);
1416 class->add (area, renderer);
1418 va_start (var_args, first_prop_name);
1419 gtk_cell_area_cell_set_valist (area, renderer, first_prop_name, var_args);
1423 g_warning ("GtkCellAreaClass::add not implemented for `%s'",
1424 g_type_name (G_TYPE_FROM_INSTANCE (area)));
1428 gtk_cell_area_cell_set (GtkCellArea *area,
1429 GtkCellRenderer *renderer,
1430 const gchar *first_prop_name,
1435 g_return_if_fail (GTK_IS_CELL_AREA (area));
1436 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1438 va_start (var_args, first_prop_name);
1439 gtk_cell_area_cell_set_valist (area, renderer, first_prop_name, var_args);
1444 gtk_cell_area_cell_get (GtkCellArea *area,
1445 GtkCellRenderer *renderer,
1446 const gchar *first_prop_name,
1451 g_return_if_fail (GTK_IS_CELL_AREA (area));
1452 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1454 va_start (var_args, first_prop_name);
1455 gtk_cell_area_cell_get_valist (area, renderer, first_prop_name, var_args);
1460 area_get_cell_property (GtkCellArea *area,
1461 GtkCellRenderer *renderer,
1465 GtkCellAreaClass *class = g_type_class_peek (pspec->owner_type);
1467 class->get_cell_property (area, renderer, PARAM_SPEC_PARAM_ID (pspec), value, pspec);
1471 area_set_cell_property (GtkCellArea *area,
1472 GtkCellRenderer *renderer,
1474 const GValue *value)
1476 GValue tmp_value = { 0, };
1477 GtkCellAreaClass *class = g_type_class_peek (pspec->owner_type);
1479 /* provide a copy to work from, convert (if necessary) and validate */
1480 g_value_init (&tmp_value, G_PARAM_SPEC_VALUE_TYPE (pspec));
1481 if (!g_value_transform (value, &tmp_value))
1482 g_warning ("unable to set cell property `%s' of type `%s' from value of type `%s'",
1484 g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)),
1485 G_VALUE_TYPE_NAME (value));
1486 else if (g_param_value_validate (pspec, &tmp_value) && !(pspec->flags & G_PARAM_LAX_VALIDATION))
1488 gchar *contents = g_strdup_value_contents (value);
1490 g_warning ("value \"%s\" of type `%s' is invalid for property `%s' of type `%s'",
1492 G_VALUE_TYPE_NAME (value),
1494 g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)));
1499 class->set_cell_property (area, renderer, PARAM_SPEC_PARAM_ID (pspec), &tmp_value, pspec);
1501 g_value_unset (&tmp_value);
1505 gtk_cell_area_cell_set_valist (GtkCellArea *area,
1506 GtkCellRenderer *renderer,
1507 const gchar *first_property_name,
1512 g_return_if_fail (GTK_IS_CELL_AREA (area));
1513 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1515 name = first_property_name;
1518 GValue value = { 0, };
1519 gchar *error = NULL;
1521 g_param_spec_pool_lookup (cell_property_pool, name,
1522 G_OBJECT_TYPE (area), TRUE);
1525 g_warning ("%s: cell area class `%s' has no cell property named `%s'",
1526 G_STRLOC, G_OBJECT_TYPE_NAME (area), name);
1529 if (!(pspec->flags & G_PARAM_WRITABLE))
1531 g_warning ("%s: cell property `%s' of cell area class `%s' is not writable",
1532 G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (area));
1536 g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
1537 G_VALUE_COLLECT (&value, var_args, 0, &error);
1540 g_warning ("%s: %s", G_STRLOC, error);
1543 /* we purposely leak the value here, it might not be
1544 * in a sane state if an error condition occoured
1548 area_set_cell_property (area, renderer, pspec, &value);
1549 g_value_unset (&value);
1550 name = va_arg (var_args, gchar*);
1555 gtk_cell_area_cell_get_valist (GtkCellArea *area,
1556 GtkCellRenderer *renderer,
1557 const gchar *first_property_name,
1562 g_return_if_fail (GTK_IS_CELL_AREA (area));
1563 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1565 name = first_property_name;
1568 GValue value = { 0, };
1572 pspec = g_param_spec_pool_lookup (cell_property_pool, name,
1573 G_OBJECT_TYPE (area), TRUE);
1576 g_warning ("%s: cell area class `%s' has no cell property named `%s'",
1577 G_STRLOC, G_OBJECT_TYPE_NAME (area), name);
1580 if (!(pspec->flags & G_PARAM_READABLE))
1582 g_warning ("%s: cell property `%s' of cell area class `%s' is not readable",
1583 G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (area));
1587 g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
1588 area_get_cell_property (area, renderer, pspec, &value);
1589 G_VALUE_LCOPY (&value, var_args, 0, &error);
1592 g_warning ("%s: %s", G_STRLOC, error);
1594 g_value_unset (&value);
1597 g_value_unset (&value);
1598 name = va_arg (var_args, gchar*);
1603 gtk_cell_area_cell_set_property (GtkCellArea *area,
1604 GtkCellRenderer *renderer,
1605 const gchar *property_name,
1606 const GValue *value)
1610 g_return_if_fail (GTK_IS_CELL_AREA (area));
1611 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1612 g_return_if_fail (property_name != NULL);
1613 g_return_if_fail (G_IS_VALUE (value));
1615 pspec = g_param_spec_pool_lookup (cell_property_pool, property_name,
1616 G_OBJECT_TYPE (area), TRUE);
1618 g_warning ("%s: cell area class `%s' has no cell property named `%s'",
1619 G_STRLOC, G_OBJECT_TYPE_NAME (area), property_name);
1620 else if (!(pspec->flags & G_PARAM_WRITABLE))
1621 g_warning ("%s: cell property `%s' of cell area class `%s' is not writable",
1622 G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (area));
1625 area_set_cell_property (area, renderer, pspec, value);
1630 gtk_cell_area_cell_get_property (GtkCellArea *area,
1631 GtkCellRenderer *renderer,
1632 const gchar *property_name,
1637 g_return_if_fail (GTK_IS_CELL_AREA (area));
1638 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1639 g_return_if_fail (property_name != NULL);
1640 g_return_if_fail (G_IS_VALUE (value));
1642 pspec = g_param_spec_pool_lookup (cell_property_pool, property_name,
1643 G_OBJECT_TYPE (area), TRUE);
1645 g_warning ("%s: cell area class `%s' has no cell property named `%s'",
1646 G_STRLOC, G_OBJECT_TYPE_NAME (area), property_name);
1647 else if (!(pspec->flags & G_PARAM_READABLE))
1648 g_warning ("%s: cell property `%s' of cell area class `%s' is not readable",
1649 G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (area));
1652 GValue *prop_value, tmp_value = { 0, };
1654 /* auto-conversion of the callers value type
1656 if (G_VALUE_TYPE (value) == G_PARAM_SPEC_VALUE_TYPE (pspec))
1658 g_value_reset (value);
1661 else if (!g_value_type_transformable (G_PARAM_SPEC_VALUE_TYPE (pspec), G_VALUE_TYPE (value)))
1663 g_warning ("can't retrieve cell property `%s' of type `%s' as value of type `%s'",
1665 g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)),
1666 G_VALUE_TYPE_NAME (value));
1671 g_value_init (&tmp_value, G_PARAM_SPEC_VALUE_TYPE (pspec));
1672 prop_value = &tmp_value;
1675 area_get_cell_property (area, renderer, pspec, prop_value);
1677 if (prop_value != value)
1679 g_value_transform (prop_value, value);
1680 g_value_unset (&tmp_value);
1685 /*************************************************************
1687 *************************************************************/
1690 * gtk_cell_area_grab_focus:
1691 * @area: a #GtkCellArea
1692 * @direction: the #GtkDirectionType from which focus came
1694 * This should be called by the @area's owning layout widget
1695 * when focus should be passed to @area for a given row data.
1697 * Note that after applying new attributes for @area that
1698 * gtk_cell_area_update_focus() should be called and
1699 * gtk_cell_area_can_focus() should be checked before trying
1700 * to pass focus to @area.
1702 * Implementing #GtkCellArea classes should implement this
1703 * method to receive focus in it's own way particular to
1704 * how it lays out cells.
1707 gtk_cell_area_grab_focus (GtkCellArea *area,
1708 GtkDirectionType direction)
1710 GtkCellAreaClass *class;
1712 g_return_if_fail (GTK_IS_CELL_AREA (area));
1714 class = GTK_CELL_AREA_GET_CLASS (area);
1716 if (class->grab_focus)
1717 class->grab_focus (area, direction);
1719 g_warning ("GtkCellAreaClass::grab_focus not implemented for `%s'",
1720 g_type_name (G_TYPE_FROM_INSTANCE (area)));
1724 * gtk_cell_area_focus_leave:
1725 * @area: a #GtkCellArea
1726 * @direction: the #GtkDirectionType in which focus
1728 * @path: the current #GtkTreePath string for the
1729 * event which was handled by @area
1731 * Notifies that focus is to leave @area in the
1734 * This is called by #GtkCellArea implementations upon
1735 * handling a key event that caused focus to leave the
1736 * cell. The resulting signal can be handled by the
1737 * owning layouting widget to decide which new @area
1738 * to pass focus to and from what @direction. Or to
1739 * pass focus along to an entirely new data row.
1742 gtk_cell_area_focus_leave (GtkCellArea *area,
1743 GtkDirectionType direction,
1746 g_return_if_fail (GTK_IS_CELL_AREA (area));
1748 g_signal_emit (area, cell_area_signals[SIGNAL_FOCUS_LEAVE], 0, direction, path);
1752 * gtk_cell_area_update_focus:
1753 * @area: a #GtkCellArea
1755 * Updates focus information on @area for a given
1758 * After calling gtk_cell_area_apply_attributes() to
1759 * the @area this method should be called to update
1760 * information about whether the @area can focus and
1761 * which is the cell currently in focus.
1764 gtk_cell_area_update_focus (GtkCellArea *area)
1766 g_return_if_fail (GTK_IS_CELL_AREA (area));
1768 GTK_CELL_AREA_GET_CLASS (area)->update_focus (area);
1772 * gtk_cell_area_set_can_focus:
1773 * @area: a #GtkCellArea
1774 * @can_focus: whether @area can receive focus
1776 * This is generally called from GtkCellArea::update_focus()
1777 * implementations to update if the @area can focus after
1778 * applying new row data attributes.
1781 gtk_cell_area_set_can_focus (GtkCellArea *area,
1784 GtkCellAreaPrivate *priv;
1786 g_return_if_fail (GTK_IS_CELL_AREA (area));
1790 if (priv->can_focus != can_focus)
1792 priv->can_focus = can_focus;
1797 * gtk_cell_area_get_can_focus:
1798 * @area: a #GtkCellArea
1800 * Returns whether the area can receive keyboard focus,
1801 * after applying new attributes to @area,
1802 * gtk_cell_area_update_focus() needs to be called before
1803 * calling this method.
1805 * Returns: whether @area can receive focus.
1808 gtk_cell_area_get_can_focus (GtkCellArea *area)
1810 GtkCellAreaPrivate *priv;
1812 g_return_val_if_fail (GTK_IS_CELL_AREA (area), FALSE);
1816 return priv->can_focus;
1821 * gtk_cell_area_set_focus_cell:
1822 * @area: a #GtkCellArea
1823 * @focus_cell: the #GtkCellRenderer to give focus to
1825 * This is generally called from #GtkCellArea implementations
1826 * either gtk_cell_area_grab_focus() or gtk_cell_area_update_focus()
1827 * is called. It's also up to the #GtkCellArea implementation
1828 * to update the focused cell when receiving events from
1829 * gtk_cell_area_event() appropriately.
1832 gtk_cell_area_set_focus_cell (GtkCellArea *area,
1833 GtkCellRenderer *renderer)
1835 GtkCellAreaPrivate *priv;
1837 g_return_if_fail (GTK_IS_CELL_AREA (area));
1838 g_return_if_fail (renderer == NULL || GTK_IS_CELL_RENDERER (renderer));
1842 if (priv->focus_cell != renderer)
1844 if (priv->focus_cell)
1845 g_object_unref (priv->focus_cell);
1847 priv->focus_cell = renderer;
1849 if (priv->focus_cell)
1850 g_object_ref (priv->focus_cell);
1852 g_object_notify (G_OBJECT (area), "focus-cell");
1857 * gtk_cell_area_get_focus_cell:
1858 * @area: a #GtkCellArea
1860 * Retrieves the currently focused cell for @area
1862 * Returns: the currently focused cell in @area.
1865 gtk_cell_area_get_focus_cell (GtkCellArea *area)
1867 GtkCellAreaPrivate *priv;
1869 g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL);
1873 return priv->focus_cell;
1877 /*************************************************************
1878 * API: Cell Activation/Editing *
1879 *************************************************************/
1881 gtk_cell_area_editing_started (GtkCellArea *area,
1882 GtkCellRenderer *renderer,
1883 GtkCellEditable *editable)
1885 g_signal_emit (area, cell_area_signals[SIGNAL_EDITING_STARTED], 0,
1886 renderer, editable, area->priv->current_path);
1890 gtk_cell_area_editing_canceled (GtkCellArea *area,
1891 GtkCellRenderer *renderer)
1893 g_signal_emit (area, cell_area_signals[SIGNAL_EDITING_CANCELED], 0, renderer);
1897 gtk_cell_area_editing_done (GtkCellArea *area,
1898 GtkCellRenderer *renderer,
1899 GtkCellEditable *editable)
1901 g_signal_emit (area, cell_area_signals[SIGNAL_EDITING_DONE], 0, renderer, editable);
1905 gtk_cell_area_remove_editable (GtkCellArea *area,
1906 GtkCellRenderer *renderer,
1907 GtkCellEditable *editable)
1909 g_signal_emit (area, cell_area_signals[SIGNAL_REMOVE_EDITABLE], 0, renderer, editable);
1913 cell_area_editing_done_cb (GtkCellEditable *editable,
1916 GtkCellAreaPrivate *priv = area->priv;
1918 g_assert (priv->edit_widget == editable);
1919 g_assert (priv->edited_cell != NULL);
1921 gtk_cell_area_editing_done (area, priv->edited_cell, priv->edit_widget);
1925 cell_area_remove_widget_cb (GtkCellEditable *editable,
1928 GtkCellAreaPrivate *priv = area->priv;
1930 g_assert (priv->edit_widget == editable);
1931 g_assert (priv->edited_cell != NULL);
1933 gtk_cell_area_remove_editable (area, priv->edited_cell, priv->edit_widget);
1935 /* Now that we're done with editing the widget and it can be removed,
1936 * remove our references to the widget and disconnect handlers */
1937 gtk_cell_area_set_edited_cell (area, NULL);
1938 gtk_cell_area_set_edit_widget (area, NULL);
1942 gtk_cell_area_set_edited_cell (GtkCellArea *area,
1943 GtkCellRenderer *renderer)
1945 GtkCellAreaPrivate *priv;
1947 g_return_if_fail (GTK_IS_CELL_AREA (area));
1948 g_return_if_fail (renderer == NULL || GTK_IS_CELL_RENDERER (renderer));
1952 if (priv->edited_cell != renderer)
1954 if (priv->edited_cell)
1955 g_object_unref (priv->edited_cell);
1957 priv->edited_cell = renderer;
1959 if (priv->edited_cell)
1960 g_object_ref (priv->edited_cell);
1962 g_object_notify (G_OBJECT (area), "edited-cell");
1967 gtk_cell_area_get_edited_cell (GtkCellArea *area)
1969 GtkCellAreaPrivate *priv;
1971 g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL);
1975 return priv->edited_cell;
1979 gtk_cell_area_set_edit_widget (GtkCellArea *area,
1980 GtkCellEditable *editable)
1982 GtkCellAreaPrivate *priv;
1984 g_return_if_fail (GTK_IS_CELL_AREA (area));
1985 g_return_if_fail (editable == NULL || GTK_IS_CELL_EDITABLE (editable));
1989 if (priv->edit_widget != editable)
1991 if (priv->edit_widget)
1993 g_signal_handler_disconnect (priv->edit_widget, priv->editing_done_id);
1994 g_signal_handler_disconnect (priv->edit_widget, priv->remove_widget_id);
1996 g_object_unref (priv->edit_widget);
1999 priv->edit_widget = editable;
2001 if (priv->edit_widget)
2003 priv->editing_done_id =
2004 g_signal_connect (priv->edit_widget, "editing-done",
2005 G_CALLBACK (cell_area_editing_done_cb), area);
2006 priv->remove_widget_id =
2007 g_signal_connect (priv->edit_widget, "remove-widget",
2008 G_CALLBACK (cell_area_remove_widget_cb), area);
2010 g_object_ref (priv->edit_widget);
2013 g_object_notify (G_OBJECT (area), "edit-widget");
2018 gtk_cell_area_get_edit_widget (GtkCellArea *area)
2020 GtkCellAreaPrivate *priv;
2022 g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL);
2026 return priv->edit_widget;
2030 gtk_cell_area_activate_cell (GtkCellArea *area,
2032 GtkCellRenderer *renderer,
2034 const GdkRectangle *cell_area,
2035 GtkCellRendererState flags)
2037 GtkCellRendererMode mode;
2038 GdkRectangle inner_area;
2039 GtkCellAreaPrivate *priv;
2041 g_return_val_if_fail (GTK_IS_CELL_AREA (area), FALSE);
2042 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
2043 g_return_val_if_fail (GTK_IS_CELL_RENDERER (renderer), FALSE);
2044 g_return_val_if_fail (event != NULL, FALSE);
2045 g_return_val_if_fail (cell_area != NULL, FALSE);
2049 /* Remove margins from the background area to produce the cell area.
2051 * XXX Maybe have to do some rtl mode treatment here...
2053 gtk_cell_area_inner_cell_area (area, cell_area, &inner_area);
2055 g_object_get (renderer, "mode", &mode, NULL);
2057 if (mode == GTK_CELL_RENDERER_MODE_ACTIVATABLE)
2059 if (gtk_cell_renderer_activate (renderer,
2067 else if (mode == GTK_CELL_RENDERER_MODE_EDITABLE)
2069 GtkCellEditable *editable_widget;
2072 gtk_cell_renderer_start_editing (renderer,
2079 if (editable_widget != NULL)
2081 g_return_val_if_fail (GTK_IS_CELL_EDITABLE (editable_widget), FALSE);
2083 gtk_cell_area_set_edited_cell (area, renderer);
2084 gtk_cell_area_set_edit_widget (area, editable_widget);
2086 /* Signal that editing started so that callers can get
2087 * a handle on the editable_widget */
2088 gtk_cell_area_editing_started (area, priv->focus_cell, editable_widget);
2098 gtk_cell_area_stop_editing (GtkCellArea *area,
2101 GtkCellAreaPrivate *priv;
2103 g_return_if_fail (GTK_IS_CELL_AREA (area));
2107 if (priv->edited_cell)
2109 /* Stop editing of the cell renderer */
2110 gtk_cell_renderer_stop_editing (priv->edited_cell, canceled);
2112 /* Signal that editing has been canceled */
2114 gtk_cell_area_editing_canceled (area, priv->edited_cell);
2116 /* Remove any references to the editable widget */
2117 gtk_cell_area_set_edited_cell (area, NULL);
2118 gtk_cell_area_set_edit_widget (area, NULL);
2122 /*************************************************************
2124 *************************************************************/
2126 gtk_cell_area_get_cell_margin_left (GtkCellArea *area)
2128 g_return_val_if_fail (GTK_IS_CELL_AREA (area), 0);
2130 return area->priv->cell_border.left;
2134 gtk_cell_area_set_cell_margin_left (GtkCellArea *area,
2137 GtkCellAreaPrivate *priv;
2139 g_return_if_fail (GTK_IS_CELL_AREA (area));
2143 if (priv->cell_border.left != margin)
2145 priv->cell_border.left = margin;
2147 g_object_notify (G_OBJECT (area), "margin-left");
2152 gtk_cell_area_get_cell_margin_right (GtkCellArea *area)
2154 g_return_val_if_fail (GTK_IS_CELL_AREA (area), 0);
2156 return area->priv->cell_border.right;
2160 gtk_cell_area_set_cell_margin_right (GtkCellArea *area,
2163 GtkCellAreaPrivate *priv;
2165 g_return_if_fail (GTK_IS_CELL_AREA (area));
2169 if (priv->cell_border.right != margin)
2171 priv->cell_border.right = margin;
2173 g_object_notify (G_OBJECT (area), "margin-right");
2178 gtk_cell_area_get_cell_margin_top (GtkCellArea *area)
2180 g_return_val_if_fail (GTK_IS_CELL_AREA (area), 0);
2182 return area->priv->cell_border.top;
2186 gtk_cell_area_set_cell_margin_top (GtkCellArea *area,
2189 GtkCellAreaPrivate *priv;
2191 g_return_if_fail (GTK_IS_CELL_AREA (area));
2195 if (priv->cell_border.top != margin)
2197 priv->cell_border.top = margin;
2199 g_object_notify (G_OBJECT (area), "margin-top");
2204 gtk_cell_area_get_cell_margin_bottom (GtkCellArea *area)
2206 g_return_val_if_fail (GTK_IS_CELL_AREA (area), 0);
2208 return area->priv->cell_border.bottom;
2212 gtk_cell_area_set_cell_margin_bottom (GtkCellArea *area,
2215 GtkCellAreaPrivate *priv;
2217 g_return_if_fail (GTK_IS_CELL_AREA (area));
2221 if (priv->cell_border.bottom != margin)
2223 priv->cell_border.bottom = margin;
2225 g_object_notify (G_OBJECT (area), "margin-bottom");
2230 gtk_cell_area_inner_cell_area (GtkCellArea *area,
2231 const GdkRectangle *background_area,
2232 GdkRectangle *cell_area)
2234 GtkCellAreaPrivate *priv;
2236 g_return_if_fail (GTK_IS_CELL_AREA (area));
2237 g_return_if_fail (background_area != NULL);
2238 g_return_if_fail (cell_area != NULL);
2242 *cell_area = *background_area;
2244 cell_area->x += priv->cell_border.left;
2245 cell_area->width -= (priv->cell_border.left + priv->cell_border.right);
2246 cell_area->y += priv->cell_border.top;
2247 cell_area->height -= (priv->cell_border.top + priv->cell_border.bottom);
2251 gtk_cell_area_request_renderer (GtkCellArea *area,
2252 GtkCellRenderer *renderer,
2253 GtkOrientation orientation,
2259 GtkCellAreaPrivate *priv;
2261 g_return_if_fail (GTK_IS_CELL_AREA (area));
2262 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
2263 g_return_if_fail (GTK_IS_WIDGET (widget));
2264 g_return_if_fail (minimum_size != NULL);
2265 g_return_if_fail (natural_size != NULL);
2269 if (orientation == GTK_ORIENTATION_HORIZONTAL)
2272 gtk_cell_renderer_get_preferred_width (renderer, widget, minimum_size, natural_size);
2275 for_size = MAX (0, for_size - (priv->cell_border.top + priv->cell_border.bottom));
2277 gtk_cell_renderer_get_preferred_width_for_height (renderer, widget, for_size,
2278 minimum_size, natural_size);
2281 *minimum_size += (priv->cell_border.left + priv->cell_border.right);
2282 *natural_size += (priv->cell_border.left + priv->cell_border.right);
2284 else /* GTK_ORIENTATION_VERTICAL */
2287 gtk_cell_renderer_get_preferred_height (renderer, widget, minimum_size, natural_size);
2290 for_size = MAX (0, for_size - (priv->cell_border.left + priv->cell_border.right));
2292 gtk_cell_renderer_get_preferred_height_for_width (renderer, widget, for_size,
2293 minimum_size, natural_size);
2296 *minimum_size += (priv->cell_border.top + priv->cell_border.bottom);
2297 *natural_size += (priv->cell_border.top + priv->cell_border.bottom);