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

//
// implementation of persistence interfaces for COleControl.
//
#include "header.h"
#include "internet.h"

#include "CtlHelp.H"

// this is the name of the stream we'll save our ole controls to.
//
const WCHAR wszCtlSaveStream [] = L"CONTROLSAVESTREAM";

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

//=--------------------------------------------------------------------------=
// to help with out stream save implementation ...
//
#define STREAMHDR_SIGNATURE 0x12344321	// Signature to identify our format (avoid crashes!)
#define IPROP_END 0xFF					// Marker at end of property list
#define MAXAUTOBUF 3800 				// Best if < 1 page.

typedef struct tagSTREAMHDR {

	DWORD  dwSignature; 	// Signature.
	size_t cbWritten;		// Number of bytes written

} STREAMHDR;

//=--------------------------------------------------------------------------=
// COleControl persistence interfaces
//=--------------------------------------------------------------------------=


//=--------------------------------------------------------------------------=
// COleControl::Load	[IPersistPropertyBag]
//=--------------------------------------------------------------------------=
// IPersistPropertyBag.  we've got a property bag, so let's load our properties
// from it.
//
// Parameters:
//	  IPropertyBag *	  - [in] pbag from which to read props.
//	  IErrorLog *		  - [in] error log to write to
//
// Output:
//	  HRESULT
//
// Notes:
//
STDMETHODIMP COleControl::Load
(
	IPropertyBag *pPropertyBag,
	IErrorLog	 *pErrorLog
)
{
	HRESULT hr;

	// load in our standard state first.  nothing serious here ... currently,
	// we've just got two properties, for cx and cy.
	//
	hr = LoadStandardState(pPropertyBag, pErrorLog);
	RETURN_ON_FAILURE(hr);

	// now call the user text load function, and get them to load in whatever
	// they're interested in.
	//
	hr = LoadTextState(pPropertyBag, pErrorLog);

	return hr;
}

//=--------------------------------------------------------------------------=
// COleControl::Save	[IPersistPropertyBag]
//=--------------------------------------------------------------------------=
// given a property bag, save out all the relevant state information.
//
// Parameters:
//	  IPropertyBag *		- [in] property to write to
//	  BOOL					- [in] do we clear the dirty bit?
//	  BOOL					- [in] do we write out default values anyhoo?
//
// Output:
//	  HRESULT
//
// Notes:
//
STDMETHODIMP COleControl::Save
(
	IPropertyBag *pPropertyBag,
	BOOL		  fClearDirty,
	BOOL		  fWriteDefault
)
{
	HRESULT hr;

	// save out standard state information
	//
	hr = SaveStandardState(pPropertyBag);
	RETURN_ON_FAILURE(hr);

	// now call the user function and get them to save out
	// all of their properties.

	hr = SaveTextState(pPropertyBag, fWriteDefault);
	RETURN_ON_FAILURE(hr);

	// now clear the dirty flag and send out notification that we're
	// done.

	if (fClearDirty)
		m_fDirty = FALSE;

	if (m_pOleAdviseHolder)
		m_pOleAdviseHolder->SendOnSave();

	return S_OK;
}

//=--------------------------------------------------------------------------=
// COleControl::GetClassID	  [IPersistStreamInit]
//=--------------------------------------------------------------------------=
// returns the classid of this mamma
//
// Parameters:
//	  CLSID *		  - [out] where to put the clsid
//
// Output:
//	  HRESULT
//
// Notes:
//
STDMETHODIMP COleControl::GetClassID
(
	CLSID *pclsid
)
{
	CHECK_POINTER(pclsid);

	// copy the thing over
	//
	*pclsid = CLSIDOFOBJECT(m_ObjectType);
	return S_OK;
}


//=--------------------------------------------------------------------------=
// COleControl::IsDirty    [IPersistStreamInit]
//=--------------------------------------------------------------------------=
// asks if we're dirty or not.  duh.
//
// Output:
//	  HRESULT		 - S_OK: dirty, S_FALSE: not dirty
//
// Notes:
//
STDMETHODIMP COleControl::IsDirty
(
	void
)
{
	return (m_fDirty) ? S_OK : S_FALSE;
}

//=--------------------------------------------------------------------------=
// COleControl::InitNew    [IPersistStreamInit]
//=--------------------------------------------------------------------------=
// causes the control to intialize itself with a new bunch of state information
//
// Output:
//	  HRESULT
//
// Notes:
//
STDMETHODIMP COleControl::InitNew
(
	void
)
{
	BOOL f;

	// call the overridable function to do this work
	//
	f = InitializeNewState();
	return (f) ? S_OK : E_FAIL;
}

//=--------------------------------------------------------------------------=
// COleControl::GetSizeMax	  [IPersistStreamInit]
//=--------------------------------------------------------------------------=
//
// Parameters:
//	  ULARGE_INTEGER *	  - [out]
//
// Output:
//	  HRESULT
//
// Notes:
//
STDMETHODIMP COleControl::GetSizeMax
(
	ULARGE_INTEGER *pulMaxSize
)
{
	return E_NOTIMPL;
}

//=--------------------------------------------------------------------------=
// COleControl::Load	[IPersistStreamInit]
//=--------------------------------------------------------------------------=
// load from an IStream
//
// Parameters:
//	  IStream *    - [in] stream from which to load
//
// Output:
//	  HRESULT
//
// Notes:
//

STDMETHODIMP COleControl::Load(IStream *pStream)
{
	HRESULT hr;

	// first thing to do is read in standard properties the user don't
	// persist themselves.

	hr = LoadStandardState(pStream);
	RETURN_ON_FAILURE(hr);

	// load in the user properties.  this method is one they -have- to implement
	// themselves.

	hr = LoadBinaryState(pStream);

	return hr;
}

//=--------------------------------------------------------------------------=
// COleControl::Save	[IPersistStreamInit]
//=--------------------------------------------------------------------------=
// saves out our state using streams
//
// Parameters:
//	  IStream * 	   - [in]
//	  BOOL			   - [in] clear dirty bit?
//
// Output:
//	  HRESULT
//
// Notes:
//
STDMETHODIMP COleControl::Save
(
	IStream *pStream,
	BOOL	 fClearDirty
)
{
	HRESULT hr;

	// use our helper routine that we share with the IStorage persistence
	// code.
	//
	hr = m_SaveToStream(pStream);
	RETURN_ON_FAILURE(hr);

	// clear out dirty flag [if appropriate] and notify that we're done
	// with save.
	//
	if (fClearDirty)
		m_fDirty = FALSE;
	if (m_pOleAdviseHolder)
		m_pOleAdviseHolder->SendOnSave();

	return S_OK;
}

//=--------------------------------------------------------------------------=
// COleControl::InitNew    [IPersistStorage]
//=--------------------------------------------------------------------------=
// ipersiststorage version of this.  fweee
//
// Parameters:
//	  IStorage *	- [in] we don't use this
//
// Output:
//	  HRESULT
//
// Notes:
//
STDMETHODIMP COleControl::InitNew
(
	IStorage *pStorage
)
{
	// we already have an implementation of this [for IPersistStreamInit]
	//
	return InitNew();
}

//=--------------------------------------------------------------------------=
// COleControl::Load	[IPersistStorage]
//=--------------------------------------------------------------------------=
// Ipersiststorage version of this
//
// Parameters:
//	  IStorage *	- [in] DUH.
//
// Output:
//	  HRESULT
//
// Notes:
//
STDMETHODIMP COleControl::Load(IStorage *pStorage)
{
	IStream *pStream;
	HRESULT  hr;

	// we're going to use IPersistStream::Load from the CONTENTS stream.
	//
	hr = pStorage->OpenStream(wszCtlSaveStream, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
	RETURN_ON_FAILURE(hr);

	// IPersistStreamInit::Load
	//
	hr = Load(pStream);
	pStream->Release();
	return hr;
}

//=--------------------------------------------------------------------------=
// COleControl::Save	[IPersistStorage]
//=--------------------------------------------------------------------------=
// save into the contents stream of the given storage object.
//
// Parameters:
//	  IStorage *		- [in] 10 points if you figure it out
//	  BOOL				- [in] is the storage the same as the load storage?
//
// Output:
//	  HRESULT
//
// Notes:
//
STDMETHODIMP COleControl::Save
(
	IStorage *pStorage,
	BOOL	  fSameAsLoad
)
{
	IStream *pStream;
	HRESULT  hr;

	// we're just going to save out to the CONTENTES stream.
	//
	hr = pStorage->CreateStream(wszCtlSaveStream, STGM_WRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE,
								0, 0, &pStream);
	RETURN_ON_FAILURE(hr);

	// use our helper routine.
	//
	hr = m_SaveToStream(pStream);
	m_fSaveSucceeded = (FAILED(hr)) ? FALSE : TRUE;
	pStream->Release();
	return hr;
}

//=--------------------------------------------------------------------------=
// COleControl::SaveCompleted	 [IPersistStorage]
//=--------------------------------------------------------------------------=
// lets us clear out our flags.
//
// Parameters:
//	  IStorage *	- ignored
//
// Output:
//	  HRESULT
//
// Notes:
//
STDMETHODIMP COleControl::SaveCompleted
(
	IStorage *pStorageNew
)
{
	// if our save succeeded, then we can do our post save work.
	//
	if (m_fSaveSucceeded) {
		m_fDirty = FALSE;
		if (m_pOleAdviseHolder)
			m_pOleAdviseHolder->SendOnSave();
	}

	return S_OK;
}

//=--------------------------------------------------------------------------=
// COleControl::HandsOffStorage    [IPersistStorage]
//=--------------------------------------------------------------------------=
// not interesting
//
// Output:
//	  S_OK
//
// Notes:
//
STDMETHODIMP COleControl::HandsOffStorage
(
	void
)
{
	// we don't ever hold on to  a storage pointer, so this is remarkably
	// uninteresting to us.
	//
	return S_OK;
}

//=--------------------------------------------------------------------------=
// COleControl::m_SaveToStream	  [helper: IPersistStreamInit/IPersistStorage]
//=--------------------------------------------------------------------------=
// save ourselves to a stream
//
// Parameters:
//	  IStream * 	   - figure it out
//
// Output:
//	  HRESULT

HRESULT COleControl::m_SaveToStream ( IStream *pStream )
{
	return SaveStandardState(pStream);

#if 0
	01-Sep-1997 [ralphw] We don't save binary state

	HRESULT hr;

	// save out standard state information that the user has no control
	// over

	hr = SaveStandardState(pStream);
	RETURN_ON_FAILURE(hr);

	// save out user-specific satte information.  they MUST implement this
	// function

	hr = SaveBinaryState(pStream);

	return hr;
#endif
}

//=--------------------------------------------------------------------------=
// COleControl::LoadStandardState	 [ helper ]
//=--------------------------------------------------------------------------=
// reads in standard properties that all controls are going to have, using
// text persistence APIs.  there is another version for streams.
//
// Parameters:
//	  IPropertyBag *	- [in]
//	  IErrorLog *		- [in]
//
// Output:
//	  HRESULT
//
// Notes:
//
HRESULT COleControl::LoadStandardState
(
	IPropertyBag *pPropertyBag,
	IErrorLog	 *pErrorLog
)
{
	VARIANT v;
	HRESULT hr;
	SIZEL	slHiMetric = { 100, 50 };

	// currently, our only standard properties are related to size.
	// if we can't find them, then we'll just use some defaults.
	//
	v.vt = VT_I4;
	v.lVal = 0;
	hr = pPropertyBag->Read(L"Width", &v, pErrorLog);
	if (SUCCEEDED(hr)) slHiMetric.cx = v.lVal;

	v.lVal = 0;
	hr = pPropertyBag->Read(L"Height", &v, pErrorLog);
	if (SUCCEEDED(hr)) slHiMetric.cy = v.lVal;

	HiMetricToPixel(&slHiMetric, &m_Size);
	return S_OK;
}

//=--------------------------------------------------------------------------=
// COleControl::LoadStandardState	 [ helper ]
//=--------------------------------------------------------------------------=
// reads in standard properties that all controls are going to have, using
// stream persistence APIs.  there is another version for text.
//
// Parameters:
//	  IStream * 		- [in]
//
// Output:
//	  HRESULT
//
// Notes:
//
HRESULT COleControl::LoadStandardState
(
	IStream *pStream
)
{
	STREAMHDR stmhdr;
	HRESULT hr;
	SIZEL	slHiMetric;

	// look for our header structure, so we can verify stream validity.
	//
	hr = pStream->Read(&stmhdr, sizeof(STREAMHDR), NULL);
	RETURN_ON_FAILURE(hr);

	if (stmhdr.dwSignature != STREAMHDR_SIGNATURE)
		return E_UNEXPECTED;

	// currently, the only standard state we're writing out is
	// a SIZEL structure describing the control's size.
	//
	if (stmhdr.cbWritten != sizeof(m_Size))
		return E_UNEXPECTED;

	// we like the stream.	let's go load in our two properties.
	//
	hr = pStream->Read(&slHiMetric, sizeof(slHiMetric), NULL);
	RETURN_ON_FAILURE(hr);

	HiMetricToPixel(&slHiMetric, &m_Size);
	return S_OK;
}

//=--------------------------------------------------------------------------=
// COleControl::SaveStandardState	 [ helper ]
//=--------------------------------------------------------------------------=
// saves out standard properties that we're managing for a control using text
// persistence APIs.  there is another version for stream persistence.
//
// Parameters:
//	  IPropertyBag *		- [in]
//
// Output:
//	  HRESULT
//
// Notes:
//
HRESULT COleControl::SaveStandardState
(
	IPropertyBag *pPropertyBag
)
{
	HRESULT hr;
	VARIANT v;
	SIZEL	slHiMetric;

	// currently, the only standard proprerties we persist are Size related
	//
	PixelToHiMetric(&m_Size, &slHiMetric);

	v.vt = VT_I4;
	v.lVal = slHiMetric.cx;

	hr = pPropertyBag->Write(L"Width", &v);
	RETURN_ON_FAILURE(hr);

	v.lVal = slHiMetric.cy;

	hr = pPropertyBag->Write(L"Height", &v);

	return hr;
}

//=--------------------------------------------------------------------------=
// COleControl::SaveStandardState	 [ helper ]
//=--------------------------------------------------------------------------=
// saves out standard properties that we're managing for a control using stream
// persistence APIs.  there is another version for text persistence.
//
// Parameters:
//	  IStream * 		   - [in]
//
// Output:
//	  HRESULT
//
// Notes:
//
HRESULT COleControl::SaveStandardState
(
	IStream *pStream
)
{
	STREAMHDR streamhdr = { STREAMHDR_SIGNATURE, sizeof(SIZEL) };
	HRESULT hr;
	SIZEL	slHiMetric;


	// first thing to do is write out our stream hdr structure.
	//
	hr = pStream->Write(&streamhdr, sizeof(STREAMHDR), NULL);
	RETURN_ON_FAILURE(hr);

	// the only properties we're currently persisting here are the size
	// properties for this control.  make sure we do that in HiMetric
	//
	PixelToHiMetric(&m_Size, &slHiMetric);

	hr = pStream->Write(&slHiMetric, sizeof(slHiMetric), NULL);
	return hr;
}

//=--------------------------------------------------------------------------=
// COleControl::InitializeNewState	  [overridable]
//=--------------------------------------------------------------------------=
// the user can override this to initialize variables
//
// Output:
//	  BOOL		  - FALSE means couldn't do it.
//
// Notes:
//
BOOL COleControl::InitializeNewState
(
	void
)
{
	// we find this largely uninteresting
	//
	return TRUE;
}



