1 /*
2  * Copyright 2005 Richard Wilson <info@tinct.net>
3  * Copyright 2010, 2011 Stephen Fryatt <stevef@netsurf-browser.org>
4  *
5  * This file is part of NetSurf, http://www.netsurf-browser.org/
6  *
7  * NetSurf is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; version 2 of the License.
10  *
11  * NetSurf is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 /** \file
21  * Automated RISC OS WIMP event handling (implementation).
22  */
23 
24 #include <assert.h>
25 #include <inttypes.h>
26 #include <stdbool.h>
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include "oslib/os.h"
31 #include "oslib/osbyte.h"
32 #include "oslib/serviceinternational.h"
33 #include "oslib/wimp.h"
34 
35 #include "utils/log.h"
36 
37 #include "riscos/gui.h"
38 #include "riscos/dialog.h"
39 #include "riscos/menus.h"
40 #include "riscos/ucstables.h"
41 #include "riscos/wimp.h"
42 #include "riscos/wimp_event.h"
43 #include "riscos/wimputils.h"
44 
45 #define WIN_HASH_SIZE 32
46 #define WIN_HASH(w) (((unsigned)(w) >> 5) % WIN_HASH_SIZE)
47 
48 typedef enum {
49 	EVENT_NUMERIC_FIELD,
50 	EVENT_TEXT_FIELD,
51 	EVENT_UP_ARROW,
52 	EVENT_DOWN_ARROW,
53 	EVENT_MENU_GRIGHT,
54 	EVENT_CHECKBOX,
55 	EVENT_RADIO,
56 	EVENT_BUTTON,
57 	EVENT_CANCEL,
58 	EVENT_OK
59 } event_type;
60 
61 struct event_data_numeric_field {
62 	int stepping;
63 	int min;
64 	int max;
65 	int decimal_places;
66 };
67 
68 struct event_data_menu_gright {
69 	wimp_i field;
70 	wimp_menu *menu;
71 };
72 
73 struct icon_event {
74 	event_type type;
75 	wimp_i i;
76 	union {
77 		struct event_data_numeric_field numeric_field;
78 		struct event_data_menu_gright menu_gright;
79 		wimp_i linked_icon;
80 		int radio_group;
81 		void (*callback)(wimp_pointer *pointer);
82 	} data;
83 	union {
84 		char *textual;
85 		bool boolean;
86 	} previous_value;
87 	bool previous_shaded;
88 	struct icon_event *next;
89 };
90 
91 struct event_window {
92 	wimp_w w;
93 	bool (*ok_click)(wimp_w w);
94 	bool (*mouse_click)(wimp_pointer *pointer);
95 	bool (*keypress)(wimp_key *key);
96 	void (*open_window)(wimp_open *open);
97 	void (*close_window)(wimp_w w);
98 	void (*redraw_window)(wimp_draw *redraw);
99 	void (*scroll_window)(wimp_scroll *scroll);
100 	void (*entering_window)(wimp_entering *entering);
101 	bool (*menu_prepare)(wimp_w w, wimp_i i, wimp_menu *m,
102 			wimp_pointer *p);
103 	bool (*menu_selection)(wimp_w w, wimp_i i, wimp_menu *m,
104 			wimp_selection *s, menu_action a);
105 	void (*menu_warning)(wimp_w w, wimp_i i, wimp_menu *m,
106 			wimp_selection *s, menu_action a);
107 	void (*menu_close)(wimp_w w, wimp_i i, wimp_menu *m);
108 	wimp_menu *window_menu;
109 	bool window_menu_auto;
110 	bool window_menu_iconbar;
111 	const char *help_prefix;
112 	const char *(*get_help_suffix)(wimp_w w, wimp_i i, os_coord *pos,
113 			wimp_mouse_state buttons);
114 	void *user_data;
115 	struct icon_event *first;
116 	struct event_window *next;
117 	int max_radio_group;
118 };
119 
120 static void ro_gui_wimp_event_ok_click(struct event_window *window,
121 		wimp_mouse_state state);
122 static struct event_window *ro_gui_wimp_event_get_window(wimp_w w);
123 static struct event_window *ro_gui_wimp_event_find_window(wimp_w w);
124 static struct icon_event *ro_gui_wimp_event_get_event(wimp_w w, wimp_i i,
125 		event_type type);
126 static void ro_gui_wimp_event_prepare_gright_menu(wimp_w w, struct icon_event *event);
127 static struct event_window *ro_gui_wimp_event_remove_window(wimp_w w);
128 
129 static struct event_window *ro_gui_wimp_event_windows[WIN_HASH_SIZE];
130 
131 static wimp_w ro_gui_wimp_event_submenu;
132 
133 /**
134  * Memorises the current state of any registered components in a window.
135  *
136  * \param w	the window to memorise
137  * \return true on success, false on memory exhaustion or for an unknown window
138  */
ro_gui_wimp_event_memorise(wimp_w w)139 bool ro_gui_wimp_event_memorise(wimp_w w)
140 {
141 	struct event_window *window;
142 	struct icon_event *event;
143 	bool error = false;
144 
145 	window = ro_gui_wimp_event_find_window(w);
146 	if (!window)
147 		return false;
148 
149 	for (event = window->first; event; event = event->next) {
150 		switch (event->type) {
151 			case EVENT_NUMERIC_FIELD:
152 			case EVENT_TEXT_FIELD:
153 				if (event->previous_value.textual)
154 					free(event->previous_value.textual);
155 				event->previous_value.textual = strdup(
156 					ro_gui_get_icon_string(window->w, event->i));
157 				if (!event->previous_value.textual) {
158 					error = true;
159 					NSLOG(netsurf, INFO,
160 					      "Unable to store state for icon %i",
161 					      event->i);
162 				}
163 				break;
164 			case EVENT_CHECKBOX:
165 			case EVENT_RADIO:
166 				event->previous_value.boolean =
167 					ro_gui_get_icon_selected_state(window->w, event->i);
168 				break;
169 			default:
170 				break;
171 		}
172 		if (event->type != EVENT_MENU_GRIGHT)
173 			event->previous_shaded = ro_gui_get_icon_shaded_state(window->w,
174 					event->i);
175 	}
176 	return !error;
177 }
178 
179 
180 /**
181  * Restore the state of any registered components in a window to their memorised state.
182  *
183  * \param w	the window to restore
184  * \return true on success, false for an unknown window
185  */
ro_gui_wimp_event_restore(wimp_w w)186 bool ro_gui_wimp_event_restore(wimp_w w)
187 {
188 	struct event_window *window;
189 	struct icon_event *event;
190 
191 	window = ro_gui_wimp_event_find_window(w);
192 	if (!window)
193 		return false;
194 
195 	for (event = window->first; event; event = event->next) {
196 		switch (event->type) {
197 			case EVENT_NUMERIC_FIELD:
198 			case EVENT_TEXT_FIELD:
199 				if (event->previous_value.textual)
200 					ro_gui_set_icon_string(window->w, event->i,
201 						event->previous_value.textual, true);
202 				break;
203 			case EVENT_CHECKBOX:
204 			case EVENT_RADIO:
205 				ro_gui_set_icon_selected_state(window->w, event->i,
206 					event->previous_value.boolean);
207 				break;
208 			default:
209 				break;
210 		}
211 		if (event->type != EVENT_MENU_GRIGHT)
212 			ro_gui_set_icon_shaded_state(window->w, event->i,
213 					event->previous_shaded);
214 	}
215 	return true;
216 }
217 
218 
219 /**
220  * Ensures all values are within pre-determined boundaries.
221  *
222  * \param w	the window to memorise
223  * \return true on success, false for an unknown window
224  */
ro_gui_wimp_event_validate(wimp_w w)225 bool ro_gui_wimp_event_validate(wimp_w w)
226 {
227 	struct event_window *window;
228 	struct icon_event *event;
229 	int value;
230 
231 	window = ro_gui_wimp_event_find_window(w);
232 	if (!window)
233 		return false;
234 
235 	for (event = window->first; event; event = event->next) {
236 		switch (event->type) {
237 			case EVENT_NUMERIC_FIELD:
238 				value = ro_gui_get_icon_decimal(window->w, event->i,
239 						event->data.numeric_field.decimal_places);
240 				if (value < event->data.numeric_field.min)
241 					value = event->data.numeric_field.min;
242 				else if (value > event->data.numeric_field.max)
243 					value = event->data.numeric_field.max;
244 				ro_gui_set_icon_decimal(window->w, event->i, value,
245 						event->data.numeric_field.decimal_places);
246 				break;
247 			default:
248 				break;
249 		}
250 	}
251 	return true;
252 }
253 
254 /**
255  * Transfer event data from one window to another. This can be used as an
256  * alternative to ro_gui_wimp_event_finalise() and re-registering, if
257  * events need to continue across a change of window handle.
258  *
259  * All aspects of the registered events MUST remain the same in the new
260  * window!
261  *
262  * \param from		The current window, which is to be deleted.
263  * \param to		The window to which the events should transfer.
264  * \return		true on success; false for an unknown window.
265  */
266 
ro_gui_wimp_event_transfer(wimp_w from,wimp_w to)267 bool ro_gui_wimp_event_transfer(wimp_w from, wimp_w to)
268 {
269 	struct event_window	*window;
270 	int			h;
271 
272 	NSLOG(netsurf, INFO,
273 	      "Transferring all events from window 0x%x to window 0x%x",
274 	      (unsigned int)from,
275 	      (unsigned int)to);
276 
277 	window = ro_gui_wimp_event_remove_window(from);
278 	if (window == NULL || window->w != from)
279 		return false;
280 
281 	h = WIN_HASH(to);
282 	window->w = to;
283 	window->next = ro_gui_wimp_event_windows[h];
284 	ro_gui_wimp_event_windows[h] = window;
285 
286 	ro_gui_menu_window_changed(from, to);
287 
288 	return true;
289 }
290 
291 /**
292  * Free any resources associated with a window.
293  *
294  * \param w	the window to free resources for
295  */
ro_gui_wimp_event_finalise(wimp_w w)296 void ro_gui_wimp_event_finalise(wimp_w w)
297 {
298 	struct event_window *window;
299 	struct icon_event *event;
300 
301 	NSLOG(netsurf, INFO, "Removing all events for window 0x%x",
302 	      (unsigned int)w);
303 	window = ro_gui_wimp_event_remove_window(w);
304 	if (!window)
305 		return;
306 
307 	while (window->first) {
308 		event = window->first;
309 		window->first = event->next;
310 		switch (event->type) {
311 			case EVENT_NUMERIC_FIELD:
312 			case EVENT_TEXT_FIELD:
313 				if (event->previous_value.textual)
314 					free(event->previous_value.textual);
315 				event->previous_value.textual = NULL;
316 				break;
317 			default:
318 				break;
319 		}
320 		free(event);
321 	}
322 	free(window);
323 	return;
324 }
325 
326 
327 /**
328  * Free any resources associated with a specific icon in a window.
329  *
330  * \param w		The window containing the icon.
331  * \param i		The icon to free resources for.
332  */
333 
ro_gui_wimp_event_deregister(wimp_w w,wimp_i i)334 void ro_gui_wimp_event_deregister(wimp_w w, wimp_i i)
335 {
336 	struct event_window	*window;
337 	struct icon_event	*event, *parent, *child;
338 
339 	NSLOG(netsurf, INFO, "Removing all events for window 0x%x, icon %d",
340 	      (unsigned int)w, (int)i);
341 	window = ro_gui_wimp_event_get_window(w);
342 	if (!window)
343 		return;
344 
345 	/* Remove any events that apply to the given icon. */
346 
347 	event = window->first;
348 	parent = NULL;
349 
350 	while (event != NULL) {
351 		child = event->next;
352 
353 		if (event->i == i) {
354 			NSLOG(netsurf, INFO, "Removing event 0x%x",
355 			      (unsigned int)event);
356 
357 			if (parent == NULL)
358 				window->first = child;
359 			else
360 				parent->next = child;
361 
362 			switch (event->type) {
363 			case EVENT_NUMERIC_FIELD:
364 			case EVENT_TEXT_FIELD:
365 				if (event->previous_value.textual)
366 					free(event->previous_value.textual);
367 				event->previous_value.textual = NULL;
368 				break;
369 			default:
370 				break;
371 			}
372 
373 			free(event);
374 		} else {
375 			parent = event;
376 		}
377 
378 		event = child;
379 	}
380 }
381 
382 
383 /**
384  * Set the associated help prefix for a given window.
385  *
386  * \param w		the window to get the prefix for
387  * \param help_prefix	the prefix to associate with the window (used directly)
388  * \return true on success, or NULL for memory exhaustion
389  */
ro_gui_wimp_event_set_help_prefix(wimp_w w,const char * help_prefix)390 bool ro_gui_wimp_event_set_help_prefix(wimp_w w, const char *help_prefix)
391 {
392 	struct event_window *window;
393 
394 	window = ro_gui_wimp_event_get_window(w);
395 	if (!window)
396 		return false;
397 	window->help_prefix = help_prefix;
398 	return true;
399 }
400 
401 
402 /**
403  * Get the associated help prefix.
404  *
405  * \param w	the window to get the prefix for
406  * \return the associated prefix, or NULL
407  */
ro_gui_wimp_event_get_help_prefix(wimp_w w)408 const char *ro_gui_wimp_event_get_help_prefix(wimp_w w)
409 {
410 	struct event_window *window;
411 
412 	window = ro_gui_wimp_event_find_window(w);
413 	if (window)
414 		return window->help_prefix;
415 	return NULL;
416 }
417 
418 
419 /**
420  * Register a handler to decode help suffixes for a given window.
421  *
422  */
423 
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))424 bool ro_gui_wimp_event_register_help_suffix(wimp_w w,
425 		const char *(*get_help_suffix)(wimp_w w, wimp_i i,
426 			os_coord *pos, wimp_mouse_state buttons))
427 {
428 	struct event_window *window;
429 
430 	window = ro_gui_wimp_event_get_window(w);
431 	if (!window)
432 		return false;
433 	window->get_help_suffix = get_help_suffix;
434 	return true;
435 }
436 
437 
438 /**
439  * Get the associated help suffix.
440  *
441  * \param w  The window to get the suffix for
442  * \param i  The icon
443  * \param pos The os coordinates
444  * \param buttons The button state.
445  * \return The associated prefix, or NULL
446  */
447 
ro_gui_wimp_event_get_help_suffix(wimp_w w,wimp_i i,os_coord * pos,wimp_mouse_state buttons)448 const char *ro_gui_wimp_event_get_help_suffix(wimp_w w, wimp_i i,
449 		os_coord *pos, wimp_mouse_state buttons)
450 {
451 	struct event_window *window;
452 
453 	window = ro_gui_wimp_event_find_window(w);
454 	if (window == NULL || window->get_help_suffix == NULL)
455 		return NULL;
456 
457 	return window->get_help_suffix(w, i, pos, buttons);
458 }
459 
460 
461 /**
462  * Sets the user data associated with a window.
463  *
464  * \param w	the window to associate the data with
465  * \param user	the data to associate
466  */
ro_gui_wimp_event_set_user_data(wimp_w w,void * user)467 bool ro_gui_wimp_event_set_user_data(wimp_w w, void *user)
468 {
469 	struct event_window *window;
470 
471 	window = ro_gui_wimp_event_get_window(w);
472 	if (!window)
473 		return false;
474 	window->user_data = user;
475 	return true;
476 
477 }
478 
479 
480 /**
481  * Gets the user data associated with a window.
482  *
483  * \param w	the window to retrieve the data for
484  * \return the associated data, or NULL
485  */
ro_gui_wimp_event_get_user_data(wimp_w w)486 void *ro_gui_wimp_event_get_user_data(wimp_w w)
487 {
488 	struct event_window *window;
489 
490 	window = ro_gui_wimp_event_find_window(w);
491 	if (window)
492 		return window->user_data;
493 	return NULL;
494 }
495 
496 
497 /**
498  * Handles a menu selection event.
499  *
500  * (At present, this is tied to being called from menus.c and relies on that
501  * module decoding the menu into an action code.  If menus.c loses its
502  * menu handling in the future, such decoding might need to move here.)
503  *
504  * The order of execution is:
505  *
506  * 1. Try to match the menu to a pop-up menu.  If successful, handle it as
507  *    this.
508  * 2. Try to match the menu to a window menu.  If successful, pass control to
509  *    the menu's registered _select handler.
510  * 3. Return event as unhandled.
511  *
512  * \param w		the window to owning the menu
513  * \param i		the icon owning the menu
514  * \param menu		the menu that has been selected
515  * \param selection	the selection information
516  * \param action	the menu action info from menus.c
517  * \return		true if the menu is OK for an Adjust re-open;
518  *			else false.
519  */
ro_gui_wimp_event_menu_selection(wimp_w w,wimp_i i,wimp_menu * menu,wimp_selection * selection,menu_action action)520 bool ro_gui_wimp_event_menu_selection(wimp_w w, wimp_i i, wimp_menu *menu,
521 		wimp_selection *selection, menu_action action)
522 {
523 	struct event_window *window;
524 	struct icon_event *event;
525 	wimp_menu_entry *menu_entry;
526 	wimp_key key;
527 	os_error *error;
528 	wimp_caret caret;
529 	wimp_icon_state ic;
530 	unsigned int button_type;
531 	bool prepared;
532 
533 	window = ro_gui_wimp_event_find_window(w);
534 	if (window == NULL)
535 		return false;
536 
537 	/* Start by looking for an icon event that matches.  If there isn't one,
538 	 * then return details for an unconnected menu.  It's up to the
539 	 * event recipient to sort out if this is a window menu or not, based
540 	 * on the menu handle passed back.
541 	 */
542 
543 	for (event = window->first; event; event = event->next)
544 		if ((event->type == EVENT_MENU_GRIGHT) && (event->i == i))
545 			break;
546 	if (!event) {
547 		if (window->menu_selection)
548 			window->menu_selection(window->w, wimp_ICON_WINDOW,
549 					menu, selection, action);
550 
551 		/* Prepare the menu pending a possible Adjust click. */
552 		if (window->menu_prepare)
553 			if (!window->menu_prepare(window->w, wimp_ICON_WINDOW,
554 					menu, NULL))
555 				return false;
556 
557 		return true;
558 	}
559 
560 	menu_entry = &menu->entries[selection->items[0]];
561 	for (i = 1; selection->items[i] != -1; i++)
562 		menu_entry = &menu_entry->sub_menu->
563 				entries[selection->items[i]];
564 
565 	/* if the entry is already ticked then we do nothing */
566 	if (menu_entry->menu_flags & wimp_MENU_TICKED)
567 		return true;
568 
569 	ro_gui_set_icon_string(window->w, event->data.menu_gright.field,
570 			menu_entry->data.indirected_text.text, false);
571 	if (window->menu_selection)
572 		window->menu_selection(window->w, event->i, menu,
573 				selection, action);
574 	prepared = true;
575 	if (window->menu_prepare)
576 		prepared = window->menu_prepare(window->w, event->i,
577 				menu, NULL);
578 	if (prepared)
579 		ro_gui_wimp_event_prepare_gright_menu(window->w, event);
580 
581 	/* set the caret for writable icons and send a CTRL+U keypress to
582 	 * stimulate activity if needed */
583 	ic.w = window->w;
584 	ic.i = event->data.menu_gright.field;
585 	error = xwimp_get_icon_state(&ic);
586 	if (error) {
587 		NSLOG(netsurf, INFO, "xwimp_get_icon_state: 0x%x: %s",
588 		      error->errnum, error->errmess);
589 		ro_warn_user("WimpError", error->errmess);
590 		return false;
591 	}
592 	button_type = (ic.icon.flags & wimp_ICON_BUTTON_TYPE) >> wimp_ICON_BUTTON_TYPE_SHIFT;
593 	if ((button_type != wimp_BUTTON_WRITABLE) &&
594 			(button_type != wimp_BUTTON_WRITE_CLICK_DRAG))
595 		return prepared;
596 	error = xwimp_get_caret_position(&caret);
597 	if (error) {
598 		NSLOG(netsurf, INFO, "xwimp_get_caret_position: 0x%x: %s",
599 		      error->errnum, error->errmess);
600 		ro_warn_user("WimpError", error->errmess);
601 		return false;
602 	}
603 	if ((caret.w != window->w) || (caret.i != event->data.menu_gright.field)) {
604 		error = xwimp_set_caret_position(window->w, event->data.menu_gright.field,
605 				-1, -1, -1, strlen(menu_entry->data.indirected_text.text));
606 		if (error) {
607 			NSLOG(netsurf, INFO,
608 			      "xwimp_set_caret_position: 0x%x: %s",
609 			      error->errnum,
610 			      error->errmess);
611 			ro_warn_user("WimpError", error->errmess);
612 		}
613 	}
614 	if (window->keypress) {
615 		key.w = window->w;
616 		key.c = 21;	// ctrl+u
617 		window->keypress(&key);
618 	}
619 	return prepared;
620 }
621 
622 
623 /**
624  * Handles a mouse click event in a registered window.
625  *
626  * The order of execution is:
627  *
628  *  1. If a menu click, and the window has an automatic window menu, this is
629  *     processed immediately.
630  *  2. Any registered mouse_click routine (see ro_gui_wimp_register_mouse_click())
631  *  3. If the current icon is not registered with a type then it is assumed that no
632  *     action is necessary, and the click is deemed to have been handled.
633  *  4. If the registered mouse_click routine returned false, or there was no registered
634  *     routine then the automated action for the registered icon type is performed
635  *
636  * \param pointer	the current pointer state
637  * \return true if the event was handled, false otherwise
638  */
ro_gui_wimp_event_mouse_click(wimp_pointer * pointer)639 bool ro_gui_wimp_event_mouse_click(wimp_pointer *pointer)
640 {
641 	struct event_window *window;
642 	struct icon_event *event;
643 	wimp_w w;
644 	struct icon_event *search;
645 	int current, step, stepping, min, max, decimal_places;
646 	wimp_window_state open;
647 	wimp_caret caret;
648 	bool prepared;
649 
650 	w = pointer->w;
651 	window = ro_gui_wimp_event_find_window(w);
652 	if (!window)
653 		return false;
654 
655 	/* Menu clicks take priority if there is an auto menu defined. */
656 	if ((pointer->buttons == wimp_CLICK_MENU) &&
657 			(window->window_menu != NULL) &&
658 			(window->window_menu_auto)) {
659 		ro_gui_wimp_event_process_window_menu_click(pointer);
660 		return true;
661 	}
662 
663 	/* registered routines take next priority */
664 	if ((window->mouse_click) && (window->mouse_click(pointer)))
665 		return true;
666 
667 	for (event = window->first; event; event = event->next)
668 		if (event->i == pointer->i)
669 			break;
670 	if (!event)
671 		return true;
672 
673 	switch (event->type) {
674 		case EVENT_NUMERIC_FIELD:
675 		case EVENT_TEXT_FIELD:
676 			break;
677 		case EVENT_UP_ARROW:
678 		case EVENT_DOWN_ARROW:
679 			for (search = window->first; search; search = search->next)
680 				if (search->i == event->data.linked_icon) break;
681 			if (!search) {
682 				NSLOG(netsurf, INFO, "Incorrect reference.");
683 				return false;
684 			}
685 			stepping = search->data.numeric_field.stepping;
686 			min = search->data.numeric_field.min;
687 			max = search->data.numeric_field.max;
688 			decimal_places = search->data.numeric_field.decimal_places;
689 
690 			if (pointer->buttons & wimp_CLICK_ADJUST)
691 				step = -stepping;
692 			else if (pointer->buttons & wimp_CLICK_SELECT)
693 				step = stepping;
694 			else
695 				return true;
696 			if (event->type == EVENT_DOWN_ARROW)
697 				step = -step;
698 
699 			current = ro_gui_get_icon_decimal(pointer->w, event->data.linked_icon,
700 					decimal_places);
701 			current += step;
702 			if (current < min)
703 				current = min;
704 			if (current > max)
705 				current = max;
706 			ro_gui_set_icon_decimal(pointer->w, event->data.linked_icon, current,
707 					decimal_places);
708 			break;
709 		case EVENT_MENU_GRIGHT:
710 			/* if there's already a menu open then we assume that we are part of it.
711 			 * to follow the standard RISC OS behaviour we add a 'send to the back'
712 			 * button, then close the menu (which closes us) and then finally
713 			 * re-open ourselves. ugh! */
714 			if (current_menu != NULL) {
715 				os_error *error;
716 				open.w = pointer->w;
717 				error = xwimp_get_window_state(&open);
718 				if (error) {
719 					NSLOG(netsurf, INFO,
720 					      "xwimp_get_window_state: 0x%x: %s",
721 					      error->errnum,
722 					      error->errmess);
723 					ro_warn_user("WimpError", error->errmess);
724 					return false;
725 				}
726 				error = xwimp_get_caret_position(&caret);
727 				if (error) {
728 					NSLOG(netsurf, INFO,
729 					      "xwimp_get_caret_position: 0x%x: %s",
730 					      error->errnum,
731 					      error->errmess);
732 					ro_warn_user("WimpError", error->errmess);
733 					return false;
734 				}
735 				ro_gui_dialog_add_persistent(current_menu_window,
736 						pointer->w);
737 				ro_gui_menu_destroy();
738 				error = xwimp_open_window(PTR_WIMP_OPEN(&open));
739 				if (error) {
740 					NSLOG(netsurf, INFO,
741 					      "xwimp_open_window: 0x%x: %s",
742 					      error->errnum,
743 					      error->errmess);
744 					ro_warn_user("WimpError", error->errmess);
745 					return false;
746 				}
747 				if (caret.w == pointer->w) {
748 					error = xwimp_set_caret_position(caret.w,
749 							caret.i,
750 							caret.pos.x, caret.pos.y,
751 							-1, caret.index);
752 					if (error) {
753 						NSLOG(netsurf, INFO,
754 						      "xwimp_set_caret_position: 0x%x: %s",
755 						      error->errnum,
756 						      error->errmess);
757 						ro_warn_user("WimpError", error->errmess);
758 					}
759 				}
760 			}
761 			/* display the menu */
762 
763 			prepared = true;
764 			if (window->menu_prepare != NULL)
765 				prepared = window->menu_prepare(pointer->w, pointer->i,
766 						event->data.menu_gright.menu, pointer);
767 			if (prepared) {
768 				ro_gui_wimp_event_prepare_gright_menu(pointer->w, event);
769 				ro_gui_popup_menu(event->data.menu_gright.menu, pointer->w, pointer->i);
770 			}
771 			break;
772 		case EVENT_CHECKBOX:
773 			break;
774 		case EVENT_RADIO:
775 			for (search = window->first; search; search = search->next)
776 				if ((search->type == EVENT_RADIO) &&
777 						(search->data.radio_group ==
778 							event->data.radio_group))
779 					ro_gui_set_icon_selected_state(pointer->w,
780 							search->i, (search == event));
781 			break;
782 		case EVENT_BUTTON:
783 			if (event->data.callback)
784 				event->data.callback(pointer);
785 			break;
786 		case EVENT_CANCEL:
787 			if (pointer->buttons & wimp_CLICK_SELECT) {
788 				ro_gui_dialog_close(pointer->w);
789 				ro_gui_wimp_event_close_window(pointer->w);
790 				ro_gui_menu_destroy();
791 			} else {
792 				ro_gui_wimp_event_restore(pointer->w);
793 			}
794 			break;
795 		case EVENT_OK:
796 			ro_gui_wimp_event_ok_click(window, pointer->buttons);
797 			break;
798 	}
799 	return true;
800 }
801 
802 
803 /**
804  * Prepare a menu ready for use
805  *
806  * /param w	the window owning the menu
807  * /param event	the icon event owning the menu
808  */
ro_gui_wimp_event_prepare_gright_menu(wimp_w w,struct icon_event * event)809 void ro_gui_wimp_event_prepare_gright_menu(wimp_w w, struct icon_event *event)
810 {
811 	int i;
812 	const char *text;
813 	unsigned int button_type;
814 	wimp_icon_state ic;
815 	wimp_menu *menu;
816 	os_error *error;
817 
818 	/* if the linked icon is not writable then we set the ticked state
819 	 * of the menu item that matches the contents */
820 	ic.w = w;
821 	ic.i = event->data.menu_gright.field;
822 	error = xwimp_get_icon_state(&ic);
823 	if (error) {
824 		NSLOG(netsurf, INFO, "xwimp_get_icon_state: 0x%x: %s",
825 		      error->errnum, error->errmess);
826 		ro_warn_user("WimpError", error->errmess);
827 		return;
828 	}
829 	button_type = (ic.icon.flags & wimp_ICON_BUTTON_TYPE)
830 			>> wimp_ICON_BUTTON_TYPE_SHIFT;
831 	if ((button_type == wimp_BUTTON_WRITABLE) ||
832 			(button_type == wimp_BUTTON_WRITE_CLICK_DRAG))
833 		return;
834 	text = ro_gui_get_icon_string(w, event->data.menu_gright.field);
835 	menu = event->data.menu_gright.menu;
836 	i = 0;
837 	do {
838 		if (!strcmp(menu->entries[i].data.indirected_text.text, text))
839 			menu->entries[i].menu_flags |= wimp_MENU_TICKED;
840 		else
841 			menu->entries[i].menu_flags &= ~wimp_MENU_TICKED;
842 	} while (!(menu->entries[i++].menu_flags & wimp_MENU_LAST));
843 }
844 
845 
846 /**
847  * Perform the necessary actions following a click on the OK button.
848  *
849  * /param window	the window to perform the action on
850  * /param state		the mouse button state
851  */
ro_gui_wimp_event_ok_click(struct event_window * window,wimp_mouse_state state)852 void ro_gui_wimp_event_ok_click(struct event_window *window,
853 		wimp_mouse_state state)
854 {
855 	struct icon_event *search;
856 
857 	for (search = window->first; search; search = search->next)
858 		if (search->type == EVENT_OK) {
859 			if (ro_gui_get_icon_shaded_state(window->w, search->i))
860 				return;
861 			break;
862 		}
863 	ro_gui_wimp_event_validate(window->w);
864 
865 	if (window->ok_click)
866 		if (!window->ok_click(window->w))
867 			return;
868 
869 	if (state & wimp_CLICK_SELECT) {
870 		ro_gui_dialog_close(window->w);
871 		ro_gui_wimp_event_close_window(window->w);
872 		ro_gui_menu_destroy();
873 	} else {
874 		ro_gui_wimp_event_memorise(window->w);
875 	}
876 }
877 
878 
879 /**
880  * Handle any registered keypresses, and the standard RISC OS ones
881  *
882  * \param key	the key state
883  * \return true if keypress handled, false otherwise
884  */
ro_gui_wimp_event_keypress(wimp_key * key)885 bool ro_gui_wimp_event_keypress(wimp_key *key)
886 {
887 	static const int *ucstable = NULL;
888 	static int alphabet = 0;
889 	static uint32_t wc = 0;	/* buffer for UTF8 alphabet */
890 	static int shift = 0;
891 	struct event_window *window;
892 	struct icon_event *event;
893 	wimp_pointer pointer;
894 	wimp_key k;
895 	uint32_t c = (uint32_t) key->c;
896 	int t_alphabet;
897 	os_error *error;
898 
899 	window = ro_gui_wimp_event_find_window(key->w);
900 	if (!window)
901 		return false;
902 
903 	/* copy key state so we can corrupt it safely */
904 	memcpy(&k, key, sizeof(wimp_key));
905 
906 	/* In order to make sensible use of the 0x80->0xFF ranges specified
907 	 * in the RISC OS 8bit alphabets, we must do the following:
908 	 *
909 	 * + Read the currently selected alphabet
910 	 * + Acquire a pointer to the UCS conversion table for this alphabet:
911 	 *     + Try using ServiceInternational 8 to get the table
912 	 *     + If that fails, use our internal table (see ucstables.c)
913 	 * + If the alphabet is not UTF8 and the conversion table exists:
914 	 *     + Lookup UCS code in the conversion table
915 	 *     + If code is -1 (i.e. undefined):
916 	 *         + Use codepoint 0xFFFD instead
917 	 * + If the alphabet is UTF8, we must buffer input, thus:
918 	 *     + If the keycode is < 0x80:
919 	 *         + Handle it directly
920 	 *     + If the keycode is a UTF8 sequence start:
921 	 *         + Initialise the buffer appropriately
922 	 *     + Otherwise:
923 	 *         + OR in relevant bits from keycode to buffer
924 	 *         + If we've received an entire UTF8 character:
925 	 *             + Handle UCS code
926 	 * + Otherwise:
927 	 *     + Simply handle the keycode directly, as there's no easy way
928 	 *       of performing the mapping from keycode -> UCS4 codepoint.
929 	 */
930 	error = xosbyte1(osbyte_ALPHABET_NUMBER, 127, 0, &t_alphabet);
931 	if (error) {
932 		NSLOG(netsurf, INFO, "failed reading alphabet: 0x%x: %s",
933 		      error->errnum, error->errmess);
934 		/* prevent any corruption of ucstable */
935 		t_alphabet = alphabet;
936 	}
937 
938 	if (t_alphabet != alphabet) {
939 		void *ostable;
940 		osbool unclaimed;
941 		/* Alphabet has changed, so read UCS table location */
942 		alphabet = t_alphabet;
943 
944 		error = xserviceinternational_get_ucs_conversion_table(
945 						alphabet, &unclaimed, &ostable);
946 		if (error != NULL) {
947 			NSLOG(netsurf, INFO,
948 			      "failed reading UCS conversion table: 0x%x: %s",
949 			      error->errnum,
950 			      error->errmess);
951 			/* Try using our own table instead */
952 			ucstable = ucstable_from_alphabet(alphabet);
953 		} else if (unclaimed) {
954 			/* Service wasn't claimed so use our own ucstable */
955 			ucstable = ucstable_from_alphabet(alphabet);
956 		} else {
957 			/* Use the table provided by the OS */
958 			ucstable = ostable;
959 		}
960 	}
961 
962 	if (c < 256) {
963 		if (alphabet != 111 /* UTF8 */ && ucstable != NULL) {
964 			/* defined in this alphabet? */
965 			if (ucstable[c] == -1)
966 				return true;
967 
968 			/* read UCS4 value out of table */
969 			k.c = ucstable[c];
970 		}
971 		else if (alphabet == 111 /* UTF8 */) {
972 			if ((c & 0x80) == 0x00 || (c & 0xC0) == 0xC0) {
973 				/* UTF8 start sequence */
974 				if ((c & 0xE0) == 0xC0) {
975 					wc = ((c & 0x1F) << 6);
976 					shift = 1;
977 					return true;
978 				}
979 				else if ((c & 0xF0) == 0xE0) {
980 					wc = ((c & 0x0F) << 12);
981 					shift = 2;
982 					return true;
983 				}
984 				else if ((c & 0xF8) == 0xF0) {
985 					wc = ((c & 0x07) << 18);
986 					shift = 3;
987 					return true;
988 				}
989 				/* These next two have been removed
990 				 * from RFC3629, but there's no
991 				 * guarantee that RISC OS won't
992 				 * generate a UCS4 value outside the
993 				 * UTF16 plane, so we handle them
994 				 * anyway. */
995 				else if ((c & 0xFC) == 0xF8) {
996 					wc = ((c & 0x03) << 24);
997 					shift = 4;
998 				}
999 				else if ((c & 0xFE) == 0xFC) {
1000 					wc = ((c & 0x01) << 30);
1001 					shift = 5;
1002 				}
1003 				else if (c >= 0x80) {
1004 					/* If this ever happens,
1005 					 * RISC OS' UTF8 keyboard
1006 					 * drivers are broken */
1007 					NSLOG(netsurf, INFO,
1008 					      "unexpected UTF8 start"" byte %x (ignoring)",
1009 					      c);
1010 					return true;
1011 				}
1012 				/* Anything else is ASCII, so just
1013 				 * handle it directly. */
1014 			}
1015 			else {
1016 				if ((c & 0xC0) != 0x80) {
1017 					/* If this ever happens,
1018 					 * RISC OS' UTF8 keyboard
1019 					 * drivers are broken */
1020 					NSLOG(netsurf, INFO,
1021 					      "unexpected keycode: ""%x (ignoring)",
1022 					      c);
1023 					return true;
1024 				}
1025 
1026 				/* Continuation of UTF8 character */
1027 				wc |= ((c & 0x3F) << (6 * --shift));
1028 				if (shift > 0)
1029 					/* partial character */
1030 					return true;
1031 				else
1032 					/* got entire character, so
1033 					 * fetch from buffer and
1034 					 * handle it */
1035 					k.c = wc;
1036 			}
1037 		}
1038 	} else {
1039 		k.c |= IS_WIMP_KEY;
1040 	}
1041 
1042 	/* registered routines take priority */
1043 	if (window->keypress)
1044 		if (window->keypress(&k))
1045 			return true;
1046 
1047 	switch (key->c) {
1048 		/* Escape performs the CANCEL action (simulated click) */
1049 		case wimp_KEY_ESCAPE:
1050 			for (event = window->first; event; event = event->next) {
1051 				switch (event->type) {
1052 					case EVENT_CANCEL:
1053 						pointer.w = key->w;
1054 						pointer.i = event->i;
1055 						pointer.buttons = wimp_CLICK_SELECT;
1056 						ro_gui_wimp_event_mouse_click(&pointer);
1057 						return true;
1058 					default:
1059 						break;
1060 				}
1061 			}
1062 			return false;
1063 		/* CTRL+F2 closes a window with a close icon */
1064 		case wimp_KEY_CONTROL + wimp_KEY_F2:
1065 			if (!ro_gui_wimp_check_window_furniture(key->w,
1066 					wimp_WINDOW_CLOSE_ICON))
1067 				return false;
1068 			ro_gui_dialog_close(key->w);
1069 			ro_gui_wimp_event_close_window(key->w);
1070 			ro_gui_menu_destroy();
1071 			return true;
1072 		/* Return performs the OK action */
1073 		case wimp_KEY_RETURN:
1074 			if (!window->ok_click)
1075 				return false;
1076 			/* todo: check we aren't greyed out */
1077 			ro_gui_wimp_event_ok_click(window, wimp_CLICK_SELECT);
1078 			return true;
1079 	}
1080 	return false;
1081 }
1082 
1083 
1084 /**
1085  * Handle any open window requests
1086  *
1087  * \param open	the window open request
1088  */
ro_gui_wimp_event_open_window(wimp_open * open)1089 bool ro_gui_wimp_event_open_window(wimp_open *open)
1090 {
1091 	struct event_window *window;
1092 
1093 	window = ro_gui_wimp_event_find_window(open->w);
1094 	if ((window) && (window->open_window)) {
1095 		window->open_window(open);
1096 		return true;
1097 	}
1098 	return false;
1099 }
1100 
1101 
1102 /**
1103  * Service any close window handlers
1104  *
1105  * \param w	the window being closed
1106  */
ro_gui_wimp_event_close_window(wimp_w w)1107 bool ro_gui_wimp_event_close_window(wimp_w w)
1108 {
1109 	struct event_window *window;
1110 
1111 	NSLOG(netsurf, INFO, "Close event received for window 0x%x",
1112 	      (unsigned int)w);
1113 	if (w == ro_gui_wimp_event_submenu)
1114 		ro_gui_wimp_event_submenu = 0;
1115 	window = ro_gui_wimp_event_find_window(w);
1116 	if ((window) && (window->close_window)) {
1117 		window->close_window(w);
1118 		return true;
1119 	}
1120 	return false;
1121 }
1122 
1123 
1124 /**
1125  * Handle any redraw window requests
1126  *
1127  * \param redraw	the window redraw request
1128  */
ro_gui_wimp_event_redraw_window(wimp_draw * redraw)1129 bool ro_gui_wimp_event_redraw_window(wimp_draw *redraw)
1130 {
1131 	struct event_window *window;
1132 
1133 	window = ro_gui_wimp_event_find_window(redraw->w);
1134 	if ((window) && (window->redraw_window)) {
1135 		window->redraw_window(redraw);
1136 		return true;
1137 	}
1138 	return false;
1139 }
1140 
1141 
1142 /**
1143  * Handle any scroll window requests
1144  *
1145  * \param scroll	the window scroll request
1146  */
ro_gui_wimp_event_scroll_window(wimp_scroll * scroll)1147 bool ro_gui_wimp_event_scroll_window(wimp_scroll *scroll)
1148 {
1149 	struct event_window *window;
1150 
1151 	window = ro_gui_wimp_event_find_window(scroll->w);
1152 	if ((window) && (window->scroll_window)) {
1153 		window->scroll_window(scroll);
1154 		return true;
1155 	}
1156 	return false;
1157 }
1158 
1159 
1160 /**
1161  * Handle any pointer entering window requests
1162  *
1163  * \param entering	the pointer entering window request
1164  */
ro_gui_wimp_event_pointer_entering_window(wimp_entering * entering)1165 bool ro_gui_wimp_event_pointer_entering_window(wimp_entering *entering)
1166 {
1167 	struct event_window *window;
1168 
1169 	window = ro_gui_wimp_event_find_window(entering->w);
1170 	if ((window) && (window->entering_window)) {
1171 		window->entering_window(entering);
1172 		return true;
1173 	}
1174 	return false;
1175 }
1176 
1177 
1178 /**
1179  * Process a Menu click in a window, by checking for a registered window
1180  * menu and opening it if one is found.
1181  *
1182  * \param  pointer The pointer block from the mouse click event.
1183  * \return true if the click was actioned; else false.
1184  */
1185 
ro_gui_wimp_event_process_window_menu_click(wimp_pointer * pointer)1186 bool ro_gui_wimp_event_process_window_menu_click(wimp_pointer *pointer)
1187 {
1188 	struct event_window	*window;
1189 
1190 	window = ro_gui_wimp_event_find_window(pointer->w);
1191 	if ((window) && (window->window_menu)
1192 			&& (pointer->buttons == wimp_CLICK_MENU)) {
1193 		int xpos, ypos;
1194 
1195 		if (window->menu_prepare)
1196 			if (!window->menu_prepare(window->w, wimp_ICON_WINDOW,
1197 					window->window_menu, pointer))
1198 				return false;
1199 
1200 		if (window->window_menu_iconbar) {
1201 			int entry = 0;
1202 			int line_height = window->window_menu->height +
1203 					window->window_menu->gap;
1204 			int gap_height = 24; /* The fixed dotted line height */
1205 
1206 			xpos = pointer->pos.x;
1207 			ypos = 96;
1208 			do {
1209 				ypos += line_height;
1210 				if ((window->window_menu->
1211 						entries[entry].menu_flags &
1212 						wimp_MENU_SEPARATE) != 0)
1213 					ypos += gap_height;
1214 			} while ((window->window_menu->
1215 					entries[entry++].menu_flags &
1216 					wimp_MENU_LAST) == 0);
1217 		} else {
1218 			xpos = pointer->pos.x;
1219 			ypos = pointer->pos.y;
1220 		}
1221 
1222 		ro_gui_menu_create(window->window_menu, xpos, ypos, window->w);
1223 		return true;
1224 	}
1225 	return false;
1226 }
1227 
1228 
1229 /**
1230  * Trigger a window's Prepare Menu event.
1231  *
1232  * \param w			The window to use.
1233  * \param i			The icon to use.
1234  * \param *menu			The menu handle to use.
1235  * \return			true if the affected menu was prepared OK; else
1236  *				false.
1237  */
1238 
ro_gui_wimp_event_prepare_menu(wimp_w w,wimp_i i,wimp_menu * menu)1239 bool ro_gui_wimp_event_prepare_menu(wimp_w w, wimp_i i, wimp_menu *menu)
1240 {
1241 	struct event_window	*window;
1242 
1243 	window = ro_gui_wimp_event_find_window(w);
1244 	if (window == NULL)
1245 		return false;
1246 
1247 	if (window->menu_prepare)
1248 		return window->menu_prepare(w, i, menu, NULL);
1249 
1250 	/* The menu is always OK if there's no event handler. */
1251 
1252 	return true;
1253 }
1254 
1255 /**
1256  * Register a window menu to be (semi-)automatically handled.
1257  *
1258  * \param w              The window to attach the menu to.
1259  * \param m              The menu to be attached.
1260  * \param menu_auto      true if the menu should be opened autimatically on
1261  *                        Menu clicks with no task intervention; false to pass
1262  *                        clicks to the window's Mouse Event handler and leave
1263  *                        that to pass the menu click back to us for handling
1264  *                        and menu opening.
1265  * \param position_ibar	 true if the menu should open in an iconbar
1266  *				position; false to open at the pointer.
1267  * \return true if the menu was registed ok; else false.
1268  */
1269 
ro_gui_wimp_event_register_menu(wimp_w w,wimp_menu * m,bool menu_auto,bool position_ibar)1270 bool ro_gui_wimp_event_register_menu(wimp_w w, wimp_menu *m,
1271 		bool menu_auto, bool position_ibar)
1272 {
1273 	struct event_window *window;
1274 
1275 	window = ro_gui_wimp_event_get_window(w);
1276 	if (!window)
1277 		return false;
1278 	window->window_menu = m;
1279 	window->window_menu_auto = menu_auto;
1280 	window->window_menu_iconbar = position_ibar;
1281 	return true;
1282 }
1283 
1284 /**
1285  * Register a numeric field to be automatically handled
1286  */
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)1287 bool ro_gui_wimp_event_register_numeric_field(wimp_w w, wimp_i i,
1288 		wimp_i up, wimp_i down,
1289 		int min, int max, int stepping, int decimal_places)
1290 {
1291 	struct icon_event *event;
1292 
1293 	event = ro_gui_wimp_event_get_event(w, i, EVENT_NUMERIC_FIELD);
1294 	if (!event)
1295 		return false;
1296 	event->data.numeric_field.min = min;
1297 	event->data.numeric_field.max = max;
1298 	event->data.numeric_field.stepping = stepping;
1299 	event->data.numeric_field.decimal_places = decimal_places;
1300 
1301 	event = ro_gui_wimp_event_get_event(w, up, EVENT_UP_ARROW);
1302 	if (!event)
1303 		return false;
1304 	event->data.linked_icon = i;
1305 
1306 	event = ro_gui_wimp_event_get_event(w, down, EVENT_DOWN_ARROW);
1307 	if (!event)
1308 		return false;
1309 	event->data.linked_icon = i;
1310 
1311 	return true;
1312 }
1313 
1314 
1315 /**
1316  * Register a text field to be automatically handled
1317  */
ro_gui_wimp_event_register_text_field(wimp_w w,wimp_i i)1318 bool ro_gui_wimp_event_register_text_field(wimp_w w, wimp_i i) {
1319 	struct icon_event *event;
1320 
1321 	event = ro_gui_wimp_event_get_event(w, i, EVENT_TEXT_FIELD);
1322 	if (!event)
1323 		return false;
1324 	return true;
1325 }
1326 
1327 
1328 /**
1329  * Register an icon menu to be automatically handled
1330  */
ro_gui_wimp_event_register_menu_gright(wimp_w w,wimp_i i,wimp_i gright,wimp_menu * menu)1331 bool ro_gui_wimp_event_register_menu_gright(wimp_w w, wimp_i i,
1332 		wimp_i gright, wimp_menu *menu)
1333 {
1334 	struct icon_event *event;
1335 
1336 	event = ro_gui_wimp_event_get_event(w, gright, EVENT_MENU_GRIGHT);
1337 	if (!event)
1338 		return false;
1339 	event->data.menu_gright.field = i;
1340 	event->data.menu_gright.menu = menu;
1341 
1342 	return ro_gui_wimp_event_register_text_field(w, i);
1343 }
1344 
1345 
1346 /**
1347  * Register a checkbox to be automatically handled
1348  */
ro_gui_wimp_event_register_checkbox(wimp_w w,wimp_i i)1349 bool ro_gui_wimp_event_register_checkbox(wimp_w w, wimp_i i)
1350 {
1351 	struct icon_event *event;
1352 
1353 	event = ro_gui_wimp_event_get_event(w, i, EVENT_CHECKBOX);
1354 	if (!event)
1355 		return false;
1356 	return true;
1357 }
1358 
1359 
1360 /**
1361  * Register a group of radio icons to be automatically handled
1362  */
ro_gui_wimp_event_register_radio(wimp_w w,wimp_i * i)1363 bool ro_gui_wimp_event_register_radio(wimp_w w, wimp_i *i)
1364 {
1365 	struct event_window *window;
1366 
1367 	window = ro_gui_wimp_event_get_window(w);
1368 	if (!window)
1369 		return false;
1370 	window->max_radio_group++;
1371 
1372 	while (*i != -1) {
1373 		struct icon_event *event = ro_gui_wimp_event_get_event(w, *i,
1374 				EVENT_RADIO);
1375 		if (!event)
1376 			return false;
1377 		event->data.radio_group = window->max_radio_group;
1378 		i++;
1379 	}
1380 	return true;
1381 }
1382 
1383 
1384 /**
1385  * Register a function to be called when a particular button is pressed.
1386  */
ro_gui_wimp_event_register_button(wimp_w w,wimp_i i,void (* callback)(wimp_pointer * pointer))1387 bool ro_gui_wimp_event_register_button(wimp_w w, wimp_i i,
1388 		void (*callback)(wimp_pointer *pointer))
1389 {
1390 	struct icon_event *event;
1391 
1392 	event = ro_gui_wimp_event_get_event(w, i, EVENT_BUTTON);
1393 	if (!event)
1394 		return false;
1395 	event->data.callback = callback;
1396 	return true;
1397 }
1398 
1399 
1400 /**
1401  * Register a function to be called for the Cancel action on a window.
1402  */
ro_gui_wimp_event_register_cancel(wimp_w w,wimp_i i)1403 bool ro_gui_wimp_event_register_cancel(wimp_w w, wimp_i i)
1404 {
1405 	struct icon_event *event;
1406 
1407 	event = ro_gui_wimp_event_get_event(w, i, EVENT_CANCEL);
1408 	if (!event)
1409 		return false;
1410 	return true;
1411 }
1412 
1413 
1414 /**
1415  * Register a function to be called for the OK action on a window.
1416  */
ro_gui_wimp_event_register_ok(wimp_w w,wimp_i i,bool (* callback)(wimp_w w))1417 bool ro_gui_wimp_event_register_ok(wimp_w w, wimp_i i,
1418 		bool (*callback)(wimp_w w))
1419 {
1420 	struct event_window *window;
1421 	struct icon_event *event;
1422 
1423 	window = ro_gui_wimp_event_get_window(w);
1424 	if (!window)
1425 		return false;
1426 	window->ok_click = callback;
1427 
1428 	event = ro_gui_wimp_event_get_event(w, i, EVENT_OK);
1429 	if (!event)
1430 		return false;
1431 	return true;
1432 }
1433 
1434 
1435 /**
1436  * Register a function to be called for all mouse-clicks to icons
1437  * in a window that don't have registered actions.
1438  */
ro_gui_wimp_event_register_mouse_click(wimp_w w,bool (* callback)(wimp_pointer * pointer))1439 bool ro_gui_wimp_event_register_mouse_click(wimp_w w,
1440 		bool (*callback)(wimp_pointer *pointer))
1441 {
1442 	struct event_window *window;
1443 
1444 	window = ro_gui_wimp_event_get_window(w);
1445 	if (!window)
1446 		return false;
1447 	window->mouse_click = callback;
1448 	return true;
1449 }
1450 
1451 
1452 /**
1453  * Register a function to be called for all keypresses within a
1454  * particular window.
1455  *
1456  * Important: the character code passed to the callback in key->c
1457  * is UTF-32 (i.e. in the range [0, &10ffff]). WIMP keys (e.g. F1)
1458  * will have bit 31 set.
1459  *
1460  */
ro_gui_wimp_event_register_keypress(wimp_w w,bool (* callback)(wimp_key * key))1461 bool ro_gui_wimp_event_register_keypress(wimp_w w,
1462 		bool (*callback)(wimp_key *key))
1463 {
1464 	struct event_window *window;
1465 
1466 	window = ro_gui_wimp_event_get_window(w);
1467 	if (!window)
1468 		return false;
1469 	window->keypress = callback;
1470 	return true;
1471 }
1472 
1473 
1474 /**
1475  * Register a function to be called for all window opening requests.
1476  */
ro_gui_wimp_event_register_open_window(wimp_w w,void (* callback)(wimp_open * open))1477 bool ro_gui_wimp_event_register_open_window(wimp_w w,
1478 		void (*callback)(wimp_open *open))
1479 {
1480 	struct event_window *window;
1481 
1482 	window = ro_gui_wimp_event_get_window(w);
1483 	if (!window)
1484 		return false;
1485 	window->open_window = callback;
1486 	return true;
1487 }
1488 
1489 /**
1490  * Register a function to be called after the window has been closed.
1491  */
ro_gui_wimp_event_register_close_window(wimp_w w,void (* callback)(wimp_w w))1492 bool ro_gui_wimp_event_register_close_window(wimp_w w,
1493 		void (*callback)(wimp_w w))
1494 {
1495 	struct event_window *window;
1496 
1497 	window = ro_gui_wimp_event_get_window(w);
1498 	if (!window)
1499 		return false;
1500 	window->close_window = callback;
1501 	return true;
1502 }
1503 
1504 /**
1505  * Register a function to be called for all window redraw operations.
1506  */
ro_gui_wimp_event_register_redraw_window(wimp_w w,void (* callback)(wimp_draw * redraw))1507 bool ro_gui_wimp_event_register_redraw_window(wimp_w w,
1508 		void (*callback)(wimp_draw *redraw))
1509 {
1510 	struct event_window *window;
1511 
1512 	window = ro_gui_wimp_event_get_window(w);
1513 	if (!window)
1514 		return false;
1515 	window->redraw_window = callback;
1516 	return true;
1517 }
1518 
1519 /**
1520  * Register a function to be called for all window scroll requests.
1521  */
1522 
ro_gui_wimp_event_register_scroll_window(wimp_w w,void (* callback)(wimp_scroll * scroll))1523 bool ro_gui_wimp_event_register_scroll_window(wimp_w w,
1524 		void (*callback)(wimp_scroll *scroll))
1525 {
1526 	struct event_window *window;
1527 
1528 	window = ro_gui_wimp_event_get_window(w);
1529 	if (!window)
1530 		return false;
1531 	window->scroll_window = callback;
1532 	return true;
1533 }
1534 
1535 /**
1536  * Register a function to be called for all pointer entering window requests.
1537  */
1538 
ro_gui_wimp_event_register_pointer_entering_window(wimp_w w,void (* callback)(wimp_entering * entering))1539 bool ro_gui_wimp_event_register_pointer_entering_window(wimp_w w,
1540 		void (*callback)(wimp_entering *entering))
1541 {
1542 	struct event_window *window;
1543 
1544 	window = ro_gui_wimp_event_get_window(w);
1545 	if (!window)
1546 		return false;
1547 	window->entering_window = callback;
1548 	return true;
1549 }
1550 
1551 /**
1552  * Register a function to be called before a menu is (re-)opened.
1553  *
1554  * \param *w			The window for which events should be returned.
1555  * \param *callback		A function to be called beofre the menu is
1556  *				(re-)opened.
1557  * \return			true if the menu was registed ok; else false.
1558  */
ro_gui_wimp_event_register_menu_prepare(wimp_w w,bool (* callback)(wimp_w w,wimp_i i,wimp_menu * m,wimp_pointer * p))1559 bool ro_gui_wimp_event_register_menu_prepare(wimp_w w,
1560 		bool (*callback)(wimp_w w, wimp_i i, wimp_menu *m,
1561 			wimp_pointer *p))
1562 {
1563 	struct event_window *window;
1564 
1565 	window = ro_gui_wimp_event_get_window(w);
1566 	if (!window)
1567 		return false;
1568 	window->menu_prepare = callback;
1569 	return true;
1570 }
1571 
1572 
1573 /**
1574  * Register a function to be called following a menu selection.
1575  *
1576  * \param *w			The window for which events should be returned.
1577  * \param *callback		A function to be called when a selection is
1578  *				made.
1579  * \return			true if the menu was registed ok; else false.
1580  */
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))1581 bool ro_gui_wimp_event_register_menu_selection(wimp_w w,
1582 		bool (*callback)(wimp_w w, wimp_i i, wimp_menu *m,
1583 			wimp_selection *s, menu_action a))
1584 {
1585 	struct event_window *window;
1586 
1587 	window = ro_gui_wimp_event_get_window(w);
1588 	if (!window)
1589 		return false;
1590 	window->menu_selection = callback;
1591 	return true;
1592 }
1593 
1594 
1595 /**
1596  * Register a function to be called when a sub-menu warning is received.
1597  *
1598  * \param *w			The window for which events should be returned.
1599  * \param *callback		A function to be called whenever a submenu
1600  *				warning is received for the menu.
1601  * \return			true if the menu was registed ok; else false.
1602  */
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))1603 bool ro_gui_wimp_event_register_menu_warning(wimp_w w,
1604 		void (*callback)(wimp_w w, wimp_i i, wimp_menu *m,
1605 			wimp_selection *s, menu_action a))
1606 {
1607 	struct event_window *window;
1608 
1609 	window = ro_gui_wimp_event_get_window(w);
1610 	if (!window)
1611 		return false;
1612 	window->menu_warning = callback;
1613 	return true;
1614 }
1615 
1616 
1617 /**
1618  * Register a function to be called before a menu is finally closed.
1619  *
1620  * \param *w			The window for which events should be returned.
1621  * \param *callback		A function to be called when the menu is closed.
1622  * \return			true if the menu was registed ok; else false.
1623  */
ro_gui_wimp_event_register_menu_close(wimp_w w,void (* callback)(wimp_w w,wimp_i i,wimp_menu * m))1624 bool ro_gui_wimp_event_register_menu_close(wimp_w w,
1625 		void (*callback)(wimp_w w, wimp_i i, wimp_menu *m))
1626 {
1627 	struct event_window *window;
1628 
1629 	window = ro_gui_wimp_event_get_window(w);
1630 	if (!window)
1631 		return false;
1632 	window->menu_close = callback;
1633 	return true;
1634 }
1635 
1636 
1637 /**
1638  * Finds the event data associated with a given window handle, or creates a
1639  *  new one.
1640  *
1641  * \param w	the window to find data for
1642  */
ro_gui_wimp_event_get_window(wimp_w w)1643 struct event_window *ro_gui_wimp_event_get_window(wimp_w w)
1644 {
1645 	struct event_window *window;
1646 	int h;
1647 
1648 	assert((int)w != 0);
1649 	window = ro_gui_wimp_event_find_window(w);
1650 	if (window)
1651 		return window;
1652 
1653 	NSLOG(netsurf, INFO, "Creating structure for window 0x%x",
1654 	      (unsigned int)w);
1655 	window = calloc(1, sizeof(struct event_window));
1656 	if (!window)
1657 		return NULL;
1658 
1659 	h = WIN_HASH(w);
1660 	window->w = w;
1661 	window->next = ro_gui_wimp_event_windows[h];
1662 	ro_gui_wimp_event_windows[h] = window;
1663 	return window;
1664 }
1665 
1666 
1667 /**
1668  * Removes the event data associated with a given handle from the hash tables,
1669  * but does not delete it.
1670  *
1671  * \param   w  the window to be removed
1672  * \return  pointer to the event data or NULL if not found
1673  */
1674 
ro_gui_wimp_event_remove_window(wimp_w w)1675 struct event_window *ro_gui_wimp_event_remove_window(wimp_w w)
1676 {
1677 	struct event_window **prev;
1678 	int h = WIN_HASH(w);
1679 
1680 	/* search hash chain for the window */
1681 	prev = &ro_gui_wimp_event_windows[h];
1682 	while (*prev) {
1683 		struct event_window *window = *prev;
1684 
1685 		if (window->w == w) {
1686 			/* remove from chain */
1687 			*prev = window->next;
1688 			return window;
1689 		}
1690 		prev = &window->next;
1691 	}
1692 
1693 	/* not found */
1694 	return NULL;
1695 }
1696 
1697 /**
1698  * Find the event data associated with a given window handle
1699  *
1700  * \param w	the window to find data for
1701  */
ro_gui_wimp_event_find_window(wimp_w w)1702 struct event_window *ro_gui_wimp_event_find_window(wimp_w w)
1703 {
1704 	struct event_window *window;
1705 	int h = WIN_HASH(w);
1706 
1707 	/* search hash chain for window */
1708 	for (window = ro_gui_wimp_event_windows[h]; window; window = window->next) {
1709 		if (window->w == w)
1710 			return window;
1711 	}
1712 	return NULL;
1713 }
1714 
ro_gui_wimp_event_get_event(wimp_w w,wimp_i i,event_type type)1715 struct icon_event *ro_gui_wimp_event_get_event(wimp_w w, wimp_i i,
1716 		event_type type)
1717 {
1718 	struct event_window *window;
1719 	struct icon_event *event;
1720 
1721 	window = ro_gui_wimp_event_get_window(w);
1722 	if (!window)
1723 		return NULL;
1724 
1725 	for (event = window->first; event; event = event->next) {
1726 		if (event->i == i) {
1727 			event->type = type;
1728 			return event;
1729 		}
1730 	}
1731 
1732 	event = calloc(1, sizeof(struct icon_event));
1733 	if (!event)
1734 		return NULL;
1735 	event->i = i;
1736 	event->type = type;
1737 	event->next = window->first;
1738 	window->first = event;
1739 
1740 	return event;
1741 }
1742 
1743 /* Handle sumbenu warnings.  This is called from ro_gui_menu_warning(), and
1744  * returns to that function to have the submenu opened correctly.
1745  *
1746  * \param w		the window to owning the menu
1747  * \param i		the icon owning the menu
1748  * \param menu		the menu that has been selected
1749  * \param selection	the selection information
1750  * \param action	the menu action info from menus.c
1751  * \return		true if the event was handled, false otherwise
1752  */
1753 
ro_gui_wimp_event_submenu_warning(wimp_w w,wimp_i i,wimp_menu * menu,wimp_selection * selection,menu_action action)1754 bool ro_gui_wimp_event_submenu_warning(wimp_w w, wimp_i i, wimp_menu *menu,
1755 		wimp_selection *selection, menu_action action)
1756 {
1757 	struct event_window *window;
1758 	struct icon_event *event;
1759 
1760 	ro_gui_wimp_event_register_submenu(0);
1761 
1762 	/* Process the event for any window menus.  Find the window data, then
1763 	 * try and match to an icon event.  If we can, then there isn't anything
1764 	 * to do.
1765 	 */
1766 
1767 	window = ro_gui_wimp_event_find_window(w);
1768 	if (!window)
1769 		return false;
1770 
1771 	for (event = window->first; event; event = event->next)
1772 		if ((event->type == EVENT_MENU_GRIGHT) && (event->i == i))
1773 			break;
1774 	if (event) {
1775 		if (window->menu_close != NULL &&
1776 				event->type == EVENT_MENU_GRIGHT &&
1777 				event->data.menu_gright.menu == menu) {
1778 			window->menu_close(w, i, menu);
1779 			return true;
1780 		}
1781 
1782 		return false;
1783 	}
1784 
1785 	/* If the warning is for a window menu, then pass the event on to it. */
1786 
1787 	if ((window->window_menu) && (window->window_menu == menu)) {
1788 		if (window->menu_warning) {
1789 			window->menu_warning(w, wimp_ICON_WINDOW, menu,
1790 					selection, action);
1791 			return true;
1792 		}
1793 	}
1794 
1795 	return false;
1796 }
1797 
1798 /**
1799  * Handle menus being closed.  This is called from the menus modules, in
1800  * every scenario when one of our own menus is open.
1801  *
1802  * \param w		the window to owning the menu
1803  * \param i		the icon owning the menu
1804  * \param menu		the menu that has been selected
1805  */
1806 
ro_gui_wimp_event_menus_closed(wimp_w w,wimp_i i,wimp_menu * menu)1807 void ro_gui_wimp_event_menus_closed(wimp_w w, wimp_i i, wimp_menu *menu)
1808 {
1809 	struct event_window *window;
1810 	struct icon_event *event;
1811 
1812 	ro_gui_wimp_event_register_submenu(0);
1813 
1814 	/* Process the event for any window menus.  Find the window data, then
1815 	 * try and match to an icon event.  If we can, then GRight menus are
1816 	 * sent the event; otherwise, we do nothing.
1817 	 */
1818 
1819 	window = ro_gui_wimp_event_find_window(w);
1820 	if (!window)
1821 		return;
1822 
1823 	for (event = window->first; event; event = event->next)
1824 		if ((event->type == EVENT_MENU_GRIGHT) && (event->i == i))
1825 			break;
1826 	if (event) {
1827 		if (window->menu_close != NULL &&
1828 				event->type == EVENT_MENU_GRIGHT &&
1829 				event->data.menu_gright.menu == menu)
1830 			window->menu_close(w, i, menu);
1831 		return;
1832 	}
1833 
1834 	/* If the close is for a window menu, then pass the event on to it. */
1835 
1836 	if ((window->window_menu) && (window->window_menu == menu) &&
1837 			(window->menu_close))
1838 		window->menu_close(w, wimp_ICON_WINDOW, menu);
1839 }
1840 
1841 /**
1842  * Register a submenu as being opened
1843  */
ro_gui_wimp_event_register_submenu(wimp_w w)1844 void ro_gui_wimp_event_register_submenu(wimp_w w)
1845 {
1846 	if (ro_gui_wimp_event_submenu)
1847 		ro_gui_wimp_event_close_window(ro_gui_wimp_event_submenu);
1848 	ro_gui_wimp_event_submenu = w;
1849 }
1850 
1851