netsurf/riscos/theme.c

2249 lines
62 KiB
C

/*
* Copyright 2004, 2005 Richard Wilson <info@tinct.net>
*
* This file is part of NetSurf, http://www.netsurf-browser.org/
*
* NetSurf 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; version 2 of the License.
*
* NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
/** \file
* Window themes and toolbars (implementation).
*/
#include <alloca.h>
#include <assert.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include "oslib/dragasprite.h"
#include "oslib/os.h"
#include "oslib/osgbpb.h"
#include "oslib/osfile.h"
#include "oslib/osfind.h"
#include "oslib/osspriteop.h"
#include "oslib/wimpspriteop.h"
#include "oslib/squash.h"
#include "oslib/wimp.h"
#include "oslib/wimpextend.h"
#include "oslib/wimpspriteop.h"
#include "content/content.h"
#include "desktop/gui.h"
#include "riscos/dialog.h"
#include "riscos/gui.h"
#include "riscos/menus.h"
#include "riscos/options.h"
#include "riscos/theme.h"
#include "riscos/treeview.h"
#include "riscos/wimp.h"
#include "riscos/wimp_event.h"
#include "riscos/wimputils.h"
#include "utils/log.h"
#include "utils/utils.h"
#define THEME_URL_MEMORY 256
#define THEME_THROBBER_MEMORY 12
static struct theme_descriptor *theme_current = NULL;
static struct theme_descriptor *theme_descriptors = NULL;
static struct toolbar *theme_toolbar_drag = NULL;
static struct toolbar_icon *theme_toolbar_icon_drag = NULL;
static bool theme_toolbar_editor_drag = false;
/* these order of the icons must match the numbers defined in riscos/gui.h */
static const char * theme_browser_icons[] = {"back", "forward", "stop",
"reload", "home", "history", "save", "print", "hotlist",
"scale", "search", "up", NULL};
static const char * theme_hotlist_icons[] = {"delete", "expand", "open",
"launch", "create", NULL};
static const char * theme_history_icons[] = {"delete", "expand", "open",
"launch", NULL};
static const char * theme_cookies_icons[] = {"delete", "expand", "open",
NULL};
static bool ro_gui_theme_add_descriptor(const char *folder, const char *leafname);
static void ro_gui_theme_redraw(wimp_draw *redraw);
static void ro_gui_theme_get_available_in_dir(const char *directory);
static void ro_gui_theme_free(struct theme_descriptor *descriptor);
static struct toolbar_icon *ro_gui_theme_add_toolbar_icon(
struct toolbar *toolbar, const char *name, int icon_number);
static void ro_gui_theme_update_toolbar_icon(struct toolbar *toolbar,
struct toolbar_icon *icon);
static void ro_gui_theme_destroy_toolbar_icon(struct toolbar_icon *icon);
static void ro_gui_theme_link_toolbar_icon(struct toolbar *toolbar,
struct toolbar_icon *icon, struct toolbar_icon *link,
bool before);
static void ro_gui_theme_delink_toolbar_icon(struct toolbar *toolbar,
struct toolbar_icon *icon);
static struct toolbar_icon *ro_gui_theme_toolbar_get_insert_icon(
struct toolbar *toolbar, int x, int y, bool *before);
static void ro_gui_theme_add_toolbar_icons(struct toolbar *toolbar,
const char* icons[], const char* ident);
static void ro_gui_theme_set_help_prefix(struct toolbar *toolbar);
/* A basic window for the toolbar and status
*/
static wimp_window theme_toolbar_window = {
{0, 0, 1, 1},
0,
0,
wimp_TOP,
wimp_WINDOW_NEW_FORMAT | wimp_WINDOW_MOVEABLE | wimp_WINDOW_NO_BOUNDS |
wimp_WINDOW_FURNITURE_WINDOW |
wimp_WINDOW_IGNORE_XEXTENT | wimp_WINDOW_IGNORE_YEXTENT,
wimp_COLOUR_BLACK,
wimp_COLOUR_LIGHT_GREY,
wimp_COLOUR_LIGHT_GREY,
wimp_COLOUR_VERY_LIGHT_GREY,
wimp_COLOUR_DARK_GREY,
wimp_COLOUR_MID_LIGHT_GREY,
wimp_COLOUR_CREAM,
wimp_WINDOW_NEVER3D | 0x16u /* RISC OS 5.03+ */,
{0, 0, 16384, 16384},
0,
0,
wimpspriteop_AREA,
1,
1,
{""},
0,
{ }
};
/* Shared icon validation
*/
static char theme_url_validation[] = "Pptr_write;KN";
static char theme_null_text_string[] = "";
static char theme_separator_name[] = "separator";
static char theme_favicon_sprite[12];
/**
* Initialise the theme handler
*/
void ro_gui_theme_initialise(void)
{
struct theme_descriptor *descriptor;
theme_descriptors = ro_gui_theme_get_available();
descriptor = ro_gui_theme_find(option_theme);
if (!descriptor)
descriptor = ro_gui_theme_find("Aletheia");
ro_gui_theme_apply(descriptor);
}
/**
* Finalise the theme handler
*/
void ro_gui_theme_finalise(void)
{
ro_gui_theme_close(theme_current, false);
ro_gui_theme_free(theme_descriptors);
}
/**
* Finds a theme from the cached values.
*
* The returned theme is only guaranteed to be valid until the next call
* to ro_gui_theme_get_available() unless it has been opened using
* ro_gui_theme_open().
*
* \param leafname the filename of the theme_descriptor to return
* \return the requested theme_descriptor, or NULL if not found
*/
struct theme_descriptor *ro_gui_theme_find(const char *leafname)
{
struct theme_descriptor *descriptor;
if (!leafname)
return NULL;
for (descriptor = theme_descriptors; descriptor;
descriptor = descriptor->next)
if (!strcmp(leafname, descriptor->leafname))
return descriptor;
/* fallback for 10 chars on old filesystems */
for (descriptor = theme_descriptors; descriptor;
descriptor = descriptor->next)
if (!strncmp(leafname, descriptor->leafname, 10))
return descriptor;
return NULL;
}
/**
* Reads and caches the currently available themes.
*
* \return the requested theme_descriptor, or NULL if not found
*/
struct theme_descriptor *ro_gui_theme_get_available(void)
{
struct theme_descriptor *current;
struct theme_descriptor *test;
/* close any unused descriptors */
ro_gui_theme_free(theme_descriptors);
/* add our default 'Aletheia' theme */
ro_gui_theme_add_descriptor("NetSurf:Resources", "Aletheia");
/* scan our choices directory */
ro_gui_theme_get_available_in_dir(option_theme_path);
/* sort alphabetically in a very rubbish way */
if ((theme_descriptors) && (theme_descriptors->next)) {
current = theme_descriptors;
while ((test = current->next)) {
if (strcmp(current->name, test->name) > 0) {
current->next->previous = current->previous;
if (current->previous)
current->previous->next = current->next;
current->next = test->next;
test->next = current;
current->previous = test;
if (current->next)
current->next->previous = current;
current = test->previous;
if (!current) current = test;
} else {
current = current->next;
}
}
while (theme_descriptors->previous)
theme_descriptors = theme_descriptors->previous;
}
return theme_descriptors;
}
/**
* Adds the themes in a directory to the global cache.
*
* \param directory the directory to scan
*/
static void ro_gui_theme_get_available_in_dir(const char *directory)
{
int context = 0;
int read_count;
osgbpb_INFO(100) info;
os_error *error;
while (context != -1) {
/* read some directory info */
error = xosgbpb_dir_entries_info(directory,
(osgbpb_info_list *) &info, 1, context,
sizeof(info), 0, &read_count, &context);
if (error) {
LOG(("xosgbpb_dir_entries_info: 0x%x: %s",
error->errnum, error->errmess));
if (error->errnum == 0xd6) /* no such dir */
return;
warn_user("MiscError", error->errmess);
break;
}
/* only process files */
if ((read_count != 0) && (info.obj_type == fileswitch_IS_FILE))
ro_gui_theme_add_descriptor(directory, info.name);
}
}
/**
* Checks a theme is valid and adds it to the current list
*
* \param folder the theme folder
* \param leafname the theme leafname
* \return whether the theme was added
*/
bool ro_gui_theme_add_descriptor(const char *folder, const char *leafname)
{
struct theme_file_header file_header;
struct theme_descriptor *current;
struct theme_descriptor *test;
int output_left;
os_fw file_handle;
os_error *error;
char *filename;
/* create a full filename */
filename = malloc(strlen(folder) + strlen(leafname) + 2);
if (!filename) {
LOG(("No memory for malloc"));
warn_user("NoMemory", 0);
return false;
}
sprintf(filename, "%s.%s", folder, leafname);
/* get the header */
error = xosfind_openinw(osfind_NO_PATH, filename, 0,
&file_handle);
if (error) {
LOG(("xosfind_openinw: 0x%x: %s",
error->errnum, error->errmess));
warn_user("FileError", error->errmess);
free(filename);
return false;
}
if (file_handle == 0) {
free(filename);
return false;
}
error = xosgbpb_read_atw(file_handle,
(byte *) &file_header,
sizeof (struct theme_file_header),
0, &output_left);
xosfind_closew(file_handle);
if (error) {
LOG(("xosbgpb_read_atw: 0x%x: %s",
error->errnum, error->errmess));
warn_user("FileError", error->errmess);
free(filename);
return false;
}
if (output_left > 0) { /* should try to read more? */
free(filename);
return false;
}
/* create a new theme descriptor */
current = (struct theme_descriptor *)calloc(1,
sizeof(struct theme_descriptor));
if (!current) {
LOG(("calloc failed"));
warn_user("NoMemory", 0);
free(filename);
return false;
}
if (!ro_gui_theme_read_file_header(current, &file_header)) {
free(filename);
free(current);
return false;
}
current->filename = filename;
current->leafname = current->filename + strlen(folder) + 1;
/* don't add duplicates */
for (test = theme_descriptors; test; test = test->next) {
if (!strcmp(current->name, test->name)) {
free(current->filename);
free(current);
return false;
}
}
/* link in our new descriptor at the head*/
if (theme_descriptors) {
current->next = theme_descriptors;
theme_descriptors->previous = current;
}
theme_descriptors = current;
return true;
}
/**
* Fills in the basic details for a descriptor from a file header.
* The filename string is not set.
*
* \param descriptor the descriptor to set up
* \param file_header the header to read from
* \return false for a badly formed theme, true otherwise
*/
bool ro_gui_theme_read_file_header(struct theme_descriptor *descriptor,
struct theme_file_header *file_header)
{
if ((file_header->magic_value != 0x4d54534e) ||
(file_header->parser_version > 2))
return false;
strcpy(descriptor->name, file_header->name);
strcpy(descriptor->author, file_header->author);
descriptor->browser_background = file_header->browser_bg;
descriptor->hotlist_background = file_header->hotlist_bg;
descriptor->status_background = file_header->status_bg;
descriptor->status_foreground = file_header->status_fg;
descriptor->decompressed_size = file_header->decompressed_sprite_size;
descriptor->compressed_size = file_header->compressed_sprite_size;
if (file_header->parser_version >= 2) {
descriptor->throbber_right =
!(file_header->theme_flags & (1 << 0));
descriptor->throbber_redraw =
file_header->theme_flags & (1 << 1);
} else {
descriptor->throbber_right =
(file_header->theme_flags == 0x00);
descriptor->throbber_redraw = true;
}
return true;
}
/**
* Opens a theme ready for use.
*
* \param descriptor the theme_descriptor to open
* \param list whether to open all themes in the list
* \return whether the operation was successful
*/
bool ro_gui_theme_open(struct theme_descriptor *descriptor, bool list)
{
fileswitch_object_type obj_type;
squash_output_status status;
os_coord dimensions;
os_mode mode;
os_error *error;
struct theme_descriptor *next_descriptor;
char sprite_name[16];
const char *name = sprite_name;
bool result = true;
int i, n;
int workspace_size, file_size;
char *raw_data, *workspace;
osspriteop_area *decompressed;
/* If we are freeing the whole of the list then we need to
start at the first descriptor.
*/
if (list && descriptor)
while (descriptor->previous) descriptor = descriptor->previous;
/* Open the themes
*/
for (; descriptor; descriptor = next_descriptor) {
/* see if we should iterate through the entire list */
if (list)
next_descriptor = descriptor->next;
else
next_descriptor = NULL;
/* if we are already loaded, increase the usage count */
if (descriptor->theme) {
descriptor->theme->users = descriptor->theme->users + 1;
continue;
}
/* create a new theme */
descriptor->theme = (struct theme *)calloc(1,
sizeof(struct theme));
if (!descriptor->theme) {
LOG(("calloc() failed"));
warn_user("NoMemory", 0);
continue;
}
descriptor->theme->users = 1;
/* try to load the associated file */
error = xosfile_read_stamped_no_path(descriptor->filename,
&obj_type, 0, 0, &file_size, 0, 0);
if (error) {
LOG(("xosfile_read_stamped_no_path: 0x%x: %s",
error->errnum, error->errmess));
warn_user("FileError", error->errmess);
continue;
}
if (obj_type != fileswitch_IS_FILE)
continue;
raw_data = malloc(file_size);
if (!raw_data) {
LOG(("malloc() failed"));
warn_user("NoMemory", 0);
continue;
}
error = xosfile_load_stamped_no_path(descriptor->filename,
(byte *)raw_data, 0, 0, 0, 0, 0);
if (error) {
free(raw_data);
LOG(("xosfile_load_stamped_no_path: 0x%x: %s",
error->errnum, error->errmess));
warn_user("FileError", error->errmess);
continue;
}
/* decompress the new data */
error = xsquash_decompress_return_sizes(-1, &workspace_size, 0);
if (error) {
free(raw_data);
LOG(("xsquash_decompress_return_sizes: 0x%x: %s",
error->errnum, error->errmess));
warn_user("MiscError", error->errmess);
continue;
}
decompressed = (osspriteop_area *)malloc(
descriptor->decompressed_size);
workspace = malloc(workspace_size);
if ((!decompressed) || (!workspace)) {
free(decompressed);
free(raw_data);
LOG(("malloc() failed"));
warn_user("NoMemory", 0);
continue;
}
error = xsquash_decompress(squash_INPUT_ALL_PRESENT, workspace,
(byte *)(raw_data + sizeof(
struct theme_file_header)),
descriptor->compressed_size,
(byte *)decompressed,
descriptor->decompressed_size,
&status, 0, 0, 0, 0);
free(workspace);
free(raw_data);
if (error) {
free(decompressed);
LOG(("xsquash_decompress: 0x%x: %s",
error->errnum, error->errmess));
warn_user("MiscError", error->errmess);
continue;
}
if (status != 0) {
free(decompressed);
continue;
}
descriptor->theme->sprite_area = decompressed;
/* find the highest sprite called 'throbber%i', and get the
* maximum dimensions for all 'thobber%i' icons. */
for (i = 1; i <= descriptor->theme->sprite_area->sprite_count;
i++) {
error = xosspriteop_return_name(osspriteop_USER_AREA,
descriptor->theme->sprite_area,
sprite_name, 16, i, 0);
if (error) {
LOG(("xosspriteop_return_name: 0x%x: %s",
error->errnum, error->errmess));
warn_user("MiscError", error->errmess);
continue;
}
if (strncmp(sprite_name, "throbber", 8))
continue;
/* get the max sprite width/height */
error = xosspriteop_read_sprite_info(
osspriteop_USER_AREA,
descriptor->theme->sprite_area,
(osspriteop_id) name,
&dimensions.x, &dimensions.y,
(osbool *) 0, &mode);
if (error) {
LOG(("xosspriteop_read_sprite_info: 0x%x: %s",
error->errnum, error->errmess));
warn_user("MiscError", error->errmess);
continue;
}
ro_convert_pixels_to_os_units(&dimensions, mode);
if (descriptor->theme->throbber_width < dimensions.x)
descriptor->theme->throbber_width =
dimensions.x;
if (descriptor->theme->throbber_height < dimensions.y)
descriptor->theme->throbber_height =
dimensions.y;
/* get the throbber number */
n = atoi(sprite_name + 8);
if (descriptor->theme->throbber_frames < n)
descriptor->theme->throbber_frames = n;
}
}
return result;
}
/**
* Applies the theme to all current windows and subsequent ones.
*
* \param descriptor the theme_descriptor to open
* \return whether the operation was successful
*/
bool ro_gui_theme_apply(struct theme_descriptor *descriptor)
{
struct theme_descriptor *theme_previous;
/* check if the theme is already applied */
if (descriptor == theme_current)
return true;
/* re-open the new-theme and release the current theme */
if (!ro_gui_theme_open(descriptor, false))
return false;
theme_previous = theme_current;
theme_current = descriptor;
/* apply the theme to all the current windows */
ro_gui_window_update_theme();
ro_gui_tree_update_theme(hotlist_tree);
ro_gui_tree_update_theme(global_history_tree);
ro_gui_tree_update_theme(cookies_tree);
ro_gui_theme_close(theme_previous, false);
return true;
}
/**
* Closes a theme after use.
*
* \param descriptor the theme_descriptor to close
* \param list whether to open all themes in the list
* \return whether the operation was successful
*/
void ro_gui_theme_close(struct theme_descriptor *descriptor, bool list)
{
if (!descriptor)
return;
/* move to the start of the list */
while (list && descriptor->previous)
descriptor = descriptor->previous;
/* close the themes */
while (descriptor) {
if (descriptor->theme) {
descriptor->theme->users = descriptor->theme->users - 1;
if (descriptor->theme->users <= 0) {
free(descriptor->theme->sprite_area);
free(descriptor->theme);
descriptor->theme = NULL;
}
}
if (!list)
return;
descriptor = descriptor->next;
}
}
/**
* Performs the redraw for a toolbar
*
* \param redraw the redraw area
* \param toolbar the toolbar to redraw
*/
void ro_gui_theme_redraw(wimp_draw *redraw)
{
struct toolbar *toolbar;
struct gui_window *g;
struct toolbar_icon *icon;
osbool more;
wimp_icon separator_icon;
os_error *error;
bool perform_redraw = false;
toolbar = (struct toolbar *)ro_gui_wimp_event_get_user_data(redraw->w);
assert(toolbar);
/* set the content-type icon */
g = ro_gui_toolbar_lookup(toolbar->toolbar_handle);
/* only set type for browser windows */
sprintf(theme_favicon_sprite, "Ssmall_xxx");
if (g) {
assert(toolbar->type == THEME_BROWSER_TOOLBAR);
assert(g->bw);
if (g->bw->current_content) {
sprintf(theme_favicon_sprite, "Ssmall_%.3x",
ro_content_filetype_from_type(
content_get_type(g->bw->current_content)));
if (!ro_gui_wimp_sprite_exists(theme_favicon_sprite + 1))
sprintf(theme_favicon_sprite, "Ssmall_xxx");
}
}
/* set up the icon */
if ((toolbar->descriptor) && (toolbar->descriptor->theme) &&
(toolbar->descriptor->theme->sprite_area)) {
const char *name = theme_separator_name;
separator_icon.flags = wimp_ICON_SPRITE | wimp_ICON_INDIRECTED |
wimp_ICON_HCENTRED | wimp_ICON_VCENTRED;
separator_icon.data.indirected_sprite.id = (osspriteop_id) name;
separator_icon.data.indirected_sprite.area =
toolbar->descriptor->theme->sprite_area;
separator_icon.data.indirected_sprite.size = 12;
separator_icon.extent.y0 = 0;
separator_icon.extent.y1 = toolbar->height;
perform_redraw = true;
}
perform_redraw &= toolbar->display_buttons || toolbar->editor;
if ((toolbar->editor) && (toolbar->editor->toolbar_handle == redraw->w))
toolbar = toolbar->editor;
error = xwimp_redraw_window(redraw, &more);
if (error) {
LOG(("xwimp_redraw_window: 0x%x: %s",
error->errnum, error->errmess));
warn_user("WimpError", error->errmess);
return;
}
while (more) {
if (perform_redraw)
for (icon = toolbar->icon; icon; icon = icon->next)
if ((icon->icon_number == -1) &&
(icon->display)) {
separator_icon.extent.x0 = icon->x;
separator_icon.extent.x1 = icon->x +
icon->width;
xwimp_plot_icon(&separator_icon);
}
error = xwimp_get_rectangle(redraw, &more);
if (error) {
LOG(("xwimp_get_rectangle: 0x%x: %s",
error->errnum, error->errmess));
warn_user("WimpError", error->errmess);
return;
}
}
}
/**
* Frees any unused theme descriptors.
*
* \param descriptor the theme_descriptor to free
* \param list whether to open all themes in the list
* \return whether the operation was successful
*/
void ro_gui_theme_free(struct theme_descriptor *descriptor)
{
struct theme_descriptor *next_descriptor;
if (!descriptor)
return;
/* move to the start of the list */
while (descriptor->previous)
descriptor = descriptor->previous;
/* free closed themes */
for (; descriptor; descriptor = next_descriptor) {
next_descriptor = descriptor->next;
/* no theme? no descriptor */
if (!descriptor->theme) {
if (descriptor->previous)
descriptor->previous->next = descriptor->next;
if (descriptor->next)
descriptor->next->previous =
descriptor->previous;
/* keep the cached list in sync */
if (theme_descriptors == descriptor)
theme_descriptors = next_descriptor;
/* release any memory */
free(descriptor->filename);
free(descriptor);
}
}
}
/**
* Creates a toolbar.
*
* \param descriptor the theme to use, or NULL for current
* \param type the toolbar type
* \return a new toolbar, or NULL for failure
*/
struct toolbar *ro_gui_theme_create_toolbar(struct theme_descriptor *descriptor,
toolbar_type type)
{
struct toolbar *toolbar;
/* Create a new toolbar
*/
toolbar = calloc(sizeof(struct toolbar), 1);
if (!toolbar) {
LOG(("No memory for malloc()"));
warn_user("NoMemory", 0);
return NULL;
}
toolbar->type = type;
/* Store the theme
*/
if (!descriptor) descriptor = theme_current;
toolbar->descriptor = descriptor;
/* Apply the default settings
*/
toolbar->display_buttons = true;
toolbar->toolbar_current = 16384;
switch (type) {
case THEME_BROWSER_TOOLBAR:
toolbar->display_url = true;
toolbar->display_throbber = true;
ro_gui_theme_add_toolbar_icons(toolbar,
theme_browser_icons,
option_toolbar_browser);
toolbar->suggest = ro_gui_theme_add_toolbar_icon(NULL,
"gright",
ICON_TOOLBAR_SUGGEST);
break;
case THEME_HOTLIST_TOOLBAR:
ro_gui_theme_add_toolbar_icons(toolbar,
theme_hotlist_icons,
option_toolbar_hotlist);
break;
case THEME_HISTORY_TOOLBAR:
ro_gui_theme_add_toolbar_icons(toolbar,
theme_history_icons,
option_toolbar_history);
break;
case THEME_COOKIES_TOOLBAR:
ro_gui_theme_add_toolbar_icons(toolbar,
theme_cookies_icons,
option_toolbar_cookies);
break;
case THEME_BROWSER_EDIT_TOOLBAR:
ro_gui_theme_add_toolbar_icons(toolbar,
theme_browser_icons,
"0123456789ab|");
break;
case THEME_HOTLIST_EDIT_TOOLBAR:
ro_gui_theme_add_toolbar_icons(toolbar,
theme_hotlist_icons,
"40123|");
break;
case THEME_HISTORY_EDIT_TOOLBAR:
ro_gui_theme_add_toolbar_icons(toolbar,
theme_history_icons,
"0123|");
break;
case THEME_COOKIES_EDIT_TOOLBAR:
ro_gui_theme_add_toolbar_icons(toolbar,
theme_cookies_icons,
"012|");
break;
}
/* Claim the memory for our Wimp indirection
*/
if (type == THEME_BROWSER_TOOLBAR) {
toolbar->url_buffer = calloc(1, THEME_URL_MEMORY +
THEME_THROBBER_MEMORY);
if (!toolbar->url_buffer) {
LOG(("No memory for calloc()"));
ro_gui_theme_destroy_toolbar(toolbar);
return NULL;
}
toolbar->throbber_buffer = toolbar->url_buffer +
THEME_URL_MEMORY;
sprintf(toolbar->throbber_buffer, "throbber0");
}
/* Apply the desired theme to the toolbar
*/
if (!ro_gui_theme_update_toolbar(descriptor, toolbar)) {
ro_gui_theme_destroy_toolbar(toolbar);
return NULL;
}
toolbar->old_height = ro_gui_theme_toolbar_full_height(toolbar);
return toolbar;
}
/**
* Updates a toolbar to use a particular theme.
* The toolbar may be unstable on failure and should be destroyed.
*
* \param descriptor the theme to use, or NULL for current
* \param toolbar the toolbar to update
* \return whether the operation was successful
*/
bool ro_gui_theme_update_toolbar(struct theme_descriptor *descriptor,
struct toolbar *toolbar)
{
wimp_icon_create new_icon;
os_error *error;
osspriteop_area *sprite_area;
struct toolbar_icon *toolbar_icon;
int width, max_icon;
wimp_icon_flags icon_flags;
struct gui_window *g;
if (!toolbar) return false;
/* Set the theme and window sprite area
*/
if (!descriptor) descriptor = theme_current;
toolbar->descriptor = descriptor;
if ((toolbar->descriptor) && (toolbar->descriptor->theme))
sprite_area = toolbar->descriptor->theme->sprite_area;
else
sprite_area = (osspriteop_area *)1;
theme_toolbar_window.sprite_area = sprite_area;
/* Update the icon sizes
*/
for (toolbar_icon = toolbar->icon; toolbar_icon;
toolbar_icon = toolbar_icon->next)
ro_gui_theme_update_toolbar_icon(toolbar, toolbar_icon);
if (toolbar->suggest)
ro_gui_theme_update_toolbar_icon(toolbar, toolbar->suggest);
/* Recreate the toolbar window
*/
if (toolbar->descriptor) {
if (toolbar->type == THEME_BROWSER_TOOLBAR)
theme_toolbar_window.work_bg =
toolbar->descriptor->browser_background;
else
theme_toolbar_window.work_bg =
toolbar->descriptor->hotlist_background;
} else {
theme_toolbar_window.work_bg = wimp_COLOUR_VERY_LIGHT_GREY;
}
theme_toolbar_window.work_flags &= ~wimp_ICON_BUTTON_TYPE;
if ((toolbar->editor) ||
(toolbar->type == THEME_HOTLIST_EDIT_TOOLBAR) ||
(toolbar->type == THEME_HISTORY_EDIT_TOOLBAR) ||
(toolbar->type == THEME_BROWSER_EDIT_TOOLBAR) ||
(toolbar->type == THEME_COOKIES_EDIT_TOOLBAR))
theme_toolbar_window.work_flags |= (wimp_BUTTON_CLICK_DRAG <<
wimp_ICON_BUTTON_TYPE_SHIFT);
theme_toolbar_window.sprite_area = sprite_area;
if (toolbar->toolbar_handle) {
error = xwimp_delete_window(toolbar->toolbar_handle);
if (error)
LOG(("xwimp_delete_window: 0x%x: %s",
error->errnum, error->errmess));
ro_gui_wimp_event_finalise(toolbar->toolbar_handle);
toolbar->toolbar_handle = NULL;
}
error = xwimp_create_window(&theme_toolbar_window,
&toolbar->toolbar_handle);
if (error) {
LOG(("xwimp_create_window: 0x%x: %s",
error->errnum, error->errmess));
warn_user("WimpError", error->errmess);
return false;
}
ro_gui_wimp_event_register_redraw_window(toolbar->toolbar_handle,
ro_gui_theme_redraw);
ro_gui_wimp_event_set_user_data(toolbar->toolbar_handle, toolbar);
switch (toolbar->type) {
case THEME_BROWSER_TOOLBAR:
case THEME_BROWSER_EDIT_TOOLBAR:
ro_gui_wimp_event_register_mouse_click(toolbar->toolbar_handle,
ro_gui_toolbar_click);
break;
case THEME_HOTLIST_TOOLBAR:
case THEME_HOTLIST_EDIT_TOOLBAR:
case THEME_HISTORY_TOOLBAR:
case THEME_HISTORY_EDIT_TOOLBAR:
case THEME_COOKIES_TOOLBAR:
case THEME_COOKIES_EDIT_TOOLBAR:
ro_gui_wimp_event_register_mouse_click(toolbar->toolbar_handle,
ro_gui_tree_toolbar_click);
break;
}
/* Create the basic icons
*/
if ((toolbar->type == THEME_HOTLIST_TOOLBAR) ||
(toolbar->type == THEME_HOTLIST_EDIT_TOOLBAR))
max_icon = ICON_TOOLBAR_HOTLIST_LAST;
else if ((toolbar->type == THEME_HISTORY_TOOLBAR) ||
(toolbar->type == THEME_HISTORY_EDIT_TOOLBAR))
max_icon = ICON_TOOLBAR_HISTORY_LAST;
else if ((toolbar->type == THEME_COOKIES_TOOLBAR) ||
(toolbar->type == THEME_COOKIES_EDIT_TOOLBAR))
max_icon = ICON_TOOLBAR_COOKIES_LAST;
else
max_icon = ICON_TOOLBAR_LAST;
new_icon.w = toolbar->toolbar_handle;
new_icon.icon.data.indirected_text.size = 1;
new_icon.icon.flags = wimp_ICON_TEXT | wimp_ICON_SPRITE |
wimp_ICON_INDIRECTED | wimp_ICON_HCENTRED |
wimp_ICON_VCENTRED;
if ((toolbar->editor) ||
(toolbar->type == THEME_HOTLIST_EDIT_TOOLBAR) ||
(toolbar->type == THEME_HISTORY_EDIT_TOOLBAR) ||
(toolbar->type == THEME_COOKIES_EDIT_TOOLBAR) ||
(toolbar->type == THEME_BROWSER_EDIT_TOOLBAR))
new_icon.icon.flags |= (wimp_BUTTON_CLICK_DRAG <<
wimp_ICON_BUTTON_TYPE_SHIFT);
else
new_icon.icon.flags |= (wimp_BUTTON_CLICK <<
wimp_ICON_BUTTON_TYPE_SHIFT);
if (toolbar->descriptor)
new_icon.icon.flags |= (toolbar->descriptor->browser_background
<< wimp_ICON_BG_COLOUR_SHIFT);
else
new_icon.icon.flags |= (wimp_COLOUR_VERY_LIGHT_GREY
<< wimp_ICON_BG_COLOUR_SHIFT);
icon_flags = new_icon.icon.flags;
for (int i = 0; i < max_icon; i++) {
new_icon.icon.data.indirected_text.text =
theme_null_text_string;
new_icon.icon.data.indirected_text.validation =
theme_null_text_string;
toolbar_icon = toolbar->icon;
while (toolbar_icon) {
if (toolbar_icon->icon_number == i) {
new_icon.icon.data.indirected_text.validation =
toolbar_icon->validation;
break;
} else {
toolbar_icon = toolbar_icon->next;
}
}
error = xwimp_create_icon(&new_icon, 0);
if (error) {
LOG(("xwimp_create_icon: 0x%x: %s",
error->errnum, error->errmess));
warn_user("WimpError", error->errmess);
return false;
}
}
/* Create the URL/throbber icons
*/
if (toolbar->type == THEME_BROWSER_TOOLBAR) {
/* container for all URL bits (ie border) */
new_icon.icon.flags = wimp_ICON_BORDER | (wimp_COLOUR_BLACK <<
wimp_ICON_FG_COLOUR_SHIFT);
error = xwimp_create_icon(&new_icon, 0);
if (error) {
LOG(("xwimp_create_icon: 0x%x: %s",
error->errnum, error->errmess));
warn_user("WimpError", error->errmess);
return false;
}
/* favicon image */
new_icon.icon.flags = wimp_ICON_TEXT | wimp_ICON_SPRITE |
wimp_ICON_INDIRECTED | wimp_ICON_FILLED |
wimp_ICON_HCENTRED | wimp_ICON_VCENTRED |
(wimp_BUTTON_CLICK_DRAG <<
wimp_ICON_BUTTON_TYPE_SHIFT);
new_icon.icon.data.indirected_text.text = theme_null_text_string;
new_icon.icon.data.indirected_text.validation =
theme_favicon_sprite;
new_icon.icon.data.indirected_text.size = 1;
error = xwimp_create_icon(&new_icon, 0);
if (error) {
LOG(("xwimp_create_icon: 0x%x: %s",
error->errnum, error->errmess));
warn_user("WimpError", error->errmess);
return false;
}
/* Writable text portion */
new_icon.icon.flags = wimp_ICON_TEXT | wimp_ICON_INDIRECTED |
wimp_ICON_VCENTRED |
wimp_ICON_FILLED | (wimp_COLOUR_BLACK <<
wimp_ICON_FG_COLOUR_SHIFT) |
(wimp_BUTTON_WRITE_CLICK_DRAG <<
wimp_ICON_BUTTON_TYPE_SHIFT);
new_icon.icon.data.indirected_text.text = toolbar->url_buffer;
new_icon.icon.data.indirected_text.validation =
theme_url_validation;
new_icon.icon.data.indirected_text.size = THEME_URL_MEMORY;
error = xwimp_create_icon(&new_icon, 0);
if (error) {
LOG(("xwimp_create_icon: 0x%x: %s",
error->errnum, error->errmess));
warn_user("WimpError", error->errmess);
return false;
}
/* Now the URL suggestion icon
*/
new_icon.icon.data.indirected_text.text =
theme_null_text_string;
new_icon.icon.data.indirected_text.size = 1;
new_icon.icon.flags = icon_flags | (wimp_BUTTON_CLICK <<
wimp_ICON_BUTTON_TYPE_SHIFT);
if (toolbar->suggest)
new_icon.icon.data.indirected_text.validation =
toolbar->suggest->validation;
else
new_icon.icon.data.indirected_text.validation =
theme_null_text_string;
error = xwimp_create_icon(&new_icon, 0);
if (error) {
LOG(("xwimp_create_icon: 0x%x: %s",
error->errnum, error->errmess));
warn_user("WimpError", error->errmess);
return false;
}
/* Now the throbber
*/
new_icon.icon.flags = wimp_ICON_SPRITE | wimp_ICON_INDIRECTED |
wimp_ICON_HCENTRED | wimp_ICON_VCENTRED;
new_icon.icon.data.indirected_sprite.id =
(osspriteop_id)toolbar->throbber_buffer;
new_icon.icon.data.indirected_sprite.area = sprite_area;
new_icon.icon.data.indirected_sprite.size =
THEME_THROBBER_MEMORY;
error = xwimp_create_icon(&new_icon, 0);
if (error) {
LOG(("xwimp_create_icon: 0x%x: %s",
error->errnum, error->errmess));
warn_user("WimpError", error->errmess);
return false;
}
}
if (toolbar->parent_handle)
ro_gui_theme_attach_toolbar(toolbar, toolbar->parent_handle);
/* Force a re-processing of the toolbar
*/
width = toolbar->toolbar_current;
toolbar->reformat_buttons = true;
toolbar->toolbar_current = -1;
ro_gui_theme_process_toolbar(toolbar, width);
/* Keep menus up to date etc
*/
ro_gui_theme_set_help_prefix(toolbar);
switch (toolbar->type) {
case THEME_BROWSER_TOOLBAR:
g = ro_gui_window_lookup(toolbar->parent_handle);
if (g)
ro_gui_prepare_navigate(g);
break;
case THEME_HOTLIST_TOOLBAR:
case THEME_HISTORY_TOOLBAR:
case THEME_COOKIES_TOOLBAR:
ro_gui_menu_prepare_action(toolbar->parent_handle,
TREE_SELECTION, false);
ro_gui_menu_prepare_action(toolbar->parent_handle,
TREE_EXPAND_ALL, false);
break;
default:
break;
}
return true;
}
/**
* Attaches a toolbar to a window.
*
* \param toolbar the toolbar to update
* \param parent the window to contain the toolbar
* \return whether the operation was successful
*/
bool ro_gui_theme_attach_toolbar(struct toolbar *toolbar, wimp_w parent)
{
wimp_outline outline;
wimp_window_state state;
int height;
os_error *error;
if (!toolbar)
return false;
/* Attach/close the windows
*/
toolbar->parent_handle = parent;
height = ro_gui_theme_toolbar_height(toolbar);
if (height > 0) {
outline.w = parent;
xwimp_get_window_outline(&outline);
state.w = parent;
xwimp_get_window_state(&state);
state.w = toolbar->toolbar_handle;
state.visible.x1 = outline.outline.x1 - 2;
state.visible.y0 = state.visible.y1 - height + 2;
state.xscroll = 0;
state.yscroll = toolbar->height - 2; /* clipped by the WIMP */
error = xwimp_open_window_nested(PTR_WIMP_OPEN(&state), parent,
wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
<< wimp_CHILD_XORIGIN_SHIFT |
wimp_CHILD_LINKS_PARENT_VISIBLE_TOP_OR_RIGHT
<< wimp_CHILD_YORIGIN_SHIFT |
wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
<< wimp_CHILD_LS_EDGE_SHIFT |
wimp_CHILD_LINKS_PARENT_VISIBLE_TOP_OR_RIGHT
<< wimp_CHILD_BS_EDGE_SHIFT |
wimp_CHILD_LINKS_PARENT_VISIBLE_TOP_OR_RIGHT
<< wimp_CHILD_RS_EDGE_SHIFT |
wimp_CHILD_LINKS_PARENT_VISIBLE_TOP_OR_RIGHT
<< wimp_CHILD_TS_EDGE_SHIFT);
if (error) {
LOG(("xwimp_open_window_nested: 0x%x: %s",
error->errnum, error->errmess));
warn_user("WimpError", error->errmess);
return false;
}
if (!toolbar->editor)
return true;
state.w = toolbar->editor->toolbar_handle;
state.visible.y1 -= toolbar->height;
state.yscroll = toolbar->editor->height - 2;
error = xwimp_open_window_nested(PTR_WIMP_OPEN(&state),
toolbar->toolbar_handle,
wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
<< wimp_CHILD_XORIGIN_SHIFT |
wimp_CHILD_LINKS_PARENT_VISIBLE_TOP_OR_RIGHT
<< wimp_CHILD_YORIGIN_SHIFT |
wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
<< wimp_CHILD_LS_EDGE_SHIFT |
wimp_CHILD_LINKS_PARENT_VISIBLE_TOP_OR_RIGHT
<< wimp_CHILD_BS_EDGE_SHIFT |
wimp_CHILD_LINKS_PARENT_VISIBLE_TOP_OR_RIGHT
<< wimp_CHILD_RS_EDGE_SHIFT |
wimp_CHILD_LINKS_PARENT_VISIBLE_TOP_OR_RIGHT
<< wimp_CHILD_TS_EDGE_SHIFT);
if (error) {
LOG(("xwimp_open_window_nested: 0x%x: %s",
error->errnum, error->errmess));
warn_user("WimpError", error->errmess);
return false;
}
return true;
}
error = xwimp_close_window(toolbar->toolbar_handle);
if (error) {
LOG(("xwimp_close_window: 0x%x: %s",
error->errnum, error->errmess));
warn_user("WimpError", error->errmess);
return false;
}
return true;
}
/**
* Updates the toolbar to reflect changes to the icon flags and any reformatting
* required due to the change in parent window size.
*
* \param toolbar the toolbar to update
* \param width a specific width to resize to, or -1 to use parent width
* \return whether the operation was successful
*/
bool ro_gui_theme_process_toolbar(struct toolbar *toolbar, int width)
{
wimp_caret caret;
os_box extent = { 0, 0, 0, 0 };
os_error *error;
wimp_outline outline;
wimp_window_state state;
int height = -1;
int throbber_x = -1;
int left_edge, right_edge, bottom_edge;
int old_height;
int old_width;
struct toolbar_icon *toolbar_icon;
bool visible_icon = false;
int collapse_height;
int xeig, yeig;
os_coord pixel = {1, 1};
int top, bottom, right;
if (!toolbar)
return false;
old_height = toolbar->height;
old_width = toolbar->toolbar_current;
/* calculate 1px in OS units */
ro_convert_pixels_to_os_units(&pixel, (os_mode)-1);
xeig = pixel.x;
yeig = pixel.y;
/* find the parent window handle if we need to process the status
* window, or the caller has requested we calculate the width ourself */
if ((toolbar->parent_handle) && (width == -1)) {
outline.w = toolbar->parent_handle;
error = xwimp_get_window_outline(&outline);
if (error) {
LOG(("xwimp_get_window_outline: 0x%x: %s",
error->errnum, error->errmess));
warn_user("WimpError", error->errmess);
return false;
}
if (width == -1)
width = outline.outline.x1 - outline.outline.x0 - 2;
}
/* Find the parent visible height to clip our toolbar height to
*/
if ((toolbar->toolbar_handle) && (toolbar->parent_handle)) {
/* Get the current state
*/
state.w = toolbar->parent_handle;
error = xwimp_get_window_state(&state);
if (error) {
LOG(("xwimp_get_window_state: 0x%x: %s",
error->errnum, error->errmess));
warn_user("WimpError", error->errmess);
return false;
}
height = state.visible.y1 - state.visible.y0 + 2;
/* We can't obscure the height of the scroll bar as we
lose the resize icon if we do.
*/
if ((state.flags & wimp_WINDOW_SIZE_ICON) &&
!(state.flags & wimp_WINDOW_HSCROLL))
height -= ro_get_hscroll_height(0) - 2;
/* Update our position
*/
if (height != toolbar->max_height) {
if ((state.flags & wimp_WINDOW_SIZE_ICON) &&
!(state.flags & wimp_WINDOW_HSCROLL) &&
(toolbar->height > toolbar->max_height))
xwimp_force_redraw(toolbar->parent_handle,
0, -16384, 16384, 16384);
toolbar->max_height = height;
collapse_height = toolbar->height +
(toolbar->editor ? toolbar->editor->height : 0);
ro_gui_theme_attach_toolbar(toolbar, toolbar->parent_handle);
if ((state.flags & wimp_WINDOW_SIZE_ICON) &&
!(state.flags & wimp_WINDOW_HSCROLL) &&
(collapse_height > toolbar->max_height))
xwimp_force_redraw(toolbar->parent_handle,
0, -16384, 16384, 16384);
}
}
/* Reformat the buttons starting with the throbber
*/
if ((width != old_width) || (toolbar->reformat_buttons)) {
left_edge = 6;
right_edge = width - 8;
toolbar->height = 0;
if ((toolbar->descriptor) && (toolbar->descriptor->theme) &&
(toolbar->type == THEME_BROWSER_TOOLBAR) &&
(toolbar->display_throbber)) {
if (!toolbar->descriptor->throbber_right) {
throbber_x = left_edge;
left_edge += toolbar->descriptor->theme->throbber_width + 8;
}
toolbar->height = toolbar->descriptor->theme->throbber_height + 8;
}
if ((toolbar->type == THEME_BROWSER_TOOLBAR) && (toolbar->display_url)) {
if (toolbar->height < 52 + 8)
toolbar->height = 52 + 8;
if ((toolbar->suggest) && (toolbar->height <
(toolbar->suggest->height + 8)))
toolbar->height = toolbar->suggest->height + 8;
}
/* Get the minimum height of the icons
*/
bottom_edge = left_edge;
if ((toolbar->display_buttons || toolbar->editor) && (toolbar->descriptor) &&
(toolbar->descriptor->theme)) {
toolbar_icon = toolbar->icon;
while (toolbar_icon) {
if (toolbar_icon->display) {
bottom_edge += toolbar_icon->width;
visible_icon = true;
if ((toolbar_icon->height != 0) &&
(toolbar->height < toolbar_icon->height + 8)) {
toolbar->height = toolbar_icon->height + 8;
}
}
toolbar_icon = toolbar_icon->next;
}
if (visible_icon) bottom_edge += 8;
}
/* Check for minimum widths
*/
if (toolbar->type == THEME_BROWSER_TOOLBAR) {
if (!toolbar->reformat_buttons) left_edge = bottom_edge;
if (toolbar->display_url) {
bottom_edge += 112;
if (toolbar->suggest)
bottom_edge += toolbar->suggest->width + 8;
}
if (bottom_edge > right_edge)
right_edge = bottom_edge;
if ((toolbar->descriptor) && (toolbar->descriptor->theme) &&
(toolbar->display_throbber) &&
(toolbar->descriptor->throbber_right)) {
bottom_edge += toolbar->descriptor->theme->throbber_width;
if (bottom_edge > right_edge) right_edge = bottom_edge;
throbber_x = right_edge -
toolbar->descriptor->theme->throbber_width;
right_edge -= toolbar->descriptor->theme->throbber_width + 8;
}
}
if (toolbar->height != 0)
toolbar->height += 2;
if (toolbar->reformat_buttons) {
/* Hide the URL bar if we should
*/
if ((!toolbar->display_url) &&
(toolbar->type == THEME_BROWSER_TOOLBAR)) {
if (!xwimp_get_caret_position(&caret)) {
if ((caret.w == toolbar->toolbar_handle) &&
(caret.i == ICON_TOOLBAR_URL)) {
if (toolbar->parent_handle)
xwimp_set_caret_position(
toolbar->parent_handle,
wimp_ICON_WINDOW,
-100, -100, 32, -1);
else
xwimp_set_caret_position((wimp_w)-1,
0, 0, 0, 0, 0);
}
}
xwimp_resize_icon(toolbar->toolbar_handle, ICON_TOOLBAR_SURROUND,
0, -16384, 0, -16384);
xwimp_resize_icon(toolbar->toolbar_handle, ICON_TOOLBAR_FAVICON,
0, -16384, 0, -16384);
xwimp_resize_icon(toolbar->toolbar_handle, ICON_TOOLBAR_URL,
0, -16384, 0, -16384);
xwimp_resize_icon(toolbar->toolbar_handle, ICON_TOOLBAR_SUGGEST,
0, -16384, 0, -16384);
} else if (toolbar->type == THEME_BROWSER_TOOLBAR) {
ro_gui_set_icon_shaded_state(toolbar->toolbar_handle,
ICON_TOOLBAR_URL, !toolbar->display_url);
}
xwimp_force_redraw(toolbar->toolbar_handle,
0, 0, 16384, 16384);
/* Move the buttons
*/
toolbar_icon = toolbar->icon;
while (toolbar_icon) {
if ((toolbar->display_buttons || toolbar->editor) &&
(toolbar_icon->display)
&& (toolbar_icon->width > 0)) {
visible_icon = true;
bottom_edge = (toolbar->height -
toolbar_icon->height) / 2;
toolbar_icon->x = left_edge;
toolbar_icon->y = bottom_edge;
xwimp_resize_icon(toolbar->toolbar_handle,
toolbar_icon->icon_number,
left_edge, bottom_edge,
left_edge + toolbar_icon->width,
bottom_edge + toolbar_icon->height);
left_edge += toolbar_icon->width;
} else {
xwimp_resize_icon(toolbar->toolbar_handle,
toolbar_icon->icon_number,
0, -16384, 0, -16384);
}
toolbar_icon = toolbar_icon->next;
}
if (visible_icon) left_edge += 8;
}
if (toolbar->type == THEME_BROWSER_TOOLBAR) {
/* Move the URL bar
*/
if (toolbar->display_url) {
top = (toolbar->height / 2) + 26;
bottom = (toolbar->height / 2) - 26;
if (toolbar->suggest) {
right = right_edge - toolbar->suggest->width - 8;
xwimp_resize_icon(toolbar->toolbar_handle,
ICON_TOOLBAR_SUGGEST,
right_edge - toolbar->suggest->width,
(toolbar->height - toolbar->suggest->height) / 2,
right_edge,
(toolbar->height + toolbar->suggest->height) / 2);
} else {
right = right_edge;
}
xwimp_resize_icon(toolbar->toolbar_handle, ICON_TOOLBAR_URL,
left_edge + 52,
bottom + yeig,
right - xeig,
top - yeig);
xwimp_resize_icon(toolbar->toolbar_handle, ICON_TOOLBAR_FAVICON,
left_edge + xeig,
bottom + yeig,
left_edge + 52,
top - yeig);
xwimp_resize_icon(toolbar->toolbar_handle, ICON_TOOLBAR_SURROUND,
left_edge,
bottom,
right,
top);
xwimp_force_redraw(toolbar->toolbar_handle,
right - xeig, 0, 16384, 16384);
xwimp_force_redraw(toolbar->toolbar_handle,
left_edge,
bottom,
right,
bottom + yeig);
xwimp_force_redraw(toolbar->toolbar_handle,
left_edge,
top - yeig,
right,
top);
if (!xwimp_get_caret_position(&caret)) {
if ((caret.w == toolbar->toolbar_handle) &&
(caret.i == ICON_TOOLBAR_URL)) {
xwimp_set_caret_position(toolbar->toolbar_handle,
ICON_TOOLBAR_URL,
caret.pos.x, caret.pos.y,
-1, caret.index);
}
}
ro_gui_redraw_icon(toolbar->toolbar_handle, ICON_TOOLBAR_URL);
}
/* Move the throbber
*/
if ((toolbar->descriptor) && (toolbar->descriptor->theme) &&
(throbber_x >= 0) && (toolbar->display_throbber)) {
xwimp_resize_icon(toolbar->toolbar_handle, ICON_TOOLBAR_THROBBER,
throbber_x, 0,
throbber_x + toolbar->descriptor->theme->throbber_width,
toolbar->height);
if (toolbar->descriptor->throbber_right) {
xwimp_force_redraw(toolbar->toolbar_handle,
old_width - width + throbber_x, 0, 16384, 16384);
xwimp_force_redraw(toolbar->toolbar_handle,
throbber_x, 0, 16384, 16384);
}
} else {
xwimp_resize_icon(toolbar->toolbar_handle, ICON_TOOLBAR_THROBBER,
0, -16384, 0, -16384);
}
}
/* Re-attach to the parent
*/
toolbar->toolbar_current = width;
if (toolbar->reformat_buttons) {
extent.x1 = 16384;
extent.y0 = (toolbar->editor ? -toolbar->editor->height : 0);
extent.y1 = toolbar->height - 2;
xwimp_set_extent(toolbar->toolbar_handle, &extent);
if ((toolbar->parent_handle) && (old_height != toolbar->height))
ro_gui_theme_attach_toolbar(toolbar, toolbar->parent_handle);
}
toolbar->reformat_buttons = false;
}
return true;
}
/**
* Destroys a toolbar and frees any associated memory.
*
* \param toolbar the toolbar to destroy
*/
void ro_gui_theme_destroy_toolbar(struct toolbar *toolbar)
{
struct toolbar_icon *icon;
struct toolbar_icon *next_icon;
if (!toolbar) return;
/* Destroy our editor
*/
if (toolbar->editor) {
toolbar->editor = NULL;
ro_gui_theme_destroy_toolbar(toolbar->editor);
}
/* Delete our windows
*/
if (toolbar->toolbar_handle) {
xwimp_delete_window(toolbar->toolbar_handle);
ro_gui_wimp_event_finalise(toolbar->toolbar_handle);
}
/* Free the Wimp buffer (we only created one for them all)
*/
free(toolbar->url_buffer);
/* Free all the icons
*/
next_icon = toolbar->icon;
while ((icon = next_icon) != NULL) {
next_icon = icon->next;
ro_gui_theme_destroy_toolbar_icon(icon);
}
ro_gui_theme_destroy_toolbar_icon(toolbar->suggest);
free(toolbar);
}
/**
* Toggles the toolbar editing mode
*
* \param toolbar the toolbar to toggle editing for
*/
void ro_gui_theme_toggle_edit(struct toolbar *toolbar)
{
int icons = 0;
struct toolbar_icon *icon;
struct gui_window *g = NULL;
wimp_window_state state;
os_error *error;
char *option;
char hex_no[4];
if (!toolbar)
return;
if ((toolbar->type == THEME_BROWSER_TOOLBAR) &&
(toolbar->parent_handle))
g = ro_gui_window_lookup(toolbar->parent_handle);
if (toolbar->editor) {
/* save options */
icons = 0;
for (icon = toolbar->icon; icon; icon = icon->next)
if (icon->display) icons++;
option = calloc(icons + 1, 1);
if (!option) {
LOG(("No memory to save toolbar options"));
warn_user("NoMemory", 0);
} else {
icons = 0;
for (icon = toolbar->icon; icon; icon = icon->next)
if (icon->display) {
if (icon->icon_number == -1) {
option[icons] = '|';
} else {
sprintf(hex_no, "%x", icon->icon_number);
option[icons] = hex_no[0];
}
icons++;
}
switch (toolbar->type) {
case THEME_BROWSER_TOOLBAR:
free(option_toolbar_browser);
option_toolbar_browser = option;
break;
case THEME_HOTLIST_TOOLBAR:
free(option_toolbar_hotlist);
option_toolbar_hotlist = option;
break;
case THEME_HISTORY_TOOLBAR:
free(option_toolbar_history);
option_toolbar_history = option;
break;
case THEME_COOKIES_TOOLBAR:
free(option_toolbar_cookies);
option_toolbar_cookies = option;
break;
default:
break;
}
ro_gui_save_options();
}
/* turn off editing */
ro_gui_theme_destroy_toolbar(toolbar->editor);
toolbar->editor = NULL;
ro_gui_theme_update_toolbar(toolbar->descriptor, toolbar);
switch (toolbar->type) {
case THEME_BROWSER_TOOLBAR:
if (g)
gui_window_update_extent(g);
break;
default:
if (toolbar->parent_handle)
xwimp_force_redraw(toolbar->parent_handle,
0, -16384, 16384, 16384);
break;
}
} else {
/* create/initialise the toolbar editor */
switch (toolbar->type) {
case THEME_BROWSER_TOOLBAR:
toolbar->editor = ro_gui_theme_create_toolbar(
toolbar->descriptor,
THEME_BROWSER_EDIT_TOOLBAR);
break;
case THEME_HOTLIST_TOOLBAR:
toolbar->editor = ro_gui_theme_create_toolbar(
toolbar->descriptor,
THEME_HOTLIST_EDIT_TOOLBAR);
break;
case THEME_HISTORY_TOOLBAR:
toolbar->editor = ro_gui_theme_create_toolbar(
toolbar->descriptor,
THEME_HISTORY_EDIT_TOOLBAR);
break;
case THEME_COOKIES_TOOLBAR:
toolbar->editor = ro_gui_theme_create_toolbar(
toolbar->descriptor,
THEME_COOKIES_EDIT_TOOLBAR);
break;
default:
return;
}
if (!toolbar->editor) {
LOG(("Unable to create toolbar editor"));
return;
}
ro_gui_wimp_event_set_user_data(toolbar->editor->toolbar_handle,
ro_gui_wimp_event_get_user_data(toolbar->toolbar_handle));
ro_gui_theme_update_toolbar(toolbar->descriptor, toolbar);
switch (toolbar->type) {
case THEME_BROWSER_TOOLBAR:
if (g)
gui_window_update_extent(g);
break;
default:
if (toolbar->parent_handle) {
state.w = toolbar->parent_handle;
error = xwimp_get_window_state(&state);
if (error) {
LOG(("xwimp_get_window_state: 0x%x: %s",
error->errnum, error->errmess));
warn_user("WimpError", error->errmess);
return;
}
ro_gui_open_window_request(PTR_WIMP_OPEN(&state));
xwimp_force_redraw(toolbar->parent_handle,
0, -16384, 16384, 16384);
}
break;
}
ro_gui_theme_process_toolbar(toolbar, -1);
ro_gui_theme_toolbar_editor_sync(toolbar);
}
ro_gui_theme_set_help_prefix(toolbar);
}
/**
* Synchronise a toolbar window with the associated editor.
*
* \param toolbar the toolbar to synchronise
*/
void ro_gui_theme_toolbar_editor_sync(struct toolbar *toolbar)
{
struct toolbar_icon *icon;
struct toolbar_icon *icon_edit;
if ((!toolbar) || (!toolbar->editor))
return;
for (icon = toolbar->icon; icon; icon = icon->next)
if ((icon->icon_number >= 0) && (icon->width > 0))
for (icon_edit = toolbar->editor->icon; icon_edit;
icon_edit = icon_edit->next)
if (icon_edit->icon_number == icon->icon_number)
ro_gui_set_icon_shaded_state(
toolbar->editor->toolbar_handle,
icon_edit->icon_number,
icon->display);
}
/**
* Handle a toolbar click during an editor session
*
* \param toolbar the base toolbar (ie not editor) to respond to a click for
* \param pointer the WIMP pointer details
*/
void ro_gui_theme_toolbar_editor_click(struct toolbar *toolbar,
wimp_pointer *pointer) {
wimp_window_state state;
os_error *error;
os_box box;
if (!toolbar->editor)
return;
if ((pointer->buttons != (wimp_CLICK_SELECT << 4)) &&
(pointer->buttons != (wimp_CLICK_ADJUST << 4)))
return;
state.w = pointer->w;
error = xwimp_get_window_state(&state);
if (error) {
LOG(("xwimp_get_window_state: 0x%x: %s",
error->errnum, error->errmess));
warn_user("WimpError", error->errmess);
return;
}
gui_current_drag_type = GUI_DRAG_TOOLBAR_CONFIG;
theme_toolbar_drag = toolbar;
theme_toolbar_editor_drag = !(pointer->w == toolbar->toolbar_handle);
if (theme_toolbar_editor_drag)
theme_toolbar_icon_drag =
ro_gui_theme_toolbar_get_icon(toolbar->editor,
pointer->pos.x - state.visible.x0,
state.visible.y1 - pointer->pos.y);
else
theme_toolbar_icon_drag =
ro_gui_theme_toolbar_get_icon(toolbar,
pointer->pos.x - state.visible.x0,
state.visible.y1 - pointer->pos.y);
if (!theme_toolbar_icon_drag)
return;
if ((theme_toolbar_icon_drag->icon_number >= 0) &&
(pointer->w == toolbar->editor->toolbar_handle) &&
(ro_gui_get_icon_shaded_state(
toolbar->editor->toolbar_handle,
theme_toolbar_icon_drag->icon_number)))
return;
box.x0 = pointer->pos.x - theme_toolbar_icon_drag->width / 2;
box.x1 = box.x0 + theme_toolbar_icon_drag->width;
box.y0 = pointer->pos.y - theme_toolbar_icon_drag->height / 2;
box.y1 = box.y0 + theme_toolbar_icon_drag->height;
error = xdragasprite_start(dragasprite_HPOS_CENTRE |
dragasprite_VPOS_CENTRE |
dragasprite_BOUND_POINTER |
dragasprite_DROP_SHADOW,
toolbar->descriptor->theme->sprite_area,
theme_toolbar_icon_drag->name, &box, 0);
if (error)
LOG(("xdragasprite_start: 0x%x: %s",
error->errnum, error->errmess));
}
/**
* Handle the end of a drag
*
* \param drag the details for the drag end
*/
void ro_gui_theme_toolbar_editor_drag_end(wimp_dragged *drag)
{
wimp_window_state state;
os_error *error;
wimp_pointer pointer;
struct toolbar_icon *insert_icon;
struct toolbar_icon *local_icon = NULL;
struct toolbar_icon *icon;
bool before = false;
if ((!theme_toolbar_drag) || (!theme_toolbar_icon_drag) ||
(!theme_toolbar_drag->editor))
return;
error = xwimp_get_pointer_info(&pointer);
if (error) {
LOG(("xwimp_get_pointer_info: 0x%x: %s",
error->errnum, error->errmess));
warn_user("WimpError", error->errmess);
return;
}
if (pointer.w == theme_toolbar_drag->toolbar_handle) {
/* drag from editor or toolbar to toolbar */
state.w = pointer.w;
error = xwimp_get_window_state(&state);
if (error) {
LOG(("xwimp_get_window_state: 0x%x: %s",
error->errnum, error->errmess));
warn_user("WimpError", error->errmess);
return;
}
insert_icon = ro_gui_theme_toolbar_get_insert_icon(
theme_toolbar_drag,
pointer.pos.x - state.visible.x0,
state.visible.y1 - pointer.pos.y, &before);
if (theme_toolbar_icon_drag->icon_number == -1) {
if (theme_toolbar_editor_drag) {
theme_toolbar_icon_drag =
ro_gui_theme_add_toolbar_icon(
theme_toolbar_drag,
NULL, -1);
ro_gui_theme_update_toolbar_icon(
theme_toolbar_drag,
theme_toolbar_icon_drag);
}
/* move the separator */
if (theme_toolbar_icon_drag != insert_icon) {
ro_gui_theme_delink_toolbar_icon(
theme_toolbar_drag,
theme_toolbar_icon_drag);
ro_gui_theme_link_toolbar_icon(
theme_toolbar_drag,
theme_toolbar_icon_drag,
insert_icon, before);
}
} else {
/* move/enable the icon */
for (icon = theme_toolbar_drag->icon; icon;
icon = icon->next)
if (theme_toolbar_icon_drag->icon_number ==
icon->icon_number)
local_icon = icon;
if (!local_icon)
return;
if (local_icon != insert_icon) {
ro_gui_theme_delink_toolbar_icon(
theme_toolbar_drag, local_icon);
ro_gui_theme_link_toolbar_icon(
theme_toolbar_drag, local_icon,
insert_icon, before);
}
local_icon->display = true;
}
} else if ((pointer.w == theme_toolbar_drag->editor->toolbar_handle) &&
(!theme_toolbar_editor_drag)) {
/* drag from toolbar to editor */
if (theme_toolbar_icon_drag->icon_number == -1) {
/* delete separators */
ro_gui_theme_delink_toolbar_icon(theme_toolbar_drag,
theme_toolbar_icon_drag);
ro_gui_theme_destroy_toolbar_icon(
theme_toolbar_icon_drag);
} else {
/* hide icons */
theme_toolbar_icon_drag->display = false;
}
}
theme_toolbar_drag->reformat_buttons = true;
ro_gui_theme_process_toolbar(theme_toolbar_drag, -1);
ro_gui_theme_toolbar_editor_sync(theme_toolbar_drag);
}
/**
* Adds a toolbar icon to the end of a toolbar
*
* \param toolbar the toolbar to add the icon to the end of (or NULL)
* \param name the icon name, or NULL for a separator
* \param icon_number RISC OS wimp icon number for the icon (not separators)
*/
struct toolbar_icon *ro_gui_theme_add_toolbar_icon(struct toolbar *toolbar,
const char *name, int icon_number)
{
struct toolbar_icon *toolbar_icon;
struct toolbar_icon *link_icon;
/* Separators are really a sprite called "separator"
*/
if (name == NULL) {
name = "separator";
icon_number = -1;
}
/* Create a new toolbar
*/
toolbar_icon = calloc(sizeof(struct toolbar_icon), 1);
if (!toolbar_icon) {
LOG(("No memory for malloc()"));
warn_user("NoMemory", 0);
return NULL;
}
/* Set up and link in the icon
*/
sprintf(toolbar_icon->name, name);
sprintf(toolbar_icon->validation, "R5;S%s,p%s", name, name);
toolbar_icon->icon_number = icon_number;
toolbar_icon->display = true;
if (toolbar) {
if (!toolbar->icon) {
toolbar->icon = toolbar_icon;
} else {
link_icon = toolbar->icon;
while (link_icon->next) link_icon = link_icon->next;
link_icon->next = toolbar_icon;
}
}
return toolbar_icon;
}
/**
* Updates a toolbar icon with respect to the associated sprite.
*
* \param icon the toolbar icon to update
*/
void ro_gui_theme_update_toolbar_icon(struct toolbar *toolbar,
struct toolbar_icon *icon)
{
os_coord dimensions = {0, 0};
os_mode mode;
os_error *error = NULL;
int default_width = 0;
osspriteop_area *sprite_area = NULL;
/* Separators default to a width of 16 */
if (icon->icon_number == -1)
default_width = 16;
/* Handle no theme/no sprite area */
if (!toolbar)
return;
if ((toolbar->descriptor) && (toolbar->descriptor->theme))
sprite_area = toolbar->descriptor->theme->sprite_area;
/* Get the sprite details */
if (sprite_area)
error = xosspriteop_read_sprite_info(osspriteop_USER_AREA,
sprite_area, (osspriteop_id)icon->name,
&dimensions.x, &dimensions.y, 0, &mode);
/* fallback to Wimp pool, if necessary */
if (error || sprite_area == NULL)
error = xwimpspriteop_read_sprite_info(icon->name,
&dimensions.x, &dimensions.y, 0, &mode);
/* Give up, if both failed */
if (error) {
icon->width = default_width;
icon->height = 0;
if (error->errnum != error_SPRITE_OP_DOESNT_EXIST) {
LOG(("xosspriteop_read_sprite_info: 0x%x: %s",
error->errnum, error->errmess));
warn_user("MiscError", error->errmess);
}
return;
}
/* Store the details */
ro_convert_pixels_to_os_units(&dimensions, mode);
icon->width = dimensions.x;
icon->height = dimensions.y;
}
/**
* Destroys a toolbar icon and frees any associated memory.
* The icon is not removed from any linked list.
*
* \param icon the toolbar icon to destroy
*/
void ro_gui_theme_destroy_toolbar_icon(struct toolbar_icon *icon)
{
free(icon);
}
/**
* Links a toolbar icon
*
* \param icon the toolbar icon to link
*/
void ro_gui_theme_link_toolbar_icon(struct toolbar *toolbar,
struct toolbar_icon *icon, struct toolbar_icon *link,
bool before)
{
struct toolbar_icon *temp;
assert(toolbar);
assert(icon);
assert(icon != link);
/* no icon set, no link icon, or insert at head of list */
if ((!toolbar->icon) || (!link) ||
(before && (toolbar->icon == link))) {
if (toolbar->icon != icon) {
icon->next = toolbar->icon;
toolbar->icon = icon;
}
return;
}
if (before) {
for (temp = toolbar->icon; temp; temp = temp->next)
if (temp->next == link) {
temp->next = icon;
icon->next = link;
return;
}
} else {
icon->next = link->next;
link->next = icon;
}
}
/**
* Delinks a toolbar icon
*
* \param icon the toolbar icon to delink
*/
void ro_gui_theme_delink_toolbar_icon(struct toolbar *toolbar,
struct toolbar_icon *icon)
{
struct toolbar_icon *link;
assert(toolbar);
assert(icon);
if (toolbar->icon == icon) {
toolbar->icon = icon->next;
icon->next = NULL;
return;
}
for (link = toolbar->icon; link; link = link->next)
if (link->next == icon) {
link->next = icon->next;
icon->next = NULL;
return;
}
}
/**
* Returns the toolbar icon at a specified position
*
* \param toolbar the toolbar to examine
* \param x the x co-ordinate to check
* \param y the y co-ordinate to check
* \return the toolbar icon at the specified position, or NULL for no icon
*/
struct toolbar_icon *ro_gui_theme_toolbar_get_icon(struct toolbar *toolbar,
int x, int y)
{
struct toolbar_icon *icon;
for (icon = toolbar->icon; icon; icon = icon->next)
if ((icon->display) && (icon->width > 0) &&
(icon->x <= x) && (icon->y <= y) &&
(icon->x + icon->width > x) &&
(icon->y + icon->height > y))
return icon;
return NULL;
}
/**
* Returns the toolbar icon closest to the specified position, and whether the
* position is before (left) or after (right) of it.
*
* \param toolbar the toolbar to examine
* \param x the x co-ordinate to check
* \param y the y co-ordinate to check
* \return the toolbar icon closest to the specified position, or NULL
*/
struct toolbar_icon *ro_gui_theme_toolbar_get_insert_icon(
struct toolbar *toolbar, int x, int y, bool *before)
{
struct toolbar_icon *match = NULL;
struct toolbar_icon *icon;
int closest = 65536;
int distance;
if (!toolbar->icon)
return NULL;
for (icon = toolbar->icon; icon; icon = icon->next) {
if ((icon->display) && (icon->width > 0)) {
distance = icon->x + icon->width / 2 - x;
if (distance < 0)
distance = -distance;
if (distance < closest) {
closest = distance;
match = icon;
*before = (icon->x + icon->width / 2 - x) > 0;
}
}
}
return match;
}
/**
* Sets up a toolbar with icons according to an identifier string
*/
void ro_gui_theme_add_toolbar_icons(struct toolbar *toolbar,
const char* icons[], const char* ident)
{
struct toolbar_icon *icon;
int index = 0;
int number = 0;
char hex_no[4];
/* step 1: add all main icons in their correct state */
while (icons[index]) {
icon = ro_gui_theme_add_toolbar_icon(toolbar, icons[index],
index);
sprintf(hex_no, "%x", index);
if ((icon) && (!strchr(ident, hex_no[0])))
icon->display = false;
index++;
}
/* step 2: re-order and add separators */
index = strlen(ident);
while (index--) {
if (ident[index] == '|') {
icon = ro_gui_theme_add_toolbar_icon(NULL, NULL, -1);
if (icon)
ro_gui_theme_link_toolbar_icon(toolbar, icon,
NULL, NULL);
} else {
hex_no[0] = ident[index];
hex_no[1] = '\0';
number = strtol(hex_no, NULL, 16);
for (icon = toolbar->icon; icon; icon = icon->next)
if (icon->icon_number == number) {
ro_gui_theme_delink_toolbar_icon(
toolbar, icon);
ro_gui_theme_link_toolbar_icon(toolbar,
icon, NULL, NULL);
}
}
}
}
/**
* Sets the correct help prefix for a toolbar
*/
void ro_gui_theme_set_help_prefix(struct toolbar *toolbar)
{
if (toolbar->editor) {
ro_gui_wimp_event_set_help_prefix(toolbar->toolbar_handle,
"HelpEditToolbar");
return;
}
switch (toolbar->type) {
case THEME_BROWSER_TOOLBAR:
ro_gui_wimp_event_set_help_prefix(toolbar->toolbar_handle,
"HelpToolbar");
break;
case THEME_HOTLIST_TOOLBAR:
ro_gui_wimp_event_set_help_prefix(toolbar->toolbar_handle,
"HelpHotToolbar");
break;
case THEME_HISTORY_TOOLBAR:
ro_gui_wimp_event_set_help_prefix(toolbar->toolbar_handle,
"HelpGHistToolbar");
break;
case THEME_COOKIES_TOOLBAR:
ro_gui_wimp_event_set_help_prefix(toolbar->toolbar_handle,
"HelpCookiesToolbar");
break;
case THEME_BROWSER_EDIT_TOOLBAR:
case THEME_HOTLIST_EDIT_TOOLBAR:
case THEME_HISTORY_EDIT_TOOLBAR:
case THEME_COOKIES_EDIT_TOOLBAR:
ro_gui_wimp_event_set_help_prefix(toolbar->toolbar_handle,
"HelpEditToolbar");
break;
}
}
int ro_gui_theme_height_change(struct toolbar *toolbar)
{
int height, cur_height;
cur_height = ro_gui_theme_toolbar_full_height(toolbar);
height = toolbar->old_height - cur_height;
toolbar->old_height = cur_height;
return height;
}