svg text alignment
[dia.git] / app / gtkvwrapbox.c
blob118c9d52edec28f5ca42f6c313e4f0ab4a8a2088
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.
22 #include <config.h>
24 #include "gtkvwrapbox.h"
25 #include <math.h>
28 /* --- prototypes --- */
29 static void gtk_vwrap_box_class_init (GtkVWrapBoxClass *klass);
30 static void gtk_vwrap_box_init (GtkVWrapBox *vwbox);
31 static void gtk_vwrap_box_size_request (GtkWidget *widget,
32 GtkRequisition *requisition);
33 static void gtk_vwrap_box_size_allocate (GtkWidget *widget,
34 GtkAllocation *allocation);
35 static GSList* reverse_list_col_children (GtkWrapBox *wbox,
36 GtkWrapBoxChild **child_p,
37 GtkAllocation *area,
38 guint *max_width,
39 gboolean *can_hexpand);
42 /* --- variables --- */
43 static gpointer parent_class = NULL;
46 /* --- functions --- */
47 GtkType
48 gtk_vwrap_box_get_type (void)
50 static GtkType vwrap_box_type = 0;
52 if (!vwrap_box_type)
54 static const GtkTypeInfo vwrap_box_info =
56 "GtkVWrapBox",
57 sizeof (GtkVWrapBox),
58 sizeof (GtkVWrapBoxClass),
59 (GtkClassInitFunc) gtk_vwrap_box_class_init,
60 (GtkObjectInitFunc) gtk_vwrap_box_init,
61 /* reserved_1 */ NULL,
62 /* reserved_2 */ NULL,
63 (GtkClassInitFunc) NULL,
66 vwrap_box_type = gtk_type_unique (GTK_TYPE_WRAP_BOX, &vwrap_box_info);
69 return vwrap_box_type;
72 static void
73 gtk_vwrap_box_class_init (GtkVWrapBoxClass *class)
75 GtkObjectClass *object_class;
76 GtkWidgetClass *widget_class;
77 GtkContainerClass *container_class;
78 GtkWrapBoxClass *wrap_box_class;
80 object_class = GTK_OBJECT_CLASS (class);
81 widget_class = GTK_WIDGET_CLASS (class);
82 container_class = GTK_CONTAINER_CLASS (class);
83 wrap_box_class = GTK_WRAP_BOX_CLASS (class);
85 parent_class = gtk_type_class (GTK_TYPE_WRAP_BOX);
87 widget_class->size_request = gtk_vwrap_box_size_request;
88 widget_class->size_allocate = gtk_vwrap_box_size_allocate;
90 wrap_box_class->rlist_line_children = reverse_list_col_children;
93 static void
94 gtk_vwrap_box_init (GtkVWrapBox *vwbox)
96 vwbox->max_child_height = 0;
97 vwbox->max_child_width = 0;
100 GtkWidget*
101 gtk_vwrap_box_new (gboolean homogeneous)
103 GtkVWrapBox *vwbox;
105 vwbox = GTK_VWRAP_BOX (gtk_widget_new (GTK_TYPE_VWRAP_BOX, NULL));
107 GTK_WRAP_BOX (vwbox)->homogeneous = homogeneous ? TRUE : FALSE;
109 return GTK_WIDGET (vwbox);
112 static inline void
113 get_child_requisition (GtkWrapBox *wbox,
114 GtkWidget *child,
115 GtkRequisition *child_requisition)
117 if (wbox->homogeneous)
119 GtkVWrapBox *vwbox = GTK_VWRAP_BOX (wbox);
121 child_requisition->height = vwbox->max_child_height;
122 child_requisition->width = vwbox->max_child_width;
124 else
125 gtk_widget_get_child_requisition (child, child_requisition);
128 #if THIS_IS_PROBABLY_DEAD_CODE
129 static void
130 _gtk_vwrap_box_size_request (GtkWidget *widget,
131 GtkRequisition *requisition)
133 GtkVWrapBox *this = GTK_VWRAP_BOX (widget);
134 GtkWrapBox *wbox = GTK_WRAP_BOX (widget);
135 GtkWrapBoxChild *child;
136 guint area = 0;
138 g_return_if_fail (requisition != NULL);
140 /*<h2v-off>*/
141 requisition->width = 0;
142 requisition->height = 0;
143 this->max_child_width = 0;
144 this->max_child_height = 0;
146 for (child = wbox->children; child; child = child->next)
147 if (GTK_WIDGET_VISIBLE (child->widget))
149 GtkRequisition child_requisition;
151 gtk_widget_size_request (child->widget, &child_requisition);
153 area += child_requisition.width * child_requisition.height;
154 this->max_child_width = MAX (this->max_child_width, child_requisition.width);
155 this->max_child_height = MAX (this->max_child_height, child_requisition.height);
157 if (wbox->homogeneous)
158 area = this->max_child_width * this->max_child_height * wbox->n_children;
160 if (area)
162 requisition->width = sqrt (area * wbox->aspect_ratio);
163 requisition->height = area / requisition->width;
165 else
167 requisition->width = 0;
168 requisition->height = 0;
171 requisition->width += GTK_CONTAINER (wbox)->border_width * 2;
172 requisition->height += GTK_CONTAINER (wbox)->border_width * 2;
173 /*<h2v-on>*/
175 #endif
177 static gfloat
178 get_layout_size (GtkVWrapBox *this,
179 guint max_height,
180 guint *height_inc)
182 GtkWrapBox *wbox = GTK_WRAP_BOX (this);
183 GtkWrapBoxChild *child;
184 guint n_cols, left_over = 0, total_width = 0;
185 gboolean last_col_filled = TRUE;
187 *height_inc = this->max_child_height + 1;
189 n_cols = 0;
190 for (child = wbox->children; child; child = child->next)
192 GtkWrapBoxChild *col_child;
193 GtkRequisition child_requisition;
194 guint col_height, col_width, n = 1;
196 if (!GTK_WIDGET_VISIBLE (child->widget))
197 continue;
199 get_child_requisition (wbox, child->widget, &child_requisition);
200 if (!last_col_filled)
201 *height_inc = MIN (*height_inc, child_requisition.height - left_over);
202 col_height = child_requisition.height;
203 col_width = child_requisition.width;
204 for (col_child = child->next; col_child && n < wbox->child_limit; col_child = col_child->next)
206 if (GTK_WIDGET_VISIBLE (col_child->widget))
208 get_child_requisition (wbox, col_child->widget, &child_requisition);
209 if (col_height + wbox->vspacing + child_requisition.height > max_height)
210 break;
211 col_height += wbox->vspacing + child_requisition.height;
212 col_width = MAX (col_width, child_requisition.width);
213 n++;
215 child = col_child;
217 last_col_filled = n >= wbox->child_limit;
218 left_over = last_col_filled ? 0 : max_height - (col_height + wbox->vspacing);
219 total_width += (n_cols ? wbox->hspacing : 0) + col_width;
220 n_cols++;
223 if (*height_inc > this->max_child_height)
224 *height_inc = 0;
226 return MAX (total_width, 1);
229 static void
230 gtk_vwrap_box_size_request (GtkWidget *widget,
231 GtkRequisition *requisition)
233 GtkVWrapBox *this = GTK_VWRAP_BOX (widget);
234 GtkWrapBox *wbox = GTK_WRAP_BOX (widget);
235 GtkWrapBoxChild *child;
236 gfloat ratio_dist, layout_height = 0;
237 guint col_inc = 0;
239 g_return_if_fail (requisition != NULL);
241 requisition->height = 0;
242 requisition->width = 0;
243 this->max_child_height = 0;
244 this->max_child_width = 0;
246 /* size_request all children */
247 for (child = wbox->children; child; child = child->next)
248 if (GTK_WIDGET_VISIBLE (child->widget))
250 GtkRequisition child_requisition;
252 gtk_widget_size_request (child->widget, &child_requisition);
254 this->max_child_height = MAX (this->max_child_height, child_requisition.height);
255 this->max_child_width = MAX (this->max_child_width, child_requisition.width);
258 /* figure all possible layouts */
259 ratio_dist = 32768;
260 layout_height = this->max_child_height;
263 gfloat layout_width;
264 gfloat ratio, dist;
266 layout_height += col_inc;
267 layout_width = get_layout_size (this, layout_height, &col_inc);
268 ratio = layout_width / layout_height; /*<h2v-skip>*/
269 dist = MAX (ratio, wbox->aspect_ratio) - MIN (ratio, wbox->aspect_ratio);
270 if (dist < ratio_dist)
272 ratio_dist = dist;
273 requisition->height = layout_height;
274 requisition->width = layout_width;
277 /* g_print ("ratio for height %d width %d = %f\n",
278 (gint) layout_height,
279 (gint) layout_width,
280 ratio);
283 while (col_inc);
285 requisition->width += GTK_CONTAINER (wbox)->border_width * 2; /*<h2v-skip>*/
286 requisition->height += GTK_CONTAINER (wbox)->border_width * 2; /*<h2v-skip>*/
287 /* g_print ("choosen: height %d, width %d\n",
288 requisition->height,
289 requisition->width);
293 static GSList*
294 reverse_list_col_children (GtkWrapBox *wbox,
295 GtkWrapBoxChild **child_p,
296 GtkAllocation *area,
297 guint *max_child_size,
298 gboolean *expand_line)
300 GSList *slist = NULL;
301 guint height = 0, col_height = area->height;
302 GtkWrapBoxChild *child = *child_p;
304 *max_child_size = 0;
305 *expand_line = FALSE;
307 while (child && !GTK_WIDGET_VISIBLE (child->widget))
309 *child_p = child->next;
310 child = *child_p;
313 if (child)
315 GtkRequisition child_requisition;
316 guint n = 1;
318 get_child_requisition (wbox, child->widget, &child_requisition);
319 height += child_requisition.height;
320 *max_child_size = MAX (*max_child_size, child_requisition.width);
321 *expand_line |= child->hexpand;
322 slist = g_slist_prepend (slist, child);
323 *child_p = child->next;
324 child = *child_p;
326 while (child && n < wbox->child_limit)
328 if (GTK_WIDGET_VISIBLE (child->widget))
330 get_child_requisition (wbox, child->widget, &child_requisition);
331 if (height + wbox->vspacing + child_requisition.height > col_height ||
332 child->forced_break)
333 break;
334 height += wbox->vspacing + child_requisition.height;
335 *max_child_size = MAX (*max_child_size, child_requisition.width);
336 *expand_line |= child->hexpand;
337 slist = g_slist_prepend (slist, child);
338 n++;
340 *child_p = child->next;
341 child = *child_p;
345 return slist;
348 static void
349 layout_col (GtkWrapBox *wbox,
350 GtkAllocation *area,
351 GSList *children,
352 guint children_per_line,
353 gboolean hexpand)
355 GSList *slist;
356 guint n_children = 0, n_expand_children = 0, have_expand_children = 0, total_height = 0;
357 gfloat y, height, extra;
358 GtkAllocation child_allocation;
360 for (slist = children; slist; slist = slist->next)
362 GtkWrapBoxChild *child = slist->data;
363 GtkRequisition child_requisition;
365 n_children++;
366 if (child->vexpand)
367 n_expand_children++;
369 get_child_requisition (wbox, child->widget, &child_requisition);
370 total_height += child_requisition.height;
373 height = MAX (1, area->height - (n_children - 1) * wbox->vspacing);
374 if (height > total_height)
375 extra = height - total_height;
376 else
377 extra = 0;
378 have_expand_children = n_expand_children && extra;
380 y = area->y;
381 if (wbox->homogeneous)
383 height = MAX (1, area->height - (children_per_line - 1) * wbox->vspacing);
384 height /= ((gdouble) children_per_line);
385 extra = 0;
387 else if (have_expand_children && wbox->justify != GTK_JUSTIFY_FILL)
389 height = extra;
390 extra /= ((gdouble) n_expand_children);
392 else
394 if (wbox->justify == GTK_JUSTIFY_FILL)
396 height = extra;
397 have_expand_children = TRUE;
398 n_expand_children = n_children;
399 extra /= ((gdouble) n_expand_children);
401 else if (wbox->justify == GTK_JUSTIFY_CENTER)
403 y += extra / 2;
404 height = 0;
405 extra = 0;
407 else if (wbox->justify == GTK_JUSTIFY_LEFT)
409 height = 0;
410 extra = 0;
412 else if (wbox->justify == GTK_JUSTIFY_RIGHT)
414 y += extra;
415 height = 0;
416 extra = 0;
420 n_children = 0;
421 for (slist = children; slist; slist = slist->next)
423 GtkWrapBoxChild *child = slist->data;
425 child_allocation.y = y;
426 child_allocation.x = area->x;
427 if (wbox->homogeneous)
429 child_allocation.width = area->width;
430 child_allocation.height = height;
431 y += child_allocation.height + wbox->vspacing;
433 else
435 GtkRequisition child_requisition;
437 get_child_requisition (wbox, child->widget, &child_requisition);
439 if (child_requisition.width >= area->width)
440 child_allocation.width = area->width;
441 else
443 child_allocation.width = child_requisition.width;
444 if (wbox->line_justify == GTK_JUSTIFY_FILL || child->hfill)
445 child_allocation.width = area->width;
446 else if (child->hexpand || wbox->line_justify == GTK_JUSTIFY_CENTER)
447 child_allocation.x += (area->width - child_requisition.width) / 2;
448 else if (wbox->line_justify == GTK_JUSTIFY_BOTTOM)
449 child_allocation.x += area->width - child_requisition.width;
452 if (have_expand_children)
454 child_allocation.height = child_requisition.height;
455 if (child->vexpand || wbox->justify == GTK_JUSTIFY_FILL)
457 guint space;
459 n_expand_children--;
460 space = extra * n_expand_children;
461 space = height - space;
462 height -= space;
463 if (child->vfill)
464 child_allocation.height += space;
465 else
467 child_allocation.y += space / 2;
468 y += space;
472 else
474 /* g_print ("child_allocation.y %d += %d * %f ",
475 child_allocation.y, n_children, extra); */
476 child_allocation.y += n_children * extra;
477 /* g_print ("= %d\n",
478 child_allocation.y); */
479 child_allocation.height = MIN (child_requisition.height,
480 area->height - child_allocation.y + area->y);
484 y += child_allocation.height + wbox->vspacing;
485 gtk_widget_size_allocate (child->widget, &child_allocation);
486 n_children++;
490 typedef struct _Line Line;
491 struct _Line
493 GSList *children;
494 guint16 min_size;
495 guint expand : 1;
496 Line *next;
499 static void
500 layout_cols (GtkWrapBox *wbox,
501 GtkAllocation *area)
503 GtkWrapBoxChild *next_child;
504 guint min_width;
505 gboolean hexpand;
506 GSList *slist;
507 Line *line_list = NULL;
508 guint total_width = 0, n_expand_lines = 0, n_lines = 0;
509 gfloat shrink_width;
510 guint children_per_line;
512 next_child = wbox->children;
513 slist = GTK_WRAP_BOX_GET_CLASS (wbox)->rlist_line_children (wbox,
514 &next_child,
515 area,
516 &min_width,
517 &hexpand);
518 slist = g_slist_reverse (slist);
520 children_per_line = g_slist_length (slist);
521 while (slist)
523 Line *line = g_new (Line, 1);
525 line->children = slist;
526 line->min_size = min_width;
527 total_width += min_width;
528 line->expand = hexpand;
529 if (hexpand)
530 n_expand_lines++;
531 line->next = line_list;
532 line_list = line;
533 n_lines++;
535 slist = GTK_WRAP_BOX_GET_CLASS (wbox)->rlist_line_children (wbox,
536 &next_child,
537 area,
538 &min_width,
539 &hexpand);
540 slist = g_slist_reverse (slist);
543 if (total_width > area->width)
544 shrink_width = total_width - area->width;
545 else
546 shrink_width = 0;
548 if (1) /* reverse lines and shrink */
550 Line *prev = NULL, *last = NULL;
551 gfloat n_shrink_lines = n_lines;
553 while (line_list)
555 Line *tmp = line_list->next;
557 if (shrink_width)
559 Line *line = line_list;
560 guint shrink_fract = shrink_width / n_shrink_lines + 0.5;
562 if (line->min_size > shrink_fract)
564 shrink_width -= shrink_fract;
565 line->min_size -= shrink_fract;
567 else
569 shrink_width -= line->min_size - 1;
570 line->min_size = 1;
573 n_shrink_lines--;
575 last = line_list;
576 line_list->next = prev;
577 prev = line_list;
578 line_list = tmp;
580 line_list = last;
583 if (n_lines)
585 Line *line;
586 gfloat x, width, extra = 0;
588 width = area->width;
589 width = MAX (n_lines, width - (n_lines - 1) * wbox->hspacing);
591 if (wbox->homogeneous)
592 width /= ((gdouble) n_lines);
593 else if (n_expand_lines)
595 width = MAX (0, width - total_width);
596 extra = width / ((gdouble) n_expand_lines);
598 else
599 width = 0;
601 x = area->x;
602 line = line_list;
603 while (line)
605 GtkAllocation col_allocation;
606 Line *next_line = line->next;
608 col_allocation.y = area->y;
609 col_allocation.height = area->height;
610 if (wbox->homogeneous)
611 col_allocation.width = width;
612 else
614 col_allocation.width = line->min_size;
616 if (line->expand)
617 col_allocation.width += extra;
620 col_allocation.x = x;
622 x += col_allocation.width + wbox->hspacing;
623 layout_col (wbox,
624 &col_allocation,
625 line->children,
626 children_per_line,
627 line->expand);
629 g_slist_free (line->children);
630 g_free (line);
631 line = next_line;
636 static void
637 gtk_vwrap_box_size_allocate (GtkWidget *widget,
638 GtkAllocation *allocation)
640 GtkWrapBox *wbox = GTK_WRAP_BOX (widget);
641 GtkAllocation area;
642 guint border = GTK_CONTAINER (wbox)->border_width; /*<h2v-skip>*/
644 widget->allocation = *allocation;
645 area.y = allocation->y + border;
646 area.x = allocation->x + border;
647 area.height = MAX (1, (gint) allocation->height - border * 2);
648 area.width = MAX (1, (gint) allocation->width - border * 2);
650 /*<h2v-off>*/
651 /* g_print ("got: width %d, height %d\n",
652 allocation->width,
653 allocation->height);
655 /*<h2v-on>*/
657 layout_cols (wbox, &area);