//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ // //=============================================================================// #include #include "hlfaceposer.h" #include "GestureTool.h" #include "mdlviewer.h" #include "choreowidgetdrawhelper.h" #include "TimelineItem.h" #include "expressions.h" #include "expclass.h" #include "choreoevent.h" #include "StudioModel.h" #include "choreoscene.h" #include "choreoactor.h" #include "choreochannel.h" #include "ChoreoView.h" #include "InputProperties.h" #include "ControlPanel.h" #include "FlexPanel.h" #include "mxExpressionTray.h" #include "ExpressionProperties.h" #include "tier1/strtools.h" #include "faceposer_models.h" #include "UtlBuffer.h" #include "filesystem.h" #include "iscenetokenprocessor.h" #include "choreoviewcolors.h" #include "MatSysWin.h" GestureTool *g_pGestureTool = 0; #define TRAY_HEIGHT 20 #define TRAY_ITEM_INSET 10 #define TAG_TOP ( TRAY_HEIGHT + 32 ) #define TAG_BOTTOM ( TAG_TOP + 20 ) #define MAX_TIME_ZOOM 1000 // 10% per step #define TIME_ZOOM_STEP 2 float SnapTime( float input, float granularity ); GestureTool::GestureTool( mxWindow *parent ) : IFacePoserToolWindow( "GestureTool", "Gesture" ), mxWindow( parent, 0, 0, 0, 0 ) { m_bSuppressLayout = false; SetAutoProcess( true ); m_nFocusEventGlobalID = -1; m_flScrub = 0.0f; m_flScrubTarget = 0.0f; m_nDragType = DRAGTYPE_NONE; m_nClickedX = 0; m_nClickedY = 0; m_hPrevCursor = 0; m_nStartX = 0; m_nStartY = 0; m_pLastEvent = NULL; m_nMousePos[ 0 ] = m_nMousePos[ 1 ] = 0; m_nMinX = 0; m_nMaxX = 0; m_bUseBounds = false; m_bLayoutIsValid = false; m_flPixelsPerSecond = 500.0f; m_flLastDuration = 0.0f; m_nScrollbarHeight = 12; m_flLeftOffset = 0.0f; m_nLastHPixelsNeeded = -1; m_pHorzScrollBar = new mxScrollbar( this, 0, 0, 18, 100, IDC_GESTUREHSCROLL, mxScrollbar::Horizontal ); m_pHorzScrollBar->setVisible( false ); m_bInSetEvent = false; m_flScrubberTimeOffset = 0.0f; } GestureTool::~GestureTool( void ) { } void GestureTool::SetEvent( CChoreoEvent *event ) { if ( m_bInSetEvent ) return; m_bInSetEvent = true; if ( event == m_pLastEvent ) { if ( event ) { if ( event->GetDuration() != m_flLastDuration ) { m_flLastDuration = event->GetDuration(); m_nLastHPixelsNeeded = -1; m_flLeftOffset = 0.0f; InvalidateLayout(); } m_nFocusEventGlobalID = event->GetGlobalID(); } m_bInSetEvent = false; return; } m_pLastEvent = event; m_nFocusEventGlobalID = -1; if ( event ) { m_nFocusEventGlobalID = event->GetGlobalID(); } if ( event ) { m_flLastDuration = event->GetDuration(); } else { m_flLastDuration = 0.0f; } m_flLeftOffset = 0.0f; m_nLastHPixelsNeeded = -1; InvalidateLayout(); m_bInSetEvent = false; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CChoreoEvent *GestureTool::GetSafeEvent( void ) { if ( m_nFocusEventGlobalID == -1 ) return NULL; if ( !g_pChoreoView ) return NULL; CChoreoScene *scene = g_pChoreoView->GetScene(); if ( !scene ) return NULL; // Find event by name for ( int i = 0; i < scene->GetNumEvents() ; i++ ) { CChoreoEvent *e = scene->GetEvent( i ); if ( !e || e->GetType() != CChoreoEvent::GESTURE ) continue; if ( e->GetGlobalID() == m_nFocusEventGlobalID ) { return e; } } return NULL; } //----------------------------------------------------------------------------- // Purpose: // Input : rcHandle - //----------------------------------------------------------------------------- void GestureTool::GetScrubHandleRect( RECT& rcHandle, float scrub, bool clipped ) { float pixel = 0.0f; if ( w2() > 0 ) { pixel = GetPixelForTimeValue( scrub ); if ( clipped ) { pixel = clamp( pixel, SCRUBBER_HANDLE_WIDTH / 2, w2() - SCRUBBER_HANDLE_WIDTH / 2 ); } } rcHandle.left = pixel- SCRUBBER_HANDLE_WIDTH / 2; rcHandle.right = pixel + SCRUBBER_HANDLE_WIDTH / 2; rcHandle.top = 2 + GetCaptionHeight(); rcHandle.bottom = rcHandle.top + SCRUBBER_HANDLE_HEIGHT; } void GestureTool::GetScrubHandleReferenceRect( RECT& rcHandle, float scrub, bool clipped /*= false*/ ) { float pixel = 0.0f; if ( w2() > 0 ) { pixel = GetPixelForTimeValue( scrub ); if ( clipped ) { pixel = clamp( pixel, SCRUBBER_HANDLE_WIDTH/2, w2() - SCRUBBER_HANDLE_WIDTH/2 ); } } rcHandle.left = pixel-SCRUBBER_HANDLE_WIDTH/2; rcHandle.right = pixel + SCRUBBER_HANDLE_WIDTH/2; rcHandle.top = 2 + GetCaptionHeight() + 195; rcHandle.bottom = rcHandle.top + SCRUBBER_HANDLE_HEIGHT; } //----------------------------------------------------------------------------- // Purpose: // Input : drawHelper - // rcHandle - //----------------------------------------------------------------------------- void GestureTool::DrawScrubHandle( CChoreoWidgetDrawHelper& drawHelper, RECT& rcHandle, float scrub, bool reference ) { HBRUSH br = CreateSolidBrush( reference ? RGB( 150, 0, 0 ) : RGB( 0, 150, 100 ) ); COLORREF areaBorder = RGB( 230, 230, 220 ); drawHelper.DrawColoredLine( areaBorder, PS_SOLID, 1, 0, rcHandle.top, w2(), rcHandle.top ); drawHelper.DrawColoredLine( areaBorder, PS_SOLID, 1, 0, rcHandle.bottom, w2(), rcHandle.bottom ); drawHelper.DrawFilledRect( br, rcHandle ); // char sz[ 32 ]; sprintf( sz, "%.3f", scrub ); CChoreoEvent *ev = GetSafeEvent(); if ( ev ) { float st, ed; st = ev->GetStartTime(); ed = ev->GetEndTime(); float dt = ed - st; if ( dt > 0.0f ) { sprintf( sz, "%.3f", st + scrub ); } } int len = drawHelper.CalcTextWidth( "Arial", 9, 500, sz ); RECT rcText = rcHandle; int textw = rcText.right - rcText.left; rcText.left += ( textw - len ) / 2; drawHelper.DrawColoredText( "Arial", 9, 500, RGB( 255, 255, 255 ), rcText, sz ); DeleteObject( br ); } //----------------------------------------------------------------------------- // Purpose: // Input : *event - // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool GestureTool::IsMouseOverScrubHandle( mxEvent *event ) { RECT rcHandle; GetScrubHandleRect( rcHandle, m_flScrub, true ); InflateRect( &rcHandle, 2, 2 ); POINT pt; pt.x = (short)event->x; pt.y = (short)event->y; if ( PtInRect( &rcHandle, pt ) ) { return true; } return false; } //----------------------------------------------------------------------------- // Purpose: // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool GestureTool::IsProcessing( void ) { if ( !GetSafeEvent() ) return false; if ( m_flScrub != m_flScrubTarget ) return true; return false; } bool GestureTool::IsScrubbing( void ) const { bool scrubbing = ( m_nDragType == DRAGTYPE_SCRUBBER ) ? true : false; return scrubbing; } void GestureTool::SetScrubTime( float t ) { m_flScrub = t; CChoreoEvent *e = GetSafeEvent(); if ( e && e->GetDuration() ) { float realtime = e->GetStartTime() + m_flScrub; g_pChoreoView->SetScrubTime( realtime ); g_pChoreoView->DrawScrubHandle(); } } void GestureTool::SetScrubTargetTime( float t ) { m_flScrubTarget = t; CChoreoEvent *e = GetSafeEvent(); if ( e && e->GetDuration() ) { float realtime = e->GetStartTime() + m_flScrubTarget; g_pChoreoView->SetScrubTargetTime( realtime ); } } //----------------------------------------------------------------------------- // Purpose: // Input : dt - //----------------------------------------------------------------------------- void GestureTool::Think( float dt ) { CChoreoEvent *event = GetSafeEvent(); if ( !event ) return; bool scrubbing = IsScrubbing(); ScrubThink( dt, scrubbing ); } //----------------------------------------------------------------------------- // Purpose: // Input : dt - //----------------------------------------------------------------------------- void GestureTool::ScrubThink( float dt, bool scrubbing ) { CChoreoEvent *event = GetSafeEvent(); if ( !event ) return; if ( m_flScrubTarget == m_flScrub && !scrubbing ) return; float d = m_flScrubTarget - m_flScrub; int sign = d > 0.0f ? 1 : -1; float maxmove = dt; if ( sign > 0 ) { if ( d < maxmove ) { SetScrubTime( m_flScrubTarget ); } else { SetScrubTime( m_flScrub + maxmove ); } } else { if ( -d < maxmove ) { SetScrubTime( m_flScrubTarget ); } else { SetScrubTime( m_flScrub - maxmove ); } } if ( scrubbing ) { g_pMatSysWindow->Frame(); } } void GestureTool::DrawScrubHandles() { RECT rcTray; RECT rcHandle; GetScrubHandleRect( rcHandle, m_flScrub, true ); rcTray = rcHandle; rcTray.left = 0; rcTray.right = w2(); CChoreoWidgetDrawHelper drawHelper( this, rcTray ); DrawScrubHandle( drawHelper, rcHandle, m_flScrub, false ); CChoreoEvent *ev = GetSafeEvent(); if ( ev && ev->GetDuration() > 0.0f ) { float scrub = ev->GetOriginalPercentageFromPlaybackPercentage( m_flScrub / ev->GetDuration() ) * ev->GetDuration(); GetScrubHandleReferenceRect( rcHandle, scrub, true ); rcTray = rcHandle; rcTray.left = 0; rcTray.right = w2(); CChoreoWidgetDrawHelper drawHelper( this, rcTray ); DrawScrubHandle( drawHelper, rcHandle, scrub, true ); } } void GestureTool::redraw() { if ( !ToolCanDraw() ) return; CChoreoWidgetDrawHelper drawHelper( this ); HandleToolRedraw( drawHelper ); RECT rc; drawHelper.GetClientRect( rc ); CChoreoEvent *ev = GetSafeEvent(); if ( ev ) { RECT rcText; drawHelper.GetClientRect( rcText ); rcText.top += GetCaptionHeight()+1; rcText.bottom = rcText.top + 13; rcText.left += 5; rcText.right -= 5; OffsetRect( &rcText, 0, 12 ); int current, total; g_pChoreoView->GetUndoLevels( current, total ); if ( total > 0 ) { RECT rcUndo = rcText; OffsetRect( &rcUndo, 0, 2 ); drawHelper.DrawColoredText( "Small Fonts", 8, FW_NORMAL, RGB( 0, 100, 0 ), rcUndo, "Undo: %i/%i", current, total ); } rcText.left += 60; // Found it, write out description // float seqduration; ev->GetGestureSequenceDuration( seqduration ); RECT rcTextLine = rcText; drawHelper.DrawColoredText( "Arial", 11, 900, RGB( 200, 0, 0 ), rcTextLine, "Event: %s", ev->GetName() ); OffsetRect( &rcTextLine, 0, 12 ); drawHelper.DrawColoredText( "Arial", 11, 900, RGB( 200, 0, 0 ), rcTextLine, "Sequence: '%s' %.3f s.", ev->GetParameters(), seqduration ); RECT rcTimeLine; drawHelper.GetClientRect( rcTimeLine ); rcTimeLine.left = 0; rcTimeLine.right = w2(); rcTimeLine.top += ( GetCaptionHeight() + 70 ); float lefttime = GetTimeValueForMouse( 0 ); float righttime = GetTimeValueForMouse( w2() ); DrawTimeLine( drawHelper, rcTimeLine, lefttime, righttime ); OffsetRect( &rcText, 0, 30 ); rcText.left = 5; RECT timeRect = rcText; timeRect.right = timeRect.left + 100; char sz[ 32 ]; Q_snprintf( sz, sizeof( sz ), "%.2f", lefttime + ev->GetStartTime() ); drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 0, 0, 0 ), timeRect, sz ); timeRect = rcText; Q_snprintf( sz, sizeof( sz ), "%.2f", righttime + ev->GetStartTime() ); int textW = drawHelper.CalcTextWidth( "Arial", 9, FW_NORMAL, sz ); timeRect.right = w2() - 10; timeRect.left = timeRect.right - textW; drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 0, 0, 0 ), timeRect, sz ); } RECT rcHandle; GetScrubHandleRect( rcHandle, m_flScrub, true ); DrawScrubHandle( drawHelper, rcHandle, m_flScrub, false ); DrawEventEnd( drawHelper ); if ( ev && ev->GetDuration() > 0.0f ) { float scrub = ev->GetOriginalPercentageFromPlaybackPercentage( m_flScrub / ev->GetDuration() ) * ev->GetDuration(); GetScrubHandleReferenceRect( rcHandle, scrub, true ); DrawScrubHandle( drawHelper, rcHandle, scrub, true ); } RECT rcTags = rc; rcTags.top = TAG_TOP + GetCaptionHeight(); rcTags.bottom = TAG_BOTTOM + GetCaptionHeight(); DrawRelativeTags( drawHelper, rcTags ); DrawAbsoluteTags( drawHelper ); RECT rcPos; GetMouseOverPosRect( rcPos ); DrawMouseOverPos( drawHelper, rcPos ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void GestureTool::ShowContextMenu( mxEvent *event, bool include_track_menus ) { // Construct main menu mxPopupMenu *pop = new mxPopupMenu(); int current, total; g_pChoreoView->GetUndoLevels( current, total ); if ( total > 0 ) { if ( current > 0 ) { pop->add( va( "Undo %s", g_pChoreoView->GetUndoDescription() ), IDC_UNDO_GT ); } if ( current <= total - 1 ) { pop->add( va( "Redo %s", g_pChoreoView->GetRedoDescription() ), IDC_REDO_GT ); } pop->addSeparator(); } CEventAbsoluteTag *tag = IsMouseOverTag( (short)event->x, (short)event->y ); if ( tag ) { pop->add( va( "Delete '%s'...", tag->GetName() ), IDC_GT_DELETE_TAG ); } else { pop->add( "Insert Tag...", IDC_GT_INSERT_TAG ); } pop->add( "Revert Tag Timings", IDC_GT_REVERT ); pop->add( va( "Change scale..." ), IDC_GT_CHANGESCALE ); pop->popup( this, (short)event->x, (short)event->y ); } void GestureTool::GetWorkspaceLeftRight( int& left, int& right ) { left = 0; right = w2(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void GestureTool::DrawFocusRect( void ) { HDC dc = GetDC( NULL ); for ( int i = 0; i < m_FocusRects.Size(); i++ ) { RECT rc = m_FocusRects[ i ].m_rcFocus; ::DrawFocusRect( dc, &rc ); } ReleaseDC( NULL, dc ); } void GestureTool::SetClickedPos( int x, int y ) { m_nClickedX = x; m_nClickedY = y; } float GestureTool::GetTimeForClickedPos( void ) { CChoreoEvent *e = GetSafeEvent(); if ( !e ) return 0.0f; float t = GetTimeValueForMouse( m_nClickedX ); return t; } //----------------------------------------------------------------------------- // Purpose: // Input : dragtype - // startx - // cursor - //----------------------------------------------------------------------------- void GestureTool::StartDragging( int dragtype, int startx, int starty, HCURSOR cursor ) { m_nDragType = dragtype; m_nStartX = startx; m_nLastX = startx; m_nStartY = starty; m_nLastY = starty; if ( m_hPrevCursor ) { SetCursor( m_hPrevCursor ); m_hPrevCursor = NULL; } m_hPrevCursor = SetCursor( cursor ); m_FocusRects.Purge(); RECT rcStart; rcStart.left = startx; rcStart.right = startx; bool addrect = true; switch ( dragtype ) { default: case DRAGTYPE_SCRUBBER: { RECT rcScrub; GetScrubHandleRect( rcScrub, m_flScrub, true ); rcStart = rcScrub; rcStart.left = ( rcScrub.left + rcScrub.right ) / 2; rcStart.right = rcStart.left; rcStart.top = rcScrub.bottom; rcStart.bottom = h2(); } break; case DRAGTYPE_ABSOLUTE_TIMING_TAG: { rcStart.top = 0; rcStart.bottom = h2(); } break; } if ( addrect ) { AddFocusRect( rcStart ); } DrawFocusRect(); } void GestureTool::OnMouseMove( mxEvent *event ) { int mx = (short)event->x; int my = (short)event->y; event->x = (short)mx; if ( m_nDragType != DRAGTYPE_NONE ) { DrawFocusRect(); for ( int i = 0; i < m_FocusRects.Size(); i++ ) { CFocusRect *f = &m_FocusRects[ i ]; f->m_rcFocus = f->m_rcOrig; switch ( m_nDragType ) { default: case DRAGTYPE_SCRUBBER: { ApplyBounds( mx, my ); if ( w2() > 0 ) { float t = GetTimeValueForMouse( mx ); t += m_flScrubberTimeOffset; ForceScrubPosition( t ); } } break; case DRAGTYPE_ABSOLUTE_TIMING_TAG: { ApplyBounds( mx, my ); } break; } OffsetRect( &f->m_rcFocus, ( mx - m_nStartX ), 0 ); } DrawFocusRect(); } else { if ( m_hPrevCursor ) { SetCursor( m_hPrevCursor ); m_hPrevCursor = NULL; } if ( IsMouseOverScrubHandle( event ) ) { m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) ); } else if ( IsMouseOverTag( mx, my ) ) { m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) ); } } m_nLastX = (short)event->x; m_nLastY = (short)event->y; } int GestureTool::handleEvent( mxEvent *event ) { MDLCACHE_CRITICAL_SECTION_( g_pMDLCache ); int iret = 0; if ( HandleToolEvent( event ) ) { return iret; } switch ( event->event ) { case mxEvent::Size: { int w, h; w = event->width; h = event->height; m_nLastHPixelsNeeded = 0; InvalidateLayout(); iret = 1; } break; case mxEvent::MouseWheeled: { CChoreoScene *scene = g_pChoreoView->GetScene(); if ( scene ) { int tz = g_pChoreoView->GetTimeZoom( GetToolName() ); bool shiftdown = ( event->modifiers & mxEvent::KeyShift ) ? true : false; int stepMultipiler = shiftdown ? 5 : 1; // Zoom time in / out if ( event->height > 0 ) { tz = min( tz + TIME_ZOOM_STEP * stepMultipiler, MAX_TIME_ZOOM ); } else { tz = max( tz - TIME_ZOOM_STEP * stepMultipiler, TIME_ZOOM_STEP ); } g_pChoreoView->SetPreservedTimeZoom( this, tz ); } RepositionHSlider(); redraw(); iret = 1; } break; case mxEvent::MouseDown: { iret = 1; int mx = (short)event->x; int my = (short)event->y; SetClickedPos( mx, my ); SetMouseOverPos( mx, my ); DrawMouseOverPos(); if ( event->buttons & mxEvent::MouseRightButton ) { ShowContextMenu( event, false ); return iret; } if ( m_nDragType == DRAGTYPE_NONE ) { if ( IsMouseOverScrubHandle( event ) ) { if ( w2() > 0 ) { float t = GetTimeValueForMouse( (short)event->x ); m_flScrubberTimeOffset = m_flScrub - t; float maxoffset = 0.5f * (float)SCRUBBER_HANDLE_WIDTH / GetPixelsPerSecond(); m_flScrubberTimeOffset = clamp( m_flScrubberTimeOffset, -maxoffset, maxoffset ); t += m_flScrubberTimeOffset; ForceScrubPosition( t ); } StartDragging( DRAGTYPE_SCRUBBER, m_nClickedX, m_nClickedY, LoadCursor( NULL, IDC_SIZEWE ) ); } else if ( IsMouseOverTag( mx, my ) ) { StartDragging( DRAGTYPE_ABSOLUTE_TIMING_TAG, m_nClickedX, m_nClickedY, LoadCursor( NULL, IDC_SIZEWE ) ); } else { if ( w2() > 0 ) { float t = GetTimeValueForMouse( (short)event->x ); SetScrubTargetTime( t ); } } CalcBounds( m_nDragType ); } } break; case mxEvent::MouseDrag: case mxEvent::MouseMove: { int mx = (short)event->x; int my = (short)event->y; SetMouseOverPos( mx, my ); DrawMouseOverPos(); OnMouseMove( event ); iret = 1; } break; case mxEvent::MouseUp: { if ( event->buttons & mxEvent::MouseRightButton ) { return 1; } int mx = (short)event->x; int my = (short)event->y; if ( m_nDragType != DRAGTYPE_NONE ) { DrawFocusRect(); } if ( m_hPrevCursor ) { SetCursor( m_hPrevCursor ); m_hPrevCursor = 0; } switch ( m_nDragType ) { case DRAGTYPE_NONE: break; case DRAGTYPE_SCRUBBER: { ApplyBounds( mx, my ); if ( w2() > 0 ) { float t = GetTimeValueForMouse( (short)event->x ); t += m_flScrubberTimeOffset; ForceScrubPosition( t ); m_flScrubberTimeOffset = 0.0f; } } break; case DRAGTYPE_ABSOLUTE_TIMING_TAG: { ApplyBounds( mx, my ); CEventAbsoluteTag *tag = IsMouseOverTag( m_nClickedX, m_nClickedY ); if ( tag && w2() && GetSafeEvent() ) { float t = GetTimeValueForMouse( mx ); float lastfrac = t / GetSafeEvent()->GetDuration(); lastfrac = clamp( lastfrac, 0.0f, 1.0f ); g_pChoreoView->SetDirty( true ); g_pChoreoView->PushUndo( "move absolute tag" ); tag->SetPercentage( lastfrac ); g_pChoreoView->PushRedo( "move absolute tag" ); g_pChoreoView->InvalidateLayout(); redraw(); } } break; } m_nDragType = DRAGTYPE_NONE; SetMouseOverPos( mx, my ); DrawMouseOverPos(); iret = 1; } break; case mxEvent::KeyDown: { iret = g_pChoreoView->HandleZoomKey( this, event->key ); } break; case mxEvent::Action: { iret = 1; switch ( event->action ) { default: iret = 0; break; case IDC_UNDO_GT: OnUndo(); break; case IDC_REDO_GT: OnRedo(); break; case IDC_GT_DELETE_TAG: OnDeleteTag(); break; case IDC_GT_INSERT_TAG: OnInsertTag(); break; case IDC_GT_REVERT: OnRevert(); break; case IDC_GESTUREHSCROLL: { int offset = 0; bool processed = true; switch ( event->modifiers ) { case SB_THUMBTRACK: offset = event->height; break; case SB_PAGEUP: offset = m_pHorzScrollBar->getValue(); offset -= 20; offset = max( offset, m_pHorzScrollBar->getMinValue() ); break; case SB_PAGEDOWN: offset = m_pHorzScrollBar->getValue(); offset += 20; offset = min( offset, m_pHorzScrollBar->getMaxValue() ); break; case SB_LINEUP: offset = m_pHorzScrollBar->getValue(); offset -= 10; offset = max( offset, m_pHorzScrollBar->getMinValue() ); break; case SB_LINEDOWN: offset = m_pHorzScrollBar->getValue(); offset += 10; offset = min( offset, m_pHorzScrollBar->getMaxValue() ); break; default: processed = false; break; } if ( processed ) { MoveTimeSliderToPos( offset ); } } break; case IDC_GT_CHANGESCALE: { OnChangeScale(); } break; } } break; } return iret; } void GestureTool::ApplyBounds( int& mx, int& my ) { if ( !m_bUseBounds ) return; mx = clamp( mx, m_nMinX, m_nMaxX ); } int GestureTool::GetTagTypeForTag( CEventAbsoluteTag const *tag ) { CChoreoEvent *e = GetSafeEvent(); if ( !e ) return -1; for ( int t = 0; t < CChoreoEvent::NUM_ABS_TAG_TYPES; t++ ) { CChoreoEvent::AbsTagType tagtype = (CChoreoEvent::AbsTagType)t; for ( int i = 0; i < e->GetNumAbsoluteTags( tagtype ); i++ ) { CEventAbsoluteTag *ptag = e->GetAbsoluteTag( tagtype, i ); Assert( ptag ); if ( ptag == tag ) return t; } } return -1; } void GestureTool::CalcBounds( int movetype ) { switch ( movetype ) { default: case DRAGTYPE_NONE: { m_bUseBounds = false; m_nMinX = 0; m_nMaxX = 0; } break; case DRAGTYPE_SCRUBBER: { m_bUseBounds = true; m_nMinX = 0; m_nMaxX = w2(); } break; case DRAGTYPE_ABSOLUTE_TIMING_TAG: { m_bUseBounds = true; m_nMinX = 0; m_nMaxX = w2(); CChoreoEvent *e = GetSafeEvent(); CEventAbsoluteTag *tag = IsMouseOverTag( m_nClickedX, m_nClickedY ); if ( tag && e && e->GetDuration() ) { m_nMinX = GetPixelForTimeValue( 0 ); m_nMaxX = max( w2(), GetPixelForTimeValue( e->GetDuration() ) ); int t = GetTagTypeForTag( tag ); if ( t != -1 ) { CChoreoEvent::AbsTagType tagtype = (CChoreoEvent::AbsTagType)t; CEventAbsoluteTag *prevTag = NULL, *nextTag = NULL; int c = e->GetNumAbsoluteTags( tagtype ); int i; for ( i = 0; i < c; i++ ) { CEventAbsoluteTag *t = e->GetAbsoluteTag( tagtype, i ); Assert( t ); if ( t == tag ) { prevTag = i > 0 ? e->GetAbsoluteTag( tagtype, i-1 ) : NULL; nextTag = i < c - 1 ? e->GetAbsoluteTag( tagtype, i+1 ) : NULL; break; } } if ( i < c ) { if ( prevTag ) { m_nMinX = GetPixelForTimeValue( prevTag->GetPercentage() * e->GetDuration() ) + 1; } if ( nextTag ) { m_nMaxX = GetPixelForTimeValue( nextTag->GetPercentage() * e->GetDuration() ) - 1; } } else { Assert( 0 ); } } } } break; } } bool GestureTool::PaintBackground() { redraw(); return false; } void GestureTool::OnUndo( void ) { g_pChoreoView->Undo(); } void GestureTool::OnRedo( void ) { g_pChoreoView->Redo(); } void GestureTool::ForceScrubPositionFromSceneTime( float scenetime ) { CChoreoEvent *e = GetSafeEvent(); if ( !e || !e->GetDuration() ) return; float t = scenetime - e->GetStartTime(); m_flScrub = t; m_flScrubTarget = t; DrawScrubHandles(); } void GestureTool::ForceScrubPosition( float t ) { m_flScrub = t; m_flScrubTarget = t; CChoreoEvent *e = GetSafeEvent(); if ( e && e->GetDuration() ) { float realtime = e->GetStartTime() + t; g_pChoreoView->SetScrubTime( realtime ); g_pChoreoView->SetScrubTargetTime( realtime ); g_pChoreoView->DrawScrubHandle(); } DrawScrubHandles(); } void GestureTool::SetMouseOverPos( int x, int y ) { m_nMousePos[ 0 ] = x; m_nMousePos[ 1 ] = y; } void GestureTool::GetMouseOverPos( int &x, int& y ) { x = m_nMousePos[ 0 ]; y = m_nMousePos[ 1 ]; } //----------------------------------------------------------------------------- // Purpose: // Input : rcPos - //----------------------------------------------------------------------------- void GestureTool::GetMouseOverPosRect( RECT& rcPos ) { rcPos.top = GetCaptionHeight() + 12; rcPos.left = w2() - 200; rcPos.right = w2() - 5; rcPos.bottom = rcPos.top + 13; } //----------------------------------------------------------------------------- // Purpose: // Input : drawHelper - // rcPos - //----------------------------------------------------------------------------- void GestureTool::DrawMouseOverPos( CChoreoWidgetDrawHelper& drawHelper, RECT& rcPos ) { // Compute time for pixel x float t = GetTimeValueForMouse( m_nMousePos[ 0 ] ); CChoreoEvent *e = GetSafeEvent(); if ( !e ) return; t += e->GetStartTime(); float snapped = FacePoser_SnapTime( t ); // Found it, write out description // char sz[ 128 ]; if ( t != snapped ) { Q_snprintf( sz, sizeof( sz ), "%s", FacePoser_DescribeSnappedTime( t ) ); } else { Q_snprintf( sz, sizeof( sz ), "%.3f", t ); } int len = drawHelper.CalcTextWidth( "Arial", 11, 900, sz ); RECT rcText = rcPos; rcText.left = max( rcPos.left, rcPos.right - len ); drawHelper.DrawColoredText( "Arial", 11, 900, RGB( 255, 50, 70 ), rcText, sz ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void GestureTool::DrawMouseOverPos() { RECT rcPos; GetMouseOverPosRect( rcPos ); CChoreoWidgetDrawHelper drawHelper( this, rcPos ); DrawMouseOverPos( drawHelper, rcPos ); } void GestureTool::AddFocusRect( RECT& rc ) { RECT rcFocus = rc; POINT offset; offset.x = 0; offset.y = 0; ClientToScreen( (HWND)getHandle(), &offset ); OffsetRect( &rcFocus, offset.x, offset.y ); // Convert to screen space? CFocusRect fr; fr.m_rcFocus = rcFocus; fr.m_rcOrig = rcFocus; m_FocusRects.AddToTail( fr ); } //----------------------------------------------------------------------------- // Purpose: // Input : &rcClient - // tagtype - // rcTray - //----------------------------------------------------------------------------- void GestureTool::GetTagTrayRect( RECT &rcClient, int tagtype, RECT& rcTray ) { rcTray = rcClient; rcTray.top += ( GetCaptionHeight() + 110 ); rcTray.bottom = rcTray.top + 6; if ( tagtype == CChoreoEvent::ORIGINAL ) { OffsetRect( &rcTray, 0, 45 ); } } //----------------------------------------------------------------------------- // Purpose: // Input : rcClient - // *event - // tagtype - // *tag - // rcTag - // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool GestureTool::GetAbsTagRect( RECT& rcClient, CChoreoEvent *event, int tagtype, CEventAbsoluteTag *tag, RECT& rcTag ) { rcTag = rcClient; GetTagTrayRect( rcClient, tagtype, rcTag ); bool clipped = false; float t = tag->GetPercentage() * event->GetDuration(); int tagx = GetPixelForTimeValue( t, &clipped ); rcTag.left = tagx - 3; rcTag.right = tagx + 3; if ( clipped ) return false; return true; } void GestureTool::DrawAbsoluteTags( CChoreoWidgetDrawHelper& drawHelper ) { CChoreoEvent *event = GetSafeEvent(); if ( !event ) return; RECT rcClient; drawHelper.GetClientRect( rcClient ); bool showDots = true; if ( event->GetNumAbsoluteTags( (CChoreoEvent::AbsTagType)0 ) != event->GetNumAbsoluteTags( (CChoreoEvent::AbsTagType)1 ) ) { showDots = false; } int t; for ( t = 0; t < CChoreoEvent::NUM_ABS_TAG_TYPES; t++ ) { CChoreoEvent::AbsTagType tagtype = ( CChoreoEvent::AbsTagType )t; RECT rcTray; GetTagTrayRect( rcClient, tagtype, rcTray ); drawHelper.DrawColoredLine( RGB( 220, 220, 220 ), PS_SOLID, 1, rcTray.left, rcTray.top, rcTray.right, rcTray.top ); drawHelper.DrawColoredLine( RGB( 220, 220, 220 ), PS_SOLID, 1, rcTray.left, rcTray.bottom, rcTray.right, rcTray.bottom ); RECT rcText; rcText = rcTray; InflateRect( &rcText, 0, 4 ); OffsetRect( &rcText, 0, t == 0 ? -10 : 10 ); rcText.left = 2; drawHelper.DrawColoredText( "Arial", 9, 500, RGB( 150, 150, 150 ), rcText, "%s", t == 0 ? "Playback Time" : "Original Time" ); for ( int i = 0; i < event->GetNumAbsoluteTags( tagtype ); i++ ) { CEventAbsoluteTag *tag = event->GetAbsoluteTag( tagtype, i ); if ( !tag ) continue; RECT rcMark; bool visible = GetAbsTagRect( rcClient, event, tagtype, tag, rcMark ); if ( showDots && t == 1 ) { CChoreoEvent::AbsTagType tagtypeOther = (CChoreoEvent::AbsTagType)0; RECT rcMark2; CEventAbsoluteTag *otherTag = event->GetAbsoluteTag( tagtypeOther, i ); if ( otherTag ) { GetAbsTagRect( rcClient, event, tagtypeOther, otherTag, rcMark2 ); { int midx1 = ( rcMark.left + rcMark.right ) / 2; int midx2 = ( rcMark2.left + rcMark2.right ) / 2; int y1 = rcMark.top; int y2 = rcMark2.bottom; drawHelper.DrawColoredLine( RGB( 200, 200, 200 ), PS_SOLID, 1, midx1, y1, midx2, y2 ); } } } if ( !visible ) continue; drawHelper.DrawTriangleMarker( rcMark, RGB( 200, 0, 30 ), tagtype != CChoreoEvent::PLAYBACK ); RECT rcText; rcText = rcMark; if ( tagtype == CChoreoEvent::PLAYBACK ) { rcText.top -= 15; } else { rcText.top += 10; } char text[ 256 ]; sprintf( text, "%s", tag->GetName() ); int len = drawHelper.CalcTextWidth( "Arial", 9, FW_NORMAL, text ); rcText.left = ( rcMark.left + rcMark.right ) / 2 - len / 2; rcText.right = rcText.left + len + 2; rcText.bottom = rcText.top + 10; drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 200, 100, 100 ), rcText, text ); if ( tagtype == CChoreoEvent::PLAYBACK ) { rcText.top -= 10; } else { rcText.top += 10; } // sprintf( text, "%.3f", tag->GetPercentage() * event->GetDuration() + event->GetStartTime() ); sprintf( text, "%.3f", tag->GetPercentage() ); len = drawHelper.CalcTextWidth( "Arial", 9, FW_NORMAL, text ); rcText.left = ( rcMark.left + rcMark.right ) / 2 - len / 2; rcText.right = rcText.left + len + 2; rcText.bottom = rcText.top + 10; drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 200, 100, 100 ), rcText, text ); } } } //----------------------------------------------------------------------------- // Purpose: // Input : drawHelper - // rc - // left - // right - //----------------------------------------------------------------------------- void GestureTool::DrawTimeLine( CChoreoWidgetDrawHelper& drawHelper, RECT& rc, float left, float right ) { RECT rcLabel; float granularity = 0.5f; drawHelper.DrawColoredLine( RGB( 150, 150, 200 ), PS_SOLID, 1, rc.left, rc.top + 2, rc.right, rc.top + 2 ); float f = SnapTime( left, granularity ); while ( f < right ) { float frac = ( f - left ) / ( right - left ); if ( frac >= 0.0f && frac <= 1.0f ) { rcLabel.left = GetPixelForTimeValue( f ); rcLabel.top = rc.top + 5; rcLabel.bottom = rcLabel.top + 10; if ( f != left ) { drawHelper.DrawColoredLine( RGB( 220, 220, 240 ), PS_DOT, 1, rcLabel.left, rc.top, rcLabel.left, h2() ); } char sz[ 32 ]; sprintf( sz, "%.2f", f ); int textWidth = drawHelper.CalcTextWidth( "Arial", 9, FW_NORMAL, sz ); rcLabel.right = rcLabel.left + textWidth; OffsetRect( &rcLabel, -textWidth / 2, 0 ); RECT rcOut = rcLabel; if ( rcOut.left <= 0 ) { OffsetRect( &rcOut, -rcOut.left + 2, 0 ); } drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 0, 50, 150 ), rcOut, sz ); } f += granularity; } } //----------------------------------------------------------------------------- // Purpose: // Input : mx - // my - // Output : CFlexTimingTag //----------------------------------------------------------------------------- CEventAbsoluteTag *GestureTool::IsMouseOverTag( int mx, int my ) { CChoreoEvent *event = GetSafeEvent(); if ( !event ) return NULL; RECT rcClient; GetClientRect( (HWND)getHandle(), &rcClient ); POINT pt; pt.x = mx; pt.y = my; for ( int t = 0; t < CChoreoEvent::NUM_ABS_TAG_TYPES; t++ ) { CChoreoEvent::AbsTagType tagtype = ( CChoreoEvent::AbsTagType )t; for ( int i = 0; i < event->GetNumAbsoluteTags( tagtype ); i++ ) { CEventAbsoluteTag *tag = event->GetAbsoluteTag( tagtype, i ); if ( !tag ) continue; if ( tag->GetLocked() ) continue; RECT rcTag; if ( !GetAbsTagRect( rcClient, event, tagtype, tag, rcTag ) ) continue; if ( !PtInRect( &rcTag, pt ) ) continue; return tag; } } return NULL; } //----------------------------------------------------------------------------- // Purpose: // Input : mx - // my - // Output : int //----------------------------------------------------------------------------- int GestureTool::GetTagTypeForMouse( int mx, int my ) { RECT rcClient; rcClient.left = 0; rcClient.right = w2(); rcClient.top = 0; rcClient.bottom = h2(); POINT pt; pt.x = mx; pt.y = my; for ( int t = 0; t < CChoreoEvent::NUM_ABS_TAG_TYPES; t++ ) { RECT rcTray; GetTagTrayRect( rcClient, t, rcTray ); if ( PtInRect( &rcTray, pt ) ) { return t; } } return -1; } void GestureTool::OnInsertTag( void ) { CChoreoEvent *event = GetSafeEvent(); if ( !event ) return; if ( event->GetType() != CChoreoEvent::GESTURE ) { Con_ErrorPrintf( "Absolute Tag: Can only tag GESTURE events\n" ); return; } CInputParams params; memset( ¶ms, 0, sizeof( params ) ); strcpy( params.m_szDialogTitle, "Absolute Tag Name" ); strcpy( params.m_szPrompt, "Name:" ); strcpy( params.m_szInputText, "" ); if ( !InputProperties( ¶ms ) ) return; if ( strlen( params.m_szInputText ) <= 0 ) { Con_ErrorPrintf( "Timing Tag Name: No name entered!\n" ); return; } // Convert click to frac float t = GetTimeValueForMouse( m_nClickedX ) / event->GetDuration(); float tshifted = event->GetOriginalPercentageFromPlaybackPercentage( t ); g_pChoreoView->SetDirty( true ); g_pChoreoView->PushUndo( "Add Gesture Tag" ); event->AddAbsoluteTag( CChoreoEvent::ORIGINAL, params.m_szInputText, tshifted ); event->AddAbsoluteTag( CChoreoEvent::PLAYBACK, params.m_szInputText, t ); g_pChoreoView->PushRedo( "Add Gesture Tag" ); // Redraw this window redraw(); } void GestureTool::OnRevert() { CChoreoEvent *event = GetSafeEvent(); if ( !event ) return; if ( !event->GetNumAbsoluteTags( CChoreoEvent::PLAYBACK ) ) return; if ( event->GetNumAbsoluteTags( CChoreoEvent::PLAYBACK ) != event->GetNumAbsoluteTags( CChoreoEvent::ORIGINAL ) ) { Assert( 0 ); return; } g_pChoreoView->SetDirty( true ); g_pChoreoView->PushUndo( "Revert Gesture Tags" ); int c = event->GetNumAbsoluteTags( CChoreoEvent::PLAYBACK ); for ( int i = 0; i < c; i++ ) { CEventAbsoluteTag *original = event->GetAbsoluteTag( CChoreoEvent::ORIGINAL, i ); CEventAbsoluteTag *playback = event->GetAbsoluteTag( CChoreoEvent::PLAYBACK, i ); playback->SetPercentage( original->GetPercentage() ); } g_pChoreoView->PushRedo( "Revert Gesture Tags" ); // Redraw this window redraw(); } void GestureTool::OnDeleteTag( void ) { CChoreoEvent *event = GetSafeEvent(); if ( !event ) return; CEventAbsoluteTag *tag = IsMouseOverTag( m_nClickedX, m_nClickedY ); if ( !tag ) return; g_pChoreoView->SetDirty( true ); g_pChoreoView->PushUndo( "Remove Gesture Tag" ); char sz[ 512 ]; Q_strncpy( sz, tag->GetName(), sizeof( sz ) ); for ( int t = 0; t < CChoreoEvent::NUM_ABS_TAG_TYPES; t++ ) { event->RemoveAbsoluteTag( (CChoreoEvent::AbsTagType)t, sz ); } g_pChoreoView->PushRedo( "Remove Gesture Tags" ); // Redraw this window redraw(); } void GestureTool::DrawRelativeTags( CChoreoWidgetDrawHelper& drawHelper, RECT& rc ) { CChoreoEvent *gesture = GetSafeEvent(); if ( !gesture ) return; CChoreoScene *scene = gesture->GetScene(); if ( !scene ) return; float starttime = GetTimeValueForMouse( 0 ); float endtime = GetTimeValueForMouse( w2() ); if ( endtime - starttime <= 0.0f ) return; drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 0, 100, 200 ), rc, "Timing Tags:" ); // Loop through all events in scene int c = scene->GetNumEvents(); int i; for ( i = 0; i < c; i++ ) { CChoreoEvent *e = scene->GetEvent( i ); if ( !e ) continue; if ( e->GetNumRelativeTags() <= 0 ) continue; // See if time overlaps if ( !e->HasEndTime() ) continue; if ( ( e->GetEndTime() - e->GetStartTime() ) < starttime ) continue; if ( ( e->GetStartTime() - e->GetStartTime() ) > endtime ) continue; DrawRelativeTagsForEvent( drawHelper, rc, gesture, e, starttime, endtime ); } } //----------------------------------------------------------------------------- // Purpose: // Input : drawHelper - // rc - //----------------------------------------------------------------------------- void GestureTool::DrawRelativeTagsForEvent( CChoreoWidgetDrawHelper& drawHelper, RECT& rc, CChoreoEvent *gesture, CChoreoEvent *event, float starttime, float endtime ) { if ( !event ) return; //drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, PEColor( COLOR_PHONEME_TIMING_TAG ), rc, "Timing Tags:" ); for ( int i = 0; i < event->GetNumRelativeTags(); i++ ) { CEventRelativeTag *tag = event->GetRelativeTag( i ); if ( !tag ) continue; // float tagtime = ( event->GetStartTime() + tag->GetPercentage() * event->GetDuration() ) - gesture->GetStartTime(); if ( tagtime < starttime || tagtime > endtime ) continue; bool clipped = false; int left = GetPixelForTimeValue( tagtime, &clipped ); if ( clipped ) continue; //float frac = ( tagtime - starttime ) / ( endtime - starttime ); //int left = rc.left + (int)( frac * ( float )( rc.right - rc.left ) + 0.5f ); RECT rcMark; rcMark = rc; rcMark.top = rc.bottom - 8; rcMark.bottom = rc.bottom; rcMark.left = left - 4; rcMark.right = left + 4; drawHelper.DrawTriangleMarker( rcMark, RGB( 0, 100, 200 ) ); RECT rcText; rcText = rc; rcText.bottom = rc.bottom - 10; rcText.top = rcText.bottom - 10; int len = drawHelper.CalcTextWidth( "Arial", 9, FW_NORMAL, tag->GetName() ); rcText.left = left - len / 2; rcText.right = rcText.left + len + 2; drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 0, 100, 200 ), rcText, tag->GetName() ); } } //----------------------------------------------------------------------------- // Purpose: // Output : int //----------------------------------------------------------------------------- int GestureTool::ComputeHPixelsNeeded( void ) { CChoreoEvent *event = GetSafeEvent(); if ( !event ) return 0; int pixels = 0; float maxtime = event->GetDuration(); pixels = (int)( ( maxtime ) * GetPixelsPerSecond() ) + 10; return pixels; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void GestureTool::RepositionHSlider( void ) { int pixelsneeded = ComputeHPixelsNeeded(); if ( pixelsneeded <= w2() ) { m_pHorzScrollBar->setVisible( false ); } else { m_pHorzScrollBar->setVisible( true ); } m_pHorzScrollBar->setBounds( 0, h2() - m_nScrollbarHeight, w2() - m_nScrollbarHeight, m_nScrollbarHeight ); m_flLeftOffset = max( 0.f, m_flLeftOffset ); m_flLeftOffset = min( (float)pixelsneeded, m_flLeftOffset ); m_pHorzScrollBar->setRange( 0, pixelsneeded ); m_pHorzScrollBar->setValue( (int)m_flLeftOffset ); m_pHorzScrollBar->setPagesize( w2() ); m_nLastHPixelsNeeded = pixelsneeded; } //----------------------------------------------------------------------------- // Purpose: // Output : float //----------------------------------------------------------------------------- float GestureTool::GetPixelsPerSecond( void ) { return m_flPixelsPerSecond * (float)g_pChoreoView->GetTimeZoom( GetToolName() )/100.0f; } //----------------------------------------------------------------------------- // Purpose: // Input : x - //----------------------------------------------------------------------------- void GestureTool::MoveTimeSliderToPos( int x ) { m_flLeftOffset = (float)x; m_pHorzScrollBar->setValue( (int)m_flLeftOffset ); InvalidateRect( (HWND)m_pHorzScrollBar->getHandle(), NULL, TRUE ); InvalidateLayout(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void GestureTool::InvalidateLayout( void ) { if ( m_bSuppressLayout ) return; if ( ComputeHPixelsNeeded() != m_nLastHPixelsNeeded ) { RepositionHSlider(); } m_bLayoutIsValid = false; redraw(); } //----------------------------------------------------------------------------- // Purpose: // Input : st - // ed - //----------------------------------------------------------------------------- void GestureTool::GetStartAndEndTime( float& st, float& ed ) { st = m_flLeftOffset / GetPixelsPerSecond(); ed = st + (float)w2() / GetPixelsPerSecond(); } //----------------------------------------------------------------------------- // Purpose: // Input : - // Output : float //----------------------------------------------------------------------------- float GestureTool::GetEventEndTime() { CChoreoEvent *ev = GetSafeEvent(); if ( !ev ) return 1.0f; return ev->GetDuration(); } //----------------------------------------------------------------------------- // Purpose: // Input : time - // *clipped - // Output : int //----------------------------------------------------------------------------- int GestureTool::GetPixelForTimeValue( float time, bool *clipped /*=NULL*/ ) { if ( clipped ) { *clipped = false; } float st, ed; GetStartAndEndTime( st, ed ); float frac = ( time - st ) / ( ed - st ); if ( frac < 0.0 || frac > 1.0 ) { if ( clipped ) { *clipped = true; } } int pixel = ( int )( frac * w2() ); return pixel; } //----------------------------------------------------------------------------- // Purpose: // Input : mx - // clip - // Output : float //----------------------------------------------------------------------------- float GestureTool::GetTimeValueForMouse( int mx, bool clip /*=false*/) { float st, ed; GetStartAndEndTime( st, ed ); if ( clip ) { if ( mx < 0 ) { return st; } if ( mx > w2() ) { return ed; } } float frac = (float)( mx ) / (float)( w2() ); return st + frac * ( ed - st ); } void GestureTool::OnChangeScale( void ) { CChoreoScene *scene = g_pChoreoView->GetScene(); if ( !scene ) { return; } // Zoom time in / out CInputParams params; memset( ¶ms, 0, sizeof( params ) ); strcpy( params.m_szDialogTitle, "Change Zoom" ); strcpy( params.m_szPrompt, "New scale (e.g., 2.5x):" ); Q_snprintf( params.m_szInputText, sizeof( params.m_szInputText ), "%.2f", (float)g_pChoreoView->GetTimeZoom( GetToolName() ) / 100.0f ); if ( !InputProperties( ¶ms ) ) return; g_pChoreoView->SetTimeZoom( GetToolName(), clamp( (int)( 100.0f * atof( params.m_szInputText ) ), 1, MAX_TIME_ZOOM ), false ); m_nLastHPixelsNeeded = -1; m_flLeftOffset= 0.0f; InvalidateLayout(); Con_Printf( "Zoom factor %i %%\n", g_pChoreoView->GetTimeZoom( GetToolName() ) ); } void GestureTool::DrawEventEnd( CChoreoWidgetDrawHelper& drawHelper ) { CChoreoEvent *e = GetSafeEvent(); if ( !e ) return; float duration = e->GetDuration(); if ( !duration ) return; int leftx = GetPixelForTimeValue( duration ); if ( leftx >= w2() ) return; RECT rcClient; drawHelper.GetClientRect( rcClient ); drawHelper.DrawColoredLine( COLOR_CHOREO_ENDTIME, PS_SOLID, 1, leftx, GetCaptionHeight() + 73, leftx, rcClient.bottom ); } void GestureTool::OnModelChanged() { redraw(); }