/*
 *  WICONIFY    A utility that allows you to iconify any Intuition window
 *              on any screen, and to open WB windows on any screen.
 *
 *  wScreen.c   Handles menu and screen functions.
 *
 *  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 <libraries/dosextens.h>
#include "wHandler.h"
#include "wMenu.h"


#define SCREENMEM(p)    RASSIZE(p.Width,p.Height*p.Depth)

#define RED(i)          (((Color[i])>>8)&0x0F)
#define GREEN(i)        (((Color[i])>>4)&0x0F)
#define BLUE(i)         ( (Color[i])    &0x0F)

/*
 *  The newScreen structure for wIconify screens
 */
static struct NewScreen IconScreen =
   {0,0, 640,200,2, 0,1, HIRES,CUSTOMSCREEN, NULL,"wIconify Screen",NULL,NULL};

static struct Window *PreviousWindow;

static char *LockName = "UnLock";
extern struct wMenuItem OpenWindowMenu[],
                        NewScreenMenu[],
                        IconMenu[],
                        ScreenMenu[];

/*
 *  Macros for modifying the menus during menu update
 */
#define ENABLESCREEN(n)     ScreenMenu[n].Item.Flags |=  ITEMENABLED
#define DISABLESCREEN(n)    ScreenMenu[n].Item.Flags &= ~ITEMENABLED
#define ENABLEICON(n)       IconMenu[n].Item.Flags |=  ITEMENABLED
#define DISABLEICON(n)      IconMenu[n].Item.Flags &= ~ITEMENABLED
#define CHECKITEM(n)        NewScreenMenu[n].Item.Flags |=  CHECKED
#define UNCHECKITEM(n)      NewScreenMenu[n].Item.Flags &= ~CHECKED
#define ISCHECKED(n)        (NewScreenMenu[n].Item.Flags & CHECKED)
#define SETLOCK(n)\
   (((struct IntuiText *)(IconMenu[n].Item.ItemFill))->IText = &LockName[2])
#define SETUNLOCK(n)\
   (((struct IntuiText *)(IconMenu[n].Item.ItemFill))->IText = LockName)


/*
 *  SetScreenMenu()
 *
 *  Check the correct HIRES/LORES menu
 *  Check or uncheck the INTERLACE and HAM menus
 */

void SetScreenMenu(Modes)
UWORD Modes;
{
   if (Modes & HIRES)
   {
      CHECKITEM(NS_HIRES);
      UNCHECKITEM(NS_LORES);
   } else {
      CHECKITEM(NS_LORES);
      UNCHECKITEM(NS_HIRES);
   }
   if (Modes & LACE) CHECKITEM(NS_INTERLACE); else UNCHECKITEM(NS_INTERLACE);
   if (Modes & HAM)  CHECKITEM(NS_HAM);       else UNCHECKITEM(NS_HAM);
}


/*
 *  SetActiveMenu()
 *
 *  Get the screen from the backdrop window
 *  If the screen is the RealWB, don't allow it to be iconified
 *  If the screen is the current WB or it is in the process of closing
 *    or real WB is the current WB and so is the screen we are using
 *      don't allow the MAKEWB menu
 *  Don't allow the CLOSESCREEN item, unless...
 *  If the screen is a wIconify screen and it has no windows
 *    And the only icons on the screen are AUTOREMOVE icons
 *      Then allow CLOSESCREEN
 *  If the active backdrop window has changed, update the NEWSCREEN menus
 *  If there are icons on the screen
 *    Enable CLEANUP, ORGANIZE and OPENALL
 *  Otherwise disable them
 *  Disable the CLOSE item, unless...
 *  If there are selected icons
 *    Enable OPEN and UNLOCK and disable CLEANUP, unless..
 *    While ther eare more selected gadgets
 *      If the gadget is allowed to be close, enable CLOSE
 *      If the gadget is not locked
 *        Set the UNLOCK menu to LOCK
 *        If the icon can be organized, enable CLEANUP
 *      Go on to the next selected icon
 *  Otherwise (no selected icons) disable OPEN and LOCK
 *  If the screen can hold the tallest menu, set the menu strip
 */

static void SetActiveMenu(theWindow)
struct Window *theWindow;
{
   WSCREEN *theScreen;
   WICONREF *theIcon;
   struct wGadget *theGadget;

   Forbid();
   theScreen = (WSCREEN *)theWindow->UserData;
   if (theScreen == RealWB) DISABLESCREEN(SM_ICONIFY);
      else ENABLESCREEN(SM_ICONIFY);
   if (theScreen == WBScreen || (theScreen->Flags & WI_CLOSING) ||
      (WBScreen == NULL && theScreen == RealWB))
          DISABLESCREEN(SM_MAKEWB); else ENABLESCREEN(SM_MAKEWB);
   DISABLESCREEN(SM_CLOSESCREEN);
   if ((theScreen->Flags & WI_WSCREEN) && theScreen->Window == NULL)
   {
      theIcon = theScreen->IconRef;
      while (theIcon && ((theIcon->Icon.Flags & WI_AUTOREMOVE)))
         theIcon = theIcon->Next;
      if (theIcon == NULL) ENABLESCREEN(SM_CLOSESCREEN);
   }
   if (ActiveWindow != PreviousWindow)
      SetScreenMenu(theScreen->Screen->ViewPort.Modes);

   if (theScreen->BackDrop->FirstGadget)
   {
      ENABLEICON(IM_CLEANUP);
      ENABLEICON(IM_ORGANIZE);
      ENABLEICON(IM_OPENALL);
   } else {
      DISABLEICON(IM_CLEANUP);
      DISABLEICON(IM_ORGANIZE);
      DISABLEICON(IM_OPENALL);
   }
   DISABLEICON(IM_CLOSE);
   if (theScreen->Selected)
   {
      ENABLEICON(IM_OPEN);
      ENABLEICON(IM_LOCK); SETUNLOCK(IM_LOCK);
      DISABLEICON(IM_CLEANUP);
      theGadget = theScreen->Selected;
      while (theGadget)
      {
         if ((GADGETICON->Icon.Flags & WI_NOCLOSE) == FALSE)
            ENABLEICON(IM_CLOSE);
         if ((GADGETICON->Icon.Flags & WI_LOCKED) == FALSE)
         {
            SETLOCK(IM_LOCK);
            if ((GADGETICON->Icon.Flags & WI_NOORGANIZE) == FALSE)
               ENABLEICON(IM_CLEANUP);
         }
         theGadget = theGadget->NextSelect;
      }
   } else {
      DISABLEICON(IM_OPEN);
      DISABLEICON(IM_LOCK); SETLOCK(IM_LOCK);
   }
   Permit();
   if (theScreen->Screen->Height >= TOTAL_MENUH)
      SetMenuStrip(ActiveWindow,wMenu);
}


/*
 *  SetWindowMenu()
 *
 *  If a backdrop window is active, remove its menu strip
 *  If the OpenOn type has changed
 *    Check and uncheck the correct items in the menu
 *  If SizeToFit has changed
 *    Check or uncheck it
 *  If a backdrop window was active, add the menu strip again
 */

void SetWindowMenu(ScreenType,SizeToFit)
int ScreenType,SizeToFit;
{
   short i;

   if (ActiveWindow) ClearMenuStrip(ActiveWindow);
   Forbid();
   if (ScreenType != NOCHANGE)
   {
      for (i=OW_ACTIVESCRN; i<=OW_REALWB; i++)
      {
         if (i == ScreenType)
           OpenWindowMenu[i].Item.Flags |=  CHECKED;
          else
           OpenWindowMenu[i].Item.Flags &= ~CHECKED;
      }
   }
   if (SizeToFit != NOCHANGE)
   {
      if (SizeToFit)
         OpenWindowMenu[OW_AUTORESIZE].Item.Flags |=  CHECKED;
        else
         OpenWindowMenu[OW_AUTORESIZE].Item.Flags &= ~CHECKED;
   }
   Permit();
   if (ActiveWindow) SetActiveMenu(ActiveWindow);
}


/*
 *  SetActiveWindow()
 *
 *  If the window is not already the active one
 *    If there is an old active one
 *      Get the window's screen
 *      If it has icons that are being dragged, cancel the dragging
 *      Remove the menu strip
 *      Report that the icon window is becoming inactive to anyone who cares
 *    Set the new active window
 *    If there is a new window
 *      Clear any error message from the title
 *      Setup the menu strip and add it
 *      Record the fact that this was the last window active
 *      Report the activation to anyone who wants to know
 */

void SetActiveWindow(theWindow)
struct Window *theWindow;
{
   WSCREEN *theScreen;

   if (theWindow != ActiveWindow)
   {
      if (ActiveWindow)
      {
         theScreen = (WSCREEN *)ActiveWindow->UserData;
         if (theScreen->Flags & (WI_STARTDRAG| WI_DRAGGING))
             CancelDragging(theScreen);
         ClearMenuStrip(ActiveWindow);
         ReportMulti(WI_REPORTINACTIVE,ActiveWindow,theScreen);
      }
      ActiveWindow = theWindow;
      if (ActiveWindow)
      {
         if (ActiveWindow->ScreenTitle != wIconifyTitle)
            SetWindowTitles(ActiveWindow,-ONE,wIconifyTitle);
         SetActiveMenu(ActiveWindow);
         PreviousWindow = ActiveWindow;
         ReportMulti(WI_REPORTACTIVATE,ActiveWindow,ActiveWindow->UserData);
      }
   }
}


/*
 *  UpdateActiveMenu()
 *
 *  If there is an active window
 *    Remove its menu, then set up and add the menu again
 */

void UpdateActiveMenu()
{
   if (ActiveWindow)
   {
      ClearMenuStrip(ActiveWindow);
      SetActiveMenu(ActiveWindow);
   }
}


/*
 *  SetScreenColors()
 *
 *  If the new scren depth is too big, clip it
 *  For each color in the map, if no color was specified
 *    Get it from the screen's color map (always should be the same)
 *  Set the screen's color map to the specified colors
 */

static void SetScreenColors(theScreen)
struct Screen *theScreen;
{
   short i;
   struct ViewPort *theVP = &(theScreen->ViewPort);
   short Depth = (1 << theScreen->BitMap.Depth);
   
   if (Depth > 32) Depth = 32;
   for (i=0; i<Depth; i++)
      if (Colors[i] == NOCOLOR) Colors[i] = GetRGB4(theVP->ColorMap,i);
   LoadRGB4(theVP,&Colors[0],Depth);
}


/*
 *  DoNewScreen()
 *
 *  Set the modes and width from the current screen according to whether
 *    the new screen should be HIRES or not
 *  Set the modes and height from the current screen according to whether
 *    the new screen should be interlaced or not
 *  Set the modes and depth for HAM screens
 *  If there is enough CHIP memory for the screen plus some extra,
 *    Attempt to open the screen
 *    If successful
 *      Set the new screen's color map
 *      If the screen got a wIconify backdrop window (usually should)
 *        Set the screen as a wIconify screen and as the new WB screen
 *  Otherwise give an error
 */

WSCREEN *DoNewScreen(Depth,oldScreen)
UWORD Depth;
struct Screen *oldScreen;
{
   struct Screen *newScreen;
   WSCREEN *theScreen = NULL;
   UWORD Modes = oldScreen->ViewPort.Modes;
   extern long AvailMem();
   
   if (ISCHECKED(NS_HIRES))
   {
      IconScreen.ViewModes |= HIRES;
      IconScreen.Width = (Modes & HIRES)? oldScreen->Width: oldScreen->Width*2;
      if (Depth > 4) Depth = 4;
   } else {
      IconScreen.ViewModes &= ~HIRES;
      IconScreen.Width = (Modes & HIRES)? oldScreen->Width/2: oldScreen->Width;
   }
   if (ISCHECKED(NS_INTERLACE))
   {
      IconScreen.ViewModes |= LACE;
      IconScreen.Height = (Modes&LACE)? oldScreen->Height: oldScreen->Height*2;
   } else {
      IconScreen.ViewModes &= ~LACE;
      IconScreen.Height = (Modes&LACE)? oldScreen->Height/2: oldScreen->Height;
   }
   if (ISCHECKED(NS_HAM))
   {
      IconScreen.ViewModes |= HAM;
      Depth = 6;
   } else {
      IconScreen.ViewModes &= ~HAM;
   }
   IconScreen.Depth = Depth;
   if (AvailMem(MEMF_CHIP) > 125 * SCREENMEM(IconScreen) / 100)
   {
      newScreen = OpenScreen(&IconScreen);
      if (newScreen)
      {
         SetScreenColors(newScreen);
         if (theScreen = FindScreen(newScreen))
         {
            theScreen->Flags |= WI_WSCREEN;
            NewWBScreen(theScreen);
         }
      } else IconError("Can't open New Screen");
   } else IconError("Too low on memory to open New Screen");
   return(theScreen);
}


/*
 *  DoScreenMenu()
 *
 *  Do the right thing for each Screen menu item:
 *    SCREENTOFRONT:
 *      Send the screen to the front and activate it's backdrop
 *    SCREENTOBACK:
 *      Find the screen following this one
 *      Send the screen to the back
 *      If the following screen has a backdrop window, activate it
 *    WBTOFRONT:
 *      Get the current WB screen and bring it to the front
 *      Activate its backdrop, if any
 *    TOGGLETITLE:
 *      Toggle the title for the screen
 *    ICONIFY:
 *      Set up a bogus message and call the Iconify routine to iconify it
 *    MAKEWB:
 *      Set the screen as the new WB screen
 *      Activate the CurrentWB menu item
 *    NEWCLI:
 *      Create the NewCLI
 *    NEWSCREEN:
 *      Get the subcode
 *      If it is a depth code
 *        Create a new screen of the proper depth
 *        If the screen was created and it has a backdrop window, activate it
 *    CLOSESCREEN:
 *      If the screen is a wIconify screen and it has no windows open on it
 *        Report the screen close to those interested in it
 *          (some icons may choose to close themselves)
 *        Remove the auto-remove icons
 *        If there are no icons left close the screen
 */

int DoScreenMenu(theCode,theScreen,EndItAll)
USHORT theCode;
WSCREEN *theScreen;
int EndItAll;
{
   struct Screen *nextScreen;
   struct wIconMessage tmpMessage;

   switch(ITEMNUM(theCode))
   {
      case SM_TOFRONT:
         ScreenToFront(theScreen->Screen);
         ActivateWindow(theScreen->BackDrop);
         break;

      case SM_TOBACK:
         Forbid(); nextScreen = IntuitionBase->FirstScreen; Permit();
         if (nextScreen == theScreen->Screen)
            nextScreen = nextScreen->NextScreen;
         ScreenToBack(theScreen->Screen);
         theScreen = FindScreen(nextScreen);
         if (theScreen && theScreen->BackDrop)
            ActivateWindow(theScreen->BackDrop);
         break;

      case SM_WBTOFRONT:
         if (WBScreen) theScreen = WBScreen; else theScreen = RealWB;
         ScreenToFront(theScreen->Screen);
         if (theScreen->BackDrop) ActivateWindow(theScreen->BackDrop);
         break;

      case SM_TOGGLE:
         if (theScreen->Screen->Flags & SHOWTITLE)
             ShowTitle(theScreen->Screen,FALSE);
            else
             ShowTitle(theScreen->Screen,TRUE);
         break;

      case SM_ICONIFY:
         tmpMessage.Action = WI_ICONIFY;
         tmpMessage.Window = theScreen->BackDrop;
         tmpMessage.Flags = 0;
         DoIconify(&tmpMessage);
         break;

      case SM_MAKEWB:
         NewWBScreen(theScreen);
         SetWindowMenu(OW_CURRENTWB,NOCHANGE);
         break;

      case SM_NEWCLI:
         DoNewCLI(theScreen);
         break;

      case SM_NEWSCREEN:
         theCode = SUBNUM(theCode);
         if (theCode >= NS_DEPTH1 && theCode <= NS_DEPTH5)
         {
            theScreen = DoNewScreen(theCode-NS_DEPTH1+1,theScreen->Screen);
            if (theScreen && theScreen->BackDrop)
               ActivateWindow(theScreen->BackDrop);
         }
         break;

      case SM_CLOSESCREEN:
         if ((theScreen->Flags & WI_WSCREEN) && theScreen->Window == NULL)
         {
            ReportMulti(WI_REPORTSCREENCLOSE,theScreen->Screen,theScreen);
            RemoveAutoIcons(theScreen);
            if (theScreen->IconRef == NULL) CloseScreen(theScreen->Screen);
         }
         break;
   }
   Permit();
   return(EndItAll);
}
