www.pudn.com > ObjectInspector_demo.rar > ObjectInspector.cpp


// ObkjectInspector.cpp : implementation file 
// 
// MFC Object-Inspector Control v1.0 
// 
// Written by Gunnar Bolle  
// Copyright (c) 2001. All Rights Reserved. 
// 
// This code may be used in compiled form in any way you desire. This 
// file may be redistributed unmodified by any means PROVIDING it is  
// not sold for profit without the authors written consent, and  
// providing that this notice and the authors name and all copyright  
// notices remains intact.  
// 
// This file is provided "as is" with no expressed or implied warranty. 
// The author accepts no liability for any damage/loss of business that 
// this product may cause. 
// 
// Expect bugs! 
// 
// Additional source codes by : 
//  
// CMemDC		- Keith Rule 
// CColorButton - James White 
// CColourPopup - Chris Maunder 
//				  Alexander Bischofberger 
//  
// History: 
// -------- 
// 
//          1.0			30 Jun 2001 
//                      First release version.  
// 
///////////////////////////////////////////////////////////////////////////// 
 
#include "stdafx.h" 
#include "MemDC.h" 
#include "GfxTools.h" 
#include "ColorButton.h" 
#include "ObjectInspectorTest.h" 
#include "ObjectInspector.h" 
 
#ifdef _DEBUG 
#define new DEBUG_NEW 
#undef THIS_FILE 
static char THIS_FILE[] = __FILE__; 
#endif 
 
///////////////////////////////////////////////////////////////////////////// 
// CObjectInspector 
 
 
// For sorting a vector with pointers to objects 
template  struct iter_less : public std::binary_function 
{ 
  bool operator()(const T x, const T y) const { return *x < *y; } 
}; 
 
template  struct iter_greater : public std::binary_function 
{ 
  bool operator()(const T x, const T y) const { return !(*x < *y); } 
}; 
 
CObjectInspector::CProperty::~CProperty()  
{  
	DestroyInplaceControl();  
	 
	delete m_pControlFont;  
	 
	// Destroy childs as well 
	for (vector::iterator i = m_Childs.begin(); i != m_Childs.end(); i++) 
		delete (*i); 
 
	m_Childs.clear (); 
}; 
 
 
 
bool CObjectInspector::CProperty::GetValueFromControl(short *psValue) 
{ 
	if (psValue && m_ptType == ptShort && m_bEditing) 
	{ 
		CString cValue; 
		m_pInplaceControl->GetWindowText(cValue); 
		int nValue = atoi(cValue); 
		*psValue = (short) nValue; 
		return true; 
	} 
	 
	return false; 
} 
 
bool CObjectInspector::CProperty::GetValueFromControl(int *pnValue) 
{ 
	if (pnValue && m_ptType == ptInteger && m_bEditing) 
	{ 
		CString cValue; 
		m_pInplaceControl->GetWindowText(cValue); 
		int nValue = atoi(cValue); 
		*pnValue = nValue; 
		return true; 
	} 
	 
	return false; 
} 
 
bool CObjectInspector::CProperty::GetValueFromControl(bool *pbValue) 
{ 
	if (pbValue && m_ptType == ptBool && m_bEditing) 
	{ 
		*pbValue = ((CButton*)m_pInplaceControl)->GetCheck () ? true : false; 
		return true; 
	} 
	 
	return false; 
} 
 
bool CObjectInspector::CProperty::GetValueFromControl(float *pfValue) 
{ 
	if (pfValue && m_ptType == ptFloat && m_bEditing) 
	{ 
		CString cValue; 
		m_pInplaceControl->GetWindowText(cValue); 
		*pfValue = (float) atof(cValue); 
		return true; 
	} 
	 
	return false; 
} 
 
bool CObjectInspector::CProperty::GetValueFromControl(double *pdblValue) 
{ 
	if (pdblValue && m_ptType == ptDouble && m_bEditing) 
	{ 
		CString cValue; 
		m_pInplaceControl->GetWindowText(cValue); 
		*pdblValue = (double) atof(cValue); 
		return true; 
	} 
	 
	return false; 
} 
 
bool CObjectInspector::CProperty::GetValueFromControl(COleDateTime *pdtValue) 
{ 
	if (pdtValue && m_ptType == ptDate && m_bEditing) 
	{ 
		COleDateTime dtValue; 
		((CDateTimeCtrl*)m_pInplaceControl)->GetTime(*pdtValue); 
		return true; 
	} 
 
	return false; 
} 
 
bool CObjectInspector::CProperty::GetValueFromControl(CString *pszValue) 
{ 
	if (pszValue && m_ptType == ptStringList && m_bEditing) 
	{ 
		CString cValue; 
		m_pInplaceControl->GetWindowText(*pszValue); 
		return true; 
	} 
	 
	return false; 
} 
 
bool CObjectInspector::CProperty::GetValueFromControl(COLORREF *pclValue) 
{ 
	if (pclValue && m_ptType == ptColor && m_bEditing) 
	{ 
		*pclValue = ((CColorButton*)m_pInplaceControl)->GetColor (); 
		return true; 
	} 
	 
	return false; 
} 
 
CWnd* CObjectInspector::CProperty::GetInplaceControl()  
{  
	return m_pInplaceControl;  
}; 
 
CRect CObjectInspector::CProperty::GetPropertyRect() 
{ 
	return m_rtPropertyRect; 
} 
 
CRect CObjectInspector::CProperty::GetValueRect() 
{ 
	return m_rtValueRect; 
} 
 
void CObjectInspector::CProperty::SetRectangles(CRect rtProperty, CRect rtValue) 
{ 
	m_rtPropertyRect = rtProperty; 
	m_rtValueRect    = rtValue; 
 
	if (m_bEditing && m_pInplaceControl && ::IsWindow (m_pInplaceControl->m_hWnd)) 
		m_pInplaceControl->MoveWindow (&m_rtValueRect); 
	 
} 
 
CWnd *CObjectInspector::CProperty::CreateControlFromType() 
{ 
	CWnd *pWndCtrl = NULL; 
 
	switch (m_ptType) 
	{ 
	case ptFloat : 
	case ptDouble : 
		{  
			 CEdit *pCtrl = new CEdit; 
			 if (!pCtrl->Create(ES_AUTOHSCROLL | WS_VISIBLE | WS_CHILD, m_rtValueRect, m_pParentWnd, IDC_INPLACECONTROL)) 
				 AfxThrowMemoryException(); 
			 else 
				pWndCtrl = pCtrl; 
			 break; 
		} 
	case ptShort : 
	case ptInteger :  
		{  
			 CEdit *pCtrl = new CEdit; 
			 if (!pCtrl->Create(ES_AUTOHSCROLL | WS_VISIBLE | WS_CHILD | ES_NUMBER, m_rtValueRect, m_pParentWnd, IDC_INPLACECONTROL)) 
				 AfxThrowMemoryException(); 
			 else 
				pWndCtrl = pCtrl; 
			 break; 
		} 
	case ptBool :  
		{  
			 CButton *pCtrl = new CButton; 
			 if (!pCtrl->Create("", WS_VISIBLE | WS_CHILD | BS_FLAT | (m_bRadio ? BS_RADIOBUTTON : BS_AUTOCHECKBOX ), m_rtValueRect, m_pParentWnd, IDC_INPLACECONTROL)) 
				 AfxThrowMemoryException(); 
			 else 
				pWndCtrl = pCtrl; 
			 break; 
		} 
	case ptDate :  
		{ 
			 CDateTimeCtrl *pCtrl = new CDateTimeCtrl; 
			 if (!pCtrl->Create(WS_VISIBLE | WS_CHILD | (m_bUseTime ? DTS_TIMEFORMAT : DTS_SHORTDATEFORMAT), m_rtValueRect, m_pParentWnd, IDC_INPLACECONTROL)) 
				 AfxThrowMemoryException(); 
			 else 
				pWndCtrl = pCtrl; 
			 break; 
		} 
	case ptColor : 
		{ 
			CColorButton *pCtrl = new CColorButton; 
			if (!pCtrl->Create("", WS_VISIBLE | WS_CHILD, m_rtValueRect, m_pParentWnd, IDC_INPLACECONTROL)) 
				AfxThrowMemoryException(); 
			else 
				pWndCtrl = pCtrl; 
			break; 
		} 
	case ptStringList: 
		{ 
			if (m_pStringList == NULL) 
			{ 
				CEdit *pCtrl = new CEdit; 
				if (!pCtrl->Create(WS_VISIBLE | WS_CHILD, m_rtValueRect, m_pParentWnd, IDC_INPLACECONTROL)) 
					AfxThrowMemoryException(); 
				else 
					pWndCtrl = pCtrl; 
			} 
			else 
			{ 
				CComboBox *pCtrl = new CComboBox; 
				if (!pCtrl->Create (WS_VISIBLE | WS_CHILD | WS_VSCROLL | m_dwComboBoxStyle, RectCopyInflate(m_rtValueRect,0,0,0,100), m_pParentWnd, IDC_INPLACECONTROL)) 
					AfxThrowMemoryException(); 
				else 
				for (POSITION pos = (*m_pStringList).GetHeadPosition (); pos != NULL; ) 
				{ 
					CString cItem = (*m_pStringList).GetNext (pos); 
					pCtrl->AddString (cItem); 
				} 
 
				pWndCtrl = pCtrl; 
			} 
			break; 
		} 
	 
	} 
	 
	if (pWndCtrl) 
		pWndCtrl->SetFont (m_pControlFont); 
 
	return pWndCtrl; 
} 
 
void CObjectInspector::CProperty::Create(CString cName, EPropertyType ptType) 
{ 
	m_bVisible        = true; 
	m_bExpanded       = false; 
	m_bFocused		  = false; 
	m_bEditing		  = false; 
	m_bRadio          = false; 
	m_pParentWnd	  = NULL; 
	m_pParent         = NULL; 
	m_pInplaceControl = NULL; 
	m_pItemData       = NULL; 
	m_ptType		  = ptType; 
	m_cName			  = cName; 
	m_nImage          = -1; 
	m_cFloatFormat    = _T("%-5.1f"); 
	m_pControlFont    = new CQuickFont("MS Sans Serif", 10, FW_NORMAL, false); 
} 
 
 
void CObjectInspector::CProperty::AddProperty(CProperty *pProperty) 
{ 
	pProperty->m_pParentWnd = m_pParentWnd; 
	pProperty->m_pParent    = this; 
	pProperty->m_nIndex     = m_Childs.size(); 
	 
	m_Childs.insert(m_Childs.end(), pProperty); 
} 
	 
void CObjectInspector::CProperty::SendNotification(int nMsg) 
{ 
	NM_OIVIEW OIMsg; 
 
	ASSERT(m_pParentWnd); 
 
	UINT wParam = m_pParentWnd->GetDlgCtrlID(); 
 
	OIMsg.hdr.code	   = nMsg; 
	OIMsg.hdr.hwndFrom = m_pParentWnd->m_hWnd; 
	OIMsg.hdr.idFrom   = m_pParentWnd->GetDlgCtrlID(); 
	OIMsg.pProperty	   = this; 
 
	::SendMessage(m_pParentWnd->GetParent()->m_hWnd , WM_NOTIFY,  wParam, (LPARAM) &OIMsg ); 
} 
 
 
CString CObjectInspector::CProperty::GetValueAsText()  
{  
	switch (m_ptType) 
	{ 
	case ptShort : 
		{ 
			CString cValue; 
			cValue.Format("%d", *m_psValue); 
			return cValue; 
		} 
	 
	case ptInteger :  
		{ 
			CString cValue; 
			cValue.Format("%d", *m_pnValue); 
			return cValue; 
		} 
	 
	case ptFloat :  
		{ 
			CString cValue; 
			cValue.Format(m_cFloatFormat, *m_pfValue); 
			return cValue; 
		} 
 
	case ptDouble : 
		{ 
			CString cValue; 
			cValue.Format(m_cFloatFormat, *m_pdblValue); 
			return cValue; 
		} 
	 
	case ptBool: 
			return _T(""); 
		 
	case ptDate : 
		{ 
			CString cValue; 
			cValue.Format("%s", (*m_pdtValue).Format(m_bUseTime ? VAR_TIMEVALUEONLY : VAR_DATEVALUEONLY)); 
			return cValue; 
		} 
	case ptStringList: 
			return *m_pszValue; 
	} 
 
	return _T(""); 
} 
	 
void CObjectInspector::CProperty::RangeError(int nMin, int nMax ) 
{ 
	CString cMin, cMax; 
	cMin.Format("%d", nMin); 
	cMax.Format("%d", nMax); 
	CString prompt; 
	AfxFormatString2(prompt, AFX_IDP_PARSE_INT_RANGE, cMin, cMax); 
	AfxMessageBox(prompt, MB_ICONEXCLAMATION, AFX_IDP_PARSE_INT_RANGE); 
} 
 
void CObjectInspector::CProperty::RangeError(float fMin, float fMax ) 
{ 
	CString cMin, cMax; 
	cMin.Format("%.*g", FLT_DIG, fMin); 
	cMax.Format("%.*g", FLT_DIG, fMax); 
	CString prompt; 
	AfxFormatString2(prompt, AFX_IDP_PARSE_REAL_RANGE, cMin, cMax); 
	AfxMessageBox(prompt, MB_ICONEXCLAMATION, AFX_IDP_PARSE_REAL_RANGE); 
} 
 
void CObjectInspector::CProperty::RangeError(double dblMin, double dblMax ) 
{ 
	CString cMin, cMax; 
	cMin.Format("%.*g", DBL_DIG, dblMin); 
	cMax.Format("%.*g", DBL_DIG, dblMax); 
	CString prompt; 
	AfxFormatString2(prompt, AFX_IDP_PARSE_REAL_RANGE, cMin, cMax); 
	AfxMessageBox(prompt, MB_ICONEXCLAMATION, AFX_IDP_PARSE_REAL_RANGE); 
} 
 
bool CObjectInspector::CProperty::GetValueInternal() 
{ 
	switch (m_ptType) 
	{ 
		case ptShort: 
		{ 
			CString cValue; 
			m_pInplaceControl->GetWindowText(cValue); 
			int nValue = atoi(cValue); 
 
			if ((double) nValue < m_dblMin || (double) nValue > m_dblMax) 
			{ 
				RangeError((int)m_dblMin,(int)m_dblMax); 
				return false; 
			} 
 
			if (nValue != *m_psValue) 
			{ 
				*m_psValue = (short) nValue; 
				SendNotification(); 
			} 
			break; 
		} 
 
		case ptInteger: 
		{ 
			CString cValue; 
			m_pInplaceControl->GetWindowText(cValue); 
			int nValue = atoi(cValue); 
			if ((double) nValue < m_dblMin || (double) nValue > m_dblMax) 
			{ 
				RangeError((int)m_dblMin,(int)m_dblMax); 
				return false; 
			} 
 
			if (nValue != *m_pnValue) 
			{ 
				*m_pnValue = nValue; 
				SendNotification(); 
			} 
			break; 
		} 
 
		case ptFloat: 
		{ 
			CString cValue; 
			m_pInplaceControl->GetWindowText(cValue); 
			float fValue = (float) atof(cValue); 
			if ((double) fValue < m_dblMin || (double) fValue > m_dblMax) 
			{ 
				RangeError((float)m_dblMin,(float)m_dblMax); 
				return false; 
			} 
 
			if (fValue != *m_pfValue) 
			{ 
				*m_pfValue = fValue; 
				SendNotification(); 
			} 
			break; 
		} 
 
		case ptDouble: 
		{ 
			CString cValue; 
			m_pInplaceControl->GetWindowText(cValue); 
			double fValue = atof(cValue); 
			if ((double) fValue < m_dblMin || (double) fValue > m_dblMax) 
			{ 
				RangeError((double)m_dblMin,(double)m_dblMax); 
				return false; 
			} 
 
			if (fValue != *m_pdblValue) 
			{ 
				*m_pdblValue = fValue; 
				SendNotification(); 
			} 
			break; 
		} 
 
 
		case ptBool: 
		{ 
			 
			bool bValue =  ((CButton*)m_pInplaceControl)->GetCheck () ? true : false; 
 
			if (m_bRadio && m_nIndex != *m_pnValue) 
			{ 
				*m_pnValue = m_nIndex; 
				SendNotification(); 
			} 
			else 
			if (bValue != *m_pbValue) 
			{ 
				*m_pbValue = bValue; 
				SendNotification(); 
			} 
			break; 
		} 
 
		case ptDate: 
		{ 
			COleDateTime dtValue; 
			((CDateTimeCtrl*)m_pInplaceControl)->GetTime(dtValue); 
 
			if (dtValue != *m_pdtValue) 
			{ 
				*m_pdtValue = dtValue; 
				SendNotification(); 
			} 
 
			break; 
		} 
		case ptColor: 
		{ 
			COLORREF color; 
			color = ((CColorButton*)m_pInplaceControl)->GetColor (); 
			if (color != *m_pclValue) 
			{ 
				*m_pclValue = color; 
				SendNotification(); 
			} 
			break; 
		} 
		case ptStringList: 
		{ 
			CString cValue; 
			m_pInplaceControl->GetWindowText(cValue); 
			if (cValue != *m_pszValue) 
			{ 
				*m_pszValue = cValue; 
				SendNotification(); 
			} 
		} 
	} 
 
	return true; 
} 
 
void  CObjectInspector::CProperty::SetValueInternal() 
{ 
	CString cValue; 
 
	switch (m_ptType) 
	{ 
	case ptShort : 
			cValue.Format("%d",*m_psValue ); 
			m_pInplaceControl->SetWindowText (cValue); 
			break; 
		case ptInteger: 
			cValue.Format("%d",*m_pnValue ); 
			m_pInplaceControl->SetWindowText (cValue); 
			break; 
		case ptFloat: 
			cValue.Format(m_cFloatFormat, *m_pfValue ); 
			m_pInplaceControl->SetWindowText (cValue); 
			break; 
		case ptDouble: 
			cValue.Format(m_cFloatFormat, *m_pdblValue ); 
			m_pInplaceControl->SetWindowText (cValue); 
			break; 
		case ptBool: 
			((CButton*)m_pInplaceControl)->SetCheck (*m_pbValue); 
			break; 
		case ptDate: 
			((CDateTimeCtrl*)m_pInplaceControl)->SetTime(*m_pdtValue); 
			break; 
		case ptColor: 
			((CColorButton*)m_pInplaceControl)->SetColor(*m_pclValue); 
			break; 
		case ptStringList: 
			if (!m_pStringList) 
				m_pInplaceControl->SetWindowText (*m_pszValue); 
			else 
				((CComboBox*)m_pInplaceControl)->SelectString (-1,*m_pszValue); 
			break; 
	} 
 
	 
} 
 
void CObjectInspector::CProperty::BeginEdit () 
{ 
	m_bEditing = true; 
	 
	CreateInplaceControl(); 
 
	SetValueInternal(); 
 
	if (m_pInplaceControl) 
		m_pInplaceControl->SetFocus (); 
} 
 
bool CObjectInspector::CProperty::EndEdit () 
{ 
	if (!GetValueInternal()) 
		return false; 
 
	m_bEditing = false; 
 
	DestroyInplaceControl(); 
 
	return true; 
} 
 
void CObjectInspector::CProperty::CreateInplaceControl() 
{ 
	m_pInplaceControl = CreateControlFromType(); 
} 
 
void CObjectInspector::CProperty::DestroyInplaceControl() 
{ 
	if (m_pInplaceControl) 
	{ 
		delete m_pInplaceControl; 
		m_pInplaceControl = NULL; 
	} 
} 
 
CObjectInspector::CObjectInspector() 
{ 
 
	m_cPropertyHeaderTitle = _T("Property"); 
	m_cPropertyValueTitle  = _T("Value"); 
	m_nXSplitterPos        = -1; 
	m_nHeaderHeight        = 0; 
	m_nFirstVisibleItem    = 0; 
	m_nScrollPos           = 0; 
	m_nMinPropertyColWidth = 50; 
	m_nMinValueColWidth    = 50; 
	m_bSplitterMoving      = false; 
	m_bScrollbarVisible	   = false; 
	m_b3dFocus             = false; 
	m_bHotTrack			   = false; 
	m_bSorted              = false; 
	m_bW2KStyle            = true; 
	m_bsStyle			   = bsLowered; 
	m_clBackColor          = ::GetSysColor (COLOR_BTNFACE); 
	m_clPropertyRowColor   = (COLORREF) 0; 
	m_clGridLineColor      = RGB(120,120,120); 
	m_clValueRowColor      = RGB(0,0,255); 
	m_clFocusLineColor     = RGB(100,100,255); 
	m_pEditingProperty     = NULL; 
	m_pImageList           = NULL; 
	m_pLastEnteredProperty = NULL; 
	m_sdSortDirection      = sdUp; 
 
	WNDCLASS wndcls; 
    HINSTANCE hInst = AfxGetInstanceHandle(); 
 
    // Register window class 
    if (!(::GetClassInfo(hInst, CLASS_OBJECTINSPECTOR, &wndcls))) 
    { 
        wndcls.style            = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW; 
        wndcls.lpfnWndProc      = ::DefWindowProc; 
        wndcls.cbClsExtra       = wndcls.cbWndExtra = 0; 
        wndcls.hInstance        = hInst; 
        wndcls.hIcon            = NULL; 
        wndcls.hCursor          = AfxGetApp()->LoadStandardCursor(IDC_ARROW); 
        wndcls.hbrBackground    = (HBRUSH) (COLOR_3DFACE + 1); 
        wndcls.lpszMenuName     = NULL; 
        wndcls.lpszClassName    = CLASS_OBJECTINSPECTOR; 
 
        if (!AfxRegisterClass(&wndcls)) 
        { 
            AfxThrowResourceException(); 
            return ; 
        } 
    } 
} 
 
CObjectInspector::~CObjectInspector() 
{ 
	CleanUp(); 
	 
} 
 
 
BEGIN_MESSAGE_MAP(CObjectInspector, CWnd) 
	//{{AFX_MSG_MAP(CObjectInspector) 
	ON_WM_PAINT() 
	ON_WM_MOUSEMOVE() 
	ON_WM_LBUTTONUP() 
	ON_WM_LBUTTONDOWN() 
	ON_WM_VSCROLL() 
	ON_WM_LBUTTONDBLCLK() 
	ON_EN_CHANGE(IDC_INPLACECONTROL, OnChangeInplaceControl) 
	ON_NOTIFY(DTN_DATETIMECHANGE, IDC_INPLACECONTROL, OnChangeDateTimeCtrl) 
	ON_CBN_SELCHANGE(IDC_INPLACECONTROL, OnSelchangeComboBox) 
	ON_WM_MOUSEWHEEL() 
	ON_BN_CLICKED(IDC_INPLACECONTROL, OnRadioSelect) 
	ON_WM_CTLCOLOR() 
	//}}AFX_MSG_MAP 
END_MESSAGE_MAP() 
 
 
///////////////////////////////////////////////////////////////////////////// 
// Behandlungsroutinen für Nachrichten CObjectInspector  
 
BOOL CObjectInspector::Create(const RECT& rect, CWnd* parent, UINT nID, DWORD dwStyle) 
{ 
	BOOL bRet = CWnd::CreateEx(WS_EX_TOOLWINDOW, CLASS_OBJECTINSPECTOR, NULL, dwStyle, rect, parent, nID, 0); 
	 
	return bRet; 
} 
 
void CObjectInspector::DrawBorder(CDC *pDC, CRect& rect) 
{ 
	CBrush brush((COLORREF)0); 
 
	switch (m_bsStyle) 
	{ 
		case bsNone    : break; 
		case bsSingle  : pDC->FrameRect(&rect, &brush); rect.DeflateRect (1,1,1,1); break; 
		case bsLowered : pDC->DrawEdge( &rect, EDGE_SUNKEN, BF_RECT); rect.DeflateRect (2,2,2,2); break; 
		case bsRaised  : pDC->DrawEdge( &rect, EDGE_RAISED, BF_RECT); rect.DeflateRect (2,2,2,2); break; 
	} 
} 
 
void CObjectInspector::DrawSplitter(CDC *pDC) 
{ 
	if (m_nXSplitterPos == -1) 
		m_nXSplitterPos = m_rtClientRect.CenterPoint().x; 
 
	m_rtSplitterRect = CRect(m_nXSplitterPos, m_rtClientRect.top, m_nXSplitterPos + 1, m_rtClientRect.bottom ); 
 
	{ 
		CPenSelector grayPen(PS_SOLID,1, RGB(80,80,80), pDC); 
	 
		pDC->MoveTo(m_rtSplitterRect.left, m_rtSplitterRect.top); 
		pDC->LineTo(m_rtSplitterRect.left, m_rtSplitterRect.bottom); 
	} 
 
	{ 
		CPenSelector whitePen(PS_SOLID,1, RGB(255,255,255), pDC); 
	 
		pDC->MoveTo(m_rtSplitterRect.right, m_rtSplitterRect.top); 
		pDC->LineTo(m_rtSplitterRect.right, m_rtSplitterRect.bottom); 
	} 
} 
 
void CObjectInspector::DrawHeader(CDC *pDC) 
{ 
	m_nHeaderHeight = pDC->GetTextExtent("pP").cy + 6; 
 
	m_rtPropertyHeaderRect = CRect (m_rtClientRect.left , m_rtClientRect.top, m_rtSplitterRect.left + 1 , m_rtClientRect.top + m_nHeaderHeight ); 
	m_rtValueHeaderRect    = CRect (m_rtSplitterRect.left + 2 , m_rtClientRect.top, m_rtClientRect.right, m_rtClientRect.top + m_nHeaderHeight); 
	 
	pDC->FillSolidRect (m_rtClientRect.left, m_rtClientRect.top, m_rtClientRect.Width (), m_nHeaderHeight, ::GetSysColor(COLOR_BTNFACE)); 
	 
	if (m_bSorted) 
	{ 
		CBrush brush (::GetSysColor(COLOR_BTNFACE)); 
		CRect  copyRect (m_rtPropertyHeaderRect); 
		copyRect.OffsetRect (CPoint(-4,0)); 
		copyRect.left = copyRect.right - GetSystemMetrics(SM_CXHSCROLL); 
		pDC->DrawFrameControl (copyRect, DFC_SCROLL, (m_sdSortDirection == sdUp ? DFCS_SCROLLUP : DFCS_SCROLLDOWN) | DFCS_MONO ); 
		pDC->FrameRect (copyRect, &brush); 
		copyRect.DeflateRect (1,1,1,1); 
		pDC->FrameRect (copyRect, &brush); 
		 
	} 
 
	CFontSelector headerFont(&CQuickFont("MS Sans Serif", 10, FW_BOLD), pDC); 
	 
	pDC->Draw3dRect (&m_rtPropertyHeaderRect, RGB(255,255,255), RGB(50,50,50)); 
	pDC->Draw3dRect (&m_rtValueHeaderRect, RGB(255,255,255), RGB(50,50,50)); 
	 
	m_rtPropertyHeaderRect.DeflateRect (3,0,1,0); 
	m_rtValueHeaderRect.DeflateRect (3,0,1,0); 
 
	pDC->DrawText( m_cPropertyHeaderTitle, m_rtPropertyHeaderRect, DT_LEFT | DT_SINGLELINE | DT_VCENTER | DT_END_ELLIPSIS ); 
	pDC->DrawText( m_cPropertyValueTitle, m_rtValueHeaderRect, DT_LEFT | DT_SINGLELINE | DT_VCENTER | DT_END_ELLIPSIS ); 
} 
  
void CObjectInspector::DrawItem(CDC*pDC, CProperty *pProperty, int& nY, int& nCellHeight) 
{ 
 
	CFontSelector cellFont(&CQuickFont("MS Sans Serif", 10, FW_NORMAL, pProperty->m_bFocused && m_bHotTrack), pDC); 
 
	CRect rtPropertyRect(m_rtClientRect.left + 1, nY + 1, m_rtSplitterRect.left - 1, nY + nCellHeight - 1); 
	CRect rtValueRect(m_rtSplitterRect.right + 1, nY + 1, m_rtClientRect.right, nY + nCellHeight - 1); 
 
	pProperty->SetRectangles (rtPropertyRect, rtValueRect); 
 
	if (!pProperty->m_bVisible) 
	{ 
		if (pProperty->m_bEditing ) 
		{ 
			pProperty->EndEdit (); 
			m_pEditingProperty = NULL; 
		} 
 
		return; 
	} 
 
	m_nExpandedItemsCount++; 
	 
	if (pProperty->GetInplaceControl () && !pProperty->GetInplaceControl ()->IsWindowVisible ()) 
		pProperty->GetInplaceControl ()->ShowWindow (SW_SHOW); 
 
	if (pProperty->m_bEditing) 
	{ 
		if (m_b3dFocus) 
			pDC->DrawEdge (RectCopyInflate(pProperty->GetPropertyRect(),0,1,0,1), EDGE_SUNKEN, BF_RECT); 
		else 
			pDC->FillSolidRect (RectCopyInflate(pProperty->GetPropertyRect(),0,-1,0,0), m_clFocusLineColor); 
		 
	} 
	 
	int nBaseIndent = 12; 
 
	if (m_pImageList && m_pImageList->GetImageCount () > 0) 
	{ 
		IMAGEINFO imageInfo; 
		m_pImageList->GetImageInfo (0, &imageInfo); 
		int nImageHeight = CRect(imageInfo.rcImage).Height(); 
		if (pProperty->m_nImage != -1 && pProperty->m_Childs.size() == 0) 
		{ 
			CPoint ptImage = CPoint(pProperty->GetPropertyRect().left, pProperty->GetPropertyRect().CenterPoint ().y - (nImageHeight / 2)); 
			m_pImageList->Draw (pDC, pProperty->m_nImage, ptImage,ILD_TRANSPARENT); 
		} 
		nBaseIndent = CRect(imageInfo.rcImage).Width() + 4; 
	} 
 
	if (pProperty->GetType () == ptGroup) 
	{ 
		CFontSelector cellFontBold(&CQuickFont("MS Sans Serif", 10, FW_BOLD, pProperty->m_bFocused && m_bHotTrack), pDC); 
	 
		if (m_bW2KStyle) 
		{ 
			pDC->FillSolidRect(RectCopyInflate(pProperty->m_rtPropertyRect,0,0,1,1),::GetSysColor(COLOR_INACTIVECAPTION)); 
			pDC->FillSolidRect(RectCopyInflate(pProperty->m_rtValueRect,0,0,1,1),::GetSysColor(COLOR_INACTIVECAPTION)); 
			pDC->SetTextColor(RGB(255,255,255));		 
 
		}		 
		else 
			pDC->SetTextColor (m_clPropertyRowColor); 
 
		pDC->DrawText(pProperty->m_cName, RectCopyDeflate(rtPropertyRect,nBaseIndent + (m_nIndent * 10),0,0,0),DT_LEFT | DT_SINGLELINE | DT_VCENTER | DT_END_ELLIPSIS); 
	} 
	else 
	{ 
		pDC->SetTextColor ((!m_b3dFocus && pProperty->m_bEditing) ? RGB(255,255,255) : m_clPropertyRowColor); 
		pDC->DrawText(pProperty->m_cName, RectCopyDeflate(rtPropertyRect,nBaseIndent + (m_nIndent * 10),0,0,0),DT_LEFT | DT_SINGLELINE | DT_VCENTER | DT_END_ELLIPSIS); 
	} 
	pDC->SetTextColor (m_clValueRowColor); 
 
	if (pProperty->GetType() == ptColor) 
	{ 
		CPenSelector(PS_SOLID,1,(COLORREF)0, pDC); 
		pDC->Rectangle (RectCopyDeflate(pProperty->GetValueRect (),2,2,2,1)); 
		pDC->FillSolidRect (RectCopyDeflate(pProperty->GetValueRect (),3,3,3,2), *pProperty->m_pclValue); 
	} 
	else 
	if (pProperty->GetType() == ptBool) 
	{ 
		CRect copyRect (rtValueRect); 
		copyRect.left += 2; 
		copyRect.right = copyRect.left + GetSystemMetrics(SM_CXMENUCHECK); 
		if (pProperty->m_bRadio) 
			pDC->DrawFrameControl (copyRect, DFC_BUTTON, DFCS_FLAT | DFCS_BUTTONRADIO | (*pProperty->m_pbValue ? DFCS_CHECKED : 0)); 
		else 
			pDC->DrawFrameControl (copyRect, DFC_BUTTON, DFCS_FLAT | DFCS_BUTTONCHECK | (*pProperty->m_pbValue ? DFCS_CHECKED : 0)); 
 
	} 
	else 
		pDC->DrawText( pProperty->GetValueAsText(), RectCopyDeflate(rtValueRect,2,0,0,0),DT_LEFT | DT_SINGLELINE | DT_VCENTER | DT_END_ELLIPSIS); 
 
	if (pProperty->GetType () == ptGroup) 
	{ 
		CPenSelector(PS_SOLID, 1, (COLORREF)0, pDC); 
 
		pProperty->m_rtGroupRect = CRect(( m_nIndent * 10 ) + pProperty->GetPropertyRect ().left + 1 , 
										 pProperty->GetPropertyRect ().CenterPoint ().y - 4, 
										 ( m_nIndent * 10 ) + pProperty->GetPropertyRect ().left + 10, 
										 pProperty->GetPropertyRect ().CenterPoint ().y + 5); 
 
		 
		pDC->Rectangle (pProperty->m_rtGroupRect); 
 
		pDC->MoveTo (pProperty->m_rtGroupRect.left + 1, pProperty->m_rtGroupRect.CenterPoint ().y); 
		pDC->LineTo (pProperty->m_rtGroupRect.right - 1, pProperty->m_rtGroupRect.CenterPoint ().y); 
	 
		if (!pProperty->m_bExpanded) 
		{ 
			pDC->MoveTo (pProperty->m_rtGroupRect.CenterPoint().x, pProperty->m_rtGroupRect.top + 1); 
			pDC->LineTo (pProperty->m_rtGroupRect.CenterPoint().x, pProperty->m_rtGroupRect.bottom - 1); 
		} 
 
	} 
 
	nY += nCellHeight; 
 
	m_nOverallItemHeight += nCellHeight; 
 
	CPenSelector grayPen(PS_SOLID, 1, m_clGridLineColor, pDC); 
 
	pDC->MoveTo(m_rtClientRect.left, nY); 
	pDC->LineTo(m_rtSplitterRect.left - 1, nY); 
	pDC->MoveTo(m_rtSplitterRect.right + 1, nY); 
	pDC->LineTo(m_rtClientRect.right , nY); 
} 
 
void CObjectInspector::DrawChildItems(CDC*pDC,int& nItem, CProperty *pProperty, int& nY, int& nCellHeight) 
{ 
	if (!m_bW2KStyle) 
		m_nIndent ++; 
 
	for (vector::iterator i = pProperty->m_Childs.begin(); i != pProperty->m_Childs.end(); i++) 
	{ 
		if ((*i)->m_pParent && (!(*i)->m_pParent->m_bExpanded || !(*i)->m_pParent->m_bVisible)) 
			(*i)->m_bVisible = false; 
		else 
			(*i)->m_bVisible = true; 
 
		DrawItem(pDC, *i, nY, nCellHeight);  
		nItem++; 
		if ((*i)->m_Childs.size() != 0 ) 
			DrawChildItems(pDC, nItem, *i, nY, nCellHeight); 
	} 
 
	if (!m_bW2KStyle) 
		m_nIndent--; 
} 
 
void CObjectInspector::DrawItems(CDC *pDC) 
{ 
	int nCellHeight = pDC->GetTextExtent("pP").cy + 6;  
 
	m_nCellHeight = nCellHeight; 
 
	int nY = m_rtPropertyHeaderRect.bottom - m_nScrollPos; 
 
	m_nOverallItemHeight  = 0; 
	m_nExpandedItemsCount = 0; 
 
	int nItem = 0; 
	 
	for (vector::iterator i = m_PropertyList.begin(); i != m_PropertyList.end(); i++, nItem++) 
	{ 
		m_nIndent = 0; 
		DrawItem(pDC, *i, nY, nCellHeight); 
		if ((*i)->m_Childs.size() != 0) 
			DrawChildItems(pDC, nItem, *i, nY, nCellHeight); 
 
	} 
	 
	if (m_nOverallItemHeight > (m_rtClientRect.Height() - m_nHeaderHeight )&& !m_bScrollbarVisible ) 
	{ 
		m_bScrollbarVisible = true; 
		Invalidate(TRUE); 
		return; 
	} 
 
	if (m_nOverallItemHeight < (m_rtClientRect.Height() - m_nHeaderHeight ) && m_bScrollbarVisible) 
	{ 
		m_bScrollbarVisible = false; 
		Invalidate(TRUE); 
		return; 
	} 
} 
 
 
void CObjectInspector::CleanUp() 
{ 
	for (vector::iterator i = m_PropertyList.begin(); i != m_PropertyList.end(); i++) 
		delete (*i); 
	 
	m_PropertyList.clear (); 
} 
 
void CObjectInspector::CalculateScrollRange(CProperty *pProperty) 
{ 
	static int nItemsVisible = 0; 
 
	if (pProperty == NULL) 
	{ 
		nItemsVisible = 0; 
		for (vector::iterator i = m_PropertyList.begin(); i != m_PropertyList.end(); i++) 
		{ 
			if (!((*i)->m_pParent && !(*i)->m_pParent->m_bExpanded)) 
				nItemsVisible++; 
			if ((*i)->m_Childs.size () != 0) 
				CalculateScrollRange(*i); 
		} 
	} 
	else 
	{ 
		if (!(pProperty->m_pParent && !pProperty->m_pParent->m_bExpanded)) 
				nItemsVisible++; 
		if (pProperty->m_Childs.size != 0) 
			for (vector::iterator i = pProperty->m_Childs.begin(); i != pProperty->m_Childs.end(); i++) 
					CalculateScrollRange(*i); 
	} 
 
	m_nScrollRange = nItemsVisible * m_nCellHeight - m_rtClientRect.Height (); 
} 
 
void CObjectInspector::DrawScrollBar() 
{ 
	if (m_bScrollbarVisible) 
	{ 
 
		if (!::IsWindow(m_ScrollBar.m_hWnd)) 
			m_ScrollBar.Create (SBS_VERT | SBS_TOPALIGN | WS_CHILD, CRect(0,0,0,0), this, IDC_SCROLLBAR); 
	 
 
		m_rtClientRect.DeflateRect (0,0, ::GetSystemMetrics (SM_CXVSCROLL),0); 
 
		if (!m_ScrollBar.IsWindowVisible ()) 
			m_ScrollBar.ShowWindow (SW_SHOW); 
 
		CRect sbRect; 
		m_ScrollBar.GetWindowRect (&sbRect); 
		CRect sbNewRect (m_rtClientRect.right, m_rtClientRect.top, m_rtClientRect.right + ::GetSystemMetrics (SM_CXVSCROLL), m_rtClientRect.top + m_rtClientRect.Height ()); 
 
		if (sbRect != sbNewRect) 
			m_ScrollBar.MoveWindow(sbNewRect, TRUE); 
	 
				 
		m_ScrollBar.SetScrollRange (0, m_nScrollRange); 
		 
	} 
	else 
		if (::IsWindow(m_ScrollBar.m_hWnd)) 
			m_ScrollBar.ShowWindow (SW_HIDE); 
 
} 
 
void CObjectInspector::OnPaint()  
{ 
	CPaintDC dc(this);  
 
	CMemDC memDC(&dc); 
	 
	GetClientRect(&m_rtClientRect); 
 
	CBitmap		bmp; 
	CDC			saveDC; 
	CDC*		pControlDC; 
 
	int			nBmpWidth  = 0;   
	int			nBmpHeight = 0; 
	CGdiObject* pOldObject = NULL; 
	 
	// Check if there's a currently active inplace edit control 
	if (m_pEditingProperty && m_pEditingProperty->GetInplaceControl () && 
		m_pEditingProperty->GetInplaceControl ()->IsWindowVisible ()) 
	{ 
		// Get a bitmap of the inplace control to prevent flickering 
		nBmpWidth  = m_pEditingProperty->GetValueRect ().Width (); 
		nBmpHeight = m_pEditingProperty->GetValueRect ().Height(); 
		pControlDC = m_pEditingProperty->GetInplaceControl ()->GetDC(); 
		saveDC.CreateCompatibleDC (pControlDC); 
		bmp.CreateCompatibleBitmap (pControlDC, nBmpWidth, nBmpHeight); 
		pOldObject = saveDC.SelectObject (&bmp); 
		saveDC.BitBlt (0,0, nBmpWidth, nBmpHeight, pControlDC, 0, 0, SRCCOPY); 
		m_pEditingProperty->GetInplaceControl ()->ReleaseDC(pControlDC); 
	} 
	 
	 
	memDC.FillSolidRect (&m_rtClientRect, m_clBackColor); 
 
	memDC.SetBkColor(::GetSysColor (COLOR_BTNFACE)); 
 
	memDC.SetBkMode (TRANSPARENT); 
 
	DrawBorder(&memDC, m_rtClientRect); 
 
	CalculateScrollRange(); 
 
	DrawScrollBar(); 
 
	DrawSplitter(&memDC); 
 
	DrawHeader(&memDC); 
 
	CRgn rgn; 
	rgn.CreateRectRgn (m_rtClientRect.left, m_rtPropertyHeaderRect.bottom, m_rtClientRect.right, m_rtClientRect.bottom-1); 
	memDC.SelectClipRgn(&rgn); 
 
	DrawItems(&memDC); 
 
	if (m_pEditingProperty && m_pEditingProperty->GetInplaceControl () && 
		m_pEditingProperty->GetInplaceControl ()->IsWindowVisible ()) 
	{ 
		memDC.BitBlt (m_pEditingProperty->GetValueRect ().left , m_pEditingProperty->GetValueRect ().top, nBmpWidth, nBmpHeight, &saveDC, 0, 0, SRCCOPY); 
 
		if (pOldObject) 
			saveDC.SelectObject (pOldObject); 
 
		m_pEditingProperty->GetInplaceControl ()->Invalidate (TRUE); 
	} 
} 
 
void CObjectInspector::SetBorderStyle(EBorderStyle bsStyle) 
{ 
	if (m_bsStyle != bsStyle) 
	{ 
		m_bsStyle = bsStyle; 
		Invalidate(TRUE); 
	} 
} 
 
CObjectInspector::CProperty *CObjectInspector::GetPropertyFromPoint(CPoint point, CObjectInspector::CProperty *pRoot) 
{ 
	CProperty *pProperty = NULL; 
 
	if (pRoot == NULL) 
	{ 
		for (vector::iterator i = m_PropertyList.begin(); i != m_PropertyList.end(); i++) 
		{ 
			if (!(*i)->m_bVisible) 
				continue; 
 
			if ((*i)->GetPropertyRect().PtInRect (point) || (*i)->GetValueRect().PtInRect (point)) 
				return &(**i); 
 
			if ((*i)->m_Childs.size() != 0) 
			{ 
				pProperty = GetPropertyFromPoint(point, (*i)); 
				if (pProperty) 
					return pProperty; 
			} 
		} 
	} 
	else 
	{ 
		for (vector::iterator i = pRoot->m_Childs.begin(); i != pRoot->m_Childs.end(); i++) 
		{ 
			if (!(*i)->m_bVisible) 
				continue; 
		 
			if ((*i)->GetPropertyRect().PtInRect (point) || (*i)->GetValueRect().PtInRect (point)) 
				return  &(**i); 
 
			if ((*i)->m_Childs.size() != 0) 
			{ 
				pProperty = GetPropertyFromPoint(point, (*i)); 
				if (pProperty) 
					return pProperty; 
			} 
		} 
	} 
 
	return pProperty; 
} 
 
void CObjectInspector::AddProperty(CObjectInspector::CProperty* pProperty) 
{ 
	pProperty->m_pParentWnd = this; 
	 
	m_PropertyList.insert(m_PropertyList.end(), pProperty); 
	 
	Invalidate(FALSE); 
} 
 
void CObjectInspector::OnMouseMove(UINT nFlags, CPoint point)  
{ 
	static CProperty *pLastFocusedProperty = NULL; 
 
	CRect rtSplitterGripRect = m_rtSplitterRect; 
 
	rtSplitterGripRect.InflateRect (2,0,2,0); 
 
	if (rtSplitterGripRect.PtInRect (point)) 
		::SetCursor(AfxGetApp()->LoadStandardCursor(IDC_SIZEWE)); 
	else 
		::SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW)); 
	 
	if (m_bSplitterMoving && point.x < m_rtClientRect.right && point.x > 0 && point.x > m_nMinPropertyColWidth && (m_rtClientRect.Width() - point.x) > m_nMinValueColWidth ) 
	{ 
		m_nXSplitterPos = point.x; 
		InvalidateRect(m_rtClientRect,FALSE); 
	} 
 
	if (m_bHotTrack) 
	{ 
		CProperty *pProperty = GetPropertyFromPoint(point, NULL); 
 
		if (pProperty) 
		{ 
			if (pProperty->m_bFocused) 
				return; 
			 
			pProperty->m_bFocused = true; 
			 
			if (pLastFocusedProperty) 
				pLastFocusedProperty->m_bFocused = false; 
			 
			pLastFocusedProperty = pProperty; 
 
			InvalidateRect(m_rtClientRect,FALSE); 
		} 
	} 
 
	CWnd::OnMouseMove(nFlags, point); 
} 
 
void CObjectInspector::OnLButtonUp(UINT nFlags, CPoint point)  
{ 
	m_bSplitterMoving = false; 
	 
	ReleaseCapture(); 
	 
	CWnd::OnLButtonUp(nFlags, point); 
} 
 
void CObjectInspector::OnLButtonDown(UINT nFlags, CPoint point)  
{ 
	 
	CRect rtSplitterGripRect = m_rtSplitterRect; 
 
	rtSplitterGripRect.InflateRect (2,0,2,0); 
 
	if (m_rtPropertyHeaderRect.PtInRect (point)) 
	{ 
		m_bSorted = true; 
		 
		m_sdSortDirection = (ESortDirection) (((int) m_sdSortDirection + 1 ) & 1); 
		 
		if (m_sdSortDirection == sdDown) 
			std::sort (m_PropertyList.begin(), m_PropertyList.end(), iter_less ()); 
		else 
			std::sort (m_PropertyList.begin(), m_PropertyList.end(), iter_greater ()); 
 
		InvalidateRect(m_rtClientRect); 
	} 
	else 
	if (rtSplitterGripRect.PtInRect (point)) 
	{ 
		SetCapture(); 
		m_bSplitterMoving = true; 
	} 
	else 
	{ 
		CProperty *pProperty = GetPropertyFromPoint (point, NULL); 
		 
		if (pProperty) 
		{ 
			if (pProperty->GetType () != ptGroup ) 
			{ 
				if (pProperty->m_bEditing) 
					return; 
				 
				if (m_pLastEnteredProperty && m_pLastEnteredProperty->m_bEditing) 
					if (!m_pLastEnteredProperty->EndEdit()) 
						return; 
 
				if (pProperty->m_ptType == ptBool && pProperty->m_bRadio) 
				{ 
					for (vector::iterator i = pProperty->m_pParent->m_Childs.begin(); i!= pProperty->m_pParent->m_Childs.end(); i++) 
						if ((*i)->m_bRadio) 
							*(*i)->m_pbValue = false; 
			 
					*pProperty->m_pbValue = true; 
				} 
					 
				pProperty->BeginEdit(); 
 
				if (pProperty->m_ptType == ptBool && !pProperty->m_bRadio) 
					((CButton*)pProperty->GetInplaceControl ())->SetCheck (!((CButton*)pProperty->GetInplaceControl ())->GetCheck()); 
				 
				m_pEditingProperty = pProperty; 
								 
				m_pLastEnteredProperty = pProperty; 
				 
				InvalidateRect(m_rtClientRect,FALSE); 
			} 
			else 
			{ 
				if (pProperty->m_rtGroupRect.PtInRect(point)) 
				{						 
					pProperty->m_bExpanded = !pProperty->m_bExpanded; 
					InvalidateRect(m_rtClientRect, FALSE); 
					CalculateScrollRange(); 
					 
				} 
			} 
		} 
	} 
	 
	CWnd::OnLButtonDown(nFlags, point); 
} 
 
void CObjectInspector::OnRadioSelect() 
{ 
	if (m_pEditingProperty && m_pEditingProperty->m_bRadio) 
	{		 
		CProperty *pParent = m_pEditingProperty->m_pParent; 
		if (pParent) 
			for (vector::iterator i = pParent->m_Childs.begin(); i!= pParent->m_Childs.end(); i++) 
				if ((*i)->m_bRadio) 
					*(*i)->m_pbValue = false; 
 
		if (m_pEditingProperty->GetInplaceControl()) 
			((CButton*)m_pEditingProperty->GetInplaceControl())->SetCheck (true); 
	} 
} 
 
void CObjectInspector::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)  
{ 
	if (!m_bScrollbarVisible) 
		return; 
 
	if (nSBCode == SB_LINEDOWN && m_nScrollPos < m_nScrollRange)  
		m_nScrollPos ++; 
 
	if (nSBCode == SB_LINEUP && m_nScrollPos > 0)  
		m_nScrollPos --; 
 
	if (nSBCode == SB_BOTTOM) 
		m_nScrollPos = m_nScrollRange; 
 
	if (nSBCode == SB_TOP) 
		m_nScrollPos = 0; 
 
	if (nSBCode == SB_PAGEDOWN) 
	{ 
		if ((m_nScrollPos + m_nCellHeight) <= m_nScrollRange) 
			m_nScrollPos += m_nCellHeight; 
		else 
			m_nScrollPos = m_nScrollRange; 
	} 
	 
	if (nSBCode == SB_PAGEUP) 
	{ 
		if ((m_nScrollPos - m_nCellHeight) >= 0) 
			m_nScrollPos -= m_nCellHeight; 
		else 
			m_nScrollPos = 0; 
	} 
 
	if (nSBCode == SB_THUMBPOSITION || nSBCode == SB_THUMBTRACK) 
		m_nScrollPos = nPos; 
 
	if (m_pEditingProperty) 
	{ 
		m_pEditingProperty->EndEdit (); 
		m_pEditingProperty = NULL; 
	} 
 
 
	m_ScrollBar.SetScrollPos (m_nScrollPos);			 
 
	InvalidateRect(m_rtClientRect,FALSE); 
} 
 
void CObjectInspector::OnLButtonDblClk(UINT nFlags, CPoint point)  
{ 
	CProperty *pProperty = GetPropertyFromPoint (point, NULL); 
		 
	if (pProperty && pProperty->GetType () == ptGroup) 
	{ 
		pProperty->m_bExpanded = !pProperty->m_bExpanded; 
		InvalidateRect(m_rtClientRect, FALSE); 
	} 
	 
	CWnd::OnLButtonDblClk(nFlags, point); 
} 
 
void CObjectInspector::SetHeaderTitles(CString cPropertyHeaderTitle, CString cPropertyValueTitle) 
{ 
	m_cPropertyHeaderTitle = cPropertyHeaderTitle; 
	m_cPropertyValueTitle  = cPropertyValueTitle; 
} 
 
void CObjectInspector::SetStyle(bool bW2K) 
{ 
	m_bW2KStyle = bW2K; 
	Invalidate(); 
} 
 
void CObjectInspector::SetHotTrack(bool bEnabled)  
{  
	m_bHotTrack = bEnabled;  
	Invalidate();  
}; 
 
void CObjectInspector::Set3dFocus(bool bValue) 
{ 
	m_b3dFocus = bValue; 
	Invalidate(); 
} 
 
void CObjectInspector::SetBackColor(COLORREF clBackColor) 
{ 
	m_clBackColor = clBackColor; 
	Invalidate(); 
} 
 
void CObjectInspector::SetMinPropertyColWidth(int nWidth) 
{ 
	m_nMinPropertyColWidth = nWidth; 
	Invalidate(); 
} 
 
void CObjectInspector::SetMinValueColWidth(int nWidth) 
{ 
	m_nMinValueColWidth = nWidth; 
	Invalidate(); 
} 
 
void CObjectInspector::SetPropertyColTextColor(COLORREF clPropertyRowColor) 
{ 
	m_clPropertyRowColor = clPropertyRowColor; 
	Invalidate(); 
} 
 
void CObjectInspector::SetValueColTextColor(COLORREF clValueRowColor) 
{ 
	m_clValueRowColor = clValueRowColor; 
	Invalidate(); 
} 
 
void CObjectInspector::SetGridLineColor(COLORREF clGridLineColor) 
{ 
	m_clGridLineColor = clGridLineColor; 
	Invalidate(); 
} 
 
void CObjectInspector::SetFocusLineColor(COLORREF clFocusLineColor) 
{ 
	m_clFocusLineColor = clFocusLineColor; 
	Invalidate(); 
} 
 
void CObjectInspector::GeHeaderTitles(CString& cPropertyHeaderTitle, CString& cPropertyValueTitle) 
{ 
	cPropertyHeaderTitle = m_cPropertyHeaderTitle; 
	cPropertyValueTitle = m_cPropertyValueTitle; 
} 
 
CObjectInspector::EBorderStyle CObjectInspector::GetBorderStyle() 
{ 
	return m_bsStyle; 
} 
 
bool CObjectInspector::GetHotTrack() 
{ 
	return m_bHotTrack; 
} 
 
bool CObjectInspector::Get3dFocus() 
{ 
	return m_b3dFocus; 
} 
 
COLORREF CObjectInspector::GetBackColor() 
{ 
	return m_clBackColor; 
} 
 
COLORREF CObjectInspector::GetPropertyColTextColor() 
{ 
	return m_clPropertyRowColor;  
} 
 
COLORREF CObjectInspector::GetValueColTextColor() 
{ 
	return m_clValueRowColor;  
} 
 
COLORREF CObjectInspector::GetGridLineColor() 
{ 
	return m_clGridLineColor; 
} 
 
COLORREF CObjectInspector::GetFocusLineColor() 
{ 
	return m_clFocusLineColor; 
} 
 
int CObjectInspector::GetMinPropertyColWidth() 
{ 
	return m_nMinPropertyColWidth; 
} 
 
int CObjectInspector::GetMinValueColWidth() 
{ 
	return m_nMinValueColWidth; 
} 
  
void CObjectInspector::OnChangeInplaceControl() 
{ 
	if (m_pEditingProperty) 
		m_pEditingProperty->SendNotification (OIVN_ITEMCHANGING); 
} 
 
void CObjectInspector::OnChangeDateTimeCtrl(NMHDR* pNMHDR, LRESULT* pResult) 
{ 
	if (m_pEditingProperty) 
		m_pEditingProperty->SendNotification (OIVN_ITEMCHANGING); 
} 
 
void CObjectInspector::OnSelchangeComboBox() 
{ 
	if (m_pEditingProperty) 
		m_pEditingProperty->SendNotification (OIVN_ITEMCHANGING); 
} 
 
CObjectInspector::CProperty *CObjectInspector::GetNextProperty(CProperty *pProperty) 
{ 
	if (pProperty->m_pParent) 
	{ 
		vector ::iterator pos = find(pProperty->m_pParent->m_Childs.begin(), 
											     pProperty->m_pParent->m_Childs.end(), 
											     pProperty); 
 
		if (*pos != pProperty->m_pParent->m_Childs.back()) 
			return *(++pos); 
		else 
			return GetNextProperty(pProperty->m_pParent); 
	} 
	else 
	{ 
		vector ::iterator pos = find(m_PropertyList.begin(), 
											     m_PropertyList.end(), 
											     pProperty); 
 
		if (*pos != m_PropertyList.back()) 
			return *(++pos); 
		else 
			return NULL; 
		 
	} 
} 
 
CObjectInspector::CProperty *CObjectInspector::GetPrevProperty(CProperty *pProperty) 
{ 
	if (pProperty->m_pParent) 
	{ 
		vector ::iterator pos = find(pProperty->m_pParent->m_Childs.begin(), 
											     pProperty->m_pParent->m_Childs.end(), 
											     pProperty); 
 
		if (*pos != pProperty->m_pParent->m_Childs.front()) 
			return *(--pos); 
		else 
			return GetPrevProperty(pProperty->m_pParent); 
	} 
	else 
	{ 
		vector ::iterator pos = find(m_PropertyList.begin(), 
											     m_PropertyList.end(), 
											     pProperty); 
 
		if (*pos != m_PropertyList.front()) 
			return *(--pos); 
		else 
			return NULL; 
	} 
} 
 
 
BOOL CObjectInspector::PreTranslateMessage(MSG* pMsg)  
{ 
	if (m_pEditingProperty != NULL && pMsg->message == WM_KEYDOWN && IsCtrlDown()) 
	{ 
		int nVirtKey = (int) pMsg->wParam; 
 
		CProperty *pProperty; 
 
		if (nVirtKey == VK_DOWN) 
		{ 
			pProperty = GetNextProperty(m_pEditingProperty); 
			 
			if (pProperty) 
			{ 
				m_pEditingProperty->EndEdit (); 
				m_pLastEnteredProperty = m_pEditingProperty = pProperty; 
				InvalidateRect(m_rtClientRect, FALSE); 
				m_pEditingProperty->BeginEdit (); 
				return TRUE; 
			} 
			else 
				m_pEditingProperty = NULL; 
			 
		} 
 
		if (nVirtKey == VK_UP) 
		{ 
			pProperty = GetPrevProperty(m_pEditingProperty); 
 
			if (pProperty) 
			{ 
				m_pEditingProperty->EndEdit (); 
				m_pLastEnteredProperty = m_pEditingProperty = pProperty; 
				InvalidateRect(m_rtClientRect, FALSE); 
				m_pEditingProperty->BeginEdit (); 
				return TRUE; 
			} 
			else 
				m_pEditingProperty = NULL; 
	 
		} 
	} 
	 
	return CWnd::PreTranslateMessage(pMsg); 
} 
 
// Handles mouse wheel notifications 
#if (_MFC_VER >= 0x0421) 
BOOL CObjectInspector::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt) 
{ 
   int nRowsScrolled = zDelta / 120; 
 
   if (nRowsScrolled > 0) 
        for (int i = 0; i < nRowsScrolled; i++) 
            PostMessage(WM_VSCROLL, SB_PAGEUP, 0); 
    else 
        for (int i = 0; i > nRowsScrolled; i--) 
            PostMessage(WM_VSCROLL, SB_PAGEDOWN, 0); 
 
    return CWnd::OnMouseWheel(nFlags, zDelta, pt); 
} 
#endif // (_MFC_VER >= 0x0421) 
 
HBRUSH CObjectInspector::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)  
{ 
	if (m_pEditingProperty && m_pEditingProperty->GetType () == ptBool) 
	{ 
		m_CtrlBrush.Detach (); 
		m_CtrlBrush.CreateSolidBrush (m_clBackColor); 
		return m_CtrlBrush; 
	} 
	else 
		return CWnd::OnCtlColor(pDC, pWnd, nCtlColor); 
}