home *** CD-ROM | disk | FTP | other *** search
/ ARM Club 1 / ARM_CLUB_CD.iso / contents / apps / clib / progs / desklib / Libraries / Slider / c / Slider
Encoding:
Text File  |  1994-03-13  |  20.0 KB  |  654 lines

  1. /*
  2.     ####             #    #     # #
  3.     #   #            #    #       #          The FreeWare C library for 
  4.     #   #  ##   ###  #  # #     # ###             RISC OS machines
  5.     #   # #  # #     # #  #     # #  #   ___________________________________
  6.     #   # ####  ###  ##   #     # #  #                                      
  7.     #   # #        # # #  #     # #  #    Please refer to the accompanying
  8.     ####   ### ####  #  # ##### # ###    documentation for conditions of use
  9.     ________________________________________________________________________
  10.  
  11.     File:    Slider.Slider.c
  12.     Author:  Copyright © 1994 Peter Gaunt
  13.     Version: 1.00 (12 Mar 1994)
  14.     Purpose: Encapsulate slider-type icons in an easy-to-use system.
  15. */
  16.  
  17. #include "Core.h"
  18. #include "WimpSWIs.h"
  19. #include "Event.h"
  20. #include "GFX.h"
  21. #include "Sprite.h"
  22. #include "Screen.h"
  23. #include "ColourTran.h"
  24. #include "Coord.h"
  25. #include "Icon.h"
  26. #include "Slider.h"
  27.  
  28.  
  29. /****************************************************************************
  30.  
  31.   static int Slider_UserToSlider( slider_info *slider, int value )
  32.     
  33.   Inputs:   slider - the slider info for this slider.
  34.               value - the value (in user units) to be converted.
  35.   Returns:  The converted value.
  36.   Purpose:  Converts an external (user) value to an internal value in the
  37.               range 0 -> SLIDER_MAX.
  38.               If value is less than limits.min then returns 0.
  39.               If value is greater than limits.max then returns SLIDER_MAX.
  40.               If limits.min = limits.max then returns 0.
  41.   SeeAlso:  Slider_UserValue
  42.  
  43. ****************************************************************************/
  44.  
  45. static int Slider_UserToSlider( slider_info *slider, int value )
  46. {
  47.   int low  = slider->limits.min, 
  48.       high = slider->limits.max, 
  49.       range;
  50.       
  51.   double scale;
  52.  
  53.   /* 
  54.    * If the lower limit is higher than the upper limit, 
  55.    * then swap them over. 
  56.    */
  57.   if (low > high)
  58.   {
  59.     int temp = low;
  60.     low = high;
  61.     high = temp;
  62.   }
  63.  
  64.   /* Cope with out of range values, or zero ranges. */
  65.   if ( value > high )
  66.     return( SLIDER_MAX );
  67.   if (value < low || low == high )
  68.     return( 0 );
  69.  
  70.   /* Scale the value to slider units. */
  71.   value = value - low;
  72.   range = high - low;
  73.  
  74.   /* Work out the scaling ratio */
  75.   scale = ((double) SLIDER_MAX) / ((double) range);
  76.  
  77.   /* Scale the value and convert back to an integer */  
  78.   return ((int) (0.5 + (((double) value) * scale)));
  79. }
  80.  
  81.  
  82.  
  83.  
  84. /****************************************************************************
  85.  
  86.   static int Slider_UserValue( slider_info *slider, int value )
  87.     
  88.   Inputs:   slider - the slider info for this slider.
  89.               value - the value (in slider units) to be converted.
  90.   Returns:  The converted value.
  91.   Purpose:  Converts an internal slider value to an external (user) value
  92.   SeeAlso:  Slider_UserToSlider
  93.  
  94. ****************************************************************************/
  95.  
  96. static int Slider_UserValue( slider_info *slider, int value )
  97. {
  98.   int range;
  99.   double scale;
  100.  
  101.   /* Cope with zero ranges. */
  102.   if ( slider->limits.min == slider->limits.max )
  103.     return 0;
  104.     
  105.   /* Scale internal units to user units. */
  106.   range = slider->limits.max - slider->limits.min;
  107.   
  108.   /* Work out the scaling ratio */
  109.   scale = ((double) range) / ((double) SLIDER_MAX);
  110.  
  111.   /* Scale the value and convert back to an integer */  
  112.   return (slider->limits.min + (int) (0.5 + (((double) value) * scale)));
  113. }
  114.  
  115.  
  116.  
  117.  
  118.  
  119. /****************************************************************************
  120.  
  121. > os_error *Slider_Redraw(slider_info *slider, wimp_rect *clipwindow);
  122.  
  123.     
  124.   Inputs:   slider - the slider info for this slider.
  125.               clipwindow - the area of the window being redrawn (in screen
  126.               coordinates).
  127.   Returns:  An error pointer, or NULL if no errors occured.
  128.   Purpose:  Redraws a slider icon - call this function from within your 
  129.               redraw loops.
  130.             If clipwindow != NULL then does nothing if slider is outside 
  131.             clip window.
  132.   Errors:   Unable to use the colour indicated in 'slider'.
  133.   SeeAlso:  Slider_ReadValue; Slider_SetValue; Slider_Drag
  134.  
  135. ****************************************************************************/
  136.  
  137. extern os_error *Slider_Redraw( slider_info *slider, wimp_rect *clipwindow )
  138. {
  139.   wimp_rect rect;
  140.   int       sliderx, 
  141.               slidery, 
  142.               sliderwidth, 
  143.               sliderheight;
  144.   int       barsize, 
  145.               backsize;
  146.   os_error  *error;
  147.  
  148.   /* Give up if strange colours are requested. */
  149.   if ((slider->colour.background == -1) && (slider->colour.foreground == -1))
  150.     return( NULL );
  151.  
  152.   /* Obtain the icon's position in screen coordinates. */
  153.   Icon_ScreenPos( slider->window, slider->icon, &rect );
  154.  
  155.   /* If slider is outside the redraw region, then don't bother to draw it. */
  156.   if (clipwindow && !Coord_RectsOverlap(clipwindow, &rect))
  157.       return NULL;
  158.  
  159.   /* Compute slider dimensions. */
  160.   sliderx = rect.min.x + slider->border.x;
  161.   slidery = rect.min.y + slider->border.y;
  162.   sliderwidth  = rect.max.x - rect.min.x - ( slider->border.x * 2 )
  163.                   - screen_delta.x;
  164.   sliderheight = rect.max.y - rect.min.y - ( slider->border.y * 2 )
  165.                  - screen_delta.y;
  166.  
  167.   /* Work out the size of the slider 'bar'. */
  168.   if (slider->flags.vertical)
  169.     barsize = ( sliderheight * slider->value ) / SLIDER_MAX;
  170.   else
  171.     barsize = ( sliderwidth * slider->value ) / SLIDER_MAX;
  172.  
  173.   /*
  174.    * Draw the background of the slider using the colours specified in the 
  175.    * slider_info structure.
  176.    */
  177.    
  178.   if (slider->colour.background != -1)
  179.   {
  180.     if (!slider->flags.rgb)
  181.       error = Wimp_SetColour(slider->colour.background);
  182.     else
  183.       error = ColourTrans_SetGCOL(slider->colour.background, 0x100, 0);
  184.  
  185.     if (error != NULL)
  186.       return error;
  187.  
  188.     if (slider->flags.vertical)
  189.     {
  190.       backsize = sliderheight - barsize;
  191.       GFX_RectangleFill(sliderx, slidery + barsize, sliderwidth, backsize);
  192.     }
  193.     else
  194.     {
  195.       backsize = sliderwidth - barsize;
  196.       GFX_RectangleFill(sliderx + barsize, slidery, backsize, sliderheight);
  197.     }
  198.   }
  199.  
  200.   /*
  201.    * Draw the slider 'bar' itself, using the colours specified in the 
  202.    * slider_info structure.
  203.    */
  204.    
  205.   if ((slider->value > 0) && (slider->colour.foreground != -1))
  206.   {
  207.     if (!slider->flags.rgb)
  208.       error = Wimp_SetColour(slider->colour.foreground);
  209.     else
  210.       error = ColourTrans_SetGCOL(slider->colour.foreground, 0x100, 0);
  211.  
  212.     if (error != NULL)
  213.       return error;
  214.  
  215.     if (slider->flags.vertical)
  216.     {
  217.       GFX_RectangleFill(sliderx, slidery, sliderwidth, barsize);
  218.     }
  219.     else
  220.     {
  221.       GFX_RectangleFill(sliderx, slidery, barsize, sliderheight);
  222.     }
  223.   }
  224.   
  225.   /* Everything went according to plan... */
  226.   return NULL;
  227. }
  228.  
  229.  
  230. /****************************************************************************
  231.  
  232.   int Slider_ReadValue(slider_info *slider);
  233.     
  234.   Inputs:   slider - the slider info for this slider.
  235.   Returns:  The current slider value, in user units.
  236.   Purpose:  Returns current slider setting in user units.
  237.             (i.e. between slider->limits.min and slider->limits.max)
  238.   SeeAlso:  Slider_SetValue; Slider_Drag; Slider_Redraw
  239.  
  240. ****************************************************************************/
  241.  
  242. extern int Slider_ReadValue(slider_info *slider)
  243. {
  244.   return(Slider_UserValue(slider, slider->value));
  245. }
  246.  
  247.  
  248. /****************************************************************************
  249.  
  250.   static os_error *Slider_Set(slider_info *slider, int value, 
  251.                                     void *ref, BOOL *stop)
  252.     
  253.   Inputs:   slider - the slider info for this slider.
  254.               value  - the value (in slider units) that the slider is to be
  255.                          set to.
  256.   Outputs:  stop - if non-NULL and the update callback function returned
  257.                        a non-NULL value, then stop is set to TRUE.
  258.   Returns:  Standard error block, or NULL if no errors encountered.
  259.   Purpose:  Sets slider to value (in internal units, 0-SLIDER_MAX).
  260.               Value is clamped to the range 0 - SLIDER_MAX.
  261.  
  262.               Redraws the window rectangle containing the icon using 
  263.               Wimp_UpdateWindow.
  264.  
  265.               The icon must not be overlapped by any areas of the window which 
  266.               need redrawing separately since they will not be redrawn here.
  267.  
  268.               If the slider->update function pointer is not NULL and the slider
  269.               value in *user units* changes then this calls the update
  270.               callback function.
  271.   Errors:   -
  272.   SeeAlso:  Slider_SetValue
  273.  
  274. ****************************************************************************/
  275.  
  276. static os_error *Slider_Set(slider_info *slider, int value, 
  277.                                 void *ref, BOOL *stop)
  278. {
  279.   icon_block         istate;
  280.   window_redrawblock redraw;
  281.   BOOL               more;
  282.   int                oldvalue, stopdrag;
  283.   os_error           *error;
  284.  
  285.   /* Limit value to the allowed range for slider units. */
  286.   if ( value < 0 )
  287.     value = 0;
  288.   if ( value > SLIDER_MAX )
  289.     value = SLIDER_MAX;
  290.  
  291.   /*
  292.    * Remember the current slider value, and set the slider to the new
  293.    * value.  This is so we can see if the slider value actually changes,
  294.    * and call the update callback function if it does.
  295.    */
  296.   oldvalue = slider->value;
  297.   slider->value = value;
  298.  
  299.   /* 
  300.    * Only redraw slider if value has changed.
  301.    * Helps prevent flicker.
  302.    */
  303.   if (oldvalue != value)
  304.   {
  305.     error = Wimp_GetIconState( slider->window, slider->icon, &istate );
  306.     if (error != NULL)
  307.       return error;
  308.  
  309.     redraw.window = slider->window;
  310.     redraw.rect   = istate.workarearect;
  311.  
  312.     error = Wimp_UpdateWindow( &redraw, &more );
  313.     if (error != NULL)
  314.       return error;
  315.  
  316.     while (more)
  317.     {
  318.       /* Redraw this part of the slider */
  319.       error = Slider_Redraw( slider, &redraw.cliprect );
  320.       if (error == NULL)
  321.         error = Wimp_GetRectangle( &redraw, &more );
  322.       if (error != NULL)
  323.         return error;
  324.     }
  325.   }
  326.  
  327.   if (stop != NULL)
  328.     *stop = FALSE;
  329.  
  330.   /* Call updated function (if any) in main code if user value has changed */
  331.   value = Slider_UserValue(slider, value);
  332.   oldvalue = Slider_UserValue(slider, oldvalue);
  333.   if (slider->update && (value != oldvalue))
  334.   {
  335.     stopdrag = slider->update(slider, ref);
  336.     if ((stop != NULL) && stopdrag && slider->flags.dragging)
  337.       *stop = TRUE;
  338.   }
  339.   
  340.   /* Everything went ok. */
  341.   return NULL;
  342. }
  343.  
  344.  
  345. /****************************************************************************
  346.  
  347.   os_error *Slider_SetValue(slider_info *slider,
  348.                             int value,
  349.                             int *valueset,
  350.                             void *ref );
  351.     
  352.   Inputs:   slider - the slider info for this slider.
  353.               value  - the value (in user units) that the slider should be
  354.                          set to.
  355.               ref    - a reference to pass to the update callback funtion.
  356.   Outputs:  valueset - if not NULL, this is updated to hold the value
  357.                        actually the slider is actually set to (this can be
  358.                        different to 'value', e.g. if value is outside the
  359.                        slider limits).
  360.   Returns:  Standard error block or NULL if no error occurs.
  361.   Purpose:  Sets slider to value in value (user units).
  362.               If the slider is being dragged (i.e. if slider->status.dragging 
  363.               is set) then the function does nothing.
  364.  
  365.             The value is clamped to numbers between slider->limits.min and
  366.             slider->limits.max.
  367.  
  368.             The slider->update function (if any) will be called if the value
  369.             has changed.
  370.  
  371.             Can also be used to alter other settings (e.g. colour) by 
  372.             directly changing the slider structure before calling.
  373.   Errors:   An error is returned if there is a problem accessing or
  374.               redrawing the icon.
  375.   SeeAlso:  Slider_ReadValue; Slider_Drag; Slider_Redraw
  376.  
  377. ****************************************************************************/
  378.  
  379. extern os_error *Slider_SetValue(slider_info *slider, int value, 
  380.                                          int *valueset, void *ref )
  381. {
  382.   os_error *error;
  383.  
  384.   /* Ignore if slider is being dragged */
  385.   if (slider->flags.dragging)
  386.   {
  387.     if (valueset != NULL)
  388.       *valueset = Slider_ReadValue(slider);
  389.     return NULL;
  390.   }
  391.  
  392.   error = Slider_Set(slider, Slider_UserToSlider(slider, value), ref, NULL);
  393.   if (valueset != NULL)
  394.     *valueset = Slider_ReadValue(slider);
  395.  
  396.   return error;
  397. }
  398.  
  399.  
  400.  
  401.  
  402.  
  403. /****************************************************************************
  404.  
  405.   static os_error *Slider_Update(slider_info *slider, 
  406.                                          void *ref, BOOL *stop)
  407.  
  408.   Inputs:   slider - the slider info for this slider.
  409.               ref    - the reference to be passed to the update callback.
  410.   Outputs:  stop - if not NULL, then if the update callback returns a value
  411.               indicating that dragging should stop, then stop is set to TRUE.
  412.   Returns:  Standard error block, or NULL if no errors encountered.
  413.   Purpose:  Updates slider from current mouse pointer position.
  414.               If value differs from that in slider->value then calls Slider_Set
  415.               (which in turn calls the slider's slider->update function, if 
  416.               any).
  417.   Errors:   Unable to get the mouse position, or unable to redraw or access
  418.               the slider icon in some way.
  419.  
  420. ****************************************************************************/
  421.  
  422. static os_error *Slider_Update( slider_info *slider, void *ref, BOOL *stop )
  423. {
  424.   wimp_rect   rect;
  425.   mouse_block mouse;
  426.   int         sliderx, 
  427.                 slidery, 
  428.                 sliderwidth, 
  429.                 sliderheight, 
  430.                 sliderpos;
  431.   os_error    *error;
  432.  
  433.   /* Find out where the mouse pointer is. */
  434.   error = Wimp_GetPointerInfo(&mouse);
  435.   if (error != NULL)
  436.     return error;
  437.  
  438.   /* Obtain the icon's position in screen coordinates. */
  439.   Icon_ScreenPos(slider->window, slider->icon, &rect);
  440.  
  441.   /* Calculate the slider dimensions. */
  442.   if (slider->flags.vertical)
  443.   {
  444.     sliderheight = rect.max.y - rect.min.y - (slider->border.y * 2);
  445.     slidery      = rect.min.y + slider->border.y;
  446.     sliderpos = (mouse.pos.y - slidery) * SLIDER_MAX;
  447.     sliderpos = (int) (0.5 + (((double) sliderpos) / ((double) sliderheight)));
  448.   }
  449.   else
  450.   {
  451.     sliderwidth = rect.max.x - rect.min.x - ( slider->border.x * 2 );
  452.     sliderx     = rect.min.x + slider->border.x;
  453.     sliderpos = (mouse.pos.x - sliderx) * SLIDER_MAX;
  454.     sliderpos = (int) (0.5 + (((double) sliderpos) / ((double) sliderwidth)));
  455.   }
  456.  
  457.   /* Limit the slider's position to the legal range of slider values. */
  458.   if ( sliderpos < 0 )
  459.     sliderpos = 0;
  460.   if ( sliderpos > SLIDER_MAX )
  461.     sliderpos = SLIDER_MAX;
  462.  
  463.   /* If the slider position has changed, update the slider. */
  464.   if (sliderpos != slider->value)
  465.   {
  466.     if (!slider->flags.clickstop)
  467.       error = Slider_Set(slider, sliderpos, ref, stop);
  468.     else
  469.     /* If click stops then only redraw slider if *user* value has altered */
  470.     {
  471.       int newvalue = Slider_UserValue(slider, sliderpos);
  472.       
  473.       if (newvalue != Slider_UserValue(slider, slider->value))
  474.       {
  475.         /*
  476.          * This oddity simply ensures that slider is set to an internal value
  477.          * corresponding to an integral user value. Does this by converting
  478.          * internal value to user value and then back again.
  479.          */
  480.         error = Slider_Set(slider, Slider_UserToSlider(slider, newvalue),
  481.                            ref, stop );
  482.       }
  483.     }
  484.   }
  485.  
  486.   /* Inform caller of any problems. */
  487.   return error;
  488. }
  489.  
  490. /****************************************************************************
  491.  
  492.   os_error *Slider_Drag(slider_info *slider,
  493.                         BOOL *closed,
  494.                         int *value,
  495.                         void *ref )
  496.     
  497.   Inputs:   slider - the slider info for this slider.
  498.               ref    - a reference to pass to the update callback funtion.
  499.   Outputs:  closed - if not NULL, and the window is closed during the drag,
  500.                          then closed is set to TRUE.
  501.               value  - if not NULL, then it is set to the slider value (in
  502.                          user units) on exit.
  503.   Returns:  Standard error block, or none if no errors encountered.
  504.   Purpose:  Drag a slider. Call when slider's base icon is clicked on.
  505.               Polls the Wimp, grabbing NULL events but passing the rest on to 
  506.               Event_Process.
  507.               Exits when dragging stops or the slider->update function (if any)
  508.               returns a non-NULL value.
  509.               Also exits if window is closed while dragging (see Outputs).
  510.   Errors:   An error is returned if there is a problem accessing or
  511.               redrawing the icon.
  512.   SeeAlso:  Slider_SetValue; Slider_ReadValue; Slider_Redraw
  513.  
  514. ****************************************************************************/
  515.  
  516. extern os_error *Slider_Drag(slider_info *slider, int *closed, 
  517.                                    int *value, void *ref)
  518. {
  519.   convert_block   convert;
  520.   window_state    state;
  521.   drag_block      dragdata;
  522.   icon_block      bicon;
  523.   event_pollblock *event;
  524.   event_pollmask  mask;
  525.   window_state    wstate;
  526.   BOOL            finished = FALSE;
  527.   os_error        *error;
  528.  
  529.   if (closed != NULL)
  530.     *closed = FALSE;
  531.  
  532.   error = Wimp_GetIconState(slider->window, slider->icon, &bicon);
  533.   if (error !=NULL)
  534.     return error;
  535.  
  536.   error= Wimp_GetWindowState(slider->window, &state);
  537.   if (error != NULL)
  538.     return error;
  539.     
  540.   /* Start the Wimp drag process */
  541.   convert.screenrect = state.openblock.screenrect;
  542.   convert.scroll     = state.openblock.scroll;
  543.  
  544.   dragdata.window       = slider->window;
  545.   dragdata.type         = drag_INVISIBLE;
  546.   dragdata.screenrect   = state.openblock.screenrect;
  547.   dragdata.parent.min.x = bicon.workarearect.min.x;
  548.   dragdata.parent.max.x = bicon.workarearect.max.x - screen_delta.x;
  549.   dragdata.parent.min.y = bicon.workarearect.min.y;
  550.   dragdata.parent.max.y = bicon.workarearect.max.y - screen_delta.y;
  551.  
  552.   Coord_RectToScreen( &dragdata.parent, &convert );
  553.  
  554.   error = Wimp_DragBox( &dragdata );
  555.   if (error != NULL)
  556.     return error;
  557.  
  558.   /*
  559.    * Inform the other functions in this module that the slider is
  560.    * currently being dragged.
  561.    */
  562.   slider->flags.dragging = 1;
  563.  
  564.   /* Try to update the slider once. */
  565.   error = Slider_Update(slider, ref, &finished);
  566.   if (error != NULL)
  567.     return error;
  568.  
  569.   /* Did the update callback ask for the drag to be terminated? */
  570.   if (finished)
  571.   {
  572.     /* Yes - tell the Wimp to cancel the drag. */
  573.     error = Wimp_DragBox((drag_block *) -1);
  574.     if (error != NULL)
  575.       return error;
  576.       
  577.     /*
  578.      * Inform the rest of the module that the slider is no longer
  579.      * being dragged.
  580.      */
  581.     slider->flags.dragging = 0;
  582.     
  583.     /* Return the current slider value to caller if required. */
  584.     if (value != NULL)
  585.       *value = Slider_ReadValue(slider);
  586.       
  587.     return NULL;
  588.   }
  589.  
  590.   /*
  591.    * Poll the Wimp for events.
  592.    * Null events are used to update/redraw the slider position.
  593.    * DragBox events are used to detect the end of the drag.
  594.    * All other events are passed on for normal processing by Event_Process.
  595.    */
  596.   event = &event_lastevent;
  597.   mask = event_mask;
  598.   mask.data.null = 0;
  599.  
  600.   while (!finished)
  601.   {
  602.     error = Wimp_Poll(mask, event);
  603.     if (error != NULL)
  604.       return error;
  605.  
  606.     switch (event->type)
  607.     {
  608.       case event_NULL:
  609.           error = Wimp_GetWindowState(slider->window, &wstate);
  610.         if (error != NULL)
  611.           return error;
  612.           
  613.         if (wstate.flags.data.open)
  614.         {
  615.           /* Window is still open - update the slider display. */
  616.           error = Slider_Update(slider, ref, &finished);
  617.             if (error != NULL)
  618.             return error;
  619.         }
  620.         else
  621.         {
  622.           /* Window has been closed during drag - terminate the drag. */
  623.             finished = TRUE;
  624.           if (closed != NULL)
  625.             *closed = TRUE;
  626.         }
  627.         break;
  628.  
  629.       case event_USERDRAG:
  630.           /* User has let go of the mouse button - the drag has finished. */
  631.         finished = TRUE;
  632.         break;
  633.  
  634.       default:
  635.           /* Pass on for usual event processing. */
  636.         Event_Process(event);
  637.         break;
  638.     }
  639.   }
  640.  
  641.   /* We're not dragging the slider any more. */  
  642.   slider->flags.dragging = 0;
  643.   
  644.   /* Ensure the Wimp's drag operation has finished. */
  645.   error = Wimp_DragBox((drag_block *) -1);
  646.  
  647.   /* Return the new slider value to the caller, if required. */
  648.   if (value != NULL)
  649.     *value = Slider_ReadValue(slider);
  650.  
  651.   /* Inform caller of any errors that occured. */
  652.   return error;
  653. }
  654.