/*
 *	$Source: /u1/Xr/src/Xrlib/Editor/RCS/ScrollBar.c,v $
 *	$Header: ScrollBar.c,v 1.1 86/12/17 09:04:54 swick Exp $
 */

#ifndef lint
static char *rcsid_ScrollBar_c = "$Header: ScrollBar.c,v 1.1 86/12/17 09:04:54 swick Exp $";
#endif	lint


#include <Xr/xr-copyright.h>

/* $Header: ScrollBar.c,v 1.1 86/12/17 09:04:54 swick Exp $ */
/* Copyright 1986, Hewlett-Packard Company */
/* Copyright 1986, Massachussetts Institute of Technology */

static char rcsid[] = "$Header: ScrollBar.c,v 1.1 86/12/17 09:04:54 swick Exp $";
/*************************************<+>*************************************
 *****************************************************************************
 **
 **   File:        ScrollBar.c
 **
 **   Project:     X-ray Toolbox
 **
 **   Description: 
 **         Source code for the X-ray ScrollBar field editor.
 **
 **
 **   ------------------------ MODIFICATION RECORD   ------------------------
 *
 * $Log:	ScrollBar.c,v $
 * Revision 1.1  86/12/17  09:04:54  swick
 * Initial revision
 * 
 * Revision 7.0  86/11/13  08:27:23  08:27:23  fred ()
 * Final QA Release
 * 
 * Revision 6.1  86/11/11  15:22:20  15:22:20  fred ()
 * Fixed a NULL pointer bug in MSG_GETPARAMETERS.
 * 
 * Revision 6.0  86/11/10  15:35:39  15:35:39  fred ()
 * QA #2 release
 * 
 * Revision 5.3  86/11/07  14:22:54  14:22:54  fred ()
 * Added new copyright message.
 * 
 * Revision 5.2  86/11/04  09:01:48  09:01:48  fred ()
 * Fixed a bug dealing with the generation of a fake select up event.
 * The detail bits were being masked off.
 * 
 * Revision 5.1  86/10/30  13:28:49  13:28:49  fred ()
 * Added code to initialize the 'flags' field of the xr_PolyList structure.
 * 
 * Revision 5.0  86/10/28  08:33:54  08:33:54  fred ()
 * QA #1.1 release
 * 
 * Revision 4.2  86/10/23  09:09:00  09:09:00  fred ()
 * Removed unused variables.
 * 
 * Revision 4.1  86/10/22  14:51:40  14:51:40  fred ()
 * Filled in the procedure headers.
 * 
 * Revision 4.0  86/10/20  12:13:30  12:13:30  fred ()
 * QA #1 release
 * 
 * Revision 3.5  86/10/20  11:10:11  11:10:11  fred ()
 * Fixed an interactive slide bug, which occurred when the slide box
 * was the same size as the slide region.
 * 
 * Revision 3.4  86/10/16  09:17:27  09:17:27  fred ()
 * Performance enhanced: added use of register variables.
 * 
 * Revision 3.3  86/10/13  13:46:53  13:46:53  fred ()
 * Removed MSG_SLIDETOP message, because it could not work
 * 100% of the time, with the way scrollbars are implemented.
 * 
 * Revision 3.2  86/10/13  10:06:37  10:06:37  fred ()
 * Added use of the default tile, if needed.
 * 
 * Revision 3.1  86/10/09  07:54:41  07:54:41  fred ()
 * Added default color check to create routine.
 * 
 * Revision 3.0  86/10/02  16:01:25  16:01:25  fred ()
 * Alpha release set to 3.0
 * 
 * Revision 2.5  86/10/02  09:45:58  09:45:58  fred ()
 * Added MSG_SLIDETOP.
 * 
 * Revision 2.4  86/09/29  14:23:40  14:23:40  fred ()
 * Fixed an 'off by 1' error in CalculateSlideBox(), where the
 * slide box was divided by 'unitWid' instead of (unitWid + 1).
 * Caused the slide box to be too large.
 * 
 * Revision 2.3  86/09/24  11:15:30  11:15:30  fred ()
 * No longer swallow select up events when one of the
 * scroll arrows are selected; this allows an application
 * to do continuous scrolling, if it wants.
 * 
 * Revision 2.2  86/09/22  13:11:55  13:11:55  fred ()
 * Added calls to XrEditorGroup().
 * 
 * Revision 2.1  86/09/19  07:12:37  07:12:37  fred ()
 * Added a check for XrVISIBLE before doing any drawing.
 * 
 * Revision 2.0  86/09/16  08:10:03  08:10:03  fred ()
 * Updated all input processing routines to use new SELECT strategy.
 * 
 * Revision 1.4  86/09/16  05:49:58  05:49:58  fred ()
 * Modified to swallow a select up event.
 * 
 * Revision 1.3  86/09/04  11:51:59  11:51:59  fred ()
 * In invalidSBParameters(), the granularity value was being
 * checked, even though the interactive slide feature was disabled.
 * 
 * Revision 1.2  86/09/04  09:45:38  09:45:38  fred ()
 * In processScrollBar(), changed the size of the local variable
 * value1 from INT16 to INT8, to match the xrEvent structure.
 * 
 * Revision 1.1  86/09/03  13:57:43  13:57:43  fred ()
 * Initial revision
 * 
 *
 *****************************************************************************
 *************************************<+>*************************************/



#include <X/Xlib.h>
#include <Xr/defs.h>
#include <Xr/types.h>
#include <Xr/in_types.h>

extern INT32 drawScrollBar();
extern INT32 createScrollBar();
extern INT32 processScrollBar();
extern INT32 sbFreeMemory();


/* The default values for the scrollbar configuration parameters */
static xrSBParameters dft_configuration = {
     XrSCROLLARROWS | XrSLIDEBOX,
     0,
     100,
     0,
     10,
     NULL,
     10
};


/*************************************<->*************************************
 *
 *  xrEditor *
 *  XrScrollBar (scrollBar, message, data)
 *
 *     xrEditor * scrollBar;
 *     INT32      message;
 *     INT8     * data;
 *
 *   Description:
 *   -----------
 *     This is the main entry point and message handler for the scrollbar
 *     field editor.  It takes all message requests, and passes them onto
 *     the appropriate handler; invalid messages will fail and return an
 *     error.
 *
 *
 *   Inputs:
 *   ------
 *     scrollBar = For all messages but MSG_NEW and MSG_SIZE, this contains
 *                 the instance pointer.
 *
 *     message = This indicates which action the editor should perform.
 *
 *     data = This is the message specific data.  It's usage depends upon
 *            the 'message' parameter, and may be a scalar or a pointer.
 * 
 *   Outputs:
 *   -------
 *     Upon successful completion of a command, the instance pointer
 *          is returned; additional information may be returned by means
 *          of the 'data' parameter.
 *
 *     Upon failure, NULL is returned, and xrErrno is set.
 *
 *   Procedures Called
 *   -----------------
 *   _MsgNew()           [MsgCommon.c]
 *   _MsgFree()          [MsgCommon.c]
 *   _MsgGetState()      [MsgCommon.c]
 *   _MsgRedraw()        [MsgCommon.c]
 *   _MsgEdit()          [MsgCommon.c]
 *   XrCopyRect()        [calc.c]
 *   XrOffsetRect()      [calc.c]
 *   _XrMakeInvisible()  [editorUtil.c]
 *   XrEditorGroup()     [group.c]
 *   sizeScrollBar()
 *   drawScrollBar()
 *   createScrollBar()
 *   sbFreeMemory()
 *   invalidSBParameters()
 *
 *************************************<->***********************************/

xrEditor *
XrScrollBar (scrollBar, message, data)

   register xrEditor * scrollBar;
            INT32      message;
            INT8     * data;

{
   /* Determine the action being requested */
   switch (message)
   {
      case MSG_NEW:
      {
           /*
            * Create a new scrollbar instance.
            * The only parameter of interest is the 'data' parameter,
            * which is a pointer to a filled instance of the
            * xrScrollBarInfo structure.
            */
           return ((xrEditor *) _MsgNew (scrollBar, data,
                                         sizeof(xrScrollBarData),
                                         createScrollBar, drawScrollBar,
                                         sbFreeMemory, XrScrollBar, NULL));
      }

      case MSG_FREE:
      {
           /*
            * Destroy the specified scrollbar editor instance.
            * The 'scrollBar' parameter specifies the instance to
            * be destroyed; the 'data' parameter is unused.
            */
           return ((xrEditor *) _MsgFree (scrollBar, sbFreeMemory));
      }

      case MSG_GETSTATE:
      {
           /*
            * Return the settings of the state flags for the
            * specified scrollbar instance.  The 'scrollBar' parameter
            * specifies the instance to be queried, while the 'data'
            * parameter must point to an INT8 value, into which the
            * state flags will be placed.
            */
           return ((xrEditor *) _MsgGetState (scrollBar, data));
      }

      case MSG_SETSTATE:
      {
           /*
            * Change the state flag settings for the specified
            * instance.  The 'scrollBar' parameter specifies the
            * instance to be modified, and the 'data' parameter is
            * interpreted as an INT8 value, containing the new state
            * flag settings.
            */
           register INT8 oldState;
           register INT8 newState;

           if (scrollBar == NULL)
           {
              xrErrno = XrINVALIDID;
              return ((xrEditor *)NULL);
           }

           oldState = scrollBar->editorState;
           newState = (INT8) data;
           scrollBar->editorState = (INT8) data;

           /* Redraw the editor, using the new state */
           if (((oldState & XrVISIBLE) != (newState & XrVISIBLE)) ||
              ((newState & XrVISIBLE) &&
              ((oldState & XrSENSITIVE) != (newState & XrSENSITIVE))))
           {
              drawScrollBar (scrollBar, NULL);
           }
           return (scrollBar);
      }

      case MSG_SIZE:
      {
	   /*
            * Return the size of the rectangle needed to enclose
            * an instance of this editor, using the specifications
            * passed in by the application program.  The 'data'
            * parameter must point to a partially filled out instance
            * of the 'xrScrollBarInfo' structure; upon completion,
            * the 'editorRect' field of the 'info' structure will be
            * filled.
	    */
           xrScrollBarInfo *sbInfoPtr;

           sbInfoPtr = (xrScrollBarInfo *)data;

           if (sbInfoPtr == NULL)
           {
              xrErrno = XrINVALIDPTR;
              return ((xrEditor *)NULL);
           }
           else if (sizeScrollBar (sbInfoPtr, &sbInfoPtr->editorRect) == FALSE)
           {
              /* Size request failed; xrErrno set by sizeScrollBar() */
              return ((xrEditor *)NULL);
           }

           return ((xrEditor *) TRUE);
      }

      case MSG_REDRAW:
      {
         /*
          * Redraw a scrollbar instance.
          * The 'scrollBar' parameter specifies the instance to redraw,
          * while the 'data' parameter is interpreted as an INT32 value,
          * specifying the type of redraw to perform.
          */
         return ((xrEditor *) _MsgRedraw (scrollBar, data,
                                          drawScrollBar, NULL));
      }

      case MSG_MOVE:
      {
         /*
          * Relocate a scrollbar instance to a new location.
          * The 'scrollBar' parameter specifies the instance to
          * be moved, while the 'data' parameter must point to a 
          * POINT structure, containing the new editor rectangle
          * origin.
          */
                  POINT           * ptPtr = (POINT *) data;
         register xrScrollBarData * sbDataPtr;
                  RECTANGLE         workRect;
         register INT16             xDelta;
         register INT16             yDelta;
         register INT32             i;

         if (scrollBar == NULL)
         {
            xrErrno = XrINVALIDID;
            return ((xrEditor *) NULL);
         }
         else if (data == NULL)
         {
            xrErrno = XrINVALIDPTR;
            return ((xrEditor *) NULL);
         }

         /* Determine how much to move each of the scrollbar components */
         sbDataPtr = (xrScrollBarData *) scrollBar->editorData;
         XrCopyRect (&scrollBar->editorRect, &workRect);
         xDelta = ptPtr->x - scrollBar->editorRect.x;
         yDelta = ptPtr->y - scrollBar->editorRect.y;

         /* Move the origin of each of the scrollbar components */
         XrOffsetRect (&scrollBar->editorRect, xDelta, yDelta);
         XrOffsetRect (&sbDataPtr->arrow1, xDelta, yDelta);
         XrOffsetRect (&sbDataPtr->arrow2, xDelta, yDelta);
         XrOffsetRect (&sbDataPtr->slideArea, xDelta, yDelta);
         XrOffsetRect (&sbDataPtr->slideBox, xDelta, yDelta);

         /* Translate the scroll arrow vector lists */
         for (i = 0; i < 9; i++)
         {
            sbDataPtr->arrow1_vl[i].x += xDelta;
            sbDataPtr->arrow1_vl[i].y += yDelta;
            sbDataPtr->arrow2_vl[i].x += xDelta;
            sbDataPtr->arrow2_vl[i].y += yDelta;
         }

         if (scrollBar->editorState & XrVISIBLE)
         {
            /* Remove the instance from the window */
            _XrMakeInvisible (scrollBar->editorWindowId, &workRect, TRUE);

            /* Redisplay the instance */
            drawScrollBar (scrollBar, NULL);
         }

         /* Force the editor group rectangle to be recalculated */
         XrEditorGroup (NULL, MSG_ADJUSTGROUPRECT, scrollBar);
         return (scrollBar);
      }

      case MSG_RESIZE:
      {
         /*
          * Resize an existing scrollbar instance.
          * The 'scrollBar' parameter indicates the instance to
          * be resized, while the 'data' parameter must point to a
          * RECTANGLE structure, containing the new editor rectangle
          * definition.
          */
         register xrScrollBarData * sbDataPtr;
                  xrScrollBarInfo   sbInfo;
                  RECTANGLE         workRect;

         if (scrollBar == NULL)
         {
            xrErrno = XrINVALIDID;
            return ((xrEditor *) NULL);
         }
         else if (data == NULL)
         {
            xrErrno = XrINVALIDPTR;
            return ((xrEditor *) NULL);
         }

         /* Create a pseudo Info structure */
         sbDataPtr = (xrScrollBarData *) scrollBar->editorData;
         XrCopyRect (&scrollBar->editorRect, &workRect);
         XrCopyRect ((RECTANGLE *) data, &sbInfo.editorRect);
         sbInfo.orientation = sbDataPtr->orientation;
         _Xrmemcpy (&(sbInfo.configuration), &(sbDataPtr->configuration),
                 sizeof (xrSBParameters));

         if (createScrollBar (sbDataPtr, &sbInfo, MSG_RESIZE) == FALSE)
            /* xrErrno is set by createScrollBar() */
            return ((xrEditor *) NULL);

         /* Save the new rectangle definition */
         XrCopyRect (&sbInfo.editorRect, &scrollBar->editorRect);

         if (scrollBar->editorState & XrVISIBLE)
         {
            /* Remove the instance from the window */
            _XrMakeInvisible (scrollBar->editorWindowId, &workRect, TRUE);

            /* Redisplay the instance */
            drawScrollBar (scrollBar, NULL);
         }

         /* Force the editor group rectangle to be recalculated */
         XrEditorGroup (NULL, MSG_ADJUSTGROUPRECT, scrollBar);
         return (scrollBar);
      }

      case MSG_GETPARAMETERS:
      {
         /*
          * Return a copy of the current configuration parameters
          * associated with the specified scrollbar instance,
          * or the default setting, if the instance parameter is NULL.
          * The 'data' parameter must point to an instance of the
          * xrSBParameters structure, into which the configuration
          * data will be placed.
          */
         xrScrollBarData * sbDataPtr;

         if (data == NULL)
         {
            xrErrno = XrINVALIDPTR;
            return ((xrEditor *) NULL);
         }

         if (scrollBar)
         {
            sbDataPtr = (xrScrollBarData *)scrollBar->editorData;
            _Xrmemcpy(data, &sbDataPtr->configuration, sizeof (xrSBParameters));
         }
         else
            _Xrmemcpy(data, &dft_configuration, sizeof (xrSBParameters));

         return (scrollBar);
      }

      case MSG_SETPARAMETERS:
      {
         /*
          * Save a copy of the new configuration parameters, and redraw
          * any affected regions of the scrollbar.  The 'scrollBar'
          * parameter specifies the instance to be modified, while the
          * 'data' parameter must point to an instance of the xrSBParameters
          * structure, which must contain the new configuration values.
          */
         xrSBParameters  * sbParmPtr;
         xrScrollBarData * sbDataPtr;

         if (scrollBar == NULL)
         {
            xrErrno = XrINVALIDID;
            return ((xrEditor *) NULL);
         }
         else if (data == NULL)
         {
            xrErrno = XrINVALIDPTR;
            return ((xrEditor *) NULL);
         }

         /* Grab copies of the parameters we will be validating */
         sbParmPtr = (xrSBParameters *) data;
         sbDataPtr = (xrScrollBarData *) scrollBar->editorData;

         if (invalidSBParameters (sbDataPtr, sbParmPtr))
            /* xrErrno set by invalidSBParameters() */
            return ((xrEditor *) NULL);

         /* Redraw only the affected components */
         updateComponents (scrollBar, sbDataPtr);

         return (scrollBar);
      }

      case MSG_EDIT:
      {
	 /*
          * Process the incoming keystroke, and generate a return
          * keystroke, to indicate to the application program
          * how the editor instance was modified.  The 'data'
          * parameter must point to the event to be processed.
	  */
         return ((xrEditor *) _MsgEdit (scrollBar, data,
                                        processScrollBar, XrSCROLLBAR));
      }

      default:
         /* All other commands are invalid */
         xrErrno = XrINVALIDMSG;
         return ((xrEditor *)NULL);

   }  /* end of switch */
}  /* end of XrScrollBar() */


/*************************************<->*************************************
 *
 *  INT32
 *  sbFreeMemory (sbDataPtr)
 *
 *     xrScrollBarData * sbDataPtr;
 *
 *   Description:
 *   -----------
 *     This routine is called both when an existing instance is freed,
 *     and when a MSG_NEW request fails after createScrollBar() has
 *     been called.  It will free up all resources which createScrollBar()
 *     allocated for the instance.
 *
 *
 *   Inputs:
 *   ------
 *     sbDataPtr = Points to the instance's internal 'data' structure.
 * 
 *   Outputs:
 *   -------
 *
 *   Procedures Called
 *   -----------------
 *   XFreePixmap()  [libX.a]
 *
 *************************************<->***********************************/

static
INT32
sbFreeMemory (sbDataPtr)

   xrScrollBarData * sbDataPtr;

{
   if (sbDataPtr->sbTileId != xrDefaultTile)
      XFreePixmap (sbDataPtr->sbTileId);
}


/*************************************<->*************************************
 *
 *  INT32
 *  sizeScrollBar (sbInfoPtr, rectPtr)
 *
 *     xrScrollBarInfo * sbInfoPtr;
 *     RECTANGLE       * rectPtr;
 *
 *   Description:
 *   -----------
 *     This routine will validate the needed parameters contained within
 *     the 'info' structure pointed to by 'sbInfoPtr', and will then
 *     calculate the size of the editor rectangle needed to contain the
 *     described instance.
 *
 *
 *   Inputs:
 *   ------
 *     sbInfoPtr = Points to an instance of the 'xrScrollBarInfo' structure,
 *                 which describes the instance to be sized.
 *
 *     rectPtr = Points to a RECTANGLE structure, into which the editor
 *               rectangle definition will be placed.
 * 
 *   Outputs:
 *   -------
 *     Upon successful completion, TRUE is returned, and the editor
 *          rectangle is returned.
 *
 *     Upon failure, FALSE is returned, and xrErrno is set.
 *
 *   Procedures Called
 *   -----------------
 *
 *************************************<->***********************************/

static
INT32
sizeScrollBar (sbInfoPtr, rectPtr)

   register xrScrollBarInfo * sbInfoPtr;
   register RECTANGLE       * rectPtr;

{
   register INT16 bar_wid, arrow_ht, slide_ht;
            INT16 slide_wid;

   switch (sbInfoPtr->orientation)
   {
      case XrHORIZONTAL:
      {
   	 /* Verify that a valid height was supplied */
   	 if (rectPtr->height < XrMIN_SCROLLBAR_HT)
         {
            xrErrno = XrINVALIDPARM;
	    return (FALSE);
         }

	 /* Calculate the size of the components for the scrollbar */
	 bar_wid = rectPtr->height;
	 break; 
      }

      case XrVERTICAL:
      {
   	 /* Verify that a valid width was supplied */
   	 if (rectPtr->width < XrMIN_SCROLLBAR_WID)
         {
            xrErrno = XrINVALIDPARM;
	    return (FALSE);
         }

	 /* Calculate the size of the components for the scrollbar */
	 bar_wid = rectPtr->width;
	 break; 
      }

      default:
      {
	 /* Invalid scrollbar orientation value specified */
         xrErrno = XrINVALIDOPTION;
	 return (FALSE);
      }
   }

   arrow_ht = bar_wid + 1;
   slide_wid = bar_wid - 4;
   slide_ht =  slide_wid;

   /* Fill in the coordinates for the editor rectangle */
   rectPtr->x = 0;
   rectPtr->y = 0;
   if (sbInfoPtr->orientation == XrHORIZONTAL)
   {
      rectPtr->width = arrow_ht + 1 + slide_ht + 1 + arrow_ht;
      rectPtr->height = bar_wid;
   }
   else
   {
      rectPtr->width = bar_wid;
      rectPtr->height = arrow_ht + 1 + slide_ht + 1 + arrow_ht;
   }
   return (TRUE);
}


/*************************************<->*************************************
 *
 *  INT32
 *  createScrollBar (sbDataPtr, sbInfoPtr, message)
 *
 *     xrScrollBarData * sbDataPtr;
 *     xrScrollBarInfo * sbInfoPtr;
 *     INT32             message;
 *
 *   Description:
 *   -----------
 *     if (message == MSG_NEW)
 *     This routine is responsible for creating a new scrollbar instance.
 *     The description of the new instance is contained within the structure
 *     pointed to by the 'sbInfoPtr' parameter.  Besides allocating the
 *     resources needed for the new instance, this procedure will also fill
 *     in the structure pointed to by the 'sbDataPtr' parameter with the
 *     state information for the new instance.
 *
 *     if (message == MSG_RESIZE)
 *     This routine will take the information contained within the 
 *     'sbInfoPtr' structure, and will recalculate the location of each
 *     component within an existing scrollbar instance, to match the new
 *     data.  This will do no resource allocation.
 *
 *
 *   Inputs:
 *   ------
 *     sbDataPtr = Points to the 'data' structure for the instance being
 *                 created or resized.
 *
 *     sbInfoPtr = Points to the 'info' structure, which describes how the
 *                 instance is to be laid out.
 *
 *     message = This can be set to MSG_NEW or MSG_RESIZE.
 * 
 *   Outputs:
 *   -------
 *     Upon successful completion, TRUE is returned, and the 'sbDataPtr'
 *          structure is filled out.
 *
 *     Upon failure, FALSE is returned, and xrErrno is set.
 *
 *   Procedures Called
 *   -----------------
 *   XrCopyRect()   [calc.c]
 *   XrResource()   [resource.c]
 *   XMakePixmap()  [libX.a]
 *   CalculateSlideBox()
 *   MakeHorzArrows()
 *   MkaeVertArrows()
 *   sizeScrollBar()
 *   invalidSBParameters()
 *
 *************************************<->***********************************/

static
INT32
createScrollBar (sbDataPtr, sbInfoPtr, message)

   register xrScrollBarData * sbDataPtr;
   register xrScrollBarInfo * sbInfoPtr;
            INT32             message;

{
            INT8             orientation = sbInfoPtr->orientation;
            RECTANGLE        tempRect;
            RECTANGLE        workRect;
            RECTANGLE      * rectPtr;
   register INT16            bar_ht; 
            INT16            bar_wid;
            xrResourceInfo   bitmapInfo;

   /*
    * Validate the percent and orientation values.
    */
   if (invalidSBParameters (sbDataPtr, &sbInfoPtr->configuration))
   {
      /* xrErrno is set by invalidSBParameters() */
      return (FALSE);
   }
   else if (orientation != XrHORIZONTAL && orientation != XrVERTICAL)
   {
      xrErrno = XrINVALIDOPTION;
      return (FALSE);
   }

   /*
    * Make sure the editorRect is large enough to hold the instance.
    */
   XrCopyRect (&(sbInfoPtr->editorRect), &workRect);
   if (sizeScrollBar (sbInfoPtr, &workRect) == FALSE)
   {
      /* xrErrno is set by sizeScrollBar() */
      return (FALSE);
   }
   XrCopyRect (&(sbInfoPtr->editorRect), &tempRect);

   if (orientation == XrHORIZONTAL)
   {
      if ((tempRect.height < XrMIN_SCROLLBAR_HT) ||
         (tempRect.width < workRect.width))
      {
         xrErrno = XrINVALIDRECT;
         return (FALSE);
      }
   }
   else
   {
      if ((tempRect.width < XrMIN_SCROLLBAR_WID) ||
         (tempRect.height < workRect.height))
      {
         xrErrno = XrINVALIDRECT;
         return (FALSE);
      }
   }

   if (message == MSG_NEW)
   {
      /*
       * Now that we know all of the parameters are valid, we can start
       * filling in the structure pointed to by 'sbDataPtr'.  For starters,
       * we will copy out the data from the 'sbInfoPtr' structure which we
       * need.
       */
      sbDataPtr->orientation = orientation;
      sbDataPtr->sbFGColor = (sbInfoPtr->editorFGColor == -1) ?
                 xrForegroundColor : sbInfoPtr->editorFGColor;
      sbDataPtr->sbBGColor = (sbInfoPtr->editorBGColor == -1) ?
                 xrBackgroundColor : sbInfoPtr->editorBGColor;
   
      /*
       * Create the 50% tile we will need when drawing the background
       * for the scrollbar.
       */
      if ((sbInfoPtr->editorFGColor == -1) &&
          (sbInfoPtr->editorBGColor == -1))
      {
         /* Use the default 50% tile */
         sbDataPtr->sbTileId = xrDefaultTile;
      }
      else
      {
         bitmapInfo.resourceType = XrTYPE_BITMAPID;
         bitmapInfo.resourceId = XrPERCENT50;
         if ((XrResource (MSG_FIND, &bitmapInfo) == FALSE) ||
            ((sbDataPtr->sbTileId = 
               XMakePixmap (((xrBitmapId *)bitmapInfo.resourceObject)->bitmapId,
                            sbDataPtr->sbFGColor, sbDataPtr->sbBGColor)) == 0))
         {
            xrErrno = XrXCALLFAILED;
            return (FALSE);
         }
      }
   }

   /*
    * Generate the rectangles which enclose each of the scrollbar
    * components.  The formulae used will depend upon the orientation
    * of the scrollbar.
    */
   XrCopyRect (&(sbInfoPtr->editorRect), &workRect);
   XrCopyRect (&workRect, &(sbDataPtr->arrow1));
   XrCopyRect (&workRect, &(sbDataPtr->arrow2));
   rectPtr = &sbDataPtr->slideArea;
   XrCopyRect (&workRect, rectPtr);

   switch (orientation)
   {
      case XrHORIZONTAL:
      {
	   bar_ht = workRect.height;

           /* Scroll arrow1 */
           sbDataPtr->arrow1.width = bar_ht + 1;

           /* Scroll arrow2 */
           sbDataPtr->arrow2.x = (workRect.x + workRect.width - 1) - bar_ht;
           sbDataPtr->arrow2.width = bar_ht + 1;

           /* Slide area */
           rectPtr->x = (sbDataPtr->arrow1.x + sbDataPtr->arrow1.width - 1) + 2;
           rectPtr->width = (sbDataPtr->arrow2.x - rectPtr->x - 1);

           /* Slide box */
	   CalculateSlideBox (sbDataPtr);

           /* Lastly, generate the vector lists for the scroll arrows */
           MakeHorzArrows (sbDataPtr, workRect.height%2);

           break;
      }

      case XrVERTICAL:
      {
	   bar_wid = workRect.width;

           /* Scroll arrow1 */
           sbDataPtr->arrow1.height = bar_wid + 1;

           /* Scroll arrow2 */
           sbDataPtr->arrow2.y = (workRect.y + workRect.height - 1) - bar_wid;
           sbDataPtr->arrow2.height = bar_wid + 1;

           /* Slide area */
           rectPtr->y = (sbDataPtr->arrow1.y + sbDataPtr->arrow1.height - 1) +2;
           rectPtr->height = (sbDataPtr->arrow2.y - rectPtr->y - 1);

           /* Slide box */
	   CalculateSlideBox (sbDataPtr);

           /* Lastly, generate the vector lists for the scroll arrows */
           MakeVertArrows (sbDataPtr, workRect.width%2);

           break;
      }
   }

   return (TRUE);
}


/*************************************<->*************************************
 *
 *  INT32
 *  MakeHorzArrows (sbDataPtr, oddFlag)
 *
 *     xrScrollBarData * sbDataPtr;
 *     INT32             oddFlag;
 *
 *   Description:
 *   -----------
 *     This routine generates the endpoints which describe the two
 *     horizontal scrollbar arrows; the endpoints describe a polygon.
 *     The 'oddFlag' parameter signals whether the scrollbar height
 *     is an odd or even number of pixels.  The polygon endpoints are
 *     stored within the 'sbDataPtr' structure.
 *
 *
 *   Inputs:
 *   ------
 *     sbDataPtr = Points to the instance's internal 'data' structure.
 *
 *     oddFlag = TRUE if the scrollbar height is an odd number of pixels.
 * 
 *   Outputs:
 *   -------
 *
 *   Procedures Called
 *   -----------------
 *
 *************************************<->***********************************/

static
INT32
MakeHorzArrows (sbDataPtr, oddFlag)

   register xrScrollBarData * sbDataPtr;
            INT32             oddFlag;

{
   register POINT * arrow1 = sbDataPtr->arrow1_vl;
   register POINT * arrow2 = sbDataPtr->arrow2_vl;
   register INT16   left, top, right, bottom;


   /* Initialize the point count for each scroll arrow */
   sbDataPtr->pointCount = 8;

   /*
    * Get the info describing the rectangle into which scroll
    * arrow 1 must fit.
    */
   left = sbDataPtr->arrow1.x + 2;
   top = sbDataPtr->arrow1.y + 2;
   right = (sbDataPtr->arrow1.x + sbDataPtr->arrow1.width - 1) - 2;
   bottom = (sbDataPtr->arrow1.y + sbDataPtr->arrow1.height - 1) - 2;

   /* Start generating the vector lists */
   if (oddFlag)
   {
      /* Generate the endpoints for scroll arrow 1 */
      (arrow1 + 0)->x = left;
      (arrow1 + 0)->y = bottom - (bottom - top)/2;
      (arrow1 + 1)->x = left + (right - left)/2;
      (arrow1 + 1)->y = bottom;
      (arrow1 + 2)->x = left + (right - left)/2;
      (arrow1 + 2)->y = bottom - (bottom - top)/3;
      (arrow1 + 3)->x = right;
      (arrow1 + 3)->y = bottom - (bottom - top)/3;
      (arrow1 + 4)->x = right;
      (arrow1 + 4)->y = top + (bottom - top)/3;
      (arrow1 + 5)->x = left + (right - left)/2;
      (arrow1 + 5)->y = top + (bottom - top)/3;
      (arrow1 + 6)->x = left + (right - left)/2;
      (arrow1 + 6)->y = top;
      (arrow1 + 7)->x = left;
      (arrow1 + 7)->y = bottom - (bottom - top)/2;

      /*
       * Get the info describing the rectangle into which scroll
       * arrow 2 must fit.
       */
      left = sbDataPtr->arrow2.x + 2;
      top = sbDataPtr->arrow2.y + 2;
      right = (sbDataPtr->arrow2.x + sbDataPtr->arrow2.width - 1) - 2;
      bottom = (sbDataPtr->arrow2.y + sbDataPtr->arrow2.height - 1) - 2;

      /* Generate the endpoints for scroll arrow 2 */
      (arrow2 + 0)->x = right;
      (arrow2 + 0)->y = bottom - (bottom - top)/2;
      (arrow2 + 1)->x = right - (right - left)/2;
      (arrow2 + 1)->y = bottom;
      (arrow2 + 2)->x = right - (right - left)/2;
      (arrow2 + 2)->y = bottom - (bottom - top)/3;
      (arrow2 + 3)->x = left;
      (arrow2 + 3)->y = bottom - (bottom - top)/3;
      (arrow2 + 4)->x = left;
      (arrow2 + 4)->y = top + (bottom - top)/3;
      (arrow2 + 5)->x = right - (right - left)/2;
      (arrow2 + 5)->y = top + (bottom - top)/3;
      (arrow2 + 6)->x = right - (right - left)/2;
      (arrow2 + 6)->y = top;
      (arrow2 + 7)->x = right;
      (arrow2 + 7)->y = bottom - (bottom - top)/2;
   }
   else
   {
      /* Initialize the point count for each scroll arrow */
      sbDataPtr->pointCount++;

      /* Generate the endpoints for scroll arrow 1 */
      (arrow1 + 0)->x = left;
      (arrow1 + 0)->y = bottom - (bottom - top)/2;
      (arrow1 + 1)->x = left - 1 + (right - left)/2;
      (arrow1 + 1)->y = bottom;
      (arrow1 + 2)->x = left - 1 + (right - left)/2;
      (arrow1 + 2)->y = bottom - (bottom - top + 2)/3;
      (arrow1 + 3)->x = right;
      (arrow1 + 3)->y = bottom - (bottom - top + 2)/3;
      (arrow1 + 4)->x = right;
      (arrow1 + 4)->y = top + (bottom - top + 2)/3;
      (arrow1 + 5)->x = left - 1 + (right - left)/2;
      (arrow1 + 5)->y = top + (bottom - top + 2)/3;
      (arrow1 + 6)->x = left - 1 + (right - left)/2;
      (arrow1 + 6)->y = top;
      (arrow1 + 7)->x = left;
      (arrow1 + 7)->y = top + (bottom - top)/2;
      (arrow1 + 8)->x = left;
      (arrow1 + 8)->y = bottom - (bottom - top)/2;

      /*
       * Get the info describing the rectangle into which scroll
       * arrow 2 must fit.
       */
      left = sbDataPtr->arrow2.x + 2;
      top = sbDataPtr->arrow2.y + 2;
      right = (sbDataPtr->arrow2.x + sbDataPtr->arrow2.width - 1) - 2;
      bottom = (sbDataPtr->arrow2.y + sbDataPtr->arrow2.height - 1) - 2;

      (arrow2 + 0)->x = right;
      (arrow2 + 0)->y = bottom - (bottom - top)/2;
      (arrow2 + 1)->x = right + 1 - (right - left)/2;
      (arrow2 + 1)->y = bottom;
      (arrow2 + 2)->x = right + 1 - (right - left)/2;
      (arrow2 + 2)->y = bottom - (bottom - top + 2)/3;
      (arrow2 + 3)->x = left;
      (arrow2 + 3)->y = bottom - (bottom - top + 2)/3;
      (arrow2 + 4)->x = left;
      (arrow2 + 4)->y = top + (bottom - top + 2)/3;
      (arrow2 + 5)->x = right + 1 - (right - left)/2;
      (arrow2 + 5)->y = top + (bottom - top + 2)/3;
      (arrow2 + 6)->x = right + 1 - (right - left)/2;
      (arrow2 + 6)->y = top;
      (arrow2 + 7)->x = right;
      (arrow2 + 7)->y = top + (bottom - top)/2;
      (arrow2 + 8)->x = right;
      (arrow2 + 8)->y = bottom - (bottom - top)/2;
   }
}


/*************************************<->*************************************
 *
 *  INT32
 *  MakeVertArrows (sbDataPtr, oddFlag)
 *
 *     xrScrollBarData * sbDataPtr;
 *     INT32             oddFlag;
 *
 *   Description:
 *   -----------
 *     This routine generates the endpoints which describe the two
 *     vertical scrollbar arrows; the endpoints describe a polygon.
 *     The 'oddFlag' parameter signals whether the scrollbar width
 *     is an odd or even number of pixels.  The polygon endpoints are
 *     stored within the 'sbDataPtr' structure.
 *
 *
 *   Inputs:
 *   ------
 *     sbDataPtr = Points to the instance's internal 'data' structure.
 *
 *     oddFlag = TRUE if the scrollbar width is an odd number of pixels.
 * 
 *   Outputs:
 *   -------
 *
 *   Procedures Called
 *   -----------------
 *
 *************************************<->***********************************/

static
INT32
MakeVertArrows (sbDataPtr, oddFlag)

   register xrScrollBarData * sbDataPtr;
            INT32             oddFlag;

{
   register POINT * arrow1 = sbDataPtr->arrow1_vl;
   register POINT * arrow2 = sbDataPtr->arrow2_vl;
   register INT16   left, top, right, bottom;
            INT32   i;

   /* Initialize the point count for each scroll arrow */
   sbDataPtr->pointCount = 8;

   /*
    * Get the info describing the rectangle into which scroll
    * arrow 1 must fit.
    */
   left = sbDataPtr->arrow1.x + 2;
   top = sbDataPtr->arrow1.y + 2;
   right = (sbDataPtr->arrow1.x + sbDataPtr->arrow1.width - 1) - 2;
   bottom = (sbDataPtr->arrow1.y + sbDataPtr->arrow1.height - 1) - 2;

   /* Start generating the vector lists */
   if (oddFlag)
   {
      /* Generate the endpoints for scroll arrow 1 */
      (arrow1 + 0)->x = left + (right - left)/2;
      (arrow1 + 0)->y = top;
      (arrow1 + 1)->x = left;
      (arrow1 + 1)->y = top + (bottom - top)/2;
      (arrow1 + 2)->x = left + (right - left)/3;
      (arrow1 + 2)->y = top + (bottom - top)/2;
      (arrow1 + 3)->x = left + (right - left)/3;
      (arrow1 + 3)->y = bottom;
      (arrow1 + 4)->x = right - (right - left)/3;
      (arrow1 + 4)->y = bottom;
      (arrow1 + 5)->x = right - (right - left)/3;
      (arrow1 + 5)->y = top + (bottom - top)/2;
      (arrow1 + 6)->x = right;
      (arrow1 + 6)->y = top + (bottom - top)/2;
      (arrow1 + 7)->x = left + (right - left)/2;
      (arrow1 + 7)->y = top;

      /*
       * Get the info describing the rectangle into which scroll
       * arrow 2 must fit.
       */
      left = sbDataPtr->arrow2.x + 2;
      top = sbDataPtr->arrow2.y + 2;
      right = (sbDataPtr->arrow2.x + sbDataPtr->arrow2.width - 1) - 2;
      bottom = (sbDataPtr->arrow2.y + sbDataPtr->arrow2.height - 1) - 2;

      /* Generate the endpoints for scroll arrow 2 */
      (arrow2 + 0)->x = left + (right - left)/2;
      (arrow2 + 0)->y = bottom;
      (arrow2 + 1)->x = left;
      (arrow2 + 1)->y = bottom - (bottom - top)/2;
      (arrow2 + 2)->x = left + (right - left)/3;
      (arrow2 + 2)->y = bottom - (bottom - top)/2;
      (arrow2 + 3)->x = left + (right - left)/3;
      (arrow2 + 3)->y = top;
      (arrow2 + 4)->x = right - (right - left)/3;
      (arrow2 + 4)->y = top;
      (arrow2 + 5)->x = right - (right - left)/3;
      (arrow2 + 5)->y = bottom - (bottom - top)/2;
      (arrow2 + 6)->x = right;
      (arrow2 + 6)->y = bottom - (bottom - top)/2;
      (arrow2 + 7)->x = left + (right - left)/2;
      (arrow2 + 7)->y = bottom;
   }
   else
   {
      /* Initialize the point count for each scroll arrow */
      sbDataPtr->pointCount++;

      /* Generate the endpoints for scroll arrow 1 */
      (arrow1 + 0)->x = left + (right - left)/2;
      (arrow1 + 0)->y = top;
      (arrow1 + 1)->x = left;
      (arrow1 + 1)->y = top - 1 + (bottom - top)/2;
      (arrow1 + 2)->x = left + (right - left + 2)/3;
      (arrow1 + 2)->y = top - 1 + (bottom - top)/2;
      (arrow1 + 3)->x = left + (right - left + 2)/3;
      (arrow1 + 3)->y = bottom;
      (arrow1 + 4)->x = right - (right - left + 2)/3;
      (arrow1 + 4)->y = bottom;
      (arrow1 + 5)->x = right - (right - left + 2)/3;
      (arrow1 + 5)->y = top - 1 + (bottom - top)/2;
      (arrow1 + 6)->x = right;
      (arrow1 + 6)->y = top - 1 + (bottom - top)/2;
      (arrow1 + 7)->x = right - (right - left)/2;
      (arrow1 + 7)->y = top;
      (arrow1 + 8)->x = left + (right - left)/2;
      (arrow1 + 8)->y = top;

      /*
       * Get the info describing the rectangle into which scroll
       * arrow 2 must fit.
       */
      left = sbDataPtr->arrow2.x + 2;
      top = sbDataPtr->arrow2.y + 2;
      right = (sbDataPtr->arrow2.x + sbDataPtr->arrow2.width - 1) - 2;
      bottom = (sbDataPtr->arrow2.y + sbDataPtr->arrow2.height - 1) - 2;

      /* Generate the endpoints for scroll arrow 2 */
      (arrow2 + 0)->x = left + (right - left)/2;
      (arrow2 + 0)->y = bottom;
      (arrow2 + 1)->x = left;
      (arrow2 + 1)->y = bottom + 1 - (bottom - top)/2;
      (arrow2 + 2)->x = left + (right - left + 2)/3;
      (arrow2 + 2)->y = bottom + 1 - (bottom - top)/2;
      (arrow2 + 3)->x = left + (right - left + 2)/3;
      (arrow2 + 3)->y = top;
      (arrow2 + 4)->x = right - (right - left + 2)/3;
      (arrow2 + 4)->y = top;
      (arrow2 + 5)->x = right - (right - left + 2)/3;
      (arrow2 + 5)->y = bottom + 1 - (bottom - top)/2;
      (arrow2 + 6)->x = right;
      (arrow2 + 6)->y = bottom + 1 - (bottom - top)/2;
      (arrow2 + 7)->x = right - (right - left)/2;
      (arrow2 + 7)->y = bottom;
      (arrow2 + 8)->x = left + (right - left)/2;
      (arrow2 + 8)->y = bottom;
   }
}


/*************************************<->*************************************
 *
 *  INT32
 *  drawScrollBar (scrollBar, drawOption)
 *
 *     xrEditor * scrollBar;
 *     INT32      drawOption;
 *
 *   Description:
 *   -----------
 *     This routine will display a complete scrollbar instance.
 *
 *
 *   Inputs:
 *   ------
 *     scrollBar = Points to the instance pointer for the scrollbar
 *                 being drawn.
 *
 *     drawOption = Not used; this parameter is present only so that
 *                  this routine may be called by _MsgNew().
 * 
 *   Outputs:
 *   -------
 *
 *   Procedures Called
 *   -----------------
 *   _XrMakeInvisible()        [editorUtil.c]
 *   _XrBorderFillRectangle()  [rectUtil.c]
 *   setMyGCs()
 *   drawArrowBoxes()
 *   drawScrollArrows()
 *   drawSlideBox()
 *
 *************************************<->***********************************/

static
INT32
drawScrollBar (scrollBar, drawOption)

   register xrEditor * scrollBar;
            INT32      drawOption;

{
   register xrScrollBarData * sbDataPtr;
   register INT8              sensitive;
   register Window            windowId = scrollBar->editorWindowId;
            RECTANGLE         workRect;

   sbDataPtr =(xrScrollBarData *)scrollBar->editorData;
   XrCopyRect (&scrollBar->editorRect, &workRect);

   /*
    * If the instance is not visible, then fill its area with the
    * background tile for the port, thus making the instance invisible.
    */
   if (!(scrollBar->editorState & XrVISIBLE))
   {
      _XrMakeInvisible (windowId, &workRect, TRUE);
      return; 
   }

   /* Initialize the graphic contexts we will be using */
   setMyGCs (sbDataPtr);

   sensitive = (scrollBar->editorState & XrSENSITIVE) ? TRUE : FALSE;

   /* Draw and fill the rectangle defining the scrollbar */
   if (sensitive)
   {
      /* Draw a bordered rectangle with a 50% tile fill */
      _XrBorderFillRectangle (windowId, xrEditorGC1, xrEditorGC3,
                              &workRect);
   }
   else
   {
      /* Draw a bordered rectangle, filled with the background color */
      _XrBorderFillRectangle (windowId, xrEditorGC1, xrEditorGC2,
                              &workRect);
   }

   /* Draw the bordered and filled boxes for the scroll arrows */
   drawArrowBoxes (windowId, sbDataPtr);

   /* Draw the scroll arrows */
   if (sbDataPtr->configuration.components & XrSCROLLARROWS)
      drawScrollArrows (windowId, sbDataPtr, sensitive);

   /* Draw the slide box, if defined */
   if (sbDataPtr->configuration.components & XrSLIDEBOX)
      drawSlideBox (windowId, sbDataPtr, sensitive);
}
 

/*************************************<->*************************************
 *
 *  INT32
 *  CalculateSlidebox (sbDataPtr)
 *  
 *     xrScrollBarData * sbDataPtr;
 *
 *   Description:
 *   -----------
 *     This routine takes the slide box position, expressed in units
 *     which are in the range of the max and min values for the instance,
 *     and calculates the (x, y, height and width) for the rectangle which
 *     describes the slide box.  This information is stored in the
 *     'sbDataPtr' structure.
 *
 *
 *   Inputs:
 *   ------
 *     sbDataPtr = Points to the instance's internal data structure.
 * 
 *   Outputs:
 *   -------
 *
 *   Procedures Called
 *   -----------------
 *
 *************************************<->***********************************/

static
INT32
CalculateSlideBox (sbDataPtr)

   register xrScrollBarData    * sbDataPtr;

{
   register RECTANGLE   * scrollRegion;
   register INT16         slide_wid; 
            INT16         slide_ht;
            INT16         max, min;
            INT16         position;
            INT16         slideSize;
            INT16         unitWid;

   /* Set up local variables needed for our calculations */
   scrollRegion = &sbDataPtr->slideArea;
   max = sbDataPtr->configuration.max;
   min = sbDataPtr->configuration.min;
   unitWid = max - min;
   position  = sbDataPtr->configuration.slidePosition - min;
   slideSize  = sbDataPtr->configuration.slideSize;

   if (sbDataPtr->orientation == XrHORIZONTAL)
   {
      slide_wid = (slideSize * scrollRegion->width) / (unitWid + 1);

      sbDataPtr->slideBox.x = scrollRegion->x + 
                   ((scrollRegion->width - slide_wid) * position)/unitWid;
      sbDataPtr->slideBox.y = scrollRegion->y + 2;
      sbDataPtr->slideBox.width = (slide_wid == 0 ? 1 : slide_wid);
      sbDataPtr->slideBox.height = scrollRegion->height - 4;
   }
   else
   {
      slide_ht = (slideSize * scrollRegion->height) / (unitWid + 1);

      sbDataPtr->slideBox.x = scrollRegion->x + 2;
      sbDataPtr->slideBox.y = scrollRegion->y + 
                      ((scrollRegion->height - slide_ht) * position)/unitWid;
      sbDataPtr->slideBox.width = scrollRegion->width - 4;
      sbDataPtr->slideBox.height = (slide_ht == 0 ? 1 : slide_ht);
   }
}


/*************************************<->*************************************
 *
 *  INT32
 *  invalidSBParameters (sbDataPtr, sbParmPtr)
 *
 *     xrScrollBarData * sbDataPtr;
 *     xrSBParameters  * sbParmPtr;
 *
 *   Description:
 *   -----------
 *     This routine is called anytime the scrollbar configuration
 *     parameters are being changed.  It will validate all of the
 *     new settings, and will fail if an invalid setting is found.
 *     If the settings are valid, then they will be saved in the
 *     instance's internal 'data' structure.
 *
 *
 *   Inputs:
 *   ------
 *     sbDataPtr = Points to the internal 'data' structure associated
 *                 with the instance which is having its configuration
 *                 parameters modified.
 *
 *     sbParmPtr = Points to the structure containing the new configuration
 *                 parameters.
 * 
 *   Outputs:
 *   -------
 *     Upon successful completion, TRUE is returned, and the new
 *          configuration parameter will be saved.
 *
 *     Upon failure, FALSE is returned, and xrErrno is set.
 *
 *   Procedures Called
 *   -----------------
 *
 *************************************<->***********************************/

static
INT32
invalidSBParameters (sbDataPtr, sbParmPtr)

   register xrScrollBarData * sbDataPtr;
   register xrSBParameters  * sbParmPtr;

{
   register INT16 min;
   register INT16 max;

   min = sbParmPtr->min;
   max = sbParmPtr->max;

   /* Validate the new configuration parameters */
   if (min >= max)
   {
      xrErrno = XrINVALIDPARM;
      return (TRUE);
   }

   if (sbParmPtr->components & XrSLIDEBOX)
   {
      if ((sbParmPtr->slidePosition < min) || 
          (sbParmPtr->slidePosition > max) ||
          (sbParmPtr->slideSize > (max - min + 1)) ||
          (sbParmPtr->slideSize <= 0) ||
          ((sbParmPtr->handler) && ((sbParmPtr->granularity <= 0) ||
          (sbParmPtr->granularity > (max - min + 1)))))
      {
         xrErrno = XrPARMOUTOFRANGE;
         return (TRUE);
      }

      _Xrmemcpy (&sbDataPtr->configuration, sbParmPtr, sizeof (xrSBParameters));
   }
   else
   {
      _Xrmemcpy (&sbDataPtr->configuration, sbParmPtr, sizeof (xrSBParameters));
      sbDataPtr->configuration.slidePosition = min;
      sbDataPtr->configuration.slideSize = 1;
      sbDataPtr->configuration.granularity = 1;
      sbDataPtr->configuration.handler = NULL;
   }

   return (FALSE);
}


/*************************************<->*************************************
 *
 *  INT32
 *  setMyGCs (sbDataPtr)
 *
 *     xrScrollBarData * sbDataPtr;
 *
 *   Description:
 *   -----------
 *     This routine initializes the 3 graphic context structures which
 *     are needed each time a scrollbar instance is displayed.
 *
 *
 *   Inputs:
 *   ------
 *     sbDataPtr = This points to the instance's internal 'data'
 *                 structure.  The foreground and background colors
 *                 are contained here.
 * 
 *   Outputs:
 *   -------
 *
 *   Procedures Called
 *   -----------------
 *   _XrInitEditorGCs()  [gcUtil.c]
 *   _XrCopyGC()         [gcUtil.c]
 *   _XrChangeGC()       [gcUtil.c]
 *
 *************************************<->***********************************/

static
INT32
setMyGCs (sbDataPtr)

   xrScrollBarData * sbDataPtr;

{
   INT32     changeList[21];
   INT32     changeMask;

   /* Initialize the graphic contexts we will use during drawing */
   _XrInitEditorGCs (sbDataPtr->sbFGColor, sbDataPtr->sbBGColor,
                     xrBaseFontInfo->id);

   /*
    * Create another graphics context, which we will use whenever
    * we need to tile fill the background of a sensitive scrollbar.
    */
   changeList[XrFILLSTYLEVAL] = Tiled;
   changeList[XrTILEVAL] = sbDataPtr->sbTileId;
   changeMask = (XrFILLSTYLE | XrTILE);
   _XrCopyGC (xrDefaultGC, xrEditorGC3);
   _XrChangeGC (xrEditorGC3, changeMask, changeList);
}


/*************************************<->*************************************
 *
 *  INT32
 *  updateComponents (scrollBar, sbDataPtr)
 *
 *     xrEditor        * scrollBar;
 *     xrScrollBarData * sbDataPtr;
 *
 *   Description:
 *   -----------
 *     This routine looks at the new configuration parameters for the
 *     specified scrollbar instance, and calculates the new slide box
 *     location. In addition, it redraws the instance to match, assuming 
 *     the instance is visible
 *
 *
 *   Inputs:
 *   ------
 *     scrollBar = Points to the instance structure associated with the
 *                 scrollbar being worked on.  This contains the window
 *                 Id for the window to which the instance is attached.
 *
 *     sbDataPtr = Points to the instance's internal 'data' structure.
 * 
 *   Outputs:
 *   -------
 *
 *   Procedures Called
 *   -----------------
 *   XrCopyRect()   [calc.c]
 *   XrEqualRect()  [calc.c]
 *   removeSlideBox()
 *   drawSlideBox()
 *   CalculateSlideBox()
 *   drawScrollArrows()
 *   drawArrowBoxes()
 *   setMyGCs()
 *
 *************************************<->***********************************/

static
INT32
updateComponents (scrollBar, sbDataPtr)

   register xrEditor        * scrollBar;
   register xrScrollBarData * sbDataPtr;

{
   INT8      sensitive = scrollBar->editorState & XrSENSITIVE;
   RECTANGLE slideBox;

   /* Initialize the graphic contexts we will use during drawing */
   setMyGCs (sbDataPtr);

   /* Calculate the new slide position */
   XrCopyRect (&sbDataPtr->slideBox, &slideBox);
   CalculateSlideBox (sbDataPtr);

   /* Update the visual instance only if it is visible */
   if (scrollBar->editorState & XrVISIBLE)
   {
      /* See if the state of the scroll arrows has changed */
      if (sbDataPtr->configuration.components & XrSCROLLARROWS)
         drawScrollArrows (scrollBar->editorWindowId, sbDataPtr, sensitive);
      else
         drawArrowBoxes (scrollBar->editorWindowId, sbDataPtr);

      /* See if the state of the slide box has changed */
      if (! (sbDataPtr->configuration.components & XrSLIDEBOX) ||
            (! XrEqualRect (&slideBox, &sbDataPtr->slideBox)))
      {
         removeSlideBox (scrollBar->editorWindowId, &slideBox, sbDataPtr, 
                         sensitive, FALSE);
      }

      if (sbDataPtr->configuration.components & XrSLIDEBOX)
         drawSlideBox (scrollBar->editorWindowId, sbDataPtr, sensitive);
   }
}


/*************************************<->*************************************
 *
 *  INT32
 *  removeSlideBox (windowId, rectPtr, sbDataPtr, sensitive, xparentMode)
 *
 *   Description:
 *   -----------
 *     This routine is used both during interactive slide operations, and
 *     any other time the slide box needs to be removed.  The manner in
 *     which the removal is performed depends upon the 'xparentMode'
 *     parameter.  If 'xparentMode' is set to FALSE, then the slide box
 *     is removed by overwriting it with the background tile (if the
 *     instance is sensitive) or the background color (if the instance is
 *     not sensitive).  However, if 'xparentMode' is set to TRUE, then
 *     we know that an interactive slide is going on, so we only want to
 *     'remove' that portion of the transparent slide box which does not
 *     intersect with the real slide box.
 *
 *
 *   Inputs:
 *   ------
 *     windowId = Id of window to which the scrollbar is attached.
 *
 *     rectPtr = Current slide box (normal or transparent) location.
 *
 *     sbDataPtr = Points to instance's internal 'data' structure.
 *
 *     sensitive = TRUE if instance is sensitive; FALSE otherwise.
 *
 *     xparentMode = TRUE if transparent mode interactive slide operation
 *                   is currently taking place.
 * 
 *   Outputs:
 *   -------
 *
 *   Procedures Called
 *   -----------------
 *   XrCopyRect()        [calc.c]
 *   _XrFillRectangle()  [rectUtil.c]
 *
 *************************************<->***********************************/

static
INT32
removeSlideBox (windowId, rectPtr, sbDataPtr, sensitive, xparentMode)

            Window            windowId;
   register RECTANGLE       * rectPtr;
            INT8              sensitive;
            xrScrollBarData * sbDataPtr;
            INT8              xparentMode;

{

   /* Check for a transparent mode slide operation in progress */
   if (xparentMode)
   {
               RECTANGLE   workRect;
      register RECTANGLE * sbPtr = &sbDataPtr->slideBox;

      XrCopyRect (rectPtr, &workRect);

      /*
       * Determine the portion of the slide outline which does
       * not intersect with the real slide box.  This is the
       * part which must be removed.
       */
      if (sbPtr->x > rectPtr->x)
         workRect.width = sbPtr->x - rectPtr->x;
      else if (sbPtr->x < rectPtr->x)
      {
         workRect.x = sbPtr->x + sbPtr->width;
         workRect.width = rectPtr->width - (workRect.x - rectPtr->x);
      }

      if (sbPtr->y > rectPtr->y)
         workRect.height = sbPtr->y - rectPtr->y;
      else if (sbPtr->y < rectPtr->y)
      {
         workRect.y = sbPtr->y + sbPtr->height;
         workRect.height = rectPtr->height - (workRect.y - rectPtr->y);
      }

      _XrFillRectangle (windowId, xrEditorGC3, &workRect);
   }
   else
   {
      /* Cover the slide box, using the 50% background tile */
      if (sensitive)
         _XrFillRectangle (windowId, xrEditorGC3, rectPtr);
      else
         _XrFillRectangle (windowId, xrEditorGC2, rectPtr);
   }
}


/*************************************<->*************************************
 *
 *  INT32
 *  drawSlidebox (windowId, sbDataPtr, sensitive)
 *
 *     Window            windowId;
 *     xrScrollBarData * sbDataPtr;
 *     INT8              sensitive;
 *
 *   Description:
 *   -----------
 *     This routine draws the slide box at its current location, as
 *     specified in the instance's internal 'data' structure.
 *
 *
 *   Inputs:
 *   ------
 *     windowId = Id of window to which the instance is attached.
 *
 *     sbDataPtr = Points to the instance's internal 'data' structure.
 *
 *     sensitive = TRUE if the instance is currently sensitive; FALSE
 *                 otherwise.
 * 
 *   Outputs:
 *   -------
 *
 *   Procedures Called
 *   -----------------
 *   XrBorderFillRectangle()  [rectUtil.c]
 *
 *************************************<->***********************************/

static
INT32
drawSlideBox (windowId, sbDataPtr, sensitive)

   Window            windowId;
   xrScrollBarData * sbDataPtr;
   INT8              sensitive;

{
   INT32       fillGC;

   if (sensitive)
      fillGC = xrEditorGC1;
   else
      fillGC = xrEditorGC2;

   _XrBorderFillRectangle (windowId, xrEditorGC1, fillGC, &sbDataPtr->slideBox);
}


/*************************************<->*************************************
 *
 *  INT32
 *  drawArrowBoxes (windowId, sbDataPtr)
 *
 *     Window            windowId;
 *     xrScrollBarData * sbDataPtr;
 *
 *   Description:
 *   -----------
 *     This routine will draw the two scroll arrow boxes (not the scroll
 *     arrows themselves) at either end of the scrollbar.
 *
 *
 *   Inputs:
 *   ------
 *     windowId = Id of window to which the instance is attached.
 *
 *     sbDataPtr = Points to the instance's internal 'data' structure.
 * 
 *   Outputs:
 *   -------
 *
 *   Procedures Called
 *   -----------------
 *   _XrBorderFillRectangle()  [rectUtil.c]
 *
 *************************************<->***********************************/

static
INT32
drawArrowBoxes (windowId, sbDataPtr)

   Window            windowId;
   xrScrollBarData * sbDataPtr;

{
   _XrBorderFillRectangle (windowId, xrEditorGC1, xrEditorGC2, 
                           &sbDataPtr->arrow1);

   _XrBorderFillRectangle (windowId, xrEditorGC1, xrEditorGC2, 
                           &sbDataPtr->arrow2);
}


/*************************************<->*************************************
 *
 *  INT32
 *  drawScrollArrows (windowId, sbDataPtr, sensitive)
 *
 *     Window            windowId;
 *     xrScrollBarData * sbDataPtr;
 *     INT8              sensitive;
 *
 *   Description:
 *   -----------
 *     This routine will draw the two scroll arrows located at either
 *     end of a scrollbar.  They will be drawn as filled arrows if the
 *     instance is sensitive; otherwise, only their outline will be
 *     drawn.  The definitions for the polygons describing the arrow
 *     are contained in the instance's internal 'data' structure.
 *
 *
 *   Inputs:
 *   ------
 *     windowId = Id of the window to which the instance is attached.
 *
 *     sbDataPtr = Points to the instance's internal 'data' structure.
 *
 *     sensitive = TRUE if the instance is sensitive; FALSE otherwise.
 * 
 *   Outputs:
 *   -------
 *
 *   Procedures Called
 *   -----------------
 *   _XrFillPoly()  [polyUtil.c]
 *   _XrPoly()      [polyUtil.c]
 *
 *************************************<->***********************************/

static
INT32
drawScrollArrows (windowId, sbDataPtr, sensitive)

   register Window            windowId;
   register xrScrollBarData * sbDataPtr;
            INT8              sensitive;

{
   register INT32 i;
   register INT16 pointCount;

   /* Draw scroll arrow 1 */
   pointCount = sbDataPtr->pointCount;
   for (i = 0; i < pointCount; i++)
   {
      xr_PolyList[i].x = sbDataPtr->arrow1_vl[i].x;
      xr_PolyList[i].y = sbDataPtr->arrow1_vl[i].y;
      xr_PolyList[i].flags = 0;
   }
   if (sensitive)
      _XrFillPoly (windowId, xrEditorGC1, pointCount, xr_PolyList);
   else
      _XrPoly (windowId, xrEditorGC1, pointCount, xr_PolyList);

   /* Draw scroll arrow 2 */
   for (i = 0; i < pointCount; i++)
   {
      xr_PolyList[i].x = sbDataPtr->arrow2_vl[i].x;
      xr_PolyList[i].y = sbDataPtr->arrow2_vl[i].y;
      xr_PolyList[i].flags = 0;
   }
   if (sensitive)
      _XrFillPoly (windowId, xrEditorGC1, pointCount, xr_PolyList);
   else
      _XrPoly (windowId, xrEditorGC1, pointCount, xr_PolyList);
}


/*************************************<->*************************************
 *
 *  INT32
 *  processScrollBar (scrollBar, input, returnEvent)
 *
 *     xrEditor     * scrollBar;
 *     XButtonEvent * input;
 *     xrEvent      * returnEvent;
 *
 *   Description:
 *   -----------
 *     This is the event processing routine for the scrollbar editor.
 *     It takes an event and determines which component of the scrollbar
 *     the select occurred in.  The possible choices are one of the
 *     scroll arrows, the slide region or the slide box.
 *
 *
 *   Inputs:
 *   ------
 *     scrollBar = Instance pointer for the scrollbar being selected.
 *
 *     input = Points to the event to be processed.
 *
 *     returnEvent = Points to a partially filled out X-ray event
 *                   structure.  It can be used by this routine when
 *                   it needs to generate a return event for the
 *                   application.  Every field is already filled, except
 *                   for the 'value' fields.
 * 
 *   Outputs:
 *   -------
 *
 *   Procedures Called
 *   -----------------
 *   XrSetPt()           [calc.c]
 *   XrPtInRect()        [calc.c]
 *   XrGetWindowEvent()  [utilities.c]
 *   XrInput()           [input.c]
 *   XrCopyRect()        [calc.c]
 *   interactiveSlide()
 *
 *************************************<->***********************************/

static
INT32
processScrollBar (scrollBar, input, returnEvent)

   xrEditor     * scrollBar;
   XButtonEvent * input;
   xrEvent      * returnEvent;

{
   register xrScrollBarData * sbDataPtr;
   register INT8              orientation;
            POINT             spritePt;
   register RECTANGLE       * saPtr;         /* Ptr to slide area rectangle */
            RECTANGLE         slideBox;
            INT8              arrowsShowing;
            INT8              slideBoxShowing;
            INT16             min;
            INT16             max;
            XButtonEvent      selectUp;
            xrWindowEvent     windowEvent;

   sbDataPtr = (xrScrollBarData *) scrollBar->editorData;
   min = sbDataPtr->configuration.min;
   max = sbDataPtr->configuration.max;
   orientation = sbDataPtr->orientation;
   arrowsShowing = sbDataPtr->configuration.components & XrSCROLLARROWS;
   slideBoxShowing = sbDataPtr->configuration.components & XrSLIDEBOX;
   XrSetPt (&spritePt, input->x, input->y);
   returnEvent->value2 = sbDataPtr->configuration.slidePosition;

   /*
    * Determine which region was selected 
    */

   /* Check for a select in scroll arrow 1 region */
   if (XrPtInRect (&spritePt, &sbDataPtr->arrow1))
   {
      /* Do nothing but set up the return event */
      if (! arrowsShowing)
         returnEvent->value1 = NULL;
      else
      {
         if (orientation == XrHORIZONTAL)
            returnEvent->value1 = XrSCROLL_LEFT;
         else
            returnEvent->value1 = XrSCROLL_UP;
      }

      /*
       * Push a fake select up event, so that the real
       * select up event won't be swallowed by _MsgEdit(),
       * and will instead pass through to the application.
       */
      XrGetWindowEvent (XrSELECTUP, &windowEvent);
      selectUp.type = windowEvent.inputType;
      selectUp.detail = windowEvent.inputCode;
      XrInput (NULL, MSG_PUSHEVENT, &selectUp);
      return;
   }

   /* Check for a select in scroll arrow 2 region */
   if (XrPtInRect (&spritePt, &sbDataPtr->arrow2))
   {
      /* Do nothing but set up the return event */
      if (! arrowsShowing)
         returnEvent->value1 = NULL;
      else
      {
         if (orientation == XrHORIZONTAL)
            returnEvent->value1 = XrSCROLL_RIGHT;
         else
            returnEvent->value1 = XrSCROLL_DOWN;
      }

      /*
       * Push a fake select up event, so that the real
       * select up event won't be swallowed by _MsgEdit(),
       * and will instead pass through to the application.
       */
      XrGetWindowEvent (XrSELECTUP, &windowEvent);
      selectUp.type = windowEvent.inputType;
      selectUp.detail = windowEvent.inputCode;
      XrInput (NULL, MSG_PUSHEVENT, &selectUp);
      return;
   }

   /* Check for a select in the scroll area; excluding the slide box */
   saPtr = &sbDataPtr->slideArea;
   XrCopyRect (&sbDataPtr->slideBox, &slideBox);

   /*
    * Increase the size of the slide box, to include the one
    * pixel of space above and below it (for a horizontal scrollbar)
    * or the one pixel of space to the right and left of it (for
    * a vertical scrollbar).
    */
   if (sbDataPtr->orientation == XrHORIZONTAL)
   {
      slideBox.y -= 2;
      slideBox.height += 4;
   }
   else
   {
      slideBox.x -= 2;
      slideBox.width += 4;
   }

   if ( (!slideBoxShowing) || !(XrPtInRect (&spritePt, &slideBox)) )
   {
      /* Calculate the sprite position within the scroll area */
      if (orientation == XrHORIZONTAL)
      {
         if (spritePt.x < saPtr->x)
            spritePt.x = saPtr->x;
         else if (spritePt.x > (saPtr->x + saPtr->width - 1))
            spritePt.x = (saPtr->x + saPtr->width - 1);

         returnEvent->value3 = min + 
              (((max - min + 1)*(spritePt.x - saPtr->x))/saPtr->width);
      
         if ( (slideBoxShowing) && (spritePt.x < slideBox.x) )
            returnEvent->value1 = XrSCROLL_LESS;
         else
            returnEvent->value1 = XrSCROLL_MORE;
      }
      else
      {
         if (spritePt.y < saPtr->y)
            spritePt.y = saPtr->y;
         else if (spritePt.y > (saPtr->y + saPtr->height - 1))
            spritePt.y = (saPtr->y + saPtr->height - 1);

         returnEvent->value3 = min + 
             (((max - min + 1)*(spritePt.y - saPtr->y))/saPtr->height);

         if ( (slideBoxShowing) && (spritePt.y < slideBox.y) )
            returnEvent->value1 = XrSCROLL_LESS;
         else
            returnEvent->value1 = XrSCROLL_MORE;
      }

      return;
   }

   /*
    * By default, the slide box must have been selected
    * Keep processing, until the Select button is released
    */
   interactiveSlide (scrollBar, sbDataPtr, &spritePt, returnEvent);
}


/*************************************<->*************************************
 *
 *  INT32
 *  interactiveSlide (scrollBar, sbDataPtr, spritePtr, returnEvent)
 *
 *     xrEditor        * scrollBar;
 *     xrScrollBarData * sbDataPtr;
 *     POINT           * spritePtr;
 *     xrEvent         * returnEvent;
 *
 *   Description:
 *   -----------
 *     This routine is invoked anytime a select event occurs within the
 *     slide box.  When this happens, this routine will continue to move
 *     the slide box, until either the user releases the select button,
 *     or any other X event is received; under either of these conditions,
 *     the interactive slide operation will terminate, and a return event
 *     will be generated.
 *
 *
 *   Inputs:
 *   ------
 *     scrollBar = Points to the instance structure.
 *
 *     sbDataPtr = Points to the instance's internal 'data' structure.
 *
 *     spritePtr = Points to the initial mouse position at the time the
 *                 select event occurred.
 *
 *     returnEvent = Points to a partially filled out X-ray event structure.
 *                   It can be used by this routine when it needs to
 *                   generate its return event.  Every field is already
 *                   filled, except for the 'value' fields.
 * 
 *   Outputs:
 *   -------
 *
 *   Procedures Called
 *   -----------------
 *   XrCopyPt()      [calc.c]
 *   XrSetPt()       [calc.c]
 *   XrCopyRect()    [calc.c]
 *   XrOffsetRect()  [calc.c]
 *   _XrRectangle()  [rectUtil.c]
 *   XrInput()       [input.c]
 *   XFlush()        [libX.a]
 *   XQueryMouse()   [libX.a]
 *   removeSlideBox()
 *   drawSlideBox()
 *   setMyGCs()
 *
 *************************************<->***********************************/

static
INT32
interactiveSlide (scrollBar, sbDataPtr, spritePtr, returnEvent)

            xrEditor        * scrollBar;
   register xrScrollBarData * sbDataPtr;
            POINT           * spritePtr;
            xrEvent         * returnEvent;

{
   register INT8              orientation;
   register INT8              transparentMode;
   register RECTANGLE       * saPtr;         /* Ptr to slide area rectangle */
   register RECTANGLE       * sbPtr;         /* Ptr to slide box rectangle */
            RECTANGLE         workRect;
            RECTANGLE         xparentBox;
            xrEvent           key;
            INT16             xOffset, yOffset;
            POINT             oldCursor;
            INT32             cursorX, cursorY;
            Window            subw;
            Window            windowId;
            INT16             location;
            INT16             min;
            INT16             max;
register    INT16             slidePosition;
register    INT16             granularity;
            INT8              handlerFlag;

   /* Initialize local variables */
   min = sbDataPtr->configuration.min;
   max = sbDataPtr->configuration.max;
   orientation = sbDataPtr->orientation;
   XrCopyPt (spritePtr, &oldCursor);
   setMyGCs (sbDataPtr);
   saPtr = &sbDataPtr->slideArea;
   windowId = scrollBar->editorWindowId;
   transparentMode = scrollBar->editorState & XrTRANSPARENT;
   handlerFlag = (sbDataPtr->configuration.handler ? TRUE : FALSE);
   slidePosition = sbDataPtr->configuration.slidePosition;
   granularity = sbDataPtr->configuration.granularity;

   /* Set up the slide box pointer */
   if (transparentMode)
   {
      sbPtr = &xparentBox;
      XrCopyRect (&sbDataPtr->slideBox, sbPtr);

      /* Display the slidebox outline */
      _XrRectangle (windowId, xrEditorGC1, sbPtr);
   }
   else
      sbPtr = &sbDataPtr->slideBox;

   /* Determine where, within the slide box, the cursor is */
   xOffset = spritePtr->x - sbPtr->x + 1;
   yOffset = spritePtr->y - sbPtr->y + 1;

   while (1)
   {
      /* Stop as soon as any X event is received */
      if (XrInput (NULL, MSG_NONBLKREAD, &key))
      {
         /* Finish up the slide operation */
         if (orientation == XrHORIZONTAL)
         {
            if (saPtr->width == sbPtr->width)
               location = min;
            else
               location = (((sbPtr->x - saPtr->x) * (max - min)) /
                   (saPtr->width - sbPtr->width)) + min;
         }
         else
         {
            if (saPtr->height == sbPtr->height)
               location = min;
            else
               location = (((sbPtr->y - saPtr->y) * (max - min)) /
                   (saPtr->height - sbPtr->height)) + min;
         }

         /*
          * Regardless of what this event is, push it back onto
          * the input queue, so that _MsgEdit() will properly
          * strip the select up event.
          */
         XrInput (NULL, MSG_PUSHEVENT, &key);

         /* Generate our return event */
         if (transparentMode)
         {
            /* Remove the slide box outline */
            removeSlideBox (windowId, sbPtr, sbDataPtr, XrSENSITIVE, TRUE);
            drawSlideBox (windowId, sbDataPtr, XrSENSITIVE);
            XFlush();

            returnEvent->value3 = location;
            returnEvent->value1 = XrTSCROLL_SLIDE;
            return;
         }
         else
         {
            returnEvent->value2 = location;
            sbDataPtr->configuration.slidePosition = location;
            returnEvent->value1 = XrSCROLL_SLIDE;
            return;
         }
      }

      /* Get cursor position, and process it */
      XQueryMouse (scrollBar->editorWindowId, &cursorX, &cursorY, &subw);

      /* Update only if the cursor location has changed */
      if (cursorX != oldCursor.x || cursorY != oldCursor.y)
      {
         if (orientation == XrHORIZONTAL)
         {
            /*
             * Check for the slide box already being located at one
             * end of the slide region, or for the cursor being outside
             * the slide region.
             */
            if (cursorX < saPtr->x + xOffset - 1)
            {
               if (sbPtr->x > saPtr->x)
                  cursorX= saPtr->x + xOffset - 1;
               else
                  continue;
            }
            else if (cursorX > ((saPtr->x + saPtr->width - 1) - 
                                  sbPtr->width + xOffset))
            {
               if (sbPtr->x + sbPtr->width - 1 < saPtr->x + saPtr->width - 1)
                  cursorX = (saPtr->x + saPtr->width - 1) + 
                              xOffset - sbPtr->width;
               else
                  continue;
            }

            XrCopyRect (sbPtr, &workRect);
            XrOffsetRect (&workRect, (cursorX - xOffset + 1) - sbPtr->x, 0);
         }
         else
         {
            /*
             * Check for the slide box already being located at one
             * end of the slide region, or for the cursor being outside
             * the slide region.
             */
            if (cursorY < saPtr->y + yOffset - 1)
            {
               if (sbPtr->y > saPtr->y)
                  cursorY= saPtr->y + yOffset - 1;
               else
                  continue;
            }
            else if (cursorY > ((saPtr->y + saPtr->height - 1) - 
                                  sbPtr->height + yOffset))
            {
               if (sbPtr->y + sbPtr->height - 1 < saPtr->y + saPtr->height - 1)
                  cursorY = (saPtr->y + saPtr->height - 1) + 
                              yOffset - sbPtr->height;
               else
                  continue;
            }

            XrCopyRect (sbPtr, &workRect);
            XrOffsetRect (&workRect, 0, (cursorY - yOffset + 1) - sbPtr->y);
         }

         /* Draw the scrollbox at its new position */
         if (transparentMode)
         {
            /* Remove the old slide box outline */
            removeSlideBox (windowId, sbPtr, sbDataPtr, XrSENSITIVE, TRUE);
            drawSlideBox (windowId, sbDataPtr, XrSENSITIVE);

            /* Draw the slide box outline at the new position */
            _XrRectangle (windowId, xrEditorGC1, &workRect);

            XrCopyRect (&workRect, sbPtr);
         }
         else
         {
            removeSlideBox (windowId, sbPtr, sbDataPtr, XrSENSITIVE, FALSE);
            XrCopyRect (&workRect, sbPtr);
            drawSlideBox (windowId, sbDataPtr, XrSENSITIVE);
         }
         XrSetPt (&oldCursor, cursorX, cursorY);

         /*
          * Call the application's handler, if the granularity
          * delta was surpassed, and a handler is specified.
          */
         if (handlerFlag)
         {
            if (orientation == XrHORIZONTAL)
            {
               location = (((sbPtr->x - saPtr->x) * (max - min)) /
                      (saPtr->width - sbPtr->width)) + min;

            }
            else
            {
               location = (((sbPtr->y - saPtr->y) * (max - min)) /
                      (saPtr->height - sbPtr->height)) + min;
            }

            if ((location <= slidePosition - granularity) ||
                (location >= slidePosition + granularity) ||
                (location == max && slidePosition != max) || 
                (location == min && slidePosition != min))
            {
               (*sbDataPtr->configuration.handler) (windowId,
                       scrollBar, location);

               slidePosition = location;
            }
         }
      }
   }
}
