home *** CD-ROM | disk | FTP | other *** search
/ Xentax forum attachments archive / xentax.7z / 8090 / ModelEdit.7z / KeyframeWnd.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2006-03-08  |  34.0 KB  |  1,319 lines

  1. // KeyframeWnd.cpp : implementation file
  2. //
  3.  
  4. #include "precompile.h"
  5. #include "modeledit.h"
  6. #include "modeleditdlg.h"
  7. #include "keyframewnd.h"
  8. #include "keyframetimedlg.h"
  9. #include "ltbasetypes.h"
  10. #include "keyframestringdlg.h"
  11. #include "model_ops.h"
  12. #include "timeoffsetdlg.h"
  13.  
  14. #ifdef _DEBUG
  15. #define new DEBUG_NEW
  16. #undef THIS_FILE
  17. static char THIS_FILE[] = __FILE__;
  18. #endif
  19.  
  20. /////////////////////////////////////////////////////////////////////////////
  21. // CKeyframeWnd
  22.  
  23. CKeyframeWnd::CKeyframeWnd()
  24. {
  25.     m_pModelAnim = NULL;
  26.     m_nCurrentTime = 0;
  27.     m_crKeyframe = RGB(255,0,0);
  28.     m_crKeyframeString = RGB(0,255,64);
  29.     m_crTaggedKeyframe = RGB(0,0,255);
  30.     m_crTaggedKeyframeString = RGB(0,128,128);
  31.     m_bTracking = FALSE;
  32.     m_bSelectionBox = FALSE;
  33.     m_bActive = FALSE;
  34.     m_pModelAnim = NULL;
  35. }
  36.  
  37. CKeyframeWnd::~CKeyframeWnd()
  38. {
  39. }
  40.  
  41.  
  42. BEGIN_MESSAGE_MAP(CKeyframeWnd, CWnd)
  43.     //{{AFX_MSG_MAP(CKeyframeWnd)
  44.     ON_WM_PAINT()
  45.     ON_WM_LBUTTONDOWN()
  46.     ON_WM_RBUTTONDOWN()
  47.     ON_WM_CREATE()
  48.     ON_WM_DESTROY()
  49.     ON_WM_LBUTTONUP()
  50.     ON_WM_MOUSEMOVE()
  51.     ON_COMMAND(ID_CLIP_AFTER, OnClipAfter)
  52.     ON_COMMAND(ID_CLIP_BEFORE, OnClipBefore)
  53.     ON_COMMAND(ID_TAG_KEYFRAME, OnTagKeyframe)
  54.     ON_COMMAND(ID_UNTAG_KEYFRAME, OnUntagKeyframe)
  55.     ON_COMMAND(ID_EDIT_KEYFRAME_TIME, OnEditKeyframeTime)
  56.     ON_COMMAND(ID_DELETE_KEYFRAME, OnDeleteKeyframe)
  57.     ON_COMMAND(ID_DELETE_TAGGED_KEYFRAMES, OnDeleteTaggedKeyframes)
  58.     ON_COMMAND(ID_EDITKEYFRAMESTRING, OnEditTaggedKeyframeString)
  59.     ON_COMMAND(ID_TAGALL, OnTagAll )
  60.     ON_COMMAND(ID_UNTAGALL, OnUnTagAll )
  61.     ON_COMMAND(ID_INSERT_TIME_DELAY, OnInsertTimeDelay)
  62.     ON_WM_LBUTTONDBLCLK()
  63.     //}}AFX_MSG_MAP
  64. END_MESSAGE_MAP()
  65.  
  66.  
  67. /////////////////////////////////////////////////////////////////////////////
  68. // CKeyframeWnd operations
  69.  
  70. BOOL CKeyframeWnd::InitTaggedArray()
  71. {
  72.     DWORD dwSize;
  73.  
  74.     dwSize = m_pModelAnim ? m_pModelAnim->NumKeyframes() : 0;
  75.     return m_Tagged.SetSizeInit2(dwSize, FALSE);
  76. }
  77.  
  78. void CKeyframeWnd::SetAnim (ModelAnim* pAnim)
  79. {
  80.     m_pModelAnim = pAnim;
  81.  
  82.  
  83.     if(!m_hWnd)
  84.         return;
  85.  
  86.  
  87.     InitTaggedArray();
  88.     
  89.     m_nCurrentTime = 0;
  90.     DWORD nKeyframes = m_pModelAnim ? m_pModelAnim->NumKeyFrames() : 0;
  91.  
  92.     // create new x value array
  93.  
  94.     m_XVal.SetSize(nKeyframes);
  95.  
  96.     // erase keyframe rect and markers
  97.  
  98.     m_dcOffscreen.Rectangle (m_rcKeyframes);
  99.     m_dcOffscreen.FillRect (m_rcTopMarker, &m_brBackground);
  100.     m_dcOffscreen.FillRect (m_rcBottomMarker, &m_brBackground);
  101.     
  102.     // reset the marker rects
  103.  
  104.     m_rcTopMarker.OffsetRect (-m_rcTopMarker.left, 0);
  105.     m_rcBottomMarker.OffsetRect (-m_rcBottomMarker.left, 0);
  106.     m_dcOffscreen.BitBlt (m_rcTopMarker.left, m_rcTopMarker.top, m_rcTopMarker.Width(), m_rcTopMarker.Height(), &m_dcTopMarker, 0, 0, SRCCOPY);
  107.     m_dcOffscreen.BitBlt (m_rcBottomMarker.left, m_rcBottomMarker.top, m_rcBottomMarker.Width(), m_rcBottomMarker.Height(), &m_dcBottomMarker, 0, 0, SRCCOPY);
  108.  
  109.     // draw the keyframe markers
  110.  
  111.     CPen penKeyframe (PS_SOLID, 1, m_crKeyframe);
  112.     CPen penKeyframeString (PS_SOLID, 1, m_crKeyframeString);
  113.     CPen penTaggedKeyframe (PS_SOLID, 1, m_crTaggedKeyframe);
  114.     CPen penTaggedKeyframeString (PS_SOLID, 1, m_crTaggedKeyframeString);
  115.     CPen* pOldPen = m_dcOffscreen.SelectObject (&penKeyframe);
  116.  
  117.     DWORD nTotalTime = m_pModelAnim ? m_pModelAnim->GetAnimTime() : 0;
  118.  
  119.     //the last location we drew each type, we don't want to draw red on top of green, and
  120.     //we don't want to draw green on top of blue
  121.     uint32 nLastStringX = 0xFFFFFFFF;
  122.     uint32 nLastSelX = 0xFFFFFFFF;
  123.  
  124.     if (nTotalTime > 0)
  125.     {
  126.         for (DWORD i = 0; i < nKeyframes; i++)
  127.         {
  128.             float x = ((float)m_pModelAnim->m_KeyFrames[i].m_Time * (float)(m_rcKeyframes.Width() - 3)) / (float)nTotalTime;
  129.             uint32 nXVal = m_rcKeyframes.left + 1 + (int)x;
  130.             m_XVal[i] = nXVal;
  131.  
  132.             //if we have already drawn a selection on this place, just skip
  133.             if(nLastSelX == nXVal)
  134.                 continue;
  135.             
  136.             //draw a tag mark, unless we already have at this location
  137.             if (m_Tagged[i])
  138.             {
  139.                 if( m_pModelAnim->m_KeyFrames[i].m_pString[0] == '\0' )
  140.                     m_dcOffscreen.SelectObject (&penTaggedKeyframe);
  141.                 else
  142.                     m_dcOffscreen.SelectObject (&penTaggedKeyframeString);
  143.  
  144.                 nLastSelX = nXVal;
  145.             }
  146.             //draw a normal selection marker, but only if we haven't already drawn a string
  147.             else
  148.             {                
  149.                 if( (m_pModelAnim->m_KeyFrames[i].m_pString[0] == '\0') && (nXVal != nLastStringX) )
  150.                 {
  151.                     m_dcOffscreen.SelectObject (&penKeyframe);
  152.                 }
  153.                 else
  154.                 {
  155.                     m_dcOffscreen.SelectObject (&penKeyframeString);
  156.                     nLastStringX = nXVal;
  157.                 }
  158.             }
  159.  
  160.             m_dcOffscreen.MoveTo (nXVal, m_rcKeyframes.top + 1);
  161.             m_dcOffscreen.LineTo (nXVal, m_rcKeyframes.bottom - 1);
  162.         }
  163.     }
  164.  
  165.     m_dcOffscreen.SelectObject (pOldPen);
  166.     penKeyframe.DeleteObject();
  167.     penKeyframeString.DeleteObject();
  168.     penTaggedKeyframe.DeleteObject();
  169.     penTaggedKeyframeString.DeleteObject();
  170.  
  171.     Invalidate (FALSE);
  172. }
  173.  
  174. //this function will verify that this operation can be performed given the current animation
  175. //settings, and will inform the user and return false if it cannot
  176. bool CKeyframeWnd::CanPerformOperation(const char* pszDescription)
  177. {
  178.     //make sure that we have an animation
  179.     if(!m_pModelAnim)
  180.     {
  181.         CString sMessage;
  182.         sMessage.Format("Error performing operation:%s\nNo animation is currently selected in this track", pszDescription);
  183.         MessageBox(sMessage, "Error performing operation", MB_OK | MB_ICONEXCLAMATION);
  184.         return false;
  185.     }
  186.  
  187.     if(((CModelEditDlg*)GetParent())->GetModel() != m_pModelAnim->GetModel())
  188.     {
  189.         CString sMessage;
  190.         sMessage.Format("Error performing operation:%s\nYou cannot modify child model animations", pszDescription);
  191.         MessageBox(sMessage, "Error performing operation", MB_OK | MB_ICONEXCLAMATION);
  192.         return false;
  193.     }
  194.  
  195.     //success
  196.     return true;
  197. }
  198.  
  199. void CKeyframeWnd::SetTime (DWORD nCurrentTime)
  200. {
  201.     m_nCurrentTime = nCurrentTime;
  202.  
  203.     if(!m_pModelAnim)
  204.         return;
  205.  
  206.     // get new x location of marker
  207.     
  208.     DWORD nTotalTime = m_pModelAnim->GetAnimTime();
  209.     float x = ((float)m_nCurrentTime * (float)(m_rcKeyframes.Width() - 3)) / (float)nTotalTime;
  210.     int nNewTopX = (int)x;
  211.     
  212.     if (nNewTopX == m_rcTopMarker.left)
  213.     {
  214.         // same place - just return
  215.         return;
  216.     }
  217.  
  218.     int nNewBottomX = (int)x;
  219.  
  220.     // get the new rects
  221.  
  222.     CRect rcOldTop = m_rcTopMarker;
  223.     CRect rcOldBottom = m_rcBottomMarker;
  224.     m_rcTopMarker.OffsetRect (nNewTopX - m_rcTopMarker.left, 0);
  225.     m_rcBottomMarker.OffsetRect (nNewTopX - m_rcBottomMarker.left, 0);
  226.  
  227.     // get the union of old and new rects
  228.  
  229.     CRect rcUnionTop;
  230.     rcUnionTop.UnionRect (rcOldTop, m_rcTopMarker);
  231.     CRect rcUnionBottom;
  232.     rcUnionBottom.UnionRect (rcOldBottom, m_rcBottomMarker);
  233.  
  234.     // update keyframe bar
  235.  
  236.     m_dcOffscreen.FillRect (rcOldTop, &m_brBackground);
  237.     m_dcOffscreen.FillRect (rcOldBottom, &m_brBackground);
  238.     m_dcOffscreen.BitBlt (m_rcTopMarker.left, m_rcTopMarker.top, m_rcTopMarker.Width(), m_rcTopMarker.Height(), &m_dcTopMarker, 0, 0, SRCCOPY);
  239.     m_dcOffscreen.BitBlt (m_rcBottomMarker.left, m_rcBottomMarker.top, m_rcBottomMarker.Width(), m_rcBottomMarker.Height(), &m_dcBottomMarker, 0, 0, SRCCOPY);
  240.  
  241.     CDC* pDC = GetDC();
  242.     pDC->BitBlt (rcUnionTop.left, rcUnionTop.top, rcUnionTop.Width(), rcUnionTop.Height(), &m_dcOffscreen, rcUnionTop.left, rcUnionTop.top, SRCCOPY);
  243.     pDC->BitBlt (rcUnionBottom.left, rcUnionBottom.top, rcUnionBottom.Width(), rcUnionBottom.Height(), &m_dcOffscreen, rcUnionBottom.left, rcUnionBottom.top, SRCCOPY);
  244.     ReleaseDC (pDC);
  245. }
  246.  
  247. void CKeyframeWnd::Redraw( )
  248. {
  249.     if( !m_pModelAnim )
  250.         return;
  251.  
  252.     DWORD nKeyframes = m_pModelAnim->m_KeyFrames;
  253.  
  254.     // erase keyframe rect and markers
  255.  
  256.     m_dcOffscreen.Rectangle (m_rcKeyframes);
  257.     m_dcOffscreen.FillRect (m_rcTopMarker, &m_brBackground);
  258.     m_dcOffscreen.FillRect (m_rcBottomMarker, &m_brBackground);
  259.     
  260.     m_dcOffscreen.BitBlt (m_rcTopMarker.left, m_rcTopMarker.top, m_rcTopMarker.Width(), m_rcTopMarker.Height(), &m_dcTopMarker, 0, 0, SRCCOPY);
  261.     m_dcOffscreen.BitBlt (m_rcBottomMarker.left, m_rcBottomMarker.top, m_rcBottomMarker.Width(), m_rcBottomMarker.Height(), &m_dcBottomMarker, 0, 0, SRCCOPY);
  262.  
  263.     // draw the keyframe markers
  264.  
  265.     CPen penKeyframe (PS_SOLID, 1, m_crKeyframe);
  266.     CPen penKeyframeString (PS_SOLID, 1, m_crKeyframeString);
  267.     CPen penTaggedKeyframe (PS_SOLID, 1, m_crTaggedKeyframe);
  268.     CPen penTaggedKeyframeString (PS_SOLID, 1, m_crTaggedKeyframeString);
  269.     CPen* pOldPen = m_dcOffscreen.SelectObject (&penKeyframe);
  270.  
  271.     DWORD nTotalTime = m_pModelAnim->GetAnimTime();
  272.     if (nTotalTime > 0)
  273.     {
  274.         for (DWORD i = 0; i < nKeyframes; i++)
  275.         {
  276.             float x = ((float)m_pModelAnim->m_KeyFrames[i].m_Time * (float)(m_rcKeyframes.Width() - 3)) / (float)nTotalTime;
  277.             m_XVal[i] = m_rcKeyframes.left + 1 + (int)x;
  278.             
  279.             if (m_Tagged[i])
  280.             {
  281.                 if( m_pModelAnim->m_KeyFrames[i].m_pString[0] == '\0' )
  282.                     m_dcOffscreen.SelectObject (&penTaggedKeyframe);
  283.                 else
  284.                     m_dcOffscreen.SelectObject (&penTaggedKeyframeString);
  285.             }
  286.             else
  287.             {
  288.                 if( m_pModelAnim->m_KeyFrames[i].m_pString[0] == '\0' )
  289.                     m_dcOffscreen.SelectObject (&penKeyframe);
  290.                 else
  291.                     m_dcOffscreen.SelectObject (&penKeyframeString);
  292.             }
  293.  
  294.             m_dcOffscreen.MoveTo (m_XVal[i], m_rcKeyframes.top + 1);
  295.             m_dcOffscreen.LineTo (m_XVal[i], m_rcKeyframes.bottom - 1);
  296.         }
  297.     }
  298.  
  299.     m_dcOffscreen.SelectObject (pOldPen);
  300.     penKeyframe.DeleteObject();
  301.     penKeyframeString.DeleteObject();
  302.     penTaggedKeyframe.DeleteObject();
  303.     penTaggedKeyframeString.DeleteObject();
  304.  
  305.     Invalidate (FALSE);
  306. }
  307.  
  308. DWORD CKeyframeWnd::ForceNearestKeyframe()
  309. {
  310.     DWORD nClosestKeyframe = 0;
  311.  
  312.     if( !m_pModelAnim )
  313.         return 0;
  314.  
  315.     if(m_pModelAnim->m_KeyFrames.GetSize() <= 1)
  316.     {
  317.         nClosestKeyframe = 0;
  318.     }
  319.     else
  320.     {
  321.         // see if we're already on a keyframe
  322.         DWORD nKeyframes = m_pModelAnim->m_KeyFrames;
  323.         int nXVal = m_rcTopMarker.left + (m_rcTopMarker.Width() >> 1);
  324.         for (DWORD i = 0; i < nKeyframes; i++)
  325.         {
  326.             if (nXVal == m_XVal[i])
  327.             {
  328.                 return i;
  329.             }
  330.  
  331.             if (m_XVal[i] > nXVal)
  332.             {
  333.                 break;
  334.             }
  335.         }
  336.  
  337.         // adjust the pointer and return the number of the nearest keyframe
  338.  
  339.         DWORD nKeyframe1 = i - 1;
  340.         DWORD nKeyframe2 = i;
  341.         if(i == 0)
  342.         {
  343.             nKeyframe1 = nKeyframe2 = 0;
  344.         }
  345.  
  346.         nKeyframe1 = DMIN(nKeyframe1, m_pModelAnim->NumKeyFrames());
  347.         nKeyframe2 = DMIN(nKeyframe2, m_pModelAnim->NumKeyFrames());
  348.         
  349.         float nPercent = (float)(m_nCurrentTime - m_pModelAnim->m_KeyFrames[nKeyframe1].m_Time) / 
  350.                          (float)(m_pModelAnim->m_KeyFrames[nKeyframe2].m_Time - m_pModelAnim->m_KeyFrames[nKeyframe1].m_Time);
  351.  
  352.         if (nPercent >= 0.5f)
  353.         {
  354.             nClosestKeyframe = nKeyframe2;
  355.         }
  356.         else
  357.         {
  358.             nClosestKeyframe = nKeyframe1;
  359.         }
  360.     }
  361.  
  362.     m_nCurrentTime = m_pModelAnim->m_KeyFrames[nClosestKeyframe].m_Time;
  363.     SetTime (m_nCurrentTime);
  364.     ((CModelEditDlg*)GetParent())->SetCurrentPosition(this, nClosestKeyframe, nClosestKeyframe, 0);
  365.  
  366.     return nClosestKeyframe;
  367. }
  368.  
  369.  
  370. DWORD CKeyframeWnd::GetNearestKeyframe( int x )
  371. {
  372.     if( !m_pModelAnim )
  373.         return 0;
  374.  
  375.     // see if we're already on a keyframe
  376.     DWORD nKeyframes = m_pModelAnim->m_KeyFrames;
  377.     int nXVal = x;
  378.     for (DWORD i = 0; i < nKeyframes; i++)
  379.     {
  380.         if (nXVal == m_XVal[i])
  381.         {
  382.             return i;
  383.         }
  384.  
  385.         if (m_XVal[i] > nXVal)
  386.         {
  387.             break;
  388.         }
  389.     }
  390.  
  391.     // adjust the pointer and return the number of the nearest keyframe
  392.  
  393.     DWORD nClosestKeyframe = 0;
  394.     DWORD nKeyframe1 = i - 1;
  395.     DWORD nKeyframe2 = i;
  396.     float fPercent = ((float)(nXVal - m_XVal[nKeyframe1])) / ((float)(m_XVal[nKeyframe2] - m_XVal[nKeyframe1]));
  397.  
  398.     if (fPercent >= 0.5f)
  399.     {
  400.         nClosestKeyframe = nKeyframe2;
  401.     }
  402.     else
  403.     {
  404.         nClosestKeyframe = nKeyframe1;
  405.     }
  406.  
  407.     return nClosestKeyframe;
  408. }
  409.  
  410.  
  411. void CKeyframeWnd::RemoveKeyframe (DWORD nKeyframe)
  412. {
  413.     DWORD nKeyframes;
  414.  
  415.     if(!m_pModelAnim)
  416.         return;
  417.  
  418.     nKeyframes = m_pModelAnim->NumKeyFrames();
  419.     if(!m_pModelAnim->RemoveKeyFrame(nKeyframe))
  420.         return;
  421.  
  422.     m_XVal.Remove(nKeyframe);
  423.  
  424.     // don't delete tag information from tagged array, just move all down one
  425.     m_Tagged.Remove(nKeyframe);
  426.  
  427.     m_nCurrentTime = DCLAMP(m_nCurrentTime, (int)0, (int)m_pModelAnim->GetAnimTime());
  428. }
  429.  
  430.  
  431. void CKeyframeWnd::MoveKeyframe (DWORD nDst, DWORD nSrc)
  432. {
  433.     ModelAnim *pKeyFrame;
  434.     DWORD nOldKeyFrames;
  435.  
  436.  
  437.     nOldKeyFrames = m_pModelAnim->NumKeyFrames();
  438.  
  439.     // get adjusted destination index
  440.     DWORD nAdjustedDst = nDst;
  441.     if (nDst > nSrc)
  442.     {
  443.         nAdjustedDst--;
  444.     }
  445.  
  446.     // Do the stuff in the model.
  447.     pKeyFrame = m_pModelAnim->CutKeyFrame(nSrc);
  448.     if(pKeyFrame)
  449.     {
  450.         m_pModelAnim->PasteKeyFrame(pKeyFrame, nAdjustedDst);
  451.         delete pKeyFrame;
  452.     }
  453.     else
  454.     {
  455.         return;
  456.     }    
  457.  
  458.     // move the values in the tagged array
  459.     BOOL bTagged = m_Tagged[nSrc];
  460.  
  461.     if(nSrc < nOldKeyFrames - 1)
  462.         memmove (&m_Tagged[nSrc], &m_Tagged[nSrc + 1], sizeof (BOOL) * (nOldKeyFrames - nSrc - 1));
  463.  
  464.     if(nAdjustedDst < nOldKeyFrames - 1)
  465.         memmove (&m_Tagged[nAdjustedDst + 1], &m_Tagged[nAdjustedDst], sizeof (BOOL) * ((nOldKeyFrames - 1) - nAdjustedDst));
  466.  
  467.     m_Tagged[nAdjustedDst] = bTagged;
  468. }
  469.  
  470.  
  471. ////////////////////////////////////////////////////////////////////////////
  472. // CKeyframeWnd message handlers
  473.  
  474. int CKeyframeWnd::OnCreate(LPCREATESTRUCT lpCreateStruct) 
  475. {
  476.     if (CWnd::OnCreate(lpCreateStruct) == -1)
  477.         return -1;
  478.     
  479.     CRect rcClient;
  480.     GetClientRect (rcClient);
  481.  
  482.     CDC* pDC = GetDC();
  483.  
  484.     // set up the offscreen dc
  485.  
  486.     m_dcOffscreen.CreateCompatibleDC (pDC);
  487.     m_bmpOffscreen.CreateCompatibleBitmap (pDC, rcClient.Width(), rcClient.Height());
  488.     m_pOldBmpOffscreen = m_dcOffscreen.SelectObject (&m_bmpOffscreen);
  489.  
  490.     // set up the marker dcs
  491.  
  492.     m_dcTopMarker.CreateCompatibleDC (pDC);
  493.     m_bmpTopMarker.LoadBitmap (IDB_MARKER_TOP);
  494.     m_pOldBmpTopMarker = m_dcTopMarker.SelectObject (&m_bmpTopMarker);
  495.  
  496.     m_dcBottomMarker.CreateCompatibleDC (pDC);
  497.     m_bmpBottomMarker.LoadBitmap (IDB_MARKER_BOTTOM);
  498.     m_pOldBmpBottomMarker = m_dcBottomMarker.SelectObject (&m_bmpBottomMarker);
  499.     
  500.     // set up the marker rects
  501.  
  502.     BITMAP bm;
  503.     m_bmpTopMarker.GetObject (sizeof (BITMAP), &bm);
  504.     m_rcTopMarker.SetRect (0, 0, bm.bmWidth, bm.bmHeight);
  505.  
  506.     m_bmpBottomMarker.GetObject (sizeof (BITMAP), &bm);
  507.     m_rcBottomMarker.SetRect (0, rcClient.bottom - bm.bmHeight, bm.bmWidth, rcClient.bottom);
  508.  
  509.     // create the background brush
  510.  
  511.     m_brBackground.CreateSolidBrush (RGB(192,192,192));
  512.  
  513.     // set up the keyframe rect
  514.     
  515.     m_rcKeyframes.SetRect ((m_rcTopMarker.Width() >> 1) - 1, m_rcTopMarker.Height(), rcClient.Width() - (m_rcTopMarker.Width() >> 1) + 1, m_rcBottomMarker.top);
  516.  
  517.     // draw the basic layout
  518.  
  519.     m_dcOffscreen.FillRect (rcClient, &m_brBackground);
  520.     m_dcOffscreen.Rectangle (m_rcKeyframes);
  521.     m_dcOffscreen.BitBlt (m_rcTopMarker.left, m_rcTopMarker.top, m_rcTopMarker.Width(), m_rcTopMarker.Height(), &m_dcTopMarker, 0, 0, SRCCOPY);
  522.     m_dcOffscreen.BitBlt (m_rcBottomMarker.left, m_rcBottomMarker.top, m_rcBottomMarker.Width(), m_rcBottomMarker.Height(), &m_dcBottomMarker, 0, 0, SRCCOPY);
  523.  
  524.     ReleaseDC (pDC);
  525.  
  526.     Invalidate( FALSE );
  527.     
  528.     return 0;
  529. }
  530.  
  531. void CKeyframeWnd::OnDestroy() 
  532. {
  533.     CWnd::OnDestroy();
  534.     
  535.     m_dcOffscreen.SelectObject (m_pOldBmpOffscreen);
  536.     m_dcOffscreen.DeleteDC();
  537.     m_bmpOffscreen.DeleteObject();
  538.  
  539.     m_dcTopMarker.SelectObject (m_pOldBmpTopMarker);
  540.     m_dcTopMarker.DeleteDC();
  541.     m_bmpTopMarker.DeleteObject();
  542.  
  543.     m_dcBottomMarker.SelectObject (m_pOldBmpBottomMarker);
  544.     m_dcBottomMarker.DeleteDC();
  545.     m_bmpBottomMarker.DeleteObject();
  546.  
  547.     m_brBackground.DeleteObject();
  548.  
  549. }
  550.  
  551. void CKeyframeWnd::OnPaint() 
  552. {
  553.     CPaintDC dc(this); // device context for painting
  554.     
  555.     CRect rcClient;
  556.     GetClientRect (rcClient);
  557.  
  558.     dc.BitBlt (0, 0, rcClient.Width(), rcClient.Height(), &m_dcOffscreen, 0, 0, SRCCOPY);
  559. }
  560.  
  561. void CKeyframeWnd::OnLButtonDown(UINT nFlags, CPoint point) 
  562. {
  563.     if( m_pModelAnim )
  564.     {
  565.         if (m_rcTopMarker.PtInRect (point) || m_rcBottomMarker.PtInRect (point))
  566.         {
  567.             // start dragging
  568.  
  569.             m_bTracking = TRUE;
  570.             SetCapture();
  571.             m_ptOld = point;
  572.  
  573.             GetModelEditDlg()->GetAnimInfo(m_iWnd)->StopPlayback();
  574.         }
  575.         else
  576.         {
  577.  
  578.             if( GetAsyncKeyState( VK_SHIFT ))
  579.             {
  580.                 m_bSelectionBox = TRUE;
  581.                 m_ptStartBox = point;
  582.             }
  583.             else
  584.             {
  585.                 // just immediately move to new position
  586.                 // (do this by tricking mousemove handler)
  587.  
  588.                 m_ptOld.x = m_rcTopMarker.left + (m_rcTopMarker.Width() >> 1);
  589.                 m_bTracking = TRUE;
  590.                 OnMouseMove (0, point);
  591.                 m_bTracking = FALSE;
  592.             }
  593.         }
  594.     }
  595.         
  596.     CWnd::OnLButtonDown(nFlags, point);
  597. }
  598.  
  599. void CKeyframeWnd::OnLButtonUp(UINT nFlags, CPoint point) 
  600. {
  601.     DWORD nStartKeyframe, nEndKeyframe;
  602.     int nStart, nEnd;
  603.  
  604.     if (m_bTracking)
  605.     {
  606.         // end dragging
  607.  
  608.         ReleaseCapture();
  609.         m_bTracking = FALSE;
  610.     }
  611.     else if( m_bSelectionBox )
  612.     {
  613.         // End selection box..
  614.  
  615.         ReleaseCapture();
  616.         m_bSelectionBox = FALSE;
  617.  
  618.         // Make sure start is lower than end...
  619.         if( m_ptStartBox.x <= point.x )
  620.         {
  621.             nStart = m_ptStartBox.x;
  622.             nEnd = point.x;
  623.         }
  624.         else
  625.         {
  626.             nStart = point.x;
  627.             nEnd = m_ptStartBox.x;
  628.         }
  629.  
  630.         // Get the nearest keyframes...
  631.         nStartKeyframe = GetNearestKeyframe( nStart );
  632.         nEndKeyframe = GetNearestKeyframe( nEnd );
  633.  
  634.         // Choose the keyframes between start and finish...
  635.         if( m_XVal[nStartKeyframe] < nStart )
  636.             nStartKeyframe++;
  637.         if( m_XVal[nEndKeyframe] > nEnd )
  638.             nEndKeyframe--;
  639.  
  640.         // Redraw the keyframe window...
  641.         Redraw( );
  642.  
  643.         // Tag away...
  644.         DoTagKeyframes( nStartKeyframe, nEndKeyframe );
  645.     }
  646.  
  647.     ForceNearestKeyframe();    
  648.     CWnd::OnLButtonUp(nFlags, point);
  649. }
  650.  
  651. void CKeyframeWnd::OnMouseMove(UINT nFlags, CPoint point) 
  652. {
  653.     if(!m_pModelAnim)
  654.         return;    
  655.  
  656.     if (m_bTracking)
  657.     {
  658.         CRect rcClient;
  659.         GetClientRect (rcClient);
  660.  
  661.         // get new positions of markers
  662.  
  663.         CRect rcOldTop = m_rcTopMarker;
  664.         CRect rcOldBottom = m_rcBottomMarker;
  665.  
  666.         m_rcTopMarker.OffsetRect (point.x - m_ptOld.x, 0);
  667.         m_rcBottomMarker.OffsetRect (point.x - m_ptOld.x, 0);
  668.  
  669.         if (m_rcTopMarker.left < 0)
  670.         {
  671.             m_rcTopMarker.OffsetRect (-m_rcTopMarker.left, 0);
  672.         }
  673.         if (m_rcTopMarker.right > rcClient.right - 1)
  674.         {
  675.             m_rcTopMarker.OffsetRect (rcClient.right - m_rcTopMarker.right, 0);
  676.         }
  677.  
  678.         if (m_rcBottomMarker.left < 0)
  679.         {
  680.             m_rcBottomMarker.OffsetRect (-m_rcBottomMarker.left, 0);
  681.         }
  682.         if (m_rcBottomMarker.right > rcClient.right - 1)
  683.         {
  684.             m_rcBottomMarker.OffsetRect (rcClient.right - m_rcBottomMarker.right, 0);
  685.         }
  686.  
  687.         if (rcOldTop == m_rcTopMarker && rcOldBottom == m_rcBottomMarker)
  688.         {
  689.             return;
  690.         }
  691.  
  692.         // update keyframe bar
  693.  
  694.         CRect rcUnionTop;
  695.         rcUnionTop.UnionRect (rcOldTop, m_rcTopMarker);
  696.         CRect rcUnionBottom;
  697.         rcUnionBottom.UnionRect (rcOldBottom, m_rcBottomMarker);
  698.  
  699.         m_dcOffscreen.FillRect (rcOldTop, &m_brBackground);
  700.         m_dcOffscreen.FillRect (rcOldBottom, &m_brBackground);
  701.         m_dcOffscreen.BitBlt (m_rcTopMarker.left, m_rcTopMarker.top, m_rcTopMarker.Width(), m_rcTopMarker.Height(), &m_dcTopMarker, 0, 0, SRCCOPY);
  702.         m_dcOffscreen.BitBlt (m_rcBottomMarker.left, m_rcBottomMarker.top, m_rcBottomMarker.Width(), m_rcBottomMarker.Height(), &m_dcBottomMarker, 0, 0, SRCCOPY);
  703.  
  704.         CDC* pDC = GetDC();
  705.         pDC->BitBlt (rcUnionTop.left, rcUnionTop.top, rcUnionTop.Width(), rcUnionTop.Height(), &m_dcOffscreen, rcUnionTop.left, rcUnionTop.top, SRCCOPY);
  706.         pDC->BitBlt (rcUnionBottom.left, rcUnionBottom.top, rcUnionBottom.Width(), rcUnionBottom.Height(), &m_dcOffscreen, rcUnionBottom.left, rcUnionBottom.top, SRCCOPY);
  707.         ReleaseDC (pDC);
  708.  
  709.         // get keyframes we're between
  710.  
  711.         int nXVal = m_rcTopMarker.left + (m_rcTopMarker.Width() >> 1);
  712.         for (DWORD i = 0; i < m_pModelAnim->m_KeyFrames; i++)
  713.         {
  714.             if (m_XVal[i] == nXVal)
  715.             {
  716.                 break;
  717.             }
  718.             if (m_XVal[i] > nXVal)
  719.             {
  720.                 break;
  721.             }
  722.         }
  723.         
  724.         DWORD nKeyframe1 = 0;
  725.         DWORD nKeyframe2 = 0;
  726.         float nPercent = 0.0f;
  727.         
  728.         if (m_XVal[i] != nXVal)
  729.         {
  730.             // between two keyframes
  731.  
  732.             nKeyframe1 = i - 1;
  733.             nKeyframe2 = i;
  734.             nPercent = ((float)(nXVal - m_XVal[nKeyframe1])) / ((float)(m_XVal[nKeyframe2] - m_XVal[nKeyframe1]));
  735.             m_nCurrentTime = m_pModelAnim->m_KeyFrames[nKeyframe1].m_Time + 
  736.                              (DWORD)(nPercent * (float)(m_pModelAnim->m_KeyFrames[nKeyframe2].m_Time - m_pModelAnim->m_KeyFrames[nKeyframe1].m_Time));
  737.         }
  738.         else
  739.         {
  740.             // right on a keyframe
  741.             nKeyframe1 = i;
  742.             nKeyframe2 = i;
  743.             nPercent = 0.0f;
  744.             m_nCurrentTime = m_pModelAnim->m_KeyFrames[nKeyframe1].m_Time;
  745.         }
  746.  
  747.         GetModelEditDlg()->SetCurrentPosition (this, nKeyframe1, nKeyframe2, nPercent);
  748.  
  749.         m_ptOld = point;
  750.     }
  751.     else if( m_bSelectionBox )
  752.     {
  753.         CRect rcSelectionBox;
  754.         CPoint ptClientPoint;
  755.  
  756.         rcSelectionBox = m_rcKeyframes;
  757.         rcSelectionBox.left = m_ptStartBox.x;
  758.         rcSelectionBox.right = point.x;
  759.         Redraw( );
  760.         m_dcOffscreen.InvertRect( &rcSelectionBox );
  761.         Invalidate( FALSE );
  762.     }
  763.     
  764.     CWnd::OnMouseMove(nFlags, point);
  765. }
  766.  
  767. void CKeyframeWnd::OnRButtonDown(UINT nFlags, CPoint point) 
  768. {
  769.     ClientToScreen (&point);
  770.  
  771.     CMenu menu;
  772.     menu.LoadMenu (IDR_KEYFRAME_MENU);
  773.     CMenu* pMenu = menu.GetSubMenu (0);
  774.     pMenu->TrackPopupMenu (TPM_LEFTALIGN | TPM_LEFTBUTTON, point.x, point.y, this, NULL);
  775.     
  776.     CWnd::OnRButtonDown(nFlags, point);
  777. }
  778.  
  779. void CKeyframeWnd::OnClipAfter() 
  780. {
  781.     if(!CanPerformOperation("Clip After Keyframe"))
  782.         return;
  783.  
  784.     DWORD nKeyframes = m_pModelAnim->m_KeyFrames;
  785.     DWORD nKeyframe = ForceNearestKeyframe();
  786.     if (nKeyframe == nKeyframes) return;
  787.     
  788.     if (nKeyframe == nKeyframes - 1)
  789.     {
  790.         MessageBox ("Cannot clip past the last keyframe", "Clip Animation", MB_OK | MB_ICONINFORMATION);
  791.         return;
  792.     }
  793.  
  794.     int nResponse = MessageBox ("Are you sure you want to clip the animation past the current keyframe?", "Confirm Clip", MB_YESNO | MB_ICONQUESTION);
  795.     if (nResponse == IDNO)
  796.     {
  797.         return;
  798.     }
  799.  
  800.     // remove all keyframes before the current position
  801.  
  802.     for (long i = nKeyframes - 1; i > (long)nKeyframe; i--)
  803.     {
  804.         RemoveKeyframe ((DWORD)i);
  805.     }
  806.  
  807.     // notify selves and parent dialog of the changes
  808.  
  809.     DWORD nTime = m_nCurrentTime;
  810.     SetAnim (m_pModelAnim);
  811.     SetTime (nTime);
  812.     GetModelEditDlg()->SetCurrentPosition (this, nKeyframe, nKeyframe, 0.0f);
  813.     GetModelEditDlg()->SetChangesMade();
  814. }
  815.  
  816. void CKeyframeWnd::OnClipBefore() 
  817. {
  818.     if(!CanPerformOperation("Clip Before Keyframe"))
  819.         return;
  820.  
  821.     DWORD nKeyframes = m_pModelAnim->m_KeyFrames;
  822.     DWORD nKeyframe = ForceNearestKeyframe();
  823.     if (nKeyframe == nKeyframes) return;
  824.     
  825.     if (nKeyframe == 0)
  826.     {
  827.         MessageBox ("Cannot clip before the first keyframe", "Clip Animation", MB_OK | MB_ICONINFORMATION);
  828.         return;
  829.     }
  830.     
  831.     int nResponse = MessageBox ("Are you sure you want to clip the animation before the current keyframe?", "Confirm Clip", MB_YESNO | MB_ICONQUESTION);
  832.     if (nResponse == IDNO)
  833.     {
  834.         return;
  835.     }
  836.  
  837.     // remove all keyframes before the current position
  838.  
  839.     for (long i = nKeyframe - 1; i >= 0; i--)
  840.     {
  841.         RemoveKeyframe ((DWORD)i);
  842.     }
  843.  
  844.     // notify selves and parent dialog of the changes
  845.  
  846.     m_nCurrentTime = 0;
  847.     DWORD nTime = m_nCurrentTime;
  848.     SetAnim (m_pModelAnim);
  849.     SetTime (nTime);
  850.     GetModelEditDlg()->SetCurrentPosition (this, 0, 0, 0.0f);
  851.     GetModelEditDlg()->SetChangesMade();
  852. }
  853.  
  854. void CKeyframeWnd::OnTagKeyframe() 
  855. {
  856.     // attempt to get current keyframe
  857.  
  858.     DWORD nKeyframes = m_pModelAnim->m_KeyFrames;
  859.     DWORD nKeyframe = ForceNearestKeyframe();
  860.     if (nKeyframe == nKeyframes)
  861.     {
  862.         return;
  863.     }
  864.  
  865.     // can't tag first or last keyframe
  866.  
  867.     if (nKeyframe == 0 || nKeyframe == nKeyframes - 1)
  868.     {
  869.         MessageBox ("Cannot tag the first nor the last keyframe in an animation", "Tag Keyframe", MB_OK | MB_ICONINFORMATION);
  870.         return;
  871.     }
  872.  
  873.     DoTagKeyframes( nKeyframe, nKeyframe );
  874. }
  875.  
  876. // Tag keyframe range.  bForce forces value to bTag, rather than just toggle
  877. BOOL CKeyframeWnd::DoTagKeyframes( DWORD nStartKeyframe, DWORD nEndKeyframe, BOOL bForce, BOOL bTag )
  878. {
  879.     if( !m_pModelAnim )
  880.         return FALSE;
  881.  
  882.     // attempt to get current keyframe
  883.     DWORD nKeyframe;
  884.     BOOL bRet;
  885.     DWORD nKeyframes = m_pModelAnim->m_KeyFrames;
  886.     if (nEndKeyframe == nKeyframes)
  887.     {
  888.         return FALSE;
  889.     }
  890.  
  891.     // can't tag first or last keyframe
  892.  
  893.     if (nStartKeyframe == 0 || nEndKeyframe >= nKeyframes - 1)
  894.     {
  895.         MessageBox ("Cannot tag the first nor the last keyframe in an animation", "Tag Keyframe", MB_OK | MB_ICONINFORMATION);
  896.         return FALSE;
  897.     }
  898.  
  899.     // get the tagged array
  900.     // make sure there are at least 3 untagged keyframes
  901.     int nUntagged = 0;
  902.     for (DWORD i = 0; i < nKeyframes; i++)
  903.     {
  904.         if (!m_Tagged[i])
  905.         {
  906.             nUntagged++;
  907.         }
  908.     }
  909.  
  910.     bRet = TRUE;
  911.     for( nKeyframe = nStartKeyframe; nKeyframe <= nEndKeyframe && bRet; nKeyframe++ )
  912.     {
  913.         // if the keyframe is already tagged, untag it...
  914.         if (m_Tagged[nKeyframe])
  915.         {            
  916.             if( !bForce || ( bForce && bTag == FALSE ))
  917.             {
  918.                 // draw the line in the normal color
  919.                 CPen pen;
  920.                 if( m_pModelAnim->m_KeyFrames[nKeyframe].m_pString[0] == '\0' )
  921.                     pen.CreatePen( PS_SOLID, 1, m_crKeyframe );
  922.                 else
  923.                     pen.CreatePen( PS_SOLID, 1, m_crKeyframeString );
  924.                 CPen* pOldPen = m_dcOffscreen.SelectObject (&pen);
  925.                 m_dcOffscreen.MoveTo (m_XVal[nKeyframe], m_rcKeyframes.top + 1);
  926.                 m_dcOffscreen.LineTo (m_XVal[nKeyframe], m_rcKeyframes.bottom - 1);
  927.                 m_dcOffscreen.SelectObject (pOldPen);
  928.                 pen.DeleteObject();
  929.  
  930.                 CRect rcInvalid (m_XVal[nKeyframe], m_rcKeyframes.top + 1, m_XVal[nKeyframe] + 1, m_rcKeyframes.bottom - 1);
  931.                 InvalidateRect (rcInvalid, FALSE);
  932.                 
  933.                 // mark the keyframe as untagged
  934.                 m_Tagged[nKeyframe] = FALSE;
  935.                 nUntagged++;
  936.             }
  937.         }
  938.         else
  939.         {
  940.             if( !bForce || ( bForce && bTag == TRUE ))
  941.             {
  942.                 if (nUntagged < 3)
  943.                 {
  944.                     MessageBox ("Must have at least 2 untagged keyframes", "Tag Keyframe", MB_OK | MB_ICONINFORMATION);
  945.                     bRet = FALSE;
  946.                 }
  947.  
  948.                 // draw the line in the tagged color
  949.                 CPen pen;
  950.                 if( m_pModelAnim->m_KeyFrames[nKeyframe].m_pString[0] == '\0' )
  951.                     pen.CreatePen( PS_SOLID, 1, m_crTaggedKeyframe );
  952.                 else
  953.                     pen.CreatePen( PS_SOLID, 1, m_crTaggedKeyframeString );
  954.                 CPen* pOldPen = m_dcOffscreen.SelectObject (&pen);
  955.                 m_dcOffscreen.MoveTo (m_XVal[nKeyframe], m_rcKeyframes.top + 1);
  956.                 m_dcOffscreen.LineTo (m_XVal[nKeyframe], m_rcKeyframes.bottom - 1);
  957.                 m_dcOffscreen.SelectObject (pOldPen);
  958.                 pen.DeleteObject();
  959.  
  960.                 CRect rcInvalid (m_XVal[nKeyframe], m_rcKeyframes.top + 1, m_XVal[nKeyframe] + 1, m_rcKeyframes.bottom - 1);
  961.                 InvalidateRect (rcInvalid, FALSE);
  962.  
  963.                 // mark the keyframe as tagged
  964.  
  965.                 m_Tagged[nKeyframe] = TRUE;
  966.                 nUntagged--;
  967.             }
  968.         }
  969.     }
  970.  
  971.     return bRet;
  972. }
  973.  
  974. void CKeyframeWnd::OnUntagKeyframe() 
  975. {
  976.     // attempt to get current keyframe
  977.  
  978.     DWORD nKeyframe = ForceNearestKeyframe();
  979.     if (nKeyframe == m_pModelAnim->m_KeyFrames)
  980.     {
  981.         return;
  982.     }
  983.     
  984.     // get the tagged array
  985.  
  986.     // if the keyframe is already untagged, just return
  987.  
  988.     if (!m_Tagged[nKeyframe])
  989.     {
  990.         return;
  991.     }
  992.  
  993.     // draw the line in the normal color
  994.  
  995.     CPen pen;
  996.     if( m_pModelAnim->m_KeyFrames[nKeyframe].m_pString[0] == '\0' )
  997.         pen.CreatePen( PS_SOLID, 1, m_crKeyframe );
  998.     else
  999.         pen.CreatePen( PS_SOLID, 1, m_crKeyframeString );
  1000.     CPen* pOldPen = m_dcOffscreen.SelectObject (&pen);
  1001.     m_dcOffscreen.MoveTo (m_XVal[nKeyframe], m_rcKeyframes.top + 1);
  1002.     m_dcOffscreen.LineTo (m_XVal[nKeyframe], m_rcKeyframes.bottom - 1);
  1003.     m_dcOffscreen.SelectObject (pOldPen);
  1004.     pen.DeleteObject();
  1005.  
  1006.     CRect rcInvalid (m_XVal[nKeyframe], m_rcKeyframes.top + 1, m_XVal[nKeyframe] + 1, m_rcKeyframes.bottom - 1);
  1007.     InvalidateRect (rcInvalid, FALSE);
  1008.     
  1009.     // mark the keyframe as untagged
  1010.  
  1011.     m_Tagged[nKeyframe] = FALSE;
  1012. }
  1013.  
  1014. void CKeyframeWnd::OnEditKeyframeTime() 
  1015. {
  1016.     // get nearest keyframe
  1017.  
  1018.     DWORD nKeyframes = m_pModelAnim->m_KeyFrames;
  1019.     DWORD nKeyframe = ForceNearestKeyframe();
  1020.     if (nKeyframe == nKeyframes)
  1021.     {
  1022.         return;
  1023.     }
  1024.  
  1025.     // cannot edit time of first keyframe
  1026.  
  1027.     if (nKeyframe == 0)
  1028.     {
  1029.         MessageBox ("Cannot edit keyframe time of first keyframe", "Edit Keyframe Time", MB_OK | MB_ICONINFORMATION);
  1030.         return;
  1031.     }
  1032.  
  1033.     // present the dialog to the user
  1034.  
  1035.     CKeyframeTimeDlg dlg;
  1036.     dlg.m_nCurrentTime = m_pModelAnim->m_KeyFrames[nKeyframe].m_Time;
  1037.     dlg.m_nNewTime = dlg.m_nCurrentTime;
  1038.     dlg.m_sCaption = "Edit Keyframe Time";
  1039.     if (dlg.DoModal() == IDCANCEL || dlg.m_nNewTime == dlg.m_nCurrentTime)
  1040.     {
  1041.         return;
  1042.     }
  1043.  
  1044.     DoEditKeyframeTime( nKeyframe, dlg.m_nNewTime );
  1045. }
  1046.  
  1047. void CKeyframeWnd::DoEditKeyframeTime( DWORD nKeyframe, DWORD dwNewKeyframeTime )
  1048. {
  1049.     // get nearest keyframe
  1050.     if( !m_pModelAnim )
  1051.         return;
  1052.  
  1053.     DWORD nKeyframes = m_pModelAnim->m_KeyFrames;
  1054.     if (nKeyframe == nKeyframes)
  1055.     {
  1056.         return;
  1057.     }
  1058.  
  1059.     // cannot edit time of first keyframe
  1060.  
  1061.     if (nKeyframe == 0)
  1062.     {
  1063.         MessageBox ("Cannot edit keyframe time of first keyframe", "Edit Keyframe Time", MB_OK | MB_ICONINFORMATION);
  1064.         return;
  1065.     }
  1066.  
  1067.     // present the dialog to the user
  1068.  
  1069.     if(dwNewKeyframeTime == m_pModelAnim->m_KeyFrames[nKeyframe].m_Time)
  1070.     {
  1071.         return;
  1072.     }
  1073.  
  1074.     // find where it's new position would be, and check for a duplicate time value
  1075.  
  1076.     for (DWORD i = 0; i < nKeyframes; i++)
  1077.     {
  1078.         if (m_pModelAnim->m_KeyFrames[i].m_Time == dwNewKeyframeTime && i != nKeyframe)
  1079.         {
  1080.             MessageBox ("A keyframe already exists with that time value.  Not changing keyframe time.", "Edit Keyframe Time", MB_OK | MB_ICONINFORMATION);
  1081.             return;
  1082.         }
  1083.  
  1084.         if (m_pModelAnim->m_KeyFrames[i].m_Time > dwNewKeyframeTime)
  1085.         {
  1086.             break;
  1087.         }
  1088.     }
  1089.  
  1090.     // set the new time value
  1091.  
  1092.     m_pModelAnim->m_KeyFrames[nKeyframe].m_Time = dwNewKeyframeTime;
  1093.  
  1094.     // if it's new position is the same, we don't need to do anything more...
  1095.  
  1096.     if (i - 1 != nKeyframe)
  1097.     {
  1098.         // new position different - take appropriate action
  1099.  
  1100.         MoveKeyframe (i, nKeyframe);
  1101.     }
  1102.  
  1103.  
  1104.     int32 nKeyToSelect = (nKeyframe < i) ? i - 1 : i;
  1105.  
  1106.     GetModelEditDlg()->SetCurrentPosition (this, nKeyToSelect, nKeyToSelect, 0);
  1107.     GetModelEditDlg()->SetChangesMade();
  1108.  
  1109.     SetAnim (m_pModelAnim);
  1110.     SetTime (dwNewKeyframeTime);
  1111. }
  1112.  
  1113. void CKeyframeWnd::OnDeleteKeyframe() 
  1114. {
  1115.     if(!CanPerformOperation("Delete Keyframe"))
  1116.         return;
  1117.  
  1118.     // get nearest keyframe
  1119.  
  1120.     DWORD nKeyframes = m_pModelAnim->m_KeyFrames;
  1121.     DWORD nKeyframe = ForceNearestKeyframe();
  1122.     if (nKeyframe == nKeyframes)
  1123.     {
  1124.         return;
  1125.     }
  1126.     
  1127.     // must be at least two keyframes in an animation
  1128.  
  1129.     if (nKeyframes <= 2)
  1130.     {
  1131.         MessageBox ("Cannot delete one of last two keyframes", "Delete Keyframe", MB_OK | MB_ICONINFORMATION);
  1132.         return;
  1133.     }
  1134.  
  1135.     // remove it
  1136.  
  1137.     RemoveKeyframe (nKeyframe);
  1138.  
  1139.     // let the dialog know what the current status is
  1140.  
  1141.     DWORD nSelKeyframe = LTMAX(0, LTMIN((int32)nKeyframes - 1, (int32)nKeyframe - 1));    
  1142.     GetModelEditDlg()->SetCurrentPosition (this, nSelKeyframe, nSelKeyframe, 0.0f);
  1143.     GetModelEditDlg()->SetChangesMade();
  1144.     
  1145.     SetAnim (m_pModelAnim);
  1146.     SetTime (m_pModelAnim->m_KeyFrames[nSelKeyframe].m_Time);
  1147. }
  1148.  
  1149. void CKeyframeWnd::OnDeleteTaggedKeyframes() 
  1150. {
  1151.     if(!CanPerformOperation("Delete Tagged Keyframes"))
  1152.         return;
  1153.  
  1154.     // go through and remove all tagged keyframes
  1155.  
  1156.     DWORD nKeyframes = m_pModelAnim->m_KeyFrames;
  1157.  
  1158.     for (long j = nKeyframes - 1; j >= 0; j--)
  1159.     {
  1160.         if (m_Tagged[j])
  1161.         {
  1162.             RemoveKeyframe ((DWORD)j);
  1163.         }
  1164.     }
  1165.  
  1166.     // now find out where we are on the bar and let the dialog know the status
  1167.  
  1168.     for (DWORD i = 0; i < m_pModelAnim->m_KeyFrames; i++)
  1169.     {
  1170.         if (m_pModelAnim->m_KeyFrames[i].m_Time >= m_nCurrentTime)
  1171.         {
  1172.             break;
  1173.         }
  1174.     }
  1175.  
  1176.     //make sure it is within range
  1177.     uint32 nSelKeyframe = LTMAX(0, LTMIN((int32)m_pModelAnim->m_KeyFrames - 1, (int32)i - 1));    
  1178.  
  1179.     GetModelEditDlg()->SetCurrentPosition (this, nSelKeyframe, nSelKeyframe, 0.0f);
  1180.     GetModelEditDlg()->SetChangesMade();
  1181.     
  1182.     SetAnim (m_pModelAnim);
  1183.     SetTime (m_pModelAnim->m_KeyFrames[nSelKeyframe].m_Time);
  1184. }
  1185.  
  1186.  
  1187. void CKeyframeWnd::OnEditTaggedKeyframeString()
  1188. {
  1189.     if(!CanPerformOperation("Edit Tagged Keyframes String"))
  1190.         return;
  1191.  
  1192.     DWORD nKeyframe;
  1193.     CKeyframeStringDlg dlg;
  1194.     Model *pModel;
  1195.     int nTaggedKeyframes = 0;
  1196.  
  1197.     pModel = ((CModelEditDlg*)GetParent())->GetModel();
  1198.     if(pModel && m_pModelAnim)
  1199.     {
  1200.         // Loop over the tagged keyframes and see if all the strings match...
  1201.         for( nKeyframe = 0; nKeyframe < m_pModelAnim->m_KeyFrames; nKeyframe++ )
  1202.         {
  1203.             // Keyframe tagged?
  1204.             if( m_Tagged[nKeyframe] )
  1205.             {
  1206.                 // On the first one found, accept it's string as the master...
  1207.                 if( nTaggedKeyframes == 0 )
  1208.                     dlg.m_KeyframeString = m_pModelAnim->m_KeyFrames[nKeyframe].m_pString;
  1209.                 else
  1210.                 {
  1211.                     // Check if the strings are the same...
  1212.                     if( strcmp( dlg.m_KeyframeString, m_pModelAnim->m_KeyFrames[nKeyframe].m_pString ) != 0 )
  1213.                     {
  1214.                         // Strings are different, set master string to blank...
  1215.                         dlg.m_KeyframeString = "";
  1216.                         break;
  1217.                     }
  1218.                 }
  1219.                 nTaggedKeyframes++;
  1220.             }
  1221.         }
  1222.  
  1223.         // No keyframes tagged, nothing to do...
  1224.         if( nTaggedKeyframes == 0 )
  1225.             return;
  1226.  
  1227.         if(dlg.DoModal() == IDOK)
  1228.         {
  1229.             // Loop over the tagged keyframes and set the strings.
  1230.             for( nKeyframe = 0; nKeyframe < m_pModelAnim->m_KeyFrames; nKeyframe++ )
  1231.             {
  1232.                 // Keyframe tagged?
  1233.                 if( m_Tagged[nKeyframe] )
  1234.                 {
  1235.                     DoEditKeyframeString( nKeyframe, dlg.m_KeyframeString );
  1236.                 }
  1237.             }
  1238.         }
  1239.  
  1240.         ((CModelEditDlg*)GetParent())->UpdateEditFrameString( );
  1241.     }
  1242. }
  1243.  
  1244. void CKeyframeWnd::DoEditKeyframeString( DWORD nKeyframe, CString &sFrameString )
  1245. {
  1246.     Model *pModel;
  1247.  
  1248.     pModel = ((CModelEditDlg*)GetParent())->GetModel();
  1249.     if(pModel && m_pModelAnim)
  1250.     {
  1251.         m_pModelAnim->m_KeyFrames[nKeyframe].m_pString = 
  1252.             pModel->AddString((char*)(LPCTSTR)sFrameString);
  1253.         Redraw();
  1254.         ((CModelEditDlg*)GetParent())->SetChangesMade();
  1255.     }
  1256. }
  1257.  
  1258. void CKeyframeWnd::OnLButtonDblClk(UINT nFlags, CPoint point) 
  1259. {
  1260.     // just immediately move to new position
  1261.     // (do this by tricking mousemove handler)
  1262.  
  1263.     m_ptOld.x = m_rcTopMarker.left + (m_rcTopMarker.Width() >> 1);
  1264.     m_bTracking = TRUE;
  1265.     OnMouseMove (0, point);
  1266.     m_bTracking = FALSE;
  1267.     DWORD nKeyframe = ForceNearestKeyframe();
  1268.     DoTagKeyframes( nKeyframe, nKeyframe );
  1269.     
  1270.     CWnd::OnLButtonDblClk(nFlags, point);
  1271. }
  1272.  
  1273.  
  1274. void CKeyframeWnd::OnTagAll()
  1275. {
  1276.     DWORD nKeyframes = m_pModelAnim->m_KeyFrames;
  1277.  
  1278.     if( nKeyframes < 3 )
  1279.         return;
  1280.  
  1281.     DoTagKeyframes( 1, nKeyframes - 2, TRUE, TRUE );
  1282. }
  1283.  
  1284. void CKeyframeWnd::OnUnTagAll()
  1285. {
  1286.     DWORD nKeyframes = m_pModelAnim->m_KeyFrames;
  1287.  
  1288.     if( nKeyframes < 3 )
  1289.         return;
  1290.  
  1291.     DoTagKeyframes( 1, nKeyframes - 2, TRUE, FALSE );
  1292. }
  1293.  
  1294.  
  1295. void CKeyframeWnd::OnInsertTimeDelay()
  1296. {
  1297.     if(!CanPerformOperation("Insert Time Delay"))
  1298.         return;
  1299.  
  1300.     DWORD i, nKeyframe;
  1301.     TimeOffsetDlg dlg;
  1302.  
  1303.     if(!m_pModelAnim)
  1304.         return;
  1305.     
  1306.     if(dlg.DoModal() == IDOK)
  1307.     {
  1308.         nKeyframe = ForceNearestKeyframe();
  1309.  
  1310.         for(i=nKeyframe; i < m_pModelAnim->NumKeyFrames(); i++)
  1311.         {
  1312.             m_pModelAnim->m_KeyFrames[i].m_Time += dlg.m_TimeOffset;
  1313.         }
  1314.  
  1315.         Redraw();
  1316.         GetModelEditDlg()->SetChangesMade();
  1317.     }        
  1318. }
  1319.