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.

1211 lines
28 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "hlfaceposer.h"
  8. #include <windows.h>
  9. #include <stdio.h>
  10. #include <mxtk/mxWindow.h>
  11. #include <mxtk/mxScrollBar.h>
  12. #include "mxexpressiontray.h"
  13. #include "expressions.h"
  14. #include "expclass.h"
  15. #include "ControlPanel.h"
  16. #include "FlexPanel.h"
  17. #include <mxtk/mxPopupMenu.h>
  18. #include "ChoreoView.h"
  19. #include "StudioModel.h"
  20. #include "ExpressionProperties.h"
  21. #include "InputProperties.h"
  22. #include "ViewerSettings.h"
  23. #include "mxExpressionTab.h"
  24. #include "choreowidgetdrawhelper.h"
  25. #include "ExpressionTool.h"
  26. #include "faceposer_models.h"
  27. #include "tier0/icommandline.h"
  28. #include "filesystem.h"
  29. #define MAX_THUMBNAILSIZE 256
  30. #define MIN_THUMBNAILSIZE 64
  31. #define THUMBNAIL_SIZE_STEP 4
  32. #define DEFAULT_THUMBNAIL_SIZE 128
  33. #define TOP_GAP 45
  34. mxExpressionTray *g_pExpressionTrayTool = 0;
  35. mxExpressionTray::mxExpressionTray( mxWindow *parent, int id /*=0*/ )
  36. : IFacePoserToolWindow( "ExpressionTrayTool", "Expressions" ), mxWindow( parent, 0, 0, 0, 0, "ExpressionTrayTool", id )
  37. {
  38. setId( id );
  39. m_nTopOffset = 0;
  40. slScrollbar = new mxScrollbar( this, 0, 0, 18, 100, IDC_TRAYSCROLL, mxScrollbar::Vertical );
  41. m_nLastNumExpressions = -1;
  42. m_nGranularity = 10;
  43. m_nPrevCell = -1;
  44. m_nCurCell = -1;
  45. m_nClickedCell = -1;
  46. m_nButtonSquare = 16;
  47. m_nGap = 4;
  48. m_nDescriptionHeight = 34;
  49. m_nSnapshotWidth = g_viewerSettings.thumbnailsize;
  50. m_nSnapshotWidth = max( MIN_THUMBNAILSIZE, m_nSnapshotWidth );
  51. m_nSnapshotWidth = min( MAX_THUMBNAILSIZE, m_nSnapshotWidth );
  52. g_viewerSettings.thumbnailsize = m_nSnapshotWidth;
  53. m_nSnapshotHeight = m_nSnapshotWidth + m_nDescriptionHeight;
  54. m_pButtons = NULL;
  55. m_nPreviousExpressionCount = -1;
  56. m_bDragging = false;
  57. m_nDragCell = -1;
  58. CreateButtons();
  59. g_pExpressionClass = new mxExpressionTab( this, 5, 5, 500, 20, IDC_EXPRESSIONCLASS );
  60. m_pABButton = new mxButton( this, 520, 8, 50, 18, "A/B", IDC_AB );
  61. m_pThumbnailIncreaseButton = new mxButton( this, 0, 0, 18, 18, "+", IDC_THUMBNAIL_INCREASE );
  62. m_pThumbnailDecreaseButton = new mxButton( this, 0, 0, 18, 18, "-", IDC_THUMBNAIL_DECREASE );
  63. }
  64. //-----------------------------------------------------------------------------
  65. // Purpose:
  66. // Output : mxExpressionTray::~mxExpressionTray
  67. //-----------------------------------------------------------------------------
  68. mxExpressionTray::~mxExpressionTray ( void )
  69. {
  70. DeleteAllButtons();
  71. g_pExpressionTrayTool = NULL;
  72. }
  73. //-----------------------------------------------------------------------------
  74. // Purpose:
  75. // Input : cellsize -
  76. //-----------------------------------------------------------------------------
  77. void mxExpressionTray::SetCellSize( int cellsize )
  78. {
  79. m_nSnapshotWidth = cellsize;
  80. m_nSnapshotHeight = cellsize + m_nDescriptionHeight;
  81. DeleteAllButtons();
  82. CreateButtons();
  83. redraw();
  84. }
  85. //-----------------------------------------------------------------------------
  86. // Purpose:
  87. //-----------------------------------------------------------------------------
  88. void mxExpressionTray::Deselect( void )
  89. {
  90. CExpClass *active = expressions->GetActiveClass();
  91. if ( active )
  92. {
  93. for ( int i = 0 ; i < active->GetNumExpressions(); i++ )
  94. {
  95. CExpression *exp = active->GetExpression( i );
  96. if ( exp )
  97. {
  98. exp->SetSelected( false );
  99. }
  100. }
  101. }
  102. m_nCurCell = m_nPrevCell = -1;
  103. redraw();
  104. }
  105. //-----------------------------------------------------------------------------
  106. // Purpose:
  107. // Input : exp -
  108. //-----------------------------------------------------------------------------
  109. void mxExpressionTray::Select( int exp, bool deselect /*=true*/ )
  110. {
  111. int oldcell = m_nCurCell;
  112. if ( deselect )
  113. {
  114. Deselect();
  115. }
  116. m_nPrevCell = oldcell;
  117. m_nCurCell = exp;
  118. if ( m_nCurCell >= 0 )
  119. {
  120. CExpClass *active = expressions->GetActiveClass();
  121. if ( active )
  122. {
  123. CExpression *exp = active->GetExpression( m_nCurCell );
  124. if ( exp )
  125. {
  126. exp->SetSelected( true );
  127. }
  128. }
  129. }
  130. redraw();
  131. }
  132. //-----------------------------------------------------------------------------
  133. // Purpose:
  134. // Input : *btn -
  135. //-----------------------------------------------------------------------------
  136. void mxExpressionTray::AddButton( const char *name, const char *tooltip, const char *bitmap, ETMEMBERFUNC pfnCallback,
  137. bool active, int x, int y, int w, int h )
  138. {
  139. mxETButton *btn = new mxETButton;
  140. strcpy( btn->m_szName, name );
  141. strcpy( btn->m_szToolTip, tooltip );
  142. btn->m_bActive = active;
  143. btn->m_rc.left = x;
  144. btn->m_rc.top = y;
  145. btn->m_rc.right = x + w;
  146. btn->m_rc.bottom = y + h;
  147. btn->m_pImage = new mxbitmapdata_t;
  148. Assert( btn->m_pImage );
  149. btn->m_pImage->valid = false;
  150. LoadBitmapFromFile( bitmap, *btn->m_pImage );
  151. btn->m_fnCallback = pfnCallback;
  152. btn->next = m_pButtons;
  153. m_pButtons = btn;
  154. }
  155. //-----------------------------------------------------------------------------
  156. // Purpose:
  157. //-----------------------------------------------------------------------------
  158. void mxExpressionTray::CreateButtons( void )
  159. {
  160. int x = m_nSnapshotWidth - 2 * ( m_nButtonSquare + 4 );
  161. int y = 4;
  162. AddButton( "undo", "Undo", "gfx/hlfaceposer/undo.bmp", &mxExpressionTray::ET_Undo, true, x, y, m_nButtonSquare, m_nButtonSquare );
  163. x += ( m_nButtonSquare + 4 );
  164. AddButton( "redo", "Redo", "gfx/hlfaceposer/redo.bmp", &mxExpressionTray::ET_Redo, true, x, y, m_nButtonSquare, m_nButtonSquare );
  165. }
  166. void mxExpressionTray::ActivateButton( const char *name, bool active )
  167. {
  168. mxETButton *btn = FindButton( name );
  169. if ( !name )
  170. return;
  171. btn->m_bActive = active;
  172. }
  173. mxExpressionTray::mxETButton *mxExpressionTray::FindButton( const char *name )
  174. {
  175. mxETButton *p = m_pButtons;
  176. while ( p )
  177. {
  178. if ( !stricmp( p->m_szName, name ) )
  179. return p;
  180. p = p->next;
  181. }
  182. return NULL;
  183. }
  184. //-----------------------------------------------------------------------------
  185. // Purpose:
  186. //-----------------------------------------------------------------------------
  187. void mxExpressionTray::DeleteAllButtons( void )
  188. {
  189. mxETButton *p = m_pButtons, *n;
  190. while ( p )
  191. {
  192. n = p->next;
  193. delete p->m_pImage;
  194. delete p;
  195. p = n;
  196. }
  197. m_pButtons = NULL;
  198. }
  199. //-----------------------------------------------------------------------------
  200. // Purpose:
  201. // Input : x -
  202. // y -
  203. // Output : mxExpressionTray::mxETButton
  204. //-----------------------------------------------------------------------------
  205. mxExpressionTray::mxETButton *mxExpressionTray::GetItemUnderCursor( int x, int y )
  206. {
  207. // Convert to cell space
  208. int cell = GetCellUnderPosition( x, y );
  209. if ( cell == -1 )
  210. {
  211. return NULL;
  212. }
  213. // Cell is off screen?
  214. int cx, cy, cw, ch;
  215. if ( !ComputeRect( cell, cx, cy, cw, ch ) )
  216. {
  217. return NULL;
  218. }
  219. mxETButton *p = m_pButtons;
  220. while ( p )
  221. {
  222. if ( p->m_bActive &&
  223. x >= cx &&
  224. x <= cx + cw &&
  225. y >= cy &&
  226. y <= cy + ch )
  227. {
  228. // In-side cell
  229. int cellx = x - cx;
  230. int celly = y - cy;
  231. if ( cellx >= p->m_rc.left &&
  232. cellx <= p->m_rc.right &&
  233. celly >= p->m_rc.top &&
  234. celly <= p->m_rc.bottom )
  235. {
  236. return p;
  237. }
  238. }
  239. p = p->next;
  240. }
  241. return NULL;
  242. }
  243. void mxExpressionTray::DrawButton( CChoreoWidgetDrawHelper& helper, int cell, mxETButton *btn )
  244. {
  245. if ( !btn || !btn->m_pImage || !btn->m_pImage->valid )
  246. return;
  247. if ( !btn->m_bActive )
  248. return;
  249. int x, y, w, h;
  250. if ( !ComputeRect( cell, x, y, w, h ) )
  251. return;
  252. x += btn->m_rc.left;
  253. y += btn->m_rc.top;
  254. w = btn->m_rc.right - btn->m_rc.left;
  255. h = btn->m_rc.bottom - btn->m_rc.top;
  256. HDC dc = helper.GrabDC();
  257. DrawBitmapToDC( dc, x, y, w, h, *btn->m_pImage );
  258. helper.DrawOutlinedRect( RGB( 170, 170, 170 ), PS_SOLID, 1, x, y, x + w, y + h );
  259. }
  260. //-----------------------------------------------------------------------------
  261. // Purpose:
  262. // Output : int
  263. //-----------------------------------------------------------------------------
  264. int mxExpressionTray::ComputePixelsNeeded( void )
  265. {
  266. CExpClass *active = expressions->GetActiveClass();
  267. if ( !active )
  268. return 100;
  269. // Remove scroll bar
  270. int w = this->w2() - 16;
  271. int colsperrow;
  272. colsperrow = ( w - m_nGap ) / ( m_nSnapshotWidth + m_nGap );
  273. // At least one
  274. colsperrow = max( 1, colsperrow );
  275. int rowsneeded = ( ( active->GetNumExpressions() + colsperrow - 1 ) / colsperrow );
  276. return rowsneeded * ( m_nSnapshotHeight + m_nGap ) + m_nGap + TOP_GAP + GetCaptionHeight();
  277. }
  278. bool mxExpressionTray::ComputeRect( int cell, int& rcx, int& rcy, int& rcw, int& rch )
  279. {
  280. // Remove scroll bar
  281. int w = this->w2() - 16;
  282. int colsperrow;
  283. colsperrow = ( w - m_nGap ) / ( m_nSnapshotWidth + m_nGap );
  284. // At least one
  285. colsperrow = max( 1, colsperrow );
  286. int row, col;
  287. row = cell / colsperrow;
  288. col = cell % colsperrow;
  289. // don't allow partial columns
  290. rcx = m_nGap + col * ( m_nSnapshotWidth + m_nGap );
  291. rcy = GetCaptionHeight() + TOP_GAP + ( -m_nTopOffset * m_nGranularity ) + m_nGap + row * ( m_nSnapshotHeight + m_nGap );
  292. // Starts off screen
  293. if ( rcx < 0 )
  294. return false;
  295. // Ends off screen
  296. if ( rcx + m_nSnapshotWidth + m_nGap > this->w2() )
  297. return false;
  298. // Allow partial in y direction
  299. if ( rcy > this->h2() )
  300. return false;
  301. if ( rcy + m_nSnapshotHeight + m_nGap < 0 )
  302. return false;
  303. // Some portion is onscreen
  304. rcw = m_nSnapshotWidth;
  305. rch = m_nSnapshotHeight;
  306. return true;
  307. }
  308. void mxExpressionTray::DrawExpressionFocusRect( CChoreoWidgetDrawHelper& helper, int x, int y, int w, int h, COLORREF clr )
  309. {
  310. helper.DrawOutlinedRect( clr, PS_SOLID, 4, x, y, x + w, y + h );
  311. }
  312. void mxExpressionTray::DrawExpressionDescription( CChoreoWidgetDrawHelper& helper, int x, int y, int w, int h, const char *expressionname, const char *description )
  313. {
  314. int textheight = 15;
  315. RECT textRect;
  316. textRect.left = x + 5;
  317. textRect.top = y + h - 2 * textheight - 12;
  318. textRect.right = x + w - 10;
  319. textRect.bottom = y + h - 12;
  320. helper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 0, 0, 0 ), textRect, "%s", expressionname );
  321. // DrawText( hdc, expressionname, strlen( expressionname ), &textRect, DT_NOPREFIX | DT_SINGLELINE | DT_LEFT | DT_VCENTER | DT_WORD_ELLIPSIS );
  322. OffsetRect( &textRect, 0, textheight );
  323. helper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 63, 63, 63 ), textRect, "%s", description );
  324. // DrawText( hdc, description, strlen( description ), &textRect, DT_NOPREFIX | DT_SINGLELINE | DT_LEFT | DT_VCENTER | DT_WORD_ELLIPSIS );
  325. }
  326. //-----------------------------------------------------------------------------
  327. // Purpose:
  328. // Input : dc -
  329. // current -
  330. // rcx -
  331. // rcy -
  332. // rcw -
  333. // rch -
  334. //-----------------------------------------------------------------------------
  335. void mxExpressionTray::DrawDirtyFlag( CChoreoWidgetDrawHelper& helper, CExpression *current, int rcx, int rcy, int rcw, int rch )
  336. {
  337. // Not dirty
  338. if ( !current || ( !current->CanUndo() && !current->GetDirty() ) )
  339. return;
  340. int fontsize = 14;
  341. RECT textRect;
  342. textRect.left = rcx + 5;
  343. textRect.right = rcx + rcw;
  344. textRect.top = rcy + 5;
  345. textRect.bottom = textRect.top + fontsize + 2;
  346. helper.DrawColoredText( "Arial", fontsize, FW_NORMAL, RGB( 100, 240, 255 ), textRect, "*" );
  347. }
  348. bool mxExpressionTray::PaintBackground( void )
  349. {
  350. redraw();
  351. return false;
  352. }
  353. void mxExpressionTray::DrawThumbNail( CExpClass *active, CExpression *current, CChoreoWidgetDrawHelper& helper, int rcx, int rcy, int rcw, int rch, int c, int selected, bool updateselection )
  354. {
  355. if ( !current )
  356. return;
  357. HDC dc = helper.GrabDC();
  358. helper.DrawFilledRect( GetSysColor( COLOR_BTNFACE ), rcx, rcy, rcw + rcx, rch + rcy );
  359. if ( current->m_Bitmap[ models->GetActiveModelIndex() ].valid )
  360. {
  361. DrawBitmapToDC( dc, rcx, rcy, rcw, rch - m_nDescriptionHeight, current->m_Bitmap[ models->GetActiveModelIndex() ] );
  362. helper.DrawOutlinedRect( RGB( 127, 127, 127 ), PS_SOLID, 1, rcx, rcy, rcx + rcw, rcy + rch - m_nDescriptionHeight );
  363. }
  364. DrawDirtyFlag( helper, current, rcx, rcy, rcw, rch );
  365. DrawExpressionDescription( helper, rcx, rcy, rcw, rch, current->name, current->description );
  366. if ( c == selected )
  367. {
  368. DrawExpressionFocusRect( helper, rcx, rcy, rcw, rch - m_nDescriptionHeight, RGB( 255, 100, 63 ) );
  369. if ( updateselection )
  370. {
  371. m_nPrevCell = -1;
  372. m_nCurCell = c;
  373. }
  374. if ( current->CanUndo() || current->CanRedo() )
  375. {
  376. if ( current->CanUndo() )
  377. {
  378. DrawButton( helper, c, FindButton( "undo" ) );
  379. }
  380. if ( current->CanRedo() )
  381. {
  382. DrawButton( helper, c, FindButton( "redo" ) );
  383. }
  384. RECT rc;
  385. rc.left = rcx + rcw - 2 * ( m_nButtonSquare + 4 );
  386. rc.top = rcy + m_nButtonSquare + 6;
  387. rc.right = rc.left + 2 * ( m_nButtonSquare + 4 );
  388. rc.bottom = rc.top + 15;
  389. helper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 200, 200, 200 ), rc,
  390. "%i/%i", current->UndoCurrent(), current->UndoLevels() );
  391. }
  392. }
  393. else
  394. {
  395. if ( current->GetSelected() )
  396. {
  397. DrawExpressionFocusRect( helper, rcx, rcy, rcw, rch - m_nDescriptionHeight, RGB( 127, 127, 220 ) );
  398. }
  399. }
  400. }
  401. void mxExpressionTray::redraw()
  402. {
  403. if ( !ToolCanDraw() )
  404. return;
  405. bool updateSelection = false;
  406. CExpClass *active = expressions->GetActiveClass();
  407. if ( active && active->GetNumExpressions() != m_nPreviousExpressionCount )
  408. {
  409. m_nTopOffset = 0;
  410. RepositionSlider();
  411. m_nPreviousExpressionCount = active->GetNumExpressions();
  412. }
  413. CChoreoWidgetDrawHelper helper( this, GetSysColor( COLOR_BTNFACE ) );
  414. HandleToolRedraw( helper );
  415. int w, h;
  416. w = w2();
  417. h = h2();
  418. if ( active )
  419. {
  420. RECT clipRect;
  421. helper.GetClientRect( clipRect );
  422. clipRect.top += TOP_GAP + GetCaptionHeight();
  423. helper.StartClipping( clipRect );
  424. if ( m_nLastNumExpressions != active->GetNumExpressions() )
  425. {
  426. m_nTopOffset = 0;
  427. m_nLastNumExpressions = active->GetNumExpressions();
  428. RepositionSlider();
  429. updateSelection = true;
  430. }
  431. int selected = active->GetSelectedExpression();
  432. int rcx, rcy, rcw, rch;
  433. int c = 0;
  434. while ( c < active->GetNumExpressions() )
  435. {
  436. if ( !ComputeRect( c, rcx, rcy, rcw, rch ) )
  437. {
  438. c++;
  439. continue;
  440. }
  441. CExpression *current = active->GetExpression( c );
  442. if ( !current )
  443. break;
  444. DrawThumbNail( active, current, helper, rcx, rcy, rcw, rch, c, selected, updateSelection );
  445. c++;
  446. }
  447. helper.StopClipping();
  448. }
  449. else
  450. {
  451. RECT rc;
  452. helper.GetClientRect( rc );
  453. // Arial 36 normal
  454. char sz[ 256 ];
  455. sprintf( sz, "No expression file loaded" );
  456. int pointsize = 18;
  457. int textlen = helper.CalcTextWidth( "Arial", pointsize, FW_NORMAL, sz );
  458. RECT rcText;
  459. rcText.top = ( rc.bottom - rc.top ) / 2 - pointsize / 2;
  460. rcText.bottom = rcText.top + pointsize + 10;
  461. int fullw = rc.right - rc.left;
  462. rcText.left = rc.left + ( fullw - textlen ) / 2;
  463. rcText.right = rcText.left + textlen;
  464. helper.DrawColoredText( "Arial", pointsize, FW_NORMAL, RGB( 80, 80, 80 ), rcText, sz );
  465. }
  466. // ValidateRect( (HWND)getHandle(), &rc );
  467. }
  468. int mxExpressionTray::GetCellUnderPosition( int x, int y )
  469. {
  470. CExpClass *active = expressions->GetActiveClass();
  471. if ( !active )
  472. return -1;
  473. int rcx, rcy, rcw, rch;
  474. int c = 0;
  475. while ( c < active->GetNumExpressions() )
  476. {
  477. if ( !ComputeRect( c, rcx, rcy, rcw, rch ) )
  478. {
  479. c++;
  480. continue;
  481. }
  482. if ( x >= rcx && x <= rcx + rcw &&
  483. y >= rcy && y <= rcy + rch )
  484. {
  485. return c;
  486. }
  487. c++;
  488. }
  489. return -1;
  490. }
  491. void mxExpressionTray::RepositionSlider( void )
  492. {
  493. int trueh = h2() - GetCaptionHeight();
  494. int heightpixels = trueh / m_nGranularity;
  495. int rangepixels = ComputePixelsNeeded() / m_nGranularity;
  496. if ( rangepixels < heightpixels )
  497. {
  498. m_nTopOffset = 0;
  499. slScrollbar->setVisible( false );
  500. }
  501. else
  502. {
  503. slScrollbar->setVisible( true );
  504. }
  505. slScrollbar->setBounds( w2() - 16, GetCaptionHeight() + TOP_GAP, 16, trueh - TOP_GAP );
  506. m_nTopOffset = max( 0, m_nTopOffset );
  507. m_nTopOffset = min( rangepixels, m_nTopOffset );
  508. slScrollbar->setRange( 0, rangepixels );
  509. slScrollbar->setValue( m_nTopOffset );
  510. slScrollbar->setPagesize( heightpixels );
  511. }
  512. void mxExpressionTray::AB( void )
  513. {
  514. if ( m_nPrevCell == -1 && m_nCurCell == -1 )
  515. return;
  516. CExpClass *active = expressions->GetActiveClass();
  517. if ( !active )
  518. return;
  519. if ( m_nPrevCell >= 0 && m_nPrevCell < active->GetNumExpressions() )
  520. {
  521. active->SelectExpression( m_nPrevCell );
  522. }
  523. }
  524. int mxExpressionTray::CountSelected( void )
  525. {
  526. CExpClass *active = expressions->GetActiveClass();
  527. if ( !active )
  528. return 0;
  529. int c = 0;
  530. for ( int i = 0; i < active->GetNumExpressions(); i++ )
  531. {
  532. CExpression *exp = active->GetExpression( i );
  533. if ( !exp )
  534. continue;
  535. if ( exp->GetSelected() )
  536. {
  537. c++;
  538. }
  539. }
  540. return c;
  541. }
  542. void mxExpressionTray::SetClickedCell( int cell )
  543. {
  544. m_nClickedCell = cell;
  545. }
  546. void mxExpressionTray::ShowRightClickMenu( int mx, int my )
  547. {
  548. CExpClass *active = expressions->GetActiveClass();
  549. if ( !active )
  550. return;
  551. mxPopupMenu *pop = new mxPopupMenu();
  552. Assert( pop );
  553. CExpression *exp = NULL;
  554. if ( m_nClickedCell != -1 )
  555. {
  556. exp = active->GetExpression( m_nClickedCell );
  557. }
  558. pop->add( "New Expression...", IDC_CONTEXT_NEWEXP );
  559. if ( exp )
  560. {
  561. pop->addSeparator();
  562. pop->add( va( "Edit '%s'...", exp->name ), IDC_CONTEXT_EDITEXP );
  563. pop->add( va( "Save '%s'", exp->name ), IDC_CONTEXT_SAVEEXP );
  564. if ( exp->CanUndo() || exp->CanRedo() )
  565. {
  566. pop->add( va( "Revert '%s'", exp->name ), IDC_CONTEXT_REVERT );
  567. }
  568. pop->addSeparator();
  569. pop->add( va( "Delete '%s'", exp->name ), IDC_CONTEXT_DELETEXP );
  570. pop->addSeparator();
  571. pop->add( va( "Re-create thumbnail for '%s'", exp->name ), IDC_CONTEXT_CREATEBITMAP );
  572. }
  573. pop->popup( this, mx, my );
  574. }
  575. //-----------------------------------------------------------------------------
  576. // Purpose:
  577. //-----------------------------------------------------------------------------
  578. void mxExpressionTray::DrawFocusRect( void )
  579. {
  580. HDC dc = GetDC( NULL );
  581. ::DrawFocusRect( dc, &m_rcFocus );
  582. ReleaseDC( NULL, dc );
  583. }
  584. static bool IsWindowOrChild( mxWindow *parent, HWND test )
  585. {
  586. HWND parentHwnd = (HWND)parent->getHandle();
  587. if ( test == parentHwnd ||
  588. IsChild( parentHwnd, test ) )
  589. {
  590. return true;
  591. }
  592. return false;
  593. }
  594. int mxExpressionTray::handleEvent (mxEvent *event)
  595. {
  596. MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
  597. int iret = 0;
  598. if ( HandleToolEvent( event ) )
  599. {
  600. return iret;
  601. }
  602. switch ( event->event )
  603. {
  604. case mxEvent::Action:
  605. {
  606. iret = 1;
  607. switch ( event->action )
  608. {
  609. default:
  610. iret = 0;
  611. break;
  612. case IDC_EXPRESSIONCLASS:
  613. {
  614. int index = g_pExpressionClass->getSelectedIndex();
  615. if ( index >= 0 )
  616. {
  617. CExpClass *current = expressions->GetClass( index );
  618. if ( current )
  619. {
  620. // Switch classname
  621. expressions->ActivateExpressionClass( current );
  622. current->SelectExpression( 0 );
  623. }
  624. }
  625. }
  626. break;
  627. case IDC_CONTEXT_NEWEXP:
  628. g_pFlexPanel->NewExpression();
  629. break;
  630. case IDC_CONTEXT_EDITEXP:
  631. if ( m_nClickedCell != -1 )
  632. {
  633. g_pFlexPanel->EditExpression();
  634. }
  635. break;
  636. case IDC_CONTEXT_REVERT:
  637. if ( m_nClickedCell != -1 )
  638. {
  639. g_pFlexPanel->RevertExpression( m_nClickedCell );
  640. }
  641. break;
  642. case IDC_CONTEXT_SAVEEXP:
  643. if ( m_nClickedCell != -1 )
  644. {
  645. g_pFlexPanel->SaveExpression( m_nClickedCell );
  646. }
  647. break;
  648. case IDC_CONTEXT_DELETEXP:
  649. if ( m_nClickedCell != -1 )
  650. {
  651. g_pControlPanel->DeleteExpression( m_nClickedCell );
  652. }
  653. break;
  654. case IDC_TRAYSCROLL:
  655. {
  656. if (event->modifiers == SB_THUMBTRACK)
  657. {
  658. int offset = event->height;
  659. slScrollbar->setValue( offset );
  660. m_nTopOffset = offset;
  661. redraw();
  662. }
  663. else if ( event->modifiers == SB_PAGEUP )
  664. {
  665. int offset = slScrollbar->getValue();
  666. offset -= m_nGranularity;
  667. offset = max( offset, slScrollbar->getMinValue() );
  668. slScrollbar->setValue( offset );
  669. InvalidateRect( (HWND)slScrollbar->getHandle(), NULL, TRUE );
  670. m_nTopOffset = offset;
  671. redraw();
  672. }
  673. else if ( event->modifiers == SB_PAGEDOWN )
  674. {
  675. int offset = slScrollbar->getValue();
  676. offset += m_nGranularity;
  677. offset = min( offset, slScrollbar->getMaxValue() );
  678. slScrollbar->setValue( offset );
  679. InvalidateRect( (HWND)slScrollbar->getHandle(), NULL, TRUE );
  680. m_nTopOffset = offset;
  681. redraw();
  682. }
  683. }
  684. break;
  685. case IDC_AB:
  686. {
  687. AB();
  688. }
  689. break;
  690. case IDC_THUMBNAIL_INCREASE:
  691. {
  692. ThumbnailIncrease();
  693. }
  694. break;
  695. case IDC_THUMBNAIL_DECREASE:
  696. {
  697. ThumbnailDecrease();
  698. }
  699. break;
  700. case IDC_CONTEXT_CREATEBITMAP:
  701. {
  702. if ( m_nClickedCell >= 0 )
  703. {
  704. CExpClass *active = expressions->GetActiveClass();
  705. if ( active )
  706. {
  707. CExpression *exp = active->GetExpression( m_nClickedCell );
  708. if ( exp )
  709. {
  710. active->SelectExpression( m_nClickedCell );
  711. exp->CreateNewBitmap( models->GetActiveModelIndex() );
  712. redraw();
  713. }
  714. }
  715. }
  716. }
  717. break;
  718. }
  719. break;
  720. }
  721. case mxEvent::MouseDown:
  722. {
  723. if ( !( event->buttons & mxEvent::MouseRightButton ) )
  724. {
  725. // Figure out cell #
  726. int cell = GetCellUnderPosition( event->x, event->y );
  727. CExpClass *active = expressions->GetActiveClass();
  728. if ( active )
  729. {
  730. if ( cell == m_nCurCell && cell >= 0 && cell < active->GetNumExpressions() )
  731. {
  732. mxETButton *btn = GetItemUnderCursor( event->x, event->y );
  733. if ( btn && btn->m_fnCallback )
  734. {
  735. (this->*(btn->m_fnCallback))( cell );
  736. return iret;
  737. }
  738. }
  739. if ( cell >= 0 && cell < active->GetNumExpressions() )
  740. {
  741. active->SelectExpression( cell, event->modifiers & mxEvent::KeyShift ? false : true );
  742. int cx, cy, cw, ch;
  743. if ( ComputeRect( cell, cx, cy, cw, ch ) )
  744. {
  745. m_bDragging = true;
  746. m_nDragCell = cell;
  747. m_nXStart = (short)event->x;
  748. m_nYStart = (short)event->y;
  749. m_rcFocus.left = cx;
  750. m_rcFocus.top = cy;
  751. m_rcFocus.right = cx + cw;
  752. m_rcFocus.bottom = cy + ch - m_nDescriptionHeight;
  753. POINT pt;
  754. pt.x = pt.y = 0;
  755. ClientToScreen( (HWND)getHandle(), &pt );
  756. OffsetRect( &m_rcFocus, pt.x, pt.y );
  757. m_rcOrig = m_rcFocus;
  758. DrawFocusRect();
  759. }
  760. }
  761. else
  762. {
  763. Deselect();
  764. active->DeselectExpression();
  765. redraw();
  766. }
  767. }
  768. }
  769. iret = 1;
  770. }
  771. break;
  772. case mxEvent::MouseDrag:
  773. {
  774. if ( m_bDragging )
  775. {
  776. // Draw drag line of some kind
  777. DrawFocusRect();
  778. // update pos
  779. m_rcFocus = m_rcOrig;
  780. OffsetRect( &m_rcFocus, ( (short)event->x - m_nXStart ),
  781. ( (short)event->y - m_nYStart ) );
  782. DrawFocusRect();
  783. }
  784. iret = 1;
  785. }
  786. break;
  787. case mxEvent::MouseUp:
  788. {
  789. iret = 1;
  790. if ( event->buttons & mxEvent::MouseRightButton )
  791. {
  792. SetClickedCell( GetCellUnderPosition( (short)event->x, (short)event->y ) );
  793. ShowRightClickMenu( (short)event->x, (short)event->y );
  794. return iret;
  795. }
  796. int cell = GetCellUnderPosition( event->x, event->y );
  797. CExpClass *active = expressions->GetActiveClass();
  798. if ( m_bDragging )
  799. {
  800. DrawFocusRect();
  801. m_bDragging = false;
  802. // See if we let go on top of the choreo view
  803. if ( active )
  804. {
  805. // Convert x, y to screen space
  806. POINT pt;
  807. pt.x = (short)event->x;
  808. pt.y = (short)event->y;
  809. ClientToScreen( (HWND)getHandle(), &pt );
  810. HWND maybeTool = WindowFromPoint( pt );
  811. // Now tell choreo view
  812. CExpression *exp = active->GetExpression( m_nDragCell );
  813. if ( exp && maybeTool )
  814. {
  815. if ( IsWindowOrChild( g_pChoreoView, maybeTool ) )
  816. {
  817. if ( g_pChoreoView->CreateExpressionEvent( pt.x, pt.y, active, exp ) )
  818. {
  819. return iret;
  820. }
  821. }
  822. if ( IsWindowOrChild( g_pExpressionTool, maybeTool ) )
  823. {
  824. if ( g_pExpressionTool->SetFlexAnimationTrackFromExpression( pt.x, pt.y, active, exp ) )
  825. {
  826. return iret;
  827. }
  828. }
  829. }
  830. }
  831. }
  832. if ( active )
  833. {
  834. // Over a new cell
  835. if ( cell >= 0 &&
  836. cell < active->GetNumExpressions() &&
  837. cell != m_nCurCell &&
  838. m_nCurCell != -1 )
  839. {
  840. // Swap cells
  841. CExpression *exp = active->GetExpression( m_nCurCell );
  842. if ( exp )
  843. {
  844. active->SwapExpressionOrder( m_nCurCell, cell );
  845. active->SetDirty( true );
  846. active->SelectExpression( cell );
  847. }
  848. }
  849. }
  850. }
  851. break;
  852. case mxEvent::Size:
  853. {
  854. int width = w2();
  855. int ch = GetCaptionHeight();
  856. g_pExpressionClass->setBounds( 5, 5 + ch, width - 120, 20 );
  857. m_pABButton->setBounds( width - 60, 4 + ch, 60, 16 );
  858. m_pThumbnailIncreaseButton->setBounds( width - 60 - 40, 4 + ch, 16, 16 );
  859. m_pThumbnailDecreaseButton->setBounds( width - 60 - 20, 4 + ch, 16, 16 );
  860. m_nTopOffset = 0;
  861. RepositionSlider();
  862. redraw();
  863. iret = 1;
  864. }
  865. break;
  866. case mxEvent::MouseWheeled:
  867. {
  868. // Figure out cell #
  869. POINT pt;
  870. pt.x = event->x;
  871. pt.y = event->y;
  872. ScreenToClient( (HWND)getHandle(), &pt );
  873. if ( event->height < 0 )
  874. {
  875. m_nTopOffset = min( m_nTopOffset + 10, slScrollbar->getMaxValue() );
  876. }
  877. else
  878. {
  879. m_nTopOffset = max( m_nTopOffset - 10, 0 );
  880. }
  881. RepositionSlider();
  882. redraw();
  883. iret = 1;
  884. }
  885. break;
  886. };
  887. if ( iret )
  888. {
  889. SetActiveTool( this );
  890. }
  891. return iret;
  892. }
  893. void mxExpressionTray::ET_Undo( int cell )
  894. {
  895. g_pControlPanel->UndoExpression( cell );
  896. }
  897. void mxExpressionTray::ET_Redo( int cell )
  898. {
  899. g_pControlPanel->RedoExpression( cell );
  900. }
  901. //-----------------------------------------------------------------------------
  902. // Purpose:
  903. //-----------------------------------------------------------------------------
  904. void mxExpressionTray::ThumbnailIncrease( void )
  905. {
  906. if ( m_nSnapshotWidth + THUMBNAIL_SIZE_STEP <= MAX_THUMBNAILSIZE )
  907. {
  908. m_nSnapshotWidth += THUMBNAIL_SIZE_STEP;
  909. g_viewerSettings.thumbnailsize = m_nSnapshotWidth;
  910. m_nSnapshotHeight = m_nSnapshotWidth + m_nDescriptionHeight;
  911. Con_Printf( "Thumbnail size %i x %i\n", m_nSnapshotWidth, m_nSnapshotWidth );
  912. redraw();
  913. }
  914. }
  915. //-----------------------------------------------------------------------------
  916. // Purpose:
  917. //-----------------------------------------------------------------------------
  918. void mxExpressionTray::ThumbnailDecrease( void )
  919. {
  920. if ( m_nSnapshotWidth - THUMBNAIL_SIZE_STEP >= MIN_THUMBNAILSIZE )
  921. {
  922. m_nSnapshotWidth -= THUMBNAIL_SIZE_STEP;
  923. g_viewerSettings.thumbnailsize = m_nSnapshotWidth;
  924. m_nSnapshotHeight = m_nSnapshotWidth + m_nDescriptionHeight;
  925. Con_Printf( "Thumbnail size %i x %i\n", m_nSnapshotWidth, m_nSnapshotWidth );
  926. redraw();
  927. }
  928. }
  929. //-----------------------------------------------------------------------------
  930. // Purpose:
  931. //-----------------------------------------------------------------------------
  932. void mxExpressionTray::RestoreThumbnailSize( void )
  933. {
  934. m_nSnapshotWidth = g_viewerSettings.thumbnailsize;
  935. m_nSnapshotWidth = max( MIN_THUMBNAILSIZE, m_nSnapshotWidth );
  936. m_nSnapshotWidth = min( MAX_THUMBNAILSIZE, m_nSnapshotWidth );
  937. g_viewerSettings.thumbnailsize = m_nSnapshotWidth;
  938. m_nSnapshotHeight = m_nSnapshotWidth + m_nDescriptionHeight;
  939. redraw();
  940. }
  941. void mxExpressionTray::ReloadBitmaps( void )
  942. {
  943. CExpClass *cl;
  944. int c = expressions->GetNumClasses();
  945. EnableStickySnapshotMode();
  946. for ( int i = 0 ; i < c; i++ )
  947. {
  948. cl = expressions->GetClass( i );
  949. if ( !cl )
  950. continue;
  951. cl->ReloadBitmaps();
  952. }
  953. DisableStickySnapshotMode();
  954. redraw();
  955. }
  956. bool IsUsingPerPlayerExpressions()
  957. {
  958. bool bPerPlayerExpressions = false;
  959. if ( CommandLine()->CheckParm( "-perplayerexpressions" ) )
  960. {
  961. bPerPlayerExpressions = true;
  962. }
  963. else
  964. {
  965. // Returns the search path, each path is separated by ;s. Returns the length of the string returned
  966. char pSearchPath[2048];
  967. if ( g_pFullFileSystem->GetSearchPath( "GAME", false, pSearchPath, sizeof(pSearchPath) ) )
  968. {
  969. Q_FixSlashes( pSearchPath );
  970. if ( Q_stristr( pSearchPath, "\\tf" ) )
  971. {
  972. bPerPlayerExpressions = true;
  973. }
  974. }
  975. }
  976. return bPerPlayerExpressions;
  977. }
  978. void mxExpressionTray::OnModelChanged()
  979. {
  980. if ( IsUsingPerPlayerExpressions() )
  981. {
  982. Msg( "Closing current phoneme set\n" );
  983. if ( !g_pControlPanel->Closeall() )
  984. return;
  985. // See if per-model overrides exist for this model
  986. char fn[ MAX_PATH ];
  987. Q_snprintf( fn, sizeof( fn ), "expressions/%s/phonemes/phonemes.txt", models->GetActiveModelName() );
  988. // Load appropriate classes
  989. char rootDir[ MAX_PATH ];
  990. Q_snprintf( rootDir, sizeof( rootDir ), "%s/phonemes/", models->GetActiveModelName() );
  991. FacePoser_SetPhonemeRootDir( rootDir );
  992. FacePoser_EnsurePhonemesLoaded();
  993. }
  994. ReloadBitmaps();
  995. RestoreThumbnailSize();
  996. }