/* easy menus: Copyright 1987 Peter da Silva, all rights reserved.
 *
 *	Permission is granted to use this in any application, so long as
 *	this notice is retained in the source. Permission is granted to
 *	modify the code as you like, so long as this notice (between the
 *	first line beginning "easy menus" and the end of this paragraph,
 *	inclusive) is retained intact.
 *
 * Usage:
 *
 *	#include "menu.h"
 *
 *	struct MenuPtr menudata;
 *	struct MenuPtr *menuptr = &menudata;
 *
 *	init_menus(menuptr);	/ * Just zero menu pointer out * /
 *
 *	for(each menu item) {
 *		add_menu(menuptr, menu, item, subitem, flags);
 *	}
 *
 *  Flags:
 *    SUBITEM_NOCHECK	-- subitem does not require a checkmark.
 *    SUBITEM_SELECTOR	-- subitem is a 1 of n selector, use mutual-exclude.
 *    SUBITEM_TOGGLE	-- subitem is a toggled flag.
 *    SUBITEM_SELECTED	-- defaults to checked.
 *
 *
 *	SetMenuStrip(yourwindow, menuptr->MenuBar);
 *
 *	...
 *
 *	ClearMenuStrip(yourwindow);
 *
 *	trash_menus(menuptr);
 *
 * Notes:
 *
 *	if you don't want any subitems, use zero for the subitem value.
 *
 *	subitem is always initialised as a CHECKIT item with all the other
 *	subitems mutually excluded.
 *
 *	it is intended that the menu be set up with all action items in
 *	the first level of the menu, and all toggles in the second level...
 *	this is a piece of blatant authoritarianism on my part. I've seen
 *	too many menus with no rhyme or reason. Look at AmigaTerm (the term
 *	program that comes with the Amiga modem) some time. Baud rate has
 *	an item all by itself, but word size is hidden off in a menu with
 *	things like bell sound.
 *
 *	the appearance of the menus produced by this is (in my humble
 *	opinion) good. I took some care making text centered in menu boxes,
 *	for example.
 */
#include <exec/memory.h>
#include <intuition/intuition.h>
#include "menu.h"
#include "fonts.h"

/*
struct MenuPtr {
	struct Menu *MenuBar;
	struct Remember *MenuMemory;
};
*/

char *AllocRemember();

static struct Menu *new_menu();
static struct MenuItem *new_item(), *new_subitem();

#define TOMENUNUM(i,j,k) (SHIFTMENU(i)|SHIFTITEM(j)|SHIFTSUB(k))
#define TextLen(s) (strlen(s)*FONTWIDTH)

trash_menus(menuptr)
struct MenuPtr *menuptr;
{
	FreeRemember(&menuptr->MenuMemory, 1);
	menuptr->MenuMemory = 0;
	menuptr->MenuBar = 0;
}

init_menus(menuptr)
struct MenuPtr *menuptr;
{
	menuptr->MenuMemory = 0;
	menuptr->MenuBar = 0;
}

int add_menu(menuptr, menuname, itemname, subitemname, flags)
struct MenuPtr *menuptr;
char *menuname, *itemname, *subitemname;
long flags;
{
	int i, j, k;
	struct Menu *menu;
	struct MenuItem *item;
	struct MenuItem *subitem;

	if(menuptr->MenuBar) {
		for(i = 0, menu = menuptr->MenuBar;
		    menu;
			menu = menu->NextMenu, i++
		   )
			if(strcmp(menuname, menu->MenuName)==0)
				break;
		if(!menu)
			menu = new_menu(menuptr, menuname);
		if(!menu)
			return MENUNULL;
	} else {
		i = 0;
		menu = new_menu(menuptr, menuname);
		if(!menu)
			return MENUNULL;
	}
	for(j = 0, item = menu->FirstItem;
		item;
		item = item->NextItem, j++
	   ) {
		struct IntuiText *text;
		text = (struct IntuiText *)item->ItemFill;
		if(strcmp(itemname, text->IText) == 0)
			break;
	}
	if(subitemname) {
		if(!item)
			item = new_item(menuptr, menu, itemname);
		if(!item)
			return MENUNULL;
		for(k = 0, subitem = item->SubItem;
			subitem;
			subitem = subitem->NextItem, k++
		   ) {
			struct IntuiText *text;
			text = (struct IntuiText *)subitem->ItemFill;
			if(strcmp(subitemname, text->IText) == 0)
				break;
		}
		if(!subitem)
			subitem = new_subitem(menuptr, item, subitemname, flags);
		if(!subitem)
			return MENUNULL;
		return TOMENUNUM(i, j, k);
	} else {
		if(!item)
			item = new_item(menuptr, menu, itemname);
		if(!item)
			return MENUNULL;
		return TOMENUNUM(i, j, NOSUB);
	}
}

static struct Menu *
new_menu(menuptr, name)
struct MenuPtr *menuptr;
char *name;
{
	struct Menu *menu;

	menu = (struct Menu *)AllocRemember(
		&menuptr->MenuMemory,
		sizeof(struct Menu),
		MEMF_PUBLIC);
	if(!menu)
		return 0;
	menu->NextMenu = NULL;
	menu->LeftEdge = 0;
	menu->TopEdge = 0;
	menu->Width = TextLen(name)+FONTWIDTH;
	menu->Height = 0;
	menu->Flags = MENUENABLED;
	menu->MenuName = name;
	menu->FirstItem = 0;
	if(menuptr->MenuBar) {
		struct Menu *ptr, *prev;
		for(ptr = menuptr->MenuBar; ptr; ptr=ptr->NextMenu) {
			menu->LeftEdge += ptr->Width;
			prev = ptr;
		}
		prev->NextMenu = menu;
	} else {
		menuptr->MenuBar = menu;
	}

	return menu;
}

static struct item *
new_item(menuptr, menu, name)
struct MenuPtr *menuptr;
char *name;
struct Menu *menu;
{
	struct MenuItem *item;
	struct IntuiText *text;

	item = (struct MenuItem *)AllocRemember(
		&menuptr->MenuMemory,
		sizeof(struct MenuItem),
		MEMF_PUBLIC);
	if(!item)
		return 0;
	text = (struct IntuiText *)AllocRemember(
		&menuptr->MenuMemory,
		sizeof(struct IntuiText),
		MEMF_PUBLIC);
	if(!text)
		return 0;

	text->FrontPen = AUTOFRONTPEN;
	text->BackPen = AUTOBACKPEN;
	text->DrawMode = JAM2;
	text->LeftEdge = 1;
	text->TopEdge = 1;
	text->ITextFont = NULL;
	text->IText = name;
	text->NextText = NULL;

	item->NextItem = NULL;
	item->LeftEdge = 0;
	item->TopEdge = 0;
	item->Width = IntuiTextLength(text)+2;
	if(item->Width <= menu->Width)
		item->Width = menu->Width+1;
	item->Height = FONTHEIGHT+1;
	item->Flags = ITEMTEXT|HIGHCOMP|ITEMENABLED;
	item->MutualExclude = 0;
	item->ItemFill = text;
	item->SelectFill = NULL;
	item->Command = 0;
	item->SubItem = NULL;
	item->NextSelect = NULL;

	if(menu->FirstItem) {
		struct MenuItem *ptr, *prev;
		for(ptr = menu->FirstItem; ptr; ptr=ptr->NextItem) {
			if(item->Width > ptr->Width) {
				if(ptr->SubItem)
					nudge(ptr->SubItem, item->Width-ptr->Width);
				ptr->Width = item->Width;
			} else if(ptr->Width>item->Width)
				item->Width = ptr->Width;
			prev = ptr;
		}
		item->TopEdge = prev->TopEdge + prev->Height;
		prev->NextItem = item;
	} else {
		menu->FirstItem = item;
	}

	return item;
}

static nudge(item, delta)
struct MenuItem *item;
int delta;
{
	while(item) {
		item->LeftEdge += delta;
		item = item->NextItem;
	}
}

static struct item *
new_subitem(menuptr, item, name, flags)
struct MenuPtr *menuptr;
char *name;
struct MenuItem *item;
long flags;
{
	struct MenuItem *subitem;
	struct IntuiText *text;

	subitem = (struct MenuItem *)AllocRemember(
		&menuptr->MenuMemory,
		sizeof(struct MenuItem),
		MEMF_PUBLIC);
	if(!subitem)
		return 0;
	text = (struct IntuiText *)AllocRemember(
		&menuptr->MenuMemory,
		sizeof(struct IntuiText),
		MEMF_PUBLIC);
	if(!text)
		return 0;

	text->FrontPen = AUTOFRONTPEN;
	text->BackPen = AUTOBACKPEN;
	text->DrawMode = JAM2;
	text->LeftEdge = CHECKWIDTH+1;
	text->TopEdge = 1;
	text->ITextFont = NULL;
	text->IText = name;
	text->NextText = NULL;

	subitem->NextItem = NULL;
	subitem->LeftEdge = item->Width;
	subitem->TopEdge = 0;
	subitem->Width = IntuiTextLength(text)+2;
	if(flags != SUBITEM_NOCHECK) subitem->Width += CHECKWIDTH;
	subitem->Height = FONTHEIGHT+1;
	subitem->Flags = ITEMTEXT|ITEMENABLED|HIGHCOMP;
	subitem->MutualExclude = 0;
	if(flags != SUBITEM_NOCHECK) {
		subitem->Flags |= CHECKIT;
		if(flags & SUBITEM_TOGGLE) subitem->Flags |= MENUTOGGLE;
		if(flags & SUBITEM_SELECTED) subitem->Flags |= CHECKED;
	}
	subitem->ItemFill = text;
	subitem->SelectFill = NULL;
	subitem->Command = 0;
	subitem->SubItem = NULL;
	subitem->NextSelect = NULL;

	if(item->SubItem) {
		struct MenuItem *ptr, *prev;
		int i;
		for(i=0, ptr = item->SubItem; ptr; i++, ptr=ptr->NextItem) {
			if(subitem->Width > ptr->Width)
				ptr->Width = subitem->Width;
			else if(ptr->Width>subitem->Width)
				subitem->Width = ptr->Width;
			prev = ptr;
		}
		subitem->TopEdge = prev->TopEdge + prev->Height;
		if(flags & SUBITEM_SELECTOR)
			subitem->MutualExclude = ~(1<<i);
		prev->NextItem = subitem;
	} else {
		item->SubItem = subitem;
		if(flags & SUBITEM_SELECTOR)
			subitem->MutualExclude = ~1;
	}

	return subitem;
}
