windows-nt/Source/XPSP1/NT/shell/ext/logondui/fx.cpp
2020-09-26 16:20:57 +08:00

1135 lines
30 KiB
C++

#include "priv.h"
#ifdef GADGET_ENABLE_GDIPLUS
using namespace DirectUI;
#include "Logon.h"
#include "Fx.h"
#include "Stub.h"
#include "Super.h"
const float flIGNORE = -10000.0f;
const float flFadeOut = 0.50f;
#define ENABLE_USEVALUEFLOW 1
/***************************************************************************\
*
* F2T
*
* F2T() converts from frames to time, using a constant. This allows easily
* conversion from frames in Flash or Director to time used by DirectUser.
*
\***************************************************************************/
inline float
F2T(
IN int cFrames)
{
return cFrames / 30.0f;
}
inline BYTE
GetAlphaByte(float fl)
{
if (fl <= 0.0f) {
return 0;
} else if (fl >= 1.0f) {
return 255;
} else {
return (BYTE) (fl * 255.0f);
}
}
inline float
GetAlphaFloat(BYTE b)
{
return b * 255.0f;
}
/***************************************************************************\
*
* GetVPatternDelay
*
* GetVPatternDelay() computes the delay time for a standard "v-pattern" of
* items that start from the middle and work outward.
*
\***************************************************************************/
inline float
GetVPatternDelay(
IN float flTimeLevel,
IN EFadeDirection dir,
IN int idxCur,
IN int cItems)
{
float flBase = flTimeLevel * (float) (abs(cItems / 2 - idxCur));
switch (dir)
{
case fdIn:
return flBase;
case fdOut:
return flTimeLevel * (abs(cItems / 2)) - flBase;
default:
DUIAssertForce("Unknown direction");
return 0;
}
}
//------------------------------------------------------------------------------
HRESULT
BuildLinearAlpha(
OUT Sequence ** ppseq,
OUT Interpolation ** ppip)
{
HRESULT hr = E_FAIL;
LinearInterpolation * pip = NULL;
#if ENABLE_USEVALUEFLOW
ValueFlow * pflow = NULL;
#else
AlphaFlow * pflow = NULL;
#endif
Sequence * pseq = NULL;
pip = LinearInterpolation::Build();
if (pip == NULL) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto ErrorExit;
}
#if ENABLE_USEVALUEFLOW
ValueFlow::ValueFlowCI fci;
ZeroMemory(&fci, sizeof(fci));
fci.cbSize = sizeof(fci);
pflow = ValueFlow::Build(&fci);
#else
Flow::FlowCI fci;
ZeroMemory(&fci, sizeof(fci));
fci.cbSize = sizeof(fci);
pflow = AlphaFlow::Build(&fci);
#endif
if (pflow == NULL) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto ErrorExit;
}
pseq = Sequence::Build();
if (pseq == NULL) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto ErrorExit;
}
pseq->SetFlow(pflow);
pflow->Release();
*ppseq = pseq;
*ppip = pip;
return S_OK;
ErrorExit:
if (pseq != NULL)
pseq->Release();
if (pflow != NULL)
pflow->Release();
if (pip != NULL)
pip->Release();
*ppseq = NULL;
*ppip = NULL;
return hr;
}
/***************************************************************************\
*
* class SyncVisible
*
* SyncVisible provides a mechansim to synchronize state between DirectUI and
* DirectUser for the fading in / out effects. This allows an Element to be
* marked as "visible" during the fade out and then become "not visible" when
* the fade is done.
*
* This is important for several reasons, including not modifying the mouse
* cursor when an Element becomes invisible.
*
\***************************************************************************/
class SyncVisible
{
public:
static void Wait(Element * pel, EventGadget * pgeOperation, UINT nMsg)
{
SyncVisible * psv = new SyncVisible;
if (psv != NULL)
{
psv->_pel = pel;
if (SUCCEEDED(pgeOperation->AddHandlerD(nMsg, EVENT_DELEGATE(psv, EventProc))))
{
// Successfully attached delegate
return;
}
delete psv;
}
// Unable to create a delegate, so set now
Sync(pel);
}
static void Wait(Element * pel, EventGadget * pgeOperation, const GUID * pguid)
{
MSGID nMsg;
if (FindGadgetMessages(&pguid, &nMsg, 1))
{
SyncVisible * psv = new SyncVisible;
if (psv != NULL)
{
psv->_nMsg = nMsg;
psv->_pel = pel;
psv->_pgeOperation = pgeOperation;
if (SUCCEEDED(pgeOperation->AddHandlerD(nMsg, EVENT_DELEGATE(psv, EventProc))))
{
// Successfully attached delegate
return;
}
delete psv;
}
}
// Unable to create a delegate, so set now
Sync(pel);
}
static void Sync(Element * pel)
{
HGADGET hgad = pel->GetDisplayNode();
bool fVisible = true;
if (GetGadgetStyle(hgad) & GS_BUFFERED)
{
BUFFER_INFO bi;
bi.cbSize = sizeof(bi);
bi.nMask = GBIM_ALPHA;
GetGadgetBufferInfo(hgad, &bi);
if (bi.bAlpha < 5)
fVisible = false;
}
pel->SetVisible(fVisible);
}
protected:
UINT CALLBACK EventProc(GMSG_EVENT * pmsg)
{
DUIAssert(GET_EVENT_DEST(pmsg) == GMF_EVENT, "Must be an event handler");
Animation::CompleteEvent * pmsgC = (Animation::CompleteEvent *) pmsg;
if (pmsgC->fNormal) {
Sync(_pel);
}
_pgeOperation->RemoveHandlerD(_nMsg, EVENT_DELEGATE(this, EventProc));
delete this;
return GPR_NOTHANDLED;
}
UINT _nMsg;
Element * _pel;
EventGadget * _pgeOperation;
};
/***************************************************************************\
*
* FxSetAlpha
*
* FxSetAlpha() provides a convenient mechanism to directly set the DirectUser
* "alpha" state on a Visual Gadget without modifying the DirectUI "alpha"
* property.
*
* NOTE: Eventually, we want to synchronize these, but for now, the DirectUI
* "alpha" property doesn't work with DirectUser's new (improved!) Animations
* infrastructure.
*
\***************************************************************************/
void
FxSetAlpha(
IN Element * pe,
IN float flNewAlpha,
IN float fSync)
{
#if ENABLE_USEVALUEFLOW
pe->SetAlpha(GetAlphaByte(flNewAlpha));
#else
HGADGET hgad = pe->GetDisplayNode();
if (flNewAlpha >= 0.97f) {
// Turn off alpha
SetGadgetStyle(hgad, 0, GS_BUFFERED);
} else {
SetGadgetStyle(hgad, GS_BUFFERED | GS_OPAQUE, GS_BUFFERED | GS_OPAQUE);
BUFFER_INFO bi;
ZeroMemory(&bi, sizeof(bi));
bi.cbSize = sizeof(bi);
bi.nMask = GBIM_ALPHA;
bi.bAlpha = (BYTE) (flNewAlpha * 255.0f);
SetGadgetBufferInfo(hgad, &bi);
}
#endif
if (fSync) {
SyncVisible::Sync(pe);
}
}
/***************************************************************************\
*
* FxPlayLinearAlpha
*
* FxPlayLinearAlpha() "plays" a linear, "simple" alpha-animation on a given
* Element.
*
\***************************************************************************/
HRESULT
FxPlayLinearAlpha(
IN Element * pe,
IN float flOldAlpha,
IN float flNewAlpha,
IN float flDuration,
IN float flDelay)
{
HRESULT hr = E_FAIL;
HGADGET hgad = pe->GetDisplayNode();
DUIAssert(hgad != NULL, "Must have valid Gadget");
Visual * pgvSubject = Visual::Cast(hgad);
pgvSubject->SetStyle(GS_OPAQUE, GS_OPAQUE);
//
// If an old alpha is specified, have it take place immediately. We can't
// use the Animation to do this because it will wait the delay.
//
if (flOldAlpha >= 0.0f) {
FxSetAlpha(pe, flOldAlpha, false);
}
LinearInterpolation * pip = NULL;
#if ENABLE_USEVALUEFLOW
ValueFlow * pflow = NULL;
ValueFlow::ValueKeyFrame kf;
#else
AlphaFlow * pflow = NULL;
AlphaFlow::AlphaKeyFrame kf;
#endif
Animation * pani = NULL;
pip = LinearInterpolation::Build();
if (pip == NULL) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto ErrorExit;
}
#if ENABLE_USEVALUEFLOW
ValueFlow::ValueFlowCI fci;
ZeroMemory(&fci, sizeof(fci));
fci.cbSize = sizeof(fci);
fci.pgvSubject = pgvSubject;
fci.ppi = DirectUI::Element::AlphaProp;
pflow = ValueFlow::Build(&fci);
#else
Flow::FlowCI fci;
ZeroMemory(&fci, sizeof(fci));
fci.cbSize = sizeof(fci);
fci.pgvSubject = pgvSubject;
pflow = AlphaFlow::Build(&fci);
#endif
if (pflow == NULL) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto ErrorExit;
}
#if ENABLE_USEVALUEFLOW
kf.cbSize = sizeof(kf);
kf.ppi = DirectUI::Element::AlphaProp;
kf.rv.SetInt(GetAlphaByte(flNewAlpha));
#else
kf.cbSize = sizeof(kf);
kf.flAlpha = flNewAlpha;
#endif
pflow->SetKeyFrame(Flow::tEnd, &kf);
Animation::AniCI aci;
ZeroMemory(&aci, sizeof(aci));
aci.cbSize = sizeof(aci);
aci.act.flDelay = flDelay;
aci.act.flDuration = flDuration;
aci.act.dwPause = (DWORD) -1;
aci.pgvSubject = pgvSubject;
aci.pipol = pip;
aci.pgflow = pflow;
pani = Animation::Build(&aci);
if (pani == NULL) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto ErrorExit;
}
SyncVisible::Wait(pe, pani, &__uuidof(Animation::evComplete));
pani->Release();
pflow->Release();
pip->Release();
return S_OK;
ErrorExit:
if (pani != NULL)
pani->Release();
if (pflow != NULL)
pflow->Release();
if (pip != NULL)
pip->Release();
return hr;
}
/***************************************************************************\
*
* LogonFrame::FxFadeInAccounts
*
* FxFadeInAccounts() performs the first stage of animation:
* - Fades in the user accounts
* - Fades in the "options" in the bottom panel
*
\***************************************************************************/
HRESULT
LogonFrame::FxStartup()
{
HRESULT hr = E_FAIL, hrT;
hrT = FxFadeAccounts(fdIn);
if (FAILED(hrT))
hr = hrT;
//
// Fade in the "bottom pane" info
//
hrT = FxPlayLinearAlpha(_peOptions, 0.0f, 1.0f, F2T(16), F2T(32));
if (FAILED(hrT))
hr = hrT;
return hr;
}
/***************************************************************************\
*
* LogonFrame::FxFadeAccounts
*
* FxFadeAccounts() fades the user accounts using a "v-delay" pattern
*
\***************************************************************************/
HRESULT
LogonFrame::FxFadeAccounts(
IN EFadeDirection dir,
IN float flCommonDelay)
{
HRESULT hr = E_FAIL;
Element * peSelection = NULL;
float flOldAlpha, flNewAlpha, flTimeLevel;
switch (dir)
{
case fdIn:
// Fading accounts in (startup)
flOldAlpha = 0.0f;
flNewAlpha = 1.0f;
flTimeLevel = F2T(5);
break;
case fdOut:
// Fading accounts out (login)
flOldAlpha = flIGNORE;
flNewAlpha = 0.0f;
flTimeLevel = F2T(5);
peSelection = _peAccountList->GetSelection();
break;
default:
DUIAssertForce("Unknown direction");
return E_FAIL;
}
Value* pvChildren;
ElementList* peList = _peAccountList->GetChildren(&pvChildren);
if (peList)
{
hr = S_OK;
LogonAccount* peAccount;
int cAccounts = peList->GetSize();
for (int i = 0; i < cAccounts; i++)
{
peAccount = (LogonAccount*)peList->GetItem(i);
//
// When fading out, we don't want to fade the selected item
//
if ((dir == fdOut) && (peAccount == peSelection))
{
continue;
}
float flDuration = F2T(15);
float flDelay = GetVPatternDelay(flTimeLevel, dir, i, cAccounts) + flCommonDelay;
HRESULT hrT = FxPlayLinearAlpha(peAccount, flOldAlpha, flNewAlpha, flDuration, flDelay);
if (FAILED(hrT)) {
hr = hrT;
}
}
}
pvChildren->Release();
return hr;
}
/***************************************************************************\
*
* LogonFrame::FxLogUserOn
*
* FxLogUserOn() performs the login stage of animation:
* - Fade out Password field, "Type your password", "go" & "help" button
* - WAIT
* - Fade out scroll-bar
* - WAIT
* - Fade out other accounts (outside to selection), fade out "Click on your user..."
* - WAIT
* - Fade out of selection bitmap
* - Scroll-up of Icon / Name
* - Fade in "Logging in to Microsoft Windows"
* - Fade out "Turn off..." and "To manage or change accounts..."
*
\***************************************************************************/
HRESULT
LogonFrame::FxLogUserOn(LogonAccount * pla)
{
HRESULT hr = S_OK;
pla->FxLogUserOn();
FxFadeAccounts(fdOut);
// Fade out the "bottom pane" info
FxPlayLinearAlpha(_peOptions, flIGNORE, 0.0f, F2T(10), F2T(65));
GMA_ACTION act;
ZeroMemory(&act, sizeof(act));
act.cbSize = sizeof(act);
act.flDelay = F2T(50);
act.pvData = this;
act.pfnProc = OnLoginCenterStage;
CreateAction(&act);
return hr;
}
/***************************************************************************\
*
* LogonFrame::OnLoginCenterStage
*
* OnLoginCenterStage() is called after everything has faded away, and we
* are in the final steps.
*
\***************************************************************************/
void CALLBACK
LogonFrame::OnLoginCenterStage(GMA_ACTIONINFO * pmai)
{
if (!pmai->fFinished) {
return;
}
LogonFrame * plf = (LogonFrame *) pmai->pvData;
// Set keyfocus back to frame so it isn't pushed anywhere when controls are removed.
// This will also cause a remove of the password panel from the current account
plf->SetKeyFocus();
// Clear list of logon accounts except the one logging on
Value* pvChildren;
ElementList* peList = plf->_peAccountList->GetChildren(&pvChildren);
if (peList)
{
LogonAccount* peAccount;
for (UINT i = 0; i < peList->GetSize(); i++)
{
peAccount = (LogonAccount*)peList->GetItem(i);
if (peAccount->GetLogonState() == LS_Denied)
{
peAccount->SetLayoutPos(LP_None);
}
}
}
pvChildren->Release();
}
/***************************************************************************\
*
* LogonAccount::FxLogUserOn
*
* FxLogUserOn() performs the login stage of animation for the selected
* account.
* - Fade out Password field, "Type your password", "go" & "help" button
*
\***************************************************************************/
HRESULT
LogonAccount::FxLogUserOn()
{
HRESULT hr = S_OK;
// Need to manually hide the edit control
HideEdit();
// Fade out the password panel
FxPlayLinearAlpha(_pePwdPanel, 1.0f, 0.0f, F2T(10));
return hr;
}
/***************************************************************************\
*
* LogonAccountList::FxMouseWithin
*
* FxMouseWithin() performs animations when the mouse enters or leaves the
* account list.
*
\***************************************************************************/
HRESULT
LogonAccountList::FxMouseWithin(
IN EFadeDirection dir)
{
HRESULT hr = E_FAIL;
float flOldAlpha, flNewAlpha, flDuration;
switch (dir)
{
case fdIn:
// Entering list, so fade non-mouse-within accounts out
flOldAlpha = 1.00f;
flNewAlpha = flFadeOut;
flDuration = F2T(5);
break;
case fdOut:
// Leaving list, so fade non-mouse-within accounts in
flOldAlpha = flIGNORE;
flNewAlpha = 1.00f;
flDuration = F2T(5);
break;
default:
DUIAssertForce("Unknown direction");
return E_FAIL;
}
Value* pvChildren;
ElementList* peList = GetChildren(&pvChildren);
if (peList)
{
hr = S_OK;
LogonAccount* peAccount;
int cAccounts = peList->GetSize();
for (int i = 0; i < cAccounts; i++)
{
peAccount = (LogonAccount*)peList->GetItem(i);
if (peAccount->GetLogonState() == LS_Pending)
{
//
// Animations to use before login
//
if (peAccount->GetMouseWithin())
{
//
// Mouse is within this child. We need to special case this
// node since the list is notified of the MouseWithin property
// change AFTER the child itself is. If we didn't special case
// this, we would fade the MouseWithin child out with the rest
// of its siblings.
//
FxSetAlpha(peAccount, 1.0f, true);
}
else
{
//
// Mouse was not within this child, so apply the defaults
//
HRESULT hrT = FxPlayLinearAlpha(peAccount, flOldAlpha, flNewAlpha, flDuration);
if (FAILED(hrT)) {
hr = hrT;
}
}
}
}
}
pvChildren->Release();
return hr;
}
/***************************************************************************\
*
* LogonAccount::FxMouseWithin
*
* FxMouseWithin() performs animations when the mouse enters an individual
* account item.
*
\***************************************************************************/
HRESULT
LogonAccount::FxMouseWithin(
IN EFadeDirection dir)
{
HRESULT hr = S_OK;
//
// Only apply fades when we are not actually logging in. This is important
// because we kick off an entire set of animations that could be overridden
// if we don't respect this. When we log in, we change each of the
// accounts from LS_Pending.
//
switch (dir)
{
case fdIn:
// Entering account, so fade non-mouse-within accounts out
if (_fHasPwdPanel)
ShowEdit();
if (GetLogonState() == LS_Pending)
hr = FxPlayLinearAlpha(this, flIGNORE, 1.0f, F2T(3));
break;
case fdOut:
// Leaving account, so fade non-mouse-within accounts in
if (_fHasPwdPanel)
HideEdit();
if (GetLogonState() == LS_Pending)
hr = FxPlayLinearAlpha(this, flIGNORE, flFadeOut, F2T(10));
break;
default:
DUIAssertForce("Unknown direction");
return E_FAIL;
}
return hr;
}
/***************************************************************************\
*****************************************************************************
*
* helper Compute() functions
*
*****************************************************************************
\***************************************************************************/
//------------------------------------------------------------------------------
inline int
Round(float f)
{
return (int) (f + 0.5);
}
//------------------------------------------------------------------------------
inline int
Compute(Interpolation * pipol, float flProgress, int nStart, int nEnd)
{
return Round(pipol->Compute(flProgress, (float) nStart, (float) nEnd));
}
//------------------------------------------------------------------------------
inline bool
Compute(Interpolation * pipol, float flProgress, bool fStart, bool fEnd)
{
return (pipol->Compute(flProgress, 0.0f, 1.0f) < 0.5f) ? fStart : fEnd;
}
//------------------------------------------------------------------------------
POINT
Compute(Interpolation * pipol, float flProgress, const POINT * pptStart, const POINT * pptEnd)
{
POINT pt;
pt.x = Compute(pipol, flProgress, pptStart->x, pptEnd->x);
pt.y = Compute(pipol, flProgress, pptStart->y, pptEnd->y);
return pt;
}
//------------------------------------------------------------------------------
SIZE
Compute(Interpolation * pipol, float flProgress, const SIZE * psizeStart, const SIZE * psizeEnd)
{
SIZE size;
size.cx = Compute(pipol, flProgress, psizeStart->cx, psizeEnd->cx);
size.cy = Compute(pipol, flProgress, psizeStart->cy, psizeEnd->cy);
return size;
}
//------------------------------------------------------------------------------
RECT
Compute(Interpolation * pipol, float flProgress, const RECT * prcStart, const RECT * prcEnd)
{
RECT rc;
rc.left = Compute(pipol, flProgress, prcStart->left, prcEnd->left);
rc.top = Compute(pipol, flProgress, prcStart->top, prcEnd->top);
rc.right = Compute(pipol, flProgress, prcStart->right, prcEnd->right);
rc.bottom = Compute(pipol, flProgress, prcStart->bottom, prcEnd->bottom);
return rc;
}
//------------------------------------------------------------------------------
COLORREF
Compute(Interpolation * pipol, float flProgress, COLORREF crStart, COLORREF crEnd)
{
int nAlpha = Compute(pipol, flProgress, GetAValue(crStart), GetAValue(crEnd));
int nRed = Compute(pipol, flProgress, GetRValue(crStart), GetRValue(crEnd));
int nGreen = Compute(pipol, flProgress, GetGValue(crStart), GetGValue(crEnd));
int nBlue = Compute(pipol, flProgress, GetBValue(crStart), GetBValue(crEnd));
return ARGB(nAlpha, nRed, nGreen, nBlue);
}
//------------------------------------------------------------------------------
DirectUI::Color
Compute(Interpolation * pipol, float flProgress, const DirectUI::Color * pclrStart, const DirectUI::Color * pclrEnd)
{
DirectUI::Color clr;
clr.dType = pclrStart->dType;
clr.cr = Compute(pipol, flProgress, pclrStart->cr, pclrEnd->cr);
switch (clr.dType)
{
case COLORTYPE_TriHGradient:
case COLORTYPE_TriVGradient:
clr.cr3 = Compute(pipol, flProgress, pclrStart->cr, pclrEnd->cr);
// Fall through
case COLORTYPE_HGradient:
case COLORTYPE_VGradient:
clr.cr2 = Compute(pipol, flProgress, pclrStart->cr, pclrEnd->cr);
}
return clr;
}
//------------------------------------------------------------------------------
inline float
Compute(Interpolation * pipol, float flProgress, float flStart, float flEnd)
{
return pipol->Compute(flProgress, flStart, flEnd);
}
/***************************************************************************\
*****************************************************************************
*
* class DuiValueFlow
*
*****************************************************************************
\***************************************************************************/
class DuiValueFlow : public ValueFlowImpl<DuiValueFlow, SFlow>
{
// Construction
public:
static HRESULT InitClass();
HRESULT PostBuild(DUser::Gadget::ConstructInfo * pci);
// Operations
public:
// Public API:
public:
dapi PRID ApiGetPRID() { return s_prid; }
dapi HRESULT ApiGetKeyFrame(Flow::ETime time, DUser::KeyFrame * pkf);
dapi HRESULT ApiSetKeyFrame(Flow::ETime time, const DUser::KeyFrame * pkf);
dapi void ApiOnReset(Visual * pgvSubject);
dapi void ApiOnAction(Visual * pgvSubject, Interpolation * pipol, float flProgress);
// Implementaton
protected:
Element * GetElement(Visual * pgvSubject);
// Data
public:
static PRID s_prid;
protected:
DirectUI::PropertyInfo*
m_ppi;
ValueFlow::RawValue
m_rvStart;
ValueFlow::RawValue
m_rvEnd;
};
/***************************************************************************\
*****************************************************************************
*
* class DuiValueFlow
*
*****************************************************************************
\***************************************************************************/
PRID DuiValueFlow::s_prid = 0;
const GUID guidValueFlow = { 0xad9f0bd4, 0x1610, 0x47f3, { 0xba, 0xc9, 0x2c, 0x82, 0xe, 0x35, 0x2, 0xdf } }; // {AD9F0BD4-1610-47f3-BAC9-2C820E3502DF}
IMPLEMENT_GUTS_ValueFlow(DuiValueFlow, SFlow);
//------------------------------------------------------------------------------
HRESULT
DuiValueFlow::InitClass()
{
s_prid = RegisterGadgetProperty(&guidValueFlow);
return s_prid != 0 ? S_OK : (HRESULT) GetLastError();
}
//------------------------------------------------------------------------------
HRESULT
DuiValueFlow::PostBuild(
IN DUser::Gadget::ConstructInfo * pci)
{
//
// Get the information from the Gadget / Element
//
ValueFlow::ValueFlowCI * pDesc = static_cast<ValueFlow::ValueFlowCI *>(pci);
DirectUI::Element * pel = GetElement(pDesc->pgvSubject);
if ((pDesc != NULL) && (pel != NULL)) {
m_ppi = pDesc->ppi;
if (m_ppi != NULL) {
DirectUI::Value * pvSrc = pel->GetValue(m_ppi, PI_Specified);
DUIAssert(pvSrc != Value::pvUnset, "Value must be defined");
m_rvStart.SetValue(pvSrc);
m_rvEnd = m_rvStart;
pvSrc->Release();
}
}
#if DEBUG_TRACECREATION
TRACE("DuiValueFlow 0x%p on 0x%p initialized\n", pgvSubject, this);
#endif // DEBUG_TRACECREATION
return S_OK;
}
//------------------------------------------------------------------------------
HRESULT
DuiValueFlow::ApiGetKeyFrame(Flow::ETime time, DUser::KeyFrame * pkf)
{
if (pkf->cbSize != sizeof(ValueFlow::ValueKeyFrame)) {
return E_INVALIDARG;
}
ValueFlow::ValueKeyFrame * pkfV = static_cast<ValueFlow::ValueKeyFrame *>(pkf);
switch (time)
{
case Flow::tBegin:
pkfV->ppi = m_ppi;
pkfV->rv = m_rvStart;
return S_OK;
case Flow::tEnd:
pkfV->ppi = m_ppi;
pkfV->rv = m_rvEnd;
return S_OK;
default:
return E_INVALIDARG;
}
}
//------------------------------------------------------------------------------
HRESULT
DuiValueFlow::ApiSetKeyFrame(Flow::ETime time, const DUser::KeyFrame * pkf)
{
if (pkf->cbSize != sizeof(ValueFlow::ValueKeyFrame)) {
return E_INVALIDARG;
}
const ValueFlow::ValueKeyFrame * pkfV = static_cast<const ValueFlow::ValueKeyFrame *>(pkf);
switch (time)
{
case Flow::tBegin:
m_ppi = pkfV->ppi;
m_rvStart = pkfV->rv;
return S_OK;
case Flow::tEnd:
m_ppi = pkfV->ppi;
m_rvEnd = pkfV->rv;
return S_OK;
default:
return E_INVALIDARG;
}
}
//------------------------------------------------------------------------------
void
DuiValueFlow::ApiOnReset(Visual * pgvSubject)
{
DirectUI::Element * pel;
if ((m_ppi != NULL) && ((pel = GetElement(pgvSubject)) != NULL)) {
DirectUI::Value * pvNew = NULL;
if (SUCCEEDED(m_rvStart.GetValue(&pvNew))) {
DUIAssert(pvNew != NULL, "Must have valid value");
pel->SetValue(m_ppi, PI_Local, pvNew);
pvNew->Release();
}
}
}
//------------------------------------------------------------------------------
void
DuiValueFlow::ApiOnAction(Visual * pgvSubject, Interpolation * pipol, float flProgress)
{
DirectUI::Element * pel;
if ((m_ppi != NULL) && ((pel = GetElement(pgvSubject)) != NULL)) {
if (m_rvStart.GetType() != m_rvEnd.GetType()) {
DUITrace("DuiValueFlow: Start and end value types do not match\n");
} else {
ValueFlow::RawValue rvCompute;
BOOL fValid = TRUE;
switch (m_rvStart.GetType())
{
case DUIV_INT:
rvCompute.SetInt(Compute(pipol, flProgress, m_rvStart.GetInt(), m_rvEnd.GetInt()));
break;
case DUIV_BOOL:
rvCompute.SetBool(Compute(pipol, flProgress, m_rvStart.GetBool(), m_rvEnd.GetBool()));
break;
case DUIV_POINT:
rvCompute.SetPoint(Compute(pipol, flProgress, m_rvStart.GetPoint(), m_rvEnd.GetPoint()));
break;
case DUIV_SIZE:
rvCompute.SetSize(Compute(pipol, flProgress, m_rvStart.GetSize(), m_rvEnd.GetSize()));
break;
case DUIV_RECT:
rvCompute.SetRect(Compute(pipol, flProgress, m_rvStart.GetRect(), m_rvEnd.GetRect()));
break;
case DUIV_COLOR:
rvCompute.SetColor(Compute(pipol, flProgress, m_rvStart.GetColor(), m_rvEnd.GetColor()));
break;
default:
ASSERT(0 && "Unknown value type");
fValid = FALSE;
}
if (fValid) {
DirectUI::Value * pvNew = NULL;
if (SUCCEEDED(rvCompute.GetValue(&pvNew))) {
DUIAssert(pvNew != NULL, "Must have valid value");
pel->SetValue(m_ppi, PI_Local, pvNew);
pvNew->Release();
}
}
}
}
}
//------------------------------------------------------------------------------
Element *
DuiValueFlow::GetElement(Visual * pgvSubject)
{
Element * pel = NULL;
if (pgvSubject != NULL) {
HGADGET hgadSubject = pgvSubject->GetHandle();
DUIAssert(hgadSubject != NULL, "Must have valid handle");
pel = DirectUI::ElementFromGadget(hgadSubject);
DUIAssert(pel != NULL, "Must have a valid DirectUI Element");
}
return pel;
}
//------------------------------------------------------------------------------
HRESULT FxInitGuts()
{
if (!DuiValueFlow::InitValueFlow()) {
return E_OUTOFMEMORY;
}
return S_OK;
}
#endif // GADGET_ENABLE_GDIPLUS