You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1763 lines
42 KiB
1763 lines
42 KiB
//========= Copyright Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose:
|
|
//
|
|
// $NoKeywords: $
|
|
//=============================================================================//
|
|
#include "hlfaceposer.h"
|
|
#include <stdio.h>
|
|
#include "TimelineItem.h"
|
|
#include "choreowidgetdrawhelper.h"
|
|
#include "mathlib/mathlib.h"
|
|
#include "expressions.h"
|
|
#include "StudioModel.h"
|
|
#include "expclass.h"
|
|
#include "mathlib/mathlib.h"
|
|
#include "ExpressionTool.h"
|
|
#include "choreoevent.h"
|
|
#include "choreoscene.h"
|
|
#include "choreoactor.h"
|
|
#include "choreochannel.h"
|
|
#include "ChoreoView.h"
|
|
#include "ControlPanel.h"
|
|
#include "faceposer_models.h"
|
|
#include "MatSysWin.h"
|
|
#include "choreoviewcolors.h"
|
|
#include "ifaceposersound.h"
|
|
#include "curveeditorhelpers.h"
|
|
|
|
extern double realtime;
|
|
|
|
#define DOUBLE_CLICK_TIME 0.2
|
|
|
|
#define GROW_HANDLE_WIDTH 66
|
|
#define GROW_HANDLE_HEIGHT 8
|
|
#define GROW_HANDLE_INSETPIXELS 8
|
|
|
|
#define TIMELINEITEM_DEFAULT_HEIGHT 100
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
TimelineItem::TimelineItem( mxWindow *workspace )
|
|
{
|
|
m_pHelper = new CCurveEditorHelper< TimelineItem >( this );
|
|
|
|
m_pWorkspace = workspace;
|
|
|
|
m_nDragging = DRAGTYPE_NONE;
|
|
m_nLastX = 0;
|
|
m_nLastY = 0;
|
|
m_nStartX = 0;
|
|
m_nStartY = 0;
|
|
|
|
SetExpressionInfo( NULL, 0 );
|
|
|
|
m_nNumSelected = 0;
|
|
|
|
m_nEditType = 0;
|
|
|
|
SetCollapsed( false );
|
|
SetActive( false );
|
|
|
|
m_nUndoSetup = 0;
|
|
m_rcBounds.top = 0;
|
|
m_rcBounds.bottom = 0;
|
|
m_rcBounds.left = 0;
|
|
m_rcBounds.right = 0;
|
|
m_bVisible = false;
|
|
m_flLastClickTime = -1;
|
|
|
|
m_nCurrentHeight = TIMELINEITEM_DEFAULT_HEIGHT;
|
|
}
|
|
|
|
TimelineItem::~TimelineItem( void )
|
|
{
|
|
delete m_pHelper;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void TimelineItem::ResetHeight()
|
|
{
|
|
m_nCurrentHeight = TIMELINEITEM_DEFAULT_HEIGHT;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : mx -
|
|
// Output : float
|
|
//-----------------------------------------------------------------------------
|
|
float TimelineItem::GetTimeForMouse( int mx, bool clip /*= false*/ )
|
|
{
|
|
float start, end;
|
|
g_pExpressionTool->GetStartAndEndTime( start, end );
|
|
|
|
if ( clip )
|
|
{
|
|
if ( mx < m_rcBounds.left )
|
|
{
|
|
return start;
|
|
}
|
|
else if ( mx >= m_rcBounds.right )
|
|
{
|
|
return end;
|
|
}
|
|
}
|
|
|
|
float frac = (float)( mx - m_rcBounds.left ) / (float)( m_rcBounds.right - m_rcBounds.left );
|
|
float t = start + frac * ( end - start );
|
|
return t;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : t -
|
|
// Output : int
|
|
//-----------------------------------------------------------------------------
|
|
int TimelineItem::GetMouseForTime( float t, bool *clipped /*= NULL*/ )
|
|
{
|
|
float start, end;
|
|
g_pExpressionTool->GetStartAndEndTime( start, end );
|
|
|
|
float frac = ( t - start ) / ( end - start );
|
|
|
|
if ( frac < 0.0 || frac > 1.0 )
|
|
{
|
|
if ( clipped )
|
|
{
|
|
*clipped = true;
|
|
}
|
|
}
|
|
|
|
int mx = m_rcBounds.left + ( int ) ( frac * (float)( m_rcBounds.right - m_rcBounds.left ) );
|
|
|
|
return mx;
|
|
}
|
|
|
|
int TimelineItem::NumSamples()
|
|
{
|
|
CFlexAnimationTrack *track = GetSafeTrack();
|
|
if ( !track )
|
|
return 0;
|
|
|
|
// Aggregate both types of tracks together
|
|
return track->GetNumSamples( 0 ) + track->GetNumSamples( 1 );
|
|
// return track->GetNumSamples( m_nEditType );
|
|
}
|
|
|
|
CExpressionSample *TimelineItem::GetSample( int idx )
|
|
{
|
|
CFlexAnimationTrack *track = GetSafeTrack();
|
|
if ( !track )
|
|
return NULL;
|
|
|
|
if ( idx >= track->GetNumSamples( 0 ) )
|
|
{
|
|
// Rebase and look at left/right track instead
|
|
idx -= track->GetNumSamples( 0 );
|
|
return track->GetSample( idx, 1 );
|
|
}
|
|
return track->GetSample( idx, 0 );
|
|
}
|
|
|
|
CExpressionSample *TimelineItem::GetSampleUnderMouse( int mx, int my, float tolerance /*= FP_TL_SELECTION_TOLERANCE*/ )
|
|
{
|
|
CFlexAnimationTrack *track = GetSafeTrack();
|
|
if ( !track )
|
|
return NULL;
|
|
|
|
CChoreoEvent *e = track->GetEvent();
|
|
if ( !e )
|
|
return NULL;
|
|
|
|
float closest_dist = 9999999.f;
|
|
CExpressionSample *bestsample = NULL;
|
|
|
|
// Add a sample point
|
|
int height = m_rcBounds.bottom - m_rcBounds.top;
|
|
|
|
mx += m_rcBounds.left;
|
|
|
|
for ( int i = 0; i < track->GetNumSamples( m_nEditType ); i++ )
|
|
{
|
|
CExpressionSample *sample = track->GetSample( i, m_nEditType );
|
|
|
|
bool clipped = false;
|
|
int px = GetMouseForTime( sample->time, &clipped );
|
|
int py = height * ( 1.0f - sample->value );
|
|
|
|
int dx = px - mx;
|
|
int dy = py - my;
|
|
|
|
float dist = sqrt( (float)(dx * dx + dy * dy) );
|
|
if ( dist < closest_dist )
|
|
{
|
|
bestsample = sample;
|
|
closest_dist = dist;
|
|
}
|
|
}
|
|
|
|
// Not close to any of them!!!
|
|
if ( ( tolerance != 0.0f ) &&
|
|
( closest_dist > tolerance ) )
|
|
return NULL;
|
|
|
|
return bestsample;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void TimelineItem::DeselectAll( void )
|
|
{
|
|
g_pExpressionTool->DeselectAll();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void TimelineItem::SelectAll( void )
|
|
{
|
|
CFlexAnimationTrack *track = GetSafeTrack();
|
|
if ( !track )
|
|
return;
|
|
|
|
for ( int t = 0; t < 2; t++ )
|
|
{
|
|
for ( int i = 0; i < track->GetNumSamples( t ); i++ )
|
|
{
|
|
CExpressionSample *sample = track->GetSample( i, t );
|
|
sample->selected = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void TimelineItem::Delete( void )
|
|
{
|
|
g_pExpressionTool->DeleteSelectedSamples();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : sample -
|
|
//-----------------------------------------------------------------------------
|
|
void TimelineItem::AddSample( CExpressionSample const& sample )
|
|
{
|
|
CFlexAnimationTrack *track = GetSafeTrack();
|
|
if ( !track )
|
|
return;
|
|
|
|
PreDataChanged( "Add sample point" );
|
|
|
|
track->AddSample( sample.time, sample.value, m_nEditType );
|
|
track->Resort( m_nEditType );
|
|
|
|
SetActive( true );
|
|
|
|
PostDataChanged( "Add sample point" );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
int TimelineItem::CountSelected( void )
|
|
{
|
|
m_nNumSelected = m_pHelper->CountSelected( false );
|
|
return m_nNumSelected;
|
|
}
|
|
|
|
void TimelineItem::SetMousePositionForEvent( mxEvent *event )
|
|
{
|
|
POINT pt;
|
|
GetCursorPos( &pt );
|
|
ScreenToClient( (HWND)m_pWorkspace->getHandle(), &pt );
|
|
|
|
pt.x -= m_rcBounds.left;
|
|
pt.y -= m_rcBounds.top;
|
|
|
|
event->x = pt.x;
|
|
event->y = pt.y;
|
|
}
|
|
|
|
int TimelineItem::handleEvent( mxEvent *event )
|
|
{
|
|
int iret = 0;
|
|
|
|
// Give helper a shot at the event
|
|
if ( m_pHelper->HelperHandleEvent( event ) )
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
switch ( event->event )
|
|
{
|
|
case mxEvent::KeyDown:
|
|
{
|
|
switch ( event->key )
|
|
{
|
|
default:
|
|
iret = g_pChoreoView->HandleZoomKey( g_pExpressionTool, event->key );
|
|
break;
|
|
case VK_ESCAPE:
|
|
DeselectAll();
|
|
DrawSelf();
|
|
break;
|
|
case VK_DELETE:
|
|
Delete();
|
|
DrawSelf();
|
|
break;
|
|
case 'C':
|
|
Copy();
|
|
DrawSelf();
|
|
break;
|
|
case 'V':
|
|
Paste();
|
|
DrawSelf();
|
|
break;
|
|
case 'J':
|
|
{
|
|
g_pExpressionTool->OnCopyToFlex( g_pExpressionTool->GetScrubberSceneTime(), true );
|
|
}
|
|
break;
|
|
case 'K':
|
|
{
|
|
g_pExpressionTool->OnCopyFromFlex( g_pExpressionTool->GetScrubberSceneTime(), false );
|
|
}
|
|
break;
|
|
case 188: // VK_OEM_COMMA:
|
|
{
|
|
g_pExpressionTool->SetScrubTargetTime( 0.0f );
|
|
}
|
|
break;
|
|
case 190: // VK_OEM_PERIOD:
|
|
{
|
|
CChoreoScene *scene = g_pChoreoView->GetScene();
|
|
if ( scene )
|
|
{
|
|
g_pExpressionTool->SetScrubTargetTime( scene->FindStopTime() );
|
|
}
|
|
}
|
|
break;
|
|
case VK_LEFT:
|
|
{
|
|
CChoreoScene *scene = g_pChoreoView->GetScene();
|
|
if ( scene && scene->GetSceneFPS() > 0 )
|
|
{
|
|
float curscrub = g_pExpressionTool->GetScrub();
|
|
curscrub -= ( 1.0f / (float)scene->GetSceneFPS() );
|
|
curscrub = max( curscrub, 0.0f );
|
|
g_pExpressionTool->SetScrubTargetTime( curscrub );
|
|
}
|
|
}
|
|
break;
|
|
case VK_RIGHT:
|
|
{
|
|
CChoreoScene *scene = g_pChoreoView->GetScene();
|
|
if ( scene && scene->GetSceneFPS() > 0 )
|
|
{
|
|
float curscrub = g_pExpressionTool->GetScrub();
|
|
curscrub += ( 1.0f / (float)scene->GetSceneFPS() );
|
|
curscrub = min( curscrub, scene->FindStopTime() );
|
|
g_pExpressionTool->SetScrubTargetTime( curscrub );
|
|
}
|
|
}
|
|
break;
|
|
case 191:
|
|
{
|
|
if ( g_pChoreoView->IsPlayingScene() )
|
|
{
|
|
g_pChoreoView->StopScene();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
iret = 1;
|
|
}
|
|
break;
|
|
case mxEvent::KeyUp:
|
|
{
|
|
switch ( event->key )
|
|
{
|
|
case VK_SPACE:
|
|
{
|
|
CFlexAnimationTrack *track = GetSafeTrack();
|
|
if ( track && track->IsComboType() )
|
|
{
|
|
SetEditType( m_nEditType == 0 ? 1 : 0 );
|
|
DrawSelf();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
iret = 1;
|
|
}
|
|
break;
|
|
case mxEvent::MouseDown:
|
|
{
|
|
sound->Flush();
|
|
|
|
SetFocus( (HWND)g_pExpressionTool->getHandle() );
|
|
|
|
int height = m_rcBounds.bottom - m_rcBounds.top;
|
|
|
|
bool rightbutton = ( event->buttons & mxEvent::MouseRightButton ) ? true : false;
|
|
|
|
if ( m_nDragging == DRAGTYPE_NONE )
|
|
{
|
|
bool ctrlDown = ( event->modifiers & mxEvent::KeyCtrl ) ? true : false;
|
|
|
|
CExpressionSample *sample = GetSampleUnderMouse( event->x, event->y, ctrlDown ? FP_TL_ADDSAMPLE_TOLERANCE : FP_TL_SELECTION_TOLERANCE );
|
|
|
|
if ( IsMouseOverGrowHandle( (short)event->x, (short)event->y ) )
|
|
{
|
|
m_nDragging = DRAGTYPE_GROW;
|
|
m_nLastX = (short)event->x;
|
|
m_nLastY = (short)event->y;
|
|
|
|
m_nStartX = m_nLastX;
|
|
m_nStartY = m_nLastY;
|
|
|
|
MouseDrag( (short)event->x, (short)event->y, event->modifiers );
|
|
|
|
DrawGrowRect();
|
|
}
|
|
else if ( sample )
|
|
{
|
|
if ( event->modifiers & mxEvent::KeyShift )
|
|
{
|
|
sample->selected = !sample->selected;
|
|
DrawSelf();
|
|
}
|
|
else if ( sample->selected )
|
|
{
|
|
m_nDragging = rightbutton ? DRAGTYPE_MOVEPOINTS_TIME : DRAGTYPE_MOVEPOINTS_VALUE;
|
|
m_nLastX = (short)event->x;
|
|
m_nLastY = (short)event->y;
|
|
|
|
m_nStartX = m_nLastX;
|
|
m_nStartY = m_nLastY;
|
|
|
|
PreDataChanged( "Move sample point(s)" );
|
|
|
|
MouseDrag( (short)event->x, (short)event->y, event->modifiers );
|
|
|
|
DrawSelf();
|
|
}
|
|
else
|
|
{
|
|
if ( !( event->modifiers & mxEvent::KeyShift ) )
|
|
{
|
|
DeselectAll();
|
|
DrawSelf();
|
|
}
|
|
|
|
m_nDragging = DRAGTYPE_SELECTION;
|
|
m_nLastX = (short)event->x;
|
|
m_nLastY = (short)event->y;
|
|
|
|
m_nStartX = m_nLastX;
|
|
m_nStartY = m_nLastY;
|
|
|
|
MouseDrag( (short)event->x, (short)event->y, event->modifiers );
|
|
|
|
DrawFocusRect();
|
|
}
|
|
}
|
|
else if ( event->modifiers & mxEvent::KeyCtrl )
|
|
{
|
|
CChoreoEvent *e = g_pExpressionTool->GetSafeEvent();
|
|
if ( e )
|
|
{
|
|
// Add a sample point
|
|
float t = GetTimeForMouse( (short)event->x + m_rcBounds.left );
|
|
|
|
CExpressionSample sample;
|
|
sample.time = FacePoser_SnapTime( t );
|
|
sample.value = 1.0f - (float)( (short)( event->y ) ) / (float)height;
|
|
sample.selected = false;
|
|
|
|
AddSample( sample );
|
|
|
|
DrawSelf();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( rightbutton )
|
|
{
|
|
POINT pt;
|
|
pt.x = event->x;
|
|
pt.y = event->y;
|
|
ClientToScreen( (HWND)m_pWorkspace->getHandle(), &pt );
|
|
ScreenToClient( (HWND)g_pExpressionTool->getHandle(), &pt );
|
|
event->x = pt.x;
|
|
event->y = pt.y;
|
|
g_pExpressionTool->ShowContextMenu( event, true );
|
|
return iret;
|
|
}
|
|
|
|
if ( !( event->modifiers & mxEvent::KeyShift ) )
|
|
{
|
|
DeselectAll();
|
|
DrawSelf();
|
|
}
|
|
|
|
m_nDragging = DRAGTYPE_SELECTION;
|
|
m_nLastX = (short)event->x;
|
|
m_nLastY = (short)event->y;
|
|
|
|
m_nStartX = m_nLastX;
|
|
m_nStartY = m_nLastY;
|
|
|
|
MouseDrag( (short)event->x, (short)event->y, event->modifiers );
|
|
|
|
DrawFocusRect();
|
|
}
|
|
}
|
|
iret = 1;
|
|
}
|
|
break;
|
|
case mxEvent::MouseDrag:
|
|
case mxEvent::MouseMove:
|
|
{
|
|
if ( m_nDragging != DRAGTYPE_NONE )
|
|
{
|
|
if ( m_nDragging == DRAGTYPE_SELECTION )
|
|
{
|
|
DrawFocusRect();
|
|
}
|
|
else if ( m_nDragging == DRAGTYPE_GROW )
|
|
{
|
|
DrawGrowRect();
|
|
}
|
|
|
|
MouseDrag( (short)event->x, (short)event->y, event->modifiers );
|
|
|
|
if ( m_nDragging == DRAGTYPE_SELECTION )
|
|
{
|
|
DrawFocusRect();
|
|
}
|
|
else if ( m_nDragging == DRAGTYPE_GROW )
|
|
{
|
|
DrawGrowRect();
|
|
}
|
|
|
|
if ( m_nDragging == DRAGTYPE_MOVEPOINTS_TIME ||
|
|
m_nDragging == DRAGTYPE_MOVEPOINTS_VALUE )
|
|
{
|
|
DrawSelf();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// See if anything is selected
|
|
CountSelected();
|
|
if ( m_nNumSelected <= 0 && g_pExpressionTool->IsFocusItem( this ) )
|
|
{
|
|
// Nothing selected
|
|
// Draw auto highlight
|
|
DrawAutoHighlight( event );
|
|
}
|
|
}
|
|
iret = 1;
|
|
}
|
|
break;
|
|
case mxEvent::MouseUp:
|
|
{
|
|
bool overgrow = IsMouseOverGrowHandle( (short)event->x, (short)event->y );
|
|
|
|
if ( m_nDragging != DRAGTYPE_NONE )
|
|
{
|
|
if ( m_nDragging == DRAGTYPE_SELECTION )
|
|
{
|
|
DrawFocusRect();
|
|
}
|
|
else if ( m_nDragging == DRAGTYPE_GROW )
|
|
{
|
|
DrawGrowRect();
|
|
}
|
|
|
|
MouseDrag( (short)event->x, (short)event->y, event->modifiers, true );
|
|
|
|
if ( m_nDragging == DRAGTYPE_GROW )
|
|
{
|
|
// Finish grow by resizing control
|
|
int desiredheight = m_nCurrentHeight + event->y - m_nStartY;
|
|
if ( desiredheight >= 10 )
|
|
{
|
|
m_nCurrentHeight = desiredheight;
|
|
g_pExpressionTool->LayoutItems( true );
|
|
}
|
|
}
|
|
else if ( m_nDragging != DRAGTYPE_MOVEPOINTS_VALUE &&
|
|
m_nDragging != DRAGTYPE_MOVEPOINTS_TIME )
|
|
{
|
|
SelectPoints();
|
|
}
|
|
else
|
|
{
|
|
PostDataChanged( "Move sample point(s)" );
|
|
}
|
|
|
|
m_nDragging = DRAGTYPE_NONE;
|
|
|
|
DrawSelf();
|
|
}
|
|
|
|
bool rightbutton = ( event->buttons & mxEvent::MouseRightButton ) ? true : false;
|
|
bool shift = ( event->modifiers & mxEvent::KeyShift ) ? true : false;
|
|
bool ctrl = ( event->modifiers & mxEvent::KeyCtrl ) ? true : false;
|
|
|
|
if ( !rightbutton && !shift && !ctrl )
|
|
{
|
|
if ( realtime - m_flLastClickTime < DOUBLE_CLICK_TIME )
|
|
{
|
|
if ( overgrow || IsCollapsed() )
|
|
{
|
|
OnDoubleClicked();
|
|
}
|
|
}
|
|
|
|
m_flLastClickTime = realtime;
|
|
}
|
|
|
|
iret = 1;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return iret;
|
|
}
|
|
|
|
void TimelineItem::MouseDrag( int x, int y, int modifiers, bool snap /*=false*/ )
|
|
{
|
|
if ( m_nDragging == DRAGTYPE_NONE )
|
|
return;
|
|
|
|
int width = m_rcBounds.right - m_rcBounds.left;
|
|
int height = m_rcBounds.bottom - m_rcBounds.top;
|
|
|
|
if ( m_nDragging == DRAGTYPE_MOVEPOINTS_TIME ||
|
|
m_nDragging == DRAGTYPE_MOVEPOINTS_VALUE )
|
|
{
|
|
int dx = x - m_nLastX;
|
|
int dy = y - m_nLastY;
|
|
|
|
if ( !( modifiers & mxEvent::KeyCtrl ) )
|
|
{
|
|
// Zero out motion on other axis
|
|
if ( m_nDragging == DRAGTYPE_MOVEPOINTS_VALUE )
|
|
{
|
|
dx = 0;
|
|
x = m_nLastX;
|
|
}
|
|
else
|
|
{
|
|
dy = 0;
|
|
y = m_nLastY;
|
|
}
|
|
}
|
|
|
|
float dfdx = (float)dx / g_pExpressionTool->GetPixelsPerSecond();
|
|
float dfdy = (float)dy / (float)height;
|
|
|
|
g_pExpressionTool->MoveSelectedSamples( dfdx, dfdy, snap );
|
|
|
|
// Update the scrubber
|
|
if ( (float)width > 0 )
|
|
{
|
|
float t = GetTimeForMouse( x + m_rcBounds.left );
|
|
g_pExpressionTool->ForceScrubPosition( t );
|
|
|
|
g_pMatSysWindow->Frame();
|
|
}
|
|
}
|
|
|
|
m_nLastX = x;
|
|
m_nLastY = y;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void TimelineItem::DrawFocusRect( void )
|
|
{
|
|
RECT rcFocus;
|
|
|
|
rcFocus.left = m_nStartX < m_nLastX ? m_nStartX : m_nLastX;
|
|
rcFocus.right = m_nStartX < m_nLastX ? m_nLastX : m_nStartX;
|
|
|
|
rcFocus.top = m_nStartY < m_nLastY ? m_nStartY : m_nLastY;
|
|
rcFocus.bottom = m_nStartY < m_nLastY ? m_nLastY : m_nStartY;
|
|
|
|
POINT offset;
|
|
offset.x = m_rcBounds.left;
|
|
offset.y = m_rcBounds.top;
|
|
ClientToScreen( (HWND)m_pWorkspace->getHandle(), &offset );
|
|
OffsetRect( &rcFocus, offset.x, offset.y );
|
|
|
|
HDC dc = GetDC( NULL );
|
|
|
|
::DrawFocusRect( dc, &rcFocus );
|
|
|
|
ReleaseDC( NULL, dc );
|
|
}
|
|
|
|
void TimelineItem::DrawSelf( void )
|
|
{
|
|
CChoreoWidgetDrawHelper drawHelper( m_pWorkspace, m_rcBounds );
|
|
Draw( drawHelper );
|
|
}
|
|
|
|
void TimelineItem::Draw( CChoreoWidgetDrawHelper& drawHelper )
|
|
{
|
|
CFlexAnimationTrack *track = GetSafeTrack();
|
|
if ( !track )
|
|
return;
|
|
|
|
CChoreoEvent *e = track->GetEvent();
|
|
if ( !e )
|
|
return;
|
|
|
|
Assert( e->HasEndTime() );
|
|
|
|
bool active = track && ( IsValid() || IsActive() );
|
|
|
|
float starttime;
|
|
float endtime;
|
|
|
|
g_pExpressionTool->GetStartAndEndTime( starttime, endtime );
|
|
|
|
CountSelected();
|
|
int scount = GetNumSelected();
|
|
|
|
COLORREF bgColor = RGB( 230, 230, 200 );
|
|
if ( IsCollapsed() && active )
|
|
{
|
|
bgColor = RGB( 200, 230, 200 );
|
|
}
|
|
|
|
RECT rcClient = m_rcBounds;
|
|
|
|
drawHelper.DrawFilledRect( bgColor, rcClient );
|
|
|
|
COLORREF gray = RGB( 200, 200, 200 );
|
|
|
|
DrawEventEnd( drawHelper );
|
|
|
|
DrawRelativeTags( drawHelper );
|
|
if ( !IsCollapsed() && track )
|
|
{
|
|
if ( m_nEditType == 1 )
|
|
{
|
|
float zero = track->GetZeroValue( m_nEditType, true );
|
|
|
|
drawHelper.DrawColoredLine( RGB( 180, 200, 220 ), PS_SOLID, 1,
|
|
rcClient.left, ( rcClient.top * zero + rcClient.bottom * (1 - zero)) ,
|
|
rcClient.right, ( rcClient.top * zero + rcClient.bottom * (1 - zero)) );
|
|
}
|
|
|
|
drawHelper.DrawOutlinedRect( RGB( 100, 150, 200 ), PS_SOLID, 1, rcClient );
|
|
|
|
// Draw grow handle into background...
|
|
if ( CanHaveGrowHandle() )
|
|
{
|
|
RECT handleRect;
|
|
GetGrowHandleRect( handleRect );
|
|
DrawGrowHandle( drawHelper, handleRect );
|
|
}
|
|
|
|
// Draw left/right underneath amount so go backbard
|
|
for ( int type = ( track->IsComboType() ? 1 : 0 ); type >= 0; type-- )
|
|
{
|
|
COLORREF lineColor = ( type == m_nEditType ) ? RGB( 0, 0, 255 ) : gray;
|
|
COLORREF shadowColor = ( type == m_nEditType ) ? RGB( 150, 150, 250 ) : gray;
|
|
COLORREF dotColor = ( type == m_nEditType ) ? RGB( 0, 0, 255 ) : gray;
|
|
COLORREF dotColorSelected = ( type == m_nEditType ) ? RGB( 240, 80, 20 ) : gray;
|
|
|
|
int height = rcClient.bottom - rcClient.top;
|
|
int bottom = rcClient.bottom;
|
|
|
|
// Fixme, could look at 1st derivative and do more sampling at high rate of change?
|
|
// or near actual sample points!
|
|
float linelength = g_pExpressionTool->IsFocusItem( this ) ? 2.0f : 8.0f;
|
|
|
|
float timestepperpixel = linelength / g_pExpressionTool->GetPixelsPerSecond();
|
|
|
|
float stoptime = min( endtime, e->GetDuration() );
|
|
|
|
float prev_t = starttime;
|
|
float prev_value = track->GetFracIntensity( prev_t, type );
|
|
|
|
CUtlVector< POINT > segments;
|
|
|
|
/*
|
|
if (type == m_nEditType)
|
|
{
|
|
// draw hermite version of time step
|
|
float i0, i1, i2;
|
|
float time10hz = starttime;
|
|
|
|
i0 = track->GetFracIntensity( time10hz, type );
|
|
i1 = i0;
|
|
time10hz = starttime + 0.1;
|
|
i2 = track->GetFracIntensity( time10hz, type );;
|
|
|
|
for ( float t = starttime; t <= stoptime; t += timestepperpixel )
|
|
{
|
|
while (t >= time10hz)
|
|
{
|
|
time10hz += 0.1;
|
|
i0 = i1;
|
|
i1 = i2;
|
|
i2 = track->GetFracIntensity( time10hz, type );;
|
|
}
|
|
|
|
float value = Hermite_Spline( i0, i1, i2, (t - time10hz + 0.1) / 0.1 );
|
|
|
|
int prevx, x;
|
|
|
|
bool clipped1, clipped2;
|
|
x = GetMouseForTime( t, &clipped1 );
|
|
prevx = GetMouseForTime( prev_t, &clipped2 );
|
|
|
|
//if ( !clipped1 && !clipped2 )
|
|
{
|
|
// Draw segment
|
|
//drawHelper.DrawColoredLine( lineColor, PS_SOLID, 1,
|
|
// prevx, bottom - prev_value * height,
|
|
// x, bottom - value * height );
|
|
|
|
POINT pt;
|
|
|
|
if ( segments.Count() == 0 )
|
|
{
|
|
pt.x = prevx;
|
|
pt.y = bottom - prev_value * height;
|
|
|
|
segments.AddToTail( pt );
|
|
}
|
|
|
|
pt.x = x;
|
|
pt.y = bottom - value * height;
|
|
|
|
segments.AddToTail( pt );
|
|
}
|
|
|
|
prev_t = t;
|
|
prev_value = value;
|
|
}
|
|
|
|
if ( segments.Count() >= 2 )
|
|
{
|
|
drawHelper.DrawColoredPolyLine( shadowColor, PS_SOLID, 1, segments );
|
|
}
|
|
|
|
segments.RemoveAll();
|
|
}
|
|
*/
|
|
for ( float t = starttime; t <= stoptime; t += timestepperpixel )
|
|
{
|
|
float value = track->GetFracIntensity( t, type );
|
|
|
|
int prevx, x;
|
|
|
|
bool clipped1, clipped2;
|
|
x = GetMouseForTime( t, &clipped1 );
|
|
prevx = GetMouseForTime( prev_t, &clipped2 );
|
|
|
|
//if ( !clipped1 && !clipped2 )
|
|
{
|
|
// Draw segment
|
|
//drawHelper.DrawColoredLine( lineColor, PS_SOLID, 1,
|
|
// prevx, bottom - prev_value * height,
|
|
// x, bottom - value * height );
|
|
|
|
POINT pt;
|
|
|
|
if ( segments.Count() == 0 )
|
|
{
|
|
pt.x = prevx;
|
|
pt.y = bottom - prev_value * height;
|
|
|
|
segments.AddToTail( pt );
|
|
}
|
|
|
|
pt.x = x;
|
|
pt.y = bottom - value * height;
|
|
|
|
segments.AddToTail( pt );
|
|
}
|
|
|
|
prev_t = t;
|
|
prev_value = value;
|
|
}
|
|
|
|
if ( segments.Count() >= 2 )
|
|
{
|
|
drawHelper.DrawColoredPolyLine( lineColor, PS_SOLID, 1, segments );
|
|
}
|
|
|
|
for ( int sample = 0; sample < track->GetNumSamples( type ); sample++ )
|
|
{
|
|
bool dummy;
|
|
CExpressionSample *start = track->GetBoundedSample( sample, dummy, type );
|
|
|
|
/*
|
|
int pixel = (int)( ( start->time / event_time ) * width + 0.5f);
|
|
int x = m_rcBounds.left + pixel;
|
|
float roundedfrac = (float)pixel / (float)width;
|
|
*/
|
|
float value = start->value; // track->GetFracIntensity( start->time, type );
|
|
bool clipped = false;
|
|
int x = GetMouseForTime( start->time, &clipped );
|
|
if ( clipped )
|
|
continue;
|
|
int y = bottom - value * height;
|
|
|
|
int dotsize = 6;
|
|
int dotSizeSelected = 6;
|
|
|
|
COLORREF clr = dotColor;
|
|
COLORREF clrSelected = dotColorSelected;
|
|
|
|
drawHelper.DrawCircle(
|
|
start->selected ? clrSelected : clr,
|
|
x, y,
|
|
start->selected ? dotSizeSelected : dotsize,
|
|
true );
|
|
|
|
|
|
if ( !start->selected )
|
|
continue;
|
|
|
|
if ( start->GetCurveType() == CURVE_DEFAULT )
|
|
continue;
|
|
|
|
// Draw curve type indicator...
|
|
char sz[ 128 ];
|
|
Q_snprintf( sz, sizeof( sz ), "%s", Interpolator_NameForCurveType( start->GetCurveType(), true ) );
|
|
RECT rc;
|
|
int fontSize = 9;
|
|
rc.top = clamp( y + 5, rcClient.top + 2, rcClient.bottom - 2 - fontSize );
|
|
rc.bottom = rc.top + fontSize + 1;
|
|
rc.left = x - 75;
|
|
rc.right = x + 175;
|
|
drawHelper.DrawColoredText( "Arial", fontSize, 500, shadowColor, rc, sz );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( track && track->IsComboType() && !IsCollapsed() )
|
|
{
|
|
RECT title = rcClient;
|
|
title.left += 10;
|
|
title.top += 14;
|
|
title.bottom = title.top + 9;
|
|
|
|
char sz[ 128 ];
|
|
|
|
if ( m_nEditType == 1 )
|
|
{
|
|
sprintf( sz, "left" );
|
|
|
|
drawHelper.DrawColoredText( "Arial", 9, 500, RGB( 0, 0, 255 ), title, sz );
|
|
|
|
sprintf( sz, "right" );
|
|
|
|
title.top = rcClient.bottom - 22;
|
|
title.bottom = rcClient.bottom;
|
|
|
|
drawHelper.DrawColoredText( "Arial", 9, 500, RGB( 0, 0, 255 ), title, sz );
|
|
}
|
|
|
|
int mid = ( rcClient.top + rcClient.bottom ) / 2;
|
|
|
|
title.top = mid - 10;
|
|
title.bottom = mid;
|
|
|
|
sprintf( sz, "editmode: <%s>", m_nEditType == 0 ? "amount" : "left/right" );
|
|
|
|
drawHelper.DrawColoredText( "Arial", 9, 500, RGB( 0, 0, 255 ), title, sz );
|
|
}
|
|
|
|
if ( track )
|
|
{
|
|
RECT title = rcClient;
|
|
title.left += 2;
|
|
title.top += 2;
|
|
title.bottom = title.top + 9;
|
|
|
|
char const *name = track->GetFlexControllerName();
|
|
char sz[ 128 ];
|
|
|
|
if ( scount > 0 )
|
|
{
|
|
sprintf( sz, "{%i} - ", scount );
|
|
|
|
int len = drawHelper.CalcTextWidth( "Arial", 9, 500, sz );
|
|
drawHelper.DrawColoredText( "Arial", 9, 500,
|
|
RGB( 120, 120, 0 ), title, sz );
|
|
|
|
title.left += len + 2;
|
|
}
|
|
|
|
sprintf( sz, "%s -", name );
|
|
|
|
int len = drawHelper.CalcTextWidth( "Arial", 9, 500, sz );
|
|
|
|
drawHelper.DrawColoredText( "Arial", 9, 500,
|
|
active ? RGB( 0, 150, 100 ) : RGB( 100, 100, 100 ),
|
|
title, sz );
|
|
|
|
sprintf( sz, "%s", IsActive() ? "enabled" : "disabled" );
|
|
|
|
title.left += len + 2;
|
|
|
|
len = drawHelper.CalcTextWidth( "Arial", 9, 500, sz );
|
|
drawHelper.DrawColoredText( "Arial", 9, 500,
|
|
active ? RGB( 0, 150, 100 ) : RGB( 100, 100, 100 ),
|
|
title, sz );
|
|
|
|
if ( active )
|
|
{
|
|
title.left += len + 2;
|
|
|
|
sprintf( sz, " <%i>", track->GetNumSamples( 0 ) );
|
|
|
|
len = drawHelper.CalcTextWidth( "Arial", 9, 500, sz );
|
|
drawHelper.DrawColoredText( "Arial", 9, 500, RGB( 220, 0, 00 ), title, sz );
|
|
}
|
|
}
|
|
}
|
|
|
|
void TimelineItem::DrawAutoHighlight( mxEvent *event )
|
|
{
|
|
if ( IsCollapsed() )
|
|
return;
|
|
|
|
CFlexAnimationTrack *track = GetSafeTrack();
|
|
if ( !track )
|
|
return;
|
|
|
|
CExpressionSample *hover = GetSampleUnderMouse( event->x, event->y, 0.0f );
|
|
CChoreoWidgetDrawHelper drawHelper( m_pWorkspace, m_rcBounds, true );
|
|
|
|
RECT rcClient = m_rcBounds;
|
|
|
|
// Draw left/right underneath amount so go backbard
|
|
int type = m_nEditType;
|
|
|
|
COLORREF dotColor = RGB( 0, 0, 255 );
|
|
COLORREF dotColorSelected = RGB( 240, 80, 20 );
|
|
COLORREF clrHighlighted = RGB( 0, 200, 0 );
|
|
|
|
int height = rcClient.bottom - rcClient.top;
|
|
int bottom = rcClient.bottom;
|
|
|
|
int dotsize = 6;
|
|
int dotSizeSelected = 6;
|
|
int dotSizeHighlighted = 6;
|
|
|
|
COLORREF clr = dotColor;
|
|
COLORREF clrSelected = dotColorSelected;
|
|
COLORREF bgColor = RGB( 230, 230, 200 );
|
|
|
|
// Fixme, could look at 1st derivative and do more sampling at high rate of change?
|
|
// or near actual sample points!
|
|
for ( int sample = 0; sample < track->GetNumSamples( type ); sample++ )
|
|
{
|
|
bool dummy;
|
|
CExpressionSample *start = track->GetBoundedSample( sample, dummy, type );
|
|
|
|
float value = start->value;
|
|
bool clipped = false;
|
|
int x = GetMouseForTime( start->time, &clipped );
|
|
if ( clipped )
|
|
continue;
|
|
int y = bottom - value * height;
|
|
|
|
if ( hover == start )
|
|
{
|
|
drawHelper.DrawCircle(
|
|
bgColor,
|
|
x, y,
|
|
dotSizeHighlighted,
|
|
true );
|
|
|
|
drawHelper.DrawCircle(
|
|
clrHighlighted,
|
|
x, y,
|
|
dotSizeHighlighted,
|
|
false );
|
|
|
|
|
|
}
|
|
else
|
|
{
|
|
drawHelper.DrawCircle(
|
|
start->selected ? clrSelected : clr,
|
|
x, y,
|
|
start->selected ? dotSizeSelected : dotsize,
|
|
true );
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : drawHelper -
|
|
//-----------------------------------------------------------------------------
|
|
void TimelineItem::DrawRelativeTags( CChoreoWidgetDrawHelper& drawHelper )
|
|
{
|
|
CFlexAnimationTrack *track = GetSafeTrack();
|
|
if ( !track )
|
|
return;
|
|
|
|
CChoreoEvent *event = track->GetEvent();
|
|
if ( !event )
|
|
return;
|
|
|
|
float duration = event->GetDuration();
|
|
|
|
if ( duration <= 0.0f )
|
|
return;
|
|
|
|
CChoreoScene *scene = g_pChoreoView->GetScene();
|
|
if ( !scene )
|
|
return;
|
|
|
|
RECT rcClient = m_rcBounds;;
|
|
//drawHelper.GetClientRect( rcClient );
|
|
|
|
// Iterate relative tags
|
|
for ( int i = 0; i < scene->GetNumActors(); i++ )
|
|
{
|
|
CChoreoActor *a = scene->GetActor( i );
|
|
if ( !a )
|
|
continue;
|
|
|
|
for ( int j = 0; j < a->GetNumChannels(); j++ )
|
|
{
|
|
CChoreoChannel *c = a->GetChannel( j );
|
|
if ( !c )
|
|
continue;
|
|
|
|
for ( int k = 0 ; k < c->GetNumEvents(); k++ )
|
|
{
|
|
CChoreoEvent *e = c->GetEvent( k );
|
|
if ( !e )
|
|
continue;
|
|
|
|
// add each tag to combo box
|
|
for ( int t = 0; t < e->GetNumRelativeTags(); t++ )
|
|
{
|
|
CEventRelativeTag *tag = e->GetRelativeTag( t );
|
|
if ( !tag )
|
|
continue;
|
|
|
|
//SendMessage( control, CB_ADDSTRING, 0, (LPARAM)va( "\"%s\" \"%s\"", tag->GetName(), e->GetParameters() ) );
|
|
bool clipped = false;
|
|
int tagx = GetMouseForTime( tag->GetStartTime() - event->GetStartTime(), &clipped );
|
|
if ( clipped )
|
|
continue;
|
|
|
|
drawHelper.DrawColoredLine( RGB( 180, 180, 220 ), PS_SOLID, 1, tagx, rcClient.top, tagx, rcClient.bottom );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for ( int t = 0; t < event->GetNumTimingTags(); t++ )
|
|
{
|
|
CFlexTimingTag *tag = event->GetTimingTag( t );
|
|
if ( !tag )
|
|
continue;
|
|
|
|
bool clipped = false;
|
|
int tagx = GetMouseForTime( tag->GetStartTime() - event->GetStartTime(), &clipped );
|
|
if ( clipped )
|
|
continue;
|
|
|
|
// Draw relative tag marker
|
|
drawHelper.DrawColoredLine( RGB( 220, 180, 180 ), PS_SOLID, 1, tagx, rcClient.top, tagx, rcClient.bottom );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : *exp -
|
|
// flexnum -
|
|
//-----------------------------------------------------------------------------
|
|
void TimelineItem::SetExpressionInfo( CFlexAnimationTrack *track, int flexnum )
|
|
{
|
|
m_szTrackName[ 0 ] = 0;
|
|
if ( track )
|
|
{
|
|
V_strcpy_safe( m_szTrackName, track->GetFlexControllerName() );
|
|
SetActive( track->IsTrackActive() );
|
|
}
|
|
|
|
m_nFlexNum = flexnum;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void TimelineItem::Copy( void )
|
|
{
|
|
if ( !g_pExpressionTool )
|
|
return;
|
|
|
|
CFlexAnimationTrack *track = GetSafeTrack();
|
|
if ( !track )
|
|
return;
|
|
|
|
g_pExpressionTool->Copy( track );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void TimelineItem::Paste( void )
|
|
{
|
|
if ( !g_pExpressionTool )
|
|
return;
|
|
|
|
if ( !g_pExpressionTool->HasCopyData() )
|
|
return;
|
|
|
|
CFlexAnimationTrack *track = GetSafeTrack();
|
|
if ( !track )
|
|
return;
|
|
|
|
g_pExpressionTool->Paste( track );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void TimelineItem::Clear( bool preserveundo )
|
|
{
|
|
CFlexAnimationTrack *track = GetSafeTrack();
|
|
if ( !track )
|
|
return;
|
|
|
|
if ( preserveundo )
|
|
{
|
|
PreDataChanged( "Clear" );
|
|
}
|
|
|
|
track->Clear();
|
|
|
|
if ( preserveundo )
|
|
{
|
|
PostDataChanged( "Clear" );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : state -
|
|
//-----------------------------------------------------------------------------
|
|
void TimelineItem::SetCollapsed( bool state )
|
|
{
|
|
m_bCollapsed = state;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Output : Returns true on success, false on failure.
|
|
//-----------------------------------------------------------------------------
|
|
bool TimelineItem::IsCollapsed( void ) const
|
|
{
|
|
return m_bCollapsed;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Output : int
|
|
//-----------------------------------------------------------------------------
|
|
int TimelineItem::GetHeight( void )
|
|
{
|
|
return ( IsCollapsed() ? 12 : m_nCurrentHeight );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : state -
|
|
//-----------------------------------------------------------------------------
|
|
void TimelineItem::SetActive( bool state )
|
|
{
|
|
CFlexAnimationTrack *track = GetSafeTrack();
|
|
if ( !track )
|
|
return;
|
|
|
|
track->SetTrackActive( state );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Output : Returns true on success, false on failure.
|
|
//-----------------------------------------------------------------------------
|
|
bool TimelineItem::IsActive( void )
|
|
{
|
|
CFlexAnimationTrack *track = GetSafeTrack();
|
|
if ( !track )
|
|
return false;
|
|
|
|
return track->IsTrackActive();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Output : Returns true on success, false on failure.
|
|
//-----------------------------------------------------------------------------
|
|
bool TimelineItem::IsValid( void )
|
|
{
|
|
CFlexAnimationTrack *track = GetSafeTrack();
|
|
if ( !track )
|
|
return false;
|
|
|
|
if ( track->GetNumSamples( 0 ) > 0 )
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Output : CFlexAnimationTrack
|
|
//-----------------------------------------------------------------------------
|
|
CFlexAnimationTrack *TimelineItem::GetSafeTrack( void )
|
|
{
|
|
if ( !g_pExpressionTool )
|
|
return NULL;
|
|
|
|
CChoreoEvent *ev = g_pExpressionTool->GetSafeEvent();
|
|
if ( !ev )
|
|
return NULL;
|
|
|
|
// Find track by name
|
|
for ( int i = 0; i < ev->GetNumFlexAnimationTracks() ; i++ )
|
|
{
|
|
CFlexAnimationTrack *track = ev->GetFlexAnimationTrack( i );
|
|
if ( track && !stricmp( track->GetFlexControllerName(), m_szTrackName ) )
|
|
{
|
|
return track;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : type -
|
|
//-----------------------------------------------------------------------------
|
|
void TimelineItem::SetEditType( int type )
|
|
{
|
|
Assert( type == 0 || type == 1 );
|
|
|
|
CFlexAnimationTrack *track = GetSafeTrack();
|
|
if ( !track || !track->IsComboType() )
|
|
{
|
|
type = 0;
|
|
}
|
|
|
|
m_nEditType = type;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Output : int
|
|
//-----------------------------------------------------------------------------
|
|
int TimelineItem::GetEditType( void )
|
|
{
|
|
return m_nEditType;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void TimelineItem::SelectPoints( void )
|
|
{
|
|
RECT rcSelection;
|
|
|
|
rcSelection.left = m_nStartX < m_nLastX ? m_nStartX : m_nLastX;
|
|
rcSelection.right = m_nStartX < m_nLastX ? m_nLastX : m_nStartX;
|
|
|
|
rcSelection.top = m_nStartY < m_nLastY ? m_nStartY : m_nLastY;
|
|
rcSelection.bottom = m_nStartY < m_nLastY ? m_nLastY : m_nStartY;
|
|
|
|
int selW = rcSelection.right - rcSelection.left;
|
|
int selH = rcSelection.bottom - rcSelection.top;
|
|
|
|
float tolerance = FP_TL_SELECTION_RECTANGLE_TOLERANCE;
|
|
// If they are just clicking and releasing in one spot, capture any items w/in a larger tolerance
|
|
if ( selW <= 2 && selH <= 2 )
|
|
{
|
|
tolerance = FP_TL_SELECTION_TOLERANCE;
|
|
|
|
CExpressionSample *sample = GetSampleUnderMouse( rcSelection.left + selW * 0.5f, rcSelection.top + selH * 0.5f );
|
|
if ( sample )
|
|
{
|
|
sample->selected = true;
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
InflateRect( &rcSelection, 3, 3 );
|
|
}
|
|
|
|
int width = m_rcBounds.right - m_rcBounds.left;
|
|
int height = m_rcBounds.bottom - m_rcBounds.top;
|
|
|
|
CFlexAnimationTrack *track = GetSafeTrack();
|
|
if ( !track || !width || !height )
|
|
return;
|
|
|
|
CChoreoEvent *e = track->GetEvent();
|
|
Assert( e );
|
|
if ( !e )
|
|
return;
|
|
|
|
float duration = e->GetDuration();
|
|
|
|
float fleft = (float)GetTimeForMouse( rcSelection.left + m_rcBounds.left );
|
|
float fright = (float)GetTimeForMouse( rcSelection.right + m_rcBounds.left );
|
|
|
|
//fleft *= duration;
|
|
//fright *= duration;
|
|
|
|
float ftop = (float)rcSelection.top / (float)height;
|
|
float fbottom = (float)rcSelection.bottom / (float)height;
|
|
|
|
fleft = clamp( fleft, 0.0f, duration );
|
|
fright = clamp( fright, 0.0f, duration );
|
|
ftop = clamp( ftop, 0.0f, 1.0f );
|
|
fbottom = clamp( fbottom, 0.0f, 1.0f );
|
|
|
|
float timestepperpixel = 1.0f / g_pExpressionTool->GetPixelsPerSecond();
|
|
float yfracstepperpixel = 1.0f / (float)height;
|
|
|
|
float epsx = tolerance*timestepperpixel;
|
|
float epsy = tolerance*yfracstepperpixel;
|
|
|
|
for ( int i = 0; i < track->GetNumSamples( m_nEditType ); i++ )
|
|
{
|
|
CExpressionSample *sample = track->GetSample( i, m_nEditType );
|
|
|
|
if ( sample->time + epsx < fleft )
|
|
continue;
|
|
|
|
if ( sample->time - epsx > fright )
|
|
continue;
|
|
|
|
if ( (1.0f - sample->value ) + epsy < ftop )
|
|
continue;
|
|
|
|
if ( (1.0f - sample->value ) - epsy > fbottom )
|
|
continue;
|
|
|
|
sample->selected = true;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : *undodescription -
|
|
//-----------------------------------------------------------------------------
|
|
void TimelineItem::PreDataChanged( char const *undodescription )
|
|
{
|
|
if ( m_nUndoSetup == 0 )
|
|
{
|
|
g_pChoreoView->SetDirty( true );
|
|
g_pChoreoView->PushUndo( undodescription );
|
|
}
|
|
++m_nUndoSetup;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : *redodescription -
|
|
//-----------------------------------------------------------------------------
|
|
void TimelineItem::PostDataChanged( char const *redodescription )
|
|
{
|
|
--m_nUndoSetup;
|
|
if ( m_nUndoSetup == 0 )
|
|
{
|
|
g_pChoreoView->PushRedo( redodescription );
|
|
g_pExpressionTool->InvalidateLayout();
|
|
}
|
|
}
|
|
|
|
void TimelineItem::SetBounds( const RECT& rect )
|
|
{
|
|
m_rcBounds = rect;
|
|
}
|
|
|
|
void TimelineItem::GetBounds( RECT& rect )
|
|
{
|
|
rect = m_rcBounds;
|
|
}
|
|
|
|
void TimelineItem::SetVisible( bool vis )
|
|
{
|
|
m_bVisible = vis;
|
|
}
|
|
|
|
bool TimelineItem::GetVisible( void ) const
|
|
{
|
|
return m_bVisible;
|
|
}
|
|
|
|
int TimelineItem::GetNumSelected( void )
|
|
{
|
|
return m_nNumSelected;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void TimelineItem::SnapAll()
|
|
{
|
|
CFlexAnimationTrack *track = GetSafeTrack();
|
|
if ( !track )
|
|
return;
|
|
|
|
for ( int t = 0; t < 2; t++ )
|
|
{
|
|
for ( int i = 0; i < track->GetNumSamples( t ); i++ )
|
|
{
|
|
CExpressionSample *sample = track->GetSample( i, t );
|
|
sample->time = FacePoser_SnapTime( sample->time );
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void TimelineItem::SnapSelected()
|
|
{
|
|
CFlexAnimationTrack *track = GetSafeTrack();
|
|
if ( !track )
|
|
return;
|
|
|
|
for ( int t = 0; t < 2; t++ )
|
|
{
|
|
for ( int i = 0; i < track->GetNumSamples( t ); i++ )
|
|
{
|
|
CExpressionSample *sample = track->GetSample( i, t );
|
|
if ( !sample->selected )
|
|
continue;
|
|
|
|
sample->time = FacePoser_SnapTime( sample->time );
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : start -
|
|
// end -
|
|
//-----------------------------------------------------------------------------
|
|
void TimelineItem::DeletePoints( float start, float end )
|
|
{
|
|
CFlexAnimationTrack *track = GetSafeTrack();
|
|
if ( !track )
|
|
return;
|
|
|
|
for ( int t = 0; t < 2; t++ )
|
|
{
|
|
int num = track->GetNumSamples( t );
|
|
for ( int i = num - 1; i >= 0 ; i-- )
|
|
{
|
|
CExpressionSample *sample = track->GetSample( i, t );
|
|
if ( sample->time < start || sample->time > end )
|
|
continue;
|
|
|
|
track->RemoveSample( i, t );
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void TimelineItem::OnDoubleClicked()
|
|
{
|
|
// Disabled for now by request of BillF
|
|
CFlexAnimationTrack *track = GetSafeTrack();
|
|
if ( !track )
|
|
return;
|
|
|
|
SetCollapsed( !IsCollapsed() );
|
|
g_pExpressionTool->LayoutItems( true );
|
|
}
|
|
|
|
void TimelineItem::DrawEventEnd( CChoreoWidgetDrawHelper& drawHelper )
|
|
{
|
|
CFlexAnimationTrack *track = GetSafeTrack();
|
|
if ( !track )
|
|
return;
|
|
|
|
CChoreoEvent *e = track->GetEvent();
|
|
if ( !e )
|
|
return;
|
|
|
|
float duration = e->GetDuration();
|
|
if ( !duration )
|
|
return;
|
|
|
|
int leftx = GetMouseForTime( duration );
|
|
if ( leftx > m_rcBounds.right )
|
|
return;
|
|
|
|
drawHelper.DrawColoredLine(
|
|
COLOR_CHOREO_ENDTIME, PS_SOLID, 1,
|
|
leftx, m_rcBounds.top, leftx, m_rcBounds.bottom );
|
|
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : helper -
|
|
// handleRect -
|
|
//-----------------------------------------------------------------------------
|
|
void TimelineItem::DrawGrowHandle( CChoreoWidgetDrawHelper& helper, RECT& handleRect )
|
|
{
|
|
Assert(CanHaveGrowHandle());
|
|
|
|
RECT useRect = handleRect;
|
|
helper.OffsetSubRect( useRect );
|
|
|
|
POINT region[4];
|
|
int cPoints = 4;
|
|
|
|
region[ 0 ].x = useRect.left + GROW_HANDLE_INSETPIXELS;
|
|
region[ 0 ].y = useRect.top;
|
|
|
|
region[ 1 ].x = useRect.right - GROW_HANDLE_INSETPIXELS;
|
|
region[ 1 ].y = useRect.top;
|
|
|
|
region[ 2 ].x = useRect.right;
|
|
region[ 2 ].y = useRect.bottom;
|
|
|
|
region[ 3 ].x = useRect.left;
|
|
region[ 3 ].y = useRect.bottom;
|
|
|
|
HDC dc = helper.GrabDC();
|
|
|
|
HRGN rgn = CreatePolygonRgn( region, cPoints, ALTERNATE );
|
|
|
|
int oldPF = SetPolyFillMode( dc, ALTERNATE );
|
|
|
|
HBRUSH brBg = CreateSolidBrush( RGB( 150, 150, 150 ) );
|
|
HBRUSH brBorder = CreateSolidBrush( RGB( 200, 200, 200 ) );
|
|
|
|
FillRgn( dc, rgn, brBg );
|
|
FrameRgn( dc, rgn, brBorder, 1, 1 );
|
|
|
|
SetPolyFillMode( dc, oldPF );
|
|
|
|
DeleteObject( rgn );
|
|
|
|
DeleteObject( brBg );
|
|
DeleteObject( brBorder );
|
|
|
|
// draw a line in the middle
|
|
int midy = ( handleRect.bottom + handleRect.top ) * 0.5f;
|
|
int lineinset = GROW_HANDLE_INSETPIXELS *1.5;
|
|
|
|
helper.DrawColoredLine( RGB( 63, 63, 63 ), PS_SOLID, 1,
|
|
handleRect.left + lineinset, midy,
|
|
handleRect.right - lineinset, midy );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : rc -
|
|
//-----------------------------------------------------------------------------
|
|
void TimelineItem::GetGrowHandleRect( RECT& rc )
|
|
{
|
|
rc = m_rcBounds;
|
|
rc.bottom -= 1;
|
|
rc.top = rc.bottom - GROW_HANDLE_HEIGHT;
|
|
rc.left = ( rc.right + rc.left ) / 2 - GROW_HANDLE_WIDTH / 2;
|
|
rc.right = rc.left + GROW_HANDLE_WIDTH;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Output : Returns true on success, false on failure.
|
|
//-----------------------------------------------------------------------------
|
|
bool TimelineItem::CanHaveGrowHandle()
|
|
{
|
|
if ( IsCollapsed() )
|
|
return false;
|
|
|
|
if ( !g_pExpressionTool->IsFocusItem( this ) )
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : x -
|
|
// y -
|
|
// Output : Returns true on success, false on failure.
|
|
//-----------------------------------------------------------------------------
|
|
bool TimelineItem::IsMouseOverGrowHandle( int x, int y)
|
|
{
|
|
if ( !CanHaveGrowHandle() )
|
|
return false;
|
|
|
|
RECT rcGrowHandle;
|
|
GetGrowHandleRect( rcGrowHandle );
|
|
|
|
POINT pt;
|
|
pt.x = x + m_rcBounds.left;
|
|
pt.y = y + m_rcBounds.top;
|
|
|
|
return PtInRect( &rcGrowHandle, pt ) ? true : false;
|
|
}
|
|
|
|
void TimelineItem::DrawGrowRect()
|
|
{
|
|
RECT rcFocus = m_rcBounds;
|
|
rcFocus.bottom = m_rcBounds.top + m_nLastY;
|
|
OffsetRect( &rcFocus, -m_rcBounds.left, -m_rcBounds.top );
|
|
|
|
POINT offset;
|
|
offset.x = m_rcBounds.left;
|
|
offset.y = m_rcBounds.top;
|
|
ClientToScreen( (HWND)m_pWorkspace->getHandle(), &offset );
|
|
OffsetRect( &rcFocus, offset.x, offset.y );
|
|
|
|
HDC dc = GetDC( NULL );
|
|
|
|
::DrawFocusRect( dc, &rcFocus );
|
|
|
|
ReleaseDC( NULL, dc );
|
|
}
|
|
|
|
void TimelineItem::GetWorkList( bool reflect, CUtlVector< TimelineItem * >& list )
|
|
{
|
|
if ( !reflect )
|
|
{
|
|
list.AddToTail( this );
|
|
}
|
|
else
|
|
{
|
|
g_pExpressionTool->GetTimelineItems( list );
|
|
}
|
|
}
|