1 /*
2  * Copyright 2004, 2005 Richard Wilson <info@tinct.net>
3  * Copyright 2008 John Tytgat <joty@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  * General RISC OS WIMP/OS library functions (implementation).
22  */
23 
24 #include <assert.h>
25 #include <locale.h>
26 #include <stdbool.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <stdio.h>
30 #include "oslib/colourtrans.h"
31 #include "oslib/os.h"
32 #include "oslib/osfile.h"
33 #include "oslib/wimp.h"
34 #include "oslib/wimpextend.h"
35 #include "oslib/wimpspriteop.h"
36 
37 #include "utils/log.h"
38 #include "utils/utf8.h"
39 
40 #include "riscos/gui.h"
41 #include "riscos/oslib_pre7.h"
42 #include "riscos/wimp.h"
43 #include "riscos/ucstables.h"
44 
45 
46 static void ro_gui_wimp_cache_furniture_sizes(wimp_w w);
47 static size_t ro_gui_strlen(const char *str);
48 static int ro_gui_strncmp(const char *s1, const char *s2, size_t len);
49 
50 static wimpextend_furniture_sizes furniture_sizes;
51 static wimp_w furniture_window = NULL;
52 
53 /**
54  * Gets the horizontal scrollbar height
55  *
56  * \param  w  the window to read (or NULL to read a cached value)
57  */
ro_get_hscroll_height(wimp_w w)58 int ro_get_hscroll_height(wimp_w w)
59 {
60 	ro_gui_wimp_cache_furniture_sizes(w);
61 	return furniture_sizes.border_widths.y0;
62 }
63 
64 
65 /**
66  * Gets the vertical scrollbar width
67  *
68  * \param  w  the window to read (or NULL to read a cached value)
69  */
ro_get_vscroll_width(wimp_w w)70 int ro_get_vscroll_width(wimp_w w)
71 {
72 	ro_gui_wimp_cache_furniture_sizes(w);
73 	return furniture_sizes.border_widths.x1;
74 }
75 
76 
77 /**
78  * Gets the title bar height
79  *
80  * \param  w  the window to read (or NULL to read a cached value)
81  */
ro_get_title_height(wimp_w w)82 int ro_get_title_height(wimp_w w)
83 {
84 	ro_gui_wimp_cache_furniture_sizes(w);
85 	return furniture_sizes.border_widths.y1;
86 }
87 
88 /**
89  * Caches window furniture information
90  *
91  * \param  w  the window to cache information from
92  * \return true on success, false on error (default values cached)
93  */
ro_gui_wimp_cache_furniture_sizes(wimp_w w)94 void ro_gui_wimp_cache_furniture_sizes(wimp_w w)
95 {
96 	os_error *error;
97 
98 	if (furniture_window == w)
99 		return;
100 	furniture_window = w;
101 	furniture_sizes.w = w;
102 	furniture_sizes.border_widths.y0 = 40;
103 	furniture_sizes.border_widths.x1 = 40;
104 	error = xwimpextend_get_furniture_sizes(&furniture_sizes);
105 	if (error) {
106 		NSLOG(netsurf, INFO,
107 		      "xwimpextend_get_furniture_sizes: 0x%x: %s",
108 		      error->errnum,
109 		      error->errmess);
110 		ro_warn_user("WimpError", error->errmess);
111 	}
112 }
113 
114 
115 /**
116  * Reads a modes EIG factors.
117  *
118  * \param[in] mode  mode to read EIG factors for, or -1 for current
119  * \param[out] xeig  The x eig value
120  * \param[out] yeig  The y eig value
121  * \return true on success else false.
122  */
ro_gui_wimp_read_eig_factors(os_mode mode,int * xeig,int * yeig)123 bool ro_gui_wimp_read_eig_factors(os_mode mode, int *xeig, int *yeig)
124 {
125 	os_error *error;
126 
127 	error = xos_read_mode_variable(mode, os_MODEVAR_XEIG_FACTOR, xeig, 0);
128 	if (error) {
129 		NSLOG(netsurf, INFO, "xos_read_mode_variable: 0x%x: %s",
130 		      error->errnum, error->errmess);
131 		ro_warn_user("MiscError", error->errmess);
132 		return false;
133 	}
134 	error = xos_read_mode_variable(mode, os_MODEVAR_YEIG_FACTOR, yeig, 0);
135 	if (error) {
136 		NSLOG(netsurf, INFO, "xos_read_mode_variable: 0x%x: %s",
137 		      error->errnum, error->errmess);
138 		ro_warn_user("MiscError", error->errmess);
139 		return false;
140 	}
141 	return true;
142 }
143 
144 
145 /**
146  * Converts the supplied os_coord from OS units to pixels.
147  *
148  * \param  os_units  values to convert
149  * \param  mode	     mode to use EIG factors for, or -1 for current
150  */
ro_convert_os_units_to_pixels(os_coord * os_units,os_mode mode)151 void ro_convert_os_units_to_pixels(os_coord *os_units, os_mode mode)
152 {
153 	int xeig = 1, yeig = 1;
154 
155 	ro_gui_wimp_read_eig_factors(mode, &xeig, &yeig);
156 	os_units->x = ((os_units->x + (1 << xeig) - 1) >> xeig);
157 	os_units->y = ((os_units->y + (1 << yeig) - 1) >> yeig);
158 }
159 
160 
161 /**
162  * Converts the supplied os_coord from pixels to OS units.
163  *
164  * \param  pixels  values to convert
165  * \param  mode	   mode to use EIG factors for, or -1 for current
166  */
ro_convert_pixels_to_os_units(os_coord * pixels,os_mode mode)167 void ro_convert_pixels_to_os_units(os_coord *pixels, os_mode mode)
168 {
169 	int xeig = 1, yeig = 1;
170 
171 	ro_gui_wimp_read_eig_factors(mode, &xeig, &yeig);
172 	pixels->x = (pixels->x << xeig);
173 	pixels->y = (pixels->y << yeig);
174 }
175 
176 
177 /**
178  * Redraws an icon
179  *
180  * \param  w  window handle
181  * \param  i  icon handle
182  */
183 
184 #define ro_gui_redraw_icon(w, i) xwimp_set_icon_state(w, i, 0, 0)
185 
186 
187 /**
188  * Forces an icon to be redrawn entirely (ie not just updated).
189  *
190  * \param  w  window handle
191  * \param  i  icon handle
192  */
ro_gui_force_redraw_icon(wimp_w w,wimp_i i)193 void ro_gui_force_redraw_icon(wimp_w w, wimp_i i)
194 {
195 	wimp_icon_state ic;
196 	os_error *error;
197 
198 	/*	Get the icon data
199 	*/
200 	ic.w = w;
201 	ic.i = i;
202 	error = xwimp_get_icon_state(&ic);
203 	if (error) {
204 		NSLOG(netsurf, INFO, "xwimp_get_icon_state: 0x%x: %s",
205 		      error->errnum, error->errmess);
206 		ro_warn_user("WimpError", error->errmess);
207 		return;
208 	}
209 	error = xwimp_force_redraw(w, ic.icon.extent.x0, ic.icon.extent.y0,
210 			ic.icon.extent.x1, ic.icon.extent.y1);
211 	if (error) {
212 		NSLOG(netsurf, INFO, "xwimp_force_redraw: 0x%x: %s",
213 		      error->errnum, error->errmess);
214 		ro_warn_user("WimpError", error->errmess);
215 	}
216 }
217 
218 
219 /**
220  * Read the contents of a text or sprite icon.
221  *
222  * \param  w  window handle
223  * \param  i  icon handle
224  * \return NUL terminated string in icon
225  *
226  * If the icon contains direct text then the returned data will
227  * be invalidated by the next call to this function. Therefore,
228  * all client calls to this function must either copy the string or
229  * ensure that this function is not called again until they are
230  * finished with the string data returned.
231  *
232  * \todo this doesn't do local encoding -> UTF-8 to match what is done in
233  * ro_gui_set_icon_string.
234  */
ro_gui_get_icon_string(wimp_w w,wimp_i i)235 const char *ro_gui_get_icon_string(wimp_w w, wimp_i i)
236 {
237 	static wimp_icon_state ic;
238 	os_error *error;
239 	char *itext;
240 
241 	ic.w = w;
242 	ic.i = i;
243 	error = xwimp_get_icon_state(&ic);
244 	if (error) {
245 		NSLOG(netsurf, INFO, "xwimp_get_icon_state: 0x%x: %s",
246 		      error->errnum, error->errmess);
247 		ro_warn_user("WimpError", error->errmess);
248 		return NULL;
249 	}
250 	itext = (ic.icon.flags & wimp_ICON_INDIRECTED)
251 			? ic.icon.data.indirected_text.text : ic.icon.data.text;
252 	/* Guarantee NUL termination.  */
253 	itext[ro_gui_strlen(itext)] = '\0';
254 
255 	return itext;
256 }
257 
258 
259 /**
260  * Set the contents of a text or sprite icon to a string.
261  *
262  * \param  w	 window handle
263  * \param  i	 icon handle
264  * \param  text  NUL terminated string (copied)
265  * \param  is_utf8 When true, the given string is UTF-8 encoded and will be
266  * converted to local encoding currently used by the Wimp. When false, the
267  * given string is assumed to be in local encoding in use by the Wimp.
268  */
ro_gui_set_icon_string(wimp_w w,wimp_i i,const char * text,bool is_utf8)269 void ro_gui_set_icon_string(wimp_w w, wimp_i i, const char *text, bool is_utf8)
270 {
271 	wimp_caret caret;
272 	wimp_icon_state ic;
273 	os_error *error;
274 	size_t old_len, new_len;
275 	char *local_text = NULL;
276 	const char *text_for_icon;
277 	char *dst_text;
278 	size_t dst_max_len;
279 	unsigned int button_type;
280 
281 	if (is_utf8) {
282 		nserror err;
283 		/* convert text to local encoding */
284 		err = utf8_to_local_encoding(text, 0, &local_text);
285 		if (err != NSERROR_OK) {
286 			/* A bad encoding should never happen, so assert this */
287 			assert(err != NSERROR_BAD_ENCODING);
288 			NSLOG(netsurf, INFO, "utf8_to_enc failed");
289 			/* Paranoia */
290 			local_text = NULL;
291 		}
292 		text_for_icon = local_text ? local_text : text;
293 	}
294 	else
295 		text_for_icon = text;
296 	new_len = strlen(text_for_icon);
297 
298 	/* get the icon data */
299 	ic.w = w;
300 	ic.i = i;
301 	error = xwimp_get_icon_state(&ic);
302 	if (error) {
303 		NSLOG(netsurf, INFO, "xwimp_get_icon_state: 0x%x: %s",
304 		      error->errnum, error->errmess);
305 		ro_warn_user("WimpError", error->errmess);
306 		goto exit;
307 	}
308 
309 	if (ic.icon.flags & wimp_ICON_INDIRECTED) {
310 		dst_text = ic.icon.data.indirected_text.text;
311 		dst_max_len = ic.icon.data.indirected_text.size;
312 	}
313 	else {
314 		dst_text = ic.icon.data.text;
315 		dst_max_len = sizeof(ic.icon.data.text);
316 	}
317 	old_len = ro_gui_strlen(dst_text);
318 	assert(old_len < dst_max_len);
319 
320 	/* check that the existing text is not the same as the updated text
321 	 * to stop flicker */
322 	if (dst_max_len) {
323 		if (!ro_gui_strncmp(dst_text, text_for_icon, dst_max_len))
324 			goto exit;
325 
326 		/* copy the text across */
327 		strncpy(dst_text, text_for_icon, dst_max_len - 1);
328 		dst_text[dst_max_len - 1] = '\0';
329 
330 		/* handle the caret being in the icon */
331 		button_type = (ic.icon.flags & wimp_ICON_BUTTON_TYPE)
332 				>> wimp_ICON_BUTTON_TYPE_SHIFT;
333 		if ((button_type == wimp_BUTTON_WRITABLE) ||
334 				(button_type == wimp_BUTTON_WRITE_CLICK_DRAG)) {
335 			error = xwimp_get_caret_position(&caret);
336 			if (error) {
337 				NSLOG(netsurf, INFO,
338 				      "xwimp_get_caret_position: 0x%x: %s",
339 				      error->errnum,
340 				      error->errmess);
341 				ro_warn_user("WimpError", error->errmess);
342 				goto exit;
343 			}
344 			if ((caret.w == w) && (caret.i == i)) {
345 				if ((size_t)caret.index > new_len
346 						|| (size_t)caret.index == old_len)
347 					caret.index = new_len;
348 				error = xwimp_set_caret_position(w, i, caret.pos.x,
349 						caret.pos.y, -1, caret.index);
350 				if (error) {
351 					NSLOG(netsurf, INFO,
352 					      "xwimp_set_caret_position: 0x%x: %s",
353 					      error->errnum,
354 					      error->errmess);
355 					ro_warn_user("WimpError", error->errmess);
356 				}
357 			}
358 		}
359 		ro_gui_redraw_icon(w, i);
360 	}
361 
362 exit:
363 	free(local_text);
364 }
365 
366 
367 /**
368  * Set the contents of an icon to a number.
369  *
370  * \param  w	  window handle
371  * \param  i	  icon handle
372  * \param  value  value
373  */
ro_gui_set_icon_integer(wimp_w w,wimp_i i,int value)374 void ro_gui_set_icon_integer(wimp_w w, wimp_i i, int value)
375 {
376 	char buffer[20]; // Big enough for 64-bit int
377 
378 	sprintf(buffer, "%d", value);
379 
380 	ro_gui_set_icon_string(w, i, buffer, true);
381 }
382 
383 
384 /**
385  * Set the contents of an icon to a number.
386  *
387  * \param w  window handle
388  * \param i  icon handle
389  * \param value  value to use in icon.
390  * \param decimal_places The number of decimal places to use.
391  */
ro_gui_set_icon_decimal(wimp_w w,wimp_i i,int value,int decimal_places)392 void ro_gui_set_icon_decimal(wimp_w w, wimp_i i, int value, int decimal_places)
393 {
394 	char buffer[20]; // Big enough for 64-bit int
395 
396 	switch (decimal_places) {
397 		case 0:
398 			sprintf(buffer, "%d", value);
399 			break;
400 		case 1:
401 			sprintf(buffer, "%.1f", (float)value / 10);
402 			break;
403 		case 2:
404 			sprintf(buffer, "%.2f", (float)value / 100);
405 			break;
406 		default:
407 			assert(!"Unsupported decimal format");
408 			break;
409 	}
410 
411 	ro_gui_set_icon_string(w, i, buffer, true);
412 }
413 
414 
415 /**
416  * Get the contents of an icon as a number.
417  *
418  * \param w  window handle
419  * \param i  icon handle
420  * \param decimal_places  number of places to show.
421  * \return value used.
422  */
ro_gui_get_icon_decimal(wimp_w w,wimp_i i,int decimal_places)423 int ro_gui_get_icon_decimal(wimp_w w, wimp_i i, int decimal_places)
424 {
425 	double value;
426 	int multiple = 1;
427 
428 	for (; decimal_places > 0; decimal_places--)
429 		multiple *= 10;
430 
431 	value = atof(ro_gui_get_icon_string(w, i)) * multiple;
432 
433 	return (int)value;
434 }
435 
436 
437 /**
438  * Set the selected state of an icon.
439  *
440  * \param  w	 window handle
441  * \param  i	 icon handle
442  * \param  state selected state
443  */
ro_gui_set_icon_selected_state(wimp_w w,wimp_i i,bool state)444 void ro_gui_set_icon_selected_state(wimp_w w, wimp_i i, bool state)
445 {
446 	os_error *error;
447 	if (ro_gui_get_icon_selected_state(w, i) == state) return;
448 	error = xwimp_set_icon_state(w, i,
449 			(state ? wimp_ICON_SELECTED : 0), wimp_ICON_SELECTED);
450 	if (error) {
451 		NSLOG(netsurf, INFO, "xwimp_set_icon_state: 0x%x: %s",
452 		      error->errnum, error->errmess);
453 		ro_warn_user("WimpError", error->errmess);
454 	}
455 }
456 
457 /**
458  * Gets the selected state of an icon.
459  *
460  * \param  w	 window handle
461  * \param  i	 icon handle
462  */
ro_gui_get_icon_selected_state(wimp_w w,wimp_i i)463 bool ro_gui_get_icon_selected_state(wimp_w w, wimp_i i)
464 {
465 	os_error *error;
466 	wimp_icon_state ic;
467 	ic.w = w;
468 	ic.i = i;
469 	error = xwimp_get_icon_state(&ic);
470 	if (error) {
471 		NSLOG(netsurf, INFO, "xwimp_get_icon_state: 0x%x: %s",
472 		      error->errnum, error->errmess);
473 		ro_warn_user("WimpError", error->errmess);
474 		return false;
475 	}
476 	return ((ic.icon.flags & wimp_ICON_SELECTED) != 0);
477 }
478 
479 
480 /**
481  * Set the shaded state of an icon.
482  *
483  * \param  w	 window handle
484  * \param  i	 icon handle
485  * \param  state shaded state
486  */
ro_gui_set_icon_shaded_state(wimp_w w,wimp_i i,bool state)487 void ro_gui_set_icon_shaded_state(wimp_w w, wimp_i i, bool state)
488 {
489 	wimp_caret caret;
490 	os_error *error;
491 
492 	/* update the state */
493 	if (ro_gui_get_icon_shaded_state(w, i) == state)
494 		return;
495 	error = xwimp_set_icon_state(w, i,
496 			(state ? wimp_ICON_SHADED : 0), wimp_ICON_SHADED);
497 	if (error) {
498 		NSLOG(netsurf, INFO, "xwimp_get_icon_state: 0x%x: %s",
499 		      error->errnum, error->errmess);
500 		ro_warn_user("WimpError", error->errmess);
501 	}
502 	if (!state)
503 		return;
504 
505 	/* ensure the caret is not in a shaded icon */
506 	error = xwimp_get_caret_position(&caret);
507 	if (error) {
508 		NSLOG(netsurf, INFO, "xwimp_get_caret_position: 0x%x: %s",
509 		      error->errnum, error->errmess);
510 		ro_warn_user("WimpError", error->errmess);
511 		return;
512 	}
513 	if ((caret.w != w) || (caret.i != i))
514 		return;
515 	/* move the caret to the first avaiable writable */
516 	if (ro_gui_set_caret_first(w))
517 		return;
518 	/* lose the caret */
519 	error = xwimp_set_caret_position((wimp_w)-1, (wimp_i)-1, -1, -1, -1, -1);
520 	if (error) {
521 		NSLOG(netsurf, INFO, "xwimp_set_caret_position: 0x%x: %s",
522 		      error->errnum, error->errmess);
523 		ro_warn_user("WimpError", error->errmess);
524 		return;
525 	}
526 }
527 
528 
529 /**
530  * Gets the shaded state of an icon.
531  *
532  * \param  w	 window handle
533  * \param  i	 icon handle
534  */
ro_gui_get_icon_shaded_state(wimp_w w,wimp_i i)535 bool ro_gui_get_icon_shaded_state(wimp_w w, wimp_i i)
536 {
537 	wimp_icon_state ic;
538 	ic.w = w;
539 	ic.i = i;
540 	xwimp_get_icon_state(&ic);
541 	return (ic.icon.flags & wimp_ICON_SHADED) != 0;
542 }
543 
544 
545 /**
546  * Set the deleted state of an icon.
547  *
548  * \param  w	 window handle
549  * \param  i	 icon handle
550  * \param  state shaded state
551  */
ro_gui_set_icon_deleted_state(wimp_w w,wimp_i i,bool state)552 void ro_gui_set_icon_deleted_state(wimp_w w, wimp_i i, bool state)
553 {
554 	wimp_caret caret;
555 	os_error *error;
556 
557 	/* update the state */
558 	if (ro_gui_get_icon_deleted_state(w, i) == state)
559 		return;
560 	error = xwimp_set_icon_state(w, i,
561 			(state ? wimp_ICON_DELETED : 0), wimp_ICON_DELETED);
562 	if (error) {
563 		NSLOG(netsurf, INFO, "xwimp_get_icon_state: 0x%x: %s",
564 		      error->errnum, error->errmess);
565 		ro_warn_user("WimpError", error->errmess);
566 	}
567 	if (!state)
568 		return;
569 
570 	/* ensure the caret is not in a shaded icon */
571 	error = xwimp_get_caret_position(&caret);
572 	if (error) {
573 		NSLOG(netsurf, INFO, "xwimp_get_caret_position: 0x%x: %s",
574 		      error->errnum, error->errmess);
575 		ro_warn_user("WimpError", error->errmess);
576 		return;
577 	}
578 	if ((caret.w != w) || (caret.i != i))
579 		return;
580 	/* move the caret to the first avaiable writable */
581 	if (ro_gui_set_caret_first(w))
582 		return;
583 	/* lose the caret */
584 	error = xwimp_set_caret_position((wimp_w)-1, (wimp_i)-1, -1, -1, -1, -1);
585 	if (error) {
586 		NSLOG(netsurf, INFO, "xwimp_set_caret_position: 0x%x: %s",
587 		      error->errnum, error->errmess);
588 		ro_warn_user("WimpError", error->errmess);
589 		return;
590 	}
591 }
592 
593 
594 /**
595  * Gets the deleted state of an icon.
596  *
597  * \param  w	 window handle
598  * \param  i	 icon handle
599  */
ro_gui_get_icon_deleted_state(wimp_w w,wimp_i i)600 bool ro_gui_get_icon_deleted_state(wimp_w w, wimp_i i)
601 {
602 	wimp_icon_state ic;
603 	ic.w = w;
604 	ic.i = i;
605 	xwimp_get_icon_state(&ic);
606 	return (ic.icon.flags & wimp_ICON_DELETED) != 0;
607 }
608 
609 
610 /**
611  * Set the button type of an icon.
612  *
613  * \param  w	window handle
614  * \param  i	icon handle
615  * \param  type button type
616  */
ro_gui_set_icon_button_type(wimp_w w,wimp_i i,int type)617 void ro_gui_set_icon_button_type(wimp_w w, wimp_i i, int type)
618 {
619 	os_error *error;
620 	error = xwimp_set_icon_state(w, i, wimp_ICON_BUTTON_TYPE,
621 			(type << wimp_ICON_BUTTON_TYPE_SHIFT));
622 	if (error) {
623 		NSLOG(netsurf, INFO, "xwimp_set_icon_state: 0x%x: %s",
624 		      error->errnum, error->errmess);
625 		ro_warn_user("WimpError", error->errmess);
626 	}
627 }
628 
629 
630 /**
631  * Set an icon's sprite
632  *
633  * \param  w	window handle
634  * \param  i	icon handle
635  * \param  area sprite area containing sprite
636  * \param  name name of sprite in area (in local encoding)
637  */
ro_gui_set_icon_sprite(wimp_w w,wimp_i i,osspriteop_area * area,const char * name)638 void ro_gui_set_icon_sprite(wimp_w w, wimp_i i, osspriteop_area *area,
639 		const char *name)
640 {
641 	wimp_icon_state ic;
642 	os_error *error;
643 
644 	/* get the icon data */
645 	ic.w = w;
646 	ic.i = i;
647 	error = xwimp_get_icon_state(&ic);
648 	if (error) {
649 		NSLOG(netsurf, INFO, "xwimp_get_icon_state: 0x%x: %s",
650 		      error->errnum, error->errmess);
651 		ro_warn_user("WimpError", error->errmess);
652 		return;
653 	}
654 
655 	/* copy the name across */
656 	if (ic.icon.data.indirected_text.size) {
657 		strncpy(ic.icon.data.indirected_text.text, name,
658 			(unsigned int)ic.icon.data.indirected_text.size - 1);
659 		ic.icon.data.indirected_text.text[
660 				ic.icon.data.indirected_text.size - 1] = '\0';
661 	}
662 
663 	ic.icon.data.indirected_sprite.area = area;
664 
665 	ro_gui_redraw_icon(w, i);
666 }
667 
668 
669 /**
670  * Set a window title
671  *
672  * \param  w	 window handle
673  * \param  text  new title (copied)
674  */
ro_gui_set_window_title(wimp_w w,const char * text)675 void ro_gui_set_window_title(wimp_w w, const char *text)
676 {
677 	wimp_window_info_base window;
678 	os_error *error;
679 	char *title_local_enc;
680 	nserror err;
681 
682 	/*	Get the window details
683 	*/
684 	window.w = w;
685 	error = xwimp_get_window_info_header_only((wimp_window_info *)&window);
686 	if (error) {
687 		NSLOG(netsurf, INFO, "xwimp_get_window_info: 0x%x: %s",
688 		      error->errnum, error->errmess);
689 		ro_warn_user("WimpError", error->errmess);
690 		return;
691 	}
692 
693 	/* convert text to local encoding */
694 	err = utf8_to_local_encoding(text, 0, &title_local_enc);
695 	if (err != NSERROR_OK) {
696 		/* A bad encoding should never happen,
697 		 * so assert this */
698 		assert(err != NSERROR_BAD_ENCODING);
699 		NSLOG(netsurf, INFO, "utf8_to_enc failed");
700 		return;
701 	}
702 
703 	/*	Set the title string
704 	*/
705 	strncpy(window.title_data.indirected_text.text, title_local_enc,
706 			(unsigned int)window.title_data.indirected_text.size
707 					- 1);
708 	window.title_data.indirected_text.text[
709 			window.title_data.indirected_text.size - 1] = '\0';
710 
711 	/*	Redraw accordingly
712 	*/
713 	error = xwimp_force_redraw_title(w);
714 	if (error) {
715 		NSLOG(netsurf, INFO, "xwimp_force_redraw_title: 0x%x: %s",
716 		      error->errnum, error->errmess);
717 		ro_warn_user("WimpError", error->errmess);
718 		return;
719 	}
720 
721 	free(title_local_enc);
722 }
723 
724 
725 /**
726  * Places the caret in the first available icon
727  *
728  * \param w the window to place the caret in
729  * \return true if the caret was placed, false otherwise
730  */
ro_gui_set_caret_first(wimp_w w)731 bool ro_gui_set_caret_first(wimp_w w)
732 {
733 	int icon, b;
734 	wimp_window_state win_state;
735 	wimp_window_info_base window;
736 	wimp_icon_state state;
737 	os_error *error;
738 
739 	/* check the window is open */
740 	win_state.w = w;
741 	error = xwimp_get_window_state(&win_state);
742 	if (error) {
743 		NSLOG(netsurf, INFO, "xwimp_get_window_state: 0x%x: %s",
744 		      error->errnum, error->errmess);
745 		ro_warn_user("WimpError", error->errmess);
746 		return false;
747 	}
748 	if (!(win_state.flags & wimp_WINDOW_OPEN))
749 		return false;
750 
751 	/* get the window details for the icon count */
752 	window.w = w;
753 	error = xwimp_get_window_info_header_only((wimp_window_info *)&window);
754 	if (error) {
755 		NSLOG(netsurf, INFO, "xwimp_get_window_info: 0x%x: %s",
756 		      error->errnum, error->errmess);
757 		ro_warn_user("WimpError", error->errmess);
758 		return false;
759 	}
760 
761 	/* work through all the icons */
762 	state.w = w;
763 	for (icon = 0; icon < window.icon_count; icon++) {
764 		state.i = icon;
765 		error = xwimp_get_icon_state(&state);
766 		if (error) {
767 			NSLOG(netsurf, INFO,
768 			      "xwimp_get_icon_state: 0x%x: %s",
769 			      error->errnum,
770 			      error->errmess);
771 			ro_warn_user("WimpError", error->errmess);
772 			return false;
773 		}
774 
775 		/* ignore if it's shaded or not writable */
776 		if (state.icon.flags & wimp_ICON_SHADED)
777 			continue;
778 		b = (state.icon.flags >> wimp_ICON_BUTTON_TYPE_SHIFT) & 0xf;
779 		if ((b != wimp_BUTTON_WRITE_CLICK_DRAG) &&
780 				(b != wimp_BUTTON_WRITABLE))
781 			continue;
782 
783 		/* move the caret */
784 		error = xwimp_set_caret_position(w, icon, 0, 0, -1,
785 				strlen(state.icon.data.indirected_text.text));
786 		if (error) {
787 			NSLOG(netsurf, INFO,
788 			      "xwimp_set_caret_position: 0x%x: %s",
789 			      error->errnum,
790 			      error->errmess);
791 			ro_warn_user("WimpError", error->errmess);
792 		}
793 		return true;
794 	}
795 	return false;
796 }
797 
798 
799 /**
800  * Load a sprite file into memory.
801  *
802  * \param  pathname  file to load
803  * \return  sprite area, or 0 on memory exhaustion or error and error reported
804  */
805 
ro_gui_load_sprite_file(const char * pathname)806 osspriteop_area *ro_gui_load_sprite_file(const char *pathname)
807 {
808 	int len;
809 	fileswitch_object_type obj_type;
810 	osspriteop_area *area;
811 	os_error *error;
812 
813 	error = xosfile_read_stamped_no_path(pathname,
814 			&obj_type, 0, 0, &len, 0, 0);
815 	if (error) {
816 		NSLOG(netsurf, INFO,
817 		      "xosfile_read_stamped_no_path: 0x%x: %s",
818 		      error->errnum,
819 		      error->errmess);
820 		ro_warn_user("MiscError", error->errmess);
821 		return 0;
822 	}
823 	if (obj_type != fileswitch_IS_FILE) {
824 		ro_warn_user("FileError", pathname);
825 		return 0;
826 	}
827 
828 	area = malloc(len + 4);
829 	if (!area) {
830 		ro_warn_user("NoMemory", 0);
831 		return 0;
832 	}
833 
834 	area->size = len + 4;
835 	area->sprite_count = 0;
836 	area->first = 16;
837 	area->used = 16;
838 
839 	error = xosspriteop_load_sprite_file(osspriteop_USER_AREA,
840 			area, pathname);
841 	if (error) {
842 		NSLOG(netsurf, INFO,
843 		      "xosspriteop_load_sprite_file: 0x%x: %s",
844 		      error->errnum,
845 		      error->errmess);
846 		ro_warn_user("MiscError", error->errmess);
847 		free(area);
848 		return 0;
849 	}
850 
851 	return area;
852 }
853 
854 
855 /**
856  * Check if a sprite is present in the Wimp sprite pool.
857  *
858  * \param  sprite  name of sprite
859  * \return  true if the sprite is present
860  */
861 
ro_gui_wimp_sprite_exists(const char * sprite)862 bool ro_gui_wimp_sprite_exists(const char *sprite)
863 {
864 	static char last_sprite_found[16];
865 	os_error *error;
866 
867 	/* make repeated calls fast */
868 	if (!strncmp(sprite, last_sprite_found, sizeof(last_sprite_found)))
869 		return true;
870 
871 	/* fallback if not known to exist */
872 	error = xwimpspriteop_select_sprite(sprite, 0);
873 	if (error) {
874 		if (error->errnum != error_SPRITE_OP_DOESNT_EXIST) {
875 			NSLOG(netsurf, INFO,
876 			      "xwimpspriteop_select_sprite: 0x%x: %s",
877 			      error->errnum,
878 			      error->errmess);
879 			ro_warn_user("MiscError", error->errmess);
880 		}
881 		return false;
882 	}
883   	snprintf(last_sprite_found, sizeof(last_sprite_found), sprite);
884 	return true;
885 }
886 
887 
888 /**
889  * Locate a sprite in the Wimp sprite pool, returning a pointer to it.
890  *
891  * \param  name	   sprite name
892  * \param  sprite  receives pointer to sprite if found
893  * \return error ptr iff not found
894  */
895 
ro_gui_wimp_get_sprite(const char * name,osspriteop_header ** sprite)896 os_error *ro_gui_wimp_get_sprite(const char *name, osspriteop_header **sprite)
897 {
898 	osspriteop_area *rom_base, *ram_base;
899 	os_error *error;
900 
901 	error = xwimp_base_of_sprites(&rom_base, &ram_base);
902 	if (error) return error;
903 
904 	error = xosspriteop_select_sprite(osspriteop_USER_AREA,
905 			ram_base, (osspriteop_id)name, sprite);
906 
907 	if (error && error->errnum == error_SPRITE_OP_DOESNT_EXIST)
908 		error = xosspriteop_select_sprite(osspriteop_USER_AREA,
909 				rom_base, (osspriteop_id)name, sprite);
910 
911 	return error;
912 }
913 
914 
915 /**
916  * Get the dimensions of a sprite
917  *
918  * \param *area			The sprite area to use.
919  * \param *sprite		Pointer to the sprite name.
920  * \param *width		Return the sprite width.
921  * \param *height		Return the sprite height.
922  * \return			true if successful; else false.
923  */
924 
ro_gui_wimp_get_sprite_dimensions(osspriteop_area * area,char * sprite,int * width,int * height)925 bool ro_gui_wimp_get_sprite_dimensions(osspriteop_area *area, char *sprite,
926 		int *width, int *height)
927 {
928 	os_error			*error = NULL;
929 	os_mode				mode;
930 	os_coord			dimensions;
931 
932 	dimensions.x = 0;
933 	dimensions.y = 0;
934 
935 	if (area != (osspriteop_area *) -1)
936 		error = xosspriteop_read_sprite_info(osspriteop_USER_AREA,
937 				area, (osspriteop_id) sprite,
938 				&dimensions.x, &dimensions.y, 0, &mode);
939 
940 	if (error != NULL || area == (osspriteop_area *) -1)
941 		error = xwimpspriteop_read_sprite_info(sprite,
942 				&dimensions.x, &dimensions.y, 0, &mode);
943 
944 	if (error == NULL) {
945 		ro_convert_pixels_to_os_units(&dimensions, mode);
946 		if (width != NULL)
947 			*width = dimensions.x;
948 		if (height != NULL)
949 			*height = dimensions.y;
950 	} else if (error->errnum != error_SPRITE_OP_DOESNT_EXIST) {
951 		NSLOG(netsurf, INFO,
952 		      "xosspriteop_read_sprite_info: 0x%x: %s",
953 		      error->errnum,
954 		      error->errmess);
955 		ro_warn_user("MiscError", error->errmess);
956 		return false;
957 	}
958 
959 	return true;
960 }
961 
962 
963 /**
964  * Performs simple user redraw for a window.
965  *
966  * \param redraw  wimp draw
967  * \param user_fill  whether to fill the redraw area
968  * \param user_colour  the colour to use when filling
969  */
970 
ro_gui_user_redraw(wimp_draw * redraw,bool user_fill,os_colour user_colour)971 void ro_gui_user_redraw(wimp_draw *redraw, bool user_fill,
972 		os_colour user_colour)
973 {
974 	os_error *error;
975 	osbool more;
976 
977 	error = xwimp_redraw_window(redraw, &more);
978 	if (error) {
979 		NSLOG(netsurf, INFO, "xwimp_redraw_window: 0x%x: %s",
980 		      error->errnum, error->errmess);
981 		ro_warn_user("WimpError", error->errmess);
982 		return;
983 	}
984 	while (more) {
985 		if (user_fill) {
986 			error = xcolourtrans_set_gcol(user_colour,
987 					colourtrans_SET_BG_GCOL,
988 					os_ACTION_OVERWRITE, 0, 0);
989 			if (error) {
990 				NSLOG(netsurf, INFO,
991 				      "xcolourtrans_set_gcol: 0x%x: %s",
992 				      error->errnum,
993 				      error->errmess);
994 				ro_warn_user("MiscError", error->errmess);
995 			}
996 			os_clg();
997 		}
998 		error = xwimp_get_rectangle(redraw, &more);
999 		if (error) {
1000 			NSLOG(netsurf, INFO, "xwimp_get_rectangle: 0x%x: %s",
1001 			      error->errnum, error->errmess);
1002 			ro_warn_user("WimpError", error->errmess);
1003 			return;
1004 		}
1005 	}
1006 }
1007 
1008 
1009 /**
1010  * Sets whether a piece of window furniture is present for a window.
1011  *
1012  * \param  w	     the window to modify
1013  * \param  bic_mask  the furniture flags to clear
1014  * \param  xor_mask  the furniture flags to toggle
1015  */
ro_gui_wimp_update_window_furniture(wimp_w w,wimp_window_flags bic_mask,wimp_window_flags xor_mask)1016 void ro_gui_wimp_update_window_furniture(wimp_w w, wimp_window_flags bic_mask,
1017 		wimp_window_flags xor_mask)
1018 {
1019 	wimp_window_state state;
1020 	wimp_w parent;
1021 	bits linkage;
1022 	os_error *error;
1023 	bool open;
1024 
1025 	state.w = w;
1026 	error = xwimp_get_window_state_and_nesting(&state, &parent, &linkage);
1027 	if (error) {
1028 		NSLOG(netsurf, INFO, "xwimp_get_window_state: 0x%x: %s",
1029 		      error->errnum, error->errmess);
1030 		ro_warn_user("WimpError", error->errmess);
1031 		return;
1032 	}
1033 
1034 	open = state.flags & wimp_WINDOW_OPEN;
1035 	state.flags &= ~(63 << 16); /* clear bits 16-21 */
1036 	state.flags &= ~bic_mask;
1037 	state.flags ^= xor_mask;
1038 	if (!open)
1039 		state.next = wimp_HIDDEN;
1040 	error = xwimp_open_window_nested_with_flags(&state, parent, linkage);
1041 	if (error) {
1042 		NSLOG(netsurf, INFO, "xwimp_open_window: 0x%x: %s",
1043 		      error->errnum, error->errmess);
1044 		ro_warn_user("WimpError", error->errmess);
1045 		return;
1046 	}
1047 
1048 	if (!open) {
1049 		error = xwimp_close_window(w);
1050 		if (error) {
1051 			NSLOG(netsurf, INFO, "xwimp_close_window: 0x%x: %s",
1052 			      error->errnum, error->errmess);
1053 			ro_warn_user("WimpError", error->errmess);
1054 			return;
1055 		}
1056 	}
1057 }
1058 
1059 
1060 /**
1061  * Checks whether a piece of window furniture is present for a window.
1062  *
1063  * \param  w	     the window to modify
1064  * \param  mask	     the furniture flags to check
1065  */
ro_gui_wimp_check_window_furniture(wimp_w w,wimp_window_flags mask)1066 bool ro_gui_wimp_check_window_furniture(wimp_w w, wimp_window_flags mask)
1067 {
1068 	wimp_window_state state;
1069 	os_error *error;
1070 
1071 	state.w = w;
1072 	error = xwimp_get_window_state(&state);
1073 	if (error) {
1074 		NSLOG(netsurf, INFO, "xwimp_get_window_state: 0x%x: %s",
1075 		      error->errnum, error->errmess);
1076 		ro_warn_user("WimpError", error->errmess);
1077 		return false;
1078 	}
1079 	return state.flags & mask;
1080 }
1081 
1082 /**
1083  * RO GUI-specific strlen, for control character terminated strings
1084  *
1085  * \param str  The string to measure the length of
1086  * \return The length of the string
1087  */
ro_gui_strlen(const char * str)1088 size_t ro_gui_strlen(const char *str)
1089 {
1090 	const char *str_begin;
1091 
1092 	if (str == NULL)
1093 		return 0;
1094 
1095 	for (str_begin = str; *str++ >= ' '; /* */)
1096 		/* */;
1097 
1098 	return str - str_begin - 1;
1099 }
1100 
1101 /**
1102  * RO GUI-specific strncmp, for control character terminated strings
1103  *
1104  * \param s1 The first string for comparison
1105  * \param s2 The second string for comparison
1106  * \param len Maximum number of bytes to be checked
1107  * \return 0 for equal strings up to len bytes; pos for s1 being bigger than
1108  * s2; neg for s1 being smaller than s2.
1109  */
ro_gui_strncmp(const char * s1,const char * s2,size_t len)1110 int ro_gui_strncmp(const char *s1, const char *s2, size_t len)
1111 {
1112 	while (len--) {
1113 		char c1 = *s1++;
1114 		char c2 = *s2++;
1115 		if (c1 < ' ' || c2 < ' ')
1116 			return (c1 < ' ' ? 0 : c1) - (c2 < ' ' ? 0 : c2);
1117 		int diff = c1 - c2;
1118 		if (diff)
1119 			return diff;
1120 	}
1121 	return 0;
1122 }
1123 
1124 
1125 /**
1126  * Generic window scroll event handler.
1127  *
1128  * \param  *scroll		Pointer to Scroll Event block.
1129  */
1130 
ro_gui_scroll(wimp_scroll * scroll)1131 void ro_gui_scroll(wimp_scroll *scroll)
1132 {
1133 	os_error	*error;
1134 	int		x = scroll->visible.x1 - scroll->visible.x0 - 32;
1135 	int		y = scroll->visible.y1 - scroll->visible.y0 - 32;
1136 
1137 	switch (scroll->xmin) {
1138 	case wimp_SCROLL_PAGE_LEFT:
1139 		scroll->xscroll -= x;
1140 		break;
1141 	case wimp_SCROLL_COLUMN_LEFT:
1142 		scroll->xscroll -= 100;
1143 		break;
1144 	case wimp_SCROLL_COLUMN_RIGHT:
1145 		scroll->xscroll += 100;
1146 		break;
1147 	case wimp_SCROLL_PAGE_RIGHT:
1148 		scroll->xscroll += x;
1149 		break;
1150 	default:
1151 		scroll->xscroll += (x * (scroll->xmin>>2)) >> 2;
1152 		break;
1153 	}
1154 
1155 	switch (scroll->ymin) {
1156 	case wimp_SCROLL_PAGE_UP:
1157 		scroll->yscroll += y;
1158 		break;
1159 	case wimp_SCROLL_LINE_UP:
1160 		scroll->yscroll += 100;
1161 		break;
1162 	case wimp_SCROLL_LINE_DOWN:
1163 		scroll->yscroll -= 100;
1164 		break;
1165 	case wimp_SCROLL_PAGE_DOWN:
1166 		scroll->yscroll -= y;
1167 		break;
1168 	default:
1169 		scroll->yscroll += (y * (scroll->ymin>>2)) >> 2;
1170 		break;
1171 	}
1172 
1173 	error = xwimp_open_window((wimp_open *) scroll);
1174 	if (error) {
1175 		NSLOG(netsurf, INFO, "xwimp_open_window: 0x%x: %s",
1176 		      error->errnum, error->errmess);
1177 	}
1178 }
1179 
1180