summaryrefslogtreecommitdiffstats
path: root/grub-core/gfxmenu
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 10:54:16 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 10:54:16 +0000
commit485f6ecd453d8a2fd8b9b9fadea03159d8b50797 (patch)
tree32451fa3cdd9321fb2591fada9891b2cb70a9cd1 /grub-core/gfxmenu
parentInitial commit. (diff)
downloadgrub2-485f6ecd453d8a2fd8b9b9fadea03159d8b50797.tar.xz
grub2-485f6ecd453d8a2fd8b9b9fadea03159d8b50797.zip
Adding upstream version 2.06.upstream/2.06upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'grub-core/gfxmenu')
-rw-r--r--grub-core/gfxmenu/font.c116
-rw-r--r--grub-core/gfxmenu/gfxmenu.c150
-rw-r--r--grub-core/gfxmenu/gui_box.c428
-rw-r--r--grub-core/gfxmenu/gui_canvas.c278
-rw-r--r--grub-core/gfxmenu/gui_circular_progress.c331
-rw-r--r--grub-core/gfxmenu/gui_image.c270
-rw-r--r--grub-core/gfxmenu/gui_label.c277
-rw-r--r--grub-core/gfxmenu/gui_list.c953
-rw-r--r--grub-core/gfxmenu/gui_progress_bar.c457
-rw-r--r--grub-core/gfxmenu/gui_string_util.c206
-rw-r--r--grub-core/gfxmenu/gui_util.c101
-rw-r--r--grub-core/gfxmenu/icon_manager.c257
-rw-r--r--grub-core/gfxmenu/theme_loader.c828
-rw-r--r--grub-core/gfxmenu/view.c647
-rw-r--r--grub-core/gfxmenu/widget-box.c360
15 files changed, 5659 insertions, 0 deletions
diff --git a/grub-core/gfxmenu/font.c b/grub-core/gfxmenu/font.c
new file mode 100644
index 0000000..756c24f
--- /dev/null
+++ b/grub-core/gfxmenu/font.c
@@ -0,0 +1,116 @@
+/* font.c - Font API and font file loader. */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2003,2005,2006,2007,2008,2009,2010 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/bufio.h>
+#include <grub/dl.h>
+#include <grub/file.h>
+#include <grub/font.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/types.h>
+#include <grub/video.h>
+#include <grub/bitmap.h>
+#include <grub/charset.h>
+#include <grub/unicode.h>
+#include <grub/fontformat.h>
+#include <grub/gfxmenu_view.h>
+
+/* Draw a UTF-8 string of text on the current video render target.
+ The x coordinate specifies the starting x position for the first character,
+ while the y coordinate specifies the baseline position.
+ If the string contains a character that FONT does not contain, then
+ a glyph from another loaded font may be used instead. */
+grub_err_t
+grub_font_draw_string (const char *str, grub_font_t font,
+ grub_video_color_t color,
+ int left_x, int baseline_y)
+{
+ int x;
+ grub_uint32_t *logical;
+ grub_ssize_t logical_len, visual_len;
+ struct grub_unicode_glyph *visual, *ptr;
+ grub_err_t err;
+
+ logical_len = grub_utf8_to_ucs4_alloc (str, &logical, 0);
+ if (logical_len < 0)
+ return grub_errno;
+
+ visual_len = grub_bidi_logical_to_visual (logical, logical_len, &visual,
+ 0, 0, 0, 0, 0, 0, 0);
+ grub_free (logical);
+ if (visual_len < 0)
+ return grub_errno;
+
+ err = GRUB_ERR_NONE;
+ for (ptr = visual, x = left_x; ptr < visual + visual_len; ptr++)
+ {
+ struct grub_font_glyph *glyph;
+ glyph = grub_font_construct_glyph (font, ptr);
+ if (!glyph)
+ {
+ err = grub_errno;
+ goto out;
+ }
+ err = grub_font_draw_glyph (glyph, color, x, baseline_y);
+ if (err)
+ goto out;
+ x += glyph->device_width;
+ }
+
+out:
+ for (ptr = visual; ptr < visual + visual_len; ptr++)
+ grub_unicode_destroy_glyph (ptr);
+ grub_free (visual);
+
+ return err;
+}
+
+/* Get the width in pixels of the specified UTF-8 string, when rendered in
+ in the specified font (but falling back on other fonts for glyphs that
+ are missing). */
+int
+grub_font_get_string_width (grub_font_t font, const char *str)
+{
+ int width = 0;
+ grub_uint32_t *ptr;
+ grub_ssize_t logical_len;
+ grub_uint32_t *logical;
+
+ logical_len = grub_utf8_to_ucs4_alloc (str, &logical, 0);
+ if (logical_len < 0)
+ {
+ grub_errno = GRUB_ERR_NONE;
+ return 0;
+ }
+
+ for (ptr = logical; ptr < logical + logical_len;)
+ {
+ struct grub_unicode_glyph glyph;
+
+ ptr += grub_unicode_aglomerate_comb (ptr,
+ logical_len - (ptr - logical),
+ &glyph);
+ width += grub_font_get_constructed_device_width (font, &glyph);
+
+ grub_unicode_destroy_glyph (&glyph);
+ }
+ grub_free (logical);
+
+ return width;
+}
diff --git a/grub-core/gfxmenu/gfxmenu.c b/grub-core/gfxmenu/gfxmenu.c
new file mode 100644
index 0000000..8a17dda
--- /dev/null
+++ b/grub-core/gfxmenu/gfxmenu.c
@@ -0,0 +1,150 @@
+/* gfxmenu.c - Graphical menu interface controller. */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2008 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/types.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/err.h>
+#include <grub/dl.h>
+#include <grub/command.h>
+#include <grub/video.h>
+#include <grub/gfxterm.h>
+#include <grub/bitmap.h>
+#include <grub/bitmap_scale.h>
+#include <grub/term.h>
+#include <grub/env.h>
+#include <grub/normal.h>
+#include <grub/gfxwidgets.h>
+#include <grub/menu.h>
+#include <grub/menu_viewer.h>
+#include <grub/gfxmenu_model.h>
+#include <grub/gfxmenu_view.h>
+#include <grub/time.h>
+#include <grub/i18n.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+static grub_gfxmenu_view_t cached_view;
+
+static void
+grub_gfxmenu_viewer_fini (void *data __attribute__ ((unused)))
+{
+}
+
+/* FIXME: Previously 't' changed to text menu is it necessary? */
+static grub_err_t
+grub_gfxmenu_try (int entry, grub_menu_t menu, int nested)
+{
+ grub_gfxmenu_view_t view = NULL;
+ const char *theme_path;
+ char *full_theme_path = 0;
+ struct grub_menu_viewer *instance;
+ grub_err_t err;
+ struct grub_video_mode_info mode_info;
+
+ theme_path = grub_env_get ("theme");
+ if (! theme_path)
+ return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("variable `%s' isn't set"),
+ "theme");
+
+ err = grub_video_get_info (&mode_info);
+ if (err)
+ return err;
+
+ instance = grub_zalloc (sizeof (*instance));
+ if (!instance)
+ return grub_errno;
+
+ if (theme_path[0] != '/' && theme_path[0] != '(')
+ {
+ const char *prefix;
+ prefix = grub_env_get ("prefix");
+ full_theme_path = grub_xasprintf ("%s/themes/%s",
+ prefix,
+ theme_path);
+ }
+
+ if (!cached_view || grub_strcmp (cached_view->theme_path,
+ full_theme_path ? : theme_path) != 0
+ || cached_view->screen.width != mode_info.width
+ || cached_view->screen.height != mode_info.height)
+ {
+ grub_gfxmenu_view_destroy (cached_view);
+ /* Create the view. */
+ cached_view = grub_gfxmenu_view_new (full_theme_path ? : theme_path,
+ mode_info.width,
+ mode_info.height);
+ }
+ grub_free (full_theme_path);
+
+ if (! cached_view)
+ {
+ grub_free (instance);
+ return grub_errno;
+ }
+
+ view = cached_view;
+
+ view->double_repaint = (mode_info.mode_type
+ & GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED)
+ && !(mode_info.mode_type & GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP);
+ view->selected = entry;
+ view->menu = menu;
+ view->nested = nested;
+ view->first_timeout = -1;
+
+ grub_video_set_viewport (0, 0, mode_info.width, mode_info.height);
+ if (view->double_repaint)
+ {
+ grub_video_swap_buffers ();
+ grub_video_set_viewport (0, 0, mode_info.width, mode_info.height);
+ }
+
+ grub_gfxmenu_view_draw (view);
+
+ instance->data = view;
+ instance->set_chosen_entry = grub_gfxmenu_set_chosen_entry;
+ instance->fini = grub_gfxmenu_viewer_fini;
+ instance->print_timeout = grub_gfxmenu_print_timeout;
+ instance->clear_timeout = grub_gfxmenu_clear_timeout;
+
+ grub_menu_register_viewer (instance);
+
+ return GRUB_ERR_NONE;
+}
+
+GRUB_MOD_INIT (gfxmenu)
+{
+ struct grub_term_output *term;
+
+ FOR_ACTIVE_TERM_OUTPUTS(term)
+ if (grub_gfxmenu_try_hook && term->fullscreen)
+ {
+ term->fullscreen ();
+ break;
+ }
+
+ grub_gfxmenu_try_hook = grub_gfxmenu_try;
+}
+
+GRUB_MOD_FINI (gfxmenu)
+{
+ grub_gfxmenu_view_destroy (cached_view);
+ grub_gfxmenu_try_hook = NULL;
+}
diff --git a/grub-core/gfxmenu/gui_box.c b/grub-core/gfxmenu/gui_box.c
new file mode 100644
index 0000000..37bab3f
--- /dev/null
+++ b/grub-core/gfxmenu/gui_box.c
@@ -0,0 +1,428 @@
+/* gui_box.c - GUI container that stack components. */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2008,2009 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/gui.h>
+#include <grub/gui_string_util.h>
+
+struct component_node
+{
+ grub_gui_component_t component;
+ struct component_node *next;
+ struct component_node *prev;
+};
+
+typedef struct grub_gui_box *grub_gui_box_t;
+
+typedef void (*layout_func_t) (grub_gui_box_t self, int modify_layout,
+ unsigned *minimal_width,
+ unsigned *minimal_height);
+
+struct grub_gui_box
+{
+ struct grub_gui_container container;
+
+ grub_gui_container_t parent;
+ grub_video_rect_t bounds;
+ char *id;
+
+ /* Doubly linked list of components with dummy head & tail nodes. */
+ struct component_node chead;
+ struct component_node ctail;
+
+ /* The layout function: differs for vertical and horizontal boxes. */
+ layout_func_t layout_func;
+};
+
+static void
+box_destroy (void *vself)
+{
+ grub_gui_box_t self = vself;
+ struct component_node *cur;
+ struct component_node *next;
+ for (cur = self->chead.next; cur != &self->ctail; cur = next)
+ {
+ /* Copy the 'next' pointer, since we need it for the next iteration,
+ and we're going to free the memory it is stored in. */
+ next = cur->next;
+ /* Destroy the child component. */
+ cur->component->ops->destroy (cur->component);
+ /* Free the linked list node. */
+ grub_free (cur);
+ }
+ grub_free (self);
+}
+
+static const char *
+box_get_id (void *vself)
+{
+ grub_gui_box_t self = vself;
+ return self->id;
+}
+
+static int
+box_is_instance (void *vself __attribute__((unused)), const char *type)
+{
+ return (grub_strcmp (type, "component") == 0
+ || grub_strcmp (type, "container") == 0);
+}
+
+static void
+layout_horizontally (grub_gui_box_t self, int modify_layout,
+ unsigned *min_width, unsigned *min_height)
+{
+ /* Start at the left (chead) and set the x coordinates as we go right. */
+ /* All components have their width set to the box's width. */
+
+ struct component_node *cur;
+ unsigned w = 0, mwfrac = 0, h = 0, x = 0;
+ grub_fixed_signed_t wfrac = 0;
+ int bogus_frac = 0;
+
+ for (cur = self->chead.next; cur != &self->ctail; cur = cur->next)
+ {
+ grub_gui_component_t c = cur->component;
+ unsigned mw = 0, mh = 0;
+
+ if (c->ops->get_minimal_size)
+ c->ops->get_minimal_size (c, &mw, &mh);
+
+ if (c->h > (signed) h)
+ h = c->h;
+ if (mh > h)
+ h = mh;
+ wfrac += c->wfrac;
+ w += c->w;
+ if (mw - c->w > 0)
+ mwfrac += mw - c->w;
+ }
+ if (wfrac > GRUB_FIXED_1 || (w > 0 && wfrac == GRUB_FIXED_1))
+ bogus_frac = 1;
+
+ if (min_width)
+ {
+ if (wfrac < GRUB_FIXED_1)
+ *min_width = grub_fixed_sfs_divide (w, GRUB_FIXED_1 - wfrac);
+ else
+ *min_width = w;
+ if (*min_width < w + mwfrac)
+ *min_width = w + mwfrac;
+ }
+ if (min_height)
+ *min_height = h;
+
+ if (!modify_layout)
+ return;
+
+ for (cur = self->chead.next; cur != &self->ctail; cur = cur->next)
+ {
+ grub_video_rect_t r;
+ grub_gui_component_t c = cur->component;
+ unsigned mw = 0, mh = 0;
+
+ r.x = x;
+ r.y = 0;
+ r.height = h;
+
+ if (c->ops->get_minimal_size)
+ c->ops->get_minimal_size (c, &mw, &mh);
+
+ r.width = c->w;
+ if (!bogus_frac)
+ r.width += grub_fixed_sfs_multiply (self->bounds.width, c->wfrac);
+
+ if (r.width < mw)
+ r.width = mw;
+
+ c->ops->set_bounds (c, &r);
+
+ x += r.width;
+ }
+}
+
+static void
+layout_vertically (grub_gui_box_t self, int modify_layout,
+ unsigned *min_width, unsigned *min_height)
+{
+ /* Start at the top (chead) and set the y coordinates as we go rdown. */
+ /* All components have their height set to the box's height. */
+
+ struct component_node *cur;
+ unsigned h = 0, mhfrac = 0, w = 0, y = 0;
+ grub_fixed_signed_t hfrac = 0;
+ int bogus_frac = 0;
+
+ for (cur = self->chead.next; cur != &self->ctail; cur = cur->next)
+ {
+ grub_gui_component_t c = cur->component;
+ unsigned mw = 0, mh = 0;
+
+ if (c->ops->get_minimal_size)
+ c->ops->get_minimal_size (c, &mw, &mh);
+
+ if (c->w > (signed) w)
+ w = c->w;
+ if (mw > w)
+ w = mw;
+ hfrac += c->hfrac;
+ h += c->h;
+ if (mh - c->h > 0)
+ mhfrac += mh - c->h;
+ }
+ if (hfrac > GRUB_FIXED_1 || (h > 0 && hfrac == GRUB_FIXED_1))
+ bogus_frac = 1;
+
+ if (min_height)
+ {
+ if (hfrac < GRUB_FIXED_1)
+ *min_height = grub_fixed_sfs_divide (h, GRUB_FIXED_1 - hfrac);
+ else
+ *min_height = h;
+ if (*min_height < h + mhfrac)
+ *min_height = h + mhfrac;
+ }
+ if (min_width)
+ *min_width = w;
+
+ if (!modify_layout)
+ return;
+
+ for (cur = self->chead.next; cur != &self->ctail; cur = cur->next)
+ {
+ grub_video_rect_t r;
+ grub_gui_component_t c = cur->component;
+ unsigned mw = 0, mh = 0;
+
+ r.x = 0;
+ r.y = y;
+ r.width = w;
+
+ if (c->ops->get_minimal_size)
+ c->ops->get_minimal_size (c, &mw, &mh);
+
+ r.height = c->h;
+ if (!bogus_frac)
+ r.height += grub_fixed_sfs_multiply (self->bounds.height, c->hfrac);
+
+ if (r.height < mh)
+ r.height = mh;
+
+ c->ops->set_bounds (c, &r);
+
+ y += r.height;
+ }
+}
+
+static void
+box_paint (void *vself, const grub_video_rect_t *region)
+{
+ grub_gui_box_t self = vself;
+
+ struct component_node *cur;
+ grub_video_rect_t vpsave;
+
+ grub_video_area_status_t box_area_status;
+ grub_video_get_area_status (&box_area_status);
+
+ grub_gui_set_viewport (&self->bounds, &vpsave);
+ for (cur = self->chead.next; cur != &self->ctail; cur = cur->next)
+ {
+ grub_gui_component_t comp = cur->component;
+ grub_video_rect_t r;
+ comp->ops->get_bounds(comp, &r);
+
+ if (!grub_video_have_common_points (region, &r))
+ continue;
+
+ /* Paint the child. */
+ if (box_area_status == GRUB_VIDEO_AREA_ENABLED
+ && grub_video_bounds_inside_region (&r, region))
+ grub_video_set_area_status (GRUB_VIDEO_AREA_DISABLED);
+ comp->ops->paint (comp, region);
+ if (box_area_status == GRUB_VIDEO_AREA_ENABLED)
+ grub_video_set_area_status (GRUB_VIDEO_AREA_ENABLED);
+ }
+ grub_gui_restore_viewport (&vpsave);
+}
+
+static void
+box_set_parent (void *vself, grub_gui_container_t parent)
+{
+ grub_gui_box_t self = vself;
+ self->parent = parent;
+}
+
+static grub_gui_container_t
+box_get_parent (void *vself)
+{
+ grub_gui_box_t self = vself;
+ return self->parent;
+}
+
+static void
+box_set_bounds (void *vself, const grub_video_rect_t *bounds)
+{
+ grub_gui_box_t self = vself;
+ self->bounds = *bounds;
+ self->layout_func (self, 1, 0, 0); /* Relayout the children. */
+}
+
+static void
+box_get_bounds (void *vself, grub_video_rect_t *bounds)
+{
+ grub_gui_box_t self = vself;
+ *bounds = self->bounds;
+}
+
+/* The box's preferred size is based on the preferred sizes
+ of its children. */
+static void
+box_get_minimal_size (void *vself, unsigned *width, unsigned *height)
+{
+ grub_gui_box_t self = vself;
+ self->layout_func (self, 0, width, height); /* Just calculate the size. */
+}
+
+static grub_err_t
+box_set_property (void *vself, const char *name, const char *value)
+{
+ grub_gui_box_t self = vself;
+ if (grub_strcmp (name, "id") == 0)
+ {
+ grub_free (self->id);
+ if (value)
+ {
+ self->id = grub_strdup (value);
+ if (! self->id)
+ return grub_errno;
+ }
+ else
+ self->id = 0;
+ }
+
+ return grub_errno;
+}
+
+static void
+box_add (void *vself, grub_gui_component_t comp)
+{
+ grub_gui_box_t self = vself;
+ struct component_node *node;
+ node = grub_malloc (sizeof (*node));
+ if (! node)
+ return; /* Note: probably should handle the error. */
+ node->component = comp;
+ /* Insert the node before the tail. */
+ node->prev = self->ctail.prev;
+ node->prev->next = node;
+ node->next = &self->ctail;
+ node->next->prev = node;
+
+ comp->ops->set_parent (comp, (grub_gui_container_t) self);
+ self->layout_func (self, 1, 0, 0); /* Relayout the children. */
+}
+
+static void
+box_remove (void *vself, grub_gui_component_t comp)
+{
+ grub_gui_box_t self = vself;
+ struct component_node *cur;
+ for (cur = self->chead.next; cur != &self->ctail; cur = cur->next)
+ {
+ if (cur->component == comp)
+ {
+ /* Unlink 'cur' from the list. */
+ cur->prev->next = cur->next;
+ cur->next->prev = cur->prev;
+ /* Free the node's memory (but don't destroy the component). */
+ grub_free (cur);
+ /* Must not loop again, since 'cur' would be dereferenced! */
+ return;
+ }
+ }
+}
+
+static void
+box_iterate_children (void *vself,
+ grub_gui_component_callback cb, void *userdata)
+{
+ grub_gui_box_t self = vself;
+ struct component_node *cur;
+ for (cur = self->chead.next; cur != &self->ctail; cur = cur->next)
+ cb (cur->component, userdata);
+}
+
+static struct grub_gui_component_ops box_comp_ops =
+ {
+ .destroy = box_destroy,
+ .get_id = box_get_id,
+ .is_instance = box_is_instance,
+ .paint = box_paint,
+ .set_parent = box_set_parent,
+ .get_parent = box_get_parent,
+ .set_bounds = box_set_bounds,
+ .get_bounds = box_get_bounds,
+ .get_minimal_size = box_get_minimal_size,
+ .set_property = box_set_property
+ };
+
+static struct grub_gui_container_ops box_ops =
+{
+ .add = box_add,
+ .remove = box_remove,
+ .iterate_children = box_iterate_children
+};
+
+/* Box constructor. Specify the appropriate layout function to create
+ a horizontal or vertical stacking box. */
+static grub_gui_box_t
+box_new (layout_func_t layout_func)
+{
+ grub_gui_box_t box;
+ box = grub_zalloc (sizeof (*box));
+ if (! box)
+ return 0;
+ box->container.ops = &box_ops;
+ box->container.component.ops = &box_comp_ops;
+ box->chead.next = &box->ctail;
+ box->ctail.prev = &box->chead;
+ box->layout_func = layout_func;
+ return box;
+}
+
+/* Create a new container that stacks its child components horizontally,
+ from left to right. Each child get a width corresponding to its
+ preferred width. The height of each child is set the maximum of the
+ preferred heights of all children. */
+grub_gui_container_t
+grub_gui_hbox_new (void)
+{
+ return (grub_gui_container_t) box_new (layout_horizontally);
+}
+
+/* Create a new container that stacks its child components verticallyj,
+ from top to bottom. Each child get a height corresponding to its
+ preferred height. The width of each child is set the maximum of the
+ preferred widths of all children. */
+grub_gui_container_t
+grub_gui_vbox_new (void)
+{
+ return (grub_gui_container_t) box_new (layout_vertically);
+}
diff --git a/grub-core/gfxmenu/gui_canvas.c b/grub-core/gfxmenu/gui_canvas.c
new file mode 100644
index 0000000..a054912
--- /dev/null
+++ b/grub-core/gfxmenu/gui_canvas.c
@@ -0,0 +1,278 @@
+/* gui_canvas.c - GUI container allowing manually placed components. */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2008,2009 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/gui.h>
+#include <grub/gui_string_util.h>
+
+/* TODO Add layering so that components can be properly overlaid. */
+
+struct component_node
+{
+ grub_gui_component_t component;
+ struct component_node *next;
+};
+
+struct grub_gui_canvas
+{
+ struct grub_gui_container container;
+
+ grub_gui_container_t parent;
+ grub_video_rect_t bounds;
+ char *id;
+ /* Component list (dummy head node). */
+ struct component_node components;
+};
+
+typedef struct grub_gui_canvas *grub_gui_canvas_t;
+
+static void
+canvas_destroy (void *vself)
+{
+ grub_gui_canvas_t self = vself;
+ struct component_node *cur;
+ struct component_node *next;
+ for (cur = self->components.next; cur; cur = next)
+ {
+ /* Copy the 'next' pointer, since we need it for the next iteration,
+ and we're going to free the memory it is stored in. */
+ next = cur->next;
+ /* Destroy the child component. */
+ cur->component->ops->destroy (cur->component);
+ /* Free the linked list node. */
+ grub_free (cur);
+ }
+ grub_free (self);
+}
+
+static const char *
+canvas_get_id (void *vself)
+{
+ grub_gui_canvas_t self = vself;
+ return self->id;
+}
+
+static int
+canvas_is_instance (void *vself __attribute__((unused)), const char *type)
+{
+ return (grub_strcmp (type, "component") == 0
+ || grub_strcmp (type, "container") == 0);
+}
+
+static void
+canvas_paint (void *vself, const grub_video_rect_t *region)
+{
+ grub_gui_canvas_t self = vself;
+
+ struct component_node *cur;
+ grub_video_rect_t vpsave;
+
+ grub_video_area_status_t canvas_area_status;
+ grub_video_get_area_status (&canvas_area_status);
+
+ grub_gui_set_viewport (&self->bounds, &vpsave);
+ for (cur = self->components.next; cur; cur = cur->next)
+ {
+ grub_video_rect_t r;
+ grub_gui_component_t comp;
+ signed x, y, w, h;
+
+ comp = cur->component;
+
+ w = grub_fixed_sfs_multiply (self->bounds.width, comp->wfrac) + comp->w;
+ h = grub_fixed_sfs_multiply (self->bounds.height, comp->hfrac) + comp->h;
+ x = grub_fixed_sfs_multiply (self->bounds.width, comp->xfrac) + comp->x;
+ y = grub_fixed_sfs_multiply (self->bounds.height, comp->yfrac) + comp->y;
+
+ if (comp->ops->get_minimal_size)
+ {
+ unsigned mw;
+ unsigned mh;
+ comp->ops->get_minimal_size (comp, &mw, &mh);
+ if (w < (signed) mw)
+ w = mw;
+ if (h < (signed) mh)
+ h = mh;
+ }
+
+ /* Sanity checks. */
+ if (w <= 0)
+ w = 32;
+ if (h <= 0)
+ h = 32;
+
+ if (x >= (signed) self->bounds.width)
+ x = self->bounds.width - 32;
+ if (y >= (signed) self->bounds.height)
+ y = self->bounds.height - 32;
+
+ if (x < 0)
+ x = 0;
+ if (y < 0)
+ y = 0;
+
+ if (x + w >= (signed) self->bounds.width)
+ w = self->bounds.width - x;
+ if (y + h >= (signed) self->bounds.height)
+ h = self->bounds.height - y;
+
+ r.x = x;
+ r.y = y;
+ r.width = w;
+ r.height = h;
+ comp->ops->set_bounds (comp, &r);
+
+ if (!grub_video_have_common_points (region, &r))
+ continue;
+
+ /* Paint the child. */
+ if (canvas_area_status == GRUB_VIDEO_AREA_ENABLED
+ && grub_video_bounds_inside_region (&r, region))
+ grub_video_set_area_status (GRUB_VIDEO_AREA_DISABLED);
+ comp->ops->paint (comp, region);
+ if (canvas_area_status == GRUB_VIDEO_AREA_ENABLED)
+ grub_video_set_area_status (GRUB_VIDEO_AREA_ENABLED);
+ }
+ grub_gui_restore_viewport (&vpsave);
+}
+
+static void
+canvas_set_parent (void *vself, grub_gui_container_t parent)
+{
+ grub_gui_canvas_t self = vself;
+ self->parent = parent;
+}
+
+static grub_gui_container_t
+canvas_get_parent (void *vself)
+{
+ grub_gui_canvas_t self = vself;
+ return self->parent;
+}
+
+static void
+canvas_set_bounds (void *vself, const grub_video_rect_t *bounds)
+{
+ grub_gui_canvas_t self = vself;
+ self->bounds = *bounds;
+}
+
+static void
+canvas_get_bounds (void *vself, grub_video_rect_t *bounds)
+{
+ grub_gui_canvas_t self = vself;
+ *bounds = self->bounds;
+}
+
+static grub_err_t
+canvas_set_property (void *vself, const char *name, const char *value)
+{
+ grub_gui_canvas_t self = vself;
+ if (grub_strcmp (name, "id") == 0)
+ {
+ grub_free (self->id);
+ if (value)
+ {
+ self->id = grub_strdup (value);
+ if (! self->id)
+ return grub_errno;
+ }
+ else
+ self->id = 0;
+ }
+ return grub_errno;
+}
+
+static void
+canvas_add (void *vself, grub_gui_component_t comp)
+{
+ grub_gui_canvas_t self = vself;
+ struct component_node *node;
+ node = grub_malloc (sizeof (*node));
+ if (! node)
+ return; /* Note: probably should handle the error. */
+ node->component = comp;
+ node->next = self->components.next;
+ self->components.next = node;
+ comp->ops->set_parent (comp, (grub_gui_container_t) self);
+}
+
+static void
+canvas_remove (void *vself, grub_gui_component_t comp)
+{
+ grub_gui_canvas_t self = vself;
+ struct component_node *cur;
+ struct component_node *prev;
+ prev = &self->components;
+ for (cur = self->components.next; cur; prev = cur, cur = cur->next)
+ {
+ if (cur->component == comp)
+ {
+ /* Unlink 'cur' from the list. */
+ prev->next = cur->next;
+ /* Free the node's memory (but don't destroy the component). */
+ grub_free (cur);
+ /* Must not loop again, since 'cur' would be dereferenced! */
+ return;
+ }
+ }
+}
+
+static void
+canvas_iterate_children (void *vself,
+ grub_gui_component_callback cb, void *userdata)
+{
+ grub_gui_canvas_t self = vself;
+ struct component_node *cur;
+ for (cur = self->components.next; cur; cur = cur->next)
+ cb (cur->component, userdata);
+}
+
+static struct grub_gui_component_ops canvas_comp_ops =
+{
+ .destroy = canvas_destroy,
+ .get_id = canvas_get_id,
+ .is_instance = canvas_is_instance,
+ .paint = canvas_paint,
+ .set_parent = canvas_set_parent,
+ .get_parent = canvas_get_parent,
+ .set_bounds = canvas_set_bounds,
+ .get_bounds = canvas_get_bounds,
+ .set_property = canvas_set_property
+};
+
+static struct grub_gui_container_ops canvas_ops =
+{
+ .add = canvas_add,
+ .remove = canvas_remove,
+ .iterate_children = canvas_iterate_children
+};
+
+grub_gui_container_t
+grub_gui_canvas_new (void)
+{
+ grub_gui_canvas_t canvas;
+ canvas = grub_zalloc (sizeof (*canvas));
+ if (! canvas)
+ return 0;
+ canvas->container.ops = &canvas_ops;
+ canvas->container.component.ops = &canvas_comp_ops;
+ return (grub_gui_container_t) canvas;
+}
diff --git a/grub-core/gfxmenu/gui_circular_progress.c b/grub-core/gfxmenu/gui_circular_progress.c
new file mode 100644
index 0000000..7578bfb
--- /dev/null
+++ b/grub-core/gfxmenu/gui_circular_progress.c
@@ -0,0 +1,331 @@
+/* gui_circular_process.c - GUI circular progress indicator component. */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2008,2009 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/gui.h>
+#include <grub/font.h>
+#include <grub/gui_string_util.h>
+#include <grub/gfxmenu_view.h>
+#include <grub/gfxwidgets.h>
+#include <grub/trig.h>
+
+struct grub_gui_circular_progress
+{
+ struct grub_gui_progress progress;
+
+ grub_gui_container_t parent;
+ grub_video_rect_t bounds;
+ char *id;
+ int visible;
+ int start;
+ int end;
+ int value;
+ unsigned num_ticks;
+ int start_angle;
+ int ticks_disappear;
+ char *theme_dir;
+ int need_to_load_pixmaps;
+ char *center_file;
+ char *tick_file;
+ struct grub_video_bitmap *center_bitmap;
+ struct grub_video_bitmap *tick_bitmap;
+};
+
+typedef struct grub_gui_circular_progress *circular_progress_t;
+
+static void
+circprog_destroy (void *vself)
+{
+ circular_progress_t self = vself;
+ grub_gfxmenu_timeout_unregister ((grub_gui_component_t) self);
+ grub_free (self);
+}
+
+static const char *
+circprog_get_id (void *vself)
+{
+ circular_progress_t self = vself;
+ return self->id;
+}
+
+static int
+circprog_is_instance (void *vself __attribute__((unused)), const char *type)
+{
+ return grub_strcmp (type, "component") == 0;
+}
+
+static struct grub_video_bitmap *
+load_bitmap (const char *dir, const char *file)
+{
+ struct grub_video_bitmap *bitmap;
+ char *abspath;
+
+ /* Check arguments. */
+ if (! dir || ! file)
+ return 0;
+
+ /* Resolve to an absolute path. */
+ abspath = grub_resolve_relative_path (dir, file);
+ if (! abspath)
+ return 0;
+
+ /* Load the image. */
+ grub_errno = GRUB_ERR_NONE;
+ grub_video_bitmap_load (&bitmap, abspath);
+ grub_errno = GRUB_ERR_NONE;
+
+ grub_free (abspath);
+ return bitmap;
+}
+
+static int
+check_pixmaps (circular_progress_t self)
+{
+ if (self->need_to_load_pixmaps)
+ {
+ if (self->center_bitmap)
+ grub_video_bitmap_destroy (self->center_bitmap);
+ self->center_bitmap = load_bitmap (self->theme_dir, self->center_file);
+ self->tick_bitmap = load_bitmap (self->theme_dir, self->tick_file);
+ self->need_to_load_pixmaps = 0;
+ }
+
+ return (self->center_bitmap != 0 && self->tick_bitmap != 0);
+}
+
+static void
+circprog_paint (void *vself, const grub_video_rect_t *region)
+{
+ circular_progress_t self = vself;
+
+ if (! self->visible)
+ return;
+
+ if (!grub_video_have_common_points (region, &self->bounds))
+ return;
+
+ if (! check_pixmaps (self))
+ return;
+
+ grub_video_rect_t vpsave;
+ grub_gui_set_viewport (&self->bounds, &vpsave);
+
+ int width = self->bounds.width;
+ int height = self->bounds.height;
+ int center_width = grub_video_bitmap_get_width (self->center_bitmap);
+ int center_height = grub_video_bitmap_get_height (self->center_bitmap);
+ int tick_width = grub_video_bitmap_get_width (self->tick_bitmap);
+ int tick_height = grub_video_bitmap_get_height (self->tick_bitmap);
+ grub_video_blit_bitmap (self->center_bitmap, GRUB_VIDEO_BLIT_BLEND,
+ (width - center_width) / 2,
+ (height - center_height) / 2, 0, 0,
+ center_width, center_height);
+
+ if (self->num_ticks)
+ {
+ int radius = grub_min (height, width) / 2 - grub_max (tick_height, tick_width) / 2 - 1;
+ unsigned nticks;
+ unsigned tick_begin;
+ unsigned tick_end;
+ if (self->end <= self->start
+ || self->value <= self->start)
+ nticks = 0;
+ else
+ nticks = ((unsigned) (self->num_ticks
+ * (self->value - self->start)))
+ / ((unsigned) (self->end - self->start));
+ /* Do ticks appear or disappear as the value approached the end? */
+ if (self->ticks_disappear)
+ {
+ tick_begin = nticks;
+ tick_end = self->num_ticks;
+ }
+ else
+ {
+ tick_begin = 0;
+ tick_end = nticks;
+ }
+
+ unsigned i;
+ for (i = tick_begin; i < tick_end; i++)
+ {
+ int x;
+ int y;
+ int angle;
+
+ /* Calculate the location of the tick. */
+ angle = self->start_angle
+ + i * GRUB_TRIG_ANGLE_MAX / self->num_ticks;
+ x = width / 2 + (grub_cos (angle) * radius / GRUB_TRIG_FRACTION_SCALE);
+ y = height / 2 + (grub_sin (angle) * radius / GRUB_TRIG_FRACTION_SCALE);
+
+ /* Adjust (x,y) so the tick is centered. */
+ x -= tick_width / 2;
+ y -= tick_height / 2;
+
+ /* Draw the tick. */
+ grub_video_blit_bitmap (self->tick_bitmap, GRUB_VIDEO_BLIT_BLEND,
+ x, y, 0, 0, tick_width, tick_height);
+ }
+ }
+ grub_gui_restore_viewport (&vpsave);
+}
+
+static void
+circprog_set_parent (void *vself, grub_gui_container_t parent)
+{
+ circular_progress_t self = vself;
+ self->parent = parent;
+}
+
+static grub_gui_container_t
+circprog_get_parent (void *vself)
+{
+ circular_progress_t self = vself;
+ return self->parent;
+}
+
+static void
+circprog_set_bounds (void *vself, const grub_video_rect_t *bounds)
+{
+ circular_progress_t self = vself;
+ self->bounds = *bounds;
+}
+
+static void
+circprog_get_bounds (void *vself, grub_video_rect_t *bounds)
+{
+ circular_progress_t self = vself;
+ *bounds = self->bounds;
+}
+
+static void
+circprog_set_state (void *vself, int visible, int start,
+ int current, int end)
+{
+ circular_progress_t self = vself;
+ self->visible = visible;
+ self->start = start;
+ self->value = current;
+ self->end = end;
+}
+
+static int
+parse_angle (const char *value)
+{
+ const char *ptr;
+ int angle;
+
+ angle = grub_strtol (value, &ptr, 10);
+ if (grub_errno)
+ return 0;
+ while (grub_isspace (*ptr))
+ ptr++;
+ if (grub_strcmp (ptr, "deg") == 0
+ /* Unicode symbol of degrees (a circle, U+b0). Put here in UTF-8 to
+ avoid potential problem with text file reesncoding */
+ || grub_strcmp (ptr, "\xc2\xb0") == 0)
+ angle = grub_divide_round (angle * 64, 90);
+ return angle;
+}
+
+static grub_err_t
+circprog_set_property (void *vself, const char *name, const char *value)
+{
+ circular_progress_t self = vself;
+ if (grub_strcmp (name, "num_ticks") == 0)
+ {
+ self->num_ticks = grub_strtoul (value, 0, 10);
+ }
+ else if (grub_strcmp (name, "start_angle") == 0)
+ {
+ self->start_angle = parse_angle (value);
+ }
+ else if (grub_strcmp (name, "ticks_disappear") == 0)
+ {
+ self->ticks_disappear = grub_strcmp (value, "false") != 0;
+ }
+ else if (grub_strcmp (name, "center_bitmap") == 0)
+ {
+ self->need_to_load_pixmaps = 1;
+ grub_free (self->center_file);
+ self->center_file = value ? grub_strdup (value) : 0;
+ }
+ else if (grub_strcmp (name, "tick_bitmap") == 0)
+ {
+ self->need_to_load_pixmaps = 1;
+ grub_free (self->tick_file);
+ self->tick_file = value ? grub_strdup (value) : 0;
+ }
+ else if (grub_strcmp (name, "theme_dir") == 0)
+ {
+ self->need_to_load_pixmaps = 1;
+ grub_free (self->theme_dir);
+ self->theme_dir = value ? grub_strdup (value) : 0;
+ }
+ else if (grub_strcmp (name, "id") == 0)
+ {
+ grub_gfxmenu_timeout_unregister ((grub_gui_component_t) self);
+ grub_free (self->id);
+ if (value)
+ self->id = grub_strdup (value);
+ else
+ self->id = 0;
+ if (self->id && grub_strcmp (self->id, GRUB_GFXMENU_TIMEOUT_COMPONENT_ID)
+ == 0)
+ grub_gfxmenu_timeout_register ((grub_gui_component_t) self,
+ circprog_set_state);
+ }
+ return grub_errno;
+}
+
+static struct grub_gui_component_ops circprog_ops =
+{
+ .destroy = circprog_destroy,
+ .get_id = circprog_get_id,
+ .is_instance = circprog_is_instance,
+ .paint = circprog_paint,
+ .set_parent = circprog_set_parent,
+ .get_parent = circprog_get_parent,
+ .set_bounds = circprog_set_bounds,
+ .get_bounds = circprog_get_bounds,
+ .set_property = circprog_set_property
+};
+
+static struct grub_gui_progress_ops circprog_prog_ops =
+ {
+ .set_state = circprog_set_state
+ };
+
+grub_gui_component_t
+grub_gui_circular_progress_new (void)
+{
+ circular_progress_t self;
+ self = grub_zalloc (sizeof (*self));
+ if (! self)
+ return 0;
+ self->progress.ops = &circprog_prog_ops;
+ self->progress.component.ops = &circprog_ops;
+ self->visible = 1;
+ self->num_ticks = 64;
+ self->start_angle = -64;
+
+ return (grub_gui_component_t) self;
+}
diff --git a/grub-core/gfxmenu/gui_image.c b/grub-core/gfxmenu/gui_image.c
new file mode 100644
index 0000000..6b2e976
--- /dev/null
+++ b/grub-core/gfxmenu/gui_image.c
@@ -0,0 +1,270 @@
+/* gui_image.c - GUI component to display an image. */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2008,2009 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/gui.h>
+#include <grub/gui_string_util.h>
+#include <grub/bitmap.h>
+#include <grub/bitmap_scale.h>
+
+struct grub_gui_image
+{
+ struct grub_gui_component component;
+
+ grub_gui_container_t parent;
+ grub_video_rect_t bounds;
+ char *id;
+ char *theme_dir;
+ struct grub_video_bitmap *raw_bitmap;
+ struct grub_video_bitmap *bitmap;
+};
+
+typedef struct grub_gui_image *grub_gui_image_t;
+
+static void
+image_destroy (void *vself)
+{
+ grub_gui_image_t self = vself;
+
+ /* Free the scaled bitmap, unless it's a reference to the raw bitmap. */
+ if (self->bitmap && (self->bitmap != self->raw_bitmap))
+ grub_video_bitmap_destroy (self->bitmap);
+ if (self->raw_bitmap)
+ grub_video_bitmap_destroy (self->raw_bitmap);
+
+ grub_free (self);
+}
+
+static const char *
+image_get_id (void *vself)
+{
+ grub_gui_image_t self = vself;
+ return self->id;
+}
+
+static int
+image_is_instance (void *vself __attribute__((unused)), const char *type)
+{
+ return grub_strcmp (type, "component") == 0;
+}
+
+static void
+image_paint (void *vself, const grub_video_rect_t *region)
+{
+ grub_gui_image_t self = vself;
+ grub_video_rect_t vpsave;
+
+ if (! self->bitmap)
+ return;
+ if (!grub_video_have_common_points (region, &self->bounds))
+ return;
+
+ grub_gui_set_viewport (&self->bounds, &vpsave);
+ grub_video_blit_bitmap (self->bitmap, GRUB_VIDEO_BLIT_BLEND,
+ 0, 0, 0, 0,
+ grub_video_bitmap_get_width (self->bitmap),
+ grub_video_bitmap_get_height (self->bitmap));
+ grub_gui_restore_viewport (&vpsave);
+}
+
+static void
+image_set_parent (void *vself, grub_gui_container_t parent)
+{
+ grub_gui_image_t self = vself;
+ self->parent = parent;
+}
+
+static grub_gui_container_t
+image_get_parent (void *vself)
+{
+ grub_gui_image_t self = vself;
+ return self->parent;
+}
+
+static grub_err_t
+rescale_image (grub_gui_image_t self)
+{
+ signed width;
+ signed height;
+
+ if (! self->raw_bitmap)
+ {
+ if (self->bitmap)
+ {
+ grub_video_bitmap_destroy (self->bitmap);
+ self->bitmap = 0;
+ }
+ return grub_errno;
+ }
+
+ width = self->bounds.width;
+ height = self->bounds.height;
+
+ if (self->bitmap
+ && ((signed) grub_video_bitmap_get_width (self->bitmap) == width)
+ && ((signed) grub_video_bitmap_get_height (self->bitmap) == height))
+ {
+ /* Nothing to do; already the right size. */
+ return grub_errno;
+ }
+
+ /* Free any old scaled bitmap,
+ *unless* it's a reference to the raw bitmap. */
+ if (self->bitmap && (self->bitmap != self->raw_bitmap))
+ grub_video_bitmap_destroy (self->bitmap);
+
+ self->bitmap = 0;
+
+ /* Create a scaled bitmap, unless the requested size is the same
+ as the raw size -- in that case a reference is made. */
+ if ((signed) grub_video_bitmap_get_width (self->raw_bitmap) == width
+ && (signed) grub_video_bitmap_get_height (self->raw_bitmap) == height)
+ {
+ self->bitmap = self->raw_bitmap;
+ return grub_errno;
+ }
+
+ /* Don't scale to an invalid size. */
+ if (width <= 0 || height <= 0)
+ return grub_errno;
+
+ /* Create the scaled bitmap. */
+ grub_video_bitmap_create_scaled (&self->bitmap,
+ width,
+ height,
+ self->raw_bitmap,
+ GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST);
+ return grub_errno;
+}
+
+static void
+image_set_bounds (void *vself, const grub_video_rect_t *bounds)
+{
+ grub_gui_image_t self = vself;
+ self->bounds = *bounds;
+ rescale_image (self);
+}
+
+static void
+image_get_bounds (void *vself, grub_video_rect_t *bounds)
+{
+ grub_gui_image_t self = vself;
+ *bounds = self->bounds;
+}
+
+/* FIXME: inform rendering system it's not forced minimum. */
+static void
+image_get_minimal_size (void *vself, unsigned *width, unsigned *height)
+{
+ grub_gui_image_t self = vself;
+
+ if (self->raw_bitmap)
+ {
+ *width = grub_video_bitmap_get_width (self->raw_bitmap);
+ *height = grub_video_bitmap_get_height (self->raw_bitmap);
+ }
+ else
+ {
+ *width = 0;
+ *height = 0;
+ }
+}
+
+static grub_err_t
+load_image (grub_gui_image_t self, const char *path)
+{
+ struct grub_video_bitmap *bitmap;
+ if (grub_video_bitmap_load (&bitmap, path) != GRUB_ERR_NONE)
+ return grub_errno;
+
+ if (self->bitmap && (self->bitmap != self->raw_bitmap))
+ {
+ grub_video_bitmap_destroy (self->bitmap);
+ self->bitmap = 0;
+ }
+ if (self->raw_bitmap)
+ grub_video_bitmap_destroy (self->raw_bitmap);
+
+ self->raw_bitmap = bitmap;
+ return rescale_image (self);
+}
+
+static grub_err_t
+image_set_property (void *vself, const char *name, const char *value)
+{
+ grub_gui_image_t self = vself;
+ if (grub_strcmp (name, "theme_dir") == 0)
+ {
+ grub_free (self->theme_dir);
+ self->theme_dir = grub_strdup (value);
+ }
+ else if (grub_strcmp (name, "file") == 0)
+ {
+ char *absvalue;
+ grub_err_t err;
+
+ /* Resolve to an absolute path. */
+ if (! self->theme_dir)
+ return grub_error (GRUB_ERR_BUG, "unspecified theme_dir");
+ absvalue = grub_resolve_relative_path (self->theme_dir, value);
+ if (! absvalue)
+ return grub_errno;
+
+ err = load_image (self, absvalue);
+ grub_free (absvalue);
+
+ return err;
+ }
+ else if (grub_strcmp (name, "id") == 0)
+ {
+ grub_free (self->id);
+ if (value)
+ self->id = grub_strdup (value);
+ else
+ self->id = 0;
+ }
+ return grub_errno;
+}
+
+static struct grub_gui_component_ops image_ops =
+{
+ .destroy = image_destroy,
+ .get_id = image_get_id,
+ .is_instance = image_is_instance,
+ .paint = image_paint,
+ .set_parent = image_set_parent,
+ .get_parent = image_get_parent,
+ .set_bounds = image_set_bounds,
+ .get_bounds = image_get_bounds,
+ .get_minimal_size = image_get_minimal_size,
+ .set_property = image_set_property
+};
+
+grub_gui_component_t
+grub_gui_image_new (void)
+{
+ grub_gui_image_t image;
+ image = grub_zalloc (sizeof (*image));
+ if (! image)
+ return 0;
+ image->component.ops = &image_ops;
+ return (grub_gui_component_t) image;
+}
+
diff --git a/grub-core/gfxmenu/gui_label.c b/grub-core/gfxmenu/gui_label.c
new file mode 100644
index 0000000..1c19054
--- /dev/null
+++ b/grub-core/gfxmenu/gui_label.c
@@ -0,0 +1,277 @@
+/* gui_label.c - GUI component to display a line of text. */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2008,2009 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/gui.h>
+#include <grub/font.h>
+#include <grub/gui_string_util.h>
+#include <grub/i18n.h>
+#include <grub/color.h>
+
+static const char *align_options[] =
+{
+ "left",
+ "center",
+ "right",
+ 0
+};
+
+enum align_mode {
+ align_left,
+ align_center,
+ align_right
+};
+
+struct grub_gui_label
+{
+ struct grub_gui_component comp;
+
+ grub_gui_container_t parent;
+ grub_video_rect_t bounds;
+ char *id;
+ int visible;
+ char *text;
+ char *template;
+ grub_font_t font;
+ grub_video_rgba_color_t color;
+ int value;
+ enum align_mode align;
+};
+
+typedef struct grub_gui_label *grub_gui_label_t;
+
+static void
+label_destroy (void *vself)
+{
+ grub_gui_label_t self = vself;
+ grub_gfxmenu_timeout_unregister ((grub_gui_component_t) self);
+ grub_free (self->text);
+ grub_free (self->template);
+ grub_free (self);
+}
+
+static const char *
+label_get_id (void *vself)
+{
+ grub_gui_label_t self = vself;
+ return self->id;
+}
+
+static int
+label_is_instance (void *vself __attribute__((unused)), const char *type)
+{
+ return grub_strcmp (type, "component") == 0;
+}
+
+static void
+label_paint (void *vself, const grub_video_rect_t *region)
+{
+ grub_gui_label_t self = vself;
+
+ if (! self->visible)
+ return;
+
+ if (!grub_video_have_common_points (region, &self->bounds))
+ return;
+
+ /* Calculate the starting x coordinate. */
+ int left_x;
+ if (self->align == align_left)
+ left_x = 0;
+ else if (self->align == align_center)
+ left_x = (self->bounds.width
+ - grub_font_get_string_width (self->font, self->text)) / 2;
+ else if (self->align == align_right)
+ left_x = (self->bounds.width
+ - grub_font_get_string_width (self->font, self->text));
+ else
+ return; /* Invalid alignment. */
+
+ if (left_x < 0 || left_x > (int) self->bounds.width)
+ left_x = 0;
+
+ grub_video_rect_t vpsave;
+ grub_gui_set_viewport (&self->bounds, &vpsave);
+ grub_font_draw_string (self->text,
+ self->font,
+ grub_video_map_rgba_color (self->color),
+ left_x,
+ grub_font_get_ascent (self->font));
+ grub_gui_restore_viewport (&vpsave);
+}
+
+static void
+label_set_parent (void *vself, grub_gui_container_t parent)
+{
+ grub_gui_label_t self = vself;
+ self->parent = parent;
+}
+
+static grub_gui_container_t
+label_get_parent (void *vself)
+{
+ grub_gui_label_t self = vself;
+ return self->parent;
+}
+
+static void
+label_set_bounds (void *vself, const grub_video_rect_t *bounds)
+{
+ grub_gui_label_t self = vself;
+ self->bounds = *bounds;
+}
+
+static void
+label_get_bounds (void *vself, grub_video_rect_t *bounds)
+{
+ grub_gui_label_t self = vself;
+ *bounds = self->bounds;
+}
+
+static void
+label_get_minimal_size (void *vself, unsigned *width, unsigned *height)
+{
+ grub_gui_label_t self = vself;
+ *width = grub_font_get_string_width (self->font, self->text);
+ *height = (grub_font_get_ascent (self->font)
+ + grub_font_get_descent (self->font));
+}
+
+#pragma GCC diagnostic ignored "-Wformat-nonliteral"
+
+static void
+label_set_state (void *vself, int visible, int start __attribute__ ((unused)),
+ int current, int end __attribute__ ((unused)))
+{
+ grub_gui_label_t self = vself;
+ self->value = -current;
+ self->visible = visible;
+ grub_free (self->text);
+ self->text = grub_xasprintf (self->template ? : "%d", self->value);
+}
+
+static grub_err_t
+label_set_property (void *vself, const char *name, const char *value)
+{
+ grub_gui_label_t self = vself;
+ if (grub_strcmp (name, "text") == 0)
+ {
+ grub_free (self->text);
+ grub_free (self->template);
+ if (! value)
+ {
+ self->template = NULL;
+ self->text = grub_strdup ("");
+ }
+ else
+ {
+ if (grub_strcmp (value, "@KEYMAP_LONG@") == 0)
+ value = _("Press enter to boot the selected OS, "
+ "`e' to edit the commands before booting "
+ "or `c' for a command-line. ESC to return previous menu.");
+ else if (grub_strcmp (value, "@KEYMAP_MIDDLE@") == 0)
+ value = _("Press enter to boot the selected OS, "
+ "`e' to edit the commands before booting "
+ "or `c' for a command-line.");
+ else if (grub_strcmp (value, "@KEYMAP_SHORT@") == 0)
+ value = _("enter: boot, `e': options, `c': cmd-line");
+ /* FIXME: Add more templates here if needed. */
+
+ if (grub_printf_fmt_check(value, "%d") != GRUB_ERR_NONE)
+ value = ""; /* Unsupported format. */
+
+ self->template = grub_strdup (value);
+ self->text = grub_xasprintf (value, self->value);
+ }
+ }
+ else if (grub_strcmp (name, "font") == 0)
+ {
+ self->font = grub_font_get (value);
+ }
+ else if (grub_strcmp (name, "color") == 0)
+ {
+ grub_video_parse_color (value, &self->color);
+ }
+ else if (grub_strcmp (name, "align") == 0)
+ {
+ int i;
+ for (i = 0; align_options[i]; i++)
+ {
+ if (grub_strcmp (align_options[i], value) == 0)
+ {
+ self->align = i; /* Set the alignment mode. */
+ break;
+ }
+ }
+ }
+ else if (grub_strcmp (name, "visible") == 0)
+ {
+ self->visible = grub_strcmp (value, "false") != 0;
+ }
+ else if (grub_strcmp (name, "id") == 0)
+ {
+ grub_gfxmenu_timeout_unregister ((grub_gui_component_t) self);
+ grub_free (self->id);
+ if (value)
+ self->id = grub_strdup (value);
+ else
+ self->id = 0;
+ if (self->id && grub_strcmp (self->id, GRUB_GFXMENU_TIMEOUT_COMPONENT_ID)
+ == 0)
+ grub_gfxmenu_timeout_register ((grub_gui_component_t) self,
+ label_set_state);
+ }
+ return GRUB_ERR_NONE;
+}
+
+#pragma GCC diagnostic error "-Wformat-nonliteral"
+
+static struct grub_gui_component_ops label_ops =
+{
+ .destroy = label_destroy,
+ .get_id = label_get_id,
+ .is_instance = label_is_instance,
+ .paint = label_paint,
+ .set_parent = label_set_parent,
+ .get_parent = label_get_parent,
+ .set_bounds = label_set_bounds,
+ .get_bounds = label_get_bounds,
+ .get_minimal_size = label_get_minimal_size,
+ .set_property = label_set_property
+};
+
+grub_gui_component_t
+grub_gui_label_new (void)
+{
+ grub_gui_label_t label;
+ label = grub_zalloc (sizeof (*label));
+ if (! label)
+ return 0;
+ label->comp.ops = &label_ops;
+ label->visible = 1;
+ label->text = grub_strdup ("");
+ label->font = grub_font_get ("Unknown Regular 16");
+ label->color.red = 0;
+ label->color.green = 0;
+ label->color.blue = 0;
+ label->color.alpha = 255;
+ label->align = align_left;
+ return (grub_gui_component_t) label;
+}
diff --git a/grub-core/gfxmenu/gui_list.c b/grub-core/gfxmenu/gui_list.c
new file mode 100644
index 0000000..df334a6
--- /dev/null
+++ b/grub-core/gfxmenu/gui_list.c
@@ -0,0 +1,953 @@
+/* gui_list.c - GUI component to display a selectable list of items. */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2008,2009 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/gui.h>
+#include <grub/gui_string_util.h>
+#include <grub/gfxmenu_view.h>
+#include <grub/gfxwidgets.h>
+#include <grub/color.h>
+
+enum scrollbar_slice_mode {
+ SCROLLBAR_SLICE_WEST,
+ SCROLLBAR_SLICE_CENTER,
+ SCROLLBAR_SLICE_EAST
+};
+
+struct grub_gui_list_impl
+{
+ struct grub_gui_list list;
+
+ grub_gui_container_t parent;
+ grub_video_rect_t bounds;
+ char *id;
+ int visible;
+
+ int icon_width;
+ int icon_height;
+ int item_height;
+ int item_padding;
+ int item_icon_space;
+ int item_spacing;
+ grub_font_t item_font;
+ int selected_item_font_inherit;
+ grub_font_t selected_item_font;
+ grub_video_rgba_color_t item_color;
+ int selected_item_color_inherit;
+ grub_video_rgba_color_t selected_item_color;
+
+ int draw_scrollbar;
+ int need_to_recreate_scrollbar;
+ char *scrollbar_frame_pattern;
+ char *scrollbar_thumb_pattern;
+ grub_gfxmenu_box_t scrollbar_frame;
+ grub_gfxmenu_box_t scrollbar_thumb;
+ int scrollbar_thumb_overlay;
+ int scrollbar_width;
+ enum scrollbar_slice_mode scrollbar_slice;
+ int scrollbar_left_pad;
+ int scrollbar_right_pad;
+ int scrollbar_top_pad;
+ int scrollbar_bottom_pad;
+
+ int first_shown_index;
+
+ int need_to_recreate_boxes;
+ char *theme_dir;
+ char *menu_box_pattern;
+ char *item_box_pattern;
+ int selected_item_box_pattern_inherit;
+ char *selected_item_box_pattern;
+ grub_gfxmenu_box_t menu_box;
+ grub_gfxmenu_box_t selected_item_box;
+ grub_gfxmenu_box_t item_box;
+
+ grub_gfxmenu_icon_manager_t icon_manager;
+
+ grub_gfxmenu_view_t view;
+};
+
+typedef struct grub_gui_list_impl *list_impl_t;
+
+static void
+list_destroy (void *vself)
+{
+ list_impl_t self = vself;
+
+ grub_free (self->theme_dir);
+ grub_free (self->menu_box_pattern);
+ grub_free (self->item_box_pattern);
+ grub_free (self->selected_item_box_pattern);
+ if (self->menu_box)
+ self->menu_box->destroy (self->menu_box);
+ if (self->item_box)
+ self->item_box->destroy (self->item_box);
+ if (self->selected_item_box)
+ self->selected_item_box->destroy (self->selected_item_box);
+ if (self->icon_manager)
+ grub_gfxmenu_icon_manager_destroy (self->icon_manager);
+ if (self->scrollbar_thumb)
+ self->scrollbar_thumb->destroy (self->scrollbar_thumb);
+ if (self->scrollbar_frame)
+ self->scrollbar_frame->destroy (self->scrollbar_frame);
+ grub_free (self->scrollbar_thumb_pattern);
+ grub_free (self->scrollbar_frame_pattern);
+ grub_free (self);
+}
+
+static int
+get_num_shown_items (list_impl_t self)
+{
+ int boxpad = self->item_padding;
+ int item_vspace = self->item_spacing;
+ int item_height = self->item_height;
+
+ grub_gfxmenu_box_t box = self->menu_box;
+ int box_top_pad = box->get_top_pad (box);
+ int box_bottom_pad = box->get_bottom_pad (box);
+ grub_gfxmenu_box_t itembox = self->item_box;
+ grub_gfxmenu_box_t selbox = self->selected_item_box;
+ int item_top_pad = itembox->get_top_pad (itembox);
+ int item_bottom_pad = itembox->get_bottom_pad (itembox);
+ int sel_top_pad = selbox->get_top_pad (selbox);
+ int sel_bottom_pad = selbox->get_bottom_pad (selbox);
+ int max_top_pad = grub_max (item_top_pad, sel_top_pad);
+ int max_bottom_pad = grub_max (item_bottom_pad, sel_bottom_pad);
+
+ if (item_height + item_vspace <= 0)
+ return 1;
+
+ return (self->bounds.height + item_vspace - 2 * boxpad
+ - max_top_pad - max_bottom_pad
+ - box_top_pad - box_bottom_pad) / (item_height + item_vspace);
+}
+
+static int
+check_boxes (list_impl_t self)
+{
+ if (self->need_to_recreate_boxes)
+ {
+ grub_gui_recreate_box (&self->menu_box,
+ self->menu_box_pattern,
+ self->theme_dir);
+
+ grub_gui_recreate_box (&self->item_box,
+ self->item_box_pattern,
+ self->theme_dir);
+
+ grub_gui_recreate_box (&self->selected_item_box,
+ self->selected_item_box_pattern,
+ self->theme_dir);
+
+ self->need_to_recreate_boxes = 0;
+ }
+
+ return (self->menu_box != 0 && self->selected_item_box != 0
+ && self->item_box != 0);
+}
+
+static int
+check_scrollbar (list_impl_t self)
+{
+ if (self->need_to_recreate_scrollbar)
+ {
+ grub_gui_recreate_box (&self->scrollbar_frame,
+ self->scrollbar_frame_pattern,
+ self->theme_dir);
+
+ grub_gui_recreate_box (&self->scrollbar_thumb,
+ self->scrollbar_thumb_pattern,
+ self->theme_dir);
+
+ self->need_to_recreate_scrollbar = 0;
+ }
+
+ if (self->scrollbar_frame == 0 || self->scrollbar_thumb == 0)
+ return 0;
+
+ /* Sanity checks. */
+ grub_gfxmenu_box_t frame = self->scrollbar_frame;
+ grub_gfxmenu_box_t thumb = self->scrollbar_thumb;
+ grub_gfxmenu_box_t menu = self->menu_box;
+ int min_width = frame->get_left_pad (frame)
+ + frame->get_right_pad (frame);
+ int min_height = frame->get_top_pad (frame)
+ + frame->get_bottom_pad (frame)
+ + self->scrollbar_top_pad + self->scrollbar_bottom_pad
+ + menu->get_top_pad (menu)
+ + menu->get_bottom_pad (menu);
+ if (!self->scrollbar_thumb_overlay)
+ {
+ min_width += thumb->get_left_pad (thumb)
+ + thumb->get_right_pad (thumb);
+ min_height += thumb->get_top_pad (thumb)
+ + thumb->get_bottom_pad (thumb);
+ }
+ if (min_width <= self->scrollbar_width
+ && min_height <= (int) self->bounds.height)
+ return 1;
+
+ /* Unprintable dimenstions. */
+ self->draw_scrollbar = 0;
+ return 0;
+}
+
+static const char *
+list_get_id (void *vself)
+{
+ list_impl_t self = vself;
+ return self->id;
+}
+
+static int
+list_is_instance (void *vself __attribute__((unused)), const char *type)
+{
+ return (grub_strcmp (type, "component") == 0
+ || grub_strcmp (type, "list") == 0);
+}
+
+static struct grub_video_bitmap *
+get_item_icon (list_impl_t self, int item_index)
+{
+ grub_menu_entry_t entry;
+ entry = grub_menu_get_entry (self->view->menu, item_index);
+ if (! entry)
+ return 0;
+
+ return grub_gfxmenu_icon_manager_get_icon (self->icon_manager, entry);
+}
+
+static void
+make_selected_item_visible (list_impl_t self)
+{
+ int selected_index = self->view->selected;
+ if (selected_index < 0)
+ return; /* No item is selected. */
+ int num_shown_items = get_num_shown_items (self);
+ int last_shown_index = self->first_shown_index + (num_shown_items - 1);
+ if (selected_index < self->first_shown_index)
+ self->first_shown_index = selected_index;
+ else if (selected_index > last_shown_index)
+ self->first_shown_index = selected_index - (num_shown_items - 1);
+}
+
+/* Draw a scrollbar on the menu. */
+static void
+draw_scrollbar (list_impl_t self,
+ int value, int extent, int min, int max,
+ int scrollbar_width, int scrollbar_height)
+{
+ unsigned thumby, thumbheight;
+
+ grub_gfxmenu_box_t frame = self->scrollbar_frame;
+ grub_gfxmenu_box_t thumb = self->scrollbar_thumb;
+ int frame_vertical_pad = (frame->get_top_pad (frame)
+ + frame->get_bottom_pad (frame));
+ int frame_horizontal_pad = (frame->get_left_pad (frame)
+ + frame->get_right_pad (frame));
+ unsigned thumb_vertical_pad = (thumb->get_top_pad (thumb)
+ + thumb->get_bottom_pad (thumb));
+ int thumb_horizontal_pad = (thumb->get_left_pad (thumb)
+ + thumb->get_right_pad (thumb));
+ int tracktop = frame->get_top_pad (frame);
+ unsigned tracklen;
+ if (scrollbar_height <= frame_vertical_pad)
+ tracklen = 0;
+ else
+ tracklen = scrollbar_height - frame_vertical_pad;
+ frame->set_content_size (frame,
+ scrollbar_width - frame_horizontal_pad,
+ tracklen);
+ if (self->scrollbar_thumb_overlay)
+ {
+ tracklen += thumb_vertical_pad;
+ tracktop -= thumb->get_top_pad (thumb);
+ }
+ if (value <= min || max <= min)
+ thumby = 0;
+ else
+ thumby = ((unsigned) tracklen * (value - min))
+ / ((unsigned) (max - min));
+ if (max <= min)
+ thumbheight = 1;
+ else
+ thumbheight = ((unsigned) (tracklen * extent)
+ / ((unsigned) (max - min))) + 1;
+ /* Rare occasion: too many entries or too low height. */
+ if (thumbheight < thumb_vertical_pad)
+ {
+ thumbheight = thumb_vertical_pad;
+ if (value <= min || max <= extent
+ || tracklen <= thumb_vertical_pad)
+ thumby = 0;
+ else
+ thumby = ((unsigned) ((tracklen - thumb_vertical_pad) * (value - min))
+ / ((unsigned)(max - extent)));
+ }
+ thumby += tracktop;
+ int thumbx = frame->get_left_pad (frame);
+ int thumbwidth = scrollbar_width - frame_horizontal_pad;
+ if (!self->scrollbar_thumb_overlay)
+ thumbwidth -= thumb_horizontal_pad;
+ else
+ thumbx -= thumb->get_left_pad (thumb);
+ thumb->set_content_size (thumb, thumbwidth,
+ thumbheight - thumb_vertical_pad);
+ frame->draw (frame, 0, 0);
+ thumb->draw (thumb, thumbx, thumby);
+}
+
+/* Draw the list of items. */
+static void
+draw_menu (list_impl_t self, int num_shown_items)
+{
+ if (! self->menu_box || ! self->selected_item_box || ! self->item_box)
+ return;
+
+ int boxpad = self->item_padding;
+ int icon_text_space = self->item_icon_space;
+ int item_vspace = self->item_spacing;
+
+ int ascent = grub_font_get_ascent (self->item_font);
+ int descent = grub_font_get_descent (self->item_font);
+ int selected_ascent = grub_font_get_ascent (self->selected_item_font);
+ int selected_descent = grub_font_get_descent (self->selected_item_font);
+ int text_box_height = self->item_height;
+
+ make_selected_item_visible (self);
+
+ grub_gfxmenu_box_t itembox = self->item_box;
+ grub_gfxmenu_box_t selbox = self->selected_item_box;
+ int item_leftpad = itembox->get_left_pad (itembox);
+ int item_rightpad = itembox->get_right_pad (itembox);
+ int item_border_width = item_leftpad + item_rightpad;
+ int item_toppad = itembox->get_top_pad (itembox);
+ int sel_leftpad = selbox->get_left_pad (selbox);
+ int sel_rightpad = selbox->get_right_pad (selbox);
+ int sel_border_width = sel_leftpad + sel_rightpad;
+ int sel_toppad = selbox->get_top_pad (selbox);
+
+ int max_leftpad = grub_max (item_leftpad, sel_leftpad);
+ int max_toppad = grub_max (item_toppad, sel_toppad);
+ int item_top = 0;
+ int menu_index;
+ int visible_index;
+ struct grub_video_rect oviewport;
+
+ grub_video_get_viewport (&oviewport.x, &oviewport.y,
+ &oviewport.width, &oviewport.height);
+ grub_video_set_viewport (oviewport.x + boxpad,
+ oviewport.y + boxpad,
+ oviewport.width - 2 * boxpad,
+ oviewport.height - 2 * boxpad);
+
+ int cwidth = oviewport.width - 2 * boxpad;
+
+ itembox->set_content_size (itembox, cwidth - item_border_width,
+ text_box_height);
+ selbox->set_content_size (selbox, cwidth - sel_border_width,
+ text_box_height);
+
+ int text_left_offset = self->icon_width + icon_text_space;
+ int item_text_top_offset = (text_box_height - (ascent + descent)) / 2 + ascent;
+ int sel_text_top_offset = (text_box_height - (selected_ascent
+ + selected_descent)) / 2
+ + selected_ascent;
+
+ grub_video_rect_t svpsave, sviewport;
+ sviewport.x = max_leftpad + text_left_offset;
+ int text_viewport_width = cwidth - sviewport.x;
+ sviewport.height = text_box_height;
+
+ grub_video_color_t item_color;
+ grub_video_color_t sel_color;
+ item_color = grub_video_map_rgba_color (self->item_color);
+ sel_color = grub_video_map_rgba_color (self->selected_item_color);
+
+ int item_box_top_offset = max_toppad - item_toppad;
+ int sel_box_top_offset = max_toppad - sel_toppad;
+ int item_viewport_width = text_viewport_width - item_rightpad;
+ int sel_viewport_width = text_viewport_width - sel_rightpad;
+ int tmp_icon_top_offset = (text_box_height - self->icon_height) / 2;
+ int item_icon_top_offset = item_toppad + tmp_icon_top_offset;
+ int sel_icon_top_offset = sel_toppad + tmp_icon_top_offset;
+
+ for (visible_index = 0, menu_index = self->first_shown_index;
+ visible_index < num_shown_items && menu_index < self->view->menu->size;
+ visible_index++, menu_index++)
+ {
+ int is_selected = (menu_index == self->view->selected);
+ struct grub_video_bitmap *icon;
+ grub_font_t font;
+ grub_video_color_t color;
+ int text_top_offset;
+ int top_pad;
+ int icon_top_offset;
+ int viewport_width;
+
+ if (is_selected)
+ {
+ selbox->draw (selbox, 0, item_top + sel_box_top_offset);
+ font = self->selected_item_font;
+ color = sel_color;
+ text_top_offset = sel_text_top_offset;
+ top_pad = sel_toppad;
+ icon_top_offset = sel_icon_top_offset;
+ viewport_width = sel_viewport_width;
+ }
+ else
+ {
+ itembox->draw (itembox, 0, item_top + item_box_top_offset);
+ font = self->item_font;
+ color = item_color;
+ text_top_offset = item_text_top_offset;
+ top_pad = item_toppad;
+ icon_top_offset = item_icon_top_offset;
+ viewport_width = item_viewport_width;
+ }
+
+ icon = get_item_icon (self, menu_index);
+ if (icon != 0)
+ grub_video_blit_bitmap (icon, GRUB_VIDEO_BLIT_BLEND,
+ max_leftpad,
+ item_top + icon_top_offset,
+ 0, 0, self->icon_width, self->icon_height);
+
+ const char *item_title =
+ grub_menu_get_entry (self->view->menu, menu_index)->title;
+
+ sviewport.y = item_top + top_pad;
+ sviewport.width = viewport_width;
+ grub_gui_set_viewport (&sviewport, &svpsave);
+ grub_font_draw_string (item_title,
+ font,
+ color,
+ 0,
+ text_top_offset);
+ grub_gui_restore_viewport (&svpsave);
+
+ item_top += text_box_height + item_vspace;
+ }
+ grub_video_set_viewport (oviewport.x,
+ oviewport.y,
+ oviewport.width,
+ oviewport.height);
+}
+
+static void
+list_paint (void *vself, const grub_video_rect_t *region)
+{
+ list_impl_t self = vself;
+ grub_video_rect_t vpsave;
+
+ if (! self->visible)
+ return;
+ if (!grub_video_have_common_points (region, &self->bounds))
+ return;
+
+ check_boxes (self);
+
+ if (! self->menu_box || ! self->selected_item_box || ! self->item_box)
+ return;
+
+ grub_gui_set_viewport (&self->bounds, &vpsave);
+ {
+ grub_gfxmenu_box_t box = self->menu_box;
+ int box_left_pad = box->get_left_pad (box);
+ int box_top_pad = box->get_top_pad (box);
+ int box_right_pad = box->get_right_pad (box);
+ int box_bottom_pad = box->get_bottom_pad (box);
+ grub_video_rect_t vpsave2, content_rect;
+ int num_shown_items = get_num_shown_items (self);
+ int drawing_scrollbar = (self->draw_scrollbar
+ && (num_shown_items < self->view->menu->size)
+ && check_scrollbar (self));
+ int scrollbar_width = self->scrollbar_width;
+
+ content_rect.x = box_left_pad;
+ content_rect.y = box_top_pad;
+ content_rect.width = self->bounds.width - box_left_pad - box_right_pad;
+ content_rect.height = self->bounds.height - box_top_pad - box_bottom_pad;
+
+ box->set_content_size (box, content_rect.width, content_rect.height);
+
+ box->draw (box, 0, 0);
+
+ switch (self->scrollbar_slice)
+ {
+ case SCROLLBAR_SLICE_WEST:
+ content_rect.x += self->scrollbar_right_pad;
+ content_rect.width -= self->scrollbar_right_pad;
+ break;
+ case SCROLLBAR_SLICE_CENTER:
+ if (drawing_scrollbar)
+ content_rect.width -= scrollbar_width + self->scrollbar_left_pad
+ + self->scrollbar_right_pad;
+ break;
+ case SCROLLBAR_SLICE_EAST:
+ content_rect.width -= self->scrollbar_left_pad;
+ break;
+ }
+
+ grub_gui_set_viewport (&content_rect, &vpsave2);
+ draw_menu (self, num_shown_items);
+ grub_gui_restore_viewport (&vpsave2);
+
+ if (drawing_scrollbar)
+ {
+ content_rect.y += self->scrollbar_top_pad;
+ content_rect.height -= self->scrollbar_top_pad
+ + self->scrollbar_bottom_pad;
+ content_rect.width = scrollbar_width;
+ switch (self->scrollbar_slice)
+ {
+ case SCROLLBAR_SLICE_WEST:
+ if (box_left_pad > scrollbar_width)
+ {
+ content_rect.x = box_left_pad - scrollbar_width;
+ content_rect.width = scrollbar_width;
+ }
+ else
+ {
+ content_rect.x = 0;
+ content_rect.width = box_left_pad;
+ }
+ break;
+ case SCROLLBAR_SLICE_CENTER:
+ content_rect.x = self->bounds.width - box_right_pad
+ - scrollbar_width - self->scrollbar_right_pad;
+ content_rect.width = scrollbar_width;
+ break;
+ case SCROLLBAR_SLICE_EAST:
+ content_rect.x = self->bounds.width - box_right_pad;
+ content_rect.width = box_right_pad;
+ break;
+ }
+
+ grub_gui_set_viewport (&content_rect, &vpsave2);
+ draw_scrollbar (self,
+ self->first_shown_index, num_shown_items,
+ 0, self->view->menu->size,
+ scrollbar_width,
+ content_rect.height);
+ grub_gui_restore_viewport (&vpsave2);
+ }
+ }
+
+ grub_gui_restore_viewport (&vpsave);
+}
+
+static void
+list_set_parent (void *vself, grub_gui_container_t parent)
+{
+ list_impl_t self = vself;
+ self->parent = parent;
+}
+
+static grub_gui_container_t
+list_get_parent (void *vself)
+{
+ list_impl_t self = vself;
+ return self->parent;
+}
+
+static void
+list_set_bounds (void *vself, const grub_video_rect_t *bounds)
+{
+ list_impl_t self = vself;
+ self->bounds = *bounds;
+}
+
+static void
+list_get_bounds (void *vself, grub_video_rect_t *bounds)
+{
+ list_impl_t self = vself;
+ *bounds = self->bounds;
+}
+
+static void
+list_get_minimal_size (void *vself, unsigned *width, unsigned *height)
+{
+ list_impl_t self = vself;
+
+ if (check_boxes (self))
+ {
+ int boxpad = self->item_padding;
+ int item_vspace = self->item_spacing;
+ int item_height = self->item_height;
+ int num_items = 3;
+
+ grub_gfxmenu_box_t box = self->menu_box;
+ int box_left_pad = box->get_left_pad (box);
+ int box_top_pad = box->get_top_pad (box);
+ int box_right_pad = box->get_right_pad (box);
+ int box_bottom_pad = box->get_bottom_pad (box);
+ unsigned width_s;
+
+ grub_gfxmenu_box_t selbox = self->selected_item_box;
+ int sel_top_pad = selbox->get_top_pad (selbox);
+ int sel_bottom_pad = selbox->get_bottom_pad (selbox);
+ int sel_left_pad = selbox->get_left_pad (selbox);
+ int sel_right_pad = selbox->get_right_pad (selbox);
+
+ grub_gfxmenu_box_t itembox = self->item_box;
+ int item_top_pad = itembox->get_top_pad (itembox);
+ int item_bottom_pad = itembox->get_bottom_pad (itembox);
+ int item_left_pad = itembox->get_left_pad (itembox);
+ int item_right_pad = itembox->get_right_pad (itembox);
+
+ int max_left_pad = grub_max (item_left_pad, sel_left_pad);
+ int max_right_pad = grub_max (item_right_pad, sel_right_pad);
+ int max_top_pad = grub_max (item_top_pad, sel_top_pad);
+ int max_bottom_pad = grub_max (item_bottom_pad, sel_bottom_pad);
+
+ *width = grub_font_get_string_width (self->item_font, "Typical OS");
+ width_s = grub_font_get_string_width (self->selected_item_font,
+ "Typical OS");
+ if (*width < width_s)
+ *width = width_s;
+
+ *width += 2 * boxpad + box_left_pad + box_right_pad
+ + max_left_pad + max_right_pad
+ + self->item_icon_space + self->icon_width;
+
+ switch (self->scrollbar_slice)
+ {
+ case SCROLLBAR_SLICE_WEST:
+ *width += self->scrollbar_right_pad;
+ break;
+ case SCROLLBAR_SLICE_CENTER:
+ *width += self->scrollbar_width + self->scrollbar_left_pad
+ + self->scrollbar_right_pad;
+ break;
+ case SCROLLBAR_SLICE_EAST:
+ *width += self->scrollbar_left_pad;
+ break;
+ }
+
+ /* Set the menu box height to fit the items. */
+ *height = (item_height * num_items
+ + item_vspace * (num_items - 1)
+ + 2 * boxpad
+ + box_top_pad + box_bottom_pad
+ + max_top_pad + max_bottom_pad);
+ }
+ else
+ {
+ *width = 0;
+ *height = 0;
+ }
+}
+
+static grub_err_t
+list_set_property (void *vself, const char *name, const char *value)
+{
+ list_impl_t self = vself;
+ if (grub_strcmp (name, "item_font") == 0)
+ {
+ self->item_font = grub_font_get (value);
+ if (self->selected_item_font_inherit)
+ self->selected_item_font = self->item_font;
+ }
+ else if (grub_strcmp (name, "selected_item_font") == 0)
+ {
+ if (! value || grub_strcmp (value, "inherit") == 0)
+ {
+ self->selected_item_font = self->item_font;
+ self->selected_item_font_inherit = 1;
+ }
+ else
+ {
+ self->selected_item_font = grub_font_get (value);
+ self->selected_item_font_inherit = 0;
+ }
+ }
+ else if (grub_strcmp (name, "item_color") == 0)
+ {
+ grub_video_rgba_color_t color;
+ if (grub_video_parse_color (value, &color) == GRUB_ERR_NONE)
+ {
+ self->item_color = color;
+ if (self->selected_item_color_inherit)
+ self->selected_item_color = self->item_color;
+ }
+ }
+ else if (grub_strcmp (name, "selected_item_color") == 0)
+ {
+ if (! value || grub_strcmp (value, "inherit") == 0)
+ {
+ self->selected_item_color = self->item_color;
+ self->selected_item_color_inherit = 1;
+ }
+ else
+ {
+ grub_video_rgba_color_t color;
+ if (grub_video_parse_color (value, &color)
+ == GRUB_ERR_NONE)
+ {
+ self->selected_item_color = color;
+ self->selected_item_color_inherit = 0;
+ }
+ }
+ }
+ else if (grub_strcmp (name, "icon_width") == 0)
+ {
+ self->icon_width = grub_strtol (value, 0, 10);
+ grub_gfxmenu_icon_manager_set_icon_size (self->icon_manager,
+ self->icon_width,
+ self->icon_height);
+ }
+ else if (grub_strcmp (name, "icon_height") == 0)
+ {
+ self->icon_height = grub_strtol (value, 0, 10);
+ grub_gfxmenu_icon_manager_set_icon_size (self->icon_manager,
+ self->icon_width,
+ self->icon_height);
+ }
+ else if (grub_strcmp (name, "item_height") == 0)
+ {
+ self->item_height = grub_strtol (value, 0, 10);
+ }
+ else if (grub_strcmp (name, "item_padding") == 0)
+ {
+ self->item_padding = grub_strtol (value, 0, 10);
+ }
+ else if (grub_strcmp (name, "item_icon_space") == 0)
+ {
+ self->item_icon_space = grub_strtol (value, 0, 10);
+ }
+ else if (grub_strcmp (name, "item_spacing") == 0)
+ {
+ self->item_spacing = grub_strtol (value, 0, 10);
+ }
+ else if (grub_strcmp (name, "visible") == 0)
+ {
+ self->visible = grub_strcmp (value, "false") != 0;
+ }
+ else if (grub_strcmp (name, "menu_pixmap_style") == 0)
+ {
+ self->need_to_recreate_boxes = 1;
+ grub_free (self->menu_box_pattern);
+ self->menu_box_pattern = value ? grub_strdup (value) : 0;
+ }
+ else if (grub_strcmp (name, "item_pixmap_style") == 0)
+ {
+ self->need_to_recreate_boxes = 1;
+ grub_free (self->item_box_pattern);
+ self->item_box_pattern = value ? grub_strdup (value) : 0;
+ if (self->selected_item_box_pattern_inherit)
+ {
+ grub_free (self->selected_item_box_pattern);
+ self->selected_item_box_pattern = value ? grub_strdup (value) : 0;
+ }
+ }
+ else if (grub_strcmp (name, "selected_item_pixmap_style") == 0)
+ {
+ if (!value || grub_strcmp (value, "inherit") == 0)
+ {
+ grub_free (self->selected_item_box_pattern);
+ char *tmp = self->item_box_pattern;
+ self->selected_item_box_pattern = tmp ? grub_strdup (tmp) : 0;
+ self->selected_item_box_pattern_inherit = 1;
+ }
+ else
+ {
+ self->need_to_recreate_boxes = 1;
+ grub_free (self->selected_item_box_pattern);
+ self->selected_item_box_pattern = grub_strdup (value);
+ self->selected_item_box_pattern_inherit = 0;
+ }
+ }
+ else if (grub_strcmp (name, "scrollbar_frame") == 0)
+ {
+ self->need_to_recreate_scrollbar = 1;
+ grub_free (self->scrollbar_frame_pattern);
+ self->scrollbar_frame_pattern = value ? grub_strdup (value) : 0;
+ }
+ else if (grub_strcmp (name, "scrollbar_thumb") == 0)
+ {
+ self->need_to_recreate_scrollbar = 1;
+ grub_free (self->scrollbar_thumb_pattern);
+ self->scrollbar_thumb_pattern = value ? grub_strdup (value) : 0;
+ }
+ else if (grub_strcmp (name, "scrollbar_thumb_overlay") == 0)
+ {
+ self->scrollbar_thumb_overlay = grub_strcmp (value, "true") == 0;
+ }
+ else if (grub_strcmp (name, "scrollbar_width") == 0)
+ {
+ self->scrollbar_width = grub_strtol (value, 0, 10);
+ }
+ else if (grub_strcmp (name, "scrollbar_slice") == 0)
+ {
+ if (grub_strcmp (value, "west") == 0)
+ self->scrollbar_slice = SCROLLBAR_SLICE_WEST;
+ else if (grub_strcmp (value, "center") == 0)
+ self->scrollbar_slice = SCROLLBAR_SLICE_CENTER;
+ else if (grub_strcmp (value, "east") == 0)
+ self->scrollbar_slice = SCROLLBAR_SLICE_EAST;
+ }
+ else if (grub_strcmp (name, "scrollbar_left_pad") == 0)
+ {
+ self->scrollbar_left_pad = grub_strtol (value, 0, 10);
+ }
+ else if (grub_strcmp (name, "scrollbar_right_pad") == 0)
+ {
+ self->scrollbar_right_pad = grub_strtol (value, 0, 10);
+ }
+ else if (grub_strcmp (name, "scrollbar_top_pad") == 0)
+ {
+ self->scrollbar_top_pad = grub_strtol (value, 0, 10);
+ }
+ else if (grub_strcmp (name, "scrollbar_bottom_pad") == 0)
+ {
+ self->scrollbar_bottom_pad = grub_strtol (value, 0, 10);
+ }
+ else if (grub_strcmp (name, "scrollbar") == 0)
+ {
+ self->draw_scrollbar = grub_strcmp (value, "false") != 0;
+ }
+ else if (grub_strcmp (name, "theme_dir") == 0)
+ {
+ self->need_to_recreate_boxes = 1;
+ grub_free (self->theme_dir);
+ self->theme_dir = value ? grub_strdup (value) : 0;
+ }
+ else if (grub_strcmp (name, "id") == 0)
+ {
+ grub_free (self->id);
+ if (value)
+ self->id = grub_strdup (value);
+ else
+ self->id = 0;
+ }
+ return grub_errno;
+}
+
+/* Set necessary information that the gfxmenu view provides. */
+static void
+list_set_view_info (void *vself,
+ grub_gfxmenu_view_t view)
+{
+ list_impl_t self = vself;
+ grub_gfxmenu_icon_manager_set_theme_path (self->icon_manager,
+ view->theme_path);
+ self->view = view;
+}
+
+/* Refresh list variables */
+static void
+list_refresh_info (void *vself,
+ grub_gfxmenu_view_t view)
+{
+ list_impl_t self = vself;
+ if (view->nested)
+ self->first_shown_index = 0;
+}
+
+static struct grub_gui_component_ops list_comp_ops =
+ {
+ .destroy = list_destroy,
+ .get_id = list_get_id,
+ .is_instance = list_is_instance,
+ .paint = list_paint,
+ .set_parent = list_set_parent,
+ .get_parent = list_get_parent,
+ .set_bounds = list_set_bounds,
+ .get_bounds = list_get_bounds,
+ .get_minimal_size = list_get_minimal_size,
+ .set_property = list_set_property
+ };
+
+static struct grub_gui_list_ops list_ops =
+{
+ .set_view_info = list_set_view_info,
+ .refresh_list = list_refresh_info
+};
+
+grub_gui_component_t
+grub_gui_list_new (void)
+{
+ list_impl_t self;
+ grub_font_t default_font;
+ grub_video_rgba_color_t default_fg_color;
+
+ self = grub_zalloc (sizeof (*self));
+ if (! self)
+ return 0;
+
+ self->list.ops = &list_ops;
+ self->list.component.ops = &list_comp_ops;
+
+ self->visible = 1;
+
+ default_font = grub_font_get ("Unknown Regular 16");
+ default_fg_color = grub_video_rgba_color_rgb (0, 0, 0);
+
+ self->icon_width = 32;
+ self->icon_height = 32;
+ self->item_height = 42;
+ self->item_padding = 14;
+ self->item_icon_space = 4;
+ self->item_spacing = 16;
+ self->item_font = default_font;
+ self->selected_item_font_inherit = 1; /* Default to using the item_font. */
+ self->selected_item_font = default_font;
+ self->item_color = default_fg_color;
+ self->selected_item_color_inherit = 1; /* Default to using the item_color. */
+ self->selected_item_color = default_fg_color;
+
+ self->draw_scrollbar = 1;
+ self->need_to_recreate_scrollbar = 1;
+ self->scrollbar_frame = 0;
+ self->scrollbar_thumb = 0;
+ self->scrollbar_frame_pattern = 0;
+ self->scrollbar_thumb_pattern = 0;
+ self->scrollbar_thumb_overlay = 0;
+ self->scrollbar_width = 16;
+ self->scrollbar_slice = SCROLLBAR_SLICE_EAST;
+ self->scrollbar_left_pad = 2;
+ self->scrollbar_right_pad = 0;
+ self->scrollbar_top_pad = 0;
+ self->scrollbar_bottom_pad = 0;
+
+ self->first_shown_index = 0;
+
+ self->need_to_recreate_boxes = 0;
+ self->theme_dir = 0;
+ self->menu_box_pattern = 0;
+ self->item_box_pattern = 0;
+ self->selected_item_box_pattern_inherit = 1;/*Default to using the item_box.*/
+ self->selected_item_box_pattern = 0;
+ self->menu_box = grub_gfxmenu_create_box (0, 0);
+ self->item_box = grub_gfxmenu_create_box (0, 0);
+ self->selected_item_box = grub_gfxmenu_create_box (0, 0);
+
+ self->icon_manager = grub_gfxmenu_icon_manager_new ();
+ if (! self->icon_manager)
+ {
+ self->list.component.ops->destroy (self);
+ return 0;
+ }
+ grub_gfxmenu_icon_manager_set_icon_size (self->icon_manager,
+ self->icon_width,
+ self->icon_height);
+ return (grub_gui_component_t) self;
+}
diff --git a/grub-core/gfxmenu/gui_progress_bar.c b/grub-core/gfxmenu/gui_progress_bar.c
new file mode 100644
index 0000000..ace85a1
--- /dev/null
+++ b/grub-core/gfxmenu/gui_progress_bar.c
@@ -0,0 +1,457 @@
+/* gui_progress_bar.c - GUI progress bar component. */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2008,2009 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/gui.h>
+#include <grub/font.h>
+#include <grub/gui_string_util.h>
+#include <grub/gfxmenu_view.h>
+#include <grub/gfxwidgets.h>
+#include <grub/i18n.h>
+#include <grub/color.h>
+
+struct grub_gui_progress_bar
+{
+ struct grub_gui_progress progress;
+
+ grub_gui_container_t parent;
+ grub_video_rect_t bounds;
+ char *id;
+ int visible;
+ int start;
+ int end;
+ int value;
+ char *template;
+ grub_font_t font;
+ grub_video_rgba_color_t text_color;
+ grub_video_rgba_color_t border_color;
+ grub_video_rgba_color_t bg_color;
+ grub_video_rgba_color_t fg_color;
+
+ char *theme_dir;
+ int need_to_recreate_pixmaps;
+ int pixmapbar_available;
+ char *bar_pattern;
+ char *highlight_pattern;
+ grub_gfxmenu_box_t bar_box;
+ grub_gfxmenu_box_t highlight_box;
+ int highlight_overlay;
+};
+
+typedef struct grub_gui_progress_bar *grub_gui_progress_bar_t;
+
+static void
+progress_bar_destroy (void *vself)
+{
+ grub_gui_progress_bar_t self = vself;
+ grub_free (self->theme_dir);
+ grub_free (self->template);
+ grub_free (self->id);
+ grub_gfxmenu_timeout_unregister ((grub_gui_component_t) self);
+ grub_free (self);
+}
+
+static const char *
+progress_bar_get_id (void *vself)
+{
+ grub_gui_progress_bar_t self = vself;
+ return self->id;
+}
+
+static int
+progress_bar_is_instance (void *vself __attribute__((unused)), const char *type)
+{
+ return grub_strcmp (type, "component") == 0;
+}
+
+static int
+check_pixmaps (grub_gui_progress_bar_t self)
+{
+ if (!self->pixmapbar_available)
+ return 0;
+ if (self->need_to_recreate_pixmaps)
+ {
+ grub_gui_recreate_box (&self->bar_box,
+ self->bar_pattern,
+ self->theme_dir);
+
+ grub_gui_recreate_box (&self->highlight_box,
+ self->highlight_pattern,
+ self->theme_dir);
+
+ self->need_to_recreate_pixmaps = 0;
+ }
+
+ return (self->bar_box != 0 && self->highlight_box != 0);
+}
+
+static void
+draw_filled_rect_bar (grub_gui_progress_bar_t self)
+{
+ /* Set the progress bar's frame. */
+ grub_video_rect_t f;
+ f.x = 1;
+ f.y = 1;
+ f.width = self->bounds.width - 2;
+ f.height = self->bounds.height - 2;
+
+ /* Border. */
+ grub_video_fill_rect (grub_video_map_rgba_color (self->border_color),
+ f.x - 1, f.y - 1,
+ f.width + 2, f.height + 2);
+
+ /* Bar background. */
+ unsigned barwidth;
+
+ if (self->end <= self->start
+ || self->value <= self->start)
+ barwidth = 0;
+ else
+ barwidth = (f.width
+ * (self->value - self->start)
+ / (self->end - self->start));
+ grub_video_fill_rect (grub_video_map_rgba_color (self->bg_color),
+ f.x + barwidth, f.y,
+ f.width - barwidth, f.height);
+
+ /* Bar foreground. */
+ grub_video_fill_rect (grub_video_map_rgba_color (self->fg_color),
+ f.x, f.y,
+ barwidth, f.height);
+}
+
+static void
+draw_pixmap_bar (grub_gui_progress_bar_t self)
+{
+ grub_gfxmenu_box_t bar = self->bar_box;
+ grub_gfxmenu_box_t hl = self->highlight_box;
+ int w = self->bounds.width;
+ int h = self->bounds.height;
+ int bar_l_pad = bar->get_left_pad (bar);
+ int bar_r_pad = bar->get_right_pad (bar);
+ int bar_t_pad = bar->get_top_pad (bar);
+ int bar_b_pad = bar->get_bottom_pad (bar);
+ int bar_h_pad = bar_l_pad + bar_r_pad;
+ int bar_v_pad = bar_t_pad + bar_b_pad;
+ int hl_l_pad = hl->get_left_pad (hl);
+ int hl_r_pad = hl->get_right_pad (hl);
+ int hl_t_pad = hl->get_top_pad (hl);
+ int hl_b_pad = hl->get_bottom_pad (hl);
+ int hl_h_pad = hl_l_pad + hl_r_pad;
+ int hl_v_pad = hl_t_pad + hl_b_pad;
+ int tracklen = w - bar_h_pad;
+ int trackheight = h - bar_v_pad;
+ int barwidth;
+ int hlheight = trackheight;
+ int hlx = bar_l_pad;
+ int hly = bar_t_pad;
+
+ bar->set_content_size (bar, tracklen, trackheight);
+ bar->draw (bar, 0, 0);
+
+ if (self->highlight_overlay)
+ {
+ tracklen += hl_h_pad;
+ hlx -= hl_l_pad;
+ hly -= hl_t_pad;
+ }
+ else
+ hlheight -= hl_v_pad;
+
+ if (self->value <= self->start
+ || self->end <= self->start)
+ barwidth = 0;
+ else
+ barwidth = ((unsigned) (tracklen * (self->value - self->start))
+ / ((unsigned) (self->end - self->start)));
+
+ if (barwidth >= hl_h_pad)
+ {
+ hl->set_content_size (hl, barwidth - hl_h_pad, hlheight);
+ hl->draw (hl, hlx, hly);
+ }
+}
+
+#pragma GCC diagnostic ignored "-Wformat-nonliteral"
+
+static void
+draw_text (grub_gui_progress_bar_t self)
+{
+ if (self->template)
+ {
+ grub_font_t font = self->font;
+ grub_video_color_t text_color =
+ grub_video_map_rgba_color (self->text_color);
+ int width = self->bounds.width;
+ int height = self->bounds.height;
+ char *text;
+ text = grub_xasprintf (self->template,
+ self->value > 0 ? self->value : -self->value);
+ if (!text)
+ {
+ grub_print_error ();
+ grub_errno = GRUB_ERR_NONE;
+ return;
+ }
+ /* Center the text. */
+ int text_width = grub_font_get_string_width (font, text);
+ int x = (width - text_width) / 2;
+ int y = ((height - grub_font_get_descent (font)) / 2
+ + grub_font_get_ascent (font) / 2);
+ grub_font_draw_string (text, font, text_color, x, y);
+ grub_free (text);
+ }
+}
+
+#pragma GCC diagnostic error "-Wformat-nonliteral"
+
+static void
+progress_bar_paint (void *vself, const grub_video_rect_t *region)
+{
+ grub_gui_progress_bar_t self = vself;
+ grub_video_rect_t vpsave;
+
+ if (! self->visible)
+ return;
+ if (!grub_video_have_common_points (region, &self->bounds))
+ return;
+
+ if (self->end == self->start)
+ return;
+
+ grub_gui_set_viewport (&self->bounds, &vpsave);
+
+ if (check_pixmaps (self))
+ draw_pixmap_bar (self);
+ else
+ draw_filled_rect_bar (self);
+
+ draw_text (self);
+
+ grub_gui_restore_viewport (&vpsave);
+}
+
+static void
+progress_bar_set_parent (void *vself, grub_gui_container_t parent)
+{
+ grub_gui_progress_bar_t self = vself;
+ self->parent = parent;
+}
+
+static grub_gui_container_t
+progress_bar_get_parent (void *vself)
+{
+ grub_gui_progress_bar_t self = vself;
+ return self->parent;
+}
+
+static void
+progress_bar_set_bounds (void *vself, const grub_video_rect_t *bounds)
+{
+ grub_gui_progress_bar_t self = vself;
+ self->bounds = *bounds;
+}
+
+static void
+progress_bar_get_bounds (void *vself, grub_video_rect_t *bounds)
+{
+ grub_gui_progress_bar_t self = vself;
+ *bounds = self->bounds;
+}
+
+static void
+progress_bar_get_minimal_size (void *vself,
+ unsigned *width, unsigned *height)
+{
+ unsigned min_width = 0;
+ unsigned min_height = 0;
+ grub_gui_progress_bar_t self = vself;
+
+ if (self->template)
+ {
+ min_width = grub_font_get_string_width (self->font, self->template);
+ min_width += grub_font_get_string_width (self->font, "XXXXXXXXXX");
+ min_height = grub_font_get_descent (self->font)
+ + grub_font_get_ascent (self->font);
+ }
+ if (check_pixmaps (self))
+ {
+ grub_gfxmenu_box_t bar = self->bar_box;
+ grub_gfxmenu_box_t hl = self->highlight_box;
+ min_width += bar->get_left_pad (bar) + bar->get_right_pad (bar);
+ min_height += bar->get_top_pad (bar) + bar->get_bottom_pad (bar);
+ if (!self->highlight_overlay)
+ {
+ min_width += hl->get_left_pad (hl) + hl->get_right_pad (hl);
+ min_height += hl->get_top_pad (hl) + hl->get_bottom_pad (hl);
+ }
+ }
+ else
+ {
+ min_height += 2;
+ min_width += 2;
+ }
+ *width = 200;
+ if (*width < min_width)
+ *width = min_width;
+ *height = 28;
+ if (*height < min_height)
+ *height = min_height;
+}
+
+static void
+progress_bar_set_state (void *vself, int visible, int start,
+ int current, int end)
+{
+ grub_gui_progress_bar_t self = vself;
+ self->visible = visible;
+ self->start = start;
+ self->value = current;
+ self->end = end;
+}
+
+static grub_err_t
+progress_bar_set_property (void *vself, const char *name, const char *value)
+{
+ grub_gui_progress_bar_t self = vself;
+ if (grub_strcmp (name, "text") == 0)
+ {
+ grub_free (self->template);
+ if (grub_strcmp (value, "@TIMEOUT_NOTIFICATION_LONG@") == 0)
+ value
+ = _("The highlighted entry will be executed automatically in %ds.");
+ else if (grub_strcmp (value, "@TIMEOUT_NOTIFICATION_MIDDLE@") == 0)
+ /* TRANSLATORS: 's' stands for seconds.
+ It's a standalone timeout notification.
+ Please use the short form in your language. */
+ value = _("%ds remaining.");
+ else if (grub_strcmp (value, "@TIMEOUT_NOTIFICATION_SHORT@") == 0)
+ /* TRANSLATORS: 's' stands for seconds.
+ It's a standalone timeout notification.
+ Please use the shortest form available in you language. */
+ value = _("%ds");
+
+ if (grub_printf_fmt_check(value, "%d") != GRUB_ERR_NONE)
+ value = ""; /* Unsupported format. */
+
+ self->template = grub_strdup (value);
+ }
+ else if (grub_strcmp (name, "font") == 0)
+ {
+ self->font = grub_font_get (value);
+ }
+ else if (grub_strcmp (name, "text_color") == 0)
+ {
+ grub_video_parse_color (value, &self->text_color);
+ }
+ else if (grub_strcmp (name, "border_color") == 0)
+ {
+ grub_video_parse_color (value, &self->border_color);
+ }
+ else if (grub_strcmp (name, "bg_color") == 0)
+ {
+ grub_video_parse_color (value, &self->bg_color);
+ }
+ else if (grub_strcmp (name, "fg_color") == 0)
+ {
+ grub_video_parse_color (value, &self->fg_color);
+ }
+ else if (grub_strcmp (name, "bar_style") == 0)
+ {
+ self->need_to_recreate_pixmaps = 1;
+ self->pixmapbar_available = 1;
+ grub_free (self->bar_pattern);
+ self->bar_pattern = value ? grub_strdup (value) : 0;
+ }
+ else if (grub_strcmp (name, "highlight_style") == 0)
+ {
+ self->need_to_recreate_pixmaps = 1;
+ self->pixmapbar_available = 1;
+ grub_free (self->highlight_pattern);
+ self->highlight_pattern = value ? grub_strdup (value) : 0;
+ }
+ else if (grub_strcmp (name, "highlight_overlay") == 0)
+ {
+ self->highlight_overlay = grub_strcmp (value, "true") == 0;
+ }
+ else if (grub_strcmp (name, "theme_dir") == 0)
+ {
+ self->need_to_recreate_pixmaps = 1;
+ grub_free (self->theme_dir);
+ self->theme_dir = value ? grub_strdup (value) : 0;
+ }
+ else if (grub_strcmp (name, "id") == 0)
+ {
+ grub_gfxmenu_timeout_unregister ((grub_gui_component_t) self);
+ grub_free (self->id);
+ if (value)
+ self->id = grub_strdup (value);
+ else
+ self->id = 0;
+ if (self->id && grub_strcmp (self->id, GRUB_GFXMENU_TIMEOUT_COMPONENT_ID)
+ == 0)
+ grub_gfxmenu_timeout_register ((grub_gui_component_t) self,
+ progress_bar_set_state);
+ }
+ return grub_errno;
+}
+
+static struct grub_gui_component_ops progress_bar_ops =
+{
+ .destroy = progress_bar_destroy,
+ .get_id = progress_bar_get_id,
+ .is_instance = progress_bar_is_instance,
+ .paint = progress_bar_paint,
+ .set_parent = progress_bar_set_parent,
+ .get_parent = progress_bar_get_parent,
+ .set_bounds = progress_bar_set_bounds,
+ .get_bounds = progress_bar_get_bounds,
+ .get_minimal_size = progress_bar_get_minimal_size,
+ .set_property = progress_bar_set_property
+};
+
+static struct grub_gui_progress_ops progress_bar_pb_ops =
+ {
+ .set_state = progress_bar_set_state
+ };
+
+grub_gui_component_t
+grub_gui_progress_bar_new (void)
+{
+ grub_gui_progress_bar_t self;
+ self = grub_zalloc (sizeof (*self));
+ if (! self)
+ return 0;
+
+ self->progress.ops = &progress_bar_pb_ops;
+ self->progress.component.ops = &progress_bar_ops;
+ self->visible = 1;
+ self->font = grub_font_get ("Unknown Regular 16");
+ grub_video_rgba_color_t black = { .red = 0, .green = 0, .blue = 0, .alpha = 255 };
+ grub_video_rgba_color_t gray = { .red = 128, .green = 128, .blue = 128, .alpha = 255 };
+ grub_video_rgba_color_t lightgray = { .red = 200, .green = 200, .blue = 200, .alpha = 255 };
+ self->text_color = black;
+ self->border_color = black;
+ self->bg_color = gray;
+ self->fg_color = lightgray;
+ self->highlight_overlay = 0;
+
+ return (grub_gui_component_t) self;
+}
diff --git a/grub-core/gfxmenu/gui_string_util.c b/grub-core/gfxmenu/gui_string_util.c
new file mode 100644
index 0000000..ba1e1ea
--- /dev/null
+++ b/grub-core/gfxmenu/gui_string_util.c
@@ -0,0 +1,206 @@
+/* gui_string_util.c - String utilities used by the GUI system. */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2008,2009 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/gui_string_util.h>
+#include <grub/types.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+
+/* Create a new NUL-terminated string on the heap as a substring of BUF.
+ The range of buf included is the half-open interval [START,END).
+ The index START is inclusive, END is exclusive. */
+char *
+grub_new_substring (const char *buf,
+ grub_size_t start, grub_size_t end)
+{
+ if (end < start)
+ return 0;
+ grub_size_t len = end - start;
+ char *s = grub_malloc (len + 1);
+ if (! s)
+ return 0;
+ grub_memcpy (s, buf + start, len);
+ s[len] = '\0';
+ return s;
+}
+
+/* Eliminate "." and ".." path elements from PATH. A new heap-allocated
+ string is returned. */
+static char *
+canonicalize_path (const char *path)
+{
+ int i;
+ const char *p;
+ char *newpath = 0;
+
+ /* Count the path components in path. */
+ int components = 1;
+ for (p = path; *p; p++)
+ if (*p == '/')
+ components++;
+
+ char **path_array = grub_calloc (components, sizeof (*path_array));
+ if (! path_array)
+ return 0;
+
+ /* Initialize array elements to NULL pointers; in case once of the
+ allocations fails, the cleanup code can just call grub_free() for all
+ pointers in the array. */
+ for (i = 0; i < components; i++)
+ path_array[i] = 0;
+
+ /* Parse the path into path_array. */
+ p = path;
+ for (i = 0; i < components && p; i++)
+ {
+ /* Find the end of the path element. */
+ const char *end = grub_strchr (p, '/');
+ if (!end)
+ end = p + grub_strlen (p);
+
+ /* Copy the element. */
+ path_array[i] = grub_new_substring (p, 0, end - p);
+ if (! path_array[i])
+ goto cleanup;
+
+ /* Advance p to point to the start of the next element, or NULL. */
+ if (*end)
+ p = end + 1;
+ else
+ p = 0;
+ }
+
+ /* Eliminate '.' and '..' elements from the path array. */
+ int newpath_length = 0;
+ for (i = components - 1; i >= 0; --i)
+ {
+ if (! grub_strcmp (path_array[i], "."))
+ {
+ grub_free (path_array[i]);
+ path_array[i] = 0;
+ }
+ else if (! grub_strcmp (path_array[i], "..")
+ && i > 0)
+ {
+ /* Delete the '..' and the prior path element. */
+ grub_free (path_array[i]);
+ path_array[i] = 0;
+ --i;
+ grub_free (path_array[i]);
+ path_array[i] = 0;
+ }
+ else
+ {
+ newpath_length += grub_strlen (path_array[i]) + 1;
+ }
+ }
+
+ /* Construct a new path string. */
+ newpath = grub_malloc (newpath_length + 1);
+ if (! newpath)
+ goto cleanup;
+
+ newpath[0] = '\0';
+ char *newpath_end = newpath;
+ int first = 1;
+ for (i = 0; i < components; i++)
+ {
+ char *element = path_array[i];
+ if (element)
+ {
+ /* For all components but the first, prefix with a slash. */
+ if (! first)
+ newpath_end = grub_stpcpy (newpath_end, "/");
+ newpath_end = grub_stpcpy (newpath_end, element);
+ first = 0;
+ }
+ }
+
+cleanup:
+ for (i = 0; i < components; i++)
+ grub_free (path_array[i]);
+ grub_free (path_array);
+
+ return newpath;
+}
+
+/* Return a new heap-allocated string representing to absolute path
+ to the file referred to by PATH. If PATH is an absolute path, then
+ the returned path is a copy of PATH. If PATH is a relative path, then
+ BASE is with PATH used to construct the absolute path. */
+char *
+grub_resolve_relative_path (const char *base, const char *path)
+{
+ char *abspath;
+ char *canonpath;
+ char *p;
+ grub_size_t l;
+
+ /* If PATH is an absolute path, then just use it as is. */
+ if (path[0] == '/' || path[0] == '(')
+ return canonicalize_path (path);
+
+ abspath = grub_malloc (grub_strlen (base) + grub_strlen (path) + 3);
+ if (! abspath)
+ return 0;
+
+ /* Concatenate BASE and PATH. */
+ p = grub_stpcpy (abspath, base);
+ l = grub_strlen (abspath);
+ if (l == 0 || abspath[l-1] != '/')
+ {
+ *p = '/';
+ p++;
+ *p = 0;
+ }
+ grub_stpcpy (p, path);
+
+ canonpath = canonicalize_path (abspath);
+ if (! canonpath)
+ return abspath;
+
+ grub_free (abspath);
+ return canonpath;
+}
+
+/* Get the path of the directory where the file at FILE_PATH is located.
+ FILE_PATH should refer to a file, not a directory. The returned path
+ includes a trailing slash.
+ This does not handle GRUB "(hd0,0)" paths properly yet since it only
+ looks at slashes. */
+char *
+grub_get_dirname (const char *file_path)
+{
+ int i;
+ int last_slash;
+
+ last_slash = -1;
+ for (i = grub_strlen (file_path) - 1; i >= 0; --i)
+ {
+ if (file_path[i] == '/')
+ {
+ last_slash = i;
+ break;
+ }
+ }
+ if (last_slash == -1)
+ return grub_strdup ("/");
+
+ return grub_new_substring (file_path, 0, last_slash + 1);
+}
diff --git a/grub-core/gfxmenu/gui_util.c b/grub-core/gfxmenu/gui_util.c
new file mode 100644
index 0000000..eba7bb3
--- /dev/null
+++ b/grub-core/gfxmenu/gui_util.c
@@ -0,0 +1,101 @@
+/* gui_util.c - GUI utility functions. */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2008 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/gui.h>
+#include <grub/gui_string_util.h>
+
+
+struct find_by_id_state
+{
+ const char *match_id;
+ grub_gui_component_callback match_callback;
+ void *match_userdata;
+};
+
+static void
+find_by_id_recursively (grub_gui_component_t component, void *userdata)
+{
+ struct find_by_id_state *state;
+ const char *id;
+
+ state = (struct find_by_id_state *) userdata;
+ id = component->ops->get_id (component);
+ if (id && grub_strcmp (id, state->match_id) == 0)
+ state->match_callback (component, state->match_userdata);
+
+ if (component->ops->is_instance (component, "container"))
+ {
+ grub_gui_container_t container;
+ container = (grub_gui_container_t) component;
+ container->ops->iterate_children (container,
+ find_by_id_recursively,
+ state);
+ }
+}
+
+void
+grub_gui_find_by_id (grub_gui_component_t root,
+ const char *id,
+ grub_gui_component_callback cb,
+ void *userdata)
+{
+ struct find_by_id_state state;
+ state.match_id = id;
+ state.match_callback = cb;
+ state.match_userdata = userdata;
+ find_by_id_recursively (root, &state);
+}
+
+
+struct iterate_recursively_state
+{
+ grub_gui_component_callback callback;
+ void *userdata;
+};
+
+static
+void iterate_recursively_cb (grub_gui_component_t component, void *userdata)
+{
+ struct iterate_recursively_state *state;
+
+ state = (struct iterate_recursively_state *) userdata;
+ state->callback (component, state->userdata);
+
+ if (component->ops->is_instance (component, "container"))
+ {
+ grub_gui_container_t container;
+ container = (grub_gui_container_t) component;
+ container->ops->iterate_children (container,
+ iterate_recursively_cb,
+ state);
+ }
+}
+
+void
+grub_gui_iterate_recursively (grub_gui_component_t root,
+ grub_gui_component_callback cb,
+ void *userdata)
+{
+ struct iterate_recursively_state state;
+ state.callback = cb;
+ state.userdata = userdata;
+ iterate_recursively_cb (root, &state);
+}
diff --git a/grub-core/gfxmenu/icon_manager.c b/grub-core/gfxmenu/icon_manager.c
new file mode 100644
index 0000000..1894682
--- /dev/null
+++ b/grub-core/gfxmenu/icon_manager.c
@@ -0,0 +1,257 @@
+/* icon_manager.c - gfxmenu icon manager. */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2008,2009 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/types.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/err.h>
+#include <grub/gui_string_util.h>
+#include <grub/bitmap.h>
+#include <grub/bitmap_scale.h>
+#include <grub/menu.h>
+#include <grub/icon_manager.h>
+#include <grub/env.h>
+
+/* Currently hard coded to '.png' extension. */
+static const char icon_extension[] = ".png";
+
+typedef struct icon_entry
+{
+ char *class_name;
+ struct grub_video_bitmap *bitmap;
+ struct icon_entry *next;
+} *icon_entry_t;
+
+struct grub_gfxmenu_icon_manager
+{
+ char *theme_path;
+ int icon_width;
+ int icon_height;
+
+ /* Icon cache: linked list w/ dummy head node. */
+ struct icon_entry cache;
+};
+
+
+/* Create a new icon manager and return a point to it. */
+grub_gfxmenu_icon_manager_t
+grub_gfxmenu_icon_manager_new (void)
+{
+ grub_gfxmenu_icon_manager_t mgr;
+ mgr = grub_malloc (sizeof (*mgr));
+ if (! mgr)
+ return 0;
+
+ mgr->theme_path = 0;
+ mgr->icon_width = 0;
+ mgr->icon_height = 0;
+
+ /* Initialize the dummy head node. */
+ mgr->cache.class_name = 0;
+ mgr->cache.bitmap = 0;
+ mgr->cache.next = 0;
+
+ return mgr;
+}
+
+/* Destroy the icon manager MGR, freeing all resources used by it.
+
+Note: Any bitmaps returned by grub_gfxmenu_icon_manager_get_icon()
+are destroyed and must not be used by the caller after this function
+is called. */
+void
+grub_gfxmenu_icon_manager_destroy (grub_gfxmenu_icon_manager_t mgr)
+{
+ grub_gfxmenu_icon_manager_clear_cache (mgr);
+ grub_free (mgr->theme_path);
+ grub_free (mgr);
+}
+
+/* Clear the icon cache. */
+void
+grub_gfxmenu_icon_manager_clear_cache (grub_gfxmenu_icon_manager_t mgr)
+{
+ icon_entry_t cur;
+ icon_entry_t next;
+ for (cur = mgr->cache.next; cur; cur = next)
+ {
+ next = cur->next;
+ grub_free (cur->class_name);
+ grub_video_bitmap_destroy (cur->bitmap);
+ grub_free (cur);
+ }
+ mgr->cache.next = 0;
+}
+
+/* Set the theme path. If the theme path is changed, the icon cache
+ is cleared. */
+void
+grub_gfxmenu_icon_manager_set_theme_path (grub_gfxmenu_icon_manager_t mgr,
+ const char *path)
+{
+ /* Clear the cache if the theme path has changed. */
+ if (mgr->theme_path == 0 && path == 0)
+ return;
+ if (mgr->theme_path == 0 || path == 0
+ || grub_strcmp (mgr->theme_path, path) != 0)
+ grub_gfxmenu_icon_manager_clear_cache (mgr);
+
+ grub_free (mgr->theme_path);
+ mgr->theme_path = path ? grub_strdup (path) : 0;
+}
+
+/* Set the icon size. When icons are requested from the icon manager,
+ they are scaled to this size before being returned. If the size is
+ changed, the icon cache is cleared. */
+void
+grub_gfxmenu_icon_manager_set_icon_size (grub_gfxmenu_icon_manager_t mgr,
+ int width, int height)
+{
+ /* If the width or height is changed, we must clear the cache, since the
+ scaled bitmaps are stored in the cache. */
+ if (width != mgr->icon_width || height != mgr->icon_height)
+ grub_gfxmenu_icon_manager_clear_cache (mgr);
+
+ mgr->icon_width = width;
+ mgr->icon_height = height;
+}
+
+/* Try to load an icon for the specified CLASS_NAME in the directory DIR.
+ Returns 0 if the icon could not be loaded, or returns a pointer to a new
+ bitmap if it was successful. */
+static struct grub_video_bitmap *
+try_loading_icon (grub_gfxmenu_icon_manager_t mgr,
+ const char *dir, const char *class_name)
+{
+ char *path, *ptr;
+
+ path = grub_malloc (grub_strlen (dir) + grub_strlen (class_name)
+ + grub_strlen (icon_extension) + 3);
+ if (! path)
+ return 0;
+
+ ptr = grub_stpcpy (path, dir);
+ if (path == ptr || ptr[-1] != '/')
+ *ptr++ = '/';
+ ptr = grub_stpcpy (ptr, class_name);
+ ptr = grub_stpcpy (ptr, icon_extension);
+ *ptr = '\0';
+
+ struct grub_video_bitmap *raw_bitmap;
+ grub_video_bitmap_load (&raw_bitmap, path);
+ grub_free (path);
+ grub_errno = GRUB_ERR_NONE; /* Critical to clear the error!! */
+ if (! raw_bitmap)
+ return 0;
+
+ struct grub_video_bitmap *scaled_bitmap;
+ grub_video_bitmap_create_scaled (&scaled_bitmap,
+ mgr->icon_width, mgr->icon_height,
+ raw_bitmap,
+ GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST);
+ grub_video_bitmap_destroy (raw_bitmap);
+ if (! scaled_bitmap)
+ return 0;
+
+ return scaled_bitmap;
+}
+
+/* Get the icon for the specified class CLASS_NAME. If an icon for
+ CLASS_NAME already exists in the cache, then a reference to the cached
+ bitmap is returned. If it is not cached, then it is loaded and cached.
+ If no icon could be could for CLASS_NAME, then 0 is returned. */
+static struct grub_video_bitmap *
+get_icon_by_class (grub_gfxmenu_icon_manager_t mgr, const char *class_name)
+{
+ /* First check the icon cache. */
+ icon_entry_t entry;
+ for (entry = mgr->cache.next; entry; entry = entry->next)
+ {
+ if (grub_strcmp (entry->class_name, class_name) == 0)
+ return entry->bitmap;
+ }
+
+ if (! mgr->theme_path)
+ return 0;
+
+ /* Otherwise, we search for an icon to load. */
+ char *theme_dir = grub_get_dirname (mgr->theme_path);
+ char *icons_dir;
+ struct grub_video_bitmap *icon;
+ icon = 0;
+ /* First try the theme's own icons, from "grub/themes/NAME/icons/" */
+ icons_dir = grub_resolve_relative_path (theme_dir, "icons/");
+ if (icons_dir)
+ {
+ icon = try_loading_icon (mgr, icons_dir, class_name);
+ grub_free (icons_dir);
+ }
+
+ grub_free (theme_dir);
+ if (! icon)
+ {
+ const char *icondir;
+
+ icondir = grub_env_get ("icondir");
+ if (icondir)
+ icon = try_loading_icon (mgr, icondir, class_name);
+ }
+
+ /* No icon was found. */
+ /* This should probably be noted in the cache, so that a search is not
+ performed each time an icon for CLASS_NAME is requested. */
+ if (! icon)
+ return 0;
+
+ /* Insert a new cache entry for this icon. */
+ entry = grub_malloc (sizeof (*entry));
+ if (! entry)
+ {
+ grub_video_bitmap_destroy (icon);
+ return 0;
+ }
+ entry->class_name = grub_strdup (class_name);
+ entry->bitmap = icon;
+ entry->next = mgr->cache.next;
+ mgr->cache.next = entry; /* Link it into the cache. */
+ return entry->bitmap;
+}
+
+/* Get the best available icon for ENTRY. Beginning with the first class
+ listed in the menu entry and proceeding forward, an icon for each class
+ is searched for. The first icon found is returned. The returned icon
+ is scaled to the size specified by
+ grub_gfxmenu_icon_manager_set_icon_size().
+
+ Note: Bitmaps returned by this function are destroyed when the
+ icon manager is destroyed.
+ */
+struct grub_video_bitmap *
+grub_gfxmenu_icon_manager_get_icon (grub_gfxmenu_icon_manager_t mgr,
+ grub_menu_entry_t entry)
+{
+ struct grub_menu_entry_class *c;
+ struct grub_video_bitmap *icon;
+
+ /* Try each class in succession. */
+ icon = 0;
+ for (c = entry->classes; c && ! icon; c = c->next)
+ icon = get_icon_by_class (mgr, c->name);
+ return icon;
+}
diff --git a/grub-core/gfxmenu/theme_loader.c b/grub-core/gfxmenu/theme_loader.c
new file mode 100644
index 0000000..eae8308
--- /dev/null
+++ b/grub-core/gfxmenu/theme_loader.c
@@ -0,0 +1,828 @@
+/* theme_loader.c - Theme file loader for gfxmenu. */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2008 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/types.h>
+#include <grub/file.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/err.h>
+#include <grub/dl.h>
+#include <grub/video.h>
+#include <grub/gui_string_util.h>
+#include <grub/bitmap.h>
+#include <grub/bitmap_scale.h>
+#include <grub/gfxwidgets.h>
+#include <grub/gfxmenu_view.h>
+#include <grub/gui.h>
+#include <grub/color.h>
+
+static grub_err_t
+parse_proportional_spec (const char *value, signed *abs, grub_fixed_signed_t *prop);
+
+/* Construct a new box widget using ABSPATTERN to find the pixmap files for
+ it, storing the new box instance at *BOXPTR.
+ PATTERN should be of the form: "(hd0,0)/somewhere/style*.png".
+ The '*' then gets substituted with the various pixmap names that the
+ box uses. */
+static grub_err_t
+recreate_box_absolute (grub_gfxmenu_box_t *boxptr, const char *abspattern)
+{
+ char *prefix;
+ char *suffix;
+ char *star;
+ grub_gfxmenu_box_t box;
+
+ star = grub_strchr (abspattern, '*');
+ if (! star)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ "missing `*' in box pixmap pattern `%s'", abspattern);
+
+ /* Prefix: Get the part before the '*'. */
+ prefix = grub_malloc (star - abspattern + 1);
+ if (! prefix)
+ return grub_errno;
+
+ grub_memcpy (prefix, abspattern, star - abspattern);
+ prefix[star - abspattern] = '\0';
+
+ /* Suffix: Everything after the '*' is the suffix. */
+ suffix = star + 1;
+
+ box = grub_gfxmenu_create_box (prefix, suffix);
+ grub_free (prefix);
+ if (! box)
+ return grub_errno;
+
+ if (*boxptr)
+ (*boxptr)->destroy (*boxptr);
+ *boxptr = box;
+ return grub_errno;
+}
+
+
+/* Construct a new box widget using PATTERN to find the pixmap files for it,
+ storing the new widget at *BOXPTR. PATTERN should be of the form:
+ "somewhere/style*.png". The '*' then gets substituted with the various
+ pixmap names that the widget uses.
+
+ Important! The value of *BOXPTR must be initialized! It must either
+ (1) Be 0 (a NULL pointer), or
+ (2) Be a pointer to a valid 'grub_gfxmenu_box_t' instance.
+ In this case, the previous instance is destroyed. */
+grub_err_t
+grub_gui_recreate_box (grub_gfxmenu_box_t *boxptr,
+ const char *pattern, const char *theme_dir)
+{
+ char *abspattern;
+
+ /* Check arguments. */
+ if (! pattern)
+ {
+ /* If no pixmap pattern is given, then just create an empty box. */
+ if (*boxptr)
+ (*boxptr)->destroy (*boxptr);
+ *boxptr = grub_gfxmenu_create_box (0, 0);
+ return grub_errno;
+ }
+
+ if (! theme_dir)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ "styled box missing theme directory");
+
+ /* Resolve to an absolute path. */
+ abspattern = grub_resolve_relative_path (theme_dir, pattern);
+ if (! abspattern)
+ return grub_errno;
+
+ /* Create the box. */
+ recreate_box_absolute (boxptr, abspattern);
+ grub_free (abspattern);
+ return grub_errno;
+}
+
+static grub_err_t
+theme_get_unsigned_int_from_proportional (const char *value,
+ unsigned absolute_value,
+ unsigned int *parsed_value)
+{
+ grub_err_t err;
+ grub_fixed_signed_t frac;
+ signed pixels;
+ err = parse_proportional_spec (value, &pixels, &frac);
+ if (err != GRUB_ERR_NONE)
+ return err;
+ int result = grub_fixed_sfs_multiply (absolute_value, frac) + pixels;
+ if (result < 0)
+ result = 0;
+ *parsed_value = result;
+ return GRUB_ERR_NONE;
+}
+
+/* Set the specified property NAME on the view to the given string VALUE.
+ The caller is responsible for the lifetimes of NAME and VALUE. */
+static grub_err_t
+theme_set_string (grub_gfxmenu_view_t view,
+ const char *name,
+ const char *value,
+ const char *theme_dir,
+ const char *filename,
+ int line_num,
+ int col_num)
+{
+ if (! grub_strcmp ("title-font", name))
+ view->title_font = grub_font_get (value);
+ else if (! grub_strcmp ("message-font", name))
+ view->message_font = grub_font_get (value);
+ else if (! grub_strcmp ("terminal-font", name))
+ {
+ grub_free (view->terminal_font_name);
+ view->terminal_font_name = grub_strdup (value);
+ if (! view->terminal_font_name)
+ return grub_errno;
+ }
+ else if (! grub_strcmp ("title-color", name))
+ grub_video_parse_color (value, &view->title_color);
+ else if (! grub_strcmp ("message-color", name))
+ grub_video_parse_color (value, &view->message_color);
+ else if (! grub_strcmp ("message-bg-color", name))
+ grub_video_parse_color (value, &view->message_bg_color);
+ else if (! grub_strcmp ("desktop-image", name))
+ {
+ struct grub_video_bitmap *raw_bitmap;
+ char *path;
+ path = grub_resolve_relative_path (theme_dir, value);
+ if (! path)
+ return grub_errno;
+ if (grub_video_bitmap_load (&raw_bitmap, path) != GRUB_ERR_NONE)
+ {
+ grub_free (path);
+ return grub_errno;
+ }
+ grub_free(path);
+ grub_video_bitmap_destroy (view->raw_desktop_image);
+ view->raw_desktop_image = raw_bitmap;
+ }
+ else if (! grub_strcmp ("desktop-image-scale-method", name))
+ {
+ if (! value || ! grub_strcmp ("stretch", value))
+ view->desktop_image_scale_method =
+ GRUB_VIDEO_BITMAP_SELECTION_METHOD_STRETCH;
+ else if (! grub_strcmp ("crop", value))
+ view->desktop_image_scale_method =
+ GRUB_VIDEO_BITMAP_SELECTION_METHOD_CROP;
+ else if (! grub_strcmp ("padding", value))
+ view->desktop_image_scale_method =
+ GRUB_VIDEO_BITMAP_SELECTION_METHOD_PADDING;
+ else if (! grub_strcmp ("fitwidth", value))
+ view->desktop_image_scale_method =
+ GRUB_VIDEO_BITMAP_SELECTION_METHOD_FITWIDTH;
+ else if (! grub_strcmp ("fitheight", value))
+ view->desktop_image_scale_method =
+ GRUB_VIDEO_BITMAP_SELECTION_METHOD_FITHEIGHT;
+ else
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ "Unsupported scale method: %s",
+ value);
+ }
+ else if (! grub_strcmp ("desktop-image-h-align", name))
+ {
+ if (! grub_strcmp ("left", value))
+ view->desktop_image_h_align = GRUB_VIDEO_BITMAP_H_ALIGN_LEFT;
+ else if (! grub_strcmp ("center", value))
+ view->desktop_image_h_align = GRUB_VIDEO_BITMAP_H_ALIGN_CENTER;
+ else if (! grub_strcmp ("right", value))
+ view->desktop_image_h_align = GRUB_VIDEO_BITMAP_H_ALIGN_RIGHT;
+ else
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ "Unsupported horizontal align method: %s",
+ value);
+ }
+ else if (! grub_strcmp ("desktop-image-v-align", name))
+ {
+ if (! grub_strcmp ("top", value))
+ view->desktop_image_v_align = GRUB_VIDEO_BITMAP_V_ALIGN_TOP;
+ else if (! grub_strcmp ("center", value))
+ view->desktop_image_v_align = GRUB_VIDEO_BITMAP_V_ALIGN_CENTER;
+ else if (! grub_strcmp ("bottom", value))
+ view->desktop_image_v_align = GRUB_VIDEO_BITMAP_V_ALIGN_BOTTOM;
+ else
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ "Unsupported vertical align method: %s",
+ value);
+ }
+ else if (! grub_strcmp ("desktop-color", name))
+ grub_video_parse_color (value, &view->desktop_color);
+ else if (! grub_strcmp ("terminal-box", name))
+ {
+ grub_err_t err;
+ err = grub_gui_recreate_box (&view->terminal_box, value, theme_dir);
+ if (err != GRUB_ERR_NONE)
+ return err;
+ }
+ else if (! grub_strcmp ("terminal-border", name))
+ {
+ view->terminal_border = grub_strtoul (value, 0, 10);
+ if (grub_errno)
+ return grub_errno;
+ }
+ else if (! grub_strcmp ("terminal-left", name))
+ {
+ unsigned int tmp;
+ int err = theme_get_unsigned_int_from_proportional (value,
+ view->screen.width,
+ &tmp);
+ if (err != GRUB_ERR_NONE)
+ return err;
+ view->terminal_rect.x = tmp;
+ }
+ else if (! grub_strcmp ("terminal-top", name))
+ {
+ unsigned int tmp;
+ int err = theme_get_unsigned_int_from_proportional (value,
+ view->screen.height,
+ &tmp);
+ if (err != GRUB_ERR_NONE)
+ return err;
+ view->terminal_rect.y = tmp;
+ }
+ else if (! grub_strcmp ("terminal-width", name))
+ {
+ unsigned int tmp;
+ int err = theme_get_unsigned_int_from_proportional (value,
+ view->screen.width,
+ &tmp);
+ if (err != GRUB_ERR_NONE)
+ return err;
+ view->terminal_rect.width = tmp;
+ }
+ else if (! grub_strcmp ("terminal-height", name))
+ {
+ unsigned int tmp;
+ int err = theme_get_unsigned_int_from_proportional (value,
+ view->screen.height,
+ &tmp);
+ if (err != GRUB_ERR_NONE)
+ return err;
+ view->terminal_rect.height = tmp;
+ }
+ else if (! grub_strcmp ("title-text", name))
+ {
+ grub_free (view->title_text);
+ view->title_text = grub_strdup (value);
+ if (! view->title_text)
+ return grub_errno;
+ }
+ else
+ {
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ "%s:%d:%d unknown property `%s'",
+ filename, line_num, col_num, name);
+ }
+ return grub_errno;
+}
+
+struct parsebuf
+{
+ char *buf;
+ int pos;
+ int len;
+ int line_num;
+ int col_num;
+ const char *filename;
+ char *theme_dir;
+ grub_gfxmenu_view_t view;
+};
+
+static int
+has_more (struct parsebuf *p)
+{
+ return p->pos < p->len;
+}
+
+static int
+read_char (struct parsebuf *p)
+{
+ if (has_more (p))
+ {
+ char c;
+ c = p->buf[p->pos++];
+ if (c == '\n')
+ {
+ p->line_num++;
+ p->col_num = 1;
+ }
+ else
+ {
+ p->col_num++;
+ }
+ return c;
+ }
+ else
+ return -1;
+}
+
+static int
+peek_char (struct parsebuf *p)
+{
+ if (has_more (p))
+ return p->buf[p->pos];
+ else
+ return -1;
+}
+
+static int
+is_whitespace (char c)
+{
+ return (c == ' '
+ || c == '\t'
+ || c == '\r'
+ || c == '\n'
+ || c == '\f');
+}
+
+static void
+skip_whitespace (struct parsebuf *p)
+{
+ while (has_more (p) && is_whitespace(peek_char (p)))
+ read_char (p);
+}
+
+static void
+advance_to_next_line (struct parsebuf *p)
+{
+ int c;
+
+ /* Eat characters up to the newline. */
+ do
+ {
+ c = read_char (p);
+ }
+ while (c != -1 && c != '\n');
+}
+
+static int
+is_identifier_char (int c)
+{
+ return (c != -1
+ && (grub_isalpha(c)
+ || grub_isdigit(c)
+ || c == '_'
+ || c == '-'));
+}
+
+static char *
+read_identifier (struct parsebuf *p)
+{
+ /* Index of the first character of the identifier in p->buf. */
+ int start;
+ /* Next index after the last character of the identifer in p->buf. */
+ int end;
+
+ skip_whitespace (p);
+
+ /* Capture the start of the identifier. */
+ start = p->pos;
+
+ /* Scan for the end. */
+ while (is_identifier_char (peek_char (p)))
+ read_char (p);
+ end = p->pos;
+
+ if (end - start < 1)
+ return 0;
+
+ return grub_new_substring (p->buf, start, end);
+}
+
+static char *
+read_expression (struct parsebuf *p)
+{
+ int start;
+ int end;
+
+ skip_whitespace (p);
+ if (peek_char (p) == '"')
+ {
+ /* Read as a quoted string.
+ The quotation marks are not included in the expression value. */
+ /* Skip opening quotation mark. */
+ read_char (p);
+ start = p->pos;
+ while (has_more (p) && peek_char (p) != '"')
+ read_char (p);
+ end = p->pos;
+ /* Skip the terminating quotation mark. */
+ read_char (p);
+ }
+ else if (peek_char (p) == '(')
+ {
+ /* Read as a parenthesized string -- for tuples/coordinates. */
+ /* The parentheses are included in the expression value. */
+ int c;
+
+ start = p->pos;
+ do
+ {
+ c = read_char (p);
+ }
+ while (c != -1 && c != ')');
+ end = p->pos;
+ }
+ else if (has_more (p))
+ {
+ /* Read as a single word -- for numeric values or words without
+ whitespace. */
+ start = p->pos;
+ while (has_more (p) && ! is_whitespace (peek_char (p)))
+ read_char (p);
+ end = p->pos;
+ }
+ else
+ {
+ /* The end of the theme file has been reached. */
+ grub_error (GRUB_ERR_IO, "%s:%d:%d expression expected in theme file",
+ p->filename, p->line_num, p->col_num);
+ return 0;
+ }
+
+ return grub_new_substring (p->buf, start, end);
+}
+
+static grub_err_t
+parse_proportional_spec (const char *value, signed *abs, grub_fixed_signed_t *prop)
+{
+ signed num;
+ const char *ptr;
+ int sig = 0;
+ *abs = 0;
+ *prop = 0;
+ ptr = value;
+ while (*ptr)
+ {
+ sig = 0;
+
+ while (*ptr == '-' || *ptr == '+')
+ {
+ if (*ptr == '-')
+ sig = !sig;
+ ptr++;
+ }
+
+ num = grub_strtoul (ptr, &ptr, 0);
+ if (grub_errno)
+ return grub_errno;
+ if (sig)
+ num = -num;
+ if (*ptr == '%')
+ {
+ *prop += grub_fixed_fsf_divide (grub_signed_to_fixed (num), 100);
+ ptr++;
+ }
+ else
+ *abs += num;
+ }
+ return GRUB_ERR_NONE;
+}
+
+
+/* Read a GUI object specification from the theme file.
+ Any components created will be added to the GUI container PARENT. */
+static grub_err_t
+read_object (struct parsebuf *p, grub_gui_container_t parent)
+{
+ grub_video_rect_t bounds;
+
+ char *name;
+ name = read_identifier (p);
+ if (! name)
+ goto cleanup;
+
+ grub_gui_component_t component = 0;
+ if (grub_strcmp (name, "label") == 0)
+ {
+ component = grub_gui_label_new ();
+ }
+ else if (grub_strcmp (name, "image") == 0)
+ {
+ component = grub_gui_image_new ();
+ }
+ else if (grub_strcmp (name, "vbox") == 0)
+ {
+ component = (grub_gui_component_t) grub_gui_vbox_new ();
+ }
+ else if (grub_strcmp (name, "hbox") == 0)
+ {
+ component = (grub_gui_component_t) grub_gui_hbox_new ();
+ }
+ else if (grub_strcmp (name, "canvas") == 0)
+ {
+ component = (grub_gui_component_t) grub_gui_canvas_new ();
+ }
+ else if (grub_strcmp (name, "progress_bar") == 0)
+ {
+ component = grub_gui_progress_bar_new ();
+ }
+ else if (grub_strcmp (name, "circular_progress") == 0)
+ {
+ component = grub_gui_circular_progress_new ();
+ }
+ else if (grub_strcmp (name, "boot_menu") == 0)
+ {
+ component = grub_gui_list_new ();
+ }
+ else
+ {
+ /* Unknown type. */
+ grub_error (GRUB_ERR_IO, "%s:%d:%d unknown object type `%s'",
+ p->filename, p->line_num, p->col_num, name);
+ goto cleanup;
+ }
+
+ if (! component)
+ goto cleanup;
+
+ /* Inform the component about the theme so it can find its resources. */
+ component->ops->set_property (component, "theme_dir", p->theme_dir);
+ component->ops->set_property (component, "theme_path", p->filename);
+
+ /* Add the component as a child of PARENT. */
+ bounds.x = 0;
+ bounds.y = 0;
+ bounds.width = -1;
+ bounds.height = -1;
+ component->ops->set_bounds (component, &bounds);
+ parent->ops->add (parent, component);
+
+ skip_whitespace (p);
+ if (read_char (p) != '{')
+ {
+ grub_error (GRUB_ERR_IO,
+ "%s:%d:%d expected `{' after object type name `%s'",
+ p->filename, p->line_num, p->col_num, name);
+ goto cleanup;
+ }
+
+ while (has_more (p))
+ {
+ skip_whitespace (p);
+
+ /* Check whether the end has been encountered. */
+ if (peek_char (p) == '}')
+ {
+ /* Skip the closing brace. */
+ read_char (p);
+ break;
+ }
+
+ if (peek_char (p) == '#')
+ {
+ /* Skip comments. */
+ advance_to_next_line (p);
+ continue;
+ }
+
+ if (peek_char (p) == '+')
+ {
+ /* Skip the '+'. */
+ read_char (p);
+
+ /* Check whether this component is a container. */
+ if (component->ops->is_instance (component, "container"))
+ {
+ /* Read the sub-object recursively and add it as a child. */
+ if (read_object (p, (grub_gui_container_t) component) != 0)
+ goto cleanup;
+ /* After reading the sub-object, resume parsing, expecting
+ another property assignment or sub-object definition. */
+ continue;
+ }
+ else
+ {
+ grub_error (GRUB_ERR_IO,
+ "%s:%d:%d attempted to add object to non-container",
+ p->filename, p->line_num, p->col_num);
+ goto cleanup;
+ }
+ }
+
+ char *property;
+ property = read_identifier (p);
+ if (! property)
+ {
+ grub_error (GRUB_ERR_IO, "%s:%d:%d identifier expected in theme file",
+ p->filename, p->line_num, p->col_num);
+ goto cleanup;
+ }
+
+ skip_whitespace (p);
+ if (read_char (p) != '=')
+ {
+ grub_error (GRUB_ERR_IO,
+ "%s:%d:%d expected `=' after property name `%s'",
+ p->filename, p->line_num, p->col_num, property);
+ grub_free (property);
+ goto cleanup;
+ }
+ skip_whitespace (p);
+
+ char *value;
+ value = read_expression (p);
+ if (! value)
+ {
+ grub_free (property);
+ goto cleanup;
+ }
+
+ /* Handle the property value. */
+ if (grub_strcmp (property, "left") == 0)
+ parse_proportional_spec (value, &component->x, &component->xfrac);
+ else if (grub_strcmp (property, "top") == 0)
+ parse_proportional_spec (value, &component->y, &component->yfrac);
+ else if (grub_strcmp (property, "width") == 0)
+ parse_proportional_spec (value, &component->w, &component->wfrac);
+ else if (grub_strcmp (property, "height") == 0)
+ parse_proportional_spec (value, &component->h, &component->hfrac);
+ else
+ /* General property handling. */
+ component->ops->set_property (component, property, value);
+
+ grub_free (value);
+ grub_free (property);
+ if (grub_errno != GRUB_ERR_NONE)
+ goto cleanup;
+ }
+
+cleanup:
+ grub_free (name);
+ return grub_errno;
+}
+
+static grub_err_t
+read_property (struct parsebuf *p)
+{
+ char *name;
+
+ /* Read the property name. */
+ name = read_identifier (p);
+ if (! name)
+ {
+ advance_to_next_line (p);
+ return grub_errno;
+ }
+
+ /* Skip whitespace before separator. */
+ skip_whitespace (p);
+
+ /* Read separator. */
+ if (read_char (p) != ':')
+ {
+ grub_error (GRUB_ERR_IO,
+ "%s:%d:%d missing separator after property name `%s'",
+ p->filename, p->line_num, p->col_num, name);
+ goto done;
+ }
+
+ /* Skip whitespace after separator. */
+ skip_whitespace (p);
+
+ /* Get the value based on its type. */
+ if (peek_char (p) == '"')
+ {
+ /* String value (e.g., '"My string"'). */
+ char *value = read_expression (p);
+ if (! value)
+ {
+ grub_error (GRUB_ERR_IO, "%s:%d:%d missing property value",
+ p->filename, p->line_num, p->col_num);
+ goto done;
+ }
+ /* If theme_set_string results in an error, grub_errno will be returned
+ below. */
+ theme_set_string (p->view, name, value, p->theme_dir,
+ p->filename, p->line_num, p->col_num);
+ grub_free (value);
+ }
+ else
+ {
+ grub_error (GRUB_ERR_IO,
+ "%s:%d:%d property value invalid; "
+ "enclose literal values in quotes (\")",
+ p->filename, p->line_num, p->col_num);
+ goto done;
+ }
+
+done:
+ grub_free (name);
+ return grub_errno;
+}
+
+/* Set properties on the view based on settings from the specified
+ theme file. */
+grub_err_t
+grub_gfxmenu_view_load_theme (grub_gfxmenu_view_t view, const char *theme_path)
+{
+ grub_file_t file;
+ struct parsebuf p;
+
+ p.view = view;
+ p.theme_dir = grub_get_dirname (theme_path);
+
+ file = grub_file_open (theme_path, GRUB_FILE_TYPE_THEME);
+ if (! file)
+ {
+ grub_free (p.theme_dir);
+ return grub_errno;
+ }
+
+ p.len = grub_file_size (file);
+ p.buf = grub_malloc (p.len);
+ p.pos = 0;
+ p.line_num = 1;
+ p.col_num = 1;
+ p.filename = theme_path;
+ if (! p.buf)
+ {
+ grub_file_close (file);
+ grub_free (p.theme_dir);
+ return grub_errno;
+ }
+ if (grub_file_read (file, p.buf, p.len) != p.len)
+ {
+ grub_free (p.buf);
+ grub_file_close (file);
+ grub_free (p.theme_dir);
+ return grub_errno;
+ }
+
+ if (view->canvas)
+ view->canvas->component.ops->destroy (view->canvas);
+
+ view->canvas = grub_gui_canvas_new ();
+ if (!view->canvas)
+ goto fail;
+ ((grub_gui_component_t) view->canvas)
+ ->ops->set_bounds ((grub_gui_component_t) view->canvas,
+ &view->screen);
+
+ while (has_more (&p))
+ {
+ /* Skip comments (lines beginning with #). */
+ if (peek_char (&p) == '#')
+ {
+ advance_to_next_line (&p);
+ continue;
+ }
+
+ /* Find the first non-whitespace character. */
+ skip_whitespace (&p);
+
+ /* Handle the content. */
+ if (peek_char (&p) == '+')
+ {
+ /* Skip the '+'. */
+ read_char (&p);
+ read_object (&p, view->canvas);
+ }
+ else
+ {
+ read_property (&p);
+ }
+
+ if (grub_errno != GRUB_ERR_NONE)
+ goto fail;
+ }
+
+ /* Set the new theme path. */
+ grub_free (view->theme_path);
+ view->theme_path = grub_strdup (theme_path);
+ goto cleanup;
+
+fail:
+ if (view->canvas)
+ {
+ view->canvas->component.ops->destroy (view->canvas);
+ view->canvas = 0;
+ }
+
+cleanup:
+ grub_free (p.buf);
+ grub_file_close (file);
+ grub_free (p.theme_dir);
+ return grub_errno;
+}
diff --git a/grub-core/gfxmenu/view.c b/grub-core/gfxmenu/view.c
new file mode 100644
index 0000000..ad5e82b
--- /dev/null
+++ b/grub-core/gfxmenu/view.c
@@ -0,0 +1,647 @@
+/* view.c - Graphical menu interface MVC view. */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2008 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/types.h>
+#include <grub/file.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/err.h>
+#include <grub/dl.h>
+#include <grub/normal.h>
+#include <grub/video.h>
+#include <grub/gfxterm.h>
+#include <grub/bitmap.h>
+#include <grub/bitmap_scale.h>
+#include <grub/term.h>
+#include <grub/gfxwidgets.h>
+#include <grub/time.h>
+#include <grub/menu.h>
+#include <grub/menu_viewer.h>
+#include <grub/gfxmenu_view.h>
+#include <grub/gui_string_util.h>
+#include <grub/icon_manager.h>
+#include <grub/i18n.h>
+
+static void
+init_terminal (grub_gfxmenu_view_t view);
+static void
+init_background (grub_gfxmenu_view_t view);
+static grub_gfxmenu_view_t term_view;
+
+/* Create a new view object, loading the theme specified by THEME_PATH and
+ associating MODEL with the view. */
+grub_gfxmenu_view_t
+grub_gfxmenu_view_new (const char *theme_path,
+ int width, int height)
+{
+ grub_gfxmenu_view_t view;
+ grub_font_t default_font;
+ grub_video_rgba_color_t default_fg_color;
+ grub_video_rgba_color_t default_bg_color;
+
+ view = grub_malloc (sizeof (*view));
+ if (! view)
+ return 0;
+
+ while (grub_gfxmenu_timeout_notifications)
+ {
+ struct grub_gfxmenu_timeout_notify *p;
+ p = grub_gfxmenu_timeout_notifications;
+ grub_gfxmenu_timeout_notifications = grub_gfxmenu_timeout_notifications->next;
+ grub_free (p);
+ }
+
+ view->screen.x = 0;
+ view->screen.y = 0;
+ view->screen.width = width;
+ view->screen.height = height;
+
+ view->need_to_check_sanity = 1;
+ view->terminal_border = 3;
+ view->terminal_rect.width = view->screen.width * 7 / 10;
+ view->terminal_rect.height = view->screen.height * 7 / 10;
+ view->terminal_rect.x = view->screen.x + (view->screen.width
+ - view->terminal_rect.width) / 2;
+ view->terminal_rect.y = view->screen.y + (view->screen.height
+ - view->terminal_rect.height) / 2;
+
+ default_font = grub_font_get ("Unknown Regular 16");
+ default_fg_color = grub_video_rgba_color_rgb (0, 0, 0);
+ default_bg_color = grub_video_rgba_color_rgb (255, 255, 255);
+
+ view->canvas = 0;
+
+ view->title_font = default_font;
+ view->message_font = default_font;
+ view->terminal_font_name = grub_strdup ("Fixed 10");
+ view->title_color = default_fg_color;
+ view->message_color = default_bg_color;
+ view->message_bg_color = default_fg_color;
+ view->raw_desktop_image = 0;
+ view->scaled_desktop_image = 0;
+ view->desktop_image_scale_method = GRUB_VIDEO_BITMAP_SELECTION_METHOD_STRETCH;
+ view->desktop_image_h_align = GRUB_VIDEO_BITMAP_H_ALIGN_CENTER;
+ view->desktop_image_v_align = GRUB_VIDEO_BITMAP_V_ALIGN_CENTER;
+ view->desktop_color = default_bg_color;
+ view->terminal_box = grub_gfxmenu_create_box (0, 0);
+ view->title_text = grub_strdup (_("GRUB Boot Menu"));
+ view->progress_message_text = 0;
+ view->theme_path = 0;
+
+ /* Set the timeout bar's frame. */
+ view->progress_message_frame.width = view->screen.width * 4 / 5;
+ view->progress_message_frame.height = 50;
+ view->progress_message_frame.x = view->screen.x
+ + (view->screen.width - view->progress_message_frame.width) / 2;
+ view->progress_message_frame.y = view->screen.y
+ + view->screen.height - 90 - 20 - view->progress_message_frame.height;
+
+ if (grub_gfxmenu_view_load_theme (view, theme_path) != 0)
+ {
+ grub_gfxmenu_view_destroy (view);
+ return 0;
+ }
+
+ return view;
+}
+
+/* Destroy the view object. All used memory is freed. */
+void
+grub_gfxmenu_view_destroy (grub_gfxmenu_view_t view)
+{
+ if (!view)
+ return;
+ while (grub_gfxmenu_timeout_notifications)
+ {
+ struct grub_gfxmenu_timeout_notify *p;
+ p = grub_gfxmenu_timeout_notifications;
+ grub_gfxmenu_timeout_notifications = grub_gfxmenu_timeout_notifications->next;
+ grub_free (p);
+ }
+ grub_video_bitmap_destroy (view->raw_desktop_image);
+ grub_video_bitmap_destroy (view->scaled_desktop_image);
+ if (view->terminal_box)
+ view->terminal_box->destroy (view->terminal_box);
+ grub_free (view->terminal_font_name);
+ grub_free (view->title_text);
+ grub_free (view->progress_message_text);
+ grub_free (view->theme_path);
+ if (view->canvas)
+ view->canvas->component.ops->destroy (view->canvas);
+ grub_free (view);
+}
+
+static void
+redraw_background (grub_gfxmenu_view_t view,
+ const grub_video_rect_t *bounds)
+{
+ if (view->scaled_desktop_image)
+ {
+ struct grub_video_bitmap *img = view->scaled_desktop_image;
+ grub_video_blit_bitmap (img, GRUB_VIDEO_BLIT_REPLACE,
+ bounds->x, bounds->y,
+ bounds->x - view->screen.x,
+ bounds->y - view->screen.y,
+ bounds->width, bounds->height);
+ }
+ else
+ {
+ grub_video_fill_rect (grub_video_map_rgba_color (view->desktop_color),
+ bounds->x, bounds->y,
+ bounds->width, bounds->height);
+ }
+}
+
+static void
+draw_title (grub_gfxmenu_view_t view)
+{
+ if (! view->title_text)
+ return;
+
+ /* Center the title. */
+ int title_width = grub_font_get_string_width (view->title_font,
+ view->title_text);
+ int x = (view->screen.width - title_width) / 2;
+ int y = 40 + grub_font_get_ascent (view->title_font);
+ grub_font_draw_string (view->title_text,
+ view->title_font,
+ grub_video_map_rgba_color (view->title_color),
+ x, y);
+}
+
+struct progress_value_data
+{
+ int visible;
+ int start;
+ int end;
+ int value;
+};
+
+struct grub_gfxmenu_timeout_notify *grub_gfxmenu_timeout_notifications;
+
+static void
+update_timeouts (int visible, int start, int value, int end)
+{
+ struct grub_gfxmenu_timeout_notify *cur;
+
+ for (cur = grub_gfxmenu_timeout_notifications; cur; cur = cur->next)
+ cur->set_state (cur->self, visible, start, value, end);
+}
+
+static void
+redraw_timeouts (struct grub_gfxmenu_view *view)
+{
+ struct grub_gfxmenu_timeout_notify *cur;
+
+ for (cur = grub_gfxmenu_timeout_notifications; cur; cur = cur->next)
+ {
+ grub_video_rect_t bounds;
+ cur->self->ops->get_bounds (cur->self, &bounds);
+ grub_video_set_area_status (GRUB_VIDEO_AREA_ENABLED);
+ grub_gfxmenu_view_redraw (view, &bounds);
+ }
+}
+
+void
+grub_gfxmenu_print_timeout (int timeout, void *data)
+{
+ struct grub_gfxmenu_view *view = data;
+
+ if (view->first_timeout == -1)
+ view->first_timeout = timeout;
+
+ update_timeouts (1, -view->first_timeout, -timeout, 0);
+ redraw_timeouts (view);
+ grub_video_swap_buffers ();
+ if (view->double_repaint)
+ redraw_timeouts (view);
+}
+
+void
+grub_gfxmenu_clear_timeout (void *data)
+{
+ struct grub_gfxmenu_view *view = data;
+
+ update_timeouts (0, 1, 0, 0);
+ redraw_timeouts (view);
+ grub_video_swap_buffers ();
+ if (view->double_repaint)
+ redraw_timeouts (view);
+}
+
+static void
+update_menu_visit (grub_gui_component_t component,
+ void *userdata)
+{
+ grub_gfxmenu_view_t view;
+ view = userdata;
+ if (component->ops->is_instance (component, "list"))
+ {
+ grub_gui_list_t list = (grub_gui_list_t) component;
+ list->ops->set_view_info (list, view);
+ }
+}
+
+/* Update any boot menu components with the current menu model and
+ theme path. */
+static void
+update_menu_components (grub_gfxmenu_view_t view)
+{
+ grub_gui_iterate_recursively ((grub_gui_component_t) view->canvas,
+ update_menu_visit, view);
+}
+
+static void
+refresh_menu_visit (grub_gui_component_t component,
+ void *userdata)
+{
+ grub_gfxmenu_view_t view;
+ view = userdata;
+ if (component->ops->is_instance (component, "list"))
+ {
+ grub_gui_list_t list = (grub_gui_list_t) component;
+ list->ops->refresh_list (list, view);
+ }
+}
+
+/* Refresh list information (useful for submenus) */
+static void
+refresh_menu_components (grub_gfxmenu_view_t view)
+{
+ grub_gui_iterate_recursively ((grub_gui_component_t) view->canvas,
+ refresh_menu_visit, view);
+}
+
+static void
+draw_message (grub_gfxmenu_view_t view)
+{
+ char *text = view->progress_message_text;
+ grub_video_rect_t f = view->progress_message_frame;
+ if (! text)
+ return;
+
+ grub_font_t font = view->message_font;
+ grub_video_color_t color = grub_video_map_rgba_color (view->message_color);
+
+ /* Border. */
+ grub_video_fill_rect (color,
+ f.x-1, f.y-1, f.width+2, f.height+2);
+ /* Fill. */
+ grub_video_fill_rect (grub_video_map_rgba_color (view->message_bg_color),
+ f.x, f.y, f.width, f.height);
+
+ /* Center the text. */
+ int text_width = grub_font_get_string_width (font, text);
+ int x = f.x + (f.width - text_width) / 2;
+ int y = (f.y + (f.height - grub_font_get_descent (font)) / 2
+ + grub_font_get_ascent (font) / 2);
+ grub_font_draw_string (text, font, color, x, y);
+}
+
+void
+grub_gfxmenu_view_redraw (grub_gfxmenu_view_t view,
+ const grub_video_rect_t *region)
+{
+ if (grub_video_have_common_points (&view->terminal_rect, region))
+ grub_gfxterm_schedule_repaint ();
+
+ grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
+ grub_video_area_status_t area_status;
+ grub_video_get_area_status (&area_status);
+ if (area_status == GRUB_VIDEO_AREA_ENABLED)
+ grub_video_set_region (region->x, region->y,
+ region->width, region->height);
+
+ redraw_background (view, region);
+ if (view->canvas)
+ view->canvas->component.ops->paint (view->canvas, region);
+ draw_title (view);
+ if (grub_video_have_common_points (&view->progress_message_frame, region))
+ draw_message (view);
+
+ if (area_status == GRUB_VIDEO_AREA_ENABLED)
+ grub_video_set_area_status (GRUB_VIDEO_AREA_ENABLED);
+}
+
+void
+grub_gfxmenu_view_draw (grub_gfxmenu_view_t view)
+{
+ init_terminal (view);
+
+ init_background (view);
+
+ /* Clear the screen; there may be garbage left over in video memory. */
+ grub_video_fill_rect (grub_video_map_rgb (0, 0, 0),
+ view->screen.x, view->screen.y,
+ view->screen.width, view->screen.height);
+ grub_video_swap_buffers ();
+ if (view->double_repaint)
+ grub_video_fill_rect (grub_video_map_rgb (0, 0, 0),
+ view->screen.x, view->screen.y,
+ view->screen.width, view->screen.height);
+
+ refresh_menu_components (view);
+ update_menu_components (view);
+
+ grub_video_set_area_status (GRUB_VIDEO_AREA_DISABLED);
+ grub_gfxmenu_view_redraw (view, &view->screen);
+ grub_video_swap_buffers ();
+ if (view->double_repaint)
+ {
+ grub_video_set_area_status (GRUB_VIDEO_AREA_DISABLED);
+ grub_gfxmenu_view_redraw (view, &view->screen);
+ }
+
+}
+
+static void
+redraw_menu_visit (grub_gui_component_t component,
+ void *userdata)
+{
+ grub_gfxmenu_view_t view;
+ view = userdata;
+ if (component->ops->is_instance (component, "list"))
+ {
+ grub_video_rect_t bounds;
+
+ component->ops->get_bounds (component, &bounds);
+ grub_video_set_area_status (GRUB_VIDEO_AREA_ENABLED);
+ grub_gfxmenu_view_redraw (view, &bounds);
+ }
+}
+
+void
+grub_gfxmenu_redraw_menu (grub_gfxmenu_view_t view)
+{
+ update_menu_components (view);
+
+ grub_gui_iterate_recursively ((grub_gui_component_t) view->canvas,
+ redraw_menu_visit, view);
+ grub_video_swap_buffers ();
+ if (view->double_repaint)
+ {
+ grub_gui_iterate_recursively ((grub_gui_component_t) view->canvas,
+ redraw_menu_visit, view);
+ }
+}
+
+void
+grub_gfxmenu_set_chosen_entry (int entry, void *data)
+{
+ grub_gfxmenu_view_t view = data;
+
+ view->selected = entry;
+ grub_gfxmenu_redraw_menu (view);
+}
+
+static void
+grub_gfxmenu_draw_terminal_box (void)
+{
+ grub_gfxmenu_box_t term_box;
+
+ term_box = term_view->terminal_box;
+ if (!term_box)
+ return;
+
+ grub_video_set_area_status (GRUB_VIDEO_AREA_DISABLED);
+
+ term_box->set_content_size (term_box, term_view->terminal_rect.width,
+ term_view->terminal_rect.height);
+
+ term_box->draw (term_box,
+ term_view->terminal_rect.x - term_box->get_left_pad (term_box),
+ term_view->terminal_rect.y - term_box->get_top_pad (term_box));
+}
+
+static void
+get_min_terminal (grub_font_t terminal_font,
+ unsigned int border_width,
+ unsigned int *min_terminal_width,
+ unsigned int *min_terminal_height)
+{
+ struct grub_font_glyph *glyph;
+ glyph = grub_font_get_glyph (terminal_font, 'M');
+ *min_terminal_width = (glyph? glyph->device_width : 8) * 80
+ + 2 * border_width;
+ *min_terminal_height = grub_font_get_max_char_height (terminal_font) * 24
+ + 2 * border_width;
+}
+
+static void
+terminal_sanity_check (grub_gfxmenu_view_t view)
+{
+ if (!view->need_to_check_sanity)
+ return;
+
+ /* terminal_font was checked before in the init_terminal function. */
+ grub_font_t terminal_font = grub_font_get (view->terminal_font_name);
+
+ /* Non-negative numbers below. */
+ int scr_x = view->screen.x;
+ int scr_y = view->screen.y;
+ int scr_width = view->screen.width;
+ int scr_height = view->screen.height;
+ int term_x = view->terminal_rect.x;
+ int term_y = view->terminal_rect.y;
+ int term_width = view->terminal_rect.width;
+ int term_height = view->terminal_rect.height;
+
+ /* Check that border_width isn't too big. */
+ unsigned int border_width = view->terminal_border;
+ unsigned int min_terminal_width;
+ unsigned int min_terminal_height;
+ get_min_terminal (terminal_font, border_width,
+ &min_terminal_width, &min_terminal_height);
+ if (border_width > 3 && ((int) min_terminal_width >= scr_width
+ || (int) min_terminal_height >= scr_height))
+ {
+ border_width = 3;
+ get_min_terminal (terminal_font, border_width,
+ &min_terminal_width, &min_terminal_height);
+ }
+
+ /* Sanity checks. */
+ if (term_width > scr_width)
+ term_width = scr_width;
+ if (term_height > scr_height)
+ term_height = scr_height;
+
+ if (scr_width <= (int) min_terminal_width
+ || scr_height <= (int) min_terminal_height)
+ {
+ /* The screen resulution is too low. Use all space, except a small border
+ to show the user, that it is a window. Then center the window. */
+ term_width = scr_width - 6 * border_width;
+ term_height = scr_height - 6 * border_width;
+ term_x = scr_x + (scr_width - term_width) / 2;
+ term_y = scr_y + (scr_height - term_height) / 2;
+ }
+ else if (term_width < (int) min_terminal_width
+ || term_height < (int) min_terminal_height)
+ {
+ /* The screen resolution is big enough. Make sure, that terminal screen
+ dimensions aren't less than minimal values. Then center the window. */
+ term_width = (int) min_terminal_width;
+ term_height = (int) min_terminal_height;
+ term_x = scr_x + (scr_width - term_width) / 2;
+ term_y = scr_y + (scr_height - term_height) / 2;
+ }
+
+ /* At this point w and h are satisfying. */
+ if (term_x + term_width > scr_width)
+ term_x = scr_width - term_width;
+ if (term_y + term_height > scr_height)
+ term_y = scr_height - term_height;
+
+ /* Write down corrected data. */
+ view->terminal_rect.x = (unsigned int) term_x;
+ view->terminal_rect.y = (unsigned int) term_y;
+ view->terminal_rect.width = (unsigned int) term_width;
+ view->terminal_rect.height = (unsigned int) term_height;
+ view->terminal_border = border_width;
+
+ view->need_to_check_sanity = 0;
+}
+
+static void
+init_terminal (grub_gfxmenu_view_t view)
+{
+ grub_font_t terminal_font;
+
+ terminal_font = grub_font_get (view->terminal_font_name);
+ if (!terminal_font)
+ {
+ grub_error (GRUB_ERR_BAD_FONT, "no font loaded");
+ return;
+ }
+
+ /* Check that terminal window size and position are sane. */
+ terminal_sanity_check (view);
+
+ term_view = view;
+
+ /* Note: currently there is no API for changing the gfxterm font
+ on the fly, so whatever font the initially loaded theme specifies
+ will be permanent. */
+ grub_gfxterm_set_window (GRUB_VIDEO_RENDER_TARGET_DISPLAY,
+ view->terminal_rect.x,
+ view->terminal_rect.y,
+ view->terminal_rect.width,
+ view->terminal_rect.height,
+ view->double_repaint,
+ terminal_font,
+ view->terminal_border);
+ grub_gfxterm_decorator_hook = grub_gfxmenu_draw_terminal_box;
+}
+
+static void
+init_background (grub_gfxmenu_view_t view)
+{
+ if (view->scaled_desktop_image)
+ return;
+
+ struct grub_video_bitmap *scaled_bitmap;
+ if (view->desktop_image_scale_method ==
+ GRUB_VIDEO_BITMAP_SELECTION_METHOD_STRETCH)
+ grub_video_bitmap_create_scaled (&scaled_bitmap,
+ view->screen.width,
+ view->screen.height,
+ view->raw_desktop_image,
+ GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST);
+ else
+ grub_video_bitmap_scale_proportional (&scaled_bitmap,
+ view->screen.width,
+ view->screen.height,
+ view->raw_desktop_image,
+ GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST,
+ view->desktop_image_scale_method,
+ view->desktop_image_v_align,
+ view->desktop_image_h_align);
+ if (! scaled_bitmap)
+ return;
+ view->scaled_desktop_image = scaled_bitmap;
+
+}
+
+/* FIXME: previously notifications were displayed in special case.
+ Is it necessary?
+ */
+#if 0
+/* Sets MESSAGE as the progress message for the view.
+ MESSAGE can be 0, in which case no message is displayed. */
+static void
+set_progress_message (grub_gfxmenu_view_t view, const char *message)
+{
+ grub_free (view->progress_message_text);
+ if (message)
+ view->progress_message_text = grub_strdup (message);
+ else
+ view->progress_message_text = 0;
+}
+
+static void
+notify_booting (grub_menu_entry_t entry, void *userdata)
+{
+ grub_gfxmenu_view_t view = (grub_gfxmenu_view_t) userdata;
+
+ char *s = grub_malloc (100 + grub_strlen (entry->title));
+ if (!s)
+ return;
+
+ grub_sprintf (s, "Booting '%s'", entry->title);
+ set_progress_message (view, s);
+ grub_free (s);
+ grub_gfxmenu_view_redraw (view, &view->progress_message_frame);
+ grub_video_swap_buffers ();
+ if (view->double_repaint)
+ grub_gfxmenu_view_redraw (view, &view->progress_message_frame);
+}
+
+static void
+notify_fallback (grub_menu_entry_t entry, void *userdata)
+{
+ grub_gfxmenu_view_t view = (grub_gfxmenu_view_t) userdata;
+
+ char *s = grub_malloc (100 + grub_strlen (entry->title));
+ if (!s)
+ return;
+
+ grub_sprintf (s, "Falling back to '%s'", entry->title);
+ set_progress_message (view, s);
+ grub_free (s);
+ grub_gfxmenu_view_redraw (view, &view->progress_message_frame);
+ grub_video_swap_buffers ();
+ if (view->double_repaint)
+ grub_gfxmenu_view_redraw (view, &view->progress_message_frame);
+}
+
+static void
+notify_execution_failure (void *userdata __attribute__ ((unused)))
+{
+}
+
+
+static struct grub_menu_execute_callback execute_callback =
+{
+ .notify_booting = notify_booting,
+ .notify_fallback = notify_fallback,
+ .notify_failure = notify_execution_failure
+};
+
+#endif
diff --git a/grub-core/gfxmenu/widget-box.c b/grub-core/gfxmenu/widget-box.c
new file mode 100644
index 0000000..470597d
--- /dev/null
+++ b/grub-core/gfxmenu/widget-box.c
@@ -0,0 +1,360 @@
+/* widget_box.c - Pixmap-stylized box widget. */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2008,2009 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/types.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/err.h>
+#include <grub/video.h>
+#include <grub/bitmap.h>
+#include <grub/bitmap_scale.h>
+#include <grub/gfxwidgets.h>
+
+enum box_pixmaps
+{
+ BOX_PIXMAP_NW, BOX_PIXMAP_NE, BOX_PIXMAP_SE, BOX_PIXMAP_SW,
+ BOX_PIXMAP_N, BOX_PIXMAP_E, BOX_PIXMAP_S, BOX_PIXMAP_W,
+ BOX_PIXMAP_CENTER
+};
+
+static const char *box_pixmap_names[] = {
+ /* Corners: */
+ "nw", "ne", "se", "sw",
+ /* Sides: */
+ "n", "e", "s", "w",
+ /* Center: */
+ "c"
+};
+
+#define BOX_NUM_PIXMAPS (sizeof(box_pixmap_names)/sizeof(*box_pixmap_names))
+
+static int
+get_height (struct grub_video_bitmap *bitmap)
+{
+ if (bitmap)
+ return grub_video_bitmap_get_height (bitmap);
+ else
+ return 0;
+}
+
+static int
+get_width (struct grub_video_bitmap *bitmap)
+{
+ if (bitmap)
+ return grub_video_bitmap_get_width (bitmap);
+ else
+ return 0;
+}
+
+static void
+blit (grub_gfxmenu_box_t self, int pixmap_index, int x, int y)
+{
+ struct grub_video_bitmap *bitmap;
+ bitmap = self->scaled_pixmaps[pixmap_index];
+ if (! bitmap)
+ return;
+ grub_video_blit_bitmap (bitmap, GRUB_VIDEO_BLIT_BLEND,
+ x, y, 0, 0,
+ grub_video_bitmap_get_width (bitmap),
+ grub_video_bitmap_get_height (bitmap));
+}
+
+static void
+draw (grub_gfxmenu_box_t self, int x, int y)
+{
+ int height_n;
+ int width_w;
+ int tmp;
+
+ /* Count maximum height of NW, N, NE. */
+ height_n = get_height (self->scaled_pixmaps[BOX_PIXMAP_NW]);
+ tmp = get_height (self->scaled_pixmaps[BOX_PIXMAP_N]);
+ if (tmp > height_n)
+ height_n = tmp;
+ tmp = get_height (self->scaled_pixmaps[BOX_PIXMAP_NE]);
+ if (tmp > height_n)
+ height_n = tmp;
+
+ /* Count maximum width of NW, W, SW. */
+ width_w = get_width (self->scaled_pixmaps[BOX_PIXMAP_NW]);
+ tmp = get_width (self->scaled_pixmaps[BOX_PIXMAP_W]);
+ if (tmp > width_w)
+ width_w = tmp;
+ tmp = get_width (self->scaled_pixmaps[BOX_PIXMAP_SW]);
+ if (tmp > width_w)
+ width_w = tmp;
+
+ /* Draw sides. */
+ blit (self, BOX_PIXMAP_N, x + width_w, y);
+ blit (self, BOX_PIXMAP_S, x + width_w, y + height_n + self->content_height);
+ blit (self, BOX_PIXMAP_E, x + width_w + self->content_width, y + height_n);
+ blit (self, BOX_PIXMAP_W, x, y + height_n);
+
+ /* Draw corners. */
+ blit (self, BOX_PIXMAP_NW, x, y);
+ blit (self, BOX_PIXMAP_NE, x + width_w + self->content_width, y);
+ blit (self, BOX_PIXMAP_SE,
+ x + width_w + self->content_width,
+ y + height_n + self->content_height);
+ blit (self, BOX_PIXMAP_SW, x, y + height_n + self->content_height);
+
+ /* Draw center. */
+ blit (self, BOX_PIXMAP_CENTER, x + width_w, y + height_n);
+}
+
+static grub_err_t
+scale_pixmap (grub_gfxmenu_box_t self, int i, int w, int h)
+{
+ struct grub_video_bitmap **scaled = &self->scaled_pixmaps[i];
+ struct grub_video_bitmap *raw = self->raw_pixmaps[i];
+
+ if (raw == 0)
+ return grub_errno;
+
+ if (w == -1)
+ w = grub_video_bitmap_get_width (raw);
+ if (h == -1)
+ h = grub_video_bitmap_get_height (raw);
+
+ if (*scaled == 0
+ || ((int) grub_video_bitmap_get_width (*scaled) != w)
+ || ((int) grub_video_bitmap_get_height (*scaled) != h))
+ {
+ if (*scaled)
+ {
+ grub_video_bitmap_destroy (*scaled);
+ *scaled = 0;
+ }
+
+ /* Don't try to create a bitmap with a zero dimension. */
+ if (w != 0 && h != 0)
+ grub_video_bitmap_create_scaled (scaled, w, h, raw,
+ GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST);
+ }
+
+ return grub_errno;
+}
+
+static void
+set_content_size (grub_gfxmenu_box_t self,
+ int width, int height)
+{
+ self->content_width = width;
+ self->content_height = height;
+
+ /* Resize sides to match the width and height. */
+ /* It is assumed that the corners width/height match the adjacent sides. */
+
+ /* Resize N and S sides to match width. */
+ if (scale_pixmap(self, BOX_PIXMAP_N, width, -1) != GRUB_ERR_NONE)
+ return;
+ if (scale_pixmap(self, BOX_PIXMAP_S, width, -1) != GRUB_ERR_NONE)
+ return;
+
+ /* Resize E and W sides to match height. */
+ if (scale_pixmap(self, BOX_PIXMAP_E, -1, height) != GRUB_ERR_NONE)
+ return;
+ if (scale_pixmap(self, BOX_PIXMAP_W, -1, height) != GRUB_ERR_NONE)
+ return;
+
+ /* Don't scale the corners--they are assumed to match the sides. */
+ if (scale_pixmap(self, BOX_PIXMAP_NW, -1, -1) != GRUB_ERR_NONE)
+ return;
+ if (scale_pixmap(self, BOX_PIXMAP_SW, -1, -1) != GRUB_ERR_NONE)
+ return;
+ if (scale_pixmap(self, BOX_PIXMAP_NE, -1, -1) != GRUB_ERR_NONE)
+ return;
+ if (scale_pixmap(self, BOX_PIXMAP_SE, -1, -1) != GRUB_ERR_NONE)
+ return;
+
+ /* Scale the center area. */
+ if (scale_pixmap(self, BOX_PIXMAP_CENTER, width, height) != GRUB_ERR_NONE)
+ return;
+}
+
+static int
+get_border_width (grub_gfxmenu_box_t self)
+{
+ return (get_width (self->raw_pixmaps[BOX_PIXMAP_E])
+ + get_width (self->raw_pixmaps[BOX_PIXMAP_W]));
+}
+
+static int
+get_left_pad (grub_gfxmenu_box_t self)
+{
+ int v, c;
+
+ v = get_width (self->raw_pixmaps[BOX_PIXMAP_W]);
+ c = get_width (self->raw_pixmaps[BOX_PIXMAP_NW]);
+ if (c > v)
+ v = c;
+ c = get_width (self->raw_pixmaps[BOX_PIXMAP_SW]);
+ if (c > v)
+ v = c;
+
+ return v;
+}
+
+static int
+get_top_pad (grub_gfxmenu_box_t self)
+{
+ int v, c;
+
+ v = get_height (self->raw_pixmaps[BOX_PIXMAP_N]);
+ c = get_height (self->raw_pixmaps[BOX_PIXMAP_NW]);
+ if (c > v)
+ v = c;
+ c = get_height (self->raw_pixmaps[BOX_PIXMAP_NE]);
+ if (c > v)
+ v = c;
+
+ return v;
+}
+
+static int
+get_right_pad (grub_gfxmenu_box_t self)
+{
+ int v, c;
+
+ v = get_width (self->raw_pixmaps[BOX_PIXMAP_E]);
+ c = get_width (self->raw_pixmaps[BOX_PIXMAP_NE]);
+ if (c > v)
+ v = c;
+ c = get_width (self->raw_pixmaps[BOX_PIXMAP_SE]);
+ if (c > v)
+ v = c;
+
+ return v;
+}
+
+static int
+get_bottom_pad (grub_gfxmenu_box_t self)
+{
+ int v, c;
+
+ v = get_height (self->raw_pixmaps[BOX_PIXMAP_S]);
+ c = get_height (self->raw_pixmaps[BOX_PIXMAP_SW]);
+ if (c > v)
+ v = c;
+ c = get_height (self->raw_pixmaps[BOX_PIXMAP_SE]);
+ if (c > v)
+ v = c;
+
+ return v;
+}
+
+static void
+destroy (grub_gfxmenu_box_t self)
+{
+ unsigned i;
+ for (i = 0; i < BOX_NUM_PIXMAPS; i++)
+ {
+ if (self->raw_pixmaps[i])
+ grub_video_bitmap_destroy(self->raw_pixmaps[i]);
+ self->raw_pixmaps[i] = 0;
+
+ if (self->scaled_pixmaps[i])
+ grub_video_bitmap_destroy(self->scaled_pixmaps[i]);
+ self->scaled_pixmaps[i] = 0;
+ }
+ grub_free (self->raw_pixmaps);
+ self->raw_pixmaps = 0;
+ grub_free (self->scaled_pixmaps);
+ self->scaled_pixmaps = 0;
+
+ /* Free self: must be the last step! */
+ grub_free (self);
+}
+
+
+/* Create a new box. If PIXMAPS_PREFIX and PIXMAPS_SUFFIX are both non-null,
+ then an attempt is made to load the north, south, east, west, northwest,
+ northeast, southeast, southwest, and center pixmaps.
+ If either PIXMAPS_PREFIX or PIXMAPS_SUFFIX is 0, then no pixmaps are
+ loaded, and the box has zero-width borders and is drawn transparent. */
+grub_gfxmenu_box_t
+grub_gfxmenu_create_box (const char *pixmaps_prefix,
+ const char *pixmaps_suffix)
+{
+ unsigned i;
+ grub_gfxmenu_box_t box;
+
+ box = (grub_gfxmenu_box_t) grub_malloc (sizeof (*box));
+ if (! box)
+ return 0;
+
+ box->content_width = 0;
+ box->content_height = 0;
+ box->raw_pixmaps =
+ (struct grub_video_bitmap **)
+ grub_calloc (BOX_NUM_PIXMAPS, sizeof (struct grub_video_bitmap *));
+ box->scaled_pixmaps =
+ (struct grub_video_bitmap **)
+ grub_calloc (BOX_NUM_PIXMAPS, sizeof (struct grub_video_bitmap *));
+
+ /* Initialize all pixmap pointers to NULL so that proper destruction can
+ be performed if an error is encountered partway through construction. */
+ for (i = 0; i < BOX_NUM_PIXMAPS; i++)
+ box->raw_pixmaps[i] = 0;
+ for (i = 0; i < BOX_NUM_PIXMAPS; i++)
+ box->scaled_pixmaps[i] = 0;
+
+ /* Load the pixmaps. */
+ for (i = 0; i < BOX_NUM_PIXMAPS; i++)
+ {
+ if (pixmaps_prefix && pixmaps_suffix)
+ {
+ char *path;
+ char *path_end;
+
+ path = grub_malloc (grub_strlen (pixmaps_prefix)
+ + grub_strlen (box_pixmap_names[i])
+ + grub_strlen (pixmaps_suffix)
+ + 1);
+ if (! path)
+ goto fail_and_destroy;
+
+ /* Construct the specific path for this pixmap. */
+ path_end = grub_stpcpy (path, pixmaps_prefix);
+ path_end = grub_stpcpy (path_end, box_pixmap_names[i]);
+ path_end = grub_stpcpy (path_end, pixmaps_suffix);
+
+ grub_video_bitmap_load (&box->raw_pixmaps[i], path);
+ grub_free (path);
+
+ /* Ignore missing pixmaps. */
+ grub_errno = GRUB_ERR_NONE;
+ }
+ }
+
+ box->draw = draw;
+ box->set_content_size = set_content_size;
+ box->get_border_width = get_border_width;
+
+ box->get_left_pad = get_left_pad;
+ box->get_top_pad = get_top_pad;
+ box->get_right_pad = get_right_pad;
+ box->get_bottom_pad = get_bottom_pad;
+ box->destroy = destroy;
+ return box;
+
+fail_and_destroy:
+ destroy (box);
+ return 0;
+}