Click here to Skip to main content
15,896,606 members
Articles / Desktop Programming / WTL

Form Designer

26 Jul 2021CPOL24 min read 352.2K   82.5K   230  
Component for adding scriptable forms capabilities to an application.
// PropertyInfo.cpp: implementation of the CPropertyInfo class.
//
// Author : David Shepherd
//			Copyright (c) 2002, DaeDoe-Software
//
/////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "PropertyInfo.h"

/////////////////////////////////////////////////////////////////////////////
// CPredefinedValue

CPredefinedValue::CPredefinedValue()
{
	// initialise everything
}

CPredefinedValue::~CPredefinedValue()
{
	// clean up
}

/////////////////////////////////////////////////////////////////////////////
// CPropertyInfo

CPropertyInfo::CPropertyInfo()
{
	// initialise everything
	m_DispatchId=DISPID_UNKNOWN;
	m_VariantType=VT_EMPTY;
	m_Type=TypeUnknown;
}

CPropertyInfo::~CPropertyInfo()
{
	// clean up
}

void CPropertyInfo::GetDispatchType(const CComPtr<ITypeInfo> &spTypeInfo)
{
	// get type attributes
	CAutoTypeAttrPtr pTypeAttr(spTypeInfo);
	// check for the font dispatch interface
	if(pTypeAttr->guid==IID_IFontDisp)
	{
		m_VariantType=VT_DISPATCH;
		m_Type=TypeFont;
	}
	// check for the picture dispatch interface
	if(pTypeAttr->guid==IID_IPictureDisp)
	{
		m_VariantType=VT_DISPATCH;
		m_Type=TypePicture;
	}
}

void CPropertyInfo::GetUserDefinedType(
	const CComPtr<ITypeInfo> &spTypeInfo,const TYPEDESC &TypeDesc)
{
	// get type info
	CComPtr<ITypeInfo> spTypeInfoUser;
	if(!SUCCEEDED(spTypeInfo->GetRefTypeInfo(TypeDesc.hreftype,&spTypeInfoUser)))
	{
		throw std::exception();
	}
	// get type attributes
	CAutoTypeAttrPtr pTypeAttrUser(spTypeInfoUser);
	// determine the user defined property type
	switch(pTypeAttrUser->typekind)
	{
	// alias
	case TKIND_ALIAS:
		// check for the color alias
		if(pTypeAttrUser->guid==GUID_COLOR)
		{
			m_VariantType=VT_I4;
			m_Type=TypeColor;
		}
		else	// some other alias
		{
			GetType(spTypeInfoUser,pTypeAttrUser->tdescAlias);
		}
		break;
	// enumerator
	case TKIND_ENUM:
		// set the enumerator predefined values
		SetPredefinedValuesOnEnum(spTypeInfoUser);
		m_VariantType=VT_I4;
		m_Type=TypeEnum;
		break;
	// dispatch interface
	case TKIND_DISPATCH:
		// determine the dispatch interface type
		GetDispatchType(spTypeInfoUser);
		break;
	// other
	default:
		// this is ok
		break;
	}
}

void CPropertyInfo::GetType(
	const CComPtr<ITypeInfo> &spTypeInfo,const TYPEDESC &TypeDesc)
{
	// determine the property type
	m_VariantType=TypeDesc.vt;
	switch(m_VariantType)
	{
	// simple type
	case VT_I2:
	case VT_I4:
	case VT_R4:
	case VT_R8:
	case VT_CY:
	case VT_DATE:
	case VT_BSTR:
	case VT_ERROR:
	case VT_I1:
	case VT_UI1:
	case VT_UI2:
	case VT_UI4:
	case VT_I8:
	case VT_UI8:
	case VT_INT:
	case VT_UINT:
		// all simple types can be represented as a string
		m_Type=TypeString;
		break;
	// boolean type
	case VT_BOOL:
		// set the boolean predefined values
		SetPredefinedValuesOnBool();
		m_Type=TypeBool;
		break;
	// pointer type
	case VT_PTR:
		// determine the type pointed to
		GetType(spTypeInfo,*TypeDesc.lptdesc);
		break;
	// user defined type
	case VT_USERDEFINED:
		// determine the user defined type
		GetUserDefinedType(spTypeInfo,TypeDesc);
		break;
	// variant type
	case VT_VARIANT:
		// force this type to a string
		m_VariantType=VT_BSTR;
		m_Type=TypeString;
		break;
	// dispatch type
	case VT_DISPATCH:
		// determine the dispatch interface type
		GetDispatchType(spTypeInfo);
		break;
	// other type
	default:
		// this is ok
		break;
	}
}

void CPropertyInfo::GetPredefinedValues(const CComPtr<IDispatch> &spDispatch)
{
	// allows type independant auto release of CoTaskMem
	typedef CAutoCoTaskMem<void> CAutoCoFree;

	// get the IPerPropertyBrowsing interface
	CComQIPtr<IPerPropertyBrowsing> spPerPropertyBrowsing(spDispatch);
	if(spPerPropertyBrowsing==NULL)
	{
		return;	// per property browsing is not available
	}
	// retreive predefined values
	CALPOLESTR StringArray	= { 0,NULL };
	CADWORD CookieArray		= { 0,NULL };
	if(!SUCCEEDED(spPerPropertyBrowsing->GetPredefinedStrings(
		m_DispatchId,&StringArray,&CookieArray)))
	{
		return;	// per property browsing is not available
	}
	ATLASSERT(StringArray.cElems==CookieArray.cElems);
	DWORD Count=StringArray.cElems;
	// auto release the string array
	CAutoCoFree pStringElements(StringArray.pElems);
	// auto release the cookie array
	CAutoCoFree pCookieElements(CookieArray.pElems);
	// auto release each string
	// todo : if possible prevent CoTaskMem leaks if new throws
	auto_array_ptr<CAutoCoFree> pStrings(new CAutoCoFree[Count]);
	for(long l=0; l<(long)Count; l++)
	{
		pStrings.get()[l]=CAutoCoFree(StringArray.pElems[l]);
	}
	// add all predefined values to the vector
	if(Count)
	{
		// remove lower priority values
		m_PredefinedValues.clear();
	}
	for(l=0; l<(long)Count; l++)
	{
		// create the predefined value
		CPredefinedValue PredefinedValue;
		// name
		PredefinedValue.m_Name=StringArray.pElems[l];
		// value
		if(!SUCCEEDED(spPerPropertyBrowsing->GetPredefinedValue(
			m_DispatchId,CookieArray.pElems[l],&PredefinedValue.m_Value)))
		{
			throw std::exception();
		}
		// update the vector
		m_PredefinedValues.push_back(PredefinedValue);
	}
}

void CPropertyInfo::SetPredefinedValuesOnBool()
{
	// the vector should be empty
	ATLASSERT(m_PredefinedValues.empty());

	// create the true value
	CPredefinedValue PredefinedValue;
	PredefinedValue.m_Name=L"True";
	PredefinedValue.m_Value=VARIANT_TRUE;
	// ensure the variant type is VT_BOOL
	if(!SUCCEEDED(PredefinedValue.m_Value.ChangeType(VT_BOOL)))
	{
		throw std::exception();
	}
	// update the vector
	m_PredefinedValues.push_back(PredefinedValue);
	// create the false value
	PredefinedValue.m_Name=L"False";
	PredefinedValue.m_Value=VARIANT_FALSE;
	// ensure the variant type is VT_BOOL
	if(!SUCCEEDED(PredefinedValue.m_Value.ChangeType(VT_BOOL)))
	{
		throw std::exception();
	}
	// update the vector
	m_PredefinedValues.push_back(PredefinedValue);
}

void CPropertyInfo::SetPredefinedValuesOnEnum(
	const CComPtr<ITypeInfo> &spTypeInfo)
{
	// the vector should be empty
	ATLASSERT(m_PredefinedValues.empty());

	// get type attributes
	CAutoTypeAttrPtr pTypeAttr(spTypeInfo);
	// add all enumerator values to the vector
	for(long l=0; l<pTypeAttr->cVars; l++)
	{
		// get the variable description
		CAutoVarDescPtr pVarDesc(l,spTypeInfo);
		// create the predefined value
		CPredefinedValue PredefinedValue;
		// name prefix
		CComVariant Value=*pVarDesc->lpvarValue;
		if(!SUCCEEDED(Value.ChangeType(VT_BSTR)))
		{
			throw std::exception();
		}
		std::wstring NamePrefix=BSTR2W(Value.bstrVal);
		NamePrefix+=L" - ";
		// name
		UINT Count=0;
		CComBSTR Name;
		if(!SUCCEEDED(spTypeInfo->GetNames(pVarDesc->memid,&Name,1,&Count)))
		{
			throw std::exception();
		}
		PredefinedValue.m_Name=NamePrefix+BSTR2W(Name);	// include the prefix
		// value
		PredefinedValue.m_Value=*pVarDesc->lpvarValue;
		// update the vector
		m_PredefinedValues.push_back(PredefinedValue);
	}
}

void CPropertyInfo::Create(const CComPtr<IDispatch> &spDispatch,DISPID DispatchId,
	const TYPEDESC &TypeDesc)
{
	// object reuse is not allowed
	ATLASSERT(m_VariantType==VT_EMPTY);

	// get type info
	CComPtr<ITypeInfo> spTypeInfo;
	if(!SUCCEEDED(spDispatch->GetTypeInfo(0,LOCALE_USER_DEFAULT,&spTypeInfo)))
	{
		throw std::exception();
	}
	// create property info
	// name
	UINT Count=0;
	CComBSTR Name;
	if(!SUCCEEDED(spTypeInfo->GetNames(DispatchId,&Name,1,&Count)))
	{
		throw std::exception();
	}
	m_Name=BSTR2W(Name);
	// dispatch id
	m_DispatchId=DispatchId;
	// type
	GetType(spTypeInfo,TypeDesc);
	// predefined values
	GetPredefinedValues(spDispatch);
	// value
	if(m_Type!=TypeUnknown and m_Type!=TypeInvalid)
	{
		GetValue(spDispatch);
	}
}

void CPropertyInfo::GetValue(const CComPtr<IDispatch> &spDispatch)
{
	// the property type should have been determined
	ATLASSERT(m_Type!=TypeUnknown and m_Type!=TypeInvalid);

	// retreive the property value
	CComVariant Value;
	CComDispatchDriver DispatchDriver(spDispatch);
	if(!SUCCEEDED(DispatchDriver.GetProperty(m_DispatchId,&Value)))
	{
		// this may fail for valid reasons so clear the current value
		if(!SUCCEEDED(m_Value.Clear()))
		{
			// unexpected
			ATLASSERT(FALSE);
		}
		return;
	}
	// change the value to the correct type
	if(!SUCCEEDED(Value.ChangeType(m_VariantType)))
	{
		// this may fail for valid reasons so clear the current value
		if(!SUCCEEDED(m_Value.Clear()))
		{
			// unexpected
			ATLASSERT(FALSE);
		}
		return;
	}
	m_Value=Value;	// releases the old value if required
}

std::wstring CPropertyInfo::GetValueName() const
{
	// the value should be set
	ATLASSERT(m_Value.vt!=VT_EMPTY);

	// return the property value name
	std::wstring Name;
	for(long l=0; l<(long)m_PredefinedValues.size() and Name.empty(); l++)
	{
		CPredefinedValue PredefinedValue=m_PredefinedValues[l];
		// match the predefined value type to the property value type
		if(!SUCCEEDED(PredefinedValue.m_Value.ChangeType(m_Value.vt)))
		{
			throw std::exception();
		}
		// compare values
		if(PredefinedValue.m_Value==m_Value)
		{
			Name=PredefinedValue.m_Name;
		}
	}
	return Name;
}

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