/*
**	GadTools layout toolkit
**
**	Copyright © 1993-1996 by Olaf `Olsen' Barthel
**		Freely distributable.
**
**	:ts=4
*/

#ifndef _GTLAYOUT_GLOBAL_H
#include "gtlayout_global.h"
#endif

#ifdef DO_MENUS

	/* LTP_LayoutMenu(RootMenu *Root,WORD ExtraFront,WORD ExtraSpace):
	 *
	 *	Layout the menus, menu items and submenu items.
	 */

BOOL
LTP_LayoutMenu(RootMenu *Root,LONG ExtraFront,LONG ExtraSpace)
{
	LONG		MenuLeft,
				ItemTop,
				ItemWidth,
				ItemShift,
				CommandWidth,
				Width;

	MenuNode	*Menu;
	ItemNode	*Item,*FirstItem = NULL,*LastItem;

	BOOL		Correction;

		// That one's simple

	for(MenuLeft = ExtraFront, Menu = (MenuNode *)Root->MenuList.mlh_Head ; Menu->Node.mln_Succ ; Menu = (MenuNode *)Menu->Node.mln_Succ)
	{
		Menu->Menu.LeftEdge = MenuLeft;

		MenuLeft += Menu->Menu.Width + ExtraSpace;
	}

		// Now run down the list of items

	for(Item = (ItemNode *)Root->ItemList.mlh_Head ; Item->Node.mln_Succ ; Item = (ItemNode *)Item->Node.mln_Succ)
	{
			// Hit a submenu item?

		if(Item->Flags & ITEMF_FirstSub)
		{
			ItemNode	*FirstSub,*LastSub,*Sub;
			LONG		 SubTop			= 0,
						 SubWidth		= 0;
			LONG		 CommandWidth	= 0;

				// This is where we started

			FirstSub = Item;

				// Now determine the widest entry and
				// line them up below one another

			for(Sub = FirstSub ; Sub->Node.mln_Succ && (Sub->Flags & ITEMF_IsSub) ; Sub = (ItemNode *)Sub->Node.mln_Succ)
			{
				LastSub = Sub;

				if(Sub->Item.Width > SubWidth)
					SubWidth = Sub->Item.Width;

				if((Width = LTP_GetCommandWidth(Root,Sub)) > CommandWidth)
					CommandWidth = Width;

				Sub->Item.TopEdge = SubTop;

				SubTop += Sub->Item.Height;
			}

			SubWidth += 4 + CommandWidth;

				// In the second pass, make all entries
				// use the same width

			for(Sub = FirstSub ; ; Sub = (ItemNode *)Sub->Node.mln_Succ)
			{
					// Indent the entries that need it

				if(Sub->Item.Flags & CHECKIT)
					((struct IntuiText *)Sub->Item.ItemFill)->LeftEdge += 2 + Root->CheckWidth;

				Sub->Item.Width = SubWidth;

					// Adapt the separator bar size

				if(Sub->Flags & ITEMF_IsBar)
					((struct Image *)Sub->Item.ItemFill)->Width = Sub->Item.Width - 4;
				else
				{
						// Take care of command keys

					if(Sub->Flags & ITEMF_Command)
					{
						struct IntuiText *Command = ((struct IntuiText *)Sub->Item.ItemFill)->NextText;

						Command->LeftEdge = Sub->Item.Width - (IntuiTextLength(Command) + 2);
					}
				}

					// In the next iteration, continue after the last
					// submenu item

				if(Sub == LastSub)
				{
					Item = Sub;

					break;
				}
			}

			DB(kprintf("last sub |%s|\n",((struct IntuiText *)Item->Item.ItemFill)->IText));
		}
		else
		{
				// Start a new menu?

			if(!FirstItem)
			{
					// This is where we started

				FirstItem = Item;

					// Reset the data

				ItemTop = ItemWidth = ItemShift = CommandWidth = 0;
			}

				// Line up the entries in a column

			Item->Item.TopEdge = ItemTop;

			ItemTop += Item->Item.Height;

				// Search for the widest entry

			if(Item->Item.Width > ItemWidth)
				ItemWidth = Item->Item.Width;

				// Search for the widest command sequence

			if((Width = LTP_GetCommandWidth(Root,Item)) > CommandWidth)
				CommandWidth = Width;

				// This is for submenu items which will get
				// indented by this amount

			if(Item->Item.Width > ItemShift)
				ItemShift = Item->Item.Width;
		}

			// Is this the last item for this menu?

		if(Item->Flags & ITEMF_LastItem)
		{
			ItemWidth += 4 + CommandWidth;

				// Restart and layout all the items in this menu

			for(Item = FirstItem ; Item->Node.mln_Succ ; Item = (ItemNode *)Item->Node.mln_Succ)
			{
					// Indent submenu items

				if(Item->Flags & ITEMF_IsSub)
					Item->Item.LeftEdge += ItemShift + 6;
				else
				{
						// Indent items as necessary

					if(Item->Item.Flags & CHECKIT)
						((struct IntuiText *)Item->Item.ItemFill)->LeftEdge += 2 + Root->CheckWidth;

					Item->Item.Width = ItemWidth;

						// Adapt the separator bar size

					if(Item->Flags & ITEMF_IsBar)
						((struct Image *)Item->Item.ItemFill)->Width = Item->Item.Width - 4;
					else
					{
							// Take care of submenu item indicators and
							// command sequences

						if(Item->Flags & (ITEMF_Command | ITEMF_HasSub))
						{
							struct IntuiText *Command = ((struct IntuiText *)Item->Item.ItemFill)->NextText;

							Command->LeftEdge = Item->Item.Width - (IntuiTextLength(Command) + 2);
						}
					}
				}

					// Abort if this is the last item for this menu

				if(Item->Flags & ITEMF_LastItem)
					break;
			}

			DB(kprintf("last item |%s|\n",((struct IntuiText *)Item->Item.ItemFill)->IText));

				// The next iteration will start a new menu

			FirstItem = NULL;
		}
	}

		// Calculate the effective positions

	LTP_AdjustMenuPosition(Root);

	Correction = FALSE;

		// We start by chopping down the menus

	for(Item = (ItemNode *)Root->ItemList.mlh_Head ; Item->Node.mln_Succ ; Item = (ItemNode *)Item->Node.mln_Succ)
	{
			// That's where we start

		if(!FirstItem)
			FirstItem = Item;

			// Skip submenu items

		if(!(Item->Flags & ITEMF_IsSub))
			LastItem = Item;

			// Did we reach the end of the menu?

		if(Item->Flags & ITEMF_LastItem)
		{
			DB(kprintf("chopping |%s|->|%s|\n",((struct IntuiText *)FirstItem->Item.ItemFill)->IText,((struct IntuiText *)LastItem->Item.ItemFill)->IText));

				// Chop down the menus

			Correction |= LTP_CorrectItemList(Root,FirstItem,LastItem);

			FirstItem = NULL;
		}
	}

	if(Correction)
	{
		Correction = FALSE;

			// Recalculate the positions

		LTP_AdjustMenuPosition(Root);
	}

		// Next we shift the menus around to make them fit

	for(Menu = (MenuNode *)Root->MenuList.mlh_Head ; Menu->Node.mln_Succ ; Menu = (MenuNode *)Menu->Node.mln_Succ)
	{
		MenuLeft = Menu->Menu.LeftEdge + 4 + Menu->Width + 4;

			// Does it cross the right screen border?

		if(MenuLeft > Root->Screen->Width)
		{
				// Is the menu wider than the screen?

			if(4 + Menu->Width + 4 > Root->Screen->Width)
			{
				DB(kprintf("menu too wide\n"));
				return(FALSE);
			}
			else
			{
				LONG Left = MenuLeft - Root->Screen->Width + 1;

					// Move up

				for(Item = (ItemNode *)((ULONG)Menu->Menu.FirstItem - sizeof(struct MinNode)) ; Item->Node.mln_Succ ; Item = (ItemNode *)Item->Node.mln_Succ)
				{
					if(!(Item->Flags & ITEMF_IsSub))
						Item->Item.LeftEdge -= Left;

					if(Item->Flags & ITEMF_LastItem)
						break;
				}

				Correction = TRUE;
			}
		}
	}

	if(Correction)
	{
		Correction = FALSE;

			// Recalculate the positions

		LTP_AdjustMenuPosition(Root);
	}

		// Now deal with the submenus

	for(Item = (ItemNode *)Root->ItemList.mlh_Head ; Item->Node.mln_Succ ; Item = (ItemNode *)Item->Node.mln_Succ)
	{
			// Only submenu items, please

		if(Item->Flags & ITEMF_FirstSub)
		{
			ItemNode *Here;

				// Find the first and the last entry

			for(Here = Item ; Here->Node.mln_Succ ; Here = (ItemNode *)Here->Node.mln_Succ)
			{
				if(!(Here->Flags & ITEMF_IsSub))
					break;
				else
					LastItem = Here;
			}

			DB(kprintf("2) chopping |%s|->|%s|\n",((struct IntuiText *)Item->Item.ItemFill)->IText,((struct IntuiText *)LastItem->Item.ItemFill)->IText));

				// Chop down the submenus

			Correction |= LTP_CorrectItemList(Root,Item,LastItem);
		}
	}

	if(Correction)
	{
		Correction = FALSE;

		LTP_AdjustMenuPosition(Root);
	}

		// Almost finished, now shift the submenus around

	for(Item = (ItemNode *)Root->ItemList.mlh_Head ; Item->Node.mln_Succ ; Item = (ItemNode *)Item->Node.mln_Succ)
	{
			// Did we hit a submenu?

		if(Item->Flags & ITEMF_HasSub)
		{
			ItemNode *Here = (ItemNode *)((ULONG)Item->Item.SubItem - sizeof(struct MinNode));

				// Does this one also cross the right screen border?

			if((MenuLeft = Here->Left + 4 + Here->Item.Width + 4) > Root->Screen->Width)
			{
				LONG Left = MenuLeft - Root->Screen->Width;

					// Check if the subitem would cover too much
					// of the item it is attached to

				if(Here->Left - Left < (Item->Left + 4 + Item->Width - CommandWidth))
				{
					Left = Here->Left + (4 + Here->Item.Width + 4) - Item->Left - 5;

					if(Here->Left - Left < 0)
					{
						DB(kprintf("sub too fat\n"));
						return(FALSE);
					}
				}

				DB(kprintf("3) shifting...\n"));

					// Shift the menu around

				do
				{
					if(!(Here->Flags & ITEMF_IsSub))
						break;
					else
					{
						Here->Item.LeftEdge -= Left;
						Here->Left -= Left;

						Correction = TRUE;

						if(Here->Flags & ITEMF_LastItem)
							break;

						Here = (ItemNode *)Here->Node.mln_Succ;
					}
				}
				while(Here->Node.mln_Succ);
			}
		}
	}

		// The last step; see if the alignment stuff worked

	if(Correction)
		LTP_AdjustMenuPosition(Root);

		// First check the menus

	for(Menu = (MenuNode *)Root->MenuList.mlh_Head ; Menu->Node.mln_Succ ; Menu = (MenuNode *)Menu->Node.mln_Succ)
	{
			// Does it cross the screen borders?

		if((MenuLeft = Menu->Menu.LeftEdge + 4 + Menu->Width + 4) > Root->Screen->Width || Menu->Menu.LeftEdge < 0)
		{
			DB(kprintf("menu crosses screen border\n"));
			return(FALSE);
		}
	}

		// Now check the submenus

	for(Item = (ItemNode *)Root->ItemList.mlh_Head ; Item->Node.mln_Succ ; Item = (ItemNode *)Item->Node.mln_Succ)
	{
			// Did we hit a submenu?

		if(Item->Flags & ITEMF_HasSub)
		{
			ItemNode *Here = (ItemNode *)((ULONG)Item->Item.SubItem - sizeof(struct MinNode));

			if(Here->Left + Here->Width > Root->Screen->Width)
			{
				DB(kprintf("submenu crosses screen border\n"));
				return(FALSE);
			}
		}
	}

	LTP_FillMenu(&Root->Menu);

	return(TRUE);
}

#endif	/* DO_MENUS */
