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

#include "gtlayout_global.h"

#ifdef DO_MENUS

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

BOOLEAN __regargs
LTP_LayoutMenu(RootMenu *Root,WORD ExtraFront,WORD ExtraSpace)
{
	WORD		MenuLeft,
				ItemTop,
				ItemWidth,
				ItemShift,
				CommandWidth,
				Width;

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

	BOOLEAN		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;
			WORD		 SubTop			= 0,
						 SubWidth		= 0;
			WORD		 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("too wide\n"));
				return(FALSE);
			}
			else
			{
				WORD 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)
			{
				WORD Left = MenuLeft - Root -> Screen -> Width;

					// This shouldn't be wider than the screen, but...

				if(Here -> Left - Left < Item -> Left + 10)
				{
					DB(kprintf("sub too wide\n"));
					return(FALSE);
				}
				else
				{
					DB(kprintf("3) shifting...\n"));

						// Shift the menu around

					do
					{
						if(!(Here -> Flags & ITEMF_IsSub))
							break;
						else
						{
							Here -> Item . LeftEdge -= 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)
			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)
				return(FALSE);
		}
	}

	LTP_FillMenu(&Root -> Menu);

	return(TRUE);
}

#endif	/* DO_MENUS */
