// AlignDlg.cpp : implementation file
//

#include "stdafx.h"
#include "demopaja.h"
#include "AlignDlg.h"
#include "demopajadoc.h"
#include "PajaTypes.h"
#include "Vector2C.h"
#include "BBox2C.h"
#include <math.h>
#include "SceneC.h"
#include "LayerC.h"
#include "EffectI.h"
#include "ParamI.h"
//#include "Mainfrm.h"
#include "UndoC.h"
#include <vector>

using namespace Composition;
using namespace PajaTypes;
using namespace Edit;

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CAlignDlg dialog


CAlignDlg::CAlignDlg( CWnd* pParent /*=NULL*/) :
	CDialog(CAlignDlg::IDD, pParent)
{
	//{{AFX_DATA_INIT(CAlignDlg)
		// NOTE: the ClassWizard will add member initialization here
	//}}AFX_DATA_INIT
}


void CAlignDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CAlignDlg)
	DDX_Control(pDX, IDC_VERT_DIST_TOP, m_rVertDistTop);
	DDX_Control(pDX, IDC_VERT_DIST_HEIGHTS, m_rVertDistHeights);
	DDX_Control(pDX, IDC_VERT_DIST_CENTER, m_rVertDistCenter);
	DDX_Control(pDX, IDC_VERT_DIST_BOTTOM, m_rVertDistBottom);
	DDX_Control(pDX, IDC_VERT_ALIGN_TOP, m_rVertAlignTop);
	DDX_Control(pDX, IDC_VERT_ALIGN_CENTER, m_rVertAlignCenter);
	DDX_Control(pDX, IDC_VERT_ALIGN_BOTTOM, m_rVertAlignBottom);
	DDX_Control(pDX, IDC_HORIZ_DIST_WIDTHS, m_rHorizDistWidths);
	DDX_Control(pDX, IDC_HORIZ_DIST_RIGHT, m_rHorizDistRight);
	DDX_Control(pDX, IDC_HORIZ_DIST_LEFT, m_rHorizDistLeft);
	DDX_Control(pDX, IDC_HORIZ_DIST_CENTER, m_rHorizDistCenter);
	DDX_Control(pDX, IDC_HORIZ_ALIGN_RIGHT, m_rHorizAlignRight);
	DDX_Control(pDX, IDC_HORIZ_ALIGN_LEFT, m_rHorizAlignLeft);
	DDX_Control(pDX, IDC_HORIZ_ALIGN_CENTER, m_rHorizAlignCenter);
	DDX_Control(pDX, IDC_ALIGN_TO_PAGE, m_rAlignToPage);
	//}}AFX_DATA_MAP
}


BEGIN_MESSAGE_MAP(CAlignDlg, CDialog)
	//{{AFX_MSG_MAP(CAlignDlg)
	ON_BN_CLICKED(IDC_ALIGN_TO_PAGE, OnAlignToPage)
	ON_BN_CLICKED(IDC_HORIZ_ALIGN_CENTER, OnHorizAlignCenter)
	ON_BN_CLICKED(IDC_HORIZ_ALIGN_LEFT, OnHorizAlignLeft)
	ON_BN_CLICKED(IDC_HORIZ_ALIGN_RIGHT, OnHorizAlignRight)
	ON_BN_CLICKED(IDC_HORIZ_DIST_CENTER, OnHorizDistCenter)
	ON_BN_CLICKED(IDC_HORIZ_DIST_LEFT, OnHorizDistLeft)
	ON_BN_CLICKED(IDC_HORIZ_DIST_RIGHT, OnHorizDistRight)
	ON_BN_CLICKED(IDC_HORIZ_DIST_WIDTHS, OnHorizDistWidths)
	ON_BN_CLICKED(IDC_VERT_ALIGN_BOTTOM, OnVertAlignBottom)
	ON_BN_CLICKED(IDC_VERT_ALIGN_CENTER, OnVertAlignCenter)
	ON_BN_CLICKED(IDC_VERT_ALIGN_TOP, OnVertAlignTop)
	ON_BN_CLICKED(IDC_VERT_DIST_BOTTOM, OnVertDistBottom)
	ON_BN_CLICKED(IDC_VERT_DIST_CENTER, OnVertDistCenter)
	ON_BN_CLICKED(IDC_VERT_DIST_HEIGHTS, OnVertDistHeights)
	ON_BN_CLICKED(IDC_VERT_DIST_TOP, OnVertDistTop)
	//}}AFX_MSG_MAP
	ON_NOTIFY_EX( TTN_NEEDTEXT, 0, OnToolTipNotify )
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CAlignDlg message handlers

void CAlignDlg::OnAlignToPage() 
{
	
}


bool CAlignDlg::GetSelectedEffects( std::vector<EffectI*>& rEffects, std::vector<int32>& rParamTimes )
{
	CDemopajaDoc*			pDoc = GetDoc();
	ASSERT_VALID( pDoc );

	SceneC*		pScene = pDoc->GetCurrentScene();
	int32		i32Time = pDoc->GetTimecursor() * pDoc->GetFrameSizeInTicks();
	bool		bFirstBBox = true;
	float32		f32Left = 0;
	uint32		i;

	for( i = 0; i < pScene->get_layer_count(); i++ ) {

		LayerC*	pLayer = pScene->get_layer( i );

		if( !pLayer || !(pLayer->get_flags() & ITEM_VISIBLE) || (pLayer->get_flags() & ITEM_LOCKED) ||
			!pLayer->get_timesegment()->is_visible( i32Time ) )
			continue;

		int32	i32TimeOffset = pLayer->get_timesegment()->get_segment_start();

		for( uint32 j = 0; j < pLayer->get_effect_count(); j++ ) {
			EffectI*	pEffect = pLayer->get_effect( j );

			if( !pEffect || !(pEffect->get_flags() & ITEM_VISIBLE) || (pEffect->get_flags() & ITEM_LOCKED) ||
				!pEffect->get_timesegment()->is_visible( i32Time - i32TimeOffset ) )
				continue;

			if( pEffect->get_flags() & ITEM_SELECTED ) {
				int32	i32ParamTime = i32Time - (i32TimeOffset + pEffect->get_timesegment()->get_segment_start());
				rEffects.push_back( pEffect );
				rParamTimes.push_back( i32ParamTime );
			}
		}
	}

	return true;
}


void CAlignDlg::OnHorizAlignCenter() 
{
	CDemopajaDoc*	pDoc = GetDoc();
	ASSERT_VALID( pDoc );

	SceneC*		pScene = pDoc->GetCurrentScene();
	int32		i32Time = pDoc->GetTimecursor() * pDoc->GetFrameSizeInTicks();
	bool		bFirstBBox = true;
	float32		f32Center = 0;
	uint32		i;
	std::vector<EffectI*>	rEffects;
	std::vector<int32>		rParamTimes;


	GetSelectedEffects( rEffects, rParamTimes );

	if( rEffects.size() ) {

		for( i = 0; i < rEffects.size(); i++ ) {
			BBox2C	rEffectBBox = rEffects[i]->get_bbox();
			f32Center += rEffectBBox.center()[0];
		}

		f32Center /= (float32)rEffects.size();

		// align to page
		if( m_rAlignToPage.GetCheck() == 1 )
			f32Center = (float32)(pDoc->GetLayoutWidth() / 2);


		UndoC*	pUndo = new UndoC( "Align Center" );
		UndoC*	pOldUndo;

		for( i = 0; i < rEffects.size(); i++ ) {
			EffectI*	pEffect = rEffects[i];
			int32		i32ParamTime = rParamTimes[i];

			BBox2C	rEffectBBox = pEffect->get_bbox();
			float32	f32Delta = f32Center - rEffectBBox.center()[0];

			ParamVector2C*	pParamMove = (ParamVector2C*)pEffect->get_default_param( DEFAULT_PARAM_POSITION );
			// save undo stuff
			if( pParamMove ) {
				Vector2C	rPos;
				pOldUndo = pParamMove->begin_editing( pUndo );
				pParamMove->get_val( i32ParamTime, rPos );
				rPos[0] += f32Delta;
				pDoc->HandleParamNotify( pParamMove->set_val( i32ParamTime, rPos ) );	// create key at the timecursor time.
				pParamMove->end_editing( pOldUndo );
			}

		}
		pDoc->GetUndoManager()->push( pUndo );
		pDoc->SetModifiedFlag();
	}

	pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
}

void CAlignDlg::OnHorizAlignLeft() 
{
	CDemopajaDoc*	pDoc = GetDoc();
	ASSERT_VALID( pDoc );

	SceneC*		pScene = pDoc->GetCurrentScene();
	int32		i32Time = pDoc->GetTimecursor() * pDoc->GetFrameSizeInTicks();
	bool		bFirstBBox = true;
	float32		f32Left = 0;
	uint32		i;
	std::vector<EffectI*>	rEffects;
	std::vector<int32>		rParamTimes;


	GetSelectedEffects( rEffects, rParamTimes );

	if( rEffects.size() ) {

		for( i = 0; i < rEffects.size(); i++ ) {
			BBox2C	rEffectBBox = rEffects[i]->get_bbox();
			// collect selected bounds
			if( bFirstBBox ) {
				f32Left = rEffectBBox[0][0];
				bFirstBBox = false;
			}
			else if( rEffectBBox[0][0] < f32Left )
				f32Left = rEffectBBox[0][0];
		}


		// align to page
		if( m_rAlignToPage.GetCheck() == 1 )
			f32Left = 0;


		UndoC*	pUndo = new UndoC( "Align Left" );
		UndoC*	pOldUndo;

		for( i = 0; i < rEffects.size(); i++ ) {
			EffectI*	pEffect = rEffects[i];
			int32		i32ParamTime = rParamTimes[i];

			BBox2C	rEffectBBox = pEffect->get_bbox();
			float32	f32Delta = f32Left - rEffectBBox[0][0];

			ParamVector2C*	pParamMove = (ParamVector2C*)pEffect->get_default_param( DEFAULT_PARAM_POSITION );
			// save undo stuff
			if( pParamMove ) {
				Vector2C	rPos;
				pOldUndo = pParamMove->begin_editing( pUndo );
				pParamMove->get_val( i32ParamTime, rPos );
				rPos[0] += f32Delta;
				pDoc->HandleParamNotify( pParamMove->set_val( i32ParamTime, rPos ) );	// create key at the timecursor time.
				pParamMove->end_editing( pOldUndo );
			}

		}
		pDoc->GetUndoManager()->push( pUndo );

		pDoc->SetModifiedFlag();
	}

	pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
}

void CAlignDlg::OnHorizAlignRight() 
{
	CDemopajaDoc*	pDoc = GetDoc();
	ASSERT_VALID( pDoc );

	SceneC*		pScene = pDoc->GetCurrentScene();
	int32		i32Time = pDoc->GetTimecursor() * pDoc->GetFrameSizeInTicks();
	bool		bFirstBBox = true;
	float32		f32Right = 0;
	uint32		i;
	std::vector<EffectI*>	rEffects;
	std::vector<int32>		rParamTimes;


	GetSelectedEffects( rEffects, rParamTimes );

	if( rEffects.size() ) {

		for( i = 0; i < rEffects.size(); i++ ) {
			BBox2C	rEffectBBox = rEffects[i]->get_bbox();
			// collect selected bounds
			if( bFirstBBox ) {
				f32Right = rEffectBBox[1][0];
				bFirstBBox = false;
			}
			else if( rEffectBBox[1][0] > f32Right )
				f32Right = rEffectBBox[1][0];
		}


		// align to page
		if( m_rAlignToPage.GetCheck() == 1 )
			f32Right = (float32)pDoc->GetLayoutWidth();


		UndoC*	pUndo = new UndoC( "Align Right" );
		UndoC*	pOldUndo;

		for( i = 0; i < rEffects.size(); i++ ) {
			EffectI*	pEffect = rEffects[i];
			int32		i32ParamTime = rParamTimes[i];

			BBox2C	rEffectBBox = pEffect->get_bbox();
			float32	f32Delta = f32Right - rEffectBBox[1][0];

			ParamVector2C*	pParamMove = (ParamVector2C*)pEffect->get_default_param( DEFAULT_PARAM_POSITION );
			// save undo stuff
			if( pParamMove ) {
				Vector2C	rPos;
				pOldUndo = pParamMove->begin_editing( pUndo );
				pParamMove->get_val( i32ParamTime, rPos );
				rPos[0] += f32Delta;
				pDoc->HandleParamNotify( pParamMove->set_val( i32ParamTime, rPos ) );	// create key at the timecursor time.
				pParamMove->end_editing( pOldUndo );
			}

		}
		pDoc->GetUndoManager()->push( pUndo );
		pDoc->SetModifiedFlag();
	}

	pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
}

void CAlignDlg::OnHorizDistCenter() 
{
	CDemopajaDoc*	pDoc = GetDoc();
	ASSERT_VALID( pDoc );

	SceneC*		pScene = pDoc->GetCurrentScene();
	int32		i32Time = pDoc->GetTimecursor() * pDoc->GetFrameSizeInTicks();
	bool		bFirstBBox = true;
	float32		f32Left = 0;
	float32		f32Right = 0;
	uint32		i;
	std::vector<EffectI*>	rEffects;
	std::vector<int32>		rParamTimes;


	GetSelectedEffects( rEffects, rParamTimes );

	if( rEffects.size() > 1 ) {

		for( i = 0; i < rEffects.size(); i++ ) {
			BBox2C	rEffectBBox = rEffects[i]->get_bbox();
			// collect selected bounds
			if( bFirstBBox ) {
				f32Left = f32Right = rEffectBBox.center()[0];
				bFirstBBox = false;
			}
			else {
				Vector2C	rCenter = rEffectBBox.center();
				if( rCenter[0] < f32Left )
					f32Left = rCenter[0];
				if( rCenter[0] > f32Right )
					f32Right = rCenter[0];
			}
		}

		UndoC*	pUndo = new UndoC( "Distribute Centers" );
		UndoC*	pOldUndo;

		// sort effects (yes!, bubble sort)
		for( i = 0; i < rEffects.size() - 1; i++ ) {
			for( uint32 j = i + 1; j < rEffects.size(); j++ ) {
				Vector2C	rCenterI = rEffects[i]->get_bbox().center();
				Vector2C	rCenterJ = rEffects[j]->get_bbox().center();
				if( rCenterJ[0] < rCenterI[0] ) {
					EffectI*	pTmp = rEffects[j];
					rEffects[j] = rEffects[i];
					rEffects[i] = pTmp;
					int32	i32Tmp = rParamTimes[i];
					rParamTimes[i] = rParamTimes[j];
					rParamTimes[j] = i32Tmp;
				}
			}
		}

		// distance between each element
		float32	f32Dist = (f32Right - f32Left) / (float32)(rEffects.size() - 1);

		for( i = 0; i < rEffects.size(); i++ ) {
			EffectI*	pEffect = rEffects[i];
			int32		i32ParamTime = rParamTimes[i];

			Vector2C	rCenter = pEffect->get_bbox().center();
			float32		f32Delta = (f32Left + (float32)i * f32Dist) - rCenter[0];

			ParamVector2C*	pParamMove = (ParamVector2C*)pEffect->get_default_param( DEFAULT_PARAM_POSITION );
			// save undo stuff
			if( pParamMove ) {
				Vector2C	rPos;
				pOldUndo = pParamMove->begin_editing( pUndo );
				pParamMove->get_val( i32ParamTime, rPos );
				rPos[0] += f32Delta;
				pDoc->HandleParamNotify( pParamMove->set_val( i32ParamTime, rPos ) );	// create key at the timecursor time.
				pParamMove->end_editing( pOldUndo );
			}

		}
		pDoc->GetUndoManager()->push( pUndo );
		pDoc->SetModifiedFlag();
	}

	pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
}

void CAlignDlg::OnHorizDistLeft() 
{
	CDemopajaDoc*	pDoc = GetDoc();
	ASSERT_VALID( pDoc );

	SceneC*		pScene = pDoc->GetCurrentScene();
	int32		i32Time = pDoc->GetTimecursor() * pDoc->GetFrameSizeInTicks();
	bool		bFirstBBox = true;
	float32		f32Left = 0;
	float32		f32Right = 0;
	uint32		i;
	std::vector<EffectI*>	rEffects;
	std::vector<int32>		rParamTimes;


	GetSelectedEffects( rEffects, rParamTimes );

	if( rEffects.size() > 1 ) {

		for( i = 0; i < rEffects.size(); i++ ) {
			BBox2C	rEffectBBox = rEffects[i]->get_bbox();
			// collect selected bounds
			if( bFirstBBox ) {
				f32Left = f32Right = rEffectBBox[0][0];
				bFirstBBox = false;
			}
			else {
				if( rEffectBBox[0][0] < f32Left )
					f32Left = rEffectBBox[0][0];
				if( rEffectBBox[0][0] > f32Right )
					f32Right = rEffectBBox[0][0];
			}
		}

		UndoC*	pUndo = new UndoC( "Distribute Lefts" );
		UndoC*	pOldUndo;

		// sort effects (yes!, bubble sort)
		for( i = 0; i < rEffects.size() - 1; i++ ) {
			for( uint32 j = i + 1; j < rEffects.size(); j++ ) {
				BBox2C	rBBoxI = rEffects[i]->get_bbox();
				BBox2C	rBBoxJ = rEffects[j]->get_bbox();
				if( rBBoxJ[0][0] < rBBoxI[0][0] ) {
					EffectI*	pTmp = rEffects[j];
					rEffects[j] = rEffects[i];
					rEffects[i] = pTmp;
					int32	i32Tmp = rParamTimes[i];
					rParamTimes[i] = rParamTimes[j];
					rParamTimes[j] = i32Tmp;
				}
			}
		}

		// distance between each element
		float32	f32Dist = (f32Right - f32Left) / (float32)(rEffects.size() - 1);

		for( i = 0; i < rEffects.size(); i++ ) {
			EffectI*	pEffect = rEffects[i];
			int32		i32ParamTime = rParamTimes[i];

			BBox2C	rBBox = pEffect->get_bbox();
			float32	f32Delta = (f32Left + (float32)i * f32Dist) - rBBox[0][0];

			ParamVector2C*	pParamMove = (ParamVector2C*)pEffect->get_default_param( DEFAULT_PARAM_POSITION );
			// save undo stuff
			if( pParamMove ) {
				Vector2C	rPos;
				pOldUndo = pParamMove->begin_editing( pUndo );
				pParamMove->get_val( i32ParamTime, rPos );
				rPos[0] += f32Delta;
				pDoc->HandleParamNotify( pParamMove->set_val( i32ParamTime, rPos ) );	// create key at the timecursor time.
				pParamMove->end_editing( pOldUndo );
			}

		}
		pDoc->GetUndoManager()->push( pUndo );
		pDoc->SetModifiedFlag();
	}

	pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
}

void CAlignDlg::OnHorizDistRight() 
{
	CDemopajaDoc*	pDoc = GetDoc();
	ASSERT_VALID( pDoc );

	SceneC*		pScene = pDoc->GetCurrentScene();
	int32		i32Time = pDoc->GetTimecursor() * pDoc->GetFrameSizeInTicks();
	bool		bFirstBBox = true;
	float32		f32Left = 0;
	float32		f32Right = 0;
	uint32		i;
	std::vector<EffectI*>	rEffects;
	std::vector<int32>		rParamTimes;


	GetSelectedEffects( rEffects, rParamTimes );

	if( rEffects.size() > 1 ) {

		for( i = 0; i < rEffects.size(); i++ ) {
			BBox2C	rEffectBBox = rEffects[i]->get_bbox();
			// collect selected bounds
			if( bFirstBBox ) {
				f32Left = f32Right = rEffectBBox[1][0];
				bFirstBBox = false;
			}
			else {
				if( rEffectBBox[1][0] < f32Left )
					f32Left = rEffectBBox[1][0];
				if( rEffectBBox[1][0] > f32Right )
					f32Right = rEffectBBox[1][0];
			}
		}

		UndoC*	pUndo = new UndoC( "Distribute Rights" );
		UndoC*	pOldUndo;

		// sort effects (yes!, bubble sort)
		for( i = 0; i < rEffects.size() - 1; i++ ) {
			for( uint32 j = i + 1; j < rEffects.size(); j++ ) {
				BBox2C	rBBoxI = rEffects[i]->get_bbox();
				BBox2C	rBBoxJ = rEffects[j]->get_bbox();
				if( rBBoxJ[1][0] < rBBoxI[1][0] ) {
					EffectI*	pTmp = rEffects[j];
					rEffects[j] = rEffects[i];
					rEffects[i] = pTmp;
					int32	i32Tmp = rParamTimes[i];
					rParamTimes[i] = rParamTimes[j];
					rParamTimes[j] = i32Tmp;
				}
			}
		}

		// distance between each element
		float32	f32Dist = (f32Right - f32Left) / (float32)(rEffects.size() - 1);

		for( i = 0; i < rEffects.size(); i++ ) {
			EffectI*	pEffect = rEffects[i];
			int32		i32ParamTime = rParamTimes[i];

			BBox2C	rBBox = pEffect->get_bbox();
			float32	f32Delta = (f32Left + (float32)i * f32Dist) - rBBox[1][0];

			ParamVector2C*	pParamMove = (ParamVector2C*)pEffect->get_default_param( DEFAULT_PARAM_POSITION );
			// save undo stuff
			if( pParamMove ) {
				Vector2C	rPos;
				pOldUndo = pParamMove->begin_editing( pUndo );
				pParamMove->get_val( i32ParamTime, rPos );
				rPos[0] += f32Delta;
				pDoc->HandleParamNotify( pParamMove->set_val( i32ParamTime, rPos ) );	// create key at the timecursor time.
				pParamMove->end_editing( pOldUndo );
			}

		}
		pDoc->GetUndoManager()->push( pUndo );
		pDoc->SetModifiedFlag();
	}

	pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
}

void CAlignDlg::OnHorizDistWidths() 
{
	CDemopajaDoc*	pDoc = GetDoc();
	ASSERT_VALID( pDoc );

	SceneC*		pScene = pDoc->GetCurrentScene();
	int32		i32Time = pDoc->GetTimecursor() * pDoc->GetFrameSizeInTicks();
	bool		bFirstBBox = true;
	float32		f32Left = 0;
	float32		f32Right = 0;
	float32		f32UsedSpace = 0;
	uint32		i;
	std::vector<EffectI*>	rEffects;
	std::vector<int32>		rParamTimes;


	GetSelectedEffects( rEffects, rParamTimes );

	if( rEffects.size() > 1 ) {

		for( i = 0; i < rEffects.size(); i++ ) {
			BBox2C	rEffectBBox = rEffects[i]->get_bbox();
			// collect selected bounds
			if( bFirstBBox ) {
				f32Left = rEffectBBox[0][0];
				f32Right = rEffectBBox[1][0];
				bFirstBBox = false;
			}
			else {
				if( rEffectBBox[0][0] < f32Left )
					f32Left = rEffectBBox[0][0];
				if( rEffectBBox[1][0] > f32Right )
					f32Right = rEffectBBox[1][0];
			}

			f32UsedSpace += rEffectBBox.width();
		}

		UndoC*	pUndo = new UndoC( "Distribute Widths" );
		UndoC*	pOldUndo;

		// sort effects (yes!, bubble sort)
		for( i = 0; i < rEffects.size() - 1; i++ ) {
			for( uint32 j = i + 1; j < rEffects.size(); j++ ) {
				BBox2C	rBBoxI = rEffects[i]->get_bbox();
				BBox2C	rBBoxJ = rEffects[j]->get_bbox();
				if( rBBoxJ[0][0] < rBBoxI[0][0] ) {
					EffectI*	pTmp = rEffects[j];
					rEffects[j] = rEffects[i];
					rEffects[i] = pTmp;
					int32	i32Tmp = rParamTimes[i];
					rParamTimes[i] = rParamTimes[j];
					rParamTimes[j] = i32Tmp;
				}
			}
		}

		// distance between each element
		float32	f32Dist = ((f32Right - f32Left) - f32UsedSpace) / (float32)(rEffects.size() - 1);

		float32	f32X = f32Left;

		for( i = 0; i < rEffects.size(); i++ ) {
			EffectI*	pEffect = rEffects[i];
			int32		i32ParamTime = rParamTimes[i];

			BBox2C	rBBox = pEffect->get_bbox();
			float32	f32Delta = f32X - rBBox[0][0];

			f32X += rBBox.width() + f32Dist;

			ParamVector2C*	pParamMove = (ParamVector2C*)pEffect->get_default_param( DEFAULT_PARAM_POSITION );
			// save undo stuff
			if( pParamMove ) {
				Vector2C	rPos;
				pOldUndo = pParamMove->begin_editing( pUndo );
				pParamMove->get_val( i32ParamTime, rPos );
				rPos[0] += f32Delta;
				pDoc->HandleParamNotify( pParamMove->set_val( i32ParamTime, rPos ) );	// create key at the timecursor time.
				pParamMove->end_editing( pOldUndo );
			}

		}
		pDoc->GetUndoManager()->push( pUndo );
		pDoc->SetModifiedFlag();
	}

	pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
}

void CAlignDlg::OnVertAlignBottom() 
{
	CDemopajaDoc*	pDoc = GetDoc();
	ASSERT_VALID( pDoc );

	SceneC*		pScene = pDoc->GetCurrentScene();
	int32		i32Time = pDoc->GetTimecursor() * pDoc->GetFrameSizeInTicks();
	bool		bFirstBBox = true;
	float32		f32Bottom = 0;
	uint32		i;
	std::vector<EffectI*>	rEffects;
	std::vector<int32>		rParamTimes;


	GetSelectedEffects( rEffects, rParamTimes );

	if( rEffects.size() ) {

		for( i = 0; i < rEffects.size(); i++ ) {
			BBox2C	rEffectBBox = rEffects[i]->get_bbox();
			// collect selected bounds
			if( bFirstBBox ) {
				f32Bottom = rEffectBBox[0][1];
				bFirstBBox = false;
			}
			else if( rEffectBBox[0][1] < f32Bottom )
				f32Bottom = rEffectBBox[0][1];
		}


		// align to page
		if( m_rAlignToPage.GetCheck() == 1 )
			f32Bottom = 0;


		UndoC*	pUndo = new UndoC( "Align Bottom" );
		UndoC*	pOldUndo;

		for( i = 0; i < rEffects.size(); i++ ) {
			EffectI*	pEffect = rEffects[i];
			int32		i32ParamTime = rParamTimes[i];

			BBox2C	rEffectBBox = pEffect->get_bbox();
			float32	f32Delta = f32Bottom - rEffectBBox[0][1];

			ParamVector2C*	pParamMove = (ParamVector2C*)pEffect->get_default_param( DEFAULT_PARAM_POSITION );
			// save undo stuff
			if( pParamMove ) {
				Vector2C	rPos;
				pOldUndo = pParamMove->begin_editing( pUndo );
				pParamMove->get_val( i32ParamTime, rPos );
				rPos[1] += f32Delta;
				pDoc->HandleParamNotify( pParamMove->set_val( i32ParamTime, rPos ) );	// create key at the timecursor time.
				pParamMove->end_editing( pOldUndo );
			}

		}
		pDoc->GetUndoManager()->push( pUndo );
		pDoc->SetModifiedFlag();
	}

	pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
}

void CAlignDlg::OnVertAlignCenter() 
{
	CDemopajaDoc*	pDoc = GetDoc();
	ASSERT_VALID( pDoc );

	SceneC*		pScene = pDoc->GetCurrentScene();
	int32		i32Time = pDoc->GetTimecursor() * pDoc->GetFrameSizeInTicks();
	bool		bFirstBBox = true;
	float32		f32Center = 0;
	uint32		i;
	std::vector<EffectI*>	rEffects;
	std::vector<int32>		rParamTimes;


	GetSelectedEffects( rEffects, rParamTimes );

	if( rEffects.size() ) {

		for( i = 0; i < rEffects.size(); i++ ) {
			BBox2C	rEffectBBox = rEffects[i]->get_bbox();
			f32Center += rEffectBBox.center()[1];
		}

		f32Center /= (float32)rEffects.size();

		// align to page
		if( m_rAlignToPage.GetCheck() == 1 )
			f32Center = (float32)(pDoc->GetLayoutHeight() / 2);


		UndoC*	pUndo = new UndoC( "Align Center" );
		UndoC*	pOldUndo;

		for( i = 0; i < rEffects.size(); i++ ) {
			EffectI*	pEffect = rEffects[i];
			int32		i32ParamTime = rParamTimes[i];

			BBox2C	rEffectBBox = pEffect->get_bbox();
			float32	f32Delta = f32Center - rEffectBBox.center()[1];

			ParamVector2C*	pParamMove = (ParamVector2C*)pEffect->get_default_param( DEFAULT_PARAM_POSITION );
			// save undo stuff
			if( pParamMove ) {
				Vector2C	rPos;
				pOldUndo = pParamMove->begin_editing( pUndo );
				pParamMove->get_val( i32ParamTime, rPos );
				rPos[1] += f32Delta;
				pDoc->HandleParamNotify( pParamMove->set_val( i32ParamTime, rPos ) );	// create key at the timecursor time.
				pParamMove->end_editing( pOldUndo );
			}

		}
		pDoc->GetUndoManager()->push( pUndo );
		pDoc->SetModifiedFlag();
	}

	pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
}

void CAlignDlg::OnVertAlignTop() 
{
	CDemopajaDoc*	pDoc = GetDoc();
	ASSERT_VALID( pDoc );

	SceneC*		pScene = pDoc->GetCurrentScene();
	int32		i32Time = pDoc->GetTimecursor() * pDoc->GetFrameSizeInTicks();
	bool		bFirstBBox = true;
	float32		f32Top = 0;
	uint32		i;
	std::vector<EffectI*>	rEffects;
	std::vector<int32>		rParamTimes;


	GetSelectedEffects( rEffects, rParamTimes );

	if( rEffects.size() ) {

		for( i = 0; i < rEffects.size(); i++ ) {
			BBox2C	rEffectBBox = rEffects[i]->get_bbox();
			// collect selected bounds
			if( bFirstBBox ) {
				f32Top = rEffectBBox[1][1];
				bFirstBBox = false;
			}
			else if( rEffectBBox[1][1] > f32Top )
				f32Top = rEffectBBox[1][1];
		}


		// align to page
		if( m_rAlignToPage.GetCheck() == 1 )
			f32Top = (float32)pDoc->GetLayoutHeight();


		UndoC*	pUndo = new UndoC( "Align Top" );
		UndoC*	pOldUndo;

		for( i = 0; i < rEffects.size(); i++ ) {
			EffectI*	pEffect = rEffects[i];
			int32		i32ParamTime = rParamTimes[i];

			BBox2C	rEffectBBox = pEffect->get_bbox();
			float32	f32Delta = f32Top - rEffectBBox[1][1];

			ParamVector2C*	pParamMove = (ParamVector2C*)pEffect->get_default_param( DEFAULT_PARAM_POSITION );
			// save undo stuff
			if( pParamMove ) {
				Vector2C	rPos;
				pOldUndo = pParamMove->begin_editing( pUndo );
				pParamMove->get_val( i32ParamTime, rPos );
				rPos[1] += f32Delta;
				pDoc->HandleParamNotify( pParamMove->set_val( i32ParamTime, rPos ) );	// create key at the timecursor time.
				pParamMove->end_editing( pOldUndo );
			}

		}
		pDoc->GetUndoManager()->push( pUndo );
		pDoc->SetModifiedFlag();
	}

	pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
}

void CAlignDlg::OnVertDistBottom() 
{
	CDemopajaDoc*	pDoc = GetDoc();
	ASSERT_VALID( pDoc );

	SceneC*		pScene = pDoc->GetCurrentScene();
	int32		i32Time = pDoc->GetTimecursor() * pDoc->GetFrameSizeInTicks();
	bool		bFirstBBox = true;
	float32		f32Bottom = 0;
	float32		f32Top = 0;
	uint32		i;
	std::vector<EffectI*>	rEffects;
	std::vector<int32>		rParamTimes;


	GetSelectedEffects( rEffects, rParamTimes );

	if( rEffects.size() > 1 ) {

		for( i = 0; i < rEffects.size(); i++ ) {
			BBox2C	rEffectBBox = rEffects[i]->get_bbox();
			// collect selected bounds
			if( bFirstBBox ) {
				f32Bottom = f32Top = rEffectBBox[0][1];
				bFirstBBox = false;
			}
			else {
				if( rEffectBBox[0][1] < f32Bottom )
					f32Bottom = rEffectBBox[0][1];
				if( rEffectBBox[0][1] > f32Top )
					f32Top = rEffectBBox[0][1];
			}
		}

		UndoC*	pUndo = new UndoC( "Distribute Bottoms" );
		UndoC*	pOldUndo;

		// sort effects (yes!, bubble sort)
		for( i = 0; i < rEffects.size() - 1; i++ ) {
			for( uint32 j = i + 1; j < rEffects.size(); j++ ) {
				BBox2C	rBBoxI = rEffects[i]->get_bbox();
				BBox2C	rBBoxJ = rEffects[j]->get_bbox();
				if( rBBoxJ[0][1] < rBBoxI[0][1] ) {
					EffectI*	pTmp = rEffects[j];
					rEffects[j] = rEffects[i];
					rEffects[i] = pTmp;
					int32	i32Tmp = rParamTimes[i];
					rParamTimes[i] = rParamTimes[j];
					rParamTimes[j] = i32Tmp;
				}
			}
		}

		// distance between each element
		float32	f32Dist = (f32Top - f32Bottom) / (float32)(rEffects.size() - 1);

		for( i = 0; i < rEffects.size(); i++ ) {
			EffectI*	pEffect = rEffects[i];
			int32		i32ParamTime = rParamTimes[i];

			BBox2C	rBBox = pEffect->get_bbox();
			float32	f32Delta = (f32Bottom + (float32)i * f32Dist) - rBBox[0][1];

			ParamVector2C*	pParamMove = (ParamVector2C*)pEffect->get_default_param( DEFAULT_PARAM_POSITION );
			// save undo stuff
			if( pParamMove ) {
				Vector2C	rPos;
				pOldUndo = pParamMove->begin_editing( pUndo );
				pParamMove->get_val( i32ParamTime, rPos );
				rPos[1] += f32Delta;
				pDoc->HandleParamNotify( pParamMove->set_val( i32ParamTime, rPos ) );	// create key at the timecursor time.
				pParamMove->end_editing( pOldUndo );
			}

		}
		pDoc->GetUndoManager()->push( pUndo );
		pDoc->SetModifiedFlag();
	}

	pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
}

void CAlignDlg::OnVertDistCenter() 
{
	CDemopajaDoc*	pDoc = GetDoc();
	ASSERT_VALID( pDoc );

	SceneC*		pScene = pDoc->GetCurrentScene();
	int32		i32Time = pDoc->GetTimecursor() * pDoc->GetFrameSizeInTicks();
	bool		bFirstBBox = true;
	float32		f32Bottom = 0;
	float32		f32Top = 0;
	uint32		i;
	std::vector<EffectI*>	rEffects;
	std::vector<int32>		rParamTimes;


	GetSelectedEffects( rEffects, rParamTimes );

	if( rEffects.size() > 1 ) {

		for( i = 0; i < rEffects.size(); i++ ) {
			BBox2C	rEffectBBox = rEffects[i]->get_bbox();
			// collect selected bounds
			if( bFirstBBox ) {
				f32Bottom = f32Top = rEffectBBox.center()[1];
				bFirstBBox = false;
			}
			else {
				Vector2C	rCenter = rEffectBBox.center();
				if( rCenter[1] < f32Bottom )
					f32Bottom = rCenter[1];
				if( rCenter[1] > f32Top )
					f32Top = rCenter[1];
			}
		}

		UndoC*	pUndo = new UndoC( "Distribute Centers" );
		UndoC*	pOldUndo;

		// sort effects (yes!, bubble sort)
		for( i = 0; i < rEffects.size() - 1; i++ ) {
			for( uint32 j = i + 1; j < rEffects.size(); j++ ) {
				Vector2C	rCenterI = rEffects[i]->get_bbox().center();
				Vector2C	rCenterJ = rEffects[j]->get_bbox().center();
				if( rCenterJ[1] < rCenterI[1] ) {
					EffectI*	pTmp = rEffects[j];
					rEffects[j] = rEffects[i];
					rEffects[i] = pTmp;
					int32	i32Tmp = rParamTimes[i];
					rParamTimes[i] = rParamTimes[j];
					rParamTimes[j] = i32Tmp;
				}
			}
		}

		// distance between each element
		float32	f32Dist = (f32Top - f32Bottom) / (float32)(rEffects.size() - 1);

		for( i = 0; i < rEffects.size(); i++ ) {
			EffectI*	pEffect = rEffects[i];
			int32		i32ParamTime = rParamTimes[i];

			Vector2C	rCenter = pEffect->get_bbox().center();
			float32		f32Delta = (f32Bottom + (float32)i * f32Dist) - rCenter[1];

			ParamVector2C*	pParamMove = (ParamVector2C*)pEffect->get_default_param( DEFAULT_PARAM_POSITION );
			// save undo stuff
			if( pParamMove ) {
				Vector2C	rPos;
				pOldUndo = pParamMove->begin_editing( pUndo );
				pParamMove->get_val( i32ParamTime, rPos );
				rPos[1] += f32Delta;
				pDoc->HandleParamNotify( pParamMove->set_val( i32ParamTime, rPos ) );	// create key at the timecursor time.
				pParamMove->end_editing( pOldUndo );
			}

		}
		pDoc->GetUndoManager()->push( pUndo );
		pDoc->SetModifiedFlag();
	}

	pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
}

void CAlignDlg::OnVertDistHeights() 
{
	CDemopajaDoc*	pDoc = GetDoc();
	ASSERT_VALID( pDoc );

	SceneC*		pScene = pDoc->GetCurrentScene();
	int32		i32Time = pDoc->GetTimecursor() * pDoc->GetFrameSizeInTicks();
	bool		bFirstBBox = true;
	float32		f32Bottom = 0;
	float32		f32Top = 0;
	float32		f32UsedSpace = 0;
	uint32		i;
	std::vector<EffectI*>	rEffects;
	std::vector<int32>		rParamTimes;


	GetSelectedEffects( rEffects, rParamTimes );

	if( rEffects.size() > 1 ) {

		for( i = 0; i < rEffects.size(); i++ ) {
			BBox2C	rEffectBBox = rEffects[i]->get_bbox();
			// collect selected bounds
			if( bFirstBBox ) {
				f32Bottom = rEffectBBox[0][1];
				f32Top = rEffectBBox[1][1];
				bFirstBBox = false;
			}
			else {
				if( rEffectBBox[0][1] < f32Bottom )
					f32Bottom = rEffectBBox[0][1];
				if( rEffectBBox[0][1] > f32Top )
					f32Top = rEffectBBox[0][1];

				if( rEffectBBox[1][1] > f32Top )
					f32Top = rEffectBBox[1][1];
				if( rEffectBBox[1][1] < f32Bottom )
					f32Bottom = rEffectBBox[1][1];
			}

			f32UsedSpace += rEffectBBox.height();
		}


		UndoC*	pUndo = new UndoC( "Distribute Heights" );
		UndoC*	pOldUndo;

		// sort effects (yes!, bubble sort)
		for( i = 0; i < rEffects.size() - 1; i++ ) {
			for( uint32 j = i + 1; j < rEffects.size(); j++ ) {
				BBox2C	rBBoxI = rEffects[i]->get_bbox();
				BBox2C	rBBoxJ = rEffects[j]->get_bbox();
				if( rBBoxJ[0][1] < rBBoxI[0][1] ) {
					EffectI*	pTmp = rEffects[j];
					rEffects[j] = rEffects[i];
					rEffects[i] = pTmp;
					int32	i32Tmp = rParamTimes[i];
					rParamTimes[i] = rParamTimes[j];
					rParamTimes[j] = i32Tmp;
				}
			}
		}

		// distance between each element
		float32	f32Dist = ((f32Top - f32Bottom) - f32UsedSpace) / (float32)(rEffects.size() - 1);
		float32	f32Y = f32Bottom;

		for( i = 0; i < rEffects.size(); i++ ) {
			EffectI*	pEffect = rEffects[i];
			int32		i32ParamTime = rParamTimes[i];

			BBox2C	rBBox = pEffect->get_bbox();
			float32	f32Delta = f32Y - rBBox[0][1];

			f32Y += rBBox.height() + f32Dist;

			ParamVector2C*	pParamMove = (ParamVector2C*)pEffect->get_default_param( DEFAULT_PARAM_POSITION );
			// save undo stuff
			if( pParamMove ) {
				Vector2C	rPos;
				pOldUndo = pParamMove->begin_editing( pUndo );
				pParamMove->get_val( i32ParamTime, rPos );
				rPos[1] += f32Delta;
				pDoc->HandleParamNotify( pParamMove->set_val( i32ParamTime, rPos ) );	// create key at the timecursor time.
				pParamMove->end_editing( pOldUndo );
			}

		}
		pDoc->GetUndoManager()->push( pUndo );
		pDoc->SetModifiedFlag();
	}

	pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
}

void CAlignDlg::OnVertDistTop() 
{
	CDemopajaDoc*	pDoc = GetDoc();
	ASSERT_VALID( pDoc );

	SceneC*		pScene = pDoc->GetCurrentScene();
	int32		i32Time = pDoc->GetTimecursor() * pDoc->GetFrameSizeInTicks();
	bool		bFirstBBox = true;
	float32		f32Bottom = 0;
	float32		f32Top = 0;
	uint32		i;
	std::vector<EffectI*>	rEffects;
	std::vector<int32>		rParamTimes;


	GetSelectedEffects( rEffects, rParamTimes );

	if( rEffects.size() > 1 ) {

		for( i = 0; i < rEffects.size(); i++ ) {
			BBox2C	rEffectBBox = rEffects[i]->get_bbox();
			// collect selected bounds
			if( bFirstBBox ) {
				f32Bottom = f32Top = rEffectBBox[1][1];
				bFirstBBox = false;
			}
			else {
				if( rEffectBBox[1][1] < f32Bottom )
					f32Bottom = rEffectBBox[1][1];
				if( rEffectBBox[1][1] > f32Top )
					f32Top = rEffectBBox[1][1];
			}
		}

		UndoC*	pUndo = new UndoC( "Distribute Bottoms" );
		UndoC*	pOldUndo;

		// sort effects (yes!, bubble sort)
		for( i = 0; i < rEffects.size() - 1; i++ ) {
			for( uint32 j = i + 1; j < rEffects.size(); j++ ) {
				BBox2C	rBBoxI = rEffects[i]->get_bbox();
				BBox2C	rBBoxJ = rEffects[j]->get_bbox();
				if( rBBoxJ[1][1] < rBBoxI[1][1] ) {
					EffectI*	pTmp = rEffects[j];
					rEffects[j] = rEffects[i];
					rEffects[i] = pTmp;
					int32	i32Tmp = rParamTimes[i];
					rParamTimes[i] = rParamTimes[j];
					rParamTimes[j] = i32Tmp;
				}
			}
		}

		// distance between each element
		float32	f32Dist = (f32Top - f32Bottom) / (float32)(rEffects.size() - 1);

		for( i = 0; i < rEffects.size(); i++ ) {
			EffectI*	pEffect = rEffects[i];
			int32		i32ParamTime = rParamTimes[i];

			BBox2C	rBBox = pEffect->get_bbox();
			float32	f32Delta = (f32Bottom + (float32)i * f32Dist) - rBBox[1][1];

			ParamVector2C*	pParamMove = (ParamVector2C*)pEffect->get_default_param( DEFAULT_PARAM_POSITION );
			// save undo stuff
			if( pParamMove ) {
				Vector2C	rPos;
				pOldUndo = pParamMove->begin_editing( pUndo );
				pParamMove->get_val( i32ParamTime, rPos );
				rPos[1] += f32Delta;
				pDoc->HandleParamNotify( pParamMove->set_val( i32ParamTime, rPos ) );	// create key at the timecursor time.
				pParamMove->end_editing( pOldUndo );
			}

		}
		pDoc->GetUndoManager()->push( pUndo );
		pDoc->SetModifiedFlag();
	}

	pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
}


BOOL CAlignDlg::OnInitDialog() 
{
	CDialog::OnInitDialog();
	
	// Create imagelist for images used in timeline
	if( !m_rImages.Create( IDB_ALIGN, 16, 15, RGB( 255, 0, 255 ) ) )
		return -1;

	m_rHorizAlignLeft.SetIcon( m_rImages.ExtractIcon( 0 ) );
	m_rHorizAlignCenter.SetIcon( m_rImages.ExtractIcon( 1 ) );
	m_rHorizAlignRight.SetIcon( m_rImages.ExtractIcon( 2 ) );

	m_rVertAlignTop.SetIcon( m_rImages.ExtractIcon( 3 ) );
	m_rVertAlignCenter.SetIcon( m_rImages.ExtractIcon( 4 ) );
	m_rVertAlignBottom.SetIcon( m_rImages.ExtractIcon( 5 ) );

	m_rHorizDistWidths.SetIcon( m_rImages.ExtractIcon( 10 ) );
	m_rHorizDistLeft.SetIcon( m_rImages.ExtractIcon( 11 ) );
	m_rHorizDistCenter.SetIcon( m_rImages.ExtractIcon( 12 ) );
	m_rHorizDistRight.SetIcon( m_rImages.ExtractIcon( 13 ) );

	m_rVertDistHeights.SetIcon( m_rImages.ExtractIcon( 6 ) );
	m_rVertDistTop.SetIcon( m_rImages.ExtractIcon( 7 ) );
	m_rVertDistCenter.SetIcon( m_rImages.ExtractIcon( 8 ) );
	m_rVertDistBottom.SetIcon( m_rImages.ExtractIcon( 9 ) );

	m_rAlignToPage.SetIcon( m_rImages.ExtractIcon( 14 ) );

	EnableToolTips( TRUE );

	return TRUE;
}

BOOL CAlignDlg::Create( CWnd* pParentWnd ) 
{
	return CDialog::Create(IDD, pParentWnd);
}

BOOL CAlignDlg::OnToolTipNotify( UINT id, NMHDR* pNMHDR, LRESULT* pResult )
{
	TOOLTIPTEXT*	pTTT = (TOOLTIPTEXT*)pNMHDR;
	UINT			nID = pNMHDR->idFrom;

	// idFrom is actually the HWND of the tool
	if( pTTT->uFlags & TTF_IDISHWND )
		nID = ::GetDlgCtrlID( (HWND)nID );

	if( nID ) {
		CWnd*	pWnd = GetDlgItem( nID );
		if( pWnd ) {
			CString	sText;
			pWnd->GetWindowText( sText );
			pTTT->hinst = 0;
			strcpy( pTTT->szText, sText );
		}
	}

	return FALSE;
}
