//------------------------------------------------------------------------------
//	Copyright (c) 2001-2002, OpenBeOS
//
//	Permission is hereby granted, free of charge, to any person obtaining a
//	copy of this software and associated documentation files (the "Software"),
//	to deal in the Software without restriction, including without limitation
//	the rights to use, copy, modify, merge, publish, distribute, sublicense,
//	and/or sell copies of the Software, and to permit persons to whom the
//	Software is furnished to do so, subject to the following conditions:
//
//	The above copyright notice and this permission notice shall be included in
//	all copies or substantial portions of the Software.
//
//	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
//	FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
//	DEALINGS IN THE SOFTWARE.
//
//	File Name:		MenuField.cpp
//	Author:			Marc Flerackers (mflerackers@androme.be)
//	Description:	BMenuField displays a labeled pop-up menu.
//------------------------------------------------------------------------------

// Standard Includes -----------------------------------------------------------

// System Includes -------------------------------------------------------------
#include <MenuBar.h>
#include <MenuField.h>
#include <Message.h>
#include <BMCPrivate.h>
#include <Window.h>

// Project Includes ------------------------------------------------------------

// Local Includes --------------------------------------------------------------

// Local Defines ---------------------------------------------------------------

// Globals ---------------------------------------------------------------------
static float kVMargin = 2.0f;

//------------------------------------------------------------------------------
BMenuField::BMenuField(BRect frame, const char *name, const char *label,
					   BMenu *menu, uint32 resize, uint32 flags)
	:	BView(frame, name, resize, flags)
{
	InitObject(label);
	SetFont(be_plain_font);
	fMenu = menu;
	InitMenu(menu);

	frame.OffsetTo(0.0f, 0.0f);
	fMenuBar = new _BMCMenuBar_(BRect(frame.left + fDivider + 2.0f,
		frame.top + kVMargin, frame.right - 2.0f, frame.bottom - kVMargin),
		false, this);

	AddChild(fMenuBar);
	fMenuBar->AddItem(menu);

	fMenuBar->SetFont(be_plain_font);

	InitObject2();
}
//------------------------------------------------------------------------------
BMenuField::BMenuField(BRect frame, const char *name, const char *label,
					   BMenu *menu, bool fixed_size, uint32 resize,
					   uint32 flags)
	:	BView(frame, name, resize, flags)
{
	InitObject(label);
	SetFont(be_plain_font);
	fMenu = menu;
	InitMenu(menu);

	fFixedSizeMB = fixed_size;

	frame.OffsetTo(0.0f, 0.0f);
	fMenuBar = new _BMCMenuBar_(BRect(frame.left + fDivider + 2.0f,
		frame.top + kVMargin, frame.right - 2.0f, frame.bottom - kVMargin),
		fixed_size, this);

	AddChild(fMenuBar);
	fMenuBar->AddItem(new _BMCItem_(menu));

	fMenuBar->SetFont(be_plain_font);

	InitObject2();
}
//------------------------------------------------------------------------------
BMenuField::BMenuField(BMessage *data)
	:	BView(data)
{
	const char *label = NULL;

	data->FindString("_label", &label);
	
	InitObject(label);

	fMenuBar = (BMenuBar*)FindView("_mc_mb_");
	fMenu = fMenuBar->SubmenuAt(0);

	InitObject2();

	bool disable;

	if (data->FindBool("_disable", &disable) == B_OK)
		SetEnabled(!disable);

	int32 align;

	data->FindInt32("_align", &align);
		SetAlignment((alignment)align);

	data->FindFloat("_divide", &fDivider);

	bool fixed;
	
	if (data->FindBool("be:fixeds", &fixed) == B_OK)
		fFixedSizeMB = fixed;

	BMenuItem *item = fMenuBar->ItemAt(0);

	if (!item)
		return;

	_BMCItem_ *bmcitem = dynamic_cast<_BMCItem_*>(item);

	if (!bmcitem)
		return;
	
	bool dmark;

	if (data->FindBool("be:dmark", &dmark))
		bmcitem->fShowPopUpMarker = dmark;
}
//------------------------------------------------------------------------------
BMenuField::~BMenuField()
{
	if (fLabel)
		free(fLabel);

	if (fMenuTaskID >= 0)
		kill_thread(fMenuTaskID);
}
//------------------------------------------------------------------------------
BArchivable *BMenuField::Instantiate(BMessage *data)
{
	if (validate_instantiation(data, "BMenuField"))
		return new BMenuField(data);
	else
		return NULL;
}
//------------------------------------------------------------------------------
status_t BMenuField::Archive(BMessage *data, bool deep) const
{
	BView::Archive(data, deep);

	if (Label())
		data->AddString("_label", Label());

	if (!IsEnabled())
		data->AddBool("_disable", true);

	data->AddInt32("_align", Alignment());

	data->AddFloat("_divide", Divider());

	if (fFixedSizeMB)
		data->AddBool("be:fixeds", true);

	BMenuItem *item = fMenuBar->ItemAt(0);

	if (!item)
		return B_OK;

	_BMCItem_ *bmcitem = dynamic_cast<_BMCItem_*>(item);

	if (bmcitem && !bmcitem->fShowPopUpMarker)
		data->AddBool("be:dmark", false);

	return B_OK;
}
//------------------------------------------------------------------------------
void BMenuField::Draw(BRect update)
{
	BRect bounds(Bounds());
	bool active = false;

	if (IsFocus())
		active = Window()->IsActive();

	/*
	SetHighColor(0, 255, 0);
	FillRect(bounds);
	*/

	DrawLabel(bounds, update);

	BRect frame(fMenuBar->Frame());
	//rgb_color color = HighColor();

	if (frame.InsetByCopy(-2, -2).Intersects(update))
	{
		SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), B_DARKEN_2_TINT));
		StrokeLine(BPoint(frame.left - 1.0f, frame.top - 1.0f),
			BPoint(frame.left - 1.0f, frame.bottom - 1.0f));
		StrokeLine(BPoint(frame.left - 1.0f, frame.top - 1.0f),
			BPoint(frame.right - 1.0f, frame.top - 1.0f));

		StrokeLine(BPoint(frame.left + 1.0f, frame.bottom + 1.0f),
			BPoint(frame.right + 1.0f, frame.bottom + 1.0f));
		StrokeLine(BPoint(frame.right + 1.0f, frame.top + 1.0f));

		SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), B_DARKEN_4_TINT));
		StrokeLine(BPoint(frame.left - 1.0f, frame.bottom),
			BPoint(frame.left - 1.0f, frame.bottom));
		StrokeLine(BPoint(frame.right, frame.top - 1.0f),
			BPoint(frame.right, frame.top - 1.0f));

	}

	if (active || fTransition)
	{
		SetHighColor(active ? ui_color(B_KEYBOARD_NAVIGATION_COLOR) :
			ViewColor());
		StrokeRect(frame.InsetByCopy(-kVMargin, -kVMargin));

		fTransition = false;
	}
}
//------------------------------------------------------------------------------
void BMenuField::AttachedToWindow()
{
	if (Parent())
	{
		SetViewColor(Parent()->ViewColor());
		SetLowColor(Parent()->ViewColor());
	}

	if (fLabel)
			fStringWidth = StringWidth(fLabel);
}
//------------------------------------------------------------------------------
void BMenuField::AllAttached()
{
	ResizeTo(Bounds().Width(),
		fMenuBar->Bounds().Height() + kVMargin + kVMargin);
}
//------------------------------------------------------------------------------
void BMenuField::MouseDown(BPoint where)
{
	if (where.x > fDivider && !fMenuBar->Frame().Contains(where))
		return;

	BRect bounds(Bounds());

	fMenuBar->StartMenuBar(0, false, true,
		&fMenuBar->ConvertFromParent(bounds));

	fMenuTaskID = spawn_thread((thread_func)MenuTask, "_m_task_",
		B_NORMAL_PRIORITY, this);

	if (fMenuTaskID)
		resume_thread(fMenuTaskID);
}
//------------------------------------------------------------------------------
void BMenuField::KeyDown(const char *bytes, int32 numBytes)
{
	switch (bytes[0])
	{
		case B_SPACE:
		case B_RIGHT_ARROW:
		case B_DOWN_ARROW:
		{
			BRect bounds(Bounds());

			fMenuBar->StartMenuBar(0, true, true,
				&fMenuBar->ConvertFromParent(bounds));

			fSelected = true;
			fTransition = true;

			bounds = Bounds();
			bounds.right = fDivider;

			Draw(bounds);
		}
		default:
			BView::KeyDown(bytes, numBytes);
	}
}
//------------------------------------------------------------------------------
void BMenuField::MakeFocus(bool state)
{
	if (IsFocus() == state)
		return;

	BView::MakeFocus(state);

	if(Window())
		Draw(Bounds()); // TODO: use fStringWidth
}
//------------------------------------------------------------------------------
void BMenuField::MessageReceived(BMessage *msg)
{
	BView::MessageReceived(msg);
}
//------------------------------------------------------------------------------
void BMenuField::WindowActivated(bool state)
{
	BView::WindowActivated(state);

	if (IsFocus())
		Draw(Bounds());
}
//------------------------------------------------------------------------------
void BMenuField::MouseUp(BPoint pt)
{
	BView::MouseUp(pt);
}
//------------------------------------------------------------------------------
void BMenuField::MouseMoved(BPoint pt, uint32 code, const BMessage *msg)
{
	BView::MouseMoved(pt, code, msg);
}
//------------------------------------------------------------------------------
void BMenuField::DetachedFromWindow()
{
	BView::DetachedFromWindow();
}
//------------------------------------------------------------------------------
void BMenuField::AllDetached()
{
	BView::AllDetached();
}
//------------------------------------------------------------------------------
void BMenuField::FrameMoved(BPoint new_position)
{
	BView::FrameMoved(new_position);
}
//------------------------------------------------------------------------------
void BMenuField::FrameResized(float new_width, float new_height)
{
	BView::FrameResized(new_width, new_height);
}
//------------------------------------------------------------------------------
BMenu *BMenuField::Menu() const
{
	return fMenu;
}
//------------------------------------------------------------------------------
BMenuBar *BMenuField::MenuBar() const
{
	return fMenuBar;
}
//------------------------------------------------------------------------------
BMenuItem *BMenuField::MenuItem() const
{
	return fMenuBar->ItemAt(0);
}
//------------------------------------------------------------------------------
void BMenuField::SetLabel(const char *label)
{
	if (fLabel)
	{
		if (label && strcmp(fLabel, label) == 0)
			return;

		free(fLabel);
	}

	fLabel = strdup(label);

	if (Window())
	{
		Invalidate();
		if (fLabel)
			fStringWidth = StringWidth(fLabel);
	}
}
//------------------------------------------------------------------------------
const char *BMenuField::Label() const
{
	return fLabel;
}
//------------------------------------------------------------------------------
void BMenuField::SetEnabled(bool on)
{
	if (fEnabled == on)
		return;

	fEnabled = on;
	fMenuBar->SetEnabled(on);

	if (Window())
	{
		fMenuBar->Invalidate(fMenuBar->Bounds());
		Invalidate(Bounds());
	}
}
//------------------------------------------------------------------------------
bool BMenuField::IsEnabled() const
{
	return fEnabled;
}
//------------------------------------------------------------------------------
void BMenuField::SetAlignment(alignment label)
{
	fAlign = label;
}
//------------------------------------------------------------------------------
alignment BMenuField::Alignment() const
{
	return fAlign;
}
//------------------------------------------------------------------------------
void BMenuField::SetDivider(float dividing_line)
{
	if (fDivider - dividing_line == 0.0f)
		return;

	MenuBar()->MoveBy(dividing_line - fDivider, 0.0f);
	MenuBar()->ResizeBy(fDivider - dividing_line, 0.0f);

	fDivider = dividing_line;
}
//------------------------------------------------------------------------------
float BMenuField::Divider() const
{
	return fDivider;
}
//------------------------------------------------------------------------------
void BMenuField::ShowPopUpMarker()
{
	// TODO:
}
//------------------------------------------------------------------------------
void BMenuField::HidePopUpMarker()
{
	// TODO:
}
//------------------------------------------------------------------------------
BHandler *BMenuField::ResolveSpecifier(BMessage *msg,
								int32 index,
								BMessage *specifier,
								int32 form,
								const char *property)
{
	return BView::ResolveSpecifier(msg, index, specifier, form, property);
}
//------------------------------------------------------------------------------
status_t BMenuField::GetSupportedSuites(BMessage *data)
{
	return BView::GetSupportedSuites(data);
}
//------------------------------------------------------------------------------
void BMenuField::ResizeToPreferred()
{
	BView::ResizeToPreferred();
}
//------------------------------------------------------------------------------
void BMenuField::GetPreferredSize(float *width, float *height)
{
	BView::GetPreferredSize(width, height);
}
//------------------------------------------------------------------------------
status_t BMenuField::Perform(perform_code d, void *arg)
{
	return BView::Perform(d, arg);
}
//------------------------------------------------------------------------------
void BMenuField::_ReservedMenuField1() {}
void BMenuField::_ReservedMenuField2() {}
void BMenuField::_ReservedMenuField3() {}
//------------------------------------------------------------------------------
BMenuField &BMenuField::operator=(const BMenuField &)
{
	return *this;
}
//------------------------------------------------------------------------------
void BMenuField::InitObject(const char *label)
{
	fLabel = NULL;
	fMenu = NULL;
	fMenuBar = NULL;
	fAlign = B_ALIGN_LEFT;
	fStringWidth = 0;
	fEnabled = true;
	fSelected = false;
	fTransition = false;
	fFixedSizeMB = false;
	fMenuTaskID = -1;

	SetLabel(label);

	if (label)
		fDivider = (float)floor(Frame().Width() / 2.0f);
	else
		fDivider = 0;
}
//------------------------------------------------------------------------------
void BMenuField::InitObject2()
{
	// TODO set filter
}
//------------------------------------------------------------------------------
void BMenuField::DrawLabel(BRect bounds, BRect update)
{
	font_height fh;
	GetFontHeight(&fh);

	if (Label())
	{
		SetLowColor(ViewColor());

		float y = (float)ceil(fh.ascent + fh.descent + fh.leading) + 2.0f;
		float x;

		switch (fAlign)
		{
			case B_ALIGN_RIGHT:
				x = fDivider - StringWidth(Label()) - 3.0f;
				break;

			case B_ALIGN_CENTER:
				x = fDivider - StringWidth(Label()) / 2.0f;
				break;

			default:
				x = 3.0f;
				break;
		}
		
		SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
					 IsEnabled() ? B_DARKEN_MAX_TINT : B_DISABLED_LABEL_TINT));
		DrawString(Label(), BPoint(x, y));
	}
}
//------------------------------------------------------------------------------
void BMenuField::InitMenu(BMenu *menu)
{
	menu->SetFont(be_plain_font);

	int32 index = 0;
	BMenu *subMenu;

	while (subMenu = menu->SubmenuAt(index++))
		InitMenu(subMenu);
}
//------------------------------------------------------------------------------
long BMenuField::MenuTask(void *arg)
{
	BMenuField *menuField = (BMenuField*)arg;

	if (!menuField->LockLooper())
		return 0;

	menuField->fSelected = true;
	menuField->fTransition = true;
	menuField->Draw(menuField->Bounds());

	menuField->UnlockLooper();

	bool tracking;

	do
	{
		snooze(20000);

		if (!menuField->LockLooper())
			return 0;

		tracking = menuField->fMenuBar->fTracking;

		menuField->UnlockLooper();
	} while (tracking);

	if (!menuField->LockLooper())
		return 0;

	menuField->fSelected = false;
	menuField->fTransition = true;
	menuField->Draw(menuField->Bounds());

	menuField->UnlockLooper();

	return 0;
}
//------------------------------------------------------------------------------

