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.
24 #include "gtkvwrapbox.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
,
39 gboolean
*can_hexpand
);
42 /* --- variables --- */
43 static gpointer parent_class
= NULL
;
46 /* --- functions --- */
48 gtk_vwrap_box_get_type (void)
50 static GtkType vwrap_box_type
= 0;
54 static const GtkTypeInfo vwrap_box_info
=
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
;
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
;
94 gtk_vwrap_box_init (GtkVWrapBox
*vwbox
)
96 vwbox
->max_child_height
= 0;
97 vwbox
->max_child_width
= 0;
101 gtk_vwrap_box_new (gboolean homogeneous
)
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
);
113 get_child_requisition (GtkWrapBox
*wbox
,
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
;
125 gtk_widget_get_child_requisition (child
, child_requisition
);
128 #if THIS_IS_PROBABLY_DEAD_CODE
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
;
138 g_return_if_fail (requisition
!= NULL
);
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
;
162 requisition
->width
= sqrt (area
* wbox
->aspect_ratio
);
163 requisition
->height
= area
/ requisition
->width
;
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;
178 get_layout_size (GtkVWrapBox
*this,
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;
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
))
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
)
211 col_height
+= wbox
->vspacing
+ child_requisition
.height
;
212 col_width
= MAX (col_width
, child_requisition
.width
);
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
;
223 if (*height_inc
> this->max_child_height
)
226 return MAX (total_width
, 1);
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;
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 */
260 layout_height
= this->max_child_height
;
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
)
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,
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",
294 reverse_list_col_children (GtkWrapBox
*wbox
,
295 GtkWrapBoxChild
**child_p
,
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
;
305 *expand_line
= FALSE
;
307 while (child
&& !GTK_WIDGET_VISIBLE (child
->widget
))
309 *child_p
= child
->next
;
315 GtkRequisition child_requisition
;
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
;
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
||
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
);
340 *child_p
= child
->next
;
349 layout_col (GtkWrapBox
*wbox
,
352 guint children_per_line
,
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
;
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
;
378 have_expand_children
= n_expand_children
&& extra
;
381 if (wbox
->homogeneous
)
383 height
= MAX (1, area
->height
- (children_per_line
- 1) * wbox
->vspacing
);
384 height
/= ((gdouble
) children_per_line
);
387 else if (have_expand_children
&& wbox
->justify
!= GTK_JUSTIFY_FILL
)
390 extra
/= ((gdouble
) n_expand_children
);
394 if (wbox
->justify
== GTK_JUSTIFY_FILL
)
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
)
407 else if (wbox
->justify
== GTK_JUSTIFY_LEFT
)
412 else if (wbox
->justify
== GTK_JUSTIFY_RIGHT
)
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
;
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
;
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
)
460 space
= extra
* n_expand_children
;
461 space
= height
- space
;
464 child_allocation
.height
+= space
;
467 child_allocation
.y
+= space
/ 2;
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
);
490 typedef struct _Line Line
;
500 layout_cols (GtkWrapBox
*wbox
,
503 GtkWrapBoxChild
*next_child
;
507 Line
*line_list
= NULL
;
508 guint total_width
= 0, n_expand_lines
= 0, n_lines
= 0;
510 guint children_per_line
;
512 next_child
= wbox
->children
;
513 slist
= GTK_WRAP_BOX_GET_CLASS (wbox
)->rlist_line_children (wbox
,
518 slist
= g_slist_reverse (slist
);
520 children_per_line
= g_slist_length (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
;
531 line
->next
= line_list
;
535 slist
= GTK_WRAP_BOX_GET_CLASS (wbox
)->rlist_line_children (wbox
,
540 slist
= g_slist_reverse (slist
);
543 if (total_width
> area
->width
)
544 shrink_width
= total_width
- area
->width
;
548 if (1) /* reverse lines and shrink */
550 Line
*prev
= NULL
, *last
= NULL
;
551 gfloat n_shrink_lines
= n_lines
;
555 Line
*tmp
= line_list
->next
;
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
;
569 shrink_width
-= line
->min_size
- 1;
576 line_list
->next
= prev
;
586 gfloat x
, width
, extra
= 0;
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
);
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
;
614 col_allocation
.width
= line
->min_size
;
617 col_allocation
.width
+= extra
;
620 col_allocation
.x
= x
;
622 x
+= col_allocation
.width
+ wbox
->hspacing
;
629 g_slist_free (line
->children
);
637 gtk_vwrap_box_size_allocate (GtkWidget
*widget
,
638 GtkAllocation
*allocation
)
640 GtkWrapBox
*wbox
= GTK_WRAP_BOX (widget
);
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);
651 /* g_print ("got: width %d, height %d\n",
657 layout_cols (wbox
, &area
);