//**************************************************************************
//**
//**	##   ##    ##    ##   ##   ####     ####   ###     ###
//**	##   ##  ##  ##  ##   ##  ##  ##   ##  ##  ####   ####
//**	 ## ##  ##    ##  ## ##  ##    ## ##    ## ## ## ## ##
//**	 ## ##  ########  ## ##  ##    ## ##    ## ##  ###  ##
//**	  ###   ##    ##   ###    ##  ##   ##  ##  ##       ##
//**	   #    ##    ##    #      ####     ####   ##       ##
//**
//**	$Id: ui_widget.cpp 4297 2010-06-03 22:49:00Z firebrand_kh $
//**
//**	Copyright (C) 1999-2006 Jānis Legzdiņš
//**
//**	This program is free software; you can redistribute it and/or
//**  modify it under the terms of the GNU General Public License
//**  as published by the Free Software Foundation; either version 2
//**  of the License, or (at your option) any later version.
//**
//**	This program is distributed in the hope that it will be useful,
//**  but WITHOUT ANY WARRANTY; without even the implied warranty of
//**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//**  GNU General Public License for more details.
//**
//**************************************************************************

// HEADER FILES ------------------------------------------------------------

#include "gamedefs.h"
#include "cl_local.h"
#include "ui.h"

// MACROS ------------------------------------------------------------------

// TYPES -------------------------------------------------------------------

// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------

// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------

// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------

// EXTERNAL DATA DECLARATIONS ----------------------------------------------

// PUBLIC DATA DEFINITIONS -------------------------------------------------

IMPLEMENT_CLASS(V, Widget);

// PRIVATE DATA DEFINITIONS ------------------------------------------------

// CODE --------------------------------------------------------------------

//==========================================================================
//
//	VWidget::CreateNewWidget
//
//==========================================================================

VWidget* VWidget::CreateNewWidget(VClass* AClass, VWidget* AParent)
{
	guard(VWidget::CreateNewWidget);
	VWidget* W = (VWidget*)StaticSpawnObject(AClass);
	W->Init(AParent);
	return W;
	unguardf(("(%s)", AClass->GetName()));
}

//==========================================================================
//
//	VWidget::Init
//
//==========================================================================

void VWidget::Init(VWidget* AParent)
{
	guard(VWidget::Init);
	// Set default values
	SetFont(SmallFont);
	SetTextAlign(hleft, vtop);

	ParentWidget = AParent;
	if (ParentWidget)
	{
		ParentWidget->AddChild(this);
	}
	ClipTree();
	OnCreate();
	unguard;
}

//==========================================================================
//
//	VWidget::Destroy
//
//==========================================================================

void VWidget::Destroy()
{
	guard(VWidget::Destroy);
	OnDestroy();
	DestroyAllChildren();
	if (ParentWidget)
	{
		ParentWidget->RemoveChild(this);
	}
	Super::Destroy();
	unguard;
}

//==========================================================================
//
//	VWidget::AddChild
//
//==========================================================================

void VWidget::AddChild(VWidget* NewChild)
{
	guard(VWidget::AddChild);
	NewChild->PrevWidget = LastChildWidget;
	NewChild->NextWidget = NULL;
	if (LastChildWidget)
	{
		LastChildWidget->NextWidget = NewChild;
	}
	else
	{
		FirstChildWidget = NewChild;
	}
	LastChildWidget = NewChild;
	OnChildAdded(NewChild);
	if (!CurrentFocusChild)
	{
		SetCurrentFocusChild(NewChild);
	}
	unguard;
}

//==========================================================================
//
//	VWidget::RemoveChild
//
//==========================================================================

void VWidget::RemoveChild(VWidget* InChild)
{
	guard(VWidget::RemoveChild);
	if (InChild->PrevWidget)
	{
		InChild->PrevWidget->NextWidget = InChild->NextWidget;
	}
	else
	{
		FirstChildWidget = InChild->NextWidget;
	}
	if (InChild->NextWidget)
	{
		InChild->NextWidget->PrevWidget = InChild->PrevWidget;
	}
	else
	{
		LastChildWidget = InChild->PrevWidget;
	}
	InChild->PrevWidget = NULL;
	InChild->NextWidget = NULL;
	InChild->ParentWidget = NULL;
	OnChildRemoved(InChild);
	if (CurrentFocusChild == InChild)
	{
		FindNewFocus();
	}
	unguard;
}

//==========================================================================
//
//	VWidget::DestroyAllChildren
//
//==========================================================================

void VWidget::DestroyAllChildren()
{
	guard(VWidget::DestroyAllChildren);
	while (FirstChildWidget)
	{
		FirstChildWidget->ConditionalDestroy();
	}
	unguard;
}

//==========================================================================
//
//	VWidget::GetRootWidget
//
//==========================================================================

VRootWidget* VWidget::GetRootWidget()
{
	guard(VWidget::GetRootWidget);
	VWidget* W = this;
	while (W->ParentWidget)
	{
		W = W->ParentWidget;
	}
	return (VRootWidget*)W;
	unguard;
}

//==========================================================================
//
//	VWidget::Lower
//
//==========================================================================

void VWidget::Lower()
{
	guard(VWidget::Lower);
	if (!ParentWidget)
	{
		Sys_Error("Can't lower root window");
	}

	if (ParentWidget->FirstChildWidget == this)
	{
		//	Already there
		return;
	}

	//	Unlink from current location
	PrevWidget->NextWidget = NextWidget;
	if (NextWidget)
	{
		NextWidget->PrevWidget = PrevWidget;
	}
	else
	{
		ParentWidget->LastChildWidget = PrevWidget;
	}

	//	Link on bottom
	PrevWidget = NULL;
	NextWidget = ParentWidget->FirstChildWidget;
	ParentWidget->FirstChildWidget->PrevWidget = this;
	ParentWidget->FirstChildWidget = this;
	unguard;
}

//==========================================================================
//
//	VWidget::Raise
//
//==========================================================================

void VWidget::Raise()
{
	guard(VWidget::Raise);
	if (!ParentWidget)
	{
		Sys_Error("Can't raise root window");
	}

	if (ParentWidget->LastChildWidget == this)
	{
		//	Already there
		return;
	}

	//	Unlink from current location
	NextWidget->PrevWidget = PrevWidget;
	if (PrevWidget)
	{
		PrevWidget->NextWidget = NextWidget;
	}
	else
	{
		ParentWidget->FirstChildWidget = NextWidget;
	}

	//	Link on top
	PrevWidget = ParentWidget->LastChildWidget;
	NextWidget = NULL;
	ParentWidget->LastChildWidget->NextWidget = this;
	ParentWidget->LastChildWidget = this;
	unguard;
}

//==========================================================================
//
//	VWidget::MoveBefore
//
//==========================================================================

void VWidget::MoveBefore(VWidget* Other)
{
	guard(VWidget::MoveBefore);
	if (ParentWidget != Other->ParentWidget)
	{
		Sys_Error("Must have the same parent widget");
	}
	if (Other == this)
	{
		Sys_Error("Can't move before self");
	}

	if (Other->PrevWidget == this)
	{
		//	Already there
		return;
	}

	//	Unlink from current location
	if (PrevWidget)
	{
		PrevWidget->NextWidget = NextWidget;
	}
	else
	{
		ParentWidget->FirstChildWidget = NextWidget;
	}
	if (NextWidget)
	{
		NextWidget->PrevWidget = PrevWidget;
	}
	else
	{
		ParentWidget->LastChildWidget = PrevWidget;
	}

	//	Link in new position
	PrevWidget = Other->PrevWidget;
	NextWidget = Other;
	Other->PrevWidget = this;
	if (PrevWidget)
	{
		PrevWidget->NextWidget = this;
	}
	else
	{
		ParentWidget->FirstChildWidget = this;
	}
	unguard;
}

//==========================================================================
//
//	VWidget::MoveAfter
//
//==========================================================================

void VWidget::MoveAfter(VWidget* Other)
{
	guard(VWidget::MoveAfter);
	if (ParentWidget != Other->ParentWidget)
	{
		Sys_Error("Must have the same parent widget");
	}
	if (Other == this)
	{
		Sys_Error("Can't move after self");
	}

	if (Other->NextWidget == this)
	{
		//	Already there
		return;
	}

	//	Unlink from current location
	if (PrevWidget)
	{
		PrevWidget->NextWidget = NextWidget;
	}
	else
	{
		ParentWidget->FirstChildWidget = NextWidget;
	}
	if (NextWidget)
	{
		NextWidget->PrevWidget = PrevWidget;
	}
	else
	{
		ParentWidget->LastChildWidget = PrevWidget;
	}

	//	Link in new position
	NextWidget = Other->NextWidget;
	PrevWidget = Other;
	Other->NextWidget = this;
	if (NextWidget)
	{
		NextWidget->PrevWidget = this;
	}
	else
	{
		ParentWidget->LastChildWidget = this;
	}
	unguard;
}

//==========================================================================
//
//	VWidget::ClipTree
//
//==========================================================================

void VWidget::ClipTree()
{
	guard(VWidget::ClipTree);
	//	Set up clipping rectangle.
	if (ParentWidget)
	{
		//	Clipping rectangle is relative to the parent widget.
		ClipRect.OriginX = ParentWidget->ClipRect.OriginX +
			ParentWidget->ClipRect.ScaleX * PosX;
		ClipRect.OriginY = ParentWidget->ClipRect.OriginY +
			ParentWidget->ClipRect.ScaleY * PosY;
		ClipRect.ScaleX = ParentWidget->ClipRect.ScaleX * SizeScaleX;
		ClipRect.ScaleY = ParentWidget->ClipRect.ScaleY * SizeScaleY;
		ClipRect.ClipX1 = ClipRect.OriginX;
		ClipRect.ClipY1 = ClipRect.OriginY;
		ClipRect.ClipX2 = ClipRect.OriginX + ClipRect.ScaleX * SizeWidth;
		ClipRect.ClipY2 = ClipRect.OriginY + ClipRect.ScaleY * SizeHeight;

		//	Clip against the parent widget's clipping rectangle.
		if (ClipRect.ClipX1 < ParentWidget->ClipRect.ClipX1)
		{
			ClipRect.ClipX1 = ParentWidget->ClipRect.ClipX1;
		}
		if (ClipRect.ClipY1 < ParentWidget->ClipRect.ClipY1)
		{
			ClipRect.ClipY1 = ParentWidget->ClipRect.ClipY1;
		}
		if (ClipRect.ClipX2 > ParentWidget->ClipRect.ClipX2)
		{
			ClipRect.ClipX2 = ParentWidget->ClipRect.ClipX2;
		}
		if (ClipRect.ClipY2 > ParentWidget->ClipRect.ClipY2)
		{
			ClipRect.ClipY2 = ParentWidget->ClipRect.ClipY2;
		}
	}
	else
	{
		//	This is the root widget.
		ClipRect.OriginX = PosX;
		ClipRect.OriginY = PosY;
		ClipRect.ScaleX = SizeScaleX;
		ClipRect.ScaleY = SizeScaleY;
		ClipRect.ClipX1 = ClipRect.OriginX;
		ClipRect.ClipY1 = ClipRect.OriginY;
		ClipRect.ClipX2 = ClipRect.OriginX + ClipRect.ScaleX * SizeWidth;
		ClipRect.ClipY2 = ClipRect.OriginY + ClipRect.ScaleY * SizeHeight;
	}

	//	Set up clipping rectangles in child widgets.
	for (VWidget* W = FirstChildWidget; W; W = W->NextWidget)
	{
		W->ClipTree();
	}
	unguard;
}

//==========================================================================
//
//	VWidget::SetConfiguration
//
//==========================================================================

void VWidget::SetConfiguration(int NewX, int NewY, int NewWidth,
	int HewHeight, float NewScaleX, float NewScaleY)
{
	guard(VWidget::SetConfiguration);
	PosX = NewX;
	PosY = NewY;
	SizeWidth = NewWidth;
	SizeHeight = HewHeight;
	SizeScaleX = NewScaleX;
	SizeScaleY = NewScaleY;
	ClipTree();
	OnConfigurationChanged();
	unguard;
}

//==========================================================================
//
//	VWidget::SetVisibility
//
//==========================================================================

void VWidget::SetVisibility(bool NewVisibility)
{
	guard(VWidget::SetVisibility);
	if (!!(WidgetFlags & WF_IsVisible) != NewVisibility)
	{
		if (NewVisibility)
		{
			WidgetFlags |= WF_IsVisible;
			if (ParentWidget && !ParentWidget->CurrentFocusChild)
			{
				ParentWidget->SetCurrentFocusChild(this);
			}
		}
		else
		{
			WidgetFlags &= ~WF_IsVisible;
			if (ParentWidget && ParentWidget->CurrentFocusChild == this)
			{
				ParentWidget->FindNewFocus();
			}
		}
		OnVisibilityChanged(NewVisibility);
	}
	unguard;
}

//==========================================================================
//
//	VWidget::SetEnabled
//
//==========================================================================

void VWidget::SetEnabled(bool NewEnabled)
{
	guard(VWidget::SetEnabled);
	if (!!(WidgetFlags & WF_IsEnabled) != NewEnabled)
	{
		if (NewEnabled)
		{
			WidgetFlags |= WF_IsEnabled;
			if (ParentWidget && !ParentWidget->CurrentFocusChild)
			{
				ParentWidget->SetCurrentFocusChild(this);
			}
		}
		else
		{
			WidgetFlags &= ~WF_IsEnabled;
			if (ParentWidget && ParentWidget->CurrentFocusChild == this)
			{
				ParentWidget->FindNewFocus();
			}
		}
		OnEnableChanged(NewEnabled);
	}
	unguard;
}

//==========================================================================
//
//	VWidget::SetFocusable
//
//==========================================================================

void VWidget::SetFocusable(bool NewFocusable)
{
	guard(VWidget::SetFocusable);
	if (!!(WidgetFlags & WF_IsFocusable) != NewFocusable)
	{
		if (NewFocusable)
		{
			WidgetFlags |= WF_IsFocusable;
			if (ParentWidget && !ParentWidget->CurrentFocusChild)
			{
				ParentWidget->SetCurrentFocusChild(this);
			}
		}
		else
		{
			WidgetFlags &= ~WF_IsFocusable;
			if (ParentWidget && ParentWidget->CurrentFocusChild == this)
			{
				ParentWidget->FindNewFocus();
			}
		}
		OnFocusableChanged(NewFocusable);
	}
	unguard;
}

//==========================================================================
//
//	VWidget::SetCurrentFocusChild
//
//==========================================================================

void VWidget::SetCurrentFocusChild(VWidget* NewFocus)
{
	guard(VWidget::SetCurrentFocusChild);
	//	Check f it's already focused.
	if (CurrentFocusChild == NewFocus)
	{
		return;
	}

	//	Make sure it's visible, enabled and focusable.
	if (NewFocus && (!(NewFocus->WidgetFlags & WF_IsVisible) ||
		!(NewFocus->WidgetFlags & WF_IsEnabled) ||
		!(NewFocus->WidgetFlags & WF_IsFocusable)))
	{
		return;
	}

	//	If we have a focused child, send focus lost event.
	if (CurrentFocusChild)
	{
		CurrentFocusChild->OnFocusLost();
	}

	//	Make it the current focus.
	CurrentFocusChild = NewFocus;
	if (CurrentFocusChild)
	{
		CurrentFocusChild->OnFocusReceived();
	}
	unguard;
}

//==========================================================================
//
//	VWidget::IsFocus
//
//==========================================================================

bool VWidget::IsFocus(bool Recurse) const
{
	guard(VWidget::IsFocus);
	//	Root is always focused.
	if (!ParentWidget)
	{
		return true;
	}
	if (Recurse)
	{
		const VWidget* W = this;
		while (W->ParentWidget && W->ParentWidget->CurrentFocusChild == W)
		{
			W = W->ParentWidget;
		}
		return !W->ParentWidget;
	}
	else
	{
		return ParentWidget->CurrentFocusChild == this;
	}
	unguard;
}

//==========================================================================
//
//	VWidget::SetFocus
//
//==========================================================================

void VWidget::SetFocus()
{
	guard(VWidget::SetFocus);
	if (ParentWidget)
	{
		ParentWidget->SetCurrentFocusChild(this);
	}
	unguard;
}

//==========================================================================
//
//	VWidget::FindNewFocus
//
//==========================================================================

void VWidget::FindNewFocus()
{
	guard(VWidget::FindNewFocus);
	for (VWidget* W = CurrentFocusChild->NextWidget; W; W = W->NextWidget)
	{
		if ((W->WidgetFlags & WF_IsFocusable) &&
			(W->WidgetFlags & WF_IsVisible) &&
			(W->WidgetFlags & WF_IsEnabled))
		{
			SetCurrentFocusChild(W);
			return;
		}
	}

	for (VWidget* W = CurrentFocusChild->PrevWidget; W; W = W->PrevWidget)
	{
		if ((W->WidgetFlags & WF_IsFocusable) &&
			(W->WidgetFlags & WF_IsVisible) &&
			(W->WidgetFlags & WF_IsEnabled))
		{
			SetCurrentFocusChild(W);
			return;
		}
	}

	SetCurrentFocusChild(NULL);
	unguard;
}

//==========================================================================
//
//	VWidget::GetWidgetAt
//
//==========================================================================

VWidget* VWidget::GetWidgetAt(float X, float Y)
{
	guard(VWidget::GetWidgetAt);
	for (VWidget* W = LastChildWidget; W; W = W->PrevWidget)
	{
		if (!(W->WidgetFlags & WF_IsVisible))
		{
			continue;
		}
		if (X >= W->ClipRect.ClipX1 && X < W->ClipRect.ClipX2 &&
			Y >= W->ClipRect.ClipY1 && Y < W->ClipRect.ClipY2)
		{
			return W->GetWidgetAt(X, Y);
		}
	}
	return this;
	unguard;
}

//==========================================================================
//
//	VWidget::DrawTree
//
//==========================================================================

void VWidget::DrawTree()
{
	guard(VWidget::DrawTree);
	if (!(WidgetFlags & WF_IsVisible) || !ClipRect.HasArea())
	{
		//	Not visible or clipped away.
		return;
	}

	//	Main draw event for this widget.
	OnDraw();

	//	Draw chid widgets.
	for (VWidget* c = FirstChildWidget; c; c = c->NextWidget)
	{
		c->DrawTree();
	}

	//	Do any drawing after child wigets have been drawn.
	OnPostDraw();
	unguard;
}

//==========================================================================
//
//	VWidget::TickTree
//
//==========================================================================

void VWidget::TickTree(float DeltaTime)
{
	guard(VWidget::TickTree);
	if (WidgetFlags & WF_TickEnabled)
	{
		Tick(DeltaTime);
	}
	for (VWidget* c = FirstChildWidget; c; c = c->NextWidget)
	{
		c->TickTree(DeltaTime);
	}
	unguard;
}

//==========================================================================
//
//	VWidget::TransferAndClipRect
//
//==========================================================================

bool VWidget::TransferAndClipRect(float& X1, float& Y1, float& X2, float& Y2,
	float& S1, float& T1, float& S2, float& T2) const
{
	guard(VWidget::TransferAndClipRect);
	X1 = ClipRect.ScaleX * X1 + ClipRect.OriginX;
	Y1 = ClipRect.ScaleY * Y1 + ClipRect.OriginY;
	X2 = ClipRect.ScaleX * X2 + ClipRect.OriginX;
	Y2 = ClipRect.ScaleY * Y2 + ClipRect.OriginY;
	if (X1 < ClipRect.ClipX1)
	{
		S1 = S1 + (X1 - ClipRect.ClipX1) / (X1 - X2) * (S2 - S1);
		X1 = ClipRect.ClipX1;
	}
	if (X2 > ClipRect.ClipX2)
	{
		S2 = S2 + (X2 - ClipRect.ClipX2) / (X1 - X2) * (S2 - S1);
		X2 = ClipRect.ClipX2;
	}
	if (Y1 < ClipRect.ClipY1)
	{
		T1 = T1 + (Y1 - ClipRect.ClipY1) / (Y1 - Y2) * (T2 - T1);
		Y1 = ClipRect.ClipY1;
	}
	if (Y2 > ClipRect.ClipY2)
	{
		T2 = T2 + (Y2 - ClipRect.ClipY2) / (Y1 - Y2) * (T2 - T1);
		Y2 = ClipRect.ClipY2;
	}
	return X1 < X2 && Y1 < Y2;
	unguard;
}

//==========================================================================
//
//	VWidget::DrawPic
//
//==========================================================================

void VWidget::DrawPic(int X, int Y, int Handle, float Alpha, int Trans)
{
	guard(VWidget::DrawPic);
	DrawPic(X, Y, GTextureManager(Handle), Alpha, Trans);
	unguard;
}

//==========================================================================
//
//	VWidget::DrawPic
//
//==========================================================================

void VWidget::DrawPic(int X, int Y, VTexture* Tex, float Alpha, int Trans)
{
	guard(VWidget::DrawPic);
	if (!Tex)
	{
		return;
	}

	X -= Tex->GetScaledSOffset();
	Y -= Tex->GetScaledTOffset();
	float X1 = X;
	float Y1 = Y;
	float X2 = X + Tex->GetScaledWidth();
	float Y2 = Y + Tex->GetScaledHeight();
	float S1 = 0;
	float T1 = 0;
	float S2 = Tex->GetWidth();
	float T2 = Tex->GetHeight();
	if (TransferAndClipRect(X1, Y1, X2, Y2, S1, T1, S2, T2))
	{
		Drawer->DrawPic(X1, Y1, X2, Y2, S1, T1, S2, T2, Tex,
			R_GetCachedTranslation(Trans, NULL), Alpha);
	}
	unguard;
}

//==========================================================================
//
//	VWidget::DrawShadowedPic
//
//==========================================================================

void VWidget::DrawShadowedPic(int X, int Y, int Handle)
{
	guard(VWidget::DrawShadowedPic);
	DrawShadowedPic(X, Y, GTextureManager(Handle));
	unguard;
}

//==========================================================================
//
//	VWidget::DrawShadowedPic
//
//==========================================================================

void VWidget::DrawShadowedPic(int X, int Y, VTexture* Tex)
{
	guard(VWidget::DrawShadowedPic);
	if (!Tex)
	{
		return;
	}

	float X1 = X - Tex->GetScaledSOffset() + 2;
	float Y1 = Y - Tex->GetScaledTOffset() + 2;
	float X2 = X - Tex->GetScaledSOffset() + 2 + Tex->GetScaledWidth();
	float Y2 = Y - Tex->GetScaledTOffset() + 2 + Tex->GetScaledHeight();
	float S1 = 0;
	float T1 = 0;
	float S2 = Tex->GetWidth();
	float T2 = Tex->GetHeight();
	if (TransferAndClipRect(X1, Y1, X2, Y2, S1, T1, S2, T2))
	{
		Drawer->DrawPicShadow(X1, Y1, X2, Y2, S1, T1, S2, T2, Tex, 0.625);
	}

	DrawPic(X, Y, Tex);
	unguard;
}

//==========================================================================
//
//	VWidget::FillRectWithFlat
//
//==========================================================================

void VWidget::FillRectWithFlat(int X, int Y, int Width, int Height,
	VName Name)
{
	guard(VWidget::FillRectWithFlat);
	float X1 = X;
	float Y1 = Y;
	float X2 = X + Width;
	float Y2 = Y + Height;
	float S1 = 0;
	float T1 = 0;
	float S2 = Width;
	float T2 = Height;
	if (TransferAndClipRect(X1, Y1, X2, Y2, S1, T1, S2, T2))
	{
		Drawer->FillRectWithFlat(X1, Y1, X2, Y2, S1, T1, S2, T2,
			GTextureManager(GTextureManager.NumForName(Name, TEXTYPE_Flat,
			true, true)));
	}
	unguard;
}

//==========================================================================
//
//	VWidget::ShadeRect
//
//==========================================================================

void VWidget::ShadeRect(int X, int Y, int Width, int Height, float Shade)
{
	guard(VWidget::ShadeRect);
	float X1 = X;
	float Y1 = Y;
	float X2 = X + Width;
	float Y2 = Y + Height;
	float S1 = 0;
	float T1 = 0;
	float S2 = 0;
	float T2 = 0;
	if (TransferAndClipRect(X1, Y1, X2, Y2, S1, T1, S2, T2))
	{
		Drawer->ShadeRect((int)X1, (int)Y1, (int)X2 - (int)X1, (int)Y2 - (int)Y1, Shade);
	}
	unguard;
}

//==========================================================================
//
//	VWidget::SetFont
//
//==========================================================================

void VWidget::SetFont(VFont* AFont)
{
	guard(VWidget::SetFont);
	Font = AFont;
	unguard;
}

//==========================================================================
//
//	VWidget::SetFont
//
//==========================================================================

void VWidget::SetFont(VName FontName)
{
	guard(VWidget::SetFont);
	VFont* F = VFont::GetFont(FontName, FontName);
	if (F)
	{
		Font = F;
	}
	else
	{
		GCon->Logf("No such font %s", *FontName);
	}
	unguard;
}

//==========================================================================
//
//	VWidget::SetTextAlign
//
//==========================================================================

void VWidget::SetTextAlign(halign_e NewHAlign, valign_e NewVAlign)
{
	guard(VWidget::SetTextAlign);
	HAlign = NewHAlign;
	VAlign = NewVAlign;
	unguard;
}

//==========================================================================
//
//	VWidget::SetTextShadow
//
//==========================================================================

void VWidget::SetTextShadow(bool State)
{
	if (State)
		WidgetFlags |= WF_TextShadowed;
	else
		WidgetFlags &= ~WF_TextShadowed;
}

//==========================================================================
//
//	VWidget::DrawString
//
//==========================================================================

void VWidget::DrawString(int x, int y, const VStr& String, int NormalColour,
	int BoldColour, float Alpha)
{
	guard(VWidget::DrawNString);
	if (String.IsEmpty())
		return;

	int cx = x;
	int cy = y;
	int Kerning = Font->GetKerning();
	int Colour = NormalColour;

	if (HAlign == hcentre)
		cx -= Font->StringWidth(String) / 2;
	if (HAlign == hright)
		cx -= Font->StringWidth(String);

	for (const char* SPtr = *String; *SPtr;)
	{
		int c = VStr::GetChar(SPtr);

		//	Check for colour escape.
		if (c == TEXT_COLOUR_ESCAPE)
		{
			Colour = VFont::ParseColourEscape(SPtr, NormalColour, BoldColour);
			continue;
		}

		int w;
		VTexture* Tex = Font->GetChar(c, &w, Colour);
		if (Tex)
		{
			if (WidgetFlags & WF_TextShadowed)
				DrawShadowedPic(cx, cy, Tex);
			else
				DrawPic(cx, cy, Tex, Alpha);
		}
		cx += w + Kerning;
	}
	LastX = cx;
	LastY = cy;
	unguard;
}

//==========================================================================
//
//	VWidget::DrawText
//
//==========================================================================

void VWidget::DrawText(int x, int y, const VStr& String, int NormalColour,
	int BoldColour, float Alpha)
{
	guard(VWidget::DrawText);
	int start = 0;
	int cx = x;
	int cy = y;

	if (VAlign == vcentre)
		cy -= Font->TextHeight(String) / 2;
	if (VAlign == vbottom)
		cy -= Font->TextHeight(String);

	//	Need this for correct cursor position with empty strings.
	LastX = cx;
	LastY = cy;

	for (size_t i = 0; i < String.Length(); i++)
	{
		if (String[i] == '\n')
		{
			VStr cs(String, start, i - start);
			DrawString(cx, cy, cs, NormalColour, BoldColour, Alpha);
			cy += Font->GetHeight();
			start = i + 1;
		}
		if (i == String.Length() - 1)
		{
			DrawString(cx, cy, VStr(String, start, String.Length() - start),
				NormalColour, BoldColour, Alpha);
		}
	}
	unguard;
}

//==========================================================================
//
//	VWidget::DrawCursor
//
//==========================================================================

void VWidget::DrawCursor()
{
	DrawCursorAt(LastX, LastY);
}

//==========================================================================
//
//	VWidget::DrawCursorAt
//
//==========================================================================

void VWidget::DrawCursorAt(int x, int y)
{
	guard(VWidget::DrawCursorAt);
	if ((int)(host_time * 4) & 1)
	{
		int w;
		DrawPic(x, y, Font->GetChar('_', &w, CR_UNTRANSLATED));
	}
	unguard;
}

//==========================================================================
//
//	Natives
//
//==========================================================================

IMPLEMENT_FUNCTION(VWidget, NewChild)
{
	P_GET_PTR(VClass, ChildClass);
	P_GET_SELF;
	RET_REF(CreateNewWidget(ChildClass, Self));
}

IMPLEMENT_FUNCTION(VWidget, Destroy)
{
	P_GET_SELF;
	delete Self;
	Self = NULL;
}

IMPLEMENT_FUNCTION(VWidget, DestroyAllChildren)
{
	P_GET_SELF;
	Self->DestroyAllChildren();
}

IMPLEMENT_FUNCTION(VWidget, GetRootWidget)
{
	P_GET_SELF;
	RET_REF(Self->GetRootWidget());
}

IMPLEMENT_FUNCTION(VWidget, Lower)
{
	P_GET_SELF;
	Self->Lower();
}

IMPLEMENT_FUNCTION(VWidget, Raise)
{
	P_GET_SELF;
	Self->Raise();
}

IMPLEMENT_FUNCTION(VWidget, MoveBefore)
{
	P_GET_REF(VWidget, Other);
	P_GET_SELF;
	Self->MoveBefore(Other);
}

IMPLEMENT_FUNCTION(VWidget, MoveAfter)
{
	P_GET_REF(VWidget, Other);
	P_GET_SELF;
	Self->MoveAfter(Other);
}

IMPLEMENT_FUNCTION(VWidget, SetPos)
{
	P_GET_INT(NewY);
	P_GET_INT(NewX);
	P_GET_SELF;
	Self->SetPos(NewX, NewY);
}

IMPLEMENT_FUNCTION(VWidget, SetX)
{
	P_GET_INT(NewX);
	P_GET_SELF;
	Self->SetX(NewX);
}

IMPLEMENT_FUNCTION(VWidget, SetY)
{
	P_GET_INT(NewY);
	P_GET_SELF;
	Self->SetY(NewY);
}

IMPLEMENT_FUNCTION(VWidget, SetSize)
{
	P_GET_INT(NewHeight);
	P_GET_INT(NewWidth);
	P_GET_SELF;
	Self->SetSize(NewWidth, NewHeight);
}

IMPLEMENT_FUNCTION(VWidget, SetWidth)
{
	P_GET_INT(NewWidth);
	P_GET_SELF;
	Self->SetWidth(NewWidth);
}

IMPLEMENT_FUNCTION(VWidget, SetHeight)
{
	P_GET_INT(NewHeight);
	P_GET_SELF;
	Self->SetHeight(NewHeight);
}

IMPLEMENT_FUNCTION(VWidget, SetScale)
{
	P_GET_FLOAT(NewScaleY);
	P_GET_FLOAT(NewScaleX);
	P_GET_SELF;
	Self->SetScale(NewScaleX, NewScaleY);
}

IMPLEMENT_FUNCTION(VWidget, SetConfiguration)
{
	P_GET_FLOAT_OPT(NewScaleY, 1.0);
	P_GET_FLOAT_OPT(NewScaleX, 1.0);
	P_GET_INT(NewHeight);
	P_GET_INT(NewWidth);
	P_GET_INT(NewY);
	P_GET_INT(NewX);
	P_GET_SELF;
	Self->SetConfiguration(NewX, NewY, NewWidth, NewHeight, NewScaleX,
		NewScaleY);
}

IMPLEMENT_FUNCTION(VWidget, SetVisibility)
{
	P_GET_BOOL(bNewVisibility);
	P_GET_SELF;
	Self->SetVisibility(bNewVisibility);
}

IMPLEMENT_FUNCTION(VWidget, Show)
{
	P_GET_SELF;
	Self->Show();
}

IMPLEMENT_FUNCTION(VWidget, Hide)
{
	P_GET_SELF;
	Self->Hide();
}

IMPLEMENT_FUNCTION(VWidget, IsVisible)
{
	P_GET_BOOL_OPT(Recurse, true);
	P_GET_SELF;
	RET_BOOL(Self->IsVisible(Recurse));
}

IMPLEMENT_FUNCTION(VWidget, SetEnabled)
{
	P_GET_BOOL(bNewEnabled);
	P_GET_SELF;
	Self->SetEnabled(bNewEnabled);
}

IMPLEMENT_FUNCTION(VWidget, Enable)
{
	P_GET_SELF;
	Self->Enable();
}

IMPLEMENT_FUNCTION(VWidget, Disable)
{
	P_GET_SELF;
	Self->Disable();
}

IMPLEMENT_FUNCTION(VWidget, IsEnabled)
{
	P_GET_BOOL_OPT(Recurse, true);
	P_GET_SELF;
	RET_BOOL(Self->IsEnabled(Recurse));
}

IMPLEMENT_FUNCTION(VWidget, SetFocusable)
{
	P_GET_BOOL(bNewFocusable);
	P_GET_SELF;
	Self->SetFocusable(bNewFocusable);
}

IMPLEMENT_FUNCTION(VWidget, IsFocusable)
{
	P_GET_SELF;
	RET_BOOL(Self->IsFocusable());
}

IMPLEMENT_FUNCTION(VWidget, SetCurrentFocusChild)
{
	P_GET_REF(VWidget, NewFocus);
	P_GET_SELF;
	Self->SetCurrentFocusChild(NewFocus);
}

IMPLEMENT_FUNCTION(VWidget, GetCurrentFocus)
{
	P_GET_SELF;
	RET_REF(Self->GetCurrentFocus());
}

IMPLEMENT_FUNCTION(VWidget, IsFocus)
{
	P_GET_BOOL_OPT(Recurse, true);
	P_GET_SELF;
	RET_BOOL(Self->IsFocus(Recurse));
}

IMPLEMENT_FUNCTION(VWidget, SetFocus)
{
	P_GET_SELF;
	Self->SetFocus();
}

IMPLEMENT_FUNCTION(VWidget, DrawPic)
{
	P_GET_INT_OPT(Translation, 0);
	P_GET_FLOAT_OPT(Alpha, 1.0);
	P_GET_INT(Handle);
	P_GET_INT(Y);
	P_GET_INT(X);
	P_GET_SELF;
	Self->DrawPic(X, Y, Handle, Alpha, Translation);
}

IMPLEMENT_FUNCTION(VWidget, DrawShadowedPic)
{
	P_GET_INT(Handle);
	P_GET_INT(Y);
	P_GET_INT(X);
	P_GET_SELF;
	Self->DrawShadowedPic(X, Y, Handle);
}

IMPLEMENT_FUNCTION(VWidget, FillRectWithFlat)
{
	P_GET_NAME(Name);
	P_GET_INT(Height);
	P_GET_INT(Width);
	P_GET_INT(Y);
	P_GET_INT(X);
	P_GET_SELF;
	Self->FillRectWithFlat(X, Y, Width, Height, Name);
}

IMPLEMENT_FUNCTION(VWidget, ShadeRect)
{
	P_GET_FLOAT(Shade);
	P_GET_INT(Height);
	P_GET_INT(Width);
	P_GET_INT(Y);
	P_GET_INT(X);
	P_GET_SELF;
	Self->ShadeRect(X, Y, Width, Height, Shade);
}

IMPLEMENT_FUNCTION(VWidget, SetFont)
{
	P_GET_NAME(FontName);
	P_GET_SELF;
	Self->SetFont(FontName);
}

IMPLEMENT_FUNCTION(VWidget, SetTextAlign)
{
	P_GET_INT(valign);
	P_GET_INT(halign);
	P_GET_SELF;
	Self->SetTextAlign((halign_e)halign, (valign_e)valign);
}

IMPLEMENT_FUNCTION(VWidget, SetTextShadow)
{
	P_GET_BOOL(State);
	P_GET_SELF;
	Self->SetTextShadow(State);
}

IMPLEMENT_FUNCTION(VWidget, TextWidth)
{
	P_GET_STR(text);
	P_GET_SELF;
	RET_INT(Self->Font->TextWidth(text));
}

IMPLEMENT_FUNCTION(VWidget, TextHeight)
{
	P_GET_STR(text);
	P_GET_SELF;
	RET_INT(Self->Font->TextHeight(text));
}

IMPLEMENT_FUNCTION(VWidget, SplitText)
{
	P_GET_INT(MaxWidth);
	P_GET_PTR(TArray<VSplitLine>, Lines);
	P_GET_STR(Text);
	P_GET_SELF;
	RET_INT(Self->Font->SplitText(Text, *Lines, MaxWidth));
}

IMPLEMENT_FUNCTION(VWidget, SplitTextWithNewlines)
{
	P_GET_INT(MaxWidth);
	P_GET_STR(Text);
	P_GET_SELF;
	RET_STR(Self->Font->SplitTextWithNewlines(Text, MaxWidth));
}

IMPLEMENT_FUNCTION(VWidget, DrawText)
{
	P_GET_FLOAT_OPT(Alpha, 1.0);
	P_GET_INT_OPT(BoldColour, CR_UNTRANSLATED);
	P_GET_INT_OPT(Colour, CR_UNTRANSLATED);
	P_GET_STR(String);
	P_GET_INT(Y);
	P_GET_INT(X);
	P_GET_SELF;
	Self->DrawText(X, Y, String, Colour, BoldColour, Alpha);
}

IMPLEMENT_FUNCTION(VWidget, DrawCursor)
{
	P_GET_SELF;
	Self->DrawCursor();
}

IMPLEMENT_FUNCTION(VWidget, FindTextColour)
{
	P_GET_STR(Name);
	RET_INT(VFont::FindTextColour(*Name.ToLower()));
}
