You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1446 lines
32 KiB
C++

// XTPReportSection.cpp : implementation of the CXTPReportSection 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/XTPDrawHelpers.h"
#include "Common/XTPSystemHelpers.h"
#include "Common/XTPImageManager.h"
#include "Common/XTPPropExchange.h"
#include "Common/XTPToolTipContext.h"
#include "Common/XTPResourceManager.h"
#include "Common/XTPMarkupRender.h"
#include "Common/XTPCustomHeap.h"
#include "Common/XTPSmartPtrInternalT.h"
#include "Common/XTPColorManager.h"
#include "XTPReportDefines.h"
#include "XTPReportRow.h"
#include "XTPReportGroupRow.h"
#include "XTPReportRecord.h"
#include "XTPReportRecords.h"
#include "XTPReportRecordItem.h"
//#include "ItemTypes/XTPReportRecordItemText.h"
#include "XTPReportControl.h"
#include "XTPReportColumns.h"
#include "XTPReportColumn.h"
#include "XTPReportRows.h"
#include "XTPReportSelectedRows.h"
#include "XTPReportSection.h"
#include "XTPReportSections.h"
#include "XTPReportPaintManager.h"
#include "XTPReportBorder.h"
#include "XTPReportThickness.h"
#define XTP_REPORT_DEBUG() (0)
#define XTP_REPORT_PERFORMANCE() (0)
CXTPReportSection::CXTPReportSection(CXTPReportControl *pControl, XTPReportRowType rowType)
: m_bAllowAccess (TRUE)
, m_bAllowEdit (FALSE)
, m_bAllowGroup (FALSE)
, m_bAllowSort (FALSE)
, m_bAllowFilter (FALSE)
, m_bSelectionEnable(TRUE)
, m_bVisible (FALSE)
, m_rcSection (0,0,0,0)
, m_heightMode (xtpReportSectionHeightModeAuto)
, m_dividerPosition (xtpReportSectionDividerPositionBottom)
, m_dividerStyle (xtpReportFixedRowsDividerNone)
, m_nHeight (0)
// Markup
, m_pMarkupContext(NULL)
, m_pBorder (NULL)
, m_pControl(pControl)
, m_pRecords(NULL)
, m_pRows (NULL)
, m_pTree (NULL)
, m_nScrollIndexV (0)
, m_nScrollOffsetV(0)
, m_nPopulatedRecordsCount(0)
, m_rowType (rowType)
{
m_pBorder = new CXTPReportBorder();
m_pRecords = new CXTPHeapObjectT<CXTPReportRecords, CXTPReportAllocatorDefault>;
m_pRows = new CXTPHeapObjectT<CXTPReportRows, CXTPReportAllocatorDefault>;
m_pTree = new CXTPHeapObjectT<CXTPReportRows, CXTPReportAllocatorDefault>;
m_pRecords->m_pControl = pControl;
m_clrDivider.SetStandardValue(COLOR_BTNFACE);
}
CXTPReportSection::~CXTPReportSection()
{
m_pRecords->SetMarkupContext(NULL);
SAFE_DELETE(m_pBorder);
CMDTARGET_RELEASE(m_pRecords);
CMDTARGET_RELEASE(m_pRows);
CMDTARGET_RELEASE(m_pTree);
}
void CXTPReportSection::SetMarkupContext(CXTPMarkupContext *pMarkupContext)
{
m_pMarkupContext = pMarkupContext;
GetRecords()->SetMarkupContext(pMarkupContext);
}
BOOL CXTPReportSection::IsVirtualMode() const
{
return m_pRecords ? m_pRecords->IsVirtualMode() : FALSE;
}
CXTPReportRecord* CXTPReportSection::AddRecord(CXTPReportRecord *pRecord)
{
ASSERT(NULL != pRecord);
if (NULL != pRecord)
{
m_pRecords->Add(pRecord);
}
return pRecord;
}
void CXTPReportSection::ResetContent()
{
if (NULL != m_pRecords)
{
m_pRecords->RemoveAll();
}
if (NULL != m_pRows)
{
m_pRows->Clear();
}
if (NULL != m_pTree)
{
m_pTree->Clear();
}
}
void CXTPReportSection::Populate(BOOL bUpdate)
{
if (bUpdate)
{
m_pControl->EditItem(NULL);
m_pControl->BeginUpdate();
}
// Save current focused row
CXTPReportRecord *pFocusedRecord = m_pRows->GetFocusedRow()
? m_pRows->GetFocusedRow()->GetRecord()
: NULL;
m_pRows->Clear();
m_pTree->Clear();
if (IsVirtualMode())
{
CXTPReportRow *pRow = m_pControl->CreateRow(); // new CXTPReportRow_Batch();
pRow->InitRow(m_pControl, this, GetRecords()->m_pVirtualRecord);
pRow->SetVisible(TRUE);
m_pRows->SetVirtualMode(pRow, GetRecords()->GetCount());
m_nPopulatedRecordsCount = GetRecords()->GetCount();
}
else
{
BuildTree(m_pTree, NULL, m_pRecords);
m_nPopulatedRecordsCount = m_pTree->GetCount();
SortRows(m_pTree);
int nGroupRowsCount = 0;
CXTPReportColumns *pColumns = m_pControl->m_pColumns;
if (IsAllowGroup() && pColumns->GetGroupsOrder()->GetCount() > 0)
{
CXTPReportRows *pGroupTree = new CXTPHeapObjectT<CXTPReportRows, CXTPReportAllocatorDefault>;
int nReserve = m_pRecords->GetCount() / (pColumns->GetGroupsOrder()->GetCount() + 1);
nReserve = max(nReserve, 300);
pGroupTree->ReserveSize(nReserve);
CXTPReportGroupRow* pLastGroup = NULL;
for (int nPlainRow = 0; nPlainRow < m_pTree->GetCount(); nPlainRow++)
{
CXTPReportRow *pRow = m_pTree->GetAt(nPlainRow);
CXTPReportGroupRow *pGroupToAdd = NULL;
for (int nColumn = 0; nColumn < pColumns->GetGroupsOrder()->GetCount(); nColumn++)
{
CXTPReportColumn* pColumn = pColumns->GetGroupsOrder()->GetAt(nColumn);
CXTPReportRecordItem* pItem = pRow->GetRecord()->GetItem(pColumn);
CString strGroup = pItem ? pItem->GetGroupCaption(pColumn) : _T("");
if (pLastGroup && GetRecords()->Compare(pLastGroup->GetCaption(), strGroup) == 0)
{
pGroupToAdd = pLastGroup;
if (pGroupToAdd->HasChildren())
pLastGroup = (CXTPReportGroupRow*)pGroupToAdd->GetChilds()->GetAt(pGroupToAdd->GetChilds()->GetCount() - 1);
}
else
{
CXTPReportGroupRow* pGroup = m_pControl->CreateGroupRow(); // new CXTPReportGroupRow_Batch();
nGroupRowsCount++;
pGroup->InitRow(m_pControl, this, NULL);
pGroup->SetCaption(strGroup);
if (pGroupToAdd)
pGroupToAdd->AddChild(pGroup);
else
pGroupTree->Add(pGroup);
pGroupToAdd = pGroup;
pLastGroup = NULL;
}
}
if (pGroupToAdd)
pGroupToAdd->AddChild(pRow);
pRow->InternalAddRef();
pLastGroup = (CXTPReportGroupRow*)pGroupTree->GetAt(pGroupTree->GetCount() - 1);
}
m_pTree->InternalRelease();
m_pTree = pGroupTree;
}
GetRows()->ReserveSize(m_pRecords->GetCount() + nGroupRowsCount + 10);
for (int nGroupRow = 0; nGroupRow < m_pTree->GetCount(); nGroupRow++)
{
CXTPReportRow *pRow = m_pTree->GetAt(nGroupRow);
InsertRow(GetRows()->GetCount(), pRow);
pRow->m_nChildIndex = nGroupRow;
ASSERT(pRow->m_pParentRows == m_pTree);
}
for (int nRow=0; nRow<m_pRows->GetCount(); nRow++)
{
CXTPReportRow *pRow = m_pRows->GetAt(nRow);
// Update indexes
if (NULL != pRow)
{
pRow->SetIndex(nRow);
pRow->m_pSection = this;
pRow->SetVisible(TRUE);
}
// Restore focus
if (pFocusedRecord && pRow->GetRecord() == pFocusedRecord)
{
m_pRows->m_nFocusedRow = pRow->GetIndex();
if (IsSelectionEnabled() && m_pControl->IsInitialSelectionEnabled())
{
//m_pSelectedRows->Select(pRow);
CXTPReportRow* pFirst = pRow;
if (pFirst)
{
if (!m_pControl->IsSkipGroupsFocusEnabled() || !pFirst->IsGroupRow())
{
m_pControl->m_pSelectedRows->Select(pFirst);
}
else
{
while (pFirst->IsGroupRow())
{
if (pFirst->HasChildren())
{
pFirst = pFirst->GetChilds()->GetAt(0);
}
}
m_pControl->m_pSelectedRows->Select(pFirst);
}
}
}
}
}
} // VirtualMode()
if (bUpdate)
{
m_pControl->AdjustIndentation();
m_pControl->AdjustLayout();
m_pControl->AdjustScrollBars();
m_pControl->UpdateSubList();
m_pControl->EndUpdate();
}
}
int CXTPReportSection::InsertRow(int nIndex, CXTPReportRow *pRow)
{
m_pRows->InsertAt(nIndex, pRow);
pRow->InternalAddRef();
pRow->m_pSection = this;
pRow->SetVisible(TRUE);
int nRowsInserted = 1;
if (pRow->IsExpanded() && pRow->HasChildren())
{
nRowsInserted += _DoExpand(nIndex, pRow);
}
return nRowsInserted;
}
int CXTPReportSection::_DoExpand(int nIndex, CXTPReportRow *pRow)
{
if (!pRow->HasChildren())
return 0;
int nStartIndex = nIndex;
for (int i = 0; i < pRow->GetChilds()->GetCount(); i++)
{
CXTPReportRow* pRowChild = pRow->GetChilds()->GetAt(i);
if (pRowChild)
{
pRowChild->m_nRowLevel = pRow->m_nRowLevel + 1;
pRowChild->m_nGroupLevel = pRow->m_nGroupLevel + (pRow->IsGroupRow() ? 1 : 0);
nIndex += InsertRow(nIndex + 1, pRowChild);
}
}
return nIndex - nStartIndex;
}
int CXTPReportSection::_DoCollapse(CXTPReportRow *pRow)
{
int nIndex = pRow->GetIndex() + 1;
int nCount = 0;
while (nIndex < m_pRows->GetCount())
{
CXTPReportRow* pRowChild = m_pRows->GetAt(nIndex);
if (pRowChild)
{
if (!pRowChild->HasParent(pRow))
break;
pRowChild->SetVisible(FALSE);
pRowChild->m_nIndex = -1;
m_pRows->RemoveAt(nIndex);
nCount++;
}
}
if (nCount > 0)
{
if (m_pRows->m_nFocusedRow >= nIndex)
m_pRows->m_nFocusedRow = max(nIndex - 1, m_pRows->m_nFocusedRow - nCount);
}
return nCount;
}
int CXTPReportSection::GetPageRowCount(CDC *pDC) const
{
int nCount = 0;
int nHeight = m_rcSection.Height();
for (int nRow=m_nScrollIndexV; nRow<m_pRows->GetCount(); nRow++)
{
CXTPReportRow *pRow = m_pRows->GetAt(nRow);
int nRowHeight = pRow->GetHeight(pDC, m_rcSection.Width());
if (nHeight < nRowHeight)
{
break;
}
else
{
nHeight -= nRowHeight;
nCount++;
}
}
return nCount;
}
CXTPReportBorder* CXTPReportSection::GetBorder() const
{
return m_pBorder;
}
int CXTPReportSection::GetTotalHeight(CDC *pDC) const
{
CSize szBorder = GetBorder()->GetSize();
return szBorder.cy + GetRowsHeight(pDC);
}
int CXTPReportSection::GetRowsHeight(CDC *pDC) const
{
#if XTP_REPORT_PERFORMANCE()
DWORD dwStart = ::GetTickCount();
#endif
int nRowsHeight = 0;
for (int i=0; i<m_pRows->GetCount(); ++i)
{
CXTPReportRow *pRow = m_pRows->GetAt(i);
nRowsHeight += pRow->GetHeight(pDC, m_rcSection.Width());
}
#if XTP_REPORT_PERFORMANCE()
DWORD dwEnd = ::GetTickCount();
TRACE(_T("GetRowsHeight: %d\n"), dwEnd-dwStart);
#endif
return nRowsHeight;
}
BOOL CXTPReportSection::GetRowAt(CDC *pDC, int nWidth, int nOffset, int &nIndex, int &nTop)
{
#if XTP_REPORT_PERFORMANCE()
DWORD dwStart = ::GetTickCount();
#endif
int nHeight = 0;
nTop = 0;
for (nIndex=0; nIndex<m_pRows->GetCount(); ++nIndex)
{
CXTPReportRow *pRow = m_pRows->GetAt(nIndex);
if (pRow->GetRect().Width() == nWidth && FALSE)
{
nHeight = pRow->GetRect().Height();
}
else
{
nHeight = pRow->GetHeight(pDC, nWidth);
}
if (nOffset>= nTop && nOffset<= nTop+nHeight)
{
#if XTP_REPORT_PERFORMANCE()
DWORD dwEnd = ::GetTickCount();
TRACE(_T("GetRowAt: %d\n"), dwEnd-dwStart);
#endif
return TRUE;
}
nTop += nHeight;
}
return FALSE;
}
void CXTPReportSection::Draw(CDC *pDC)
{
#if XTP_REPORT_PERFORMANCE()
DWORD dwStart = ::GetTickCount();
#endif
CRect rcSection(m_rcSection);
// Draw border
if (NULL != m_pBorder)
{
m_pBorder->SetBorderColor(m_pControl->GetPaintManager()->GetGridColor());
m_pBorder->Draw(pDC, rcSection);
// Deduct margin, border and padding from section rectangle
rcSection = CXTPReportBorder::DeflateRect(rcSection, m_pBorder);
}
int nIndex = m_nScrollIndexV;
int nOffset = 0;
if (m_pControl->m_scrollModeV == xtpReportScrollModeSmooth &&
m_pControl->m_pSectionScroll == this)
{
int nTop;
if (GetRowAt(pDC, rcSection.Width(), m_nScrollOffsetV, nIndex, nTop))
{
m_nScrollIndexV = nIndex;
nOffset = m_nScrollOffsetV - nTop;
}
}
m_pControl->DrawRows(pDC, rcSection, rcSection.top-nOffset, m_pRows, nIndex, 0, m_pControl->GetColumns()->GetCount());
if (xtpReportSectionDividerStyleNone != m_dividerStyle)
{
m_pControl->GetPaintManager()->DrawSectionDivider(pDC, m_rcDivider,
m_dividerPosition, m_dividerStyle, m_clrDivider);
}
#if XTP_REPORT_PERFORMANCE()
DWORD dwEnd = ::GetTickCount();
TRACE(_T("CXTPReportSection::Draw: %d\n"), dwEnd-dwStart);
#endif
}
/////////////////////////////////////////////////////////////////////////////
// Internal methods
/////////////////////////////////////////////////////////////////////////////
void CXTPReportSection::BuildTree(
CXTPReportRows *pTree,
CXTPReportRow *pParentRow,
CXTPReportRecords *pRecords)
{
ASSERT(pTree->GetCount() == 0);
pTree->ReserveSize(pRecords->GetCount());
for (int i = 0; i < pRecords->GetCount(); i++)
{
CXTPReportRecord *pRecord = pRecords->GetAt(i);
if (pRecord == NULL)
continue;
BOOL bFilter = FALSE;
if (IsAllowFilter())
{
bFilter = m_pControl->ApplyFilter(pRecord, m_pControl->GetFilterText(), m_pControl->IsPreviewMode());
}
// add record if all conditions are met
if (pRecord->IsLocked() || (pRecord->IsVisible() && !bFilter))
{
CXTPReportRow *pRow = m_pControl->CreateRow(); // new CXTPReportRow_Batch();
pRow->InitRow(m_pControl, this, pRecord);
pRow->m_pParentRow = pParentRow;
pTree->Add(pRow);
if (pRecord->HasChildren())
{
BuildTree(pRow->GetChilds(), pRow, pRecord->GetChilds());
if (pRow->GetChilds() && m_pControl->IsSortRecordChilds())
{
SortRows(pRow->GetChilds());
pRow->GetChilds()->RefreshChildIndices(FALSE);
}
}
}
}
}
void CXTPReportSection::SortTree(CXTPReportRows *pTree)
{
ASSERT(NULL != pTree);
if (NULL == pTree)
{
return;
}
SortRows(pTree);
for (int nRow=0; nRow<pTree->GetCount(); nRow++)
{
CXTPReportRow *pRow = pTree->GetAt(nRow);
ASSERT(NULL != pRow);
if (NULL != pRow)
{
CXTPReportRecord *pRecord = pRow->GetRecord();
BOOL bRecordHasChildren = NULL != pRecord ? pRecord->HasChildren() : FALSE;
BOOL bRecordSortChildren = m_pControl->m_bSortRecordChilds;
if (pRow->HasChildren() && (bRecordHasChildren && bRecordSortChildren || !bRecordHasChildren))
{
SortTree(pRow->GetChilds());
}
}
}
pTree->RefreshChildIndices(FALSE);
}
void CXTPReportSection::RefreshIndexes(BOOL bAdjustLayout, BOOL bReverseOrder)
{
int nRowCount = m_pRows->GetCount();
int nStartIdx = 0, nEndIdx = nRowCount, nStep = 1;
if (bReverseOrder)
{
nStartIdx = nRowCount - 1;
nEndIdx = nStep = -1;
}
for (int nIndex = nStartIdx; nIndex != nEndIdx; nIndex += nStep)
{
CXTPReportRow *pRow = m_pRows->GetAt(nIndex);
if (NULL != pRow)
{
BOOL bSelected = GetSelectedRows()->Contains(pRow);
if (bSelected)
{
GetSelectedRows()->Remove(pRow);
}
pRow->m_nIndex = nIndex;
ASSERT(pRow->IsVisible());
if (bSelected)
{
GetSelectedRows()->Add(pRow);
}
}
}
if (bAdjustLayout)
{
m_pControl->AdjustScrollBars();
}
}
void CXTPReportSection::ReSortRows()
{
if (IsVirtualMode())
{
Populate(TRUE);
return;
}
// Are there any selected rows?
BOOL bRowsSelected = m_pControl->m_bInitialSelectionEnable;
if (GetSelectedRows()!= NULL)
{
if (GetSelectedRows()->GetCount() > 0)
bRowsSelected = TRUE;
}
CXTPReportRecord *pFocusedRecord = NULL;
if (NULL != GetFocusedRow())
{
pFocusedRecord = GetFocusedRow()->GetRecord();
}
SortTree(m_pTree);
m_pRows->Clear();
for (int nGroupRow=0; nGroupRow<m_pTree->GetCount(); nGroupRow++)
{
CXTPReportRow *pRow = m_pTree->GetAt(nGroupRow);
InsertRow(m_pRows->GetCount(), pRow);
pRow->m_nChildIndex = nGroupRow;
ASSERT(pRow->m_pParentRows == m_pTree);
}
m_pRows->m_nFocusedRow = -1;
// Update indexes on virtual rows
int nRowCount = m_pRows->GetCount();
for (int nRow=0; nRow<nRowCount; nRow++)
{
CXTPReportRow *pRow = m_pRows->GetAt(nRow);
if (NULL != pRow)
{
pRow->SetIndex(nRow);
pRow->SetVisible(TRUE);
CXTPReportRecord *pRecord = pRow->GetRecord();
if (NULL != pRecord)
{
if (pFocusedRecord && pRecord == pFocusedRecord)
{
m_pRows->m_nFocusedRow = pRow->GetIndex();
if (IsSelectionEnabled() && bRowsSelected)
{
GetSelectedRows()->Select(pRow);
}
}
BOOL b1 = pRow->IsSelected();
BOOL b2 = pRecord->IsSelected();
if (b1 != b2)
pRow->SetSelected(b2);
}
}
}
}
void CXTPReportSection::SortRows(CXTPReportRows *pRows)
{
CXTPReportColumns *pColumns = m_pControl->m_pColumns;
XTPReportRowsCompareFunc pRowsCompareFunc = m_pControl->m_pRowsCompareFunc;
if (0 == pRows->GetCount())
return;
if (0 == pColumns->GetSortOrder()->GetCount() &&
0 == pColumns->GetGroupsOrder()->GetCount())
{
return;
}
CXTPReportRow *pRow;
CXTPReportRecord *pRecord;
if (NULL != pRowsCompareFunc)
{
pRows->SortEx(pRowsCompareFunc);
}
else
{
pRows->Sort();
}
m_UaSorted.RemoveAll();
for (int nRow=0; nRow<pRows->GetCount(); nRow++)
{
pRow = pRows->GetAt(nRow);
if (pRow)
{
pRecord = pRow->GetRecord();
if (NULL != pRecord)
{
m_UaSorted.Add(pRecord->GetIndex());
}
}
}
}
BOOL CXTPReportSection::EnsureVisible(CDC *pDC, CXTPReportRow *pRow)
{
// None of the parameters may be null
ASSERT(NULL != pDC);
ASSERT(NULL != pRow);
if (NULL == pDC || NULL == pRow)
{
return FALSE;
}
// Row must be visible
ASSERT(pRow->IsVisible());
if (!pRow->IsVisible())
{
return FALSE;
}
// Row must be in this section
ASSERT(this == pRow->GetSection());
BOOL bResult = FALSE;
if (m_pControl->IsIconView())
{
bResult = EnsureVisibleIcon(pDC, pRow);
}
else
{
switch(m_pControl->GetScrollMode(xtpReportOrientationVertical))
{
case xtpReportScrollModeSmooth:
bResult = EnsureVisibleSmooth(pDC, pRow);
break;
case xtpReportScrollModeBlockCount:
case xtpReportScrollModeBlockSize:
bResult = EnsureVisibleBlock(pDC, pRow);
break;
}
}
return bResult;
}
int CXTPReportSection::GetRowOffset(CDC *pDC, int nWidth, int nIndex)
{
// None of the parameters may be null
ASSERT(NULL != pDC);
if (NULL == pDC)
{
return -1;
}
int nOffset = 0;
for (int nRow=0; nRow<nIndex; nRow++)
{
CXTPReportRow *pRow = GetRows()->GetAt(nRow);
nOffset += pRow->GetHeight(pDC, nWidth);
}
return nOffset;
}
BOOL CXTPReportSection::EnsureVisibleSmooth(CDC *pDC, CXTPReportRow *pRow)
{
// None of the parameters may be null
ASSERT(NULL != pDC);
ASSERT(NULL != pRow);
if (NULL == pDC || NULL == pRow)
{
return FALSE;
}
int nOffset = GetRowOffset(pDC, GetRect().Width(), pRow->GetIndex());
// Scroll up
if (nOffset < m_nScrollOffsetV)
{
m_pControl->SetScrollOffsetV(nOffset);
}
// Scroll down
int nRowHeight = pRow->GetHeight(pDC, GetRect().Width());
nOffset += nRowHeight;
if (nOffset > m_nScrollOffsetV + GetRect().Height())
{
m_pControl->SetScrollOffsetV(nOffset - GetRect().Height());
}
return TRUE;
}
int CXTPReportSection::EnsureVisibleCalc(CDC *pDC, CXTPReportRow *pRow)
{
// None of the parameters may be null
ASSERT(NULL != pDC);
ASSERT(NULL != pRow);
if (NULL == pDC || NULL == pRow)
{
return -1;
}
int nIndexRow = pRow->GetIndex();
int nIndexTop = m_nScrollIndexV;
int nCount = GetRows()->GetCount();
int nReturn = -1;
// Scroll up
if (nIndexRow <= nIndexTop)
{
nReturn = nIndexRow;
}
// Scroll down
if (nIndexRow > nIndexTop)
{
LONG top = m_rcSection.top;
int nIndex = nIndexTop;
// Figure out if row is fully visible
while ((top < m_rcSection.bottom) && (nIndex <= nIndexRow) && (nIndex < nCount))
{
CXTPReportRow *pRow = GetRows()->GetAt(nIndex);
ASSERT(NULL != pRow);
int nRowHeight = pRow->GetHeight(pDC, m_rcSection.Width());
nIndex++; // Move to next row
top += nRowHeight;
}
// Row not fully visible
if (top >= m_rcSection.bottom)
{
int nHeight = m_rcSection.Height();
for (nIndex=nIndexRow; nIndex>=0; nIndex--)
{
CXTPReportRow *pRow = m_pRows->GetAt(nIndex);
ASSERT(NULL != pRow);
int nRowHeight = pRow->GetHeight(pDC, m_rcSection.Width());
if (nHeight - nRowHeight < 0)
{
break;
}
nHeight -= nRowHeight;
}
nReturn = nIndex + 1;
}
}
return nReturn;
}
BOOL CXTPReportSection::EnsureVisibleBlock(CDC *pDC, CXTPReportRow *pRow)
{
int nIndex = EnsureVisibleCalc(pDC, pRow);
if (-1 == nIndex)
{
return FALSE;
}
SetScrollIndexV(nIndex);
return TRUE;
}
BOOL CXTPReportSection::EnsureVisibleIcon(CDC *pDC, CXTPReportRow *pRow)
{
int nIndex = EnsureVisibleCalc(pDC, pRow);
if (-1 == nIndex)
{
return FALSE;
}
int nIndexRow = pRow->GetIndex();
if (nIndex != nIndexRow)
{
nIndex += m_pControl->GetRowsPerLine();
}
m_pControl->SetTopRow(nIndex);
return TRUE;
}
int CXTPReportSection::GetScrollOffsetV() const
{
return m_nScrollOffsetV;
}
void CXTPReportSection::SetScrollOffsetV(int nScrollOffsetV)
{
m_nScrollOffsetV = nScrollOffsetV;
m_nScrollIndexV = -1; // Recalculate
}
XTPReportRowType CXTPReportSection::GetRowType() const
{
return m_rowType;
}
CXTPReportSelectedRows* CXTPReportSection::GetSelectedRows() const
{
return m_pControl->GetSelectedRows();
}
CXTPReportRow* CXTPReportSection::GetFocusedRow() const
{
return GetRows()->GetFocusedRow();
}
int CXTPReportSection::GetIndex() const
{
int nIndex = -1;
const CXTPReportSections *pSections = m_pControl->GetSections();
for (int nSection=0; nSection<pSections->GetSize(); nSection++)
{
if (this == pSections->GetAt(nSection))
{
nIndex = nSection;
}
}
return nIndex;
}
void CXTPReportSection::AddRecordEx(
CXTPReportRecord *pRecord,
CXTPReportRecord *pParentRecord,
int nRowChildIndex,
int nRecordChildIndex)
{
ASSERT(pRecord);
if (!pRecord)
return;
// add record
BOOL bAddRecord = TRUE;
CXTPReportRecords* pParentRecords = pParentRecord ? pParentRecord->HasChildren() ? pParentRecord->GetChilds() : NULL : GetRecords();
if (pParentRecords)
{
for (int nChild = 0; nChild < pParentRecords->GetCount(); nChild++)
{
if (pRecord == pParentRecords->GetAt(nChild))
{
bAddRecord = FALSE;
break;
}
}
}
if (bAddRecord)
{
CXTPReportRecords* pRecords = pParentRecord ? pParentRecord->GetChilds() : GetRecords();
if (nRecordChildIndex >= 0 && nRecordChildIndex < pRecords->GetCount())
pRecords->InsertAt(nRecordChildIndex, pRecord);
else
pRecords->Add(pRecord);
}
if (!pRecord->IsVisible())
return;
// find parent record rows
CXTPReportRows* pParentRows = m_pTree;
CXTPReportRow* pParentRow = NULL;
if (pParentRecord)
{
pParentRow = m_pTree->FindInTree(pParentRecord);
if (pParentRow)
pParentRows = pParentRow->GetChilds();
}
BOOL bRoot = pParentRows == m_pTree;
// create new row
CXTPReportRow* pNewRow = m_pControl->CreateRow();
pNewRow->InitRow(m_pControl, this, pRecord);
pNewRow->m_pParentRow = NULL;
// add row to pParentRows
BOOL bInsertAfter = FALSE;
CXTPReportRow* pPlainTreeRow = NULL;
CXTPReportRow* pInsertRowPos = NULL;
int nNextSiblingIndex = m_pRows->GetCount();
if (bRoot)
{
pInsertRowPos = pParentRows->FindInsertionPos(pNewRow, bInsertAfter);
CXTPReportRow* pRow = pInsertRowPos;
while (pRow)
{
if (pRow->GetNextSiblingRow())
{
nNextSiblingIndex = pRow->GetNextSiblingRow()->GetIndex();
break;
}
else
pRow = pRow->GetParentRow();
}
CXTPReportColumns *pColumns = m_pControl->GetColumns();
if (pColumns->GetGroupsOrder()->GetCount() > 0)
{
CXTPReportGroupRow* pGroupToAdd = pInsertRowPos ? (CXTPReportGroupRow*)pInsertRowPos->GetParentRow() : NULL;
if (!pInsertRowPos || (pInsertRowPos && pInsertRowPos->IsGroupRow()))
{
for (int nColumn = pInsertRowPos ? pInsertRowPos->GetGroupLevel() : 0; nColumn < pColumns->GetGroupsOrder()->GetCount(); nColumn++)
{
CXTPReportColumn* pColumn = pColumns->GetGroupsOrder()->GetAt(nColumn);
CXTPReportRecordItem* pItem = pRecord->GetItem(pColumn);
CString strGroup = pItem ? pItem->GetGroupCaption(pColumn) : _T("");
CXTPReportGroupRow* pGroup = m_pControl->CreateGroupRow();
pGroup->InitRow(m_pControl, this, NULL);
pGroup->SetCaption(strGroup);
pGroup->m_nGroupLevel = pGroupToAdd ? pGroupToAdd->m_nRowLevel + 1 : 0;
pGroup->m_nRowLevel = nColumn;
if (pGroupToAdd)
{
if (!pPlainTreeRow)
{
pPlainTreeRow = pGroup;
pGroupToAdd->GetChilds()->InsertAt(bInsertAfter ? pInsertRowPos ? pInsertRowPos->m_nChildIndex + 1 : 0 : pInsertRowPos ? pInsertRowPos->m_nChildIndex : 0, pGroup);
}
else
pGroupToAdd->AddChild(pGroup);
pGroup->m_pParentRows = pGroupToAdd->GetChilds();
pGroup->m_pParentRow = pGroupToAdd;
pGroup->SetVisible(pGroupToAdd->IsExpanded());
}
else
{
pPlainTreeRow = pGroup;
pParentRows->InsertAt(pInsertRowPos ? pInsertRowPos->m_nChildIndex : pParentRows->GetCount(), pGroup);
pGroup->m_pParentRows = pParentRows;
pGroup->m_pParentRow = NULL;
pGroup->SetVisible(TRUE);
}
pGroupToAdd = pGroup;
}
// insert row
if (pGroupToAdd)
{
pGroupToAdd->AddChild(pNewRow);
pNewRow->m_pParentRows = pGroupToAdd->GetChilds();
// pNewRow->m_pParentRow = pGroupToAdd;
pNewRow->SetVisible(pGroupToAdd->IsExpanded());
}
}
else
{
// insert row
if (pGroupToAdd)
{
pGroupToAdd->GetChilds()->InsertAt(bInsertAfter ? pInsertRowPos ? pInsertRowPos->m_nChildIndex + 1 : 0 : pInsertRowPos ? pInsertRowPos->m_nChildIndex : 0, pNewRow);
pNewRow->m_pParentRows = pGroupToAdd->GetChilds();
pNewRow->m_pParentRow = pGroupToAdd;
pNewRow->SetVisible(pGroupToAdd->IsExpanded());
}
}
}
else
{
pParentRows->InsertAt(pInsertRowPos ? pInsertRowPos->m_nChildIndex : pParentRows->GetCount(), pNewRow);
pNewRow->m_pParentRows = pParentRows;
pNewRow->m_pParentRow = NULL;
pNewRow->SetVisible(TRUE);
}
}
else
{
if (nRowChildIndex >= 0 && nRowChildIndex < pParentRows->GetCount())
pParentRows->InsertAt(nRowChildIndex, pNewRow);
else
pParentRows->Add(pNewRow);
pNewRow->m_pParentRows = pParentRows;
pNewRow->m_pParentRow = pParentRow;
pNewRow->SetVisible(TRUE);
pNewRow->m_nRowLevel = pParentRow->m_nRowLevel + 1;
pNewRow->m_nGroupLevel = pParentRow->m_nGroupLevel;
pNewRow->m_nChildIndex = nRowChildIndex >= 0 ? nRowChildIndex : pParentRows->GetCount() - 1;
CXTPReportRow* pRow = pNewRow;
while (pRow)
{
if (pRow->GetNextSiblingRow())
{
nNextSiblingIndex = pRow->GetNextSiblingRow()->GetIndex();
break;
}
else
pRow = pRow->GetParentRow();
}
}
// refresh child indices
if (pInsertRowPos && pInsertRowPos->GetParentRows())
pInsertRowPos->GetParentRows()->RefreshChildIndices();
else
pParentRows->RefreshChildIndices();
// add row to m_pRows
if (bRoot)
{
if (pInsertRowPos)
{
CXTPReportRow* pRow = pInsertRowPos->GetParentRow();
BOOL bIsExpanded = TRUE;
while (pRow && bIsExpanded)
{
bIsExpanded = bIsExpanded && pRow->IsExpanded();
pRow = pRow->GetParentRow();
}
if (bIsExpanded)
{
if (bInsertAfter)
InsertRow(nNextSiblingIndex, pPlainTreeRow ? pPlainTreeRow : pNewRow);
else
InsertRow(pInsertRowPos->GetIndex(), pPlainTreeRow ? pPlainTreeRow : pNewRow);
}
}
else
{
InsertRow(m_pRows->GetCount(), pPlainTreeRow ? pPlainTreeRow : pNewRow);
}
}
else
{
CXTPReportRow* pRow = pNewRow->GetParentRow();
BOOL bIsExpanded = TRUE;
while (pRow && bIsExpanded)
{
bIsExpanded = bIsExpanded && pRow->IsExpanded();
pRow = pRow->GetParentRow();
}
if (bIsExpanded)
InsertRow(nNextSiblingIndex, pNewRow);
}
RefreshIndexes(FALSE, TRUE);
// add children
if (pRecord && pRecord->HasChildren())
{
for (int nChild = 0; nChild < pRecord->GetChilds()->GetCount(); nChild++)
{
AddRecordEx(pRecord->GetChilds()->GetAt(nChild), pRecord);
}
}
}
void CXTPReportSection::UpdateRecord(CXTPReportRecord *pRecord, BOOL bUpdateChildren)
{
// Get parent record
CXTPReportRecord *pParentRecord = NULL;
if (pRecord->GetRecords())
{
pParentRecord = pRecord->GetRecords()->GetOwnerRecord();
}
// Update record
if (NULL == pParentRecord || bUpdateChildren)
{
// Internal AddRef
pRecord->TreeAddRef();
// Get record row child index
CXTPReportRow *pRow = m_pTree->FindInTree(pRecord);
if (NULL != pRow)
{
int nRowChildIndex = pRow->m_nChildIndex;
int nRecordChildIndex = pRecord->GetIndex();
// Remove record
RemoveRecordEx(pRecord, FALSE);
// Add record
pRecord->InternalAddRef(); //AddRecordEx expects that pRecord has +1 ref count.
AddRecordEx(pRecord, pParentRecord, nRowChildIndex, nRecordChildIndex);
// Internal release
pRecord->TreeRelease();
}
}
m_pControl->RedrawControl();
}
BOOL CXTPReportSection::RemoveRecordEx(CXTPReportRecord* pRecord, BOOL bAdjustLayout, BOOL bRemoveFromParent)
{
CXTPReportRecords *pRecords = GetRecords();
ASSERT(pRecord && pRecords);
if (!pRecord || !pRecords)
return FALSE;
if (pRecord->HasChildren())
{
for (int i=pRecord->GetChilds()->GetCount() - 1; i>=0 ; i--)
{
RemoveRecordEx(pRecord->GetChilds()->GetAt(i), bAdjustLayout, FALSE);
}
}
BOOL bResult = FALSE;
//--------------------------------------------------------
CXTPReportRow *pRow0 = m_pTree->FindInTree(pRecord);
if (NULL != pRow0)
{
CXTPReportRow* pRow = pRow0;
pRow0 = NULL;
do
{
CXTPReportRow* pRow_parent = pRow->GetParentRow();
ASSERT(pRow->GetParentRows());
// 1. Remove from selected rows
if (GetSelectedRows()->Contains(pRow))
{
GetSelectedRows()->Remove(pRow);
}
// 2. remove from Rows Tree
if (pRow->GetParentRows())
{
VERIFY(pRow->GetParentRows()->RemoveRow(pRow) >= 0);
}
// 3. remove from Display Rows array
m_pRows->RemoveRow(pRow);
pRow = pRow_parent;
} while (pRow && pRow->IsGroupRow() && pRow->GetChilds()->GetCount() == 0);
// refresh child indices
if (pRow && pRow->HasChildren())
pRow->GetChilds()->RefreshChildIndices();
else
m_pTree->RefreshChildIndices();
bResult = TRUE;
}
//-------------------------------------------------------
//pRecord->Delete(); // the code below is more safe when record already removed!
ASSERT(pRecord->GetRecords());
if (bRemoveFromParent && pRecord->GetRecords())
{
BOOL bRecordRem = pRecord->GetRecords()->RemoveRecord(pRecord) >= 0;
pRecord = NULL;
bResult |= bRecordRem;
}
if (m_pRows->m_nFocusedRow >= 0 && m_pRows->m_nFocusedRow >= m_pRows->GetCount())
m_pRows->m_nFocusedRow = m_pRows->GetCount() - 1;
//-------------------------------------------------------
if (bResult)
{
RefreshIndexes(bAdjustLayout, FALSE);
}
return bResult;
}
BOOL CXTPReportSection::RemoveRowEx(CXTPReportRow *pRow, BOOL bAdjustLayout)
{
ASSERT(NULL != pRow);
if (NULL == pRow)
{
return FALSE;
}
if (!pRow->IsGroupRow())
{
ASSERT(pRow->GetRecord());
return RemoveRecordEx(pRow->GetRecord(), bAdjustLayout);
}
else
{
int nCount = pRow->GetChilds()->GetCount();
for (int i = nCount - 1; i >= 0 ; i--)
{
CXTPReportRow* pRowI = pRow->GetChilds()->GetAt(i);
RemoveRowEx(pRowI, bAdjustLayout);
}
return TRUE;
}
}
/////////////////////////////////////////////////////////////////////////////
// Scroll index
/////////////////////////////////////////////////////////////////////////////
int CXTPReportSection::GetScrollIndexV()
{
if (-1 == m_nScrollIndexV)
{
switch(GetScrollModeV())
{
case xtpReportScrollModeSmooth:
{
int nIndex = 0;
int nTop = 0;
CRect rcSection(m_rcSection);
// Draw border
if (NULL != m_pBorder)
{
// Deduct margin, border and padding from section rectangle
rcSection = CXTPReportBorder::DeflateRect(rcSection, m_pBorder);
}
CClientDC dc(m_pControl);
if (GetRowAt(&dc, rcSection.Width(), m_nScrollOffsetV, nIndex, nTop))
{
m_nScrollIndexV = nIndex;
}
}
break;
default:
m_nScrollIndexV = 0;
break;
}
}
return m_nScrollIndexV;
}
void CXTPReportSection::SetScrollIndexV(int nScrollIndexV)
{
m_nScrollIndexV = nScrollIndexV;
if (xtpReportScrollModeSmooth == GetScrollModeV())
{
CClientDC dc(m_pControl);
m_nScrollOffsetV = GetRowOffset(&dc, GetRect().Width(), nScrollIndexV);
}
// Update scrollbar position
if (this == m_pControl->m_pSectionScroll)
{
m_pControl->UpdateScrollBarV();
}
}