//
// The Fusion Library Interface for DOS
// Version 1.06c
// Copyright (C) 1990, 1991, 1992
// Software Dimensions
//
// MenuControl --> MenuItems --> FusionWindow
//

#include "fliwin.h"
#include "colors.h"

#ifdef __BCPLUSPLUS__
#pragma hdrstop
#endif

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

int MenuManager::IdentifyCheck=1;

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

MenuItems::MenuItems()
{
  Title=0;
  MenuHelp=0;
  MenuHotKey=0;
  CurrentOption=0;
  NumberOfOptions=0;
  SubMenus=0;
  HotKeys=0;
  Width=0;
  Height=2;
  X=0;
  Y=0;
  XStart=0;
  XEnd=0;
  SavedRegion=0;
  Option=0;
  Checkables=0;
  Selectables=0;
}

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

MenuItems::~MenuItems()
{
  if (NumberOfOptions)
  {
    for (int i=0;i<NumberOfOptions;i++)
    {
      if ((Option+i)->SubMenu)
        delete (Option+i)->SubMenu;
      if ((Option+i)->Selectables)
        delete (Option+i)->Selectables;
    }
    free(Option);
  }
}

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//
// PlaceMenu()
//
// Places a menu on the display
//
// The Refreshing variable is used to refresh a menu and does not save
// the insides, its primary purpose is to redraw ONLY the inside of the menu
//
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

int MenuItems::PlaceMenu(int SetY,int CurrentLevel,MenuItems &PreviousMenu,
  int Refreshing)
{
  BlazeClass Blaze;

  if (!Refreshing)
  {
    if (!Option)
    {
      Blaze.HelpLine(0,(!MenuEvent)?"Menu not available":MenuHelp);
      MouseHide();
      Blaze.ChangeBackground(XStart,SetY,XEnd-XStart+1,1,Colors.MenuHiLite);
      MouseShow();
      CurrentLevel++;
      X=500;
      Y=500;
      return 0;
    }
  }

  MouseHide();
  Blaze.InvisibleCursor();

  Width=0;

  Height=NumberOfOptions+2;

  for (int j=0,_OWide=0,_HWide=0,_SWide=0,_CWide=0,_EWide=0,OkaySubs=0;
    j<Height-2;j++)
  {
    _Options &Option=*(MenuItems::Option+j);

    Option.PreviousState=(Option.Available)?*Option.Available:1;

    int OWide=(strlen(Option.Option)-(strchr(Option.Option,'~')?1:0));
    int HWide=(Option.HotKeyOption)?(strlen(Option.HotKeyOption)+1):0;
    int SWide=(Option.SubMenu)?2:0;
    int CWide=(Option.Checked)?2:0;
    int EWide=0;

    if (Option.SelectCount)
    {
      for (int i=0,Widest=0,Width;i<Option.SelectCount;i++)
      {
        if ((Width=strlen(Option.Selectables[i]))>Widest)
          Widest=Width;
      }
      Option.WidestSelectable=EWide=++Widest;
    }

    if (Option.SubMenu && !Option.Checked && !Option.Selectables)
      OkaySubs++;

    if (OWide>_OWide)
      _OWide=OWide;
    if (HWide>_HWide)
      _HWide=HWide;
    if (SWide>_SWide)
      _SWide=SWide;
    if (CWide>_CWide)
      _CWide=CWide;
    if (EWide>_EWide)
      _EWide=EWide;
  }

  Width=(_OWide+_HWide+_SWide+_CWide+_EWide+5)-((OkaySubs && Selectables)?2:0);

  if (Checkables && Selectables)
  {
    if (_EWide>_CWide)
      Width-=_CWide;
    else
      Width-=_EWide;
  }

  if (!Refreshing)
  {
    if (CurrentLevel==1)
    {
      Blaze.ChangeBackground(XStart,SetY,XEnd-XStart+1,1,Colors.MenuHiLite);

      X=(XStart+Width+1>Blaze.WhatWidth()-1)?(Blaze.WhatWidth()-Width):XStart;
      Y=(SetY+Height>Blaze.WhatHeight()-2)?(Blaze.WhatHeight()-Height-1):(SetY+1);
    }
    else
    {
      if (!MenuManager::Beneath)
      {
        Y=PreviousMenu.Y+PreviousMenu.CurrentOption;
        X=PreviousMenu.X+PreviousMenu.Width;
      }
      else
      {
        Y=PreviousMenu.Y+PreviousMenu.CurrentOption+2;
        X=PreviousMenu.X+2;
      }

      X=(X+Width>=Blaze.WhatWidth())?(Blaze.WhatWidth()-Width):X;
      Y=(Y+Height>Blaze.WhatHeight()-2)?(Blaze.WhatHeight()-Height-1):Y;
    }

    if (FusionShadowing)
    {
      int XShadow=1;
      int YShadow=1;

      int XOffset=0;
      int YOffset=0;

      if (X+Width==Blaze.WhatWidth())
      {
        XShadow=0;
        XOffset=1;
      }

      if (Y+Height==Blaze.WhatHeight()-2)
      {
        YShadow=0;
        YOffset=1;
      }

      SavedRegion=new char[Blaze.ComputeNeededBytes(Width+XShadow,Height+YShadow)];

      Blaze.GetArea(X,Y,Width+XShadow,Height+YShadow,SavedRegion);
      Blaze.Shadow(X+XShadow+XOffset,Y+YShadow+YOffset,Width,Height);
    }
    else
    {
      SavedRegion=new char[Blaze.ComputeNeededBytes(Width,Height)];

      Blaze.GetArea(X,Y,Width,Height,SavedRegion);
    }
  }

  if (!Refreshing || Refreshing==2)
  {
    Blaze.Box(X,Y,Width,Height,Colors.MenuBorder);
    Blaze.EraseArea(X+1,Y+1,Width-2,Height-2,Colors.MenuInsides);
  }

  for (j=0;j<Height-2;j++)
  {
    _Options &Option=*(MenuItems::Option+j);

    if (Refreshing && Refreshing!=2)
    {
      if (j==CurrentOption &&
        (!Option.Available || (Option.Available && *Option.Available)))
        continue;

      if (j==CurrentOption && Option.Available && !*Option.Available)
        Blaze.CharacterRepeater(X+1,Y+1+j,Width-2,Colors.MenuInsides,32);
    }

    if (Option.Option)
    {
      Option.QuickKey=Blaze.QuickDisplay(X+2+((Checkables)?2:0),Y+1+j,
        (*Option.Available)?Colors.MenuQuickKey:Colors.MenuDeadOption,
        (*Option.Available)?Colors.MenuInsides:Colors.MenuDeadOption,
        Option.Option);
      if (Checkables && Option.Checked)
        Blaze (X+2,Y+1+j) <<
          ((*Option.Available)?Colors.MenuCheckMark:Colors.MenuDeadOption) <<
          (char)((*Option.Checked)?'û':((MenuManager::IdentifyCheck)?'_':' '));
      else
        if (Selectables && Option.Selectables)
        {
          Blaze (X+Width-2-strlen(Option.Selectables[*Option.Selectable]),Y+1+j) <<
            ((*Option.Available)?Colors.MenuSelectable:Colors.MenuDeadOption) <<
            Option.Selectables[*Option.Selectable];
        }
      if (Option.HotKeyOption)
      {
        Blaze
          ((X+Width-strlen(Option.HotKeyOption)-2-((SubMenus)?2:0)-
            ((Selectables)?_EWide:0)),
            Y+1+j)
          << ((*Option.Available)?Colors.MenuHotKey:Colors.MenuDeadOption)
          << Option.HotKeyOption;
      }
      if (Option.SubMenu && !Option.Checked)
      {
        Blaze (X+Width-3,Y+1+j)
          << ((*Option.Available)?Colors.MenuSubPointer:Colors.MenuDeadOption)
          << ((MenuManager::Beneath)?'\x1f':'\x10');
      }
    }
    else
    {
      Blaze.CharacterRepeater(X+1,Y+1+j,
        Width-2,Colors.MenuBorder,196);
      Blaze << Colors.MenuBorder;
      Blaze (X,Y+1+j) << '\xc3';
      Blaze (X+Width-1,Y+1+j) << '\xb4';
    }
  }

  if (Refreshing)
  {
    if (!(Option+CurrentOption)->Available ||
      ((Option+CurrentOption)->Available && *(Option+CurrentOption)->Available))
      PlaceOption(CurrentOption);
    else if ((Option+CurrentOption)->Available &&
      !*(Option+CurrentOption)->Available)
    {
      RemoveOption();
      int i=-1;
      int j=0;
      do i=(i==NumberOfOptions-1)?0:(++i);
        while ((!*(Option+i)->Available ||
          !(Option+i)->Option) &&
          ++j!=NumberOfOptions);
      PlaceOption(i);
    }

    return 0;
  }
  else
    return PlaceOption(CurrentOption);
}

void MenuItems::ReCheckOption()
{
  MouseHide();
  BlazeClass Blaze;
  Blaze (X+2,Y+1+CurrentOption) << Colors.MenuCheckMark <<
    (char)((*(Option+CurrentOption)->Checked)?'û':
      ((MenuManager::IdentifyCheck)?'_':' '));
  PlaceOption(CurrentOption);
}

void MenuItems::ReSelectOption()
{
  MouseHide();
  BlazeClass Blaze;
  _Options &Option=*(MenuItems::Option+CurrentOption);
  Blaze.CharacterRepeater(X+Width-Option.WidestSelectable-1,Y+1+CurrentOption,
    Option.WidestSelectable-3,Colors.DiaInterior,' ');
  Blaze (X+Width-2-strlen(Option.Selectables[*Option.Selectable]),Y+1+CurrentOption) <<
    ((*Option.Available)?Colors.MenuSelectable:Colors.MenuDeadOption) <<
    Option.Selectables[*Option.Selectable];
  PlaceOption(CurrentOption);
}

void MenuManager::PlaceMenu(MenuItems &Menu)
{
  SubMenuTrack=(MenuItems**)realloc(SubMenuTrack,sizeof(MenuItems*)*++CurrentLevel);
  SubMenuTrack[CurrentLevel-1]=&Menu;

  HelpId=Menu.PlaceMenu(SetY,CurrentLevel,*SubMenuTrack[CurrentLevel-2]);
}

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//
// PlaceOption()
//
// Places an option back on the menu
//
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

int MenuItems::PlaceOption(int OptionToPlace)
{
  if (!NumberOfOptions)
    return 0;

  BlazeClass Blaze;

  _Options &Option=*(MenuItems::Option+OptionToPlace);
  CurrentOption=OptionToPlace;

  MouseHide();

  if (!*Option.Available)
    Blaze.HelpLine(0,"Option not available");
  else if (!Option.Option)
    Blaze.HelpLine(0,"Not an option");
  else
  {
    Blaze.ChangeBackground(X+1,Y+OptionToPlace+1,Width-2,1,Colors.MenuHiLite);
    Blaze.HelpLine(Option.Event,Option.Help);
  }

  MouseShow();

  return (!*Option.Available || !Option.Option)?0:Option.Event;
}

void MenuManager::PlaceOption(int OptionToPlace)
{
  HelpId=SubMenuTrack[CurrentLevel-1]->PlaceOption(OptionToPlace);
}

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//
// RemoveMenu()
//
// Removes a menu from the display
//
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

void MenuItems::RemoveMenu()
{
  BlazeClass Blaze;

  MouseHide();

  if (SavedRegion)
  {
    Blaze.PutArea(X,Y,SavedRegion);
    delete SavedRegion;
    SavedRegion=0;
  }

  Blaze.HelpLine(0,"");

  MouseShow();
}

void MenuManager::RemoveMenu()
{
  if (CurrentLevel)
    CurrentLevel--;
  else
    return;

  MenuItems &Menu=*SubMenuTrack[CurrentLevel];

  Menu.RemoveMenu();

  if (!CurrentLevel)
  {
    MouseHide();
    BlazeClass Blaze;
    Blaze.ChangeBackground(Menu.XStart,SetY,Menu.XEnd-Menu.XStart+1,1,
      Colors.MenuBarNormal);
    MouseShow();
  }

  if (CurrentLevel)
  {
    MenuItems &Menu=*SubMenuTrack[CurrentLevel-1];
    _Options &Option=*(Menu.Option+Menu.NumberOfOptions-1);
    HelpId=Option.Event;
  }
  else
    HelpId=0;

  return;
}

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//
// RemoveOption()
//
// Removes highlighting from an option
//
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

void MenuItems::RemoveOption()
{
  if (!NumberOfOptions)
    return;

  BlazeClass Blaze;

  _Options &Option=*(MenuItems::Option+CurrentOption);

  if (!*Option.Available || !Option.Option)
    return;

  MouseHide();
  Blaze.ChangeBackground(X+1,Y+1+CurrentOption,Width-2,1,Colors.MenuInsides);
  MouseShow();
}

void MenuManager::RemoveOption()
{
  if (!CurrentLevel)
    return;

  SubMenuTrack[CurrentLevel-1]->RemoveOption();

  HelpId=0;

  return;
}

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//
// ScanMenu()
//
// Scan menu for a hot key
//
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

int MenuItems::ScanMenu(int Event)
{
  for (int i=0;i<NumberOfOptions;i++)
  {
    _Options &Option=*(MenuItems::Option+i);

    if (Event==Option.HotKey)
    {
      if (*Option.Available && Option.Checked && Option.Option)
      {
        *Option.Checked=(*Option.Checked)?0:1;
        return Option.Event;
      }

      if (!*Option.Available || !Option.Option)
        return 0;

      if (Option.SubMenu)
        continue;

      if (Option.Checked)
        *Option.Checked=(*Option.Checked)?0:1;
      else
        if (Option.Selectables)
          *Option.Selectable=(*Option.Selectable==Option.SelectCount-1)?0:(++(*Option.Selectable));

      return Option.Event;
    }

  }

  for (i=0;i<NumberOfOptions;i++)
  {
    _Options &Option=*(MenuItems::Option+i);

    if (Option.SubMenu)
    {
      if (!*Option.Available || !Option.Option)
        continue;

      int Save=Option.SubMenu->ScanMenu(Event);

      if (Save)
        return Save;
    }
  }

  return 0;
}

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//
// RefreshMenus()
//
// Refreshes the Menus -- if they have changed
//
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

void MenuManager::RefreshMenus()
{
  if (!CurrentLevel)
    return;

  for (int i=0;i<CurrentLevel;i++)
  {
    for (int j=0;j<SubMenuTrack[i]->NumberOfOptions;j++)
    {
      _Options &Option=*(SubMenuTrack[i]->Option+j);

      if (Option.Available && Option.PreviousState!=*Option.Available)
      {
        // Menu State HAS Changed!!

        MenuItems DudMenu;

        if (i==CurrentLevel-1) // On bottom menu tree
          SubMenuTrack[i]->PlaceMenu(0,0,DudMenu,1);
        else // Within the menu tree
        {
          // Scan the tree and see if any menus should be trimmed off

          for (int q=i;q<CurrentLevel;q++)
          {
            _Options &Option=*(SubMenuTrack[q]->Option+
              SubMenuTrack[q]->CurrentOption);

            if (!Option.Available || (Option.Available && *Option.Available))
              continue; // This level is acceptable
            else
            {
              // whoops! An option isn't available! Trim off remaining branches

              int TrimOff=CurrentLevel-q;

              for (int s=0;s<TrimOff-1;s++)
                RemoveMenu();

              if (q!=i)
                for (int r=i;r<CurrentLevel;r++)
                  SubMenuTrack[r]->PlaceMenu(0,0,DudMenu,2);

              if (CurrentLevel)
                SubMenuTrack[CurrentLevel-1]->PlaceMenu(0,0,DudMenu,1);

              // Browse through menus and knock off menus that are all dead

              for (s=CurrentLevel-1;s>=0;s--)
              {
                for (int r=0,t=0;r<SubMenuTrack[s]->NumberOfOptions;r++)
                {
                  _Options &Option=*(SubMenuTrack[s]->Option+r);

                  if ((!Option.Available ||
                    (Option.Available && *Option.Available)) &&
                    Option.Option)
                    t++;
                }
                if (!t)
                  RemoveMenu();
              }

              return;
            }
          }

          for (q=i;q<CurrentLevel;q++)
            SubMenuTrack[q]->PlaceMenu(0,0,DudMenu,2);

          return;
        }

        return;
      }
    }
  }
}

