/******************************************************************************
 JXHistoryMenuBase.cc

	Base class for menus that maintains a fixed-length history.  New items
	should be prepended to the menu so the oldest ones automatically fall off
	the bottom.

	The default icon is empty.  To change the icon for all items, call
	SetDefaultIcon().  Otherwise, you can create a derived class and
	override UpdateItemImage() to display different icons for different files.

	Since nothing on this menu is ever disabled, one only needs to listen
	for the ItemSelected message.

	We listen for NeedsUpdate and automatically call UpdateMenu(), so
	derived classes only need to override UpdateMenu().

	BASE CLASS = JXTextMenu

	Copyright © 1998 by John Lindal. All rights reserved.

 ******************************************************************************/

#include <JXHistoryMenuBase.h>
#include <JXImage.h>
#include <JString.h>
#include <iostream.h>
#include <jAssert.h>

// setup information

const JFileVersion kCurrentSetupVersion = 10000;

	// version < 10000 => the first value is really the number of items (no shortcuts)

/******************************************************************************
 Constructor (protected)

 ******************************************************************************/

JXHistoryMenuBase::JXHistoryMenuBase
	(
	const JSize			historyLength,
	const JCharacter*	title,
	JXContainer*		enclosure,
	const HSizingOption	hSizing,
	const VSizingOption	vSizing,
	const JCoordinate	x,
	const JCoordinate	y,
	const JCoordinate	w,
	const JCoordinate	h
	)
	:
	JXTextMenu(title, enclosure, hSizing, vSizing, x,y, w,h)
{
	JXHistoryMenuBaseX(historyLength);
}

JXHistoryMenuBase::JXHistoryMenuBase
	(
	const JSize		historyLength,
	JXMenu*			owner,
	const JIndex	itemIndex,
	JXContainer*	enclosure
	)
	:
	JXTextMenu(owner, itemIndex, enclosure)
{
	JXHistoryMenuBaseX(historyLength);
}

// private

void
JXHistoryMenuBase::JXHistoryMenuBaseX
	(
	const JSize historyLength
	)
{
	assert( historyLength > 0 );

	itsHistoryLength = historyLength;
	SetUpdateAction(kDisableNone);

	itsDefaultIcon     = NULL;
	itsOwnsDefIconFlag = kFalse;

	ListenTo(this);
}

/******************************************************************************
 Destructor

 ******************************************************************************/

JXHistoryMenuBase::~JXHistoryMenuBase()
{
	if (itsOwnsDefIconFlag)
		{
		delete itsDefaultIcon;
		}
}

/******************************************************************************
 SetHistoryLength

 ******************************************************************************/

void
JXHistoryMenuBase::SetHistoryLength
	(
	const JSize historyLength
	)
{
	assert( historyLength > 0 );

	itsHistoryLength = historyLength;
	AdjustLength();
}

/******************************************************************************
 AdjustLength (protected)

	Remove extra items from the bottom of the menu.

 ******************************************************************************/

void
JXHistoryMenuBase::AdjustLength()
{
	JSize itemCount = GetItemCount();
	while (itemCount > itsHistoryLength)
		{
		DeleteItem(itemCount);
		itemCount--;
		}
}

/******************************************************************************
 AddItem (protected)

	Prepend the given item to the menu and remove outdated entries
	at the bottom.

 ******************************************************************************/

void
JXHistoryMenuBase::AddItem
	(
	const JCharacter* text,
	const JCharacter* nmShortcut
	)
{
	const JSize itemCount = GetItemCount();
	JString itemNMShortcut;
	for (JIndex i=1; i<=itemCount; i++)
		{
		GetItemNMShortcut(i, &itemNMShortcut);

		const JBoolean matches = JConvertToBoolean(
			text == GetItemText(i) && nmShortcut == itemNMShortcut );
		if (matches && i == 1)
			{
			return;		// already at top of list
			}
		else if (matches)
			{
			DeleteItem(i);
			break;
			}
		}

	PrependItem(text, kFalse, kFalse, NULL, nmShortcut);
	AdjustLength();
}

/******************************************************************************
 ReadSetup

	We assert that the data is readable because there is no way to skip
	over it.

 ******************************************************************************/

void
JXHistoryMenuBase::ReadSetup
	(
	istream& input
	)
{
	RemoveAllItems();

	JFileVersion vers;
	input >> vers;
	if (input.eof() || input.fail())
		{
		return;
		}
	assert( vers <= kCurrentSetupVersion );

	JSize count = vers;
	if (vers >= 10000)
		{
		input >> count;
		if (input.eof() || input.fail())
			{
			return;
			}
		}

	JString text, nmShortcut;
	for (JIndex i=1; i<=count; i++)
		{
		input >> text;
		if (vers >= 10000)
			{
			input >> nmShortcut;
			}
		if (input.fail())
			{
			break;
			}
		AppendItem(text, kFalse, kFalse, NULL, nmShortcut);
		}

	AdjustLength();		// must always read them all to place file marker
}

/******************************************************************************
 WriteSetup

 ******************************************************************************/

void
JXHistoryMenuBase::WriteSetup
	(
	ostream& output
	)
	const
{
	output << kCurrentSetupVersion;

	const JSize count = GetItemCount();
	output << ' ' << count;

	JString nmShortcut;
	for (JIndex i=1; i<=count; i++)
		{
		GetItemNMShortcut(i, &nmShortcut);
		output << ' ' << GetItemText(i) << ' ' << nmShortcut;
		}
}

/******************************************************************************
 Receive (virtual protected)

 ******************************************************************************/

void
JXHistoryMenuBase::Receive
	(
	JBroadcaster*	sender,
	const Message&	message
	)
{
	if (sender == this && message.Is(JXMenu::kNeedsUpdate))
		{
		UpdateMenu();
		}

	JXTextMenu::Receive(sender, message);
}

/******************************************************************************
 UpdateMenu (virtual protected)

 ******************************************************************************/

void
JXHistoryMenuBase::UpdateMenu()
{
	const JSize count = GetItemCount();
	for (JIndex i=1; i<=count; i++)
		{
		UpdateItemImage(i);
		}
}

/******************************************************************************
 UpdateItemImage (virtual protected)

	Derived classes can override this to display a different icon for
	each item.

 ******************************************************************************/

void
JXHistoryMenuBase::UpdateItemImage
	(
	const JIndex index
	)
{
	const JXImage* image;
	if (!GetItemImage(index, &image))
		{
		SetItemImage(index, itsDefaultIcon, kFalse);
		}
}

/******************************************************************************
 SetDefaultIcon

 ******************************************************************************/

void
JXHistoryMenuBase::SetDefaultIcon
	(
	JXImage*		icon,
	const JBoolean	menuOwnsIcon
	)
{
	if (itsOwnsDefIconFlag)
		{
		delete itsDefaultIcon;
		}

	itsDefaultIcon     = icon;
	itsOwnsDefIconFlag = menuOwnsIcon;

	// clear all icons so UpdateItemImage() will fix it
	// (We can't just use the default icon, since it can be overridden.)

	const JSize count = GetItemCount();
	for (JIndex i=1; i<=count; i++)
		{
		ClearItemImage(i);
		}
}
