1 /* gui_box.c - GUI container that stack components. */
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2008,2009 Free Software Foundation, Inc.
6 * GRUB is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * GRUB is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
21 #include <grub/misc.h>
23 #include <grub/gui_string_util.h>
27 grub_gui_component_t component
;
28 struct component_node
*next
;
29 struct component_node
*prev
;
32 typedef struct grub_gui_box
*grub_gui_box_t
;
34 typedef void (*layout_func_t
) (grub_gui_box_t self
, int modify_layout
,
35 unsigned *minimal_width
,
36 unsigned *minimal_height
);
40 struct grub_gui_container container
;
42 grub_gui_container_t parent
;
43 grub_video_rect_t bounds
;
46 /* Doubly linked list of components with dummy head & tail nodes. */
47 struct component_node chead
;
48 struct component_node ctail
;
50 /* The layout function: differs for vertical and horizontal boxes. */
51 layout_func_t layout_func
;
55 box_destroy (void *vself
)
57 grub_gui_box_t self
= vself
;
58 struct component_node
*cur
;
59 struct component_node
*next
;
60 for (cur
= self
->chead
.next
; cur
!= &self
->ctail
; cur
= next
)
62 /* Copy the 'next' pointer, since we need it for the next iteration,
63 and we're going to free the memory it is stored in. */
65 /* Destroy the child component. */
66 cur
->component
->ops
->destroy (cur
->component
);
67 /* Free the linked list node. */
74 box_get_id (void *vself
)
76 grub_gui_box_t self
= vself
;
81 box_is_instance (void *vself
__attribute__((unused
)), const char *type
)
83 return (grub_strcmp (type
, "component") == 0
84 || grub_strcmp (type
, "container") == 0);
88 layout_horizontally (grub_gui_box_t self
, int modify_layout
,
89 unsigned *min_width
, unsigned *min_height
)
91 /* Start at the left (chead) and set the x coordinates as we go right. */
92 /* All components have their width set to the box's width. */
94 struct component_node
*cur
;
95 unsigned w
= 0, mwfrac
= 0, h
= 0, x
= 0;
96 grub_fixed_signed_t wfrac
= 0;
99 for (cur
= self
->chead
.next
; cur
!= &self
->ctail
; cur
= cur
->next
)
101 grub_gui_component_t c
= cur
->component
;
102 unsigned mw
= 0, mh
= 0;
104 if (c
->ops
->get_minimal_size
)
105 c
->ops
->get_minimal_size (c
, &mw
, &mh
);
107 if (c
->h
> (signed) h
)
116 if (wfrac
> GRUB_FIXED_1
|| (w
> 0 && wfrac
== GRUB_FIXED_1
))
121 if (wfrac
< GRUB_FIXED_1
)
122 *min_width
= grub_fixed_sfs_divide (w
, GRUB_FIXED_1
- wfrac
);
125 if (*min_width
< w
+ mwfrac
)
126 *min_width
= w
+ mwfrac
;
134 for (cur
= self
->chead
.next
; cur
!= &self
->ctail
; cur
= cur
->next
)
137 grub_gui_component_t c
= cur
->component
;
138 unsigned mw
= 0, mh
= 0;
144 if (c
->ops
->get_minimal_size
)
145 c
->ops
->get_minimal_size (c
, &mw
, &mh
);
149 r
.width
+= grub_fixed_sfs_multiply (self
->bounds
.width
, c
->wfrac
);
154 c
->ops
->set_bounds (c
, &r
);
161 layout_vertically (grub_gui_box_t self
, int modify_layout
,
162 unsigned *min_width
, unsigned *min_height
)
164 /* Start at the top (chead) and set the y coordinates as we go rdown. */
165 /* All components have their height set to the box's height. */
167 struct component_node
*cur
;
168 unsigned h
= 0, mhfrac
= 0, w
= 0, y
= 0;
169 grub_fixed_signed_t hfrac
= 0;
172 for (cur
= self
->chead
.next
; cur
!= &self
->ctail
; cur
= cur
->next
)
174 grub_gui_component_t c
= cur
->component
;
175 unsigned mw
= 0, mh
= 0;
177 if (c
->ops
->get_minimal_size
)
178 c
->ops
->get_minimal_size (c
, &mw
, &mh
);
180 if (c
->w
> (signed) w
)
189 if (hfrac
> GRUB_FIXED_1
|| (h
> 0 && hfrac
== GRUB_FIXED_1
))
194 if (hfrac
< GRUB_FIXED_1
)
195 *min_height
= grub_fixed_sfs_divide (h
, GRUB_FIXED_1
- hfrac
);
198 if (*min_height
< h
+ mhfrac
)
199 *min_height
= h
+ mhfrac
;
207 for (cur
= self
->chead
.next
; cur
!= &self
->ctail
; cur
= cur
->next
)
210 grub_gui_component_t c
= cur
->component
;
211 unsigned mw
= 0, mh
= 0;
217 if (c
->ops
->get_minimal_size
)
218 c
->ops
->get_minimal_size (c
, &mw
, &mh
);
222 r
.height
+= grub_fixed_sfs_multiply (self
->bounds
.height
, c
->hfrac
);
227 c
->ops
->set_bounds (c
, &r
);
234 box_paint (void *vself
, const grub_video_rect_t
*region
)
236 grub_gui_box_t self
= vself
;
237 struct component_node
*cur
;
238 grub_video_rect_t vpsave
;
240 grub_gui_set_viewport (&self
->bounds
, &vpsave
);
241 for (cur
= self
->chead
.next
; cur
!= &self
->ctail
; cur
= cur
->next
)
243 grub_gui_component_t comp
= cur
->component
;
244 comp
->ops
->paint (comp
, region
);
246 grub_gui_restore_viewport (&vpsave
);
250 box_set_parent (void *vself
, grub_gui_container_t parent
)
252 grub_gui_box_t self
= vself
;
253 self
->parent
= parent
;
256 static grub_gui_container_t
257 box_get_parent (void *vself
)
259 grub_gui_box_t self
= vself
;
264 box_set_bounds (void *vself
, const grub_video_rect_t
*bounds
)
266 grub_gui_box_t self
= vself
;
267 self
->bounds
= *bounds
;
268 self
->layout_func (self
, 1, 0, 0); /* Relayout the children. */
272 box_get_bounds (void *vself
, grub_video_rect_t
*bounds
)
274 grub_gui_box_t self
= vself
;
275 *bounds
= self
->bounds
;
278 /* The box's preferred size is based on the preferred sizes
281 box_get_minimal_size (void *vself
, unsigned *width
, unsigned *height
)
283 grub_gui_box_t self
= vself
;
284 self
->layout_func (self
, 0, width
, height
); /* Just calculate the size. */
288 box_set_property (void *vself
, const char *name
, const char *value
)
290 grub_gui_box_t self
= vself
;
291 if (grub_strcmp (name
, "id") == 0)
293 grub_free (self
->id
);
296 self
->id
= grub_strdup (value
);
308 box_add (void *vself
, grub_gui_component_t comp
)
310 grub_gui_box_t self
= vself
;
311 struct component_node
*node
;
312 node
= grub_malloc (sizeof (*node
));
314 return; /* Note: probably should handle the error. */
315 node
->component
= comp
;
316 /* Insert the node before the tail. */
317 node
->prev
= self
->ctail
.prev
;
318 node
->prev
->next
= node
;
319 node
->next
= &self
->ctail
;
320 node
->next
->prev
= node
;
322 comp
->ops
->set_parent (comp
, (grub_gui_container_t
) self
);
323 self
->layout_func (self
, 1, 0, 0); /* Relayout the children. */
327 box_remove (void *vself
, grub_gui_component_t comp
)
329 grub_gui_box_t self
= vself
;
330 struct component_node
*cur
;
331 for (cur
= self
->chead
.next
; cur
!= &self
->ctail
; cur
= cur
->next
)
333 if (cur
->component
== comp
)
335 /* Unlink 'cur' from the list. */
336 cur
->prev
->next
= cur
->next
;
337 cur
->next
->prev
= cur
->prev
;
338 /* Free the node's memory (but don't destroy the component). */
340 /* Must not loop again, since 'cur' would be dereferenced! */
347 box_iterate_children (void *vself
,
348 grub_gui_component_callback cb
, void *userdata
)
350 grub_gui_box_t self
= vself
;
351 struct component_node
*cur
;
352 for (cur
= self
->chead
.next
; cur
!= &self
->ctail
; cur
= cur
->next
)
353 cb (cur
->component
, userdata
);
356 static struct grub_gui_component_ops box_comp_ops
=
358 .destroy
= box_destroy
,
359 .get_id
= box_get_id
,
360 .is_instance
= box_is_instance
,
362 .set_parent
= box_set_parent
,
363 .get_parent
= box_get_parent
,
364 .set_bounds
= box_set_bounds
,
365 .get_bounds
= box_get_bounds
,
366 .get_minimal_size
= box_get_minimal_size
,
367 .set_property
= box_set_property
370 static struct grub_gui_container_ops box_ops
=
373 .remove
= box_remove
,
374 .iterate_children
= box_iterate_children
377 /* Box constructor. Specify the appropriate layout function to create
378 a horizontal or vertical stacking box. */
379 static grub_gui_box_t
380 box_new (layout_func_t layout_func
)
383 box
= grub_zalloc (sizeof (*box
));
386 box
->container
.ops
= &box_ops
;
387 box
->container
.component
.ops
= &box_comp_ops
;
388 box
->chead
.next
= &box
->ctail
;
389 box
->ctail
.prev
= &box
->chead
;
390 box
->layout_func
= layout_func
;
394 /* Create a new container that stacks its child components horizontally,
395 from left to right. Each child get a width corresponding to its
396 preferred width. The height of each child is set the maximum of the
397 preferred heights of all children. */
399 grub_gui_hbox_new (void)
401 return (grub_gui_container_t
) box_new (layout_horizontally
);
404 /* Create a new container that stacks its child components verticallyj,
405 from top to bottom. Each child get a height corresponding to its
406 preferred height. The width of each child is set the maximum of the
407 preferred widths of all children. */
409 grub_gui_vbox_new (void)
411 return (grub_gui_container_t
) box_new (layout_vertically
);