gfxmenu import
This commit is contained in:
parent
bd86691a07
commit
d920a32ab6
25 changed files with 5977 additions and 1 deletions
|
@ -417,6 +417,28 @@ hello_mod_SOURCES = hello/hello.c
|
|||
hello_mod_CFLAGS = $(COMMON_CFLAGS)
|
||||
hello_mod_LDFLAGS = $(COMMON_LDFLAGS)
|
||||
|
||||
# For gfxmenu.mod.
|
||||
pkglib_MODULES += gfxmenu.mod
|
||||
gfxmenu_mod_SOURCES = \
|
||||
gfxmenu/gfxmenu.c \
|
||||
gfxmenu/model.c \
|
||||
gfxmenu/view.c \
|
||||
gfxmenu/icon_manager.c \
|
||||
gfxmenu/theme_loader.c \
|
||||
gfxmenu/widget-box.c \
|
||||
gfxmenu/gui_canvas.c \
|
||||
gfxmenu/gui_circular_progress.c \
|
||||
gfxmenu/gui_box.c \
|
||||
gfxmenu/gui_label.c \
|
||||
gfxmenu/gui_list.c \
|
||||
gfxmenu/gui_image.c \
|
||||
gfxmenu/gui_progress_bar.c \
|
||||
gfxmenu/gui_util.c \
|
||||
gfxmenu/gui_string_util.c \
|
||||
gfxmenu/named_colors.c
|
||||
gfxmenu_mod_CFLAGS = $(COMMON_CFLAGS)
|
||||
gfxmenu_mod_LDFLAGS = $(COMMON_LDFLAGS)
|
||||
|
||||
# For parttool.mod.
|
||||
parttool_mod_SOURCES = commands/parttool.c
|
||||
parttool_mod_CFLAGS = $(COMMON_CFLAGS)
|
||||
|
|
128
docs/gfxmenu-theme-example.txt
Normal file
128
docs/gfxmenu-theme-example.txt
Normal file
|
@ -0,0 +1,128 @@
|
|||
# GRUB gfxmenu theme "winter".
|
||||
# Uses background image from:
|
||||
# http://www.cyberpunkcafe.com/e107_plugins/autogallery/autogallery.php?show=1.Open%20Source%20Wallpaper
|
||||
# "without-leaves.png" was called "Without Leafs in Winter.png"
|
||||
|
||||
lua-script: "winter.lua"
|
||||
title-text: ""
|
||||
title-font: "Helvetica Bold 18"
|
||||
status-font: "Helvetica 8"
|
||||
terminal-font: "Fixed 9"
|
||||
title-color: "40, 40, 40"
|
||||
status-color: "#FFF"
|
||||
status-bg-color: "0, 166, 183, 128"
|
||||
desktop-image: "without-leaves.png"
|
||||
desktop-color: "0, 154, 183"
|
||||
terminal-box: "terminal_*.png"
|
||||
|
||||
+ boot_menu {
|
||||
position = (120, 60)
|
||||
preferred_size = (400, -1)
|
||||
item_font = "Helvetica Bold 14"
|
||||
selected_item_font = "Helvetica Bold 14"
|
||||
item_color = "0, 0, 0"
|
||||
selected_item_color = "203, 251, 255"
|
||||
menu_pixmap_style = "menu_*.png"
|
||||
selected_item_pixmap_style = "select_*.png"
|
||||
icon_width = 44
|
||||
icon_height = 44
|
||||
item_height = 32
|
||||
item_padding = 0
|
||||
item_icon_space = 3
|
||||
item_spacing = 11
|
||||
}
|
||||
|
||||
# You can add text at arbitrary locations on the screen.
|
||||
# The specification within the "+label {...}" block is free-form,
|
||||
# so you can use as much or as little white space as you like.
|
||||
|
||||
+ label {
|
||||
position = (170, 50)
|
||||
font = "smoothansi 13"
|
||||
color = "0,0,128"
|
||||
text = "This is the Winter theme ... brought to you by GRUB!"
|
||||
}
|
||||
|
||||
# Show the text alignment supported by labels.
|
||||
+ vbox {
|
||||
position = (220, 347)
|
||||
preferred_size = (200, -1) # A preferred size of -1 means automatic.
|
||||
+ label { text="Text alignment demo" align="center" font="aqui 11" }
|
||||
+ label { text="Left" align="left" font="cure 11" }
|
||||
+ label { text="Center" align="center" font="cure 11" }
|
||||
+ label { text="Right" align="right" font="cure 11" }
|
||||
}
|
||||
|
||||
+ vbox {
|
||||
position = (580, 10)
|
||||
+ label { text="GNU" font="gelly 11" color="0, 0, 0" }
|
||||
+ label { text="GRUB" font="aqui 11" color="0, 0, 0" }
|
||||
+ label { text="boot loader" font="cure 11" color="0, 0, 0" }
|
||||
}
|
||||
|
||||
+ hbox {
|
||||
position = (80, 10)
|
||||
+ label { text="GNU" font="gelly 11" color="0, 0, 0" }
|
||||
+ label { text="GRUB" font="aqui 11" color="0, 0, 0" }
|
||||
+ label { text="boot loader" font="cure 11" color="0, 0, 0" }
|
||||
}
|
||||
|
||||
# Demonstration of a compound layout: boxes within boxes.
|
||||
+ hbox
|
||||
{
|
||||
position = (480, 3)
|
||||
|
||||
+ vbox
|
||||
{
|
||||
# Note: We can't just use 'size' to set the image's size,
|
||||
# since the vbox will resize the component according to its
|
||||
# preferred size, which for images is the native image size.
|
||||
|
||||
+ image { file="/boot/grub/themes/icons/ubuntu.png"
|
||||
preferred_size = (20, 20) }
|
||||
+ image { file="/boot/grub/themes/icons/gentoo.png"
|
||||
preferred_size = (20, 20) }
|
||||
}
|
||||
|
||||
+ vbox
|
||||
{
|
||||
+ label { text="GRand" font="cure 11" color=#99F }
|
||||
+ label { text="Unified" font="cure 11" color=#BBF }
|
||||
+ label { text="Bootloader" font="cure 11" color=#DDF }
|
||||
}
|
||||
}
|
||||
|
||||
# By defining a 'progress_bar' type component with an ID of '__timeout__',
|
||||
# the progress bar will be used to display the time remaining before an
|
||||
# the default entry is automatically booted.
|
||||
+ progress_bar
|
||||
{
|
||||
id = "__timeout__"
|
||||
position = (80, 393)
|
||||
preferred_size = (500, 24)
|
||||
font = "cure 11"
|
||||
text_color = #000
|
||||
fg_color = #CCF
|
||||
bg_color = #66B
|
||||
border_color = #006
|
||||
show_text = false
|
||||
}
|
||||
|
||||
# Although the progress_bar component is normally used to indicate the
|
||||
# time remaining, it's also possible to create other components with an ID
|
||||
# of '__timeout__'. All components with and ID of 'timeout_bar' will have
|
||||
# the following properties set based on the timeout value:
|
||||
# text, value, start, end, visible.
|
||||
# In this case, we have set 'show_text=false' on the progress bar, and use
|
||||
# the following label's 'text' property to display the message.
|
||||
+ label
|
||||
{
|
||||
id = "__timeout__"
|
||||
position = (80, 420)
|
||||
preferred_size = (500, 24)
|
||||
font = "lime 11"
|
||||
color = #117
|
||||
align = "center"
|
||||
}
|
||||
|
||||
|
235
gfxmenu/gfxmenu.c
Normal file
235
gfxmenu/gfxmenu.c
Normal file
|
@ -0,0 +1,235 @@
|
|||
/* 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>
|
||||
|
||||
static void switch_to_text_menu (void)
|
||||
{
|
||||
grub_env_set ("menuviewer", "text");
|
||||
}
|
||||
|
||||
static void
|
||||
process_key_press (int c,
|
||||
grub_gfxmenu_model_t model,
|
||||
grub_gfxmenu_view_t view,
|
||||
int nested,
|
||||
int *should_exit)
|
||||
{
|
||||
/* When a key is pressed, stop the timeout. */
|
||||
grub_gfxmenu_model_clear_timeout (model);
|
||||
|
||||
if (c == 'j' || c == GRUB_TERM_DOWN)
|
||||
{
|
||||
int i = grub_gfxmenu_model_get_selected_index (model);
|
||||
int num_items = grub_gfxmenu_model_get_num_entries (model);
|
||||
if (i < num_items - 1)
|
||||
{
|
||||
i++;
|
||||
grub_gfxmenu_model_set_selected_index (model, i);
|
||||
}
|
||||
}
|
||||
else if (c == 'k' || c == GRUB_TERM_UP)
|
||||
{
|
||||
int i = grub_gfxmenu_model_get_selected_index (model);
|
||||
if (i > 0)
|
||||
{
|
||||
i--;
|
||||
grub_gfxmenu_model_set_selected_index (model, i);
|
||||
}
|
||||
}
|
||||
else if (c == '\r' || c == '\n' || c == GRUB_TERM_RIGHT)
|
||||
{
|
||||
int selected = grub_gfxmenu_model_get_selected_index (model);
|
||||
int num_entries = grub_gfxmenu_model_get_num_entries (model);
|
||||
if (selected >= 0 && selected < num_entries)
|
||||
{
|
||||
grub_menu_entry_t entry =
|
||||
grub_gfxmenu_model_get_entry (model, selected);
|
||||
grub_gfxmenu_view_execute_entry (view, entry);
|
||||
}
|
||||
}
|
||||
else if (c == 'c')
|
||||
{
|
||||
grub_gfxmenu_view_run_terminal (view);
|
||||
}
|
||||
else if (c == 't')
|
||||
{
|
||||
/* The write hook for 'menuviewer' will cause
|
||||
* grub_menu_viewer_should_return to return nonzero. */
|
||||
switch_to_text_menu ();
|
||||
*should_exit = 1;
|
||||
}
|
||||
else if (c == '1')
|
||||
{
|
||||
grub_gfxmenu_view_load_theme (view,
|
||||
"/boot/grub/themes/proto/theme.txt");
|
||||
}
|
||||
else if (c == '2')
|
||||
{
|
||||
grub_gfxmenu_view_load_theme (view,
|
||||
"/boot/grub/themes/winter/theme.txt");
|
||||
}
|
||||
else if (c == '3')
|
||||
{
|
||||
grub_gfxmenu_view_load_theme (view,
|
||||
"/boot/grub/themes/ubuntu1/theme.txt");
|
||||
}
|
||||
else if (c == '4')
|
||||
{
|
||||
grub_gfxmenu_view_load_theme (view,
|
||||
"/boot/grub/themes/ubuntu2/theme.txt");
|
||||
}
|
||||
else if (nested && c == GRUB_TERM_ESC)
|
||||
{
|
||||
*should_exit = 1;
|
||||
}
|
||||
|
||||
if (grub_errno != GRUB_ERR_NONE)
|
||||
*should_exit = 1;
|
||||
}
|
||||
|
||||
static void
|
||||
handle_key_events (grub_gfxmenu_model_t model,
|
||||
grub_gfxmenu_view_t view,
|
||||
int nested,
|
||||
int *should_exit)
|
||||
{
|
||||
while ((! *should_exit) && (grub_checkkey () != -1))
|
||||
{
|
||||
int key = grub_getkey ();
|
||||
int c = GRUB_TERM_ASCII_CHAR (key);
|
||||
process_key_press (c, model, view, nested, should_exit);
|
||||
}
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
show_menu (grub_menu_t menu, int nested)
|
||||
{
|
||||
grub_gfxmenu_model_t model;
|
||||
|
||||
model = grub_gfxmenu_model_new (menu);
|
||||
if (! model)
|
||||
{
|
||||
grub_print_error ();
|
||||
grub_printf ("Initializing menu data for graphical menu failed;\n"
|
||||
"falling back to text based menu.\n");
|
||||
grub_wait_after_message ();
|
||||
switch_to_text_menu ();
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
grub_gfxmenu_view_t view;
|
||||
|
||||
/* Create the view. */
|
||||
const char *theme_path = grub_env_get ("theme");
|
||||
if (! theme_path)
|
||||
theme_path = "/boot/grub/themes/proto/theme.txt";
|
||||
|
||||
view = grub_gfxmenu_view_new (theme_path, model);
|
||||
if (! view)
|
||||
{
|
||||
grub_print_error ();
|
||||
grub_printf ("Starting graphical menu failed;\n"
|
||||
"falling back to text based menu.\n");
|
||||
grub_wait_after_message ();
|
||||
grub_gfxmenu_model_destroy (model);
|
||||
switch_to_text_menu ();
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
/* Initially select the default menu entry. */
|
||||
int default_index = grub_menu_get_default_entry_index (menu);
|
||||
grub_gfxmenu_model_set_selected_index (model, default_index);
|
||||
|
||||
/* Start the timer to execute the default entry. */
|
||||
grub_gfxmenu_model_set_timeout (model);
|
||||
|
||||
/* Main event loop. */
|
||||
int exit_requested = 0;
|
||||
while ((! exit_requested) && (! grub_menu_viewer_should_return ()))
|
||||
{
|
||||
if (grub_gfxmenu_model_timeout_expired (model))
|
||||
{
|
||||
grub_gfxmenu_model_clear_timeout (model);
|
||||
int i = grub_gfxmenu_model_get_selected_index (model);
|
||||
grub_menu_entry_t e = grub_gfxmenu_model_get_entry (model, i);
|
||||
grub_gfxmenu_view_execute_with_fallback (view, e);
|
||||
continue;
|
||||
}
|
||||
|
||||
grub_gfxmenu_view_draw (view);
|
||||
grub_video_swap_buffers ();
|
||||
handle_key_events (model, view, nested, &exit_requested);
|
||||
}
|
||||
|
||||
grub_gfxmenu_view_destroy (view);
|
||||
grub_gfxmenu_model_destroy (model);
|
||||
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_cmd_gfxmenu (grub_command_t cmd UNUSED,
|
||||
int argc UNUSED, char **args UNUSED)
|
||||
{
|
||||
grub_menu_t menu = grub_env_get_data_slot ("menu");
|
||||
if (! menu)
|
||||
return grub_error (GRUB_ERR_MENU, "no menu context");
|
||||
|
||||
return show_menu (menu, 1);
|
||||
}
|
||||
|
||||
static struct grub_menu_viewer menu_viewer =
|
||||
{
|
||||
.name = "gfxmenu",
|
||||
.show_menu = show_menu
|
||||
};
|
||||
|
||||
static grub_command_t cmd;
|
||||
|
||||
GRUB_MOD_INIT (gfxmenu)
|
||||
{
|
||||
(void) mod; /* To stop warning. */
|
||||
grub_menu_viewer_register (&menu_viewer);
|
||||
cmd = grub_register_command ("gfxmenu", grub_cmd_gfxmenu,
|
||||
"gfxmenu",
|
||||
"Show graphical menu interface");
|
||||
}
|
||||
|
||||
GRUB_MOD_FINI (gfxmenu)
|
||||
{
|
||||
grub_unregister_command (cmd);
|
||||
}
|
372
gfxmenu/gui_box.c
Normal file
372
gfxmenu/gui_box.c
Normal file
|
@ -0,0 +1,372 @@
|
|||
/* 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,
|
||||
int *width, int *height);
|
||||
|
||||
struct grub_gui_box
|
||||
{
|
||||
struct grub_gui_container_ops *container;
|
||||
|
||||
grub_gui_container_t parent;
|
||||
grub_video_rect_t bounds;
|
||||
char *id;
|
||||
int preferred_width;
|
||||
int preferred_height;
|
||||
|
||||
/* 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,
|
||||
int *width, int *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;
|
||||
int x = 0;
|
||||
if (height)
|
||||
*height = 0;
|
||||
for (cur = self->chead.next; cur != &self->ctail; cur = cur->next)
|
||||
{
|
||||
grub_gui_component_t c = cur->component;
|
||||
grub_video_rect_t r;
|
||||
|
||||
c->ops->get_preferred_size (c, &r.width, &r.height);
|
||||
|
||||
/* Check and possibly update the maximum width, if non-null. */
|
||||
if (height && r.height > *height)
|
||||
*height = r.height;
|
||||
|
||||
/* Set the component's bounds, if the flag is set. */
|
||||
if (modify_layout)
|
||||
{
|
||||
r.x = x;
|
||||
r.y = 0;
|
||||
/* Width comes from the component's preferred size. */
|
||||
r.height = self->bounds.height;
|
||||
c->ops->set_bounds (c, &r);
|
||||
}
|
||||
|
||||
x += r.width;
|
||||
}
|
||||
|
||||
/* Return the sum of the children's preferred widths. */
|
||||
if (width)
|
||||
*width = x;
|
||||
}
|
||||
|
||||
static void
|
||||
layout_vertically (grub_gui_box_t self, int modify_layout,
|
||||
int *width, int *height)
|
||||
{
|
||||
/* Start at the top (chead) and set the y coordinates as we go down. */
|
||||
/* All components have their width set to the vbox's width. */
|
||||
|
||||
struct component_node *cur;
|
||||
int y = 0;
|
||||
if (width)
|
||||
*width = 0;
|
||||
for (cur = self->chead.next; cur != &self->ctail; cur = cur->next)
|
||||
{
|
||||
grub_gui_component_t c = cur->component;
|
||||
grub_video_rect_t r;
|
||||
|
||||
c->ops->get_preferred_size (c, &r.width, &r.height);
|
||||
|
||||
/* Check and possibly update the maximum width, if non-null. */
|
||||
if (width && r.width > *width)
|
||||
*width = r.width;
|
||||
|
||||
/* Set the component's bounds, if the flag is set. */
|
||||
if (modify_layout)
|
||||
{
|
||||
r.x = 0;
|
||||
r.y = y;
|
||||
r.width = self->bounds.width;
|
||||
/* Height comes from the component's preferred size. */
|
||||
c->ops->set_bounds (c, &r);
|
||||
}
|
||||
|
||||
y += r.height;
|
||||
}
|
||||
|
||||
/* Return the sum of the children's preferred heights. */
|
||||
if (height)
|
||||
*height = y;
|
||||
}
|
||||
|
||||
static void
|
||||
box_paint (void *vself)
|
||||
{
|
||||
grub_gui_box_t self = vself;
|
||||
struct component_node *cur;
|
||||
grub_video_rect_t vpsave;
|
||||
|
||||
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;
|
||||
comp->ops->paint (comp);
|
||||
}
|
||||
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_preferred_size (void *vself, int *width, int *height)
|
||||
{
|
||||
grub_gui_box_t self = vself;
|
||||
self->layout_func (self, 0, width, height); /* Just calculate the size. */
|
||||
|
||||
/* Allow preferred dimensions to override the computed dimensions. */
|
||||
if (self->preferred_width >= 0)
|
||||
*width = self->preferred_width;
|
||||
if (self->preferred_height >= 0)
|
||||
*height = self->preferred_height;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
else if (grub_strcmp (name, "preferred_size") == 0)
|
||||
{
|
||||
int w;
|
||||
int h;
|
||||
if (grub_gui_parse_2_tuple (value, &w, &h) != GRUB_ERR_NONE)
|
||||
return grub_errno;
|
||||
self->preferred_width = w;
|
||||
self->preferred_height = h;
|
||||
}
|
||||
|
||||
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_container_ops box_ops =
|
||||
{
|
||||
.component =
|
||||
{
|
||||
.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_preferred_size = box_get_preferred_size,
|
||||
.set_property = box_set_property
|
||||
},
|
||||
.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_malloc (sizeof (*box));
|
||||
if (! box)
|
||||
return 0;
|
||||
box->container = &box_ops;
|
||||
box->parent = 0;
|
||||
box->bounds.x = 0;
|
||||
box->bounds.y = 0;
|
||||
box->bounds.width = 0;
|
||||
box->bounds.height = 0;
|
||||
box->id = 0;
|
||||
box->preferred_width = -1;
|
||||
box->preferred_height = -1;
|
||||
box->chead.component = 0;
|
||||
box->chead.prev = 0;
|
||||
box->chead.next = &box->ctail;
|
||||
box->ctail.component = 0;
|
||||
box->ctail.prev = &box->chead;
|
||||
box->ctail.next = 0;
|
||||
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);
|
||||
}
|
268
gfxmenu/gui_canvas.c
Normal file
268
gfxmenu/gui_canvas.c
Normal file
|
@ -0,0 +1,268 @@
|
|||
/* 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_ops *container;
|
||||
|
||||
grub_gui_container_t parent;
|
||||
grub_video_rect_t bounds;
|
||||
char *id;
|
||||
int preferred_width;
|
||||
int preferred_height;
|
||||
/* 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)
|
||||
{
|
||||
grub_gui_canvas_t self = vself;
|
||||
struct component_node *cur;
|
||||
grub_video_rect_t vpsave;
|
||||
|
||||
grub_gui_set_viewport (&self->bounds, &vpsave);
|
||||
for (cur = self->components.next; cur; cur = cur->next)
|
||||
{
|
||||
int pw;
|
||||
int ph;
|
||||
grub_video_rect_t r;
|
||||
grub_gui_component_t comp;
|
||||
|
||||
comp = cur->component;
|
||||
|
||||
/* Give the child its preferred size. */
|
||||
comp->ops->get_preferred_size (comp, &pw, &ph);
|
||||
comp->ops->get_bounds (comp, &r);
|
||||
if (r.width != pw || r.height != ph)
|
||||
{
|
||||
r.width = pw;
|
||||
r.height = ph;
|
||||
comp->ops->set_bounds (comp, &r);
|
||||
}
|
||||
|
||||
/* Paint the child. */
|
||||
comp->ops->paint (comp);
|
||||
}
|
||||
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 void
|
||||
canvas_get_preferred_size (void *vself, int *width, int *height)
|
||||
{
|
||||
grub_gui_canvas_t self = vself;
|
||||
*width = 0;
|
||||
*height = 0;
|
||||
|
||||
/* Allow preferred dimensions to override the empty dimensions. */
|
||||
if (self->preferred_width >= 0)
|
||||
*width = self->preferred_width;
|
||||
if (self->preferred_height >= 0)
|
||||
*height = self->preferred_height;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
else if (grub_strcmp (name, "preferred_size") == 0)
|
||||
{
|
||||
int w;
|
||||
int h;
|
||||
if (grub_gui_parse_2_tuple (value, &w, &h) != GRUB_ERR_NONE)
|
||||
return grub_errno;
|
||||
self->preferred_width = w;
|
||||
self->preferred_height = h;
|
||||
}
|
||||
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_container_ops canvas_ops =
|
||||
{
|
||||
.component =
|
||||
{
|
||||
.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,
|
||||
.get_preferred_size = canvas_get_preferred_size,
|
||||
.set_property = canvas_set_property
|
||||
},
|
||||
.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_malloc (sizeof (*canvas));
|
||||
if (! canvas)
|
||||
return 0;
|
||||
canvas->container = &canvas_ops;
|
||||
canvas->parent = 0;
|
||||
canvas->bounds.x = 0;
|
||||
canvas->bounds.y = 0;
|
||||
canvas->bounds.width = 0;
|
||||
canvas->bounds.height = 0;
|
||||
canvas->id = 0;
|
||||
canvas->preferred_width = -1;
|
||||
canvas->preferred_height = -1;
|
||||
canvas->components.component = 0;
|
||||
canvas->components.next = 0;
|
||||
return (grub_gui_container_t) canvas;
|
||||
}
|
339
gfxmenu/gui_circular_progress.c
Normal file
339
gfxmenu/gui_circular_progress.c
Normal file
|
@ -0,0 +1,339 @@
|
|||
/* 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_component_ops *circprog_ops;
|
||||
|
||||
grub_gui_container_t parent;
|
||||
grub_video_rect_t bounds;
|
||||
char *id;
|
||||
int preferred_width;
|
||||
int preferred_height;
|
||||
int visible;
|
||||
int start;
|
||||
int end;
|
||||
int value;
|
||||
int 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_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)
|
||||
{
|
||||
circular_progress_t self = vself;
|
||||
|
||||
if (! self->visible)
|
||||
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);
|
||||
|
||||
int radius = width / 2 - tick_width / 2 - 1;
|
||||
int nticks = (self->num_ticks
|
||||
* (self->value - self->start)
|
||||
/ (self->end - self->start));
|
||||
int tick_begin;
|
||||
int tick_end;
|
||||
/* Do ticks appear or disappear as the value approached the end? */
|
||||
if (self->ticks_disappear)
|
||||
{
|
||||
tick_begin = nticks;
|
||||
tick_end = self->num_ticks - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
tick_begin = 0;
|
||||
tick_end = nticks - 1;
|
||||
}
|
||||
|
||||
int 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_get_preferred_size (void *vself, int *width, int *height)
|
||||
{
|
||||
circular_progress_t self = vself;
|
||||
|
||||
*width = 0;
|
||||
*height = 0;
|
||||
|
||||
/* Allow preferred dimensions to override the circprog dimensions. */
|
||||
if (self->preferred_width >= 0)
|
||||
*width = self->preferred_width;
|
||||
if (self->preferred_height >= 0)
|
||||
*height = self->preferred_height;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
circprog_set_property (void *vself, const char *name, const char *value)
|
||||
{
|
||||
circular_progress_t self = vself;
|
||||
if (grub_strcmp (name, "value") == 0)
|
||||
{
|
||||
self->value = grub_strtol (value, 0, 10);
|
||||
}
|
||||
else if (grub_strcmp (name, "start") == 0)
|
||||
{
|
||||
self->start = grub_strtol (value, 0, 10);
|
||||
}
|
||||
else if (grub_strcmp (name, "end") == 0)
|
||||
{
|
||||
self->end = grub_strtol (value, 0, 10);
|
||||
}
|
||||
else if (grub_strcmp (name, "num_ticks") == 0)
|
||||
{
|
||||
self->num_ticks = grub_strtol (value, 0, 10);
|
||||
}
|
||||
else if (grub_strcmp (name, "start_angle") == 0)
|
||||
{
|
||||
self->start_angle = grub_strtol (value, 0, 10);
|
||||
}
|
||||
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, "preferred_size") == 0)
|
||||
{
|
||||
int w;
|
||||
int h;
|
||||
if (grub_gui_parse_2_tuple (value, &w, &h) != GRUB_ERR_NONE)
|
||||
return grub_errno;
|
||||
self->preferred_width = w;
|
||||
self->preferred_height = h;
|
||||
}
|
||||
else if (grub_strcmp (name, "visible") == 0)
|
||||
{
|
||||
self->visible = grub_strcmp (value, "false") != 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;
|
||||
}
|
||||
|
||||
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,
|
||||
.get_preferred_size = circprog_get_preferred_size,
|
||||
.set_property = circprog_set_property
|
||||
};
|
||||
|
||||
grub_gui_component_t
|
||||
grub_gui_circular_progress_new (void)
|
||||
{
|
||||
circular_progress_t self;
|
||||
self = grub_malloc (sizeof (*self));
|
||||
if (! self)
|
||||
return 0;
|
||||
self->circprog_ops = &circprog_ops;
|
||||
self->parent = 0;
|
||||
self->bounds.x = 0;
|
||||
self->bounds.y = 0;
|
||||
self->bounds.width = 0;
|
||||
self->bounds.height = 0;
|
||||
self->id = 0;
|
||||
self->preferred_width = -1;
|
||||
self->preferred_height = -1;
|
||||
self->visible = 1;
|
||||
self->start = 0;
|
||||
self->end = 0;
|
||||
self->value = 0;
|
||||
self->num_ticks = 64;
|
||||
self->start_angle = -64;
|
||||
self->ticks_disappear = 0;
|
||||
|
||||
self->theme_dir = 0;
|
||||
self->need_to_load_pixmaps = 0;
|
||||
self->center_file = 0;
|
||||
self->tick_file = 0;
|
||||
self->center_bitmap = 0;
|
||||
self->tick_bitmap = 0;
|
||||
|
||||
return (grub_gui_component_t) self;
|
||||
}
|
270
gfxmenu/gui_image.c
Normal file
270
gfxmenu/gui_image.c
Normal file
|
@ -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_ops *image;
|
||||
|
||||
grub_gui_container_t parent;
|
||||
grub_video_rect_t bounds;
|
||||
char *id;
|
||||
int preferred_width;
|
||||
int preferred_height;
|
||||
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)
|
||||
{
|
||||
grub_gui_image_t self = vself;
|
||||
if (! self->bitmap)
|
||||
return;
|
||||
grub_video_rect_t vpsave;
|
||||
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)
|
||||
{
|
||||
if (! self->raw_bitmap)
|
||||
{
|
||||
if (self->bitmap)
|
||||
{
|
||||
grub_video_bitmap_destroy (self->bitmap);
|
||||
self->bitmap = 0;
|
||||
}
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
unsigned width = self->bounds.width;
|
||||
unsigned height = self->bounds.height;
|
||||
|
||||
if (self->bitmap
|
||||
&& (grub_video_bitmap_get_width (self->bitmap) == width)
|
||||
&& (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 (grub_video_bitmap_get_width (self->raw_bitmap) == width
|
||||
&& 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);
|
||||
if (grub_errno != GRUB_ERR_NONE)
|
||||
{
|
||||
grub_error_push ();
|
||||
grub_error (grub_errno, "failed to scale bitmap for image component");
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
static void
|
||||
image_get_preferred_size (void *vself, int *width, int *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;
|
||||
}
|
||||
|
||||
/* Allow preferred dimensions to override the image dimensions. */
|
||||
if (self->preferred_width >= 0)
|
||||
*width = self->preferred_width;
|
||||
if (self->preferred_height >= 0)
|
||||
*height = self->preferred_height;
|
||||
}
|
||||
|
||||
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);
|
||||
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, "file") == 0)
|
||||
return load_image (self, value);
|
||||
else if (grub_strcmp (name, "preferred_size") == 0)
|
||||
{
|
||||
int w;
|
||||
int h;
|
||||
if (grub_gui_parse_2_tuple (value, &w, &h) != GRUB_ERR_NONE)
|
||||
return grub_errno;
|
||||
self->preferred_width = w;
|
||||
self->preferred_height = h;
|
||||
}
|
||||
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_preferred_size = image_get_preferred_size,
|
||||
.set_property = image_set_property
|
||||
};
|
||||
|
||||
grub_gui_component_t
|
||||
grub_gui_image_new (void)
|
||||
{
|
||||
grub_gui_image_t image;
|
||||
image = grub_malloc (sizeof (*image));
|
||||
if (! image)
|
||||
return 0;
|
||||
image->image = &image_ops;
|
||||
image->parent = 0;
|
||||
image->bounds.x = 0;
|
||||
image->bounds.y = 0;
|
||||
image->bounds.width = 0;
|
||||
image->bounds.height = 0;
|
||||
image->id = 0;
|
||||
image->preferred_width = -1;
|
||||
image->preferred_height = -1;
|
||||
image->raw_bitmap = 0;
|
||||
image->bitmap = 0;
|
||||
return (grub_gui_component_t) image;
|
||||
}
|
||||
|
248
gfxmenu/gui_label.c
Normal file
248
gfxmenu/gui_label.c
Normal file
|
@ -0,0 +1,248 @@
|
|||
/* 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>
|
||||
|
||||
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_ops *label;
|
||||
|
||||
grub_gui_container_t parent;
|
||||
grub_video_rect_t bounds;
|
||||
char *id;
|
||||
int preferred_width;
|
||||
int preferred_height;
|
||||
int visible;
|
||||
char *text;
|
||||
grub_font_t font;
|
||||
grub_gui_color_t color;
|
||||
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_free (self->text);
|
||||
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)
|
||||
{
|
||||
grub_gui_label_t self = vself;
|
||||
|
||||
if (! self->visible)
|
||||
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. */
|
||||
|
||||
grub_video_rect_t vpsave;
|
||||
grub_gui_set_viewport (&self->bounds, &vpsave);
|
||||
grub_font_draw_string (self->text,
|
||||
self->font,
|
||||
grub_gui_map_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_preferred_size (void *vself, int *width, int *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));
|
||||
|
||||
/* Allow preferred dimensions to override the computed dimensions. */
|
||||
if (self->preferred_width >= 0)
|
||||
*width = self->preferred_width;
|
||||
if (self->preferred_height >= 0)
|
||||
*height = self->preferred_height;
|
||||
}
|
||||
|
||||
static void
|
||||
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);
|
||||
if (! value)
|
||||
value = "";
|
||||
self->text = grub_strdup (value);
|
||||
}
|
||||
else if (grub_strcmp (name, "font") == 0)
|
||||
{
|
||||
self->font = grub_font_get (value);
|
||||
}
|
||||
else if (grub_strcmp (name, "color") == 0)
|
||||
{
|
||||
grub_gui_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, "preferred_size") == 0)
|
||||
{
|
||||
int w;
|
||||
int h;
|
||||
if (grub_gui_parse_2_tuple (value, &w, &h) == GRUB_ERR_NONE)
|
||||
{
|
||||
self->preferred_width = w;
|
||||
self->preferred_height = h;
|
||||
}
|
||||
}
|
||||
else if (grub_strcmp (name, "id") == 0)
|
||||
{
|
||||
grub_free (self->id);
|
||||
if (value)
|
||||
self->id = grub_strdup (value);
|
||||
else
|
||||
self->id = 0;
|
||||
}
|
||||
}
|
||||
|
||||
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_preferred_size = label_get_preferred_size,
|
||||
.set_property = label_set_property
|
||||
};
|
||||
|
||||
grub_gui_component_t
|
||||
grub_gui_label_new (void)
|
||||
{
|
||||
grub_gui_label_t label;
|
||||
label = grub_malloc (sizeof (*label));
|
||||
if (! label)
|
||||
return 0;
|
||||
label->label = &label_ops;
|
||||
label->parent = 0;
|
||||
label->bounds.x = 0;
|
||||
label->bounds.y = 0;
|
||||
label->bounds.width = 0;
|
||||
label->bounds.height = 0;
|
||||
label->id = 0;
|
||||
label->preferred_width = -1;
|
||||
label->preferred_height = -1;
|
||||
label->visible = 1;
|
||||
label->text = grub_strdup ("");
|
||||
label->font = grub_font_get ("Helvetica 10");
|
||||
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;
|
||||
}
|
625
gfxmenu/gui_list.c
Normal file
625
gfxmenu/gui_list.c
Normal file
|
@ -0,0 +1,625 @@
|
|||
/* 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>
|
||||
|
||||
struct grub_gui_list_impl
|
||||
{
|
||||
struct grub_gui_list_ops *list_ops;
|
||||
|
||||
grub_gui_container_t parent;
|
||||
grub_video_rect_t bounds;
|
||||
char *id;
|
||||
int preferred_width;
|
||||
int preferred_height;
|
||||
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;
|
||||
grub_font_t selected_item_font;
|
||||
grub_gui_color_t item_color;
|
||||
int selected_item_color_set;
|
||||
grub_gui_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_width;
|
||||
|
||||
int min_items_shown;
|
||||
int max_items_shown;
|
||||
int first_shown_index;
|
||||
|
||||
int need_to_recreate_boxes;
|
||||
char *theme_dir;
|
||||
char *menu_box_pattern;
|
||||
char *selected_item_box_pattern;
|
||||
grub_gfxmenu_box_t menu_box;
|
||||
grub_gfxmenu_box_t selected_item_box;
|
||||
|
||||
grub_gfxmenu_icon_manager_t icon_manager;
|
||||
grub_gfxmenu_model_t menu;
|
||||
};
|
||||
|
||||
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->selected_item_box_pattern);
|
||||
if (self->menu_box)
|
||||
self->menu_box->destroy (self->menu_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);
|
||||
|
||||
grub_free (self);
|
||||
}
|
||||
|
||||
static int
|
||||
get_num_shown_items (list_impl_t self)
|
||||
{
|
||||
int n = grub_gfxmenu_model_get_num_entries (self->menu);
|
||||
if (self->min_items_shown != -1 && n < self->min_items_shown)
|
||||
n = self->min_items_shown;
|
||||
if (self->max_items_shown != -1 && n > self->max_items_shown)
|
||||
n = self->max_items_shown;
|
||||
return n;
|
||||
}
|
||||
|
||||
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->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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
return (self->scrollbar_frame != 0 && self->scrollbar_thumb != 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_gfxmenu_model_get_entry (self->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 = grub_gfxmenu_model_get_selected_index (self->menu);
|
||||
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 rightx, int topy, int height)
|
||||
{
|
||||
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));
|
||||
int tracktop = topy + frame->get_top_pad (frame);
|
||||
int tracklen = height - frame_vertical_pad;
|
||||
frame->set_content_size (frame, self->scrollbar_width, tracklen);
|
||||
int thumby = tracktop + tracklen * (value - min) / (max - min);
|
||||
int thumbheight = tracklen * extent / (max - min) + 1;
|
||||
thumb->set_content_size (thumb,
|
||||
self->scrollbar_width - frame_horizontal_pad,
|
||||
thumbheight - (thumb->get_top_pad (thumb)
|
||||
+ thumb->get_bottom_pad (thumb)));
|
||||
frame->draw (frame,
|
||||
rightx - (self->scrollbar_width + frame_horizontal_pad),
|
||||
topy);
|
||||
thumb->draw (thumb,
|
||||
rightx - (self->scrollbar_width - frame->get_right_pad (frame)),
|
||||
thumby);
|
||||
}
|
||||
|
||||
/* Draw the list of items. */
|
||||
static void
|
||||
draw_menu (list_impl_t self)
|
||||
{
|
||||
if (! self->menu_box || ! self->selected_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 item_height = self->item_height;
|
||||
|
||||
int total_num_items = grub_gfxmenu_model_get_num_entries (self->menu);
|
||||
int num_shown_items = get_num_shown_items (self);
|
||||
grub_gfxmenu_box_t box = self->menu_box;
|
||||
int width = self->bounds.width;
|
||||
int height = self->bounds.height;
|
||||
|
||||
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);
|
||||
|
||||
box->set_content_size (box,
|
||||
width - box_left_pad - box_right_pad,
|
||||
height - box_top_pad - box_bottom_pad);
|
||||
|
||||
box->draw (box, 0, 0);
|
||||
|
||||
make_selected_item_visible (self);
|
||||
|
||||
int drawing_scrollbar = (self->draw_scrollbar
|
||||
&& (num_shown_items < total_num_items)
|
||||
&& check_scrollbar (self));
|
||||
|
||||
int scrollbar_h_space = drawing_scrollbar ? self->scrollbar_width : 0;
|
||||
|
||||
int item_top = box_top_pad + boxpad;
|
||||
int item_left = box_left_pad + boxpad;
|
||||
int menu_index;
|
||||
int visible_index;
|
||||
|
||||
for (visible_index = 0, menu_index = self->first_shown_index;
|
||||
visible_index < num_shown_items && menu_index < total_num_items;
|
||||
visible_index++, menu_index++)
|
||||
{
|
||||
int is_selected =
|
||||
(menu_index == grub_gfxmenu_model_get_selected_index (self->menu));
|
||||
|
||||
if (is_selected)
|
||||
{
|
||||
grub_gfxmenu_box_t selbox = self->selected_item_box;
|
||||
int sel_leftpad = selbox->get_left_pad (selbox);
|
||||
int sel_toppad = selbox->get_top_pad (selbox);
|
||||
selbox->set_content_size (selbox,
|
||||
(width - 2 * boxpad
|
||||
- box_left_pad - box_right_pad
|
||||
- scrollbar_h_space),
|
||||
item_height);
|
||||
selbox->draw (selbox,
|
||||
item_left - sel_leftpad,
|
||||
item_top - sel_toppad);
|
||||
}
|
||||
|
||||
struct grub_video_bitmap *icon;
|
||||
if ((icon = get_item_icon (self, menu_index)) != 0)
|
||||
grub_video_blit_bitmap (icon, GRUB_VIDEO_BLIT_BLEND,
|
||||
item_left,
|
||||
item_top + (item_height - self->icon_height) / 2,
|
||||
0, 0, self->icon_width, self->icon_height);
|
||||
|
||||
const char *item_title =
|
||||
grub_gfxmenu_model_get_entry_title (self->menu, menu_index);
|
||||
grub_font_t font =
|
||||
(is_selected && self->selected_item_font
|
||||
? self->selected_item_font
|
||||
: self->item_font);
|
||||
grub_gui_color_t text_color =
|
||||
((is_selected && self->selected_item_color_set)
|
||||
? self->selected_item_color
|
||||
: self->item_color);
|
||||
grub_font_draw_string (item_title,
|
||||
font,
|
||||
grub_gui_map_color (text_color),
|
||||
item_left + self->icon_width + icon_text_space,
|
||||
(item_top + (item_height - (ascent + descent))
|
||||
/ 2 + ascent));
|
||||
|
||||
item_top += item_height + item_vspace;
|
||||
}
|
||||
|
||||
if (drawing_scrollbar)
|
||||
draw_scrollbar (self,
|
||||
self->first_shown_index, num_shown_items,
|
||||
0, total_num_items,
|
||||
width - box_right_pad + self->scrollbar_width,
|
||||
box_top_pad + boxpad,
|
||||
height - box_top_pad - box_bottom_pad);
|
||||
}
|
||||
|
||||
static void
|
||||
list_paint (void *vself)
|
||||
{
|
||||
list_impl_t self = vself;
|
||||
|
||||
if (! self->visible)
|
||||
return;
|
||||
|
||||
check_boxes (self);
|
||||
|
||||
grub_video_rect_t vpsave;
|
||||
grub_gui_set_viewport (&self->bounds, &vpsave);
|
||||
draw_menu (self);
|
||||
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_preferred_size (void *vself, int *width, int *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 = get_num_shown_items (self);
|
||||
|
||||
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);
|
||||
|
||||
*width = 400 + 2 * boxpad + box_left_pad + box_right_pad;
|
||||
|
||||
/* 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);
|
||||
}
|
||||
else
|
||||
{
|
||||
*width = 0;
|
||||
*height = 0;
|
||||
}
|
||||
|
||||
/* Allow preferred dimensions to override the computed dimensions. */
|
||||
if (self->preferred_width >= 0)
|
||||
*width = self->preferred_width;
|
||||
if (self->preferred_height >= 0)
|
||||
*height = self->preferred_height;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
else if (grub_strcmp (name, "selected_item_font") == 0)
|
||||
{
|
||||
if (! value || grub_strcmp (value, "inherit") == 0)
|
||||
self->selected_item_font = 0;
|
||||
else
|
||||
self->selected_item_font = grub_font_get (value);
|
||||
}
|
||||
else if (grub_strcmp (name, "item_color") == 0)
|
||||
{
|
||||
grub_gui_parse_color (value, &self->item_color);
|
||||
}
|
||||
else if (grub_strcmp (name, "selected_item_color") == 0)
|
||||
{
|
||||
if (! value || grub_strcmp (value, "inherit") == 0)
|
||||
{
|
||||
self->selected_item_color_set = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (grub_gui_parse_color (value, &self->selected_item_color)
|
||||
== GRUB_ERR_NONE)
|
||||
self->selected_item_color_set = 1;
|
||||
}
|
||||
}
|
||||
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, "selected_item_pixmap_style") == 0)
|
||||
{
|
||||
self->need_to_recreate_boxes = 1;
|
||||
grub_free (self->selected_item_box_pattern);
|
||||
self->selected_item_box_pattern = value ? grub_strdup (value) : 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_width") == 0)
|
||||
{
|
||||
self->scrollbar_width = 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, "min_items_shown") == 0)
|
||||
{
|
||||
self->min_items_shown = grub_strtol (value, 0, 10);
|
||||
}
|
||||
else if (grub_strcmp (name, "max_items_shown") == 0)
|
||||
{
|
||||
self->max_items_shown = grub_strtol (value, 0, 10);
|
||||
}
|
||||
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, "preferred_size") == 0)
|
||||
{
|
||||
int w;
|
||||
int h;
|
||||
if (grub_gui_parse_2_tuple (value, &w, &h) != GRUB_ERR_NONE)
|
||||
return grub_errno;
|
||||
self->preferred_width = w;
|
||||
self->preferred_height = h;
|
||||
}
|
||||
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,
|
||||
const char *theme_path,
|
||||
grub_gfxmenu_model_t menu)
|
||||
{
|
||||
list_impl_t self = vself;
|
||||
grub_gfxmenu_icon_manager_set_theme_path (self->icon_manager, theme_path);
|
||||
self->menu = menu;
|
||||
}
|
||||
|
||||
static struct grub_gui_list_ops list_ops =
|
||||
{
|
||||
.component_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_preferred_size = list_get_preferred_size,
|
||||
.set_property = list_set_property
|
||||
},
|
||||
.set_view_info = list_set_view_info
|
||||
};
|
||||
|
||||
grub_gui_component_t
|
||||
grub_gui_list_new (void)
|
||||
{
|
||||
list_impl_t self;
|
||||
grub_font_t default_font;
|
||||
grub_gui_color_t default_fg_color;
|
||||
grub_gui_color_t default_bg_color;
|
||||
|
||||
self = grub_malloc (sizeof (*self));
|
||||
if (! self)
|
||||
return 0;
|
||||
|
||||
self->list_ops = &list_ops;
|
||||
self->parent = 0;
|
||||
self->bounds.x = 0;
|
||||
self->bounds.y = 0;
|
||||
self->bounds.width = 0;
|
||||
self->bounds.height = 0;
|
||||
self->id = 0;
|
||||
self->preferred_width = -1;
|
||||
self->preferred_height = -1;
|
||||
self->visible = 1;
|
||||
|
||||
default_font = grub_font_get ("Helvetica 12");
|
||||
default_fg_color = grub_gui_color_rgb (0, 0, 0);
|
||||
default_bg_color = grub_gui_color_rgb (255, 255, 255);
|
||||
|
||||
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 = 0; /* Default to using the item_font. */
|
||||
self->item_color = default_fg_color;
|
||||
self->selected_item_color_set = 0; /* 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_width = 16;
|
||||
|
||||
self->min_items_shown = -1;
|
||||
self->max_items_shown = -1;
|
||||
self->first_shown_index = 0;
|
||||
|
||||
self->need_to_recreate_boxes = 0;
|
||||
self->theme_dir = 0;
|
||||
self->menu_box_pattern = 0;
|
||||
self->selected_item_box_pattern = 0;
|
||||
self->menu_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_ops->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;
|
||||
}
|
378
gfxmenu/gui_progress_bar.c
Normal file
378
gfxmenu/gui_progress_bar.c
Normal file
|
@ -0,0 +1,378 @@
|
|||
/* 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>
|
||||
|
||||
struct grub_gui_progress_bar
|
||||
{
|
||||
struct grub_gui_component_ops *progress_bar;
|
||||
|
||||
grub_gui_container_t parent;
|
||||
grub_video_rect_t bounds;
|
||||
char *id;
|
||||
int preferred_width;
|
||||
int preferred_height;
|
||||
int visible;
|
||||
int start;
|
||||
int end;
|
||||
int value;
|
||||
int show_text;
|
||||
char *text;
|
||||
grub_font_t font;
|
||||
grub_gui_color_t text_color;
|
||||
grub_gui_color_t border_color;
|
||||
grub_gui_color_t bg_color;
|
||||
grub_gui_color_t fg_color;
|
||||
|
||||
char *theme_dir;
|
||||
int need_to_recreate_pixmaps;
|
||||
char *bar_pattern;
|
||||
char *highlight_pattern;
|
||||
grub_gfxmenu_box_t bar_box;
|
||||
grub_gfxmenu_box_t highlight_box;
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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->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_gui_map_color (self->border_color),
|
||||
f.x - 1, f.y - 1,
|
||||
f.width + 2, f.height + 2);
|
||||
|
||||
/* Bar background. */
|
||||
int barwidth = (f.width
|
||||
* (self->value - self->start)
|
||||
/ (self->end - self->start));
|
||||
grub_video_fill_rect (grub_gui_map_color (self->bg_color),
|
||||
f.x + barwidth, f.y,
|
||||
f.width - barwidth, f.height);
|
||||
|
||||
/* Bar foreground. */
|
||||
grub_video_fill_rect (grub_gui_map_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 tracklen = w - bar_h_pad;
|
||||
int trackheight = h - bar_v_pad;
|
||||
bar->set_content_size (bar, tracklen, trackheight);
|
||||
|
||||
int barwidth = (tracklen
|
||||
* (self->value - self->start)
|
||||
/ (self->end - self->start));
|
||||
hl->set_content_size (hl, barwidth, h - bar_v_pad);
|
||||
|
||||
bar->draw (bar, 0, 0);
|
||||
hl->draw (hl, bar_l_pad, bar_t_pad);
|
||||
}
|
||||
|
||||
static void
|
||||
draw_text (grub_gui_progress_bar_t self)
|
||||
{
|
||||
const char *text = self->text;
|
||||
if (text && self->show_text)
|
||||
{
|
||||
grub_font_t font = self->font;
|
||||
grub_video_color_t text_color = grub_gui_map_color (self->text_color);
|
||||
int width = self->bounds.width;
|
||||
int height = self->bounds.height;
|
||||
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
progress_bar_paint (void *vself)
|
||||
{
|
||||
grub_gui_progress_bar_t self = vself;
|
||||
if (! self->visible)
|
||||
return;
|
||||
|
||||
grub_video_rect_t vpsave;
|
||||
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_preferred_size (void *vself, int *width, int *height)
|
||||
{
|
||||
grub_gui_progress_bar_t self = vself;
|
||||
|
||||
*width = 200;
|
||||
*height = 28;
|
||||
|
||||
/* Allow preferred dimensions to override the progress_bar dimensions. */
|
||||
if (self->preferred_width >= 0)
|
||||
*width = self->preferred_width;
|
||||
if (self->preferred_height >= 0)
|
||||
*height = self->preferred_height;
|
||||
}
|
||||
|
||||
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, "value") == 0)
|
||||
{
|
||||
self->value = grub_strtol (value, 0, 10);
|
||||
}
|
||||
else if (grub_strcmp (name, "start") == 0)
|
||||
{
|
||||
self->start = grub_strtol (value, 0, 10);
|
||||
}
|
||||
else if (grub_strcmp (name, "end") == 0)
|
||||
{
|
||||
self->end = grub_strtol (value, 0, 10);
|
||||
}
|
||||
else if (grub_strcmp (name, "text") == 0)
|
||||
{
|
||||
grub_free (self->text);
|
||||
if (! value)
|
||||
value = "";
|
||||
self->text = 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_gui_parse_color (value, &self->text_color);
|
||||
}
|
||||
else if (grub_strcmp (name, "border_color") == 0)
|
||||
{
|
||||
grub_gui_parse_color (value, &self->border_color);
|
||||
}
|
||||
else if (grub_strcmp (name, "bg_color") == 0)
|
||||
{
|
||||
grub_gui_parse_color (value, &self->bg_color);
|
||||
}
|
||||
else if (grub_strcmp (name, "fg_color") == 0)
|
||||
{
|
||||
grub_gui_parse_color (value, &self->fg_color);
|
||||
}
|
||||
else if (grub_strcmp (name, "bar_style") == 0)
|
||||
{
|
||||
self->need_to_recreate_pixmaps = 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;
|
||||
grub_free (self->highlight_pattern);
|
||||
self->highlight_pattern = value ? grub_strdup (value) : 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, "preferred_size") == 0)
|
||||
{
|
||||
int w;
|
||||
int h;
|
||||
if (grub_gui_parse_2_tuple (value, &w, &h) != GRUB_ERR_NONE)
|
||||
return grub_errno;
|
||||
self->preferred_width = w;
|
||||
self->preferred_height = h;
|
||||
}
|
||||
else if (grub_strcmp (name, "visible") == 0)
|
||||
{
|
||||
self->visible = grub_strcmp (value, "false") != 0;
|
||||
}
|
||||
else if (grub_strcmp (name, "show_text") == 0)
|
||||
{
|
||||
self->show_text = grub_strcmp (value, "false") != 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;
|
||||
}
|
||||
|
||||
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_preferred_size = progress_bar_get_preferred_size,
|
||||
.set_property = progress_bar_set_property
|
||||
};
|
||||
|
||||
grub_gui_component_t
|
||||
grub_gui_progress_bar_new (void)
|
||||
{
|
||||
grub_gui_progress_bar_t self;
|
||||
self = grub_malloc (sizeof (*self));
|
||||
if (! self)
|
||||
return 0;
|
||||
self->progress_bar = &progress_bar_ops;
|
||||
self->parent = 0;
|
||||
self->bounds.x = 0;
|
||||
self->bounds.y = 0;
|
||||
self->bounds.width = 0;
|
||||
self->bounds.height = 0;
|
||||
self->id = 0;
|
||||
self->preferred_width = -1;
|
||||
self->preferred_height = -1;
|
||||
self->visible = 1;
|
||||
self->start = 0;
|
||||
self->end = 0;
|
||||
self->value = 0;
|
||||
self->show_text = 1;
|
||||
self->text = grub_strdup ("");
|
||||
self->font = grub_font_get ("Helvetica 10");
|
||||
grub_gui_color_t black = { .red = 0, .green = 0, .blue = 0, .alpha = 255 };
|
||||
grub_gui_color_t gray = { .red = 128, .green = 128, .blue = 128, .alpha = 255 };
|
||||
grub_gui_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->theme_dir = 0;
|
||||
self->need_to_recreate_pixmaps = 0;
|
||||
self->bar_pattern = 0;
|
||||
self->highlight_pattern = 0;
|
||||
self->bar_box = 0;
|
||||
self->highlight_box = 0;
|
||||
|
||||
return (grub_gui_component_t) self;
|
||||
}
|
358
gfxmenu/gui_string_util.c
Normal file
358
gfxmenu/gui_string_util.c
Normal file
|
@ -0,0 +1,358 @@
|
|||
/* 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_malloc (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;
|
||||
|
||||
/* 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) + 1);
|
||||
if (! abspath)
|
||||
return 0;
|
||||
|
||||
/* Concatenate BASE and PATH.
|
||||
Note that BASE is expected to have a trailing slash. */
|
||||
p = grub_stpcpy (abspath, base);
|
||||
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);
|
||||
}
|
||||
|
||||
static __inline int
|
||||
isxdigit (char c)
|
||||
{
|
||||
return ((c >= '0' && c <= '9')
|
||||
|| (c >= 'a' && c <= 'f')
|
||||
|| (c >= 'A' && c <= 'F'));
|
||||
}
|
||||
|
||||
static int
|
||||
parse_hex_color_component (const char *s, unsigned start, unsigned end)
|
||||
{
|
||||
unsigned len;
|
||||
char buf[3];
|
||||
|
||||
len = end - start;
|
||||
/* Check the limits so we don't overrun the buffer. */
|
||||
if (len < 1 || len > 2)
|
||||
return 0;
|
||||
|
||||
if (len == 1)
|
||||
{
|
||||
buf[0] = s[start]; /* Get the first and only hex digit. */
|
||||
buf[1] = buf[0]; /* Duplicate the hex digit. */
|
||||
}
|
||||
else if (len == 2)
|
||||
{
|
||||
buf[0] = s[start];
|
||||
buf[1] = s[start + 1];
|
||||
}
|
||||
|
||||
buf[2] = '\0';
|
||||
|
||||
return grub_strtoul (buf, 0, 16);
|
||||
}
|
||||
|
||||
/* Parse a color string of the form "r, g, b", "#RGB", "#RGBA",
|
||||
"#RRGGBB", or "#RRGGBBAA". */
|
||||
grub_err_t
|
||||
grub_gui_parse_color (const char *s, grub_gui_color_t *color)
|
||||
{
|
||||
grub_gui_color_t c;
|
||||
|
||||
/* Skip whitespace. */
|
||||
while (*s && grub_isspace (*s))
|
||||
s++;
|
||||
|
||||
if (*s == '#')
|
||||
{
|
||||
/* HTML-style. Number if hex digits:
|
||||
[6] #RRGGBB [3] #RGB
|
||||
[8] #RRGGBBAA [4] #RGBA */
|
||||
|
||||
s++; /* Skip the '#'. */
|
||||
/* Count the hexits to determine the format. */
|
||||
int hexits = 0;
|
||||
const char *end = s;
|
||||
while (isxdigit (*end))
|
||||
{
|
||||
end++;
|
||||
hexits++;
|
||||
}
|
||||
|
||||
/* Parse the color components based on the format. */
|
||||
if (hexits == 3 || hexits == 4)
|
||||
{
|
||||
c.red = parse_hex_color_component (s, 0, 1);
|
||||
c.green = parse_hex_color_component (s, 1, 2);
|
||||
c.blue = parse_hex_color_component (s, 2, 3);
|
||||
if (hexits == 4)
|
||||
c.alpha = parse_hex_color_component (s, 3, 4);
|
||||
else
|
||||
c.alpha = 255;
|
||||
}
|
||||
else if (hexits == 6 || hexits == 8)
|
||||
{
|
||||
c.red = parse_hex_color_component (s, 0, 2);
|
||||
c.green = parse_hex_color_component (s, 2, 4);
|
||||
c.blue = parse_hex_color_component (s, 4, 6);
|
||||
if (hexits == 8)
|
||||
c.alpha = parse_hex_color_component (s, 6, 8);
|
||||
else
|
||||
c.alpha = 255;
|
||||
}
|
||||
else
|
||||
return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
||||
"invalid HTML-type color string `%s'", s);
|
||||
}
|
||||
else if (grub_isdigit (*s))
|
||||
{
|
||||
/* Comma separated decimal values. */
|
||||
c.red = grub_strtoul (s, 0, 0);
|
||||
if ((s = grub_strchr (s, ',')) == 0)
|
||||
return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
||||
"missing 1st comma separator in color `%s'", s);
|
||||
s++;
|
||||
c.green = grub_strtoul (s, 0, 0);
|
||||
if ((s = grub_strchr (s, ',')) == 0)
|
||||
return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
||||
"missing 2nd comma separator in color `%s'", s);
|
||||
s++;
|
||||
c.blue = grub_strtoul (s, 0, 0);
|
||||
if ((s = grub_strchr (s, ',')) == 0)
|
||||
c.alpha = 255;
|
||||
else
|
||||
{
|
||||
s++;
|
||||
c.alpha = grub_strtoul (s, 0, 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (! grub_gui_get_named_color (s, &c))
|
||||
return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
||||
"invalid named color `%s'", s);
|
||||
}
|
||||
|
||||
if (grub_errno == GRUB_ERR_NONE)
|
||||
*color = c;
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
/* Parse a value in the form "(x, y)", storing the first element (x) into
|
||||
*PX and the second element (y) into *PY.
|
||||
Returns GRUB_ERR_NONE if successfully parsed. */
|
||||
grub_err_t
|
||||
grub_gui_parse_2_tuple (const char *s, int *px, int *py)
|
||||
{
|
||||
int x;
|
||||
int y;
|
||||
|
||||
while (*s && grub_isspace (*s))
|
||||
s++;
|
||||
if (*s != '(')
|
||||
return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
||||
"missing `(' in 2-tuple `%s'", s);
|
||||
|
||||
/* Skip the opening parentheses. */
|
||||
s++;
|
||||
if (*s == 0)
|
||||
return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
||||
"unexpected end of 2-tuple after `(' in `%s'", s);
|
||||
|
||||
/* Parse the first element. */
|
||||
x = grub_strtol (s, 0, 10);
|
||||
if ((s = grub_strchr (s, ',')) == 0)
|
||||
return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
||||
"missing comma in 2-tuple `%s'", s);
|
||||
|
||||
/* Skip the element separator (the comma). */
|
||||
s++;
|
||||
/* Parse the second element. */
|
||||
y = grub_strtol (s, 0, 10);
|
||||
|
||||
*px = x;
|
||||
*py = y;
|
||||
|
||||
return grub_errno;
|
||||
}
|
101
gfxmenu/gui_util.c
Normal file
101
gfxmenu/gui_util.c
Normal file
|
@ -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);
|
||||
}
|
258
gfxmenu/icon_manager.c
Normal file
258
gfxmenu/icon_manager.c
Normal file
|
@ -0,0 +1,258 @@
|
|||
/* 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>
|
||||
|
||||
/* 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))
|
||||
|| (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 = grub_malloc (grub_strlen (dir)
|
||||
+ grub_strlen (class_name)
|
||||
+ grub_strlen (icon_extension)
|
||||
+ 1);
|
||||
if (! path)
|
||||
return 0;
|
||||
|
||||
grub_strcpy (path, dir);
|
||||
grub_strcat (path, class_name);
|
||||
grub_strcat (path, icon_extension);
|
||||
|
||||
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)
|
||||
{
|
||||
grub_error_push ();
|
||||
grub_error (grub_errno, "failed to scale icon");
|
||||
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);
|
||||
}
|
||||
if (! icon)
|
||||
{
|
||||
/* If the theme doesn't have an appropriate icon, check in
|
||||
"grub/themes/icons". */
|
||||
/* TODO use GRUB prefix "/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);
|
||||
|
||||
/* 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->next; c && ! icon; c = c->next)
|
||||
icon = get_icon_by_class (mgr, c->name);
|
||||
return icon;
|
||||
}
|
191
gfxmenu/model.c
Normal file
191
gfxmenu/model.c
Normal file
|
@ -0,0 +1,191 @@
|
|||
/* model.c - Graphical menu interface MVC model. */
|
||||
/*
|
||||
* 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/normal.h>
|
||||
#include <grub/menu.h>
|
||||
#include <grub/time.h>
|
||||
#include <grub/gfxmenu_model.h>
|
||||
|
||||
/* Model type definition. */
|
||||
struct grub_gfxmenu_model
|
||||
{
|
||||
grub_menu_t menu;
|
||||
int num_entries;
|
||||
grub_menu_entry_t *entries;
|
||||
int selected_entry_index;
|
||||
int timeout_set;
|
||||
grub_uint64_t timeout_start;
|
||||
grub_uint64_t timeout_at;
|
||||
};
|
||||
|
||||
|
||||
grub_gfxmenu_model_t
|
||||
grub_gfxmenu_model_new (grub_menu_t menu)
|
||||
{
|
||||
grub_gfxmenu_model_t model;
|
||||
|
||||
model = grub_malloc (sizeof (*model));
|
||||
if (! model)
|
||||
return 0;
|
||||
|
||||
model->menu = menu;
|
||||
model->num_entries = menu->size;
|
||||
model->entries = 0;
|
||||
model->selected_entry_index = 0;
|
||||
model->timeout_set = 0;
|
||||
model->timeout_at = 0;
|
||||
if (model->num_entries > 0)
|
||||
{
|
||||
model->entries = grub_malloc (model->num_entries
|
||||
* sizeof (*model->entries));
|
||||
if (! model->entries)
|
||||
goto fail_and_free;
|
||||
|
||||
int i;
|
||||
grub_menu_entry_t cur;
|
||||
for (i = 0, cur = menu->entry_list;
|
||||
i < model->num_entries;
|
||||
i++, cur = cur->next)
|
||||
{
|
||||
model->entries[i] = cur;
|
||||
}
|
||||
}
|
||||
|
||||
return model;
|
||||
|
||||
fail_and_free:
|
||||
grub_free (model->entries);
|
||||
grub_free (model);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
grub_gfxmenu_model_destroy (grub_gfxmenu_model_t model)
|
||||
{
|
||||
if (! model)
|
||||
return;
|
||||
|
||||
grub_free (model->entries);
|
||||
model->entries = 0;
|
||||
|
||||
grub_free (model);
|
||||
}
|
||||
|
||||
grub_menu_t
|
||||
grub_gfxmenu_model_get_menu (grub_gfxmenu_model_t model)
|
||||
{
|
||||
return model->menu;
|
||||
}
|
||||
|
||||
void
|
||||
grub_gfxmenu_model_set_timeout (grub_gfxmenu_model_t model)
|
||||
{
|
||||
int timeout_sec = grub_menu_get_timeout ();
|
||||
if (timeout_sec >= 0)
|
||||
{
|
||||
model->timeout_start = grub_get_time_ms ();
|
||||
model->timeout_at = model->timeout_start + timeout_sec * 1000;
|
||||
model->timeout_set = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
model->timeout_set = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
grub_gfxmenu_model_clear_timeout (grub_gfxmenu_model_t model)
|
||||
{
|
||||
model->timeout_set = 0;
|
||||
grub_menu_set_timeout (-1);
|
||||
}
|
||||
|
||||
int
|
||||
grub_gfxmenu_model_get_timeout_ms (grub_gfxmenu_model_t model)
|
||||
{
|
||||
if (!model->timeout_set)
|
||||
return -1;
|
||||
|
||||
return model->timeout_at - model->timeout_start;
|
||||
}
|
||||
|
||||
int
|
||||
grub_gfxmenu_model_get_timeout_remaining_ms (grub_gfxmenu_model_t model)
|
||||
{
|
||||
if (!model->timeout_set)
|
||||
return -1;
|
||||
|
||||
return model->timeout_at - grub_get_time_ms ();
|
||||
}
|
||||
|
||||
int
|
||||
grub_gfxmenu_model_timeout_expired (grub_gfxmenu_model_t model)
|
||||
{
|
||||
if (model->timeout_set
|
||||
&& grub_get_time_ms () >= model->timeout_at)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
grub_gfxmenu_model_get_num_entries (grub_gfxmenu_model_t model)
|
||||
{
|
||||
return model->num_entries;
|
||||
}
|
||||
|
||||
int
|
||||
grub_gfxmenu_model_get_selected_index (grub_gfxmenu_model_t model)
|
||||
{
|
||||
return model->selected_entry_index;
|
||||
}
|
||||
|
||||
void
|
||||
grub_gfxmenu_model_set_selected_index (grub_gfxmenu_model_t model, int index)
|
||||
{
|
||||
model->selected_entry_index = index;
|
||||
}
|
||||
|
||||
const char *
|
||||
grub_gfxmenu_model_get_entry_title (grub_gfxmenu_model_t model, int index)
|
||||
{
|
||||
if (index < 0 || index >= model->num_entries)
|
||||
{
|
||||
grub_error (GRUB_ERR_OUT_OF_RANGE, "invalid menu index");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return model->entries[index]->title;
|
||||
}
|
||||
|
||||
grub_menu_entry_t
|
||||
grub_gfxmenu_model_get_entry (grub_gfxmenu_model_t model, int index)
|
||||
{
|
||||
if (index < 0 || index >= model->num_entries)
|
||||
{
|
||||
grub_error (GRUB_ERR_OUT_OF_RANGE, "invalid menu index");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return model->entries[index];
|
||||
}
|
209
gfxmenu/named_colors.c
Normal file
209
gfxmenu/named_colors.c
Normal file
|
@ -0,0 +1,209 @@
|
|||
/* named_colors.c - Named color values. */
|
||||
/*
|
||||
* 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/gui.h>
|
||||
#include <grub/gui_string_util.h>
|
||||
#include <grub/misc.h>
|
||||
|
||||
struct named_color
|
||||
{
|
||||
const char *name;
|
||||
grub_gui_color_t color;
|
||||
};
|
||||
|
||||
/*
|
||||
Named color list generated from the list of SVG color keywords from
|
||||
<http://www.w3.org/TR/css3-color/#svg-color>,
|
||||
processed through the following Perl command:
|
||||
perl -ne 'chomp;split;print "{ \"$_[0]\", RGB_COLOR($_[2]) },\n"'
|
||||
*/
|
||||
|
||||
#define RGB_COLOR(r,g,b) {.red = r, .green = g, .blue = b, .alpha = 255}
|
||||
|
||||
static struct named_color named_colors[] =
|
||||
{
|
||||
{ "aliceblue", RGB_COLOR(240,248,255) },
|
||||
{ "antiquewhite", RGB_COLOR(250,235,215) },
|
||||
{ "aqua", RGB_COLOR(0,255,255) },
|
||||
{ "aquamarine", RGB_COLOR(127,255,212) },
|
||||
{ "azure", RGB_COLOR(240,255,255) },
|
||||
{ "beige", RGB_COLOR(245,245,220) },
|
||||
{ "bisque", RGB_COLOR(255,228,196) },
|
||||
{ "black", RGB_COLOR(0,0,0) },
|
||||
{ "blanchedalmond", RGB_COLOR(255,235,205) },
|
||||
{ "blue", RGB_COLOR(0,0,255) },
|
||||
{ "blueviolet", RGB_COLOR(138,43,226) },
|
||||
{ "brown", RGB_COLOR(165,42,42) },
|
||||
{ "burlywood", RGB_COLOR(222,184,135) },
|
||||
{ "cadetblue", RGB_COLOR(95,158,160) },
|
||||
{ "chartreuse", RGB_COLOR(127,255,0) },
|
||||
{ "chocolate", RGB_COLOR(210,105,30) },
|
||||
{ "coral", RGB_COLOR(255,127,80) },
|
||||
{ "cornflowerblue", RGB_COLOR(100,149,237) },
|
||||
{ "cornsilk", RGB_COLOR(255,248,220) },
|
||||
{ "crimson", RGB_COLOR(220,20,60) },
|
||||
{ "cyan", RGB_COLOR(0,255,255) },
|
||||
{ "darkblue", RGB_COLOR(0,0,139) },
|
||||
{ "darkcyan", RGB_COLOR(0,139,139) },
|
||||
{ "darkgoldenrod", RGB_COLOR(184,134,11) },
|
||||
{ "darkgray", RGB_COLOR(169,169,169) },
|
||||
{ "darkgreen", RGB_COLOR(0,100,0) },
|
||||
{ "darkgrey", RGB_COLOR(169,169,169) },
|
||||
{ "darkkhaki", RGB_COLOR(189,183,107) },
|
||||
{ "darkmagenta", RGB_COLOR(139,0,139) },
|
||||
{ "darkolivegreen", RGB_COLOR(85,107,47) },
|
||||
{ "darkorange", RGB_COLOR(255,140,0) },
|
||||
{ "darkorchid", RGB_COLOR(153,50,204) },
|
||||
{ "darkred", RGB_COLOR(139,0,0) },
|
||||
{ "darksalmon", RGB_COLOR(233,150,122) },
|
||||
{ "darkseagreen", RGB_COLOR(143,188,143) },
|
||||
{ "darkslateblue", RGB_COLOR(72,61,139) },
|
||||
{ "darkslategray", RGB_COLOR(47,79,79) },
|
||||
{ "darkslategrey", RGB_COLOR(47,79,79) },
|
||||
{ "darkturquoise", RGB_COLOR(0,206,209) },
|
||||
{ "darkviolet", RGB_COLOR(148,0,211) },
|
||||
{ "deeppink", RGB_COLOR(255,20,147) },
|
||||
{ "deepskyblue", RGB_COLOR(0,191,255) },
|
||||
{ "dimgray", RGB_COLOR(105,105,105) },
|
||||
{ "dimgrey", RGB_COLOR(105,105,105) },
|
||||
{ "dodgerblue", RGB_COLOR(30,144,255) },
|
||||
{ "firebrick", RGB_COLOR(178,34,34) },
|
||||
{ "floralwhite", RGB_COLOR(255,250,240) },
|
||||
{ "forestgreen", RGB_COLOR(34,139,34) },
|
||||
{ "fuchsia", RGB_COLOR(255,0,255) },
|
||||
{ "gainsboro", RGB_COLOR(220,220,220) },
|
||||
{ "ghostwhite", RGB_COLOR(248,248,255) },
|
||||
{ "gold", RGB_COLOR(255,215,0) },
|
||||
{ "goldenrod", RGB_COLOR(218,165,32) },
|
||||
{ "gray", RGB_COLOR(128,128,128) },
|
||||
{ "green", RGB_COLOR(0,128,0) },
|
||||
{ "greenyellow", RGB_COLOR(173,255,47) },
|
||||
{ "grey", RGB_COLOR(128,128,128) },
|
||||
{ "honeydew", RGB_COLOR(240,255,240) },
|
||||
{ "hotpink", RGB_COLOR(255,105,180) },
|
||||
{ "indianred", RGB_COLOR(205,92,92) },
|
||||
{ "indigo", RGB_COLOR(75,0,130) },
|
||||
{ "ivory", RGB_COLOR(255,255,240) },
|
||||
{ "khaki", RGB_COLOR(240,230,140) },
|
||||
{ "lavender", RGB_COLOR(230,230,250) },
|
||||
{ "lavenderblush", RGB_COLOR(255,240,245) },
|
||||
{ "lawngreen", RGB_COLOR(124,252,0) },
|
||||
{ "lemonchiffon", RGB_COLOR(255,250,205) },
|
||||
{ "lightblue", RGB_COLOR(173,216,230) },
|
||||
{ "lightcoral", RGB_COLOR(240,128,128) },
|
||||
{ "lightcyan", RGB_COLOR(224,255,255) },
|
||||
{ "lightgoldenrodyellow", RGB_COLOR(250,250,210) },
|
||||
{ "lightgray", RGB_COLOR(211,211,211) },
|
||||
{ "lightgreen", RGB_COLOR(144,238,144) },
|
||||
{ "lightgrey", RGB_COLOR(211,211,211) },
|
||||
{ "lightpink", RGB_COLOR(255,182,193) },
|
||||
{ "lightsalmon", RGB_COLOR(255,160,122) },
|
||||
{ "lightseagreen", RGB_COLOR(32,178,170) },
|
||||
{ "lightskyblue", RGB_COLOR(135,206,250) },
|
||||
{ "lightslategray", RGB_COLOR(119,136,153) },
|
||||
{ "lightslategrey", RGB_COLOR(119,136,153) },
|
||||
{ "lightsteelblue", RGB_COLOR(176,196,222) },
|
||||
{ "lightyellow", RGB_COLOR(255,255,224) },
|
||||
{ "lime", RGB_COLOR(0,255,0) },
|
||||
{ "limegreen", RGB_COLOR(50,205,50) },
|
||||
{ "linen", RGB_COLOR(250,240,230) },
|
||||
{ "magenta", RGB_COLOR(255,0,255) },
|
||||
{ "maroon", RGB_COLOR(128,0,0) },
|
||||
{ "mediumaquamarine", RGB_COLOR(102,205,170) },
|
||||
{ "mediumblue", RGB_COLOR(0,0,205) },
|
||||
{ "mediumorchid", RGB_COLOR(186,85,211) },
|
||||
{ "mediumpurple", RGB_COLOR(147,112,219) },
|
||||
{ "mediumseagreen", RGB_COLOR(60,179,113) },
|
||||
{ "mediumslateblue", RGB_COLOR(123,104,238) },
|
||||
{ "mediumspringgreen", RGB_COLOR(0,250,154) },
|
||||
{ "mediumturquoise", RGB_COLOR(72,209,204) },
|
||||
{ "mediumvioletred", RGB_COLOR(199,21,133) },
|
||||
{ "midnightblue", RGB_COLOR(25,25,112) },
|
||||
{ "mintcream", RGB_COLOR(245,255,250) },
|
||||
{ "mistyrose", RGB_COLOR(255,228,225) },
|
||||
{ "moccasin", RGB_COLOR(255,228,181) },
|
||||
{ "navajowhite", RGB_COLOR(255,222,173) },
|
||||
{ "navy", RGB_COLOR(0,0,128) },
|
||||
{ "oldlace", RGB_COLOR(253,245,230) },
|
||||
{ "olive", RGB_COLOR(128,128,0) },
|
||||
{ "olivedrab", RGB_COLOR(107,142,35) },
|
||||
{ "orange", RGB_COLOR(255,165,0) },
|
||||
{ "orangered", RGB_COLOR(255,69,0) },
|
||||
{ "orchid", RGB_COLOR(218,112,214) },
|
||||
{ "palegoldenrod", RGB_COLOR(238,232,170) },
|
||||
{ "palegreen", RGB_COLOR(152,251,152) },
|
||||
{ "paleturquoise", RGB_COLOR(175,238,238) },
|
||||
{ "palevioletred", RGB_COLOR(219,112,147) },
|
||||
{ "papayawhip", RGB_COLOR(255,239,213) },
|
||||
{ "peachpuff", RGB_COLOR(255,218,185) },
|
||||
{ "peru", RGB_COLOR(205,133,63) },
|
||||
{ "pink", RGB_COLOR(255,192,203) },
|
||||
{ "plum", RGB_COLOR(221,160,221) },
|
||||
{ "powderblue", RGB_COLOR(176,224,230) },
|
||||
{ "purple", RGB_COLOR(128,0,128) },
|
||||
{ "red", RGB_COLOR(255,0,0) },
|
||||
{ "rosybrown", RGB_COLOR(188,143,143) },
|
||||
{ "royalblue", RGB_COLOR(65,105,225) },
|
||||
{ "saddlebrown", RGB_COLOR(139,69,19) },
|
||||
{ "salmon", RGB_COLOR(250,128,114) },
|
||||
{ "sandybrown", RGB_COLOR(244,164,96) },
|
||||
{ "seagreen", RGB_COLOR(46,139,87) },
|
||||
{ "seashell", RGB_COLOR(255,245,238) },
|
||||
{ "sienna", RGB_COLOR(160,82,45) },
|
||||
{ "silver", RGB_COLOR(192,192,192) },
|
||||
{ "skyblue", RGB_COLOR(135,206,235) },
|
||||
{ "slateblue", RGB_COLOR(106,90,205) },
|
||||
{ "slategray", RGB_COLOR(112,128,144) },
|
||||
{ "slategrey", RGB_COLOR(112,128,144) },
|
||||
{ "snow", RGB_COLOR(255,250,250) },
|
||||
{ "springgreen", RGB_COLOR(0,255,127) },
|
||||
{ "steelblue", RGB_COLOR(70,130,180) },
|
||||
{ "tan", RGB_COLOR(210,180,140) },
|
||||
{ "teal", RGB_COLOR(0,128,128) },
|
||||
{ "thistle", RGB_COLOR(216,191,216) },
|
||||
{ "tomato", RGB_COLOR(255,99,71) },
|
||||
{ "turquoise", RGB_COLOR(64,224,208) },
|
||||
{ "violet", RGB_COLOR(238,130,238) },
|
||||
{ "wheat", RGB_COLOR(245,222,179) },
|
||||
{ "white", RGB_COLOR(255,255,255) },
|
||||
{ "whitesmoke", RGB_COLOR(245,245,245) },
|
||||
{ "yellow", RGB_COLOR(255,255,0) },
|
||||
{ "yellowgreen", RGB_COLOR(154,205,50) },
|
||||
{ 0, { 0, 0, 0, 0 } } /* Terminator. */
|
||||
};
|
||||
|
||||
/* Get the color named NAME. If the color was found, returns 1 and
|
||||
stores the color into *COLOR. If the color was not found, returns 0 and
|
||||
does not modify *COLOR. */
|
||||
int
|
||||
grub_gui_get_named_color (const char *name,
|
||||
grub_gui_color_t *color)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; named_colors[i].name; i++)
|
||||
{
|
||||
if (grub_strcmp (named_colors[i].name, name) == 0)
|
||||
{
|
||||
*color = named_colors[i].color;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
720
gfxmenu/theme_loader.c
Normal file
720
gfxmenu/theme_loader.c
Normal file
|
@ -0,0 +1,720 @@
|
|||
/* 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>
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* 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_gui_parse_color (value, &view->title_color);
|
||||
else if (! grub_strcmp ("message-color", name))
|
||||
grub_gui_parse_color (value, &view->message_color);
|
||||
else if (! grub_strcmp ("message-bg-color", name))
|
||||
grub_gui_parse_color (value, &view->message_bg_color);
|
||||
else if (! grub_strcmp ("desktop-image", name))
|
||||
{
|
||||
struct grub_video_bitmap *raw_bitmap;
|
||||
struct grub_video_bitmap *scaled_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_create_scaled (&scaled_bitmap,
|
||||
view->screen.width,
|
||||
view->screen.height,
|
||||
raw_bitmap,
|
||||
GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST);
|
||||
grub_video_bitmap_destroy (raw_bitmap);
|
||||
if (! scaled_bitmap)
|
||||
{
|
||||
grub_error_push ();
|
||||
return grub_error (grub_errno, "error scaling desktop image");
|
||||
}
|
||||
|
||||
grub_video_bitmap_destroy (view->desktop_image);
|
||||
view->desktop_image = scaled_bitmap;
|
||||
}
|
||||
else if (! grub_strcmp ("desktop-color", name))
|
||||
grub_gui_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 ("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);
|
||||
}
|
||||
|
||||
/* 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, "position") == 0)
|
||||
{
|
||||
/* Special case for position value. */
|
||||
int x;
|
||||
int y;
|
||||
|
||||
if (grub_gui_parse_2_tuple (value, &x, &y) == GRUB_ERR_NONE)
|
||||
{
|
||||
grub_video_rect_t r;
|
||||
component->ops->get_bounds (component, &r);
|
||||
r.x = x;
|
||||
r.y = y;
|
||||
component->ops->set_bounds (component, &r);
|
||||
}
|
||||
}
|
||||
else if (grub_strcmp (property, "size") == 0)
|
||||
{
|
||||
/* Special case for size value. */
|
||||
int w;
|
||||
int h;
|
||||
|
||||
if (grub_gui_parse_2_tuple (value, &w, &h) == GRUB_ERR_NONE)
|
||||
{
|
||||
grub_video_rect_t r;
|
||||
component->ops->get_bounds (component, &r);
|
||||
r.width = w;
|
||||
r.height = h;
|
||||
component->ops->set_bounds (component, &r);
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
/* Set the object's size to its preferred size unless the user has
|
||||
explicitly specified the size. */
|
||||
component->ops->get_bounds (component, &bounds);
|
||||
if (bounds.width == -1 || bounds.height == -1)
|
||||
{
|
||||
component->ops->get_preferred_size (component,
|
||||
&bounds.width, &bounds.height);
|
||||
component->ops->set_bounds (component, &bounds);
|
||||
}
|
||||
|
||||
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);
|
||||
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->ops->component.destroy (view->canvas);
|
||||
|
||||
view->canvas = grub_gui_canvas_new ();
|
||||
((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->ops->component.destroy (view->canvas);
|
||||
view->canvas = 0;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
grub_free (p.buf);
|
||||
grub_file_close (file);
|
||||
grub_free (p.theme_dir);
|
||||
return grub_errno;
|
||||
}
|
497
gfxmenu/view.c
Normal file
497
gfxmenu/view.c
Normal file
|
@ -0,0 +1,497 @@
|
|||
/* 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/gui_string_util.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.h>
|
||||
#include <grub/icon_manager.h>
|
||||
|
||||
/* The component ID identifying GUI components to be updated as the timeout
|
||||
status changes. */
|
||||
#define TIMEOUT_COMPONENT_ID "__timeout__"
|
||||
|
||||
static void init_terminal (grub_gfxmenu_view_t view);
|
||||
static void destroy_terminal (void);
|
||||
static grub_err_t set_graphics_mode (void);
|
||||
static grub_err_t set_text_mode (void);
|
||||
|
||||
/* 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, grub_gfxmenu_model_t model)
|
||||
{
|
||||
grub_gfxmenu_view_t view;
|
||||
|
||||
view = grub_malloc (sizeof (*view));
|
||||
if (! view)
|
||||
return 0;
|
||||
|
||||
set_graphics_mode ();
|
||||
grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
|
||||
grub_video_get_viewport ((unsigned *) &view->screen.x,
|
||||
(unsigned *) &view->screen.y,
|
||||
(unsigned *) &view->screen.width,
|
||||
(unsigned *) &view->screen.height);
|
||||
|
||||
/* Clear the screen; there may be garbage left over in video memory, and
|
||||
loading the menu style (particularly the background) can take a while. */
|
||||
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 ();
|
||||
|
||||
grub_font_t default_font;
|
||||
grub_gui_color_t default_fg_color;
|
||||
grub_gui_color_t default_bg_color;
|
||||
|
||||
default_font = grub_font_get ("Helvetica 12");
|
||||
default_fg_color = grub_gui_color_rgb (0, 0, 0);
|
||||
default_bg_color = grub_gui_color_rgb (255, 255, 255);
|
||||
|
||||
view->model = model;
|
||||
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->desktop_image = 0;
|
||||
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;
|
||||
|
||||
if (grub_gfxmenu_view_load_theme (view, theme_path) != 0)
|
||||
{
|
||||
grub_gfxmenu_view_destroy (view);
|
||||
return 0;
|
||||
}
|
||||
|
||||
init_terminal (view);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
/* Destroy the view object. All used memory is freed. */
|
||||
void
|
||||
grub_gfxmenu_view_destroy (grub_gfxmenu_view_t view)
|
||||
{
|
||||
grub_video_bitmap_destroy (view->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->ops->component.destroy (view->canvas);
|
||||
grub_free (view);
|
||||
|
||||
set_text_mode ();
|
||||
destroy_terminal ();
|
||||
}
|
||||
|
||||
/* 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
|
||||
draw_background (grub_gfxmenu_view_t view)
|
||||
{
|
||||
if (view->desktop_image)
|
||||
{
|
||||
struct grub_video_bitmap *img = view->desktop_image;
|
||||
grub_video_blit_bitmap (img, GRUB_VIDEO_BLIT_REPLACE,
|
||||
view->screen.x, view->screen.y, 0, 0,
|
||||
grub_video_bitmap_get_width (img),
|
||||
grub_video_bitmap_get_height (img));
|
||||
}
|
||||
else
|
||||
{
|
||||
grub_video_fill_rect (grub_gui_map_color (view->desktop_color),
|
||||
view->screen.x, view->screen.y,
|
||||
view->screen.width, view->screen.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_gui_map_color (view->title_color),
|
||||
x, y);
|
||||
}
|
||||
|
||||
struct progress_value_data
|
||||
{
|
||||
const char *visible;
|
||||
const char *start;
|
||||
const char *end;
|
||||
const char *value;
|
||||
const char *text;
|
||||
};
|
||||
|
||||
static void
|
||||
update_timeout_visit (grub_gui_component_t component,
|
||||
void *userdata)
|
||||
{
|
||||
struct progress_value_data *pv;
|
||||
pv = (struct progress_value_data *) userdata;
|
||||
component->ops->set_property (component, "visible", pv->visible);
|
||||
component->ops->set_property (component, "start", pv->start);
|
||||
component->ops->set_property (component, "end", pv->end);
|
||||
component->ops->set_property (component, "value", pv->value);
|
||||
component->ops->set_property (component, "text", pv->text);
|
||||
}
|
||||
|
||||
static void
|
||||
update_timeout (grub_gfxmenu_view_t view)
|
||||
{
|
||||
char startbuf[20];
|
||||
char valuebuf[20];
|
||||
char msgbuf[120];
|
||||
|
||||
int timeout = grub_gfxmenu_model_get_timeout_ms (view->model);
|
||||
int remaining = grub_gfxmenu_model_get_timeout_remaining_ms (view->model);
|
||||
struct progress_value_data pv;
|
||||
|
||||
pv.visible = timeout > 0 ? "true" : "false";
|
||||
grub_sprintf (startbuf, "%d", -timeout);
|
||||
pv.start = startbuf;
|
||||
pv.end = "0";
|
||||
grub_sprintf (valuebuf, "%d", remaining > 0 ? -remaining : 0);
|
||||
pv.value = valuebuf;
|
||||
|
||||
int seconds_remaining_rounded_up = (remaining + 999) / 1000;
|
||||
grub_sprintf (msgbuf,
|
||||
"The highlighted entry will be booted automatically in %d s.",
|
||||
seconds_remaining_rounded_up);
|
||||
pv.text = msgbuf;
|
||||
|
||||
grub_gui_find_by_id ((grub_gui_component_t) view->canvas,
|
||||
TIMEOUT_COMPONENT_ID, update_timeout_visit, &pv);
|
||||
}
|
||||
|
||||
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->theme_path, view->model);
|
||||
}
|
||||
}
|
||||
|
||||
/* 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
|
||||
draw_message (grub_gfxmenu_view_t view)
|
||||
{
|
||||
char *text = view->progress_message_text;
|
||||
if (! text)
|
||||
return;
|
||||
|
||||
grub_font_t font = view->message_font;
|
||||
grub_video_color_t color = grub_gui_map_color (view->message_color);
|
||||
|
||||
/* Set the timeout bar's frame. */
|
||||
grub_video_rect_t f;
|
||||
f.width = view->screen.width * 4 / 5;
|
||||
f.height = 50;
|
||||
f.x = view->screen.x + (view->screen.width - f.width) / 2;
|
||||
f.y = view->screen.y + view->screen.height - 90 - 20 - f.height;
|
||||
|
||||
/* Border. */
|
||||
grub_video_fill_rect (color,
|
||||
f.x-1, f.y-1, f.width+2, f.height+2);
|
||||
/* Fill. */
|
||||
grub_video_fill_rect (grub_gui_map_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_draw (grub_gfxmenu_view_t view)
|
||||
{
|
||||
grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
|
||||
update_timeout (view);
|
||||
update_menu_components (view);
|
||||
|
||||
draw_background (view);
|
||||
if (view->canvas)
|
||||
view->canvas->ops->component.paint (view->canvas);
|
||||
draw_title (view);
|
||||
draw_message (view);
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
set_graphics_mode (void)
|
||||
{
|
||||
const char *modestr = grub_env_get ("gfxmode");
|
||||
if (!modestr || !modestr[0])
|
||||
modestr = "auto";
|
||||
return grub_video_set_mode (modestr, GRUB_VIDEO_MODE_TYPE_PURE_TEXT, 0);
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
set_text_mode (void)
|
||||
{
|
||||
return grub_video_restore ();
|
||||
}
|
||||
|
||||
static int term_target_width;
|
||||
static int term_target_height;
|
||||
static struct grub_video_render_target *term_target;
|
||||
static int term_initialized;
|
||||
static grub_term_output_t term_original;
|
||||
static grub_gfxmenu_view_t term_view;
|
||||
|
||||
static void
|
||||
repaint_terminal (int x __attribute ((unused)),
|
||||
int y __attribute ((unused)),
|
||||
int width __attribute ((unused)),
|
||||
int height __attribute ((unused)))
|
||||
{
|
||||
if (! term_view)
|
||||
return;
|
||||
|
||||
grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
|
||||
grub_gfxmenu_view_draw (term_view);
|
||||
grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
|
||||
|
||||
int termx = term_view->screen.x
|
||||
+ term_view->screen.width * (10 - 7) / 10 / 2;
|
||||
int termy = term_view->screen.y
|
||||
+ term_view->screen.height * (10 - 7) / 10 / 2;
|
||||
|
||||
grub_gfxmenu_box_t term_box = term_view->terminal_box;
|
||||
if (term_box)
|
||||
{
|
||||
term_box->set_content_size (term_box,
|
||||
term_target_width, term_target_height);
|
||||
|
||||
term_box->draw (term_box,
|
||||
termx - term_box->get_left_pad (term_box),
|
||||
termy - term_box->get_top_pad (term_box));
|
||||
}
|
||||
|
||||
grub_video_blit_render_target (term_target, GRUB_VIDEO_BLIT_REPLACE,
|
||||
termx, termy,
|
||||
0, 0, term_target_width, term_target_height);
|
||||
grub_video_swap_buffers ();
|
||||
}
|
||||
|
||||
static void
|
||||
init_terminal (grub_gfxmenu_view_t view)
|
||||
{
|
||||
term_original = grub_term_get_current_output ();
|
||||
|
||||
term_target_width = view->screen.width * 7 / 10;
|
||||
term_target_height = view->screen.height * 7 / 10;
|
||||
|
||||
grub_video_create_render_target (&term_target,
|
||||
term_target_width,
|
||||
term_target_height,
|
||||
GRUB_VIDEO_MODE_TYPE_RGB
|
||||
| GRUB_VIDEO_MODE_TYPE_ALPHA);
|
||||
if (grub_errno != GRUB_ERR_NONE)
|
||||
return;
|
||||
|
||||
/* 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_init_window (term_target, 0, 0,
|
||||
term_target_width, term_target_height, 0,
|
||||
view->terminal_font_name, 3);
|
||||
if (grub_errno != GRUB_ERR_NONE)
|
||||
return;
|
||||
term_initialized = 1;
|
||||
|
||||
/* XXX: store static pointer to the 'view' object so the repaint callback can access it. */
|
||||
term_view = view;
|
||||
grub_gfxterm_set_repaint_callback (repaint_terminal);
|
||||
grub_term_set_current_output (grub_gfxterm_get_term ());
|
||||
}
|
||||
|
||||
static void destroy_terminal (void)
|
||||
{
|
||||
term_view = 0;
|
||||
if (term_initialized)
|
||||
grub_gfxterm_destroy_window ();
|
||||
grub_gfxterm_set_repaint_callback (0);
|
||||
if (term_target)
|
||||
grub_video_delete_render_target (term_target);
|
||||
if (term_original)
|
||||
grub_term_set_current_output (term_original);
|
||||
}
|
||||
|
||||
|
||||
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_draw (view);
|
||||
grub_video_swap_buffers ();
|
||||
}
|
||||
|
||||
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_draw (view);
|
||||
grub_video_swap_buffers ();
|
||||
}
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
int
|
||||
grub_gfxmenu_view_execute_with_fallback (grub_gfxmenu_view_t view,
|
||||
grub_menu_entry_t entry)
|
||||
{
|
||||
grub_menu_execute_with_fallback (grub_gfxmenu_model_get_menu (view->model),
|
||||
entry, &execute_callback, (void *) view);
|
||||
|
||||
if (set_graphics_mode () != GRUB_ERR_NONE)
|
||||
return 0; /* Failure. */
|
||||
|
||||
/* If we returned, there was a failure. */
|
||||
set_progress_message (view,
|
||||
"Unable to automatically boot. "
|
||||
"Press SPACE to continue.");
|
||||
grub_gfxmenu_view_draw (view);
|
||||
grub_video_swap_buffers ();
|
||||
while (GRUB_TERM_ASCII_CHAR(grub_getkey ()) != ' ')
|
||||
{
|
||||
/* Wait for SPACE to be pressed. */
|
||||
}
|
||||
|
||||
set_progress_message (view, 0); /* Clear the message. */
|
||||
|
||||
return 1; /* Ok. */
|
||||
}
|
||||
|
||||
int
|
||||
grub_gfxmenu_view_execute_entry (grub_gfxmenu_view_t view,
|
||||
grub_menu_entry_t entry)
|
||||
{
|
||||
/* Currently we switch back to text mode by restoring
|
||||
the original terminal before executing the menu entry.
|
||||
It is hard to make it work when executing a menu entry
|
||||
that switches video modes -- it using gfxterm in a
|
||||
window, the repaint callback seems to crash GRUB. */
|
||||
/* TODO: Determine if this works when 'gfxterm' was set as
|
||||
the current terminal before invoking the gfxmenu. */
|
||||
destroy_terminal ();
|
||||
|
||||
grub_menu_execute_entry (entry);
|
||||
if (grub_errno != GRUB_ERR_NONE)
|
||||
grub_wait_after_message ();
|
||||
|
||||
if (set_graphics_mode () != GRUB_ERR_NONE)
|
||||
return 0; /* Failure. */
|
||||
|
||||
init_terminal (view);
|
||||
return 1; /* Ok. */
|
||||
}
|
||||
|
||||
void
|
||||
grub_gfxmenu_view_run_terminal (grub_gfxmenu_view_t view __attribute__((unused)))
|
||||
{
|
||||
grub_cmdline_run (1);
|
||||
}
|
313
gfxmenu/widget-box.c
Normal file
313
gfxmenu/widget-box.c
Normal file
|
@ -0,0 +1,313 @@
|
|||
/* 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 height_s;
|
||||
int height_e;
|
||||
int height_w;
|
||||
int width_n;
|
||||
int width_s;
|
||||
int width_e;
|
||||
int width_w;
|
||||
|
||||
height_n = get_height (self->scaled_pixmaps[BOX_PIXMAP_N]);
|
||||
height_s = get_height (self->scaled_pixmaps[BOX_PIXMAP_S]);
|
||||
height_e = get_height (self->scaled_pixmaps[BOX_PIXMAP_E]);
|
||||
height_w = get_height (self->scaled_pixmaps[BOX_PIXMAP_W]);
|
||||
width_n = get_width (self->scaled_pixmaps[BOX_PIXMAP_N]);
|
||||
width_s = get_width (self->scaled_pixmaps[BOX_PIXMAP_S]);
|
||||
width_e = get_width (self->scaled_pixmaps[BOX_PIXMAP_E]);
|
||||
width_w = get_width (self->scaled_pixmaps[BOX_PIXMAP_W]);
|
||||
|
||||
/* 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);
|
||||
if (grub_errno != GRUB_ERR_NONE)
|
||||
{
|
||||
grub_error_push ();
|
||||
grub_error (grub_errno,
|
||||
"failed to scale bitmap for styled box pixmap #%d", i);
|
||||
}
|
||||
}
|
||||
|
||||
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_left_pad (grub_gfxmenu_box_t self)
|
||||
{
|
||||
return get_width (self->raw_pixmaps[BOX_PIXMAP_W]);
|
||||
}
|
||||
|
||||
static int
|
||||
get_top_pad (grub_gfxmenu_box_t self)
|
||||
{
|
||||
return get_height (self->raw_pixmaps[BOX_PIXMAP_N]);
|
||||
}
|
||||
|
||||
static int
|
||||
get_right_pad (grub_gfxmenu_box_t self)
|
||||
{
|
||||
return get_width (self->raw_pixmaps[BOX_PIXMAP_E]);
|
||||
}
|
||||
|
||||
static int
|
||||
get_bottom_pad (grub_gfxmenu_box_t self)
|
||||
{
|
||||
return get_height (self->raw_pixmaps[BOX_PIXMAP_S]);
|
||||
}
|
||||
|
||||
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_malloc (BOX_NUM_PIXMAPS * sizeof (struct grub_video_bitmap *));
|
||||
box->scaled_pixmaps =
|
||||
(struct grub_video_bitmap **)
|
||||
grub_malloc (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_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;
|
||||
}
|
59
include/grub/gfxmenu_model.h
Normal file
59
include/grub/gfxmenu_model.h
Normal file
|
@ -0,0 +1,59 @@
|
|||
/* gfxmenu_model.h - gfxmenu model interface. */
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef GRUB_GFXMENU_MODEL_HEADER
|
||||
#define GRUB_GFXMENU_MODEL_HEADER 1
|
||||
|
||||
#include <grub/menu.h>
|
||||
|
||||
struct grub_gfxmenu_model; /* Forward declaration of opaque type. */
|
||||
typedef struct grub_gfxmenu_model *grub_gfxmenu_model_t;
|
||||
|
||||
|
||||
grub_gfxmenu_model_t grub_gfxmenu_model_new (grub_menu_t menu);
|
||||
|
||||
void grub_gfxmenu_model_destroy (grub_gfxmenu_model_t model);
|
||||
|
||||
grub_menu_t grub_gfxmenu_model_get_menu (grub_gfxmenu_model_t model);
|
||||
|
||||
void grub_gfxmenu_model_set_timeout (grub_gfxmenu_model_t model);
|
||||
|
||||
void grub_gfxmenu_model_clear_timeout (grub_gfxmenu_model_t model);
|
||||
|
||||
int grub_gfxmenu_model_get_timeout_ms (grub_gfxmenu_model_t model);
|
||||
|
||||
int grub_gfxmenu_model_get_timeout_remaining_ms (grub_gfxmenu_model_t model);
|
||||
|
||||
int grub_gfxmenu_model_timeout_expired (grub_gfxmenu_model_t model);
|
||||
|
||||
int grub_gfxmenu_model_get_num_entries (grub_gfxmenu_model_t model);
|
||||
|
||||
int grub_gfxmenu_model_get_selected_index (grub_gfxmenu_model_t model);
|
||||
|
||||
void grub_gfxmenu_model_set_selected_index (grub_gfxmenu_model_t model,
|
||||
int index);
|
||||
|
||||
const char *grub_gfxmenu_model_get_entry_title (grub_gfxmenu_model_t model,
|
||||
int index);
|
||||
|
||||
grub_menu_entry_t grub_gfxmenu_model_get_entry (grub_gfxmenu_model_t model,
|
||||
int index);
|
||||
|
||||
#endif /* GRUB_GFXMENU_MODEL_HEADER */
|
||||
|
91
include/grub/gfxmenu_view.h
Normal file
91
include/grub/gfxmenu_view.h
Normal file
|
@ -0,0 +1,91 @@
|
|||
/* gfxmenu_view.h - gfxmenu view interface. */
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef GRUB_GFXMENU_VIEW_HEADER
|
||||
#define GRUB_GFXMENU_VIEW_HEADER 1
|
||||
|
||||
#include <grub/types.h>
|
||||
#include <grub/err.h>
|
||||
#include <grub/menu.h>
|
||||
#include <grub/font.h>
|
||||
#include <grub/gfxmenu_model.h>
|
||||
#include <grub/gfxwidgets.h>
|
||||
|
||||
struct grub_gfxmenu_view; /* Forward declaration of opaque type. */
|
||||
typedef struct grub_gfxmenu_view *grub_gfxmenu_view_t;
|
||||
|
||||
|
||||
grub_gfxmenu_view_t grub_gfxmenu_view_new (const char *theme_path,
|
||||
grub_gfxmenu_model_t model);
|
||||
|
||||
void grub_gfxmenu_view_destroy (grub_gfxmenu_view_t view);
|
||||
|
||||
/* 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_err_t grub_gui_recreate_box (grub_gfxmenu_box_t *boxptr,
|
||||
const char *pattern, const char *theme_dir);
|
||||
|
||||
void grub_gfxmenu_view_draw (grub_gfxmenu_view_t view);
|
||||
|
||||
int grub_gfxmenu_view_execute_with_fallback (grub_gfxmenu_view_t view,
|
||||
grub_menu_entry_t entry);
|
||||
|
||||
int grub_gfxmenu_view_execute_entry (grub_gfxmenu_view_t view,
|
||||
grub_menu_entry_t entry);
|
||||
|
||||
void grub_gfxmenu_view_run_terminal (grub_gfxmenu_view_t view);
|
||||
|
||||
|
||||
|
||||
/* Implementation details -- this should not be used outside of the
|
||||
view itself. */
|
||||
|
||||
#include <grub/video.h>
|
||||
#include <grub/bitmap.h>
|
||||
#include <grub/gui.h>
|
||||
#include <grub/gfxwidgets.h>
|
||||
#include <grub/icon_manager.h>
|
||||
|
||||
/* Definition of the private representation of the view. */
|
||||
struct grub_gfxmenu_view
|
||||
{
|
||||
grub_video_rect_t screen;
|
||||
|
||||
grub_font_t title_font;
|
||||
grub_font_t message_font;
|
||||
char *terminal_font_name;
|
||||
grub_gui_color_t title_color;
|
||||
grub_gui_color_t message_color;
|
||||
grub_gui_color_t message_bg_color;
|
||||
struct grub_video_bitmap *desktop_image;
|
||||
grub_gui_color_t desktop_color;
|
||||
grub_gfxmenu_box_t terminal_box;
|
||||
char *title_text;
|
||||
char *progress_message_text;
|
||||
char *theme_path;
|
||||
|
||||
grub_gui_container_t canvas;
|
||||
|
||||
grub_gfxmenu_model_t model;
|
||||
};
|
||||
|
||||
#endif /* ! GRUB_GFXMENU_VIEW_HEADER */
|
49
include/grub/gfxwidgets.h
Normal file
49
include/grub/gfxwidgets.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
/* gfxwidgets.h - Widgets for the graphical menu (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/>.
|
||||
*/
|
||||
|
||||
#ifndef GRUB_GFXWIDGETS_HEADER
|
||||
#define GRUB_GFXWIDGETS_HEADER 1
|
||||
|
||||
#include <grub/video.h>
|
||||
|
||||
typedef struct grub_gfxmenu_box *grub_gfxmenu_box_t;
|
||||
|
||||
struct grub_gfxmenu_box
|
||||
{
|
||||
/* The size of the content. */
|
||||
int content_width;
|
||||
int content_height;
|
||||
|
||||
struct grub_video_bitmap **raw_pixmaps;
|
||||
struct grub_video_bitmap **scaled_pixmaps;
|
||||
|
||||
void (*draw) (grub_gfxmenu_box_t self, int x, int y);
|
||||
void (*set_content_size) (grub_gfxmenu_box_t self,
|
||||
int width, int height);
|
||||
int (*get_left_pad) (grub_gfxmenu_box_t self);
|
||||
int (*get_top_pad) (grub_gfxmenu_box_t self);
|
||||
int (*get_right_pad) (grub_gfxmenu_box_t self);
|
||||
int (*get_bottom_pad) (grub_gfxmenu_box_t self);
|
||||
void (*destroy) (grub_gfxmenu_box_t self);
|
||||
};
|
||||
|
||||
grub_gfxmenu_box_t grub_gfxmenu_create_box (const char *pixmaps_prefix,
|
||||
const char *pixmaps_suffix);
|
||||
|
||||
#endif /* ! GRUB_GFXWIDGETS_HEADER */
|
165
include/grub/gui.h
Normal file
165
include/grub/gui.h
Normal file
|
@ -0,0 +1,165 @@
|
|||
/* gui.h - GUI components header file. */
|
||||
/*
|
||||
* 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/err.h>
|
||||
#include <grub/video.h>
|
||||
#include <grub/bitmap.h>
|
||||
#include <grub/gfxmenu_model.h>
|
||||
|
||||
#ifndef GRUB_GUI_H
|
||||
#define GRUB_GUI_H 1
|
||||
|
||||
/* A representation of a color. Unlike grub_video_color_t, this
|
||||
representation is independent of any video mode specifics. */
|
||||
typedef struct grub_gui_color
|
||||
{
|
||||
grub_uint8_t red;
|
||||
grub_uint8_t green;
|
||||
grub_uint8_t blue;
|
||||
grub_uint8_t alpha;
|
||||
} grub_gui_color_t;
|
||||
|
||||
typedef struct grub_gui_component *grub_gui_component_t;
|
||||
typedef struct grub_gui_container *grub_gui_container_t;
|
||||
typedef struct grub_gui_list *grub_gui_list_t;
|
||||
|
||||
typedef void (*grub_gui_component_callback) (grub_gui_component_t component,
|
||||
void *userdata);
|
||||
|
||||
/* Component interface. */
|
||||
|
||||
struct grub_gui_component_ops
|
||||
{
|
||||
void (*destroy) (void *self);
|
||||
const char * (*get_id) (void *self);
|
||||
int (*is_instance) (void *self, const char *type);
|
||||
void (*paint) (void *self);
|
||||
void (*set_parent) (void *self, grub_gui_container_t parent);
|
||||
grub_gui_container_t (*get_parent) (void *self);
|
||||
void (*set_bounds) (void *self, const grub_video_rect_t *bounds);
|
||||
void (*get_bounds) (void *self, grub_video_rect_t *bounds);
|
||||
void (*get_preferred_size) (void *self, int *width, int *height);
|
||||
grub_err_t (*set_property) (void *self, const char *name, const char *value);
|
||||
};
|
||||
|
||||
struct grub_gui_container_ops
|
||||
{
|
||||
struct grub_gui_component_ops component;
|
||||
void (*add) (void *self, grub_gui_component_t comp);
|
||||
void (*remove) (void *self, grub_gui_component_t comp);
|
||||
void (*iterate_children) (void *self,
|
||||
grub_gui_component_callback cb, void *userdata);
|
||||
};
|
||||
|
||||
struct grub_gui_list_ops
|
||||
{
|
||||
struct grub_gui_component_ops component_ops;
|
||||
void (*set_view_info) (void *self,
|
||||
const char *theme_path,
|
||||
grub_gfxmenu_model_t menu);
|
||||
};
|
||||
|
||||
struct grub_gui_component
|
||||
{
|
||||
struct grub_gui_component_ops *ops;
|
||||
};
|
||||
|
||||
struct grub_gui_container
|
||||
{
|
||||
struct grub_gui_container_ops *ops;
|
||||
};
|
||||
|
||||
struct grub_gui_list
|
||||
{
|
||||
struct grub_gui_list_ops *ops;
|
||||
};
|
||||
|
||||
|
||||
/* Interfaces to concrete component classes. */
|
||||
|
||||
grub_gui_container_t grub_gui_canvas_new (void);
|
||||
grub_gui_container_t grub_gui_vbox_new (void);
|
||||
grub_gui_container_t grub_gui_hbox_new (void);
|
||||
grub_gui_component_t grub_gui_label_new (void);
|
||||
grub_gui_component_t grub_gui_image_new (void);
|
||||
grub_gui_component_t grub_gui_progress_bar_new (void);
|
||||
grub_gui_component_t grub_gui_list_new (void);
|
||||
grub_gui_component_t grub_gui_circular_progress_new (void);
|
||||
|
||||
/* Manipulation functions. */
|
||||
|
||||
/* Visit all components with the specified ID. */
|
||||
void grub_gui_find_by_id (grub_gui_component_t root,
|
||||
const char *id,
|
||||
grub_gui_component_callback cb,
|
||||
void *userdata);
|
||||
|
||||
/* Visit all components. */
|
||||
void grub_gui_iterate_recursively (grub_gui_component_t root,
|
||||
grub_gui_component_callback cb,
|
||||
void *userdata);
|
||||
|
||||
/* Helper functions. */
|
||||
|
||||
static __inline void
|
||||
grub_gui_save_viewport (grub_video_rect_t *r)
|
||||
{
|
||||
grub_video_get_viewport ((unsigned *) &r->x,
|
||||
(unsigned *) &r->y,
|
||||
(unsigned *) &r->width,
|
||||
(unsigned *) &r->height);
|
||||
}
|
||||
|
||||
static __inline void
|
||||
grub_gui_restore_viewport (const grub_video_rect_t *r)
|
||||
{
|
||||
grub_video_set_viewport (r->x, r->y, r->width, r->height);
|
||||
}
|
||||
|
||||
/* Set a new viewport relative the the current one, saving the current
|
||||
viewport in OLD so it can be later restored. */
|
||||
static __inline void
|
||||
grub_gui_set_viewport (const grub_video_rect_t *r, grub_video_rect_t *old)
|
||||
{
|
||||
grub_gui_save_viewport (old);
|
||||
grub_video_set_viewport (old->x + r->x,
|
||||
old->y + r->y,
|
||||
r->width,
|
||||
r->height);
|
||||
}
|
||||
|
||||
static __inline grub_gui_color_t
|
||||
grub_gui_color_rgb (int r, int g, int b)
|
||||
{
|
||||
grub_gui_color_t c;
|
||||
c.red = r;
|
||||
c.green = g;
|
||||
c.blue = b;
|
||||
c.alpha = 255;
|
||||
return c;
|
||||
}
|
||||
|
||||
static __inline grub_video_color_t
|
||||
grub_gui_map_color (grub_gui_color_t c)
|
||||
{
|
||||
return grub_video_map_rgba (c.red, c.green, c.blue, c.alpha);
|
||||
}
|
||||
|
||||
#endif /* ! GRUB_GUI_H */
|
39
include/grub/gui_string_util.h
Normal file
39
include/grub/gui_string_util.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
/* gui_string_util.h - String utilities for the graphical menu interface. */
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef GRUB_GUI_STRING_UTIL_HEADER
|
||||
#define GRUB_GUI_STRING_UTIL_HEADER 1
|
||||
|
||||
#include <grub/types.h>
|
||||
#include <grub/gui.h>
|
||||
|
||||
char *grub_new_substring (const char *buf,
|
||||
grub_size_t start, grub_size_t end);
|
||||
|
||||
char *grub_resolve_relative_path (const char *base, const char *path);
|
||||
|
||||
char *grub_get_dirname (const char *file_path);
|
||||
|
||||
int grub_gui_get_named_color (const char *name, grub_gui_color_t *color);
|
||||
|
||||
grub_err_t grub_gui_parse_color (const char *s, grub_gui_color_t *color);
|
||||
|
||||
grub_err_t grub_gui_parse_2_tuple (const char *s, int *px, int *py);
|
||||
|
||||
#endif /* GRUB_GUI_STRING_UTIL_HEADER */
|
41
include/grub/icon_manager.h
Normal file
41
include/grub/icon_manager.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
/* icon_manager.h - gfxmenu icon manager. */
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef GRUB_ICON_MANAGER_HEADER
|
||||
#define GRUB_ICON_MANAGER_HEADER 1
|
||||
|
||||
#include <grub/menu.h>
|
||||
#include <grub/bitmap.h>
|
||||
|
||||
/* Forward declaration of opaque structure handle type. */
|
||||
typedef struct grub_gfxmenu_icon_manager *grub_gfxmenu_icon_manager_t;
|
||||
|
||||
grub_gfxmenu_icon_manager_t grub_gfxmenu_icon_manager_new (void);
|
||||
void grub_gfxmenu_icon_manager_destroy (grub_gfxmenu_icon_manager_t mgr);
|
||||
void grub_gfxmenu_icon_manager_clear_cache (grub_gfxmenu_icon_manager_t mgr);
|
||||
void grub_gfxmenu_icon_manager_set_theme_path (grub_gfxmenu_icon_manager_t mgr,
|
||||
const char *path);
|
||||
void grub_gfxmenu_icon_manager_set_icon_size (grub_gfxmenu_icon_manager_t mgr,
|
||||
int width, int height);
|
||||
struct grub_video_bitmap *
|
||||
grub_gfxmenu_icon_manager_get_icon (grub_gfxmenu_icon_manager_t mgr,
|
||||
grub_menu_entry_t entry);
|
||||
|
||||
#endif /* GRUB_ICON_MANAGER_HEADER */
|
||||
|
|
@ -261,7 +261,7 @@ grub_term_set_current_input (grub_term_input_t term)
|
|||
}
|
||||
|
||||
static inline grub_err_t
|
||||
grub_term_set_current_output (grub_term_output_t term)
|
||||
grub_term_set_current_output (const struct grub_term_output *term)
|
||||
{
|
||||
return grub_handler_set_current (&grub_term_output_class,
|
||||
GRUB_AS_HANDLER (term));
|
||||
|
|
Loading…
Reference in a new issue