/*
 *	$Source: /u1/Xr/src/Xrlib/Editor/RCS/RButton.c,v $
 *	$Header: RButton.c,v 1.1 86/12/17 09:02:37 swick Exp $
 */

#ifndef lint
static char *rcsid_RButton_c = "$Header: RButton.c,v 1.1 86/12/17 09:02:37 swick Exp $";
#endif	lint


#include <Xr/xr-copyright.h>

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

static char rcsid[] = "$Header: RButton.c,v 1.1 86/12/17 09:02:37 swick Exp $";
/*************************************<+>*************************************
 *****************************************************************************
 **
 **   File:        RButton.c
 **
 **   Project:     X-ray Toolbox
 **
 **   Description: 
 **         This file contains the source code for the X-ray radio
 **         button field editor.
 **
 **
 **   ------------------------ MODIFICATION RECORD   ------------------------
 *
 * $Log:	RButton.c,v $
 * Revision 1.1  86/12/17  09:02:37  swick
 * Initial revision
 * 
 * Revision 7.0  86/11/13  08:24:18  08:24:18  fred ()
 * Final QA Release
 * 
 * Revision 6.1  86/11/10  16:12:17  16:12:17  fred ()
 * Revised circle drawing algorithm.
 * 
 * Revision 6.0  86/11/10  15:29:57  15:29:57  fred ()
 * QA #2 release
 * 
 * Revision 5.2  86/11/07  14:20:50  14:20:50  fred ()
 * Added new copyright message.
 * 
 * Revision 5.1  86/10/30  13:28:06  13:28:06  fred ()
 * Added check for numCols <= 0, and changed the button
 * drawing algorithm.
 * 
 * Revision 5.0  86/10/28  08:29:24  08:29:24  fred ()
 * QA #1.1 release
 * 
 * Revision 4.0  86/10/20  12:11:06  12:11:06  fred ()
 * QA #1 release
 * 
 * Revision 3.5  86/10/16  10:04:35  10:04:35  fred ()
 * Filled in the procedure headers.
 * 
 * Revision 3.4  86/10/16  09:15:42  09:15:42  fred ()
 * Performance enhanced: added use of register variables.
 * 
 * Revision 3.3  86/10/13  10:06:03  10:06:03  fred ()
 * Added use of the default tile, if needed.
 * 
 * Revision 3.2  86/10/09  07:45:30  07:45:30  fred ()
 * Added default color check to create routine.
 * 
 * Revision 3.1  86/10/07  16:04:54  16:04:54  fred ()
 * Added check for invalid rectangle size.
 * 
 * Revision 3.0  86/10/02  15:59:26  15:59:26  fred ()
 * Alpha release set to 3.0
 * 
 * Revision 2.5  86/10/01  06:54:05  06:54:05  fred ()
 * Changed default label padding from font's maxWidth to avgWidth.
 * 
 * Revision 2.4  86/09/30  08:34:23  08:34:23  fred ()
 * Modified the return event so that value3 contains the index
 * of the previously active radio button.
 * 
 * Revision 2.3  86/09/25  13:25:29  13:25:29  fred ()
 * Added the compile ifdef MVAX, for drawing real circles.
 * 
 * Revision 2.2  86/09/22  13:11:00  13:11:00  fred ()
 * Added calls to XrEditorGroup().
 * 
 * Revision 2.1  86/09/19  07:10:26  07:10:26  fred ()
 * Added a check for XrVISIBLE before doing any drawing.
 * 
 * Revision 2.0  86/09/16  08:06:54  08:06:54  fred ()
 * Updated input processing routine to use new SELECT strategy.
 * 
 * Revision 1.4  86/09/16  05:47:14  05:47:14  fred ()
 * Modified to swallow a select up event.
 * 
 * Revision 1.3  86/09/10  07:26:23  07:26:23  fred ()
 * Changed numRows to numCols in all data structures.
 * 
 * Revision 1.2  86/09/09  13:08:09  13:08:09  fred ()
 * changed some variables from INT32 to INT16
 * 
 * Revision 1.1  86/09/04  06:43:06  06:43:06  fred ()
 * Initial revision
 * 
 * 
 *
 *****************************************************************************
 *************************************<+>*************************************/



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

extern INT32 createRButtons();
extern INT32 drawRButtons();
extern INT32 processRButtons();
extern INT32 rbFreeMemory();
extern INT32 calcRBComponents();


/*************************************<->*************************************
 *
 *  xrEditor *
 *  XrRadioButton (radioButton, message, data)
 *
 *     xrEditor * radioButton;
 *     INT32      message;
 *     INT8     * data;
 *
 *   Description:
 *   -----------
 *     This is the main entry point and message handler for the radio
 *     button field editor.  It takes all message requests, and passes
 *     them onto the appropriate handler; invalid messages will fail
 *     and return an error.
 *
 *
 *   Inputs:
 *   ------
 *     radioButton = 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]
 *   _MsgSetState()       [MsgCommon.c]
 *   _MsgEdit()           [MsgCommon.c]
 *   _MsgGetItemStates()  [MsgItem.c]
 *   _MsgSetItemStates()  [MsgItem.c]
 *   _MsgGetItemCount()   [MsgItem.c]
 *   _MsgGetItemRects()   [MsgItem.c]
 *   _MsgSize()           [MsgItem.c]
 *   _MsgMove()           [MsgItem.c]
 *   _XrMakeInvisible()   [editorUtil.c]
 *   XrCopyRect()         [calc.c]
 *   processRButtons()
 *   calcRBComponents()
 *   createRButtons()
 *   drawRButtons()
 *   rbFreeMemory()
 *
 *************************************<->***********************************/

xrEditor *
XrRadioButton (radioButton, message, data)

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

{
   /* Determine the action being requested */
   switch (message)
   {
      case MSG_NEW:
      {
           /*
            * Create a new radio button instance.
            * The only parameter of interest is the 'data'
            * parameter, which is a pointer to a filled
            * instance of the xrRadioButtonInfo structure.
            */
           return ((xrEditor *)_MsgNew (radioButton, data, 
                                        sizeof(xrRadioButtonData),
                                        createRButtons, drawRButtons,
                                        rbFreeMemory, XrRadioButton,
                                        XrALLBUTTONS));
      }

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

      case MSG_GETSTATE:
      {
           /*
            * Return the settings of the state flags for the
            * specified radio button instance.  The 'radioButton'
            * 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 (radioButton, data));
      }

      case MSG_SETSTATE:
      {
           /*
            * Change the state flag settings for the specified
            * instance.  The 'radioButton' parameter specifies the
            * instance to be modified, and the 'data' parameter is
            * interpreted as an INT8 value, containing the new state
            * flag settings.
            */
           return ((xrEditor *) _MsgSetState (radioButton, data, 
                                              drawRButtons, XrALLBUTTONS));
      }

      case MSG_GETITEMSTATES:
      {
         /*
          * Return the state flags for the individual radio buttons.
          * The 'radioButton' parameter specifies the instance to be
          * queried, while the 'data' parameter must point to an array
          * of INT8 values, into which the state flags will be returned.
          */
         return ((xrEditor *)_MsgGetItemStates(radioButton, data));
      }

      case MSG_SETITEMSTATES:
      {
         /*
          * Set the individual radio button states; redraw only those
          * whose state has then changed.  The 'radioButton' parameter
          * specifies the instance to be modified, while the 'data'
          * parameter points to an array of INT8 values, containing
          * the new state flags.
          */
         return ((xrEditor *) _MsgSetItemStates (radioButton, data, 
                                                 drawRButtons));
      }

      case MSG_GETITEMCOUNT:
      {
         /*
          * Return the number of radio buttons in the specified instance.
          * The 'radioButton' parameter specifies the instance to be
          * queried, while the 'data' parameter must point to an INT32
          * value, into which the item count will be placed.
          */
         return ((xrEditor *) _MsgGetItemCount (radioButton, data));
      }

      case MSG_GETITEMRECTS:
      {
         /*
          * Fill the array passed in by the application, with the
          * rectangle information describing each radio button in
          * the specified instance.  The 'radioButton' parameter
          * indicates the instance to be queried, while the 'data'
          * parameter must point to an array of RECTANGLE structures,
          * into which the individual button rectangles will be placed.
          */
         return ((xrEditor *) _MsgGetItemRects (radioButton, data));
      }

      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 'xrRadioButtonInfo' structure; upon completion,
            * the 'editorRect' field of the 'info' structure will
            * be filled.
	    */
           return ((xrEditor *) _MsgSize (data, calcRBComponents));
      }

      case MSG_MOVE:
      {
         /*
          * Relocate an instance of radio buttons to a new location.
          * The 'radioButton' parameter specifies the instance to be
          * moved, while the 'data' parameter must point to a POINT
          * structure, containing the new editor rectangle origin.
          */
         return ((xrEditor *) _MsgMove (radioButton, data, drawRButtons));
      }

      case MSG_RESIZE:
      {
         /*
          * Resize an existing instance of radio buttons.
          * The 'radioButton' parameter indicates the instance
          * to be resized, while the 'data' parameter must point
          * to a RECTANGLE structure, containing the new editor
          * rectangle definition.
          */
                  RECTANGLE           workRect;
                  xrRadioButtonInfo   rbInfo;
         register xrRadioButtonData * rbDataPtr;
         register xrRadioButtonInfo * rbInfoPtr;

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

         /* Create a pseudo Info structure */
         rbDataPtr = (xrRadioButtonData *) radioButton->editorData;
         XrCopyRect (&radioButton->editorRect, &workRect);
         rbInfoPtr = &rbInfo;
         XrCopyRect ((RECTANGLE *) data, &rbInfoPtr->editorRect);
         rbInfoPtr->editorFont = rbDataPtr->rbFont.fontInfo;
         rbInfoPtr->numFields = rbDataPtr->rbNumFields;
         rbInfoPtr->numCols = rbDataPtr->rbNumCols;
         rbInfoPtr->labels = rbDataPtr->rbLabels;

         /* Calculate the component positions */
         if (createRButtons (rbDataPtr, rbInfoPtr, MSG_RESIZE) == FALSE)
            return ((xrEditor *) NULL);
         XrCopyRect (&rbInfoPtr->editorRect, &radioButton->editorRect);

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

            /* Redisplay the instance */
            drawRButtons (radioButton, XrALLBUTTONS);
         }

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

      case MSG_REDRAW:
      {
         /*
          * Redraw a radio button instance.
          * The 'radioButton' parameter specifies the instance to redraw,
          * while the 'data' parameter is interpreted as an INT32 value,
          * specifying the type of redraw to perform.
          */
                  INT32 redrawMode = (INT32) data;
         register xrRadioButtonData * rbDataPtr;

         /* Validate the instance pointer and the redraw mode */
         if (radioButton == NULL)
         {
            xrErrno = XrINVALIDID;
            return ((xrEditor *) NULL);
         }
         else if (redrawMode == XrREDRAW_ALL) 
         {
            rbDataPtr = (xrRadioButtonData *) radioButton->editorData;

            /* Check for an invalid active button index */
            if ((*rbDataPtr->rbActive < 0) ||
                (*rbDataPtr->rbActive >= rbDataPtr->rbNumFields))
            {
               *rbDataPtr->rbActive = rbDataPtr->rblastActive;
               xrErrno = XrPARMOUTOFRANGE;
               return ((xrEditor *) NULL);
            }

            if (radioButton->editorState & XrVISIBLE)
               drawRButtons (radioButton, XrALLBUTTONS);

            rbDataPtr->rblastActive = *rbDataPtr->rbActive;
            return (radioButton);
         }
         else if (redrawMode == XrREDRAW_ACTIVE)
         {
            rbDataPtr = (xrRadioButtonData *) radioButton->editorData;

            /* Check for an invalid active button index */
            if ((*rbDataPtr->rbActive < 0) ||
                (*rbDataPtr->rbActive >= rbDataPtr->rbNumFields))
            {
               *rbDataPtr->rbActive = rbDataPtr->rblastActive;
               xrErrno = XrPARMOUTOFRANGE;
               return ((xrEditor *) NULL);
            }

            if (radioButton->editorState & XrVISIBLE)
            {
               if (rbDataPtr->rblastActive != *rbDataPtr->rbActive)
                  drawRButtons (radioButton, rbDataPtr->rblastActive);
               drawRButtons (radioButton, *rbDataPtr->rbActive);
            }
            rbDataPtr->rblastActive = *rbDataPtr->rbActive;
            return (radioButton);
         }
         else
         {
            xrErrno = XrINVALIDOPTION;
            return ((xrEditor *) NULL);
         }
      }

      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 (radioButton, data, 
                                        processRButtons, XrRADIOBUTTON));
      }

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

   }  /* end of switch */
}  /* end of XrRadioButton() */

/*************************************<->*************************************
 *
 *  INT32
 *  rbFreeMemory (rbDataPtr)
 *
 *     xrRadioButtonData * rbDataPtr;
 *
 *   Description:
 *   -----------
 *     This routine is called both when an existing instance is freed,
 *     and when a MSG_NEW request fails after createRButtons() has
 *     been called.  It will free up all resources which createRButtons()
 *     allocated for the instance.
 *
 *
 *   Inputs:
 *   ------
 *     rbDataPtr = This is a pointer to the instance's internal 'data'
 *                 structure.
 * 
 *   Outputs:
 *   -------
 *
 *   Procedures Called
 *   -----------------
 *   XFreePixmap()  [libX.a]
 *
 *************************************<->***********************************/

static
INT32
rbFreeMemory (rbDataPtr)

   xrRadioButtonData * rbDataPtr;

{
   /*
    * Free up the background tile, and the array holding the
    * individual radio button information.
    */
   if (rbDataPtr->rbTileId != xrDefaultTile)
      XFreePixmap (rbDataPtr->rbTileId);
   (*xrFree) (rbDataPtr->rbFields);
}


/*************************************<->*************************************
 *
 *  INT32
 *  calcRBComponents (rbInfoPtr, fields, cmd)
 *
 *     xrRadioButtonInfo * rbInfoPtr;
 *     xrItemData        * fields;
 *     INT32               cmd;
 *
 *   Description:
 *   -----------
 *     If the 'cmd' parameter is set to MSG_SIZE, then this routine will
 *     calculate the size of the editor rectangle needed to contain the
 *     instance described by the structure pointed to by 'rbInfoPtr; the
 *     editor rectangle information is returned in the 'editorRect' field
 *     within the structure pointed to by the 'rbInfoPtr' parameter.
 *     If the 'cmd' parameter is set to any other value, then this
 *     routine will calculate the location and size of each radio button
 *     rectangle and corresponding button label; this information is then
 *     stored in the structure pointed to by the 'fields' parameter.
 *
 *
 *   Inputs:
 *   ------
 *     rbInfoPtr = This points to an instance of the radio button 'info'
 *                 structure, which describes the instance being sized
 *                 or created.
 *
 *     fields = This must point to an array of structures, which will be
 *              used to store temporary information, if 'cmd' is set to
 *              MSG_SIZE, or will be used to store the component information,
 *              if 'cmd' is set to MSG_NEW.
 *
 *     cmd = This indicates whether just the size of the editor rectangle
 *           needs to be calculated (MSG_SIZE), or whether the size and
 *           location of each component must be calculated (MSG_NEW).
 * 
 *   Outputs:
 *   -------
 *     Upon successful completion, TRUE is returned, along with some
 *          additional information in either the 'rbInfoPtr' structure,
 *          or the 'fields' structure.
 *
 *     Upon failure, FALSE is returned, and xrErrno is set.
 *
 *   Procedures Called
 *   -----------------
 *   _XrTextInfo()    [textUtil.c]
 *   XrStringWidth()  [utilities.c]
 *   XrCopyRect()     [calc.c]
 *   XrSetRect()      [calc.c]
 *   XrSetPt()        [calc.c]
 *
 *************************************<->***********************************/

static
INT32
calcRBComponents (rbInfoPtr, fields, cmd)

   register xrRadioButtonInfo  * rbInfoPtr;
   register xrItemData         * fields;
            INT32                cmd;

{
            INT16        numFields = rbInfoPtr->numFields;
            INT16        numCols = rbInfoPtr->numCols;
            INT16        numRows;
            RECTANGLE  * rectPtr = &(rbInfoPtr->editorRect);
            FontInfo   * fontPtr;
            xrTextInfo   fontData;
   register INT16        i, col;
            INT16        hPadding, vPadding;
            INT16        xPos, yPos;
            INT16        itemWidth, maxItemWidth;
            INT16        heightMin, widthMin;
            INT16        lineHt;

   /* Check for invalid dimensions */
   if ((numFields < numCols) || (numFields <= 0) || (numCols <= 0))
   {
      xrErrno = XrINVALIDPARM;
      return (FALSE);
   }

   /* Initialize variables we'll need for our calculations */
   numRows = (numFields + numCols - 1) / numCols;
   fontPtr = rbInfoPtr->editorFont ? rbInfoPtr->editorFont : xrBaseFontInfo;
   _XrTextInfo (fontPtr, &fontData);
   lineHt = fontData.ascent + fontData.descent;
   rectPtr = &(rbInfoPtr->editorRect);

   /*
    * Determine the preliminary size of the instance.  This is done by
    * using the following formulae:
    *
    *   height = number of Rows * height of a line
    *   width  = Sum of longest item in each column
    *
    * An item in a column is composed of a radio button, some padding,
    * and an optional label.
    */

   heightMin = numRows * lineHt;
   for (widthMin = 0, col = 0; col < numCols; col++)
   {
      /* Keep track of longest item in each column */
      maxItemWidth = 0;

      /* Calculate the width of each item in the column */
      for (i = col; i < numFields; i += numCols)
      {
         /* Take into account the size of the radio button */
         itemWidth = lineHt;

         /* Include the label and padding, if a label is defined */
         if ((rbInfoPtr->labels) && (rbInfoPtr->labels[i]))
            itemWidth += fontData.avgWidth + XrStringWidth (fontData.fontInfo,
                         rbInfoPtr->labels[i], XrNULLTERMINATED, 0, 0);

         /* See if this is the longest item encountered in the column */
         if (itemWidth > maxItemWidth)
            maxItemWidth = itemWidth;
      }

      /* Keep a running total of all the column width */
      widthMin += maxItemWidth;
   }

   /*
    * If this is being done in response to a MSG_SIZE request, then
    * take the height and width values calculated above, and add some
    * default padding values between each row and column; then return
    * the rectangle definition.  The following padding values are used:
    *
    *    hPadding = 2 * font leading     vPadding = font leading
    */

   if (cmd == MSG_SIZE)
   {
      /* Add the default padding */
      XrCopyRect (&xrZeroRect, rectPtr);
      rectPtr->height = heightMin + (numRows - 1) * fontData.leading;
      rectPtr->width = widthMin + (numCols - 1) * (fontData.leading << 1);
      return (TRUE);
   }

   /* If the rectangle passed in by the application is larger than the
    * preliminary rectangle calculated above, then use this extra space
    * as padding between the row and column items.
    */

   hPadding = (rectPtr->width - widthMin)/((numCols == 1) ? 1 : (numCols - 1));
   vPadding = (rectPtr->height - heightMin)/((numRows == 1) ? 1:(numRows - 1));

   /* Now, we can calculate the rectangle definition for each radio button */

   for (xPos = rectPtr->x, col = 0; col < numCols; col++)
   {
      maxItemWidth = 0;
      yPos = rectPtr->y;

      for (i = col; i < numFields; i += numCols)
      {
         /* Keep track of the longest item in a column */
         itemWidth = lineHt;
         if ((rbInfoPtr->labels) && (rbInfoPtr->labels[i]))
            itemWidth += fontData.avgWidth + XrStringWidth (fontData.fontInfo,
                         rbInfoPtr->labels[i], XrNULLTERMINATED, 0, 0);
         if (itemWidth > maxItemWidth)
            maxItemWidth = itemWidth;

         /* Set the rectangle describing the complete radio button */
         XrSetRect (&((fields+i)->subRectangle), xPos, yPos, itemWidth, lineHt);

         /* Set the rectangle describing just the radio button; no label */
         XrSetRect (&((fields+i)->rectangle), xPos, yPos, lineHt, lineHt);

         /* Set the point describing where the label is displayed */
         XrSetPt (&((fields+i)->labelPt), xPos + lineHt + fontData.avgWidth,
                  yPos);

         /* Increment for the next radio button in the column */
         yPos += lineHt + vPadding;
      }

      /* Increment for the start of the next column */
      xPos += maxItemWidth + hPadding;
   }

   return (TRUE);
}


/*************************************<->*************************************
 *
 *  static
 *  INT32
 *  createRButtons (rbDataPtr, rbInfoPtr, message)
 *
 *     xrRadioButtonData * rbDataPtr;
 *     xrRadioButtonInfo * rbInfoPtr;
 *     INT32               message;
 *
 *   Description:
 *   -----------
 *     if (message == MSG_NEW):
 *     This routine is responsible for creating a new radio button instance.
 *     The description of the new instance is contained within the structure
 *     pointed to by the 'rbInfoPtr' parameter.  Besides allocating the
 *     resources needed for the new instance, this procedure will also fill
 *     in the structure pointed to by the 'rbDataPtr' parameter with the
 *     state information for the new instance.
 *
 *     if (message == MSG_RESIZE)
 *     This routine will take the information contained within the 'rbInfoPtr'
 *     structure, and will recalculate the location of each component within
 *     an existing instance of radio buttons, to match the new data.  This
 *     will do no resource allocation.
 *
 *
 *   Inputs:
 *   ------
 *     rbDataPtr = Points to the 'data' structure for the instance being
 *                 created or resized.
 *
 *     rbInfoPtr = 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 'rbDataPtr'
 *          structure is filled out.
 *
 *     Upon failure, FALSE is returned, and xrErrno is set.
 *
 *   Procedures Called
 *   -----------------
 *   XrResource()   [resource.c]
 *   XrCopyRect()   [calc.c]
 *   _XrTextInfo()  [textUtil.c]
 *   XMakePixmap()  [libX.a]
 *   XFreePixmap()  [libX.a]
 *   calcRBComponents()
 *
 *************************************<->***********************************/

static
INT32
createRButtons (rbDataPtr, rbInfoPtr, message)

   register xrRadioButtonData * rbDataPtr;
   register xrRadioButtonInfo * rbInfoPtr;
            int                 message;

{
            FontInfo     * fontPtr;
   register xrItemData   * aButton;
   register INT16          i;
            xrResourceInfo bitmapInfo;
            RECTANGLE      minRect;
            RECTANGLE      editorRect;
            xrItemData   * tempItems;

   /* Allocate space to hold our rectangle size calculations */
   if ((tempItems = (xrItemData *) (*xrMalloc) 
       (sizeof (xrItemData) * rbInfoPtr->numFields)) == NULL)
   {
      /* Unable to allocate the memory we need */
      xrErrno = XrOUTOFMEM;
      return (FALSE);
   }

   if (message == MSG_NEW)
   {
      /* Validate incoming parameters */
      if (rbInfoPtr->value == NULL)
      {
         (*xrFree)(tempItems);
         xrErrno = XrINVALIDPTR;
         return (FALSE);
      }
      else if ((*rbInfoPtr->value < 0) || 
               (*rbInfoPtr->value >= rbInfoPtr->numFields))
      {
         (*xrFree)(tempItems);
         xrErrno = XrPARMOUTOFRANGE;
         return (FALSE);
      }

      /* Allocate space to hold the individual radio button information */
      if ((rbDataPtr->rbFields = (xrItemData *) (*xrMalloc) 
          (sizeof (xrItemData) * rbInfoPtr->numFields)) == NULL)
      {
         /* Unable to allocate the memory we need */
         (*xrFree)(tempItems);
         xrErrno = XrOUTOFMEM;
         return (FALSE);
      }

      rbDataPtr->rbFGColor = (rbInfoPtr->editorFGColor == -1) ?
                  xrForegroundColor : rbInfoPtr->editorFGColor;
      rbDataPtr->rbBGColor = (rbInfoPtr->editorBGColor == -1) ?
                  xrBackgroundColor : rbInfoPtr->editorBGColor;

      /* Create the 50% tile we will need when drawing insensitive box */
      if ((rbInfoPtr->editorFGColor == -1) &&
          (rbInfoPtr->editorBGColor == -1))
      {
         /* Use the default 50% tile */
         rbDataPtr->rbTileId = xrDefaultTile;
      }
      else
      {
         bitmapInfo.resourceType = XrTYPE_BITMAPID;
         bitmapInfo.resourceId = XrPERCENT50;
         if ((XrResource (MSG_FIND, &bitmapInfo) == FALSE) ||
            ((rbDataPtr->rbTileId = 
            XMakePixmap (((xrBitmapId *)bitmapInfo.resourceObject)->bitmapId,
                        rbDataPtr->rbFGColor, rbDataPtr->rbBGColor)) == 0))
         {
            /* Unable to create the tile */
            (*xrFree)(tempItems);
            (*xrFree) (rbDataPtr->rbFields);
            xrErrno = XrXCALLFAILED;
            return (FALSE);
         }
      }
   }
   
   /* Check the rectangle definition */
   XrCopyRect (&rbInfoPtr->editorRect, &editorRect);
   if (calcRBComponents (rbInfoPtr, tempItems, MSG_SIZE) == FALSE)
   {
      /* The application supplied invalid information */
      /* xrErrno is set by calcRBComponents */
      XrCopyRect (&editorRect, &rbInfoPtr->editorRect);
      (*xrFree) (tempItems);
      if (message == MSG_NEW)
      {
         if (rbDataPtr->rbTileId != xrDefaultTile)
            XFreePixmap (rbDataPtr->rbTileId);
         (*xrFree) (rbDataPtr->rbFields);
      }
      return (FALSE);
   }
   (*xrFree)(tempItems);
   XrCopyRect (&rbInfoPtr->editorRect, &minRect);
   XrCopyRect (&editorRect, &rbInfoPtr->editorRect);
   if ((editorRect.height < minRect.height) ||
       (editorRect.width < minRect.width))
   {
      /* The application supplied an invalid rectangle */
      xrErrno = XrINVALIDRECT;
      if (message == MSG_NEW)
      {
         if (rbDataPtr->rbTileId != xrDefaultTile)
            XFreePixmap (rbDataPtr->rbTileId);
         (*xrFree) (rbDataPtr->rbFields);
      }
      return (FALSE);
   }

   /* Generate the location and size information associated with each button */
   calcRBComponents (rbInfoPtr, rbDataPtr->rbFields, MSG_NEW);

   if (message == MSG_NEW)
   {
      /* Fill in the rest of the fields in the radio button structures */
      rbDataPtr->rbNumFields = rbInfoPtr->numFields;
      rbDataPtr->rbNumCols = rbInfoPtr->numCols;
      fontPtr = rbInfoPtr->editorFont ? rbInfoPtr->editorFont : xrBaseFontInfo;
      _XrTextInfo (fontPtr, &rbDataPtr->rbFont);
      rbDataPtr->rbLabels = rbInfoPtr->labels;
      rbDataPtr->rbActive = rbInfoPtr->value;
      rbDataPtr->rblastActive = *(rbInfoPtr->value);
      rbDataPtr->rbStates = rbInfoPtr->stateFlags;
      aButton = rbDataPtr->rbFields;

      for (i = 0; i < rbDataPtr->rbNumFields; i++, aButton++)
      {
         /* Copy over each radio button label */
         if (rbInfoPtr->labels)
            aButton->label = rbInfoPtr->labels[i];
         else
            aButton->label = NULL;
   
         /* Copy the state flags */
         if (rbInfoPtr->stateFlags)
            aButton->state = rbInfoPtr->stateFlags[i];
         else
            aButton->state =  (XrVISIBLE | XrSENSITIVE);
      }
   }

   return (TRUE);
}


/*************************************<->*************************************
 *
 *  static
 *  INT32
 *  drawRButtons (radioButton, buttonNum)
 *
 *     xrEditor * radioButton;
 *     INT32      buttonNum;
 *
 *   Description:
 *   -----------
 *     This routine will display either a single radio button, or else
 *     all of the radio buttons, depending upon the value specified in
 *     the 'buttonNum' parameter.
 *
 *
 *   Inputs:
 *   ------
 *     radioButton = Points to the editor instance structure.
 *
 *     buttonNum = This can be set to the index of the button to be drawn,
 *                 or to the define XrALLBUTTONS, if all the buttons should
 *                 be redrawn.
 * 
 *   Outputs:
 *   -------
 *
 *   Procedures Called
 *   -----------------
 *   _XrMakeInvisible()  [editorUtil.c]
 *   _XrInitEditorGCs()  [gcUtil.c]
 *   _XrCopyGC()         [gcUtil.c]
 *   _XrChangeGC()       [gcUtil.c]
 *   XrCopyRect()        [calc.c]
 *   _XrFillEllipse()    [ellipUtil.c]
 *   _XrEllipse()        [ellipUtil.c]
 *   _XrImageText8()     [textUtil.c]
 *
 *************************************<->***********************************/

static
INT32
drawRButtons (radioButton, buttonNum)

   xrEditor * radioButton;
   INT32      buttonNum;

{
   register xrRadioButtonData   * rbDataPtr;
            Window                windowId;
            INT32                 sensitive;
   register INT16                 firstButton, lastButton;
   register xrItemData          * aButton;
            RECTANGLE             workRect;
   register RECTANGLE           * workRectPtr;
            INT32                 changeList[21];
            INT8                  makeGCFlag;

   /* Initialize variables */
   rbDataPtr = (xrRadioButtonData *) radioButton->editorData;
   windowId = radioButton->editorWindowId;
   workRectPtr = &workRect;
   makeGCFlag = TRUE;

   /* Check for an invalid active button index */
   if ((*rbDataPtr->rbActive < 0) ||
       (*rbDataPtr->rbActive >= rbDataPtr->rbNumFields))
   {
      *rbDataPtr->rbActive = rbDataPtr->rblastActive;
   }
   else
      rbDataPtr->rblastActive = *rbDataPtr->rbActive;

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

   /* Initialize the standard graphic contexts we will be using */
   _XrInitEditorGCs (rbDataPtr->rbFGColor, rbDataPtr->rbBGColor,
                     rbDataPtr->rbFont.fontInfo->id);

   /* Initialize the graphics context used to draw insensitive radio buttons */
   _XrCopyGC (xrDefaultGC, xrEditorGC3);
   changeList[XrTILEVAL] = rbDataPtr->rbTileId;
   changeList[XrFILLSTYLEVAL] = Tiled;
   _XrChangeGC (xrEditorGC3, (XrTILE | XrFILLSTYLE), changeList);

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

   /* Determine the range of radio buttons to be drawn */
   if (buttonNum == XrALLBUTTONS)
   {
      firstButton = 0;
      lastButton = rbDataPtr->rbNumFields - 1;
   }
   else
      firstButton = lastButton = buttonNum;

   while (firstButton <= lastButton)
   {
      aButton = (rbDataPtr->rbFields) + firstButton;
      XrCopyRect (&(aButton->rectangle), workRectPtr);

      /* Determine how to draw the button, depending upon it's state */
      if (aButton->state & XrVISIBLE)
      {
         if (sensitive && (aButton->state & XrSENSITIVE))
         {
            /* Determine if the button is active or inactive */
            if (firstButton == *(rbDataPtr->rbActive))
            {
               /*
                * The state of the radio button is 'ACTIVE', so draw
                * it as a filled circle, using the foreground color.
                */
               draw1Button (windowId, xrEditorGC1, xrEditorGC1, workRectPtr);
            }
            else
            {
               /*
                * The state of the radio button is 'INACTIVE', so draw
                * draw the circle with the border in the foreground
                * color, and the interior in the background color.
                */
               draw1Button (windowId, xrEditorGC2, xrEditorGC1, workRectPtr);
            }
         }
         else
         {
            /*
             * The radio button is insensitive, so we will draw it as a
             * filled circle, using the 50% tile.
             */
            draw1Button (windowId, xrEditorGC3, xrEditorGC1, workRectPtr);
         }

         /* If the radio button has a label, then we will draw that now. */
         if ((aButton->label) && (strlen(aButton->label) > 0))
            _XrImageText8 (windowId, xrEditorGC1, strlen(aButton->label),
                           aButton->labelPt.x, aButton->labelPt.y,
                           aButton->label);
      }
      else
      {
         /*
          * The radio button is not visible, so fill the area it would occupy
          * with the window's background color.
          */
         _XrMakeInvisible (windowId, &(aButton->subRectangle), makeGCFlag);
          makeGCFlag = FALSE;
      }

      /* Go on and process the next radio button */
      firstButton++;
   }
}


/*************************************<->*************************************
 *
 *  static
 *  INT32
 *  processRButtons (radioButton, input, returnEvent)
 *
 *     xrEditor     * radioButton;
 *     XButtonEvent * input;
 *     xrEvent      * returnEvent;
 *
 *   Description:
 *   -----------
 *     This is the event processing routine for the radio button editor.
 *     It takes an event, and determines which, if any, of the radio
 *     buttons the event occurred within.  If a radio button was selected,
 *     then it will be redrawn as active, and its index will be returned
 *     to the application.
 *
 *
 *   Inputs:
 *   ------
 *     radioButton = This is the instance pointer for the instance in which
 *                   the event occurred.
 *
 *     input = This points to the event to be processed.
 *
 *     returnEvent = This points to a partially filled out X-ray event
 *                   structure.  It can be used by this routine when it
 *                   needs to push a return event on the input queue.
 *                   Every field is already filled, except for the 'value'
 *                   fields.
 * 
 *   Outputs:
 *   -------
 *   Although a value is not directly returned, an input event will be
 *       pushed onto the input queue, telling the application which
 *       part of the instance was selected.
 *
 *   Procedures Called
 *   -----------------
 *   XrSetPt()     [calc.c]
 *   XrPtInRect()  [calc.c]
 *   drawRButtons()
 *
 *************************************<->***********************************/

static
INT32
processRButtons (radioButton, input, returnEvent)

   xrEditor     * radioButton;
   XButtonEvent * input;
   xrEvent      * returnEvent;

{
   register xrRadioButtonData  * rbDataPtr;
            POINT                spritePt;
   register INT16                i;
   register xrItemData         * aButton;

   rbDataPtr = (xrRadioButtonData *) radioButton->editorData;
   XrSetPt (&spritePt, input->x, input->y);
   returnEvent->value1 = NULL;

   /* Check each radio button to see if one was selected */
   for (i = 0; i < rbDataPtr->rbNumFields; i++)
   {
      aButton = (rbDataPtr->rbFields) + i;

      if (XrPtInRect (&spritePt, &(aButton->rectangle)) &&
         ((aButton->state & (XrVISIBLE | XrSENSITIVE)) ==
         (XrVISIBLE | XrSENSITIVE)))
      {
         /* A radio button was selected; modify its value and redraw it */
         *rbDataPtr->rbActive = i;
         returnEvent->value3 = rbDataPtr->rblastActive;
         if (i != rbDataPtr->rblastActive)
            drawRButtons (radioButton, rbDataPtr->rblastActive);
         drawRButtons (radioButton, i);
  
         /* Save the index of the radio button which changed, and return */
         returnEvent->value2 = i;
         returnEvent->value1 = XrSELECT;
      }
   }
}


/*************************************<->*************************************
 *
 *  draw1Button (windowId, fillGC, borderGC, rectPtr)
 *
 *     Window      windowId;
 *     INT32       fillGC;
 *     INT32       borderGC;
 *     RECTANGLE * rectPtr;
 *
 *   Description:
 *   -----------
 *     This routines draws and fills a single radio button.  The button
 *     is drawn as a 12 sided polygon.
 *
 *
 *   Inputs:
 *   ------
 *     windowId = Id of window in which button is to be drawn.
 *
 *     fillGC = Graphic context to use to fill the interior of the button.
 *
 *     borderGC = Graphic context to use to draw the button border.
 *
 *     rectPtr = Describes the square in which the button will be drawn.
 * 
 *   Outputs:
 *   -------
 *
 *   Procedures Called
 *   -----------------
 *   _XrPoly()      [polyUtil.c]
 *   _XrFillPoly()  [polyUtil.c]
 *
 *************************************<->***********************************/

static
INT32
draw1Button (windowId, fillGC, borderGC, rect)

   Window      windowId;
   INT32       fillGC;
   INT32       borderGC;
   RECTANGLE * rect;

{
   INT16 rightEdge, bottom;
   INT16 oneEighthH, threeEighthsH;
   INT16 oneEighthW, threeEighthsW;
   INT16 halfHeight, halfWidth;
   INT32 i;
   RECTANGLE workRect;

   /*
    * Calculate our working values.
    * Force the rectangle to an odd height and width.
    */
   XrCopyRect (rect, &workRect);
   if ((workRect.height & 0x01) == 0)
      workRect.height --;
   if ((workRect.width & 0x01) == 0)
      workRect.width --;

   rightEdge = workRect.x + workRect.width - 1;
   bottom = workRect.y + workRect.height - 1;
   oneEighthH = workRect.height >> 3;
   oneEighthW = workRect.width >> 3;
   threeEighthsH = (workRect.height * 3) >> 3;
   threeEighthsW = (workRect.width * 3) >> 3;
   halfWidth = workRect.x + (workRect.width >> 1);
   halfHeight = workRect.y + (workRect.height >> 1);

   /* Fill in the 'flags' field of the polygon structure */
   for (i = 0; i < 13; i++)
      xr_PolyList[i].flags = 0;

   /* Calculate the polygon endpoints */
   xr_PolyList[0].x = halfWidth - oneEighthW;
   xr_PolyList[0].y = workRect.y;
   xr_PolyList[1].x = halfWidth + oneEighthW;
   xr_PolyList[1].y = workRect.y;
   xr_PolyList[2].x = halfWidth + threeEighthsW;
   xr_PolyList[2].y = halfHeight - threeEighthsH;
   xr_PolyList[3].x = rightEdge;
   xr_PolyList[3].y = halfHeight - oneEighthH;
   xr_PolyList[4].x = rightEdge;
   xr_PolyList[4].y = halfHeight + oneEighthH;
   xr_PolyList[5].x = halfWidth + threeEighthsW;
   xr_PolyList[5].y = halfHeight + threeEighthsH;
   xr_PolyList[6].x = halfWidth + oneEighthW;
   xr_PolyList[6].y = bottom;
   xr_PolyList[7].x = halfWidth - oneEighthW;
   xr_PolyList[7].y = bottom;
   xr_PolyList[8].x = halfWidth - threeEighthsW;
   xr_PolyList[8].y = halfHeight + threeEighthsH;
   xr_PolyList[9].x = workRect.x;
   xr_PolyList[9].y = halfHeight + oneEighthH;
   xr_PolyList[10].x = workRect.x;
   xr_PolyList[10].y = halfHeight - oneEighthH;
   xr_PolyList[11].x = halfWidth - threeEighthsW;
   xr_PolyList[11].y = halfHeight - threeEighthsH;
   xr_PolyList[12].x = halfWidth - oneEighthW;
   xr_PolyList[12].y = workRect.y;

   /* Draw the button */
   _XrFillPoly (windowId, fillGC, 13, xr_PolyList);
   _XrPoly (windowId, borderGC, 13, xr_PolyList);
}
