//=--------------------------------------------------------------------------=
// CtlView.Cpp
//=--------------------------------------------------------------------------=
// Copyright 1995-1996 Microsoft Corporation.  All Rights Reserved.
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF 
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO 
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 
// PARTICULAR PURPOSE.
//=--------------------------------------------------------------------------=
//
// implementation of the IViewObjectEx interface, which is a moderately
// non-trivial bunch of code.
//
#include "IPServer.H"

#include "CtrlObj.H"
#include "Globals.H"
#include "Util.H"

// for ASSERT and FAIL
//
SZTHISFILE

// local functions we're going to find useful
//
HDC _CreateOleDC(DVTARGETDEVICE *ptd);

//=--------------------------------------------------------------------------=
// COleControl::Draw    [IViewObject2]
//=--------------------------------------------------------------------------=
// Draws a representation of an object onto the specified device context. 
//
// Parameters:
//    DWORD                - [in] draw aspect
//    LONG                 - [in] part of object to draw [not relevant]
//    void *               - NULL
//    DVTARGETDEVICE *     - [in] specifies the target device
//    HDC                  - [in] information context for target device
//    HDC                  - [in] target device context
//    LPCRECTL             - [in] rectangle in which the object is drawn
//    LPCRECTL             - [in] window extent and origin for metafiles
//    BOOL (*)(DWORD)      - [in] callback for continuing or cancelling drawing
//    DWORD                - [in] parameter to pass to callback.
//
// Output:
//    HRESULT
//
// Notes:
//    - we support the following OCX 96 extensions
//        a. flicker free drawing [multi-pass drawing]
//        b. pvAspect != NULL for optimized DC handling
//        c. prcBounds == NULL for windowless inplace active objects
//
STDMETHODIMP COleControl::Draw
(
    DWORD            dwDrawAspect,
    LONG             lIndex,
    void            *pvAspect,
    DVTARGETDEVICE  *ptd,
    HDC              hicTargetDevice,
    HDC              hdcDraw,
    LPCRECTL         prcBounds,
    LPCRECTL         prcWBounds,
    BOOL (__stdcall *pfnContinue)(DWORD dwContinue),
    DWORD            dwContinue
)
{
    HRESULT hr;
    RECTL rc;
    POINT pVp, pW;
    BOOL  fOptimize = FALSE;
    int iMode;
    BYTE fMetafile = FALSE;
    BYTE fDeleteDC = FALSE;

    // support the aspects required for multi-pass drawing
    //
    switch (dwDrawAspect) {
        case DVASPECT_CONTENT:
        case DVASPECT_OPAQUE:
        case DVASPECT_TRANSPARENT:
            break;
        default:
            return DV_E_DVASPECT;
    }

    // first, have to do a little bit to support printing.
    //
    if (GetDeviceCaps(hdcDraw, TECHNOLOGY) == DT_METAFILE) {

        // We are dealing with a metafile.
        //
        fMetafile = TRUE;

        // If attributes DC is NULL, create one, based on ptd.
        //
        if (!hicTargetDevice) {

            // Does _CreateOleDC have to return an hDC
            // or can it be flagged to return an hIC 
            // for this particular case?
            //
            hicTargetDevice = _CreateOleDC(ptd);
            fDeleteDC = TRUE;
        }
    }

    // check to see if we have any flags passed in the pvAspect parameter.
    //
    if (pvAspect && ((DVASPECTINFO *)pvAspect)->cb == sizeof(DVASPECTINFO))
        fOptimize = (((DVASPECTINFO *)pvAspect)->dwFlags & DVASPECTINFOFLAG_CANOPTIMIZE) ? TRUE : FALSE;

    // if we are windowless, then we just pass this on to the end control code.
    //
    if (m_fInPlaceActive) {

        // give them a rectangle with which to draw
        //
        //ASSERT(!m_fInPlaceActive || !prcBounds, "Inplace active and somebody passed in prcBounds!!!");
        if (prcBounds)
		memcpy(&rc, prcBounds, sizeof(rc));
	else
		memcpy(&rc, &m_rcLocation, sizeof(rc));
    } else {

        // first -- convert the DC back to MM_TEXT mapping mode so that the
        // window proc and OnDraw can share the same painting code.  save
        // some information on it, so we can restore it later [without using
        // a SaveDC/RestoreDC]
        //
        rc = *prcBounds;

        // Don't do anything to hdcDraw if it's a metafile.
        // The control's Draw method must make the appropriate
        // accomodations for drawing to a metafile
        //
        if (!fMetafile) {
            LPtoDP(hdcDraw, (POINT *)&rc, 2);
            SetViewportOrgEx(hdcDraw, 0, 0, &pVp);
            SetWindowOrgEx(hdcDraw, 0, 0, &pW);
            iMode = SetMapMode(hdcDraw, MM_TEXT);
        }
    }

    // prcWBounds is NULL and not used if we are not dealing with a metafile.
    // For metafiles, we pass on rc as *prcBounds, we should also include
    // prcWBounds
    //
    hr = OnDraw(dwDrawAspect, hdcDraw, &rc, prcWBounds, hicTargetDevice, fOptimize);

    // clean up the DC when we're done with it, if appropriate.
    //
    if (!m_fInPlaceActive) {
        SetViewportOrgEx(hdcDraw, pVp.x, pVp.y, NULL);
        SetWindowOrgEx(hdcDraw, pW.x, pW.y, NULL);
        SetMapMode(hdcDraw, iMode);
    }

    // if we created a dc, blow it away now
    //
    if (fDeleteDC) DeleteDC(hicTargetDevice);
    return hr;
}

//=--------------------------------------------------------------------------=
// COleControl::DoSuperClassPaint
//=--------------------------------------------------------------------------=
// design time painting of a subclassed control.
//
// Parameters:
//    HDC                - [in]  dc to work with
//    LPCRECTL           - [in]  rectangle to paint to.  should be in pixels
//
// Output:
//    HRESULT
//
// Notes:
//
HRESULT COleControl::DoSuperClassPaint
(
    HDC      hdc,
    LPCRECTL prcBounds
)
{
    HWND hwnd;
    RECT rcClient;
    int  iMapMode;
    POINT ptWOrg, ptVOrg;
    SIZE  sWOrg, sVOrg;

    // make sure we have a window.
    //
    hwnd = CreateInPlaceWindow(0,0, FALSE);
    if (!hwnd)
        return E_FAIL;

    GetClientRect(hwnd, &rcClient);

    // set up the DC for painting.  this code largely taken from the MFC CDK
    // DoSuperClassPaint() fn.  doesn't always get things like command
    // buttons quite right ...
    //
    // NOTE: there is a windows 95 problem in which the font instance manager
    // will leak a bunch of bytes in the global GDI pool whenever you 
    // change your extents and have an active font.  this code gets around
    // this for on-screen cases, but not for printing [which shouldn't be
    // too serious, because you're not often changing your control size and
    // printing rapidly in succession]
    //
    if ((rcClient.right - rcClient.left != prcBounds->right - prcBounds->left)
        && (rcClient.bottom - rcClient.top != prcBounds->bottom - prcBounds->top)) {

        iMapMode = SetMapMode(hdc, MM_ANISOTROPIC);
        SetWindowExtEx(hdc, rcClient.right, rcClient.bottom, &sWOrg);
        SetViewportExtEx(hdc, prcBounds->right - prcBounds->left, prcBounds->bottom - prcBounds->top, &sVOrg);
    }

    SetWindowOrgEx(hdc, 0, 0, &ptWOrg);
    SetViewportOrgEx(hdc, prcBounds->left, prcBounds->top, &ptVOrg);

#if STRICT
    CallWindowProc((WNDPROC)SUBCLASSWNDPROCOFCONTROL(m_ObjectType), hwnd, (g_fSysWin95Shell) ? WM_PRINT : WM_PAINT, (WPARAM)hdc, (LPARAM)(g_fSysWin95Shell ? PRF_CHILDREN | PRF_CLIENT : 0));
#else
    CallWindowProc((FARPROC)SUBCLASSWNDPROCOFCONTROL(m_ObjectType), hwnd, (g_fSysWin95Shell) ? WM_PRINT : WM_PAINT, (WPARAM)hdc, (LPARAM)(g_fSysWin95Shell ? PRF_CHILDREN | PRF_CLIENT : 0));
#endif // STRICT

    return S_OK;
}


//=--------------------------------------------------------------------------=
// COleControl::GetColorSet    [IViewObject2]
//=--------------------------------------------------------------------------=
// Returns the logical palette that the control will use for drawing in its
// IViewObject::Draw method with the corresponding parameters.
//
// Parameters:
//    DWORD                - [in]  how the object is to be represented
//    LONG                 - [in]  part of the object to draw [not relevant]
//    void *               - NULL
//    DVTARGETDEVICE *     - [in]  specifies the target device
//    HDC                  - [in]  information context for the target device
//    LOGPALETTE **        - [out] where to put palette
//
// Output:
//    S_OK                 - Control has a palette, and returned it through the out param.
//    S_FALSE              - Control does not currently have a palette.
//    E_NOTIMPL            - Control will never have a palette so optimize handling of this control.
//
// Notes:
//
STDMETHODIMP COleControl::GetColorSet
(
    DWORD            dwDrawAspect,
    LONG             lindex,
    void            *IgnoreMe,
    DVTARGETDEVICE  *ptd,
    HDC              hicTargetDevice,
    LOGPALETTE     **ppColorSet
)
{
    if (dwDrawAspect != DVASPECT_CONTENT)
        return DV_E_DVASPECT;

    *ppColorSet = NULL;
    return (OnGetPalette(hicTargetDevice, ppColorSet)) ? ((*ppColorSet) ? S_OK : S_FALSE) : E_NOTIMPL;
}

//=--------------------------------------------------------------------------=
// COleControl::Freeze    [IViewObject2]
//=--------------------------------------------------------------------------=
// Freezes a certain aspect of the object's presentation so that it does not
// change until the IViewObject::Unfreeze method is called.
//
// Parameters:
//    DWORD            - [in] aspect
//    LONG             - [in] part of object to draw
//    void *           - NULL
//    DWORD *          - [out] for Unfreeze
//
// Output:
//    HRESULT
//
// Notes:
//
STDMETHODIMP COleControl::Freeze
(
    DWORD   dwDrawAspect,
    LONG    lIndex,
    void   *IgnoreMe,
    DWORD  *pdwFreeze
)
{
    return E_NOTIMPL;
}

//=--------------------------------------------------------------------------=
// COleControl::Unfreeze    [IVewObject2]
//=--------------------------------------------------------------------------=
// Releases a previously frozen drawing. The most common use of this method
// is for banded printing.
//
// Parameters:
//    DWORD        - [in] cookie from freeze
//
// Output:
//    HRESULT
//
// Notes:
//
STDMETHODIMP COleControl::Unfreeze
(
    DWORD dwFreeze
)
{
    return E_NOTIMPL;
}

//=--------------------------------------------------------------------------=
// COleControl::SetAdvise    [IViewObject2]
//=--------------------------------------------------------------------------=
// Sets up a connection between the control and an advise sink so that the
// advise sink can be notified about changes in the control's view.
//
// Parameters:
//    DWORD            - [in] aspect
//    DWORD            - [in] info about the sink
//    IAdviseSink *    - [in] the sink
//
// Output:
//    HRESULT
//
// Notes:
//
STDMETHODIMP COleControl::SetAdvise
(
    DWORD        dwAspects,
    DWORD        dwAdviseFlags,
    IAdviseSink *pAdviseSink
)
{
    // if it's not a content aspect, we don't support it.
    //
    if (!(dwAspects & DVASPECT_CONTENT)) {
        return DV_E_DVASPECT;
    }

    // set up some flags  [we gotta stash for GetAdvise ...]
    //
    m_fViewAdvisePrimeFirst = (dwAdviseFlags & ADVF_PRIMEFIRST) ? TRUE : FALSE;
    m_fViewAdviseOnlyOnce = (dwAdviseFlags & ADVF_ONLYONCE) ? TRUE : FALSE;

    RELEASE_OBJECT(m_pViewAdviseSink);
    m_pViewAdviseSink = pAdviseSink;
    ADDREF_OBJECT(m_pViewAdviseSink);

    // prime them if they want it [we need to store this so they can get flags later]
    //
    if (m_fViewAdvisePrimeFirst)
        ViewChanged();

    return S_OK;
}

//=--------------------------------------------------------------------------=
// COleControl::GetAdvise    [IViewObject2]
//=--------------------------------------------------------------------------=
// Retrieves the existing advisory connection on the control if there is one.
// This method simply returns the parameters used in the most recent call to
// the IViewObject::SetAdvise method.
//
// Parameters:
//    DWORD *            - [out]  aspects
//    DWORD *            - [out]  advise flags
//    IAdviseSink **     - [out]  the sink
//
// Output:
//    HRESULT
//
// Notes;
//
STDMETHODIMP COleControl::GetAdvise
(
    DWORD        *pdwAspects,
    DWORD        *pdwAdviseFlags,
    IAdviseSink **ppAdviseSink
)
{
    // if they want it, give it to them
    //
    if (pdwAspects)
        *pdwAspects = DVASPECT_CONTENT;

    if (pdwAdviseFlags) {
        *pdwAdviseFlags = 0;
        if (m_fViewAdviseOnlyOnce) *pdwAdviseFlags |= ADVF_ONLYONCE;
        if (m_fViewAdvisePrimeFirst) *pdwAdviseFlags |= ADVF_PRIMEFIRST;
    }

    if (ppAdviseSink) {
        *ppAdviseSink = m_pViewAdviseSink;
        ADDREF_OBJECT(*ppAdviseSink);
    }

    return S_OK;
}

//=--------------------------------------------------------------------------=
// COleControl::GetExtent    [IViewObject2]
//=--------------------------------------------------------------------------=
// Returns the size that the control will be drawn on the
// specified target device.
//
// Parameters:
//    DWORD            - [in] draw aspect
//    LONG             - [in] part of object to draw
//    DVTARGETDEVICE * - [in] information about target device
//    LPSIZEL          - [out] where to put the size
//
// Output:
//    HRESULT
//
// Notes:
//
STDMETHODIMP COleControl::GetExtent
(
    DWORD           dwDrawAspect,
    LONG            lindex,
    DVTARGETDEVICE *ptd,
    LPSIZEL         psizel
)
{
    // we already have an implementation of this [from IOleObject]
    //
    return GetExtent(dwDrawAspect, psizel);
}


//=--------------------------------------------------------------------------=
// COleControl::OnGetPalette    [overridable]
//=--------------------------------------------------------------------------=
// called when the host wants palette information.  ideally, people should use
// this sparingly and carefully.
//
// Parameters:
//    HDC            - [in]  HIC for the target device
//    LOGPALETTE **  - [out] where to put the palette
//
// Output:
//    BOOL           - TRUE means we processed it, false means nope.
//
// Notes:
//
BOOL COleControl::OnGetPalette
(
    HDC          hicTargetDevice,
    LOGPALETTE **ppColorSet
)
{
    return FALSE;
}


//=--------------------------------------------------------------------------=
// COleControl::GetRect    [IViewObjectEx]
//=--------------------------------------------------------------------------=
// returns a rectnagle describing a given drawing aspect
//
// Parameters:
//    DWORD             - [in]  aspect
//    LPRECTL           - [out] region rectangle
//
// Output:
//    HRESULT
//
// Notes:
//
STDMETHODIMP COleControl::GetRect
(
    DWORD    dvAspect,
    LPRECTL  prcRect
)
{
    RECTL rc;
    BOOL  f;

    // call the user routine and let them return the size
    //
    f = OnGetRect(dvAspect, &rc);
    if (!f) return DV_E_DVASPECT;

    // transform these dudes.
    //
    PixelToHiMetric((LPSIZEL)&rc, (LPSIZEL)prcRect);
    PixelToHiMetric((LPSIZEL)(LPBYTE)&rc + sizeof(SIZEL), (LPSIZEL)((LPBYTE)prcRect + sizeof(SIZEL)));

    return S_OK;
}

//=--------------------------------------------------------------------------=
// COleControl::GetViewStatus    [IViewObjectEx]
//=--------------------------------------------------------------------------=
// returns information about the opactiy of the object and what drawing
// aspects are supported
//
// Parameters:
//    DWORD *            - [out] the status
//
/// Output:
//    HRESULT
//
// Notes:
//
STDMETHODIMP COleControl::GetViewStatus
(
    DWORD *pdwStatus
)
{
    // depending on the flag in the CONTROLOBJECTINFO structure, indicate our
    // transparency vs opacity.
    // OVERRIDE:  controls that wish to support multi-pass drawing should
    // override this routine and return, in addition to the flags indication
    // opacity, flags indicating what sort of drawing aspects they support.
    //
    *pdwStatus = FCONTROLISOPAQUE(m_ObjectType) ? VIEWSTATUS_OPAQUE : 0;
    return S_OK;
}

//=--------------------------------------------------------------------------=
// COleControl::QueryHitPoint    [IViewObjectEx]
//=--------------------------------------------------------------------------=
// indicates whether a point is within a given aspect of an object.
//
// Parameters:
//    DWORD                - [in]  aspect
//    LPCRECT              - [in]  Bounds rectangle
//    POINT                - [in]  hit location client coordinates
//    LONG                 - [in]  what the container considers close
//    DWORD *              - [out] info about the hit
//
// Output:
//    HRESULT
//
// Notes:
//
STDMETHODIMP COleControl::QueryHitPoint
(
    DWORD    dvAspect,
    LPCRECT  prcBounds,
    POINT    ptLocation,
    LONG     lCloseHint,
    DWORD   *pdwHitResult
)
{
    // OVERRIDE: override me if you want to provide additional [non-opaque]
    // functionality
    //
    if (dvAspect != DVASPECT_CONTENT)
        return DV_E_DVASPECT;

    *pdwHitResult = PtInRect(prcBounds, ptLocation) ? HITRESULT_HIT : HITRESULT_OUTSIDE;
    return S_OK;
}

//=--------------------------------------------------------------------------=
// COleControl::QueryHitRect    [IViewObjectEx]
//=--------------------------------------------------------------------------=
// indicates wheter any point in a rectangle is within a given drawing aspect
// of an object.
//
// Parameters:
//    DWORD            - [in]  aspect
//    LPCRECT          - [in]  bounds
//    LPCRECT          - [in]  location
//    LONG             - [in]  what host considers close
//    DWORD *          - [out] hit result
//
// Output:
//    HRESULT
//
// Notes:
//
STDMETHODIMP COleControl::QueryHitRect
(
    DWORD     dvAspect,
    LPCRECT   prcBounds,
    LPCRECT   prcLocation,
    LONG      lCloseHint,
    DWORD    *pdwHitResult
)
{
    RECT rc;

    // OVERRIDE: override this for additional behaviour
    //
    if (dvAspect != DVASPECT_CONTENT)
        return DV_E_DVASPECT;

    *pdwHitResult = IntersectRect(&rc, prcBounds, prcLocation) ? HITRESULT_HIT : HITRESULT_OUTSIDE;
    return S_OK;
}

//=--------------------------------------------------------------------------=
// COleControl::GetNaturalExtent    [IViewObjectEx]
//=--------------------------------------------------------------------------=
// supports two types of control sizing, content and integral.
//
// Parameters:
//    DWORD            - [in]  aspect
//    LONG             - [in]  index
//    DVTARGETDEVICE * - [in]  target device information
//    HDC              - [in]  HIC
//    DVEXTENTINFO *   - [in]  sizing data
//    LPSIZEL          - [out] sizing data retunred by control
//
// Output:
//    HRESULT
//
// Notes:
//
STDMETHODIMP COleControl::GetNaturalExtent
(
    DWORD           dvAspect,
    LONG            lIndex,
    DVTARGETDEVICE *ptd,
    HDC             hicTargetDevice,
    DVEXTENTINFO   *pExtentInfo,
    LPSIZEL         pSizel
)
{
    return E_NOTIMPL;
}

//=--------------------------------------------------------------------------=
// COleControl::OnGetRect    [overridable
//=--------------------------------------------------------------------------=
// returns our rectangle
//
// Parameters:
//    DWORD              - [in]  aspect they want the rect for
//    RECTL *            - [out] the rectangle that matches this aspect
//
// Output:
//    BOOL               - false means we don't like the aspect
//
// Notes:
//
BOOL COleControl::OnGetRect
(
    DWORD   dvAspect,
    RECTL  *pRect
)
{
    // by default, we only support content drawing.
    //
    if (dvAspect != DVASPECT_CONTENT)
        return FALSE;

    // just give them our bounding rectangle
    //
    *((LPRECT)pRect) = m_rcLocation;
    return TRUE;
}

//=--------------------------------------------------------------------------=
// _CreateOleDC
//=--------------------------------------------------------------------------=
// creates an HDC given a DVTARGETDEVICE structure.
//
// Parameters:
//    DVTARGETDEVICE *              - [in] duh.
//
// Output:
//    HDC
//
// Notes:
//
HDC _CreateOleDC
(
    DVTARGETDEVICE *ptd
)
{
    LPDEVMODEW   pDevModeW;
    DEVMODEA     DevModeA, *pDevModeA;
    LPOLESTR     lpwszDriverName;
    LPOLESTR     lpwszDeviceName;
    LPOLESTR     lpwszPortName;
    HDC          hdc;

    // return screen DC for NULL target device
    //
    if (!ptd)
        return CreateDC("DISPLAY", NULL, NULL, NULL);

    if (ptd->tdExtDevmodeOffset == 0)
        pDevModeW = NULL;
    else
        pDevModeW = (LPDEVMODEW)((LPSTR)ptd + ptd->tdExtDevmodeOffset);

    lpwszDriverName = (LPOLESTR)((BYTE*)ptd + ptd->tdDriverNameOffset);
    lpwszDeviceName = (LPOLESTR)((BYTE*)ptd + ptd->tdDeviceNameOffset);
    lpwszPortName   = (LPOLESTR)((BYTE*)ptd + ptd->tdPortNameOffset);

    MAKE_ANSIPTR_FROMWIDE(pszDriverName, lpwszDriverName);
    MAKE_ANSIPTR_FROMWIDE(pszDeviceName, lpwszDeviceName);
    MAKE_ANSIPTR_FROMWIDE(pszPortName,   lpwszPortName);

    // wow, this sucks.
    //
    if (pDevModeW) {
        WideCharToMultiByte(CP_ACP, 0, pDevModeW->dmDeviceName, -1, (LPSTR)DevModeA.dmDeviceName, CCHDEVICENAME, NULL, NULL);
	memcpy(&DevModeA.dmSpecVersion, &pDevModeW->dmSpecVersion,
		offsetof(DEVMODEA, dmFormName) - offsetof(DEVMODEA, dmSpecVersion));
        WideCharToMultiByte(CP_ACP, 0, pDevModeW->dmFormName, -1, (LPSTR)DevModeA.dmFormName, CCHFORMNAME, NULL, NULL);
	memcpy(&DevModeA.dmLogPixels, &pDevModeW->dmLogPixels, sizeof(DEVMODEA) - offsetof(DEVMODEA, dmLogPixels));
        if (pDevModeW->dmDriverExtra) {
            pDevModeA = (DEVMODEA *)HeapAlloc(g_hHeap, 0, sizeof(DEVMODEA) + pDevModeW->dmDriverExtra);
            if (!pDevModeA) return NULL;
            memcpy(pDevModeA, &DevModeA, sizeof(DEVMODEA));
            memcpy(pDevModeA + 1, pDevModeW + 1, pDevModeW->dmDriverExtra);
        } else
            pDevModeA = &DevModeA;

	DevModeA.dmSize = sizeof(DEVMODEA);
    } else
        pDevModeA = NULL;

    hdc = CreateDC(pszDriverName, pszDeviceName, pszPortName, pDevModeA);
    if (pDevModeA != &DevModeA) HeapFree(g_hHeap, 0, pDevModeA);
    return hdc;
}
