From d21447d096a320a08b3efb2b8768fad0dcdcfd64 Mon Sep 17 00:00:00 2001 From: Vincent Sanders Date: Thu, 5 May 2016 22:28:51 +0100 Subject: move frontends into sub directory --- frontends/riscos/wimp_event.c | 1814 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1814 insertions(+) create mode 100644 frontends/riscos/wimp_event.c (limited to 'frontends/riscos/wimp_event.c') 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 + * Copyright 2010, 2011 Stephen Fryatt + * + * 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 . + */ + +/** \file + * Automated RISC OS WIMP event handling (implementation). + */ + +#include +#include +#include +#include +#include +#include +#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; +} + -- cgit v1.2.3