//
// The Fusion Library Interface for DOS
// Version 1.06c
// Copyright (C) 1990, 1991, 1992
// Software Dimensions
//
// PopUpMenu
//

#include "fli.h"
#include "colors.h"

#ifdef __BCPLUSPLUS__
#pragma hdrstop
#endif

#include <alloc.h>
#include <string.h>
#include <dos.h>
#include <ctype.h>

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//
// PopUpMenu()
//
// Constructor for PopUpMenu class
//
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

PopUpMenu::PopUpMenu()
{
  AllocationError=0;
  AutoSeek=1;
  FusionHelpStorage=NULL;
  HelpStorage=NULL;
  NumberOfOptions=0;
  OptionAvailable=NULL;
  OptionStorage=NULL;
  QuickKeyStorage=NULL;
  TitleOfMenu=NULL;
  FlushMouseQueue();
}

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//
// ~PopUpMenu()
//
// Destructor for PopUpMenu class
//
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

PopUpMenu::~PopUpMenu()
{
  if (OptionStorage)
    free(OptionStorage);

  if (HelpStorage)
    free(HelpStorage);

  if (FusionHelpStorage)
    free(FusionHelpStorage);

  if (OptionAvailable)
    free(OptionAvailable);

  if (QuickKeyStorage)
    free(QuickKeyStorage);

  FlushMouseQueue();
}

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//
// Location()
//
// Define position of menu.  If this function is not called by the
// caller (programmer) than the class will automatically place the
// menu in the center of the screen.
//
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

void PopUpMenu::Location(int X,int Y)
{
  Event::X=X;
  Event::Y=Y;
  AutoSeek=0;
}

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//
// NoCloseIcon()
//
// Gets rid of close icon.
//
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

void PopUpMenu::NoCloseIcon()
{
  CloseIcon=0;
}

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//
// Option()
//
// Define a menu option.  This defines a menu option for the menu.
//
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

void PopUpMenu::Option(char *Option,char *HelpLine,int FusionHelp)
{
  if (!AllocationError)
  {
    NumberOfOptions++;

    if (!(OptionStorage=(char **)realloc(OptionStorage,sizeof(char *)*NumberOfOptions)))
      AllocationError++;

    if (!(HelpStorage=(char **)realloc(HelpStorage,sizeof(char *)*NumberOfOptions)))
      AllocationError++;

    if (!(OptionAvailable=(int **)realloc(OptionAvailable,sizeof(int *)*NumberOfOptions)))
      AllocationError++;

    if (!(FusionHelpStorage=(int *)realloc(FusionHelpStorage,sizeof(int)*NumberOfOptions)))
      AllocationError++;

    if (!(QuickKeyStorage=(int *)realloc(QuickKeyStorage,sizeof(int)*NumberOfOptions)))
      AllocationError++;

    if (AllocationError)
      return;

    static int Available=1;

    OptionAvailable[NumberOfOptions-1]=&Available;
    OptionStorage[NumberOfOptions-1]=Option;
    HelpStorage[NumberOfOptions-1]=HelpLine;
    *(FusionHelpStorage+NumberOfOptions-1)=FusionHelp;
  }
}

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//
// Available()
//
// Passes a pointer which indicates if an option is dead or alive
//
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

void PopUpMenu::Available(int &Available)
{
  if (!AllocationError)
    OptionAvailable[NumberOfOptions-1]=&Available;
}

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//
// Title()
//
// Defines title for menu
//
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

void PopUpMenu::Title(char *Title)
{
  TitleOfMenu=Title;
}

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//
// UseMenu()
//
// Activate the menu and pass control to PopUpMenu class.
//
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

int PopUpMenu::UseMenu()
{
  if (AllocationError)
    return MemoryAllocationError;

  if (!NumberOfOptions)
    return NoAvailableOptions;

  int Option=0;

  while (OptionAvailable[Option] && !*OptionAvailable[Option])
  {
    Option++;
    if (Option==NumberOfOptions)
      return 700;
  }

  int ComputedWidth=0;

  for (register int i=0, j;i<NumberOfOptions;i++)
    if ((j=strlen(OptionStorage[i])-((strchr(OptionStorage[i],'~'))?1:0))
      >ComputedWidth)
      ComputedWidth=j;

  if (AutoSeek)
  {
    X=(Blaze.WhatWidth()-ComputedWidth-4)/2;
    Y=(Blaze.WhatHeight()-NumberOfOptions-2)/2;
  }

  if ((X+ComputedWidth+4)>Blaze.WhatWidth())
    X=Blaze.WhatWidth()-ComputedWidth-4;
  if ((Y+NumberOfOptions+2)>Blaze.WhatHeight()-1)
    Y=Blaze.WhatHeight()-NumberOfOptions-1;

  Width=ComputedWidth+4;
  Height=NumberOfOptions+2;

  MouseHide();

  SpecifyWindow(X,Y,Width,Height);

  if (!ScreenSave)
    return 800;

PlopMenu:

  MouseHide();

  Blaze.InvisibleCursor();

  Blaze.Box(X,Y,Width,Height,Colors.MenuBorder);
  Blaze.EraseArea(X+1,Y+1,ComputedWidth+2,NumberOfOptions,Colors.MenuInsides);
  if (CloseIcon)
    Blaze (X+1,Y) << Colors.MenuBorder << '['
      << Colors.MenuCloseIcon << '\x7'
      << Colors.MenuBorder << ']';

  if (TitleOfMenu && strlen(TitleOfMenu)<=(ComputedWidth-2))
    Blaze ((CloseIcon)?(X+5):(X+1),Y) << Colors.MenuTitle << TitleOfMenu;

  Blaze.Window(X+1,Y+1,ComputedWidth+2,NumberOfOptions);

InsideAgain:

  for (i=0;i<NumberOfOptions;i++)
  {
    int Insides, QuickKey;

    if (!*OptionAvailable[i])
      Insides=QuickKey=Colors.MenuDeadOption;
    else
    {
      Insides=Colors.MenuInsides;
      QuickKey=Colors.MenuQuickKey;
    }

    *(QuickKeyStorage+i)=Blaze.QuickDisplay(1,i,QuickKey,Insides,OptionStorage[i]);
    *(QuickKeyStorage+i)-=(islower(*(QuickKeyStorage+i)))?32:0;
  }

  Blaze.Window(X+1,Y+1,ComputedWidth+2,NumberOfOptions);

  for (int EventCode;;)
  {
    HelpId=*(FusionHelpStorage+Option);
    MouseHide();
    Blaze.ChangeBackground(0,Option,ComputedWidth+2,1,Colors.MenuHiLite);
    Blaze.HelpLine(*(FusionHelpStorage+Option),HelpStorage[Option]);
    MouseShow();

    switch (EventCode=GetEvent())
    {
      case kbDown:
        MouseHide();
        Blaze.ChangeBackground(0,Option,ComputedWidth+2,1,Colors.MenuInsides);
        MouseShow();
        do Option=(Option==NumberOfOptions-1)?0:(++Option);
          while (OptionAvailable[Option] && !*OptionAvailable[Option]);
        continue;

      case kbUp:
        MouseHide();
        Blaze.ChangeBackground(0,Option,ComputedWidth+2,1,Colors.MenuInsides);
        MouseShow();
        do Option=(!Option)?(NumberOfOptions-1):(--Option);
          while (OptionAvailable[Option] && !*OptionAvailable[Option]);
        continue;

      case kbEsc:
        if (CloseIcon)
        {
          RestoreWindow();
          return 0;
        }
        continue;

      case kbCr:
        switch (EventHandler(*(FusionHelpStorage+Option)))
        {
          case RemoveMenu:
            RestoreWindow();
            return 0;
          case KeepMenu:
            if (OptionAvailable[Option] && !*OptionAvailable[Option])
            {
              do Option=(Option==NumberOfOptions-1)?0:(++Option);
                while (OptionAvailable[Option] && !*OptionAvailable[Option]);
            }
            goto InsideAgain;
        }
        continue;

      case MoveEvent:
        Blaze.Window(0,0,Blaze.WhatWidth(),Blaze.WhatHeight());
        goto PlopMenu;

      case MousedEvent:
        if (MouseEvent&MouseLeftButtonRelease && MouseVertical>Y &&
          MouseVertical<=Y+NumberOfOptions &&
          MouseHorizontal>X && MouseHorizontal<=X+ComputedWidth+2)
        {
          if (OptionAvailable[MouseVertical-Y-1]
            && !*OptionAvailable[MouseVertical-Y-1])
              continue;
          MouseHide();
          Blaze.ChangeBackground(0,Option,ComputedWidth+2,1,Colors.MenuInsides);
          Option=MouseVertical-Y-1;
          Blaze.ChangeBackground(0,Option,ComputedWidth+2,1,Colors.MenuHiLite);
          Blaze.HelpLine(*(FusionHelpStorage+Option),HelpStorage[Option]);
          MouseShow();
          switch (EventHandler(*(FusionHelpStorage+Option)))
          {
            case RemoveMenu:
              RestoreWindow();
              return 0;
            case KeepMenu:
              if (OptionAvailable[Option] && !*OptionAvailable[Option])
              {
                do Option=(Option==NumberOfOptions-1)?0:(++Option);
                  while (OptionAvailable[Option] && !*OptionAvailable[Option]);
              }
              goto InsideAgain;
          }
          goto InsideAgain;
        }

        continue;

      case CloseEvent:
      case OutsideEvent:
        if (CloseIcon)
        {
          RestoreWindow();
          return 0;
        }
        continue;

      default:
        if (EventCode<128)
        {
          EventCode-=((islower(EventCode))?32:0);
          for (i=0;i<NumberOfOptions;i++)
            if (*(QuickKeyStorage+i)==EventCode)
            {
              if (OptionAvailable[i])
              {
                MouseHide();
                Blaze.ChangeBackground(0,Option,ComputedWidth+2,1,Colors.MenuInsides);
                Option=i;
                Blaze.ChangeBackground(0,Option,ComputedWidth+2,1,Colors.MenuHiLite);
                Blaze.HelpLine(*(FusionHelpStorage+Option),HelpStorage[Option]);
                MouseShow();
                switch (EventHandler(*(FusionHelpStorage+Option)))
                {
                  case RemoveMenu:
                    RestoreWindow();
                    return 0;
                  case KeepMenu:
                    if (OptionAvailable[Option] && !*OptionAvailable[Option])
                    {
                      do Option=(Option==NumberOfOptions-1)?0:(++Option);
                        while (OptionAvailable[Option] && !*OptionAvailable[Option]);
                    }
                    goto InsideAgain;
                }
                continue;
              }
            }
          continue;
        }

        if (EventCode>=1000) // User event (i.e. button)
        {
          switch (EventHandler(Option))
          {
            case RemoveMenu:
              RestoreWindow();
              return 0;
            case KeepMenu:
              if (OptionAvailable[Option] && !*OptionAvailable[Option])
              {
                do Option=(Option==NumberOfOptions-1)?0:(++Option);
                  while (OptionAvailable[Option] && !*OptionAvailable[Option]);
              }
              goto InsideAgain;
          }
        }
    }
  }
}
