1 /* gtkcellareaboxcontext.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 #include "config.h"
23 #include "gtkintl.h"
24 #include "gtkcellareabox.h"
25 #include "gtkcellareaboxcontextprivate.h"
26 #include "gtkorientable.h"
27
28 /* GObjectClass */
29 static void _gtk_cell_area_box_context_finalize (GObject *object);
30
31 /* GtkCellAreaContextClass */
32 static void _gtk_cell_area_box_context_reset (GtkCellAreaContext *context);
33 static void _gtk_cell_area_box_context_get_preferred_height_for_width (GtkCellAreaContext *context,
34 gint width,
35 gint *minimum_height,
36 gint *natural_height);
37 static void _gtk_cell_area_box_context_get_preferred_width_for_height (GtkCellAreaContext *context,
38 gint height,
39 gint *minimum_width,
40 gint *natural_width);
41
42
43
44 /* Internal functions */
45 static void _gtk_cell_area_box_context_sum (GtkCellAreaBoxContext *context,
46 GtkOrientation orientation,
47 gint for_size,
48 gint *minimum_size,
49 gint *natural_size);
50 static void free_cache_array (GArray *array);
51 static GArray *group_array_new (GtkCellAreaBoxContext *context);
52 static GArray *get_array (GtkCellAreaBoxContext *context,
53 GtkOrientation orientation,
54 gint for_size);
55 static gboolean group_expands (GtkCellAreaBoxContext *context,
56 gint group_idx);
57 static gint count_expand_groups (GtkCellAreaBoxContext *context);
58
59
60 /* CachedSize management */
61 typedef struct {
62 gint min_size;
63 gint nat_size;
64 } CachedSize;
65
66 struct _GtkCellAreaBoxContextPrivate
67 {
68 /* Table of per renderer CachedSizes */
69 GArray *base_widths;
70 GArray *base_heights;
71
72 /* Table of per height/width hash tables of per renderer CachedSizes */
73 GHashTable *widths;
74 GHashTable *heights;
75
76 /* Whether each group expands */
77 gboolean *expand;
78
79 /* Whether each group is aligned */
80 gboolean *align;
81 };
82
G_DEFINE_TYPE_WITH_PRIVATE(GtkCellAreaBoxContext,_gtk_cell_area_box_context,GTK_TYPE_CELL_AREA_CONTEXT)83 G_DEFINE_TYPE_WITH_PRIVATE (GtkCellAreaBoxContext, _gtk_cell_area_box_context, GTK_TYPE_CELL_AREA_CONTEXT)
84
85 static void
86 free_cache_array (GArray *array)
87 {
88 g_array_free (array, TRUE);
89 }
90
91 static GArray *
group_array_new(GtkCellAreaBoxContext * context)92 group_array_new (GtkCellAreaBoxContext *context)
93 {
94 GtkCellAreaBoxContextPrivate *priv = context->priv;
95 GArray *group_array;
96
97 group_array = g_array_new (FALSE, TRUE, sizeof (CachedSize));
98 g_array_set_size (group_array, priv->base_widths->len);
99
100 return group_array;
101 }
102
103 static GArray *
get_array(GtkCellAreaBoxContext * context,GtkOrientation orientation,gint for_size)104 get_array (GtkCellAreaBoxContext *context,
105 GtkOrientation orientation,
106 gint for_size)
107 {
108 GtkCellAreaBoxContextPrivate *priv = context->priv;
109 GArray *array;
110
111 if (for_size < 0)
112 {
113 if (orientation == GTK_ORIENTATION_HORIZONTAL)
114 array = priv->base_widths;
115 else
116 array = priv->base_heights;
117 }
118 else
119 {
120 if (orientation == GTK_ORIENTATION_HORIZONTAL)
121 {
122 array = g_hash_table_lookup (priv->widths, GINT_TO_POINTER (for_size));
123
124 if (!array)
125 array = priv->base_widths;
126 }
127 else
128 {
129 array = g_hash_table_lookup (priv->heights, GINT_TO_POINTER (for_size));
130
131 if (!array)
132 array = priv->base_heights;
133 }
134 }
135
136 return array;
137 }
138
139 static gboolean
group_expands(GtkCellAreaBoxContext * context,gint group_idx)140 group_expands (GtkCellAreaBoxContext *context,
141 gint group_idx)
142 {
143 GtkCellAreaBoxContextPrivate *priv = context->priv;
144
145 g_assert (group_idx >= 0 && group_idx < priv->base_widths->len);
146
147 return priv->expand[group_idx];
148 }
149
150 static gint
count_expand_groups(GtkCellAreaBoxContext * context)151 count_expand_groups (GtkCellAreaBoxContext *context)
152 {
153 GtkCellAreaBoxContextPrivate *priv = context->priv;
154 gint i, expand = 0;
155
156 for (i = 0; i < priv->base_widths->len; i++)
157 {
158 if (priv->expand[i])
159 expand++;
160 }
161
162 return expand;
163 }
164
165 static void
_gtk_cell_area_box_context_init(GtkCellAreaBoxContext * box_context)166 _gtk_cell_area_box_context_init (GtkCellAreaBoxContext *box_context)
167 {
168 GtkCellAreaBoxContextPrivate *priv;
169
170 box_context->priv = _gtk_cell_area_box_context_get_instance_private (box_context);
171 priv = box_context->priv;
172
173 priv->base_widths = g_array_new (FALSE, TRUE, sizeof (CachedSize));
174 priv->base_heights = g_array_new (FALSE, TRUE, sizeof (CachedSize));
175
176 priv->widths = g_hash_table_new_full (g_direct_hash, g_direct_equal,
177 NULL, (GDestroyNotify)free_cache_array);
178 priv->heights = g_hash_table_new_full (g_direct_hash, g_direct_equal,
179 NULL, (GDestroyNotify)free_cache_array);
180 }
181
182 static void
_gtk_cell_area_box_context_class_init(GtkCellAreaBoxContextClass * class)183 _gtk_cell_area_box_context_class_init (GtkCellAreaBoxContextClass *class)
184 {
185 GObjectClass *object_class = G_OBJECT_CLASS (class);
186 GtkCellAreaContextClass *context_class = GTK_CELL_AREA_CONTEXT_CLASS (class);
187
188 /* GObjectClass */
189 object_class->finalize = _gtk_cell_area_box_context_finalize;
190
191 context_class->reset = _gtk_cell_area_box_context_reset;
192 context_class->get_preferred_height_for_width = _gtk_cell_area_box_context_get_preferred_height_for_width;
193 context_class->get_preferred_width_for_height = _gtk_cell_area_box_context_get_preferred_width_for_height;
194 }
195
196 /*************************************************************
197 * GObjectClass *
198 *************************************************************/
199 static void
_gtk_cell_area_box_context_finalize(GObject * object)200 _gtk_cell_area_box_context_finalize (GObject *object)
201 {
202 GtkCellAreaBoxContext *box_context = GTK_CELL_AREA_BOX_CONTEXT (object);
203 GtkCellAreaBoxContextPrivate *priv = box_context->priv;
204
205 g_array_free (priv->base_widths, TRUE);
206 g_array_free (priv->base_heights, TRUE);
207 g_hash_table_destroy (priv->widths);
208 g_hash_table_destroy (priv->heights);
209
210 g_free (priv->expand);
211 g_free (priv->align);
212
213 G_OBJECT_CLASS (_gtk_cell_area_box_context_parent_class)->finalize (object);
214 }
215
216 /*************************************************************
217 * GtkCellAreaContextClass *
218 *************************************************************/
219 static void
_gtk_cell_area_box_context_reset(GtkCellAreaContext * context)220 _gtk_cell_area_box_context_reset (GtkCellAreaContext *context)
221 {
222 GtkCellAreaBoxContext *box_context = GTK_CELL_AREA_BOX_CONTEXT (context);
223 GtkCellAreaBoxContextPrivate *priv = box_context->priv;
224 CachedSize *size;
225 gint i;
226
227 for (i = 0; i < priv->base_widths->len; i++)
228 {
229 size = &g_array_index (priv->base_widths, CachedSize, i);
230
231 size->min_size = 0;
232 size->nat_size = 0;
233
234 size = &g_array_index (priv->base_heights, CachedSize, i);
235
236 size->min_size = 0;
237 size->nat_size = 0;
238 }
239
240 /* Reset context sizes as well */
241 g_hash_table_remove_all (priv->widths);
242 g_hash_table_remove_all (priv->heights);
243
244 GTK_CELL_AREA_CONTEXT_CLASS
245 (_gtk_cell_area_box_context_parent_class)->reset (context);
246 }
247
248 static void
_gtk_cell_area_box_context_sum(GtkCellAreaBoxContext * context,GtkOrientation orientation,gint for_size,gint * minimum_size,gint * natural_size)249 _gtk_cell_area_box_context_sum (GtkCellAreaBoxContext *context,
250 GtkOrientation orientation,
251 gint for_size,
252 gint *minimum_size,
253 gint *natural_size)
254 {
255 GtkCellAreaBoxContextPrivate *priv = context->priv;
256 GtkCellAreaBox *area;
257 GtkOrientation box_orientation;
258 GArray *array;
259 gint spacing, i, last_aligned_group_idx;
260 gint min_size = 0, nat_size = 0;
261
262 area = (GtkCellAreaBox *)gtk_cell_area_context_get_area (GTK_CELL_AREA_CONTEXT (context));
263 spacing = gtk_cell_area_box_get_spacing (area);
264 box_orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (area));
265 array = get_array (context, orientation, for_size);
266
267 /* Get the last visible aligned group
268 * (we need to get space at least up till this group) */
269 for (i = array->len - 1; i >= 0; i--)
270 {
271 if (priv->align[i] &&
272 _gtk_cell_area_box_group_visible (area, i))
273 break;
274 }
275 last_aligned_group_idx = i >= 0 ? i : 0;
276
277 for (i = 0; i < array->len; i++)
278 {
279 CachedSize *size = &g_array_index (array, CachedSize, i);
280
281 if (box_orientation == orientation)
282 {
283 if (i > last_aligned_group_idx &&
284 !_gtk_cell_area_box_group_visible (area, i))
285 continue;
286
287 /* Dont add spacing for 0 size groups, they can be 0 size because
288 * they contain only invisible cells for this round of requests
289 */
290 if (min_size > 0 && size->nat_size > 0)
291 {
292 min_size += spacing;
293 nat_size += spacing;
294 }
295
296 min_size += size->min_size;
297 nat_size += size->nat_size;
298 }
299 else
300 {
301 min_size = MAX (min_size, size->min_size);
302 nat_size = MAX (nat_size, size->nat_size);
303 }
304 }
305
306 if (for_size < 0)
307 {
308 if (orientation == GTK_ORIENTATION_HORIZONTAL)
309 gtk_cell_area_context_push_preferred_width (GTK_CELL_AREA_CONTEXT (context), min_size, nat_size);
310 else
311 gtk_cell_area_context_push_preferred_height (GTK_CELL_AREA_CONTEXT (context), min_size, nat_size);
312 }
313
314 if (minimum_size)
315 *minimum_size = min_size;
316 if (natural_size)
317 *natural_size = nat_size;
318 }
319
320 static void
_gtk_cell_area_box_context_get_preferred_height_for_width(GtkCellAreaContext * context,gint width,gint * minimum_height,gint * natural_height)321 _gtk_cell_area_box_context_get_preferred_height_for_width (GtkCellAreaContext *context,
322 gint width,
323 gint *minimum_height,
324 gint *natural_height)
325 {
326 _gtk_cell_area_box_context_sum (GTK_CELL_AREA_BOX_CONTEXT (context), GTK_ORIENTATION_VERTICAL,
327 width, minimum_height, natural_height);
328 }
329
330 static void
_gtk_cell_area_box_context_get_preferred_width_for_height(GtkCellAreaContext * context,gint height,gint * minimum_width,gint * natural_width)331 _gtk_cell_area_box_context_get_preferred_width_for_height (GtkCellAreaContext *context,
332 gint height,
333 gint *minimum_width,
334 gint *natural_width)
335 {
336 _gtk_cell_area_box_context_sum (GTK_CELL_AREA_BOX_CONTEXT (context), GTK_ORIENTATION_HORIZONTAL,
337 height, minimum_width, natural_width);
338 }
339
340 /*************************************************************
341 * API *
342 *************************************************************/
343 static void
copy_size_array(GArray * src_array,GArray * dest_array)344 copy_size_array (GArray *src_array,
345 GArray *dest_array)
346 {
347 gint i;
348
349 for (i = 0; i < src_array->len; i++)
350 {
351 CachedSize *src = &g_array_index (src_array, CachedSize, i);
352 CachedSize *dest = &g_array_index (dest_array, CachedSize, i);
353
354 memcpy (dest, src, sizeof (CachedSize));
355 }
356 }
357
358 static void
for_size_copy(gpointer key,GArray * size_array,GHashTable * dest_hash)359 for_size_copy (gpointer key,
360 GArray *size_array,
361 GHashTable *dest_hash)
362 {
363 GArray *new_array;
364
365 new_array = g_array_new (FALSE, TRUE, sizeof (CachedSize));
366 g_array_set_size (new_array, size_array->len);
367
368 copy_size_array (size_array, new_array);
369
370 g_hash_table_insert (dest_hash, key, new_array);
371 }
372
373 GtkCellAreaBoxContext *
_gtk_cell_area_box_context_copy(GtkCellAreaBox * box,GtkCellAreaBoxContext * context)374 _gtk_cell_area_box_context_copy (GtkCellAreaBox *box,
375 GtkCellAreaBoxContext *context)
376 {
377 GtkCellAreaBoxContext *copy;
378
379 copy = g_object_new (GTK_TYPE_CELL_AREA_BOX_CONTEXT,
380 "area", box, NULL);
381
382 _gtk_cell_area_box_init_groups (copy,
383 context->priv->base_widths->len,
384 context->priv->expand,
385 context->priv->align);
386
387 /* Copy the base arrays */
388 copy_size_array (context->priv->base_widths,
389 copy->priv->base_widths);
390 copy_size_array (context->priv->base_heights,
391 copy->priv->base_heights);
392
393 /* Copy each for size */
394 g_hash_table_foreach (context->priv->heights,
395 (GHFunc)for_size_copy, copy->priv->heights);
396 g_hash_table_foreach (context->priv->widths,
397 (GHFunc)for_size_copy, copy->priv->widths);
398
399
400 return copy;
401 }
402
403 void
_gtk_cell_area_box_init_groups(GtkCellAreaBoxContext * box_context,guint n_groups,gboolean * expand_groups,gboolean * align_groups)404 _gtk_cell_area_box_init_groups (GtkCellAreaBoxContext *box_context,
405 guint n_groups,
406 gboolean *expand_groups,
407 gboolean *align_groups)
408 {
409 GtkCellAreaBoxContextPrivate *priv;
410
411 g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (box_context));
412 g_return_if_fail (n_groups == 0 || expand_groups != NULL);
413
414 /* When the group dimensions change, all info must be reset
415 * Note this already clears the min/nat values on the CachedSizes
416 */
417 gtk_cell_area_context_reset (GTK_CELL_AREA_CONTEXT (box_context));
418
419 priv = box_context->priv;
420 g_array_set_size (priv->base_widths, n_groups);
421 g_array_set_size (priv->base_heights, n_groups);
422
423 g_free (priv->expand);
424 priv->expand = g_memdup (expand_groups, n_groups * sizeof (gboolean));
425
426 g_free (priv->align);
427 priv->align = g_memdup (align_groups, n_groups * sizeof (gboolean));
428 }
429
430 void
_gtk_cell_area_box_context_push_group_width(GtkCellAreaBoxContext * box_context,gint group_idx,gint minimum_width,gint natural_width)431 _gtk_cell_area_box_context_push_group_width (GtkCellAreaBoxContext *box_context,
432 gint group_idx,
433 gint minimum_width,
434 gint natural_width)
435 {
436 GtkCellAreaBoxContextPrivate *priv;
437 CachedSize *size;
438 gboolean grew = FALSE;
439
440 g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (box_context));
441
442 priv = box_context->priv;
443 g_return_if_fail (group_idx < priv->base_widths->len);
444
445 size = &g_array_index (priv->base_widths, CachedSize, group_idx);
446 if (minimum_width > size->min_size)
447 {
448 size->min_size = minimum_width;
449 grew = TRUE;
450 }
451 if (natural_width > size->nat_size)
452 {
453 size->nat_size = natural_width;
454 grew = TRUE;
455 }
456
457 if (grew)
458 _gtk_cell_area_box_context_sum (box_context, GTK_ORIENTATION_HORIZONTAL, -1, NULL, NULL);
459 }
460
461 void
_gtk_cell_area_box_context_push_group_height_for_width(GtkCellAreaBoxContext * box_context,gint group_idx,gint for_width,gint minimum_height,gint natural_height)462 _gtk_cell_area_box_context_push_group_height_for_width (GtkCellAreaBoxContext *box_context,
463 gint group_idx,
464 gint for_width,
465 gint minimum_height,
466 gint natural_height)
467 {
468 GtkCellAreaBoxContextPrivate *priv;
469 GArray *group_array;
470 CachedSize *size;
471
472 g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (box_context));
473
474 priv = box_context->priv;
475 g_return_if_fail (group_idx < priv->base_widths->len);
476
477 group_array = g_hash_table_lookup (priv->heights, GINT_TO_POINTER (for_width));
478 if (!group_array)
479 {
480 group_array = group_array_new (box_context);
481 g_hash_table_insert (priv->heights, GINT_TO_POINTER (for_width), group_array);
482 }
483
484 size = &g_array_index (group_array, CachedSize, group_idx);
485 size->min_size = MAX (size->min_size, minimum_height);
486 size->nat_size = MAX (size->nat_size, natural_height);
487 }
488
489 void
_gtk_cell_area_box_context_push_group_height(GtkCellAreaBoxContext * box_context,gint group_idx,gint minimum_height,gint natural_height)490 _gtk_cell_area_box_context_push_group_height (GtkCellAreaBoxContext *box_context,
491 gint group_idx,
492 gint minimum_height,
493 gint natural_height)
494 {
495 GtkCellAreaBoxContextPrivate *priv;
496 CachedSize *size;
497 gboolean grew = FALSE;
498
499 g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (box_context));
500
501 priv = box_context->priv;
502 g_return_if_fail (group_idx < priv->base_heights->len);
503
504 size = &g_array_index (priv->base_heights, CachedSize, group_idx);
505 if (minimum_height > size->min_size)
506 {
507 size->min_size = minimum_height;
508 grew = TRUE;
509 }
510 if (natural_height > size->nat_size)
511 {
512 size->nat_size = natural_height;
513 grew = TRUE;
514 }
515
516 if (grew)
517 _gtk_cell_area_box_context_sum (box_context, GTK_ORIENTATION_VERTICAL, -1, NULL, NULL);
518 }
519
520 void
_gtk_cell_area_box_context_push_group_width_for_height(GtkCellAreaBoxContext * box_context,gint group_idx,gint for_height,gint minimum_width,gint natural_width)521 _gtk_cell_area_box_context_push_group_width_for_height (GtkCellAreaBoxContext *box_context,
522 gint group_idx,
523 gint for_height,
524 gint minimum_width,
525 gint natural_width)
526 {
527 GtkCellAreaBoxContextPrivate *priv;
528 GArray *group_array;
529 CachedSize *size;
530
531 g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (box_context));
532
533 priv = box_context->priv;
534 g_return_if_fail (group_idx < priv->base_widths->len);
535
536 group_array = g_hash_table_lookup (priv->widths, GINT_TO_POINTER (for_height));
537 if (!group_array)
538 {
539 group_array = group_array_new (box_context);
540 g_hash_table_insert (priv->widths, GINT_TO_POINTER (for_height), group_array);
541 }
542
543 size = &g_array_index (group_array, CachedSize, group_idx);
544 size->min_size = MAX (size->min_size, minimum_width);
545 size->nat_size = MAX (size->nat_size, natural_width);
546 }
547
548 void
_gtk_cell_area_box_context_get_group_width(GtkCellAreaBoxContext * box_context,gint group_idx,gint * minimum_width,gint * natural_width)549 _gtk_cell_area_box_context_get_group_width (GtkCellAreaBoxContext *box_context,
550 gint group_idx,
551 gint *minimum_width,
552 gint *natural_width)
553 {
554 GtkCellAreaBoxContextPrivate *priv;
555 CachedSize *size;
556
557 g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (box_context));
558
559 priv = box_context->priv;
560 g_return_if_fail (group_idx < priv->base_widths->len);
561
562 size = &g_array_index (priv->base_widths, CachedSize, group_idx);
563
564 if (minimum_width)
565 *minimum_width = size->min_size;
566
567 if (natural_width)
568 *natural_width = size->nat_size;
569 }
570
571 void
_gtk_cell_area_box_context_get_group_height_for_width(GtkCellAreaBoxContext * box_context,gint group_idx,gint for_width,gint * minimum_height,gint * natural_height)572 _gtk_cell_area_box_context_get_group_height_for_width (GtkCellAreaBoxContext *box_context,
573 gint group_idx,
574 gint for_width,
575 gint *minimum_height,
576 gint *natural_height)
577 {
578 GtkCellAreaBoxContextPrivate *priv;
579 GArray *group_array;
580
581 g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (box_context));
582
583 priv = box_context->priv;
584 g_return_if_fail (group_idx < priv->base_widths->len);
585
586 group_array = g_hash_table_lookup (priv->heights, GINT_TO_POINTER (for_width));
587
588 if (group_array)
589 {
590 CachedSize *size = &g_array_index (group_array, CachedSize, group_idx);
591
592 if (minimum_height)
593 *minimum_height = size->min_size;
594
595 if (natural_height)
596 *natural_height = size->nat_size;
597 }
598 else
599 {
600 if (minimum_height)
601 *minimum_height = -1;
602
603 if (natural_height)
604 *natural_height = -1;
605 }
606 }
607
608 void
_gtk_cell_area_box_context_get_group_height(GtkCellAreaBoxContext * box_context,gint group_idx,gint * minimum_height,gint * natural_height)609 _gtk_cell_area_box_context_get_group_height (GtkCellAreaBoxContext *box_context,
610 gint group_idx,
611 gint *minimum_height,
612 gint *natural_height)
613 {
614 GtkCellAreaBoxContextPrivate *priv;
615 CachedSize *size;
616
617 g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (box_context));
618
619 priv = box_context->priv;
620 g_return_if_fail (group_idx < priv->base_heights->len);
621
622 size = &g_array_index (priv->base_heights, CachedSize, group_idx);
623
624 if (minimum_height)
625 *minimum_height = size->min_size;
626
627 if (natural_height)
628 *natural_height = size->nat_size;
629 }
630
631 void
_gtk_cell_area_box_context_get_group_width_for_height(GtkCellAreaBoxContext * box_context,gint group_idx,gint for_height,gint * minimum_width,gint * natural_width)632 _gtk_cell_area_box_context_get_group_width_for_height (GtkCellAreaBoxContext *box_context,
633 gint group_idx,
634 gint for_height,
635 gint *minimum_width,
636 gint *natural_width)
637 {
638 GtkCellAreaBoxContextPrivate *priv;
639 GArray *group_array;
640
641 g_return_if_fail (GTK_IS_CELL_AREA_BOX_CONTEXT (box_context));
642
643 priv = box_context->priv;
644 g_return_if_fail (group_idx < priv->base_widths->len);
645
646 group_array = g_hash_table_lookup (priv->widths, GINT_TO_POINTER (for_height));
647
648 if (group_array)
649 {
650 CachedSize *size = &g_array_index (group_array, CachedSize, group_idx);
651
652 if (minimum_width)
653 *minimum_width = size->min_size;
654
655 if (natural_width)
656 *natural_width = size->nat_size;
657 }
658 else
659 {
660 if (minimum_width)
661 *minimum_width = -1;
662
663 if (natural_width)
664 *natural_width = -1;
665 }
666 }
667
668 static GtkRequestedSize *
_gtk_cell_area_box_context_get_requests(GtkCellAreaBoxContext * box_context,GtkCellAreaBox * area,GtkOrientation orientation,gint for_size,gint * n_requests)669 _gtk_cell_area_box_context_get_requests (GtkCellAreaBoxContext *box_context,
670 GtkCellAreaBox *area,
671 GtkOrientation orientation,
672 gint for_size,
673 gint *n_requests)
674 {
675 GtkCellAreaBoxContextPrivate *priv = box_context->priv;
676 GtkRequestedSize *requests;
677 GArray *array;
678 CachedSize *size;
679 gint visible_groups = 0;
680 gint last_aligned_group_idx = 0;
681 gint i, j;
682
683 /* Get the last visible aligned group
684 * (we need to get space at least up till this group) */
685 for (i = priv->base_widths->len - 1; i >= 0; i--)
686 {
687 if (priv->align[i] &&
688 _gtk_cell_area_box_group_visible (area, i))
689 break;
690 }
691 last_aligned_group_idx = i >= 0 ? i : 0;
692
693 priv = box_context->priv;
694 array = get_array (box_context, orientation, for_size);
695
696 for (i = 0; i < array->len; i++)
697 {
698 size = &g_array_index (array, CachedSize, i);
699
700 if (size->nat_size > 0 &&
701 (i <= last_aligned_group_idx ||
702 _gtk_cell_area_box_group_visible (area, i)))
703 visible_groups++;
704 }
705
706 requests = g_new (GtkRequestedSize, visible_groups);
707
708 for (j = 0, i = 0; i < array->len; i++)
709 {
710 size = &g_array_index (array, CachedSize, i);
711
712 if (size->nat_size > 0 &&
713 (i <= last_aligned_group_idx ||
714 _gtk_cell_area_box_group_visible (area, i)))
715 {
716 requests[j].data = GINT_TO_POINTER (i);
717 requests[j].minimum_size = size->min_size;
718 requests[j].natural_size = size->nat_size;
719 j++;
720 }
721 }
722
723 if (n_requests)
724 *n_requests = visible_groups;
725
726 return requests;
727 }
728
729 static GtkCellAreaBoxAllocation *
allocate_for_orientation(GtkCellAreaBoxContext * context,GtkCellAreaBox * area,GtkOrientation orientation,gint spacing,gint size,gint for_size,gint * n_allocs)730 allocate_for_orientation (GtkCellAreaBoxContext *context,
731 GtkCellAreaBox *area,
732 GtkOrientation orientation,
733 gint spacing,
734 gint size,
735 gint for_size,
736 gint *n_allocs)
737 {
738 GtkCellAreaBoxContextPrivate *priv = context->priv;
739 GtkCellAreaBoxAllocation *allocs;
740 GtkRequestedSize *sizes;
741 gint n_expand_groups = 0;
742 gint i, n_groups, position, vis_position;
743 gint extra_size, extra_extra;
744 gint avail_size = size;
745
746 sizes = _gtk_cell_area_box_context_get_requests (context, area, orientation, for_size, &n_groups);
747 n_expand_groups = count_expand_groups (context);
748
749 /* First start by naturally allocating space among groups */
750 avail_size -= (n_groups - 1) * spacing;
751 for (i = 0; i < n_groups; i++)
752 avail_size -= sizes[i].minimum_size;
753
754 if (avail_size > 0)
755 avail_size = gtk_distribute_natural_allocation (avail_size, n_groups, sizes);
756 else
757 avail_size = 0;
758
759 /* Calculate/distribute expand for groups */
760 if (n_expand_groups > 0)
761 {
762 extra_size = avail_size / n_expand_groups;
763 extra_extra = avail_size % n_expand_groups;
764 }
765 else
766 extra_size = extra_extra = 0;
767
768 allocs = g_new (GtkCellAreaBoxAllocation, n_groups);
769
770 for (vis_position = 0, position = 0, i = 0; i < n_groups; i++)
771 {
772 allocs[i].group_idx = GPOINTER_TO_INT (sizes[i].data);
773
774 if (priv->align[allocs[i].group_idx])
775 vis_position = position;
776
777 allocs[i].position = vis_position;
778 allocs[i].size = sizes[i].minimum_size;
779
780 if (group_expands (context, allocs[i].group_idx))
781 {
782 allocs[i].size += extra_size;
783 if (extra_extra)
784 {
785 allocs[i].size++;
786 extra_extra--;
787 }
788 }
789
790 position += allocs[i].size;
791 position += spacing;
792
793 if (_gtk_cell_area_box_group_visible (area, allocs[i].group_idx))
794 {
795 vis_position += allocs[i].size;
796 vis_position += spacing;
797 }
798 }
799
800 if (n_allocs)
801 *n_allocs = n_groups;
802
803 g_free (sizes);
804
805 return allocs;
806 }
807
808 GtkRequestedSize *
_gtk_cell_area_box_context_get_widths(GtkCellAreaBoxContext * box_context,gint * n_widths)809 _gtk_cell_area_box_context_get_widths (GtkCellAreaBoxContext *box_context,
810 gint *n_widths)
811 {
812 GtkCellAreaBox *area = (GtkCellAreaBox *)gtk_cell_area_context_get_area (GTK_CELL_AREA_CONTEXT (box_context));
813
814 return _gtk_cell_area_box_context_get_requests (box_context, area, GTK_ORIENTATION_HORIZONTAL, -1, n_widths);
815 }
816
817 GtkRequestedSize *
_gtk_cell_area_box_context_get_heights(GtkCellAreaBoxContext * box_context,gint * n_heights)818 _gtk_cell_area_box_context_get_heights (GtkCellAreaBoxContext *box_context,
819 gint *n_heights)
820 {
821 GtkCellAreaBox *area = (GtkCellAreaBox *)gtk_cell_area_context_get_area (GTK_CELL_AREA_CONTEXT (box_context));
822
823 return _gtk_cell_area_box_context_get_requests (box_context, area, GTK_ORIENTATION_VERTICAL, -1, n_heights);
824 }
825
826 GtkCellAreaBoxAllocation *
_gtk_cell_area_box_context_get_orientation_allocs(GtkCellAreaBoxContext * context,gint * n_allocs)827 _gtk_cell_area_box_context_get_orientation_allocs (GtkCellAreaBoxContext *context,
828 gint *n_allocs)
829 {
830 GtkCellAreaContext *ctx = GTK_CELL_AREA_CONTEXT (context);
831 GtkCellAreaBox *area;
832 GtkOrientation orientation;
833 gint spacing, width, height, alloc_count = 0;
834 GtkCellAreaBoxAllocation *allocs = NULL;
835
836 area = (GtkCellAreaBox *)gtk_cell_area_context_get_area (ctx);
837 orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (area));
838 spacing = gtk_cell_area_box_get_spacing (area);
839
840 gtk_cell_area_context_get_allocation (ctx, &width, &height);
841
842 if (orientation == GTK_ORIENTATION_HORIZONTAL && width > 0)
843 allocs = allocate_for_orientation (context, area, orientation,
844 spacing, width, height,
845 &alloc_count);
846 else if (orientation == GTK_ORIENTATION_VERTICAL && height > 0)
847 allocs = allocate_for_orientation (context, area, orientation,
848 spacing, height, width,
849 &alloc_count);
850
851 *n_allocs = alloc_count;
852
853 return allocs;
854 }
855