/*********************************************************************\
**                               ________________________________    **
**    A n t h o n y             |________    __    __    ________|   **
**                                       |  |o_|  |o_|  |            **
**            T h y s s e n            __|   __    __   |__          **
**                                  __|   __|  |  |  |__   |__       **
**   `` Dragon Computing ! ''    __|   __|     |  |     |__   |__    **
**                              |_____|        |__|        |_____|   **
**                                                                   **
\*********************************************************************/
/*   Given individual menu entries (Menu,Item,SubItem,CommandChar),
** add them to the MPort as Extended Menu Items.
** Calculate positioning information required to complete the menus.
*/
#include "AMenu.h"
#include "AllocStr.h"          /* string copy/allocate/deallocate macros */
#include <Graphics/GfxBase.h>  /* so we can get the font width and height */

unsigned WBMenus, NextGap, MenuGap;     /* Number WB menus, Menu Pos */
UBYTE    Color[]={2,1}, TColor[]={2,3}; /* Colors :- Items and Title */
USHORT   ItemStyle = HIGHCOMP;          /* MenuItem Highlighting style */
UWORD    FontWidth, FontHeight;         /* size of font in use */

struct ExtMenuItem  **EItemList;        /* Last ExtMenuItem in EItemList */
struct Menu         **MenuList, *CurrMenu;
struct MenuItem     **OldItemList, **ItemList, *CurrSubMenu;
unsigned              OldItemPos, ItemPos;

DB( int NumMenus; int NumItems; )    /* Debugging variables - memory checks */


/*   NOTE: The TopEdge entry in the head element of a menu or sub-menu
** is used to store information about the column structure of that
** menu.  The macros that follow, map this information in to, and out
** of this store.
*/
#define SHIFTCOLS(n) ((n)&255)
#define SHIFTPOS(n)  (((n)&255)<<8)
#define NUMCOLS(n)   ((n)&255)
#define NUMPOS(n)    (((n)>>8)&255)

/*   The LeftEdge entry in individual items (including the sub-menu head
** item) is used to hold the position number of that item in the final
** columned array of the menu.
*/

/*---------------------------------------------------------------*/


void
StartMenu()
  /* Initialise the facts needed */
{
  struct Menu        *Menu;
  ULONG Ilock;

  M->AMenu = NULL;
  MenuList = &M->AMenu;
  M->EItemList = NULL;
  EItemList = &M->EItemList;
  FontWidth  = GfxBase->DefaultFont->tf_XSize; /* font size */
  FontHeight = GfxBase->DefaultFont->tf_YSize;

  Ilock = LockIBase(0L);
  Menu = M->WBWindow->MenuStrip;               /* The First WB Menu */
  MenuGap = Menu->NextMenu->LeftEdge -
              (Menu->LeftEdge + Menu->Width);  /* The Gap between WB Menus */
  for( WBMenus=1; Menu->NextMenu; Menu=Menu->NextMenu)  /* Count WB Menus */
    WBMenus++;
  NextGap = Menu->LeftEdge + Menu->Width;      /* Start of Next Menu Gap */
  UnlockIBase(Ilock);

  DB( NumMenus = NumItems = 0; )
}


static void
PositionMenu(ItemList, SavedInfo, Top, Left)
  struct MenuItem  *ItemList;
  unsigned          SavedInfo, Top, Left;
/*   Given an ItemList organise it into a number of Columns
** Offset by Top and Left (for SubMenus).
** Assumes that LeftEdge contains the Position Number for
** each Item in Item List.
*/
{
  struct MenuItem *Start, *End;       /* Position within ItemList */
  unsigned  NumColumns, ItemsPerColumn, Column;

  NumColumns = NUMCOLS(SavedInfo);
  ItemsPerColumn = NUMPOS(SavedInfo) / NumColumns + 1;

  End = ItemList;

  for(Column=0; Column<NumColumns; Column++ ) {
    SHORT  Width, ColWidth=0;

    Start = End;       /* first Item in column = End of last Column */

      /* determine Columns Width and Extent */
    while( End && (End->LeftEdge/ItemsPerColumn) == Column ) {
      Width = End->Width;
      if( End->Flags & COMMSEQ )
        Width += COMMWIDTH + FontWidth + 4;
      if( Width > ColWidth )
        ColWidth = Width;

      End = End->NextItem;
    }

     /* Position all the Items in Column */
    while( Start != End ) {

      /* if their a Sub-Menu within this menu - Position it*/
      if( Start->SubItem )
        PositionMenu( Start->SubItem, Start->TopEdge, 0, ColWidth );

       /* position this Item */
      Start->TopEdge  = Top +
                    (Start->LeftEdge % ItemsPerColumn) * (FontHeight+2);
      Start->LeftEdge = Left;
      Start->Width    = ColWidth;

      Start = Start->NextItem;
    }

    Left = Left + ColWidth + 8;  /* Gap between columns */
  } /* For each Column */

}


BOOL
FinishMenu()
  /* Cleanup New Menus - Postition, Size, etc. */
{
  struct Menu  *Menu;
  BOOL          ScreenOverflow;

  DB( Printf("Added -- %ld Menus, %ld Items\n", NumMenus, NumItems); )

  ScreenOverflow = ( NextGap > M->WBWindow->Width );
                        /* screen overflow on number of menus ? */

  for (Menu = M->AMenu;  Menu;  Menu=Menu->NextMenu) {
    PositionMenu( Menu->FirstItem, Menu->TopEdge, 0, 0 );
    Menu->TopEdge = 0;    /* explictly remove information saved */
  }

  return ScreenOverflow;        /* if overflow - have parser WARN user */
}


/*---------------------------------------------------------------------*/

/* create and initialize a new menu item */
struct ExtMenuItem *
AddItem(ItemName, CommandChar)
  char *ItemName, CommandChar;
{
  register struct ExtMenuItem *EItem;
  register struct MenuItem    *Item;
  register struct IntuiText   *IText;

  EItem = (struct ExtMenuItem *)
           AllocMem(sizeof(struct ExtMenuItem), MEMF_PUBLIC|MEMF_CLEAR);
  IText = (struct IntuiText *)
           AllocMem(sizeof(struct IntuiText), MEMF_PUBLIC|MEMF_CLEAR);
  Item = (struct MenuItem *)EItem;    /* same object different type */

    /* fill out the selection */
  IText->FrontPen = Color[0];
  IText->BackPen  = Color[1];
  IText->DrawMode = JAM2;
  IText->IText    = (UBYTE*)AllocStr(ItemName);
  Item->Height   = FontHeight;
  Item->Width    = IntuiTextLength(IText);
  Item->Flags    = ITEMTEXT | ITEMENABLED | ItemStyle;
  Item->ItemFill = (APTR)IText;
  if( CommandChar ) {
    Item->Command = CommandChar;
    Item->Flags |= COMMSEQ;
  }

  *EItemList = EItem;            /* add to EItemList for Free'ing */
  EItemList = &EItem->NextEItem;

  *ItemList = Item;              /* add to the current MenuItem chain */
  ItemList = &Item->NextItem;
  Item->LeftEdge = ItemPos++;

  DB( NumItems++; )
  return EItem;
}


void
AddTitle( TitleName )
  char *TitleName;
/* create a Menu Item in the form of a Title
*/
{
  struct ExtMenuItem *EItem = AddItem( TitleName, '\0' );
  struct IntuiText *IText =
           (struct IntuiText *)EItem->MenuItem.ItemFill;
  IText->FrontPen = TColor[0];
  IText->BackPen  = TColor[1];
  EItem->MenuItem.Flags = ITEMTEXT | ITEMENABLED | HIGHNONE;
}


void
AddSkip()
/* skip a position in the Menu Items (Leave a space)
*/
{
  ItemPos++;  /* adjust position number for latter items and menu size */
}


/*-------------------------------------------------------------*/

void
AddSubMenu(SubName, NumColumns)
  char *SubName;
  int   NumColumns;
{
  struct ExtMenuItem *EItem = AddItem(SubName, '\0');
  struct MenuItem    *Item  = (struct MenuItem *)EItem;

  Item->TopEdge = SHIFTCOLS(NumColumns);  /* store information needed */

  CurrSubMenu = Item;
  OldItemList = ItemList;       /* setup item list */
  ItemList = &Item->SubItem;
  OldItemPos = ItemPos;
  ItemPos = 0;
}


void
EndSubMenu()
/* Cleanup and finalize a SubMenu */
{
  CurrSubMenu->TopEdge |= SHIFTPOS(ItemPos-1); /* last position number */
  ItemPos  = OldItemPos;      /* return to a normal Menu */
  ItemList = OldItemList;
}

/*------------------------------------------------------------*/

void
AddMenu(MenuName, NumColumns)
  char *MenuName;
  int  NumColumns;
/* Start a new menu attaching it to the AMenu list and storing
** Information about its characteristics
*/

{
  struct Menu *Menu = (struct Menu *)
          AllocMem(sizeof(struct Menu), MEMF_PUBLIC|MEMF_CLEAR);

  Menu->MenuName = (UBYTE *)AllocStr(MenuName);
  Menu->LeftEdge = NextGap + MenuGap;      /* use menugap on creation */
  Menu->Width = (strlen(MenuName)+1) * FontWidth;
  Menu->Flags = MENUENABLED;
  NextGap = Menu->LeftEdge + Menu->Width;
  Menu->TopEdge = SHIFTCOLS(NumColumns);

  CurrMenu = Menu;
  *MenuList = Menu;              /* add to menu list */
  MenuList = &Menu->NextMenu;
  ItemList = &Menu->FirstItem;   /* initialize Item list */
  ItemPos = 0;

  DB( NumMenus++; );
}


void
EndMenu()
/* Finalize the Position Tally for this Menu */
{
  CurrMenu->TopEdge |= SHIFTPOS(ItemPos-1); /* last position number */
}

/*------------------------------------------------------------*/

void
FreeMenus()
  /* free up all space taken up by our menus */
{
  struct Menu *Menu;
  struct ExtMenuItem *EItem;
  struct IntuiText *IText;

  DB( NumMenus = NumItems = 0; )

  while( EItem = M->EItemList ) {           /* free all the ExtMenuItems */
    M->EItemList = EItem->NextEItem;
    if( EItem->Cmd )       FreeStr(EItem->Cmd);
    if( EItem->Args )      FreeStr(EItem->Args);
    if( EItem->Console )   FreeStr(EItem->Console);
    if( EItem->Directory ) FreeStr(EItem->Directory);
    IText = (struct IntuiText *)EItem->MenuItem.ItemFill;
    FreeStr(IText->IText);
    FreeMem(IText, sizeof(struct IntuiText));
    FreeMem(EItem, sizeof(struct ExtMenuItem));
    DB( NumItems++; )
  }

  while( Menu = M->AMenu ) {             /* Free all the Menus */
    M->AMenu = Menu->NextMenu;
    FreeStr(Menu->MenuName);
    FreeMem(Menu, sizeof(struct Menu));
    DB( NumMenus++; )
  }
  DB( Printf("\nFreed -- %ld Menus, %ld Items\n", NumMenus, NumItems); )
}

