/*
 *  WICONIFY    A utility that allows you to iconify any Intuition window
 *              on any screen, and to open WB windows on any screen.
 *
 *  wGadget.c   Handles the icon gadgets for the backdrop windows.
 *
 *  Copyright 1990 by Davide P. Cervone, all rights reserved.
 *  You may use this code, provided this copyright notice is kept intact.
 */

#define INTUITION_PREFERENCES_H             /* don't need 'em */
#include <intuition/intuitionbase.h>
#include "wHandler.h"

#define SELECTEDICON ((WICONREF *)(theIcon->Screen->Selected->Gadget.UserData))
#define BOOLINFO     ((struct BoolInfo *)theGadget->SpecialInfo)
#define GADGICON     ((WICONREF *)(theGadget->UserData))


/*
 *  SetupGadget()
 *
 *  If the icon is a screen icon use the screen defaults
 *  Otherwise use the window icon defaults
 *  Get the icon's image
 *  Set the gadget variables appropriately (UserData points to the icon)
 *  Set the render and select imagery
 *  Set the gadget's text (use the window title for the name if none supplied)
 *  Set the gadget's mask
 *  Initialize the gadget's position on the screen
 */

void SetupGadget(theIcon)
WICONREF *theIcon;
{
   struct Image *DefImage,*DefSelect,*DefMask;
   struct Image *theImage;
   struct Gadget *theGadget = &(theIcon->Gadget->Gadget);
   struct IntuiText *theText = &(theIcon->Gadget->IText);
   struct BoolInfo *theInfo = &(theIcon->Gadget->Mask);

   if (theIcon->Icon.Flags & WI_SCREENICON)
   {
      DefImage  = DefaultScreenImage;
      DefSelect = DefaultScreenSelect;
      DefMask   = DefaultScreenMask;
   } else {
      DefImage  = DefaultImage;
      DefSelect = DefaultSelect;
      DefMask   = DefaultMask;
   }
   theImage = (theIcon->Icon.Image)? theIcon->Icon.Image: DefImage;

   theGadget->UserData = (APTR) theIcon;
   theGadget->LeftEdge = theIcon->Icon.x;
   theGadget->TopEdge  = theIcon->Icon.y;
   theGadget->NextGadget = NULL;
   theGadget->Width  = theImage->Width;
   theGadget->Height = theImage->Height;
   theGadget->Activation = GADGIMMEDIATE;
   theGadget->GadgetType = BOOLGADGET;
   theGadget->MutualExclude = 0L;

   theGadget->Flags = GADGIMAGE | GADGHNONE;
   theGadget->GadgetRender = (APTR) theImage;
   if (theIcon->Icon.Select)
      theGadget->SelectRender = (APTR)theIcon->Icon.Select;
   else if (theImage == DefImage && DefSelect)
      theGadget->SelectRender = (APTR)DefSelect;

   theGadget->GadgetText = theText;
      theText->FrontPen  = TEXTPEN;
      theText->BackPen   = BACKGROUND;
      theText->DrawMode  = JAM2;
      theText->TopEdge   = (theImage->Height + 2);
      theText->ITextFont = theIcon->Screen->Screen->Font;
      theText->NextText  = NULL;
      if (theIcon->Icon.Name)     theText->IText = theIcon->Icon.Name;
      else if (theIcon->Window)   theText->IText = theIcon->Window->Title;
      if (theText->IText == NULL) theText->IText = "[No Title]";
      theText->LeftEdge  = (theImage->Width - IntuiTextLength(theText)) / 2;

   theInfo->Flags = BOOLMASK;
   if (theIcon->Icon.Mask) theInfo->Mask = (UWORD *)theIcon->Icon.Mask;
   else if (theImage == DefImage && theImage != NULL && DefMask)
      theInfo->Mask = DefMask->ImageData;
   if (theGadget->SelectRender == NULL && theInfo->Mask)
      theGadget->Activation |= BOOLEXTEND;
   theGadget->SpecialInfo = (APTR) theInfo;

   InitPosition(theGadget);
}


/*
 *  UnLinkGadget()
 *
 *  If the gadget is selected unlink it from the selection list
 */

void UnLinkGadget(theGadget)
struct wGadget *theGadget;
{
   Forbid();
   if (GADGETICON->Icon.Flags & WI_SELECTED)
   {
      if (theGadget->NextSelect)
         theGadget->NextSelect->PrevSelect = theGadget->PrevSelect;
      if (theGadget->PrevSelect)
         theGadget->PrevSelect->NextSelect = theGadget->NextSelect;
      else GADGETICON->Screen->Selected = theGadget->NextSelect;
   }
   Permit();
}


/*
 *  MakeSelected()
 *
 *  Clear the highlight flags
 *  If the gadget has a select image
 *    If it has a mask, mark the gadget as having one
 *    Mark the gadget as having an image
 *  Otherwise
 *    Mark it as a complementing gadget
 */

void MakeSelected(theGadget)
struct Gadget *theGadget;
{
   theGadget->Flags &= ~GADGHIGHBITS;
   if (theGadget->SelectRender)
   {
      if (BOOLINFO->Mask) theGadget->Activation |= BOOLEXTEND;
      theGadget->Flags |= SELECTED | GADGHIMAGE;
   } else {
      theGadget->Flags |= SELECTED | GADGHCOMP;
   }
}


/*
 *  MakeUnSelected()
 *
 *  Clear the selection and highlight flags
 *  Set the highlighting to none
 *  Remove the bool mask, if any
 */

static void MakeUnSelected(theGadget)
struct Gadget *theGadget;
{
   theGadget->Flags &= ~(SELECTED | GADGHIGHBITS);
   theGadget->Flags |=  GADGHNONE;
   if (theGadget->SelectRender && BOOLINFO->Mask)
      theGadget->Activation &= ~BOOLEXTEND;
}


/*
 *  DeselectIcon()
 *
 *  If dragging is currently in progress, cancel it
 *  If the icon is selected
 *    If the icon has a gadget and the screen had a wIconify window
 *      Unlink the gadget from the selection list
 *      Remove it from the screen
 *      Mark it as unselected
 *      Put it back on the screen
 *      Mark the icon as unselected
 *      Refresh the icons on the given screen
 *      Report the unselect event, if necessary
 *    If no more icons are selected, update the menus (turn off OpenAll, etc)
 */

void DeselectIcon(theIcon)
WICONREF *theIcon;
{
   LONG GadgetPos;
   struct Gadget *theGadget = &theIcon->Gadget->Gadget;

   if (theIcon->Screen->Flags & (WI_DRAGGING| WI_STARTDRAG))
       CancelDragging(theIcon->Screen);
   if (theIcon->Icon.Flags & WI_SELECTED)
   {
      if (theGadget && theIcon->Screen->BackDrop)
      {
         Forbid();
         UnLinkGadget(theGadget);
         GadgetPos = RemoveGadget(theIcon->Screen->BackDrop,theGadget);
         MakeUnSelected(theGadget);
         AddGadget(theIcon->Screen->BackDrop,theGadget,GadgetPos);
         theIcon->Icon.Flags &= ~WI_SELECTED;
         Permit();
         RefreshIcons(theIcon->Screen,FALSE);
         if (theIcon->Icon.Report & WI_REPORTUNSELECT)
            ReportEvent(WI_REPORTUNSELECT,theIcon);
      }
      if (theIcon->Screen->Selected == NULL) UpdateActiveMenu();
   }
}


/*
 *  DeselectAll()
 *
 *  If the screen has a wIconify window
 *    If icons are being dragged, cancel the dragging
 *    While there are more selected icons
 *      Remove the selected gadget from the screen
 *      Mark it as unselected
 *      Put it back on screen
 *      Clear its links to other selected gadgets and mark it as unselected
 *      Report the unselect event, if necessary
 *    Refresh the screen's icons
 *    Update the menus (turn off OpenAll, etc)
 */

void DeselectAll(theScreen)
WSCREEN *theScreen;
{
   LONG GadgetPos;
   struct wGadget *theGadget;

   if (theScreen && theScreen->BackDrop)
   {
      Forbid();
      if (theScreen->Flags & (WI_DRAGGING| WI_STARTDRAG))
          CancelDragging(theScreen);
      while ((theGadget = theScreen->Selected) != NULL)
      {
         GadgetPos = RemoveGadget(theScreen->BackDrop,theGadget);
         MakeUnSelected(&theGadget->Gadget);
         AddGadget(theScreen->BackDrop,theGadget,GadgetPos);
         theScreen->Selected = theGadget->NextSelect;
         theGadget->NextSelect = NULL;
         theGadget->PrevSelect = NULL;
         GADGETICON->Icon.Flags &= ~WI_SELECTED;
         if (GADGETICON->Icon.Report & WI_REPORTUNSELECT)
            ReportEvent(WI_REPORTUNSELECT,GADGETICON);
      }
      Permit();
      RefreshIcons(theScreen,FALSE);
      UpdateActiveMenu();
   }
}


/*
 *  SelectIcon()
 *
 *  If an icon was given
 *    Get its screen
 *    If no icons are being dragged on the screen
 *      Get the icons gadget
 *      If the SHIFT key was pressed
 *        If the icon is already selected
 *          Deselect it and clear the gadget
 *        Otherwise if there were any select icons
 *          If this icon or the selected one does not allow multiple selection
 *            Clear the gadget
 *      Otherwise (SHIFT not pressed)
 *        If the icon is already selected, unlink it (we'll relink it later)
 *        Otherwise deselect all selected icons
 *      If we still have a gadget and a window to work with,
 *        Remove the gadget from the screen
 *        Mark it as selected and replace it on the screen
 *        Mark the icon as selected
 *        Link the gadget into the selected-gadget list
 *        Refresh the screen's icons
 *        Update the menus (activate OpenAll, etc)
 *        Report the selection event, if necessary
 */

void SelectIcon(theIcon,Shifted)
WICONREF *theIcon;
int Shifted;
{
   WSCREEN *theScreen;
   struct wGadget *theGadget;
   int AlreadySelected = FALSE;

   if (theIcon)
   {
      Forbid();
      theScreen = theIcon->Screen;
      if ((theScreen->Flags & WI_DRAGGING) == 0)
      {
         theGadget = theIcon->Gadget;
         if (Shifted)
         {
            if (theIcon->Icon.Flags & WI_SELECTED)
            {
               DeselectIcon(theIcon);
               theGadget = NULL;
            } else if (theIcon->Screen->Selected) {
               if ((theIcon->Icon.Flags & WI_NOMULTISELECT) ||
                   (SELECTEDICON->Icon.Flags & WI_NOMULTISELECT))
                      theGadget = NULL;
            }
         } else {
            AlreadySelected = (theIcon->Icon.Flags & WI_SELECTED);
            if (AlreadySelected)
               UnLinkGadget(theGadget); else DeselectAll(theScreen);
         }
         if (theGadget && theScreen->BackDrop)
         {
            RemoveGadget(theScreen->BackDrop,theGadget);
            MakeSelected(&theGadget->Gadget);
            AddGadget(theScreen->BackDrop,theGadget,0);
            GADGETICON->Icon.Flags |= WI_SELECTED;

            theGadget->PrevSelect = NULL;
            theGadget->NextSelect = theScreen->Selected;
            if (theScreen->Selected)
                theScreen->Selected->PrevSelect = theGadget;
            theScreen->Selected = theGadget;
            RefreshIcons(theIcon->Screen,FALSE);
            UpdateActiveMenu();
            if ((GADGETICON->Icon.Report & WI_REPORTSELECT) &&
                 AlreadySelected == FALSE)
                    ReportEvent(WI_REPORTSELECT,GADGETICON);
         }
      }
      Permit();
   }
}


/*
 *  MoveGadget()
 *
 *  If we have a gadget and a window to work with
 *    Check that the position is within the window bounds
 *    If the gadget has moved,
 *       Remove the gadget from the screen
 *       Set the new position
 *       Put the icon back on the screen
 *       Refresh the screen's icons
 *       Report the move event, if necessary
 */

void MoveGadget(theGadget,dx,dy,theScreen)
struct Gadget *theGadget;
WORD dx,dy;
WSCREEN *theScreen;
{
   LONG GadgetPos;
   WORD NewX = theGadget->LeftEdge + dx;
   WORD NewY = theGadget->TopEdge  + dy;

   if (theGadget && theScreen->BackDrop)
   {
      if (NewX < 0) NewX = 0;
      if (NewX > theScreen->BackDrop->Width - theGadget->Width)
         NewX = theScreen->BackDrop->Width - theGadget->Width;
      if (NewY < 0) NewY = 0;
      if (NewY > theScreen->BackDrop->Height - theGadget->Height - 10)
         NewY = theScreen->BackDrop->Height - theGadget->Height - 10;

      if (theGadget->LeftEdge != NewX || theGadget->TopEdge != NewY)
      {
         Forbid();
         GadgetPos = RemoveGadget(theScreen->BackDrop,theGadget);
         theGadget->LeftEdge = NewX; GADGICON->Icon.x = NewX;
         theGadget->TopEdge  = NewY; GADGICON->Icon.y = NewY;
         AddGadget(theScreen->BackDrop,theGadget,GadgetPos);
         Permit();
         RefreshIcons(theScreen,TRUE);
         if (GADGICON->Icon.Report & WI_REPORTMOVED)
            ReportEvent(WI_REPORTMOVED,theGadget->UserData);
      }
   }
}
