Click here to Skip to main content
15,891,253 members
Articles / Desktop Programming / WTL

Form Designer

26 Jul 2021CPOL24 min read 351.8K   82.5K   230  
Component for adding scriptable forms capabilities to an application.
// PropPageAll.cpp : Implementation of CPropPageAll
//
// Author : David Shepherd
//			Copyright (c) 2002, DaeDoe-Software
//
/////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "DDPropPageAll.h"
#include "PropPageAll.h"

// list box border dimensions
#define LIST_BORDER_WIDTH		8
#define LIST_BORDER_HEIGHT		10

/////////////////////////////////////////////////////////////////////////////
// CPropPageAll

CPropPageAll::CPropPageAll() : m_ListBox(this,MSG_MAP_LIST)
{
	// initialise everything
	m_dwTitleID=IDS_TITLEPropPageAll;
	m_dwHelpFileID=IDS_HELPFILEPropPageAll;
	m_dwDocStringID=IDS_DOCSTRINGPropPageAll;
	m_ConnectionCookie=0;
}

CFontHandle CPropPageAll::GetFont()
{
	// return the property page font
	CFontHandle hFont=(HFONT)GetStockObject(DEFAULT_GUI_FONT);
	if(hFont==NULL)
	{
		throw std::exception();
	}
	return hFont;
}

void CPropPageAll::LoadPropertyMap()
{
	USES_CONVERSION;

	// get the IDispatch interface
	CComQIPtr<IDispatch> spDispatch(m_ppUnk[0]);
	if(spDispatch==NULL)
	{
		throw std::exception();
	}
	// get type info
	CComPtr<ITypeInfo> spTypeInfo;
	if(!SUCCEEDED(spDispatch->GetTypeInfo(0,LOCALE_USER_DEFAULT,&spTypeInfo)))
	{
		throw std::exception();
	}
	// get type attributes
	CAutoTypeAttrPtr pTypeAttr(spTypeInfo);
	// add all variables
	for(long l=0; l<pTypeAttr->cVars; l++)
	{
		// get the variable description
		CAutoVarDescPtr pVarDesc(l,spTypeInfo);
		// ignore if read only
		if(pVarDesc->wVarFlags & VARFLAG_FREADONLY)
		{
			continue;
		}
		// ignore if hidden
		if(pVarDesc->wVarFlags & VARFLAG_FHIDDEN)
		{
			continue;
		}
		// ignore if not browsable
		if(pVarDesc->wVarFlags & VARFLAG_FNONBROWSABLE)
		{
			continue;
		}
		// ignore if not a dispatch variable
		if(pVarDesc->varkind!=VAR_DISPATCH)
		{
			continue;
		}
		// create property info
		CPropertyInfo PropertyInfo;
		PropertyInfo.Create(spDispatch.p,pVarDesc->memid,
			pVarDesc->elemdescVar.tdesc);
		// update the property map ignoring unknown and invalid types
		if(	PropertyInfo.m_Type!=CPropertyInfo::TypeUnknown and
			PropertyInfo.m_Type!=CPropertyInfo::TypeInvalid)
		{
			m_PropertyMap[PropertyInfo.m_DispatchId]=PropertyInfo;
		}
		else
		{
			ATLTRACE(_T("CPropPageAll::Load() - Ignoring %s\n"),
				W2CT(PropertyInfo.m_Name.c_str()));	
		}
	}
	// find all property accessor functions
	for(l=0; l<pTypeAttr->cFuncs; l++)
	{
		// get the function description
		CAutoFuncDescPtr pFuncDesc(l,spTypeInfo);
		// ignore if not a property accessor
		if(	pFuncDesc->invkind != INVOKE_PROPERTYPUT and
			pFuncDesc->invkind != INVOKE_PROPERTYPUTREF)
		{
			continue;
		}
		// ignore if hidden
		if(pFuncDesc->wFuncFlags & FUNCFLAG_FHIDDEN)
		{
			continue;
		}
		// ignore if not browsable
		if(pFuncDesc->wFuncFlags & FUNCFLAG_FNONBROWSABLE)
		{
			continue;
		}
		// ignore if extra parameters are required
		if(pFuncDesc->cParams!=1)
		{
			continue;
		}
		// create property info
		CPropertyInfo PropertyInfo;
		PropertyInfo.Create(spDispatch.p,pFuncDesc->memid,
			pFuncDesc->lprgelemdescParam[0].tdesc);
		// update the property map ignoring unknown and invalid types
		if(	PropertyInfo.m_Type!=CPropertyInfo::TypeUnknown and
			PropertyInfo.m_Type!=CPropertyInfo::TypeInvalid)
		{
			m_PropertyMap[PropertyInfo.m_DispatchId]=PropertyInfo;
		}
		else
		{
			ATLTRACE(_T("CPropPageAll::Load() - Ignoring %s\n"),
				W2CT(PropertyInfo.m_Name.c_str()));	
		}
	}
}

void CPropPageAll::PropertyChanged(DISPID DispatchId)
{
	// get the IDispatch interface
	CComQIPtr<IDispatch> spDispatch(m_ppUnk[0]);
	if(spDispatch==NULL)
	{
		throw std::exception();
	}
	// update the property value
	m_PropertyMap[DispatchId].GetValue(spDispatch.p);
	// invalidate the item rect
	if(m_ListBox.InvalidateRect(GetItemRect(DispatchId),FALSE)==FALSE)
	{
		throw std::exception();
	}
}

CRect CPropPageAll::GetItemRect(DISPID DispatchId)
{
	// return the item rectangle
	CRect ItemRect(0,0,0,0);
	// get the item index
	DWORD ItemIndex=m_ListBox.FindString(-1,(LPCTSTR)DispatchId);
	if(ItemIndex==LB_ERR)
	{
		throw std::exception();
	}
	// get the item rect
	if(m_ListBox.GetItemRect(ItemIndex,ItemRect)==LB_ERR)
	{
		throw std::exception();
	}
	return ItemRect;
}

CRect CPropPageAll::GetItemNameRect(const CRect &ItemRect)
{
	// return the item name rectangle
	CRect NameRect=ItemRect;
	NameRect.right=NameRect.left+NameRect.Width()/2;
	// allow room for the grid
	NameRect.bottom-=1;

	return NameRect;
}

CRect CPropPageAll::GetItemValueRect(const CRect &ItemRect)
{
	// return the item value rectangle
	CRect ValueRect=ItemRect;
	ValueRect.left=GetItemNameRect(ItemRect).right;
	// allow room for the grid
	ValueRect.bottom-=1;
	ValueRect.left+=1;

	return ValueRect;
}

CRect CPropPageAll::GetItemButtonRect(const CRect &ItemRect)
{
	// return the item button rectangle
	CRect ButtonRect=ItemRect;
	// the button may overwrite all other components including the grid
	ButtonRect.left=ButtonRect.right-GetSystemMetrics(SM_CXVSCROLL);

	return ButtonRect;
}

void CPropPageAll::DrawValueAsString(CDCHandle &hDC,
	const CPropertyInfo &PropertyInfo,const CRect &ValueRect)
{
	USES_CONVERSION;

	// get the value name
	std::wstring Name=PropertyInfo.GetValueName();
	// if the name could not be determined use the actual value
	if(Name.empty())
	{
		// convert the value to a string
		CComVariant Value=PropertyInfo.m_Value;
		if(!SUCCEEDED(Value.ChangeType(VT_BSTR)))
		{
			throw std::exception();
		}
		Name=BSTR2W(Value.bstrVal);
	}
	// draw the value
	if(hDC.DrawText(W2CT(Name.c_str()),
		-1,CRect(ValueRect),DT_SINGLELINE|DT_VCENTER|DT_NOPREFIX)==0)
	{
		throw std::exception();
	}
}

void CPropPageAll::DrawValueAsColor(CDCHandle &hDC,
	const CPropertyInfo &PropertyInfo,const CRect &ValueRect)
{
	USES_CONVERSION;

	// create GDI object wrappers
	// this must be done before the device context state is saved
	CPen Pen;
	CBrush Brush;
	// save the device context state
	CAutoDCState AutoDCState(hDC);
	// get the color sample rect
	CRect ColorSampleRect=ValueRect;
	ColorSampleRect.right=
		ColorSampleRect.left+(long)((float)ColorSampleRect.Height()*ASPECT_RATIO(hDC));
	ColorSampleRect.InflateRect(-1,-1);
	// set the pen
	if(Pen.CreatePen(PS_SOLID,0,RGB(0,0,0))==NULL)
	{
		throw std::exception();
	}
	if(hDC.SelectPen(Pen)==NULL)
	{
		throw std::exception();
	}
	// translate the color to an RGB value
	COLORREF RGBColor=RGB(0,0,0);
	if(!SUCCEEDED(OleTranslateColor(PropertyInfo.m_Value.lVal,NULL,&RGBColor)))
	{
		throw std::exception();
	}
	// set the brush
	if(Brush.CreateSolidBrush(RGBColor)==NULL)
	{
		throw std::exception();
	}
	if(hDC.SelectBrush(Brush)==NULL)
	{
		throw std::exception();
	}
	// draw the color sample
	if(hDC.Rectangle(ColorSampleRect)==FALSE)
	{
		throw std::exception();
	}
	// get the hex value rect
	CRect HexRect=ValueRect;
	HexRect.left=ColorSampleRect.right+1;
	// translate the color to a hex value
	std::wstringstream strm;
	strm << L" &H";
	strm << std::hex			<<
			std::uppercase		<<
			std::setw(8)		<<
			std::setfill(L'0')	<<
			PropertyInfo.m_Value.lVal;
	strm << L"&";
	// draw the hex value
	if(hDC.DrawText(W2CT(strm.str().c_str()),
		-1,HexRect,DT_SINGLELINE|DT_VCENTER|DT_NOPREFIX)==0)
	{
		throw std::exception();
	}
}

void CPropPageAll::DrawValueAsFont(CDCHandle &hDC,
	const CPropertyInfo &PropertyInfo,const CRect &ValueRect)
{
	USES_CONVERSION;

	// get font information
	std::wstringstream strm;
	if(PropertyInfo.m_Value.pdispVal!=NULL)
	{
		CComDispatchDriver DispatchDriver(PropertyInfo.m_Value.pdispVal);
		// font name
		CComVariant Name;
		if(!SUCCEEDED(DispatchDriver.GetProperty(DISPID_FONT_NAME,&Name)))
		{
			throw std::exception();
		}
		strm << BSTR2W(Name.bstrVal);
		// font size
		CComVariant Size;
		if(!SUCCEEDED(DispatchDriver.GetProperty(DISPID_FONT_SIZE,&Size)))
		{
			throw std::exception();
		}
		strm <<	L" - ";
		strm << std::fixed				<<
				std::setprecision(0)	<<
				(float)Size.cyVal.Lo/10000;
		strm <<	L"pt";
	}
	else	// the font has not been set
	{
		strm << L"<Invalid>";
	}
	// draw font information
	if(hDC.DrawText(W2CT(strm.str().c_str()),
		-1,CRect(ValueRect),DT_SINGLELINE|DT_VCENTER|DT_NOPREFIX)==0)
	{
		throw std::exception();
	}
}

void CPropPageAll::DrawValueAsPicture(CDCHandle &hDC,
	const CPropertyInfo &PropertyInfo,const CRect &ValueRect)
{
	USES_CONVERSION;

	// get picture information
	std::wstringstream strm;
	if(PropertyInfo.m_Value.pdispVal!=NULL)
	{
		CComDispatchDriver DispatchDriver(PropertyInfo.m_Value.pdispVal);
		// picture type
		CComVariant Type;
		if(!SUCCEEDED(DispatchDriver.GetProperty(DISPID_PICT_TYPE,&Type)))
		{
			throw std::exception();
		}
		switch(Type.iVal)
		{
		// uninitialised
		case PICTYPE_UNINITIALIZED:
			strm << L"<Uninitialised>";
			break;
		// none
		case PICTYPE_NONE:
			strm << L"<None>";
			break;
		// bitmap
		case PICTYPE_BITMAP:
			strm << L"<Bitmap>";
			break;
		// metafile
		case PICTYPE_METAFILE:
			strm << L"<Metafile>";
			break;
		// icon
		case PICTYPE_ICON:
			strm << L"<Icon>";
			break;
		// enhanced metafile
		case PICTYPE_ENHMETAFILE:
			strm << L"<Enhanced Metafile>";
			break;
		// unknown
		default:
			strm << L"<Unknown>";
			break;
		}
		// picture dimensions
		if(	Type.iVal==PICTYPE_BITMAP	or
			Type.iVal==PICTYPE_METAFILE or
			Type.iVal==PICTYPE_ICON		or
			Type.iVal==PICTYPE_ENHMETAFILE)
		{
			// width
			CComVariant Width;
			if(!SUCCEEDED(
				DispatchDriver.GetProperty(DISPID_PICT_WIDTH,&Width)))
			{
				throw std::exception();
			}
			// height
			CComVariant Height;
			if(!SUCCEEDED(
				DispatchDriver.GetProperty(DISPID_PICT_HEIGHT,&Height)))
			{
				throw std::exception();
			}
			// convert to pixels
			CSize PixelSize(Width.lVal,Height.lVal);
			hDC.HIMETRICtoDP(&PixelSize);
			strm << L" - ";
			strm << PixelSize.cx << L"x" << PixelSize.cy;
		}
	}
	else	// the picture has not been set
	{
		strm << L"<Invalid>";
	}
	// draw picture information
	if(hDC.DrawText(W2CT(strm.str().c_str()),
		-1,CRect(ValueRect),DT_SINGLELINE|DT_VCENTER|DT_NOPREFIX)==0)
	{
		throw std::exception();
	}
}

void CPropPageAll::DrawItemGrid(CDCHandle &hDC,
	const CPropertyInfo &PropertyInfo,const CRect &ItemRect,BOOL IsSelected)
{
	// create GDI object wrappers
	// this must be done before the device context state is saved
	CPen Pen;
	// save the device context state
	CAutoDCState AutoDCState(hDC);
	// set the pen
	if(Pen.CreatePen(PS_SOLID,0,GetSysColor(COLOR_3DFACE))==NULL)
	{
		throw std::exception();
	}
	if(hDC.SelectPen(Pen)==NULL)
	{
		throw std::exception();
	}
	// draw the horizontal grid line
	if(hDC.MoveTo(ItemRect.left,ItemRect.bottom-1)==FALSE)
	{
		throw std::exception();
	}
	if(hDC.LineTo(ItemRect.right,ItemRect.bottom-1)==FALSE)
	{
		throw std::exception();
	}
	// draw the vertical grid line
	if(hDC.MoveTo(GetItemNameRect(ItemRect).right,ItemRect.top)==FALSE)
	{
		throw std::exception();
	}
	if(hDC.LineTo(GetItemNameRect(ItemRect).right,ItemRect.bottom)==FALSE)
	{
		throw std::exception();
	}
}

void CPropPageAll::DrawItemName(CDCHandle &hDC,
	const CPropertyInfo &PropertyInfo,const CRect &ItemRect,BOOL IsSelected)
{
	USES_CONVERSION;

	// save the device context state
	CAutoDCState AutoDCState(hDC);
	// get the name rect
	CRect NameRect=GetItemNameRect(ItemRect);
	// set the font
	if(hDC.SelectFont(GetFont())==NULL)
	{
		throw std::exception();
	}
	// set the background color
	COLORREF BackColor=GetSysColor(
		IsSelected ? COLOR_HIGHLIGHT : COLOR_WINDOW);
	if(hDC.SetBkColor(BackColor)==CLR_INVALID)
	{
		throw std::exception();
	}
	// set the foreground color
	COLORREF ForeColor=GetSysColor(
		IsSelected ? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT);
	if(hDC.SetTextColor(ForeColor)==CLR_INVALID)
	{
		throw std::exception();
	}
	// fill in the background
	hDC.FillSolidRect(NameRect,BackColor);
	// draw the name
	if(hDC.DrawText(W2CT(PropertyInfo.m_Name.c_str()),
		-1,NameRect,DT_SINGLELINE|DT_VCENTER|DT_NOPREFIX)==0)
	{
		throw std::exception();
	}
}

void CPropPageAll::DrawItemValue(CDCHandle &hDC,
	const CPropertyInfo &PropertyInfo,const CRect &ItemRect,BOOL IsSelected)
{
	// save the device context state
	CAutoDCState AutoDCState(hDC);
	// get the value rect
	CRect ValueRect=GetItemValueRect(ItemRect);
	// set the font
	if(hDC.SelectFont(GetFont())==NULL)
	{
		throw std::exception();
	}
	// set the background color
	COLORREF BackColor=GetSysColor(COLOR_WINDOW);
	if(hDC.SetBkColor(BackColor)==CLR_INVALID)
	{
		throw std::exception();
	}
	// set the foreground color
	COLORREF ForeColor=GetSysColor(COLOR_WINDOWTEXT);
	if(hDC.SetTextColor(ForeColor)==CLR_INVALID)
	{
		throw std::exception();
	}
	// fill in the background
	hDC.FillSolidRect(ValueRect,BackColor);
	// draw the value
	if(PropertyInfo.m_Value.vt!=VT_EMPTY)
	{
		switch(PropertyInfo.m_Type)
		{
		// string
		case CPropertyInfo::TypeString:
		case CPropertyInfo::TypeBool:
		case CPropertyInfo::TypeEnum:
			DrawValueAsString(hDC,PropertyInfo,ValueRect);
			break;
		// color
		case CPropertyInfo::TypeColor:
			DrawValueAsColor(hDC,PropertyInfo,ValueRect);
			break;
		// font
		case CPropertyInfo::TypeFont:
			DrawValueAsFont(hDC,PropertyInfo,ValueRect);
			break;
		// picture
		case CPropertyInfo::TypePicture:
			DrawValueAsPicture(hDC,PropertyInfo,ValueRect);
			break;
		// unknown
		default:
			ATLASSERT(FALSE);
			break;
		}
	}
	else	// the value is unknown
	{
		if(hDC.DrawText(_T("<Unknown>"),
			-1,ValueRect,DT_SINGLELINE|DT_VCENTER|DT_NOPREFIX)==0)
		{
			throw std::exception();
		}
	}
}

void CPropPageAll::DrawItemButton(CDCHandle &hDC,
	const CPropertyInfo &PropertyInfo,const CRect &ItemRect,BOOL IsSelected)
{
	// save the device context state
	CAutoDCState AutoDCState(hDC);
	// get the button rect
	CRect ButtonRect=GetItemButtonRect(ItemRect);
	// set the font
	if(hDC.SelectFont(GetFont())==NULL)
	{
		throw std::exception();
	}
	// set the background color
	COLORREF BackColor=GetSysColor(COLOR_BTNFACE);
	if(hDC.SetBkColor(BackColor)==CLR_INVALID)
	{
		throw std::exception();
	}
	// set the foreground color
	COLORREF ForeColor=GetSysColor(COLOR_BTNTEXT);
	if(hDC.SetTextColor(ForeColor)==CLR_INVALID)
	{
		throw std::exception();
	}
	// draw the button
	switch(PropertyInfo.m_Type)
	{
	// drop down button
	case CPropertyInfo::TypeString:
		if(PropertyInfo.m_PredefinedValues.size()==0)
		{
			// there are no predefined values
			break;
		}
		// fall through
	case CPropertyInfo::TypeBool:
	case CPropertyInfo::TypeEnum:
	case CPropertyInfo::TypeColor:
		if(hDC.DrawFrameControl(
			ButtonRect,DFC_SCROLL,DFCS_SCROLLDOWN)==FALSE)
		{
			throw std::exception();
		}
		break;
	// browse button
	case CPropertyInfo::TypeFont:
	case CPropertyInfo::TypePicture:
		if(hDC.DrawFrameControl(
			ButtonRect,DFC_BUTTON,DFCS_BUTTONPUSH)==FALSE)
		{
			throw std::exception();
		}
		// draw the elipsis
		if(hDC.SetBkMode(TRANSPARENT)==0)
		{
			throw std::exception();
		}
		if(hDC.DrawText(_T("..."),
			-1,ButtonRect,DT_SINGLELINE|DT_CENTER|DT_VCENTER|DT_NOPREFIX)==0)
		{
			throw std::exception();
		}
		break;
	// unknown
	default:
		ATLASSERT(FALSE);
		break;
	}
}

void CPropPageAll::ClearPicture(const CPropertyInfo &PropertyInfo)
{
	// get the picture description
	PICTDESC PictDesc;
	ZeroMemory(&PictDesc,sizeof(PICTDESC));
	PictDesc.cbSizeofstruct=sizeof(PICTDESC);
	PictDesc.picType=PICTYPE_NONE;
	// create the picture
	CComPtr<IPictureDisp> spPictureDisp;
	if(!SUCCEEDED(OleCreatePictureIndirect(
		&PictDesc,IID_IPictureDisp,TRUE,(void**)&spPictureDisp)))
	{
		throw std::exception();
	}
	// set the picture
	CComDispatchDriver DispatchDriver(m_ppUnk[0]);
	if(!SUCCEEDED(DispatchDriver.PutProperty(PropertyInfo.m_DispatchId,
		&CComVariant(spPictureDisp))))
	{
		throw std::exception();
	}
}

void CPropPageAll::SetPictureOnBitmap(
	const CPropertyInfo &PropertyInfo,const std::wstring &FileName)
{
	USES_CONVERSION;

	// load the bitmap
	HANDLE hImage=LoadImage(NULL,W2CT(FileName.c_str()),
		IMAGE_BITMAP,0,0,LR_CREATEDIBSECTION|LR_LOADFROMFILE);
	if(hImage==NULL)
	{
		throw std::exception();
	}
	// get the picture description
	PICTDESC PictDesc;
	ZeroMemory(&PictDesc,sizeof(PICTDESC));
	PictDesc.cbSizeofstruct=sizeof(PICTDESC);
	PictDesc.picType=PICTYPE_BITMAP;
	PictDesc.bmp.hbitmap=(HBITMAP)hImage;
	PictDesc.bmp.hpal=NULL;	// todo : check this is allowed
	// create the picture
	CComPtr<IPictureDisp> spPictureDisp;
	if(!SUCCEEDED(OleCreatePictureIndirect(
		&PictDesc,IID_IPictureDisp,TRUE,(void**)&spPictureDisp)))
	{
		throw std::exception();
	}
	// set the picture
	CComDispatchDriver DispatchDriver(m_ppUnk[0]);
	if(!SUCCEEDED(DispatchDriver.PutProperty(PropertyInfo.m_DispatchId,
		&CComVariant(spPictureDisp))))
	{
		throw std::exception();
	}
}

void CPropPageAll::SetPictureOnIcon(
	const CPropertyInfo &PropertyInfo,const std::wstring &FileName)
{
	USES_CONVERSION;

	// load the icon
	HANDLE hImage=LoadImage(NULL,W2CT(FileName.c_str()),
		IMAGE_ICON,0,0,LR_LOADFROMFILE);
	if(hImage==NULL)
	{
		throw std::exception();
	}
	// get the picture description
	PICTDESC PictDesc;
	ZeroMemory(&PictDesc,sizeof(PICTDESC));
	PictDesc.cbSizeofstruct=sizeof(PICTDESC);
	PictDesc.picType=PICTYPE_ICON;
	PictDesc.icon.hicon=(HICON)hImage;
	// create the picture
	CComPtr<IPictureDisp> spPictureDisp;
	if(!SUCCEEDED(OleCreatePictureIndirect(
		&PictDesc,IID_IPictureDisp,TRUE,(void**)&spPictureDisp)))
	{
		throw std::exception();
	}
	// set the picture
	CComDispatchDriver DispatchDriver(m_ppUnk[0]);
	if(!SUCCEEDED(DispatchDriver.PutProperty(PropertyInfo.m_DispatchId,
		&CComVariant(spPictureDisp))))
	{
		throw std::exception();
	}
}

void CPropPageAll::SetPictureOnMetafile(
	const CPropertyInfo &PropertyInfo,const std::wstring &FileName)
{
	USES_CONVERSION;

	// load the metafile
	HENHMETAFILE hMetafile=GetEnhMetaFile(W2CT(FileName.c_str()));
	if(hMetafile==NULL)
	{
		throw std::exception();
	}
	// get the picture description
	PICTDESC PictDesc;
	ZeroMemory(&PictDesc,sizeof(PICTDESC));
	PictDesc.cbSizeofstruct=sizeof(PICTDESC);
	PictDesc.picType=PICTYPE_ENHMETAFILE;
	PictDesc.emf.hemf=hMetafile;
	// create the picture
	CComPtr<IPictureDisp> spPictureDisp;
	if(!SUCCEEDED(OleCreatePictureIndirect(
		&PictDesc,IID_IPictureDisp,TRUE,(void**)&spPictureDisp)))
	{
		throw std::exception();
	}
	// set the picture
	CComDispatchDriver DispatchDriver(m_ppUnk[0]);
	if(!SUCCEEDED(DispatchDriver.PutProperty(PropertyInfo.m_DispatchId,
		&CComVariant(spPictureDisp))))
	{
		throw std::exception();
	}
}

void CPropPageAll::EditValue(const CPropertyInfo &PropertyInfo)
{
	// get the item rect
	CRect ItemRect=GetItemRect(PropertyInfo.m_DispatchId);
	// get the value rect
	CRect ValueRect=GetItemValueRect(ItemRect);
	// convert to screen coordinates
	if(m_ListBox.ClientToScreen(&ValueRect)==FALSE)
	{
		throw std::exception();
	}
	// convert the value to a string
	CComVariant Value=PropertyInfo.m_Value;
	if(!SUCCEEDED(Value.ChangeType(VT_BSTR)))
	{
		throw std::exception();
	}
	// show the value editor
	m_ValueEditor.m_Value=BSTR2W(Value.bstrVal);
	if(m_ValueEditor.EditValue(m_ListBox,ValueRect))
	{
		// set the value
		CComDispatchDriver DispatchDriver(m_ppUnk[0]);
		if(!SUCCEEDED(DispatchDriver.PutProperty(PropertyInfo.m_DispatchId,
			&CComVariant(m_ValueEditor.m_Value.c_str()))))
		{
			throw std::exception();
		}
	}
}

void CPropPageAll::PickValue(const CPropertyInfo &PropertyInfo)
{
	// get the item rect
	CRect ItemRect=GetItemRect(PropertyInfo.m_DispatchId);
	// get the value rect
	CRect ValueRect=GetItemValueRect(ItemRect);
	// get the value picker top right position
	CPoint TopRight(ValueRect.right-1,ValueRect.bottom);
	// convert to screen coordinates
	if(m_ListBox.ClientToScreen(&TopRight)==FALSE)
	{
		throw std::exception();
	}
	// load the value picker
	m_ValuePicker.m_Values.clear();
	for(long l=0; l<(long)PropertyInfo.m_PredefinedValues.size(); l++)
	{
		// create the value picker value
		CValuePickerValue Value;
		Value.m_Name=PropertyInfo.m_PredefinedValues[l].m_Name;
		Value.m_Value=PropertyInfo.m_PredefinedValues[l].m_Value;
		// match the value type to the property value type
		if(!SUCCEEDED(Value.m_Value.ChangeType(PropertyInfo.m_Value.vt)))
		{
			throw std::exception();
		}
		// update the vector
		m_ValuePicker.m_Values.push_back(Value);
	}
	// show the value picker
	m_ValuePicker.m_Value=PropertyInfo.m_Value;
	if(m_ValuePicker.PickValue(m_ListBox,TopRight,ValueRect.Width()+1))
	{
		// set the value
		CComDispatchDriver DispatchDriver(m_ppUnk[0]);
		if(!SUCCEEDED(DispatchDriver.PutProperty(PropertyInfo.m_DispatchId,
			&CComVariant(m_ValuePicker.m_Value))))
		{
			throw std::exception();
		}
	}
}

void CPropPageAll::PickColor(const CPropertyInfo &PropertyInfo)
{
	// get the item rect
	CRect ItemRect=GetItemRect(PropertyInfo.m_DispatchId);
	// get the value rect
	CRect ValueRect=GetItemValueRect(ItemRect);
	// get the color picker top right position
	CPoint TopRight(ValueRect.right-1,ValueRect.bottom);
	// convert to screen coordinates
	if(m_ListBox.ClientToScreen(&TopRight)==FALSE)
	{
		throw std::exception();
	}
	// show the color picker
	m_ColorPicker.m_Color=PropertyInfo.m_Value.lVal;
	if(m_ColorPicker.PickColor(m_ListBox,TopRight))
	{
		// set the color
		CComDispatchDriver DispatchDriver(m_ppUnk[0]);
		if(!SUCCEEDED(DispatchDriver.PutProperty(PropertyInfo.m_DispatchId,
			&CComVariant((long)m_ColorPicker.m_Color))))
		{
			throw std::exception();
		}
	}
}

void CPropPageAll::PickFont(const CPropertyInfo &PropertyInfo)
{
	USES_CONVERSION;

	// get the current font
	LOGFONT LogFont;
	if(PropertyInfo.m_Value.pdispVal!=NULL)
	{
		// get the IFont interface
		CComQIPtr<IFont> spFont(PropertyInfo.m_Value.pdispVal);
		if(spFont==NULL)
		{
			throw std::exception();
		}
		// get the font handle
		HFONT hFont=NULL;
		if(!SUCCEEDED(spFont->get_hFont(&hFont)))
		{
			throw std::exception();
		}
		// get the log font
		if(GetObject(hFont,sizeof(LOGFONT),&LogFont)==0)
		{
			throw std::exception();
		}
	}
	// show the font picker
	CHOOSEFONT cf;
	ZeroMemory(&cf,sizeof(CHOOSEFONT));
	cf.lStructSize=sizeof(CHOOSEFONT);
	cf.hwndOwner=*this;
	cf.lpLogFont=&LogFont;
	cf.Flags=CF_SCREENFONTS;
	// initialise using the current font
	if(PropertyInfo.m_Value.pdispVal!=NULL)
	{
		cf.Flags|=CF_INITTOLOGFONTSTRUCT;
	}
	if(ChooseFont(&cf))
	{
		// get the font description
		FONTDESC FontDesc;
		ZeroMemory(&FontDesc,sizeof(FONTDESC));
		FontDesc.cbSizeofstruct=sizeof(FONTDESC);
		FontDesc.lpstrName=T2W(LogFont.lfFaceName);
		FontDesc.cySize.int64=cf.iPointSize*1000;
		FontDesc.sWeight=(short)LogFont.lfWeight;
		FontDesc.sCharset=LogFont.lfCharSet;
		FontDesc.fItalic=LogFont.lfItalic;
		FontDesc.fUnderline=LogFont.lfUnderline;
		FontDesc.fStrikethrough=LogFont.lfStrikeOut;
		// create the font
		CComPtr<IFontDisp> spFontDisp;
		if(!SUCCEEDED(OleCreateFontIndirect(
			&FontDesc,IID_IFontDisp,(void**)&spFontDisp)))
		{
			throw std::exception();
		}
		// set the font
		CComDispatchDriver DispatchDriver(m_ppUnk[0]);
		if(!SUCCEEDED(DispatchDriver.PutProperty(PropertyInfo.m_DispatchId,
			&CComVariant(spFontDisp))))
		{
			throw std::exception();
		}
	}
}

void CPropPageAll::PickPicture(const CPropertyInfo &PropertyInfo)
{
	USES_CONVERSION;

	// file name buffer
	const DWORD MaxFileName=2048;
	TCHAR FileName[MaxFileName+1]=_T("");
	// show the file picker
	OPENFILENAME ofn;
	ZeroMemory(&ofn,sizeof(OPENFILENAME));
	ofn.lStructSize=sizeof(OPENFILENAME);
	ofn.hwndOwner=*this;
	ofn.lpstrFilter=
		_T("Image Files (*.bmp;*.ico;*.emf)\0*.bmp;*.ico;*.emf\0")
		_T("Bitmap Files (*.bmp)\0*.bmp\0")
		_T("Icon Files (*.ico)\0*.ico\0")
		_T("Metafile Files (*.emf)\0*.emf\0")
		_T("All Files (*.*)\0*.*\0");
	ofn.lpstrCustomFilter=NULL;
	ofn.nFilterIndex=1;
	ofn.lpstrFile=FileName;
	ofn.nMaxFile=MaxFileName;
	ofn.lpstrFileTitle=NULL;
	ofn.lpstrInitialDir=NULL;
	ofn.lpstrTitle=NULL;
	ofn.Flags=OFN_FILEMUSTEXIST|OFN_HIDEREADONLY;
	ofn.lpstrDefExt=NULL;
	if(GetOpenFileName(&ofn))
	{
		// a file extension is required
		if(ofn.nFileExtension==0)
		{
			throw std::exception();
		}
		// set the picture using a bitmap
		if(!_tcsicmp(FileName+ofn.nFileExtension,_T("bmp")))
		{
			SetPictureOnBitmap(PropertyInfo,T2CW(FileName));
		}
		// set the picture using an icon
		else if(!_tcsicmp(FileName+ofn.nFileExtension,_T("ico")))
		{
			SetPictureOnIcon(PropertyInfo,T2CW(FileName));
		}
		// set the picture using a metafile
		else if(!_tcsicmp(FileName+ofn.nFileExtension,_T("emf")))
		{
			SetPictureOnMetafile(PropertyInfo,T2CW(FileName));
		}
		// unknown file extension
		else
		{
			throw std::exception();
		}
	}
}

STDMETHODIMP CPropPageAll::Apply()
{
IMP_BEGIN
	// todo : implement this when required
IMP_END
	return RetVal;
}

STDMETHODIMP CPropPageAll::OnChanged(DISPID dispID)
{
IMP_BEGIN
	// if multiple properties have changed
	if(dispID==DISPID_UNKNOWN)
	{
		for(CDispatchIdToPropertyInfoMap::const_iterator 
				iter=m_PropertyMap.begin();
			iter!=m_PropertyMap.end();
			iter++)
		{
			// handle the property change
			PropertyChanged(iter->first);
		}
	}
	// only a single property has changed
	else
	{
		// some properties may not be in the property map
		if(m_PropertyMap.find(dispID)!=m_PropertyMap.end())
		{
			// handle the property change
			PropertyChanged(dispID);
		}
	}
IMP_END
	return S_OK;	// must always return S_OK
}

STDMETHODIMP CPropPageAll::OnRequestEdit(DISPID dispID)
{
IMP_BEGIN
IMP_END
	return S_OK;	// always allow the value to change
}

LRESULT CPropPageAll::OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
TRY
	// check there is only a single associated object
	if(m_nObjects!=1)
	{
		throw std::exception();
	}
	if(m_ppUnk[0]==NULL)
	{
		throw std::exception();
	}
	// wrap the list box
	HWND hWnd=GetDlgItem(IDC_LIST);
	if(hWnd==NULL)
	{
		throw std::exception();
	}
	if(m_ListBox.SubclassWindow(hWnd)==FALSE)
	{
		throw std::exception();
	}
	// load the property map
	LoadPropertyMap();
	// load the list box
	for(CDispatchIdToPropertyInfoMap::const_iterator
			iter=m_PropertyMap.begin();
		iter!=m_PropertyMap.end();
		iter++)
	{
		// add by dispatch id
		LRESULT Result=m_ListBox.AddString((LPCTSTR)iter->first);
		if(Result==LB_ERR or Result==LB_ERRSPACE)
		{
			throw std::exception();
		}
	}
	// connect the property notification sink
	(void)AtlAdvise(m_ppUnk[0],
		GetUnknown(),IID_IPropertyNotifySink,&m_ConnectionCookie);
CATCH_ALL
	// if an error occoured reset the list box
	if(Caught and m_ListBox.IsWindow())
	{
		m_ListBox.ResetContent();
	}
	return FALSE;
}

LRESULT CPropPageAll::OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
TRY
	// disconnect the property notification sink
	if(m_ConnectionCookie!=0)
	{
		(void)AtlUnadvise(m_ppUnk[0],
			IID_IPropertyNotifySink,m_ConnectionCookie);
	}
CATCH_ALL
	return 0;
}

LRESULT CPropPageAll::OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
TRY
	// get the client rect
	CRect ClientRect(0,0,0,0);
	if(GetClientRect(ClientRect)==FALSE)
	{
		throw std::exception();
	}
	// get the list box rect
	CRect ListBoxRect=ClientRect;
	ListBoxRect.InflateRect(-LIST_BORDER_WIDTH,-LIST_BORDER_HEIGHT);
	// size the list box
	if(m_ListBox.SetWindowPos(NULL,ListBoxRect,SWP_NOZORDER)==FALSE)
	{
		throw std::exception();
	}
CATCH_ALL
	return 0;
}

LRESULT CPropPageAll::OnMeasureItem(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
TRY
	// should be for the list box
	ATLASSERT(wParam==IDC_LIST);

	// get the measure item struct
	MEASUREITEMSTRUCT *pMeasureItemStruct=(MEASUREITEMSTRUCT *)lParam;
	// initialise the item height to something sensible
	DWORD MinItemHeight=8;
	pMeasureItemStruct->itemHeight=MinItemHeight;
	// create the display information context
	CDC dc=CreateIC(_T("Display"),NULL,NULL,NULL);
	if(dc==NULL)
	{
		throw std::exception();
	}
	// get the font height
	LOGFONT lf;
	if(GetObject(GetFont(),sizeof(LOGFONT),&lf)==0)
	{
		throw std::exception();
	}
	DWORD FontHeight=abs(lf.lfHeight);
	// set the item height
	DWORD ItemHeight=POINT_SIZE_TO_PIXELS(dc,FontHeight);
	ItemHeight+=1;	// allow room for the grid
	pMeasureItemStruct->itemHeight=max(ItemHeight,MinItemHeight);
CATCH_ALL
	return TRUE;
}

LRESULT CPropPageAll::OnCompareItem(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
TRY
	// should be for the list box
	ATLASSERT(wParam==IDC_LIST);

	// get the compare item struct
	COMPAREITEMSTRUCT *pCompareItemStruct=(COMPAREITEMSTRUCT *)lParam;
	// get the first item name
	std::wstring Name1=m_PropertyMap[pCompareItemStruct->itemData1].m_Name;
	// get the second item name
	std::wstring Name2=m_PropertyMap[pCompareItemStruct->itemData2].m_Name;
	// do the comparison
	int Result=_wcsicmp(Name1.c_str(),Name2.c_str());
	Result=max(Result,-1);
	Result=min(Result,1);
	return Result;
CATCH_ALL
	// should not normally get to here
	ATLASSERT(FALSE);
	return 0;
}

LRESULT CPropPageAll::OnDrawItem(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
TRY
	// should be for the list box
	ATLASSERT(wParam==IDC_LIST);

	// get the draw item struct
	DRAWITEMSTRUCT *pDrawItemStruct=(DRAWITEMSTRUCT *)lParam;
	// check there is something to draw
	if(pDrawItemStruct->itemID==-1)
	{
		throw std::exception();
	}
	// wrap the device context
	CDCHandle hDC=pDrawItemStruct->hDC;
	// get the item rect
	CRect ItemRect=pDrawItemStruct->rcItem;
	// determine if the item is selected
	BOOL IsSelected=(pDrawItemStruct->itemState & ODS_SELECTED) ? TRUE : FALSE;

	// create GDI object wrappers
	// this must be done before the memory device context state is saved
	CBitmap Bitmap;
	// create the memory device context
	CDC dc;
	if(dc.CreateCompatibleDC(hDC)==NULL)
	{
		throw std::exception();
	}
	// save the memory device context state
	CAutoDCState AutoDCState(dc);
	// create the bitmap and select it into the memory device context
	if(Bitmap.CreateCompatibleBitmap(
		hDC,ItemRect.Width(),ItemRect.Height())==NULL)
	{
		throw std::exception();
	}
	if(dc.SelectBitmap(Bitmap)==NULL)
	{
		throw std::exception();
	}
	// map coordinates to the list box
	if(dc.SetWindowOrg(ItemRect.left,ItemRect.top)==FALSE)
	{
		throw std::exception();
	}

	// get property info
	CPropertyInfo PropertyInfo=m_PropertyMap[pDrawItemStruct->itemData];

	// draw the item grid
	DrawItemGrid(CDCHandle(dc),PropertyInfo,ItemRect,IsSelected);
	// draw the item name
	DrawItemName(CDCHandle(dc),PropertyInfo,ItemRect,IsSelected);
	// draw the item value
	DrawItemValue(CDCHandle(dc),PropertyInfo,ItemRect,IsSelected);
	// draw the item button
	if(IsSelected)
	{
		DrawItemButton(CDCHandle(dc),PropertyInfo,ItemRect,IsSelected);
	}

	// update the screen
	if(hDC.BitBlt(
		ItemRect.left,ItemRect.top,ItemRect.Width(),ItemRect.Height(),
			dc,ItemRect.left,ItemRect.top,SRCCOPY)==FALSE)
	{
		throw std::exception();
	}
CATCH_ALL
	return TRUE;
}

LRESULT CPropPageAll::OnSelchangeList(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
{
TRY
	// get the selected item
	DWORD ItemIndex=m_ListBox.GetCurSel();
	if(ItemIndex==LB_ERR)
	{
		throw std::exception();
	}
	// get the item data
	DWORD ItemData=m_ListBox.GetItemData(ItemIndex);
	if(ItemData==LB_ERR)
	{
		throw std::exception();
	}
	// get property info
	CPropertyInfo PropertyInfo=m_PropertyMap[ItemData];
	// ensure the property value is up to date
	// this is required for properties which do not support IPropertyNotify
	PropertyChanged(PropertyInfo.m_DispatchId);
CATCH_ALL
	return 0;
}

LRESULT CPropPageAll::OnListKeyDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
TRY
	// assume the message will not be handled
	bHandled=FALSE;
	// get the selected item
	DWORD ItemIndex=m_ListBox.GetCurSel();
	if(ItemIndex==LB_ERR)
	{
		throw std::exception();
	}
	// get the item data
	DWORD ItemData=m_ListBox.GetItemData(ItemIndex);
	if(ItemData==LB_ERR)
	{
		throw std::exception();
	}
	// get property info
	CPropertyInfo PropertyInfo=m_PropertyMap[ItemData];
	// if the delete or backspace key was pressed
	if((wParam==VK_DELETE or wParam==VK_BACK) and
		// and the property type is a picture
		PropertyInfo.m_Type==CPropertyInfo::TypePicture)
	{
		// clear the picture
		ClearPicture(PropertyInfo);
		bHandled=TRUE;
		// ensure the property value is up to date
		// this is required for properties which do not support IPropertyNotify
		PropertyChanged(PropertyInfo.m_DispatchId);
	}
CATCH_ALL
	return 0;
}

LRESULT CPropPageAll::OnListLButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
TRY
	// assume the message will not be handled
	bHandled=FALSE;
	// get the cursor position
	CPoint CursorPos(GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam));
	// determine which item was clicked
	DWORD Count=m_ListBox.GetCount();
	if(Count==LB_ERR or Count==0)
	{
		throw std::exception();
	}
	BOOL Outside=FALSE;
	DWORD ItemIndex=m_ListBox.ItemFromPoint(CursorPos,Outside);
	// get the item rect
	CRect ItemRect(0,0,0,0);
	if(m_ListBox.GetItemRect(ItemIndex,ItemRect)==LB_ERR)
	{
		throw std::exception();
	}
	// get the item data
	DWORD ItemData=m_ListBox.GetItemData(ItemIndex);
	if(ItemData==LB_ERR)
	{
		throw std::exception();
	}
	// get property info
	CPropertyInfo PropertyInfo=m_PropertyMap[ItemData];
	// if the currently selected item was clicked
	if(ItemIndex==m_ListBox.GetCurSel())
	{
		switch(PropertyInfo.m_Type)
		{
		// show the value editor or value picker
		case CPropertyInfo::TypeString:
			if(PropertyInfo.m_PredefinedValues.size()==0)
			{
				bHandled=TRUE;
				// there are no predefined values
				EditValue(PropertyInfo);
				break;
			}
			// fall through
		case CPropertyInfo::TypeBool:
		case CPropertyInfo::TypeEnum:
			if(GetItemButtonRect(ItemRect).PtInRect(CursorPos))
			{
				bHandled=TRUE;
				PickValue(PropertyInfo);
			}
			break;
		// show the color picker
		case CPropertyInfo::TypeColor:
			if(GetItemButtonRect(ItemRect).PtInRect(CursorPos))
			{
				bHandled=TRUE;
				PickColor(PropertyInfo);
			}
			break;
		// show the font picker
		case CPropertyInfo::TypeFont:
			if(GetItemButtonRect(ItemRect).PtInRect(CursorPos))
			{
				bHandled=TRUE;
				PickFont(PropertyInfo);
			}
			break;
		// show the picture picker
		case CPropertyInfo::TypePicture:
			if(GetItemButtonRect(ItemRect).PtInRect(CursorPos))
			{
				bHandled=TRUE;
				PickPicture(PropertyInfo);
			}
			break;
		// unknown
		default:
			ATLASSERT(FALSE);
			break;
		}
		// ensure the property value is up to date
		// this is required for properties which do not support IPropertyNotify
		if(bHandled)
		{
			PropertyChanged(PropertyInfo.m_DispatchId);
		}
	}
CATCH_ALL
	return 0;
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
United Kingdom United Kingdom
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions