You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
445 lines
11 KiB
445 lines
11 KiB
#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
|