www.pudn.com > PropBrowser_demo.zip > PropertyBrowserPage.h


// PropertyBrowserPage.h: interface for the CPropertyBrowserPage class. 
// 
////////////////////////////////////////////////////////////////////// 
 
#if !defined(AFX_PROPERTYBROWSERPAGE_H__12892D12_03FF_11D3_84F5_00C04FD1E2CA__INCLUDED_) 
#define AFX_PROPERTYBROWSERPAGE_H__12892D12_03FF_11D3_84F5_00C04FD1E2CA__INCLUDED_ 
 
#if _MSC_VER >= 1000 
#pragma once 
#endif // _MSC_VER >= 1000 
#include  
 
/* 
Property Browser Page v1.0 
Template class that provides automatic property page for ATL-based ActiveX Controls 
Copyrights, (c) 1999 Vladimir Scherbakov (rise@alt.ru) 
 
*/ 
 
 
/* 
 CPropBrowseWnd custom messages 
 */ 
#define WM_PB_STARTEDIT		(WM_APP + 12) 
#define WM_PB_WARNING		(WM_APP + 14) 
#define WM_PB_PROPCHANGE	(WM_APP + 16) 
#define WM_PB_SHOWPOPUP		(WM_APP + 15) 
/* 
 Color picker custom message 
 */ 
#define WM_ENDPICK			(WM_USER + 11)  
class CPropBrowseWnd; 
class CPBProperty; 
 
 
 
// used in color picker's 'System' tab 
typedef struct 
	{ 
	OLE_COLOR c; 
	TCHAR *szText; 
	} PBSystemColorEntry; 
 
class CPBColorPicker : public CWindowImpl 
	{ 
	public: 
		OLE_COLOR m_clr; 
  
		DECLARE_WND_CLASS(_T("PBColorPicker_1_0")) 
		HRESULT PickColor(CPBProperty *pProp,RECT* r); 
		BOOL CreateWnd(CPropBrowseWnd * pParent); 
		CPBColorPicker():m_wndTab(WC_TABCONTROL,this,1),m_wndPalette(_T("BUTTON"),this,2), 
			m_wndList(_T("LISTBOX"),this,3) 
			{ 
			SetRectEmpty(&m_rcDisplay); 
			m_bTrackPaletteColor = FALSE; 
			m_nTrackIndex = -1; 
			} 
		~CPBColorPicker() 
			{ 
			} 
	LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); 
	LRESULT OnDrawItem(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); 
 
	LRESULT OnActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 
		{ 
		WORD fActive = LOWORD(wParam); 
		if(fActive == WA_INACTIVE) 
			{ 
			ShowWindow(SW_HIDE);// hide window now (if drag other window's caption) 
			PostMessage(WM_ENDPICK); 
			} 
		return 0; 
		} 
 
 
	LRESULT OnKillFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 
		{ 
		HWND hwndGetFocus = (HWND) wParam;  
		if(::GetParent(hwndGetFocus) != m_hWnd) 
			PostMessage(WM_ENDPICK,0,0); 
		return 0; 
		} 
	LRESULT OnKillFocusChild(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled) 
		{ 
		HWND hwndGetFocus = GetFocus(); 
		if(hwndGetFocus != m_hWnd && ::GetParent(hwndGetFocus) != m_hWnd) 
			PostMessage(WM_ENDPICK,0,0); 
		return 0; 
		} 
 
	LRESULT OnTabSelChange(int idCtrl, LPNMHDR pnmh, BOOL& bHandled) 
		{ 
		int nSel = TabCtrl_GetCurSel(m_wndTab.m_hWnd); 
		m_wndPalette.ShowWindow(!nSel); 
		m_wndList.ShowWindow(nSel); 
		return 0; 
		}; 
 
	LRESULT OnSelChangeList(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled) 
		{ 
		int nSel = m_wndList.SendMessage(LB_GETCURSEL); 
		if(nSel >= 0) 
			{ 
			m_clr = m_SysColors[nSel].c; 
			PostMessage(WM_ENDPICK,1); 
			} 
		return 0; 
		} 
 
	LRESULT OnLButtonDownPalette(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); 
	LRESULT OnLButtonUpPalette(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); 
	LRESULT OnMouseMovePalette(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); 
	LRESULT OnEraseBkGndPalette(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 
		{ 
		return 1; 
		} 
	LRESULT OnParentNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 
		{ 
		WORD fwEvent = LOWORD(wParam);  // event flags  
		WORD idChild = HIWORD(wParam);  // identifier of child window  
		if(fwEvent == WM_RBUTTONDOWN && m_wndPalette.IsWindowVisible()) 
			{ 
			POINT pt;pt.x = (int)LOWORD(lParam);pt.y = (int)HIWORD(lParam); 
			ClientToScreen(&pt); 
			m_wndPalette.ScreenToClient(&pt); 
			int nIdx = HitTestPalette(pt); 
			if(nIdx > 47) 
				m_nChooseColor = nIdx; 
			} 
		return 0; 
		} 
 
 
	BEGIN_MSG_MAP(CPBColorPicker) 
		MESSAGE_HANDLER(WM_CREATE,OnCreate) 
		MESSAGE_HANDLER(WM_DRAWITEM,OnDrawItem) 
		MESSAGE_HANDLER(WM_KILLFOCUS,OnKillFocus) 
		MESSAGE_HANDLER(WM_ACTIVATE,OnActivate) 
		MESSAGE_HANDLER(WM_PARENTNOTIFY,OnParentNotify) 
 
		NOTIFY_HANDLER(1,TCN_SELCHANGE,OnTabSelChange) 
		COMMAND_HANDLER(3,LBN_KILLFOCUS,OnKillFocusChild) 
		COMMAND_HANDLER(2,BN_KILLFOCUS,OnKillFocusChild) 
		COMMAND_HANDLER(3,LBN_SELCHANGE,OnSelChangeList) 
 
	ALT_MSG_MAP(1) 
	ALT_MSG_MAP(2) 
		MESSAGE_HANDLER(WM_ERASEBKGND,OnEraseBkGndPalette) 
		MESSAGE_HANDLER(WM_LBUTTONDOWN,OnLButtonDownPalette) 
		MESSAGE_HANDLER(WM_LBUTTONUP,OnLButtonUpPalette) 
		MESSAGE_HANDLER(WM_MOUSEMOVE,OnMouseMovePalette) 
 
	ALT_MSG_MAP(3) 
	END_MSG_MAP() 
	protected: 
		int m_nChooseColor; 
		void SetStartColor(OLE_COLOR c); 
		int m_nTrackIndex; 
		int GetPaletteItemRect(int nIdx,RECT *r); 
		int HitTestPalette(POINT p); 
		void DrawListItem(LPDRAWITEMSTRUCT di); 
		void DrawPalette(LPDRAWITEMSTRUCT di); 
		void CalcSizes(); 
		int m_nCellSize; 
		BOOL  m_bTrackPaletteColor; 
		CContainedWindow m_wndTab; 
		CContainedWindow m_wndPalette; 
		CContainedWindow m_wndList; 
		RECT m_rcDisplay; 
		CPropBrowseWnd * m_pParent; 
		CPBProperty *m_pProp; 
	static PBSystemColorEntry m_SysColors[]; 
	static OLE_COLOR m_clrPalette[]; 
	}; 
 
 
class CPBProperty 
	{ 
	public: 
		LPOLESTR GetNextPredefinedString(LPCTSTR szText); 
		BOOL SetNewValue(VARIANT *pVar); 
		CComBSTR m_bstrText; 
		BOOL m_bDirty; 
		BOOL SetNewText(BSTR bstrNewText); 
		CALPOLESTR     m_castr; 
		CADWORD        m_cadw;  
		VARIANT * m_pvPredefinedValues; 
	CPBProperty() 
		{ 
		::ZeroMemory(&m_castr,sizeof(m_castr)); 
		::ZeroMemory(&m_cadw,sizeof(m_cadw)); 
		m_bDirty = FALSE; 
		m_ulHelpContext = 0; 
		m_DisplayType = PBT_ERROR; 
		m_id = 0; 
		m_vt = VT_NULL; 
		} 
	virtual ~CPBProperty() 
		{ 
		ClearPredefinedValues(); 
		} 
 
	void ClearPredefinedValues() 
		{ 
		if(m_cadw.cElems > 0) 
			{ 
			for(ULONG i=0;i < m_cadw.cElems;i++) 
				VariantClear(m_pvPredefinedValues + i); 
			CoTaskMemFree((void *)m_cadw.pElems); 
			if(m_pvPredefinedValues) 
				CoTaskMemFree((void *)m_pvPredefinedValues); 
			m_pvPredefinedValues = NULL; 
			m_cadw.cElems = 0; 
			} 
		if(m_castr.cElems > 0) 
			{ 
			for (ULONG i=0; i < m_castr.cElems; i++) 
				CoTaskMemFree((void *)m_castr.pElems[i]);   
			CoTaskMemFree((void *)m_castr.pElems);  
			m_castr.cElems = 0; 
			} 
 
		} 
	HRESULT InitPredefinedValues(IDispatch * pDisp) 
		{ 
		ULONG i = 0;HRESULT hr = S_OK; 
		IPerPropertyBrowsing *pPropBrowse = NULL; 
		ClearPredefinedValues(); 
		if(m_castr.cElems <=0 && m_DisplayType == PBT_BOOL) 
			{ 
			m_castr.cElems = 2; 
			m_castr.pElems = (LPOLESTR *)CoTaskMemAlloc(2*sizeof(LPOLESTR)); 
			m_cadw.cElems = 2; 
			m_cadw.pElems = (DWORD FAR*)CoTaskMemAlloc(2*sizeof(DWORD FAR*)); 
			if (NULL==m_castr.pElems || NULL==m_cadw.pElems) 
				{ 
				hr = E_OUTOFMEMORY;goto error; 
				} 
			for (int i = 0; i < 2; i++) 
				{ 
				m_castr.pElems[i] = (LPOLESTR)CoTaskMemAlloc(128); 
				if (!m_castr.pElems[i]) 
					{ 
					hr = E_OUTOFMEMORY;goto error; 
					} 
				} 
			ocscpy(m_castr.pElems[0],L"False");m_cadw.pElems[0] = 0; 
			ocscpy(m_castr.pElems[1],L"True");m_cadw.pElems[1] = 1; 
			m_pvPredefinedValues = (VARIANT*) CoTaskMemAlloc(m_cadw.cElems*sizeof(VARIANT)); 
			m_pvPredefinedValues[0].vt = VT_BOOL;m_pvPredefinedValues[0].boolVal = FALSE; 
			m_pvPredefinedValues[1].vt = VT_BOOL;m_pvPredefinedValues[1].boolVal = -1; 
			return S_OK; 
			} 
		hr = pDisp->QueryInterface(IID_IPerPropertyBrowsing,(void**)&pPropBrowse); 
		if(FAILED(hr)) goto error; 
		hr = pPropBrowse->GetPredefinedStrings(m_id,&m_castr,&m_cadw); 
		if(FAILED(hr)) goto error; 
		if(m_cadw.cElems > 0) 
			{ 
			m_pvPredefinedValues = (VARIANT*) CoTaskMemAlloc(m_cadw.cElems*sizeof(VARIANT)); 
			if(!m_pvPredefinedValues) { hr = E_OUTOFMEMORY;goto error;} 
			} 
		for(i=0;i < m_cadw.cElems;i++) 
			{ 
			VariantInit(m_pvPredefinedValues + i); 
			hr = pPropBrowse->GetPredefinedValue(m_id,m_cadw.pElems[i],m_pvPredefinedValues + i); 
			if(FAILED(hr)) 
				goto error; 
			} 
		pPropBrowse->Release(); 
		return hr; 
		error: 
		ClearPredefinedValues(); 
		if(pPropBrowse) pPropBrowse->Release(); 
		return hr; 
		} 
	enum PBPropTypes 
		{ 
		PBT_ERROR	=0, 
		PBT_STR		=1, 
		PBT_COLOR	=2, 
		PBT_FONT	=3, 
		PBT_ENUM	=4, 
		PBT_PICTURE	=5, 
		PBT_BOOL	=6 
		} m_DisplayType ; 
 
	static CPBProperty::PBPropTypes GetPropType(LPTYPEINFO pTypeInfo,TYPEDESC* lptd,VARTYPE *vt) 
		{ 
		LPTYPEINFO lpUserTI=NULL; 
		TYPEATTR* ta=NULL; 
		PBPropTypes PropType = PBT_ERROR; 
		if(vt) 
			*vt = lptd->vt; 
		switch(lptd->vt) 
			{ 
				// simple types supported 
			case VT_UI1: 
			case VT_UI2: 
			case VT_UI4: 
			case VT_UI8: 
			case VT_INT: 
			case VT_UINT: 
			case VT_I8: 
			case VT_I1: 
			case VT_I2: 
			case VT_I4: 
			case VT_R4: 
			case VT_R8: 
			case VT_BSTR: 
			case VT_DATE: 
			case VT_CY: 
				return PBT_STR; 
			case VT_BOOL: 
				return PBT_BOOL; 
			case VT_USERDEFINED: 
				{ 
				// looks for OLE_COLOR and user-defined enums 
				HRESULT hr; 
		        hr = pTypeInfo->GetRefTypeInfo(lptd->hreftype, 
					&lpUserTI); 
		        if (FAILED(hr)) goto error; 
				hr = lpUserTI->GetTypeAttr(&ta); 
		        if (FAILED(hr)) goto error; 
				switch(ta->typekind) 
					{ 
					case TKIND_ALIAS: 
						if(InlineIsEqualGUID(ta->guid,GUID_COLOR)) 
							PropType = PBT_COLOR; 
						else 
						PropType= GetPropType(lpUserTI,&ta->tdescAlias,vt); 
						break; 
					case TKIND_ENUM: 
 
						if(InlineIsEqualGUID(ta->guid,GUID_COLOR)) 
							PropType = PBT_COLOR; 
						else 
						PropType = PBT_ENUM; 
						break; 
					case TKIND_DISPATCH: 
						if(InlineIsEqualGUID(ta->guid,IID_IFontDisp)) 
							PropType = PBT_FONT; 
						else if(InlineIsEqualGUID(ta->guid,IID_IPictureDisp)) 
							PropType = PBT_PICTURE; 
						else PropType = PBT_ERROR; 
						break; 
					default: 
						PropType = PBT_ERROR; 
					} 
				lpUserTI->ReleaseTypeAttr(ta); 
				lpUserTI->Release(); 
				} 
				break; 
			case VT_PTR: 
				// looks for IFontDisp* and IPictureDisp* 
				PropType = GetPropType(pTypeInfo,lptd->lptdesc,vt); 
				break; 
			default: 
				// any others are unsupported 
				ATLTRACE(_T("[CPropertyBrowserPage]:Unsupported property type\n")); 
				goto error; 
			} 
		return PropType; 
		error: 
		if(ta && lpUserTI) 
			lpUserTI->ReleaseTypeAttr(ta); 
		if(lpUserTI) 
			lpUserTI->Release(); 
		return PBT_ERROR; 
		} 
	static HRESULT Create(ITypeInfo *pTypeInfo,unsigned int nIdx, CPBProperty ** ppProp) 
		{ 
		CPBProperty* pProp = new CPBProperty(); 
		FUNCDESC *fd=NULL; 
		PBPropTypes propType = PBT_ERROR; 
		LPTYPEINFO ptinfoUserDefined = NULL; 
		if(!pProp) 
			return E_OUTOFMEMORY; 
		_ASSERT(pTypeInfo); 
		HRESULT hr = pTypeInfo->GetFuncDesc(nIdx,&fd); 
		if(FAILED(hr)) goto error; 
		pProp->m_id = fd->memid; 
		pProp->m_invkind = fd->invkind; 
		hr = pTypeInfo->GetDocumentation(pProp->m_id, 
										&pProp->m_bstrName.m_str, 
										&pProp->m_bstrDocumentation.m_str, 
										&pProp->m_ulHelpContext, 
										&pProp->m_bstrHelpFile 
										); 
 
		if(FAILED(hr)) goto error; 
		if(fd->cParams != 1) 
			{ 
			hr = E_INVALIDARG; 
			goto error; 
			} 
		propType = GetPropType(pTypeInfo,&fd->lprgelemdescParam[0].tdesc,&pProp->m_vt); 
		if(propType == PBT_ERROR) 
			{ 
			hr = E_INVALIDARG; 
			goto error; 
			} 
		pProp->m_DisplayType = propType; 
 
		pTypeInfo->ReleaseFuncDesc(fd); 
		(*ppProp) = pProp; 
		return S_OK; 
		error: 
		if(pProp) delete pProp; 
		if(fd) pTypeInfo->ReleaseFuncDesc(fd); 
		if(ptinfoUserDefined) ptinfoUserDefined->Release(); 
		(*ppProp) = NULL; 
		return hr; 
		} 
	MEMBERID m_id; 
	INVOKEKIND m_invkind; 
	VARTYPE m_vt; 
	CComVariant	m_value;  
	CComBSTR m_bstrName; 
	CComBSTR m_bstrDocumentation; 
	CComBSTR m_bstrHelpFile; 
	unsigned long m_ulHelpContext; 
	protected: 
	}; 
static const int nAllocSize = 32; 
 
class CPBPropertyHolder 
	{ 
	public: 
		CPBPropertyHolder() 
			{ 
			m_ppProp = NULL; 
			m_nCount = 0; 
			m_nAllocCount = 0; 
			} 
		virtual ~CPBPropertyHolder() 
			{ 
			CleanUp(); 
			} 
		BOOL Init(IDispatch *pDisp) 
			{ 
			ITypeInfo *pTypeInfo = NULL; 
 
			TYPEATTR* ta; 
			HRESULT hr =  pDisp->GetTypeInfo(0,NULL,&pTypeInfo); 
			if(FAILED(hr)) {_ASSERT(FALSE); return FALSE;} 
			hr =pTypeInfo->GetTypeAttr(&ta); 
			if(FAILED(hr)) {_ASSERT(FALSE); return FALSE;} 
			for(int i=0;i < ta->cFuncs;i++) 
				AddFuncProperty(pTypeInfo,i); 
			pTypeInfo->ReleaseTypeAttr(ta); 
			pTypeInfo->Release(); 
			pTypeInfo = NULL; 
			CComDispatchDriver dd(pDisp); 
			for(i = 0;i < m_nCount;i++) 
				{ 
				CComVariant v; 
				hr = dd.GetProperty(m_ppProp[i]->m_id,&v); 
				_ASSERT(SUCCEEDED(hr)); 
				m_ppProp[i]->InitPredefinedValues(pDisp); 
				m_ppProp[i]->SetNewValue(&v); 
				} 
			return TRUE; 
			} 
		int GetCount() {return m_nCount;} 
		CPBProperty* operator[](int nIdx) 
			{ 
			if(nIdx < 0 || nIdx >= m_nCount) 
				{ 
				_ASSERT(FALSE); 
				return NULL; 
				} 
			return m_ppProp[nIdx]; 
			} 
		void CleanUp() 
			{ 
			if(m_ppProp) 
				{ 
				for(int i=0;i < m_nCount;i++) 
					delete m_ppProp[i]; 
				delete[] m_ppProp; 
				m_ppProp = NULL; 
				m_nCount=0; 
				m_nAllocCount = 0; 
				} 
			} 
	protected: 
		void AddFuncProperty(ITypeInfo * pTypeInfo,int nIdx) 
			{ 
			HRESULT hr; 
			FUNCDESC* fd; 
			hr = pTypeInfo->GetFuncDesc(nIdx,&fd); 
			if(FAILED(hr)){_ASSERT(FALSE); return;} 
			// looking for a property (not read-only) 
			if(fd->invkind == INVOKE_PROPERTYPUT || fd->invkind == INVOKE_PROPERTYPUTREF) 
				{ 
				// don't show hidden and non-browsable elements 
				if(fd->wFuncFlags & (FUNCFLAG_FHIDDEN | FUNCFLAG_FNONBROWSABLE)) 
					return; 
				if(fd->cParams != 1) 
					return; 
				if(CPBProperty::GetPropType(pTypeInfo,&fd->lprgelemdescParam[0].tdesc,NULL) == CPBProperty::PBPropTypes::PBT_ERROR) 
					return; 
				if(!Find(fd->memid)) 
					_Add(pTypeInfo,nIdx); 
				} 
			} 
		CPBProperty** m_ppProp; 
		int m_nCount; 
		int m_nAllocCount; 
		//dumb finder 
		CPBProperty* Find(MEMBERID mid) 
			{ 
			for(int i=0;i < m_nCount;i++) 
				if(m_ppProp[i]->m_id == mid) 
					return m_ppProp[i]; 
			return NULL; 
			} 
		static inline int _CmpPropNames(CPBProperty * px,CPBProperty* py) 
			{ 
			return _wcsicmp(px->m_bstrName.m_str,py->m_bstrName.m_str); 
			} 
		HRESULT _Add(ITypeInfo * pTypeInfo,int nIdx) 
			{ 
			int i=0; 
			if(m_nAllocCount == m_nCount) 
				if(!_AllocMore()) 
					return FALSE; 
			CPBProperty* pProp = NULL; 
			HRESULT hr = CPBProperty::Create(pTypeInfo,nIdx,&pProp); 
			if(FAILED(hr)) goto error; 
			for(i=0;i < m_nCount;i++) 
				{ 
				if(_CmpPropNames(m_ppProp[i],pProp) < 0) 
					continue; 
				memmove(m_ppProp + i + 1,m_ppProp+i,sizeof(m_ppProp[0])*(m_nCount-i)); 
				m_ppProp[i] = pProp; 
				m_nCount++; 
				return S_OK; 
				} 
			m_ppProp[m_nCount] = pProp; 
			m_nCount++; 
			return S_OK; 
			error: 
			if(pProp) 
				delete pProp; 
			return hr; 
			} 
		BOOL _AllocMore() 
			{ 
			CPBProperty** ppTemp = NULL; 
			ppTemp = new CPBProperty*[m_nAllocCount + nAllocSize]; 
			if(!ppTemp) 
				{ 
				_ASSERT(ppTemp);// out of memory? 
				return FALSE; 
				} 
			_ASSERT(m_nCount <= m_nAllocCount); 
			m_nAllocCount += nAllocSize; 
			for(int i=0;i < m_nCount;i++) 
				ppTemp[i]=m_ppProp[i]; 
			if(m_ppProp) 
				delete[] m_ppProp; 
			m_ppProp = ppTemp; 
			return TRUE; 
			} 
	}; 
 
class CPBDropListBox : public CWindowImpl 
	{ 
	enum { _ID_LIST=1}; 
	public: 
		void Display(RECT *r,CPBProperty *pProp,HWND hwndEdit); 
		int LoadStrings(CPBProperty * pProp); 
		CPBDropListBox() : m_wndList(_T("LISTBOX"),this,1) 
			{ 
			m_hwndBuddyEdit = NULL; 
			m_pProp = NULL; 
			} 
 
		~CPBDropListBox() 
			{ 
			} 
	CContainedWindow m_wndList; 
	HWND m_hwndBuddyEdit; 
	CPBProperty* m_pProp; 
	LRESULT OnSelChangeList(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled) 
		{ 
		int nSel = m_wndList.SendMessage(LB_GETCURSEL); 
		if(nSel >= 0) 
			{ 
			TCHAR szText[128]; 
			m_wndList.SendMessage(LB_GETTEXT,nSel,(LPARAM)szText); 
			_ASSERT(m_hwndBuddyEdit); 
			::SendMessage(m_hwndBuddyEdit,WM_SETTEXT,0,(LPARAM)szText); 
			_ASSERT(m_pProp); 
//			m_pProp->m_bDirty = TRUE; 
			} 
		ShowWindow(SW_HIDE); 
		return 0; 
		} 
 
	LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); 
	 
	BEGIN_MSG_MAP(CPBDropListBox) 
		MESSAGE_HANDLER(WM_CREATE,OnCreate) 
		COMMAND_HANDLER(1,LBN_SELCHANGE,OnSelChangeList) 
 
	ALT_MSG_MAP(1) 
	END_MSG_MAP() 
	}; 
 
 
class CPropBrowseWnd : public CWindowImpl 
	{ 
	enum {_ID_EDIT=1}; 
	public: 
		HRESULT TranslateAccelerator(MSG* pMsg); 
		BOOL Create(HWND hWndParent,LPRECT prcPos,UINT nId); 
		void AddProperty(CPBProperty * pProp); 
		CPropBrowseWnd():m_wndEdit(_T("EDIT"),this,1) 
			{ 
			m_pEditProp = NULL; 
			m_bEditReadOnly = FALSE; 
			m_nColWidth = 0; 
			m_clrText = ::GetSysColor(COLOR_WINDOWTEXT); 
			m_clrBack = ::GetSysColor(COLOR_WINDOW); 
			m_clrHighlite = ::GetSysColor(COLOR_HIGHLIGHT); 
			m_clrHighliteText = ::GetSysColor(COLOR_HIGHLIGHTTEXT); 
			m_hGridPen = CreatePen(PS_SOLID,1,::GetSysColor(COLOR_3DLIGHT)); 
 
			m_clrDirtyText = RGB(0xC0,0x00,0x00);// red color 
			} 
		virtual ~CPropBrowseWnd() 
			{ 
			if(m_hGridPen) 
				::DeleteObject(m_hGridPen); 
			if(::IsWindow(m_wndColor.m_hWnd)) 
				m_wndColor.DestroyWindow(); 
			if(::IsWindow(m_wndLBox.m_hWnd)) 
				m_wndLBox.DestroyWindow(); 
			} 
	DECLARE_WND_SUPERCLASS(_T("PropBrowseWnd_1_0"),_T("LISTBOX")) 
 
	LRESULT DrawItem(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); 
	LRESULT OnLButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); 
	LRESULT OnLButtonDoubleClick(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); 
	LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); 
	// mesage handler for start edit 
	LRESULT OnStartEdit(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); 
	// message handler to safe display a message box  
	LRESULT OnDisplayWarning(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); 
 
	LRESULT OnKillFocusEdit(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); 
	LRESULT OnShowPopup(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); 
	LRESULT OnLDoubleClickEdit(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); 
	LRESULT OnSysKeyDownEdit(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); 
	LRESULT OnKeyDownEdit(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); 
	LRESULT OnCutPasteEdit(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 
		{ 
		bHandled = m_bEditReadOnly; 
		return 0; 
		} 
 
 
BEGIN_MSG_MAP(CPropBrowseWnd) 
	MESSAGE_HANDLER(WM_CREATE,OnCreate) 
	MESSAGE_HANDLER(WM_LBUTTONDOWN,OnLButtonDown) 
	MESSAGE_HANDLER(WM_PB_STARTEDIT,OnStartEdit) 
	MESSAGE_HANDLER(WM_PB_WARNING,OnDisplayWarning) 
	MESSAGE_HANDLER(WM_LBUTTONDBLCLK,OnLButtonDoubleClick) 
	MESSAGE_HANDLER(WM_PB_SHOWPOPUP,OnShowPopup) 
ALT_MSG_MAP(1) 
	MESSAGE_HANDLER(WM_KILLFOCUS,OnKillFocusEdit) 
	MESSAGE_HANDLER(WM_PASTE,OnCutPasteEdit) 
	MESSAGE_HANDLER(WM_CUT,OnCutPasteEdit) 
	MESSAGE_HANDLER(WM_LBUTTONDBLCLK,OnLDoubleClickEdit) 
	MESSAGE_HANDLER(WM_SYSKEYDOWN,OnSysKeyDownEdit) 
	MESSAGE_HANDLER(WM_KEYDOWN,OnKeyDownEdit) 
END_MSG_MAP() 
	protected: 
		HRESULT AddMetaFile(CPBProperty * pProp,LPCTSTR szFileName); 
		HRESULT AddBitmapFile(CPBProperty * pProp,LPCTSTR szFileName); 
		HRESULT AddIconFile(CPBProperty * pProp,LPCTSTR szFileName); 
		HRESULT SelectPicture(CPBProperty * pProp); 
		BOOL m_bEditReadOnly; 
		HRESULT SelectFont(CPBProperty *pProp); 
		void DrawItemArrow(HDC hDC,LPRECT prcItem,CPBProperty * pProp,BOOL bSelected); 
		BOOL NeedArrow(CPBProperty *pProp); 
		static int m_nDeltaX; 
		int GetItemWidth(CPBProperty* pProp); 
		int m_nColWidth; 
		COLORREF	m_clrText; 
		COLORREF	m_clrBack; 
		COLORREF	m_clrHighlite; 
		COLORREF	m_clrHighliteText; 
		COLORREF	m_clrDirtyText; 
		HPEN		m_hGridPen; 
		CPBProperty * m_pEditProp;	// current editing property 
		CContainedWindow m_wndEdit; 
		CPBColorPicker m_wndColor; 
		CPBDropListBox m_wndLBox; 
		RECT m_rcEdit; 
	}; 
 
 
template  class CPropertyBrowserPage : 
				public IPropertyPageImpl, 
				public CDialogImpl 
 
{ 
public: 
	CPropertyBrowserPage() 
		{ 
		} 
	virtual ~CPropertyBrowserPage() 
		{ 
		} 
 
	STDMETHOD(TranslateAccelerator)( MSG* pMsg ) 
		{ 
		HRESULT hr = m_wndList.TranslateAccelerator(pMsg); 
		if(hr != S_OK) 
			return IPropertyPageImpl::TranslateAccelerator(pMsg); 
		else 
			return hr; 
		} 
	STDMETHOD(SetObjects)( ULONG nObjects, IUnknown** ppUnk ) 
		{ 
		HRESULT hr = IPropertyPageImpl::SetObjects(nObjects,ppUnk ); 
		if(FAILED(hr)) 
			return hr; 
		IDispatch * pDisp; 
 
		m_Array.CleanUp(); 
 
		if(nObjects > 0) 
			{ 
			hr = ppUnk[0]->QueryInterface(IID_IDispatch,(void**)&pDisp); 
			if(FAILED(hr))	{_ASSERT(FALSE);return E_FAIL;} 
			m_Array.Init(pDisp); 
			pDisp->Release(); 
			} 
		if(::IsWindow(m_wndList.m_hWnd)) 
			{ 
			m_wndList.SendMessage(LB_RESETCONTENT);// clear list box 
			for(int i=0;i < m_Array.GetCount();i++) 
				m_wndList.AddProperty(m_Array[i]); 
			} 
		 
		return S_OK; 
		} 
 
	enum {_ID_LISTWND = 110}; 
	LRESULT OnPropertyChange(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 
		{ 
		// receive notification from listbox about property has been changed 
		SetDirty(TRUE); 
		return 0; 
		} 
 
// looks like ATL's implementation has a bug so implement the very own one 
	void SetDirty(BOOL bDirty) 
		{ 
		T* pT = static_cast(this); 
		if (!pT->m_bDirty && bDirty) 
			{ 
			pT->m_bDirty = bDirty;// this line should be added to fix the problem 
			// (to provide subsequent IsPageDirty() call with valid m_bDirty value) 
			pT->m_pPageSite->OnStatusChange(PROPPAGESTATUS_DIRTY | PROPPAGESTATUS_VALIDATE); 
			} 
		pT->m_bDirty = bDirty; 
		} 
 
	STDMETHOD(Apply)(void) 
		{ 
		ATLTRACE(_T("CPropertyBrowserPage::Apply\n")); 
		for (UINT i = 0; i < m_nObjects; i++) 
			{ 
			CComQIPtr pDisp(m_ppUnk[i]); 
			if(!pDisp) 
				{ 
				ATLTRACE(_T("Failed to obtain a IDispatch pointer!\n")); 
				continue; 
				} 
			CComDispatchDriver dd(pDisp); 
			for(int j=0;j < m_Array.GetCount();j++) 
				{ 
				HRESULT hr = dd.PutProperty(m_Array[j]->m_id,&(m_Array[j]->m_value)); 
				if(FAILED(hr)) 
					{ 
					MessageBox(_T("Failed to apply properties!"),_T("Error"),MB_OK | MB_ICONSTOP); 
					} 
				m_Array[j]->m_bDirty = FALSE; 
				} 
			} 
		m_bDirty = FALSE; 
		m_wndList.RedrawWindow(); 
		return S_OK; 
		} 
 
	LRESULT OnDrawItem(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 
		{ 
		// just delegate 
		return m_wndList.DrawItem(uMsg,wParam,lParam,bHandled); 
		} 
 
	LRESULT OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 
		{ 
		RECT r,rd; 
		GetClientRect(&r); 
		::SetRectEmpty(&rd); 
		rd.right = rd.bottom = 4; 
		::MapDialogRect(m_hWnd,&rd); 
		r.left += rd.right; 
		r.top += rd.bottom; 
		r.right -= rd.right; 
		r.bottom -= rd.bottom; 
		BOOL bRet = m_wndList.Create(m_hWnd,&r,_ID_LISTWND); 
		_ASSERT(bRet); 
			for(int i=0;i < m_Array.GetCount();i++) 
				m_wndList.AddProperty(m_Array[i]); 
		return 0; 
		} 
protected: 
	CPropBrowseWnd m_wndList; 
	CPBPropertyHolder m_Array; 
BEGIN_MSG_MAP(CPropertyBrowserPage) 
	MESSAGE_HANDLER(WM_INITDIALOG,OnInitDialog) 
	MESSAGE_HANDLER(WM_DRAWITEM,OnDrawItem) 
	MESSAGE_HANDLER(WM_PB_PROPCHANGE,OnPropertyChange) 
END_MSG_MAP() 
}; 
 
#endif // !defined(AFX_PROPERTYBROWSERPAGE_H__12892D12_03FF_11D3_84F5_00C04FD1E2CA__INCLUDED_)