/*==LICENSE==*

CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.

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 3 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.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.

Additional permissions under GNU GPL version 3 section 7

If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.

You can contact Cyan Worlds, Inc. by email legal@cyan.com
 or by snail mail at:
      Cyan Worlds, Inc.
      14617 N Newport Hwy
      Mead, WA   99021

*==LICENSE==*/
//////////////////////////////////////////////////////////////////////////////
//																			//
//	pfGUIDialogMod Definition												//
//																			//
//////////////////////////////////////////////////////////////////////////////

#include "hsTypes.h"
#include "pfGameGUIMgr.h"
#include "pfGUIDialogMod.h"
#include "pfGUIControlMod.h"
#include "pfGUIDialogHandlers.h"
#include "pfGUIDialogNotifyProc.h"
#include "pfGUIListElement.h"
#include "../plScene/plPostEffectMod.h"

#include "../pnMessage/plRefMsg.h"
#include "../pfMessage/pfGameGUIMsg.h"
#include "../plMessage/plAnimCmdMsg.h"
#include "../plScene/plSceneNode.h"
#include "../pnSceneObject/plSceneObject.h"
#include "../pnKeyedObject/plKey.h"
#include "../pnKeyedObject/plFixedKey.h"
#include "../pnSceneObject/plCoordinateInterface.h"

#include "../plStatusLog/plStatusLog.h"

#include "plgDispatch.h"
#include "hsResMgr.h"
#include "plViewTransform.h"


//// Constructor/Destructor //////////////////////////////////////////////////

pfGUIDialogMod::pfGUIDialogMod() : fRenderMod( nil ), fNext( nil ), fPrevPtr( nil )
{
	memset( fName, 0, sizeof( fName ) );
	fEnabled = false;
	fControlOfInterest = nil;
	fFocusCtrl = nil;
	fMousedCtrl = nil;
	fTagID = 0;
	fHandler = nil;
	fVersion = 0;

	fDragMode = false;
	fDragReceptive = false;
	fDragTarget = nil;
	fProcReceiver = nil;

	fColorScheme = TRACKED_NEW pfGUIColorScheme();
}

pfGUIDialogMod::~pfGUIDialogMod()
{
	// Call the handler's destroy if there is one
	if( fHandler )
		fHandler->OnDestroy();

	// Unregister us with the Game GUI manager
	plUoid lu( kGameGUIMgr_KEY );
	plKey mgrKey = hsgResMgr::ResMgr()->FindKey( lu );
	if( mgrKey )
	{
		plGenRefMsg *refMsg = TRACKED_NEW plGenRefMsg( mgrKey, plRefMsg::kOnRemove, 0, pfGameGUIMgr::kDlgModRef );
		refMsg->SetRef( this );
		plgDispatch::MsgSend( refMsg );
	}

	SetHandler( nil );

	hsRefCnt_SafeUnRef( fColorScheme );
	fColorScheme = nil;
}

//// ScreenToWorldPoint //////////////////////////////////////////////////////
//	Sometimes it just sucks not having access to the pipeline at just the
//	right time.

void	pfGUIDialogMod::ScreenToWorldPoint( hsScalar x, hsScalar y, hsScalar z, hsPoint3 &outPt )
{
	plViewTransform view = fRenderMod->GetViewTransform();
	view.SetScreenSize( 1, 1 );

	outPt = view.ScreenToWorld( hsPoint3( x, y, z ) );
}

//// WorldToScreenPoint //////////////////////////////////////////////////////
//	Given a point in world-space, translates it into screen coordinates 
//	(with range 0-1, origin top-left).

hsPoint3	pfGUIDialogMod::WorldToScreenPoint( const hsPoint3 &inPt )
{
	plViewTransform view = fRenderMod->GetViewTransform();
	view.SetScreenSize( 1, 1 );

	hsPoint3 tempPt = view.WorldToScreen( inPt );
	tempPt.fZ = view.WorldToCamera( inPt ).fZ;
	return tempPt;
}

//// IEval ///////////////////////////////////////////////////////////////////

hsBool	pfGUIDialogMod::IEval( double secs, hsScalar del, UInt32 dirty )
{
	return false;
}

//// MsgReceive //////////////////////////////////////////////////////////////

hsBool	pfGUIDialogMod::MsgReceive( plMessage *msg )
{
	plGenRefMsg *ref = plGenRefMsg::ConvertNoRef( msg );
	if( ref )
	{
		switch( ref->fType )
		{
			case kRenderModRef:
				if( ref->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) )
				{
					fRenderMod = plPostEffectMod::ConvertNoRef( ref->GetRef() );
					fRenderMod->EnableLightsOnRenderRequest();

					if( fEnabled )
					{
						plAnimCmdMsg	*animMsg = TRACKED_NEW plAnimCmdMsg( GetKey(), fRenderMod->GetKey(), nil );
						animMsg->SetCmd( plAnimCmdMsg::kContinue );
						plgDispatch::MsgSend( animMsg );
					}
				}
				else if( ref->GetContext() & ( plRefMsg::kOnRemove | plRefMsg::kOnDestroy ) )
				{
					plAnimCmdMsg	*animMsg = TRACKED_NEW plAnimCmdMsg( GetKey(), fRenderMod->GetKey(), nil );
					animMsg->SetCmd( plAnimCmdMsg::kStop );
					plgDispatch::MsgSend( animMsg );

					fRenderMod = nil;
				}
				break;

			case kControlRef:
				if( ref->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) )
				{
					if( ref->fWhich >= fControls.GetCount() )
					{
						hsAssert( false, "Bad index in reffing a control on a GUI dialog" );
					}
					else
					{
						pfGUIControlMod *oldCtrl = fControls[ ref->fWhich ];

						fControls[ ref->fWhich ] = pfGUIControlMod::ConvertNoRef( ref->GetRef() );
						fControls[ ref->fWhich ]->ISetDialog( this );
						if( oldCtrl != fControls[ ref->fWhich ] )
							// They're equal on export time, when we DON'T want to be updating the bounds
							fControls[ ref->fWhich ]->CalcInitialBounds();

						if( fControls[ ref->fWhich ]->HasFlag( pfGUIControlMod::kInheritProcFromDlg ) )
							fControls[ ref->fWhich ]->ISetHandler( fHandler );
					}
				}

				else if( ref->GetContext() & ( plRefMsg::kOnRemove | plRefMsg::kOnDestroy ) )
				{
					if( ref->fWhich >= fControls.GetCount() )
					{
						hsAssert( false, "Bad index in unreffing a control on a GUI dialog." );
					}
					else
					{
						if( fControls[ ref->fWhich ] != nil )
							fControls[ ref->fWhich ]->ISetDialog( nil );
						fControls[ ref->fWhich ] = nil;
					}
				}
				break;
		}
		return true;
	}

	return plSingleModifier::MsgReceive( msg );
}

//// AddControl //////////////////////////////////////////////////////////////

void		pfGUIDialogMod::AddControl( pfGUIControlMod *ctrl )
{
	fControls.Append( ctrl );
	ctrl->ISetDialog( this );
	ctrl->CalcInitialBounds();
}

//// AddControlOnExport //////////////////////////////////////////////////////

void		pfGUIDialogMod::AddControlOnExport( pfGUIControlMod *ctrl )
{
	fControls.Append( ctrl );
	hsgResMgr::ResMgr()->AddViaNotify( ctrl->GetKey(), TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, fControls.GetCount() - 1, pfGUIDialogMod::kControlRef ), plRefFlags::kActiveRef );
}

//// SetEnabled //////////////////////////////////////////////////////////////

void	pfGUIDialogMod::SetEnabled( hsBool e )
{
	if( e == fEnabled )
		return;

	fEnabled = e;

	if( fHandler != nil )
	{
		if( fEnabled )
			fHandler->OnShow();
		else
			fHandler->OnHide();
	}

	if ( !fEnabled )
	{
		// if we are being hidden then there should be no controls that have interest
		fControlOfInterest = nil;
		// also we can purge the dynaText images on the controls
		int i;
		for( i = 0; i < fControls.GetCount(); i++ )
		{
			if( fControls[ i ] == nil )
				continue;
			fControls[ i ]->PurgeDynaTextMapImage();
		}
	}

	if( fRenderMod != nil )
	{
		plAnimCmdMsg	*animMsg = TRACKED_NEW plAnimCmdMsg( GetKey(), fRenderMod->GetKey(), nil );
		if( fEnabled )
		{
			animMsg->SetCmd( plAnimCmdMsg::kContinue );

			// Update the bounds on all controls that we own
			UpdateAllBounds();
		}
		else
			animMsg->SetCmd( plAnimCmdMsg::kStop );
		plgDispatch::MsgSend( animMsg );
	}

}

//// Read/Write //////////////////////////////////////////////////////////////

void	pfGUIDialogMod::Read( hsStream *s, hsResMgr *mgr )
{
	plSingleModifier::Read(s, mgr);

	mgr->ReadKeyNotifyMe( s, TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, -1, kRenderModRef ), plRefFlags::kActiveRef );

	s->Read( sizeof( fName ), fName );

	UInt32	i, count = s->ReadSwap32();
	fControls.SetCountAndZero( count );
	for( i = 0; i < count; i++ )
		mgr->ReadKeyNotifyMe( s, TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, i, kControlRef ), plRefFlags::kActiveRef );

	// Register us with the Game GUI manager
	plUoid lu( kGameGUIMgr_KEY );
	plKey mgrKey = hsgResMgr::ResMgr()->FindKey( lu );
	if( mgrKey )
	{
		plGenRefMsg *refMsg = TRACKED_NEW plGenRefMsg( mgrKey, plRefMsg::kOnCreate, 0, pfGameGUIMgr::kDlgModRef );
		hsgResMgr::ResMgr()->AddViaNotify( GetKey(), refMsg, plRefFlags::kPassiveRef );		
	}

	s->ReadSwap( &fTagID );

	fProcReceiver = mgr->ReadKey( s );
	if( fProcReceiver != nil )
		SetHandler( TRACKED_NEW pfGUIDialogNotifyProc( fProcReceiver ) );

	s->ReadSwap( &fVersion );

	fColorScheme->Read( s );

	fSceneNodeKey = mgr->ReadKey( s );
}

void	pfGUIDialogMod::Write( hsStream *s, hsResMgr *mgr )
{
	UInt32	i;


	plSingleModifier::Write( s, mgr );

	mgr->WriteKey( s, fRenderMod->GetKey() );
	s->Write( sizeof( fName ), fName );

	s->WriteSwap32( fControls.GetCount() );
	for( i = 0; i < fControls.GetCount(); i++ )
		mgr->WriteKey( s, fControls[ i ]->GetKey() );

	s->WriteSwap( fTagID );

	mgr->WriteKey( s, fProcReceiver );

	s->WriteSwap( fVersion );

	fColorScheme->Write( s );

	mgr->WriteKey( s, fSceneNodeKey );
}

plKey	pfGUIDialogMod::GetSceneNodeKey( void )
{
	if( fSceneNodeKey != nil )
		return fSceneNodeKey;

	// Attempt to grab it
	if( GetTarget() != nil && GetTarget()->GetSceneNode() != nil )
		return ( fSceneNodeKey = GetTarget()->GetSceneNode() );

	return nil;
}

//// UpdateInterestingThings /////////////////////////////////////////////////
//	Really. We go through and make sure every control marked as interesting
//	still has the mouse inside it and vice versa.

void	pfGUIDialogMod::UpdateInterestingThings( hsScalar mouseX, hsScalar mouseY, UInt8 modifiers, hsBool modalPreset )
{
	int			i;
	hsPoint3	mousePoint;


	mousePoint.Set( mouseX, mouseY, 0.f );

	for( i = 0; i < fControls.GetCount(); i++ )
	{
		if( fControls[ i ] == nil )
			continue;

		// if there was a modal present and we are not modal, then everything is unInteresting!
		if ( modalPreset && !HasFlag(pfGUIDialogMod::kModal) )
		{
			if( fControls[ i ]->IsInteresting() )
				fControls[ i ]->SetInteresting( false );
		}
		else
		{
			if( !fControls[ i ]->HasFlag( pfGUIControlMod::kIntangible ) && fControls[ i ]->PointInBounds( mousePoint ) || fControls[ i ] == fControlOfInterest )
			{
				if( !fControls[ i ]->IsInteresting() )
					fControls[ i ]->SetInteresting( true );
			}
			else
			{
				if( fControls[ i ]->IsInteresting() )
					fControls[ i ]->SetInteresting( false );
			}
		}
	}
}

//// HandleMouseEvent ////////////////////////////////////////////////////////

#ifdef HS_DEBUGGING		 // Debugging bounds rects
#include "../plPipeline/plDebugText.h"
#endif

hsBool		pfGUIDialogMod::HandleMouseEvent( pfGameGUIMgr::EventType event, hsScalar mouseX, hsScalar mouseY,
												UInt8 modifiers )
{
	hsPoint3	mousePoint;
	UInt32		i;

	pfGUIControlMod	*oldInterestingCtrl = nil;
	hsScalar		smallestZ;

#ifdef HS_DEBUGGING	 // Debugging bounds rects
static bool		showBounds = false;

	if( showBounds )
	{
		UInt32 sW, sH;
		plDebugText::Instance().GetScreenSize(&sW,&sH);
		for( i = 0; i < fControls.GetCount(); i++ )
		{
			if( fControls[ i ] == nil )
				continue;
			if( fControls[ i ]->HasFlag( pfGUIControlMod::kIntangible ) )
				continue;

			if( fControls[ i ]->fBoundsPoints.GetCount() > 0 )
			{
				const hsBounds3 &bnds = fControls[ i ]->GetBounds();
				plDebugText::Instance().Draw3DBorder( (UInt16)(sW * bnds.GetMins().fX),
										(UInt16)(sH * bnds.GetMins().fY),
										(UInt16)(sW * bnds.GetMaxs().fX),
										(UInt16)(sH * bnds.GetMaxs().fY), 0x3000ffff, 0x3000ffff );

				UInt32 color = 0xffff0000;
				for( int j = 0; j < fControls[ i ]->fBoundsPoints.GetCount(); j++ )
				{
//					color = 0xff000000 | ( ( j * 16 ) << 16 );
					float x = sW * fControls[ i ]->fBoundsPoints[ j ].fX;
					float y = sH * fControls[ i ]->fBoundsPoints[ j ].fY;
					plDebugText::Instance().DrawRect( (UInt16)(x - 2), (UInt16)(y - 2), (UInt16)(x + 2), (UInt16)(y + 2), color );
					char str[ 16 ];
					itoa( j, str, 10 );
					plDebugText::Instance().DrawString( (UInt16)(x + 8), (UInt16)(y - 8), str, color );
				}
			}
			else
			{
				const hsBounds3 &bnds = fControls[ i ]->GetBounds();
				plDebugText::Instance().Draw3DBorder( (UInt16)(sW * bnds.GetMins().fX),
										(UInt16)(sH * bnds.GetMins().fY),
										(UInt16)(sW * bnds.GetMaxs().fX),
										(UInt16)(sH * bnds.GetMaxs().fY), 0x300000ff, 0x300000ff );
			}
		}
	}
#endif

	mousePoint.Set( mouseX, mouseY, 0.f );

	if( fDragMode )
	{
		IHandleDrag( mousePoint, event, modifiers );
		return true;		// We ALWAYS handle events if we're in drag mode
	}

	oldInterestingCtrl = fMousedCtrl;
	if( fControlOfInterest != nil )
	{
		// A particular control already has interest--pass messages directly to it no matter what
		fMousedCtrl = fControlOfInterest;
	}
	else
	{
		for( i = 0, fMousedCtrl = nil, smallestZ = 1.e30f; i < fControls.GetCount(); i++ )
		{
			if( fControls[ i ] != nil && !fControls[ i ]->HasFlag( pfGUIControlMod::kIntangible ) && fControls[ i ]->PointInBounds( mousePoint ) && fControls[ i ]->IsVisible() && fControls[ i ]->IsEnabled() )
			{
				if( fControls[ i ]->GetScreenMinZ() < smallestZ )
				{
					if( fControls[ i ]->FilterMousePosition( mousePoint ) )
					{
						fMousedCtrl = fControls[ i ];
						smallestZ = fControls[ i ]->GetScreenMinZ();
					}
				}
			}
		}
	}

	if( fMousedCtrl != nil )
	{
#ifdef HS_DEBUGGING	 // Debugging bounds rects
if( showBounds )
{
	const hsBounds3 &bnds = fMousedCtrl->GetBounds();
	plDebugText::Instance().DrawString( (UInt16)(bnds.GetMins().fX), (UInt16)(bnds.GetMins().fY), fMousedCtrl->GetKeyName(), (UInt32)0xffffff00 );
}
#endif

		if( event == pfGameGUIMgr::kMouseDown )
		{
			if( fMousedCtrl->HasFlag( pfGUIControlMod::kWantsInterest ) )
				fControlOfInterest = fMousedCtrl;

			fMousedCtrl->HandleMouseDown( mousePoint, modifiers );

			// Clicking on a control (mouse down) also sets focus to that control. Unlike
			// control-of-interest, this does NOT get reset until a new control is clicked on
			if( fFocusCtrl != fMousedCtrl )
			{
				if( fHandler != nil )
					fHandler->OnCtrlFocusChange( fFocusCtrl, fMousedCtrl );

				if( fFocusCtrl != nil )
					fFocusCtrl->SetFocused( false );
				fFocusCtrl = fMousedCtrl;
				fFocusCtrl->SetFocused( true );
			}
		}
		else if( event == pfGameGUIMgr::kMouseUp )
		{
			fMousedCtrl->HandleMouseUp( mousePoint, modifiers );

			// Controls lose interest on mouse up
			fControlOfInterest = nil;
		}
		else if( event == pfGameGUIMgr::kMouseMove )
			fMousedCtrl->HandleMouseHover( mousePoint, modifiers );
		else if( event == pfGameGUIMgr::kMouseDrag )
			fMousedCtrl->HandleMouseDrag( mousePoint, modifiers );
		else if( event == pfGameGUIMgr::kMouseDblClick )
			fMousedCtrl->HandleMouseDblClick( mousePoint, modifiers );

		return true;
	}
	// Clicked on nobody, make sure we lose focus on any controls
	if( fFocusCtrl != nil && event == pfGameGUIMgr::kMouseDown )
	{
		if( fHandler != nil )
			fHandler->OnCtrlFocusChange( fFocusCtrl, nil );

		if( fFocusCtrl != nil )	// The handler call could've changed it
			fFocusCtrl->SetFocused( false );
		fFocusCtrl = nil;
	}

	return false;
}

//// HandleKeyEvent //////////////////////////////////////////////////////////

hsBool		pfGUIDialogMod::HandleKeyEvent( pfGameGUIMgr::EventType event, plKeyDef key, UInt8 modifiers )
{
	// Only process if a control has focus...
	if( fFocusCtrl != nil )
	{
		// And guess what, it's up to that control to process it! Gee, how easy...
		return fFocusCtrl->HandleKeyEvent( event, key, modifiers );
	}
	return false;
}

//// HandleKeyPress //////////////////////////////////////////////////////////

hsBool		pfGUIDialogMod::HandleKeyPress( char key, UInt8 modifiers )
{
	// Same deal as HandleKeyPress. Only problem is, we needed the msg to translate
	// to a char, so it had to be done up at the mgr level (sadly)
	// Only process if a control has focus...

	if( fFocusCtrl != nil )
	{
		return fFocusCtrl->HandleKeyPress( key, modifiers );
	}

	return false;
}

//// SetFocus ////////////////////////////////////////////////////////////////

void	pfGUIDialogMod::SetFocus( pfGUIControlMod *ctrl )
{
	if( ctrl != nil && ctrl->fDialog != this )
	{
		if( fHandler != nil )
			fHandler->OnCtrlFocusChange( fFocusCtrl, nil );

		if( fFocusCtrl != nil )
			fFocusCtrl->SetFocused( false );
		fFocusCtrl = nil;

		ctrl->fDialog->SetFocus( ctrl );
	}
	else if( ctrl != fFocusCtrl )
	{
		if( fFocusCtrl != nil )
			fFocusCtrl->SetFocused( false );

		if( fHandler != nil )
			fHandler->OnCtrlFocusChange( fFocusCtrl, ctrl );

		fFocusCtrl = ctrl;
		if( fFocusCtrl != nil )
			fFocusCtrl->SetFocused( true );
	}
}

//// Show/Hide ///////////////////////////////////////////////////////////////

void	pfGUIDialogMod::Show( void )
{
	pfGameGUIMgr::GetInstance()->ShowDialog( this );
}

void	pfGUIDialogMod::ShowNoReset( void )
{
	pfGameGUIMgr::GetInstance()->ShowDialog( this, false );
}

void	pfGUIDialogMod::Hide( void )
{
	pfGameGUIMgr::GetInstance()->HideDialog( this );
}

//// GetControlFromTag ///////////////////////////////////////////////////////

pfGUIControlMod	*pfGUIDialogMod::GetControlFromTag( UInt32 tagID )
{
	int		i;

	int ctrlCount = fControls.GetCount();

	for( i = 0; i < ctrlCount; i++ )
	{
		pfGUIControlMod *ctrl = fControls[i];
		if( ctrl && ctrl->GetTagID() == tagID )
			return fControls[ i ];
	}

	return nil;
}

//// SetControlOfInterest ////////////////////////////////////////////////////

void	pfGUIDialogMod::SetControlOfInterest( pfGUIControlMod *c )
{
	fControlOfInterest = c;
}

//// SetHandler //////////////////////////////////////////////////////////////

void	pfGUIDialogMod::SetHandler( pfGUIDialogProc *hdlr )
{
	int		i;


	if( fHandler && fHandler->DecRef() )
		delete fHandler;

	fHandler = hdlr;
	if( fHandler != nil )
	{
		fHandler->IncRef();
		fHandler->SetDialog( this );
	}

	// We also set the handler for any controls that are flagged to inherit
	// from the parent dialog. Note that SetHandlerForAll() can thus be
	// seen as a function that forces this flag (temporarily) on all controls
	for( i = 0; i < fControls.GetCount(); i++ )
	{
		// Test for nil controls since we get this also on destruct
		if( fControls[ i ] != nil && fControls[ i ]->HasFlag( pfGUIControlMod::kInheritProcFromDlg ) )
			fControls[ i ]->ISetHandler( hdlr );
	}
}

//// SetHandlerForAll ////////////////////////////////////////////////////////
//	Does SetHandler() for the dialog and all of its controls. Handy if you
//	have one of those all-encompasing dialog procs. :)

void	pfGUIDialogMod::SetHandlerForAll( pfGUIDialogProc *hdlr )
{
	int		i;


	SetHandler( hdlr );
	for( i = 0; i < fControls.GetCount(); i++ )
		fControls[ i ]->ISetHandler( hdlr );
}

//// SetControlHandler ///////////////////////////////////////////////////////

void	pfGUIDialogMod::SetControlHandler( UInt32 tagID, pfGUIDialogProc *hdlr )
{
	int		i;
	for( i = 0; i < fControls.GetCount(); i++ )
	{
		if( fControls[ i ]->GetTagID() == tagID )
		{
			fControls[ i ]->SetHandler( hdlr );
			break;
		}
	}
}


//// UpdateAspectRatio ///////////////////////////////////////////////////////

void	pfGUIDialogMod::UpdateAspectRatio( void )
{
	if (fRenderMod)
	{
		// Set width fov respecting height fov
		fRenderMod->SetFovX(pfGameGUIMgr::GetInstance()->GetAspectRatio() * fRenderMod->GetFovY());
	}
	UpdateAllBounds();
}


//// UpdateAllBounds /////////////////////////////////////////////////////////

void	pfGUIDialogMod::UpdateAllBounds( void )
{
	int i;
	for( i = 0; i < fControls.GetCount(); i++ )
	{
		if( fControls[ i ] != nil )
			fControls[ i ]->UpdateBounds( nil, true );
	}
}

//// RefreshAllControls //////////////////////////////////////////////////////

void	pfGUIDialogMod::RefreshAllControls( void )
{
	int i;
	for( i = 0; i < fControls.GetCount(); i++ )
		fControls[ i ]->IUpdate();
}

//////////////////////////////////////////////////////////////////////////////
//// ListElement Drag Functions //////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////

//// ClearDragList ///////////////////////////////////////////////////////////

void	pfGUIDialogMod::ClearDragList( void )
{
	fDragElements.Reset();
}

//// AddToDragList ///////////////////////////////////////////////////////////

void	pfGUIDialogMod::AddToDragList( pfGUIListElement *e )
{
	fDragElements.Append( e );
}

//// EnterDragMode ///////////////////////////////////////////////////////////

void	pfGUIDialogMod::EnterDragMode( pfGUIControlMod *source )
{
	if( fDragElements.GetCount() > 0 )
	{
		fDragMode = true;
		fDragReceptive = false;
		fDragTarget = nil;

		fDragSource = source;
	}
}

//// IHandleDrag /////////////////////////////////////////////////////////////
//	Oooh, we're in dragging-list-elements-around mode! So completely ignore 
//	the normal way we do things; what we need to do is wait until the mouse 
//	button is up, all the while testing to see if the control we're on top of
//	is capable of receiving the elements we have. Once the mouse button is let
//	up, if the control is indeed receptive, we call its drag handler for each 
//	of our elements, and either way, exit drag mode.

void	pfGUIDialogMod::IHandleDrag( hsPoint3 &mousePoint, pfGameGUIMgr::EventType event, UInt8 modifiers )
{
	int				i;
	hsScalar		smallestZ;


	// First, see if our target control has changed
	for( i = 0, fMousedCtrl = nil, smallestZ = 1.e30f; i < fControls.GetCount(); i++ )
	{
		if( fControls[ i ]->PointInBounds( mousePoint ) && fControls[ i ]->GetBounds().GetMaxs().fZ < smallestZ )
			fMousedCtrl = fControls[ i ];
	}

	if( fMousedCtrl != fDragTarget )
	{
		// Target has changed, update our receptive flag
		fDragTarget = fMousedCtrl;
		if( fDragTarget == nil )
			fDragReceptive = false;
		else
		{
			pfGUIDropTargetProc *dropProc = fDragTarget->GetDropTargetHdlr();
			if( dropProc == nil )
				fDragReceptive = false;
			else
			{
				fDragReceptive = true;
				for( i = 0; i < fDragElements.GetCount(); i++ )
				{
					if( !dropProc->CanEat( fDragElements[ i ], fDragSource ) )
					{
						fDragReceptive = false;
						break;
					}
				}
			}
		}
	}

	if( event == pfGameGUIMgr::kMouseUp )
	{
		/// Mouse got let up--we're exiting drag mode, but can we process the drop?
		fDragMode = false;
		if( fDragReceptive )
		{
			pfGUIDropTargetProc *dropProc = fDragTarget->GetDropTargetHdlr();
			for( i = 0; i < fDragElements.GetCount(); i++ )
				dropProc->Eat( fDragElements[ i ], fDragSource, fDragTarget );
		}
	}	
}

//// GetDesiredCursor ////////////////////////////////////////////////////////

UInt32		pfGUIDialogMod::GetDesiredCursor( void ) const
{
	if( fMousedCtrl != nil ) 
		return fMousedCtrl->IGetDesiredCursor();

	return 0;
}