netsurf/riscos/gui/status_bar.c
2014-11-18 17:16:26 +00:00

643 lines
16 KiB
C

/*
* Copyright 2006 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
* UTF8 status bar (implementation).
*/
#include <assert.h>
#include <stdbool.h>
#include <string.h>
#include "swis.h"
#include "oslib/colourtrans.h"
#include "oslib/os.h"
#include "oslib/wimp.h"
#include "oslib/wimpspriteop.h"
#include "desktop/plotters.h"
#include "utils/log.h"
#include "utils/utils.h"
#include "riscos/gui.h"
#include "riscos/wimp.h"
#include "riscos/wimp_event.h"
#include "riscos/wimputils.h"
#include "riscos/font.h"
#include "riscos/gui/progress_bar.h"
#include "riscos/gui/status_bar.h"
#define ICON_WIDGET 0
#define WIDGET_WIDTH 12
#define PROGRESS_WIDTH 160
struct status_bar {
wimp_w w; /**< status bar window handle */
wimp_w parent; /**< parent window handle */
const char *text; /**< status bar text */
struct progress_bar *pb; /**< progress bar */
unsigned int scale; /**< current status bar scale */
int width; /**< current status bar width */
bool visible; /**< status bar is visible? */
};
static char status_widget_text[] = "";
static char status_widget_validation[] = "R5;Pptr_lr,8,6";
wimp_WINDOW(1) status_bar_definition = {
{0, 0, 1, 1},
0,
0,
wimp_TOP,
wimp_WINDOW_NEW_FORMAT | wimp_WINDOW_MOVEABLE |
wimp_WINDOW_FURNITURE_WINDOW |
wimp_WINDOW_IGNORE_XEXTENT,
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, 65535, 65535},
0,
0,
wimpspriteop_AREA,
1,
1,
{""},
1,
{
{
{0, 0, 1, 1},
wimp_ICON_TEXT | wimp_ICON_INDIRECTED |
wimp_ICON_BORDER | wimp_ICON_FILLED |
(wimp_COLOUR_LIGHT_GREY <<
wimp_ICON_BG_COLOUR_SHIFT) |
(wimp_BUTTON_CLICK_DRAG <<
wimp_ICON_BUTTON_TYPE_SHIFT),
{
.indirected_text = {
status_widget_text,
status_widget_validation,
1
}
}
}
}
};
static void ro_gui_status_bar_open(wimp_open *open);
static bool ro_gui_status_bar_click(wimp_pointer *pointer);
static void ro_gui_status_bar_redraw(wimp_draw *redraw);
static void ro_gui_status_bar_redraw_callback(void *handle);
static void ro_gui_status_position_progress_bar(struct status_bar *sb);
/**
* Create a new status bar
*
* \param parent the window to contain the status bar
* \param width the proportional width to use (0...10,000)
*/
struct status_bar *ro_gui_status_bar_create(wimp_w parent, unsigned int width)
{
struct status_bar *sb;
os_error *error;
sb = calloc(1, sizeof(*sb));
if (!sb)
return NULL;
sb->pb = ro_gui_progress_bar_create();
if (!sb->pb)
return NULL;
error = xwimp_create_window((wimp_window *)&status_bar_definition,
&sb->w);
if (error) {
LOG(("xwimp_create_window: 0x%x: %s",
error->errnum, error->errmess));
free(sb);
return NULL;
}
sb->parent = parent;
sb->scale = width;
sb->visible = true;
ro_gui_wimp_event_set_user_data(sb->w, sb);
ro_gui_wimp_event_register_open_window(sb->w,
ro_gui_status_bar_open);
ro_gui_wimp_event_register_mouse_click(sb->w,
ro_gui_status_bar_click);
ro_gui_wimp_event_register_redraw_window(sb->w,
ro_gui_status_bar_redraw);
ro_gui_wimp_event_set_help_prefix(sb->w, "HelpStatus");
ro_gui_status_bar_resize(sb);
return sb;
}
/**
* Destroy a status bar and free all associated resources
*
* \param sb the status bar to destroy
*/
void ro_gui_status_bar_destroy(struct status_bar *sb)
{
os_error *error;
assert(sb);
ro_gui_wimp_event_finalise(sb->w);
error = xwimp_delete_window(sb->w);
if (error) {
LOG(("xwimp_delete_window: 0x%x:%s",
error->errnum, error->errmess));
}
ro_gui_progress_bar_destroy(sb->pb);
/* Remove any scheduled redraw callbacks */
riscos_schedule(-1, ro_gui_status_bar_redraw_callback, (void *) sb);
free(sb);
}
/**
* Get the handle of the window that represents a status bar
*
* \param sb the status bar to get the window handle of
* \return the status bar's window handle
*/
wimp_w ro_gui_status_bar_get_window(struct status_bar *sb)
{
assert(sb);
return sb->w;
}
/**
* Get the proportional width the status bar is currently using
*
* \param sb the status bar to get the width of
* \return the status bar's width (0...10,000)
*/
unsigned int ro_gui_status_bar_get_width(struct status_bar *sb)
{
assert(sb);
return sb->scale;
}
/**
* Set the visibility status of the status bar
*
* \param sb the status bar to check the visiblity of
* \param visible if the status bar should be shown or not.
* \return whether the status bar is visible
*/
void ro_gui_status_bar_set_visible(struct status_bar *sb, bool visible)
{
assert(sb);
sb->visible = visible;
if (visible) {
ro_gui_status_bar_resize(sb);
} else {
os_error *error = xwimp_close_window(sb->w);
if (error) {
LOG(("xwimp_close_window: 0x%x:%s",
error->errnum, error->errmess));
}
}
}
/**
* Get the visibility status of the status bar
*
* \param sb the status bar to check the visiblity of
* \return whether the status bar is visible
*/
bool ro_gui_status_bar_get_visible(struct status_bar *sb)
{
assert(sb);
return sb->visible;
}
/**
* Set the value of the progress bar
*
* \param sb the status bar to set the progress of
* \param value the value to use
*/
void ro_gui_status_bar_set_progress_value(struct status_bar *sb,
unsigned int value)
{
assert(sb);
ro_gui_status_bar_set_progress_range(sb,
max(value, ro_gui_progress_bar_get_range(sb->pb)));
ro_gui_progress_bar_set_value(sb->pb, value);
}
/**
* Set the range of the progress bar
*
* \param sb the status bar to set the range of
* \param range the range of the progress bar
*/
void ro_gui_status_bar_set_progress_range(struct status_bar *sb,
unsigned int range)
{
unsigned int old_range;
assert(sb);
old_range = ro_gui_progress_bar_get_range(sb->pb);
ro_gui_progress_bar_set_range(sb->pb, range);
LOG(("Ranges are %i vs %i", old_range, range));
if ((old_range == 0) && (range != 0)) {
ro_gui_status_position_progress_bar(sb);
} else if ((old_range != 0) && (range == 0)) {
os_error *error = xwimp_close_window(
ro_gui_progress_bar_get_window(sb->pb));
if (error) {
LOG(("xwimp_close_window: 0x%x:%s",
error->errnum, error->errmess));
}
}
}
/**
* Set the icon for the progress bar
*
* \param sb the status bar to set the icon for
* \param icon the icon to use, or NULL for no icon
*/
void ro_gui_status_bar_set_progress_icon(struct status_bar *sb,
const char *icon)
{
assert(sb);
ro_gui_progress_bar_set_icon(sb->pb, icon);
}
/**
* Set the text to display in the status bar
*
* \param sb the status bar to set the text for
* \param text the UTF8 text to display, or NULL for none
*/
void ro_gui_status_bar_set_text(struct status_bar *sb, const char *text)
{
assert(sb);
sb->text = text;
/* Schedule a window redraw for 1cs' time.
*
* We do this to ensure that redraws as a result of text changes
* do not prevent other applications obtaining CPU time.
*
* The scheduled callback will be run when we receive the first
* null poll after 1cs has elapsed. It may then issue a redraw
* request to the Wimp.
*
* The scheduler ensures that only one instance of the
* { callback, handle } pair is registered at once.
*/
if (sb->visible && text != NULL) {
riscos_schedule(10, ro_gui_status_bar_redraw_callback, sb);
}
}
/**
* Resize a status bar following a change in the dimensions of the
* parent window.
*
* \param sb the status bar to resize
*/
void ro_gui_status_bar_resize(struct status_bar *sb)
{
int window_width;
int status_width, status_height;
wimp_window_state state;
os_error *error;
os_box extent;
if ((!sb) || (!sb->visible))
return;
/* get the window work area dimensions */
state.w = sb->parent;
error = xwimp_get_window_state(&state);
if (error) {
LOG(("xwimp_get_window_state: 0x%x: %s",
error->errnum, error->errmess));
return;
}
window_width = state.visible.x1 - state.visible.x0;
/* recalculate the scaled width */
status_width = (window_width * sb->scale) / 10000;
if (status_width < WIDGET_WIDTH)
status_width = WIDGET_WIDTH;
status_height = ro_get_hscroll_height(sb->parent);
/* resize the status/resize icons */
if (status_width != sb->width) {
/* update the window extent */
int redraw_left, redraw_right;
extent.x0 = 0;
extent.y0 = 0;
extent.x1 = status_width;
extent.y1 = status_height - 4;
error = xwimp_set_extent(sb->w, &extent);
if (error) {
LOG(("xwimp_set_extent: 0x%x: %s",
error->errnum, error->errmess));
return;
}
/* re-open the nested window */
state.w = sb->w;
state.xscroll = 0;
state.yscroll = 0;
state.next = wimp_TOP;
state.visible.x0 = state.visible.x0;
state.visible.y1 = state.visible.y0 - 2;
state.visible.x1 = state.visible.x0 + status_width;
state.visible.y0 = state.visible.y1 - status_height + 4;
error = xwimp_open_window_nested(PTR_WIMP_OPEN(&state),
sb->parent,
wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
<< wimp_CHILD_XORIGIN_SHIFT |
wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
<< wimp_CHILD_YORIGIN_SHIFT |
wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
<< wimp_CHILD_LS_EDGE_SHIFT |
wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
<< wimp_CHILD_BS_EDGE_SHIFT |
wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
<< wimp_CHILD_RS_EDGE_SHIFT |
wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
<< wimp_CHILD_TS_EDGE_SHIFT);
if (error) {
LOG(("xwimp_open_window_nested: 0x%x: %s",
error->errnum, error->errmess));
return;
}
ro_gui_status_position_progress_bar(sb);
error = xwimp_resize_icon(sb->w, ICON_WIDGET,
status_width - WIDGET_WIDTH, 0,
status_width, status_height - 4);
if (error) {
LOG(("xwimp_resize_icon: 0x%x: %s",
error->errnum, error->errmess));
return;
}
redraw_left = min(status_width, sb->width) - WIDGET_WIDTH - 2;
redraw_right = max(status_width, sb->width);
xwimp_force_redraw(sb->w, redraw_left, 0,
redraw_right, status_height);
sb->width = status_width;
}
}
/**
* Process a WIMP redraw request
*
* \param redraw the redraw request to process
*/
void ro_gui_status_bar_redraw(wimp_draw *redraw)
{
struct status_bar *sb;
os_error *error;
osbool more;
rufl_code code;
sb = (struct status_bar *)ro_gui_wimp_event_get_user_data(redraw->w);
assert(sb);
/* initialise the plotters */
ro_plot_origin_x = 0;
ro_plot_origin_y = 0;
/* redraw the window */
error = xwimp_redraw_window(redraw, &more);
if (error) {
LOG(("xwimp_redraw_window: 0x%x: %s",
error->errnum, error->errmess));
return;
}
while (more) {
/* redraw the status text */
if (sb->text) {
error = xcolourtrans_set_font_colours(font_CURRENT,
0xeeeeee00, 0x00000000, 14, 0, 0, 0);
if (error) {
LOG(("xcolourtrans_set_font_colours: 0x%x: %s",
error->errnum, error->errmess));
return;
}
code = rufl_paint(ro_gui_desktop_font_family,
ro_gui_desktop_font_style,
ro_gui_desktop_font_size,
sb->text, strlen(sb->text),
redraw->box.x0 + 6, redraw->box.y0 + 8,
rufl_BLEND_FONT);
if (code != rufl_OK) {
if (code == rufl_FONT_MANAGER_ERROR)
LOG(("rufl_FONT_MANAGER_ERROR: 0x%x: %s",
rufl_fm_error->errnum,
rufl_fm_error->errmess));
else
LOG(("rufl_paint: 0x%x", code));
}
}
/* separate the widget from the text with a line */
ro_plotters.rectangle((redraw->box.x0 + sb->width - WIDGET_WIDTH - 2) >> 1,
-redraw->box.y0 >> 1,
(redraw->box.x0 + sb->width - WIDGET_WIDTH) >> 1,
-redraw->box.y1 >> 1,
plot_style_fill_black);
error = xwimp_get_rectangle(redraw, &more);
if (error) {
LOG(("xwimp_get_rectangle: 0x%x: %s",
error->errnum, error->errmess));
return;
}
}
}
/**
* Callback for scheduled redraw
*
* \param handle Callback handle
*/
void ro_gui_status_bar_redraw_callback(void *handle)
{
struct status_bar *sb = handle;
wimp_force_redraw(sb->w, 0, 0, sb->width - WIDGET_WIDTH, 65536);
}
/**
* Process an mouse_click event for a status window.
*
* \param pointer details of the mouse click
*/
bool ro_gui_status_bar_click(wimp_pointer *pointer)
{
wimp_drag drag;
os_error *error;
switch (pointer->i) {
case ICON_WIDGET:
drag.w = pointer->w;
drag.type = wimp_DRAG_SYSTEM_SIZE;
drag.initial.x0 = pointer->pos.x;
drag.initial.x1 = pointer->pos.x;
drag.initial.y0 = pointer->pos.y;
drag.initial.y1 = pointer->pos.y;
error = xwimp_drag_box(&drag);
if (error) {
LOG(("xwimp_drag_box: 0x%x: %s",
error->errnum, error->errmess));
}
break;
}
return true;
}
/**
* Process an open_window request for a status window.
*
* \param open the request to process
*/
void ro_gui_status_bar_open(wimp_open *open)
{
struct status_bar *sb;
int window_width, status_width;
wimp_window_state state;
os_error *error;
/* get the parent width for scaling */
sb = (struct status_bar *)ro_gui_wimp_event_get_user_data(open->w);
state.w = sb->parent;
error = xwimp_get_window_state(&state);
if (error) {
LOG(("xwimp_get_window_state: 0x%x: %s",
error->errnum, error->errmess));
return;
}
window_width = state.visible.x1 - state.visible.x0;
if (window_width == 0)
window_width = 1;
status_width = open->visible.x1 - open->visible.x0;
if (status_width <= 12)
status_width = 0;
/* store the new size */
sb->scale = (10000 * status_width) / window_width;
if (sb->scale > 10000)
sb->scale = 10000;
ro_gui_status_bar_resize(sb);
}
/**
* Reposition the progress component following a change in the
* dimension of the status window.
*
* \param sb the status bar to update
*/
void ro_gui_status_position_progress_bar(struct status_bar *sb)
{
wimp_window_state state;
os_error *error;
int left, right;
if (!sb)
return;
if (ro_gui_progress_bar_get_range(sb->pb) == 0)
return;
/* get the window work area dimensions */
state.w = sb->w;
error = xwimp_get_window_state(&state);
if (error) {
LOG(("xwimp_get_window_state: 0x%x: %s",
error->errnum, error->errmess));
return;
}
/* calculate the dimensions */
right = state.visible.x1 - WIDGET_WIDTH - 2;
left = max(state.visible.x0, right - PROGRESS_WIDTH);
/* re-open the nested window */
state.w = ro_gui_progress_bar_get_window(sb->pb);
state.xscroll = 0;
state.yscroll = 0;
state.next = wimp_TOP;
state.visible.x0 = left;
state.visible.x1 = right;
error = xwimp_open_window_nested(PTR_WIMP_OPEN(&state),
sb->w,
wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
<< wimp_CHILD_XORIGIN_SHIFT |
wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
<< wimp_CHILD_YORIGIN_SHIFT |
wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
<< wimp_CHILD_LS_EDGE_SHIFT |
wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
<< wimp_CHILD_BS_EDGE_SHIFT |
wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
<< wimp_CHILD_RS_EDGE_SHIFT |
wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
<< wimp_CHILD_TS_EDGE_SHIFT);
if (error) {
LOG(("xwimp_open_window: 0x%x: %s",
error->errnum, error->errmess));
}
/* update the progress bar display on non-standard width */
if ((right - left) != PROGRESS_WIDTH)
ro_gui_progress_bar_update(sb->pb, right - left,
state.visible.y1 - state.visible.y0);
}