// Copyright (C) 1996-1997 Microsoft Corporation. All rights reserved.

#include "header.h"
#include "internet.h"
#include <stddef.h>

#ifndef _DEBUG
#undef THIS_FILE
static const char THIS_FILE[] = __FILE__;
#endif

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)(ULONG_PTR dwContinue),
	ULONG_PTR 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) {
		// Can't use CopyRect because OLE decided to use RECTL instead of RECT
		memcpy(&rc, &m_rcLocation, sizeof(m_rcLocation));
	} else {
		memcpy(&rc, prcBounds, sizeof(rc));

		// 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]
		//

		// 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

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);

	CallWindowProc((WNDPROC)SUBCLASSWNDPROCOFCONTROL(m_ObjectType), hwnd, (g_fSysWin95Shell) ? WM_PRINT : WM_PAINT, (WPARAM)hdc, (LPARAM)(g_fSysWin95Shell ? PRF_CHILDREN | PRF_CLIENT : 0));
	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.

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

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

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

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

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

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.

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

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

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

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 whether 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

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

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

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.

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);

	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 *)lcCalloc(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)
		lcFree(pDevModeA);
	return hdc;
}
