e74b3947af
Don't skip first class.
263 lines
7.7 KiB
C
263 lines
7.7 KiB
C
/* icon_manager.c - gfxmenu icon manager. */
|
|
/*
|
|
* GRUB -- GRand Unified Bootloader
|
|
* Copyright (C) 2008,2009 Free Software Foundation, Inc.
|
|
*
|
|
* GRUB is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* GRUB is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <grub/types.h>
|
|
#include <grub/misc.h>
|
|
#include <grub/mm.h>
|
|
#include <grub/err.h>
|
|
#include <grub/gui_string_util.h>
|
|
#include <grub/bitmap.h>
|
|
#include <grub/bitmap_scale.h>
|
|
#include <grub/menu.h>
|
|
#include <grub/icon_manager.h>
|
|
#include <grub/env.h>
|
|
|
|
/* Currently hard coded to '.png' extension. */
|
|
static const char icon_extension[] = ".png";
|
|
|
|
typedef struct icon_entry
|
|
{
|
|
char *class_name;
|
|
struct grub_video_bitmap *bitmap;
|
|
struct icon_entry *next;
|
|
} *icon_entry_t;
|
|
|
|
struct grub_gfxmenu_icon_manager
|
|
{
|
|
char *theme_path;
|
|
int icon_width;
|
|
int icon_height;
|
|
|
|
/* Icon cache: linked list w/ dummy head node. */
|
|
struct icon_entry cache;
|
|
};
|
|
|
|
|
|
/* Create a new icon manager and return a point to it. */
|
|
grub_gfxmenu_icon_manager_t
|
|
grub_gfxmenu_icon_manager_new (void)
|
|
{
|
|
grub_gfxmenu_icon_manager_t mgr;
|
|
mgr = grub_malloc (sizeof (*mgr));
|
|
if (! mgr)
|
|
return 0;
|
|
|
|
mgr->theme_path = 0;
|
|
mgr->icon_width = 0;
|
|
mgr->icon_height = 0;
|
|
|
|
/* Initialize the dummy head node. */
|
|
mgr->cache.class_name = 0;
|
|
mgr->cache.bitmap = 0;
|
|
mgr->cache.next = 0;
|
|
|
|
return mgr;
|
|
}
|
|
|
|
/* Destroy the icon manager MGR, freeing all resources used by it.
|
|
|
|
Note: Any bitmaps returned by grub_gfxmenu_icon_manager_get_icon()
|
|
are destroyed and must not be used by the caller after this function
|
|
is called. */
|
|
void
|
|
grub_gfxmenu_icon_manager_destroy (grub_gfxmenu_icon_manager_t mgr)
|
|
{
|
|
grub_gfxmenu_icon_manager_clear_cache (mgr);
|
|
grub_free (mgr->theme_path);
|
|
grub_free (mgr);
|
|
}
|
|
|
|
/* Clear the icon cache. */
|
|
void
|
|
grub_gfxmenu_icon_manager_clear_cache (grub_gfxmenu_icon_manager_t mgr)
|
|
{
|
|
icon_entry_t cur;
|
|
icon_entry_t next;
|
|
for (cur = mgr->cache.next; cur; cur = next)
|
|
{
|
|
next = cur->next;
|
|
grub_free (cur->class_name);
|
|
grub_video_bitmap_destroy (cur->bitmap);
|
|
grub_free (cur);
|
|
}
|
|
mgr->cache.next = 0;
|
|
}
|
|
|
|
/* Set the theme path. If the theme path is changed, the icon cache
|
|
is cleared. */
|
|
void
|
|
grub_gfxmenu_icon_manager_set_theme_path (grub_gfxmenu_icon_manager_t mgr,
|
|
const char *path)
|
|
{
|
|
/* Clear the cache if the theme path has changed. */
|
|
if (((mgr->theme_path == 0) != (path == 0))
|
|
|| (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;
|
|
int l;
|
|
|
|
path = grub_malloc (grub_strlen (dir) + grub_strlen (class_name)
|
|
+ grub_strlen (icon_extension) + 3);
|
|
if (! path)
|
|
return 0;
|
|
|
|
grub_strcpy (path, dir);
|
|
l = grub_strlen (path);
|
|
if (path[l-1] != '/')
|
|
{
|
|
path[l] = '/';
|
|
path[l+1] = 0;
|
|
}
|
|
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);
|
|
}
|
|
|
|
grub_free (theme_dir);
|
|
if (! icon)
|
|
{
|
|
const char *icondir;
|
|
|
|
icondir = grub_env_get ("icondir");
|
|
if (icondir)
|
|
icon = try_loading_icon (mgr, icondir, class_name);
|
|
}
|
|
|
|
/* No icon was found. */
|
|
/* This should probably be noted in the cache, so that a search is not
|
|
performed each time an icon for CLASS_NAME is requested. */
|
|
if (! icon)
|
|
return 0;
|
|
|
|
/* Insert a new cache entry for this icon. */
|
|
entry = grub_malloc (sizeof (*entry));
|
|
if (! entry)
|
|
{
|
|
grub_video_bitmap_destroy (icon);
|
|
return 0;
|
|
}
|
|
entry->class_name = grub_strdup (class_name);
|
|
entry->bitmap = icon;
|
|
entry->next = mgr->cache.next;
|
|
mgr->cache.next = entry; /* Link it into the cache. */
|
|
return entry->bitmap;
|
|
}
|
|
|
|
/* Get the best available icon for ENTRY. Beginning with the first class
|
|
listed in the menu entry and proceeding forward, an icon for each class
|
|
is searched for. The first icon found is returned. The returned icon
|
|
is scaled to the size specified by
|
|
grub_gfxmenu_icon_manager_set_icon_size().
|
|
|
|
Note: Bitmaps returned by this function are destroyed when the
|
|
icon manager is destroyed.
|
|
*/
|
|
struct grub_video_bitmap *
|
|
grub_gfxmenu_icon_manager_get_icon (grub_gfxmenu_icon_manager_t mgr,
|
|
grub_menu_entry_t entry)
|
|
{
|
|
struct grub_menu_entry_class *c;
|
|
struct grub_video_bitmap *icon;
|
|
|
|
/* Try each class in succession. */
|
|
icon = 0;
|
|
for (c = entry->classes; c && ! icon; c = c->next)
|
|
icon = get_icon_by_class (mgr, c->name);
|
|
return icon;
|
|
}
|