// XTPReportRow.cpp : implementation of the CXTPReportRow class.
//
// This file is a part of the XTREME REPORTCONTROL 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/XTPToolTipContext.h"
#include "Common/XTPMarkupRender.h"
#include "Common/XTPSystemHelpers.h"
#include "Common/XTPResourceManager.h"
#include "Common/XTPCustomHeap.h"
#include "Common/XTPSmartPtrInternalT.h"
#include "Common/XTPColorManager.h"

#include "XTPReportDefines.h"
#include "XTPReportRecordItemRange.h"
#include "XTPReportRecord.h"
#include "XTPReportControl.h"
#include "XTPReportPaintManager.h"
#include "XTPReportRow.h"
#include "XTPReportRows.h"
#include "XTPReportSelectedRows.h"
#include "XTPReportColumns.h"
#include "XTPReportColumn.h"
#include "XTPReportTip.h"
#include "XTPReportHeader.h"
#include "XTPReportSection.h"
#include "XTPReportBorder.h"
#include "XTPReportRecordItem.h"
#include "ItemTypes/XTPReportRecordItemPreview.h"
#include "ItemTypes/XTPReportRecordItemIcon.h"

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

IMPLEMENT_DYNAMIC(CXTPReportRow, CCmdTarget)
/////////////////////////////////////////////////////////////////////////////
// CXTPReportRow

CXTPReportRow::CXTPReportRow()
	: m_pParentRows(NULL)
	, m_pParentRow (NULL)
	, m_pRecord    (NULL)
	, m_pChilds    (NULL)
	, m_pControl   (NULL)
	, m_pSection   (NULL)

	, m_nIndex     (-1)
	, m_nChildIndex(-1)

	, m_nGroupLevel (0)
	, m_nRowLevel   (0)

	, m_bVisible   (FALSE)
	, m_bExpanded  (TRUE)
	, m_bLockExpand(FALSE)
{


	m_rcRow.SetRectEmpty();
	m_rcCollapse.SetRectEmpty();
	m_bHasSelectedChilds = FALSE;

	m_nPreviewHeight = 0;
	m_nFreeHeight = 0;

	EnableAutomation();
}

void CXTPReportRow::InitRow(
	CXTPReportControl *pControl,
	CXTPReportSection *pSection,
	CXTPReportRecord  *pRecord)
{
	ASSERT(pRecord || IsGroupRow());

	m_pControl = pControl;
	m_pSection = pSection;

	if (m_pControl->m_bFreeHeightMode)
		m_nFreeHeight = m_pControl->m_nDefaultRowFreeHeight;

	if (pRecord)
	{
		m_pRecord = pRecord;
		m_bExpanded = pRecord->m_bExpanded;
		m_pRecord->InternalAddRef();
	}
}

void CXTPReportRow::InitRow(CXTPReportRow *pRow)
{
	ASSERT(pRow->m_pRecord);
	ASSERT(pRow->m_pParentRow == NULL);

	m_pControl = pRow->m_pControl;
	m_pRecord  = pRow->m_pRecord;
	m_pSection = pRow->m_pSection;

	if (NULL != m_pRecord)
	{
		m_pRecord->InternalAddRef();
	}

	m_rcRow          = pRow->m_rcRow;
	m_rcCollapse     = pRow->m_rcCollapse;
	m_bVisible       = pRow->m_bVisible;
	m_bExpanded      = pRow->m_bExpanded;
	m_nIndex         = pRow->m_nIndex;
	m_nPreviewHeight = pRow->m_nPreviewHeight;
	m_nRowLevel      = pRow->m_nRowLevel;
	m_nGroupLevel    = pRow->m_nGroupLevel;

	if (m_pControl->m_bFreeHeightMode)
		m_nFreeHeight = m_pControl->m_nDefaultRowFreeHeight;
}


CXTPReportRows* CXTPReportRow::GetChilds(BOOL bCreate)
{
	if (!m_pChilds && bCreate)
		m_pChilds = new CXTPReportRows();

	return m_pChilds;
}



CXTPReportRow::~CXTPReportRow()
{
	if (m_pChilds)
		m_pChilds->InternalRelease();

	if (m_pRecord)
		m_pRecord->InternalRelease();
}

int CXTPReportRow::GetHeight(CDC *pDC, int nWidth)
{
	ASSERT(m_pControl);
	if (!m_pControl)
		return 0;

	int nHeight = m_pControl->GetPaintManager()->GetRowHeight(pDC, this, nWidth);

	if (!IsGroupRow() && !IsItemsVisible())
	{
		nHeight = 0;
	}


	m_nPreviewHeight = 0;

	if (IsPreviewVisible())
	{
		CXTPReportRecordItemPreview *pItem = GetRecord()->GetItemPreview();

		m_nPreviewHeight = pItem->GetPreviewHeight(pDC, this, nWidth);
		m_nPreviewHeight = m_pControl->GetPaintManager()->GetPreviewItemHeight(pDC, this, nWidth, m_nPreviewHeight);
	}

	return nHeight + m_nPreviewHeight;
}


BOOL CXTPReportRow::IsFocused() const
{
	return m_pSection->GetRows()->GetFocusedRowIndex() == m_nIndex;
}

BOOL CXTPReportRow::IsSelected() const
{
	return m_pControl->GetSelectedRows()->Contains(this);
}

void CXTPReportRow::SetSelected(BOOL bSelected)
{
	if (bSelected == IsSelected())
		return;

	if (bSelected)
	{
		if (!m_pControl->IsMultipleSelection())
			m_pControl->GetSelectedRows()->Clear();

		m_pControl->GetSelectedRows()->Add(this);
	}
	else
	{
		m_pControl->GetSelectedRows()->Remove(this);
	}

	m_pControl->RedrawControl();
}

int CXTPReportRow::GetLastChildRow(CXTPReportRows* pChilds) const
{
	CXTPReportRow* pRow = pChilds->GetAt(pChilds->GetCount() - 1);

	if (!pRow)
		return -1;

	if (pRow->HasChildren() && pRow->IsExpanded())
		return GetLastChildRow(pRow->GetChilds());

	return pRow->GetIndex();
}

void CXTPReportRow::SelectChilds()
{
	BOOL bM = m_pControl->IsMultipleSelection();
	BOOL bC = HasChildren();
	BOOL bE = IsExpanded();
	if (!(bM && bC && bE && m_nIndex != -1))
		return;

	m_pControl->BeginUpdate();

	int nIndexBegin = m_nIndex + 1;
	int nIndexEnd = GetLastChildRow(GetChilds());

	m_pControl->GetSelectedRows()->AddBlock(nIndexBegin, nIndexEnd);

	m_pControl->EndUpdate();
}

BOOL CXTPReportRow::IsItemsVisible() const
{
	return TRUE;
}


BOOL CXTPReportRow::IsPreviewVisible() const
{
	return !IsGroupRow()
		&& m_pRecord
		&& m_pControl->IsPreviewMode()
		&& !m_pControl->IsIconView()
		&& m_pRecord->GetItemPreview();
}


void CXTPReportRow::DrawItemGrid(CDC *pDC, CXTPReportColumn *pColumn, CRect rcItem)
{
	CXTPReportPaintManager *pPaintManager = m_pControl->GetPaintManager();

	const BOOL bGridVisible = pPaintManager->IsGridVisible(FALSE);

	rcItem.DeflateRect(0,0,0,1);

	// Draw horizontal grid
	if (bGridVisible && !IsPreviewVisible())
	{
		pPaintManager->DrawGrid(pDC, xtpReportOrientationHorizontal, rcItem);
	}

	// Draw vertical grid
	switch(GetType())
	{
		case xtpRowTypeBody:
		{
			pPaintManager->DrawGrid(pDC, xtpReportOrientationVertical, rcItem);
		}
		break;

		case xtpRowTypeHeader:
		{
			if (pColumn->GetDrawHeaderRowsVGrid())
			{
				pPaintManager->DrawGrid(pDC, xtpReportOrientationVertical, rcItem);
			}
		}
		break;

		case xtpRowTypeFooter:
		{
			if (pColumn->GetDrawFooterRowsVGrid())
			{
				pPaintManager->DrawGrid(pDC, xtpReportOrientationVertical, rcItem);
			}
		}
		break;
	} // switch
}


BOOL AdjustClipRect(CDC *pDC, CRect &rcClip)
{
	if (NULL != pDC && pDC->IsKindOf(RUNTIME_CLASS(CPreviewDC)))
	{
		CPreviewDC *pPreviewDC = DYNAMIC_DOWNCAST(CPreviewDC, pDC);

		pPreviewDC->PrinterDPtoScreenDP(&rcClip.TopLeft());
		pPreviewDC->PrinterDPtoScreenDP(&rcClip.BottomRight());

		// Offset the result by the viewport
		// origin of the print preview window
		CPoint ptOrg;

		// Note: pDC->GetViewportOrg() uses the m_hAttributDC
		::GetViewportOrgEx(pDC->m_hDC, &ptOrg);
		rcClip += ptOrg;
	}

	return TRUE;
}


void CXTPReportRow::Draw(CDC *pDC, CRect rcRow, CRect rcClip, int nLeftOffset,
	CXTPReportRecordMergeItems &mergeItems, int nColumnFrom, int nColumnTo)
{
	// Check preconditions
	ASSERT(NULL != m_pControl);

	if (NULL == m_pControl)
	{
		return;
	}

	const BOOL bIsPrinting = pDC->IsPrinting();
	const int nFreezeCount = m_pControl->GetFreezeColumnsCount();
	const int nFreezeIndex = m_pControl->GetFreezeColumnsIndex();
	const int nHeaderIndent = m_pControl->GetHeaderIndent();

	// Paint Manager
	CXTPReportPaintManager *pPaintManager = m_pControl->GetPaintManager();
	const BOOL bGridVisible = pPaintManager->IsGridVisible(xtpReportOrientationHorizontal);

	int nIconColumnIndex = -1;
	CXTPReportColumn* pRecNumCol = m_pControl->GetColumns()->Find(m_pControl->m_iColumnForNum);
	if (pRecNumCol)
		nIconColumnIndex = pRecNumCol->GetIndex();

	m_rcRow        = rcRow;
	m_rcRow.left  -= nLeftOffset;
	m_rcRow.right -= nLeftOffset;

	if (nFreezeCount == 0)
		rcRow = m_rcRow;

	XTP_REPORTRECORDITEM_DRAWARGS drawArgs;
	drawArgs.pDC      = pDC;
	drawArgs.pControl = m_pControl;
	drawArgs.pRow     = this;

	CRect rcIndent(nFreezeCount ? rcRow : m_rcRow);
	rcIndent.right = rcIndent.left + nHeaderIndent;

	CRect rcItem(m_rcRow);
	rcItem.bottom -= m_nPreviewHeight; // Deduct preview item

	int nFreezeRight = 0;

	if (nFreezeCount > 0)
	{
		CXTPReportColumn *pColumn = m_pControl->GetColumns()->GetAt(nFreezeIndex);
		nFreezeRight = pColumn->GetRect().right;
	}

	if (NULL != m_pRecord) // if drawing record, not group
	{
		if (bIsPrinting)
		{
			rcItem.right = rcRow.left;
		}

		///////////////////////////////////////////////////////////////////////
		// Paint record items
		///////////////////////////////////////////////////////////////////////
		CRect rcClipRow;   // Clipping rectangle for row
		CRect rcClipItem;  // Clipping rectangle for item

		BOOL bFirstColumn = TRUE;

		int nColumnPos = -1;

		for (int nColumn=nColumnFrom; nColumn<nColumnTo; nColumn++)
		{
			CXTPReportColumn *pColumn = m_pControl->GetColumns()->GetAt(nColumn);

			if (NULL != pColumn && pColumn->IsVisible() && IsItemsVisible())
			{
				++nColumnPos;

				/////////////////////////////////////////////////////////////////
				// Determine item position
				/////////////////////////////////////////////////////////////////

				rcItem.bottom  = m_rcRow.bottom;
				rcItem.bottom -= m_nPreviewHeight;

				if (bIsPrinting) // Printing
				{
					if (pPaintManager->IsColumnWidthWYSIWYG())
					{
						rcItem.left  = rcItem.right;
						rcItem.right = rcItem.left + pColumn->GetWidth();
					}
					else
					{
						rcItem.left  = rcItem.right;
						rcItem.right = rcItem.left + pColumn->GetPrintWidth(rcRow.Width());
					}
				}
				else // Drawing
				{
					rcItem.left  = pColumn->GetRect().left;
					rcItem.right = pColumn->GetRect().right;
				}

				// Offset first column by header indentation
				if (bFirstColumn)
				{
					bFirstColumn = FALSE;
					rcItem.left += nHeaderIndent;
				}

				/////////////////////////////////////////////////////////////////
				//
				/////////////////////////////////////////////////////////////////
				BOOL bDrawFreeze = nColumn<=nFreezeIndex;

				if (bDrawFreeze)
				{
					// Frozen column
					rcClipRow.SetRect(rcRow.left, rcRow.top, nFreezeRight, rcRow.bottom);
				}
				else
				{
					// Non-frozen column
					rcClipRow.SetRect(nFreezeRight, rcRow.top, rcRow.right, rcRow.bottom);
				}

				// Determine clipping rectangle
				rcClipItem.IntersectRect(rcItem, rcClip);        // Clip section
				rcClipItem.IntersectRect(rcClipItem, rcClipRow); // Clip row

				// In print preview mode the clipping rectangle needs to be
				// adjusted before creating the clipping region
				//AdjustClipRect(pDC, rcClipItem);


				if (!bDrawFreeze && !bIsPrinting && rcItem.right < nFreezeRight)
				{
					// Column is entirely under frozen column, do not draw
					continue;
				}

				if (!CRect().IntersectRect(rcClip, rcItem) && !bIsPrinting)
					continue;

				// Create clipping region
				CRgn rgnClipItem;
				if (!bIsPrinting)
				{
					rgnClipItem.CreateRectRgnIndirect(rcClipItem);
					pDC->SelectClipRgn(&rgnClipItem, RGN_COPY);
				}

				CXTPReportRecordItem *pItem = m_pRecord->GetItem(pColumn);

#if 0
				CXTPReportBorder *pBorder = pPaintManager->GetItemBorder(pItem);

				if (NULL != pBorder)
				{
					pBorder->Draw(pDC, rcItem);
					rcItem = CXTPReportBorder::DeflateRect(rcItem, pBorder);
				}
#endif
				// Draw row background
				pPaintManager->FillRow(pDC, this, rcItem);

				// Draw shade background if sorted by this row
				if (pColumn->IsSorted())
				{
					pPaintManager->FillItemShade(pDC, rcItem);
				}

				if (NULL != pItem)
				{
					// draw item
					drawArgs.pColumn    = pColumn;
					drawArgs.rcItem     = rcItem;
					drawArgs.nTextAlign = pColumn->GetAlignment();
					drawArgs.pItem      = pItem;

					if (!pItem->IsMerged())
					{
						int nItemTextWidth = pItem->Draw(&drawArgs);
						pColumn->m_nMaxItemWidth = max(pColumn->m_nMaxItemWidth, nItemTextWidth);
					}
					else
					{
						CXTPReportRecordItem *pMergeItem = pItem->GetMergeItem();
						CXTPReportRecordMergeItemId itemId(nColumnPos, nColumn, GetIndex());
						mergeItems[pMergeItem].AddItem(itemId, rcItem);
					}
				}
				else if (m_pControl->IsIconView() && !m_pControl->IsVirtualMode())
				{
					// IconView - non Virtual Mode!
					pItem = new CXTPReportRecordItemIcon();
					drawArgs.pColumn = pColumn;
					drawArgs.rcItem = rcItem;
					CXTPReportColumn* pDataColumn = m_pControl->GetColumns()->GetAt(m_pControl->m_iIconPropNum);
					CXTPReportRecordItem* pDataItem = GetRecord()->GetItem(m_pControl->m_iIconPropNum);
					if (pDataColumn != NULL && pDataItem != NULL)
					{
						pItem->SetCaption(pDataItem->GetCaption(pDataColumn));
						pItem->SetIconIndex(m_pControl->m_iIconNum);
					}
					drawArgs.pItem = pItem;
					pItem->Draw(&drawArgs);
					delete pItem;
					pItem = NULL;
				}
				else if (nIconColumnIndex > -1
					&& !m_pControl->IsIconView()
					&& !m_pControl->IsVirtualMode()) //no Item! (ghost)
				{
					CRect rcIcon = m_pControl->GetColumns()->GetAt(nIconColumnIndex)->GetRect();
					rcIcon.top = m_rcRow.top;
					rcIcon.bottom = m_rcRow.bottom - m_nPreviewHeight;
					rcIcon.left = 0;
					pPaintManager->DrawRowNumber(pDC, rcIcon, this);
				}
#if 1
				if (pItem && !pItem->IsMerged())
				{
					CRect rcGridItem(rcItem);
					rcGridItem.left--;
					DrawItemGrid(pDC, pColumn, rcGridItem);
				}
#endif
				// Release clipping
				if (!bIsPrinting)
				{
					pDC->SelectClipRgn(NULL, RGN_COPY);
					rgnClipItem.DeleteObject();
				}

				if (nColumn == nFreezeIndex)
				{
					pPaintManager->DrawFreezeColsDivider(pDC, rcItem, GetControl(), this);
				}
			}
		} // for (nColumn)

		if (IsPreviewVisible())
		{
			CRect rcPreviewItem(m_rcRow);
			rcPreviewItem.DeflateRect(nHeaderIndent, rcPreviewItem.Height() - m_nPreviewHeight, 0, 0);

			drawArgs.rcItem     = rcPreviewItem;
			drawArgs.nTextAlign = DT_LEFT;
			drawArgs.pColumn    = NULL;
			drawArgs.pItem      = m_pRecord->GetItemPreview();

			// Determine clipping rectangle
			rcClipItem.IntersectRect(rcPreviewItem, rcClip); // Clip section
			rcClipItem.IntersectRect(rcClipItem, rcClipRow); // Clip row

			CRgn rgnClipItem;
			if (!bIsPrinting)
			{
				rgnClipItem.CreateRectRgnIndirect(rcClipItem);
				pDC->SelectClipRgn(&rgnClipItem, RGN_COPY);
			}

			drawArgs.pItem->Draw(&drawArgs);

			// Release clipping
			if (!bIsPrinting)
			{
				pDC->SelectClipRgn(NULL, RGN_COPY);
				rgnClipItem.DeleteObject();
			}
		}

		if (nHeaderIndent > 0) // draw indent column
		{
			rcIndent.IntersectRect(rcIndent, rcClip);
			pPaintManager->FillIndent(pDC, rcIndent);
		}
	} // (NULL != m_pRecord)

	CRgn rgnClip;
	if (!bIsPrinting)
	{
		rgnClip.CreateRectRgnIndirect(&rcClip);
		pDC->SelectClipRgn(&rgnClip, RGN_COPY);
	}

	CRect rcFocus(rcIndent.right, m_rcRow.top, m_rcRow.right, m_rcRow.bottom - (bGridVisible ? 1 : 0));

	if (!m_pControl->IsIconView() && IsFocused() && m_pControl->HasFocus() && m_pControl->IsRowFocusVisible())
	{
		pPaintManager->DrawFocusedRow(pDC, rcFocus);
	}

	if (nHeaderIndent > 0 && bGridVisible)
	{
		if (m_nIndex < m_pControl->GetRows()->GetCount() - 1)
		{
			CXTPReportRow *pNextRow = m_pControl->GetRows()->GetAt(m_nIndex+1);
			ASSERT(pNextRow);
			rcFocus.left = rcIndent.left + min(nHeaderIndent, pPaintManager->m_nTreeIndent * pNextRow->GetTreeDepth());
		}
		else
		{
			rcFocus.left = m_rcRow.left;
		}
		pPaintManager->DrawGrid(pDC, xtpReportOrientationHorizontal, rcFocus);
	}

	if (IsPreviewVisible())
	{
		pPaintManager->DrawGrid(pDC, xtpReportOrientationHorizontal, rcFocus);
	}

	if (!bIsPrinting)
	{
		pDC->SelectClipRgn(NULL, RGN_COPY);
		rgnClip.DeleteObject();
	}
}

int CXTPReportRow::GetTreeDepth() const
{
	return m_nRowLevel;
}

INT_PTR CXTPReportRow::OnToolHitTest(CPoint point, TOOLINFO* pTI)
{
	CRect rcItem;
	CXTPReportRecordItem* pItem = HitTest(point, &rcItem);

	if (!pItem)
		return -1;


	INT_PTR nHit = pItem->OnToolHitTest(point, pTI);
	if (nHit != -1)
		return nHit;

	nHit = (INT_PTR) pItem;
	CString strTip = pItem->GetTooltip();

	m_pControl->OnGetToolTipInfo(this, pItem, strTip);

	if (strTip.IsEmpty() || strTip == _T(" "))
		return -1;

	if (m_pControl->GetPaintManager()->m_bCleanTooltip)
	{
		//Clean markup
		CString s(strTip), u, v;
		int j = s.Find(_T("<"));
		int k = s.Find(_T(">"));
		while (j > -1 && k > -1)
		{
			u = s.Left(j);
			v = s.Mid(k + 1);
			s = u + v;
			strTip = s;
			j = s.Find(_T("<"));
			k = s.Find(_T(">"));
		}
	}

	CXTPToolTipContext::FillInToolInfo(pTI, m_pControl->m_hWnd, rcItem, nHit, strTip);

	return nHit;
}

BOOL CXTPReportRow::OnLButtonDown(XTP_REPORTRECORDITEM_CLICKARGS *pClickArgs)
{
	if (m_pControl->m_bFreeHeightMode)
	{
		XTPReportMouseMode mouseMode = m_pControl->GetMouseMode();
		if (mouseMode == xtpReportMouseOverRowDivide)
		{
			m_pControl->SetCapture();
			m_pControl->SetMouseMode(xtpReportMouseNothing);
			CPoint point = pClickArgs->ptClient;
			m_pControl->ClientToScreen(&point);
			CRect rcControl = m_pControl->GetReportRectangle();
			CRect rcRow(GetRect());
			rcRow.right = rcControl.right;
			m_pControl->ClientToScreen(&rcRow);
			m_pControl->ClientToScreen(&rcControl);

			CXTPSplitterTracker tracker(TRUE, m_pControl->m_bDesktopTrackerMode);

			CRect rcBound(rcRow.left, rcRow.top + m_pControl->m_nDefaultRowFreeHeight, rcRow.right, point.y);
			CRect rcTracker(rcRow.left, point.y - 2, rcRow.right, point.y - 1);
			CRect rcAvail(rcRow.left, rcRow.top + m_pControl->m_nDefaultRowFreeHeight, rcRow.right, rcControl.bottom);

			if (tracker.Track(m_pControl, rcAvail, rcTracker, point, FALSE))
			{
				m_nFreeHeight = rcTracker.bottom - rcRow.top;

				if (GetRecord())
					GetRecord()->m_nFreeHeight = m_nFreeHeight;

				m_pControl->AdjustScrollBars();
				m_pControl->RedrawControl();
			}
			return TRUE;
		}
	}

	if (pClickArgs->pItem && pClickArgs->pItem->OnLButtonDown(pClickArgs))
	{
		return TRUE;
	}

	return (BOOL) m_pControl->SendMessageToParent(this, pClickArgs->pItem, pClickArgs->pColumn, XTP_NM_REPORT_LBUTTONDOWN, &pClickArgs->ptClient);
}

BOOL CXTPReportRow::OnLButtonUp(XTP_REPORTRECORDITEM_CLICKARGS *pClickArgs)
{
	if (m_pControl->m_bFreeHeightMode)
	{
		m_pControl->SetMouseMode(xtpReportMouseNothing);
		ReleaseCapture();
	}

	if (pClickArgs->pItem && pClickArgs->pItem->OnLButtonUp(pClickArgs))
	{
		return TRUE;
	}

	return (BOOL) m_pControl->SendMessageToParent(this, pClickArgs->pItem, pClickArgs->pColumn, XTP_NM_REPORT_LBUTTONUP, &pClickArgs->ptClient);
}



BOOL CXTPReportRow::IsLockExpand() const
{
	BOOL bLockExpand = FALSE;

	// Locking on control level
	if (m_pControl)
	{
		bLockExpand = bLockExpand || m_pControl->IsLockExpand();
	}

	// Locking on rows level (not supported)
	if (m_pParentRows)
	{
		//bLockExpand = bLockExpand || m_pParentRows;
	}

	// Locking on row level
	bLockExpand = bLockExpand || m_bLockExpand;

	// Locking on record level (not supported)
	if (m_pRecord)
	{
		//bLockExpand = bLockExpand || m_pRecord;
	}

	return bLockExpand;
}

void CXTPReportRow::OnClick(CPoint ptClicked)
{
	XTP_REPORTRECORDITEM_CLICKARGS clickArgs;
	clickArgs.pControl = m_pControl;
	clickArgs.pRow     = this;
	clickArgs.pColumn  = NULL;
	clickArgs.ptClient = ptClicked;

	// find clicked item
	clickArgs.pItem = HitTest(ptClicked, &clickArgs.rcItem, &clickArgs.pColumn);

	if (m_pControl->IsVirtualMode())
	{
		if (m_rcCollapse.PtInRect(ptClicked)
			&& clickArgs.pColumn && clickArgs.pColumn->IsTreeColumn()
			&& m_rcCollapse.PtInRect(m_pControl->m_mouseDownState.ptMouse))
		{
			m_pControl->SendMessageToParent(this, NULL, clickArgs.pColumn, XTP_NM_REPORT_ROWEXPANDED, NULL);
			return;
		}
	}
	if (HasChildren() && m_rcCollapse.PtInRect(ptClicked)
		&& clickArgs.pColumn && clickArgs.pColumn->IsTreeColumn()
		&& m_rcCollapse.PtInRect(m_pControl->m_mouseDownState.ptMouse)
		&& !IsLockExpand())
	{
		SetExpanded(!IsExpanded());
		return;
	}

	// notify item if found
	if (!clickArgs.pItem)
		return;

	clickArgs.pItem->OnClick(&clickArgs);
}

void CXTPReportRow::OnDblClick(CPoint ptClicked)
{
	XTP_REPORTRECORDITEM_CLICKARGS clickArgs;
	clickArgs.pControl = m_pControl;
	clickArgs.pRow = this;
	clickArgs.ptClient = ptClicked;
	clickArgs.pColumn = NULL;

	// find clicked item
	clickArgs.pItem = HitTest(ptClicked, &clickArgs.rcItem, &clickArgs.pColumn);

	// notify item if found
	if (clickArgs.pItem != NULL)
	{
		clickArgs.pItem->OnDblClick(&clickArgs);
	}
	else
	{
		// just notify parent
		m_pControl->SendMessageToParent(this, NULL, clickArgs.pColumn, NM_DBLCLK, &ptClicked, -1);
	}
}

CRect CXTPReportRow::GetItemRect(CXTPReportRecordItem* pItem)
{
	if (!IsItemsVisible() || !pItem || !m_pRecord)
		return CRect(0, 0, 0, 0);

	if (pItem->IsMerged())
	{
	}

	if (IsPreviewVisible() && pItem == GetRecord()->GetItemPreview())
	{
		return CRect(m_rcRow.left + m_pControl->GetHeaderIndent(),
			m_rcRow.bottom - m_nPreviewHeight, m_rcRow.right, m_rcRow.bottom);
	}

	CXTPReportColumns* pColumns = m_pControl->GetColumns();
	int nColumnCount = pColumns->GetCount();

	CRect rcItem(0, m_rcRow.top, 0, m_rcRow.bottom - m_nPreviewHeight);

	int nFreezeColCount = m_pControl->GetFreezeColumnsCount();
	if (nFreezeColCount > m_pControl->GetColumns()->GetCount() - 1)
	{
		m_pControl->SetFreezeColumnsCount(0);
		nFreezeColCount = 0;
	}

	int nLeft = nFreezeColCount ? pColumns->GetAt(nFreezeColCount - 1)->GetRect().right : 0;

	for (int nColumn = 0; nColumn < nColumnCount; nColumn++)
	{
		CXTPReportColumn* pColumn = pColumns->GetAt(nColumn);
		if (pColumn && pColumn->IsVisible())
		{
			if (m_pRecord->GetItem(pColumn) != pItem)
				continue;

			rcItem.left = pColumn->GetRect().left;
			rcItem.right = pColumn->GetRect().right;
			if (pColumn->GetIndex() >= nFreezeColCount)
			{
				rcItem.left = max(rcItem.left, nLeft);
				rcItem.right = max(rcItem.right, nLeft);
			}
			if (rcItem.Width() <= 0)
				return CRect(0, 0, 0, 0);
			ShiftTreeIndent(rcItem, pColumn);
			return rcItem;
		}
	}
	return CRect(0, 0, 0, 0);
}

void CXTPReportRow::ShiftTreeIndent(CRect& rcItem, CXTPReportColumn* pColumn) const
{
	if (pColumn->IsTreeColumn())
	{
		int nTreeDepth = GetTreeDepth() - m_nGroupLevel;
		if (nTreeDepth > 0)
			nTreeDepth++;
		rcItem.left += m_pControl->GetIndent(nTreeDepth);

		CRect rcBitmap(rcItem);
		int nIndent = m_pControl->GetPaintManager()->DrawCollapsedBitmap(NULL, this, rcBitmap).cx;
		rcItem.left += nIndent + 2;
	}
}

CXTPReportRecordItem* CXTPReportRow::HitTest(CPoint ptPoint, CRect* pRectItem, CXTPReportColumn** ppColumn) const
{
	if (!m_pRecord)
		return NULL;

	// find record item
	int x = m_rcRow.left + m_pControl->GetHeaderIndent();

	CXTPReportColumns* pColumns = m_pControl->GetColumns();
	int nColumnCount = pColumns->GetCount();

	// if hittest for Preview item
	if (IsPreviewVisible())
	{
		CXTPReportRecordItemPreview* pPreviewItem = GetRecord()->GetItemPreview();
		if (pPreviewItem)
		{
			CRect rcItem(x, m_rcRow.bottom - m_nPreviewHeight, m_rcRow.right, m_rcRow.bottom);
			if (rcItem.PtInRect(ptPoint))
			{
				if (pRectItem)
				{
					*pRectItem = rcItem;
				}
				return pPreviewItem;
			}
		}
	}

	CRect rcItem(0, m_rcRow.top, 0, m_rcRow.bottom - m_nPreviewHeight);

	if (!IsItemsVisible())
		return NULL;

	int nFreezeColCount = m_pControl->GetFreezeColumnsCount();
	if (nFreezeColCount > m_pControl->GetColumns()->GetCount() - 1)
	{
		m_pControl->SetFreezeColumnsCount(0);
		nFreezeColCount = 0;
	}

	int nLeft = nFreezeColCount ? pColumns->GetAt(nFreezeColCount - 1)->GetRect().right : 0;

	for (int nColumn = 0; nColumn < nColumnCount; nColumn++)
	{
		CXTPReportColumn* pColumn = pColumns->GetAt(nColumn);
		if (pColumn && pColumn->IsVisible())
		{
			rcItem.left = pColumn->GetRect().left;
			rcItem.right = pColumn->GetRect().right;
			if (pColumn->GetIndex() >= nFreezeColCount)
			{
				rcItem.left = max(rcItem.left, nLeft);
				rcItem.right = max(rcItem.right, nLeft);
			}
			if (!rcItem.Width())
				continue;

			if (rcItem.PtInRect(ptPoint) && ppColumn)
			{
				*ppColumn = pColumn;
			}
			// make tooltip shift if tree view (see also Draw function)
			ShiftTreeIndent(rcItem, pColumn);

			// check point
			if (rcItem.PtInRect(ptPoint))
			{
				if (pRectItem)
				{
					*pRectItem = rcItem;
				}

				CXTPReportRecordItem *pItem = m_pRecord->GetItem(pColumn);

				if (pItem && pItem->IsMerged())
				{
					pItem = pItem->GetMergeItem();
				}

				return pItem;
			}
		}
	}

	return NULL;
}

void CXTPReportRow::OnMouseMove(UINT nFlags, CPoint point)
{
	if (m_pControl->m_bFreeHeightMode)
	{
		XTPReportMouseMode mouseMode = m_pControl->GetMouseMode();
		if (mouseMode == xtpReportMouseNothing || mouseMode == xtpReportMouseOverRowDivide)
		{
			CRect rc = GetRect();
			if (rc.bottom - point.y > -2 && rc.bottom - point.y < 4)
			{
				if (mouseMode == xtpReportMouseNothing)
					SetCursor(XTPResourceManager()->LoadCursor(XTP_IDC_VSPLITBAR));

				m_pControl->SetMouseMode(xtpReportMouseOverRowDivide);
				return;
			}
		}
	}

	CRect rcItem;
	CXTPReportRecordItem* pItem = HitTest(point, &rcItem);
	if (pItem != NULL)
		pItem->OnMouseMove(nFlags, point, m_pControl);
}

void CXTPReportRow::FillMetrics(CXTPReportColumn* pColumn, CXTPReportRecordItem* pItem, XTP_REPORTRECORDITEM_METRICS* pMetrics)
{
	m_pControl->m_nLockUpdateCount++;
	XTP_REPORTRECORDITEM_DRAWARGS drawArgs;
	drawArgs.pDC = NULL;
	drawArgs.pColumn = pColumn;
	drawArgs.pControl = m_pControl;
	drawArgs.pRow = this;
	drawArgs.rcItem.SetRectEmpty();
	drawArgs.pItem = pItem;
	drawArgs.nTextAlign = pColumn ? pColumn->GetAlignment() : DT_LEFT;
	GetItemMetrics(&drawArgs, pMetrics);
	m_pControl->m_nLockUpdateCount--;
}

void CXTPReportRow::ShowToolTip(CPoint ptTip, CXTPReportTip* pTipWnd)
{
	CRect rcItem(0, 0, 0, 0);
	CXTPReportColumn* pColumn = NULL;
	CXTPReportRecordItem* pItem = HitTest(ptTip, &rcItem, &pColumn);
	// show tooltip
	if (!(pItem &&
		(pItem->IsPreviewItem() || !pItem->IsPreviewItem() && pColumn)))
	{
		pTipWnd->m_pItem = NULL;
		pTipWnd->m_nRowIndex = -1;
		return;
	}

	if (!m_pControl->IsIconView() &&
		!CXTPDrawHelpers::IsTopParentActive(m_pControl->GetSafeHwnd())
		|| m_pControl->GetActiveItem())
		return;

	if ((pItem != pTipWnd->m_pItem)
		|| (m_nIndex != pTipWnd->m_nRowIndex))
	{
		pTipWnd->m_pItem = pItem;
		pTipWnd->m_nRowIndex = m_nIndex;

		CString strTip = pItem->GetTooltip();
		m_pControl->OnGetToolTipInfo(this, pItem, strTip);

		if (!strTip.IsEmpty()
			|| strTip == _T(" ")
			|| pColumn
			   &&
			  (pColumn->GetAlignment() & DT_WORDBREAK)
			  && !m_pControl->GetPaintManager()->m_bForceShowTooltip)
			return;

		if (pItem->GetMarkupUIElement() != NULL)
			return;

		XTP_REPORTRECORDITEM_METRICS* pMetrics = new XTP_REPORTRECORDITEM_METRICS();
		pMetrics->strText = pItem->GetCaption(pColumn);
		FillMetrics(pColumn, pItem, pMetrics);

		CString strText(pMetrics->strText);

		strText.TrimRight();

		if (strText.IsEmpty())
		{
			pMetrics->InternalRelease();
			return;
		}

		if (m_pControl->GetMarkupContext() && strText[0] == '<' && strText[strText.GetLength() - 1] == '>') // Markup check
		{
			CXTPMarkupUIElement* pElement = XTPMarkupParseText(m_pControl->GetMarkupContext(), strText);

			if (pElement)
			{
				XTPMarkupReleaseElement(pElement);

				pMetrics->InternalRelease();
				return;
			}
		}


		if (!pItem->GetFormula().IsEmpty())
			strText = pItem->GetFormula();

		XTP_REPORTRECORDITEM_ARGS itemArgs;
		itemArgs.pControl = m_pControl;
		itemArgs.pRow = this;

		pItem->GetCaptionRect(&itemArgs, rcItem);

		m_pControl->ClientToScreen(&rcItem);

		if (!pTipWnd->GetSafeHwnd())
		{
			pTipWnd->Create(m_pControl);
		}

		CWindowDC dc(m_pControl);
		CXTPFontDC font(&dc, pMetrics->pFont);
		CRect rcTooltip(rcItem);
		BOOL bActivate = FALSE;
		pTipWnd->SetTooltipText(strText);
		if (pItem->IsPreviewItem())
		{
			CRect rcCalc(rcTooltip.left, 0, rcTooltip.right, 0);
			dc.DrawText(strText, &rcCalc, DT_WORDBREAK | DT_CALCRECT | DT_NOPREFIX);
			bActivate = (rcCalc.Height() / dc.GetTextExtent(_T(" "), 1).cy) > m_pControl->GetPaintManager()->GetMaxPreviewLines();
			rcTooltip.bottom = rcTooltip.top + rcCalc.Height();
			rcTooltip.right++;
		}
		else if (m_pControl->IsIconView())
		{
			CRect rcCalc;
			rcCalc = m_pControl->GetPaintManager()->CalculateMaxTextRect(&dc,
				pMetrics->strText, &rcItem, TRUE, FALSE, DT_NOPREFIX | DT_WORDBREAK);
			bActivate = ((rcCalc.Width() >= rcItem.Width() - 3)
				|| (rcCalc.Height() >= rcItem.Height() - 3));

			rcTooltip.bottom = rcTooltip.top + rcCalc.Height() + 5;
			rcTooltip.right = rcTooltip.left + rcCalc.Width() + 5;
		}
		else
		{
			// Calculate tooltip fine rect
			if (pTipWnd->IsMultilineForce())
			{
				CRect rcCalc;
				rcCalc = m_pControl->GetPaintManager()->CalculateMaxTextRect(&dc,
				pMetrics->strText, &rcItem, TRUE, FALSE, DT_NOPREFIX | DT_WORDBREAK);
				bActivate = ((rcCalc.Width() >= rcItem.Width() - 3)
					|| (rcCalc.Height() >= rcItem.Height() - 3));

				// with small tuning
				rcTooltip.bottom = rcTooltip.top + rcCalc.Height();
				rcTooltip.right = rcTooltip.left + rcCalc.Width() + 15;
			}
			else
			{
				CSize sz = dc.GetTextExtent(strText);
				bActivate = sz.cx > (rcTooltip.Width() - 4);
			}
		}
		if (bActivate ||  m_pControl->GetPaintManager()->m_bForceShowTooltip)
		{
			rcTooltip.InflateRect(1, 1, 0, 0);

			if (!m_pControl->GetPaintManager()->IsGridVisible(FALSE))
				rcTooltip.top++;

			CRect rcHover(m_rcRow);
			m_pControl->ClientToScreen(&rcHover);
			pTipWnd->SetFont(pMetrics->pFont);
			//pTipWnd->SetTooltipText(strText);
			pTipWnd->SetHoverRect(m_pControl->IsIconView() ? rcHover : rcTooltip);
			pTipWnd->SetTooltipRect(rcTooltip);
			pTipWnd->Activate(TRUE, pTipWnd->IsMultilineForce() || pItem->IsPreviewItem() || m_pControl->IsIconView());

			TRACKMOUSEEVENT tme =
			{
				sizeof(TRACKMOUSEEVENT), TME_LEAVE, m_pControl->GetSafeHwnd(), 0
			};
			_TrackMouseEvent (&tme);
		}

		pMetrics->InternalRelease();
	}
}

BOOL CXTPReportRow::HasParent(CXTPReportRow* pRow)
{
	if (m_pParentRow == NULL)
		return FALSE;
	if (pRow == m_pParentRow)
		return TRUE;
	return m_pParentRow->HasParent(pRow);
}

void CXTPReportRow::SetFullExpanded(BOOL bExpanded)
{
	if (bExpanded != m_bExpanded && HasChildren())
	{
		if (bExpanded)
		{
			m_pControl->_DoExpand(this);
		}
		else
		{
			m_pControl->_DoCollapse(this);
		}

		if (m_pRecord)
		{
			m_pRecord->m_bExpanded = bExpanded;
		}

		m_bExpanded = bExpanded;

		m_pControl->_RefreshIndexes();
		m_pControl->SendMessageToParent(this, NULL, NULL, XTP_NM_REPORT_ROWEXPANDED, NULL);

		int n = GetChilds()->GetCount();
		for (int i = n - 1; i >= 0; i--)
		{
			CXTPReportRow* pRow = GetChilds()->GetAt(i);
			if (pRow)
			{
				m_pControl->EndUpdate();
				m_pControl->BeginUpdate();

				pRow->SetExpanded(bExpanded, TRUE);
				m_pControl->_RefreshIndexes();
				m_pControl->SendMessageToParent(pRow, NULL, NULL, XTP_NM_REPORT_ROWEXPANDED, NULL);
			}
		}
	}
	else
	{
		m_bExpanded = bExpanded;
	}
}

void CXTPReportRow::SetExpanded(BOOL bExpanded, BOOL bRecursive)
{
	if (bExpanded != m_bExpanded && HasChildren())
	{
		if (bExpanded)
		{
			m_pControl->_DoExpand(this);
		}
		else
		{
			m_pControl->_DoCollapse(this);
		}

		if (m_pRecord)
		{
			//m_pRecord->m_bExpanded = bExpanded;
			m_pRecord->SetExpanded(bExpanded);
		}

		m_bExpanded = bExpanded;

		m_pControl->_RefreshIndexes();
		m_pControl->SendMessageToParent(this, NULL, NULL, XTP_NM_REPORT_ROWEXPANDED, NULL);

		if (bRecursive)
		{
			int n = GetChilds()->GetCount();
			for (int i = n - 1; i >= 0; i--)
			{
				CXTPReportRow* pRow = GetChilds()->GetAt(i);
				if (pRow)
				{
					m_pControl->EndUpdate();
					m_pControl->BeginUpdate();

					pRow->SetExpanded(bExpanded, bRecursive);
					m_pControl->_RefreshIndexes();
					m_pControl->SendMessageToParent(pRow, NULL, NULL, XTP_NM_REPORT_ROWEXPANDED, NULL);
				}
			}
		}
	}
	else
	{
		m_bExpanded = bExpanded;
	}
}

void CXTPReportRow::LockExpand(BOOL bLock)
{
	m_bLockExpand = bLock;
}

CXTPReportRow* CXTPReportRow::AddChild(CXTPReportRow* pRow)
{
	GetChilds()->Add(pRow);
	pRow->m_pParentRow = this;

	return pRow;
}

void CXTPReportRow::OnContextMenu(CPoint ptClick)
{
	CXTPReportColumn     *pColumn = NULL;
	CXTPReportRecordItem *pItem   = HitTest(ptClick, NULL, &pColumn);

	m_pControl->ClientToScreen(&ptClick);

	// send process notification to the user and wait for the reaction
	m_pControl->SendMessageToParent(this, pItem, pColumn, NM_RCLICK, &ptClick);
}

void CXTPReportRow::GetItemMetrics(
	XTP_REPORTRECORDITEM_DRAWARGS *pDrawArgs,
	XTP_REPORTRECORDITEM_METRICS  *pItemMetrics)
{
	ASSERT(m_pRecord);
	ASSERT(pDrawArgs->pRow  == this);
	ASSERT(pDrawArgs->pItem != NULL);

	if (!m_pRecord || !pDrawArgs->pItem)
		return;

	CXTPReportPaintManager *pPaintManager = pDrawArgs->pControl->GetPaintManager();

	pItemMetrics->pFont            = &pPaintManager->m_fontText;
	pItemMetrics->clrForeground    = pPaintManager->m_clrWindowText;
	pItemMetrics->clrBackground    = XTP_REPORT_COLOR_DEFAULT;
	pItemMetrics->nColumnAlignment = pDrawArgs->nTextAlign;
	pItemMetrics->nItemIcon        = XTP_REPORT_NOICON;

	m_pRecord->GetItemMetrics(pDrawArgs, pItemMetrics);
	pDrawArgs->pItem->GetItemMetrics(pDrawArgs, pItemMetrics);

	m_pControl->GetItemMetrics(pDrawArgs, pItemMetrics);
	pDrawArgs->nTextAlign = pItemMetrics->nColumnAlignment;

	if (IsSelected()
		&& (pDrawArgs->pDC
		&& !pDrawArgs->pDC->IsPrinting())
		&& !pDrawArgs->pControl->IsIconView())
	{
		if (pDrawArgs->pColumn
			&& IsFocused()
			&& m_pControl->m_pFocusedColumn == pDrawArgs->pColumn)
			return;

		if (GetControl()->HasFocus())
		{
			pItemMetrics->clrForeground = pPaintManager->m_clrHighlightText;
			pItemMetrics->clrBackground = pPaintManager->m_clrHighlight;
		}
		else if (!pPaintManager->m_bHideSelection)
		{
			pItemMetrics->clrForeground = pPaintManager->m_clrSelectedRowText;
			pItemMetrics->clrBackground = pPaintManager->m_clrSelectedRow;
		}
	}
}

BOOL CXTPReportRow::HasChildren() const
{
	return m_pChilds && m_pChilds->GetCount() > 0;
}

int CXTPReportRow::GetIndex() const
{
	return m_nIndex;
}

BOOL CXTPReportRow::IsGroupRow() const
{
	return FALSE;
}

BOOL CXTPReportRow::IsExpanded() const
{
	return m_bExpanded;
}

BOOL CXTPReportRow::IsVisible() const
{
	return m_bVisible;
}

void CXTPReportRow::SetVisible(BOOL bVisible)
{
	m_bVisible = bVisible;
}

BOOL CXTPReportRow::IsLastTreeRow() const
{
	if (!m_pParentRow)
		return FALSE;

	CXTPReportRows* pRows = m_pParentRow->GetChilds();

	return pRows->GetCount() > 0 && pRows->GetAt(pRows->GetCount() - 1) == this;
}

void CXTPReportRow::EnsureVisible()
{
	m_pControl->EnsureVisible(this);
}

CXTPReportRow* CXTPReportRow::GetNextSiblingRow() const
{
	if (!m_pParentRows)
		return 0;

	if (m_nChildIndex == -1)
		return 0;

	ASSERT(m_pParentRows->GetAt(m_nChildIndex) == this);

	if (m_nChildIndex < m_pParentRows->GetCount() - 1)
		return m_pParentRows->GetAt(m_nChildIndex + 1);

	return 0;
}


BOOL CXTPReportRow::HasMergedItems() const
{
	BOOL bMerged = FALSE;

	CXTPReportRecord *pRecord = GetRecord();

	if (pRecord)
	{
		for (int iItem=0; iItem<pRecord->GetItemCount(); iItem++)
		{
			CXTPReportRecordItem *pItem = pRecord->GetItem(iItem);

			if (pItem->IsMerged())
			{
				bMerged = TRUE;
			}
		}
	}

	return bMerged;
}

XTPReportRowType CXTPReportRow::GetType() const
{
	XTPReportRowType rowType = xtpRowTypeInvalid;

	if (NULL != m_pSection)
	{
		rowType = m_pSection->GetRowType();
	}

	return rowType;
}


/////////////////////////////////////////////////////////////////////////////
// IAccessible
/////////////////////////////////////////////////////////////////////////////

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

HRESULT CXTPReportRow::GetAccessibleParent(IDispatch* FAR* ppdispParent)
{
	SAFE_MANAGE_STATE(m_pModuleState);

	*ppdispParent = m_pControl->GetIDispatch(TRUE);
	return S_OK;
}

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

	if (m_pRecord)
	{
		*pChildCount = m_pRecord->GetItemCount();

	}

	return S_OK;
}

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

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

	*ppdispChild = NULL;
	return S_FALSE;
}

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

	if (nIndex == CHILDID_SELF)
	{
		CString strCaption = _T("Report Row");
		*pszName = strCaption.AllocSysString();
		return S_OK;
	}

	CXTPReportRecordItem* pItem = m_pRecord->GetItem(nIndex - 1);
	if (!pItem)
		return E_INVALIDARG;

	CString strCaption = pItem->GetCaption(m_pControl->GetColumns()->Find(pItem->GetIndex()));
	*pszName = strCaption.AllocSysString();
	return S_OK;

}

HRESULT CXTPReportRow::GetAccessibleRole(VARIANT varChild, VARIANT* pvarRole)
{
	int nIndex = GetChildIndex(&varChild);

	if (nIndex == CHILDID_SELF)
	{
		pvarRole->vt = VT_I4;
		pvarRole->lVal = ROLE_SYSTEM_ROW;

	}
	else
	{
		pvarRole->vt = VT_I4;
		pvarRole->lVal = ROLE_SYSTEM_CELL;
	}
	return S_OK;
}

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

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

		if (IsSelected())
			pvarState->lVal = STATE_SYSTEM_SELECTED;
	}
	else
	{
		CXTPReportRecordItem* pItem = m_pRecord->GetItem(nChild - 1);
		if (pItem)
		{
			CXTPReportColumn* pColumn = m_pControl->GetColumns()->Find(pItem->GetIndex());
			if (pColumn && !pColumn->IsVisible())
			{
				pvarState->lVal = STATE_SYSTEM_INVISIBLE;
			}
		}
	}

	return S_OK;
}


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

	CRect rc(0, 0, 0, 0);

	int nChild = GetChildIndex(&varChild);

	if (nChild == CHILDID_SELF)
	{
		rc = GetRect();
		m_pControl->ClientToScreen(&rc);
	}
	else
	{
		CXTPReportRecordItem* pItem = m_pRecord->GetItem(nChild - 1);
		if (pItem)
		{
			rc = GetItemRect(pItem);
			m_pControl->ClientToScreen(&rc);
		}

	}

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

	return S_OK;
}

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

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

	CPoint pt(xLeft, yTop);
	m_pControl->ScreenToClient(&pt);

	if (!GetRect().PtInRect(pt))
		return S_FALSE;


	CXTPReportRecordItem* pItem = HitTest(pt);
	if (pItem)
	{
		pvarID->lVal = pItem->GetIndex() + 1;
		return S_OK;
	}

	return S_OK;
}


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