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"
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
,
38 gboolean
*can_vexpand
);
41 /* --- variables --- */
42 static gpointer parent_class
= NULL
;
45 /* --- functions --- */
47 gtk_hwrap_box_get_type (void)
49 static GtkType hwrap_box_type
= 0;
53 static const GtkTypeInfo hwrap_box_info
=
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
;
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
;
93 gtk_hwrap_box_init (GtkHWrapBox
*hwbox
)
95 hwbox
->max_child_width
= 0;
96 hwbox
->max_child_height
= 0;
100 gtk_hwrap_box_new (gboolean homogeneous
)
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
);
112 get_child_requisition (GtkWrapBox
*wbox
,
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
;
124 gtk_widget_get_child_requisition (child
, child_requisition
);
128 get_layout_size (GtkHWrapBox
*this,
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;
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
))
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
)
161 row_width
+= wbox
->hspacing
+ child_requisition
.width
;
162 row_height
= MAX (row_height
, child_requisition
.height
);
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
;
173 if (*width_inc
> this->max_child_width
)
176 return MAX (total_height
, 1);
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;
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 */
210 layout_width
= this->max_child_width
;
213 gfloat layout_height
;
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
)
223 requisition
->width
= layout_width
;
224 requisition
->height
= layout_height
;
227 /* g_print ("ratio for width %d height %d = %f\n",
229 (gint) layout_height,
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",
239 requisition->height);
244 reverse_list_row_children (GtkWrapBox
*wbox
,
245 GtkWrapBoxChild
**child_p
,
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
;
255 *expand_line
= FALSE
;
257 while (child
&& !GTK_WIDGET_VISIBLE (child
->widget
))
259 *child_p
= child
->next
;
265 GtkRequisition child_requisition
;
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
;
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
||
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
);
290 *child_p
= child
->next
;
299 layout_row (GtkWrapBox
*wbox
,
302 guint children_per_line
,
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
;
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
;
329 have_expand_children
= n_expand_children
&& extra
;
332 if (wbox
->homogeneous
)
334 width
= MAX (1, area
->width
- (children_per_line
- 1) * wbox
->hspacing
);
335 width
/= ((gdouble
) children_per_line
);
338 else if (have_expand_children
&& wbox
->justify
!= GTK_JUSTIFY_FILL
)
341 extra
/= ((gdouble
) n_expand_children
);
345 if (wbox
->justify
== GTK_JUSTIFY_FILL
)
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
)
358 else if (wbox
->justify
== GTK_JUSTIFY_LEFT
)
363 else if (wbox
->justify
== GTK_JUSTIFY_RIGHT
)
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
;
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
;
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
)
411 space
= extra
* n_expand_children
;
412 space
= width
- space
;
415 child_allocation
.width
+= space
;
418 child_allocation
.x
+= space
/ 2;
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
);
441 typedef struct _Line Line
;
451 layout_rows (GtkWrapBox
*wbox
,
454 GtkWrapBoxChild
*next_child
;
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
,
469 slist
= g_slist_reverse (slist
);
471 children_per_line
= g_slist_length (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
;
482 line
->next
= line_list
;
486 slist
= GTK_WRAP_BOX_GET_CLASS (wbox
)->rlist_line_children (wbox
,
491 slist
= g_slist_reverse (slist
);
494 if (total_height
> area
->height
)
495 shrink_height
= total_height
- area
->height
;
499 if (1) /* reverse lines and shrink */
501 Line
*prev
= NULL
, *last
= NULL
;
502 gfloat n_shrink_lines
= n_lines
;
506 Line
*tmp
= line_list
->next
;
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
;
520 shrink_height
-= line
->min_size
- 1;
527 line_list
->next
= prev
;
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
);
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
;
565 row_allocation
.height
= line
->min_size
;
568 row_allocation
.height
+= extra
;
571 row_allocation
.y
= y
;
573 y
+= row_allocation
.height
+ wbox
->vspacing
;
580 g_slist_free (line
->children
);
588 gtk_hwrap_box_size_allocate (GtkWidget
*widget
,
589 GtkAllocation
*allocation
)
591 GtkWrapBox
*wbox
= GTK_WRAP_BOX (widget
);
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);
602 /* g_print ("got: width %d, height %d\n",
608 layout_rows (wbox
, &area
);