3 * Copyright (C) 2010 Openismus GmbH
6 * Tristan Van Berkom <tristanvb@openismus.com>
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
31 #include "gtkcelllayout.h"
32 #include "gtkcellarea.h"
33 #include "gtkcellareaiter.h"
34 #include "gtkmarshalers.h"
35 #include "gtkprivate.h"
37 #include <gobject/gvaluecollector.h>
41 static void gtk_cell_area_dispose (GObject *object);
42 static void gtk_cell_area_finalize (GObject *object);
43 static void gtk_cell_area_set_property (GObject *object,
47 static void gtk_cell_area_get_property (GObject *object,
52 /* GtkCellAreaClass */
53 static void gtk_cell_area_real_get_preferred_height_for_width (GtkCellArea *area,
54 GtkCellAreaIter *iter,
58 gint *natural_height);
59 static void gtk_cell_area_real_get_preferred_width_for_height (GtkCellArea *area,
60 GtkCellAreaIter *iter,
66 /* GtkCellLayoutIface */
67 static void gtk_cell_area_cell_layout_init (GtkCellLayoutIface *iface);
68 static void gtk_cell_area_pack_default (GtkCellLayout *cell_layout,
69 GtkCellRenderer *renderer,
71 static void gtk_cell_area_clear (GtkCellLayout *cell_layout);
72 static void gtk_cell_area_add_attribute (GtkCellLayout *cell_layout,
73 GtkCellRenderer *renderer,
74 const gchar *attribute,
76 static void gtk_cell_area_set_cell_data_func (GtkCellLayout *cell_layout,
77 GtkCellRenderer *cell,
78 GtkCellLayoutDataFunc func,
80 GDestroyNotify destroy);
81 static void gtk_cell_area_clear_attributes (GtkCellLayout *cell_layout,
82 GtkCellRenderer *renderer);
83 static void gtk_cell_area_reorder (GtkCellLayout *cell_layout,
84 GtkCellRenderer *cell,
86 static GList *gtk_cell_area_get_cells (GtkCellLayout *cell_layout);
88 /* Attribute/Cell metadata */
90 const gchar *attribute;
97 GtkCellLayoutDataFunc func;
99 GDestroyNotify destroy;
102 static CellInfo *cell_info_new (GtkCellLayoutDataFunc func,
104 GDestroyNotify destroy);
105 static void cell_info_free (CellInfo *info);
106 static CellAttribute *cell_attribute_new (GtkCellRenderer *renderer,
107 const gchar *attribute,
109 static void cell_attribute_free (CellAttribute *attribute);
110 static gint cell_attribute_find (CellAttribute *cell_attribute,
111 const gchar *attribute);
113 /* Struct to pass data along while looping over
114 * cell renderers to apply attributes
122 struct _GtkCellAreaPrivate
124 GHashTable *cell_info;
126 GtkBorder cell_border;
128 GtkCellRenderer *focus_cell;
135 PROP_CELL_MARGIN_LEFT,
136 PROP_CELL_MARGIN_RIGHT,
137 PROP_CELL_MARGIN_TOP,
138 PROP_CELL_MARGIN_BOTTOM
146 /* Keep the paramspec pool internal, no need to deliver notifications
147 * on cells. at least no percieved need for now */
148 static GParamSpecPool *cell_property_pool = NULL;
149 static guint cell_area_signals[LAST_SIGNAL] = { 0 };
151 #define PARAM_SPEC_PARAM_ID(pspec) ((pspec)->param_id)
152 #define PARAM_SPEC_SET_PARAM_ID(pspec, id) ((pspec)->param_id = (id))
155 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GtkCellArea, gtk_cell_area, G_TYPE_INITIALLY_UNOWNED,
156 G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
157 gtk_cell_area_cell_layout_init));
160 gtk_cell_area_init (GtkCellArea *area)
162 GtkCellAreaPrivate *priv;
164 area->priv = G_TYPE_INSTANCE_GET_PRIVATE (area,
169 priv->cell_info = g_hash_table_new_full (g_direct_hash,
172 (GDestroyNotify)cell_info_free);
174 priv->cell_border.left = 0;
175 priv->cell_border.right = 0;
176 priv->cell_border.top = 0;
177 priv->cell_border.bottom = 0;
179 priv->focus_cell = NULL;
183 gtk_cell_area_class_init (GtkCellAreaClass *class)
185 GObjectClass *object_class = G_OBJECT_CLASS (class);
188 object_class->dispose = gtk_cell_area_dispose;
189 object_class->finalize = gtk_cell_area_finalize;
190 object_class->get_property = gtk_cell_area_get_property;
191 object_class->set_property = gtk_cell_area_set_property;
195 class->remove = NULL;
196 class->forall = NULL;
198 class->render = NULL;
201 class->create_iter = NULL;
202 class->get_request_mode = NULL;
203 class->get_preferred_width = NULL;
204 class->get_preferred_height = NULL;
205 class->get_preferred_height_for_width = gtk_cell_area_real_get_preferred_height_for_width;
206 class->get_preferred_width_for_height = gtk_cell_area_real_get_preferred_width_for_height;
209 class->grab_focus = NULL;
212 cell_area_signals[SIGNAL_FOCUS_LEAVE] =
213 g_signal_new (I_("focus-leave"),
214 G_TYPE_FROM_CLASS (object_class),
216 0, /* Class offset (just a notification, no class handler) */
218 _gtk_marshal_VOID__ENUM_STRING,
220 GTK_TYPE_DIRECTION_TYPE, G_TYPE_STRING);
224 g_object_class_install_property (object_class,
225 PROP_CELL_MARGIN_LEFT,
228 P_("Margin on Left"),
229 P_("Pixels of extra space on the left side of each cell"),
233 GTK_PARAM_READWRITE));
235 g_object_class_install_property (object_class,
236 PROP_CELL_MARGIN_RIGHT,
238 ("cell-margin-right",
239 P_("Margin on Right"),
240 P_("Pixels of extra space on the right side of each cell"),
244 GTK_PARAM_READWRITE));
246 g_object_class_install_property (object_class,
247 PROP_CELL_MARGIN_TOP,
251 P_("Pixels of extra space on the top side of each cell"),
255 GTK_PARAM_READWRITE));
257 g_object_class_install_property (object_class,
258 PROP_CELL_MARGIN_BOTTOM,
260 ("cell-margin-bottom",
261 P_("Margin on Bottom"),
262 P_("Pixels of extra space on the bottom side of each cell"),
266 GTK_PARAM_READWRITE));
268 /* Pool for Cell Properties */
269 if (!cell_property_pool)
270 cell_property_pool = g_param_spec_pool_new (FALSE);
272 g_type_class_add_private (object_class, sizeof (GtkCellAreaPrivate));
275 /*************************************************************
277 *************************************************************/
279 cell_info_new (GtkCellLayoutDataFunc func,
281 GDestroyNotify destroy)
283 CellInfo *info = g_slice_new (CellInfo);
285 info->attributes = NULL;
288 info->destroy = destroy;
294 cell_info_free (CellInfo *info)
297 info->destroy (info->data);
299 g_slist_foreach (info->attributes, (GFunc)cell_attribute_free, NULL);
300 g_slist_free (info->attributes);
302 g_slice_free (CellInfo, info);
305 static CellAttribute *
306 cell_attribute_new (GtkCellRenderer *renderer,
307 const gchar *attribute,
312 /* Check if the attribute really exists and point to
313 * the property string installed on the cell renderer
314 * class (dont dup the string)
316 pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (renderer), attribute);
320 CellAttribute *cell_attribute = g_slice_new (CellAttribute);
322 cell_attribute->attribute = pspec->name;
323 cell_attribute->column = column;
325 return cell_attribute;
332 cell_attribute_free (CellAttribute *attribute)
334 g_slice_free (CellAttribute, attribute);
337 /* GCompareFunc for g_slist_find_custom() */
339 cell_attribute_find (CellAttribute *cell_attribute,
340 const gchar *attribute)
342 return g_strcmp0 (cell_attribute->attribute, attribute);
345 /*************************************************************
347 *************************************************************/
349 gtk_cell_area_finalize (GObject *object)
351 GtkCellArea *area = GTK_CELL_AREA (object);
352 GtkCellAreaPrivate *priv = area->priv;
354 /* All cell renderers should already be removed at this point,
355 * just kill our hash table here.
357 g_hash_table_destroy (priv->cell_info);
359 G_OBJECT_CLASS (gtk_cell_area_parent_class)->finalize (object);
364 gtk_cell_area_dispose (GObject *object)
366 /* This removes every cell renderer that may be added to the GtkCellArea,
367 * subclasses should be breaking references to the GtkCellRenderers
370 gtk_cell_layout_clear (GTK_CELL_LAYOUT (object));
372 /* Remove any ref to a focused cell */
373 gtk_cell_area_set_focus_cell (GTK_CELL_AREA (object), NULL);
375 G_OBJECT_CLASS (gtk_cell_area_parent_class)->dispose (object);
379 gtk_cell_area_set_property (GObject *object,
384 GtkCellArea *area = GTK_CELL_AREA (object);
388 case PROP_CELL_MARGIN_LEFT:
389 gtk_cell_area_set_cell_margin_left (area, g_value_get_int (value));
391 case PROP_CELL_MARGIN_RIGHT:
392 gtk_cell_area_set_cell_margin_right (area, g_value_get_int (value));
394 case PROP_CELL_MARGIN_TOP:
395 gtk_cell_area_set_cell_margin_top (area, g_value_get_int (value));
397 case PROP_CELL_MARGIN_BOTTOM:
398 gtk_cell_area_set_cell_margin_bottom (area, g_value_get_int (value));
401 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
407 gtk_cell_area_get_property (GObject *object,
412 GtkCellArea *area = GTK_CELL_AREA (object);
413 GtkCellAreaPrivate *priv = area->priv;
417 case PROP_CELL_MARGIN_LEFT:
418 g_value_set_int (value, priv->cell_border.left);
420 case PROP_CELL_MARGIN_RIGHT:
421 g_value_set_int (value, priv->cell_border.right);
423 case PROP_CELL_MARGIN_TOP:
424 g_value_set_int (value, priv->cell_border.top);
426 case PROP_CELL_MARGIN_BOTTOM:
427 g_value_set_int (value, priv->cell_border.bottom);
430 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
435 /*************************************************************
437 *************************************************************/
439 gtk_cell_area_real_get_preferred_height_for_width (GtkCellArea *area,
440 GtkCellAreaIter *iter,
443 gint *minimum_height,
444 gint *natural_height)
446 /* If the area doesnt do height-for-width, fallback on base preferred height */
447 GTK_CELL_AREA_GET_CLASS (area)->get_preferred_width (area, iter, widget, minimum_height, natural_height);
451 gtk_cell_area_real_get_preferred_width_for_height (GtkCellArea *area,
452 GtkCellAreaIter *iter,
458 /* If the area doesnt do width-for-height, fallback on base preferred width */
459 GTK_CELL_AREA_GET_CLASS (area)->get_preferred_width (area, iter, widget, minimum_width, natural_width);
462 /*************************************************************
463 * GtkCellLayoutIface *
464 *************************************************************/
466 gtk_cell_area_cell_layout_init (GtkCellLayoutIface *iface)
468 iface->pack_start = gtk_cell_area_pack_default;
469 iface->pack_end = gtk_cell_area_pack_default;
470 iface->clear = gtk_cell_area_clear;
471 iface->add_attribute = gtk_cell_area_add_attribute;
472 iface->set_cell_data_func = gtk_cell_area_set_cell_data_func;
473 iface->clear_attributes = gtk_cell_area_clear_attributes;
474 iface->reorder = gtk_cell_area_reorder;
475 iface->get_cells = gtk_cell_area_get_cells;
479 gtk_cell_area_pack_default (GtkCellLayout *cell_layout,
480 GtkCellRenderer *renderer,
483 gtk_cell_area_add (GTK_CELL_AREA (cell_layout), renderer);
487 gtk_cell_area_clear (GtkCellLayout *cell_layout)
489 GtkCellArea *area = GTK_CELL_AREA (cell_layout);
491 gtk_cell_layout_get_cells (cell_layout);
493 for (l = cells; l; l = l->next)
495 GtkCellRenderer *renderer = l->data;
496 gtk_cell_area_remove (area, renderer);
503 gtk_cell_area_add_attribute (GtkCellLayout *cell_layout,
504 GtkCellRenderer *renderer,
505 const gchar *attribute,
508 gtk_cell_area_attribute_connect (GTK_CELL_AREA (cell_layout),
509 renderer, attribute, column);
513 gtk_cell_area_set_cell_data_func (GtkCellLayout *cell_layout,
514 GtkCellRenderer *renderer,
515 GtkCellLayoutDataFunc func,
517 GDestroyNotify destroy)
519 GtkCellArea *area = GTK_CELL_AREA (cell_layout);
520 GtkCellAreaPrivate *priv = area->priv;
523 info = g_hash_table_lookup (priv->cell_info, renderer);
527 if (info->destroy && info->data)
528 info->destroy (info->data);
533 info->data = func_data;
534 info->destroy = destroy;
540 info->destroy = NULL;
545 info = cell_info_new (func, func_data, destroy);
547 g_hash_table_insert (priv->cell_info, renderer, info);
552 gtk_cell_area_clear_attributes (GtkCellLayout *cell_layout,
553 GtkCellRenderer *renderer)
555 GtkCellArea *area = GTK_CELL_AREA (cell_layout);
556 GtkCellAreaPrivate *priv = area->priv;
559 info = g_hash_table_lookup (priv->cell_info, renderer);
563 g_slist_foreach (info->attributes, (GFunc)cell_attribute_free, NULL);
564 g_slist_free (info->attributes);
566 info->attributes = NULL;
571 gtk_cell_area_reorder (GtkCellLayout *cell_layout,
572 GtkCellRenderer *cell,
575 g_warning ("GtkCellLayout::reorder not implemented for `%s'",
576 g_type_name (G_TYPE_FROM_INSTANCE (cell_layout)));
580 accum_cells (GtkCellRenderer *renderer,
583 *accum = g_list_prepend (*accum, renderer);
587 gtk_cell_area_get_cells (GtkCellLayout *cell_layout)
591 gtk_cell_area_forall (GTK_CELL_AREA (cell_layout),
592 (GtkCellCallback)accum_cells,
595 return g_list_reverse (cells);
599 /*************************************************************
601 *************************************************************/
603 gtk_cell_area_add (GtkCellArea *area,
604 GtkCellRenderer *renderer)
606 GtkCellAreaClass *class;
608 g_return_if_fail (GTK_IS_CELL_AREA (area));
609 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
611 class = GTK_CELL_AREA_GET_CLASS (area);
614 class->add (area, renderer);
616 g_warning ("GtkCellAreaClass::add not implemented for `%s'",
617 g_type_name (G_TYPE_FROM_INSTANCE (area)));
621 gtk_cell_area_remove (GtkCellArea *area,
622 GtkCellRenderer *renderer)
624 GtkCellAreaClass *class;
625 GtkCellAreaPrivate *priv;
627 g_return_if_fail (GTK_IS_CELL_AREA (area));
628 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
630 class = GTK_CELL_AREA_GET_CLASS (area);
633 /* Remove any custom attributes and custom cell data func here first */
634 g_hash_table_remove (priv->cell_info, renderer);
637 class->remove (area, renderer);
639 g_warning ("GtkCellAreaClass::remove not implemented for `%s'",
640 g_type_name (G_TYPE_FROM_INSTANCE (area)));
644 gtk_cell_area_forall (GtkCellArea *area,
645 GtkCellCallback callback,
646 gpointer callback_data)
648 GtkCellAreaClass *class;
650 g_return_if_fail (GTK_IS_CELL_AREA (area));
651 g_return_if_fail (callback != NULL);
653 class = GTK_CELL_AREA_GET_CLASS (area);
656 class->forall (area, callback, callback_data);
658 g_warning ("GtkCellAreaClass::forall not implemented for `%s'",
659 g_type_name (G_TYPE_FROM_INSTANCE (area)));
663 gtk_cell_area_event (GtkCellArea *area,
664 GtkCellAreaIter *iter,
667 const GdkRectangle *cell_area,
668 GtkCellRendererState flags)
670 GtkCellAreaClass *class;
672 g_return_val_if_fail (GTK_IS_CELL_AREA (area), 0);
673 g_return_val_if_fail (GTK_IS_CELL_AREA_ITER (iter), 0);
674 g_return_val_if_fail (GTK_IS_WIDGET (widget), 0);
675 g_return_val_if_fail (event != NULL, 0);
676 g_return_val_if_fail (cell_area != NULL, 0);
678 class = GTK_CELL_AREA_GET_CLASS (area);
681 return class->event (area, iter, widget, event, cell_area, flags);
683 g_warning ("GtkCellAreaClass::event not implemented for `%s'",
684 g_type_name (G_TYPE_FROM_INSTANCE (area)));
689 gtk_cell_area_render (GtkCellArea *area,
690 GtkCellAreaIter *iter,
693 const GdkRectangle *cell_area,
694 GtkCellRendererState flags)
696 GtkCellAreaClass *class;
698 g_return_if_fail (GTK_IS_CELL_AREA (area));
699 g_return_if_fail (GTK_IS_CELL_AREA_ITER (iter));
700 g_return_if_fail (GTK_IS_WIDGET (widget));
701 g_return_if_fail (cr != NULL);
702 g_return_if_fail (cell_area != NULL);
704 class = GTK_CELL_AREA_GET_CLASS (area);
707 class->render (area, iter, widget, cr, cell_area, flags);
709 g_warning ("GtkCellAreaClass::render not implemented for `%s'",
710 g_type_name (G_TYPE_FROM_INSTANCE (area)));
713 /*************************************************************
715 *************************************************************/
717 gtk_cell_area_create_iter (GtkCellArea *area)
719 GtkCellAreaClass *class;
721 g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL);
723 class = GTK_CELL_AREA_GET_CLASS (area);
725 if (class->create_iter)
726 return class->create_iter (area);
728 g_warning ("GtkCellAreaClass::create_iter not implemented for `%s'",
729 g_type_name (G_TYPE_FROM_INSTANCE (area)));
736 gtk_cell_area_get_request_mode (GtkCellArea *area)
738 GtkCellAreaClass *class;
740 g_return_val_if_fail (GTK_IS_CELL_AREA (area),
741 GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH);
743 class = GTK_CELL_AREA_GET_CLASS (area);
745 if (class->get_request_mode)
746 return class->get_request_mode (area);
748 g_warning ("GtkCellAreaClass::get_request_mode not implemented for `%s'",
749 g_type_name (G_TYPE_FROM_INSTANCE (area)));
751 return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
755 gtk_cell_area_get_preferred_width (GtkCellArea *area,
756 GtkCellAreaIter *iter,
761 GtkCellAreaClass *class;
763 g_return_if_fail (GTK_IS_CELL_AREA (area));
764 g_return_if_fail (GTK_IS_WIDGET (widget));
766 class = GTK_CELL_AREA_GET_CLASS (area);
768 if (class->get_preferred_width)
769 class->get_preferred_width (area, iter, widget, minimum_size, natural_size);
771 g_warning ("GtkCellAreaClass::get_preferred_width not implemented for `%s'",
772 g_type_name (G_TYPE_FROM_INSTANCE (area)));
776 gtk_cell_area_get_preferred_height_for_width (GtkCellArea *area,
777 GtkCellAreaIter *iter,
780 gint *minimum_height,
781 gint *natural_height)
783 GtkCellAreaClass *class;
785 g_return_if_fail (GTK_IS_CELL_AREA (area));
786 g_return_if_fail (GTK_IS_WIDGET (widget));
788 class = GTK_CELL_AREA_GET_CLASS (area);
789 class->get_preferred_height_for_width (area, iter, widget, width, minimum_height, natural_height);
793 gtk_cell_area_get_preferred_height (GtkCellArea *area,
794 GtkCellAreaIter *iter,
799 GtkCellAreaClass *class;
801 g_return_if_fail (GTK_IS_CELL_AREA (area));
802 g_return_if_fail (GTK_IS_WIDGET (widget));
804 class = GTK_CELL_AREA_GET_CLASS (area);
806 if (class->get_preferred_height)
807 class->get_preferred_height (area, iter, widget, minimum_size, natural_size);
809 g_warning ("GtkCellAreaClass::get_preferred_height not implemented for `%s'",
810 g_type_name (G_TYPE_FROM_INSTANCE (area)));
814 gtk_cell_area_get_preferred_width_for_height (GtkCellArea *area,
815 GtkCellAreaIter *iter,
821 GtkCellAreaClass *class;
823 g_return_if_fail (GTK_IS_CELL_AREA (area));
824 g_return_if_fail (GTK_IS_WIDGET (widget));
826 class = GTK_CELL_AREA_GET_CLASS (area);
827 class->get_preferred_width_for_height (area, iter, widget, height, minimum_width, natural_width);
830 /*************************************************************
832 *************************************************************/
834 gtk_cell_area_attribute_connect (GtkCellArea *area,
835 GtkCellRenderer *renderer,
836 const gchar *attribute,
839 GtkCellAreaPrivate *priv;
841 CellAttribute *cell_attribute;
843 g_return_if_fail (GTK_IS_CELL_AREA (area));
844 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
845 g_return_if_fail (attribute != NULL);
848 info = g_hash_table_lookup (priv->cell_info, renderer);
852 info = cell_info_new (NULL, NULL, NULL);
854 g_hash_table_insert (priv->cell_info, renderer, info);
860 /* Check we are not adding the same attribute twice */
861 if ((node = g_slist_find_custom (info->attributes, attribute,
862 (GCompareFunc)cell_attribute_find)) != NULL)
864 cell_attribute = node->data;
866 g_warning ("Cannot connect attribute `%s' for cell renderer class `%s' "
867 "since `%s' is already attributed to column %d",
869 g_type_name (G_TYPE_FROM_INSTANCE (area)),
870 attribute, cell_attribute->column);
875 cell_attribute = cell_attribute_new (renderer, attribute, column);
879 g_warning ("Cannot connect attribute `%s' for cell renderer class `%s' "
880 "since attribute does not exist",
882 g_type_name (G_TYPE_FROM_INSTANCE (area)));
886 info->attributes = g_slist_prepend (info->attributes, cell_attribute);
890 gtk_cell_area_attribute_disconnect (GtkCellArea *area,
891 GtkCellRenderer *renderer,
892 const gchar *attribute)
894 GtkCellAreaPrivate *priv;
896 CellAttribute *cell_attribute;
899 g_return_if_fail (GTK_IS_CELL_AREA (area));
900 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
901 g_return_if_fail (attribute != NULL);
904 info = g_hash_table_lookup (priv->cell_info, renderer);
908 node = g_slist_find_custom (info->attributes, attribute,
909 (GCompareFunc)cell_attribute_find);
912 cell_attribute = node->data;
914 cell_attribute_free (cell_attribute);
916 info->attributes = g_slist_delete_link (info->attributes, node);
922 apply_cell_attributes (GtkCellRenderer *renderer,
926 CellAttribute *attribute;
928 GValue value = { 0, };
930 /* Apply the attributes directly to the renderer */
931 for (list = info->attributes; list; list = list->next)
933 attribute = list->data;
935 gtk_tree_model_get_value (data->model, data->iter, attribute->column, &value);
936 g_object_set_property (G_OBJECT (renderer), attribute->attribute, &value);
937 g_value_unset (&value);
940 /* Call any GtkCellLayoutDataFunc that may have been set by the user
943 info->func (GTK_CELL_LAYOUT (data->area), renderer,
944 data->model, data->iter, info->data);
948 gtk_cell_area_apply_attributes (GtkCellArea *area,
949 GtkTreeModel *tree_model,
952 GtkCellAreaPrivate *priv;
955 g_return_if_fail (GTK_IS_CELL_AREA (area));
956 g_return_if_fail (GTK_IS_TREE_MODEL (tree_model));
957 g_return_if_fail (iter != NULL);
961 /* Go over any cells that have attributes or custom GtkCellLayoutDataFuncs and
962 * apply the data from the treemodel */
963 g_hash_table_foreach (priv->cell_info, (GHFunc)apply_cell_attributes, &data);
966 /*************************************************************
967 * API: Cell Properties *
968 *************************************************************/
970 gtk_cell_area_class_install_cell_property (GtkCellAreaClass *aclass,
974 g_return_if_fail (GTK_IS_CELL_AREA_CLASS (aclass));
975 g_return_if_fail (G_IS_PARAM_SPEC (pspec));
976 if (pspec->flags & G_PARAM_WRITABLE)
977 g_return_if_fail (aclass->set_cell_property != NULL);
978 if (pspec->flags & G_PARAM_READABLE)
979 g_return_if_fail (aclass->get_cell_property != NULL);
980 g_return_if_fail (property_id > 0);
981 g_return_if_fail (PARAM_SPEC_PARAM_ID (pspec) == 0); /* paranoid */
982 g_return_if_fail ((pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY)) == 0);
984 if (g_param_spec_pool_lookup (cell_property_pool, pspec->name, G_OBJECT_CLASS_TYPE (aclass), TRUE))
986 g_warning (G_STRLOC ": class `%s' already contains a cell property named `%s'",
987 G_OBJECT_CLASS_NAME (aclass), pspec->name);
990 g_param_spec_ref (pspec);
991 g_param_spec_sink (pspec);
992 PARAM_SPEC_SET_PARAM_ID (pspec, property_id);
993 g_param_spec_pool_insert (cell_property_pool, pspec, G_OBJECT_CLASS_TYPE (aclass));
997 gtk_cell_area_class_find_cell_property (GtkCellAreaClass *aclass,
998 const gchar *property_name)
1000 g_return_val_if_fail (GTK_IS_CELL_AREA_CLASS (aclass), NULL);
1001 g_return_val_if_fail (property_name != NULL, NULL);
1003 return g_param_spec_pool_lookup (cell_property_pool,
1005 G_OBJECT_CLASS_TYPE (aclass),
1010 gtk_cell_area_class_list_cell_properties (GtkCellAreaClass *aclass,
1011 guint *n_properties)
1013 GParamSpec **pspecs;
1016 g_return_val_if_fail (GTK_IS_CELL_AREA_CLASS (aclass), NULL);
1018 pspecs = g_param_spec_pool_list (cell_property_pool,
1019 G_OBJECT_CLASS_TYPE (aclass),
1028 gtk_cell_area_add_with_properties (GtkCellArea *area,
1029 GtkCellRenderer *renderer,
1030 const gchar *first_prop_name,
1033 GtkCellAreaClass *class;
1035 g_return_if_fail (GTK_IS_CELL_AREA (area));
1036 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1038 class = GTK_CELL_AREA_GET_CLASS (area);
1044 class->add (area, renderer);
1046 va_start (var_args, first_prop_name);
1047 gtk_cell_area_cell_set_valist (area, renderer, first_prop_name, var_args);
1051 g_warning ("GtkCellAreaClass::add not implemented for `%s'",
1052 g_type_name (G_TYPE_FROM_INSTANCE (area)));
1056 gtk_cell_area_cell_set (GtkCellArea *area,
1057 GtkCellRenderer *renderer,
1058 const gchar *first_prop_name,
1063 g_return_if_fail (GTK_IS_CELL_AREA (area));
1064 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1066 va_start (var_args, first_prop_name);
1067 gtk_cell_area_cell_set_valist (area, renderer, first_prop_name, var_args);
1072 gtk_cell_area_cell_get (GtkCellArea *area,
1073 GtkCellRenderer *renderer,
1074 const gchar *first_prop_name,
1079 g_return_if_fail (GTK_IS_CELL_AREA (area));
1080 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1082 va_start (var_args, first_prop_name);
1083 gtk_cell_area_cell_get_valist (area, renderer, first_prop_name, var_args);
1088 area_get_cell_property (GtkCellArea *area,
1089 GtkCellRenderer *renderer,
1093 GtkCellAreaClass *class = g_type_class_peek (pspec->owner_type);
1095 class->get_cell_property (area, renderer, PARAM_SPEC_PARAM_ID (pspec), value, pspec);
1099 area_set_cell_property (GtkCellArea *area,
1100 GtkCellRenderer *renderer,
1102 const GValue *value)
1104 GValue tmp_value = { 0, };
1105 GtkCellAreaClass *class = g_type_class_peek (pspec->owner_type);
1107 /* provide a copy to work from, convert (if necessary) and validate */
1108 g_value_init (&tmp_value, G_PARAM_SPEC_VALUE_TYPE (pspec));
1109 if (!g_value_transform (value, &tmp_value))
1110 g_warning ("unable to set cell property `%s' of type `%s' from value of type `%s'",
1112 g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)),
1113 G_VALUE_TYPE_NAME (value));
1114 else if (g_param_value_validate (pspec, &tmp_value) && !(pspec->flags & G_PARAM_LAX_VALIDATION))
1116 gchar *contents = g_strdup_value_contents (value);
1118 g_warning ("value \"%s\" of type `%s' is invalid for property `%s' of type `%s'",
1120 G_VALUE_TYPE_NAME (value),
1122 g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)));
1127 class->set_cell_property (area, renderer, PARAM_SPEC_PARAM_ID (pspec), &tmp_value, pspec);
1129 g_value_unset (&tmp_value);
1133 gtk_cell_area_cell_set_valist (GtkCellArea *area,
1134 GtkCellRenderer *renderer,
1135 const gchar *first_property_name,
1140 g_return_if_fail (GTK_IS_CELL_AREA (area));
1141 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1143 name = first_property_name;
1146 GValue value = { 0, };
1147 gchar *error = NULL;
1149 g_param_spec_pool_lookup (cell_property_pool, name,
1150 G_OBJECT_TYPE (area), TRUE);
1153 g_warning ("%s: cell area class `%s' has no cell property named `%s'",
1154 G_STRLOC, G_OBJECT_TYPE_NAME (area), name);
1157 if (!(pspec->flags & G_PARAM_WRITABLE))
1159 g_warning ("%s: cell property `%s' of cell area class `%s' is not writable",
1160 G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (area));
1164 g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
1165 G_VALUE_COLLECT (&value, var_args, 0, &error);
1168 g_warning ("%s: %s", G_STRLOC, error);
1171 /* we purposely leak the value here, it might not be
1172 * in a sane state if an error condition occoured
1176 area_set_cell_property (area, renderer, pspec, &value);
1177 g_value_unset (&value);
1178 name = va_arg (var_args, gchar*);
1183 gtk_cell_area_cell_get_valist (GtkCellArea *area,
1184 GtkCellRenderer *renderer,
1185 const gchar *first_property_name,
1190 g_return_if_fail (GTK_IS_CELL_AREA (area));
1191 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1193 name = first_property_name;
1196 GValue value = { 0, };
1200 pspec = g_param_spec_pool_lookup (cell_property_pool, name,
1201 G_OBJECT_TYPE (area), TRUE);
1204 g_warning ("%s: cell area class `%s' has no cell property named `%s'",
1205 G_STRLOC, G_OBJECT_TYPE_NAME (area), name);
1208 if (!(pspec->flags & G_PARAM_READABLE))
1210 g_warning ("%s: cell property `%s' of cell area class `%s' is not readable",
1211 G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (area));
1215 g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
1216 area_get_cell_property (area, renderer, pspec, &value);
1217 G_VALUE_LCOPY (&value, var_args, 0, &error);
1220 g_warning ("%s: %s", G_STRLOC, error);
1222 g_value_unset (&value);
1225 g_value_unset (&value);
1226 name = va_arg (var_args, gchar*);
1231 gtk_cell_area_cell_set_property (GtkCellArea *area,
1232 GtkCellRenderer *renderer,
1233 const gchar *property_name,
1234 const GValue *value)
1238 g_return_if_fail (GTK_IS_CELL_AREA (area));
1239 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1240 g_return_if_fail (property_name != NULL);
1241 g_return_if_fail (G_IS_VALUE (value));
1243 pspec = g_param_spec_pool_lookup (cell_property_pool, property_name,
1244 G_OBJECT_TYPE (area), TRUE);
1246 g_warning ("%s: cell area class `%s' has no cell property named `%s'",
1247 G_STRLOC, G_OBJECT_TYPE_NAME (area), property_name);
1248 else if (!(pspec->flags & G_PARAM_WRITABLE))
1249 g_warning ("%s: cell property `%s' of cell area class `%s' is not writable",
1250 G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (area));
1253 area_set_cell_property (area, renderer, pspec, value);
1258 gtk_cell_area_cell_get_property (GtkCellArea *area,
1259 GtkCellRenderer *renderer,
1260 const gchar *property_name,
1265 g_return_if_fail (GTK_IS_CELL_AREA (area));
1266 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1267 g_return_if_fail (property_name != NULL);
1268 g_return_if_fail (G_IS_VALUE (value));
1270 pspec = g_param_spec_pool_lookup (cell_property_pool, property_name,
1271 G_OBJECT_TYPE (area), TRUE);
1273 g_warning ("%s: cell area class `%s' has no cell property named `%s'",
1274 G_STRLOC, G_OBJECT_TYPE_NAME (area), property_name);
1275 else if (!(pspec->flags & G_PARAM_READABLE))
1276 g_warning ("%s: cell property `%s' of cell area class `%s' is not readable",
1277 G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (area));
1280 GValue *prop_value, tmp_value = { 0, };
1282 /* auto-conversion of the callers value type
1284 if (G_VALUE_TYPE (value) == G_PARAM_SPEC_VALUE_TYPE (pspec))
1286 g_value_reset (value);
1289 else if (!g_value_type_transformable (G_PARAM_SPEC_VALUE_TYPE (pspec), G_VALUE_TYPE (value)))
1291 g_warning ("can't retrieve cell property `%s' of type `%s' as value of type `%s'",
1293 g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)),
1294 G_VALUE_TYPE_NAME (value));
1299 g_value_init (&tmp_value, G_PARAM_SPEC_VALUE_TYPE (pspec));
1300 prop_value = &tmp_value;
1303 area_get_cell_property (area, renderer, pspec, prop_value);
1305 if (prop_value != value)
1307 g_value_transform (prop_value, value);
1308 g_value_unset (&tmp_value);
1313 /*************************************************************
1315 *************************************************************/
1317 gtk_cell_area_grab_focus (GtkCellArea *area,
1318 GtkDirectionType direction)
1320 GtkCellAreaClass *class;
1322 g_return_if_fail (GTK_IS_CELL_AREA (area));
1324 class = GTK_CELL_AREA_GET_CLASS (area);
1326 if (class->grab_focus)
1327 class->grab_focus (area, direction);
1329 g_warning ("GtkCellAreaClass::grab_focus not implemented for `%s'",
1330 g_type_name (G_TYPE_FROM_INSTANCE (area)));
1334 gtk_cell_area_focus_leave (GtkCellArea *area,
1335 GtkDirectionType direction,
1338 g_return_if_fail (GTK_IS_CELL_AREA (area));
1340 g_signal_emit (area, cell_area_signals[SIGNAL_FOCUS_LEAVE], 0, direction, path);
1344 gtk_cell_area_set_can_focus (GtkCellArea *area,
1347 GtkCellAreaPrivate *priv;
1349 g_return_if_fail (GTK_IS_CELL_AREA (area));
1353 if (priv->can_focus != can_focus)
1355 priv->can_focus = can_focus;
1360 gtk_cell_area_get_can_focus (GtkCellArea *area)
1362 GtkCellAreaPrivate *priv;
1364 g_return_val_if_fail (GTK_IS_CELL_AREA (area), FALSE);
1368 return priv->can_focus;
1372 gtk_cell_area_set_focus_cell (GtkCellArea *area,
1373 GtkCellRenderer *renderer)
1375 GtkCellAreaPrivate *priv;
1377 g_return_if_fail (GTK_IS_CELL_AREA (area));
1378 g_return_if_fail (renderer == NULL || GTK_IS_CELL_RENDERER (renderer));
1382 if (priv->focus_cell != renderer)
1384 if (priv->focus_cell)
1385 g_object_unref (priv->focus_cell);
1387 priv->focus_cell = renderer;
1389 if (priv->focus_cell)
1390 g_object_ref (priv->focus_cell);
1395 gtk_cell_area_get_focus_cell (GtkCellArea *area)
1397 GtkCellAreaPrivate *priv;
1399 g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL);
1403 return priv->focus_cell;
1406 /*************************************************************
1408 *************************************************************/
1410 gtk_cell_area_get_cell_margin_left (GtkCellArea *area)
1412 g_return_val_if_fail (GTK_IS_CELL_AREA (area), 0);
1414 return area->priv->cell_border.left;
1418 gtk_cell_area_set_cell_margin_left (GtkCellArea *area,
1421 GtkCellAreaPrivate *priv;
1423 g_return_if_fail (GTK_IS_CELL_AREA (area));
1427 if (priv->cell_border.left != margin)
1429 priv->cell_border.left = margin;
1431 g_object_notify (G_OBJECT (area), "margin-left");
1436 gtk_cell_area_get_cell_margin_right (GtkCellArea *area)
1438 g_return_val_if_fail (GTK_IS_CELL_AREA (area), 0);
1440 return area->priv->cell_border.right;
1444 gtk_cell_area_set_cell_margin_right (GtkCellArea *area,
1447 GtkCellAreaPrivate *priv;
1449 g_return_if_fail (GTK_IS_CELL_AREA (area));
1453 if (priv->cell_border.right != margin)
1455 priv->cell_border.right = margin;
1457 g_object_notify (G_OBJECT (area), "margin-right");
1462 gtk_cell_area_get_cell_margin_top (GtkCellArea *area)
1464 g_return_val_if_fail (GTK_IS_CELL_AREA (area), 0);
1466 return area->priv->cell_border.top;
1470 gtk_cell_area_set_cell_margin_top (GtkCellArea *area,
1473 GtkCellAreaPrivate *priv;
1475 g_return_if_fail (GTK_IS_CELL_AREA (area));
1479 if (priv->cell_border.top != margin)
1481 priv->cell_border.top = margin;
1483 g_object_notify (G_OBJECT (area), "margin-top");
1488 gtk_cell_area_get_cell_margin_bottom (GtkCellArea *area)
1490 g_return_val_if_fail (GTK_IS_CELL_AREA (area), 0);
1492 return area->priv->cell_border.bottom;
1496 gtk_cell_area_set_cell_margin_bottom (GtkCellArea *area,
1499 GtkCellAreaPrivate *priv;
1501 g_return_if_fail (GTK_IS_CELL_AREA (area));
1505 if (priv->cell_border.bottom != margin)
1507 priv->cell_border.bottom = margin;
1509 g_object_notify (G_OBJECT (area), "margin-bottom");
1513 /* For convenience in area implementations */
1515 gtk_cell_area_inner_cell_area (GtkCellArea *area,
1516 GdkRectangle *background_area,
1517 GdkRectangle *cell_area)
1519 GtkCellAreaPrivate *priv;
1521 g_return_if_fail (GTK_IS_CELL_AREA (area));
1522 g_return_if_fail (background_area != NULL);
1523 g_return_if_fail (cell_area != NULL);
1527 *cell_area = *background_area;
1529 cell_area->x += priv->cell_border.left;
1530 cell_area->width -= (priv->cell_border.left + priv->cell_border.right);
1531 cell_area->y += priv->cell_border.top;
1532 cell_area->height -= (priv->cell_border.top + priv->cell_border.bottom);
1536 gtk_cell_area_request_renderer (GtkCellArea *area,
1537 GtkCellRenderer *renderer,
1538 GtkOrientation orientation,
1544 GtkCellAreaPrivate *priv;
1546 g_return_if_fail (GTK_IS_CELL_AREA (area));
1547 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
1548 g_return_if_fail (GTK_IS_WIDGET (widget));
1549 g_return_if_fail (minimum_size != NULL);
1550 g_return_if_fail (natural_size != NULL);
1554 if (orientation == GTK_ORIENTATION_HORIZONTAL)
1557 gtk_cell_renderer_get_preferred_width (renderer, widget, minimum_size, natural_size);
1560 for_size = MAX (0, for_size - (priv->cell_border.top + priv->cell_border.bottom));
1562 gtk_cell_renderer_get_preferred_width_for_height (renderer, widget, for_size,
1563 minimum_size, natural_size);
1566 *minimum_size += (priv->cell_border.left + priv->cell_border.right);
1567 *natural_size += (priv->cell_border.left + priv->cell_border.right);
1569 else /* GTK_ORIENTATION_VERTICAL */
1572 gtk_cell_renderer_get_preferred_height (renderer, widget, minimum_size, natural_size);
1575 for_size = MAX (0, for_size - (priv->cell_border.left + priv->cell_border.right));
1577 gtk_cell_renderer_get_preferred_height_for_width (renderer, widget, for_size,
1578 minimum_size, natural_size);
1581 *minimum_size += (priv->cell_border.top + priv->cell_border.bottom);
1582 *natural_size += (priv->cell_border.top + priv->cell_border.bottom);