Team Fortress 2 Source Code as on 22/4/2020
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

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "hlfaceposer.h"
  8. #include <stdio.h>
  9. #include "TimelineItem.h"
  10. #include "choreowidgetdrawhelper.h"
  11. #include "mathlib/mathlib.h"
  12. #include "expressions.h"
  13. #include "StudioModel.h"
  14. #include "expclass.h"
  15. #include "mathlib/mathlib.h"
  16. #include "ExpressionTool.h"
  17. #include "choreoevent.h"
  18. #include "choreoscene.h"
  19. #include "choreoactor.h"
  20. #include "choreochannel.h"
  21. #include "ChoreoView.h"
  22. #include "ControlPanel.h"
  23. #include "faceposer_models.h"
  24. #include "MatSysWin.h"
  25. #include "choreoviewcolors.h"
  26. #include "ifaceposersound.h"
  27. #include "curveeditorhelpers.h"
  28. extern double realtime;
  29. #define DOUBLE_CLICK_TIME 0.2
  30. #define GROW_HANDLE_WIDTH 66
  31. #define GROW_HANDLE_HEIGHT 8
  32. #define GROW_HANDLE_INSETPIXELS 8
  33. #define TIMELINEITEM_DEFAULT_HEIGHT 100
  34. //-----------------------------------------------------------------------------
  35. // Purpose:
  36. //-----------------------------------------------------------------------------
  37. TimelineItem::TimelineItem( mxWindow *workspace )
  38. {
  39. m_pHelper = new CCurveEditorHelper< TimelineItem >( this );
  40. m_pWorkspace = workspace;
  41. m_nDragging = DRAGTYPE_NONE;
  42. m_nLastX = 0;
  43. m_nLastY = 0;
  44. m_nStartX = 0;
  45. m_nStartY = 0;
  46. SetExpressionInfo( NULL, 0 );
  47. m_nNumSelected = 0;
  48. m_nEditType = 0;
  49. SetCollapsed( false );
  50. SetActive( false );
  51. m_nUndoSetup = 0;
  52. m_rcBounds.top = 0;
  53. m_rcBounds.bottom = 0;
  54. m_rcBounds.left = 0;
  55. m_rcBounds.right = 0;
  56. m_bVisible = false;
  57. m_flLastClickTime = -1;
  58. m_nCurrentHeight = TIMELINEITEM_DEFAULT_HEIGHT;
  59. }
  60. TimelineItem::~TimelineItem( void )
  61. {
  62. delete m_pHelper;
  63. }
  64. //-----------------------------------------------------------------------------
  65. // Purpose:
  66. //-----------------------------------------------------------------------------
  67. void TimelineItem::ResetHeight()
  68. {
  69. m_nCurrentHeight = TIMELINEITEM_DEFAULT_HEIGHT;
  70. }
  71. //-----------------------------------------------------------------------------
  72. // Purpose:
  73. // Input : mx -
  74. // Output : float
  75. //-----------------------------------------------------------------------------
  76. float TimelineItem::GetTimeForMouse( int mx, bool clip /*= false*/ )
  77. {
  78. float start, end;
  79. g_pExpressionTool->GetStartAndEndTime( start, end );
  80. if ( clip )
  81. {
  82. if ( mx < m_rcBounds.left )
  83. {
  84. return start;
  85. }
  86. else if ( mx >= m_rcBounds.right )
  87. {
  88. return end;
  89. }
  90. }
  91. float frac = (float)( mx - m_rcBounds.left ) / (float)( m_rcBounds.right - m_rcBounds.left );
  92. float t = start + frac * ( end - start );
  93. return t;
  94. }
  95. //-----------------------------------------------------------------------------
  96. // Purpose:
  97. // Input : t -
  98. // Output : int
  99. //-----------------------------------------------------------------------------
  100. int TimelineItem::GetMouseForTime( float t, bool *clipped /*= NULL*/ )
  101. {
  102. float start, end;
  103. g_pExpressionTool->GetStartAndEndTime( start, end );
  104. float frac = ( t - start ) / ( end - start );
  105. if ( frac < 0.0 || frac > 1.0 )
  106. {
  107. if ( clipped )
  108. {
  109. *clipped = true;
  110. }
  111. }
  112. int mx = m_rcBounds.left + ( int ) ( frac * (float)( m_rcBounds.right - m_rcBounds.left ) );
  113. return mx;
  114. }
  115. int TimelineItem::NumSamples()
  116. {
  117. CFlexAnimationTrack *track = GetSafeTrack();
  118. if ( !track )
  119. return 0;
  120. // Aggregate both types of tracks together
  121. return track->GetNumSamples( 0 ) + track->GetNumSamples( 1 );
  122. // return track->GetNumSamples( m_nEditType );
  123. }
  124. CExpressionSample *TimelineItem::GetSample( int idx )
  125. {
  126. CFlexAnimationTrack *track = GetSafeTrack();
  127. if ( !track )
  128. return NULL;
  129. if ( idx >= track->GetNumSamples( 0 ) )
  130. {
  131. // Rebase and look at left/right track instead
  132. idx -= track->GetNumSamples( 0 );
  133. return track->GetSample( idx, 1 );
  134. }
  135. return track->GetSample( idx, 0 );
  136. }
  137. CExpressionSample *TimelineItem::GetSampleUnderMouse( int mx, int my, float tolerance /*= FP_TL_SELECTION_TOLERANCE*/ )
  138. {
  139. CFlexAnimationTrack *track = GetSafeTrack();
  140. if ( !track )
  141. return NULL;
  142. CChoreoEvent *e = track->GetEvent();
  143. if ( !e )
  144. return NULL;
  145. float closest_dist = 9999999.f;
  146. CExpressionSample *bestsample = NULL;
  147. // Add a sample point
  148. int height = m_rcBounds.bottom - m_rcBounds.top;
  149. mx += m_rcBounds.left;
  150. for ( int i = 0; i < track->GetNumSamples( m_nEditType ); i++ )
  151. {
  152. CExpressionSample *sample = track->GetSample( i, m_nEditType );
  153. bool clipped = false;
  154. int px = GetMouseForTime( sample->time, &clipped );
  155. int py = height * ( 1.0f - sample->value );
  156. int dx = px - mx;
  157. int dy = py - my;
  158. float dist = sqrt( (float)(dx * dx + dy * dy) );
  159. if ( dist < closest_dist )
  160. {
  161. bestsample = sample;
  162. closest_dist = dist;
  163. }
  164. }
  165. // Not close to any of them!!!
  166. if ( ( tolerance != 0.0f ) &&
  167. ( closest_dist > tolerance ) )
  168. return NULL;
  169. return bestsample;
  170. }
  171. //-----------------------------------------------------------------------------
  172. // Purpose:
  173. //-----------------------------------------------------------------------------
  174. void TimelineItem::DeselectAll( void )
  175. {
  176. g_pExpressionTool->DeselectAll();
  177. }
  178. //-----------------------------------------------------------------------------
  179. // Purpose:
  180. //-----------------------------------------------------------------------------
  181. void TimelineItem::SelectAll( void )
  182. {
  183. CFlexAnimationTrack *track = GetSafeTrack();
  184. if ( !track )
  185. return;
  186. for ( int t = 0; t < 2; t++ )
  187. {
  188. for ( int i = 0; i < track->GetNumSamples( t ); i++ )
  189. {
  190. CExpressionSample *sample = track->GetSample( i, t );
  191. sample->selected = true;
  192. }
  193. }
  194. }
  195. //-----------------------------------------------------------------------------
  196. // Purpose:
  197. //-----------------------------------------------------------------------------
  198. void TimelineItem::Delete( void )
  199. {
  200. g_pExpressionTool->DeleteSelectedSamples();
  201. }
  202. //-----------------------------------------------------------------------------
  203. // Purpose:
  204. // Input : sample -
  205. //-----------------------------------------------------------------------------
  206. void TimelineItem::AddSample( CExpressionSample const& sample )
  207. {
  208. CFlexAnimationTrack *track = GetSafeTrack();
  209. if ( !track )
  210. return;
  211. PreDataChanged( "Add sample point" );
  212. track->AddSample( sample.time, sample.value, m_nEditType );
  213. track->Resort( m_nEditType );
  214. SetActive( true );
  215. PostDataChanged( "Add sample point" );
  216. }
  217. //-----------------------------------------------------------------------------
  218. // Purpose:
  219. //-----------------------------------------------------------------------------
  220. int TimelineItem::CountSelected( void )
  221. {
  222. m_nNumSelected = m_pHelper->CountSelected( false );
  223. return m_nNumSelected;
  224. }
  225. void TimelineItem::SetMousePositionForEvent( mxEvent *event )
  226. {
  227. POINT pt;
  228. GetCursorPos( &pt );
  229. ScreenToClient( (HWND)m_pWorkspace->getHandle(), &pt );
  230. pt.x -= m_rcBounds.left;
  231. pt.y -= m_rcBounds.top;
  232. event->x = pt.x;
  233. event->y = pt.y;
  234. }
  235. int TimelineItem::handleEvent( mxEvent *event )
  236. {
  237. int iret = 0;
  238. // Give helper a shot at the event
  239. if ( m_pHelper->HelperHandleEvent( event ) )
  240. {
  241. return 1;
  242. }
  243. switch ( event->event )
  244. {
  245. case mxEvent::KeyDown:
  246. {
  247. switch ( event->key )
  248. {
  249. default:
  250. iret = g_pChoreoView->HandleZoomKey( g_pExpressionTool, event->key );
  251. break;
  252. case VK_ESCAPE:
  253. DeselectAll();
  254. DrawSelf();
  255. break;
  256. case VK_DELETE:
  257. Delete();
  258. DrawSelf();
  259. break;
  260. case 'C':
  261. Copy();
  262. DrawSelf();
  263. break;
  264. case 'V':
  265. Paste();
  266. DrawSelf();
  267. break;
  268. case 'J':
  269. {
  270. g_pExpressionTool->OnCopyToFlex( g_pExpressionTool->GetScrubberSceneTime(), true );
  271. }
  272. break;
  273. case 'K':
  274. {
  275. g_pExpressionTool->OnCopyFromFlex( g_pExpressionTool->GetScrubberSceneTime(), false );
  276. }
  277. break;
  278. case 188: // VK_OEM_COMMA:
  279. {
  280. g_pExpressionTool->SetScrubTargetTime( 0.0f );
  281. }
  282. break;
  283. case 190: // VK_OEM_PERIOD:
  284. {
  285. CChoreoScene *scene = g_pChoreoView->GetScene();
  286. if ( scene )
  287. {
  288. g_pExpressionTool->SetScrubTargetTime( scene->FindStopTime() );
  289. }
  290. }
  291. break;
  292. case VK_LEFT:
  293. {
  294. CChoreoScene *scene = g_pChoreoView->GetScene();
  295. if ( scene && scene->GetSceneFPS() > 0 )
  296. {
  297. float curscrub = g_pExpressionTool->GetScrub();
  298. curscrub -= ( 1.0f / (float)scene->GetSceneFPS() );
  299. curscrub = max( curscrub, 0.0f );
  300. g_pExpressionTool->SetScrubTargetTime( curscrub );
  301. }
  302. }
  303. break;
  304. case VK_RIGHT:
  305. {
  306. CChoreoScene *scene = g_pChoreoView->GetScene();
  307. if ( scene && scene->GetSceneFPS() > 0 )
  308. {
  309. float curscrub = g_pExpressionTool->GetScrub();
  310. curscrub += ( 1.0f / (float)scene->GetSceneFPS() );
  311. curscrub = min( curscrub, scene->FindStopTime() );
  312. g_pExpressionTool->SetScrubTargetTime( curscrub );
  313. }
  314. }
  315. break;
  316. case 191:
  317. {
  318. if ( g_pChoreoView->IsPlayingScene() )
  319. {
  320. g_pChoreoView->StopScene();
  321. }
  322. }
  323. break;
  324. }
  325. iret = 1;
  326. }
  327. break;
  328. case mxEvent::KeyUp:
  329. {
  330. switch ( event->key )
  331. {
  332. case VK_SPACE:
  333. {
  334. CFlexAnimationTrack *track = GetSafeTrack();
  335. if ( track && track->IsComboType() )
  336. {
  337. SetEditType( m_nEditType == 0 ? 1 : 0 );
  338. DrawSelf();
  339. }
  340. }
  341. break;
  342. }
  343. iret = 1;
  344. }
  345. break;
  346. case mxEvent::MouseDown:
  347. {
  348. sound->Flush();
  349. SetFocus( (HWND)g_pExpressionTool->getHandle() );
  350. int height = m_rcBounds.bottom - m_rcBounds.top;
  351. bool rightbutton = ( event->buttons & mxEvent::MouseRightButton ) ? true : false;
  352. if ( m_nDragging == DRAGTYPE_NONE )
  353. {
  354. bool ctrlDown = ( event->modifiers & mxEvent::KeyCtrl ) ? true : false;
  355. CExpressionSample *sample = GetSampleUnderMouse( event->x, event->y, ctrlDown ? FP_TL_ADDSAMPLE_TOLERANCE : FP_TL_SELECTION_TOLERANCE );
  356. if ( IsMouseOverGrowHandle( (short)event->x, (short)event->y ) )
  357. {
  358. m_nDragging = DRAGTYPE_GROW;
  359. m_nLastX = (short)event->x;
  360. m_nLastY = (short)event->y;
  361. m_nStartX = m_nLastX;
  362. m_nStartY = m_nLastY;
  363. MouseDrag( (short)event->x, (short)event->y, event->modifiers );
  364. DrawGrowRect();
  365. }
  366. else if ( sample )
  367. {
  368. if ( event->modifiers & mxEvent::KeyShift )
  369. {
  370. sample->selected = !sample->selected;
  371. DrawSelf();
  372. }
  373. else if ( sample->selected )
  374. {
  375. m_nDragging = rightbutton ? DRAGTYPE_MOVEPOINTS_TIME : DRAGTYPE_MOVEPOINTS_VALUE;
  376. m_nLastX = (short)event->x;
  377. m_nLastY = (short)event->y;
  378. m_nStartX = m_nLastX;
  379. m_nStartY = m_nLastY;
  380. PreDataChanged( "Move sample point(s)" );
  381. MouseDrag( (short)event->x, (short)event->y, event->modifiers );
  382. DrawSelf();
  383. }
  384. else
  385. {
  386. if ( !( event->modifiers & mxEvent::KeyShift ) )
  387. {
  388. DeselectAll();
  389. DrawSelf();
  390. }
  391. m_nDragging = DRAGTYPE_SELECTION;
  392. m_nLastX = (short)event->x;
  393. m_nLastY = (short)event->y;
  394. m_nStartX = m_nLastX;
  395. m_nStartY = m_nLastY;
  396. MouseDrag( (short)event->x, (short)event->y, event->modifiers );
  397. DrawFocusRect();
  398. }
  399. }
  400. else if ( event->modifiers & mxEvent::KeyCtrl )
  401. {
  402. CChoreoEvent *e = g_pExpressionTool->GetSafeEvent();
  403. if ( e )
  404. {
  405. // Add a sample point
  406. float t = GetTimeForMouse( (short)event->x + m_rcBounds.left );
  407. CExpressionSample sample;
  408. sample.time = FacePoser_SnapTime( t );
  409. sample.value = 1.0f - (float)( (short)( event->y ) ) / (float)height;
  410. sample.selected = false;
  411. AddSample( sample );
  412. DrawSelf();
  413. }
  414. }
  415. else
  416. {
  417. if ( rightbutton )
  418. {
  419. POINT pt;
  420. pt.x = event->x;
  421. pt.y = event->y;
  422. ClientToScreen( (HWND)m_pWorkspace->getHandle(), &pt );
  423. ScreenToClient( (HWND)g_pExpressionTool->getHandle(), &pt );
  424. event->x = pt.x;
  425. event->y = pt.y;
  426. g_pExpressionTool->ShowContextMenu( event, true );
  427. return iret;
  428. }
  429. if ( !( event->modifiers & mxEvent::KeyShift ) )
  430. {
  431. DeselectAll();
  432. DrawSelf();
  433. }
  434. m_nDragging = DRAGTYPE_SELECTION;
  435. m_nLastX = (short)event->x;
  436. m_nLastY = (short)event->y;
  437. m_nStartX = m_nLastX;
  438. m_nStartY = m_nLastY;
  439. MouseDrag( (short)event->x, (short)event->y, event->modifiers );
  440. DrawFocusRect();
  441. }
  442. }
  443. iret = 1;
  444. }
  445. break;
  446. case mxEvent::MouseDrag:
  447. case mxEvent::MouseMove:
  448. {
  449. if ( m_nDragging != DRAGTYPE_NONE )
  450. {
  451. if ( m_nDragging == DRAGTYPE_SELECTION )
  452. {
  453. DrawFocusRect();
  454. }
  455. else if ( m_nDragging == DRAGTYPE_GROW )
  456. {
  457. DrawGrowRect();
  458. }
  459. MouseDrag( (short)event->x, (short)event->y, event->modifiers );
  460. if ( m_nDragging == DRAGTYPE_SELECTION )
  461. {
  462. DrawFocusRect();
  463. }
  464. else if ( m_nDragging == DRAGTYPE_GROW )
  465. {
  466. DrawGrowRect();
  467. }
  468. if ( m_nDragging == DRAGTYPE_MOVEPOINTS_TIME ||
  469. m_nDragging == DRAGTYPE_MOVEPOINTS_VALUE )
  470. {
  471. DrawSelf();
  472. }
  473. }
  474. else
  475. {
  476. // See if anything is selected
  477. CountSelected();
  478. if ( m_nNumSelected <= 0 && g_pExpressionTool->IsFocusItem( this ) )
  479. {
  480. // Nothing selected
  481. // Draw auto highlight
  482. DrawAutoHighlight( event );
  483. }
  484. }
  485. iret = 1;
  486. }
  487. break;
  488. case mxEvent::MouseUp:
  489. {
  490. bool overgrow = IsMouseOverGrowHandle( (short)event->x, (short)event->y );
  491. if ( m_nDragging != DRAGTYPE_NONE )
  492. {
  493. if ( m_nDragging == DRAGTYPE_SELECTION )
  494. {
  495. DrawFocusRect();
  496. }
  497. else if ( m_nDragging == DRAGTYPE_GROW )
  498. {
  499. DrawGrowRect();
  500. }
  501. MouseDrag( (short)event->x, (short)event->y, event->modifiers, true );
  502. if ( m_nDragging == DRAGTYPE_GROW )
  503. {
  504. // Finish grow by resizing control
  505. int desiredheight = m_nCurrentHeight + event->y - m_nStartY;
  506. if ( desiredheight >= 10 )
  507. {
  508. m_nCurrentHeight = desiredheight;
  509. g_pExpressionTool->LayoutItems( true );
  510. }
  511. }
  512. else if ( m_nDragging != DRAGTYPE_MOVEPOINTS_VALUE &&
  513. m_nDragging != DRAGTYPE_MOVEPOINTS_TIME )
  514. {
  515. SelectPoints();
  516. }
  517. else
  518. {
  519. PostDataChanged( "Move sample point(s)" );
  520. }
  521. m_nDragging = DRAGTYPE_NONE;
  522. DrawSelf();
  523. }
  524. bool rightbutton = ( event->buttons & mxEvent::MouseRightButton ) ? true : false;
  525. bool shift = ( event->modifiers & mxEvent::KeyShift ) ? true : false;
  526. bool ctrl = ( event->modifiers & mxEvent::KeyCtrl ) ? true : false;
  527. if ( !rightbutton && !shift && !ctrl )
  528. {
  529. if ( realtime - m_flLastClickTime < DOUBLE_CLICK_TIME )
  530. {
  531. if ( overgrow || IsCollapsed() )
  532. {
  533. OnDoubleClicked();
  534. }
  535. }
  536. m_flLastClickTime = realtime;
  537. }
  538. iret = 1;
  539. }
  540. break;
  541. }
  542. return iret;
  543. }
  544. void TimelineItem::MouseDrag( int x, int y, int modifiers, bool snap /*=false*/ )
  545. {
  546. if ( m_nDragging == DRAGTYPE_NONE )
  547. return;
  548. int width = m_rcBounds.right - m_rcBounds.left;
  549. int height = m_rcBounds.bottom - m_rcBounds.top;
  550. if ( m_nDragging == DRAGTYPE_MOVEPOINTS_TIME ||
  551. m_nDragging == DRAGTYPE_MOVEPOINTS_VALUE )
  552. {
  553. int dx = x - m_nLastX;
  554. int dy = y - m_nLastY;
  555. if ( !( modifiers & mxEvent::KeyCtrl ) )
  556. {
  557. // Zero out motion on other axis
  558. if ( m_nDragging == DRAGTYPE_MOVEPOINTS_VALUE )
  559. {
  560. dx = 0;
  561. x = m_nLastX;
  562. }
  563. else
  564. {
  565. dy = 0;
  566. y = m_nLastY;
  567. }
  568. }
  569. float dfdx = (float)dx / g_pExpressionTool->GetPixelsPerSecond();
  570. float dfdy = (float)dy / (float)height;
  571. g_pExpressionTool->MoveSelectedSamples( dfdx, dfdy, snap );
  572. // Update the scrubber
  573. if ( (float)width > 0 )
  574. {
  575. float t = GetTimeForMouse( x + m_rcBounds.left );
  576. g_pExpressionTool->ForceScrubPosition( t );
  577. g_pMatSysWindow->Frame();
  578. }
  579. }
  580. m_nLastX = x;
  581. m_nLastY = y;
  582. }
  583. //-----------------------------------------------------------------------------
  584. // Purpose:
  585. //-----------------------------------------------------------------------------
  586. void TimelineItem::DrawFocusRect( void )
  587. {
  588. RECT rcFocus;
  589. rcFocus.left = m_nStartX < m_nLastX ? m_nStartX : m_nLastX;
  590. rcFocus.right = m_nStartX < m_nLastX ? m_nLastX : m_nStartX;
  591. rcFocus.top = m_nStartY < m_nLastY ? m_nStartY : m_nLastY;
  592. rcFocus.bottom = m_nStartY < m_nLastY ? m_nLastY : m_nStartY;
  593. POINT offset;
  594. offset.x = m_rcBounds.left;
  595. offset.y = m_rcBounds.top;
  596. ClientToScreen( (HWND)m_pWorkspace->getHandle(), &offset );
  597. OffsetRect( &rcFocus, offset.x, offset.y );
  598. HDC dc = GetDC( NULL );
  599. ::DrawFocusRect( dc, &rcFocus );
  600. ReleaseDC( NULL, dc );
  601. }
  602. void TimelineItem::DrawSelf( void )
  603. {
  604. CChoreoWidgetDrawHelper drawHelper( m_pWorkspace, m_rcBounds );
  605. Draw( drawHelper );
  606. }
  607. void TimelineItem::Draw( CChoreoWidgetDrawHelper& drawHelper )
  608. {
  609. CFlexAnimationTrack *track = GetSafeTrack();
  610. if ( !track )
  611. return;
  612. CChoreoEvent *e = track->GetEvent();
  613. if ( !e )
  614. return;
  615. Assert( e->HasEndTime() );
  616. bool active = track && ( IsValid() || IsActive() );
  617. float starttime;
  618. float endtime;
  619. g_pExpressionTool->GetStartAndEndTime( starttime, endtime );
  620. CountSelected();
  621. int scount = GetNumSelected();
  622. COLORREF bgColor = RGB( 230, 230, 200 );
  623. if ( IsCollapsed() && active )
  624. {
  625. bgColor = RGB( 200, 230, 200 );
  626. }
  627. RECT rcClient = m_rcBounds;
  628. drawHelper.DrawFilledRect( bgColor, rcClient );
  629. COLORREF gray = RGB( 200, 200, 200 );
  630. DrawEventEnd( drawHelper );
  631. DrawRelativeTags( drawHelper );
  632. if ( !IsCollapsed() && track )
  633. {
  634. if ( m_nEditType == 1 )
  635. {
  636. float zero = track->GetZeroValue( m_nEditType, true );
  637. drawHelper.DrawColoredLine( RGB( 180, 200, 220 ), PS_SOLID, 1,
  638. rcClient.left, ( rcClient.top * zero + rcClient.bottom * (1 - zero)) ,
  639. rcClient.right, ( rcClient.top * zero + rcClient.bottom * (1 - zero)) );
  640. }
  641. drawHelper.DrawOutlinedRect( RGB( 100, 150, 200 ), PS_SOLID, 1, rcClient );
  642. // Draw grow handle into background...
  643. if ( CanHaveGrowHandle() )
  644. {
  645. RECT handleRect;
  646. GetGrowHandleRect( handleRect );
  647. DrawGrowHandle( drawHelper, handleRect );
  648. }
  649. // Draw left/right underneath amount so go backbard
  650. for ( int type = ( track->IsComboType() ? 1 : 0 ); type >= 0; type-- )
  651. {
  652. COLORREF lineColor = ( type == m_nEditType ) ? RGB( 0, 0, 255 ) : gray;
  653. COLORREF shadowColor = ( type == m_nEditType ) ? RGB( 150, 150, 250 ) : gray;
  654. COLORREF dotColor = ( type == m_nEditType ) ? RGB( 0, 0, 255 ) : gray;
  655. COLORREF dotColorSelected = ( type == m_nEditType ) ? RGB( 240, 80, 20 ) : gray;
  656. int height = rcClient.bottom - rcClient.top;
  657. int bottom = rcClient.bottom;
  658. // Fixme, could look at 1st derivative and do more sampling at high rate of change?
  659. // or near actual sample points!
  660. float linelength = g_pExpressionTool->IsFocusItem( this ) ? 2.0f : 8.0f;
  661. float timestepperpixel = linelength / g_pExpressionTool->GetPixelsPerSecond();
  662. float stoptime = min( endtime, e->GetDuration() );
  663. float prev_t = starttime;
  664. float prev_value = track->GetFracIntensity( prev_t, type );
  665. CUtlVector< POINT > segments;
  666. /*
  667. if (type == m_nEditType)
  668. {
  669. // draw hermite version of time step
  670. float i0, i1, i2;
  671. float time10hz = starttime;
  672. i0 = track->GetFracIntensity( time10hz, type );
  673. i1 = i0;
  674. time10hz = starttime + 0.1;
  675. i2 = track->GetFracIntensity( time10hz, type );;
  676. for ( float t = starttime; t <= stoptime; t += timestepperpixel )
  677. {
  678. while (t >= time10hz)
  679. {
  680. time10hz += 0.1;
  681. i0 = i1;
  682. i1 = i2;
  683. i2 = track->GetFracIntensity( time10hz, type );;
  684. }
  685. float value = Hermite_Spline( i0, i1, i2, (t - time10hz + 0.1) / 0.1 );
  686. int prevx, x;
  687. bool clipped1, clipped2;
  688. x = GetMouseForTime( t, &clipped1 );
  689. prevx = GetMouseForTime( prev_t, &clipped2 );
  690. //if ( !clipped1 && !clipped2 )
  691. {
  692. // Draw segment
  693. //drawHelper.DrawColoredLine( lineColor, PS_SOLID, 1,
  694. // prevx, bottom - prev_value * height,
  695. // x, bottom - value * height );
  696. POINT pt;
  697. if ( segments.Count() == 0 )
  698. {
  699. pt.x = prevx;
  700. pt.y = bottom - prev_value * height;
  701. segments.AddToTail( pt );
  702. }
  703. pt.x = x;
  704. pt.y = bottom - value * height;
  705. segments.AddToTail( pt );
  706. }
  707. prev_t = t;
  708. prev_value = value;
  709. }
  710. if ( segments.Count() >= 2 )
  711. {
  712. drawHelper.DrawColoredPolyLine( shadowColor, PS_SOLID, 1, segments );
  713. }
  714. segments.RemoveAll();
  715. }
  716. */
  717. for ( float t = starttime; t <= stoptime; t += timestepperpixel )
  718. {
  719. float value = track->GetFracIntensity( t, type );
  720. int prevx, x;
  721. bool clipped1, clipped2;
  722. x = GetMouseForTime( t, &clipped1 );
  723. prevx = GetMouseForTime( prev_t, &clipped2 );
  724. //if ( !clipped1 && !clipped2 )
  725. {
  726. // Draw segment
  727. //drawHelper.DrawColoredLine( lineColor, PS_SOLID, 1,
  728. // prevx, bottom - prev_value * height,
  729. // x, bottom - value * height );
  730. POINT pt;
  731. if ( segments.Count() == 0 )
  732. {
  733. pt.x = prevx;
  734. pt.y = bottom - prev_value * height;
  735. segments.AddToTail( pt );
  736. }
  737. pt.x = x;
  738. pt.y = bottom - value * height;
  739. segments.AddToTail( pt );
  740. }
  741. prev_t = t;
  742. prev_value = value;
  743. }
  744. if ( segments.Count() >= 2 )
  745. {
  746. drawHelper.DrawColoredPolyLine( lineColor, PS_SOLID, 1, segments );
  747. }
  748. for ( int sample = 0; sample < track->GetNumSamples( type ); sample++ )
  749. {
  750. bool dummy;
  751. CExpressionSample *start = track->GetBoundedSample( sample, dummy, type );
  752. /*
  753. int pixel = (int)( ( start->time / event_time ) * width + 0.5f);
  754. int x = m_rcBounds.left + pixel;
  755. float roundedfrac = (float)pixel / (float)width;
  756. */
  757. float value = start->value; // track->GetFracIntensity( start->time, type );
  758. bool clipped = false;
  759. int x = GetMouseForTime( start->time, &clipped );
  760. if ( clipped )
  761. continue;
  762. int y = bottom - value * height;
  763. int dotsize = 6;
  764. int dotSizeSelected = 6;
  765. COLORREF clr = dotColor;
  766. COLORREF clrSelected = dotColorSelected;
  767. drawHelper.DrawCircle(
  768. start->selected ? clrSelected : clr,
  769. x, y,
  770. start->selected ? dotSizeSelected : dotsize,
  771. true );
  772. if ( !start->selected )
  773. continue;
  774. if ( start->GetCurveType() == CURVE_DEFAULT )
  775. continue;
  776. // Draw curve type indicator...
  777. char sz[ 128 ];
  778. Q_snprintf( sz, sizeof( sz ), "%s", Interpolator_NameForCurveType( start->GetCurveType(), true ) );
  779. RECT rc;
  780. int fontSize = 9;
  781. rc.top = clamp( y + 5, rcClient.top + 2, rcClient.bottom - 2 - fontSize );
  782. rc.bottom = rc.top + fontSize + 1;
  783. rc.left = x - 75;
  784. rc.right = x + 175;
  785. drawHelper.DrawColoredText( "Arial", fontSize, 500, shadowColor, rc, sz );
  786. }
  787. }
  788. }
  789. if ( track && track->IsComboType() && !IsCollapsed() )
  790. {
  791. RECT title = rcClient;
  792. title.left += 10;
  793. title.top += 14;
  794. title.bottom = title.top + 9;
  795. char sz[ 128 ];
  796. if ( m_nEditType == 1 )
  797. {
  798. sprintf( sz, "left" );
  799. drawHelper.DrawColoredText( "Arial", 9, 500, RGB( 0, 0, 255 ), title, sz );
  800. sprintf( sz, "right" );
  801. title.top = rcClient.bottom - 22;
  802. title.bottom = rcClient.bottom;
  803. drawHelper.DrawColoredText( "Arial", 9, 500, RGB( 0, 0, 255 ), title, sz );
  804. }
  805. int mid = ( rcClient.top + rcClient.bottom ) / 2;
  806. title.top = mid - 10;
  807. title.bottom = mid;
  808. sprintf( sz, "editmode: <%s>", m_nEditType == 0 ? "amount" : "left/right" );
  809. drawHelper.DrawColoredText( "Arial", 9, 500, RGB( 0, 0, 255 ), title, sz );
  810. }
  811. if ( track )
  812. {
  813. RECT title = rcClient;
  814. title.left += 2;
  815. title.top += 2;
  816. title.bottom = title.top + 9;
  817. char const *name = track->GetFlexControllerName();
  818. char sz[ 128 ];
  819. if ( scount > 0 )
  820. {
  821. sprintf( sz, "{%i} - ", scount );
  822. int len = drawHelper.CalcTextWidth( "Arial", 9, 500, sz );
  823. drawHelper.DrawColoredText( "Arial", 9, 500,
  824. RGB( 120, 120, 0 ), title, sz );
  825. title.left += len + 2;
  826. }
  827. sprintf( sz, "%s -", name );
  828. int len = drawHelper.CalcTextWidth( "Arial", 9, 500, sz );
  829. drawHelper.DrawColoredText( "Arial", 9, 500,
  830. active ? RGB( 0, 150, 100 ) : RGB( 100, 100, 100 ),
  831. title, sz );
  832. sprintf( sz, "%s", IsActive() ? "enabled" : "disabled" );
  833. title.left += len + 2;
  834. len = drawHelper.CalcTextWidth( "Arial", 9, 500, sz );
  835. drawHelper.DrawColoredText( "Arial", 9, 500,
  836. active ? RGB( 0, 150, 100 ) : RGB( 100, 100, 100 ),
  837. title, sz );
  838. if ( active )
  839. {
  840. title.left += len + 2;
  841. sprintf( sz, " <%i>", track->GetNumSamples( 0 ) );
  842. len = drawHelper.CalcTextWidth( "Arial", 9, 500, sz );
  843. drawHelper.DrawColoredText( "Arial", 9, 500, RGB( 220, 0, 00 ), title, sz );
  844. }
  845. }
  846. }
  847. void TimelineItem::DrawAutoHighlight( mxEvent *event )
  848. {
  849. if ( IsCollapsed() )
  850. return;
  851. CFlexAnimationTrack *track = GetSafeTrack();
  852. if ( !track )
  853. return;
  854. CExpressionSample *hover = GetSampleUnderMouse( event->x, event->y, 0.0f );
  855. CChoreoWidgetDrawHelper drawHelper( m_pWorkspace, m_rcBounds, true );
  856. RECT rcClient = m_rcBounds;
  857. // Draw left/right underneath amount so go backbard
  858. int type = m_nEditType;
  859. COLORREF dotColor = RGB( 0, 0, 255 );
  860. COLORREF dotColorSelected = RGB( 240, 80, 20 );
  861. COLORREF clrHighlighted = RGB( 0, 200, 0 );
  862. int height = rcClient.bottom - rcClient.top;
  863. int bottom = rcClient.bottom;
  864. int dotsize = 6;
  865. int dotSizeSelected = 6;
  866. int dotSizeHighlighted = 6;
  867. COLORREF clr = dotColor;
  868. COLORREF clrSelected = dotColorSelected;
  869. COLORREF bgColor = RGB( 230, 230, 200 );
  870. // Fixme, could look at 1st derivative and do more sampling at high rate of change?
  871. // or near actual sample points!
  872. for ( int sample = 0; sample < track->GetNumSamples( type ); sample++ )
  873. {
  874. bool dummy;
  875. CExpressionSample *start = track->GetBoundedSample( sample, dummy, type );
  876. float value = start->value;
  877. bool clipped = false;
  878. int x = GetMouseForTime( start->time, &clipped );
  879. if ( clipped )
  880. continue;
  881. int y = bottom - value * height;
  882. if ( hover == start )
  883. {
  884. drawHelper.DrawCircle(
  885. bgColor,
  886. x, y,
  887. dotSizeHighlighted,
  888. true );
  889. drawHelper.DrawCircle(
  890. clrHighlighted,
  891. x, y,
  892. dotSizeHighlighted,
  893. false );
  894. }
  895. else
  896. {
  897. drawHelper.DrawCircle(
  898. start->selected ? clrSelected : clr,
  899. x, y,
  900. start->selected ? dotSizeSelected : dotsize,
  901. true );
  902. }
  903. }
  904. }
  905. //-----------------------------------------------------------------------------
  906. // Purpose:
  907. // Input : drawHelper -
  908. //-----------------------------------------------------------------------------
  909. void TimelineItem::DrawRelativeTags( CChoreoWidgetDrawHelper& drawHelper )
  910. {
  911. CFlexAnimationTrack *track = GetSafeTrack();
  912. if ( !track )
  913. return;
  914. CChoreoEvent *event = track->GetEvent();
  915. if ( !event )
  916. return;
  917. float duration = event->GetDuration();
  918. if ( duration <= 0.0f )
  919. return;
  920. CChoreoScene *scene = g_pChoreoView->GetScene();
  921. if ( !scene )
  922. return;
  923. RECT rcClient = m_rcBounds;;
  924. //drawHelper.GetClientRect( rcClient );
  925. // Iterate relative tags
  926. for ( int i = 0; i < scene->GetNumActors(); i++ )
  927. {
  928. CChoreoActor *a = scene->GetActor( i );
  929. if ( !a )
  930. continue;
  931. for ( int j = 0; j < a->GetNumChannels(); j++ )
  932. {
  933. CChoreoChannel *c = a->GetChannel( j );
  934. if ( !c )
  935. continue;
  936. for ( int k = 0 ; k < c->GetNumEvents(); k++ )
  937. {
  938. CChoreoEvent *e = c->GetEvent( k );
  939. if ( !e )
  940. continue;
  941. // add each tag to combo box
  942. for ( int t = 0; t < e->GetNumRelativeTags(); t++ )
  943. {
  944. CEventRelativeTag *tag = e->GetRelativeTag( t );
  945. if ( !tag )
  946. continue;
  947. //SendMessage( control, CB_ADDSTRING, 0, (LPARAM)va( "\"%s\" \"%s\"", tag->GetName(), e->GetParameters() ) );
  948. bool clipped = false;
  949. int tagx = GetMouseForTime( tag->GetStartTime() - event->GetStartTime(), &clipped );
  950. if ( clipped )
  951. continue;
  952. drawHelper.DrawColoredLine( RGB( 180, 180, 220 ), PS_SOLID, 1, tagx, rcClient.top, tagx, rcClient.bottom );
  953. }
  954. }
  955. }
  956. }
  957. for ( int t = 0; t < event->GetNumTimingTags(); t++ )
  958. {
  959. CFlexTimingTag *tag = event->GetTimingTag( t );
  960. if ( !tag )
  961. continue;
  962. bool clipped = false;
  963. int tagx = GetMouseForTime( tag->GetStartTime() - event->GetStartTime(), &clipped );
  964. if ( clipped )
  965. continue;
  966. // Draw relative tag marker
  967. drawHelper.DrawColoredLine( RGB( 220, 180, 180 ), PS_SOLID, 1, tagx, rcClient.top, tagx, rcClient.bottom );
  968. }
  969. }
  970. //-----------------------------------------------------------------------------
  971. // Purpose:
  972. // Input : *exp -
  973. // flexnum -
  974. //-----------------------------------------------------------------------------
  975. void TimelineItem::SetExpressionInfo( CFlexAnimationTrack *track, int flexnum )
  976. {
  977. m_szTrackName[ 0 ] = 0;
  978. if ( track )
  979. {
  980. V_strcpy_safe( m_szTrackName, track->GetFlexControllerName() );
  981. SetActive( track->IsTrackActive() );
  982. }
  983. m_nFlexNum = flexnum;
  984. }
  985. //-----------------------------------------------------------------------------
  986. // Purpose:
  987. //-----------------------------------------------------------------------------
  988. void TimelineItem::Copy( void )
  989. {
  990. if ( !g_pExpressionTool )
  991. return;
  992. CFlexAnimationTrack *track = GetSafeTrack();
  993. if ( !track )
  994. return;
  995. g_pExpressionTool->Copy( track );
  996. }
  997. //-----------------------------------------------------------------------------
  998. // Purpose:
  999. //-----------------------------------------------------------------------------
  1000. void TimelineItem::Paste( void )
  1001. {
  1002. if ( !g_pExpressionTool )
  1003. return;
  1004. if ( !g_pExpressionTool->HasCopyData() )
  1005. return;
  1006. CFlexAnimationTrack *track = GetSafeTrack();
  1007. if ( !track )
  1008. return;
  1009. g_pExpressionTool->Paste( track );
  1010. }
  1011. //-----------------------------------------------------------------------------
  1012. // Purpose:
  1013. //-----------------------------------------------------------------------------
  1014. void TimelineItem::Clear( bool preserveundo )
  1015. {
  1016. CFlexAnimationTrack *track = GetSafeTrack();
  1017. if ( !track )
  1018. return;
  1019. if ( preserveundo )
  1020. {
  1021. PreDataChanged( "Clear" );
  1022. }
  1023. track->Clear();
  1024. if ( preserveundo )
  1025. {
  1026. PostDataChanged( "Clear" );
  1027. }
  1028. }
  1029. //-----------------------------------------------------------------------------
  1030. // Purpose:
  1031. // Input : state -
  1032. //-----------------------------------------------------------------------------
  1033. void TimelineItem::SetCollapsed( bool state )
  1034. {
  1035. m_bCollapsed = state;
  1036. }
  1037. //-----------------------------------------------------------------------------
  1038. // Purpose:
  1039. // Output : Returns true on success, false on failure.
  1040. //-----------------------------------------------------------------------------
  1041. bool TimelineItem::IsCollapsed( void ) const
  1042. {
  1043. return m_bCollapsed;
  1044. }
  1045. //-----------------------------------------------------------------------------
  1046. // Purpose:
  1047. // Output : int
  1048. //-----------------------------------------------------------------------------
  1049. int TimelineItem::GetHeight( void )
  1050. {
  1051. return ( IsCollapsed() ? 12 : m_nCurrentHeight );
  1052. }
  1053. //-----------------------------------------------------------------------------
  1054. // Purpose:
  1055. // Input : state -
  1056. //-----------------------------------------------------------------------------
  1057. void TimelineItem::SetActive( bool state )
  1058. {
  1059. CFlexAnimationTrack *track = GetSafeTrack();
  1060. if ( !track )
  1061. return;
  1062. track->SetTrackActive( state );
  1063. }
  1064. //-----------------------------------------------------------------------------
  1065. // Purpose:
  1066. // Output : Returns true on success, false on failure.
  1067. //-----------------------------------------------------------------------------
  1068. bool TimelineItem::IsActive( void )
  1069. {
  1070. CFlexAnimationTrack *track = GetSafeTrack();
  1071. if ( !track )
  1072. return false;
  1073. return track->IsTrackActive();
  1074. }
  1075. //-----------------------------------------------------------------------------
  1076. // Purpose:
  1077. // Output : Returns true on success, false on failure.
  1078. //-----------------------------------------------------------------------------
  1079. bool TimelineItem::IsValid( void )
  1080. {
  1081. CFlexAnimationTrack *track = GetSafeTrack();
  1082. if ( !track )
  1083. return false;
  1084. if ( track->GetNumSamples( 0 ) > 0 )
  1085. return true;
  1086. return false;
  1087. }
  1088. //-----------------------------------------------------------------------------
  1089. // Purpose:
  1090. // Output : CFlexAnimationTrack
  1091. //-----------------------------------------------------------------------------
  1092. CFlexAnimationTrack *TimelineItem::GetSafeTrack( void )
  1093. {
  1094. if ( !g_pExpressionTool )
  1095. return NULL;
  1096. CChoreoEvent *ev = g_pExpressionTool->GetSafeEvent();
  1097. if ( !ev )
  1098. return NULL;
  1099. // Find track by name
  1100. for ( int i = 0; i < ev->GetNumFlexAnimationTracks() ; i++ )
  1101. {
  1102. CFlexAnimationTrack *track = ev->GetFlexAnimationTrack( i );
  1103. if ( track && !stricmp( track->GetFlexControllerName(), m_szTrackName ) )
  1104. {
  1105. return track;
  1106. }
  1107. }
  1108. return NULL;
  1109. }
  1110. //-----------------------------------------------------------------------------
  1111. // Purpose:
  1112. // Input : type -
  1113. //-----------------------------------------------------------------------------
  1114. void TimelineItem::SetEditType( int type )
  1115. {
  1116. Assert( type == 0 || type == 1 );
  1117. CFlexAnimationTrack *track = GetSafeTrack();
  1118. if ( !track || !track->IsComboType() )
  1119. {
  1120. type = 0;
  1121. }
  1122. m_nEditType = type;
  1123. }
  1124. //-----------------------------------------------------------------------------
  1125. // Purpose:
  1126. // Output : int
  1127. //-----------------------------------------------------------------------------
  1128. int TimelineItem::GetEditType( void )
  1129. {
  1130. return m_nEditType;
  1131. }
  1132. //-----------------------------------------------------------------------------
  1133. // Purpose:
  1134. //-----------------------------------------------------------------------------
  1135. void TimelineItem::SelectPoints( void )
  1136. {
  1137. RECT rcSelection;
  1138. rcSelection.left = m_nStartX < m_nLastX ? m_nStartX : m_nLastX;
  1139. rcSelection.right = m_nStartX < m_nLastX ? m_nLastX : m_nStartX;
  1140. rcSelection.top = m_nStartY < m_nLastY ? m_nStartY : m_nLastY;
  1141. rcSelection.bottom = m_nStartY < m_nLastY ? m_nLastY : m_nStartY;
  1142. int selW = rcSelection.right - rcSelection.left;
  1143. int selH = rcSelection.bottom - rcSelection.top;
  1144. float tolerance = FP_TL_SELECTION_RECTANGLE_TOLERANCE;
  1145. // If they are just clicking and releasing in one spot, capture any items w/in a larger tolerance
  1146. if ( selW <= 2 && selH <= 2 )
  1147. {
  1148. tolerance = FP_TL_SELECTION_TOLERANCE;
  1149. CExpressionSample *sample = GetSampleUnderMouse( rcSelection.left + selW * 0.5f, rcSelection.top + selH * 0.5f );
  1150. if ( sample )
  1151. {
  1152. sample->selected = true;
  1153. return;
  1154. }
  1155. }
  1156. else
  1157. {
  1158. InflateRect( &rcSelection, 3, 3 );
  1159. }
  1160. int width = m_rcBounds.right - m_rcBounds.left;
  1161. int height = m_rcBounds.bottom - m_rcBounds.top;
  1162. CFlexAnimationTrack *track = GetSafeTrack();
  1163. if ( !track || !width || !height )
  1164. return;
  1165. CChoreoEvent *e = track->GetEvent();
  1166. Assert( e );
  1167. if ( !e )
  1168. return;
  1169. float duration = e->GetDuration();
  1170. float fleft = (float)GetTimeForMouse( rcSelection.left + m_rcBounds.left );
  1171. float fright = (float)GetTimeForMouse( rcSelection.right + m_rcBounds.left );
  1172. //fleft *= duration;
  1173. //fright *= duration;
  1174. float ftop = (float)rcSelection.top / (float)height;
  1175. float fbottom = (float)rcSelection.bottom / (float)height;
  1176. fleft = clamp( fleft, 0.0f, duration );
  1177. fright = clamp( fright, 0.0f, duration );
  1178. ftop = clamp( ftop, 0.0f, 1.0f );
  1179. fbottom = clamp( fbottom, 0.0f, 1.0f );
  1180. float timestepperpixel = 1.0f / g_pExpressionTool->GetPixelsPerSecond();
  1181. float yfracstepperpixel = 1.0f / (float)height;
  1182. float epsx = tolerance*timestepperpixel;
  1183. float epsy = tolerance*yfracstepperpixel;
  1184. for ( int i = 0; i < track->GetNumSamples( m_nEditType ); i++ )
  1185. {
  1186. CExpressionSample *sample = track->GetSample( i, m_nEditType );
  1187. if ( sample->time + epsx < fleft )
  1188. continue;
  1189. if ( sample->time - epsx > fright )
  1190. continue;
  1191. if ( (1.0f - sample->value ) + epsy < ftop )
  1192. continue;
  1193. if ( (1.0f - sample->value ) - epsy > fbottom )
  1194. continue;
  1195. sample->selected = true;
  1196. }
  1197. }
  1198. //-----------------------------------------------------------------------------
  1199. // Purpose:
  1200. // Input : *undodescription -
  1201. //-----------------------------------------------------------------------------
  1202. void TimelineItem::PreDataChanged( char const *undodescription )
  1203. {
  1204. if ( m_nUndoSetup == 0 )
  1205. {
  1206. g_pChoreoView->SetDirty( true );
  1207. g_pChoreoView->PushUndo( undodescription );
  1208. }
  1209. ++m_nUndoSetup;
  1210. }
  1211. //-----------------------------------------------------------------------------
  1212. // Purpose:
  1213. // Input : *redodescription -
  1214. //-----------------------------------------------------------------------------
  1215. void TimelineItem::PostDataChanged( char const *redodescription )
  1216. {
  1217. --m_nUndoSetup;
  1218. if ( m_nUndoSetup == 0 )
  1219. {
  1220. g_pChoreoView->PushRedo( redodescription );
  1221. g_pExpressionTool->InvalidateLayout();
  1222. }
  1223. }
  1224. void TimelineItem::SetBounds( const RECT& rect )
  1225. {
  1226. m_rcBounds = rect;
  1227. }
  1228. void TimelineItem::GetBounds( RECT& rect )
  1229. {
  1230. rect = m_rcBounds;
  1231. }
  1232. void TimelineItem::SetVisible( bool vis )
  1233. {
  1234. m_bVisible = vis;
  1235. }
  1236. bool TimelineItem::GetVisible( void ) const
  1237. {
  1238. return m_bVisible;
  1239. }
  1240. int TimelineItem::GetNumSelected( void )
  1241. {
  1242. return m_nNumSelected;
  1243. }
  1244. //-----------------------------------------------------------------------------
  1245. // Purpose:
  1246. //-----------------------------------------------------------------------------
  1247. void TimelineItem::SnapAll()
  1248. {
  1249. CFlexAnimationTrack *track = GetSafeTrack();
  1250. if ( !track )
  1251. return;
  1252. for ( int t = 0; t < 2; t++ )
  1253. {
  1254. for ( int i = 0; i < track->GetNumSamples( t ); i++ )
  1255. {
  1256. CExpressionSample *sample = track->GetSample( i, t );
  1257. sample->time = FacePoser_SnapTime( sample->time );
  1258. }
  1259. }
  1260. }
  1261. //-----------------------------------------------------------------------------
  1262. // Purpose:
  1263. //-----------------------------------------------------------------------------
  1264. void TimelineItem::SnapSelected()
  1265. {
  1266. CFlexAnimationTrack *track = GetSafeTrack();
  1267. if ( !track )
  1268. return;
  1269. for ( int t = 0; t < 2; t++ )
  1270. {
  1271. for ( int i = 0; i < track->GetNumSamples( t ); i++ )
  1272. {
  1273. CExpressionSample *sample = track->GetSample( i, t );
  1274. if ( !sample->selected )
  1275. continue;
  1276. sample->time = FacePoser_SnapTime( sample->time );
  1277. }
  1278. }
  1279. }
  1280. //-----------------------------------------------------------------------------
  1281. // Purpose:
  1282. // Input : start -
  1283. // end -
  1284. //-----------------------------------------------------------------------------
  1285. void TimelineItem::DeletePoints( float start, float end )
  1286. {
  1287. CFlexAnimationTrack *track = GetSafeTrack();
  1288. if ( !track )
  1289. return;
  1290. for ( int t = 0; t < 2; t++ )
  1291. {
  1292. int num = track->GetNumSamples( t );
  1293. for ( int i = num - 1; i >= 0 ; i-- )
  1294. {
  1295. CExpressionSample *sample = track->GetSample( i, t );
  1296. if ( sample->time < start || sample->time > end )
  1297. continue;
  1298. track->RemoveSample( i, t );
  1299. }
  1300. }
  1301. }
  1302. //-----------------------------------------------------------------------------
  1303. // Purpose:
  1304. //-----------------------------------------------------------------------------
  1305. void TimelineItem::OnDoubleClicked()
  1306. {
  1307. // Disabled for now by request of BillF
  1308. CFlexAnimationTrack *track = GetSafeTrack();
  1309. if ( !track )
  1310. return;
  1311. SetCollapsed( !IsCollapsed() );
  1312. g_pExpressionTool->LayoutItems( true );
  1313. }
  1314. void TimelineItem::DrawEventEnd( CChoreoWidgetDrawHelper& drawHelper )
  1315. {
  1316. CFlexAnimationTrack *track = GetSafeTrack();
  1317. if ( !track )
  1318. return;
  1319. CChoreoEvent *e = track->GetEvent();
  1320. if ( !e )
  1321. return;
  1322. float duration = e->GetDuration();
  1323. if ( !duration )
  1324. return;
  1325. int leftx = GetMouseForTime( duration );
  1326. if ( leftx > m_rcBounds.right )
  1327. return;
  1328. drawHelper.DrawColoredLine(
  1329. COLOR_CHOREO_ENDTIME, PS_SOLID, 1,
  1330. leftx, m_rcBounds.top, leftx, m_rcBounds.bottom );
  1331. }
  1332. //-----------------------------------------------------------------------------
  1333. // Purpose:
  1334. // Input : helper -
  1335. // handleRect -
  1336. //-----------------------------------------------------------------------------
  1337. void TimelineItem::DrawGrowHandle( CChoreoWidgetDrawHelper& helper, RECT& handleRect )
  1338. {
  1339. Assert(CanHaveGrowHandle());
  1340. RECT useRect = handleRect;
  1341. helper.OffsetSubRect( useRect );
  1342. POINT region[4];
  1343. int cPoints = 4;
  1344. region[ 0 ].x = useRect.left + GROW_HANDLE_INSETPIXELS;
  1345. region[ 0 ].y = useRect.top;
  1346. region[ 1 ].x = useRect.right - GROW_HANDLE_INSETPIXELS;
  1347. region[ 1 ].y = useRect.top;
  1348. region[ 2 ].x = useRect.right;
  1349. region[ 2 ].y = useRect.bottom;
  1350. region[ 3 ].x = useRect.left;
  1351. region[ 3 ].y = useRect.bottom;
  1352. HDC dc = helper.GrabDC();
  1353. HRGN rgn = CreatePolygonRgn( region, cPoints, ALTERNATE );
  1354. int oldPF = SetPolyFillMode( dc, ALTERNATE );
  1355. HBRUSH brBg = CreateSolidBrush( RGB( 150, 150, 150 ) );
  1356. HBRUSH brBorder = CreateSolidBrush( RGB( 200, 200, 200 ) );
  1357. FillRgn( dc, rgn, brBg );
  1358. FrameRgn( dc, rgn, brBorder, 1, 1 );
  1359. SetPolyFillMode( dc, oldPF );
  1360. DeleteObject( rgn );
  1361. DeleteObject( brBg );
  1362. DeleteObject( brBorder );
  1363. // draw a line in the middle
  1364. int midy = ( handleRect.bottom + handleRect.top ) * 0.5f;
  1365. int lineinset = GROW_HANDLE_INSETPIXELS *1.5;
  1366. helper.DrawColoredLine( RGB( 63, 63, 63 ), PS_SOLID, 1,
  1367. handleRect.left + lineinset, midy,
  1368. handleRect.right - lineinset, midy );
  1369. }
  1370. //-----------------------------------------------------------------------------
  1371. // Purpose:
  1372. // Input : rc -
  1373. //-----------------------------------------------------------------------------
  1374. void TimelineItem::GetGrowHandleRect( RECT& rc )
  1375. {
  1376. rc = m_rcBounds;
  1377. rc.bottom -= 1;
  1378. rc.top = rc.bottom - GROW_HANDLE_HEIGHT;
  1379. rc.left = ( rc.right + rc.left ) / 2 - GROW_HANDLE_WIDTH / 2;
  1380. rc.right = rc.left + GROW_HANDLE_WIDTH;
  1381. }
  1382. //-----------------------------------------------------------------------------
  1383. // Purpose:
  1384. // Output : Returns true on success, false on failure.
  1385. //-----------------------------------------------------------------------------
  1386. bool TimelineItem::CanHaveGrowHandle()
  1387. {
  1388. if ( IsCollapsed() )
  1389. return false;
  1390. if ( !g_pExpressionTool->IsFocusItem( this ) )
  1391. return false;
  1392. return true;
  1393. }
  1394. //-----------------------------------------------------------------------------
  1395. // Purpose:
  1396. // Input : x -
  1397. // y -
  1398. // Output : Returns true on success, false on failure.
  1399. //-----------------------------------------------------------------------------
  1400. bool TimelineItem::IsMouseOverGrowHandle( int x, int y)
  1401. {
  1402. if ( !CanHaveGrowHandle() )
  1403. return false;
  1404. RECT rcGrowHandle;
  1405. GetGrowHandleRect( rcGrowHandle );
  1406. POINT pt;
  1407. pt.x = x + m_rcBounds.left;
  1408. pt.y = y + m_rcBounds.top;
  1409. return PtInRect( &rcGrowHandle, pt ) ? true : false;
  1410. }
  1411. void TimelineItem::DrawGrowRect()
  1412. {
  1413. RECT rcFocus = m_rcBounds;
  1414. rcFocus.bottom = m_rcBounds.top + m_nLastY;
  1415. OffsetRect( &rcFocus, -m_rcBounds.left, -m_rcBounds.top );
  1416. POINT offset;
  1417. offset.x = m_rcBounds.left;
  1418. offset.y = m_rcBounds.top;
  1419. ClientToScreen( (HWND)m_pWorkspace->getHandle(), &offset );
  1420. OffsetRect( &rcFocus, offset.x, offset.y );
  1421. HDC dc = GetDC( NULL );
  1422. ::DrawFocusRect( dc, &rcFocus );
  1423. ReleaseDC( NULL, dc );
  1424. }
  1425. void TimelineItem::GetWorkList( bool reflect, CUtlVector< TimelineItem * >& list )
  1426. {
  1427. if ( !reflect )
  1428. {
  1429. list.AddToTail( this );
  1430. }
  1431. else
  1432. {
  1433. g_pExpressionTool->GetTimelineItems( list );
  1434. }
  1435. }