// DeskMovr.cpp : Implementation of CDeskMovr
#include "stdafx.h"
#pragma hdrstop

#include "deskmovr.h"

#define DEFAULT_INTERVAL        200     // check every 1/5th of a second.
#define DEFAULT_ENABLED         TRUE
#define DETECT_TIMER_ID         2323
#ifndef SHDOC401_DLL
#define ANIMATE_TIMER_ID        2324
#define ANIMATE_TIMER_INTERVAL  (60*1000)
#endif

#ifdef DEBUG
const static TCHAR sz_DM1[]  = TEXT("no dragable part");
const static TCHAR sz_DM2[]  = TEXT("caption menu button");
const static TCHAR sz_DM3[]  = TEXT("caption close button");
const static TCHAR sz_DM4[]  = TEXT("move the component");
const static TCHAR sz_DM5[]  = TEXT("resize width and height from bottom right corner");
const static TCHAR sz_DM6[]  = TEXT("resize width and height from top left corner");
const static TCHAR sz_DM7[]  = TEXT("resize width and height from top right corner");
const static TCHAR sz_DM8[]  = TEXT("resize width and height from bottom left corner");
const static TCHAR sz_DM9[]  = TEXT("resize from the top edge");
const static TCHAR sz_DM10[]  = TEXT("resize from the bottom edge");
const static TCHAR sz_DM11[]  = TEXT("resize from the left edge");
const static TCHAR sz_DM12[] = TEXT("resize from the right edge");

const LPCTSTR g_szDragModeStr[] = {
        sz_DM1,
        sz_DM2,
        sz_DM3,
        sz_DM4,
        sz_DM5,
        sz_DM6,
        sz_DM7,
        sz_DM8,
        sz_DM9,
        sz_DM10,
        sz_DM11,
        sz_DM12
    };

#endif // DEBUG

// Globals used to track cdeskmovr instances.  Useful for optimizing the
// detection code so we can turn off the timer when the mouse is not over our
// window.  We track the cdeskmovr instances only on the first thread that instantiates
// us to keep the code simple, this should be the active desktop case.
#define CDESKMOVR_TRACK_COUNT 16    // 2 is what we use now for the active desktop, but we need
                                    // extra slots in the array due to the fact that a new instance
                                    // is created before the old one is destroyed during refresh.
                                    // Make the array large to handle nested refreshes!
HHOOK g_hMouseHook;
HHOOK g_hKeyboardHook;
DWORD g_dwHookThreadId;
#ifndef SHDOC401_DLL
BOOL  g_fAnimTimer = FALSE;
#endif
typedef CDeskMovr *PDM;
PDM g_apDM[CDESKMOVR_TRACK_COUNT];
BOOL CombView_EnableAnimations(BOOL fEnable);


DWORD  g_fIgnoreTimers = 0;
#define IGNORE_CONTEXTMENU_UP 0x0001
#define IGNORE_CAPTURE_SET    0x0002

#define GET_SKIP_COUNT  (2 * ((GetDoubleClickTime() / m_lInterval) + 1))

MAKE_CONST_BSTR(s_sstrNameMember,       L"name");
MAKE_CONST_BSTR(s_sstrHidden,           L"hidden");
MAKE_CONST_BSTR(s_sstrVisible,          L"visible");
MAKE_CONST_BSTR(s_sstrResizeableMember, L"resizeable");

// These were declared in shellprv.h, so use DEFINE instead of MAKE
DEFINE_CONST_BSTR(s_sstrIDMember,         L"id");
DEFINE_CONST_BSTR(s_sstrSubSRCMember,     L"subscribed_url");
DEFINE_CONST_BSTR(s_sstrSRCMember,        L"src");

#define CAPTION_ONLY (m_ItemState & (IS_FULLSCREEN | IS_SPLIT))
#define ISNORMAL (m_ItemState & IS_NORMAL)
#define ISFULLSCREEN (m_ItemState & IS_FULLSCREEN)
#define ISSPLIT (m_ItemState & IS_SPLIT)
#define CAPTIONBAR_HOTAREA(cyDefaultCaption, cyCurrentCaption) (((cyCurrentCaption == 0) && CAPTION_ONLY) ? (cyDefaultCaption / 2) : 3 * cyDefaultCaption)

#define MAX_ID_LENGTH 5

void ObtainSavedStateForElem( IHTMLElement *pielem,
                       LPCOMPSTATEINFO pCompState, BOOL fRestoredState);

DWORD g_aDMtoCSPushed[] = {0, CS_MENUPUSHED, CS_CLOSEPUSHED, CS_RESTOREPUSHED, CS_FULLSCREENPUSHED, CS_SPLITPUSHED};
DWORD g_aDMtoCSTracked[] = {0, CS_MENUTRACKED, CS_CLOSETRACKED, CS_RESTORETRACKED, CS_FULLSCREENTRACKED, CS_SPLITTRACKED};
DWORD g_aDMDCfromDragMode[] = {0, DMDC_MENU, DMDC_CLOSE, DMDC_RESTORE, DMDC_FULLSCREEN, DMDC_SPLIT};
#define PUSHED(dm) (g_aDMtoCSPushed[(dm)])
#define TRACKED(dm) (g_aDMtoCSTracked[(dm)])
#define DMDCFROMDM(dm) (g_aDMDCfromDragMode[(dm)])

// Trident will flash if you change the zindex, even if it's to the same index,
// so we prevent the no-op call.
HRESULT SafeZOrderSet(IHTMLStyle * pistyle, LONG lNewZIndex)
{
    HRESULT hr = S_OK;
    VARIANT varZ;

    ASSERT(pistyle);
    pistyle->get_zIndex(&varZ);

    // Does the component need to be moved to the top?
    if ((VT_I4 != varZ.vt) || (varZ.lVal != lNewZIndex))
    {
        // Yes.
        varZ.vt = VT_I4;
        varZ.lVal = lNewZIndex;
        hr = pistyle->put_zIndex(varZ);
    }

    return hr;
}

// Keyboard hook is installed when first instance of deskmovr is created.  Used to implement the keyboard
// interface for accessing the deskmovr control.  Hook is removed when there are no more deskmovr's
// being tracked.
LRESULT CALLBACK DeskMovr_KeyboardHook(int nCode, WPARAM wParam, LPARAM lParam)
{
    LRESULT lRes;
    BOOL fHaveMover = FALSE;

    for (int i = 0; i < CDESKMOVR_TRACK_COUNT; i++) {
        if (g_apDM[i])
        {
            g_apDM[i]->OnKeyboardHook(wParam, lParam);
            fHaveMover = TRUE;
        }
    }

    lRes = CallNextHookEx(g_hKeyboardHook, nCode, wParam, lParam);

    if (!fHaveMover)
    {
        UnhookWindowsHookEx(g_hKeyboardHook);
        g_hKeyboardHook = NULL;
    }

    return lRes;
}

// Helper function used to track cdeskmovr intances so that we can turn off the
// timer if the mouse leaves our window.
void TrackMover(PDM pdm, BOOL fAdd)
{
    if (!g_dwHookThreadId)
        g_dwHookThreadId = GetCurrentThreadId();

    if (!g_hKeyboardHook && fAdd)
        g_hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD, DeskMovr_KeyboardHook, NULL, GetCurrentThreadId());

    if (!fAdd || (g_dwHookThreadId == GetCurrentThreadId())) {
        int i = 0;
        PDM pdmFind = fAdd ? NULL : pdm;
        PDM pdmAssign = fAdd ? pdm : NULL;

        while (i < CDESKMOVR_TRACK_COUNT) {
            if (g_apDM[i] == pdmFind) {
                g_apDM[i] = pdmAssign;
                break;
            }
            i++;
        }

        // If we ever fail to track a mover then we'll never be able to optimize
        // again.  Shouldn't happen in practice for the case we care about.
        if (fAdd && (i >= CDESKMOVR_TRACK_COUNT))
            g_dwHookThreadId = 0xffffffff;

        ASSERT(!fAdd || (i < CDESKMOVR_TRACK_COUNT));
    }
}

#if 0
void AnimateToTray(HWND hwnd, LONG lLeft, LONG lTop, LONG lWidth, LONG lHeight)
{
    HWND hwndTray;
    if (hwndTray = FindWindow(c_szTrayClass, NULL))
    {
        RECT rcComp, rcTray;

        SetRect(&rcComp, lLeft, lTop, lLeft + lWidth, lTop + lHeight);
        MapWindowPoints(hwnd, NULL, (LPPOINT)&rcComp, 2);

        GetWindowRect(hwndTray, &rcTray);
        if ((rcTray.right - rcTray.left) > (rcTray.bottom - rcTray.top))
            rcTray.left = rcTray.right - GetSystemMetrics(SM_CXSMICON);
        else
            rcTray.top = rcTray.bottom - GetSystemMetrics(SM_CYSMICON);

        DrawAnimatedRects(hwnd, IDANI_CAPTION, (CONST RECT *)&rcComp, (CONST RECT *)&rcTray);
    }
}
#endif

void AnimateComponent(HWND hwnd, LONG lLeftS, LONG lTopS, LONG lWidthS, LONG lHeightS,
                      LONG lLeftD, LONG lTopD, LONG lWidthD, LONG lHeightD)
{
    RECT rcSource, rcDest;

    SetRect(&rcSource, lLeftS, lTopS, lLeftS + lWidthS, lTopS + lHeightS);
    SetRect(&rcDest, lLeftD, lTopD, lLeftD + lWidthD, lTopD + lHeightD);

//  98/10/02 vtan: Removed the mapping as not required.

//  MapWindowPoints(hwnd, NULL, (LPPOINT)&rcSource, 2);
//  MapWindowPoints(hwnd, NULL, (LPPOINT)&rcDest, 2);

    DrawAnimatedRects(hwnd, IDANI_CAPTION, (CONST RECT *)&rcSource, (CONST RECT *)&rcDest);
}

// Hook is installed when we detect we can turn our tracking timer off.  The first
// time we get a mouse event in the hook we reactivate all the movers and unhook
// ourself.
LRESULT CALLBACK DeskMovr_MouseHook(int nCode, WPARAM wParam, LPARAM lParam)
{
    LRESULT lRes;

#ifndef SHDOC401_DLL
    // If we are getting mouse messages then a portion of the window must be
    // visible so enable animations.
    CombView_EnableAnimations(TRUE);
#endif

    for (int i = 0; i < CDESKMOVR_TRACK_COUNT; i++) {
        if (g_apDM[i]) 
            g_apDM[i]->SmartActivateMovr(ERROR_SUCCESS);
    }

    lRes = CallNextHookEx(g_hMouseHook, nCode, wParam, lParam);

    UnhookWindowsHookEx(g_hMouseHook);
    g_hMouseHook = NULL;

    return lRes;
}

/////////////////////////////////////////////////////////////////////////////
// CDeskMovr

CDeskMovr::CDeskMovr()
 : m_TimerWnd(_T("STATIC"), this, 1)
{
    TraceMsg(TF_CUSTOM2, "CDeskMovr::CDeskMovr()");

    m_fEnabled = DEFAULT_ENABLED;
    m_lInterval = DEFAULT_INTERVAL;

    m_cxSMBorder = GetSystemMetrics(SM_CXBORDER);
    m_cySMBorder = GetSystemMetrics(SM_CYBORDER);
    m_cxBorder = m_cxSMBorder;
    m_cyBorder = m_cySMBorder;
    m_cyCaption = 0; 

    m_dmCur = dmNull;
    m_dmTrack = dmNull;

    m_hcursor = LoadCursor(NULL, IDC_ARROW);
    m_CaptionState = 0;
    m_hwndParent;

    m_fTimer = FALSE;
    m_fCaptured = FALSE;
    m_uiTimerID = DETECT_TIMER_ID;
    m_pistyle = NULL;

    m_pistyleTarget = NULL;
    m_pielemTarget = NULL;
    m_iSrcTarget = -1;
    
    m_bstrTargetName = NULL;

    m_dx = m_dy = 0;
    
    m_top = m_left = m_width = m_height = 0;

    // Tell ATL that we don't want to be Windowless
    m_bWindowOnly = TRUE;

    // Track this instance
    TrackMover(this, TRUE);
}

CDeskMovr::~CDeskMovr(void)
{
    TraceMsg(TF_CUSTOM2, "CDeskMovr::~CDeskMovr() m_bstrTargetName=%ls.", GEN_DEBUGSTRW(m_bstrTargetName));

 // clean up, detach from events, if necessary. 
    DeactivateMovr(TRUE);

    if ( m_bstrTargetName != NULL )
        SysFreeString( m_bstrTargetName );

    TrackMover(this, FALSE);
}


HRESULT CDeskMovr::SmartActivateMovr(HRESULT hrPropagate)
{
    if ((FALSE == m_nFreezeEvents) && m_fEnabled && !m_pielemTarget)
    {
#ifndef SHDOC401_DLL
        // Release our animation timer if it exists and create our regular one
        if (g_fAnimTimer && (m_uiTimerID == ANIMATE_TIMER_ID))
        {
            m_TimerWnd.KillTimer(m_uiTimerID);
            m_uiTimerID = DETECT_TIMER_ID;
            g_fAnimTimer = FALSE;
            m_fTimer = m_TimerWnd.SetTimer(m_uiTimerID, m_lInterval) != 0;
        }
#endif
        hrPropagate = ActivateMovr();
        if (!EVAL(SUCCEEDED(hrPropagate)))
            DeactivateMovr(FALSE);   // Clean up mess.
    }

    return hrPropagate;
}

HRESULT CDeskMovr::FreezeEvents(BOOL fFreeze)
{
    HRESULT hr = IOleControlImpl<CDeskMovr>::FreezeEvents(fFreeze);
    TraceMsg(TF_CUSTOM1, "CDeskMovr::FreezeEvents(fFreeze=%lx) m_nFreezeEvents=%lx; m_fEnabled=%lx, m_bstrTargetName=%ls", (DWORD)fFreeze, m_nFreezeEvents, m_fEnabled, GEN_DEBUGSTRW(m_bstrTargetName));

    m_nFreezeEvents = fFreeze;

    if (fFreeze)
        DeactivateMovr(FALSE);
    else
        hr = SmartActivateMovr(hr);

    return hr;
}

HRESULT CDeskMovr::Load(LPPROPERTYBAG pPropBag, LPERRORLOG pErrorLog)
{
    HRESULT hr;
    VARIANT var;

    ATLTRACE(_T("IPersistPropertyBagImpl::Load\n"));

    var.vt = VT_BOOL;
    hr = pPropBag->Read(L"Enabled", &var, NULL);
    if (SUCCEEDED(hr) && var.vt==VT_BOOL) {
        m_fEnabled = var.boolVal;
    }

    var.vt = VT_I4;
    hr = pPropBag->Read(L"Interval", &var, NULL);
    if (SUCCEEDED(hr) && var.vt==VT_I4) {
        m_lInterval = var.lVal;
    }

    var.vt = VT_BSTR;
    var.bstrVal = NULL;
    hr = pPropBag->Read(L"TargetName", &var, NULL);
    if (SUCCEEDED(hr) && var.vt==VT_BSTR) {
        m_bstrTargetName = var.bstrVal;
    }

    // This PARAM determines whether the control will be in the
    // windowed or windowless "layer" of the Trident layout.
    var.vt = VT_BOOL;
    hr = pPropBag->Read(L"WindowOnly", &var, NULL);
    if (SUCCEEDED(hr) && var.vt==VT_BOOL) {
        m_bWindowOnly = var.boolVal;
    }

    hr = _GetZOrderSlot(&m_zIndexTop, TRUE);
    ASSERT(SUCCEEDED(hr));
    hr = _GetZOrderSlot(&m_zIndexBottom, FALSE);
    ASSERT(SUCCEEDED(hr));

    return hr;
}

HRESULT CDeskMovr::Save(LPPROPERTYBAG pPropBag, BOOL fClearDirty, BOOL fSaveAllProperties)
{
    return E_NOTIMPL;
}

BOOL CDeskMovr::GetCaptionButtonRect(DragMode dm, LPRECT lprc)
{
    BOOL fSuccess;

    *lprc = m_rectCaption;

    switch (dm) {
        case dmClose:
            lprc->left = m_rectCaption.right - (m_cyCaption + m_cxSMBorder);
            fSuccess = (lprc->left > (m_rectCaption.left + m_cyCaption));
            break;

        case dmMenu:
            lprc->right = lprc->left + (m_cyCaption + m_cxSMBorder);
            fSuccess = (m_rectCaption.right > (m_rectCaption.left + m_cyCaption));
            break;

        case dmRestore:
            if (ISNORMAL)
                return FALSE;
            else if (ISSPLIT)
                goto CalcSplit;
            else if (ISFULLSCREEN)
                goto CalcFullScreen;

            ASSERT(FALSE);

        case dmSplit:
            if (ISSPLIT || !m_fCanResizeX || !m_fCanResizeY)
            {
                return FALSE;
            }
CalcSplit:
            lprc->left = m_rectCaption.right - (m_cyCaption + m_cxSMBorder);
            OffsetRect(lprc, -(lprc->right - lprc->left), 0);
            fSuccess = (lprc->left > (m_rectCaption.left + 2 * m_cyCaption));
            break;

        case dmFullScreen:
            if (ISFULLSCREEN || !m_fCanResizeX || !m_fCanResizeY)
            {
                return FALSE;
            }
CalcFullScreen:
            lprc->left = m_rectCaption.right - (m_cyCaption + m_cxSMBorder);
            OffsetRect(lprc, -((lprc->right - lprc->left) * 2 - 2 * m_cxSMBorder), 0);
            fSuccess = (lprc->left > (m_rectCaption.left + 2 * m_cyCaption));
            break;

        default:
            ASSERT(FALSE);
            fSuccess = FALSE;
            break;
    }

    // Shrink the button within the caption and position it adjacent to the border
    if (fSuccess) {
        OffsetRect(lprc, ((dm == dmClose) ? m_cxSMBorder : -m_cxSMBorder), -m_cySMBorder);
        InflateRect(lprc, -m_cxSMBorder, -m_cySMBorder);
        lprc->bottom -= m_cySMBorder;  // Take an extra border off the bottom
    }

    return fSuccess;
}

void CDeskMovr::DrawCaptionButton(HDC hdc, LPRECT lprc, UINT uType, UINT uState, BOOL fErase)
{
    RECT rcT;
    HRGN hrgnWnd, hrgnRect;
    int iRet;

    if (fErase)
        FillRect(hdc, lprc, (HBRUSH)(COLOR_3DFACE + 1));

    rcT = *lprc;
    InflateRect(&rcT, -2*m_cxSMBorder, -2*m_cySMBorder);

    switch (uType) {
        case DMDC_CLOSE:
            uType = DFC_CAPTION;
            goto Draw;
        case DMDC_MENU:
            uType = DFC_SCROLL;
Draw:
            // We need to clip the border of the outer edge in order to get the drawing effect we
            // want here...
            if (hrgnWnd = CreateRectRgn(0, 0, 0, 0)) {
                if ((iRet = GetClipRgn(hdc, hrgnWnd)) != -1) {
                    if (hrgnRect = CreateRectRgnIndirect(&rcT)) {
                        SelectClipRgn(hdc, hrgnRect);
                        DeleteObject(hrgnRect);
                    }
                }
            }
    
            DrawFrameControl(hdc, lprc, uType, uState);

            if (hrgnWnd != NULL)
            {
                SelectClipRgn(hdc, (iRet == 1) ? hrgnWnd : NULL);
            }
            if (hrgnWnd)
                DeleteObject(hrgnWnd);
            break;

        case DMDC_FULLSCREEN:
        case DMDC_SPLIT:
        case DMDC_RESTORE:
            {
                if (uState & DFCS_PUSHED)
                    OffsetRect(&rcT, 1, 1);

                DrawEdge(hdc, &rcT, BDR_OUTER, BF_FLAT | BF_MONO | BF_RECT);

#ifndef OLD_CODE
                switch (uType) {
                    case DMDC_RESTORE:
                        rcT.right = rcT.left + (rcT.right - rcT.left) * 3 / 4;
                        rcT.bottom = rcT.top + (rcT.bottom - rcT.top) * 3 / 4;
                        rcT.left += (rcT.right - rcT.left) / 2 + 1;
                        rcT.top  += (rcT.bottom - rcT.top) / 2 + 1;
                        FillRect(hdc, &rcT, (HBRUSH)(COLOR_WINDOWFRAME + 1));
                        break;

                    case DMDC_SPLIT:
                        rcT.top += m_cySMBorder;
                        rcT.left += (rcT.right - rcT.left) * 3 / 10;
                        DrawEdge(hdc, &rcT, BDR_OUTER, BF_FLAT | BF_MONO | BF_TOP | BF_LEFT);
                        break;

                    case DMDC_FULLSCREEN:
                        rcT.top += m_cySMBorder;
                        DrawEdge(hdc, &rcT, BDR_OUTER, BF_FLAT | BF_MONO | BF_TOP);
                        break;
                }
#else
                switch (uType) {
                    case DMDC_RESTORE:
                        rcT.right = rcT.left + (rcT.right - rcT.left) * 3 / 4;
                        rcT.bottom = rcT.top + (rcT.bottom - rcT.top) * 3 / 4;
                        rcT.left += (rcT.right - rcT.left) / 2 + 1;
                        rcT.top  += (rcT.bottom - rcT.top) / 2 + 1;
                        break;
                    case DMDC_SPLIT:
                        rcT.left += (rcT.right - rcT.left) * 3 / 10;
                        break;
                    case DMDC_FULLSCREEN:
                        break;
                }

                FillRect(hdc, &rcT, (HBRUSH)(COLOR_WINDOWFRAME + 1));
#endif
            }
            break;
    }

    // DFCS_FLAT means no border to us
    if (!(uState & DFCS_FLAT))
        DrawEdge(hdc, lprc, ((uState & DFCS_PUSHED) ? BDR_SUNKENOUTER : BDR_RAISEDINNER), BF_RECT);
}

void CDeskMovr::DrawCaption(HDC hdc, UINT uDrawFlags, int x, int y)
{
    RECT rect;
    UINT uState;
    DragMode dmT;

    // Draw the caption
    if (uDrawFlags & DMDC_CAPTION) {
        rect = m_rectCaption;
        OffsetRect(&rect, x, y);
        FillRect( hdc, &rect, (HBRUSH)(COLOR_3DFACE + 1) );
    }

    // Draw the caption frame controls
    for (dmT = dmMenu; dmT < dmMove; dmT = (DragMode)((int)dmT + 1))
    {
        if ((uDrawFlags & DMDCFROMDM(dmT)) && GetCaptionButtonRect(dmT, &rect))
        {
            if (dmT == dmMenu)
                uState = DFCS_SCROLLDOWN;
            else if (dmT == dmClose)
                uState = DFCS_CAPTIONCLOSE;
            else
                uState = 0;

            if ((dmT == dmClose) && SHRestricted(REST_NOCLOSEDESKCOMP))
                uState |= DFCS_INACTIVE | DFCS_FLAT;
            else
            {
                if (m_CaptionState & PUSHED(dmT))
                    uState |= DFCS_PUSHED;
                if (!(m_CaptionState & (TRACKED(dmT) | PUSHED(dmT))))
                    uState |= DFCS_FLAT;
            }
            OffsetRect(&rect, x, y);
            DrawCaptionButton(hdc, &rect, DMDCFROMDM(dmT), uState, !(uDrawFlags & DMDC_CAPTION));
        }
    }
}

HRESULT CDeskMovr::OnDraw(ATL_DRAWINFO& di)
{
    RECT& rc = *(RECT*)di.prcBounds;

    RECT r;
    HBRUSH  hbrush = (HBRUSH)(COLOR_3DFACE + 1);
    
    // top edge
    r.left = rc.left;
    r.top = rc.top;
    r.right = rc.right;
    r.bottom = rc.top + m_cyBorder;
    FillRect( di.hdcDraw, &r, hbrush );
    // left edge
    r.top = rc.top + m_cyBorder;
    r.right = rc.left + m_cxBorder;
    r.bottom = rc.bottom - m_cyBorder;
    FillRect( di.hdcDraw, &r, hbrush );
    // right edge
    r.right = rc.right;
    r.left = rc.right - m_cxBorder;
    FillRect( di.hdcDraw, &r, hbrush );
    // bottom edge
    r.left = rc.left;
    r.top = rc.bottom - m_cyBorder;
    r.right = rc.right;
    r.bottom = rc.bottom;
    FillRect( di.hdcDraw, &r, hbrush );

    if ( m_cyCaption != 0 ) {
        DrawCaption(di.hdcDraw, DMDC_ALL, rc.left, rc.top);
    }

    return S_OK;
}

HRESULT CDeskMovr::GetParentWindow(void)
{
    HRESULT hr = S_OK;

    if (!m_hwndParent)
    {
        if (m_spInPlaceSite) 
            hr = m_spInPlaceSite->GetWindow(&m_hwndParent);
        else 
        {
            IOleInPlaceSiteWindowless * poipsw;

            ASSERT(m_spClientSite);
            if (m_spClientSite &&
                SUCCEEDED(hr = m_spClientSite->QueryInterface(IID_IOleInPlaceSiteWindowless, (void **)&poipsw)))
            {
                hr = poipsw->GetWindow(&m_hwndParent);
                poipsw->Release();
            }
        }

        if (!m_hwndParent)
            hr = S_FALSE;   // We failed to get it.
    }

    return hr;
}

void CDeskMovr::DeactivateMovr(BOOL fDestroy)
{
    TraceMsg(TF_CUSTOM2, "CDeskMovr::DeactivateMovr() m_fTimer=%lx, m_bstrTargetName=%ls", m_fTimer, GEN_DEBUGSTRW(m_bstrTargetName));

    if (fDestroy || (m_uiTimerID == DETECT_TIMER_ID)) {
        if (m_fTimer)
        {
            m_TimerWnd.KillTimer(m_uiTimerID);
            m_fTimer = FALSE;
        }
        if (m_TimerWnd.m_hWnd)
            m_TimerWnd.DestroyWindow();
#ifndef SHDOC401_DLL
        if (m_uiTimerID == ANIMATE_TIMER_ID)
            g_fAnimTimer = FALSE;
#endif
    }

    // DismissSelfNow();

    ATOMICRELEASE( m_pistyle );
    ATOMICRELEASE( m_pistyleTarget );
    ATOMICRELEASE( m_pielemTarget );

    _ChangeCapture(FALSE);
}


HRESULT CDeskMovr::ActivateMovr()
{
    HRESULT           hr;

    // flush out old interface pointers
    DeactivateMovr(FALSE);
    TraceMsg(TF_CUSTOM2, "CDeskMovr::ActivateMovr() m_fTimer=%lx, m_bstrTargetName=%ls", m_fTimer, GEN_DEBUGSTRW(m_bstrTargetName));

    if (m_fEnabled)
    {        
        if (SUCCEEDED(hr = GetOurStyle()))
        {
            if ((m_bstrTargetName != NULL) && (m_lInterval > 0))
            {
                if (!m_TimerWnd.m_hWnd)
                {
                    // create a new timer.
                    RECT rc = {0, 0, 0 , 0};

                    // We attempt to get our parent HWND (m_hwndParent) now.
                    // If we fail (and we will sometimes), then we will get it later when
                    // we need it and Trident is then ready.
                    GetParentWindow();

                    m_TimerWnd.Create(NULL, rc, _T("Timer"), WS_POPUP);
                }
                if (!m_fTimer)
                    m_fTimer = m_TimerWnd.SetTimer(m_uiTimerID, m_lInterval) != 0;
            }
            else
            {
#ifdef HIDE_ALL_HANDLES
                hr = S_FALSE;
#else
                hr = E_FAIL;
#endif
            }
        }
    }
    else
    {
        hr = E_FAIL;
    }

    return hr;
}

HRESULT CDeskMovr::GetOurStyle(void)
{
    HRESULT           hr;
    IOleControlSite   *pictlsite = 0;
    IDispatch         *pidisp = 0;

    // Reach up to get our extender, who is the custodian of our element style
    if (m_spClientSite &&
        EVAL(SUCCEEDED(hr = m_spClientSite->QueryInterface(IID_IOleControlSite, (LPVOID*)&pictlsite))) &&
        EVAL(SUCCEEDED(hr = pictlsite->GetExtendedControl(&pidisp))))
    {
        DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0};
        VARIANT var;

        VariantInit( &var );

        // Alas, all we have is IDispatch on our extender, so we'll have to use Invoke to get
        // the style object...
        hr = pidisp->Invoke( DISPID_IHTMLELEMENT_STYLE, IID_NULL,
                             LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET,
                             &dispparamsNoArgs, &var, NULL, NULL );

        if ( SUCCEEDED(hr) ) {
            if ( var.vt == VT_DISPATCH )
                hr = var.pdispVal->QueryInterface( IID_IHTMLStyle, (LPVOID*)&m_pistyle );
            else
                hr = E_FAIL; // Try VariantChangeType?????

            VariantClear( &var );
        }
    }

    ATOMICRELEASE( pictlsite );
    ATOMICRELEASE( pidisp );

    return hr;
}



void CDeskMovr::UpdateCaption(UINT uDrawFlags)
{
    HDC hdc;
    int x = 0, y = 0;

    if (m_bWndLess) {
        if (!m_spInPlaceSite || !SUCCEEDED(m_spInPlaceSite->GetDC(NULL, 0, &hdc)))
            return;
    } else {
        hdc = ::GetDC(m_hWnd);
    }

    _MapPoints(&x, &y);

    DrawCaption(hdc, uDrawFlags, -x, -y);

    if (m_bWndLess) {
        m_spInPlaceSite->ReleaseDC(hdc);
    } else {
        ::ReleaseDC(m_hWnd, hdc);
    }
}

void CDeskMovr::CheckCaptionState(int x, int y)
{
    DragMode dm, dmT;
    UINT uDrawFlags = 0;

    _MapPoints (&x, &y);

    POINT pt = { x, y };

    if (m_fCaptured)
        dm = dmNull;
    else
        dm = DragModeFromPoint( pt );

    if (dm >= dmMenu && dm < dmMove)
    {
        if (!(m_CaptionState & (PUSHED(dm) | TRACKED(dm))))
        {
            m_CaptionState |= TRACKED(dm);
            uDrawFlags |= DMDCFROMDM(dm);
        }
    }

    for (dmT = dmMenu; dmT < dmMove; dmT = (DragMode)((int)dmT + 1))
    {
        if (dm != dmT && (m_CaptionState & (PUSHED(dmT) | TRACKED(dmT))))
        {
            m_CaptionState &= ~(PUSHED(dmT) | TRACKED(dmT));
            uDrawFlags |= DMDCFROMDM(dmT);
        }
    }

    if (uDrawFlags)
        UpdateCaption(uDrawFlags);
}

//=--------------------------------------------------------------------------=
// CDeskMovr::DoMouseDown   [instance method]
//=--------------------------------------------------------------------------=
// Respond to mouse down messages in our control. Initiate move/resize.
//
// Parameters:
//    int                - [in]  mouse message key flags
//    int                - [in]  mouse x location in control coords
//    int                - [in]  mouse y location in control coords
//
// Output:
//    <none>
//
// Notes:
BOOL CDeskMovr::HandleNonMoveSize(DragMode dm)
{
    m_dmCur = dm;
    switch (dm) {
        case dmMenu:
        case dmClose:
        case dmRestore:
        case dmFullScreen:
        case dmSplit:
            if (m_dmCur != dmClose || !SHRestricted(REST_NOCLOSEDESKCOMP)) // Special case for Close, check restriction
            {
                m_CaptionState &= ~(TRACKED(m_dmCur));
                m_CaptionState |= PUSHED(m_dmCur);
                UpdateCaption(DMDCFROMDM(m_dmCur));
                // Perform the operation on the up-click of the mouse...
            }
    
            if (m_dmCur == dmMenu && EVAL(S_OK == GetParentWindow())) // Special case for Menu, invoke on the down click
            {
                _DisplayContextMenu();
            }
            return TRUE;
            break;

        default:
            return FALSE;
            break;
    }
}

LRESULT CDeskMovr::OnMouseDown( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
{
    int x = (short)LOWORD(lParam);
    int y = (short)HIWORD(lParam);

    _MapPoints(&x, &y);

    TraceMsg(TF_CUSTOM2, "CDeskMovr::OnMouseDown() Mouse=<%d,%d>, Inner=<%d,%d,%d,%d>, Caption=<%d,%d,%d,%d>, m_bstrTargetName=%ls", 
        x, y, m_rectInner.left, m_rectInner.top, m_rectInner.right, m_rectInner.bottom,
        m_rectCaption.left, m_rectCaption.top, m_rectCaption.right, m_rectCaption.bottom, GEN_DEBUGSTRW(m_bstrTargetName));

    POINT pt = { x, y };
    m_dmCur = DragModeFromPoint( pt );

    if (HandleNonMoveSize(m_dmCur))
        return 0;

    switch ( m_dmCur ) {
    case dmMove:
        m_dx = -x;
        m_dy = -y;
        break;

    case dmSizeWHBR:
        m_dx = m_rectInner.right - x;
        m_dy = m_rectInner.bottom - y;
        break;
    case dmSizeWHTL:
        m_dx = m_rectInner.left - x;
        m_dy = m_rectInner.top + m_cyCaption - y;
        break;
    case dmSizeWHTR:
        m_dx = m_rectInner.right - x;
        m_dy = m_rectInner.top + m_cyCaption - y;
        break;
    case dmSizeWHBL:
        m_dx = m_rectInner.left - x;
        m_dy = m_rectInner.bottom - y;
        break;
    case dmSizeTop:
        m_dx = 0;
        m_dy = m_rectInner.top + m_cyCaption - y;
        break;
    case dmSizeBottom:
        m_dx = 0;
        m_dy = m_rectInner.bottom - y;
        break;
    case dmSizeLeft:
        m_dx = m_rectInner.left - x;
        m_dy = 0;
        break;
    case dmSizeRight:
        m_dx = m_rectInner.right - x;
        m_dy = 0;
        break;
    default:
        bHandled = FALSE;
        return 1;
    }

#ifdef DEBUG
    TraceMsg(TF_CUSTOM2, "CDeskMovr::OnMouseDown() New DragMode=""%s""", g_szDragModeStr[m_dmCur]);
#endif // DEBUG

    // NOTE: (seanf, 1/31/97) temporary defense against 17902. We really
    // shouldn't ever be in visible and non-targeted at the same time, but
    // the resize trick we pull in CDeskMovr::ActivateMovr() to get us
    // in-place active exposes a 1X1 pixel area, just big enough for StanTak
    // to click on when we don't have a target, which then kills us when
    // we try to move the non-existent target.
    if ( m_pielemTarget != NULL ) {
        _ChangeCapture(TRUE);

        if (m_fCaptured)
        {
            // Move the target to the top and put ourselves just under it
            VARIANT varZ;

            m_pistyleTarget->get_zIndex(&varZ);

            // Does the component need to be moved to the top?
            if (!CAPTION_ONLY && ((VT_I4 != varZ.vt) || (varZ.lVal != m_zIndexTop)))
            {
                // Yes.
                varZ.vt = VT_I4;
                varZ.lVal = ++m_zIndexTop;
                // Move the DeskMover ActiveX Control on top of everything.
                m_pistyle->put_zIndex(varZ);
    
                // Move the Desktop Item on top of the DeskMover
                varZ.lVal = ++m_zIndexTop;
                m_pistyleTarget->put_zIndex(varZ);
            }
        }
#ifdef DEBUG
        if (!m_fCaptured)
            TraceMsg(TF_CUSTOM2, "CDeskMovr::OnMouseDown() Unable to get capture, tracking will fail!");
#endif

    }

    return 0;
}

//=--------------------------------------------------------------------------=
// CDeskMovr::DoMouseUp   [instance method]
//=--------------------------------------------------------------------------=
// Respond to mouse down messages in our control. Terminate move/resize.
//
// Parameters:
//    int                - [in]  mouse message key flags
//    int                - [in]  mouse x location in control coords
//    int                - [in]  mouse y location in control coords
//    UINT               - [in]  from the DeskMovrParts enum
//
// Output:
//    <none>
//
// Notes:

LRESULT CDeskMovr::OnMouseUp( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
{
    if ( m_fCaptured ) {
        PersistTargetPosition( m_pielemTarget, m_left, m_top, m_width, m_height, m_zIndexTop, FALSE, FALSE, m_ItemState );
        _ChangeCapture(FALSE);
    } else {
        int x = (short)LOWORD(lParam);
        int y = (short)HIWORD(lParam);

        _MapPoints(&x, &y);
    
        POINT pt = { x, y };
        DragMode dm = DragModeFromPoint( pt );

        if ((dm >= dmMenu) && (dm < dmMove) && (m_CaptionState & PUSHED(dm)))
        {
            m_CaptionState &= ~(PUSHED(dm));
            m_CaptionState |= TRACKED(dm);
            UpdateCaption(DMDCFROMDM(dm));

            switch ( dm ) {
                case dmClose:
//                    AnimateToTray(m_hwndParent, m_left, m_top, m_width, m_height);
                    IElemCloseDesktopComp(m_pielemTarget);
                    break;

                case dmRestore:
                    _HandleZoom(IDM_DCCM_RESTORE);
                    break;

                case dmFullScreen:
                    _HandleZoom(IDM_DCCM_FULLSCREEN);
                    break;

                case dmSplit:
                    _HandleZoom(IDM_DCCM_SPLIT);
                    break;
            }

            if (dm != dmMenu)
                DismissSelfNow();
        }
    }

    return 0;
}

//=--------------------------------------------------------------------------=
// CDeskMovrControl::DoMouseMove   [instance method]
//=--------------------------------------------------------------------------=
// Respond to mouse move messages in our control and when moving/sizing.
//
// Parameters:
//
// Output:
//    <none>
//
// Notes:

LRESULT CDeskMovr::OnPaint( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
{
    TraceMsg(TF_CUSTOM2, "CDeskMovr::OnPaint() uMsg=%lx, wParam=%lx, lParam=%lx, m_bstrTargetName=%ls", uMsg, wParam, lParam, GEN_DEBUGSTRW(m_bstrTargetName));
    return CComControl<CDeskMovr>::OnPaint( uMsg, wParam, lParam, bHandled );
}

//=--------------------------------------------------------------------------=
// CDeskMovrControl::DoMouseMove   [instance method]
//=--------------------------------------------------------------------------=
// Respond to mouse move messages in our control and when moving/sizing.
//
// Parameters:
//
// Output:
//    <none>
//
// Notes:

LRESULT CDeskMovr::OnMouseMove( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
{
    CheckCaptionState((short)LOWORD(lParam), (short)HIWORD(lParam));

    if (m_fCaptured && EVAL(S_OK == GetParentWindow()))
    {
        // Okay, it's a hit on one of our gadgets.
        // We're only interested in mouse moves and mouse ups if we're in the
        // process of a drag or resize
        HRESULT hr;
        POINT   ptDoc; // location in document window coords
        POINT   ptScreen;
        HWND    hwndParent = m_hwndParent;

        int x = (short)LOWORD(lParam);
        int y = (short)HIWORD(lParam);

        ptScreen.x = x;
        ptScreen.y = y;
        ptDoc = ptScreen;
        if ( !m_bWndLess ) 
            ::MapWindowPoints( m_hWnd, hwndParent, &ptDoc, 1 );

        if ( m_dmCur == dmMove )
            hr = MoveSelfAndTarget( ptDoc.x + m_dx + m_cxBorder, ptDoc.y + m_dy + m_cyBorder + m_cyCaption );
        else if ( m_dmCur > dmMove )
            hr = SizeSelfAndTarget( ptDoc );

        ASSERT(SUCCEEDED(hr));
    }

    // Set m_cSkipTimer so that we delay dismissing the mover...
    m_cSkipTimer = GET_SKIP_COUNT;

    return 0;
}

LRESULT CDeskMovr::OnTimer( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
{
    HRESULT hr;
    IHTMLElement *pielem;
    POINT   ptCursor;
    BOOL    fDidWork = FALSE;

#ifndef SHDOC401_DLL
    /*
     * Check our animation timer first.  If we are able to disable animations then
     * blow away the timer.  Otherwise reset the timer for 60 seconds and keep on
     * looking.
     */
    if (wParam == ANIMATE_TIMER_ID)
    {
        if (CombView_EnableAnimations(FALSE))
        {
            m_TimerWnd.SetTimer(ANIMATE_TIMER_ID, ANIMATE_TIMER_INTERVAL);
        }
        else
        {
            m_TimerWnd.KillTimer(m_uiTimerID);
            m_uiTimerID = DETECT_TIMER_ID;
            g_fAnimTimer = FALSE;
            m_fTimer = FALSE;
        }

        return 0;
    }
#endif

    if (!m_fTimer || g_fIgnoreTimers || !GetCursorPos( &ptCursor ) || !m_pistyle)
        return 0;

    if (ptCursor.x == m_ptMouseCursor.x && ptCursor.y == m_ptMouseCursor.y)
        // Mouse stayed still from last time we did a timer so, do nothing
        return 0;

    pielem = NULL;

    if (S_OK == GetParentWindow())
    {
        HWND hwndParent = m_hwndParent;
        HWND hwndCursor = WindowFromPoint(ptCursor);

        if ((hwndCursor != hwndParent) && !::IsChild(hwndParent, hwndCursor))
        {
            // The mouse has drifted out of our window, so lose our target, if any
            if (m_iSrcTarget >= 0)
            {
                hr = MoveSelfToTarget( NULL, NULL );
                ASSERT(SUCCEEDED(hr));
                if (hr != S_FALSE)
                {
                    fDidWork = TRUE;
                }
            }
            if (GetCurrentThreadId() == g_dwHookThreadId) {
#ifndef SHDOC401_DLL
                // Set ourselves up so we can look to see if our animations can be turned off
                if (!g_fAnimTimer)
                {
                    if (m_fTimer)
                        m_TimerWnd.KillTimer(m_uiTimerID);

                    if (g_fAnimTimer = (m_TimerWnd.SetTimer(ANIMATE_TIMER_ID, ANIMATE_TIMER_INTERVAL / 10) != 0))
                        m_uiTimerID = ANIMATE_TIMER_ID;
                    m_fTimer = g_fAnimTimer;
                }
#endif
                DismissSelfNow();
                DeactivateMovr(FALSE);
                if (!g_hMouseHook)
                    g_hMouseHook = SetWindowsHookEx(WH_MOUSE, DeskMovr_MouseHook, NULL, GetCurrentThreadId());
            }
        }
        else if (!(GetDesktopFlags() & COMPONENTS_LOCKED) && SUCCEEDED(hr = _IsInElement(hwndParent, &ptCursor, &pielem)))
        {
            // See if we need to do anything based on the element under the mouse pointer
            hr = _TrackElement(&ptCursor, pielem, &fDidWork);
            // we're done with this particular interface pointer
            pielem->Release();
        }
        else if (m_iSrcTarget != -1) {
            // Check to see if we should expand border to size border width
            if (TrackCaption ( &ptCursor ))
            {
                TrackTarget(NULL);
            }
        }
    }

    if (!fDidWork)
        m_ptMouseCursor = ptCursor;
    
    return 0;
}

LRESULT CDeskMovr::OnEraseBkgnd(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
{
    if (!m_bWndLess) {
        RECT rc;
        ::GetClientRect(m_hWnd, &rc);
        FillRect((HDC)wParam, &rc, (HBRUSH)(COLOR_3DFACE + 1));
    }
    bHandled = TRUE;

    return 0;
}

//
// DismissSelfNow - Little helper function to dismiss the mover immediately
//
// Normally dismissal of the mover is desired to be done on a delayed basis.  However,
// there are situations such as when the user clicks on UI or capture is lost etc. where
// it is desirable to dismiss the mover immediately.
//
void CDeskMovr::DismissSelfNow(void)
{
    HRESULT hr;
    m_cSkipTimer = 0;
    hr = MoveSelfToTarget(NULL, NULL);
    ASSERT(SUCCEEDED(hr) && (hr != S_FALSE));
}

LRESULT CDeskMovr::OnCaptureChanged( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
{
    if ( m_fCaptured ) {
        _ChangeCapture(FALSE);
        PersistTargetPosition( m_pielemTarget, m_left, m_top, m_width, m_height, m_zIndexTop, FALSE, FALSE, m_ItemState );
        DismissSelfNow();
    }

    return 0;
}

HRESULT CDeskMovr::InPlaceDeactivate(void)
{
    DeactivateMovr(FALSE);
    TraceMsg(TF_CUSTOM1, "CDeskMovr::InPlaceDeactivate()");
    return CComControl<CDeskMovr>::IOleInPlaceObject_InPlaceDeactivate();
}

LRESULT CDeskMovr::OnSetCursor( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    if (EVAL(S_OK == GetParentWindow()))
    {
        POINT   ptCursor;
        DragMode dm;

        GetCursorPos( &ptCursor );

        ::ScreenToClient( m_hwndParent, &ptCursor );

        // Get ptCursor into deskmovr local coords
        ptCursor.x -= m_left - (CAPTION_ONLY ? 0 : m_cxBorder);
        ptCursor.y -= m_top - (CAPTION_ONLY ? 0 : (m_cyBorder + m_cyCaption));

        dm = DragModeFromPoint(ptCursor);
        m_hcursor = CursorFromDragMode(dm);

        TraceMsg(TF_CUSTOM2, "CDeskMovr::OnSetCursor() Mouse=<%d,%d>, Inner=<%d,%d,%d,%d>, Caption=<%d,%d,%d,%d>, m_bstrTargetName=%ls", 
            ptCursor.x, ptCursor.y, m_rectInner.left, m_rectInner.top, m_rectInner.right, m_rectInner.bottom,
            m_rectCaption.left, m_rectCaption.top, m_rectCaption.right, m_rectCaption.bottom, GEN_DEBUGSTRW(m_bstrTargetName));

    #ifdef DEBUG
        TraceMsg(TF_CUSTOM2, "CDeskMovr::OnSetCursor() New DragMode=""%s""", g_szDragModeStr[dm]);
    #endif // DEBUG

        if (EVAL(m_hcursor != NULL))
            SetCursor( m_hcursor );
        else
            bHandled = FALSE;
    }

    return !bHandled;
}


void CDeskMovr::TrackTarget(POINT * pptDoc)
{
    HRESULT hr = S_OK;

    if ( m_fEnabled && m_pielemTarget != NULL ) {
        LONG left, top;
        POINT pt;
        VARIANT varZ;
        COMPSTATEINFO CompState;

        varZ.vt = VT_I4;

        CLEANUP_ON_FAILURE(hr = CSSOM_TopLeft(m_pielemTarget, &pt));
        m_top = pt.y;
        m_left = pt.x;

        CLEANUP_ON_FAILURE(hr = m_pielemTarget->get_offsetHeight( &m_height ));
        CLEANUP_ON_FAILURE(hr = m_pielemTarget->get_offsetWidth( &m_width ));
  
        // Hack so we don't get weird painting effect of the window hopping to the new
        // target with the old target's size.
        if (!m_bWndLess && m_cyCaption == 0)
            ::SetWindowPos(m_hWnd, NULL, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOACTIVATE);

        // Get our rectangle synced with the target (so TrackCaption works properly)
        SyncRectsToTarget();
        // If we discover we want to display the size-border or caption
        // right now then we need to recalculate our rects.
        if (pptDoc && TrackCaption(pptDoc))
            SyncRectsToTarget();

        CLEANUP_ON_FAILURE(hr = m_pistyleTarget->get_zIndex( &varZ ));
        if (!CAPTION_ONLY || (m_cxBorder == m_cxSMBorder))
            --varZ.lVal;
        else
            ++varZ.lVal;
        CLEANUP_ON_FAILURE(hr = SafeZOrderSet(m_pistyle, varZ.lVal));

        // NTRAID94268-2000/03/14 (stephstm): If this is hosted in a window that
        //         has scrollbars, we don't correctly add the screen to document
        //         offset when changing the location of the component.
        //         This causes us to drag incorrectly.

//  98/10/02 #176729 vtan: Now uses the component left and top to
//  position the caption. Offset the caption if the component is
//  not zoomed. If zoomed then just draw over the component.

        left = m_left;
        top = m_top;
        if (!CAPTION_ONLY)
        {
            left -= m_cxBorder;
            top  -= m_cyBorder;
            top  -= m_cyCaption;
        }
        hr = m_pistyle->put_pixelLeft(left);
        hr = m_pistyle->put_pixelWidth( m_rectOuter.right );
        hr = m_pistyle->put_pixelTop(top);
        hr = m_pistyle->put_pixelHeight( m_rectOuter.bottom );

        hr = m_pistyle->put_visibility((BSTR)s_sstrVisible.wsz);

        // We need to persist the original state of the item out now if the item's current width/height is -1
        // This occurs when we are fitting an image to it's default size, we need to make sure the
        // original size real values so it works properly.
        ObtainSavedStateForElem(m_pielemTarget, &CompState, FALSE);
        if (m_bWndLess && CompState.dwWidth == COMPONENT_DEFAULT_WIDTH && CompState.dwHeight == COMPONENT_DEFAULT_HEIGHT)
            PersistTargetPosition(m_pielemTarget, m_left, m_top, m_width, m_height, varZ.lVal, FALSE, TRUE, CompState.dwItemState);
    }

CleanUp:
    ASSERT(SUCCEEDED(hr));
}

BOOL CDeskMovr::TrackCaption( POINT *pptDoc )
{
    int         cyCaption, cyCaptionNew;
    POINT       ptMovr;
    DragMode    dmNew;
    BOOL        fRetVal = FALSE;

    //TraceMsg(TF_CUSTOM2, "CDeskMovr::TrackCaption() Mouse=<%d,%d>", ptMovr.x, ptMovr.y);

    if (pptDoc)
    {
        ptMovr = *pptDoc;
        // need a hit test of some sort within the deskmovr to control border swelling
        ptMovr.x -= m_left - m_cxBorder;
        ptMovr.y -= m_top - (m_cyBorder + m_cyCaption);

        dmNew = DragModeFromPoint( ptMovr );

        cyCaption = GET_CYCAPTION;

        if (dmNew == dmNull) {
            BOOL fInner;
            int iInflate;
            RECT rc;
            // Treat something near the size border as a size border hit
            // so we expand to the size border as the user nears the edge.
            fInner = PtInRect(&m_rectInner, ptMovr);
    
            if (fInner) {
                rc = m_rectInner;
                iInflate = -cyCaption;
            } else {
                rc = m_rectOuter;
                iInflate = cyCaption;
            }
    
            InflateRect(&rc, iInflate, iInflate);
            if (fInner != PtInRect(&rc, ptMovr))
                dmNew = dmSizeRight;
        }

        if ( (pptDoc->y >= m_top - (m_cyBorder + 2 * m_cyCaption) &&
            pptDoc->y <= (m_top + CAPTIONBAR_HOTAREA(cyCaption, m_cyCaption)) ) )
            cyCaptionNew = cyCaption;
        else
            cyCaptionNew = 0;
    }
    else
    {
        cyCaptionNew = GET_CYCAPTION;
        dmNew = dmSizeRight;
    }

    if ( cyCaptionNew != m_cyCaption ||
        (m_dmTrack != dmNew && !((m_dmTrack > dmMove) && (dmNew > dmMove))) ) {
        m_cyCaption = cyCaptionNew;
        if (m_cyCaption == 0)
            m_CaptionState = 0;
        m_dmTrack = dmNew;
        fRetVal = TRUE;
    } else
        m_cyCaption = cyCaptionNew;

    return fRetVal;
}

int CDeskMovr::CountActiveCaptions()
{
    int iCount = 0;

    if (g_dwHookThreadId == GetCurrentThreadId())
    {
        for (int i = 0; i < CDESKMOVR_TRACK_COUNT; i++) {
            if (g_apDM[i] && g_apDM[i]->m_pistyleTarget)
                iCount++;
        }
    }
    return iCount;
}

HRESULT CDeskMovr::_TrackElement(POINT * ppt, IHTMLElement * pielem, BOOL * fDidWork)
{
    HRESULT hr;
    IHTMLElement *pTargElem = NULL;
    LONG iSrcTarget = -1;

    ASSERT(pielem);

    if ( FFindTargetElement( pielem, &pTargElem ) )
    {
        hr = pTargElem->get_sourceIndex( &iSrcTarget );
        ASSERT(SUCCEEDED(hr));
    }

    // If the m_iSrcTarget isn't the same as the SrcTarget under our cursor,
    // then we should move on top of it.
    if ( m_iSrcTarget != iSrcTarget )
    {
        *fDidWork = TRUE;
    
        if ((CountActiveCaptions() > 1) && (-1 == iSrcTarget))
            m_cSkipTimer = 0;

        // Yes, we need to move on top of it.
        hr = MoveSelfToTarget( pTargElem, ppt );
        ASSERT(SUCCEEDED(hr));
        if (hr != S_FALSE)
            m_iSrcTarget = iSrcTarget;
    } 
    else
    {
        // No, so that means we already have focus...
        if (ppt && TrackCaption(ppt))
        {
            TrackTarget(NULL);
        }
    }

    if ( pTargElem != NULL ) { 
        pTargElem->Release(); // MoveSelfToTarget will have secured our reference
    }

    hr = (m_iSrcTarget == -1) ? S_FALSE : S_OK;

    return hr;
}

//=--------------------------------------------------------------------------=
// CDeskMovr::InitAttributes  [instance method]
//=--------------------------------------------------------------------------=
// Finds out if the element is resizeable in X and Y direction and sets the
// BITBOOLs accordingly.
//
// Also determines what state the element is in and sets m_ItemState.
//
// Parameters:
//    IHTMLElement*     [in] - interface on event source element
//
// Output:
//    HRESULT      - various. S_OK if  operation succeeded.
//
HRESULT CDeskMovr::InitAttributes(IHTMLElement *pielem)
{
    HRESULT hr;
    TCHAR   szMember[MAX_ID_LENGTH];

    ASSERT(pielem);

    m_fCanResizeX = m_fCanResizeY = FALSE;  //Assume "Can't resize!

    // The resizeable member is not required to be specified, only override defaults if present.
    if (SUCCEEDED(GetHTMLElementStrMember(pielem, szMember, SIZECHARS(szMember), (BSTR)(s_sstrResizeableMember.wsz))))
    {
        if(StrChr(szMember, TEXT('X')))
                m_fCanResizeX = TRUE;

        if(StrChr(szMember, TEXT('Y')))
                m_fCanResizeY = TRUE;
    }

    // The ItemState is required, return failure if we fail to find the ID
    if (SUCCEEDED(hr = GetHTMLElementStrMember(pielem, szMember, SIZECHARS(szMember), (BSTR)(s_sstrIDMember.wsz))))
        m_ItemState = GetCurrentState(szMember);

    return hr;
}

//=--------------------------------------------------------------------------=
// CDeskMovr::MoveSelfToTarget   [instance method]
//=--------------------------------------------------------------------------=
// Handles Trident document events as mouse moves over the desktop.
//
// Parameters:
//    IHTMLElement*     [in] - interface on event source element
//    POINT*            [in] - location of mouse (to determine if caption should be displayed)
//
// Output:
//    HRESULT      - various. S_OK if  operation succeeded.
//


HRESULT CDeskMovr::MoveSelfToTarget(IHTMLElement *pielem, POINT * pptDoc)
{
    HRESULT hr = S_OK;

    TraceMsg(TF_CUSTOM2, "CDeskMovr::MoveSelfToTarget(pielem=%lx) %s, m_bstrTargetName=%ls", pielem, (pielem ? "We are GETTING focus." : "We are LOOSING focus."), GEN_DEBUGSTRW(m_bstrTargetName));

    if (!pielem)
    {
        // The m_cSkipTimer variable is used to allow the skipping of timer ticks when determining 
        // if the mover should be dismissed.  By doing this it gives the user more time and thus
        // a better chance to manipulate the target if they are prone to drifting the mouse
        // outside the target by accident.

        // Check the m_cSkipTimer before dismissing the mover.
        if (!m_cSkipTimer)
        {
            _ChangeCapture(FALSE);
            if (m_pistyle)
                hr = m_pistyle->put_visibility((BSTR)s_sstrHidden.wsz);
            ATOMICRELEASE( m_pistyleTarget );
            ATOMICRELEASE( m_pielemTarget );
            m_iSrcTarget = -1;
        }
        else
        {
            m_cSkipTimer--;
            hr = S_FALSE;
        }

        // These are actions we want to happen right away.
        m_hcursor = CursorFromDragMode(dmNull);
        if (m_hcursor != NULL)
            SetCursor(m_hcursor);
    }

    // These are actions we want to happen after the Desktop Item
    // looses focus.
    if (hr != S_FALSE)
    {
        m_cyCaption = 0;
        m_cxBorder = m_cxSMBorder;
        m_cyBorder = m_cySMBorder;
        m_CaptionState = 0;
        m_dmTrack = dmNull;
    }

    if (pielem)
    {
        ASSERT(m_pielemTarget != pielem);

        // exchange our new target ( if any ) for the old target, if any...
        ATOMICRELEASE( m_pistyleTarget );
        ATOMICRELEASE( m_pielemTarget );

        hr = pielem->get_style(&m_pistyleTarget);
        if (SUCCEEDED(hr))
        {
            // We are gaining focus.
            m_pielemTarget = pielem;
            m_pielemTarget->AddRef();

            EVAL(SUCCEEDED(InitAttributes(m_pielemTarget)));

            if (!pptDoc)
                TrackCaption(NULL);
            TrackTarget(pptDoc);
            // Set m_cSkipTimer so that we delay dismissing the mover...
            m_cSkipTimer = GET_SKIP_COUNT;
            if (!m_bWndLess && !m_hWnd)
            {
                // This is all a hack until trident fixes the UIDeactivate stuff, bug 243801
                IOleInPlaceObject_InPlaceDeactivate();
                InPlaceActivate(OLEIVERB_UIACTIVATE);
                SetControlFocus(TRUE);
            }
        }
    }
   
    return hr;
}

//=--------------------------------------------------------------------------=
// CDeskMovrControl::MoveSelfAndTarget   [instance method]
//=--------------------------------------------------------------------------=
// Moves the control and it's target to a new location.
//
// Parameters:
//    LONG    [in] - x location, in document coord's to move to
//    LONG    [in] - y location, in document coord's to move to
//
// Output:
//    HRESULT      - various. S_OK if  operation succeeded.
//
// Notes:
//      We read back the target's location so that we stay consistent with
//      any constraint's Trident might impose on our movement.

HRESULT CDeskMovr::MoveSelfAndTarget( LONG x, LONG y )
{
    HRESULT hr;

    m_top = y;
    CLEANUP_ON_FAILURE((hr = m_pistyle->put_pixelTop( y  - m_cyBorder - m_cyCaption )));
    CLEANUP_ON_FAILURE((hr = m_pistyleTarget->put_pixelTop( y  )));
    // read it back to catch Trident constraint.
    //CLEANUP_ON_FAILURE((hr = m_pielemTarget->get_docTop( &m_top )));
    //CLEANUP_ON_FAILURE((hr = m_pistyle->put_pixelTop( m_top )));

    m_left = x;
    CLEANUP_ON_FAILURE((hr = m_pistyle->put_pixelLeft( x - m_cxBorder )));
    CLEANUP_ON_FAILURE((hr = m_pistyleTarget->put_pixelLeft( x  )));
    // read it back to catch Trident constraint.
    //CLEANUP_ON_FAILURE((hr = m_pielemTarget->get_docLeft( &m_left )));
    //CLEANUP_ON_FAILURE((hr = m_pistyle->put_pixelLeft( m_left )));

    // if ( !m_bWndLess )
    if (EVAL(S_OK == GetParentWindow()))
        ::UpdateWindow(m_hwndParent);

CleanUp:
    return hr;
}

BOOL CDeskMovr::FFindTargetElement( IHTMLElement *pielem, IHTMLElement **ppielem )
{
    *ppielem = NULL;

    if ( pielem != NULL )
    {
        IDeskMovr   *pidm = NULL;       

        // If it is over the mover return the current target, otherwise
        // find out which component if any we are over.
        if ( m_pielemTarget != NULL && 
               SUCCEEDED(pielem->QueryInterface(IID_IDeskMovr, (LPVOID*)&pidm)))
        {
            m_pielemTarget->AddRef();
            *ppielem = m_pielemTarget;
            ATOMICRELEASE(pidm);
        } else {
            HRESULT hr;
            IHTMLElement *pielem2 = pielem;

            pielem2->AddRef();

            do
            {
                VARIANT     var;
    
                VariantInit( &var );
                
                if ( SUCCEEDED(hr = pielem2->getAttribute( (BSTR)s_sstrNameMember.wsz, TRUE, &var)) ) {
                    if ( var.vt == VT_BSTR && var.bstrVal != NULL ) {
                        if ( StrCmpW( var.bstrVal, m_bstrTargetName ) == 0 )
                            hr = S_OK;
                        else
                            hr = S_FALSE;               
                    } else
                        hr = S_FALSE; // Try VariantChangeType?????
                } else
                    hr = S_FALSE; // not here, maybe in parent.
            
                VariantClear( &var );
    
                if ( hr == S_OK ) { // we found it
                    hr = pielem2->QueryInterface( IID_IHTMLElement, (LPVOID*)ppielem );
                } else if ( hr == S_FALSE ) { // not this one, climb up
                    IHTMLElement *pielemParent = NULL;
                
                    pielem2->get_parentElement( &pielemParent );
                    pielem2->Release();     // we're through at this level
                    pielem2 = pielemParent; // may be null, which just means we've reached the top.
                }
    
            } while ( SUCCEEDED(hr) && *ppielem == NULL && pielem2 != NULL );
        
            ATOMICRELEASE(pielem2);
        }
    }

    return *ppielem != NULL;
}

//=--------------------------------------------------------------------------=
// CDeskMovr::DragModeFromPoint   [instance method]
//=--------------------------------------------------------------------------=
// Moves the control and it's target to a new location.
//
// Parameters:
//    POINT        -  point to test, in local coords
//
// Output:
//    DragMode      - drag mode associated with the point
//
// Notes:
//      This is only a hit testing method. It does not alter state.

CDeskMovr::DragMode CDeskMovr::DragModeFromPoint( POINT pt )
{
    enum DragMode dm = dmNull;
    RECT rc;

    if ( PtInRect( &m_rectInner, pt ) ) 
    { // either no-hit, or on caption
        if ( PtInRect( &m_rectCaption, pt ) ) {
            DragMode dmT;

            for (dmT = dmMenu; dmT < dmMove; dmT = (DragMode)((int)dmT + 1)) {
                if (GetCaptionButtonRect(dmT, &rc) && PtInRect(&rc, pt)) {
                    dm = dmT;
                    break;
                }
            }
            if ((dmT == dmMove) && !CAPTION_ONLY)
                dm = dmMove;
        }
    } else {
        if ( PtInRect( &m_rectOuter, pt ) ) {
            if (!CAPTION_ONLY)
            {
                // a resize border hit
                if ( pt.y <= m_sizeCorner.cy ) {
                    // upper edge or corners
                    if ( pt.x <= m_sizeCorner.cx )
                        dm = dmSizeWHTL;
                    else if ( pt.x >= m_rectOuter.right - m_sizeCorner.cx )
                        dm = dmSizeWHTR;
                    else
                        dm = dmSizeTop;
                } else if ( pt.y >= m_rectOuter.bottom - m_sizeCorner.cy ) {
                    // bottom edge or corners
                    if ( pt.x <= m_sizeCorner.cx )
                        dm = dmSizeWHBL;
                    else if ( pt.x >= m_rectOuter.right - m_sizeCorner.cx )
                    dm = dmSizeWHBR;
                    else
                        dm = dmSizeBottom;
                } else {
                    // side edge hit
                    if ( pt.x > m_rectInner.left )
                        dm = dmSizeRight;
                    else 
                        dm = dmSizeLeft;
                }
            } else {
                if (m_cyCaption == 0)
                {
                    if(IS_BIDI_LOCALIZED_SYSTEM())
                    {
                        dm = dmSizeRight;
                    }
                    else
                    {
                        dm = dmSizeLeft;                        
                    }
                }    
                else
                    dm = dmNull;
            }
        }
        //Check if this element can be sized in both the directions.
        if(!m_fCanResizeX)
        {
            if((dm != dmSizeTop) && (dm != dmSizeBottom))
                dm = dmNull;
        }

        if(!m_fCanResizeY)
        {
            if((dm != dmSizeLeft) && (dm != dmSizeRight))
                dm = dmNull;
        }
    }

    return dm;
}

// Align our member RECTs with the dimensions of the target element.
void CDeskMovr::SyncRectsToTarget(void)
{
    // do the swelling thang
    if ( (m_dmTrack > dmMove) || m_cyCaption ) {
        m_cxBorder = GET_CXSIZE;
        m_cyBorder = GET_CYSIZE;
    } else {
        m_cxBorder = m_cxSMBorder;
        m_cyBorder = m_cySMBorder;
    }

    m_rectOuter.top = m_rectOuter.left = 0;

    if (CAPTION_ONLY)
    {
        if (m_cyCaption != 0)
        {
            // Displaying just caption
            m_rectOuter.bottom = m_cyCaption + m_cyBorder;
            m_rectOuter.right = m_width;
        } else {
            // Displaying just left size border
            m_rectOuter.bottom = m_height;
            if(IS_BIDI_LOCALIZED_SYSTEM())
            {
                m_rectOuter.right = m_width;
                m_rectOuter.left = m_rectOuter.right - m_cxBorder;
            }
            else
            {
                m_rectOuter.right = m_cxBorder;
            }    
        }
    } else {
        // Displaying caption and border
        m_rectOuter.bottom = m_height + 2 * m_cyBorder + m_cyCaption;
        m_rectOuter.right = m_width + 2 * m_cxBorder;
    }

    if (CAPTION_ONLY && m_cyCaption == 0)
    {
        // Displaying just left size border
        SetRectEmpty(&m_rectInner);
        SetRectEmpty(&m_rectCaption);
    } else {
        // Displaying caption and possibly border
        m_rectInner = m_rectOuter;
        InflateRect( &m_rectInner, -m_cxBorder, -m_cyBorder );

        m_rectCaption = m_rectInner;
        m_rectCaption.bottom = m_cyBorder + m_cyCaption;

    }

    if ( m_rectOuter.bottom > 2 * m_cyCaption )
        m_sizeCorner.cy = GET_CYCAPTION;
    else
        m_sizeCorner.cy = m_rectOuter.bottom / 2;

   if ( m_rectOuter.right > 2 * m_cyCaption )
        m_sizeCorner.cx = GET_CYCAPTION;
    else
        m_sizeCorner.cx = m_rectOuter.right / 2;


}

HCURSOR CDeskMovr::CursorFromDragMode( DragMode dm )
{   
    ASSERT( dm >= 0 && dm < cDragModes );
    switch (dm) {
        case dmNull:
        case dmMenu:
        case dmClose:
        case dmMove:
        case dmRestore:
        case dmFullScreen:
        case dmSplit:
        default:
            return LoadCursor(NULL, IDC_ARROW);
        case dmSizeWHBR:
        case dmSizeWHTL:
            return LoadCursor(NULL, IDC_SIZENWSE);
        case dmSizeWHTR:
        case dmSizeWHBL:
            return LoadCursor(NULL, IDC_SIZENESW);
        case dmSizeTop:
        case dmSizeBottom:
            return LoadCursor( NULL, IDC_SIZENS );
        case dmSizeLeft:
        case dmSizeRight:
            return LoadCursor( NULL, IDC_SIZEWE );
    }
}

//=--------------------------------------------------------------------------=
// CDeskMovr::SizeSelfAndTarget   [instance method]
//=--------------------------------------------------------------------------=
// Resizes our control and its target element.
//
// Parameters:
//    LONG    [in] - new width
//    LONG    [in] - new height
//
// Output:
//    HRESULT      - various. S_OK if  operation succeeded.
//
// Notes:
//      We read back the target's dimensions so that we stay consistent with
//      any constraint's Trident might impose on our sizing.

HRESULT CDeskMovr::SizeSelfAndTarget( POINT ptDoc )
{
    HRESULT hr;
    int topOld = m_top;
    int leftOld = m_left;
    int heightOld = m_height;
    int widthOld = m_width;
    int cyCaption = GET_CYCAPTION;

    switch ( m_dmCur ) {
    case dmSizeWHBR:
        m_width = (ptDoc.x + m_dx) - m_left;
        m_height = (ptDoc.y + m_dy) - m_top;
        break;
    case dmSizeWHTL:
        m_top = ptDoc.y + m_dy;
        m_height += topOld - m_top;
        m_left = ptDoc.x + m_dx;
        m_width += leftOld - m_left;
        break;
    case dmSizeWHTR:
        m_top = ptDoc.y + m_dy;
        m_height += topOld - m_top;
        m_width = (ptDoc.x + m_dx) - m_left;
        break;
    case dmSizeWHBL:
        m_height = (ptDoc.y + m_dy) - m_top;
        m_left = ptDoc.x + m_dx;
        m_width += leftOld - m_left;
        break;
    case dmSizeTop:
        m_top = ptDoc.y + m_dy;
        m_height += topOld - m_top;
        break;
    case dmSizeBottom:
        m_height = (ptDoc.y + m_dy) - m_top;
        break;
    case dmSizeLeft:
        m_left = ptDoc.x + m_dx;
        m_width += leftOld - m_left;
        break;
    case dmSizeRight:
        m_width = (ptDoc.x + m_dx) - m_left;
        break;
    default:
        ASSERT(FALSE);
        return E_FAIL;
    }

    // limit shrinkage to keep the handle accessible
    if ( m_height < cyCaption ) {
        m_height = cyCaption;
        if ( m_top != topOld )
            m_top = topOld + heightOld - m_height;
    }
 
    // limit shrinkage to keep the handle accessible
    if ( m_width < (4 * cyCaption) ) {
        m_width = 4 * cyCaption;
        if ( m_left != leftOld )
            m_left = leftOld + widthOld - m_width;
    }

    SyncRectsToTarget();

    if ( m_top != topOld ) {
        CLEANUP_ON_FAILURE((hr = m_pistyleTarget->put_pixelTop( m_top )));
        CLEANUP_ON_FAILURE((hr = m_pistyle->put_pixelTop( m_top - (m_cyBorder + m_cyCaption) )));
    }

    if ( m_left != leftOld ) {
        CLEANUP_ON_FAILURE((hr = m_pistyleTarget->put_pixelLeft( m_left )));
        CLEANUP_ON_FAILURE((hr = m_pistyle->put_pixelLeft( m_left - (CAPTION_ONLY ? 0 : m_cxBorder) )));
    }
 
    CLEANUP_ON_FAILURE((hr = m_pistyleTarget->put_pixelHeight( m_height )));
    // read it back to catch Trident constraint.
    //CLEANUP_ON_FAILURE((hr = m_pielemTarget->get_docHeight( &m_height )));
    CLEANUP_ON_FAILURE((hr = m_pistyle->put_pixelHeight( m_rectOuter.bottom )));

    CLEANUP_ON_FAILURE((hr = m_pistyleTarget->put_pixelWidth( m_width )));
    // read it back to catch Trident constraint.
    //CLEANUP_ON_FAILURE((hr = m_pielemTarget->get_docWidth( &m_width )));
    CLEANUP_ON_FAILURE((hr = m_pistyle->put_pixelWidth( m_rectOuter.right )));

    if(IS_BIDI_LOCALIZED_SYSTEM() && CAPTION_ONLY)
    {
         CLEANUP_ON_FAILURE((hr = m_pistyle->put_pixelLeft(m_rectOuter.left )));
         CLEANUP_ON_FAILURE((hr = m_pistyle->put_pixelWidth(m_rectOuter.right - m_rectOuter.left )));

    }

    if (EVAL(S_OK == GetParentWindow()))
        ::UpdateWindow(m_hwndParent);

CleanUp:
    return hr;
}


// IQuickActivate
HRESULT CDeskMovr::QuickActivate(QACONTAINER *pQACont, QACONTROL *pQACtrl)
{
    HRESULT hr = IQuickActivate_QuickActivate(pQACont, pQACtrl);

    if (pQACont)
    {
        ClearFlag(pQACtrl->dwViewStatus, VIEWSTATUS_OPAQUE);
    }

    return hr;
}


HRESULT CDeskMovr::_GetHTMLDoc(IOleClientSite * pocs, IHTMLDocument2 ** pphd2)
{
    HRESULT hr;
    IOleContainer * poc = NULL;

    if (!EVAL(pocs) || !EVAL(pphd2))
        return E_INVALIDARG;

    *pphd2 = NULL;
    hr = pocs->GetContainer(&poc);
    if (SUCCEEDED(hr))
    {
         hr = poc->QueryInterface(IID_IHTMLDocument2, (LPVOID*) pphd2);
         poc->Release();
    }

    return hr;
}



HRESULT CDeskMovr::_IsInElement(HWND hwndParent, POINT * ppt, IHTMLElement ** pphe)
{
    HRESULT hr = E_FAIL;
    ASSERT(pphe);

    *pphe = NULL;
    if (!ppt || ::ScreenToClient(hwndParent, ppt))
    {
        IHTMLDocument2 * phd2;

        ASSERT(m_spClientSite);
        hr = _GetHTMLDoc(m_spClientSite, &phd2);
        if (SUCCEEDED(hr))
        {
            if (ppt)
                hr = phd2->elementFromPoint(ppt->x, ppt->y, pphe);
            else
                hr = phd2->get_activeElement(pphe);

            if (!*pphe && SUCCEEDED(hr))
                hr = E_FAIL;    // Sometimes Trident returns S_FALSE on error.

            phd2->Release();
        }
    }

    return hr;
}



HRESULT CDeskMovr::_EnumComponents(LPFNCOMPENUM lpfn, LPVOID lpvData, DWORD dwData)
{
    HRESULT hr = E_FAIL;
    IActiveDesktop * padt = NULL;

    hr = CoCreateInstance(CLSID_ActiveDesktop, NULL, CLSCTX_INPROC, IID_IActiveDesktop, (LPVOID *)&padt);
    if (SUCCEEDED(hr))
    {
        int nCount;
        int nIndex;

        hr = padt->GetDesktopItemCount(&nCount, 0);

        if (EVAL(SUCCEEDED(hr)))
        {
            COMPONENT comp;

            for (nIndex = 0; nIndex < nCount; nIndex++)
            {
                comp.dwSize = sizeof(COMPONENT);

                hr = padt->GetDesktopItem(nIndex, &comp, 0);
                if (EVAL(SUCCEEDED(hr)))
                {
                    if ((hr = lpfn(&comp, lpvData, dwData)) != S_OK)
                        break;
                }
            }
        }

        padt->Release();
    }

    return hr;
}

HRESULT CDeskMovr::_EnumElements(LPFNELEMENUM lpfn, LPVOID lpvData, DWORD dwData)
{
    HRESULT hr;
    IHTMLDocument2 * phd2;

    ASSERT(m_spClientSite);

    if (SUCCEEDED(hr = _GetHTMLDoc(m_spClientSite, &phd2)))
    {
        IHTMLElementCollection * pelems;

        if (SUCCEEDED(hr = phd2->get_all(&pelems)))
        {
            VARIANT varIndex;
            VARIANT varDummy;
            IDispatch * pidisp;

            VariantInit(&varDummy);
            varIndex.vt = VT_I4;
            varIndex.lVal = 0;

            // Note:  This loop terminates when trident returns SUCCESS - but with a NULL pidisp.
            while (SUCCEEDED(hr = pelems->item(varIndex, varDummy, &pidisp)) && pidisp)
            {
                IHTMLElement * pielem;

                if (SUCCEEDED(hr = pidisp->QueryInterface(IID_IHTMLElement, (LPVOID *)&pielem)))
                {
                    hr = lpfn(pielem, lpvData, dwData);
                    pielem->Release();
                }

                pidisp->Release();

                if (hr != S_OK)
                    break;

                varIndex.lVal++;
            }

            pelems->Release();
        }
        phd2->Release();
    }
    return hr;
}


HRESULT lpfnZOrderCB(COMPONENT * pcomp, LPVOID lpvData, DWORD dwData)
{
    #define LPZORDERSLOT ((LONG *)lpvData)

    if (dwData ? (pcomp->cpPos.izIndex > *LPZORDERSLOT) : (pcomp->cpPos.izIndex < *LPZORDERSLOT))
        *LPZORDERSLOT = pcomp->cpPos.izIndex;

    return S_OK;
}

HRESULT CDeskMovr::_GetZOrderSlot(LONG * plZOrderSlot, BOOL fTop)
{
    HRESULT hr;

    ASSERT(plZOrderSlot);

    *plZOrderSlot = m_bWindowOnly ? 10000 : 5000;

    hr = _EnumComponents(lpfnZOrderCB, (LPVOID)plZOrderSlot, (DWORD)fTop);

    *plZOrderSlot += fTop ? 2 : -2; // Make sure we are above / below.

    return hr;
}


//=--------------------------------------------------------------------------=
// PersistTargetPosition   [helper function]
//=--------------------------------------------------------------------------=
// Update the registry entries that are the persistence of the desktop HTML.
//
// Parameters:
//    <none>
//
// Output:
//    <none>
//
// Notes:
//      If we fail, we do it quietly.
//=--------------------------------------------------------------------------=

void PersistTargetPosition( IHTMLElement *pielem,
                            int left,
                            int top,
                            int width,
                            int height,
                            int zIndex,
                            BOOL fSaveState,
                            BOOL fSaveOriginal,
                            DWORD dwNewState)
{
    // only do this persistence thing if we're in ( or completing ) an operation
    TCHAR szID[MAX_ID_LENGTH];
    BOOL fOK;

    if (SUCCEEDED(GetHTMLElementStrMember(pielem, szID, SIZECHARS(szID), (BSTR)(s_sstrIDMember.wsz))))
    {
        bool        bChangedPosition, bChangedSize;
        COMPPOS     compPos;

        // 99/03/23 #266412 vtan: The user has moved the deskmovr to a new position
        // make sure that it is within the work area of the display monitors.
        // ValidateComponentPosition() will do this for us and tell us whether the
        // the component got moved or resized.

        compPos.dwSize = sizeof(compPos);
        compPos.iLeft = left;
        compPos.iTop = top;
        compPos.dwWidth = width;
        compPos.dwHeight = height;

        ValidateComponentPosition(&compPos, dwNewState, COMP_TYPE_HTMLDOC, &bChangedPosition, &bChangedSize);
        if (bChangedPosition || bChangedSize)
        {
            IHTMLStyle  *pIStyle;

            // If the component got moved or resized then tell the object model
            // where the deskmovr is now.

            left = compPos.iLeft;
            top = compPos.iTop;
            width = compPos.dwWidth;
            height = compPos.dwHeight;
            if (SUCCEEDED(pielem->get_style(&pIStyle)))
            {
                pIStyle->put_pixelLeft(left);
                pIStyle->put_pixelTop(top);
                pIStyle->put_pixelWidth(width);
                pIStyle->put_pixelHeight(height);
                pIStyle->Release();
            }
        }
        fOK = UpdateDesktopPosition(szID, left, top, width, height, zIndex, fSaveState, fSaveOriginal, dwNewState);
    }

    TraceMsg(TF_CUSTOM2, "PersistTargetPosition(pielem=%s, <left=%d, top=%d, wid=%d, h=%d>)", szID, left, top, width, height);

}

void ObtainSavedStateForElem( IHTMLElement *pielem,
                       LPCOMPSTATEINFO pCompState, BOOL fRestoredState)
{
    // only do this persistence thing if we're in ( or completing ) an operation
    TCHAR szID[MAX_ID_LENGTH];
    BOOL fOK;

    if (SUCCEEDED(GetHTMLElementStrMember(pielem, szID, SIZECHARS(szID), (BSTR)(s_sstrIDMember.wsz))))
        fOK = GetSavedStateInfo(szID, pCompState, fRestoredState);

    TraceMsg(TF_CUSTOM2, "ObtainSavedStateForElem(pielem=%s, <left=%d, top=%d, wid=%d, h=%d>)", szID, pCompState->iLeft, pCompState->iTop, pCompState->dwWidth, pCompState->dwHeight);

}

// IOleObject
HRESULT CDeskMovr::GetMiscStatus(DWORD  dwAspect, DWORD *pdwStatus)
{
    if (dwAspect == DVASPECT_CONTENT)
    {
        *pdwStatus = OLEMISMOVR;
        return S_OK;
    }
    else
    {
        return DV_E_DVASPECT;
    }

    // dead code
}


HRESULT CDeskMovr::SetClientSite(IOleClientSite * pClientSite)
{
    if (!pClientSite)
        DeactivateMovr(FALSE);

    return CComControlBase::IOleObject_SetClientSite(pClientSite);
}

void HandleRestore(IHTMLElement * pielem, LONG lData)
{
    VARIANT varZ;
    COMPSTATEINFO csiRestore;
    IHTMLStyle * pistyle;

    if (SUCCEEDED(pielem->get_style(&pistyle)))
    {
        csiRestore.dwSize = sizeof(csiRestore);

        ObtainSavedStateForElem(pielem, &csiRestore, TRUE); // TRUE => Get restored state!

        pistyle->put_pixelLeft(csiRestore.iLeft);
        pistyle->put_pixelTop(csiRestore.iTop);
        pistyle->put_pixelWidth(csiRestore.dwWidth);
        pistyle->put_pixelHeight(csiRestore.dwHeight);

        varZ.vt = VT_I4;
        varZ.lVal = lData;
        pistyle->put_zIndex(varZ);

        PersistTargetPosition(pielem, csiRestore.iLeft, csiRestore.iTop, csiRestore.dwWidth, csiRestore.dwHeight, varZ.lVal, FALSE, FALSE, IS_NORMAL);
        pistyle->Release();
    }
}

HRESULT lpfnRestoreCB(IHTMLElement * pielem, LPVOID lpvData, LONG lData)
{
    HRESULT hres = S_OK;
    TCHAR szID[MAX_ID_LENGTH];

    if (SUCCEEDED(GetHTMLElementStrMember(pielem, szID, SIZECHARS(szID), (BSTR)(s_sstrIDMember.wsz))))
    {
        DWORD dwState = GetCurrentState(szID);

        // Since there is only one in this state we can stop the enumeration if we
        // find a fullscreen/split item on this work area.
        if (dwState & (IS_FULLSCREEN | IS_SPLIT)) {
            POINT pt;
            if (SUCCEEDED(CSSOM_TopLeft(pielem, &pt)) && PtInRect((CONST RECT *)lpvData, pt))
            {
                HandleRestore(pielem, lData);
                hres = S_FALSE;
            }
        }
    }

    return hres;
}


HRESULT CDeskMovr::_HandleZoom(LONG lCommand)
{
    LONG x, y, cx, cy, zIndex;
    VARIANT varZ;
    DWORD   dwOldItemState = m_ItemState, dwNewItemState;
    IHTMLStyle * pistyleTarget = m_pistyleTarget;
    IHTMLElement * pielemTarget = m_pielemTarget;

    // Paranoia
    if (!pistyleTarget || !pielemTarget)
    {
        ASSERT(FALSE);
        return E_FAIL;
    }

    // Hold on to these guys during this call, they could go away when we yield
    // like during the animation call below.
    pistyleTarget->AddRef();
    pielemTarget->AddRef();

    if (lCommand == IDM_DCCM_RESTORE)
    {
        COMPSTATEINFO   csi;
        csi.dwSize = sizeof(csi);

        // The "Restore" command toggles with the "Reset Original Size" command.
        // Make sure we get the correct Restore or Reset position for the element.
        ObtainSavedStateForElem(pielemTarget, &csi, !ISNORMAL);

        if (ISNORMAL)
        {
            // This is the split case, dont move the item just resize it.
            x = m_left;
            y = m_top;
        }
        else
        {

//  98/07/27 vtan #176721: The following checks restoration of a component
//  position from zoomed to user-specified position. If the component
//  is placed at the default position then it is positioned now using
//  the standard positioning code.

            if ((csi.iLeft == COMPONENT_DEFAULT_LEFT) &&
                (csi.iTop == COMPONENT_DEFAULT_TOP) &&
                (csi.dwWidth == COMPONENT_DEFAULT_WIDTH) &&
                (csi.dwHeight == COMPONENT_DEFAULT_HEIGHT))
            {
                COMPPOS     compPos;

                GetNextComponentPosition(&compPos);
                IncrementComponentsPositioned();
                csi.iLeft = compPos.iLeft;
                csi.iTop  = compPos.iTop;
                csi.dwWidth = compPos.dwWidth;
                csi.dwHeight = compPos.dwHeight;
            }
            // Restore case, go ahead and move it.
            x = csi.iLeft;
            y = csi.iTop;
        }

        cx = csi.dwWidth;
        cy = csi.dwHeight;
        m_ItemState = (m_ItemState & ~IS_VALIDSIZESTATEBITS) | IS_NORMAL;
        dwNewItemState = m_ItemState;

        m_zIndexTop += 2;
        zIndex = m_zIndexTop;
    }
    else
    {
        RECT rcZoom, rcWork;

        GetZoomRect(lCommand == IDM_DCCM_FULLSCREEN, TRUE, m_left, m_top, m_width, m_height, &rcZoom, &rcWork);
        
        x = rcZoom.left;
        y = rcZoom.top;
        cx = rcZoom.right - rcZoom.left;
        cy = rcZoom.bottom - rcZoom.top;

        if (lCommand == IDM_DCCM_FULLSCREEN)
        {
            m_ItemState = (m_ItemState & ~IS_VALIDSIZESTATEBITS) | IS_FULLSCREEN;
            dwNewItemState = m_ItemState;
        }
        else
        {
            m_ItemState = (m_ItemState & ~IS_VALIDSIZESTATEBITS) | IS_SPLIT;
            dwNewItemState = m_ItemState;
        }

        varZ.vt = VT_I4;
        pistyleTarget->get_zIndex(&varZ);

        // We currently only allow 1 component to be either split or full screen per monitor (WorkArea), so
        // restore any other component that is currently in this state.
        _EnumElements(lpfnRestoreCB, (LPVOID)&rcWork, varZ.lVal);

        m_zIndexBottom -= 2;
        zIndex = m_zIndexBottom;
    }

    // We want to do the animation call before we start moving the target, it looks better
    // that way.
    AnimateComponent(m_hwndParent, m_left, m_top, m_width, m_height, x, y, cx, cy);

    pistyleTarget->put_pixelLeft(x);
    pistyleTarget->put_pixelTop(y);
    pistyleTarget->put_pixelWidth(cx);
    pistyleTarget->put_pixelHeight(cy);

    varZ.vt = VT_I4;
    varZ.lVal = zIndex;
    pistyleTarget->put_zIndex(varZ);

    PersistTargetPosition(pielemTarget, x, y, cx, cy, zIndex, 
                            (BOOL)((dwOldItemState & IS_NORMAL) && !(dwNewItemState & IS_NORMAL)),
                            FALSE, dwNewItemState);

    pistyleTarget->Release();
    pielemTarget->Release();

    return S_OK;
}


/************************************************************************\
    FUNCTION: CDeskMovr::_DisplayContextMenu

    PARAMETERS:
        x,y - Coordinates relative to the desktop window.
\************************************************************************/
HRESULT CDeskMovr::_DisplayContextMenu()
{
    HRESULT hr = S_OK;
    HMENU hmenuContext = LoadMenuPopup(MENU_DESKCOMP_CONTEXTMENU);

    TraceMsg(TF_CUSTOM2, "CDeskMovr::DisplayContextMenu(), m_bstrTargetName=%ls", GEN_DEBUGSTRW(m_bstrTargetName));
    if (hmenuContext)
    {
        int nSelection;
        BOOL fSubscribe = FALSE;
        BOOL fRemoveSubscribe = FALSE;
        TCHAR szName[MAX_URL_STRING];
        POINT point;

        if (CAPTION_ONLY)
        {
            point.x = m_left + m_cxBorder;
            point.y = m_top + (m_cyCaption + m_cyBorder) - 4 * m_cySMBorder;
        } else {
            point.x = m_left - m_cxSMBorder;
            point.y = m_top - 4 * m_cySMBorder;
        }

        ::ClientToScreen(m_hwndParent, &point);

        //  This calculation needs to be revisited.  The reason it's so
        //  ugle and HACKy is because to look good, we want the context menu
        //  to appear on top of the 3-D edge below the triangle.


        if (SUCCEEDED(GetHTMLElementStrMember(m_pielemTarget, szName, SIZECHARS(szName), (BSTR)(s_sstrSubSRCMember.wsz))))
        {
            int nScheme = GetUrlScheme(szName);

            if ((URL_SCHEME_FILE == nScheme) || (URL_SCHEME_INVALID == nScheme))
                fRemoveSubscribe = TRUE;
        }

        // check to see if we need to turn some things off or on
        // Mainly because we are disabling features Admins don't want users to have.

        hr = IElemCheckForExistingSubscription(m_pielemTarget);
        if (fRemoveSubscribe || FAILED(hr))    // This object/thing cannot be subscribed to. (Channel Changer, Orenge Blob).
        {
            MENUITEMINFO    menuItemInfo;

            DeleteMenu(hmenuContext, IDM_DCCM_OFFLINE, MF_BYCOMMAND);
            DeleteMenu(hmenuContext, IDM_DCCM_SYNCHRONIZE, MF_BYCOMMAND);
            DeleteMenu(hmenuContext, IDM_DCCM_PROPERTIES, MF_BYCOMMAND);

            // Is the top item in the list a separator?
            menuItemInfo.cbSize = sizeof(menuItemInfo);
            menuItemInfo.fMask = MIIM_TYPE;
            if ((GetMenuItemInfo(hmenuContext, 0, TRUE, &menuItemInfo) != FALSE) &&
                (menuItemInfo.fType == MFT_SEPARATOR))
            {
                // Yes, it is, so remove it.
                DeleteMenu(hmenuContext, 0, MF_BYPOSITION);
            }
        }
        else if (S_FALSE == hr)      // Not subscribed
        {
            DeleteMenu(hmenuContext, IDM_DCCM_SYNCHRONIZE, MF_BYCOMMAND);
            DeleteMenu(hmenuContext, IDM_DCCM_PROPERTIES, MF_BYCOMMAND);
            fSubscribe = TRUE;
        }
        else if (S_OK == hr)
        {
            if (SHRestricted2(REST_NoManualUpdates, NULL, 0))
                DeleteMenu(hmenuContext, IDM_DCCM_SYNCHRONIZE, MF_BYCOMMAND);
            if (SHRestricted(REST_NOEDITDESKCOMP))
                DeleteMenu(hmenuContext, IDM_DCCM_PROPERTIES, MF_BYCOMMAND);

            CheckMenuItem(hmenuContext, IDM_DCCM_OFFLINE, MF_BYCOMMAND |MF_CHECKED);
        }

        if (SHRestricted(REST_NOCLOSEDESKCOMP))
            EnableMenuItem(hmenuContext, IDM_DCCM_CLOSE, MF_BYCOMMAND | MF_GRAYED);

        // If policy is set to lock down active desktop, don't put up the
        //    menu that invokes the web-tab
        if (SHRestricted(REST_NOACTIVEDESKTOPCHANGES) || SHRestricted(REST_NODISPBACKGROUND))
        {
            EnableMenuItem(hmenuContext, IDM_DCCM_CUSTOMIZE, MF_BYCOMMAND | MF_GRAYED);
        }
        
        if (ISNORMAL)
        {
            COMPSTATEINFO CompState;
            LoadString(HINST_THISDLL, IDS_MENU_RESET, szName, ARRAYSIZE(szName));
            ModifyMenu(hmenuContext, IDM_DCCM_RESTORE, MF_BYCOMMAND | MF_STRING, IDM_DCCM_RESTORE, szName);
            ObtainSavedStateForElem(m_pielemTarget, &CompState, FALSE);
            if ((CompState.dwWidth == COMPONENT_DEFAULT_WIDTH && CompState.dwHeight == COMPONENT_DEFAULT_HEIGHT) ||
                (CompState.dwWidth == (DWORD)m_width && CompState.dwHeight == (DWORD)m_height))
                EnableMenuItem(hmenuContext, IDM_DCCM_RESTORE, MF_BYCOMMAND | MF_GRAYED);
        }
        if (ISSPLIT || !m_fCanResizeX || !m_fCanResizeY)
            EnableMenuItem(hmenuContext, IDM_DCCM_SPLIT, MF_BYCOMMAND | MF_GRAYED);
        if (ISFULLSCREEN || !m_fCanResizeX || !m_fCanResizeY)
            EnableMenuItem(hmenuContext, IDM_DCCM_FULLSCREEN, MF_BYCOMMAND | MF_GRAYED);

        g_fIgnoreTimers |= IGNORE_CONTEXTMENU_UP;

        nSelection = TrackPopupMenu(hmenuContext, TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_NONOTIFY | TPM_RETURNCMD, point.x, point.y, 0, m_hwndParent, NULL);
        
        DestroyMenu(hmenuContext);

        m_CaptionState &= ~CS_MENUPUSHED;
        UpdateCaption(DMDC_MENU);

        switch (nSelection)
        {
            case IDM_DCCM_OFFLINE:
                if (fSubscribe)
                    hr = IElemSubscribeDialog(m_pielemTarget, m_hWnd);
                else
                    hr = IElemUnsubscribe(m_pielemTarget);
                break;

            case IDM_DCCM_SYNCHRONIZE:
                hr = IElemUpdate(m_pielemTarget);
                break;

            case IDM_DCCM_PROPERTIES:   // Subscriptions Dialog (Don't let the name fool you)
                TraceMsg(TF_CUSTOM2, "CDeskMovr::_DisplayContextMenu() IDM_DCCM_PROPERTIES m_bstrTargetName=%ls.", GEN_DEBUGSTRW(m_bstrTargetName));
                    hr = IElemGetSubscriptionsDialog(m_pielemTarget, NULL);
                break;

            case IDM_DCCM_CUSTOMIZE:   // Show Display Control Panel set to Components Sheet
                LoadString(HINST_THISDLL, IDS_COMPSETTINGS, szName, ARRAYSIZE(szName));
                SHRunControlPanel(szName, NULL);
                hr = S_OK;
                break;

            case IDM_DCCM_CLOSE:
                ASSERT(!SHRestricted(REST_NOCLOSEDESKCOMP));  // We should never be able to get here.
    
                TraceMsg(TF_CUSTOM2, "CDeskMovr::_DisplayContextMenu() IDM_DCCM_CLOSE m_bstrTargetName=%ls", GEN_DEBUGSTRW(m_bstrTargetName));
//                AnimateToTray(m_hwndParent, m_left, m_top, m_width, m_height);
                hr = IElemCloseDesktopComp(m_pielemTarget);
                break;

            case IDM_DCCM_RESTORE:
            case IDM_DCCM_FULLSCREEN:
            case IDM_DCCM_SPLIT:
                hr = _HandleZoom(nSelection);
                break;

            case IDM_DCCM_OPEN:
                {
                    BOOL fShowFrame = (GetKeyState(VK_SHIFT) < 0) ? !(m_fCanResizeX && m_fCanResizeY) : (m_fCanResizeX && m_fCanResizeY);
                    hr = IElemOpenInNewWindow(m_pielemTarget, m_spClientSite, fShowFrame, m_width, m_height);
                }
                break;

        }

        g_fIgnoreTimers &= ~IGNORE_CONTEXTMENU_UP;

        if (nSelection)
            DismissSelfNow();
    }

    return hr;
}

void CDeskMovr::_MapPoints(int * px, int * py)
{
    if (m_bWndLess)
    {
        *px -= m_left - (CAPTION_ONLY ? 0 : m_cxBorder);
        *py -= m_top - (CAPTION_ONLY ? 0 : (m_cyBorder + m_cyCaption));
    }
}

void CDeskMovr::_ChangeCapture(BOOL fSet)
{
    if (m_fCaptured != fSet)
    {
        m_fCaptured = fSet;
        if (fSet)
        {
            ASSERT(m_spInPlaceSite);
            if (m_bWndLess && m_spInPlaceSite)
            {
                m_fCaptured = SUCCEEDED(m_spInPlaceSite->SetCapture(TRUE));
            }
            else
            {
                ::SetCapture( m_hWnd );
                m_fCaptured = (GetCapture() == m_hWnd);
            }
            if (m_fCaptured)
                g_fIgnoreTimers |= IGNORE_CAPTURE_SET;
        }
        else
        {
            ASSERT(m_spInPlaceSite);
            if (m_bWndLess && m_spInPlaceSite)
            {
                m_spInPlaceSite->SetCapture(FALSE);
            }
            else
            {
                ReleaseCapture();
            }
        
            g_fIgnoreTimers &= ~IGNORE_CAPTURE_SET;
        }
    }
}

// Called from our keyboard hook so that we can implement keyboard invocation and dismissal
// of the deskmovr.
void CDeskMovr::OnKeyboardHook(WPARAM wParam, LPARAM lParam)
{
    IHTMLElement * pielem;
    HWND hwndFocus = GetFocus();

    if (!(g_fIgnoreTimers & IGNORE_CONTEXTMENU_UP) && SUCCEEDED(GetParentWindow()) && ((hwndFocus == m_hwndParent) || ::IsChild(m_hwndParent, hwndFocus)))
    {
        switch (wParam) {
            case VK_MENU:
                if (!m_pielemTarget && !(GetDesktopFlags() & COMPONENTS_LOCKED) && SUCCEEDED(SmartActivateMovr(ERROR_SUCCESS)) && SUCCEEDED(_IsInElement(NULL, NULL, &pielem)))
                {
                    BOOL fDummy;
                    _TrackElement(NULL, pielem, &fDummy);
                    pielem->Release();
                }
                break;

            case VK_ESCAPE:
            case VK_TAB:
                if ((lParam >= 0) && m_pielemTarget)  // If key down, dismiss
                    DismissSelfNow();
                break;

            case VK_SPACE:
                if (m_pielemTarget && (GET_CYCAPTION == m_cyCaption) && (HIWORD(lParam) & KF_ALTDOWN))
                {
                    HandleNonMoveSize(dmMenu);
                }
                break;
        }
    }
}


STDAPI CDeskMovr_CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppunk)
{
    return CComCreator< CComPolyObject< CDeskMovr > >::CreateInstance( (LPVOID)pUnkOuter, IID_IUnknown, (LPVOID*)ppunk );
}
