/*  menu.c   */
/*  Copyright 1989 Mountain Math Software  */
/*  All Rights Reserved                    */
#include <string.h>
#include <stream.h>

#include <ctype.h>
#include "xdrv.h"
#include <InterViews/leave-scope.h>
#include <Dispatch/leave-scope.h>


#include "cgidbg.h"
#include "baseio.h"
#include "outtok.h"
#include "usercom.h"
#include "help.h"

#include "menu.h"
#include "menucmd.h"
#include "menuspc.h"
#include "grphio.h"
#include "dogrpmen.h"
#include "menstck.h"
#include "dsp_app.h"

AllMenus * TheCurrentMenus ;

static MenuLine Generic[] ={
	{"return","Return to previous level",MenuTypeInvalid,0},
	{"main","Return to top level",MenuTypeInvalid,0},
	{"help","Help",MenuTypeInvalid,0},
	{0,0,MenuTypeInvalid}
};

static MenuLine GenericMainCommandLines[] ={
	{"return","Return to command mode",MenuTypeInvalid,0},
	{"help","Help",MenuTypeInvalid,0},
	{0}
};


static const int NumGenericActions = 3 ;

static DoMenuReturn GenericAction[] = {DoMenuPrevious,DoMenuMain,DoMenuHelp};

struct GenericOptions GenericNormal =
	{ Generic, GenericAction, NumGenericActions };

struct GenericOptions GenericMain = { Generic+NumGenericActions-1,
		GenericAction+2,NumGenericActions-2} ;

struct GenericOptions GenericMainCommand = {GenericMainCommandLines,
		GenericAction+1,NumGenericActions-1} ;

DoMenuReturn Menu::DoGraphicsMenuItem(const char * Buf)
{
	// LogOut << "Menu::DoGraphicsMenuItem(" << Buf << ")\n" ;
	return MenuDriver(*this,"Graphics Menu",GenericNormal,Buf);
}


static int GetNumber(char * Name, Menu& ThisMenu,int limit,GenericOptions& Gen)
{
	int Ret;
	if (Ret=ThisMenu.FindName(Name)) return Ret;
	int i=0 ;
	const char * nm ;
	while (nm = Gen.Lines[i++].Command) if (!strcmp(nm,Name))
		return limit-Gen.NumActions+i -1 ;
	// Check for command from any menu
	if (!TheCurrentMenus->TestAndDo(Name)) {
		// LogMsg("Writting error messages: is not a unique ..");
		*Output + OutputHelp <<
			"`" << Name << "' is not a uniqe menu option.\n" ;
		ReturnToContinue(OutputHelp) ;
	}
	return 0;
}

DoMenuReturn DoMenuItem(Menu& ThisMenu, const char * GetHere, int limit,
GenericOptions& Gen, const char * InputLine=0)
{
	NameOrNumber Input ;
	// LogOut << "DoMenuItem\n" ;
	// if (InputLine) LogOut << "InputLine is " << InputLine << "\n" ;

	if (InputLine) Input = GetNameOrNumber(InputLine) ;
	else Input = GetNameOrNumber(InputMenu) ;
	// LogOut << "After get name or number\n" ;
	int Number = Input.Number ;
	if (Input.Name) {
		if (!strlen(Input.Name)) return DoMenuPrevious ;
		Number = GetNumber(Input.Name,ThisMenu,limit,Gen);
		delete Input.Name ;
		if (!Number) return DoMenuCurrent ;
	}
	if (Number < 1 || Number >= limit) {
		*Output + OutputHelp << "Number selection " << Number <<
			" is invalid.\n" <<
			"Value must be > 0 and < " << limit << ".\n" ;
		ReturnToContinue(OutputHelp) ;
		return DoMenuCurrent ;
	}
	if (!GraphicsMode ) {
		int GenAct = Number - limit + Gen.NumActions  ;
		if (GenAct > -1 ) return Gen.Actions[GenAct] ;
	}
	return ThisMenu.DoItem(Number-1,GetHere) ;
}

int Menu::command_index(const char * cmd) const
{
	for (int i = 0 ; i < NoMenuItems;i++) if (!strcmp(cmd,
		MenuItems[i].Command)) return i ;
	return -1 ;
}

DoMenuReturn Menu::DoGraphicsMenuItem()
{
	return MenuDriver(*this,"Graphics Menu",GenericNormal);
}


void ReDoMainMenu()
{
	MenuDriver(*(TheCurrentMenus->GetMainMenu()),"command",
		GenericMainCommand);
}




Menu::Menu(const char * Head, MenuExtension * Ext,int MulUse,
		CommandParameters * init, MenuLine * parent):
		ExpandedMenu(0)
{
	SeparatedMenuList = 0;
	NextLine= -1 ;
	MultipleUseFlag = MulUse ;
	MenuLine * TheLines = new MenuLine[2];
	TheLines[0].SetMenuObject(MenuTypeExtension,Ext);
	TheLines[1].TheType = MenuTypeInvalid;
	SetMenuItems(TheLines) ;
	// LogOut << "Menu::Menu NoMenuItems = " << NoMenuItems << "\n" ;
	Header = Head ;
	Init = init ;
	Parent = parent;
}

Menu::Menu(const char * Head, MenuLine * Items,int MulUse,
		CommandParameters * init, MenuLine * parent):
		ExpandedMenu(0)
{
	SeparatedMenuList = 0;
	NextLine= -1 ;
	MultipleUseFlag = MulUse ;
	SetMenuItems(Items) ;
	Header = Head ;
	Init = init ;
	Parent = parent;
}


Menu * Menu::find_parent(Menu * menu)
{
	MenuLine * TheMenuLine ;
	GetMenuLine(MenuInit);
	while (TheMenuLine = GetMenuLine()) {
		Menu * test = TheMenuLine->GetNewMenu();
		if (!test) continue ;
		if (test == menu) return this ;
		if (test = test->find_parent(menu)) return test ;
	}
	return 0 ;
}

void Menu::InitParent(MenuLine * parent)
{
	// LogOut << "InitParent::" << Header << "\n" ;
	if (Parent) return ;
	if (!MultipleUseFlag) Parent = parent ;
	Menu * DaughterMenu ;
	MenuLine * From ;
	for (int i = 0; From = GetIndexedMenuLine(i);i++) {
		if (DaughterMenu = From->GetNewMenu())
			DaughterMenu->InitParent(From);
	}
}

static int CountItems(MenuLine * MenuItems)
{
	// LogOut << "CountItems, MenuItems = 0x" << hex((long)MenuItems)
	// 	<< "\n" ;
	int Return = 0;
	if (MenuItems) while (MenuItems[Return].TheType) Return++;
	// LogOut << "Returning " << Return << "\n" ;
	return Return ;
}

MenuExtension::MenuExtension(MenuLine * Items,int Number)
{
	SeparatedMenuList = 0;
	MenuItems = Items ;
	if (Number) NoMenuItems = Number ;
	else NoMenuItems = CountItems(MenuItems);
	SetTotalMenuItems();
}

void MenuExtension::AddMenuSegment(MenuExtension * NextExt)
{
	if (SeparatedMenuList) DbgError("MenuExtension::AddMenuSegment",
		"list already set");
	SeparatedMenuList = NextExt ;
}

void MenuExtension::AddItem(MenuLine * NewItem)
{
	MenuExtension * Next ;
	if (!(Next = GetNextMenuSegment())) {
		MenuItems[NoMenuItems++] = *NewItem ;
		SetTotalMenuItems();
		return ;
	}
	MenuExtension * Previous = Next ;
	while (Next = Next->GetNextMenuSegment()) Previous=Next ;
	Previous->AddItem(NewItem);
}

int MenuExtension::Delete(const char * Command)
{
	for (MenuExtension * Ext = this ; Ext; Ext->GetNextMenuSegment())
		if(Ext->DeleteLocal(Command)) return 1 ;
	return 0;
}

int MenuExtension::DeleteLocal(const char * Command)
{
	for (int i = 0 ; i < NoMenuItems; i++) {
		const char * Cmd = GetEntry(i)->GetCommand();
		if (!strcmp(Cmd,Command)) {
			if (!GetEntry(i)->Delete()) return 0;
			for (int j = i + 1; j < NoMenuItems;j++)
				MenuItems[j-1] = MenuItems[j];
			SetMenuItems(MenuItems,NoMenuItems-1);
			return 1 ;
		}
	}
	return 0 ;
}

void MenuExtension::SetMenuItems(MenuLine * NewItems, int Number)
{
	NoMenuItems = Number ;
	MenuItems = NewItems ;
	SetTotalMenuItems();
	// LogOut << "TotalMenuItems = " << TotalMenuItems << "\n" ;
}



void Menu::SetMenuItems(MenuLine * NewItems)
{
	MenuItems = NewItems ;
	NoMenuItems = CountItems(NewItems);
	SetTotalMenuItems();
}

void Menu::ExpandIfNeeded(int LineWidth)
{
	// LogOut << "ExpandIfNeeded(" << LineWidth << ")\n" ;
	int SumLength = 0;
	MenuLine * pt ;
	for (int i = 0 ; pt = GetIndexedMenuLine(i);i++)
		SumLength += strlen(pt->Command);
	// assume 1 character spacing between elements
	int Separators = i - 1 ;
	SumLength += Separators ;
	// LogOut << "SumLength = " << SumLength <<
 	//	", LineWidth = " << LineWidth << "\n" ;
	if (SumLength > LineWidth) {
		const char * cmd = "other" ;
		int NewSumLength = strlen(cmd);
		// LogOut << "Expanding `" << Header << "'.\n" ;
		for (int i = 0 ; pt = GetIndexedMenuLine(i);i++) {
			int SaveSum = NewSumLength ;
			NewSumLength += strlen(pt->Command) + 1;
			if (NewSumLength > LineWidth) {
				// LogOut << "Expanding at " << i-1 <<  "\n" ;
				// Expand(i-1,cmd);
				Expand(i,cmd);
				return ;
			}
		}
	}
}

static const char * DefaultPrompt = "Other options" ;
static const char * DefaultHelp[] = {
	"This menu is too long to fit on a single line.",
	"This option selects a submenu of additional options.",
	0
};

MenuExtension * Menu::GetExtensionContaining(int Index, int * BaseIndex,
	int * IndexHere)
{
	int ThisIndex = 0;
	MenuLine * Line = 0;
	// LogOut << "Menu::GetExt...C, NoMenuItems = " << NoMenuItems << "\n" ;
	// LogOut << "Index = " << Index << "\n" ;
	for (int i = 0; i < NoMenuItems; i++) {
		MenuExtension * Ext = MenuItems[i].GetExtension();
/*
 *		LogOut << "Menu::GetExt...C, i = " << i << ", Ext = " <<
 *			hex((long)Ext) << ", ThisIndex = " << ThisIndex <<
 *			"\n" ;
 */

		if (BaseIndex) *BaseIndex = ThisIndex ;
		if (IndexHere) *IndexHere = i ;
		if (Ext) {
			if (Ext->GetIndexedMenuLine(
				Index-ThisIndex, &ThisIndex)) return Ext ;
			// LogOut << "Did not return Ext\n" ;
		} else if (Index == ThisIndex) return 0 ;
			else ThisIndex++ ;
	}
	return 0 ;
}


MenuExtension * MenuExtension::Expand(int Base, int& ThisIndex)
{
	// LogOut << "Expand(" << Base << ", " << ThisIndex << ")\n" ;
	if (!Base) DbgError("MenuExtension::Expand","bad Base");
	for (int i = 0; i < NoMenuItems; i++) {
		int NewBase = i ;
		MenuExtension * NewExt = MenuItems[i].GetExtension();
		MenuExtension * Temp  = 0;
		if (NewExt) {
			if (Temp = NewExt->Expand(Base - ThisIndex,
				ThisIndex)) if (Temp == NewExt) NewBase++;
		} else if (ThisIndex==Base || Temp) {
/*
 *			LogOut << "Expanding menu extension at " << i << 
 *				", NoMenuItems = " << NoMenuItems << "\n" ;
 *			LogOut << "Temp = 0x" << hex((long) Temp) <<
 *				", this = 0x" << hex((long) this) << "\n" ;
 */

			if (!Temp ) {
				if (!i) DbgError("MenuExtension::Expand",
					"expanding at start");
				if (i == NoMenuItems-1) return this ;
				NewBase++ ;
			} else if (NewBase == i+1 && i == NoMenuItems-1)
				return this;
			int NewSize = i + 1 ;
			MenuLine * NewAry = new MenuLine[NewSize];
			// LogOut << "Allocated NewAry: NewBase = " << NewBase << "\n" ;
			for (int j = 0 ; j < NewSize; j++)
				NewAry[j] =MenuItems[j] ;

			if (Temp) if (NewBase == i) MenuItems[NewBase].
				SetMenuObject(MenuTypeExtension,Temp);
			for (i = 0 ; i < NoMenuItems-NewBase; i++)
				MenuItems[i] = MenuItems[i+NewBase] ;
			NewExt = new MenuExtension(MenuItems,
				NoMenuItems-NewBase);
			AddMenuSegment(NewExt);
			NewExt->SetTotalMenuItems();
			NoMenuItems = NewSize ;
			MenuItems = NewAry ;
			SetTotalMenuItems();
			// LogOut<<"TotalMenuItems = "<<TotalMenuItems << "\n";
			// Dump();
			return NewExt;
		} else ThisIndex++ ;
	}
	return 0;
}

void Menu::Expand(int NewMenuStart,const char * NewMenuSelect)
{
	// LogOut << "Menu::Expand(" << NewMenuStart << "...)\n" ;
	int ThisIndex = 0;
	int not_extension_expanded = 1 ;
	MenuExtension * NewExt = 0;
	int NewBase ;
	ExpandedMenu = 1 ;
	for (int i = 0 ; i < NoMenuItems;i++) {
		NewBase = i ;
		MenuExtension * Temp = MenuItems[i].GetExtension();
		if (Temp) {
			not_extension_expanded = 0 ;
			 if (NewExt = Temp->Expand(NewMenuStart-ThisIndex,
				ThisIndex)) {
				if (NewExt == Temp) NewBase++;
				break ;
			}
		} else if (NewMenuStart == ThisIndex) break ;
		else ThisIndex++ ;
	}
	if (NewMenuStart != ThisIndex && !NewExt)
		DbgError("Menu::Expand", "bad start");

	Menu * NewMenuEntry = 0;
	if (!NewExt) if (!i) DbgError("Menu::Expand", "can't expand at 0");
	MenuLine * NewAry = new MenuLine[i+3] ;
	for (int j = 0 ; j < i+1; j++) NewAry[j+1] = MenuItems[j];
	NewAry[i+2].TheType = MenuTypeInvalid;
	NewAry[i+2].Command = 0;
	// LogOut << "i = " << i << "\n" ;
	
	if (NewExt &&  NewBase==i) MenuItems[NewBase].
		SetMenuObject(MenuTypeExtension,NewExt);
	// LogOut << "After SetMenuObject\n" ;
	// NewMenuEntry = new Menu(Header, MenuItems+NewBase, MultipleUseFlag,
	NewMenuEntry = new Menu(Header, MenuItems+NewBase + not_extension_expanded,
		MultipleUseFlag, Init);
	MenuItems = NewAry ;
	AddMenuSegment(NewMenuEntry);
	if (Parent) MenuItems[0] = (new MenuLine)->SetMenuLine(NewMenuSelect,
		Parent->Line, MenuTypeMenu, NewMenuEntry,
		Parent->Conflicts, Parent->Init, Parent->HelpText,
		Parent->HelpFileName,MenuTypeSpecialExpanded);
	else MenuItems[0] = (new MenuLine)->SetMenuLine(NewMenuSelect,
		DefaultPrompt, MenuTypeMenu, NewMenuEntry, 0, 0,
		DefaultHelp, 0);
	NoMenuItems = i + 2 ;
	SetTotalMenuItems();
	// Dump();
}

// void Menu::Contract(int Line) // combine lines Line and
// {
// 
// }

MenuObject::MenuObject(void * TheObject, MenuType type)
{
	switch (type) {
case MenuTypeInvalid:
case MenuTypeHelp:
default:
		DbgError("MenuObject::MenuObject", "bad type");
		break ;
case MenuTypeCommand:
		TheCommand = (CommandParameters *) TheObject ;
		break ;
case MenuTypeMenu:
		TheSubMenu = (Menu *) TheObject ;
		break ;
case MenuTypeExtension:
		TheExtension = (MenuExtension *) TheObject ;
		break ;
	}
}


int MenuLine::invalid()
{
	switch (TheType) {
case MenuTypeCommand:
case MenuTypeHelp:
		return 0 ;
default:
case MenuTypeInvalid:
		return 1 ;
case MenuTypeMenu:
		{
			if (!GetNewMenu()) return 1 ;
			if (!GetNewMenu()->GetTotalMenuItems()) return 1 ;
			return 0 ;
			break ;
		}
case MenuTypeExtension:
		if (GetExtension()) return 0 ;
		return 1 ;
	}
	return 0 ;
}
MenuObject MenuLine::GetMenuObject()
{
	switch (TheType) {
case MenuTypeInvalid:
case MenuTypeHelp:
default:
		return MenuObject((Menu *) 0);
case MenuTypeCommand:
		return MenuObject(GetDoCommand());
case MenuTypeMenu:
		return MenuObject(GetNewMenu());
case MenuTypeExtension:
		return MenuObject(GetExtension());
	}
}

void Menu::AddMenuItem(MenuLine * TheItem)
{
	// LogOut << "Enter\n" ;
	// LogOut << "AddMenuItem for command: " << TheItem->Command << "\n" ;
	static MenuLine Dummy = {0,0,MenuTypeInvalid,0};
	int NewSize = CountItems(MenuItems);
	// LogOut << "NewSize = " << NewSize << "\n" ;
	MenuLine * NewItems = new MenuLine[NewSize+2] ;
	for (int i = 0 ; i < NewSize; i++) NewItems[i] = MenuItems[i] ;
	NewItems[NewSize]= *TheItem;
	NewItems[NewSize+1] = Dummy ;
	// delete MenuItems ;
	MenuItems = NewItems ;
	NoMenuItems++;
	TotalMenuItems++ ;
}


void MenuLine::AddMenuItem(Menu * TheMenu)
{
	// LogOut << "AddMenuItem enter\n" ;
	if (TheType != MenuTypeMenu) DbgError("MenuLine::AddMenuItem",
		"not a menu");
	Menu * CurrentMenu = GetMenuObject().TheSubMenu ;
	if (!CurrentMenu) {
		SetMenuObject(MenuTypeMenu,TheMenu);
		return ;
	} else {
		// LogOut << "Command is: " << Command << "\n" ;
		// LogOut << "Line is: " << Line << "\n" ;
		MenuLine * TheMenuLine ;
		TheMenu->GetMenuLine(MenuInit);
		while (TheMenuLine = TheMenu->GetMenuLine())
			CurrentMenu->AddMenuItem(TheMenuLine);

	}
}


void MenuLine::SetMenuObject(MenuType type, MenuObject object)
{
	// LogOut << "SetMenuObject enter\n" ;
	switch (type) {
case MenuTypeInvalid:
case MenuTypeHelp:
default:
		DbgError("MenuLine::SetMenuObject", "bad type");
		break ;
case MenuTypeCommand:
		TheObject = (void *) object.TheCommand ;
		break ;
case MenuTypeMenu:
		TheObject = (void *) object.TheSubMenu ;
		break ;
case MenuTypeExtension:
		TheObject = (void *) object.TheExtension ;
		break ;
	}
	TheType = type ;
}

CommandParameters * MenuLine::GetDoCommand()
{
	if(TheType == MenuTypeCommand) return
		MenuObject(TheObject,TheType).TheCommand ;
	return 0;
}

Menu * MenuLine::GetNewMenu()
{
	if(TheType == MenuTypeMenu) return
		MenuObject(TheObject,TheType).TheSubMenu ;
	return 0;
}
	
MenuExtension * MenuLine::GetExtension()
{
	if(TheType == MenuTypeExtension) return
		MenuObject(TheObject,TheType).TheExtension ;
	return 0;
}
	

// Simulated constructor for class we want to init 
MenuLine& MenuLine::SetMenuLine(const char * cmd, const char *ln,
	MenuType type, MenuObject object, const char ** conf,
	CommandParameters * init, const char ** help, const char * file,
	MenuTypeSpecial spc)
{
	Command = cmd ;
	Line = ln;
	Conflicts = conf ;
	Init = init;
	HelpText = help ;
	HelpFileName = file;
	type_special = spc ;
	SetMenuObject(type,object);
	return *this ;
}


int MenuLine::Delete()
{
	// delete Command;
	// delete Line; 
	// for (const char ** ToDel = Conflicts; *ToDel ; delete *ToDel);
	// delete Conflicts ;
	return 1;
}

static MenuLine * GetIndexedMenuLine(MenuLine * MenuItems,
	int NoMenuItems, int Index, int * NewIndex)
{
	int ThisIndex = 0;
	MenuLine * Return = 0;
/*
 *	LogOut << "GetIndexedMenuLine(" << NoMenuItems << ", " <<Index 
 *		<< "...)\n" ;
 */
	for (int i = 0; i < NoMenuItems; i++) {
		if (!MenuItems[i].TheType) break ;
		MenuExtension * Ext = MenuItems[i].GetExtension();
		// LogOut << "Ext = 0x" << hex((long)Ext) << "\n" ;
		if (Ext) Return = Ext->GetIndexedMenuLine(
			Index-ThisIndex, &ThisIndex);
		else {
			if (Index == ThisIndex) Return = MenuItems + i ;
			else ThisIndex++ ;
		}
/*
 *		LogOut << "Return = 0x" << hex((long)Return) << 
 *			", i = " << i << "\n" ;
 */
		if (Return) break ;
	}
	if (NewIndex) *NewIndex += ThisIndex ;
	return Return ;
}

MenuLine * Menu::GetIndexedMenuLine(int Index, int * NewIndex)
{
	return ::GetIndexedMenuLine(MenuItems,NoMenuItems, Index, NewIndex);
}

MenuLine * MenuExtension::GetIndexedMenuLine(int Index, int * NewIndex)
{
	return ::GetIndexedMenuLine(MenuItems,NoMenuItems, Index, NewIndex);
}

void Menu::SetTotalMenuItems()
{
	// LogOut << "SetTotalMenuItems\n" ;
	TotalMenuItems = 0;
	// LogOut << "SetTotalMenuItems did set\n" ;
	GetIndexedMenuLine(32767,&TotalMenuItems);
	MenuExtension * Ext ;
	for (int i = 0 ; i < NoMenuItems;i++)
		if (Ext = MenuItems[i].GetExtension()) Ext->SetTotalMenuItems();
	if (SeparatedMenuList) SeparatedMenuList->SetTotalMenuItems();
	// LogOut << "SetTotalMenuItems  exit\n" ;
}

void MenuExtension::SetTotalMenuItems()
{
	TotalMenuItems = 0;
	GetIndexedMenuLine(NoMenuItems,&TotalMenuItems);
}


MenuLine * Menu::GetMenuLine(int i)
{
	// LogOut << "GetmenuLine(" << i << ")\n" ;
	// LogOut << Header << "\n" ;
	if (i == MenuInit ) {
		NextLine = 0 ;
		// LogOut << "returning MenuInit\n" ;
		return 0;
	}
	MenuLine * Return ;
	if (i == MenuNext) {
		if (NextLine < 0) return 0 ;
		Return = GetIndexedMenuLine(NextLine++);
		if (Return) return Return ;
		NextLine = -1 ;
		return 0;
	}
	if (i > -1 ) Return = GetIndexedMenuLine(i);
	if (Return) return Return ;
	Display() ;
	DbgError("Menu::GetMenuLine", "Invalid index") ;
	return 0;
}

int Menu::FindName(const char * Name)
{
	MenuLine * pt ;
	for (int i = 0 ; pt = GetIndexedMenuLine(i);i++)
		if (!strcmp(pt->Command,Name)) return i+1 ;
	return 0;
}

MenuLine * Menu::IsValidCommand(const char * cmd)
{
	MenuLine *pt ;
	for (int i = 0 ; pt = GetIndexedMenuLine(i);i++) {
		if (!strcmp(pt->Command,cmd)) {
			CheckInit() ;
			return pt;
		}
	}
	return 0;
}

void Menu::Display()
{
	if (!this) DbgError("Menu::Display","null this pointer");
	TheLog << "Header:\n" << Header <<
		"\n\n Number entries = " <<
		NoMenuItems << "\n" ;
	MenuLine *pt ;
	for (int i = 0 ; pt = GetIndexedMenuLine(i);i++) {
		TheLog << pt->Command <<  "::"  << pt->Line
		<< "\n" ;
		pt++;
	}
}

MenuEnums::ExitStatus Menu::execute_command(MenuTreeTraverseObject& obj,
	const char*where,int ix)
{
	obj.check_on_enter(this);
	// LogOut << "Menu::execute_command(,\"" << where << "\", " << ix << ")\n" ;
	MenuLine *line;
	StackMenuBase * entry = new StackMenuBase(this,where,ix,obj.size());
	obj.push(entry);
	for (int count = 0; line = GetIndexedMenuLine(count); count++) {
		const char * used = 0 ;
		Menu * menu = line->GetNewMenu();
		int found_at_this_level = 0 ;
/*
 *		LogOut << "checking " << obj.next() << " : " << line->Command <<
 *			", met = " << obj.constraint_met() << "\n" ;
 */

		if (obj.constraint_met()) if (obj.next()) {
			if(menu) if (!strcmp(line->Command,obj.next()))  {
				// LogOut << "found `" << line->Command << "'\n" ;
				found_at_this_level = 1 ;
				used = obj.next() ;
				obj.next_next();
			}
		} else if (!menu) if(!strcmp(line->Command,obj.command())) {
			obj.stack_menu(entry,count);
			found_at_this_level = 1 ;
			// LogOut << "Setting enty`" << line->Command << "'\n" ;
			return MenuEnums::found ;
		}
		if (menu) {
			if (obj.constraint_met()) if (!found_at_this_level) {
				if (obj.must_be_other()) if (strcmp("other",line->Command))
					continue ;
			}
			switch (menu->execute_command(obj,line->Command,count)) {
case MenuEnums::found:
				return MenuEnums::found ;
case MenuEnums::not_found:
				break ;
case MenuEnums::abort:
				return MenuEnums::abort ;
			}
			if (used) obj.push_next(used);
		}
	}
	if (!obj.found()) obj.pop();
/*
 *	LogOut << "Menu::execute_command(,\"" << where << "\", " << ix << ") exit, "
 *		<< (void *) obj.found() << "n" ;
 */
	return obj.check_on_exit(this);
}

void Menu::find_command(MenuTreeTraverseObject& obj, const char * where, int ix)
{
	// LogOut << "Menu::find_command(,\"" << where << "\", " << ix << ")\n" ;
	MenuLine *line;
	StackMenuBase * entry = new StackMenuBase(this,where,ix,obj.size());
	if (!obj.found()) obj.push(entry);
	for (int count = 0; line = GetIndexedMenuLine(count); count++) {
		// LogOut << "Checking `" << line->Command << "'\n" ;
		if (!strcmp(line->Command,obj.command())) {
			if (obj.found()) {
				// LogOut << "Set duplicate\n" ;
				obj.duplicate();
				break ;
			}
			obj.stack_menu(entry,count);
			// LogOut << "Setting enty\n" ;
		}
		Menu * menu = line->GetNewMenu();
		if (menu) {
			menu->find_command(obj,line->Command,count);
			if (obj.is_duplicate()) break ;
		}
	}
	if (!obj.found()) obj.pop();
/*
 *	LogOut << "Menu::find_command(,\"" << where << "\", " << ix << ") exit, "
 *		<< (void *) obj.found() << "n" ;
 */
}

int AllMenus::execute_command(MenuTreeTraverseObject& obj)
{
	// LogOut << "AllMenus::execute_command(obj)\n" ;
	// LogOut << "command = `" << obj.command() << "'.\n" ;
	
	if ( MainMenu->execute_command(obj,0,-1) != MenuEnums::found) return 0;
	DspApplication::root_window()->execute_menu_command(obj);
	return 1 ;
}

void AllMenus::find_command(MenuTreeTraverseObject& obj)
{
	// LogOut << "AllMenus::find_command(obj)\n" ;
	MainMenu->find_command(obj,0,-1);
}

Menu * AllMenus::FindMenuFromCommand(const char * cmd)
{
	// LogOut << "FindMenuFromCommand(\"" << cmd << "\")\n" ;
	for (Menu ** Check = TheMenus; *Check ; Check++) {
		(*Check)->GetMenuLine(MenuInit);
		MenuLine * TheItem ;
		// LogOut << "Searching for " << cmd << "\n" ;
		while (TheItem = (*Check)->GetMenuLine()) {
			// LogOut << "Checking: "<<TheItem->GetCommand()<<"\n" ;
			if (!strcmp(TheItem->GetCommand(),cmd)) {
				Menu * TheMenu = TheItem->GetNewMenu();
				if (TheMenu) return TheMenu ;
			}
		}
	}
	return 0;
}

void AllMenus::DumpStack(OutTokens& Out)
{
	char buf[BufSize];
	ConstStringListIterator Next(CommandStack) ;
	const char * cmd ;
	int i = 1 ;
	while (cmd = Next()) {
		Out.NextFillOut(strcpy(buf,dec(i++,4)));
		Out.NextConcat("    ");
		Out.NextFillOut(cmd);
		Out.NewLine();
	}
}

MenuLine * AllMenus::IsValidCommand(const char * cmd)
{
	Menu ** AMenu = TheMenus ;
	MenuLine * MenuItem ;
	while (*AMenu)
		if (MenuItem=(*(AMenu++))->IsValidCommand(cmd)) return MenuItem;
	return 0 ;
}

const char * AllMenus::IsLegalCommand(MenuLine * MenuItem)
{
	const char ** Conflicts = MenuItem->GetConflicts();
	if (!Conflicts) return 0;
	while (*Conflicts) {
 		ConstStringListIterator Next(CommandStack) ;
		const char * PossibleConflict ;
		while (PossibleConflict=Next()) if (!strcmp(*Conflicts,
			PossibleConflict)) return PossibleConflict ;
		Conflicts++;
	}
	return 0;
}

int AllMenus::IsLegal(MenuLine * MenuItem)
{
	const char * ErrorCmd = IsLegalCommand(MenuItem);
	if (ErrorCmd) {
		*Output + OutputHelp << "Cannot execute command `" <<
			MenuItem->Command <<
			"' while command `" << ErrorCmd << "' is suspended.\n";
		*Output + OutputHelp <<
			"Use command `stack' to see all suspended commands.\n";
		ReturnToContinue(OutputHelp) ;
		return 0;
	}
	return 1;
}

int AllMenus::TestAndDo(const char * cmd)
{
	MenuLine * MenuItem = IsValidCommand(cmd);
	if (!MenuItem) return 0;
	MenuItem->DoItem("command");
	return 1 ;
}

/****************
int AllMenus::do(const char * cmd)
{
	// LogOut << "AllMenus::TestAndDo(\"" << cmd << "\")\n" ;
	if (!cmd) return 0;
	if (!*cmd) return 0 ;
	MenuTreeTraverseObject obj(cmd);
	// traverse_menu_tree(obj);
	if (!obj.is_error()) {
		obj.error_message("the full menu tree");
		return 0 ;
	}
	// MenuLine * MenuItem = IsValidCommand(cmd);
	// obj->found()->DoItem("command");
	return 1 ;
}
**************************/

DoMenuReturn Menu::DoItem(int Index,const char *GetHere)
{
	MenuLine * ThisLine = GetMenuLine(Index);
	if (ThisLine) return ThisLine->DoItem(GetHere);
	DbgError("Menu::DoItem", "Invalid option");
	return DoMenuPrevious ; // Never executed
}

DoMenuReturn MenuLine::DoItem(const char * GetHere)
{
	if (!TheCurrentMenus->IsLegal(this)) return DoMenuCurrent;
	HelpDo.AutoDescribeMenuAction(HelpText);
	CheckInit();
	if (GetNewMenu()) {
		GetNewMenu()->CheckInit();
		TheLog << "MUST add graphics mode DoGraphicsMenu.\n" ;
		// if (GraphicsMode) return DoGraphicsMenu(GetNewMenu(),Command) ;
		char * NewName = new char[strlen(GetHere)+
			strlen(Command) + 2] ;
		if(!strlen(GetHere)) strcpy(NewName,Command) ;
		else strcat(strcat(strcpy(NewName,GetHere),"."),
			Command);
		TheCurrentMenus->Push(Command);
		DoMenuReturn Ret = MenuDriver(*(GetNewMenu()),NewName,
			GenericNormal);
		TheCurrentMenus->Pop();
		delete NewName ;
		return Ret ;
	} else if (GetDoCommand()) {
		TheCurrentMenus->Push(Command);
		GetDoCommand()->Execute();
		TheCurrentMenus->Pop();
		return DoMenuCurrent ;
	} else HelpDo.DisplayHelpFile(HelpFileName) ;
	return DoMenuCurrent ;

}

void AllMenus::CheckMenu(Menu& AnMenu, MenuList& All)
{
	Menu * MenuSeen ;
	MenuListIterator Next(All) ;
	if (AnMenu.GetMultipleUseFlag()) return ;
	while (MenuSeen=Next()) if (MenuSeen==&AnMenu) return ;
	All.Append (&AnMenu) ;
	MenuLine * ThisLine ;
	AnMenu.GetMenuLine(MenuInit) ;
	while (ThisLine = AnMenu.GetMenuLine()) {
		Menu * LowerMenu ;
		if (LowerMenu=ThisLine->GetNewMenu()) CheckMenu(*LowerMenu,All);
	}
}

AllMenus::AllMenus(Menu& TheMainMenu)
{
	MainMenu = &TheMainMenu ;
	MenuList All ;
	CheckMenu(TheMainMenu,All);
	int k = All.Size();
	Menu ** Pt = TheMenus = new Menu * [k+1] ;
	MenuListIterator Next(All) ;
	while(*Pt++ = Next()) ; 
}

void AllMenus::Init()
{
	// Traverse tree setting parent Menu for all non MultiUse menus
	// Traversing algorithm assumes tree has no recursive loops with
	// only multiple use menus involved - other recursions are
	// stopped by Parent being non zero
	MainMenu->InitParent(0);
}

void MenuLine::Help(OutTokens& Out)
{
	Out.NextConcat(Command);
	for (int i= 0; i < 20 - strlen(Command);i++) Out.NextConcat(" ");
	Out.NextFillOut(Line);
	Out.NewLine();
}

void Menu::Help(OutTokens& Out)
{
	MenuLine *pt ;
	for (int i = 0 ; pt = GetIndexedMenuLine(i);i++)pt->Help(Out);
}

void AllMenus::Help()
{
	//OutTokens Out(&cout);
	OutTokens Out(&(Output->GetStream(OutputHelp)));
	Out.NextConcat("Command             Action");
	Out.NewLine();
	Menu ** Pt = TheMenus ;
	Menu * AnMenu ;
	while (AnMenu = *Pt++) AnMenu->Help(Out);
}

Menu * MenuList::GetNthEntry(int N)
{
	MenuListIterator Next(*this) ;
	Menu * AMenu ;
	for (int i = 1 ; AMenu = Next() ; i++) if (i==N) return AMenu ;
	return 0;
}

Menu * MenuList::GetNFromTop(int N)
{
	MenuListIterator Next(*this) ;
	int Sz = Size() ;
	if (Sz < N) return 0 ;
	N = Sz - N ;
	Menu * AMenu ;
	for (int i = 1 ; AMenu = Next() ; i++) if (i==N) return AMenu ;
	return 0;
}

void Menu::AddMenuSegment(Menu * NewSegment)
{
	Menu * Next ;
	if (!(Next = GetNextMenuSegment())) {
		SeparatedMenuList = NewSegment ;
		return ;
	}
	Menu * Prev = Next ;
	while (Next = Next->GetNextMenuSegment()) Prev = Next ;
	Prev->AddMenuSegment(NewSegment);
}

static const char * MakeSelectName(const char * Node, const char * Operation)
{
	const BufSize = 256 ;
	static char Buf[BufSize+1];
	const char * Prefix = "Select" ;
	int Size = strlen(Prefix) + strlen(Node) + strlen(Operation) ;
	if (Size > BufSize) DbgError("MakeSelectName","too big");
	strcpy(Buf,Prefix);
	strcat(Buf,Node);
	strcat(Buf,Operation);
	return Buf ;
}

static Menu * GetOrphanFromName(const char * OrphanName)
{
	// LogOut << "Looking for orphan `" << OrphanName << "'\n" ;
	for (OrphanedMenus * TheOrphans = TheOrphanedMenus ;
		TheOrphans->GetName(); TheOrphans++) {
		if (!strcmp(TheOrphans->GetName(),OrphanName))
			return TheOrphans->GetMenu();
	}
	return 0;
}

static void DoAttachOrphan(Menu * AddTo, Menu * ToAdd, const char * Cmd)
{
	// LogOut << "DoAttachOrphan - cmd is: " << Cmd << "\n" ;
	AddTo->GetMenuLine(MenuInit);
	MenuLine * TheItem;
	while (TheItem = AddTo->GetMenuLine()) {
		// LogOut << "Checking - " << TheItem->GetCommand() << "\n" ;
		if (!strcmp(TheItem->GetCommand(),Cmd)) {
			TheItem->AddMenuItem(ToAdd);
			return ;
		}
	}
	DbgError("DoAttachOrphan","cannot attach");
}

void AttachOrphan(const char * BaseClass, const char * DerivedClass)
{
/*
 *	LogOut << "Command to attach `" << BaseClass << "' to `" <<
 *		DerivedClass << "'\n" ;
 */
 
	Menu * AddTo = TheCurrentMenus->FindMenuFromCommand(DerivedClass);
	if (!AddTo) {
 		TheLog << "Command to attach `" << BaseClass << "' to `" <<
			DerivedClass << "'\n" ;
		TheLog << "AttachOrphan: no such menu\n";
		return ;
	}
	const char * SelectName = MakeSelectName(BaseClass,"MemberDescribe");
	Menu * ToAdd = GetOrphanFromName(SelectName) ;
	if (!ToAdd) {
/*
 * 		LogOut << "Command to attach `" << BaseClass << "' to `" <<
 *			DerivedClass << "'\n" ;
 *		LogOut << "AttachOrphan: no such orphan\n";
 *		LogOut << "SelectName is " << SelectName << "\n" ;
 */
		return ;
	}
	DoAttachOrphan(AddTo,ToAdd,"members");

	MenuLine * InstanceLine = AddTo->FindLineFromCommand("instance");
	if (!InstanceLine) DbgError("AttachOrphan","no instance menu line");
	Menu * InstanceMenu = InstanceLine->GetNewMenu();
	if (!InstanceMenu) DbgError("AttachOrphan","no instance menu");
	InstanceMenu->GetMenuLine(MenuInit);
	MenuLine * MenuItem = InstanceMenu->GetMenuLine();
	if (!MenuItem) {
/*
 *		LogOut << "Base = `" << BaseClass << "', Derived = `"
 *			<< DerivedClass << "'\n" ;
 *		LogOut << "Header from instance: "
 *			<< InstanceMenu->GetHeader() << "\n" ;
 *		LogOut << "NO INSTANCES\n" ;
 */
		return ;
		// DbgError("AttachOrphan","no instances");
	}
	Menu * AnInstanceMenu = MenuItem->GetNewMenu();
	if (!AnInstanceMenu) DbgError("AttachOrphan","no instance submenu");

	SelectName = MakeSelectName(BaseClass,"MemberExecute");
	ToAdd = GetOrphanFromName(SelectName) ;
	if (!ToAdd) {
 		TheLog << "Command to attach `" << BaseClass << "' to `" <<
			DerivedClass << "'\n" ;
		TheLog << "AttachOrphan: no such orphan\n";
		TheLog << "SelectName is " << SelectName << "\n" ;
		return ;
	}
	DoAttachOrphan(AnInstanceMenu,ToAdd,"exec");
}

