|
|
#include "stdafx.h"
#include "Ctrl.h"
#include "Animation.h"
#if ENABLE_MSGTABLE_API
/***************************************************************************\
***************************************************************************** * * Global Functions * ***************************************************************************** \***************************************************************************/
//------------------------------------------------------------------------------
DUSER_API void WINAPI DUserStopAnimation(Visual * pgvSubject, PRID pridAni) { if (pgvSubject == NULL) { PromptInvalid("Invalid pgvSubject"); return; } if (pridAni <= 0) { PromptInvalid("Invalid Animation pridAni"); return; }
DuExtension * pext = DuExtension::GetExtension(pgvSubject, pridAni); if (pext != NULL) { pext->GetStub()->OnRemoveExisting(); } }
/***************************************************************************\
***************************************************************************** * * class DuAnimation * ***************************************************************************** \***************************************************************************/
MSGID DuAnimation::s_msgidComplete = 0;
//------------------------------------------------------------------------------
DuAnimation::~DuAnimation() { Destroy(TRUE);
SafeRelease(m_pipol); SafeRelease(m_pgflow);
#if DEBUG_TRACECREATION
Trace("STOP Animation 0x%p @ %d (%d frames)\n", this, GetTickCount(), m_DEBUG_cUpdates); #endif // DEBUG_TRACECREATION
//
// Ensure proper destruction
//
AssertMsg(m_hact == NULL, "Action should already be destroyed"); }
/***************************************************************************\
* * DuAnimation::InitClass * * InitClass() is called during startup and provides an opportunity to * initialize common data. * \***************************************************************************/
HRESULT DuAnimation::InitClass() { s_msgidComplete = RegisterGadgetMessage(&_uuidof(Animation::evComplete)); if (s_msgidComplete == 0) { return (HRESULT) GetLastError(); }
return S_OK; }
//------------------------------------------------------------------------------
HRESULT DuAnimation::PreBuild(DUser::Gadget::ConstructInfo * pci) { //
// Validate parameters
//
Animation::AniCI * pDesc = reinterpret_cast<Animation::AniCI *>(pci); if ((pDesc->pipol == NULL) || (pDesc->pgflow == NULL)) { PromptInvalid("Must provide valid Interpolation and Flow objects"); return E_INVALIDARG; }
PRID pridExtension = 0; VerifyHR(pDesc->pgflow->GetPRID(&pridExtension)); if (pridExtension == 0) { PromptInvalid("Flow must register PRID"); return E_INVALIDARG; }
return S_OK; }
//------------------------------------------------------------------------------
HRESULT DuAnimation::PostBuild(DUser::Gadget::ConstructInfo * pci) { //
// Check parameters. This should be validated in PreBuild().
//
Animation::AniCI * pDesc = reinterpret_cast<Animation::AniCI *>(pci);
Assert(pDesc->pipol != NULL); Assert(pDesc->pgflow != NULL);
//
// Setup the Action
//
GMA_ACTION gma; ZeroMemory(&gma, sizeof(gma)); gma.cbSize = sizeof(gma); gma.flDelay = pDesc->act.flDelay; gma.flDuration = pDesc->act.flDuration; gma.flPeriod = pDesc->act.flPeriod; gma.cRepeat = pDesc->act.cRepeat; gma.dwPause = pDesc->act.dwPause; gma.pfnProc = RawActionProc; gma.pvData = this;
m_hact = CreateAction(&gma); if (m_hact == NULL) { return (HRESULT) GetLastError(); }
PRID pridExtension; VerifyHR(pDesc->pgflow->GetPRID(&pridExtension)); HRESULT hr = DuExtension::Create(pDesc->pgvSubject, pridExtension, DuExtension::oAsyncDestroy); if (FAILED(hr)) { return hr; }
//
// Store the related objects
//
pDesc->pipol->AddRef(); pDesc->pgflow->AddRef();
m_pipol = pDesc->pipol; m_pgflow = pDesc->pgflow;
//
// Animations need to be AddRef()'d again (have a reference count of 2)
// because they need to outlive the initial call to Release() after the
// called has setup the animation returned from BuildAnimation().
//
// This is because the Animation continues to life until it has fully
// executed (or has been aborted).
//
AddRef();
return S_OK; }
//------------------------------------------------------------------------------
void DuAnimation::Destroy(BOOL fFinal) { //
// Mark that we have already started the destruction process and don't need
// to start again. We only want to post the destruction message once.
//
if (m_fStartDestroy) { return; } m_fStartDestroy = TRUE;
if (m_pgvSubject != NULL) { #if DBG
DuAnimation * paniExist = static_cast<DuAnimation *> (GetExtension(m_pgvSubject, m_pridListen)); if (paniExist != NULL) { AssertMsg(paniExist == this, "Animations must match"); } #endif // DBG
CleanupChangeGadget(); }
//
// Destroy the Animation
//
AssertMsg(!fFinal, "Object is already being destructed"); if (fFinal) { GetStub()->OnAsyncDestroy(); } else { PostAsyncDestroy(); } }
//------------------------------------------------------------------------------
HRESULT DuAnimation::ApiOnAsyncDestroy(Animation::OnAsyncDestroyMsg *) { AssertMsg(m_fStartDestroy, "Must call Destroy() to start the destruction process."); AssertMsg(!m_fProcessing, "Should not be processing when start destruction");
AssertMsg(m_pgvSubject == NULL, "Animation should already have detached from Gadget"); HACTION hact = m_hact;
//
// Set everything to NULL now.
//
m_hact = NULL; if (hact != NULL) { ::DeleteHandle(hact); hact = NULL; }
Release();
return S_OK; }
//------------------------------------------------------------------------------
HRESULT DuAnimation::ApiSetTime(Animation::SetTimeMsg * pmsg) { GMA_ACTIONINFO mai;
//
// TODO: Need to save these values from the last time so that they are
// valid.
//
mai.hact = m_hact; mai.pvData = this; mai.flDuration = 0.0f;
m_time = (Animation::ETime) pmsg->time; switch (pmsg->time) { case Animation::tComplete: // Don't do anything
return S_OK;
default: case Animation::tAbort: case Animation::tDestroy: goto Done;
case Animation::tEnd: mai.flProgress = 1.0f; break;
case Animation::tReset: mai.flProgress = 0.0f; break; }
mai.cEvent = 0; mai.cPeriods = 0; mai.fFinished = FALSE;
m_fProcessing = TRUE; m_pgflow->OnAction(m_pgvSubject, m_pipol, mai.flProgress); Assert(m_fProcessing); m_fProcessing = FALSE;
Done: ::DeleteHandle(m_hact); return S_OK; }
//------------------------------------------------------------------------------
void DuAnimation::CleanupChangeGadget() { //
// Give the derived Animation a chance to cleanup
//
// Check that we are still the Animation attached to this Gadget. We need
// to remove this property immediately. We can not wait for a posted
// message to be processed because we may need to set it right now if we are
// creating a new Animation.
//
BOOL fStarted = FALSE;
Animation::CompleteEvent msg; msg.cbSize = sizeof(msg); msg.nMsg = s_msgidComplete; msg.hgadMsg = GetHandle(); msg.fNormal = IsStartDelete(m_pgvSubject->GetHandle(), &fStarted) && (!fStarted);
DUserSendEvent(&msg, 0);
Assert(m_pgvSubject != NULL); Assert(m_pridListen != 0);
Verify(SUCCEEDED(m_pgvSubject->RemoveProperty(m_pridListen)));
m_pgvSubject = NULL; }
//------------------------------------------------------------------------------
void CALLBACK DuAnimation::RawActionProc( IN GMA_ACTIONINFO * pmai) { //
// Need to AddRef while processing the Animation to ensure that it does not
// get destroyed from under us, for example, during one of the callbacks.
//
DuAnimation * pani = (DuAnimation *) pmai->pvData; pani->AddRef();
Assert(!pani->m_fProcessing);
#if DEBUG_TRACECREATION
Trace("START RawActionP 0x%p @ %d\n", pani, GetTickCount()); #endif // DEBUG_TRACECREATION
pani->ActionProc(pmai);
#if DEBUG_TRACECREATION
Trace("STOP RawActionP 0x%p @ %d\n", pani, GetTickCount()); #endif // DEBUG_TRACECREATION
Assert(!pani->m_fProcessing);
pani->Release(); }
//------------------------------------------------------------------------------
void DuAnimation::ActionProc( IN GMA_ACTIONINFO * pmai) { #if DBG
m_DEBUG_cUpdates++; #endif // DBG
if ((!m_fStartDestroy) && (m_pgvSubject != NULL)) { //
// This ActionProc will be called when the Action is being destroyed, so
// we only want to invoke the Action under certain circumstances.
//
switch (m_time) { case Animation::tComplete: case Animation::tEnd: case Animation::tReset: //
// All of these are valid to complete. If it isn't in this list, we
// don't want to execute it during a shutdown.
//
m_fProcessing = TRUE; m_pgflow->OnAction(m_pgvSubject, m_pipol, pmai->flProgress); Assert(m_fProcessing); m_fProcessing = FALSE; break; } }
if (pmai->fFinished) { m_hact = NULL; Destroy(FALSE); } }
//------------------------------------------------------------------------------
HRESULT DuAnimation::ApiOnRemoveExisting(Animation::OnRemoveExistingMsg *) { GetStub()->SetTime(Animation::tDestroy); return S_OK; }
//------------------------------------------------------------------------------
HRESULT DuAnimation::ApiOnDestroySubject(Animation::OnDestroySubjectMsg *) { AddRef();
if (m_pgvSubject != NULL) { CleanupChangeGadget();
//
// The Gadget that we are modifying is being destroyed, so we need
// to stop animating it.
//
m_time = Animation::tDestroy; Destroy(FALSE); }
Release();
return S_OK; }
#else
//------------------------------------------------------------------------------
DUSER_API void WINAPI DUserStopAnimation(Visual * pgvSubject, PRID pridAni) { UNREFERENCED_PARAMETER(pgvSubject); UNREFERENCED_PARAMETER(pridAni);
PromptInvalid("Not implemented without MsgTable support"); }
#endif // ENABLE_MSGTABLE_API
|