1 /*
2 * Copyright 2006 Richard Wilson <info@tinct.net>
3 *
4 * This file is part of NetSurf, http://www.netsurf-browser.org/
5 *
6 * NetSurf is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * NetSurf is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 /** \file
20 * Progress bar (implementation).
21 */
22
23 #include <assert.h>
24 #include <stdbool.h>
25 #include <string.h>
26 #include "swis.h"
27 #include "oslib/colourtrans.h"
28 #include "oslib/os.h"
29 #include "oslib/osspriteop.h"
30 #include "oslib/wimp.h"
31 #include "oslib/wimpspriteop.h"
32
33 #include "utils/log.h"
34 #include "utils/utils.h"
35 #include "netsurf/plotters.h"
36
37 #include "riscos/gui.h"
38 #include "riscos/tinct.h"
39 #include "riscos/wimp_event.h"
40 #include "riscos/gui/progress_bar.h"
41
42 #define MARGIN 6
43
44 struct progress_bar {
45 wimp_w w; /**< progress bar window handle */
46 unsigned int range; /**< progress bar range */
47 unsigned int value; /**< progress bar value */
48 char icon[13]; /**< current icon */
49 int offset; /**< progress bar rotation */
50 os_box visible; /**< progress bar position */
51 int icon_x0; /**< icon x0 */
52 int icon_y0; /**< icon y0 */
53 osspriteop_header *icon_img; /**< icon image */
54 bool animating; /**< progress bar is animating */
55 bool recalculate; /**< recalculation required */
56 int cur_width; /**< current calculated width */
57 int cur_height; /**< current calculated height */
58 bool icon_obscured; /**< icon is partially obscured */
59 };
60
61 static char progress_animation_sprite[] = "progress";
62 static osspriteop_header *progress_icon;
63 static unsigned int progress_width;
64 static unsigned int progress_height;
65
66 struct wimp_window_base progress_bar_definition = {
67 {0, 0, 1, 1},
68 0,
69 0,
70 wimp_TOP,
71 wimp_WINDOW_NEW_FORMAT | wimp_WINDOW_MOVEABLE | wimp_WINDOW_NO_BOUNDS,
72 0xff,
73 wimp_COLOUR_LIGHT_GREY,
74 wimp_COLOUR_LIGHT_GREY,
75 wimp_COLOUR_VERY_LIGHT_GREY,
76 wimp_COLOUR_DARK_GREY,
77 wimp_COLOUR_MID_LIGHT_GREY,
78 wimp_COLOUR_CREAM,
79 wimp_WINDOW_NEVER3D | 0x16u /* RISC OS 5.03+ */,
80 {0, 0, 65535, 65535},
81 0,
82 0,
83 wimpspriteop_AREA,
84 1,
85 1,
86 {""},
87 0
88 };
89
90
91 static void ro_gui_progress_bar_calculate(struct progress_bar *pb, int width,
92 int height);
93 static void ro_gui_progress_bar_redraw(wimp_draw *redraw);
94 static void ro_gui_progress_bar_redraw_window(wimp_draw *redraw,
95 struct progress_bar *pb);
96 static void ro_gui_progress_bar_animate(void *p);
97
98
99 /**
100 * Initialise the progress bar
101 *
102 * \param icons the sprite area to use for icons
103 */
ro_gui_progress_bar_init(osspriteop_area * icons)104 void ro_gui_progress_bar_init(osspriteop_area *icons)
105 {
106 const char *name = progress_animation_sprite;
107 os_error *error;
108
109 progress_bar_definition.sprite_area = icons;
110
111 progress_icon = NULL;
112 error = xosspriteop_select_sprite(osspriteop_USER_AREA,
113 progress_bar_definition.sprite_area,
114 (osspriteop_id) name, &progress_icon);
115 if (!error) {
116 xosspriteop_read_sprite_info(osspriteop_USER_AREA,
117 progress_bar_definition.sprite_area,
118 (osspriteop_id) name,
119 (int *) &progress_width, (int *) &progress_height,
120 0, 0);
121 }
122 }
123
124
125 /**
126 * Create a new progress bar
127 */
ro_gui_progress_bar_create(void)128 struct progress_bar *ro_gui_progress_bar_create(void)
129 {
130 struct progress_bar *pb;
131 os_error *error;
132
133 pb = calloc(1, sizeof(*pb));
134 if (!pb)
135 return NULL;
136
137 error = xwimp_create_window((wimp_window *)&progress_bar_definition,
138 &pb->w);
139 if (error) {
140 NSLOG(netsurf, INFO, "xwimp_create_window: 0x%x: %s",
141 error->errnum, error->errmess);
142 free(pb);
143 return NULL;
144 }
145
146 ro_gui_wimp_event_register_redraw_window(pb->w,
147 ro_gui_progress_bar_redraw);
148 ro_gui_wimp_event_set_user_data(pb->w, pb);
149 return pb;
150 }
151
152
153 /**
154 * Destroy a progress bar and free all associated resources
155 *
156 * \param pb the progress bar to destroy
157 */
ro_gui_progress_bar_destroy(struct progress_bar * pb)158 void ro_gui_progress_bar_destroy(struct progress_bar *pb)
159 {
160 os_error *error;
161 assert(pb);
162
163 if (pb->animating) {
164 riscos_schedule(-1, ro_gui_progress_bar_animate, pb);
165 }
166 ro_gui_wimp_event_finalise(pb->w);
167 error = xwimp_delete_window(pb->w);
168 if (error) {
169 NSLOG(netsurf, INFO, "xwimp_delete_window: 0x%x:%s",
170 error->errnum, error->errmess);
171 }
172
173 free(pb);
174 }
175
176
177 /**
178 * Get the handle of the window that represents a progress bar
179 *
180 * \param pb the progress bar to get the window handle of
181 * \return the progress bar's window handle
182 */
ro_gui_progress_bar_get_window(struct progress_bar * pb)183 wimp_w ro_gui_progress_bar_get_window(struct progress_bar *pb)
184 {
185 assert(pb);
186
187 return pb->w;
188 }
189
190
191 /**
192 * Set the icon for a progress bar
193 *
194 * \param pb the progress bar to set the icon for
195 * \param icon the icon to use, or NULL for no icon
196 */
ro_gui_progress_bar_set_icon(struct progress_bar * pb,const char * icon)197 void ro_gui_progress_bar_set_icon(struct progress_bar *pb, const char *icon)
198 {
199 assert(pb);
200
201 if (!strcmp(icon, pb->icon))
202 return;
203 if (!icon)
204 pb->icon[0] = '\0';
205 else {
206 strncpy(pb->icon, icon, 12);
207 pb->icon[12] = '\0';
208 }
209 pb->recalculate = true;
210 xwimp_force_redraw(pb->w, 0, 0, 32, 32);
211 ro_gui_progress_bar_update(pb, pb->cur_width, pb->cur_height);
212 }
213
214
215 /**
216 * Set the value of a progress bar
217 *
218 * \param pb the progress bar to set the value for
219 * \param value the value to use
220 */
ro_gui_progress_bar_set_value(struct progress_bar * pb,unsigned int value)221 void ro_gui_progress_bar_set_value(struct progress_bar *pb, unsigned int value)
222 {
223 assert(pb);
224
225 pb->value = value;
226 if (pb->value > pb->range)
227 pb->range = pb->value;
228 ro_gui_progress_bar_update(pb, pb->cur_width, pb->cur_height);
229 }
230
231
232 /**
233 * Get the value of a progress bar
234 *
235 * \param pb the progress bar to get the value of
236 * \return the current value
237 */
ro_gui_progress_bar_get_value(struct progress_bar * pb)238 unsigned int ro_gui_progress_bar_get_value(struct progress_bar *pb)
239 {
240 assert(pb);
241
242 return pb->value;
243 }
244
245
246 /**
247 * Set the range of a progress bar
248 *
249 * \param pb the progress bar to set the range for
250 * \param range the range to use
251 */
ro_gui_progress_bar_set_range(struct progress_bar * pb,unsigned int range)252 void ro_gui_progress_bar_set_range(struct progress_bar *pb, unsigned int range)
253 {
254 assert(pb);
255
256 pb->range = range;
257 if (pb->value > pb->range)
258 pb->value = pb->range;
259 ro_gui_progress_bar_update(pb, pb->cur_width, pb->cur_height);
260 }
261
262
263 /**
264 * Get the range of a progress bar
265 *
266 * \param pb the progress bar to get the range of
267 * \return the current range
268 */
ro_gui_progress_bar_get_range(struct progress_bar * pb)269 unsigned int ro_gui_progress_bar_get_range(struct progress_bar *pb)
270 {
271 assert(pb);
272
273 return pb->range;
274 }
275
276
277 /**
278 * Update the progress bar to a new dimension.
279 *
280 * \param pb the progress bar to update
281 * \param width the new progress bar width
282 * \param height the new progress bar height
283 */
ro_gui_progress_bar_update(struct progress_bar * pb,int width,int height)284 void ro_gui_progress_bar_update(struct progress_bar *pb, int width, int height)
285 {
286 wimp_draw redraw;
287 os_error *error;
288 osbool more;
289 os_box cur;
290
291 /* don't allow negative dimensions */
292 width = max(width, 0);
293 height = max(height, 0);
294
295 /* update the animation state */
296 if ((pb->value == 0) || (pb->value == pb->range)) {
297 if (pb->animating) {
298 riscos_schedule(-1, ro_gui_progress_bar_animate, pb);
299 }
300 pb->animating = false;
301 } else {
302 if (!pb->animating) {
303 riscos_schedule(200, ro_gui_progress_bar_animate, pb);
304 }
305 pb->animating = true;
306 }
307
308 /* get old and new positions */
309 cur = pb->visible;
310 pb->recalculate = true;
311 ro_gui_progress_bar_calculate(pb, width, height);
312
313 /* see if the progress bar hasn't moved. we don't need to consider
314 * the left edge moving as this is handled by the icon setting
315 * function */
316 if (cur.x1 == pb->visible.x1)
317 return;
318
319 /* if size has decreased then we must force a redraw */
320 if (cur.x1 > pb->visible.x1) {
321 xwimp_force_redraw(pb->w, pb->visible.x1, pb->visible.y0,
322 cur.x1, pb->visible.y1);
323 return;
324 }
325
326 /* perform a minimal redraw update */
327 redraw.w = pb->w;
328 redraw.box = pb->visible;
329 redraw.box.x0 = cur.x1;
330 error = xwimp_update_window(&redraw, &more);
331 if (error) {
332 NSLOG(netsurf, INFO, "Error getting update window: 0x%x: %s",
333 error->errnum, error->errmess);
334 return;
335 }
336 if (more)
337 ro_gui_progress_bar_redraw_window(&redraw, pb);
338 }
339
340
341 /**
342 * Process a WIMP redraw request
343 *
344 * \param redraw the redraw request to process
345 */
ro_gui_progress_bar_redraw(wimp_draw * redraw)346 void ro_gui_progress_bar_redraw(wimp_draw *redraw)
347 {
348 struct progress_bar *pb;
349 os_error *error;
350 osbool more;
351
352 pb = (struct progress_bar *)ro_gui_wimp_event_get_user_data(redraw->w);
353 assert(pb);
354
355 error = xwimp_redraw_window(redraw, &more);
356 if (error) {
357 NSLOG(netsurf, INFO, "xwimp_redraw_window: 0x%x: %s",
358 error->errnum, error->errmess);
359 return;
360 }
361 if (more)
362 ro_gui_progress_bar_redraw_window(redraw, pb);
363 }
364
365
366 /**
367 * Animate the progress bar
368 *
369 * \param p the progress bar to animate
370 */
ro_gui_progress_bar_animate(void * p)371 void ro_gui_progress_bar_animate(void *p)
372 {
373 wimp_draw redraw;
374 os_error *error;
375 osbool more;
376 struct progress_bar *pb = p;
377
378 if (!progress_icon)
379 return;
380 pb->offset -= 6;
381 if (pb->offset < 0)
382 pb->offset += progress_width * 2;
383
384 if (pb->animating) {
385 riscos_schedule(200, ro_gui_progress_bar_animate, pb);
386 }
387
388 redraw.w = pb->w;
389 redraw.box = pb->visible;
390 error = xwimp_update_window(&redraw, &more);
391 if (error != NULL) {
392 NSLOG(netsurf, INFO, "Error getting update window: '%s'",
393 error->errmess);
394 return;
395 }
396 if (more)
397 ro_gui_progress_bar_redraw_window(&redraw, pb);
398 }
399
400
401 /**
402 * Calculate the position of the progress bar
403 *
404 * \param pb the progress bar to recalculate
405 * \param width the width of the progress bar
406 * \param height the height of the progress bar
407 * \return the address of the associated icon, or NULL
408 */
ro_gui_progress_bar_calculate(struct progress_bar * pb,int width,int height)409 void ro_gui_progress_bar_calculate(struct progress_bar *pb, int width,
410 int height)
411 {
412 int icon_width, icon_height;
413 int icon_x0 = 0, icon_y0 = 0, progress_x0, progress_x1;
414 osspriteop_header *icon = NULL;
415 bool icon_redraw = false;
416
417 /* try to use cached values */
418 if ((!pb->recalculate) && (pb->cur_width == width) &&
419 (pb->cur_height == height))
420 return;
421
422 /* update cache status */
423 pb->recalculate = false;
424 pb->cur_width = width;
425 pb->cur_height = height;
426
427 /* get the window dimensions */
428 width -= MARGIN * 2;
429 icon_width = icon_height = 0;
430 progress_x0 = MARGIN;
431
432 /* get the icon information */
433 if (progress_bar_definition.sprite_area != wimpspriteop_AREA) {
434 int progress_ymid = height / 2;
435 os_error *error;
436 error = xosspriteop_read_sprite_info(osspriteop_USER_AREA,
437 progress_bar_definition.sprite_area,
438 (osspriteop_id)pb->icon,
439 &icon_width, &icon_height, 0, 0);
440 if (!error) {
441 error = xosspriteop_select_sprite(osspriteop_USER_AREA,
442 progress_bar_definition.sprite_area,
443 (osspriteop_id)pb->icon, &icon);
444 }
445 if (!error) {
446 progress_x0 += 32 + MARGIN;
447 width -= 32 + MARGIN;
448 icon_x0 = MARGIN + 16 - icon_width;
449 icon_y0 = progress_ymid - icon_height;
450 if (width < -MARGIN) {
451 icon_x0 += width + MARGIN;
452 icon_redraw = true;
453 }
454 }
455 }
456
457 /* update the icon */
458 if ((pb->icon_obscured) || (icon_redraw)) {
459 if (icon_x0 != pb->icon_x0)
460 xwimp_force_redraw(pb->w, 0, 0, 32 + MARGIN, 65536);
461 }
462 pb->icon_obscured = icon_redraw;
463
464 progress_x1 = progress_x0;
465 if ((pb->range > 0) && (width > 0))
466 progress_x1 += (width * pb->value) / pb->range;
467
468 pb->visible.x0 = progress_x0;
469 pb->visible.y0 = MARGIN;
470 pb->visible.x1 = progress_x1;
471 pb->visible.y1 = height - MARGIN;
472 pb->icon_x0 = icon_x0;
473 pb->icon_y0 = icon_y0;
474 pb->icon_img = icon;
475 }
476
477
478 /**
479 * Redraw a section of a progress bar window
480 *
481 * \param redraw the section of the window to redraw
482 * \param pb the progress bar to redraw
483 */
ro_gui_progress_bar_redraw_window(wimp_draw * redraw,struct progress_bar * pb)484 void ro_gui_progress_bar_redraw_window(wimp_draw *redraw,
485 struct progress_bar *pb)
486 {
487 osbool more = true;
488 struct rect clip;
489 int progress_ymid;
490 struct redraw_context ctx = {
491 .interactive = true,
492 .background_images = true,
493 .plot = &ro_plotters
494 };
495
496 /* initialise the plotters */
497 ro_plot_origin_x = 0;
498 ro_plot_origin_y = 0;
499
500 /* recalculate the progress bar */
501 ro_gui_progress_bar_calculate(pb, redraw->box.x1 - redraw->box.x0,
502 redraw->box.y1 - redraw->box.y0);
503 progress_ymid = redraw->box.y0 + pb->visible.y0 +
504 ((pb->visible.y1 - pb->visible.y0) >> 1);
505
506 /* redraw the window */
507 while (more) {
508 os_error *error;
509 if (pb->icon)
510 _swix(Tinct_PlotAlpha, _IN(2) | _IN(3) | _IN(4) | _IN(7),
511 pb->icon_img,
512 redraw->box.x0 + pb->icon_x0,
513 redraw->box.y0 + pb->icon_y0,
514 tinct_ERROR_DIFFUSE);
515 if (!pb->icon_obscured) {
516 clip.x0 = max(redraw->clip.x0,
517 redraw->box.x0 + pb->visible.x0) >> 1;
518 clip.y0 = -min(redraw->clip.y1,
519 redraw->box.y0 + pb->visible.y1) >> 1;
520 clip.x1 = min(redraw->clip.x1,
521 redraw->box.x0 + pb->visible.x1) >> 1;
522 clip.y1 = -max(redraw->clip.y0,
523 redraw->box.y0 + pb->visible.y0) >> 1;
524 if ((clip.x0 < clip.x1) && (clip.y0 < clip.y1)) {
525 if (progress_icon) {
526 ctx.plot->clip(&ctx, &clip);
527 _swix(Tinct_Plot, _IN(2) | _IN(3) | _IN(4) | _IN(7),
528 progress_icon,
529 redraw->box.x0 - pb->offset,
530 progress_ymid - progress_height,
531 tinct_FILL_HORIZONTALLY);
532 } else {
533 ctx.plot->rectangle(&ctx,
534 plot_style_fill_red,
535 &clip);
536 }
537 }
538 }
539 error = xwimp_get_rectangle(redraw, &more);
540 if (error) {
541 NSLOG(netsurf, INFO, "xwimp_get_rectangle: 0x%x: %s",
542 error->errnum, error->errmess);
543 return;
544 }
545 }
546 }
547