home *** CD-ROM | disk | FTP | other *** search
- /*
- #### # # # #
- # # # # # The FreeWare C library for
- # # ## ### # # # # ### RISC OS machines
- # # # # # # # # # # # ___________________________________
- # # #### ### ## # # # #
- # # # # # # # # # # Please refer to the accompanying
- #### ### #### # # ##### # ### documentation for conditions of use
- ________________________________________________________________________
-
- File: Slider.Slider.c
- Author: Copyright © 1994 Peter Gaunt
- Version: 1.00 (12 Mar 1994)
- Purpose: Encapsulate slider-type icons in an easy-to-use system.
- */
-
- #include "Core.h"
- #include "WimpSWIs.h"
- #include "Event.h"
- #include "GFX.h"
- #include "Sprite.h"
- #include "Screen.h"
- #include "ColourTran.h"
- #include "Coord.h"
- #include "Icon.h"
- #include "Slider.h"
-
-
- /****************************************************************************
-
- static int Slider_UserToSlider( slider_info *slider, int value )
-
- Inputs: slider - the slider info for this slider.
- value - the value (in user units) to be converted.
- Returns: The converted value.
- Purpose: Converts an external (user) value to an internal value in the
- range 0 -> SLIDER_MAX.
- If value is less than limits.min then returns 0.
- If value is greater than limits.max then returns SLIDER_MAX.
- If limits.min = limits.max then returns 0.
- SeeAlso: Slider_UserValue
-
- ****************************************************************************/
-
- static int Slider_UserToSlider( slider_info *slider, int value )
- {
- int low = slider->limits.min,
- high = slider->limits.max,
- range;
-
- double scale;
-
- /*
- * If the lower limit is higher than the upper limit,
- * then swap them over.
- */
- if (low > high)
- {
- int temp = low;
- low = high;
- high = temp;
- }
-
- /* Cope with out of range values, or zero ranges. */
- if ( value > high )
- return( SLIDER_MAX );
- if (value < low || low == high )
- return( 0 );
-
- /* Scale the value to slider units. */
- value = value - low;
- range = high - low;
-
- /* Work out the scaling ratio */
- scale = ((double) SLIDER_MAX) / ((double) range);
-
- /* Scale the value and convert back to an integer */
- return ((int) (0.5 + (((double) value) * scale)));
- }
-
-
-
-
- /****************************************************************************
-
- static int Slider_UserValue( slider_info *slider, int value )
-
- Inputs: slider - the slider info for this slider.
- value - the value (in slider units) to be converted.
- Returns: The converted value.
- Purpose: Converts an internal slider value to an external (user) value
- SeeAlso: Slider_UserToSlider
-
- ****************************************************************************/
-
- static int Slider_UserValue( slider_info *slider, int value )
- {
- int range;
- double scale;
-
- /* Cope with zero ranges. */
- if ( slider->limits.min == slider->limits.max )
- return 0;
-
- /* Scale internal units to user units. */
- range = slider->limits.max - slider->limits.min;
-
- /* Work out the scaling ratio */
- scale = ((double) range) / ((double) SLIDER_MAX);
-
- /* Scale the value and convert back to an integer */
- return (slider->limits.min + (int) (0.5 + (((double) value) * scale)));
- }
-
-
-
-
-
- /****************************************************************************
-
- > os_error *Slider_Redraw(slider_info *slider, wimp_rect *clipwindow);
-
-
- Inputs: slider - the slider info for this slider.
- clipwindow - the area of the window being redrawn (in screen
- coordinates).
- Returns: An error pointer, or NULL if no errors occured.
- Purpose: Redraws a slider icon - call this function from within your
- redraw loops.
- If clipwindow != NULL then does nothing if slider is outside
- clip window.
- Errors: Unable to use the colour indicated in 'slider'.
- SeeAlso: Slider_ReadValue; Slider_SetValue; Slider_Drag
-
- ****************************************************************************/
-
- extern os_error *Slider_Redraw( slider_info *slider, wimp_rect *clipwindow )
- {
- wimp_rect rect;
- int sliderx,
- slidery,
- sliderwidth,
- sliderheight;
- int barsize,
- backsize;
- os_error *error;
-
- /* Give up if strange colours are requested. */
- if ((slider->colour.background == -1) && (slider->colour.foreground == -1))
- return( NULL );
-
- /* Obtain the icon's position in screen coordinates. */
- Icon_ScreenPos( slider->window, slider->icon, &rect );
-
- /* If slider is outside the redraw region, then don't bother to draw it. */
- if (clipwindow && !Coord_RectsOverlap(clipwindow, &rect))
- return NULL;
-
- /* Compute slider dimensions. */
- sliderx = rect.min.x + slider->border.x;
- slidery = rect.min.y + slider->border.y;
- sliderwidth = rect.max.x - rect.min.x - ( slider->border.x * 2 )
- - screen_delta.x;
- sliderheight = rect.max.y - rect.min.y - ( slider->border.y * 2 )
- - screen_delta.y;
-
- /* Work out the size of the slider 'bar'. */
- if (slider->flags.vertical)
- barsize = ( sliderheight * slider->value ) / SLIDER_MAX;
- else
- barsize = ( sliderwidth * slider->value ) / SLIDER_MAX;
-
- /*
- * Draw the background of the slider using the colours specified in the
- * slider_info structure.
- */
-
- if (slider->colour.background != -1)
- {
- if (!slider->flags.rgb)
- error = Wimp_SetColour(slider->colour.background);
- else
- error = ColourTrans_SetGCOL(slider->colour.background, 0x100, 0);
-
- if (error != NULL)
- return error;
-
- if (slider->flags.vertical)
- {
- backsize = sliderheight - barsize;
- GFX_RectangleFill(sliderx, slidery + barsize, sliderwidth, backsize);
- }
- else
- {
- backsize = sliderwidth - barsize;
- GFX_RectangleFill(sliderx + barsize, slidery, backsize, sliderheight);
- }
- }
-
- /*
- * Draw the slider 'bar' itself, using the colours specified in the
- * slider_info structure.
- */
-
- if ((slider->value > 0) && (slider->colour.foreground != -1))
- {
- if (!slider->flags.rgb)
- error = Wimp_SetColour(slider->colour.foreground);
- else
- error = ColourTrans_SetGCOL(slider->colour.foreground, 0x100, 0);
-
- if (error != NULL)
- return error;
-
- if (slider->flags.vertical)
- {
- GFX_RectangleFill(sliderx, slidery, sliderwidth, barsize);
- }
- else
- {
- GFX_RectangleFill(sliderx, slidery, barsize, sliderheight);
- }
- }
-
- /* Everything went according to plan... */
- return NULL;
- }
-
-
- /****************************************************************************
-
- int Slider_ReadValue(slider_info *slider);
-
- Inputs: slider - the slider info for this slider.
- Returns: The current slider value, in user units.
- Purpose: Returns current slider setting in user units.
- (i.e. between slider->limits.min and slider->limits.max)
- SeeAlso: Slider_SetValue; Slider_Drag; Slider_Redraw
-
- ****************************************************************************/
-
- extern int Slider_ReadValue(slider_info *slider)
- {
- return(Slider_UserValue(slider, slider->value));
- }
-
-
- /****************************************************************************
-
- static os_error *Slider_Set(slider_info *slider, int value,
- void *ref, BOOL *stop)
-
- Inputs: slider - the slider info for this slider.
- value - the value (in slider units) that the slider is to be
- set to.
- Outputs: stop - if non-NULL and the update callback function returned
- a non-NULL value, then stop is set to TRUE.
- Returns: Standard error block, or NULL if no errors encountered.
- Purpose: Sets slider to value (in internal units, 0-SLIDER_MAX).
- Value is clamped to the range 0 - SLIDER_MAX.
-
- Redraws the window rectangle containing the icon using
- Wimp_UpdateWindow.
-
- The icon must not be overlapped by any areas of the window which
- need redrawing separately since they will not be redrawn here.
-
- If the slider->update function pointer is not NULL and the slider
- value in *user units* changes then this calls the update
- callback function.
- Errors: -
- SeeAlso: Slider_SetValue
-
- ****************************************************************************/
-
- static os_error *Slider_Set(slider_info *slider, int value,
- void *ref, BOOL *stop)
- {
- icon_block istate;
- window_redrawblock redraw;
- BOOL more;
- int oldvalue, stopdrag;
- os_error *error;
-
- /* Limit value to the allowed range for slider units. */
- if ( value < 0 )
- value = 0;
- if ( value > SLIDER_MAX )
- value = SLIDER_MAX;
-
- /*
- * Remember the current slider value, and set the slider to the new
- * value. This is so we can see if the slider value actually changes,
- * and call the update callback function if it does.
- */
- oldvalue = slider->value;
- slider->value = value;
-
- /*
- * Only redraw slider if value has changed.
- * Helps prevent flicker.
- */
- if (oldvalue != value)
- {
- error = Wimp_GetIconState( slider->window, slider->icon, &istate );
- if (error != NULL)
- return error;
-
- redraw.window = slider->window;
- redraw.rect = istate.workarearect;
-
- error = Wimp_UpdateWindow( &redraw, &more );
- if (error != NULL)
- return error;
-
- while (more)
- {
- /* Redraw this part of the slider */
- error = Slider_Redraw( slider, &redraw.cliprect );
- if (error == NULL)
- error = Wimp_GetRectangle( &redraw, &more );
- if (error != NULL)
- return error;
- }
- }
-
- if (stop != NULL)
- *stop = FALSE;
-
- /* Call updated function (if any) in main code if user value has changed */
- value = Slider_UserValue(slider, value);
- oldvalue = Slider_UserValue(slider, oldvalue);
- if (slider->update && (value != oldvalue))
- {
- stopdrag = slider->update(slider, ref);
- if ((stop != NULL) && stopdrag && slider->flags.dragging)
- *stop = TRUE;
- }
-
- /* Everything went ok. */
- return NULL;
- }
-
-
- /****************************************************************************
-
- os_error *Slider_SetValue(slider_info *slider,
- int value,
- int *valueset,
- void *ref );
-
- Inputs: slider - the slider info for this slider.
- value - the value (in user units) that the slider should be
- set to.
- ref - a reference to pass to the update callback funtion.
- Outputs: valueset - if not NULL, this is updated to hold the value
- actually the slider is actually set to (this can be
- different to 'value', e.g. if value is outside the
- slider limits).
- Returns: Standard error block or NULL if no error occurs.
- Purpose: Sets slider to value in value (user units).
- If the slider is being dragged (i.e. if slider->status.dragging
- is set) then the function does nothing.
-
- The value is clamped to numbers between slider->limits.min and
- slider->limits.max.
-
- The slider->update function (if any) will be called if the value
- has changed.
-
- Can also be used to alter other settings (e.g. colour) by
- directly changing the slider structure before calling.
- Errors: An error is returned if there is a problem accessing or
- redrawing the icon.
- SeeAlso: Slider_ReadValue; Slider_Drag; Slider_Redraw
-
- ****************************************************************************/
-
- extern os_error *Slider_SetValue(slider_info *slider, int value,
- int *valueset, void *ref )
- {
- os_error *error;
-
- /* Ignore if slider is being dragged */
- if (slider->flags.dragging)
- {
- if (valueset != NULL)
- *valueset = Slider_ReadValue(slider);
- return NULL;
- }
-
- error = Slider_Set(slider, Slider_UserToSlider(slider, value), ref, NULL);
- if (valueset != NULL)
- *valueset = Slider_ReadValue(slider);
-
- return error;
- }
-
-
-
-
-
- /****************************************************************************
-
- static os_error *Slider_Update(slider_info *slider,
- void *ref, BOOL *stop)
-
- Inputs: slider - the slider info for this slider.
- ref - the reference to be passed to the update callback.
- Outputs: stop - if not NULL, then if the update callback returns a value
- indicating that dragging should stop, then stop is set to TRUE.
- Returns: Standard error block, or NULL if no errors encountered.
- Purpose: Updates slider from current mouse pointer position.
- If value differs from that in slider->value then calls Slider_Set
- (which in turn calls the slider's slider->update function, if
- any).
- Errors: Unable to get the mouse position, or unable to redraw or access
- the slider icon in some way.
-
- ****************************************************************************/
-
- static os_error *Slider_Update( slider_info *slider, void *ref, BOOL *stop )
- {
- wimp_rect rect;
- mouse_block mouse;
- int sliderx,
- slidery,
- sliderwidth,
- sliderheight,
- sliderpos;
- os_error *error;
-
- /* Find out where the mouse pointer is. */
- error = Wimp_GetPointerInfo(&mouse);
- if (error != NULL)
- return error;
-
- /* Obtain the icon's position in screen coordinates. */
- Icon_ScreenPos(slider->window, slider->icon, &rect);
-
- /* Calculate the slider dimensions. */
- if (slider->flags.vertical)
- {
- sliderheight = rect.max.y - rect.min.y - (slider->border.y * 2);
- slidery = rect.min.y + slider->border.y;
- sliderpos = (mouse.pos.y - slidery) * SLIDER_MAX;
- sliderpos = (int) (0.5 + (((double) sliderpos) / ((double) sliderheight)));
- }
- else
- {
- sliderwidth = rect.max.x - rect.min.x - ( slider->border.x * 2 );
- sliderx = rect.min.x + slider->border.x;
- sliderpos = (mouse.pos.x - sliderx) * SLIDER_MAX;
- sliderpos = (int) (0.5 + (((double) sliderpos) / ((double) sliderwidth)));
- }
-
- /* Limit the slider's position to the legal range of slider values. */
- if ( sliderpos < 0 )
- sliderpos = 0;
- if ( sliderpos > SLIDER_MAX )
- sliderpos = SLIDER_MAX;
-
- /* If the slider position has changed, update the slider. */
- if (sliderpos != slider->value)
- {
- if (!slider->flags.clickstop)
- error = Slider_Set(slider, sliderpos, ref, stop);
- else
- /* If click stops then only redraw slider if *user* value has altered */
- {
- int newvalue = Slider_UserValue(slider, sliderpos);
-
- if (newvalue != Slider_UserValue(slider, slider->value))
- {
- /*
- * This oddity simply ensures that slider is set to an internal value
- * corresponding to an integral user value. Does this by converting
- * internal value to user value and then back again.
- */
- error = Slider_Set(slider, Slider_UserToSlider(slider, newvalue),
- ref, stop );
- }
- }
- }
-
- /* Inform caller of any problems. */
- return error;
- }
-
- /****************************************************************************
-
- os_error *Slider_Drag(slider_info *slider,
- BOOL *closed,
- int *value,
- void *ref )
-
- Inputs: slider - the slider info for this slider.
- ref - a reference to pass to the update callback funtion.
- Outputs: closed - if not NULL, and the window is closed during the drag,
- then closed is set to TRUE.
- value - if not NULL, then it is set to the slider value (in
- user units) on exit.
- Returns: Standard error block, or none if no errors encountered.
- Purpose: Drag a slider. Call when slider's base icon is clicked on.
- Polls the Wimp, grabbing NULL events but passing the rest on to
- Event_Process.
- Exits when dragging stops or the slider->update function (if any)
- returns a non-NULL value.
- Also exits if window is closed while dragging (see Outputs).
- Errors: An error is returned if there is a problem accessing or
- redrawing the icon.
- SeeAlso: Slider_SetValue; Slider_ReadValue; Slider_Redraw
-
- ****************************************************************************/
-
- extern os_error *Slider_Drag(slider_info *slider, int *closed,
- int *value, void *ref)
- {
- convert_block convert;
- window_state state;
- drag_block dragdata;
- icon_block bicon;
- event_pollblock *event;
- event_pollmask mask;
- window_state wstate;
- BOOL finished = FALSE;
- os_error *error;
-
- if (closed != NULL)
- *closed = FALSE;
-
- error = Wimp_GetIconState(slider->window, slider->icon, &bicon);
- if (error !=NULL)
- return error;
-
- error= Wimp_GetWindowState(slider->window, &state);
- if (error != NULL)
- return error;
-
- /* Start the Wimp drag process */
- convert.screenrect = state.openblock.screenrect;
- convert.scroll = state.openblock.scroll;
-
- dragdata.window = slider->window;
- dragdata.type = drag_INVISIBLE;
- dragdata.screenrect = state.openblock.screenrect;
- dragdata.parent.min.x = bicon.workarearect.min.x;
- dragdata.parent.max.x = bicon.workarearect.max.x - screen_delta.x;
- dragdata.parent.min.y = bicon.workarearect.min.y;
- dragdata.parent.max.y = bicon.workarearect.max.y - screen_delta.y;
-
- Coord_RectToScreen( &dragdata.parent, &convert );
-
- error = Wimp_DragBox( &dragdata );
- if (error != NULL)
- return error;
-
- /*
- * Inform the other functions in this module that the slider is
- * currently being dragged.
- */
- slider->flags.dragging = 1;
-
- /* Try to update the slider once. */
- error = Slider_Update(slider, ref, &finished);
- if (error != NULL)
- return error;
-
- /* Did the update callback ask for the drag to be terminated? */
- if (finished)
- {
- /* Yes - tell the Wimp to cancel the drag. */
- error = Wimp_DragBox((drag_block *) -1);
- if (error != NULL)
- return error;
-
- /*
- * Inform the rest of the module that the slider is no longer
- * being dragged.
- */
- slider->flags.dragging = 0;
-
- /* Return the current slider value to caller if required. */
- if (value != NULL)
- *value = Slider_ReadValue(slider);
-
- return NULL;
- }
-
- /*
- * Poll the Wimp for events.
- * Null events are used to update/redraw the slider position.
- * DragBox events are used to detect the end of the drag.
- * All other events are passed on for normal processing by Event_Process.
- */
- event = &event_lastevent;
- mask = event_mask;
- mask.data.null = 0;
-
- while (!finished)
- {
- error = Wimp_Poll(mask, event);
- if (error != NULL)
- return error;
-
- switch (event->type)
- {
- case event_NULL:
- error = Wimp_GetWindowState(slider->window, &wstate);
- if (error != NULL)
- return error;
-
- if (wstate.flags.data.open)
- {
- /* Window is still open - update the slider display. */
- error = Slider_Update(slider, ref, &finished);
- if (error != NULL)
- return error;
- }
- else
- {
- /* Window has been closed during drag - terminate the drag. */
- finished = TRUE;
- if (closed != NULL)
- *closed = TRUE;
- }
- break;
-
- case event_USERDRAG:
- /* User has let go of the mouse button - the drag has finished. */
- finished = TRUE;
- break;
-
- default:
- /* Pass on for usual event processing. */
- Event_Process(event);
- break;
- }
- }
-
- /* We're not dragging the slider any more. */
- slider->flags.dragging = 0;
-
- /* Ensure the Wimp's drag operation has finished. */
- error = Wimp_DragBox((drag_block *) -1);
-
- /* Return the new slider value to the caller, if required. */
- if (value != NULL)
- *value = Slider_ReadValue(slider);
-
- /* Inform caller of any errors that occured. */
- return error;
- }
-