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