fix baseline build (old cairo) - 'cairo_rectangle_int_t' does not name a type
[LibreOffice.git] / sc / source / core / data / dptabres.cxx
blob146bbf400d060d9103035b54258ed9eec0eb4c7d
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "dptabres.hxx"
22 #include "dptabdat.hxx"
23 #include "dptabsrc.hxx"
24 #include "global.hxx"
25 #include "subtotal.hxx"
26 #include "globstr.hrc"
27 #include "dpitemdata.hxx"
29 #include "document.hxx"
30 #include "dpresfilter.hxx"
31 #include "dputil.hxx"
33 #include <osl/diagnose.h>
34 #include <rtl/math.hxx>
35 #include <rtl/strbuf.hxx>
37 #include <math.h>
38 #include <float.h>
39 #include <algorithm>
40 #include <unordered_map>
41 #include <boost/checked_delete.hpp>
42 #include <boost/scoped_ptr.hpp>
44 #include <com/sun/star/sheet/DataResultFlags.hpp>
45 #include <com/sun/star/sheet/MemberResultFlags.hpp>
46 #include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
47 #include <com/sun/star/sheet/DataPilotFieldReferenceType.hpp>
48 #include <com/sun/star/sheet/DataPilotFieldReferenceItemType.hpp>
49 #include <com/sun/star/sheet/DataPilotFieldShowItemsMode.hpp>
50 #include <com/sun/star/sheet/DataPilotFieldSortMode.hpp>
51 #include <com/sun/star/sheet/DataPilotFieldFilter.hpp>
53 using namespace com::sun::star;
54 using ::std::vector;
55 using ::std::pair;
56 using ::com::sun::star::uno::Sequence;
58 namespace {
60 sal_uInt16 nFuncStrIds[12] = // matching enum ScSubTotalFunc
62 0, // SUBTOTAL_FUNC_NONE
63 STR_FUN_TEXT_AVG, // SUBTOTAL_FUNC_AVE
64 STR_FUN_TEXT_COUNT, // SUBTOTAL_FUNC_CNT
65 STR_FUN_TEXT_COUNT, // SUBTOTAL_FUNC_CNT2
66 STR_FUN_TEXT_MAX, // SUBTOTAL_FUNC_MAX
67 STR_FUN_TEXT_MIN, // SUBTOTAL_FUNC_MIN
68 STR_FUN_TEXT_PRODUCT, // SUBTOTAL_FUNC_PROD
69 STR_FUN_TEXT_STDDEV, // SUBTOTAL_FUNC_STD
70 STR_FUN_TEXT_STDDEV, // SUBTOTAL_FUNC_STDP
71 STR_FUN_TEXT_SUM, // SUBTOTAL_FUNC_SUM
72 STR_FUN_TEXT_VAR, // SUBTOTAL_FUNC_VAR
73 STR_FUN_TEXT_VAR // SUBTOTAL_FUNC_VARP
76 bool lcl_SearchMember( const std::vector <ScDPResultMember *>& list, SCROW nOrder, SCROW& rIndex)
78 rIndex = list.size();
79 bool bFound = false;
80 SCROW nLo = 0;
81 SCROW nHi = list.size() - 1;
82 SCROW nIndex;
83 while (nLo <= nHi)
85 nIndex = (nLo + nHi) / 2;
86 if ( list[nIndex]->GetOrder() < nOrder )
87 nLo = nIndex + 1;
88 else
90 nHi = nIndex - 1;
91 if ( list[nIndex]->GetOrder() == nOrder )
93 bFound = true;
94 nLo = nIndex;
98 rIndex = nLo;
99 return bFound;
102 class FilterStack
104 std::vector<ScDPResultFilter>& mrFilters;
105 public:
106 FilterStack(std::vector<ScDPResultFilter>& rFilters) : mrFilters(rFilters) {}
108 void pushDimName(const OUString& rName, bool bDataLayout)
110 mrFilters.push_back(ScDPResultFilter(rName, bDataLayout));
113 void pushDimValue(const OUString& rValue)
115 ScDPResultFilter& rFilter = mrFilters.back();
116 rFilter.maValue = rValue;
117 rFilter.mbHasValue = true;
120 ~FilterStack()
122 ScDPResultFilter& rFilter = mrFilters.back();
123 if (rFilter.mbHasValue)
124 rFilter.mbHasValue = false;
125 else
126 mrFilters.pop_back();
132 // function objects for sorting of the column and row members:
134 class ScDPRowMembersOrder
136 ScDPResultDimension& rDimension;
137 long nMeasure;
138 bool bAscending;
140 public:
141 ScDPRowMembersOrder( ScDPResultDimension& rDim, long nM, bool bAsc ) :
142 rDimension(rDim),
143 nMeasure(nM),
144 bAscending(bAsc)
146 ~ScDPRowMembersOrder() {}
148 bool operator()( sal_Int32 nIndex1, sal_Int32 nIndex2 ) const;
151 class ScDPColMembersOrder
153 ScDPDataDimension& rDimension;
154 long nMeasure;
155 bool bAscending;
157 public:
158 ScDPColMembersOrder( ScDPDataDimension& rDim, long nM, bool bAsc ) :
159 rDimension(rDim),
160 nMeasure(nM),
161 bAscending(bAsc)
163 ~ScDPColMembersOrder() {}
165 bool operator()( sal_Int32 nIndex1, sal_Int32 nIndex2 ) const;
168 static bool lcl_IsLess( const ScDPDataMember* pDataMember1, const ScDPDataMember* pDataMember2, long nMeasure, bool bAscending )
170 // members can be NULL if used for rows
172 ScDPSubTotalState aEmptyState;
173 const ScDPAggData* pAgg1 = pDataMember1 ? pDataMember1->GetConstAggData( nMeasure, aEmptyState ) : NULL;
174 const ScDPAggData* pAgg2 = pDataMember2 ? pDataMember2->GetConstAggData( nMeasure, aEmptyState ) : NULL;
176 bool bError1 = pAgg1 && pAgg1->HasError();
177 bool bError2 = pAgg2 && pAgg2->HasError();
178 if ( bError1 )
179 return false; // errors are always sorted at the end
180 else if ( bError2 )
181 return true; // errors are always sorted at the end
182 else
184 double fVal1 = ( pAgg1 && pAgg1->HasData() ) ? pAgg1->GetResult() : 0.0; // no data is sorted as 0
185 double fVal2 = ( pAgg2 && pAgg2->HasData() ) ? pAgg2->GetResult() : 0.0;
187 // compare values
188 // don't have to check approxEqual, as this is the only sort criterion
190 return bAscending ? ( fVal1 < fVal2 ) : ( fVal1 > fVal2 );
194 static bool lcl_IsEqual( const ScDPDataMember* pDataMember1, const ScDPDataMember* pDataMember2, long nMeasure )
196 // members can be NULL if used for rows
198 ScDPSubTotalState aEmptyState;
199 const ScDPAggData* pAgg1 = pDataMember1 ? pDataMember1->GetConstAggData( nMeasure, aEmptyState ) : NULL;
200 const ScDPAggData* pAgg2 = pDataMember2 ? pDataMember2->GetConstAggData( nMeasure, aEmptyState ) : NULL;
202 bool bError1 = pAgg1 && pAgg1->HasError();
203 bool bError2 = pAgg2 && pAgg2->HasError();
204 if ( bError1 )
206 if ( bError2 )
207 return true; // equal
208 else
209 return false;
211 else if ( bError2 )
212 return false;
213 else
215 double fVal1 = ( pAgg1 && pAgg1->HasData() ) ? pAgg1->GetResult() : 0.0; // no data is sorted as 0
216 double fVal2 = ( pAgg2 && pAgg2->HasData() ) ? pAgg2->GetResult() : 0.0;
218 // compare values
219 // this is used to find equal data at the end of the AutoShow range, so approxEqual must be used
221 return rtl::math::approxEqual( fVal1, fVal2 );
225 bool ScDPRowMembersOrder::operator()( sal_Int32 nIndex1, sal_Int32 nIndex2 ) const
227 const ScDPResultMember* pMember1 = rDimension.GetMember(nIndex1);
228 const ScDPResultMember* pMember2 = rDimension.GetMember(nIndex2);
230 // make the hide item to the largest order.
231 if ( !pMember1->IsVisible() || !pMember2->IsVisible() )
232 return pMember1->IsVisible();
233 const ScDPDataMember* pDataMember1 = pMember1->GetDataRoot() ;
234 const ScDPDataMember* pDataMember2 = pMember2->GetDataRoot();
235 // GetDataRoot can be NULL if there was no data.
236 // IsVisible == false can happen after AutoShow.
237 return lcl_IsLess( pDataMember1, pDataMember2, nMeasure, bAscending );
240 bool ScDPColMembersOrder::operator()( sal_Int32 nIndex1, sal_Int32 nIndex2 ) const
242 const ScDPDataMember* pDataMember1 = rDimension.GetMember(nIndex1);
243 const ScDPDataMember* pDataMember2 = rDimension.GetMember(nIndex2);
244 bool bHide1 = pDataMember1 && !pDataMember1->IsVisible();
245 bool bHide2 = pDataMember2 && !pDataMember2->IsVisible();
246 if ( bHide1 || bHide2 )
247 return !bHide1;
248 return lcl_IsLess( pDataMember1, pDataMember2, nMeasure, bAscending );
251 ScDPInitState::Member::Member(long nSrcIndex, SCROW nNameIndex) :
252 mnSrcIndex(nSrcIndex), mnNameIndex(nNameIndex) {}
254 void ScDPInitState::AddMember( long nSourceIndex, SCROW nMember )
256 maMembers.push_back(Member(nSourceIndex, nMember));
259 void ScDPInitState::RemoveMember()
261 OSL_ENSURE(!maMembers.empty(), "ScDPInitState::RemoveMember: Attempt to remmove member while empty.");
262 if (!maMembers.empty())
263 maMembers.pop_back();
266 namespace {
268 #if DEBUG_PIVOT_TABLE
269 void lcl_DumpRow(
270 const OUString& rType, const OUString& rName, const ScDPAggData* pAggData,
271 ScDocument* pDoc, ScAddress& rPos )
273 SCCOL nCol = rPos.Col();
274 SCROW nRow = rPos.Row();
275 SCTAB nTab = rPos.Tab();
276 pDoc->SetString( nCol++, nRow, nTab, rType );
277 pDoc->SetString( nCol++, nRow, nTab, rName );
278 while ( pAggData )
280 pDoc->SetValue( nCol++, nRow, nTab, pAggData->GetResult() );
281 pAggData = pAggData->GetExistingChild();
283 rPos.SetRow( nRow + 1 );
286 void lcl_Indent( ScDocument* pDoc, SCROW nStartRow, const ScAddress& rPos )
288 SCCOL nCol = rPos.Col();
289 SCTAB nTab = rPos.Tab();
291 OUString aString;
292 for (SCROW nRow = nStartRow; nRow < rPos.Row(); nRow++)
294 aString = pDoc->GetString(nCol, nRow, nTab);
295 if (!aString.isEmpty())
297 aString = " " + aString;
298 pDoc->SetString( nCol, nRow, nTab, aString );
302 #endif
306 ScDPRunningTotalState::ScDPRunningTotalState( ScDPResultMember* pColRoot, ScDPResultMember* pRowRoot ) :
307 pColResRoot(pColRoot), pRowResRoot(pRowRoot)
309 // These arrays should never be empty as the terminating value must be present at all times.
310 maColVisible.push_back(-1);
311 maColSorted.push_back(-1);
312 maRowVisible.push_back(-1);
313 maRowSorted.push_back(-1);
316 void ScDPRunningTotalState::AddColIndex( long nVisible, long nSorted )
318 maColVisible.back() = nVisible;
319 maColVisible.push_back(-1);
321 maColSorted.back() = nSorted;
322 maColSorted.push_back(-1);
325 void ScDPRunningTotalState::AddRowIndex( long nVisible, long nSorted )
327 maRowVisible.back() = nVisible;
328 maRowVisible.push_back(-1);
330 maRowSorted.back() = nSorted;
331 maRowSorted.push_back(-1);
334 void ScDPRunningTotalState::RemoveColIndex()
336 OSL_ENSURE(!maColVisible.empty() && !maColSorted.empty(), "ScDPRunningTotalState::RemoveColIndex: array is already empty!");
337 if (maColVisible.size() >= 2)
339 maColVisible.pop_back();
340 maColVisible.back() = -1;
343 if (maColSorted.size() >= 2)
345 maColSorted.pop_back();
346 maColSorted.back() = -1;
350 void ScDPRunningTotalState::RemoveRowIndex()
352 OSL_ENSURE(!maRowVisible.empty() && !maRowSorted.empty(), "ScDPRunningTotalState::RemoveRowIndex: array is already empty!");
353 if (maRowVisible.size() >= 2)
355 maRowVisible.pop_back();
356 maRowVisible.back() = -1;
359 if (maRowSorted.size() >= 2)
361 maRowSorted.pop_back();
362 maRowSorted.back() = -1;
366 ScDPRelativePos::ScDPRelativePos( long nBase, long nDir ) :
367 nBasePos( nBase ),
368 nDirection( nDir )
372 void ScDPAggData::Update( const ScDPValue& rNext, ScSubTotalFunc eFunc, const ScDPSubTotalState& rSubState )
374 if (nCount<0) // error?
375 return; // nothing more...
377 if (rNext.meType == ScDPValue::Empty)
378 return;
380 if ( rSubState.eColForce != SUBTOTAL_FUNC_NONE && rSubState.eRowForce != SUBTOTAL_FUNC_NONE &&
381 rSubState.eColForce != rSubState.eRowForce )
382 return;
383 if ( rSubState.eColForce != SUBTOTAL_FUNC_NONE ) eFunc = rSubState.eColForce;
384 if ( rSubState.eRowForce != SUBTOTAL_FUNC_NONE ) eFunc = rSubState.eRowForce;
386 if ( eFunc == SUBTOTAL_FUNC_NONE )
387 return;
389 if ( eFunc != SUBTOTAL_FUNC_CNT2 ) // CNT2 counts everything, incl. strings and errors
391 if (rNext.meType == ScDPValue::Error)
393 nCount = -1; // -1 for error (not for CNT2)
394 return;
396 if (rNext.meType == ScDPValue::String)
397 return; // ignore
400 ++nCount; // for all functions
402 switch (eFunc)
404 case SUBTOTAL_FUNC_SUM:
405 case SUBTOTAL_FUNC_AVE:
406 if ( !SubTotal::SafePlus( fVal, rNext.mfValue ) )
407 nCount = -1; // -1 for error
408 break;
409 case SUBTOTAL_FUNC_PROD:
410 if ( nCount == 1 ) // copy first value (fVal is initialized to 0)
411 fVal = rNext.mfValue;
412 else if ( !SubTotal::SafeMult( fVal, rNext.mfValue ) )
413 nCount = -1; // -1 for error
414 break;
415 case SUBTOTAL_FUNC_CNT:
416 case SUBTOTAL_FUNC_CNT2:
417 // nothing more than incrementing nCount
418 break;
419 case SUBTOTAL_FUNC_MAX:
420 if ( nCount == 1 || rNext.mfValue > fVal )
421 fVal = rNext.mfValue;
422 break;
423 case SUBTOTAL_FUNC_MIN:
424 if ( nCount == 1 || rNext.mfValue < fVal )
425 fVal = rNext.mfValue;
426 break;
427 case SUBTOTAL_FUNC_STD:
428 case SUBTOTAL_FUNC_STDP:
429 case SUBTOTAL_FUNC_VAR:
430 case SUBTOTAL_FUNC_VARP:
432 // fAux is used to sum up squares
433 if ( !SubTotal::SafePlus( fVal, rNext.mfValue ) )
434 nCount = -1; // -1 for error
435 double fAdd = rNext.mfValue;
436 if ( !SubTotal::SafeMult( fAdd, rNext.mfValue ) ||
437 !SubTotal::SafePlus( fAux, fAdd ) )
438 nCount = -1; // -1 for error
440 break;
441 default:
442 OSL_FAIL("invalid function");
446 void ScDPAggData::Calculate( ScSubTotalFunc eFunc, const ScDPSubTotalState& rSubState )
448 // calculate the original result
449 // (without reference value, used as the basis for reference value calculation)
451 // called several times at the cross-section of several subtotals - don't calculate twice then
452 if ( IsCalculated() )
453 return;
455 if ( rSubState.eColForce != SUBTOTAL_FUNC_NONE ) eFunc = rSubState.eColForce;
456 if ( rSubState.eRowForce != SUBTOTAL_FUNC_NONE ) eFunc = rSubState.eRowForce;
458 if ( eFunc == SUBTOTAL_FUNC_NONE ) // this happens when there is no data dimension
460 nCount = SC_DPAGG_RESULT_EMPTY; // make sure there's a valid state for HasData etc.
461 return;
464 // check the error conditions for the selected function
466 bool bError = false;
467 switch (eFunc)
469 case SUBTOTAL_FUNC_SUM:
470 case SUBTOTAL_FUNC_PROD:
471 case SUBTOTAL_FUNC_CNT:
472 case SUBTOTAL_FUNC_CNT2:
473 bError = ( nCount < 0 ); // only real errors
474 break;
476 case SUBTOTAL_FUNC_AVE:
477 case SUBTOTAL_FUNC_MAX:
478 case SUBTOTAL_FUNC_MIN:
479 case SUBTOTAL_FUNC_STDP:
480 case SUBTOTAL_FUNC_VARP:
481 bError = ( nCount <= 0 ); // no data is an error
482 break;
484 case SUBTOTAL_FUNC_STD:
485 case SUBTOTAL_FUNC_VAR:
486 bError = ( nCount < 2 ); // need at least 2 values
487 break;
489 default:
490 OSL_FAIL("invalid function");
493 // calculate the selected function
495 double fResult = 0.0;
496 if ( !bError )
498 switch (eFunc)
500 case SUBTOTAL_FUNC_MAX:
501 case SUBTOTAL_FUNC_MIN:
502 case SUBTOTAL_FUNC_SUM:
503 case SUBTOTAL_FUNC_PROD:
504 // different error conditions are handled above
505 fResult = fVal;
506 break;
508 case SUBTOTAL_FUNC_CNT:
509 case SUBTOTAL_FUNC_CNT2:
510 fResult = nCount;
511 break;
513 case SUBTOTAL_FUNC_AVE:
514 if ( nCount > 0 )
515 fResult = fVal / (double) nCount;
516 break;
518 //TODO: use safe mul for fVal * fVal
520 case SUBTOTAL_FUNC_STD:
521 if ( nCount >= 2 )
522 fResult = sqrt((fAux - fVal*fVal/(double)(nCount)) / (double)(nCount-1));
523 break;
524 case SUBTOTAL_FUNC_VAR:
525 if ( nCount >= 2 )
526 fResult = (fAux - fVal*fVal/(double)(nCount)) / (double)(nCount-1);
527 break;
528 case SUBTOTAL_FUNC_STDP:
529 if ( nCount > 0 )
530 fResult = sqrt((fAux - fVal*fVal/(double)(nCount)) / (double)nCount);
531 break;
532 case SUBTOTAL_FUNC_VARP:
533 if ( nCount > 0 )
534 fResult = (fAux - fVal*fVal/(double)(nCount)) / (double)nCount;
535 break;
536 default:
537 OSL_FAIL("invalid function");
541 bool bEmpty = ( nCount == 0 ); // no data
543 // store the result
544 // Empty is checked first, so empty results are shown empty even for "average" etc.
545 // If these results should be treated as errors in reference value calculations,
546 // a separate state value (EMPTY_ERROR) is needed.
547 // Now, for compatibility, empty "average" results are counted as 0.
549 if ( bEmpty )
550 nCount = SC_DPAGG_RESULT_EMPTY;
551 else if ( bError )
552 nCount = SC_DPAGG_RESULT_ERROR;
553 else
554 nCount = SC_DPAGG_RESULT_VALID;
556 if ( bEmpty || bError )
557 fResult = 0.0; // default, in case the state is later modified
559 fVal = fResult; // used directly from now on
560 fAux = 0.0; // used for running total or original result of reference value
563 bool ScDPAggData::IsCalculated() const
565 return ( nCount <= SC_DPAGG_RESULT_EMPTY );
568 double ScDPAggData::GetResult() const
570 OSL_ENSURE( IsCalculated(), "ScDPAggData not calculated" );
572 return fVal; // use calculated value
575 bool ScDPAggData::HasError() const
577 OSL_ENSURE( IsCalculated(), "ScDPAggData not calculated" );
579 return ( nCount == SC_DPAGG_RESULT_ERROR );
582 bool ScDPAggData::HasData() const
584 OSL_ENSURE( IsCalculated(), "ScDPAggData not calculated" );
586 return ( nCount != SC_DPAGG_RESULT_EMPTY ); // values or error
589 void ScDPAggData::SetResult( double fNew )
591 OSL_ENSURE( IsCalculated(), "ScDPAggData not calculated" );
593 fVal = fNew; // don't reset error flag
596 void ScDPAggData::SetError()
598 OSL_ENSURE( IsCalculated(), "ScDPAggData not calculated" );
600 nCount = SC_DPAGG_RESULT_ERROR;
603 void ScDPAggData::SetEmpty( bool bSet )
605 OSL_ENSURE( IsCalculated(), "ScDPAggData not calculated" );
607 if ( bSet )
608 nCount = SC_DPAGG_RESULT_EMPTY;
609 else
610 nCount = SC_DPAGG_RESULT_VALID;
613 double ScDPAggData::GetAuxiliary() const
615 // after Calculate, fAux is used as auxiliary value for running totals and reference values
616 OSL_ENSURE( IsCalculated(), "ScDPAggData not calculated" );
618 return fAux;
621 void ScDPAggData::SetAuxiliary( double fNew )
623 // after Calculate, fAux is used as auxiliary value for running totals and reference values
624 OSL_ENSURE( IsCalculated(), "ScDPAggData not calculated" );
626 fAux = fNew;
629 ScDPAggData* ScDPAggData::GetChild()
631 if (!pChild)
632 pChild = new ScDPAggData;
633 return pChild;
636 void ScDPAggData::Reset()
638 fVal = 0.0;
639 fAux = 0.0;
640 nCount = SC_DPAGG_EMPTY;
641 delete pChild;
642 pChild = NULL;
645 #if DEBUG_PIVOT_TABLE
646 void ScDPAggData::Dump(int nIndent) const
648 std::string aIndent(nIndent*2, ' ');
649 std::cout << aIndent << "* ";
650 if (IsCalculated())
651 std::cout << GetResult();
652 else
653 std::cout << "not calculated";
655 std::cout << " [val=" << fVal << "; aux=" << fAux << "; count=" << nCount << "]" << std::endl;
657 #endif
659 ScDPRowTotals::ScDPRowTotals() :
660 bIsInColRoot( false )
664 ScDPRowTotals::~ScDPRowTotals()
668 static ScDPAggData* lcl_GetChildTotal( ScDPAggData* pFirst, long nMeasure )
670 OSL_ENSURE( nMeasure >= 0, "GetColTotal: no measure" );
672 ScDPAggData* pAgg = pFirst;
673 long nSkip = nMeasure;
675 // subtotal settings are ignored - column/row totals exist once per measure
677 for ( long nPos=0; nPos<nSkip; nPos++ )
678 pAgg = pAgg->GetChild(); // column total is constructed empty - children need to be created
680 if ( !pAgg->IsCalculated() )
682 // for first use, simulate an empty calculation
683 ScDPSubTotalState aEmptyState;
684 pAgg->Calculate( SUBTOTAL_FUNC_SUM, aEmptyState );
687 return pAgg;
690 ScDPAggData* ScDPRowTotals::GetRowTotal( long nMeasure )
692 return lcl_GetChildTotal( &aRowTotal, nMeasure );
695 ScDPAggData* ScDPRowTotals::GetGrandTotal( long nMeasure )
697 return lcl_GetChildTotal( &aGrandTotal, nMeasure );
700 static ScSubTotalFunc lcl_GetForceFunc( const ScDPLevel* pLevel, long nFuncNo )
702 ScSubTotalFunc eRet = SUBTOTAL_FUNC_NONE;
703 if ( pLevel )
705 //TODO: direct access via ScDPLevel
707 uno::Sequence<sheet::GeneralFunction> aSeq = pLevel->getSubTotals();
708 long nSequence = aSeq.getLength();
709 if ( nSequence && aSeq[0] != sheet::GeneralFunction_AUTO )
711 // For manual subtotals, "automatic" is added as first function.
712 // ScDPResultMember::GetSubTotalCount adds to the count, here NONE has to be
713 // returned as the first function then.
715 --nFuncNo; // keep NONE for first (check below), move the other entries
718 if ( nFuncNo >= 0 && nFuncNo < nSequence )
720 sheet::GeneralFunction eUser = aSeq.getConstArray()[nFuncNo];
721 if (eUser != sheet::GeneralFunction_AUTO)
722 eRet = ScDPUtil::toSubTotalFunc(eUser);
725 return eRet;
728 ScDPResultData::ScDPResultData( ScDPSource& rSrc ) :
729 mrSource(rSrc),
730 bLateInit( false ),
731 bDataAtCol( false ),
732 bDataAtRow( false )
736 ScDPResultData::~ScDPResultData()
738 std::for_each(maDimMembers.begin(), maDimMembers.end(), boost::checked_deleter<ResultMembers>());
741 void ScDPResultData::SetMeasureData(
742 std::vector<ScSubTotalFunc>& rFunctions, std::vector<sheet::DataPilotFieldReference>& rRefs,
743 std::vector<sal_uInt16>& rRefOrient, std::vector<OUString>& rNames )
745 // We need to have at least one measure data at all times.
747 maMeasureFuncs.swap(rFunctions);
748 if (maMeasureFuncs.empty())
749 maMeasureFuncs.push_back(SUBTOTAL_FUNC_NONE);
751 maMeasureRefs.swap(rRefs);
752 if (maMeasureRefs.empty())
753 maMeasureRefs.push_back(sheet::DataPilotFieldReference()); // default ctor is ok.
755 maMeasureRefOrients.swap(rRefOrient);
756 if (maMeasureRefOrients.empty())
757 maMeasureRefOrients.push_back(sheet::DataPilotFieldOrientation_HIDDEN);
759 maMeasureNames.swap(rNames);
760 if (maMeasureNames.empty())
761 maMeasureNames.push_back(ScGlobal::GetRscString(STR_EMPTYDATA));
764 void ScDPResultData::SetDataLayoutOrientation( sal_uInt16 nOrient )
766 bDataAtCol = ( nOrient == sheet::DataPilotFieldOrientation_COLUMN );
767 bDataAtRow = ( nOrient == sheet::DataPilotFieldOrientation_ROW );
770 void ScDPResultData::SetLateInit( bool bSet )
772 bLateInit = bSet;
775 long ScDPResultData::GetColStartMeasure() const
777 if (maMeasureFuncs.size() == 1)
778 return 0;
780 return bDataAtCol ? SC_DPMEASURE_ALL : SC_DPMEASURE_ANY;
783 long ScDPResultData::GetRowStartMeasure() const
785 if (maMeasureFuncs.size() == 1)
786 return 0;
788 return bDataAtRow ? SC_DPMEASURE_ALL : SC_DPMEASURE_ANY;
791 ScSubTotalFunc ScDPResultData::GetMeasureFunction(long nMeasure) const
793 OSL_ENSURE((size_t) nMeasure < maMeasureFuncs.size(), "bumm");
794 return maMeasureFuncs[nMeasure];
797 const sheet::DataPilotFieldReference& ScDPResultData::GetMeasureRefVal(long nMeasure) const
799 OSL_ENSURE((size_t) nMeasure < maMeasureRefs.size(), "bumm");
800 return maMeasureRefs[nMeasure];
803 sal_uInt16 ScDPResultData::GetMeasureRefOrient(long nMeasure) const
805 OSL_ENSURE((size_t) nMeasure < maMeasureRefOrients.size(), "bumm");
806 return maMeasureRefOrients[nMeasure];
809 OUString ScDPResultData::GetMeasureString(long nMeasure, bool bForce, ScSubTotalFunc eForceFunc, bool& rbTotalResult) const
811 // with bForce==true, return function instead of "result" for single measure
812 // with eForceFunc != SUBTOTAL_FUNC_NONE, always use eForceFunc
813 rbTotalResult = false;
814 if ( nMeasure < 0 || (maMeasureFuncs.size() == 1 && !bForce && eForceFunc == SUBTOTAL_FUNC_NONE) )
816 // for user-specified subtotal function with all measures,
817 // display only function name
818 if ( eForceFunc != SUBTOTAL_FUNC_NONE )
819 return ScGlobal::GetRscString(nFuncStrIds[eForceFunc]);
821 rbTotalResult = true;
822 return ScGlobal::GetRscString(STR_TABLE_ERGEBNIS);
824 else
826 OSL_ENSURE((size_t) nMeasure < maMeasureFuncs.size(), "bumm");
827 const ScDPDimension* pDataDim = mrSource.GetDataDimension(nMeasure);
828 if (pDataDim)
830 const OUString* pLayoutName = pDataDim->GetLayoutName();
831 if (pLayoutName)
832 return *pLayoutName;
835 ScSubTotalFunc eFunc = ( eForceFunc == SUBTOTAL_FUNC_NONE ) ?
836 GetMeasureFunction(nMeasure) : eForceFunc;
838 return ScDPUtil::getDisplayedMeasureName(maMeasureNames[nMeasure], eFunc);
842 OUString ScDPResultData::GetMeasureDimensionName(long nMeasure) const
844 if ( nMeasure < 0 )
846 OSL_FAIL("GetMeasureDimensionName: negative");
847 return OUString("***");
850 return mrSource.GetDataDimName(nMeasure);
853 bool ScDPResultData::IsBaseForGroup( long nDim ) const
855 return mrSource.GetData()->IsBaseForGroup(nDim);
858 long ScDPResultData::GetGroupBase( long nGroupDim ) const
860 return mrSource.GetData()->GetGroupBase(nGroupDim);
863 bool ScDPResultData::IsNumOrDateGroup( long nDim ) const
865 return mrSource.GetData()->IsNumOrDateGroup(nDim);
868 bool ScDPResultData::IsInGroup( SCROW nGroupDataId, long nGroupIndex,
869 const ScDPItemData& rBaseData, long nBaseIndex ) const
871 const ScDPItemData* pGroupData = mrSource.GetItemDataById(nGroupIndex , nGroupDataId);
872 if ( pGroupData )
873 return mrSource.GetData()->IsInGroup(*pGroupData, nGroupIndex, rBaseData, nBaseIndex);
874 else
875 return false;
878 bool ScDPResultData::HasCommonElement( SCROW nFirstDataId, long nFirstIndex,
879 const ScDPItemData& rSecondData, long nSecondIndex ) const
881 const ScDPItemData* pFirstData = mrSource.GetItemDataById(nFirstIndex , nFirstDataId);
882 if ( pFirstData )
883 return mrSource.GetData()->HasCommonElement(*pFirstData, nFirstIndex, rSecondData, nSecondIndex);
884 else
885 return false;
888 ResultMembers* ScDPResultData::GetDimResultMembers(long nDim, ScDPDimension* pDim, ScDPLevel* pLevel) const
890 if (nDim < static_cast<long>(maDimMembers.size()) && maDimMembers[nDim])
891 return maDimMembers[nDim];
893 maDimMembers.resize(nDim+1, NULL);
895 ResultMembers* pResultMembers = new ResultMembers();
896 // global order is used to initialize aMembers, so it doesn't have to be looked at later
897 const ScMemberSortOrder& rGlobalOrder = pLevel->GetGlobalOrder();
899 ScDPMembers* pMembers = pLevel->GetMembersObject();
900 long nMembCount = pMembers->getCount();
901 for (long i = 0; i < nMembCount; ++i)
903 long nSorted = rGlobalOrder.empty() ? i : rGlobalOrder[i];
904 ScDPMember* pMember = pMembers->getByIndex(nSorted);
905 if (!pResultMembers->FindMember(pMember->GetItemDataId()))
907 ScDPParentDimData* pNew = new ScDPParentDimData(i, pDim, pLevel, pMember);
908 pResultMembers->InsertMember(pNew);
912 maDimMembers[nDim] = pResultMembers;
913 return maDimMembers[nDim];
916 ScDPResultMember::ScDPResultMember(
917 const ScDPResultData* pData, const ScDPParentDimData& rParentDimData, bool bForceSub ) :
918 pResultData( pData ),
919 aParentDimData( rParentDimData ),
920 pChildDimension( NULL ),
921 pDataRoot( NULL ),
922 bHasElements( false ),
923 bForceSubTotal( bForceSub ),
924 bHasHiddenDetails( false ),
925 bInitialized( false ),
926 bAutoHidden( false ),
927 nMemberStep( 1 )
929 // pParentLevel/pMemberDesc is 0 for root members
932 ScDPResultMember::ScDPResultMember(
933 const ScDPResultData* pData, bool bForceSub ) :
934 pResultData( pData ),
935 pChildDimension( NULL ),
936 pDataRoot( NULL ),
937 bHasElements( false ),
938 bForceSubTotal( bForceSub ),
939 bHasHiddenDetails( false ),
940 bInitialized( false ),
941 bAutoHidden( false ),
942 nMemberStep( 1 )
945 ScDPResultMember::~ScDPResultMember()
947 delete pChildDimension;
948 delete pDataRoot;
951 OUString ScDPResultMember::GetName() const
953 const ScDPMember* pMemberDesc = GetDPMember();
954 if (pMemberDesc)
955 return pMemberDesc->GetNameStr();
956 else
957 return ScGlobal::GetRscString(STR_PIVOT_TOTAL); // root member
960 OUString ScDPResultMember::GetDisplayName() const
962 const ScDPMember* pDPMember = GetDPMember();
963 if (!pDPMember)
964 return OUString();
966 ScDPItemData aItem;
967 pDPMember->FillItemData(aItem);
968 if (aParentDimData.mpParentDim)
970 long nDim = aParentDimData.mpParentDim->GetDimension();
971 return pResultData->GetSource().GetData()->GetFormattedString(nDim, aItem);
974 return aItem.GetString();
977 void ScDPResultMember::FillItemData( ScDPItemData& rData ) const
979 const ScDPMember* pMemberDesc = GetDPMember();
980 if (pMemberDesc)
981 pMemberDesc->FillItemData( rData );
982 else
983 rData.SetString( ScGlobal::GetRscString(STR_PIVOT_TOTAL) ); // root member
986 bool ScDPResultMember::IsNamedItem( SCROW nIndex ) const
988 //TODO: store ScDPMember pointer instead of ScDPMember ???
989 const ScDPMember* pMemberDesc = GetDPMember();
990 if (pMemberDesc)
991 return pMemberDesc->IsNamedItem(nIndex);
992 return false;
995 bool ScDPResultMember::IsValidEntry( const vector< SCROW >& aMembers ) const
997 if ( !IsValid() )
998 return false;
1000 const ScDPResultDimension* pChildDim = GetChildDimension();
1001 if (pChildDim)
1003 if (aMembers.size() < 2)
1004 return false;
1006 vector<SCROW>::const_iterator itr = aMembers.begin();
1007 vector<SCROW> aChildMembers(++itr, aMembers.end());
1008 return pChildDim->IsValidEntry(aChildMembers);
1010 else
1011 return true;
1014 void ScDPResultMember::InitFrom( const vector<ScDPDimension*>& ppDim, const vector<ScDPLevel*>& ppLev,
1015 size_t nPos, ScDPInitState& rInitState ,
1016 bool bInitChild )
1018 // with LateInit, initialize only those members that have data
1019 if ( pResultData->IsLateInit() )
1020 return;
1022 bInitialized = true;
1024 if (nPos >= ppDim.size())
1025 return;
1027 // skip child dimension if details are not shown
1028 if ( GetDPMember() && !GetDPMember()->getShowDetails() )
1030 // Show DataLayout dimension
1031 nMemberStep = 1;
1032 while ( nPos < ppDim.size() )
1034 if ( ppDim[nPos]->getIsDataLayoutDimension() )
1036 if ( !pChildDimension )
1037 pChildDimension = new ScDPResultDimension( pResultData );
1038 pChildDimension->InitFrom( ppDim, ppLev, nPos, rInitState , false );
1039 return;
1041 else
1042 { //find next dim
1043 nPos ++;
1044 nMemberStep ++;
1047 bHasHiddenDetails = true; // only if there is a next dimension
1048 return;
1051 if ( bInitChild )
1053 pChildDimension = new ScDPResultDimension( pResultData );
1054 pChildDimension->InitFrom(ppDim, ppLev, nPos, rInitState, true);
1058 void ScDPResultMember::LateInitFrom(
1059 LateInitParams& rParams, const vector<SCROW>& pItemData, size_t nPos, ScDPInitState& rInitState)
1061 // without LateInit, everything has already been initialized
1062 if ( !pResultData->IsLateInit() )
1063 return;
1065 bInitialized = true;
1067 if ( rParams.IsEnd( nPos ) /*nPos >= ppDim.size()*/)
1068 // No next dimension. Bail out.
1069 return;
1071 // skip child dimension if details are not shown
1072 if ( GetDPMember() && !GetDPMember()->getShowDetails() )
1074 // Show DataLayout dimension
1075 nMemberStep = 1;
1076 while ( !rParams.IsEnd( nPos ) )
1078 if ( rParams.GetDim( nPos )->getIsDataLayoutDimension() )
1080 if ( !pChildDimension )
1081 pChildDimension = new ScDPResultDimension( pResultData );
1083 // #i111462# reset InitChild flag only for this child dimension's LateInitFrom call,
1084 // not for following members of parent dimensions
1085 bool bWasInitChild = rParams.GetInitChild();
1086 rParams.SetInitChild( false );
1087 pChildDimension->LateInitFrom( rParams, pItemData, nPos, rInitState );
1088 rParams.SetInitChild( bWasInitChild );
1089 return;
1091 else
1092 { //find next dim
1093 nPos ++;
1094 nMemberStep ++;
1097 bHasHiddenDetails = true; // only if there is a next dimension
1098 return;
1101 // LateInitFrom is called several times...
1102 if ( rParams.GetInitChild() )
1104 if ( !pChildDimension )
1105 pChildDimension = new ScDPResultDimension( pResultData );
1106 pChildDimension->LateInitFrom( rParams, pItemData, nPos, rInitState );
1110 bool ScDPResultMember::IsSubTotalInTitle(long nMeasure) const
1112 bool bRet = false;
1113 if ( pChildDimension && /*pParentLevel*/GetParentLevel() &&
1114 /*pParentLevel*/GetParentLevel()->IsOutlineLayout() && /*pParentLevel*/GetParentLevel()->IsSubtotalsAtTop() )
1116 long nUserSubStart;
1117 long nSubTotals = GetSubTotalCount( &nUserSubStart );
1118 nSubTotals -= nUserSubStart; // visible count
1119 if ( nSubTotals )
1121 if ( nMeasure == SC_DPMEASURE_ALL )
1122 nSubTotals *= pResultData->GetMeasureCount(); // number of subtotals that will be inserted
1124 // only a single subtotal row will be shown in the outline title row
1125 if ( nSubTotals == 1 )
1126 bRet = true;
1129 return bRet;
1132 long ScDPResultMember::GetSize(long nMeasure) const
1134 if ( !IsVisible() )
1135 return 0;
1136 const ScDPLevel* pParentLevel = GetParentLevel();
1137 long nExtraSpace = 0;
1138 if ( pParentLevel && pParentLevel->IsAddEmpty() )
1139 ++nExtraSpace;
1141 if ( pChildDimension )
1143 // outline layout takes up an extra row for the title only if subtotals aren't shown in that row
1144 if ( pParentLevel && pParentLevel->IsOutlineLayout() && !IsSubTotalInTitle( nMeasure ) )
1145 ++nExtraSpace;
1147 long nSize = pChildDimension->GetSize(nMeasure);
1148 long nUserSubStart;
1149 long nUserSubCount = GetSubTotalCount( &nUserSubStart );
1150 nUserSubCount -= nUserSubStart; // for output size, use visible count
1151 if ( nUserSubCount )
1153 if ( nMeasure == SC_DPMEASURE_ALL )
1154 nSize += pResultData->GetMeasureCount() * nUserSubCount;
1155 else
1156 nSize += nUserSubCount;
1158 return nSize + nExtraSpace;
1160 else
1162 if ( nMeasure == SC_DPMEASURE_ALL )
1163 return pResultData->GetMeasureCount() + nExtraSpace;
1164 else
1165 return 1 + nExtraSpace;
1169 bool ScDPResultMember::IsVisible() const
1171 if (!bInitialized)
1172 return false;
1174 if (!IsValid())
1175 return false;
1177 if (bHasElements)
1178 return true;
1180 // not initialized -> shouldn't be there at all
1181 // (allocated only to preserve ordering)
1182 const ScDPLevel* pParentLevel = GetParentLevel();
1184 return (pParentLevel && pParentLevel->getShowEmpty());
1187 bool ScDPResultMember::IsValid() const
1189 // non-Valid members are left out of calculation
1191 // was member set no invisible at the DataPilotSource?
1192 const ScDPMember* pMemberDesc = GetDPMember();
1193 if ( pMemberDesc && !pMemberDesc->isVisible() )
1194 return false;
1196 if ( bAutoHidden )
1197 return false;
1199 return true;
1202 long ScDPResultMember::GetSubTotalCount( long* pUserSubStart ) const
1204 if ( pUserSubStart )
1205 *pUserSubStart = 0; // default
1207 const ScDPLevel* pParentLevel = GetParentLevel();
1209 if ( bForceSubTotal ) // set if needed for root members
1210 return 1; // grand total is always "automatic"
1211 else if ( pParentLevel )
1213 //TODO: direct access via ScDPLevel
1215 uno::Sequence<sheet::GeneralFunction> aSeq = pParentLevel->getSubTotals();
1216 long nSequence = aSeq.getLength();
1217 if ( nSequence && aSeq[0] != sheet::GeneralFunction_AUTO )
1219 // For manual subtotals, always add "automatic" as first function
1220 // (used for calculation, but not for display, needed for sorting, see lcl_GetForceFunc)
1222 ++nSequence;
1223 if ( pUserSubStart )
1224 *pUserSubStart = 1; // visible subtotals start at 1
1226 return nSequence;
1228 else
1229 return 0;
1232 void ScDPResultMember::ProcessData( const vector< SCROW >& aChildMembers, const ScDPResultDimension* pDataDim,
1233 const vector< SCROW >& aDataMembers, const vector<ScDPValue>& aValues )
1235 SetHasElements();
1237 if (pChildDimension)
1238 pChildDimension->ProcessData( aChildMembers, pDataDim, aDataMembers, aValues );
1240 if ( !pDataRoot )
1242 pDataRoot = new ScDPDataMember( pResultData, NULL );
1243 if ( pDataDim )
1244 pDataRoot->InitFrom( pDataDim ); // recursive
1247 ScDPSubTotalState aSubState; // initial state
1249 long nUserSubCount = GetSubTotalCount();
1251 // Calculate at least automatic if no subtotals are selected,
1252 // show only own values if there's no child dimension (innermost).
1253 if ( !nUserSubCount || !pChildDimension )
1254 nUserSubCount = 1;
1256 const ScDPLevel* pParentLevel = GetParentLevel();
1258 for (long nUserPos=0; nUserPos<nUserSubCount; nUserPos++) // including hidden "automatic"
1260 // #i68338# if nUserSubCount is 1 (automatic only), don't set nRowSubTotalFunc
1261 if ( pChildDimension && nUserSubCount > 1 )
1263 aSubState.nRowSubTotalFunc = nUserPos;
1264 aSubState.eRowForce = lcl_GetForceFunc( pParentLevel, nUserPos );
1267 pDataRoot->ProcessData( aDataMembers, aValues, aSubState );
1272 * Parse subtotal string and replace all occurrences of '?' with the caption
1273 * string. Do ensure that escaped characters are not translated.
1275 static OUString lcl_parseSubtotalName(const OUString& rSubStr, const OUString& rCaption)
1277 OUStringBuffer aNewStr;
1278 sal_Int32 n = rSubStr.getLength();
1279 bool bEscaped = false;
1280 for (sal_Int32 i = 0; i < n; ++i)
1282 sal_Unicode c = rSubStr[i];
1283 if (!bEscaped && c == '\\')
1285 bEscaped = true;
1286 continue;
1289 if (!bEscaped && c == '?')
1290 aNewStr.append(rCaption);
1291 else
1292 aNewStr.append(c);
1293 bEscaped = false;
1295 return aNewStr.makeStringAndClear();
1298 void ScDPResultMember::FillMemberResults(
1299 uno::Sequence<sheet::MemberResult>* pSequences, long& rPos, long nMeasure, bool bRoot,
1300 const OUString* pMemberName, const OUString* pMemberCaption )
1302 // IsVisible() test is in ScDPResultDimension::FillMemberResults
1303 // (not on data layout dimension)
1305 if (!pSequences->getLength())
1306 // empty sequence. Bail out.
1307 return;
1309 long nSize = GetSize(nMeasure);
1310 sheet::MemberResult* pArray = pSequences->getArray();
1311 OSL_ENSURE( rPos+nSize <= pSequences->getLength(), "bumm" );
1313 bool bIsNumeric = false;
1314 OUString aName;
1315 if ( pMemberName ) // if pMemberName != NULL, use instead of real member name
1317 aName = *pMemberName;
1319 else
1321 ScDPItemData aItemData;
1322 FillItemData( aItemData );
1323 if (aParentDimData.mpParentDim)
1325 long nDim = aParentDimData.mpParentDim->GetDimension();
1326 aName = pResultData->GetSource().GetData()->GetFormattedString(nDim, aItemData);
1328 else
1330 long nDim = -1;
1331 const ScDPMember* pMem = GetDPMember();
1332 if (pMem)
1333 nDim = pMem->GetDim();
1334 aName = pResultData->GetSource().GetData()->GetFormattedString(nDim, aItemData);
1337 ScDPItemData::Type eType = aItemData.GetType();
1338 bIsNumeric = eType == ScDPItemData::Value || eType == ScDPItemData::GroupValue;
1341 const ScDPDimension* pParentDim = GetParentDim();
1342 if ( bIsNumeric && pParentDim && pResultData->IsNumOrDateGroup( pParentDim->GetDimension() ) )
1344 // Numeric group dimensions use numeric entries for proper sorting,
1345 // but the group titles must be output as text.
1346 bIsNumeric = false;
1349 OUString aCaption = aName;
1350 const ScDPMember* pMemberDesc = GetDPMember();
1351 if (pMemberDesc)
1353 const OUString* pLayoutName = pMemberDesc->GetLayoutName();
1354 if (pLayoutName)
1356 aCaption = *pLayoutName;
1357 bIsNumeric = false; // layout name is always non-numeric.
1361 if ( pMemberCaption ) // use pMemberCaption if != NULL
1362 aCaption = *pMemberCaption;
1363 if (aCaption.isEmpty())
1364 aCaption = ScGlobal::GetRscString(STR_EMPTYDATA);
1366 if (bIsNumeric)
1367 pArray[rPos].Flags |= sheet::MemberResultFlags::NUMERIC;
1368 else
1369 pArray[rPos].Flags &= ~sheet::MemberResultFlags::NUMERIC;
1371 const ScDPLevel* pParentLevel = GetParentLevel();
1372 if ( nSize && !bRoot ) // root is overwritten by first dimension
1374 pArray[rPos].Name = aName;
1375 pArray[rPos].Caption = aCaption;
1376 pArray[rPos].Flags |= sheet::MemberResultFlags::HASMEMBER;
1378 // set "continue" flag (removed for subtotals later)
1379 for (long i=1; i<nSize; i++)
1380 pArray[rPos+i].Flags |= sheet::MemberResultFlags::CONTINUE;
1381 if ( pParentLevel && pParentLevel->getRepeatItemLabels() )
1383 long nSizeNonEmpty = nSize;
1384 if ( pParentLevel->IsAddEmpty() )
1385 --nSizeNonEmpty;
1386 for (long i=1; i<nSizeNonEmpty; i++)
1388 pArray[rPos+i].Name = aName;
1389 pArray[rPos+i].Caption = aCaption;
1390 pArray[rPos+i].Flags |= sheet::MemberResultFlags::HASMEMBER;
1395 long nExtraSpace = 0;
1396 if ( pParentLevel && pParentLevel->IsAddEmpty() )
1397 ++nExtraSpace;
1399 bool bTitleLine = false;
1400 if ( pParentLevel && pParentLevel->IsOutlineLayout() )
1401 bTitleLine = true;
1403 // if the subtotals are shown at the top (title row) in outline layout,
1404 // no extra row for the subtotals is needed
1405 bool bSubTotalInTitle = IsSubTotalInTitle( nMeasure );
1407 bool bHasChild = ( pChildDimension != NULL );
1408 if (bHasChild)
1410 if ( bTitleLine ) // in tabular layout the title is on a separate row
1411 ++rPos; // -> fill child dimension one row below
1413 if (bRoot) // same sequence for root member
1414 pChildDimension->FillMemberResults( pSequences, rPos, nMeasure );
1415 else
1416 pChildDimension->FillMemberResults( pSequences + nMemberStep/*1*/, rPos, nMeasure );
1418 if ( bTitleLine ) // title row is included in GetSize, so the following
1419 --rPos; // positions are calculated with the normal values
1422 rPos += nSize;
1424 long nUserSubStart;
1425 long nUserSubCount = GetSubTotalCount(&nUserSubStart);
1426 if ( nUserSubCount && pChildDimension && !bSubTotalInTitle )
1428 long nMemberMeasure = nMeasure;
1429 long nSubSize = pResultData->GetCountForMeasure(nMeasure);
1431 rPos -= nSubSize * (nUserSubCount - nUserSubStart); // GetSize includes space for SubTotal
1432 rPos -= nExtraSpace; // GetSize includes the empty line
1434 for (long nUserPos=nUserSubStart; nUserPos<nUserSubCount; nUserPos++)
1436 for ( long nSubCount=0; nSubCount<nSubSize; nSubCount++ )
1438 if ( nMeasure == SC_DPMEASURE_ALL )
1439 nMemberMeasure = nSubCount;
1441 ScSubTotalFunc eForce = SUBTOTAL_FUNC_NONE;
1442 if (bHasChild)
1443 eForce = lcl_GetForceFunc( pParentLevel, nUserPos );
1445 bool bTotalResult = false;
1446 OUString aSubStr = aCaption + " " + pResultData->GetMeasureString(nMemberMeasure, false, eForce, bTotalResult);
1448 if (bTotalResult)
1450 if (pMemberDesc)
1452 // single data field layout.
1453 const OUString* pSubtotalName = pParentDim->GetSubtotalName();
1454 if (pSubtotalName)
1455 aSubStr = lcl_parseSubtotalName(*pSubtotalName, aCaption);
1456 pArray[rPos].Flags &= ~sheet::MemberResultFlags::GRANDTOTAL;
1458 else
1460 // root member - subtotal (grand total?) for multi-data field layout.
1461 const OUString* pGrandTotalName = pResultData->GetSource().GetGrandTotalName();
1462 if (pGrandTotalName)
1463 aSubStr = *pGrandTotalName;
1464 pArray[rPos].Flags |= sheet::MemberResultFlags::GRANDTOTAL;
1468 pArray[rPos].Name = aName;
1469 pArray[rPos].Caption = aSubStr;
1470 pArray[rPos].Flags = ( pArray[rPos].Flags |
1471 ( sheet::MemberResultFlags::HASMEMBER | sheet::MemberResultFlags::SUBTOTAL) ) &
1472 ~sheet::MemberResultFlags::CONTINUE;
1474 if ( nMeasure == SC_DPMEASURE_ALL )
1476 // data layout dimension is (direct/indirect) child of this.
1477 // data layout dimension must have name for all entries.
1479 uno::Sequence<sheet::MemberResult>* pLayoutSeq = pSequences;
1480 if (!bRoot)
1481 ++pLayoutSeq;
1482 ScDPResultDimension* pLayoutDim = pChildDimension;
1483 while ( pLayoutDim && !pLayoutDim->IsDataLayout() )
1485 pLayoutDim = pLayoutDim->GetFirstChildDimension();
1486 ++pLayoutSeq;
1488 if ( pLayoutDim )
1490 sheet::MemberResult* pLayoutArray = pLayoutSeq->getArray();
1491 pLayoutArray[rPos].Name = pResultData->GetMeasureDimensionName(nMemberMeasure);
1495 rPos += 1;
1499 rPos += nExtraSpace; // add again (subtracted above)
1503 void ScDPResultMember::FillDataResults(
1504 const ScDPResultMember* pRefMember,
1505 ScDPResultFilterContext& rFilterCxt, uno::Sequence<uno::Sequence<sheet::DataResult> >& rSequence,
1506 long nMeasure) const
1508 boost::scoped_ptr<FilterStack> pFilterStack;
1509 const ScDPMember* pDPMember = GetDPMember();
1510 if (pDPMember)
1512 // Root result has no corresponding DP member. Only take the non-root results.
1513 OUString aMemStr = GetDisplayName();
1514 pFilterStack.reset(new FilterStack(rFilterCxt.maFilters));
1515 pFilterStack->pushDimValue(aMemStr);
1518 // IsVisible() test is in ScDPResultDimension::FillDataResults
1519 // (not on data layout dimension)
1520 const ScDPLevel* pParentLevel = GetParentLevel();
1521 long nStartRow = rFilterCxt.mnRow;
1523 long nExtraSpace = 0;
1524 if ( pParentLevel && pParentLevel->IsAddEmpty() )
1525 ++nExtraSpace;
1527 bool bTitleLine = false;
1528 if ( pParentLevel && pParentLevel->IsOutlineLayout() )
1529 bTitleLine = true;
1531 bool bSubTotalInTitle = IsSubTotalInTitle( nMeasure );
1533 bool bHasChild = ( pChildDimension != NULL );
1534 if (bHasChild)
1536 if ( bTitleLine ) // in tabular layout the title is on a separate row
1537 ++rFilterCxt.mnRow; // -> fill child dimension one row below
1539 long nOldRow = rFilterCxt.mnRow;
1540 pChildDimension->FillDataResults(pRefMember, rFilterCxt, rSequence, nMeasure);
1541 rFilterCxt.mnRow = nOldRow; // Revert to the original row before the call.
1543 rFilterCxt.mnRow += GetSize( nMeasure );
1545 if ( bTitleLine ) // title row is included in GetSize, so the following
1546 --rFilterCxt.mnRow; // positions are calculated with the normal values
1549 long nUserSubStart;
1550 long nUserSubCount = GetSubTotalCount(&nUserSubStart);
1551 if ( nUserSubCount || !bHasChild )
1553 // Calculate at least automatic if no subtotals are selected,
1554 // show only own values if there's no child dimension (innermost).
1555 if ( !nUserSubCount || !bHasChild )
1557 nUserSubCount = 1;
1558 nUserSubStart = 0;
1561 long nMemberMeasure = nMeasure;
1562 long nSubSize = pResultData->GetCountForMeasure(nMeasure);
1563 if (bHasChild)
1565 rFilterCxt.mnRow -= nSubSize * ( nUserSubCount - nUserSubStart ); // GetSize includes space for SubTotal
1566 rFilterCxt.mnRow -= nExtraSpace; // GetSize includes the empty line
1569 long nMoveSubTotal = 0;
1570 if ( bSubTotalInTitle )
1572 nMoveSubTotal = rFilterCxt.mnRow - nStartRow; // force to first (title) row
1573 rFilterCxt.mnRow = nStartRow;
1576 if ( pDataRoot )
1578 ScDPSubTotalState aSubState; // initial state
1580 for (long nUserPos=nUserSubStart; nUserPos<nUserSubCount; nUserPos++)
1582 if ( bHasChild && nUserSubCount > 1 )
1584 aSubState.nRowSubTotalFunc = nUserPos;
1585 aSubState.eRowForce = lcl_GetForceFunc( /*pParentLevel*/GetParentLevel() , nUserPos );
1588 for ( long nSubCount=0; nSubCount<nSubSize; nSubCount++ )
1590 if ( nMeasure == SC_DPMEASURE_ALL )
1591 nMemberMeasure = nSubCount;
1592 else if ( pResultData->GetColStartMeasure() == SC_DPMEASURE_ALL )
1593 nMemberMeasure = SC_DPMEASURE_ALL;
1595 OSL_ENSURE( rFilterCxt.mnRow < rSequence.getLength(), "bumm" );
1596 uno::Sequence<sheet::DataResult>& rSubSeq = rSequence.getArray()[rFilterCxt.mnRow];
1597 rFilterCxt.mnCol = 0;
1598 if (pRefMember->IsVisible())
1599 pDataRoot->FillDataRow(pRefMember, rFilterCxt, rSubSeq, nMemberMeasure, bHasChild, aSubState);
1601 rFilterCxt.mnRow += 1;
1605 else
1606 rFilterCxt.mnRow += nSubSize * ( nUserSubCount - nUserSubStart ); // empty rows occur when ShowEmpty is true
1608 // add extra space again if subtracted from GetSize above,
1609 // add to own size if no children
1610 rFilterCxt.mnRow += nExtraSpace;
1611 rFilterCxt.mnRow += nMoveSubTotal;
1615 void ScDPResultMember::UpdateDataResults( const ScDPResultMember* pRefMember, long nMeasure ) const
1617 // IsVisible() test is in ScDPResultDimension::FillDataResults
1618 // (not on data layout dimension)
1620 bool bHasChild = ( pChildDimension != NULL );
1622 long nUserSubCount = GetSubTotalCount();
1624 // process subtotals even if not shown
1626 // Calculate at least automatic if no subtotals are selected,
1627 // show only own values if there's no child dimension (innermost).
1628 if (!nUserSubCount || !bHasChild)
1629 nUserSubCount = 1;
1631 long nMemberMeasure = nMeasure;
1632 long nSubSize = pResultData->GetCountForMeasure(nMeasure);
1634 if (pDataRoot)
1636 ScDPSubTotalState aSubState; // initial state
1638 for (long nUserPos = 0; nUserPos < nUserSubCount; ++nUserPos) // including hidden "automatic"
1640 if (bHasChild && nUserSubCount > 1)
1642 aSubState.nRowSubTotalFunc = nUserPos;
1643 aSubState.eRowForce = lcl_GetForceFunc(GetParentLevel(), nUserPos);
1646 for (long nSubCount = 0; nSubCount < nSubSize; ++nSubCount)
1648 if (nMeasure == SC_DPMEASURE_ALL)
1649 nMemberMeasure = nSubCount;
1650 else if (pResultData->GetColStartMeasure() == SC_DPMEASURE_ALL)
1651 nMemberMeasure = SC_DPMEASURE_ALL;
1653 pDataRoot->UpdateDataRow(pRefMember, nMemberMeasure, bHasChild, aSubState);
1658 if (bHasChild) // child dimension must be processed last, so the column total is known
1660 pChildDimension->UpdateDataResults( pRefMember, nMeasure );
1664 void ScDPResultMember::SortMembers( ScDPResultMember* pRefMember )
1666 bool bHasChild = ( pChildDimension != NULL );
1667 if (bHasChild)
1668 pChildDimension->SortMembers( pRefMember ); // sorting is done at the dimension
1670 if ( IsRoot() && pDataRoot )
1672 // use the row root member to sort columns
1673 // sub total count is always 1
1675 pDataRoot->SortMembers( pRefMember );
1679 void ScDPResultMember::DoAutoShow( ScDPResultMember* pRefMember )
1681 bool bHasChild = ( pChildDimension != NULL );
1682 if (bHasChild)
1683 pChildDimension->DoAutoShow( pRefMember ); // sorting is done at the dimension
1685 if ( IsRoot()&& pDataRoot )
1687 // use the row root member to sort columns
1688 // sub total count is always 1
1690 pDataRoot->DoAutoShow( pRefMember );
1694 void ScDPResultMember::ResetResults()
1696 if (pDataRoot)
1697 pDataRoot->ResetResults();
1699 if (pChildDimension)
1700 pChildDimension->ResetResults();
1703 void ScDPResultMember::UpdateRunningTotals( const ScDPResultMember* pRefMember, long nMeasure,
1704 ScDPRunningTotalState& rRunning, ScDPRowTotals& rTotals ) const
1706 // IsVisible() test is in ScDPResultDimension::FillDataResults
1707 // (not on data layout dimension)
1709 rTotals.SetInColRoot( IsRoot() );
1711 bool bHasChild = ( pChildDimension != NULL );
1713 long nUserSubCount = GetSubTotalCount();
1714 //if ( nUserSubCount || !bHasChild )
1716 // Calculate at least automatic if no subtotals are selected,
1717 // show only own values if there's no child dimension (innermost).
1718 if ( !nUserSubCount || !bHasChild )
1719 nUserSubCount = 1;
1721 long nMemberMeasure = nMeasure;
1722 long nSubSize = pResultData->GetCountForMeasure(nMeasure);
1724 if ( pDataRoot )
1726 ScDPSubTotalState aSubState; // initial state
1728 for (long nUserPos=0; nUserPos<nUserSubCount; nUserPos++) // including hidden "automatic"
1730 if ( bHasChild && nUserSubCount > 1 )
1732 aSubState.nRowSubTotalFunc = nUserPos;
1733 aSubState.eRowForce = lcl_GetForceFunc(GetParentLevel(), nUserPos);
1736 for ( long nSubCount=0; nSubCount<nSubSize; nSubCount++ )
1738 if ( nMeasure == SC_DPMEASURE_ALL )
1739 nMemberMeasure = nSubCount;
1740 else if ( pResultData->GetColStartMeasure() == SC_DPMEASURE_ALL )
1741 nMemberMeasure = SC_DPMEASURE_ALL;
1743 if (pRefMember->IsVisible())
1744 pDataRoot->UpdateRunningTotals(
1745 pRefMember, nMemberMeasure, bHasChild, aSubState, rRunning, rTotals, *this);
1751 if (bHasChild) // child dimension must be processed last, so the column total is known
1753 pChildDimension->UpdateRunningTotals( pRefMember, nMeasure, rRunning, rTotals );
1757 #if DEBUG_PIVOT_TABLE
1758 void ScDPResultMember::DumpState( const ScDPResultMember* pRefMember, ScDocument* pDoc, ScAddress& rPos ) const
1760 lcl_DumpRow( OUString("ScDPResultMember"), GetName(), NULL, pDoc, rPos );
1761 SCROW nStartRow = rPos.Row();
1763 if (pDataRoot)
1764 pDataRoot->DumpState( pRefMember, pDoc, rPos );
1766 if (pChildDimension)
1767 pChildDimension->DumpState( pRefMember, pDoc, rPos );
1769 lcl_Indent( pDoc, nStartRow, rPos );
1772 void ScDPResultMember::Dump(int nIndent) const
1774 std::string aIndent(nIndent*2, ' ');
1775 std::cout << aIndent << "-- result member '" << GetName() << "'" << std::endl;
1777 std::cout << aIndent << " column totals" << std::endl;
1778 for (const ScDPAggData* p = &aColTotal; p; p = p->GetExistingChild())
1779 p->Dump(nIndent+1);
1781 if (pChildDimension)
1782 pChildDimension->Dump(nIndent+1);
1784 if (pDataRoot)
1786 std::cout << aIndent << " data root" << std::endl;
1787 pDataRoot->Dump(nIndent+1);
1790 #endif
1792 ScDPAggData* ScDPResultMember::GetColTotal( long nMeasure ) const
1794 return lcl_GetChildTotal( const_cast<ScDPAggData*>(&aColTotal), nMeasure );
1797 void ScDPResultMember::FillVisibilityData(ScDPResultVisibilityData& rData) const
1799 if (pChildDimension)
1800 pChildDimension->FillVisibilityData(rData);
1803 ScDPDataMember::ScDPDataMember( const ScDPResultData* pData, const ScDPResultMember* pRes ) :
1804 pResultData( pData ),
1805 pResultMember( pRes ),
1806 pChildDimension( NULL )
1808 // pResultMember is 0 for root members
1811 ScDPDataMember::~ScDPDataMember()
1813 delete pChildDimension;
1816 OUString ScDPDataMember::GetName() const
1818 if (pResultMember)
1819 return pResultMember->GetName();
1820 else
1821 return EMPTY_OUSTRING;
1824 bool ScDPDataMember::IsVisible() const
1826 if (pResultMember)
1827 return pResultMember->IsVisible();
1828 else
1829 return false;
1832 bool ScDPDataMember::IsNamedItem( SCROW nRow ) const
1834 if (pResultMember)
1835 return pResultMember->IsNamedItem(nRow);
1836 else
1837 return false;
1840 bool ScDPDataMember::HasHiddenDetails() const
1842 if (pResultMember)
1843 return pResultMember->HasHiddenDetails();
1844 else
1845 return false;
1848 void ScDPDataMember::InitFrom( const ScDPResultDimension* pDim )
1850 if ( !pChildDimension )
1851 pChildDimension = new ScDPDataDimension(pResultData);
1852 pChildDimension->InitFrom(pDim);
1855 const long SC_SUBTOTALPOS_AUTO = -1; // default
1856 const long SC_SUBTOTALPOS_SKIP = -2; // don't use
1858 static long lcl_GetSubTotalPos( const ScDPSubTotalState& rSubState )
1860 if ( rSubState.nColSubTotalFunc >= 0 && rSubState.nRowSubTotalFunc >= 0 &&
1861 rSubState.nColSubTotalFunc != rSubState.nRowSubTotalFunc )
1863 // #i68338# don't return the same index for different combinations (leading to repeated updates),
1864 // return a "don't use" value instead
1866 return SC_SUBTOTALPOS_SKIP;
1869 long nRet = SC_SUBTOTALPOS_AUTO;
1870 if ( rSubState.nColSubTotalFunc >= 0 ) nRet = rSubState.nColSubTotalFunc;
1871 if ( rSubState.nRowSubTotalFunc >= 0 ) nRet = rSubState.nRowSubTotalFunc;
1872 return nRet;
1875 void ScDPDataMember::UpdateValues( const vector<ScDPValue>& aValues, const ScDPSubTotalState& rSubState )
1877 //TODO: find out how many and which subtotals are used
1879 ScDPAggData* pAgg = &aAggregate;
1881 long nSubPos = lcl_GetSubTotalPos(rSubState);
1882 if (nSubPos == SC_SUBTOTALPOS_SKIP)
1883 return;
1884 if (nSubPos > 0)
1886 long nSkip = nSubPos * pResultData->GetMeasureCount();
1887 for (long i=0; i<nSkip; i++)
1888 pAgg = pAgg->GetChild(); // created if not there
1891 size_t nCount = aValues.size();
1892 for (size_t nPos = 0; nPos < nCount; ++nPos)
1894 pAgg->Update(aValues[nPos], pResultData->GetMeasureFunction(nPos), rSubState);
1895 pAgg = pAgg->GetChild();
1899 void ScDPDataMember::ProcessData( const vector< SCROW >& aChildMembers, const vector<ScDPValue>& aValues,
1900 const ScDPSubTotalState& rSubState )
1902 if ( pResultData->IsLateInit() && !pChildDimension && pResultMember && pResultMember->GetChildDimension() )
1904 // if this DataMember doesn't have a child dimension because the ResultMember's
1905 // child dimension wasn't there yet during this DataMembers's creation,
1906 // create the child dimension now
1907 InitFrom( pResultMember->GetChildDimension() );
1910 long nUserSubCount = pResultMember ? pResultMember->GetSubTotalCount() : 0;
1912 // Calculate at least automatic if no subtotals are selected,
1913 // show only own values if there's no child dimension (innermost).
1914 if ( !nUserSubCount || !pChildDimension )
1915 nUserSubCount = 1;
1917 ScDPSubTotalState aLocalSubState = rSubState; // keep row state, modify column
1918 for (long nUserPos=0; nUserPos<nUserSubCount; nUserPos++) // including hidden "automatic"
1920 if ( pChildDimension && nUserSubCount > 1 )
1922 const ScDPLevel* pForceLevel = pResultMember ? pResultMember->GetParentLevel() : NULL;
1923 aLocalSubState.nColSubTotalFunc = nUserPos;
1924 aLocalSubState.eColForce = lcl_GetForceFunc( pForceLevel, nUserPos );
1927 UpdateValues( aValues, aLocalSubState );
1930 if (pChildDimension)
1931 pChildDimension->ProcessData( aChildMembers, aValues, rSubState ); // with unmodified subtotal state
1934 bool ScDPDataMember::HasData( long nMeasure, const ScDPSubTotalState& rSubState ) const
1936 if ( rSubState.eColForce != SUBTOTAL_FUNC_NONE && rSubState.eRowForce != SUBTOTAL_FUNC_NONE &&
1937 rSubState.eColForce != rSubState.eRowForce )
1938 return false;
1940 // HasData can be different between measures!
1942 const ScDPAggData* pAgg = GetConstAggData( nMeasure, rSubState );
1943 if (!pAgg)
1944 return false; //TODO: error?
1946 return pAgg->HasData();
1949 bool ScDPDataMember::HasError( long nMeasure, const ScDPSubTotalState& rSubState ) const
1951 const ScDPAggData* pAgg = GetConstAggData( nMeasure, rSubState );
1952 if (!pAgg)
1953 return true;
1955 return pAgg->HasError();
1958 double ScDPDataMember::GetAggregate( long nMeasure, const ScDPSubTotalState& rSubState ) const
1960 const ScDPAggData* pAgg = GetConstAggData( nMeasure, rSubState );
1961 if (!pAgg)
1962 return DBL_MAX; //TODO: error?
1964 return pAgg->GetResult();
1967 ScDPAggData* ScDPDataMember::GetAggData( long nMeasure, const ScDPSubTotalState& rSubState )
1969 OSL_ENSURE( nMeasure >= 0, "GetAggData: no measure" );
1971 ScDPAggData* pAgg = &aAggregate;
1972 long nSkip = nMeasure;
1973 long nSubPos = lcl_GetSubTotalPos(rSubState);
1974 if (nSubPos == SC_SUBTOTALPOS_SKIP)
1975 return NULL;
1976 if (nSubPos > 0)
1977 nSkip += nSubPos * pResultData->GetMeasureCount();
1979 for ( long nPos=0; nPos<nSkip; nPos++ )
1980 pAgg = pAgg->GetChild(); //TODO: need to create children here?
1982 return pAgg;
1985 const ScDPAggData* ScDPDataMember::GetConstAggData( long nMeasure, const ScDPSubTotalState& rSubState ) const
1987 OSL_ENSURE( nMeasure >= 0, "GetConstAggData: no measure" );
1989 const ScDPAggData* pAgg = &aAggregate;
1990 long nSkip = nMeasure;
1991 long nSubPos = lcl_GetSubTotalPos(rSubState);
1992 if (nSubPos == SC_SUBTOTALPOS_SKIP)
1993 return NULL;
1994 if (nSubPos > 0)
1995 nSkip += nSubPos * pResultData->GetMeasureCount();
1997 for ( long nPos=0; nPos<nSkip; nPos++ )
1999 pAgg = pAgg->GetExistingChild();
2000 if (!pAgg)
2001 return NULL;
2004 return pAgg;
2007 void ScDPDataMember::FillDataRow(
2008 const ScDPResultMember* pRefMember, ScDPResultFilterContext& rFilterCxt,
2009 uno::Sequence<sheet::DataResult>& rSequence, long nMeasure, bool bIsSubTotalRow,
2010 const ScDPSubTotalState& rSubState) const
2012 boost::scoped_ptr<FilterStack> pFilterStack;
2013 if (pResultMember)
2015 // Topmost data member (pResultMember=NULL) doesn't need to be handled
2016 // since its immediate parent result member is linked to the same
2017 // dimension member.
2018 pFilterStack.reset(new FilterStack(rFilterCxt.maFilters));
2019 pFilterStack->pushDimValue(pResultMember->GetDisplayName());
2022 OSL_ENSURE( pRefMember == pResultMember || !pResultMember, "bla" );
2024 long nStartCol = rFilterCxt.mnCol;
2026 const ScDPDataDimension* pDataChild = GetChildDimension();
2027 const ScDPResultDimension* pRefChild = pRefMember->GetChildDimension();
2029 const ScDPLevel* pRefParentLevel = pRefMember->GetParentLevel();
2031 long nExtraSpace = 0;
2032 if ( pRefParentLevel && pRefParentLevel->IsAddEmpty() )
2033 ++nExtraSpace;
2035 bool bTitleLine = false;
2036 if ( pRefParentLevel && pRefParentLevel->IsOutlineLayout() )
2037 bTitleLine = true;
2039 bool bSubTotalInTitle = pRefMember->IsSubTotalInTitle( nMeasure );
2041 // leave space for children even if the DataMember hasn't been initialized
2042 // (pDataChild is null then, this happens when no values for it are in this row)
2043 bool bHasChild = ( pRefChild != NULL );
2045 if ( bHasChild )
2047 if ( bTitleLine ) // in tabular layout the title is on a separate column
2048 ++rFilterCxt.mnCol; // -> fill child dimension one column below
2050 if ( pDataChild )
2052 long nOldCol = rFilterCxt.mnCol;
2053 pDataChild->FillDataRow(pRefChild, rFilterCxt, rSequence, nMeasure, bIsSubTotalRow, rSubState);
2054 rFilterCxt.mnCol = nOldCol; // Revert to the old column value before the call.
2056 rFilterCxt.mnCol += (sal_uInt16)pRefMember->GetSize( nMeasure );
2058 if ( bTitleLine ) // title column is included in GetSize, so the following
2059 --rFilterCxt.mnCol; // positions are calculated with the normal values
2062 long nUserSubStart;
2063 long nUserSubCount = pRefMember->GetSubTotalCount(&nUserSubStart);
2064 if ( nUserSubCount || !bHasChild )
2066 // Calculate at least automatic if no subtotals are selected,
2067 // show only own values if there's no child dimension (innermost).
2068 if ( !nUserSubCount || !bHasChild )
2070 nUserSubCount = 1;
2071 nUserSubStart = 0;
2074 ScDPSubTotalState aLocalSubState(rSubState); // keep row state, modify column
2076 long nMemberMeasure = nMeasure;
2077 long nSubSize = pResultData->GetCountForMeasure(nMeasure);
2078 if (bHasChild)
2080 rFilterCxt.mnCol -= nSubSize * ( nUserSubCount - nUserSubStart ); // GetSize includes space for SubTotal
2081 rFilterCxt.mnCol -= nExtraSpace; // GetSize includes the empty line
2084 long nMoveSubTotal = 0;
2085 if ( bSubTotalInTitle )
2087 nMoveSubTotal = rFilterCxt.mnCol - nStartCol; // force to first (title) column
2088 rFilterCxt.mnCol = nStartCol;
2091 for (long nUserPos=nUserSubStart; nUserPos<nUserSubCount; nUserPos++)
2093 if ( pChildDimension && nUserSubCount > 1 )
2095 const ScDPLevel* pForceLevel = pResultMember ? pResultMember->GetParentLevel() : NULL;
2096 aLocalSubState.nColSubTotalFunc = nUserPos;
2097 aLocalSubState.eColForce = lcl_GetForceFunc( pForceLevel, nUserPos );
2100 for ( long nSubCount=0; nSubCount<nSubSize; nSubCount++ )
2102 if ( nMeasure == SC_DPMEASURE_ALL )
2103 nMemberMeasure = nSubCount;
2105 OSL_ENSURE( rFilterCxt.mnCol < rSequence.getLength(), "bumm" );
2106 sheet::DataResult& rRes = rSequence.getArray()[rFilterCxt.mnCol];
2108 if ( HasData( nMemberMeasure, aLocalSubState ) )
2110 if ( HasError( nMemberMeasure, aLocalSubState ) )
2112 rRes.Value = 0;
2113 rRes.Flags |= sheet::DataResultFlags::ERROR;
2115 else
2117 rRes.Value = GetAggregate( nMemberMeasure, aLocalSubState );
2118 rRes.Flags |= sheet::DataResultFlags::HASDATA;
2122 if ( bHasChild || bIsSubTotalRow )
2123 rRes.Flags |= sheet::DataResultFlags::SUBTOTAL;
2125 rFilterCxt.maFilterSet.add(rFilterCxt.maFilters, rFilterCxt.mnCol, rFilterCxt.mnRow, rRes.Value);
2126 rFilterCxt.mnCol += 1;
2130 // add extra space again if subtracted from GetSize above,
2131 // add to own size if no children
2132 rFilterCxt.mnCol += nExtraSpace;
2133 rFilterCxt.mnCol += nMoveSubTotal;
2137 void ScDPDataMember::UpdateDataRow(
2138 const ScDPResultMember* pRefMember, long nMeasure, bool bIsSubTotalRow,
2139 const ScDPSubTotalState& rSubState )
2141 OSL_ENSURE( pRefMember == pResultMember || !pResultMember, "bla" );
2143 // Calculate must be called even if not visible (for use as reference value)
2144 const ScDPDataDimension* pDataChild = GetChildDimension();
2145 const ScDPResultDimension* pRefChild = pRefMember->GetChildDimension();
2147 // leave space for children even if the DataMember hasn't been initialized
2148 // (pDataChild is null then, this happens when no values for it are in this row)
2149 bool bHasChild = ( pRefChild != NULL );
2151 // process subtotals even if not shown
2152 long nUserSubCount = pRefMember->GetSubTotalCount();
2154 // Calculate at least automatic if no subtotals are selected,
2155 // show only own values if there's no child dimension (innermost).
2156 if ( !nUserSubCount || !bHasChild )
2157 nUserSubCount = 1;
2159 ScDPSubTotalState aLocalSubState(rSubState); // keep row state, modify column
2161 long nMemberMeasure = nMeasure;
2162 long nSubSize = pResultData->GetCountForMeasure(nMeasure);
2164 for (long nUserPos=0; nUserPos<nUserSubCount; nUserPos++) // including hidden "automatic"
2166 if ( pChildDimension && nUserSubCount > 1 )
2168 const ScDPLevel* pForceLevel = pResultMember ? pResultMember->GetParentLevel() : NULL;
2169 aLocalSubState.nColSubTotalFunc = nUserPos;
2170 aLocalSubState.eColForce = lcl_GetForceFunc( pForceLevel, nUserPos );
2173 for ( long nSubCount=0; nSubCount<nSubSize; nSubCount++ )
2175 if ( nMeasure == SC_DPMEASURE_ALL )
2176 nMemberMeasure = nSubCount;
2178 // update data...
2179 ScDPAggData* pAggData = GetAggData( nMemberMeasure, aLocalSubState );
2180 if (pAggData)
2182 //TODO: aLocalSubState?
2183 ScSubTotalFunc eFunc = pResultData->GetMeasureFunction( nMemberMeasure );
2184 sheet::DataPilotFieldReference aReferenceValue = pResultData->GetMeasureRefVal( nMemberMeasure );
2185 sal_Int32 eRefType = aReferenceValue.ReferenceType;
2187 // calculate the result first - for all members, regardless of reference value
2188 pAggData->Calculate( eFunc, aLocalSubState );
2190 if ( eRefType == sheet::DataPilotFieldReferenceType::ITEM_DIFFERENCE ||
2191 eRefType == sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE ||
2192 eRefType == sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE )
2194 // copy the result into auxiliary value, so differences can be
2195 // calculated in any order
2196 pAggData->SetAuxiliary( pAggData->GetResult() );
2198 // column/row percentage/index is now in UpdateRunningTotals, so it doesn't disturb sorting
2203 if ( bHasChild ) // child dimension must be processed last, so the row total is known
2205 if ( pDataChild )
2206 pDataChild->UpdateDataRow( pRefChild, nMeasure, bIsSubTotalRow, rSubState );
2210 void ScDPDataMember::SortMembers( ScDPResultMember* pRefMember )
2212 OSL_ENSURE( pRefMember == pResultMember || !pResultMember, "bla" );
2214 if ( pRefMember->IsVisible() ) //TODO: here or in ScDPDataDimension ???
2216 ScDPDataDimension* pDataChild = GetChildDimension();
2217 ScDPResultDimension* pRefChild = pRefMember->GetChildDimension();
2218 if ( pRefChild && pDataChild )
2219 pDataChild->SortMembers( pRefChild ); // sorting is done at the dimension
2223 void ScDPDataMember::DoAutoShow( ScDPResultMember* pRefMember )
2225 OSL_ENSURE( pRefMember == pResultMember || !pResultMember, "bla" );
2227 if ( pRefMember->IsVisible() ) //TODO: here or in ScDPDataDimension ???
2229 ScDPDataDimension* pDataChild = GetChildDimension();
2230 ScDPResultDimension* pRefChild = pRefMember->GetChildDimension();
2231 if ( pRefChild && pDataChild )
2232 pDataChild->DoAutoShow( pRefChild ); // sorting is done at the dimension
2236 void ScDPDataMember::ResetResults()
2238 aAggregate.Reset();
2240 ScDPDataDimension* pDataChild = GetChildDimension();
2241 if ( pDataChild )
2242 pDataChild->ResetResults();
2245 void ScDPDataMember::UpdateRunningTotals(
2246 const ScDPResultMember* pRefMember, long nMeasure, bool bIsSubTotalRow,
2247 const ScDPSubTotalState& rSubState, ScDPRunningTotalState& rRunning,
2248 ScDPRowTotals& rTotals, const ScDPResultMember& rRowParent )
2250 OSL_ENSURE( pRefMember == pResultMember || !pResultMember, "bla" );
2252 const ScDPDataDimension* pDataChild = GetChildDimension();
2253 const ScDPResultDimension* pRefChild = pRefMember->GetChildDimension();
2255 bool bIsRoot = ( pResultMember == NULL || pResultMember->GetParentLevel() == NULL );
2257 // leave space for children even if the DataMember hasn't been initialized
2258 // (pDataChild is null then, this happens when no values for it are in this row)
2259 bool bHasChild = ( pRefChild != NULL );
2261 long nUserSubCount = pRefMember->GetSubTotalCount();
2263 // Calculate at least automatic if no subtotals are selected,
2264 // show only own values if there's no child dimension (innermost).
2265 if ( !nUserSubCount || !bHasChild )
2266 nUserSubCount = 1;
2268 ScDPSubTotalState aLocalSubState(rSubState); // keep row state, modify column
2270 long nMemberMeasure = nMeasure;
2271 long nSubSize = pResultData->GetCountForMeasure(nMeasure);
2273 for (long nUserPos=0; nUserPos<nUserSubCount; nUserPos++) // including hidden "automatic"
2275 if ( pChildDimension && nUserSubCount > 1 )
2277 const ScDPLevel* pForceLevel = pResultMember ? pResultMember->GetParentLevel() : NULL;
2278 aLocalSubState.nColSubTotalFunc = nUserPos;
2279 aLocalSubState.eColForce = lcl_GetForceFunc( pForceLevel, nUserPos );
2282 for ( long nSubCount=0; nSubCount<nSubSize; nSubCount++ )
2284 if ( nMeasure == SC_DPMEASURE_ALL )
2285 nMemberMeasure = nSubCount;
2287 // update data...
2288 ScDPAggData* pAggData = GetAggData( nMemberMeasure, aLocalSubState );
2289 if (pAggData)
2291 //TODO: aLocalSubState?
2292 sheet::DataPilotFieldReference aReferenceValue = pResultData->GetMeasureRefVal( nMemberMeasure );
2293 sal_Int32 eRefType = aReferenceValue.ReferenceType;
2295 if ( eRefType == sheet::DataPilotFieldReferenceType::RUNNING_TOTAL ||
2296 eRefType == sheet::DataPilotFieldReferenceType::ITEM_DIFFERENCE ||
2297 eRefType == sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE ||
2298 eRefType == sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE )
2300 bool bRunningTotal = ( eRefType == sheet::DataPilotFieldReferenceType::RUNNING_TOTAL );
2301 bool bRelative =
2302 ( aReferenceValue.ReferenceItemType != sheet::DataPilotFieldReferenceItemType::NAMED && !bRunningTotal );
2303 long nRelativeDir = bRelative ?
2304 ( ( aReferenceValue.ReferenceItemType == sheet::DataPilotFieldReferenceItemType::PREVIOUS ) ? -1 : 1 ) : 0;
2306 const ScDPRunningTotalState::IndexArray& rColVisible = rRunning.GetColVisible();
2307 const ScDPRunningTotalState::IndexArray& rColSorted = rRunning.GetColSorted();
2308 const ScDPRunningTotalState::IndexArray& rRowVisible = rRunning.GetRowVisible();
2309 const ScDPRunningTotalState::IndexArray& rRowSorted = rRunning.GetRowSorted();
2311 OUString aRefFieldName = aReferenceValue.ReferenceField;
2313 //TODO: aLocalSubState?
2314 sal_uInt16 nRefOrient = pResultData->GetMeasureRefOrient( nMemberMeasure );
2315 bool bRefDimInCol = ( nRefOrient == sheet::DataPilotFieldOrientation_COLUMN );
2316 bool bRefDimInRow = ( nRefOrient == sheet::DataPilotFieldOrientation_ROW );
2318 ScDPResultDimension* pSelectDim = NULL;
2319 long nRowPos = 0;
2320 long nColPos = 0;
2322 // find the reference field in column or row dimensions
2324 if ( bRefDimInRow ) // look in row dimensions
2326 pSelectDim = rRunning.GetRowResRoot()->GetChildDimension();
2327 while ( pSelectDim && pSelectDim->GetName() != aRefFieldName )
2329 long nIndex = rRowSorted[nRowPos];
2330 if ( nIndex >= 0 && nIndex < pSelectDim->GetMemberCount() )
2331 pSelectDim = pSelectDim->GetMember(nIndex)->GetChildDimension();
2332 else
2333 pSelectDim = NULL;
2334 ++nRowPos;
2336 // child dimension of innermost member?
2337 if ( pSelectDim && rRowSorted[nRowPos] < 0 )
2338 pSelectDim = NULL;
2341 if ( bRefDimInCol ) // look in column dimensions
2343 pSelectDim = rRunning.GetColResRoot()->GetChildDimension();
2344 while ( pSelectDim && pSelectDim->GetName() != aRefFieldName )
2346 long nIndex = rColSorted[nColPos];
2347 if ( nIndex >= 0 && nIndex < pSelectDim->GetMemberCount() )
2348 pSelectDim = pSelectDim->GetMember(nIndex)->GetChildDimension();
2349 else
2350 pSelectDim = NULL;
2351 ++nColPos;
2353 // child dimension of innermost member?
2354 if ( pSelectDim && rColSorted[nColPos] < 0 )
2355 pSelectDim = NULL;
2358 bool bNoDetailsInRef = false;
2359 if ( pSelectDim && bRunningTotal )
2361 // Running totals:
2362 // If details are hidden for this member in the reference dimension,
2363 // don't show or sum up the value. Otherwise, for following members,
2364 // the running totals of details and subtotals wouldn't match.
2366 long nMyIndex = bRefDimInCol ? rColSorted[nColPos] : rRowSorted[nRowPos];
2367 if ( nMyIndex >= 0 && nMyIndex < pSelectDim->GetMemberCount() )
2369 const ScDPResultMember* pMyRefMember = pSelectDim->GetMember(nMyIndex);
2370 if ( pMyRefMember && pMyRefMember->HasHiddenDetails() )
2372 pSelectDim = NULL; // don't calculate
2373 bNoDetailsInRef = true; // show error, not empty
2378 if ( bRelative )
2380 // Difference/Percentage from previous/next:
2381 // If details are hidden for this member in the innermost column/row
2382 // dimension (the orientation of the reference dimension), show an
2383 // error value.
2384 // - If the no-details dimension is the reference dimension, its
2385 // members will be skipped when finding the previous/next member,
2386 // so there must be no results for its members.
2387 // - If the no-details dimension is outside of the reference dimension,
2388 // no calculation in the reference dimension is possible.
2389 // - Otherwise, the error isn't strictly necessary, but shown for
2390 // consistency.
2392 bool bInnerNoDetails = bRefDimInCol ? HasHiddenDetails() :
2393 ( !bRefDimInRow || rRowParent.HasHiddenDetails() );
2394 if ( bInnerNoDetails )
2396 pSelectDim = NULL;
2397 bNoDetailsInRef = true; // show error, not empty
2401 if ( !bRefDimInCol && !bRefDimInRow ) // invalid dimension specified
2402 bNoDetailsInRef = true; // pSelectDim is then already NULL
2404 // get the member for the reference item and do the calculation
2406 if ( bRunningTotal )
2408 // running total in (dimension) -> find first existing member
2410 if ( pSelectDim )
2412 ScDPDataMember* pSelectMember;
2413 if ( bRefDimInCol )
2414 pSelectMember = ScDPResultDimension::GetColReferenceMember( NULL, NULL,
2415 nColPos, rRunning );
2416 else
2418 const long* pRowSorted = &rRowSorted[0];
2419 const long* pColSorted = &rColSorted[0];
2420 pRowSorted += nRowPos + 1; // including the reference dimension
2421 pSelectMember = pSelectDim->GetRowReferenceMember(
2422 NULL, NULL, pRowSorted, pColSorted);
2425 if ( pSelectMember )
2427 // The running total is kept as the auxiliary value in
2428 // the first available member for the reference dimension.
2429 // Members are visited in final order, so each one's result
2430 // can be used and then modified.
2432 ScDPAggData* pSelectData = pSelectMember->
2433 GetAggData( nMemberMeasure, aLocalSubState );
2434 if ( pSelectData )
2436 double fTotal = pSelectData->GetAuxiliary();
2437 fTotal += pAggData->GetResult();
2438 pSelectData->SetAuxiliary( fTotal );
2439 pAggData->SetResult( fTotal );
2440 pAggData->SetEmpty(false); // always display
2443 else
2444 pAggData->SetError();
2446 else if (bNoDetailsInRef)
2447 pAggData->SetError();
2448 else
2449 pAggData->SetEmpty(true); // empty (dim set to 0 above)
2451 else
2453 // difference/percentage -> find specified member
2455 if ( pSelectDim )
2457 OUString aRefItemName = aReferenceValue.ReferenceItemName;
2458 ScDPRelativePos aRefItemPos( 0, nRelativeDir ); // nBasePos is modified later
2460 const OUString* pRefName = NULL;
2461 const ScDPRelativePos* pRefPos = NULL;
2462 if ( bRelative )
2463 pRefPos = &aRefItemPos;
2464 else
2465 pRefName = &aRefItemName;
2467 ScDPDataMember* pSelectMember;
2468 if ( bRefDimInCol )
2470 aRefItemPos.nBasePos = rColVisible[nColPos]; // without sort order applied
2471 pSelectMember = ScDPResultDimension::GetColReferenceMember( pRefPos, pRefName,
2472 nColPos, rRunning );
2474 else
2476 aRefItemPos.nBasePos = rRowVisible[nRowPos]; // without sort order applied
2477 const long* pRowSorted = &rRowSorted[0];
2478 const long* pColSorted = &rColSorted[0];
2479 pRowSorted += nRowPos + 1; // including the reference dimension
2480 pSelectMember = pSelectDim->GetRowReferenceMember(
2481 pRefPos, pRefName, pRowSorted, pColSorted);
2484 // difference or perc.difference is empty for the reference item itself
2485 if ( pSelectMember == this &&
2486 eRefType != sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE )
2488 pAggData->SetEmpty(true);
2490 else if ( pSelectMember )
2492 const ScDPAggData* pOtherAggData = pSelectMember->
2493 GetConstAggData( nMemberMeasure, aLocalSubState );
2494 OSL_ENSURE( pOtherAggData, "no agg data" );
2495 if ( pOtherAggData )
2497 // Reference member may be visited before or after this one,
2498 // so the auxiliary value is used for the original result.
2500 double fOtherResult = pOtherAggData->GetAuxiliary();
2501 double fThisResult = pAggData->GetResult();
2502 bool bError = false;
2503 switch ( eRefType )
2505 case sheet::DataPilotFieldReferenceType::ITEM_DIFFERENCE:
2506 fThisResult = fThisResult - fOtherResult;
2507 break;
2508 case sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE:
2509 if ( fOtherResult == 0.0 )
2510 bError = true;
2511 else
2512 fThisResult = fThisResult / fOtherResult;
2513 break;
2514 case sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE:
2515 if ( fOtherResult == 0.0 )
2516 bError = true;
2517 else
2518 fThisResult = ( fThisResult - fOtherResult ) / fOtherResult;
2519 break;
2520 default:
2521 OSL_FAIL("invalid calculation type");
2523 if ( bError )
2525 pAggData->SetError();
2527 else
2529 pAggData->SetResult(fThisResult);
2530 pAggData->SetEmpty(false); // always display
2532 //TODO: errors in data?
2535 else if (bRelative && !bNoDetailsInRef)
2536 pAggData->SetEmpty(true); // empty
2537 else
2538 pAggData->SetError(); // error
2540 else if (bNoDetailsInRef)
2541 pAggData->SetError(); // error
2542 else
2543 pAggData->SetEmpty(true); // empty
2546 else if ( eRefType == sheet::DataPilotFieldReferenceType::ROW_PERCENTAGE ||
2547 eRefType == sheet::DataPilotFieldReferenceType::COLUMN_PERCENTAGE ||
2548 eRefType == sheet::DataPilotFieldReferenceType::TOTAL_PERCENTAGE ||
2549 eRefType == sheet::DataPilotFieldReferenceType::INDEX )
2552 // set total values when they are encountered (always before their use)
2554 ScDPAggData* pColTotalData = pRefMember->GetColTotal( nMemberMeasure );
2555 ScDPAggData* pRowTotalData = rTotals.GetRowTotal( nMemberMeasure );
2556 ScDPAggData* pGrandTotalData = rTotals.GetGrandTotal( nMemberMeasure );
2558 double fTotalValue = pAggData->HasError() ? 0 : pAggData->GetResult();
2560 if ( bIsRoot && rTotals.IsInColRoot() && pGrandTotalData )
2561 pGrandTotalData->SetAuxiliary( fTotalValue );
2563 if ( bIsRoot && pRowTotalData )
2564 pRowTotalData->SetAuxiliary( fTotalValue );
2566 if ( rTotals.IsInColRoot() && pColTotalData )
2567 pColTotalData->SetAuxiliary( fTotalValue );
2569 // find relation to total values
2571 switch ( eRefType )
2573 case sheet::DataPilotFieldReferenceType::ROW_PERCENTAGE:
2574 case sheet::DataPilotFieldReferenceType::COLUMN_PERCENTAGE:
2575 case sheet::DataPilotFieldReferenceType::TOTAL_PERCENTAGE:
2577 double nTotal;
2578 if ( eRefType == sheet::DataPilotFieldReferenceType::ROW_PERCENTAGE )
2579 nTotal = pRowTotalData->GetAuxiliary();
2580 else if ( eRefType == sheet::DataPilotFieldReferenceType::COLUMN_PERCENTAGE )
2581 nTotal = pColTotalData->GetAuxiliary();
2582 else
2583 nTotal = pGrandTotalData->GetAuxiliary();
2585 if ( nTotal == 0.0 )
2586 pAggData->SetError();
2587 else
2588 pAggData->SetResult( pAggData->GetResult() / nTotal );
2590 break;
2591 case sheet::DataPilotFieldReferenceType::INDEX:
2593 double nColTotal = pColTotalData->GetAuxiliary();
2594 double nRowTotal = pRowTotalData->GetAuxiliary();
2595 double nGrandTotal = pGrandTotalData->GetAuxiliary();
2596 if ( nRowTotal == 0.0 || nColTotal == 0.0 )
2597 pAggData->SetError();
2598 else
2599 pAggData->SetResult(
2600 ( pAggData->GetResult() * nGrandTotal ) /
2601 ( nRowTotal * nColTotal ) );
2603 break;
2611 if ( bHasChild ) // child dimension must be processed last, so the row total is known
2613 if ( pDataChild )
2614 pDataChild->UpdateRunningTotals( pRefChild, nMeasure,
2615 bIsSubTotalRow, rSubState, rRunning, rTotals, rRowParent );
2619 #if DEBUG_PIVOT_TABLE
2620 void ScDPDataMember::DumpState( const ScDPResultMember* pRefMember, ScDocument* pDoc, ScAddress& rPos ) const
2622 lcl_DumpRow( OUString("ScDPDataMember"), GetName(), &aAggregate, pDoc, rPos );
2623 SCROW nStartRow = rPos.Row();
2625 const ScDPDataDimension* pDataChild = GetChildDimension();
2626 const ScDPResultDimension* pRefChild = pRefMember->GetChildDimension();
2627 if ( pDataChild && pRefChild )
2628 pDataChild->DumpState( pRefChild, pDoc, rPos );
2630 lcl_Indent( pDoc, nStartRow, rPos );
2633 void ScDPDataMember::Dump(int nIndent) const
2635 std::string aIndent(nIndent*2, ' ');
2636 std::cout << aIndent << "-- data member '"
2637 << (pResultMember ? pResultMember->GetName() : OUString()) << "'" << std::endl;
2638 for (const ScDPAggData* pAgg = &aAggregate; pAgg; pAgg = pAgg->GetExistingChild())
2639 pAgg->Dump(nIndent+1);
2641 if (pChildDimension)
2642 pChildDimension->Dump(nIndent+1);
2644 #endif
2646 // Helper class to select the members to include in
2647 // ScDPResultDimension::InitFrom or LateInitFrom if groups are used
2649 class ScDPGroupCompare
2651 private:
2652 const ScDPResultData* pResultData;
2653 const ScDPInitState& rInitState;
2654 long nDimSource;
2655 bool bIncludeAll;
2656 bool bIsBase;
2657 long nGroupBase;
2658 public:
2659 ScDPGroupCompare( const ScDPResultData* pData, const ScDPInitState& rState, long nDimension );
2660 ~ScDPGroupCompare() {}
2662 bool IsIncluded( const ScDPMember& rMember ) { return bIncludeAll || TestIncluded( rMember ); }
2663 bool TestIncluded( const ScDPMember& rMember );
2666 ScDPGroupCompare::ScDPGroupCompare( const ScDPResultData* pData, const ScDPInitState& rState, long nDimension ) :
2667 pResultData( pData ),
2668 rInitState( rState ),
2669 nDimSource( nDimension )
2671 bIsBase = pResultData->IsBaseForGroup( nDimSource );
2672 nGroupBase = pResultData->GetGroupBase( nDimSource ); //TODO: get together in one call?
2674 // if bIncludeAll is set, TestIncluded doesn't need to be called
2675 bIncludeAll = !( bIsBase || nGroupBase >= 0 );
2678 bool ScDPGroupCompare::TestIncluded( const ScDPMember& rMember )
2680 bool bInclude = true;
2681 if ( bIsBase )
2683 // need to check all previous groups
2684 //TODO: get array of groups (or indexes) before loop?
2685 ScDPItemData aMemberData;
2686 rMember.FillItemData( aMemberData );
2688 const std::vector<ScDPInitState::Member>& rMemStates = rInitState.GetMembers();
2689 std::vector<ScDPInitState::Member>::const_iterator it = rMemStates.begin(), itEnd = rMemStates.end();
2690 for (; it != itEnd && bInclude; ++it)
2692 if (pResultData->GetGroupBase(it->mnSrcIndex) == nDimSource)
2694 bInclude = pResultData->IsInGroup(
2695 it->mnNameIndex, it->mnSrcIndex, aMemberData, nDimSource);
2699 else if ( nGroupBase >= 0 )
2701 // base isn't used in preceding fields
2702 // -> look for other groups using the same base
2704 //TODO: get array of groups (or indexes) before loop?
2705 ScDPItemData aMemberData;
2706 rMember.FillItemData( aMemberData );
2707 const std::vector<ScDPInitState::Member>& rMemStates = rInitState.GetMembers();
2708 std::vector<ScDPInitState::Member>::const_iterator it = rMemStates.begin(), itEnd = rMemStates.end();
2709 for (; it != itEnd && bInclude; ++it)
2711 if (pResultData->GetGroupBase(it->mnSrcIndex) == nGroupBase)
2713 // coverity[copy_paste_error] - same base (hierarchy between
2714 // the two groups is irrelevant)
2715 bInclude = pResultData->HasCommonElement(
2716 it->mnNameIndex, it->mnSrcIndex, aMemberData, nDimSource);
2722 return bInclude;
2725 ScDPResultDimension::ScDPResultDimension( const ScDPResultData* pData ) :
2726 pResultData( pData ),
2727 nSortMeasure( 0 ),
2728 bIsDataLayout( false ),
2729 bSortByData( false ),
2730 bSortAscending( false ),
2731 bAutoShow( false ),
2732 bAutoTopItems( false ),
2733 bInitialized( false ),
2734 nAutoMeasure( 0 ),
2735 nAutoCount( 0 )
2739 ScDPResultDimension::~ScDPResultDimension()
2741 for( int i = maMemberArray.size () ; i-- > 0 ; )
2742 delete maMemberArray[i];
2745 ScDPResultMember *ScDPResultDimension::FindMember( SCROW iData ) const
2747 if( bIsDataLayout )
2748 return maMemberArray[0];
2750 MemberHash::const_iterator aRes = maMemberHash.find( iData );
2751 if( aRes != maMemberHash.end()) {
2752 if ( aRes->second->IsNamedItem( iData ) )
2753 return aRes->second;
2754 OSL_FAIL("problem! hash result is not the same as IsNamedItem");
2757 unsigned int i;
2758 unsigned int nCount = maMemberArray.size();
2759 ScDPResultMember* pResultMember;
2760 for( i = 0; i < nCount ; i++ )
2762 pResultMember = maMemberArray[i];
2763 if ( pResultMember->IsNamedItem( iData ) )
2764 return pResultMember;
2766 return NULL;
2769 void ScDPResultDimension::InitFrom(
2770 const vector<ScDPDimension*>& ppDim, const vector<ScDPLevel*>& ppLev,
2771 size_t nPos, ScDPInitState& rInitState, bool bInitChild )
2773 if (nPos >= ppDim.size() || nPos >= ppLev.size())
2775 bInitialized = true;
2776 return;
2779 ScDPDimension* pThisDim = ppDim[nPos];
2780 ScDPLevel* pThisLevel = ppLev[nPos];
2782 if (!pThisDim || !pThisLevel)
2784 bInitialized = true;
2785 return;
2788 bIsDataLayout = pThisDim->getIsDataLayoutDimension(); // member
2789 aDimensionName = pThisDim->getName(); // member
2791 // Check the autoshow setting. If it's enabled, store the settings.
2792 const sheet::DataPilotFieldAutoShowInfo& rAutoInfo = pThisLevel->GetAutoShow();
2793 if ( rAutoInfo.IsEnabled )
2795 bAutoShow = true;
2796 bAutoTopItems = ( rAutoInfo.ShowItemsMode == sheet::DataPilotFieldShowItemsMode::FROM_TOP );
2797 nAutoMeasure = pThisLevel->GetAutoMeasure();
2798 nAutoCount = rAutoInfo.ItemCount;
2801 // Check the sort info, and store the settings if appropriate.
2802 const sheet::DataPilotFieldSortInfo& rSortInfo = pThisLevel->GetSortInfo();
2803 if ( rSortInfo.Mode == sheet::DataPilotFieldSortMode::DATA )
2805 bSortByData = true;
2806 bSortAscending = rSortInfo.IsAscending;
2807 nSortMeasure = pThisLevel->GetSortMeasure();
2810 // global order is used to initialize aMembers, so it doesn't have to be looked at later
2811 const ScMemberSortOrder& rGlobalOrder = pThisLevel->GetGlobalOrder();
2813 long nDimSource = pThisDim->GetDimension(); //TODO: check GetSourceDim?
2814 ScDPGroupCompare aCompare( pResultData, rInitState, nDimSource );
2816 // Now, go through all members and initialize them.
2817 ScDPMembers* pMembers = pThisLevel->GetMembersObject();
2818 long nMembCount = pMembers->getCount();
2819 for ( long i=0; i<nMembCount; i++ )
2821 long nSorted = rGlobalOrder.empty() ? i : rGlobalOrder[i];
2823 ScDPMember* pMember = pMembers->getByIndex(nSorted);
2824 if ( aCompare.IsIncluded( *pMember ) )
2826 ScDPParentDimData aData( i, pThisDim, pThisLevel, pMember);
2827 ScDPResultMember* pNew = AddMember( aData );
2829 rInitState.AddMember(nDimSource, pNew->GetDataId());
2830 pNew->InitFrom( ppDim, ppLev, nPos+1, rInitState, bInitChild );
2831 rInitState.RemoveMember();
2834 bInitialized = true;
2837 void ScDPResultDimension::LateInitFrom(
2838 LateInitParams& rParams, const vector<SCROW>& pItemData, size_t nPos, ScDPInitState& rInitState)
2840 if ( rParams.IsEnd( nPos ) )
2841 return;
2842 OSL_ENSURE( nPos <= pItemData.size(), OString::number(pItemData.size()).getStr() );
2843 ScDPDimension* pThisDim = rParams.GetDim( nPos );
2844 ScDPLevel* pThisLevel = rParams.GetLevel( nPos );
2845 SCROW rThisData = pItemData[nPos];
2847 if (!pThisDim || !pThisLevel)
2848 return;
2850 long nDimSource = pThisDim->GetDimension(); //TODO: check GetSourceDim?
2852 bool bShowEmpty = pThisLevel->getShowEmpty();
2854 if ( !bInitialized )
2855 { // init some values
2856 // create all members at the first call (preserve order)
2857 bIsDataLayout = pThisDim->getIsDataLayoutDimension();
2858 aDimensionName = pThisDim->getName();
2860 const sheet::DataPilotFieldAutoShowInfo& rAutoInfo = pThisLevel->GetAutoShow();
2861 if ( rAutoInfo.IsEnabled )
2863 bAutoShow = true;
2864 bAutoTopItems = ( rAutoInfo.ShowItemsMode == sheet::DataPilotFieldShowItemsMode::FROM_TOP );
2865 nAutoMeasure = pThisLevel->GetAutoMeasure();
2866 nAutoCount = rAutoInfo.ItemCount;
2869 const sheet::DataPilotFieldSortInfo& rSortInfo = pThisLevel->GetSortInfo();
2870 if ( rSortInfo.Mode == sheet::DataPilotFieldSortMode::DATA )
2872 bSortByData = true;
2873 bSortAscending = rSortInfo.IsAscending;
2874 nSortMeasure = pThisLevel->GetSortMeasure();
2878 bool bLateInitAllMembers= bIsDataLayout || rParams.GetInitAllChild() || bShowEmpty;
2880 if ( !bLateInitAllMembers )
2882 ResultMembers* pMembers = pResultData->GetDimResultMembers(nDimSource, pThisDim, pThisLevel);
2883 bLateInitAllMembers = pMembers->IsHasHideDetailsMembers();
2884 #if OSL_DEBUG_LEVEL > 1
2885 OSL_TRACE( "%s", OUStringToOString(aDimensionName, RTL_TEXTENCODING_UTF8).getStr() );
2886 if ( pMembers->IsHasHideDetailsMembers() )
2887 OSL_TRACE( "HasHideDetailsMembers" );
2888 #endif
2889 pMembers->SetHasHideDetailsMembers( false );
2892 bool bNewAllMembers = (!rParams.IsRow()) || nPos == 0 || bLateInitAllMembers;
2894 if (bNewAllMembers )
2896 // global order is used to initialize aMembers, so it doesn't have to be looked at later
2897 if ( !bInitialized )
2898 { //init all members
2899 const ScMemberSortOrder& rGlobalOrder = pThisLevel->GetGlobalOrder();
2901 ScDPGroupCompare aCompare( pResultData, rInitState, nDimSource );
2902 ScDPMembers* pMembers = pThisLevel->GetMembersObject();
2903 long nMembCount = pMembers->getCount();
2904 for ( long i=0; i<nMembCount; i++ )
2906 long nSorted = rGlobalOrder.empty() ? i : rGlobalOrder[i];
2908 ScDPMember* pMember = pMembers->getByIndex(nSorted);
2909 if ( aCompare.IsIncluded( *pMember ) )
2910 { // add all members
2911 ScDPParentDimData aData( i, pThisDim, pThisLevel, pMember );
2912 AddMember( aData );
2915 bInitialized = true; // don't call again, even if no members were included
2917 // initialize only specific member (or all if "show empty" flag is set)
2918 if ( bLateInitAllMembers )
2920 long nCount = maMemberArray.size();
2921 for (long i=0; i<nCount; i++)
2923 ScDPResultMember* pResultMember = maMemberArray[i];
2925 // check show empty
2926 bool bAllChildren = false;
2927 if( bShowEmpty )
2929 if ( pResultMember->IsNamedItem( rThisData ) )
2930 bAllChildren = false;
2931 else
2932 bAllChildren = true;
2934 rParams.SetInitAllChildren( bAllChildren );
2935 rInitState.AddMember( nDimSource, pResultMember->GetDataId() );
2936 pResultMember->LateInitFrom( rParams, pItemData, nPos+1, rInitState );
2937 rInitState.RemoveMember();
2940 else
2942 ScDPResultMember* pResultMember = FindMember( rThisData );
2943 if( NULL != pResultMember )
2945 rInitState.AddMember( nDimSource, pResultMember->GetDataId() );
2946 pResultMember->LateInitFrom( rParams, pItemData, nPos+1, rInitState );
2947 rInitState.RemoveMember();
2951 else
2952 InitWithMembers( rParams, pItemData, nPos, rInitState );
2955 long ScDPResultDimension::GetSize(long nMeasure) const
2957 long nTotal = 0;
2958 long nMemberCount = maMemberArray.size();
2959 if (bIsDataLayout)
2961 OSL_ENSURE(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1,
2962 "DataLayout dimension twice?");
2963 // repeat first member...
2964 nTotal = nMemberCount * maMemberArray[0]->GetSize(0); // all measures have equal size
2966 else
2968 // add all members
2969 for (long nMem=0; nMem<nMemberCount; nMem++)
2970 nTotal += maMemberArray[nMem]->GetSize(nMeasure);
2972 return nTotal;
2975 bool ScDPResultDimension::IsValidEntry( const vector< SCROW >& aMembers ) const
2977 if (aMembers.empty())
2978 return false;
2980 const ScDPResultMember* pMember = FindMember( aMembers[0] );
2981 if ( NULL != pMember )
2982 return pMember->IsValidEntry( aMembers );
2983 #if OSL_DEBUG_LEVEL > 1
2984 OStringBuffer strTemp("IsValidEntry: Member not found, DimName = ");
2985 strTemp.append(OUStringToOString(GetName(), RTL_TEXTENCODING_UTF8));
2986 OSL_TRACE("%s", strTemp.getStr());
2987 #endif
2988 return false;
2991 void ScDPResultDimension::ProcessData( const vector< SCROW >& aMembers,
2992 const ScDPResultDimension* pDataDim,
2993 const vector< SCROW >& aDataMembers,
2994 const vector<ScDPValue>& aValues ) const
2996 if (aMembers.empty())
2997 return;
2999 ScDPResultMember* pMember = FindMember( aMembers[0] );
3000 if ( NULL != pMember )
3002 vector<SCROW> aChildMembers;
3003 if (aMembers.size() > 1)
3005 vector<SCROW>::const_iterator itr = aMembers.begin();
3006 aChildMembers.insert(aChildMembers.begin(), ++itr, aMembers.end());
3008 pMember->ProcessData( aChildMembers, pDataDim, aDataMembers, aValues );
3009 return;
3012 OSL_FAIL("ProcessData: Member not found");
3015 void ScDPResultDimension::FillMemberResults( uno::Sequence<sheet::MemberResult>* pSequences,
3016 long nStart, long nMeasure )
3018 long nPos = nStart;
3019 long nCount = maMemberArray.size();
3021 for (long i=0; i<nCount; i++)
3023 long nSorted = aMemberOrder.empty() ? i : aMemberOrder[i];
3025 ScDPResultMember* pMember = maMemberArray[nSorted];
3026 // in data layout dimension, use first member with different measures/names
3027 if ( bIsDataLayout )
3029 bool bTotalResult = false;
3030 OUString aMbrName = pResultData->GetMeasureDimensionName( nSorted );
3031 OUString aMbrCapt = pResultData->GetMeasureString( nSorted, false, SUBTOTAL_FUNC_NONE, bTotalResult );
3032 maMemberArray[0]->FillMemberResults( pSequences, nPos, nSorted, false, &aMbrName, &aMbrCapt );
3034 else if ( pMember->IsVisible() )
3036 pMember->FillMemberResults( pSequences, nPos, nMeasure, false, NULL, NULL );
3038 // nPos is modified
3042 void ScDPResultDimension::FillDataResults(
3043 const ScDPResultMember* pRefMember, ScDPResultFilterContext& rFilterCxt,
3044 uno::Sequence< uno::Sequence<sheet::DataResult> >& rSequence, long nMeasure) const
3046 FilterStack aFilterStack(rFilterCxt.maFilters);
3047 aFilterStack.pushDimName(GetName(), bIsDataLayout);
3049 long nMemberMeasure = nMeasure;
3050 long nCount = maMemberArray.size();
3051 for (long i=0; i<nCount; i++)
3053 long nSorted = aMemberOrder.empty() ? i : aMemberOrder[i];
3055 const ScDPResultMember* pMember;
3056 if (bIsDataLayout)
3058 OSL_ENSURE(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1,
3059 "DataLayout dimension twice?");
3060 pMember = maMemberArray[0];
3061 nMemberMeasure = nSorted;
3063 else
3064 pMember = maMemberArray[nSorted];
3066 if ( pMember->IsVisible() )
3067 pMember->FillDataResults(pRefMember, rFilterCxt, rSequence, nMemberMeasure);
3071 void ScDPResultDimension::UpdateDataResults( const ScDPResultMember* pRefMember, long nMeasure ) const
3073 long nMemberMeasure = nMeasure;
3074 long nCount = maMemberArray.size();
3075 for (long i=0; i<nCount; i++)
3077 const ScDPResultMember* pMember;
3078 if (bIsDataLayout)
3080 OSL_ENSURE(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1,
3081 "DataLayout dimension twice?");
3082 pMember = maMemberArray[0];
3083 nMemberMeasure = i;
3085 else
3086 pMember = maMemberArray[i];
3088 if ( pMember->IsVisible() )
3089 pMember->UpdateDataResults( pRefMember, nMemberMeasure );
3093 void ScDPResultDimension::SortMembers( ScDPResultMember* pRefMember )
3095 long nCount = maMemberArray.size();
3097 if ( bSortByData )
3099 // sort members
3101 OSL_ENSURE( aMemberOrder.empty(), "sort twice?" );
3102 aMemberOrder.resize( nCount );
3103 for (long nPos=0; nPos<nCount; nPos++)
3104 aMemberOrder[nPos] = nPos;
3106 ScDPRowMembersOrder aComp( *this, nSortMeasure, bSortAscending );
3107 ::std::sort( aMemberOrder.begin(), aMemberOrder.end(), aComp );
3110 // handle children
3112 // for data layout, call only once - sorting measure is always taken from settings
3113 long nLoopCount = bIsDataLayout ? 1 : nCount;
3114 for (long i=0; i<nLoopCount; i++)
3116 ScDPResultMember* pMember = maMemberArray[i];
3117 if ( pMember->IsVisible() )
3118 pMember->SortMembers( pRefMember );
3122 void ScDPResultDimension::DoAutoShow( ScDPResultMember* pRefMember )
3124 long nCount = maMemberArray.size();
3126 // handle children first, before changing the visible state
3128 // for data layout, call only once - sorting measure is always taken from settings
3129 long nLoopCount = bIsDataLayout ? 1 : nCount;
3130 for (long i=0; i<nLoopCount; i++)
3132 ScDPResultMember* pMember = maMemberArray[i];
3133 if ( pMember->IsVisible() )
3134 pMember->DoAutoShow( pRefMember );
3137 if ( bAutoShow && nAutoCount > 0 && nAutoCount < nCount )
3139 // establish temporary order, hide remaining members
3141 ScMemberSortOrder aAutoOrder;
3142 aAutoOrder.resize( nCount );
3143 long nPos;
3144 for (nPos=0; nPos<nCount; nPos++)
3145 aAutoOrder[nPos] = nPos;
3147 ScDPRowMembersOrder aComp( *this, nAutoMeasure, !bAutoTopItems );
3148 ::std::sort( aAutoOrder.begin(), aAutoOrder.end(), aComp );
3150 // look for equal values to the last included one
3152 long nIncluded = nAutoCount;
3153 const ScDPResultMember* pMember1 = maMemberArray[aAutoOrder[nIncluded - 1]];
3154 const ScDPDataMember* pDataMember1 = pMember1->IsVisible() ? pMember1->GetDataRoot() : NULL;
3155 bool bContinue = true;
3156 while ( bContinue )
3158 bContinue = false;
3159 if ( nIncluded < nCount )
3161 const ScDPResultMember* pMember2 = maMemberArray[aAutoOrder[nIncluded]];
3162 const ScDPDataMember* pDataMember2 = pMember2->IsVisible() ? pMember2->GetDataRoot() : NULL;
3164 if ( lcl_IsEqual( pDataMember1, pDataMember2, nAutoMeasure ) )
3166 ++nIncluded; // include more members if values are equal
3167 bContinue = true;
3172 // hide the remaining members
3174 for (nPos = nIncluded; nPos < nCount; nPos++)
3176 ScDPResultMember* pMember = maMemberArray[aAutoOrder[nPos]];
3177 pMember->SetAutoHidden();
3182 void ScDPResultDimension::ResetResults()
3184 long nCount = maMemberArray.size();
3185 for (long i=0; i<nCount; i++)
3187 // sort order doesn't matter
3188 ScDPResultMember* pMember = maMemberArray[bIsDataLayout ? 0 : i];
3189 pMember->ResetResults();
3193 long ScDPResultDimension::GetSortedIndex( long nUnsorted ) const
3195 return aMemberOrder.empty() ? nUnsorted : aMemberOrder[nUnsorted];
3198 void ScDPResultDimension::UpdateRunningTotals( const ScDPResultMember* pRefMember, long nMeasure,
3199 ScDPRunningTotalState& rRunning, ScDPRowTotals& rTotals ) const
3201 const ScDPResultMember* pMember;
3202 long nMemberMeasure = nMeasure;
3203 long nCount = maMemberArray.size();
3204 for (long i=0; i<nCount; i++)
3206 long nSorted = aMemberOrder.empty() ? i : aMemberOrder[i];
3208 if (bIsDataLayout)
3210 OSL_ENSURE(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1,
3211 "DataLayout dimension twice?");
3212 pMember = maMemberArray[0];
3213 nMemberMeasure = nSorted;
3215 else
3216 pMember = maMemberArray[nSorted];
3218 if ( pMember->IsVisible() )
3220 if ( bIsDataLayout )
3221 rRunning.AddRowIndex( 0, 0 );
3222 else
3223 rRunning.AddRowIndex( i, nSorted );
3224 pMember->UpdateRunningTotals( pRefMember, nMemberMeasure, rRunning, rTotals );
3225 rRunning.RemoveRowIndex();
3230 ScDPDataMember* ScDPResultDimension::GetRowReferenceMember(
3231 const ScDPRelativePos* pRelativePos, const OUString* pName,
3232 const long* pRowIndexes, const long* pColIndexes ) const
3234 // get named, previous/next, or first member of this dimension (first existing if pRelativePos and pName are NULL)
3236 OSL_ENSURE( pRelativePos == NULL || pName == NULL, "can't use position and name" );
3238 ScDPDataMember* pColMember = NULL;
3240 bool bFirstExisting = ( pRelativePos == NULL && pName == NULL );
3241 long nMemberCount = maMemberArray.size();
3242 long nMemberIndex = 0; // unsorted
3243 long nDirection = 1; // forward if no relative position is used
3244 if ( pRelativePos )
3246 nDirection = pRelativePos->nDirection;
3247 nMemberIndex = pRelativePos->nBasePos + nDirection; // bounds are handled below
3249 OSL_ENSURE( nDirection == 1 || nDirection == -1, "Direction must be 1 or -1" );
3251 else if ( pName )
3253 // search for named member
3255 const ScDPResultMember* pRowMember = maMemberArray[GetSortedIndex(nMemberIndex)];
3257 //TODO: use ScDPItemData, as in ScDPDimension::IsValidPage?
3258 while ( pRowMember && pRowMember->GetName() != *pName )
3260 ++nMemberIndex;
3261 if ( nMemberIndex < nMemberCount )
3262 pRowMember = maMemberArray[GetSortedIndex(nMemberIndex)];
3263 else
3264 pRowMember = NULL;
3268 bool bContinue = true;
3269 while ( bContinue && nMemberIndex >= 0 && nMemberIndex < nMemberCount )
3271 const ScDPResultMember* pRowMember = maMemberArray[GetSortedIndex(nMemberIndex)];
3273 // get child members by given indexes
3275 const long* pNextRowIndex = pRowIndexes;
3276 while ( *pNextRowIndex >= 0 && pRowMember )
3278 const ScDPResultDimension* pRowChild = pRowMember->GetChildDimension();
3279 if ( pRowChild && *pNextRowIndex < pRowChild->GetMemberCount() )
3280 pRowMember = pRowChild->GetMember( *pNextRowIndex );
3281 else
3282 pRowMember = NULL;
3283 ++pNextRowIndex;
3286 if ( pRowMember && pRelativePos )
3288 // Skip the member if it has hidden details
3289 // (because when looking for the details, it is skipped, too).
3290 // Also skip if the member is invisible because it has no data,
3291 // for consistent ordering.
3292 if ( pRowMember->HasHiddenDetails() || !pRowMember->IsVisible() )
3293 pRowMember = NULL;
3296 if ( pRowMember )
3298 pColMember = pRowMember->GetDataRoot();
3300 const long* pNextColIndex = pColIndexes;
3301 while ( *pNextColIndex >= 0 && pColMember )
3303 ScDPDataDimension* pColChild = pColMember->GetChildDimension();
3304 if ( pColChild && *pNextColIndex < pColChild->GetMemberCount() )
3305 pColMember = pColChild->GetMember( *pNextColIndex );
3306 else
3307 pColMember = NULL;
3308 ++pNextColIndex;
3312 // continue searching only if looking for first existing or relative position
3313 bContinue = ( pColMember == NULL && ( bFirstExisting || pRelativePos ) );
3314 nMemberIndex += nDirection;
3317 return pColMember;
3320 ScDPDataMember* ScDPResultDimension::GetColReferenceMember(
3321 const ScDPRelativePos* pRelativePos, const OUString* pName,
3322 long nRefDimPos, const ScDPRunningTotalState& rRunning )
3324 OSL_ENSURE( pRelativePos == NULL || pName == NULL, "can't use position and name" );
3326 const long* pColIndexes = &rRunning.GetColSorted()[0];
3327 const long* pRowIndexes = &rRunning.GetRowSorted()[0];
3329 // get own row member using all indexes
3331 const ScDPResultMember* pRowMember = rRunning.GetRowResRoot();
3332 ScDPDataMember* pColMember = NULL;
3334 const long* pNextRowIndex = pRowIndexes;
3335 while ( *pNextRowIndex >= 0 && pRowMember )
3337 const ScDPResultDimension* pRowChild = pRowMember->GetChildDimension();
3338 if ( pRowChild && *pNextRowIndex < pRowChild->GetMemberCount() )
3339 pRowMember = pRowChild->GetMember( *pNextRowIndex );
3340 else
3341 pRowMember = NULL;
3342 ++pNextRowIndex;
3345 // get column (data) members before the reference field
3346 //TODO: pass rRowParent from ScDPDataMember::UpdateRunningTotals instead
3348 if ( pRowMember )
3350 pColMember = pRowMember->GetDataRoot();
3352 const long* pNextColIndex = pColIndexes;
3353 long nColSkipped = 0;
3354 while ( *pNextColIndex >= 0 && pColMember && nColSkipped < nRefDimPos )
3356 ScDPDataDimension* pColChild = pColMember->GetChildDimension();
3357 if ( pColChild && *pNextColIndex < pColChild->GetMemberCount() )
3358 pColMember = pColChild->GetMember( *pNextColIndex );
3359 else
3360 pColMember = NULL;
3361 ++pNextColIndex;
3362 ++nColSkipped;
3366 // get column member for the reference field
3368 if ( pColMember )
3370 ScDPDataDimension* pReferenceDim = pColMember->GetChildDimension();
3371 if ( pReferenceDim )
3373 long nReferenceCount = pReferenceDim->GetMemberCount();
3375 bool bFirstExisting = ( pRelativePos == NULL && pName == NULL );
3376 long nMemberIndex = 0; // unsorted
3377 long nDirection = 1; // forward if no relative position is used
3378 pColMember = NULL; // don't use parent dimension's member if none found
3379 if ( pRelativePos )
3381 nDirection = pRelativePos->nDirection;
3382 nMemberIndex = pRelativePos->nBasePos + nDirection; // bounds are handled below
3384 else if ( pName )
3386 // search for named member
3388 pColMember = pReferenceDim->GetMember( pReferenceDim->GetSortedIndex( nMemberIndex ) );
3390 //TODO: use ScDPItemData, as in ScDPDimension::IsValidPage?
3391 while ( pColMember && pColMember->GetName() != *pName )
3393 ++nMemberIndex;
3394 if ( nMemberIndex < nReferenceCount )
3395 pColMember = pReferenceDim->GetMember( pReferenceDim->GetSortedIndex( nMemberIndex ) );
3396 else
3397 pColMember = NULL;
3401 bool bContinue = true;
3402 while ( bContinue && nMemberIndex >= 0 && nMemberIndex < nReferenceCount )
3404 pColMember = pReferenceDim->GetMember( pReferenceDim->GetSortedIndex( nMemberIndex ) );
3406 // get column members below the reference field
3408 const long* pNextColIndex = pColIndexes + nRefDimPos + 1;
3409 while ( *pNextColIndex >= 0 && pColMember )
3411 ScDPDataDimension* pColChild = pColMember->GetChildDimension();
3412 if ( pColChild && *pNextColIndex < pColChild->GetMemberCount() )
3413 pColMember = pColChild->GetMember( *pNextColIndex );
3414 else
3415 pColMember = NULL;
3416 ++pNextColIndex;
3419 if ( pColMember && pRelativePos )
3421 // Skip the member if it has hidden details
3422 // (because when looking for the details, it is skipped, too).
3423 // Also skip if the member is invisible because it has no data,
3424 // for consistent ordering.
3425 if ( pColMember->HasHiddenDetails() || !pColMember->IsVisible() )
3426 pColMember = NULL;
3429 // continue searching only if looking for first existing or relative position
3430 bContinue = ( pColMember == NULL && ( bFirstExisting || pRelativePos ) );
3431 nMemberIndex += nDirection;
3434 else
3435 pColMember = NULL;
3438 return pColMember;
3441 #if DEBUG_PIVOT_TABLE
3442 void ScDPResultDimension::DumpState( const ScDPResultMember* pRefMember, ScDocument* pDoc, ScAddress& rPos ) const
3444 OUString aDimName = bIsDataLayout ? OUString("(data layout)") : OUString(GetName());
3445 lcl_DumpRow( OUString("ScDPResultDimension"), aDimName, NULL, pDoc, rPos );
3447 SCROW nStartRow = rPos.Row();
3449 long nCount = bIsDataLayout ? 1 : maMemberArray.size();
3450 for (long i=0; i<nCount; i++)
3452 const ScDPResultMember* pMember = maMemberArray[i];
3453 pMember->DumpState( pRefMember, pDoc, rPos );
3456 lcl_Indent( pDoc, nStartRow, rPos );
3459 void ScDPResultDimension::Dump(int nIndent) const
3461 std::string aIndent(nIndent*2, ' ');
3462 std::cout << aIndent << "-- dimension '" << GetName() << "'" << std::endl;
3463 MemberArray::const_iterator it = maMemberArray.begin(), itEnd = maMemberArray.end();
3464 for (; it != itEnd; ++it)
3466 const ScDPResultMember* p = *it;
3467 p->Dump(nIndent+1);
3470 #endif
3472 long ScDPResultDimension::GetMemberCount() const
3474 return maMemberArray.size();
3477 const ScDPResultMember* ScDPResultDimension::GetMember(long n) const
3479 return maMemberArray[n];
3481 ScDPResultMember* ScDPResultDimension::GetMember(long n)
3483 return maMemberArray[n];
3486 ScDPResultDimension* ScDPResultDimension::GetFirstChildDimension() const
3488 if ( maMemberArray.size() > 0 )
3489 return maMemberArray[0]->GetChildDimension();
3490 else
3491 return NULL;
3494 void ScDPResultDimension::FillVisibilityData(ScDPResultVisibilityData& rData) const
3496 if (IsDataLayout())
3497 return;
3499 MemberArray::const_iterator itr = maMemberArray.begin(), itrEnd = maMemberArray.end();
3501 for (;itr != itrEnd; ++itr)
3503 ScDPResultMember* pMember = *itr;
3504 if (pMember->IsValid())
3506 ScDPItemData aItem;
3507 pMember->FillItemData(aItem);
3508 rData.addVisibleMember(GetName(), aItem);
3509 pMember->FillVisibilityData(rData);
3514 ScDPDataDimension::ScDPDataDimension( const ScDPResultData* pData ) :
3515 pResultData( pData ),
3516 pResultDimension( NULL ),
3517 bIsDataLayout( false )
3521 ScDPDataDimension::~ScDPDataDimension()
3523 std::for_each(maMembers.begin(), maMembers.end(), boost::checked_deleter<ScDPDataMember>());
3526 void ScDPDataDimension::InitFrom( const ScDPResultDimension* pDim )
3528 if (!pDim)
3529 return;
3531 pResultDimension = pDim;
3532 bIsDataLayout = pDim->IsDataLayout();
3534 // Go through all result members under the given result dimension, and
3535 // create a new data member instance for each result member.
3536 long nCount = pDim->GetMemberCount();
3537 for (long i=0; i<nCount; i++)
3539 const ScDPResultMember* pResMem = pDim->GetMember(i);
3541 ScDPDataMember* pNew = new ScDPDataMember( pResultData, pResMem );
3542 maMembers.push_back( pNew);
3544 if ( !pResultData->IsLateInit() )
3546 // with LateInit, pResMem hasn't necessarily been initialized yet,
3547 // so InitFrom for the new result member is called from its ProcessData method
3549 const ScDPResultDimension* pChildDim = pResMem->GetChildDimension();
3550 if ( pChildDim )
3551 pNew->InitFrom( pChildDim );
3556 void ScDPDataDimension::ProcessData( const vector< SCROW >& aDataMembers, const vector<ScDPValue>& aValues,
3557 const ScDPSubTotalState& rSubState )
3559 // the ScDPItemData array must contain enough entries for all dimensions - this isn't checked
3561 long nCount = maMembers.size();
3562 for (long i=0; i<nCount; i++)
3564 ScDPDataMember* pMember = maMembers[(sal_uInt16)i];
3566 // always first member for data layout dim
3567 if ( bIsDataLayout || ( !aDataMembers.empty() && pMember->IsNamedItem(aDataMembers[0]) ) )
3569 vector<SCROW> aChildDataMembers;
3570 if (aDataMembers.size() > 1)
3572 vector<SCROW>::const_iterator itr = aDataMembers.begin();
3573 aChildDataMembers.insert(aChildDataMembers.begin(), ++itr, aDataMembers.end());
3575 pMember->ProcessData( aChildDataMembers, aValues, rSubState );
3576 return;
3580 OSL_FAIL("ProcessData: Member not found");
3583 void ScDPDataDimension::FillDataRow(
3584 const ScDPResultDimension* pRefDim, ScDPResultFilterContext& rFilterCxt,
3585 uno::Sequence<sheet::DataResult>& rSequence, long nMeasure, bool bIsSubTotalRow,
3586 const ScDPSubTotalState& rSubState) const
3588 OUString aDimName;
3589 bool bDataLayout = false;
3590 if (pResultDimension)
3592 aDimName = pResultDimension->GetName();
3593 bDataLayout = pResultDimension->IsDataLayout();
3596 FilterStack aFilterStack(rFilterCxt.maFilters);
3597 aFilterStack.pushDimName(aDimName, bDataLayout);
3599 OSL_ENSURE( pRefDim && static_cast<size_t>(pRefDim->GetMemberCount()) == maMembers.size(), "dimensions don't match" );
3600 OSL_ENSURE( pRefDim == pResultDimension, "wrong dim" );
3602 const ScMemberSortOrder& rMemberOrder = pRefDim->GetMemberOrder();
3604 long nMemberMeasure = nMeasure;
3605 long nCount = maMembers.size();
3606 for (long i=0; i<nCount; i++)
3608 long nSorted = rMemberOrder.empty() ? i : rMemberOrder[i];
3610 long nMemberPos = nSorted;
3611 if (bIsDataLayout)
3613 OSL_ENSURE(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1,
3614 "DataLayout dimension twice?");
3615 nMemberPos = 0;
3616 nMemberMeasure = nSorted;
3619 const ScDPResultMember* pRefMember = pRefDim->GetMember(nMemberPos);
3620 if ( pRefMember->IsVisible() ) //TODO: here or in ScDPDataMember::FillDataRow ???
3622 const ScDPDataMember* pDataMember = maMembers[(sal_uInt16)nMemberPos];
3623 pDataMember->FillDataRow(pRefMember, rFilterCxt, rSequence, nMemberMeasure, bIsSubTotalRow, rSubState);
3628 void ScDPDataDimension::UpdateDataRow( const ScDPResultDimension* pRefDim,
3629 long nMeasure, bool bIsSubTotalRow,
3630 const ScDPSubTotalState& rSubState ) const
3632 OSL_ENSURE( pRefDim && static_cast<size_t>(pRefDim->GetMemberCount()) == maMembers.size(), "dimensions don't match" );
3633 OSL_ENSURE( pRefDim == pResultDimension, "wrong dim" );
3635 long nMemberMeasure = nMeasure;
3636 long nCount = maMembers.size();
3637 for (long i=0; i<nCount; i++)
3639 long nMemberPos = i;
3640 if (bIsDataLayout)
3642 OSL_ENSURE(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1,
3643 "DataLayout dimension twice?");
3644 nMemberPos = 0;
3645 nMemberMeasure = i;
3648 // Calculate must be called even if the member is not visible (for use as reference value)
3649 const ScDPResultMember* pRefMember = pRefDim->GetMember(nMemberPos);
3650 ScDPDataMember* pDataMember = maMembers[(sal_uInt16)nMemberPos];
3651 pDataMember->UpdateDataRow( pRefMember, nMemberMeasure, bIsSubTotalRow, rSubState );
3655 void ScDPDataDimension::SortMembers( ScDPResultDimension* pRefDim )
3657 long nCount = maMembers.size();
3659 if ( pRefDim->IsSortByData() )
3661 // sort members
3663 ScMemberSortOrder& rMemberOrder = pRefDim->GetMemberOrder();
3664 OSL_ENSURE( rMemberOrder.empty(), "sort twice?" );
3665 rMemberOrder.resize( nCount );
3666 for (long nPos=0; nPos<nCount; nPos++)
3667 rMemberOrder[nPos] = nPos;
3669 ScDPColMembersOrder aComp( *this, pRefDim->GetSortMeasure(), pRefDim->IsSortAscending() );
3670 ::std::sort( rMemberOrder.begin(), rMemberOrder.end(), aComp );
3673 // handle children
3675 OSL_ENSURE( pRefDim && static_cast<size_t>(pRefDim->GetMemberCount()) == maMembers.size(), "dimensions don't match" );
3676 OSL_ENSURE( pRefDim == pResultDimension, "wrong dim" );
3678 // for data layout, call only once - sorting measure is always taken from settings
3679 long nLoopCount = bIsDataLayout ? 1 : nCount;
3680 for (long i=0; i<nLoopCount; i++)
3682 ScDPResultMember* pRefMember = pRefDim->GetMember(i);
3683 if ( pRefMember->IsVisible() ) //TODO: here or in ScDPDataMember ???
3685 ScDPDataMember* pDataMember = maMembers[(sal_uInt16)i];
3686 pDataMember->SortMembers( pRefMember );
3691 void ScDPDataDimension::DoAutoShow( ScDPResultDimension* pRefDim )
3693 long nCount = maMembers.size();
3695 // handle children first, before changing the visible state
3697 OSL_ENSURE( pRefDim && static_cast<size_t>(pRefDim->GetMemberCount()) == maMembers.size(), "dimensions don't match" );
3698 OSL_ENSURE( pRefDim == pResultDimension, "wrong dim" );
3700 // for data layout, call only once - sorting measure is always taken from settings
3701 long nLoopCount = bIsDataLayout ? 1 : nCount;
3702 for (long i=0; i<nLoopCount; i++)
3704 ScDPResultMember* pRefMember = pRefDim->GetMember(i);
3705 if ( pRefMember->IsVisible() ) //TODO: here or in ScDPDataMember ???
3707 ScDPDataMember* pDataMember = maMembers[i];
3708 pDataMember->DoAutoShow( pRefMember );
3712 if ( pRefDim->IsAutoShow() && pRefDim->GetAutoCount() > 0 && pRefDim->GetAutoCount() < nCount )
3714 // establish temporary order, hide remaining members
3716 ScMemberSortOrder aAutoOrder;
3717 aAutoOrder.resize( nCount );
3718 long nPos;
3719 for (nPos=0; nPos<nCount; nPos++)
3720 aAutoOrder[nPos] = nPos;
3722 ScDPColMembersOrder aComp( *this, pRefDim->GetAutoMeasure(), !pRefDim->IsAutoTopItems() );
3723 ::std::sort( aAutoOrder.begin(), aAutoOrder.end(), aComp );
3725 // look for equal values to the last included one
3727 long nIncluded = pRefDim->GetAutoCount();
3728 ScDPDataMember* pDataMember1 = maMembers[aAutoOrder[nIncluded - 1]];
3729 if ( !pDataMember1->IsVisible() )
3730 pDataMember1 = NULL;
3731 bool bContinue = true;
3732 while ( bContinue )
3734 bContinue = false;
3735 if ( nIncluded < nCount )
3737 ScDPDataMember* pDataMember2 = maMembers[aAutoOrder[nIncluded]];
3738 if ( !pDataMember2->IsVisible() )
3739 pDataMember2 = NULL;
3741 if ( lcl_IsEqual( pDataMember1, pDataMember2, pRefDim->GetAutoMeasure() ) )
3743 ++nIncluded; // include more members if values are equal
3744 bContinue = true;
3749 // hide the remaining members
3751 for (nPos = nIncluded; nPos < nCount; nPos++)
3753 ScDPResultMember* pMember = pRefDim->GetMember(aAutoOrder[nPos]);
3754 pMember->SetAutoHidden();
3759 void ScDPDataDimension::ResetResults()
3761 long nCount = maMembers.size();
3762 for (long i=0; i<nCount; i++)
3764 // sort order doesn't matter
3766 long nMemberPos = bIsDataLayout ? 0 : i;
3767 ScDPDataMember* pDataMember = maMembers[nMemberPos];
3768 pDataMember->ResetResults();
3772 long ScDPDataDimension::GetSortedIndex( long nUnsorted ) const
3774 if (!pResultDimension)
3775 return nUnsorted;
3777 const ScMemberSortOrder& rMemberOrder = pResultDimension->GetMemberOrder();
3778 return rMemberOrder.empty() ? nUnsorted : rMemberOrder[nUnsorted];
3781 void ScDPDataDimension::UpdateRunningTotals( const ScDPResultDimension* pRefDim,
3782 long nMeasure, bool bIsSubTotalRow,
3783 const ScDPSubTotalState& rSubState, ScDPRunningTotalState& rRunning,
3784 ScDPRowTotals& rTotals, const ScDPResultMember& rRowParent ) const
3786 OSL_ENSURE( pRefDim && static_cast<size_t>(pRefDim->GetMemberCount()) == maMembers.size(), "dimensions don't match" );
3787 OSL_ENSURE( pRefDim == pResultDimension, "wrong dim" );
3789 long nMemberMeasure = nMeasure;
3790 long nCount = maMembers.size();
3791 for (long i=0; i<nCount; i++)
3793 const ScMemberSortOrder& rMemberOrder = pRefDim->GetMemberOrder();
3794 long nSorted = rMemberOrder.empty() ? i : rMemberOrder[i];
3796 long nMemberPos = nSorted;
3797 if (bIsDataLayout)
3799 OSL_ENSURE(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1,
3800 "DataLayout dimension twice?");
3801 nMemberPos = 0;
3802 nMemberMeasure = nSorted;
3805 const ScDPResultMember* pRefMember = pRefDim->GetMember(nMemberPos);
3806 if ( pRefMember->IsVisible() )
3808 if ( bIsDataLayout )
3809 rRunning.AddColIndex( 0, 0 );
3810 else
3811 rRunning.AddColIndex( i, nSorted );
3813 ScDPDataMember* pDataMember = maMembers[nMemberPos];
3814 pDataMember->UpdateRunningTotals(
3815 pRefMember, nMemberMeasure, bIsSubTotalRow, rSubState, rRunning, rTotals, rRowParent);
3817 rRunning.RemoveColIndex();
3822 #if DEBUG_PIVOT_TABLE
3823 void ScDPDataDimension::DumpState( const ScDPResultDimension* pRefDim, ScDocument* pDoc, ScAddress& rPos ) const
3825 OUString aDimName = bIsDataLayout ? OUString("(data layout)") : OUString("(unknown)");
3826 lcl_DumpRow( OUString("ScDPDataDimension"), aDimName, NULL, pDoc, rPos );
3828 SCROW nStartRow = rPos.Row();
3830 long nCount = bIsDataLayout ? 1 : maMembers.size();
3831 for (long i=0; i<nCount; i++)
3833 const ScDPResultMember* pRefMember = pRefDim->GetMember(i);
3834 const ScDPDataMember* pDataMember = maMembers[i];
3835 pDataMember->DumpState( pRefMember, pDoc, rPos );
3838 lcl_Indent( pDoc, nStartRow, rPos );
3841 void ScDPDataDimension::Dump(int nIndent) const
3843 std::string aIndent(nIndent*2, ' ');
3844 std::cout << aIndent << "-- data dimension '"
3845 << (pResultDimension ? pResultDimension->GetName() : OUString()) << "'" << std::endl;
3846 ScDPDataMembers::const_iterator it = maMembers.begin(), itEnd = maMembers.end();
3847 for (; it != itEnd; ++it)
3848 (*it)->Dump(nIndent+1);
3850 #endif
3852 long ScDPDataDimension::GetMemberCount() const
3854 return maMembers.size();
3857 const ScDPDataMember* ScDPDataDimension::GetMember(long n) const
3859 return maMembers[n];
3862 ScDPDataMember* ScDPDataDimension::GetMember(long n)
3864 return maMembers[n];
3867 ScDPResultVisibilityData::ScDPResultVisibilityData(
3868 ScDPSource* pSource) :
3869 mpSource(pSource)
3873 ScDPResultVisibilityData::~ScDPResultVisibilityData()
3877 void ScDPResultVisibilityData::addVisibleMember(const OUString& rDimName, const ScDPItemData& rMemberItem)
3879 DimMemberType::iterator itr = maDimensions.find(rDimName);
3880 if (itr == maDimensions.end())
3882 pair<DimMemberType::iterator, bool> r = maDimensions.insert(
3883 DimMemberType::value_type(rDimName, VisibleMemberType()));
3885 if (!r.second)
3886 // insertion failed.
3887 return;
3889 itr = r.first;
3891 VisibleMemberType& rMem = itr->second;
3892 VisibleMemberType::iterator itrMem = rMem.find(rMemberItem);
3893 if (itrMem == rMem.end())
3894 rMem.insert(rMemberItem);
3897 void ScDPResultVisibilityData::fillFieldFilters(vector<ScDPFilteredCache::Criterion>& rFilters) const
3899 typedef std::unordered_map<OUString, long, OUStringHash> FieldNameMapType;
3900 FieldNameMapType aFieldNames;
3901 ScDPTableData* pData = mpSource->GetData();
3902 long nColumnCount = pData->GetColumnCount();
3903 for (long i = 0; i < nColumnCount; ++i)
3905 aFieldNames.insert(
3906 FieldNameMapType::value_type(pData->getDimensionName(i), i));
3909 const ScDPDimensions* pDims = mpSource->GetDimensionsObject();
3910 for (DimMemberType::const_iterator itr = maDimensions.begin(), itrEnd = maDimensions.end();
3911 itr != itrEnd; ++itr)
3913 const OUString& rDimName = itr->first;
3914 ScDPFilteredCache::Criterion aCri;
3915 FieldNameMapType::const_iterator itrField = aFieldNames.find(rDimName);
3916 if (itrField == aFieldNames.end())
3917 // This should never happen!
3918 continue;
3920 long nDimIndex = itrField->second;
3921 aCri.mnFieldIndex = static_cast<sal_Int32>(nDimIndex);
3922 aCri.mpFilter.reset(new ScDPFilteredCache::GroupFilter);
3924 ScDPFilteredCache::GroupFilter* pGrpFilter =
3925 static_cast<ScDPFilteredCache::GroupFilter*>(aCri.mpFilter.get());
3927 const VisibleMemberType& rMem = itr->second;
3928 for (VisibleMemberType::const_iterator itrMem = rMem.begin(), itrMemEnd = rMem.end();
3929 itrMem != itrMemEnd; ++itrMem)
3931 const ScDPItemData& rMemItem = *itrMem;
3932 pGrpFilter->addMatchItem(rMemItem);
3935 ScDPDimension* pDim = pDims->getByIndex(nDimIndex);
3936 ScDPMembers* pMembers = pDim->GetHierarchiesObject()->getByIndex(0)->
3937 GetLevelsObject()->getByIndex(0)->GetMembersObject();
3938 if (pGrpFilter->getMatchItemCount() < static_cast<size_t>(pMembers->getCount()))
3939 rFilters.push_back(aCri);
3943 size_t ScDPResultVisibilityData::MemberHash::operator() (const ScDPItemData& r) const
3945 if (r.IsValue())
3946 return static_cast<size_t>(::rtl::math::approxFloor(r.GetValue()));
3947 else
3948 return r.GetString().hashCode();
3950 SCROW ScDPResultMember::GetDataId( ) const
3952 const ScDPMember* pMemberDesc = GetDPMember();
3953 if (pMemberDesc)
3954 return pMemberDesc->GetItemDataId();
3955 return -1;
3958 ScDPResultMember* ScDPResultDimension::AddMember(const ScDPParentDimData &aData )
3960 ScDPResultMember* pMember = new ScDPResultMember( pResultData, aData, false );
3961 SCROW nDataIndex = pMember->GetDataId();
3962 maMemberArray.push_back( pMember );
3964 if ( maMemberHash.end() == maMemberHash.find( nDataIndex ) )
3965 maMemberHash.insert( std::pair< SCROW, ScDPResultMember *>( nDataIndex, pMember ) );
3966 return pMember;
3969 ScDPResultMember* ScDPResultDimension::InsertMember(ScDPParentDimData *pMemberData)
3971 SCROW nInsert = 0;
3972 if ( !lcl_SearchMember( maMemberArray, pMemberData->mnOrder , nInsert ) )
3974 ScDPResultMember* pNew = new ScDPResultMember( pResultData, *pMemberData, false );
3975 maMemberArray.insert( maMemberArray.begin()+nInsert, pNew );
3977 SCROW nDataIndex = pMemberData->mpMemberDesc->GetItemDataId();
3978 if ( maMemberHash.end() == maMemberHash.find( nDataIndex ) )
3979 maMemberHash.insert( std::pair< SCROW, ScDPResultMember *>( nDataIndex, pNew ) );
3980 return pNew;
3982 return maMemberArray[ nInsert ];
3985 void ScDPResultDimension::InitWithMembers(
3986 LateInitParams& rParams, const std::vector<SCROW>& pItemData, size_t nPos,
3987 ScDPInitState& rInitState)
3989 if ( rParams.IsEnd( nPos ) )
3990 return;
3991 ScDPDimension* pThisDim = rParams.GetDim( nPos );
3992 ScDPLevel* pThisLevel = rParams.GetLevel( nPos );
3993 SCROW nDataID = pItemData[nPos];
3995 if (pThisDim && pThisLevel)
3997 long nDimSource = pThisDim->GetDimension(); //TODO: check GetSourceDim?
3999 // create all members at the first call (preserve order)
4000 ResultMembers* pMembers = pResultData->GetDimResultMembers(nDimSource, pThisDim, pThisLevel);
4001 ScDPGroupCompare aCompare( pResultData, rInitState, nDimSource );
4002 // initialize only specific member (or all if "show empty" flag is set)
4003 ScDPResultMember* pResultMember = NULL;
4004 if ( bInitialized )
4005 pResultMember = FindMember( nDataID );
4006 else
4007 bInitialized = true;
4009 if ( pResultMember == NULL )
4010 { //only insert found item
4011 ScDPParentDimData* pMemberData = pMembers->FindMember( nDataID );
4012 if ( pMemberData && aCompare.IsIncluded( *( pMemberData->mpMemberDesc ) ) )
4013 pResultMember = InsertMember( pMemberData );
4015 if ( pResultMember )
4017 rInitState.AddMember( nDimSource, pResultMember->GetDataId() );
4018 pResultMember->LateInitFrom(rParams, pItemData, nPos+1, rInitState);
4019 rInitState.RemoveMember();
4024 ScDPParentDimData::ScDPParentDimData() :
4025 mnOrder(-1), mpParentDim(NULL), mpParentLevel(NULL), mpMemberDesc(NULL) {}
4027 ScDPParentDimData::ScDPParentDimData(
4028 SCROW nIndex, const ScDPDimension* pDim, const ScDPLevel* pLev, const ScDPMember* pMember) :
4029 mnOrder(nIndex), mpParentDim(pDim), mpParentLevel(pLev), mpMemberDesc(pMember) {}
4031 ScDPParentDimData* ResultMembers::FindMember( SCROW nIndex ) const
4033 DimMemberHash::const_iterator aRes = maMemberHash.find( nIndex );
4034 if( aRes != maMemberHash.end()) {
4035 if ( aRes->second->mpMemberDesc && aRes->second->mpMemberDesc->GetItemDataId()==nIndex )
4036 return aRes->second;
4038 return NULL;
4040 void ResultMembers::InsertMember( ScDPParentDimData* pNew )
4042 if ( !pNew->mpMemberDesc->getShowDetails() )
4043 mbHasHideDetailsMember = true;
4044 maMemberHash.insert( std::pair< const SCROW, ScDPParentDimData *>( pNew->mpMemberDesc->GetItemDataId(), pNew ) );
4047 ResultMembers::ResultMembers():
4048 mbHasHideDetailsMember( false )
4051 ResultMembers::~ResultMembers()
4053 for ( DimMemberHash::const_iterator iter = maMemberHash.begin(); iter != maMemberHash.end(); ++iter )
4054 delete iter->second;
4057 LateInitParams::LateInitParams(
4058 const vector<ScDPDimension*>& ppDim, const vector<ScDPLevel*>& ppLev, bool bRow, bool bInitChild, bool bAllChildren ) :
4059 mppDim( ppDim ),
4060 mppLev( ppLev ),
4061 mbRow( bRow ),
4062 mbInitChild( bInitChild ),
4063 mbAllChildren( bAllChildren )
4067 LateInitParams::~LateInitParams()
4071 bool LateInitParams::IsEnd( size_t nPos ) const
4073 return nPos >= mppDim.size();
4076 void ScDPResultDimension::CheckShowEmpty( bool bShow )
4078 long nCount = maMemberArray.size();
4080 ScDPResultMember* pMember = NULL;
4081 for (long i=0; i<nCount; i++)
4083 pMember = maMemberArray.at(i);
4084 pMember->CheckShowEmpty(bShow);
4089 void ScDPResultMember::CheckShowEmpty( bool bShow )
4091 if (bHasElements)
4093 ScDPResultDimension* pChildDim = GetChildDimension();
4094 if (pChildDim)
4095 pChildDim->CheckShowEmpty();
4097 else if (IsValid() && bInitialized)
4099 bShow = bShow || (GetParentLevel() && GetParentLevel()->getShowEmpty());
4100 if (bShow)
4102 SetHasElements();
4103 ScDPResultDimension* pChildDim = GetChildDimension();
4104 if (pChildDim)
4105 pChildDim->CheckShowEmpty(true);
4110 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */