Modified source engine (2017) developed by valve and leaked in 2020. Not for commercial purporses
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.

1212 lines
28 KiB

5 years ago
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "hlfaceposer.h"
#include <windows.h>
#include <stdio.h>
#include <mxtk/mxWindow.h>
#include <mxtk/mxScrollBar.h>
#include "mxexpressiontray.h"
#include "expressions.h"
#include "expclass.h"
#include "ControlPanel.h"
#include "FlexPanel.h"
#include <mxtk/mxPopupMenu.h>
#include "ChoreoView.h"
#include "StudioModel.h"
#include "ExpressionProperties.h"
#include "InputProperties.h"
#include "ViewerSettings.h"
#include "mxExpressionTab.h"
#include "choreowidgetdrawhelper.h"
#include "ExpressionTool.h"
#include "faceposer_models.h"
#include "tier0/icommandline.h"
#include "filesystem.h"
#define MAX_THUMBNAILSIZE 256
#define MIN_THUMBNAILSIZE 64
#define THUMBNAIL_SIZE_STEP 4
#define DEFAULT_THUMBNAIL_SIZE 128
#define TOP_GAP 45
mxExpressionTray *g_pExpressionTrayTool = 0;
mxExpressionTray::mxExpressionTray( mxWindow *parent, int id /*=0*/ )
: IFacePoserToolWindow( "ExpressionTrayTool", "Expressions" ), mxWindow( parent, 0, 0, 0, 0, "ExpressionTrayTool", id )
{
setId( id );
m_nTopOffset = 0;
slScrollbar = new mxScrollbar( this, 0, 0, 18, 100, IDC_TRAYSCROLL, mxScrollbar::Vertical );
m_nLastNumExpressions = -1;
m_nGranularity = 10;
m_nPrevCell = -1;
m_nCurCell = -1;
m_nClickedCell = -1;
m_nButtonSquare = 16;
m_nGap = 4;
m_nDescriptionHeight = 34;
m_nSnapshotWidth = g_viewerSettings.thumbnailsize;
m_nSnapshotWidth = max( MIN_THUMBNAILSIZE, m_nSnapshotWidth );
m_nSnapshotWidth = min( MAX_THUMBNAILSIZE, m_nSnapshotWidth );
g_viewerSettings.thumbnailsize = m_nSnapshotWidth;
m_nSnapshotHeight = m_nSnapshotWidth + m_nDescriptionHeight;
m_pButtons = NULL;
m_nPreviousExpressionCount = -1;
m_bDragging = false;
m_nDragCell = -1;
CreateButtons();
g_pExpressionClass = new mxExpressionTab( this, 5, 5, 500, 20, IDC_EXPRESSIONCLASS );
m_pABButton = new mxButton( this, 520, 8, 50, 18, "A/B", IDC_AB );
m_pThumbnailIncreaseButton = new mxButton( this, 0, 0, 18, 18, "+", IDC_THUMBNAIL_INCREASE );
m_pThumbnailDecreaseButton = new mxButton( this, 0, 0, 18, 18, "-", IDC_THUMBNAIL_DECREASE );
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : mxExpressionTray::~mxExpressionTray
//-----------------------------------------------------------------------------
mxExpressionTray::~mxExpressionTray ( void )
{
DeleteAllButtons();
g_pExpressionTrayTool = NULL;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : cellsize -
//-----------------------------------------------------------------------------
void mxExpressionTray::SetCellSize( int cellsize )
{
m_nSnapshotWidth = cellsize;
m_nSnapshotHeight = cellsize + m_nDescriptionHeight;
DeleteAllButtons();
CreateButtons();
redraw();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void mxExpressionTray::Deselect( void )
{
CExpClass *active = expressions->GetActiveClass();
if ( active )
{
for ( int i = 0 ; i < active->GetNumExpressions(); i++ )
{
CExpression *exp = active->GetExpression( i );
if ( exp )
{
exp->SetSelected( false );
}
}
}
m_nCurCell = m_nPrevCell = -1;
redraw();
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : exp -
//-----------------------------------------------------------------------------
void mxExpressionTray::Select( int exp, bool deselect /*=true*/ )
{
int oldcell = m_nCurCell;
if ( deselect )
{
Deselect();
}
m_nPrevCell = oldcell;
m_nCurCell = exp;
if ( m_nCurCell >= 0 )
{
CExpClass *active = expressions->GetActiveClass();
if ( active )
{
CExpression *exp = active->GetExpression( m_nCurCell );
if ( exp )
{
exp->SetSelected( true );
}
}
}
redraw();
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *btn -
//-----------------------------------------------------------------------------
void mxExpressionTray::AddButton( const char *name, const char *tooltip, const char *bitmap, ETMEMBERFUNC pfnCallback,
bool active, int x, int y, int w, int h )
{
mxETButton *btn = new mxETButton;
strcpy( btn->m_szName, name );
strcpy( btn->m_szToolTip, tooltip );
btn->m_bActive = active;
btn->m_rc.left = x;
btn->m_rc.top = y;
btn->m_rc.right = x + w;
btn->m_rc.bottom = y + h;
btn->m_pImage = new mxbitmapdata_t;
Assert( btn->m_pImage );
btn->m_pImage->valid = false;
LoadBitmapFromFile( bitmap, *btn->m_pImage );
btn->m_fnCallback = pfnCallback;
btn->next = m_pButtons;
m_pButtons = btn;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void mxExpressionTray::CreateButtons( void )
{
int x = m_nSnapshotWidth - 2 * ( m_nButtonSquare + 4 );
int y = 4;
AddButton( "undo", "Undo", "gfx/hlfaceposer/undo.bmp", &mxExpressionTray::ET_Undo, true, x, y, m_nButtonSquare, m_nButtonSquare );
x += ( m_nButtonSquare + 4 );
AddButton( "redo", "Redo", "gfx/hlfaceposer/redo.bmp", &mxExpressionTray::ET_Redo, true, x, y, m_nButtonSquare, m_nButtonSquare );
}
void mxExpressionTray::ActivateButton( const char *name, bool active )
{
mxETButton *btn = FindButton( name );
if ( !name )
return;
btn->m_bActive = active;
}
mxExpressionTray::mxETButton *mxExpressionTray::FindButton( const char *name )
{
mxETButton *p = m_pButtons;
while ( p )
{
if ( !stricmp( p->m_szName, name ) )
return p;
p = p->next;
}
return NULL;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void mxExpressionTray::DeleteAllButtons( void )
{
mxETButton *p = m_pButtons, *n;
while ( p )
{
n = p->next;
delete p->m_pImage;
delete p;
p = n;
}
m_pButtons = NULL;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : x -
// y -
// Output : mxExpressionTray::mxETButton
//-----------------------------------------------------------------------------
mxExpressionTray::mxETButton *mxExpressionTray::GetItemUnderCursor( int x, int y )
{
// Convert to cell space
int cell = GetCellUnderPosition( x, y );
if ( cell == -1 )
{
return NULL;
}
// Cell is off screen?
int cx, cy, cw, ch;
if ( !ComputeRect( cell, cx, cy, cw, ch ) )
{
return NULL;
}
mxETButton *p = m_pButtons;
while ( p )
{
if ( p->m_bActive &&
x >= cx &&
x <= cx + cw &&
y >= cy &&
y <= cy + ch )
{
// In-side cell
int cellx = x - cx;
int celly = y - cy;
if ( cellx >= p->m_rc.left &&
cellx <= p->m_rc.right &&
celly >= p->m_rc.top &&
celly <= p->m_rc.bottom )
{
return p;
}
}
p = p->next;
}
return NULL;
}
void mxExpressionTray::DrawButton( CChoreoWidgetDrawHelper& helper, int cell, mxETButton *btn )
{
if ( !btn || !btn->m_pImage || !btn->m_pImage->valid )
return;
if ( !btn->m_bActive )
return;
int x, y, w, h;
if ( !ComputeRect( cell, x, y, w, h ) )
return;
x += btn->m_rc.left;
y += btn->m_rc.top;
w = btn->m_rc.right - btn->m_rc.left;
h = btn->m_rc.bottom - btn->m_rc.top;
HDC dc = helper.GrabDC();
DrawBitmapToDC( dc, x, y, w, h, *btn->m_pImage );
helper.DrawOutlinedRect( RGB( 170, 170, 170 ), PS_SOLID, 1, x, y, x + w, y + h );
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : int
//-----------------------------------------------------------------------------
int mxExpressionTray::ComputePixelsNeeded( void )
{
CExpClass *active = expressions->GetActiveClass();
if ( !active )
return 100;
// Remove scroll bar
int w = this->w2() - 16;
int colsperrow;
colsperrow = ( w - m_nGap ) / ( m_nSnapshotWidth + m_nGap );
// At least one
colsperrow = max( 1, colsperrow );
int rowsneeded = ( ( active->GetNumExpressions() + colsperrow - 1 ) / colsperrow );
return rowsneeded * ( m_nSnapshotHeight + m_nGap ) + m_nGap + TOP_GAP + GetCaptionHeight();
}
bool mxExpressionTray::ComputeRect( int cell, int& rcx, int& rcy, int& rcw, int& rch )
{
// Remove scroll bar
int w = this->w2() - 16;
int colsperrow;
colsperrow = ( w - m_nGap ) / ( m_nSnapshotWidth + m_nGap );
// At least one
colsperrow = max( 1, colsperrow );
int row, col;
row = cell / colsperrow;
col = cell % colsperrow;
// don't allow partial columns
rcx = m_nGap + col * ( m_nSnapshotWidth + m_nGap );
rcy = GetCaptionHeight() + TOP_GAP + ( -m_nTopOffset * m_nGranularity ) + m_nGap + row * ( m_nSnapshotHeight + m_nGap );
// Starts off screen
if ( rcx < 0 )
return false;
// Ends off screen
if ( rcx + m_nSnapshotWidth + m_nGap > this->w2() )
return false;
// Allow partial in y direction
if ( rcy > this->h2() )
return false;
if ( rcy + m_nSnapshotHeight + m_nGap < 0 )
return false;
// Some portion is onscreen
rcw = m_nSnapshotWidth;
rch = m_nSnapshotHeight;
return true;
}
void mxExpressionTray::DrawExpressionFocusRect( CChoreoWidgetDrawHelper& helper, int x, int y, int w, int h, COLORREF clr )
{
helper.DrawOutlinedRect( clr, PS_SOLID, 4, x, y, x + w, y + h );
}
void mxExpressionTray::DrawExpressionDescription( CChoreoWidgetDrawHelper& helper, int x, int y, int w, int h, const char *expressionname, const char *description )
{
int textheight = 15;
RECT textRect;
textRect.left = x + 5;
textRect.top = y + h - 2 * textheight - 12;
textRect.right = x + w - 10;
textRect.bottom = y + h - 12;
helper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 0, 0, 0 ), textRect, "%s", expressionname );
// DrawText( hdc, expressionname, strlen( expressionname ), &textRect, DT_NOPREFIX | DT_SINGLELINE | DT_LEFT | DT_VCENTER | DT_WORD_ELLIPSIS );
OffsetRect( &textRect, 0, textheight );
helper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 63, 63, 63 ), textRect, "%s", description );
// DrawText( hdc, description, strlen( description ), &textRect, DT_NOPREFIX | DT_SINGLELINE | DT_LEFT | DT_VCENTER | DT_WORD_ELLIPSIS );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : dc -
// current -
// rcx -
// rcy -
// rcw -
// rch -
//-----------------------------------------------------------------------------
void mxExpressionTray::DrawDirtyFlag( CChoreoWidgetDrawHelper& helper, CExpression *current, int rcx, int rcy, int rcw, int rch )
{
// Not dirty
if ( !current || ( !current->CanUndo() && !current->GetDirty() ) )
return;
int fontsize = 14;
RECT textRect;
textRect.left = rcx + 5;
textRect.right = rcx + rcw;
textRect.top = rcy + 5;
textRect.bottom = textRect.top + fontsize + 2;
helper.DrawColoredText( "Arial", fontsize, FW_NORMAL, RGB( 100, 240, 255 ), textRect, "*" );
}
bool mxExpressionTray::PaintBackground( void )
{
redraw();
return false;
}
void mxExpressionTray::DrawThumbNail( CExpClass *active, CExpression *current, CChoreoWidgetDrawHelper& helper, int rcx, int rcy, int rcw, int rch, int c, int selected, bool updateselection )
{
if ( !current )
return;
HDC dc = helper.GrabDC();
helper.DrawFilledRect( GetSysColor( COLOR_BTNFACE ), rcx, rcy, rcw + rcx, rch + rcy );
if ( current->m_Bitmap[ models->GetActiveModelIndex() ].valid )
{
DrawBitmapToDC( dc, rcx, rcy, rcw, rch - m_nDescriptionHeight, current->m_Bitmap[ models->GetActiveModelIndex() ] );
helper.DrawOutlinedRect( RGB( 127, 127, 127 ), PS_SOLID, 1, rcx, rcy, rcx + rcw, rcy + rch - m_nDescriptionHeight );
}
DrawDirtyFlag( helper, current, rcx, rcy, rcw, rch );
DrawExpressionDescription( helper, rcx, rcy, rcw, rch, current->name, current->description );
if ( c == selected )
{
DrawExpressionFocusRect( helper, rcx, rcy, rcw, rch - m_nDescriptionHeight, RGB( 255, 100, 63 ) );
if ( updateselection )
{
m_nPrevCell = -1;
m_nCurCell = c;
}
if ( current->CanUndo() || current->CanRedo() )
{
if ( current->CanUndo() )
{
DrawButton( helper, c, FindButton( "undo" ) );
}
if ( current->CanRedo() )
{
DrawButton( helper, c, FindButton( "redo" ) );
}
RECT rc;
rc.left = rcx + rcw - 2 * ( m_nButtonSquare + 4 );
rc.top = rcy + m_nButtonSquare + 6;
rc.right = rc.left + 2 * ( m_nButtonSquare + 4 );
rc.bottom = rc.top + 15;
helper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 200, 200, 200 ), rc,
"%i/%i", current->UndoCurrent(), current->UndoLevels() );
}
}
else
{
if ( current->GetSelected() )
{
DrawExpressionFocusRect( helper, rcx, rcy, rcw, rch - m_nDescriptionHeight, RGB( 127, 127, 220 ) );
}
}
}
void mxExpressionTray::redraw()
{
if ( !ToolCanDraw() )
return;
bool updateSelection = false;
CExpClass *active = expressions->GetActiveClass();
if ( active && active->GetNumExpressions() != m_nPreviousExpressionCount )
{
m_nTopOffset = 0;
RepositionSlider();
m_nPreviousExpressionCount = active->GetNumExpressions();
}
CChoreoWidgetDrawHelper helper( this, GetSysColor( COLOR_BTNFACE ) );
HandleToolRedraw( helper );
int w, h;
w = w2();
h = h2();
if ( active )
{
RECT clipRect;
helper.GetClientRect( clipRect );
clipRect.top += TOP_GAP + GetCaptionHeight();
helper.StartClipping( clipRect );
if ( m_nLastNumExpressions != active->GetNumExpressions() )
{
m_nTopOffset = 0;
m_nLastNumExpressions = active->GetNumExpressions();
RepositionSlider();
updateSelection = true;
}
int selected = active->GetSelectedExpression();
int rcx, rcy, rcw, rch;
int c = 0;
while ( c < active->GetNumExpressions() )
{
if ( !ComputeRect( c, rcx, rcy, rcw, rch ) )
{
c++;
continue;
}
CExpression *current = active->GetExpression( c );
if ( !current )
break;
DrawThumbNail( active, current, helper, rcx, rcy, rcw, rch, c, selected, updateSelection );
c++;
}
helper.StopClipping();
}
else
{
RECT rc;
helper.GetClientRect( rc );
// Arial 36 normal
char sz[ 256 ];
sprintf( sz, "No expression file loaded" );
int pointsize = 18;
int textlen = helper.CalcTextWidth( "Arial", pointsize, FW_NORMAL, sz );
RECT rcText;
rcText.top = ( rc.bottom - rc.top ) / 2 - pointsize / 2;
rcText.bottom = rcText.top + pointsize + 10;
int fullw = rc.right - rc.left;
rcText.left = rc.left + ( fullw - textlen ) / 2;
rcText.right = rcText.left + textlen;
helper.DrawColoredText( "Arial", pointsize, FW_NORMAL, RGB( 80, 80, 80 ), rcText, sz );
}
// ValidateRect( (HWND)getHandle(), &rc );
}
int mxExpressionTray::GetCellUnderPosition( int x, int y )
{
CExpClass *active = expressions->GetActiveClass();
if ( !active )
return -1;
int rcx, rcy, rcw, rch;
int c = 0;
while ( c < active->GetNumExpressions() )
{
if ( !ComputeRect( c, rcx, rcy, rcw, rch ) )
{
c++;
continue;
}
if ( x >= rcx && x <= rcx + rcw &&
y >= rcy && y <= rcy + rch )
{
return c;
}
c++;
}
return -1;
}
void mxExpressionTray::RepositionSlider( void )
{
int trueh = h2() - GetCaptionHeight();
int heightpixels = trueh / m_nGranularity;
int rangepixels = ComputePixelsNeeded() / m_nGranularity;
if ( rangepixels < heightpixels )
{
m_nTopOffset = 0;
slScrollbar->setVisible( false );
}
else
{
slScrollbar->setVisible( true );
}
slScrollbar->setBounds( w2() - 16, GetCaptionHeight() + TOP_GAP, 16, trueh - TOP_GAP );
m_nTopOffset = max( 0, m_nTopOffset );
m_nTopOffset = min( rangepixels, m_nTopOffset );
slScrollbar->setRange( 0, rangepixels );
slScrollbar->setValue( m_nTopOffset );
slScrollbar->setPagesize( heightpixels );
}
void mxExpressionTray::AB( void )
{
if ( m_nPrevCell == -1 && m_nCurCell == -1 )
return;
CExpClass *active = expressions->GetActiveClass();
if ( !active )
return;
if ( m_nPrevCell >= 0 && m_nPrevCell < active->GetNumExpressions() )
{
active->SelectExpression( m_nPrevCell );
}
}
int mxExpressionTray::CountSelected( void )
{
CExpClass *active = expressions->GetActiveClass();
if ( !active )
return 0;
int c = 0;
for ( int i = 0; i < active->GetNumExpressions(); i++ )
{
CExpression *exp = active->GetExpression( i );
if ( !exp )
continue;
if ( exp->GetSelected() )
{
c++;
}
}
return c;
}
void mxExpressionTray::SetClickedCell( int cell )
{
m_nClickedCell = cell;
}
void mxExpressionTray::ShowRightClickMenu( int mx, int my )
{
CExpClass *active = expressions->GetActiveClass();
if ( !active )
return;
mxPopupMenu *pop = new mxPopupMenu();
Assert( pop );
CExpression *exp = NULL;
if ( m_nClickedCell != -1 )
{
exp = active->GetExpression( m_nClickedCell );
}
pop->add( "New Expression...", IDC_CONTEXT_NEWEXP );
if ( exp )
{
pop->addSeparator();
pop->add( va( "Edit '%s'...", exp->name ), IDC_CONTEXT_EDITEXP );
pop->add( va( "Save '%s'", exp->name ), IDC_CONTEXT_SAVEEXP );
if ( exp->CanUndo() || exp->CanRedo() )
{
pop->add( va( "Revert '%s'", exp->name ), IDC_CONTEXT_REVERT );
}
pop->addSeparator();
pop->add( va( "Delete '%s'", exp->name ), IDC_CONTEXT_DELETEXP );
pop->addSeparator();
pop->add( va( "Re-create thumbnail for '%s'", exp->name ), IDC_CONTEXT_CREATEBITMAP );
}
pop->popup( this, mx, my );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void mxExpressionTray::DrawFocusRect( void )
{
HDC dc = GetDC( NULL );
::DrawFocusRect( dc, &m_rcFocus );
ReleaseDC( NULL, dc );
}
static bool IsWindowOrChild( mxWindow *parent, HWND test )
{
HWND parentHwnd = (HWND)parent->getHandle();
if ( test == parentHwnd ||
IsChild( parentHwnd, test ) )
{
return true;
}
return false;
}
int mxExpressionTray::handleEvent (mxEvent *event)
{
MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
int iret = 0;
if ( HandleToolEvent( event ) )
{
return iret;
}
switch ( event->event )
{
case mxEvent::Action:
{
iret = 1;
switch ( event->action )
{
default:
iret = 0;
break;
case IDC_EXPRESSIONCLASS:
{
int index = g_pExpressionClass->getSelectedIndex();
if ( index >= 0 )
{
CExpClass *current = expressions->GetClass( index );
if ( current )
{
// Switch classname
expressions->ActivateExpressionClass( current );
current->SelectExpression( 0 );
}
}
}
break;
case IDC_CONTEXT_NEWEXP:
g_pFlexPanel->NewExpression();
break;
case IDC_CONTEXT_EDITEXP:
if ( m_nClickedCell != -1 )
{
g_pFlexPanel->EditExpression();
}
break;
case IDC_CONTEXT_REVERT:
if ( m_nClickedCell != -1 )
{
g_pFlexPanel->RevertExpression( m_nClickedCell );
}
break;
case IDC_CONTEXT_SAVEEXP:
if ( m_nClickedCell != -1 )
{
g_pFlexPanel->SaveExpression( m_nClickedCell );
}
break;
case IDC_CONTEXT_DELETEXP:
if ( m_nClickedCell != -1 )
{
g_pControlPanel->DeleteExpression( m_nClickedCell );
}
break;
case IDC_TRAYSCROLL:
{
if (event->modifiers == SB_THUMBTRACK)
{
int offset = event->height;
slScrollbar->setValue( offset );
m_nTopOffset = offset;
redraw();
}
else if ( event->modifiers == SB_PAGEUP )
{
int offset = slScrollbar->getValue();
offset -= m_nGranularity;
offset = max( offset, slScrollbar->getMinValue() );
slScrollbar->setValue( offset );
InvalidateRect( (HWND)slScrollbar->getHandle(), NULL, TRUE );
m_nTopOffset = offset;
redraw();
}
else if ( event->modifiers == SB_PAGEDOWN )
{
int offset = slScrollbar->getValue();
offset += m_nGranularity;
offset = min( offset, slScrollbar->getMaxValue() );
slScrollbar->setValue( offset );
InvalidateRect( (HWND)slScrollbar->getHandle(), NULL, TRUE );
m_nTopOffset = offset;
redraw();
}
}
break;
case IDC_AB:
{
AB();
}
break;
case IDC_THUMBNAIL_INCREASE:
{
ThumbnailIncrease();
}
break;
case IDC_THUMBNAIL_DECREASE:
{
ThumbnailDecrease();
}
break;
case IDC_CONTEXT_CREATEBITMAP:
{
if ( m_nClickedCell >= 0 )
{
CExpClass *active = expressions->GetActiveClass();
if ( active )
{
CExpression *exp = active->GetExpression( m_nClickedCell );
if ( exp )
{
active->SelectExpression( m_nClickedCell );
exp->CreateNewBitmap( models->GetActiveModelIndex() );
redraw();
}
}
}
}
break;
}
break;
}
case mxEvent::MouseDown:
{
if ( !( event->buttons & mxEvent::MouseRightButton ) )
{
// Figure out cell #
int cell = GetCellUnderPosition( event->x, event->y );
CExpClass *active = expressions->GetActiveClass();
if ( active )
{
if ( cell == m_nCurCell && cell >= 0 && cell < active->GetNumExpressions() )
{
mxETButton *btn = GetItemUnderCursor( event->x, event->y );
if ( btn && btn->m_fnCallback )
{
(this->*(btn->m_fnCallback))( cell );
return iret;
}
}
if ( cell >= 0 && cell < active->GetNumExpressions() )
{
active->SelectExpression( cell, event->modifiers & mxEvent::KeyShift ? false : true );
int cx, cy, cw, ch;
if ( ComputeRect( cell, cx, cy, cw, ch ) )
{
m_bDragging = true;
m_nDragCell = cell;
m_nXStart = (short)event->x;
m_nYStart = (short)event->y;
m_rcFocus.left = cx;
m_rcFocus.top = cy;
m_rcFocus.right = cx + cw;
m_rcFocus.bottom = cy + ch - m_nDescriptionHeight;
POINT pt;
pt.x = pt.y = 0;
ClientToScreen( (HWND)getHandle(), &pt );
OffsetRect( &m_rcFocus, pt.x, pt.y );
m_rcOrig = m_rcFocus;
DrawFocusRect();
}
}
else
{
Deselect();
active->DeselectExpression();
redraw();
}
}
}
iret = 1;
}
break;
case mxEvent::MouseDrag:
{
if ( m_bDragging )
{
// Draw drag line of some kind
DrawFocusRect();
// update pos
m_rcFocus = m_rcOrig;
OffsetRect( &m_rcFocus, ( (short)event->x - m_nXStart ),
( (short)event->y - m_nYStart ) );
DrawFocusRect();
}
iret = 1;
}
break;
case mxEvent::MouseUp:
{
iret = 1;
if ( event->buttons & mxEvent::MouseRightButton )
{
SetClickedCell( GetCellUnderPosition( (short)event->x, (short)event->y ) );
ShowRightClickMenu( (short)event->x, (short)event->y );
return iret;
}
int cell = GetCellUnderPosition( event->x, event->y );
CExpClass *active = expressions->GetActiveClass();
if ( m_bDragging )
{
DrawFocusRect();
m_bDragging = false;
// See if we let go on top of the choreo view
if ( active )
{
// Convert x, y to screen space
POINT pt;
pt.x = (short)event->x;
pt.y = (short)event->y;
ClientToScreen( (HWND)getHandle(), &pt );
HWND maybeTool = WindowFromPoint( pt );
// Now tell choreo view
CExpression *exp = active->GetExpression( m_nDragCell );
if ( exp && maybeTool )
{
if ( IsWindowOrChild( g_pChoreoView, maybeTool ) )
{
if ( g_pChoreoView->CreateExpressionEvent( pt.x, pt.y, active, exp ) )
{
return iret;
}
}
if ( IsWindowOrChild( g_pExpressionTool, maybeTool ) )
{
if ( g_pExpressionTool->SetFlexAnimationTrackFromExpression( pt.x, pt.y, active, exp ) )
{
return iret;
}
}
}
}
}
if ( active )
{
// Over a new cell
if ( cell >= 0 &&
cell < active->GetNumExpressions() &&
cell != m_nCurCell &&
m_nCurCell != -1 )
{
// Swap cells
CExpression *exp = active->GetExpression( m_nCurCell );
if ( exp )
{
active->SwapExpressionOrder( m_nCurCell, cell );
active->SetDirty( true );
active->SelectExpression( cell );
}
}
}
}
break;
case mxEvent::Size:
{
int width = w2();
int ch = GetCaptionHeight();
g_pExpressionClass->setBounds( 5, 5 + ch, width - 120, 20 );
m_pABButton->setBounds( width - 60, 4 + ch, 60, 16 );
m_pThumbnailIncreaseButton->setBounds( width - 60 - 40, 4 + ch, 16, 16 );
m_pThumbnailDecreaseButton->setBounds( width - 60 - 20, 4 + ch, 16, 16 );
m_nTopOffset = 0;
RepositionSlider();
redraw();
iret = 1;
}
break;
case mxEvent::MouseWheeled:
{
// Figure out cell #
POINT pt;
pt.x = event->x;
pt.y = event->y;
ScreenToClient( (HWND)getHandle(), &pt );
if ( event->height < 0 )
{
m_nTopOffset = min( m_nTopOffset + 10, slScrollbar->getMaxValue() );
}
else
{
m_nTopOffset = max( m_nTopOffset - 10, 0 );
}
RepositionSlider();
redraw();
iret = 1;
}
break;
};
if ( iret )
{
SetActiveTool( this );
}
return iret;
}
void mxExpressionTray::ET_Undo( int cell )
{
g_pControlPanel->UndoExpression( cell );
}
void mxExpressionTray::ET_Redo( int cell )
{
g_pControlPanel->RedoExpression( cell );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void mxExpressionTray::ThumbnailIncrease( void )
{
if ( m_nSnapshotWidth + THUMBNAIL_SIZE_STEP <= MAX_THUMBNAILSIZE )
{
m_nSnapshotWidth += THUMBNAIL_SIZE_STEP;
g_viewerSettings.thumbnailsize = m_nSnapshotWidth;
m_nSnapshotHeight = m_nSnapshotWidth + m_nDescriptionHeight;
Con_Printf( "Thumbnail size %i x %i\n", m_nSnapshotWidth, m_nSnapshotWidth );
redraw();
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void mxExpressionTray::ThumbnailDecrease( void )
{
if ( m_nSnapshotWidth - THUMBNAIL_SIZE_STEP >= MIN_THUMBNAILSIZE )
{
m_nSnapshotWidth -= THUMBNAIL_SIZE_STEP;
g_viewerSettings.thumbnailsize = m_nSnapshotWidth;
m_nSnapshotHeight = m_nSnapshotWidth + m_nDescriptionHeight;
Con_Printf( "Thumbnail size %i x %i\n", m_nSnapshotWidth, m_nSnapshotWidth );
redraw();
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void mxExpressionTray::RestoreThumbnailSize( void )
{
m_nSnapshotWidth = g_viewerSettings.thumbnailsize;
m_nSnapshotWidth = max( MIN_THUMBNAILSIZE, m_nSnapshotWidth );
m_nSnapshotWidth = min( MAX_THUMBNAILSIZE, m_nSnapshotWidth );
g_viewerSettings.thumbnailsize = m_nSnapshotWidth;
m_nSnapshotHeight = m_nSnapshotWidth + m_nDescriptionHeight;
redraw();
}
void mxExpressionTray::ReloadBitmaps( void )
{
CExpClass *cl;
int c = expressions->GetNumClasses();
EnableStickySnapshotMode();
for ( int i = 0 ; i < c; i++ )
{
cl = expressions->GetClass( i );
if ( !cl )
continue;
cl->ReloadBitmaps();
}
DisableStickySnapshotMode();
redraw();
}
bool IsUsingPerPlayerExpressions()
{
bool bPerPlayerExpressions = false;
if ( CommandLine()->CheckParm( "-perplayerexpressions" ) )
{
bPerPlayerExpressions = true;
}
else
{
// Returns the search path, each path is separated by ;s. Returns the length of the string returned
char pSearchPath[2048];
if ( g_pFullFileSystem->GetSearchPath( "GAME", false, pSearchPath, sizeof(pSearchPath) ) )
{
Q_FixSlashes( pSearchPath );
if ( Q_stristr( pSearchPath, "\\tf" ) )
{
bPerPlayerExpressions = true;
}
}
}
return bPerPlayerExpressions;
}
void mxExpressionTray::OnModelChanged()
{
if ( IsUsingPerPlayerExpressions() )
{
Msg( "Closing current phoneme set\n" );
if ( !g_pControlPanel->Closeall() )
return;
// See if per-model overrides exist for this model
char fn[ MAX_PATH ];
Q_snprintf( fn, sizeof( fn ), "expressions/%s/phonemes/phonemes.txt", models->GetActiveModelName() );
// Load appropriate classes
char rootDir[ MAX_PATH ];
Q_snprintf( rootDir, sizeof( rootDir ), "%s/phonemes/", models->GetActiveModelName() );
FacePoser_SetPhonemeRootDir( rootDir );
FacePoser_EnsurePhonemesLoaded();
}
ReloadBitmaps();
RestoreThumbnailSize();
}