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