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 * UTF8 status 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/wimp.h"
30 #include "oslib/wimpspriteop.h"
31
32 #include "utils/log.h"
33 #include "utils/utils.h"
34 #include "netsurf/plotters.h"
35
36 #include "riscos/gui.h"
37 #include "riscos/wimp.h"
38 #include "riscos/wimp_event.h"
39 #include "riscos/wimputils.h"
40 #include "riscos/font.h"
41 #include "riscos/gui/progress_bar.h"
42 #include "riscos/gui/status_bar.h"
43
44 #define ICON_WIDGET 0
45 #define WIDGET_WIDTH 12
46 #define PROGRESS_WIDTH 160
47
48 struct status_bar {
49 wimp_w w; /**< status bar window handle */
50 wimp_w parent; /**< parent window handle */
51 const char *text; /**< status bar text */
52 struct progress_bar *pb; /**< progress bar */
53 unsigned int scale; /**< current status bar scale */
54 int width; /**< current status bar width */
55 bool visible; /**< status bar is visible? */
56 };
57
58 static char status_widget_text[] = "";
59 static char status_widget_validation[] = "R5;Pptr_lr,8,6";
60
61 wimp_WINDOW(1) status_bar_definition = {
62 {0, 0, 1, 1},
63 0,
64 0,
65 wimp_TOP,
66 wimp_WINDOW_NEW_FORMAT | wimp_WINDOW_MOVEABLE |
67 wimp_WINDOW_FURNITURE_WINDOW |
68 wimp_WINDOW_IGNORE_XEXTENT,
69 wimp_COLOUR_BLACK,
70 wimp_COLOUR_LIGHT_GREY,
71 wimp_COLOUR_LIGHT_GREY,
72 wimp_COLOUR_VERY_LIGHT_GREY,
73 wimp_COLOUR_DARK_GREY,
74 wimp_COLOUR_MID_LIGHT_GREY,
75 wimp_COLOUR_CREAM,
76 wimp_WINDOW_NEVER3D | 0x16u /* RISC OS 5.03+ */,
77 {0, 0, 65535, 65535},
78 0,
79 0,
80 wimpspriteop_AREA,
81 1,
82 1,
83 {""},
84 1,
85 {
86 {
87 {0, 0, 1, 1},
88 wimp_ICON_TEXT | wimp_ICON_INDIRECTED |
89 wimp_ICON_BORDER | wimp_ICON_FILLED |
90 (wimp_COLOUR_LIGHT_GREY <<
91 wimp_ICON_BG_COLOUR_SHIFT) |
92 (wimp_BUTTON_CLICK_DRAG <<
93 wimp_ICON_BUTTON_TYPE_SHIFT),
94 {
95 .indirected_text = {
96 status_widget_text,
97 status_widget_validation,
98 1
99 }
100 }
101 }
102 }
103 };
104
105 static void ro_gui_status_bar_open(wimp_open *open);
106 static bool ro_gui_status_bar_click(wimp_pointer *pointer);
107 static void ro_gui_status_bar_redraw(wimp_draw *redraw);
108 static void ro_gui_status_bar_redraw_callback(void *handle);
109 static void ro_gui_status_position_progress_bar(struct status_bar *sb);
110
111
112 /**
113 * Create a new status bar
114 *
115 * \param parent the window to contain the status bar
116 * \param width the proportional width to use (0...10,000)
117 */
ro_gui_status_bar_create(wimp_w parent,unsigned int width)118 struct status_bar *ro_gui_status_bar_create(wimp_w parent, unsigned int width)
119 {
120 struct status_bar *sb;
121 os_error *error;
122
123 sb = calloc(1, sizeof(*sb));
124 if (!sb)
125 return NULL;
126
127 sb->pb = ro_gui_progress_bar_create();
128 if (!sb->pb)
129 return NULL;
130
131 error = xwimp_create_window((wimp_window *)&status_bar_definition,
132 &sb->w);
133 if (error) {
134 NSLOG(netsurf, INFO, "xwimp_create_window: 0x%x: %s",
135 error->errnum, error->errmess);
136 free(sb);
137 return NULL;
138 }
139 sb->parent = parent;
140 sb->scale = width;
141 sb->visible = true;
142
143 ro_gui_wimp_event_set_user_data(sb->w, sb);
144 ro_gui_wimp_event_register_open_window(sb->w,
145 ro_gui_status_bar_open);
146 ro_gui_wimp_event_register_mouse_click(sb->w,
147 ro_gui_status_bar_click);
148 ro_gui_wimp_event_register_redraw_window(sb->w,
149 ro_gui_status_bar_redraw);
150 ro_gui_wimp_event_set_help_prefix(sb->w, "HelpStatus");
151 ro_gui_status_bar_resize(sb);
152 return sb;
153 }
154
155
156 /**
157 * Destroy a status bar and free all associated resources
158 *
159 * \param sb the status bar to destroy
160 */
ro_gui_status_bar_destroy(struct status_bar * sb)161 void ro_gui_status_bar_destroy(struct status_bar *sb)
162 {
163 os_error *error;
164 assert(sb);
165
166 ro_gui_wimp_event_finalise(sb->w);
167 error = xwimp_delete_window(sb->w);
168 if (error) {
169 NSLOG(netsurf, INFO, "xwimp_delete_window: 0x%x:%s",
170 error->errnum, error->errmess);
171 }
172
173 ro_gui_progress_bar_destroy(sb->pb);
174
175 /* Remove any scheduled redraw callbacks */
176 riscos_schedule(-1, ro_gui_status_bar_redraw_callback, (void *) sb);
177
178 free(sb);
179 }
180
181
182 /**
183 * Get the handle of the window that represents a status bar
184 *
185 * \param sb the status bar to get the window handle of
186 * \return the status bar's window handle
187 */
ro_gui_status_bar_get_window(struct status_bar * sb)188 wimp_w ro_gui_status_bar_get_window(struct status_bar *sb)
189 {
190 assert(sb);
191
192 return sb->w;
193 }
194
195
196 /**
197 * Get the proportional width the status bar is currently using
198 *
199 * \param sb the status bar to get the width of
200 * \return the status bar's width (0...10,000)
201 */
ro_gui_status_bar_get_width(struct status_bar * sb)202 unsigned int ro_gui_status_bar_get_width(struct status_bar *sb)
203 {
204 assert(sb);
205
206 return sb->scale;
207 }
208
209
210 /**
211 * Set the visibility status of the status bar
212 *
213 * \param sb the status bar to check the visiblity of
214 * \param visible if the status bar should be shown or not.
215 * \return whether the status bar is visible
216 */
ro_gui_status_bar_set_visible(struct status_bar * sb,bool visible)217 void ro_gui_status_bar_set_visible(struct status_bar *sb, bool visible)
218 {
219 assert(sb);
220
221 sb->visible = visible;
222 if (visible) {
223 ro_gui_status_bar_resize(sb);
224 } else {
225 os_error *error = xwimp_close_window(sb->w);
226 if (error) {
227 NSLOG(netsurf, INFO, "xwimp_close_window: 0x%x:%s",
228 error->errnum, error->errmess);
229 }
230 }
231 }
232
233
234 /**
235 * Get the visibility status of the status bar
236 *
237 * \param sb the status bar to check the visiblity of
238 * \return whether the status bar is visible
239 */
ro_gui_status_bar_get_visible(struct status_bar * sb)240 bool ro_gui_status_bar_get_visible(struct status_bar *sb)
241 {
242 assert(sb);
243
244 return sb->visible;
245 }
246
247
248 /**
249 * Set the value of the progress bar
250 *
251 * \param sb the status bar to set the progress of
252 * \param value the value to use
253 */
ro_gui_status_bar_set_progress_value(struct status_bar * sb,unsigned int value)254 void ro_gui_status_bar_set_progress_value(struct status_bar *sb,
255 unsigned int value)
256 {
257 assert(sb);
258
259 ro_gui_status_bar_set_progress_range(sb,
260 max(value, ro_gui_progress_bar_get_range(sb->pb)));
261 ro_gui_progress_bar_set_value(sb->pb, value);
262 }
263
264
265 /**
266 * Set the range of the progress bar
267 *
268 * \param sb the status bar to set the range of
269 * \param range the range of the progress bar
270 */
ro_gui_status_bar_set_progress_range(struct status_bar * sb,unsigned int range)271 void ro_gui_status_bar_set_progress_range(struct status_bar *sb,
272 unsigned int range)
273 {
274 unsigned int old_range;
275
276 assert(sb);
277
278 old_range = ro_gui_progress_bar_get_range(sb->pb);
279 ro_gui_progress_bar_set_range(sb->pb, range);
280
281 NSLOG(netsurf, INFO, "Ranges are %i vs %i", old_range, range);
282 if ((old_range == 0) && (range != 0)) {
283 ro_gui_status_position_progress_bar(sb);
284 } else if ((old_range != 0) && (range == 0)) {
285 os_error *error = xwimp_close_window(
286 ro_gui_progress_bar_get_window(sb->pb));
287 if (error) {
288 NSLOG(netsurf, INFO, "xwimp_close_window: 0x%x:%s",
289 error->errnum, error->errmess);
290 }
291 }
292 }
293
294
295 /**
296 * Set the icon for the progress bar
297 *
298 * \param sb the status bar to set the icon for
299 * \param icon the icon to use, or NULL for no icon
300 */
ro_gui_status_bar_set_progress_icon(struct status_bar * sb,const char * icon)301 void ro_gui_status_bar_set_progress_icon(struct status_bar *sb,
302 const char *icon)
303 {
304 assert(sb);
305
306 ro_gui_progress_bar_set_icon(sb->pb, icon);
307 }
308
309
310 /**
311 * Set the text to display in the status bar
312 *
313 * \param sb the status bar to set the text for
314 * \param text the UTF8 text to display, or NULL for none
315 */
ro_gui_status_bar_set_text(struct status_bar * sb,const char * text)316 void ro_gui_status_bar_set_text(struct status_bar *sb, const char *text)
317 {
318 assert(sb);
319
320 sb->text = text;
321
322 /* Schedule a window redraw for 1cs' time.
323 *
324 * We do this to ensure that redraws as a result of text changes
325 * do not prevent other applications obtaining CPU time.
326 *
327 * The scheduled callback will be run when we receive the first
328 * null poll after 1cs has elapsed. It may then issue a redraw
329 * request to the Wimp.
330 *
331 * The scheduler ensures that only one instance of the
332 * { callback, handle } pair is registered at once.
333 */
334 if (sb->visible && text != NULL) {
335 riscos_schedule(10, ro_gui_status_bar_redraw_callback, sb);
336 }
337 }
338
339
340 /**
341 * Resize a status bar following a change in the dimensions of the
342 * parent window.
343 *
344 * \param sb the status bar to resize
345 */
ro_gui_status_bar_resize(struct status_bar * sb)346 void ro_gui_status_bar_resize(struct status_bar *sb)
347 {
348 int window_width;
349 int status_width, status_height;
350 wimp_window_state state;
351 os_error *error;
352 os_box extent;
353
354 if ((!sb) || (!sb->visible))
355 return;
356
357 /* get the window work area dimensions */
358 state.w = sb->parent;
359 error = xwimp_get_window_state(&state);
360 if (error) {
361 NSLOG(netsurf, INFO, "xwimp_get_window_state: 0x%x: %s",
362 error->errnum, error->errmess);
363 return;
364 }
365 window_width = state.visible.x1 - state.visible.x0;
366
367
368 /* recalculate the scaled width */
369 status_width = (window_width * sb->scale) / 10000;
370 if (status_width < WIDGET_WIDTH)
371 status_width = WIDGET_WIDTH;
372 status_height = ro_get_hscroll_height(sb->parent);
373
374 /* resize the status/resize icons */
375 if (status_width != sb->width) {
376 /* update the window extent */
377 int redraw_left, redraw_right;
378
379 extent.x0 = 0;
380 extent.y0 = 0;
381 extent.x1 = status_width;
382 extent.y1 = status_height - 4;
383 error = xwimp_set_extent(sb->w, &extent);
384 if (error) {
385 NSLOG(netsurf, INFO, "xwimp_set_extent: 0x%x: %s",
386 error->errnum, error->errmess);
387 return;
388 }
389
390 /* re-open the nested window */
391 state.w = sb->w;
392 state.xscroll = 0;
393 state.yscroll = 0;
394 state.next = wimp_TOP;
395 state.visible.x0 = state.visible.x0;
396 state.visible.y1 = state.visible.y0 - 2;
397 state.visible.x1 = state.visible.x0 + status_width;
398 state.visible.y0 = state.visible.y1 - status_height + 4;
399 error = xwimp_open_window_nested(PTR_WIMP_OPEN(&state),
400 sb->parent,
401 wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
402 << wimp_CHILD_XORIGIN_SHIFT |
403 wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
404 << wimp_CHILD_YORIGIN_SHIFT |
405 wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
406 << wimp_CHILD_LS_EDGE_SHIFT |
407 wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
408 << wimp_CHILD_BS_EDGE_SHIFT |
409 wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
410 << wimp_CHILD_RS_EDGE_SHIFT |
411 wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
412 << wimp_CHILD_TS_EDGE_SHIFT);
413 if (error) {
414 NSLOG(netsurf, INFO,
415 "xwimp_open_window_nested: 0x%x: %s",
416 error->errnum,
417 error->errmess);
418 return;
419 }
420 ro_gui_status_position_progress_bar(sb);
421 error = xwimp_resize_icon(sb->w, ICON_WIDGET,
422 status_width - WIDGET_WIDTH, 0,
423 status_width, status_height - 4);
424 if (error) {
425 NSLOG(netsurf, INFO, "xwimp_resize_icon: 0x%x: %s",
426 error->errnum, error->errmess);
427 return;
428 }
429
430 redraw_left = min(status_width, sb->width) - WIDGET_WIDTH - 2;
431 redraw_right = max(status_width, sb->width);
432 xwimp_force_redraw(sb->w, redraw_left, 0,
433 redraw_right, status_height);
434 sb->width = status_width;
435 }
436 }
437
438
439 /**
440 * Process a WIMP redraw request
441 *
442 * \param redraw the redraw request to process
443 */
ro_gui_status_bar_redraw(wimp_draw * redraw)444 void ro_gui_status_bar_redraw(wimp_draw *redraw)
445 {
446 struct status_bar *sb;
447 os_error *error;
448 osbool more;
449 rufl_code code;
450 struct redraw_context ctx = {
451 .interactive = true,
452 .background_images = true,
453 .plot = &ro_plotters
454 };
455 struct rect rect;
456
457 sb = (struct status_bar *)ro_gui_wimp_event_get_user_data(redraw->w);
458 assert(sb);
459
460 /* initialise the plotters */
461 ro_plot_origin_x = 0;
462 ro_plot_origin_y = 0;
463
464 /* redraw the window */
465 error = xwimp_redraw_window(redraw, &more);
466 if (error) {
467 NSLOG(netsurf, INFO, "xwimp_redraw_window: 0x%x: %s",
468 error->errnum, error->errmess);
469 return;
470 }
471 while (more) {
472 /* redraw the status text */
473 if (sb->text) {
474 error = xcolourtrans_set_font_colours(font_CURRENT,
475 0xeeeeee00, 0x00000000, 14, 0, 0, 0);
476 if (error) {
477 NSLOG(netsurf, INFO,
478 "xcolourtrans_set_font_colours: 0x%x: %s",
479 error->errnum,
480 error->errmess);
481 return;
482 }
483 code = rufl_paint(ro_gui_desktop_font_family,
484 ro_gui_desktop_font_style,
485 ro_gui_desktop_font_size,
486 sb->text, strlen(sb->text),
487 redraw->box.x0 + 6, redraw->box.y0 + 8,
488 rufl_BLEND_FONT);
489 if (code != rufl_OK) {
490 if (code == rufl_FONT_MANAGER_ERROR)
491 NSLOG(netsurf, INFO,
492 "rufl_FONT_MANAGER_ERROR: 0x%x: %s",
493 rufl_fm_error->errnum,
494 rufl_fm_error->errmess);
495 else
496 NSLOG(netsurf, INFO,
497 "rufl_paint: 0x%x", code);
498 }
499 }
500
501 rect.x0 = (redraw->box.x0 + sb->width - WIDGET_WIDTH - 2) >> 1;
502 rect.y0 = -redraw->box.y0 >> 1;
503 rect.x1 = (redraw->box.x0 + sb->width - WIDGET_WIDTH) >> 1;
504 rect.y1 = -redraw->box.y1 >> 1;
505
506 /* separate the widget from the text with a line */
507 ctx.plot->rectangle(&ctx,
508 plot_style_fill_black,
509 &rect);
510
511 error = xwimp_get_rectangle(redraw, &more);
512 if (error) {
513 NSLOG(netsurf, INFO, "xwimp_get_rectangle: 0x%x: %s",
514 error->errnum, error->errmess);
515 return;
516 }
517 }
518 }
519
520 /**
521 * Callback for scheduled redraw
522 *
523 * \param handle Callback handle
524 */
ro_gui_status_bar_redraw_callback(void * handle)525 void ro_gui_status_bar_redraw_callback(void *handle)
526 {
527 struct status_bar *sb = handle;
528
529 wimp_force_redraw(sb->w, 0, 0, sb->width - WIDGET_WIDTH, 65536);
530 }
531
532
533 /**
534 * Process an mouse_click event for a status window.
535 *
536 * \param pointer details of the mouse click
537 */
ro_gui_status_bar_click(wimp_pointer * pointer)538 bool ro_gui_status_bar_click(wimp_pointer *pointer)
539 {
540 wimp_drag drag;
541 os_error *error;
542
543 switch (pointer->i) {
544 case ICON_WIDGET:
545 drag.w = pointer->w;
546 drag.type = wimp_DRAG_SYSTEM_SIZE;
547 drag.initial.x0 = pointer->pos.x;
548 drag.initial.x1 = pointer->pos.x;
549 drag.initial.y0 = pointer->pos.y;
550 drag.initial.y1 = pointer->pos.y;
551 error = xwimp_drag_box(&drag);
552 if (error) {
553 NSLOG(netsurf, INFO,
554 "xwimp_drag_box: 0x%x: %s",
555 error->errnum,
556 error->errmess);
557 }
558 break;
559 }
560 return true;
561 }
562
563
564 /**
565 * Process an open_window request for a status window.
566 *
567 * \param open the request to process
568 */
ro_gui_status_bar_open(wimp_open * open)569 void ro_gui_status_bar_open(wimp_open *open)
570 {
571 struct status_bar *sb;
572 int window_width, status_width;
573 wimp_window_state state;
574 os_error *error;
575
576 /* get the parent width for scaling */
577 sb = (struct status_bar *)ro_gui_wimp_event_get_user_data(open->w);
578 state.w = sb->parent;
579 error = xwimp_get_window_state(&state);
580 if (error) {
581 NSLOG(netsurf, INFO, "xwimp_get_window_state: 0x%x: %s",
582 error->errnum, error->errmess);
583 return;
584 }
585 window_width = state.visible.x1 - state.visible.x0;
586 if (window_width == 0)
587 window_width = 1;
588 status_width = open->visible.x1 - open->visible.x0;
589 if (status_width <= 12)
590 status_width = 0;
591
592 /* store the new size */
593 sb->scale = (10000 * status_width) / window_width;
594 if (sb->scale > 10000)
595 sb->scale = 10000;
596 ro_gui_status_bar_resize(sb);
597 }
598
599
600 /**
601 * Reposition the progress component following a change in the
602 * dimension of the status window.
603 *
604 * \param sb the status bar to update
605 */
ro_gui_status_position_progress_bar(struct status_bar * sb)606 void ro_gui_status_position_progress_bar(struct status_bar *sb)
607 {
608 wimp_window_state state;
609 os_error *error;
610 int left, right;
611
612 if (!sb)
613 return;
614 if (ro_gui_progress_bar_get_range(sb->pb) == 0)
615 return;
616
617 /* get the window work area dimensions */
618 state.w = sb->w;
619 error = xwimp_get_window_state(&state);
620 if (error) {
621 NSLOG(netsurf, INFO, "xwimp_get_window_state: 0x%x: %s",
622 error->errnum, error->errmess);
623 return;
624 }
625
626 /* calculate the dimensions */
627 right = state.visible.x1 - WIDGET_WIDTH - 2;
628 left = max(state.visible.x0, right - PROGRESS_WIDTH);
629
630 /* re-open the nested window */
631 state.w = ro_gui_progress_bar_get_window(sb->pb);
632 state.xscroll = 0;
633 state.yscroll = 0;
634 state.next = wimp_TOP;
635 state.visible.x0 = left;
636 state.visible.x1 = right;
637 error = xwimp_open_window_nested(PTR_WIMP_OPEN(&state),
638 sb->w,
639 wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
640 << wimp_CHILD_XORIGIN_SHIFT |
641 wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
642 << wimp_CHILD_YORIGIN_SHIFT |
643 wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
644 << wimp_CHILD_LS_EDGE_SHIFT |
645 wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
646 << wimp_CHILD_BS_EDGE_SHIFT |
647 wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
648 << wimp_CHILD_RS_EDGE_SHIFT |
649 wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT
650 << wimp_CHILD_TS_EDGE_SHIFT);
651 if (error) {
652 NSLOG(netsurf, INFO, "xwimp_open_window: 0x%x: %s",
653 error->errnum, error->errmess);
654 }
655
656 /* update the progress bar display on non-standard width */
657 if ((right - left) != PROGRESS_WIDTH)
658 ro_gui_progress_bar_update(sb->pb, right - left,
659 state.visible.y1 - state.visible.y0);
660 }
661