summaryrefslogtreecommitdiff
path: root/frontends/riscos/wimp_event.c
diff options
context:
space:
mode:
Diffstat (limited to 'frontends/riscos/wimp_event.c')
-rw-r--r--frontends/riscos/wimp_event.c1814
1 files changed, 1814 insertions, 0 deletions
diff --git a/frontends/riscos/wimp_event.c b/frontends/riscos/wimp_event.c
new file mode 100644
index 000000000..015e87baf
--- /dev/null
+++ b/frontends/riscos/wimp_event.c
@@ -0,0 +1,1814 @@
+/*
+ * Copyright 2005 Richard Wilson <info@tinct.net>
+ * Copyright 2010, 2011 Stephen Fryatt <stevef@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
+ * Automated RISC OS WIMP event handling (implementation).
+ */
+
+#include <assert.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "oslib/os.h"
+#include "oslib/osbyte.h"
+#include "oslib/serviceinternational.h"
+#include "oslib/wimp.h"
+
+#include "utils/log.h"
+
+#include "riscos/gui.h"
+#include "riscos/dialog.h"
+#include "riscos/menus.h"
+#include "riscos/ucstables.h"
+#include "riscos/wimp.h"
+#include "riscos/wimp_event.h"
+#include "riscos/wimputils.h"
+
+#define WIN_HASH_SIZE 32
+#define WIN_HASH(w) (((unsigned)(w) >> 5) % WIN_HASH_SIZE)
+
+typedef enum {
+ EVENT_NUMERIC_FIELD,
+ EVENT_TEXT_FIELD,
+ EVENT_UP_ARROW,
+ EVENT_DOWN_ARROW,
+ EVENT_MENU_GRIGHT,
+ EVENT_CHECKBOX,
+ EVENT_RADIO,
+ EVENT_BUTTON,
+ EVENT_CANCEL,
+ EVENT_OK
+} event_type;
+
+struct event_data_numeric_field {
+ int stepping;
+ int min;
+ int max;
+ int decimal_places;
+};
+
+struct event_data_menu_gright {
+ wimp_i field;
+ wimp_menu *menu;
+};
+
+struct icon_event {
+ event_type type;
+ wimp_i i;
+ union {
+ struct event_data_numeric_field numeric_field;
+ struct event_data_menu_gright menu_gright;
+ wimp_i linked_icon;
+ int radio_group;
+ void (*callback)(wimp_pointer *pointer);
+ } data;
+ union {
+ char *textual;
+ bool boolean;
+ } previous_value;
+ bool previous_shaded;
+ struct icon_event *next;
+};
+
+struct event_window {
+ wimp_w w;
+ bool (*ok_click)(wimp_w w);
+ bool (*mouse_click)(wimp_pointer *pointer);
+ bool (*keypress)(wimp_key *key);
+ void (*open_window)(wimp_open *open);
+ void (*close_window)(wimp_w w);
+ void (*redraw_window)(wimp_draw *redraw);
+ void (*scroll_window)(wimp_scroll *scroll);
+ void (*entering_window)(wimp_entering *entering);
+ bool (*menu_prepare)(wimp_w w, wimp_i i, wimp_menu *m,
+ wimp_pointer *p);
+ bool (*menu_selection)(wimp_w w, wimp_i i, wimp_menu *m,
+ wimp_selection *s, menu_action a);
+ void (*menu_warning)(wimp_w w, wimp_i i, wimp_menu *m,
+ wimp_selection *s, menu_action a);
+ void (*menu_close)(wimp_w w, wimp_i i, wimp_menu *m);
+ wimp_menu *window_menu;
+ bool window_menu_auto;
+ bool window_menu_iconbar;
+ const char *help_prefix;
+ const char *(*get_help_suffix)(wimp_w w, wimp_i i, os_coord *pos,
+ wimp_mouse_state buttons);
+ void *user_data;
+ struct icon_event *first;
+ struct event_window *next;
+ int max_radio_group;
+};
+
+static void ro_gui_wimp_event_ok_click(struct event_window *window,
+ wimp_mouse_state state);
+static struct event_window *ro_gui_wimp_event_get_window(wimp_w w);
+static struct event_window *ro_gui_wimp_event_find_window(wimp_w w);
+static struct icon_event *ro_gui_wimp_event_get_event(wimp_w w, wimp_i i,
+ event_type type);
+static void ro_gui_wimp_event_prepare_gright_menu(wimp_w w, struct icon_event *event);
+static struct event_window *ro_gui_wimp_event_remove_window(wimp_w w);
+
+static struct event_window *ro_gui_wimp_event_windows[WIN_HASH_SIZE];
+
+static wimp_w ro_gui_wimp_event_submenu;
+
+/**
+ * Memorises the current state of any registered components in a window.
+ *
+ * \param w the window to memorise
+ * \return true on success, false on memory exhaustion or for an unknown window
+ */
+bool ro_gui_wimp_event_memorise(wimp_w w)
+{
+ struct event_window *window;
+ struct icon_event *event;
+ bool error = false;
+
+ window = ro_gui_wimp_event_find_window(w);
+ if (!window)
+ return false;
+
+ for (event = window->first; event; event = event->next) {
+ switch (event->type) {
+ case EVENT_NUMERIC_FIELD:
+ case EVENT_TEXT_FIELD:
+ if (event->previous_value.textual)
+ free(event->previous_value.textual);
+ event->previous_value.textual = strdup(
+ ro_gui_get_icon_string(window->w, event->i));
+ if (!event->previous_value.textual) {
+ error = true;
+ LOG("Unable to store state for icon %i", event->i);
+ }
+ break;
+ case EVENT_CHECKBOX:
+ case EVENT_RADIO:
+ event->previous_value.boolean =
+ ro_gui_get_icon_selected_state(window->w, event->i);
+ break;
+ default:
+ break;
+ }
+ if (event->type != EVENT_MENU_GRIGHT)
+ event->previous_shaded = ro_gui_get_icon_shaded_state(window->w,
+ event->i);
+ }
+ return !error;
+}
+
+
+/**
+ * Restore the state of any registered components in a window to their memorised state.
+ *
+ * \param w the window to restore
+ * \return true on success, false for an unknown window
+ */
+bool ro_gui_wimp_event_restore(wimp_w w)
+{
+ struct event_window *window;
+ struct icon_event *event;
+
+ window = ro_gui_wimp_event_find_window(w);
+ if (!window)
+ return false;
+
+ for (event = window->first; event; event = event->next) {
+ switch (event->type) {
+ case EVENT_NUMERIC_FIELD:
+ case EVENT_TEXT_FIELD:
+ if (event->previous_value.textual)
+ ro_gui_set_icon_string(window->w, event->i,
+ event->previous_value.textual, true);
+ break;
+ case EVENT_CHECKBOX:
+ case EVENT_RADIO:
+ ro_gui_set_icon_selected_state(window->w, event->i,
+ event->previous_value.boolean);
+ break;
+ default:
+ break;
+ }
+ if (event->type != EVENT_MENU_GRIGHT)
+ ro_gui_set_icon_shaded_state(window->w, event->i,
+ event->previous_shaded);
+ }
+ return true;
+}
+
+
+/**
+ * Ensures all values are within pre-determined boundaries.
+ *
+ * \param w the window to memorise
+ * \return true on success, false for an unknown window
+ */
+bool ro_gui_wimp_event_validate(wimp_w w)
+{
+ struct event_window *window;
+ struct icon_event *event;
+ int value;
+
+ window = ro_gui_wimp_event_find_window(w);
+ if (!window)
+ return false;
+
+ for (event = window->first; event; event = event->next) {
+ switch (event->type) {
+ case EVENT_NUMERIC_FIELD:
+ value = ro_gui_get_icon_decimal(window->w, event->i,
+ event->data.numeric_field.decimal_places);
+ if (value < event->data.numeric_field.min)
+ value = event->data.numeric_field.min;
+ else if (value > event->data.numeric_field.max)
+ value = event->data.numeric_field.max;
+ ro_gui_set_icon_decimal(window->w, event->i, value,
+ event->data.numeric_field.decimal_places);
+ break;
+ default:
+ break;
+ }
+ }
+ return true;
+}
+
+/**
+ * Transfer event data from one window to another. This can be used as an
+ * alternative to ro_gui_wimp_event_finalise() and re-registering, if
+ * events need to continue across a change of window handle.
+ *
+ * All aspects of the registered events MUST remain the same in the new
+ * window!
+ *
+ * \param from The current window, which is to be deleted.
+ * \param to The window to which the events should transfer.
+ * \return true on success; false for an unknown window.
+ */
+
+bool ro_gui_wimp_event_transfer(wimp_w from, wimp_w to)
+{
+ struct event_window *window;
+ int h;
+
+ LOG("Transferring all events from window 0x%x to window 0x%x", (unsigned int)from, (unsigned int)to);
+
+ window = ro_gui_wimp_event_remove_window(from);
+ if (window == NULL || window->w != from)
+ return false;
+
+ h = WIN_HASH(to);
+ window->w = to;
+ window->next = ro_gui_wimp_event_windows[h];
+ ro_gui_wimp_event_windows[h] = window;
+
+ ro_gui_menu_window_changed(from, to);
+
+ return true;
+}
+
+/**
+ * Free any resources associated with a window.
+ *
+ * \param w the window to free resources for
+ */
+void ro_gui_wimp_event_finalise(wimp_w w)
+{
+ struct event_window *window;
+ struct icon_event *event;
+
+ LOG("Removing all events for window 0x%x", (unsigned int)w);
+ window = ro_gui_wimp_event_remove_window(w);
+ if (!window)
+ return;
+
+ while (window->first) {
+ event = window->first;
+ window->first = event->next;
+ switch (event->type) {
+ case EVENT_NUMERIC_FIELD:
+ case EVENT_TEXT_FIELD:
+ if (event->previous_value.textual)
+ free(event->previous_value.textual);
+ event->previous_value.textual = NULL;
+ break;
+ default:
+ break;
+ }
+ free(event);
+ }
+ free(window);
+ return;
+}
+
+
+/**
+ * Free any resources associated with a specific icon in a window.
+ *
+ * \param w The window containing the icon.
+ * \param i The icon to free resources for.
+ */
+
+void ro_gui_wimp_event_deregister(wimp_w w, wimp_i i)
+{
+ struct event_window *window;
+ struct icon_event *event, *parent, *child;
+
+ LOG("Removing all events for window 0x%x, icon %d", (unsigned int)w, (int)i);
+ window = ro_gui_wimp_event_get_window(w);
+ if (!window)
+ return;
+
+ /* Remove any events that apply to the given icon. */
+
+ event = window->first;
+ parent = NULL;
+
+ while (event != NULL) {
+ child = event->next;
+
+ if (event->i == i) {
+ LOG("Removing event 0x%x", (unsigned int)event);
+
+ if (parent == NULL)
+ window->first = child;
+ else
+ parent->next = child;
+
+ switch (event->type) {
+ case EVENT_NUMERIC_FIELD:
+ case EVENT_TEXT_FIELD:
+ if (event->previous_value.textual)
+ free(event->previous_value.textual);
+ event->previous_value.textual = NULL;
+ break;
+ default:
+ break;
+ }
+
+ free(event);
+ } else {
+ parent = event;
+ }
+
+ event = child;
+ }
+}
+
+
+/**
+ * Set the associated help prefix for a given window.
+ *
+ * \param w the window to get the prefix for
+ * \param help_prefix the prefix to associate with the window (used directly)
+ * \return true on success, or NULL for memory exhaustion
+ */
+bool ro_gui_wimp_event_set_help_prefix(wimp_w w, const char *help_prefix)
+{
+ struct event_window *window;
+
+ window = ro_gui_wimp_event_get_window(w);
+ if (!window)
+ return false;
+ window->help_prefix = help_prefix;
+ return true;
+}
+
+
+/**
+ * Get the associated help prefix.
+ *
+ * \param w the window to get the prefix for
+ * \return the associated prefix, or NULL
+ */
+const char *ro_gui_wimp_event_get_help_prefix(wimp_w w)
+{
+ struct event_window *window;
+
+ window = ro_gui_wimp_event_find_window(w);
+ if (window)
+ return window->help_prefix;
+ return NULL;
+}
+
+
+/**
+ * Register a handler to decode help suffixes for a given window.
+ *
+ */
+
+bool ro_gui_wimp_event_register_help_suffix(wimp_w w,
+ const char *(*get_help_suffix)(wimp_w w, wimp_i i,
+ os_coord *pos, wimp_mouse_state buttons))
+{
+ struct event_window *window;
+
+ window = ro_gui_wimp_event_get_window(w);
+ if (!window)
+ return false;
+ window->get_help_suffix = get_help_suffix;
+ return true;
+}
+
+
+/**
+ * Get the associated help suffix.
+ *
+ * \param w The window to get the suffix for
+ * \param i The icon
+ * \param pos The os coordinates
+ * \param buttons The button state.
+ * \return The associated prefix, or NULL
+ */
+
+const char *ro_gui_wimp_event_get_help_suffix(wimp_w w, wimp_i i,
+ os_coord *pos, wimp_mouse_state buttons)
+{
+ struct event_window *window;
+
+ window = ro_gui_wimp_event_find_window(w);
+ if (window == NULL || window->get_help_suffix == NULL)
+ return NULL;
+
+ return window->get_help_suffix(w, i, pos, buttons);
+}
+
+
+/**
+ * Sets the user data associated with a window.
+ *
+ * \param w the window to associate the data with
+ * \param user the data to associate
+ */
+bool ro_gui_wimp_event_set_user_data(wimp_w w, void *user)
+{
+ struct event_window *window;
+
+ window = ro_gui_wimp_event_get_window(w);
+ if (!window)
+ return false;
+ window->user_data = user;
+ return true;
+
+}
+
+
+/**
+ * Gets the user data associated with a window.
+ *
+ * \param w the window to retrieve the data for
+ * \return the associated data, or NULL
+ */
+void *ro_gui_wimp_event_get_user_data(wimp_w w)
+{
+ struct event_window *window;
+
+ window = ro_gui_wimp_event_find_window(w);
+ if (window)
+ return window->user_data;
+ return NULL;
+}
+
+
+/**
+ * Handles a menu selection event.
+ *
+ * (At present, this is tied to being called from menus.c and relies on that
+ * module decoding the menu into an action code. If menus.c loses its
+ * menu handling in the future, such decoding might need to move here.)
+ *
+ * The order of execution is:
+ *
+ * 1. Try to match the menu to a pop-up menu. If successful, handle it as
+ * this.
+ * 2. Try to match the menu to a window menu. If successful, pass control to
+ * the menu's registered _select handler.
+ * 3. Return event as unhandled.
+ *
+ * \param w the window to owning the menu
+ * \param i the icon owning the menu
+ * \param menu the menu that has been selected
+ * \param selection the selection information
+ * \param action the menu action info from menus.c
+ * \return true if the menu is OK for an Adjust re-open;
+ * else false.
+ */
+bool ro_gui_wimp_event_menu_selection(wimp_w w, wimp_i i, wimp_menu *menu,
+ wimp_selection *selection, menu_action action)
+{
+ struct event_window *window;
+ struct icon_event *event;
+ wimp_menu_entry *menu_entry;
+ wimp_key key;
+ os_error *error;
+ wimp_caret caret;
+ wimp_icon_state ic;
+ unsigned int button_type;
+ bool prepared;
+
+ window = ro_gui_wimp_event_find_window(w);
+ if (window == NULL)
+ return false;
+
+ /* Start by looking for an icon event that matches. If there isn't one,
+ * then return details for an unconnected menu. It's up to the
+ * event recipient to sort out if this is a window menu or not, based
+ * on the menu handle passed back.
+ */
+
+ for (event = window->first; event; event = event->next)
+ if ((event->type == EVENT_MENU_GRIGHT) && (event->i == i))
+ break;
+ if (!event) {
+ if (window->menu_selection)
+ window->menu_selection(window->w, wimp_ICON_WINDOW,
+ menu, selection, action);
+
+ /* Prepare the menu pending a possible Adjust click. */
+ if (window->menu_prepare)
+ if (!window->menu_prepare(window->w, wimp_ICON_WINDOW,
+ menu, NULL))
+ return false;
+
+ return true;
+ }
+
+ menu_entry = &menu->entries[selection->items[0]];
+ for (i = 1; selection->items[i] != -1; i++)
+ menu_entry = &menu_entry->sub_menu->
+ entries[selection->items[i]];
+
+ /* if the entry is already ticked then we do nothing */
+ if (menu_entry->menu_flags & wimp_MENU_TICKED)
+ return true;
+
+ ro_gui_set_icon_string(window->w, event->data.menu_gright.field,
+ menu_entry->data.indirected_text.text, false);
+ if (window->menu_selection)
+ window->menu_selection(window->w, event->i, menu,
+ selection, action);
+ prepared = true;
+ if (window->menu_prepare)
+ prepared = window->menu_prepare(window->w, event->i,
+ menu, NULL);
+ if (prepared)
+ ro_gui_wimp_event_prepare_gright_menu(window->w, event);
+
+ /* set the caret for writable icons and send a CTRL+U keypress to
+ * stimulate activity if needed */
+ ic.w = window->w;
+ ic.i = event->data.menu_gright.field;
+ 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;
+ }
+ 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))
+ return prepared;
+ 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 false;
+ }
+ if ((caret.w != window->w) || (caret.i != event->data.menu_gright.field)) {
+ error = xwimp_set_caret_position(window->w, event->data.menu_gright.field,
+ -1, -1, -1, strlen(menu_entry->data.indirected_text.text));
+ if (error) {
+ LOG("xwimp_set_caret_position: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ }
+ }
+ if (window->keypress) {
+ key.w = window->w;
+ key.c = 21; // ctrl+u
+ window->keypress(&key);
+ }
+ return prepared;
+}
+
+
+/**
+ * Handles a mouse click event in a registered window.
+ *
+ * The order of execution is:
+ *
+ * 1. If a menu click, and the window has an automatic window menu, this is
+ * processed immediately.
+ * 2. Any registered mouse_click routine (see ro_gui_wimp_register_mouse_click())
+ * 3. If the current icon is not registered with a type then it is assumed that no
+ * action is necessary, and the click is deemed to have been handled.
+ * 4. If the registered mouse_click routine returned false, or there was no registered
+ * routine then the automated action for the registered icon type is performed
+ *
+ * \param pointer the current pointer state
+ * \return true if the event was handled, false otherwise
+ */
+bool ro_gui_wimp_event_mouse_click(wimp_pointer *pointer)
+{
+ struct event_window *window;
+ struct icon_event *event;
+ wimp_w w;
+ struct icon_event *search;
+ int current, step, stepping, min, max, decimal_places;
+ wimp_window_state open;
+ wimp_caret caret;
+ bool prepared;
+
+ w = pointer->w;
+ window = ro_gui_wimp_event_find_window(w);
+ if (!window)
+ return false;
+
+ /* Menu clicks take priority if there is an auto menu defined. */
+ if ((pointer->buttons == wimp_CLICK_MENU) &&
+ (window->window_menu != NULL) &&
+ (window->window_menu_auto)) {
+ ro_gui_wimp_event_process_window_menu_click(pointer);
+ return true;
+ }
+
+ /* registered routines take next priority */
+ if ((window->mouse_click) && (window->mouse_click(pointer)))
+ return true;
+
+ for (event = window->first; event; event = event->next)
+ if (event->i == pointer->i)
+ break;
+ if (!event)
+ return true;
+
+ switch (event->type) {
+ case EVENT_NUMERIC_FIELD:
+ case EVENT_TEXT_FIELD:
+ break;
+ case EVENT_UP_ARROW:
+ case EVENT_DOWN_ARROW:
+ for (search = window->first; search; search = search->next)
+ if (search->i == event->data.linked_icon) break;
+ if (!search) {
+ LOG("Incorrect reference.");
+ return false;
+ }
+ stepping = search->data.numeric_field.stepping;
+ min = search->data.numeric_field.min;
+ max = search->data.numeric_field.max;
+ decimal_places = search->data.numeric_field.decimal_places;
+
+ if (pointer->buttons & wimp_CLICK_ADJUST)
+ step = -stepping;
+ else if (pointer->buttons & wimp_CLICK_SELECT)
+ step = stepping;
+ else
+ return true;
+ if (event->type == EVENT_DOWN_ARROW)
+ step = -step;
+
+ current = ro_gui_get_icon_decimal(pointer->w, event->data.linked_icon,
+ decimal_places);
+ current += step;
+ if (current < min)
+ current = min;
+ if (current > max)
+ current = max;
+ ro_gui_set_icon_decimal(pointer->w, event->data.linked_icon, current,
+ decimal_places);
+ break;
+ case EVENT_MENU_GRIGHT:
+ /* if there's already a menu open then we assume that we are part of it.
+ * to follow the standard RISC OS behaviour we add a 'send to the back'
+ * button, then close the menu (which closes us) and then finally
+ * re-open ourselves. ugh! */
+ if (current_menu != NULL) {
+ os_error *error;
+ open.w = pointer->w;
+ error = xwimp_get_window_state(&open);
+ if (error) {
+ LOG("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return false;
+ }
+ 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 false;
+ }
+ ro_gui_dialog_add_persistent(current_menu_window,
+ pointer->w);
+ ro_gui_menu_destroy();
+ error = xwimp_open_window(PTR_WIMP_OPEN(&open));
+ if (error) {
+ LOG("xwimp_open_window: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("WimpError", error->errmess);
+ return false;
+ }
+ if (caret.w == pointer->w) {
+ error = xwimp_set_caret_position(caret.w,
+ caret.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);
+ }
+ }
+ }
+ /* display the menu */
+
+ prepared = true;
+ if (window->menu_prepare != NULL)
+ prepared = window->menu_prepare(pointer->w, pointer->i,
+ event->data.menu_gright.menu, pointer);
+ if (prepared) {
+ ro_gui_wimp_event_prepare_gright_menu(pointer->w, event);
+ ro_gui_popup_menu(event->data.menu_gright.menu, pointer->w, pointer->i);
+ }
+ break;
+ case EVENT_CHECKBOX:
+ break;
+ case EVENT_RADIO:
+ for (search = window->first; search; search = search->next)
+ if ((search->type == EVENT_RADIO) &&
+ (search->data.radio_group ==
+ event->data.radio_group))
+ ro_gui_set_icon_selected_state(pointer->w,
+ search->i, (search == event));
+ break;
+ case EVENT_BUTTON:
+ if (event->data.callback)
+ event->data.callback(pointer);
+ break;
+ case EVENT_CANCEL:
+ if (pointer->buttons & wimp_CLICK_SELECT) {
+ ro_gui_dialog_close(pointer->w);
+ ro_gui_wimp_event_close_window(pointer->w);
+ ro_gui_menu_destroy();
+ } else {
+ ro_gui_wimp_event_restore(pointer->w);
+ }
+ break;
+ case EVENT_OK:
+ ro_gui_wimp_event_ok_click(window, pointer->buttons);
+ break;
+ }
+ return true;
+}
+
+
+/**
+ * Prepare a menu ready for use
+ *
+ * /param w the window owning the menu
+ * /param event the icon event owning the menu
+ */
+void ro_gui_wimp_event_prepare_gright_menu(wimp_w w, struct icon_event *event)
+{
+ int i;
+ const char *text;
+ unsigned int button_type;
+ wimp_icon_state ic;
+ wimp_menu *menu;
+ os_error *error;
+
+ /* if the linked icon is not writable then we set the ticked state
+ * of the menu item that matches the contents */
+ ic.w = w;
+ ic.i = event->data.menu_gright.field;
+ 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;
+ }
+ 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))
+ return;
+ text = ro_gui_get_icon_string(w, event->data.menu_gright.field);
+ menu = event->data.menu_gright.menu;
+ i = 0;
+ do {
+ if (!strcmp(menu->entries[i].data.indirected_text.text, text))
+ menu->entries[i].menu_flags |= wimp_MENU_TICKED;
+ else
+ menu->entries[i].menu_flags &= ~wimp_MENU_TICKED;
+ } while (!(menu->entries[i++].menu_flags & wimp_MENU_LAST));
+}
+
+
+/**
+ * Perform the necessary actions following a click on the OK button.
+ *
+ * /param window the window to perform the action on
+ * /param state the mouse button state
+ */
+void ro_gui_wimp_event_ok_click(struct event_window *window,
+ wimp_mouse_state state)
+{
+ struct icon_event *search;
+
+ for (search = window->first; search; search = search->next)
+ if (search->type == EVENT_OK) {
+ if (ro_gui_get_icon_shaded_state(window->w, search->i))
+ return;
+ break;
+ }
+ ro_gui_wimp_event_validate(window->w);
+
+ if (window->ok_click)
+ if (!window->ok_click(window->w))
+ return;
+
+ if (state & wimp_CLICK_SELECT) {
+ ro_gui_dialog_close(window->w);
+ ro_gui_wimp_event_close_window(window->w);
+ ro_gui_menu_destroy();
+ } else {
+ ro_gui_wimp_event_memorise(window->w);
+ }
+}
+
+
+/**
+ * Handle any registered keypresses, and the standard RISC OS ones
+ *
+ * \param key the key state
+ * \return true if keypress handled, false otherwise
+ */
+bool ro_gui_wimp_event_keypress(wimp_key *key)
+{
+ static const int *ucstable = NULL;
+ static int alphabet = 0;
+ static uint32_t wc = 0; /* buffer for UTF8 alphabet */
+ static int shift = 0;
+ struct event_window *window;
+ struct icon_event *event;
+ wimp_pointer pointer;
+ wimp_key k;
+ uint32_t c = (uint32_t) key->c;
+ int t_alphabet;
+ os_error *error;
+
+ window = ro_gui_wimp_event_find_window(key->w);
+ if (!window)
+ return false;
+
+ /* copy key state so we can corrupt it safely */
+ memcpy(&k, key, sizeof(wimp_key));
+
+ /* In order to make sensible use of the 0x80->0xFF ranges specified
+ * in the RISC OS 8bit alphabets, we must do the following:
+ *
+ * + Read the currently selected alphabet
+ * + Acquire a pointer to the UCS conversion table for this alphabet:
+ * + Try using ServiceInternational 8 to get the table
+ * + If that fails, use our internal table (see ucstables.c)
+ * + If the alphabet is not UTF8 and the conversion table exists:
+ * + Lookup UCS code in the conversion table
+ * + If code is -1 (i.e. undefined):
+ * + Use codepoint 0xFFFD instead
+ * + If the alphabet is UTF8, we must buffer input, thus:
+ * + If the keycode is < 0x80:
+ * + Handle it directly
+ * + If the keycode is a UTF8 sequence start:
+ * + Initialise the buffer appropriately
+ * + Otherwise:
+ * + OR in relevant bits from keycode to buffer
+ * + If we've received an entire UTF8 character:
+ * + Handle UCS code
+ * + Otherwise:
+ * + Simply handle the keycode directly, as there's no easy way
+ * of performing the mapping from keycode -> UCS4 codepoint.
+ */
+ error = xosbyte1(osbyte_ALPHABET_NUMBER, 127, 0, &t_alphabet);
+ if (error) {
+ LOG("failed reading alphabet: 0x%x: %s", error->errnum, error->errmess);
+ /* prevent any corruption of ucstable */
+ t_alphabet = alphabet;
+ }
+
+ if (t_alphabet != alphabet) {
+ void *ostable;
+ osbool unclaimed;
+ /* Alphabet has changed, so read UCS table location */
+ alphabet = t_alphabet;
+
+ error = xserviceinternational_get_ucs_conversion_table(
+ alphabet, &unclaimed, &ostable);
+ if (error != NULL) {
+ LOG("failed reading UCS conversion table: 0x%x: %s", error->errnum, error->errmess);
+ /* Try using our own table instead */
+ ucstable = ucstable_from_alphabet(alphabet);
+ } else if (unclaimed) {
+ /* Service wasn't claimed so use our own ucstable */
+ ucstable = ucstable_from_alphabet(alphabet);
+ } else {
+ /* Use the table provided by the OS */
+ ucstable = ostable;
+ }
+ }
+
+ if (c < 256) {
+ if (alphabet != 111 /* UTF8 */ && ucstable != NULL) {
+ /* defined in this alphabet? */
+ if (ucstable[c] == -1)
+ return true;
+
+ /* read UCS4 value out of table */
+ k.c = ucstable[c];
+ }
+ else if (alphabet == 111 /* UTF8 */) {
+ if ((c & 0x80) == 0x00 || (c & 0xC0) == 0xC0) {
+ /* UTF8 start sequence */
+ if ((c & 0xE0) == 0xC0) {
+ wc = ((c & 0x1F) << 6);
+ shift = 1;
+ return true;
+ }
+ else if ((c & 0xF0) == 0xE0) {
+ wc = ((c & 0x0F) << 12);
+ shift = 2;
+ return true;
+ }
+ else if ((c & 0xF8) == 0xF0) {
+ wc = ((c & 0x07) << 18);
+ shift = 3;
+ return true;
+ }
+ /* These next two have been removed
+ * from RFC3629, but there's no
+ * guarantee that RISC OS won't
+ * generate a UCS4 value outside the
+ * UTF16 plane, so we handle them
+ * anyway. */
+ else if ((c & 0xFC) == 0xF8) {
+ wc = ((c & 0x03) << 24);
+ shift = 4;
+ }
+ else if ((c & 0xFE) == 0xFC) {
+ wc = ((c & 0x01) << 30);
+ shift = 5;
+ }
+ else if (c >= 0x80) {
+ /* If this ever happens,
+ * RISC OS' UTF8 keyboard
+ * drivers are broken */
+ LOG("unexpected UTF8 start"" byte %x (ignoring)", c);
+ return true;
+ }
+ /* Anything else is ASCII, so just
+ * handle it directly. */
+ }
+ else {
+ if ((c & 0xC0) != 0x80) {
+ /* If this ever happens,
+ * RISC OS' UTF8 keyboard
+ * drivers are broken */
+ LOG("unexpected keycode: ""%x (ignoring)", c);
+ return true;
+ }
+
+ /* Continuation of UTF8 character */
+ wc |= ((c & 0x3F) << (6 * --shift));
+ if (shift > 0)
+ /* partial character */
+ return true;
+ else
+ /* got entire character, so
+ * fetch from buffer and
+ * handle it */
+ k.c = wc;
+ }
+ }
+ } else {
+ k.c |= IS_WIMP_KEY;
+ }
+
+ /* registered routines take priority */
+ if (window->keypress)
+ if (window->keypress(&k))
+ return true;
+
+ switch (key->c) {
+ /* Escape performs the CANCEL action (simulated click) */
+ case wimp_KEY_ESCAPE:
+ for (event = window->first; event; event = event->next) {
+ switch (event->type) {
+ case EVENT_CANCEL:
+ pointer.w = key->w;
+ pointer.i = event->i;
+ pointer.buttons = wimp_CLICK_SELECT;
+ ro_gui_wimp_event_mouse_click(&pointer);
+ return true;
+ default:
+ break;
+ }
+ }
+ return false;
+ /* CTRL+F2 closes a window with a close icon */
+ case wimp_KEY_CONTROL + wimp_KEY_F2:
+ if (!ro_gui_wimp_check_window_furniture(key->w,
+ wimp_WINDOW_CLOSE_ICON))
+ return false;
+ ro_gui_dialog_close(key->w);
+ ro_gui_wimp_event_close_window(key->w);
+ ro_gui_menu_destroy();
+ return true;
+ /* Return performs the OK action */
+ case wimp_KEY_RETURN:
+ if (!window->ok_click)
+ return false;
+ /* todo: check we aren't greyed out */
+ ro_gui_wimp_event_ok_click(window, wimp_CLICK_SELECT);
+ return true;
+ }
+ return false;
+}
+
+
+/**
+ * Handle any open window requests
+ *
+ * \param open the window open request
+ */
+bool ro_gui_wimp_event_open_window(wimp_open *open)
+{
+ struct event_window *window;
+
+ window = ro_gui_wimp_event_find_window(open->w);
+ if ((window) && (window->open_window)) {
+ window->open_window(open);
+ return true;
+ }
+ return false;
+}
+
+
+/**
+ * Service any close window handlers
+ *
+ * \param w the window being closed
+ */
+bool ro_gui_wimp_event_close_window(wimp_w w)
+{
+ struct event_window *window;
+
+ LOG("Close event received for window 0x%x", (unsigned int)w);
+ if (w == ro_gui_wimp_event_submenu)
+ ro_gui_wimp_event_submenu = 0;
+ window = ro_gui_wimp_event_find_window(w);
+ if ((window) && (window->close_window)) {
+ window->close_window(w);
+ return true;
+ }
+ return false;
+}
+
+
+/**
+ * Handle any redraw window requests
+ *
+ * \param redraw the window redraw request
+ */
+bool ro_gui_wimp_event_redraw_window(wimp_draw *redraw)
+{
+ struct event_window *window;
+
+ window = ro_gui_wimp_event_find_window(redraw->w);
+ if ((window) && (window->redraw_window)) {
+ window->redraw_window(redraw);
+ return true;
+ }
+ return false;
+}
+
+
+/**
+ * Handle any scroll window requests
+ *
+ * \param scroll the window scroll request
+ */
+bool ro_gui_wimp_event_scroll_window(wimp_scroll *scroll)
+{
+ struct event_window *window;
+
+ window = ro_gui_wimp_event_find_window(scroll->w);
+ if ((window) && (window->scroll_window)) {
+ window->scroll_window(scroll);
+ return true;
+ }
+ return false;
+}
+
+
+/**
+ * Handle any pointer entering window requests
+ *
+ * \param entering the pointer entering window request
+ */
+bool ro_gui_wimp_event_pointer_entering_window(wimp_entering *entering)
+{
+ struct event_window *window;
+
+ window = ro_gui_wimp_event_find_window(entering->w);
+ if ((window) && (window->entering_window)) {
+ window->entering_window(entering);
+ return true;
+ }
+ return false;
+}
+
+
+/**
+ * Process a Menu click in a window, by checking for a registered window
+ * menu and opening it if one is found.
+ *
+ * \param pointer The pointer block from the mouse click event.
+ * \return true if the click was actioned; else false.
+ */
+
+bool ro_gui_wimp_event_process_window_menu_click(wimp_pointer *pointer)
+{
+ struct event_window *window;
+
+ window = ro_gui_wimp_event_find_window(pointer->w);
+ if ((window) && (window->window_menu)
+ && (pointer->buttons == wimp_CLICK_MENU)) {
+ int xpos, ypos;
+
+ if (window->menu_prepare)
+ if (!window->menu_prepare(window->w, wimp_ICON_WINDOW,
+ window->window_menu, pointer))
+ return false;
+
+ if (window->window_menu_iconbar) {
+ int entry = 0;
+ int line_height = window->window_menu->height +
+ window->window_menu->gap;
+ int gap_height = 24; /* The fixed dotted line height */
+
+ xpos = pointer->pos.x;
+ ypos = 96;
+ do {
+ ypos += line_height;
+ if ((window->window_menu->
+ entries[entry].menu_flags &
+ wimp_MENU_SEPARATE) != 0)
+ ypos += gap_height;
+ } while ((window->window_menu->
+ entries[entry++].menu_flags &
+ wimp_MENU_LAST) == 0);
+ } else {
+ xpos = pointer->pos.x;
+ ypos = pointer->pos.y;
+ }
+
+ ro_gui_menu_create(window->window_menu, xpos, ypos, window->w);
+ return true;
+ }
+ return false;
+}
+
+
+/**
+ * Trigger a window's Prepare Menu event.
+ *
+ * \param w The window to use.
+ * \param i The icon to use.
+ * \param *menu The menu handle to use.
+ * \return true if the affected menu was prepared OK; else
+ * false.
+ */
+
+bool ro_gui_wimp_event_prepare_menu(wimp_w w, wimp_i i, wimp_menu *menu)
+{
+ struct event_window *window;
+
+ window = ro_gui_wimp_event_find_window(w);
+ if (window == NULL)
+ return false;
+
+ if (window->menu_prepare)
+ return window->menu_prepare(w, i, menu, NULL);
+
+ /* The menu is always OK if there's no event handler. */
+
+ return true;
+}
+
+/**
+ * Register a window menu to be (semi-)automatically handled.
+ *
+ * \param w The window to attach the menu to.
+ * \param m The menu to be attached.
+ * \param menu_auto true if the menu should be opened autimatically on
+ * Menu clicks with no task intervention; false to pass
+ * clicks to the window's Mouse Event handler and leave
+ * that to pass the menu click back to us for handling
+ * and menu opening.
+ * \param position_ibar true if the menu should open in an iconbar
+ * position; false to open at the pointer.
+ * \return true if the menu was registed ok; else false.
+ */
+
+bool ro_gui_wimp_event_register_menu(wimp_w w, wimp_menu *m,
+ bool menu_auto, bool position_ibar)
+{
+ struct event_window *window;
+
+ window = ro_gui_wimp_event_get_window(w);
+ if (!window)
+ return false;
+ window->window_menu = m;
+ window->window_menu_auto = menu_auto;
+ window->window_menu_iconbar = position_ibar;
+ return true;
+}
+
+/**
+ * Register a numeric field to be automatically handled
+ */
+bool ro_gui_wimp_event_register_numeric_field(wimp_w w, wimp_i i,
+ wimp_i up, wimp_i down,
+ int min, int max, int stepping, int decimal_places)
+{
+ struct icon_event *event;
+
+ event = ro_gui_wimp_event_get_event(w, i, EVENT_NUMERIC_FIELD);
+ if (!event)
+ return false;
+ event->data.numeric_field.min = min;
+ event->data.numeric_field.max = max;
+ event->data.numeric_field.stepping = stepping;
+ event->data.numeric_field.decimal_places = decimal_places;
+
+ event = ro_gui_wimp_event_get_event(w, up, EVENT_UP_ARROW);
+ if (!event)
+ return false;
+ event->data.linked_icon = i;
+
+ event = ro_gui_wimp_event_get_event(w, down, EVENT_DOWN_ARROW);
+ if (!event)
+ return false;
+ event->data.linked_icon = i;
+
+ return true;
+}
+
+
+/**
+ * Register a text field to be automatically handled
+ */
+bool ro_gui_wimp_event_register_text_field(wimp_w w, wimp_i i) {
+ struct icon_event *event;
+
+ event = ro_gui_wimp_event_get_event(w, i, EVENT_TEXT_FIELD);
+ if (!event)
+ return false;
+ return true;
+}
+
+
+/**
+ * Register an icon menu to be automatically handled
+ */
+bool ro_gui_wimp_event_register_menu_gright(wimp_w w, wimp_i i,
+ wimp_i gright, wimp_menu *menu)
+{
+ struct icon_event *event;
+
+ event = ro_gui_wimp_event_get_event(w, gright, EVENT_MENU_GRIGHT);
+ if (!event)
+ return false;
+ event->data.menu_gright.field = i;
+ event->data.menu_gright.menu = menu;
+
+ return ro_gui_wimp_event_register_text_field(w, i);
+}
+
+
+/**
+ * Register a checkbox to be automatically handled
+ */
+bool ro_gui_wimp_event_register_checkbox(wimp_w w, wimp_i i)
+{
+ struct icon_event *event;
+
+ event = ro_gui_wimp_event_get_event(w, i, EVENT_CHECKBOX);
+ if (!event)
+ return false;
+ return true;
+}
+
+
+/**
+ * Register a group of radio icons to be automatically handled
+ */
+bool ro_gui_wimp_event_register_radio(wimp_w w, wimp_i *i)
+{
+ struct event_window *window;
+
+ window = ro_gui_wimp_event_get_window(w);
+ if (!window)
+ return false;
+ window->max_radio_group++;
+
+ while (*i != -1) {
+ struct icon_event *event = ro_gui_wimp_event_get_event(w, *i,
+ EVENT_RADIO);
+ if (!event)
+ return false;
+ event->data.radio_group = window->max_radio_group;
+ i++;
+ }
+ return true;
+}
+
+
+/**
+ * Register a function to be called when a particular button is pressed.
+ */
+bool ro_gui_wimp_event_register_button(wimp_w w, wimp_i i,
+ void (*callback)(wimp_pointer *pointer))
+{
+ struct icon_event *event;
+
+ event = ro_gui_wimp_event_get_event(w, i, EVENT_BUTTON);
+ if (!event)
+ return false;
+ event->data.callback = callback;
+ return true;
+}
+
+
+/**
+ * Register a function to be called for the Cancel action on a window.
+ */
+bool ro_gui_wimp_event_register_cancel(wimp_w w, wimp_i i)
+{
+ struct icon_event *event;
+
+ event = ro_gui_wimp_event_get_event(w, i, EVENT_CANCEL);
+ if (!event)
+ return false;
+ return true;
+}
+
+
+/**
+ * Register a function to be called for the OK action on a window.
+ */
+bool ro_gui_wimp_event_register_ok(wimp_w w, wimp_i i,
+ bool (*callback)(wimp_w w))
+{
+ struct event_window *window;
+ struct icon_event *event;
+
+ window = ro_gui_wimp_event_get_window(w);
+ if (!window)
+ return false;
+ window->ok_click = callback;
+
+ event = ro_gui_wimp_event_get_event(w, i, EVENT_OK);
+ if (!event)
+ return false;
+ return true;
+}
+
+
+/**
+ * Register a function to be called for all mouse-clicks to icons
+ * in a window that don't have registered actions.
+ */
+bool ro_gui_wimp_event_register_mouse_click(wimp_w w,
+ bool (*callback)(wimp_pointer *pointer))
+{
+ struct event_window *window;
+
+ window = ro_gui_wimp_event_get_window(w);
+ if (!window)
+ return false;
+ window->mouse_click = callback;
+ return true;
+}
+
+
+/**
+ * Register a function to be called for all keypresses within a
+ * particular window.
+ *
+ * Important: the character code passed to the callback in key->c
+ * is UTF-32 (i.e. in the range [0, &10ffff]). WIMP keys (e.g. F1)
+ * will have bit 31 set.
+ *
+ */
+bool ro_gui_wimp_event_register_keypress(wimp_w w,
+ bool (*callback)(wimp_key *key))
+{
+ struct event_window *window;
+
+ window = ro_gui_wimp_event_get_window(w);
+ if (!window)
+ return false;
+ window->keypress = callback;
+ return true;
+}
+
+
+/**
+ * Register a function to be called for all window opening requests.
+ */
+bool ro_gui_wimp_event_register_open_window(wimp_w w,
+ void (*callback)(wimp_open *open))
+{
+ struct event_window *window;
+
+ window = ro_gui_wimp_event_get_window(w);
+ if (!window)
+ return false;
+ window->open_window = callback;
+ return true;
+}
+
+/**
+ * Register a function to be called after the window has been closed.
+ */
+bool ro_gui_wimp_event_register_close_window(wimp_w w,
+ void (*callback)(wimp_w w))
+{
+ struct event_window *window;
+
+ window = ro_gui_wimp_event_get_window(w);
+ if (!window)
+ return false;
+ window->close_window = callback;
+ return true;
+}
+
+/**
+ * Register a function to be called for all window redraw operations.
+ */
+bool ro_gui_wimp_event_register_redraw_window(wimp_w w,
+ void (*callback)(wimp_draw *redraw))
+{
+ struct event_window *window;
+
+ window = ro_gui_wimp_event_get_window(w);
+ if (!window)
+ return false;
+ window->redraw_window = callback;
+ return true;
+}
+
+/**
+ * Register a function to be called for all window scroll requests.
+ */
+
+bool ro_gui_wimp_event_register_scroll_window(wimp_w w,
+ void (*callback)(wimp_scroll *scroll))
+{
+ struct event_window *window;
+
+ window = ro_gui_wimp_event_get_window(w);
+ if (!window)
+ return false;
+ window->scroll_window = callback;
+ return true;
+}
+
+/**
+ * Register a function to be called for all pointer entering window requests.
+ */
+
+bool ro_gui_wimp_event_register_pointer_entering_window(wimp_w w,
+ void (*callback)(wimp_entering *entering))
+{
+ struct event_window *window;
+
+ window = ro_gui_wimp_event_get_window(w);
+ if (!window)
+ return false;
+ window->entering_window = callback;
+ return true;
+}
+
+/**
+ * Register a function to be called before a menu is (re-)opened.
+ *
+ * \param *w The window for which events should be returned.
+ * \param *callback A function to be called beofre the menu is
+ * (re-)opened.
+ * \return true if the menu was registed ok; else false.
+ */
+bool ro_gui_wimp_event_register_menu_prepare(wimp_w w,
+ bool (*callback)(wimp_w w, wimp_i i, wimp_menu *m,
+ wimp_pointer *p))
+{
+ struct event_window *window;
+
+ window = ro_gui_wimp_event_get_window(w);
+ if (!window)
+ return false;
+ window->menu_prepare = callback;
+ return true;
+}
+
+
+/**
+ * Register a function to be called following a menu selection.
+ *
+ * \param *w The window for which events should be returned.
+ * \param *callback A function to be called when a selection is
+ * made.
+ * \return true if the menu was registed ok; else false.
+ */
+bool ro_gui_wimp_event_register_menu_selection(wimp_w w,
+ bool (*callback)(wimp_w w, wimp_i i, wimp_menu *m,
+ wimp_selection *s, menu_action a))
+{
+ struct event_window *window;
+
+ window = ro_gui_wimp_event_get_window(w);
+ if (!window)
+ return false;
+ window->menu_selection = callback;
+ return true;
+}
+
+
+/**
+ * Register a function to be called when a sub-menu warning is received.
+ *
+ * \param *w The window for which events should be returned.
+ * \param *callback A function to be called whenever a submenu
+ * warning is received for the menu.
+ * \return true if the menu was registed ok; else false.
+ */
+bool ro_gui_wimp_event_register_menu_warning(wimp_w w,
+ void (*callback)(wimp_w w, wimp_i i, wimp_menu *m,
+ wimp_selection *s, menu_action a))
+{
+ struct event_window *window;
+
+ window = ro_gui_wimp_event_get_window(w);
+ if (!window)
+ return false;
+ window->menu_warning = callback;
+ return true;
+}
+
+
+/**
+ * Register a function to be called before a menu is finally closed.
+ *
+ * \param *w The window for which events should be returned.
+ * \param *callback A function to be called when the menu is closed.
+ * \return true if the menu was registed ok; else false.
+ */
+bool ro_gui_wimp_event_register_menu_close(wimp_w w,
+ void (*callback)(wimp_w w, wimp_i i, wimp_menu *m))
+{
+ struct event_window *window;
+
+ window = ro_gui_wimp_event_get_window(w);
+ if (!window)
+ return false;
+ window->menu_close = callback;
+ return true;
+}
+
+
+/**
+ * Finds the event data associated with a given window handle, or creates a
+ * new one.
+ *
+ * \param w the window to find data for
+ */
+struct event_window *ro_gui_wimp_event_get_window(wimp_w w)
+{
+ struct event_window *window;
+ int h;
+
+ assert((int)w != 0);
+ window = ro_gui_wimp_event_find_window(w);
+ if (window)
+ return window;
+
+ LOG("Creating structure for window 0x%x", (unsigned int)w);
+ window = calloc(1, sizeof(struct event_window));
+ if (!window)
+ return NULL;
+
+ h = WIN_HASH(w);
+ window->w = w;
+ window->next = ro_gui_wimp_event_windows[h];
+ ro_gui_wimp_event_windows[h] = window;
+ return window;
+}
+
+
+/**
+ * Removes the event data associated with a given handle from the hash tables,
+ * but does not delete it.
+ *
+ * \param w the window to be removed
+ * \return pointer to the event data or NULL if not found
+ */
+
+struct event_window *ro_gui_wimp_event_remove_window(wimp_w w)
+{
+ struct event_window **prev;
+ int h = WIN_HASH(w);
+
+ /* search hash chain for the window */
+ prev = &ro_gui_wimp_event_windows[h];
+ while (*prev) {
+ struct event_window *window = *prev;
+
+ if (window->w == w) {
+ /* remove from chain */
+ *prev = window->next;
+ return window;
+ }
+ prev = &window->next;
+ }
+
+ /* not found */
+ return NULL;
+}
+
+/**
+ * Find the event data associated with a given window handle
+ *
+ * \param w the window to find data for
+ */
+struct event_window *ro_gui_wimp_event_find_window(wimp_w w)
+{
+ struct event_window *window;
+ int h = WIN_HASH(w);
+
+ /* search hash chain for window */
+ for (window = ro_gui_wimp_event_windows[h]; window; window = window->next) {
+ if (window->w == w)
+ return window;
+ }
+ return NULL;
+}
+
+struct icon_event *ro_gui_wimp_event_get_event(wimp_w w, wimp_i i,
+ event_type type)
+{
+ struct event_window *window;
+ struct icon_event *event;
+
+ window = ro_gui_wimp_event_get_window(w);
+ if (!window)
+ return NULL;
+
+ for (event = window->first; event; event = event->next) {
+ if (event->i == i) {
+ event->type = type;
+ return event;
+ }
+ }
+
+ event = calloc(1, sizeof(struct icon_event));
+ if (!event)
+ return NULL;
+ event->i = i;
+ event->type = type;
+ event->next = window->first;
+ window->first = event;
+
+ return event;
+}
+
+/* Handle sumbenu warnings. This is called from ro_gui_menu_warning(), and
+ * returns to that function to have the submenu opened correctly.
+ *
+ * \param w the window to owning the menu
+ * \param i the icon owning the menu
+ * \param menu the menu that has been selected
+ * \param selection the selection information
+ * \param action the menu action info from menus.c
+ * \return true if the event was handled, false otherwise
+ */
+
+bool ro_gui_wimp_event_submenu_warning(wimp_w w, wimp_i i, wimp_menu *menu,
+ wimp_selection *selection, menu_action action)
+{
+ struct event_window *window;
+ struct icon_event *event;
+
+ ro_gui_wimp_event_register_submenu(0);
+
+ /* Process the event for any window menus. Find the window data, then
+ * try and match to an icon event. If we can, then there isn't anything
+ * to do.
+ */
+
+ window = ro_gui_wimp_event_find_window(w);
+ if (!window)
+ return false;
+
+ for (event = window->first; event; event = event->next)
+ if ((event->type == EVENT_MENU_GRIGHT) && (event->i == i))
+ break;
+ if (event) {
+ if (window->menu_close != NULL &&
+ event->type == EVENT_MENU_GRIGHT &&
+ event->data.menu_gright.menu == menu) {
+ window->menu_close(w, i, menu);
+ return true;
+ }
+
+ return false;
+ }
+
+ /* If the warning is for a window menu, then pass the event on to it. */
+
+ if ((window->window_menu) && (window->window_menu == menu)) {
+ if (window->menu_warning) {
+ window->menu_warning(w, wimp_ICON_WINDOW, menu,
+ selection, action);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * Handle menus being closed. This is called from the menus modules, in
+ * every scenario when one of our own menus is open.
+ *
+ * \param w the window to owning the menu
+ * \param i the icon owning the menu
+ * \param menu the menu that has been selected
+ */
+
+void ro_gui_wimp_event_menus_closed(wimp_w w, wimp_i i, wimp_menu *menu)
+{
+ struct event_window *window;
+ struct icon_event *event;
+
+ ro_gui_wimp_event_register_submenu(0);
+
+ /* Process the event for any window menus. Find the window data, then
+ * try and match to an icon event. If we can, then GRight menus are
+ * sent the event; otherwise, we do nothing.
+ */
+
+ window = ro_gui_wimp_event_find_window(w);
+ if (!window)
+ return;
+
+ for (event = window->first; event; event = event->next)
+ if ((event->type == EVENT_MENU_GRIGHT) && (event->i == i))
+ break;
+ if (event) {
+ if (window->menu_close != NULL &&
+ event->type == EVENT_MENU_GRIGHT &&
+ event->data.menu_gright.menu == menu)
+ window->menu_close(w, i, menu);
+ return;
+ }
+
+ /* If the close is for a window menu, then pass the event on to it. */
+
+ if ((window->window_menu) && (window->window_menu == menu) &&
+ (window->menu_close))
+ window->menu_close(w, wimp_ICON_WINDOW, menu);
+}
+
+/**
+ * Register a submenu as being opened
+ */
+void ro_gui_wimp_event_register_submenu(wimp_w w)
+{
+ if (ro_gui_wimp_event_submenu)
+ ro_gui_wimp_event_close_window(ro_gui_wimp_event_submenu);
+ ro_gui_wimp_event_submenu = w;
+}
+