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.

4815 lines
106 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. #include <stdio.h>
  9. #include "hlfaceposer.h"
  10. #include "ExpressionTool.h"
  11. #include "mdlviewer.h"
  12. #include "choreowidgetdrawhelper.h"
  13. #include "TimelineItem.h"
  14. #include "expressions.h"
  15. #include "expclass.h"
  16. #include "choreoevent.h"
  17. #include "StudioModel.h"
  18. #include "choreoscene.h"
  19. #include "choreoactor.h"
  20. #include "choreochannel.h"
  21. #include "ChoreoView.h"
  22. #include "InputProperties.h"
  23. #include "ControlPanel.h"
  24. #include "FlexPanel.h"
  25. #include "mxExpressionTray.h"
  26. #include "ExpressionProperties.h"
  27. #include "tier1/strtools.h"
  28. #include "faceposer_models.h"
  29. #include "UtlBuffer.h"
  30. #include "filesystem.h"
  31. #include "iscenetokenprocessor.h"
  32. #include "MatSysWin.h"
  33. #include "choreoviewcolors.h"
  34. #include "scriplib.h"
  35. #include "EdgeProperties.h"
  36. ExpressionTool *g_pExpressionTool = 0;
  37. #define TRAY_HEIGHT 55
  38. #define TRAY_ITEM_INSET 10
  39. #define MAX_TIME_ZOOM 1000
  40. // 10% per step
  41. #define TIME_ZOOM_STEP 2
  42. void SetupFlexControllerTracks( CStudioHdr *hdr, CChoreoEvent *event );
  43. class CExpressionToolWorkspace : public mxWindow
  44. {
  45. public:
  46. CExpressionToolWorkspace( mxWindow *parent );
  47. ~CExpressionToolWorkspace();
  48. virtual int handleEvent( mxEvent *event );
  49. virtual void redraw( void );
  50. virtual bool PaintBackground( void )
  51. {
  52. redraw();
  53. return false;
  54. }
  55. void RepositionVSlider( void );
  56. int ComputeVPixelsNeeded( void );
  57. // Playback tick
  58. void Think( float dt );
  59. void LayoutItems( bool force = false );
  60. void HideTimelines( void );
  61. void CollapseAll( TimelineItem *keepExpanded );
  62. void ExpandAll( void );
  63. void ExpandValid( void );
  64. void DisableAllExcept( void );
  65. void EnableValid( void );
  66. TimelineItem *GetItem( int number );
  67. TimelineItem *GetClickedItem( void );
  68. void ClearClickedItem( void );
  69. void OnSnapAll();
  70. void OnDeleteColumn();
  71. void MoveSelectedSamples( float dfdx, float dfdy, bool snap );
  72. void DeleteSelectedSamples( void );
  73. int CountSelectedSamples( void );
  74. void DeselectAll( void );
  75. void SelectPoints( float start, float end );
  76. void DrawEventEnd( CChoreoWidgetDrawHelper& drawHelper );
  77. void OnSortByUsed( void );
  78. void OnSortByName( void );
  79. private:
  80. int GetItemUnderMouse( int mx, int my );
  81. void MouseToToolMouse( int& mx, int& my, char *reason );
  82. TimelineItem *m_pItems[ GLOBAL_STUDIO_FLEX_CONTROL_COUNT ];
  83. // The scroll bars
  84. mxScrollbar *m_pVertScrollBar;
  85. int m_nLastVPixelsNeeded;
  86. int m_nTopOffset;
  87. int m_nScrollbarHeight;
  88. int m_nItemGap;
  89. int m_nFocusItem;
  90. };
  91. CExpressionToolWorkspace::CExpressionToolWorkspace( mxWindow *parent ) :
  92. mxWindow( parent, 0, 0, 0, 0 )
  93. {
  94. HWND wnd = (HWND)getHandle();
  95. DWORD style = GetWindowLong( wnd, GWL_STYLE );
  96. style |= WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
  97. SetWindowLong( wnd, GWL_STYLE, style );
  98. for ( int i = 0; i < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; i++ )
  99. {
  100. m_pItems[ i ] = new TimelineItem( this );
  101. }
  102. m_nItemGap = 2;
  103. m_nScrollbarHeight = 12;
  104. m_nTopOffset = 0;
  105. m_nLastVPixelsNeeded = -1;
  106. m_pVertScrollBar = new mxScrollbar( this, 0, 0, 12, 100, IDC_EXPRESSIONTOOLVSCROLL, mxScrollbar::Vertical );
  107. m_nFocusItem = -1;
  108. HideTimelines();
  109. LayoutItems();
  110. }
  111. CExpressionToolWorkspace::~CExpressionToolWorkspace()
  112. {
  113. }
  114. void CExpressionToolWorkspace::redraw()
  115. {
  116. CChoreoWidgetDrawHelper drawHelper( this );
  117. DrawEventEnd( drawHelper );
  118. for ( int i = 0; i < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; i++ )
  119. {
  120. TimelineItem *item = GetItem( i );
  121. if ( !item )
  122. continue;
  123. if ( !item->GetVisible() )
  124. continue;
  125. RECT rcBounds;
  126. item->GetBounds( rcBounds );
  127. if ( rcBounds.bottom < 0 )
  128. continue;
  129. if ( rcBounds.top > h2() )
  130. continue;
  131. item->Draw( drawHelper );
  132. }
  133. }
  134. //-----------------------------------------------------------------------------
  135. // Purpose:
  136. // Input : *elem1 -
  137. // *elem2 -
  138. // Output : int
  139. //-----------------------------------------------------------------------------
  140. int SortFuncByUse(const void *elem1, const void *elem2 )
  141. {
  142. TimelineItem *item1 = *( TimelineItem ** )elem1;
  143. TimelineItem *item2 = *( TimelineItem ** )elem2;
  144. if ( item1->IsValid() == item2->IsValid() )
  145. return 0;
  146. if ( !item2->IsValid() && item1->IsValid() )
  147. return -1;
  148. return 1;
  149. }
  150. //-----------------------------------------------------------------------------
  151. // Purpose:
  152. // Input : *elem1 -
  153. // *elem2 -
  154. // Output : int
  155. //-----------------------------------------------------------------------------
  156. int SortFuncByName(const void *elem1, const void *elem2 )
  157. {
  158. TimelineItem *item1 = *( TimelineItem ** )elem1;
  159. TimelineItem *item2 = *( TimelineItem ** )elem2;
  160. CFlexAnimationTrack *track1 = item1->GetSafeTrack();
  161. CFlexAnimationTrack *track2 = item2->GetSafeTrack();
  162. if ( !track1 || !track2 )
  163. {
  164. if ( track1 )
  165. return -1;
  166. if ( track2 )
  167. return 1;
  168. return 0;
  169. }
  170. return stricmp( track1->GetFlexControllerName(), track2->GetFlexControllerName() );
  171. }
  172. void CExpressionToolWorkspace::OnSortByUsed( void )
  173. {
  174. qsort( m_pItems, GLOBAL_STUDIO_FLEX_CONTROL_COUNT, sizeof( TimelineItem * ), SortFuncByUse );
  175. LayoutItems( false );
  176. }
  177. void CExpressionToolWorkspace::OnSortByName( void )
  178. {
  179. qsort( m_pItems, GLOBAL_STUDIO_FLEX_CONTROL_COUNT, sizeof( TimelineItem * ), SortFuncByName );
  180. LayoutItems( false );
  181. }
  182. void CExpressionToolWorkspace::DrawEventEnd( CChoreoWidgetDrawHelper& drawHelper )
  183. {
  184. if ( !g_pExpressionTool )
  185. return;
  186. CChoreoEvent *e = g_pExpressionTool->GetSafeEvent();
  187. if ( !e )
  188. return;
  189. float duration = e->GetDuration();
  190. if ( !duration )
  191. return;
  192. int leftx = g_pExpressionTool->GetPixelForTimeValue( duration ) -5;
  193. if ( leftx >= w2() )
  194. return;
  195. RECT rcClient;
  196. drawHelper.GetClientRect( rcClient );
  197. drawHelper.DrawColoredLine(
  198. COLOR_CHOREO_ENDTIME, PS_SOLID, 1,
  199. leftx, rcClient.top, leftx, rcClient.bottom );
  200. }
  201. int CExpressionToolWorkspace::GetItemUnderMouse( int mx, int my )
  202. {
  203. POINT pt;
  204. pt.x = mx;
  205. pt.y = my;
  206. for ( int i = 0; i < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; i++ )
  207. {
  208. TimelineItem *item = GetItem( i );
  209. if ( !item )
  210. continue;
  211. if ( !item->GetVisible() )
  212. continue;
  213. RECT rc;
  214. item->GetBounds( rc );
  215. if ( PtInRect( &rc, pt ) )
  216. {
  217. return i;
  218. }
  219. }
  220. return -1;
  221. }
  222. void CExpressionToolWorkspace::MouseToToolMouse( int& mx, int& my, char *reason )
  223. {
  224. POINT pt;
  225. pt.x = mx;
  226. pt.y = my;
  227. ClientToScreen( (HWND)getHandle(), &pt );
  228. ScreenToClient( (HWND)getParent()->getHandle(), &pt );
  229. mx = pt.x;
  230. my = pt.y;
  231. }
  232. int CExpressionToolWorkspace::handleEvent( mxEvent *event )
  233. {
  234. MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
  235. int iret = 0;
  236. switch ( event->event )
  237. {
  238. case mxEvent::MouseDown:
  239. {
  240. HWND wnd = (HWND)getParent()->getHandle();
  241. SetFocus( wnd );
  242. SetWindowPos( wnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
  243. {
  244. int mx = (short)event->x;
  245. int my = (short)event->y;
  246. MouseToToolMouse( mx, my, "CExpressionToolWorkspace mousedown" );
  247. g_pExpressionTool->SetClickedPos( mx, my );
  248. g_pExpressionTool->SetMouseOverPos( mx, my );
  249. g_pExpressionTool->DrawMouseOverPos();
  250. }
  251. int oldFocus = m_nFocusItem;
  252. m_nFocusItem = GetItemUnderMouse( (short)event->x, (short)event->y );
  253. if ( oldFocus != -1 &&
  254. oldFocus != m_nFocusItem )
  255. {
  256. TimelineItem *item = GetItem( oldFocus );
  257. if ( item )
  258. {
  259. item->DrawSelf();
  260. }
  261. }
  262. if ( m_nFocusItem != -1 )
  263. {
  264. TimelineItem *item = GetItem( m_nFocusItem );
  265. if ( item )
  266. {
  267. RECT rc;
  268. item->GetBounds( rc );
  269. event->x -= rc.left;
  270. event->y -= rc.top;
  271. iret = item->handleEvent( event );
  272. }
  273. }
  274. iret = 1;
  275. }
  276. break;
  277. case mxEvent::MouseDrag:
  278. case mxEvent::MouseMove:
  279. {
  280. //
  281. bool handled = false;
  282. if ( m_nFocusItem != -1 )
  283. {
  284. TimelineItem *item = GetItem( m_nFocusItem );
  285. if ( item )
  286. {
  287. RECT rc;
  288. item->GetBounds( rc );
  289. event->x -= rc.left;
  290. event->y -= rc.top;
  291. iret = item->handleEvent( event );
  292. if ( event->event == mxEvent::MouseDrag )
  293. {
  294. int mx, my;
  295. item->GetLastMouse( mx, my );
  296. mx += rc.left;
  297. my += rc.top;
  298. MouseToToolMouse( mx, my, "CExpressionToolWorkspace mousedrag" );
  299. g_pExpressionTool->SetMouseOverPos( mx, my );
  300. g_pExpressionTool->DrawMouseOverPos();
  301. handled = true;
  302. }
  303. }
  304. }
  305. if ( !handled )
  306. {
  307. int mx = (short)event->x;
  308. int my = (short)event->y;
  309. mx += TRAY_ITEM_INSET;
  310. MouseToToolMouse( mx, my, "CExpressionToolWorkspace mousemove" );
  311. g_pExpressionTool->SetMouseOverPos( mx, my );
  312. g_pExpressionTool->DrawMouseOverPos();
  313. }
  314. }
  315. break;
  316. case mxEvent::MouseUp:
  317. {
  318. //
  319. {
  320. int mx = (short)event->x;
  321. int my = (short)event->y;
  322. MouseToToolMouse( mx, my, "CExpressionToolWorkspace mouseup" );
  323. g_pExpressionTool->SetMouseOverPos( mx, my );
  324. g_pExpressionTool->DrawMouseOverPos();
  325. }
  326. if ( m_nFocusItem != -1 )
  327. {
  328. TimelineItem *item = GetItem( m_nFocusItem );
  329. if ( item )
  330. {
  331. RECT rc;
  332. item->GetBounds( rc );
  333. event->x -= rc.left;
  334. event->y -= rc.top;
  335. iret = item->handleEvent( event );
  336. }
  337. }
  338. }
  339. break;
  340. case mxEvent::Size:
  341. {
  342. RepositionVSlider();
  343. LayoutItems();
  344. iret = 1;
  345. }
  346. break;
  347. case mxEvent::MouseWheeled:
  348. // Tell parent
  349. {
  350. if ( event->modifiers & mxEvent::KeyShift )
  351. {
  352. CChoreoScene *scene = g_pChoreoView->GetScene();
  353. if ( scene )
  354. {
  355. int tz = g_pChoreoView->GetTimeZoom( g_pExpressionTool->GetToolName() );
  356. // Zoom time in / out
  357. if ( event->height > 0 )
  358. {
  359. g_pChoreoView->SetTimeZoom( g_pExpressionTool->GetToolName(), min( tz + TIME_ZOOM_STEP, MAX_TIME_ZOOM ), false );
  360. }
  361. else
  362. {
  363. g_pChoreoView->SetTimeZoom( g_pExpressionTool->GetToolName(), min( tz - TIME_ZOOM_STEP, MAX_TIME_ZOOM ), false );
  364. }
  365. g_pExpressionTool->RepositionHSlider();
  366. }
  367. redraw();
  368. iret = 1;
  369. return iret;
  370. }
  371. int offset = 0;
  372. int jump = 50;
  373. if ( event->height < 0 )
  374. {
  375. offset = m_pVertScrollBar->getValue();
  376. offset += jump;
  377. offset = min( offset, m_pVertScrollBar->getMaxValue() );
  378. }
  379. else
  380. {
  381. offset = m_pVertScrollBar->getValue();
  382. offset -= jump;
  383. offset = max( offset, m_pVertScrollBar->getMinValue() );
  384. }
  385. m_pVertScrollBar->setValue( offset );
  386. InvalidateRect( (HWND)m_pVertScrollBar->getHandle(), NULL, TRUE );
  387. m_nTopOffset = offset;
  388. LayoutItems();
  389. iret = 1;
  390. }
  391. break;
  392. case mxEvent::Action:
  393. {
  394. iret = 1;
  395. switch ( event->action )
  396. {
  397. default:
  398. iret = 0;
  399. break;
  400. case IDC_EXPRESSIONTOOLVSCROLL:
  401. {
  402. int offset = 0;
  403. bool processed = true;
  404. switch ( event->modifiers )
  405. {
  406. case SB_THUMBTRACK:
  407. offset = event->height;
  408. break;
  409. case SB_PAGEUP:
  410. offset = m_pVertScrollBar->getValue();
  411. offset -= 100;
  412. offset = max( offset, m_pVertScrollBar->getMinValue() );
  413. break;
  414. case SB_PAGEDOWN:
  415. offset = m_pVertScrollBar->getValue();
  416. offset += 100;
  417. offset = min( offset, m_pVertScrollBar->getMaxValue() );
  418. break;
  419. case SB_LINEDOWN:
  420. offset = m_pVertScrollBar->getValue();
  421. offset += 10;
  422. offset = min( offset, m_pVertScrollBar->getMaxValue() );
  423. break;
  424. case SB_LINEUP:
  425. offset = m_pVertScrollBar->getValue();
  426. offset -= 10;
  427. offset = max( offset, m_pVertScrollBar->getMinValue() );
  428. break;
  429. default:
  430. processed = false;
  431. break;
  432. }
  433. if ( processed )
  434. {
  435. m_pVertScrollBar->setValue( offset );
  436. InvalidateRect( (HWND)m_pVertScrollBar->getHandle(), NULL, TRUE );
  437. m_nTopOffset = offset;
  438. LayoutItems();
  439. }
  440. }
  441. }
  442. }
  443. break;
  444. }
  445. return iret;
  446. }
  447. void CExpressionToolWorkspace::HideTimelines( void )
  448. {
  449. for ( int i = 0; i < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; i++ )
  450. {
  451. TimelineItem *item = GetItem( i );
  452. Assert( item );
  453. item->SetVisible( false );
  454. }
  455. redraw();
  456. }
  457. TimelineItem *CExpressionToolWorkspace::GetItem( int number )
  458. {
  459. if ( number < 0 || number >= GLOBAL_STUDIO_FLEX_CONTROL_COUNT )
  460. {
  461. return NULL;
  462. }
  463. return m_pItems[ number ];
  464. }
  465. TimelineItem *CExpressionToolWorkspace::GetClickedItem( void )
  466. {
  467. return GetItem( m_nFocusItem );
  468. }
  469. //-----------------------------------------------------------------------------
  470. // Purpose:
  471. //-----------------------------------------------------------------------------
  472. void CExpressionToolWorkspace::ClearClickedItem( void )
  473. {
  474. m_nFocusItem = -1;
  475. }
  476. //-----------------------------------------------------------------------------
  477. // Purpose:
  478. // Input : force - force vert scrollbar recomputation
  479. //-----------------------------------------------------------------------------
  480. void CExpressionToolWorkspace::LayoutItems( bool force /* = false */ )
  481. {
  482. int x = TRAY_ITEM_INSET;
  483. int y = - m_nTopOffset;
  484. int width = w2() - 2 * TRAY_ITEM_INSET - m_nScrollbarHeight;
  485. int height;
  486. for ( int i = 0; i < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; i++ )
  487. {
  488. TimelineItem *item = GetItem( i );
  489. if ( !item || !item->GetVisible() )
  490. continue;
  491. height = item->GetHeight();
  492. RECT rcBounds;
  493. rcBounds.left = x;
  494. rcBounds.top = y;
  495. rcBounds.right = x + width;
  496. rcBounds.bottom = y + height;
  497. item->SetBounds( rcBounds );
  498. y += height + m_nItemGap;
  499. }
  500. if ( force || ( ComputeVPixelsNeeded() != m_nLastVPixelsNeeded ) )
  501. {
  502. RepositionVSlider();
  503. }
  504. redraw();
  505. }
  506. int CExpressionToolWorkspace::ComputeVPixelsNeeded( void )
  507. {
  508. int pixels = 0;
  509. // Count visible
  510. int c = 0;
  511. for ( int i = 0; i < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; i++ )
  512. {
  513. TimelineItem *item = GetItem( i );
  514. if ( !item || !item->GetVisible() )
  515. continue;
  516. c += item->GetHeight();
  517. c += m_nItemGap;
  518. }
  519. pixels += c;
  520. return pixels;
  521. }
  522. //-----------------------------------------------------------------------------
  523. // Purpose:
  524. //-----------------------------------------------------------------------------
  525. void CExpressionToolWorkspace::RepositionVSlider( void )
  526. {
  527. int pixelsneeded = ComputeVPixelsNeeded();
  528. if ( pixelsneeded <= ( h2() ))
  529. {
  530. m_pVertScrollBar->setVisible( false );
  531. m_nTopOffset = 0;
  532. }
  533. else
  534. {
  535. m_pVertScrollBar->setVisible( true );
  536. }
  537. m_pVertScrollBar->setBounds(
  538. w2() - m_nScrollbarHeight,
  539. 0,
  540. m_nScrollbarHeight,
  541. h2() );
  542. m_nTopOffset = max( 0, m_nTopOffset );
  543. m_nTopOffset = min( pixelsneeded, m_nTopOffset );
  544. m_pVertScrollBar->setRange( 0, pixelsneeded );
  545. m_pVertScrollBar->setValue( m_nTopOffset );
  546. m_pVertScrollBar->setPagesize( h2() );
  547. m_nLastVPixelsNeeded = pixelsneeded;
  548. }
  549. //-----------------------------------------------------------------------------
  550. // Purpose:
  551. //-----------------------------------------------------------------------------
  552. void CExpressionToolWorkspace::DisableAllExcept( void )
  553. {
  554. TimelineItem *keepExpanded = GetClickedItem();
  555. if ( !keepExpanded )
  556. return;
  557. g_pChoreoView->SetDirty( true );
  558. g_pChoreoView->PushUndo( "Disable All Except" );
  559. for ( int i = 0; i < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; i++ )
  560. {
  561. TimelineItem *item = GetItem( i );
  562. item->SetActive( item == keepExpanded ? true : false );
  563. }
  564. LayoutItems();
  565. g_pChoreoView->PushRedo( "Disable All Except" );
  566. }
  567. //-----------------------------------------------------------------------------
  568. // Purpose:
  569. //-----------------------------------------------------------------------------
  570. void CExpressionToolWorkspace::EnableValid( void )
  571. {
  572. g_pChoreoView->SetDirty( true );
  573. g_pChoreoView->PushUndo( "Enable Valid" );
  574. for ( int i = 0; i < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; i++ )
  575. {
  576. TimelineItem *item = GetItem( i );
  577. item->SetActive( item->IsValid() );
  578. }
  579. LayoutItems();
  580. g_pChoreoView->PushRedo( "Enable Valid" );
  581. }
  582. //-----------------------------------------------------------------------------
  583. // Purpose:
  584. //-----------------------------------------------------------------------------
  585. void CExpressionToolWorkspace::CollapseAll( TimelineItem *keepExpanded )
  586. {
  587. for ( int i = 0; i < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; i++ )
  588. {
  589. TimelineItem *item = GetItem( i );
  590. item->SetCollapsed( item == keepExpanded ? false : true );
  591. }
  592. LayoutItems();
  593. }
  594. //-----------------------------------------------------------------------------
  595. // Purpose:
  596. //-----------------------------------------------------------------------------
  597. void CExpressionToolWorkspace::ExpandAll( void )
  598. {
  599. for ( int i = 0; i < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; i++ )
  600. {
  601. TimelineItem *item = GetItem( i );
  602. item->SetCollapsed( false );
  603. }
  604. LayoutItems();
  605. }
  606. //-----------------------------------------------------------------------------
  607. // Purpose:
  608. //-----------------------------------------------------------------------------
  609. void CExpressionToolWorkspace::OnSnapAll()
  610. {
  611. g_pChoreoView->SetDirty( true );
  612. g_pChoreoView->PushUndo( "Snap All" );
  613. for ( int i = 0; i < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; i++ )
  614. {
  615. TimelineItem *item = GetItem( i );
  616. item->SnapAll();
  617. }
  618. g_pChoreoView->PushRedo( "Snap All" );
  619. redraw();
  620. }
  621. //-----------------------------------------------------------------------------
  622. // Purpose:
  623. //-----------------------------------------------------------------------------
  624. void CExpressionToolWorkspace::OnDeleteColumn()
  625. {
  626. float t = g_pExpressionTool->GetTimeForClickedPos();
  627. float snapped = FacePoser_SnapTime( t );
  628. int scenefps = FacePoser_GetSceneFPS();
  629. if ( scenefps <= 0 )
  630. {
  631. Con_Printf( "Can't delete column, scene fps is <= 0 (%i)\n", scenefps );
  632. return;
  633. }
  634. int clickedframe = ( int ) ( scenefps * snapped + 0.5f );
  635. // One half of 1/fps on each side
  636. float epsilon = epsilon = 0.5f / (float)scenefps;
  637. CInputParams params;
  638. memset( &params, 0, sizeof( params ) );
  639. strcpy( params.m_szDialogTitle, "Delete Column" );
  640. strcpy( params.m_szPrompt, "Frame(s) to delete [e.g., 82 or 81-91 ]:" );
  641. Q_snprintf( params.m_szInputText, sizeof( params.m_szInputText ), "%i", clickedframe );
  642. if ( !InputProperties( &params ) )
  643. return;
  644. int deleteframestart;
  645. int deleteframeend;
  646. char *sep = Q_strstr( params.m_szInputText, "-" );
  647. if ( sep )
  648. {
  649. *sep = 0;
  650. deleteframestart = atoi( params.m_szInputText );
  651. deleteframeend = atoi( sep + 1 );
  652. deleteframeend = max( deleteframestart, deleteframeend );
  653. }
  654. else
  655. {
  656. deleteframestart = atoi( params.m_szInputText );
  657. deleteframeend = deleteframestart;
  658. }
  659. float start, end;
  660. start = (float)deleteframestart / (float)scenefps;
  661. end = (float)deleteframeend / (float)scenefps;
  662. g_pChoreoView->SetDirty( true );
  663. g_pChoreoView->PushUndo( "Delete Column" );
  664. for ( int i = 0; i < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; i++ )
  665. {
  666. TimelineItem *item = GetItem( i );
  667. item->DeletePoints( start - epsilon, end + epsilon );
  668. }
  669. g_pChoreoView->PushRedo( "Delete Column" );
  670. redraw();
  671. }
  672. //-----------------------------------------------------------------------------
  673. // Purpose:
  674. //-----------------------------------------------------------------------------
  675. void CExpressionToolWorkspace::ExpandValid( void )
  676. {
  677. for ( int i = 0; i < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; i++ )
  678. {
  679. TimelineItem *item = GetItem( i );
  680. bool valid = item->IsValid();
  681. item->SetCollapsed( !valid );
  682. }
  683. LayoutItems();
  684. }
  685. //-----------------------------------------------------------------------------
  686. // Purpose:
  687. // Output : int
  688. //-----------------------------------------------------------------------------
  689. int CExpressionToolWorkspace::CountSelectedSamples( void )
  690. {
  691. int c = 0;
  692. for ( int i = 0; i < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; i++ )
  693. {
  694. TimelineItem *item = GetItem( i );
  695. Assert( item );
  696. item->CountSelected();
  697. c += item->GetNumSelected();
  698. }
  699. return c;
  700. }
  701. void CExpressionToolWorkspace::MoveSelectedSamples( float dfdx, float dfdy, bool snap )
  702. {
  703. int selecteditems = CountSelectedSamples();
  704. if ( !selecteditems )
  705. return;
  706. CChoreoEvent *e = g_pExpressionTool->GetSafeEvent();
  707. if ( !e )
  708. return;
  709. float eventduration = e->GetDuration();
  710. for ( int controller = 0; controller < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; controller++ )
  711. {
  712. TimelineItem *item = GetItem( controller );
  713. if ( !item )
  714. continue;
  715. CFlexAnimationTrack *track = item->GetSafeTrack();
  716. if ( !track )
  717. continue;
  718. // If the track is a combo type track, then move any underlying selected samples, too
  719. for ( int edittype = 0; edittype <= ( track->IsComboType() ? 1 : 0 ); edittype++ )
  720. {
  721. for ( int i = 0; i < (int)track->GetNumSamples( edittype ); i++ )
  722. {
  723. CExpressionSample *sample = track->GetSample( i, edittype );
  724. if ( !sample || !sample->selected )
  725. continue;
  726. sample->time += dfdx;
  727. sample->time = clamp( sample->time, 0.0f, eventduration );
  728. if ( snap )
  729. {
  730. sample->time = FacePoser_SnapTime( sample->time );
  731. }
  732. sample->value -= dfdy;
  733. sample->value = clamp( sample->value, 0.0f, 1.0f );
  734. }
  735. }
  736. track->Resort();
  737. item->DrawSelf();
  738. }
  739. }
  740. void CExpressionToolWorkspace::DeleteSelectedSamples( void )
  741. {
  742. int i, t;
  743. int selecteditems = CountSelectedSamples();
  744. if ( !selecteditems )
  745. return;
  746. g_pChoreoView->SetDirty( true );
  747. g_pChoreoView->PushUndo( "Delete points" );
  748. for ( int controller = 0; controller < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; controller++ )
  749. {
  750. TimelineItem *item = GetItem( controller );
  751. if ( !item )
  752. continue;
  753. CFlexAnimationTrack *track = item->GetSafeTrack();
  754. if ( !track )
  755. continue;
  756. for ( t = 0; t < 2; t++ )
  757. {
  758. for ( i = track->GetNumSamples( t ) - 1; i >= 0 ; i-- )
  759. {
  760. CExpressionSample *sample = track->GetSample( i, t );
  761. if ( !sample->selected )
  762. continue;
  763. track->RemoveSample( i, t );
  764. }
  765. }
  766. item->DrawSelf();
  767. }
  768. g_pChoreoView->PushRedo( "Delete points" );
  769. }
  770. //-----------------------------------------------------------------------------
  771. // Purpose:
  772. //-----------------------------------------------------------------------------
  773. void CExpressionToolWorkspace::DeselectAll( void )
  774. {
  775. int i, t;
  776. int selecteditems = CountSelectedSamples();
  777. if ( !selecteditems )
  778. return;
  779. for ( int controller = 0; controller < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; controller++ )
  780. {
  781. TimelineItem *item = GetItem( controller );
  782. if ( !item )
  783. continue;
  784. CFlexAnimationTrack *track = item->GetSafeTrack();
  785. if ( !track )
  786. continue;
  787. for ( t = 0; t < 2; t++ )
  788. {
  789. for ( i = track->GetNumSamples( t ) - 1; i >= 0 ; i-- )
  790. {
  791. CExpressionSample *sample = track->GetSample( i, t );
  792. sample->selected = false;
  793. }
  794. }
  795. item->DrawSelf();
  796. }
  797. }
  798. void CExpressionToolWorkspace::SelectPoints( float start, float end )
  799. {
  800. int i, t;
  801. for ( int controller = 0; controller < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; controller++ )
  802. {
  803. TimelineItem *item = GetItem( controller );
  804. if ( !item )
  805. continue;
  806. CFlexAnimationTrack *track = item->GetSafeTrack();
  807. if ( !track )
  808. continue;
  809. for ( t = 0; t < 2; t++ )
  810. {
  811. for ( i = track->GetNumSamples( t ) - 1; i >= 0 ; i-- )
  812. {
  813. CExpressionSample *sample = track->GetSample( i, t );
  814. bool inrange = ( sample->time >= start && sample->time <= end );
  815. sample->selected = inrange;
  816. }
  817. }
  818. item->DrawSelf();
  819. }
  820. }
  821. ExpressionTool::ExpressionTool( mxWindow *parent )
  822. : IFacePoserToolWindow( "ExpressionTool", "Flex Animation" ), mxWindow( parent, 0, 0, 0, 0 )
  823. {
  824. m_bSuppressLayout = false;
  825. SetAutoProcess( true );
  826. m_pWorkspace = new CExpressionToolWorkspace( this );
  827. m_nFocusEventGlobalID = -1;
  828. m_flScrub = 0.0f;
  829. m_flScrubTarget = 0.0f;
  830. m_nDragType = DRAGTYPE_NONE;
  831. m_nClickedX = 0;
  832. m_nClickedY = 0;
  833. m_hPrevCursor = 0;
  834. m_nStartX = 0;
  835. m_nStartY = 0;
  836. m_nMinX = 0;
  837. m_nMaxX = 0;
  838. m_bUseBounds = false;
  839. m_pLastEvent = NULL;
  840. m_nMousePos[ 0 ] = m_nMousePos[ 1 ] = 0;
  841. m_flSelection[ 0 ] = m_flSelection[ 1 ] = 0.0f;
  842. m_bSelectionActive = false;
  843. m_bLayoutIsValid = false;
  844. m_flPixelsPerSecond = 500.0f;
  845. m_flLastDuration = 0.0f;
  846. m_nScrollbarHeight = 12;
  847. m_flLeftOffset = 0.0f;
  848. m_nLastHPixelsNeeded = -1;
  849. m_pHorzScrollBar = new mxScrollbar( this, 0, 0, 18, 100, IDC_FLEXHSCROLL, mxScrollbar::Horizontal );
  850. m_pHorzScrollBar->setVisible( false );
  851. m_bInSetEvent = false;
  852. m_flScrubberTimeOffset = 0.0f;
  853. }
  854. ExpressionTool::~ExpressionTool( void )
  855. {
  856. }
  857. void ExpressionTool::DoTrackLookup( CChoreoEvent *event )
  858. {
  859. if ( !event || !models->GetActiveStudioModel() )
  860. return;
  861. //if ( event->GetTrackLookupSet() )
  862. // return;
  863. // Force recompute
  864. SetEvent( event );
  865. }
  866. #pragma optimize( "g", off )
  867. void ExpressionTool::SetEvent( CChoreoEvent *event )
  868. {
  869. if ( m_bInSetEvent )
  870. return;
  871. m_bInSetEvent = true;
  872. if ( event == m_pLastEvent )
  873. {
  874. if ( event )
  875. {
  876. float dur = event->GetDuration();
  877. if ( dur != m_flLastDuration )
  878. {
  879. m_flLastDuration = dur;
  880. m_nLastHPixelsNeeded = -1;
  881. m_flLeftOffset = 0.0f;
  882. InvalidateLayout();
  883. }
  884. m_nFocusEventGlobalID = event->GetGlobalID();
  885. }
  886. m_bInSetEvent = false;
  887. return;
  888. }
  889. m_pLastEvent = event;
  890. m_pWorkspace->HideTimelines();
  891. m_nFocusEventGlobalID = -1;
  892. if ( event )
  893. {
  894. m_nFocusEventGlobalID = event->GetGlobalID();
  895. if ( models->GetActiveStudioModel() )
  896. {
  897. CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();
  898. if ( hdr )
  899. {
  900. // Force re-lookup
  901. event->SetTrackLookupSet( false );
  902. SetupFlexControllerTracks( hdr, event );
  903. int itemCount = 0;
  904. for ( int i = 0; i < event->GetNumFlexAnimationTracks(); i++ )
  905. {
  906. CFlexAnimationTrack *track = event->GetFlexAnimationTrack( i );
  907. Assert( track );
  908. if ( !track )
  909. continue;
  910. TimelineItem *item = m_pWorkspace->GetItem( itemCount++ );
  911. item->SetExpressionInfo( track, track->GetFlexControllerIndex( 0 ) );
  912. item->SetCollapsed( track->GetNumSamples( 0 ) <= 0 );
  913. item->SetVisible( true );
  914. }
  915. m_pWorkspace->LayoutItems( true );
  916. }
  917. }
  918. }
  919. DeselectAll();
  920. if ( event )
  921. {
  922. m_flLastDuration = event->GetDuration();
  923. }
  924. else
  925. {
  926. m_flLastDuration = 0.0f;
  927. }
  928. m_flLeftOffset = 0.0f;
  929. m_nLastHPixelsNeeded = -1;
  930. InvalidateLayout();
  931. m_bInSetEvent = false;
  932. }
  933. #pragma optimize( "g", on )
  934. //-----------------------------------------------------------------------------
  935. // Purpose:
  936. // Output : Returns true on success, false on failure.
  937. //-----------------------------------------------------------------------------
  938. bool ExpressionTool::HasCopyData( void )
  939. {
  940. return ( m_CopyData[0].Size() != 0 ) ? true : false;
  941. }
  942. //-----------------------------------------------------------------------------
  943. // Purpose:
  944. // Input : *source -
  945. //-----------------------------------------------------------------------------
  946. void ExpressionTool::Copy( CFlexAnimationTrack *source )
  947. {
  948. for ( int t = 0; t < 2; t++ )
  949. {
  950. m_CopyData[ t ].RemoveAll();
  951. if ( t == 0 || source->IsComboType() )
  952. {
  953. for ( int i = 0 ; i < source->GetNumSamples( t ); i++ )
  954. {
  955. CExpressionSample *s = source->GetSample( i, t );
  956. m_CopyData[ t ].AddToTail( *s );
  957. }
  958. }
  959. }
  960. }
  961. //-----------------------------------------------------------------------------
  962. // Purpose:
  963. // Input : *destination -
  964. //-----------------------------------------------------------------------------
  965. void ExpressionTool::Paste( CFlexAnimationTrack *destination )
  966. {
  967. g_pChoreoView->SetDirty( true );
  968. g_pChoreoView->PushUndo( "Paste" );
  969. destination->Clear();
  970. for ( int t = 0; t < 2; t++ )
  971. {
  972. for ( int i = 0; i < m_CopyData[ t ].Size() ; i++ )
  973. {
  974. CExpressionSample *s = &m_CopyData[ t ][ i ];
  975. if ( t == 0 || destination->IsComboType() )
  976. {
  977. destination->AddSample( s->time, s->value, t );
  978. }
  979. }
  980. destination->Resort( t );
  981. }
  982. g_pChoreoView->PushRedo( "Paste" );
  983. }
  984. //-----------------------------------------------------------------------------
  985. // Purpose:
  986. //-----------------------------------------------------------------------------
  987. CChoreoEvent *ExpressionTool::GetSafeEvent( void )
  988. {
  989. if ( m_nFocusEventGlobalID == -1 )
  990. return NULL;
  991. if ( !g_pChoreoView )
  992. return NULL;
  993. CChoreoScene *scene = g_pChoreoView->GetScene();
  994. if ( !scene )
  995. return NULL;
  996. // look to see if it's focused any any event
  997. for ( int i = 0; i < scene->GetNumEvents() ; i++ )
  998. {
  999. CChoreoEvent *e = scene->GetEvent( i );
  1000. if ( !e || e->GetType() != CChoreoEvent::FLEXANIMATION )
  1001. continue;
  1002. if ( e->GetGlobalID() == m_nFocusEventGlobalID )
  1003. {
  1004. DoTrackLookup( e );
  1005. return e;
  1006. }
  1007. }
  1008. return NULL;
  1009. }
  1010. //-----------------------------------------------------------------------------
  1011. // Purpose:
  1012. // Input : rcHandle -
  1013. //-----------------------------------------------------------------------------
  1014. void ExpressionTool::GetScrubHandleRect( RECT& rcHandle, bool clipped )
  1015. {
  1016. float pixel = 0.0f;
  1017. if ( m_pWorkspace->w2() > 0 )
  1018. {
  1019. pixel = GetPixelForTimeValue( m_flScrub );
  1020. if ( clipped )
  1021. {
  1022. pixel = clamp( pixel, SCRUBBER_HANDLE_WIDTH/2, w2() - SCRUBBER_HANDLE_WIDTH/2 );
  1023. }
  1024. }
  1025. rcHandle.left = pixel-SCRUBBER_HANDLE_WIDTH/2;
  1026. rcHandle.right = pixel + SCRUBBER_HANDLE_WIDTH/2;
  1027. rcHandle.top = 2 + GetCaptionHeight();
  1028. rcHandle.bottom = rcHandle.top + SCRUBBER_HANDLE_HEIGHT;
  1029. }
  1030. //-----------------------------------------------------------------------------
  1031. // Purpose:
  1032. // Input : drawHelper -
  1033. // rcHandle -
  1034. //-----------------------------------------------------------------------------
  1035. void ExpressionTool::DrawScrubHandle( CChoreoWidgetDrawHelper& drawHelper, RECT& rcHandle )
  1036. {
  1037. HBRUSH br = CreateSolidBrush( RGB( 0, 150, 100 ) );
  1038. COLORREF areaBorder = RGB( 230, 230, 220 );
  1039. drawHelper.DrawColoredLine( areaBorder,
  1040. PS_SOLID, 1, 0, rcHandle.top, w2(), rcHandle.top );
  1041. drawHelper.DrawColoredLine( areaBorder,
  1042. PS_SOLID, 1, 0, rcHandle.bottom, w2(), rcHandle.bottom );
  1043. drawHelper.DrawFilledRect( br, rcHandle );
  1044. //
  1045. char sz[ 32 ];
  1046. sprintf( sz, "%.3f", m_flScrub );
  1047. CChoreoEvent *ev = GetSafeEvent();
  1048. if ( ev )
  1049. {
  1050. float st, ed;
  1051. st = ev->GetStartTime();
  1052. ed = ev->GetEndTime();
  1053. float dt = ed - st;
  1054. if ( dt > 0.0f )
  1055. {
  1056. sprintf( sz, "%.3f", st + m_flScrub );
  1057. }
  1058. }
  1059. int len = drawHelper.CalcTextWidth( "Arial", 9, 500, sz );
  1060. RECT rcText = rcHandle;
  1061. int textw = rcText.right - rcText.left;
  1062. rcText.left += ( textw - len ) / 2;
  1063. drawHelper.DrawColoredText( "Arial", 9, 500, RGB( 255, 255, 255 ), rcText, sz );
  1064. DeleteObject( br );
  1065. }
  1066. //-----------------------------------------------------------------------------
  1067. // Purpose:
  1068. // Input : *event -
  1069. // Output : Returns true on success, false on failure.
  1070. //-----------------------------------------------------------------------------
  1071. bool ExpressionTool::IsMouseOverScrubHandle( mxEvent *event )
  1072. {
  1073. RECT rcHandle;
  1074. GetScrubHandleRect( rcHandle, true );
  1075. InflateRect( &rcHandle, 2, 2 );
  1076. POINT pt;
  1077. pt.x = (short)event->x;
  1078. pt.y = (short)event->y;
  1079. if ( PtInRect( &rcHandle, pt ) )
  1080. {
  1081. return true;
  1082. }
  1083. return false;
  1084. }
  1085. //-----------------------------------------------------------------------------
  1086. // Purpose:
  1087. // Output : Returns true on success, false on failure.
  1088. //-----------------------------------------------------------------------------
  1089. bool ExpressionTool::IsProcessing( void )
  1090. {
  1091. if ( !GetSafeEvent() )
  1092. return false;
  1093. if ( m_flScrub != m_flScrubTarget )
  1094. return true;
  1095. return false;
  1096. }
  1097. bool ExpressionTool::IsScrubbing( void ) const
  1098. {
  1099. bool scrubbing = ( m_nDragType == DRAGTYPE_SCRUBBER ) ? true : false;
  1100. return scrubbing;
  1101. }
  1102. void ExpressionTool::Think( float dt )
  1103. {
  1104. CChoreoEvent *event = GetSafeEvent();
  1105. if ( !event )
  1106. return;
  1107. bool scrubbing = IsScrubbing();
  1108. ScrubThink( dt, scrubbing );
  1109. }
  1110. void ExpressionTool::SetScrubTime( float t )
  1111. {
  1112. m_flScrub = t;
  1113. CChoreoEvent *e = GetSafeEvent();
  1114. if ( e )
  1115. {
  1116. float realtime = e->GetStartTime() + m_flScrub;
  1117. g_pChoreoView->SetScrubTime( realtime );
  1118. g_pChoreoView->DrawScrubHandle();
  1119. }
  1120. }
  1121. void ExpressionTool::SetScrubTargetTime( float t )
  1122. {
  1123. m_flScrubTarget = t;
  1124. CChoreoEvent *e = GetSafeEvent();
  1125. if ( e )
  1126. {
  1127. float realtime = e->GetStartTime() + m_flScrubTarget;
  1128. g_pChoreoView->SetScrubTargetTime( realtime );
  1129. }
  1130. }
  1131. //-----------------------------------------------------------------------------
  1132. // Purpose:
  1133. // Input : dt -
  1134. //-----------------------------------------------------------------------------
  1135. void ExpressionTool::ScrubThink( float dt, bool scrubbing )
  1136. {
  1137. CChoreoEvent *event = GetSafeEvent();
  1138. if ( !event )
  1139. return;
  1140. if ( m_flScrubTarget == m_flScrub && !scrubbing )
  1141. return;
  1142. float d = m_flScrubTarget - m_flScrub;
  1143. int sign = d > 0.0f ? 1 : -1;
  1144. float maxmove = dt;
  1145. if ( sign > 0 )
  1146. {
  1147. if ( d < maxmove )
  1148. {
  1149. SetScrubTime( m_flScrubTarget );
  1150. }
  1151. else
  1152. {
  1153. SetScrubTime( m_flScrub + maxmove );
  1154. }
  1155. }
  1156. else
  1157. {
  1158. if ( -d < maxmove )
  1159. {
  1160. SetScrubTime( m_flScrubTarget );
  1161. }
  1162. else
  1163. {
  1164. SetScrubTime( m_flScrub - maxmove );
  1165. }
  1166. }
  1167. if ( scrubbing )
  1168. {
  1169. g_pMatSysWindow->Frame();
  1170. }
  1171. }
  1172. void ExpressionTool::redraw()
  1173. {
  1174. if ( !ToolCanDraw() )
  1175. return;
  1176. CChoreoWidgetDrawHelper drawHelper( this );
  1177. HandleToolRedraw( drawHelper );
  1178. COLORREF areaBorder = RGB( 230, 230, 220 );
  1179. RECT rcSelection;
  1180. GetWorkspaceRect( rcSelection );
  1181. drawHelper.DrawColoredLine( areaBorder,
  1182. PS_SOLID, 1, 0, rcSelection.top, w2(), rcSelection.top );
  1183. drawHelper.DrawColoredLine( areaBorder,
  1184. PS_SOLID, 1, 0, rcSelection.bottom, w2(), rcSelection.bottom );
  1185. if ( m_bSelectionActive )
  1186. {
  1187. RECT rcClient;
  1188. drawHelper.GetClientRect( rcClient );
  1189. int left, right;
  1190. left = GetPixelForTimeValue( m_flSelection[ 0 ] );
  1191. right = GetPixelForTimeValue( m_flSelection[ 1 ] );
  1192. rcSelection.left = left;
  1193. rcSelection.right = right;
  1194. rcSelection.bottom = TRAY_HEIGHT;
  1195. drawHelper.DrawFilledRect( RGB( 200, 220, 230 ), rcSelection );
  1196. drawHelper.DrawColoredLine( RGB( 100, 100, 255 ), PS_SOLID, 3, rcSelection.left, rcSelection.top, rcSelection.left, rcSelection.bottom );
  1197. drawHelper.DrawColoredLine( RGB( 100, 100, 255 ), PS_SOLID, 3, rcSelection.right, rcSelection.top, rcSelection.right, rcSelection.bottom );
  1198. }
  1199. CChoreoEvent *ev = GetSafeEvent();
  1200. if ( ev )
  1201. {
  1202. RECT rcText;
  1203. drawHelper.GetClientRect( rcText );
  1204. rcText.top += GetCaptionHeight()+1;
  1205. rcText.bottom = rcText.top + 13;
  1206. rcText.left += 5;
  1207. rcText.right -= 5;
  1208. OffsetRect( &rcText, 0, 12 );
  1209. int current, total;
  1210. g_pChoreoView->GetUndoLevels( current, total );
  1211. if ( total > 0 )
  1212. {
  1213. RECT rcUndo = rcText;
  1214. OffsetRect( &rcUndo, 0, 2 );
  1215. drawHelper.DrawColoredText( "Small Fonts", 8, FW_NORMAL, RGB( 0, 100, 0 ), rcUndo,
  1216. "Undo: %i/%i", current, total );
  1217. }
  1218. rcText.left += 60;
  1219. // Found it, write out description
  1220. //
  1221. drawHelper.DrawColoredText( "Arial", 11, 900, RGB( 200, 150, 100 ), rcText,
  1222. "Event: %s",
  1223. ev->GetName() );
  1224. OffsetRect( &rcText, 0, 30 );
  1225. rcText.left = 5;
  1226. RECT timeRect = rcText;
  1227. timeRect.right = timeRect.left + 100;
  1228. char sz[ 32 ];
  1229. float st, ed;
  1230. GetStartAndEndTime( st, ed );
  1231. st += ev->GetStartTime();
  1232. ed += ev->GetStartTime();
  1233. Q_snprintf( sz, sizeof( sz ), "%.2f", st );
  1234. drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 0, 0, 0 ), timeRect, sz );
  1235. timeRect = rcText;
  1236. Q_snprintf( sz, sizeof( sz ), "%.2f", ed );
  1237. int textW = drawHelper.CalcTextWidth( "Arial", 9, FW_NORMAL, sz );
  1238. timeRect.right = w2() - 10;
  1239. timeRect.left = timeRect.right - textW;
  1240. drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 0, 0, 0 ), timeRect, sz );
  1241. }
  1242. RECT rcHandle;
  1243. GetScrubHandleRect( rcHandle, true );
  1244. DrawScrubHandle( drawHelper, rcHandle );
  1245. DrawRelativeTags( drawHelper );
  1246. RECT rcPos;
  1247. GetMouseOverPosRect( rcPos );
  1248. DrawMouseOverPos( drawHelper, rcPos );
  1249. DrawEventEnd( drawHelper );
  1250. }
  1251. //-----------------------------------------------------------------------------
  1252. // Purpose:
  1253. // Input : *tag -
  1254. //-----------------------------------------------------------------------------
  1255. bool ExpressionTool::GetTimingTagRect( RECT& rcClient, CChoreoEvent *event, CFlexTimingTag *tag, RECT& rcTag )
  1256. {
  1257. rcTag = rcClient;
  1258. int tagx = GetPixelForTimeValue( tag->GetStartTime() - event->GetStartTime() );
  1259. rcTag.top = rcClient.bottom - 6;
  1260. rcTag.bottom = rcTag.top + 6;
  1261. rcTag.left = tagx - 3;
  1262. rcTag.right = tagx + 3;
  1263. return true;
  1264. }
  1265. // Get workspace min, max point in terms of tool window
  1266. void ExpressionTool::GetWorkspaceLeftRight( int& left, int& right )
  1267. {
  1268. POINT pt;
  1269. pt.x = TRAY_ITEM_INSET;
  1270. pt.y = 0;
  1271. ClientToScreen( (HWND)m_pWorkspace->getHandle(), &pt );
  1272. ScreenToClient( (HWND)getHandle(), &pt );
  1273. left = (short)pt.x;
  1274. pt.x = m_pWorkspace->w2() - TRAY_ITEM_INSET - 12;
  1275. pt.y = 0;
  1276. ClientToScreen( (HWND)m_pWorkspace->getHandle(), &pt );
  1277. ScreenToClient( (HWND)getHandle(), &pt );
  1278. right = (short)pt.x;
  1279. }
  1280. //-----------------------------------------------------------------------------
  1281. // Purpose:
  1282. // Input : mx -
  1283. // my -
  1284. // Output : CFlexTimingTag
  1285. //-----------------------------------------------------------------------------
  1286. CFlexTimingTag *ExpressionTool::IsMouseOverTag( int mx, int my )
  1287. {
  1288. CChoreoEvent *event = GetSafeEvent();
  1289. if ( !event )
  1290. return NULL;
  1291. RECT rcClient;
  1292. GetClientRect( (HWND)getHandle(), &rcClient );
  1293. int left, right;
  1294. GetWorkspaceLeftRight( left, right );
  1295. rcClient.left = left;
  1296. rcClient.right = right;
  1297. rcClient.top = GetCaptionHeight();
  1298. rcClient.bottom = rcClient.top + TRAY_HEIGHT;
  1299. POINT pt;
  1300. pt.x = mx;
  1301. pt.y = my;
  1302. for ( int i = 0 ; i < event->GetNumTimingTags(); i++ )
  1303. {
  1304. CFlexTimingTag *tag = event->GetTimingTag( i );
  1305. if ( !tag )
  1306. continue;
  1307. RECT rcTag;
  1308. if ( !GetTimingTagRect( rcClient, event, tag, rcTag ) )
  1309. continue;
  1310. if ( !PtInRect( &rcTag, pt ) )
  1311. continue;
  1312. return tag;
  1313. }
  1314. return NULL;
  1315. }
  1316. //-----------------------------------------------------------------------------
  1317. // Purpose:
  1318. // Input : drawHelper -
  1319. //-----------------------------------------------------------------------------
  1320. void ExpressionTool::DrawRelativeTags( CChoreoWidgetDrawHelper& drawHelper )
  1321. {
  1322. CChoreoEvent *event = GetSafeEvent();
  1323. if ( !event )
  1324. return;
  1325. float st, ed;
  1326. GetStartAndEndTime( st, ed );
  1327. if ( event->GetDuration() <= 0.0f )
  1328. return;
  1329. CChoreoScene *scene = g_pChoreoView->GetScene();
  1330. if ( !scene )
  1331. return;
  1332. RECT rcClient;
  1333. drawHelper.GetClientRect( rcClient );
  1334. int left, right;
  1335. GetWorkspaceLeftRight( left, right );
  1336. rcClient.top += GetCaptionHeight();
  1337. rcClient.left = left;
  1338. rcClient.right = right;
  1339. rcClient.bottom = rcClient.top + TRAY_HEIGHT;
  1340. // Iterate relative tags
  1341. for ( int i = 0; i < scene->GetNumActors(); i++ )
  1342. {
  1343. CChoreoActor *a = scene->GetActor( i );
  1344. if ( !a )
  1345. continue;
  1346. for ( int j = 0; j < a->GetNumChannels(); j++ )
  1347. {
  1348. CChoreoChannel *c = a->GetChannel( j );
  1349. if ( !c )
  1350. continue;
  1351. for ( int k = 0 ; k < c->GetNumEvents(); k++ )
  1352. {
  1353. CChoreoEvent *e = c->GetEvent( k );
  1354. if ( !e )
  1355. continue;
  1356. // add each tag to combo box
  1357. for ( int t = 0; t < e->GetNumRelativeTags(); t++ )
  1358. {
  1359. CEventRelativeTag *tag = e->GetRelativeTag( t );
  1360. if ( !tag )
  1361. continue;
  1362. //SendMessage( control, CB_ADDSTRING, 0, (LPARAM)va( "\"%s\" \"%s\"", tag->GetName(), e->GetParameters() ) );
  1363. bool clipped;
  1364. int tagx = GetPixelForTimeValue( tag->GetStartTime() - event->GetStartTime(), &clipped );
  1365. if ( clipped )
  1366. continue;
  1367. //drawHelper.DrawColoredLine( RGB( 180, 180, 220 ), PS_SOLID, 1, tagx, rcClient.top, tagx, rcClient.bottom );
  1368. RECT rcMark;
  1369. rcMark = rcClient;
  1370. rcMark.top = rcClient.bottom - 6;
  1371. rcMark.left = tagx - 3;
  1372. rcMark.right = tagx + 3;
  1373. drawHelper.DrawTriangleMarker( rcMark, RGB( 0, 100, 250 ) );
  1374. RECT rcText;
  1375. rcText = rcMark;
  1376. rcText.top -= 10;
  1377. int len = drawHelper.CalcTextWidth( "Arial", 9, FW_NORMAL, tag->GetName() );
  1378. rcText.left = tagx - len / 2;
  1379. rcText.right = rcText.left + len + 2;
  1380. rcText.bottom = rcText.top + 10;
  1381. drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 0, 100, 200 ), rcText, tag->GetName() );
  1382. }
  1383. }
  1384. }
  1385. }
  1386. for ( int t = 0; t < event->GetNumTimingTags(); t++ )
  1387. {
  1388. CFlexTimingTag *tag = event->GetTimingTag( t );
  1389. if ( !tag )
  1390. continue;
  1391. RECT rcMark;
  1392. if ( !GetTimingTagRect( rcClient, event, tag, rcMark ) )
  1393. continue;
  1394. drawHelper.DrawTriangleMarker( rcMark, RGB( 250, 100, 0 ) );
  1395. RECT rcText;
  1396. rcText = rcMark;
  1397. rcText.top -= 20;
  1398. char text[ 256 ];
  1399. sprintf( text, "%s", tag->GetName() );
  1400. if ( tag->GetLocked() )
  1401. {
  1402. strcat( text, " - locked" );
  1403. }
  1404. int len = drawHelper.CalcTextWidth( "Arial", 9, FW_NORMAL, text );
  1405. rcText.left = ( rcMark.left + rcMark.right ) / 2 - len / 2;
  1406. rcText.right = rcText.left + len + 2;
  1407. rcText.bottom = rcText.top + 10;
  1408. drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 200, 100, 0 ), rcText, text );
  1409. }
  1410. }
  1411. //-----------------------------------------------------------------------------
  1412. // Purpose:
  1413. //-----------------------------------------------------------------------------
  1414. void ExpressionTool::ShowContextMenu( mxEvent *event, bool include_track_menus )
  1415. {
  1416. // Construct main menu
  1417. mxPopupMenu *pop = new mxPopupMenu();
  1418. TimelineItem *item = NULL;
  1419. CFlexAnimationTrack *track = NULL;
  1420. if ( include_track_menus )
  1421. {
  1422. item = m_pWorkspace->GetClickedItem();
  1423. if ( item )
  1424. {
  1425. item->CountSelected();
  1426. track = item->GetSafeTrack();
  1427. }
  1428. }
  1429. int current, total;
  1430. g_pChoreoView->GetUndoLevels( current, total );
  1431. if ( total > 0 )
  1432. {
  1433. if ( current > 0 )
  1434. {
  1435. pop->add( va( "Undo %s", g_pChoreoView->GetUndoDescription() ), IDC_UNDO_FA );
  1436. }
  1437. if ( current <= total - 1 )
  1438. {
  1439. pop->add( va( "Redo %s", g_pChoreoView->GetRedoDescription() ), IDC_REDO_FA );
  1440. }
  1441. pop->addSeparator();
  1442. }
  1443. // Create expand menu
  1444. mxPopupMenu *expand = new mxPopupMenu();
  1445. if ( item && track && item->IsCollapsed() )
  1446. {
  1447. expand->add( va( "Track '%s'", track->GetFlexControllerName() ), IDC_TL_EXPAND );
  1448. }
  1449. expand->add( "All tracks", IDC_EXPANDALL );
  1450. expand->add( "Used tracks", IDC_EXPANDVALID );
  1451. pop->addMenu( "Expand", expand );
  1452. mxPopupMenu *collapse = new mxPopupMenu;
  1453. if ( item && track && !item->IsCollapsed() )
  1454. {
  1455. collapse->add( va( "Track '%s'", track->GetFlexControllerName() ), IDC_TL_COLLAPSE );
  1456. collapse->add( va( "All tracks except '%s'", track->GetFlexControllerName() ), IDC_COLLAPSE_ALL_EXCEPT );
  1457. }
  1458. collapse->add( "All tracks", IDC_COLLAPSEALL );
  1459. pop->addMenu( "Collapse", collapse );
  1460. pop->addSeparator();
  1461. pop->add( va( "Enable all valid" ), IDC_ENABLE_ALL_VALID );
  1462. if ( item && track )
  1463. {
  1464. if ( item->IsActive() )
  1465. {
  1466. pop->add( va( "Disable '%s'", track->GetFlexControllerName() ), IDC_TL_DISABLE );
  1467. }
  1468. else
  1469. {
  1470. pop->add( va( "Enable '%s'", track->GetFlexControllerName() ), IDC_TL_ENABLE );
  1471. }
  1472. pop->add( va( "Disable all except '%s'", track->GetFlexControllerName() ), IDC_DISABLE_ALL_EXCEPT );
  1473. pop->addSeparator();
  1474. pop->add( "Copy", IDC_TL_COPY );
  1475. if ( HasCopyData() )
  1476. {
  1477. pop->add( "Paste", IDC_TL_PASTE );
  1478. }
  1479. pop->addSeparator();
  1480. if ( item->GetNumSelected() > 0 )
  1481. {
  1482. pop->add( va( "Delete" ), IDC_TL_DELETE );
  1483. pop->add( "Deselect all", IDC_TL_DESELECT );
  1484. pop->add( va( "Scale selected..." ), IDC_FLEX_SCALESAMPLES );
  1485. }
  1486. pop->add( "Select all", IDC_TL_SELECTALL );
  1487. if ( FacePoser_IsSnapping() )
  1488. {
  1489. mxPopupMenu *snap = new mxPopupMenu();
  1490. snap->add( va( "All points" ), IDC_TL_SNAPALL );
  1491. snap->add( va( "All points in '%s'", track->GetFlexControllerName() ), IDC_TL_SNAPPOINTS );
  1492. snap->add( va( "Selected points in '%s'", track->GetFlexControllerName() ), IDC_TL_SNAPSELECTED );
  1493. pop->addSeparator();
  1494. pop->addMenu( "Snap", snap );
  1495. }
  1496. if ( track->IsComboType() )
  1497. {
  1498. pop->addSeparator();
  1499. if ( item->GetEditType() == 0 )
  1500. {
  1501. pop->add( "Edit <left/right>", IDC_TL_EDITLEFTRIGHT );
  1502. }
  1503. else
  1504. {
  1505. pop->add( "Edit <amount>", IDC_TL_EDITNORMAL );
  1506. }
  1507. }
  1508. pop->addSeparator();
  1509. mxPopupMenu *heightMenu = new mxPopupMenu();
  1510. heightMenu->add( va( "Reset '%s'", track->GetFlexControllerName() ) , IDC_ET_RESET_ITEM_SIZE );
  1511. heightMenu->add( "Reset All", IDC_ET_RESET_ALL_ITEM_SIZES );
  1512. pop->addMenu( "Height", heightMenu );
  1513. pop->addSeparator();
  1514. pop->add( "Edge Properties...", IDC_ET_EDGEPROPERTIES );
  1515. }
  1516. pop->addSeparator();
  1517. mxPopupMenu *tagmenu = new mxPopupMenu();
  1518. CFlexTimingTag *tag = IsMouseOverTag( (short)event->x, (short)event->y );
  1519. if ( tag )
  1520. {
  1521. if ( tag->GetLocked() )
  1522. {
  1523. tagmenu->add( va( "Unlock tag '%s'...", tag->GetName() ), IDC_UNLOCK_TIMING_TAG );
  1524. }
  1525. else
  1526. {
  1527. tagmenu->add( va( "Lock tag '%s'...", tag->GetName() ), IDC_LOCK_TIMING_TAG );
  1528. }
  1529. tagmenu->addSeparator();
  1530. tagmenu->add( va( "Delete tag '%s'...", tag->GetName() ), IDC_DELETE_TIMING_TAG );
  1531. }
  1532. else
  1533. {
  1534. tagmenu->add( "Insert...", IDC_INSERT_TIMING_TAG );
  1535. }
  1536. bool bMouseOverSelection = IsMouseOverSelection( (short)event->x, (short)event->y );
  1537. if ( bMouseOverSelection || HasCopiedColumn() )
  1538. {
  1539. mxPopupMenu *selectionMenu = new mxPopupMenu();
  1540. if ( bMouseOverSelection )
  1541. {
  1542. selectionMenu->add( "Copy samples", IDC_ET_SELECTION_COPY );
  1543. }
  1544. if ( HasCopiedColumn() )
  1545. {
  1546. selectionMenu->add( "Paste samples", IDC_ET_SELECTION_PASTE );
  1547. }
  1548. if ( bMouseOverSelection )
  1549. {
  1550. selectionMenu->addSeparator();
  1551. selectionMenu->add( "Delete samples", IDC_ET_SELECTION_DELETE );
  1552. selectionMenu->add( "Delete samples and shift remainder", IDC_ET_SELECTION_EXCISE );
  1553. }
  1554. pop->addMenu( "Column", selectionMenu );
  1555. }
  1556. pop->addMenu( "Timing Tags", tagmenu );
  1557. if ( FacePoser_IsSnapping() )
  1558. {
  1559. pop->addSeparator();
  1560. pop->add( "Delete keys by frame", IDC_TL_DELETECOLUMN );
  1561. pop->addSeparator();
  1562. }
  1563. mxPopupMenu *flexmenu = new mxPopupMenu();
  1564. flexmenu->add( "Copy to sliders", IDC_COPY_TO_FLEX );
  1565. flexmenu->add( "Copy from sliders", IDC_COPY_FROM_FLEX );
  1566. pop->addMenu( "Flex", flexmenu );
  1567. pop->add( "Create expression...", IDC_NEW_EXPRESSION_FROM_FLEXANIMATION );
  1568. CChoreoEvent *e = GetSafeEvent();
  1569. if ( e )
  1570. {
  1571. mxPopupMenu *importexport = new mxPopupMenu();
  1572. importexport->add( "Export flex animation...", IDC_EXPORT_FA );
  1573. importexport->add( "Import flex animation...", IDC_IMPORT_FA );
  1574. pop->addMenu( "Import/Export", importexport );
  1575. }
  1576. pop->add( va( "Change scale..." ), IDC_FLEX_CHANGESCALE );
  1577. mxPopupMenu *sortmenu = new mxPopupMenu();
  1578. sortmenu->add( "Sort by name", IDC_ET_SORT_BY_NAME );
  1579. sortmenu->add( "Sort by used", IDC_ET_SORT_BY_USED );
  1580. pop->addMenu( "Sort", sortmenu );
  1581. pop->popup( this, (short)event->x, (short)event->y );
  1582. }
  1583. //-----------------------------------------------------------------------------
  1584. // Purpose:
  1585. //-----------------------------------------------------------------------------
  1586. void ExpressionTool::DrawFocusRect( void )
  1587. {
  1588. HDC dc = GetDC( NULL );
  1589. for ( int i = 0; i < m_FocusRects.Size(); i++ )
  1590. {
  1591. RECT rc = m_FocusRects[ i ].m_rcFocus;
  1592. ::DrawFocusRect( dc, &rc );
  1593. }
  1594. ReleaseDC( NULL, dc );
  1595. }
  1596. void ExpressionTool::SetClickedPos( int x, int y )
  1597. {
  1598. m_nClickedX = x;
  1599. m_nClickedY = y;
  1600. }
  1601. float ExpressionTool::GetTimeForClickedPos( void )
  1602. {
  1603. CChoreoEvent *e = GetSafeEvent();
  1604. if ( !e )
  1605. return 0.0f;
  1606. float t = GetTimeValueForMouse( m_nClickedX );
  1607. // Get spline intensity for controller
  1608. float faketime = e->GetStartTime() + t;
  1609. return faketime;
  1610. }
  1611. //-----------------------------------------------------------------------------
  1612. // Purpose:
  1613. // Input : dragtype -
  1614. // startx -
  1615. // cursor -
  1616. //-----------------------------------------------------------------------------
  1617. void ExpressionTool::StartDragging( int dragtype, int startx, int starty, HCURSOR cursor )
  1618. {
  1619. m_nDragType = dragtype;
  1620. m_nStartX = startx;
  1621. m_nLastX = startx;
  1622. m_nStartY = starty;
  1623. m_nLastY = starty;
  1624. if ( m_hPrevCursor )
  1625. {
  1626. SetCursor( m_hPrevCursor );
  1627. m_hPrevCursor = NULL;
  1628. }
  1629. m_hPrevCursor = SetCursor( cursor );
  1630. m_FocusRects.Purge();
  1631. RECT rc;
  1632. GetWorkspaceRect( rc );
  1633. RECT rcStart;
  1634. rcStart.left = startx;
  1635. rcStart.right = startx;
  1636. bool addrect = true;
  1637. switch ( dragtype )
  1638. {
  1639. default:
  1640. case DRAGTYPE_SCRUBBER:
  1641. {
  1642. RECT rcScrub;
  1643. GetScrubHandleRect( rcScrub, true );
  1644. rcStart = rcScrub;
  1645. rcStart.left = ( rcScrub.left + rcScrub.right ) / 2;
  1646. rcStart.right = rcStart.left;
  1647. rcStart.top = rcScrub.bottom;
  1648. rcStart.bottom = h2();
  1649. }
  1650. break;
  1651. case DRAGTYPE_FLEXTIMINGTAG:
  1652. {
  1653. rcStart.top = rc.top;
  1654. rcStart.bottom = h2();
  1655. }
  1656. break;
  1657. case DRAGTYPE_SELECTSAMPLES:
  1658. {
  1659. float st = GetTimeValueForMouse( startx );
  1660. rcStart.left = GetPixelForTimeValue( st );
  1661. rcStart.right = rcStart.left;
  1662. m_nStartX = rcStart.left;
  1663. m_nLastX = rcStart.left;
  1664. }
  1665. case DRAGTYPE_MOVESELECTIONSTART:
  1666. case DRAGTYPE_MOVESELECTIONEND:
  1667. {
  1668. rcStart.top = rc.top;
  1669. rcStart.bottom = rc.bottom;
  1670. }
  1671. break;
  1672. case DRAGTYPE_MOVESELECTION:
  1673. {
  1674. rcStart.top = rc.top;
  1675. rcStart.bottom = rc.bottom;
  1676. // Compute left/right pixels for selection
  1677. rcStart.left = GetPixelForTimeValue( m_flSelection[ 0 ] );
  1678. rcStart.right = GetPixelForTimeValue( m_flSelection[ 1 ] );
  1679. }
  1680. break;
  1681. }
  1682. if ( addrect )
  1683. {
  1684. AddFocusRect( rcStart );
  1685. }
  1686. DrawFocusRect();
  1687. }
  1688. void ExpressionTool::OnMouseMove( mxEvent *event )
  1689. {
  1690. int mx = (short)event->x;
  1691. int my = (short)event->y;
  1692. event->x = (short)mx;
  1693. if ( m_nDragType != DRAGTYPE_NONE )
  1694. {
  1695. DrawFocusRect();
  1696. for ( int i = 0; i < m_FocusRects.Size(); i++ )
  1697. {
  1698. CFocusRect *f = &m_FocusRects[ i ];
  1699. f->m_rcFocus = f->m_rcOrig;
  1700. switch ( m_nDragType )
  1701. {
  1702. default:
  1703. {
  1704. OffsetRect( &f->m_rcFocus, ( (short)event->x - m_nStartX ), 0 );
  1705. }
  1706. break;
  1707. case DRAGTYPE_SELECTSAMPLES:
  1708. {
  1709. float st = GetTimeValueForMouse( mx );
  1710. int snapx = GetPixelForTimeValue( st );
  1711. f->m_rcFocus.left = min( snapx, m_nStartX );
  1712. f->m_rcFocus.right = max( snapx, m_nStartY );
  1713. POINT offset;
  1714. offset.x = 0;
  1715. offset.y = 0;
  1716. ClientToScreen( (HWND)getHandle(), &offset );
  1717. OffsetRect( &f->m_rcFocus, offset.x, 0 );
  1718. }
  1719. break;
  1720. }
  1721. }
  1722. DrawFocusRect();
  1723. }
  1724. else
  1725. {
  1726. if ( m_hPrevCursor )
  1727. {
  1728. SetCursor( m_hPrevCursor );
  1729. m_hPrevCursor = NULL;
  1730. }
  1731. if ( IsMouseOverScrubHandle( event ) )
  1732. {
  1733. m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) );
  1734. }
  1735. else if ( IsMouseOverTag( mx, my ) )
  1736. {
  1737. m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) );
  1738. }
  1739. else if ( IsMouseOverSelection( (short)event->x, (short)event->y ) )
  1740. {
  1741. if ( IsMouseOverSelectionStartEdge( event ) )
  1742. {
  1743. m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) );
  1744. }
  1745. else if ( IsMouseOverSelectionEndEdge( event ) )
  1746. {
  1747. m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) );
  1748. }
  1749. else
  1750. {
  1751. if ( event->modifiers & mxEvent::KeyShift )
  1752. {
  1753. m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEALL ) );
  1754. }
  1755. }
  1756. }
  1757. }
  1758. switch ( m_nDragType )
  1759. {
  1760. default:
  1761. break;
  1762. case DRAGTYPE_FLEXTIMINGTAG:
  1763. {
  1764. ApplyBounds( mx, my );
  1765. }
  1766. break;
  1767. case DRAGTYPE_SCRUBBER:
  1768. {
  1769. ApplyBounds( mx, my );
  1770. if ( w2() > 0 )
  1771. {
  1772. float t = GetTimeValueForMouse( mx );
  1773. t += m_flScrubberTimeOffset;
  1774. ForceScrubPosition( t );
  1775. }
  1776. }
  1777. break;
  1778. }
  1779. m_nLastX = (short)event->x;
  1780. m_nLastY = (short)event->y;
  1781. }
  1782. int ExpressionTool::handleEvent( mxEvent *event )
  1783. {
  1784. MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
  1785. int iret = 0;
  1786. if ( HandleToolEvent( event ) )
  1787. {
  1788. return iret;
  1789. }
  1790. switch ( event->event )
  1791. {
  1792. case mxEvent::Size:
  1793. {
  1794. int w, h;
  1795. w = event->width;
  1796. h = event->height;
  1797. m_pWorkspace->setBounds( 5, TRAY_HEIGHT + GetCaptionHeight(), w - 10, h - ( TRAY_HEIGHT + 5 + GetCaptionHeight() ) - m_nScrollbarHeight );
  1798. m_nLastHPixelsNeeded = 0;
  1799. InvalidateLayout();
  1800. iret = 1;
  1801. }
  1802. break;
  1803. case mxEvent::MouseWheeled:
  1804. {
  1805. CChoreoScene *scene = g_pChoreoView->GetScene();
  1806. if ( scene )
  1807. {
  1808. int tz = g_pChoreoView->GetTimeZoom( GetToolName() );
  1809. bool shiftdown = ( event->modifiers & mxEvent::KeyShift ) ? true : false;
  1810. int stepMultipiler = shiftdown ? 5 : 1;
  1811. // Zoom time in / out
  1812. if ( event->height > 0 )
  1813. {
  1814. tz = min( tz + TIME_ZOOM_STEP * stepMultipiler, MAX_TIME_ZOOM );
  1815. }
  1816. else
  1817. {
  1818. tz = max( tz - TIME_ZOOM_STEP * stepMultipiler, TIME_ZOOM_STEP );
  1819. }
  1820. g_pChoreoView->SetPreservedTimeZoom( this, tz );
  1821. }
  1822. //RepositionHSlider();
  1823. m_pWorkspace->redraw();
  1824. redraw();
  1825. iret = 1;
  1826. }
  1827. break;
  1828. case mxEvent::MouseDown:
  1829. {
  1830. // bool ctrldown = ( event->modifiers & mxEvent::KeyCtrl ) ? true : false;
  1831. bool shiftdown = ( event->modifiers & mxEvent::KeyShift ) ? true : false;
  1832. iret = 1;
  1833. int mx = (short)event->x;
  1834. int my = (short)event->y;
  1835. SetClickedPos( mx, my );
  1836. SetMouseOverPos( mx, my );
  1837. DrawMouseOverPos();
  1838. if ( event->buttons & mxEvent::MouseRightButton )
  1839. {
  1840. ShowContextMenu( event, false );
  1841. return iret;
  1842. }
  1843. if ( m_nDragType == DRAGTYPE_NONE )
  1844. {
  1845. if ( IsMouseOverScrubHandle( event ) )
  1846. {
  1847. if ( w2() > 0 )
  1848. {
  1849. float t = GetTimeValueForMouse( (short)event->x );
  1850. m_flScrubberTimeOffset = m_flScrub - t;
  1851. float maxoffset = 0.5f * (float)SCRUBBER_HANDLE_WIDTH / GetPixelsPerSecond();
  1852. m_flScrubberTimeOffset = clamp( m_flScrubberTimeOffset, -maxoffset, maxoffset );
  1853. t += m_flScrubberTimeOffset;
  1854. ForceScrubPosition( t );
  1855. }
  1856. StartDragging( DRAGTYPE_SCRUBBER, m_nClickedX, m_nClickedY, LoadCursor( NULL, IDC_SIZEWE ) );
  1857. }
  1858. else if ( IsMouseOverTag( m_nClickedX, m_nClickedY ) )
  1859. {
  1860. StartDragging( DRAGTYPE_FLEXTIMINGTAG, m_nClickedX, m_nClickedY, LoadCursor( NULL, IDC_SIZEWE ) );
  1861. }
  1862. else if ( IsMouseOverPoints( m_nClickedX, m_nClickedY ) )
  1863. {
  1864. if ( !m_bSelectionActive )
  1865. {
  1866. StartDragging( DRAGTYPE_SELECTSAMPLES, m_nClickedX, m_nClickedY, LoadCursor( NULL, IDC_SIZEWE ) );
  1867. }
  1868. else
  1869. {
  1870. // Either move, move edge if ctrl key is held, or deselect
  1871. if ( IsMouseOverSelection( m_nClickedX,m_nClickedY ) )
  1872. {
  1873. if ( IsMouseOverSelectionStartEdge( event ) )
  1874. {
  1875. StartDragging( DRAGTYPE_MOVESELECTIONSTART, m_nClickedX, m_nClickedY, LoadCursor( NULL, IDC_SIZEWE ) );
  1876. }
  1877. else if ( IsMouseOverSelectionEndEdge( event ) )
  1878. {
  1879. StartDragging( DRAGTYPE_MOVESELECTIONEND, m_nClickedX, m_nClickedY, LoadCursor( NULL, IDC_SIZEWE ) );
  1880. }
  1881. else
  1882. {
  1883. if ( shiftdown )
  1884. {
  1885. StartDragging( DRAGTYPE_MOVESELECTION, m_nClickedX, m_nClickedY, LoadCursor( NULL, IDC_SIZEALL ) );
  1886. }
  1887. }
  1888. }
  1889. else
  1890. {
  1891. m_bSelectionActive = false;
  1892. redraw();
  1893. return iret;
  1894. }
  1895. }
  1896. }
  1897. else
  1898. {
  1899. if ( w2() > 0 )
  1900. {
  1901. float t = GetTimeValueForMouse( (short)event->x );
  1902. SetScrubTargetTime( t );
  1903. }
  1904. }
  1905. CalcBounds( m_nDragType );
  1906. }
  1907. }
  1908. break;
  1909. case mxEvent::MouseDrag:
  1910. case mxEvent::MouseMove:
  1911. {
  1912. int mx = (short)event->x;
  1913. int my = (short)event->y;
  1914. SetMouseOverPos( mx, my );
  1915. DrawMouseOverPos();
  1916. OnMouseMove( event );
  1917. iret = 1;
  1918. }
  1919. break;
  1920. case mxEvent::MouseUp:
  1921. {
  1922. if ( event->buttons & mxEvent::MouseRightButton )
  1923. {
  1924. return 1;
  1925. }
  1926. int mx = (short)event->x;
  1927. int my = (short)event->y;
  1928. if ( m_nDragType != DRAGTYPE_NONE )
  1929. {
  1930. DrawFocusRect();
  1931. }
  1932. if ( m_hPrevCursor )
  1933. {
  1934. SetCursor( m_hPrevCursor );
  1935. m_hPrevCursor = 0;
  1936. }
  1937. switch ( m_nDragType )
  1938. {
  1939. case DRAGTYPE_NONE:
  1940. break;
  1941. case DRAGTYPE_SELECTSAMPLES:
  1942. FinishSelect( m_nStartX, mx );
  1943. break;
  1944. case DRAGTYPE_MOVESELECTION:
  1945. FinishMoveSelection( m_nStartX, mx );
  1946. break;
  1947. case DRAGTYPE_MOVESELECTIONSTART:
  1948. FinishMoveSelectionStart( m_nStartX, mx );
  1949. break;
  1950. case DRAGTYPE_MOVESELECTIONEND:
  1951. FinishMoveSelectionEnd( m_nStartX, mx );
  1952. break;
  1953. case DRAGTYPE_SCRUBBER:
  1954. {
  1955. ApplyBounds( mx, my );
  1956. // int dx = mx - m_nStartX;
  1957. // int dy = my = m_nStartY;
  1958. if ( w2() > 0 )
  1959. {
  1960. float t = GetTimeValueForMouse( (short)event->x );
  1961. t += m_flScrubberTimeOffset;
  1962. m_flScrubberTimeOffset = 0.0f;
  1963. ForceScrubPosition( t );
  1964. }
  1965. }
  1966. break;
  1967. case DRAGTYPE_FLEXTIMINGTAG:
  1968. {
  1969. ApplyBounds( mx, my );
  1970. // int dx = mx - m_nStartX;
  1971. // int dy = my = m_nStartY;
  1972. // Compute dx, dy and apply to sections
  1973. //Con_Printf( "dx == %i\n", dx );
  1974. CFlexTimingTag *tag = IsMouseOverTag( m_nStartX, m_nStartY );
  1975. CChoreoEvent *ev = GetSafeEvent();
  1976. if ( tag && g_pChoreoView && ev && ev->GetDuration() )
  1977. {
  1978. float t = GetTimeValueForMouse( mx );
  1979. float percent = t / ev->GetDuration();
  1980. g_pChoreoView->SetDirty( true );
  1981. g_pChoreoView->PushUndo( "Move Timing Tag" );
  1982. if ( tag->GetLocked() )
  1983. {
  1984. // Resample all control points on right/left
  1985. // of locked tags all the way to the next lock or edge
  1986. ResampleControlPoints( tag, percent );
  1987. }
  1988. tag->SetPercentage( percent );
  1989. g_pChoreoView->PushRedo( "Move Timing Tag" );
  1990. }
  1991. LayoutItems( true );
  1992. redraw();
  1993. }
  1994. break;
  1995. }
  1996. m_nDragType = DRAGTYPE_NONE;
  1997. SetMouseOverPos( mx, my );
  1998. DrawMouseOverPos();
  1999. iret = 1;
  2000. }
  2001. break;
  2002. case mxEvent::Action:
  2003. {
  2004. iret = 1;
  2005. switch ( event->action )
  2006. {
  2007. default:
  2008. iret = 0;
  2009. break;
  2010. case IDC_ET_RESET_ITEM_SIZE:
  2011. OnResetItemSize();
  2012. break;
  2013. case IDC_ET_RESET_ALL_ITEM_SIZES:
  2014. OnResetAllItemSizes();
  2015. break;
  2016. case IDC_ET_SELECTION_DELETE:
  2017. OnDeleteSelection( false );
  2018. break;
  2019. case IDC_ET_SELECTION_EXCISE:
  2020. OnDeleteSelection( true );
  2021. break;
  2022. case IDC_ET_SELECTION_COPY:
  2023. OnCopyColumn();
  2024. break;
  2025. case IDC_ET_SELECTION_PASTE:
  2026. OnPasteColumn();
  2027. break;
  2028. case IDC_ET_SORT_BY_USED:
  2029. OnSortByUsed();
  2030. break;
  2031. case IDC_ET_SORT_BY_NAME:
  2032. OnSortByName();
  2033. break;
  2034. case IDC_EXPORT_FA:
  2035. OnExportFlexAnimation();
  2036. break;
  2037. case IDC_IMPORT_FA:
  2038. OnImportFlexAnimation();
  2039. break;
  2040. case IDC_LOCK_TIMING_TAG:
  2041. LockTimingTag();
  2042. break;
  2043. case IDC_UNLOCK_TIMING_TAG:
  2044. UnlockTimingTag();
  2045. break;
  2046. case IDC_DELETE_TIMING_TAG:
  2047. DeleteFlexTimingTag( m_nClickedX, m_nClickedY );
  2048. break;
  2049. case IDC_INSERT_TIMING_TAG:
  2050. AddFlexTimingTag( m_nClickedX );
  2051. break;
  2052. case IDC_EXPANDALL:
  2053. m_pWorkspace->ExpandAll();
  2054. break;
  2055. case IDC_COLLAPSEALL:
  2056. m_pWorkspace->CollapseAll( NULL );
  2057. break;
  2058. case IDC_COLLAPSE_ALL_EXCEPT:
  2059. m_pWorkspace->CollapseAll( m_pWorkspace->GetClickedItem() );
  2060. break;
  2061. case IDC_EXPANDVALID:
  2062. m_pWorkspace->ExpandValid();
  2063. break;
  2064. case IDC_COPY_TO_FLEX:
  2065. OnCopyToFlex( true );
  2066. break;
  2067. case IDC_COPY_FROM_FLEX:
  2068. OnCopyFromFlex( false );
  2069. break;
  2070. case IDC_NEW_EXPRESSION_FROM_FLEXANIMATION:
  2071. OnNewExpression();
  2072. break;
  2073. case IDC_UNDO_FA:
  2074. OnUndo();
  2075. break;
  2076. case IDC_REDO_FA:
  2077. OnRedo();
  2078. break;
  2079. case IDC_TL_EDITNORMAL:
  2080. {
  2081. TimelineItem *item = m_pWorkspace->GetClickedItem();
  2082. if ( item )
  2083. {
  2084. item->SetEditType( 0 );
  2085. item->DrawSelf();
  2086. }
  2087. }
  2088. break;
  2089. case IDC_TL_EDITLEFTRIGHT:
  2090. {
  2091. TimelineItem *item = m_pWorkspace->GetClickedItem();
  2092. if ( item )
  2093. {
  2094. item->SetEditType( 1 );
  2095. item->DrawSelf();
  2096. }
  2097. }
  2098. break;
  2099. case IDC_TL_EXPAND:
  2100. {
  2101. TimelineItem *item = m_pWorkspace->GetClickedItem();
  2102. if ( item )
  2103. {
  2104. item->SetCollapsed( false );
  2105. }
  2106. LayoutItems();
  2107. }
  2108. break;
  2109. case IDC_TL_COLLAPSE:
  2110. {
  2111. TimelineItem *item = m_pWorkspace->GetClickedItem();
  2112. if ( item )
  2113. {
  2114. item->SetCollapsed( true );
  2115. }
  2116. LayoutItems();
  2117. }
  2118. break;
  2119. case IDC_TL_ENABLE:
  2120. {
  2121. TimelineItem *item = m_pWorkspace->GetClickedItem();
  2122. if ( item )
  2123. {
  2124. g_pChoreoView->SetDirty( true );
  2125. g_pChoreoView->PushUndo( "Enable item" );
  2126. item->SetActive( true );
  2127. g_pChoreoView->PushRedo( "Enable item" );
  2128. item->DrawSelf();
  2129. }
  2130. }
  2131. break;
  2132. case IDC_TL_DISABLE:
  2133. {
  2134. TimelineItem *item = m_pWorkspace->GetClickedItem();
  2135. if ( item )
  2136. {
  2137. g_pChoreoView->SetDirty( true );
  2138. g_pChoreoView->PushUndo( "Disable item" );
  2139. item->SetActive( false );
  2140. g_pChoreoView->PushRedo( "Disable item" );
  2141. item->DrawSelf();
  2142. }
  2143. }
  2144. break;
  2145. case IDC_TL_COPY:
  2146. {
  2147. TimelineItem *item = m_pWorkspace->GetClickedItem();
  2148. if ( item )
  2149. {
  2150. item->Copy();
  2151. }
  2152. }
  2153. break;
  2154. case IDC_TL_PASTE:
  2155. {
  2156. TimelineItem *item = m_pWorkspace->GetClickedItem();
  2157. if ( item )
  2158. {
  2159. item->Paste();
  2160. item->DrawSelf();
  2161. }
  2162. }
  2163. break;
  2164. case IDC_TL_DELETE:
  2165. {
  2166. TimelineItem *item = m_pWorkspace->GetClickedItem();
  2167. if ( item )
  2168. {
  2169. item->Delete();
  2170. item->DrawSelf();
  2171. }
  2172. }
  2173. break;
  2174. case IDC_TL_DESELECT:
  2175. {
  2176. TimelineItem *item = m_pWorkspace->GetClickedItem();
  2177. if ( item )
  2178. {
  2179. item->DeselectAll();
  2180. item->DrawSelf();
  2181. }
  2182. }
  2183. break;
  2184. case IDC_TL_SELECTALL:
  2185. {
  2186. TimelineItem *item = m_pWorkspace->GetClickedItem();
  2187. if ( item )
  2188. {
  2189. item->SelectAll();
  2190. item->DrawSelf();
  2191. }
  2192. }
  2193. break;
  2194. case IDC_DISABLE_ALL_EXCEPT:
  2195. {
  2196. m_pWorkspace->DisableAllExcept();
  2197. }
  2198. break;
  2199. case IDC_ENABLE_ALL_VALID:
  2200. {
  2201. m_pWorkspace->EnableValid();
  2202. }
  2203. break;
  2204. case IDC_TL_SNAPSELECTED:
  2205. {
  2206. TimelineItem *item = m_pWorkspace->GetClickedItem();
  2207. if ( item )
  2208. {
  2209. g_pChoreoView->SetDirty( true );
  2210. g_pChoreoView->PushUndo( "Snap Selected" );
  2211. item->SnapSelected();
  2212. g_pChoreoView->PushRedo( "Snap Selected" );
  2213. item->DrawSelf();
  2214. }
  2215. }
  2216. break;
  2217. case IDC_TL_SNAPPOINTS:
  2218. {
  2219. TimelineItem *item = m_pWorkspace->GetClickedItem();
  2220. if ( item )
  2221. {
  2222. g_pChoreoView->SetDirty( true );
  2223. g_pChoreoView->PushUndo( "Snap Item" );
  2224. item->SnapAll();
  2225. g_pChoreoView->PushRedo( "Snap Item" );
  2226. item->DrawSelf();
  2227. }
  2228. }
  2229. break;
  2230. case IDC_TL_DELETECOLUMN:
  2231. {
  2232. m_pWorkspace->OnDeleteColumn();
  2233. }
  2234. break;
  2235. case IDC_TL_SNAPALL:
  2236. {
  2237. m_pWorkspace->OnSnapAll();
  2238. }
  2239. break;
  2240. case IDC_FLEXHSCROLL:
  2241. {
  2242. int offset = 0;
  2243. bool processed = true;
  2244. switch ( event->modifiers )
  2245. {
  2246. case SB_THUMBTRACK:
  2247. offset = event->height;
  2248. break;
  2249. case SB_PAGEUP:
  2250. offset = m_pHorzScrollBar->getValue();
  2251. offset -= 20;
  2252. offset = max( offset, m_pHorzScrollBar->getMinValue() );
  2253. break;
  2254. case SB_PAGEDOWN:
  2255. offset = m_pHorzScrollBar->getValue();
  2256. offset += 20;
  2257. offset = min( offset, m_pHorzScrollBar->getMaxValue() );
  2258. break;
  2259. case SB_LINEUP:
  2260. offset = m_pHorzScrollBar->getValue();
  2261. offset -= 10;
  2262. offset = max( offset, m_pHorzScrollBar->getMinValue() );
  2263. break;
  2264. case SB_LINEDOWN:
  2265. offset = m_pHorzScrollBar->getValue();
  2266. offset += 10;
  2267. offset = min( offset, m_pHorzScrollBar->getMaxValue() );
  2268. break;
  2269. default:
  2270. processed = false;
  2271. break;
  2272. }
  2273. if ( processed )
  2274. {
  2275. MoveTimeSliderToPos( offset );
  2276. }
  2277. }
  2278. break;
  2279. case IDC_FLEX_CHANGESCALE:
  2280. {
  2281. OnChangeScale();
  2282. }
  2283. break;
  2284. case IDC_FLEX_SCALESAMPLES:
  2285. {
  2286. OnScaleSamples();
  2287. }
  2288. break;
  2289. case IDC_ET_EDGEPROPERTIES:
  2290. {
  2291. OnEdgeProperties();
  2292. }
  2293. break;
  2294. }
  2295. }
  2296. break;
  2297. case mxEvent::KeyDown:
  2298. case mxEvent::KeyUp:
  2299. {
  2300. TimelineItem *item = m_pWorkspace->GetClickedItem();
  2301. if ( item )
  2302. {
  2303. iret = item->handleEvent( event );
  2304. }
  2305. if ( !iret )
  2306. {
  2307. switch ( event->key )
  2308. {
  2309. default:
  2310. break;
  2311. case VK_ESCAPE:
  2312. {
  2313. DeselectAll();
  2314. iret = 1;
  2315. }
  2316. break;
  2317. }
  2318. }
  2319. }
  2320. break;
  2321. }
  2322. return iret;
  2323. }
  2324. //-----------------------------------------------------------------------------
  2325. // Purpose:
  2326. // Input : false -
  2327. //-----------------------------------------------------------------------------
  2328. void ExpressionTool::LayoutItems( bool force /*= false*/ )
  2329. {
  2330. m_pWorkspace->LayoutItems( force );
  2331. }
  2332. //-----------------------------------------------------------------------------
  2333. // Purpose:
  2334. // Input : *event -
  2335. //-----------------------------------------------------------------------------
  2336. void ExpressionTool::AddFlexTimingTag( int mx )
  2337. {
  2338. Assert( g_pChoreoView );
  2339. CChoreoEvent *event = GetSafeEvent();
  2340. if ( !event )
  2341. return;
  2342. if ( event->GetType() != CChoreoEvent::FLEXANIMATION )
  2343. {
  2344. Con_ErrorPrintf( "Timing Tag: Can only tag FLEXANIMATION events\n" );
  2345. return;
  2346. }
  2347. CInputParams params;
  2348. memset( &params, 0, sizeof( params ) );
  2349. strcpy( params.m_szDialogTitle, "Event Tag Name" );
  2350. strcpy( params.m_szPrompt, "Name:" );
  2351. strcpy( params.m_szInputText, "" );
  2352. if ( !InputProperties( &params ) )
  2353. return;
  2354. if ( strlen( params.m_szInputText ) <= 0 )
  2355. {
  2356. Con_ErrorPrintf( "Timing Tag Name: No name entered!\n" );
  2357. return;
  2358. }
  2359. // Convert click to frac
  2360. float t = GetTimeValueForMouse( mx );
  2361. float frac = 0.0f;
  2362. if ( event->GetDuration() )
  2363. {
  2364. frac = t / event->GetDuration();
  2365. frac = clamp( frac, 0.0f, 1.0f );
  2366. }
  2367. g_pChoreoView->SetDirty( true );
  2368. g_pChoreoView->PushUndo( "Add Timing Tag" );
  2369. event->AddTimingTag( params.m_szInputText, frac, true );
  2370. g_pChoreoView->PushRedo( "Add Timing Tag" );
  2371. // Redraw this window
  2372. m_pWorkspace->redraw();
  2373. redraw();
  2374. }
  2375. void ExpressionTool::DeleteFlexTimingTag( int mx, int my )
  2376. {
  2377. Assert( g_pChoreoView );
  2378. CChoreoEvent *event = GetSafeEvent();
  2379. if ( !event )
  2380. return;
  2381. CFlexTimingTag *tag = IsMouseOverTag( mx, my );
  2382. if ( !tag )
  2383. return;
  2384. g_pChoreoView->SetDirty( true );
  2385. g_pChoreoView->PushUndo( "Delete Timing Tag" );
  2386. event->RemoveTimingTag( tag->GetName() );
  2387. g_pChoreoView->PushRedo( "Delete Timing Tag" );
  2388. LayoutItems( true );
  2389. // Redraw this window
  2390. redraw();
  2391. }
  2392. void ExpressionTool::LockTimingTag( void )
  2393. {
  2394. Assert( g_pChoreoView );
  2395. CChoreoEvent *event = GetSafeEvent();
  2396. if ( !event )
  2397. return;
  2398. CFlexTimingTag *tag = IsMouseOverTag( m_nClickedX, m_nClickedY );
  2399. if ( !tag )
  2400. return;
  2401. if ( tag->GetLocked() )
  2402. return;
  2403. g_pChoreoView->SetDirty( true );
  2404. g_pChoreoView->PushUndo( "Lock Timing Tag" );
  2405. tag->SetLocked( true );
  2406. g_pChoreoView->PushRedo( "Lock Timing Tag" );
  2407. redraw();
  2408. }
  2409. void ExpressionTool::UnlockTimingTag( void )
  2410. {
  2411. Assert( g_pChoreoView );
  2412. CChoreoEvent *event = GetSafeEvent();
  2413. if ( !event )
  2414. return;
  2415. CFlexTimingTag *tag = IsMouseOverTag( m_nClickedX, m_nClickedY );
  2416. if ( !tag )
  2417. return;
  2418. if ( !tag->GetLocked() )
  2419. return;
  2420. g_pChoreoView->SetDirty( true );
  2421. g_pChoreoView->PushUndo( "Unlock Timing Tag" );
  2422. tag->SetLocked( false );
  2423. g_pChoreoView->PushRedo( "Unlock Timing Tag" );
  2424. redraw();
  2425. }
  2426. void ExpressionTool::ApplyBounds( int& mx, int& my )
  2427. {
  2428. if ( !m_bUseBounds )
  2429. return;
  2430. mx = clamp( mx, m_nMinX, m_nMaxX );
  2431. }
  2432. void ExpressionTool::CalcBounds( int movetype )
  2433. {
  2434. switch ( movetype )
  2435. {
  2436. default:
  2437. case DRAGTYPE_NONE:
  2438. m_bUseBounds = false;
  2439. m_nMinX = 0;
  2440. m_nMaxX = 0;
  2441. break;
  2442. case DRAGTYPE_SCRUBBER:
  2443. m_bUseBounds = true;
  2444. m_nMinX = 0;
  2445. m_nMaxX = w2();
  2446. break;
  2447. case DRAGTYPE_FLEXTIMINGTAG:
  2448. {
  2449. m_bUseBounds = true;
  2450. int left, right;
  2451. GetWorkspaceLeftRight( left, right );
  2452. m_nMinX = left;
  2453. m_nMaxX = right;
  2454. RECT rcClient;
  2455. rcClient.left = left;
  2456. rcClient.right = right;
  2457. rcClient.top = 0;
  2458. rcClient.bottom = TRAY_HEIGHT;
  2459. CFlexTimingTag *tag = IsMouseOverTag( m_nStartX, m_nStartY );
  2460. if ( tag &&
  2461. tag->GetOwner() )
  2462. {
  2463. CChoreoEvent *e = tag->GetOwner();
  2464. float st = e->GetStartTime();
  2465. float ed = e->GetEndTime();
  2466. if ( ed > st )
  2467. {
  2468. // Find previous tag, if any
  2469. CFlexTimingTag *prev = NULL;
  2470. CFlexTimingTag *next = NULL;
  2471. for ( int i = 0; i < e->GetNumTimingTags(); i++ )
  2472. {
  2473. CFlexTimingTag *test = e->GetTimingTag( i );
  2474. if ( test != tag )
  2475. continue;
  2476. // Found it
  2477. if ( i > 0 )
  2478. {
  2479. prev = e->GetTimingTag( i - 1 );
  2480. }
  2481. if ( i + 1 < e->GetNumTimingTags() )
  2482. {
  2483. next = e->GetTimingTag( i + 1 );
  2484. }
  2485. break;
  2486. }
  2487. if ( prev )
  2488. {
  2489. // Compute x pixel of prev tag
  2490. float frac = ( prev->GetStartTime() - st ) / ( ed - st );
  2491. if ( frac >= 0.0f && frac <= 1.0f )
  2492. {
  2493. int tagx = rcClient.left + (int)( frac * (float)( rcClient.right - rcClient.left ) );
  2494. m_nMinX = max( m_nMinX, tagx + 5 );
  2495. }
  2496. }
  2497. if ( next )
  2498. {
  2499. // Compute x pixel of next tag
  2500. float frac = ( next->GetStartTime() - st ) / ( ed - st );
  2501. if ( frac >= 0.0f && frac <= 1.0f )
  2502. {
  2503. int tagx = rcClient.left + (int)( frac * (float)( rcClient.right - rcClient.left ) );
  2504. m_nMaxX = min( m_nMaxX, tagx - 5 );
  2505. }
  2506. }
  2507. }
  2508. }
  2509. }
  2510. break;
  2511. }
  2512. }
  2513. //-----------------------------------------------------------------------------
  2514. // Purpose:
  2515. // Input : *tag -
  2516. // newposition -
  2517. //-----------------------------------------------------------------------------
  2518. void ExpressionTool::ResampleControlPoints( CFlexTimingTag *tag, float newposition )
  2519. {
  2520. CChoreoEvent *e = tag->GetOwner();
  2521. if ( !e )
  2522. return;
  2523. float duration = e->GetDuration();
  2524. float leftedge = 0.0f;
  2525. float rightedge = duration;
  2526. // Find neighboring locked tags, if any
  2527. CFlexTimingTag *prev = NULL;
  2528. CFlexTimingTag *next = NULL;
  2529. int i;
  2530. for ( i = 0; i < e->GetNumTimingTags(); i++ )
  2531. {
  2532. CFlexTimingTag *test = e->GetTimingTag( i );
  2533. if ( test != tag )
  2534. continue;
  2535. // Found it
  2536. if ( i > 0 )
  2537. {
  2538. int i1 = i - 1;
  2539. while ( 1 )
  2540. {
  2541. if ( i1 < 0 )
  2542. {
  2543. prev = NULL;
  2544. break;
  2545. }
  2546. prev = e->GetTimingTag( i1 );
  2547. if ( prev->GetLocked() )
  2548. break;
  2549. i1--;
  2550. }
  2551. }
  2552. if ( i + 1 < e->GetNumTimingTags() )
  2553. {
  2554. int i1 = i + 1;
  2555. while ( 1 )
  2556. {
  2557. if ( i1 >= e->GetNumTimingTags() )
  2558. {
  2559. next = NULL;
  2560. break;
  2561. }
  2562. next = e->GetTimingTag( i1 );
  2563. if ( next->GetLocked() )
  2564. break;
  2565. i1++;
  2566. }
  2567. }
  2568. break;
  2569. }
  2570. if ( prev )
  2571. {
  2572. leftedge = prev->GetPercentage() * duration;
  2573. }
  2574. if ( next )
  2575. {
  2576. rightedge = next->GetPercentage() * duration;
  2577. }
  2578. // Now, using the tags old position as a pivot, rescale intervening
  2579. // sample points based on size delta of new vs old range
  2580. float oldpivot = tag->GetPercentage() * duration;
  2581. float newpivot = newposition * duration;
  2582. float oldleftrange = oldpivot - leftedge;
  2583. float oldrightrange = rightedge - oldpivot;
  2584. float newleftrange = newpivot - leftedge;
  2585. float newrightrange = rightedge - newpivot;
  2586. if ( oldleftrange <= 0.0f ||
  2587. oldrightrange <= 0.0f ||
  2588. newleftrange <= 0.0f ||
  2589. newrightrange <= 0.0f )
  2590. {
  2591. Con_Printf( "Range problem!!! avoiding division by zero\n" );
  2592. return;
  2593. }
  2594. for ( i = 0 ; i < e->GetNumFlexAnimationTracks(); i++ )
  2595. {
  2596. CFlexAnimationTrack *track = e->GetFlexAnimationTrack( i );
  2597. if ( !track )
  2598. continue;
  2599. for ( int t = 0; t < ( track->IsComboType() ? 2 : 1 ); t++ )
  2600. {
  2601. for ( int j = 0; j < track->GetNumSamples( t ); j++ )
  2602. {
  2603. CExpressionSample *s = track->GetSample( j, t );
  2604. if ( !s )
  2605. continue;
  2606. float oldtime = s->time;
  2607. // In old range?
  2608. if ( oldtime < leftedge )
  2609. continue;
  2610. if ( oldtime > rightedge )
  2611. continue;
  2612. // In left or right side( tiebreak toward left )
  2613. float newtime = oldtime;
  2614. if ( oldtime <= oldpivot )
  2615. {
  2616. float n = ( oldtime - leftedge ) / oldleftrange;
  2617. newtime = leftedge + n * newleftrange;
  2618. }
  2619. else
  2620. {
  2621. float n = ( oldtime - oldpivot ) / oldrightrange;
  2622. newtime = newpivot + n * newrightrange;
  2623. }
  2624. //newtime = FacePoser_SnapTime( newtime );
  2625. s->time = newtime;
  2626. }
  2627. }
  2628. }
  2629. }
  2630. //-----------------------------------------------------------------------------
  2631. // Purpose:
  2632. //-----------------------------------------------------------------------------
  2633. void ExpressionTool::OnNewExpression( void )
  2634. {
  2635. CChoreoEvent *e = GetSafeEvent();
  2636. if ( !e )
  2637. return;
  2638. CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();
  2639. if ( !hdr )
  2640. {
  2641. Con_ErrorPrintf( "ExpressionTool::OnNewExpression: Can't create new face pose, must load a model first!\n" );
  2642. return;
  2643. }
  2644. CExpClass *active = expressions->GetActiveClass();
  2645. if ( !active )
  2646. {
  2647. Con_ErrorPrintf( "ExpressionTool::OnNewExpression: Can't create new face pose, must load an expression file first!\n" );
  2648. return;
  2649. }
  2650. g_pExpressionTrayTool->Deselect();
  2651. float t = GetTimeValueForMouse( m_nClickedX );
  2652. // Get spline intensity for controller
  2653. float faketime = e->GetStartTime() + t;
  2654. float settings[ GLOBAL_STUDIO_FLEX_CONTROL_COUNT ];
  2655. float weights[ GLOBAL_STUDIO_FLEX_CONTROL_COUNT ];
  2656. memset( settings, 0, sizeof( settings ) );
  2657. memset( weights, 0, sizeof( settings ) );
  2658. for ( int i = 0 ; i < e->GetNumFlexAnimationTracks(); i++ )
  2659. {
  2660. CFlexAnimationTrack *track = e->GetFlexAnimationTrack( i );
  2661. if ( !track )
  2662. continue;
  2663. // Disabled
  2664. if ( !track->IsTrackActive() )
  2665. continue;
  2666. // Map track flex controller to global name
  2667. if ( track->IsComboType() )
  2668. {
  2669. for ( int side = 0; side < 2; side++ )
  2670. {
  2671. int controller = track->GetFlexControllerIndex( side );
  2672. if ( controller != -1 )
  2673. {
  2674. // Get spline intensity for controller
  2675. float flIntensity = track->GetIntensity( faketime, side );
  2676. settings[ controller ] = flIntensity;
  2677. weights[ controller ] = 1.0f;
  2678. }
  2679. }
  2680. }
  2681. else
  2682. {
  2683. int controller = track->GetFlexControllerIndex( 0 );
  2684. if ( controller != -1 )
  2685. {
  2686. // Get spline intensity for controller
  2687. float flIntensity = track->GetIntensity( faketime, 0 );
  2688. settings[ controller ] = flIntensity;
  2689. weights[ controller ] = 1.0f;
  2690. }
  2691. }
  2692. }
  2693. CExpressionParams params;
  2694. memset( &params, 0, sizeof( params ) );
  2695. strcpy( params.m_szDialogTitle, "Add Expression" );
  2696. strcpy( params.m_szName, "" );
  2697. strcpy( params.m_szDescription, "" );
  2698. if ( !ExpressionProperties( &params ) )
  2699. return;
  2700. if ( ( strlen( params.m_szName ) <= 0 ) ||
  2701. !stricmp( params.m_szName, "unnamed" ) )
  2702. {
  2703. Con_ErrorPrintf( "You must type in a valid name\n" );
  2704. return;
  2705. }
  2706. if ( ( strlen( params.m_szDescription ) <= 0 ) ||
  2707. !stricmp( params.m_szDescription, "description" ) )
  2708. {
  2709. Con_ErrorPrintf( "You must type in a valid description\n" );
  2710. return;
  2711. }
  2712. active->AddExpression( params.m_szName, params.m_szDescription, settings, weights, true, true );
  2713. }
  2714. LocalFlexController_t FindFlexControllerIndexByName( StudioModel *model, char const *searchname )
  2715. {
  2716. if ( !model )
  2717. return LocalFlexController_t(-1);
  2718. CStudioHdr *hdr = model->GetStudioHdr();
  2719. if ( !hdr )
  2720. return LocalFlexController_t(-1);
  2721. for ( LocalFlexController_t i = LocalFlexController_t(0); i < hdr->numflexcontrollers(); i++ )
  2722. {
  2723. char const *name = hdr->pFlexcontroller( i )->pszName();
  2724. if ( !name )
  2725. continue;
  2726. if ( strcmp( name, searchname ) )
  2727. continue;
  2728. return i;
  2729. }
  2730. return LocalFlexController_t(-1);
  2731. }
  2732. void ExpressionTool::OnCopyToFlex( bool isEdited )
  2733. {
  2734. // local time in the expression tool for the last mouse click
  2735. float t = GetTimeValueForMouse( m_nClickedX );
  2736. CChoreoEvent *e = GetSafeEvent();
  2737. if ( !e )
  2738. return;
  2739. float scenetime = e->GetStartTime() + t;
  2740. OnCopyToFlex( scenetime, isEdited );
  2741. return;
  2742. }
  2743. void ExpressionTool::OnCopyToFlex( float scenetime, bool isEdited )
  2744. {
  2745. CChoreoEvent *e = GetSafeEvent();
  2746. if ( !e )
  2747. return;
  2748. if ( scenetime < e->GetStartTime() || scenetime > e->GetEndTime() )
  2749. return;
  2750. bool needundo = false;
  2751. float *settings = NULL;
  2752. float *weights = NULL;
  2753. CExpression *exp = NULL;
  2754. CExpClass *active = expressions->GetActiveClass();
  2755. if ( active )
  2756. {
  2757. int index = active->GetSelectedExpression();
  2758. if ( index != -1 )
  2759. {
  2760. exp = active->GetExpression( index );
  2761. if ( exp )
  2762. {
  2763. needundo = true;
  2764. settings = exp->GetSettings();
  2765. weights = exp->GetWeights();
  2766. }
  2767. }
  2768. }
  2769. if ( needundo && exp )
  2770. {
  2771. exp->PushUndoInformation();
  2772. active->SetDirty( true );
  2773. }
  2774. g_pFlexPanel->ResetSliders( false, true );
  2775. StudioModel *model = models->GetActiveStudioModel();
  2776. for ( int i = 0 ; i < e->GetNumFlexAnimationTracks(); i++ )
  2777. {
  2778. CFlexAnimationTrack *track = e->GetFlexAnimationTrack( i );
  2779. if ( !track )
  2780. continue;
  2781. // Disabled
  2782. if ( !track->IsTrackActive() )
  2783. continue;
  2784. // Map track flex controller to global name
  2785. for ( int side = 0; side < 1 + track->IsComboType(); side++ )
  2786. {
  2787. int controller = track->GetFlexControllerIndex( side );
  2788. if ( controller != -1 )
  2789. {
  2790. // Get spline intensity for controller
  2791. float flIntensity = track->GetIntensity( scenetime, side );
  2792. g_pFlexPanel->SetSlider( controller, flIntensity );
  2793. g_pFlexPanel->SetInfluence( controller, 1.0f );
  2794. g_pFlexPanel->SetEdited( controller, isEdited );
  2795. if( model )
  2796. {
  2797. LocalFlexController_t raw = track->GetRawFlexControllerIndex( side );
  2798. if ( raw != LocalFlexController_t(-1) )
  2799. {
  2800. model->SetFlexController( raw, flIntensity );
  2801. }
  2802. }
  2803. if ( settings && weights )
  2804. {
  2805. settings[ controller ] = flIntensity;
  2806. weights[ controller ] = 1.0f;
  2807. }
  2808. }
  2809. }
  2810. }
  2811. if ( needundo && exp )
  2812. {
  2813. exp->PushRedoInformation();
  2814. }
  2815. }
  2816. void ExpressionTool::OnCopyFromFlex( bool isEdited )
  2817. {
  2818. // local time in the expression tool for the last mouse click
  2819. float t = GetTimeValueForMouse( m_nClickedX );
  2820. CChoreoEvent *e = GetSafeEvent();
  2821. if ( !e )
  2822. return;
  2823. float scenetime = e->GetStartTime() + t;
  2824. OnCopyFromFlex( scenetime, isEdited );
  2825. return;
  2826. }
  2827. void ExpressionTool::OnSetSingleKeyFromFlex( char const *sliderName )
  2828. {
  2829. CChoreoEvent *e = GetSafeEvent();
  2830. if ( !e || !e->GetDuration() )
  2831. return;
  2832. float scenetime = g_pChoreoView->GetScene()->GetTime();
  2833. if ( scenetime < e->GetStartTime() || scenetime > e->GetEndTime() )
  2834. return;
  2835. scenetime = FacePoser_SnapTime( scenetime );
  2836. float relativetime = scenetime - e->GetStartTime();
  2837. // Get spline intensity for controller
  2838. float setting;
  2839. float influence;
  2840. float minvalue, maxvalue;
  2841. g_pChoreoView->SetDirty( true );
  2842. g_pChoreoView->PushUndo( "Set Single Key" );
  2843. for (int j = 0; j < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; j++)
  2844. {
  2845. if ( !g_pFlexPanel->IsValidSlider( j ) )
  2846. continue;
  2847. if ( Q_stricmp( g_pFlexPanel->getLabel(), sliderName ) )
  2848. continue;
  2849. setting = g_pFlexPanel->GetSliderRawValue( j );
  2850. influence = g_pFlexPanel->GetInfluence( j );
  2851. // g_pFlexPanel->SetEdited( j, isEdited );
  2852. g_pFlexPanel->GetSliderRange( j, minvalue, maxvalue );
  2853. bool found = false;
  2854. for ( int i = 0 ; i < e->GetNumFlexAnimationTracks() && !found; i++ )
  2855. {
  2856. CFlexAnimationTrack *track = e->GetFlexAnimationTrack( i );
  2857. if ( !track )
  2858. continue;
  2859. for ( int side = 0; side < 1 + track->IsComboType(); side++ )
  2860. {
  2861. if ( track->GetFlexControllerIndex( side ) != j )
  2862. continue;
  2863. float normalized = setting;
  2864. if ( side == 0 )
  2865. {
  2866. if ( minvalue != maxvalue )
  2867. {
  2868. normalized = ( setting - minvalue ) / ( maxvalue - minvalue );
  2869. }
  2870. if (track->IsInverted())
  2871. {
  2872. normalized = 1.0 - normalized;
  2873. }
  2874. }
  2875. found = true;
  2876. int nSampleCount = track->GetNumSamples( side );
  2877. int j = 0;
  2878. for ( ; j < nSampleCount; ++j )
  2879. {
  2880. CExpressionSample *s = track->GetSample( j, side );
  2881. if ( s->time == relativetime )
  2882. break;
  2883. }
  2884. if ( j >= nSampleCount )
  2885. {
  2886. track->AddSample( relativetime, normalized, side );
  2887. track->Resort( side );
  2888. }
  2889. else
  2890. {
  2891. CExpressionSample *s = track->GetSample( j, side );
  2892. s->value = normalized;
  2893. }
  2894. track->SetTrackActive( true );
  2895. break;
  2896. }
  2897. }
  2898. }
  2899. g_pChoreoView->PushRedo( "Set Single Key" );
  2900. m_pWorkspace->redraw();
  2901. redraw();
  2902. }
  2903. void ExpressionTool::OnCopyFromFlex( float scenetime, bool isEdited )
  2904. {
  2905. CChoreoEvent *e = GetSafeEvent();
  2906. if ( !e || !e->GetDuration() )
  2907. return;
  2908. if ( scenetime < e->GetStartTime() || scenetime > e->GetEndTime() )
  2909. return;
  2910. scenetime = FacePoser_SnapTime( scenetime );
  2911. float relativetime = scenetime - e->GetStartTime();
  2912. // Get spline intensity for controller
  2913. float setting;
  2914. float influence;
  2915. float minvalue, maxvalue;
  2916. g_pChoreoView->SetDirty( true );
  2917. g_pChoreoView->PushUndo( "Copy from Flex" );
  2918. for (int j = 0; j < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; j++)
  2919. {
  2920. if ( !g_pFlexPanel->IsValidSlider( j ) )
  2921. continue;
  2922. setting = g_pFlexPanel->GetSliderRawValue( j );
  2923. //setting = g_pFlexPanel->GetSlider( j );
  2924. influence = g_pFlexPanel->GetInfluence( j );
  2925. g_pFlexPanel->SetEdited( j, isEdited );
  2926. g_pFlexPanel->GetSliderRange( j, minvalue, maxvalue );
  2927. // Found it
  2928. if ( !influence )
  2929. {
  2930. continue;
  2931. }
  2932. bool found = false;
  2933. for ( int i = 0 ; i < e->GetNumFlexAnimationTracks() && !found; i++ )
  2934. {
  2935. CFlexAnimationTrack *track = e->GetFlexAnimationTrack( i );
  2936. if ( !track )
  2937. continue;
  2938. for ( int side = 0; side < 1 + track->IsComboType(); side++ )
  2939. {
  2940. if ( track->GetFlexControllerIndex( side ) != j )
  2941. continue;
  2942. float normalized = setting;
  2943. if ( side == 0 )
  2944. {
  2945. if ( minvalue != maxvalue )
  2946. {
  2947. normalized = ( setting - minvalue ) / ( maxvalue - minvalue );
  2948. }
  2949. if (track->IsInverted())
  2950. {
  2951. normalized = 1.0 - normalized;
  2952. }
  2953. }
  2954. found = true;
  2955. track->AddSample( relativetime, normalized, side );
  2956. track->Resort( side );
  2957. track->SetTrackActive( true );
  2958. break;
  2959. }
  2960. }
  2961. }
  2962. g_pChoreoView->PushRedo( "Copy from Flex" );
  2963. m_pWorkspace->redraw();
  2964. redraw();
  2965. }
  2966. bool ExpressionTool::SetFlexAnimationTrackFromExpression( int mx, int my, CExpClass *cl, CExpression *exp )
  2967. {
  2968. CChoreoEvent *e = GetSafeEvent();
  2969. if ( !e | !e->GetDuration() )
  2970. {
  2971. return false;
  2972. }
  2973. if ( !exp )
  2974. {
  2975. return false;
  2976. }
  2977. // Convert screen to client
  2978. POINT pt;
  2979. pt.x = mx;
  2980. pt.y = my;
  2981. ScreenToClient( (HWND)getHandle(), &pt );
  2982. if ( pt.x < 0 || pt.y < 0 )
  2983. {
  2984. return false;
  2985. }
  2986. if ( pt.x > w2() || pt.y > h2() )
  2987. {
  2988. return false;
  2989. }
  2990. float t = GetTimeValueForMouse( (short)pt.x );
  2991. // Get spline intensity for controller
  2992. // Get spline intensity for controller
  2993. float relativetime = t;
  2994. float faketime = e->GetStartTime() + relativetime;
  2995. faketime = FacePoser_SnapTime( faketime );
  2996. float *settings = exp->GetSettings();
  2997. float *influence = exp->GetWeights();
  2998. if ( !settings || !influence )
  2999. return false;
  3000. g_pChoreoView->SetDirty( true );
  3001. g_pChoreoView->PushUndo( "Copy from Expression" );
  3002. for ( int i = 0 ; i < e->GetNumFlexAnimationTracks(); i++ )
  3003. {
  3004. CFlexAnimationTrack *track = e->GetFlexAnimationTrack( i );
  3005. if ( !track )
  3006. continue;
  3007. if ( track->IsComboType() )
  3008. {
  3009. int left = track->GetFlexControllerIndex( 0 );
  3010. int right = track->GetFlexControllerIndex( 1 );
  3011. float leftval = settings[ left ];
  3012. float leftinfluence = influence[ left ];
  3013. float rightval = settings[ right ];
  3014. float rightinfluence = influence[ right ];
  3015. if ( leftinfluence || rightinfluence )
  3016. {
  3017. //Con_Printf( "%s %i(side %i): amount %f inf %f\n", track->GetFlexControllerName(), j, side, s, inf );
  3018. float mag, leftright;
  3019. if (leftval < rightval)
  3020. {
  3021. mag = rightval;
  3022. leftright = 1.0 - (leftval / rightval) * 0.5;
  3023. }
  3024. else if (leftval > rightval)
  3025. {
  3026. mag = leftval;
  3027. leftright = (rightval / leftval) * 0.5;
  3028. }
  3029. else
  3030. {
  3031. mag = leftval;
  3032. leftright = 0.5;
  3033. }
  3034. track->AddSample( relativetime, mag * leftinfluence, 0 );
  3035. track->AddSample( relativetime, leftright, 1 );
  3036. track->Resort( 0 );
  3037. track->Resort( 1 );
  3038. track->SetTrackActive( true );
  3039. }
  3040. }
  3041. else
  3042. {
  3043. int j = track->GetFlexControllerIndex( 0 );
  3044. float s = settings[ j ];
  3045. float inf = influence[ j ];
  3046. if ( inf )
  3047. {
  3048. track->AddSample( relativetime, s, 0 );
  3049. track->Resort( 0 );
  3050. track->SetTrackActive( true );
  3051. }
  3052. }
  3053. }
  3054. g_pChoreoView->PushRedo( "Copy from Expression" );
  3055. m_pWorkspace->redraw();
  3056. redraw();
  3057. return true;
  3058. }
  3059. bool ExpressionTool::PaintBackground()
  3060. {
  3061. redraw();
  3062. return false;
  3063. }
  3064. void ExpressionTool::OnExportFlexAnimation( void )
  3065. {
  3066. CChoreoEvent *event = GetSafeEvent();
  3067. if ( !event )
  3068. return;
  3069. // Create flexanimations dir
  3070. CreatePath( "flexanimations/foo" );
  3071. char fafilename[ 512 ];
  3072. if ( !FacePoser_ShowSaveFileNameDialog( fafilename, sizeof( fafilename ), "flexanimations", "*.vfa" ) )
  3073. {
  3074. return;
  3075. }
  3076. Q_DefaultExtension( fafilename, ".vfa", sizeof( fafilename ) );
  3077. Con_Printf( "Exporting events to %s\n", fafilename );
  3078. CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
  3079. CChoreoScene::FileSaveFlexAnimations( buf, 0, event );
  3080. // Write it out baby
  3081. FileHandle_t fh = filesystem->Open( fafilename, "wt" );
  3082. if (fh)
  3083. {
  3084. filesystem->Write( buf.Base(), buf.TellPut(), fh );
  3085. filesystem->Close(fh);
  3086. }
  3087. else
  3088. {
  3089. Con_Printf( "Unable to write file %s!!!\n", fafilename );
  3090. }
  3091. }
  3092. void ExpressionTool::OnImportFlexAnimation( void )
  3093. {
  3094. CChoreoEvent *event = GetSafeEvent();
  3095. if ( !event )
  3096. return;
  3097. char fafilename[ 512 ];
  3098. if ( !FacePoser_ShowOpenFileNameDialog( fafilename, sizeof( fafilename ), "flexanimations", "*.vfa" ) )
  3099. {
  3100. return;
  3101. }
  3102. if ( !filesystem->FileExists( fafilename ) )
  3103. return;
  3104. char fullpath[ 512 ];
  3105. filesystem->RelativePathToFullPath( fafilename, "MOD", fullpath, sizeof( fullpath ) );
  3106. LoadScriptFile( (char *)fullpath );
  3107. tokenprocessor->GetToken( true );
  3108. if ( stricmp( tokenprocessor->CurrentToken(), "flexanimations" ) )
  3109. {
  3110. Con_Printf( "ExpressionTool::OnImportFlexAnimation: %s, expecting \"flexanimations\"\n",
  3111. fullpath );
  3112. }
  3113. else
  3114. {
  3115. g_pChoreoView->SetDirty( true );
  3116. g_pChoreoView->PushUndo( "Import flex animations" );
  3117. CChoreoScene::ParseFlexAnimations( tokenprocessor, event, true );
  3118. // Force a full reset
  3119. m_pLastEvent = NULL;
  3120. SetEvent( event );
  3121. g_pChoreoView->PushRedo( "Import flex animations" );
  3122. Con_Printf( "Parsed flex animations from %s\n", fullpath );
  3123. }
  3124. }
  3125. void ExpressionTool::OnUndo( void )
  3126. {
  3127. g_pChoreoView->Undo();
  3128. }
  3129. void ExpressionTool::OnRedo( void )
  3130. {
  3131. g_pChoreoView->Redo();
  3132. }
  3133. void ExpressionTool::ForceScrubPositionFromSceneTime( float scenetime )
  3134. {
  3135. CChoreoEvent *e = GetSafeEvent();
  3136. if ( !e || !e->GetDuration() )
  3137. return;
  3138. float t = scenetime - e->GetStartTime();
  3139. m_flScrub = t;
  3140. m_flScrubTarget = t;
  3141. DrawScrubHandles();
  3142. }
  3143. void ExpressionTool::ForceScrubPosition( float frac )
  3144. {
  3145. m_flScrub = frac;
  3146. m_flScrubTarget = frac;
  3147. CChoreoEvent *e = GetSafeEvent();
  3148. if ( e )
  3149. {
  3150. float realtime = e->GetStartTime() + frac;
  3151. g_pChoreoView->SetScrubTime( realtime );
  3152. g_pChoreoView->SetScrubTargetTime( realtime );
  3153. g_pChoreoView->DrawScrubHandle();
  3154. }
  3155. DrawScrubHandles();
  3156. }
  3157. void ExpressionTool::DrawScrubHandles()
  3158. {
  3159. RECT rcHandle;
  3160. GetScrubHandleRect( rcHandle, true );
  3161. RECT rcTray = rcHandle;
  3162. rcTray.left = 0;
  3163. rcTray.right = w2();
  3164. CChoreoWidgetDrawHelper drawHelper( this, rcTray );
  3165. DrawScrubHandle( drawHelper, rcHandle );
  3166. }
  3167. void ExpressionTool::SetMouseOverPos( int x, int y )
  3168. {
  3169. m_nMousePos[ 0 ] = x;
  3170. m_nMousePos[ 1 ] = y;
  3171. }
  3172. void ExpressionTool::GetMouseOverPos( int &x, int& y )
  3173. {
  3174. x = m_nMousePos[ 0 ];
  3175. y = m_nMousePos[ 1 ];
  3176. }
  3177. //-----------------------------------------------------------------------------
  3178. // Purpose:
  3179. // Input : rcPos -
  3180. //-----------------------------------------------------------------------------
  3181. void ExpressionTool::GetMouseOverPosRect( RECT& rcPos )
  3182. {
  3183. rcPos.top = GetCaptionHeight() + 12;
  3184. rcPos.left = w2() - 200;
  3185. rcPos.right = w2() - 5;
  3186. rcPos.bottom = rcPos.top + 13;
  3187. }
  3188. //-----------------------------------------------------------------------------
  3189. // Purpose:
  3190. // Input : drawHelper -
  3191. // rcPos -
  3192. //-----------------------------------------------------------------------------
  3193. void ExpressionTool::DrawMouseOverPos( CChoreoWidgetDrawHelper& drawHelper, RECT& rcPos )
  3194. {
  3195. // Compute time for pixel x
  3196. float t = GetTimeValueForMouse( m_nMousePos[ 0 ] );
  3197. CChoreoEvent *e = GetSafeEvent();
  3198. if ( !e )
  3199. return;
  3200. t += e->GetStartTime();
  3201. float snapped = FacePoser_SnapTime( t );
  3202. // Found it, write out description
  3203. //
  3204. char sz[ 128 ];
  3205. if ( t != snapped )
  3206. {
  3207. Q_snprintf( sz, sizeof( sz ), "%s", FacePoser_DescribeSnappedTime( t ) );
  3208. }
  3209. else
  3210. {
  3211. Q_snprintf( sz, sizeof( sz ), "%.3f", t );
  3212. }
  3213. int len = drawHelper.CalcTextWidth( "Arial", 11, 900, sz );
  3214. RECT rcText = rcPos;
  3215. rcText.left = max( rcPos.left, rcPos.right - len );
  3216. drawHelper.DrawColoredText( "Arial", 11, 900, RGB( 255, 50, 70 ), rcText, sz );
  3217. }
  3218. //-----------------------------------------------------------------------------
  3219. // Purpose:
  3220. //-----------------------------------------------------------------------------
  3221. void ExpressionTool::DrawMouseOverPos()
  3222. {
  3223. RECT rcPos;
  3224. GetMouseOverPosRect( rcPos );
  3225. CChoreoWidgetDrawHelper drawHelper( this, rcPos );
  3226. DrawMouseOverPos( drawHelper, rcPos );
  3227. }
  3228. int ExpressionTool::CountSelectedSamples( void )
  3229. {
  3230. return m_pWorkspace->CountSelectedSamples();
  3231. }
  3232. void ExpressionTool::MoveSelectedSamples( float dfdx, float dfdy, bool snap )
  3233. {
  3234. m_pWorkspace->MoveSelectedSamples( dfdx, dfdy, snap );
  3235. }
  3236. void ExpressionTool::DeleteSelectedSamples( void )
  3237. {
  3238. m_pWorkspace->DeleteSelectedSamples();
  3239. }
  3240. void ExpressionTool::DeselectAll( void )
  3241. {
  3242. m_pWorkspace->DeselectAll();
  3243. m_bSelectionActive = false;
  3244. redraw();
  3245. }
  3246. //-----------------------------------------------------------------------------
  3247. // Purpose:
  3248. // Input : start -
  3249. // end -
  3250. //-----------------------------------------------------------------------------
  3251. void ExpressionTool::SelectPoints( float starttime, float endtime )
  3252. {
  3253. // Make sure order is correct
  3254. if ( endtime < starttime )
  3255. {
  3256. float temp = endtime;
  3257. endtime = starttime;
  3258. starttime = temp;
  3259. }
  3260. DeselectAll();
  3261. m_flSelection[ 0 ] = starttime;
  3262. m_flSelection[ 1 ] = endtime;
  3263. m_bSelectionActive = true;
  3264. // Select any words that span the selection
  3265. //
  3266. m_pWorkspace->SelectPoints( starttime, endtime );
  3267. redraw();
  3268. }
  3269. void ExpressionTool::FinishMoveSelection( int startx, int mx )
  3270. {
  3271. float start = GetTimeValueForMouse( startx );
  3272. float end = GetTimeValueForMouse( mx );
  3273. float delta = end - start;
  3274. for ( int i = 0; i < 2; i++ )
  3275. {
  3276. m_flSelection[ i ] += delta;
  3277. }
  3278. SelectPoints( m_flSelection[ 0 ], m_flSelection[ 1 ] );
  3279. redraw();
  3280. }
  3281. void ExpressionTool::FinishMoveSelectionStart( int startx, int mx )
  3282. {
  3283. float start = GetTimeValueForMouse( startx );
  3284. float end = GetTimeValueForMouse( mx );
  3285. float delta = end - start;
  3286. m_flSelection[ 0 ] += delta;
  3287. SelectPoints( m_flSelection[ 0 ], m_flSelection[ 1 ] );
  3288. redraw();
  3289. }
  3290. void ExpressionTool::FinishMoveSelectionEnd( int startx, int mx )
  3291. {
  3292. float start = GetTimeValueForMouse( startx );
  3293. float end = GetTimeValueForMouse( mx );
  3294. float delta = end - start;
  3295. m_flSelection[ 1 ] += delta;
  3296. SelectPoints( m_flSelection[ 0 ], m_flSelection[ 1 ] );
  3297. redraw();
  3298. }
  3299. //-----------------------------------------------------------------------------
  3300. // Purpose:
  3301. // Input : startx -
  3302. // mx -
  3303. //-----------------------------------------------------------------------------
  3304. void ExpressionTool::FinishSelect( int startx, int mx )
  3305. {
  3306. // Don't select really small areas
  3307. if ( abs( startx - mx ) < 1 )
  3308. return;
  3309. float start = GetTimeValueForMouse( startx );
  3310. float end = GetTimeValueForMouse( mx );
  3311. SelectPoints( start, end );
  3312. }
  3313. //-----------------------------------------------------------------------------
  3314. // Purpose:
  3315. // Input : mx -
  3316. // my -
  3317. // Output : Returns true on success, false on failure.
  3318. //-----------------------------------------------------------------------------
  3319. bool ExpressionTool::IsMouseOverPoints( int mx, int my )
  3320. {
  3321. RECT rc;
  3322. GetWorkspaceRect( rc );
  3323. // Over tag
  3324. if ( my > TRAY_HEIGHT )
  3325. return false;
  3326. if ( my <= 12 + GetCaptionHeight() )
  3327. return false;
  3328. return true;
  3329. }
  3330. //-----------------------------------------------------------------------------
  3331. // Purpose:
  3332. // Input : mx -
  3333. // my -
  3334. // Output : Returns true on success, false on failure.
  3335. //-----------------------------------------------------------------------------
  3336. bool ExpressionTool::IsMouseOverSelection( int mx, int my )
  3337. {
  3338. if ( !m_bSelectionActive )
  3339. return false;
  3340. if ( !IsMouseOverPoints( mx, my ) )
  3341. return false;
  3342. float t = GetTimeValueForMouse( mx );
  3343. if ( t >= m_flSelection[ 0 ] &&
  3344. t <= m_flSelection[ 1 ] )
  3345. {
  3346. return true;
  3347. }
  3348. return false;
  3349. }
  3350. bool ExpressionTool::IsMouseOverSelectionStartEdge( mxEvent *event )
  3351. {
  3352. int mx, my;
  3353. mx = (short)event->x;
  3354. my = (short)event->y;
  3355. if ( !(event->modifiers & mxEvent::KeyCtrl ) )
  3356. return false;
  3357. if ( !IsMouseOverSelection( mx, my ) )
  3358. return false;
  3359. int left;
  3360. left = GetPixelForTimeValue( m_flSelection[ 0 ] );
  3361. if ( abs( left - mx ) <= 2 )
  3362. {
  3363. return true;
  3364. }
  3365. return false;
  3366. }
  3367. bool ExpressionTool::IsMouseOverSelectionEndEdge( mxEvent *event )
  3368. {
  3369. int mx, my;
  3370. mx = (short)event->x;
  3371. my = (short)event->y;
  3372. if ( !(event->modifiers & mxEvent::KeyCtrl ) )
  3373. return false;
  3374. if ( !IsMouseOverSelection( mx, my ) )
  3375. return false;
  3376. int right;
  3377. right = GetPixelForTimeValue( m_flSelection[ 1 ] );
  3378. if ( abs( right - mx ) <= 2 )
  3379. {
  3380. return true;
  3381. }
  3382. return false;
  3383. }
  3384. //-----------------------------------------------------------------------------
  3385. // Purpose:
  3386. // Input : &rc -
  3387. //-----------------------------------------------------------------------------
  3388. void ExpressionTool::GetWorkspaceRect( RECT &rc )
  3389. {
  3390. GetClientRect( (HWND)getHandle(), &rc );
  3391. rc.top = TRAY_HEIGHT - 17;
  3392. rc.bottom = TRAY_HEIGHT - 1;
  3393. //InflateRect( &rc, -1, -1 );
  3394. }
  3395. void ExpressionTool::AddFocusRect( RECT& rc )
  3396. {
  3397. RECT rcFocus = rc;
  3398. POINT offset;
  3399. offset.x = 0;
  3400. offset.y = 0;
  3401. ClientToScreen( (HWND)getHandle(), &offset );
  3402. OffsetRect( &rcFocus, offset.x, offset.y );
  3403. // Convert to screen space?
  3404. CFocusRect fr;
  3405. fr.m_rcFocus = rcFocus;
  3406. fr.m_rcOrig = rcFocus;
  3407. m_FocusRects.AddToTail( fr );
  3408. }
  3409. //-----------------------------------------------------------------------------
  3410. // Purpose:
  3411. // Output : int
  3412. //-----------------------------------------------------------------------------
  3413. int ExpressionTool::ComputeHPixelsNeeded( void )
  3414. {
  3415. CChoreoEvent *event = GetSafeEvent();
  3416. if ( !event )
  3417. return 0;
  3418. int pixels = 0;
  3419. float maxtime = event->GetDuration();
  3420. pixels = (int)( ( maxtime + 5.0 ) * GetPixelsPerSecond() + 10 );
  3421. return pixels;
  3422. }
  3423. //-----------------------------------------------------------------------------
  3424. // Purpose:
  3425. //-----------------------------------------------------------------------------
  3426. void ExpressionTool::RepositionHSlider( void )
  3427. {
  3428. int pixelsneeded = ComputeHPixelsNeeded();
  3429. if ( pixelsneeded <= w2() )
  3430. {
  3431. m_pHorzScrollBar->setVisible( false );
  3432. }
  3433. else
  3434. {
  3435. m_pHorzScrollBar->setVisible( true );
  3436. }
  3437. m_pHorzScrollBar->setBounds( 0, h2() - m_nScrollbarHeight, w2(), m_nScrollbarHeight );
  3438. m_flLeftOffset = max( 0.f, m_flLeftOffset );
  3439. m_flLeftOffset = min( (float)pixelsneeded, m_flLeftOffset );
  3440. m_pHorzScrollBar->setRange( 0, pixelsneeded );
  3441. m_pHorzScrollBar->setValue( m_flLeftOffset );
  3442. m_pHorzScrollBar->setPagesize( w2() );
  3443. m_nLastHPixelsNeeded = pixelsneeded;
  3444. }
  3445. //-----------------------------------------------------------------------------
  3446. // Purpose:
  3447. // Output : float
  3448. //-----------------------------------------------------------------------------
  3449. float ExpressionTool::GetPixelsPerSecond( void )
  3450. {
  3451. return m_flPixelsPerSecond * (float)g_pChoreoView->GetTimeZoom( GetToolName() ) / 100.0f;
  3452. }
  3453. //-----------------------------------------------------------------------------
  3454. // Purpose:
  3455. // Input : x -
  3456. //-----------------------------------------------------------------------------
  3457. void ExpressionTool::MoveTimeSliderToPos( int x )
  3458. {
  3459. m_flLeftOffset = x;
  3460. m_pHorzScrollBar->setValue( m_flLeftOffset );
  3461. InvalidateRect( (HWND)m_pHorzScrollBar->getHandle(), NULL, TRUE );
  3462. InvalidateLayout();
  3463. }
  3464. //-----------------------------------------------------------------------------
  3465. // Purpose:
  3466. //-----------------------------------------------------------------------------
  3467. void ExpressionTool::InvalidateLayout( void )
  3468. {
  3469. if ( m_bSuppressLayout )
  3470. return;
  3471. if ( ComputeHPixelsNeeded() != m_nLastHPixelsNeeded )
  3472. {
  3473. RepositionHSlider();
  3474. }
  3475. m_bLayoutIsValid = false;
  3476. m_pWorkspace->redraw();
  3477. redraw();
  3478. }
  3479. //-----------------------------------------------------------------------------
  3480. // Purpose:
  3481. // Input : time -
  3482. // *clipped -
  3483. // Output : int
  3484. //-----------------------------------------------------------------------------
  3485. int ExpressionTool::GetPixelForTimeValue( float time, bool *clipped /*=NULL*/ )
  3486. {
  3487. int left, right;
  3488. GetWorkspaceLeftRight( left, right );
  3489. if ( clipped )
  3490. {
  3491. *clipped = false;
  3492. }
  3493. float st, ed;
  3494. GetStartAndEndTime( st, ed );
  3495. float frac = ( time - st ) / ( ed - st );
  3496. if ( frac < 0.0 || frac > 1.0 )
  3497. {
  3498. if ( clipped )
  3499. {
  3500. *clipped = true;
  3501. }
  3502. }
  3503. int pixel = left + ( int )( frac * (right - left ) );
  3504. return pixel;
  3505. }
  3506. void ExpressionTool::OnChangeScale( void )
  3507. {
  3508. CChoreoScene *scene = g_pChoreoView->GetScene();
  3509. if ( !scene )
  3510. {
  3511. return;
  3512. }
  3513. // Zoom time in / out
  3514. CInputParams params;
  3515. memset( &params, 0, sizeof( params ) );
  3516. strcpy( params.m_szDialogTitle, "Change Zoom" );
  3517. strcpy( params.m_szPrompt, "New scale (e.g., 2.5x):" );
  3518. Q_snprintf( params.m_szInputText, sizeof( params.m_szInputText ), "%.2f", (float)g_pChoreoView->GetTimeZoom( GetToolName() ) / 100.0f );
  3519. if ( !InputProperties( &params ) )
  3520. return;
  3521. g_pChoreoView->SetTimeZoom( GetToolName(), clamp( (int)( 100.0f * atof( params.m_szInputText ) ), 1, MAX_TIME_ZOOM ), false );
  3522. m_nLastHPixelsNeeded = -1;
  3523. InvalidateLayout();
  3524. Con_Printf( "Zoom factor %i %%\n", g_pChoreoView->GetTimeZoom( GetToolName() ) );
  3525. }
  3526. //-----------------------------------------------------------------------------
  3527. // Purpose:
  3528. // Input : st -
  3529. // ed -
  3530. //-----------------------------------------------------------------------------
  3531. void ExpressionTool::GetStartAndEndTime( float& st, float& ed )
  3532. {
  3533. st = m_flLeftOffset / GetPixelsPerSecond();
  3534. int left, right;
  3535. GetWorkspaceLeftRight( left, right );
  3536. if ( right <= left )
  3537. {
  3538. ed = st;
  3539. }
  3540. else
  3541. {
  3542. ed = st + (float)( right - left ) / GetPixelsPerSecond();
  3543. }
  3544. }
  3545. //-----------------------------------------------------------------------------
  3546. // Purpose:
  3547. // Input : -
  3548. // Output : float
  3549. //-----------------------------------------------------------------------------
  3550. float ExpressionTool::GetEventEndTime()
  3551. {
  3552. CChoreoEvent *ev = GetSafeEvent();
  3553. if ( !ev )
  3554. return 1.0f;
  3555. return ev->GetDuration();
  3556. }
  3557. //-----------------------------------------------------------------------------
  3558. // Purpose:
  3559. // Input : mx -
  3560. // clip -
  3561. // Output : float
  3562. //-----------------------------------------------------------------------------
  3563. float ExpressionTool::GetTimeValueForMouse( int mx, bool clip /*=false*/)
  3564. {
  3565. int left, right;
  3566. GetWorkspaceLeftRight( left, right );
  3567. float st, ed;
  3568. GetStartAndEndTime( st, ed );
  3569. if ( clip )
  3570. {
  3571. if ( mx < 0 )
  3572. {
  3573. return st;
  3574. }
  3575. if ( mx > w2() )
  3576. {
  3577. return ed;
  3578. }
  3579. }
  3580. float frac = (float)( mx - left ) / (float)( right - left );
  3581. return st + frac * ( ed - st );
  3582. }
  3583. void ExpressionTool::DrawEventEnd( CChoreoWidgetDrawHelper& drawHelper )
  3584. {
  3585. CChoreoEvent *e = GetSafeEvent();
  3586. if ( !e )
  3587. return;
  3588. float duration = e->GetDuration();
  3589. if ( !duration )
  3590. return;
  3591. int leftx = GetPixelForTimeValue( duration );
  3592. if ( leftx >= w2() )
  3593. return;
  3594. RECT rcClient;
  3595. drawHelper.GetClientRect( rcClient );
  3596. drawHelper.DrawColoredLine(
  3597. COLOR_CHOREO_ENDTIME, PS_SOLID, 1,
  3598. leftx, rcClient.top + TRAY_HEIGHT, leftx, rcClient.bottom );
  3599. }
  3600. void ExpressionTool::OnSortByUsed( void )
  3601. {
  3602. m_pWorkspace->OnSortByUsed();
  3603. }
  3604. void ExpressionTool::OnSortByName( void )
  3605. {
  3606. m_pWorkspace->OnSortByName();
  3607. }
  3608. //-----------------------------------------------------------------------------
  3609. // Purpose:
  3610. // Input : *item -
  3611. // Output : Returns true on success, false on failure.
  3612. //-----------------------------------------------------------------------------
  3613. bool ExpressionTool::IsFocusItem( TimelineItem *item )
  3614. {
  3615. return m_pWorkspace->GetClickedItem() == item;
  3616. }
  3617. //-----------------------------------------------------------------------------
  3618. // Purpose: Delete a vertical column of samples between the selection
  3619. // markers. If excise_time is true, shifts remaining samples left
  3620. // Input : excise_time -
  3621. //-----------------------------------------------------------------------------
  3622. void ExpressionTool::OnDeleteSelection( bool excise_time )
  3623. {
  3624. if ( !m_bSelectionActive )
  3625. return;
  3626. // Force selection of everything again!
  3627. SelectPoints( m_flSelection[ 0 ], m_flSelection[ 1 ] );
  3628. int i, t;
  3629. char const *undotext = excise_time ? "Excise column" : "Delete column";
  3630. float shift_left_time = m_flSelection[ 1 ] - m_flSelection[ 0 ];
  3631. Assert( shift_left_time > 0.0f );
  3632. g_pChoreoView->SetDirty( true );
  3633. g_pChoreoView->PushUndo( undotext );
  3634. for ( int controller = 0; controller < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; controller++ )
  3635. {
  3636. TimelineItem *item = m_pWorkspace->GetItem( controller );
  3637. if ( !item )
  3638. continue;
  3639. CFlexAnimationTrack *track = item->GetSafeTrack();
  3640. if ( !track )
  3641. continue;
  3642. for ( t = 0; t < 2; t++ )
  3643. {
  3644. for ( i = track->GetNumSamples( t ) - 1; i >= 0 ; i-- )
  3645. {
  3646. CExpressionSample *sample = track->GetSample( i, t );
  3647. if ( !sample->selected )
  3648. continue;
  3649. track->RemoveSample( i, t );
  3650. }
  3651. if ( !excise_time )
  3652. continue;
  3653. // Now shift things after m_flSelection[0] to the left
  3654. for ( i = track->GetNumSamples( t ) - 1; i >= 0 ; i-- )
  3655. {
  3656. CExpressionSample *sample = track->GetSample( i, t );
  3657. if ( sample->time < m_flSelection[ 1 ] )
  3658. continue;
  3659. // Shift it
  3660. sample->time -= shift_left_time;
  3661. }
  3662. }
  3663. item->DrawSelf();
  3664. }
  3665. g_pChoreoView->PushRedo( undotext );
  3666. // Clear selection and redraw()
  3667. DeselectAll();
  3668. }
  3669. //-----------------------------------------------------------------------------
  3670. // Purpose:
  3671. //-----------------------------------------------------------------------------
  3672. void ExpressionTool::OnResetItemSize()
  3673. {
  3674. TimelineItem *item = m_pWorkspace->GetClickedItem();
  3675. if ( !item )
  3676. return;
  3677. item->ResetHeight();
  3678. m_pWorkspace->LayoutItems( true );
  3679. redraw();
  3680. }
  3681. //-----------------------------------------------------------------------------
  3682. // Purpose:
  3683. //-----------------------------------------------------------------------------
  3684. void ExpressionTool::OnResetAllItemSizes()
  3685. {
  3686. for ( int controller = 0; controller < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; controller++ )
  3687. {
  3688. TimelineItem *item = m_pWorkspace->GetItem( controller );
  3689. if ( !item )
  3690. continue;
  3691. item->ResetHeight();
  3692. }
  3693. m_pWorkspace->LayoutItems( true );
  3694. redraw();
  3695. }
  3696. //-----------------------------------------------------------------------------
  3697. // Purpose:
  3698. //-----------------------------------------------------------------------------
  3699. void ExpressionTool::OnScaleSamples()
  3700. {
  3701. int t, i;
  3702. //Scale samples
  3703. CInputParams params;
  3704. memset( &params, 0, sizeof( params ) );
  3705. strcpy( params.m_szDialogTitle, "Scale selected samples" );
  3706. strcpy( params.m_szPrompt, "Factor:" );
  3707. strcpy( params.m_szInputText, "1.0" );
  3708. if ( !InputProperties( &params ) )
  3709. return;
  3710. float scale_factor = atof( params.m_szInputText );
  3711. if( scale_factor <= 0.0f )
  3712. {
  3713. Con_Printf( "Can't scale to %.2f\n", scale_factor );
  3714. }
  3715. char const *undotext = "Scale samples";
  3716. g_pChoreoView->SetDirty( true );
  3717. g_pChoreoView->PushUndo( undotext );
  3718. for ( int controller = 0; controller < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; controller++ )
  3719. {
  3720. TimelineItem *item = m_pWorkspace->GetItem( controller );
  3721. if ( !item )
  3722. continue;
  3723. CFlexAnimationTrack *track = item->GetSafeTrack();
  3724. if ( !track )
  3725. continue;
  3726. for ( t = 0; t < 2; t++ )
  3727. {
  3728. for ( i = track->GetNumSamples( t ) - 1; i >= 0 ; i-- )
  3729. {
  3730. CExpressionSample *sample = track->GetSample( i, t );
  3731. if ( !sample->selected )
  3732. continue;
  3733. // Scale it
  3734. float curvalue = sample->value;
  3735. curvalue *= scale_factor;
  3736. // Clamp it
  3737. curvalue = clamp( curvalue, 0.0f, 1.0f );
  3738. sample->value = curvalue;
  3739. }
  3740. }
  3741. }
  3742. g_pChoreoView->PushRedo( undotext );
  3743. m_pWorkspace->redraw();
  3744. redraw();
  3745. }
  3746. void ExpressionTool::OnModelChanged()
  3747. {
  3748. SetEvent( NULL );
  3749. redraw();
  3750. }
  3751. void ExpressionTool::OnEdgeProperties()
  3752. {
  3753. TimelineItem *item = m_pWorkspace->GetClickedItem();
  3754. if ( !item )
  3755. return;
  3756. CFlexAnimationTrack *track = item->GetSafeTrack();
  3757. if ( !track )
  3758. return;
  3759. CEdgePropertiesParams params;
  3760. Q_memset( &params, 0, sizeof( params ) );
  3761. Q_strcpy( params.m_szDialogTitle, "Edge Properties" );
  3762. params.SetFromFlexTrack( track );
  3763. if ( !EdgeProperties( &params ) )
  3764. {
  3765. return;
  3766. }
  3767. char const *undotext = "Change Edge Properties";
  3768. g_pChoreoView->SetDirty( true );
  3769. g_pChoreoView->PushUndo( undotext );
  3770. // Apply changes.
  3771. params.ApplyToTrack( track );
  3772. g_pChoreoView->PushRedo( undotext );
  3773. m_pWorkspace->redraw();
  3774. redraw();
  3775. }
  3776. float ExpressionTool::GetScrubberSceneTime()
  3777. {
  3778. CChoreoEvent *ev = GetSafeEvent();
  3779. if ( !ev )
  3780. return 0.0f;
  3781. float curtime = GetScrub();
  3782. curtime += ev->GetStartTime();
  3783. return curtime;
  3784. }
  3785. void ExpressionTool::GetTimelineItems( CUtlVector< TimelineItem * >& list )
  3786. {
  3787. for ( int i = 0; i < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; i++ )
  3788. {
  3789. TimelineItem *item = m_pWorkspace->GetItem( i );
  3790. if ( !item )
  3791. continue;
  3792. list.AddToTail( item );
  3793. }
  3794. }
  3795. bool ExpressionTool::HasCopiedColumn()
  3796. {
  3797. return m_ColumnCopy.m_bActive;
  3798. }
  3799. void ExpressionTool::OnCopyColumn()
  3800. {
  3801. m_ColumnCopy.Reset();
  3802. m_ColumnCopy.m_bActive = true;
  3803. m_ColumnCopy.m_flCopyTimes[ 0 ] = m_flSelection[ 0 ];
  3804. m_ColumnCopy.m_flCopyTimes[ 1 ] = m_flSelection[ 1 ];
  3805. for ( int controller = 0; controller < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; ++controller )
  3806. {
  3807. TimelineItem *item = m_pWorkspace->GetItem( controller );
  3808. if ( !item )
  3809. continue;
  3810. CFlexAnimationTrack *track = item->GetSafeTrack();
  3811. if ( !track )
  3812. continue;
  3813. for ( int t = 0; t < 2; t++ )
  3814. {
  3815. for ( int i = track->GetNumSamples( t ) - 1; i >= 0 ; i-- )
  3816. {
  3817. CExpressionSample *sample = track->GetSample( i, t );
  3818. if ( !sample->selected )
  3819. continue;
  3820. // Add to dictionary
  3821. CExpressionSample copy( *sample );
  3822. copy.selected = false;
  3823. int tIndex = m_ColumnCopy.m_Data.Find( track->GetFlexControllerName() );
  3824. if ( tIndex == m_ColumnCopy.m_Data.InvalidIndex() )
  3825. {
  3826. tIndex = m_ColumnCopy.m_Data.Insert( track->GetFlexControllerName() );
  3827. }
  3828. CColumnCopier::CTrackData &data = m_ColumnCopy.m_Data[ tIndex ];
  3829. data.m_Samples[ t ].AddToTail( copy );
  3830. }
  3831. }
  3832. }
  3833. }
  3834. void ExpressionTool::OnPasteColumn()
  3835. {
  3836. if ( !m_ColumnCopy.m_bActive )
  3837. {
  3838. Msg( "Nothing to paste\n" );
  3839. return;
  3840. }
  3841. float flPasteTime = GetTimeForClickedPos();
  3842. float flPasteEndTime = flPasteTime + m_ColumnCopy.m_flCopyTimes[ 1 ] - m_ColumnCopy.m_flCopyTimes[ 0 ];
  3843. // Clear selection and redraw()
  3844. DeselectAll();
  3845. // Select everthing in the paste region so we can delete the existing stuff
  3846. SelectPoints( flPasteTime, flPasteEndTime );
  3847. int i, t;
  3848. char const *undotext = "Paste column";
  3849. g_pChoreoView->SetDirty( true );
  3850. g_pChoreoView->PushUndo( undotext );
  3851. for ( int controller = 0; controller < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; controller++ )
  3852. {
  3853. TimelineItem *item = m_pWorkspace->GetItem( controller );
  3854. if ( !item )
  3855. continue;
  3856. CFlexAnimationTrack *track = item->GetSafeTrack();
  3857. if ( !track )
  3858. continue;
  3859. int tIndex = m_ColumnCopy.m_Data.Find( track->GetFlexControllerName() );
  3860. for ( t = 0; t < 2; t++ )
  3861. {
  3862. // Remove all selected samples
  3863. for ( i = track->GetNumSamples( t ) - 1; i >= 0 ; i-- )
  3864. {
  3865. CExpressionSample *sample = track->GetSample( i, t );
  3866. if ( !sample->selected )
  3867. continue;
  3868. track->RemoveSample( i, t );
  3869. }
  3870. // Now add the new samples, if any in the time selection
  3871. if ( tIndex != m_ColumnCopy.m_Data.InvalidIndex() )
  3872. {
  3873. CColumnCopier::CTrackData &data = m_ColumnCopy.m_Data[ tIndex ];
  3874. for ( int j = 0; j < data.m_Samples[ t ].Count(); ++j )
  3875. {
  3876. CExpressionSample *s = &data.m_Samples[ t ][ j ];
  3877. CExpressionSample *newSample = track->AddSample( s->time - m_ColumnCopy.m_flCopyTimes[ 0 ] + flPasteTime, s->value, t );
  3878. newSample->selected = true;
  3879. }
  3880. }
  3881. track->Resort( t );
  3882. }
  3883. item->DrawSelf();
  3884. }
  3885. g_pChoreoView->PushRedo( undotext );
  3886. }
  3887. void ExpressionTool::ClearColumnCopy()
  3888. {
  3889. m_ColumnCopy.Reset();
  3890. }