1 /*
2  * Copyright 2004, 2005 Richard Wilson <info@tinct.net>
3  * Copyright 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  * Throbber (implementation).
22  */
23 
24 #include <alloca.h>
25 #include <assert.h>
26 #include <stdio.h>
27 #include <stdbool.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include "oslib/os.h"
31 #include "oslib/osspriteop.h"
32 #include "oslib/wimp.h"
33 
34 #include "utils/log.h"
35 #include "riscos/gui.h"
36 
37 #include "riscos/gui/throbber.h"
38 #include "riscos/theme.h"
39 #include "riscos/wimp.h"
40 
41 #define THROBBER_SPRITE_NAME_LENGTH 12
42 #define THROBBER_ANIMATE_INTERVAL 10
43 
44 struct throbber {
45 	/** The applied theme (or NULL to use the default) */
46 	struct theme_descriptor	*theme;
47 
48 	/** The widget dimensions. */
49 	int			x_min, y_min;
50 
51 	/** The window and icon details. */
52 	wimp_w			window;
53 	wimp_i			icon;
54 	os_box			extent;
55 	osspriteop_area		*sprites;
56 	bool			hidden;
57 	bool			shaded;
58 
59 	/** The animation details. */
60 	int			max_frame;
61 	int			current_frame;
62 	os_t			last_update;
63 	char			sprite_name[THROBBER_SPRITE_NAME_LENGTH];
64 	bool			force_redraw;
65 };
66 
67 /*
68  * Private function prototypes.
69  */
70 
71 static bool ro_gui_throbber_icon_update(struct throbber *throbber);
72 static bool ro_gui_throbber_icon_resize(struct throbber *throbber);
73 
74 /* This is an exported interface documented in throbber.h */
75 
ro_gui_throbber_create(struct theme_descriptor * theme)76 struct throbber *ro_gui_throbber_create(struct theme_descriptor *theme)
77 {
78 	struct throbber		*throbber;
79 
80 	/* Allocate memory. */
81 
82 	throbber = malloc(sizeof(struct throbber));
83 	if (throbber == NULL) {
84 		NSLOG(netsurf, INFO, "No memory for malloc()");
85 		return NULL;
86 	}
87 
88 	/* Set up default parameters. If reading the throbber theme data
89 	 * fails, we give up and return a failure.
90 	 */
91 
92 	if (!ro_gui_theme_get_throbber_data(theme, &throbber->max_frame,
93 			&throbber->x_min, &throbber->y_min, NULL,
94 			&throbber->force_redraw)) {
95 		free(throbber);
96 		return NULL;
97 	}
98 
99 	throbber->sprites = ro_gui_theme_get_sprites(theme);
100 
101 	throbber->theme = theme;
102 
103 	throbber->extent.x0 = 0;
104 	throbber->extent.y0 = 0;
105 	throbber->extent.x1 = 0;
106 	throbber->extent.y1 = 0;
107 
108 	throbber->current_frame = 0;
109 	throbber->last_update = 0;
110 
111 	throbber->window = NULL;
112 	throbber->icon = -1;
113 
114 	throbber->hidden = false;
115 	throbber->shaded = false;
116 
117 	return throbber;
118 }
119 
120 
121 /* This is an exported interface documented in throbber.h */
122 
ro_gui_throbber_rebuild(struct throbber * throbber,struct theme_descriptor * theme,theme_style style,wimp_w window,bool shaded)123 bool ro_gui_throbber_rebuild(struct throbber *throbber,
124 		struct theme_descriptor *theme, theme_style style,
125 		wimp_w window, bool shaded)
126 {
127 	if (throbber == NULL)
128 		return false;
129 
130 	throbber->theme = theme;
131 	throbber->window = window;
132 	throbber->sprites = ro_gui_theme_get_sprites(theme);
133 
134 	throbber->icon = -1;
135 
136 	throbber->shaded = shaded;
137 
138 	strcpy(throbber->sprite_name, "throbber0");
139 
140 	if (!ro_gui_theme_get_throbber_data(theme, &throbber->max_frame,
141 			&throbber->x_min, &throbber->y_min, NULL,
142 			&throbber->force_redraw)) {
143 		free(throbber);
144 		return false;
145 	}
146 
147 	return ro_gui_throbber_icon_update(throbber);
148 }
149 
150 
151 /* This is an exported interface documented in throbber.h */
152 
ro_gui_throbber_destroy(struct throbber * throbber)153 void ro_gui_throbber_destroy(struct throbber *throbber)
154 {
155 	if (throbber == NULL)
156 		return;
157 
158 	free(throbber);
159 }
160 
161 
162 /* This is an exported interface documented in throbber.h */
163 
ro_gui_throbber_get_dims(struct throbber * throbber,int * width,int * height)164 bool ro_gui_throbber_get_dims(struct throbber *throbber,
165 		int *width, int *height)
166 {
167 	if (throbber == NULL)
168 		return false;
169 
170 	if (throbber->x_min != -1 && throbber->y_min != -1) {
171 		if (width != NULL)
172 			*width = throbber->x_min;
173 		if (height != NULL)
174 			*height = throbber->y_min;
175 
176 		return true;
177 	}
178 
179 	return false;
180 }
181 
182 
183 /* This is an exported interface documented in throbber.h */
184 
ro_gui_throbber_set_extent(struct throbber * throbber,int x0,int y0,int x1,int y1)185 bool ro_gui_throbber_set_extent(struct throbber *throbber,
186 		int x0, int y0, int x1, int y1)
187 {
188 	if (throbber == NULL)
189 		return false;
190 
191 	if ((x1 - x0) < throbber->x_min || (y1 - y0) < throbber->y_min)
192 		return false;
193 
194 	if (throbber->extent.x0 == x0 && throbber->extent.y0 == y0 &&
195 			throbber->extent.x1 == x1 &&
196 			throbber->extent.y1 == y1)
197 		return true;
198 
199 	/* Redraw the relevant bits of the toolbar. */
200 
201 	if (throbber->window != NULL && throbber->icon != -1) {
202 		xwimp_force_redraw(throbber->window,
203 				throbber->extent.x0, throbber->extent.y0,
204 				throbber->extent.x1, throbber->extent.y1);
205 		xwimp_force_redraw(throbber->window, x0, y0, x1, y1);
206 	}
207 
208 	/* Update the throbber position */
209 
210 	throbber->extent.x0 = x0;
211 	throbber->extent.y0 = y0;
212 	throbber->extent.x1 = x1;
213 	throbber->extent.y1 = y1;
214 
215 	return ro_gui_throbber_icon_resize(throbber);
216 }
217 
218 
219 /**
220  * Create or delete a throbber's icon if required to bring it into sync with
221  * the current hidden setting.
222  *
223  * \param *throbber		The throbber to update.
224  * \return			true if successful; else false.
225  */
226 
ro_gui_throbber_icon_update(struct throbber * throbber)227 bool ro_gui_throbber_icon_update(struct throbber *throbber)
228 {
229 	wimp_icon_create	icon;
230 	os_error		*error;
231 
232 	if (throbber == NULL || throbber->window == NULL)
233 		return false;
234 
235 	if (!throbber->hidden && throbber->icon == -1) {
236 		icon.w = throbber->window;
237 		icon.icon.extent.x0 = throbber->extent.x0;
238 		icon.icon.extent.y0 = throbber->extent.y0;
239 		icon.icon.extent.x1 = throbber->extent.x1;
240 		icon.icon.extent.y1 = throbber->extent.y1;
241 		icon.icon.flags = wimp_ICON_SPRITE | wimp_ICON_INDIRECTED |
242 				wimp_ICON_HCENTRED | wimp_ICON_VCENTRED;
243 		icon.icon.data.indirected_sprite.id =
244 				(osspriteop_id) throbber->sprite_name;
245 		icon.icon.data.indirected_sprite.area = throbber->sprites;
246 		icon.icon.data.indirected_sprite.size =
247 				THROBBER_SPRITE_NAME_LENGTH;
248 
249 		error = xwimp_create_icon(&icon, &throbber->icon);
250 		if (error != NULL) {
251 			NSLOG(netsurf, INFO, "xwimp_create_icon: 0x%x: %s",
252 			      error->errnum, error->errmess);
253 			ro_warn_user("WimpError", error->errmess);
254 			throbber->icon = -1;
255 			return false;
256 		}
257 
258 		if (!ro_gui_throbber_icon_resize(throbber))
259 			return false;
260 	} else if (throbber->hidden && throbber->icon != -1) {
261 		error = xwimp_delete_icon(throbber->window, throbber->icon);
262 		if (error != NULL) {
263 			NSLOG(netsurf, INFO, "xwimp_delete_icon: 0x%x: %s",
264 			      error->errnum, error->errmess);
265 			ro_warn_user("WimpError", error->errmess);
266 			return false;
267 		}
268 
269 		throbber->icon = -1;
270 	}
271 
272 	if (throbber->icon != -1)
273 		ro_gui_set_icon_shaded_state(throbber->window,
274 				throbber->icon, throbber->shaded);
275 
276 	return true;
277 }
278 
279 
280 /**
281  * Position the icons in the throbber to take account of the currently
282  * configured extent.
283  *
284  * \param *throbber		The throbber to update.
285  * \return			true if successful; else false.
286  */
287 
ro_gui_throbber_icon_resize(struct throbber * throbber)288 bool ro_gui_throbber_icon_resize(struct throbber *throbber)
289 {
290 
291 	if (throbber->window == NULL)
292 		return false;
293 
294 	if (throbber->icon != -1) {
295 		os_error *error;
296 		error = xwimp_resize_icon(throbber->window, throbber->icon,
297 				throbber->extent.x0, throbber->extent.y0,
298 				throbber->extent.x1, throbber->extent.y1);
299 		if (error != NULL) {
300 			NSLOG(netsurf, INFO, "xwimp_resize_icon: 0x%x: %s",
301 			      error->errnum, error->errmess);
302 			ro_warn_user("WimpError", error->errmess);
303 			throbber->icon = -1;
304 			return false;
305 		}
306 	}
307 
308 	return true;
309 }
310 
311 
312 /* This is an exported interface documented in throbber.h */
313 
ro_gui_throbber_hide(struct throbber * throbber,bool hide)314 bool ro_gui_throbber_hide(struct throbber *throbber, bool hide)
315 {
316 	if (throbber == NULL || throbber->hidden == hide)
317 		return (throbber == NULL) ? false : true;
318 
319 	throbber->hidden = hide;
320 
321 	return ro_gui_throbber_icon_update(throbber);
322 }
323 
324 
325 /* This is an exported interface documented in throbber.h */
326 
ro_gui_throbber_help_suffix(struct throbber * throbber,wimp_i i,os_coord * mouse,wimp_window_state * state,wimp_mouse_state buttons,const char ** suffix)327 bool ro_gui_throbber_help_suffix(struct throbber *throbber, wimp_i i,
328 		os_coord *mouse, wimp_window_state *state,
329 		wimp_mouse_state buttons, const char **suffix)
330 {
331 	os_coord			pos;
332 
333 	if (throbber == NULL || throbber->hidden)
334 		return false;
335 
336 	/* Check that the click was within our part of the window. */
337 
338 	pos.x = mouse->x - state->visible.x0 + state->xscroll;
339 	pos.y = mouse->y - state->visible.y1 + state->yscroll;
340 
341 	if (pos.x < throbber->extent.x0 || pos.x > throbber->extent.x1 ||
342 			pos.y < throbber->extent.y0 ||
343 			pos.y > throbber->extent.y1)
344 		return false;
345 
346 	/* Return a hard-coded icon number that matches the one that was
347 	 * always allocated to the throbber in a previous implementation.
348 	 * If Messages can be updated, this could be changed.
349 	 */
350 
351 	if (i == throbber->icon)
352 		*suffix = "16";
353 	else
354 		*suffix = "";
355 
356 	return true;
357 }
358 
359 
360 /* This is an exported interface documented in throbber.h */
361 
ro_gui_throbber_animate(struct throbber * throbber)362 bool ro_gui_throbber_animate(struct throbber *throbber)
363 {
364 	os_t	t;
365 	char	sprite_name[THROBBER_SPRITE_NAME_LENGTH];
366 
367 	if (throbber == NULL || throbber->hidden)
368 		return (throbber == NULL) ? false : true;
369 
370 	xos_read_monotonic_time(&t);
371 
372 	/* Drop out if we're not ready for the next frame, unless this
373 	 * call is to start animation from a stopped throbber (ie. if
374 	 * the current frame is 0).
375 	 */
376 
377 	if ((t < (throbber->last_update + THROBBER_ANIMATE_INTERVAL)) &&
378 			(throbber->current_frame > 0))
379 		return true;
380 
381 	throbber->last_update = t;
382 	throbber->current_frame++;
383 
384 	if (throbber->current_frame > throbber->max_frame)
385 		throbber->current_frame = 1;
386 
387 	snprintf(sprite_name, THROBBER_SPRITE_NAME_LENGTH,
388 			"throbber%i", throbber->current_frame);
389 	ro_gui_set_icon_string(throbber->window, throbber->icon,
390 			sprite_name, true);
391 
392 	if (throbber->force_redraw)
393 		ro_gui_force_redraw_icon(throbber->window, throbber->icon);
394 
395 	return true;
396 }
397 
398 
399 /* This is an exported interface documented in throbber.h */
400 
ro_gui_throbber_stop(struct throbber * throbber)401 bool ro_gui_throbber_stop(struct throbber *throbber)
402 {
403 	char	sprite_name[THROBBER_SPRITE_NAME_LENGTH];
404 
405 	if (throbber == NULL || throbber->hidden ||
406 			throbber->current_frame == 0)
407 		return (throbber == FALSE) ? false : true;
408 
409 	throbber->current_frame = 0;
410 	throbber->last_update = 0;
411 
412 	strcpy(sprite_name, "throbber0");
413 	ro_gui_set_icon_string(throbber->window, throbber->icon,
414 			sprite_name, true);
415 
416 	if (throbber->force_redraw)
417 		ro_gui_force_redraw_icon(throbber->window, throbber->icon);
418 
419 	return true;
420 }
421 
422