/* :ts=8 */
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
/*                                                              */
/*      MenuStuf.c                                              */
/*                                                              */
/*      Menu routines for AmigaPLUS article about Intuition    */
/*      Written by Michael G. Lehman                            */
/*      of Intuitive Technologies                               */
/*                                                              */
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/


/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
/*      Complete Include file for Amiga programs                */
/*      Derived from examples supplied by Commodore Amiga       */
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/


#include <exec/types.h>
#include <exec/nodes.h>
#include <exec/lists.h>
#include <exec/memory.h>
#include <exec/ports.h>
#include <exec/tasks.h>
#include <exec/libraries.h>
#include <exec/devices.h>
#include <exec/io.h>
#include <exec/devices.h>

#include <libraries/dos.h>

#include <graphics/gfx.h> /* ALWAYS INCLUDE GFX.H before other includes */
#include <graphics/display.h>
#include <graphics/clip.h>
#include <graphics/rastport.h>
#include <graphics/gfxbase.h>
#include <graphics/text.h>
#include <graphics/regions.h>
#include <graphics/copper.h>
#include <graphics/gels.h>

#include <devices/inputevent.h>
#include <devices/gameport.h>
#include <devices/console.h>
#include <devices/keymap.h>

#define int32 long
#define int16 int

#include <intuition/intuition.h>
struct XMenuItem                /* our extended menu structure */
{
        struct XMenuItem *NextItem;
        SHORT LeftEdge, TopEdge;
        SHORT Width, Height;
        USHORT Flags;
        LONG MutualExclude;
        APTR ItemFill;
        APTR SelectFill;
        BYTE Command;
        struct XMenuItem *SubItem;
        USHORT NextSelect;
        int (*ItemHndlr)();     /* all above is standard and we add this */
};


extern UBYTE * AllocMem();

#define SECONDS io_Actual
#define MICROSECONDS io_Length
#define PUBCLEAR MEMF_CHIP | MEMF_CLEAR

struct Menu *NewMenu();
struct XMenuItem *AddItem();
struct XMenuItem *AddSubItem();

extern struct Window *Mwindow;  /* currently chosen window and screen for */
extern struct Screen *Mscreen; /* this menu build operation               */

#define ITEMSTD         ITEMTEXT | ITEMENABLED | HIGHCOMP
#define CHKITEMSTD      ITEMSTD | CHECKIT

/****************************************************************/
/*                                                              */
/*      NewMenu                                                 */
/*      Build a menu header                                     */
/*      LeftEdge automatically calcuated if passed as -1        */
/*      Width automatically calculated if passed as -1          */
/*                                                              */
/*      Returns pointer to the Menu for calling AddItem         */
/*                                                              */
/****************************************************************/

struct Menu *NewMenu(root,left,top,width,height,flags,title)
struct Menu *root;
int32 left,top,width,height,flags;
register char *title;
{

  register int32 i;
  register struct Menu *p;
  register struct Menu *p2;

  p = AllocMem(sizeof(struct Menu),PUBCLEAR);
  p2 = NULL;

  if (root != NULL)
    {
        p2 = root /* -> NextMenu */ ;
        while(p2->NextMenu !=NULL)
          p2 = p2 -> NextMenu;
    }

  if (p2 != NULL)
    p2 -> NextMenu = p;

  p -> NextMenu         = NULL;
  if (left == -1 && p2 != NULL)
    p -> LeftEdge = p2 -> LeftEdge + p2 -> Width + 6;
  else
    p -> LeftEdge       = left;
  p -> TopEdge          = top;
  if (width == -1)
    p -> Width = TextLength(&Mscreen -> RastPort,title,strlen(title))+10;
  else
    p -> Width          = width;
  p -> Height           = height;
  p -> Flags            = flags;
  p -> FirstItem        = NULL;
  p -> MenuName         = title;

  return(p);
}

/****************************************************************/
/*                                                              */
/*      AddItem                                                 */
/*      Add a menu item to a menu list                          */
/*      Width and Height automatically calculated               */
/*                                                              */
/*      Returns pointer to the item for calling AddSubItem      */
/*                                                              */
/****************************************************************/

struct XMenuItem *AddItem(mp,flags,text,handler,cmdchar)
struct Menu *mp;
int32 flags;
char *text;
int32 (*handler)(); /* subroutine to handle this menu item */
char cmdchar;   /* valid only if flags & COMMSEQ is true */
{
  register struct XMenuItem *mp2;
  register struct XMenuItem *mi;
  register struct IntuiText *ip;

  register int32 i;
  int32 maxwidth;

  i = 0;


  if (mp -> FirstItem != NULL)
    {
      mp2 = mp -> FirstItem;
      i++;
      while(mp2 -> NextItem != NULL)
        {
          i++;
          mp2 = mp2 -> NextItem;        /* get to the bottom of the list */
        }
    }
  else
    mp2 = NULL; /* so our debug stuff doesn't blow up */

  mi = AllocMem(sizeof(struct XMenuItem),PUBCLEAR);/*this zeroes it as well */
  ip = (struct IntuiText *) AllocMem(sizeof(struct IntuiText),PUBCLEAR);
  ip -> FrontPen = 0;
  ip -> BackPen = Mwindow -> BlockPen;
  ip -> DrawMode = JAM2;
  ip -> LeftEdge = 0;
  ip -> TopEdge  = 1;
  ip -> ITextFont = NULL;
  ip -> NextText = NULL;
  ip -> IText = text;

  mi -> NextItem = NULL;
  mi -> LeftEdge = 0;
  mi -> TopEdge  = /*12*/ 9 * i;        /* standard height = 12 lines */
  mi -> Width = IntuiTextLength(ip);
  if (flags & COMMSEQ)
    mi -> Width += COMMWIDTH;
  mi -> Height= /*12*/ 9;               /* standard height = 12 lines */
  mi -> Flags = flags;
  mi -> MutualExclude = 0;
  mi -> ItemFill =  (APTR) ip;
  mi -> SelectFill = NULL;
  mi -> Command = 0;
  mi -> SubItem = NULL;
  mi -> NextSelect = 0;
  mi -> ItemHndlr = handler;
  if (flags & COMMSEQ)
    mi -> Command = cmdchar;

  if (i == 0)
    mp -> FirstItem = mi;
  else
    mp2 -> NextItem = mi;

  /* now run the menu list looking for the widest */
  mp2 = mp -> FirstItem;
  maxwidth = mp2 -> Width;

  while(mp2 -> NextItem != NULL)
    {
        mp2 = mp2 -> NextItem;
        if (mp2 -> Width > maxwidth)
          maxwidth = mp2 -> Width;
    }

  if (maxwidth <= mp -> Width)
    maxwidth = mp -> Width;

  /* now set all items to the max width */
  mp2 = mp -> FirstItem;
  while(mp2 != NULL)
    {
        mp2 -> Width = maxwidth;
        mp2 = mp2 -> NextItem;
    }

  SetMaximumWidth(mp);

  return(mi);
}

/****************************************************************/
/*                                                              */
/*      AddSubItem                                              */
/*      Add a sub menu item to a menu item                      */
/*      Width and Height automatically calculated               */
/*                                                              */
/*      Returns pointer for adding sub-sub items                */
/*                                                              */
/****************************************************************/

struct XMenuItem *AddSubItem(mp,flags,text,cmdchar)
struct XMenuItem *mp;
int32 flags;
char *text;
char cmdchar;   /* valid only if flags & COMMSEQ is true */
{
  register struct XMenuItem *mp2;
  register struct XMenuItem *mi;
  register struct IntuiText *ip;

  register int32 i;
  int32 maxwidth;

  i = 0;

  if (mp -> SubItem != NULL)
    {
      mp2 = mp -> SubItem;
      i++;
      while(mp2 -> NextItem != NULL)
        {
          i++;
          mp2 = mp2 -> NextItem;        /* get to the bottom of the list */
        }
    }

  mi = AllocMem(sizeof(struct XMenuItem),PUBCLEAR);/* this zeroes it as well */
  ip = (struct IntuiText *) AllocMem(sizeof(struct IntuiText),PUBCLEAR);
  ip -> FrontPen = 0;
  ip -> BackPen = Mwindow -> BlockPen;
  ip -> DrawMode = JAM2;
  ip -> LeftEdge = 0;
  ip -> TopEdge  = 1;
  ip -> ITextFont = NULL;
  ip -> NextText = NULL;
  ip -> IText = text;

  mi -> NextItem = NULL;
  mi -> LeftEdge = mp -> LeftEdge + mp -> Width -2;
  mi -> TopEdge  = /*12*/ 9 * i;        /* standard height = 12 lines */
  mi -> Width = IntuiTextLength(ip);
  if (flags & COMMSEQ)
    mi -> Width += COMMWIDTH;
  mi -> Height= /*12*/ 9;               /* standard height = 12 lines */
  mi -> Flags = flags;
  mi -> MutualExclude = 0;
  mi -> ItemFill =  (APTR) ip;
  mi -> SelectFill = NULL;
  mi -> Command = 0;
  mi -> SubItem = NULL;
  mi -> ItemHndlr = mp -> ItemHndlr; /* copy from the parent menu */
  mi -> NextSelect = 0;
  if (flags & COMMSEQ)
    mi -> Command = cmdchar;

  if (i == 0)
    mp -> SubItem = mi;
  else
    mp2 -> NextItem = mi;

  /* now run the menu list looking for the widest */
  mp2 = mp -> SubItem;
  maxwidth = mp2 -> Width;
  while(mp2 -> NextItem != NULL)
    {
        mp2 = mp2 -> NextItem;
        if (mp2 -> Width > maxwidth)
          maxwidth = mp2 -> Width;
    }

  /* now set all items to the max width */
  mp2 = mp -> SubItem;
  while(mp2 != NULL)
    {
        mp2 -> Width = maxwidth;
        mp2 = mp2 -> NextItem;
    }

  return(mi);
}


/****************************************************************/
/*                                                              */
/*      UnMakeMenuStrip(rmp)                                    */
/*                                                              */
/*      Takes as input a "root menu pointer" i.e. the pointer   */
/*      from the Window record                                  */
/*      calls as needed:                                        */
/*              UnMakeMenu                                      */
/*              UnMakeItem                                      */
/*              UnMakeSubItem                                   */
/****************************************************************/

void UnMakeMenu();
void UnMakeItem();
void UnMakeSubItem();

void UnMakeMenuStrip(rmp)
struct Menu *rmp;
{
  register struct Menu *mp,*mp2;

  if (rmp == NULL)
    return;
  mp = rmp;

  while(mp)
    {
      mp2 = mp -> NextMenu;
      UnMakeMenu(mp);                   /* deallocate each menu in turn */
      mp = mp2;
    }
}

void UnMakeMenu(mp)
struct Menu *mp;
{
  register struct XMenuItem *mip,*mip2;

  if (mp == NULL)
    return;
  mip = mp -> FirstItem;

  while(mip)
    {
      mip2 = mip -> NextItem;
      UnMakeItem(mip);
      mip = mip2;
    }
  FreeMem(mp,sizeof(struct Menu));
}

void UnMakeSubItem(mip)
struct XMenuItem *mip;
{
  if (mip == NULL)
    return;
  FreeMem(mip -> ItemFill,sizeof(struct IntuiText));
  FreeMem(mip, sizeof(struct XMenuItem));
}

int32 ScanForMaxWidth(Menu)
  struct Menu *Menu;
{
  register int32 width = 0;
  register struct XMenuItem *MenuItem;

  MenuItem = Menu -> FirstItem;

  while ( MenuItem ) {

    if ( MenuItem -> Width > width ) width = MenuItem -> Width;

    MenuItem = MenuItem -> NextItem;

  }

  return ( width );

}

SetMaximumWidth(Menu)
  struct Menu *Menu;
{
  register struct XMenuItem *MenuItem,*SubMenu;
  int32 MaxWidth;

  MaxWidth = ScanForMaxWidth ( Menu );

  MenuItem = Menu -> FirstItem;

  while ( MenuItem ) {

    SubMenu = MenuItem -> SubItem;

    while ( SubMenu ) {

      SubMenu -> LeftEdge = MaxWidth;
      SubMenu = SubMenu -> NextItem;

    }

    MenuItem -> Width = MaxWidth;

    MenuItem = MenuItem -> NextItem;

  }

}

void UnMakeItem(mip)
struct MenuItem *mip;
{
  register struct MenuItem *mip2,*mip3;

  if (mip == NULL)
    return;

  mip2 = mip -> SubItem;
  while(mip2)
    {
      mip3 = mip2 -> NextItem;
      UnMakeSubItem(mip2);
      mip2 = mip3;
    }

  FreeMem(mip -> ItemFill,sizeof(struct IntuiText));
  FreeMem(mip, sizeof(struct XMenuItem));
}

