/*
 *
 *            Copywrite (C) 1986 by Davide P. Cervone
 *
 *    Permission is granted for any individual or institution to use,
 *    copy, or redistribute this software, provided this copywrite
 *    notice is retained.
 *
 */

#include "exec/types.h"
#include "exec/devices.h"
#include "libraries/dos.h"
#include "intuition/intuition.h"
#include "graphics/gfxbase.h"
#include "graphics/regions.h"
#include "graphics/copper.h"
#include "graphics/gels.h"
#include "devices/keymap.h"
#include "hardware/blit.h"

#include "mxgadget.h"

#define  USERDATA(x)     ((struct MxGadgetMasks *) x->UserData)

SelectGadget(theGadget,theWindow,theRequester)
struct Gadget *theGadget;
struct Window *theWindow;
struct Requester *theRequester;
{
   if (isMxGadget(theGadget))
   {
      struct MxGadgetMasks *theData = USERDATA(theWindow);
      LONG Exclude = theGadget->MutualExclude;
                             /* Groups this gadget excludes */
      LONG Disable = 0;      /* Groups this gadget disables */
      LONG Enable  = 0;      /* Groups this gadget enables  */

      if (theData != NULL && Exclude != 0)
      {
         if (MxFlagSet(theGadget,MXENABLEGADG))
            Enable  = Exclude & theData->EnableMask;
         if (MxFlagSet(theGadget,MXDISABLEGADG))
            Disable = Exclude & theData->DisableMask;
         Exclude &= ~(theData->EnableMask | theData->DisableMask);
      }

/*
 *  If the gadget already is selected, just mutual exclude other gadgets;
 *  Otherwise, if a TOGGLE gadget has just been unselected, then undo
 *    any disabling or enabling caused when it was selected;
 *  Otherwise, we are selecting a previously unselected, non-TOOGLE
 *    gadget, so select it, draw its imagery, and mutual exclude others.
 */

      if (isSelected(theGadget))
      {
         ExcludeMxGadgets(theGadget,theWindow,theRequester,
                          Exclude,Enable,Disable);
      } else {
         if (theGadget->Activation & TOGGLESELECT)
         {
            ExcludeMxGadgets(theGadget,theWindow,theRequester,
                             0,Disable,Enable);
         } else {
            SetGadgFlag(theGadget,SELECTED);
            DrawGadget(theGadget,theWindow,theRequester);
            ExcludeMxGadgets(theGadget,theWindow,theRequester,
                             Exclude,Enable,Disable);
         }
      }
   }
}

UnSelectGadget(theGadget,theWindow,theRequester)
struct Gadget *theGadget;
struct Window *theWindow;
struct Requester *theRequester;
{

/*
 *  If the gadget is selected, then clear the selection flags; then, if it is
 *  not disbaled, redraw it with unselected imagery.
 */

   if (isAnySelected(theGadget))
   {
      ClearGadgFlag(theGadget,SELECTED);
      ClearMxFlag(theGadget,MXSELECT);
      if (isNotDisabled(theGadget))
         DrawGadget(theGadget,theWindow,theRequester);
   }
}

MxOffGadget(theGadget,theWindow,theRequester)
struct Gadget *theGadget;
struct Window *theWindow;
struct Requester *theRequester;
{

/*
 *  If the gadget currently is selected, save the status via the MXSELECT
 *  flag so that when we enable the gadget again, we can re-select it.
 *  Finally, "ghost" the gadget by turning it off.
 */

   if (isSelected(theGadget) && isMxGadget(theGadget))
   {
      ClearGadgFlag(theGadget,SELECTED);
      SetMxFlag(theGadget,MXSELECT);
      DrawGadget(theGadget,theWindow,theRequester);
   }
   IsolateGadget(theGadget,theWindow,theRequester);
   OffGadget(theGadget,theWindow,theRequester);
   RestoreGadget(theGadget,theWindow,theRequester);
}

MxOnGadget(theGadget,theWindow,theRequester)
struct Gadget *theGadget;
struct Window *theWindow;
struct Requester *theRequester;
{

/*
 *  Turn the gadget on, and if it was previously marked as SELECTED
 *  (i.e., we saved the fact that it was selected via the MXSELECT flag),
 *  then select the gadget and draw the selected imagery.
 */

   IsolateGadget(theGadget,theWindow,theRequester);
   OnGadget(theGadget,theWindow,theRequester);
   RestoreGadget(theGadget,theWindow,theRequester);
   if (isMxGadget(theGadget) && isMxSelected(theGadget))
   {
      SetGadgFlag(theGadget,SELECTED);
      ClearMxFlag(theGadget,MXSELECT);
      DrawGadget(theGadget,theWindow,theRequester);
   }
}

struct Window *
MxOpenWindow(NewWindow)
struct NewWindow *NewWindow;
{
   struct Window *theWindow, *OpenWindow();
   struct MxGadgetMasks *theData, *AllocMem();

/*
 *  Open the window and allocate memory for the Enable/Diable masks for this
 *  window.  Since we are using the UserData pointer, supply another one,
 *  so users have something to use.
 */

   if ((theWindow = OpenWindow(NewWindow)) != NULL)
   {
      theData = AllocMem(sizeof(struct MxGadgetMasks),0);
      if (theData != NULL)
      {
         theData->DisableMask = 0;
         theData->EnableMask  = 0;
         theData->UserData = NULL;
         theWindow->UserData = (BYTE *) theData;
      }
   }
   return(theWindow);
}

MxCloseWindow(theWindow)
struct Window *theWindow;
{

/*
 *  Free the mask data memory, and then close the window.
 */

   if (theWindow != NULL)
   {
      if (theWindow->UserData != NULL)
         FreeMem(theWindow->UserData,sizeof(struct MxGadgetMasks));
      CloseWindow(theWindow);
   }
}

SetMxGadgetMasks(theWindow,EnableMask,DisableMask)
struct Window *theWindow;
LONG EnableMask, DisableMask;
{
   struct MxGadgetMasks *theData = USERDATA(theWindow);

   if (theData != NULL)
   {
      theData->DisableMask = DisableMask;
      theData->EnableMask  = EnableMask;
   }
}

LONG GetMxGadgetMasks(theWindow,EnableMask,DisableMask)
struct Window *theWindow;
LONG *EnableMask, *DisableMask;
{
   struct MxGadgetMasks *theData = USERDATA(theWindow);

   if (theData != NULL)
   {
      *DisableMask = theData->DisableMask;
      *EnableMask  = theData->EnableMask;
   } else {
      *DisableMask = 0;
      *EnableMask  = 0;
   }
}

DrawGadget(theGadget,theWindow,theRequester)
struct Gadget *theGadget;
struct Window *theWindow;
struct Requester *theRequester;
{
   LONG NotSelected = GadgFlagNotSet(theGadget,SELECTED);

/*
 *  If the gadget is not an IMAGE gadget (i.e., it uses complementation
 *  to show that it is selected), then temporarily select it, so that we
 *  are sure that complementation takes place.  Refresh the gadget:  for
 *  IMAGE gadgets, the proper image is shown (selected or not), while for
 *  non-IMAGE gadgets, this forces complementation (so that previously
 *  selected gadgets become unselected, and vice versa).  Finally, if the
 *  gadget was not selected, clear the selected flag, and refresh non-IMAGE
 *  gadgets again, to be sure that the border and text are shown in the
 *  proper colors.  Note that gadget borders and text should be rendered in
 *  JAM1 mode, not COMPLEMENT mode.  If you need to use COMPLEMENT mode,
 *  remove the second RefreshGadgets call.
 */

   if (GadgFlagNotSet(theGadget,GADGHIMAGE))
      SetGadgFlag(theGadget,SELECTED);
   IsolateGadget(theGadget,theWindow,theRequester);
   RefreshGadgets(theGadget,theWindow,theRequester);
   if (NotSelected)
   {
      ClearGadgFlag(theGadget,SELECTED);
      if (GadgFlagNotSet(theGadget,GADGHIMAGE))
         RefreshGadgets(theGadget,theWindow,theRequester);
   }
   RestoreGadget(theGadget,theWindow,theRequester);
}

static struct Gadget *FirstGadget = NULL;
static struct Gadget *NextGadget  = NULL;

/*
 *  When gadgets are mutually excluded, they are refreshed to show their
 *  imagery properly selected or unselected.  Unfortunately, refreshing
 *  the complete gadget list toggles the displayed status for gadgets
 *  that use GADGHCOMP or GADGHBOX selection.  To avoid this, mutually
 *  excluded gadgets are updated individually.  First they are isolated
 *  by making the gadget the only one in the window or requester gadget
 *  list (the original pointers are stored in the two variable listed
 *  above), then refreshed.  Finally the gadget list is restored to its
 *  original state using the saved pointers.  This avoids the unwanted
 *  toggling of COMP and BOX gadgets, and removes the annoying flicker
 *  produced when IMAGE gadgets are refreshed unnecessarily.
 *
 *  This is a little sneaky, but it should be relatively safe, since
 *  it happens very quickly, and the integrity of the rest of the list is
 *  not destroyed during the procedure.  An alternate approach would be to
 *  mark all the COMP and BOX gadgets in the list as being not SELECTED,
 *  then do the refresh; finally, restore the SELECT flag to the proper
 *  gadgets again.  If you don't use COMP or BOX selection, then you can
 *  remove the bodies of these two routines (just make them empty brackets).
 */

IsolateGadget(theGadget,theWindow,theRequester)
struct Gadget *theGadget;
struct Window *theWindow;
struct Requester *theRequester;
{
   NextGadget = theGadget->NextGadget;
   if (isReqGadg(theGadget))
   {
      FirstGadget = theRequester->ReqGadget;
      theRequester->ReqGadget = theGadget;
   } else {
      FirstGadget = theWindow->FirstGadget;
      theWindow->FirstGadget = theGadget;
   }
   theGadget->NextGadget = NULL;
}

RestoreGadget(theGadget,theWindow,theRequester)
struct Gadget *theGadget;
struct Window *theWindow;
struct Requester *theRequester;
{
   theGadget->NextGadget = NextGadget;
   if (isReqGadg(theGadget))
      theRequester->ReqGadget = FirstGadget;
   else
      theWindow->FirstGadget = FirstGadget;
}

ExcludeMxGadgets(theGadget,theWindow,theRequester,Exclude,Enable,Disable)
struct Gadget *theGadget;
struct Window *theWindow;
struct Requester *theRequester;
LONG Exclude, Enable, Disable;
{
   struct Gadget *CheckGadget = (isReqGadg(theGadget)) ?
                                   theRequester->ReqGadget :
                                   theWindow->FirstGadget;
   LONG MutualExclude = Exclude | Enable | Disable;
   LONG CheckExclude;

/*
 *  Check each gadget in the window or requester list:
 *  If it is excluded (or enabled or diabled) by the selected gadget,
 *      and it is not the selected gadget then:
 *    if it is selected and excluded then unselect it;
 *    if it is an ON-OFF gadget (i.e., it can be enabled or disabled), then:
 *      if it is disabled by the selected gadget and
 *         it's not already disabled, then disable it;
 *      if it is enabled by the selected gadget, and
 *         it's currently disabled, then enable it.
 */

   if (MutualExclude)
   {
      while (CheckGadget != NULL)
      {
         CheckExclude = CheckGadget->MutualExclude;
         if ((MutualExclude & CheckExclude) &&
            (CheckGadget != theGadget))
         {
            if (isAnySelected(CheckGadget) &&
               (CheckExclude & Exclude))
                  UnSelectGadget(CheckGadget,theWindow,theRequester);

            if (MxFlagSet(CheckGadget,MXGADGONOFF))
            {
               if ((CheckExclude & Disable) &&
                  (isNotDisabled(CheckGadget)))
                     MxOffGadget(CheckGadget,theWindow,theRequester);

               if ((CheckExclude & Enable) &&
                  (isDisabled(CheckGadget)))
                     MxOnGadget(CheckGadget,theWindow,theRequester);
            }
         }
         CheckGadget = CheckGadget->NextGadget;
      }
   }
}
