/******************************************************************************
 JXStyleMenu.cc

	Base class for changing font styles.  This menu is an action menu, so all
	messages that are broadcast are meaningless to outsiders.

	Derived classes must override the following functions:

		GetFontStyleForMenuUpdate
			Return the current font style so the appropriate checkboxes
			can be set on the menu.

		HandleMenuItem
			Respond to the selected menu item.  UpdateStyle() is provided
			for convenience.

	If a derived class turns on custom colors, it must override:

		HandleCustomColor
			At least call JColormap::UsingColor() so the color won't be
			lost when the menu widget is deleted.

	Derived classes can also override the following functions:

		UpdateMenu
			Derived classes can override to enable/disable items before
			calling this function.

	BASE CLASS = JXTextMenu

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

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

#include <JXStyleMenu.h>
#include <JXStyleMenuDirector.h>
#include <JXChooseColorDialog.h>
#include <JXWindow.h>
#include <JXColormap.h>
#include <jAssert.h>

static const JCharacter* kStyleMenuTitleStr    = "Style";
static const JCharacter* kStyleMenuShortcutStr = "#S";

static const JCharacter* kMacMenuStr =
	"Plain %b %k Meta-T"
	"%l| Bold %b %k Meta-B | Italic %b %k Meta-I"
	"| Underline %b %k Meta-U | Double underline %b"
	"| Strike %b"
	"%l| Black %r | Gray %r | Brown %r | Orange %r | Red %r | Dark red %r"
	"| Green %r | Blue %r | Light blue %r | Pink %r %l | Other %r";

static const JCharacter* kWinMenuStr =
	"Plain %b %h p %k Ctrl-T"
	"%l| Bold %b %h b %kCtrl-B | Italic %b %h i %k Ctrl-I"
	"| Underline %b %h u %k Ctrl-U | Double underline %b %h d"
	"| Strike %b %h s"
	"%l| Black %r | Gray %r | Brown %r | Orange %r | Red %r | Dark red %r"
	"| Green %r | Blue %r | Light blue %r | Pink %r %l | Other %r";

// remember to update kColorCount and JXStyleMenuX()

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

	Because of egcs thunks bug, derived classes must implement Create()
	and call JXStyleMenuX().

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

JXStyleMenu::JXStyleMenu
	(
	const JXMenu::Style	menuStyle,
	const JBoolean		allowChooseCustomColors,
	JXContainer*		enclosure,
	const HSizingOption	hSizing,
	const VSizingOption	vSizing,
	const JCoordinate	x,
	const JCoordinate	y,
	const JCoordinate	w,
	const JCoordinate	h
	)
	:
	JXTextMenu(kStyleMenuTitleStr, enclosure, hSizing, vSizing, x,y, w,h),
	itsChooseCustomColorFlag(allowChooseCustomColors)
{
//	JXStyleMenuX(menuStyle);	// egcs thunks
}

JXStyleMenu::JXStyleMenu
	(
	const JXMenu::Style	menuStyle,
	const JBoolean		allowChooseCustomColors,
	JXMenu*				owner,
	const JIndex		itemIndex,
	JXContainer*		enclosure
	)
	:
	JXTextMenu(owner, itemIndex, enclosure),
	itsChooseCustomColorFlag(allowChooseCustomColors)
{
//	JXStyleMenuX(menuStyle);	// egcs thunks
}

// private -- protected for egcs thunks bug

void
JXStyleMenu::JXStyleMenuX
	(
	const JXMenu::Style menuStyle
	)
{
	itsChooseColorDialog = NULL;

	if (menuStyle == kMacintoshStyle)
		{
		SetMenuItems(kMacMenuStr);
		}
	else
		{
		SetShortcuts(kStyleMenuShortcutStr);
		SetMenuItems(kWinMenuStr);
		}

	JXColormap* colormap = GetColormap();
	const JColorIndex blackColor = colormap->GetBlackColor();

	SetItemFontStyle(
		kBoldStyleCmd, JFontStyle(kTrue, kFalse, 0, kFalse, blackColor));
	SetItemFontStyle(
		kItalicStyleCmd, JFontStyle(kFalse, kTrue, 0, kFalse, blackColor));
	SetItemFontStyle(
		kUnderlineStyleCmd, JFontStyle(kFalse, kFalse, 1, kFalse, blackColor));
	SetItemFontStyle(
		kDblUnderlineStyleCmd, JFontStyle(kFalse, kFalse, 2, kFalse, blackColor));
	SetItemFontStyle(
		kStrikeStyleCmd, JFontStyle(kFalse, kFalse, 0, kTrue, blackColor));

	assert( kColorCount == 11 );
	itsColorList[ 0] = blackColor;
	itsColorList[ 1] = colormap->GetGray60Color();
	itsColorList[ 2] = colormap->GetBrownColor();
	itsColorList[ 3] = colormap->GetOrangeColor();
	itsColorList[ 4] = colormap->GetRedColor();
	itsColorList[ 5] = colormap->GetDarkRedColor();
	itsColorList[ 6] = colormap->GetDarkGreenColor();
	itsColorList[ 7] = colormap->GetBlueColor();
	itsColorList[ 8] = colormap->GetLightBlueColor();
	itsColorList[ 9] = colormap->GetPinkColor();
	itsColorList[10] = colormap->GetDefaultBackColor();

	ListenTo(this);
}

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

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

JXStyleMenu::~JXStyleMenu()
{
	(GetColormap())->DeallocateColor(itsColorList[ kColorCount-1 ]);
}

/******************************************************************************
 CreateWindow (virtual protected)

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

JXMenuDirector*
JXStyleMenu::CreateWindow
	(
	JXWindowDirector* supervisor
	)
{
	JXStyleMenuDirector* dir =
		new JXStyleMenuDirector(supervisor, this, GetTextMenuData());
	assert( dir != NULL );
	return dir;
}

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

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

void
JXStyleMenu::Receive
	(
	JBroadcaster*	sender,
	const Message&	message
	)
{
	if (sender == this && message.Is(JXMenu::kNeedsUpdate))
		{
		UpdateMenu();
		}
	else if (sender == this && message.Is(JXMenu::kItemSelected))
		{
		const JXMenu::ItemSelected* selection =
			dynamic_cast(const JXMenu::ItemSelected*, &message);
		assert( selection != NULL );

		const JIndex i = selection->GetIndex();
		if (i == kCustomColorCmd)
			{
			assert( itsChooseCustomColorFlag );
			ChooseColor();
			}
		else
			{
			if (i >= kFirstColorCmd)
				{
				itsColorIndex = IndexToColor(i);
				}
			HandleMenuItem(i);
			}
		}

	else if (sender == itsChooseColorDialog &&
			 message.Is(JXDialogDirector::kDeactivated))
		{
		const JXDialogDirector::Deactivated* info =
			dynamic_cast(const JXDialogDirector::Deactivated*, &message);
		assert( info != NULL );
		if (info->Successful())
			{
			itsColorIndex = itsChooseColorDialog->GetColor();
			SetCustomColor(itsColorIndex);
			HandleCustomColor(itsColorIndex);
			}
		itsChooseColorDialog = NULL;
		}

	else
		{
		JXTextMenu::Receive(sender, message);
		}
}

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

	Derived classes can override to enable/disable items before calling this
	function.

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

void
JXStyleMenu::UpdateMenu()
{
	const JFontStyle style = GetFontStyleForMenuUpdate();

	// styles

	JBoolean plain = kTrue;

	if (style.bold)
		{
		CheckItem(kBoldStyleCmd);
		plain = kFalse;
		}

	if (style.italic)
		{
		CheckItem(kItalicStyleCmd);
		plain = kFalse;
		}

	if (style.underlineCount == 1)
		{
		CheckItem(kUnderlineStyleCmd);
		plain = kFalse;
		}
	else if (style.underlineCount == 2)
		{
		CheckItem(kDblUnderlineStyleCmd);
		plain = kFalse;
		}
	else if (style.underlineCount > 0)
		{
		// no menu item to check, but it's not plain
		plain = kFalse;
		}

	if (style.strike)
		{
		CheckItem(kStrikeStyleCmd);
		plain = kFalse;
		}

	if (plain && style.color == (GetColormap())->GetBlackColor())
		{
		CheckItem(kPlainStyleCmd);
		}

	// color

	itsColorIndex = style.color;
	CheckItem(ColorToIndex(itsColorIndex));

	if (!itsChooseCustomColorFlag)
		{
		DisableItem(kCustomColorCmd);
		}
}

/******************************************************************************
 UpdateStyle (protected)

	Convenience function that can be called from HandleMenuItem().

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

void
JXStyleMenu::UpdateStyle
	(
	const JIndex	index,
	JFontStyle*		style
	)
{
	if (index == kPlainStyleCmd)
		{
		*style = JFontStyle();
		}

	else if (index == kBoldStyleCmd)
		{
		style->bold = JNegate(style->bold);
		}

	else if (index == kItalicStyleCmd)
		{
		style->italic = JNegate(style->italic);
		}

	else if (index == kUnderlineStyleCmd && style->underlineCount != 1)
		{
		style->underlineCount = 1;
		}
	else if (index == kUnderlineStyleCmd)
		{
		style->underlineCount = 0;
		}

	else if (index == kDblUnderlineStyleCmd && style->underlineCount != 2)
		{
		style->underlineCount = 2;
		}
	else if (index == kDblUnderlineStyleCmd)
		{
		style->underlineCount = 0;
		}

	else if (index == kStrikeStyleCmd)
		{
		style->strike = JNegate(style->strike);
		}

	else if (index >= kFirstColorCmd)
		{
		style->color = itsColorIndex;
		}
}

/******************************************************************************
 ChooseColor (private)

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

void
JXStyleMenu::ChooseColor()
{
	assert( itsChooseColorDialog == NULL );

	JXWindowDirector* supervisor = (GetWindow())->GetDirector();
	itsChooseColorDialog =
		new JXChooseColorDialog(supervisor, IndexToColor(kCustomColorCmd));
	assert( itsChooseColorDialog != NULL );

	ListenTo(itsChooseColorDialog);
	itsChooseColorDialog->BeginDialog();
}

/******************************************************************************
 HandleCustomColor (virtual protected)

	If a derived class turns on custom colors, it must override this function.

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

void
JXStyleMenu::HandleCustomColor
	(
	const JColorIndex color
	)
{
	assert( 0 /* The programmer forgot to override JXStyleMenu::HandleCustomColor() */ );
}

/******************************************************************************
 IndexToColor (protected)

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

JColorIndex
JXStyleMenu::IndexToColor
	(
	const JIndex menuIndex
	)
	const
{
	assert( kFirstColorCmd <= menuIndex && menuIndex <= kCustomColorCmd );
	return itsColorList[ menuIndex - kFirstColorCmd ];
}

/******************************************************************************
 ColorToIndex (protected)

	Since this function must never fail, we use the "other" item to display
	the color if it is not in our list.

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

JIndex
JXStyleMenu::ColorToIndex
	(
	const JColorIndex color
	)
	const
{
	for (JIndex i=0; i<kColorCount-1; i++)
		{
		if (itsColorList[i] == color)
			{
			return kFirstColorCmd + i;
			}
		}

	// change color associated with "other" and return this index

	const_cast<JXStyleMenu*>(this)->SetCustomColor(color);
	return kCustomColorCmd;
}

/******************************************************************************
 SetCustomColor (private)

	Change the color associated with "other".

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

void
JXStyleMenu::SetCustomColor
	(
	const JColorIndex color
	)
{
	const JColorIndex origColor = itsColorList[ kColorCount-1 ];
	if (origColor != color)
		{
		itsColorList[ kColorCount-1 ] = color;

		JXColormap* colormap = GetColormap();
		colormap->DeallocateColor(origColor);
		colormap->UsingColor(color);
		}
}
