/*
 *  CALC      Provides a calculator that opens on the active screen when
 *            you press a specific key sequence.  Otherwise, the program
 *            waits quitely in the background.
 *
 *              Copyright 1989 by Davide P. Cervone.
 *  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/dos.h>
#include <proto/intuition.h>
#include <proto/exec.h>

#include "cHandler.h"
#include "cSetup.h"
#include "cKeys.h"

#define SCREENTITLE\
   "Screen Calculator v3.1    Copyright (c) 1989 by Davide P. Cervone"

#define SIGNALCLOSE      TRUE
#define NOSIGNAL         FALSE

#define INTUITION_REV    0
#define GRAPHICS_REV     0
#define ERROR_EXIT      10


#define GADGFLAGS       WINDOWCLOSE | WINDOWDRAG | WINDOWDEPTH
#define IDCMPFLAGS\
   CLOSEWINDOW | GADGETUP | GADGETDOWN | VANILLAKEY |\
   NOCAREREFRESH | ACTIVEWINDOW

struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;

static struct NewWindow NewWindow =
{
   -1,-1, WINDW,WINDH, -1,-1, IDCMPFLAGS,
   SIMPLE_REFRESH | ACTIVATE | GADGFLAGS, NULL, NULL,

#ifdef SWINDOWS
   "::",
#else
   NULL,
#endif

   NULL, NULL, 20,20, -1,-1, WBENCHSCREEN
};


struct Screen *CalcScreen;      /* the screen containing the calculator */
struct Window *CalcWindow;      /* the calculator window */
struct RastPort *rp;            /* the windows' rast port */
static LONG CalcWMask;          /* the calculator handler task */


/*
 *  CloseCalcWindow()
 *
 *  If the window is open, save the calculator position for later, and
 *  close the window.  Clear the pointers to the screen and window, and
 *  forget which signal we used to use for the window.
 *
 *  If we were supposed to let someone know when the window was closed,
 *  then signal the proper task.
 */

void CloseCalcWindow(signal)
int signal;
{
   if (CalcWindow != NULL)
   {
      NewWindow.LeftEdge = CalcWindow->LeftEdge;
      NewWindow.TopEdge  = CalcWindow->TopEdge;
      CloseWindow(CalcWindow);
      CalcWindow = NULL;
      CalcScreen = NULL;
      CalcWMask = 0;
   }
#ifndef SWINDOWS
   if (signal) SignalClosed();
#endif
}


/*
 *  OpenCalcWindow()
 *
 *  If we have a screen to open on, then set the window type and screen
 *  pointer.  Check that the calculator is positioned within the given
 *  screen size.  Open the calulator.  If we were successful, then get the
 *  rastport pointer, and set the mask for the message port.
 */

static void OpenCalcWindow(theScreen)
struct Screen *theScreen;
{
   extern struct Window *OpenWindow();

   if (theScreen)
   {
      CalcScreen = theScreen;
#ifndef NL_DAEMON
      if (theScreen->BitMap.Depth == 1)
      {
         NewWindow.DetailPen = 0;
         NewWindow.BlockPen  = 1;
      } else {
         NewWindow.DetailPen = DARKPEN;
         NewWindow.BlockPen  = BACKPEN;
      }
#endif
#ifndef SWINDOWS
      NewWindow.Screen = CalcScreen;
      NewWindow.Type   = CUSTOMSCREEN;
#endif
      if (NewWindow.LeftEdge < 0 || NewWindow.TopEdge < 0 ||
          NewWindow.LeftEdge + NewWindow.Width > CalcScreen->Width ||
          NewWindow.TopEdge + NewWindow.Height > CalcScreen->Height)
      {
         NewWindow.LeftEdge = (CalcScreen->Width - NewWindow.Width)/2;
         NewWindow.TopEdge = (CalcScreen->Height - NewWindow.Height)/2;
         if (NewWindow.LeftEdge < 0) NewWindow.LeftEdge = 0;
         if (NewWindow.TopEdge  < 0) NewWindow.TopEdge  = 0;
      }
      if ((CalcWindow = OpenWindow(&NewWindow)) != NULL)
      {
         rp = CalcWindow->RPort;
         CalcWMask = (1<<CalcWindow->UserPort->mp_SigBit);
         CalcScreen = CalcWindow->WScreen;
      } else {
         DisplayBeep(theScreen);
      }
   }
}


/*
 *  SetupGadgets()
 *
 *  This fixes the standard Intuition gadgets so that their images are
 *  the New Look images.  This makes the Calulator look cool.  Once
 *  the changes have been made, refresh the window frame so that the new
 *  gadgets show up, then add the rest of the Calc gadgets into the window, 
 *  and refresh them so that they show up.
 */

static void SetupGadgets()
{
#ifndef NL_DAEMON
   struct Gadget *theGadget = CalcWindow->FirstGadget;
   extern struct Image UpFrontImage,DownBackImage,CloseImage;
   
   while (theGadget)
   {
      switch(theGadget->GadgetType & ~SYSGADGET)
      {
         case WUPFRONT:
            if (CalcScreen->BitMap.Depth > 1)
            {
               theGadget->GadgetRender = (APTR)&UpFrontImage;
               theGadget->LeftEdge = -UpFrontImage.Width + 1;
               theGadget->Width = UpFrontImage.Width;
            }
            break;

         case WDOWNBACK:
            if (CalcScreen->BitMap.Depth > 1)
            {
               theGadget->GadgetRender = (APTR)&DownBackImage;
               theGadget->LeftEdge = -UpFrontImage.Width-DownBackImage.Width+1;
               theGadget->Width = DownBackImage.Width;
            }
            break;

         case CLOSE:
            theGadget->GadgetRender = (APTR)&CloseImage;
            theGadget->LeftEdge = 0;
            theGadget->Width = CloseImage.Width;
            break;
      }
      theGadget = theGadget->NextGadget;
   }
   RefreshWindowFrame(CalcWindow);
#endif
   AddGList(CalcWindow,&CalcGadget[0],-1,GADGET_COUNT,NULL);
   RefreshGadgets(&CalcGadget[0],CalcWindow,NULL);
}

#define CloseCalculator     CloseCalcWindow

/*
 *  OpenCalculator()
 *
 *  If the active screen is not the one where the calculator already exists,
 *  then if the calculator is already open, close it (don't signal anyone).
 *  Open the calculator on the new screen.  If the calculator opened OK, 
 *  the setup the gadgets and set the screen title.
 */

static void OpenCalculator()
{
   if (IntuitionBase->ActiveScreen != CalcScreen)
   {
      if (CalcWindow) CloseCalculator(NOSIGNAL);
      OpenCalcWindow(IntuitionBase->ActiveScreen);
      if (CalcWindow)
      {
         SetupGadgets();
         SetWindowTitles(CalcWindow,-1,SCREENTITLE);
      }
   }
}


/*
 *  WaitForAction()
 *
 *  The main Calculator event loop.
 *
 *  While we have not been asked to exit,
 *    if the calc window is open somewhere,
 *      Check for new messages from the calculator window.
 *      If any (and the calculator hasn't been closed or asked to exit) then
 *        get the class code and gadget pointer from the IntuiMessage,
 *        and reply the message.
 *        For CLOSWINDOW events, set the flag indicating we should close.
 *        For keyboard messages, do the appropriate action.
 *        For gadget messages, do the proper gadget action.
 *        For activation events, refresh the lines in the Drag Bar.
 *      When all the messages have been processed,
 *      if we were asked to close the window, then do so.
 *    If we haven't been asked to exit, then wait for more messages.
 *    If we get an ENDSIGNAL from the loader, set the done flag.
 *    If we get a signal to open the calculator, then do so.
 *    If we get a signal to close the calculator (from a CloseScreen
 *      call), then do so.
 */

#define NEWMESSAGE      (theMessage = GetMsg(CalcWindow->UserPort))
#ifndef SWINDOWS
#define MESSAGEWAIT     Wait(CalcSMask|CalcCMask|CalcWMask|ENDSIGNAL)
#else
#define MESSAGEWAIT     Wait(CalcSMask|CalcWMask|ENDSIGNAL)
#endif

static void WaitForAction()
{
   struct IntuiMessage *theMessage;
   extern struct IntuiMessage *GetMsg();
   int Class, Code;
   struct Gadget *theGadget;
   LONG Signals;
   int NotClosed;
   int NotDone = TRUE;

   while (NotDone)
   {
      if (CalcWindow)
      {
         NotClosed = TRUE;
         while (NEWMESSAGE && NotDone && NotClosed)
         {
            Class = theMessage->Class;
            Code  = theMessage->Code;
            theGadget = (struct Gadget *)theMessage->IAddress;
            ReplyMsg(theMessage);

            switch(Class)
            {
               case CLOSEWINDOW:
                  NotClosed = FALSE;
                  break;

               case VANILLAKEY:
                  NotClosed = DoKey(Code);
                  break;

               case GADGETUP:
               case GADGETDOWN:
                  DoGadget(theGadget);
                  break;

               case ACTIVEWINDOW:
                  RefreshGList(&CalcGadget[GADG_BRDR],CalcWindow,NULL,1);
                  break;
            }
         }
         if (NotClosed == FALSE) CloseCalculator(NOSIGNAL);
      }
      if (NotDone) Signals = MESSAGEWAIT;
      if (Signals & ENDSIGNAL) NotDone = FALSE;
      if (Signals & CalcSMask) OpenCalculator();
#ifndef SWINDOWS
      if (Signals & CalcCMask) CloseCalculator(SIGNALCLOSE);
#endif
   }
}


/*
 *  _main()
 *
 *  Initialize the calculator.
 *  As long as we have not been asked to exit, do the main event loop.
 *  Once the main event loop end, we have been asked to exit this task.
 *  close the calculator if it is open, and try to confirm with the loader
 *  that we can exit properly.  If not, then keep running, otherwise
 *  call our exit routine.
 */

void _main()
{
   int NotDone = TRUE;

   InitCalc();
   while (NotDone)
   {
      WaitForAction();
      CloseCalculator(NOSIGNAL);
      NotDone = ConfirmExit();
   }
   cExit();
}
