1 /*
2  * Copyright 2004, 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  * Window toolbars (implementation).
22  */
23 
24 #include <alloca.h>
25 #include <assert.h>
26 #include <stdio.h>
27 #include <stdbool.h>
28 #include <string.h>
29 #include "oslib/dragasprite.h"
30 #include "oslib/os.h"
31 #include "oslib/osgbpb.h"
32 #include "oslib/osfile.h"
33 #include "oslib/osfind.h"
34 #include "oslib/osspriteop.h"
35 #include "oslib/wimpspriteop.h"
36 #include "oslib/squash.h"
37 #include "oslib/wimp.h"
38 #include "oslib/wimpextend.h"
39 #include "oslib/wimpspriteop.h"
40 
41 #include "utils/log.h"
42 #include "utils/nsoption.h"
43 
44 #include "riscos/cookies.h"
45 #include "riscos/dialog.h"
46 #include "riscos/global_history.h"
47 #include "riscos/gui.h"
48 #include "riscos/gui/button_bar.h"
49 #include "riscos/gui/throbber.h"
50 #include "riscos/gui/url_bar.h"
51 #include "riscos/hotlist.h"
52 #include "riscos/menus.h"
53 #include "riscos/save.h"
54 #include "riscos/theme.h"
55 #include "riscos/toolbar.h"
56 #include "riscos/url_complete.h"
57 #include "riscos/wimp.h"
58 #include "riscos/wimp_event.h"
59 #include "riscos/wimputils.h"
60 #include "riscos/window.h"
61 
62 
63 #define TOOLBAR_WIDGET_GUTTER 8
64 #define TOOLBAR_DEFAULT_WIDTH 16384
65 
66 /* Toolbar rows used to index into the arrays of row-specific data.
67  */
68 
69 #define TOOLBAR_ROW_TOP   0
70 #define TOOLBAR_ROW_DIV1  1
71 #define TOOLBAR_ROW_EDIT  2
72 #define TOOLBAR_MAX_ROWS  3
73 
74 /* The toolbar data structure.
75  */
76 
77 struct toolbar {
78 	/** Bar details. */
79 	struct theme_descriptor		*theme;
80 	theme_style			style;
81 	toolbar_flags			flags;
82 
83 	int				current_width, current_height;
84 	int				full_width, full_height;
85 	int				clip_width, clip_height;
86 
87 	/** Toolbar and parent window handles. */
88 	wimp_w				toolbar_handle;
89 	wimp_w				parent_handle;
90 
91 	/** Row locations and sizes. */
92 	int				row_y0[TOOLBAR_MAX_ROWS];
93 	int				row_y1[TOOLBAR_MAX_ROWS];
94 
95 	/** Details for the button bar. */
96 	struct button_bar		*buttons;
97 	bool				buttons_display;
98 	os_coord			buttons_size;
99 
100 	/** Details for the URL bar. */
101 	struct url_bar			*url;
102 	bool				url_display;
103 	os_coord			url_size;
104 
105 	/** Details for the throbber. */
106 	struct throbber			*throbber;
107 	bool				throbber_display;
108 	bool				throbber_right;
109 	os_coord			throbber_size;
110 
111 	/** Client callback data. */
112 	const struct toolbar_callbacks	*callbacks;
113 	void				*client_data;
114 
115 	/** Details for the toolbar editor. */
116 	wimp_i				editor_div1;
117 	struct button_bar		*editor;
118 	os_coord			editor_size;
119 
120 	bool				editing;
121 
122 	/** Interactive help data. */
123 
124 	const char			*help_prefix;
125 
126 	/** The next bar in the toolbar list. */
127 	struct toolbar			*next;
128 };
129 
130 
131 /* Global variables for the toolbar module.
132  */
133 
134 /** The list of defined toolbars. */
135 static struct toolbar			*ro_toolbar_bars = NULL;
136 
137 /** The Toolber Menu */
138 wimp_menu				*toolbar_menu;
139 
140 
141 /*	A basic window definition for the toolbar and status bar.
142  */
143 
144 static wimp_window ro_toolbar_window = {
145 	{0, 0, 1, 1},
146 	0,
147 	0,
148 	wimp_TOP,
149 	wimp_WINDOW_NEW_FORMAT | wimp_WINDOW_MOVEABLE | wimp_WINDOW_NO_BOUNDS |
150 			wimp_WINDOW_FURNITURE_WINDOW |
151 			wimp_WINDOW_IGNORE_XEXTENT | wimp_WINDOW_IGNORE_YEXTENT,
152 	wimp_COLOUR_BLACK,
153 	wimp_COLOUR_LIGHT_GREY,
154 	wimp_COLOUR_LIGHT_GREY,
155 	wimp_COLOUR_VERY_LIGHT_GREY,
156 	wimp_COLOUR_DARK_GREY,
157 	wimp_COLOUR_MID_LIGHT_GREY,
158 	wimp_COLOUR_CREAM,
159 	wimp_WINDOW_NEVER3D | 0x16u /* RISC OS 5.03+ */,
160 	{0, 0, TOOLBAR_DEFAULT_WIDTH, 16384},
161 	0,
162 	wimp_BUTTON_DOUBLE_CLICK_DRAG << wimp_ICON_BUTTON_TYPE_SHIFT,
163 	wimpspriteop_AREA,
164 	1,
165 	1,
166 	{""},
167 	0,
168 	{ }
169 };
170 
171 static char ro_toolbar_null_string[] = "";
172 static char ro_toolbar_line_validation[] = "R2";
173 
174 /*
175  * Private function prototypes.
176  */
177 
178 static void ro_toolbar_update_current_widgets(struct toolbar *toolbar);
179 static void ro_toolbar_refresh_widget_dimensions(struct toolbar *toolbar);
180 static void ro_toolbar_reformat_widgets(struct toolbar *toolbar);
181 
182 static void ro_toolbar_redraw(wimp_draw *redraw);
183 static bool ro_toolbar_click(wimp_pointer *pointer);
184 static bool ro_toolbar_keypress(wimp_key *key);
185 static bool ro_toolbar_menu_prepare(wimp_w w, wimp_i i, wimp_menu *menu,
186 		wimp_pointer *pointer);
187 static void ro_toolbar_menu_warning(wimp_w w, wimp_i i, wimp_menu *menu,
188 		wimp_selection *selection, menu_action action);
189 static bool ro_toolbar_menu_select(wimp_w w, wimp_i i, wimp_menu *menu,
190 		wimp_selection *selection, menu_action action);
191 static const char *ro_toolbar_get_help_suffix(wimp_w w, wimp_i i, os_coord *pos,
192 		wimp_mouse_state buttons);
193 
194 static void ro_toolbar_update_buttons(struct toolbar *toolbar);
195 
196 
197 /* This is an exported interface documented in toolbar.h */
198 
ro_toolbar_init(void)199 void ro_toolbar_init(void)
200 {
201 	/* browser toolbar menu */
202 	static const struct ns_menu toolbar_definition = {
203 		"Toolbar", {
204 			{ "Toolbars", NO_ACTION, 0 },
205 			{ "Toolbars.ToolButtons", TOOLBAR_BUTTONS, 0 },
206 			{ "Toolbars.ToolAddress", TOOLBAR_ADDRESS_BAR, 0 },
207 			{ "Toolbars.ToolThrob", TOOLBAR_THROBBER, 0 },
208 			{ "EditToolbar", TOOLBAR_EDIT, 0 },
209 			{NULL, 0, 0}
210 		}
211 	};
212 	toolbar_menu = ro_gui_menu_define_menu(
213 			&toolbar_definition);
214 }
215 
216 
217 /* This is an exported interface documented in toolbar.h */
218 
ro_toolbar_create(struct theme_descriptor * descriptor,wimp_w parent,theme_style style,toolbar_flags bar_flags,const struct toolbar_callbacks * callbacks,void * client_data,const char * help)219 struct toolbar *ro_toolbar_create(struct theme_descriptor *descriptor,
220 		wimp_w parent, theme_style style, toolbar_flags bar_flags,
221 		const struct toolbar_callbacks *callbacks, void *client_data,
222 		const char *help)
223 {
224 	struct toolbar *toolbar;
225 
226 	/* Allocate memory for the bar and link it into the list of bars. */
227 
228 	toolbar = calloc(sizeof(struct toolbar), 1);
229 	if (toolbar == NULL) {
230 		NSLOG(netsurf, INFO, "No memory for malloc()");
231 		ro_warn_user("NoMemory", 0);
232 		return NULL;
233 	}
234 
235 	toolbar->next = ro_toolbar_bars;
236 	ro_toolbar_bars = toolbar;
237 
238 	/* Store the supplied settings. */
239 
240 	toolbar->flags = bar_flags;
241 	toolbar->theme = descriptor;
242 	toolbar->style = style;
243 	toolbar->parent_handle = parent;
244 	toolbar->callbacks = callbacks;
245 	toolbar->client_data = client_data;
246 
247 	/* Set up the internal widgets: initially, there are none. */
248 
249 	toolbar->buttons = NULL;
250 	toolbar->buttons_display = false;
251 
252 	toolbar->url = NULL;
253 	toolbar->url_display = false;
254 
255 	toolbar->throbber = NULL;
256 	toolbar->throbber_display = false;
257 
258 	/* Set up the bar editor. */
259 
260 	toolbar->editor = NULL;
261 	toolbar->editor_div1 = -1;
262 
263 	toolbar->editing = false;
264 
265 	toolbar->help_prefix = help;
266 
267 	return toolbar;
268 }
269 
270 
271 /* This is an exported interface documented in toolbar.h */
272 
ro_toolbar_add_buttons(struct toolbar * toolbar,const struct button_bar_buttons buttons[],char * button_order)273 bool ro_toolbar_add_buttons(struct toolbar *toolbar,
274 		const struct button_bar_buttons buttons[], char *button_order)
275 {
276 	if (toolbar == NULL)
277 		return false;
278 
279 	if (toolbar->buttons != NULL)
280 		return false;
281 
282 	toolbar->buttons = ro_gui_button_bar_create(toolbar->theme, buttons);
283 	if (toolbar->buttons != NULL) {
284 		toolbar->buttons_display = true;
285 		ro_gui_button_bar_arrange_buttons(toolbar->buttons,
286 				button_order);
287 	}
288 
289 	toolbar->editor = ro_gui_button_bar_create(toolbar->theme, buttons);
290 	if (toolbar->editor != NULL)
291 		ro_gui_button_bar_hide(toolbar->editor, !toolbar->editing);
292 
293 	if (toolbar->buttons != NULL && toolbar->editor != NULL)
294 		if (!ro_gui_button_bar_link_editor(toolbar->buttons,
295 				toolbar->editor,
296 				(void (*)(void *))
297 					ro_toolbar_update_current_widgets,
298 				toolbar))
299 			return false;
300 
301 	return (toolbar->buttons == NULL || toolbar->editor == NULL) ?
302 			false : true;
303 }
304 
305 
306 /* This is an exported interface documented in toolbar.h */
307 
ro_toolbar_add_throbber(struct toolbar * toolbar)308 bool ro_toolbar_add_throbber(struct toolbar *toolbar)
309 {
310 	if (toolbar == NULL)
311 		return false;
312 
313 	if (toolbar->throbber != NULL)
314 		return false;
315 
316 	toolbar->throbber = ro_gui_throbber_create(toolbar->theme);
317 
318 	if (toolbar->throbber != NULL)
319 		toolbar->throbber_display = true;
320 
321 	return (toolbar->throbber == NULL) ? false : true;
322 }
323 
324 
325 /* This is an exported interface documented in toolbar.h */
326 
ro_toolbar_add_url(struct toolbar * toolbar)327 bool ro_toolbar_add_url(struct toolbar *toolbar)
328 {
329 	if (toolbar == NULL)
330 		return false;
331 
332 	if (toolbar->url != NULL)
333 		return false;
334 
335 	toolbar->url = ro_gui_url_bar_create(toolbar->theme);
336 
337 	if (toolbar->url != NULL)
338 		toolbar->url_display = true;
339 
340 	return (toolbar->url == NULL) ? false : true;
341 }
342 
343 
344 /* This is an exported interface documented in toolbar.h */
345 
ro_toolbar_rebuild(struct toolbar * toolbar)346 bool ro_toolbar_rebuild(struct toolbar *toolbar)
347 {
348 	os_error		*error;
349 	wimp_icon_create	icon;
350 	wimp_w			old_window = NULL;
351 
352 	if (toolbar == NULL)
353 		return false;
354 
355 	/* Start to set up the toolbar window. */
356 
357 	ro_toolbar_window.sprite_area =
358 			ro_gui_theme_get_sprites(toolbar->theme);
359 	ro_toolbar_window.work_bg =
360 			ro_gui_theme_get_style_element(toolbar->theme,
361 			toolbar->style, THEME_ELEMENT_BACKGROUND);
362 
363 	/* Delete any existing toolbar window... */
364 
365 	if (toolbar->toolbar_handle != NULL) {
366 		old_window = toolbar->toolbar_handle;
367 		error = xwimp_delete_window(toolbar->toolbar_handle);
368 		if (error)
369 			NSLOG(netsurf, INFO, "xwimp_delete_window: 0x%x: %s",
370 			      error->errnum, error->errmess);
371 		toolbar->toolbar_handle = NULL;
372 	}
373 
374 	/* ...and create a new window. */
375 
376 	error = xwimp_create_window(&ro_toolbar_window,
377 			&toolbar->toolbar_handle);
378 	if (error) {
379 		NSLOG(netsurf, INFO, "xwimp_create_window: 0x%x: %s",
380 		      error->errnum, error->errmess);
381 		ro_warn_user("WimpError", error->errmess);
382 		return false;
383 	}
384 
385 	/* Set up the toolbar's event handlers.  Only set the user activity-
386 	 * related callbacks if the bar isn't for display purposes.  If the
387 	 * toolbar is being recreated, simply transfer the handlers across
388 	 * from the old, now-deleted window.
389 	 */
390 
391 	if (old_window == NULL) {
392 		ro_gui_wimp_event_register_redraw_window(
393 				toolbar->toolbar_handle, ro_toolbar_redraw);
394 		ro_gui_wimp_event_set_user_data(toolbar->toolbar_handle,
395 				toolbar);
396 
397 		if (!(toolbar->flags & TOOLBAR_FLAGS_DISPLAY)) {
398 			ro_gui_wimp_event_register_mouse_click(
399 					toolbar->toolbar_handle,
400 					ro_toolbar_click);
401 			ro_gui_wimp_event_register_keypress(
402 					toolbar->toolbar_handle,
403 					ro_toolbar_keypress);
404 			ro_gui_wimp_event_register_menu_prepare(
405 					toolbar->toolbar_handle,
406 					ro_toolbar_menu_prepare);
407 			ro_gui_wimp_event_register_menu_warning(
408 					toolbar->toolbar_handle,
409 					ro_toolbar_menu_warning);
410 			ro_gui_wimp_event_register_menu_selection(
411 					toolbar->toolbar_handle,
412 					ro_toolbar_menu_select);
413 			ro_gui_wimp_event_register_menu(toolbar->toolbar_handle,
414 					toolbar_menu, true, false);
415 			ro_gui_wimp_event_register_help_suffix(
416 					toolbar->toolbar_handle,
417 					ro_toolbar_get_help_suffix);
418 		}
419 	} else {
420 		ro_gui_wimp_event_transfer(old_window, toolbar->toolbar_handle);
421 	}
422 
423 	/* The help prefix changes from edit to non-edit mode. */
424 
425 	ro_gui_wimp_event_set_help_prefix(toolbar->toolbar_handle,
426 			(toolbar->editing) ?
427 				"HelpEditToolbar" : toolbar->help_prefix);
428 
429 	/* Place the widgets into the new bar, using the new theme.
430 	 *
431 	 * \TODO -- If any widgets fail to rebuild, then we currently just
432 	 *          carry on without them.  Not sure if the whole bar
433 	 *          rebuild should fail here?
434 	 */
435 
436 	if (toolbar->throbber != NULL) {
437 		if (!ro_gui_throbber_rebuild(toolbar->throbber, toolbar->theme,
438 				toolbar->style, toolbar->toolbar_handle,
439 				toolbar->editing)) {
440 			ro_gui_throbber_destroy(toolbar->throbber);
441 			toolbar->throbber = NULL;
442 		}
443 
444 		ro_gui_theme_get_throbber_data(toolbar->theme, NULL, NULL, NULL,
445 				&toolbar->throbber_right, NULL);
446 	}
447 
448 	if (toolbar->buttons != NULL) {
449 		if (!ro_gui_button_bar_rebuild(toolbar->buttons, toolbar->theme,
450 				toolbar->style, toolbar->toolbar_handle,
451 				toolbar->editing)) {
452 			ro_gui_button_bar_destroy(toolbar->buttons);
453 			toolbar->buttons = NULL;
454 		}
455 	}
456 
457 	if (toolbar->editor != NULL) {
458 		if (!ro_gui_button_bar_rebuild(toolbar->editor, toolbar->theme,
459 				toolbar->style, toolbar->toolbar_handle,
460 				toolbar->editing)) {
461 			ro_gui_button_bar_destroy(toolbar->editor);
462 			toolbar->editor = NULL;
463 		}
464 	}
465 
466 	if (toolbar->url != NULL) {
467 		if (!ro_gui_url_bar_rebuild(toolbar->url, toolbar->theme,
468 				toolbar->style, toolbar->toolbar_handle,
469 				toolbar->flags & TOOLBAR_FLAGS_DISPLAY,
470 				toolbar->editing)) {
471 			ro_gui_url_bar_destroy(toolbar->url);
472 			toolbar->url = NULL;
473 		}
474 	}
475 
476 	/* If this is an editor, add in a divider icon and the editor
477 	 * button bar.
478 	 */
479 
480 	if (toolbar->editing) {
481 		icon.w = toolbar->toolbar_handle;
482 		icon.icon.flags = wimp_ICON_TEXT | wimp_ICON_INDIRECTED |
483 				wimp_ICON_VCENTRED | wimp_ICON_BORDER |
484 				(wimp_COLOUR_BLACK <<
485 					wimp_ICON_FG_COLOUR_SHIFT) |
486 				(wimp_COLOUR_VERY_LIGHT_GREY <<
487 					wimp_ICON_BG_COLOUR_SHIFT);
488 		icon.icon.extent.x0 = 0;
489 		icon.icon.extent.x1 = 0;
490 		icon.icon.extent.y1 = 0;
491 		icon.icon.extent.y0 = 0;
492 		icon.icon.data.indirected_text.text = ro_toolbar_null_string;
493 		icon.icon.data.indirected_text.validation =
494 				ro_toolbar_line_validation;
495 		icon.icon.data.indirected_text.size = 1;
496 		error = xwimp_create_icon(&icon, &toolbar->editor_div1);
497 		if (error) {
498 			NSLOG(netsurf, INFO, "xwimp_create_icon: 0x%x: %s",
499 			      error->errnum, error->errmess);
500 			ro_warn_user("WimpError", error->errmess);
501 			toolbar->editor_div1 = -1;
502 		}
503 	}
504 
505 	/* Establish the required dimensions to fit the widgets, then
506 	 * reflow the bar contents.
507 	 */
508 
509 	ro_toolbar_refresh_widget_dimensions(toolbar);
510 
511 	ro_toolbar_process(toolbar, -1, true);
512 
513 	if (toolbar->parent_handle != NULL)
514 		ro_toolbar_attach(toolbar, toolbar->parent_handle);
515 
516 	ro_toolbar_update_buttons(toolbar);
517 
518 	return true;
519 }
520 
521 
522 /* This is an exported interface documented in toolbar.h */
523 
ro_toolbar_attach(struct toolbar * toolbar,wimp_w parent)524 bool ro_toolbar_attach(struct toolbar *toolbar, wimp_w parent)
525 {
526 	wimp_outline		outline;
527 	wimp_window_state	state;
528 	os_error		*error;
529 
530 	if (toolbar == NULL || toolbar->toolbar_handle == NULL)
531 		return false;
532 
533 	toolbar->parent_handle = parent;
534 
535 	/* Only try to attach the toolbar if there's any of it visible to
536 	 * matter.
537 	 */
538 
539 	if (toolbar->current_height > 0) {
540 		outline.w = parent;
541 		xwimp_get_window_outline(&outline);
542 		state.w = parent;
543 		xwimp_get_window_state(&state);
544 		state.w = toolbar->toolbar_handle;
545 		state.visible.x1 = outline.outline.x1 - 2;
546 		state.visible.y0 = state.visible.y1 + 2 -
547 				toolbar->current_height;
548 		state.xscroll = 0;
549 		state.yscroll = 0;
550 		error = xwimp_open_window_nested(PTR_WIMP_OPEN(&state), parent,
551 				wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
552 						<< wimp_CHILD_XORIGIN_SHIFT |
553 				wimp_CHILD_LINKS_PARENT_VISIBLE_TOP_OR_RIGHT
554 						<< wimp_CHILD_YORIGIN_SHIFT |
555 				wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
556 						<< wimp_CHILD_LS_EDGE_SHIFT |
557 				wimp_CHILD_LINKS_PARENT_VISIBLE_TOP_OR_RIGHT
558 						<< wimp_CHILD_BS_EDGE_SHIFT |
559 				wimp_CHILD_LINKS_PARENT_VISIBLE_TOP_OR_RIGHT
560 						<< wimp_CHILD_RS_EDGE_SHIFT |
561 				wimp_CHILD_LINKS_PARENT_VISIBLE_TOP_OR_RIGHT
562 						<< wimp_CHILD_TS_EDGE_SHIFT);
563 		if (error) {
564 			NSLOG(netsurf, INFO,
565 			      "xwimp_open_window_nested: 0x%x: %s",
566 			      error->errnum,
567 			      error->errmess);
568 			ro_warn_user("WimpError", error->errmess);
569 			return false;
570 		}
571 
572 		return true;
573 	}
574 
575 	error = xwimp_close_window(toolbar->toolbar_handle);
576 	if (error) {
577 		NSLOG(netsurf, INFO, "xwimp_close_window: 0x%x: %s",
578 		      error->errnum, error->errmess);
579 		ro_warn_user("WimpError", error->errmess);
580 		return false;
581 	}
582 	return true;
583 }
584 
585 
586 /* This is an exported interface documented in toolbar.h */
587 
ro_toolbar_process(struct toolbar * toolbar,int width,bool reformat)588 bool ro_toolbar_process(struct toolbar *toolbar, int width, bool reformat)
589 {
590 	os_error		*error;
591 	wimp_outline		outline;
592 	wimp_window_state	state;
593 	os_box			extent;
594 	int			old_height, old_width;
595 
596 	if (!toolbar)
597 		return false;
598 
599 	old_height = toolbar->current_height;
600 	old_width = toolbar->current_width;
601 
602 	/* Measure the parent window width if the caller has asked us to
603 	 * calculate the clip width ourselves.  Otherwise, if a clip width
604 	 * has been specified, set the clip to that.
605 	 */
606 
607 	if ((toolbar->parent_handle != NULL) && (width == -1)) {
608 		outline.w = toolbar->parent_handle;
609 		error = xwimp_get_window_outline(&outline);
610 		if (error) {
611 			NSLOG(netsurf, INFO,
612 			      "xwimp_get_window_outline: 0x%x: %s",
613 			      error->errnum,
614 			      error->errmess);
615 			ro_warn_user("WimpError", error->errmess);
616 			return false;
617 		}
618 
619 		toolbar->clip_width = outline.outline.x1 -
620 				outline.outline.x0 - 2;
621 		toolbar->current_width = toolbar->clip_width;
622 	} else if (width != -1) {
623 		toolbar->clip_width = width;
624 		toolbar->current_width = toolbar->clip_width;
625 	}
626 
627 	/* Find the parent visible height to clip our toolbar height to
628 	 */
629 
630 	if (toolbar->parent_handle != NULL) {
631 		state.w = toolbar->parent_handle;
632 		error = xwimp_get_window_state(&state);
633 		if (error) {
634 			NSLOG(netsurf, INFO,
635 			      "xwimp_get_window_state: 0x%x: %s",
636 			      error->errnum,
637 			      error->errmess);
638 			ro_warn_user("WimpError", error->errmess);
639 			return false;
640 		}
641 
642 		toolbar->clip_height = state.visible.y1 - state.visible.y0 + 2;
643 
644 		/* We can't obscure the height of the scroll bar as we
645 		 * lose the resize icon if we do.
646 		 */
647 
648 		if (toolbar->clip_height >= toolbar->full_height)
649 			toolbar->current_height = toolbar->full_height;
650 		else
651 			toolbar->current_height = toolbar->clip_height;
652 
653 		/* Resize the work area extent and update our position. */
654 
655 		if (old_height != toolbar->current_height) {
656 			extent.x0 = 0;
657 			extent.y0 = 0;
658 			extent.x1 = TOOLBAR_DEFAULT_WIDTH;
659 			extent.y1 = toolbar->current_height - 2;
660 			error = xwimp_set_extent(toolbar->toolbar_handle,
661 					&extent);
662 			if (error) {
663 				NSLOG(netsurf, INFO,
664 				      "xwimp_get_window_state: 0x%x: %s",
665 				      error->errnum,
666 				      error->errmess);
667 				ro_warn_user("WimpError", error->errmess);
668 			}
669 
670 			ro_toolbar_attach(toolbar, toolbar->parent_handle);
671 		}
672 	} else {
673 		toolbar->clip_height = toolbar->full_height;
674 		toolbar->current_height = toolbar->full_height;
675 	}
676 
677 	/* Reflow the widgets into the toolbar if the dimensions have
678 	 * changed or we have been asked to anyway. */
679 
680 	if (toolbar->current_width != old_width || reformat)
681 		ro_toolbar_reformat_widgets(toolbar);
682 
683 	return true;
684 }
685 
686 
687 /**
688  * Update the widgets currently on view in a toolbar.  This can be used
689  * generally, but is primarily offered to widgets as a way for them
690  * to force an update.
691  *
692  * \param *toolbar		The toolbar to update.
693  */
694 
ro_toolbar_update_current_widgets(struct toolbar * toolbar)695 void ro_toolbar_update_current_widgets(struct toolbar *toolbar)
696 {
697 	int		old_height;
698 
699 	if (toolbar == NULL)
700 		return;
701 
702 	old_height = toolbar->full_height;
703 
704 	ro_toolbar_refresh_widget_dimensions(toolbar);
705 	ro_toolbar_reformat_widgets(toolbar);
706 
707 	/* If the toolbar height has changed, we need to tell the client. */
708 
709 	if (toolbar->full_height != old_height)
710 		ro_toolbar_refresh(toolbar);
711 }
712 
713 
714 /**
715  * Get the minimum dimenstions required by the toolbar widgets after
716  * these have changed.  The minimum dimensions are assumed not to change
717  * unless we change theme (ie. we rebuild the bar) or we knowingly
718  * alter a widget (eg. we add or remove button-bar buttons).
719  *
720  *
721  * \param *toolbar		The toolbar to refresh.
722  */
723 
ro_toolbar_refresh_widget_dimensions(struct toolbar * toolbar)724 void ro_toolbar_refresh_widget_dimensions(struct toolbar *toolbar)
725 {
726 	int		width, height;
727 	int		row_width, row_height;
728 
729 	if (toolbar == NULL)
730 		return;
731 
732 	/* Process the toolbar editor and any associated divider rows.
733 	 */
734 
735 	if (toolbar->editor != NULL && toolbar->editing) {
736 		width = 0;
737 		height = 0;
738 		ro_gui_button_bar_get_dims(toolbar->editor, &width, &height);
739 
740 		toolbar->editor_size.x = width;
741 		toolbar->editor_size.y = height;
742 
743 		toolbar->row_y0[TOOLBAR_ROW_EDIT] = TOOLBAR_WIDGET_GUTTER;
744 		toolbar->row_y1[TOOLBAR_ROW_EDIT] = TOOLBAR_WIDGET_GUTTER
745 				+ height;
746 
747 		toolbar->row_y0[TOOLBAR_ROW_DIV1] = TOOLBAR_WIDGET_GUTTER +
748 				toolbar->row_y1[TOOLBAR_ROW_EDIT];
749 		toolbar->row_y1[TOOLBAR_ROW_DIV1] = 8 +
750 				toolbar->row_y0[TOOLBAR_ROW_DIV1];
751 	} else {
752 		toolbar->editor_size.x = 0;
753 		toolbar->editor_size.y = 0;
754 
755 		toolbar->row_y0[TOOLBAR_ROW_EDIT] = 0;
756 		toolbar->row_y1[TOOLBAR_ROW_EDIT] = 0;
757 		toolbar->row_y0[TOOLBAR_ROW_DIV1] = 0;
758 		toolbar->row_y1[TOOLBAR_ROW_DIV1] = 0;
759 	}
760 
761 	/* Process the top row icons. */
762 
763 	row_width = 0;
764 	row_height = 0;
765 
766 	/* If the editor is active, any button bar if forced into view. */
767 
768 	if (toolbar->buttons != NULL &&
769 			(toolbar->buttons_display || toolbar->editing)) {
770 		width = 0;
771 		height = 0;
772 		ro_gui_button_bar_get_dims(toolbar->buttons, &width, &height);
773 
774 		row_width += width;
775 		toolbar->buttons_size.x = width;
776 		toolbar->buttons_size.y = height;
777 
778 		if (height > row_height)
779 			row_height = height;
780 	} else {
781 		toolbar->buttons_size.x = 0;
782 		toolbar->buttons_size.y = 0;
783 	}
784 
785 	if (toolbar->url != NULL && toolbar->url_display) {
786 		width = 0;
787 		height = 0;
788 		ro_gui_url_bar_get_dims(toolbar->url, &width, &height);
789 
790 		if (row_width > 0)
791 			row_width += TOOLBAR_WIDGET_GUTTER;
792 		row_width += width;
793 
794 		toolbar->url_size.x = width;
795 		toolbar->url_size.y = height;
796 
797 		if (height > row_height)
798 			row_height = height;
799 	} else {
800 		toolbar->url_size.x = 0;
801 		toolbar->url_size.y = 0;
802 	}
803 
804 	if (toolbar->throbber != NULL && toolbar->throbber_display) {
805 		width = 0;
806 		height = 0;
807 		ro_gui_throbber_get_dims(toolbar->throbber, &width, &height);
808 
809 		if (row_width > 0)
810 			row_width += TOOLBAR_WIDGET_GUTTER;
811 		row_width += width;
812 
813 		toolbar->throbber_size.x = width;
814 		toolbar->throbber_size.y = height;
815 
816 		if (height > row_height)
817 			row_height = height;
818 	} else {
819 		toolbar->throbber_size.x = 0;
820 		toolbar->throbber_size.y = 0;
821 	}
822 
823 	if (row_height > 0) {
824 		toolbar->row_y0[TOOLBAR_ROW_TOP] = TOOLBAR_WIDGET_GUTTER +
825 				toolbar->row_y1[TOOLBAR_ROW_DIV1];
826 		toolbar->row_y1[TOOLBAR_ROW_TOP] = row_height +
827 				toolbar->row_y0[TOOLBAR_ROW_TOP];
828 	} else {
829 		toolbar->row_y0[TOOLBAR_ROW_TOP] = 0;
830 		toolbar->row_y1[TOOLBAR_ROW_TOP] = 0;
831 	}
832 
833 	/* Establish the full dimensions of the bar.
834 	 *
835 	 * \TODO -- This currently assumes an "all or nothing" approach to
836 	 *          the editor bar, and will need reworking once we have to
837 	 *          worry about tab bars.
838 	 */
839 
840 	if (toolbar->row_y1[TOOLBAR_ROW_TOP] > 0) {
841 		toolbar->full_height = toolbar->row_y1[TOOLBAR_ROW_TOP] +
842 				TOOLBAR_WIDGET_GUTTER;
843 	} else {
844 		toolbar->full_height = 0;
845 	}
846 	toolbar->full_width = 2 * TOOLBAR_WIDGET_GUTTER +
847 			((row_width > toolbar->editor_size.x) ?
848 				row_width : toolbar->editor_size.x);
849 }
850 
851 
852 /**
853  * Reformat (reflow) the widgets into the toolbar, based on the toolbar size
854  * and the previously calculated widget dimensions.
855  *
856  * \param *toolbar		The toolbar to reformat.
857  */
858 
ro_toolbar_reformat_widgets(struct toolbar * toolbar)859 void ro_toolbar_reformat_widgets(struct toolbar *toolbar)
860 {
861 	int		left_margin, right_margin;
862 
863 	left_margin = TOOLBAR_WIDGET_GUTTER;
864 	right_margin = toolbar->clip_width - TOOLBAR_WIDGET_GUTTER;
865 
866 	/* Flow the toolbar editor row, which will be a fixed with and
867 	 * may alter the right margin.
868 	 */
869 
870 	if (toolbar->editor != NULL && toolbar->editing) {
871 		if (right_margin < left_margin + toolbar->editor_size.x)
872 			right_margin = left_margin + toolbar->editor_size.x;
873 
874 		ro_gui_button_bar_set_extent(toolbar->editor,
875 				left_margin,
876 				toolbar->row_y0[TOOLBAR_ROW_EDIT],
877 				left_margin + toolbar->editor_size.x,
878 				toolbar->row_y1[TOOLBAR_ROW_EDIT]);
879 
880 		if (toolbar->editor_div1 != -1)
881 			xwimp_resize_icon(toolbar->toolbar_handle,
882 					toolbar->editor_div1, -8,
883 					toolbar->row_y0[TOOLBAR_ROW_DIV1],
884 					toolbar->clip_width + 8,
885 					toolbar->row_y1[TOOLBAR_ROW_DIV1]);
886 	}
887 
888 	/* Flow the top row. */
889 
890 	if (toolbar->throbber != NULL && toolbar->throbber_display) {
891 		if (toolbar->throbber_right) {
892 			right_margin -= (toolbar->throbber_size.x +
893 					TOOLBAR_WIDGET_GUTTER);
894 		} else {
895 			ro_gui_throbber_set_extent(toolbar->throbber,
896 					left_margin,
897 					toolbar->row_y0[TOOLBAR_ROW_TOP],
898 					left_margin + toolbar->throbber_size.x,
899 					toolbar->row_y1[TOOLBAR_ROW_TOP]);
900 			left_margin += (toolbar->throbber_size.x +
901 					TOOLBAR_WIDGET_GUTTER);
902 		}
903 	}
904 
905 	if (toolbar->buttons != NULL &&
906 			(toolbar->buttons_display || toolbar->editing)) {
907 		if (right_margin < left_margin + toolbar->buttons_size.x)
908 			right_margin = left_margin + toolbar->buttons_size.x;
909 
910 		ro_gui_button_bar_set_extent(toolbar->buttons,
911 				left_margin,
912 				toolbar->row_y0[TOOLBAR_ROW_TOP],
913 				left_margin + toolbar->buttons_size.x,
914 				toolbar->row_y1[TOOLBAR_ROW_TOP]);
915 		left_margin += (toolbar->buttons_size.x +
916 				TOOLBAR_WIDGET_GUTTER);
917 	}
918 
919 	if (toolbar->url != NULL && toolbar->url_display) {
920 		if (right_margin < left_margin + toolbar->url_size.x)
921 			right_margin = left_margin + toolbar->url_size.x;
922 
923 		ro_gui_url_bar_set_extent(toolbar->url,
924 				left_margin,
925 				toolbar->row_y0[TOOLBAR_ROW_TOP],
926 				right_margin,
927 				toolbar->row_y1[TOOLBAR_ROW_TOP]);
928 
929 		left_margin = right_margin + TOOLBAR_WIDGET_GUTTER;
930 	}
931 
932 	if (toolbar->throbber != NULL && toolbar->throbber_display &&
933 			toolbar->throbber_right) {
934 		left_margin = right_margin + TOOLBAR_WIDGET_GUTTER;
935 		ro_gui_throbber_set_extent(toolbar->throbber,
936 				left_margin,
937 				toolbar->row_y0[TOOLBAR_ROW_TOP],
938 				left_margin + toolbar->throbber_size.x,
939 				toolbar->row_y1[TOOLBAR_ROW_TOP]);
940 	}
941 
942 }
943 
944 
945 /* This is an exported interface documented in toolbar.h */
946 
ro_toolbar_destroy(struct toolbar * toolbar)947 void ro_toolbar_destroy(struct toolbar *toolbar)
948 {
949 	struct toolbar *bar;
950 
951 	if (toolbar == NULL)
952 		return;
953 
954 	NSLOG(netsurf, INFO, "Destroying toolbar 0x%x", (unsigned int)toolbar);
955 
956 	/* Destroy the widgets. */
957 
958 	if (toolbar->buttons != NULL)
959 		ro_gui_button_bar_destroy(toolbar->buttons);
960 
961 	if (toolbar->editor != NULL)
962 		ro_gui_button_bar_destroy(toolbar->editor);
963 
964 	if (toolbar->url != NULL)
965 		ro_gui_url_bar_destroy(toolbar->url);
966 
967 	if (toolbar->throbber != NULL)
968 		ro_gui_throbber_destroy(toolbar->throbber);
969 
970 	/* Delete the toolbar window. */
971 
972 	if (toolbar->toolbar_handle != NULL) {
973 		xwimp_delete_window(toolbar->toolbar_handle);
974 		ro_gui_wimp_event_finalise(toolbar->toolbar_handle);
975 	}
976 
977 	/* Remove the bar from the list and free the memory.
978 	 */
979 
980 	if (ro_toolbar_bars == toolbar) {
981 		ro_toolbar_bars = toolbar->next;
982 	} else {
983 		for (bar = ro_toolbar_bars; bar != NULL && bar->next != toolbar;
984 				bar = bar->next);
985 
986 		if (bar->next == toolbar)
987 			bar->next = toolbar->next;
988 	}
989 
990 	free(toolbar);
991 
992 }
993 
994 
995 /**
996  * Handle redraw request events for a toolbar workarea.
997  *
998  * \param *redraw		The redraw block for the event.
999  */
1000 
ro_toolbar_redraw(wimp_draw * redraw)1001 void ro_toolbar_redraw(wimp_draw *redraw)
1002 {
1003 	struct toolbar *toolbar;
1004 	osbool more;
1005 	os_error *error;
1006 
1007 	toolbar = (struct toolbar *)ro_gui_wimp_event_get_user_data(redraw->w);
1008 
1009 	assert(toolbar != NULL);
1010 
1011 	error = xwimp_redraw_window(redraw, &more);
1012 	if (error) {
1013 		NSLOG(netsurf, INFO, "xwimp_redraw_window: 0x%x: %s",
1014 		      error->errnum, error->errmess);
1015 		ro_warn_user("WimpError", error->errmess);
1016 		return;
1017 	}
1018 	while (more) {
1019 		ro_plot_origin_x = redraw->box.x0 - redraw->xscroll;
1020 		ro_plot_origin_y = redraw->box.y1 - redraw->yscroll;
1021 
1022 		if (toolbar->buttons != NULL && toolbar->buttons_display)
1023 			ro_gui_button_bar_redraw(toolbar->buttons, redraw);
1024 
1025 		if (toolbar->editor != NULL && toolbar->editing)
1026 			ro_gui_button_bar_redraw(toolbar->editor, redraw);
1027 
1028 		if (toolbar->url != NULL && toolbar->url_display)
1029 			ro_gui_url_bar_redraw(toolbar->url, redraw);
1030 
1031 		error = xwimp_get_rectangle(redraw, &more);
1032 		if (error) {
1033 			NSLOG(netsurf, INFO, "xwimp_get_rectangle: 0x%x: %s",
1034 			      error->errnum, error->errmess);
1035 			ro_warn_user("WimpError", error->errmess);
1036 			return;
1037 		}
1038 	}
1039 }
1040 
1041 
1042 /**
1043  * Process clicks on a toolbar, passing details on to clients where necessary.
1044  *
1045  * \param *pointer		The wimp mouse click event.
1046  * \return			True if the event was handled; else false.
1047  */
1048 
ro_toolbar_click(wimp_pointer * pointer)1049 bool ro_toolbar_click(wimp_pointer *pointer)
1050 {
1051 	struct toolbar		*toolbar;
1052 	union toolbar_action	action;
1053 	wimp_window_state	state;
1054 	os_error		*error;
1055 
1056 	toolbar = (struct toolbar *)
1057 			ro_gui_wimp_event_get_user_data(pointer->w);
1058 
1059 	if (toolbar == NULL)
1060 		return false;
1061 
1062 	assert(pointer->w == toolbar->toolbar_handle);
1063 
1064 	state.w = toolbar->toolbar_handle;
1065 	error = xwimp_get_window_state(&state);
1066 	if (error) {
1067 		NSLOG(netsurf, INFO, "xwimp_get_window_state: 0x%x: %s",
1068 		      error->errnum, error->errmess);
1069 		ro_warn_user("WimpError", error->errmess);
1070 		return false;
1071 	}
1072 
1073 	/* If the click wasn't in the URL Bar's text field, then it will
1074 	 * need to close any URL Complete window that is open.
1075 	 *
1076 	 * \TODO -- This should really move into the URL Bar module, as
1077 	 *          URL Complete is really an extension to that.
1078 	 */
1079 
1080 	if (toolbar->url != NULL && toolbar->url_display &&
1081 			!ro_gui_url_bar_test_for_text_field_click(toolbar->url,
1082 				pointer))
1083 		ro_gui_url_complete_close();
1084 
1085 	/* Pass the click around the toolbar widgets. */
1086 
1087 	if (toolbar->buttons != NULL &&
1088 			(toolbar->buttons_display || toolbar->editing) &&
1089 			ro_gui_button_bar_click(toolbar->buttons, pointer,
1090 				&state, &action.button)) {
1091 		if (action.button != TOOLBAR_BUTTON_NONE &&
1092 				!toolbar->editing &&
1093 				toolbar->callbacks != NULL &&
1094 				toolbar->callbacks->user_action != NULL)
1095 			toolbar->callbacks->user_action(toolbar->client_data,
1096 					TOOLBAR_ACTION_BUTTON, action);
1097 		return true;
1098 	}
1099 
1100 	if (toolbar->url != NULL && toolbar->url_display &&
1101 			ro_gui_url_bar_click(toolbar->url, pointer,
1102 				&state, &action.url)) {
1103 		if (action.url != TOOLBAR_URL_NONE &&
1104 				!toolbar->editing &&
1105 				toolbar->callbacks != NULL &&
1106 				toolbar->callbacks->user_action != NULL)
1107 			toolbar->callbacks->user_action(toolbar->client_data,
1108 					TOOLBAR_ACTION_URL, action);
1109 		return true;
1110 	}
1111 
1112 	if (toolbar->editor != NULL && toolbar->editing &&
1113 			ro_gui_button_bar_click(toolbar->editor, pointer,
1114 				&state, &action.button)) {
1115 		return true;
1116 	}
1117 
1118 	/* Nothing else has handled this, so try passing it to the
1119 	 * URL Complete module.
1120 	 *
1121 	 * \TODO -- This should really move into the URL Bar module, as
1122 	 *          URL Complete is really an extension to that.
1123 	 */
1124 
1125 	if (toolbar->url != NULL && toolbar->url_display &&
1126 			ro_gui_url_bar_test_for_text_field_click(toolbar->url,
1127 				pointer)) {
1128 		ro_gui_url_complete_start(toolbar);
1129 		return true;
1130 	}
1131 
1132 	return false;
1133 }
1134 
1135 
1136 /**
1137  * Process keypresses in a toolbar, passing details on to clients where
1138  * necessary.
1139  *
1140  * \param *key			The wimp key press event.
1141  * \return			True if the event was handled; else false.
1142  */
1143 
ro_toolbar_keypress(wimp_key * key)1144 bool ro_toolbar_keypress(wimp_key *key)
1145 {
1146 	struct toolbar		*toolbar;
1147 
1148 	toolbar = (struct toolbar *) ro_gui_wimp_event_get_user_data(key->w);
1149 
1150 	if (toolbar == NULL)
1151 		return false;
1152 
1153 	/* Pass the keypress on to the client and stop if they handle it. */
1154 
1155 	if (toolbar->callbacks->key_press != NULL &&
1156 			toolbar->callbacks->key_press(toolbar->client_data, key))
1157 		return true;
1158 
1159 	/* If the caret is in the URL bar, ask the URL Complete module if it
1160 	 * wants to handle the keypress.
1161 	 *
1162 	 * \TODO -- This should really move into the URL Bar module, as
1163 	 *          URL Complete is really an extension to that.
1164 	 */
1165 
1166 	if (toolbar->url != NULL && toolbar->url_display &&
1167 			ro_gui_url_bar_test_for_text_field_keypress(
1168 				toolbar->url, key) &&
1169 			ro_gui_url_complete_keypress(toolbar, key->c))
1170 		return true;
1171 
1172 	return false;
1173 }
1174 
1175 
1176 /**
1177  * Prepare the toolbar menu for (re-)opening
1178  *
1179  * \param  w			The window owning the menu.
1180  * \param  i			The icon owning the menu.
1181  * \param  *menu		The menu about to be opened.
1182  * \param  *pointer		Pointer to the relevant wimp event block, or
1183  *				NULL for an Adjust click.
1184  * \return			true if the event was handled; else false.
1185  */
1186 
ro_toolbar_menu_prepare(wimp_w w,wimp_i i,wimp_menu * menu,wimp_pointer * pointer)1187 bool ro_toolbar_menu_prepare(wimp_w w, wimp_i i, wimp_menu *menu,
1188 		wimp_pointer *pointer)
1189 {
1190 	struct toolbar		*toolbar;
1191 
1192 	toolbar = (struct toolbar *) ro_gui_wimp_event_get_user_data(w);
1193 
1194 	if (toolbar == NULL)
1195 		return false;
1196 
1197 	/* Pass the event on to potentially interested widgets. */
1198 
1199 	if (toolbar->url != NULL && ro_gui_url_bar_menu_prepare(toolbar->url,
1200 			i, menu, pointer))
1201 		return true;
1202 
1203 	/* Try to process the event as a toolbar menu. */
1204 
1205 	if (menu != toolbar_menu)
1206 		return false;
1207 
1208 	/* Shade menu entries according to the state of the window and object
1209 	 * under the pointer.
1210 	 */
1211 
1212 	/* Toolbar (Sub)Menu */
1213 
1214 	ro_gui_menu_set_entry_shaded(menu, TOOLBAR_EDIT,
1215 			ro_toolbar_menu_edit_shade(toolbar));
1216 	ro_gui_menu_set_entry_ticked(menu, TOOLBAR_EDIT,
1217 			ro_toolbar_menu_edit_tick(toolbar));
1218 
1219 	ro_gui_menu_set_entry_shaded(menu, TOOLBAR_BUTTONS,
1220 			ro_toolbar_menu_option_shade(toolbar) ||
1221 			toolbar->buttons == NULL);
1222 	ro_gui_menu_set_entry_ticked(menu, TOOLBAR_BUTTONS,
1223 			ro_toolbar_menu_buttons_tick(toolbar) &&
1224 			toolbar->buttons != NULL);
1225 
1226 	ro_gui_menu_set_entry_shaded(menu, TOOLBAR_ADDRESS_BAR,
1227 			ro_toolbar_menu_edit_shade(toolbar) ||
1228 			toolbar->url == NULL);
1229 	ro_gui_menu_set_entry_ticked(menu, TOOLBAR_ADDRESS_BAR,
1230 			ro_toolbar_menu_url_tick(toolbar) &&
1231 			toolbar->url != NULL);
1232 
1233 	ro_gui_menu_set_entry_shaded(menu, TOOLBAR_THROBBER,
1234 			ro_toolbar_menu_edit_shade(toolbar) ||
1235 			toolbar->throbber == NULL);
1236 	ro_gui_menu_set_entry_ticked(menu, TOOLBAR_THROBBER,
1237 			ro_toolbar_menu_throbber_tick(toolbar) &&
1238 			toolbar->throbber != NULL);
1239 
1240 	return true;
1241 }
1242 
1243 
1244 /**
1245  * Handle submenu warnings for the toolbar menu
1246  *
1247  * \param  w			The window owning the menu.
1248  * \param  i			The icon owning the menu.
1249  * \param  *menu		The menu to which the warning applies.
1250  * \param  *selection		The wimp menu selection data.
1251  * \param  action		The selected menu action.
1252  */
1253 
ro_toolbar_menu_warning(wimp_w w,wimp_i i,wimp_menu * menu,wimp_selection * selection,menu_action action)1254 void ro_toolbar_menu_warning(wimp_w w, wimp_i i, wimp_menu *menu,
1255 		wimp_selection *selection, menu_action action)
1256 {
1257 	/* Do nothing */
1258 }
1259 
1260 
1261 /**
1262  * Handle selections from the toolbar menu
1263  *
1264  * \param  w			The window owning the menu.
1265  * \param  i			The icon owning the menu.
1266  * \param  *menu		The menu from which the selection was made.
1267  * \param  *selection		The wimp menu selection data.
1268  * \param  action		The selected menu action.
1269  * \return			true if action accepted; else false.
1270  */
1271 
ro_toolbar_menu_select(wimp_w w,wimp_i i,wimp_menu * menu,wimp_selection * selection,menu_action action)1272 bool ro_toolbar_menu_select(wimp_w w, wimp_i i, wimp_menu *menu,
1273 		wimp_selection *selection, menu_action action)
1274 {
1275 	struct toolbar		*toolbar;
1276 
1277 	toolbar = (struct toolbar *) ro_gui_wimp_event_get_user_data(w);
1278 
1279 	if (toolbar == NULL)
1280 		return false;
1281 
1282 	/* Pass the event on to potentially interested widgets. */
1283 
1284 	if (toolbar->url != NULL && ro_gui_url_bar_menu_select(toolbar->url,
1285 			i, menu, selection, action))
1286 		return true;
1287 
1288 	/* Try to process the event as a toolbar menu. */
1289 
1290 	if (menu != toolbar_menu)
1291 		return false;
1292 
1293 	switch (action) {
1294 	case TOOLBAR_BUTTONS:
1295 		ro_toolbar_set_display_buttons(toolbar,
1296 				!ro_toolbar_get_display_buttons(toolbar));
1297 		break;
1298 	case TOOLBAR_ADDRESS_BAR:
1299 		ro_toolbar_set_display_url(toolbar,
1300 				!ro_toolbar_get_display_url(toolbar));
1301 		if (ro_toolbar_get_display_url(toolbar))
1302 			ro_toolbar_take_caret(toolbar);
1303 		break;
1304 	case TOOLBAR_THROBBER:
1305 		ro_toolbar_set_display_throbber(toolbar,
1306 				!ro_toolbar_get_display_throbber(toolbar));
1307 		break;
1308 	case TOOLBAR_EDIT:
1309 		ro_toolbar_toggle_edit(toolbar);
1310 		break;
1311 	default:
1312 		return false;
1313 	}
1314 
1315 	return true;
1316 }
1317 
1318 
1319 /**
1320  * Translate the contents of a message_HELP_REQUEST into a suffix for a
1321  * NetSurf message token.  The help system will then add this to whatever
1322  * prefix the current toolbar has registered with WimpEvent.
1323  *
1324  * \param w			The window handle under the mouse.
1325  * \param i			The icon handle under the mouse.
1326  * \param *pos			The mouse position.
1327  * \param buttons		The mouse button state.
1328  * \return			The required help token suffix.
1329  */
1330 
ro_toolbar_get_help_suffix(wimp_w w,wimp_i i,os_coord * pos,wimp_mouse_state buttons)1331 const char *ro_toolbar_get_help_suffix(wimp_w w, wimp_i i, os_coord *pos,
1332 		wimp_mouse_state buttons)
1333 {
1334 	struct toolbar		*toolbar;
1335 	wimp_window_state	state;
1336 	os_error		*error;
1337 	const char		*suffix;
1338 
1339 	toolbar = (struct toolbar *) ro_gui_wimp_event_get_user_data(w);
1340 
1341 	if (toolbar == NULL || toolbar->toolbar_handle != w)
1342 		return NULL;
1343 
1344 	state.w = toolbar->toolbar_handle;
1345 	error = xwimp_get_window_state(&state);
1346 	if (error) {
1347 		NSLOG(netsurf, INFO, "xwimp_get_window_state: 0x%x: %s",
1348 		      error->errnum, error->errmess);
1349 		ro_warn_user("WimpError", error->errmess);
1350 		return NULL;
1351 	}
1352 
1353 	/* Pass the help request around the toolbar widgets. */
1354 
1355 	if (toolbar->throbber != NULL && toolbar->throbber_display &&
1356 			ro_gui_throbber_help_suffix(toolbar->throbber, i,
1357 			pos, &state, buttons, &suffix))
1358 		return suffix;
1359 
1360 	if (toolbar->url != NULL && toolbar->url_display &&
1361 			ro_gui_url_bar_help_suffix(toolbar->url, i,
1362 			pos, &state, buttons, &suffix))
1363 		return suffix;
1364 
1365 	if (toolbar->buttons != NULL && toolbar->buttons_display &&
1366 			ro_gui_button_bar_help_suffix(toolbar->buttons, i,
1367 			pos, &state, buttons, &suffix))
1368 		return suffix;
1369 
1370 	return "";
1371 }
1372 
1373 
1374 /* This is an exported interface documented in toolbar.h */
1375 
ro_toolbar_update_client_data(struct toolbar * toolbar,void * client_data)1376 void ro_toolbar_update_client_data(struct toolbar *toolbar, void *client_data)
1377 {
1378 	if (toolbar != NULL)
1379 		toolbar->client_data = client_data;
1380 }
1381 
1382 
1383 /* This is an exported interface documented in toolbar.h */
1384 
ro_toolbar_update_all_buttons(void)1385 void ro_toolbar_update_all_buttons(void)
1386 {
1387 	struct toolbar *bar;
1388 
1389 	bar = ro_toolbar_bars;
1390 	while (bar != NULL) {
1391 		ro_toolbar_update_buttons(bar);
1392 
1393 		bar = bar->next;
1394 	}
1395 }
1396 
1397 
1398 /**
1399  * Update the state of a toolbar's buttons.
1400  *
1401  * \param toolbar  the toolbar to update
1402  */
1403 
ro_toolbar_update_buttons(struct toolbar * toolbar)1404 void ro_toolbar_update_buttons(struct toolbar *toolbar)
1405 {
1406 	assert(toolbar != NULL);
1407 
1408 	if (toolbar->callbacks != NULL &&
1409 			toolbar->callbacks->update_buttons != NULL)
1410 		toolbar->callbacks->update_buttons(toolbar->client_data);
1411 }
1412 
1413 /* This is an exported interface documented in toolbar.h */
1414 
ro_toolbar_refresh(struct toolbar * toolbar)1415 void ro_toolbar_refresh(struct toolbar *toolbar)
1416 {
1417 	assert(toolbar != NULL);
1418 
1419 	ro_toolbar_process(toolbar, -1, true);
1420 	if (toolbar->callbacks != NULL &&
1421 			toolbar->callbacks->change_size != NULL)
1422 		toolbar->callbacks->change_size(toolbar->client_data);
1423 
1424 	if (toolbar->toolbar_handle != NULL)
1425 		xwimp_force_redraw(toolbar->toolbar_handle, 0, 0,
1426 				toolbar->current_width,
1427 				toolbar->current_height);
1428 }
1429 
1430 
1431 /* This is an exported interface documented in toolbar.h */
1432 
ro_toolbar_theme_update(void)1433 void ro_toolbar_theme_update(void)
1434 {
1435 	struct toolbar	*bar, *next;
1436 	bool		ok;
1437 
1438 	bar = ro_toolbar_bars;
1439 	while (bar != NULL) {
1440 		/* Take the next bar address now, as *bar may become invalid
1441 		 * during the update process (if an update fails and
1442 		 * ro_toolbar_destroy() is called) and we don't want to lose
1443 		 * the link to the rest of the chain.
1444 		 */
1445 
1446 		next = bar->next;
1447 
1448 		/* Only process the bar if the theme is set to the default.
1449 		 * Otherwise, it's up to the owner to do whatever they need
1450 		 * to do for themselves.
1451 		 */
1452 
1453 		if (bar->theme == NULL) {
1454 			ok = ro_toolbar_rebuild(bar);
1455 
1456 			if (!ok)
1457 				ro_toolbar_destroy(bar);
1458 		} else {
1459 			ok = true;
1460 		}
1461 
1462 		if (bar->callbacks != NULL &&
1463 				bar->callbacks->theme_update != NULL)
1464 			bar->callbacks->theme_update(bar->client_data, ok);
1465 
1466 		bar = next;
1467 	}
1468 }
1469 
1470 
1471 /* This is an exported interface documented in toolbar.h */
1472 
ro_toolbar_parent_window_lookup(wimp_w w)1473 struct toolbar *ro_toolbar_parent_window_lookup(wimp_w w)
1474 {
1475 	struct toolbar *toolbar;
1476 
1477 	toolbar = ro_toolbar_bars;
1478 	while (toolbar != NULL && toolbar->parent_handle != w)
1479 		toolbar = toolbar->next;
1480 
1481 	return toolbar;
1482 }
1483 
1484 
1485 /* This is an exported interface documented in toolbar.h */
1486 
ro_toolbar_window_lookup(wimp_w w)1487 struct toolbar *ro_toolbar_window_lookup(wimp_w w)
1488 {
1489 	struct toolbar *toolbar;
1490 
1491 	toolbar = ro_toolbar_bars;
1492 	while (toolbar != NULL && toolbar->toolbar_handle != w)
1493 		toolbar = toolbar->next;
1494 
1495 	return toolbar;
1496 }
1497 
1498 
1499 /* This is an exported interface documented in toolbar.h */
1500 
ro_toolbar_get_parent_window(struct toolbar * toolbar)1501 wimp_w ro_toolbar_get_parent_window(struct toolbar *toolbar)
1502 {
1503 	return (toolbar != NULL) ? toolbar->parent_handle : 0;
1504 }
1505 
1506 
1507 /* This is an exported interface documented in toolbar.h */
1508 
ro_toolbar_get_window(struct toolbar * toolbar)1509 wimp_w ro_toolbar_get_window(struct toolbar *toolbar)
1510 {
1511 	return (toolbar != NULL) ? toolbar->toolbar_handle : 0;
1512 }
1513 
1514 
1515 /* This is an exported interface documented in toolbar.h */
1516 
ro_toolbar_height(struct toolbar * toolbar)1517 int ro_toolbar_height(struct toolbar *toolbar)
1518 {
1519 	return (toolbar == NULL) ? 0 : toolbar->current_height;
1520 }
1521 
1522 
1523 /* This is an exported interface documented in toolbar.h */
1524 
ro_toolbar_full_height(struct toolbar * toolbar)1525 int ro_toolbar_full_height(struct toolbar *toolbar)
1526 {
1527 	return (toolbar == NULL) ? 0 : toolbar->full_height;
1528 }
1529 
1530 
1531 /* This is an exported interface documented in toolbar.h */
1532 
ro_toolbar_start_throbbing(struct toolbar * toolbar)1533 void ro_toolbar_start_throbbing(struct toolbar *toolbar)
1534 {
1535 	if (toolbar != NULL && toolbar->throbber != NULL)
1536 		ro_gui_throbber_animate(toolbar->throbber);
1537 }
1538 
1539 
1540 /* This is an exported interface documented in toolbar.h */
ro_toolbar_stop_throbbing(struct toolbar * toolbar)1541 void ro_toolbar_stop_throbbing(struct toolbar *toolbar)
1542 {
1543 	if (toolbar != NULL && toolbar->throbber != NULL)
1544 		ro_gui_throbber_stop(toolbar->throbber);
1545 }
1546 
1547 
1548 /* This is an exported interface documented in toolbar.h */
ro_toolbar_page_info_change(struct toolbar * toolbar)1549 void ro_toolbar_page_info_change(struct toolbar *toolbar)
1550 {
1551 	if (toolbar == NULL || toolbar->url == NULL)
1552 		return;
1553 
1554 	ro_gui_url_bar_page_info_change(toolbar->url);
1555 }
1556 
1557 
1558 /* This is an exported interface documented in toolbar.h */
ro_toolbar_throb(struct toolbar * toolbar)1559 void ro_toolbar_throb(struct toolbar *toolbar)
1560 {
1561 	if (toolbar != NULL && toolbar->throbber != NULL)
1562 		ro_gui_throbber_animate(toolbar->throbber);
1563 }
1564 
1565 
1566 /* This is an exported interface documented in toolbar.h */
1567 
ro_toolbar_set_button_order(struct toolbar * toolbar,char order[])1568 bool ro_toolbar_set_button_order(struct toolbar *toolbar, char order[])
1569 {
1570 	if (toolbar == NULL || toolbar->buttons == NULL)
1571 		return false;
1572 
1573 	if (!ro_gui_button_bar_arrange_buttons(toolbar->buttons, order))
1574 		return false;
1575 
1576 	ro_toolbar_refresh_widget_dimensions(toolbar);
1577 
1578 	return ro_toolbar_process(toolbar, -1, true);
1579 }
1580 
1581 
1582 /* This is an exported interface documented in toolbar.h */
1583 
ro_toolbar_set_button_shaded_state(struct toolbar * toolbar,button_bar_action action,bool shaded)1584 void ro_toolbar_set_button_shaded_state(struct toolbar *toolbar,
1585 		button_bar_action action, bool shaded)
1586 {
1587 	if (toolbar == NULL || toolbar->buttons == NULL)
1588 		return;
1589 
1590 	ro_gui_button_bar_shade_button(toolbar->buttons, action, shaded);
1591 }
1592 
1593 
1594 /* This is an exported interface documented in toolbar.h */
1595 
ro_toolbar_take_caret(struct toolbar * toolbar)1596 bool ro_toolbar_take_caret(struct toolbar *toolbar)
1597 {
1598 	if (toolbar == NULL || toolbar->url == NULL || !toolbar->url_display)
1599 		return false;
1600 
1601 	return ro_gui_url_bar_take_caret(toolbar->url);
1602 }
1603 
1604 
1605 /* This is an exported interface documented in toolbar.h */
1606 
ro_toolbar_set_url(struct toolbar * toolbar,const char * url,bool is_utf8,bool set_caret)1607 void ro_toolbar_set_url(struct toolbar *toolbar, const char *url,
1608 		bool is_utf8, bool set_caret)
1609 {
1610 	if (toolbar != NULL && toolbar->url != NULL)
1611 		ro_gui_url_bar_set_url(toolbar->url, url, is_utf8, set_caret);
1612 }
1613 
1614 
1615 /* This is an exported interface documented in toolbar.h */
1616 
ro_toolbar_get_url(struct toolbar * toolbar)1617 const char *ro_toolbar_get_url(struct toolbar *toolbar)
1618 {
1619 	if (toolbar == NULL || toolbar->url == NULL)
1620 		return NULL;
1621 
1622 	return ro_gui_url_bar_get_url(toolbar->url);
1623 }
1624 
1625 
1626 /* This is an exported interface documented in toolbar.h */
1627 
ro_toolbar_update_all_hotlists(void)1628 void ro_toolbar_update_all_hotlists(void)
1629 {
1630 	struct toolbar *bar;
1631 
1632 	bar = ro_toolbar_bars;
1633 	while (bar != NULL) {
1634 		ro_toolbar_update_hotlist(bar);
1635 
1636 		bar = bar->next;
1637 	}
1638 }
1639 
1640 
1641 /* This is an exported interface documented in toolbar.h */
1642 
ro_toolbar_update_hotlist(struct toolbar * toolbar)1643 void ro_toolbar_update_hotlist(struct toolbar *toolbar)
1644 {
1645 	if (toolbar == NULL || toolbar->url == NULL)
1646 		return;
1647 
1648 	ro_gui_url_bar_update_hotlist(toolbar->url);
1649 }
1650 
1651 
1652 /* This is an exported interface documented in toolbar.h */
1653 
ro_toolbar_get_url_field_extent(struct toolbar * toolbar,os_box * extent)1654 bool ro_toolbar_get_url_field_extent(struct toolbar *toolbar, os_box *extent)
1655 {
1656 	if (toolbar == NULL || toolbar->url == NULL)
1657 		return false;
1658 
1659 	if (extent == NULL)
1660 		return true;
1661 
1662 	return ro_gui_url_bar_get_url_extent(toolbar->url, extent);
1663 }
1664 
1665 
1666 /* This is an exported interface documented in toolbar.h */
ro_toolbar_set_site_favicon(struct toolbar * toolbar,struct hlcache_handle * h)1667 void ro_toolbar_set_site_favicon(struct toolbar *toolbar,
1668 		struct hlcache_handle *h)
1669 {
1670 	if (toolbar == NULL || toolbar->url == NULL)
1671 		return;
1672 
1673 	ro_gui_url_bar_set_site_favicon(toolbar->url, h);
1674 }
1675 
1676 
1677 /* This is an exported interface documented in toolbar.h */
ro_toolbar_set_content_favicon(struct toolbar * toolbar,struct gui_window * g)1678 void ro_toolbar_set_content_favicon(struct toolbar *toolbar,
1679 		struct gui_window *g)
1680 {
1681 	if (toolbar == NULL || toolbar->url == NULL)
1682 		return;
1683 
1684 	ro_gui_url_bar_set_content_favicon(toolbar->url, g);
1685 }
1686 
1687 
1688 /* This is an exported interface documented in toolbar.h */
1689 
ro_toolbar_update_urlsuggest(struct toolbar * toolbar)1690 void ro_toolbar_update_urlsuggest(struct toolbar *toolbar)
1691 {
1692 	if (toolbar == NULL || toolbar->url == NULL)
1693 		return;
1694 
1695 	ro_gui_url_bar_update_urlsuggest(toolbar->url);
1696 }
1697 
1698 
1699 /* This is an exported interface documented in toolbar.h */
1700 
ro_toolbar_set_display_buttons(struct toolbar * toolbar,bool display)1701 void ro_toolbar_set_display_buttons(struct toolbar *toolbar, bool display)
1702 {
1703 	if (toolbar == NULL || toolbar->buttons == NULL)
1704 		return;
1705 
1706 	toolbar->buttons_display = display;
1707 	ro_gui_button_bar_hide(toolbar->buttons, !display);
1708 	ro_toolbar_refresh_widget_dimensions(toolbar);
1709 	ro_toolbar_refresh(toolbar);
1710 }
1711 
1712 
1713 /* This is an exported interface documented in toolbar.h */
1714 
ro_toolbar_set_display_url(struct toolbar * toolbar,bool display)1715 void ro_toolbar_set_display_url(struct toolbar *toolbar, bool display)
1716 {
1717 	if (toolbar == NULL || toolbar->url == NULL)
1718 		return;
1719 
1720 	toolbar->url_display = display;
1721 	ro_gui_url_bar_hide(toolbar->url, !display);
1722 	ro_toolbar_refresh_widget_dimensions(toolbar);
1723 	ro_toolbar_refresh(toolbar);
1724 }
1725 
1726 
1727 /* This is an exported interface documented in toolbar.h */
1728 
ro_toolbar_set_display_throbber(struct toolbar * toolbar,bool display)1729 void ro_toolbar_set_display_throbber(struct toolbar *toolbar, bool display)
1730 {
1731 	if (toolbar == NULL || toolbar->throbber == NULL)
1732 		return;
1733 
1734 	toolbar->throbber_display = display;
1735 	ro_gui_throbber_hide(toolbar->throbber, !display);
1736 	ro_toolbar_refresh_widget_dimensions(toolbar);
1737 	ro_toolbar_refresh(toolbar);
1738 }
1739 
1740 
1741 /* This is an exported interface documented in toolbar.h */
1742 
ro_toolbar_get_display_buttons(struct toolbar * toolbar)1743 bool ro_toolbar_get_display_buttons(struct toolbar *toolbar)
1744 {
1745 	return (toolbar == NULL || toolbar->buttons == NULL) ?
1746 			false : toolbar->buttons_display;
1747 }
1748 
1749 
1750 /* This is an exported interface documented in toolbar.h */
1751 
ro_toolbar_get_display_url(struct toolbar * toolbar)1752 bool ro_toolbar_get_display_url(struct toolbar *toolbar)
1753 {
1754 	return (toolbar == NULL || toolbar->url == NULL) ?
1755 			false : toolbar->url_display;
1756 }
1757 
1758 
1759 /* This is an exported interface documented in toolbar.h */
1760 
ro_toolbar_get_display_throbber(struct toolbar * toolbar)1761 bool ro_toolbar_get_display_throbber(struct toolbar *toolbar)
1762 {
1763 	return (toolbar == NULL || toolbar->throbber == NULL) ?
1764 			false : toolbar->throbber_display;
1765 }
1766 
1767 
1768 /* This is an exported interface documented in toolbar.h */
1769 
ro_toolbar_get_editing(struct toolbar * toolbar)1770 bool ro_toolbar_get_editing(struct toolbar *toolbar)
1771 {
1772 	return (toolbar == NULL || !toolbar->editing) ? false : true;
1773 }
1774 
1775 
1776 /* This is an exported interface documented in toolbar.h */
1777 
ro_toolbar_toggle_edit(struct toolbar * toolbar)1778 bool ro_toolbar_toggle_edit(struct toolbar *toolbar)
1779 {
1780 	if (toolbar == NULL || toolbar->editor == NULL)
1781 		return false;
1782 
1783 	toolbar->editing = !toolbar->editing;
1784 
1785 	ro_gui_button_bar_hide(toolbar->editor, !toolbar->editing);
1786 	ro_gui_button_bar_hide(toolbar->buttons,
1787 			!toolbar->buttons_display && !toolbar->editing);
1788 
1789 	if (!ro_toolbar_rebuild(toolbar)) {
1790 		ro_toolbar_destroy(toolbar);
1791 		return false;
1792 	}
1793 
1794 	ro_toolbar_refresh(toolbar);
1795 
1796 	/* If there's a callback registered and an edit has finished,
1797 	 * tell out client what the new button state is.
1798 	 */
1799 
1800 	if (!toolbar->editing && toolbar->buttons != NULL &&
1801 			toolbar->callbacks != NULL &&
1802 			toolbar->callbacks->save_buttons != NULL) {
1803 		char *new_buttons;
1804 		new_buttons = ro_gui_button_bar_get_config(toolbar->buttons);
1805 		toolbar->callbacks->save_buttons(toolbar->client_data,
1806 				new_buttons);
1807 	}
1808 
1809 	return true;
1810 }
1811 
1812