/******************************************************************************
 JXMenu.cc

	When the menu is pulled down, the requested items are initially disabled
	(see UpdateAction).  The default action is to disable all items that do
	not have submenus (kDisableSingles).

	All items are unchecked because the menu's owner should store this
	information anyway, and it saves a lot of work to not have to enforce
	one selection per radio group.  The client should be storing only a single
	value for each radio group anyway, so it will be taken care of automatically.

	If the menu is a PopupChoice, then it assumes that the entire menu is a
	radio group so that only one item can be selected at a time.  The owner
	of the menu remains responsible for tracking the selection.  We simply
	include the text of the selected item in the menu title as a convenience.
	Note that SetPopupChoice() does not need to be called when ItemSelected
	is broadcast because we take care of it automatically in this case.

	Items are not required to have ID's, but if they do, they should be
	unique (like JBroadcaster messages) so each object can find its own
	items.  The exception is an item that performs a standard action like
	Copy and can therefore be shared by all objects.  The standard ID's
	are stored in jXActions.h

	BASE CLASS = JXWidget

	Copyright © 1996-98 by John Lindal. All rights reserved.

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

#include <JXMenu.h>
#include <JXMenuData.h>
#include <JXMenuDirector.h>
#include <JXMenuTable.h>
#include <JXMenuBar.h>
#include <JXMenuManager.h>

#include <JXDisplay.h>
#include <JXWindowDirector.h>
#include <JXWindowPainter.h>
#include <JXWindow.h>
#include <JXImage.h>
#include <JXColormap.h>
#include <jXPainterUtil.h>
#include <jXGlobals.h>

#include <JFontManager.h>
#include <JString.h>
#include <jMath.h>
#include <string.h>
#include <jAssert.h>

const JCoordinate kTitleExtraWidth = 16;

// information for drawing arrow when used as popup menu

const JCoordinate kTotalArrowWidth   = 28;
const JCoordinate kArrowWidth        = 16;
const JCoordinate kArrowHalfHeight   = 4;
const JCoordinate kArrowShrinkWidth  = 4;
const JCoordinate kArrowShrinkHeight = 2;

// JBroadcaster message types

const JCharacter* JXMenu::kNeedsUpdate  = "NeedsUpdate::JXMenu";
const JCharacter* JXMenu::kItemSelected = "ItemSelected::JXMenu";

/******************************************************************************
 Constructor

	*** derived class must call SetItemData()

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

JXMenu::JXMenu
	(
	const JCharacter*	title,
	JXContainer*		enclosure,
	const HSizingOption	hSizing,
	const VSizingOption	vSizing,
	const JCoordinate	x,
	const JCoordinate	y,
	const JCoordinate	w,
	const JCoordinate	h
	)
	:
	JXWidget(enclosure, hSizing, vSizing, x,y, w,h)
{
	JXMenuX(title, NULL, kTrue);
}

JXMenu::JXMenu
	(
	JXImage*			image,
	const JBoolean		menuOwnsImage,
	JXContainer*		enclosure,
	const HSizingOption	hSizing,
	const VSizingOption	vSizing,
	const JCoordinate	x,
	const JCoordinate	y,
	const JCoordinate	w,
	const JCoordinate	h
	)
	:
	JXWidget(enclosure, hSizing, vSizing, x,y, w,h)
{
	JXMenuX("", image, menuOwnsImage);
}

JXMenu::JXMenu
	(
	JXMenu*			owner,
	const JIndex	itemIndex,
	JXContainer*	enclosure
	)
	:
	JXWidget(enclosure, kFixedLeft, kFixedTop, 0,0, 10,10)
{
	JXMenuX("", NULL, kTrue);
	owner->AttachSubmenu(itemIndex, this);
}

// private

void
JXMenu::JXMenuX
	(
	const JCharacter*	title,
	JXImage*			image,
	const JBoolean		menuOwnsImage
	)
{
	itsTitle = new JString;
	assert( itsTitle != NULL );

	itsTitleImage         = NULL;
	itsOwnsTitleImageFlag = kTrue;
	itsShortcuts          = NULL;
	itsULIndex            = 0;

	itsTitleFontName = new JString(JGetDefaultFontName());
	assert( itsTitleFontName != NULL );

	itsTitleSize      = kJXDefaultFontSize;
	itsTitleStyle     = (GetColormap())->GetBlackColor();
	itsTrueTitleColor = itsTitleStyle.color;
	if (!IsActive())
		{
		itsTitleStyle.color = (GetColormap())->GetInactiveLabelColor();
		}

	itsBaseItemData = NULL;
	itsMenuBar      = NULL;
	itsOwner        = NULL;
	itsUpdateAction = kDisableSingles;

	itsArrowPosition           = kArrowAtRight;
	itsIsPopupChoiceFlag       = kFalse;
	itsIsHiddenPopupMenuFlag   = kFalse;
	itsMenuDirector            = NULL;
	itsCloseAfterSelectionFlag = kTrue;

	if (image != NULL)
		{
		SetTitleImage(image, menuOwnsImage);
		}
	else
		{
		SetTitle(title);
		}

	itsShouldBeActiveFlag = WouldBeActive();
	itsUpdateSBAFlag      = kFalse;
	Deactivate();
	itsUpdateSBAFlag      = kTrue;
}

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

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

JXMenu::~JXMenu()
{
	assert( itsMenuDirector == NULL );

	if (itsMenuBar != NULL)
		{
		assert( itsOwner == NULL );
		itsMenuBar->RemoveMenu(this);
		}
	else if (itsOwner != NULL)
		{
		assert( itsMenuBar == NULL );
		itsOwner->RemoveSubmenu(this);
		}

	delete itsTitle;
	delete itsShortcuts;
	delete itsTitleFontName;

	if (itsOwnsTitleImageFlag)
		{
		delete itsTitleImage;
		}
}

/******************************************************************************
 SetBaseItemData (protected)

	*** This must be called in the derived class constructor after the
		derived class of JXMenuData has been created.

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

void
JXMenu::SetBaseItemData
	(
	JXMenuData* baseItemData
	)
{
	assert( itsBaseItemData == NULL && baseItemData != NULL );

	itsBaseItemData = baseItemData;
	ListenTo(itsBaseItemData->GetOrderedSet());
}

/******************************************************************************
 ClearBaseItemData (protected)

	*** This must be called in the derived class destructor before the
		JXMenuData object is deleted.

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

void
JXMenu::ClearBaseItemData()
{
	assert( itsBaseItemData != NULL );

	StopListening(itsBaseItemData->GetOrderedSet());
	itsBaseItemData = NULL;
}

/******************************************************************************
 SetTitle

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

void
JXMenu::SetTitle
	(
	const JCharacter* title
	)
{
	*itsTitle  = title;
	itsULIndex = JXWindow::GetULShortcutIndex(*itsTitle, itsShortcuts);

	if (itsOwnsTitleImageFlag)
		{
		delete itsTitleImage;
		}
	itsTitleImage = NULL;

	JCoordinate w = 0;
	if (strlen(title) > 0)
		{
		w = kTitleExtraWidth +
			(GetFontManager())->GetStringWidth(*itsTitleFontName, itsTitleSize,
											   itsTitleStyle, *itsTitle);
		}
	AdjustAppearance(w);

	Refresh();
}

/******************************************************************************
 SetTitleFontName

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

void
JXMenu::SetTitleFontName
	(
	const JCharacter* fontName
	)
{
	*itsTitleFontName = fontName;

	if (itsTitleImage == NULL)
		{
		const JString title = *itsTitle;	// force recalc
		SetTitle(title);
		}
}

/******************************************************************************
 SetTitleFontSize

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

void
JXMenu::SetTitleFontSize
	(
	const JSize size
	)
{
	itsTitleSize = size;

	if (itsTitleImage == NULL)
		{
		const JString title = *itsTitle;	// force recalc
		SetTitle(title);
		}
}

/******************************************************************************
 SetTitleFontStyle

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

void
JXMenu::SetTitleFontStyle
	(
	const JFontStyle& style
	)
{
	itsTitleStyle     = style;
	itsTrueTitleColor = itsTitleStyle.color;
	if (!IsActive())
		{
		itsTitleStyle.color = (GetColormap())->GetInactiveLabelColor();
		}

	if (itsTitleImage == NULL)
		{
		const JString title = *itsTitle;	// force recalc
		SetTitle(title);
		}
}

/******************************************************************************
 SetTitleImage

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

void
JXMenu::SetTitleImage
	(
	JXImage*		image,
	const JBoolean	menuOwnsImage
	)
{
	assert( image != NULL );

	if (itsOwnsTitleImageFlag)
		{
		delete itsTitleImage;
		}

	itsTitleImage         = image;
	itsOwnsTitleImageFlag = menuOwnsImage;

	itsTitle->Clear();
	itsULIndex = 0;

	AdjustAppearance(itsTitleImage->GetWidth() + kTitleExtraWidth);
	Refresh();
}

/******************************************************************************
 IsEmpty

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

JBoolean
JXMenu::IsEmpty()
	const
{
	return itsBaseItemData->IsEmpty();
}

/******************************************************************************
 GetItemCount

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

JSize
JXMenu::GetItemCount()
	const
{
	return itsBaseItemData->GetElementCount();
}

/******************************************************************************
 RemoveAllItems

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

void
JXMenu::RemoveAllItems()
{
	itsBaseItemData->DeleteAll();
}

/******************************************************************************
 DeleteItem

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

void
JXMenu::DeleteItem
	(
	const JIndex index
	)
{
	itsBaseItemData->DeleteItem(index);
}

/******************************************************************************
 SetItemShortcuts

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

void
JXMenu::SetItemShortcuts
	(
	const JIndex		index,
	const JCharacter*	shortcuts
	)
{
	itsBaseItemData->SetItemShortcuts(index, shortcuts);
}

/******************************************************************************
 GetItemID

	Returns kTrue if the item has an id.

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

JBoolean
JXMenu::GetItemID
	(
	const JIndex	index,
	const JString**	id
	)
	const
{
	return itsBaseItemData->GetItemID(index, id);
}

/******************************************************************************
 SetItemID

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

void
JXMenu::SetItemID
	(
	const JIndex		index,
	const JCharacter*	id
	)
{
	itsBaseItemData->SetItemID(index, id);
}

/******************************************************************************
 ItemIDToIndex

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

JBoolean
JXMenu::ItemIDToIndex
	(
	const JCharacter*	targetID,
	JIndex*				index
	)
	const
{
	const JSize count = GetItemCount();
	for (JIndex i=1; i<=count; i++)
		{
		const JString* id;
		if (GetItemID(i, &id) && *id == targetID)
			{
			*index = i;
			return kTrue;
			}
		}

	*index = 0;
	return kFalse;
}

/******************************************************************************
 EnableItem

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

void
JXMenu::EnableItem
	(
	const JIndex index
	)
{
	itsBaseItemData->EnableItem(index);
}

/******************************************************************************
 EnableAll

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

void
JXMenu::EnableAll()
{
	itsBaseItemData->EnableAll();
}

/******************************************************************************
 DisableItem

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

void
JXMenu::DisableItem
	(
	const JIndex index
	)
{
	itsBaseItemData->DisableItem(index);
}

/******************************************************************************
 DisableAll

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

void
JXMenu::DisableAll()
{
	itsBaseItemData->DisableAll();
}

/******************************************************************************
 SetItemEnable (virtual protected)

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

void
JXMenu::SetItemEnable
	(
	const JIndex	index,
	const JBoolean	enabled
	)
{
	itsBaseItemData->SetItemEnable(index, enabled);
}

/******************************************************************************
 CheckItem

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

void
JXMenu::CheckItem
	(
	const JIndex index
	)
{
	itsBaseItemData->CheckItem(index);
}

/******************************************************************************
 IsChecked

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

JBoolean
JXMenu::IsChecked
	(
	const JIndex index
	)
	const
{
	return itsBaseItemData->IsChecked(index);
}

/******************************************************************************
 AttachSubmenu

	Deletes any old menu that was attached and attaches the new one.

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

void
JXMenu::AttachSubmenu
	(
	const JIndex	index,
	JXMenu*			submenu
	)
{
	itsBaseItemData->AttachSubmenu(index, submenu);
	submenu->SetOwner(this);
	submenu->Hide();
}

/******************************************************************************
 RemoveSubmenu

	Returns kTrue if there was a submenu at the given index.
	The caller now owns the menu and is responsible for deleting it.

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

JBoolean
JXMenu::RemoveSubmenu
	(
	const JIndex	index,
	JXMenu**		theMenu
	)
{
	return itsBaseItemData->RemoveSubmenu(index, theMenu);
}

// private

void
JXMenu::RemoveSubmenu
	(
	JXMenu* theMenu
	)
{
	itsBaseItemData->RemoveSubmenu(theMenu);
}

/******************************************************************************
 DeleteSubmenu

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

void
JXMenu::DeleteSubmenu
	(
	const JIndex index
	)
{
	itsBaseItemData->DeleteSubmenu(index);
}

/******************************************************************************
 GetSubmenu

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

JBoolean
JXMenu::GetSubmenu
	(
	const JIndex	index,
	const JXMenu**	menu
	)
	const
{
	return itsBaseItemData->GetSubmenu(index, const_cast<JXMenu**>(menu));
}

/******************************************************************************
 GetMenuBar

	Returns kTrue if we are part of a menu bar.

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

JBoolean
JXMenu::GetMenuBar
	(
	JXMenuBar** menuBar
	)
	const
{
	JXMenu* topMenu = GetTopLevelMenu();
	*menuBar = topMenu->itsMenuBar;
	return JConvertToBoolean( *menuBar != NULL );
}

/******************************************************************************
 GetTopLevelMenu

	Return the top level menu in our tree.

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

JXMenu*
JXMenu::GetTopLevelMenu()
	const
{
	JXMenu* topMenu = const_cast<JXMenu*>(this);
	while (topMenu->itsOwner != NULL)
		{
		topMenu = topMenu->itsOwner;
		}
	return topMenu;
}

/******************************************************************************
 SetMenuBar (private for JXMenuBar)

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

void
JXMenu::SetMenuBar
	(
	JXMenuBar* bar
	)
{
	if (itsMenuBar == bar)
		{
		return;
		}
	else if (itsMenuBar != NULL)
		{
		assert( itsOwner == NULL );
		itsMenuBar->RemoveMenu(this);
		}
	else if (itsOwner != NULL)
		{
		assert( itsMenuBar == NULL );
		itsOwner->RemoveSubmenu(this);
		}

	itsMenuBar = bar;
	itsOwner   = NULL;

	AdjustAppearance();
}

/******************************************************************************
 SetOwner (private)

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

void
JXMenu::SetOwner
	(
	JXMenu* owner
	)
{
	if (itsOwner == owner)
		{
		return;
		}
	else if (itsOwner != NULL)
		{
		assert( itsMenuBar == NULL );
		itsOwner->RemoveSubmenu(this);
		}
	else if (itsMenuBar != NULL)
		{
		assert( itsOwner == NULL );
		itsMenuBar->RemoveMenu(this);
		}

	itsMenuBar = NULL;
	itsOwner   = owner;

	AdjustAppearance();
}

/******************************************************************************
 AdjustAppearance (private)

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

void
JXMenu::AdjustAppearance
	(
	const JCoordinate minWidth
	)
{
	itsMinWidth = minWidth;

	JCoordinate w = minWidth;
	if (itsOwner == NULL && itsMenuBar == NULL)
		{
		SetBorderWidth(kJXDefaultBorderWidth);
		w += kTotalArrowWidth + 2*kJXDefaultBorderWidth;
		}
	else
		{
		SetBorderWidth(0);
		}

	if (w <= 0)
		{
		w = kTitleExtraWidth;
		}

	const JCoordinate dw = w - GetFrameWidth();
	if (dw != 0)
		{
		AdjustSize(dw, 0);
		if (itsMenuBar != NULL)
			{
			itsMenuBar->MenuWidthChanged(this, dw);
			}
		}
}

/******************************************************************************
 PrepareToOpenMenu (protected)

	Returns kFalse if the menu is completely disabled.

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

JBoolean
JXMenu::PrepareToOpenMenu()
{
	if (!IsActive() ||
		(!IsVisible() &&
		 itsOwner == NULL &&			// visibility is irrelevant for sub-menus
		 !itsIsHiddenPopupMenuFlag))	// and hidden popups
		{
		return kFalse;
		}

	// disable the requested items, uncheck all items

	itsBaseItemData->PrepareToOpenMenu(itsUpdateAction);

	// let owner update us as appropriate

	Broadcast(NeedsUpdate());
	return JConvertToBoolean( GetItemCount() > 0 );
}

/******************************************************************************
 Open (private)

	Callers can provide two different points where the menu could
	reasonably be placed (left and right for submenus).

	Returns kTrue if successful.

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

JBoolean
JXMenu::Open
	(
	const JPoint& userLeftPtR,
	const JPoint& userRightPtR
	)
{
	JXMenuManager* menuManager = GetMenuManager();
	if (itsOwner == NULL)
		{
		menuManager->CloseCurrentMenus();
		}
	assert( itsMenuDirector == NULL );

	if (!PrepareToOpenMenu())
		{
		return kFalse;
		}
	assert( !IsEmpty() );

	// create window to display menu

	JPoint leftPtR, rightPtR;
	if (itsOwner == NULL)
		{
		// place below widget frame

		JXWindow* w  = GetWindow();
		JRect frameG = GetFrameGlobal();
		leftPtR      = w->GlobalToRoot(frameG.bottomRight());
		rightPtR     = w->GlobalToRoot(frameG.bottomLeft());
		}
	else
		{
		// use suggested points

		leftPtR  = userLeftPtR;
		rightPtR = userRightPtR;
		}

	itsMenuDirector = CreateWindow((GetWindow())->GetDirector());
	itsMenuDirector->BuildWindow(leftPtR, rightPtR);
	itsMenuDirector->Activate();
	itsMenuDirector->GrabKeyboard();
	menuManager->MenuOpened(this);
	Redraw();
	return kTrue;
}

/******************************************************************************
 Close (private)

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

void
JXMenu::Close()
{
	assert( itsMenuDirector != NULL );

	(GetMenuManager())->MenuClosed(this);

	const JBoolean ok = itsMenuDirector->Close();
	assert( ok );
	itsMenuDirector = NULL;

	Redraw();
}

/******************************************************************************
 BroadcastSelection (protected)

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

void
JXMenu::BroadcastSelection
	(
	const JIndex itemIndex
	)
{
	// close all menus

	if (itsCloseAfterSelectionFlag)
		{
		(GetMenuManager())->CloseCurrentMenus();
		}

	// update the menu title to show the new selection

	if (itsIsPopupChoiceFlag)
		{
		AdjustPopupChoiceTitle(itemIndex);
		}

	// broadcast the message

	Broadcast(ItemSelected(itemIndex));		// can delete us
}

/******************************************************************************
 Draw (virtual protected)

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

void
JXMenu::Draw
	(
	JXWindowPainter&	p,
	const JRect&		rect
	)
{
	const JRect bounds = GetBounds();

	const JCoordinate borderWidth = GetBorderWidth();
	if (itsMenuDirector != NULL && borderWidth == 0)
		{
		JXDrawUpFrame(p, bounds, kJXDefaultBorderWidth);
		}

	JRect r = bounds;
	if (borderWidth > 0)
		{
		JRect ra  = bounds;
		ra.top    = bounds.ycenter() - kArrowHalfHeight;
		ra.bottom = ra.top + 2*kArrowHalfHeight;

		if (itsArrowPosition == kArrowAtRight)
			{
			ra.left  = bounds.right - (kTotalArrowWidth + kArrowWidth)/2;
			ra.right = ra.left + kArrowWidth;
			r.right -= kTotalArrowWidth;
			}
		else
			{
			assert( itsArrowPosition == kArrowAtLeft );
			ra.right = bounds.left + (kTotalArrowWidth + kArrowWidth)/2;
			ra.left  = ra.right - kArrowWidth;
			r.left  += kTotalArrowWidth;
			}

		if (IsActive())
			{
			JXDrawDownArrowDown(p, ra, kJXDefaultBorderWidth);
			}
		else
			{
			JXFillArrowDown(p, ra, (GetColormap())->GetInactiveLabelColor());
			ra.Shrink(kArrowShrinkWidth, kArrowShrinkHeight);
			JXFillArrowDown(p, ra, GetBackColor());
			}
		}

	if (itsTitleImage != NULL)
		{
		p.Image(*itsTitleImage, itsTitleImage->GetBounds(), r);
		}
	else
		{
		p.SetFont(*itsTitleFontName, itsTitleSize, itsTitleStyle);
		p.String(r.left, r.top, *itsTitle, itsULIndex,
				 r.width(), JPainter::kHAlignCenter,
				 r.height(), JPainter::kVAlignCenter);
		}
}

/******************************************************************************
 DrawBorder (virtual protected)

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

void
JXMenu::DrawBorder
	(
	JXWindowPainter&	p,
	const JRect&		frame
	)
{
	const JCoordinate borderWidth = GetBorderWidth();
	if (borderWidth > 0 && IsActive())
		{
		JXDrawUpFrame(p, frame, borderWidth);
		}
	else if (borderWidth > 0)
		{
		p.SetLineWidth(borderWidth);
		p.SetPenColor((GetColormap())->GetInactiveLabelColor());
		p.RectInside(frame);
		}
}

/******************************************************************************
 PopUp

	Call this to make the menu pop open at the specified point inside
	mouseOwner.  This must only be called from mouseOwner's HandleMouseDown()
	or HandleMouseDrag().  If this function is successful, you will already
	have received HandleMouseUp() by the time it returns.  It will return kTrue
	so you know that it succeeded and that you should return immediately.

	pt must be in the bounds coordinates of mouseOwner.  In addition, the
	menu must not be visible, because this would confuse the user.  We also
	require that the menu is not in a menu bar and is not a sub-menu.

	Try to avoid using this feature since it will usually not be obvious
	to the user that a menu can be popped up at all.

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

JBoolean
JXMenu::PopUp
	(
	JXContainer*			mouseOwner,
	const JPoint&			pt,
	const JXButtonStates&	buttonStates,
	const JXKeyModifiers&	modifiers
	)
{
	assert( itsIsHiddenPopupMenuFlag );

	if (itsMenuBar == NULL && itsOwner == NULL &&
		!IsVisible() && !buttonStates.AllOff() &&
		itsMenuDirector == NULL)
		{
		Place(pt.x, pt.y - GetFrameHeight());
		if (Open())
			{
			itsCloseAfterSelectionFlag = kTrue;
			return (GetDisplay())->SwitchDrag(mouseOwner, pt, buttonStates, modifiers,
											  itsMenuDirector->GetMenuTable());
			}
		}

	return kFalse;
}

/******************************************************************************
 HandleMouseDown (virtual protected)

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

void
JXMenu::HandleMouseDown
	(
	const JPoint&			pt,
	const JXMouseButton		button,
	const JSize				clickCount,
	const JXButtonStates&	buttonStates,
	const JXKeyModifiers&	modifiers
	)
{
	if (button == kJXLeftButton && itsMenuDirector == NULL && Open())
		{
		itsCloseAfterSelectionFlag = kTrue;		// JNegate( modifiers.meta() );

		const JBoolean ok =
			(GetDisplay())->SwitchDrag(this, pt, buttonStates, modifiers,
									   itsMenuDirector->GetMenuTable());
		if (!ok && IsOpen())	// SwitchDrag() can trigger Close()
			{
			Close();
			}
		}
}

/******************************************************************************
 HandleShortcut

	All shortcuts open the menu.

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

void
JXMenu::HandleShortcut
	(
	const int				key,
	const JXKeyModifiers&	modifiers
	)
{
	if (itsMenuDirector == NULL && Open())
		{
		itsMenuDirector->GrabPointer();
		}
}

/******************************************************************************
 SetShortcuts

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

void
JXMenu::SetShortcuts
	(
	const JCharacter* list
	)
{
	JXWindow* w = GetWindow();
	w->ClearShortcuts(this);
	w->InstallShortcuts(this, list);

	#define LabelVarName	itsTitle
	#include <JXUpdateShortcutIndex.th>
	#undef LabelVarName

	Refresh();
}

/******************************************************************************
 Activate (virtual)

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

void
JXMenu::Activate()
{
	itsShouldBeActiveFlag = kTrue;
	if ((itsBaseItemData->GetOrderedSet())->IsEmpty())
		{
		return;
		}

	const JBoolean wasActive = IsActive();
	JXWidget::Activate();
	if (!wasActive && IsActive())
		{
		itsTitleStyle.color = itsTrueTitleColor;
		if (itsTitleImage == NULL)
			{
			Refresh();
			}

		JIndex ownerItemIndex;
		if (itsOwner != NULL &&
			itsOwner->itsBaseItemData->FindSubmenu(this, &ownerItemIndex))
			{
			itsOwner->EnableItem(ownerItemIndex);
			}
		}
}

/******************************************************************************
 Deactivate (virtual)

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

void
JXMenu::Deactivate()
{
	if (itsUpdateSBAFlag)
		{
		itsShouldBeActiveFlag = kFalse;
		}

	const JBoolean wasActive = IsActive();
	JXWidget::Deactivate();
	if (wasActive && !IsActive())
		{
		itsTitleStyle.color = (GetColormap())->GetInactiveLabelColor();
		if (itsTitleImage == NULL)
			{
			Refresh();
			}

		JIndex ownerItemIndex;
		if (itsOwner != NULL &&
			itsOwner->itsBaseItemData->FindSubmenu(this, &ownerItemIndex))
			{
			itsOwner->DisableItem(ownerItemIndex);
			}
		}
}

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

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

void
JXMenu::Receive
	(
	JBroadcaster*	sender,
	const Message&	message
	)
{
	const JCollection* array = itsBaseItemData->GetOrderedSet();

	if (sender == const_cast<JCollection*>(array))
		{
		if (array->IsEmpty() && IsActive())
			{
			itsUpdateSBAFlag = kFalse;
			Deactivate();
			itsUpdateSBAFlag = kTrue;
			}
		else if (itsShouldBeActiveFlag && !IsActive())
			{
			Activate();
			}
		}
	else
		{
		JXWidget::Receive(sender, message);
		}
}

/******************************************************************************
 Menu ID routine (virtual)

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

JBoolean
JXMenu::IsMenu()
	const
{
	return kTrue;
}

/******************************************************************************
 Cast to JXTextMenu*

	Not inline because they are virtual

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

JXTextMenu*
JXMenu::CastToJXTextMenu()
{
	return NULL;
}

const JXTextMenu*
JXMenu::CastToJXTextMenu()
	const
{
	return NULL;
}

/******************************************************************************
 Cast to JXImageMenu*

	Not inline because they are virtual

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

JXImageMenu*
JXMenu::CastToJXImageMenu()
{
	return NULL;
}

const JXImageMenu*
JXMenu::CastToJXImageMenu()
	const
{
	return NULL;
}

#include <JArray.tmpls>
#define JTemplateType JXMenu
#include <JPtrArray.tmpls>
#undef JTemplateType
