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.
26 * SECTION:gtkcellareabox
27 * @Short_Description: A cell area that renders #GtkCellRenderers into a row or a column
28 * @Title: GtkCellAreaBox
30 * The #GtkCellAreaBox renders cell renderers into a row or a column depending on
31 * its #GtkOrientation.
33 * GtkCellAreaBox uses a notion of <emphasis>packing</emphasis>. Packing
34 * refers to adding cell renderers with reference to a particular position
35 * in a #GtkCellAreaBox. There are two reference positions: the
36 * <emphasis>start</emphasis> and the <emphasis>end</emphasis> of the box.
37 * When the #GtkCellAreaBox is oriented in the %GTK_ORIENTATION_VERTICAL orientation,
38 * the start is defined as the top of the box and the end is defined as the bottom.
39 * In the %GTK_ORIENTATION_HORIZONTAL orientation start is defined as the
40 * left side and the end is defined as the right side.
42 * Alignments of #GtkCellRenderers rendered in adjacent rows can be configured
43 * by configuring the #GtkCellAreaBox:align child cell property with
44 * gtk_cell_area_cell_set_property() or by specifying the "align" argument
45 * to gtk_cell_area_box_pack_start() and gtk_cell_area_box_pack_end().
50 #include "gtkorientable.h"
51 #include "gtkcelllayout.h"
52 #include "gtkcellareabox.h"
53 #include "gtkcellareaboxcontext.h"
54 #include "gtkprivate.h"
58 static void gtk_cell_area_box_finalize (GObject *object);
59 static void gtk_cell_area_box_dispose (GObject *object);
60 static void gtk_cell_area_box_set_property (GObject *object,
64 static void gtk_cell_area_box_get_property (GObject *object,
69 /* GtkCellAreaClass */
70 static void gtk_cell_area_box_add (GtkCellArea *area,
71 GtkCellRenderer *renderer);
72 static void gtk_cell_area_box_remove (GtkCellArea *area,
73 GtkCellRenderer *renderer);
74 static void gtk_cell_area_box_forall (GtkCellArea *area,
75 GtkCellCallback callback,
76 gpointer callback_data);
77 static void gtk_cell_area_box_get_cell_allocation (GtkCellArea *area,
78 GtkCellAreaContext *context,
80 GtkCellRenderer *renderer,
81 const GdkRectangle *cell_area,
82 GdkRectangle *allocation);
83 static gint gtk_cell_area_box_event (GtkCellArea *area,
84 GtkCellAreaContext *context,
87 const GdkRectangle *cell_area,
88 GtkCellRendererState flags);
89 static void gtk_cell_area_box_render (GtkCellArea *area,
90 GtkCellAreaContext *context,
93 const GdkRectangle *background_area,
94 const GdkRectangle *cell_area,
95 GtkCellRendererState flags,
96 gboolean paint_focus);
97 static void gtk_cell_area_box_set_cell_property (GtkCellArea *area,
98 GtkCellRenderer *renderer,
102 static void gtk_cell_area_box_get_cell_property (GtkCellArea *area,
103 GtkCellRenderer *renderer,
107 static GtkCellAreaContext *gtk_cell_area_box_create_context (GtkCellArea *area);
108 static GtkSizeRequestMode gtk_cell_area_box_get_request_mode (GtkCellArea *area);
109 static void gtk_cell_area_box_get_preferred_width (GtkCellArea *area,
110 GtkCellAreaContext *context,
113 gint *natural_width);
114 static void gtk_cell_area_box_get_preferred_height (GtkCellArea *area,
115 GtkCellAreaContext *context,
117 gint *minimum_height,
118 gint *natural_height);
119 static void gtk_cell_area_box_get_preferred_height_for_width (GtkCellArea *area,
120 GtkCellAreaContext *context,
123 gint *minimum_height,
124 gint *natural_height);
125 static void gtk_cell_area_box_get_preferred_width_for_height (GtkCellArea *area,
126 GtkCellAreaContext *context,
130 gint *natural_width);
131 static gboolean gtk_cell_area_box_focus (GtkCellArea *area,
132 GtkDirectionType direction);
134 /* GtkCellLayoutIface */
135 static void gtk_cell_area_box_cell_layout_init (GtkCellLayoutIface *iface);
136 static void gtk_cell_area_box_layout_pack_start (GtkCellLayout *cell_layout,
137 GtkCellRenderer *renderer,
139 static void gtk_cell_area_box_layout_pack_end (GtkCellLayout *cell_layout,
140 GtkCellRenderer *renderer,
142 static void gtk_cell_area_box_layout_reorder (GtkCellLayout *cell_layout,
143 GtkCellRenderer *renderer,
147 /* CellInfo/CellGroup metadata handling and convenience functions */
149 GtkCellRenderer *renderer;
151 guint expand : 1; /* Whether the cell expands */
152 guint pack : 1; /* Whether the cell is packed from the start or end */
153 guint align : 1; /* Whether to align this cell's position with adjacent rows */
161 guint expand_cells : 8;
165 GtkCellRenderer *renderer;
171 static CellInfo *cell_info_new (GtkCellRenderer *renderer,
175 static void cell_info_free (CellInfo *info);
176 static gint cell_info_find (CellInfo *info,
177 GtkCellRenderer *renderer);
179 static AllocatedCell *allocated_cell_new (GtkCellRenderer *renderer,
182 static void allocated_cell_free (AllocatedCell *cell);
183 static GList *list_consecutive_cells (GtkCellAreaBox *box);
184 static gint count_expand_groups (GtkCellAreaBox *box);
185 static void context_weak_notify (GtkCellAreaBox *box,
186 GtkCellAreaBoxContext *dead_context);
187 static void reset_contexts (GtkCellAreaBox *box);
188 static void init_context_groups (GtkCellAreaBox *box);
189 static void init_context_group (GtkCellAreaBox *box,
190 GtkCellAreaBoxContext *context);
191 static GSList *get_allocated_cells (GtkCellAreaBox *box,
192 GtkCellAreaBoxContext *context,
198 struct _GtkCellAreaBoxPrivate
200 GtkOrientation orientation;
223 G_DEFINE_TYPE_WITH_CODE (GtkCellAreaBox, gtk_cell_area_box, GTK_TYPE_CELL_AREA,
224 G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
225 gtk_cell_area_box_cell_layout_init)
226 G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL));
228 #define OPPOSITE_ORIENTATION(orientation) \
229 ((orientation) == GTK_ORIENTATION_HORIZONTAL ? \
230 GTK_ORIENTATION_VERTICAL : GTK_ORIENTATION_HORIZONTAL)
233 gtk_cell_area_box_init (GtkCellAreaBox *box)
235 GtkCellAreaBoxPrivate *priv;
237 box->priv = G_TYPE_INSTANCE_GET_PRIVATE (box,
238 GTK_TYPE_CELL_AREA_BOX,
239 GtkCellAreaBoxPrivate);
242 priv->orientation = GTK_ORIENTATION_HORIZONTAL;
243 priv->groups = g_array_new (FALSE, TRUE, sizeof (CellGroup));
245 priv->contexts = NULL;
250 gtk_cell_area_box_class_init (GtkCellAreaBoxClass *class)
252 GObjectClass *object_class = G_OBJECT_CLASS (class);
253 GtkCellAreaClass *area_class = GTK_CELL_AREA_CLASS (class);
256 object_class->finalize = gtk_cell_area_box_finalize;
257 object_class->dispose = gtk_cell_area_box_dispose;
258 object_class->set_property = gtk_cell_area_box_set_property;
259 object_class->get_property = gtk_cell_area_box_get_property;
261 /* GtkCellAreaClass */
262 area_class->add = gtk_cell_area_box_add;
263 area_class->remove = gtk_cell_area_box_remove;
264 area_class->forall = gtk_cell_area_box_forall;
265 area_class->get_cell_allocation = gtk_cell_area_box_get_cell_allocation;
266 area_class->event = gtk_cell_area_box_event;
267 area_class->render = gtk_cell_area_box_render;
268 area_class->set_cell_property = gtk_cell_area_box_set_cell_property;
269 area_class->get_cell_property = gtk_cell_area_box_get_cell_property;
271 area_class->create_context = gtk_cell_area_box_create_context;
272 area_class->get_request_mode = gtk_cell_area_box_get_request_mode;
273 area_class->get_preferred_width = gtk_cell_area_box_get_preferred_width;
274 area_class->get_preferred_height = gtk_cell_area_box_get_preferred_height;
275 area_class->get_preferred_height_for_width = gtk_cell_area_box_get_preferred_height_for_width;
276 area_class->get_preferred_width_for_height = gtk_cell_area_box_get_preferred_width_for_height;
278 area_class->focus = gtk_cell_area_box_focus;
281 g_object_class_override_property (object_class, PROP_ORIENTATION, "orientation");
284 * GtkCellAreaBox:spacing:
286 * The amount of space to reserve between cells.
290 g_object_class_install_property (object_class,
292 g_param_spec_int ("spacing",
294 P_("Space which is inserted between cells"),
298 GTK_PARAM_READWRITE));
300 /* Cell Properties */
302 * GtkCellAreaBox:expand:
304 * Whether the cell renderer should receive extra space when the area receives
305 * more than its natural size.
309 gtk_cell_area_class_install_cell_property (area_class,
314 P_("Whether the cell expands"),
316 GTK_PARAM_READWRITE));
319 * GtkCellAreaBox:align:
321 * Whether the cell renderer should be aligned in adjacent rows.
325 gtk_cell_area_class_install_cell_property (area_class,
330 P_("Whether cell should align with adjacent rows"),
332 GTK_PARAM_READWRITE));
335 * GtkCellAreaBox:pack-type:
337 * A GtkPackType indicating whether the cell renderer is packed with reference to the
338 * start or end of the area.
342 gtk_cell_area_class_install_cell_property (area_class,
347 P_("A GtkPackType indicating whether the cell is packed with "
348 "reference to the start or end of the cell area"),
349 GTK_TYPE_PACK_TYPE, GTK_PACK_START,
350 GTK_PARAM_READWRITE));
352 g_type_class_add_private (object_class, sizeof (GtkCellAreaBoxPrivate));
356 /*************************************************************
357 * CellInfo/CellGroup basics and convenience functions *
358 *************************************************************/
360 cell_info_new (GtkCellRenderer *renderer,
365 CellInfo *info = g_slice_new (CellInfo);
367 info->renderer = g_object_ref_sink (renderer);
369 info->expand = expand;
376 cell_info_free (CellInfo *info)
378 g_object_unref (info->renderer);
380 g_slice_free (CellInfo, info);
384 cell_info_find (CellInfo *info,
385 GtkCellRenderer *renderer)
387 return (info->renderer == renderer) ? 0 : -1;
390 static AllocatedCell *
391 allocated_cell_new (GtkCellRenderer *renderer,
395 AllocatedCell *cell = g_slice_new (AllocatedCell);
397 cell->renderer = renderer;
398 cell->position = position;
405 allocated_cell_free (AllocatedCell *cell)
407 g_slice_free (AllocatedCell, cell);
411 list_consecutive_cells (GtkCellAreaBox *box)
413 GtkCellAreaBoxPrivate *priv = box->priv;
414 GList *l, *consecutive_cells = NULL, *pack_end_cells = NULL;
417 /* List cells in consecutive order taking their
418 * PACK_START/PACK_END options into account
420 for (l = priv->cells; l; l = l->next)
424 if (info->pack == GTK_PACK_START)
425 consecutive_cells = g_list_prepend (consecutive_cells, info);
428 for (l = priv->cells; l; l = l->next)
432 if (info->pack == GTK_PACK_END)
433 pack_end_cells = g_list_prepend (pack_end_cells, info);
436 consecutive_cells = g_list_reverse (consecutive_cells);
437 consecutive_cells = g_list_concat (consecutive_cells, pack_end_cells);
439 return consecutive_cells;
443 cell_groups_clear (GtkCellAreaBox *box)
445 GtkCellAreaBoxPrivate *priv = box->priv;
448 for (i = 0; i < priv->groups->len; i++)
450 CellGroup *group = &g_array_index (priv->groups, CellGroup, i);
452 g_list_free (group->cells);
455 g_array_set_size (priv->groups, 0);
459 cell_groups_rebuild (GtkCellAreaBox *box)
461 GtkCellAreaBoxPrivate *priv = box->priv;
462 CellGroup group = { 0, };
463 CellGroup *group_ptr;
467 cell_groups_clear (box);
472 cells = list_consecutive_cells (box);
474 /* First group is implied */
475 g_array_append_val (priv->groups, group);
476 group_ptr = &g_array_index (priv->groups, CellGroup, id);
478 for (l = cells; l; l = l->next)
480 CellInfo *info = l->data;
482 /* A new group starts with any aligned cell, the first group is implied */
483 if (info->align && l != cells)
485 memset (&group, 0x0, sizeof (CellGroup));
488 g_array_append_val (priv->groups, group);
489 group_ptr = &g_array_index (priv->groups, CellGroup, id);
492 group_ptr->cells = g_list_prepend (group_ptr->cells, info);
493 group_ptr->n_cells++;
495 /* A group expands if it contains any expand cells */
497 group_ptr->expand_cells++;
502 for (id = 0; id < priv->groups->len; id++)
504 group_ptr = &g_array_index (priv->groups, CellGroup, id);
506 group_ptr->cells = g_list_reverse (group_ptr->cells);
509 /* Contexts need to be updated with the new grouping information */
510 init_context_groups (box);
514 count_visible_cells (CellGroup *group,
518 gint visible_cells = 0;
519 gint n_expand_cells = 0;
521 for (l = group->cells; l; l = l->next)
523 CellInfo *info = l->data;
525 if (gtk_cell_renderer_get_visible (info->renderer))
535 *expand_cells = n_expand_cells;
537 return visible_cells;
541 count_expand_groups (GtkCellAreaBox *box)
543 GtkCellAreaBoxPrivate *priv = box->priv;
545 gint expand_groups = 0;
547 for (i = 0; i < priv->groups->len; i++)
549 CellGroup *group = &g_array_index (priv->groups, CellGroup, i);
551 if (group->expand_cells > 0)
555 return expand_groups;
559 context_weak_notify (GtkCellAreaBox *box,
560 GtkCellAreaBoxContext *dead_context)
562 GtkCellAreaBoxPrivate *priv = box->priv;
564 priv->contexts = g_slist_remove (priv->contexts, dead_context);
568 init_context_group (GtkCellAreaBox *box,
569 GtkCellAreaBoxContext *context)
571 GtkCellAreaBoxPrivate *priv = box->priv;
572 gint *expand_groups, i;
574 expand_groups = g_new (gboolean, priv->groups->len);
576 for (i = 0; i < priv->groups->len; i++)
578 CellGroup *group = &g_array_index (priv->groups, CellGroup, i);
580 expand_groups[i] = (group->expand_cells > 0);
583 /* This call implies reseting the request info */
584 gtk_cell_area_box_init_groups (context, priv->groups->len, expand_groups);
585 g_free (expand_groups);
589 init_context_groups (GtkCellAreaBox *box)
591 GtkCellAreaBoxPrivate *priv = box->priv;
594 /* When the box's groups are reconstructed, contexts need to
597 for (l = priv->contexts; l; l = l->next)
599 GtkCellAreaBoxContext *context = l->data;
601 init_context_group (box, context);
606 reset_contexts (GtkCellAreaBox *box)
608 GtkCellAreaBoxPrivate *priv = box->priv;
611 /* When the box layout changes, contexts need to
612 * be reset and sizes for the box get requested again
614 for (l = priv->contexts; l; l = l->next)
616 GtkCellAreaContext *context = l->data;
618 gtk_cell_area_context_reset (context);
622 /* Fall back on a completely unaligned dynamic allocation of cells
623 * when not allocated for the said orientation, alignment of cells
624 * is not done when each area gets a different size in the orientation
628 allocate_cells_manually (GtkCellAreaBox *box,
633 GtkCellAreaBoxPrivate *priv = box->priv;
635 GSList *allocated_cells = NULL;
636 GtkRequestedSize *sizes;
638 gint nvisible = 0, nexpand = 0, group_expand;
639 gint avail_size, extra_size, extra_extra;
640 gint position = 0, for_size;
646 cells = list_consecutive_cells (box);
648 /* Count the visible and expand cells */
649 for (i = 0; i < priv->groups->len; i++)
651 CellGroup *group = &g_array_index (priv->groups, CellGroup, i);
653 nvisible += count_visible_cells (group, &group_expand);
654 nexpand += group_expand;
663 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
674 /* Go ahead and collect the requests on the fly */
675 sizes = g_new0 (GtkRequestedSize, nvisible);
676 for (l = cells, i = 0; l; l = l->next)
678 CellInfo *info = l->data;
680 if (!gtk_cell_renderer_get_visible (info->renderer))
683 gtk_cell_area_request_renderer (GTK_CELL_AREA (box), info->renderer,
686 &sizes[i].minimum_size,
687 &sizes[i].natural_size);
689 avail_size -= sizes[i].minimum_size;
691 sizes[i].data = info;
696 /* Naturally distribute the allocation */
697 avail_size -= (nvisible - 1) * priv->spacing;
698 avail_size = gtk_distribute_natural_allocation (avail_size, nvisible, sizes);
700 /* Calculate/distribute expand for cells */
703 extra_size = avail_size / nexpand;
704 extra_extra = avail_size % nexpand;
707 extra_size = extra_extra = 0;
709 /* Create the allocated cells */
710 for (i = 0; i < nvisible; i++)
712 CellInfo *info = sizes[i].data;
717 sizes[i].minimum_size += extra_size;
720 sizes[i].minimum_size++;
725 cell = allocated_cell_new (info->renderer, position, sizes[i].minimum_size);
727 allocated_cells = g_slist_prepend (allocated_cells, cell);
729 position += sizes[i].minimum_size;
730 position += priv->spacing;
736 /* Note it might not be important to reverse the list here at all,
737 * we have the correct positions, no need to allocate from left to right */
738 return g_slist_reverse (allocated_cells);
741 /* Returns an allocation for each cell in the orientation of the box,
742 * used in ->render()/->event() implementations to get a straight-forward
743 * list of allocated cells to operate on.
746 get_allocated_cells (GtkCellAreaBox *box,
747 GtkCellAreaBoxContext *context,
752 GtkCellAreaBoxAllocation *group_allocs;
753 GtkCellArea *area = GTK_CELL_AREA (box);
754 GtkCellAreaBoxPrivate *priv = box->priv;
756 GSList *allocated_cells = NULL;
760 group_allocs = gtk_cell_area_box_context_get_orientation_allocs (context, &n_allocs);
762 return allocate_cells_manually (box, widget, width, height);
764 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
769 for (i = 0; i < n_allocs; i++)
771 /* We dont always allocate all groups, sometimes the requested group has only invisible
772 * cells for every row, hence the usage of group_allocs[i].group_idx here
774 CellGroup *group = &g_array_index (priv->groups, CellGroup, group_allocs[i].group_idx);
776 /* Exception for single cell groups */
777 if (group->n_cells == 1)
779 CellInfo *info = group->cells->data;
780 AllocatedCell *cell =
781 allocated_cell_new (info->renderer, group_allocs[i].position, group_allocs[i].size);
783 allocated_cells = g_slist_prepend (allocated_cells, cell);
787 GtkRequestedSize *sizes;
788 gint avail_size, position;
789 gint visible_cells, expand_cells;
790 gint extra_size, extra_extra;
792 visible_cells = count_visible_cells (group, &expand_cells);
794 /* If this row has no visible cells in this group, just
795 * skip the allocation */
796 if (visible_cells == 0)
799 /* Offset the allocation to the group position and allocate into
800 * the group's available size */
801 position = group_allocs[i].position;
802 avail_size = group_allocs[i].size;
804 sizes = g_new (GtkRequestedSize, visible_cells);
806 for (j = 0, cell_list = group->cells; cell_list; cell_list = cell_list->next)
808 CellInfo *info = cell_list->data;
810 if (!gtk_cell_renderer_get_visible (info->renderer))
813 gtk_cell_area_request_renderer (area, info->renderer,
816 &sizes[j].minimum_size,
817 &sizes[j].natural_size);
819 sizes[j].data = info;
820 avail_size -= sizes[j].minimum_size;
825 /* Distribute cells naturally within the group */
826 avail_size -= (visible_cells - 1) * priv->spacing;
828 avail_size = gtk_distribute_natural_allocation (avail_size, visible_cells, sizes);
832 /* Calculate/distribute expand for cells */
833 if (expand_cells > 0)
835 extra_size = avail_size / expand_cells;
836 extra_extra = avail_size % expand_cells;
839 extra_size = extra_extra = 0;
841 /* Create the allocated cells (loop only over visible cells here) */
842 for (j = 0; j < visible_cells; j++)
844 CellInfo *info = sizes[j].data;
849 sizes[j].minimum_size += extra_size;
852 sizes[j].minimum_size++;
857 cell = allocated_cell_new (info->renderer, position, sizes[j].minimum_size);
859 allocated_cells = g_slist_prepend (allocated_cells, cell);
861 position += sizes[j].minimum_size;
862 position += priv->spacing;
869 /* Note it might not be important to reverse the list here at all,
870 * we have the correct positions, no need to allocate from left to right */
871 return g_slist_reverse (allocated_cells);
874 /*************************************************************
876 *************************************************************/
878 gtk_cell_area_box_finalize (GObject *object)
880 GtkCellAreaBox *box = GTK_CELL_AREA_BOX (object);
881 GtkCellAreaBoxPrivate *priv = box->priv;
884 /* Unref/free the context list */
885 for (l = priv->contexts; l; l = l->next)
886 g_object_weak_unref (G_OBJECT (l->data), (GWeakNotify)context_weak_notify, box);
888 g_slist_free (priv->contexts);
889 priv->contexts = NULL;
891 /* Free the cell grouping info */
892 cell_groups_clear (box);
893 g_array_free (priv->groups, TRUE);
895 G_OBJECT_CLASS (gtk_cell_area_box_parent_class)->finalize (object);
899 gtk_cell_area_box_dispose (GObject *object)
901 G_OBJECT_CLASS (gtk_cell_area_box_parent_class)->dispose (object);
905 gtk_cell_area_box_set_property (GObject *object,
910 GtkCellAreaBox *box = GTK_CELL_AREA_BOX (object);
914 case PROP_ORIENTATION:
915 box->priv->orientation = g_value_get_enum (value);
917 /* Notify that size needs to be requested again */
918 reset_contexts (box);
921 gtk_cell_area_box_set_spacing (box, g_value_get_int (value));
924 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
930 gtk_cell_area_box_get_property (GObject *object,
935 GtkCellAreaBox *box = GTK_CELL_AREA_BOX (object);
939 case PROP_ORIENTATION:
940 g_value_set_enum (value, box->priv->orientation);
943 g_value_set_int (value, gtk_cell_area_box_get_spacing (box));
946 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
951 /*************************************************************
953 *************************************************************/
955 gtk_cell_area_box_add (GtkCellArea *area,
956 GtkCellRenderer *renderer)
958 gtk_cell_area_box_pack_start (GTK_CELL_AREA_BOX (area),
959 renderer, FALSE, TRUE);
963 gtk_cell_area_box_remove (GtkCellArea *area,
964 GtkCellRenderer *renderer)
966 GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area);
967 GtkCellAreaBoxPrivate *priv = box->priv;
970 node = g_list_find_custom (priv->cells, renderer,
971 (GCompareFunc)cell_info_find);
975 CellInfo *info = node->data;
977 cell_info_free (info);
979 priv->cells = g_list_delete_link (priv->cells, node);
981 /* Reconstruct cell groups */
982 cell_groups_rebuild (box);
985 g_warning ("Trying to remove a cell renderer that is not present GtkCellAreaBox");
989 gtk_cell_area_box_forall (GtkCellArea *area,
990 GtkCellCallback callback,
991 gpointer callback_data)
993 GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area);
994 GtkCellAreaBoxPrivate *priv = box->priv;
997 for (list = priv->cells; list; list = list->next)
999 CellInfo *info = list->data;
1001 callback (info->renderer, callback_data);
1006 gtk_cell_area_box_get_cell_allocation (GtkCellArea *area,
1007 GtkCellAreaContext *context,
1009 GtkCellRenderer *renderer,
1010 const GdkRectangle *cell_area,
1011 GdkRectangle *allocation)
1013 GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area);
1014 GtkCellAreaBoxPrivate *priv = box->priv;
1015 GtkCellAreaBoxContext *box_context = GTK_CELL_AREA_BOX_CONTEXT (context);
1016 GSList *allocated_cells, *l;
1018 *allocation = *cell_area;
1020 /* Get a list of cells with allocation sizes decided regardless
1021 * of alignments and pack order etc. */
1022 allocated_cells = get_allocated_cells (box, box_context, widget,
1023 cell_area->width, cell_area->height);
1025 for (l = allocated_cells; l; l = l->next)
1027 AllocatedCell *cell = l->data;
1029 if (cell->renderer == renderer)
1031 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
1033 allocation->x = cell_area->x + cell->position;
1034 allocation->width = cell->size;
1038 allocation->y = cell_area->y + cell->position;
1039 allocation->height = cell->size;
1046 g_slist_foreach (allocated_cells, (GFunc)allocated_cell_free, NULL);
1047 g_slist_free (allocated_cells);
1057 gtk_cell_area_box_event (GtkCellArea *area,
1058 GtkCellAreaContext *context,
1061 const GdkRectangle *cell_area,
1062 GtkCellRendererState flags)
1066 /* First let the parent class handle activation of cells via keystrokes */
1068 GTK_CELL_AREA_CLASS (gtk_cell_area_box_parent_class)->event (area, context, widget,
1069 event, cell_area, flags);
1074 /* Also detect mouse events, for mouse events we need to allocate the renderers
1075 * and find which renderer needs to be activated.
1077 if (event->type == GDK_BUTTON_PRESS)
1079 GdkEventButton *button_event = (GdkEventButton *)event;
1081 if (button_event->button == 1)
1083 GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area);
1084 GtkCellAreaBoxPrivate *priv = box->priv;
1085 GtkCellAreaBoxContext *box_context = GTK_CELL_AREA_BOX_CONTEXT (context);
1086 GSList *allocated_cells, *l;
1087 GdkRectangle cell_background, inner_area;
1088 gint event_x, event_y;
1090 /* We may need some semantics to tell us the offset of the event
1091 * window we are handling events for (i.e. GtkTreeView has a bin_window) */
1092 event_x = button_event->x;
1093 event_y = button_event->y;
1095 cell_background = *cell_area;
1097 /* Get a list of cells with allocation sizes decided regardless
1098 * of alignments and pack order etc. */
1099 allocated_cells = get_allocated_cells (box, box_context, widget,
1100 cell_area->width, cell_area->height);
1102 for (l = allocated_cells; l; l = l->next)
1104 AllocatedCell *cell = l->data;
1106 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
1108 cell_background.x = cell_area->x + cell->position;
1109 cell_background.width = cell->size;
1113 cell_background.y = cell_area->y + cell->position;
1114 cell_background.height = cell->size;
1117 /* Remove margins from the background area to produce the cell area
1119 gtk_cell_area_inner_cell_area (area, widget, &cell_background, &inner_area);
1121 if (event_x >= inner_area.x && event_x <= inner_area.x + inner_area.width &&
1122 event_y >= inner_area.y && event_y <= inner_area.y + inner_area.height)
1124 GtkCellRenderer *event_renderer = NULL;
1125 GtkCellRenderer *focus_renderer;
1127 focus_renderer = gtk_cell_area_get_focus_from_sibling (area, cell->renderer);
1129 event_renderer = focus_renderer;
1131 event_renderer = cell->renderer;
1133 event_renderer = cell->renderer;
1137 if (gtk_cell_area_get_edited_cell (area))
1139 /* XXX Was it really canceled in this case ? */
1140 gtk_cell_area_stop_editing (area, TRUE);
1141 gtk_cell_area_set_focus_cell (area, event_renderer);
1146 /* If we are activating via a focus sibling, we need to fix the
1148 if (event_renderer != cell->renderer)
1149 gtk_cell_area_inner_cell_area (area, widget, cell_area, &cell_background);
1151 gtk_cell_area_set_focus_cell (area, event_renderer);
1153 retval = gtk_cell_area_activate_cell (area, widget, event_renderer,
1154 event, &cell_background, flags);
1160 g_slist_foreach (allocated_cells, (GFunc)allocated_cell_free, NULL);
1161 g_slist_free (allocated_cells);
1169 gtk_cell_area_box_render (GtkCellArea *area,
1170 GtkCellAreaContext *context,
1173 const GdkRectangle *background_area,
1174 const GdkRectangle *cell_area,
1175 GtkCellRendererState flags,
1176 gboolean paint_focus)
1178 GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area);
1179 GtkCellAreaBoxPrivate *priv = box->priv;
1180 GtkCellAreaBoxContext *box_context = GTK_CELL_AREA_BOX_CONTEXT (context);
1181 GSList *allocated_cells, *l;
1182 GdkRectangle cell_background, inner_area;
1183 GtkCellRenderer *focus_cell = NULL;
1184 GdkRectangle focus_rect = { 0, };
1185 gboolean first_focus_cell = TRUE;
1186 gboolean focus_all = FALSE;
1188 /* Make sure we dont paint a focus rectangle while there
1189 * is an editable widget in play
1191 if (gtk_cell_area_get_edited_cell (area))
1192 paint_focus = FALSE;
1194 if (flags & GTK_CELL_RENDERER_FOCUSED)
1196 focus_cell = gtk_cell_area_get_focus_cell (area);
1197 flags &= ~GTK_CELL_RENDERER_FOCUSED;
1199 /* If no cell can activate but the caller wants focus painted,
1200 * then we paint focus around all cells */
1201 if (paint_focus && !gtk_cell_area_is_activatable (area))
1205 cell_background = *cell_area;
1207 /* Get a list of cells with allocation sizes decided regardless
1208 * of alignments and pack order etc. */
1209 allocated_cells = get_allocated_cells (box, box_context, widget,
1210 cell_area->width, cell_area->height);
1212 for (l = allocated_cells; l; l = l->next)
1214 AllocatedCell *cell = l->data;
1215 GtkCellRendererState cell_fields = 0;
1216 GdkRectangle render_background;
1218 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
1220 cell_background.x = cell_area->x + cell->position;
1221 cell_background.width = cell->size;
1225 cell_background.y = cell_area->y + cell->position;
1226 cell_background.height = cell->size;
1229 /* Stop rendering cells if they flow out of the render area,
1230 * this can happen because the render area can actually be
1231 * smaller than the requested area (treeview columns can
1232 * be user resizable and can be resized to be smaller than
1233 * the actual requested area). */
1234 if (cell_background.x > cell_area->x + cell_area->width ||
1235 cell_background.y > cell_area->y + cell_area->height)
1238 /* Special case for the last cell... let the last cell consume the remaining
1239 * space in the area (the last cell is allowed to consume the remaining space if
1240 * the space given for rendering is actually larger than allocation, this can
1241 * happen in the expander GtkTreeViewColumn where only the deepest depth column
1242 * receives the allocation... shallow columns recieve more width). */
1245 cell_background.width = cell_area->x + cell_area->width - cell_background.x;
1246 cell_background.height = cell_area->y + cell_area->height - cell_background.y;
1250 /* If the cell we are rendering doesnt fit into the remaining space, clip it
1251 * so that the underlying renderer has a chance to deal with it (for instance
1252 * text renderers get a chance to ellipsize).
1254 if (cell_background.x + cell_background.width > cell_area->x + cell_area->width)
1255 cell_background.width = cell_area->x + cell_area->width - cell_background.x;
1257 if (cell_background.y + cell_background.height > cell_area->y + cell_area->height)
1258 cell_background.height = cell_area->y + cell_area->height - cell_background.y;
1261 /* Remove margins from the background area to produce the cell area
1263 gtk_cell_area_inner_cell_area (area, widget, &cell_background, &inner_area);
1265 /* Add portions of the background_area to the cell_background
1266 * to create the render_background */
1267 render_background = cell_background;
1269 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
1271 if (l == allocated_cells)
1273 render_background.width += render_background.x - background_area->x;
1274 render_background.x = background_area->x;
1277 if (l->next == NULL)
1278 render_background.width =
1279 background_area->width - (render_background.x - background_area->x);
1281 render_background.y = background_area->y;
1282 render_background.height = background_area->height;
1286 if (l == allocated_cells)
1288 render_background.height += render_background.y - background_area->y;
1289 render_background.y = background_area->y;
1292 if (l->next == NULL)
1293 render_background.height =
1294 background_area->height - (render_background.y - background_area->y);
1296 render_background.x = background_area->x;
1297 render_background.width = background_area->width;
1302 (cell->renderer == focus_cell ||
1303 gtk_cell_area_is_focus_sibling (area, focus_cell, cell->renderer))))
1305 cell_fields |= GTK_CELL_RENDERER_FOCUSED;
1309 GdkRectangle cell_focus;
1311 gtk_cell_renderer_get_aligned_area (cell->renderer, widget, flags, &inner_area, &cell_focus);
1313 /* Accumulate the focus rectangle for all focus siblings */
1314 if (first_focus_cell)
1316 focus_rect = cell_focus;
1317 first_focus_cell = FALSE;
1320 gdk_rectangle_union (&focus_rect, &cell_focus, &focus_rect);
1324 /* We have to do some per-cell considerations for the 'flags'
1325 * for focus handling */
1326 gtk_cell_renderer_render (cell->renderer, cr, widget,
1327 &render_background, &inner_area,
1328 flags | cell_fields);
1331 if (paint_focus && focus_rect.width != 0 && focus_rect.height != 0)
1333 GtkStateType renderer_state =
1334 flags & GTK_CELL_RENDERER_SELECTED ? GTK_STATE_SELECTED :
1335 (flags & GTK_CELL_RENDERER_PRELIT ? GTK_STATE_PRELIGHT :
1336 (flags & GTK_CELL_RENDERER_INSENSITIVE ? GTK_STATE_INSENSITIVE : GTK_STATE_NORMAL));
1338 gtk_paint_focus (gtk_widget_get_style (widget), cr,
1339 renderer_state, widget,
1340 gtk_cell_area_get_style_detail (area),
1341 focus_rect.x, focus_rect.y,
1342 focus_rect.width, focus_rect.height);
1346 g_slist_foreach (allocated_cells, (GFunc)allocated_cell_free, NULL);
1347 g_slist_free (allocated_cells);
1351 gtk_cell_area_box_set_cell_property (GtkCellArea *area,
1352 GtkCellRenderer *renderer,
1354 const GValue *value,
1357 GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area);
1358 GtkCellAreaBoxPrivate *priv = box->priv;
1361 gboolean rebuild = FALSE;
1363 GtkPackType pack_type;
1365 node = g_list_find_custom (priv->cells, renderer,
1366 (GCompareFunc)cell_info_find);
1374 case CELL_PROP_EXPAND:
1375 val = g_value_get_boolean (value);
1377 if (info->expand != val)
1384 case CELL_PROP_ALIGN:
1385 val = g_value_get_boolean (value);
1387 if (info->align != val)
1394 case CELL_PROP_PACK_TYPE:
1395 pack_type = g_value_get_enum (value);
1397 if (info->pack != pack_type)
1399 info->pack = pack_type;
1404 GTK_CELL_AREA_WARN_INVALID_CELL_PROPERTY_ID (area, prop_id, pspec);
1408 /* Groups need to be rebuilt */
1410 cell_groups_rebuild (box);
1414 gtk_cell_area_box_get_cell_property (GtkCellArea *area,
1415 GtkCellRenderer *renderer,
1420 GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area);
1421 GtkCellAreaBoxPrivate *priv = box->priv;
1425 node = g_list_find_custom (priv->cells, renderer,
1426 (GCompareFunc)cell_info_find);
1434 case CELL_PROP_EXPAND:
1435 g_value_set_boolean (value, info->expand);
1438 case CELL_PROP_ALIGN:
1439 g_value_set_boolean (value, info->align);
1442 case CELL_PROP_PACK_TYPE:
1443 g_value_set_enum (value, info->pack);
1446 GTK_CELL_AREA_WARN_INVALID_CELL_PROPERTY_ID (area, prop_id, pspec);
1452 static GtkCellAreaContext *
1453 gtk_cell_area_box_create_context (GtkCellArea *area)
1455 GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area);
1456 GtkCellAreaBoxPrivate *priv = box->priv;
1457 GtkCellAreaContext *context =
1458 (GtkCellAreaContext *)g_object_new (GTK_TYPE_CELL_AREA_BOX_CONTEXT,
1459 "area", area, NULL);
1461 priv->contexts = g_slist_prepend (priv->contexts, context);
1463 g_object_weak_ref (G_OBJECT (context), (GWeakNotify)context_weak_notify, box);
1465 /* Tell the new group about our cell layout */
1466 init_context_group (box, GTK_CELL_AREA_BOX_CONTEXT (context));
1471 static GtkSizeRequestMode
1472 gtk_cell_area_box_get_request_mode (GtkCellArea *area)
1474 GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area);
1475 GtkCellAreaBoxPrivate *priv = box->priv;
1477 return (priv->orientation) == GTK_ORIENTATION_HORIZONTAL ?
1478 GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH :
1479 GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT;
1483 compute_size (GtkCellAreaBox *box,
1484 GtkOrientation orientation,
1485 GtkCellAreaBoxContext *context,
1491 GtkCellAreaBoxPrivate *priv = box->priv;
1492 GtkCellArea *area = GTK_CELL_AREA (box);
1498 for (i = 0; i < priv->groups->len; i++)
1500 CellGroup *group = &g_array_index (priv->groups, CellGroup, i);
1501 gint group_min_size = 0;
1502 gint group_nat_size = 0;
1504 for (list = group->cells; list; list = list->next)
1506 CellInfo *info = list->data;
1507 gint renderer_min_size, renderer_nat_size;
1509 if (!gtk_cell_renderer_get_visible (info->renderer))
1512 gtk_cell_area_request_renderer (area, info->renderer, orientation, widget, for_size,
1513 &renderer_min_size, &renderer_nat_size);
1515 if (orientation == priv->orientation)
1519 min_size += priv->spacing;
1520 nat_size += priv->spacing;
1523 if (group_min_size > 0)
1525 group_min_size += priv->spacing;
1526 group_nat_size += priv->spacing;
1529 min_size += renderer_min_size;
1530 nat_size += renderer_nat_size;
1531 group_min_size += renderer_min_size;
1532 group_nat_size += renderer_nat_size;
1536 min_size = MAX (min_size, renderer_min_size);
1537 nat_size = MAX (nat_size, renderer_nat_size);
1538 group_min_size = MAX (group_min_size, renderer_min_size);
1539 group_nat_size = MAX (group_nat_size, renderer_nat_size);
1543 if (orientation == GTK_ORIENTATION_HORIZONTAL)
1546 gtk_cell_area_box_context_push_group_width (context, group->id, group_min_size, group_nat_size);
1548 gtk_cell_area_box_context_push_group_width_for_height (context, group->id, for_size,
1549 group_min_size, group_nat_size);
1554 gtk_cell_area_box_context_push_group_height (context, group->id, group_min_size, group_nat_size);
1556 gtk_cell_area_box_context_push_group_height_for_width (context, group->id, for_size,
1557 group_min_size, group_nat_size);
1561 *minimum_size = min_size;
1562 *natural_size = nat_size;
1566 get_group_sizes (GtkCellArea *area,
1568 GtkOrientation orientation,
1572 GtkRequestedSize *sizes;
1576 *n_sizes = count_visible_cells (group, NULL);
1577 sizes = g_new (GtkRequestedSize, *n_sizes);
1579 for (l = group->cells, i = 0; l; l = l->next)
1581 CellInfo *info = l->data;
1583 if (!gtk_cell_renderer_get_visible (info->renderer))
1586 sizes[i].data = info;
1588 gtk_cell_area_request_renderer (area, info->renderer,
1589 orientation, widget, -1,
1590 &sizes[i].minimum_size,
1591 &sizes[i].natural_size);
1600 compute_group_size_for_opposing_orientation (GtkCellAreaBox *box,
1607 GtkCellAreaBoxPrivate *priv = box->priv;
1608 GtkCellArea *area = GTK_CELL_AREA (box);
1610 /* Exception for single cell groups */
1611 if (group->n_cells == 1)
1613 CellInfo *info = group->cells->data;
1615 gtk_cell_area_request_renderer (area, info->renderer,
1616 OPPOSITE_ORIENTATION (priv->orientation),
1617 widget, for_size, minimum_size, natural_size);
1621 GtkRequestedSize *orientation_sizes;
1624 gint avail_size = for_size;
1625 gint extra_size, extra_extra;
1626 gint min_size = 0, nat_size = 0;
1628 orientation_sizes = get_group_sizes (area, group, priv->orientation, widget, &n_sizes);
1630 /* First naturally allocate the cells in the group into the for_size */
1631 avail_size -= (n_sizes - 1) * priv->spacing;
1632 for (i = 0; i < n_sizes; i++)
1633 avail_size -= orientation_sizes[i].minimum_size;
1636 avail_size = gtk_distribute_natural_allocation (avail_size, n_sizes, orientation_sizes);
1640 /* Calculate/distribute expand for cells */
1641 if (group->expand_cells > 0)
1643 extra_size = avail_size / group->expand_cells;
1644 extra_extra = avail_size % group->expand_cells;
1647 extra_size = extra_extra = 0;
1649 for (i = 0; i < n_sizes; i++)
1651 gint cell_min, cell_nat;
1653 info = orientation_sizes[i].data;
1657 orientation_sizes[i].minimum_size += extra_size;
1660 orientation_sizes[i].minimum_size++;
1665 gtk_cell_area_request_renderer (area, info->renderer,
1666 OPPOSITE_ORIENTATION (priv->orientation),
1668 orientation_sizes[i].minimum_size,
1669 &cell_min, &cell_nat);
1671 min_size = MAX (min_size, cell_min);
1672 nat_size = MAX (nat_size, cell_nat);
1675 *minimum_size = min_size;
1676 *natural_size = nat_size;
1678 g_free (orientation_sizes);
1683 compute_size_for_opposing_orientation (GtkCellAreaBox *box,
1684 GtkCellAreaBoxContext *context,
1690 GtkCellAreaBoxPrivate *priv = box->priv;
1692 GtkRequestedSize *orientation_sizes;
1693 gint n_groups, n_expand_groups, i;
1694 gint avail_size = for_size;
1695 gint extra_size, extra_extra;
1696 gint min_size = 0, nat_size = 0;
1698 n_expand_groups = count_expand_groups (box);
1700 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
1701 orientation_sizes = gtk_cell_area_box_context_get_widths (context, &n_groups);
1703 orientation_sizes = gtk_cell_area_box_context_get_heights (context, &n_groups);
1705 /* First start by naturally allocating space among groups of cells */
1706 avail_size -= (n_groups - 1) * priv->spacing;
1707 for (i = 0; i < n_groups; i++)
1708 avail_size -= orientation_sizes[i].minimum_size;
1711 avail_size = gtk_distribute_natural_allocation (avail_size, n_groups, orientation_sizes);
1715 /* Calculate/distribute expand for groups */
1716 if (n_expand_groups > 0)
1718 extra_size = avail_size / n_expand_groups;
1719 extra_extra = avail_size % n_expand_groups;
1722 extra_size = extra_extra = 0;
1724 /* Now we need to naturally allocate sizes for cells in each group
1725 * and push the height-for-width for each group accordingly while accumulating
1726 * the overall height-for-width for this row.
1728 for (i = 0; i < n_groups; i++)
1730 gint group_min, group_nat;
1731 gint group_idx = GPOINTER_TO_INT (orientation_sizes[i].data);
1733 group = &g_array_index (priv->groups, CellGroup, group_idx);
1735 if (group->expand_cells > 0)
1737 orientation_sizes[i].minimum_size += extra_size;
1740 orientation_sizes[i].minimum_size++;
1745 /* Now we have the allocation for the group, request it's height-for-width */
1746 compute_group_size_for_opposing_orientation (box, group, widget,
1747 orientation_sizes[i].minimum_size,
1748 &group_min, &group_nat);
1750 min_size = MAX (min_size, group_min);
1751 nat_size = MAX (nat_size, group_nat);
1753 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
1755 gtk_cell_area_box_context_push_group_height_for_width (context, group_idx, for_size,
1756 group_min, group_nat);
1760 gtk_cell_area_box_context_push_group_width_for_height (context, group_idx, for_size,
1761 group_min, group_nat);
1765 *minimum_size = min_size;
1766 *natural_size = nat_size;
1768 g_free (orientation_sizes);
1774 gtk_cell_area_box_get_preferred_width (GtkCellArea *area,
1775 GtkCellAreaContext *context,
1777 gint *minimum_width,
1778 gint *natural_width)
1780 GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area);
1781 GtkCellAreaBoxContext *box_context;
1782 gint min_width, nat_width;
1784 g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (context));
1786 box_context = GTK_CELL_AREA_BOX_CONTEXT (context);
1788 /* Compute the size of all renderers for current row data,
1789 * bumping cell alignments in the context along the way */
1790 compute_size (box, GTK_ORIENTATION_HORIZONTAL,
1791 box_context, widget, -1, &min_width, &nat_width);
1794 *minimum_width = min_width;
1797 *natural_width = nat_width;
1801 gtk_cell_area_box_get_preferred_height (GtkCellArea *area,
1802 GtkCellAreaContext *context,
1804 gint *minimum_height,
1805 gint *natural_height)
1807 GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area);
1808 GtkCellAreaBoxContext *box_context;
1809 gint min_height, nat_height;
1811 g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (context));
1813 box_context = GTK_CELL_AREA_BOX_CONTEXT (context);
1815 /* Compute the size of all renderers for current row data,
1816 * bumping cell alignments in the context along the way */
1817 compute_size (box, GTK_ORIENTATION_VERTICAL,
1818 box_context, widget, -1, &min_height, &nat_height);
1821 *minimum_height = min_height;
1824 *natural_height = nat_height;
1828 gtk_cell_area_box_get_preferred_height_for_width (GtkCellArea *area,
1829 GtkCellAreaContext *context,
1832 gint *minimum_height,
1833 gint *natural_height)
1835 GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area);
1836 GtkCellAreaBoxContext *box_context;
1837 GtkCellAreaBoxPrivate *priv;
1838 gint min_height, nat_height;
1840 g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (context));
1842 box_context = GTK_CELL_AREA_BOX_CONTEXT (context);
1845 if (priv->orientation == GTK_ORIENTATION_VERTICAL)
1847 /* Add up vertical requests of height for width and push the overall
1848 * cached sizes for alignments */
1849 compute_size (box, priv->orientation, box_context, widget, width, &min_height, &nat_height);
1853 /* Juice: virtually allocate cells into the for_width using the
1854 * alignments and then return the overall height for that width, and cache it */
1855 compute_size_for_opposing_orientation (box, box_context, widget, width, &min_height, &nat_height);
1859 *minimum_height = min_height;
1862 *natural_height = nat_height;
1866 gtk_cell_area_box_get_preferred_width_for_height (GtkCellArea *area,
1867 GtkCellAreaContext *context,
1870 gint *minimum_width,
1871 gint *natural_width)
1873 GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area);
1874 GtkCellAreaBoxContext *box_context;
1875 GtkCellAreaBoxPrivate *priv;
1876 gint min_width, nat_width;
1878 g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (context));
1880 box_context = GTK_CELL_AREA_BOX_CONTEXT (context);
1883 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
1885 /* Add up horizontal requests of width for height and push the overall
1886 * cached sizes for alignments */
1887 compute_size (box, priv->orientation, box_context, widget, height, &min_width, &nat_width);
1891 /* Juice: horizontally allocate cells into the for_height using the
1892 * alignments and then return the overall width for that height, and cache it */
1893 compute_size_for_opposing_orientation (box, box_context, widget, height, &min_width, &nat_width);
1897 *minimum_width = min_width;
1900 *natural_width = nat_width;
1904 gtk_cell_area_box_focus (GtkCellArea *area,
1905 GtkDirectionType direction)
1907 GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area);
1908 GtkCellAreaBoxPrivate *priv = box->priv;
1909 gint cycle = FOCUS_NONE;
1910 gboolean cycled_focus = FALSE;
1911 GtkCellRenderer *focus_cell;
1913 focus_cell = gtk_cell_area_get_focus_cell (area);
1915 /* Special case, when there is no activatable cell, focus
1916 * is painted around the entire area... in this case we
1917 * let focus leave the area directly.
1919 if (focus_cell && !gtk_cell_area_is_activatable (area))
1921 gtk_cell_area_set_focus_cell (area, NULL);
1927 case GTK_DIR_TAB_FORWARD:
1930 case GTK_DIR_TAB_BACKWARD:
1934 if (priv->orientation == GTK_ORIENTATION_VERTICAL || !focus_cell)
1938 if (priv->orientation == GTK_ORIENTATION_VERTICAL || !focus_cell)
1942 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL || !focus_cell)
1946 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL || !focus_cell)
1953 if (cycle != FOCUS_NONE)
1955 gboolean found_cell = FALSE;
1959 /* If there is no focused cell, focus on the first (or last) one in the list */
1963 for (i = (cycle == FOCUS_NEXT) ? 0 : priv->groups->len -1;
1964 cycled_focus == FALSE && i >= 0 && i < priv->groups->len;
1965 i = (cycle == FOCUS_NEXT) ? i + 1 : i - 1)
1967 CellGroup *group = &g_array_index (priv->groups, CellGroup, i);
1969 for (list = (cycle == FOCUS_NEXT) ? g_list_first (group->cells) : g_list_last (group->cells);
1970 list; list = (cycle == FOCUS_NEXT) ? list->next : list->prev)
1972 CellInfo *info = list->data;
1974 if (info->renderer == focus_cell)
1976 else if (found_cell && /* Dont give focus to cells that are siblings to a focus cell */
1977 gtk_cell_area_get_focus_from_sibling (area, info->renderer) == NULL)
1979 gtk_cell_area_set_focus_cell (area, info->renderer);
1980 cycled_focus = TRUE;
1987 gtk_cell_area_set_focus_cell (area, NULL);
1989 return cycled_focus;
1993 /*************************************************************
1994 * GtkCellLayoutIface *
1995 *************************************************************/
1997 gtk_cell_area_box_cell_layout_init (GtkCellLayoutIface *iface)
1999 iface->pack_start = gtk_cell_area_box_layout_pack_start;
2000 iface->pack_end = gtk_cell_area_box_layout_pack_end;
2001 iface->reorder = gtk_cell_area_box_layout_reorder;
2005 gtk_cell_area_box_layout_pack_start (GtkCellLayout *cell_layout,
2006 GtkCellRenderer *renderer,
2009 gtk_cell_area_box_pack_start (GTK_CELL_AREA_BOX (cell_layout), renderer, expand, TRUE);
2013 gtk_cell_area_box_layout_pack_end (GtkCellLayout *cell_layout,
2014 GtkCellRenderer *renderer,
2017 gtk_cell_area_box_pack_end (GTK_CELL_AREA_BOX (cell_layout), renderer, expand, TRUE);
2021 gtk_cell_area_box_layout_reorder (GtkCellLayout *cell_layout,
2022 GtkCellRenderer *renderer,
2025 GtkCellAreaBox *box = GTK_CELL_AREA_BOX (cell_layout);
2026 GtkCellAreaBoxPrivate *priv = box->priv;
2030 node = g_list_find_custom (priv->cells, renderer,
2031 (GCompareFunc)cell_info_find);
2037 priv->cells = g_list_delete_link (priv->cells, node);
2038 priv->cells = g_list_insert (priv->cells, info, position);
2040 cell_groups_rebuild (box);
2044 /*************************************************************
2046 *************************************************************/
2048 * gtk_cell_area_box_new:
2050 * Creates a new #GtkCellAreaBox.
2052 * Return value: a newly created #GtkCellAreaBox
2055 gtk_cell_area_box_new (void)
2057 return (GtkCellArea *)g_object_new (GTK_TYPE_CELL_AREA_BOX, NULL);
2061 * gtk_cell_area_box_pack_start:
2062 * @box: a #GtkCellAreaBox
2063 * @renderer: the #GtkCellRenderer to add
2064 * @expand: whether @renderer should receive extra space when the area receives
2065 * more than its natural size
2066 * @align: whether @renderer should be aligned in adjacent rows.
2068 * Adds @renderer to @box, packed with reference to the start of @box.
2070 * The @renderer is packed after any other #GtkCellRenderer packed with reference
2071 * to the start of @box.
2076 gtk_cell_area_box_pack_start (GtkCellAreaBox *box,
2077 GtkCellRenderer *renderer,
2081 GtkCellAreaBoxPrivate *priv;
2084 g_return_if_fail (GTK_IS_CELL_AREA_BOX (box));
2085 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
2089 if (g_list_find_custom (priv->cells, renderer,
2090 (GCompareFunc)cell_info_find))
2092 g_warning ("Refusing to add the same cell renderer to a GtkCellAreaBox twice");
2096 info = cell_info_new (renderer, GTK_PACK_START, expand, align);
2098 priv->cells = g_list_append (priv->cells, info);
2100 cell_groups_rebuild (box);
2104 * gtk_cell_area_box_pack_end:
2105 * @box: a #GtkCellAreaBox
2106 * @renderer: the #GtkCellRenderer to add
2107 * @expand: whether @renderer should receive extra space when the area receives
2108 * more than its natural size
2109 * @align: whether @renderer should be aligned in adjacent rows.
2111 * Adds @renderer to @box, packed with reference to the end of @box.
2113 * The @renderer is packed after (away from end of) any other #GtkCellRenderer
2114 * packed with reference to the end of @box.
2119 gtk_cell_area_box_pack_end (GtkCellAreaBox *box,
2120 GtkCellRenderer *renderer,
2124 GtkCellAreaBoxPrivate *priv;
2127 g_return_if_fail (GTK_IS_CELL_AREA_BOX (box));
2128 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
2132 if (g_list_find_custom (priv->cells, renderer,
2133 (GCompareFunc)cell_info_find))
2135 g_warning ("Refusing to add the same cell renderer to a GtkCellArea twice");
2139 info = cell_info_new (renderer, GTK_PACK_END, expand, align);
2141 priv->cells = g_list_append (priv->cells, info);
2143 cell_groups_rebuild (box);
2147 * gtk_cell_area_box_get_spacing:
2148 * @box: a #GtkCellAreaBox
2150 * Gets the spacing added between cell renderers.
2152 * Return value: the space added between cell renderers in @box.
2157 gtk_cell_area_box_get_spacing (GtkCellAreaBox *box)
2159 g_return_val_if_fail (GTK_IS_CELL_AREA_BOX (box), 0);
2161 return box->priv->spacing;
2165 * gtk_cell_area_box_set_spacing:
2166 * @box: a #GtkCellAreaBox
2167 * @spacing: the space to add between #GtkCellRenderers
2169 * Sets the spacing to add between cell renderers in @box.
2174 gtk_cell_area_box_set_spacing (GtkCellAreaBox *box,
2177 GtkCellAreaBoxPrivate *priv;
2179 g_return_if_fail (GTK_IS_CELL_AREA_BOX (box));
2183 if (priv->spacing != spacing)
2185 priv->spacing = spacing;
2187 g_object_notify (G_OBJECT (box), "spacing");
2189 /* Notify that size needs to be requested again */
2190 reset_contexts (box);