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"
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
,
37 gboolean
*can_hexpand
);
40 /* --- variables --- */
41 static gpointer parent_class
= NULL
;
44 /* --- functions --- */
46 gtk_vwrap_box_get_type (void)
48 static GtkType vwrap_box_type
= 0;
52 static const GtkTypeInfo vwrap_box_info
=
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
;
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
;
92 gtk_vwrap_box_init (GtkVWrapBox
*vwbox
)
94 vwbox
->max_child_height
= 0;
95 vwbox
->max_child_width
= 0;
99 gtk_vwrap_box_new (gboolean homogeneous
)
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
);
111 get_child_requisition (GtkWrapBox
*wbox
,
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
;
123 gtk_widget_get_child_requisition (child
, child_requisition
);
127 get_layout_size (GtkVWrapBox
*this,
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;
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
))
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
)
160 col_height
+= wbox
->vspacing
+ child_requisition
.height
;
161 col_width
= MAX (col_width
, child_requisition
.width
);
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
;
172 if (*height_inc
> this->max_child_height
)
175 return MAX (total_width
, 1);
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;
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 */
209 layout_height
= this->max_child_height
;
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
)
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,
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",
243 reverse_list_col_children (GtkWrapBox
*wbox
,
244 GtkWrapBoxChild
**child_p
,
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
;
254 *expand_line
= FALSE
;
256 while (child
&& !GTK_WIDGET_VISIBLE (child
->widget
))
258 *child_p
= child
->next
;
264 GtkRequisition child_requisition
;
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
;
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
||
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
);
289 *child_p
= child
->next
;
298 layout_col (GtkWrapBox
*wbox
,
301 guint children_per_line
,
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
;
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
;
328 have_expand_children
= n_expand_children
&& extra
;
331 if (wbox
->homogeneous
)
333 height
= MAX (1, area
->height
- (children_per_line
- 1) * wbox
->vspacing
);
334 height
/= ((gdouble
) children_per_line
);
337 else if (have_expand_children
&& wbox
->justify
!= GTK_JUSTIFY_FILL
)
340 extra
/= ((gdouble
) n_expand_children
);
344 if (wbox
->justify
== GTK_JUSTIFY_FILL
)
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
)
357 else if (wbox
->justify
== GTK_JUSTIFY_LEFT
)
362 else if (wbox
->justify
== GTK_JUSTIFY_RIGHT
)
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
;
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
;
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
)
410 space
= extra
* n_expand_children
;
411 space
= height
- space
;
414 child_allocation
.height
+= space
;
417 child_allocation
.y
+= space
/ 2;
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
);
440 typedef struct _Line Line
;
450 layout_cols (GtkWrapBox
*wbox
,
453 GtkWrapBoxChild
*next_child
;
457 Line
*line_list
= NULL
;
458 guint total_width
= 0, n_expand_lines
= 0, n_lines
= 0;
460 guint children_per_line
;
462 next_child
= wbox
->children
;
463 slist
= GTK_WRAP_BOX_GET_CLASS (wbox
)->rlist_line_children (wbox
,
468 slist
= g_slist_reverse (slist
);
470 children_per_line
= g_slist_length (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
;
481 line
->next
= line_list
;
485 slist
= GTK_WRAP_BOX_GET_CLASS (wbox
)->rlist_line_children (wbox
,
490 slist
= g_slist_reverse (slist
);
493 if (total_width
> area
->width
)
494 shrink_width
= total_width
- area
->width
;
498 if (1) /* reverse lines and shrink */
500 Line
*prev
= NULL
, *last
= NULL
;
501 gfloat n_shrink_lines
= n_lines
;
505 Line
*tmp
= line_list
->next
;
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
;
519 shrink_width
-= line
->min_size
- 1;
526 line_list
->next
= prev
;
536 gfloat x
, width
, extra
= 0;
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
);
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
;
564 col_allocation
.width
= line
->min_size
;
567 col_allocation
.width
+= extra
;
570 col_allocation
.x
= x
;
572 x
+= col_allocation
.width
+ wbox
->hspacing
;
579 g_slist_free (line
->children
);
587 gtk_vwrap_box_size_allocate (GtkWidget
*widget
,
588 GtkAllocation
*allocation
)
590 GtkWrapBox
*wbox
= GTK_WRAP_BOX (widget
);
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);
601 /* g_print ("got: width %d, height %d\n",
607 layout_cols (wbox
, &area
);