re-enabled user-defined numeric fields for dBase export
[LibreOffice.git] / sc / source / core / data / dptabres.cxx
blob08a79870af1a03ece9e0ded94053d4a2e54dc645
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" // for DumpState only!
30 #include "stlalgorithm.hxx"
31 #include "dpresfilter.hxx"
32 #include "dputil.hxx"
34 #include <osl/diagnose.h>
35 #include <rtl/math.hxx>
36 #include <rtl/strbuf.hxx>
38 #include <math.h>
39 #include <float.h> //! Test !!!
40 #include <algorithm>
41 #include <boost/unordered_map.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] = // passend zum 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();
133 // function objects for sorting of the column and row members:
136 class ScDPRowMembersOrder
138 ScDPResultDimension& rDimension;
139 long nMeasure;
140 bool bAscending;
142 public:
143 ScDPRowMembersOrder( ScDPResultDimension& rDim, long nM, bool bAsc ) :
144 rDimension(rDim),
145 nMeasure(nM),
146 bAscending(bAsc)
148 ~ScDPRowMembersOrder() {}
150 bool operator()( sal_Int32 nIndex1, sal_Int32 nIndex2 ) const;
153 class ScDPColMembersOrder
155 ScDPDataDimension& rDimension;
156 long nMeasure;
157 bool bAscending;
159 public:
160 ScDPColMembersOrder( ScDPDataDimension& rDim, long nM, bool bAsc ) :
161 rDimension(rDim),
162 nMeasure(nM),
163 bAscending(bAsc)
165 ~ScDPColMembersOrder() {}
167 bool operator()( sal_Int32 nIndex1, sal_Int32 nIndex2 ) const;
170 static bool lcl_IsLess( const ScDPDataMember* pDataMember1, const ScDPDataMember* pDataMember2, long nMeasure, bool bAscending )
172 // members can be NULL if used for rows
174 ScDPSubTotalState aEmptyState;
175 const ScDPAggData* pAgg1 = pDataMember1 ? pDataMember1->GetConstAggData( nMeasure, aEmptyState ) : NULL;
176 const ScDPAggData* pAgg2 = pDataMember2 ? pDataMember2->GetConstAggData( nMeasure, aEmptyState ) : NULL;
178 bool bError1 = pAgg1 && pAgg1->HasError();
179 bool bError2 = pAgg2 && pAgg2->HasError();
180 if ( bError1 )
181 return false; // errors are always sorted at the end
182 else if ( bError2 )
183 return true; // errors are always sorted at the end
184 else
186 double fVal1 = ( pAgg1 && pAgg1->HasData() ) ? pAgg1->GetResult() : 0.0; // no data is sorted as 0
187 double fVal2 = ( pAgg2 && pAgg2->HasData() ) ? pAgg2->GetResult() : 0.0;
189 // compare values
190 // don't have to check approxEqual, as this is the only sort criterion
192 return bAscending ? ( fVal1 < fVal2 ) : ( fVal1 > fVal2 );
196 static bool lcl_IsEqual( const ScDPDataMember* pDataMember1, const ScDPDataMember* pDataMember2, long nMeasure )
198 // members can be NULL if used for rows
200 ScDPSubTotalState aEmptyState;
201 const ScDPAggData* pAgg1 = pDataMember1 ? pDataMember1->GetConstAggData( nMeasure, aEmptyState ) : NULL;
202 const ScDPAggData* pAgg2 = pDataMember2 ? pDataMember2->GetConstAggData( nMeasure, aEmptyState ) : NULL;
204 bool bError1 = pAgg1 && pAgg1->HasError();
205 bool bError2 = pAgg2 && pAgg2->HasError();
206 if ( bError1 )
208 if ( bError2 )
209 return true; // equal
210 else
211 return false;
213 else if ( bError2 )
214 return false;
215 else
217 double fVal1 = ( pAgg1 && pAgg1->HasData() ) ? pAgg1->GetResult() : 0.0; // no data is sorted as 0
218 double fVal2 = ( pAgg2 && pAgg2->HasData() ) ? pAgg2->GetResult() : 0.0;
220 // compare values
221 // this is used to find equal data at the end of the AutoShow range, so approxEqual must be used
223 return rtl::math::approxEqual( fVal1, fVal2 );
227 bool ScDPRowMembersOrder::operator()( sal_Int32 nIndex1, sal_Int32 nIndex2 ) const
229 const ScDPResultMember* pMember1 = rDimension.GetMember(nIndex1);
230 const ScDPResultMember* pMember2 = rDimension.GetMember(nIndex2);
232 // make the hide item to the largest order.
233 if ( !pMember1->IsVisible() || !pMember2->IsVisible() )
234 return pMember1->IsVisible();
235 const ScDPDataMember* pDataMember1 = pMember1->GetDataRoot() ;
236 const ScDPDataMember* pDataMember2 = pMember2->GetDataRoot();
237 // GetDataRoot can be NULL if there was no data.
238 // IsVisible == false can happen after AutoShow.
239 return lcl_IsLess( pDataMember1, pDataMember2, nMeasure, bAscending );
242 bool ScDPColMembersOrder::operator()( sal_Int32 nIndex1, sal_Int32 nIndex2 ) const
244 const ScDPDataMember* pDataMember1 = rDimension.GetMember(nIndex1);
245 const ScDPDataMember* pDataMember2 = rDimension.GetMember(nIndex2);
246 bool bHide1 = pDataMember1 && !pDataMember1->IsVisible();
247 bool bHide2 = pDataMember2 && !pDataMember2->IsVisible();
248 if ( bHide1 || bHide2 )
249 return !bHide1;
250 return lcl_IsLess( pDataMember1, pDataMember2, nMeasure, bAscending );
253 ScDPInitState::Member::Member(long nSrcIndex, SCROW nNameIndex) :
254 mnSrcIndex(nSrcIndex), mnNameIndex(nNameIndex) {}
256 void ScDPInitState::AddMember( long nSourceIndex, SCROW nMember )
258 maMembers.push_back(Member(nSourceIndex, nMember));
261 void ScDPInitState::RemoveMember()
263 OSL_ENSURE(!maMembers.empty(), "ScDPInitState::RemoveMember: Attempt to remmove member while empty.");
264 if (!maMembers.empty())
265 maMembers.pop_back();
268 namespace {
270 #if DEBUG_PIVOT_TABLE
271 void lcl_DumpRow(
272 const OUString& rType, const OUString& rName, const ScDPAggData* pAggData,
273 ScDocument* pDoc, ScAddress& rPos )
275 SCCOL nCol = rPos.Col();
276 SCROW nRow = rPos.Row();
277 SCTAB nTab = rPos.Tab();
278 pDoc->SetString( nCol++, nRow, nTab, rType );
279 pDoc->SetString( nCol++, nRow, nTab, rName );
280 while ( pAggData )
282 pDoc->SetValue( nCol++, nRow, nTab, pAggData->GetResult() );
283 pAggData = pAggData->GetExistingChild();
285 rPos.SetRow( nRow + 1 );
288 void lcl_Indent( ScDocument* pDoc, SCROW nStartRow, const ScAddress& rPos )
290 SCCOL nCol = rPos.Col();
291 SCTAB nTab = rPos.Tab();
293 OUString aString;
294 for (SCROW nRow = nStartRow; nRow < rPos.Row(); nRow++)
296 aString = pDoc->GetString(nCol, nRow, nTab);
297 if (!aString.isEmpty())
299 aString = " " + aString;
300 pDoc->SetString( nCol, nRow, nTab, aString );
304 #endif
308 ScDPRunningTotalState::ScDPRunningTotalState( ScDPResultMember* pColRoot, ScDPResultMember* pRowRoot ) :
309 pColResRoot(pColRoot), pRowResRoot(pRowRoot)
311 // These arrays should never be empty as the terminating value must be present at all times.
312 maColVisible.push_back(-1);
313 maColSorted.push_back(-1);
314 maRowVisible.push_back(-1);
315 maRowSorted.push_back(-1);
318 const ScDPRunningTotalState::IndexArray& ScDPRunningTotalState::GetColVisible() const
320 return maColVisible;
323 const ScDPRunningTotalState::IndexArray& ScDPRunningTotalState::GetColSorted() const
325 return maColSorted;
328 const ScDPRunningTotalState::IndexArray& ScDPRunningTotalState::GetRowVisible() const
330 return maRowVisible;
333 const ScDPRunningTotalState::IndexArray& ScDPRunningTotalState::GetRowSorted() const
335 return maRowSorted;
338 void ScDPRunningTotalState::AddColIndex( long nVisible, long nSorted )
340 maColVisible.back() = nVisible;
341 maColVisible.push_back(-1);
343 maColSorted.back() = nSorted;
344 maColSorted.push_back(-1);
347 void ScDPRunningTotalState::AddRowIndex( long nVisible, long nSorted )
349 maRowVisible.back() = nVisible;
350 maRowVisible.push_back(-1);
352 maRowSorted.back() = nSorted;
353 maRowSorted.push_back(-1);
356 void ScDPRunningTotalState::RemoveColIndex()
358 OSL_ENSURE(!maColVisible.empty() && !maColSorted.empty(), "ScDPRunningTotalState::RemoveColIndex: array is already empty!");
359 if (maColVisible.size() >= 2)
361 maColVisible.pop_back();
362 maColVisible.back() = -1;
365 if (maColSorted.size() >= 2)
367 maColSorted.pop_back();
368 maColSorted.back() = -1;
372 void ScDPRunningTotalState::RemoveRowIndex()
374 OSL_ENSURE(!maRowVisible.empty() && !maRowSorted.empty(), "ScDPRunningTotalState::RemoveRowIndex: array is already empty!");
375 if (maRowVisible.size() >= 2)
377 maRowVisible.pop_back();
378 maRowVisible.back() = -1;
381 if (maRowSorted.size() >= 2)
383 maRowSorted.pop_back();
384 maRowSorted.back() = -1;
388 // -----------------------------------------------------------------------
390 ScDPRelativePos::ScDPRelativePos( long nBase, long nDir ) :
391 nBasePos( nBase ),
392 nDirection( nDir )
396 // -----------------------------------------------------------------------
398 void ScDPAggData::Update( const ScDPValue& rNext, ScSubTotalFunc eFunc, const ScDPSubTotalState& rSubState )
400 if (nCount<0) // error?
401 return; // nothing more...
403 if (rNext.meType == ScDPValue::Empty)
404 return;
406 if ( rSubState.eColForce != SUBTOTAL_FUNC_NONE && rSubState.eRowForce != SUBTOTAL_FUNC_NONE &&
407 rSubState.eColForce != rSubState.eRowForce )
408 return;
409 if ( rSubState.eColForce != SUBTOTAL_FUNC_NONE ) eFunc = rSubState.eColForce;
410 if ( rSubState.eRowForce != SUBTOTAL_FUNC_NONE ) eFunc = rSubState.eRowForce;
412 if ( eFunc == SUBTOTAL_FUNC_NONE )
413 return;
415 if ( eFunc != SUBTOTAL_FUNC_CNT2 ) // CNT2 counts everything, incl. strings and errors
417 if (rNext.meType == ScDPValue::Error)
419 nCount = -1; // -1 for error (not for CNT2)
420 return;
422 if (rNext.meType == ScDPValue::String)
423 return; // ignore
426 ++nCount; // for all functions
428 switch (eFunc)
430 case SUBTOTAL_FUNC_SUM:
431 case SUBTOTAL_FUNC_AVE:
432 if ( !SubTotal::SafePlus( fVal, rNext.mfValue ) )
433 nCount = -1; // -1 for error
434 break;
435 case SUBTOTAL_FUNC_PROD:
436 if ( nCount == 1 ) // copy first value (fVal is initialized to 0)
437 fVal = rNext.mfValue;
438 else if ( !SubTotal::SafeMult( fVal, rNext.mfValue ) )
439 nCount = -1; // -1 for error
440 break;
441 case SUBTOTAL_FUNC_CNT:
442 case SUBTOTAL_FUNC_CNT2:
443 // nothing more than incrementing nCount
444 break;
445 case SUBTOTAL_FUNC_MAX:
446 if ( nCount == 1 || rNext.mfValue > fVal )
447 fVal = rNext.mfValue;
448 break;
449 case SUBTOTAL_FUNC_MIN:
450 if ( nCount == 1 || rNext.mfValue < fVal )
451 fVal = rNext.mfValue;
452 break;
453 case SUBTOTAL_FUNC_STD:
454 case SUBTOTAL_FUNC_STDP:
455 case SUBTOTAL_FUNC_VAR:
456 case SUBTOTAL_FUNC_VARP:
458 // fAux is used to sum up squares
459 if ( !SubTotal::SafePlus( fVal, rNext.mfValue ) )
460 nCount = -1; // -1 for error
461 double fAdd = rNext.mfValue;
462 if ( !SubTotal::SafeMult( fAdd, rNext.mfValue ) ||
463 !SubTotal::SafePlus( fAux, fAdd ) )
464 nCount = -1; // -1 for error
466 break;
467 default:
468 OSL_FAIL("invalid function");
472 void ScDPAggData::Calculate( ScSubTotalFunc eFunc, const ScDPSubTotalState& rSubState )
474 // calculate the original result
475 // (without reference value, used as the basis for reference value calculation)
477 // called several times at the cross-section of several subtotals - don't calculate twice then
478 if ( IsCalculated() )
479 return;
481 if ( rSubState.eColForce != SUBTOTAL_FUNC_NONE ) eFunc = rSubState.eColForce;
482 if ( rSubState.eRowForce != SUBTOTAL_FUNC_NONE ) eFunc = rSubState.eRowForce;
484 if ( eFunc == SUBTOTAL_FUNC_NONE ) // this happens when there is no data dimension
486 nCount = SC_DPAGG_RESULT_EMPTY; // make sure there's a valid state for HasData etc.
487 return;
490 // check the error conditions for the selected function
492 bool bError = false;
493 switch (eFunc)
495 case SUBTOTAL_FUNC_SUM:
496 case SUBTOTAL_FUNC_PROD:
497 case SUBTOTAL_FUNC_CNT:
498 case SUBTOTAL_FUNC_CNT2:
499 bError = ( nCount < 0 ); // only real errors
500 break;
502 case SUBTOTAL_FUNC_AVE:
503 case SUBTOTAL_FUNC_MAX:
504 case SUBTOTAL_FUNC_MIN:
505 case SUBTOTAL_FUNC_STDP:
506 case SUBTOTAL_FUNC_VARP:
507 bError = ( nCount <= 0 ); // no data is an error
508 break;
510 case SUBTOTAL_FUNC_STD:
511 case SUBTOTAL_FUNC_VAR:
512 bError = ( nCount < 2 ); // need at least 2 values
513 break;
515 default:
516 OSL_FAIL("invalid function");
519 // calculate the selected function
521 double fResult = 0.0;
522 if ( !bError )
524 switch (eFunc)
526 case SUBTOTAL_FUNC_MAX:
527 case SUBTOTAL_FUNC_MIN:
528 case SUBTOTAL_FUNC_SUM:
529 case SUBTOTAL_FUNC_PROD:
530 // different error conditions are handled above
531 fResult = fVal;
532 break;
534 case SUBTOTAL_FUNC_CNT:
535 case SUBTOTAL_FUNC_CNT2:
536 fResult = nCount;
537 break;
539 case SUBTOTAL_FUNC_AVE:
540 if ( nCount > 0 )
541 fResult = fVal / (double) nCount;
542 break;
544 //! use safe mul for fVal * fVal
546 case SUBTOTAL_FUNC_STD:
547 if ( nCount >= 2 )
548 fResult = sqrt((fAux - fVal*fVal/(double)(nCount)) / (double)(nCount-1));
549 break;
550 case SUBTOTAL_FUNC_VAR:
551 if ( nCount >= 2 )
552 fResult = (fAux - fVal*fVal/(double)(nCount)) / (double)(nCount-1);
553 break;
554 case SUBTOTAL_FUNC_STDP:
555 if ( nCount > 0 )
556 fResult = sqrt((fAux - fVal*fVal/(double)(nCount)) / (double)nCount);
557 break;
558 case SUBTOTAL_FUNC_VARP:
559 if ( nCount > 0 )
560 fResult = (fAux - fVal*fVal/(double)(nCount)) / (double)nCount;
561 break;
562 default:
563 OSL_FAIL("invalid function");
567 bool bEmpty = ( nCount == 0 ); // no data
569 // store the result
570 // Empty is checked first, so empty results are shown empty even for "average" etc.
571 // If these results should be treated as errors in reference value calculations,
572 // a separate state value (EMPTY_ERROR) is needed.
573 // Now, for compatibility, empty "average" results are counted as 0.
575 if ( bEmpty )
576 nCount = SC_DPAGG_RESULT_EMPTY;
577 else if ( bError )
578 nCount = SC_DPAGG_RESULT_ERROR;
579 else
580 nCount = SC_DPAGG_RESULT_VALID;
582 if ( bEmpty || bError )
583 fResult = 0.0; // default, in case the state is later modified
585 fVal = fResult; // used directly from now on
586 fAux = 0.0; // used for running total or original result of reference value
589 bool ScDPAggData::IsCalculated() const
591 return ( nCount <= SC_DPAGG_RESULT_EMPTY );
594 double ScDPAggData::GetResult() const
596 OSL_ENSURE( IsCalculated(), "ScDPAggData not calculated" );
598 return fVal; // use calculated value
601 bool ScDPAggData::HasError() const
603 OSL_ENSURE( IsCalculated(), "ScDPAggData not calculated" );
605 return ( nCount == SC_DPAGG_RESULT_ERROR );
608 bool ScDPAggData::HasData() const
610 OSL_ENSURE( IsCalculated(), "ScDPAggData not calculated" );
612 return ( nCount != SC_DPAGG_RESULT_EMPTY ); // values or error
615 void ScDPAggData::SetResult( double fNew )
617 OSL_ENSURE( IsCalculated(), "ScDPAggData not calculated" );
619 fVal = fNew; // don't reset error flag
622 void ScDPAggData::SetError()
624 OSL_ENSURE( IsCalculated(), "ScDPAggData not calculated" );
626 nCount = SC_DPAGG_RESULT_ERROR;
629 void ScDPAggData::SetEmpty( bool bSet )
631 OSL_ENSURE( IsCalculated(), "ScDPAggData not calculated" );
633 if ( bSet )
634 nCount = SC_DPAGG_RESULT_EMPTY;
635 else
636 nCount = SC_DPAGG_RESULT_VALID;
639 double ScDPAggData::GetAuxiliary() const
641 // after Calculate, fAux is used as auxiliary value for running totals and reference values
642 OSL_ENSURE( IsCalculated(), "ScDPAggData not calculated" );
644 return fAux;
647 void ScDPAggData::SetAuxiliary( double fNew )
649 // after Calculate, fAux is used as auxiliary value for running totals and reference values
650 OSL_ENSURE( IsCalculated(), "ScDPAggData not calculated" );
652 fAux = fNew;
655 ScDPAggData* ScDPAggData::GetChild()
657 if (!pChild)
658 pChild = new ScDPAggData;
659 return pChild;
662 void ScDPAggData::Reset()
664 fVal = 0.0;
665 fAux = 0.0;
666 nCount = SC_DPAGG_EMPTY;
667 delete pChild;
668 pChild = NULL;
671 #if DEBUG_PIVOT_TABLE
672 void ScDPAggData::Dump(int nIndent) const
674 std::string aIndent(nIndent*2, ' ');
675 std::cout << aIndent << "* ";
676 if (IsCalculated())
677 std::cout << GetResult();
678 else
679 std::cout << "not calculated";
681 std::cout << " [val=" << fVal << "; aux=" << fAux << "; count=" << nCount << "]" << std::endl;
683 #endif
685 // -----------------------------------------------------------------------
687 ScDPRowTotals::ScDPRowTotals() :
688 bIsInColRoot( false )
692 ScDPRowTotals::~ScDPRowTotals()
696 static ScDPAggData* lcl_GetChildTotal( ScDPAggData* pFirst, long nMeasure )
698 OSL_ENSURE( nMeasure >= 0, "GetColTotal: no measure" );
700 ScDPAggData* pAgg = pFirst;
701 long nSkip = nMeasure;
703 // subtotal settings are ignored - colum/row totals exist once per measure
705 for ( long nPos=0; nPos<nSkip; nPos++ )
706 pAgg = pAgg->GetChild(); // column total is constructed empty - children need to be created
708 if ( !pAgg->IsCalculated() )
710 // for first use, simulate an empty calculation
711 ScDPSubTotalState aEmptyState;
712 pAgg->Calculate( SUBTOTAL_FUNC_SUM, aEmptyState );
715 return pAgg;
718 ScDPAggData* ScDPRowTotals::GetRowTotal( long nMeasure )
720 return lcl_GetChildTotal( &aRowTotal, nMeasure );
723 ScDPAggData* ScDPRowTotals::GetGrandTotal( long nMeasure )
725 return lcl_GetChildTotal( &aGrandTotal, nMeasure );
728 // -----------------------------------------------------------------------
730 static ScSubTotalFunc lcl_GetForceFunc( const ScDPLevel* pLevel, long nFuncNo )
732 ScSubTotalFunc eRet = SUBTOTAL_FUNC_NONE;
733 if ( pLevel )
735 //! direct access via ScDPLevel
737 uno::Sequence<sheet::GeneralFunction> aSeq = pLevel->getSubTotals();
738 long nSequence = aSeq.getLength();
739 if ( nSequence && aSeq[0] != sheet::GeneralFunction_AUTO )
741 // For manual subtotals, "automatic" is added as first function.
742 // ScDPResultMember::GetSubTotalCount adds to the count, here NONE has to be
743 // returned as the first function then.
745 --nFuncNo; // keep NONE for first (check below), move the other entries
748 if ( nFuncNo >= 0 && nFuncNo < nSequence )
750 sheet::GeneralFunction eUser = aSeq.getConstArray()[nFuncNo];
751 if (eUser != sheet::GeneralFunction_AUTO)
752 eRet = ScDPUtil::toSubTotalFunc(eUser);
755 return eRet;
758 // -----------------------------------------------------------------------
760 ScDPResultData::ScDPResultData( ScDPSource& rSrc ) :
761 mrSource(rSrc),
762 bLateInit( false ),
763 bDataAtCol( false ),
764 bDataAtRow( false )
768 ScDPResultData::~ScDPResultData()
770 std::for_each(maDimMembers.begin(), maDimMembers.end(), ScDeleteObjectByPtr<ResultMembers>());
773 void ScDPResultData::SetMeasureData(
774 std::vector<ScSubTotalFunc>& rFunctions, std::vector<sheet::DataPilotFieldReference>& rRefs,
775 std::vector<sal_uInt16>& rRefOrient, std::vector<OUString>& rNames )
777 // We need to have at least one measure data at all times.
779 maMeasureFuncs.swap(rFunctions);
780 if (maMeasureFuncs.empty())
781 maMeasureFuncs.push_back(SUBTOTAL_FUNC_NONE);
783 maMeasureRefs.swap(rRefs);
784 if (maMeasureRefs.empty())
785 maMeasureRefs.push_back(sheet::DataPilotFieldReference()); // default ctor is ok.
787 maMeasureRefOrients.swap(rRefOrient);
788 if (maMeasureRefOrients.empty())
789 maMeasureRefOrients.push_back(sheet::DataPilotFieldOrientation_HIDDEN);
791 maMeasureNames.swap(rNames);
792 if (maMeasureNames.empty())
793 maMeasureNames.push_back(ScGlobal::GetRscString(STR_EMPTYDATA));
796 void ScDPResultData::SetDataLayoutOrientation( sal_uInt16 nOrient )
798 bDataAtCol = ( nOrient == sheet::DataPilotFieldOrientation_COLUMN );
799 bDataAtRow = ( nOrient == sheet::DataPilotFieldOrientation_ROW );
802 void ScDPResultData::SetLateInit( bool bSet )
804 bLateInit = bSet;
807 long ScDPResultData::GetColStartMeasure() const
809 if (maMeasureFuncs.size() == 1)
810 return 0;
812 return bDataAtCol ? SC_DPMEASURE_ALL : SC_DPMEASURE_ANY;
815 long ScDPResultData::GetRowStartMeasure() const
817 if (maMeasureFuncs.size() == 1)
818 return 0;
820 return bDataAtRow ? SC_DPMEASURE_ALL : SC_DPMEASURE_ANY;
823 ScSubTotalFunc ScDPResultData::GetMeasureFunction(long nMeasure) const
825 OSL_ENSURE((size_t) nMeasure < maMeasureFuncs.size(), "bumm");
826 return maMeasureFuncs[nMeasure];
829 const sheet::DataPilotFieldReference& ScDPResultData::GetMeasureRefVal(long nMeasure) const
831 OSL_ENSURE((size_t) nMeasure < maMeasureRefs.size(), "bumm");
832 return maMeasureRefs[nMeasure];
835 sal_uInt16 ScDPResultData::GetMeasureRefOrient(long nMeasure) const
837 OSL_ENSURE((size_t) nMeasure < maMeasureRefOrients.size(), "bumm");
838 return maMeasureRefOrients[nMeasure];
841 OUString ScDPResultData::GetMeasureString(long nMeasure, bool bForce, ScSubTotalFunc eForceFunc, bool& rbTotalResult) const
843 // with bForce==true, return function instead of "result" for single measure
844 // with eForceFunc != SUBTOTAL_FUNC_NONE, always use eForceFunc
845 rbTotalResult = false;
846 if ( nMeasure < 0 || (maMeasureFuncs.size() == 1 && !bForce && eForceFunc == SUBTOTAL_FUNC_NONE) )
848 // for user-specified subtotal function with all measures,
849 // display only function name
850 if ( eForceFunc != SUBTOTAL_FUNC_NONE )
851 return ScGlobal::GetRscString(nFuncStrIds[eForceFunc]);
853 rbTotalResult = true;
854 return ScGlobal::GetRscString(STR_TABLE_ERGEBNIS);
856 else
858 OSL_ENSURE((size_t) nMeasure < maMeasureFuncs.size(), "bumm");
859 const ScDPDimension* pDataDim = mrSource.GetDataDimension(nMeasure);
860 if (pDataDim)
862 const OUString* pLayoutName = pDataDim->GetLayoutName();
863 if (pLayoutName)
864 return *pLayoutName;
867 ScSubTotalFunc eFunc = ( eForceFunc == SUBTOTAL_FUNC_NONE ) ?
868 GetMeasureFunction(nMeasure) : eForceFunc;
870 return ScDPUtil::getDisplayedMeasureName(maMeasureNames[nMeasure], eFunc);
874 OUString ScDPResultData::GetMeasureDimensionName(long nMeasure) const
876 if ( nMeasure < 0 )
878 OSL_FAIL("GetMeasureDimensionName: negative");
879 return OUString::createFromAscii("***");
882 return mrSource.GetDataDimName(nMeasure);
885 bool ScDPResultData::IsBaseForGroup( long nDim ) const
887 return mrSource.GetData()->IsBaseForGroup(nDim);
890 long ScDPResultData::GetGroupBase( long nGroupDim ) const
892 return mrSource.GetData()->GetGroupBase(nGroupDim);
895 bool ScDPResultData::IsNumOrDateGroup( long nDim ) const
897 return mrSource.GetData()->IsNumOrDateGroup(nDim);
900 bool ScDPResultData::IsInGroup( SCROW nGroupDataId, long nGroupIndex,
901 const ScDPItemData& rBaseData, long nBaseIndex ) const
903 const ScDPItemData* pGroupData = mrSource.GetItemDataById(nGroupIndex , nGroupDataId);
904 if ( pGroupData )
905 return mrSource.GetData()->IsInGroup(*pGroupData, nGroupIndex, rBaseData, nBaseIndex);
906 else
907 return false;
910 bool ScDPResultData::HasCommonElement( SCROW nFirstDataId, long nFirstIndex,
911 const ScDPItemData& rSecondData, long nSecondIndex ) const
913 const ScDPItemData* pFirstData = mrSource.GetItemDataById(nFirstIndex , nFirstDataId);
914 if ( pFirstData )
915 return mrSource.GetData()->HasCommonElement(*pFirstData, nFirstIndex, rSecondData, nSecondIndex);
916 else
917 return false;
920 const ScDPSource& ScDPResultData::GetSource() const
922 return mrSource;
925 ResultMembers* ScDPResultData::GetDimResultMembers(long nDim, ScDPDimension* pDim, ScDPLevel* pLevel) const
927 if (nDim < static_cast<long>(maDimMembers.size()) && maDimMembers[nDim])
928 return maDimMembers[nDim];
930 maDimMembers.resize(nDim+1, NULL);
932 ResultMembers* pResultMembers = new ResultMembers();
933 // global order is used to initialize aMembers, so it doesn't have to be looked at later
934 const ScMemberSortOrder& rGlobalOrder = pLevel->GetGlobalOrder();
936 ScDPMembers* pMembers = pLevel->GetMembersObject();
937 long nMembCount = pMembers->getCount();
938 for (long i = 0; i < nMembCount; ++i)
940 long nSorted = rGlobalOrder.empty() ? i : rGlobalOrder[i];
941 ScDPMember* pMember = pMembers->getByIndex(nSorted);
942 if (!pResultMembers->FindMember(pMember->GetItemDataId()))
944 ScDPParentDimData* pNew = new ScDPParentDimData(i, pDim, pLevel, pMember);
945 pResultMembers->InsertMember(pNew);
949 maDimMembers[nDim] = pResultMembers;
950 return maDimMembers[nDim];
953 // -----------------------------------------------------------------------
956 ScDPResultMember::ScDPResultMember(
957 const ScDPResultData* pData, const ScDPParentDimData& rParentDimData, bool bForceSub ) :
958 pResultData( pData ),
959 aParentDimData( rParentDimData ),
960 pChildDimension( NULL ),
961 pDataRoot( NULL ),
962 bHasElements( false ),
963 bForceSubTotal( bForceSub ),
964 bHasHiddenDetails( false ),
965 bInitialized( false ),
966 bAutoHidden( false ),
967 nMemberStep( 1 )
969 // pParentLevel/pMemberDesc is 0 for root members
972 ScDPResultMember::ScDPResultMember(
973 const ScDPResultData* pData, bool bForceSub ) :
974 pResultData( pData ),
975 pChildDimension( NULL ),
976 pDataRoot( NULL ),
977 bHasElements( false ),
978 bForceSubTotal( bForceSub ),
979 bHasHiddenDetails( false ),
980 bInitialized( false ),
981 bAutoHidden( false ),
982 nMemberStep( 1 )
985 ScDPResultMember::~ScDPResultMember()
987 delete pChildDimension;
988 delete pDataRoot;
991 OUString ScDPResultMember::GetName() const
993 const ScDPMember* pMemberDesc = GetDPMember();
994 if (pMemberDesc)
995 return pMemberDesc->GetNameStr();
996 else
997 return ScGlobal::GetRscString(STR_PIVOT_TOTAL); // root member
1000 OUString ScDPResultMember::GetDisplayName() const
1002 const ScDPMember* pDPMember = GetDPMember();
1003 if (!pDPMember)
1004 return OUString();
1006 ScDPItemData aItem;
1007 pDPMember->FillItemData(aItem);
1008 if (aParentDimData.mpParentDim)
1010 long nDim = aParentDimData.mpParentDim->GetDimension();
1011 return pResultData->GetSource().GetData()->GetFormattedString(nDim, aItem);
1014 return aItem.GetString();
1017 void ScDPResultMember::FillItemData( ScDPItemData& rData ) const
1019 const ScDPMember* pMemberDesc = GetDPMember();
1020 if (pMemberDesc)
1021 pMemberDesc->FillItemData( rData );
1022 else
1023 rData.SetString( ScGlobal::GetRscString(STR_PIVOT_TOTAL) ); // root member
1026 bool ScDPResultMember::IsNamedItem( SCROW nIndex ) const
1028 //! store ScDPMember pointer instead of ScDPMember ???
1029 const ScDPMember* pMemberDesc = GetDPMember();
1030 if (pMemberDesc)
1031 return pMemberDesc->IsNamedItem(nIndex);
1032 return false;
1035 bool ScDPResultMember::IsValidEntry( const vector< SCROW >& aMembers ) const
1037 if ( !IsValid() )
1038 return false;
1040 const ScDPResultDimension* pChildDim = GetChildDimension();
1041 if (pChildDim)
1043 if (aMembers.size() < 2)
1044 return false;
1046 vector<SCROW>::const_iterator itr = aMembers.begin();
1047 vector<SCROW> aChildMembers(++itr, aMembers.end());
1048 return pChildDim->IsValidEntry(aChildMembers);
1050 else
1051 return true;
1054 void ScDPResultMember::InitFrom( const vector<ScDPDimension*>& ppDim, const vector<ScDPLevel*>& ppLev,
1055 size_t nPos, ScDPInitState& rInitState ,
1056 bool bInitChild )
1058 // with LateInit, initialize only those members that have data
1059 if ( pResultData->IsLateInit() )
1060 return;
1062 bInitialized = true;
1064 if (nPos >= ppDim.size())
1065 return;
1067 // skip child dimension if details are not shown
1068 if ( GetDPMember() && !GetDPMember()->getShowDetails() )
1070 // Show DataLayout dimention
1071 nMemberStep = 1;
1072 while ( nPos < ppDim.size() )
1074 if ( ppDim[nPos] ->getIsDataLayoutDimension() )
1076 if ( !pChildDimension )
1077 pChildDimension = new ScDPResultDimension( pResultData );
1078 pChildDimension->InitFrom( ppDim, ppLev, nPos, rInitState , false );
1079 return;
1081 else
1082 { //find next dim
1083 nPos ++;
1084 nMemberStep ++;
1087 bHasHiddenDetails = true; // only if there is a next dimension
1088 return;
1091 if ( bInitChild )
1093 pChildDimension = new ScDPResultDimension( pResultData );
1094 pChildDimension->InitFrom(ppDim, ppLev, nPos, rInitState, true);
1098 void ScDPResultMember::LateInitFrom(
1099 LateInitParams& rParams, const vector<SCROW>& pItemData, size_t nPos, ScDPInitState& rInitState)
1101 // without LateInit, everything has already been initialized
1102 if ( !pResultData->IsLateInit() )
1103 return;
1105 bInitialized = true;
1107 if ( rParams.IsEnd( nPos ) /*nPos >= ppDim.size()*/)
1108 // No next dimension. Bail out.
1109 return;
1111 // skip child dimension if details are not shown
1112 if ( GetDPMember() && !GetDPMember()->getShowDetails() )
1114 // Show DataLayout dimention
1115 nMemberStep = 1;
1116 while ( !rParams.IsEnd( nPos ) )
1118 if ( rParams.GetDim( nPos ) ->getIsDataLayoutDimension() )
1120 if ( !pChildDimension )
1121 pChildDimension = new ScDPResultDimension( pResultData );
1123 // #i111462# reset InitChild flag only for this child dimension's LateInitFrom call,
1124 // not for following members of parent dimensions
1125 bool bWasInitChild = rParams.GetInitChild();
1126 rParams.SetInitChild( false );
1127 pChildDimension->LateInitFrom( rParams, pItemData, nPos, rInitState );
1128 rParams.SetInitChild( bWasInitChild );
1129 return;
1131 else
1132 { //find next dim
1133 nPos ++;
1134 nMemberStep ++;
1137 bHasHiddenDetails = true; // only if there is a next dimension
1138 return;
1141 // LateInitFrom is called several times...
1142 if ( rParams.GetInitChild() )
1144 if ( !pChildDimension )
1145 pChildDimension = new ScDPResultDimension( pResultData );
1146 pChildDimension->LateInitFrom( rParams, pItemData, nPos, rInitState );
1150 bool ScDPResultMember::IsSubTotalInTitle(long nMeasure) const
1152 bool bRet = false;
1153 if ( pChildDimension && /*pParentLevel*/GetParentLevel() &&
1154 /*pParentLevel*/GetParentLevel()->IsOutlineLayout() && /*pParentLevel*/GetParentLevel()->IsSubtotalsAtTop() )
1156 long nUserSubStart;
1157 long nSubTotals = GetSubTotalCount( &nUserSubStart );
1158 nSubTotals -= nUserSubStart; // visible count
1159 if ( nSubTotals )
1161 if ( nMeasure == SC_DPMEASURE_ALL )
1162 nSubTotals *= pResultData->GetMeasureCount(); // number of subtotals that will be inserted
1164 // only a single subtotal row will be shown in the outline title row
1165 if ( nSubTotals == 1 )
1166 bRet = true;
1169 return bRet;
1172 long ScDPResultMember::GetSize(long nMeasure) const
1174 if ( !IsVisible() )
1175 return 0;
1176 const ScDPLevel* pParentLevel = GetParentLevel();
1177 long nExtraSpace = 0;
1178 if ( pParentLevel && pParentLevel->IsAddEmpty() )
1179 ++nExtraSpace;
1181 if ( pChildDimension )
1183 // outline layout takes up an extra row for the title only if subtotals aren't shown in that row
1184 if ( pParentLevel && pParentLevel->IsOutlineLayout() && !IsSubTotalInTitle( nMeasure ) )
1185 ++nExtraSpace;
1187 long nSize = pChildDimension->GetSize(nMeasure);
1188 long nUserSubStart;
1189 long nUserSubCount = GetSubTotalCount( &nUserSubStart );
1190 nUserSubCount -= nUserSubStart; // for output size, use visible count
1191 if ( nUserSubCount )
1193 if ( nMeasure == SC_DPMEASURE_ALL )
1194 nSize += pResultData->GetMeasureCount() * nUserSubCount;
1195 else
1196 nSize += nUserSubCount;
1198 return nSize + nExtraSpace;
1200 else
1202 if ( nMeasure == SC_DPMEASURE_ALL )
1203 return pResultData->GetMeasureCount() + nExtraSpace;
1204 else
1205 return 1 + nExtraSpace;
1209 bool ScDPResultMember::IsVisible() const
1211 if (!bInitialized)
1212 return false;
1214 if (!IsValid())
1215 return false;
1217 if (bHasElements)
1218 return true;
1220 // not initialized -> shouldn't be there at all
1221 // (allocated only to preserve ordering)
1222 const ScDPLevel* pParentLevel = GetParentLevel();
1224 return (pParentLevel && pParentLevel->getShowEmpty());
1227 bool ScDPResultMember::IsValid() const
1229 // non-Valid members are left out of calculation
1231 // was member set no invisible at the DataPilotSource?
1232 const ScDPMember* pMemberDesc = GetDPMember();
1233 if ( pMemberDesc && !pMemberDesc->isVisible() )
1234 return false;
1236 if ( bAutoHidden )
1237 return false;
1239 return true;
1242 bool ScDPResultMember::HasHiddenDetails() const
1244 // bHasHiddenDetails is set only if the "show details" flag is off,
1245 // and there was a child dimension to skip
1247 return bHasHiddenDetails;
1250 long ScDPResultMember::GetSubTotalCount( long* pUserSubStart ) const
1252 if ( pUserSubStart )
1253 *pUserSubStart = 0; // default
1255 const ScDPLevel* pParentLevel = GetParentLevel();
1257 if ( bForceSubTotal ) // set if needed for root members
1258 return 1; // grand total is always "automatic"
1259 else if ( pParentLevel )
1261 //! direct access via ScDPLevel
1263 uno::Sequence<sheet::GeneralFunction> aSeq = pParentLevel->getSubTotals();
1264 long nSequence = aSeq.getLength();
1265 if ( nSequence && aSeq[0] != sheet::GeneralFunction_AUTO )
1267 // For manual subtotals, always add "automatic" as first function
1268 // (used for calculation, but not for display, needed for sorting, see lcl_GetForceFunc)
1270 ++nSequence;
1271 if ( pUserSubStart )
1272 *pUserSubStart = 1; // visible subtotals start at 1
1274 return nSequence;
1276 else
1277 return 0;
1280 void ScDPResultMember::ProcessData( const vector< SCROW >& aChildMembers, const ScDPResultDimension* pDataDim,
1281 const vector< SCROW >& aDataMembers, const vector<ScDPValue>& aValues )
1283 SetHasElements();
1285 if (pChildDimension)
1286 pChildDimension->ProcessData( aChildMembers, pDataDim, aDataMembers, aValues );
1288 if ( !pDataRoot )
1290 pDataRoot = new ScDPDataMember( pResultData, NULL );
1291 if ( pDataDim )
1292 pDataRoot->InitFrom( pDataDim ); // recursive
1295 ScDPSubTotalState aSubState; // initial state
1297 long nUserSubCount = GetSubTotalCount();
1299 // Calculate at least automatic if no subtotals are selected,
1300 // show only own values if there's no child dimension (innermost).
1301 if ( !nUserSubCount || !pChildDimension )
1302 nUserSubCount = 1;
1304 const ScDPLevel* pParentLevel = GetParentLevel();
1306 for (long nUserPos=0; nUserPos<nUserSubCount; nUserPos++) // including hidden "automatic"
1308 // #i68338# if nUserSubCount is 1 (automatic only), don't set nRowSubTotalFunc
1309 if ( pChildDimension && nUserSubCount > 1 )
1311 aSubState.nRowSubTotalFunc = nUserPos;
1312 aSubState.eRowForce = lcl_GetForceFunc( pParentLevel, nUserPos );
1315 pDataRoot->ProcessData( aDataMembers, aValues, aSubState );
1320 * Parse subtotal string and replace all occurrences of '?' with the caption
1321 * string. Do ensure that escaped characters are not translated.
1323 static String lcl_parseSubtotalName(const String& rSubStr, const String& rCaption)
1325 String aNewStr;
1326 xub_StrLen n = rSubStr.Len();
1327 bool bEscaped = false;
1328 for (xub_StrLen i = 0; i < n; ++i)
1330 sal_Unicode c = rSubStr.GetChar(i);
1331 if (!bEscaped && c == sal_Unicode('\\'))
1333 bEscaped = true;
1334 continue;
1337 if (!bEscaped && c == sal_Unicode('?'))
1338 aNewStr.Append(rCaption);
1339 else
1340 aNewStr.Append(c);
1341 bEscaped = false;
1343 return aNewStr;
1346 void ScDPResultMember::FillMemberResults(
1347 uno::Sequence<sheet::MemberResult>* pSequences, long& rPos, long nMeasure, bool bRoot,
1348 const OUString* pMemberName, const OUString* pMemberCaption )
1350 // IsVisible() test is in ScDPResultDimension::FillMemberResults
1351 // (not on data layout dimension)
1353 if (!pSequences->getLength())
1354 // empty sequence. Bail out.
1355 return;
1357 long nSize = GetSize(nMeasure);
1358 sheet::MemberResult* pArray = pSequences->getArray();
1359 OSL_ENSURE( rPos+nSize <= pSequences->getLength(), "bumm" );
1361 bool bIsNumeric = false;
1362 OUString aName;
1363 if ( pMemberName ) // if pMemberName != NULL, use instead of real member name
1365 aName = *pMemberName;
1367 else
1369 ScDPItemData aItemData;
1370 FillItemData( aItemData );
1371 if (aParentDimData.mpParentDim)
1373 long nDim = aParentDimData.mpParentDim->GetDimension();
1374 aName = pResultData->GetSource().GetData()->GetFormattedString(nDim, aItemData);
1376 else
1378 long nDim = -1;
1379 const ScDPMember* pMem = GetDPMember();
1380 if (pMem)
1381 nDim = pMem->GetDim();
1382 aName = pResultData->GetSource().GetData()->GetFormattedString(nDim, aItemData);
1385 ScDPItemData::Type eType = aItemData.GetType();
1386 bIsNumeric = eType == ScDPItemData::Value || eType == ScDPItemData::GroupValue;
1389 const ScDPDimension* pParentDim = GetParentDim();
1390 if ( bIsNumeric && pParentDim && pResultData->IsNumOrDateGroup( pParentDim->GetDimension() ) )
1392 // Numeric group dimensions use numeric entries for proper sorting,
1393 // but the group titles must be output as text.
1394 bIsNumeric = false;
1397 OUString aCaption = aName;
1398 const ScDPMember* pMemberDesc = GetDPMember();
1399 if (pMemberDesc)
1401 const OUString* pLayoutName = pMemberDesc->GetLayoutName();
1402 if (pLayoutName)
1404 aCaption = *pLayoutName;
1405 bIsNumeric = false; // layout name is always non-numeric.
1409 if ( pMemberCaption ) // use pMemberCaption if != NULL
1410 aCaption = *pMemberCaption;
1411 if (aCaption.isEmpty())
1412 aCaption = ScGlobal::GetRscString(STR_EMPTYDATA);
1414 if (bIsNumeric)
1415 pArray[rPos].Flags |= sheet::MemberResultFlags::NUMERIC;
1416 else
1417 pArray[rPos].Flags &= ~sheet::MemberResultFlags::NUMERIC;
1419 if ( nSize && !bRoot ) // root is overwritten by first dimension
1421 pArray[rPos].Name = aName;
1422 pArray[rPos].Caption = aCaption;
1423 pArray[rPos].Flags |= sheet::MemberResultFlags::HASMEMBER;
1425 // set "continue" flag (removed for subtotals later)
1426 for (long i=1; i<nSize; i++)
1427 pArray[rPos+i].Flags |= sheet::MemberResultFlags::CONTINUE;
1430 const ScDPLevel* pParentLevel = GetParentLevel();
1431 long nExtraSpace = 0;
1432 if ( pParentLevel && pParentLevel->IsAddEmpty() )
1433 ++nExtraSpace;
1435 bool bTitleLine = false;
1436 if ( pParentLevel && pParentLevel->IsOutlineLayout() )
1437 bTitleLine = true;
1439 // if the subtotals are shown at the top (title row) in outline layout,
1440 // no extra row for the subtotals is needed
1441 bool bSubTotalInTitle = IsSubTotalInTitle( nMeasure );
1443 bool bHasChild = ( pChildDimension != NULL );
1444 if (bHasChild)
1446 if ( bTitleLine ) // in tabular layout the title is on a separate row
1447 ++rPos; // -> fill child dimension one row below
1449 if (bRoot) // same sequence for root member
1450 pChildDimension->FillMemberResults( pSequences, rPos, nMeasure );
1451 else
1452 pChildDimension->FillMemberResults( pSequences + nMemberStep/*1*/, rPos, nMeasure );
1454 if ( bTitleLine ) // title row is included in GetSize, so the following
1455 --rPos; // positions are calculated with the normal values
1458 rPos += nSize;
1460 long nUserSubStart;
1461 long nUserSubCount = GetSubTotalCount(&nUserSubStart);
1462 if ( nUserSubCount && pChildDimension && !bSubTotalInTitle )
1464 long nMemberMeasure = nMeasure;
1465 long nSubSize = pResultData->GetCountForMeasure(nMeasure);
1467 rPos -= nSubSize * (nUserSubCount - nUserSubStart); // GetSize includes space for SubTotal
1468 rPos -= nExtraSpace; // GetSize includes the empty line
1470 for (long nUserPos=nUserSubStart; nUserPos<nUserSubCount; nUserPos++)
1472 for ( long nSubCount=0; nSubCount<nSubSize; nSubCount++ )
1474 if ( nMeasure == SC_DPMEASURE_ALL )
1475 nMemberMeasure = nSubCount;
1477 ScSubTotalFunc eForce = SUBTOTAL_FUNC_NONE;
1478 if (bHasChild)
1479 eForce = lcl_GetForceFunc( pParentLevel, nUserPos );
1481 bool bTotalResult = false;
1482 OUString aSubStr = aCaption + " " + pResultData->GetMeasureString(nMemberMeasure, false, eForce, bTotalResult);
1484 if (bTotalResult)
1486 if (pMemberDesc)
1488 // single data field layout.
1489 const OUString* pSubtotalName = pParentDim->GetSubtotalName();
1490 if (pSubtotalName)
1491 aSubStr = lcl_parseSubtotalName(*pSubtotalName, aCaption);
1492 pArray[rPos].Flags &= ~sheet::MemberResultFlags::GRANDTOTAL;
1494 else
1496 // root member - subtotal (grand total?) for multi-data field layout.
1497 const OUString* pGrandTotalName = pResultData->GetSource().GetGrandTotalName();
1498 if (pGrandTotalName)
1499 aSubStr = *pGrandTotalName;
1500 pArray[rPos].Flags |= sheet::MemberResultFlags::GRANDTOTAL;
1504 pArray[rPos].Name = aName;
1505 pArray[rPos].Caption = aSubStr;
1506 pArray[rPos].Flags = ( pArray[rPos].Flags |
1507 ( sheet::MemberResultFlags::HASMEMBER | sheet::MemberResultFlags::SUBTOTAL) ) &
1508 ~sheet::MemberResultFlags::CONTINUE;
1510 if ( nMeasure == SC_DPMEASURE_ALL )
1512 // data layout dimension is (direct/indirect) child of this.
1513 // data layout dimension must have name for all entries.
1515 uno::Sequence<sheet::MemberResult>* pLayoutSeq = pSequences;
1516 if (!bRoot)
1517 ++pLayoutSeq;
1518 ScDPResultDimension* pLayoutDim = pChildDimension;
1519 while ( pLayoutDim && !pLayoutDim->IsDataLayout() )
1521 pLayoutDim = pLayoutDim->GetFirstChildDimension();
1522 ++pLayoutSeq;
1524 if ( pLayoutDim )
1526 sheet::MemberResult* pLayoutArray = pLayoutSeq->getArray();
1527 pLayoutArray[rPos].Name = pResultData->GetMeasureDimensionName(nMemberMeasure);
1531 rPos += 1;
1535 rPos += nExtraSpace; // add again (subtracted above)
1539 void ScDPResultMember::FillDataResults(
1540 const ScDPResultMember* pRefMember,
1541 ScDPResultFilterContext& rFilterCxt, uno::Sequence<uno::Sequence<sheet::DataResult> >& rSequence,
1542 long nMeasure) const
1544 boost::scoped_ptr<FilterStack> pFilterStack;
1545 const ScDPMember* pDPMember = GetDPMember();
1546 if (pDPMember)
1548 // Root result has no corresponding DP member. Only take the non-root results.
1549 OUString aMemStr = GetDisplayName();
1550 pFilterStack.reset(new FilterStack(rFilterCxt.maFilters));
1551 pFilterStack->pushDimValue(aMemStr);
1554 // IsVisible() test is in ScDPResultDimension::FillDataResults
1555 // (not on data layout dimension)
1556 const ScDPLevel* pParentLevel = GetParentLevel();
1557 long nStartRow = rFilterCxt.mnRow;
1559 long nExtraSpace = 0;
1560 if ( pParentLevel && pParentLevel->IsAddEmpty() )
1561 ++nExtraSpace;
1563 bool bTitleLine = false;
1564 if ( pParentLevel && pParentLevel->IsOutlineLayout() )
1565 bTitleLine = true;
1567 bool bSubTotalInTitle = IsSubTotalInTitle( nMeasure );
1569 bool bHasChild = ( pChildDimension != NULL );
1570 if (bHasChild)
1572 if ( bTitleLine ) // in tabular layout the title is on a separate row
1573 ++rFilterCxt.mnRow; // -> fill child dimension one row below
1575 long nOldRow = rFilterCxt.mnRow;
1576 pChildDimension->FillDataResults(pRefMember, rFilterCxt, rSequence, nMeasure);
1577 rFilterCxt.mnRow = nOldRow; // Revert to the original row before the call.
1579 rFilterCxt.mnRow += GetSize( nMeasure );
1581 if ( bTitleLine ) // title row is included in GetSize, so the following
1582 --rFilterCxt.mnRow; // positions are calculated with the normal values
1585 long nUserSubStart;
1586 long nUserSubCount = GetSubTotalCount(&nUserSubStart);
1587 if ( nUserSubCount || !bHasChild )
1589 // Calculate at least automatic if no subtotals are selected,
1590 // show only own values if there's no child dimension (innermost).
1591 if ( !nUserSubCount || !bHasChild )
1593 nUserSubCount = 1;
1594 nUserSubStart = 0;
1597 long nMemberMeasure = nMeasure;
1598 long nSubSize = pResultData->GetCountForMeasure(nMeasure);
1599 if (bHasChild)
1601 rFilterCxt.mnRow -= nSubSize * ( nUserSubCount - nUserSubStart ); // GetSize includes space for SubTotal
1602 rFilterCxt.mnRow -= nExtraSpace; // GetSize includes the empty line
1605 long nMoveSubTotal = 0;
1606 if ( bSubTotalInTitle )
1608 nMoveSubTotal = rFilterCxt.mnRow - nStartRow; // force to first (title) row
1609 rFilterCxt.mnRow = nStartRow;
1612 if ( pDataRoot )
1614 ScDPSubTotalState aSubState; // initial state
1616 for (long nUserPos=nUserSubStart; nUserPos<nUserSubCount; nUserPos++)
1618 if ( bHasChild && nUserSubCount > 1 )
1620 aSubState.nRowSubTotalFunc = nUserPos;
1621 aSubState.eRowForce = lcl_GetForceFunc( /*pParentLevel*/GetParentLevel() , nUserPos );
1624 for ( long nSubCount=0; nSubCount<nSubSize; nSubCount++ )
1626 if ( nMeasure == SC_DPMEASURE_ALL )
1627 nMemberMeasure = nSubCount;
1628 else if ( pResultData->GetColStartMeasure() == SC_DPMEASURE_ALL )
1629 nMemberMeasure = SC_DPMEASURE_ALL;
1631 OSL_ENSURE( rFilterCxt.mnRow < rSequence.getLength(), "bumm" );
1632 uno::Sequence<sheet::DataResult>& rSubSeq = rSequence.getArray()[rFilterCxt.mnRow];
1633 rFilterCxt.mnCol = 0;
1634 if (pRefMember->IsVisible())
1635 pDataRoot->FillDataRow(pRefMember, rFilterCxt, rSubSeq, nMemberMeasure, bHasChild, aSubState);
1637 rFilterCxt.mnRow += 1;
1641 else
1642 rFilterCxt.mnRow += nSubSize * ( nUserSubCount - nUserSubStart ); // empty rows occur when ShowEmpty is true
1644 // add extra space again if subtracted from GetSize above,
1645 // add to own size if no children
1646 rFilterCxt.mnRow += nExtraSpace;
1647 rFilterCxt.mnRow += nMoveSubTotal;
1651 void ScDPResultMember::UpdateDataResults( const ScDPResultMember* pRefMember, long nMeasure ) const
1653 // IsVisible() test is in ScDPResultDimension::FillDataResults
1654 // (not on data layout dimension)
1656 bool bHasChild = ( pChildDimension != NULL );
1658 long nUserSubCount = GetSubTotalCount();
1660 // process subtotals even if not shown
1662 // Calculate at least automatic if no subtotals are selected,
1663 // show only own values if there's no child dimension (innermost).
1664 if (!nUserSubCount || !bHasChild)
1665 nUserSubCount = 1;
1667 long nMemberMeasure = nMeasure;
1668 long nSubSize = pResultData->GetCountForMeasure(nMeasure);
1670 if (pDataRoot)
1672 ScDPSubTotalState aSubState; // initial state
1674 for (long nUserPos = 0; nUserPos < nUserSubCount; ++nUserPos) // including hidden "automatic"
1676 if (bHasChild && nUserSubCount > 1)
1678 aSubState.nRowSubTotalFunc = nUserPos;
1679 aSubState.eRowForce = lcl_GetForceFunc(GetParentLevel(), nUserPos);
1682 for (long nSubCount = 0; nSubCount < nSubSize; ++nSubCount)
1684 if (nMeasure == SC_DPMEASURE_ALL)
1685 nMemberMeasure = nSubCount;
1686 else if (pResultData->GetColStartMeasure() == SC_DPMEASURE_ALL)
1687 nMemberMeasure = SC_DPMEASURE_ALL;
1689 pDataRoot->UpdateDataRow(pRefMember, nMemberMeasure, bHasChild, aSubState);
1694 if (bHasChild) // child dimension must be processed last, so the column total is known
1696 pChildDimension->UpdateDataResults( pRefMember, nMeasure );
1700 void ScDPResultMember::SortMembers( ScDPResultMember* pRefMember )
1702 bool bHasChild = ( pChildDimension != NULL );
1703 if (bHasChild)
1704 pChildDimension->SortMembers( pRefMember ); // sorting is done at the dimension
1706 if ( IsRoot() && pDataRoot )
1708 // use the row root member to sort columns
1709 // sub total count is always 1
1711 pDataRoot->SortMembers( pRefMember );
1715 void ScDPResultMember::DoAutoShow( ScDPResultMember* pRefMember )
1717 bool bHasChild = ( pChildDimension != NULL );
1718 if (bHasChild)
1719 pChildDimension->DoAutoShow( pRefMember ); // sorting is done at the dimension
1721 if ( IsRoot()&& pDataRoot )
1723 // use the row root member to sort columns
1724 // sub total count is always 1
1726 pDataRoot->DoAutoShow( pRefMember );
1730 void ScDPResultMember::ResetResults()
1732 if (pDataRoot)
1733 pDataRoot->ResetResults();
1735 if (pChildDimension)
1736 pChildDimension->ResetResults();
1739 void ScDPResultMember::UpdateRunningTotals( const ScDPResultMember* pRefMember, long nMeasure,
1740 ScDPRunningTotalState& rRunning, ScDPRowTotals& rTotals ) const
1742 // IsVisible() test is in ScDPResultDimension::FillDataResults
1743 // (not on data layout dimension)
1745 rTotals.SetInColRoot( IsRoot() );
1747 bool bHasChild = ( pChildDimension != NULL );
1749 long nUserSubCount = GetSubTotalCount();
1750 //if ( nUserSubCount || !bHasChild )
1752 // Calculate at least automatic if no subtotals are selected,
1753 // show only own values if there's no child dimension (innermost).
1754 if ( !nUserSubCount || !bHasChild )
1755 nUserSubCount = 1;
1757 long nMemberMeasure = nMeasure;
1758 long nSubSize = pResultData->GetCountForMeasure(nMeasure);
1760 if ( pDataRoot )
1762 ScDPSubTotalState aSubState; // initial state
1764 for (long nUserPos=0; nUserPos<nUserSubCount; nUserPos++) // including hidden "automatic"
1766 if ( bHasChild && nUserSubCount > 1 )
1768 aSubState.nRowSubTotalFunc = nUserPos;
1769 aSubState.eRowForce = lcl_GetForceFunc(GetParentLevel(), nUserPos);
1772 for ( long nSubCount=0; nSubCount<nSubSize; nSubCount++ )
1774 if ( nMeasure == SC_DPMEASURE_ALL )
1775 nMemberMeasure = nSubCount;
1776 else if ( pResultData->GetColStartMeasure() == SC_DPMEASURE_ALL )
1777 nMemberMeasure = SC_DPMEASURE_ALL;
1779 if (pRefMember->IsVisible())
1780 pDataRoot->UpdateRunningTotals(
1781 pRefMember, nMemberMeasure, bHasChild, aSubState, rRunning, rTotals, *this);
1787 if (bHasChild) // child dimension must be processed last, so the column total is known
1789 pChildDimension->UpdateRunningTotals( pRefMember, nMeasure, rRunning, rTotals );
1793 #if DEBUG_PIVOT_TABLE
1794 void ScDPResultMember::DumpState( const ScDPResultMember* pRefMember, ScDocument* pDoc, ScAddress& rPos ) const
1796 lcl_DumpRow( OUString("ScDPResultMember"), GetName(), NULL, pDoc, rPos );
1797 SCROW nStartRow = rPos.Row();
1799 if (pDataRoot)
1800 pDataRoot->DumpState( pRefMember, pDoc, rPos );
1802 if (pChildDimension)
1803 pChildDimension->DumpState( pRefMember, pDoc, rPos );
1805 lcl_Indent( pDoc, nStartRow, rPos );
1808 void ScDPResultMember::Dump(int nIndent) const
1810 std::string aIndent(nIndent*2, ' ');
1811 std::cout << aIndent << "-- result member '" << GetName() << "'" << std::endl;
1813 std::cout << aIndent << " column totals" << std::endl;
1814 for (const ScDPAggData* p = &aColTotal; p; p = p->GetExistingChild())
1815 p->Dump(nIndent+1);
1817 if (pChildDimension)
1818 pChildDimension->Dump(nIndent+1);
1820 if (pDataRoot)
1822 std::cout << aIndent << " data root" << std::endl;
1823 pDataRoot->Dump(nIndent+1);
1826 #endif
1828 ScDPAggData* ScDPResultMember::GetColTotal( long nMeasure ) const
1830 return lcl_GetChildTotal( const_cast<ScDPAggData*>(&aColTotal), nMeasure );
1833 void ScDPResultMember::FillVisibilityData(ScDPResultVisibilityData& rData) const
1835 if (pChildDimension)
1836 pChildDimension->FillVisibilityData(rData);
1839 // -----------------------------------------------------------------------
1841 ScDPDataMember::ScDPDataMember( const ScDPResultData* pData, const ScDPResultMember* pRes ) :
1842 pResultData( pData ),
1843 pResultMember( pRes ),
1844 pChildDimension( NULL )
1846 // pResultMember is 0 for root members
1849 ScDPDataMember::~ScDPDataMember()
1851 delete pChildDimension;
1854 OUString ScDPDataMember::GetName() const
1856 if (pResultMember)
1857 return pResultMember->GetName();
1858 else
1859 return EMPTY_OUSTRING;
1862 bool ScDPDataMember::IsVisible() const
1864 if (pResultMember)
1865 return pResultMember->IsVisible();
1866 else
1867 return false;
1870 bool ScDPDataMember::IsNamedItem( SCROW nRow ) const
1872 if (pResultMember)
1873 return pResultMember->IsNamedItem(nRow);
1874 else
1875 return false;
1878 bool ScDPDataMember::HasHiddenDetails() const
1880 if (pResultMember)
1881 return pResultMember->HasHiddenDetails();
1882 else
1883 return false;
1886 void ScDPDataMember::InitFrom( const ScDPResultDimension* pDim )
1888 if ( !pChildDimension )
1889 pChildDimension = new ScDPDataDimension(pResultData);
1890 pChildDimension->InitFrom(pDim);
1893 const long SC_SUBTOTALPOS_AUTO = -1; // default
1894 const long SC_SUBTOTALPOS_SKIP = -2; // don't use
1896 static long lcl_GetSubTotalPos( const ScDPSubTotalState& rSubState )
1898 if ( rSubState.nColSubTotalFunc >= 0 && rSubState.nRowSubTotalFunc >= 0 &&
1899 rSubState.nColSubTotalFunc != rSubState.nRowSubTotalFunc )
1901 // #i68338# don't return the same index for different combinations (leading to repeated updates),
1902 // return a "don't use" value instead
1904 return SC_SUBTOTALPOS_SKIP;
1907 long nRet = SC_SUBTOTALPOS_AUTO;
1908 if ( rSubState.nColSubTotalFunc >= 0 ) nRet = rSubState.nColSubTotalFunc;
1909 if ( rSubState.nRowSubTotalFunc >= 0 ) nRet = rSubState.nRowSubTotalFunc;
1910 return nRet;
1913 void ScDPDataMember::UpdateValues( const vector<ScDPValue>& aValues, const ScDPSubTotalState& rSubState )
1915 //! find out how many and which subtotals are used
1917 ScDPAggData* pAgg = &aAggregate;
1919 long nSubPos = lcl_GetSubTotalPos(rSubState);
1920 if (nSubPos == SC_SUBTOTALPOS_SKIP)
1921 return;
1922 if (nSubPos > 0)
1924 long nSkip = nSubPos * pResultData->GetMeasureCount();
1925 for (long i=0; i<nSkip; i++)
1926 pAgg = pAgg->GetChild(); // created if not there
1929 size_t nCount = aValues.size();
1930 for (size_t nPos = 0; nPos < nCount; ++nPos)
1932 pAgg->Update(aValues[nPos], pResultData->GetMeasureFunction(nPos), rSubState);
1933 pAgg = pAgg->GetChild();
1937 void ScDPDataMember::ProcessData( const vector< SCROW >& aChildMembers, const vector<ScDPValue>& aValues,
1938 const ScDPSubTotalState& rSubState )
1940 if ( pResultData->IsLateInit() && !pChildDimension && pResultMember && pResultMember->GetChildDimension() )
1942 // if this DataMember doesn't have a child dimension because the ResultMember's
1943 // child dimension wasn't there yet during this DataMembers's creation,
1944 // create the child dimension now
1945 InitFrom( pResultMember->GetChildDimension() );
1948 long nUserSubCount = pResultMember ? pResultMember->GetSubTotalCount() : 0;
1950 // Calculate at least automatic if no subtotals are selected,
1951 // show only own values if there's no child dimension (innermost).
1952 if ( !nUserSubCount || !pChildDimension )
1953 nUserSubCount = 1;
1955 ScDPSubTotalState aLocalSubState = rSubState; // keep row state, modify column
1956 for (long nUserPos=0; nUserPos<nUserSubCount; nUserPos++) // including hidden "automatic"
1958 if ( pChildDimension && nUserSubCount > 1 )
1960 const ScDPLevel* pForceLevel = pResultMember ? pResultMember->GetParentLevel() : NULL;
1961 aLocalSubState.nColSubTotalFunc = nUserPos;
1962 aLocalSubState.eColForce = lcl_GetForceFunc( pForceLevel, nUserPos );
1965 UpdateValues( aValues, aLocalSubState );
1968 if (pChildDimension)
1969 pChildDimension->ProcessData( aChildMembers, aValues, rSubState ); // with unmodified subtotal state
1972 bool ScDPDataMember::HasData( long nMeasure, const ScDPSubTotalState& rSubState ) const
1974 if ( rSubState.eColForce != SUBTOTAL_FUNC_NONE && rSubState.eRowForce != SUBTOTAL_FUNC_NONE &&
1975 rSubState.eColForce != rSubState.eRowForce )
1976 return false;
1978 // HasData can be different between measures!
1980 const ScDPAggData* pAgg = GetConstAggData( nMeasure, rSubState );
1981 if (!pAgg)
1982 return false; //! error?
1984 return pAgg->HasData();
1987 bool ScDPDataMember::HasError( long nMeasure, const ScDPSubTotalState& rSubState ) const
1989 const ScDPAggData* pAgg = GetConstAggData( nMeasure, rSubState );
1990 if (!pAgg)
1991 return true;
1993 return pAgg->HasError();
1996 double ScDPDataMember::GetAggregate( long nMeasure, const ScDPSubTotalState& rSubState ) const
1998 const ScDPAggData* pAgg = GetConstAggData( nMeasure, rSubState );
1999 if (!pAgg)
2000 return DBL_MAX; //! error?
2002 return pAgg->GetResult();
2005 ScDPAggData* ScDPDataMember::GetAggData( long nMeasure, const ScDPSubTotalState& rSubState )
2007 OSL_ENSURE( nMeasure >= 0, "GetAggData: no measure" );
2009 ScDPAggData* pAgg = &aAggregate;
2010 long nSkip = nMeasure;
2011 long nSubPos = lcl_GetSubTotalPos(rSubState);
2012 if (nSubPos == SC_SUBTOTALPOS_SKIP)
2013 return NULL;
2014 if (nSubPos > 0)
2015 nSkip += nSubPos * pResultData->GetMeasureCount();
2017 for ( long nPos=0; nPos<nSkip; nPos++ )
2018 pAgg = pAgg->GetChild(); //! need to create children here?
2020 return pAgg;
2023 const ScDPAggData* ScDPDataMember::GetConstAggData( long nMeasure, const ScDPSubTotalState& rSubState ) const
2025 OSL_ENSURE( nMeasure >= 0, "GetConstAggData: no measure" );
2027 const ScDPAggData* pAgg = &aAggregate;
2028 long nSkip = nMeasure;
2029 long nSubPos = lcl_GetSubTotalPos(rSubState);
2030 if (nSubPos == SC_SUBTOTALPOS_SKIP)
2031 return NULL;
2032 if (nSubPos > 0)
2033 nSkip += nSubPos * pResultData->GetMeasureCount();
2035 for ( long nPos=0; nPos<nSkip; nPos++ )
2037 pAgg = pAgg->GetExistingChild();
2038 if (!pAgg)
2039 return NULL;
2042 return pAgg;
2045 void ScDPDataMember::FillDataRow(
2046 const ScDPResultMember* pRefMember, ScDPResultFilterContext& rFilterCxt,
2047 uno::Sequence<sheet::DataResult>& rSequence, long nMeasure, bool bIsSubTotalRow,
2048 const ScDPSubTotalState& rSubState) const
2050 boost::scoped_ptr<FilterStack> pFilterStack;
2051 if (pResultMember)
2053 // Topmost data member (pResultMember=NULL) doesn't need to be handled
2054 // since its immediate parent result member is linked to the same
2055 // dimension member.
2056 pFilterStack.reset(new FilterStack(rFilterCxt.maFilters));
2057 pFilterStack->pushDimValue(pResultMember->GetDisplayName());
2060 OSL_ENSURE( pRefMember == pResultMember || !pResultMember, "bla" );
2062 long nStartCol = rFilterCxt.mnCol;
2064 const ScDPDataDimension* pDataChild = GetChildDimension();
2065 const ScDPResultDimension* pRefChild = pRefMember->GetChildDimension();
2067 const ScDPLevel* pRefParentLevel = const_cast<ScDPResultMember*>(pRefMember)->GetParentLevel();
2069 long nExtraSpace = 0;
2070 if ( pRefParentLevel && pRefParentLevel->IsAddEmpty() )
2071 ++nExtraSpace;
2073 bool bTitleLine = false;
2074 if ( pRefParentLevel && pRefParentLevel->IsOutlineLayout() )
2075 bTitleLine = true;
2077 bool bSubTotalInTitle = pRefMember->IsSubTotalInTitle( nMeasure );
2079 // leave space for children even if the DataMember hasn't been initialized
2080 // (pDataChild is null then, this happens when no values for it are in this row)
2081 bool bHasChild = ( pRefChild != NULL );
2083 if ( bHasChild )
2085 if ( bTitleLine ) // in tabular layout the title is on a separate column
2086 ++rFilterCxt.mnCol; // -> fill child dimension one column below
2088 if ( pDataChild )
2090 long nOldCol = rFilterCxt.mnCol;
2091 pDataChild->FillDataRow(pRefChild, rFilterCxt, rSequence, nMeasure, bIsSubTotalRow, rSubState);
2092 rFilterCxt.mnCol = nOldCol; // Revert to the old column value before the call.
2094 rFilterCxt.mnCol += (sal_uInt16)pRefMember->GetSize( nMeasure );
2096 if ( bTitleLine ) // title column is included in GetSize, so the following
2097 --rFilterCxt.mnCol; // positions are calculated with the normal values
2100 long nUserSubStart;
2101 long nUserSubCount = pRefMember->GetSubTotalCount(&nUserSubStart);
2102 if ( nUserSubCount || !bHasChild )
2104 // Calculate at least automatic if no subtotals are selected,
2105 // show only own values if there's no child dimension (innermost).
2106 if ( !nUserSubCount || !bHasChild )
2108 nUserSubCount = 1;
2109 nUserSubStart = 0;
2112 ScDPSubTotalState aLocalSubState(rSubState); // keep row state, modify column
2114 long nMemberMeasure = nMeasure;
2115 long nSubSize = pResultData->GetCountForMeasure(nMeasure);
2116 if (bHasChild)
2118 rFilterCxt.mnCol -= nSubSize * ( nUserSubCount - nUserSubStart ); // GetSize includes space for SubTotal
2119 rFilterCxt.mnCol -= nExtraSpace; // GetSize includes the empty line
2122 long nMoveSubTotal = 0;
2123 if ( bSubTotalInTitle )
2125 nMoveSubTotal = rFilterCxt.mnCol - nStartCol; // force to first (title) column
2126 rFilterCxt.mnCol = nStartCol;
2129 for (long nUserPos=nUserSubStart; nUserPos<nUserSubCount; nUserPos++)
2131 if ( pChildDimension && nUserSubCount > 1 )
2133 const ScDPLevel* pForceLevel = pResultMember ? pResultMember->GetParentLevel() : NULL;
2134 aLocalSubState.nColSubTotalFunc = nUserPos;
2135 aLocalSubState.eColForce = lcl_GetForceFunc( pForceLevel, nUserPos );
2138 for ( long nSubCount=0; nSubCount<nSubSize; nSubCount++ )
2140 if ( nMeasure == SC_DPMEASURE_ALL )
2141 nMemberMeasure = nSubCount;
2143 OSL_ENSURE( rFilterCxt.mnCol < rSequence.getLength(), "bumm" );
2144 sheet::DataResult& rRes = rSequence.getArray()[rFilterCxt.mnCol];
2146 if ( HasData( nMemberMeasure, aLocalSubState ) )
2148 if ( HasError( nMemberMeasure, aLocalSubState ) )
2150 rRes.Value = 0;
2151 rRes.Flags |= sheet::DataResultFlags::ERROR;
2153 else
2155 rRes.Value = GetAggregate( nMemberMeasure, aLocalSubState );
2156 rRes.Flags |= sheet::DataResultFlags::HASDATA;
2160 if ( bHasChild || bIsSubTotalRow )
2161 rRes.Flags |= sheet::DataResultFlags::SUBTOTAL;
2163 rFilterCxt.maFilterSet.add(rFilterCxt.maFilters, rFilterCxt.mnCol, rFilterCxt.mnRow, rRes.Value);
2164 rFilterCxt.mnCol += 1;
2168 // add extra space again if subtracted from GetSize above,
2169 // add to own size if no children
2170 rFilterCxt.mnCol += nExtraSpace;
2171 rFilterCxt.mnCol += nMoveSubTotal;
2175 void ScDPDataMember::UpdateDataRow(
2176 const ScDPResultMember* pRefMember, long nMeasure, bool bIsSubTotalRow,
2177 const ScDPSubTotalState& rSubState )
2179 OSL_ENSURE( pRefMember == pResultMember || !pResultMember, "bla" );
2181 // Calculate must be called even if not visible (for use as reference value)
2182 const ScDPDataDimension* pDataChild = GetChildDimension();
2183 const ScDPResultDimension* pRefChild = pRefMember->GetChildDimension();
2185 // leave space for children even if the DataMember hasn't been initialized
2186 // (pDataChild is null then, this happens when no values for it are in this row)
2187 bool bHasChild = ( pRefChild != NULL );
2189 // process subtotals even if not shown
2190 long nUserSubCount = pRefMember->GetSubTotalCount();
2192 // Calculate at least automatic if no subtotals are selected,
2193 // show only own values if there's no child dimension (innermost).
2194 if ( !nUserSubCount || !bHasChild )
2195 nUserSubCount = 1;
2197 ScDPSubTotalState aLocalSubState(rSubState); // keep row state, modify column
2199 long nMemberMeasure = nMeasure;
2200 long nSubSize = pResultData->GetCountForMeasure(nMeasure);
2202 for (long nUserPos=0; nUserPos<nUserSubCount; nUserPos++) // including hidden "automatic"
2204 if ( pChildDimension && nUserSubCount > 1 )
2206 const ScDPLevel* pForceLevel = pResultMember ? pResultMember->GetParentLevel() : NULL;
2207 aLocalSubState.nColSubTotalFunc = nUserPos;
2208 aLocalSubState.eColForce = lcl_GetForceFunc( pForceLevel, nUserPos );
2211 for ( long nSubCount=0; nSubCount<nSubSize; nSubCount++ )
2213 if ( nMeasure == SC_DPMEASURE_ALL )
2214 nMemberMeasure = nSubCount;
2216 // update data...
2217 ScDPAggData* pAggData = GetAggData( nMemberMeasure, aLocalSubState );
2218 if (pAggData)
2220 //! aLocalSubState?
2221 ScSubTotalFunc eFunc = pResultData->GetMeasureFunction( nMemberMeasure );
2222 sheet::DataPilotFieldReference aReferenceValue = pResultData->GetMeasureRefVal( nMemberMeasure );
2223 sal_Int32 eRefType = aReferenceValue.ReferenceType;
2225 // calculate the result first - for all members, regardless of reference value
2226 pAggData->Calculate( eFunc, aLocalSubState );
2228 if ( eRefType == sheet::DataPilotFieldReferenceType::ITEM_DIFFERENCE ||
2229 eRefType == sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE ||
2230 eRefType == sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE )
2232 // copy the result into auxiliary value, so differences can be
2233 // calculated in any order
2234 pAggData->SetAuxiliary( pAggData->GetResult() );
2236 // column/row percentage/index is now in UpdateRunningTotals, so it doesn't disturb sorting
2241 if ( bHasChild ) // child dimension must be processed last, so the row total is known
2243 if ( pDataChild )
2244 pDataChild->UpdateDataRow( pRefChild, nMeasure, bIsSubTotalRow, rSubState );
2248 void ScDPDataMember::SortMembers( ScDPResultMember* pRefMember )
2250 OSL_ENSURE( pRefMember == pResultMember || !pResultMember, "bla" );
2252 if ( pRefMember->IsVisible() ) //! here or in ScDPDataDimension ???
2254 ScDPDataDimension* pDataChild = GetChildDimension();
2255 ScDPResultDimension* pRefChild = pRefMember->GetChildDimension();
2256 if ( pRefChild && pDataChild )
2257 pDataChild->SortMembers( pRefChild ); // sorting is done at the dimension
2261 void ScDPDataMember::DoAutoShow( ScDPResultMember* pRefMember )
2263 OSL_ENSURE( pRefMember == pResultMember || !pResultMember, "bla" );
2265 if ( pRefMember->IsVisible() ) //! here or in ScDPDataDimension ???
2267 ScDPDataDimension* pDataChild = GetChildDimension();
2268 ScDPResultDimension* pRefChild = pRefMember->GetChildDimension();
2269 if ( pRefChild && pDataChild )
2270 pDataChild->DoAutoShow( pRefChild ); // sorting is done at the dimension
2274 void ScDPDataMember::ResetResults()
2276 aAggregate.Reset();
2278 ScDPDataDimension* pDataChild = GetChildDimension();
2279 if ( pDataChild )
2280 pDataChild->ResetResults();
2283 void ScDPDataMember::UpdateRunningTotals(
2284 const ScDPResultMember* pRefMember, long nMeasure, bool bIsSubTotalRow,
2285 const ScDPSubTotalState& rSubState, ScDPRunningTotalState& rRunning,
2286 ScDPRowTotals& rTotals, const ScDPResultMember& rRowParent )
2288 OSL_ENSURE( pRefMember == pResultMember || !pResultMember, "bla" );
2290 const ScDPDataDimension* pDataChild = GetChildDimension();
2291 const ScDPResultDimension* pRefChild = pRefMember->GetChildDimension();
2293 bool bIsRoot = ( pResultMember == NULL || pResultMember->GetParentLevel() == NULL );
2295 // leave space for children even if the DataMember hasn't been initialized
2296 // (pDataChild is null then, this happens when no values for it are in this row)
2297 bool bHasChild = ( pRefChild != NULL );
2299 long nUserSubCount = pRefMember->GetSubTotalCount();
2301 // Calculate at least automatic if no subtotals are selected,
2302 // show only own values if there's no child dimension (innermost).
2303 if ( !nUserSubCount || !bHasChild )
2304 nUserSubCount = 1;
2306 ScDPSubTotalState aLocalSubState(rSubState); // keep row state, modify column
2308 long nMemberMeasure = nMeasure;
2309 long nSubSize = pResultData->GetCountForMeasure(nMeasure);
2311 for (long nUserPos=0; nUserPos<nUserSubCount; nUserPos++) // including hidden "automatic"
2313 if ( pChildDimension && nUserSubCount > 1 )
2315 const ScDPLevel* pForceLevel = pResultMember ? pResultMember->GetParentLevel() : NULL;
2316 aLocalSubState.nColSubTotalFunc = nUserPos;
2317 aLocalSubState.eColForce = lcl_GetForceFunc( pForceLevel, nUserPos );
2320 for ( long nSubCount=0; nSubCount<nSubSize; nSubCount++ )
2322 if ( nMeasure == SC_DPMEASURE_ALL )
2323 nMemberMeasure = nSubCount;
2325 // update data...
2326 ScDPAggData* pAggData = GetAggData( nMemberMeasure, aLocalSubState );
2327 if (pAggData)
2329 //! aLocalSubState?
2330 sheet::DataPilotFieldReference aReferenceValue = pResultData->GetMeasureRefVal( nMemberMeasure );
2331 sal_Int32 eRefType = aReferenceValue.ReferenceType;
2333 if ( eRefType == sheet::DataPilotFieldReferenceType::RUNNING_TOTAL ||
2334 eRefType == sheet::DataPilotFieldReferenceType::ITEM_DIFFERENCE ||
2335 eRefType == sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE ||
2336 eRefType == sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE )
2338 bool bRunningTotal = ( eRefType == sheet::DataPilotFieldReferenceType::RUNNING_TOTAL );
2339 bool bRelative =
2340 ( aReferenceValue.ReferenceItemType != sheet::DataPilotFieldReferenceItemType::NAMED && !bRunningTotal );
2341 long nRelativeDir = bRelative ?
2342 ( ( aReferenceValue.ReferenceItemType == sheet::DataPilotFieldReferenceItemType::PREVIOUS ) ? -1 : 1 ) : 0;
2344 const ScDPRunningTotalState::IndexArray& rColVisible = rRunning.GetColVisible();
2345 const ScDPRunningTotalState::IndexArray& rColSorted = rRunning.GetColSorted();
2346 const ScDPRunningTotalState::IndexArray& rRowVisible = rRunning.GetRowVisible();
2347 const ScDPRunningTotalState::IndexArray& rRowSorted = rRunning.GetRowSorted();
2349 String aRefFieldName = aReferenceValue.ReferenceField;
2351 //! aLocalSubState?
2352 sal_uInt16 nRefOrient = pResultData->GetMeasureRefOrient( nMemberMeasure );
2353 bool bRefDimInCol = ( nRefOrient == sheet::DataPilotFieldOrientation_COLUMN );
2354 bool bRefDimInRow = ( nRefOrient == sheet::DataPilotFieldOrientation_ROW );
2356 ScDPResultDimension* pSelectDim = NULL;
2357 long nRowPos = 0;
2358 long nColPos = 0;
2361 // find the reference field in column or row dimensions
2364 if ( bRefDimInRow ) // look in row dimensions
2366 pSelectDim = rRunning.GetRowResRoot()->GetChildDimension();
2367 while ( pSelectDim && pSelectDim->GetName() != aRefFieldName )
2369 long nIndex = rRowSorted[nRowPos];
2370 if ( nIndex >= 0 && nIndex < pSelectDim->GetMemberCount() )
2371 pSelectDim = pSelectDim->GetMember(nIndex)->GetChildDimension();
2372 else
2373 pSelectDim = NULL;
2374 ++nRowPos;
2376 // child dimension of innermost member?
2377 if ( pSelectDim && rRowSorted[nRowPos] < 0 )
2378 pSelectDim = NULL;
2381 if ( bRefDimInCol ) // look in column dimensions
2383 pSelectDim = rRunning.GetColResRoot()->GetChildDimension();
2384 while ( pSelectDim && pSelectDim->GetName() != aRefFieldName )
2386 long nIndex = rColSorted[nColPos];
2387 if ( nIndex >= 0 && nIndex < pSelectDim->GetMemberCount() )
2388 pSelectDim = pSelectDim->GetMember(nIndex)->GetChildDimension();
2389 else
2390 pSelectDim = NULL;
2391 ++nColPos;
2393 // child dimension of innermost member?
2394 if ( pSelectDim && rColSorted[nColPos] < 0 )
2395 pSelectDim = NULL;
2398 bool bNoDetailsInRef = false;
2399 if ( pSelectDim && bRunningTotal )
2401 // Running totals:
2402 // If details are hidden for this member in the reference dimension,
2403 // don't show or sum up the value. Otherwise, for following members,
2404 // the running totals of details and subtotals wouldn't match.
2406 long nMyIndex = bRefDimInCol ? rColSorted[nColPos] : rRowSorted[nRowPos];
2407 if ( nMyIndex >= 0 && nMyIndex < pSelectDim->GetMemberCount() )
2409 const ScDPResultMember* pMyRefMember = pSelectDim->GetMember(nMyIndex);
2410 if ( pMyRefMember && pMyRefMember->HasHiddenDetails() )
2412 pSelectDim = NULL; // don't calculate
2413 bNoDetailsInRef = true; // show error, not empty
2418 if ( bRelative )
2420 // Difference/Percentage from previous/next:
2421 // If details are hidden for this member in the innermost column/row
2422 // dimension (the orientation of the reference dimension), show an
2423 // error value.
2424 // - If the no-details dimension is the reference dimension, its
2425 // members will be skipped when finding the previous/next member,
2426 // so there must be no results for its members.
2427 // - If the no-details dimension is outside of the reference dimension,
2428 // no calculation in the reference dimension is possible.
2429 // - Otherwise, the error isn't strictly necessary, but shown for
2430 // consistency.
2432 bool bInnerNoDetails = bRefDimInCol ? HasHiddenDetails() :
2433 ( bRefDimInRow ? rRowParent.HasHiddenDetails() : true );
2434 if ( bInnerNoDetails )
2436 pSelectDim = NULL;
2437 bNoDetailsInRef = true; // show error, not empty
2441 if ( !bRefDimInCol && !bRefDimInRow ) // invalid dimension specified
2442 bNoDetailsInRef = true; // pSelectDim is then already NULL
2445 // get the member for the reference item and do the calculation
2448 if ( bRunningTotal )
2450 // running total in (dimension) -> find first existing member
2452 if ( pSelectDim )
2454 ScDPDataMember* pSelectMember;
2455 if ( bRefDimInCol )
2456 pSelectMember = ScDPResultDimension::GetColReferenceMember( NULL, NULL,
2457 nColPos, rRunning );
2458 else
2460 const long* pRowSorted = &rRowSorted[0];
2461 const long* pColSorted = &rColSorted[0];
2462 pRowSorted += nRowPos + 1; // including the reference dimension
2463 pSelectMember = pSelectDim->GetRowReferenceMember(
2464 NULL, NULL, pRowSorted, pColSorted);
2467 if ( pSelectMember )
2469 // The running total is kept as the auxiliary value in
2470 // the first available member for the reference dimension.
2471 // Members are visited in final order, so each one's result
2472 // can be used and then modified.
2474 ScDPAggData* pSelectData = pSelectMember->
2475 GetAggData( nMemberMeasure, aLocalSubState );
2476 if ( pSelectData )
2478 double fTotal = pSelectData->GetAuxiliary();
2479 fTotal += pAggData->GetResult();
2480 pSelectData->SetAuxiliary( fTotal );
2481 pAggData->SetResult( fTotal );
2482 pAggData->SetEmpty(false); // always display
2485 else
2486 pAggData->SetError();
2488 else if (bNoDetailsInRef)
2489 pAggData->SetError();
2490 else
2491 pAggData->SetEmpty(true); // empty (dim set to 0 above)
2493 else
2495 // difference/percentage -> find specified member
2497 if ( pSelectDim )
2499 OUString aRefItemName = aReferenceValue.ReferenceItemName;
2500 ScDPRelativePos aRefItemPos( 0, nRelativeDir ); // nBasePos is modified later
2502 const OUString* pRefName = NULL;
2503 const ScDPRelativePos* pRefPos = NULL;
2504 if ( bRelative )
2505 pRefPos = &aRefItemPos;
2506 else
2507 pRefName = &aRefItemName;
2509 ScDPDataMember* pSelectMember;
2510 if ( bRefDimInCol )
2512 aRefItemPos.nBasePos = rColVisible[nColPos]; // without sort order applied
2513 pSelectMember = ScDPResultDimension::GetColReferenceMember( pRefPos, pRefName,
2514 nColPos, rRunning );
2516 else
2518 aRefItemPos.nBasePos = rRowVisible[nRowPos]; // without sort order applied
2519 const long* pRowSorted = &rRowSorted[0];
2520 const long* pColSorted = &rColSorted[0];
2521 pRowSorted += nRowPos + 1; // including the reference dimension
2522 pSelectMember = pSelectDim->GetRowReferenceMember(
2523 pRefPos, pRefName, pRowSorted, pColSorted);
2526 // difference or perc.difference is empty for the reference item itself
2527 if ( pSelectMember == this &&
2528 eRefType != sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE )
2530 pAggData->SetEmpty(true);
2532 else if ( pSelectMember )
2534 const ScDPAggData* pOtherAggData = pSelectMember->
2535 GetConstAggData( nMemberMeasure, aLocalSubState );
2536 OSL_ENSURE( pOtherAggData, "no agg data" );
2537 if ( pOtherAggData )
2539 // Reference member may be visited before or after this one,
2540 // so the auxiliary value is used for the original result.
2542 double fOtherResult = pOtherAggData->GetAuxiliary();
2543 double fThisResult = pAggData->GetResult();
2544 bool bError = false;
2545 switch ( eRefType )
2547 case sheet::DataPilotFieldReferenceType::ITEM_DIFFERENCE:
2548 fThisResult = fThisResult - fOtherResult;
2549 break;
2550 case sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE:
2551 if ( fOtherResult == 0.0 )
2552 bError = true;
2553 else
2554 fThisResult = fThisResult / fOtherResult;
2555 break;
2556 case sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE:
2557 if ( fOtherResult == 0.0 )
2558 bError = true;
2559 else
2560 fThisResult = ( fThisResult - fOtherResult ) / fOtherResult;
2561 break;
2562 default:
2563 OSL_FAIL("invalid calculation type");
2565 if ( bError )
2567 pAggData->SetError();
2569 else
2571 pAggData->SetResult(fThisResult);
2572 pAggData->SetEmpty(false); // always display
2574 //! errors in data?
2577 else if (bRelative && !bNoDetailsInRef)
2578 pAggData->SetEmpty(true); // empty
2579 else
2580 pAggData->SetError(); // error
2582 else if (bNoDetailsInRef)
2583 pAggData->SetError(); // error
2584 else
2585 pAggData->SetEmpty(true); // empty
2588 else if ( eRefType == sheet::DataPilotFieldReferenceType::ROW_PERCENTAGE ||
2589 eRefType == sheet::DataPilotFieldReferenceType::COLUMN_PERCENTAGE ||
2590 eRefType == sheet::DataPilotFieldReferenceType::TOTAL_PERCENTAGE ||
2591 eRefType == sheet::DataPilotFieldReferenceType::INDEX )
2594 // set total values when they are encountered (always before their use)
2597 ScDPAggData* pColTotalData = pRefMember->GetColTotal( nMemberMeasure );
2598 ScDPAggData* pRowTotalData = rTotals.GetRowTotal( nMemberMeasure );
2599 ScDPAggData* pGrandTotalData = rTotals.GetGrandTotal( nMemberMeasure );
2601 double fTotalValue = pAggData->HasError() ? 0 : pAggData->GetResult();
2603 if ( bIsRoot && rTotals.IsInColRoot() && pGrandTotalData )
2604 pGrandTotalData->SetAuxiliary( fTotalValue );
2606 if ( bIsRoot && pRowTotalData )
2607 pRowTotalData->SetAuxiliary( fTotalValue );
2609 if ( rTotals.IsInColRoot() && pColTotalData )
2610 pColTotalData->SetAuxiliary( fTotalValue );
2613 // find relation to total values
2616 switch ( eRefType )
2618 case sheet::DataPilotFieldReferenceType::ROW_PERCENTAGE:
2619 case sheet::DataPilotFieldReferenceType::COLUMN_PERCENTAGE:
2620 case sheet::DataPilotFieldReferenceType::TOTAL_PERCENTAGE:
2622 double nTotal;
2623 if ( eRefType == sheet::DataPilotFieldReferenceType::ROW_PERCENTAGE )
2624 nTotal = pRowTotalData->GetAuxiliary();
2625 else if ( eRefType == sheet::DataPilotFieldReferenceType::COLUMN_PERCENTAGE )
2626 nTotal = pColTotalData->GetAuxiliary();
2627 else
2628 nTotal = pGrandTotalData->GetAuxiliary();
2630 if ( nTotal == 0.0 )
2631 pAggData->SetError();
2632 else
2633 pAggData->SetResult( pAggData->GetResult() / nTotal );
2635 break;
2636 case sheet::DataPilotFieldReferenceType::INDEX:
2638 double nColTotal = pColTotalData->GetAuxiliary();
2639 double nRowTotal = pRowTotalData->GetAuxiliary();
2640 double nGrandTotal = pGrandTotalData->GetAuxiliary();
2641 if ( nRowTotal == 0.0 || nColTotal == 0.0 )
2642 pAggData->SetError();
2643 else
2644 pAggData->SetResult(
2645 ( pAggData->GetResult() * nGrandTotal ) /
2646 ( nRowTotal * nColTotal ) );
2648 break;
2656 if ( bHasChild ) // child dimension must be processed last, so the row total is known
2658 if ( pDataChild )
2659 pDataChild->UpdateRunningTotals( pRefChild, nMeasure,
2660 bIsSubTotalRow, rSubState, rRunning, rTotals, rRowParent );
2664 #if DEBUG_PIVOT_TABLE
2665 void ScDPDataMember::DumpState( const ScDPResultMember* pRefMember, ScDocument* pDoc, ScAddress& rPos ) const
2667 lcl_DumpRow( OUString("ScDPDataMember"), GetName(), &aAggregate, pDoc, rPos );
2668 SCROW nStartRow = rPos.Row();
2670 const ScDPDataDimension* pDataChild = GetChildDimension();
2671 const ScDPResultDimension* pRefChild = pRefMember->GetChildDimension();
2672 if ( pDataChild && pRefChild )
2673 pDataChild->DumpState( pRefChild, pDoc, rPos );
2675 lcl_Indent( pDoc, nStartRow, rPos );
2678 void ScDPDataMember::Dump(int nIndent) const
2680 std::string aIndent(nIndent*2, ' ');
2681 std::cout << aIndent << "-- data member '"
2682 << (pResultMember ? pResultMember->GetName() : OUString()) << "'" << std::endl;
2683 for (const ScDPAggData* pAgg = &aAggregate; pAgg; pAgg = pAgg->GetExistingChild())
2684 pAgg->Dump(nIndent+1);
2686 if (pChildDimension)
2687 pChildDimension->Dump(nIndent+1);
2689 #endif
2691 // -----------------------------------------------------------------------
2693 // Helper class to select the members to include in
2694 // ScDPResultDimension::InitFrom or LateInitFrom if groups are used
2696 class ScDPGroupCompare
2698 private:
2699 const ScDPResultData* pResultData;
2700 const ScDPInitState& rInitState;
2701 long nDimSource;
2702 bool bIncludeAll;
2703 bool bIsBase;
2704 long nGroupBase;
2705 public:
2706 ScDPGroupCompare( const ScDPResultData* pData, const ScDPInitState& rState, long nDimension );
2707 ~ScDPGroupCompare() {}
2709 bool IsIncluded( const ScDPMember& rMember ) { return bIncludeAll || TestIncluded( rMember ); }
2710 bool TestIncluded( const ScDPMember& rMember );
2713 ScDPGroupCompare::ScDPGroupCompare( const ScDPResultData* pData, const ScDPInitState& rState, long nDimension ) :
2714 pResultData( pData ),
2715 rInitState( rState ),
2716 nDimSource( nDimension )
2718 bIsBase = pResultData->IsBaseForGroup( nDimSource );
2719 nGroupBase = pResultData->GetGroupBase( nDimSource ); //! get together in one call?
2721 // if bIncludeAll is set, TestIncluded doesn't need to be called
2722 bIncludeAll = !( bIsBase || nGroupBase >= 0 );
2725 bool ScDPGroupCompare::TestIncluded( const ScDPMember& rMember )
2727 bool bInclude = true;
2728 if ( bIsBase )
2730 // need to check all previous groups
2731 //! get array of groups (or indexes) before loop?
2732 ScDPItemData aMemberData;
2733 rMember.FillItemData( aMemberData );
2735 const std::vector<ScDPInitState::Member>& rMemStates = rInitState.GetMembers();
2736 std::vector<ScDPInitState::Member>::const_iterator it = rMemStates.begin(), itEnd = rMemStates.end();
2737 for (; it != itEnd && bInclude; ++it)
2739 if (pResultData->GetGroupBase(it->mnSrcIndex) == nDimSource)
2741 bInclude = pResultData->IsInGroup(
2742 it->mnNameIndex, it->mnSrcIndex, aMemberData, nDimSource);
2746 else if ( nGroupBase >= 0 )
2748 // base isn't used in preceding fields
2749 // -> look for other groups using the same base
2751 //! get array of groups (or indexes) before loop?
2752 ScDPItemData aMemberData;
2753 rMember.FillItemData( aMemberData );
2754 const std::vector<ScDPInitState::Member>& rMemStates = rInitState.GetMembers();
2755 std::vector<ScDPInitState::Member>::const_iterator it = rMemStates.begin(), itEnd = rMemStates.end();
2756 for (; it != itEnd && bInclude; ++it)
2758 if (pResultData->GetGroupBase(it->mnSrcIndex) == nGroupBase)
2760 // same base (hierarchy between the two groups is irrelevant)
2761 bInclude = pResultData->HasCommonElement(
2762 it->mnNameIndex, it->mnSrcIndex, aMemberData, nDimSource);
2768 return bInclude;
2771 // -----------------------------------------------------------------------
2773 ScDPResultDimension::ScDPResultDimension( const ScDPResultData* pData ) :
2774 pResultData( pData ),
2775 nSortMeasure( 0 ),
2776 bIsDataLayout( false ),
2777 bSortByData( false ),
2778 bSortAscending( false ),
2779 bAutoShow( false ),
2780 bAutoTopItems( false ),
2781 bInitialized( false ),
2782 nAutoMeasure( 0 ),
2783 nAutoCount( 0 )
2787 ScDPResultDimension::~ScDPResultDimension()
2789 for( int i = maMemberArray.size () ; i-- > 0 ; )
2790 delete maMemberArray[i];
2793 ScDPResultMember *ScDPResultDimension::FindMember( SCROW iData ) const
2795 if( bIsDataLayout )
2796 return maMemberArray[0];
2798 MemberHash::const_iterator aRes = maMemberHash.find( iData );
2799 if( aRes != maMemberHash.end()) {
2800 if ( aRes->second->IsNamedItem( iData ) )
2801 return aRes->second;
2802 OSL_FAIL("problem! hash result is not the same as IsNamedItem");
2805 unsigned int i;
2806 unsigned int nCount = maMemberArray.size();
2807 ScDPResultMember* pResultMember;
2808 for( i = 0; i < nCount ; i++ )
2810 pResultMember = maMemberArray[i];
2811 if ( pResultMember->IsNamedItem( iData ) )
2812 return pResultMember;
2814 return NULL;
2817 void ScDPResultDimension::InitFrom(
2818 const vector<ScDPDimension*>& ppDim, const vector<ScDPLevel*>& ppLev,
2819 size_t nPos, ScDPInitState& rInitState, bool bInitChild )
2821 if (nPos >= ppDim.size() || nPos >= ppLev.size())
2823 bInitialized = true;
2824 return;
2827 ScDPDimension* pThisDim = ppDim[nPos];
2828 ScDPLevel* pThisLevel = ppLev[nPos];
2830 if (!pThisDim || !pThisLevel)
2832 bInitialized = true;
2833 return;
2836 bIsDataLayout = pThisDim->getIsDataLayoutDimension(); // member
2837 aDimensionName = pThisDim->getName(); // member
2839 // Check the autoshow setting. If it's enabled, store the settings.
2840 const sheet::DataPilotFieldAutoShowInfo& rAutoInfo = pThisLevel->GetAutoShow();
2841 if ( rAutoInfo.IsEnabled )
2843 bAutoShow = true;
2844 bAutoTopItems = ( rAutoInfo.ShowItemsMode == sheet::DataPilotFieldShowItemsMode::FROM_TOP );
2845 nAutoMeasure = pThisLevel->GetAutoMeasure();
2846 nAutoCount = rAutoInfo.ItemCount;
2849 // Check the sort info, and store the settings if appropriate.
2850 const sheet::DataPilotFieldSortInfo& rSortInfo = pThisLevel->GetSortInfo();
2851 if ( rSortInfo.Mode == sheet::DataPilotFieldSortMode::DATA )
2853 bSortByData = true;
2854 bSortAscending = rSortInfo.IsAscending;
2855 nSortMeasure = pThisLevel->GetSortMeasure();
2858 // global order is used to initialize aMembers, so it doesn't have to be looked at later
2859 const ScMemberSortOrder& rGlobalOrder = pThisLevel->GetGlobalOrder();
2861 long nDimSource = pThisDim->GetDimension(); //! check GetSourceDim?
2862 ScDPGroupCompare aCompare( pResultData, rInitState, nDimSource );
2864 // Now, go through all members and initialize them.
2865 ScDPMembers* pMembers = pThisLevel->GetMembersObject();
2866 long nMembCount = pMembers->getCount();
2867 for ( long i=0; i<nMembCount; i++ )
2869 long nSorted = rGlobalOrder.empty() ? i : rGlobalOrder[i];
2871 ScDPMember* pMember = pMembers->getByIndex(nSorted);
2872 if ( aCompare.IsIncluded( *pMember ) )
2874 ScDPParentDimData aData( i, pThisDim, pThisLevel, pMember);
2875 ScDPResultMember* pNew = AddMember( aData );
2877 rInitState.AddMember(nDimSource, pNew->GetDataId());
2878 pNew->InitFrom( ppDim, ppLev, nPos+1, rInitState, bInitChild );
2879 rInitState.RemoveMember();
2882 bInitialized = true;
2885 void ScDPResultDimension::LateInitFrom(
2886 LateInitParams& rParams, const vector<SCROW>& pItemData, size_t nPos, ScDPInitState& rInitState)
2888 if ( rParams.IsEnd( nPos ) )
2889 return;
2890 OSL_ENSURE( nPos <= pItemData.size(), OString::number(pItemData.size()).getStr() );
2891 ScDPDimension* pThisDim = rParams.GetDim( nPos );
2892 ScDPLevel* pThisLevel = rParams.GetLevel( nPos );
2893 SCROW rThisData = pItemData[nPos];
2895 if (!pThisDim || !pThisLevel)
2896 return;
2898 long nDimSource = pThisDim->GetDimension(); //! check GetSourceDim?
2900 bool bShowEmpty = pThisLevel->getShowEmpty();
2902 if ( !bInitialized )
2903 { // init some values
2904 // create all members at the first call (preserve order)
2905 bIsDataLayout = pThisDim->getIsDataLayoutDimension();
2906 aDimensionName = pThisDim->getName();
2908 const sheet::DataPilotFieldAutoShowInfo& rAutoInfo = pThisLevel->GetAutoShow();
2909 if ( rAutoInfo.IsEnabled )
2911 bAutoShow = true;
2912 bAutoTopItems = ( rAutoInfo.ShowItemsMode == sheet::DataPilotFieldShowItemsMode::FROM_TOP );
2913 nAutoMeasure = pThisLevel->GetAutoMeasure();
2914 nAutoCount = rAutoInfo.ItemCount;
2917 const sheet::DataPilotFieldSortInfo& rSortInfo = pThisLevel->GetSortInfo();
2918 if ( rSortInfo.Mode == sheet::DataPilotFieldSortMode::DATA )
2920 bSortByData = true;
2921 bSortAscending = rSortInfo.IsAscending;
2922 nSortMeasure = pThisLevel->GetSortMeasure();
2926 bool bLateInitAllMembers= bIsDataLayout || rParams.GetInitAllChild() || bShowEmpty;
2928 if ( !bLateInitAllMembers )
2930 ResultMembers* pMembers = pResultData->GetDimResultMembers(nDimSource, pThisDim, pThisLevel);
2931 bLateInitAllMembers = pMembers->IsHasHideDetailsMembers();
2932 #if OSL_DEBUG_LEVEL > 1
2933 OSL_TRACE( "%s", aDimensionName.getStr() );
2934 if ( pMembers->IsHasHideDetailsMembers() )
2935 OSL_TRACE( "HasHideDetailsMembers" );
2936 #endif
2937 pMembers->SetHasHideDetailsMembers( false );
2940 bool bNewAllMembers = (!rParams.IsRow()) || nPos == 0 || bLateInitAllMembers;
2942 if (bNewAllMembers )
2944 // global order is used to initialize aMembers, so it doesn't have to be looked at later
2945 if ( !bInitialized )
2946 { //init all members
2947 const ScMemberSortOrder& rGlobalOrder = pThisLevel->GetGlobalOrder();
2949 ScDPGroupCompare aCompare( pResultData, rInitState, nDimSource );
2950 ScDPMembers* pMembers = pThisLevel->GetMembersObject();
2951 long nMembCount = pMembers->getCount();
2952 for ( long i=0; i<nMembCount; i++ )
2954 long nSorted = rGlobalOrder.empty() ? i : rGlobalOrder[i];
2956 ScDPMember* pMember = pMembers->getByIndex(nSorted);
2957 if ( aCompare.IsIncluded( *pMember ) )
2958 { // add all members
2959 ScDPParentDimData aData( i, pThisDim, pThisLevel, pMember );
2960 AddMember( aData );
2963 bInitialized = true; // don't call again, even if no members were included
2965 // initialize only specific member (or all if "show empty" flag is set)
2966 if ( bLateInitAllMembers )
2968 long nCount = maMemberArray.size();
2969 for (long i=0; i<nCount; i++)
2971 ScDPResultMember* pResultMember = maMemberArray[i];
2973 // check show empty
2974 bool bAllChildren = false;
2975 if( bShowEmpty )
2977 if ( pResultMember->IsNamedItem( rThisData ) )
2978 bAllChildren = false;
2979 else
2980 bAllChildren = true;
2982 rParams.SetInitAllChildren( bAllChildren );
2983 rInitState.AddMember( nDimSource, pResultMember->GetDataId() );
2984 pResultMember->LateInitFrom( rParams, pItemData, nPos+1, rInitState );
2985 rInitState.RemoveMember();
2988 else
2990 ScDPResultMember* pResultMember = FindMember( rThisData );
2991 if( NULL != pResultMember )
2993 rInitState.AddMember( nDimSource, pResultMember->GetDataId() );
2994 pResultMember->LateInitFrom( rParams, pItemData, nPos+1, rInitState );
2995 rInitState.RemoveMember();
2999 else
3000 InitWithMembers( rParams, pItemData, nPos, rInitState );
3003 long ScDPResultDimension::GetSize(long nMeasure) const
3005 long nTotal = 0;
3006 long nMemberCount = maMemberArray.size();
3007 if (bIsDataLayout)
3009 OSL_ENSURE(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1,
3010 "DataLayout dimension twice?");
3011 // repeat first member...
3012 nTotal = nMemberCount * maMemberArray[0]->GetSize(0); // all measures have equal size
3014 else
3016 // add all members
3017 for (long nMem=0; nMem<nMemberCount; nMem++)
3018 nTotal += maMemberArray[nMem]->GetSize(nMeasure);
3020 return nTotal;
3023 bool ScDPResultDimension::IsValidEntry( const vector< SCROW >& aMembers ) const
3025 if (aMembers.empty())
3026 return false;
3028 const ScDPResultMember* pMember = FindMember( aMembers[0] );
3029 if ( NULL != pMember )
3030 return pMember->IsValidEntry( aMembers );
3031 #if OSL_DEBUG_LEVEL > 1
3032 OStringBuffer strTemp(RTL_CONSTASCII_STRINGPARAM(
3033 "IsValidEntry: Member not found, DimName = "));
3034 strTemp.append(OUStringToOString(GetName(), RTL_TEXTENCODING_UTF8));
3035 OSL_TRACE("%s", strTemp.getStr());
3036 #endif
3037 return false;
3040 void ScDPResultDimension::ProcessData( const vector< SCROW >& aMembers,
3041 const ScDPResultDimension* pDataDim,
3042 const vector< SCROW >& aDataMembers,
3043 const vector<ScDPValue>& aValues ) const
3045 if (aMembers.empty())
3046 return;
3048 ScDPResultMember* pMember = FindMember( aMembers[0] );
3049 if ( NULL != pMember )
3051 vector<SCROW> aChildMembers;
3052 if (aMembers.size() > 1)
3054 vector<SCROW>::const_iterator itr = aMembers.begin();
3055 aChildMembers.insert(aChildMembers.begin(), ++itr, aMembers.end());
3057 pMember->ProcessData( aChildMembers, pDataDim, aDataMembers, aValues );
3058 return;
3061 OSL_FAIL("ProcessData: Member not found");
3064 void ScDPResultDimension::FillMemberResults( uno::Sequence<sheet::MemberResult>* pSequences,
3065 long nStart, long nMeasure )
3067 long nPos = nStart;
3068 long nCount = maMemberArray.size();
3070 for (long i=0; i<nCount; i++)
3072 long nSorted = aMemberOrder.empty() ? i : aMemberOrder[i];
3074 ScDPResultMember* pMember = maMemberArray[nSorted];
3075 // in data layout dimension, use first member with different measures/names
3076 if ( bIsDataLayout )
3078 bool bTotalResult = false;
3079 OUString aMbrName = pResultData->GetMeasureDimensionName( nSorted );
3080 OUString aMbrCapt = pResultData->GetMeasureString( nSorted, false, SUBTOTAL_FUNC_NONE, bTotalResult );
3081 maMemberArray[0]->FillMemberResults( pSequences, nPos, nSorted, false, &aMbrName, &aMbrCapt );
3083 else if ( pMember->IsVisible() )
3085 pMember->FillMemberResults( pSequences, nPos, nMeasure, false, NULL, NULL );
3087 // nPos is modified
3091 void ScDPResultDimension::FillDataResults(
3092 const ScDPResultMember* pRefMember, ScDPResultFilterContext& rFilterCxt,
3093 uno::Sequence< uno::Sequence<sheet::DataResult> >& rSequence, long nMeasure) const
3095 FilterStack aFilterStack(rFilterCxt.maFilters);
3096 aFilterStack.pushDimName(GetName(), bIsDataLayout);
3098 long nMemberMeasure = nMeasure;
3099 long nCount = maMemberArray.size();
3100 for (long i=0; i<nCount; i++)
3102 long nSorted = aMemberOrder.empty() ? i : aMemberOrder[i];
3104 const ScDPResultMember* pMember;
3105 if (bIsDataLayout)
3107 OSL_ENSURE(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1,
3108 "DataLayout dimension twice?");
3109 pMember = maMemberArray[0];
3110 nMemberMeasure = nSorted;
3112 else
3113 pMember = maMemberArray[nSorted];
3115 if ( pMember->IsVisible() )
3116 pMember->FillDataResults(pRefMember, rFilterCxt, rSequence, nMemberMeasure);
3120 void ScDPResultDimension::UpdateDataResults( const ScDPResultMember* pRefMember, long nMeasure ) const
3122 long nMemberMeasure = nMeasure;
3123 long nCount = maMemberArray.size();
3124 for (long i=0; i<nCount; i++)
3126 const ScDPResultMember* pMember;
3127 if (bIsDataLayout)
3129 OSL_ENSURE(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1,
3130 "DataLayout dimension twice?");
3131 pMember = maMemberArray[0];
3132 nMemberMeasure = i;
3134 else
3135 pMember = maMemberArray[i];
3137 if ( pMember->IsVisible() )
3138 pMember->UpdateDataResults( pRefMember, nMemberMeasure );
3142 void ScDPResultDimension::SortMembers( ScDPResultMember* pRefMember )
3144 long nCount = maMemberArray.size();
3146 if ( bSortByData )
3148 // sort members
3150 OSL_ENSURE( aMemberOrder.empty(), "sort twice?" );
3151 aMemberOrder.resize( nCount );
3152 for (long nPos=0; nPos<nCount; nPos++)
3153 aMemberOrder[nPos] = nPos;
3155 ScDPRowMembersOrder aComp( *this, nSortMeasure, bSortAscending );
3156 ::std::sort( aMemberOrder.begin(), aMemberOrder.end(), aComp );
3159 // handle children
3161 // for data layout, call only once - sorting measure is always taken from settings
3162 long nLoopCount = bIsDataLayout ? 1 : nCount;
3163 for (long i=0; i<nLoopCount; i++)
3165 ScDPResultMember* pMember = maMemberArray[i];
3166 if ( pMember->IsVisible() )
3167 pMember->SortMembers( pRefMember );
3171 void ScDPResultDimension::DoAutoShow( ScDPResultMember* pRefMember )
3173 long nCount = maMemberArray.size();
3175 // handle children first, before changing the visible state
3177 // for data layout, call only once - sorting measure is always taken from settings
3178 long nLoopCount = bIsDataLayout ? 1 : nCount;
3179 for (long i=0; i<nLoopCount; i++)
3181 ScDPResultMember* pMember = maMemberArray[i];
3182 if ( pMember->IsVisible() )
3183 pMember->DoAutoShow( pRefMember );
3186 if ( bAutoShow && nAutoCount > 0 && nAutoCount < nCount )
3188 // establish temporary order, hide remaining members
3190 ScMemberSortOrder aAutoOrder;
3191 aAutoOrder.resize( nCount );
3192 long nPos;
3193 for (nPos=0; nPos<nCount; nPos++)
3194 aAutoOrder[nPos] = nPos;
3196 ScDPRowMembersOrder aComp( *this, nAutoMeasure, !bAutoTopItems );
3197 ::std::sort( aAutoOrder.begin(), aAutoOrder.end(), aComp );
3199 // look for equal values to the last included one
3201 long nIncluded = nAutoCount;
3202 const ScDPResultMember* pMember1 = maMemberArray[aAutoOrder[nIncluded - 1]];
3203 const ScDPDataMember* pDataMember1 = pMember1->IsVisible() ? pMember1->GetDataRoot() : NULL;
3204 bool bContinue = true;
3205 while ( bContinue )
3207 bContinue = false;
3208 if ( nIncluded < nCount )
3210 const ScDPResultMember* pMember2 = maMemberArray[aAutoOrder[nIncluded]];
3211 const ScDPDataMember* pDataMember2 = pMember2->IsVisible() ? pMember2->GetDataRoot() : NULL;
3213 if ( lcl_IsEqual( pDataMember1, pDataMember2, nAutoMeasure ) )
3215 ++nIncluded; // include more members if values are equal
3216 bContinue = true;
3221 // hide the remaining members
3223 for (nPos = nIncluded; nPos < nCount; nPos++)
3225 ScDPResultMember* pMember = maMemberArray[aAutoOrder[nPos]];
3226 pMember->SetAutoHidden();
3231 void ScDPResultDimension::ResetResults()
3233 long nCount = maMemberArray.size();
3234 for (long i=0; i<nCount; i++)
3236 // sort order doesn't matter
3237 ScDPResultMember* pMember = maMemberArray[bIsDataLayout ? 0 : i];
3238 pMember->ResetResults();
3242 long ScDPResultDimension::GetSortedIndex( long nUnsorted ) const
3244 return aMemberOrder.empty() ? nUnsorted : aMemberOrder[nUnsorted];
3247 void ScDPResultDimension::UpdateRunningTotals( const ScDPResultMember* pRefMember, long nMeasure,
3248 ScDPRunningTotalState& rRunning, ScDPRowTotals& rTotals ) const
3250 const ScDPResultMember* pMember;
3251 long nMemberMeasure = nMeasure;
3252 long nCount = maMemberArray.size();
3253 for (long i=0; i<nCount; i++)
3255 long nSorted = aMemberOrder.empty() ? i : aMemberOrder[i];
3257 if (bIsDataLayout)
3259 OSL_ENSURE(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1,
3260 "DataLayout dimension twice?");
3261 pMember = maMemberArray[0];
3262 nMemberMeasure = nSorted;
3264 else
3265 pMember = maMemberArray[nSorted];
3267 if ( pMember->IsVisible() )
3269 if ( bIsDataLayout )
3270 rRunning.AddRowIndex( 0, 0 );
3271 else
3272 rRunning.AddRowIndex( i, nSorted );
3273 pMember->UpdateRunningTotals( pRefMember, nMemberMeasure, rRunning, rTotals );
3274 rRunning.RemoveRowIndex();
3279 ScDPDataMember* ScDPResultDimension::GetRowReferenceMember(
3280 const ScDPRelativePos* pRelativePos, const OUString* pName,
3281 const long* pRowIndexes, const long* pColIndexes ) const
3283 // get named, previous/next, or first member of this dimension (first existing if pRelativePos and pName are NULL)
3285 OSL_ENSURE( pRelativePos == NULL || pName == NULL, "can't use position and name" );
3287 ScDPDataMember* pColMember = NULL;
3289 bool bFirstExisting = ( pRelativePos == NULL && pName == NULL );
3290 long nMemberCount = maMemberArray.size();
3291 long nMemberIndex = 0; // unsorted
3292 long nDirection = 1; // forward if no relative position is used
3293 if ( pRelativePos )
3295 nDirection = pRelativePos->nDirection;
3296 nMemberIndex = pRelativePos->nBasePos + nDirection; // bounds are handled below
3298 OSL_ENSURE( nDirection == 1 || nDirection == -1, "Direction must be 1 or -1" );
3300 else if ( pName )
3302 // search for named member
3304 const ScDPResultMember* pRowMember = maMemberArray[GetSortedIndex(nMemberIndex)];
3306 //! use ScDPItemData, as in ScDPDimension::IsValidPage?
3307 while ( pRowMember && pRowMember->GetName() != *pName )
3309 ++nMemberIndex;
3310 if ( nMemberIndex < nMemberCount )
3311 pRowMember = maMemberArray[GetSortedIndex(nMemberIndex)];
3312 else
3313 pRowMember = NULL;
3317 bool bContinue = true;
3318 while ( bContinue && nMemberIndex >= 0 && nMemberIndex < nMemberCount )
3320 const ScDPResultMember* pRowMember = maMemberArray[GetSortedIndex(nMemberIndex)];
3322 // get child members by given indexes
3324 const long* pNextRowIndex = pRowIndexes;
3325 while ( *pNextRowIndex >= 0 && pRowMember )
3327 const ScDPResultDimension* pRowChild = pRowMember->GetChildDimension();
3328 if ( pRowChild && *pNextRowIndex < pRowChild->GetMemberCount() )
3329 pRowMember = pRowChild->GetMember( *pNextRowIndex );
3330 else
3331 pRowMember = NULL;
3332 ++pNextRowIndex;
3335 if ( pRowMember && pRelativePos )
3337 // Skip the member if it has hidden details
3338 // (because when looking for the details, it is skipped, too).
3339 // Also skip if the member is invisible because it has no data,
3340 // for consistent ordering.
3341 if ( pRowMember->HasHiddenDetails() || !pRowMember->IsVisible() )
3342 pRowMember = NULL;
3345 if ( pRowMember )
3347 pColMember = pRowMember->GetDataRoot();
3349 const long* pNextColIndex = pColIndexes;
3350 while ( *pNextColIndex >= 0 && pColMember )
3352 ScDPDataDimension* pColChild = pColMember->GetChildDimension();
3353 if ( pColChild && *pNextColIndex < pColChild->GetMemberCount() )
3354 pColMember = pColChild->GetMember( *pNextColIndex );
3355 else
3356 pColMember = NULL;
3357 ++pNextColIndex;
3361 // continue searching only if looking for first existing or relative position
3362 bContinue = ( pColMember == NULL && ( bFirstExisting || pRelativePos ) );
3363 nMemberIndex += nDirection;
3366 return pColMember;
3369 ScDPDataMember* ScDPResultDimension::GetColReferenceMember(
3370 const ScDPRelativePos* pRelativePos, const OUString* pName,
3371 long nRefDimPos, const ScDPRunningTotalState& rRunning )
3373 OSL_ENSURE( pRelativePos == NULL || pName == NULL, "can't use position and name" );
3375 const long* pColIndexes = &rRunning.GetColSorted()[0];
3376 const long* pRowIndexes = &rRunning.GetRowSorted()[0];
3378 // get own row member using all indexes
3380 const ScDPResultMember* pRowMember = rRunning.GetRowResRoot();
3381 ScDPDataMember* pColMember = NULL;
3383 const long* pNextRowIndex = pRowIndexes;
3384 while ( *pNextRowIndex >= 0 && pRowMember )
3386 const ScDPResultDimension* pRowChild = pRowMember->GetChildDimension();
3387 if ( pRowChild && *pNextRowIndex < pRowChild->GetMemberCount() )
3388 pRowMember = pRowChild->GetMember( *pNextRowIndex );
3389 else
3390 pRowMember = NULL;
3391 ++pNextRowIndex;
3394 // get column (data) members before the reference field
3395 //! pass rRowParent from ScDPDataMember::UpdateRunningTotals instead
3397 if ( pRowMember )
3399 pColMember = pRowMember->GetDataRoot();
3401 const long* pNextColIndex = pColIndexes;
3402 long nColSkipped = 0;
3403 while ( *pNextColIndex >= 0 && pColMember && nColSkipped < nRefDimPos )
3405 ScDPDataDimension* pColChild = pColMember->GetChildDimension();
3406 if ( pColChild && *pNextColIndex < pColChild->GetMemberCount() )
3407 pColMember = pColChild->GetMember( *pNextColIndex );
3408 else
3409 pColMember = NULL;
3410 ++pNextColIndex;
3411 ++nColSkipped;
3415 // get column member for the reference field
3417 if ( pColMember )
3419 ScDPDataDimension* pReferenceDim = pColMember->GetChildDimension();
3420 if ( pReferenceDim )
3422 long nReferenceCount = pReferenceDim->GetMemberCount();
3424 bool bFirstExisting = ( pRelativePos == NULL && pName == NULL );
3425 long nMemberIndex = 0; // unsorted
3426 long nDirection = 1; // forward if no relative position is used
3427 pColMember = NULL; // don't use parent dimension's member if none found
3428 if ( pRelativePos )
3430 nDirection = pRelativePos->nDirection;
3431 nMemberIndex = pRelativePos->nBasePos + nDirection; // bounds are handled below
3433 else if ( pName )
3435 // search for named member
3437 pColMember = pReferenceDim->GetMember( pReferenceDim->GetSortedIndex( nMemberIndex ) );
3439 //! use ScDPItemData, as in ScDPDimension::IsValidPage?
3440 while ( pColMember && pColMember->GetName() != *pName )
3442 ++nMemberIndex;
3443 if ( nMemberIndex < nReferenceCount )
3444 pColMember = pReferenceDim->GetMember( pReferenceDim->GetSortedIndex( nMemberIndex ) );
3445 else
3446 pColMember = NULL;
3450 bool bContinue = true;
3451 while ( bContinue && nMemberIndex >= 0 && nMemberIndex < nReferenceCount )
3453 pColMember = pReferenceDim->GetMember( pReferenceDim->GetSortedIndex( nMemberIndex ) );
3455 // get column members below the reference field
3457 const long* pNextColIndex = pColIndexes + nRefDimPos + 1;
3458 while ( *pNextColIndex >= 0 && pColMember )
3460 ScDPDataDimension* pColChild = pColMember->GetChildDimension();
3461 if ( pColChild && *pNextColIndex < pColChild->GetMemberCount() )
3462 pColMember = pColChild->GetMember( *pNextColIndex );
3463 else
3464 pColMember = NULL;
3465 ++pNextColIndex;
3468 if ( pColMember && pRelativePos )
3470 // Skip the member if it has hidden details
3471 // (because when looking for the details, it is skipped, too).
3472 // Also skip if the member is invisible because it has no data,
3473 // for consistent ordering.
3474 if ( pColMember->HasHiddenDetails() || !pColMember->IsVisible() )
3475 pColMember = NULL;
3478 // continue searching only if looking for first existing or relative position
3479 bContinue = ( pColMember == NULL && ( bFirstExisting || pRelativePos ) );
3480 nMemberIndex += nDirection;
3483 else
3484 pColMember = NULL;
3487 return pColMember;
3490 #if DEBUG_PIVOT_TABLE
3491 void ScDPResultDimension::DumpState( const ScDPResultMember* pRefMember, ScDocument* pDoc, ScAddress& rPos ) const
3493 OUString aDimName = bIsDataLayout ? OUString("(data layout)") : OUString(GetName());
3494 lcl_DumpRow( OUString("ScDPResultDimension"), aDimName, NULL, pDoc, rPos );
3496 SCROW nStartRow = rPos.Row();
3498 long nCount = bIsDataLayout ? 1 : maMemberArray.size();
3499 for (long i=0; i<nCount; i++)
3501 const ScDPResultMember* pMember = maMemberArray[i];
3502 pMember->DumpState( pRefMember, pDoc, rPos );
3505 lcl_Indent( pDoc, nStartRow, rPos );
3508 void ScDPResultDimension::Dump(int nIndent) const
3510 std::string aIndent(nIndent*2, ' ');
3511 std::cout << aIndent << "-- dimension '" << GetName() << "'" << std::endl;
3512 MemberArray::const_iterator it = maMemberArray.begin(), itEnd = maMemberArray.end();
3513 for (; it != itEnd; ++it)
3515 const ScDPResultMember* p = *it;
3516 p->Dump(nIndent+1);
3519 #endif
3521 long ScDPResultDimension::GetMemberCount() const
3523 return maMemberArray.size();
3526 const ScDPResultMember* ScDPResultDimension::GetMember(long n) const
3528 return maMemberArray[n];
3530 ScDPResultMember* ScDPResultDimension::GetMember(long n)
3532 return maMemberArray[n];
3535 ScDPResultDimension* ScDPResultDimension::GetFirstChildDimension() const
3537 if ( maMemberArray.size() > 0 )
3538 return maMemberArray[0]->GetChildDimension();
3539 else
3540 return NULL;
3543 void ScDPResultDimension::FillVisibilityData(ScDPResultVisibilityData& rData) const
3545 if (IsDataLayout())
3546 return;
3548 MemberArray::const_iterator itr = maMemberArray.begin(), itrEnd = maMemberArray.end();
3550 for (;itr != itrEnd; ++itr)
3552 ScDPResultMember* pMember = *itr;
3553 if (pMember->IsValid())
3555 ScDPItemData aItem;
3556 pMember->FillItemData(aItem);
3557 rData.addVisibleMember(GetName(), aItem);
3558 pMember->FillVisibilityData(rData);
3563 // -----------------------------------------------------------------------
3565 ScDPDataDimension::ScDPDataDimension( const ScDPResultData* pData ) :
3566 pResultData( pData ),
3567 pResultDimension( NULL ),
3568 bIsDataLayout( false )
3572 ScDPDataDimension::~ScDPDataDimension()
3574 std::for_each(maMembers.begin(), maMembers.end(), ScDeleteObjectByPtr<ScDPDataMember>());
3577 void ScDPDataDimension::InitFrom( const ScDPResultDimension* pDim )
3579 if (!pDim)
3580 return;
3582 pResultDimension = pDim;
3583 bIsDataLayout = pDim->IsDataLayout();
3585 // Go through all result members under the given result dimension, and
3586 // create a new data member instance for each result member.
3587 long nCount = pDim->GetMemberCount();
3588 for (long i=0; i<nCount; i++)
3590 const ScDPResultMember* pResMem = pDim->GetMember(i);
3592 ScDPDataMember* pNew = new ScDPDataMember( pResultData, pResMem );
3593 maMembers.push_back( pNew);
3595 if ( !pResultData->IsLateInit() )
3597 // with LateInit, pResMem hasn't necessarily been initialized yet,
3598 // so InitFrom for the new result member is called from its ProcessData method
3600 const ScDPResultDimension* pChildDim = pResMem->GetChildDimension();
3601 if ( pChildDim )
3602 pNew->InitFrom( pChildDim );
3607 void ScDPDataDimension::ProcessData( const vector< SCROW >& aDataMembers, const vector<ScDPValue>& aValues,
3608 const ScDPSubTotalState& rSubState )
3610 // the ScDPItemData array must contain enough entries for all dimensions - this isn't checked
3612 long nCount = maMembers.size();
3613 for (long i=0; i<nCount; i++)
3615 ScDPDataMember* pMember = maMembers[(sal_uInt16)i];
3617 // always first member for data layout dim
3618 if ( bIsDataLayout || ( !aDataMembers.empty() && pMember->IsNamedItem(aDataMembers[0]) ) )
3620 vector<SCROW> aChildDataMembers;
3621 if (aDataMembers.size() > 1)
3623 vector<SCROW>::const_iterator itr = aDataMembers.begin();
3624 aChildDataMembers.insert(aChildDataMembers.begin(), ++itr, aDataMembers.end());
3626 pMember->ProcessData( aChildDataMembers, aValues, rSubState );
3627 return;
3631 OSL_FAIL("ProcessData: Member not found");
3634 void ScDPDataDimension::FillDataRow(
3635 const ScDPResultDimension* pRefDim, ScDPResultFilterContext& rFilterCxt,
3636 uno::Sequence<sheet::DataResult>& rSequence, long nMeasure, bool bIsSubTotalRow,
3637 const ScDPSubTotalState& rSubState) const
3639 OUString aDimName;
3640 bool bDataLayout = false;
3641 if (pResultDimension)
3643 aDimName = pResultDimension->GetName();
3644 bDataLayout = pResultDimension->IsDataLayout();
3647 FilterStack aFilterStack(rFilterCxt.maFilters);
3648 aFilterStack.pushDimName(aDimName, bDataLayout);
3650 OSL_ENSURE( pRefDim && static_cast<size_t>(pRefDim->GetMemberCount()) == maMembers.size(), "dimensions don't match" );
3651 OSL_ENSURE( pRefDim == pResultDimension, "wrong dim" );
3653 const ScMemberSortOrder& rMemberOrder = pRefDim->GetMemberOrder();
3655 long nMemberMeasure = nMeasure;
3656 long nCount = maMembers.size();
3657 for (long i=0; i<nCount; i++)
3659 long nSorted = rMemberOrder.empty() ? i : rMemberOrder[i];
3661 long nMemberPos = nSorted;
3662 if (bIsDataLayout)
3664 OSL_ENSURE(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1,
3665 "DataLayout dimension twice?");
3666 nMemberPos = 0;
3667 nMemberMeasure = nSorted;
3670 const ScDPResultMember* pRefMember = pRefDim->GetMember(nMemberPos);
3671 if ( pRefMember->IsVisible() ) //! here or in ScDPDataMember::FillDataRow ???
3673 const ScDPDataMember* pDataMember = maMembers[(sal_uInt16)nMemberPos];
3674 pDataMember->FillDataRow(pRefMember, rFilterCxt, rSequence, nMemberMeasure, bIsSubTotalRow, rSubState);
3679 void ScDPDataDimension::UpdateDataRow( const ScDPResultDimension* pRefDim,
3680 long nMeasure, bool bIsSubTotalRow,
3681 const ScDPSubTotalState& rSubState ) const
3683 OSL_ENSURE( pRefDim && static_cast<size_t>(pRefDim->GetMemberCount()) == maMembers.size(), "dimensions don't match" );
3684 OSL_ENSURE( pRefDim == pResultDimension, "wrong dim" );
3686 long nMemberMeasure = nMeasure;
3687 long nCount = maMembers.size();
3688 for (long i=0; i<nCount; i++)
3690 long nMemberPos = i;
3691 if (bIsDataLayout)
3693 OSL_ENSURE(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1,
3694 "DataLayout dimension twice?");
3695 nMemberPos = 0;
3696 nMemberMeasure = i;
3699 // Calculate must be called even if the member is not visible (for use as reference value)
3700 const ScDPResultMember* pRefMember = pRefDim->GetMember(nMemberPos);
3701 ScDPDataMember* pDataMember = maMembers[(sal_uInt16)nMemberPos];
3702 pDataMember->UpdateDataRow( pRefMember, nMemberMeasure, bIsSubTotalRow, rSubState );
3706 void ScDPDataDimension::SortMembers( ScDPResultDimension* pRefDim )
3708 long nCount = maMembers.size();
3710 if ( pRefDim->IsSortByData() )
3712 // sort members
3714 ScMemberSortOrder& rMemberOrder = pRefDim->GetMemberOrder();
3715 OSL_ENSURE( rMemberOrder.empty(), "sort twice?" );
3716 rMemberOrder.resize( nCount );
3717 for (long nPos=0; nPos<nCount; nPos++)
3718 rMemberOrder[nPos] = nPos;
3720 ScDPColMembersOrder aComp( *this, pRefDim->GetSortMeasure(), pRefDim->IsSortAscending() );
3721 ::std::sort( rMemberOrder.begin(), rMemberOrder.end(), aComp );
3724 // handle children
3726 OSL_ENSURE( pRefDim && static_cast<size_t>(pRefDim->GetMemberCount()) == maMembers.size(), "dimensions don't match" );
3727 OSL_ENSURE( pRefDim == pResultDimension, "wrong dim" );
3729 // for data layout, call only once - sorting measure is always taken from settings
3730 long nLoopCount = bIsDataLayout ? 1 : nCount;
3731 for (long i=0; i<nLoopCount; i++)
3733 ScDPResultMember* pRefMember = pRefDim->GetMember(i);
3734 if ( pRefMember->IsVisible() ) //! here or in ScDPDataMember ???
3736 ScDPDataMember* pDataMember = maMembers[(sal_uInt16)i];
3737 pDataMember->SortMembers( pRefMember );
3742 void ScDPDataDimension::DoAutoShow( ScDPResultDimension* pRefDim )
3744 long nCount = maMembers.size();
3746 // handle children first, before changing the visible state
3748 OSL_ENSURE( pRefDim && static_cast<size_t>(pRefDim->GetMemberCount()) == maMembers.size(), "dimensions don't match" );
3749 OSL_ENSURE( pRefDim == pResultDimension, "wrong dim" );
3751 // for data layout, call only once - sorting measure is always taken from settings
3752 long nLoopCount = bIsDataLayout ? 1 : nCount;
3753 for (long i=0; i<nLoopCount; i++)
3755 ScDPResultMember* pRefMember = pRefDim->GetMember(i);
3756 if ( pRefMember->IsVisible() ) //! here or in ScDPDataMember ???
3758 ScDPDataMember* pDataMember = maMembers[i];
3759 pDataMember->DoAutoShow( pRefMember );
3763 if ( pRefDim->IsAutoShow() && pRefDim->GetAutoCount() > 0 && pRefDim->GetAutoCount() < nCount )
3765 // establish temporary order, hide remaining members
3767 ScMemberSortOrder aAutoOrder;
3768 aAutoOrder.resize( nCount );
3769 long nPos;
3770 for (nPos=0; nPos<nCount; nPos++)
3771 aAutoOrder[nPos] = nPos;
3773 ScDPColMembersOrder aComp( *this, pRefDim->GetAutoMeasure(), !pRefDim->IsAutoTopItems() );
3774 ::std::sort( aAutoOrder.begin(), aAutoOrder.end(), aComp );
3776 // look for equal values to the last included one
3778 long nIncluded = pRefDim->GetAutoCount();
3779 ScDPDataMember* pDataMember1 = maMembers[aAutoOrder[nIncluded - 1]];
3780 if ( !pDataMember1->IsVisible() )
3781 pDataMember1 = NULL;
3782 bool bContinue = true;
3783 while ( bContinue )
3785 bContinue = false;
3786 if ( nIncluded < nCount )
3788 ScDPDataMember* pDataMember2 = maMembers[aAutoOrder[nIncluded]];
3789 if ( !pDataMember2->IsVisible() )
3790 pDataMember2 = NULL;
3792 if ( lcl_IsEqual( pDataMember1, pDataMember2, pRefDim->GetAutoMeasure() ) )
3794 ++nIncluded; // include more members if values are equal
3795 bContinue = true;
3800 // hide the remaining members
3802 for (nPos = nIncluded; nPos < nCount; nPos++)
3804 ScDPResultMember* pMember = pRefDim->GetMember(aAutoOrder[nPos]);
3805 pMember->SetAutoHidden();
3810 void ScDPDataDimension::ResetResults()
3812 long nCount = maMembers.size();
3813 for (long i=0; i<nCount; i++)
3815 // sort order doesn't matter
3817 long nMemberPos = bIsDataLayout ? 0 : i;
3818 ScDPDataMember* pDataMember = maMembers[nMemberPos];
3819 pDataMember->ResetResults();
3823 long ScDPDataDimension::GetSortedIndex( long nUnsorted ) const
3825 if (!pResultDimension)
3826 return nUnsorted;
3828 const ScMemberSortOrder& rMemberOrder = pResultDimension->GetMemberOrder();
3829 return rMemberOrder.empty() ? nUnsorted : rMemberOrder[nUnsorted];
3832 void ScDPDataDimension::UpdateRunningTotals( const ScDPResultDimension* pRefDim,
3833 long nMeasure, bool bIsSubTotalRow,
3834 const ScDPSubTotalState& rSubState, ScDPRunningTotalState& rRunning,
3835 ScDPRowTotals& rTotals, const ScDPResultMember& rRowParent ) const
3837 OSL_ENSURE( pRefDim && static_cast<size_t>(pRefDim->GetMemberCount()) == maMembers.size(), "dimensions don't match" );
3838 OSL_ENSURE( pRefDim == pResultDimension, "wrong dim" );
3840 long nMemberMeasure = nMeasure;
3841 long nCount = maMembers.size();
3842 for (long i=0; i<nCount; i++)
3844 const ScMemberSortOrder& rMemberOrder = pRefDim->GetMemberOrder();
3845 long nSorted = rMemberOrder.empty() ? i : rMemberOrder[i];
3847 long nMemberPos = nSorted;
3848 if (bIsDataLayout)
3850 OSL_ENSURE(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1,
3851 "DataLayout dimension twice?");
3852 nMemberPos = 0;
3853 nMemberMeasure = nSorted;
3856 const ScDPResultMember* pRefMember = pRefDim->GetMember(nMemberPos);
3857 if ( pRefMember->IsVisible() )
3859 if ( bIsDataLayout )
3860 rRunning.AddColIndex( 0, 0 );
3861 else
3862 rRunning.AddColIndex( i, nSorted );
3864 ScDPDataMember* pDataMember = maMembers[nMemberPos];
3865 pDataMember->UpdateRunningTotals(
3866 pRefMember, nMemberMeasure, bIsSubTotalRow, rSubState, rRunning, rTotals, rRowParent);
3868 rRunning.RemoveColIndex();
3873 #if DEBUG_PIVOT_TABLE
3874 void ScDPDataDimension::DumpState( const ScDPResultDimension* pRefDim, ScDocument* pDoc, ScAddress& rPos ) const
3876 OUString aDimName = bIsDataLayout ? OUString("(data layout)") : OUString("(unknown)");
3877 lcl_DumpRow( OUString("ScDPDataDimension"), aDimName, NULL, pDoc, rPos );
3879 SCROW nStartRow = rPos.Row();
3881 long nCount = bIsDataLayout ? 1 : maMembers.size();
3882 for (long i=0; i<nCount; i++)
3884 const ScDPResultMember* pRefMember = pRefDim->GetMember(i);
3885 const ScDPDataMember* pDataMember = maMembers[i];
3886 pDataMember->DumpState( pRefMember, pDoc, rPos );
3889 lcl_Indent( pDoc, nStartRow, rPos );
3892 void ScDPDataDimension::Dump(int nIndent) const
3894 std::string aIndent(nIndent*2, ' ');
3895 std::cout << aIndent << "-- data dimension '"
3896 << (pResultDimension ? pResultDimension->GetName() : OUString()) << "'" << std::endl;
3897 ScDPDataMembers::const_iterator it = maMembers.begin(), itEnd = maMembers.end();
3898 for (; it != itEnd; ++it)
3899 (*it)->Dump(nIndent+1);
3901 #endif
3903 long ScDPDataDimension::GetMemberCount() const
3905 return maMembers.size();
3908 const ScDPDataMember* ScDPDataDimension::GetMember(long n) const
3910 return maMembers[n];
3913 ScDPDataMember* ScDPDataDimension::GetMember(long n)
3915 return maMembers[n];
3918 // ----------------------------------------------------------------------------
3920 ScDPResultVisibilityData::ScDPResultVisibilityData(
3921 ScDPSource* pSource) :
3922 mpSource(pSource)
3926 ScDPResultVisibilityData::~ScDPResultVisibilityData()
3930 void ScDPResultVisibilityData::addVisibleMember(const OUString& rDimName, const ScDPItemData& rMemberItem)
3932 DimMemberType::iterator itr = maDimensions.find(rDimName);
3933 if (itr == maDimensions.end())
3935 pair<DimMemberType::iterator, bool> r = maDimensions.insert(
3936 DimMemberType::value_type(rDimName, VisibleMemberType()));
3938 if (!r.second)
3939 // insertion failed.
3940 return;
3942 itr = r.first;
3944 VisibleMemberType& rMem = itr->second;
3945 VisibleMemberType::iterator itrMem = rMem.find(rMemberItem);
3946 if (itrMem == rMem.end())
3947 rMem.insert(rMemberItem);
3950 void ScDPResultVisibilityData::fillFieldFilters(vector<ScDPFilteredCache::Criterion>& rFilters) const
3952 typedef boost::unordered_map<String, long, ScStringHashCode> FieldNameMapType;
3953 FieldNameMapType aFieldNames;
3954 ScDPTableData* pData = mpSource->GetData();
3955 long nColumnCount = pData->GetColumnCount();
3956 for (long i = 0; i < nColumnCount; ++i)
3958 aFieldNames.insert(
3959 FieldNameMapType::value_type(pData->getDimensionName(i), i));
3962 const ScDPDimensions* pDims = mpSource->GetDimensionsObject();
3963 for (DimMemberType::const_iterator itr = maDimensions.begin(), itrEnd = maDimensions.end();
3964 itr != itrEnd; ++itr)
3966 const String& rDimName = itr->first;
3967 ScDPFilteredCache::Criterion aCri;
3968 FieldNameMapType::const_iterator itrField = aFieldNames.find(rDimName);
3969 if (itrField == aFieldNames.end())
3970 // This should never happen!
3971 continue;
3973 long nDimIndex = itrField->second;
3974 aCri.mnFieldIndex = static_cast<sal_Int32>(nDimIndex);
3975 aCri.mpFilter.reset(new ScDPFilteredCache::GroupFilter);
3977 ScDPFilteredCache::GroupFilter* pGrpFilter =
3978 static_cast<ScDPFilteredCache::GroupFilter*>(aCri.mpFilter.get());
3980 const VisibleMemberType& rMem = itr->second;
3981 for (VisibleMemberType::const_iterator itrMem = rMem.begin(), itrMemEnd = rMem.end();
3982 itrMem != itrMemEnd; ++itrMem)
3984 const ScDPItemData& rMemItem = *itrMem;
3985 pGrpFilter->addMatchItem(rMemItem);
3988 ScDPDimension* pDim = pDims->getByIndex(nDimIndex);
3989 ScDPMembers* pMembers = pDim->GetHierarchiesObject()->getByIndex(0)->
3990 GetLevelsObject()->getByIndex(0)->GetMembersObject();
3991 if (pGrpFilter->getMatchItemCount() < static_cast<size_t>(pMembers->getCount()))
3992 rFilters.push_back(aCri);
3996 size_t ScDPResultVisibilityData::MemberHash::operator() (const ScDPItemData& r) const
3998 if (r.IsValue())
3999 return static_cast<size_t>(::rtl::math::approxFloor(r.GetValue()));
4000 else
4001 return rtl_ustr_hashCode_WithLength(r.GetString().getStr(), r.GetString().getLength());
4003 SCROW ScDPResultMember::GetDataId( ) const
4005 const ScDPMember* pMemberDesc = GetDPMember();
4006 if (pMemberDesc)
4007 return pMemberDesc->GetItemDataId();
4008 return -1;
4011 ScDPResultMember* ScDPResultDimension::AddMember(const ScDPParentDimData &aData )
4013 ScDPResultMember* pMember = new ScDPResultMember( pResultData, aData, false );
4014 SCROW nDataIndex = pMember->GetDataId();
4015 maMemberArray.push_back( pMember );
4017 if ( maMemberHash.end() == maMemberHash.find( nDataIndex ) )
4018 maMemberHash.insert( std::pair< SCROW, ScDPResultMember *>( nDataIndex, pMember ) );
4019 return pMember;
4022 ScDPResultMember* ScDPResultDimension::InsertMember(ScDPParentDimData *pMemberData)
4024 SCROW nInsert = 0;
4025 if ( !lcl_SearchMember( maMemberArray, pMemberData->mnOrder , nInsert ) )
4027 ScDPResultMember* pNew = new ScDPResultMember( pResultData, *pMemberData, false );
4028 maMemberArray.insert( maMemberArray.begin()+nInsert, pNew );
4030 SCROW nDataIndex = pMemberData->mpMemberDesc->GetItemDataId();
4031 if ( maMemberHash.end() == maMemberHash.find( nDataIndex ) )
4032 maMemberHash.insert( std::pair< SCROW, ScDPResultMember *>( nDataIndex, pNew ) );
4033 return pNew;
4035 return maMemberArray[ nInsert ];
4038 void ScDPResultDimension::InitWithMembers(
4039 LateInitParams& rParams, const std::vector<SCROW>& pItemData, size_t nPos,
4040 ScDPInitState& rInitState)
4042 if ( rParams.IsEnd( nPos ) )
4043 return;
4044 ScDPDimension* pThisDim = rParams.GetDim( nPos );
4045 ScDPLevel* pThisLevel = rParams.GetLevel( nPos );
4046 SCROW nDataID = pItemData[nPos];
4048 if (pThisDim && pThisLevel)
4050 long nDimSource = pThisDim->GetDimension(); //! check GetSourceDim?
4052 // create all members at the first call (preserve order)
4053 ResultMembers* pMembers = pResultData->GetDimResultMembers(nDimSource, pThisDim, pThisLevel);
4054 ScDPGroupCompare aCompare( pResultData, rInitState, nDimSource );
4055 // initialize only specific member (or all if "show empty" flag is set)
4056 ScDPResultMember* pResultMember = NULL;
4057 if ( bInitialized )
4058 pResultMember = FindMember( nDataID );
4059 else
4060 bInitialized = true;
4062 if ( pResultMember == NULL )
4063 { //only insert found item
4064 ScDPParentDimData* pMemberData = pMembers->FindMember( nDataID );
4065 if ( pMemberData && aCompare.IsIncluded( *( pMemberData->mpMemberDesc ) ) )
4066 pResultMember = InsertMember( pMemberData );
4068 if ( pResultMember )
4070 rInitState.AddMember( nDimSource, pResultMember->GetDataId() );
4071 pResultMember->LateInitFrom(rParams, pItemData, nPos+1, rInitState);
4072 rInitState.RemoveMember();
4077 ScDPParentDimData::ScDPParentDimData() :
4078 mnOrder(-1), mpParentDim(NULL), mpParentLevel(NULL), mpMemberDesc(NULL) {}
4080 ScDPParentDimData::ScDPParentDimData(
4081 SCROW nIndex, const ScDPDimension* pDim, const ScDPLevel* pLev, const ScDPMember* pMember) :
4082 mnOrder(nIndex), mpParentDim(pDim), mpParentLevel(pLev), mpMemberDesc(pMember) {}
4084 ScDPParentDimData* ResultMembers::FindMember( SCROW nIndex ) const
4086 DimMemberHash::const_iterator aRes = maMemberHash.find( nIndex );
4087 if( aRes != maMemberHash.end()) {
4088 if ( aRes->second->mpMemberDesc && aRes->second->mpMemberDesc->GetItemDataId()==nIndex )
4089 return aRes->second;
4091 return NULL;
4093 void ResultMembers::InsertMember( ScDPParentDimData* pNew )
4095 if ( !pNew->mpMemberDesc->getShowDetails() )
4096 mbHasHideDetailsMember = true;
4097 maMemberHash.insert( std::pair< const SCROW, ScDPParentDimData *>( pNew->mpMemberDesc->GetItemDataId(), pNew ) );
4100 ResultMembers::ResultMembers():
4101 mbHasHideDetailsMember( false )
4104 ResultMembers::~ResultMembers()
4106 for ( DimMemberHash::const_iterator iter = maMemberHash.begin(); iter != maMemberHash.end(); ++iter )
4107 delete iter->second;
4109 // -----------------------------------------------------------------------
4110 LateInitParams::LateInitParams(
4111 const vector<ScDPDimension*>& ppDim, const vector<ScDPLevel*>& ppLev, bool bRow, bool bInitChild, bool bAllChildren ) :
4112 mppDim( ppDim ),
4113 mppLev( ppLev ),
4114 mbRow( bRow ),
4115 mbInitChild( bInitChild ),
4116 mbAllChildren( bAllChildren )
4120 LateInitParams::~LateInitParams()
4124 bool LateInitParams::IsEnd( size_t nPos ) const
4126 return nPos >= mppDim.size();
4129 void ScDPResultDimension::CheckShowEmpty( bool bShow )
4131 long nCount = maMemberArray.size();
4133 ScDPResultMember* pMember = NULL;
4134 for (long i=0; i<nCount; i++)
4136 pMember = maMemberArray.at(i);
4137 pMember->CheckShowEmpty(bShow);
4142 void ScDPResultMember::CheckShowEmpty( bool bShow )
4144 if (bHasElements)
4146 ScDPResultDimension* pChildDim = GetChildDimension();
4147 if (pChildDim)
4148 pChildDim->CheckShowEmpty();
4150 else if (IsValid() && bInitialized)
4152 bShow = bShow || (GetParentLevel() && GetParentLevel()->getShowEmpty());
4153 if (bShow)
4155 SetHasElements();
4156 ScDPResultDimension* pChildDim = GetChildDimension();
4157 if (pChildDim)
4158 pChildDim->CheckShowEmpty(true);
4163 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */