/*
 * Simple menu package.  Needs lotsa work to handle some cases.
 * 
 * Copyright 1985 Louis A. Mamakos Software & Stuff 14813 Ashford Place Laurel,
 * MD  20707
 * 
 * For non-commerical use only.  This program, or any modifications, may not be
 * sold or incorporated into any product without prior permission from the
 * author.
 * 
 * Modified by mwm to handle "stacking" menus. NB - adding item to a menu that's
 * been "popped" back to doesn't work, and probably never will. Modified
 * again by MPK to allow subitems again (non-stacking), and fix bug when
 * visiting files not in last menu.
 * 
 * Modified once again by MPK to avoid calling strsave() if desired.  This is
 * controlled by the newly-added last parameter to *_Add(), which indicates
 * whether the menu name should be saved off by strsave(). I suppose *not*
 * saving strings might break on a system where MEMF_PUBLIC meant something,
 * but I think that's far in the future...
 */

#include "do_menu.h"
#ifdef	DO_MENU

#undef	LATTICE
#undef	MANX
#undef AZTEC
#include "compiler.h"

#include <exec/types.h>
#include <exec/nodes.h>
#include <exec/lists.h>
#include <exec/ports.h>
#include <exec/devices.h>
#include <exec/memory.h>
#include <hardware/blit.h>
#include <graphics/copper.h>
#include <graphics/regions.h>
#include <graphics/rastport.h>
#include <graphics/gfxbase.h>
#include <graphics/gels.h>
#include <intuition/intuition.h>
#ifdef LATTICE
#include <proto/all.h>
#else
#include <functions.h>
#endif

#undef TRUE
#undef FALSE
#include "def.h"

#ifdef ANSI
#include <string.h>
#endif

#define MNUM(menu,item,sub) (SHIFTMENU(menu)|SHIFTITEM(item)|SHIFTSUB(sub))
#define	Menu_Clear	DisposeMenus	/* For ttyio.c	 */

struct Mem_Node {
	struct Node     mn_Node;
	struct Remember *mn_Memory;
	struct Menu    *mn_Menu;
}              *Top;

extern struct Screen WBInfo;	/* For Screen width & Height	 */
#define SCREENHEIGHT	(WBInfo . Height)
#define SCREENWIDTH	(WBInfo . Width)

static struct List Memory;
static int      Cur_Menu, Cur_MenuItem, Cur_SubItem;
static struct Menu *LastMenu;
static struct MenuItem *LastMenuItem, *LastSubItem;

struct Menu    *AutoMenu;	/* menu struct being dynamically built */

#ifdef	MANX
char           *strsave();	/* Save a string in the remember list */
#else
#define strsave(s) strdup(s)
#ifdef AZTEC
char *strdup PROTO((char *));
#endif
#endif

VOID
Menu_Init()
{
	Memory.lh_Head = (struct Node *) & (Memory.lh_Tail);
	Memory.lh_TailPred = (struct Node *) & (Memory.lh_Head);
	Memory.lh_Tail = NULL;
	Memory.lh_Type = NT_MEMORY;
	Top = NULL;
	Cur_Menu = Cur_MenuItem = Cur_SubItem = -1;
	AutoMenu = LastMenu = NULL;	/* no menu chain yet */
	LastMenuItem = LastSubItem = NULL;
}

VOID
Menu_Clear()
{

	while ((Top = (struct Mem_Node *) RemHead(&Memory)) != NULL) {
		FreeRemember(&(Top->mn_Memory), (LONG) TRUE);
		FreeMem(Top, (LONG) sizeof(struct Mem_Node));
	}
	Menu_Init();		/* Just for safeties sake */
}

VOID
Menu_Pop()
{

	if ((Top = (struct Mem_Node *) RemHead(&Memory)) == NULL)
		return;
	FreeRemember(&(Top->mn_Memory), (LONG) TRUE);
	FreeMem(Top, (LONG) sizeof(struct Mem_Node));
	/* Now, set Top back to the real list head */
	Top = (struct Mem_Node *) Memory.lh_Head;
	LastMenu = Top->mn_Menu;
	LastMenu->NextMenu = NULL;	/* Tie off the menu list */
	LastMenuItem = NULL;	/* Wrong, but you can't add items here anyway */
	LastSubItem = NULL;	/* ditto				      */
	Cur_Menu--;
}

/*
 * Add a MENU item.  Args are the text of the menu item, and an enable flag.
 * Returns an Intuition type menu number, with the MenuItem and Menu SubItem
 * being NOITEM and NOSUB.  The MENUITEM part is valid.
 */
/* dummy Intuitext used to calculate length of menu names */
static struct IntuiText itd = {
	AUTOFRONTPEN, AUTOBACKPEN, JAM2, 1, 1, NULL, NULL, NULL
};

int
Menu_Add(name, enabled, saveit)
	char           *name;
	int             enabled;
	int             saveit;
{
	register struct Menu *m;

	if (!name)
		return 0;
	if ((Top = (struct Mem_Node *) AllocMem(
						(LONG) sizeof(struct Mem_Node), (LONG) MEMF_PUBLIC | MEMF_CLEAR)) == NULL)
		return 0;
	Top->mn_Node.ln_Type = NT_MEMORY;

	if ((m = (struct Menu *) AllocRemember(&(Top->mn_Memory),
	     (LONG) sizeof(struct Menu), MEMF_PUBLIC | MEMF_CLEAR)) == NULL)
		return 0;
	Top->mn_Menu = m;

	if (LastMenu == NULL)
		AutoMenu = m;	/* first menu on list */
	else
		LastMenu->NextMenu = m;	/* link it in */

	LastMenuItem = NULL;	/* end of previous MenuItem list */
	LastSubItem = NULL;
	Cur_MenuItem = Cur_SubItem = -1;	/* reset item numbers */
	if (LastMenu == NULL)
		m->LeftEdge = 0;
	else
		m->LeftEdge = LastMenu->LeftEdge + LastMenu->Width;
	m->TopEdge = 0;
	itd.IText = (UBYTE *) name;
	m->Width = IntuiTextLength(&itd);
	Top->mn_Node.ln_Name = m->MenuName = saveit ? strsave(name) : name;
	m->Height = 0;
	m->Flags = enabled ? MENUENABLED : 0;
	m->FirstItem = NULL;
	LastMenu = m;

	AddHead(&Memory, (struct Node *) Top);
	return MNUM(++Cur_Menu, NOITEM, NOSUB);
}

/*
 * Add a menu item to the current MENU.  Note that Add_Menu *must* be called
 * before this function.
 */
int
Menu_Item_Add(name, flags, mux, ch, saveit)
	char           *name;	/* name of menu item */
	unsigned int	flags;
	LONG            mux;	/* mutual exclusion mask */
	int		ch;	/* command sequence character, if COMMSEQ */
	int             saveit;	/* save name using strsave? */
{
	register struct MenuItem *m, *n;
	register struct IntuiText *it;

	if (!name)
		return 0;
	flags &= CHECKIT | CHECKED | COMMSEQ | MENUTOGGLE | ITEMENABLED | HIGHCOMP | HIGHBOX;
	if (LastMenu == NULL)
		return MNUM(NOMENU, NOITEM, NOSUB);

	if ((m = (struct MenuItem *) AllocRemember(&(Top->mn_Memory),
						   (LONG) sizeof(struct MenuItem), (LONG) MEMF_PUBLIC | MEMF_CLEAR)) == NULL)
		return MNUM(NOMENU, NOITEM, NOSUB);

	LastSubItem = NULL;	/* terminate possible list of subitems */
	Cur_SubItem = -1;
	if (LastMenuItem == NULL)
		LastMenu->FirstItem = m;
	else
		LastMenuItem->NextItem = m;
	m->Flags = flags | ITEMTEXT;
	/*
	 * Check for highlight mode:  if none selected, use HIGHCOMP
	 */
	if ((m->Flags & (HIGHCOMP | HIGHBOX)) == 0)
		m->Flags |= HIGHCOMP;
	m->Command = ch;
	m->MutualExclude = mux;
	m->SubItem = NULL;
	m->ItemFill = (APTR) AllocRemember(&(Top->mn_Memory),
	  (LONG) sizeof(struct IntuiText), (LONG) MEMF_PUBLIC | MEMF_CLEAR);
	it = (struct IntuiText *) m->ItemFill;
	it->FrontPen = AUTOFRONTPEN;
	it->BackPen = AUTOBACKPEN;
	it->DrawMode = JAM2;
	if (flags & CHECKIT)
		it->LeftEdge = CHECKWIDTH + 1;
	else
		it->LeftEdge = 1;
	it->TopEdge = 1;
	it->ITextFont = NULL;	/* default font */
	it->IText = (UBYTE *) (saveit ? strsave(name) : name);
	it->NextText = NULL;
	if (LastMenuItem == NULL) {
		m->TopEdge = 2;
		m->LeftEdge = 0;
	} else if (LastMenuItem->TopEdge + 40 > SCREENHEIGHT) {
		m->TopEdge = 2;
		m->LeftEdge = LastMenuItem->LeftEdge + LastMenuItem->Width + 12;
		if (m->LeftEdge > SCREENWIDTH) {
			LastMenuItem->NextItem = NULL;
			LastMenuItem->Flags &= ~ITEMENABLED;
			return MNUM(NOMENU, NOITEM, NOSUB);
		}
	} else {
		m->TopEdge = LastMenuItem->TopEdge + 10;
		m->LeftEdge = LastMenuItem->LeftEdge;
	}
	m->Width = 0;
	if (flags & CHECKIT)
		m->Width += CHECKWIDTH;
	if (flags & COMMSEQ)
		m->Width += COMMWIDTH + 20;
	m->Width += IntuiTextLength((struct IntuiText *) m->ItemFill);
	m->Height = 10;
	/*
	 * Check last menu item's width to see if it is larger than this
	 * item's.  If new item is larger, then update width of all other
	 * items.
	 */
	if (LastMenuItem) {
		if (LastMenuItem->Width > m->Width)
			m->Width = LastMenuItem->Width;
		else {
			register short  delta = m->Width - LastMenuItem->Width;

			for (n = LastMenu->FirstItem; n != m; n = n->NextItem) {
				n->Width = m->Width;
				if (n->LeftEdge > 0)
					n->LeftEdge += delta;
			}
			if (m->LeftEdge > 0)
				m->LeftEdge += delta;
		}
	}
	LastMenuItem = m;
	return MNUM(Cur_Menu, ++Cur_MenuItem, NOSUB);
}

int
Menu_SubItem_Add(name, flags, mux, ch, saveit)
	char           *name;
	unsigned int	flags;
	LONG            mux;
	int		ch;
	int             saveit;
{
	register struct MenuItem *m, *n;
	register struct IntuiText *it;

	if (!name)
		return 0;
	flags &= CHECKIT | CHECKED | COMMSEQ | MENUTOGGLE | ITEMENABLED | HIGHCOMP | HIGHBOX;
	if (LastMenuItem == NULL)
		return MNUM(NOMENU, NOITEM, NOSUB);

	if ((m = (struct MenuItem *) AllocRemember(&(Top->mn_Memory),
						   (LONG) sizeof(struct MenuItem), (LONG) MEMF_PUBLIC | MEMF_CLEAR)) == NULL)
		return MNUM(NOMENU, NOITEM, NOSUB);

	if (LastSubItem == NULL)
		LastMenuItem->SubItem = m;
	else
		LastSubItem->NextItem = m;
	m->Flags = flags | ITEMTEXT;
	/*
	 * check for highlight mode.  If none selected, use HIGHCOMP
	 */
	if ((m->Flags & (HIGHCOMP | HIGHBOX)) == 0)
		m->Flags |= HIGHCOMP;
	m->Command = ch;
	m->MutualExclude = mux;
	m->SubItem = NULL;
	m->ItemFill = (APTR) AllocRemember(&(Top->mn_Memory),
	  (LONG) sizeof(struct IntuiText), (LONG) MEMF_PUBLIC | MEMF_CLEAR);
	it = (struct IntuiText *) m->ItemFill;
	it->FrontPen = AUTOFRONTPEN;
	it->BackPen = AUTOBACKPEN;
	it->DrawMode = JAM2;
	if (flags & CHECKIT)
		it->LeftEdge = CHECKWIDTH + 1;
	else
		it->LeftEdge = 1;
	it->TopEdge = 1;
	it->ITextFont = NULL;	/* default font */
	it->IText = (UBYTE *) (saveit ? strsave(name) : name);
	it->NextText = NULL;
	m->LeftEdge = LastMenuItem->Width + 10;
	m->Width = 0;
	if (LastSubItem == NULL)
		m->TopEdge = 1;
	else
		m->TopEdge = LastSubItem->TopEdge + 10;
	if (flags & CHECKIT)
		m->Width += CHECKWIDTH;
	if (flags & COMMSEQ)
		m->Width += COMMWIDTH + 20;
	m->Width += IntuiTextLength((struct IntuiText *) m->ItemFill);
	m->Height = 10;
	/*
	 * Check last menu item's width to see if it is larger than this
	 * item's.  If new item is larger, then update width of all other
	 * items.
	 */
	if (LastSubItem) {
		if (LastSubItem->Width > m->Width)
			m->Width = LastSubItem->Width;
		else
			for (n = LastMenuItem->SubItem; n != m; n = n->NextItem)
				n->Width = m->Width;
	}
	LastSubItem = m;
	return MNUM(Cur_Menu, Cur_MenuItem, ++Cur_SubItem);
}

#ifdef	MANX
char           *
strsave(string)
	char           *string;
{
	char           *out;

	out = (char *) AllocRemember(&(Top->mn_Memory), (LONG) (strlen(string) + 1),
				     (LONG) MEMF_PUBLIC);
	if (out == NULL)
		return NULL;

	(void) strcpy(out, string);
	return out;
}
#endif
#else
#include "nullfile.h"
#endif
