1/* gtkcellareabox.c
2 *
3 * Copyright (C) 2010 Openismus GmbH
4 *
5 * Authors:
6 * Tristan Van Berkom <tristanvb@openismus.com>
7 *
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.
12 *
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.
17 *
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22
23/**
24 * GtkCellAreaBox:
25 *
26 * A cell area that renders GtkCellRenderers into a row or a column
27 *
28 * The `GtkCellAreaBox` renders cell renderers into a row or a column
29 * depending on its `GtkOrientation`.
30 *
31 * GtkCellAreaBox uses a notion of packing. Packing
32 * refers to adding cell renderers with reference to a particular position
33 * in a `GtkCellAreaBox`. There are two reference positions: the
34 * start and the end of the box.
35 * When the `GtkCellAreaBox` is oriented in the %GTK_ORIENTATION_VERTICAL
36 * orientation, the start is defined as the top of the box and the end is
37 * defined as the bottom. In the %GTK_ORIENTATION_HORIZONTAL orientation
38 * start is defined as the left side and the end is defined as the right
39 * side.
40 *
41 * Alignments of `GtkCellRenderer`s rendered in adjacent rows can be
42 * configured by configuring the `GtkCellAreaBox` align child cell property
43 * with gtk_cell_area_cell_set_property() or by specifying the "align"
44 * argument to gtk_cell_area_box_pack_start() and gtk_cell_area_box_pack_end().
45 */
46
47#include "config.h"
48#include "gtkintl.h"
49#include "gtkorientable.h"
50#include "gtkcelllayout.h"
51#include "gtkcellareabox.h"
52#include "gtkcellareaboxcontextprivate.h"
53#include "gtktypebuiltins.h"
54#include "gtkprivate.h"
55
56
57/* GObjectClass */
58static void gtk_cell_area_box_finalize (GObject *object);
59static void gtk_cell_area_box_dispose (GObject *object);
60static void gtk_cell_area_box_set_property (GObject *object,
61 guint prop_id,
62 const GValue *value,
63 GParamSpec *pspec);
64static void gtk_cell_area_box_get_property (GObject *object,
65 guint prop_id,
66 GValue *value,
67 GParamSpec *pspec);
68
69/* GtkCellAreaClass */
70static void gtk_cell_area_box_add (GtkCellArea *area,
71 GtkCellRenderer *renderer);
72static void gtk_cell_area_box_remove (GtkCellArea *area,
73 GtkCellRenderer *renderer);
74static void gtk_cell_area_box_foreach (GtkCellArea *area,
75 GtkCellCallback callback,
76 gpointer callback_data);
77static void gtk_cell_area_box_foreach_alloc (GtkCellArea *area,
78 GtkCellAreaContext *context,
79 GtkWidget *widget,
80 const GdkRectangle *cell_area,
81 const GdkRectangle *background_area,
82 GtkCellAllocCallback callback,
83 gpointer callback_data);
84static void gtk_cell_area_box_apply_attributes (GtkCellArea *area,
85 GtkTreeModel *tree_model,
86 GtkTreeIter *iter,
87 gboolean is_expander,
88 gboolean is_expanded);
89static void gtk_cell_area_box_set_cell_property (GtkCellArea *area,
90 GtkCellRenderer *renderer,
91 guint prop_id,
92 const GValue *value,
93 GParamSpec *pspec);
94static void gtk_cell_area_box_get_cell_property (GtkCellArea *area,
95 GtkCellRenderer *renderer,
96 guint prop_id,
97 GValue *value,
98 GParamSpec *pspec);
99static GtkCellAreaContext *gtk_cell_area_box_create_context (GtkCellArea *area);
100static GtkCellAreaContext *gtk_cell_area_box_copy_context (GtkCellArea *area,
101 GtkCellAreaContext *context);
102static GtkSizeRequestMode gtk_cell_area_box_get_request_mode (GtkCellArea *area);
103static void gtk_cell_area_box_get_preferred_width (GtkCellArea *area,
104 GtkCellAreaContext *context,
105 GtkWidget *widget,
106 int *minimum_width,
107 int *natural_width);
108static void gtk_cell_area_box_get_preferred_height (GtkCellArea *area,
109 GtkCellAreaContext *context,
110 GtkWidget *widget,
111 int *minimum_height,
112 int *natural_height);
113static void gtk_cell_area_box_get_preferred_height_for_width (GtkCellArea *area,
114 GtkCellAreaContext *context,
115 GtkWidget *widget,
116 int width,
117 int *minimum_height,
118 int *natural_height);
119static void gtk_cell_area_box_get_preferred_width_for_height (GtkCellArea *area,
120 GtkCellAreaContext *context,
121 GtkWidget *widget,
122 int height,
123 int *minimum_width,
124 int *natural_width);
125static gboolean gtk_cell_area_box_focus (GtkCellArea *area,
126 GtkDirectionType direction);
127
128/* GtkCellLayoutIface */
129static void gtk_cell_area_box_cell_layout_init (GtkCellLayoutIface *iface);
130static void gtk_cell_area_box_layout_pack_start (GtkCellLayout *cell_layout,
131 GtkCellRenderer *renderer,
132 gboolean expand);
133static void gtk_cell_area_box_layout_pack_end (GtkCellLayout *cell_layout,
134 GtkCellRenderer *renderer,
135 gboolean expand);
136static void gtk_cell_area_box_layout_reorder (GtkCellLayout *cell_layout,
137 GtkCellRenderer *renderer,
138 int position);
139static void gtk_cell_area_box_focus_changed (GtkCellArea *area,
140 GParamSpec *pspec,
141 GtkCellAreaBox *box);
142
143
144/* CellInfo/CellGroup metadata handling and convenience functions */
145typedef struct {
146 GtkCellRenderer *renderer;
147
148 guint expand : 1; /* Whether the cell expands */
149 guint pack : 1; /* Whether it is packed from the start or end */
150 guint align : 1; /* Whether to align its position with adjacent rows */
151 guint fixed : 1; /* Whether to require the same size for all rows */
152} CellInfo;
153
154typedef struct {
155 GList *cells;
156
157 guint id : 8;
158 guint n_cells : 8;
159 guint expand_cells : 8;
160 guint align : 1;
161 guint visible : 1;
162} CellGroup;
163
164typedef struct {
165 GtkCellRenderer *renderer;
166
167 int position;
168 int size;
169} AllocatedCell;
170
171static CellInfo *cell_info_new (GtkCellRenderer *renderer,
172 GtkPackType pack,
173 gboolean expand,
174 gboolean align,
175 gboolean fixed);
176static void cell_info_free (CellInfo *info);
177static int cell_info_find (CellInfo *info,
178 GtkCellRenderer *renderer);
179
180static AllocatedCell *allocated_cell_new (GtkCellRenderer *renderer,
181 int position,
182 int size);
183static void allocated_cell_free (AllocatedCell *cell);
184static GList *list_consecutive_cells (GtkCellAreaBox *box);
185static int count_expand_groups (GtkCellAreaBox *box);
186static void context_weak_notify (GtkCellAreaBox *box,
187 GtkCellAreaBoxContext *dead_context);
188static void reset_contexts (GtkCellAreaBox *box);
189static void init_context_groups (GtkCellAreaBox *box);
190static void init_context_group (GtkCellAreaBox *box,
191 GtkCellAreaBoxContext *context);
192static GSList *get_allocated_cells (GtkCellAreaBox *box,
193 GtkCellAreaBoxContext *context,
194 GtkWidget *widget,
195 int width,
196 int height);
197
198typedef struct _GtkCellAreaBoxClass GtkCellAreaBoxClass;
199typedef struct _GtkCellAreaBoxPrivate GtkCellAreaBoxPrivate;
200
201struct _GtkCellAreaBox
202{
203 GtkCellArea parent_instance;
204};
205
206struct _GtkCellAreaBoxClass
207{
208 GtkCellAreaClass parent_class;
209};
210
211struct _GtkCellAreaBoxPrivate
212{
213 /* We hold on to the previously focused cell when navigating
214 * up and down in a horizontal box (or left and right on a vertical one)
215 * this way we always re-enter the last focused cell.
216 */
217 GtkCellRenderer *last_focus_cell;
218 gulong focus_cell_id;
219
220 GList *cells;
221 GArray *groups;
222
223 GSList *contexts;
224
225 GtkOrientation orientation;
226 int spacing;
227
228 /* We hold on to the rtl state from a widget we are requested for
229 * so that we can navigate focus correctly
230 */
231 gboolean rtl;
232};
233
234enum {
235 PROP_0,
236 PROP_ORIENTATION,
237 PROP_SPACING
238};
239
240enum {
241 CELL_PROP_0,
242 CELL_PROP_EXPAND,
243 CELL_PROP_ALIGN,
244 CELL_PROP_FIXED_SIZE,
245 CELL_PROP_PACK_TYPE
246};
247
248G_DEFINE_TYPE_WITH_CODE (GtkCellAreaBox, gtk_cell_area_box, GTK_TYPE_CELL_AREA,
249 G_ADD_PRIVATE (GtkCellAreaBox)
250 G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
251 gtk_cell_area_box_cell_layout_init)
252 G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL))
253
254static void
255gtk_cell_area_box_init (GtkCellAreaBox *box)
256{
257 GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (self: box);
258
259 priv->orientation = GTK_ORIENTATION_HORIZONTAL;
260 priv->groups = g_array_new (FALSE, TRUE, element_size: sizeof (CellGroup));
261 priv->cells = NULL;
262 priv->contexts = NULL;
263 priv->spacing = 0;
264 priv->rtl = FALSE;
265
266 /* Watch whenever focus is given to a cell, even if it's not with keynav,
267 * this way we remember upon entry of the area where focus was last time
268 * around
269 */
270 priv->focus_cell_id = g_signal_connect (box, "notify::focus-cell",
271 G_CALLBACK (gtk_cell_area_box_focus_changed), box);
272}
273
274static void
275gtk_cell_area_box_class_init (GtkCellAreaBoxClass *class)
276{
277 GObjectClass *object_class = G_OBJECT_CLASS (class);
278 GtkCellAreaClass *area_class = GTK_CELL_AREA_CLASS (class);
279
280 /* GObjectClass */
281 object_class->finalize = gtk_cell_area_box_finalize;
282 object_class->dispose = gtk_cell_area_box_dispose;
283 object_class->set_property = gtk_cell_area_box_set_property;
284 object_class->get_property = gtk_cell_area_box_get_property;
285
286 /* GtkCellAreaClass */
287 area_class->add = gtk_cell_area_box_add;
288 area_class->remove = gtk_cell_area_box_remove;
289 area_class->foreach = gtk_cell_area_box_foreach;
290 area_class->foreach_alloc = gtk_cell_area_box_foreach_alloc;
291 area_class->apply_attributes = gtk_cell_area_box_apply_attributes;
292 area_class->set_cell_property = gtk_cell_area_box_set_cell_property;
293 area_class->get_cell_property = gtk_cell_area_box_get_cell_property;
294
295 area_class->create_context = gtk_cell_area_box_create_context;
296 area_class->copy_context = gtk_cell_area_box_copy_context;
297 area_class->get_request_mode = gtk_cell_area_box_get_request_mode;
298 area_class->get_preferred_width = gtk_cell_area_box_get_preferred_width;
299 area_class->get_preferred_height = gtk_cell_area_box_get_preferred_height;
300 area_class->get_preferred_height_for_width = gtk_cell_area_box_get_preferred_height_for_width;
301 area_class->get_preferred_width_for_height = gtk_cell_area_box_get_preferred_width_for_height;
302
303 area_class->focus = gtk_cell_area_box_focus;
304
305 /* Properties */
306 g_object_class_override_property (oclass: object_class, property_id: PROP_ORIENTATION, name: "orientation");
307
308 /**
309 * GtkCellAreaBox:spacing:
310 *
311 * The amount of space to reserve between cells.
312 */
313 g_object_class_install_property (oclass: object_class,
314 property_id: PROP_SPACING,
315 pspec: g_param_spec_int (name: "spacing",
316 P_("Spacing"),
317 P_("Space which is inserted between cells"),
318 minimum: 0,
319 G_MAXINT,
320 default_value: 0,
321 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
322
323 /* Cell Properties */
324 /**
325 * GtkCellAreaBox:expand:
326 *
327 * Whether the cell renderer should receive extra space
328 * when the area receives more than its natural size.
329 */
330 gtk_cell_area_class_install_cell_property (aclass: area_class,
331 property_id: CELL_PROP_EXPAND,
332 pspec: g_param_spec_boolean
333 (name: "expand",
334 P_("Expand"),
335 P_("Whether the cell expands"),
336 FALSE,
337 GTK_PARAM_READWRITE));
338
339 /**
340 * GtkCellAreaBox:align:
341 *
342 * Whether the cell renderer should be aligned in adjacent rows.
343 */
344 gtk_cell_area_class_install_cell_property (aclass: area_class,
345 property_id: CELL_PROP_ALIGN,
346 pspec: g_param_spec_boolean
347 (name: "align",
348 P_("Align"),
349 P_("Whether cell should align with adjacent rows"),
350 FALSE,
351 GTK_PARAM_READWRITE));
352
353 /**
354 * GtkCellAreaBox:fixed-size:
355 *
356 * Whether the cell renderer should require the same size
357 * for all rows for which it was requested.
358 */
359 gtk_cell_area_class_install_cell_property (aclass: area_class,
360 property_id: CELL_PROP_FIXED_SIZE,
361 pspec: g_param_spec_boolean
362 (name: "fixed-size",
363 P_("Fixed Size"),
364 P_("Whether cells should be the same size in all rows"),
365 TRUE,
366 GTK_PARAM_READWRITE));
367
368 /**
369 * GtkCellAreaBox:pack-type:
370 *
371 * A GtkPackType indicating whether the cell renderer is packed
372 * with reference to the start or end of the area.
373 */
374 gtk_cell_area_class_install_cell_property (aclass: area_class,
375 property_id: CELL_PROP_PACK_TYPE,
376 pspec: g_param_spec_enum
377 (name: "pack-type",
378 P_("Pack Type"),
379 P_("A GtkPackType indicating whether the cell is packed with "
380 "reference to the start or end of the cell area"),
381 enum_type: GTK_TYPE_PACK_TYPE, default_value: GTK_PACK_START,
382 GTK_PARAM_READWRITE));
383}
384
385
386/*************************************************************
387 * CellInfo/CellGroup basics and convenience functions *
388 *************************************************************/
389static CellInfo *
390cell_info_new (GtkCellRenderer *renderer,
391 GtkPackType pack,
392 gboolean expand,
393 gboolean align,
394 gboolean fixed)
395{
396 CellInfo *info = g_slice_new (CellInfo);
397
398 info->renderer = g_object_ref_sink (renderer);
399 info->pack = pack;
400 info->expand = expand;
401 info->align = align;
402 info->fixed = fixed;
403
404 return info;
405}
406
407static void
408cell_info_free (CellInfo *info)
409{
410 g_object_unref (object: info->renderer);
411
412 g_slice_free (CellInfo, info);
413}
414
415static int
416cell_info_find (CellInfo *info,
417 GtkCellRenderer *renderer)
418{
419 return (info->renderer == renderer) ? 0 : -1;
420}
421
422static AllocatedCell *
423allocated_cell_new (GtkCellRenderer *renderer,
424 int position,
425 int size)
426{
427 AllocatedCell *cell = g_slice_new (AllocatedCell);
428
429 cell->renderer = renderer;
430 cell->position = position;
431 cell->size = size;
432
433 return cell;
434}
435
436static void
437allocated_cell_free (AllocatedCell *cell)
438{
439 g_slice_free (AllocatedCell, cell);
440}
441
442static GList *
443list_consecutive_cells (GtkCellAreaBox *box)
444{
445 GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (self: box);
446 GList *l, *consecutive_cells = NULL, *pack_end_cells = NULL;
447 CellInfo *info;
448
449 /* List cells in consecutive order taking their
450 * PACK_START/PACK_END options into account
451 */
452 for (l = priv->cells; l; l = l->next)
453 {
454 info = l->data;
455
456 if (info->pack == GTK_PACK_START)
457 consecutive_cells = g_list_prepend (list: consecutive_cells, data: info);
458 }
459
460 for (l = priv->cells; l; l = l->next)
461 {
462 info = l->data;
463
464 if (info->pack == GTK_PACK_END)
465 pack_end_cells = g_list_prepend (list: pack_end_cells, data: info);
466 }
467
468 consecutive_cells = g_list_reverse (list: consecutive_cells);
469 consecutive_cells = g_list_concat (list1: consecutive_cells, list2: pack_end_cells);
470
471 return consecutive_cells;
472}
473
474static void
475cell_groups_clear (GtkCellAreaBox *box)
476{
477 GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (self: box);
478 int i;
479
480 for (i = 0; i < priv->groups->len; i++)
481 {
482 CellGroup *group = &g_array_index (priv->groups, CellGroup, i);
483
484 g_list_free (list: group->cells);
485 }
486
487 g_array_set_size (array: priv->groups, length: 0);
488}
489
490static void
491cell_groups_rebuild (GtkCellAreaBox *box)
492{
493 GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (self: box);
494 CellGroup group = { 0, };
495 CellGroup *group_ptr;
496 GList *cells, *l;
497 guint id = 0;
498 gboolean last_cell_fixed = FALSE;
499
500 cell_groups_clear (box);
501
502 if (!priv->cells)
503 return;
504
505 cells = list_consecutive_cells (box);
506
507 /* First group is implied */
508 g_array_append_val (priv->groups, group);
509 group_ptr = &g_array_index (priv->groups, CellGroup, id);
510
511 for (l = cells; l; l = l->next)
512 {
513 CellInfo *info = l->data;
514
515 /* A new group starts with any aligned cell, or
516 * at the beginning and end of a fixed size cell.
517 * the first group is implied */
518 if ((info->align || info->fixed || last_cell_fixed) && l != cells)
519 {
520 memset (s: &group, c: 0x0, n: sizeof (CellGroup));
521 group.id = ++id;
522
523 g_array_append_val (priv->groups, group);
524 group_ptr = &g_array_index (priv->groups, CellGroup, id);
525 }
526
527 group_ptr->cells = g_list_prepend (list: group_ptr->cells, data: info);
528 group_ptr->n_cells++;
529
530 /* Not every group is aligned, some are floating
531 * fixed size cells */
532 if (info->align)
533 group_ptr->align = TRUE;
534
535 /* A group expands if it contains any expand cells */
536 if (info->expand)
537 group_ptr->expand_cells++;
538
539 last_cell_fixed = info->fixed;
540 }
541
542 g_list_free (list: cells);
543
544 for (id = 0; id < priv->groups->len; id++)
545 {
546 group_ptr = &g_array_index (priv->groups, CellGroup, id);
547
548 group_ptr->cells = g_list_reverse (list: group_ptr->cells);
549 }
550
551 /* Contexts need to be updated with the new grouping information */
552 init_context_groups (box);
553}
554
555static int
556count_visible_cells (CellGroup *group,
557 int *expand_cells)
558{
559 GList *l;
560 int visible_cells = 0;
561 int n_expand_cells = 0;
562
563 for (l = group->cells; l; l = l->next)
564 {
565 CellInfo *info = l->data;
566
567 if (gtk_cell_renderer_get_visible (cell: info->renderer))
568 {
569 visible_cells++;
570
571 if (info->expand)
572 n_expand_cells++;
573 }
574 }
575
576 if (expand_cells)
577 *expand_cells = n_expand_cells;
578
579 return visible_cells;
580}
581
582static int
583count_expand_groups (GtkCellAreaBox *box)
584{
585 GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (self: box);
586 int i;
587 int expand_groups = 0;
588
589 for (i = 0; i < priv->groups->len; i++)
590 {
591 CellGroup *group = &g_array_index (priv->groups, CellGroup, i);
592
593 if (group->expand_cells > 0)
594 expand_groups++;
595 }
596
597 return expand_groups;
598}
599
600static void
601context_weak_notify (GtkCellAreaBox *box,
602 GtkCellAreaBoxContext *dead_context)
603{
604 GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (self: box);
605
606 priv->contexts = g_slist_remove (list: priv->contexts, data: dead_context);
607}
608
609static void
610init_context_group (GtkCellAreaBox *box,
611 GtkCellAreaBoxContext *context)
612{
613 GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (self: box);
614 int *expand_groups, *align_groups, i;
615
616 expand_groups = g_new (gboolean, priv->groups->len);
617 align_groups = g_new (gboolean, priv->groups->len);
618
619 for (i = 0; i < priv->groups->len; i++)
620 {
621 CellGroup *group = &g_array_index (priv->groups, CellGroup, i);
622
623 expand_groups[i] = (group->expand_cells > 0);
624 align_groups[i] = group->align;
625 }
626
627 /* This call implies resetting the request info */
628 _gtk_cell_area_box_init_groups (box_context: context, n_groups: priv->groups->len, expand_groups, align_groups);
629 g_free (mem: expand_groups);
630 g_free (mem: align_groups);
631}
632
633static void
634init_context_groups (GtkCellAreaBox *box)
635{
636 GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (self: box);
637 GSList *l;
638
639 /* When the box's groups are reconstructed,
640 * contexts need to be reinitialized.
641 */
642 for (l = priv->contexts; l; l = l->next)
643 {
644 GtkCellAreaBoxContext *context = l->data;
645
646 init_context_group (box, context);
647 }
648}
649
650static void
651reset_contexts (GtkCellAreaBox *box)
652{
653 GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (self: box);
654 GSList *l;
655
656 /* When the box layout changes, contexts need to
657 * be reset and sizes for the box get requested again
658 */
659 for (l = priv->contexts; l; l = l->next)
660 {
661 GtkCellAreaContext *context = l->data;
662
663 gtk_cell_area_context_reset (context);
664 }
665}
666
667/* Fall back on a completely unaligned dynamic allocation of cells
668 * when not allocated for the said orientation, alignment of cells
669 * is not done when each area gets a different size in the orientation
670 * of the box.
671 */
672static GSList *
673allocate_cells_manually (GtkCellAreaBox *box,
674 GtkWidget *widget,
675 int width,
676 int height)
677{
678 GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (self: box);
679 GList *cells, *l;
680 GSList *allocated_cells = NULL;
681 GtkRequestedSize *sizes;
682 int i;
683 int nvisible = 0, nexpand = 0, group_expand;
684 int avail_size, extra_size, extra_extra, full_size;
685 int position = 0, for_size;
686 gboolean rtl;
687
688 if (!priv->cells)
689 return NULL;
690
691 /* For vertical oriented boxes, we just let the cell renderers
692 * realign themselves for rtl
693 */
694 rtl = (priv->orientation == GTK_ORIENTATION_HORIZONTAL &&
695 gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
696
697 cells = list_consecutive_cells (box);
698
699 /* Count the visible and expand cells */
700 for (i = 0; i < priv->groups->len; i++)
701 {
702 CellGroup *group = &g_array_index (priv->groups, CellGroup, i);
703
704 nvisible += count_visible_cells (group, expand_cells: &group_expand);
705 nexpand += group_expand;
706 }
707
708 if (nvisible <= 0)
709 {
710 g_list_free (list: cells);
711 return NULL;
712 }
713
714 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
715 {
716 full_size = avail_size = width;
717 for_size = height;
718 }
719 else
720 {
721 full_size = avail_size = height;
722 for_size = width;
723 }
724
725 /* Go ahead and collect the requests on the fly */
726 sizes = g_new0 (GtkRequestedSize, nvisible);
727 for (l = cells, i = 0; l; l = l->next)
728 {
729 CellInfo *info = l->data;
730
731 if (!gtk_cell_renderer_get_visible (cell: info->renderer))
732 continue;
733
734 gtk_cell_area_request_renderer (GTK_CELL_AREA (box), renderer: info->renderer,
735 orientation: priv->orientation,
736 widget, for_size,
737 minimum_size: &sizes[i].minimum_size,
738 natural_size: &sizes[i].natural_size);
739
740 avail_size -= sizes[i].minimum_size;
741
742 sizes[i].data = info;
743
744 i++;
745 }
746
747 /* Naturally distribute the allocation */
748 avail_size -= (nvisible - 1) * priv->spacing;
749 if (avail_size > 0)
750 avail_size = gtk_distribute_natural_allocation (extra_space: avail_size, n_requested_sizes: nvisible, sizes);
751 else
752 avail_size = 0;
753
754 /* Calculate/distribute expand for cells */
755 if (nexpand > 0)
756 {
757 extra_size = avail_size / nexpand;
758 extra_extra = avail_size % nexpand;
759 }
760 else
761 extra_size = extra_extra = 0;
762
763 /* Create the allocated cells */
764 for (i = 0; i < nvisible; i++)
765 {
766 CellInfo *info = sizes[i].data;
767 AllocatedCell *cell;
768
769 if (info->expand)
770 {
771 sizes[i].minimum_size += extra_size;
772 if (extra_extra)
773 {
774 sizes[i].minimum_size++;
775 extra_extra--;
776 }
777 }
778
779 if (rtl)
780 cell = allocated_cell_new (renderer: info->renderer,
781 position: full_size - (position + sizes[i].minimum_size),
782 size: sizes[i].minimum_size);
783 else
784 cell = allocated_cell_new (renderer: info->renderer, position, size: sizes[i].minimum_size);
785
786 allocated_cells = g_slist_prepend (list: allocated_cells, data: cell);
787
788 position += sizes[i].minimum_size;
789 position += priv->spacing;
790 }
791
792 g_free (mem: sizes);
793 g_list_free (list: cells);
794
795 /* Note it might not be important to reverse the list here at all,
796 * we have the correct positions, no need to allocate from left to right
797 */
798 return g_slist_reverse (list: allocated_cells);
799}
800
801/* Returns an allocation for each cell in the orientation of the box,
802 * used in ->render()/->event() implementations to get a straight-forward
803 * list of allocated cells to operate on.
804 */
805static GSList *
806get_allocated_cells (GtkCellAreaBox *box,
807 GtkCellAreaBoxContext *context,
808 GtkWidget *widget,
809 int width,
810 int height)
811{
812 GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (self: box);
813 GtkCellAreaBoxAllocation *group_allocs;
814 GtkCellArea *area = GTK_CELL_AREA (box);
815 GList *cell_list;
816 GSList *allocated_cells = NULL;
817 int i, j, n_allocs, position;
818 int for_size, full_size;
819 gboolean rtl;
820
821 group_allocs = _gtk_cell_area_box_context_get_orientation_allocs (context, n_allocs: &n_allocs);
822 if (!group_allocs)
823 return allocate_cells_manually (box, widget, width, height);
824
825 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
826 {
827 full_size = width;
828 for_size = height;
829 }
830 else
831 {
832 full_size = height;
833 for_size = width;
834 }
835
836 /* For vertical oriented boxes, we just let the cell renderers
837 * realign themselves for rtl
838 */
839 rtl = (priv->orientation == GTK_ORIENTATION_HORIZONTAL &&
840 gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
841
842 for (position = 0, i = 0; i < n_allocs; i++)
843 {
844 /* We dont always allocate all groups, sometimes the requested
845 * group has only invisible cells for every row, hence the usage
846 * of group_allocs[i].group_idx here
847 */
848 CellGroup *group = &g_array_index (priv->groups, CellGroup, group_allocs[i].group_idx);
849
850 /* Exception for single cell groups */
851 if (group->n_cells == 1)
852 {
853 CellInfo *info = group->cells->data;
854 AllocatedCell *cell;
855 int cell_position, cell_size;
856
857 if (!gtk_cell_renderer_get_visible (cell: info->renderer))
858 continue;
859
860 /* If were not aligned, place the cell after the last cell */
861 if (info->align)
862 position = cell_position = group_allocs[i].position;
863 else
864 cell_position = position;
865
866 /* If not a fixed size, use only the requested size for this row */
867 if (info->fixed)
868 cell_size = group_allocs[i].size;
869 else
870 {
871 int dummy;
872 gtk_cell_area_request_renderer (area, renderer: info->renderer,
873 orientation: priv->orientation,
874 widget, for_size,
875 minimum_size: &dummy,
876 natural_size: &cell_size);
877 cell_size = MIN (cell_size, group_allocs[i].size);
878 }
879
880 if (rtl)
881 cell = allocated_cell_new (renderer: info->renderer,
882 position: full_size - (cell_position + cell_size), size: cell_size);
883 else
884 cell = allocated_cell_new (renderer: info->renderer, position: cell_position, size: cell_size);
885
886 position += cell_size;
887 position += priv->spacing;
888
889 allocated_cells = g_slist_prepend (list: allocated_cells, data: cell);
890 }
891 else
892 {
893 GtkRequestedSize *sizes;
894 int avail_size, cell_position;
895 int visible_cells, expand_cells;
896 int extra_size, extra_extra;
897
898 visible_cells = count_visible_cells (group, expand_cells: &expand_cells);
899
900 /* If this row has no visible cells in this group, just
901 * skip the allocation
902 */
903 if (visible_cells == 0)
904 continue;
905
906 /* If were not aligned, place the cell after the last cell
907 * and eat up the extra space
908 */
909 if (group->align)
910 {
911 avail_size = group_allocs[i].size;
912 position = cell_position = group_allocs[i].position;
913 }
914 else
915 {
916 avail_size = group_allocs[i].size + (group_allocs[i].position - position);
917 cell_position = position;
918 }
919
920 sizes = g_new (GtkRequestedSize, visible_cells);
921
922 for (j = 0, cell_list = group->cells; cell_list; cell_list = cell_list->next)
923 {
924 CellInfo *info = cell_list->data;
925
926 if (!gtk_cell_renderer_get_visible (cell: info->renderer))
927 continue;
928
929 gtk_cell_area_request_renderer (area, renderer: info->renderer,
930 orientation: priv->orientation,
931 widget, for_size,
932 minimum_size: &sizes[j].minimum_size,
933 natural_size: &sizes[j].natural_size);
934
935 sizes[j].data = info;
936 avail_size -= sizes[j].minimum_size;
937
938 j++;
939 }
940
941 /* Distribute cells naturally within the group */
942 avail_size -= (visible_cells - 1) * priv->spacing;
943 if (avail_size > 0)
944 avail_size = gtk_distribute_natural_allocation (extra_space: avail_size, n_requested_sizes: visible_cells, sizes);
945 else
946 avail_size = 0;
947
948 /* Calculate/distribute expand for cells */
949 if (expand_cells > 0)
950 {
951 extra_size = avail_size / expand_cells;
952 extra_extra = avail_size % expand_cells;
953 }
954 else
955 extra_size = extra_extra = 0;
956
957 /* Create the allocated cells (loop only over visible cells here) */
958 for (j = 0; j < visible_cells; j++)
959 {
960 CellInfo *info = sizes[j].data;
961 AllocatedCell *cell;
962
963 if (info->expand)
964 {
965 sizes[j].minimum_size += extra_size;
966 if (extra_extra)
967 {
968 sizes[j].minimum_size++;
969 extra_extra--;
970 }
971 }
972
973 if (rtl)
974 cell = allocated_cell_new (renderer: info->renderer,
975 position: full_size - (cell_position + sizes[j].minimum_size),
976 size: sizes[j].minimum_size);
977 else
978 cell = allocated_cell_new (renderer: info->renderer, position: cell_position, size: sizes[j].minimum_size);
979
980 allocated_cells = g_slist_prepend (list: allocated_cells, data: cell);
981
982 cell_position += sizes[j].minimum_size;
983 cell_position += priv->spacing;
984 }
985
986 g_free (mem: sizes);
987
988 position = cell_position;
989 }
990 }
991
992 g_free (mem: group_allocs);
993
994 /* Note it might not be important to reverse the list here at all,
995 * we have the correct positions, no need to allocate from left to right
996 */
997 return g_slist_reverse (list: allocated_cells);
998}
999
1000
1001static void
1002gtk_cell_area_box_focus_changed (GtkCellArea *area,
1003 GParamSpec *pspec,
1004 GtkCellAreaBox *box)
1005{
1006 GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (self: box);
1007
1008 if (gtk_cell_area_get_focus_cell (area))
1009 priv->last_focus_cell = gtk_cell_area_get_focus_cell (area);
1010}
1011
1012/*************************************************************
1013 * GObjectClass *
1014 *************************************************************/
1015static void
1016gtk_cell_area_box_finalize (GObject *object)
1017{
1018 GtkCellAreaBox *box = GTK_CELL_AREA_BOX (object);
1019 GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (self: box);
1020 GSList *l;
1021
1022 /* Unref/free the context list */
1023 for (l = priv->contexts; l; l = l->next)
1024 g_object_weak_unref (G_OBJECT (l->data), notify: (GWeakNotify)context_weak_notify, data: box);
1025
1026 g_slist_free (list: priv->contexts);
1027 priv->contexts = NULL;
1028
1029 /* Free the cell grouping info */
1030 cell_groups_clear (box);
1031 g_array_free (array: priv->groups, TRUE);
1032
1033 G_OBJECT_CLASS (gtk_cell_area_box_parent_class)->finalize (object);
1034}
1035
1036static void
1037gtk_cell_area_box_dispose (GObject *object)
1038{
1039 G_OBJECT_CLASS (gtk_cell_area_box_parent_class)->dispose (object);
1040}
1041
1042static void
1043gtk_cell_area_box_set_property (GObject *object,
1044 guint prop_id,
1045 const GValue *value,
1046 GParamSpec *pspec)
1047{
1048 GtkCellAreaBox *box = GTK_CELL_AREA_BOX (object);
1049 GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (self: box);
1050
1051 switch (prop_id)
1052 {
1053 case PROP_ORIENTATION:
1054 if (priv->orientation != g_value_get_enum (value))
1055 {
1056 priv->orientation = g_value_get_enum (value);
1057 /* Notify that size needs to be requested again */
1058 reset_contexts (box);
1059 g_object_notify_by_pspec (object, pspec);
1060 }
1061 break;
1062 case PROP_SPACING:
1063 gtk_cell_area_box_set_spacing (box, spacing: g_value_get_int (value));
1064 break;
1065 default:
1066 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1067 break;
1068 }
1069}
1070
1071static void
1072gtk_cell_area_box_get_property (GObject *object,
1073 guint prop_id,
1074 GValue *value,
1075 GParamSpec *pspec)
1076{
1077 GtkCellAreaBox *box = GTK_CELL_AREA_BOX (object);
1078 GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (self: box);
1079
1080 switch (prop_id)
1081 {
1082 case PROP_ORIENTATION:
1083 g_value_set_enum (value, v_enum: priv->orientation);
1084 break;
1085 case PROP_SPACING:
1086 g_value_set_int (value, v_int: gtk_cell_area_box_get_spacing (box));
1087 break;
1088 default:
1089 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1090 break;
1091 }
1092}
1093
1094/*************************************************************
1095 * GtkCellAreaClass *
1096 *************************************************************/
1097static void
1098gtk_cell_area_box_add (GtkCellArea *area,
1099 GtkCellRenderer *renderer)
1100{
1101 gtk_cell_area_box_pack_start (GTK_CELL_AREA_BOX (area),
1102 renderer, FALSE, FALSE, TRUE);
1103}
1104
1105static void
1106gtk_cell_area_box_remove (GtkCellArea *area,
1107 GtkCellRenderer *renderer)
1108{
1109 GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area);
1110 GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (self: box);
1111 GList *node;
1112
1113 if (priv->last_focus_cell == renderer)
1114 priv->last_focus_cell = NULL;
1115
1116 node = g_list_find_custom (list: priv->cells, data: renderer,
1117 func: (GCompareFunc)cell_info_find);
1118
1119 if (node)
1120 {
1121 CellInfo *info = node->data;
1122
1123 cell_info_free (info);
1124
1125 priv->cells = g_list_delete_link (list: priv->cells, link_: node);
1126
1127 /* Reconstruct cell groups */
1128 cell_groups_rebuild (box);
1129 }
1130 else
1131 g_warning ("Trying to remove a cell renderer that is not present GtkCellAreaBox");
1132}
1133
1134static void
1135gtk_cell_area_box_foreach (GtkCellArea *area,
1136 GtkCellCallback callback,
1137 gpointer callback_data)
1138{
1139 GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area);
1140 GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (self: box);
1141 GList *list;
1142
1143 for (list = priv->cells; list; list = list->next)
1144 {
1145 CellInfo *info = list->data;
1146
1147 if (callback (info->renderer, callback_data))
1148 break;
1149 }
1150}
1151
1152static void
1153gtk_cell_area_box_foreach_alloc (GtkCellArea *area,
1154 GtkCellAreaContext *context,
1155 GtkWidget *widget,
1156 const GdkRectangle *cell_area,
1157 const GdkRectangle *background_area,
1158 GtkCellAllocCallback callback,
1159 gpointer callback_data)
1160{
1161 GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area);
1162 GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (self: box);
1163 GtkCellAreaBoxContext *box_context = GTK_CELL_AREA_BOX_CONTEXT (context);
1164 GSList *allocated_cells, *l;
1165 GdkRectangle cell_alloc, cell_background;
1166 gboolean rtl;
1167
1168 rtl = (priv->orientation == GTK_ORIENTATION_HORIZONTAL &&
1169 gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
1170
1171 cell_alloc = *cell_area;
1172
1173 /* Get a list of cells with allocation sizes decided regardless
1174 * of alignments and pack order etc.
1175 */
1176 allocated_cells = get_allocated_cells (box, context: box_context, widget,
1177 width: cell_area->width, height: cell_area->height);
1178
1179 for (l = allocated_cells; l; l = l->next)
1180 {
1181 AllocatedCell *cell = l->data;
1182
1183 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
1184 {
1185 cell_alloc.x = cell_area->x + cell->position;
1186 cell_alloc.width = cell->size;
1187 }
1188 else
1189 {
1190 cell_alloc.y = cell_area->y + cell->position;
1191 cell_alloc.height = cell->size;
1192 }
1193
1194 /* Stop iterating over cells if they flow out of the render
1195 * area, this can happen because the render area can actually
1196 * be smaller than the requested area (treeview columns can
1197 * be user resizable and can be resized to be smaller than
1198 * the actual requested area).
1199 */
1200 if (cell_alloc.x > cell_area->x + cell_area->width ||
1201 cell_alloc.x + cell_alloc.width < cell_area->x ||
1202 cell_alloc.y > cell_area->y + cell_area->height)
1203 break;
1204
1205 /* Special case for the last cell (or first cell in rtl)...
1206 * let the last cell consume the remaining space in the area
1207 * (the last cell is allowed to consume the remaining space if
1208 * the space given for rendering is actually larger than allocation,
1209 * this can happen in the expander GtkTreeViewColumn where only the
1210 * deepest depth column receives the allocation... shallow columns
1211 * receive more width). */
1212 if (!l->next)
1213 {
1214 if (rtl)
1215 {
1216 /* Fill the leading space for the first cell in the area
1217 * (still last in the list)
1218 */
1219 cell_alloc.width = (cell_alloc.x - cell_area->x) + cell_alloc.width;
1220 cell_alloc.x = cell_area->x;
1221 }
1222 else
1223 {
1224 cell_alloc.width = cell_area->x + cell_area->width - cell_alloc.x;
1225 cell_alloc.height = cell_area->y + cell_area->height - cell_alloc.y;
1226 }
1227 }
1228 else
1229 {
1230 /* If the cell we are rendering doesn't fit into the remaining space,
1231 * clip it so that the underlying renderer has a chance to deal with
1232 * it (for instance text renderers get a chance to ellipsize).
1233 */
1234 if (cell_alloc.x + cell_alloc.width > cell_area->x + cell_area->width)
1235 cell_alloc.width = cell_area->x + cell_area->width - cell_alloc.x;
1236
1237 if (cell_alloc.y + cell_alloc.height > cell_area->y + cell_area->height)
1238 cell_alloc.height = cell_area->y + cell_area->height - cell_alloc.y;
1239 }
1240
1241 /* Add portions of the background_area to the cell_alloc
1242 * to create the cell_background
1243 */
1244 cell_background = cell_alloc;
1245
1246 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
1247 {
1248 if (l == allocated_cells)
1249 {
1250 /* Add the depth to the first cell */
1251 if (rtl)
1252 {
1253 cell_background.width += background_area->width - cell_area->width;
1254 cell_background.x = background_area->x + background_area->width - cell_background.width;
1255 }
1256 else
1257 {
1258 cell_background.width += cell_area->x - background_area->x;
1259 cell_background.x = background_area->x;
1260 }
1261 }
1262
1263 if (l->next == NULL)
1264 {
1265 /* Grant this cell the remaining space */
1266 int remain = cell_background.x - background_area->x;
1267
1268 if (rtl)
1269 cell_background.x -= remain;
1270 else
1271 cell_background.width = background_area->width - remain;
1272 }
1273
1274 cell_background.y = background_area->y;
1275 cell_background.height = background_area->height;
1276 }
1277 else
1278 {
1279 if (l == allocated_cells)
1280 {
1281 cell_background.height += cell_background.y - background_area->y;
1282 cell_background.y = background_area->y;
1283 }
1284
1285 if (l->next == NULL)
1286 cell_background.height =
1287 background_area->height - (cell_background.y - background_area->y);
1288
1289 cell_background.x = background_area->x;
1290 cell_background.width = background_area->width;
1291 }
1292
1293 if (callback (cell->renderer, &cell_alloc, &cell_background, callback_data))
1294 break;
1295 }
1296
1297 g_slist_free_full (list: allocated_cells, free_func: (GDestroyNotify)allocated_cell_free);
1298}
1299
1300static void
1301gtk_cell_area_box_apply_attributes (GtkCellArea *area,
1302 GtkTreeModel *tree_model,
1303 GtkTreeIter *iter,
1304 gboolean is_expander,
1305 gboolean is_expanded)
1306{
1307 GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area);
1308 GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (self: box);
1309 int i;
1310
1311 /* Call the parent class to apply the attributes */
1312 GTK_CELL_AREA_CLASS
1313 (gtk_cell_area_box_parent_class)->apply_attributes (area, tree_model, iter,
1314 is_expander, is_expanded);
1315
1316 /* Update visible state for cell groups */
1317 for (i = 0; i < priv->groups->len; i++)
1318 {
1319 CellGroup *group = &g_array_index (priv->groups, CellGroup, i);
1320 GList *list;
1321
1322 group->visible = FALSE;
1323
1324 for (list = group->cells; list && group->visible == FALSE; list = list->next)
1325 {
1326 CellInfo *info = list->data;
1327
1328 if (gtk_cell_renderer_get_visible (cell: info->renderer))
1329 group->visible = TRUE;
1330 }
1331 }
1332}
1333
1334static void
1335gtk_cell_area_box_set_cell_property (GtkCellArea *area,
1336 GtkCellRenderer *renderer,
1337 guint prop_id,
1338 const GValue *value,
1339 GParamSpec *pspec)
1340{
1341 GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area);
1342 GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (self: box);
1343 GList *node;
1344 CellInfo *info;
1345 gboolean rebuild = FALSE;
1346 gboolean val;
1347 GtkPackType pack_type;
1348
1349 node = g_list_find_custom (list: priv->cells, data: renderer,
1350 func: (GCompareFunc)cell_info_find);
1351 if (!node)
1352 return;
1353
1354 info = node->data;
1355
1356 switch (prop_id)
1357 {
1358 case CELL_PROP_EXPAND:
1359 val = g_value_get_boolean (value);
1360
1361 if (info->expand != val)
1362 {
1363 info->expand = val;
1364 rebuild = TRUE;
1365 }
1366 break;
1367
1368 case CELL_PROP_ALIGN:
1369 val = g_value_get_boolean (value);
1370
1371 if (info->align != val)
1372 {
1373 info->align = val;
1374 rebuild = TRUE;
1375 }
1376 break;
1377
1378 case CELL_PROP_FIXED_SIZE:
1379 val = g_value_get_boolean (value);
1380
1381 if (info->fixed != val)
1382 {
1383 info->fixed = val;
1384 rebuild = TRUE;
1385 }
1386 break;
1387
1388 case CELL_PROP_PACK_TYPE:
1389 pack_type = g_value_get_enum (value);
1390
1391 if (info->pack != pack_type)
1392 {
1393 info->pack = pack_type;
1394 rebuild = TRUE;
1395 }
1396 break;
1397 default:
1398 GTK_CELL_AREA_WARN_INVALID_CELL_PROPERTY_ID (area, prop_id, pspec);
1399 break;
1400 }
1401
1402 /* Groups need to be rebuilt */
1403 if (rebuild)
1404 cell_groups_rebuild (box);
1405}
1406
1407static void
1408gtk_cell_area_box_get_cell_property (GtkCellArea *area,
1409 GtkCellRenderer *renderer,
1410 guint prop_id,
1411 GValue *value,
1412 GParamSpec *pspec)
1413{
1414 GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area);
1415 GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (self: box);
1416 GList *node;
1417 CellInfo *info;
1418
1419 node = g_list_find_custom (list: priv->cells, data: renderer,
1420 func: (GCompareFunc)cell_info_find);
1421 if (!node)
1422 return;
1423
1424 info = node->data;
1425
1426 switch (prop_id)
1427 {
1428 case CELL_PROP_EXPAND:
1429 g_value_set_boolean (value, v_boolean: info->expand);
1430 break;
1431
1432 case CELL_PROP_ALIGN:
1433 g_value_set_boolean (value, v_boolean: info->align);
1434 break;
1435
1436 case CELL_PROP_FIXED_SIZE:
1437 g_value_set_boolean (value, v_boolean: info->fixed);
1438 break;
1439
1440 case CELL_PROP_PACK_TYPE:
1441 g_value_set_enum (value, v_enum: info->pack);
1442 break;
1443 default:
1444 GTK_CELL_AREA_WARN_INVALID_CELL_PROPERTY_ID (area, prop_id, pspec);
1445 break;
1446 }
1447}
1448
1449
1450static GtkCellAreaContext *
1451gtk_cell_area_box_create_context (GtkCellArea *area)
1452{
1453 GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area);
1454 GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (self: box);
1455 GtkCellAreaContext *context =
1456 (GtkCellAreaContext *)g_object_new (GTK_TYPE_CELL_AREA_BOX_CONTEXT,
1457 first_property_name: "area", area, NULL);
1458
1459 priv->contexts = g_slist_prepend (list: priv->contexts, data: context);
1460
1461 g_object_weak_ref (G_OBJECT (context), notify: (GWeakNotify)context_weak_notify, data: box);
1462
1463 /* Tell the new group about our cell layout */
1464 init_context_group (box, GTK_CELL_AREA_BOX_CONTEXT (context));
1465
1466 return context;
1467}
1468
1469static GtkCellAreaContext *
1470gtk_cell_area_box_copy_context (GtkCellArea *area,
1471 GtkCellAreaContext *context)
1472{
1473 GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area);
1474 GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (self: box);
1475 GtkCellAreaContext *copy =
1476 (GtkCellAreaContext *)_gtk_cell_area_box_context_copy (GTK_CELL_AREA_BOX (area),
1477 GTK_CELL_AREA_BOX_CONTEXT (context));
1478
1479 priv->contexts = g_slist_prepend (list: priv->contexts, data: copy);
1480
1481 g_object_weak_ref (G_OBJECT (copy), notify: (GWeakNotify)context_weak_notify, data: box);
1482
1483 return copy;
1484}
1485
1486static GtkSizeRequestMode
1487gtk_cell_area_box_get_request_mode (GtkCellArea *area)
1488{
1489 GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area);
1490 GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (self: box);
1491
1492 return (priv->orientation) == GTK_ORIENTATION_HORIZONTAL ?
1493 GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH :
1494 GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT;
1495}
1496
1497static void
1498compute_size (GtkCellAreaBox *box,
1499 GtkOrientation orientation,
1500 GtkCellAreaBoxContext *context,
1501 GtkWidget *widget,
1502 int for_size,
1503 int *minimum_size,
1504 int *natural_size)
1505{
1506 GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (self: box);
1507 GtkCellArea *area = GTK_CELL_AREA (box);
1508 GList *list;
1509 int i;
1510 int min_size = 0;
1511 int nat_size = 0;
1512
1513 for (i = 0; i < priv->groups->len; i++)
1514 {
1515 CellGroup *group = &g_array_index (priv->groups, CellGroup, i);
1516 int group_min_size = 0;
1517 int group_nat_size = 0;
1518
1519 for (list = group->cells; list; list = list->next)
1520 {
1521 CellInfo *info = list->data;
1522 int renderer_min_size, renderer_nat_size;
1523
1524 if (!gtk_cell_renderer_get_visible (cell: info->renderer))
1525 continue;
1526
1527 gtk_cell_area_request_renderer (area, renderer: info->renderer, orientation, widget, for_size,
1528 minimum_size: &renderer_min_size, natural_size: &renderer_nat_size);
1529
1530 if (orientation == priv->orientation)
1531 {
1532 if (min_size > 0)
1533 {
1534 min_size += priv->spacing;
1535 nat_size += priv->spacing;
1536 }
1537
1538 if (group_min_size > 0)
1539 {
1540 group_min_size += priv->spacing;
1541 group_nat_size += priv->spacing;
1542 }
1543
1544 min_size += renderer_min_size;
1545 nat_size += renderer_nat_size;
1546 group_min_size += renderer_min_size;
1547 group_nat_size += renderer_nat_size;
1548 }
1549 else
1550 {
1551 min_size = MAX (min_size, renderer_min_size);
1552 nat_size = MAX (nat_size, renderer_nat_size);
1553 group_min_size = MAX (group_min_size, renderer_min_size);
1554 group_nat_size = MAX (group_nat_size, renderer_nat_size);
1555 }
1556 }
1557
1558 if (orientation == GTK_ORIENTATION_HORIZONTAL)
1559 {
1560 if (for_size < 0)
1561 _gtk_cell_area_box_context_push_group_width (box_context: context, group_idx: group->id, minimum_width: group_min_size, natural_width: group_nat_size);
1562 else
1563 _gtk_cell_area_box_context_push_group_width_for_height (box_context: context, group_idx: group->id, for_height: for_size,
1564 minimum_width: group_min_size, natural_width: group_nat_size);
1565 }
1566 else
1567 {
1568 if (for_size < 0)
1569 _gtk_cell_area_box_context_push_group_height (box_context: context, group_idx: group->id, minimum_height: group_min_size, natural_height: group_nat_size);
1570 else
1571 _gtk_cell_area_box_context_push_group_height_for_width (box_context: context, group_idx: group->id, for_width: for_size,
1572 minimum_height: group_min_size, natural_height: group_nat_size);
1573 }
1574 }
1575
1576 *minimum_size = min_size;
1577 *natural_size = nat_size;
1578
1579 /* Update rtl state for focus navigation to work */
1580 priv->rtl = (priv->orientation == GTK_ORIENTATION_HORIZONTAL &&
1581 gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
1582}
1583
1584static GtkRequestedSize *
1585get_group_sizes (GtkCellArea *area,
1586 CellGroup *group,
1587 GtkOrientation orientation,
1588 GtkWidget *widget,
1589 int *n_sizes)
1590{
1591 GtkRequestedSize *sizes;
1592 GList *l;
1593 int i;
1594
1595 *n_sizes = count_visible_cells (group, NULL);
1596 sizes = g_new (GtkRequestedSize, *n_sizes);
1597
1598 for (l = group->cells, i = 0; l; l = l->next)
1599 {
1600 CellInfo *info = l->data;
1601
1602 if (!gtk_cell_renderer_get_visible (cell: info->renderer))
1603 continue;
1604
1605 sizes[i].data = info;
1606
1607 gtk_cell_area_request_renderer (area, renderer: info->renderer,
1608 orientation, widget, for_size: -1,
1609 minimum_size: &sizes[i].minimum_size,
1610 natural_size: &sizes[i].natural_size);
1611
1612 i++;
1613 }
1614
1615 return sizes;
1616}
1617
1618static void
1619compute_group_size_for_opposing_orientation (GtkCellAreaBox *box,
1620 CellGroup *group,
1621 GtkWidget *widget,
1622 int for_size,
1623 int *minimum_size,
1624 int *natural_size)
1625{
1626 GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (self: box);
1627 GtkCellArea *area = GTK_CELL_AREA (box);
1628
1629 /* Exception for single cell groups */
1630 if (group->n_cells == 1)
1631 {
1632 CellInfo *info = group->cells->data;
1633
1634 gtk_cell_area_request_renderer (area, renderer: info->renderer,
1635 OPPOSITE_ORIENTATION (priv->orientation),
1636 widget, for_size, minimum_size, natural_size);
1637 }
1638 else
1639 {
1640 GtkRequestedSize *orientation_sizes;
1641 CellInfo *info;
1642 int n_sizes, i;
1643 int avail_size = for_size;
1644 int extra_size, extra_extra;
1645 int min_size = 0, nat_size = 0;
1646
1647 orientation_sizes = get_group_sizes (area, group, orientation: priv->orientation, widget, n_sizes: &n_sizes);
1648
1649 /* First naturally allocate the cells in the group into the for_size */
1650 avail_size -= (n_sizes - 1) * priv->spacing;
1651 for (i = 0; i < n_sizes; i++)
1652 avail_size -= orientation_sizes[i].minimum_size;
1653
1654 if (avail_size > 0)
1655 avail_size = gtk_distribute_natural_allocation (extra_space: avail_size, n_requested_sizes: n_sizes, sizes: orientation_sizes);
1656 else
1657 avail_size = 0;
1658
1659 /* Calculate/distribute expand for cells */
1660 if (group->expand_cells > 0)
1661 {
1662 extra_size = avail_size / group->expand_cells;
1663 extra_extra = avail_size % group->expand_cells;
1664 }
1665 else
1666 extra_size = extra_extra = 0;
1667
1668 for (i = 0; i < n_sizes; i++)
1669 {
1670 int cell_min, cell_nat;
1671
1672 info = orientation_sizes[i].data;
1673
1674 if (info->expand)
1675 {
1676 orientation_sizes[i].minimum_size += extra_size;
1677 if (extra_extra)
1678 {
1679 orientation_sizes[i].minimum_size++;
1680 extra_extra--;
1681 }
1682 }
1683
1684 gtk_cell_area_request_renderer (area, renderer: info->renderer,
1685 OPPOSITE_ORIENTATION (priv->orientation),
1686 widget,
1687 for_size: orientation_sizes[i].minimum_size,
1688 minimum_size: &cell_min, natural_size: &cell_nat);
1689
1690 min_size = MAX (min_size, cell_min);
1691 nat_size = MAX (nat_size, cell_nat);
1692 }
1693
1694 *minimum_size = min_size;
1695 *natural_size = nat_size;
1696
1697 g_free (mem: orientation_sizes);
1698 }
1699}
1700
1701static void
1702compute_size_for_opposing_orientation (GtkCellAreaBox *box,
1703 GtkCellAreaBoxContext *context,
1704 GtkWidget *widget,
1705 int for_size,
1706 int *minimum_size,
1707 int *natural_size)
1708{
1709 GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (self: box);
1710 CellGroup *group;
1711 GtkRequestedSize *orientation_sizes;
1712 int n_groups, n_expand_groups, i;
1713 int avail_size = for_size;
1714 int extra_size, extra_extra;
1715 int min_size = 0, nat_size = 0;
1716
1717 n_expand_groups = count_expand_groups (box);
1718
1719 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
1720 orientation_sizes = _gtk_cell_area_box_context_get_widths (box_context: context, n_widths: &n_groups);
1721 else
1722 orientation_sizes = _gtk_cell_area_box_context_get_heights (box_context: context, n_heights: &n_groups);
1723
1724 /* First start by naturally allocating space among groups of cells */
1725 avail_size -= (n_groups - 1) * priv->spacing;
1726 for (i = 0; i < n_groups; i++)
1727 avail_size -= orientation_sizes[i].minimum_size;
1728
1729 if (avail_size > 0)
1730 avail_size = gtk_distribute_natural_allocation (extra_space: avail_size, n_requested_sizes: n_groups, sizes: orientation_sizes);
1731 else
1732 avail_size = 0;
1733
1734 /* Calculate/distribute expand for groups */
1735 if (n_expand_groups > 0)
1736 {
1737 extra_size = avail_size / n_expand_groups;
1738 extra_extra = avail_size % n_expand_groups;
1739 }
1740 else
1741 extra_size = extra_extra = 0;
1742
1743 /* Now we need to naturally allocate sizes for cells in each group
1744 * and push the height-for-width for each group accordingly while
1745 * accumulating the overall height-for-width for this row.
1746 */
1747 for (i = 0; i < n_groups; i++)
1748 {
1749 int group_min, group_nat;
1750 int group_idx = GPOINTER_TO_INT (orientation_sizes[i].data);
1751
1752 group = &g_array_index (priv->groups, CellGroup, group_idx);
1753
1754 if (group->expand_cells > 0)
1755 {
1756 orientation_sizes[i].minimum_size += extra_size;
1757 if (extra_extra)
1758 {
1759 orientation_sizes[i].minimum_size++;
1760 extra_extra--;
1761 }
1762 }
1763
1764 /* Now we have the allocation for the group,
1765 * request its height-for-width
1766 */
1767 compute_group_size_for_opposing_orientation (box, group, widget,
1768 for_size: orientation_sizes[i].minimum_size,
1769 minimum_size: &group_min, natural_size: &group_nat);
1770
1771 min_size = MAX (min_size, group_min);
1772 nat_size = MAX (nat_size, group_nat);
1773
1774 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
1775 {
1776 _gtk_cell_area_box_context_push_group_height_for_width (box_context: context, group_idx, for_width: for_size,
1777 minimum_height: group_min, natural_height: group_nat);
1778 }
1779 else
1780 {
1781 _gtk_cell_area_box_context_push_group_width_for_height (box_context: context, group_idx, for_height: for_size,
1782 minimum_width: group_min, natural_width: group_nat);
1783 }
1784 }
1785
1786 *minimum_size = min_size;
1787 *natural_size = nat_size;
1788
1789 g_free (mem: orientation_sizes);
1790
1791 /* Update rtl state for focus navigation to work */
1792 priv->rtl = (priv->orientation == GTK_ORIENTATION_HORIZONTAL &&
1793 gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
1794}
1795
1796
1797
1798static void
1799gtk_cell_area_box_get_preferred_width (GtkCellArea *area,
1800 GtkCellAreaContext *context,
1801 GtkWidget *widget,
1802 int *minimum_width,
1803 int *natural_width)
1804{
1805 GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area);
1806 GtkCellAreaBoxContext *box_context;
1807 int min_width, nat_width;
1808
1809 g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (context));
1810
1811 box_context = GTK_CELL_AREA_BOX_CONTEXT (context);
1812
1813 /* Compute the size of all renderers for current row data,
1814 * bumping cell alignments in the context along the way
1815 */
1816 compute_size (box, orientation: GTK_ORIENTATION_HORIZONTAL,
1817 context: box_context, widget, for_size: -1, minimum_size: &min_width, natural_size: &nat_width);
1818
1819 if (minimum_width)
1820 *minimum_width = min_width;
1821
1822 if (natural_width)
1823 *natural_width = nat_width;
1824}
1825
1826static void
1827gtk_cell_area_box_get_preferred_height (GtkCellArea *area,
1828 GtkCellAreaContext *context,
1829 GtkWidget *widget,
1830 int *minimum_height,
1831 int *natural_height)
1832{
1833 GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area);
1834 GtkCellAreaBoxContext *box_context;
1835 int min_height, nat_height;
1836
1837 g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (context));
1838
1839 box_context = GTK_CELL_AREA_BOX_CONTEXT (context);
1840
1841 /* Compute the size of all renderers for current row data,
1842 * bumping cell alignments in the context along the way
1843 */
1844 compute_size (box, orientation: GTK_ORIENTATION_VERTICAL,
1845 context: box_context, widget, for_size: -1, minimum_size: &min_height, natural_size: &nat_height);
1846
1847 if (minimum_height)
1848 *minimum_height = min_height;
1849
1850 if (natural_height)
1851 *natural_height = nat_height;
1852}
1853
1854static void
1855gtk_cell_area_box_get_preferred_height_for_width (GtkCellArea *area,
1856 GtkCellAreaContext *context,
1857 GtkWidget *widget,
1858 int width,
1859 int *minimum_height,
1860 int *natural_height)
1861{
1862 GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area);
1863 GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (self: box);
1864 GtkCellAreaBoxContext *box_context = GTK_CELL_AREA_BOX_CONTEXT (context);
1865 int min_height, nat_height;
1866
1867 g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (context));
1868
1869 if (priv->orientation == GTK_ORIENTATION_VERTICAL)
1870 {
1871 /* Add up vertical requests of height for width and push
1872 * the overall cached sizes for alignments
1873 */
1874 compute_size (box, orientation: priv->orientation, context: box_context, widget, for_size: width, minimum_size: &min_height, natural_size: &nat_height);
1875 }
1876 else
1877 {
1878 /* Juice: virtually allocate cells into the for_width using the
1879 * alignments and then return the overall height for that width,
1880 * and cache it
1881 */
1882 compute_size_for_opposing_orientation (box, context: box_context, widget, for_size: width, minimum_size: &min_height, natural_size: &nat_height);
1883 }
1884
1885 if (minimum_height)
1886 *minimum_height = min_height;
1887
1888 if (natural_height)
1889 *natural_height = nat_height;
1890}
1891
1892static void
1893gtk_cell_area_box_get_preferred_width_for_height (GtkCellArea *area,
1894 GtkCellAreaContext *context,
1895 GtkWidget *widget,
1896 int height,
1897 int *minimum_width,
1898 int *natural_width)
1899{
1900 GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area);
1901 GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (self: box);
1902 GtkCellAreaBoxContext *box_context = GTK_CELL_AREA_BOX_CONTEXT (context);
1903 int min_width, nat_width;
1904
1905 g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (context));
1906
1907 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
1908 {
1909 /* Add up horizontal requests of width for height and push
1910 * the overall cached sizes for alignments
1911 */
1912 compute_size (box, orientation: priv->orientation, context: box_context, widget, for_size: height, minimum_size: &min_width, natural_size: &nat_width);
1913 }
1914 else
1915 {
1916 /* Juice: horizontally allocate cells into the for_height using the
1917 * alignments and then return the overall width for that height,
1918 * and cache it
1919 */
1920 compute_size_for_opposing_orientation (box, context: box_context, widget, for_size: height, minimum_size: &min_width, natural_size: &nat_width);
1921 }
1922
1923 if (minimum_width)
1924 *minimum_width = min_width;
1925
1926 if (natural_width)
1927 *natural_width = nat_width;
1928}
1929
1930enum {
1931 FOCUS_NONE,
1932 FOCUS_PREV,
1933 FOCUS_NEXT,
1934 FOCUS_LAST_CELL
1935};
1936
1937static gboolean
1938gtk_cell_area_box_focus (GtkCellArea *area,
1939 GtkDirectionType direction)
1940{
1941 GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area);
1942 GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (self: box);
1943 int cycle = FOCUS_NONE;
1944 gboolean cycled_focus = FALSE;
1945 GtkCellRenderer *focus_cell;
1946
1947 focus_cell = gtk_cell_area_get_focus_cell (area);
1948
1949 /* Special case, when there is no activatable cell, focus
1950 * is painted around the entire area... in this case we
1951 * let focus leave the area directly.
1952 */
1953 if (focus_cell && !gtk_cell_area_is_activatable (area))
1954 {
1955 gtk_cell_area_set_focus_cell (area, NULL);
1956 return FALSE;
1957 }
1958
1959 switch (direction)
1960 {
1961 case GTK_DIR_TAB_FORWARD:
1962 cycle = priv->rtl ? FOCUS_PREV : FOCUS_NEXT;
1963 break;
1964 case GTK_DIR_TAB_BACKWARD:
1965 cycle = priv->rtl ? FOCUS_NEXT : FOCUS_PREV;
1966 break;
1967 case GTK_DIR_UP:
1968 if (priv->orientation == GTK_ORIENTATION_VERTICAL || !priv->last_focus_cell)
1969 cycle = FOCUS_PREV;
1970 else if (!focus_cell)
1971 cycle = FOCUS_LAST_CELL;
1972 break;
1973 case GTK_DIR_DOWN:
1974 if (priv->orientation == GTK_ORIENTATION_VERTICAL || !priv->last_focus_cell)
1975 cycle = FOCUS_NEXT;
1976 else if (!focus_cell)
1977 cycle = FOCUS_LAST_CELL;
1978 break;
1979 case GTK_DIR_LEFT:
1980 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL || !priv->last_focus_cell)
1981 cycle = priv->rtl ? FOCUS_NEXT : FOCUS_PREV;
1982 else if (!focus_cell)
1983 cycle = FOCUS_LAST_CELL;
1984 break;
1985 case GTK_DIR_RIGHT:
1986 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL || !priv->last_focus_cell)
1987 cycle = priv->rtl ? FOCUS_PREV : FOCUS_NEXT;
1988 else if (!focus_cell)
1989 cycle = FOCUS_LAST_CELL;
1990 break;
1991 default:
1992 break;
1993 }
1994
1995 if (cycle == FOCUS_LAST_CELL)
1996 {
1997 gtk_cell_area_set_focus_cell (area, renderer: priv->last_focus_cell);
1998 cycled_focus = TRUE;
1999 }
2000 else if (cycle != FOCUS_NONE)
2001 {
2002 gboolean found_cell = FALSE;
2003 GList *list;
2004 int i;
2005
2006 /* If there is no focused cell, focus on the first (or last) one */
2007 if (!focus_cell)
2008 found_cell = TRUE;
2009
2010 for (i = (cycle == FOCUS_NEXT) ? 0 : priv->groups->len -1;
2011 cycled_focus == FALSE && i >= 0 && i < priv->groups->len;
2012 i = (cycle == FOCUS_NEXT) ? i + 1 : i - 1)
2013 {
2014 CellGroup *group = &g_array_index (priv->groups, CellGroup, i);
2015
2016 for (list = (cycle == FOCUS_NEXT) ? g_list_first (list: group->cells) : g_list_last (list: group->cells);
2017 cycled_focus == FALSE && list; list = (cycle == FOCUS_NEXT) ? list->next : list->prev)
2018 {
2019 CellInfo *info = list->data;
2020
2021 if (info->renderer == focus_cell)
2022 found_cell = TRUE;
2023 else if (found_cell && /* Dont give focus to cells that are siblings to a focus cell */
2024 gtk_cell_area_get_focus_from_sibling (area, renderer: info->renderer) == NULL)
2025 {
2026 gtk_cell_area_set_focus_cell (area, renderer: info->renderer);
2027 cycled_focus = TRUE;
2028 }
2029 }
2030 }
2031 }
2032
2033 if (!cycled_focus)
2034 gtk_cell_area_set_focus_cell (area, NULL);
2035
2036 return cycled_focus;
2037}
2038
2039
2040/*************************************************************
2041 * GtkCellLayoutIface *
2042 *************************************************************/
2043static void
2044gtk_cell_area_box_cell_layout_init (GtkCellLayoutIface *iface)
2045{
2046 iface->pack_start = gtk_cell_area_box_layout_pack_start;
2047 iface->pack_end = gtk_cell_area_box_layout_pack_end;
2048 iface->reorder = gtk_cell_area_box_layout_reorder;
2049}
2050
2051static void
2052gtk_cell_area_box_layout_pack_start (GtkCellLayout *cell_layout,
2053 GtkCellRenderer *renderer,
2054 gboolean expand)
2055{
2056 gtk_cell_area_box_pack_start (GTK_CELL_AREA_BOX (cell_layout), renderer, expand, FALSE, TRUE);
2057}
2058
2059static void
2060gtk_cell_area_box_layout_pack_end (GtkCellLayout *cell_layout,
2061 GtkCellRenderer *renderer,
2062 gboolean expand)
2063{
2064 gtk_cell_area_box_pack_end (GTK_CELL_AREA_BOX (cell_layout), renderer, expand, FALSE, TRUE);
2065}
2066
2067static void
2068gtk_cell_area_box_layout_reorder (GtkCellLayout *cell_layout,
2069 GtkCellRenderer *renderer,
2070 int position)
2071{
2072 GtkCellAreaBox *box = GTK_CELL_AREA_BOX (cell_layout);
2073 GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (self: box);
2074 GList *node;
2075 CellInfo *info;
2076
2077 node = g_list_find_custom (list: priv->cells, data: renderer,
2078 func: (GCompareFunc)cell_info_find);
2079
2080 if (node)
2081 {
2082 info = node->data;
2083
2084 priv->cells = g_list_delete_link (list: priv->cells, link_: node);
2085 priv->cells = g_list_insert (list: priv->cells, data: info, position);
2086
2087 cell_groups_rebuild (box);
2088 }
2089}
2090
2091/*************************************************************
2092 * Private interaction with GtkCellAreaBoxContext *
2093 *************************************************************/
2094gboolean
2095_gtk_cell_area_box_group_visible (GtkCellAreaBox *box,
2096 int group_idx)
2097{
2098 GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (self: box);
2099 CellGroup *group;
2100
2101 g_assert (group_idx >= 0 && group_idx < priv->groups->len);
2102
2103 group = &g_array_index (priv->groups, CellGroup, group_idx);
2104
2105 return group->visible;
2106}
2107
2108
2109/*************************************************************
2110 * API *
2111 *************************************************************/
2112/**
2113 * gtk_cell_area_box_new:
2114 *
2115 * Creates a new `GtkCellAreaBox`.
2116 *
2117 * Returns: a newly created `GtkCellAreaBox`
2118 */
2119GtkCellArea *
2120gtk_cell_area_box_new (void)
2121{
2122 return (GtkCellArea *)g_object_new (GTK_TYPE_CELL_AREA_BOX, NULL);
2123}
2124
2125/**
2126 * gtk_cell_area_box_pack_start:
2127 * @box: a `GtkCellAreaBox`
2128 * @renderer: the `GtkCellRenderer` to add
2129 * @expand: whether @renderer should receive extra space when the area receives
2130 * more than its natural size
2131 * @align: whether @renderer should be aligned in adjacent rows
2132 * @fixed: whether @renderer should have the same size in all rows
2133 *
2134 * Adds @renderer to @box, packed with reference to the start of @box.
2135 *
2136 * The @renderer is packed after any other `GtkCellRenderer` packed
2137 * with reference to the start of @box.
2138 */
2139void
2140gtk_cell_area_box_pack_start (GtkCellAreaBox *box,
2141 GtkCellRenderer *renderer,
2142 gboolean expand,
2143 gboolean align,
2144 gboolean fixed)
2145{
2146 GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (self: box);
2147 CellInfo *info;
2148
2149 g_return_if_fail (GTK_IS_CELL_AREA_BOX (box));
2150 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
2151
2152 if (g_list_find_custom (list: priv->cells, data: renderer,
2153 func: (GCompareFunc)cell_info_find))
2154 {
2155 g_warning ("Refusing to add the same cell renderer to a GtkCellAreaBox twice");
2156 return;
2157 }
2158
2159 info = cell_info_new (renderer, pack: GTK_PACK_START, expand, align, fixed);
2160
2161 priv->cells = g_list_append (list: priv->cells, data: info);
2162
2163 cell_groups_rebuild (box);
2164}
2165
2166/**
2167 * gtk_cell_area_box_pack_end:
2168 * @box: a `GtkCellAreaBox`
2169 * @renderer: the `GtkCellRenderer` to add
2170 * @expand: whether @renderer should receive extra space when the area receives
2171 * more than its natural size
2172 * @align: whether @renderer should be aligned in adjacent rows
2173 * @fixed: whether @renderer should have the same size in all rows
2174 *
2175 * Adds @renderer to @box, packed with reference to the end of @box.
2176 *
2177 * The @renderer is packed after (away from end of) any other
2178 * `GtkCellRenderer` packed with reference to the end of @box.
2179 */
2180void
2181gtk_cell_area_box_pack_end (GtkCellAreaBox *box,
2182 GtkCellRenderer *renderer,
2183 gboolean expand,
2184 gboolean align,
2185 gboolean fixed)
2186{
2187 GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (self: box);
2188 CellInfo *info;
2189
2190 g_return_if_fail (GTK_IS_CELL_AREA_BOX (box));
2191 g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
2192
2193 if (g_list_find_custom (list: priv->cells, data: renderer,
2194 func: (GCompareFunc)cell_info_find))
2195 {
2196 g_warning ("Refusing to add the same cell renderer to a GtkCellArea twice");
2197 return;
2198 }
2199
2200 info = cell_info_new (renderer, pack: GTK_PACK_END, expand, align, fixed);
2201
2202 priv->cells = g_list_append (list: priv->cells, data: info);
2203
2204 cell_groups_rebuild (box);
2205}
2206
2207/**
2208 * gtk_cell_area_box_get_spacing:
2209 * @box: a `GtkCellAreaBox`
2210 *
2211 * Gets the spacing added between cell renderers.
2212 *
2213 * Returns: the space added between cell renderers in @box.
2214 */
2215int
2216gtk_cell_area_box_get_spacing (GtkCellAreaBox *box)
2217{
2218 GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (self: box);
2219
2220 g_return_val_if_fail (GTK_IS_CELL_AREA_BOX (box), 0);
2221
2222 return priv->spacing;
2223}
2224
2225/**
2226 * gtk_cell_area_box_set_spacing:
2227 * @box: a `GtkCellAreaBox`
2228 * @spacing: the space to add between `GtkCellRenderer`s
2229 *
2230 * Sets the spacing to add between cell renderers in @box.
2231 */
2232void
2233gtk_cell_area_box_set_spacing (GtkCellAreaBox *box,
2234 int spacing)
2235{
2236 GtkCellAreaBoxPrivate *priv = gtk_cell_area_box_get_instance_private (self: box);
2237
2238 g_return_if_fail (GTK_IS_CELL_AREA_BOX (box));
2239
2240 if (priv->spacing != spacing)
2241 {
2242 priv->spacing = spacing;
2243
2244 g_object_notify (G_OBJECT (box), property_name: "spacing");
2245
2246 /* Notify that size needs to be requested again */
2247 reset_contexts (box);
2248 }
2249}
2250

source code of gtk/gtk/gtkcellareabox.c