// XTPReportHeader.cpp : implementation of the CXTPReportHeader 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 "Resource.h"
#include "Common/Resource.h"

#include "Common/XTPDrawHelpers.h"
#include "Common/XTPPropExchange.h"
#include "Common/XTPToolTipContext.h"
#include "Common/XTPResourceManager.h"
#include "Common/XTPSystemHelpers.h"
#include "Common/XTPCustomHeap.h"
#include "Common/XTPSmartPtrInternalT.h"
#include "Common/XTPColorManager.h"

#include "XTPReportDefines.h"
#include "XTPReportColumn.h"
#include "XTPReportColumns.h"
#include "XTPReportControl.h"
#include "XTPReportPaintManager.h"
#include "XTPReportSubListControl.h"
#include "XTPReportFilterEditControl.h"
#include "XTPReportDragDrop.h"
#include "XTPReportHeader.h"
#include "XTPReportRow.h"
#include "XTPReportRows.h"
#include "XTPReportSelectedRows.h"
#include "XTPReportRecord.h"
#include "XTPReportRecords.h"


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

/////////////////////////////////////////////////////////////////////////////
// CXTPReportHeader
int CXTPReportHeader::s_nMinAutoScrollStep = 3;
BOOL CXTPReportHeader::s_bShowItemsInGroupsPXDefault = TRUE;
BOOL CXTPReportHeader::s_bSendContextMenuForWholeHeaderArea = FALSE;

CXTPReportHeader::CXTPReportHeader(CXTPReportControl* pControl, CXTPReportColumns* pColumns)
	: m_pColumns(pColumns)
	, m_pControl(pControl)
{
	m_nResizeCaptureRange = 3;
	m_pSubList = NULL;
	m_pFilterEdit = NULL;
	m_dragMode = dragNothing;
	m_nDropIndex = -1;
	m_nVisIndex = -1;
	m_rcHeader.SetRect(0, 0, 0, 0);
	m_pDropWnd = NULL;
	m_pDragWnd = NULL;
	m_pHotTrackingColumn = NULL;
	m_bAllowColumnResize = TRUE;
	m_bAllowColumnRemove = TRUE;
	m_bAllowColumnReorder = TRUE;
	m_bAllowColumnSort = TRUE;
	m_bHideColumnAfterGroupBoxDrop = TRUE;
	m_nForceLayoutAdjustment = 0;

	m_bLastColumnExpand = FALSE;
	m_bLastColumnExpandKeep = FALSE;

	// Release 13.1
	m_pDragColumn = NULL;
	m_pResizeColumn = NULL;
	m_hResizeCursor = XTPResourceManager()->LoadCursor(XTP_IDC_VRESIZE);
	m_hDontDropCursor = XTPResourceManager()->LoadCursor(XTP_IDC_REPORT_NODROP);
	m_bShowItemsInGroups = FALSE;
	m_bDragHeader = m_bDragGroupBox = FALSE;
	m_nIndentLevel = 0;
	m_bAutoColumnSizing = TRUE;
	m_nHeaderWidth = 0;

	m_nMaxColumnWidth = 0;

	EnableAutomation();
}

CXTPReportHeader::~CXTPReportHeader()
{
	DestroyDropWnd();

	if (m_pDragWnd)
		m_pDragWnd->DestroyWindow();
}

CXTPReportPaintManager* CXTPReportHeader::GetPaintManager() const
{
	return m_pControl->GetPaintManager();
}

void CXTPReportHeader::Draw(CDC *pDC, CRect rcHeader, int nLeftOffset)
{
	int HeaderWidth = rcHeader.Width();
	int HeaderHeight = rcHeader.Height();
	int nFreezeCols = GetControl()->GetFreezeColumnsCount();

	rcHeader.OffsetRect(-nLeftOffset, 0);
	m_rcHeader = rcHeader;

	// draw background
	CXTPClientRect rcHeaderClientArea(m_pControl);
	rcHeaderClientArea.top = rcHeader.top;
	rcHeaderClientArea.bottom = rcHeader.bottom;

	if (!m_pControl->IsHeaderVisible() && m_pControl->IsGroupByVisible())
	{
		rcHeaderClientArea.top--; //fix for Office2003 - Office2007 Column Style
	}

	GetPaintManager()->FillHeaderControl(pDC, rcHeaderClientArea);

	int x = rcHeader.left;

	// draw items
	int nColumnsCount = m_pColumns->GetCount(), nColumn;
	for (nColumn = 0; nColumn < nColumnsCount; nColumn++)
	{
		CXTPReportColumn* pColumn = m_pColumns->GetAt(nColumn);

		if (pColumn && pColumn->IsVisible())
		{
			CRect rcItem(x, rcHeader.top, x + pColumn->GetWidth(), rcHeader.bottom);
			pColumn->m_rcColumn = rcItem;

			if (CRect().IntersectRect(rcHeaderClientArea, rcItem))
			{
				GetPaintManager()->DrawColumn(pDC, pColumn, this, rcItem);
			}

			x += rcItem.Width();
			pColumn->m_nMaxItemWidth = 0;
		}
	}

	int xFreeze = 0;

	// draw freeze items
	for (nColumn = 0; nFreezeCols > 0 && nColumn < nColumnsCount; nColumn++)
	{
		CXTPReportColumn* pColumn = m_pColumns->GetAt(nColumn);

		if (pColumn && pColumn->IsVisible())
		{
			CRect rcItem(xFreeze, rcHeader.top, xFreeze + pColumn->GetWidth(), rcHeader.bottom);
			pColumn->m_rcColumn = rcItem;

			if (CRect().IntersectRect(rcHeaderClientArea, rcItem))
			{
				GetPaintManager()->FillHeaderControl(pDC, pColumn->m_rcColumn);
				GetPaintManager()->DrawColumn(pDC, pColumn, this, rcItem);

				if (nFreezeCols == 1 &&
					(GetPaintManager()->GetFreezeColsDividerStyle() & xtpReportFreezeColsDividerHeader))
					GetPaintManager()->DrawFreezeColsDivider(pDC, rcItem, GetControl());
			}

			xFreeze += rcItem.Width();

			nFreezeCols--;
		}
	}

	// draw empty column to fill free header area
	if (x < rcHeaderClientArea.right)
	{
		CRect rcEmptyColumn = rcHeaderClientArea;
		rcEmptyColumn.left = x;
		rcEmptyColumn.right += 10;

		CXTPReportColumn colEmpty(0, _T(""), rcEmptyColumn.Width(), FALSE,
			XTP_REPORT_NOICON, FALSE, TRUE);
		ASSERT(m_pColumns);
		colEmpty.m_pColumns = m_pColumns;

		GetPaintManager()->DrawColumn(pDC, &colEmpty, this, rcEmptyColumn);
	}

	if (GetPaintManager()->IsColumnWidthWYSIWYG() && !pDC->IsPrinting() && GetPaintManager()->IsShowWYSIWYGMarkers())
	{
		int PageWidth = GetPaintManager()->GetPrintPageWidth();

		if (PageWidth > 0 && PageWidth <= HeaderWidth && HeaderWidth > 1)
		{
			for (int st = 1; st <= HeaderWidth / PageWidth; st++)
			{
				CRect rc;
				if (m_bShowItemsInGroups || GetControl()->IsGroupByVisible())
					rc = CRect(PageWidth * st, HeaderHeight, PageWidth * st + 2, 3 * HeaderHeight);//group row mode
				else
					rc = CRect(PageWidth * st, 0, PageWidth * st + 2, HeaderHeight); //non-group row mode
				pDC->Draw3dRect(rc, RGB(255,0,0), RGB(255,0,0));
			}
		}
	}
}

void CXTPReportHeader::CancelMouseMode()
{
	if (m_pControl->GetMouseMode() != xtpReportMouseNothing)
	{
		m_pControl->SetMouseMode(xtpReportMouseNothing);

		m_pDragColumn = NULL;
		m_pResizeColumn = NULL;

		SetHotDivider(-1);

		// Drag wnd cleanup
		if (m_pDragWnd != NULL)
		{
			m_pDragWnd->DestroyWindow();
			m_pDragWnd = NULL;
		}

		if (CWnd::GetCapture() == m_pControl)
			ReleaseCapture();

		SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW));

		m_bDragGroupBox = FALSE;
		m_bDragHeader = FALSE;

		m_pControl->RedrawControl();
	}
}

void CXTPReportHeader::SetAutoColumnSizing(BOOL bAutoColumnSizing)
{
	m_pControl->m_nScrollOffsetH = 0;
	m_bAutoColumnSizing = bAutoColumnSizing;

	if (bAutoColumnSizing && m_pControl->m_hWnd)
		m_pControl->EnableScrollBarCtrl(SB_HORZ, FALSE);

	AdjustColumnsWidth(m_pControl->m_rcHeaderArea.Width());
	m_pControl->RedrawControl();
}

void CXTPReportHeader::OnColumnsChanged(int nReason, CXTPReportColumn* pColumn)
{
	if (m_pControl->IsFullColumnScrolling()
		&& pColumn
		&& m_pControl->GetScrollPos(SB_HORZ) > (pColumn->IsVisible() ? pColumn->GetVisibleIndex() : pColumn->GetOldVisibleIndex()))
	{
		if (nReason & xtpReportColumnShown)
			m_rcHeader.left -= pColumn->GetWidth();
		else if (nReason & xtpReportColumnHidden)
			m_rcHeader.left += pColumn->GetWidth();
		AdjustColumnsWidth(m_pControl->m_rcHeaderArea.Width());
		m_pControl->OnHScroll(SB_THUMBTRACK, m_pControl->GetScrollPos(SB_HORZ), NULL);
	}
	else
	{
		AdjustColumnsWidth(m_pControl->m_rcHeaderArea.Width());
		//if (!GetPaintManager()->IsFixedRowHeight())
		//  m_pControl->AdjustScrollBars();
	}
	m_pControl->UpdateSubList();
	m_pControl->RedrawControl();

	XTP_NM_REPORTCOLUMNORDERCHANGED nmData;
	nmData.pColumn = pColumn;
	nmData.nReason = nReason;

	if (nReason & xtpReportColumnGroupOrderChanged)
		m_pControl->SendNotifyMessage(XTP_NM_REPORT_GROUPORDERCHANGED, (NMHDR*) &nmData);
	m_pControl->SendNotifyMessage(XTP_NM_REPORT_COLUMNORDERCHANGED, (NMHDR*) &nmData);
}

void CXTPReportHeader::TrackColumn(CXTPReportColumn* pColumn, CPoint point)
{
	CXTPClientRect rcControl(m_pControl);
	m_pControl->ClientToScreen(&point);

	CRect rcColumn(pColumn->GetRect());
	rcColumn.bottom = rcControl.bottom;
	m_pControl->ClientToScreen(&rcColumn);

	int nBottomMax = rcColumn.bottom;
	int nLeftMin = 0;

	CDC* pDC = m_pControl->GetDC();
	if (pDC)
	{
		CRect rcVisible;
		UINT uRes = pDC->GetBoundsRect(&rcVisible, 0);

		m_pControl->ReleaseDC(pDC);
		pDC = NULL;

		if ((uRes & DCB_SET) == DCB_SET)
		{
			m_pControl->ClientToScreen(&rcVisible);
			nBottomMax = min(rcVisible.bottom, nBottomMax);
			nLeftMin = rcVisible.left;
		}
	}

	int nMaxAvailWidth = GetMaxAvailWidth(pColumn);

	if (m_nMaxColumnWidth > 0)
	{
		if (pColumn != m_pColumns->GetLastVisibleColumn())
			nMaxAvailWidth = min(nMaxAvailWidth, m_nMaxColumnWidth);
	}

	// uncomment this line in condition if you need set
	// maximum width constraint dependence on column
	// autosize option, then client code must be following:
	//
	// m_wndReportCtrl.GetColumns()->GetAt(0)->SetAutoSize(FALSE); // first need cancel column autosize
	// m_wndReportCtrl.GetColumns()->GetAt(0)->SetMaxWidth(100);   // and then set maximum width

	if (pColumn->GetMaxWidth()/* > 0 && !pColumn->IsAutoSize()*/)
		nMaxAvailWidth = pColumn->GetMaxWidth();

	int nMinWidth = pColumn->GetMinWidth();

	CRect rcAvail(rcColumn.left + nMinWidth, rcColumn.top,
		rcColumn.left + nMaxAvailWidth, nBottomMax);

	CRect rcTracker(rcColumn.right, rcColumn.top, rcColumn.right + 1, nBottomMax);
	CRect rcBound(rcColumn.left - 1, rcColumn.top, rcColumn.left, nBottomMax);

	BOOL bLayoutRTL = m_pControl->GetExStyle() & WS_EX_LAYOUTRTL;

	if (bLayoutRTL)
	{
		rcAvail.SetRect(rcColumn.left - nMaxAvailWidth, rcColumn.top,
			rcColumn.right - nMinWidth, nBottomMax);
	}

	CXTPSplitterTracker tracker(TRUE, m_pControl->m_bDesktopTrackerMode);

	CRect rcBoundX = bLayoutRTL ? rcTracker : rcBound;

	if (!bLayoutRTL && rcBoundX.left > nLeftMin ||
		bLayoutRTL && rcBoundX.left < nLeftMin)
	{
		tracker.SetBoundRect(rcBoundX);
	}

	if (tracker.Track(m_pControl, rcAvail, bLayoutRTL ? rcBound : rcTracker, point, TRUE))
	{
		ResizeColumn(pColumn, rcTracker.left - rcBound.right);
		m_pControl->RedrawControl();
	}
}

void CXTPReportHeader::OnLButtonDblClk(CPoint ptClick)
{
	if (m_pControl->GetMouseMode() == xtpReportMouseOverColumnDivide)
	{
		CPoint ptLeftColumn(ptClick);
		ptLeftColumn.x -= m_nResizeCaptureRange + 1;
		CXTPReportColumn* pColumn = HitTest(ptLeftColumn);
		if (pColumn != NULL)
		{
			BestFit(pColumn);
		}
	}
}

void CXTPReportHeader::OnLButtonDown(CPoint ptClick)
{
	m_pControl->SetCapture();

	if (m_pControl->GetMouseMode() == xtpReportMouseOverColumnDivide)
	{
		CXTPReportColumn* pColumn = MouseOverColumnResizeArea(ptClick);

		if (pColumn != NULL
			&& pColumn->IsResizable()
			&& m_bAllowColumnResize)
		{
			if (!m_bAutoColumnSizing
				|| (!IsLastResizebleColumn(pColumn) && !IsLastVisibleColumn(pColumn)))
			{
				m_pControl->SetMouseMode(xtpReportMouseNothing);
				TrackColumn(pColumn, ptClick);
				return;
			}
		}
	}

	if (m_pControl->GetMouseMode() == xtpReportMouseNothing)
	{
		BOOL bDragHeader = FALSE;
		CXTPReportColumn* pColumn = NULL;
		if (m_rcHeader.PtInRect(ptClick))
		{
			int nColumn = HitTestHeaderColumnIndex(ptClick);
			pColumn = m_pColumns->GetAt(nColumn);

			bDragHeader = TRUE;
		}
		else if (m_rcGroupBy.PtInRect(ptClick))
		{
			int nColumn = FindGroupByColumn(ptClick, TRUE);
			pColumn = m_pColumns->GetGroupsOrder()->GetAt(nColumn);

		}
		if (pColumn != NULL)
		{
			StartDragging(pColumn, bDragHeader);

			m_ptMouse = ptClick;
			m_pControl->SetMouseMode(xtpReportMousePrepareDragColumn);
			m_pControl->RedrawControl();
		}
	}
}

void CXTPReportHeader::AdjustColumnsWidth(int nTotalWidth, int nFirstIndex)
{
	if (nTotalWidth == 0)
		return;

	if (m_bAutoColumnSizing)
	{
		int nColumnsWidth = 0;
		int nTotalColumnsWidth = 0;
		CXTPReportColumn* pLastAutoColumn = NULL;
		int nColumn;

		for (nColumn = nFirstIndex; nColumn < m_pColumns->GetCount(); nColumn++)
		{
			CXTPReportColumn* pColumn = m_pColumns->GetAt(nColumn);
			if (!pColumn->IsVisible())
				continue;

			if (pColumn->m_bAutoSize)
			{
				pLastAutoColumn = pColumn;
				nColumnsWidth += pColumn->m_nColumnAutoWidth;
			}
			else
			{
				nTotalWidth -= pColumn->GetWidth();
				nTotalColumnsWidth += pColumn->GetWidth();
			}
		}

		if (pLastAutoColumn && nTotalWidth > 0)
		{
			m_nForceLayoutAdjustment = 0;

			for (nColumn = nFirstIndex; nColumn < m_pColumns->GetCount(); nColumn++)
			{
				CXTPReportColumn* pColumn = m_pColumns->GetAt(nColumn);
				if (!pColumn->IsVisible())
					continue;

				if (pColumn->m_bAutoSize)
				{
					if (pColumn == pLastAutoColumn)
					{
						pColumn->m_nColumnStaticWidth = max(nTotalWidth, pColumn->GetMinWidth());
						if (pColumn->m_nColumnStaticWidth != pColumn->m_nColumnAutoWidth)
						{
							XTP_NM_REPORTCOLUMNRESIZE nmData;
							nmData.pColumn = pColumn;
							nmData.nPrevWidth = pColumn->m_nColumnAutoWidth;
							nmData.nNewWidth = pColumn->m_nColumnStaticWidth;
							m_pControl->SendNotifyMessage(XTP_NM_REPORT_COLUMNWIDTHCHANGED, (NMHDR*)&nmData);
						}
					}
					else
					{
						nColumnsWidth = max(1, nColumnsWidth);

						pColumn->m_nColumnStaticWidth =
							max(int(pColumn->m_nColumnAutoWidth * nTotalWidth / nColumnsWidth), pColumn->GetMinWidth());
						if (pColumn->m_nColumnStaticWidth != pColumn->m_nColumnAutoWidth)
						{
							XTP_NM_REPORTCOLUMNRESIZE nmData;
							nmData.pColumn = pColumn;
							nmData.nPrevWidth = pColumn->m_nColumnAutoWidth;
							nmData.nNewWidth = pColumn->m_nColumnStaticWidth;
							m_pControl->SendNotifyMessage(XTP_NM_REPORT_COLUMNWIDTHCHANGED, (NMHDR*)&nmData);
						}

						nTotalWidth -= pColumn->m_nColumnStaticWidth;
						nColumnsWidth -= pColumn->m_nColumnAutoWidth;
					}
				}
			}
		}
		m_nHeaderWidth = m_pControl->m_rcHeaderArea.Width();

		CXTPReportColumn* p_Column = NULL;
		int TotalMinWidth = 0;
		for (int nCol = nFirstIndex; nCol < m_pColumns->GetCount(); nCol++)
		{
			CXTPReportColumn* pColumn = m_pColumns->GetAt(nCol);
			if (pColumn)
			{
				if (!pColumn->IsVisible())
					continue;
				p_Column = pColumn;

				if (!m_pControl->m_bIconColumnIndexNotValid)
					TotalMinWidth += pColumn->GetWidth();
			}
		}
		m_nForceLayoutAdjustment = max(TotalMinWidth - m_nHeaderWidth, 0);

		if (TotalMinWidth > m_nHeaderWidth)
			m_nHeaderWidth = TotalMinWidth;
	}
	else
	{
		m_nHeaderWidth = 0;

		for (int nColumn = 0; nColumn < m_pColumns->GetCount(); nColumn++)
		{
			CXTPReportColumn* pColumn = m_pColumns->GetAt(nColumn);

			if (pColumn && pColumn->IsVisible())
				m_nHeaderWidth += pColumn->GetWidth();
		}

		if (m_nHeaderWidth == 0)
			m_nHeaderWidth = nTotalWidth;
	}
	//m_pControl->AdjustScrollBars();
}

void CXTPReportHeader::ResizeColumn(CXTPReportColumn* pColumnResize, int nWidth)
{
	int nResizeIndex = 0;
	int nColumn;
	int nTotalWidth = 0;
	m_pResizeColumn = pColumnResize;

	ASSERT(pColumnResize->IsResizable());

	for (nColumn = 0; nColumn < m_pColumns->GetCount(); nColumn++)
	{
		CXTPReportColumn* pColumn = m_pColumns->GetAt(nColumn);
		if (!pColumn->IsVisible())
			continue;

		if (nResizeIndex > 0)
			nTotalWidth += pColumn->m_nColumnStaticWidth;

		if (pColumnResize == pColumn)
		{
			int nDelta = pColumn->GetWidth() - nWidth;

			nResizeIndex = nColumn + 1;
			nTotalWidth = - nWidth + pColumn->GetWidth();
			pColumn->m_nColumnStaticWidth = nWidth;

			if (m_bAutoColumnSizing)
			{
				// if next column is "last resizeble" but not "auto size" column - resize it too
				// if special flag m_pControl->GetPaintManager()->IsLastColumnWidthWYSIWYG() not used.
				CXTPReportColumn* pColNext = CXTPReportHeader::GetNextVisibleColumn(nColumn, 1);

				if (pColNext && !pColNext->IsAutoSize()
					&& !m_pControl->GetPaintManager()->IsLastColumnWidthWYSIWYG()
					&& pColNext->IsResizable() && IsLastResizebleColumn(pColNext))
				{
					pColNext->m_nColumnStaticWidth += nDelta;
					if (pColNext->m_nColumnStaticWidth != pColNext->m_nColumnAutoWidth)
					{
						XTP_NM_REPORTCOLUMNRESIZE nmData;
						nmData.pColumn = pColNext;
						nmData.nPrevWidth = pColNext->m_nColumnAutoWidth;
						nmData.nNewWidth = pColNext->m_nColumnStaticWidth;
						m_pControl->SendNotifyMessage(XTP_NM_REPORT_COLUMNWIDTHCHANGED, (NMHDR*)&nmData);
					}
				}
			}
		}
		if (pColumn->m_nColumnStaticWidth != pColumn->m_nColumnAutoWidth)
		{
			XTP_NM_REPORTCOLUMNRESIZE nmData;
			nmData.pColumn = pColumn;
			nmData.nPrevWidth = pColumn->m_nColumnAutoWidth;
			nmData.nNewWidth = pColumn->m_nColumnStaticWidth;
			m_pControl->SendNotifyMessage(XTP_NM_REPORT_COLUMNWIDTHCHANGED, (NMHDR*)&nmData);
		}
		pColumn->m_nColumnAutoWidth = pColumn->m_nColumnStaticWidth;
	}
	AdjustColumnsWidth(nTotalWidth, nResizeIndex);

	for (nColumn = 0; nColumn < m_pColumns->GetCount(); nColumn++)
	{
		CXTPReportColumn* pColumn = m_pColumns->GetAt(nColumn);
		pColumn->m_nColumnAutoWidth = pColumn->m_nColumnStaticWidth;
	}
	AdjustColumnsWidth(m_rcHeader.Width());

	m_pControl->AdjustScrollBars();

	m_pResizeColumn = NULL;
}

void CXTPReportHeader::OnLButtonUp(UINT nFlags, CPoint ptClick)
{
	XTPReportMouseMode mouseMode = m_pControl->GetMouseMode();
	m_pControl->SetMouseMode(xtpReportMouseNothing);

	ReleaseCapture();

	if (mouseMode == xtpReportMouseDraggingColumn)
	{
		// End dragging column
		CXTPReportColumn* pDragColumn = m_pDragColumn;
		ASSERT(pDragColumn);
		if (!pDragColumn)
			return;
		BOOL bGroupOrderChanged = FALSE;

		// Drop column - change position
		if (m_dragMode == dragOutTarget || m_dragMode == dragFieldChooser)
		{
			if (m_bAllowColumnRemove && pDragColumn->m_bAllowRemove)
			{
				if (m_bDragHeader)
				{
					XTP_TRACE(_T("Removing column\n"));
					pDragColumn->SetVisible(FALSE);
				}
				else if (m_bDragGroupBox)
				{
					m_pColumns->GetGroupsOrder()->Remove(pDragColumn);
					bGroupOrderChanged = TRUE;
					m_pControl->Populate();
				}
			}
		}
		else if (((m_dragMode & dragInTarget) != 0)
			&& m_nDropIndex >= 0)
		{
			XTP_TRACE(_T("Changing column position to %d\n"), m_nDropIndex);

			CXTPClientRect rcClient(m_pControl);
			CRect rcHeaderFull = m_rcHeader;
			rcHeaderFull.right = rcClient.right;
			if (rcHeaderFull.PtInRect(ptClick))
			{
				int nDragIndex = m_pColumns->IndexOf(pDragColumn);

				pDragColumn->SetVisible();
				m_pColumns->ChangeColumnOrder(m_nDropIndex, nDragIndex);

				if (m_bDragGroupBox && ((nFlags & MK_CONTROL) == 0))
				{
					m_pColumns->GetGroupsOrder()->Remove(pDragColumn);
					bGroupOrderChanged = TRUE;
					m_pControl->Populate();
				}
			}
			else if (m_rcGroupBy.PtInRect(ptClick))
			{
				// set grouping by
				BOOL bFCS = m_pControl->IsFullColumnScrolling(); //<<>>
				//if (pDragColumn->GetIndex() > 0)
				if (pDragColumn->GetVisibleIndex() > 0)
					m_pControl->SetFullColumnScrolling(FALSE);

				m_pColumns->GetGroupsOrder()->InsertAt(m_nDropIndex, pDragColumn);
				bGroupOrderChanged = TRUE;

				if ((m_bHideColumnAfterGroupBoxDrop && m_bDragHeader && ((nFlags & MK_CONTROL) == 0)) ||
					(!m_bHideColumnAfterGroupBoxDrop && m_bDragHeader && ((nFlags & MK_CONTROL) == MK_CONTROL)))
				{
					pDragColumn->SetVisible(FALSE);
				}
				m_pControl->Populate();

				m_pControl->SetFullColumnScrolling(bFCS); //<<>>
			}
		}
		int nReason = bGroupOrderChanged ? xtpReportColumnGroupOrderChanged : xtpReportColumnOrderChanged;
		if (m_dragMode == dragFieldChooser)
			nReason |= xtpReportColumnAddedToFieldChooser;
		else if (m_dragMode == dragInHeader && !m_bDragHeader)
			nReason |= xtpReportColumnRemovedFromFieldChooser;
		if (bGroupOrderChanged && m_bDragGroupBox)
			nReason |= xtpReportColumnRemovedFromGroupby;
		else if (bGroupOrderChanged && m_dragMode == dragInGroupBox)
			nReason |= xtpReportColumnAddedToGroupby;
		m_dragMode = dragNothing;
		OnColumnsChanged(nReason, pDragColumn);

		SetHotDivider(-1);
		m_pDragColumn = NULL;

		// Drag wnd cleanup
		if (m_pDragWnd != NULL)
		{
			m_pDragWnd->DestroyWindow();
			m_pDragWnd = NULL;
		}

		m_bDragHeader = m_bDragGroupBox = FALSE;

		return; // Stop message processing
	}

	if (m_pDragColumn && m_pDragColumn == HitTest(ptClick))
	{
		if (m_pDragColumn->IsPlusMinus())
		{
			CXTPReportColumns* pColumns = m_pControl->GetColumns();
			CRect rcPlusMinus = m_pDragColumn->GetRect();
			BOOL bAction = (rcPlusMinus.right - ptClick.x <= 15); //click on Plus-Minus glyph only [13 + 2 pixels]
			if (bAction && pColumns != NULL)
			{
				BOOL bExp = m_pDragColumn->IsExpanded();
				m_pDragColumn->SetExpanded(!bExp);

				m_bDragHeader = FALSE;
				m_dragMode = dragNothing;

				m_pControl->SendMessageToParent(NULL, NULL, m_pDragColumn, XTP_NM_REPORT_PLUSMINUSCLICK, &ptClick);
				return;
			}
		}
		else
		{
			m_pControl->SendMessageToParent(NULL, NULL, m_pDragColumn, NM_CLICK, &ptClick);
		}
	}

	// End holding left mouse button - change sort order for the column
	if (mouseMode == xtpReportMousePrepareDragColumn)
	{
		if (!m_pDragColumn)
			return;

		CXTPReportColumn* pColumn = m_pDragColumn;

		m_pDragColumn = NULL;

		// change sort order
		if (pColumn->IsSortable() && m_bAllowColumnSort)
		{
			BOOL bGroupOrderChanged = FALSE;
			// do not reset sort order if clicked on the column header from the Group By area
			if (m_bDragHeader)
			{
				CXTPReportColumnOrder* pColumnOrder = m_bShowItemsInGroups
					&& pColumn->IsGroupable() ?
						m_pColumns->GetGroupsOrder() :
						m_pColumns->GetSortOrder();

				BOOL bColumnFound = pColumnOrder->IndexOf(pColumn) >= 0;

				if (GetKeyState(VK_SHIFT) >= 0)
				{
					if (m_bShowItemsInGroups && !IsAllowColumnRemove())
					{
						for (int i = 0; i < pColumnOrder->GetCount(); i++)
						{
							pColumnOrder->GetAt(i)->SetVisible();
						}
					}
					pColumnOrder->Clear();
					pColumnOrder->Add(pColumn);
					bGroupOrderChanged = m_bShowItemsInGroups;
				}
				else if (!bColumnFound)
				{
					pColumnOrder->Add(pColumn);
					bGroupOrderChanged = m_bShowItemsInGroups;
				}

				if (bColumnFound)
				{
					pColumn->m_bSortIncreasing = !pColumn->m_bSortIncreasing;
				}
			}
			else
			{
				pColumn->m_bSortIncreasing = !pColumn->m_bSortIncreasing;
			}

			CUIntArray m_arPreSel;
			if (m_pControl->m_bKeepSelectionAfterSort)
			{
				CXTPReportSelectedRows* pSelectedRows = m_pControl->GetSelectedRows();
				if (pSelectedRows)
				{
					int n = pSelectedRows->GetCount();
					for (int i = 0; i < n; i++)
					{
						CXTPReportRow* pRow = pSelectedRows->GetAt(i);
						if (pRow)
						{
							CXTPReportRecord* pRec = pRow->GetRecord();
							if (pRec)
								m_arPreSel.Add(pRec->GetIndex());
						}
					}
				}
			}

			m_pControl->SendNotifyMessage(XTP_NM_REPORT_PRESORTORDERCHANGED);

			if (bGroupOrderChanged)
				m_pControl->Populate();
			else
				m_pControl->ReSortRows();

			if (m_pControl->m_bKeepSelectionAfterSort)
			{
				CXTPReportRows* pRows = m_pControl->GetRows();
				CXTPReportRecords* pRecords = m_pControl->GetRecords();
				if (m_arPreSel.GetSize() > 0)
				{
					for (int i = 0; i < m_arPreSel.GetSize(); i++)
					{
						int j = m_arPreSel.GetAt(i);
						CXTPReportRecord* pRec = pRecords->GetAt(j);
						if (pRec)
						{
							CXTPReportRow* pRow = pRows->FindInTree(pRec);
							if (pRow)
								pRow->SetSelected(TRUE);
						}
					}
				}
			}

			m_pControl->SendNotifyMessage(XTP_NM_REPORT_SORTORDERCHANGED);

			if (bGroupOrderChanged)
			{
				OnColumnsChanged(xtpReportColumnGroupOrderChanged | xtpReportColumnAddedToGroupby | xtpReportColumnRemovedFromGroupby, pColumn);
			}
		}
		else
		{
			m_pControl->RedrawControl();
		}

		m_bDragHeader = m_bDragGroupBox = FALSE;
	}
}

CXTPReportColumn* CXTPReportHeader::HitTest(CPoint ptPoint) const
{
	int nIndex = HitTestHeaderColumnIndex(ptPoint);
	if (nIndex < 0 || m_pColumns == NULL)
		return NULL;

	return m_pColumns->GetAt(nIndex);
}

int CXTPReportHeader::HitTestHeaderColumnIndex(CPoint ptPoint) const
{
	if (!m_pControl->m_rcHeaderArea.PtInRect(ptPoint))
		return -1;

	int x = m_rcHeader.left;

	int nFreezeCols = GetControl()->GetFreezeColumnsCount();

	// enumerate items
	int nColumnCount = m_pColumns->GetCount();
	for (int nColumn = 0; nColumn < nColumnCount; nColumn++)
	{
		CXTPReportColumn* pColumn = m_pColumns->GetAt(nColumn);
		if (pColumn && pColumn->IsVisible())
		{
			x += pColumn->GetWidth();

			if (ptPoint.x < x - (nFreezeCols > 0 ? m_rcHeader.left : 0))
				return nColumn;

			nFreezeCols--;
		}
	}
	return -1;
}

//NEED TO REVIEW THIS CODE
int CXTPReportHeader::GetFullColScrollInfo(
	CXTPReportColumn*& rpPrev,
	CXTPReportColumn*& rpCurr,
	CXTPReportColumn*& rpNext,
	int& rnScrollPos,
	int& rnScrollMax) const
{
	int x = m_rcHeader.left;
	int nLBorderX = 0;

	rnScrollPos = -1;
	rnScrollMax = 0;
	rpPrev = rpCurr = rpNext = NULL;
	BOOL bFind = FALSE;
	int nFreezeCols = GetControl()->GetFreezeColumnsCount();

	// enumerate items
	int nColumnCount = m_pColumns->GetCount();
	for (int nColumn = 0; nColumn < nColumnCount; nColumn++)
	{
		CXTPReportColumn* pColumn = m_pColumns->GetAt(nColumn);
		if (pColumn && pColumn->IsVisible())
		{
			if (nFreezeCols <= 0)
				rnScrollMax++;

			if (!bFind)
			{
				if (nFreezeCols <= 0)
					rnScrollPos++;

				rpPrev = rpCurr;
				rpCurr = pColumn;

				x += pColumn->GetWidth();

				if (nFreezeCols > 0)
				{
					nLBorderX += pColumn->GetWidth();
					nFreezeCols--;
				}
				else
				{
					if (nLBorderX + 1 < x)
					{
						bFind = TRUE;
					}
				}
			}
			else if (!rpNext)
			{
				rpNext = pColumn;
			}
		}
	}
	return nLBorderX;
}

CXTPReportColumn* CXTPReportHeader::MouseOverColumnResizeArea(CPoint ptPoint)
{
	if (ptPoint.y >= m_rcHeader.bottom ||
		ptPoint.y <= m_rcHeader.top)
	{
		return NULL;
	}

	// enumerate columns
	int nVisColCount = m_pColumns->GetVisibleColumnsCount() - (m_bAutoColumnSizing ? 1 : 0);
	int nFreezeCols = min(GetControl()->GetFreezeColumnsCount(), nVisColCount);
	int xMaxFreezeR = 0;

	for (int nColumn = 0; nColumn < nVisColCount; nColumn++)
	{
		BOOL bFreezeCol = nColumn < nFreezeCols;
		int nColIdx = bFreezeCol ? nColumn : nVisColCount + nFreezeCols - 1 - nColumn;

		CXTPReportColumn* pColumn = m_pColumns->GetVisibleAt(nColIdx);
		ASSERT(pColumn->IsVisible());

		if (!pColumn->IsResizable())
			continue;

		int xBorderR = pColumn->GetRect().right;

		if (bFreezeCol)
		{
			xMaxFreezeR = max(xMaxFreezeR, xBorderR);
		}
		else if (xBorderR <= xMaxFreezeR)
		{
			return NULL;
		}

		if (abs(ptPoint.x - xBorderR) <= m_nResizeCaptureRange)
		{
			if (!m_bAutoColumnSizing
				|| (!IsLastResizebleColumn(pColumn) && !IsLastVisibleColumn(pColumn)))
				return pColumn;
		}
	}

	return NULL;
}

int CXTPReportHeader::FindHeaderColumn(CPoint ptPoint) const
{
	// Find column in header

	CXTPClientRect rcClient(m_pControl);
	CRect rcLastEmptyCol = m_rcHeader;
	rcLastEmptyCol.left = m_rcHeader.right;
	rcLastEmptyCol.right = rcClient.right;

	int nCount = m_pColumns->GetCount();
	int nVisColCount = m_pColumns->GetVisibleColumnsCount();

	if (rcLastEmptyCol.PtInRect(ptPoint))
	{
		return nCount;
	}

	if (!m_pControl->m_rcHeaderArea.PtInRect(ptPoint))
		return -1;

	int nFreezeCols = min(GetControl()->GetFreezeColumnsCount(), nVisColCount);
	int xMaxFreezeR = 0;

	for (int nColumn = 0; nColumn < nVisColCount; nColumn++)
	{
		BOOL bFreezeCol = nColumn < nFreezeCols;
		int nColIdx = bFreezeCol ? nColumn : nVisColCount + nFreezeCols - 1 - nColumn;
		BOOL bLastFreezeCol = nColumn + 1 == nFreezeCols;

		CXTPReportColumn* pColumn = m_pColumns->GetVisibleAt(nColIdx);
		ASSERT(pColumn->IsVisible());

		int xBorderR = pColumn->GetRect().right;

		if (bFreezeCol)
		{
			xMaxFreezeR = max(xMaxFreezeR, xBorderR);
		}
		else if (xBorderR <= xMaxFreezeR)
		{
			return -1;
		}

		CRect rcTest0 = pColumn->GetRect();
		if (!bFreezeCol)
		{
			rcTest0.left = max(xMaxFreezeR, rcTest0.left);
		}
		CRect rcTest1 = rcTest0;

		if (rcTest0.left != pColumn->GetRect().left)
		{
			rcTest0.SetRect(0, 0, 0, 0);
		}
		else if (rcTest0.right > rcClient.right)
		{
			rcTest1.SetRect(0, 0, 0, 0);
		}
		else
		{
			rcTest0.right -= rcTest0.Width()/2;
			rcTest1.left += rcTest0.Width()/2;
		}

		if (rcTest0.PtInRect(ptPoint))
		{
			return pColumn->GetIndex();
		}
		if (rcTest1.PtInRect(ptPoint))
		{
			if (bLastFreezeCol && GetControl()->m_nScrollOffsetH != 0)
			{
				return pColumn->GetIndex();
			}
			else
			{
				return pColumn->GetIndex() + 1;
			}
		}
	}
	return 0;
}

int CXTPReportHeader::FindGroupByColumn(CPoint ptPoint, BOOL bExactSearch) const
{
	// Find column in Group By area
	if (m_rcGroupBy.PtInRect(ptPoint))
	{
		CXTPReportPaintManager* pPaintManager = GetPaintManager();
		int nHeaderHeight = pPaintManager->GetHeaderHeight();

		if (bExactSearch)
		{
			int x = m_rcGroupBy.left + 9;
			int y = m_rcGroupBy.top + 7;
			//int nTargetColumn = -1;
			CXTPReportColumn* pColumn = NULL;

			// draw items
			int nColumnsCount = m_pColumns->GetGroupsOrder()->GetCount();
			for (int nColumn = 0; nColumn < nColumnsCount; nColumn++)
			{
				pColumn = m_pColumns->GetGroupsOrder()->GetAt(nColumn);
				if (pColumn)
				{
					CRect rcItem(x, y, x + pColumn->m_rcGroupBy.Width(), y + nHeaderHeight - 3);

					if (rcItem.PtInRect(ptPoint))
						return nColumn;

					// next column
					x = rcItem.right + 5;
					y = rcItem.top + rcItem.Height() / 2;
				}
			}
			return nColumnsCount;
		}
		else
		{
			int xLeft = m_rcGroupBy.left + 9;
			int xRight;
			//int nTargetColumn = -1;
			CXTPReportColumn* pColumn = NULL;

			// draw items
			int nColumnsCount = m_pColumns->GetGroupsOrder()->GetCount();
			for (int nColumn = 0; nColumn < nColumnsCount; nColumn++)
			{
				pColumn = m_pColumns->GetGroupsOrder()->GetAt(nColumn);

				if (pColumn)
				{
					xRight = xLeft + pColumn->m_rcGroupBy.Width();

					if (ptPoint.x <= xRight)
					{
						if (ptPoint.x <= (xLeft + xRight) / 2)
							return nColumn;
						return nColumn + 1;
					}
					// next column
					xLeft = xRight + 5;
				}
			}
			return nColumnsCount;
		}
	}
	return -1;
}

void CXTPReportHeader::DestroyDropWnd()
{
	if (m_pDropWnd != NULL)
	{
		m_pDropWnd->DestroyWindow();
		m_pDropWnd = NULL;
	}
}

int CXTPReportHeader::SetHotDivider(int nIndex, BOOL bHeader)
{
	m_nDropIndex = nIndex;

	if (nIndex < 0)
	{
		DestroyDropWnd();
		return nIndex;
	}

	CXTPReportPaintManager* pPaintManager = GetPaintManager();
	int nHeaderHeight = pPaintManager->GetHeaderHeight();

	// compare hot divider left and right columns with dragging column
	//  and do not show hot divider near dragging column
	CXTPReportColumn* pDraggingColumn = GetDraggingColumn();

	CPoint pt;
	if (bHeader)
	{
		if (pDraggingColumn)
		{
			// find column following hot divider column
			CXTPReportColumn* pLeftColumn = NULL;
			CXTPReportColumn* pRightColumn = NULL;
			int nTotalColumnCount = m_pColumns->GetCount();
			// find left visible column
			for (int nLeftColumnIndex = nIndex - 1; nLeftColumnIndex >= 0; nLeftColumnIndex--)
			{
				if (nLeftColumnIndex < nTotalColumnCount)
				{
					pLeftColumn = m_pColumns->GetAt(nLeftColumnIndex);
					if (pLeftColumn->IsVisible())
						break;

					pLeftColumn = NULL;
				}
			}
			// find right visible column
			for (int nRightColumnIndex = nIndex; nRightColumnIndex < nTotalColumnCount; nRightColumnIndex++)
			{
				pRightColumn = m_pColumns->GetAt(nRightColumnIndex);
				if (pRightColumn->IsVisible())
					break;

				pRightColumn = NULL;
			}
			// compare
			if ((pDraggingColumn == pLeftColumn) || (pDraggingColumn == pRightColumn))
			{
				DestroyDropWnd();
				return -1;
			}
		}

		pt.y = m_rcHeader.CenterPoint().y;
		pt.x = m_rcHeader.left;

		if (nIndex < m_pColumns->GetCount())
		{
			CXTPReportColumn* pColumnL = m_pColumns->GetAt(nIndex);

			if (pColumnL->IsVisible())
			{
				pt.x = pColumnL->GetRect().left;
			}
			else
			{
				if (nIndex > 0 && m_pColumns->GetAt(nIndex-1))
				{
					CXTPReportColumn* pColumnR = m_pColumns->GetAt(nIndex-1);
					ASSERT(pColumnR->IsVisible());
					pt.x = pColumnR->GetRect().right;
				}
			}
		}
		else
		{
			pt.x = m_rcHeader.right;
		}

		if (pt.x < 0)
		{
			DestroyDropWnd();
			return -1;
		}
	}
	else
	{
		int nDraggingIndex = m_pColumns->GetGroupsOrder()->IndexOf(pDraggingColumn);
		if (nDraggingIndex != -1 && (nDraggingIndex == nIndex || nDraggingIndex == nIndex - 1))
		{
			DestroyDropWnd();
			return -1;
		}

		pt.x = m_rcGroupBy.left + 9;
		pt.y = m_rcGroupBy.top + 6 + nHeaderHeight / 2;

		int nColumnCount = m_pColumns->GetGroupsOrder()->GetCount();
		int nLastColumn = min(nColumnCount, nIndex);
		for (int nColumn = 0; nColumn < nLastColumn; nColumn++)
		{
			CXTPReportColumn* pColumn = m_pColumns->GetGroupsOrder()->GetAt(nColumn);
			if (pColumn)
			{
				pt.x += pColumn->m_rcGroupBy.Width();
				if (nColumn < nColumnCount - 1)
				{
					pt.x += 5;
					pt.y += (nHeaderHeight - 3) / 2 ;
				}
			}
		}
	}

	if (m_pDropWnd == NULL)
	{
		m_pDropWnd = new CXTPReportHeaderDropWnd(pPaintManager->m_clrHotDivider);
		if (m_pDropWnd)
			m_pDropWnd->Create(m_pControl, nHeaderHeight);
	}

	if (m_pDropWnd)
	{
		m_pControl->ClientToScreen(&pt);
		m_pDropWnd->SetWindowPos(pt.x, pt.y);
	}

	return nIndex;
}

int CXTPReportHeader::GetMaxAvailWidth(CXTPReportColumn* pColumnCheck)
{
	int nTotalWidth = m_pControl->m_rcHeaderArea.Width();

	if (!m_bAutoColumnSizing)
		return 32000;

	int nWidth = 0;

	for (int i = 0; i < m_pColumns->GetCount(); i++)
	{
		CXTPReportColumn* pColumn = m_pColumns->GetAt(i);
		if (!pColumn->IsVisible())
			continue;

		if (nWidth > 0)
		{
			nWidth -= pColumn->GetMinWidth();
		}

		if (pColumn == pColumnCheck)
		{
			nWidth = nTotalWidth - pColumnCheck->GetRect().left;
		}
	}

	return nWidth;
}

CXTPReportColumn* CXTPReportHeader::GetHotTrackingColumn() const
{
	return m_pDragColumn && IsDragHeader() ? m_pDragColumn : m_pHotTrackingColumn;
}

void CXTPReportHeader::SetHotTrackingColumn(CXTPReportColumn* pColumn)
{
	if (!GetPaintManager()->IsColumHotTrackingEnabled())
		pColumn = NULL;

	if (m_pHotTrackingColumn != pColumn)
	{
		m_pHotTrackingColumn = pColumn;
		m_pControl->RedrawControl();

		if (!CWnd::GetCapture())
		{
			TRACKMOUSEEVENT tme = {sizeof(TRACKMOUSEEVENT), TME_LEAVE, m_pControl->GetSafeHwnd(), 0};
			_TrackMouseEvent (&tme);
		}
	}
}

void CXTPReportHeader::OnMouseMove(UINT /*nFlags*/, CPoint point)
{
	XTPReportMouseMode mouseMode = m_pControl->GetMouseMode();

	CXTPReportColumn* pCol = HitTest(point);

	SetHotTrackingColumn(pCol);

	if (pCol != NULL && pCol->IsPlusMinus())
	{
		CRect rcPlusMinus = pCol->GetRect();
		if (rcPlusMinus.right - point.x <= 20 && rcPlusMinus.right - point.x > 15)
		{
			CXTPToolTipContext* pTipContext = m_pControl->GetToolTipContext();
			if (pTipContext)
				pTipContext->CancelToolTips();
		}
	}

	if (m_bAllowColumnResize
		&& (mouseMode == xtpReportMouseNothing || mouseMode == xtpReportMouseOverColumnDivide)
		&& MouseOverColumnResizeArea(point))
	{
		if (mouseMode == xtpReportMouseNothing)
			SetCursor(m_hResizeCursor);

		m_pControl->SetMouseMode(xtpReportMouseOverColumnDivide);
		return;
	}

	if (mouseMode == xtpReportMousePrepareDragColumn)
	{
		CXTPReportColumn* pColumn = GetDraggingColumn();
		if (!pColumn)
			return;

		if (!m_bAllowColumnReorder
			&& (!m_bAllowColumnRemove || !pColumn->m_bAllowRemove)
			&& !m_pControl->IsGroupByVisible())
			return;

		if (abs(point.x - m_ptMouse.x) + abs(point.y - m_ptMouse.y) < 4)
			return;

		if (!(pColumn->IsAllowDragging()
			&& pColumn->GetIndex() >= m_pControl->GetDisableReorderColumnsCount()))
		{
			m_pDragColumn = NULL;
			m_pControl->SetMouseMode(xtpReportMouseNothing);
			m_pControl->RedrawControl();
		}
		else
		{
			m_pControl->SetMouseMode(xtpReportMouseDraggingColumn);
			m_dragMode = m_bDragHeader ? dragInHeader : dragInGroupBox;
			// set dragging cursor
			ASSERT(m_pDragWnd == NULL);
			m_pDragWnd = new CXTPReportHeaderDragWnd();

			CRect rcItem(pColumn->GetRect());
			if (!m_bDragHeader)
			{
				// set sizes as drawing external
				rcItem.right = rcItem.left + pColumn->m_rcGroupBy.Width();
			}
			m_pControl->ClientToScreen(&rcItem);
			m_pDragWnd->Create(rcItem, this, GetPaintManager(), pColumn);
			m_nVisIndex = pColumn->GetVisibleIndex();
			// Release 13.1
//TRACE(_T("OnMouseMove m_nVisIndex=%d\n"), m_nVisIndex);
			return;
		}
	}

	if (mouseMode == xtpReportMouseDraggingColumn)
	{
		if (!m_pDragColumn)
			return;

		CPoint ptScreen = point;
		m_pControl->ClientToScreen(&ptScreen);

		if (m_pDragWnd)
		{
			CRect rcWnd;
			m_pDragWnd->GetWindowRect(&rcWnd);

			CPoint pt(ptScreen);
			pt.Offset(-(rcWnd.Width() >> 1), -(rcWnd.Height() >> 1));

			m_pDragWnd->SetWindowPos(&CWnd::wndTop,
				pt.x, pt.y, 0, 0,
				SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOACTIVATE);
		}

		if (m_pSubList
			&& m_pSubList->GetSafeHwnd()
			&& m_pSubList->IsWindowVisible()
			&& CXTPWindowRect(m_pSubList).PtInRect(ptScreen))
		{
			if (m_dragMode == dragFieldChooser)
				return;

			SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW));

			SetHotDivider(-1);
			m_dragMode = dragFieldChooser;
			return;
		}

		CXTPClientRect rcClient(m_pControl);
		CRect rcDropTarget(m_rcHeader);
		if (m_pDragColumn->IsGroupable())
			rcDropTarget.UnionRect(m_rcHeader, m_rcGroupBy);
		rcDropTarget.right = rcClient.right;

		if (rcDropTarget.PtInRect(point))
		{
			CRect rcHeaderFull = m_rcHeader;
			rcHeaderFull.right = rcClient.right;
			BOOL bHeaderPoint = rcHeaderFull.PtInRect(point);
			// change dropping place
			int nDropPos = bHeaderPoint ? FindHeaderColumn(point) : FindGroupByColumn(point);

			if (bHeaderPoint
				&& !(m_bAllowColumnReorder && nDropPos > (m_pControl->GetDisableReorderColumnsCount() - 1))/* && m_bDragHeader*/)
				nDropPos = -1;

			XTP_TRACE(_T("nDropPos = %i, m_nDropIndex = %i\n"), nDropPos, m_nDropIndex);

			ReportDraggingMode dragMode = bHeaderPoint ? dragInHeader : dragInGroupBox;

			// dropping will change order
			if ((m_dragMode & dragInTarget) == 0)
			{
				// Change drag mode
				XTP_TRACE(_T("Switch IN point = %d %d\t"), point.x, point.y);

				SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW));
			}

			// redraw control with new arrows if dropping place changed
			//if (nDropPos != m_nDropIndex || m_dragMode != dragMode)
			//{
				SetHotDivider(nDropPos, bHeaderPoint);
			//}
			m_dragMode = dragMode;
		}
		else
		{
			// dropping will remove the column
			if (m_dragMode != dragOutTarget)
			{
				// change drag mode
				XTP_TRACE(_T("Switch OUT point = %d %d\t"), point.x, point.y);

				if (m_bAllowColumnRemove && m_pDragColumn->m_bAllowRemove)
					SetCursor(m_hDontDropCursor);

				SetHotDivider(-1);

				m_dragMode = dragOutTarget;
			}
		}
		return;
	}

	if (mouseMode != xtpReportMouseNothing)
	{
		SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW));
		m_pControl->SetMouseMode(xtpReportMouseNothing);

		return;
	}
}

void CXTPReportHeader::StartDragging(CXTPReportColumn* pColumn, BOOL bHeader)
{
	ASSERT(pColumn);
	m_pDragColumn = pColumn;
	m_bDragHeader = bHeader;
	m_bDragGroupBox = !bHeader;
	m_nDropIndex = -1;
}

CXTPReportColumn* CXTPReportHeader::GetDraggingColumn() const
{
	return m_pDragColumn;
}

CXTPReportColumn* CXTPReportHeader::GetResizingColumn() const
{
	return m_pResizeColumn;
}

BOOL CXTPReportHeader::SetSubListCtrl(CXTPReportSubListControl* pSubList)
{
	m_pSubList = pSubList;

	if (!pSubList)
		return FALSE;

	m_pSubList->SetReportCtrl(m_pControl);

	return TRUE;
}

BOOL CXTPReportHeader::SetFilterEditCtrl(CXTPReportFilterEditControl* pFilterEdit)
{
	if (!pFilterEdit)
		return FALSE;

	m_pFilterEdit = pFilterEdit;
	m_pFilterEdit->SetReportCtrl(m_pControl);

	return TRUE;
}

void CXTPReportHeader::OnContextMenu(CPoint ptClick)
{
	CPoint ptClient(ptClick);
	if (!m_pControl || !m_pControl->m_rcHeaderArea.PtInRect(ptClient))
		return;

	if (m_pControl->GetMouseMode() == xtpReportMouseNothing)
	{
		// mark the column as clicked at by the dragging
		CXTPReportColumn* pColumn = HitTest(ptClient);
		if (pColumn || s_bSendContextMenuForWholeHeaderArea)
		{
			m_pControl->ClientToScreen(&ptClick);
			// send process notification to the user and wait for the reaction
			m_pControl->SendMessageToParent(NULL, NULL, pColumn, XTP_NM_REPORT_HEADER_RCLICK, &ptClick);

			m_pControl->RedrawControl();
		}
	}
}

void CXTPReportHeader::DrawFooter(CDC* pDC, CRect& rcFooter, int nLeftOffset)
{
	UNREFERENCED_PARAMETER(nLeftOffset);
	// draw background
	GetPaintManager()->FillFooter(pDC, rcFooter);

	// draw items
	int nVisColCount = m_pColumns->GetVisibleColumnsCount();
	int nFreezeCols = min(GetControl()->GetFreezeColumnsCount(), nVisColCount);

	for (int nColumn = nVisColCount-1; nColumn >= 0; nColumn--)
	{
		BOOL bFreezeCol = nColumn < nFreezeCols;
		int nColIdx = bFreezeCol ? nFreezeCols - 1 - nColumn : nColumn;

		CXTPReportColumn* pColumn = m_pColumns->GetVisibleAt(nColIdx);
		ASSERT(pColumn && pColumn->IsVisible());
		if (pColumn)
		{
			CRect rcItem = pColumn->GetRect();
			rcItem.top = rcFooter.top;
			rcItem.bottom = rcFooter.bottom;

			if (rcItem.Height() > 0)
			{
				if (bFreezeCol)
				{
					GetPaintManager()->FillFooter(pDC, rcItem);
				}
				GetPaintManager()->DrawColumnFooter(pDC, pColumn, this, rcItem);
			}
		}
	}
}

void CXTPReportHeader::DrawGroupByControl(CDC* pDC, CRect& rcGroupBy)
{
	m_rcGroupBy = rcGroupBy;

	if (rcGroupBy.Height() <= 0)
		return;

	CXTPReportPaintManager* pPM = GetPaintManager();
	if (pPM == NULL) return;

	pPM->FillGroupByControl(pDC, rcGroupBy);

	CXTPFontDC font(pDC, &pPM->m_fontCaption);

	int x = m_rcGroupBy.left + 9;
	int y = m_rcGroupBy.top + 7;

	int nHeaderHeight = pPM->GetHeaderHeight();

	// draw items
	int nColumnsCount = m_pColumns->GetGroupsOrder()->GetCount();
	for (int nColumn = 0; nColumn < nColumnsCount; nColumn++)
	{
		CXTPReportColumn* pColumn = m_pColumns->GetGroupsOrder()->GetAt(nColumn);

		if (pColumn /*&& pColumn->IsVisible()*/)
		{
			CRect rcItem(x, y, x + pColumn->GetCaptionWidth(pDC) + 50, y + nHeaderHeight - 3);
			pColumn->m_rcGroupBy = rcItem;

			// draw background
			pPM->FillHeaderControl(pDC, rcItem);
			// draw column
			pPM->DrawColumn(pDC, pColumn, this, rcItem, TRUE);
			// next column
			x = rcItem.right + 5;
			y = rcItem.top + rcItem.Height() / 2;
			// draw connector
			if (nColumn < nColumnsCount - 1)
				pPM->DrawConnector(pDC, CPoint(rcItem.right - 5, rcItem.bottom), CPoint(x, rcItem.bottom + 4));
		}
	}

// draw default dragging text if no items there
	if (nColumnsCount == 0)
	{
		CRect rcItem(x, y, x, y + nHeaderHeight - 4);
		pPM->DrawNoGroupByText(pDC, rcItem);
	}
}

int CXTPReportHeader::GetGroupByHeight() const
{
	CXTPReportPaintManager* pPaintManager = GetPaintManager();

	int nColumnsCount = m_pColumns->GetGroupsOrder()->GetCount();
	int nHeaderHeight = pPaintManager->GetHeaderHeight();
	int nHeight = 15 + ((nHeaderHeight - 3) / 2) * (nColumnsCount + 1);
	if (nColumnsCount == 0)
		nHeight += (nHeaderHeight - 3) / 2;
	return nHeight;
}

void CXTPReportHeader::BestFit(CXTPReportColumn* pColumn)
{
	if (pColumn->IsResizable() && m_bAllowColumnResize)
	{
		if (!m_bAutoColumnSizing
			|| (!GetControl()->m_bStrictBestFit && !IsLastResizebleColumn(pColumn) && !IsLastVisibleColumn(pColumn))
			)
		{
			int nMaxAvailWidth = GetMaxAvailWidth(pColumn);
			int nMinWidth = pColumn->GetMinWidth();

			int nMaxItemWidth = min(pColumn->GetBestFitWidth(), nMaxAvailWidth);

			if (nMaxItemWidth > nMinWidth)
			{
				ResizeColumn(pColumn, nMaxItemWidth);
				m_pControl->RedrawControl();
			}
		}
	}
}

BOOL CXTPReportHeader::IsLastVisibleColumn(CXTPReportColumn* pColumn) const
{
	int nColumnCount = m_pColumns->GetCount();

	for (int nColumn = m_pColumns->IndexOf(pColumn) + 1; nColumn < nColumnCount; nColumn++)
	{
		CXTPReportColumn* pCol = m_pColumns->GetAt(nColumn);
		if (pCol && pCol->IsVisible())
			return FALSE;
	}

	return TRUE;
}

BOOL CXTPReportHeader::IsLastResizebleColumn(CXTPReportColumn* pColumn) const
{
	int nColumnCount = m_pColumns->GetCount();

	for (int nColumn = m_pColumns->IndexOf(pColumn) + 1; nColumn < nColumnCount; nColumn++)
	{
		CXTPReportColumn* pCol = m_pColumns->GetAt(nColumn);

		if (pCol && pCol->IsVisible() && pCol->IsResizable())
			return FALSE;
	}

	return TRUE;
}

void CXTPReportHeader::ShowItemsInGroups(BOOL bShowInGroups)
{
	if (m_bShowItemsInGroups == bShowInGroups)
		return;

	m_bShowItemsInGroups = bShowInGroups;

	if (m_bShowItemsInGroups)
	{
		int nColumnCount = m_pColumns->GetGroupsOrder()->GetCount();

		for (int nColumn = 0; nColumn < nColumnCount; nColumn++)
		{
			CXTPReportColumn* pCol = m_pColumns->GetGroupsOrder()->GetAt(nColumn);

			if (pCol && !pCol->IsVisible())
				pCol->SetVisible(TRUE);
		}

		m_pColumns->GetGroupsOrder()->Clear();
		if (m_pColumns->GetSortOrder()->GetCount() > 0)
		{
		  m_pColumns->GetGroupsOrder()->Add(m_pColumns->GetSortOrder()->GetAt(0));
		  m_pColumns->GetSortOrder()->Clear();
		}
		m_pControl->Populate();
		OnColumnsChanged(xtpReportColumnGroupOrderChanged, NULL);
	}
}

CXTPReportColumn* CXTPReportHeader::GetNextVisibleColumn(int nIndex, int nDirection)
{
	if (xtpReportColumnDirectionRight == nDirection)
	{
		int nColumnCount = m_pColumns->GetCount();

		for (int nColumn = nIndex + 1; nColumn < nColumnCount; nColumn++)
		{
			CXTPReportColumn* pCol = m_pColumns->GetAt(nColumn);

			if (pCol && pCol->IsVisible())
				return pCol;
		}
	}
	if (xtpReportColumnDirectionLeft == nDirection)
	{
		for (int nColumn = nIndex - 1; nColumn >= 0; nColumn--)
		{
			CXTPReportColumn* pCol = m_pColumns->GetAt(nColumn);

			if (pCol && pCol->IsVisible())
				return pCol;
		}
	}

	return NULL;
}

INT_PTR CXTPReportHeader::OnToolHitTest(CPoint point, TOOLINFO* pTI) const
{
	CXTPReportColumn* pColumn = HitTest(point);
	if (!pColumn)
		return -1;

	INT_PTR nHit = (INT_PTR) pColumn;

	CString strTip = pColumn->GetTooltip();
	CXTPReportPaintManager* pPaintManager = GetPaintManager();

	if (strTip.IsEmpty())
	{
		strTip = pColumn->GetCaption();

		if (pColumn->IsSortable() && m_bAllowColumnSort && pPaintManager != NULL)
		{
			if (m_pControl->m_iIconViewColumn > -1
				&& pColumn->GetColumns()->Find(m_pControl->m_iIconViewColumn) == pColumn)
			{
				if (pPaintManager->m_bRecOrRowNum)// flag for Record (TRUE) or Row (FALSE) number
					strTip = _T("Rec") + strTip;
				else
					strTip = _T("Row") + strTip;
			}
			else
			{
				if (pColumn->HasSortTriangle())
				{
					strTip = pPaintManager->m_strSortBy + strTip;
					if (pColumn->IsSortedDecreasing())
						strTip += pPaintManager->m_strDecreasing;
					else
						strTip += pPaintManager->m_strIncreasing;
				}
			}
		}
		if (pColumn->GetMarkupUIElement())
			return -1;
	}
	BOOL bAction(FALSE);
	if (pColumn->IsPlusMinus())
	{
		CRect rcPlusMinus = pColumn->GetRect();
		bAction = (rcPlusMinus.right - point.x <= 15); //click on Plus-Minus glyph only [13 + 2 pixels]
	}
	if (bAction && pPaintManager != NULL)
	{
		if (pColumn->IsExpanded())
			strTip.Format(_T("%s"), pPaintManager->m_strExpand);
		else
			strTip.Format(_T("%s"), pPaintManager->m_strCollapse);
	}

	if (strTip.GetLength() == 0)
		return -1;

	if (pPaintManager != NULL && pPaintManager->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, pColumn->GetRect(), nHit, strTip);
	return nHit;
}

void CXTPReportHeader::SetLastColumnExpand(BOOL bLastColumnExpand, BOOL bLastColumnExpandKeep)
{
	if (m_pColumns != NULL &&
		m_pColumns->GetLastVisibleColumn() != NULL)
	{
		SetAutoColumnSizing(FALSE);

		m_bLastColumnExpand = bLastColumnExpand;
		m_bLastColumnExpandKeep = bLastColumnExpandKeep;
		if (bLastColumnExpand)
		{
			int nColumnsCount = m_pColumns->GetCount(), nColumn;
			for (nColumn = 0; nColumn < nColumnsCount; nColumn++)
			{
				CXTPReportColumn* pColumn = m_pColumns->GetAt(nColumn);
				if (pColumn && pColumn->IsVisible())
				{
					pColumn->SetAutoSize(FALSE);
				}
			}
			m_pColumns->GetLastVisibleColumn()->SetAutoSize(TRUE);
		}

		SetAutoColumnSizing(TRUE);
	}
}


void CXTPReportHeader::DoPropExchange(CXTPPropExchange* pPX)
{
	PX_Bool(pPX, _T("AllowColumnRemove"),  m_bAllowColumnRemove,  TRUE);
	PX_Bool(pPX, _T("AllowColumnResize"),  m_bAllowColumnResize,  TRUE);
	PX_Bool(pPX, _T("AllowColumnReorder"), m_bAllowColumnReorder, TRUE);
	PX_Bool(pPX, _T("AllowColumnSort"),    m_bAllowColumnSort,    TRUE);
	PX_Bool(pPX, _T("AutoColumnSizing"),   m_bAutoColumnSizing,   TRUE);
	PX_Bool(pPX, _T("ShowItemsInGroups"),  m_bShowItemsInGroups,  s_bShowItemsInGroupsPXDefault);
}

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

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

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

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

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

	*pChildCount = m_pColumns->GetCount();
	return S_OK;
}

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

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

	*ppdispChild = NULL;
	return S_FALSE;
}

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

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

	CXTPReportColumn* pColumn = m_pColumns->GetAt(nIndex - 1);
	if (!pColumn)
		return E_INVALIDARG;

	CString strCaption = pColumn->GetCaption();
	*pszName = strCaption.AllocSysString();
	return S_OK;
}

HRESULT CXTPReportHeader::GetAccessibleRole(VARIANT /*varChild*/, VARIANT* pvarRole)
{
	pvarRole->vt = VT_I4;
	pvarRole->lVal = ROLE_SYSTEM_COLUMNHEADER;
	return S_OK;
}

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

	if (nChild == CHILDID_SELF)
	{
		pvarState->lVal = 0;
	}
	else
	{
		CXTPReportColumn* pColumn = m_pColumns->GetAt(nChild - 1);
		if (pColumn)
		{
			if (!pColumn->IsVisible())
				pvarState->lVal |= STATE_SYSTEM_INVISIBLE;

		}

	}

	return S_OK;
}

HRESULT CXTPReportHeader::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 = m_pControl->m_rcHeaderArea;
		m_pControl->ClientToScreen(&rc);
	}
	else
	{
		CXTPReportColumn* pColumn = m_pColumns->GetAt(nChild - 1);
		if (pColumn)
		{
			rc = pColumn->GetRect();
			m_pControl->ClientToScreen(&rc);
		}
	}

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

	return S_OK;
}

HRESULT CXTPReportHeader::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 (!m_pControl->m_rcHeaderArea.PtInRect(pt))
		return S_FALSE;


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

	return S_OK;
}