summaryrefslogtreecommitdiff
path: root/frontends/riscos/wimp.c
diff options
context:
space:
mode:
Diffstat (limited to 'frontends/riscos/wimp.c')
-rw-r--r--frontends/riscos/wimp.c1134
1 files changed, 1134 insertions, 0 deletions
diff --git a/frontends/riscos/wimp.c b/frontends/riscos/wimp.c
new file mode 100644
index 000000000..2579c672e
--- /dev/null
+++ b/frontends/riscos/wimp.c
@@ -0,0 +1,1134 @@
+/*
+ * Copyright 2004, 2005 Richard Wilson <info@tinct.net>
+ * Copyright 2008 John Tytgat <joty@netsurf-browser.org>
+ *
+ * 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
+ * General RISC OS WIMP/OS library functions (implementation).
+ */
+
+#include <assert.h>
+#include <locale.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "oslib/colourtrans.h"
+#include "oslib/os.h"
+#include "oslib/osfile.h"
+#include "oslib/wimp.h"
+#include "oslib/wimpextend.h"
+#include "oslib/wimpspriteop.h"
+
+#include "utils/log.h"
+#include "utils/utf8.h"
+
+#include "riscos/gui.h"
+#include "riscos/oslib_pre7.h"
+#include "riscos/wimp.h"
+#include "riscos/ucstables.h"
+
+
+static void ro_gui_wimp_cache_furniture_sizes(wimp_w w);
+static size_t ro_gui_strlen(const char *str);
+static int ro_gui_strncmp(const char *s1, const char *s2, size_t len);
+
+static wimpextend_furniture_sizes furniture_sizes;
+static wimp_w furniture_window = NULL;
+
+/**
+ * Gets the horizontal scrollbar height
+ *
+ * \param w the window to read (or NULL to read a cached value)
+ */
+int ro_get_hscroll_height(wimp_w w)
+{
+ ro_gui_wimp_cache_furniture_sizes(w);
+ return furniture_sizes.border_widths.y0;
+}
+
+
+/**
+ * Gets the vertical scrollbar width
+ *
+ * \param w the window to read (or NULL to read a cached value)
+ */
+int ro_get_vscroll_width(wimp_w w)
+{
+ ro_gui_wimp_cache_furniture_sizes(w);
+ return furniture_sizes.border_widths.x1;
+}
+
+
+/**
+ * Gets the title bar height
+ *
+ * \param w the window to read (or NULL to read a cached value)
+ */
+int ro_get_title_height(wimp_w w)
+{
+ ro_gui_wimp_cache_furniture_sizes(w);
+ return furniture_sizes.border_widths.y1;
+}
+
+/**
+ * Caches window furniture information
+ *
+ * \param w the window to cache information from
+ * \return true on success, false on error (default values cached)
+ */
+void ro_gui_wimp_cache_furniture_sizes(wimp_w w)
+{
+ os_error *error;
+
+ if (furniture_window == w)
+ return;
+ furniture_window = w;
+ furniture_sizes.w = w;
+ furniture_sizes.border_widths.y0 = 40;
+ furniture_sizes.border_widths.x1 = 40;
+ error = xwimpextend_get_furniture_sizes(&furniture_sizes);
+ if (error) {
+ LOG("xwimpextend_get_furniture_sizes: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+}
+
+
+/**
+ * Reads a modes EIG factors.
+ *
+ * \param[in] mode mode to read EIG factors for, or -1 for current
+ * \param[out] xeig The x eig value
+ * \param[out] yeig The y eig value
+ * \return true on success else false.
+ */
+bool ro_gui_wimp_read_eig_factors(os_mode mode, int *xeig, int *yeig)
+{
+ os_error *error;
+
+ error = xos_read_mode_variable(mode, os_MODEVAR_XEIG_FACTOR, xeig, 0);
+ if (error) {
+ LOG("xos_read_mode_variable: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("MiscError", error->errmess);
+ return false;
+ }
+ error = xos_read_mode_variable(mode, os_MODEVAR_YEIG_FACTOR, yeig, 0);
+ if (error) {
+ LOG("xos_read_mode_variable: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("MiscError", error->errmess);
+ return false;
+ }
+ return true;
+}
+
+
+/**
+ * Converts the supplied os_coord from OS units to pixels.
+ *
+ * \param os_units values to convert
+ * \param mode mode to use EIG factors for, or -1 for current
+ */
+void ro_convert_os_units_to_pixels(os_coord *os_units, os_mode mode)
+{
+ int xeig = 1, yeig = 1;
+
+ ro_gui_wimp_read_eig_factors(mode, &xeig, &yeig);
+ os_units->x = ((os_units->x + (1 << xeig) - 1) >> xeig);
+ os_units->y = ((os_units->y + (1 << yeig) - 1) >> yeig);
+}
+
+
+/**
+ * Converts the supplied os_coord from pixels to OS units.
+ *
+ * \param pixels values to convert
+ * \param mode mode to use EIG factors for, or -1 for current
+ */
+void ro_convert_pixels_to_os_units(os_coord *pixels, os_mode mode)
+{
+ int xeig = 1, yeig = 1;
+
+ ro_gui_wimp_read_eig_factors(mode, &xeig, &yeig);
+ pixels->x = (pixels->x << xeig);
+ pixels->y = (pixels->y << yeig);
+}
+
+
+/**
+ * Redraws an icon
+ *
+ * \param w window handle
+ * \param i icon handle
+ */
+
+#define ro_gui_redraw_icon(w, i) xwimp_set_icon_state(w, i, 0, 0)
+
+
+/**
+ * Forces an icon to be redrawn entirely (ie not just updated).
+ *
+ * \param w window handle
+ * \param i icon handle
+ */
+void ro_gui_force_redraw_icon(wimp_w w, wimp_i i)
+{
+ wimp_icon_state ic;
+ os_error *error;
+
+ /* Get the icon data
+ */
+ ic.w = w;
+ ic.i = i;
+ error = xwimp_get_icon_state(&ic);
+ if (error) {
+ LOG("xwimp_get_icon_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+ error = xwimp_force_redraw(w, ic.icon.extent.x0, ic.icon.extent.y0,
+ ic.icon.extent.x1, ic.icon.extent.y1);
+ if (error) {
+ LOG("xwimp_force_redraw: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+}
+
+
+/**
+ * Read the contents of a text or sprite icon.
+ *
+ * \param w window handle
+ * \param i icon handle
+ * \return NUL terminated string in icon
+ *
+ * If the icon contains direct text then the returned data will
+ * be invalidated by the next call to this function. Therefore,
+ * all client calls to this function must either copy the string or
+ * ensure that this function is not called again until they are
+ * finished with the string data returned.
+ *
+ * \todo this doesn't do local encoding -> UTF-8 to match what is done in
+ * ro_gui_set_icon_string.
+ */
+const char *ro_gui_get_icon_string(wimp_w w, wimp_i i)
+{
+ static wimp_icon_state ic;
+ os_error *error;
+ char *itext;
+
+ ic.w = w;
+ ic.i = i;
+ error = xwimp_get_icon_state(&ic);
+ if (error) {
+ LOG("xwimp_get_icon_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return NULL;
+ }
+ itext = (ic.icon.flags & wimp_ICON_INDIRECTED)
+ ? ic.icon.data.indirected_text.text : ic.icon.data.text;
+ /* Guarantee NUL termination. */
+ itext[ro_gui_strlen(itext)] = '\0';
+
+ return itext;
+}
+
+
+/**
+ * Set the contents of a text or sprite icon to a string.
+ *
+ * \param w window handle
+ * \param i icon handle
+ * \param text NUL terminated string (copied)
+ * \param is_utf8 When true, the given string is UTF-8 encoded and will be
+ * converted to local encoding currently used by the Wimp. When false, the
+ * given string is assumed to be in local encoding in use by the Wimp.
+ */
+void ro_gui_set_icon_string(wimp_w w, wimp_i i, const char *text, bool is_utf8)
+{
+ wimp_caret caret;
+ wimp_icon_state ic;
+ os_error *error;
+ size_t old_len, new_len;
+ char *local_text = NULL;
+ const char *text_for_icon;
+ char *dst_text;
+ size_t dst_max_len;
+ unsigned int button_type;
+
+ if (is_utf8) {
+ nserror err;
+ /* convert text to local encoding */
+ err = utf8_to_local_encoding(text, 0, &local_text);
+ if (err != NSERROR_OK) {
+ /* A bad encoding should never happen, so assert this */
+ assert(err != NSERROR_BAD_ENCODING);
+ LOG("utf8_to_enc failed");
+ /* Paranoia */
+ local_text = NULL;
+ }
+ text_for_icon = local_text ? local_text : text;
+ }
+ else
+ text_for_icon = text;
+ new_len = strlen(text_for_icon);
+
+ /* get the icon data */
+ ic.w = w;
+ ic.i = i;
+ error = xwimp_get_icon_state(&ic);
+ if (error) {
+ LOG("xwimp_get_icon_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ goto exit;
+ }
+
+ if (ic.icon.flags & wimp_ICON_INDIRECTED) {
+ dst_text = ic.icon.data.indirected_text.text;
+ dst_max_len = ic.icon.data.indirected_text.size;
+ }
+ else {
+ dst_text = ic.icon.data.text;
+ dst_max_len = sizeof(ic.icon.data.text);
+ }
+ old_len = ro_gui_strlen(dst_text);
+ assert(old_len < dst_max_len);
+
+ /* check that the existing text is not the same as the updated text
+ * to stop flicker */
+ if (dst_max_len) {
+ if (!ro_gui_strncmp(dst_text, text_for_icon, dst_max_len))
+ goto exit;
+
+ /* copy the text across */
+ strncpy(dst_text, text_for_icon, dst_max_len - 1);
+ dst_text[dst_max_len - 1] = '\0';
+
+ /* handle the caret being in the icon */
+ button_type = (ic.icon.flags & wimp_ICON_BUTTON_TYPE)
+ >> wimp_ICON_BUTTON_TYPE_SHIFT;
+ if ((button_type == wimp_BUTTON_WRITABLE) ||
+ (button_type == wimp_BUTTON_WRITE_CLICK_DRAG)) {
+ error = xwimp_get_caret_position(&caret);
+ if (error) {
+ LOG("xwimp_get_caret_position: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ goto exit;
+ }
+ if ((caret.w == w) && (caret.i == i)) {
+ if ((size_t)caret.index > new_len
+ || (size_t)caret.index == old_len)
+ caret.index = new_len;
+ error = xwimp_set_caret_position(w, i, caret.pos.x,
+ caret.pos.y, -1, caret.index);
+ if (error) {
+ LOG("xwimp_set_caret_position: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+ }
+ }
+ ro_gui_redraw_icon(w, i);
+ }
+
+exit:
+ free(local_text);
+}
+
+
+/**
+ * Set the contents of an icon to a number.
+ *
+ * \param w window handle
+ * \param i icon handle
+ * \param value value
+ */
+void ro_gui_set_icon_integer(wimp_w w, wimp_i i, int value)
+{
+ char buffer[20]; // Big enough for 64-bit int
+
+ setlocale(LC_NUMERIC, "");
+
+ sprintf(buffer, "%d", value);
+
+ setlocale(LC_NUMERIC, "C");
+
+ ro_gui_set_icon_string(w, i, buffer, true);
+}
+
+
+/**
+ * Set the contents of an icon to a number.
+ *
+ * \param w window handle
+ * \param i icon handle
+ * \param value value to use in icon.
+ * \param decimal_places The number of decimal places to use.
+ */
+void ro_gui_set_icon_decimal(wimp_w w, wimp_i i, int value, int decimal_places)
+{
+ char buffer[20]; // Big enough for 64-bit int
+
+ setlocale(LC_NUMERIC, "");
+
+ switch (decimal_places) {
+ case 0:
+ sprintf(buffer, "%d", value);
+ break;
+ case 1:
+ sprintf(buffer, "%.1f", (float)value / 10);
+ break;
+ case 2:
+ sprintf(buffer, "%.2f", (float)value / 100);
+ break;
+ default:
+ assert(!"Unsupported decimal format");
+ break;
+ }
+
+ setlocale(LC_NUMERIC, "C");
+
+ ro_gui_set_icon_string(w, i, buffer, true);
+}
+
+
+/**
+ * Get the contents of an icon as a number.
+ *
+ * \param w window handle
+ * \param i icon handle
+ * \param decimal_places number of places to show.
+ * \return value used.
+ */
+int ro_gui_get_icon_decimal(wimp_w w, wimp_i i, int decimal_places)
+{
+ double value;
+ int multiple = 1;
+
+ for (; decimal_places > 0; decimal_places--)
+ multiple *= 10;
+
+ setlocale(LC_NUMERIC, "");
+
+ value = atof(ro_gui_get_icon_string(w, i)) * multiple;
+
+ setlocale(LC_NUMERIC, "C");
+
+ return (int)value;
+}
+
+
+/**
+ * Set the selected state of an icon.
+ *
+ * \param w window handle
+ * \param i icon handle
+ * \param state selected state
+ */
+void ro_gui_set_icon_selected_state(wimp_w w, wimp_i i, bool state)
+{
+ os_error *error;
+ if (ro_gui_get_icon_selected_state(w, i) == state) return;
+ error = xwimp_set_icon_state(w, i,
+ (state ? wimp_ICON_SELECTED : 0), wimp_ICON_SELECTED);
+ if (error) {
+ LOG("xwimp_set_icon_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+}
+
+/**
+ * Gets the selected state of an icon.
+ *
+ * \param w window handle
+ * \param i icon handle
+ */
+bool ro_gui_get_icon_selected_state(wimp_w w, wimp_i i)
+{
+ os_error *error;
+ wimp_icon_state ic;
+ ic.w = w;
+ ic.i = i;
+ error = xwimp_get_icon_state(&ic);
+ if (error) {
+ LOG("xwimp_get_icon_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return false;
+ }
+ return ((ic.icon.flags & wimp_ICON_SELECTED) != 0);
+}
+
+
+/**
+ * Set the shaded state of an icon.
+ *
+ * \param w window handle
+ * \param i icon handle
+ * \param state shaded state
+ */
+void ro_gui_set_icon_shaded_state(wimp_w w, wimp_i i, bool state)
+{
+ wimp_caret caret;
+ os_error *error;
+
+ /* update the state */
+ if (ro_gui_get_icon_shaded_state(w, i) == state)
+ return;
+ error = xwimp_set_icon_state(w, i,
+ (state ? wimp_ICON_SHADED : 0), wimp_ICON_SHADED);
+ if (error) {
+ LOG("xwimp_get_icon_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+ if (!state)
+ return;
+
+ /* ensure the caret is not in a shaded icon */
+ error = xwimp_get_caret_position(&caret);
+ if (error) {
+ LOG("xwimp_get_caret_position: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+ if ((caret.w != w) || (caret.i != i))
+ return;
+ /* move the caret to the first avaiable writable */
+ if (ro_gui_set_caret_first(w))
+ return;
+ /* lose the caret */
+ error = xwimp_set_caret_position((wimp_w)-1, (wimp_i)-1, -1, -1, -1, -1);
+ if (error) {
+ LOG("xwimp_set_caret_position: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+}
+
+
+/**
+ * Gets the shaded state of an icon.
+ *
+ * \param w window handle
+ * \param i icon handle
+ */
+bool ro_gui_get_icon_shaded_state(wimp_w w, wimp_i i)
+{
+ wimp_icon_state ic;
+ ic.w = w;
+ ic.i = i;
+ xwimp_get_icon_state(&ic);
+ return (ic.icon.flags & wimp_ICON_SHADED) != 0;
+}
+
+
+/**
+ * Set the deleted state of an icon.
+ *
+ * \param w window handle
+ * \param i icon handle
+ * \param state shaded state
+ */
+void ro_gui_set_icon_deleted_state(wimp_w w, wimp_i i, bool state)
+{
+ wimp_caret caret;
+ os_error *error;
+
+ /* update the state */
+ if (ro_gui_get_icon_deleted_state(w, i) == state)
+ return;
+ error = xwimp_set_icon_state(w, i,
+ (state ? wimp_ICON_DELETED : 0), wimp_ICON_DELETED);
+ if (error) {
+ LOG("xwimp_get_icon_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+ if (!state)
+ return;
+
+ /* ensure the caret is not in a shaded icon */
+ error = xwimp_get_caret_position(&caret);
+ if (error) {
+ LOG("xwimp_get_caret_position: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+ if ((caret.w != w) || (caret.i != i))
+ return;
+ /* move the caret to the first avaiable writable */
+ if (ro_gui_set_caret_first(w))
+ return;
+ /* lose the caret */
+ error = xwimp_set_caret_position((wimp_w)-1, (wimp_i)-1, -1, -1, -1, -1);
+ if (error) {
+ LOG("xwimp_set_caret_position: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+}
+
+
+/**
+ * Gets the deleted state of an icon.
+ *
+ * \param w window handle
+ * \param i icon handle
+ */
+bool ro_gui_get_icon_deleted_state(wimp_w w, wimp_i i)
+{
+ wimp_icon_state ic;
+ ic.w = w;
+ ic.i = i;
+ xwimp_get_icon_state(&ic);
+ return (ic.icon.flags & wimp_ICON_DELETED) != 0;
+}
+
+
+/**
+ * Set the button type of an icon.
+ *
+ * \param w window handle
+ * \param i icon handle
+ * \param type button type
+ */
+void ro_gui_set_icon_button_type(wimp_w w, wimp_i i, int type)
+{
+ os_error *error;
+ error = xwimp_set_icon_state(w, i, wimp_ICON_BUTTON_TYPE,
+ (type << wimp_ICON_BUTTON_TYPE_SHIFT));
+ if (error) {
+ LOG("xwimp_set_icon_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+}
+
+
+/**
+ * Set an icon's sprite
+ *
+ * \param w window handle
+ * \param i icon handle
+ * \param area sprite area containing sprite
+ * \param name name of sprite in area (in local encoding)
+ */
+void ro_gui_set_icon_sprite(wimp_w w, wimp_i i, osspriteop_area *area,
+ const char *name)
+{
+ wimp_icon_state ic;
+ os_error *error;
+
+ /* get the icon data */
+ ic.w = w;
+ ic.i = i;
+ error = xwimp_get_icon_state(&ic);
+ if (error) {
+ LOG("xwimp_get_icon_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ /* copy the name across */
+ if (ic.icon.data.indirected_text.size) {
+ strncpy(ic.icon.data.indirected_text.text, name,
+ (unsigned int)ic.icon.data.indirected_text.size - 1);
+ ic.icon.data.indirected_text.text[
+ ic.icon.data.indirected_text.size - 1] = '\0';
+ }
+
+ ic.icon.data.indirected_sprite.area = area;
+
+ ro_gui_redraw_icon(w, i);
+}
+
+
+/**
+ * Set a window title
+ *
+ * \param w window handle
+ * \param text new title (copied)
+ */
+void ro_gui_set_window_title(wimp_w w, const char *text)
+{
+ wimp_window_info_base window;
+ os_error *error;
+ char *title_local_enc;
+ nserror err;
+
+ /* Get the window details
+ */
+ window.w = w;
+ error = xwimp_get_window_info_header_only((wimp_window_info *)&window);
+ if (error) {
+ LOG("xwimp_get_window_info: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ /* convert text to local encoding */
+ err = utf8_to_local_encoding(text, 0, &title_local_enc);
+ if (err != NSERROR_OK) {
+ /* A bad encoding should never happen,
+ * so assert this */
+ assert(err != NSERROR_BAD_ENCODING);
+ LOG("utf8_to_enc failed");
+ return;
+ }
+
+ /* Set the title string
+ */
+ strncpy(window.title_data.indirected_text.text, title_local_enc,
+ (unsigned int)window.title_data.indirected_text.size
+ - 1);
+ window.title_data.indirected_text.text[
+ window.title_data.indirected_text.size - 1] = '\0';
+
+ /* Redraw accordingly
+ */
+ error = xwimp_force_redraw_title(w);
+ if (error) {
+ LOG("xwimp_force_redraw_title: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ free(title_local_enc);
+}
+
+
+/**
+ * Places the caret in the first available icon
+ *
+ * \param w the window to place the caret in
+ * \return true if the caret was placed, false otherwise
+ */
+bool ro_gui_set_caret_first(wimp_w w)
+{
+ int icon, b;
+ wimp_window_state win_state;
+ wimp_window_info_base window;
+ wimp_icon_state state;
+ os_error *error;
+
+ /* check the window is open */
+ win_state.w = w;
+ error = xwimp_get_window_state(&win_state);
+ if (error) {
+ LOG("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return false;
+ }
+ if (!(win_state.flags & wimp_WINDOW_OPEN))
+ return false;
+
+ /* get the window details for the icon count */
+ window.w = w;
+ error = xwimp_get_window_info_header_only((wimp_window_info *)&window);
+ if (error) {
+ LOG("xwimp_get_window_info: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return false;
+ }
+
+ /* work through all the icons */
+ state.w = w;
+ for (icon = 0; icon < window.icon_count; icon++) {
+ state.i = icon;
+ error = xwimp_get_icon_state(&state);
+ if (error) {
+ LOG("xwimp_get_icon_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return false;
+ }
+
+ /* ignore if it's shaded or not writable */
+ if (state.icon.flags & wimp_ICON_SHADED)
+ continue;
+ b = (state.icon.flags >> wimp_ICON_BUTTON_TYPE_SHIFT) & 0xf;
+ if ((b != wimp_BUTTON_WRITE_CLICK_DRAG) &&
+ (b != wimp_BUTTON_WRITABLE))
+ continue;
+
+ /* move the caret */
+ error = xwimp_set_caret_position(w, icon, 0, 0, -1,
+ strlen(state.icon.data.indirected_text.text));
+ if (error) {
+ LOG("xwimp_set_caret_position: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+ return true;
+ }
+ return false;
+}
+
+
+/**
+ * Load a sprite file into memory.
+ *
+ * \param pathname file to load
+ * \return sprite area, or 0 on memory exhaustion or error and error reported
+ */
+
+osspriteop_area *ro_gui_load_sprite_file(const char *pathname)
+{
+ int len;
+ fileswitch_object_type obj_type;
+ osspriteop_area *area;
+ os_error *error;
+
+ error = xosfile_read_stamped_no_path(pathname,
+ &obj_type, 0, 0, &len, 0, 0);
+ if (error) {
+ LOG("xosfile_read_stamped_no_path: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("MiscError", error->errmess);
+ return 0;
+ }
+ if (obj_type != fileswitch_IS_FILE) {
+ ro_warn_user("FileError", pathname);
+ return 0;
+ }
+
+ area = malloc(len + 4);
+ if (!area) {
+ ro_warn_user("NoMemory", 0);
+ return 0;
+ }
+
+ area->size = len + 4;
+ area->sprite_count = 0;
+ area->first = 16;
+ area->used = 16;
+
+ error = xosspriteop_load_sprite_file(osspriteop_USER_AREA,
+ area, pathname);
+ if (error) {
+ LOG("xosspriteop_load_sprite_file: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("MiscError", error->errmess);
+ free(area);
+ return 0;
+ }
+
+ return area;
+}
+
+
+/**
+ * Check if a sprite is present in the Wimp sprite pool.
+ *
+ * \param sprite name of sprite
+ * \return true if the sprite is present
+ */
+
+bool ro_gui_wimp_sprite_exists(const char *sprite)
+{
+ static char last_sprite_found[16];
+ os_error *error;
+
+ /* make repeated calls fast */
+ if (!strncmp(sprite, last_sprite_found, sizeof(last_sprite_found)))
+ return true;
+
+ /* fallback if not known to exist */
+ error = xwimpspriteop_select_sprite(sprite, 0);
+ if (error) {
+ if (error->errnum != error_SPRITE_OP_DOESNT_EXIST) {
+ LOG("xwimpspriteop_select_sprite: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("MiscError", error->errmess);
+ }
+ return false;
+ }
+ snprintf(last_sprite_found, sizeof(last_sprite_found), sprite);
+ return true;
+}
+
+
+/**
+ * Locate a sprite in the Wimp sprite pool, returning a pointer to it.
+ *
+ * \param name sprite name
+ * \param sprite receives pointer to sprite if found
+ * \return error ptr iff not found
+ */
+
+os_error *ro_gui_wimp_get_sprite(const char *name, osspriteop_header **sprite)
+{
+ osspriteop_area *rom_base, *ram_base;
+ os_error *error;
+
+ error = xwimp_base_of_sprites(&rom_base, &ram_base);
+ if (error) return error;
+
+ error = xosspriteop_select_sprite(osspriteop_USER_AREA,
+ ram_base, (osspriteop_id)name, sprite);
+
+ if (error && error->errnum == error_SPRITE_OP_DOESNT_EXIST)
+ error = xosspriteop_select_sprite(osspriteop_USER_AREA,
+ rom_base, (osspriteop_id)name, sprite);
+
+ return error;
+}
+
+
+/**
+ * Get the dimensions of a sprite
+ *
+ * \param *area The sprite area to use.
+ * \param *sprite Pointer to the sprite name.
+ * \param *width Return the sprite width.
+ * \param *height Return the sprite height.
+ * \return true if successful; else false.
+ */
+
+bool ro_gui_wimp_get_sprite_dimensions(osspriteop_area *area, char *sprite,
+ int *width, int *height)
+{
+ os_error *error = NULL;
+ os_mode mode;
+ os_coord dimensions;
+
+ dimensions.x = 0;
+ dimensions.y = 0;
+
+ if (area != (osspriteop_area *) -1)
+ error = xosspriteop_read_sprite_info(osspriteop_USER_AREA,
+ area, (osspriteop_id) sprite,
+ &dimensions.x, &dimensions.y, 0, &mode);
+
+ if (error != NULL || area == (osspriteop_area *) -1)
+ error = xwimpspriteop_read_sprite_info(sprite,
+ &dimensions.x, &dimensions.y, 0, &mode);
+
+ if (error == NULL) {
+ ro_convert_pixels_to_os_units(&dimensions, mode);
+ if (width != NULL)
+ *width = dimensions.x;
+ if (height != NULL)
+ *height = dimensions.y;
+ } else if (error->errnum != error_SPRITE_OP_DOESNT_EXIST) {
+ LOG("xosspriteop_read_sprite_info: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("MiscError", error->errmess);
+ return false;
+ }
+
+ return true;
+}
+
+
+/**
+ * Performs simple user redraw for a window.
+ *
+ * \param redraw wimp draw
+ * \param user_fill whether to fill the redraw area
+ * \param user_colour the colour to use when filling
+ */
+
+void ro_gui_user_redraw(wimp_draw *redraw, bool user_fill,
+ os_colour user_colour)
+{
+ os_error *error;
+ osbool more;
+
+ error = xwimp_redraw_window(redraw, &more);
+ if (error) {
+ LOG("xwimp_redraw_window: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+ while (more) {
+ if (user_fill) {
+ error = xcolourtrans_set_gcol(user_colour,
+ colourtrans_SET_BG_GCOL,
+ os_ACTION_OVERWRITE, 0, 0);
+ if (error) {
+ LOG("xcolourtrans_set_gcol: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("MiscError", error->errmess);
+ }
+ os_clg();
+ }
+ error = xwimp_get_rectangle(redraw, &more);
+ if (error) {
+ LOG("xwimp_get_rectangle: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+ }
+}
+
+
+/**
+ * Sets whether a piece of window furniture is present for a window.
+ *
+ * \param w the window to modify
+ * \param bic_mask the furniture flags to clear
+ * \param xor_mask the furniture flags to toggle
+ */
+void ro_gui_wimp_update_window_furniture(wimp_w w, wimp_window_flags bic_mask,
+ wimp_window_flags xor_mask)
+{
+ wimp_window_state state;
+ wimp_w parent;
+ bits linkage;
+ os_error *error;
+ bool open;
+
+ state.w = w;
+ error = xwimp_get_window_state_and_nesting(&state, &parent, &linkage);
+ if (error) {
+ LOG("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ open = state.flags & wimp_WINDOW_OPEN;
+ state.flags &= ~(63 << 16); /* clear bits 16-21 */
+ state.flags &= ~bic_mask;
+ state.flags ^= xor_mask;
+ if (!open)
+ state.next = wimp_HIDDEN;
+ error = xwimp_open_window_nested_with_flags(&state, parent, linkage);
+ if (error) {
+ LOG("xwimp_open_window: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+
+ if (!open) {
+ error = xwimp_close_window(w);
+ if (error) {
+ LOG("xwimp_close_window: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return;
+ }
+ }
+}
+
+
+/**
+ * Checks whether a piece of window furniture is present for a window.
+ *
+ * \param w the window to modify
+ * \param mask the furniture flags to check
+ */
+bool ro_gui_wimp_check_window_furniture(wimp_w w, wimp_window_flags mask)
+{
+ wimp_window_state state;
+ os_error *error;
+
+ state.w = w;
+ error = xwimp_get_window_state(&state);
+ if (error) {
+ LOG("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return false;
+ }
+ return state.flags & mask;
+}
+
+/**
+ * RO GUI-specific strlen, for control character terminated strings
+ *
+ * \param str The string to measure the length of
+ * \return The length of the string
+ */
+size_t ro_gui_strlen(const char *str)
+{
+ const char *str_begin;
+
+ if (str == NULL)
+ return 0;
+
+ for (str_begin = str; *str++ >= ' '; /* */)
+ /* */;
+
+ return str - str_begin - 1;
+}
+
+/**
+ * RO GUI-specific strncmp, for control character terminated strings
+ *
+ * \param s1 The first string for comparison
+ * \param s2 The second string for comparison
+ * \param len Maximum number of bytes to be checked
+ * \return 0 for equal strings up to len bytes; pos for s1 being bigger than
+ * s2; neg for s1 being smaller than s2.
+ */
+int ro_gui_strncmp(const char *s1, const char *s2, size_t len)
+{
+ while (len--) {
+ char c1 = *s1++;
+ char c2 = *s2++;
+ if (c1 < ' ' || c2 < ' ')
+ return (c1 < ' ' ? 0 : c1) - (c2 < ' ' ? 0 : c2);
+ int diff = c1 - c2;
+ if (diff)
+ return diff;
+ }
+ return 0;
+}
+
+
+/**
+ * Generic window scroll event handler.
+ *
+ * \param *scroll Pointer to Scroll Event block.
+ */
+
+void ro_gui_scroll(wimp_scroll *scroll)
+{
+ os_error *error;
+ int x = scroll->visible.x1 - scroll->visible.x0 - 32;
+ int y = scroll->visible.y1 - scroll->visible.y0 - 32;
+
+ switch (scroll->xmin) {
+ case wimp_SCROLL_PAGE_LEFT:
+ scroll->xscroll -= x;
+ break;
+ case wimp_SCROLL_COLUMN_LEFT:
+ scroll->xscroll -= 100;
+ break;
+ case wimp_SCROLL_COLUMN_RIGHT:
+ scroll->xscroll += 100;
+ break;
+ case wimp_SCROLL_PAGE_RIGHT:
+ scroll->xscroll += x;
+ break;
+ default:
+ scroll->xscroll += (x * (scroll->xmin>>2)) >> 2;
+ break;
+ }
+
+ switch (scroll->ymin) {
+ case wimp_SCROLL_PAGE_UP:
+ scroll->yscroll += y;
+ break;
+ case wimp_SCROLL_LINE_UP:
+ scroll->yscroll += 100;
+ break;
+ case wimp_SCROLL_LINE_DOWN:
+ scroll->yscroll -= 100;
+ break;
+ case wimp_SCROLL_PAGE_DOWN:
+ scroll->yscroll -= y;
+ break;
+ default:
+ scroll->yscroll += (y * (scroll->ymin>>2)) >> 2;
+ break;
+ }
+
+ error = xwimp_open_window((wimp_open *) scroll);
+ if (error) {
+ LOG("xwimp_open_window: 0x%x: %s", error->errnum, error->errmess);
+ }
+}
+