/* Modulo contenente le funzioni di gestione dei menu.                  */
/*                                                                      */
/*                                                                      */
/* Il modulo viene compilato correttamente da Aztec C 5.0d con le       */
/* seguenti opzioni:                                                    */
/*                                                                      */
/*  cc menudemo -pps -so                                                */
/*                                                                      */
/* Non ho provato SAS, ma non dovrebbero essere necessarie modifiche.   */
/*                                                                      */
/* Per la compilazione sono INDISPENSABILI i file include versione 2.0, */
/* inoltre il modulo va linkato con amiga.lib versione 2.0              */

/* NOTA IMPORTANTISSIMA: perche` le funzioni funzionino correttamente,  */
/* e` necessario che il programma principale apra le librerie graphics, */
/* intuition e, sotto kickstart 2.0, gadtools (vedere menudemo.c)       */


#include <string.h>
#include <exec/memory.h>
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <utility/tagitem.h>
#include <libraries/gadtools.h>
#ifdef AZTEC_C
#include <functions.h>
#else
#include <proto/exec.h>
#include <proto/intuition.h>
#include <proto/graphics.h>
#include <proto/gadtools.h>
#endif

#include "menuhandl.h"


LONG calcmenuwidth(struct NewMenu *newmenu,struct Screen *scr);
LONG disposeitem(struct NewMenu *newmenu,struct MenuItem *item,struct Screen *scr);
void freeitem(struct MenuItem *item);



/* Questa funzione alloca dinamicamente una menustrip. Al termine, andra` */
/* liberata usando FreeMenuStrip() */
/* Si forniscono in input un'array di strutture NewMenu che descrivono la */
/* disposizione dei menu, e il puntatore alla finestra che dovra` contenere */
/* il menu. La finestra viene usata per risalire allo schermo, e determinarne */
/* dimensioni e font. */
struct Menu *CreateMenuStrip(struct NewMenu *newmenu,struct Window *win)
{
SHORT left;
struct Menu *menustrip;
struct Menu *men;
struct RastPort *srp;
extern struct IntuitionBase *IntuitionBase;

if (IntuitionBase->LibNode.lib_Version >= 36)
{
	APTR vi;

	if (vi = GetVisualInfo(win->WScreen,TAG_DONE))
	{
		if (menustrip = CreateMenus(newmenu,TAG_DONE))
		{
			if (!LayoutMenus(menustrip,vi,TAG_DONE))
			{
				FreeMenus(menustrip);
				menustrip = 0;
			}
		}
		FreeVisualInfo(vi);
	}

	return(menustrip);
}

srp = &win->WScreen->RastPort;

if (!(menustrip = AllocMem(sizeof(struct Menu) + sizeof(APTR),MEMF_PUBLIC | MEMF_CLEAR)))
	goto fail;
men = menustrip;

left = 2;

do
{
	men->LeftEdge = left;
	men->Width = TextLength(srp,newmenu->nm_Label,strlen(newmenu->nm_Label)) + 2;
	if (win->WScreen->ViewPort.Modes & HIRES) men->Width += 6;
	left += men->Width + srp->TxWidth;

	men->Flags = newmenu->nm_Flags ^ MENUENABLED;
	men->MenuName = newmenu->nm_Label;
	GTMENU_USERDATA(men) = newmenu->nm_UserData;

	newmenu++;

	if (newmenu->nm_Type == NM_ITEM)
	{
		SHORT currtop,menuwidth,menuleft;
		struct MenuItem *ite;

		if (!(men->FirstItem = AllocMem(sizeof(struct MenuItem) + sizeof(APTR),MEMF_PUBLIC | MEMF_CLEAR)))
			goto fail;

		ite = men->FirstItem;
		currtop = 0;
		menuwidth = calcmenuwidth(newmenu,win->WScreen);
		menuleft = (men->LeftEdge + menuwidth > win->WScreen->Width - 9) ?
				win->WScreen->Width - men->LeftEdge - menuwidth - 9 : 0;

		do
		{
			ite->Width = menuwidth;
			ite->LeftEdge = menuleft;
			ite->TopEdge = currtop;
			GTMENUITEM_USERDATA(ite) = newmenu->nm_UserData;
			if (!disposeitem(newmenu,ite,win->WScreen))
				goto fail;

			currtop += ite->Height;

			newmenu++;

			if (newmenu->nm_Type == NM_SUB)
			{
				SHORT currsubtop,subwidth,subleft;
				struct MenuItem *sub;

				if (!(ite->SubItem = AllocMem(sizeof(struct MenuItem) + sizeof(APTR),MEMF_PUBLIC | MEMF_CLEAR)))
					goto fail;

				sub = ite->SubItem;
				currsubtop = -1;
				subwidth = calcmenuwidth(newmenu,win->WScreen);
				if (subwidth <= menuwidth / 4) subwidth = menuwidth / 4 + 1;
				subleft = ite->Width - ite->Width / 4;
				if (men->LeftEdge + menuleft + subleft + subwidth > win->WScreen->Width - 9)
						subleft = win->WScreen->Width - men->LeftEdge - menuleft - subwidth - 9;

				do
				{
					sub->Width = subwidth;
					sub->LeftEdge = subleft;
					sub->TopEdge = currsubtop;
					GTMENUITEM_USERDATA(sub) = newmenu->nm_UserData;
					if (!disposeitem(newmenu,sub,win->WScreen))
						goto fail;

					currsubtop += ite->Height;

					newmenu++;

					if (newmenu->nm_Type == NM_SUB)
					{
						if (!(sub->NextItem = AllocMem(sizeof(struct MenuItem) + sizeof(APTR),MEMF_PUBLIC | MEMF_CLEAR)))
							goto fail;
					}
					sub = sub->NextItem;
				} while (sub);
			}

			if (newmenu->nm_Type == NM_ITEM)
			{
				if (!(ite->NextItem = AllocMem(sizeof(struct MenuItem) + sizeof(APTR),MEMF_PUBLIC | MEMF_CLEAR)))
					goto fail;
			}
			ite = ite->NextItem;
		} while (ite);
	}

	if (newmenu->nm_Type == NM_TITLE)
	{
		if (!(men->NextMenu = AllocMem(sizeof(struct Menu) + sizeof(APTR),MEMF_PUBLIC | MEMF_CLEAR)))
			goto fail;
	}
	men = men->NextMenu;
} while (men);


return(menustrip);


fail:
FreeMenuStrip(menustrip);
return(0);
}



/* funzione privata */
LONG calcmenuwidth(struct NewMenu *newmenu,struct Screen *scr)
{
UBYTE type;
SHORT maxlen,thislen,maxseq,thisseq;
SHORT check,comm;
struct RastPort *srp;

srp = &scr->RastPort;

if (scr->ViewPort.Modes & HIRES)
{
	check = CHECKWIDTH;
	comm = COMMWIDTH;
}
else
{
	check = LOWCHECKWIDTH;
	comm = LOWCOMMWIDTH;
}

maxlen = maxseq = 2;
type = newmenu->nm_Type;

/* for menus scan whole list, for submenus only sublist */
while (newmenu->nm_Type == NM_SUB || newmenu->nm_Type == type)
{
	if (newmenu->nm_Type == type && newmenu->nm_Label != NM_BARLABEL)
	{
		thislen = TextLength(srp,newmenu->nm_Label,strlen(newmenu->nm_Label));
		if (newmenu->nm_Flags & CHECKIT) thislen += check;
		if (newmenu->nm_CommKey)
		{
			thisseq = comm + TextLength(srp,newmenu->nm_CommKey,1) + srp->TxWidth;
			if (thisseq > maxseq) maxseq = thisseq;
		}
		if (newmenu->nm_Type == NM_ITEM && (newmenu + 1)->nm_Type == NM_SUB)
		{
			thisseq = TextLength(srp,"»",1) + srp->TxWidth;
			if (thisseq > maxseq) maxseq = thisseq;
		}

		if (thislen > maxlen) maxlen = thislen;
	}

	newmenu++;
}

if (maxseq) maxlen += maxseq;

return(maxlen + 3);
}



/* funzione privata */
LONG disposeitem(struct NewMenu *newmenu,struct MenuItem *item,struct Screen *scr)
{
struct RastPort *srp;

srp = &scr->RastPort;

if (newmenu->nm_Label == NM_BARLABEL)
{
	struct Image *im;

	if (!(im = AllocMem(sizeof(struct Image),MEMF_PUBLIC | MEMF_CLEAR)))
		return(0);

	item->ItemFill = (APTR)im;
	item->Height = 6;

	im->LeftEdge = im->TopEdge = im->Height = 2;
	im->Width = item->Width - 4;
}
else
{
	struct IntuiText *tx;

	if (!(tx = AllocMem(sizeof(struct IntuiText),MEMF_PUBLIC | MEMF_CLEAR)))
		return(0);

	item->ItemFill = (APTR)tx;
	item->Height = srp->TxHeight + 1;
	if (item->Height < 9) item->Height = 9;
	item->Flags = ((newmenu->nm_Flags & NM_FLAGMASK) ^ ITEMENABLED) | ITEMTEXT | HIGHCOMP;
	item->MutualExclude = newmenu->nm_MutualExclude;

	if (newmenu->nm_CommKey)
	{
		item->Flags |= COMMSEQ;
		item->Command = newmenu->nm_CommKey[0];
	}


	tx->FrontPen = 0;
	tx->BackPen = 1;
	tx->DrawMode = JAM1;
	tx->LeftEdge = 2;
	if (item->Flags & CHECKIT)
	{
		if (scr->ViewPort.Modes & HIRES) tx->LeftEdge += CHECKWIDTH;
		else tx->LeftEdge += LOWCHECKWIDTH;
	}
	tx->TopEdge = 1;
	tx->ITextFont = scr->Font;
	tx->IText = newmenu->nm_Label;

	if (newmenu->nm_Type == NM_ITEM && (newmenu + 1)->nm_Type == NM_SUB)
	{
		if (!(tx->NextText = AllocMem(sizeof(struct IntuiText),MEMF_PUBLIC | MEMF_CLEAR)))
			return(0);

		tx = tx->NextText;

		tx->FrontPen = 0;
		tx->BackPen = 1;
		tx->DrawMode = JAM1;
		tx->LeftEdge = item->Width - TextLength(srp,"»",1) - 2;
		tx->TopEdge = 1;
		tx->ITextFont = scr->Font;
		tx->IText = "»";
	}
}

return(1);
}



/* Questa funzione libera una menustrip precedentemente allocata con CreateMenuStrip(). */
void FreeMenuStrip(struct Menu *menu)
{
struct Menu *nextmenu;
struct MenuItem *item,*nextitem;
struct MenuItem *sub,*nextsub;
extern struct IntuitionBase *IntuitionBase;

if (IntuitionBase->LibNode.lib_Version >= 36)
{
	FreeMenus(menu);
	return;
}

while (menu)
{
	item = menu->FirstItem;

	while (item)
	{
		sub = item->SubItem;
		while (sub)
		{
			nextsub = sub->NextItem;
			freeitem(sub);
			sub = nextsub;
		}

		nextitem = item->NextItem;
		freeitem(item);
		item = nextitem;
	}

	nextmenu = menu->NextMenu;
	FreeMem(menu,sizeof(struct Menu) + sizeof(APTR));
	menu = nextmenu;
}
}



/* funzione privata */
void freeitem(struct MenuItem *item)
{
struct IntuiText *it,*nextit;

if (item->ItemFill)
{
	if (item->Flags & ITEMTEXT)
	{
		it = (struct IntuiText *)item->ItemFill;

		while (it)
		{
			nextit = it->NextText;
			FreeMem(it,sizeof(struct IntuiText));
			it = nextit;
		}
	}
	else FreeMem(item->ItemFill,sizeof(struct Image));
}

FreeMem(item,sizeof(struct MenuItem) + sizeof(APTR));
}




/* Questa funzione ritorna il puntatore ad un menu. */
/* In ingresso si danno il puntatore alla menustrip ritornato da CreateMenuStrip() */
/* e l'identificativo dell'item specificato nella struttura NewMenu. */
/* NOTA: questa funzione va usata SOLO per cercare un menu. Per cercare un item */
/* va usata FindItem() */
struct Menu *FindMenu(struct Menu *menu,APTR id)
{
while (menu)
{
	if (GTMENU_USERDATA(menu) == id) return(menu);

	menu = menu->NextMenu;
}

return(NULL);
}



/* Questa funzione ritorna il puntatore ad un item. */
/* In ingresso si danno il puntatore alla menustrip ritornato da CreateMenuStrip() */
/* e l'identificativo dell'item specificato nella struttura NewMenu. */
struct MenuItem *FindItem(struct Menu *menu,APTR id)
{
struct MenuItem *item,*sub;

while (menu)
{
	item = menu->FirstItem;
	while (item)
	{
		if (GTMENUITEM_USERDATA(item) == id) return(item);

		sub = item->SubItem;
		while (sub)
		{
			if (GTMENUITEM_USERDATA(sub) == id) return(sub);
			sub = sub->NextItem;
		}

		item = item->NextItem;
	}

	menu = menu->NextMenu;
}

return(NULL);
}



/* Questa funzione ritorna il codice usato da Intuition per indicare un item. */
/* In ingresso si danno il puntatore alla menustrip ritornato da CreateMenuStrip() */
/* e l'identificativo dell'item specificato nella struttura NewMenu. */
USHORT FindMenuNum(struct Menu *menu,APTR id)
{
struct MenuItem *item,*sub;
USHORT mnum,inum,snum;

mnum = 0;

while (menu)
{
	if (GTMENU_USERDATA(menu) == id) return(FULLMENUNUM(mnum,NOITEM,NOSUB));

	item = menu->FirstItem;
	inum = 0;
	while (item)
	{
		if (GTMENUITEM_USERDATA(item) == id) return(FULLMENUNUM(mnum,inum,NOSUB));

		sub = item->SubItem;
		snum = 0;
		while (sub)
		{
			if (GTMENUITEM_USERDATA(sub) == id) return(FULLMENUNUM(mnum,inum,snum));
			sub = sub->NextItem;
			snum++;
		}

		item = item->NextItem;
		inum++;
	}

	menu = menu->NextMenu;
	mnum++;
}

return(MENUNULL);
}



/* Questa funzione ritorna un booleano, indicando se un item e` marcato o no. */
/* Gli input sono il puntatore alla menustrip ritornato da CreateMenuStrip() */
/* e l'identificativo dell'item come specificato nella struttura NewMenu */
LONG IsChecked(struct Menu *menu,APTR id)
{
struct MenuItem *item;

if (item = FindItem(menu,id)) return((item->Flags & CHECKED) != 0);
else return(0);
}



/* Questa funzione serve per accendere/spengere il checkmark accanto ad un item. */
/* Si passano il puntatore alla menustrip ritornato da CreateMenuStrip(), */
/* l'identificativo dell'item come specificato nella struttura NewMenu, e un */
/* valore booleano che indica se accendere o spengere il checkmark. */
/**/
/* NOTA IMPORTANTE: Prima di chiamare la funzione e` necessario usare */
/* ClearMenuStrip(), e al termine SetMenuStrip(). Cio` non viene fatto da */
/* questa funzione per renderla piu` efficiente nel caso la si esegua su */
/* piu` item consecutivamente. In tal caso, ClearMenuStrip() e SetMenuStrip() */
/* possono essere chiamate solo una volta. */
void CheckItem(struct Menu *menu,APTR id,SHORT checkit)
{
struct MenuItem *item;

if (item = FindItem(menu,id))
{
	if (checkit) item->Flags |= CHECKED;
	else item->Flags &= ~CHECKED;
}
}
