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.

719 lines
16 KiB
C++

2 years ago
// XTPReportRows.cpp : implementation of the CXTPReportRows 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/XTPCustomHeap.h"
#include "Common/XTPSystemHelpers.h"
#include "Common/XTPSmartPtrInternalT.h"
#include "XTPReportDefines.h"
#include "XTPReportRow.h"
#include "XTPReportGroupRow.h"
#include "XTPReportRecord.h"
#include "XTPReportRecords.h"
#include "XTPReportRecordItem.h"
#include "XTPReportControl.h"
#include "XTPReportColumns.h"
#include "XTPReportColumn.h"
#include "XTPReportRows.h"
#include "ItemTypes/XTPReportRecordItemVariant.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
//////////////////////////////////////////////////////////////////////////
// CXTPReportScreenRows
CXTPReportScreenRows::CXTPReportScreenRows()
{
}
CXTPReportScreenRows::~CXTPReportScreenRows()
{
Clear();
}
void CXTPReportScreenRows::Clear()
{
// array cleanup
for (int nRow = 0; nRow < GetSize(); nRow++)
{
CXTPReportRow* pRow = GetAt(nRow);
pRow->InternalRelease();
}
RemoveAll();
}
CXTPReportRow* CXTPReportScreenRows::HitTest(CPoint pt) const
{
for (int nRow=0; nRow<GetSize(); nRow++)
{
CXTPReportRow *pRow = GetAt(nRow);
if (pRow->GetRect().PtInRect(pt))
{
return pRow;
}
}
return NULL;
}
//////////////////////////////////////////////////////////////////////////
// CXTPReportRows
CXTPReportRows::CXTPReportRows()
: m_nFocusedRow(-1)
{
m_pVirtualRow = 0;
m_nVirtualRowsCount = 0;
m_pScreenRows = NULL;
}
CXTPReportRows::~CXTPReportRows()
{
Clear();
SAFE_DELETE(m_pScreenRows);
}
//////////////////////////////////////////////////////////////////////////
// CXTPReportRows member functions
CXTPReportScreenRows *CXTPReportRows::GetScreenRows()
{
if (NULL == m_pScreenRows)
{
// Create on demand
m_pScreenRows = new CXTPReportScreenRows();
}
return m_pScreenRows;
}
BOOL CXTPReportRows::Contains(const CXTPReportRow *pRow) const
{
for (int nRow=0; nRow<m_arrRows.GetSize(); nRow++)
{
if (pRow == m_arrRows.GetAt(nRow))
{
return TRUE;
}
}
return FALSE;
}
int CXTPReportRows::GetFocusedRowIndex() const
{
return m_nFocusedRow;
}
BOOL CXTPReportRows::HasFocus() const
{
return (-1 != m_nFocusedRow);
}
void CXTPReportRows::SetFocusedRowIndex(int nRow)
{
m_nFocusedRow = nRow;
}
CXTPReportRow *CXTPReportRows::GetFocusedRow()
{
CXTPReportRow *pFocusedRow = NULL;
if (-1 != m_nFocusedRow)
{
pFocusedRow = GetAt(m_nFocusedRow);
}
return pFocusedRow;
}
void CXTPReportRows::Clear(BOOL bResetRow)
{
// array cleanup
for (int nRow = (int) m_arrRows.GetSize() - 1; nRow >= 0; nRow--)
{
CXTPReportRow* pRow = m_arrRows.GetAt(nRow);
if (bResetRow)
{
pRow->SetVisible(FALSE);
pRow->m_nIndex = -1;
}
pRow->InternalRelease();
}
m_arrRows.RemoveAll();
if (m_pVirtualRow)
{
m_pVirtualRow->InternalRelease();
m_pVirtualRow = 0;
}
GetScreenRows()->Clear();
m_nFocusedRow = -1;
}
CXTPReportRow* CXTPReportRows::GetNext(CXTPReportRow* pRow, BOOL bSkipGroupFocus)
{
if (pRow)
{
int index = pRow->GetIndex();
while (index < GetCount() - 1)
{
index++;
pRow = GetAt(index);
if (!bSkipGroupFocus || !pRow->IsGroupRow() || !pRow->IsExpanded())
return pRow;
}
}
else if (GetCount() > 0)
{
return GetAt(0);
}
return pRow;
}
CXTPReportRow* CXTPReportRows::GetPrev(CXTPReportRow* pRow, BOOL bSkipGroupFocus)
{
if (pRow)
{
int index = pRow->GetIndex();
while (index > 0)
{
index--;
pRow = GetAt(index);
if (!bSkipGroupFocus || !pRow->IsGroupRow() || !pRow->IsExpanded())
return pRow;
}
}
else if (GetCount() > 0)
{
return GetAt(0);
}
return pRow;
}
void CXTPReportRows::InsertAt(int nIndex, CXTPReportRow* pRow)
{
m_arrRows.InsertAt(nIndex, pRow);
}
int CXTPReportRows::Add(CXTPReportRow* pRow)
{
int nIndex = (int)m_arrRows.Add(pRow);
pRow->m_nChildIndex = nIndex;
pRow->m_pParentRows = this;
return nIndex;
}
void CXTPReportRows::RemoveAt(int nIndex)
{
m_arrRows.GetAt(nIndex)->InternalRelease();
m_arrRows.RemoveAt(nIndex);
}
int CXTPReportRows::RemoveRow(CXTPReportRow* pRow)
{
for (int i = 0; i < (int) m_arrRows.GetSize(); i++)
{
if (m_arrRows.GetAt(i) == pRow)
{
RemoveAt(i);
return i;
}
}
return -1;
}
void CXTPReportRows::SetVirtualMode(CXTPReportRow* pRow, int nCount)
{
ASSERT(m_pVirtualRow == NULL);
m_pVirtualRow = pRow;
m_nVirtualRowsCount = nCount;
}
int _cdecl CXTPReportRows::CompareRows(const CXTPReportRow** ppRow1, const CXTPReportRow** ppRow2)
{
CXTPReportColumns* pColumns = (**ppRow1).GetControl()->GetColumns();
CXTPReportRecord* pRecord1 = (**ppRow1).GetRecord();
CXTPReportRecord* pRecord2 = (**ppRow2).GetRecord();
ASSERT(pColumns);
ASSERT(pRecord1 && pRecord2 || !pRecord1 && !pRecord2);
if (pRecord1 == pRecord2 && pRecord1)
return 0;
if (!pRecord1 && !pRecord2)
{
// compare groupRows by its first not group child rows
if ((*ppRow1)->HasChildren() && (*ppRow2)->HasChildren())
{
CXTPReportRow* pRow1a = (CXTPReportRow*)(*ppRow1);
CXTPReportRow* pRow2a = (CXTPReportRow*)(*ppRow2);
if (pRow1a->GetChilds() && pRow1a->GetChilds()->GetCount() &&
pRow2a->GetChilds() && pRow2a->GetChilds()->GetCount())
{
pRow1a = pRow1a->GetChilds()->GetAt(0);
pRow2a = pRow2a->GetChilds()->GetAt(0);
return CompareRows((const CXTPReportRow**)&pRow1a, (const CXTPReportRow**)&pRow2a);
}
}
}
if (!pRecord1 || !pRecord2)
{
ASSERT(FALSE);
return 0;
}
for (int nGroupOrder = 0; nGroupOrder < pColumns->GetGroupsOrder()->GetCount(); nGroupOrder++)
{
CXTPReportColumn* pColumn = pColumns->GetGroupsOrder()->GetAt(nGroupOrder);
if (!pColumn->m_bAutoSortWhenGrouped)
continue;
BOOL bIncreasing = pColumn->m_bSortIncreasing;
CXTPReportRecordItem* pItem1 = pRecord1->GetItem(pColumn);
CXTPReportRecordItem* pItem2 = pRecord2->GetItem(pColumn);
if (!pItem1 || !pItem2)
continue;
int nCompareResult = pItem1->CompareGroupCaption(pColumn, pItem2);
if (nCompareResult != 0)
return nCompareResult * (bIncreasing ? 1 : -1);
}
for (int nSortOrder = 0; nSortOrder < pColumns->GetSortOrder()->GetCount(); nSortOrder++)
{
CXTPReportColumn* pColumn = pColumns->GetSortOrder()->GetAt(nSortOrder);
BOOL bIncreasing = pColumn->IsSortedIncreasing();
CXTPReportRecordItem* pItem1 = pRecord1->GetItem(pColumn);
CXTPReportRecordItem* pItem2 = pRecord2->GetItem(pColumn);
if (!pItem1 || !pItem2)
continue;
int nCompareResult = pItem1->Compare(pColumn, pItem2);
if (nCompareResult != 0)
return nCompareResult * (bIncreasing ? 1 : -1);
}
return pRecord1->GetIndex() > pRecord2->GetIndex() ? 1 : -1;
}
int _cdecl CXTPReportRows::CompareRows2(const CXTPReportRow** ppRow1, const CXTPReportRow** ppRow2)
{
CXTPReportColumns* pColumns = (**ppRow1).GetControl()->GetColumns();
CXTPReportRecord* pRecord1 = (**ppRow1).GetRecord();
CXTPReportRecord* pRecord2 = (**ppRow2).GetRecord();
ASSERT(pColumns);
ASSERT(pRecord1 && pRecord2 || !pRecord1 && !pRecord2);
if (pRecord1 == pRecord2 && pRecord1)
return 0;
if (!pRecord1 && !pRecord2)
{
// compare groupRows by its first not group child rows
if ((*ppRow1)->HasChildren() && (*ppRow2)->HasChildren())
{
CXTPReportRow* pRow1a = (CXTPReportRow*)(*ppRow1);
CXTPReportRow* pRow2a = (CXTPReportRow*)(*ppRow2);
if (pRow1a->GetChilds() && pRow1a->GetChilds()->GetCount() &&
pRow2a->GetChilds() && pRow2a->GetChilds()->GetCount())
{
pRow1a = pRow1a->GetChilds()->GetAt(0);
pRow2a = pRow2a->GetChilds()->GetAt(0);
return CompareRows((const CXTPReportRow**)&pRow1a, (const CXTPReportRow**)&pRow2a);
}
}
}
if (!pRecord1 || !pRecord2)
{
ASSERT(FALSE);
return 0;
}
for (int nGroupOrder = 0; nGroupOrder < pColumns->GetGroupsOrder()->GetCount(); nGroupOrder++)
{
CXTPReportColumn* pColumn = pColumns->GetGroupsOrder()->GetAt(nGroupOrder);
if (!pColumn->m_bAutoSortWhenGrouped)
continue;
BOOL bIncreasing = pColumn->m_bSortIncreasing;
CXTPReportRecordItem* pItem1 = pRecord1->GetItem(pColumn);
CXTPReportRecordItem* pItem2 = pRecord2->GetItem(pColumn);
if (!pItem1 || !pItem2)
continue;
int nCompareResult = pItem1->CompareGroupCaption(pColumn, pItem2);
if (nCompareResult != 0)
{
if (nGroupOrder > 0)
nCompareResult = bIncreasing ? -1 : 1;
return nCompareResult * (bIncreasing ? 1 : -1);
}
}
for (int nSortOrder = 0; nSortOrder < pColumns->GetSortOrder()->GetCount(); nSortOrder++)
{
CXTPReportColumn* pColumn = pColumns->GetSortOrder()->GetAt(nSortOrder);
BOOL bIncreasing = pColumn->IsSortedIncreasing();
CXTPReportRecordItem* pItem1 = pRecord1->GetItem(pColumn);
CXTPReportRecordItem* pItem2 = pRecord2->GetItem(pColumn);
if (!pItem1 || !pItem2)
continue;
int nCompareResult = pItem1->Compare(pColumn, pItem2);
if (nCompareResult != 0)
return nCompareResult * (bIncreasing ? 1 : -1);
}
if (pRecord1->GetIndex() == pRecord2->GetIndex())
{
ASSERT(FALSE);
return 0;
}
return pRecord1->GetIndex() < pRecord2->GetIndex() ? -1 : 1;
}
void CXTPReportRows::SortEx(XTPReportRowsCompareFunc pCompareFunc)
{
typedef int (_cdecl *GENERICCOMPAREFUNC)(const void *, const void*);
qsort(m_arrRows.GetData(), (size_t) m_arrRows.GetSize(), sizeof(CXTPReportRow*), (GENERICCOMPAREFUNC)pCompareFunc);
}
void CXTPReportRows::Sort()
{
SortEx(CompareRows);
}
int CXTPReportRows::GetCount() const
{
if (m_pVirtualRow)
return m_nVirtualRowsCount;
return (int) m_arrRows.GetSize();
}
CXTPReportRow* CXTPReportRows::GetAt(int nIndex) const
{
if (m_pVirtualRow)
{
m_pVirtualRow->m_nIndex = nIndex;
return m_pVirtualRow;
}
return (nIndex >= 0) && (nIndex < GetCount()) ? m_arrRows.GetAt(nIndex) : NULL;
}
CXTPReportRow* CXTPReportRows::Find(CXTPReportRecord *pRecord, BOOL bRecursive)
{
int nSize = (int)m_arrRows.GetSize();
for (int nRow=0; nRow<nSize; nRow++)
{
CXTPReportRow *pRow = m_arrRows.GetAt(nRow);
if (pRow->GetRecord() == pRecord)
{
return pRow;
}
if (pRow->HasChildren() && bRecursive)
{
pRow = pRow->GetChilds()->Find(pRecord, TRUE);
if (NULL != pRow)
{
return pRow;
}
}
}
return NULL;
}
CXTPReportRow* CXTPReportRows::Find(CXTPReportRecord *pRecord)
{
return Find(pRecord, FALSE);
}
CXTPReportRow* CXTPReportRows::FindInTree(CXTPReportRecord *pRecord)
{
return Find(pRecord, TRUE);
}
CXTPReportRow* CXTPReportRows::FindInsertionPos(CXTPReportRow* pRow, BOOL& bInsertAfter)
{
CXTPReportRow* pInsertionRowPos = NULL;
bInsertAfter = FALSE;
for (int i = 0; i < (int) m_arrRows.GetSize(); i++)
{
CXTPReportRow* pCurRow = m_arrRows.GetAt(i);
if (pCurRow->IsGroupRow())
{
// compare group caption
CXTPReportRecord* pRecord = pRow->GetRecord();
CXTPReportColumns* pColumns = pRow->GetControl()->GetColumns();
if (!(pRecord && pColumns))
continue;
CXTPReportColumn* pColumn = pColumns->GetGroupsOrder()->GetAt(pCurRow->GetGroupLevel());
CXTPReportRecordItem* pItem = pRecord->GetItem(pColumn);
COleVariant varGroup(pItem ? (LPCTSTR)pItem->GetGroupCaption(pColumn) : _T(""));
COleVariant varGroupRowCaption((LPCTSTR)((CXTPReportGroupRow*)pCurRow)->GetCaption());
ULONG dwFlags = pRecord->GetRecords()->IsCaseSensitive() ? 0 : NORM_IGNORECASE;
LCID lcidnSortLocale = CXTPReportRecordItemVariant::m_nSortLocale;
if (lcidnSortLocale == LOCALE_USER_DEFAULT)
{
lcidnSortLocale = CXTPReportControlLocale::GetActiveLCID();
}
int nCompareResult = VarCmp(varGroup, varGroupRowCaption, lcidnSortLocale, dwFlags) - VARCMP_EQ;
if (pColumn->m_bSortIncreasing && nCompareResult > 0 || !pColumn->m_bSortIncreasing && nCompareResult < 0)
continue;
// find in children
if (!pCurRow->GetChilds())
continue;
if (nCompareResult == 0)
pInsertionRowPos = pCurRow->GetChilds()->FindInsertionPos(pRow, bInsertAfter);
if (!pInsertionRowPos)
{
if (nCompareResult != 0)
pInsertionRowPos = pCurRow;
else
{
pInsertionRowPos = pCurRow->GetChilds()->GetAt(pCurRow->GetChilds()->GetCount() - 1);
bInsertAfter = TRUE;
}
}
break;
}
if (CompareRows2((const CXTPReportRow**)&pRow, (const CXTPReportRow**)&pCurRow) <= 0)
{
pInsertionRowPos = pCurRow;
break;
}
}
return pInsertionRowPos;
}
void CXTPReportRows::RefreshChildIndices(BOOL bRunInChildren)
{
for (int nIndex = 0; nIndex < GetCount(); nIndex++)
{
CXTPReportRow* pRow = GetAt(nIndex);
pRow->m_nChildIndex = nIndex;
ASSERT(pRow->m_pParentRows == this);
if (bRunInChildren && pRow->HasChildren() && pRow->GetChilds())
pRow->GetChilds()->RefreshChildIndices(bRunInChildren);
}
}
void CXTPReportRows::SetSize(INT_PTR nNewSize, INT_PTR nGrowBy)
{
int nSize = GetCount();
if (nNewSize < nSize)
{
for (int i = (int)nNewSize; i < nSize; i++)
{
CXTPReportRow* pRow = GetAt(i);
if (pRow)
pRow->InternalRelease();
}
}
m_arrRows.SetSize(nNewSize, nGrowBy);
}
void CXTPReportRows::ReserveSize(INT_PTR nNewSize, INT_PTR nGrowBy)
{
m_arrRows.ReserveSize(nNewSize, nGrowBy);
}
void CXTPReportRows::SetAt(INT_PTR nIndex, CXTPReportRow* pRow)
{
ASSERT(pRow);
ASSERT(nIndex >= 0 && nIndex < GetCount());
if (!pRow || nIndex < 0 || nIndex >= GetCount())
return;
CXTPReportRow* pRow_prev = GetAt((int)nIndex);
if (pRow_prev)
pRow_prev->InternalRelease();
pRow->m_nChildIndex = (int)nIndex;
pRow->m_pParentRows = this;
m_arrRows.SetAt(nIndex, pRow);
}
CXTPReportRecordItem* CXTPReportRows::FindRecordItemByRows(int nStartIndex, int nEndIndex,
int nStartColumn, int nEndColumn,
int nRecordIndex, int nItem,
LPCTSTR pcszText, int nFlags)
{
CString sFind(pcszText);
BOOL bExactStart = (nFlags & xtpReportTextSearchExactStart) > 0;
if (bExactStart)
nFlags -= xtpReportTextSearchExactStart;
// NO xtpReportTextSearchBackward case
if ((nFlags & xtpReportTextSearchBackward) > 0)
nFlags -= xtpReportTextSearchBackward;
// validate parameters
int N = GetCount();
if (N < 1)
return NULL;
nStartIndex = max(nStartIndex, 0);
nStartIndex = min(nStartIndex, N - 1);
nEndIndex = max(nStartIndex, nEndIndex);
nEndIndex = min(nEndIndex, N - 1);
nStartColumn = max(nStartColumn, 0);
nEndColumn = max(nEndColumn, 0);
nEndColumn = max(nStartColumn, nEndColumn);
nRecordIndex = max(nRecordIndex, 0);
nRecordIndex = min(nRecordIndex, N - 1);
nItem = max(nItem, nStartColumn);
nItem = min(nEndColumn, nItem);
for (int i = nStartIndex; i <= nEndIndex; i++)
{
if (i < nRecordIndex) continue;
CXTPReportRow* pTryRow = GetAt(i);
CXTPReportRecord* pTryRecord = pTryRow->GetRecord();
if (pTryRecord)
{
for (int j = nStartColumn; j <= nEndColumn; j++)
{
if (i == nRecordIndex && j < nItem) continue;
CXTPReportRecordItem* pTryItem = pTryRecord->GetItem(j);
if (pTryItem)
{
CString strCaption = pTryItem->GetCaption(NULL);
if (strCaption.IsEmpty()) continue;
int k = strCaption.Find(sFind);
//if (k > -1)
if ((bExactStart && k == 0) || (!bExactStart && k > -1))
{
if (nFlags == (xtpReportTextSearchExactPhrase |
xtpReportTextSearchMatchCase))
{
if (strCaption == sFind)
return pTryItem;
}
else if (nFlags == xtpReportTextSearchExactPhrase)
{
CString sCAP(strCaption);
sCAP.MakeUpper();
CString sFIND(sFind);
sFIND.MakeUpper();
if (sCAP == sFIND)
return pTryItem;
}
else if (nFlags == xtpReportTextSearchMatchCase)
{
if (strCaption.Mid(k) == sFind)
return pTryItem;
}
else if (nFlags == 0)
{
return pTryItem;
}
}
else if (!bExactStart)
{
CString sCAP(strCaption);
sCAP.MakeUpper();
CString sFIND(sFind);
sFIND.MakeUpper();
k = sCAP.Find(sFIND);
if (k > -1)
{
if (nFlags == xtpReportTextSearchExactPhrase)
{
if (sCAP == sFIND)
return pTryItem;
}
else if (nFlags == 0)
{
return pTryItem;
}
}
}
}
}
}
}
return NULL;
}