/*
 *	$Source: /u1/Xr/src/Xrlib/Intrinsic/RCS/group.c,v $
 *	$Header: group.c,v 1.1 86/12/17 09:08:14 swick Exp $
 */

#ifndef lint
static char *rcsid_group_c = "$Header: group.c,v 1.1 86/12/17 09:08:14 swick Exp $";
#endif	lint

#include <Xr/xr-copyright.h>

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

static char rcsid[] = "$Header: group.c,v 1.1 86/12/17 09:08:14 swick Exp $";
/*************************************<+>*************************************
 *****************************************************************************
 **
 **   File:        group.c
 **
 **   Project:     X-ray Toolbox
 **
 **   Description: This file contains the editor group handling function.
 **
 **
 **   ------------------------ MODIFICATION RECORD   ------------------------
 *
 * $Log:	group.c,v $
 * Revision 1.1  86/12/17  09:08:14  swick
 * Initial revision
 * 
 * Revision 7.0  86/11/13  08:19:49  08:19:49  rick ()
 * Final QA release
 * 
 * Revision 6.0  86/11/10  15:21:39  15:21:39  rick ()
 * QA #2 release
 * 
 * Revision 5.1  86/11/07  14:01:43  14:01:43  rick ()
 * Added the copyright message.
 * 
 * Revision 5.0  86/10/28  08:22:29  08:22:29  rick ()
 * QA #1.1 release
 * 
 * Revision 4.0  86/10/20  12:08:31  12:08:31  rick ()
 * QA 1 release
 * 
 * Revision 3.2  86/10/17  12:23:13  12:23:13  rick ()
 * Linted
 * 
 * Revision 3.1  86/10/16  11:20:23  11:20:23  rick ()
 * Added register variables.
 * 
 * Revision 3.0  86/10/02  15:58:31  15:58:31  rick ()
 *  Alpha release set to 3.0
 * 
 * Revision 2.7  86/09/22  12:34:08  12:34:08  rick ()
 * Finished MSG_ADJUSTGROUPRECT by removing the comparison between the
 * editor rectangle and group rectangle.
 * 
 * Revision 2.6  86/09/22  11:33:21  11:33:21  rick ()
 * Added a condition to MSG_SETGROUPRECT to allow a NULL data parameter
 * which will in turen force a recalculation of the group rectangle.
 * Added MSG_ADJUSTGROUPRECT to be used by XrEditor to recalculate
 * the minimal group rectangle when a editor is removed.
 * 
 * Revision 2.5  86/09/19  17:55:27  17:55:27  rick ()
 * Fixed MSG_FREE so that it works properly.
 * 
 * Revision 2.4  86/09/19  11:22:30  11:22:30  rick ()
 * Added the condition in MSG_SETSTATE that causes a redraw of any
 * groups which intersect the group being turned invisible.
 * 
 * Revision 2.3  86/09/18  16:29:34  16:29:34  rick ()
 * Added checking for NULL groupInstance's and data parameters.
 * 
 * Revision 2.2  86/09/18  12:40:26  12:40:26  rick ()
 * Added message REDRAW which redraws a single editor group.
 * 
 * Revision 2.1  86/09/18  10:47:49  10:47:49  rick ()
 * Changed the initialization of a new group's state to
 * VISIBLE or SENSITIVE in MSG_NEW.
 * 
 * Revision 2.0  86/09/16  07:59:48  07:59:48  rick ()
 * *** empty log message ***
 * 
 * Revision 1.1  86/09/11  08:06:15  08:06:15  rick ()
 * Initial revision
 * 
 *
 *****************************************************************************
 *************************************<+>*************************************/

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


/*
 *  Resource info structure used to access the registered windows in
 *  the resource manager.
 */

static xrResourceInfo resourceInfo =
   { XrTYPE_REGWINDOW, 0, 0, XrMEMORY, NULL };




/*************************************<->*************************************
 *
 *  XrEditorGroup (groupInstance, message, data)
 *  xrEditorGroup * groupInstance;
 *  INT32  message;
 *  INT8 * data;
 *
 *
 *
 *   Description:
 *   -----------
 *     XrEditorGroup() provides for the grouping of editors within 
 *     a window.
 *     
 *     For a complete description of the editor group function, refer to
 *     XrEditorGroup(3XR) and the X-ray toolbox manual.
 *
 *
 *   Inputs:
 *   ------
 *     groupInstance = Contains a pointer to the group the message is
 *                     to operate upon.
 *
 *     message = Contains the message which describes the capability
 *               to be executed.
 *
 *     data = Contains a pointer to a structure necessary for the
 *            completion of the message.
 *
 *     For a complete description of the editor group messages and
 *     data parameters, refer to XrEditorGroup(3XR) and the X-ray 
 *     toolbox manual.
 * 
 *   Outputs:
 *   -------
 *     groupInstance = Returned as the value of the function if the 
 *                     message succeeds.
 *
 *     FALSE = Returned as the value of the function if the message fails.
 *
 *     data  = Several of the messages return information in the structure
 *             pointed at by data.
 *
 *     xrError = The error variable is set to one several values upon
 *               failure of this routine.
 *
 *     For a complete description of the values returned in data, 
 *     refer to XrResource(3XR) and the X-ray toolbox manual.
 *
 *
 *   Procedures Called
 *   -----------------
 *     XrResource  -  resource.c
 *     XrInput     -  input.c
 *     XrSetRect   -  calc.c
 *     XrSectRect  -  calc.c
 *     XrCopyRect  -  calc.c
 *     XrUnionRect -  calc.c
 *     XrEqualRect -  calc.c
 *     XTileSet    -  Xlib
 *
 *************************************<->***********************************/


xrEditorGroup *
XrEditorGroup (groupInstance, message, data)
xrEditorGroup * groupInstance;
INT32  message;
INT8 * data;

{


   /*
    *  Set up a switch to handle all of the editor group's messages.
    */

   switch (message)
   {

      /*
       *  Create a new editor group, attach it to the windowId contained
       *  in the parameter data, and return the group instance pointer.
       */

      case MSG_NEW:
      {
          Window windowId;
          register xrWindow      * window;
          register xrEditorGroup * newGroup;
          register xrEditorGroup * groupPtr;

          windowId = (Window) data;


          /*
           *  Go get the window resource and extract the window
           *  and group list pointers.
           */

          resourceInfo.resourceId = windowId;
          if (XrResource (MSG_FIND, &resourceInfo) == FALSE)
             return (FALSE);

          window = (xrWindow *) resourceInfo.resourceObject;
          groupPtr = window -> groupList.nextGroup;


          /*
           *  Allocate a new group structure to be attached to the
           *  group list.
           */

          if ((newGroup = 
              (xrEditorGroup *) (*xrMalloc) (sizeof (xrEditorGroup))) == NULL)
          {
             xrErrno = XrOUTOFMEM;
             return (FALSE);
          }


          /*
           *  Initialize the group elements and set the active group.
           */

          newGroup -> groupWindowId = windowId;
          XrSetRect (&newGroup -> groupRect, 0, 0, 0, 0);
          newGroup -> groupState = XrVISIBLE | XrSENSITIVE;
          newGroup -> editorList = NULL;
          newGroup -> graphicList = NULL;
          newGroup -> nextGroup = NULL;
          window -> activeGroup = newGroup;


          /*
           *  Find the end of the group list and attach the new
           *  editor group.
           */

         if (groupPtr == NULL)
            window -> groupList.nextGroup = newGroup;
         else
         {
            while (groupPtr -> nextGroup != NULL)
               groupPtr = groupPtr -> nextGroup;
            groupPtr -> nextGroup = newGroup;
         }

         return (newGroup);
      }
      break;


      /*
       *  Remove an editor group and free any attached editors.
       */

      case MSG_FREE:
      {
          register xrWindow      * window;
          register xrEditor      * editorPtr;
          register xrEditorGroup * groupPtr;
          INT8 state;


          if (groupInstance == NULL)
          {
             xrErrno = XrINVALIDID;
             return ((xrEditorGroup *) NULL);
          }


          /*
           *  Find the window in the resource manager, extract the
           *  group list pointer, traverse the list, and remove
           *  the group which matches the parameter groupInstance.
           */

          resourceInfo.resourceId = groupInstance -> groupWindowId;
          if (XrResource (MSG_FIND, &resourceInfo) == FALSE)
             return (FALSE);

          window = (xrWindow *) resourceInfo.resourceObject;
          groupPtr = &(window -> groupList);


          /*
           *  Turn the area the group covers to invisible by setting the
           *  groups state to 0.  Save and restore the old state just
           *  in case the group being freed is the base group.
           */

          state = groupInstance -> groupState;
          XrEditorGroup (groupInstance, MSG_SETSTATE, 0);
          groupInstance -> groupState = state;


          /*
           *  Free all of the editors attached to the group.  Ensure
           *  that they don't redraw because the group has already
           *  been made invisible.
           */

          editorPtr = groupInstance -> editorList;
          while (editorPtr != NULL)
          {
             editorPtr -> editorState = 0;
             (*editorPtr -> editorFunct) (editorPtr, MSG_FREE, NULL);
             editorPtr = editorPtr -> nextEditor;
          }


         /*
          *  If groupInstance was the active group then make the base
          *  window group active.
          */

         if (groupInstance == window -> activeGroup)
            window -> activeGroup = &(window -> groupList);


         /*
          *  Find and remove the appropriate group by traversing 
          *  the list.
          */

          if (groupPtr != groupInstance)
          {
             while (groupPtr -> nextGroup != NULL)
             {
                if (groupPtr -> nextGroup == groupInstance)
                {
                   groupPtr -> nextGroup = groupPtr -> nextGroup -> nextGroup;
                   (*xrFree)(groupInstance);
                   break;
                }
                groupPtr = groupPtr -> nextGroup;
             }
         }

         return (groupInstance);
      }
      break;


      /*
       *  Return the pointer to the default group attached to the
       *  window id contained within the parameter data.
       */

      case MSG_GETDEFAULTGROUP:
      {
         xrWindow * window;


         if (data == NULL)
         {
            xrErrno = XrINVALIDPARM;
            return ((xrEditorGroup *) NULL);
         }

         resourceInfo.resourceId = (Window) data;
         if (XrResource (MSG_FIND, &resourceInfo) == FALSE)
            return (FALSE);

         window = (xrWindow *) resourceInfo.resourceObject;

         return (&(window -> groupList));
      }


      /*
       *  Get and return the state of an editor group.
       */

      case MSG_GETSTATE:
         if (groupInstance == NULL)
         {
            xrErrno = XrINVALIDID;
            return ((xrEditorGroup *) NULL);
         }

         if (data == NULL)
         {
            xrErrno = XrINVALIDPARM;
            return ((xrEditorGroup *) NULL);
         }

          *data = (INT8) (groupInstance -> groupState);
          return (groupInstance);
      break;


      /*
       *  Set the state of the requested editor group.
       */

      case MSG_SETSTATE:
      {
         register xrEditor      * editorPtr;
         register xrEditorGroup * groupPtr;
         xrWindow * window;
         INT8       newState;
         RECTANGLE  tempRect;

         newState = (INT8) data;

         if (groupInstance == NULL)
         {
            xrErrno = XrINVALIDID;
            return ((xrEditorGroup *) NULL);
         }

         if (newState != NULL && (newState & (XrVISIBLE|XrSENSITIVE)) == NULL)
         {
            xrErrno = XrINVALIDPARM;
            return ((xrEditorGroup *) NULL);
         }

         resourceInfo.resourceId = groupInstance -> groupWindowId;
         if (XrResource (MSG_FIND, &resourceInfo) == FALSE)
            return (FALSE);

         window = (xrWindow *) resourceInfo.resourceObject;


         if (groupInstance -> groupState != newState)
         {
            if (newState & XrVISIBLE)
            {
               editorPtr = groupInstance -> editorList;

               while (editorPtr != NULL)
               {
                  (*editorPtr -> editorFunct)
                    (editorPtr, MSG_REDRAW, XrREDRAW_ALL);
                  editorPtr = editorPtr -> nextEditor;
               }
            }
            else
            {
               XrEditorGroup (groupInstance, 
                              MSG_CLEARRECT, &groupInstance -> groupRect);

               groupPtr = &(window -> groupList);
               while (groupPtr != NULL)
               {
                  if (groupPtr -> groupState & XrVISIBLE &&
                      groupPtr != groupInstance          &&
                      XrSectRect (&groupInstance -> groupRect, 
                                  &groupPtr -> groupRect, &tempRect))
                     XrEditorGroup (groupPtr, MSG_REDRAW, NULL);

                  groupPtr = groupPtr -> nextGroup;
               }
            }
         }

         groupInstance -> groupState = newState;
         return (groupInstance);
      }
      break;


      /*
       *  Redraw a single VISIBLE group by first checking to ensure
       *  that the group is visible and then looping through the
       *  attached set of editors calling each visible editor to 
       *  redraw its entire instance.
       */

      case MSG_REDRAW:
      {
         register xrEditor * editorPtr;

         if (groupInstance == NULL)
         {
            xrErrno = XrINVALIDID;
            return ((xrEditorGroup *) NULL);
         }

         editorPtr = groupInstance -> editorList;

         if (groupInstance -> groupState & XrVISIBLE)
         {
            while (editorPtr != NULL)
            {
               if (editorPtr -> editorState & XrVISIBLE)
                  (*editorPtr -> editorFunct)
                    (editorPtr, MSG_REDRAW, XrREDRAW_ALL);
               editorPtr = editorPtr -> nextEditor;
            }
         }
         return (groupInstance);
      }


      /*
       *  Make the groupInstance contained within the input parameter
       *  the active group.
       */

      case MSG_ADDTOGROUP:
      {
         xrWindow * window;

         if (groupInstance == NULL)
         {
            xrErrno = XrINVALIDID;
            return ((xrEditorGroup *) NULL);
         }

         resourceInfo.resourceId = groupInstance -> groupWindowId;

         if (XrResource (MSG_FIND, &resourceInfo) == FALSE)
            return (FALSE);

         window = (xrWindow *) resourceInfo.resourceObject;
         window -> activeGroup = groupInstance;

         return (groupInstance);
      }
      break;


      /*
       *  Return the group rectangle contained within the group
       *  pointed at by the parameter groupInstance.
       */

      case MSG_GETGROUPRECT:
         if (groupInstance == NULL)
         {
            xrErrno = XrINVALIDID;
            return ((xrEditorGroup *) NULL);
         }

         if (data == NULL)
         {
            xrErrno = XrINVALIDPARM;
            return ((xrEditorGroup *) NULL);
         }

         XrCopyRect (&groupInstance -> groupRect, (RECTANGLE *) data);
         return (groupInstance);
      break;


      /*
       *  Set the requested editor groups rectangle.  Fail if smaller
       *  than the Union of all of the editors attached to the group.
       */

      case MSG_SETGROUPRECT:
      {
         register xrEditor * editorPtr;
         RECTANGLE * newRect;
         RECTANGLE   editorRect;

         if (groupInstance == NULL)
         {
            xrErrno = XrINVALIDID;
            return ((xrEditorGroup *) NULL);
         }

         newRect = (RECTANGLE *) data;
         editorPtr = groupInstance -> editorList;
   
         if (editorPtr != NULL)
            XrCopyRect (&editorPtr -> editorRect, &editorRect);
         else
         {
            if (data == NULL)
               XrSetRect (&groupInstance -> groupRect, 0, 0, 0, 0);
            else
               XrCopyRect (&newRect, &groupInstance -> groupRect);
            return (groupInstance);
         }


         /*
          *  Calculate the smallest rectangle which will contain the
          *  the entire editor set.
          */

         while (editorPtr != NULL)
         {
            XrUnionRect (&editorPtr->editorRect, &editorRect, &editorRect);
            editorPtr = editorPtr -> nextEditor;
         }


         /*
          *  If the input rectangle parameter is NULL, force the group
          *  rectangle to the above calculated rectangle.
          */

         if (newRect == NULL)
         {
            XrCopyRect (&editorRect, &groupInstance -> groupRect);
            return (groupInstance);
         }


         /*
          *  If the input rectangle is not NULL, make sure that it
          *  encompases the minimal rectangle.
          */

         if (editorRect.x < newRect -> x || editorRect.y < newRect -> y    ||
             editorRect.x + editorRect.width > newRect->x + newRect->width ||
             editorRect.y + editorRect.height > newRect->y + newRect->height)
         {
            xrErrno = XrINVALIDPARM;
            return (FALSE);
         }
            
         XrCopyRect (newRect, &groupInstance -> groupRect);

      }
      break;


      /*
       *  Recalculate the group rectangle for the group which
       *  contains the editor instance in data.
       */

      case MSG_ADJUSTGROUPRECT:
      {
         register xrEditor      * editorPtr;
         register xrEditorGroup * groupPtr;
         xrWindow * window;


         if (data == NULL)
         {
            xrErrno = XrINVALIDPARM;
            return ((Window) FALSE);
         }

         editorPtr = (xrEditor *) data;

         resourceInfo.resourceId = editorPtr -> editorWindowId;
         if (XrResource (MSG_FIND, &resourceInfo) == FALSE)
            return ((Window) FALSE);

         window = (xrWindow *) resourceInfo.resourceObject;
         groupPtr = &(window -> groupList);


         /*
          *  Loop through the group list, first seeing if the editor
          *  resides in the group and if so, loop through the editor
          *  list to see if the editor is in the list.  If found, 
          *  recalculate the group rectangle and return.
          */

         while (groupPtr != NULL)
         {
            editorPtr = groupPtr -> editorList;

            while (editorPtr != NULL)
            {
               if (editorPtr == (xrEditor *) data)
               {
                  XrCopyRect (&editorPtr->editorRect, &groupPtr->groupRect);
                  editorPtr = groupPtr -> editorList;

                  while (editorPtr != NULL)
                  {
                     XrUnionRect(&editorPtr->editorRect, 
                                 &groupPtr->groupRect, &groupPtr->groupRect);
                                     
                     editorPtr = editorPtr -> nextEditor;
                  }
                  return (groupPtr);
               }
               editorPtr = editorPtr -> nextEditor;
            }
            groupPtr = groupPtr -> nextGroup;
         }
         return ((xrEditorGroup *) NULL);
      }
      break;


      /*
       *  Clear the area defined by the input editor group rect.
       */

      case MSG_CLEARRECT:
      {
         xrWindowData wData;

         if (groupInstance == NULL)
         {
            xrErrno = XrINVALIDID;
            return ((xrEditorGroup *) NULL);
         }

         if (XrInput(groupInstance -> groupWindowId,
                     MSG_GETWINDOWDATA, &wData)==FALSE)
            return (FALSE);

         XTileSet (groupInstance-> groupWindowId, 
                   groupInstance->groupRect.x, groupInstance->groupRect.y,
                   groupInstance->groupRect.width,
                   groupInstance->groupRect.height,
                   wData.backTile);

         return (groupInstance);
      }
      break;


      default:
         xrErrno = XrINVALIDMSG;
         return (FALSE);
      break;
   }
}
