UML class fix bigtime.
[dia.git] / app / gtkhwrapbox.c
blob4335e4b212136b1f40a3b9f9b09bb6d3c99d66e2
1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
4 * GtkHWrapBox: Horizontal 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 "gtkhwrapbox.h"
24 #include <math.h>
27 /* --- prototypes --- */
28 static void gtk_hwrap_box_class_init (GtkHWrapBoxClass *klass);
29 static void gtk_hwrap_box_init (GtkHWrapBox *hwbox);
30 static void gtk_hwrap_box_size_request (GtkWidget *widget,
31 GtkRequisition *requisition);
32 static void gtk_hwrap_box_size_allocate (GtkWidget *widget,
33 GtkAllocation *allocation);
34 static GSList* reverse_list_row_children (GtkWrapBox *wbox,
35 GtkWrapBoxChild **child_p,
36 GtkAllocation *area,
37 guint *max_height,
38 gboolean *can_vexpand);
41 /* --- variables --- */
42 static gpointer parent_class = NULL;
45 /* --- functions --- */
46 GtkType
47 gtk_hwrap_box_get_type (void)
49 static GtkType hwrap_box_type = 0;
51 if (!hwrap_box_type)
53 static const GtkTypeInfo hwrap_box_info =
55 "GtkHWrapBox",
56 sizeof (GtkHWrapBox),
57 sizeof (GtkHWrapBoxClass),
58 (GtkClassInitFunc) gtk_hwrap_box_class_init,
59 (GtkObjectInitFunc) gtk_hwrap_box_init,
60 /* reserved_1 */ NULL,
61 /* reserved_2 */ NULL,
62 (GtkClassInitFunc) NULL,
65 hwrap_box_type = gtk_type_unique (GTK_TYPE_WRAP_BOX, &hwrap_box_info);
68 return hwrap_box_type;
71 static void
72 gtk_hwrap_box_class_init (GtkHWrapBoxClass *class)
74 GtkObjectClass *object_class;
75 GtkWidgetClass *widget_class;
76 GtkContainerClass *container_class;
77 GtkWrapBoxClass *wrap_box_class;
79 object_class = GTK_OBJECT_CLASS (class);
80 widget_class = GTK_WIDGET_CLASS (class);
81 container_class = GTK_CONTAINER_CLASS (class);
82 wrap_box_class = GTK_WRAP_BOX_CLASS (class);
84 parent_class = gtk_type_class (GTK_TYPE_WRAP_BOX);
86 widget_class->size_request = gtk_hwrap_box_size_request;
87 widget_class->size_allocate = gtk_hwrap_box_size_allocate;
89 wrap_box_class->rlist_line_children = reverse_list_row_children;
92 static void
93 gtk_hwrap_box_init (GtkHWrapBox *hwbox)
95 hwbox->max_child_width = 0;
96 hwbox->max_child_height = 0;
99 GtkWidget*
100 gtk_hwrap_box_new (gboolean homogeneous)
102 GtkHWrapBox *hwbox;
104 hwbox = GTK_HWRAP_BOX (gtk_widget_new (GTK_TYPE_HWRAP_BOX, NULL));
106 GTK_WRAP_BOX (hwbox)->homogeneous = homogeneous ? TRUE : FALSE;
108 return GTK_WIDGET (hwbox);
111 static inline void
112 get_child_requisition (GtkWrapBox *wbox,
113 GtkWidget *child,
114 GtkRequisition *child_requisition)
116 if (wbox->homogeneous)
118 GtkHWrapBox *hwbox = GTK_HWRAP_BOX (wbox);
120 child_requisition->width = hwbox->max_child_width;
121 child_requisition->height = hwbox->max_child_height;
123 else
124 gtk_widget_get_child_requisition (child, child_requisition);
127 static gfloat
128 get_layout_size (GtkHWrapBox *this,
129 guint max_width,
130 guint *width_inc)
132 GtkWrapBox *wbox = GTK_WRAP_BOX (this);
133 GtkWrapBoxChild *child;
134 guint n_rows, left_over = 0, total_height = 0;
135 gboolean last_row_filled = TRUE;
137 *width_inc = this->max_child_width + 1;
139 n_rows = 0;
140 for (child = wbox->children; child; child = child->next)
142 GtkWrapBoxChild *row_child;
143 GtkRequisition child_requisition;
144 guint row_width, row_height, n = 1;
146 if (!GTK_WIDGET_VISIBLE (child->widget))
147 continue;
149 get_child_requisition (wbox, child->widget, &child_requisition);
150 if (!last_row_filled)
151 *width_inc = MIN (*width_inc, child_requisition.width - left_over);
152 row_width = child_requisition.width;
153 row_height = child_requisition.height;
154 for (row_child = child->next; row_child && n < wbox->child_limit; row_child = row_child->next)
156 if (GTK_WIDGET_VISIBLE (row_child->widget))
158 get_child_requisition (wbox, row_child->widget, &child_requisition);
159 if (row_width + wbox->hspacing + child_requisition.width > max_width)
160 break;
161 row_width += wbox->hspacing + child_requisition.width;
162 row_height = MAX (row_height, child_requisition.height);
163 n++;
165 child = row_child;
167 last_row_filled = n >= wbox->child_limit;
168 left_over = last_row_filled ? 0 : max_width - (row_width + wbox->hspacing);
169 total_height += (n_rows ? wbox->vspacing : 0) + row_height;
170 n_rows++;
173 if (*width_inc > this->max_child_width)
174 *width_inc = 0;
176 return MAX (total_height, 1);
179 static void
180 gtk_hwrap_box_size_request (GtkWidget *widget,
181 GtkRequisition *requisition)
183 GtkHWrapBox *this = GTK_HWRAP_BOX (widget);
184 GtkWrapBox *wbox = GTK_WRAP_BOX (widget);
185 GtkWrapBoxChild *child;
186 gfloat ratio_dist, layout_width = 0;
187 guint row_inc = 0;
189 g_return_if_fail (requisition != NULL);
191 requisition->width = 0;
192 requisition->height = 0;
193 this->max_child_width = 0;
194 this->max_child_height = 0;
196 /* size_request all children */
197 for (child = wbox->children; child; child = child->next)
198 if (GTK_WIDGET_VISIBLE (child->widget))
200 GtkRequisition child_requisition;
202 gtk_widget_size_request (child->widget, &child_requisition);
204 this->max_child_width = MAX (this->max_child_width, child_requisition.width);
205 this->max_child_height = MAX (this->max_child_height, child_requisition.height);
208 /* figure all possible layouts */
209 ratio_dist = 32768;
210 layout_width = this->max_child_width;
213 gfloat layout_height;
214 gfloat ratio, dist;
216 layout_width += row_inc;
217 layout_height = get_layout_size (this, layout_width, &row_inc);
218 ratio = layout_width / layout_height; /*<h2v-skip>*/
219 dist = MAX (ratio, wbox->aspect_ratio) - MIN (ratio, wbox->aspect_ratio);
220 if (dist < ratio_dist)
222 ratio_dist = dist;
223 requisition->width = layout_width;
224 requisition->height = layout_height;
227 /* g_print ("ratio for width %d height %d = %f\n",
228 (gint) layout_width,
229 (gint) layout_height,
230 ratio);
233 while (row_inc);
235 requisition->width += GTK_CONTAINER (wbox)->border_width * 2; /*<h2v-skip>*/
236 requisition->height += GTK_CONTAINER (wbox)->border_width * 2; /*<h2v-skip>*/
237 /* g_print ("choosen: width %d, height %d\n",
238 requisition->width,
239 requisition->height);
243 static GSList*
244 reverse_list_row_children (GtkWrapBox *wbox,
245 GtkWrapBoxChild **child_p,
246 GtkAllocation *area,
247 guint *max_child_size,
248 gboolean *expand_line)
250 GSList *slist = NULL;
251 guint width = 0, row_width = area->width;
252 GtkWrapBoxChild *child = *child_p;
254 *max_child_size = 0;
255 *expand_line = FALSE;
257 while (child && !GTK_WIDGET_VISIBLE (child->widget))
259 *child_p = child->next;
260 child = *child_p;
263 if (child)
265 GtkRequisition child_requisition;
266 guint n = 1;
268 get_child_requisition (wbox, child->widget, &child_requisition);
269 width += child_requisition.width;
270 *max_child_size = MAX (*max_child_size, child_requisition.height);
271 *expand_line |= child->vexpand;
272 slist = g_slist_prepend (slist, child);
273 *child_p = child->next;
274 child = *child_p;
276 while (child && n < wbox->child_limit)
278 if (GTK_WIDGET_VISIBLE (child->widget))
280 get_child_requisition (wbox, child->widget, &child_requisition);
281 if (width + wbox->hspacing + child_requisition.width > row_width ||
282 child->wrapped)
283 break;
284 width += wbox->hspacing + child_requisition.width;
285 *max_child_size = MAX (*max_child_size, child_requisition.height);
286 *expand_line |= child->vexpand;
287 slist = g_slist_prepend (slist, child);
288 n++;
290 *child_p = child->next;
291 child = *child_p;
295 return slist;
298 static void
299 layout_row (GtkWrapBox *wbox,
300 GtkAllocation *area,
301 GSList *children,
302 guint children_per_line,
303 gboolean vexpand)
305 GSList *slist;
306 guint n_children = 0, n_expand_children = 0, have_expand_children = 0;
307 gint total_width = 0;
308 gfloat x, width, extra;
309 GtkAllocation child_allocation;
311 for (slist = children; slist; slist = slist->next)
313 GtkWrapBoxChild *child = slist->data;
314 GtkRequisition child_requisition;
316 n_children++;
317 if (child->hexpand)
318 n_expand_children++;
320 get_child_requisition (wbox, child->widget, &child_requisition);
321 total_width += child_requisition.width;
324 width = MAX (1, area->width - (n_children - 1) * wbox->hspacing);
325 if (width > total_width)
326 extra = width - total_width;
327 else
328 extra = 0;
329 have_expand_children = n_expand_children && extra;
331 x = area->x;
332 if (wbox->homogeneous)
334 width = MAX (1, area->width - (children_per_line - 1) * wbox->hspacing);
335 width /= ((gdouble) children_per_line);
336 extra = 0;
338 else if (have_expand_children && wbox->justify != GTK_JUSTIFY_FILL)
340 width = extra;
341 extra /= ((gdouble) n_expand_children);
343 else
345 if (wbox->justify == GTK_JUSTIFY_FILL)
347 width = extra;
348 have_expand_children = TRUE;
349 n_expand_children = n_children;
350 extra /= ((gdouble) n_expand_children);
352 else if (wbox->justify == GTK_JUSTIFY_CENTER)
354 x += extra / 2;
355 width = 0;
356 extra = 0;
358 else if (wbox->justify == GTK_JUSTIFY_LEFT)
360 width = 0;
361 extra = 0;
363 else if (wbox->justify == GTK_JUSTIFY_RIGHT)
365 x += extra;
366 width = 0;
367 extra = 0;
371 n_children = 0;
372 for (slist = children; slist; slist = slist->next)
374 GtkWrapBoxChild *child = slist->data;
376 child_allocation.x = x;
377 child_allocation.y = area->y;
378 if (wbox->homogeneous)
380 child_allocation.height = area->height;
381 child_allocation.width = width;
382 x += child_allocation.width + wbox->hspacing;
384 else
386 GtkRequisition child_requisition;
388 get_child_requisition (wbox, child->widget, &child_requisition);
390 if (child_requisition.height >= area->height)
391 child_allocation.height = area->height;
392 else
394 child_allocation.height = child_requisition.height;
395 if (wbox->line_justify == GTK_JUSTIFY_FILL || child->vfill)
396 child_allocation.height = area->height;
397 else if (child->vexpand || wbox->line_justify == GTK_JUSTIFY_CENTER)
398 child_allocation.y += (area->height - child_requisition.height) / 2;
399 else if (wbox->line_justify == GTK_JUSTIFY_BOTTOM)
400 child_allocation.y += area->height - child_requisition.height;
403 if (have_expand_children)
405 child_allocation.width = child_requisition.width;
406 if (child->hexpand || wbox->justify == GTK_JUSTIFY_FILL)
408 guint space;
410 n_expand_children--;
411 space = extra * n_expand_children;
412 space = width - space;
413 width -= space;
414 if (child->hfill)
415 child_allocation.width += space;
416 else
418 child_allocation.x += space / 2;
419 x += space;
423 else
425 /* g_print ("child_allocation.x %d += %d * %f ",
426 child_allocation.x, n_children, extra); */
427 child_allocation.x += n_children * extra;
428 /* g_print ("= %d\n",
429 child_allocation.x); */
430 child_allocation.width = MIN (child_requisition.width,
431 area->width - child_allocation.x + area->x);
435 x += child_allocation.width + wbox->hspacing;
436 gtk_widget_size_allocate (child->widget, &child_allocation);
437 n_children++;
441 typedef struct _Line Line;
442 struct _Line
444 GSList *children;
445 guint16 min_size;
446 guint expand : 1;
447 Line *next;
450 static void
451 layout_rows (GtkWrapBox *wbox,
452 GtkAllocation *area)
454 GtkWrapBoxChild *next_child;
455 guint min_height;
456 gboolean vexpand;
457 GSList *slist;
458 Line *line_list = NULL;
459 guint total_height = 0, n_expand_lines = 0, n_lines = 0;
460 gfloat shrink_height;
461 guint children_per_line;
463 next_child = wbox->children;
464 slist = GTK_WRAP_BOX_GET_CLASS (wbox)->rlist_line_children (wbox,
465 &next_child,
466 area,
467 &min_height,
468 &vexpand);
469 slist = g_slist_reverse (slist);
471 children_per_line = g_slist_length (slist);
472 while (slist)
474 Line *line = g_new (Line, 1);
476 line->children = slist;
477 line->min_size = min_height;
478 total_height += min_height;
479 line->expand = vexpand;
480 if (vexpand)
481 n_expand_lines++;
482 line->next = line_list;
483 line_list = line;
484 n_lines++;
486 slist = GTK_WRAP_BOX_GET_CLASS (wbox)->rlist_line_children (wbox,
487 &next_child,
488 area,
489 &min_height,
490 &vexpand);
491 slist = g_slist_reverse (slist);
494 if (total_height > area->height)
495 shrink_height = total_height - area->height;
496 else
497 shrink_height = 0;
499 if (1) /* reverse lines and shrink */
501 Line *prev = NULL, *last = NULL;
502 gfloat n_shrink_lines = n_lines;
504 while (line_list)
506 Line *tmp = line_list->next;
508 if (shrink_height)
510 Line *line = line_list;
511 guint shrink_fract = shrink_height / n_shrink_lines + 0.5;
513 if (line->min_size > shrink_fract)
515 shrink_height -= shrink_fract;
516 line->min_size -= shrink_fract;
518 else
520 shrink_height -= line->min_size - 1;
521 line->min_size = 1;
524 n_shrink_lines--;
526 last = line_list;
527 line_list->next = prev;
528 prev = line_list;
529 line_list = tmp;
531 line_list = last;
534 if (n_lines)
536 Line *line;
537 gfloat y, height, extra = 0;
539 height = area->height;
540 height = MAX (n_lines, height - (n_lines - 1) * wbox->vspacing);
542 if (wbox->homogeneous)
543 height /= ((gdouble) n_lines);
544 else if (n_expand_lines)
546 height = MAX (0, height - total_height);
547 extra = height / ((gdouble) n_expand_lines);
549 else
550 height = 0;
552 y = area->y;
553 line = line_list;
554 while (line)
556 GtkAllocation row_allocation;
557 Line *next_line = line->next;
559 row_allocation.x = area->x;
560 row_allocation.width = area->width;
561 if (wbox->homogeneous)
562 row_allocation.height = height;
563 else
565 row_allocation.height = line->min_size;
567 if (line->expand)
568 row_allocation.height += extra;
571 row_allocation.y = y;
573 y += row_allocation.height + wbox->vspacing;
574 layout_row (wbox,
575 &row_allocation,
576 line->children,
577 children_per_line,
578 line->expand);
580 g_slist_free (line->children);
581 g_free (line);
582 line = next_line;
587 static void
588 gtk_hwrap_box_size_allocate (GtkWidget *widget,
589 GtkAllocation *allocation)
591 GtkWrapBox *wbox = GTK_WRAP_BOX (widget);
592 GtkAllocation area;
593 gint border = GTK_CONTAINER (wbox)->border_width; /*<h2v-skip>*/
595 widget->allocation = *allocation;
596 area.x = allocation->x + border;
597 area.y = allocation->y + border;
598 area.width = MAX (1, (gint) allocation->width - border * 2);
599 area.height = MAX (1, (gint) allocation->height - border * 2);
601 /*<h2v-off>*/
602 /* g_print ("got: width %d, height %d\n",
603 allocation->width,
604 allocation->height);
606 /*<h2v-on>*/
608 layout_rows (wbox, &area);