#include "priv.h"
#include "sccls.h"

#include <mluisupp.h>

#define IPSMSG(psz)             TraceMsg(TF_SHDCONTROL, "she TR-IPS::%s called", psz)
#define IPSMSG2(psz, hres)      TraceMsg(TF_SHDCONTROL, "she TR-IPS::%s %x", psz, hres)
#define IPSMSG3(pszName, psz)   TraceMsg(TF_SHDCONTROL, "she TR-IPS::%s:%s called", pszName,psz)
#define IOOMSG(psz)             TraceMsg(TF_SHDCONTROL, "she TR-IOO::%s called", psz)
#define IOOMSGX(psz, hres)      TraceMsg(TF_SHDCONTROL, "she TR-IOO::%s returning %x", psz, hres)
#define IOOMSG2(psz, i)         TraceMsg(TF_SHDCONTROL, "she TR-IOO::%s called with (%d)", psz, i)
#define IOOMSG3(psz, i, j)      TraceMsg(TF_SHDCONTROL, "she TR-IOO::%s called with (%d, %d)", psz, i, j)
#define IVOMSG(psz)             TraceMsg(TF_SHDCONTROL, "she TR-IVO::%s called", psz)
#define IVOMSG2(psz, i)         TraceMsg(TF_SHDCONTROL, "she TR-IVO::%s called with (%d)", psz, i)
#define IVOMSG3(psz, i, j)      TraceMsg(TF_SHDCONTROL, "she TR-IVO::%s with (%d, %d)", psz, i, j)
#define CCDMSG(psz, punk)       TraceMsg(TF_SHDCONTROL, "she TR-CSV::%s called punk=%x", psz, punk)
#define IDTMSG(psz)             TraceMsg(TF_SHDCONTROL, "she TR-IDT::%s called", psz)
#define IDTMSG2(psz, i)         TraceMsg(TF_SHDCONTROL, "she TR-IDT::%s called with %d", psz, i)
#define IDTMSG3(psz, x)         TraceMsg(TF_SHDCONTROL, "she TR-IDT::%s %x", psz, x)
#define IDTMSG4(psz, i, j)      TraceMsg(TF_SHDCONTROL, "she TR-IDT::%s called with %x,%x", psz, i, j)
#define IIPMSG(psz)             TraceMsg(TF_SHDCONTROL, "she TR-IOIPO::%s called", psz)
#define IIAMSG(psz)             TraceMsg(TF_SHDCONTROL, "she TR-IOIPAO::%s called", psz)
#define IEVMSG(psz, i, j, ps)   TraceMsg(TF_SHDCONTROL, "she TR-IEV::%s called celt=%d, _iCur=%d, %x", psz, i, j, ps)

const TCHAR c_szShellEmbedding[] = TEXT("Shell Embedding");

//
// A special lindex value to be passed to ::Draw member indicating
// that it is an internal call from ::GetData
//
#define LINDEX_INTERNAL 12345

// REVIEW: We may want to use the functions in UTIL.C -- they look more efficient...
//
//=========================================================================
// Helper functions
//=========================================================================

#define HIM_PER_IN 2540

int g_iXppli = 0;
int g_iYppli = 0;

void GetLogPixels()
{
    HDC hdc = GetDC(NULL);
    if (hdc)
    {
        g_iXppli = GetDeviceCaps(hdc, LOGPIXELSX);
        g_iYppli = GetDeviceCaps(hdc, LOGPIXELSY);
        ReleaseDC(NULL, hdc);
    }
}

// Scalar conversion of MM_HIMETRIC to MM_TEXT
void MetricToPixels(SIZEL* psize)
{
    ASSERT(g_iXppli);

    psize->cx = MulDiv(psize->cx, g_iXppli, HIM_PER_IN);
    psize->cy = MulDiv(psize->cy, g_iYppli, HIM_PER_IN);
}

// Scalar conversion of MM_TEXT to MM_HIMETRIC
void PixelsToMetric(SIZEL* psize)
{
    ASSERT(g_iYppli);

    psize->cx = MulDiv(psize->cx, HIM_PER_IN, g_iXppli);
    psize->cy = MulDiv(psize->cy, HIM_PER_IN, g_iYppli);
}


//=========================================================================
// CShellEmbedding implementaiton
//=========================================================================
HRESULT CShellEmbedding::v_InternalQueryInterface(REFIID riid, LPVOID * ppvObj)
{
    static const QITAB qit[] = {
        QITABENT(CShellEmbedding, IPersist),
        QITABENT(CShellEmbedding, IOleObject),
        QITABENT(CShellEmbedding, IViewObject2),
        QITABENTMULTI(CShellEmbedding, IViewObject, IViewObject2),
        QITABENT(CShellEmbedding, IDataObject),
        QITABENT(CShellEmbedding, IOleInPlaceObject),
        QITABENTMULTI(CShellEmbedding, IOleWindow, IOleInPlaceObject),
        QITABENT(CShellEmbedding, IOleInPlaceActiveObject),
        QITABENT(CShellEmbedding, IInternetSecurityMgrSite),
        { 0 },
    };

    return QISearch(this, qit, riid, ppvObj);
}

CShellEmbedding::CShellEmbedding(IUnknown* punkOuter, LPCOBJECTINFO poi, const OLEVERB* pverbs)
    : _pverbs(pverbs)
    , _nActivate(OC_DEACTIVE)
    , CAggregatedUnknown(punkOuter)
{
    TraceMsg(TF_SHDCONTROL, "ctor CShellEmbedding %x", this);

    DllAddRef();
    _RegisterWindowClass();
    _pObjectInfo = poi;
    _size.cx = 50;
    _size.cy = 20;

    // make sure some globals are set
    GetLogPixels();

    // let our logical size match our physical size
    _sizeHIM = _size;
    PixelsToMetric(&_sizeHIM);
}

CShellEmbedding::~CShellEmbedding()
{
    ASSERT(_hwnd==NULL);
    // IE v 4.1 bug 44541.  In an Office 97 user form, we were seeing this destructor get entered
    // with a non-null hwnd, which would cause a fault the next time the hwnd received a message.
    // 
    if (_hwnd)
    {
        DestroyWindow(_hwnd);
        _hwnd = NULL;
    }
    ASSERT(_hwndChild==NULL);
    ASSERT(_pcli==NULL);
    ASSERT(_pipsite==NULL);
    ASSERT(_pipframe==NULL);
    ASSERT(_pipui==NULL);

    //
    // WARNING: Don't call any of virtual functions of this object
    //  itself for clean-up purpose. The Vtable is alreadly adjusted
    //  and we won't be able to perform any full clean up. Do it
    //  right before you delete in CShellEmbedding::CSVInner::Release.
    //
    TraceMsg(TF_SHDCONTROL, "dtor CShellEmbedding %x", this);

    // Warning: if the client site has not been released do not release the advise
    // object as some applications like VC5 will fault on this...
    if (_padv) {
        _padv->OnClose();
        if (!_pcli)
            ATOMICRELEASE(_padv);
    }

    if (!_pcli)
    {
        ATOMICRELEASE(_pdah);
        ATOMICRELEASE(_poah);
    }
    ATOMICRELEASE(_pstg);
    ATOMICRELEASE(_pcliHold);


    DllRelease();
}

// **************** IPersist ****************
HRESULT CShellEmbedding::GetClassID(CLSID *pClassID)
{
    *pClassID = CLSIDOFOBJECT(this);

    return S_OK;
}

BOOL CShellEmbedding::_ShouldDraw(LONG lindex)
{
    // Don't draw if the window is visible.
    return ! (_pipsite && lindex!=LINDEX_INTERNAL);
}

// **************** IViewObject ****************
HRESULT CShellEmbedding::Draw(
    DWORD dwDrawAspect,
    LONG lindex,
    void *pvAspect,
    DVTARGETDEVICE *ptd,
    HDC hdcTargetDev,
    HDC hdcDraw,
    LPCRECTL lprcBounds,
    LPCRECTL lprcWBounds,
    BOOL ( __stdcall *pfnContinue )(ULONG_PTR dwContinue),
    ULONG_PTR dwContinue)
{
    IVOMSG3(TEXT("Draw called"), lprcBounds->top, lprcBounds->bottom);

    // WARNING: this looks wrong to me -- I think we should always respond
    // to a Draw request, as the hdcDraw may not be the screen!
    //
    // Don't draw if the window is visible.
    if (!_ShouldDraw(lindex)) {
        return S_OK;
    }

    if (_hwnd) {
        int iDC = SaveDC(hdcDraw);
          RECTL rcBounds = *lprcBounds;
          ::LPtoDP(hdcDraw, (LPPOINT)&rcBounds, 2);
          IVOMSG3(TEXT("Draw DP=="), rcBounds.top, rcBounds.bottom);
          TraceMsg(TF_SHDCONTROL, "she Draw cx=%d cy=%d", rcBounds.right-rcBounds.left, rcBounds.bottom-rcBounds.top);

          SetMapMode(hdcDraw, MM_TEXT);         // make it 1:1
          SetMapMode(hdcDraw, MM_ANISOTROPIC);  // inherit call from MM_TEXT
          POINT pt;
          SetViewportOrgEx(hdcDraw, rcBounds.left, rcBounds.top, &pt);

          // APPCOMPAT: WordPad does a GetExtent to get the size and passes that in as lprcBounds
          // *without* doing a SetExtent, so when we resize larger (due to a BROWSE verb) _hwnd
          // is still the old size. So we IntersectClipRect to _hwnd but WordPad draws the border
          // to rcBounds. Ugly.

           RECT rc;
           GetClientRect(_hwnd, &rc);
           IntersectClipRect(hdcDraw, 0, 0, rc.right, rc.bottom);
           SendMessage(_hwnd, WM_PRINT, (WPARAM)hdcDraw,
                       PRF_NONCLIENT|PRF_CLIENT|PRF_CHILDREN|PRF_ERASEBKGND);

         SetViewportOrgEx(hdcDraw, pt.x, pt.y, NULL);
        RestoreDC(hdcDraw, iDC);
        return S_OK;
    }

    return OLE_E_BLANK;
}

HRESULT CShellEmbedding::GetColorSet(
    DWORD dwDrawAspect,
    LONG lindex,
    void *pvAspect,
    DVTARGETDEVICE *ptd,
    HDC hicTargetDev,
    LOGPALETTE **ppColorSet)
{
    IVOMSG(TEXT("GetColorSet"));
    return S_FALSE;     // Indicating that the object doesn't care
}

HRESULT CShellEmbedding::Freeze(
    DWORD dwDrawAspect,
    LONG lindex,
    void *pvAspect,
    DWORD *pdwFreeze)
{
    IVOMSG(TEXT("Freeze"));
    *pdwFreeze = 0;
    return S_OK;
}

HRESULT CShellEmbedding::Unfreeze(DWORD dwFreeze)
{
    IVOMSG(TEXT("Unfreeze"));
    return S_OK;
}

HRESULT CShellEmbedding::SetAdvise(
    DWORD aspects,
    DWORD advf,
    IAdviseSink *pAdvSink)
{
    IVOMSG2(TEXT("SetAdvise"), pAdvSink);

    if (advf & ~(ADVF_ONLYONCE | ADVF_PRIMEFIRST))
        return E_INVALIDARG;

    if (pAdvSink != _padv)
    {
        ATOMICRELEASE(_padv);

        if (pAdvSink)
        {
            _padv = pAdvSink;
            _padv->AddRef();
        }
    }

    _asp  = aspects;
    _advf = advf;

    if (advf & ADVF_PRIMEFIRST)
        _SendAdvise(OBJECTCODE_VIEWCHANGED);

    return S_OK;
}

HRESULT CShellEmbedding::GetAdvise(
    DWORD *pAspects,
    DWORD *pAdvf,
    IAdviseSink **ppAdvSink)
{
    IVOMSG(TEXT("GetAdvise"));
    if (pAspects) {
        *pAspects = _asp;
    }

    if (pAdvf) {
        *pAdvf = _advf;
    }

    if (ppAdvSink) {
        *ppAdvSink = _padv;
        if (_padv) {
            _padv->AddRef();
        }
    }

    return S_OK;
}

// **************** IViewObject2 ****************
HRESULT CShellEmbedding::GetExtent(
    DWORD dwDrawAspect,
    LONG lindex,
    DVTARGETDEVICE *ptd,
    LPSIZEL lpsizel)
{
    TraceMsg(TF_SHDCONTROL, "she GetExtent cx=%d cy=%d", _size.cx, _size.cy);
    lpsizel->cx = _size.cx;
    lpsizel->cy = _size.cy;
    PixelsToMetric(lpsizel);
    return S_OK;
}

//
// **************** IOleObject ****************
//

void CShellEmbedding::_OnSetClientSite()
{
    if (_pcli)
    {
        IOleInPlaceSite* pipsite;
        if (SUCCEEDED(_pcli->QueryInterface(IID_IOleInPlaceSite, (LPVOID*)&pipsite)))
        {
            _CreateWindowOrSetParent(pipsite);
            pipsite->Release();
        }
    }
    else if (_hwnd)
    {
        DestroyWindow(_hwnd);
        _hwnd = NULL;
    }
}

HRESULT CShellEmbedding::SetClientSite(IOleClientSite *pClientSite)
{
    IOOMSG2(TEXT("SetClientSite"), pClientSite);

    // If I have a client site on hold, get rid of it.
    //
    ATOMICRELEASE(_pcliHold);

    if (_pcli == pClientSite)
    {
        // mshtml is hitting their initialization code twice
        // no need for us to do anything here.
    }
    else
    {
        ATOMICRELEASE(_pcli);
        ATOMICRELEASE(_pipsite);
        ATOMICRELEASE(_pipframe);
        ATOMICRELEASE(_pipui);
    
        _pcli = pClientSite;
    
        if (_pcli)
            _pcli->AddRef();

        _OnSetClientSite();
    }

    return S_OK;
}


//
//  This function create _hwnd (the parent of this embedding) if it not
// created yet. Otherwise, it simply SetParent appropriately.
//
// NOTE: When this object is embedded in PowerPoint 95, the first
//  CreateWindowEx fails  when this function if called from SetClientSite
//  for some unknown reason.
//   It, however, succeeds when it is called from DoVerb. We should find
//  it out.
//
HRESULT CShellEmbedding::_CreateWindowOrSetParent(IOleWindow* pwin)
{
    HWND hwndParent = NULL;
    HRESULT hres = S_OK;

    //
    // NOTES: Unlike IE3.0, we don't fail even if pwin->GetWindow fails.
    //  It allows Trident to SetClientSite (and Navigate) before they
    //  are In-place Activated. In that case (hwndParent==NULL), we
    //  create a top-level window hidden and use it for navigation.
    //  When we are being InPlaceActivated, we hit this function again
    //  and set the parent (and window styles) correctly. Notice that
    //  we need to set WS_POPUP to avoid the Window Manager automatically
    //  add other random styles for verlapped window. 
    //
    pwin->GetWindow(&hwndParent);
#ifdef DEBUG
    // Pretend that GetWindow failed here.
    if (_hwnd==NULL && (g_dwPrototype & 0x00000200))
    {
        TraceMsg(DM_TRACE, "CSE::_CreateWindowOrSetParent pretend unsuccessful GetWindow");
        hwndParent = NULL;
    }
#endif

    _fOpen = TRUE;
    
    if (_hwnd) 
    {
        SetParentHwnd(_hwnd, hwndParent);

    } 
    else 
    {
        _hwnd = SHNoFusionCreateWindowEx(
            WS_EX_WINDOWEDGE,
            c_szShellEmbedding, NULL,
            (hwndParent ?
                (WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_TABSTOP)
                : (WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_TABSTOP)),
            0, 0, _rcPos.right - _rcPos.left, _rcPos.bottom - _rcPos.top,
            hwndParent,
            (HMENU)0,
            HINST_THISDLL,
            (LPVOID)SAFECAST(this, CImpWndProc*));

        if (!_hwnd) 
        {
            hres = E_FAIL;
            TraceMsg(TF_SHDCONTROL, "sdv TR-IOO::_CreateWindowOrSetParent CreateWindowEx failed (%d)", GetLastError());
        }
    }

    return hres;
}
HRESULT CShellEmbedding::GetClientSite(IOleClientSite **ppClientSite)
{
    IOOMSG(TEXT("GetClientSite"));
    *ppClientSite = _pcli;

    if (_pcli) {
        _pcli->AddRef();
    }

    return S_OK;
}

HRESULT CShellEmbedding::SetHostNames(
    LPCOLESTR szContainerApp,
    LPCOLESTR szContainerObj)
{
    IOOMSG(TEXT("SetHostNames"));
    // We are not interested in host name
    return S_OK;
}


// A container application calls IOleObject::Close when it wants
// to move the object from a running to a loaded state. Following
// such a call, the object still appears in its container but is
// not open for editing. Calling IOleObject::Close on an object
// that is loaded but not running has no effect.
//
HRESULT CShellEmbedding::Close(DWORD dwSaveOption)
{
    IOOMSG2(TEXT("Close"), dwSaveOption);
    // Change the state of object back to TEXT("loaded") state.

    BOOL fSave = FALSE;
    if (_fDirty &&
        ((OLECLOSE_SAVEIFDIRTY==dwSaveOption)
         || (dwSaveOption==OLECLOSE_PROMPTSAVE))) {
        fSave = TRUE;
    }

    if (fSave) {
        _SendAdvise(OBJECTCODE_SAVEOBJECT);
        _SendAdvise(OBJECTCODE_SAVED);
    }

    _SendAdvise(OBJECTCODE_CLOSED);
    _fOpen = FALSE;

    // "loaded but not running" is confusing wording... If you look
    // at the OLEIVERB_HIDE comment in _OnActivateChange, it mentions
    // that OLEIVERB_HIDE puts it in the state "just after loading"
    // and puts us in OC_DEACTIVE state. Let's do that here as well.
    //
    // it just came to my awareness that OCs UIDeactivate,
    //         not IPDeactivate...
    _DoActivateChange(NULL, OC_DEACTIVE, FALSE);

    // It seems like some containers (Trident frame set) don't
    // do a SetClientSite(NULL) on us, so do it here. Old code here
    // did a DestroyWindow(_hwnd), which SetClientSite(NULL) will do.
    // NOTE: VB does call SetClientSite, but they do it after Close.

    // If we already have one on hold, release it.
    //
    ATOMICRELEASE(_pcliHold);

    // Hold onto our client site.  We may need it if we're DoVerbed, as Office tends to do.
    //

    IOleClientSite  *pOleClientSite = _pcli;
    if (pOleClientSite)
    {
        pOleClientSite->AddRef();
    }

    SetClientSite(NULL);

    _pcliHold = pOleClientSite;

    return S_OK;
}

HRESULT CShellEmbedding::SetMoniker(
    DWORD dwWhichMoniker,
    IMoniker *pmk)
{
    IOOMSG(TEXT("SetMoniker"));
    // We are not interested in moniker.
    return S_OK;
}

HRESULT CShellEmbedding::GetMoniker(
    DWORD dwAssign,
    DWORD dwWhichMoniker,
    IMoniker **ppmk)
{
    IOOMSG(TEXT("GetMoniker"));
    return E_NOTIMPL;
}

HRESULT CShellEmbedding::InitFromData(
    IDataObject *pDataObject,
    BOOL fCreation,
    DWORD dwReserved)
{
    IOOMSG(TEXT("InitFromData"));
    // LATER: We may want to implement this later.
    return E_FAIL;
}

HRESULT CShellEmbedding::GetClipboardData(
    DWORD dwReserved,
    IDataObject **ppDataObject)
{
    IOOMSG(TEXT("GetClipboardData"));
    return E_FAIL;
}

HRESULT CShellEmbedding::DoVerb(
    LONG iVerb,
    LPMSG lpmsg,
    IOleClientSite *pActiveSite,
    LONG lindex,
    HWND hwndParent,
    LPCRECT lprcPosRect)
{
    IOOMSG2(TEXT("DoVerb"), iVerb);
    HRESULT hres = S_OK;

    // If I don't have a client site, but I have one on "hold", I need to set it up again.
    //
    if (_pcli == NULL
        && _pcliHold)
    {
        IOleClientSite *pOleClientSite = _pcliHold;
        _pcliHold = NULL;
        SetClientSite(pOleClientSite);
        pOleClientSite->Release();
    }

    switch(iVerb)
    {
    case OLEIVERB_HIDE:
        hres = _DoActivateChange(NULL, OC_DEACTIVE, FALSE);
        break;

    case OLEIVERB_OPEN:
        hres = E_FAIL;
        break;

    case OLEIVERB_PRIMARY:
    case OLEIVERB_SHOW:
        if (_pipsite) {
            return S_OK;
        }
        // Fall through
    case OLEIVERB_UIACTIVATE:
        hres = _DoActivateChange(pActiveSite, OC_UIACTIVE, TRUE); //TRUE => We want to force UIACTIVE even if we are already active.
        break;

    case OLEIVERB_INPLACEACTIVATE:
        hres = _DoActivateChange(pActiveSite, OC_INPLACEACTIVE, FALSE);
        break;

    default:
        hres = E_FAIL; // OLEOBJ_S_INVALDVERB;
        break;
    }

    IOOMSGX(TEXT("DoVerb"), hres);
    return hres;
}

//
// fForce == TRUE indicates that we need to call _OnActivateChange even if we
// are already in OC_UIACITVE state.
//
HRESULT CShellEmbedding::_DoActivateChange(IOleClientSite* pActiveSite, UINT uState, BOOL fForce)
{
    if (uState == _nActivate)
    {
        // in general, we have nothing to do if we're already in
        // the correct state. HOWEVER, OLEIVERB_UIACTIVATE is supposed
        // to set focus if we (or our children?) don't currently have it.
        // Fall into _OnActivateChange so CWebBrowserOC can tell the
        // base browser to go uiactive.
        //
        if ((uState != OC_UIACTIVE) || !fForce)
            return S_OK;
    }

    #define STATETOSTRING(n) (n==OC_DEACTIVE ? TEXT("OC_DEACTIVE") : (n==OC_INPLACEACTIVE ? TEXT("OC_INPLACEACTIVE") : (n== OC_UIACTIVE ? TEXT("OC_UIACTIVE") : TEXT("ERROR"))))
    TraceMsg(TF_SHDCONTROL, "she _DoActivateChange from %s to %s", STATETOSTRING(_nActivate), STATETOSTRING(uState));

    return _OnActivateChange(pActiveSite, uState);
}

HRESULT CShellEmbedding::_OnActivateChange(IOleClientSite* pActiveSite, UINT uState)
{
    if (uState != _nActivate)
    {
        // mark us in our new state immediately. this avoids recursion death with bad containers (ipstool)
        UINT uOldState = _nActivate;
        _nActivate = uState;
    
        if (uOldState == OC_DEACTIVE) // going from deactive to IP or UI active
        {
            if (pActiveSite==NULL)
            {
                _nActivate = uOldState;
                return E_INVALIDARG;
            }
    
            ASSERT(!_pipsite); // always true, so why check below?
            if (!_pipsite)
            {
                HRESULT hres = pActiveSite->QueryInterface(IID_IOleInPlaceSite, (LPVOID*)&_pipsite);
        
                if (FAILED(hres))
                {
                    _nActivate = uOldState;
                    return hres;
                }
                
                hres = _pipsite->CanInPlaceActivate();
                if (hres != S_OK) {
                    ATOMICRELEASE(_pipsite);
                    TraceMsg(TF_SHDCONTROL, "she - CanInPlaceActivate returned %x", hres);
                    _nActivate = uOldState;
                    return E_FAIL;
                }
        
                _OnInPlaceActivate(); // do it
            }
        }
        else if (uOldState == OC_UIACTIVE) // going from UIActive to IPActive or deactive
        {
            _OnUIDeactivate();
        }
    
        if (uState == OC_UIACTIVE) // going to UIActive
        {
            _OnUIActivate();
        }
        else if (uState == OC_DEACTIVE) // going to Deactive
        {
            // We fail creation (OLEIVERB_PRIMARY, OLEIVERB_SHOW,
            // OLEIVERB_UIACTIVATE, or OLEIVERB_INPLACEACTIVATE) if we don't
            // get a pipsite, so we should never hit this case.
            ASSERT(_pipsite);
            // OLEIVERB_HIDE should ... return it to the visual state just after
            // initial creation or reloading, before OLEIVERB_SHOW or OLEIVERB_OPEN
            // is sent. Which is what _InPlaceDeactivate does. What's the point of this?
            // htmlobj calls OLEIVERB_HIDE and then ::InPlaceDeactivate
            _OnInPlaceDeactivate();
        }
    }

    return S_OK;
}

// move from de-active to in-place-active
void CShellEmbedding::_OnInPlaceActivate()
{
    //
    // Set the appropriate parent window.
    //
    _CreateWindowOrSetParent(_pipsite);

    _pipsite->OnInPlaceActivate();
    ASSERT(_pipframe == NULL);
    ASSERT(_pipui == NULL);
    _finfo.cb = sizeof(OLEINPLACEFRAMEINFO);
    _pipsite->GetWindowContext(&_pipframe, &_pipui,
                               &_rcPos, &_rcClip, &_finfo);

    TraceMsg(TF_SHDCONTROL, "she::_OnInPlaceActivate x=%d y=%d cx=%d cy=%d (_cx=%d _cy=%d)", _rcPos.left, _rcPos.top, _rcPos.right-_rcPos.left, _rcPos.bottom-_rcPos.top, _size.cx, _size.cy);
    SetWindowPos(_hwnd, 0,
                 _rcPos.left, _rcPos.top,
                 _rcPos.right-_rcPos.left,
                 _rcPos.bottom-_rcPos.top,
                 SWP_SHOWWINDOW | SWP_NOZORDER);

    _SendAdvise(OBJECTCODE_SHOWOBJECT); // just like OLE 2nd ed (p.1074)
}

// Move from in-place-active to de-active
void CShellEmbedding::_OnInPlaceDeactivate(void)
{
    if (_hwnd) {
        ShowWindow(_hwnd, SW_HIDE);

        // re-parent our _hwnd... when we're not active we can't rely on
        // what our parent window is doing. The container can even destroy it!
        //
        // FEATURE: the standard thing to do here is DESTROY our HWND and
        // recreate it if/when we are reactivated. This may break our hosted
        // IShellView and draw code. Investigate this.
        //

        // APPCOMPAT: this has been taken out by CDturner, MikeSH assures me we don't need it, and 
        // this is causing our app to lose activation and regain it which causes the 
        // palette to flash on 256 colour machines...y
        // SetParentHwnd(_hwnd, NULL);
    }

    if (_pipsite) {
        _pipsite->OnInPlaceDeactivate();
        ATOMICRELEASE(_pipsite);
    }

    ATOMICRELEASE(_pipframe);
    ATOMICRELEASE(_pipui);

    //
    // We need to tell the container to update the cached metafile, if any.
    //
    _SendAdvise(OBJECTCODE_DATACHANGED);

}

// move from in-place-active to ui-active
void CShellEmbedding::_OnUIActivate(void)
{
    if (_pipsite) {
        _pipsite->OnUIActivate();
    }

    //
    // HACK: When we are in Excel, _pipui->SetActiveObject sets the focus
    //  to us (for some unknown reason -- trying to be nice?). Since _hwnd
    //  simply forward the focus to the _hwndChild, setting focus to _hwnd
    //  twice causes this:
    //
    //   1. SetFocus(_hwnd)             by us (if we call SetFocus(_hwnd))
    //   2. SetFocus(_hwndChild)        in _hwnd's wndproc
    //   3. SetFocus(_hwnd)             by Excel
    //   4. SetFocus(_hwndChild)        in _hwnd's wndproc
    //
    //   If _hwndChild is a control, it notifies to the parent that it
    //  lost the focus. Then, we think "oh, we lost the focus. We should
    //  deactivate this object". To avoid it, we don't call SetFocus before
    //  we call _pipui->SetActiveObject and do some tricky thing below.
    //
    // SetFocus(_hwnd);

    //
    // RDuke suggest us to change the second parameter to NULL (instead of
    // "FOO" in IE3, but we don't know the side effect of it. I'm changing
    // it to "item" for IE4. (SatoNa)
    //
    if (_pipframe) {
        _pipframe->SetActiveObject(SAFECAST(this, IOleInPlaceActiveObject*), L"item");
    }

    if (_pipui) {
        _pipui->SetActiveObject(SAFECAST(this, IOleInPlaceActiveObject*), L"item");
    }

    //
    // We don't have any menu, so tell the container to use its own menu.
    //
    if (_pipframe) {
        _pipframe->SetMenu(NULL, NULL, _hwnd);
    }

    // Find-out if one of our child window has the input focus.
    for (HWND hwndFocus = GetFocus();
         hwndFocus && hwndFocus!=_hwnd;
         hwndFocus = GetParent(hwndFocus))
    {}

    // If not, set it.
    if (hwndFocus==NULL) {
         SetFocus(_hwnd);
    }

    // If this UIActivate came from below (i.e., our hosted DocObject), then we need to inform
    // our container.  We do this by calling IOleControlSite::OnFocus.  VB5 and Visual FoxPro
    // (at least) rely on this call being made for proper focus handling.
    //
    IUnknown_OnFocusOCS(_pcli, TRUE);
}

void CShellEmbedding::_OnUIDeactivate(void)
{
    //
    // We don't have any shared menu or tools to clean up.
    //

    if (_pipframe) {
        _pipframe->SetActiveObject(NULL, NULL);
    }

    if (_pipui) {
        _pipui->SetActiveObject(NULL, NULL);
    }

    if (_pipsite) {
        _pipsite->OnUIDeactivate(FALSE);
    }
    // If this UIDeactivate came from below (i.e., our hosted DocObject), then we need to inform
    // our container.  We do this by calling IOleControlSite::OnFocus.  VB5 and Visual FoxPro
    // (at least) rely on this call being made for proper focus handling.
    //
    IUnknown_OnFocusOCS(_pcli, FALSE);
}



HRESULT CShellEmbedding::EnumVerbs(
    IEnumOLEVERB **ppEnumOleVerb)
{
    IOOMSG(TEXT("EnumVerbs"));
    *ppEnumOleVerb = new CSVVerb(_pverbs);
    return *ppEnumOleVerb ? S_OK : E_OUTOFMEMORY;
}

HRESULT CShellEmbedding::Update( void)
{
    IOOMSG(TEXT("Update"));
    // Always up-to-date

    return S_OK;
}

HRESULT CShellEmbedding::IsUpToDate( void)
{
    IOOMSG(TEXT("IsUpToDate"));
    // Always up-to-date
    return S_OK;
}

HRESULT CShellEmbedding::GetUserClassID(CLSID *pClsid)
{
    IOOMSG(TEXT("GetUserClassID"));
    return GetClassID(pClsid);
}

HRESULT CShellEmbedding::GetUserType(DWORD dwFormOfType, LPOLESTR *pszUserType)
{
    return OleRegGetUserType(CLSIDOFOBJECT(this), dwFormOfType, pszUserType);
}

HRESULT CShellEmbedding::SetExtent(DWORD dwDrawAspect, SIZEL *psizel)
{
    // SetExtent sets the LOGICAL size of an object. SetObjectRects determins
    // the size of the object on the screen. If we cared about zooming, we'd
    // keep track of this and do some sort of scaling. But we don't.
    // We still need to remember this value so we return it on GetExtent.
    //
    _sizeHIM = *psizel;

    // HOWEVER, IE3 shipped a SetExtent that changed the physical size of the
    // object. For compat (AOL uses SetExtent to change the size), if we're the
    // old WebBrowser, continue to resize.
    //
    if (_pObjectInfo->pclsid == &CLSID_WebBrowser_V1)
    {
        RECT rc;
        HDC   hdc;
        int   mmOld;
        POINT pt;

        // Make sure a container doesn't do anything strange like
        // make us negative size
        //
        // APPCOMPAT: this breaks Trident because it sizes us negative
        // and we fail that sizing and they get confused...
        //
        //ASSERT(psizel->cx >= 0 && psizel->cy <= 0);
        //if (psizel->cx < 0 || psizel->cy > 0)
        //    return E_FAIL;
    
        // We only support DVASPECT_CONTENT
        if (dwDrawAspect != DVASPECT_CONTENT)
            return E_NOTIMPL;
    
        // Map this to a SetObjectRects call -- that way superclasses
        // only have to watch one function for size changes
        //

        int nScaleFactorX = 1, nScaleFactorY = 1;

        pt.x = psizel->cx;
        pt.y = psizel->cy;

        hdc = GetDC(NULL);

        if (hdc)
        {
            mmOld = SetMapMode(hdc, MM_HIMETRIC);

            if (!g_fRunningOnNT)  // if running on Win95
            {
                // Win95 doesn't like coordinates over 32K

                // SHRT_MIN and SHRT_MAX defined in $NT/public/sdk/inc/crt/limits.h

                while (pt.x > SHRT_MAX || pt.x < SHRT_MIN)
                {
                    pt.x >>= 1;
                    nScaleFactorX <<= 1;
                }
                while (pt.y > SHRT_MAX || pt.y < SHRT_MIN)
                {
                    pt.y >>= 1;
                    nScaleFactorY <<= 1;
                }
            }

            LPtoDP(hdc, &pt, 1);

            if (!g_fRunningOnNT)
            {
                pt.x *= nScaleFactorX;
                pt.y *= nScaleFactorY;
            }

            pt.y = -pt.y;
            SetMapMode(hdc, mmOld);
            ReleaseDC(NULL, hdc);
        }
    
        rc.left = _rcPos.left;
        rc.right = rc.left + pt.x;
        rc.top = _rcPos.top;
        rc.bottom = rc.top + pt.y;
    
        // Assume that using SetExtent adjusts both the pos and clip rects
        return SetObjectRects(&rc, NULL);
    }
    else
    {
        return S_OK;
    }
}

HRESULT CShellEmbedding::GetExtent(DWORD dwDrawAspect, SIZEL *psizel)
{
    *psizel = _sizeHIM;
    return S_OK;
}

HRESULT CShellEmbedding::Advise(IAdviseSink *pAdvSink, DWORD *pdwConnection)
{
    IOOMSG2(TEXT("Advise"), pAdvSink);
    HRESULT hr = E_INVALIDARG;

    if (!pdwConnection)
        return hr;

    *pdwConnection = NULL;              // set out params to NULL

    if (!_poah)
        hr = ::CreateOleAdviseHolder(&_poah);
    else
        hr = NOERROR;

    if( SUCCEEDED(hr) )
        hr = _poah->Advise(pAdvSink, pdwConnection);

    return(hr);
}

HRESULT CShellEmbedding::Unadvise(DWORD dwConnection)
{
    IOOMSG(TEXT("Unadvise"));
    HRESULT     hr;

    if (!_poah)
        return(OLE_E_NOCONNECTION);

    hr = _poah->Unadvise(dwConnection);

    return(hr);
}

HRESULT CShellEmbedding::EnumAdvise(IEnumSTATDATA **ppenumAdvise)
{
    IOOMSG(TEXT("EnumAdvise"));
    HRESULT     hr;

    if (!ppenumAdvise)
        return(E_INVALIDARG);

    if (!_poah)
    {
        *ppenumAdvise = NULL;
        hr = S_OK;
    }
    else
    {
        hr = _poah->EnumAdvise(ppenumAdvise);
    }

    return(hr);
}

HRESULT CShellEmbedding::GetMiscStatus(DWORD dwAspect, DWORD *pdwStatus)
{
    IOOMSG(TEXT("GetMiscStatus"));

    *pdwStatus = OLEMISCFLAGSOFCONTROL(this);

    return S_OK;
}

HRESULT CShellEmbedding::SetColorScheme(LOGPALETTE *pLogpal)
{
    IOOMSG(TEXT("GetColorScheme"));
    return S_OK;
}

//
//  Helper function to create an HDC from an OLE DVTARGETDEVICE structure.
//  Very useful for metafile drawing, where the Metafile DC will be the 
//  actual "draw to" dc, and the TargetDC, if present, will describe the ultimate output device.
//
HDC CShellEmbedding::_OleStdCreateDC(DVTARGETDEVICE *ptd)
{
    HDC        hdc = NULL;
    LPDEVNAMES lpDevNames = NULL;
    LPDEVMODEA lpDevMode = NULL;
    LPSTR      lpszDriverName = NULL;
    LPSTR      lpszDeviceName = NULL;
    LPSTR      lpszPortName = NULL;

    if (ptd)
    {
        lpDevNames = (LPDEVNAMES) ptd;
        if (ptd->tdExtDevmodeOffset)
        {
            lpDevMode = (LPDEVMODEA) ( (LPSTR) ptd + ptd->tdExtDevmodeOffset);
        }

        lpszDriverName = (LPSTR) lpDevNames + ptd->tdDriverNameOffset;
        lpszDeviceName = (LPSTR) lpDevNames + ptd->tdDeviceNameOffset;
        lpszPortName   = (LPSTR) lpDevNames + ptd->tdPortNameOffset;

        hdc = CreateDCA(lpszDriverName, lpszDeviceName, lpszPortName, lpDevMode);
    }
    return hdc;
}

// *** IDataObject ***
//
// WARNING:
//   It is well-known fact that Word and Excel (in Office95) does not call
//  IViewObject::Draw to draw embedding. Instead, they GetData(CF_METAFILEPICT).
//  If we don't offer it, Word will fail to embed it and Excel will draw
//  white rectangle when our object is deactivated. To be embedded correctly
//  on those apps, we must support CF_METAFILEPICT. (SatoNa)
//
HRESULT CShellEmbedding::GetData(FORMATETC *pformatetcIn, STGMEDIUM *pmedium)
{
    IDTMSG4(TEXT("GetData"), pformatetcIn->cfFormat, pformatetcIn->tymed);
    HRESULT hres = DV_E_FORMATETC;
    HDC hdcTargetDevice = NULL;
    HENHMETAFILE hemf = NULL;

    // If a Target device is specified in the FORMATETC structure, create a DC for it.
    // This gets passed to CreateEnhMetaFile and IViewObject::Draw.
    //
    if (pformatetcIn->ptd) 
    {
        hdcTargetDevice = _OleStdCreateDC(pformatetcIn->ptd);
        if (!hdcTargetDevice)
        {
            return E_FAIL;
        }
    }

    // Enhanced metafiles need special processing.
    //
    if (pformatetcIn->cfFormat == CF_ENHMETAFILE
        && (pformatetcIn->tymed & TYMED_ENHMF))
    {
        if (_hwnd)
        {
            RECTL rectBounds = { 0, 0, _sizeHIM.cx, _sizeHIM.cy };

            //
            // Call the "A" version since we're not passing in strings and
            // this needs to work on W95.
            HDC hdc = CreateEnhMetaFileA(hdcTargetDevice, NULL, (RECT*)&rectBounds, NULL);
            IDTMSG3(TEXT("_EnhMetafileFromWindow CreateEnhMetaFile returned"), hdc);
            if (hdc)
            {
                SetMapMode(hdc, MM_HIMETRIC);
                rectBounds.bottom = -rectBounds.bottom;

                Draw(DVASPECT_CONTENT, LINDEX_INTERNAL, NULL, pformatetcIn->ptd,
                     hdcTargetDevice, hdc, &rectBounds, NULL, NULL, 0);

                hemf = CloseEnhMetaFile(hdc);
                IDTMSG3(TEXT("_EnhMetafileFromWindow CloseEnhMetaFile returned"), hemf);
            }
        }

        pmedium->hEnhMetaFile = hemf;
        if (pmedium->hEnhMetaFile) 
        {
            pmedium->tymed = TYMED_ENHMF;
            pmedium->pUnkForRelease = NULL;
            hres = S_OK;
        } 
        else 
        {
            hres = E_FAIL;
        }
    }

    // Create a standard metafile
    //
    else if (pformatetcIn->cfFormat == CF_METAFILEPICT
        && (pformatetcIn->tymed & TYMED_MFPICT))
    {
        hres = E_OUTOFMEMORY;
        HGLOBAL hmem = GlobalAlloc(GPTR, sizeof(METAFILEPICT));
        if (hmem)
        {
            LPMETAFILEPICT pmf = (LPMETAFILEPICT) hmem;
            RECTL rectBounds = { 0, 0, _sizeHIM.cx, _sizeHIM.cy };

            HDC hdc = CreateMetaFile(NULL);
            if (hdc)
            {
                SetMapMode(hdc, MM_HIMETRIC);
                rectBounds.bottom = -rectBounds.bottom;

                SetWindowOrgEx(hdc, 0, 0, NULL);
                SetWindowExtEx(hdc, _sizeHIM.cx, _sizeHIM.cy, NULL);

                Draw(DVASPECT_CONTENT, LINDEX_INTERNAL, NULL, 
                    pformatetcIn->ptd, hdcTargetDevice,
                    hdc, &rectBounds, &rectBounds, NULL, 0);

                pmf->hMF = CloseMetaFile(hdc);

                if (pmf->hMF)
                {
                    pmf->mm = MM_ANISOTROPIC;
                    pmf->xExt = _sizeHIM.cx;
                    pmf->yExt = _sizeHIM.cy;
                    TraceMsg(TF_SHDCONTROL, "sdv TR ::GetData (%d,%d)-(%d,%d)",
                             _size.cx, _size.cy, _sizeHIM.cx, _sizeHIM.cy);

                    pmedium->tymed = TYMED_MFPICT;
                    pmedium->hMetaFilePict = hmem;
                    pmedium->pUnkForRelease = NULL;
                    hres = S_OK;
                }
            }

            if (FAILED(hres))
            {
                GlobalFree(hmem);
                hmem = NULL;
            }
        }
    }

    if (hdcTargetDevice)
        DeleteDC(hdcTargetDevice);

    return hres;
}

HRESULT CShellEmbedding::GetDataHere(FORMATETC *pformatetc, STGMEDIUM *pmedium)
{
    IDTMSG2(TEXT("GetDataHere"), pformatetc->cfFormat);
    return E_NOTIMPL;
}

HRESULT CShellEmbedding::QueryGetData(FORMATETC *pformatetc)
{
    IDTMSG2(TEXT("QueryGetData"), pformatetc->cfFormat);
    HRESULT hres = S_FALSE;
    if (pformatetc->cfFormat == CF_ENHMETAFILE
        && (pformatetc->tymed & TYMED_ENHMF))
    {
        hres = S_OK;
    }
    else if (pformatetc->cfFormat == CF_METAFILEPICT
        && (pformatetc->tymed & TYMED_MFPICT))
    {
        hres = S_OK;
    }

    return hres;
}

HRESULT CShellEmbedding::GetCanonicalFormatEtc(FORMATETC *pformatetcIn, FORMATETC *pformatetcOut)
{
    IDTMSG2(TEXT("GetCanonicalFormatEtc"), pformatetcIn->cfFormat);
    *pformatetcOut = *pformatetcIn;
    return DATA_S_SAMEFORMATETC;
}

HRESULT CShellEmbedding::SetData(FORMATETC *pformatetc, STGMEDIUM *pmedium, BOOL fRelease)
{
    IDTMSG(TEXT("SetData"));
    return E_NOTIMPL;
}

HRESULT CShellEmbedding::EnumFormatEtc(DWORD dwDirection, IEnumFORMATETC **ppenumFormatEtc)
{
    IDTMSG(TEXT("EnumFormatEtc"));
    return E_NOTIMPL;
}

HRESULT CShellEmbedding::DAdvise(FORMATETC *pformatetc, DWORD advf, IAdviseSink *pAdvSink, DWORD *pdwConnection)
{
    IDTMSG(TEXT("DAdvise"));
    HRESULT hr = E_INVALIDARG;

    if (!pdwConnection)
        return hr;

    *pdwConnection = NULL;              // set out params to NULL

    if (!_pdah)
        hr = ::CreateDataAdviseHolder(&_pdah);
    else
        hr = NOERROR;

    if( SUCCEEDED(hr) )
        hr = _pdah->Advise(this, pformatetc, advf, pAdvSink, pdwConnection);

    return(hr);
}

HRESULT CShellEmbedding::DUnadvise(DWORD dwConnection)
{
    IDTMSG(TEXT("DUnadvise"));
    HRESULT     hr;

    if (!_pdah)
        return(OLE_E_NOCONNECTION);

    hr = _pdah->Unadvise(dwConnection);

    return(hr);
}

HRESULT CShellEmbedding::EnumDAdvise(IEnumSTATDATA **ppenumAdvise)
{
    IDTMSG(TEXT("EnumDAdvise"));
    HRESULT     hr;

    if (!ppenumAdvise)
        return(E_INVALIDARG);

    if (!_pdah)
    {
        *ppenumAdvise = NULL;
        hr = S_OK;
    }
    else
    {
        hr = _pdah->EnumAdvise(ppenumAdvise);
    }

    return(hr);
}

// *** IOleWindow ***
HRESULT CShellEmbedding::GetWindow(HWND * lphwnd)
{
    *lphwnd = _hwnd;
    return S_OK;
}

HRESULT CShellEmbedding::ContextSensitiveHelp(BOOL fEnterMode)
{
    return S_OK;
}


// *** IOleInPlaceObject ***
HRESULT CShellEmbedding::InPlaceDeactivate(void)
{
    IIPMSG(TEXT("InPlaceDeactivate"));
    return _DoActivateChange(NULL, OC_DEACTIVE, FALSE);
}

HRESULT CShellEmbedding::UIDeactivate(void)
{
    IIPMSG(TEXT("UIDeactivate"));
    return _DoActivateChange(NULL, OC_INPLACEACTIVE, FALSE);
}

HRESULT CShellEmbedding::SetObjectRects(LPCRECT lprcPosRect, LPCRECT lprcClipRect)
{
    RECT  rcVisible;

    _rcPos = *lprcPosRect;

    if (lprcClipRect)
    {
        _rcClip = *lprcClipRect;
    }
    else
    {
        _rcClip = _rcPos;
    }

    IntersectRect(&rcVisible, &_rcPos, &_rcClip);    
    if (EqualRect(&rcVisible, &_rcPos))
    {
        if (_fUsingWindowRgn)
        {
            SetWindowRgn(_hwnd, NULL, TRUE);
            _fUsingWindowRgn = FALSE;
        }
    }
    else 
    {
        _fUsingWindowRgn = TRUE;
        OffsetRect(&rcVisible, -_rcPos.left, -_rcPos.top);
        SetWindowRgn(_hwnd,
                CreateRectRgnIndirect(&rcVisible),
                TRUE);
    }

    // We should consider this equivalent to a SetExtent as well...
    // But only for valid sizes (html viewer gives us invalid
    // sizes during it's reformat routine). Note: we still need
    // the SetWindowPos because we may move off the window.
    int cx = _rcPos.right - _rcPos.left;
    int cy = _rcPos.bottom - _rcPos.top;
    TraceMsg(TF_SHDCONTROL, "she SetObjectRects to x=%d y=%d cx=%d cy=%d (from cx=%d cy=%d)", _rcPos.left, _rcPos.top, cx, cy, _size.cx, _size.cy);
    if (cx >= 0 && cy >= 0)
    {
        _size.cx = cx;
        _size.cy = cy;
    }

    if (_hwnd)
    {
        SetWindowPos(_hwnd, NULL,
                     _rcPos.left, _rcPos.top,
                     _size.cx,
                     _size.cy,
                     SWP_NOZORDER | SWP_NOACTIVATE);
    }
    return S_OK;
}

HRESULT CShellEmbedding::ReactivateAndUndo(void)
{
    IIPMSG(TEXT("ReactivateAndUndo"));
    return INPLACE_E_NOTUNDOABLE;
}

// *** IOleInPlaceActiveObject ***
HRESULT CShellEmbedding::TranslateAccelerator(LPMSG lpmsg)
{
    extern BOOL IsVK_TABCycler(MSG * pMsg);
    HRESULT hr = S_FALSE;

    // IIAMSG(TEXT("TranslateAccelerator"));
    // We have no accelerators (other than TAB, which we must pass up
    // to IOCS::TA to move on to the next control if any)

    if (IsVK_TABCycler(lpmsg)) {
        // NOTE: grfMods?
        hr = IUnknown_TranslateAcceleratorOCS(_pcli, lpmsg, /*grfMods*/ 0);
    }

    return hr;
}

HRESULT CShellEmbedding::OnFrameWindowActivate(BOOL fActivate)
{
    IIAMSG(TEXT("OnFrameWindowActivate"));

    if (fActivate)
    {
        // our frame has been activated and we are the active object
        // make sure we have focus
        SetFocus(_hwnd);
    }

    return S_OK;
}

HRESULT CShellEmbedding::OnDocWindowActivate(BOOL fActivate)
{
    IIAMSG(TEXT("OnDocWindowActivate"));
    // We don't care
    return S_OK;
}

HRESULT CShellEmbedding::ResizeBorder(LPCRECT prcBorder,
                    IOleInPlaceUIWindow *pUIWindow, BOOL fFrameWindow)
{
    IIAMSG(TEXT("ResizeBorder"));
    // We have no toolbars.
    return S_OK;
}

HRESULT CShellEmbedding::EnableModeless(BOOL fEnable)
{
    IIAMSG(TEXT("EnableModeless"));
    // We have no dialogs.
    return S_OK;
}

void CShellEmbedding::_RegisterWindowClass(void)
{
    WNDCLASS wc = {0};
    wc.style         = CS_DBLCLKS;
    wc.lpfnWndProc   = s_WndProc ;
    //wc.cbClsExtra    = 0;
    wc.cbWndExtra    = SIZEOF(CShellEmbedding*) * 2;
    wc.hInstance     = g_hinst ;
    //wc.hIcon         = NULL ;
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW) ;
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    //wc.lpszMenuName  = NULL ;
    wc.lpszClassName = c_szShellEmbedding;

    SHRegisterClass(&wc);
}


LRESULT CShellEmbedding::v_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch(uMsg)
    {
    case WM_NCCREATE:
        DWORD dwExStyles;
        if ((dwExStyles = GetWindowLong(hwnd, GWL_EXSTYLE)) & RTL_MIRRORED_WINDOW)
        {
             SetWindowLong(hwnd, GWL_EXSTYLE, dwExStyles &~ RTL_MIRRORED_WINDOW);
        }
        goto DoDefault;

    case WM_SETFOCUS:
        if (_hwndChild)
            SetFocus(_hwndChild);
        // If this SETFOCUS came from TABbing onto the control, VB5 expects us to call its
        // IOleControlSite::OnFocus.  Then it will UIActivate us.
        //
        IUnknown_OnFocusOCS(_pcli, TRUE);
        break;

    case WM_KILLFOCUS:
        // If this KILLFOCUS came from TABbing off the control, VB5 expects us to call its
        // IOleControlSite::OnFocus.  Then it will UIDeactivate us.
        //
        IUnknown_OnFocusOCS(_pcli, FALSE);
        break;

    case WM_WINDOWPOSCHANGED:
        if (_hwndChild)
        {
            LPWINDOWPOS lpwp = (LPWINDOWPOS)lParam;

            if (!(lpwp->flags & SWP_NOSIZE))
            {
                SetWindowPos(_hwndChild, NULL,
                    0, 0, lpwp->cx, lpwp->cy,
                    SWP_NOZORDER|SWP_NOMOVE|SWP_NOACTIVATE|
                    (lpwp->flags&(SWP_NOREDRAW|SWP_NOCOPYBITS)));
            }
        }
        goto DoDefault;

#ifdef DEBUG
    // FEATURE: we'll never get this with ShellExplorer OC, but if we did,
    // we'd need to call _DoActivateChange(OC_UIACTIVE, FALSE);
    case WM_LBUTTONDOWN:
    case WM_RBUTTONDOWN:
        TraceMsg(TF_SHDCONTROL, "she ::v_WndProc(WM_xBUTTONDOWN) - we need to UIActivate");
        goto DoDefault;
#endif

    default:
DoDefault:
        return DefWindowProc(_hwnd, uMsg, wParam, lParam);
    }

    return 0L;
}

void CShellEmbedding::_ViewChange(DWORD dwAspect, LONG lindex)
{
    dwAspect &= _asp;

    if (dwAspect && _padv)
    {
        IAdviseSink *padv = _padv;
        IUnknown *punkRelease;

        if (_advf & ADVF_ONLYONCE)
        {
            _padv = NULL;
            punkRelease = padv;
        }
        else
            punkRelease = NULL;

        padv->OnViewChange(dwAspect, lindex);

        if (punkRelease)
            punkRelease->Release();
    }
}

void CShellEmbedding::_SendAdvise(UINT uCode)
{
    DWORD       dwAspect=DVASPECT_CONTENT | DVASPECT_THUMBNAIL;

    switch (uCode)
    {
    case OBJECTCODE_SAVED:
        if (NULL!=_poah)
            _poah->SendOnSave();
        break;

    case OBJECTCODE_CLOSED:
        if (NULL!=_poah)
            _poah->SendOnClose();
        break;

    case OBJECTCODE_RENAMED:
        //Call IOleAdviseHolder::SendOnRename (later)
        break;

    case OBJECTCODE_SAVEOBJECT:
        if (_fDirty && NULL!=_pcli)
            _pcli->SaveObject();

        _fDirty=FALSE;
        break;

    case OBJECTCODE_DATACHANGED:
        // _fDirty=TRUE;

        //No flags are necessary here.
        if (NULL!=_pdah)
            _pdah->SendOnDataChange(this, 0, 0);
        //
        // fall through
        //
    case OBJECTCODE_VIEWCHANGED:
        _ViewChange(dwAspect, -1);
        break;

    case OBJECTCODE_SHOWOBJECT:
        if (NULL!=_pcli)
            _pcli->ShowObject();
        break;
    }
}

HRESULT CSVVerb::QueryInterface(REFIID riid, LPVOID * ppvObj)
{
    if (IsEqualIID(riid, IID_IEnumOLEVERB) || IsEqualIID(riid, IID_IUnknown))
    {
        *ppvObj = SAFECAST(this, IEnumOLEVERB*);
    }
    else
    {
        *ppvObj = NULL;
        return E_NOINTERFACE;
    }
    _cRef++;
    return S_OK;
}

ULONG CSVVerb::AddRef()
{
    return ++_cRef;
}

ULONG CSVVerb::Release()
{
    if (--_cRef > 0) {
        return _cRef;
    }

    delete this;
    return 0;
}

HRESULT CSVVerb::Next(
    /* [in] */ ULONG celt,
    /* [out] */ LPOLEVERB rgelt,
    /* [out] */ ULONG *pceltFetched)
{
    HRESULT hres = S_FALSE;
    ULONG celtFetched = 0;


    // We need to enumerate the predefined verbs we support,
    // or some containers will never call them. This list
    // of verbs comes from our ::DoVerb function
    //
    static const OLEVERB rgVerbs[5] =
    {
        {OLEIVERB_PRIMARY, NULL, 0, 0},
        {OLEIVERB_INPLACEACTIVATE, NULL, 0, 0},
        {OLEIVERB_UIACTIVATE, NULL, 0, 0},
        {OLEIVERB_SHOW, NULL, 0, 0},
        {OLEIVERB_HIDE, NULL, 0, 0}
    };
    if (_iCur < ARRAYSIZE(rgVerbs))
    {
        IEVMSG(TEXT("Next"), celt, _iCur, TEXT("OLEIVERB_..."));

        *rgelt = rgVerbs[_iCur++];
        hres = S_OK;
    }
    else if (_pverbs)
    {
        int iCur = _iCur - ARRAYSIZE(rgVerbs);

        IEVMSG(TEXT("Next"), celt, _iCur, _pverbs[iCur].lpszVerbName);

        //
        // FEATURE: Should we do while(celt--)?
        //
        if (_pverbs[iCur].lpszVerbName)
        {
            *rgelt = _pverbs[_iCur++];
            WCHAR* pwszVerb = (WCHAR *)CoTaskMemAlloc(128 * sizeof(WCHAR));
            if (pwszVerb)
            {
                MLLoadStringW(PtrToUint(_pverbs[iCur].lpszVerbName), pwszVerb, 128);
                rgelt->lpszVerbName = pwszVerb;
                celtFetched++;
                hres = S_OK;
            }
            else
            {
                hres = E_OUTOFMEMORY;
            }
        }
    }

    if (pceltFetched) {
        *pceltFetched = celtFetched;
    }
    return hres;
}

HRESULT CSVVerb::Skip(ULONG celt)
{
    return S_OK;
}

HRESULT CSVVerb::Reset( void)
{
    _iCur = 0;
    return S_OK;
}

HRESULT CSVVerb::Clone(IEnumOLEVERB **ppenum)
{
    *ppenum = new CSVVerb(_pverbs);
    return *ppenum ? S_OK : E_OUTOFMEMORY;
}
