// This is a part of the Microsoft Foundation Classes C++ library.
// Copyright (C) 1992-1998 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Microsoft Foundation Classes Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Microsoft Foundation Classes product.

#include "stdafx.h"

#ifdef AFXCTL_PAGE_SEG
#pragma code_seg(AFXCTL_PAGE_SEG)
#endif

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

#define new DEBUG_NEW

/////////////////////////////////////////////////////////////////////////////
// CPicturePropPage implementation

BEGIN_MESSAGE_MAP(CPicturePropPage, CStockPropPage)
	//{{AFX_MSG_MAP(CPicturePropPage)
	ON_CBN_SELCHANGE(AFX_IDC_PROPNAME, OnSelchangePictProp)
	ON_WM_PAINT()
	ON_BN_CLICKED(AFX_IDC_BROWSE, OnBrowse)
	ON_BN_CLICKED(AFX_IDC_CLEAR, OnClear)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

CPicturePropPage::CPicturePropPage() :
	CStockPropPage(IDD, AFX_IDS_PICTURE_PPG_CAPTION),
	m_pPictDisp(NULL)
{
	//{{AFX_DATA_INIT(CPicturePropPage)
		// NOTE: the ClassWizard will add member initialization here
	//}}AFX_DATA_INIT
}

CPicturePropPage::~CPicturePropPage()
{
	// Release picture, if any

	if (m_pPictDisp != NULL)
	{
		m_pPictDisp->Release();
		m_pPictDisp = NULL;
	}
}

BOOL CPicturePropPage::OnInitDialog()
{
	m_pPictDisp = NULL;
	SetModifiedFlag(FALSE);
	CStockPropPage::OnInitDialog();
	OnObjectsChanged();
	IgnoreApply(AFX_IDC_BROWSE);
	IgnoreApply(AFX_IDC_PROPNAME);
	OnSelchangePictProp();
	return TRUE;
}

void CPicturePropPage::DoDataExchange(CDataExchange* pDX)
{
	//{{AFX_DATA_MAP(CPicturePropPage)
	DDX_Control(pDX, AFX_IDC_PROPNAME, m_PropName);
	DDX_Control(pDX, AFX_IDC_PICTURE, m_Static);
	//}}AFX_DATA_MAP

	if (pDX->m_bSaveAndValidate)
	{
		if (IsModified())
			if (!SetPictureProp(pDX, m_pPictDisp, m_strPropName))
				GetPictureProp(pDX, &m_pPictDisp, m_strPropName);
	}
	else
	{
		GetPictureProp(pDX, &m_pPictDisp, m_strPropName);
		SetModifiedFlag(FALSE);
	}
}

BOOL CPicturePropPage::SetPictureProp(CDataExchange*, LPPICTUREDISP pPictDisp, LPCTSTR pszPropName)
{
	USES_CONVERSION;

	COleDispatchDriver PropDispDriver;

	// Set the properties for all the objects

	ULONG nObjects;
	LPDISPATCH* ppDisp = GetObjectArray(&nObjects);
	BOOL bSuccess = TRUE;

	for (ULONG i = 0; bSuccess && (i < nObjects); i++)
	{
		DISPID dwDispID;

		// Get the Dispatch ID for the property and if successful set the value
		LPCOLESTR lpOleStr = T2COLE(pszPropName);
		if (SUCCEEDED(ppDisp[i]->GetIDsOfNames(IID_NULL, (LPOLESTR*)&lpOleStr,
			1, m_lcid, &dwDispID)))
		{
			// Set property
			PropDispDriver.AttachDispatch(ppDisp[i], FALSE);
			TRY
			{
				PropDispDriver.SetProperty(dwDispID, VT_DISPATCH, pPictDisp);
			}
			CATCH(COleDispatchException, e)
			{
				// Display message box for dispatch exceptions.
				e->ReportError(MB_ICONEXCLAMATION | MB_OK);
				DELETE_EXCEPTION(e);

				bSuccess = FALSE;
			}
			AND_CATCH_ALL(e)
			{
				// Ignore other exceptions.
				DELETE_EXCEPTION(e);
			}
			END_CATCH_ALL
			PropDispDriver.DetachDispatch();
		}
	}
	return bSuccess;
}

BOOL CPicturePropPage::GetPictureProp(CDataExchange*, LPPICTUREDISP* ppPictDisp, LPCTSTR pszPropName)
{
	USES_CONVERSION;

	// Release previous picture, if any.
	if (*ppPictDisp != NULL)
	{
		(*ppPictDisp)->Release();
		*ppPictDisp = NULL;
	}

	COleDispatchDriver PropDispDriver;

	ULONG nObjects;
	LPDISPATCH* ppDisp = GetObjectArray(&nObjects);

	for (ULONG i = 0; i < nObjects; i++)
	{
		DISPID dwDispID;

		// Get the Dispatch ID for the property and if successful get the value
		LPCOLESTR lpOleStr = T2COLE(pszPropName);
		if (SUCCEEDED(ppDisp[i]->GetIDsOfNames(IID_NULL, (LPOLESTR*)&lpOleStr,
			1, m_lcid, &dwDispID)))
		{
			// Get property
			LPPICTUREDISP pPictDisp = NULL;

			PropDispDriver.AttachDispatch(ppDisp[i], FALSE);
			TRY
				PropDispDriver.GetProperty(dwDispID, VT_DISPATCH, &pPictDisp);
			END_TRY
			PropDispDriver.DetachDispatch();

			// If more than one object, all pictures must be the same.

			if ((i != 0) && (pPictDisp != *ppPictDisp))
			{
				if (*ppPictDisp != NULL)
					(*ppPictDisp)->Release();

				*ppPictDisp = NULL;
			}
			else
			{
				*ppPictDisp = pPictDisp;
			}

			// Don't keep multiple references to the picture.

			if ((i != 0) && (pPictDisp != NULL))
				pPictDisp->Release();
		}
	}

	InvalidateRect(NULL);
	return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
// CPicturePropPage message handlers

void CPicturePropPage::OnPaint()
{
	CPaintDC dc(this); // device context for painting

	// Determine rectangle for drawing picture

	CRect rc;
	m_Static.GetWindowRect(&rc);
	rc.InflateRect(-::GetSystemMetrics(SM_CXBORDER) * 2,
		-::GetSystemMetrics(SM_CYBORDER) * 2);
	ScreenToClient(&rc);

	LPPICTURE pPict = NULL;

	if ((m_pPictDisp != NULL) &&
		SUCCEEDED(m_pPictDisp->QueryInterface(IID_IPicture, (LPVOID*)&pPict)))
	{
		ASSERT(pPict != NULL);

		// Draw it

		OLE_XSIZE_HIMETRIC hmWidth;
		OLE_YSIZE_HIMETRIC hmHeight;

		pPict->get_Width(&hmWidth);
		pPict->get_Height(&hmHeight);

		int cxRect = rc.Width();
		int cyRect = rc.Height();
		if ((hmWidth != 0) && (hmHeight != 0) && (cxRect != 0) && (cyRect != 0))
		{
			// Adjust aspect ratio of rectangle to match picture.

			int nPictureRatio = hmWidth * 1000 / hmHeight;
			int nRectRatio = cxRect * 1000 / cyRect;

			if (nPictureRatio > nRectRatio)
			{
				int dy = (cyRect - (cxRect * 1000 / nPictureRatio) + 1) / 2;
				rc.InflateRect(0, -dy);
			}
			else
			{
				int dx = (cxRect - (cyRect * nPictureRatio / 1000) + 1) / 2;
				rc.InflateRect(-dx, 0);
			}
		}

		pPict->Render(dc.m_hDC, rc.left, rc.top, rc.Width(), rc.Height(),
			0, hmHeight-1, hmWidth, -hmHeight, &rc);

		pPict->Release();
	}
}

HRESULT _AfxCreateStreamOnFile(LPCTSTR pszPath, LPSTREAM* ppstm,
	LONG* plSize)
{
	ASSERT(pszPath != NULL);
	ASSERT(ppstm != NULL);

	*ppstm = NULL;

	if (plSize != NULL)
		*plSize = 0;

	CFileStatus fstatus;
	CFile file;
	HRESULT hr = E_FAIL;
	LONG cb;

	if (file.Open(pszPath, CFile::modeRead) &&
		file.GetStatus(pszPath, fstatus) &&
		((cb = fstatus.m_size) != -1))
	{
		HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, cb);
		LPVOID pvData = NULL;

		if (hGlobal != NULL)
		{
			if ((pvData = GlobalLock(hGlobal)) != NULL)
			{
				BOOL bRead = (file.ReadHuge(pvData, cb) == (ULONG)cb);
				GlobalUnlock(hGlobal);

				if (bRead &&
					SUCCEEDED(hr = CreateStreamOnHGlobal(hGlobal, TRUE, ppstm)))
				{
					ASSERT(*ppstm != NULL);
					if (plSize != NULL)
						*plSize = fstatus.m_size;
					hr = S_OK;
				}
			}

			if (FAILED(hr))
				GlobalFree(hGlobal);
		}
		else
		{
			hr = E_OUTOFMEMORY;
		}
	}
	else
	{
		hr = E_ACCESSDENIED;
	}

	return hr;
}

void CPicturePropPage::OnBrowse()
{
	CString strFilter;
	strFilter.LoadString(AFX_IDS_PICTUREFILTER);

	CString strTitle;
	strTitle.LoadString(AFX_IDS_PICTUREBROWSETITLE);

	CFileDialog fdlg(TRUE, NULL, NULL,
			OFN_FILEMUSTEXIST |
			OFN_HIDEREADONLY |
			OFN_PATHMUSTEXIST,
			strFilter);

	fdlg.m_ofn.lpstrTitle = strTitle;

	int nResult = fdlg.DoModal();
	SetFocus();

	if (nResult != IDOK)
		return;

	CString strPath = fdlg.GetPathName();

	LPSTREAM pstm = NULL;
	LONG lSize;
	HRESULT hr;

	if (FAILED(hr = _AfxCreateStreamOnFile(strPath, &pstm, &lSize)))
	{
		UINT idsText;
		CString strText;
		CString strCaption;

		switch (GetScode(hr))
		{
		case E_OUTOFMEMORY:
			idsText = AFX_IDP_PICTURETOOLARGE;
			break;

		case E_ACCESSDENIED:
			idsText = AFX_IDP_PICTURECANTOPEN;
			break;

		default:
			idsText = AFX_IDP_PICTUREREADFAILED;
			break;
		}

		AfxFormatString1(strText, idsText, strPath);
		strCaption.LoadString(AFX_IDS_PICTURE_PPG_CAPTION);
		MessageBox(strText, strCaption, MB_OK | MB_ICONEXCLAMATION);
		SetFocus();
		return;
	}

	ASSERT(pstm != NULL);

	LPPICTURE pPict;
	if (SUCCEEDED(::OleLoadPicture(pstm, lSize, FALSE, IID_IPicture, (LPVOID *)&pPict)))
	{
		ChangePicture(pPict);
	}
	else
	{
		CString strText;
		CString strCaption;
		AfxFormatString1(strText, AFX_IDP_PICTURECANTLOAD, strPath);
		strCaption.LoadString(AFX_IDS_PICTURE_PPG_CAPTION);
		MessageBox(strText, strCaption, MB_OK | MB_ICONEXCLAMATION);
		SetFocus();
	}

	pstm->Release();
}

AFX_STATIC_DATA PICTDESC _afxPictDescEmpty = { sizeof(PICTDESC), PICTYPE_NONE, };

void CPicturePropPage::OnClear()
{
	LPPICTURE pPict;
	if (SUCCEEDED(::OleCreatePictureIndirect(&_afxPictDescEmpty, IID_IPicture, TRUE,
		(LPVOID *)&pPict)))
	{
		ChangePicture(pPict);
	}
	else
	{
		ASSERT(FALSE);  // This should never happen!
	}
}

void CPicturePropPage::ChangePicture(LPPICTURE pPict)
{
	ASSERT(pPict != NULL);

	LPPICTUREDISP pPictDisp = NULL;
	if (SUCCEEDED(pPict->QueryInterface(IID_IPictureDisp,
			(LPVOID*)&pPictDisp)))
	{
		ASSERT(pPictDisp != NULL);

		if (m_pPictDisp != NULL)
			m_pPictDisp->Release();

		m_pPictDisp = pPictDisp;
		SetModifiedFlag();

		LPPROPERTYPAGESITE pPageSite = GetPageSite();
		if (pPageSite != NULL)
			pPageSite->OnStatusChange(
				PROPPAGESTATUS_VALIDATE | PROPPAGESTATUS_DIRTY);

		InvalidateRect(NULL);
	}

	pPict->Release();
}

void CPicturePropPage::OnSelchangePictProp()
{
	OnSelchangePropname(m_PropName);

	// disable Browse button if no property selected
	CComboBox* pPropName = (CComboBox*)GetDlgItem(AFX_IDC_PROPNAME);
	CWnd* pBrowse = GetDlgItem(AFX_IDC_BROWSE);
	pBrowse->EnableWindow(pPropName->GetCurSel() != CB_ERR);

	// redraw bitmap for new picture property
	CRect rect;

	m_Static.GetWindowRect(rect);
	rect.InflateRect(-::GetSystemMetrics(SM_CXBORDER),
		-::GetSystemMetrics(SM_CYBORDER));
	ScreenToClient(rect);

	InvalidateRect(rect);
}

BOOL CPicturePropPage::OnEditProperty(DISPID dispid)
{
	return CStockPropPage::OnEditProperty(dispid, m_PropName);
}

void CPicturePropPage::OnObjectsChanged()
{
	ULONG nObjects;
	if (GetObjectArray(&nObjects) != NULL && m_hWnd != NULL)
		FillPropnameList(IID_IPictureDisp, 1, m_PropName);

	if (m_hWnd != NULL)
		OnSelchangePictProp();
}

/////////////////////////////////////////////////////////////////////////////
// Class factory for Picture property page

#ifdef _AFXDLL

#ifdef AFXCTL_FACT_SEG
#pragma code_seg(AFXCTL_FACT_SEG)
#endif

IMPLEMENT_OLECREATE_EX(CPicturePropPage, "OCxx.CPicturePropPage",
	0x0be35202,0x8f91,0x11ce,0x9d,0xe3,0x00,0xaa,0x00,0x4b,0xb8,0x51)

BOOL CPicturePropPage::CPicturePropPageFactory::UpdateRegistry(BOOL bRegister)
{
	if (bRegister)
		return AfxOleRegisterPropertyPageClass(AfxGetInstanceHandle(),
			m_clsid, AFX_IDS_PICTURE_PPG);
	else
		return AfxOleUnregisterClass(m_clsid, NULL);
}

#endif //_AFXDLL

/////////////////////////////////////////////////////////////////////////////
// Force any extra compiler-generated code into AFX_INIT_SEG

#ifdef AFX_INIT_SEG
#pragma code_seg(AFX_INIT_SEG)
#endif

IMPLEMENT_DYNCREATE(CPicturePropPage, CStockPropPage)
