/* * Copyright 2006 Richard Wilson * * This file is part of NetSurf, http://www.netsurf-browser.org/ * * NetSurf is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * NetSurf is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /** \file * Progress bar (implementation). */ #include #include #include #include "swis.h" #include "oslib/colourtrans.h" #include "oslib/os.h" #include "oslib/osspriteop.h" #include "oslib/wimp.h" #include "oslib/wimpspriteop.h" #include "desktop/plotters.h" #include "utils/log.h" #include "utils/utils.h" #include "riscos/gui.h" #include "riscos/tinct.h" #include "riscos/wimp_event.h" #include "riscos/gui/progress_bar.h" #define MARGIN 6 struct progress_bar { wimp_w w; /**< progress bar window handle */ unsigned int range; /**< progress bar range */ unsigned int value; /**< progress bar value */ char icon[13]; /**< current icon */ int offset; /**< progress bar rotation */ os_box visible; /**< progress bar position */ int icon_x0; /**< icon x0 */ int icon_y0; /**< icon y0 */ osspriteop_header *icon_img; /**< icon image */ bool animating; /**< progress bar is animating */ bool recalculate; /**< recalculation required */ int cur_width; /**< current calculated width */ int cur_height; /**< current calculated height */ bool icon_obscured; /**< icon is partially obscured */ }; static char progress_animation_sprite[] = "progress"; static osspriteop_header *progress_icon; static unsigned int progress_width; static unsigned int progress_height; struct wimp_window_base progress_bar_definition = { {0, 0, 1, 1}, 0, 0, wimp_TOP, wimp_WINDOW_NEW_FORMAT | wimp_WINDOW_MOVEABLE | wimp_WINDOW_NO_BOUNDS, 0xff, wimp_COLOUR_LIGHT_GREY, wimp_COLOUR_LIGHT_GREY, wimp_COLOUR_VERY_LIGHT_GREY, wimp_COLOUR_DARK_GREY, wimp_COLOUR_MID_LIGHT_GREY, wimp_COLOUR_CREAM, wimp_WINDOW_NEVER3D | 0x16u /* RISC OS 5.03+ */, {0, 0, 65535, 65535}, 0, 0, wimpspriteop_AREA, 1, 1, {""}, 0 }; static void ro_gui_progress_bar_calculate(struct progress_bar *pb, int width, int height); static void ro_gui_progress_bar_redraw(wimp_draw *redraw); static void ro_gui_progress_bar_redraw_window(wimp_draw *redraw, struct progress_bar *pb); static void ro_gui_progress_bar_animate(void *p); /** * Initialise the progress bar * * \param icons the sprite area to use for icons */ void ro_gui_progress_bar_init(osspriteop_area *icons) { const char *name = progress_animation_sprite; os_error *error; progress_bar_definition.sprite_area = icons; progress_icon = NULL; error = xosspriteop_select_sprite(osspriteop_USER_AREA, progress_bar_definition.sprite_area, (osspriteop_id) name, &progress_icon); if (!error) { xosspriteop_read_sprite_info(osspriteop_USER_AREA, progress_bar_definition.sprite_area, (osspriteop_id) name, (int *) &progress_width, (int *) &progress_height, 0, 0); } } /** * Create a new progress bar */ struct progress_bar *ro_gui_progress_bar_create(void) { struct progress_bar *pb; os_error *error; pb = calloc(1, sizeof(*pb)); if (!pb) return NULL; error = xwimp_create_window((wimp_window *)&progress_bar_definition, &pb->w); if (error) { LOG(("xwimp_create_window: 0x%x: %s", error->errnum, error->errmess)); free(pb); return NULL; } ro_gui_wimp_event_register_redraw_window(pb->w, ro_gui_progress_bar_redraw); ro_gui_wimp_event_set_user_data(pb->w, pb); return pb; } /** * Destroy a progress bar and free all associated resources * * \param pb the progress bar to destroy */ void ro_gui_progress_bar_destroy(struct progress_bar *pb) { os_error *error; assert(pb); if (pb->animating) schedule_remove(ro_gui_progress_bar_animate, pb); ro_gui_wimp_event_finalise(pb->w); error = xwimp_delete_window(pb->w); if (error) { LOG(("xwimp_delete_window: 0x%x:%s", error->errnum, error->errmess)); } free(pb); } /** * Get the handle of the window that represents a progress bar * * \param pb the progress bar to get the window handle of * \return the progress bar's window handle */ wimp_w ro_gui_progress_bar_get_window(struct progress_bar *pb) { assert(pb); return pb->w; } /** * Set the icon for a progress bar * * \param pb the progress bar to set the icon for * \param icon the icon to use, or NULL for no icon */ void ro_gui_progress_bar_set_icon(struct progress_bar *pb, const char *icon) { assert(pb); if (!strcmp(icon, pb->icon)) return; if (!icon) pb->icon[0] = '\0'; else { strncpy(pb->icon, icon, 12); pb->icon[12] = '\0'; } pb->recalculate = true; xwimp_force_redraw(pb->w, 0, 0, 32, 32); ro_gui_progress_bar_update(pb, pb->cur_width, pb->cur_height); } /** * Set the value of a progress bar * * \param pb the progress bar to set the value for * \param value the value to use */ void ro_gui_progress_bar_set_value(struct progress_bar *pb, unsigned int value) { assert(pb); pb->value = value; if (pb->value > pb->range) pb->range = pb->value; ro_gui_progress_bar_update(pb, pb->cur_width, pb->cur_height); } /** * Get the value of a progress bar * * \param pb the progress bar to get the value of * \return the current value */ unsigned int ro_gui_progress_bar_get_value(struct progress_bar *pb) { assert(pb); return pb->value; } /** * Set the range of a progress bar * * \param pb the progress bar to set the range for * \param range the range to use */ void ro_gui_progress_bar_set_range(struct progress_bar *pb, unsigned int range) { assert(pb); pb->range = range; if (pb->value > pb->range) pb->value = pb->range; ro_gui_progress_bar_update(pb, pb->cur_width, pb->cur_height); } /** * Get the range of a progress bar * * \param pb the progress bar to get the range of * \return the current range */ unsigned int ro_gui_progress_bar_get_range(struct progress_bar *pb) { assert(pb); return pb->range; } /** * Update the progress bar to a new dimension. * * \param pb the progress bar to update * \param width the new progress bar width * \param height the new progress bar height */ void ro_gui_progress_bar_update(struct progress_bar *pb, int width, int height) { wimp_draw redraw; os_error *error; osbool more; os_box cur; /* don't allow negative dimensions */ width = max(width, 0); height = max(height, 0); /* update the animation state */ if ((pb->value == 0) || (pb->value == pb->range)) { if (pb->animating) schedule_remove(ro_gui_progress_bar_animate, pb); pb->animating = false; } else { if (!pb->animating) schedule(20, ro_gui_progress_bar_animate, pb); pb->animating = true; } /* get old and new positions */ cur = pb->visible; pb->recalculate = true; ro_gui_progress_bar_calculate(pb, width, height); /* see if the progress bar hasn't moved. we don't need to consider * the left edge moving as this is handled by the icon setting * function */ if (cur.x1 == pb->visible.x1) return; /* if size has decreased then we must force a redraw */ if (cur.x1 > pb->visible.x1) { xwimp_force_redraw(pb->w, pb->visible.x1, pb->visible.y0, cur.x1, pb->visible.y1); return; } /* perform a minimal redraw update */ redraw.w = pb->w; redraw.box = pb->visible; redraw.box.x0 = cur.x1; error = xwimp_update_window(&redraw, &more); if (more) ro_gui_progress_bar_redraw_window(&redraw, pb); } /** * Process a WIMP redraw request * * \param redraw the redraw request to process */ void ro_gui_progress_bar_redraw(wimp_draw *redraw) { struct progress_bar *pb; os_error *error; osbool more; pb = (struct progress_bar *)ro_gui_wimp_event_get_user_data(redraw->w); assert(pb); error = xwimp_redraw_window(redraw, &more); if (error) { LOG(("xwimp_redraw_window: 0x%x: %s", error->errnum, error->errmess)); return; } if (more) ro_gui_progress_bar_redraw_window(redraw, pb); } /** * Animate the progress bar * * \param p the progress bar to animate */ void ro_gui_progress_bar_animate(void *p) { wimp_draw redraw; os_error *error; osbool more; struct progress_bar *pb = p; if (!progress_icon) return; pb->offset -= 6; if (pb->offset < 0) pb->offset += progress_width * 2; if (pb->animating) schedule(20, ro_gui_progress_bar_animate, pb); redraw.w = pb->w; redraw.box = pb->visible; error = xwimp_update_window(&redraw, &more); if (more) ro_gui_progress_bar_redraw_window(&redraw, pb); } /** * Calculate the position of the progress bar * * \param pb the progress bar to recalculate * \param width the width of the progress bar * \param height the height of the progress bar * \return the address of the associated icon, or NULL */ void ro_gui_progress_bar_calculate(struct progress_bar *pb, int width, int height) { os_error *error; int icon_width, icon_height; int icon_x0 = 0, icon_y0 = 0, progress_x0, progress_x1, progress_ymid = 0; osspriteop_header *icon = NULL; bool icon_redraw = false; /* try to use cached values */ if ((!pb->recalculate) && (pb->cur_width == width) && (pb->cur_height == height)) return; /* update cache status */ pb->recalculate = false; pb->cur_width = width; pb->cur_height = height; /* get the window dimensions */ width -= MARGIN * 2; icon_width = icon_height = 0; progress_x0 = MARGIN; /* get the icon information */ if (progress_bar_definition.sprite_area != wimpspriteop_AREA) { progress_ymid = height / 2; error = xosspriteop_read_sprite_info(osspriteop_USER_AREA, progress_bar_definition.sprite_area, (osspriteop_id)pb->icon, &icon_width, &icon_height, 0, 0); error = xosspriteop_select_sprite(osspriteop_USER_AREA, progress_bar_definition.sprite_area, (osspriteop_id)pb->icon, &icon); if (!error) { progress_x0 += 32 + MARGIN; width -= 32 + MARGIN; icon_x0 = MARGIN + 16 - icon_width; icon_y0 = progress_ymid - icon_height; if (width < -MARGIN) { icon_x0 += width + MARGIN; icon_redraw = true; } } } /* update the icon */ if ((pb->icon_obscured) || (icon_redraw)) { if (icon_x0 != pb->icon_x0) xwimp_force_redraw(pb->w, 0, 0, 32 + MARGIN, 65536); } pb->icon_obscured = icon_redraw; progress_x1 = progress_x0; if ((pb->range > 0) && (width > 0)) progress_x1 += (width * pb->value) / pb->range; pb->visible.x0 = progress_x0; pb->visible.y0 = MARGIN; pb->visible.x1 = progress_x1; pb->visible.y1 = height - MARGIN; pb->icon_x0 = icon_x0; pb->icon_y0 = icon_y0; pb->icon_img = icon; } /** * Redraw a section of a progress bar window * * \param redraw the section of the window to redraw * \param pb the progress bar to redraw */ void ro_gui_progress_bar_redraw_window(wimp_draw *redraw, struct progress_bar *pb) { os_error *error; osbool more = true; int clip_x0 = 0, clip_y0 = 0, clip_x1 = 0, clip_y1 = 0; int progress_ymid; /* initialise the plotters */ plot = ro_plotters; ro_plot_set_scale(1.0); ro_plot_origin_x = 0; ro_plot_origin_y = 0; /* recalculate the progress bar */ ro_gui_progress_bar_calculate(pb, redraw->box.x1 - redraw->box.x0, redraw->box.y1 - redraw->box.y0); progress_ymid = redraw->box.y0 + pb->visible.y0 + ((pb->visible.y1 - pb->visible.y0) >> 1); /* redraw the window */ while (more) { if (pb->icon) _swix(Tinct_PlotAlpha, _IN(2) | _IN(3) | _IN(4) | _IN(7), pb->icon_img, redraw->box.x0 + pb->icon_x0, redraw->box.y0 + pb->icon_y0, tinct_ERROR_DIFFUSE); if (!pb->icon_obscured) { clip_x0 = max(redraw->clip.x0, redraw->box.x0 + pb->visible.x0) >> 1; clip_y0 = -min(redraw->clip.y1, redraw->box.y0 + pb->visible.y1) >> 1; clip_x1 = min(redraw->clip.x1, redraw->box.x0 + pb->visible.x1) >> 1; clip_y1 = -max(redraw->clip.y0, redraw->box.y0 + pb->visible.y0) >> 1; if ((clip_x0 < clip_x1) && (clip_y0 < clip_y1)) { if (progress_icon) { plot.clip(clip_x0, clip_y0, clip_x1, clip_y1); _swix(Tinct_Plot, _IN(2) | _IN(3) | _IN(4) | _IN(7), progress_icon, redraw->box.x0 - pb->offset, progress_ymid - progress_height, tinct_FILL_HORIZONTALLY); } else { plot.fill(clip_x0, clip_y0, clip_x1, clip_y1, plot_style_fill_red); } } } error = xwimp_get_rectangle(redraw, &more); if (error) { LOG(("xwimp_get_rectangle: 0x%x: %s", error->errnum, error->errmess)); return; } } }