// TimeLineBar.cpp : implementation file
//

#pragma warning( disable : 4786 )		// long names generated by STL

#include "PajaTypes.h"
#include "afxole.h"
#include "stdafx.h"
#include "demopaja.h"
#include "DemopajaView.h"
#include "TimeLineBar.h"
#include "LayerC.h"
#include "EffectI.h"
#include "GizmoI.h"
#include "SceneItemC.h"
#include "ParamI.h"
#include "FlatPopUpMenu.h"
#include "UndoC.h"
#include "TimeSegmentC.h"
#include "KeySelectorC.h"
#include "KeyC.h"
#include "ControllerC.h"
#include <vector>
#include <algorithm>
#include "ClassIdC.h"
#include "MainFrm.h"
#include "DeviceContextC.h"
#include "DeviceInterfaceI.h"
#include "SubSceneC.h"
#include "MusicStartTimeDlg.h"


// type-ins
#include "IntTypeInDlg.h"
#include "IntComboTypeInDlg.h"
#include "FloatTypeInDlg.h"
#include "Vector2TypeInDlg.h"
#include "Vector3TypeInDlg.h"
#include "ColorTypeInDlg.h"
#include "FileTypeInDlg.h"
#include "TextTypeInDlg.h"
#include "TimesegmentTypeInDlg.h"
#include "MarkerTypeInDlg.h"

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

using namespace PajaTypes;
using namespace Composition;
using namespace Edit;
using namespace Import;
using namespace PajaSystem;
using namespace PluginClass;



static
void
TRACE_LAST_ERROR( const char* szCaller )
{
	LPVOID lpMsgBuf;
	FormatMessage( 
		FORMAT_MESSAGE_ALLOCATE_BUFFER | 
		FORMAT_MESSAGE_FROM_SYSTEM | 
		FORMAT_MESSAGE_IGNORE_INSERTS,
		NULL,
		GetLastError(),
		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
		(LPTSTR) &lpMsgBuf,
		0,
		NULL 
	);
	TRACE( "ERROR %s: %s\n", szCaller, lpMsgBuf );
	LocalFree( lpMsgBuf );
} 


static
COLORREF
Mix( COLORREF rCol1, COLORREF rCol2, int iAlpha )
{
	int	iR, iG, iB;

	iR = ((GetRValue( rCol1 ) * iAlpha) + (GetRValue( rCol2) * (255 - iAlpha))) / 255;
	iG = ((GetGValue( rCol1 ) * iAlpha) + (GetGValue( rCol2) * (255 - iAlpha))) / 255;
	iB = ((GetBValue( rCol1 ) * iAlpha) + (GetBValue( rCol2) * (255 - iAlpha))) / 255;

	return	RGB( iR, iG, iB );
}





/////////////////////////////////////////////////////////////////////////////
// CTimeLineBar

CTimeLineBar::CTimeLineBar() :
	m_bInitialised( false ),
	m_rListRect( 0, 0, 300, 0 ),
	m_i32LayerValueSize( 100 ),
	m_i32WaveformHeight( 32 ),
	m_i32ViewTimeStart( 0 ),
	m_i32FrameWidthInPixels( 9 ),
	m_i32TrackOrigTime( 0 ),
	m_rTrackOrigPt( 0, 0 ),
	m_eTrackAction( TRACKING_NONE ),
	m_pTrackItem( 0 ),
	m_i32FirstVisible( 0 ),
	m_i32LastVisible( 0 ),
	m_hCursor( 0 ),
	m_pEditNameItem( 0 ),
	m_pEditCtrl( 0 ),
	m_ui32ScrollTimeID( 0 ),
	m_bEditingFrame( false ),
	m_bScrollingBoxSelect( false ),
	m_bFirstTimeBoxSelect( false )
{
	// empty
}

CTimeLineBar::~CTimeLineBar()
{
	if( m_ui32ScrollTimeID )
		KillTimer( m_ui32ScrollTimeID );

	delete m_pEditCtrl;
}


BEGIN_MESSAGE_MAP(CTimeLineBar, baseCMyBar)
	//{{AFX_MSG_MAP(CTimeLineBar)
	ON_WM_SIZE()
	ON_WM_CREATE()
	ON_WM_PAINT()
	ON_WM_VSCROLL()
	ON_WM_LBUTTONDOWN()
	ON_WM_LBUTTONDBLCLK()
	ON_WM_MOUSEMOVE()
	ON_WM_LBUTTONUP()
	ON_WM_ERASEBKGND()
	ON_WM_SETCURSOR()
	ON_WM_RBUTTONDOWN()
	ON_WM_TIMER()
	ON_WM_KEYDOWN()
	ON_WM_KEYUP()
	ON_WM_DESTROY()
	ON_WM_GETDLGCODE()
	ON_WM_HSCROLL()
	//}}AFX_MSG_MAP
	ON_EN_KILLFOCUS( IDC_EDITLABEL, OnKillfocusEditLabel )
	ON_EN_CHANGE( IDC_EDITLABEL, OnChangeEditLabel )
	ON_EN_CHANGE( IDC_FRAME, OnChangeFrameEdit )
	ON_BN_CLICKED( IDC_BACK, OnBack )
	ON_BN_CLICKED( IDC_PREV, OnPrev )
	ON_BN_CLICKED( IDC_PLAY, OnPlay )
	ON_BN_CLICKED( IDC_NEXT, OnNext )
	ON_BN_CLICKED( IDC_FORW, OnForw )
	ON_BN_CLICKED( IDC_CREATELAYER, OnCreateLayer )
	ON_BN_CLICKED( IDC_CREATEEFFECT, OnCreateEffect )
	ON_BN_CLICKED( IDC_DELETEEFFECT, OnDeleteEffect )
	ON_BN_CLICKED( IDC_TIMEZOOMIN, OnZoomIn )
	ON_BN_CLICKED( IDC_TIMEZOOMOUT, OnZoomOut )
	ON_NOTIFY( TCN_SELCHANGE, IDC_SCENESEL, OnSelchangeSceneSelect )
	ON_NOTIFY( NM_RCLICK, IDC_SCENESEL, OnRClickSceneSelect )
END_MESSAGE_MAP()


/////////////////////////////////////////////////////////////////////////////
// CTimeLineBar message handlers


void CTimeLineBar::OnBack()
{
	SetFocus();

	uint32		ui32NumValues = m_rTimelineRect.Width() / m_i32FrameWidthInPixels - 1;
	int32		i32MaxTime = m_i32ViewTimeStart + m_pDoc->FrameToTime( ui32NumValues );

	int32	i32Time = 0;

	SceneC*	pScene = m_pDoc->GetCurrentScene();

	if( pScene->get_marker_count() ) {
		int32	i32CursorTime = GetTimecursor();

		for( int32 i = pScene->get_marker_count() - 1; i >= 0 ; i-- ) {
			if( i32CursorTime > pScene->get_marker_time( i ) ) {
				i32Time = pScene->get_marker_time( i );
				break;
			}
		}
	}

	SetTimecursor( i32Time );

	if( GetTimecursor() < m_i32ViewTimeStart || GetTimecursor() > i32MaxTime )
		SetTimeStart( GetTimecursor() - (i32MaxTime - m_i32ViewTimeStart) / 4 );

	CDemopajaDoc*	pDoc = GetDoc();
	CDemopajaApp*	pApp = (CDemopajaApp*)AfxGetApp();
	CDemopajaView*	pView = 0;
	// get the view
	POSITION rPos = pDoc->GetFirstViewPosition();
	if( rPos )
		pView = (CDemopajaView*)pDoc->GetNextView( rPos );

	if( pView )
		pView->SetTimePos( GetTimecursor() );
	
	pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
}

void CTimeLineBar::OnPrev()
{
	SetFocus();

	uint32		ui32NumValues = m_rTimelineRect.Width() / m_i32FrameWidthInPixels - 1;
	int32		i32MaxTime = m_i32ViewTimeStart + m_pDoc->FrameToTime( ui32NumValues );

	SetTimecursor( GetTimecursor() - m_pDoc->GetFrameSizeInTicks() );

	if( GetTimecursor() < m_i32ViewTimeStart || GetTimecursor() > i32MaxTime )
		SetTimeStart( GetTimecursor() - (i32MaxTime - m_i32ViewTimeStart) / 4 );

	CDemopajaDoc*	pDoc = GetDoc();
	CDemopajaApp*	pApp = (CDemopajaApp*)AfxGetApp();
	CDemopajaView*	pView = 0;
	// get the view
	POSITION rPos = pDoc->GetFirstViewPosition();
	if( rPos )
		pView = (CDemopajaView*)pDoc->GetNextView( rPos );

	if( pView )
		pView->SetTimePos( GetTimecursor() );

	m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
}

void CTimeLineBar::OnPlay()
{
	CDemopajaDoc*	pDoc = GetDoc();
	CDemopajaApp*	pApp = (CDemopajaApp*)AfxGetApp();
	CDemopajaView*	pView = 0;
	// get the view
	POSITION rPos = pDoc->GetFirstViewPosition();
	if( rPos )
		pView = (CDemopajaView*)pDoc->GetNextView( rPos );

	if( pView ) {
		if( pView->IsPlaying() ) {
			pView->StopRenderTimer();
			m_rPlayButton.SetBitmaps( m_rPlayBitmap );
		}
		else {
			pView->StartRenderTimer( GetTimecursor(), m_pDoc->GetLastFrameTime() );
			m_rPlayButton.SetBitmaps( m_rStopBitmap );
		}
	}

	SetFocus();
	m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
}

void CTimeLineBar::OnNext()
{
	SetFocus();

	uint32		ui32NumValues = m_rTimelineRect.Width() / m_i32FrameWidthInPixels - 1;
	int32		i32MaxTime = m_i32ViewTimeStart + m_pDoc->FrameToTime( ui32NumValues );

	SetTimecursor( GetTimecursor() + m_pDoc->GetFrameSizeInTicks() );

	if( GetTimecursor() < m_i32ViewTimeStart || GetTimecursor() > i32MaxTime )
		SetTimeStart( GetTimecursor() - (i32MaxTime - m_i32ViewTimeStart) / 4 );

	CDemopajaDoc*	pDoc = GetDoc();
	CDemopajaApp*	pApp = (CDemopajaApp*)AfxGetApp();
	CDemopajaView*	pView = 0;
	// get the view
	POSITION rPos = pDoc->GetFirstViewPosition();
	if( rPos )
		pView = (CDemopajaView*)pDoc->GetNextView( rPos );

	if( pView )
		pView->SetTimePos( GetTimecursor() );

	m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
}


void CTimeLineBar::OnForw()
{
	SetFocus();

	uint32		ui32NumValues = m_rTimelineRect.Width() / m_i32FrameWidthInPixels - 1;
	int32		i32MaxTime = m_i32ViewTimeStart + m_pDoc->FrameToTime( ui32NumValues );

	int32	i32Time = m_pDoc->GetLastFrameTime();

	SceneC*	pScene = m_pDoc->GetCurrentScene();

	if( pScene->get_marker_count() ) {
		int32	i32CursorTime = GetTimecursor();

		for( uint32 i = 0; i < pScene->get_marker_count(); i++ ) {
			if( i32CursorTime < pScene->get_marker_time( i ) ) {
				i32Time = pScene->get_marker_time( i );
				break;
			}
		}
	}

	SetTimecursor( i32Time );

	if( GetTimecursor() < m_i32ViewTimeStart || GetTimecursor() > i32MaxTime )
		SetTimeStart( GetTimecursor() - (i32MaxTime - m_i32ViewTimeStart) / 4 );

	CDemopajaDoc*	pDoc = GetDoc();
	CDemopajaApp*	pApp = (CDemopajaApp*)AfxGetApp();
	CDemopajaView*	pView = 0;
	// get the view
	POSITION rPos = pDoc->GetFirstViewPosition();
	if( rPos )
		pView = (CDemopajaView*)pDoc->GetNextView( rPos );

	if( pView )
		pView->SetTimePos( GetTimecursor() );

	m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
}


void CTimeLineBar::OnNextBeat()
{
	SetFocus();

	uint32		ui32NumValues = m_rTimelineRect.Width() / m_i32FrameWidthInPixels - 1;
	int32		i32MaxTime = m_i32ViewTimeStart + m_pDoc->FrameToTime( ui32NumValues );

	int32	i32BeatLen = m_pDoc->GetQNotesPerBeat() * 256;
	int32	i32NextBeat = ((GetTimecursor() / i32BeatLen) + 1) * i32BeatLen;

	SetTimecursor( i32NextBeat );

	if( GetTimecursor() < m_i32ViewTimeStart || GetTimecursor() > i32MaxTime )
		SetTimeStart( GetTimecursor() - (i32MaxTime - m_i32ViewTimeStart) / 4 );

	CDemopajaDoc*	pDoc = GetDoc();
	CDemopajaApp*	pApp = (CDemopajaApp*)AfxGetApp();
	CDemopajaView*	pView = 0;
	// get the view
	POSITION rPos = pDoc->GetFirstViewPosition();
	if( rPos )
		pView = (CDemopajaView*)pDoc->GetNextView( rPos );

	if( pView )
		pView->SetTimePos( GetTimecursor() );

	m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
}

void CTimeLineBar::OnPrevBeat()
{
	SetFocus();

	uint32		ui32NumValues = m_rTimelineRect.Width() / m_i32FrameWidthInPixels - 1;
	int32		i32MaxTime = m_i32ViewTimeStart + m_pDoc->FrameToTime( ui32NumValues );

	int32	i32BeatLen = m_pDoc->GetQNotesPerBeat() * 256;
	int32	i32PrevBeat = ((GetTimecursor() / i32BeatLen) - 1) * i32BeatLen;

	SetTimecursor( i32PrevBeat );

	if( GetTimecursor() < m_i32ViewTimeStart || GetTimecursor() > i32MaxTime )
		SetTimeStart( GetTimecursor() - (i32MaxTime - m_i32ViewTimeStart) / 4 );

	CDemopajaDoc*	pDoc = GetDoc();
	CDemopajaApp*	pApp = (CDemopajaApp*)AfxGetApp();
	CDemopajaView*	pView = 0;
	// get the view
	POSITION rPos = pDoc->GetFirstViewPosition();
	if( rPos )
		pView = (CDemopajaView*)pDoc->GetNextView( rPos );

	if( pView )
		pView->SetTimePos( GetTimecursor() );

	m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
}



void CTimeLineBar::OnCreateLayer()
{
	SetFocus();
	m_pDoc->LayerAdd( GetTimecursor() );
	m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
}

void CTimeLineBar::OnCreateEffect()
{
	SetFocus();
	// find first selected layer
	bool	bFound = false;
	SceneItemC*	pItem = 0;
	for( uint32 i = 0; i < m_rSceneItemList.get_sceneitem_count(); i++ ) {
		pItem = m_rSceneItemList.get_sceneitem( i );
		if( pItem->get_type() == SCENEITEM_LAYER && (pItem->get_flags() & ITEM_SELECTED) ) {
			bFound = true;
			break;
		}
	}

	if( !bFound ) {
		MessageBox( "Select the Layer to insert Effect to.", "Creation Error", MB_ICONWARNING | MB_OK );
		return;
	}

	CRect	rRect;
	m_rEffectButton.GetWindowRect( rRect );

	CFlatPopupMenu	rMenuEffects;

	rMenuEffects.SetFont( "Arial" );
	rMenuEffects.SetFontSize( 8 );

	// the effects menu
	rMenuEffects.Create( AfxGetInstanceHandle() );

	FillEffectMenu( rMenuEffects );

	int32	i32Id = rMenuEffects.Track( rRect.left, rRect.bottom, NULL, true );

	// create new effect
	if( i32Id & 0x20000000 ) {
		CreateEffect( pItem, i32Id & 0x0fffffff );
	}

}

void CTimeLineBar::OnDeleteEffect()
{
	SetFocus();
	m_pDoc->LayerAndEffectDeleteSelected();
	SetScrollRanges();	// length may have changed
	m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
}

void CTimeLineBar::OnZoomIn()
{
	SetFocus();

	CString	sText;

	switch( m_i32FrameWidthInPixels ) {
	case 3:
		m_rZoomStatic.SetWindowText( "50%" );
		m_i32FrameWidthInPixels = 5;
		break;
	case 5:
		m_rZoomStatic.SetWindowText( "100%" );
		m_i32FrameWidthInPixels = 7;
		break;
	case 7:
		m_rZoomStatic.SetWindowText( "150%" );
		m_i32FrameWidthInPixels = 9;
		break;
	case 9:
		m_rZoomStatic.SetWindowText( "200%" );
		m_i32FrameWidthInPixels = 15;
		break;
	}

	m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
}

void CTimeLineBar::OnZoomOut()
{
	SetFocus();

	CString	sText;

	switch( m_i32FrameWidthInPixels ) {
	case 5:
		m_rZoomStatic.SetWindowText( "30%" );
		m_i32FrameWidthInPixels = 3;
		break;
	case 7:
		m_rZoomStatic.SetWindowText( "50%" );
		m_i32FrameWidthInPixels = 5;
		break;
	case 9:
		m_rZoomStatic.SetWindowText( "100%" );
		m_i32FrameWidthInPixels = 7;
		break;
	case 15:
		m_rZoomStatic.SetWindowText( "150%" );
		m_i32FrameWidthInPixels = 9;
		break;
	}

	m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
}

void CTimeLineBar::CutTrailingZeros( CString& str )
{
	if( str.Find( '.' ) == -1 )
		return;

	str += "0";	// just safety cap
	// remove trailing zeros
	for( int32 j = str.GetLength() - 1; j >= 0; j-- ) {
		if( str.GetAt( j ) != '0' ) {
			if( str.GetAt( j ) == '.' )
				str = str.Left( j );
			else 
				str = str.Left( j + 1 );
			break;
		}
	}
}

void CTimeLineBar::FindVisibleRange()
{
	//m_rListRect
	int32	i = 0;

	while( i < m_rSceneItemList.get_sceneitem_count() && m_rSceneItemList.get_sceneitem( i )->get_y() < GetListPos() ) {
		i++;
	}
	m_i32FirstVisible = (i == 0 ? 0 : i - 1);

	int32	i32Height = m_rListRect.Height() + GetListPos();
	while( i < m_rSceneItemList.get_sceneitem_count() && m_rSceneItemList.get_sceneitem( i )->get_y() < i32Height ) {
		i++;
	}
	m_i32LastVisible = i;

	if( m_i32FirstVisible > m_i32LastVisible )
		m_i32FirstVisible = m_i32LastVisible;
}


void CTimeLineBar::OnSize( UINT nType, int cx, int cy )
{
	baseCMyBar::OnSize( nType, cx, cy );

	if( !m_bInitialised )
		return;

	AdjustControls();
}


void CTimeLineBar::AdjustControls()
{
	CRect	rRect;

	GetClientRect( rRect );

	int32	i32VScrollSize = ::GetSystemMetrics( SM_CXVSCROLL );
	int32	i32HScrollSize = ::GetSystemMetrics( SM_CYHSCROLL );
	int32	i32BottomSize = i32HScrollSize;
	int32	i32TopSize = 23;

	if( i32BottomSize < 15 )
		i32BottomSize = 15;


	m_rSceneTab.MoveWindow( rRect.left, rRect.top, rRect.right, i32TopSize, TRUE );

	//
	// Layerlist
	//
	uint32	ui32Width = m_rListRect.Width();	// save width
	m_rListRect.left = rRect.left;
	m_rListRect.right = rRect.left + ui32Width;
	m_rListRect.top = rRect.top + 35 + m_i32WaveformHeight + i32TopSize;
	m_rListRect.bottom = rRect.bottom - i32BottomSize;

	// Layer list scrollbar
	m_rListScroll.MoveWindow( rRect.right - i32VScrollSize, m_rListRect.top, i32VScrollSize, m_rListRect.Height(), TRUE );

	//
	// Timeline
	//
	m_rTimelineRect.left = m_rListRect.right;
	m_rTimelineRect.right = rRect.right - i32VScrollSize;
	m_rTimelineRect.top = rRect.top + 35 + m_i32WaveformHeight + i32TopSize;
	m_rTimelineRect.bottom = rRect.bottom - i32BottomSize;

	// Timeline scrollbar
	m_rTimelineScroll.MoveWindow( m_rListRect.right + 82, rRect.bottom - i32BottomSize, m_rTimelineRect.Width() - 82, i32HScrollSize, TRUE );


	//
	// Ruler
	//
	m_rRulerRect.left = m_rTimelineRect.left;
	m_rRulerRect.right = m_rTimelineRect.right;
	m_rRulerRect.top = rRect.top + m_i32WaveformHeight + i32TopSize;
	m_rRulerRect.bottom = rRect.top + m_i32WaveformHeight + 35 + i32TopSize;

	//
	// Waveform
	//
	m_rWaveformRect.left = m_rTimelineRect.left;
	m_rWaveformRect.right = m_rTimelineRect.right;
	m_rWaveformRect.top = rRect.top + 2 + i32TopSize;
	m_rWaveformRect.bottom = rRect.top + m_i32WaveformHeight + i32TopSize;

	// Invalidate sampled controller data
	m_rSceneItemList.remove_cont_sample_data();

	// controlpanel
	m_rFrameEdit.MoveWindow( 150, 2 + i32TopSize, 75, 20, TRUE );

	m_rBackButton.MoveWindow( 0, 2 + i32TopSize, 25, 20, TRUE );
	m_rPrevButton.MoveWindow( 25, 2 + i32TopSize, 25, 20, TRUE );
	m_rPlayButton.MoveWindow( 50, 2 + i32TopSize, 40, 20, TRUE );
	m_rNextButton.MoveWindow( 90, 2 + i32TopSize, 25, 20, TRUE );
	m_rForwButton.MoveWindow( 115, 2 + i32TopSize, 25, 20, TRUE );

	// layer buttons
	m_rLayerButton.MoveWindow( 0, rRect.bottom - i32BottomSize, 20, i32BottomSize, TRUE );
	m_rEffectButton.MoveWindow( 20, rRect.bottom - i32BottomSize, 20, i32BottomSize, TRUE );
	m_rDelButton.MoveWindow( 40, rRect.bottom - i32BottomSize, 20, i32BottomSize, TRUE );

	// Zoom controls
	m_rZoomOutButton.MoveWindow( m_rTimelineRect.left, rRect.bottom - i32BottomSize, 20, i32BottomSize, TRUE );
	m_rZoomStatic.MoveWindow( m_rTimelineRect.left + 20, rRect.bottom - i32BottomSize + 2, 40, i32BottomSize - 2, TRUE );
	m_rZoomInButton.MoveWindow( m_rTimelineRect.left + 60, rRect.bottom - i32BottomSize, 20, i32BottomSize, TRUE );

	// update scroll ranges
	SetScrollRanges();

	Invalidate( FALSE );
}

void
CTimeLineBar::SetScrollRanges()
{
	if( !m_bInitialised )
		return;

	//
	// Layer list scroll range
	//

	SCROLLINFO	rScrollInfo;
	rScrollInfo.cbSize = sizeof( SCROLLINFO );

	int32	i32Range = m_rSceneItemList.get_list_height() + m_rListRect.Height() / 2;

	
	rScrollInfo.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_RANGE | SIF_POS;
	rScrollInfo.nMin = 0;
	rScrollInfo.nMax = i32Range;
	rScrollInfo.nPage = m_rListRect.Height();
	rScrollInfo.nPos = GetListPos();
	rScrollInfo.nTrackPos = 0;
	m_rListScroll.SetScrollInfo( &rScrollInfo );

	if( m_rListScroll.GetScrollPos() != GetListPos() )
		SetListPos( m_rListScroll.GetScrollPos() );

	FindVisibleRange();

	//
	// Timeline scroll range
	//
	int32	i32VisSeg = m_rRulerRect.Width() / m_i32FrameWidthInPixels;
	int32	i32Frames = m_pDoc->TimeToFrame( m_pDoc->GetLastFrameTime() );

	rScrollInfo.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_RANGE | SIF_POS;
	rScrollInfo.nMin = -(i32VisSeg / 3);
	rScrollInfo.nMax = i32Frames + (i32VisSeg / 3);
	rScrollInfo.nPage = i32VisSeg;
	rScrollInfo.nPos = m_pDoc->TimeToFrame( m_i32ViewTimeStart );
	rScrollInfo.nTrackPos = 0;
	m_rTimelineScroll.SetScrollInfo( &rScrollInfo );
}

void CTimeLineBar::SetTimeStart( PajaTypes::int32 i32Start )
{
	// Snap to edit time
	m_i32ViewTimeStart = m_pDoc->FrameToTime( m_pDoc->TimeToFrame( i32Start ) );

	m_rTimelineScroll.SetScrollPos( m_pDoc->TimeToFrame( m_i32ViewTimeStart ), TRUE );
}


int CTimeLineBar::OnCreate( LPCREATESTRUCT lpCreateStruct )
{
	// Get document to play with
	m_pDoc = GetDoc();
	if( !m_pDoc )
		return -1;

	if( baseCMyBar::OnCreate( lpCreateStruct ) == -1 )
		return -1;

	// register this window as drop target
	m_rDropTarget.Register( this );

	if( !m_rFont.CreateFont( 14, 0, 0, 0, FW_NORMAL, 0, 0, 0,
		ANSI_CHARSET, OUT_DEFAULT_PRECIS,
		CLIP_DEFAULT_PRECIS,
		DEFAULT_QUALITY,
		DEFAULT_PITCH | FF_DONTCARE,
		"arial" ) ) {
		return -1;
	}

	if( !m_rBoldFont.CreateFont( 14, 0, 0, 0, FW_BOLD, 0, 0, 0,
		ANSI_CHARSET, OUT_DEFAULT_PRECIS,
		CLIP_DEFAULT_PRECIS,
		DEFAULT_QUALITY,
		DEFAULT_PITCH | FF_DONTCARE,
		"arial" ) ) {
		return -1;
	}

	if( !m_rItalicFont.CreateFont( 14, 0, 0, 0, FW_NORMAL, 1, 0, 0,
		ANSI_CHARSET, OUT_DEFAULT_PRECIS,
		CLIP_DEFAULT_PRECIS,
		DEFAULT_QUALITY,
		DEFAULT_PITCH | FF_DONTCARE,
		"arial" ) ) {
		return -1;
	}

	if( !m_rSmallFont.CreateFont( 12, 0, 0, 0, FW_NORMAL, 0, 0, 0,
		ANSI_CHARSET, OUT_DEFAULT_PRECIS,
		CLIP_DEFAULT_PRECIS,
		DEFAULT_QUALITY,
		DEFAULT_PITCH | FF_DONTCARE,
		"arial" ) ) {
		return -1;
	}

	// create buttons
	if( !m_rPlayButton.Create( "", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_BITMAP | BS_VCENTER,
		CRect( 0, 0, 0, 0 ), this, IDC_PLAY ) ) {
		return -1;
	}
	if( !m_rForwButton.Create( "", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_BITMAP | BS_VCENTER,
		CRect( 0, 0, 0, 0 ), this, IDC_FORW ) )
		return -1;
	if( !m_rBackButton.Create( "", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_BITMAP | BS_VCENTER,
		CRect( 0, 0, 0, 0 ), this, IDC_BACK ) )
		return -1;
	if( !m_rNextButton.Create( "", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_BITMAP | BS_VCENTER,
		CRect( 0, 0, 0, 0 ), this, IDC_NEXT ) )
		return -1;
	if( !m_rPrevButton.Create( "", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_BITMAP | BS_VCENTER,
		CRect( 0, 0, 0, 0 ), this, IDC_PREV ) )
		return -1;


	if( !m_rLayerButton.Create( "", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_BITMAP | BS_VCENTER,
		CRect( 0, 0, 0, 0 ), this, IDC_CREATELAYER ) )
		return -1;
	if( !m_rEffectButton.Create( "", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_BITMAP | BS_VCENTER,
		CRect( 0, 0, 0, 0 ), this, IDC_CREATEEFFECT ) )
		return -1;
	if( !m_rDelButton.Create( "", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_BITMAP | BS_VCENTER,
		CRect( 0, 0, 0, 0 ), this, IDC_DELETEEFFECT ) )
		return -1;


	if( !m_rZoomInButton.Create( "", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_BITMAP | BS_VCENTER,
		CRect( 0, 0, 0, 0 ), this, IDC_TIMEZOOMIN ) )
		return -1;
	if( !m_rZoomOutButton.Create( "", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_BITMAP | BS_VCENTER,
		CRect( 0, 0, 0, 0 ), this, IDC_TIMEZOOMOUT ) )
		return -1;

	if( !m_rZoomStatic.Create( "Normal", WS_CHILD | WS_VISIBLE | SS_CENTER, CRect( 0, 0, 0, 0 ), this ) )
		return -1;
	m_rZoomStatic.SetFont( &m_rSmallFont );


	// images for scene tab
	if( !m_rImageListTab.Create( IDB_SCENETAB, 16, 2, RGB( 255, 0, 255 ) ) )
		return -1;

	if( !m_rSceneTab.Create( TCS_FIXEDWIDTH | TCS_SINGLELINE | TCS_TABS | WS_CHILD | WS_VISIBLE,
		CRect( 0, 0, 0, 0 ), this, IDC_SCENESEL ) )
		return -1;
	m_rSceneTab.SetImageList( &m_rImageListTab );
	m_rSceneTab.SetFont( &m_rSmallFont );
	m_rSceneTab.EnableDraw( BTC_ALL );
	CEnTabCtrl::EnableCustomLook( TRUE, ETC_FLAT | ETC_BACKTABS );

	UpdateSceneTab();


	// map colors
	COLORMAP	rColors[3];
	// magenta BG
	rColors[0].from = RGB( 255, 0, 255 );
	rColors[0].to = ::GetSysColor( COLOR_BTNFACE );
	// light grey
	rColors[1].from = RGB( 192, 192, 192 );
	rColors[1].to = ::GetSysColor( COLOR_BTNFACE );
	// dark grey
	rColors[2].from = RGB( 128, 128, 128 );
	rColors[2].to = ::GetSysColor( COLOR_BTNSHADOW );

	// create bitmaps for buttons
	if( !m_rPlayBitmap.LoadMappedBitmap( IDB_PLAY, 0, rColors, 3 ) )
		return -1;
	if( !m_rStopBitmap.LoadMappedBitmap( IDB_STOP, 0, rColors, 3 ) )
		return -1;
	if( !m_rForwBitmap.LoadMappedBitmap( IDB_FORW, 0, rColors, 3 ) )
		return -1;
	if( !m_rBackBitmap.LoadMappedBitmap( IDB_BACK, 0, rColors, 3 ) )
		return -1;
	if( !m_rNextBitmap.LoadMappedBitmap( IDB_NEXT, 0, rColors, 3 ) )
		return -1;
	if( !m_rPrevBitmap.LoadMappedBitmap( IDB_PREV, 0, rColors, 3 ) )
		return -1;
	if( !m_rLayerBitmap.LoadMappedBitmap( IDB_CREATELAYER, 0, rColors, 3 ) )
		return -1;
	if( !m_rEffectBitmap.LoadMappedBitmap( IDB_CREATEEFFECT, 0, rColors, 3 ) )
		return -1;
	if( !m_rDelBitmap.LoadMappedBitmap( IDB_TRASHBIN, 0, rColors, 3 ) )
		return -1;
	if( !m_rZoomInBitmap.LoadMappedBitmap( IDB_TIMEZOOMIN, 0, rColors, 3 ) )
		return -1;
	if( !m_rZoomOutBitmap.LoadMappedBitmap( IDB_TIMEZOOMOUT, 0, rColors, 3 ) )
		return -1;

	m_rPlayButton.SetBitmaps( m_rPlayBitmap );
	m_rForwButton.SetBitmaps( m_rForwBitmap );
	m_rBackButton.SetBitmaps( m_rBackBitmap );
	m_rNextButton.SetBitmaps( m_rNextBitmap );
	m_rPrevButton.SetBitmaps( m_rPrevBitmap );
	m_rPlayButton.SetFlat( FALSE );
	m_rForwButton.SetFlat( FALSE );
	m_rBackButton.SetFlat( FALSE );
	m_rNextButton.SetFlat( FALSE );
	m_rPrevButton.SetFlat( FALSE );

	m_rLayerButton.SetBitmaps( m_rLayerBitmap );
	m_rEffectButton.SetBitmaps( m_rEffectBitmap );
	m_rDelButton.SetBitmaps( m_rDelBitmap );

	m_rZoomInButton.SetBitmaps( m_rZoomInBitmap );
	m_rZoomOutButton.SetBitmaps( m_rZoomOutBitmap );

	m_rPlayButton.SetTooltipText( "Play" );
	m_rForwButton.SetTooltipText( "Forward" );
	m_rBackButton.SetTooltipText( "Back" );
	m_rNextButton.SetTooltipText( "Next" );
	m_rPrevButton.SetTooltipText( "Prev" );
	m_rLayerButton.SetTooltipText( "Create Layer" );
	m_rEffectButton.SetTooltipText( "Create Effect" );
	m_rDelButton.SetTooltipText( "Delete" );
	m_rZoomInButton.SetTooltipText( "Zoom In" );
	m_rZoomOutButton.SetTooltipText( "Zoom Out" );

	// create frame editbox
	if( !m_rFrameEdit.Create( WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL | ES_RIGHT, CRect( 0, 0, 0, 0 ), this, IDC_FRAME ) )
		return -1;
	m_rFrameEdit.ModifyStyleEx( 0, WS_EX_CLIENTEDGE, SWP_FRAMECHANGED );
	m_rFrameEdit.SetFont( &m_rBoldFont );
	m_bEditingFrame = true;
	m_rFrameEdit.SetWindowText( "0:00:00:00" );	// fake..
	m_bEditingFrame = false;

	// Create imagelist for images used in timeline
	if( !m_rImageList.Create( IDB_TIMELINE, 12, 23, RGB( 255, 0, 255 ) ) )
		return -1;

	// little images for keys
	if( !m_rImageListKeys.Create( IDB_KEYS, 8, 8, RGB( 255, 0, 255 ) ) )
		return -1;

	// Create scrollbar for layer list.
	if( !m_rListScroll.Create( WS_VISIBLE | WS_CHILD | SBS_VERT,
		CRect( 0, 0, 0, 0 ), this, IDC_LAYERLISTSCROLL ) )
		return -1;

	// Create scrollbar for timeline
	if( !m_rTimelineScroll.Create( WS_VISIBLE | WS_CHILD | SBS_HORZ,
		CRect( 0, 0, 0, 0 ), this, IDC_TIMELINESCROLL ) )
		return -1;

	m_bInitialised = true;


	SetTimeStart( -5 * m_pDoc->GetFrameSizeInTicks() );
	SetScrollRanges();


	CDemopajaApp*	pApp = (CDemopajaApp*)AfxGetApp();
	m_i32LayerValueSize = pApp->GetProfileInt( "Settings", "LayerValueSize", 150 );
	m_rListRect.right = pApp->GetProfileInt( "Settings", "LayerListWidth", 300 );
	m_i32FrameWidthInPixels = pApp->GetProfileInt( "Settings", "TimelineFrameWidthPx", 7 );
	m_i32WaveformHeight = pApp->GetProfileInt( "Settings", "WaveformHeight", 32 );

	if( m_i32FrameWidthInPixels < 3 ) m_i32FrameWidthInPixels = 3;
	if( m_i32FrameWidthInPixels > 15 ) m_i32FrameWidthInPixels = 15;

	CString	sText;
	switch( m_i32FrameWidthInPixels ) {
	case 3: sText = "30%"; break;
	case 5: sText = "50%"; break;
	case 7: sText = "100%"; break;
	case 9: sText = "150%"; break;
	case 15: sText = "200%"; break;
	}
	m_rZoomStatic.SetWindowText( sText );

	return 0;
}


const char* CTimeLineBar::GetItemName( SceneItemC* pItem )
{
	static char	szNull[] = "";

	if( pItem->get_type() == SCENEITEM_LAYER )
		return pItem->get_layer()->get_name();
	else if( pItem->get_type() == SCENEITEM_EFFECT )
		return pItem->get_effect()->get_name();
	else if( pItem->get_type() == SCENEITEM_GIZMO )
		return pItem->get_gizmo()->get_name();
	else if( pItem->get_type() == SCENEITEM_PARAMETER )
		return pItem->get_parameter()->get_name();

	return szNull;
}


void CTimeLineBar::DrawWaveform( CDC* pDC )
{
	int32	i;

	CRect	rInner = m_rWaveformRect;
	CPen	rGreyPen( PS_SOLID, 1, ::GetSysColor( COLOR_BTNSHADOW ) );
	CPen	rLightPen( PS_SOLID, 1, ::GetSysColor( COLOR_BTNHILIGHT ) );
	CPen	rWhitePen( PS_SOLID, 1, GetColor( TIMELINE_BACK ) );
	CPen*	pOldPen = pDC->SelectObject( &rGreyPen );

	CFont*	pOldFont = pDC->SelectObject( &m_rSmallFont );
	pDC->SetBkMode( TRANSPARENT );
	pDC->SetTextColor( GetColor( TIMELINE_BLACK ) );
	uint32	ui32OldAlign = pDC->SetTextAlign( TA_TOP | TA_LEFT );

	rInner.DeflateRect( 1, 0, 1, 0 );


	pDC->SelectObject( &rGreyPen );
	pDC->MoveTo( m_rWaveformRect.left, m_rWaveformRect.bottom );
	pDC->LineTo( m_rWaveformRect.left, m_rWaveformRect.top );
	pDC->LineTo( m_rWaveformRect.right, m_rWaveformRect.top );
	
	pDC->SelectObject( &rLightPen );
	pDC->MoveTo( m_rWaveformRect.right - 1, m_rWaveformRect.top );
	pDC->LineTo( m_rWaveformRect.right - 1, m_rWaveformRect.bottom );


	// save old clip region
	CRect	rOldRect;
	CRgn	rOldClipRgn;
	pDC->GetClipBox( rOldRect );
	rOldClipRgn.CreateRectRgnIndirect( rOldRect );

	// create new clip region
	CRgn	rRegion;
	rRegion.CreateRectRgnIndirect( rInner );
	pDC->SelectClipRgn( &rRegion );



	// Clear bg

	uint32	ui32NumValues = m_rTimelineRect.Width() / m_i32FrameWidthInPixels + 1;
	int32	i32MinTime = m_i32ViewTimeStart;
	int32	i32MaxTime = m_i32ViewTimeStart + m_pDoc->FrameToTime( ui32NumValues );
	SceneC*	pScene = m_pDoc->GetCurrentScene();

	COLORREF	rMarkerColors[9] = {
		0,
		RGB( 255, 255, 255 ),
		RGB( 255, 0,   0 ),
		RGB( 255, 255, 0 ),
		RGB( 0,   255, 0 ),
		RGB( 0,   255, 255 ),
		RGB( 0,   0,   255 ),
		RGB( 255, 0,   255 ),
		RGB( 0,   0,   0 ),
	};

//	COLORREF	rBaseGrey = ::GetSysColor( COLOR_BTNFACE );
//	if( m_eTrackAction == TRACKING_VERTRULER )
//		rBaseGrey = ::GetSysColor( COLOR_BTNHILIGHT );
	COLORREF	rBaseGrey = GetColor( TIMELINE_LIGHT_TICK );

	rMarkerColors[0] = rBaseGrey;


	for( i = 0; i < pScene->get_marker_count() + 1; i++ )
	{
		COLORREF	rColor = rBaseGrey;
		int32	i32StartTime = i32MinTime;
		int32 i32EndTime = i32MaxTime;

		// Get start time and section color
		if( (i - 1) >= 0 )
		{
			i32StartTime = pScene->get_marker_time( i - 1 );
			int32	i32Color = pScene->get_marker_color( i - 1 );
			if( i32Color >= 0 && i32Color < 9 )
				rColor = rMarkerColors[i32Color];
			else
				rColor = rBaseGrey;

			rColor = Mix( rColor, rBaseGrey, 64 );
		}

		// Get end time
		if( i < pScene->get_marker_count() )
		{
			i32EndTime = pScene->get_marker_time( i );
		}

		// Clamp section to active area
		if( i32StartTime < i32MinTime )
			i32StartTime = i32MinTime;

		if( i32EndTime > i32EndTime )
			i32EndTime = i32EndTime;

		// Clamp section to demo length
		if( i32StartTime < 0 )
			i32StartTime = 0;
		if( i32StartTime > pScene->get_duration() )
			i32StartTime = pScene->get_duration();

		if( i32EndTime < 0 )
			i32EndTime = 0;
		if( i32EndTime > pScene->get_duration() )
			i32EndTime = pScene->get_duration();

		if( i32StartTime > i32MaxTime )
			break;

		if( i32EndTime < 1 )
			continue;

		if( (i32EndTime - i32StartTime) < 1 )
			continue;


		int32	i32StartX = m_pDoc->TimeToFrame( i32StartTime - i32MinTime ) * m_i32FrameWidthInPixels + m_rRulerRect.left;
		int32	i32EndX = m_pDoc->TimeToFrame( i32EndTime - i32MinTime ) * m_i32FrameWidthInPixels + m_rRulerRect.left;

		pDC->FillSolidRect( i32StartX, rInner.top, i32EndX - i32StartX, rInner.Height(), rColor );
	}


//	pDC->FillSolidRect( &rInner, GetColor( TIMELINE_LIGHT_TICK ) );


	float64	f64TimeScale = (float64)m_pDoc->GetBeatsPerMin() * (float64)m_pDoc->GetQNotesPerBeat() * 256.0 / 60.0;

	int32	i32WaveFormStartTime = m_i32ViewTimeStart;
	i32WaveFormStartTime += m_pDoc->GetCurrentScene()->get_music_start_time();

//	TRACE( "start: %d (%f)\n", i32WaveFormStartTime, (float32)i32WaveFormStartTime / (float32)f64TimeScale );

	// Draw something nice
	int32	i32Height = m_rWaveformRect.Height() - 2;

	int32	i32View = m_pDoc->GetMusicDataView();

	uint8*	pData = 0;
	uint32	ui32DataLength = 0;
	int32		i32DataFreq = 1;
	bool		bIsStereo = false;

	if( i32View >= MUSIC_VIEW_BAND_0 && i32View <= MUSIC_VIEW_BAND_7 )
	{
		int32	i32Band = i32View - MUSIC_VIEW_BAND_0;
		pData = m_pDoc->GetBandData( i32Band );
		ui32DataLength = m_pDoc->GetBandDataLength();
		i32DataFreq = m_pDoc->GetBandDataSamplesPerSecond();
		bIsStereo = false;
	}
	else
	{
		pData = m_pDoc->GetMusicData();
		ui32DataLength = m_pDoc->GetMusicDataLength() / 2;
		i32DataFreq = m_pDoc->GetMusicDataSamplesPerSecond();
		bIsStereo = m_pDoc->GetMusicDataStereo();
	}


	COLORREF	rColor = GetColor( TIMELINE_PARAM_SHADOW );

	if( m_pDoc->GetMusicShowWaveform() && pData && ui32DataLength )
	{
		uint32	ui32SampleSize = 1;

		if( bIsStereo ) {
			ui32DataLength /= 2;
			ui32SampleSize = 2;
			if( i32View == MUSIC_VIEW_STEREO )
				i32Height /= 2;
		}

		int32	i32StartX = rInner.left;
		int32	i32StartY = m_rWaveformRect.top + 1;


		if( i32View == MUSIC_VIEW_STEREO ) {
			pDC->SelectObject( &rWhitePen );
			pDC->MoveTo( rInner.left, rInner.top + i32Height / 2 );
			pDC->LineTo( rInner.right, rInner.top + i32Height / 2 );
			pDC->MoveTo( rInner.left, rInner.top + i32Height + i32Height / 2 );
			pDC->LineTo( rInner.right, rInner.top + i32Height + i32Height / 2 );

			pDC->SelectObject( &rGreyPen );
			pDC->MoveTo( rInner.left, rInner.top + i32Height );
			pDC->LineTo( rInner.right, rInner.top + i32Height );
		}
		else {
			pDC->SelectObject( &rWhitePen );
			pDC->MoveTo( rInner.left, rInner.top + i32Height / 2 );
			pDC->LineTo( rInner.right, rInner.top + i32Height / 2 );
		}

		if( i32View == MUSIC_VIEW_LEFT ) {
			pDC->TextOut( rInner.left + 2, rInner.top, "L" );
		}
		else if( i32View == MUSIC_VIEW_RIGHT ) {
			pDC->TextOut( rInner.left + 2, rInner.top, "R" );
		}
		else if( i32View == MUSIC_VIEW_MONO ) {
			pDC->TextOut( rInner.left + 2, rInner.top, "M" );
		}
		else if( i32View == MUSIC_VIEW_STEREO ) {
			pDC->TextOut( rInner.left + 2, rInner.top, "L" );
			pDC->TextOut( rInner.left + 2, rInner.top + i32Height, "R" );
		}
		else if( i32View >= MUSIC_VIEW_BAND_0 && i32View <= MUSIC_VIEW_BAND_7 ) {
			char	szMsg[] = "Band 0";
			szMsg[5] += i32View - MUSIC_VIEW_BAND_0;
			pDC->TextOut( rInner.left + 2, rInner.top, szMsg );
		}

		uint32	i32Waveforms = ui32SampleSize;
		bool	bForceMono = false;
		bool	bSimpleData = false;

		if( i32View == MUSIC_VIEW_LEFT ) {
			i32Waveforms = 1;
			bSimpleData = false;
		}
		else if( i32View == MUSIC_VIEW_RIGHT ) {
			i32Waveforms = 1;
			pData++;	// next channel
			bSimpleData = false;
		}
		else if( bIsStereo && i32View == MUSIC_VIEW_MONO ) {
			bForceMono = true;
			i32Waveforms = 1;
			bSimpleData = false;
		}
		else if( i32View >= MUSIC_VIEW_BAND_0 && i32View <= MUSIC_VIEW_BAND_7 ) {
			bForceMono = false;
			i32Waveforms = 1;
			bSimpleData = true;
		}

		ui32SampleSize *= 2;

		int32	i32Index;
		int32	i32Min, i32Max;
		int32	i32CurMin, i32CurMax;
		int32	i32PrevMin, i32PrevMax;
		
		float64	f64Index, f64Delta;

		f64Delta = ((float64)m_pDoc->GetFrameSizeInTicks() / f64TimeScale * (float64)i32DataFreq) / (float64)m_i32FrameWidthInPixels;

		for( uint32 j = 0; j < i32Waveforms; j++ ) {

			i32PrevMin = i32Height / 2;
			i32PrevMax = i32Height / 2;

			i32Min = i32Height / 2;
			i32Max = i32Height / 2;
			f64Index = (float64)i32WaveFormStartTime / f64TimeScale * (float64)i32DataFreq;

			uint32	j;
			int32	i32PrevIndex = (int32)f64Index;
			if( i32PrevIndex < 0 )
				i32PrevIndex = 0;
			else if( i32PrevIndex >= (int32)ui32DataLength )
				i32PrevIndex = (int32)ui32DataLength;

			for( i = 0; i < rInner.Width(); i++ ) {

				i32Index = (int32)f64Index;
				f64Index += f64Delta;

				if( i32Index < 0 )
					continue;
				else if( i32Index >= (int32)ui32DataLength )
					break;

				if( bSimpleData )
				{
					i32Min = 0;
					i32Max = pData[i32Index] * i32Height >> 8;
					for( uint32 k = i32PrevIndex; k < i32Index; k++ ) {
						i32Max = __max( i32Max, (pData[k] * i32Height) >> 8 );
					}
				}
				else
				{
					i32Min = pData[i32Index * ui32SampleSize] * i32Height >> 8;
					i32Max = pData[i32Index * ui32SampleSize + 1] * i32Height >> 8;

					if( bForceMono ) {
						for( uint32 k = i32PrevIndex; k < i32Index; k++ ) {
							i32Min = __min( i32Min, (pData[k * ui32SampleSize] * i32Height) >> 8 );
							i32Max = __max( i32Max, (pData[k * ui32SampleSize + 1] * i32Height) >> 8 );
							i32Min = __min( i32Min, (pData[k * ui32SampleSize + 2] * i32Height) >> 8 );
							i32Max = __max( i32Max, (pData[k * ui32SampleSize + 3] * i32Height) >> 8 );
						}
					}
					else {
						for( uint32 k = i32PrevIndex; k < i32Index; k++ ) {
							i32Min = __min( i32Min, (pData[k * ui32SampleSize] * i32Height) >> 8 );
							i32Max = __max( i32Max, (pData[k * ui32SampleSize + 1] * i32Height) >> 8 );
						}
					}
				}

				i32CurMin = i32Min;
				i32CurMax = i32Max;

				pDC->FillSolidRect( i32StartX + i, i32StartY + i32Height - i32CurMax, 1, i32CurMax - i32CurMin + 1, rColor );


				i32PrevMin = i32Min;
				i32PrevMax = i32Max;
				i32PrevIndex = i32Index;
			}
			pData += 2;	// next channel
			i32StartY += i32Height;
		}
	}
	else {
		pDC->SelectObject( &rWhitePen );
		pDC->MoveTo( rInner.left, rInner.top + i32Height / 2 );
		pDC->LineTo( rInner.right, rInner.top + i32Height / 2 );
		pDC->TextOut( rInner.left + 2, rInner.top, "M" );
	}

	//
	// Draw the bands
	//
	if( m_pDoc->GetBandDataLength() > 0 )
	{

		uint32	ui32DataLength = m_pDoc->GetBandDataLength();
		int32		i32DataFreq = m_pDoc->GetBandDataSamplesPerSecond();

		int32	i32CurIndex = (int32)((float64)GetTimecursor() / f64TimeScale * (float64)i32DataFreq);

		if( i32CurIndex >= 0 && i32CurIndex < (int32)ui32DataLength )
		{
			for( i = 0; i < MUSIC_BAND_COUNT; i++ )
			{
				uint8*	pData = m_pDoc->GetBandData( i );
				if( pData )
				{
					int32	i32Bar = (pData[i32CurIndex] * (m_rWaveformRect.Height() - 4)) / 256;
					pDC->FillSolidRect( rInner.right - 2 - MUSIC_BAND_COUNT * 6 + i * 6, m_rWaveformRect.bottom - i32Bar + 2, 6, i32Bar, GetColor( TIMELINE_BLACK ) );
				}
			}
		}
	}

/*
		int32	i32Band = i32View - MUSIC_VIEW_BAND_0;
		pData = m_pDoc->GetBandData( i32Band );
		ui32DataLength = m_pDoc->GetBandDataLength() / 2;
		i32DataFreq = m_pDoc->GetBandDataSamplesPerSecond();
		bIsStereo = false;

			f64Index = (float64)i32WaveFormStartTime / f64TimeScale * (float64)i32DataFreq;
*/

	//
	// Draw markers
	//

	if( m_pDoc->GetShowMarkers() )
	{
		pDC->SelectObject( &m_rSmallFont );
		pDC->SelectObject( &rGreyPen );
		pDC->SetTextAlign( TA_TOP );

		for( i = pScene->get_marker_count() - 1; i >= 0 ; i-- ) {
			int32	i32Time = pScene->get_marker_time( i );
			if( i32Time >= i32MinTime && i32Time < i32MaxTime ) {

				CString	sName = pScene->get_marker_name( i );
				CSize	rSize = pDC->GetTextExtent( sName );
				int32	i32X = m_pDoc->TimeToFrame( i32Time - i32MinTime ) * m_i32FrameWidthInPixels + m_i32FrameWidthInPixels / 2 + m_rWaveformRect.left;
				int32	i32Y;

				i32Y = m_rWaveformRect.bottom - rSize.cy - 7;

				POINT	rPoints[5];
				rPoints[0].x = i32X;
				rPoints[0].y = i32Y;
				rPoints[1].x = i32X + rSize.cx + 4;
				rPoints[1].y = i32Y;
				rPoints[2].x = i32X + rSize.cx + 4;
				rPoints[2].y = i32Y + rSize.cy + 2;
				rPoints[3].x = i32X + 4;
				rPoints[3].y = i32Y + rSize.cy + 2;
				rPoints[4].x = i32X;
				rPoints[4].y = i32Y + rSize.cy + 2 + 4;

				pDC->Polygon( rPoints, 5 );
				pDC->TextOut( i32X + 3, i32Y + 1, sName );
			}
		}
	}

	// select old objects
	pDC->SelectObject( pOldFont );
	pDC->SelectObject( pOldPen );
	// restore text align
	pDC->SetTextAlign( ui32OldAlign );

	// restore old clip region
	pDC->SelectClipRgn( &rOldClipRgn );
	rOldClipRgn.DeleteObject();
	rRegion.DeleteObject();
}

void CTimeLineBar::DrawTimeRuler( CDC* pDC )
{
	int32	i;
	CRect	rInner = m_rRulerRect;
	CPen	rBlackPen( PS_SOLID, 1, ::GetSysColor( COLOR_BTNTEXT ) );
	CPen	rGreyPen( PS_SOLID, 1, ::GetSysColor( COLOR_BTNSHADOW ) );
	CPen	rLightPen( PS_SOLID, 1, ::GetSysColor( COLOR_BTNHILIGHT ) );
	CPen*	pOldPen = pDC->SelectObject( &rBlackPen );
	CBrush	rGreyBrush( GetColor( TIMELINE_LIGHT_TICK ) );
	CBrush*	pOldBrush = pDC->SelectObject( &rGreyBrush );

	uint32	ui32NumValues = m_rTimelineRect.Width() / m_i32FrameWidthInPixels + 1;
	int32	i32MinTime = m_i32ViewTimeStart;
	int32	i32MaxTime = m_i32ViewTimeStart + m_pDoc->FrameToTime( ui32NumValues );

	CFont*	pOldFont = pDC->SelectObject( &m_rSmallFont );
	pDC->SetBkMode( TRANSPARENT );
	pDC->SetTextColor( ::GetSysColor( COLOR_BTNTEXT ) );
	uint32	ui32OldAlign = pDC->SetTextAlign( TA_BASELINE );

	rInner.DeflateRect( 1, 15, 1, 1 );

	// Clear bg
	if( m_eTrackAction == TRACKING_VERTRULER )
		pDC->FillSolidRect( &rInner, ::GetSysColor( COLOR_BTNHILIGHT ) );
	else
		pDC->FillSolidRect( &rInner, ::GetSysColor( COLOR_BTNFACE ) );


	SceneC*	pScene = m_pDoc->GetCurrentScene();

/*	COLORREF	rMarkerColors[9] = {
		0,
		RGB( 255, 255, 255 ),
		RGB( 255, 0,   0 ),
		RGB( 255, 255, 0 ),
		RGB( 0,   255, 0 ),
		RGB( 0,   255, 255 ),
		RGB( 0,   0,   255 ),
		RGB( 255, 0,   255 ),
		RGB( 0,   0,   0 ),
	};

	COLORREF	rBaseGrey = ::GetSysColor( COLOR_BTNFACE );
	if( m_eTrackAction == TRACKING_VERTRULER )
		rBaseGrey = ::GetSysColor( COLOR_BTNHILIGHT );

	rMarkerColors[0] = rBaseGrey;


	for( i = 0; i < pScene->get_marker_count() + 1; i++ )
	{
		COLORREF	rColor = ::GetSysColor( COLOR_BTNFACE );
		int32	i32StartTime = i32MinTime;
		int32 i32EndTime = i32MaxTime;

		// Get start time and section color
		if( (i - 1) >= 0 )
		{
			i32StartTime = pScene->get_marker_time( i - 1 );
			int32	i32Color = pScene->get_marker_color( i - 1 );
			if( i32Color >= 0 && i32Color < 9 )
				rColor = rMarkerColors[i32Color];
			else
				rColor = ::GetSysColor( COLOR_BTNFACE );

			rColor = Mix( rColor, rBaseGrey, 64 );
		}

		// Get end time
		if( i < pScene->get_marker_count() )
		{
			i32EndTime = pScene->get_marker_time( i );
		}

		// Clamp section to active area
		if( i32StartTime < i32MinTime )
			i32StartTime = i32MinTime;

		if( i32EndTime > i32EndTime )
			i32EndTime = i32EndTime;

		// Clamp section to demo length
		if( i32StartTime < 0 )
			i32StartTime = 0;
		if( i32StartTime > pScene->get_duration() )
			i32StartTime = pScene->get_duration();

		if( i32EndTime < 0 )
			i32EndTime = 0;
		if( i32EndTime > pScene->get_duration() )
			i32EndTime = pScene->get_duration();

		if( i32StartTime > i32MaxTime )
			break;

		if( i32EndTime < 1 )
			continue;

		if( (i32EndTime - i32StartTime) < 1 )
			continue;


		int32	i32StartX = m_pDoc->TimeToFrame( i32StartTime - i32MinTime ) * m_i32FrameWidthInPixels + m_rRulerRect.left;
		int32	i32EndX = m_pDoc->TimeToFrame( i32EndTime - i32MinTime ) * m_i32FrameWidthInPixels + m_rRulerRect.left;

		pDC->FillSolidRect( i32StartX, rInner.top, i32EndX - i32StartX, rInner.Height(), rColor );
	}
*/

	pDC->SelectObject( &rLightPen );
	pDC->MoveTo( m_rRulerRect.right - 1, m_rRulerRect.top );
	pDC->LineTo( m_rRulerRect.right - 1, m_rRulerRect.bottom );

	pDC->SelectObject( &rBlackPen );

	// save old clip region
	CRect	rOldRect;
	CRgn	rOldClipRgn;
	pDC->GetClipBox( rOldRect );
	rOldClipRgn.CreateRectRgnIndirect( rOldRect );

	// create new clip region
	CRgn	rRegion;
	rRegion.CreateRectRgnIndirect( rInner );
	pDC->SelectClipRgn( &rRegion );

	pDC->SelectObject( &m_rSmallFont );
	pDC->SelectObject( &rGreyPen );

	int32	i32BeatLen = m_pDoc->GetQNotesPerBeat() * 256;
	int32	i32MeasureLen = m_pDoc->GetBeatsPerMeasure() * m_pDoc->GetQNotesPerBeat() * 256;
	int32	i32TimeScale = m_pDoc->GetBeatsPerMin() * m_pDoc->GetQNotesPerBeat() * 256;
	int32	i32Hours, i32Mins, i32Secs, i32Frames;
	int32	i32PrevLabelRightX = -1000;
	int32	i32Time = m_i32ViewTimeStart + pScene->get_music_start_time();
	CString	sTime;

	// Make the time start from whole tick
//	i32Time = (i32Time / m_pDoc->GetFrameSizeInTicks()) * m_pDoc->GetFrameSizeInTicks();

	//
	// Draw ticks
	//

	for( i = m_rRulerRect.left; i < m_rRulerRect.right; i += m_i32FrameWidthInPixels ) {
		pDC->MoveTo( i, m_rRulerRect.bottom );

		if( (i32Time % i32BeatLen) == 0 ) {
			if( (i32Time % i32MeasureLen) == 0 ) {
				// Draw measurement tick and label
				i32Secs = abs( (i32Time * 60 / i32TimeScale) % 60 );
				i32Mins = abs( (i32Time / i32TimeScale) % 60 );
				i32Hours = abs( i32Time / 60 / i32TimeScale );
				i32Frames = m_pDoc->TimeToFrame( abs( i32Time - ((i32Hours * 3600 + i32Mins * 60 + i32Secs) * i32TimeScale) / 60 ) );

				if( i32Hours )
					sTime.Format( "%d:%02d:%02d", i32Hours, i32Mins, i32Secs );
				else if( i32Mins )
					sTime.Format( "%d:%02d", i32Mins, i32Secs );
				else
					sTime.Format( "%d", i32Secs );

				i32PrevLabelRightX = i + 3;

				CSize	rSize;

				pDC->SelectObject( &m_rBoldFont );
				rSize = pDC->GetTextExtent( sTime );

				pDC->TextOut( i + 3, m_rRulerRect.bottom - 6, sTime );

				i32PrevLabelRightX += rSize.cx;

				sTime.Format( ":%02d", i32Frames );

				pDC->SelectObject( &m_rSmallFont );
				pDC->TextOut( i + 2 + rSize.cx, m_rRulerRect.bottom - 6/*17*/, sTime );

				rSize = pDC->GetTextExtent( sTime );

				i32PrevLabelRightX += rSize.cx;

				pDC->SelectObject( &rBlackPen );
				pDC->LineTo( i, m_rRulerRect.bottom - 22 );
				pDC->SelectObject( &rGreyPen );
			}
			else {
				if( i > i32PrevLabelRightX ) {
					// Draw beat tick and label
					pDC->LineTo( i, m_rRulerRect.bottom - 15 );
					int32	i32Frame = m_pDoc->TimeToFrame( abs( i32Time ) );
					sTime.Format( "%d", i32Frame );
					pDC->TextOut( i + 3, m_rRulerRect.bottom - 6, sTime );
				}
				else {
					// previous label overlaps this one, draw the tick smaller.
					pDC->LineTo( i, m_rRulerRect.bottom - 7 );
				}
			}
		}
		else {
			// Draw normal tick.
			pDC->LineTo( i, m_rRulerRect.bottom - 5 );
		}

		i32Time += m_pDoc->GetFrameSizeInTicks();
	}

/*
	//
	// Draw markers
	//

	pDC->SelectObject( &m_rSmallFont );
	pDC->SelectObject( &rGreyPen );
	pDC->SetTextAlign( TA_TOP );

	for( i = pScene->get_marker_count() - 1; i >= 0 ; i-- ) {
		int32	i32Time = pScene->get_marker_time( i );
		if( i32Time >= i32MinTime && i32Time < i32MaxTime ) {

			CString	sName = pScene->get_marker_name( i );
			CSize	rSize = pDC->GetTextExtent( sName );
			int32	i32X = m_pDoc->TimeToFrame( i32Time - i32MinTime ) * m_i32FrameWidthInPixels + m_i32FrameWidthInPixels / 2 + m_rRulerRect.left;
			int32	i32Y;

			i32Y = m_rRulerRect.bottom - rSize.cy - 7;

			POINT	rPoints[5];
			rPoints[0].x = i32X;
			rPoints[0].y = i32Y;
			rPoints[1].x = i32X + rSize.cx + 4;
			rPoints[1].y = i32Y;
			rPoints[2].x = i32X + rSize.cx + 4;
			rPoints[2].y = i32Y + rSize.cy + 2;
			rPoints[3].x = i32X + 4;
			rPoints[3].y = i32Y + rSize.cy + 2;
			rPoints[4].x = i32X;
			rPoints[4].y = i32Y + rSize.cy + 2 + 4;

			pDC->Polygon( rPoints, 5 );
			pDC->TextOut( i32X + 3, i32Y + 1, sName );
		}
	}
*/

	// select old objects
	pDC->SelectObject( pOldFont );
	pDC->SelectObject( pOldPen );
	pDC->SelectObject( pOldBrush );

	// restore text align
	pDC->SetTextAlign( ui32OldAlign );

	// restore old clip region
	pDC->SelectClipRgn( &rOldClipRgn );
	rOldClipRgn.DeleteObject();
	rRegion.DeleteObject();
}


// this should be drawn while the clippingregion (and new origo) of timeline or curves is still active.
void CTimeLineBar::DrawTimeCursor( CDC* pDC, bool bClearOld )
{

	CRect	rInner;
	rInner.top = m_rRulerRect.top;
	rInner.left = m_rRulerRect.left;
	rInner.right = m_rRulerRect.right;
	rInner.bottom = m_rRulerRect.top + 15;

	// Clear bg
	pDC->FillSolidRect( &rInner, ::GetSysColor( COLOR_BTNFACE ) );

	pDC->Draw3dRect( CRect( m_rRulerRect.left, m_rRulerRect.top + 5, m_rRulerRect.right, m_rRulerRect.top + 8 ), ::GetSysColor( COLOR_3DHILIGHT ), ::GetSysColor( COLOR_3DSHADOW ) );

	pDC->Draw3dRect( CRect( m_rRulerRect.left, m_rRulerRect.top, m_rRulerRect.right, m_rRulerRect.top + 14 ), ::GetSysColor( COLOR_3DSHADOW ), ::GetSysColor( COLOR_3DHILIGHT ) );

	// save old clip region
	CRect	rOldRect;
	CRgn	rOldClipRgn;
	pDC->GetClipBox( rOldRect );
	rOldClipRgn.CreateRectRgnIndirect( rOldRect );

	// create new clip region
	CRgn	rRegion;
	rRegion.CreateRectRgnIndirect( CRect( m_rTimelineRect.left + 1, m_rWaveformRect.top,
									m_rTimelineRect.right - 1, m_rTimelineRect.bottom - 1 ) );
	pDC->SelectClipRgn( &rRegion );


	int32	i32FirstFrame = 0;
	int32	i32LastFrame = m_pDoc->TimeToFrame( m_pDoc->GetLastFrameTime() );
	int32	i32Y = m_rRulerRect.top;
	int32	i32FirstX = (i32FirstFrame - m_pDoc->TimeToFrame( m_i32ViewTimeStart )) * m_i32FrameWidthInPixels + m_rRulerRect.left;
	int32	i32LastX = (i32LastFrame - m_pDoc->TimeToFrame( m_i32ViewTimeStart )) * m_i32FrameWidthInPixels + m_rRulerRect.left;

	// draw start of demo marker
	m_rImageList.DrawIndirect( pDC, 17, CPoint( i32FirstX, i32Y ), CSize( 10, 10 ), CPoint( 0, 0 ), ILD_NORMAL, SRCCOPY, CLR_NONE, CLR_DEFAULT );

	// draw end of demo marker
	m_rImageList.DrawIndirect( pDC, 18, CPoint( i32LastX, i32Y ), CSize( 10, 10 ), CPoint( 0, 0 ), ILD_NORMAL, SRCCOPY, CLR_NONE, CLR_DEFAULT );


	// draw time cursor
	CPoint	rPt;
	int32	i32CursorPos = m_rRulerRect.left + 1 + m_pDoc->TimeToFrame( GetTimecursor() - m_i32ViewTimeStart ) * m_i32FrameWidthInPixels;

	rPt.x = i32CursorPos + (m_i32FrameWidthInPixels / 2) - 4;
	rPt.y = m_rRulerRect.top;

	m_rImageList.DrawIndirect( pDC, 13, rPt, CSize( 12, 16 ), CPoint( 0, 0 ), ILD_NORMAL, SRCCOPY, CLR_NONE, CLR_DEFAULT );


	// draw old
	if( bClearOld ) {

		CPoint	rPt;
		int32	i32CursorPos = m_rRulerRect.left + 1 + m_pDoc->TimeToFrame( m_i32OldTimeCursor - m_i32ViewTimeStart ) * m_i32FrameWidthInPixels;

		// draw the cursor on time graph
		rPt.x = i32CursorPos;
		rPt.y = m_rRulerRect.top + 2;

		CPen	rRedPen( PS_SOLID, 1, RGB( 255, 15, 0 ) );
		CPen*	pOldPen = pDC->SelectObject( &rRedPen );

		int32	i32OldROP = pDC->SetROP2( R2_NOT );

		pDC->MoveTo( rPt.x + m_i32FrameWidthInPixels / 2, m_rWaveformRect.top );
		pDC->LineTo( rPt.x + m_i32FrameWidthInPixels / 2, m_rWaveformRect.bottom );
		pDC->MoveTo( rPt.x + m_i32FrameWidthInPixels / 2, m_rRulerRect.bottom );
		pDC->LineTo( rPt.x + m_i32FrameWidthInPixels / 2, m_rTimelineRect.bottom );

		pDC->SelectObject( pOldPen );

		// draw edit cursor
		for( int32 i = m_i32FirstVisible; i < m_i32LastVisible; i++ ) {

			SceneItemC*	pItem = m_rSceneItemList.get_sceneitem( i );

			if( pItem->get_flags() & ITEM_SELECTED ) {
				// draw selection bg
				int32	i32Y = m_rTimelineRect.top + pItem->get_y() - GetListPos();
				int32	i32Height = m_rSceneItemList.get_item_height();
				if( i32Y < m_rTimelineRect.top ) {
					i32Height += i32Y - m_rTimelineRect.top;
					i32Y = m_rTimelineRect.top;
				}
				// draw it in two parts to
				pDC->Rectangle( i32CursorPos, i32Y + 1, i32CursorPos + (m_i32FrameWidthInPixels / 2), i32Y + i32Height + 2 );
				pDC->Rectangle( i32CursorPos + (m_i32FrameWidthInPixels / 2) + 1, i32Y + 1, i32CursorPos + m_i32FrameWidthInPixels, i32Y + i32Height + 2 );
			}
		}
		pDC->SetROP2( i32OldROP );
	
	}

	{
		// draw the cursor on time graph
		rPt.x = i32CursorPos;
		rPt.y = m_rRulerRect.top + 2;

		CPen	rRedPen( PS_SOLID, 1, RGB( 255, 15, 0 ) );
		CPen*	pOldPen = pDC->SelectObject( &rRedPen );

		int32	i32OldROP = pDC->SetROP2( R2_NOT );

		pDC->MoveTo( rPt.x + m_i32FrameWidthInPixels / 2, m_rWaveformRect.top );
		pDC->LineTo( rPt.x + m_i32FrameWidthInPixels / 2, m_rWaveformRect.bottom );
		pDC->MoveTo( rPt.x + m_i32FrameWidthInPixels / 2, m_rRulerRect.bottom );
		pDC->LineTo( rPt.x + m_i32FrameWidthInPixels / 2, m_rTimelineRect.bottom );

		pDC->SelectObject( pOldPen );

		// draw edit cursor
		for( int32 i = m_i32FirstVisible; i < m_i32LastVisible; i++ ) {

			SceneItemC*	pItem = m_rSceneItemList.get_sceneitem( i );

			if( pItem->get_flags() & ITEM_SELECTED ) {
				// draw selection bg
				int32	i32Y = m_rTimelineRect.top + pItem->get_y() - GetListPos();
				int32	i32Height = m_rSceneItemList.get_item_height();
				if( i32Y < m_rTimelineRect.top ) {
					i32Height += i32Y - m_rTimelineRect.top;
					i32Y = m_rTimelineRect.top;
				}
				// draw it in two parts to
				pDC->Rectangle( i32CursorPos, i32Y + 1, i32CursorPos + (m_i32FrameWidthInPixels / 2), i32Y + i32Height + 2 );
				pDC->Rectangle( i32CursorPos + (m_i32FrameWidthInPixels / 2) + 1, i32Y + 1, i32CursorPos + m_i32FrameWidthInPixels, i32Y + i32Height + 2 );
			}
		}
		pDC->SetROP2( i32OldROP );
	}

	// restore old clip region
	pDC->SelectClipRgn( &rOldClipRgn );
	rOldClipRgn.DeleteObject();
	rRegion.DeleteObject();
}



void CTimeLineBar::SampleController( SceneItemC* pItem )
{
	uint32	i32NumValues = m_rTimelineRect.Width() / m_i32FrameWidthInPixels + 1;
	int32	i32MinTime = m_i32ViewTimeStart;
	bool	bUpdateMinMax = true;

	if( m_eTrackAction == TRACKING_KEYVALUES )
		bUpdateMinMax = false;

	pItem->sample_controller( i32NumValues, i32MinTime, m_pDoc->GetFrameSizeInTicks(), bUpdateMinMax );
}


//
//  return "nice number" ie. 2, 50, 1000 etc.
//
float32
CTimeLineBar::NiceNum( float32 f32X, bool bRound )
{
	float32	f32Expt;
	float32	f32F;
	float32	f32Nf;

	f32Expt = floor( log10( f32X ) );
	f32F = f32X / pow( 10.0, f32Expt );

	if( bRound ) {
		if( f32F < 1.5 )
			f32Nf = 1.0;
		else if( f32F < 3.0 )
			f32Nf = 2;
		else if( f32F < 7.0 )
			f32Nf = 5.0;
		else
			f32Nf = 10.0;
	} else {
		if( f32F <= 1.0 )
			f32Nf = 1.0;
		else if( f32F <= 2.0 )
			f32Nf = 2;
		else if( f32F <= 5.0 )
			f32Nf = 5.0;
		else
			f32Nf = 10.0;
	}

	return f32Nf * pow( 10.0, f32Expt );
}

void
CTimeLineBar::DrawExpandedContGrid( CDC* pDC, int32 i32ItemY, int32 i32ItemHeight,
								    int32 i32MinTime, int32 i32MaxTime,
									float32 f32MinVal, float32 f32MaxVal, float32 f32ScaleY, bool bUseInt )
{
	int32	i32MaxLabels = i32ItemHeight / 14;

	// make sure there's something to draw
	if( i32ItemHeight < 10 )
		return;

	if( i32MaxLabels < 1 )
		return;

	if( (f32MaxVal - f32MinVal) < 0.00001 )
		return;

	// draw value labels
	int32	i32StartX = m_pDoc->TimeToFrame( i32MinTime - m_i32ViewTimeStart ) * m_i32FrameWidthInPixels;
	int32	i32EndX = m_pDoc->TimeToFrame( i32MaxTime - m_i32ViewTimeStart + m_pDoc->GetFrameSizeInTicks() ) * m_i32FrameWidthInPixels;
	int32	i32GridStartX = i32StartX;
	int32	i32GridEndX = i32EndX;

	if( i32StartX < m_i32FrameWidthInPixels / 2 )
		i32StartX = m_i32FrameWidthInPixels / 2;
	if( i32EndX >= m_rTimelineRect.Width() + m_i32FrameWidthInPixels / 2 )
		i32EndX = m_rTimelineRect.Width() + m_i32FrameWidthInPixels / 2;

	// draw y-ticks

	float32		f32Range = NiceNum( f32MaxVal - f32MinVal, false );
	float32		f32Delta = NiceNum( f32Range / (float32)i32MaxLabels, true );

	if( bUseInt ) {
		f32Delta = (float32)((int32)f32Delta);
		if( f32Delta < 1 )
			f32Delta = 1;
	}

	float32		f32GraphMin = (float32)floor( f32MinVal / f32Delta ) * f32Delta;
	float32		f32GraphMax = (float32)ceil( f32MaxVal / f32Delta ) * f32Delta;
	int32		i32NumFrac = (int32)__max( -floor( log10( f32Delta ) ), 0 );

	// make the printf format string
	char		szFormat[16];
	_snprintf( szFormat, 15, "%-%.%df", i32NumFrac );

	float32	f32Y;
	int32	i32Y;
	CPen	rDotPen( PS_SOLID, 0, GetColor( TIMELINE_VALUE_GRID ) );
	CPen*	pOldPen = pDC->SelectObject( &rDotPen );
	CFont*	pOldFont = pDC->SelectObject( &m_rSmallFont );
	uint32	ui32OldAlign = pDC->SetTextAlign( TA_BASELINE | TA_LEFT );

	pDC->SetBkMode( TRANSPARENT );
	pDC->SetTextColor( GetColor( TIMELINE_VALUE_TEXT ) );

	CString	sLabel;

	for( f32Y = f32GraphMin; f32Y < f32GraphMax; f32Y += f32Delta ) {

		i32Y = (i32ItemY + i32ItemHeight) - (int32)((f32Y - f32MinVal) * f32ScaleY) - 5;

		sLabel.Format( szFormat, f32Y );
		CSize	rSize = pDC->GetTextExtent( sLabel );

		pDC->MoveTo( i32StartX + rSize.cx + 6, i32Y );
		pDC->LineTo( i32EndX - 2, i32Y );

		pDC->TextOut( i32StartX + 4, i32Y + 3, sLabel );
	}

	// restore DC
	pDC->SelectObject( pOldPen );
	pDC->SelectObject( pOldFont );
	pDC->SetTextAlign( ui32OldAlign );
}



static
void ClipRect( CRect& rRect, CRect& rBounds )
{
	rRect.NormalizeRect();
	rBounds.NormalizeRect();

	if( rRect.left < rBounds.left ) rRect.left = rBounds.left;
	if( rRect.left > rBounds.right ) rRect.left = rBounds.right;

	if( rRect.right > rBounds.right ) rRect.right = rBounds.right;
	if( rRect.right < rBounds.left ) rRect.right = rBounds.left;

	if( rRect.top < rBounds.top ) rRect.top = rBounds.top;
	if( rRect.top > rBounds.bottom ) rRect.top = rBounds.bottom;

	if( rRect.bottom > rBounds.bottom ) rRect.bottom = rBounds.bottom;
	if( rRect.bottom < rBounds.top ) rRect.bottom = rBounds.top;
}

void CTimeLineBar::DrawParameterCurves( CDC* pDC, SceneItemC* pItem, ParamI* pParam, int32 i32TimeOffset, int32 i32MinTime, int32 i32MaxTime )
{
	// Only draw expanded parameters.
	if( !(pItem->get_flags() & ITEM_EXPANDED) )
		return;

	ControllerC*	pCont = pParam->get_controller();
	if( !pCont )
		return;

	// Make sure the time range contains the whole controller nimation.
	int32	i32ContMinTime = pCont->get_min_time() + i32TimeOffset;
	int32	i32ContMaxTime = pCont->get_max_time() + i32TimeOffset;

	if( i32ContMinTime < i32MinTime )
		i32MinTime = i32ContMinTime;
	if( i32ContMaxTime > i32MaxTime )
		i32MaxTime = i32ContMaxTime;

	// Clamp time to visible range.
	int32		i32NumValues = (m_rTimelineRect.Width() / m_i32FrameWidthInPixels) + 1;
	int32		i32ViewMaxTime = m_i32ViewTimeStart + m_pDoc->FrameToTime( i32NumValues );

	if( i32MinTime >= i32MaxTime )
		return;
	if( i32MaxTime < m_i32ViewTimeStart )
		return;
	if( i32MinTime > i32ViewMaxTime )
		return;

	if( i32MinTime < m_i32ViewTimeStart )
		i32MinTime = m_i32ViewTimeStart;
	if( i32MaxTime >= i32ViewMaxTime )
		i32MaxTime = i32ViewMaxTime;

	// we will clip all the rectangles needed to this rectangle
	CRect	rTimelineBounds = m_rTimelineRect;
	rTimelineBounds.DeflateRect( 1, 1 );

	uint32	i, j, k;
	int32	i32ParamType = pParam->get_type();
	int32	i32NumChannels = pCont->get_num_channels();
	int32	i32ItemY = pItem->get_y() + m_rSceneItemList.get_item_height();
	int32	i32ItemHeight = pItem->get_height() - m_rSceneItemList.get_item_height();

	float32	f32ValueScale = 1.0f;
	if( pParam->get_style() & PARAM_STYLE_PERCENT )
		f32ValueScale = 100.0f;
	if( i32ParamType == PARAM_TYPE_COLOR )
		f32ValueScale = 255.0f;

	// make sure there's something to draw
	if( i32ItemHeight < 10 )
		return;
	if( i32NumValues < 2 )
		return;

	// sample the controller, if sampling fails, or there's nothing to sample, return.
	SampleController( pItem );
	if( !pItem->get_controller_sampledata()->get_sample_count() )
		return;

	// automatic scale to the current visible area of the curves.
	float32	f32Min, f32Max;
	float32	f32Scale = 1.0f;

	f32Min = pItem->get_controller_sampledata()->get_min() * f32ValueScale;
	f32Max = pItem->get_controller_sampledata()->get_max() * f32ValueScale;
	if( f32Min != f32Max )
		f32Scale = 1.0f / (f32Max - f32Min);

	f32Scale *= (float32)(i32ItemHeight - 10);

	// Calc curve bounds rect
	CRect	rBoundsRect;
	rBoundsRect.left = m_pDoc->TimeToFrame( i32MinTime - m_i32ViewTimeStart ) * m_i32FrameWidthInPixels;
	rBoundsRect.right = m_pDoc->TimeToFrame( i32MaxTime - m_i32ViewTimeStart ) * m_i32FrameWidthInPixels;
	rBoundsRect.top = i32ItemY + 1;
	rBoundsRect.bottom = i32ItemY + i32ItemHeight;

	// save old clip region
	CRect	rOldRect;
	CRgn	rOldClipRgn;
	pDC->GetClipBox( rOldRect );
	rOldRect.OffsetRect( pDC->GetViewportOrg() );
	rOldClipRgn.CreateRectRgnIndirect( rOldRect );

	// create new clip region
	CRgn	rRegion;
	CRect	rClipRect = rBoundsRect;
	rClipRect.OffsetRect( pDC->GetViewportOrg() );
	ClipRect( rClipRect, rTimelineBounds );
	rRegion.CreateRectRgnIndirect( rClipRect );
	pDC->SelectClipRgn( &rRegion );

	// Draw curves background box.
	if( i32ParamType == PARAM_TYPE_COLOR ) {
		// Draw color gradient for color parameter.
		int32	i32Time = i32MinTime - m_i32ViewTimeStart;
		int32	i32StartIdx = m_pDoc->TimeToFrame( i32MinTime - m_i32ViewTimeStart );
		int32	i32EndIdx = m_pDoc->TimeToFrame( i32MaxTime - m_i32ViewTimeStart );

		// The end idx can go outside the sampled range. clamp.
		if( i32EndIdx >= pItem->get_controller_sampledata()->get_sample_count() )
			i32EndIdx = pItem->get_controller_sampledata()->get_sample_count() - 1;

		CRect	rRect;
		for( i = i32StartIdx; i <= i32EndIdx; i++ ) {
			int32	i32X, i32R, i32G, i32B;
			float32*	pSample = pItem->get_controller_sampledata()->get_sample( i );
			i32X = m_pDoc->TimeToFrame( i32Time ) * m_i32FrameWidthInPixels;
			i32R = (int32)(pSample[0] * 255.0f);
			i32G = (int32)(pSample[1] * 255.0f);
			i32B = (int32)(pSample[2] * 255.0f);
			rRect.left = i32X;
			rRect.right = i32X + m_i32FrameWidthInPixels;
			rRect.top = i32ItemY + 1;
			rRect.bottom = i32ItemY + i32ItemHeight;
			pDC->FillSolidRect( rRect, RGB( i32R, i32G, i32B ) );
			i32Time += m_pDoc->GetFrameSizeInTicks();
		}
	}
	else {
		// Solid background for the rest of the parameters.
		pDC->FillSolidRect( rBoundsRect, GetColor( TIMELINE_PARAM_LIGHT ) );
	}

	if( i32ParamType == PARAM_TYPE_INT ) {
		ParamIntC*	pParamInt = (ParamIntC*)pParam;
		if( pParamInt->get_label_count() ) {

			// draw value labels
			CFont*	pOldFont = pDC->SelectObject( &m_rSmallFont );
			pDC->SetBkMode( TRANSPARENT );
			pDC->SetTextColor( GetColor( TIMELINE_VALUE_TEXT ) );
			uint32	ui32OldAlign = pDC->SetTextAlign( TA_BASELINE );

			int32	i32StartX = rBoundsRect.left + 3;

			if( i32StartX < m_i32FrameWidthInPixels / 2 )
				i32StartX = m_i32FrameWidthInPixels / 2;

			// draw labels
			for( i = 0; i < pParamInt->get_label_count(); i++ ) {
				int32	i32Val = pParamInt->get_label_value( i );
				int32	i32Y = (i32ItemY + i32ItemHeight - 5 - 3) - (int32)((i32Val - f32Min) * f32Scale);
				pDC->TextOut( i32StartX, i32Y + 6, CString( pParamInt->get_label_name( i ) ) );
			}

			// restore resources
			pDC->SelectObject( pOldFont );
			pDC->SetTextAlign( ui32OldAlign );
		}
		else {
			DrawExpandedContGrid( pDC, i32ItemY, i32ItemHeight,
								  i32MinTime, i32MaxTime,
								  f32Min, f32Max, f32Scale );
		}
	}
	else {
		DrawExpandedContGrid( pDC, i32ItemY, i32ItemHeight,
							  i32MinTime, i32MaxTime,
							  f32Min, f32Max, f32Scale );
	}

	pDC->Draw3dRect( rBoundsRect, GetColor( TIMELINE_PARAM_FACE ), GetColor( TIMELINE_PARAM_SHADOW ) );

	// Draw curves.

	CPen	rBlackPen( PS_SOLID, 1, GetColor( TIMELINE_BLACK ) );
	CPen*	pOldPen = pDC->SelectObject( &rBlackPen );

	int32	i32OldX[KEY_MAXCHANNELS];
	int32	i32OldY[KEY_MAXCHANNELS];
	int32	i32Time = i32MinTime - m_i32ViewTimeStart;
	int32	i32StartIdx = m_pDoc->TimeToFrame( i32MinTime - m_i32ViewTimeStart );
	int32	i32EndIdx = m_pDoc->TimeToFrame( i32MaxTime - m_i32ViewTimeStart );

	// The end idx can go outside the sampled range. clamp.
	if( i32EndIdx >= pItem->get_controller_sampledata()->get_sample_count() )
		i32EndIdx = pItem->get_controller_sampledata()->get_sample_count() - 1;

	for( i = i32StartIdx; i <= i32EndIdx; i++ ) {
		int32		i32X[KEY_MAXCHANNELS], i32Y[KEY_MAXCHANNELS];
		float32*	pSample = pItem->get_controller_sampledata()->get_sample( i );

		for( j = 0; j < i32NumChannels; j++ ) {

			i32X[j] = m_pDoc->TimeToFrame( i32Time ) * m_i32FrameWidthInPixels + (m_i32FrameWidthInPixels / 2) + 1;
			i32Y[j] = (i32ItemY + i32ItemHeight - 5) - (int32)((pSample[j] * f32ValueScale - f32Min) * f32Scale);

			if( i != i32StartIdx ) {

				if( i32ParamType == PARAM_TYPE_INT ) {
					// Draw int paremters with jaggy edges.
					pDC->MoveTo( i32OldX[j], i32OldY[j] );
					pDC->LineTo( i32X[j], i32OldY[j] );
					pDC->LineTo( i32X[j], i32Y[j] );
				}
				else {
					pDC->MoveTo( i32OldX[j], i32OldY[j] );
					pDC->LineTo( i32X[j], i32Y[j] );
				}
			}

			i32OldX[j] = i32X[j];
			i32OldY[j] = i32Y[j];
		}

		i32Time += m_pDoc->GetFrameSizeInTicks();
	}

	// Restode resources
	pDC->SelectObject( pOldPen );

	// restore old clip region
	pDC->SelectClipRgn( &rOldClipRgn );
	rOldClipRgn.DeleteObject();
	rRegion.DeleteObject();


	// Draw keys and key labels
	CFont*	pOldFont = pDC->SelectObject( &m_rSmallFont );
	pDC->SetBkMode( TRANSPARENT );
	pDC->SetTextColor( GetColor( TIMELINE_TEXT ) );
	uint32	ui32OldAlign = pDC->SetTextAlign( TA_BASELINE );

	CString	sVal, sFormat;
	sFormat.Format( "%%.%df", (int32)__max( -floor( log10( pParam->get_increment() * f32ValueScale ) ), 0 ) );

	for( i = 0; i < pCont->get_key_count(); i++ ) {
		FloatKeyC*	pKey = (FloatKeyC*)pCont->get_key( i );
		int32	i32KeyTime = i32TimeOffset + pKey->get_time();
		int32	i32X, i32Y;

		if( i32KeyTime < m_i32ViewTimeStart || i32KeyTime >= i32ViewMaxTime )
			continue;

		i32KeyTime -= m_i32ViewTimeStart;

		float32	f32Value[KEY_MAXCHANNELS];
		pKey->get_value( f32Value );

		i32X = m_pDoc->TimeToFrame( i32KeyTime ) * m_i32FrameWidthInPixels + (m_i32FrameWidthInPixels / 2) - 3;

		for( j = 0; j < i32NumChannels; j++ ) {

			i32Y = (i32ItemY + i32ItemHeight - 5 - 3) - (int32)((f32Value[j] * f32ValueScale - f32Min) * f32Scale);

			if( pKey->get_flags() & KEY_SELECTED ) {
				//selected key
				m_rImageListKeys.DrawIndirect( pDC, j * 2 + 1, CPoint( i32X, i32Y ), CSize( 6, 6 ), CPoint( 0, 0 ), ILD_NORMAL, SRCCOPY, CLR_NONE, CLR_DEFAULT );
			}
			else {
				// normal
				m_rImageListKeys.DrawIndirect( pDC, j * 2, CPoint( i32X, i32Y ), CSize( 6, 6 ), CPoint( 0, 0 ), ILD_NORMAL, SRCCOPY, CLR_NONE, CLR_DEFAULT );
			}

			if( pKey->get_flags() & KEY_SELECTED ) {

				if( i32ParamType == PARAM_TYPE_INT ) {
					// Draw named labesl for integer parameters if possible
					ParamIntC*	pPramInt = (ParamIntC*)pParam;
					int32	i32Val = (int32)f32Value[j];
					bool	bFound = false;
					if( pPramInt->get_label_count() ) {
						for( k = 0; k < pPramInt->get_label_count(); k++ ) {
							if( i32Val == pPramInt->get_label_value( k ) ) {
								sVal = pPramInt->get_label_name( k );
								CSize	rSize = pDC->GetTextExtent( sVal );
								pDC->Rectangle( i32X + 6, i32Y + 9, i32X + 6 + rSize.cx + 4, i32Y + 6 - (rSize.cy - 2) );
								pDC->TextOut( i32X + 6, i32Y + 6, sVal );
								bFound = true;
								break;
							}
						}
					}
					if( !bFound ) {
						sVal.Format( "%d", (int32)f32Value[j] );
						CSize	rSize = pDC->GetTextExtent( sVal );
						pDC->Rectangle( i32X + 6, i32Y + 9, i32X + 6 + rSize.cx + 4, i32Y + 6 - (rSize.cy - 2) );
						pDC->TextOut( i32X + 6, i32Y + 6, sVal );
					}
				}
				else {

					pKey->get_value( f32Value );
					sVal.Format( sFormat, f32Value[j] * f32ValueScale );
					CutTrailingZeros( sVal );
					if( pParam->get_style() & PARAM_STYLE_PERCENT ) 
						sVal += "%";
					else if( pParam->get_style() & PARAM_STYLE_ANGLE ) 
						sVal += "°";
					CSize	rSize = pDC->GetTextExtent( sVal );
					pDC->Rectangle( i32X + 6, i32Y + 9, i32X + 6 + rSize.cx + 4, i32Y + 6 - (rSize.cy - 2) );
					pDC->TextOut( i32X + 8, i32Y + 6, sVal );
				}
			}
		}

	}

	pDC->SetTextAlign( ui32OldAlign );
	pDC->SelectObject( pOldFont );
}

void CTimeLineBar::DrawParameterFileTimeruler( CDC* pDC, SceneItemC* pItem, ParamFileC* pParam, int32 i32FrameTime, int32 i32TimeOffset, const CRect& rBoundsRect )
{
	int32	i32Duration = pParam->get_duration( i32FrameTime );
	int32	i32FileTimeOffset = -pParam->get_time_offset( i32FrameTime ) + i32TimeOffset;

	// negative duration means this file is still/static
	if( i32Duration >= 0 ) {

		int32	i32ItemY = pItem->get_y() + m_rSceneItemList.get_item_height();
		int32	i32ItemHeight = pItem->get_height() - m_rSceneItemList.get_item_height();

		int32	i32StartX = m_pDoc->TimeToFrame( i32FileTimeOffset - m_i32ViewTimeStart ) * m_i32FrameWidthInPixels + m_i32FrameWidthInPixels / 2;
		int32	i32EndX = m_pDoc->TimeToFrame( i32FileTimeOffset + i32Duration - m_i32ViewTimeStart ) * m_i32FrameWidthInPixels + m_i32FrameWidthInPixels / 2;
		int32	i32RangeX = i32EndX - i32StartX;

		int32	i32MaxLabels = i32RangeX / 25;
		float32	f32MinVal = pParam->get_start_label( i32FrameTime );
		float32	f32MaxVal = pParam->get_end_label( i32FrameTime );
		float32	f32RangeVal = f32MaxVal - f32MinVal;

		// make sure there's something to draw
		if( i32MaxLabels < 1 || i32RangeX < 1 )
			return;

		// draw x-ticks

		float32		f32Range = NiceNum( f32MaxVal - f32MinVal, false );
		float32		f32Delta = NiceNum( f32Range / (float32)i32MaxLabels, true );

		float32		f32GraphMin = (float32)floor( f32MinVal / f32Delta ) * f32Delta;
		float32		f32GraphMax = (float32)ceil( f32MaxVal / f32Delta ) * f32Delta;
		int32		i32NumFrac = (int32)__max( -floor( log10( f32Delta ) ), 0 );

		// make the printf format string
		char		szFormat[16];
		_snprintf( szFormat, 15, "%%.%df", i32NumFrac );

		float32	f32X;
		int32	i32X;
		CPen	rDotPen( PS_SOLID, 0, GetColor( TIMELINE_GRID ) );
		CPen*	pOldPen = pDC->SelectObject( &rDotPen );
		CBrush	rHiBrush( GetColor( TIMELINE_PARAM_FACE ) );
		CBrush*	pOldBrush = pDC->SelectObject( &rHiBrush );
		CString	sLabel;

		float32	f32ScaleX = (float32)i32RangeX / (f32GraphMax - f32GraphMin);


		int32	i32NumRulers = 1;

		int32	i32RulerStartX = i32StartX;
		int32	i32RulerEndX = i32EndX;

		while( i32RulerStartX > rBoundsRect.left ) {
			i32RulerStartX -= i32RangeX - 1;
			i32NumRulers++;
		}
		while( i32RulerEndX < rBoundsRect.right ) {
			i32RulerEndX += i32RangeX - 1;
			i32NumRulers++;
		}

		if( i32NumRulers < 1 )
			i32NumRulers = 1;

		for( int32 i = 0; i < i32NumRulers; i++ ) {
			i32StartX = i32RulerStartX;
			i32EndX = i32RulerStartX + i32RangeX;
			CRect	rRect;
			rRect.left = i32StartX;
			rRect.right = i32EndX;
			rRect.top = i32ItemY;
			rRect.bottom = i32ItemY + i32ItemHeight;

			// clamp
			if( rRect.left < 0 )
				rRect.left = 0;
			if( rRect.right > m_rTimelineRect.Width() )
				rRect.right = m_rTimelineRect.Width();

			// is it visible at all?
			if( i32StartX <= m_rTimelineRect.Width() && i32EndX >= 0 ) {
				pDC->MoveTo( i32StartX, i32ItemY + 4 );
				pDC->LineTo( i32EndX - 3, i32ItemY + 4 );
			}

			for( f32X = f32GraphMin; f32X < f32GraphMax; f32X += f32Delta ) {

				i32X = i32StartX + (int32)((f32X - f32MinVal) * f32ScaleX);

				if( (i32X + 50) < rBoundsRect.left )
					continue;
				if( i32X > rBoundsRect.right )
					break;

				pDC->MoveTo( i32X, i32ItemY + 4 );
				if( f32X == f32GraphMin )
					pDC->LineTo( i32X, i32ItemY + i32ItemHeight / 4 + 4 );
				else
					pDC->LineTo( i32X, i32ItemY + i32ItemHeight / 4 );

				sLabel.Format( szFormat, f32X );
				pDC->TextOut( i32X, i32ItemY + i32ItemHeight / 4, sLabel );

			}

			i32RulerStartX += i32RangeX - 1;
		}

		// restore DC
		pDC->SelectObject( pOldPen );
		pDC->SelectObject( pOldBrush );
	}
}


void CTimeLineBar::DrawParameterFile( CDC* pDC, SceneItemC* pItem, ParamFileC* pParam, int32 i32TimeOffset, int32 i32MinTime, int32 i32MaxTime )
{
	int32			i32NumValues = m_rTimelineRect.Width() / m_i32FrameWidthInPixels + 1;
	int32			i32Time = m_i32ViewTimeStart - i32TimeOffset;
	int32			i32ViewMaxTime = m_i32ViewTimeStart + m_pDoc->FrameToTime( i32NumValues );
	int32			i32ItemY, i32ItemHeight;

	ControllerC*	pCont = pParam->get_controller();

	if( !pCont )
		return;

	i32ItemY = pItem->get_y() + m_rSceneItemList.get_item_height();
	i32ItemHeight = pItem->get_height() - m_rSceneItemList.get_item_height();

	// make sure there's something to draw
	if( i32NumValues < 2 )
		return;

	int32	i32ContMinTime = pCont->get_min_time() + i32TimeOffset;
	int32	i32ContMaxTime = pCont->get_max_time() + i32TimeOffset;

	int32	i32OrigMinTime = i32MinTime;
	int32	i32OrigMaxTime = i32MaxTime;

	if( i32ContMinTime < i32MinTime )
		i32MinTime = i32ContMinTime;
	if( i32ContMaxTime > i32MaxTime )
		i32MaxTime = i32ContMaxTime;

	if( i32MinTime >= i32MaxTime )
		return;
	if( i32MaxTime < m_i32ViewTimeStart )
		return;
	if( i32MinTime > i32ViewMaxTime )
		return;

	if( i32MinTime < m_i32ViewTimeStart )
		i32MinTime = m_i32ViewTimeStart;
	if( i32MaxTime >= i32ViewMaxTime )
		i32MaxTime = i32ViewMaxTime;

	CRect	rTimelineBounds = m_rTimelineRect;
	rTimelineBounds.DeflateRect( 1, 1 );

	CRect	rTimelineBoundsInner( 0, 0, rTimelineBounds.Width(), rTimelineBounds.Height() );
	rTimelineBoundsInner.OffsetRect( 0, m_pDoc->GetSceneLayerListPos() );

	CFont*	pOldFont = pDC->SelectObject( &m_rSmallFont );
	pDC->SetBkMode( TRANSPARENT );
	pDC->SetTextColor( GetColor( TIMELINE_TEXT ) );
	uint32	ui32OldAlign = pDC->SetTextAlign( TA_TOP );

	if( pParam->get_flags() & ITEM_ANIMATED && pCont->get_key_count() ) {

		uint32	ui32StartORT = pCont->get_start_ort();
		uint32	ui32EndORT = pCont->get_end_ort();

		// draw animated
		CPen	rDarkPen( PS_SOLID, 1, GetColor( TIMELINE_PARAM_SHADOW ) );
		CPen	rMixDarkPen( PS_SOLID, 1, Mix( GetColor( TIMELINE_PARAM_SHADOW ), GetColor( TIMELINE_BACK ), 128 ) );
		
		CPen*	pOldPen = pDC->SelectObject( &rDarkPen );
		
		CBrush	rFaceBrush( GetColor( TIMELINE_PARAM_FACE ) );
		CBrush	rMixFaceBrush( Mix( GetColor( TIMELINE_PARAM_FACE ), GetColor( TIMELINE_BACK ), 128 ) );

		CBrush*	pOldBrush = pDC->SelectObject( &rMixFaceBrush );


		// Draw start ORT
		if( ui32StartORT != CONT_ORT_CONSTANT ) {

			int32	i32StartFrame = m_pDoc->TimeToFrame( i32MinTime - m_i32ViewTimeStart );
			int32	i32EndFrame = m_pDoc->TimeToFrame( i32TimeOffset + pCont->get_min_time() - m_i32ViewTimeStart );

			if( i32StartFrame < i32EndFrame ) {

				int32	i32StartFrameX = i32StartFrame * m_i32FrameWidthInPixels;
				int32	i32EndFrameX = i32EndFrame * m_i32FrameWidthInPixels;
				int32	i32KeyFrameX = i32StartFrameX;

				// draw bar
				CRect	rRect;
				rRect.left = i32StartFrameX;
				rRect.right = i32EndFrameX;
				rRect.top = pItem->get_y() + 2;
				rRect.bottom = pItem->get_y() + m_rSceneItemList.get_item_height();

				pDC->SelectObject( &rMixDarkPen );

				pDC->Rectangle( rRect );

				for( uint32 j = i32StartFrame; j < i32EndFrame; j++ ) {
					int32	i32X = j * m_i32FrameWidthInPixels;
					pDC->MoveTo( i32X, rRect.bottom );
					pDC->LineTo( i32X, rRect.bottom - 4 );
				}

				// draw loop boundaries
				int32	i32Duration = m_pDoc->TimeToFrame( pCont->get_max_time() - pCont->get_min_time() ) * m_i32FrameWidthInPixels;
				if( i32Duration > 0 ) {
					int32	i32X = i32EndFrameX;
					while( i32X > i32StartFrameX ) {
						pDC->MoveTo( i32X, rRect.bottom );
						pDC->LineTo( i32X, rRect.top );
						i32X -= i32Duration;
					}
				}

			}
		}

		// Draw end ORT
		if( ui32EndORT != CONT_ORT_CONSTANT ) {

			int32	i32StartFrame = m_pDoc->TimeToFrame( i32TimeOffset + pCont->get_max_time() - m_i32ViewTimeStart );
			int32	i32EndFrame = m_pDoc->TimeToFrame( i32MaxTime - m_i32ViewTimeStart );

			if( i32StartFrame < i32EndFrame ) {
				int32	i32StartFrameX = i32StartFrame * m_i32FrameWidthInPixels;
				int32	i32EndFrameX = i32EndFrame * m_i32FrameWidthInPixels;
				int32	i32KeyFrameX = i32StartFrameX;

				// draw bar
				CRect	rRect;
				rRect.left = i32StartFrameX;
				rRect.right = i32EndFrameX;
				rRect.top = pItem->get_y() + 2;
				rRect.bottom = pItem->get_y() + m_rSceneItemList.get_item_height();

				pDC->SelectObject( &rMixDarkPen );

				pDC->Rectangle( rRect );

				for( uint32 j = i32StartFrame; j < i32EndFrame; j++ ) {
					int32	i32X = j * m_i32FrameWidthInPixels;
					pDC->MoveTo( i32X, rRect.bottom );
					pDC->LineTo( i32X, rRect.bottom - 4 );
				}

				// draw loop boundaries
				int32	i32Duration = m_pDoc->TimeToFrame( pCont->get_max_time() - pCont->get_min_time() ) * m_i32FrameWidthInPixels;
				if( i32Duration > 0 ) {
					int32	i32X = i32StartFrameX;
					while( i32X < i32EndFrameX ) {
						pDC->MoveTo( i32X, rRect.bottom );
						pDC->LineTo( i32X, rRect.top );
						i32X += i32Duration;
					}
				}
			}
		}

		pDC->SelectObject( &rFaceBrush );

		for( uint32 i = 0; i < pCont->get_key_count(); i++ ) {
			FileKeyC*	pKey = (FileKeyC*)pCont->get_key( i );
			FileKeyC*	pNextKey = 0;

			int32	i32StartTime = i32TimeOffset + pKey->get_time();
			int32	i32EndTime = i32MaxTime;

			if( (i + 1) < pCont->get_key_count() ) {
				pNextKey = (FileKeyC*)pCont->get_key( i + 1 );
				i32EndTime = i32TimeOffset + pNextKey->get_time();
			}


			if( i32EndTime < m_i32ViewTimeStart || i32StartTime >= i32ViewMaxTime )
				continue;

			i32StartTime -= m_i32ViewTimeStart;
			i32EndTime -= m_i32ViewTimeStart;

			int32	i32StartFrameX = m_pDoc->TimeToFrame( i32StartTime ) * m_i32FrameWidthInPixels;
			int32	i32EndFrameX = m_pDoc->TimeToFrame( i32EndTime ) * m_i32FrameWidthInPixels;
			int32	i32KeyFrameX = i32StartFrameX;

			// expand the first key only if the start ORT is "constant".
			if( ui32StartORT == CONT_ORT_CONSTANT && i == 0 && i32StartTime > i32MinTime ) {
				i32StartFrameX = m_pDoc->TimeToFrame( i32MinTime - m_i32ViewTimeStart ) * m_i32FrameWidthInPixels;
				i32StartTime = i32MinTime;
			}

			// dont draw the last bar if the end ORT is something else than "constant".
			bool	bDrawBar = true;
			if( ui32EndORT != CONT_ORT_CONSTANT &&  i == (pCont->get_key_count() - 1) ) {
				bDrawBar = false;
			}

			if( bDrawBar ) {
				// draw bar
				CRect	rRect;
				rRect.left = i32StartFrameX;
				rRect.right = i32EndFrameX;
				rRect.top = pItem->get_y() + 2;
				rRect.bottom = pItem->get_y() + m_rSceneItemList.get_item_height();

				pDC->SelectObject( &rDarkPen );

				pDC->Rectangle( rRect );
				rRect.DeflateRect( 1, 1, 1, 1 );

				for( uint32 j = m_pDoc->TimeToFrame( i32StartTime ); j < m_pDoc->TimeToFrame( i32EndTime ); j++ ) {
					int32	i32X = j * m_i32FrameWidthInPixels;
					pDC->MoveTo( i32X, rRect.bottom );
					pDC->LineTo( i32X, rRect.bottom - 4 );
				}
			}

			// Draw the key
			CPoint	rPt( i32KeyFrameX + (m_i32FrameWidthInPixels / 2 + 1) - 5, pItem->get_y() );
			int32	i32BaseImg = 6;	// hold

			if( pKey->get_flags() & KEY_SELECTED ) {
				//selected key
				m_rImageList.DrawIndirect( pDC, i32BaseImg + 1, rPt, CSize( 9, 16 ), CPoint( 0, 0 ), ILD_NORMAL, SRCCOPY, CLR_NONE, CLR_DEFAULT );
			}
			else {
				// normal
				m_rImageList.DrawIndirect( pDC, i32BaseImg, rPt, CSize( 9, 16 ), CPoint( 0, 0 ), ILD_NORMAL, SRCCOPY, CLR_NONE, CLR_DEFAULT );
			}

			if( bDrawBar ) {
				FileHandleC*	pHandle = pKey->get_file_handle();
				ImportableI*	pImportable = 0;
				std::string		sStr;

				if( pHandle )
					pImportable = pHandle->get_importable();

				if( pImportable ) {
					char szFname[_MAX_FNAME];
					char szExt[_MAX_EXT];
					_splitpath( pImportable->get_filename(), 0, 0, szFname, szExt );
					sStr += szFname;
					sStr += szExt;
				}
				else {
					sStr = "<Empty>";
				}

				CRect	rTextClipRect;
				rTextClipRect.left = i32StartFrameX;
				rTextClipRect.right = i32EndFrameX;
				rTextClipRect.top = pItem->get_y();
				rTextClipRect.bottom = pItem->get_y() + pItem->get_height();
				ClipRect( rTextClipRect, rTimelineBoundsInner );

				pDC->ExtTextOut( rPt.x + 10, rPt.y + 3, ETO_CLIPPED, &rTextClipRect, sStr.c_str(), NULL );


				// draw time ruler
				if( pItem->get_flags() & ITEM_EXPANDED ) {

					// save old clip region
					CRect	rOldRect;
					CRgn	rOldClipRgn;
					pDC->GetClipBox( rOldRect );
					rOldRect.OffsetRect( pDC->GetViewportOrg() );
					rOldClipRgn.CreateRectRgnIndirect( rOldRect );

					// create new clip region
					CRgn	rRegion;
					CRect	rClipRect = rTextClipRect;
					rClipRect.OffsetRect( pDC->GetViewportOrg() );
					ClipRect( rClipRect, rTimelineBounds );
					rRegion.CreateRectRgnIndirect( rClipRect );
					pDC->SelectClipRgn( &rRegion );

					CRect	rBoundsRect;
					rBoundsRect.left = i32StartFrameX;
					rBoundsRect.right = i32EndFrameX;
					rBoundsRect.top = i32ItemY;
					rBoundsRect.bottom = i32ItemY + i32ItemHeight;

					pDC->FillSolidRect( rBoundsRect, GetColor( TIMELINE_PARAM_LIGHT ) );
					pDC->Draw3dRect( rBoundsRect, GetColor( TIMELINE_PARAM_FACE ), GetColor( TIMELINE_PARAM_SHADOW ) );

					// draw expanded view
					int32	i32FrameTime = pKey->get_time();

					DrawParameterFileTimeruler( pDC, pItem, pParam, i32FrameTime, pKey->get_time() + i32TimeOffset, rBoundsRect );

					// restore old clip region
					pDC->SelectClipRgn( &rOldClipRgn );
					rOldClipRgn.DeleteObject();
					rRegion.DeleteObject();
				}
			}
		}


		pDC->SelectObject( pOldPen );
		pDC->SelectObject( pOldBrush );

	}
	else {


		CPen	rBlackPen( PS_SOLID, 1, GetColor( TIMELINE_BLACK ) );
		CPen	rDarkPen( PS_SOLID, 1, GetColor( TIMELINE_PARAM_SHADOW ) );
		CPen*	pOldPen = pDC->SelectObject( &rBlackPen );
		CBrush	rHiBrush( GetColor( TIMELINE_PARAM_FACE ) );
		CBrush*	pOldBrush = pDC->SelectObject( &rHiBrush );

		int32	i32StartTime = i32MinTime;
		int32	i32EndTime = i32MaxTime;

		if( i32EndTime < m_i32ViewTimeStart || i32StartTime >= i32MaxTime )
			return;

		i32StartTime -= m_i32ViewTimeStart;
		i32EndTime -= m_i32ViewTimeStart;

		int32	i32StartFrameX = m_pDoc->TimeToFrame( i32StartTime ) * m_i32FrameWidthInPixels;
		int32	i32EndFrameX = m_pDoc->TimeToFrame( i32EndTime ) * m_i32FrameWidthInPixels;
		int32	i32KeyFrameX = i32StartFrameX;

		// draw bar
		CRect	rRect;
		rRect.left = i32StartFrameX;
		rRect.right = i32EndFrameX;
		rRect.top = pItem->get_y() + 2;
		rRect.bottom = pItem->get_y() + m_rSceneItemList.get_item_height();

		pDC->SelectObject( &rDarkPen );

		pDC->Rectangle( rRect );
		rRect.DeflateRect( 1, 1, 1, 1 );

		for( uint32 j = m_pDoc->TimeToFrame( i32StartTime ); j < m_pDoc->TimeToFrame( i32EndTime ); j++ ) {
			int32	i32X = j * m_i32FrameWidthInPixels;
			pDC->MoveTo( i32X, rRect.bottom );
			pDC->LineTo( i32X, rRect.bottom - 4 );
		}


		CPoint	rPt( i32KeyFrameX + (m_i32FrameWidthInPixels / 2 + 1) - 5, pItem->get_y() );

		FileHandleC*	pHandle = pParam->get_file( 0 );
		ImportableI*	pImportable = 0;
		std::string		sStr;

		if( pHandle )
			pImportable = pHandle->get_importable();

		if( pImportable ) {
			char szFname[_MAX_FNAME];
			char szExt[_MAX_EXT];
			_splitpath( pImportable->get_filename(), 0, 0, szFname, szExt );
			sStr += szFname;
			sStr += szExt;
		}
		else {
			sStr = "<Empty>";
		}

		CRect	rTextClipRect;
		rTextClipRect.left = i32StartFrameX;
		rTextClipRect.right = i32EndFrameX;
		rTextClipRect.top = pItem->get_y();
		rTextClipRect.bottom = pItem->get_y() + pItem->get_height();
		ClipRect( rTextClipRect, rTimelineBoundsInner );

		pDC->ExtTextOut( rPt.x + 10, rPt.y + 3, ETO_CLIPPED, &rTextClipRect, sStr.c_str(), NULL );


		// draw expanded

		if( pItem->get_flags() & ITEM_EXPANDED ) {

			// save old clip region
			CRect	rOldRect;
			CRgn	rOldClipRgn;
			pDC->GetClipBox( rOldRect );
			rOldRect.OffsetRect( pDC->GetViewportOrg() );
			rOldClipRgn.CreateRectRgnIndirect( rOldRect );

			// create new clip region
			CRgn	rRegion;
			CRect	rClipRect = rTextClipRect;
			rClipRect.OffsetRect( pDC->GetViewportOrg() );
			ClipRect( rClipRect, rTimelineBounds );
			rRegion.CreateRectRgnIndirect( rClipRect );
			pDC->SelectClipRgn( &rRegion );


			CRect	rBoundsRect;
			rBoundsRect.left = i32StartFrameX;
			rBoundsRect.right = i32EndFrameX;
			rBoundsRect.top = i32ItemY;
			rBoundsRect.bottom = i32ItemY + i32ItemHeight;

			pDC->FillSolidRect( rBoundsRect, GetColor( TIMELINE_PARAM_LIGHT ) );
			pDC->Draw3dRect( rBoundsRect, GetColor( TIMELINE_PARAM_FACE ), GetColor( TIMELINE_PARAM_SHADOW ) );

			// draw expanded view
			DrawParameterFileTimeruler( pDC, pItem, pParam, 0, i32TimeOffset, rBoundsRect );

			// restore old clip region
			pDC->SelectClipRgn( &rOldClipRgn );
			rOldClipRgn.DeleteObject();
			rRegion.DeleteObject();
		}

		pDC->SelectObject( pOldPen );
		pDC->SelectObject( pOldBrush );
	}

	pDC->SelectObject( pOldFont );
	pDC->SetTextAlign( ui32OldAlign );

}


void CTimeLineBar::DrawControllerBar( CDC* pDC, SceneItemC* pItem, ControllerC* pCont, int32 i32TimeOffset, int32 i32MaxTime )
{
	if( !pCont )
		return;

	int32	i32Height = m_rSceneItemList.get_item_height();

	// draw the bar
	CPen	rBlackPen( PS_SOLID, 1, GetColor( TIMELINE_BLACK ) );
	CPen	rDarkPen( PS_SOLID, 1, GetColor( TIMELINE_PARAM_SHADOW ) );
	CPen*	pOldPen = pDC->SelectObject( &rBlackPen );
	CBrush	rHiBrush( GetColor( TIMELINE_PARAM_FACE ) );
	CBrush*	pOldBrush = pDC->SelectObject( &rHiBrush );

	int32	i32Time1, i32Time2;

	i32Time1 = i32TimeOffset + pCont->get_min_time();
	i32Time2 = i32TimeOffset + pCont->get_max_time();

	if( (i32Time2 - i32Time1) > 0 && i32Time1 < i32MaxTime && i32Time2 > m_i32ViewTimeStart ) {
		// clamp stuff
		if( i32Time1 < m_i32ViewTimeStart - m_pDoc->GetFrameSizeInTicks() )
			i32Time1 = m_i32ViewTimeStart - m_pDoc->GetFrameSizeInTicks();
		if( i32Time1 > i32MaxTime )
			i32Time1 = i32MaxTime;

		if( i32Time2 < m_i32ViewTimeStart - m_pDoc->GetFrameSizeInTicks() )
			i32Time2 = m_i32ViewTimeStart - m_pDoc->GetFrameSizeInTicks();
		if( i32Time2 > i32MaxTime )
			i32Time2 = i32MaxTime;

		i32Time1 -= m_i32ViewTimeStart;
		i32Time2 -= m_i32ViewTimeStart;

		CRect	rRect;
		rRect.left = m_pDoc->TimeToFrame( i32Time1 ) * m_i32FrameWidthInPixels + m_i32FrameWidthInPixels / 2;
		rRect.right = m_pDoc->TimeToFrame( i32Time2 ) * m_i32FrameWidthInPixels + m_i32FrameWidthInPixels / 2 + 1;
		rRect.top = pItem->get_y() + 5;
		rRect.bottom = pItem->get_y() + i32Height;

		pDC->Rectangle( rRect );
		rRect.DeflateRect( 1, 1, 1, 1 );

		pDC->SelectObject( &rDarkPen );

		for( uint32 j = m_pDoc->TimeToFrame( i32Time1 ); j < m_pDoc->TimeToFrame( i32Time2 ); j++ ) {
			int32	i32X = j * m_i32FrameWidthInPixels;
			pDC->MoveTo( i32X, rRect.bottom );
			pDC->LineTo( i32X, rRect.bottom - 4 );
		}
	}


	pDC->SelectObject( pOldPen );
	pDC->SelectObject( pOldBrush );
}

void CTimeLineBar::DrawControllerKeys( CDC* pDC, SceneItemC* pItem, ControllerC* pCont, int32 i32TimeOffset, int32 i32MaxTime )
{
	if( !pCont )
		return;

	for( uint32 i = 0; i < pCont->get_key_count(); i++ ) {
		KeyC*	pKey = pCont->get_key( i );

		int32	i32Time = i32TimeOffset + pKey->get_time();

		if( i32Time < m_i32ViewTimeStart || i32Time >= i32MaxTime )
			continue;

		i32Time -= m_i32ViewTimeStart;
		CPoint	rPt( m_pDoc->TimeToFrame( i32Time ) * m_i32FrameWidthInPixels + (m_i32FrameWidthInPixels / 2 + 1) - 5, pItem->get_y() );

		int32	i32BaseImg;
		if( pKey->get_flags() & KEY_LINEAR )
			i32BaseImg = 8;	// linear
		else if( pKey->get_flags() & KEY_SMOOTH )
			i32BaseImg = 4;	// smooth
		else
			i32BaseImg = 6;	// hold

		if( pKey->get_flags() & KEY_SELECTED ) {
			//selected key
			m_rImageList.DrawIndirect( pDC, i32BaseImg + 1, rPt, CSize( 9, 16 ), CPoint( 0, 0 ), ILD_NORMAL, SRCCOPY, CLR_NONE, CLR_DEFAULT );
		}
		else {
			// normal
			m_rImageList.DrawIndirect( pDC, i32BaseImg, rPt, CSize( 9, 16 ), CPoint( 0, 0 ), ILD_NORMAL, SRCCOPY, CLR_NONE, CLR_DEFAULT );
		}
	}
}

void CTimeLineBar::DrawParameter( CDC* pDC, SceneItemC* pItem )
{
	if( !(pItem->get_type() == SCENEITEM_PARAMETER) )
		return;

	ParamI*	pParam = pItem->get_parameter();

	if( pParam ) {

		// Find min/max time based on the effect time segment
		int32			i32TimeOffset = 0;
		TimeSegmentC*	pLayerSegment = pItem->get_layer()->get_timesegment();
		i32TimeOffset += pLayerSegment->get_segment_start();
		TimeSegmentC*	pEffectSegment = pItem->get_effect()->get_timesegment();
		i32TimeOffset += pEffectSegment->get_segment_start();

		ControllerC*	pCont = pParam->get_controller();

		uint32	ui32NumValues = m_rTimelineRect.Width() / m_i32FrameWidthInPixels + 1;
		int32	i32ViewMaxTime = m_i32ViewTimeStart + m_pDoc->FrameToTime( ui32NumValues );
		int32	i32Height = m_rSceneItemList.get_item_height();

		int32	i32MinTime = 0, i32MaxTime = 0;
		if( pEffectSegment->get_key_count() > 1 ) {
			i32MinTime = pEffectSegment->get_key_time( 0 ) + pLayerSegment->get_segment_start();
			i32MaxTime = pEffectSegment->get_key_time( pEffectSegment->get_key_count() - 1 ) + pLayerSegment->get_segment_start();
		}


		switch( pParam->get_type() ) {
		case PARAM_TYPE_INT:
			DrawControllerBar( pDC, pItem, pCont, i32TimeOffset, i32ViewMaxTime );
			DrawParameterCurves( pDC, pItem, pParam, i32TimeOffset, i32MinTime, i32MaxTime );
			DrawControllerKeys( pDC, pItem, pCont, i32TimeOffset, i32ViewMaxTime );
			break;
		case PARAM_TYPE_FLOAT:
			DrawControllerBar( pDC, pItem, pCont, i32TimeOffset, i32ViewMaxTime );
			DrawParameterCurves( pDC, pItem, pParam, i32TimeOffset, i32MinTime, i32MaxTime );
			DrawControllerKeys( pDC, pItem, pCont, i32TimeOffset, i32ViewMaxTime );
			break;
		case PARAM_TYPE_VECTOR2:
			DrawControllerBar( pDC, pItem, pCont, i32TimeOffset, i32ViewMaxTime );
			DrawParameterCurves( pDC, pItem, pParam, i32TimeOffset, i32MinTime, i32MaxTime );
			DrawControllerKeys( pDC, pItem, pCont, i32TimeOffset, i32ViewMaxTime );
			break;
		case PARAM_TYPE_VECTOR3:
			DrawControllerBar( pDC, pItem, pCont, i32TimeOffset, i32ViewMaxTime );
			DrawParameterCurves( pDC, pItem, pParam, i32TimeOffset, i32MinTime, i32MaxTime );
			DrawControllerKeys( pDC, pItem, pCont, i32TimeOffset, i32ViewMaxTime );
			break;
		case PARAM_TYPE_COLOR:
			DrawControllerBar( pDC, pItem, pCont, i32TimeOffset, i32ViewMaxTime );
			DrawParameterCurves( pDC, pItem, pParam, i32TimeOffset, i32MinTime, i32MaxTime );
			DrawControllerKeys( pDC, pItem, pCont, i32TimeOffset, i32ViewMaxTime );
			break;
		case PARAM_TYPE_FILE:
			DrawParameterFile( pDC, pItem, (ParamFileC*)pParam, i32TimeOffset, i32MinTime, i32MaxTime );
			break;
		case PARAM_TYPE_TEXT:
			// nothing to draw
			break;
		case PARAM_TYPE_LINK:
			// nothing to draw
			break;
		default:
			TRACE( "CTimeLineBar::DrawParameter(): Unsupported parameter 0x%x.\n", pParam->get_type() );
			break;
		}
	}
}

void CTimeLineBar::DrawTimeSegment( CDC* pDC, SceneItemC* pItem )
{
	TimeSegmentC*	pSegment;
	int32			i32TimeOffset;

	COLORREF	rHilight;

	// draw time segments
	if( pItem->get_type() == SCENEITEM_LAYER ) {
		pSegment = pItem->get_layer()->get_timesegment();
		i32TimeOffset = 0;
		rHilight = RGB( 196, 128, 160 );
	}
	else if( pItem->get_type() == SCENEITEM_EFFECT ) {
		i32TimeOffset = pItem->get_layer()->get_timesegment()->get_segment_start();
		pSegment = pItem->get_effect()->get_timesegment();
		rHilight = RGB( 128, 160, 196 );
	}
	else
		return;
		
	uint32	ui32NumValues = m_rTimelineRect.Width() / m_i32FrameWidthInPixels + 1;
	int32	i32MaxTime = m_i32ViewTimeStart + m_pDoc->FrameToTime( ui32NumValues );
	int32	i32SegmentStartTime = pSegment->get_segment_start();

	uint32		r = GetRValue( rHilight ), g = GetGValue( rHilight ), b = GetBValue( rHilight );
	COLORREF	rDarkHilight = RGB( r * 2 / 3, g * 2 / 3, b * 2 / 3 );
	COLORREF	rLightHilight = RGB( 255 - (255 - r) / 2, 255 - (255 - g) / 2, 255 - (255 - b) / 2 );

	CPen	rBlackPen( PS_SOLID, 1, ::GetSysColor( COLOR_BTNTEXT ) );
	CPen	rDarkPen( PS_SOLID, 1, rDarkHilight );
	CPen*	pOldPen = pDC->SelectObject( &rBlackPen );
	CBrush	rHiBrush( rHilight );
	CBrush*	pOldBrush = pDC->SelectObject( &rHiBrush );

	pDC->SelectObject( &rDarkPen );

	// draw boxes
	for( uint32 i = 0; i < pSegment->get_key_count(); i += 2 ) {

		if( i + 1 >= pSegment->get_key_count() )
			break;

		int32	i32Time1 = pSegment->get_key_time( i );
		int32	i32Time2 = pSegment->get_key_time( i + 1 );

		int32	i32Frame1 = m_pDoc->TimeToFrame( i32TimeOffset + i32Time1 - m_i32ViewTimeStart );
		int32	i32Frame2 = m_pDoc->TimeToFrame( i32TimeOffset + i32Time2 - m_i32ViewTimeStart );

		CRect	rRect;
		rRect.left = i32Frame1 * m_i32FrameWidthInPixels;
		rRect.right = i32Frame2 * m_i32FrameWidthInPixels + 1;
		rRect.top = pItem->get_y() + pItem->get_height() - 11;
		rRect.bottom = pItem->get_y() + pItem->get_height();

		// is it visible at all?
		if( rRect.left > m_rTimelineRect.Width() )
			continue;
		if( rRect.right < 0 )
			continue;

		// clamp
		if( rRect.left < 0 )
			rRect.left = 0;
		if( rRect.right > m_rTimelineRect.Width() )
			rRect.right = m_rTimelineRect.Width();

		rRect.NormalizeRect();

		pDC->Rectangle( rRect );
		rRect.DeflateRect( 1, 1, 1, 1 );

		// shaded ticks on bar
		int32	i32X = (rRect.left / m_i32FrameWidthInPixels) * m_i32FrameWidthInPixels;
		for( ; i32X < rRect.right; i32X += m_i32FrameWidthInPixels ) {
			pDC->MoveTo( i32X, rRect.bottom );
			pDC->LineTo( i32X, rRect.bottom - 4 );
		}
	}

	// draw keys
	for( i = 0; i < pSegment->get_key_count(); i++ ) {
		int32	i32Time = i32TimeOffset + pSegment->get_key_time( i );

		if( i32Time < m_i32ViewTimeStart || i32Time >= i32MaxTime )
			continue;

		i32Time -= m_i32ViewTimeStart;

		CPoint	rPt( m_pDoc->TimeToFrame( i32Time ) * m_i32FrameWidthInPixels, pItem->get_y() + pItem->get_height() - 11 );

		int32	i32Image = 26;
		if( pSegment->get_key( i )->get_flags() & KEY_SELECTED )
			i32Image++;

		if( i & 1 ) {
			i32Image += 2;
			m_rImageList.DrawIndirect( pDC, i32Image, rPt, CSize( 9, 11 ), CPoint( 0, 0 ), ILD_NORMAL, SRCCOPY, CLR_NONE, CLR_DEFAULT );
		}
		else {
			rPt.x -= 8;
			m_rImageList.DrawIndirect( pDC, i32Image, rPt, CSize( 9, 11 ), CPoint( 0, 0 ), ILD_NORMAL, SRCCOPY, CLR_NONE, CLR_DEFAULT );
		}
	}

	pDC->SelectObject( pOldPen );
	pDC->SelectObject( pOldBrush );
}


void CTimeLineBar::DrawTimeSegmentRange( CDC* pDC, SceneItemC* pItem )
{
	TimeSegmentC*	pSegment;
	int32			i32TimeOffset;

	// draw time segments
	if( pItem->get_type() == SCENEITEM_LAYER ) {
		pSegment = pItem->get_layer()->get_timesegment();
		i32TimeOffset = 0;
	}
	else if( pItem->get_type() == SCENEITEM_EFFECT ) {
		i32TimeOffset = pItem->get_layer()->get_timesegment()->get_segment_start();
		pSegment = pItem->get_effect()->get_timesegment();
	}
	else
		return;

	int32	i32Frame = m_pDoc->TimeToFrame( i32TimeOffset + pSegment->get_segment_start() - m_i32ViewTimeStart );

	CPoint	rPt( i32Frame * m_i32FrameWidthInPixels + 1, pItem->get_y() );
	m_rImageList.DrawIndirect( pDC, 17, rPt, CSize( 12, 16 ), CPoint( 0, 0 ), ILD_NORMAL, SRCCOPY, CLR_NONE, CLR_DEFAULT );
}


void CTimeLineBar::DrawGizmo( CDC* pDC, SceneItemC* pItem )
{
	// get complete time offset
	int32			i32TimeOffset = 0;
	i32TimeOffset += pItem->get_layer()->get_timesegment()->get_segment_start();
	i32TimeOffset += pItem->get_effect()->get_timesegment()->get_segment_start();

	uint32	ui32NumValues = m_rTimelineRect.Width() / m_i32FrameWidthInPixels + 1;
	int32	i32MaxTime = m_i32ViewTimeStart + m_pDoc->FrameToTime( ui32NumValues );

	GizmoI*	pGizmo = pItem->get_gizmo();

	if( pGizmo ) {

		int32	i32StartTime;
		int32	i32EndTime;
		bool	bFirst = true;
		int32	i32KeyCount = 0;

		std::vector<int32>	rKeys;


		for( uint32 i = 0; i < pGizmo->get_parameter_count(); i++ ) {
			ParamI*			pParam = pGizmo->get_parameter( i );
			if( !pParam )
				continue;
			ControllerC*	pCont = pParam->get_controller();
			if( !pCont )
				continue;

			if( pCont->get_key_count() ) {
				// the controller has keys
				i32KeyCount += pCont->get_key_count();

				int32	i32Start = pCont->get_key( 0 )->get_time();
				int32	i32End = pCont->get_key( pCont->get_key_count() - 1 )->get_time();

				// collect uniques keys from this controller
				for( uint32 j = 0; j < pCont->get_key_count(); j++ ) {
					int32	i32Time = pCont->get_key( j )->get_time();

					// avoid duplicates
					bool	bFound = false;
					for( uint32 k = 0; k < rKeys.size(); k++ ) {
						if( rKeys[k] == i32Time ) {
							bFound = true;
							break;
						}
					}

					if( !bFound )
						rKeys.push_back( i32Time );
				}

				if( bFirst ) {
					i32StartTime = i32Start;
					i32EndTime = i32End;
					bFirst = false;
				}
				else {
					// calc min/max time
					if( i32Start < i32StartTime )
						i32StartTime = i32Start;
					if( i32Start > i32EndTime )
						i32EndTime = i32Start;

					if( i32End < i32StartTime )
						i32StartTime = i32End;
					if( i32End > i32EndTime )
						i32EndTime = i32End;
				}

			}
		}

		if( i32KeyCount ) {
			// the gizmo is animated

			int32	i32Frame1 = m_pDoc->TimeToFrame( i32TimeOffset + i32StartTime - m_i32ViewTimeStart );
			int32	i32Frame2 = m_pDoc->TimeToFrame( i32TimeOffset + i32EndTime - m_i32ViewTimeStart );

			CRect	rRect;
			rRect.left = i32Frame1 * m_i32FrameWidthInPixels + 1;
			rRect.right = i32Frame2 * m_i32FrameWidthInPixels + m_i32FrameWidthInPixels + 1;
			rRect.top = pItem->get_y() + 5;
			rRect.bottom = pItem->get_y() + pItem->get_height();

			// is it visible at all?
			if( rRect.left > m_rTimelineRect.Width() )
				return;
			if( rRect.right < 0 )
				return;

			// clamp
			if( rRect.left < 0 )
				rRect.left = 0;
			if( rRect.right > m_rTimelineRect.Width() )
				rRect.right = m_rTimelineRect.Width();

			CPen	rBlackPen( PS_SOLID, 1, GetColor( TIMELINE_PARAM_SHADOW ) );//GetColor( TIMELINE_BLACK ) );
			CBrush	rHiBrush( GetColor( TIMELINE_PARAM_FACE ) );

			CPen*	pOldPen = pDC->SelectObject( &rBlackPen );
			CBrush*	pOldBrush = pDC->SelectObject( &rHiBrush );

			// draw bar
			pDC->Rectangle( rRect );
			rRect.DeflateRect( 1, 1, 1, 1 );

			pDC->SelectObject( pOldPen );
			pDC->SelectObject( pOldBrush );


			// draw keys
			for( i = 0; i < rKeys.size(); i++ ) {
				int32	i32Time = i32TimeOffset + rKeys[i];

				if( i32Time < m_i32ViewTimeStart || i32Time >= i32MaxTime )
					continue;

				i32Time -= m_i32ViewTimeStart;

				CPoint	rPt( i32Time * m_i32FrameWidthInPixels + (m_i32FrameWidthInPixels / 2 + 1) - 2, pItem->get_y() + 8 );

				// draw small dot
				m_rImageList.DrawIndirect( pDC, 12, rPt, CSize( 5, 5 ), CPoint( 0, 0 ), ILD_NORMAL, SRCCOPY, CLR_NONE, CLR_DEFAULT );
			}
		}

	}

}


void CTimeLineBar::DrawSceneTimeline( CDC* pDC )
{
	// clear background
	CRect	rClearRect = m_rTimelineRect;
	rClearRect.DeflateRect( 1, 1, 1, 1 );
	pDC->FillSolidRect( &rClearRect, GetColor( TIMELINE_BACK ) );

	// draw viewing area
	CRect	rInner;
	rInner = m_rTimelineRect;
	rInner.DeflateRect( 0, 1, 1, 1 );

	//
	// draw grid
	//

	// save old clip region
	CRect	rOldRect;
	CRgn	rOldClipRgn;
	pDC->GetClipBox( rOldRect );
	rOldClipRgn.CreateRectRgnIndirect( rOldRect );

	// create new clip region
	CRgn	rRegion;
	rRegion.CreateRectRgnIndirect( CRect( m_rTimelineRect.left + 1, m_rTimelineRect.top + 1,
									m_rTimelineRect.right - 1, m_rTimelineRect.bottom - 1 ) );
	pDC->SelectClipRgn( &rRegion );

	CPen	rBlackPen( PS_SOLID, 1, GetColor( TIMELINE_BLACK ) );
	CPen	rGreyPen( PS_SOLID, 1, GetColor( TIMELINE_GRID ) );
	CPen*	pOldPen = pDC->SelectObject( &rGreyPen );

	// set the origin
	CPoint	rOldOrigin;
	CPoint	rOrigin = rInner.TopLeft();
	rOrigin.y -= GetListPos();
	rOldOrigin = pDC->SetViewportOrg( rOrigin );

	int32	i32Height = m_rSceneItemList.get_list_height();
	int32	i32ItemHeight = m_rSceneItemList.get_item_height();

	int32	i32MaxY = 0;

	if( m_i32LastVisible != m_i32FirstVisible ) {
		SceneItemC*	pItem = m_rSceneItemList.get_sceneitem( m_rSceneItemList.get_sceneitem_count() - 1 );
		i32MaxY = pItem->get_y() + pItem->get_height();
		if( i32MaxY > GetListPos() + m_rTimelineRect.Height() )
			i32MaxY = GetListPos() + m_rTimelineRect.Height();
	}


	int32	i32Time;
	int32	i;


	int32	i32BeatLen = m_pDoc->GetQNotesPerBeat() * 256;
	int32	i32MeasureLen = m_pDoc->GetBeatsPerMeasure() * m_pDoc->GetQNotesPerBeat() * 256;

	// Vertical lines
	for( i = m_i32FirstVisible; i < m_i32LastVisible; i++ ) {
		SceneItemC*	pItem = m_rSceneItemList.get_sceneitem( i );
		int32	i32Y = pItem->get_y();

		if( pItem->get_type() == SCENEITEM_LAYER || pItem->get_type() == SCENEITEM_EFFECT ||
			(pItem->get_type() == SCENEITEM_PARAMETER && pItem->get_flags() & ITEM_ANIMATED) ) {

			pDC->MoveTo( 0, i32Y + i32ItemHeight );
			pDC->LineTo( m_rTimelineRect.Width() - 2, i32Y + i32ItemHeight );

			if( pItem->get_height() > i32ItemHeight ) {
				pDC->MoveTo( 0, i32Y + pItem->get_height() );
				pDC->LineTo( m_rTimelineRect.Width() - 2, i32Y + pItem->get_height() );
			}

			// Horizontal lines
			i32Time = m_i32ViewTimeStart;
			// Make the time start from whole tick
			i32Time = (i32Time / m_pDoc->GetFrameSizeInTicks()) * m_pDoc->GetFrameSizeInTicks();

			for( int32 j = 0; j < m_rTimelineRect.Width(); j += m_i32FrameWidthInPixels ) {


				if( ( i32Time % i32MeasureLen) == 0 ) {
					pDC->FillSolidRect( j, i32Y + 1, m_i32FrameWidthInPixels, i32ItemHeight - 1, GetColor( TIMELINE_DARK_TICK ) );
				}
				else if( (i32Time % i32BeatLen) == 0 ) {
					pDC->FillSolidRect( j, i32Y + 1, m_i32FrameWidthInPixels, i32ItemHeight - 1, GetColor( TIMELINE_LIGHT_TICK ) );
				}

				pDC->MoveTo( j, i32Y + i32ItemHeight );
				pDC->LineTo( j, i32Y + (i32ItemHeight * 4 / 5) );

				i32Time += m_pDoc->GetFrameSizeInTicks();
			}
		}


		// draw time segments
		if( pItem->get_type() == SCENEITEM_LAYER ) {
			DrawTimeSegmentRange( pDC, pItem );
			DrawTimeSegment( pDC, pItem );
		}
		else if( pItem->get_type() == SCENEITEM_EFFECT ) {
			DrawTimeSegmentRange( pDC, pItem );
			DrawTimeSegment( pDC, pItem );
		}
		else if( pItem->get_type() == SCENEITEM_GIZMO ) {
			DrawGizmo( pDC, pItem );
		}
		else if( pItem->get_type() == SCENEITEM_PARAMETER ) {
			DrawParameter( pDC, pItem );
		}
	}
	// draw the last vertical line too
	if( m_i32LastVisible != m_i32FirstVisible ) {
		SceneItemC*	pItem = m_rSceneItemList.get_sceneitem( m_rSceneItemList.get_sceneitem_count() - 1 );
		int32	i32Y = pItem->get_y() + pItem->get_height();
		pDC->MoveTo( 0, i32Y );
		pDC->LineTo( m_rTimelineRect.Width() - 2, i32Y );
	}


	// select old objects
	pDC->SelectObject( pOldPen );

	// restore old clip region
	pDC->SelectClipRgn( &rOldClipRgn );
	rOldClipRgn.DeleteObject();
	rRegion.DeleteObject();

	// return viewport origin
	pDC->SetViewportOrg( rOldOrigin );
}

void CTimeLineBar::DrawSceneList( CDC* pDC )
{
	CRect	rInner;

	// clear background
	CRect	rClearRect = m_rListRect;
	rClearRect.DeflateRect( 1, 1, 0, 1 );
	pDC->FillSolidRect( &rClearRect, GetColor( TIMELINE_LIGHT_TICK ) );

	// Inner is the area where we can draw
	rInner = m_rListRect;
	rInner.DeflateRect( 1, 1, 0, 1 );

	// draw the eye and the lock symbols
	m_rImageList.DrawIndirect( pDC, 10, CPoint( rInner.left + 4, rInner.top - 16 ), CSize( 12, 12 ), CPoint( 0, 0 ), ILD_NORMAL, SRCCOPY, CLR_NONE, CLR_DEFAULT );
	m_rImageList.DrawIndirect( pDC, 11, CPoint( rInner.left + 25, rInner.top - 16 ), CSize( 12, 12 ), CPoint( 0, 0 ), ILD_NORMAL, SRCCOPY, CLR_NONE, CLR_DEFAULT );

	// set font attributes
	CFont*	pOldFont = pDC->SelectObject( &m_rSmallFont );
	pDC->SetBkMode( TRANSPARENT );

	CRect	rClipRect;
	rClipRect.left = rInner.left + 40;
	rClipRect.right = rInner.left + rInner.Width() - m_i32LayerValueSize;
	rClipRect.top = rInner.top - 20;
	rClipRect.bottom = rInner.top;
	pDC->ExtTextOut( rInner.left + 45, rInner.top - 16, ETO_CLIPPED, rClipRect, "Scene", NULL );

	rClipRect.left = rInner.left + rInner.Width() - m_i32LayerValueSize;
	rClipRect.right = rInner.right;
	pDC->ExtTextOut( rInner.left + rInner.Width() - m_i32LayerValueSize + 5, rInner.top - 16, ETO_CLIPPED, rClipRect, "Value", NULL );

	CRect	rHeader;
	rHeader.top = m_rListRect.top - 22;
	rHeader.bottom = m_rListRect.top + 1;

	// eye
	rHeader.left = m_rListRect.left;
	rHeader.right = m_rListRect.left + 21;
	pDC->Draw3dRect( rHeader, ::GetSysColor( COLOR_3DHILIGHT ), ::GetSysColor( COLOR_3DSHADOW ) );

	// lock
	rHeader.left = rHeader.right;
	rHeader.right = m_rListRect.left + 41;
	pDC->Draw3dRect( rHeader, ::GetSysColor( COLOR_3DHILIGHT ), ::GetSysColor( COLOR_3DSHADOW ) );

	// scene
	rHeader.left = rHeader.right;
	rHeader.right = rInner.left + rInner.Width() - m_i32LayerValueSize + 1;
	pDC->Draw3dRect( rHeader, ::GetSysColor( COLOR_3DHILIGHT ), ::GetSysColor( COLOR_3DSHADOW ) );

	// value
	rHeader.left = rHeader.right;
	rHeader.right = m_rListRect.right + 1;
	pDC->Draw3dRect( rHeader, ::GetSysColor( COLOR_3DHILIGHT ), ::GetSysColor( COLOR_3DSHADOW ) );


	// save old clip region
	CRect	rOldRect;
	CRgn	rOldClipRgn;
	pDC->GetClipBox( rOldRect );
	rOldClipRgn.CreateRectRgnIndirect( rOldRect );

	// create new clip region
	CRgn	rRegion;
	rRegion.CreateRectRgnIndirect( rInner );
	pDC->SelectClipRgn( &rRegion );

	// set the origin
	CPoint	rOldOrigin;
	CPoint	rOrigin = rInner.TopLeft();
	rOrigin.y -= GetListPos();
	rOldOrigin = pDC->SetViewportOrg( rOrigin );

	int32	i32Height = m_rSceneItemList.get_list_height();
	int32	i32ItemHeight = m_rSceneItemList.get_item_height();

	// text clip rect
	rClipRect.left = 0;
	rClipRect.top = GetListPos();
	rClipRect.right = rInner.Width() - m_i32LayerValueSize;
	rClipRect.bottom = rInner.Height() + GetListPos();

	CPen		rBlackPen( PS_SOLID, 1, GetColor( TIMELINE_GRID ) );
	CPen*		pOldPen = pDC->SelectObject( &rBlackPen );
	COLORREF	rBgCol = ::GetSysColor( COLOR_BTNFACE );
	COLORREF	rHiCol = ::GetSysColor( COLOR_3DHILIGHT );
	COLORREF	rShCol = ::GetSysColor( COLOR_3DSHADOW );

	pDC->SelectObject( &m_rFont );	

	for( int32 i = m_i32FirstVisible; i < m_i32LastVisible; i++ ) {

		bool		bNextRow = ((i + 1) < m_rSceneItemList.get_sceneitem_count());
		SceneItemC*	pItem = m_rSceneItemList.get_sceneitem( i );
		SceneItemC*	pNextItem = 0;
		if( bNextRow )
			pNextItem = m_rSceneItemList.get_sceneitem( i + 1 );

		if( pItem->get_flags() & ITEM_SELECTED ) {
			// draw selection bg
			pDC->FillSolidRect( pItem->get_x() + 40, pItem->get_y(), rInner.Width() - m_i32LayerValueSize - pItem->get_x() - 40, i32ItemHeight, ::GetSysColor( COLOR_HIGHLIGHT ) );
			pDC->SetTextColor( ::GetSysColor( COLOR_HIGHLIGHTTEXT ) );
		}
		else
			pDC->SetTextColor( ::GetSysColor( COLOR_BTNTEXT ) );

		bool	bHasKeys = false;
		CRect	rTextClip = rClipRect;
		int32	i32TextX = rInner.right - m_i32LayerValueSize + 4;
		int32	i32TextY = pItem->get_y() + i32ItemHeight/2 - 4;
		int32	i32LeftX = rInner.right - m_i32LayerValueSize;
		int32	i32RightX = rInner.right;
		bool	bHasKeyNextButton = false;

		if( (pItem->get_flags() & ITEM_SELECTED) &&
			(pItem->get_type() == SCENEITEM_LAYER || pItem->get_type() == SCENEITEM_EFFECT || (pItem->get_flags() & ITEM_ANIMATED)) ) {
			bHasKeyNextButton = true;
			i32RightX -= 13;
			rTextClip.right -= 13;
		}

		if( pItem->get_type() == SCENEITEM_LAYER ) {
			// draw widest possible line above layers

			if( m_bMouseMoved && m_eTrackAction == TRACKING_EFFECTDRAG && pItem->get_layer_index() == m_rTrackTargetItem.get_layer_index() ) {
				// draw selection bg
				pDC->FillSolidRect( pItem->get_x() + 40, pItem->get_y(), rInner.Width() - m_i32LayerValueSize - pItem->get_x() - 40, i32ItemHeight, ::GetSysColor( COLOR_HIGHLIGHT ) );
				pDC->SetTextColor( ::GetSysColor( COLOR_HIGHLIGHTTEXT ) );
			}

			pDC->SelectObject( &m_rFont );
			pDC->ExtTextOut( pItem->get_x() + 50 + 9, pItem->get_y() + i32ItemHeight/2 - 6,
							ETO_CLIPPED, rClipRect, GetItemName( pItem ), NULL );
			pDC->MoveTo( 0, pItem->get_y() );
			pDC->LineTo( rInner.Width(), pItem->get_y() );

			// draw the value separator line
			pDC->MoveTo( rInner.Width() - m_i32LayerValueSize, pItem->get_y() + pItem->get_height() );
			pDC->LineTo( rInner.Width(), pItem->get_y() + pItem->get_height() );

			m_rImageList.DrawIndirect( pDC, 20, CPoint( pItem->get_x() + 40 + 3, pItem->get_y() + i32ItemHeight/2 - 4 ), CSize( 12, 9 ), CPoint( 0, 0 ), ILD_NORMAL, SRCCOPY, CLR_NONE, CLR_DEFAULT );

			// if invalidate is called while layer is tracked draw insertion mark
			if( m_bMouseMoved && m_eTrackAction == TRACKING_LAYERDRAG && pItem->get_layer_index() == m_rTrackTargetItem.get_layer_index() ) {
				if( !m_bTrackInsertAfter )
					pDC->FillSolidRect( pItem->get_x() + 20, pItem->get_y() - 1, rInner.Width(), 3, GetColor( TIMELINE_BLACK ) );
				else
					pDC->FillSolidRect( pItem->get_x() + 20, pItem->get_y() - 1 + pItem->get_height(), rInner.Width(), 3, GetColor( TIMELINE_BLACK ) );
			}

			pDC->SetTextColor( ::GetSysColor( COLOR_BTNTEXT ) );

			// draw duration

			pDC->SelectObject( &m_rSmallFont );
			pDC->SetTextColor( ::GetSysColor( COLOR_BTNTEXT ) );

			TimeSegmentC*	pLayerSegment = pItem->get_layer()->get_timesegment();
			if( pLayerSegment ) {

				CString		sValue;
				if( pLayerSegment->get_key_count() ) {
					KeyC*	pKeyStart = pLayerSegment->get_key( 0 );
					KeyC*	pKeyEnd = pLayerSegment->get_key( pLayerSegment->get_key_count() - 1 );
					int32	i32TimeOffset = pLayerSegment->get_segment_start();
					int32	i32Start = m_pDoc->TimeToFrame( pKeyStart->get_time() + i32TimeOffset );
					int32	i32End = m_pDoc->TimeToFrame( pKeyEnd->get_time() + i32TimeOffset );

					int32	i32CompWidth = (i32RightX - i32LeftX) / 2;
				
					rTextClip.left = i32LeftX;
					rTextClip.right = i32LeftX + i32CompWidth;
					// start
					sValue.Format( "Start: %d", i32Start );
					pDC->ExtTextOut( rTextClip.left + 4, i32TextY, ETO_CLIPPED, rTextClip, sValue, NULL );
					// advance
					rTextClip.left += i32CompWidth;
					rTextClip.right += i32CompWidth;
					//seprator
					pDC->MoveTo( rTextClip.left, pItem->get_y() );
					pDC->LineTo( rTextClip.left, pItem->get_y() + i32ItemHeight );
					// end						
					sValue.Format( "Len: %d", i32End - i32Start );
					pDC->ExtTextOut( rTextClip.left + 4, i32TextY, ETO_CLIPPED, rTextClip, sValue, NULL );

					// this item has keys
					bHasKeys = true;
				}
				else {
					sValue.Format( "No Keys" );
					pDC->TextOut( i32TextX, i32TextY, sValue );
				}
			}
		}
		else if( pItem->get_type() == SCENEITEM_EFFECT ) {

			EffectI*		pEffect = pItem->get_effect();

			if( pEffect ) {

				// Check if any of the gizmos are hidden
				bool	bHasHidden = false;
				for( uint32 j = 0; j < pEffect->get_gizmo_count(); j++ ) {
					GizmoI*	pGizmo = pEffect->get_gizmo( j );
					if( pGizmo->get_flags() & ITEM_GUIHIDDEN ) {
						bHasHidden = true;
						break;
					}
				}

				CString	sItemName = pEffect->get_name();
				if( bHasHidden )
					sItemName += " ...";

				// draw undeline
				pDC->SelectObject( &m_rItalicFont );
				pDC->ExtTextOut( pItem->get_x() + 50 + 6, pItem->get_y() + i32ItemHeight/2 - 6,
								ETO_CLIPPED, rClipRect, sItemName, NULL );
				pDC->MoveTo( pItem->get_x() + 40, pItem->get_y() + pItem->get_height() );
				pDC->LineTo( rInner.Width(), pItem->get_y() + pItem->get_height() );

				// draw the value separator line
				pDC->MoveTo( rInner.Width() - m_i32LayerValueSize, pItem->get_y() + pItem->get_height() );
				pDC->LineTo( rInner.Width(), pItem->get_y() + pItem->get_height() );

				if( pEffect->get_class_id() == CLASS_SUBSCENE )
					m_rImageList.DrawIndirect( pDC, 22, CPoint( pItem->get_x() + 40 + 3, pItem->get_y() + i32ItemHeight/2 - 4 ), CSize( 9, 9 ), CPoint( 0, 0 ), ILD_NORMAL, SRCCOPY, CLR_NONE, CLR_DEFAULT );
				else
					m_rImageList.DrawIndirect( pDC, 21, CPoint( pItem->get_x() + 40 + 3, pItem->get_y() + i32ItemHeight/2 - 4 ), CSize( 9, 9 ), CPoint( 0, 0 ), ILD_NORMAL, SRCCOPY, CLR_NONE, CLR_DEFAULT );


				// if invalidate is called while layer is tracked draw insertion mark
				if( m_bMouseMoved && m_eTrackAction == TRACKING_EFFECTDRAG &&
					pItem->get_layer_index() == m_rTrackTargetItem.get_layer_index() &&
					pItem->get_effect_index() == m_rTrackTargetItem.get_effect_index() ) {

					if( !m_bTrackInsertAfter )
						pDC->FillSolidRect( pItem->get_x() + 20, pItem->get_y() - 1, rInner.Width(), 3, GetColor( TIMELINE_BLACK ) );
					else
						pDC->FillSolidRect( pItem->get_x() + 20, pItem->get_y() - 1 + pItem->get_height(), rInner.Width(), 3, GetColor( TIMELINE_BLACK ) );
				}

				// draw duration
				pDC->SelectObject( &m_rSmallFont );
				pDC->SetTextColor( ::GetSysColor( COLOR_BTNTEXT ) );

				TimeSegmentC*	pEffectSegment = pEffect->get_timesegment();
				TimeSegmentC*	pLayerSegment = pItem->get_layer()->get_timesegment();

				CString		sValue;
				if( pEffectSegment->get_key_count() ) {
					KeyC*	pKeyStart = pEffectSegment->get_key( 0 );
					KeyC*	pKeyEnd = pEffectSegment->get_key( pEffectSegment->get_key_count() - 1 );
					int32	i32TimeOffset = pEffectSegment->get_segment_start() + pLayerSegment->get_segment_start();
					int32	i32Start = m_pDoc->TimeToFrame( pKeyStart->get_time() + i32TimeOffset );
					int32	i32End = m_pDoc->TimeToFrame( pKeyEnd->get_time() + i32TimeOffset );


					int32	i32CompWidth = (i32RightX - i32LeftX) / 2;
				
					rTextClip.left = i32LeftX;
					rTextClip.right = i32LeftX + i32CompWidth;
					// start
					sValue.Format( "Start: %d", i32Start );
					pDC->ExtTextOut( rTextClip.left + 4, i32TextY, ETO_CLIPPED, rTextClip, sValue, NULL );
					// advance
					rTextClip.left += i32CompWidth;
					rTextClip.right += i32CompWidth;
					//seprator
					pDC->MoveTo( rTextClip.left, pItem->get_y() );
					pDC->LineTo( rTextClip.left, pItem->get_y() + i32ItemHeight );
					// end						
					sValue.Format( "Len: %d", i32End - i32Start );
					pDC->ExtTextOut( rTextClip.left + 4, i32TextY, ETO_CLIPPED, rTextClip, sValue, NULL );

					// this item has keys
					bHasKeys = true;
				}
				else {
					sValue.Format( "No Keys" );
					pDC->TextOut( i32TextX, i32TextY, sValue );
				}
			}
		}
		else if( pItem->get_type() == SCENEITEM_GIZMO ) {

			GizmoI*		pGizmo = pItem->get_gizmo();

			if( pGizmo ) {

				// Check if any of the params are hidden
				bool	bHasHidden = false;
				for( uint32 j = 0; j < pGizmo->get_parameter_count(); j++ ) {
					ParamI*	pParam = pGizmo->get_parameter( j );
					if( pParam->get_flags() & ITEM_GUIHIDDEN ) {
						bHasHidden = true;
						break;
					}
				}

				CString	sItemName = pGizmo->get_name();
				if( bHasHidden )
					sItemName += " ...";

				pDC->SelectObject( &m_rFont );
				pDC->ExtTextOut( pItem->get_x() + 40 + 3, pItem->get_y() + i32ItemHeight/2 - 6,
								ETO_CLIPPED, rClipRect, sItemName, NULL );

				if( !(pItem->get_flags() & ITEM_EXPANDED) && bNextRow ) {
					// draw undeline only for non-expanded gizmos
					pDC->MoveTo( pItem->get_x() + 40, pItem->get_y() + pItem->get_height() );
					pDC->LineTo( rInner.Width(), pItem->get_y() + pItem->get_height() );
				}
				else {
					// draw only the value separator line
					pDC->MoveTo( rInner.Width() - m_i32LayerValueSize, pItem->get_y() + pItem->get_height() );
					pDC->LineTo( rInner.Width(), pItem->get_y() + pItem->get_height() );
				}
			}
		}
		else if( pItem->get_type() == SCENEITEM_PARAMETER ) {
			pDC->SelectObject( &m_rFont );

			if( bNextRow && !(pNextItem->get_type() == SCENEITEM_PARAMETER) ) {
				// last parameter in gizmo... draw wider line
				pDC->MoveTo( pItem->get_x() - 20 + 40, pNextItem->get_y() );
				pDC->LineTo( rInner.Width(), pNextItem->get_y() );
			}
			else {
				// draw undeline
				pDC->MoveTo( pItem->get_x() + 40, pItem->get_y() + pItem->get_height() );
				pDC->LineTo( rInner.Width(), pItem->get_y() + pItem->get_height() );
			}

			int32	i32TimeOffset = 0;
			i32TimeOffset += pItem->get_layer()->get_timesegment()->get_segment_start();
			i32TimeOffset += pItem->get_effect()->get_timesegment()->get_segment_start();

			int32	i32Time = GetTimecursor() - i32TimeOffset;

			ParamI*	pParam = pItem->get_parameter();
			CString	sValue, sFormat;

			if( pParam ) {

				if( pParam->get_type() == PARAM_TYPE_FILE || pParam->get_type() == PARAM_TYPE_LINK ) {
					pDC->ExtTextOut( pItem->get_x() + 40 + 3 + 15 + 14, pItem->get_y() + i32ItemHeight/2 - 6,
									ETO_CLIPPED, rClipRect, GetItemName( pItem ), NULL );
				}
				else {
					pDC->ExtTextOut( pItem->get_x() + 40 + 3 + 15, pItem->get_y() + i32ItemHeight/2 - 6,
									ETO_CLIPPED, rClipRect, GetItemName( pItem ), NULL );
				}

				pDC->SelectObject( &m_rSmallFont );
				pDC->SetTextColor( ::GetSysColor( COLOR_BTNTEXT ) );

				ControllerC*	pCont = pParam->get_controller();
				if( pCont && pCont->get_key_count() ) {
					// this item has keys
					bHasKeys = true;
				}

				switch( pParam->get_type() ) {
				case PARAM_TYPE_INT:
					{
						ParamIntC*	pParamInt = (ParamIntC*)pParam;

						int32		i32Val;
						pParamInt->get_val( i32Time, i32Val );

						rTextClip.left = i32LeftX;
						rTextClip.right = i32RightX - 11;

						if( pParamInt->get_style() == PARAM_STYLE_COMBOBOX && pParamInt->get_label_count() ) {
							// draw labels name if possible
							bool	bFound = false;
							for( uint32 j = 0; j < pParamInt->get_label_count(); j++ ) {
								if( i32Val == pParamInt->get_label_value( j ) ) {
									sValue = pParamInt->get_label_name( j );
									bFound = true;
									break;
								}
							}
							if( !bFound )
								sValue.Format( "%d", i32Val );

							pDC->ExtTextOut( i32TextX, i32TextY, ETO_CLIPPED, rTextClip, sValue, NULL );

							// Draw combobox arrow
							m_rImageList.DrawIndirect( pDC, 25, CPoint( i32RightX - 5 - 3, pItem->get_y() + i32ItemHeight/2 - 2 ), CSize( 5, 3 ), CPoint( 0, 0 ), ILD_NORMAL, SRCCOPY, CLR_NONE, CLR_DEFAULT );
						}
						else {
							sValue.Format( "%d", i32Val );
							pDC->ExtTextOut( i32TextX, i32TextY, ETO_CLIPPED, rTextClip, sValue, NULL );

							// Draw spinner arrow
							m_rImageList.DrawIndirect( pDC, 24, CPoint( i32RightX - 5 - 4, pItem->get_y() + i32ItemHeight/2 - 3 ), CSize( 5, 7 ), CPoint( 0, 0 ), ILD_NORMAL, SRCCOPY, CLR_NONE, CLR_DEFAULT );
						}
					}
					break;
				case PARAM_TYPE_FLOAT:
					{
						ParamFloatC*	pParamFloat = (ParamFloatC*)pParam;
						float32			f32Val;
						float32			f32Scale = 1.0f;

						if( pParam->get_style() & PARAM_STYLE_PERCENT ) 
							f32Scale = 100.0f;
						pParamFloat->get_val( i32Time, f32Val );

						rTextClip.left = i32LeftX;
						rTextClip.right = i32RightX - 11;

						sFormat.Format( "%%.%df", (int32)__max( -floor( log10( pParamFloat->get_increment() * f32Scale ) ), 0 ) );
						sValue.Format( sFormat, f32Val * f32Scale );
						if( pParam->get_style() & PARAM_STYLE_PERCENT ) 
							sValue += "%";
						else if( pParam->get_style() & PARAM_STYLE_ANGLE ) 
							sValue += "°";
						pDC->ExtTextOut( i32TextX, i32TextY, ETO_CLIPPED, rTextClip, sValue, NULL );

						// Draw spinner arrow
						m_rImageList.DrawIndirect( pDC, 24, CPoint( i32RightX - 5 - 4, pItem->get_y() + i32ItemHeight/2 - 3 ), CSize( 5, 7 ), CPoint( 0, 0 ), ILD_NORMAL, SRCCOPY, CLR_NONE, CLR_DEFAULT );
					}
					break;
				case PARAM_TYPE_VECTOR2:
					{
						ParamVector2C*	pParamVec2 = (ParamVector2C*)pParam;
						Vector2C		rVal;
						float32			f32Scale = 1.0f;

						if( pParam->get_style() & PARAM_STYLE_PERCENT ) 
							f32Scale = 100.0f;
						pParamVec2->get_val( i32Time, rVal );
						int32	i32NFrac = (int32)__max( -floor( log10( pParamVec2->get_increment() * f32Scale ) ), 0 );

						if( pParam->get_style() & PARAM_STYLE_PERCENT )
							sFormat.Format( "%%.%df%%%%", i32NFrac );
						else if( pParam->get_style() & PARAM_STYLE_ANGLE )
							sFormat.Format( "%%.%df°", i32NFrac );
						else
							sFormat.Format( "%%.%df", i32NFrac );

						sValue.Format( sFormat, rVal[0] * f32Scale, rVal[1] * f32Scale );

						int32	i32CompWidth = (i32RightX - i32LeftX) / 2;

						rTextClip.left = i32LeftX;
						rTextClip.right = i32LeftX + i32CompWidth - 11;

						sValue.Format( sFormat, rVal[0] * f32Scale );
						pDC->ExtTextOut( rTextClip.left + 4, i32TextY, ETO_CLIPPED, rTextClip, sValue, NULL );
						m_rImageList.DrawIndirect( pDC, 24, CPoint( rTextClip.right + 3, pItem->get_y() + i32ItemHeight/2 - 3 ), CSize( 5, 7 ), CPoint( 0, 0 ), ILD_NORMAL, SRCCOPY, CLR_NONE, CLR_DEFAULT );

						rTextClip.left += i32CompWidth;
						rTextClip.right += i32CompWidth;

						pDC->MoveTo( rTextClip.left, pItem->get_y() );
						pDC->LineTo( rTextClip.left, pItem->get_y() + i32ItemHeight );

						sValue.Format( sFormat, rVal[1] * f32Scale );
						pDC->ExtTextOut( rTextClip.left + 4, i32TextY, ETO_CLIPPED, rTextClip, sValue, NULL );
						m_rImageList.DrawIndirect( pDC, 24, CPoint( rTextClip.right + 3, pItem->get_y() + i32ItemHeight/2 - 3 ), CSize( 5, 7 ), CPoint( 0, 0 ), ILD_NORMAL, SRCCOPY, CLR_NONE, CLR_DEFAULT );

					}
					break;
				case PARAM_TYPE_VECTOR3:
					{
						ParamVector3C*	pParamVec3 = (ParamVector3C*)pParam;
						Vector3C		rVal;
						float32			f32Scale = 1.0f;

						if( pParam->get_style() & PARAM_STYLE_PERCENT ) 
							f32Scale = 100.0f;
						pParamVec3->get_val( i32Time, rVal );
						int32	i32NFrac = (int32)__max( -floor( log10( pParamVec3->get_increment() * f32Scale ) ), 0 );

						if( pParam->get_style() & PARAM_STYLE_PERCENT )
							sFormat.Format( "%%.%df%%%%", i32NFrac );
						else if( pParam->get_style() & PARAM_STYLE_ANGLE )
							sFormat.Format( "%%.%df°", i32NFrac );
						else
							sFormat.Format( "%%.%df", i32NFrac );

						sValue.Format( sFormat, rVal[0] * f32Scale, rVal[1] * f32Scale );

						int32	i32CompWidth = (i32RightX - i32LeftX) / 2;

						rTextClip.left = i32LeftX;
						rTextClip.right = i32LeftX + i32CompWidth - 11;

						sValue.Format( sFormat, rVal[0] * f32Scale );
						pDC->ExtTextOut( rTextClip.left + 4, i32TextY, ETO_CLIPPED, rTextClip, sValue, NULL );
						m_rImageList.DrawIndirect( pDC, 24, CPoint( rTextClip.right + 3, pItem->get_y() + i32ItemHeight/2 - 3 ), CSize( 5, 7 ), CPoint( 0, 0 ), ILD_NORMAL, SRCCOPY, CLR_NONE, CLR_DEFAULT );

						rTextClip.left += i32CompWidth;
						rTextClip.right += i32CompWidth;

						pDC->MoveTo( rTextClip.left, pItem->get_y() );
						pDC->LineTo( rTextClip.left, pItem->get_y() + i32ItemHeight );

						sValue.Format( sFormat, rVal[1] * f32Scale );
						pDC->ExtTextOut( rTextClip.left + 4, i32TextY, ETO_CLIPPED, rTextClip, sValue, NULL );
						m_rImageList.DrawIndirect( pDC, 24, CPoint( rTextClip.right + 3, pItem->get_y() + i32ItemHeight/2 - 3 ), CSize( 5, 7 ), CPoint( 0, 0 ), ILD_NORMAL, SRCCOPY, CLR_NONE, CLR_DEFAULT );

						rTextClip.left += i32CompWidth;
						rTextClip.right += i32CompWidth;

						pDC->MoveTo( rTextClip.left, pItem->get_y() );
						pDC->LineTo( rTextClip.left, pItem->get_y() + i32ItemHeight );

						sValue.Format( sFormat, rVal[2] * f32Scale );
						pDC->ExtTextOut( rTextClip.left + 4, i32TextY, ETO_CLIPPED, rTextClip, sValue, NULL );
						m_rImageList.DrawIndirect( pDC, 24, CPoint( rTextClip.right + 3, pItem->get_y() + i32ItemHeight/2 - 3 ), CSize( 5, 7 ), CPoint( 0, 0 ), ILD_NORMAL, SRCCOPY, CLR_NONE, CLR_DEFAULT );

					}
					break;
				case PARAM_TYPE_COLOR:
					{
						ParamColorC*	pParamColor = (ParamColorC*)pParam;
						ColorC			rVal;
						pParamColor->get_val( i32Time, rVal );

						sValue.Format( "R%3d G%3d B%3d A%3d", (int32)(rVal[0] * 255.0f), (int32)(rVal[1] * 255.0f), (int32)(rVal[2] * 255.0f), (int32)(rVal[3] * 255.0f) );

						pDC->TextOut( i32TextX + 20, i32TextY, sValue );

						CBrush	rColor( RGB( (int)(rVal[0] * 255.0f), (int)(rVal[1] * 255.0f), (int)(rVal[2] * 255.0f) ) ); 
						CBrush*	pOldBrush = pDC->SelectObject( &rColor );
						pDC->SelectObject( &rBlackPen );
						pDC->Rectangle( i32TextX, i32TextY - 1, i32TextX + 16, i32TextY + 10 );

						pDC->SelectObject( pOldBrush );

					}
					break;
				case PARAM_TYPE_TEXT:
					{
						ParamTextC*	pParamText = (ParamTextC*)pParam;
						pDC->TextOut( i32TextX, i32TextY, pParamText->get_val( i32Time ) );
					}
					break;
				case PARAM_TYPE_FILE:
					{
						ParamFileC*		pParamFile = (ParamFileC*)pParam;
						FileHandleC*	pHandle = pParamFile->get_file( i32Time );
						ImportableI*	pImportable = 0;
						std::string		sStr;

						if( pHandle )
							pImportable = pHandle->get_importable();

						if( pImportable ) {
							char szFname[_MAX_FNAME];
							char szExt[_MAX_EXT];
							_splitpath( pImportable->get_filename(), 0, 0, szFname, szExt );
							sStr += szFname;
							sStr += szExt;
						}
						else
							sStr = "<Empty>";

						rTextClip.left = i32LeftX;
						rTextClip.right = i32RightX - 11;

						// file icon
						m_rImageList.DrawIndirect( pDC, 16, CPoint( pItem->get_x() + 40 + 15 + 3, pItem->get_y() + i32ItemHeight/2 - 4 ), CSize( 12, 12 ), CPoint( 0, 0 ), ILD_NORMAL, SRCCOPY, CLR_NONE, CLR_DEFAULT );
						// print file name
						pDC->ExtTextOut( rTextClip.left + 20, i32TextY, ETO_CLIPPED, rTextClip, sStr.c_str(), NULL );

						// Draw combobox arrow
						m_rImageList.DrawIndirect( pDC, 25, CPoint( i32RightX - 5 - 4, pItem->get_y() + i32ItemHeight/2 - 2 ), CSize( 5, 3 ), CPoint( 0, 0 ), ILD_NORMAL, SRCCOPY, CLR_NONE, CLR_DEFAULT );
					}
					break;
				case PARAM_TYPE_LINK:
					{
						ParamLinkC*	pParamLink = (ParamLinkC*)pParam;

						rTextClip.left = i32LeftX;
						rTextClip.right = i32RightX - 11;

						if( pParamLink->get_style() == PARAM_STYLE_SINGLE_LINK )
						{
							// Draw the linked effect's name.
							EffectI*	pEffect = pParamLink->get_link( 0 );
							if( pEffect )
								pDC->ExtTextOut( rTextClip.left + 20, i32TextY, ETO_CLIPPED, rTextClip, pEffect->get_name(), NULL );
							else
								pDC->ExtTextOut( rTextClip.left + 20, i32TextY, ETO_CLIPPED, rTextClip, "<Unlinked>", NULL );
						}
						else
						{
							// If expanded, list all links.
							if( (pItem->get_flags() & ITEM_EXPANDED) )
							{
								for( int i = 0; i < pParamLink->get_link_count(); i++ )
								{
									EffectI*	pEffect = pParamLink->get_link( 0 );
									if( pEffect )
										pDC->ExtTextOut( rTextClip.left + 20, i32TextY + i32ItemHeight * i, ETO_CLIPPED, rTextClip, pEffect->get_name(), NULL );
								}
							}
							else
							{
								// When not expanded, either draw the only link's name, or how many links there are in the param.
								if( pParamLink->get_link_count() == 0 )
								{
									pDC->ExtTextOut( rTextClip.left + 20, i32TextY, ETO_CLIPPED, rTextClip, "<Unlinked>", NULL );
								}
								else if( pParamLink->get_link_count() == 1 )
								{
									EffectI*	pEffect = pParamLink->get_link( 0 );
									if( pEffect )
										pDC->ExtTextOut( rTextClip.left + 20, i32TextY, ETO_CLIPPED, rTextClip, pEffect->get_name(), NULL );
									else
										pDC->ExtTextOut( rTextClip.left + 20, i32TextY, ETO_CLIPPED, rTextClip, "<Unlinked>", NULL );
								}
								else
								{
									CString	sLabel;
									sLabel.Format( "%d Links", pParamLink->get_link_count() );
									pDC->ExtTextOut( rTextClip.left + 20, i32TextY, ETO_CLIPPED, rTextClip, sLabel, NULL );
								}
							}

						}

						// link icon
						m_rImageList.DrawIndirect( pDC, 21, CPoint( pItem->get_x() + 40 + 15 + 3, pItem->get_y() + i32ItemHeight/2 - 4 ), CSize( 12, 12 ), CPoint( 0, 0 ), ILD_NORMAL, SRCCOPY, CLR_NONE, CLR_DEFAULT );
					}
					break;
				}

				if( pItem->get_flags() & ITEM_ANIMATED ) {
					// draw some marks to indicate this parameter is animated
					m_rImageList.DrawIndirect( pDC, 15, CPoint( pItem->get_x() + 40 + 3, pItem->get_y() + i32ItemHeight/2 - 4 ), CSize( 12, 12 ), CPoint( 0, 0 ), ILD_NORMAL, SRCCOPY, CLR_NONE, CLR_DEFAULT );
				}
				else {
					// not animated
					if( pItem->get_flags() & ITEM_ANIMATABLE ) {
						// yes, this parameter can be animated
						m_rImageList.DrawIndirect( pDC, 14, CPoint( pItem->get_x() + 40 + 3, pItem->get_y() + i32ItemHeight/2 - 4 ), CSize( 12, 12 ), CPoint( 0, 0 ), ILD_NORMAL, SRCCOPY, CLR_NONE, CLR_DEFAULT );
					}
				}

				if( pItem->get_flags() & ITEM_EXPANDABLE || pItem->get_flags() & ITEM_ANIMATED ) {
					// draw the expansion state
					CPoint	rPt( pItem->get_x() - 15 + 40, pItem->get_y() + i32ItemHeight/2 - 5 );
					if( (pItem->get_flags() & ITEM_EXPANDED) )
						m_rImageList.DrawIndirect( pDC, 0, rPt, CSize( 12, 12 ), CPoint( 0, 0 ), ILD_NORMAL, SRCCOPY, CLR_NONE, CLR_DEFAULT );
					else
						m_rImageList.DrawIndirect( pDC, 1, rPt, CSize( 12, 12 ), CPoint( 0, 0 ), ILD_NORMAL, SRCCOPY, CLR_NONE, CLR_DEFAULT );
				}

			}
		}


		// draw next key arrow for selected items which has keys
		if( bHasKeyNextButton ) {
			pDC->FillSolidRect( m_rListRect.right - 13, pItem->get_y(), 12, i32ItemHeight, rBgCol );
			pDC->Draw3dRect( m_rListRect.right - 13, pItem->get_y(), 12, i32ItemHeight, rHiCol, rShCol );
			if( bHasKeys )
				// arrow
				m_rImageList.DrawIndirect( pDC, 19, CPoint( m_rListRect.right - 13 + 4, pItem->get_y() + i32ItemHeight / 2 - 3), CSize( 5, 5 ), CPoint( 0, 0 ), ILD_NORMAL, SRCCOPY, CLR_NONE, CLR_DEFAULT );
			else
				// point
				m_rImageList.DrawIndirect( pDC, 12, CPoint( m_rListRect.right - 13 + 4, pItem->get_y() + i32ItemHeight / 2 - 3), CSize( 5, 5 ), CPoint( 0, 0 ), ILD_NORMAL, SRCCOPY, CLR_NONE, CLR_DEFAULT );
		}


		// draw arrow
		CPoint	rPt( pItem->get_x() - 15 + 40, pItem->get_y() + i32ItemHeight/2 - 5 );

		if( !(pItem->get_type() == SCENEITEM_PARAMETER) ) {
			if( pItem->get_flags() & ITEM_EXPANDED )
				m_rImageList.DrawIndirect( pDC, 0, rPt, CSize( 12, 12 ), CPoint( 0, 0 ), ILD_NORMAL, SRCCOPY, CLR_NONE, CLR_DEFAULT );
			else
				m_rImageList.DrawIndirect( pDC, 1, rPt, CSize( 12, 12 ), CPoint( 0, 0 ), ILD_NORMAL, SRCCOPY, CLR_NONE, CLR_DEFAULT );
		}

		if( pItem->get_type() == SCENEITEM_LAYER || pItem->get_type() == SCENEITEM_EFFECT ) {
			// draw visibility flags
			pDC->Draw3dRect( 3, pItem->get_y() + i32ItemHeight/2 - 6, 14, 13, ::GetSysColor( COLOR_3DSHADOW ), ::GetSysColor( COLOR_3DHILIGHT ) );
			if( pItem->get_flags() & ITEM_VISIBLE )
				m_rImageList.DrawIndirect( pDC, 10, CPoint( 5, pItem->get_y() + i32ItemHeight/2 - 4 ), CSize( 12, 12 ), CPoint( 0, 0 ), ILD_NORMAL, SRCCOPY, CLR_NONE, CLR_DEFAULT );

			// draw locked flag
			pDC->Draw3dRect( 23, pItem->get_y() + i32ItemHeight/2 - 6, 14, 13, ::GetSysColor( COLOR_3DSHADOW ), ::GetSysColor( COLOR_3DHILIGHT ) );
			if( pItem->get_flags() & ITEM_LOCKED )
				m_rImageList.DrawIndirect( pDC, 11, CPoint( 25, pItem->get_y() + i32ItemHeight/2 - 4 ), CSize( 12, 12 ), CPoint( 0, 0 ), ILD_NORMAL, SRCCOPY, CLR_NONE, CLR_DEFAULT );
		}
	}

	// draw end-of-the-layers -line
	// draw widest possible line above layers
	if( m_rSceneItemList.get_sceneitem_count() ) {
		SceneItemC*	pItem = m_rSceneItemList.get_sceneitem( m_rSceneItemList.get_sceneitem_count() - 1 );
		pDC->MoveTo( 0, pItem->get_y() + pItem->get_height() );
		pDC->LineTo( rInner.Width(), pItem->get_y() + pItem->get_height() );
	}


	// select old objects
	pDC->SelectObject( pOldFont );
	pDC->SelectObject( pOldPen );

	// restore old clip region
	pDC->SelectClipRgn( &rOldClipRgn );
	rOldClipRgn.DeleteObject();
	rRegion.DeleteObject();

	// return viewport origin
	pDC->SetViewportOrg( rOldOrigin );


	// eye and lock lines
	pDC->MoveTo( 0, rInner.top ); pDC->LineTo( 0, rInner.bottom );
	pDC->MoveTo( 20, rInner.top ); pDC->LineTo( 20, rInner.bottom );
	pDC->MoveTo( 40, rInner.top ); pDC->LineTo( 40, rInner.bottom );

	// parameter value separator
	pDC->MoveTo( rInner.right - m_i32LayerValueSize, rInner.top ); pDC->LineTo( rInner.right - m_i32LayerValueSize, rInner.bottom );

	// layer list separator
	pDC->MoveTo( rInner.right, rInner.top ); pDC->LineTo( rInner.right, rInner.bottom );
}



void CTimeLineBar::OnPaint() 
{
	CPaintDC dc(this); // device context for painting

	if( !m_bInitialised || !m_pDoc )
		return;

	UpdateSceneTab();

	// start flicker free drawing
	CRect	rRect;
    CDC		rMemDC;
    CBitmap	rBitmap;

	GetClientRect( rRect );

	FindVisibleRange();

	// create memory DC
    rMemDC.CreateCompatibleDC( &dc );
    rBitmap.CreateCompatibleBitmap( &dc, rRect.Width(), rRect.Height() );
    CBitmap* pOldBm = rMemDC.SelectObject( &rBitmap );

	// clear background

	int32	i32VScrollSize = ::GetSystemMetrics( SM_CXVSCROLL );
	int32	i32HScrollSize = ::GetSystemMetrics( SM_CYHSCROLL );
	int32	i32BottomSize = i32HScrollSize;

	if( i32BottomSize < 20 )
		i32BottomSize = 20;

	// area around play console buttons
	rMemDC.FillSolidRect( 0, 0, rRect.Width(), m_rListRect.top, ::GetSysColor( COLOR_BTNFACE ) );
	// area under layer list (including the zoom buttons)
	rMemDC.FillSolidRect( 0, m_rListRect.bottom, m_rListRect.Width() + 82, i32BottomSize, ::GetSysColor( COLOR_BTNFACE ) );
	// the empty lower-right corner
	rMemDC.FillSolidRect( m_rTimelineRect.right, m_rTimelineRect.bottom, i32HScrollSize, i32BottomSize, ::GetSysColor( COLOR_BTNFACE ) );
	// the possibly empty are under the timeline scrollbar
	rMemDC.FillSolidRect( m_rTimelineRect.left, rRect.bottom - (i32BottomSize - i32HScrollSize), m_rTimelineRect.Width(), i32BottomSize - i32HScrollSize, ::GetSysColor( COLOR_BTNFACE ) );

	CRect	rArea;
	rArea = m_rListRect;
	rArea.left = m_rListRect.left;
	rArea.right = m_rTimelineRect.right;

	// draw edge
	rMemDC.Draw3dRect( rArea, ::GetSysColor( COLOR_3DSHADOW ), ::GetSysColor( COLOR_3DHILIGHT ) );
	rArea.DeflateRect( 1, 1, 1, 1 );


	// time ruler
	DrawTimeRuler( &rMemDC );

	// draw waveform
	DrawWaveform( &rMemDC );

	// draw layerlist
	DrawSceneList( &rMemDC );
	// draw timeline view
	DrawSceneTimeline( &rMemDC );

	// draw time cursor
	DrawTimeCursor( &rMemDC );

	// finish drawing
    dc.BitBlt( 0, 0, rRect.Width(), rRect.Height(), &rMemDC, 0, 0, SRCCOPY );
    ReleaseDC( &dc );
    rMemDC.SelectObject( pOldBm );
    rBitmap.DeleteObject();
    rMemDC.DeleteDC();

	// if this flag is set draw the box selection.
	// most likely this happens when we call invalidate from a timer
	// event and we are box selecting... yes that's it.
	if( m_bScrollingBoxSelect )
		DrawBoxSelect();

}


// hit test a controller
int32
CTimeLineBar::HitTestController( CPoint rPt, SceneItemC* pItem, KeyC** pOutKey, PajaTypes::uint32* ui32HitChannel, bool bForceSample )
{
	int32			i32TimeOffset = 0;

	if( !(pItem->get_flags() & ITEM_ANIMATABLE) )
		return HIT_CONT_NONE;

	ParamI*			pParam = pItem->get_parameter();
	if( !pParam )
		return HIT_CONT_NONE;

	if( pOutKey )
		*pOutKey = 0;

	if( ui32HitChannel )
		*ui32HitChannel = -1;

	i32TimeOffset += pItem->get_layer()->get_timesegment()->get_segment_start();
	i32TimeOffset += pItem->get_effect()->get_timesegment()->get_segment_start();

	uint32	i;
	int32	i32PtTime = m_pDoc->FrameToTime( (rPt.x - m_rTimelineRect.left) / m_i32FrameWidthInPixels ) + m_i32ViewTimeStart - i32TimeOffset;
	int32	i32NormItemHeight = m_rSceneItemList.get_item_height();
	int32	i32ItemHeight = pItem->get_height() - i32NormItemHeight - 10;

	rPt.y -= m_rListRect.top;
	rPt.y += GetListPos();
	rPt.y -= 1;
	rPt.y -= pItem->get_y() + i32NormItemHeight + 5;

	ControllerC*	pCont = pParam->get_controller();

	if( !pItem->get_controller_sampledata()->get_sample_count() || bForceSample )
		SampleController( pItem );

	// test keys
	for( i = 0; i < pCont->get_key_count(); i++ ) {
		KeyC*	pKey = pCont->get_key( i );
		if( i32PtTime >= pKey->get_time() && i32PtTime < (pKey->get_time() + m_pDoc->GetFrameSizeInTicks()) ) {

			if( pItem->get_flags() & ITEM_EXPANDED && i32ItemHeight >= 10 &&
				rPt.y >= -5 && pItem->get_controller_sampledata() ) {

				float32		f32Min, f32Max;
				float32		f32Scale = 1.0f;
				f32Min = pItem->get_controller_sampledata()->get_min();
				f32Max = pItem->get_controller_sampledata()->get_max();

				if( f32Min != f32Max )
					f32Scale = (1.0f / (f32Max - f32Min)) * (float)i32ItemHeight;

				if( pKey->get_flags() & KEY_FILE ) {
				}
				else {
					float32	f32Value[KEY_MAXCHANNELS];
					((FloatKeyC*)pKey)->get_value( f32Value );

					for( uint32 j = 0; j < pCont->get_num_channels(); j++ ) {
						int32	i32Y = i32ItemHeight - (int32)((f32Value[j] - f32Min) * f32Scale);
						if( rPt.y >= (i32Y - 3) && rPt.y < (i32Y + 3) ) {
							if( pOutKey )
								*pOutKey = pKey;
							if( ui32HitChannel )
								*ui32HitChannel = j;
							return HIT_CONT_KEY_VALUE;
						}
					}
				}					
			}
			else {
				if( pOutKey )
					*pOutKey = pKey;
				return HIT_CONT_KEY_TIME;
			}
		}
	}

	return HIT_CONT_SOMEWHERE;
}



// hit test a time segment
int32
CTimeLineBar::HitTestTimeSegment( CPoint rPt, SceneItemC* pItem, KeyC** pKey )
{
	TimeSegmentC*	pSegment;
	int32			i32TimeOffset;

	if( pKey )
		*pKey = 0;

	if( pItem->get_type() == SCENEITEM_LAYER ) {
		pSegment = pItem->get_layer()->get_timesegment();
		i32TimeOffset = 0;
	}
	else if( pItem->get_type() == SCENEITEM_EFFECT ) {
		pSegment = pItem->get_effect()->get_timesegment();
		i32TimeOffset = pItem->get_layer()->get_timesegment()->get_segment_start();
	}
	else
		return HIT_TSEG_NONE;

	// test keys
	for( uint32 i = 0; i < pSegment->get_key_count(); i++ ) {
		int32	i32Time = pSegment->get_key_time( i );
		int32	i32PosX = m_pDoc->TimeToFrame( i32Time + i32TimeOffset - m_i32ViewTimeStart) * m_i32FrameWidthInPixels + m_rTimelineRect.left;

		if( (i & 1) == 0 )
			i32PosX -= 9;
		if( rPt.x >= i32PosX && rPt.x < (i32PosX + 9) ) {
			if( pKey )
				*pKey = pSegment->get_key( i );
			return HIT_TSEG_KEY;
		}
	}

	int32	i32PtTime = m_pDoc->FrameToTime( (rPt.x - m_rTimelineRect.left) / m_i32FrameWidthInPixels ) - i32TimeOffset + m_i32ViewTimeStart;

	if( pSegment->is_visible( i32PtTime ) )
		return HIT_TSEG_BAR;

	return HIT_TSEG_SOMEWHERE;
}

int32
CTimeLineBar::HitTestListHeader( CPoint rPt )
{
	CRect	rHeaderRect;
	rHeaderRect = m_rListRect;
	rHeaderRect.bottom = rHeaderRect.top;
	rHeaderRect.top -= 22;

	if( rHeaderRect.PtInRect( rPt ) ) {
		// check if we hit the value separator
		if( rPt.x >= (m_rListRect.right - m_i32LayerValueSize - 5) &&
			rPt.x < (m_rListRect.right - m_i32LayerValueSize) )
			return HIT_LIST_VALUESEPARATOR;

		// check if we hit the layerlist separator
		if( rPt.x >= (m_rListRect.right - 5) &&
			rPt.x < m_rListRect.right )
			return HIT_LIST_SEPARATOR;
	}

	return HIT_LIST_NONE;
}


int32
CTimeLineBar::HitTestSceneList( CPoint rPt, SceneItemC** pHitItem )
{
	if( pHitItem )
		*pHitItem = 0;

	bool	bInsideLayerList = m_rListRect.PtInRect( rPt ) == TRUE;
	bool	bInsideTimeline = m_rTimelineRect.PtInRect( rPt ) == TRUE;

	if( !bInsideLayerList && !bInsideTimeline )
		return HIT_LIST_NONE;

	rPt.y -= m_rListRect.top;
	rPt.y += GetListPos();
	rPt.y -= 1;

	for( int32 i = m_i32FirstVisible; i < m_i32LastVisible; i++ ) {
		SceneItemC*	pItem = m_rSceneItemList.get_sceneitem( i );
		if( rPt.y >= pItem->get_y() &&  rPt.y < pItem->get_y() + pItem->get_height() ) {
			// hit found
			if( pHitItem )
				*pHitItem = pItem;
			if( rPt.x < 20 ) {
				return HIT_LIST_EYE;
			}
			else if( rPt.x < 40 ) {
				return HIT_LIST_LOCK;
			}
			else if( rPt.x < pItem->get_x() - 20 + 40 ) {
				return HIT_LIST_NONE;
			}
			else if( rPt.x < pItem->get_x() + 40 ) {
				if( (pItem->get_type() == SCENEITEM_PARAMETER) && !(pItem->get_flags() & ITEM_ANIMATED) && !(pItem->get_flags() & ITEM_EXPANDABLE) )
					return HIT_LIST_NONE;
				else
					return HIT_LIST_ARROW;
			}
			else if( rPt.x > (m_rListRect.right - m_i32LayerValueSize) && rPt.x < m_rListRect.right ) {

				bool	bHasKeyNextButton = false;

				if( (pItem->get_flags() & ITEM_SELECTED) &&
					(pItem->get_type() == SCENEITEM_LAYER || pItem->get_type() == SCENEITEM_EFFECT || (pItem->get_flags() & ITEM_ANIMATED)))
					bHasKeyNextButton = true;

				if( rPt.x > (m_rListRect.right - 12) &&bHasKeyNextButton )
					return HIT_LIST_NEXT_KEY;

				if( pItem->get_type() == SCENEITEM_PARAMETER ) {
					ParamI*	pParam = pItem->get_parameter();
					if( pParam ) {

						int32	i32LeftX = m_rListRect.right - m_i32LayerValueSize;
						int32	i32RightX = m_rListRect.right;

						if( bHasKeyNextButton )
							i32RightX -= 13;

						if( pParam->get_type() == PARAM_TYPE_FILE ) {
							if( rPt.x > (i32RightX - 11) && rPt.x < i32RightX )
								return HIT_LIST_FILECOMBO;
						}
						else if( pParam->get_type() == PARAM_TYPE_INT ) {
							if( rPt.x > (i32RightX - 11) && rPt.x < i32RightX ) {
								if( pParam->get_style() == PARAM_STYLE_COMBOBOX )
									return HIT_LIST_INTCOMBO;
								else
									return HIT_LIST_SPINNERX;
							}
						}
						else if( pParam->get_type() == PARAM_TYPE_FLOAT ) {
							if( rPt.x > (i32RightX - 11) && rPt.x < i32RightX )
								return HIT_LIST_SPINNERX;
						}
						else if( pParam->get_type() == PARAM_TYPE_VECTOR2 ) {
							int32	i32CompWidth = (i32RightX - i32LeftX) / 2;
							int32	i32CompRightX = i32LeftX + i32CompWidth;

							if( rPt.x > (i32CompRightX - 11) && rPt.x < i32CompRightX )
								return HIT_LIST_SPINNERX;

							i32CompRightX += i32CompWidth;

							if( rPt.x > (i32CompRightX - 11) && rPt.x < i32CompRightX )
								return HIT_LIST_SPINNERY;
						}
						else if( pParam->get_type() == PARAM_TYPE_VECTOR3 ) {
							int32	i32CompWidth = (i32RightX - i32LeftX) / 2;
							int32	i32CompRightX = i32LeftX + i32CompWidth;

							if( rPt.x > (i32CompRightX - 11) && rPt.x < i32CompRightX )
								return HIT_LIST_SPINNERX;

							i32CompRightX += i32CompWidth;

							if( rPt.x > (i32CompRightX - 11) && rPt.x < i32CompRightX )
								return HIT_LIST_SPINNERY;

							i32CompRightX += i32CompWidth;

							if( rPt.x > (i32CompRightX - 11) && rPt.x < i32CompRightX )
								return HIT_LIST_SPINNERZ;
						}
					}
				}

				return HIT_LIST_VALUE;
			}

			if( pItem->get_type() == SCENEITEM_PARAMETER ) {
				// special handling for parameters
				if( rPt.x < pItem->get_x() + 40 ) {
					return HIT_LIST_ARROW;
				}
				else if( (pItem->get_flags() & ITEM_ANIMATABLE) && (rPt.x < pItem->get_x() + 40 + 20) ) {
					return HIT_LIST_ANIM;
				}
				else {
					if( pItem->get_type() == SCENEITEM_PARAMETER && pItem->get_flags() & ITEM_EXPANDED ) {
						if( rPt.y >= pItem->get_y() + pItem->get_height() - 4 )
							return HIT_LIST_ITEM | HIT_LIST_SIZEPARAM;
					}
					return HIT_LIST_ITEM;
				}
			}
			else {
				// others will be handled here
				return HIT_LIST_ITEM;
			}
		}
	}

	return HIT_LIST_NONE;
}


void CTimeLineBar::OnLButtonDown( UINT nFlags, CPoint point )
{
	m_bMouseMoved = false;

	// If we are editing a label, just kill the focus.
	if( m_pEditCtrl ) {
		OnKillfocusEditLabel();
		return;
	}

	// if this window doesnt have focus, set it
	if( GetFocus() != this )
		SetFocus();

	bool	bRes = false;

	bool	bInTimeline = false, bInLayerList = false;
	if( m_rListRect.PtInRect( point ) ) {
		bInLayerList = true;
		m_pDoc->SetEditFocus( FOCUS_LAYERLIST );
	}
	if( m_rTimelineRect.PtInRect( point ) ) {
		bInTimeline = true;
		m_pDoc->SetEditFocus( FOCUS_TIMELINE );
	}

	bool	bSpacePressed = (::GetAsyncKeyState( VK_SPACE ) & 0x8000 ) != 0;

	// if spacebar is pressed the panorate
	if( bSpacePressed && (bInTimeline || bInLayerList) ) {
		SetCapture();
		m_eTrackAction = TRACKING_PAN;
		m_rTrackOrigPt = point;
		m_i32TrackOrigTime = m_i32ViewTimeStart;
		m_i32TrackOrigPos = GetListPos();
		return;
	}

	int32	i32Hit = HitTestListHeader( point );

	if( i32Hit == HIT_LIST_VALUESEPARATOR ) {
		SetCapture();
		m_eTrackAction = TRACKING_VALUESEPARATOR;
		m_i32TrackOrigPos = m_i32LayerValueSize;
		m_rTrackOrigPt = point;
		return;
	}
	else if( i32Hit == HIT_LIST_SEPARATOR ) {
		SetCapture();
		m_eTrackAction = TRACKING_SEPARATOR;
		m_rOrigListRect = m_rListRect;
		m_rTrackOrigPt = point;
		return;
	}

	bRes = OnSceneLButtonDown( nFlags, point );

	if( !bRes )
		baseCMyBar::OnLButtonDown(nFlags, point);
}

void CTimeLineBar::OnLButtonDblClk(UINT nFlags, CPoint point) 
{
	m_bMouseMoved = false;

	// if this window doesnt have focus, set it
	if( GetFocus() != this )
		SetFocus();

	bool	bRes = false;

	bool	bInTimeline = false, bInLayerList = false;
	if( m_rListRect.PtInRect( point ) ) {
		bInLayerList = true;
		m_pDoc->SetEditFocus( FOCUS_LAYERLIST );
	}
	if( m_rTimelineRect.PtInRect( point ) ) {
		bInTimeline = true;
		m_pDoc->SetEditFocus( FOCUS_TIMELINE );
	}

	bool	bSpacePressed = (::GetAsyncKeyState( VK_SPACE ) & 0x8000 ) != 0;

	// if spacebar is pressed the panorate
	if( bSpacePressed && (bInTimeline || bInLayerList) ) {
		SetCapture();
		m_eTrackAction = TRACKING_PAN;
		m_rTrackOrigPt = point;
		m_i32TrackOrigTime = m_i32ViewTimeStart;
		m_i32TrackOrigPos = GetListPos();
		return;
	}

	int32	i32Hit = HitTestListHeader( point );

	if( i32Hit == HIT_LIST_VALUESEPARATOR ) {
		SetCapture();
		m_eTrackAction = TRACKING_VALUESEPARATOR;
		m_i32TrackOrigPos = m_i32LayerValueSize;
		m_rTrackOrigPt = point;
		return;
	}
	else if( i32Hit == HIT_LIST_SEPARATOR ) {
		SetCapture();
		m_eTrackAction = TRACKING_SEPARATOR;
		m_rOrigListRect = m_rListRect;
		m_rTrackOrigPt = point;
		return;
	}

	bRes = OnSceneLButtonDblClk( nFlags, point ) ;

	if( !bRes )
		baseCMyBar::OnLButtonDblClk( nFlags, point );
}

void CTimeLineBar::OnLButtonUp(UINT nFlags, CPoint point) 
{
	if( GetCapture() == this )
		ReleaseCapture();

	if( m_ui32ScrollTimeID ) {
		KillTimer( m_ui32ScrollTimeID );
		m_ui32ScrollTimeID = 0;
	}

	bool	bRes = false;

	if( m_eTrackAction != TRACKING_NONE ) {

		bRes = OnSceneLButtonUp( nFlags, point );

		if( m_eTrackAction ) {
			m_pTrackItem = 0;
			m_eTrackAction = TRACKING_NONE;
		}

		m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
	}

	if( !bRes )
		baseCMyBar::OnLButtonUp(nFlags, point);
}

void CTimeLineBar::OnRButtonDown( UINT nFlags, CPoint rPoint ) 
{
	// If we are editing a label, just kill the focus.
	if( m_pEditCtrl ) {
		OnKillfocusEditLabel();
		return;
	}

	// if this window doesnt have focus, set it
	if( GetFocus() != this )
		SetFocus();

	::SetCursor( ::LoadCursor( NULL, IDC_ARROW ) );

	bool	bRes = false;

	if( m_rWaveformRect.PtInRect( rPoint ) ) {

		// Check if we hit a marker
		int32	i32Index = -1;
		int32	i32Time = 0;
		HitTestMarker( rPoint, &i32Index, &i32Time );

		CFlatPopupMenu	rMarkerMenu;
		CFlatPopupMenu	rGotoMenu;

		rMarkerMenu.SetFont( "Arial" );
		rGotoMenu.SetFont( "Arial" );

		rMarkerMenu.SetFontSize( 8 );
		rGotoMenu.SetFontSize( 8 );

		rMarkerMenu.Create( AfxGetInstanceHandle(), IDB_MARKERRIGHT );
		rGotoMenu.Create( AfxGetInstanceHandle() );

		// Marker menu
		rMarkerMenu.AppendItem( 0, "Add ...", 101, 0 );
		rMarkerMenu.AppendItem( i32Index == -1 ? CFlatPopupMenu::itemGrayed | CFlatPopupMenu::itemNotSelectable : 0, "Delete", 102, 1 );
		rMarkerMenu.AppendItem( i32Index == -1 ? CFlatPopupMenu::itemGrayed | CFlatPopupMenu::itemNotSelectable : 0, "Edit ...", 103, 2 );

		bool	bHasMarkers = false;

		SceneC*	pScene = 0;

		// Go to menu
		pScene = m_pDoc->GetCurrentScene();
		for( uint32 i = 0; i < pScene->get_marker_count(); i++ ) {
			rGotoMenu.AppendItem( 0, pScene->get_marker_name( i ), i | 0x10000000 );
		}
		if( pScene->get_marker_count() )
			bHasMarkers = true;

		CFlatPopupMenu	rMenu;
		CFlatPopupMenu	rChannelMenu;
		CFlatPopupMenu	rViewMenu;

		rMenu.SetFont( "Arial" );
		rMenu.SetFontSize( 8 );
		rChannelMenu.SetFont( "Arial" );
		rChannelMenu.SetFontSize( 8 );
		rViewMenu.SetFont( "Arial" );
		rViewMenu.SetFontSize( 8 );

		rMenu.Create( AfxGetInstanceHandle(), IDB_WAVERIGHT );
		rViewMenu.Create( AfxGetInstanceHandle(), IDB_WAVERIGHT );
		rChannelMenu.Create( AfxGetInstanceHandle(), IDB_WAVERIGHT );

		uint32	ui32Flags = 0;

		if( !m_pDoc->GetMusicDataStereo() )
			ui32Flags = CFlatPopupMenu::itemGrayed | CFlatPopupMenu::itemNotSelectable;

		int32	i32View = m_pDoc->GetMusicDataView();

		rChannelMenu.AppendItem( i32View == MUSIC_VIEW_MONO ? CFlatPopupMenu::itemBold : 0, "Mono", 1, 0 );
		rChannelMenu.AppendItem( ui32Flags | (i32View == MUSIC_VIEW_LEFT ? CFlatPopupMenu::itemBold : 0), "Left", 2, 1 );
		rChannelMenu.AppendItem( ui32Flags | (i32View == MUSIC_VIEW_RIGHT ? CFlatPopupMenu::itemBold : 0), "Right", 3, 2 );
		rChannelMenu.AppendItem( ui32Flags | (i32View == MUSIC_VIEW_STEREO ? CFlatPopupMenu::itemBold : 0), "Stereo", 4, 3 );

		rChannelMenu.AppendItem( CFlatPopupMenu::itemSeparator, NULL, 0 );

		rChannelMenu.AppendItem( ui32Flags | (i32View == MUSIC_VIEW_BAND_0 ? CFlatPopupMenu::itemBold : 0), "Band 0", 12 );
		rChannelMenu.AppendItem( ui32Flags | (i32View == MUSIC_VIEW_BAND_1 ? CFlatPopupMenu::itemBold : 0), "Band 1", 13 );
		rChannelMenu.AppendItem( ui32Flags | (i32View == MUSIC_VIEW_BAND_2 ? CFlatPopupMenu::itemBold : 0), "Band 2", 14 );
		rChannelMenu.AppendItem( ui32Flags | (i32View == MUSIC_VIEW_BAND_3 ? CFlatPopupMenu::itemBold : 0), "Band 3", 15 );
		rChannelMenu.AppendItem( ui32Flags | (i32View == MUSIC_VIEW_BAND_4 ? CFlatPopupMenu::itemBold : 0), "Band 4", 16 );
		rChannelMenu.AppendItem( ui32Flags | (i32View == MUSIC_VIEW_BAND_5 ? CFlatPopupMenu::itemBold : 0), "Band 5", 17 );
		rChannelMenu.AppendItem( ui32Flags | (i32View == MUSIC_VIEW_BAND_6 ? CFlatPopupMenu::itemBold : 0), "Band 6", 18 );
		rChannelMenu.AppendItem( ui32Flags | (i32View == MUSIC_VIEW_BAND_7 ? CFlatPopupMenu::itemBold : 0), "Band 7", 19 );

		rViewMenu.AppendItem( m_i32WaveformHeight == 32 ? CFlatPopupMenu::itemBold : 0, "Small View", 5, 4 );
		rViewMenu.AppendItem( m_i32WaveformHeight == 48 ? CFlatPopupMenu::itemBold : 0, "Normal View", 6, 5 );
		rViewMenu.AppendItem( m_i32WaveformHeight == 64 ? CFlatPopupMenu::itemBold : 0, "Large View", 7, 6 );

		rMenu.AppendItem( m_pDoc->GetCurrentScene() == m_pDoc->GetMainScene() ? CFlatPopupMenu::itemGrayed | CFlatPopupMenu::itemNotSelectable : 0, "Time Ruler Start...", 11, 11 );
		rMenu.AppendItem( 0, "Show/Hide Waveform", 8, m_pDoc->GetMusicShowWaveform() ? 7 : 8 );
		rMenu.AppendPopup( 0, "Channel", rChannelMenu, 9 );
		rMenu.AppendPopup( 0, "View", rViewMenu, 10 );

		rMenu.AppendItem( CFlatPopupMenu::itemSeparator, NULL, 0 );

		rMenu.AppendPopup( 0, "Marker", rMarkerMenu, 12 );
		rMenu.AppendPopup( bHasMarkers ? 0 : CFlatPopupMenu::itemGrayed | CFlatPopupMenu::itemNotSelectable, "Go To", rGotoMenu, 13 );
		rMenu.AppendItem( 0, "Show/Hide Markers", 9, m_pDoc->GetShowMarkers() ? 7 : 8 );


		CPoint	rScreenPt = rPoint;
		ClientToScreen( &rScreenPt );
		int32	i32Id = rMenu.Track( rScreenPt.x, rScreenPt.y, NULL, true );

		if( i32Id == 1 ) {
			m_pDoc->SetMusicDataView( MUSIC_VIEW_MONO );
		}
		else if( i32Id == 2 ) {
			m_pDoc->SetMusicDataView( MUSIC_VIEW_LEFT );
		}
		else if( i32Id == 3 ) {
			m_pDoc->SetMusicDataView( MUSIC_VIEW_RIGHT );
		}
		else if( i32Id == 4 ) {
			m_pDoc->SetMusicDataView( MUSIC_VIEW_STEREO );
		}

		else if( i32Id == 12 ) {
			m_pDoc->SetMusicDataView( MUSIC_VIEW_BAND_0 );
		}
		else if( i32Id == 13 ) {
			m_pDoc->SetMusicDataView( MUSIC_VIEW_BAND_1 );
		}
		else if( i32Id == 14 ) {
			m_pDoc->SetMusicDataView( MUSIC_VIEW_BAND_2 );
		}
		else if( i32Id == 15 ) {
			m_pDoc->SetMusicDataView( MUSIC_VIEW_BAND_3 );
		}
		else if( i32Id == 16 ) {
			m_pDoc->SetMusicDataView( MUSIC_VIEW_BAND_4 );
		}
		else if( i32Id == 17 ) {
			m_pDoc->SetMusicDataView( MUSIC_VIEW_BAND_5 );
		}
		else if( i32Id == 18 ) {
			m_pDoc->SetMusicDataView( MUSIC_VIEW_BAND_6 );
		}
		else if( i32Id == 19 ) {
			m_pDoc->SetMusicDataView( MUSIC_VIEW_BAND_7 );
		}

		else if( i32Id == 5 ) {
			m_i32WaveformHeight = 32;
			AdjustControls();
		}
		else if( i32Id == 6 ) {
			m_i32WaveformHeight = 48;
			AdjustControls();
		}
		else if( i32Id == 7 ) {
			m_i32WaveformHeight = 64;
			AdjustControls();
		}
		else if( i32Id == 8 ) {
			m_pDoc->SetMusicShowWaveform( !m_pDoc->GetMusicShowWaveform() );
		}
		else if( i32Id == 9 ) {
			m_pDoc->SetShowMarkers( !m_pDoc->GetShowMarkers() );
		}
		else if( i32Id == 11 ) {
			CMusicStartTimeDlg	rDlg;

			rDlg.m_i32StartTime = m_pDoc->TimeToFrame( m_pDoc->GetCurrentScene()->get_music_start_time() );
			rDlg.m_i32BeatsPerMin = m_pDoc->GetBeatsPerMin();
			rDlg.m_i32QNotesPerBeat = m_pDoc->GetQNotesPerBeat();
			rDlg.m_i32EditAccuracy = m_pDoc->GetEditAccuracy();

			SceneC*	pScene = m_pDoc->GetMainScene();
			if( pScene ) {
				for( uint32 i = 0; i < pScene->get_marker_count(); i++ ) {
					rDlg.AddMarker( pScene->get_marker_name( i ), m_pDoc->TimeToFrame( pScene->get_marker_time( i ) ) );
				}
			}

			if( rDlg.DoModal() == IDOK ) {
				m_pDoc->GetCurrentScene()->set_music_start_time( m_pDoc->FrameToTime( rDlg.m_i32StartTime ) );
			}
		}
		else if( i32Id == 101 ) {
			int32	i32Time = m_pDoc->FrameToTime( (rPoint.x - m_rTimelineRect.left) / m_i32FrameWidthInPixels ) + m_i32ViewTimeStart;
			m_pDoc->MarkerAdd( i32Time );
			Invalidate( FALSE );
		}
		else if( i32Id == 102 ) {
			m_pDoc->MarkerDelete( i32Index );
			Invalidate( FALSE );
		}
		else if( i32Id == 103 ) {
			m_pDoc->MarkerTypeIn( i32Index );
			Invalidate( FALSE );
		}
		else if( (i32Id & 0x10000000) == 0x10000000 ) {
			i32Id &= 0x0fffffff;

			uint32		ui32NumValues = m_rTimelineRect.Width() / m_i32FrameWidthInPixels - 1;
			int32		i32MaxTime = m_i32ViewTimeStart + m_pDoc->FrameToTime( ui32NumValues );

			SetTimecursor( pScene->get_marker_time( i32Id ) );

			if( GetTimecursor() < m_i32ViewTimeStart || GetTimecursor() > i32MaxTime )
				SetTimeStart( GetTimecursor() - (i32MaxTime - m_i32ViewTimeStart) / 4 );

			CDemopajaDoc*	pDoc = GetDoc();
			CDemopajaApp*	pApp = (CDemopajaApp*)AfxGetApp();
			CDemopajaView*	pView = 0;
			// get the view
			POSITION rPos = pDoc->GetFirstViewPosition();
			if( rPos )
				pView = (CDemopajaView*)pDoc->GetNextView( rPos );

			if( pView )
				pView->SetTimePos( GetTimecursor() );
			
			m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
		}

		Invalidate( FALSE );

		bRes = true;
	}
	else {
		bRes = OnSceneRButtonDown( nFlags, rPoint );
	}

	// if this window doesnt have focus, set it
	// this may have happend if popup menu has been launched
	if( !m_pEditCtrl && GetFocus() != this )
		SetFocus();

	if( !bRes )
		baseCMyBar::OnRButtonDown( nFlags, rPoint );
}

void CTimeLineBar::OnMouseMove( UINT nFlags, CPoint point )
{
	bool	bRes = false;

	if( point != m_rTrackOrigPt ) {
		m_bMouseMoved = true;
	}

	if( m_eTrackAction == TRACKING_VERTRULER ) {
		SetTimeStart( m_i32TrackOrigTime + m_pDoc->FrameToTime( (m_rTrackOrigPt.x - point.x) / m_i32FrameWidthInPixels ) );
		m_rSceneItemList.remove_cont_sample_data();	// invalidate controllerdata
		Invalidate( FALSE );
		bRes = true;
	}
	else if( m_eTrackAction == TRACKING_TIMECURSOR ) {
		if( point.x < m_rTimelineRect.left + m_i32FrameWidthInPixels ) {
			if( !m_ui32ScrollTimeID )
				m_ui32ScrollTimeID = SetTimer( TIMER_TIMERCURSOR_LEFT, 50, NULL );
		}
		else if( point.x > m_rTimelineRect.right - m_i32FrameWidthInPixels ) {
			if( !m_ui32ScrollTimeID )
				m_ui32ScrollTimeID = SetTimer( TIMER_TIMERCURSOR_RIGHT, 50, NULL );
		}
		else {
			if( m_ui32ScrollTimeID ) {
				KillTimer( m_ui32ScrollTimeID );
				m_ui32ScrollTimeID = 0;
			}
			m_i32OldTimeCursor = GetTimecursor();
			SetTimecursor( m_pDoc->FrameToTime( (point.x - m_rTimelineRect.left) / m_i32FrameWidthInPixels ) + m_i32ViewTimeStart );
//			RedrawTimecursor();
			m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
		}
		bRes = true;
	}
	else if( m_eTrackAction == TRACKING_VALUESEPARATOR ) {
		m_i32LayerValueSize = m_i32TrackOrigPos + (m_rTrackOrigPt.x - point.x);
		if( m_i32LayerValueSize < 12 )
			m_i32LayerValueSize = 12;
		if( m_i32LayerValueSize > (m_rListRect.Width() - 150) )
			m_i32LayerValueSize = (m_rListRect.Width() - 150);
		CRect	rLayerAndHeader;
		rLayerAndHeader = m_rListRect;
		rLayerAndHeader.top -= 22;
		InvalidateRect( rLayerAndHeader, FALSE );
		bRes = true;
	}
	else if( m_eTrackAction == TRACKING_SEPARATOR ) {
		CRect	rRect;
		GetClientRect( rRect );
		m_rListRect.right = m_rOrigListRect.right - (m_rTrackOrigPt.x - point.x);
		if( m_rListRect.right < (m_rListRect.left + 250) )
			m_rListRect.right = m_rListRect.left + 250;
		if( m_rListRect.right > (rRect.right - 12) )
			m_rListRect.right = (rRect.right - 12);
		if( m_i32LayerValueSize > (m_rListRect.Width() - 150) )
			m_i32LayerValueSize = (m_rListRect.Width() - 150);
		m_rTimelineRect.left = m_rListRect.right;
		m_rRulerRect.left = m_rListRect.right;
		m_rWaveformRect.left = m_rListRect.right;

		// Invalidate sampled controller data
		m_rSceneItemList.remove_cont_sample_data();

		// update controls
		AdjustControls();

		Invalidate( FALSE );
		bRes = true;
	}
	else if( m_eTrackAction == TRACKING_PAN ) {
		// move time
		SetTimeStart( m_i32TrackOrigTime + m_pDoc->FrameToTime( (m_rTrackOrigPt.x - point.x) / m_i32FrameWidthInPixels ) );

		// move y-dir
		int32	i32Range = m_rSceneItemList.get_list_height() - m_rListRect.Height() / 2;
		if( i32Range < 0 )
			i32Range = 0;

		SetListPos( m_i32TrackOrigPos + (m_rTrackOrigPt.y - point.y) );

		if( GetListPos() < 0 ) {
			SetListPos( 0 );
		}
		if( GetListPos() > i32Range ) {
			SetListPos( i32Range );
		}

		m_rListScroll.SetScrollPos( GetListPos(), TRUE );
		m_rSceneItemList.remove_cont_sample_data();	// invalidate controllerdata
		Invalidate( FALSE );

		bRes = true;
	}
	else {
		bRes = OnSceneMouseMove( nFlags, point );
	}
	if( !bRes )
		baseCMyBar::OnMouseMove(nFlags, point);
}

	
void CTimeLineBar::DrawBoxSelect()
{
	CClientDC	rDC( this );

	// contruct old selection
	CRect	rOldRect, rNewRect;
	rOldRect.top = m_rTimelineRect.top + m_i32BoxSelectStartY - GetListPos();
	rOldRect.bottom = m_rTimelineRect.top + m_i32BoxSelectLastY - GetListPos();
	rOldRect.left = m_rTimelineRect.left + m_pDoc->TimeToFrame( m_i32BoxSelectStartTime - m_i32ViewTimeStart ) * m_i32FrameWidthInPixels;
	rOldRect.right = m_rTimelineRect.left + m_pDoc->TimeToFrame( m_i32BoxSelectLastTime - m_i32ViewTimeStart ) * m_i32FrameWidthInPixels;
	rOldRect.NormalizeRect();
	rOldRect.right += m_i32FrameWidthInPixels;

	rNewRect.top = m_rTimelineRect.top + m_i32BoxSelectStartY - GetListPos();
	rNewRect.bottom = m_rTimelineRect.top + m_i32BoxSelectCurY - GetListPos();
	rNewRect.left = m_rTimelineRect.left + m_pDoc->TimeToFrame( m_i32BoxSelectStartTime - m_i32ViewTimeStart ) * m_i32FrameWidthInPixels;
	rNewRect.right = m_rTimelineRect.left + m_pDoc->TimeToFrame( m_i32BoxSelectCurTime - m_i32ViewTimeStart ) * m_i32FrameWidthInPixels;
	rNewRect.NormalizeRect();
	rNewRect.right += m_i32FrameWidthInPixels;

	// save old clip region
	CRect	rOldClipRect;
	CRgn	rOldClipRgn;
	rDC.GetClipBox( rOldClipRect );
	rOldClipRgn.CreateRectRgnIndirect( rOldClipRect );

	// create new clip region
	CRgn	rRegion;
	rRegion.CreateRectRgnIndirect( CRect( m_rTimelineRect.left + 1, m_rTimelineRect.top + 1,
								m_rTimelineRect.right - 1, m_rTimelineRect.bottom - 1 ) );
	rDC.SelectClipRgn( &rRegion );


	if( m_bFirstTimeBoxSelect || m_bScrollingBoxSelect ) {
		rDC.DrawFocusRect( rNewRect );
		m_bFirstTimeBoxSelect = false;
		m_bScrollingBoxSelect = false;
	}
	else {
		rDC.DrawFocusRect( rOldRect );
		rDC.DrawFocusRect( rNewRect );
	}

	// restore old clip region
	rDC.SelectClipRgn( &rOldClipRgn );
	rOldClipRgn.DeleteObject();
	rRegion.DeleteObject();

	// save new values
	m_i32BoxSelectLastTime = m_i32BoxSelectCurTime;
	m_i32BoxSelectLastY = m_i32BoxSelectCurY;
}


CSize
CTimeLineBar::GetTextExtent( const char* szText, CFont* pFont )
{
	CDC	rDC;
	rDC.CreateIC( "DISPLAY", NULL, NULL, NULL );
	CFont*	pOldFont = rDC.SelectObject( pFont );
	CSize	rSize = rDC.GetTextExtent( CString( szText ) );
	rDC.SelectObject( pOldFont );
	rDC.DeleteDC();
	return rSize;
}

void
CTimeLineBar::RenameItem( SceneItemC* pItem )
{
	if( m_pEditCtrl )
		OnKillfocusEditLabel();

	m_pEditNameItem = pItem;
	CString	sItemName = GetItemName( pItem );

	// delete title
	if( m_pEditNameItem->get_type() == SCENEITEM_LAYER ) {
		LayerC*	pLayer = m_pEditNameItem->get_layer();
	}
	else if( m_pEditNameItem->get_type() == SCENEITEM_EFFECT ) {
		EffectI*	pEffect = m_pEditNameItem->get_effect();
	}
	m_rSceneItemList.layer_list_modified();
	Invalidate( FALSE );

	// Create edit control
	CRect	rRect;
	CSize	rSize = GetTextExtent( sItemName, &m_rFont );
	m_pEditCtrl = new CEdit;
	rRect.top = m_rListRect.top - GetListPos() + (pItem->get_y() + m_rSceneItemList.get_item_height() / 2 - 5);
	rRect.bottom = rRect.top + rSize.cy;
	rRect.left = m_rListRect.left + (pItem->get_x() + 40);
	rRect.right = rRect.left + rSize.cx + rSize.cy;
	if( !m_pEditCtrl->Create( WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL, rRect, this, IDC_EDITLABEL ) ) {
		TRACE( "creating edit failed\n" );
		delete m_pEditCtrl;
		m_pEditCtrl = 0;
		return;
	}

	m_pEditCtrl->SetFont( &m_rFont );
	m_pEditCtrl->SetWindowText( sItemName );
	m_pEditCtrl->SetSel( 0, -1 );	// select all text
	m_pEditCtrl->SetFocus();
}


void CTimeLineBar::CreateEffect( SceneItemC* pItem, uint32 ui32Num )
{
	CDemopajaApp*	pApp = (CDemopajaApp*)AfxGetApp();
	FactoryC*		pFactory = pApp->GetFactory();
	LayerC*			pLayer = pItem->get_layer();

	ClassDescC*	pDesc = pFactory->get_classdesc( ui32Num );
	if( pDesc ) {

		std::string	sStr( "New " );
		sStr += pDesc->get_name();

		if( !m_pDoc->EffectCreate( pDesc->get_class_id(), sStr.c_str(), pLayer, GetTimecursor() ) )
			return;

		m_pDoc->NotifyViews( NOTIFY_REDRAW_ALL );	// update inspector too
	}
	else
		TRACE( "invalid desc\n" );
}

void CTimeLineBar::FillEffectMenu( CFlatPopupMenu& rMenu )
{
	CDemopajaApp*	pApp = (CDemopajaApp*)AfxGetApp();
	FactoryC*		pFactory = pApp->GetFactory();
	DeviceContextC*	pContext = m_pDoc->GetDeviceContext();

	std::vector<EffectNameS>	rEffects;

	for( uint32 i = 0; i < pFactory->get_classdesc_count(); i++ ) {
		ClassDescC*	pDesc = pFactory->get_classdesc( i );
		if( pDesc->get_classtype() == CLASS_TYPE_EFFECT ) {

			// Check device support
			int32	i32PassCount = 0;
			for( uint32 j = 0; j < pDesc->get_required_device_driver_count(); j++ ) {
				if( pContext->query_interface( pDesc->get_required_device_driver( j ) ) )
					i32PassCount++;
			}

			if( i32PassCount == pDesc->get_required_device_driver_count() ) {
				EffectNameS	rFx;
				rFx.m_sName = pDesc->get_name();
				rFx.m_ui32Idx = i | 0x20000000;
				rEffects.push_back( rFx );
			}
		}
	}

	std::sort( rEffects.begin(), rEffects.end() );

	for( i = 0; i < rEffects.size(); i++ )
		rMenu.AppendItem( 0, rEffects[i].m_sName.c_str(), rEffects[i].m_ui32Idx );
}

void CTimeLineBar::GizmoMenu( CPoint rPoint, SceneItemC* pItem )
{
	CFlatPopupMenu	rMenu;
	CFlatPopupMenu	rVisMenu;

	GizmoI*	pGizmo = pItem->get_gizmo();

	if( !pGizmo )
		return;

	rMenu.SetFont( "Arial" );
	rMenu.SetFontSize( 8 );
	rVisMenu.SetFont( "Arial" );
	rVisMenu.SetFontSize( 8 );

	rMenu.Create( AfxGetInstanceHandle(), IDB_GIZMORIGHT );
	rVisMenu.Create( AfxGetInstanceHandle(), IDB_GIZMORIGHT );


	uint32	i;
	uint32	ui32HideCount = 0;
	uint32	ui32ShowCount = 0;

	// Add items which can be shown
	for( i = 0; i < pGizmo->get_parameter_count(); i++ ) {
		ParamI*	pParam = pGizmo->get_parameter( i );
		if( pParam->get_flags() & ITEM_GUIHIDDEN )
			rVisMenu.AppendItem( 0, pParam->get_name(), i + 0x40000000, 1 );
		else
			rVisMenu.AppendItem( 0, pParam->get_name(), i + 0x80000000, 0 );
	}

	uint32	ui32ShowFlags = 0, ui32HideFlags = 0;

	if( ui32HideCount == 0 )
		ui32HideFlags = CFlatPopupMenu::itemGrayed | CFlatPopupMenu::itemNotSelectable;
	if( ui32ShowCount == 0 )
		ui32ShowFlags = CFlatPopupMenu::itemGrayed | CFlatPopupMenu::itemNotSelectable;

	rMenu.AppendPopup( 0, "&Hide/Show Param", rVisMenu, 0 );

	ClientToScreen( &rPoint );
	int32	i32Id = rMenu.Track( rPoint.x, rPoint.y, NULL, true );

	if( i32Id & 0x80000000 ) {
		// hide
		i32Id &= 0x0fffffff;
		ParamI*	pParam = pGizmo->get_parameter( i32Id );
		if( pParam ) {
			m_pDoc->ParameterShow( pParam, false );
			m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
		}
	}
	else if( i32Id & 0x40000000 ) {
		// show
		i32Id &= 0x0fffffff;
		ParamI*	pParam = pGizmo->get_parameter( i32Id );
		if( pParam ) {
			m_pDoc->ParameterShow( pParam, true );
			m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
		}
	}
}

void CTimeLineBar::EffectMenu( CPoint rPoint, SceneItemC* pItem )
{
	CFlatPopupMenu	rMenu;
	CFlatPopupMenu	rMenuEffects;
	CFlatPopupMenu	rVisMenu;

	EffectI*	pEffect = pItem->get_effect();

	if( !pEffect )
		return;

	rMenu.SetFont( "Arial" );
	rMenu.SetFontSize( 8 );
	rMenuEffects.SetFont( "Arial" );
	rMenuEffects.SetFontSize( 8 );
	rVisMenu.SetFont( "Arial" );
	rVisMenu.SetFontSize( 8 );

	// the effects menu
	rMenu.Create( AfxGetInstanceHandle(), IDB_LAYERRIGHT );
	rMenuEffects.Create( AfxGetInstanceHandle(), IDB_ARRANGE );
	rVisMenu.Create( AfxGetInstanceHandle(), IDB_LAYERRIGHT );


	uint32	i;
	uint32	ui32HideCount = 0;
	uint32	ui32ShowCount = 0;

	if( pEffect ) {
		// Add items which can be shown
		for( i = 0; i < pEffect->get_gizmo_count(); i++ ) {
			GizmoI*	pGizmo = pEffect->get_gizmo( i );
			if( pGizmo->get_flags() & ITEM_GUIHIDDEN )
				rVisMenu.AppendItem( 0, pGizmo->get_name(), i + 0x40000000, 8 );
			else
				rVisMenu.AppendItem( 0, pGizmo->get_name(), i + 0x80000000, 7 );
		}
	}

	FillEffectMenu( rMenuEffects );

	uint32	ui32ShowFlags = 0, ui32HideFlags = 0;

	if( ui32HideCount == 0 )
		ui32HideFlags = CFlatPopupMenu::itemGrayed | CFlatPopupMenu::itemNotSelectable;
	if( ui32ShowCount == 0 )
		ui32ShowFlags = CFlatPopupMenu::itemGrayed | CFlatPopupMenu::itemNotSelectable;

	rMenu.AppendPopup( 0, "&Insert Effect", rMenuEffects, 3 );
	rMenu.AppendItem( 0, "&Rename Effect", 3, 2 );
	rMenu.AppendItem( CFlatPopupMenu::itemSeparator, NULL, 0 );
	rMenu.AppendPopup( 0, "&Show/Hide Gizmo", rVisMenu, 7 );
	rMenu.AppendItem( CFlatPopupMenu::itemSeparator, NULL, 0 );
	rMenu.AppendItem( m_pDoc->CanCopy() ? 0 : CFlatPopupMenu::itemGrayed | CFlatPopupMenu::itemNotSelectable, "Cu&t", 4, 4 );
	rMenu.AppendItem( m_pDoc->CanCopy() ? 0 : CFlatPopupMenu::itemGrayed | CFlatPopupMenu::itemNotSelectable, "&Copy", 5, 5 );
	rMenu.AppendItem( m_pDoc->CanPaste() ? 0 : CFlatPopupMenu::itemGrayed | CFlatPopupMenu::itemNotSelectable, "&Paste", 6, 6 );
	rMenu.AppendItem( 0, "&Delete", 2, 1 );
	rMenu.AppendItem( CFlatPopupMenu::itemSeparator, NULL, 0 );
	rMenu.AppendItem( 0, "&Help...", 7, -1 );

	ClientToScreen( &rPoint );
	int32	i32Id = rMenu.Track( rPoint.x, rPoint.y, NULL, true );

	if( i32Id == 2 ) {
		// delete
		m_pDoc->EffectDeleteSelected();
		m_pDoc->NotifyViews( NOTIFY_REDRAW_ALL );
	}
	else if( i32Id == 3 ) {
		RenameItem( pItem );
		m_pDoc->SetModifiedFlag();
		m_rSceneItemList.layer_list_modified();
		m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
	}
	else if( i32Id == 4 ) {
		// Cut
		m_pDoc->OnCut();
	}
	else if( i32Id == 5 ) {
		// Copy
		m_pDoc->OnCopy();
	}
	else if( i32Id == 6 ) {
		// Paste
		m_pDoc->OnPaste();
	}
	else if( i32Id == 7 ) {
		// show help
		CMainFrame*	pMain = (CMainFrame*)AfxGetMainWnd();
		EffectI*	pEffect = pItem->get_effect();
		if( pMain && pEffect )
			pMain->BrowseHelp( pEffect->get_class_id() );
	}
	else if( i32Id & 0x20000000 ) {
		// create new effect
		CreateEffect( pItem, i32Id & 0x0fffffff );
	}
	else if( i32Id & 0x80000000 && pEffect ) {
		// hide
		i32Id &= 0x0fffffff;
		GizmoI*	pGizmo = pEffect->get_gizmo( i32Id );
		if( pGizmo ) {
			m_pDoc->GizmoShow( pGizmo, false );
			m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
		}
	}
	else if( i32Id & 0x40000000 && pEffect ) {
		// show
		i32Id &= 0x0fffffff;
		GizmoI*	pGizmo = pEffect->get_gizmo( i32Id );
		if( pGizmo ) {
			m_pDoc->GizmoShow( pGizmo, true );
			m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
		}
	}

}

void CTimeLineBar::LayerMenu( CPoint rPoint, SceneItemC* pItem )
{
	CFlatPopupMenu	rMenu;
	CFlatPopupMenu	rMenuEffects;

	rMenu.SetFont( "Arial" );
	rMenu.SetFontSize( 8 );
	rMenuEffects.SetFont( "Arial" );
	rMenuEffects.SetFontSize( 8 );

	// the layer menu

	rMenu.Create( AfxGetInstanceHandle(), IDB_LAYERRIGHT );
	rMenuEffects.Create( AfxGetInstanceHandle(), IDB_ARRANGE );

	FillEffectMenu( rMenuEffects );

	uint32	ui32Flags =  0;

	// if no item is selected dont allow commands which changes the item.
	if( !pItem )
		ui32Flags = CFlatPopupMenu::itemGrayed | CFlatPopupMenu::itemNotSelectable;

	rMenu.AppendItem( 0, "&Insert Layer", 1, 0 );
	rMenu.AppendPopup( ui32Flags, "&Insert Effect", rMenuEffects, 3 );
	rMenu.AppendItem( CFlatPopupMenu::itemSeparator, NULL, 0 );
	rMenu.AppendItem( ui32Flags, "&Rename Layer", 3, 2 );
	rMenu.AppendItem( CFlatPopupMenu::itemSeparator, NULL, 0 );
	rMenu.AppendItem( m_pDoc->CanCopy() ? 0 : CFlatPopupMenu::itemGrayed | CFlatPopupMenu::itemNotSelectable, "Cu&t", 4, 4 );
	rMenu.AppendItem( m_pDoc->CanCopy() ? 0 : CFlatPopupMenu::itemGrayed | CFlatPopupMenu::itemNotSelectable, "&Copy", 5, 5 );
	rMenu.AppendItem( m_pDoc->CanPaste() ? 0 : CFlatPopupMenu::itemGrayed | CFlatPopupMenu::itemNotSelectable, "&Paste", 6, 6 );
	rMenu.AppendItem( ui32Flags, "&Delete", 2, 1 );

	ClientToScreen( &rPoint );
	int32	i32Id = rMenu.Track( rPoint.x, rPoint.y, NULL, true );
	if( i32Id == 1 ) {
		m_pDoc->LayerAdd( GetTimecursor() );
		m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
	}
	else if( i32Id == 2 ) {
		m_pDoc->LayerDeleteSelected();
		// update window
		SetScrollRanges();	// length may have changed
		m_pDoc->NotifyViews( NOTIFY_REDRAW_ALL );	// update inspector too
	}
	else if( i32Id == 3 ) {
		// rename
		RenameItem( pItem );
	}
	else if( i32Id == 4 ) {
		// Cut
		m_pDoc->OnCut();
	}
	else if( i32Id == 5 ) {
		// Copy
		m_pDoc->OnCopy();
	}
	else if( i32Id == 6 ) {
		// Paste
		m_pDoc->OnPaste();
	}
	else if( i32Id & 0x20000000 ) {
		// create new effect
		CreateEffect( pItem, i32Id & 0x0fffffff );
	}
}



void CTimeLineBar::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) 
{
	// If we are editing a label, just kill the focus.
	if( m_pEditCtrl ) {
		OnKillfocusEditLabel();
		return;
	}

	if( pScrollBar->GetDlgCtrlID() != IDC_TIMELINESCROLL ) {
		baseCMyBar::OnHScroll(nSBCode, nPos, pScrollBar);
		return;
	}

	int32	i32Min, i32Max;
	pScrollBar->GetScrollRange( &i32Min, &i32Max );

	int32	i32PageSize = m_rRulerRect.Width() / m_i32FrameWidthInPixels;

	switch ( nSBCode )	{
	case SB_LINEUP:
		SetTimeStart( m_i32ViewTimeStart - 5 * m_pDoc->GetFrameSizeInTicks() );
		break;
	case SB_LINEDOWN:
		SetTimeStart( m_i32ViewTimeStart + 5 * m_pDoc->GetFrameSizeInTicks() );
		break;
	case SB_PAGEUP:
		SetTimeStart( m_i32ViewTimeStart - i32PageSize * m_pDoc->GetFrameSizeInTicks() );
		break;
	case SB_PAGEDOWN:
		SetTimeStart( m_i32ViewTimeStart + i32PageSize * m_pDoc->GetFrameSizeInTicks() );
		break;
	case SB_TOP:
		SetTimeStart( i32Min * m_pDoc->GetFrameSizeInTicks() );
		break;
	case SB_BOTTOM:
		SetTimeStart( i32Max * m_pDoc->GetFrameSizeInTicks() );
		break;		
	case SB_THUMBTRACK:
	case SB_THUMBPOSITION:
		SetTimeStart( nPos * m_pDoc->GetFrameSizeInTicks() );
		break;
	default:
		return;
	}

	// redraw
	Invalidate( FALSE );
}

void CTimeLineBar::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) 
{
	// If we are editing a label, just kill the focus.
	if( m_pEditCtrl ) {
		OnKillfocusEditLabel();
		return;
	}

	if( pScrollBar->GetDlgCtrlID() != IDC_LAYERLISTSCROLL ) {
		baseCMyBar::OnVScroll(nSBCode, nPos, pScrollBar);
		return;
	}

	SCROLLINFO	rScrollInfo;
	int32	i32Min, i32Max;
	pScrollBar->GetScrollInfo( &rScrollInfo );

	i32Min = rScrollInfo.nMin;
	i32Max = rScrollInfo.nMax - rScrollInfo.nPage;
	if( i32Max < 0 )
		i32Max = 0;

	switch ( nSBCode )	{
	case SB_LINEUP:
		SetListPos( GetListPos() - m_rSceneItemList.get_item_height() );
		break;
	case SB_LINEDOWN:
		SetListPos( GetListPos() + m_rSceneItemList.get_item_height() );
		break;
	case SB_PAGEUP:
		SetListPos( GetListPos() - m_rListRect.Height() );
		break;
	case SB_PAGEDOWN:
		SetListPos( GetListPos() + m_rListRect.Height() );
		break;
	case SB_TOP:
		SetListPos( i32Min );
		break;
	case SB_BOTTOM:
		SetListPos( i32Max );
		break;		
	case SB_THUMBTRACK:
	case SB_THUMBPOSITION:
		SetListPos( nPos );
		break;
	default:
		return;
	}

	if( GetListPos() < 0 )
		SetListPos( 0 );
	if( GetListPos() > i32Max )
		SetListPos( i32Max );

	pScrollBar->SetScrollPos( GetListPos() );

	// redraw
	Invalidate( FALSE );
}


BOOL CTimeLineBar::OnEraseBkgnd(CDC* pDC) 
{
	// dont erase background
	return FALSE;
}

BOOL CTimeLineBar::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) 
{
	if( m_hCursor ) {
		::SetCursor( m_hCursor );
		return TRUE;
	}
	return baseCMyBar::OnSetCursor(pWnd, nHitTest, message);
}


void CTimeLineBar::OnKillfocusEditLabel()
{
	if( !m_pEditCtrl || !m_pEditNameItem )
		return;

	// delete the edit box
	m_pEditCtrl->DestroyWindow();
	delete m_pEditCtrl;
	m_pEditCtrl = 0;

	// update window
	m_rSceneItemList.layer_list_modified();
	Invalidate( FALSE );
}

void CTimeLineBar::OnChangeEditLabel()
{
	if( !m_pEditCtrl || !m_pEditNameItem )
		return;

	CRect	rRect;
	CString	rStr;
	CSize	rSize;

	// adjust edit control to show whole text.
	m_pEditCtrl->GetWindowRect( &rRect );
	ScreenToClient( &rRect );
	m_pEditCtrl->GetWindowText( rStr );
	rSize = GetTextExtent( rStr, &m_rFont );
	int32	i32Width = rSize.cx + rSize.cy * 2;
	int32	i32MaxWidth = m_rListRect.Width() - (m_pEditNameItem->get_x() + 40 + 4) - m_i32LayerValueSize;
	if( i32Width > i32MaxWidth )
		i32Width = i32MaxWidth;
	rRect.right = rRect.left + i32Width;
	m_pEditCtrl->MoveWindow( &rRect );
}


void CTimeLineBar::OnChangeFrameEdit()
{
	if( m_bEditingFrame )
		return;

	m_bEditingFrame = true;

	char	szText[256];
	m_rFrameEdit.GetWindowText( szText, 255 );
	SetTimecursor( m_pDoc->GetTimeFromString( szText, TIME_FORMAT_HOUR_MIN_SEC_FRAME ) );

	uint32		ui32NumValues = m_rTimelineRect.Width() / m_i32FrameWidthInPixels - 1;
	int32		i32MaxTime = m_i32ViewTimeStart + m_pDoc->FrameToTime( ui32NumValues );

	if( GetTimecursor() < m_i32ViewTimeStart || GetTimecursor() > i32MaxTime )
		SetTimeStart( GetTimecursor() - (i32MaxTime - m_i32ViewTimeStart) / 4 );

	m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );

	m_bEditingFrame = false;
}


LRESULT CTimeLineBar::WindowProc( UINT message, WPARAM wParam, LPARAM lParam )
{
	if( message == WM_COMMAND ) {
		if( wParam == IDOK && m_pEditCtrl && m_pEditNameItem ) {
			// get the new label
			CString	sStr;
			m_pEditCtrl->GetWindowText( sStr );

			// delete the edit box
			m_pEditCtrl->DestroyWindow();
			delete m_pEditCtrl;
			m_pEditCtrl = 0;

			// update name
			if( m_pEditNameItem->get_type() == SCENEITEM_LAYER ) {
				m_pDoc->LayerEditName( m_pEditNameItem->get_layer(), sStr );
			}
			else if( m_pEditNameItem->get_type() == SCENEITEM_EFFECT ) {
				m_pDoc->EffectEditName( m_pEditNameItem->get_effect(), sStr );
			}
			// update window
			Invalidate( FALSE );

			return 0;
		}
		else if( wParam == IDCANCEL && m_pEditCtrl && m_pEditNameItem ) {
			OnKillfocusEditLabel();
			return 0;
		}
	}

	return baseCMyBar::WindowProc( message, wParam, lParam );
}

DROPEFFECT
CTimeLineBar::OnDragOver( COleDataObject* pDataObject, DWORD dwKeyState, CPoint point )
{

	if( !pDataObject->IsDataAvailable( CDemopajaDoc::GetPrivateClipboardFormat() ) )
		return DROPEFFECT_NONE;

	HGLOBAL hMem = pDataObject->GetGlobalData( CDemopajaDoc::GetPrivateClipboardFormat() );
	int32*	pMem = (int32*)::GlobalLock( hMem );
	if( !pMem ) {
		TRACE_LAST_ERROR( "CTimeLineBar::OnDragOver" );
		return FALSE;
	}
	int32	i32Num = *pMem;
	::GlobalUnlock( hMem );


	FileHandleC*	pHandle = m_pDoc->GetFileList()->get_file( i32Num );

	if( !pHandle )
		return DROPEFFECT_NONE;

	if( pHandle->get_flags() & FILEHANDLE_FOLDER )
		return DROPEFFECT_NONE;

	ImportableI*	pImp = pHandle->get_importable();

	if( !pImp )
		return DROPEFFECT_NONE;

	// only allow drop to the layer list
	if( !m_rListRect.PtInRect( point ) )
		return DROPEFFECT_NONE;

	SceneItemC*	pItem = 0;
	int32	i32Hit = HitTestSceneList( point, &pItem );

	// deselect all selected items
	m_pDoc->DelFlagsAll( ITEM_LAYER | ITEM_EFFECT | ITEM_GIZMO | ITEM_PARAMETER, ITEM_SELECTED );
	m_rSceneItemList.layer_list_modified();

	if( pItem ) {
		if( pItem->get_type() == SCENEITEM_PARAMETER ) {
			ParamI*	pParam = pItem->get_parameter();
			if( pParam && pParam->get_type() == PARAM_TYPE_FILE ) {
				ParamFileC*	pFileParam = (ParamFileC*)pParam;

				if( (pFileParam->get_class_filter() == NULL_CLASSID ||
					 pFileParam->get_class_filter() == pImp->get_class_id()) &&
					(pFileParam->get_super_class_filter() == NULL_SUPERCLASS ||
					 pFileParam->get_super_class_filter() == pImp->get_super_class_id()) ) {
					pParam->add_flags( ITEM_SELECTED );
					InvalidateRect( m_rListRect );
					return DROPEFFECT_COPY;
				}
			}
		}
		else if( pItem->get_type() == SCENEITEM_LAYER ) {
			LayerC*	pLayer = pItem->get_layer();
			if( pLayer ) {
				pLayer->add_flags( ITEM_SELECTED );
				InvalidateRect( m_rListRect );
				return DROPEFFECT_COPY;
			}
		}
	}
	// just redraw
	InvalidateRect( m_rListRect );

	return DROPEFFECT_NONE;
}

BOOL
CTimeLineBar::OnDrop( COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point )
{
	if( !pDataObject->IsDataAvailable( CDemopajaDoc::GetPrivateClipboardFormat() ) )
		return FALSE;

	HGLOBAL hMem = pDataObject->GetGlobalData( CDemopajaDoc::GetPrivateClipboardFormat() );
	int32*	pMem = (int32*)::GlobalLock( hMem );
	if( !pMem ) {
		TRACE_LAST_ERROR( "CTimeLineBar::OnDrop" );
		return FALSE;
	}
	int32	i32Num = *pMem;
	::GlobalUnlock( hMem );

	if( m_rListRect.PtInRect( point ) ) {

		SceneItemC*	pItem = 0;
		int32	i32Hit = HitTestSceneList( point, &pItem );

		// deselect all selected items
		m_pDoc->DelFlagsAll( ITEM_LAYER | ITEM_EFFECT | ITEM_GIZMO | ITEM_PARAMETER, ITEM_SELECTED );
		m_rSceneItemList.layer_list_modified();

		if( pItem ) {
			FileListC*		pFileList = m_pDoc->GetFileList();
			FileHandleC*	pHandle = pFileList->get_file( i32Num );

			if( pHandle ) {

				ImportableI*	pImp = pHandle->get_importable();

				if( !pImp )
					return FALSE;

				if( pItem->get_type() == SCENEITEM_PARAMETER ) {
					// File dropped on a parameter, assign file.
					ParamI*	pParam = pItem->get_parameter();
					if( pParam && pParam->get_type() == PARAM_TYPE_FILE ) {
						pParam->add_flags( ITEM_SELECTED );

						ParamFileC*	pFileParam = (ParamFileC*)pParam;

						if( (pFileParam->get_class_filter() == NULL_CLASSID ||
							 pFileParam->get_class_filter() == pImp->get_class_id()) &&
							(pFileParam->get_super_class_filter() == NULL_SUPERCLASS ||
							 pFileParam->get_super_class_filter() == pImp->get_super_class_id()) ) {

							m_pDoc->ParameterSetFile( pFileParam, GetTimecursor(), pHandle );

							// update reference count in inspector too
							m_pDoc->NotifyViews( NOTIFY_REDRAW_ALL );

							return TRUE;
						}
					}
				}
				else if( pItem->get_type() == SCENEITEM_LAYER ) {
					// File droppedon a layer, create new default effect, and assign the file (done in EffectCreate).
					LayerC*	pLayer = pItem->get_layer();
					if( pLayer ) {
						pLayer->add_flags( ITEM_SELECTED );

						ImportableI*	pImportable = pHandle->get_importable();

						// get name for layer
						std::string	sStr;
						char szFname[_MAX_FNAME];
						char szExt[_MAX_EXT];
						_splitpath( pImportable->get_filename(), 0, 0, szFname, szExt );
						sStr += szFname;
						sStr += szExt;

						if( !m_pDoc->EffectCreate( pImportable->get_default_effect(), sStr.c_str(), pLayer,
							GetTimecursor(), pHandle ) ) {
							MessageBox( "Cannot create the default effect the imported file suggests.\nMaybe a missing DLL?", "New Effect Failed", MB_ICONWARNING | MB_OK );
							return FALSE;
						}


						// update reference count in inspector too
						m_pDoc->NotifyViews( NOTIFY_REDRAW_ALL );

						return TRUE;
					}
				}
			}
		}
		MessageBox( "You must drop to a layer or file parameter.", "You Missed it", MB_ICONWARNING | MB_OK );
	}

	return TRUE;
}

void CTimeLineBar::OnTimer( UINT nIDEvent ) 
{

	int32	i32TimeStep = 2;

	switch( m_i32FrameWidthInPixels ) {
	case 3:		i32TimeStep = 4; break;
	case 5:		i32TimeStep = 3; break;
	case 7:		i32TimeStep = 3; break;
	case 9:		i32TimeStep = 2; break;
	case 15:	i32TimeStep = 1; break;
	}

	i32TimeStep = m_pDoc->FrameToTime( i32TimeStep );

	if( nIDEvent == TIMER_TIMERCURSOR_LEFT ) {
		SetTimeStart( m_i32ViewTimeStart - i32TimeStep );
		SetTimecursor( GetTimecursor() - i32TimeStep );
		Invalidate( FALSE );
	}
	else if( nIDEvent == TIMER_TIMERCURSOR_RIGHT ) {
		SetTimecursor( GetTimecursor() + i32TimeStep );
		SetTimeStart( m_i32ViewTimeStart + i32TimeStep );
		Invalidate( FALSE );
	}
	else {
		OnSceneTimer( nIDEvent );
	}

	baseCMyBar::OnTimer(nIDEvent);
}


void CTimeLineBar::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
	bool	bSpacePressed = (::GetAsyncKeyState( VK_SPACE ) & 0x8000 ) != 0;
	bool	bCtrl = (::GetAsyncKeyState( VK_CONTROL ) & 0x8000 ) != 0;
	bool	bShift = (::GetAsyncKeyState( VK_SHIFT ) & 0x8000 ) != 0;

	if( m_eTrackAction == TRACKING_NONE ) {
		if( bSpacePressed ) {
			m_hCursor = AfxGetApp()->LoadCursor( IDC_DPHAND );
			::SetCursor( m_hCursor );
			return;
		}

		if( nChar == VK_DELETE ) {
			if( m_pDoc->GetEditFocus() == FOCUS_TIMELINE ) {
				m_pDoc->KeysDeleteSelected();
			}
			else if( m_pDoc->GetEditFocus() == FOCUS_LAYERLIST ) {
				m_pDoc->LayerAndEffectDeleteSelected();
				// update window
				SetScrollRanges();	// length may have changed
			}

			m_pDoc->NotifyViews( NOTIFY_REDRAW_ALL );
		}
		else if( nChar == 'V' ) {
			m_pDoc->SetTool( TOOL_ARROW );
		}
		else if( nChar == 'R' ) {
			m_pDoc->SetTool( TOOL_ROTATE );
		}
		else if( nChar == 'S' ) {
			m_pDoc->SetTool( TOOL_SCALE );
		}
		else if( nChar == 'Z' ) {
			m_pDoc->SetTool( TOOL_ZOOM );
		}
		else if( nChar == 'H' ) {
			m_pDoc->SetTool( TOOL_PAN );
		}
		else if( nChar == 'A' ) {
			m_pDoc->SetTool( TOOL_KEYARROW );
		}
		else if( nChar == 13 ) {
			if( bCtrl ) {
				CDemopajaApp*	pApp = (CDemopajaApp*)AfxGetApp();
				CDemopajaDoc*	pDoc = GetDoc();
				pApp->PlayPreview( pDoc->GetTimecursor(), pDoc->GetLastFrameTime() );
				SetFocus();
				m_pDoc->NotifyViews( NOTIFY_REDRAW_ALL );
			}
			else
				OnPlay();
			return;
		}
		else if( nChar == VK_LEFT ) {
			if( !bCtrl && !bShift )
				OnPrev();
			else if( bCtrl && !bShift )
				OnBack();
			else if( !bCtrl && bShift )
				OnPrevBeat();
		}
		else if( nChar == VK_RIGHT ) {
			if( !bCtrl && !bShift )
				OnNext();
			else if( bCtrl && !bShift )
				OnForw();
			else if( !bCtrl && bShift )
				OnNextBeat();
		}
		else if( nChar == VK_HOME && bCtrl ) {
			SetFocus();
			uint32		ui32NumValues = m_rTimelineRect.Width() / m_i32FrameWidthInPixels - 1;
			int32		i32MaxTime = m_i32ViewTimeStart + m_pDoc->FrameToTime( ui32NumValues );
			SetTimecursor( 0 );

			if( GetTimecursor() < m_i32ViewTimeStart || GetTimecursor() > i32MaxTime )
				SetTimeStart( GetTimecursor() - (i32MaxTime - m_i32ViewTimeStart) / 4 );

			CDemopajaDoc*	pDoc = GetDoc();
			CDemopajaApp*	pApp = (CDemopajaApp*)AfxGetApp();
			CDemopajaView*	pView = 0;
			// get the view
			POSITION rPos = pDoc->GetFirstViewPosition();
			if( rPos )
				pView = (CDemopajaView*)pDoc->GetNextView( rPos );

			if( pView )
				pView->SetTimePos( GetTimecursor() );

			m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
		}
		else if( nChar == VK_END && bCtrl ) {
			SetFocus();
			uint32		ui32NumValues = m_rTimelineRect.Width() / m_i32FrameWidthInPixels - 1;
			int32		i32MaxTime = m_i32ViewTimeStart + m_pDoc->FrameToTime( ui32NumValues );
			SetTimecursor( m_pDoc->GetLastFrameTime() );

			if( GetTimecursor() < m_i32ViewTimeStart || GetTimecursor() > i32MaxTime )
				SetTimeStart( GetTimecursor() - (i32MaxTime - m_i32ViewTimeStart) / 4 );

			CDemopajaDoc*	pDoc = GetDoc();
			CDemopajaApp*	pApp = (CDemopajaApp*)AfxGetApp();
			CDemopajaView*	pView = 0;
			// get the view
			POSITION rPos = pDoc->GetFirstViewPosition();
			if( rPos )
				pView = (CDemopajaView*)pDoc->GetNextView( rPos );

			if( pView )
				pView->SetTimePos( GetTimecursor() );

			m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
		}
		else if( nChar == VK_ESCAPE ) {

			// Deselect all
			m_pDoc->DelFlagsAll( ITEM_EFFECT, ITEM_SELECTED );
			m_pDoc->KeysDeselectAll();		
			m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
		}
	}

	baseCMyBar::OnKeyDown(nChar, nRepCnt, nFlags);
}

void CTimeLineBar::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
	bool	bSpacePressed = (::GetAsyncKeyState( VK_SPACE ) & 0x8000 ) != 0;

	if( !bSpacePressed ) {
		if( m_hCursor == AfxGetApp()->LoadCursor( IDC_DPHAND ) && m_eTrackAction != TRACKING_PAN ) {
			m_hCursor = 0;
			::SetCursor( ::LoadCursor( NULL, IDC_ARROW ) );
		}
		return;
	}
	
	baseCMyBar::OnKeyUp(nChar, nRepCnt, nFlags);
}

/*
void CTimeLineBar::RedrawTimecursor()
{
//	CClientDC	rDC( this );
//	DrawTimeCursor( &rDC, true );

	CDemopajaDoc*	pDoc = GetDoc();
	pDoc->UpdateAllViews( NULL, REDRAW_VIEWS );
}
*/

void CTimeLineBar::SetTimecursor( PajaTypes::int32 i32Time )
{
	m_pDoc->SetTimecursor( i32Time );

	if( !m_bEditingFrame ) {
		m_bEditingFrame = true;

		int32	i32TimeScale = m_pDoc->GetBeatsPerMin() * m_pDoc->GetQNotesPerBeat() * 256;
		int32	i32Hours, i32Mins, i32Secs, i32Frames;
		i32Secs = abs( (i32Time * 60 / i32TimeScale) % 60 );
		i32Mins = abs( (i32Time / i32TimeScale) % 60 );
		i32Hours = abs( i32Time / 60 / i32TimeScale );
		i32Frames = m_pDoc->TimeToFrame( abs( i32Time - ((i32Hours * 3600 + i32Mins * 60 + i32Secs) * i32TimeScale) / 60 ) );

		CString	sTime;
		sTime.Format( "%d:%02d:%02d:%02d", i32Hours, i32Mins, i32Secs, i32Frames );
		m_rFrameEdit.SetWindowText( sTime );
		m_bEditingFrame = false;
	}
}

int32 CTimeLineBar::GetTimecursor()
{
	return m_pDoc->GetTimecursor();
}

void CTimeLineBar::OnDestroy() 
{
	CDemopajaApp*	pApp = (CDemopajaApp*)AfxGetApp();
	pApp->WriteProfileInt( "Settings", "LayerValueSize", m_i32LayerValueSize );
	pApp->WriteProfileInt( "Settings", "LayerListWidth", m_rListRect.right );
	pApp->WriteProfileInt( "Settings", "TimelineFrameWidthPx", m_i32FrameWidthInPixels );
	pApp->WriteProfileInt( "Settings", "WaveformHeight", m_i32WaveformHeight );

	m_rDropTarget.Revoke();
	baseCMyBar::OnDestroy();
}

UINT CTimeLineBar::OnGetDlgCode() 
{
	UINT	result = baseCMyBar::OnGetDlgCode();
	result |= DLGC_WANTALLKEYS;
	return result;
}

void CTimeLineBar::UpdateTimeCursor( PajaTypes::int32 i32Time )
{
	m_i32OldTimeCursor = GetTimecursor();

	SetTimecursor( i32Time );

	uint32		ui32NumValues = m_rTimelineRect.Width() / m_i32FrameWidthInPixels - 1;
	int32		i32MaxTime = m_i32ViewTimeStart + m_pDoc->FrameToTime( ui32NumValues );

	if( GetTimecursor() < m_i32ViewTimeStart || GetTimecursor() > i32MaxTime ) {
		SetTimeStart( GetTimecursor() - (i32MaxTime - m_i32ViewTimeStart) / 4 );
		Invalidate( FALSE );
	}
	else {
//		RedrawTimecursor();
		m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
	}
}


int32 CTimeLineBar::HitTestMarker( CPoint rPt, int32* pIndexOut, int32* pTimeOut )
{
	if( pIndexOut )
		*pIndexOut = -1;
	if( pTimeOut )
		*pTimeOut = 0;

	// Check if we hit a marker
	SceneC*	pScene = m_pDoc->GetCurrentScene();

	CDC	rDC;
	rDC.CreateIC( "DISPLAY", NULL, NULL, NULL );

	CFont*	pOldFont = rDC.SelectObject( &m_rSmallFont );

	uint32	ui32NumValues = m_rTimelineRect.Width() / m_i32FrameWidthInPixels + 1;
	int32	i32MinTime = m_i32ViewTimeStart;
	int32	i32MaxTime = m_i32ViewTimeStart + m_pDoc->FrameToTime( ui32NumValues );

	for( uint32 i = 0; i < pScene->get_marker_count(); i++ ) {
		int32	i32Time = pScene->get_marker_time( i );
		if( i32Time >= i32MinTime && i32Time < i32MaxTime ) {

			CString	sName = pScene->get_marker_name( i );
			CSize	rSize = rDC.GetTextExtent( sName );

			int32	i32MinX = m_pDoc->TimeToFrame( i32Time - i32MinTime ) * m_i32FrameWidthInPixels + m_i32FrameWidthInPixels / 2 + m_rWaveformRect.left;
			int32	i32MaxX = i32MinX + rSize.cx + 4;

			if( rPt.x >= i32MinX && rPt.x < i32MaxX ) {

				if( pIndexOut )
					*pIndexOut = i;
				if( pTimeOut )
					*pTimeOut = i32Time;

				rDC.SelectObject( pOldFont );
				rDC.DeleteDC();
				return HIT_MARKER_LABEL;
			}
		}
	}
	rDC.SelectObject( pOldFont );
	rDC.DeleteDC();
	return HIT_MARKER_NONE;
}

void CTimeLineBar::UpdateSceneTab()
{
	int32							i;
	typedef std::vector<SceneC*>	TSceneVec;
	typedef TSceneVec::iterator		TSceneIter;
	TSceneVec						rScenes;
	SceneC*							pCurScene = m_pDoc->GetCurrentScene();

	uint32 ui32SceneCount = m_pDoc->GetSceneCount();
	for( i = 0; i < ui32SceneCount; i++ )
		rScenes.push_back( m_pDoc->GetScene( i ) );

	
	TCITEM	rItem;
	char	szText[256];
	rItem.pszText = szText;

	for( i = m_rSceneTab.GetItemCount() - 1; i >= 0; i-- ) {
		rItem.mask = TCIF_PARAM;
		m_rSceneTab.GetItem( i, &rItem );
		
		TSceneIter it = std::find( rScenes.begin(), rScenes.end(), (SceneC*)rItem.lParam );
		if( it == rScenes.end() ) {
			m_rSceneTab.DeleteItem( i );
		}
		else {
			rItem.mask = TCIF_TEXT;
			strcpy( szText, (*it)->get_name() );
			m_rSceneTab.SetItem( i, &rItem );
			if( *it == pCurScene ) m_rSceneTab.SetCurSel( i ); // associated view is active => make it the current selection
			rScenes.erase( it );                // remove view from list
		}
	}
	
	// all remaining views in vChild have to be added as new tabs
	i = m_rSceneTab.GetItemCount();
	for( TSceneIter it = rScenes.begin(), end = rScenes.end(); it != end; ++it ) {
		rItem.mask = TCIF_TEXT | TCIF_PARAM | TCIF_IMAGE;
		rItem.iImage = 1;
		strcpy( szText, (*it)->get_name() );
		rItem.lParam = LPARAM( *it );
		m_rSceneTab.InsertItem( i, &rItem );
		if( *it == pCurScene ) m_rSceneTab.SetCurSel( i );
		++i;
	}

}


void CTimeLineBar::OnSelchangeSceneSelect( NMHDR* pNMHDR, LRESULT* pResult ) 
{
	int	iCurSel = m_rSceneTab.GetCurSel();

	TCITEM	rItem;
	rItem.mask = TCIF_PARAM;
	m_rSceneTab.GetItem( iCurSel, &rItem );

	// Choose a scene
	SceneC*	pSelScene = (SceneC*)rItem.lParam;

	uint32	ui32SceneCount = m_pDoc->GetSceneCount();

	int32	i32Sel = 0;
	for( uint32 i = 0; i < ui32SceneCount; i++ ) {
		SceneC*	pScene = m_pDoc->GetScene( i );
		if( pScene == pSelScene ) {
			i32Sel = i;
			break;
		}
	}

	m_pDoc->SetCurrentScene( i32Sel );
	SceneC*	pScene = m_pDoc->GetCurrentScene();
	
	// check if the current timeline position needs to be changed
	uint32	ui32NumValues = m_rTimelineRect.Width() / m_i32FrameWidthInPixels + 1;
	int32	i32MinTime = m_i32ViewTimeStart;
	int32	i32MaxTime = m_i32ViewTimeStart + m_pDoc->FrameToTime( ui32NumValues );
	if( GetTimecursor() < i32MinTime || GetTimecursor() > i32MaxTime ) {
		SetTimeStart( GetTimecursor() - (i32MaxTime - i32MinTime) / 4 );
	}

	m_rSceneItemList.build_sceneitem_list( m_pDoc->GetCurrentScene() );
	m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
}


void CTimeLineBar::OnRClickSceneSelect( NMHDR* pNMHDR, LRESULT* pResult ) 
{
	TCHITTESTINFO	rHit;
	CPoint			rPoint;
	// Get mouse pos
	::GetCursorPos( &rPoint );
	rHit.pt = rPoint;
	m_rSceneTab.ScreenToClient( &rHit.pt );

	// Hit test
	int32	i32Sel = m_rSceneTab.HitTest( &rHit );
	if( i32Sel != -1 ) {
		// If we hit a tab, select it
		m_rSceneTab.SetCurSel( i32Sel );

		TCITEM	rItem;
		rItem.mask = TCIF_PARAM;
		m_rSceneTab.GetItem( i32Sel, &rItem );

		// Choose a scene
		SceneC*	pSelScene = (SceneC*)rItem.lParam;

		uint32	ui32SceneCount = m_pDoc->GetSceneCount();

		int32	i32Sel = 0;
		for( uint32 i = 0; i < ui32SceneCount; i++ ) {
			SceneC*	pScene = m_pDoc->GetScene( i );
			if( pScene == pSelScene ) {
				i32Sel = i;
				break;
			}
		}

		m_pDoc->SetCurrentScene( i32Sel );
		SceneC*	pScene = m_pDoc->GetCurrentScene();
		
		// check if the current timeline position needs to be changed
		uint32	ui32NumValues = m_rTimelineRect.Width() / m_i32FrameWidthInPixels + 1;
		int32	i32MinTime = m_i32ViewTimeStart;
		int32	i32MaxTime = m_i32ViewTimeStart + m_pDoc->FrameToTime( ui32NumValues );
		if( GetTimecursor() < i32MinTime || GetTimecursor() > i32MaxTime ) {
			SetTimeStart( GetTimecursor() - (i32MaxTime - i32MinTime) / 4 );
		}
	}


	// Do menu
	CFlatPopupMenu	rMenu;
	rMenu.SetFont( "Arial" );
	rMenu.SetFontSize( 8 );
	rMenu.Create( AfxGetInstanceHandle(), IDB_FILELISTRIGHT );

	rMenu.AppendItem( 0, "Create Scene", 1, 5 );
	rMenu.AppendItem( 0, "Properties...", 2 );

	int32	i32Id = rMenu.Track( rPoint.x, rPoint.y + 1, NULL, true );

	CDemopajaDoc*	pDoc = GetDoc();
	ASSERT( pDoc );

	if( i32Id == 1 ) {
		// New scene
		pDoc->CreateScene( 0 );
		pDoc->UpdateFileReferences();
	}
	else if( i32Id == 2 ) {
		// Properties

		// find current scne from the file list
		FileListC*	pFileList = pDoc->GetFileList();

		int32	i32FileIdx = -1;

		for( uint32 i = 0; i < pFileList->get_file_count(); i++ ) {
			ImportableI*	pImp = pFileList->get_file( i )->get_importable();
			if( pImp && pImp->get_class_id() == CLASS_SUBSCENE_IMPORTABLE ) {
				SceneImportC*	pSceneImp = (SceneImportC*)pImp;
				if( pSceneImp->get_scene() == m_pDoc->GetCurrentScene() ) {
					i32FileIdx = i;
					break;
				}
			}
		}

		if( i32FileIdx == -1 ) {
			// the main tab, prompt demo properties
			pDoc->AskDemoProperties();
			pDoc->SetModifiedFlag();
		}
		else {
			// prompt file properties
			pDoc->PromptFileProperties( i32FileIdx );
			pDoc->UpdateFileReferences();
		}
	}

	m_pDoc->NotifyViews( NOTIFY_REDRAW_ALL );
}




void CTimeLineBar::ParamIntCombo( const CPoint& rPt, ParamIntC* pParam, int32 i32Time )
{
	m_pDoc->ParameterSaveState( pParam, i32Time );

	i32Time -= m_pDoc->ParameterGetTimeOffset( pParam );

	CFlatPopupMenu	rMenu;
	rMenu.SetFont( "Arial" );
	rMenu.SetFontSize( 7 );
	rMenu.SetMinWidth( m_i32LayerValueSize + 1 );
	rMenu.SetColor( CFlatPopupMenu::colorBackground, ::GetSysColor( COLOR_WINDOW ) );
	rMenu.Create( AfxGetInstanceHandle() );

	for( uint32 i = 0; i < pParam->get_label_count(); i++ )
		rMenu.AppendItem( 0, pParam->get_label_name( i ), i + 1 );

	CPoint	rPoint = rPt;
	ClientToScreen( &rPoint );
	int32	i32Id = rMenu.Track( rPoint.x, rPoint.y + 1, NULL, true );

	if( i32Id ) {
		m_pDoc->HandleParamNotify( pParam->set_val( i32Time, pParam->get_label_value( i32Id - 1 ) ) );
	}
}

void CTimeLineBar::ParamFileCombo( const CPoint& rPt, ParamFileC* pParam, int32 i32Time )
{
	m_pDoc->ParameterSaveState( pParam, i32Time );

	i32Time -= m_pDoc->ParameterGetTimeOffset( pParam );

	CFlatPopupMenu	rMenu;
	rMenu.SetFont( "Arial" );
	rMenu.SetFontSize( 7 );
	rMenu.SetMinWidth( m_i32LayerValueSize + 1 );
	rMenu.SetColor( CFlatPopupMenu::colorBackground, ::GetSysColor( COLOR_WINDOW ) );
	rMenu.Create( AfxGetInstanceHandle() );

	rMenu.AppendItem( 0, "<Empty>", 0xffffffff );

	FileListC*		pFileList = m_pDoc->GetFileList();
	std::string		sStr;
	char			szFname[_MAX_FNAME];
	char			szExt[_MAX_EXT];
	FileHandleC*	pParamHandle = pParam->get_file( i32Time );

	for( uint32 i = 0; i < pFileList->get_file_count(); i++ ) {

		FileHandleC*	pHandle = pFileList->get_file( i );
		ImportableI*	pImp = 0;

		if( !pHandle )
			continue;
		
		pImp = pHandle->get_importable();
		if( !pImp )
			continue;

		if( (pParam->get_class_filter() == NULL_CLASSID ||
			 pParam->get_class_filter() == pImp->get_class_id()) &&
			(pParam->get_super_class_filter() == NULL_SUPERCLASS ||
			pParam->get_super_class_filter() == pImp->get_super_class_id()) ) {

			_splitpath( pImp->get_filename(), 0, 0, szFname, szExt );
			sStr = szFname;
			sStr += szExt;
		
			rMenu.AppendItem( 0, sStr.c_str(), i + 1 );
		}
	}


	CPoint	rPoint = rPt;
	ClientToScreen( &rPoint );
	int32	i32Id = rMenu.Track( rPoint.x, rPoint.y + 1, NULL, true );

	if( i32Id ) {

		FileHandleC*	pHandle = 0;
		if( i32Id == 0xffffffff ) {
			pHandle = 0;
		}
		else {
			pHandle = pFileList->get_file( i32Id - 1 );
		}

		m_pDoc->BeginDeferHandleParamNotify();

		m_pDoc->HandleParamNotify( pParam->set_file( i32Time, pHandle ) );
		// if the set file causes recursion, set the file to <empty>
		if( m_pDoc->CheckFileRecursion() ) {
			m_pDoc->HandleParamNotify( pParam->set_file( i32Time, 0 ) );
		}

		m_pDoc->EndDeferHandleParamNotify();
	}
}

int32 CTimeLineBar::GetListPos()
{
	int32	i32Pos = 0;
	i32Pos = m_pDoc->GetSceneLayerListPos();

	return i32Pos;
}

void CTimeLineBar::SetListPos( int32 i32Pos )
{
	m_pDoc->SetSceneLayerListPos( i32Pos );
}


bool CTimeLineBar::OnSceneMouseMove( UINT nFlags, CPoint point )
{
	// get the latest state
	bool	bSpacePressed = (::GetAsyncKeyState( VK_SPACE ) & 0x8000 ) != 0;

	bool	bRes = true;

	// initially set the default cursor.
	m_hCursor = 0;

	if( m_eTrackAction == TRACKING_SIZEPARAM ) {
		int32	i32DeltaY = point.y - m_rTrackOrigPt.y;
		ParamI*	pParam = m_pTrackItem->get_parameter();
		if( pParam ) {
			int32	i32Height = pParam->get_expanded_height() + i32DeltaY;
			if( i32Height < m_rSceneItemList.get_item_height() * 2 )
				i32Height = m_rSceneItemList.get_item_height() * 2;
			pParam->set_expanded_height( i32Height );
		}
		m_rTrackOrigPt = point;
		m_rSceneItemList.layer_list_modified();
		SetScrollRanges();
		m_rListScroll.SetScrollPos( GetListPos() );
		Invalidate( FALSE );
	}
	else if( m_eTrackAction == TRACKING_KEYS ) {
		
		int32	i32DeltaTime = m_pDoc->FrameToTime( (point.x - m_rTimelineRect.left) / m_i32FrameWidthInPixels ) + m_i32ViewTimeStart;

		if( point.x < m_rTimelineRect.left + m_i32FrameWidthInPixels ) {
			if( !m_ui32ScrollTimeID )
				m_ui32ScrollTimeID = SetTimer( TIMER_MOVEKEYS_LEFT, 50, NULL );
		}
		else if( point.x > m_rTimelineRect.right - m_i32FrameWidthInPixels ) {
			if( !m_ui32ScrollTimeID )
				m_ui32ScrollTimeID = SetTimer( TIMER_MOVEKEYS_RIGHT, 50, NULL );
		}
		else {
			if( m_ui32ScrollTimeID ) {
				KillTimer( m_ui32ScrollTimeID );
				m_ui32ScrollTimeID = 0;
			}
			m_pDoc->KeysMoveTime( i32DeltaTime - m_i32TrackOrigTime );
			m_i32TrackOrigTime = i32DeltaTime;
			m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
		}
	}
	else if( m_eTrackAction == TRACKING_TIMESEGMENT ) {

		if( point.x < m_rTimelineRect.left + m_i32FrameWidthInPixels ) {
			if( !m_ui32ScrollTimeID )
				m_ui32ScrollTimeID = SetTimer( TIMER_MOVETIMESEGMENT_LEFT, 50, NULL );
		}
		else if( point.x > m_rTimelineRect.right - m_i32FrameWidthInPixels ) {
			if( !m_ui32ScrollTimeID )
				m_ui32ScrollTimeID = SetTimer( TIMER_MOVETIMESEGMENT_RIGHT, 50, NULL );
		}
		else {
			if( m_ui32ScrollTimeID ) {
				KillTimer( m_ui32ScrollTimeID );
				m_ui32ScrollTimeID = 0;
			}

			TimeSegmentC*	pSeg;
			if( m_pTrackItem->get_type() == SCENEITEM_LAYER )
				pSeg = m_pTrackItem->get_layer()->get_timesegment();
			else if( m_pTrackItem->get_type() == SCENEITEM_EFFECT )
				pSeg = m_pTrackItem->get_effect()->get_timesegment();

			int32	i32Time = m_pDoc->FrameToTime( (m_rTrackOrigPt.x - point.x) / m_i32FrameWidthInPixels );
			pSeg->set_segment_start( pSeg->get_segment_start() + (m_i32TrackOrigTime - i32Time) );
			m_i32TrackOrigTime = i32Time;

			// Invalidate sampled controller data
			m_rSceneItemList.remove_cont_sample_data();

			m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
		}
	}
	else if( m_eTrackAction == TRACKING_KEYVALUES && m_pTrackItem ) {
		// make sure we have the sampled data
		if( m_pTrackItem->get_controller_sampledata()->get_sample_count() ) {

			int32	i32DefaultItemHeight = m_rSceneItemList.get_item_height();
			int32	i32ItemHeight = m_pTrackItem->get_height() - i32DefaultItemHeight - 10;

			float32	f32Min = m_pTrackItem->get_controller_sampledata()->get_min();
			float32	f32Max = m_pTrackItem->get_controller_sampledata()->get_max();
			float32	f32Scale = (f32Max - f32Min) / (float)i32ItemHeight;
			float32	f32DeltaValue = (float)(m_rTrackOrigPt.y - point.y) * f32Scale;

			m_pDoc->KeysMoveValue( m_pTrackItem, m_ui32TrackEditingChannel, f32DeltaValue );
		}
		m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
	}
	else if( m_eTrackAction == TRACKING_BOXSELECT ) {

		if( point.x < m_rTimelineRect.left + m_i32FrameWidthInPixels ) {
			if( !m_ui32ScrollTimeID )
				m_ui32ScrollTimeID = SetTimer( TIMER_BOXSELECT_LEFT, 50, NULL );
			m_i32BoxSelectCurY = point.y + GetListPos() - m_rTimelineRect.top;
		}
		else if( point.x > m_rTimelineRect.right - m_i32FrameWidthInPixels ) {
			if( !m_ui32ScrollTimeID )
				m_ui32ScrollTimeID = SetTimer( TIMER_BOXSELECT_RIGHT, 50, NULL );
			m_i32BoxSelectCurY = point.y + GetListPos() - m_rTimelineRect.top;
		}
		else if( point.y < m_rTimelineRect.top - 2 ) {
			if( !m_ui32ScrollTimeID )
				m_ui32ScrollTimeID = SetTimer( TIMER_BOXSELECT_TOP, 50, NULL );
			m_i32BoxSelectCurTime = m_pDoc->FrameToTime( (point.x - m_rTimelineRect.left) / m_i32FrameWidthInPixels ) + m_i32ViewTimeStart;
		}
		else if( point.y > m_rTimelineRect.bottom - 5 ) {
			if( !m_ui32ScrollTimeID )
				m_ui32ScrollTimeID = SetTimer( TIMER_BOXSELECT_BOTTOM, 50, NULL );
			m_i32BoxSelectCurTime = m_pDoc->FrameToTime( (point.x - m_rTimelineRect.left) / m_i32FrameWidthInPixels ) + m_i32ViewTimeStart;
		}
		else {
			if( m_ui32ScrollTimeID ) {
				KillTimer( m_ui32ScrollTimeID );
				m_ui32ScrollTimeID = 0;
			}

			// new selection
			m_i32BoxSelectCurTime = m_pDoc->FrameToTime( (point.x - m_rTimelineRect.left) / m_i32FrameWidthInPixels ) + m_i32ViewTimeStart;
			m_i32BoxSelectCurY = point.y + GetListPos() - m_rTimelineRect.top;

			// draw the selection
			DrawBoxSelect();
		}
	}
	else if( m_eTrackAction == TRACKING_LAYERDRAG ) {

		int32	i32Y = point.y - (m_rListRect.top + 1) + GetListPos();

		SceneItemC*	pItem;

		for( int32 i = m_i32FirstVisible; i < m_i32LastVisible; i++ ) {
			pItem = m_rSceneItemList.get_sceneitem( i );
			pItem = m_rSceneItemList.get_sceneitem( i );
			if( pItem->get_type() == SCENEITEM_LAYER &&
				i32Y >= (pItem->get_y() - pItem->get_height() / 2) && i32Y < (pItem->get_y() + pItem->get_height() / 2) ) {
				// hit found
				m_rTrackTargetItem = *pItem;
				m_rTrackTargetItem.delete_controller_sampledata();
				m_bTrackInsertAfter = false;
				break;
			}
		}

		// check if it is before the first item
		pItem = m_rSceneItemList.get_sceneitem( m_i32FirstVisible );
		if( pItem && i32Y < (pItem->get_y() - pItem->get_height() / 2) ) {
			m_rTrackTargetItem = *pItem;
			m_rTrackTargetItem.delete_controller_sampledata();
			m_bTrackInsertAfter = false;
		}

		// check if it is after the last item
		pItem = m_rSceneItemList.get_sceneitem( m_i32LastVisible - 1 );
		if( pItem && i32Y >= (pItem->get_y() + pItem->get_height() / 2) ) {
			m_rTrackTargetItem = *pItem;
			m_rTrackTargetItem.delete_controller_sampledata();
			m_bTrackInsertAfter = true;
		}

		InvalidateRect( m_rListRect );
	}
	else if( m_eTrackAction == TRACKING_EFFECTDRAG ) {

		int32	i32Y = point.y - (m_rListRect.top + 1) + GetListPos();

		SceneItemC*	pItem;
		SceneItemC*	pItemNext = 0;

		for( int32 i = m_i32FirstVisible; i < m_i32LastVisible; i++ ) {
			pItem = m_rSceneItemList.get_sceneitem( i );

			if( !pItem )
				continue;

			if( (i + 1) < m_i32LastVisible )
				pItemNext = m_rSceneItemList.get_sceneitem( i + 1 );
			else
				pItemNext = 0;

			if( pItem->get_effect() ) {
				// drag among the effects

				// normal case
				if( i32Y >= (pItem->get_y() - pItem->get_height() / 2) && i32Y < (pItem->get_y() + pItem->get_height() / 2) ) {
					// hit found
					m_rTrackTargetItem = *pItem;
					m_rTrackTargetItem.delete_controller_sampledata();
					m_bTrackInsertAfter = false;
					break;
				}

				// last item in a layer
				if( pItemNext && !pItemNext->get_effect() ) {
					if( i32Y >= (pItem->get_y() + pItem->get_height() / 2) && i32Y < (pItem->get_y() + pItem->get_height()) ) {
						// hit found
						m_rTrackTargetItem = *pItem;
						m_rTrackTargetItem.delete_controller_sampledata();
						m_bTrackInsertAfter = true;
						break;
					}
				}
			}
			else {
				// the no-effects-in-the-layer thing
				// last item in a layer
				if( pItemNext && !pItemNext->get_effect() ) {
					if( i32Y >= (pItem->get_y() + pItem->get_height() / 2) && i32Y < (pItem->get_y() + pItem->get_height()) ) {
						// hit found
						m_rTrackTargetItem = *pItem;
						m_rTrackTargetItem.delete_controller_sampledata();
						m_bTrackInsertAfter = false;
						break;
					}
				}
			}
		}

		// check if it is before the first item
		pItem = m_rSceneItemList.get_sceneitem( m_i32FirstVisible );
		if( pItem && i32Y < (pItem->get_y() - pItem->get_height() / 2) ) {
			m_rTrackTargetItem = *pItem;
			m_rTrackTargetItem.delete_controller_sampledata();
			m_bTrackInsertAfter = false;
		}

		// check if it is after the last item
		pItem = m_rSceneItemList.get_sceneitem( m_i32LastVisible - 1 );
		if( pItem && i32Y >= (pItem->get_y() + pItem->get_height() / 2) ) {
			m_rTrackTargetItem = *pItem;
			m_rTrackTargetItem.delete_controller_sampledata();
			m_bTrackInsertAfter = true;
		}

		InvalidateRect( m_rListRect );
	}
	else if( m_eTrackAction == TRACKING_MARKER ) {

		uint32	ui32NumValues = m_rTimelineRect.Width() / m_i32FrameWidthInPixels + 1;
		int32	i32MinTime = m_i32ViewTimeStart;
		int32	i32MaxTime = m_i32ViewTimeStart + m_pDoc->FrameToTime( ui32NumValues );

		SceneC*	pScene = m_pDoc->GetCurrentScene();
		int32	i32DeltaTime = m_pDoc->FrameToTime( (point.x - m_rTrackOrigPt.x) / m_i32FrameWidthInPixels );
		pScene->set_marker_time( m_i32TrackValue, m_i32TrackOrigTime + i32DeltaTime );

		Invalidate( FALSE );
	}
	else if( m_eTrackAction == TRACKING_LOCK ) {
		SceneItemC*	pItem = 0;
		int32	i32Hit = HitTestSceneList( point, &pItem );
		if( i32Hit == HIT_LIST_LOCK ) {
			if( m_i32TrackValue == ~ITEM_LOCKED ) {
				m_rSceneItemList.set_item_flags( pItem, pItem->get_flags() & ~ITEM_LOCKED );
			}
			else {
				m_rSceneItemList.set_item_flags( pItem, pItem->get_flags() | ITEM_LOCKED );
			}
			m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
		}
	}
	else if( m_eTrackAction == TRACKING_EYE ) {
		SceneItemC*	pItem = 0;
		int32	i32Hit = HitTestSceneList( point, &pItem );
		if( i32Hit == HIT_LIST_EYE ) {
			if( m_i32TrackValue == ~ITEM_VISIBLE ) {
				m_rSceneItemList.set_item_flags( pItem, pItem->get_flags() & ~ITEM_VISIBLE );
			}
			else {
				m_rSceneItemList.set_item_flags( pItem, pItem->get_flags() | ITEM_VISIBLE );
			}
			m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
		}
	}
	else if( m_eTrackAction == TRACKING_SPINNERX || m_eTrackAction == TRACKING_SPINNERY || m_eTrackAction == TRACKING_SPINNERZ ) {
		if( m_pTrackItem && m_ui32TrackEditingChannel < KEY_MAXCHANNELS ) {
			ParamI*	pParam = m_pTrackItem->get_parameter();
			float32	f32Inc = ((float32)point.y - m_rTrackOrigPt.y) * pParam->get_increment();
			if( nFlags & MK_CONTROL )
				f32Inc *= 10.0f;
			float32	f32Val = m_f32TrackOrigVal - f32Inc;
			m_pDoc->HandleParamNotify( m_pDoc->ParameterSetValue( pParam, m_i32TrackOrigTime, m_ui32TrackEditingChannel, f32Val ) );
			m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
		}
	}
	else {
		bRes = false;
	}

	//
	// find cursor to set
	//

	CRect	rHeaderRect;
	rHeaderRect = m_rListRect;
	rHeaderRect.bottom = rHeaderRect.top;
	rHeaderRect.top -= 22;

	if( bSpacePressed ) {
		m_hCursor = AfxGetApp()->LoadCursor( IDC_DPHAND );
	}
	else if( m_eTrackAction == TRACKING_LAYERDRAG || m_eTrackAction == TRACKING_EFFECTDRAG ) {
		m_hCursor = AfxGetApp()->LoadCursor( IDC_DPHANDDRAG );
		if( GetCapture() == this )
			::SetCursor( m_hCursor );
	}
	else {
		SceneItemC*	pItem = 0;
		int32	i32Hit = HitTestSceneList( point, &pItem );
		if( i32Hit == HIT_LIST_NONE )
			i32Hit = HitTestListHeader( point );
		if( m_rTimelineRect.PtInRect( point ) ) {
			if( i32Hit == HIT_LIST_ITEM ) {
				int32	i32TSegHit = HitTestTimeSegment( point, pItem );
				if( i32TSegHit == HIT_TSEG_KEY )
					m_hCursor = AfxGetApp()->LoadCursor( IDC_MOVE_KEY );
				else if( i32TSegHit == HIT_TSEG_BAR )
					m_hCursor = AfxGetApp()->LoadCursor( IDC_MOVE_TIME );

				int32	i32ContHit = HitTestController( point, pItem );
				if( i32ContHit == HIT_CONT_KEY_TIME )
					m_hCursor = AfxGetApp()->LoadCursor( IDC_MOVE_KEY );
				else if( i32ContHit == HIT_CONT_KEY_VALUE )
					m_hCursor = AfxGetApp()->LoadCursor( IDC_MOVE_KEY_VALUE );
			}
			else if( i32Hit & HIT_LIST_SIZEPARAM ) {
				m_hCursor = AfxGetApp()->LoadCursor( IDC_SIZEY );
			}
		}
		else if( m_rListRect.PtInRect( point ) ) {
			if( i32Hit & HIT_LIST_VALUE )
				m_hCursor = AfxGetApp()->LoadCursor( IDC_FINGER );
			else if( i32Hit & HIT_LIST_SPINNERX )
				m_hCursor = AfxGetApp()->LoadCursor( IDC_ADJUSTY );
			else if( i32Hit & HIT_LIST_SPINNERY )
				m_hCursor = AfxGetApp()->LoadCursor( IDC_ADJUSTY );
			else if( i32Hit & HIT_LIST_SPINNERZ )
				m_hCursor = AfxGetApp()->LoadCursor( IDC_ADJUSTY );
			else if( i32Hit & HIT_LIST_FILECOMBO )
				m_hCursor = 0;
			else if( i32Hit & HIT_LIST_INTCOMBO )
				m_hCursor = 0;
		}
		else if( rHeaderRect.PtInRect( point ) ) {
			if( i32Hit & HIT_LIST_VALUESEPARATOR )
				m_hCursor = AfxGetApp()->LoadCursor( IDC_SIZEX );
			else if( i32Hit & HIT_LIST_SEPARATOR )
				m_hCursor = AfxGetApp()->LoadCursor( IDC_SIZEX );
		}
		else if( m_rWaveformRect.PtInRect( point ) ) { // && point.y >= (m_rRulerRect.top + 15) ) {
			if( HitTestMarker( point, NULL, NULL ) == HIT_MARKER_LABEL )
				m_hCursor = AfxGetApp()->LoadCursor( IDC_MOVE_TIME );
		}
	}

	return bRes;
}


bool CTimeLineBar::OnSceneLButtonDown( UINT nFlags, CPoint point )
{
	bool	bInTimeline = false, bInLayerList = false;

	if( m_rListRect.PtInRect( point ) ) {
		bInLayerList = true;
	}
	if( m_rTimelineRect.PtInRect( point ) ) {
		bInTimeline = true;
	}

	SceneItemC*	pItem = 0;
	int32	i32Hit = HitTestSceneList( point, &pItem );

	if( bInLayerList && pItem ) {

		if( i32Hit == HIT_LIST_EYE ) {
			if( pItem->get_flags() & ITEM_VISIBLE ) {
				m_rSceneItemList.set_item_flags( pItem, pItem->get_flags() & ~ITEM_VISIBLE );
				m_i32TrackValue = ~ITEM_VISIBLE;
			}
			else {
				m_rSceneItemList.set_item_flags( pItem, pItem->get_flags() | ITEM_VISIBLE );
				m_i32TrackValue = ITEM_VISIBLE;
			}
			m_eTrackAction = TRACKING_EYE;
			m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
		}
		else if( i32Hit == HIT_LIST_LOCK ) {
			if( pItem->get_flags() & ITEM_LOCKED ) {
				m_rSceneItemList.set_item_flags( pItem, pItem->get_flags() & ~ITEM_LOCKED );
				m_i32TrackValue = ~ITEM_LOCKED;
			}
			else {
				m_rSceneItemList.set_item_flags( pItem, pItem->get_flags() | ITEM_LOCKED );
				m_i32TrackValue = ITEM_LOCKED;
			}
			m_eTrackAction = TRACKING_LOCK;
			m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
		}
		else if( i32Hit == HIT_LIST_ARROW ) {
			if( pItem->get_flags() & ITEM_EXPANDED )
				m_rSceneItemList.set_item_flags( pItem, pItem->get_flags() & ~ITEM_EXPANDED );
			else {
				m_rSceneItemList.set_item_flags( pItem, pItem->get_flags() | ITEM_EXPANDED );

				if( pItem->get_type() == SCENEITEM_PARAMETER ) {
					// if less than 50% of the expanded size is show, scroll view.
					ParamI*	pParam = pItem->get_parameter();
					if( pParam ) {
						int32	i32ItemHeight = pParam->get_expanded_height();
						int32	i32Height = m_rListRect.Height() + GetListPos();
						if( (pItem->get_y() + i32ItemHeight) > i32Height ) {
							SetListPos( GetListPos() + (pItem->get_y() + i32ItemHeight) - i32Height + 5 );
						}
					}
				}
			}

			// deselect all keys
			m_pDoc->KeysDeselectAll();
			SetScrollRanges();	// length may have changed
			m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
		}
		else if( i32Hit == HIT_LIST_ANIM ) {

			ParamI*	pParam = pItem->get_parameter();

			if( pParam ) {
				m_pDoc->ParameterToggleAnim( pParam, GetTimecursor() );
				m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
			}
		}
		else if( i32Hit == HIT_LIST_ITEM ) {

			m_pDoc->DelFlagsAll( ITEM_LAYER | ITEM_EFFECT | ITEM_GIZMO | ITEM_PARAMETER, ITEM_SELECTED );
			m_rSceneItemList.set_item_flags( pItem, pItem->get_flags() | ITEM_SELECTED );
			m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );

			m_rTrackTargetItem = *pItem;
			m_rTrackTargetItem.delete_controller_sampledata();

			// start tracking
			if( pItem->get_type() == SCENEITEM_LAYER ) {
				SetCapture();
				m_rTrackOrigPt = point;
				m_eTrackAction = TRACKING_LAYERDRAG;
				m_pTrackItem = pItem;
			}
			else if( pItem->get_type() == SCENEITEM_EFFECT ) {
				SetCapture();
				m_rTrackOrigPt = point;
				m_eTrackAction = TRACKING_EFFECTDRAG;
				m_pTrackItem = pItem;
			}
		}
		else if( i32Hit == HIT_LIST_VALUE ) {

			m_pDoc->DelFlagsAll( ITEM_LAYER | ITEM_EFFECT | ITEM_GIZMO | ITEM_PARAMETER, ITEM_SELECTED );
			m_rSceneItemList.set_item_flags( pItem, pItem->get_flags() | ITEM_SELECTED );
			m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );

			if( pItem->get_type() == SCENEITEM_LAYER ) {
				LayerC*		pLayer = pItem->get_layer();
				m_pDoc->TimeSegmentTypeIn( pLayer, GetTimecursor() );
				m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
			}
			else if( pItem->get_type() == SCENEITEM_EFFECT ) {
				EffectI*	pEffect = pItem->get_effect();
				m_pDoc->TimeSegmentTypeIn( pEffect, GetTimecursor() );
				m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
			}
			else if( pItem->get_type() == SCENEITEM_PARAMETER ) {
				//
				// clicked on parameter value
				//

				ParamI*	pParam = pItem->get_parameter();

				if( pParam ) {
					m_pDoc->ParameterTypeIn( pParam, GetTimecursor() );
					m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
				}
			}
		}
		else if( i32Hit == HIT_LIST_SPINNERX || i32Hit == HIT_LIST_SPINNERY || i32Hit == HIT_LIST_SPINNERZ ) {

			m_pTrackItem = pItem;

			ParamI*	pParam = pItem->get_parameter();
			// save current state
			m_pDoc->ParameterSaveState( pParam, GetTimecursor() );

			m_i32TrackOrigTime = GetTimecursor();

			m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );

			m_rTrackOrigPt = point;
			if( i32Hit == HIT_LIST_SPINNERX ) {
				m_ui32TrackEditingChannel = 0;
				m_f32TrackOrigVal = m_pDoc->ParameterGetValue( pParam, m_i32TrackOrigTime, 0 );
				m_eTrackAction = TRACKING_SPINNERX;
			}
			else if( i32Hit == HIT_LIST_SPINNERY ) {
				m_ui32TrackEditingChannel = 1;
				m_f32TrackOrigVal = m_pDoc->ParameterGetValue( pParam, m_i32TrackOrigTime, 1 );
				m_eTrackAction = TRACKING_SPINNERY;
			}
			else if( i32Hit == HIT_LIST_SPINNERZ ) {
				m_ui32TrackEditingChannel = 2;
				m_f32TrackOrigVal = m_pDoc->ParameterGetValue( pParam, m_i32TrackOrigTime, 2 );
				m_eTrackAction = TRACKING_SPINNERZ;
			}

			m_pDoc->BeginDeferHandleParamNotify();

			SetCapture();
		}
		else if( i32Hit == HIT_LIST_INTCOMBO || i32Hit == HIT_LIST_FILECOMBO ) {
			// save current state
			CPoint	rLeftBottom;
			rLeftBottom.x = m_rListRect.right - m_i32LayerValueSize;
			rLeftBottom.y = m_rListRect.top + pItem->get_y() - GetListPos() + m_rSceneItemList.get_item_height();

			ParamI*	pParam = pItem->get_parameter();

			if( i32Hit == HIT_LIST_INTCOMBO )
				ParamIntCombo( rLeftBottom, (ParamIntC*)pParam, GetTimecursor() );
			else
				ParamFileCombo( rLeftBottom, (ParamFileC*)pParam, GetTimecursor() );

			m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
		}
		else if( i32Hit == HIT_LIST_NEXT_KEY ) {

			if( pItem->get_type() == SCENEITEM_LAYER || pItem->get_type() == SCENEITEM_EFFECT ) {

				int32	i32Time = GetTimecursor();
				TimeSegmentC*	pSeg = 0;
				int32			i32TimeOffset = 0;
				
				if( pItem->get_type() == SCENEITEM_LAYER ) {
					pSeg = pItem->get_layer()->get_timesegment();
				}
				else if( pItem->get_type() == SCENEITEM_EFFECT ) {
					pSeg = pItem->get_effect()->get_timesegment();
					i32TimeOffset = pItem->get_layer()->get_timesegment()->get_segment_start();
				}

				if( pSeg ) {
					bool	bFound = false;
					int32	i32NewTime = 0;
					i32Time -= i32TimeOffset;

					for( uint32 i = 0; i < pSeg->get_key_count(); i++ ) {
						if( i32Time < pSeg->get_key_time( i ) ) {
							i32NewTime = pSeg->get_key_time( i );
							bFound = true;
							break;
						}
					}

					if( !bFound ) {
						if( pSeg->get_key_count() )
							i32NewTime = pSeg->get_key_time( 0 );
						else
							i32NewTime = pSeg->get_segment_start();
					}

					i32NewTime += i32TimeOffset;
					SetTimecursor( i32NewTime );
				}
			}
			else if( pItem->get_type() == SCENEITEM_PARAMETER ) {

				int32	i32Time = GetTimecursor();
				int32	i32NewTime = 0;
				int32	i32TimeOffset = 0;

				i32TimeOffset += pItem->get_layer()->get_timesegment()->get_segment_start();
				i32TimeOffset += pItem->get_effect()->get_timesegment()->get_segment_start();

				i32Time -= i32TimeOffset;

				ParamI*	pParam = pItem->get_parameter();

				if( pParam && pParam->get_controller() ) {
					ControllerC*	pCont = pParam->get_controller();
					bool			bFound = false;

					for( uint32 i = 0; i < pCont->get_key_count(); i++ ) {
						if( i32Time < pCont->get_key( i )->get_time() ) {
							i32NewTime = pCont->get_key( i )->get_time();
							bFound = true;
							break;
						}
					}

					if( !bFound ) {
						if( pCont->get_key_count() )
							i32NewTime = pCont->get_key( 0 )->get_time();
					}

					i32NewTime += i32TimeOffset;
					SetTimecursor( i32NewTime );
				}
			}

			uint32	ui32NumValues = m_rTimelineRect.Width() / m_i32FrameWidthInPixels - 1;
			int32	i32MaxTime = m_i32ViewTimeStart + m_pDoc->FrameToTime( ui32NumValues );
			if( GetTimecursor() < m_i32ViewTimeStart || GetTimecursor() > i32MaxTime ) {
				SetTimeStart( GetTimecursor() - (i32MaxTime - m_i32ViewTimeStart) / 4 );
			}

			m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
		}
		else {
			// deselect all
			m_pDoc->DelFlagsAll( ITEM_LAYER | ITEM_EFFECT | ITEM_GIZMO | ITEM_PARAMETER, ITEM_SELECTED );
			m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
		}
	}
	else if( bInTimeline ) {

		bool	bNoHit = false;

		if( i32Hit == HIT_LIST_ITEM ) {

			if( pItem->get_type() == SCENEITEM_LAYER || pItem->get_type() == SCENEITEM_EFFECT ) {

				KeyC*	pKey;
				int32	i32TSegHit = HitTestTimeSegment( point, pItem, &pKey );

				// we hit a key
				if( i32TSegHit == HIT_TSEG_KEY && pKey ) {
					// multi deselection/selection

					if( nFlags & MK_SHIFT ) {
						// shift is pressed: toggle keys
						if( pKey->get_flags() & KEY_SELECTED )
							m_pDoc->KeysDeselectKey( pItem, pKey->get_time() );
						else
							m_pDoc->KeysSelectKey( pItem, pKey->get_time() );
					}
					else if( !(pKey->get_flags() & KEY_SELECTED) ) {
						// shift wasnt pressed and clicked key was not selected:
						// deselect all, and select the clicked key
						m_pDoc->KeysDeselectAll();
						m_pDoc->KeysSelectKey( pItem, pKey->get_time() );
					}

					// save the selected state
					m_pDoc->KeysSaveStateSelected( "Move Keys" );

					SetCapture();
					m_eTrackAction = TRACKING_KEYS;
					m_rTrackOrigPt = point;
					m_i32TrackOrigTime = m_pDoc->FrameToTime( (point.x - m_rTimelineRect.left) / m_i32FrameWidthInPixels ) + m_i32ViewTimeStart;
				}
				else if( i32TSegHit == HIT_TSEG_BAR ) {
					// Move time segment

					// get the time segment we edit.
					TimeSegmentC*	pSeg;
					if( pItem->get_type() == SCENEITEM_LAYER )
						pSeg = pItem->get_layer()->get_timesegment();
					else if( pItem->get_type() == SCENEITEM_EFFECT )
						pSeg = pItem->get_effect()->get_timesegment();

					if( pSeg ) {
						m_pDoc->TimeSegmentSaveState( pSeg, "Move Time Segment" );
						SetCapture();
						m_eTrackAction = TRACKING_TIMESEGMENT;
						m_rTrackOrigPt = point;
						m_i32TrackOrigTime = 0;
						m_pTrackItem = pItem;
					}
				}
				else
					bNoHit = true;
			}
			else if( pItem->get_type() == SCENEITEM_PARAMETER ) {

				KeyC*	pKey;
				int32	i32ContHit = HitTestController( point, pItem, &pKey, &m_ui32TrackEditingChannel, true );

				// we hit a key
				if( (i32ContHit == HIT_CONT_KEY_TIME || i32ContHit == HIT_CONT_KEY_VALUE) && pKey ) {
					// multi deselection/selection

					if( nFlags & MK_SHIFT ) {
						// shift is pressed: toggle keys
						if( pKey->get_flags() & KEY_SELECTED )
							m_pDoc->KeysDeselectKey( pItem, pKey->get_time() );
						else
							m_pDoc->KeysSelectKey( pItem, pKey->get_time() );
					}
					else if( !(pKey->get_flags() & KEY_SELECTED) ) {
						// shift wasnt pressed and clicked key was not selected:
						// deselect all, and select the clicked key
						m_pDoc->KeysDeselectAll();
						m_pDoc->KeysSelectKey( pItem, pKey->get_time() );
					}

					if( i32ContHit == HIT_CONT_KEY_TIME ) {
						// move time
						m_pDoc->KeysSaveStateSelected( "Move Keys" );

						SetCapture();
						m_eTrackAction = TRACKING_KEYS;
						m_rTrackOrigPt = point;
						m_i32TrackOrigTime = m_pDoc->FrameToTime( (point.x - m_rTimelineRect.left) / m_i32FrameWidthInPixels ) + m_i32ViewTimeStart;
					}
					else {
						// we hit the value handle
						m_pDoc->KeysSaveStateItem( pItem, "Adjust Keys" );

						// prepare to adjust keys on OnMoveMouse
						SetCapture();
						m_eTrackAction = TRACKING_KEYVALUES;
						m_rTrackOrigPt = point;
						m_pTrackItem = pItem;
					}
				}
				else
					bNoHit = true;
			}
			else
				bNoHit = true;

			// Redraw
			m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
		}
		else if( i32Hit & HIT_LIST_SIZEPARAM ) {
			// size curve window
			SetCapture();
			m_eTrackAction = TRACKING_SIZEPARAM;
			m_pTrackItem = pItem;
			m_rTrackOrigPt = point;
		}
		else {
			bNoHit = true;
			m_pDoc->DelFlagsAll( ITEM_LAYER | ITEM_EFFECT | ITEM_GIZMO | ITEM_PARAMETER, ITEM_SELECTED );
			m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
		}

		if( bNoHit ) {
			// and start box select
			m_eTrackAction = TRACKING_BOXSELECT;
			m_rTrackOrigPt = point;

			m_i32BoxSelectStartTime = m_pDoc->FrameToTime( (point.x - m_rTimelineRect.left) / m_i32FrameWidthInPixels ) + m_i32ViewTimeStart;
			m_i32BoxSelectStartY = point.y + GetListPos() - m_rTimelineRect.top;
			m_i32BoxSelectCurTime = m_i32BoxSelectLastTime = m_i32BoxSelectStartTime;
			m_i32BoxSelectCurY = m_i32BoxSelectLastY = m_i32BoxSelectStartY;
			m_bFirstTimeBoxSelect = true;

			DrawBoxSelect();

			SetCapture();
		}
	}
	else if( m_rWaveformRect.PtInRect( point ) ) {
		// Check if we hit a marker
		int32	i32Index = -1;
		int32	i32Time = 0;
		if( HitTestMarker( point, &i32Index, &i32Time ) == HIT_MARKER_LABEL ) {
			if( i32Index >= 0 ) {
				// save current state
				m_pDoc->MarkerSaveState( "Move Marker" );

				SetCapture();
				m_eTrackAction = TRACKING_MARKER;
				m_rTrackOrigPt = point;
				m_i32TrackValue = (uint32)i32Index;
				m_i32TrackOrigTime = i32Time;
				return true;
			}
		}
	}
	else if( m_rRulerRect.PtInRect( point )  ) {
		CRect	rTime;
		rTime.top = m_rRulerRect.top;
		rTime.bottom = m_rRulerRect.bottom;
		rTime.left = m_rRulerRect.left + 1 + m_pDoc->TimeToFrame( GetTimecursor() - m_i32ViewTimeStart ) * m_i32FrameWidthInPixels;
		rTime.right = rTime.left + m_i32FrameWidthInPixels;

		if( point.y < m_rRulerRect.top + 15 ) {
			// time cursor
			if( !rTime.PtInRect( point ) ) {
				SetTimecursor( m_pDoc->FrameToTime( (point.x - m_rTimelineRect.left) / m_i32FrameWidthInPixels ) + m_i32ViewTimeStart );
				Invalidate( FALSE );
			}
			SetCapture();
			m_eTrackAction = TRACKING_TIMECURSOR;
			m_i32TrackOrigTime = GetTimecursor();
			m_rTrackOrigPt = point;
			return true;
		}

		// ruler
		SetCapture();
		m_eTrackAction = TRACKING_VERTRULER;
		m_i32TrackOrigTime = m_i32ViewTimeStart;
		m_rTrackOrigPt = point;
	}
	else {
		return false;
	}

	return true;
}


bool CTimeLineBar::OnSceneLButtonDblClk(UINT nFlags, CPoint point) 
{

	bool	bInTimeline = false;
	bool	bInLayerList = false;
	bool	bInWaveForm = false;

	if( m_rListRect.PtInRect( point ) ) {
		bInLayerList = true;
	}
	if( m_rTimelineRect.PtInRect( point ) ) {
		bInTimeline = true;
	}
	if( m_rWaveformRect.PtInRect( point ) ) {
		bInWaveForm = true;
	}

	SceneItemC*	pItem = 0;
	int32	i32Hit = HitTestSceneList( point, &pItem );

	if( pItem && bInLayerList ) {

		if( i32Hit == HIT_LIST_EYE ) {
			// since this window cathes double clicks if user clicks
			// the visibility flag twice quickly double click action occurs
			if( pItem->get_flags() & ITEM_VISIBLE )
				m_rSceneItemList.set_item_flags( pItem, pItem->get_flags() & ~ITEM_VISIBLE );
			else
				m_rSceneItemList.set_item_flags( pItem, pItem->get_flags() | ITEM_VISIBLE );
			m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
		}
		else if( i32Hit == HIT_LIST_LOCK ) {
			// since this window cathes double clicks if user clicks
			// the locked flag twice quickly double click action occurs
			if( pItem->get_flags() & ITEM_LOCKED )
				m_rSceneItemList.set_item_flags( pItem, pItem->get_flags() & ~ITEM_LOCKED );
			else
				m_rSceneItemList.set_item_flags( pItem, pItem->get_flags() | ITEM_LOCKED );
			m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
		}
		else if( i32Hit == HIT_LIST_ARROW ) {
			// since this window cathes double clicks if user clicks
			// the arrow twice quickly double click action occurs
			if( pItem->get_flags() & ITEM_EXPANDED )
				m_rSceneItemList.set_item_flags( pItem, pItem->get_flags() & ~ITEM_EXPANDED );
			else
				m_rSceneItemList.set_item_flags( pItem, pItem->get_flags() | ITEM_EXPANDED );

			// deselect all keys
			m_pDoc->KeysDeselectAll();

			SetScrollRanges();	// length may have changed
			Invalidate( FALSE );
		}
		else if( i32Hit == HIT_LIST_NEXT_KEY ) {

			if( pItem->get_type() == SCENEITEM_LAYER || pItem->get_type() == SCENEITEM_EFFECT ) {

				int32	i32Time = GetTimecursor();
				TimeSegmentC*	pSeg = 0;
				int32			i32TimeOffset = 0;
				
				if( pItem->get_type() == SCENEITEM_LAYER ) {
					pSeg = pItem->get_layer()->get_timesegment();
				}
				else if( pItem->get_type() == SCENEITEM_EFFECT ) {
					i32TimeOffset = pItem->get_layer()->get_timesegment()->get_segment_start();
					pSeg = pItem->get_effect()->get_timesegment();
				}

				if( pSeg ) {
					bool	bFound = false;
					int32	i32NewTime = 0;
					i32Time -= i32TimeOffset;

					for( uint32 i = 0; i < pSeg->get_key_count(); i++ ) {
						if( i32Time < pSeg->get_key_time( i ) ) {
							i32NewTime = pSeg->get_key_time( i );
							bFound = true;
							break;
						}
					}

					if( !bFound ) {
						if( pSeg->get_key_count() )
							i32NewTime = pSeg->get_key_time( 0 );
						else
							i32NewTime = pSeg->get_segment_start();
					}

					i32NewTime += i32TimeOffset;
					SetTimecursor( i32NewTime );
				}
			}
			else if( pItem->get_type() == SCENEITEM_PARAMETER ) {

				int32	i32Time = GetTimecursor();
				int32	i32NewTime = 0;
				int32	i32TimeOffset = 0;

				LayerC*	pLayer = pItem->get_layer();
				EffectI*	pEffect = pItem->get_effect();
				if( pLayer && pEffect ) {
					i32TimeOffset += pLayer->get_timesegment()->get_segment_start();
					i32TimeOffset += pEffect->get_timesegment()->get_segment_start();
				}

				i32Time -= i32TimeOffset;

				ParamI*	pParam = pItem->get_parameter();

				if( pParam && pParam->get_controller() ) {
					ControllerC*	pCont = pParam->get_controller();
					bool			bFound = false;

					for( uint32 i = 0; i < pCont->get_key_count(); i++ ) {
						if( i32Time < pCont->get_key( i )->get_time() ) {
							i32NewTime = pCont->get_key( i )->get_time();
							bFound = true;
							break;
						}
					}

					if( !bFound ) {
						if( pCont->get_key_count() )
							i32NewTime = pCont->get_key( 0 )->get_time();
					}

					i32NewTime += i32TimeOffset;
					SetTimecursor( i32NewTime );
				}
			}

			uint32	ui32NumValues = m_rTimelineRect.Width() / m_i32FrameWidthInPixels - 1;
			int32	i32MaxTime = m_i32ViewTimeStart + m_pDoc->FrameToTime( ui32NumValues );
			if( GetTimecursor() < m_i32ViewTimeStart || GetTimecursor() > i32MaxTime ) {
				SetTimeStart( GetTimecursor() - (i32MaxTime - m_i32ViewTimeStart) / 4 );
			}

			m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
		}
		else if( (i32Hit == HIT_LIST_ITEM) || (i32Hit == HIT_LIST_VALUE) ) {
			m_pDoc->DelFlagsAll( ITEM_LAYER | ITEM_EFFECT | ITEM_GIZMO | ITEM_PARAMETER, ITEM_SELECTED );
			m_rSceneItemList.set_item_flags( pItem, pItem->get_flags() | ITEM_SELECTED );
			m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
		}
	}
	else if( pItem && bInTimeline ) {
		
		bool	bNoHit = false;

		if( i32Hit == HIT_LIST_ITEM ) {

			if( (pItem->get_type() == SCENEITEM_PARAMETER) && (pItem->get_flags() & ITEM_ANIMATED) ) {

				KeyC*	pKey;
				int32	i32ContHit = HitTestController( point, pItem, &pKey, &m_ui32TrackEditingChannel );

				// we hit a key
				if( (i32ContHit == HIT_CONT_KEY_TIME || i32ContHit == HIT_CONT_KEY_VALUE) && pKey ) {
					// multi deselection/selection

					if( nFlags & MK_SHIFT ) {
						// shift is pressed: toggle keys
						if( pKey->get_flags() & KEY_SELECTED )
							m_pDoc->KeysDeselectKey( pItem, pKey->get_time() );
						else
							m_pDoc->KeysSelectKey( pItem, pKey->get_time() );
					}
					else if( !(pKey->get_flags() & KEY_SELECTED) ) {
						// shift wasnt pressed and clicked key was not selected:
						// deselect all, and select the clicked key
						m_pDoc->KeysDeselectAll();
						m_pDoc->KeysSelectKey( pItem, pKey->get_time() );
					}

					// redraw
					InvalidateRect( m_rTimelineRect );

					ParamI*	pParam = pItem->get_parameter();
					if( pParam ) {
						m_pDoc->ParameterTypeIn( pParam, pKey->get_time() + m_pDoc->ParameterGetTimeOffset( pParam ) );
						m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
					}
				}
				else {
					// we didnt hit a key. create one.
					ParamI*	pParam = pItem->get_parameter();
					if( pParam ) {
						ControllerC*	pCont = pParam->get_controller();
						// Add new key
						int32	i32PtTime = m_pDoc->FrameToTime( (point.x - m_rTimelineRect.left) / m_i32FrameWidthInPixels ) + m_i32ViewTimeStart - m_pDoc->ParameterGetTimeOffset( pParam );
						// create command and make changes
						m_pDoc->ControllerAddKeyAtTime( pCont, i32PtTime );
						m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
					}
				}
			}
			else if( pItem->get_type() == SCENEITEM_LAYER || pItem->get_type() == SCENEITEM_EFFECT ) {

				// find time
				int32	i32PtTime = m_pDoc->FrameToTime( (point.x - m_rTimelineRect.left) / m_i32FrameWidthInPixels ) + m_i32ViewTimeStart;

				if( pItem->get_type() == SCENEITEM_LAYER )
					m_pDoc->TimeSegmentAddKeyAtTime( pItem->get_layer(), i32PtTime );
				else if( pItem->get_type() == SCENEITEM_EFFECT )
					m_pDoc->TimeSegmentAddKeyAtTime( pItem->get_effect(), i32PtTime );

				m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
			}

		}

	}
	else if( bInWaveForm ) {
		int32	i32Index = -1;
		int32	i32Time = 0;
		HitTestMarker( point, &i32Index, &i32Time );
		if( i32Index >= 0 ) {
			m_pDoc->MarkerTypeIn( i32Index );
			Invalidate( FALSE );
		}
		else
		{
			int32	i32Time = m_pDoc->FrameToTime( (point.x - m_rTimelineRect.left) / m_i32FrameWidthInPixels ) + m_i32ViewTimeStart;
			m_pDoc->MarkerAdd( i32Time );
			Invalidate( FALSE );
		}
	}

	if( !bInLayerList && !bInTimeline && !bInWaveForm )
		return false;

	return true;
}


bool CTimeLineBar::OnSceneLButtonUp(UINT nFlags, CPoint point) 
{
	m_bFirstTimeBoxSelect = false;

	if( !m_bMouseMoved && (m_eTrackAction == TRACKING_BOXSELECT ||
		m_eTrackAction == TRACKING_KEYS ||
		m_eTrackAction == TRACKING_TIMESEGMENT ||
		m_eTrackAction == TRACKING_KEYVALUES) ) {

		SceneItemC*	pItem = 0;
		int32	i32Hit = HitTestSceneList( point, &pItem );

		m_pDoc->DelFlagsAll( ITEM_LAYER | ITEM_EFFECT | ITEM_GIZMO | ITEM_PARAMETER, ITEM_SELECTED );
		if( pItem )
			m_rSceneItemList.set_item_flags( pItem, pItem->get_flags() | ITEM_SELECTED );
		SetTimecursor( m_pDoc->FrameToTime( (point.x - m_rTimelineRect.left) / m_i32FrameWidthInPixels ) + m_i32ViewTimeStart );
	}


	if( m_eTrackAction == TRACKING_BOXSELECT ) {

		bool	bNoSelection = true;

		if( m_rTrackOrigPt != point ) {
			int32	i32SelCount = 0;
			int32	i32StartTime = m_i32BoxSelectCurTime;
			int32	i32EndTime = m_i32BoxSelectStartTime;
			int32	i32StartY = m_i32BoxSelectCurY;
			int32	i32EndY = m_i32BoxSelectStartY;
			int32	i32ItemHeight = m_rSceneItemList.get_item_height();
			int32	i32HalfItemHeight = m_rSceneItemList.get_item_height() / 2;

			if( i32StartTime > i32EndTime ) {
				int32	i32Tmp = i32StartTime;
				i32StartTime = i32EndTime;
				i32EndTime = i32Tmp;
			}

			if( i32StartY > i32EndY ) {
				int32	i32Tmp = i32StartY;
				i32StartY = i32EndY;
				i32EndY = i32Tmp;
			}

			if( !(nFlags & MK_SHIFT) ) {
				m_pDoc->KeysDeselectAll();
				i32SelCount++;
			}

			for( int32 i = 0; i < m_rSceneItemList.get_sceneitem_count(); i++ ) {
				SceneItemC*	pItem = m_rSceneItemList.get_sceneitem( i );
				if( (pItem->get_y() + i32HalfItemHeight) >= i32StartY && (pItem->get_y() + i32ItemHeight) <= i32EndY ) {
					if( m_pDoc->KeysSelectInRange( pItem, i32StartTime, i32EndTime ) )
						i32SelCount++;
				}
			}

			if( i32SelCount )
				bNoSelection = false;
		}

		if( bNoSelection ) {
			// we missed the keys. deselect all.
			m_pDoc->KeysDeselectAll();
		}
	}

	if( m_eTrackAction == TRACKING_LAYERDRAG && m_rTrackOrigPt != point ) {
		if( m_bTrackInsertAfter ) {
			m_pDoc->LayerMoveAfter( m_pTrackItem->get_layer_index(), m_rTrackTargetItem.get_layer_index() );
		}
		else {
			m_pDoc->LayerMoveBefore( m_pTrackItem->get_layer_index(), m_rTrackTargetItem.get_layer_index() );
		}
	}

	if( m_eTrackAction == TRACKING_EFFECTDRAG && m_rTrackOrigPt != point ) {
		if( m_bTrackInsertAfter )
			m_pDoc->EffectMoveAfter( m_pTrackItem->get_effect_index(), m_pTrackItem->get_layer_index(), m_rTrackTargetItem.get_effect_index(), m_rTrackTargetItem.get_layer_index() );
		else
			m_pDoc->EffectMoveBefore( m_pTrackItem->get_effect_index(), m_pTrackItem->get_layer_index(), m_rTrackTargetItem.get_effect_index(), m_rTrackTargetItem.get_layer_index() );
	}

	if( m_eTrackAction == TRACKING_MARKER ) {
		SceneC*	pScene = m_pDoc->GetCurrentScene();
		pScene->sort_markers();
	}

	if( m_eTrackAction == TRACKING_KEYS ) {
			// We did not move, so cancel action.
		if( m_rTrackOrigPt == point )
			m_pDoc->CancelChanges();
		else
			m_pDoc->ApplyChanges();
	}

	if( m_eTrackAction == TRACKING_TIMESEGMENT ) {
		if( m_rTrackOrigPt == point )
			m_pDoc->CancelChanges();
		else
			m_pDoc->ApplyChanges();
	}

	if( m_eTrackAction == TRACKING_KEYVALUES ) {
		if( m_rTrackOrigPt == point )
			m_pDoc->CancelChanges();
		else
			m_pDoc->ApplyChanges();
	}

	if( m_eTrackAction == TRACKING_SPINNERX ||
		m_eTrackAction == TRACKING_SPINNERY ||
		m_eTrackAction == TRACKING_SPINNERZ ) {
		m_pDoc->EndDeferHandleParamNotify();
	}

	return true;
}


bool CTimeLineBar::OnSceneRButtonDown( UINT nFlags, CPoint rPoint )
{

	SceneItemC*	pItem = 0;
	int32	i32Hit = HitTestSceneList( rPoint, &pItem );

	if( m_rListRect.PtInRect( rPoint ) ) {

		m_pDoc->SetEditFocus( FOCUS_LAYERLIST );

		// Make sure the item is visible
		if( pItem ) {
			int32	i32Y = pItem->get_y() - GetListPos();
			if( i32Y < 0 )
				SetListPos( pItem->get_y() );
			else if( (i32Y + pItem->get_height()) > m_rListRect.Height() )
				SetListPos( pItem->get_y() + pItem->get_height() - m_rListRect.Height() );

			m_rListScroll.SetScrollPos( GetListPos() );
			Invalidate( FALSE );
		}

		switch( i32Hit ) {

		case HIT_LIST_EYE:
			{
				CFlatPopupMenu	rMenu;

				rMenu.SetFont( "Arial" );
				rMenu.SetFontSize( 8 );

				rMenu.Create( AfxGetInstanceHandle(), IDB_EYERIGHT );

				rMenu.AppendItem( 0, "&Show All", 1, 0 );
				rMenu.AppendItem( 0, "&Hide All", 2, 1 );
				rMenu.AppendItem( CFlatPopupMenu::itemSeparator, NULL, 0 );
				rMenu.AppendItem( 0, "&Invert", 3, 2 );

				ClientToScreen( &rPoint );
				int32	i32Id = rMenu.Track( rPoint.x, rPoint.y, NULL, true );
				switch( i32Id ) {
				case 1:
					m_pDoc->AddFlagsAll( ITEM_LAYER | ITEM_EFFECT, ITEM_VISIBLE );
					break;
				case 2:
					m_pDoc->DelFlagsAll( ITEM_LAYER | ITEM_EFFECT, ITEM_VISIBLE );
					break;
				case 3:
					m_pDoc->ToggleFlagsAll( ITEM_LAYER | ITEM_EFFECT, ITEM_VISIBLE );
					break;
				}
				m_rSceneItemList.layer_list_modified();
				m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
			}
			break;

		case HIT_LIST_LOCK:
			{
				CFlatPopupMenu	rMenu;

				rMenu.SetFont( "Arial" );
				rMenu.SetFontSize( 8 );

				rMenu.Create( AfxGetInstanceHandle(), IDB_LOCKRIGHT );

				rMenu.AppendItem( 0, "&Lock All", 1, 0 );
				rMenu.AppendItem( 0, "&Unlock All", 2, 1 );
				rMenu.AppendItem( CFlatPopupMenu::itemSeparator, NULL, 0 );
				rMenu.AppendItem( 0, "&Invert", 3, 2 );

				ClientToScreen( &rPoint );
				int32	i32Id = rMenu.Track( rPoint.x, rPoint.y, NULL, true );
				switch( i32Id ) {
				case 1:
					m_pDoc->AddFlagsAll( ITEM_LAYER | ITEM_EFFECT, ITEM_LOCKED );
					break;
				case 2:
					m_pDoc->DelFlagsAll( ITEM_LAYER | ITEM_EFFECT, ITEM_LOCKED );
					break;
				case 3:
					m_pDoc->ToggleFlagsAll( ITEM_LAYER | ITEM_EFFECT, ITEM_LOCKED );
					break;
				}
				m_rSceneItemList.layer_list_modified();
				Invalidate( FALSE );
			}
			break;

		case HIT_LIST_ITEM:
			{
				// update selection and redraw
				if( pItem && !(pItem->get_flags() & ITEM_SELECTED) ) {
					m_pDoc->DelFlagsAll( ITEM_LAYER | ITEM_EFFECT | ITEM_GIZMO | ITEM_PARAMETER, ITEM_SELECTED );
					m_rSceneItemList.set_item_flags( pItem, pItem->get_flags() | ITEM_SELECTED );
				}
				Invalidate( FALSE );

				if( !pItem || (pItem && pItem->get_type() == SCENEITEM_LAYER) ) {
					// the layer menu
					LayerMenu( rPoint, pItem );
				}
				else if( pItem->get_type() == SCENEITEM_EFFECT ) {
					// the effects menu
					EffectMenu( rPoint, pItem );
				}
				else if( pItem->get_type() == SCENEITEM_GIZMO ) {
					GizmoMenu( rPoint, pItem );
				}
			}
			break;

		default:
			{
				LayerMenu( rPoint, 0 );
			}
			break;
		}
	}
	else if( m_rTimelineRect.PtInRect( rPoint ) ) {

		m_pDoc->SetEditFocus( FOCUS_TIMELINE );

		// if we hit a layer or effect, then bring the time segment menu up.
		if( i32Hit == HIT_LIST_ITEM && pItem && (pItem->get_type() == SCENEITEM_LAYER || pItem->get_type() == SCENEITEM_EFFECT) ) {

			// hit test the time segment.
			KeyC*	pKey = 0;
			int32	i32SegHit = HitTestTimeSegment( rPoint, pItem, &pKey );

			// we hit a key but it's not selected
			if( i32SegHit == HIT_TSEG_KEY && pKey && !(pKey->get_flags() & KEY_SELECTED) ) {
				m_pDoc->KeysDeselectAll();
				m_pDoc->KeysSelectKey( pItem, pKey->get_time() );
				// redraw
				InvalidateRect( m_rTimelineRect );
			}


			if( i32SegHit == HIT_TSEG_NONE )
				return true;		// we didnt hit the bar

			CFlatPopupMenu	rMenu;

			rMenu.SetFont( "Arial" );
			rMenu.SetFontSize( 8 );

			rMenu.Create( AfxGetInstanceHandle(), IDB_TIMESEGRIGHT );

			uint32	ui32FlagsKey = 0;
			uint32	ui32FlagsNoKey = 0;

			if( pKey )
				ui32FlagsKey = CFlatPopupMenu::itemGrayed | CFlatPopupMenu::itemNotSelectable;
			else
				ui32FlagsNoKey = CFlatPopupMenu::itemGrayed | CFlatPopupMenu::itemNotSelectable;

			rMenu.AppendItem( ui32FlagsKey, "&Add New Key", 1, 0 );
			rMenu.AppendItem( ui32FlagsNoKey, "&Del Selected Key(s)", 2, 1 );
			rMenu.AppendItem( CFlatPopupMenu::itemSeparator, NULL, 0 );
			rMenu.AppendItem( 0, "&Select All Keys", 3 );

			CPoint	rScreenPt = rPoint;
			ClientToScreen( &rScreenPt );
			int32	i32Id = rMenu.Track( rScreenPt.x, rScreenPt.y, NULL, true );
			switch( i32Id ) {
			case 1:
				{
					// create key
					int32	i32PtTime = m_pDoc->FrameToTime( (rPoint.x - m_rTimelineRect.left) / m_i32FrameWidthInPixels ) + m_i32ViewTimeStart;
					if( pItem->get_type() == SCENEITEM_LAYER )
						m_pDoc->TimeSegmentAddKeyAtTime( pItem->get_layer(), i32PtTime );
					else if( pItem->get_type() == SCENEITEM_EFFECT )
						m_pDoc->TimeSegmentAddKeyAtTime( pItem->get_effect(), i32PtTime );
					m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
				}
				break;
			case 2:
				{
					// delete selected
					m_pDoc->KeysDeleteSelected();
					m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
				}
				break;
			case 3:
				{
					// select all keys of this item
					m_pDoc->KeysSelectAll( pItem );
					InvalidateRect( m_rTimelineRect );
				}
				break;
			}
			m_rSceneItemList.layer_list_modified();
			Invalidate( FALSE );
		}
		// if we hit a layer or effect, then bring the time segment menu up.
		else if( i32Hit == HIT_LIST_ITEM && pItem && (pItem->get_type() == SCENEITEM_PARAMETER)&& (pItem->get_flags() & ITEM_ANIMATED) ) {

			// hit test the time segment.
			KeyC*	pKey = 0;
			int32	i32ContHit = HitTestController( rPoint, pItem, &pKey );

			if( i32ContHit == HIT_CONT_NONE || i32ContHit == HIT_CONT_KEY_VALUE )
				return true;		// we didnt hit the bar

			// we hit a key but it's not selected
			if( i32ContHit == HIT_CONT_KEY_TIME && pKey && !(pKey->get_flags() & KEY_SELECTED) ) {
				m_pDoc->KeysDeselectAll();
				m_pDoc->KeysSelectKey( pItem, pKey->get_time() );
				// redraw
				InvalidateRect( m_rTimelineRect );
			}


			// Calc time-offset
			int32			i32TimeOffset = 0;
			LayerC*	pLayer = pItem->get_layer();
			EffectI*	pEffect = pItem->get_effect();
			if( pLayer && pEffect ) {
				i32TimeOffset += pLayer->get_timesegment()->get_segment_start();
				i32TimeOffset += pEffect->get_timesegment()->get_segment_start();
			}

			ParamI*			pParam = pItem->get_parameter();
			if( !pParam )
				return true;
			ControllerC*	pCont = pParam->get_controller();
			if( !pCont )
				return true;

			CFlatPopupMenu	rMenu;
			CFlatPopupMenu	rTensMenu;
			CFlatPopupMenu	rContMenu;
			CFlatPopupMenu	rBiasMenu;
			CFlatPopupMenu	rEaseInMenu;
			CFlatPopupMenu	rEaseOutMenu;
			CFlatPopupMenu	rOrtStartMenu;
			CFlatPopupMenu	rOrtEndMenu;

			rMenu.SetFont( "Arial" );
			rTensMenu.SetFont( "Arial" );
			rContMenu.SetFont( "Arial" );
			rBiasMenu.SetFont( "Arial" );
			rEaseInMenu.SetFont( "Arial" );
			rOrtStartMenu.SetFont( "Arial" );
			rOrtEndMenu.SetFont( "Arial" );

			rMenu.SetFontSize( 8 );
			rTensMenu.SetFontSize( 8 );
			rContMenu.SetFontSize( 8 );
			rBiasMenu.SetFontSize( 8 );
			rEaseInMenu.SetFontSize( 8 );
			rEaseOutMenu.SetFontSize( 8 );
			rOrtStartMenu.SetFontSize( 8 );
			rOrtEndMenu.SetFontSize( 8 );

			rMenu.Create( AfxGetInstanceHandle(), IDB_CONTROLLERRIGHT );
			rTensMenu.Create( AfxGetInstanceHandle(), IDB_CURSEL );
			rContMenu.Create( AfxGetInstanceHandle(), IDB_CURSEL );
			rBiasMenu.Create( AfxGetInstanceHandle(), IDB_CURSEL );
			rEaseInMenu.Create( AfxGetInstanceHandle(), IDB_CURSEL );
			rEaseOutMenu.Create( AfxGetInstanceHandle(), IDB_CURSEL );
			rOrtStartMenu.Create( AfxGetInstanceHandle(), IDB_ORTYPES );
			rOrtEndMenu.Create( AfxGetInstanceHandle(), IDB_ORTYPES );

			rOrtStartMenu.AppendItem( pCont->get_start_ort() == CONT_ORT_CONSTANT ? CFlatPopupMenu::itemBold : 0, "&Constant", 10, 0 );
			rOrtStartMenu.AppendItem( pCont->get_start_ort() == CONT_ORT_REPEAT ? CFlatPopupMenu::itemBold : 0, "&Repeat", 11, 1 );
			rOrtStartMenu.AppendItem( pCont->get_start_ort() == CONT_ORT_LOOP ? CFlatPopupMenu::itemBold : 0, "&Loop", 12, 2 );

			rOrtEndMenu.AppendItem( pCont->get_end_ort() == CONT_ORT_CONSTANT ? CFlatPopupMenu::itemBold : 0, "&Constant", 20, 0 );
			rOrtEndMenu.AppendItem( pCont->get_end_ort() == CONT_ORT_REPEAT ? CFlatPopupMenu::itemBold : 0, "&Repeat", 21, 1 );
			rOrtEndMenu.AppendItem( pCont->get_end_ort() == CONT_ORT_LOOP ? CFlatPopupMenu::itemBold : 0, "&Loop", 22, 2 );

			// gather the common values among the keys
			const uint32		TENS_BASE = 100;
			const uint32		CONT_BASE = 200;
			const uint32		BIAS_BASE = 300;
			const uint32		EASE_IN_BASE = 400;
			const uint32		EASE_OUT_BASE = 500;
			CommonKeyValuesS	rCommon;
			m_pDoc->KeysGatherCommonValues( &rCommon );

			for( uint32 j = 0; j <= 10; j++ ) {
				CString	sVal;
				sVal.Format( "%d", j * 5 );
				rTensMenu.AppendItem( rCommon.m_i32Tens == j ? CFlatPopupMenu::itemBold : 0, sVal, TENS_BASE + j );
				rContMenu.AppendItem( rCommon.m_i32Cont == j ? CFlatPopupMenu::itemBold : 0, sVal, CONT_BASE + j );
				rBiasMenu.AppendItem( rCommon.m_i32Bias == j ? CFlatPopupMenu::itemBold : 0, sVal, BIAS_BASE + j );
				rEaseInMenu.AppendItem( rCommon.m_i32EaseIn == j ? CFlatPopupMenu::itemBold : 0, sVal, EASE_IN_BASE + j );
				rEaseOutMenu.AppendItem( rCommon.m_i32EaseOut == j ? CFlatPopupMenu::itemBold : 0, sVal, EASE_OUT_BASE + j );
			}

			uint32	ui32FlagsKey = 0;
			uint32	ui32FlagsNoKey = 0;

			if( pKey )
				ui32FlagsKey = CFlatPopupMenu::itemGrayed | CFlatPopupMenu::itemNotSelectable;
			else
				ui32FlagsNoKey = CFlatPopupMenu::itemGrayed | CFlatPopupMenu::itemNotSelectable;

			rMenu.AppendItem( ui32FlagsKey, "&Add New Key", 1, 0 );
			rMenu.AppendItem( ui32FlagsNoKey, "&Del Selected Key(s)", 2, 1 );
			if( i32ContHit == HIT_CONT_KEY_TIME ) {
				rMenu.AppendItem( CFlatPopupMenu::itemSeparator, NULL, 0 );
				rMenu.AppendItem( ui32FlagsNoKey | (rCommon.m_i32Interpolation == KEY_SMOOTH ? CFlatPopupMenu::itemBold : 0), "S&mooth", 3, 2 );
				rMenu.AppendItem( ui32FlagsNoKey | (rCommon.m_i32Interpolation == KEY_LINEAR ? CFlatPopupMenu::itemBold : 0), "&Linear", 4, 4 );
				rMenu.AppendItem( ui32FlagsNoKey | (rCommon.m_i32Interpolation == KEY_HOLD ? CFlatPopupMenu::itemBold : 0), "&Hold", 5, 3 );
				if( rCommon.m_i32SmoothCount ) {
					rMenu.AppendItem( CFlatPopupMenu::itemSeparator, NULL, 0 );
					rMenu.AppendPopup( 0, "&Tension", rTensMenu );
					rMenu.AppendPopup( 0, "&Continuity", rContMenu );
					rMenu.AppendPopup( 0, "&Bias", rBiasMenu );
				}
				rMenu.AppendPopup( 0, "Ease &In", rEaseInMenu );
				rMenu.AppendPopup( 0, "Ease &Out", rEaseOutMenu );
			}
			else {
				rMenu.AppendItem( CFlatPopupMenu::itemSeparator, NULL, 0 );
				rMenu.AppendPopup( 0, "Out of Range &Start", rOrtStartMenu );
				rMenu.AppendPopup( 0, "Out of Range &End", rOrtEndMenu );
			}
			rMenu.AppendItem( CFlatPopupMenu::itemSeparator, NULL, 0 );
			rMenu.AppendItem( 0, "Select All &Keys", 6 );

			CPoint	rScreenPt = rPoint;
			ClientToScreen( &rScreenPt );
			int32	i32Id = rMenu.Track( rScreenPt.x, rScreenPt.y, NULL, true );

			if( i32Id == 1 ) {
				// Add new key
				int32	i32PtTime = m_pDoc->FrameToTime( (rPoint.x - m_rTimelineRect.left) / m_i32FrameWidthInPixels ) + m_i32ViewTimeStart - i32TimeOffset;
				// create command and make changes
				m_pDoc->ControllerAddKeyAtTime( pCont, i32PtTime );
			}
			else if( i32Id == 2 ) {
				// delete selected keys
				m_pDoc->KeysDeleteSelected();
			}
			else if( i32Id == 3 ) {
				// smooth interpolation
				m_pDoc->KeysSetInterpolation( KEY_SMOOTH );
			}
			else if( i32Id == 4 ) {
				// linear interpolation
				m_pDoc->KeysSetInterpolation( KEY_LINEAR );
			}
			else if( i32Id == 5 ) {
				// hold interpolation
				m_pDoc->KeysSetInterpolation( KEY_HOLD );
			}
			else if( i32Id == 6 ) {
				// select all keys
				m_pDoc->KeysSelectAll( pItem );
			}
			else if( i32Id >= 10 && i32Id <= 12 ) {
				// start ORT
				uint32	ui32Ort;
				if( i32Id == 10 )
					ui32Ort = CONT_ORT_CONSTANT;
				else if( i32Id == 11 )
					ui32Ort = CONT_ORT_REPEAT;
				else
					ui32Ort = CONT_ORT_LOOP;

				// set out of range type
				m_pDoc->ControllerSetStartOutOfRange( pCont, ui32Ort );
			}
			else if( i32Id >= 20 && i32Id <= 22 ) {
				// end ORT
				uint32	ui32Ort;
				if( i32Id == 20 )
					ui32Ort = CONT_ORT_CONSTANT;
				else if( i32Id == 21 )
					ui32Ort = CONT_ORT_REPEAT;
				else
					ui32Ort = CONT_ORT_LOOP;

				// set out of range type
				m_pDoc->ControllerSetEndOutOfRange( pCont, ui32Ort );
			}
			else if( i32Id >= EASE_OUT_BASE ) {
				// ease out
				float32	f32EaseOut = (float)(i32Id - 500) / 10.0f;
				m_pDoc->KeysSetInterpolationProperty( PROPERTY_EASE_OUT, f32EaseOut );
			}
			else if( i32Id >= EASE_IN_BASE ) {
				// ease in
				float32	f32EaseIn = (float)(i32Id - 400) / 10.0f;
				m_pDoc->KeysSetInterpolationProperty( PROPERTY_EASE_IN, f32EaseIn );
			}
			else if( i32Id >= BIAS_BASE ) {
				// bias
				float32	f32Bias = (float)(i32Id - 300) / 5.0f - 1.0f;
				m_pDoc->KeysSetInterpolationProperty( PROPERTY_BIAS, f32Bias );
			}
			else if( i32Id >= CONT_BASE ) {
				// cont
				float32	f32Cont = (float)(i32Id - 200) / 5.0f - 1.0f;
				m_pDoc->KeysSetInterpolationProperty( PROPERTY_CONT, f32Cont );
			}
			else if( i32Id >= TENS_BASE ) {
				// tens
				float32	f32Tens = (float)(i32Id - 100) / 5.0f - 1.0f;
				m_pDoc->KeysSetInterpolationProperty( PROPERTY_TENS, f32Tens );
			}

			m_pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
		}
	}
	else if( m_rRulerRect.PtInRect( rPoint ) ) {

		CFlatPopupMenu	rScaleMenu;

		rScaleMenu.SetFont( "Arial" );
		rScaleMenu.SetFontSize( 8 );
		rScaleMenu.Create( AfxGetInstanceHandle(), IDB_TIMESCALE );

		// Time ruler scale
		rScaleMenu.AppendItem( m_i32FrameWidthInPixels == 3 ? CFlatPopupMenu::itemBold : 0, "30%", 3, 0 );
		rScaleMenu.AppendItem( m_i32FrameWidthInPixels == 5 ? CFlatPopupMenu::itemBold : 0, "50%", 5 );
		rScaleMenu.AppendItem( m_i32FrameWidthInPixels == 7 ? CFlatPopupMenu::itemBold : 0, "100%", 7 );
		rScaleMenu.AppendItem( m_i32FrameWidthInPixels == 9 ? CFlatPopupMenu::itemBold : 0, "150%", 9 );
		rScaleMenu.AppendItem( m_i32FrameWidthInPixels == 15 ? CFlatPopupMenu::itemBold : 0, "200%", 15, 2 );

		CPoint	rScreenPt = rPoint;
		ClientToScreen( &rScreenPt );
		int32	i32Id = rScaleMenu.Track( rScreenPt.x, rScreenPt.y, NULL, true );

		if( i32Id == 3 || i32Id == 5 || i32Id == 7 || i32Id == 9 || i32Id == 15 ) {
			CString	sText;
			switch( i32Id ) {
			case 3: sText = "30%"; break;
			case 5: sText = "50%"; break;
			case 7: sText = "100%"; break;
			case 9: sText = "150%"; break;
			case 15: sText = "200%"; break;
			}
			m_rZoomStatic.SetWindowText( sText );
			m_i32FrameWidthInPixels = i32Id;
			Invalidate( FALSE );
		}
	}
	else {
		// this method did not process this command
		return false;
	}

	return true;
}

void CTimeLineBar::OnSceneTimer( UINT nIDEvent )
{
	int32	i32TimeStep = 2;

	switch( m_i32FrameWidthInPixels ) {
	case 3:		i32TimeStep = 4; break;
	case 5:		i32TimeStep = 3; break;
	case 7:		i32TimeStep = 3; break;
	case 9:		i32TimeStep = 2; break;
	case 15:	i32TimeStep = 1; break;
	}

	i32TimeStep = m_pDoc->FrameToTime( i32TimeStep );

	switch( nIDEvent ) {
	case TIMER_BOXSELECT_LEFT:
		{
			SetTimeStart( m_i32ViewTimeStart - i32TimeStep );
			m_i32BoxSelectCurTime -= i32TimeStep;
			m_bScrollingBoxSelect = true;
			Invalidate( FALSE );
		}
		break;
	case TIMER_BOXSELECT_RIGHT:
		{
			SetTimeStart( m_i32ViewTimeStart + i32TimeStep );
			m_i32BoxSelectCurTime += i32TimeStep;
			m_bScrollingBoxSelect = true;
			Invalidate( FALSE );
		}
		break;
	case TIMER_BOXSELECT_TOP:
		{
			SetListPos( GetListPos() - 8 );
			m_i32BoxSelectCurY -= 8;
			if( GetListPos() < 0 ) {
				m_i32BoxSelectCurY += -GetListPos();
				SetListPos( 0 );
			}
			m_bScrollingBoxSelect = true;
			m_rListScroll.SetScrollPos( GetListPos(), TRUE );
			Invalidate( FALSE );
		}
		break;
	case TIMER_BOXSELECT_BOTTOM:
		{
			int32	i32Range = m_rSceneItemList.get_list_height() - m_rListRect.Height() / 2;
			if( i32Range < 0 )
				i32Range = 0;
			SetListPos( GetListPos() + 8 );
			m_i32BoxSelectCurY += 8;
			if( GetListPos() > i32Range ) {
				m_i32BoxSelectCurY -= i32Range - GetListPos();
				SetListPos( i32Range );
			}
			m_bScrollingBoxSelect = true;
			m_rListScroll.SetScrollPos( GetListPos(), TRUE );
			Invalidate( FALSE );
		}
		break;

	case TIMER_MOVEKEYS_LEFT:
		{
			SetTimeStart( m_i32ViewTimeStart - i32TimeStep );

			m_pDoc->KeysMoveTime( -i32TimeStep );
			m_i32TrackOrigTime -= i32TimeStep;

			Invalidate( FALSE );
		}
		break;
	case TIMER_MOVEKEYS_RIGHT:
		{
			SetTimeStart( m_i32ViewTimeStart + i32TimeStep );

			m_pDoc->KeysMoveTime( i32TimeStep );
			m_i32TrackOrigTime += i32TimeStep;

			Invalidate( FALSE );
		}
		break;

	case TIMER_MOVETIMESEGMENT_LEFT:
		{
			SetTimeStart( m_i32ViewTimeStart - i32TimeStep );
			
			TimeSegmentC*	pSeg;
			if( m_pTrackItem->get_type() == SCENEITEM_LAYER )
				pSeg = m_pTrackItem->get_layer()->get_timesegment();
			else if( m_pTrackItem->get_type() == SCENEITEM_EFFECT )
				pSeg = m_pTrackItem->get_effect()->get_timesegment();

			if( pSeg ) {
				int32	i32Start = pSeg->get_segment_start();
				int32	i32DeltaTimeInTicks = -i32TimeStep;
				pSeg->set_segment_start( i32Start + i32DeltaTimeInTicks );
			}
			Invalidate( FALSE );
		}
		break;

	case TIMER_MOVETIMESEGMENT_RIGHT:
		{
			SetTimeStart( m_i32ViewTimeStart + i32TimeStep );

			TimeSegmentC*	pSeg;
			if( m_pTrackItem->get_type() == SCENEITEM_LAYER )
				pSeg = m_pTrackItem->get_layer()->get_timesegment();
			else if( m_pTrackItem->get_type() == SCENEITEM_EFFECT )
				pSeg = m_pTrackItem->get_effect()->get_timesegment();

			if( pSeg ) {
				int32	i32Start = pSeg->get_segment_start();
				int32	i32DeltaTimeInTicks = i32TimeStep;
				pSeg->set_segment_start( i32Start + i32DeltaTimeInTicks );
			}
			Invalidate( FALSE );
		}
		break;
	}
}

void
CTimeLineBar::UpdateNotify( uint32 ui32Notify )
{
	if( ui32Notify == NOTIFY_REDRAW_GRAPHICS ) {
		SetScrollRanges();
		Invalidate();
	}
	else if( ui32Notify == NOTIFY_REDRAW_ALL ) {
		SetScrollRanges();
		Invalidate();
	}
	else if( ui32Notify == NOTIFY_RESET_VIEWS ) {
		m_rSceneItemList.build_sceneitem_list( m_pDoc->GetCurrentScene() );
		SetScrollRanges();
		Invalidate();
	}
	else if( ui32Notify == NOTIFY_FILELIST_CHANGED ) {
		UpdateSceneTab();
		SetScrollRanges();
	}
	else if( ui32Notify == NOTIFY_LAYERLIST_CHANGED ) {
		m_rSceneItemList.build_sceneitem_list( m_pDoc->GetCurrentScene() );
	}
}

COLORREF
CTimeLineBar::GetColor( uint32 ui32Col )
{
	// hard coded for now
	switch( ui32Col ) {
	case TIMELINE_TEXT:			return RGB( 16, 16, 16 );
	case TIMELINE_BLACK:		return RGB( 16, 16, 16 );
	case TIMELINE_BACK:			return RGB( 255, 255, 255 );
	case TIMELINE_BACK_SEL:		return RGB( 250, 250, 250 );
	case TIMELINE_DARK_TICK:	return RGB( 240, 210, 210 );
	case TIMELINE_LIGHT_TICK:	return RGB( 240, 240, 240 );
	case TIMELINE_GRID:			return RGB( 190, 190, 190 );
	case TIMELINE_VALUE_GRID:	return RGB( 196, 196, 196 );
	case TIMELINE_VALUE_TEXT:	return RGB( 80, 80, 80 );
	case TIMELINE_PARAM_FACE:	return RGB( 196, 196, 196 );
	case TIMELINE_PARAM_LIGHT:	return RGB( 225, 225, 225 );
	case TIMELINE_PARAM_SHADOW:	return RGB( 128, 128, 128 );
	}

	return RGB( 0, 0, 0 );
}
