// XTPPropertyGridView.cpp : implementation of the CXTPPropertyGridView class.
//
// This file is a part of the XTREME PROPERTYGRID MFC class library.
// (c)1998-2012 Codejock Software, All Rights Reserved.
//
// THIS SOURCE FILE IS THE PROPERTY OF CODEJOCK SOFTWARE AND IS NOT TO BE
// RE-DISTRIBUTED BY ANY MEANS WHATSOEVER WITHOUT THE EXPRESSED WRITTEN
// CONSENT OF CODEJOCK SOFTWARE.
//
// THIS SOURCE CODE CAN ONLY BE USED UNDER THE TERMS AND CONDITIONS OUTLINED
// IN THE XTREME TOOLKIT PRO LICENSE AGREEMENT. CODEJOCK SOFTWARE GRANTS TO
// YOU (ONE SOFTWARE DEVELOPER) THE LIMITED RIGHT TO USE THIS SOFTWARE ON A
// SINGLE COMPUTER.
//
// CONTACT INFORMATION:
// support@codejock.com
// http://www.codejock.com
//
/////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "Common/Resource.h"

#include "Common/XTPDrawHelpers.h"
#include "Common/XTPResourceManager.h"
#include "Common/XTPToolTipContext.h"
#include "Common/XTPSystemHelpers.h"
#include "Common/XTPMaskEditT.h"
#include "Common/XTPColorManager.h"

#include "XTPPropertyGridDefines.h"
#include "XTPPropertyGridInplaceEdit.h"
#include "XTPPropertyGridInplaceButton.h"
#include "XTPPropertyGridItem.h"
#include "XTPPropertyGridPaintManager.h"
#include "XTPPropertyGridView.h"
#include "XTPPropertyGrid.h"

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


const UINT PGV_HIT_SPLITTER = 0x100;

/////////////////////////////////////////////////////////////////////////////
// CXTPPropertyGridToolTip

CXTPPropertyGridToolTip::CXTPPropertyGridToolTip()
{
	m_pGrid = NULL;
}

BEGIN_MESSAGE_MAP(CXTPPropertyGridToolTip, CWnd)
	//{{AFX_MSG_MAP(CXTPPropertyGridView)
	ON_WM_ERASEBKGND()
	ON_WM_PAINT()
	ON_WM_NCHITTEST_EX()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

LRESULT CXTPPropertyGridToolTip::OnNcHitTest(CPoint /*point*/)
{
	return (LRESULT)HTTRANSPARENT;
}
void CXTPPropertyGridToolTip::Create(CXTPPropertyGridView* pParentWnd)
{
	CWnd::CreateEx(0, AfxRegisterWndClass(0, AfxGetApp()->LoadStandardCursor(IDC_ARROW)), _T(""), WS_POPUP, CXTPEmptyRect(), pParentWnd, 0);
	m_pGrid = pParentWnd;

}

void CXTPPropertyGridToolTip::Activate(BOOL bActive, CXTPPropertyGridItem* pItem, BOOL bValuePart)
{
	if (bActive)
	{
		ASSERT(pItem);
		m_fnt.DeleteObject();
		LOGFONT lf;
		m_pGrid->GetPaintManager()->GetItemFont(pItem, bValuePart)->GetLogFont(&lf);

		m_fnt.CreateFontIndirect(&lf);

		CString strText;
		GetWindowText(strText);
		CWindowDC dc(this);
		CXTPFontDC font(&dc, &m_fnt);
		CXTPWindowRect rc (this);
		rc.right = rc.left + dc.GetTextExtent(strText).cx + 8;

		CRect rcWork = XTPMultiMonitor()->GetWorkArea();

		if (rc.right > rcWork.right)
			rc.OffsetRect(rcWork.right - rc.right, 0);

		if (rc.left < rcWork.left)
			rc.OffsetRect(rcWork.left - rc.left, 0);

		MoveWindow(rc);
		ShowWindow(SW_SHOWNOACTIVATE);
	}
	else
	{
		DestroyWindow();
	}
}

BOOL CXTPPropertyGridToolTip::OnEraseBkgnd(CDC*)
{
	return TRUE;
}

void CXTPPropertyGridToolTip::OnPaint()
{
	CPaintDC dc(this);
	CXTPClientRect rc(this);

	dc.SetBkMode(TRANSPARENT);

	if (m_pGrid->GetPropertyGrid()->GetToolTipContext())
	{
		m_pGrid->GetPropertyGrid()->GetToolTipContext()->DrawBackground(&dc, rc);
	}
	else
	{
		COLORREF clrText = GetXtremeColor(COLOR_INFOTEXT);
		dc.FillSolidRect(rc, GetXtremeColor(COLOR_INFOBK));

		dc.Draw3dRect(rc, clrText, clrText);
		dc.SetTextColor(clrText);
	}

	CString strText;
	GetWindowText(strText);

	CXTPFontDC font(&dc, &m_fnt);
	CRect rcText(rc);
	rcText.left += 4;

	dc.DrawText(strText, rcText, DT_SINGLELINE | DT_VCENTER | DT_NOPREFIX);

}

/////////////////////////////////////////////////////////////////////////////
// CXTPPropertyGridView

CXTPPropertyGridView::CXTPPropertyGridView()
{
	m_properetySort = xtpGridSortCategorized;

	m_dDivider = .5;
	m_bAutoDivider = TRUE;

	m_bTracking = FALSE;

	m_bVariableSplitterPos = TRUE;

	m_pSelected = NULL;
	m_pCategories = new CXTPPropertyGridItems();

	m_hCursor = XTPResourceManager()->LoadCursor(XTP_IDC_HSPLITBAR);
	m_nLockUpdate = 0;
	m_pGrid = NULL;
	m_nItemHeight = 0;

	m_pFocusedButton = NULL;
	m_pHotButton = NULL;

	EnableAutomation();
}

CXTPPropertyGridView::~CXTPPropertyGridView()
{
	m_wndTip.DestroyWindow();
	DestroyWindow();

	m_pCategories->Clear();
	m_pCategories->InternalRelease();

}

IMPLEMENT_DYNAMIC(CXTPPropertyGridView, CListBox)

BEGIN_MESSAGE_MAP(CXTPPropertyGridView, CListBox)
	//{{AFX_MSG_MAP(CXTPPropertyGridView)
	ON_WM_ERASEBKGND()
	ON_WM_PAINT()
	ON_WM_NCPAINT()
	ON_WM_NCCALCSIZE()
	ON_WM_LBUTTONDOWN()
	ON_WM_RBUTTONDOWN()
	ON_WM_RBUTTONUP()
	ON_WM_LBUTTONDBLCLK()
	ON_CONTROL_REFLECT(LBN_SELCHANGE, OnSelectionChanged)
	ON_WM_SETCURSOR()
	ON_WM_LBUTTONUP()
	ON_WM_CAPTURECHANGED()
	ON_WM_MOUSEMOVE()
	ON_WM_SIZE()
	ON_WM_KEYDOWN()
	ON_WM_SYSKEYDOWN()

	ON_WM_KEYUP()
	ON_WM_SYSKEYUP()

	ON_WM_CHAR()
	ON_WM_GETDLGCODE()

	ON_WM_VSCROLL()
	ON_WM_MOUSEWHEEL()

	ON_WM_SETFOCUS()
	ON_WM_KILLFOCUS()

	ON_MESSAGE(WM_GETOBJECT, OnGetObject)

	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CXTPPropertyGridView message handlers
void CXTPPropertyGridView::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
	if (GetScrollBarCtrl(SB_VERT) == pScrollBar)
	{
		OnScrollControl();
	}
	else
	{
		CListBox::OnVScroll(nSBCode, nPos, pScrollBar);
	}
}

struct CXTPPropertyGridView::WNDRECT
{
	HWND hWnd;
	RECT rc;
};

LRESULT CXTPPropertyGridView::OnScrollControl()
{
	if (GetCount() == 0)
		return 0;

	CArray<WNDRECT, WNDRECT&> arrRects;

	SetRedraw(FALSE);

	RECT rcTopOrig = {0, 0, 0, 0};
	GetItemRect(0, &rcTopOrig);

	HWND hWnd = ::GetWindow(m_hWnd, GW_CHILD);
	while (hWnd)
	{
		if (GetWindowLong(hWnd, GWL_STYLE) & WS_VISIBLE)
		{
			RECT rc;
			::GetWindowRect(hWnd, &rc);

			WNDRECT wndRect;
			wndRect.hWnd = hWnd;
			wndRect.rc = rc;
			arrRects.Add(wndRect);
		}

		hWnd = ::GetWindow(hWnd, GW_HWNDNEXT);
	}

	LRESULT lResult = Default();

	SetRedraw(TRUE);

	RECT rcTopDest = {0, 0, 0, 0};
	GetItemRect(0, &rcTopDest);
	int nOffset = rcTopDest.top - rcTopOrig.top;

	for (int i = 0; i < (int)arrRects.GetSize(); i++)
	{
		WNDRECT& wndRect = arrRects[i];
		RECT rc = wndRect.rc;
		ScreenToClient(&rc);

		::OffsetRect(&rc, 0, nOffset);
		::SetWindowPos(wndRect.hWnd, NULL, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top,
			SWP_NOACTIVATE | SWP_NOZORDER);
	}

	RedrawWindow(0, 0, RDW_INVALIDATE | RDW_UPDATENOW | RDW_FRAME | RDW_ALLCHILDREN);

	return lResult;
}

BOOL CXTPPropertyGridView::OnMouseWheel(UINT /*nFlags*/, short /*zDelta*/, CPoint /*pt*/)
{
	return (BOOL)OnScrollControl();
}


void CXTPPropertyGridView::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
	lpMeasureItemStruct->itemHeight = m_nItemHeight;

	GetPaintManager()->MeasureItem(lpMeasureItemStruct);
}


void CXTPPropertyGridView::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
	ASSERT(lpDrawItemStruct->CtlType == ODT_LISTBOX);

	CXTPPropertyGridItem* pItem = (CXTPPropertyGridItem*)lpDrawItemStruct->itemData;
	CXTPPropertyGridPaintManager* pPaintManager = GetPaintManager();

	if (pItem == NULL)
		return;

	if ((int)lpDrawItemStruct->itemID == GetCount() - 1)
	{
		CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
		CXTPClientRect rc(this);
		rc.top = lpDrawItemStruct->rcItem.bottom;
		pDC->FillSolidRect(rc, pPaintManager->GetItemMetrics()->m_clrBack);
	}

	if (!((m_pGrid->GetStyle() & XTP_PGS_OWNERDRAW) &&
		(SendNotifyMessage(XTP_PGN_DRAWITEM, (LPARAM)lpDrawItemStruct) == TRUE)))
	{
		pPaintManager->DrawItem(lpDrawItemStruct);
	}
}

BOOL CXTPPropertyGridView::OnEraseBkgnd(CDC*)
{
	return TRUE;
}

void CXTPPropertyGridView::OnPaint()
{
	CPaintDC dc(this);
	CXTPClientRect rc(this);
	CXTPBufferDC buffer(dc, rc);

	ASSERT(m_pGrid);
	GetPaintManager()->FillPropertyGridView(&buffer);

	if (GetCount())
	{
		CWnd::DefWindowProc(WM_PAINT, (WPARAM)buffer.m_hDC, 0);
	}
}

void CXTPPropertyGridView::OnNcPaint()
{
	Default();

	CWindowDC dc(this);

	CXTPWindowRect rc(this);
	rc.OffsetRect(-rc.TopLeft());

	ASSERT(m_pGrid);
	GetPaintManager()->DrawPropertyGridBorder(&dc, rc, FALSE);
}

void CXTPPropertyGridView::OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS* lpncsp)
{
	GetPaintManager()->DrawPropertyGridBorder(NULL, lpncsp->rgrc[0], TRUE);

	CWnd::OnNcCalcSize(bCalcValidRects, lpncsp);
}

CXTPPropertyGridItem* CXTPPropertyGridView::InsertCategory(int nIndex, LPCTSTR strCaption, CXTPPropertyGridItem* pCategory)
{
	ASSERT(nIndex >= 0 && nIndex <= m_pCategories->GetCount());

	if (nIndex < 0 || nIndex > m_pCategories->GetCount())
		nIndex = m_pCategories->GetCount();

	if (pCategory == NULL)
		pCategory = new CXTPPropertyGridItem(strCaption);

	SetPropertySort(xtpGridSortCategorized);

	pCategory->m_pGrid = this;
	pCategory->m_bCategory = TRUE;
	pCategory->m_nFlags = 0;


	m_pCategories->InsertAt(nIndex, pCategory);

	pCategory->OnCaptionChanged();

	if (m_hWnd)
	{
		int nInsertAt = (nIndex >= m_pCategories->GetCount() - 1) ? GetCount() : m_pCategories->GetAt(nIndex + 1)->m_nIndex;
		InsertItem(pCategory, nInsertAt);
	}
	_RefreshIndexes();


	return pCategory;

}

CXTPPropertyGridItem* CXTPPropertyGridView::AddCategory(LPCTSTR strCaption, CXTPPropertyGridItem* pCategory)
{
	return InsertCategory(m_pCategories->GetCount(), strCaption, pCategory);
}

void CXTPPropertyGridView::Refresh()
{
	_RefreshIndexes();
	SetPropertySort(m_properetySort, TRUE);

	SAFE_INVALIDATE(m_pGrid);
}

void CXTPPropertyGridView::ResetContent()
{
	if (m_pSelected)
	{
		m_pSelected->OnDeselect();
		m_pSelected = NULL;
		m_pGrid->OnSelectionChanged(NULL);
	}

	if (m_hWnd)
	{
		for (int i = 0; i < GetCount(); i++)
		{
			CXTPPropertyGridItem* pItem = (CXTPPropertyGridItem*)GetItemDataPtr(i);
			ASSERT(pItem);
			if (!pItem)
				continue;
			pItem->SetVisible(FALSE);
		}

		CListBox::ResetContent();

		CFont* pFont = &GetPaintManager()->GetItemMetrics()->m_fontNormal;

		SetFont(pFont, FALSE);

		CWindowDC dc(this);
		CXTPFontDC font(&dc, pFont);
		m_nItemHeight = dc.GetTextExtent(_T(" "), 1).cy + 4;

		SetItemHeight(0, m_nItemHeight);
	}
}

void CXTPPropertyGridView::SetPropertySort(XTPPropertyGridSortOrder sort, BOOL bRrefresh, BOOL bSetRedraw)
{
	if (m_nLockUpdate > 0)
	{
		m_properetySort = sort;
		return;
	}

	if (sort == m_properetySort && !bRrefresh)
		return;

	if (!m_hWnd)
		return;

	if (bSetRedraw) SetRedraw(FALSE);

	CXTPPropertyGridItem* pTopItem = bRrefresh ? GetItem(GetTopIndex()) : NULL;
	CXTPPropertyGridItem* pSelected = GetSelectedItem();

	ResetContent();

	if (sort == xtpGridSortCategorized)
	{
		for (int i = 0; i < m_pCategories->GetCount(); i++)
		{
			CXTPPropertyGridItem* pCategory = m_pCategories->GetAt(i);
			InsertItem(pCategory, GetCount());
		}
	}
	else if (sort == xtpGridSortAlphabetical || sort == xtpGridSortNoSort)
	{
		CXTPPropertyGridItems lstItems;
		int i;
		for (i = 0; i < m_pCategories->GetCount(); i++)
		{
			CXTPPropertyGridItem* pCategory = m_pCategories->GetAt(i);
			if (!pCategory->IsHidden())
				lstItems.AddTail(pCategory->m_pChilds);
		}

		if (sort != xtpGridSortNoSort)
			lstItems.Sort();

		for (i = 0; i < lstItems.GetCount(); i++)
		{
			CXTPPropertyGridItem* pItem = lstItems.GetAt(i);
			InsertItem(pItem, GetCount());
		}
	}
	else
	{
		ASSERT(FALSE);
	}

	_RefreshIndexes();

	if (pTopItem && pTopItem->IsVisible())
	{
		SetTopIndex(pTopItem->m_nIndex);
	}

	if (pSelected)
	{
		pSelected->Select();
	}
	OnSelectionChanged();

	if (bSetRedraw) SetRedraw(TRUE);

	if (sort != m_properetySort)
	{
		m_properetySort = sort;
		m_pGrid->OnSortChanged();
	}
}

void CXTPPropertyGridView::SetDividerPos(int nDivider)
{
	double dWidth = (double)CXTPWindowRect(this).Width();

	if (!m_bAutoDivider)
	{
		m_dDivider = nDivider;
		m_dDivider = __max(m_dDivider, 1.0);

		if (dWidth != 0)
		{
			m_dDivider = __min(m_dDivider, dWidth);
		}
	}
	else
	{
		m_dDivider = dWidth == 0 ? .5 : (double)nDivider / dWidth;

		m_dDivider = __max(m_dDivider, .1);
		m_dDivider = __min(m_dDivider, .85);
	}
}

int CXTPPropertyGridView::GetDividerPos() const
{
	return int(m_dDivider * (m_bAutoDivider ? CXTPWindowRect(this).Width() : 1));
}

void CXTPPropertyGridView::LockDivider()
{
	m_dDivider = GetDividerPos();
	m_bAutoDivider = FALSE;
}

int  CXTPPropertyGridView::InsertItem(CXTPPropertyGridItem* pItem, int nIndex)
{
	if (m_nLockUpdate > 0) return 0;
	if (!m_hWnd) return 0;

	if (pItem->IsHidden())
		return 0;

	pItem->OnBeforeInsert();
	nIndex = (int)::SendMessage(m_hWnd, LB_INSERTSTRING, nIndex, (LPARAM)pItem);

	ASSERT(nIndex != -1);
	SetItemDataPtr(nIndex, pItem);

	if (GetStyle() & LBS_OWNERDRAWVARIABLE)
	{
		MEASUREITEMSTRUCT measureItemStruct =
		{
			ODT_LISTBOX, 0, nIndex, 0, m_nItemHeight, (ULONG_PTR)pItem
		};
		MeasureItem(&measureItemStruct);
		SetItemHeight(nIndex, measureItemStruct.itemHeight);
	}

	pItem->SetVisible(TRUE);

	int nItemsInserted = 1;

	if (pItem->m_bExpanded)
	{
		nItemsInserted += _DoExpand(pItem, nIndex);
	}

	return nItemsInserted;
}

int CXTPPropertyGridView::_DoExpand(CXTPPropertyGridItem* pItem, int nIndex)
{
	int nStart = nIndex;

	for (int i = 0; i < pItem->GetChilds()->GetCount(); i++)
	{
		CXTPPropertyGridItem* pChild = pItem->GetChilds()->GetAt(i);

		nIndex += InsertItem(pChild, nIndex + 1);

	}
	return nIndex - nStart;
}

void CXTPPropertyGridView::_RefreshIndexes()
{
	if (m_hWnd)
	{
		for (int i = 0; i < GetCount(); i++)
		{
			CXTPPropertyGridItem* pItem = (CXTPPropertyGridItem*)GetItemDataPtr(i);
			ASSERT(pItem);
			if (!pItem)
				continue;

			if (pItem->m_nIndex != i)
			{
				pItem->m_nIndex = i;
				pItem->OnIndexChanged();
			}
		}
	}
}

void CXTPPropertyGridView::_DoCollapse(CXTPPropertyGridItem* pItem)
{
	ASSERT(pItem);
	if (!pItem)
		return;
	ASSERT(pItem->m_bExpanded);
	int nIndex = pItem->m_nIndex + 1;

	while (nIndex < GetCount())
	{
		CXTPPropertyGridItem* pChild = (CXTPPropertyGridItem*)GetItemDataPtr(nIndex);
		ASSERT(pChild);
		if (!pChild || !pChild->HasParent(pItem))
			break;

		pChild->SetVisible(FALSE);
		DeleteString(nIndex);
	}
	_RefreshIndexes();
}

CXTPPropertyGridItem* CXTPPropertyGridView::GetItem(int nIndex) const
{
	if (nIndex < 0 || nIndex >= GetCount())
		return 0;

	CXTPPropertyGridItem* pItem = (CXTPPropertyGridItem*)GetItemDataPtr(nIndex);

	if ((ULONG_PTR)pItem == (ULONG_PTR)(-1))
		return NULL;

	ASSERT(pItem);

	return pItem;
}

void CXTPPropertyGridView::SwitchExpandState(int nItem)
{
	CXTPPropertyGridItem* pItem = GetItem(nItem);
	if (!pItem || !pItem->IsExpandable())
		return;

	if (pItem->m_bExpanded)
		pItem->Collapse();
	else
		pItem->Expand();
}

void CXTPPropertyGridView::OnLButtonDown(UINT nFlags, CPoint point)
{

	if (HitTest(point) == PGV_HIT_SPLITTER)
	{
		SetFocus();
		SetCapture();
		if (m_pSelected) m_pSelected->OnValidateEdit();

		m_bTracking = TRUE;
		return;
	}


	CXTPPropertyGridItem* pItem = ItemFromPoint(point);
	if (pItem)
	{
		SetFocus();

		if (GetFocus() != this)
			return;

		if ((ItemFromPoint(point) == pItem) && pItem->OnLButtonDown(nFlags, point))
			return;
	}

	CListBox::OnLButtonDown(nFlags, point);

}

CXTPPropertyGridItem* CXTPPropertyGridView::ItemFromPoint(CPoint point) const
{
	BOOL bOutside = FALSE;
	int nIndex = CListBox::ItemFromPoint(point, bOutside);
	if (nIndex != -1 && !bOutside)
	{
		CXTPPropertyGridItem* pItem  = GetItem(nIndex);
		if (!pItem)
			return NULL;

		if (GetStyle() & LBS_OWNERDRAWVARIABLE)
		{
			CRect rc;
			GetItemRect(nIndex, rc);
			if (!rc.PtInRect(point))
				return NULL;
		}

		return pItem;
	}
	return NULL;
}

void CXTPPropertyGridView::FocusInplaceButton(CXTPPropertyGridInplaceButton* pButton)
{
	if (pButton != m_pFocusedButton)
	{
		if (pButton) SetFocus();
		m_pFocusedButton = pButton;
		Invalidate(FALSE);
	}
}

CPoint CXTPPropertyGridView::ViewToGrid(CPoint point)
{
	::MapWindowPoints(m_hWnd, GetPropertyGrid()->GetSafeHwnd(), &point, 1);

	return point;
}

void CXTPPropertyGridView::OnMouseMove(UINT nFlags, CPoint point)
{
	if (m_bTracking)
	{
		SetDividerPos(point.x);

		if (m_pSelected) m_pSelected->OnSelect();
		Invalidate(FALSE);
		return;
	}

	CXTPPropertyGridItem* pItem = ItemFromPoint(point);
	CXTPPropertyGridInplaceButton* pButton = NULL;

	if (pItem)
		pButton = pItem->GetInplaceButtons()->HitTest(point);

	if (pButton != m_pHotButton)
	{
		m_pHotButton = pButton;
		Invalidate(FALSE);
		TRACKMOUSEEVENT tme =
		{
			sizeof(TRACKMOUSEEVENT), TME_LEAVE, m_hWnd, 0
		};
		_TrackMouseEvent (&tme);
	}

	ShowToolTip(point);

	CListBox::OnMouseMove(nFlags, point);

}

void CXTPPropertyGridView::OnLButtonUp(UINT nFlags, CPoint point)
{
	if (m_bTracking)
	{
		ReleaseCapture();
		m_bTracking = FALSE;
		if (m_pSelected) m_pSelected->OnSelect();
		return;
	}

	CXTPPropertyGridItem* pItem = ItemFromPoint(point);

	if (pItem)
	{
		pItem->OnLButtonUp(nFlags, point);
	}

	CListBox::OnLButtonUp(nFlags, point);

}

void CXTPPropertyGridView::OnCaptureChanged(CWnd* pWnd)
{
	m_bTracking = FALSE;

	CListBox::OnCaptureChanged(pWnd);
}

void CXTPPropertyGridView::OnLButtonDblClk(UINT nFlags, CPoint point)
{
	CListBox::OnLButtonDblClk(nFlags, point);

	CXTPPropertyGridItem* pItem = ItemFromPoint(point);

	if (pItem)
	{
		pItem->OnLButtonDblClk(nFlags, point);


		if (pItem == ItemFromPoint(point))
		{
			SendNotifyMessage(XTP_PGN_DBLCLICK, (LPARAM)pItem);
		}
	}
}

void CXTPPropertyGridView::OnRButtonDown(UINT nFlags, CPoint point)
{
	CListBox::OnRButtonDown(nFlags, point);


	CXTPPropertyGridItem* pItem = ItemFromPoint(point);

	if (pItem)
	{
		pItem->OnRButtonDown(nFlags, point);


		if (pItem == ItemFromPoint(point))
		{
			SendNotifyMessage(XTP_PGN_RCLICK, (LPARAM)pItem);
		}
	}

}

void CXTPPropertyGridView::OnRButtonUp(UINT nFlags, CPoint point)
{
	CListBox::OnRButtonUp(nFlags, point);

}

LRESULT CXTPPropertyGridView::SendNotifyMessage(WPARAM wParam, LPARAM lParam)
{
	ASSERT(m_pGrid);
	if (!m_pGrid)
		return 0;

	return m_pGrid->SendNotifyMessage(wParam, lParam);
}

CXTPPropertyGridItem* CXTPPropertyGridView::GetSelectedItem()
{
	return m_hWnd ? GetItem(GetCurSel()) : 0;
}

void CXTPPropertyGridView::OnChar(UINT nChar, UINT nRepCntr, UINT nFlags)
{
	if (m_bTracking) return;

	if (m_pFocusedButton && nChar != VK_TAB)
		return;

	if (::GetFocus() != m_hWnd)
		return;

	CXTPPropertyGridItem* pItem = GetSelectedItem();

	if (nChar == '+' && pItem && pItem->HasChilds() && pItem->IsExpandable() && !pItem->m_bExpanded)
	{
		pItem->Expand();
		return;
	}
	if (nChar == '-' && pItem && pItem->HasChilds() && pItem->IsExpandable() && pItem->m_bExpanded)
	{
		pItem->Collapse();
		return;
	}
	if (nChar == VK_TAB)
	{
		if (m_pFocusedButton)
		{
			BOOL bForward = GetKeyState(VK_SHIFT) >= 0;
			m_pGrid->OnNavigate(bForward ? xtpGridUIInplaceEdit : xtpGridUIViewNext, bForward, pItem);
		}
		else
		{
			m_pGrid->OnNavigate(xtpGridUIView, GetKeyState(VK_SHIFT) >= 0, pItem);
		}
		return;
	}
	if (pItem && (nChar != VK_RETURN || !pItem->HasChilds()))
	{
		if (pItem->OnChar(nChar))
			return;
	}

	CWnd::OnChar(nChar, nRepCntr, nFlags);
}

UINT CXTPPropertyGridView::OnGetDlgCode()
{
	return DLGC_WANTARROWS | DLGC_WANTTAB | DLGC_WANTCHARS;
}

INT_PTR CXTPPropertyGridView::OnToolHitTest(CPoint point, TOOLINFO* pTI) const
{

	ASSERT_VALID(this);
	ASSERT(::IsWindow(m_hWnd));

	// check child windows first by calling CControlBar
	INT_PTR nHit = CWnd::OnToolHitTest(point, pTI);
	if (nHit != -1)
		return nHit;

	if (m_pHotButton)
	{
		CString strTip = m_pHotButton->GetTooltip();
		if (strTip.GetLength() == 0)
			return -1;

		CXTPToolTipContext::FillInToolInfo(pTI, m_hWnd, m_pHotButton->GetRect(),
			m_pHotButton->GetID(), strTip);

		return m_pHotButton->GetID();
	}

	CXTPPropertyGridItem* pItem = ItemFromPoint(point);
	if (pItem)
	{
		nHit = pItem->GetID();

		CString strTip = pItem->GetTooltip();
		if (strTip.GetLength() == 0)
			return -1;

		CXTPToolTipContext::FillInToolInfo(pTI, m_hWnd, pItem->GetItemRect(),
			nHit, strTip, strTip, pItem->GetDescription(), GetImageManager());

		return nHit;
	}
	return -1;
}


void CXTPPropertyGridView::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	CListBox::OnKeyUp(nChar, nRepCnt, nFlags);
}

void CXTPPropertyGridView::OnSysKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	CListBox::OnSysKeyUp(nChar, nRepCnt, nFlags);
}

void CXTPPropertyGridView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{

	CXTPPropertyGridItem* pItem = GetSelectedItem();

	if (m_pFocusedButton && m_pFocusedButton->GetItem() == pItem)
	{
		m_pFocusedButton->OnKeyDown(nChar);
		return;
	}

	if (m_pSelected && m_pSelected->OnKeyDown(nChar))
	{
		return;
	}

	if ((GetKeyState(VK_CONTROL) < 0) && (nChar == VK_RIGHT || nChar == VK_LEFT))
	{
		CXTPDrawHelpers::KeyToLayout(this, nChar);

		SetDividerPos(GetDividerPos() + (nChar == VK_RIGHT ? 3 : -3));

		if (m_pSelected) m_pSelected->OnSelect();
		Invalidate(FALSE);

		return;
	}

	if (nChar == VK_RIGHT)
	{
		if (pItem && pItem->HasChilds() && !pItem->m_bExpanded && pItem->IsExpandable())
		{
			pItem->Expand();
			return;
		}
	}
	else if (nChar == VK_LEFT)
	{
		if (pItem && pItem->HasChilds() && pItem->m_bExpanded && pItem->IsExpandable())
		{
			pItem->Collapse();
			return;
		}
	}

	if (nChar == VK_RETURN)
	{
		SwitchExpandState(GetCurSel());
		return ;
	}

	if (nChar == VK_F4 && m_pSelected)
	{
		CXTPPropertyGridInplaceButton* pButton = m_pSelected->GetInplaceButtons()->Find(XTP_ID_PROPERTYGRID_COMBOBUTTON);
		if (pButton)
			m_pSelected->OnInplaceButtonDown(pButton);
	}


	CListBox::OnKeyDown(nChar, nRepCnt, nFlags);
}

void CXTPPropertyGridView::OnSysKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{

	if ((nChar == VK_DOWN || nChar == VK_UP) && m_pSelected)
	{
		CXTPPropertyGridInplaceButton* pButton = m_pSelected->GetInplaceButtons()->Find(XTP_ID_PROPERTYGRID_COMBOBUTTON);
		if (pButton)
			m_pSelected->OnInplaceButtonDown(pButton);
	}

	CListBox::OnSysKeyDown(nChar, nRepCnt, nFlags);
}

BOOL CXTPPropertyGridView::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
	static BOOL bRelay = FALSE;
	if (m_wndTip.GetSafeHwnd() && m_wndTip.IsWindowVisible() && !bRelay)
	{
		bRelay = TRUE;
		RelayToolTipEvent(message);
		bRelay = FALSE;
	}

	if (message == WM_MOUSELEAVE && m_pHotButton)
	{
		m_pHotButton = NULL;
		Invalidate(FALSE);
	}

	if (m_pGrid->GetToolTipContext())
	{
		m_pGrid->GetToolTipContext()->FilterToolTipMessage(this, message, wParam, lParam);
	}

	return CWnd::OnWndMsg(message, wParam, lParam, pResult);
}


void CXTPPropertyGridView::OnSelectionChanged()
{
	CXTPPropertyGridItem* pItem = GetSelectedItem();

	if (m_pSelected) m_pSelected->OnDeselect();
	if (pItem) pItem->OnSelect();

	m_pSelected = pItem;

	if (m_pSelected) m_pGrid->OnSelectionChanged(m_pSelected);
}

int CXTPPropertyGridView::HitTest(CPoint point) const
{
	if (!m_bVariableSplitterPos)
		return -1;

	int nDivider = GetDividerPos();

	if ((point.x > nDivider - 4 && point.x <= nDivider + 2) && (GetCount() > 0))
		return PGV_HIT_SPLITTER;

	return -1;
}

BOOL CXTPPropertyGridView::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
	if (nHitTest == HTCLIENT)
	{
		CPoint point;

		GetCursorPos(&point);
		ScreenToClient(&point);

		if (HitTest(point) == PGV_HIT_SPLITTER)
		{
			SetCursor(m_hCursor);
			return TRUE;
		}
		if (m_pHotButton && m_pHotButton->IsHyperlink() && m_pHotButton->GetEnabled())
		{
			SetCursor(m_pGrid->m_hCursorHand);
			return TRUE;
		}
	}

	return CListBox::OnSetCursor(pWnd, nHitTest, message);
}

void CXTPPropertyGridView::OnSize(UINT nType, int cx, int cy)
{
	CListBox::OnSize(nType, cx, cy);

	OnSelectionChanged();

	Invalidate(FALSE);
}


// ToolTips routings

void CXTPPropertyGridView::_ShowToolTip(CRect rcBound, CRect rcText, CXTPPropertyGridItem* pItem, BOOL bValuePart)
{
	if (!m_wndTip.GetSafeHwnd() || m_strTipText.IsEmpty())
		return;

	m_wndTip.SetWindowText(m_strTipText);
	m_wndTip.MoveWindow(rcText);
	m_wndTip.Activate(TRUE, pItem, bValuePart);

	m_rcToolTip = rcBound;

	TRACKMOUSEEVENT tme =
	{
		sizeof(TRACKMOUSEEVENT), TME_LEAVE, m_hWnd, 0
	};
	_TrackMouseEvent (&tme);
}

CSize CXTPPropertyGridView::_GetTextExtent(const CString& str, CXTPPropertyGridItem* pItem, BOOL bValuePart)
{
	CWindowDC dc(this);
	CXTPFontDC font(&dc, GetPaintManager()->GetItemFont(pItem, bValuePart));
	return dc.GetTextExtent(str);
}

void CXTPPropertyGridView::ShowToolTip(CPoint pt)
{
	if (CXTPMouseMonitor::IsMouseHooked())
		return;

	if (!m_pGrid->m_bEnableTooltips)
		return;

	if (!m_wndTip.GetSafeHwnd())
	{
		m_wndTip.Create(this);
	}

	if (m_pHotButton)
		return;

	CXTPPropertyGridItem* pItem = ItemFromPoint(pt);

	if (!pItem || pItem->IsCategory() || !pItem->GetTooltip().IsEmpty())
		return;

	int nDividerPos = GetDividerPos();

	CRect rc = pItem->GetItemRect();
	CRect rcCaption(rc.left, rc.top, rc.left + nDividerPos, rc.bottom);

	if (rcCaption.PtInRect(pt))
	{
		if (m_strTipText == pItem->GetCaption())
			return;

		if (pItem->GetMarkupCaption())
			return;

		if (pItem->GetMetrics(FALSE, FALSE) && pItem->GetMetrics(FALSE, FALSE)->m_uDrawTextFormat & DT_WORDBREAK)
			return;

		m_strTipText = pItem->GetCaption();

		ClientToScreen(&rcCaption);

		CRect rcCaptionText(rcCaption);
		rcCaptionText.left += pItem->m_nIndent * XTP_PGI_EXPAND_BORDER + 3 - 3 -1;
		rcCaptionText.top--;
		GetPaintManager()->AdjustItemCaptionRect(pItem, rcCaptionText);

		int nTextExtent = _GetTextExtent(m_strTipText, pItem, FALSE).cx;

		if (nTextExtent + 3 > rcCaptionText.Width())
		{
			if (GetExStyle() & WS_EX_LAYOUTRTL)
			{
				rcCaptionText.left = rcCaption.right - nTextExtent - 7 - (pItem->m_nIndent * XTP_PGI_EXPAND_BORDER);

			}

			_ShowToolTip(rcCaption, rcCaptionText, pItem, FALSE);
		}
	}
	else
	{
		if (m_strTipText == pItem->GetViewValue())
			return;

		if (pItem->IsMultiLine())
			return;

		m_strTipText = pItem->GetViewValue();

		if (GetFocus() && GetFocus()->IsKindOf(RUNTIME_CLASS(CXTPPropertyGridInplaceEdit)))
		{
			if (((CXTPPropertyGridInplaceEdit*)(GetFocus()))->GetItem() == pItem)
				return;
		}

		if (pItem->IsSelected() && pItem->GetInplaceEdit().GetItem() == pItem &&
			pItem->GetInplaceEdit().GetSafeHwnd() && pItem->GetInplaceEdit().IsWindowVisible())
			return;

		if (pItem->GetMetrics(TRUE, FALSE) && pItem->GetMetrics(TRUE, FALSE)->m_uDrawTextFormat & DT_WORDBREAK)
			return;

		CRect rcValue(rc.left + nDividerPos, rc.top, rc.right, rc.bottom);
		ClientToScreen(&rcValue);

		CRect rcValueText = pItem->GetValueRect();
		rcValueText.InflateRect(1, 2, 0, 1);
		ClientToScreen(&rcValueText);

		int nTextExtent = _GetTextExtent(m_strTipText, pItem, TRUE).cx;

		if (nTextExtent + 3 > rcValueText.Width())
		{
			if (GetExStyle() & WS_EX_LAYOUTRTL)
			{
				rcValueText.left = rcValueText.right - 8 - nTextExtent;

			}
			_ShowToolTip(rcValue, rcValueText, pItem, TRUE);
		}
	}
}


void CXTPPropertyGridView::RelayToolTipEvent(UINT message)
{
	if (m_wndTip.GetSafeHwnd() && m_wndTip.IsWindowVisible())
	{
		CRect rc;
		m_wndTip.GetWindowRect(rc);

		CPoint pt;
		GetCursorPos(&pt);

		if (!m_rcToolTip.PtInRect(pt) || m_pHotButton)
		{
			m_strTipText = "";
			m_wndTip.Activate(FALSE, 0, 0);
		}

		switch (message)
		{
			case WM_MOUSEWHEEL:
				m_strTipText = "";
				m_wndTip.Activate(FALSE, 0, 0);
				break;

			case WM_KEYDOWN:
			case WM_SYSKEYDOWN:
			case WM_LBUTTONDOWN:
			case WM_RBUTTONDOWN:
			case WM_MBUTTONDOWN:
			case WM_LBUTTONUP:
			case WM_RBUTTONUP:
			case WM_MBUTTONUP:
			case WM_MOUSELEAVE:
				m_wndTip.Activate(FALSE, 0, 0);
				break;
		}
	}

}


void CXTPPropertyGridView::OnSetFocus(CWnd* pOldWnd)
{
	m_pFocusedButton = NULL;

	CListBox::OnSetFocus(pOldWnd);

	if (GetStyle() & LBS_MULTIPLESEL)
		Invalidate(FALSE);

}
void CXTPPropertyGridView::OnKillFocus (CWnd* pNewWnd)
{
	m_pFocusedButton = NULL;

	CListBox::OnKillFocus(pNewWnd);

	if (GetStyle() & LBS_MULTIPLESEL)
		Invalidate(FALSE);

}


CXTPPropertyGridPaintManager* CXTPPropertyGridView::GetPaintManager() const
{
	return m_pGrid->GetPaintManager();
}

CXTPImageManager* CXTPPropertyGridView::GetImageManager() const
{
	return m_pGrid->GetImageManager();
}

//////////////////////////////////////////////////////////////////////////
// Accessible

LRESULT CXTPPropertyGridView::OnGetObject(WPARAM wParam, LPARAM lParam)
{
	if (((LONG)lParam) != OBJID_CLIENT)
		return (LRESULT)Default();

	LPUNKNOWN lpUnknown = GetInterface(&IID_IAccessible);
	if (!lpUnknown)
		return E_FAIL;

	return LresultFromObject(IID_IAccessible, wParam, lpUnknown);
}

BEGIN_INTERFACE_MAP(CXTPPropertyGridView, CCmdTarget)
	INTERFACE_PART(CXTPPropertyGridView, IID_IAccessible, ExternalAccessible)
END_INTERFACE_MAP()

CCmdTarget* CXTPPropertyGridView::GetAccessible()
{
	return this;
}

HRESULT CXTPPropertyGridView::GetAccessibleParent(IDispatch* FAR* ppdispParent)
{
	*ppdispParent = NULL;

	if (GetSafeHwnd())
	{
		return AccessibleObjectFromWindow(GetSafeHwnd(), OBJID_WINDOW, IID_IDispatch, (void**)ppdispParent);
	}
	return E_FAIL;
}

HRESULT CXTPPropertyGridView::GetAccessibleChildCount(long FAR* pChildCount)
{
	if (pChildCount == 0)
	{
		return E_INVALIDARG;
	}

	*pChildCount = CListBox::GetCount();
	return S_OK;
}

HRESULT CXTPPropertyGridView::GetAccessibleChild(VARIANT varChild, IDispatch* FAR* ppdispChild)
{
	*ppdispChild = NULL;
	int nChild = GetChildIndex(&varChild);

	if (nChild <= 0)
	{
		return E_INVALIDARG;
	}

	CXTPPropertyGridItem* pItem = GetItem(nChild - 1);
	if (!pItem)
	{
		return E_INVALIDARG;
	}

	*ppdispChild = pItem->GetIDispatch(TRUE);
	return S_OK;
}

HRESULT CXTPPropertyGridView::GetAccessibleName(VARIANT varChild, BSTR* pszName)
{
	int nChild = GetChildIndex(&varChild);

	if (nChild == CHILDID_SELF || nChild == -1)
	{
		*pszName = SysAllocString(L"Properties Window");
		return S_OK;
	}

	return E_INVALIDARG;
}

HRESULT CXTPPropertyGridView::GetAccessibleRole(VARIANT varChild, VARIANT* pvarRole)
{
	pvarRole->vt = VT_EMPTY;
	int nChild = GetChildIndex(&varChild);

	if (nChild == CHILDID_SELF)
	{
		pvarRole->vt = VT_I4;
		pvarRole->lVal = ROLE_SYSTEM_TABLE;
		return S_OK;
	}

	return E_INVALIDARG;
}

HRESULT CXTPPropertyGridView::GetAccessibleState(VARIANT varChild, VARIANT* pvarState)
{
	pvarState->vt = VT_I4;
	pvarState->lVal = 0;
	int nChild = GetChildIndex(&varChild);

	if (nChild == CHILDID_SELF)
	{
		pvarState->lVal = STATE_SYSTEM_FOCUSABLE;
	}

	return S_OK;
}


HRESULT CXTPPropertyGridView::AccessibleLocation(long* pxLeft, long* pyTop, long* pcxWidth, long* pcyHeight, VARIANT varChild)
{
	*pxLeft = *pyTop = *pcxWidth = *pcyHeight = 0;

	if (!GetSafeHwnd())
		return S_OK;

	CRect rc;
	GetWindowRect(&rc);

	int nChild = GetChildIndex(&varChild);

	if (nChild != CHILDID_SELF)
	{
		return E_INVALIDARG;
	}

	*pxLeft = rc.left;
	*pyTop = rc.top;
	*pcxWidth = rc.Width();
	*pcyHeight = rc.Height();

	return S_OK;
}

HRESULT CXTPPropertyGridView::AccessibleHitTest(long xLeft, long yTop, VARIANT* pvarID)
{
	if (pvarID == NULL)
		return E_INVALIDARG;

	pvarID->vt = VT_EMPTY;

	if (!GetSafeHwnd())
		return S_FALSE;

	if (!CXTPWindowRect(this).PtInRect(CPoint(xLeft, yTop)))
		return S_FALSE;

	pvarID->vt = VT_I4;
	pvarID->lVal = CHILDID_SELF;

	CPoint pt(xLeft, yTop);
	ScreenToClient(&pt);

	CXTPPropertyGridItem* pItem = ItemFromPoint(pt);

	if (pItem)
	{
		pvarID->vt = VT_DISPATCH;
		pvarID->pdispVal = pItem->GetIDispatch(TRUE);
	}

	return S_OK;
}