more leaks plugged and more *_OPTIONAL
[dia.git] / app / gtkvwrapbox.c
blobbf22d807bd2182733785ab6ce333cdf8dc2a44cd
1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
4 * GtkVWrapBox: Vertical wrapping box widget
5 * Copyright (C) 1999 Tim Janik
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
23 #include "gtkvwrapbox.h"
24 #include <math.h>
26 /* --- prototypes --- */
27 static void gtk_vwrap_box_class_init (GtkVWrapBoxClass *klass);
28 static void gtk_vwrap_box_init (GtkVWrapBox *vwbox);
29 static void gtk_vwrap_box_size_request (GtkWidget *widget,
30 GtkRequisition *requisition);
31 static void gtk_vwrap_box_size_allocate (GtkWidget *widget,
32 GtkAllocation *allocation);
33 static GSList* reverse_list_col_children (GtkWrapBox *wbox,
34 GtkWrapBoxChild **child_p,
35 GtkAllocation *area,
36 guint *max_width,
37 gboolean *can_hexpand);
40 /* --- variables --- */
41 static gpointer parent_class = NULL;
44 /* --- functions --- */
45 GtkType
46 gtk_vwrap_box_get_type (void)
48 static GtkType vwrap_box_type = 0;
50 if (!vwrap_box_type)
52 static const GtkTypeInfo vwrap_box_info =
54 "GtkVWrapBox",
55 sizeof (GtkVWrapBox),
56 sizeof (GtkVWrapBoxClass),
57 (GtkClassInitFunc) gtk_vwrap_box_class_init,
58 (GtkObjectInitFunc) gtk_vwrap_box_init,
59 /* reserved_1 */ NULL,
60 /* reserved_2 */ NULL,
61 (GtkClassInitFunc) NULL,
64 vwrap_box_type = gtk_type_unique (GTK_TYPE_WRAP_BOX, &vwrap_box_info);
67 return vwrap_box_type;
70 static void
71 gtk_vwrap_box_class_init (GtkVWrapBoxClass *class)
73 GtkObjectClass *object_class;
74 GtkWidgetClass *widget_class;
75 GtkContainerClass *container_class;
76 GtkWrapBoxClass *wrap_box_class;
78 object_class = GTK_OBJECT_CLASS (class);
79 widget_class = GTK_WIDGET_CLASS (class);
80 container_class = GTK_CONTAINER_CLASS (class);
81 wrap_box_class = GTK_WRAP_BOX_CLASS (class);
83 parent_class = gtk_type_class (GTK_TYPE_WRAP_BOX);
85 widget_class->size_request = gtk_vwrap_box_size_request;
86 widget_class->size_allocate = gtk_vwrap_box_size_allocate;
88 wrap_box_class->rlist_line_children = reverse_list_col_children;
91 static void
92 gtk_vwrap_box_init (GtkVWrapBox *vwbox)
94 vwbox->max_child_height = 0;
95 vwbox->max_child_width = 0;
98 GtkWidget*
99 gtk_vwrap_box_new (gboolean homogeneous)
101 GtkVWrapBox *vwbox;
103 vwbox = GTK_VWRAP_BOX (gtk_widget_new (GTK_TYPE_VWRAP_BOX, NULL));
105 GTK_WRAP_BOX (vwbox)->homogeneous = homogeneous ? TRUE : FALSE;
107 return GTK_WIDGET (vwbox);
110 static inline void
111 get_child_requisition (GtkWrapBox *wbox,
112 GtkWidget *child,
113 GtkRequisition *child_requisition)
115 if (wbox->homogeneous)
117 GtkVWrapBox *vwbox = GTK_VWRAP_BOX (wbox);
119 child_requisition->height = vwbox->max_child_height;
120 child_requisition->width = vwbox->max_child_width;
122 else
123 gtk_widget_get_child_requisition (child, child_requisition);
126 static gfloat
127 get_layout_size (GtkVWrapBox *this,
128 guint max_height,
129 guint *height_inc)
131 GtkWrapBox *wbox = GTK_WRAP_BOX (this);
132 GtkWrapBoxChild *child;
133 guint n_cols, left_over = 0, total_width = 0;
134 gboolean last_col_filled = TRUE;
136 *height_inc = this->max_child_height + 1;
138 n_cols = 0;
139 for (child = wbox->children; child; child = child->next)
141 GtkWrapBoxChild *col_child;
142 GtkRequisition child_requisition;
143 guint col_height, col_width, n = 1;
145 if (!GTK_WIDGET_VISIBLE (child->widget))
146 continue;
148 get_child_requisition (wbox, child->widget, &child_requisition);
149 if (!last_col_filled)
150 *height_inc = MIN (*height_inc, child_requisition.height - left_over);
151 col_height = child_requisition.height;
152 col_width = child_requisition.width;
153 for (col_child = child->next; col_child && n < wbox->child_limit; col_child = col_child->next)
155 if (GTK_WIDGET_VISIBLE (col_child->widget))
157 get_child_requisition (wbox, col_child->widget, &child_requisition);
158 if (col_height + wbox->vspacing + child_requisition.height > max_height)
159 break;
160 col_height += wbox->vspacing + child_requisition.height;
161 col_width = MAX (col_width, child_requisition.width);
162 n++;
164 child = col_child;
166 last_col_filled = n >= wbox->child_limit;
167 left_over = last_col_filled ? 0 : max_height - (col_height + wbox->vspacing);
168 total_width += (n_cols ? wbox->hspacing : 0) + col_width;
169 n_cols++;
172 if (*height_inc > this->max_child_height)
173 *height_inc = 0;
175 return MAX (total_width, 1);
178 static void
179 gtk_vwrap_box_size_request (GtkWidget *widget,
180 GtkRequisition *requisition)
182 GtkVWrapBox *this = GTK_VWRAP_BOX (widget);
183 GtkWrapBox *wbox = GTK_WRAP_BOX (widget);
184 GtkWrapBoxChild *child;
185 gfloat ratio_dist, layout_height = 0;
186 guint col_inc = 0;
188 g_return_if_fail (requisition != NULL);
190 requisition->height = 0;
191 requisition->width = 0;
192 this->max_child_height = 0;
193 this->max_child_width = 0;
195 /* size_request all children */
196 for (child = wbox->children; child; child = child->next)
197 if (GTK_WIDGET_VISIBLE (child->widget))
199 GtkRequisition child_requisition;
201 gtk_widget_size_request (child->widget, &child_requisition);
203 this->max_child_height = MAX (this->max_child_height, child_requisition.height);
204 this->max_child_width = MAX (this->max_child_width, child_requisition.width);
207 /* figure all possible layouts */
208 ratio_dist = 32768;
209 layout_height = this->max_child_height;
212 gfloat layout_width;
213 gfloat ratio, dist;
215 layout_height += col_inc;
216 layout_width = get_layout_size (this, layout_height, &col_inc);
217 ratio = layout_width / layout_height; /*<h2v-skip>*/
218 dist = MAX (ratio, wbox->aspect_ratio) - MIN (ratio, wbox->aspect_ratio);
219 if (dist < ratio_dist)
221 ratio_dist = dist;
222 requisition->height = layout_height;
223 requisition->width = layout_width;
226 /* g_print ("ratio for height %d width %d = %f\n",
227 (gint) layout_height,
228 (gint) layout_width,
229 ratio);
232 while (col_inc);
234 requisition->width += GTK_CONTAINER (wbox)->border_width * 2; /*<h2v-skip>*/
235 requisition->height += GTK_CONTAINER (wbox)->border_width * 2; /*<h2v-skip>*/
236 /* g_print ("choosen: height %d, width %d\n",
237 requisition->height,
238 requisition->width);
242 static GSList*
243 reverse_list_col_children (GtkWrapBox *wbox,
244 GtkWrapBoxChild **child_p,
245 GtkAllocation *area,
246 guint *max_child_size,
247 gboolean *expand_line)
249 GSList *slist = NULL;
250 guint height = 0, col_height = area->height;
251 GtkWrapBoxChild *child = *child_p;
253 *max_child_size = 0;
254 *expand_line = FALSE;
256 while (child && !GTK_WIDGET_VISIBLE (child->widget))
258 *child_p = child->next;
259 child = *child_p;
262 if (child)
264 GtkRequisition child_requisition;
265 guint n = 1;
267 get_child_requisition (wbox, child->widget, &child_requisition);
268 height += child_requisition.height;
269 *max_child_size = MAX (*max_child_size, child_requisition.width);
270 *expand_line |= child->hexpand;
271 slist = g_slist_prepend (slist, child);
272 *child_p = child->next;
273 child = *child_p;
275 while (child && n < wbox->child_limit)
277 if (GTK_WIDGET_VISIBLE (child->widget))
279 get_child_requisition (wbox, child->widget, &child_requisition);
280 if (height + wbox->vspacing + child_requisition.height > col_height ||
281 child->wrapped)
282 break;
283 height += wbox->vspacing + child_requisition.height;
284 *max_child_size = MAX (*max_child_size, child_requisition.width);
285 *expand_line |= child->hexpand;
286 slist = g_slist_prepend (slist, child);
287 n++;
289 *child_p = child->next;
290 child = *child_p;
294 return slist;
297 static void
298 layout_col (GtkWrapBox *wbox,
299 GtkAllocation *area,
300 GSList *children,
301 guint children_per_line,
302 gboolean hexpand)
304 GSList *slist;
305 guint n_children = 0, n_expand_children = 0, have_expand_children = 0;
306 gint total_height = 0;
307 gfloat y, height, extra;
308 GtkAllocation child_allocation;
310 for (slist = children; slist; slist = slist->next)
312 GtkWrapBoxChild *child = slist->data;
313 GtkRequisition child_requisition;
315 n_children++;
316 if (child->vexpand)
317 n_expand_children++;
319 get_child_requisition (wbox, child->widget, &child_requisition);
320 total_height += child_requisition.height;
323 height = MAX (1, area->height - (n_children - 1) * wbox->vspacing);
324 if (height > total_height)
325 extra = height - total_height;
326 else
327 extra = 0;
328 have_expand_children = n_expand_children && extra;
330 y = area->y;
331 if (wbox->homogeneous)
333 height = MAX (1, area->height - (children_per_line - 1) * wbox->vspacing);
334 height /= ((gdouble) children_per_line);
335 extra = 0;
337 else if (have_expand_children && wbox->justify != GTK_JUSTIFY_FILL)
339 height = extra;
340 extra /= ((gdouble) n_expand_children);
342 else
344 if (wbox->justify == GTK_JUSTIFY_FILL)
346 height = extra;
347 have_expand_children = TRUE;
348 n_expand_children = n_children;
349 extra /= ((gdouble) n_expand_children);
351 else if (wbox->justify == GTK_JUSTIFY_CENTER)
353 y += extra / 2;
354 height = 0;
355 extra = 0;
357 else if (wbox->justify == GTK_JUSTIFY_LEFT)
359 height = 0;
360 extra = 0;
362 else if (wbox->justify == GTK_JUSTIFY_RIGHT)
364 y += extra;
365 height = 0;
366 extra = 0;
370 n_children = 0;
371 for (slist = children; slist; slist = slist->next)
373 GtkWrapBoxChild *child = slist->data;
375 child_allocation.y = y;
376 child_allocation.x = area->x;
377 if (wbox->homogeneous)
379 child_allocation.width = area->width;
380 child_allocation.height = height;
381 y += child_allocation.height + wbox->vspacing;
383 else
385 GtkRequisition child_requisition;
387 get_child_requisition (wbox, child->widget, &child_requisition);
389 if (child_requisition.width >= area->width)
390 child_allocation.width = area->width;
391 else
393 child_allocation.width = child_requisition.width;
394 if (wbox->line_justify == GTK_JUSTIFY_FILL || child->hfill)
395 child_allocation.width = area->width;
396 else if (child->hexpand || wbox->line_justify == GTK_JUSTIFY_CENTER)
397 child_allocation.x += (area->width - child_requisition.width) / 2;
398 else if (wbox->line_justify == GTK_JUSTIFY_BOTTOM)
399 child_allocation.x += area->width - child_requisition.width;
402 if (have_expand_children)
404 child_allocation.height = child_requisition.height;
405 if (child->vexpand || wbox->justify == GTK_JUSTIFY_FILL)
407 guint space;
409 n_expand_children--;
410 space = extra * n_expand_children;
411 space = height - space;
412 height -= space;
413 if (child->vfill)
414 child_allocation.height += space;
415 else
417 child_allocation.y += space / 2;
418 y += space;
422 else
424 /* g_print ("child_allocation.y %d += %d * %f ",
425 child_allocation.y, n_children, extra); */
426 child_allocation.y += n_children * extra;
427 /* g_print ("= %d\n",
428 child_allocation.y); */
429 child_allocation.height = MIN (child_requisition.height,
430 area->height - child_allocation.y + area->y);
434 y += child_allocation.height + wbox->vspacing;
435 gtk_widget_size_allocate (child->widget, &child_allocation);
436 n_children++;
440 typedef struct _Line Line;
441 struct _Line
443 GSList *children;
444 guint16 min_size;
445 guint expand : 1;
446 Line *next;
449 static void
450 layout_cols (GtkWrapBox *wbox,
451 GtkAllocation *area)
453 GtkWrapBoxChild *next_child;
454 guint min_width;
455 gboolean hexpand;
456 GSList *slist;
457 Line *line_list = NULL;
458 guint total_width = 0, n_expand_lines = 0, n_lines = 0;
459 gfloat shrink_width;
460 guint children_per_line;
462 next_child = wbox->children;
463 slist = GTK_WRAP_BOX_GET_CLASS (wbox)->rlist_line_children (wbox,
464 &next_child,
465 area,
466 &min_width,
467 &hexpand);
468 slist = g_slist_reverse (slist);
470 children_per_line = g_slist_length (slist);
471 while (slist)
473 Line *line = g_new (Line, 1);
475 line->children = slist;
476 line->min_size = min_width;
477 total_width += min_width;
478 line->expand = hexpand;
479 if (hexpand)
480 n_expand_lines++;
481 line->next = line_list;
482 line_list = line;
483 n_lines++;
485 slist = GTK_WRAP_BOX_GET_CLASS (wbox)->rlist_line_children (wbox,
486 &next_child,
487 area,
488 &min_width,
489 &hexpand);
490 slist = g_slist_reverse (slist);
493 if (total_width > area->width)
494 shrink_width = total_width - area->width;
495 else
496 shrink_width = 0;
498 if (1) /* reverse lines and shrink */
500 Line *prev = NULL, *last = NULL;
501 gfloat n_shrink_lines = n_lines;
503 while (line_list)
505 Line *tmp = line_list->next;
507 if (shrink_width)
509 Line *line = line_list;
510 guint shrink_fract = shrink_width / n_shrink_lines + 0.5;
512 if (line->min_size > shrink_fract)
514 shrink_width -= shrink_fract;
515 line->min_size -= shrink_fract;
517 else
519 shrink_width -= line->min_size - 1;
520 line->min_size = 1;
523 n_shrink_lines--;
525 last = line_list;
526 line_list->next = prev;
527 prev = line_list;
528 line_list = tmp;
530 line_list = last;
533 if (n_lines)
535 Line *line;
536 gfloat x, width, extra = 0;
538 width = area->width;
539 width = MAX (n_lines, width - (n_lines - 1) * wbox->hspacing);
541 if (wbox->homogeneous)
542 width /= ((gdouble) n_lines);
543 else if (n_expand_lines)
545 width = MAX (0, width - total_width);
546 extra = width / ((gdouble) n_expand_lines);
548 else
549 width = 0;
551 x = area->x;
552 line = line_list;
553 while (line)
555 GtkAllocation col_allocation;
556 Line *next_line = line->next;
558 col_allocation.y = area->y;
559 col_allocation.height = area->height;
560 if (wbox->homogeneous)
561 col_allocation.width = width;
562 else
564 col_allocation.width = line->min_size;
566 if (line->expand)
567 col_allocation.width += extra;
570 col_allocation.x = x;
572 x += col_allocation.width + wbox->hspacing;
573 layout_col (wbox,
574 &col_allocation,
575 line->children,
576 children_per_line,
577 line->expand);
579 g_slist_free (line->children);
580 g_free (line);
581 line = next_line;
586 static void
587 gtk_vwrap_box_size_allocate (GtkWidget *widget,
588 GtkAllocation *allocation)
590 GtkWrapBox *wbox = GTK_WRAP_BOX (widget);
591 GtkAllocation area;
592 gint border = GTK_CONTAINER (wbox)->border_width; /*<h2v-skip>*/
594 widget->allocation = *allocation;
595 area.y = allocation->y + border;
596 area.x = allocation->x + border;
597 area.height = MAX (1, (gint) allocation->height - border * 2);
598 area.width = MAX (1, (gint) allocation->width - border * 2);
600 /*<h2v-off>*/
601 /* g_print ("got: width %d, height %d\n",
602 allocation->width,
603 allocation->height);
605 /*<h2v-on>*/
607 layout_cols (wbox, &area);