merge the formfield patch from ooo-build
[ooovba.git] / sc / source / core / data / dptabres.cxx
blob0494bc6ab18a3ac216b335b3eab148e0d4d1feef
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: dptabres.cxx,v $
10 * $Revision: 1.14 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_sc.hxx"
36 // INCLUDE ---------------------------------------------------------------
38 #include <tools/debug.hxx>
39 #include <rtl/math.hxx>
41 #include "dptabdat.hxx"
42 #include "dptabres.hxx"
43 #include "dptabsrc.hxx"
44 #include "global.hxx"
45 #include "subtotal.hxx"
46 #include "globstr.hrc"
47 #include "datauno.hxx" // ScDataUnoConversion
49 #include "document.hxx" // for DumpState only!
51 #include <math.h>
52 #include <float.h> //! Test !!!
53 #include <algorithm>
54 #include <hash_map>
56 #include <com/sun/star/sheet/DataResultFlags.hpp>
57 #include <com/sun/star/sheet/MemberResultFlags.hpp>
58 #include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
59 #include <com/sun/star/sheet/DataPilotFieldReferenceType.hpp>
60 #include <com/sun/star/sheet/DataPilotFieldReferenceItemType.hpp>
61 #include <com/sun/star/sheet/DataPilotFieldShowItemsMode.hpp>
62 #include <com/sun/star/sheet/DataPilotFieldSortMode.hpp>
63 #include <com/sun/star/sheet/DataPilotFieldFilter.hpp>
65 using namespace com::sun::star;
66 using ::std::vector;
67 using ::std::pair;
68 using ::std::hash_map;
69 using ::com::sun::star::uno::Sequence;
70 using ::rtl::OUString;
72 // -----------------------------------------------------------------------
74 SV_IMPL_PTRARR( ScDPDataMembers, ScDPDataMemberPtr );
76 // -----------------------------------------------------------------------
78 static USHORT nFuncStrIds[12] = // passend zum enum ScSubTotalFunc
80 0, // SUBTOTAL_FUNC_NONE
81 STR_FUN_TEXT_AVG, // SUBTOTAL_FUNC_AVE
82 STR_FUN_TEXT_COUNT, // SUBTOTAL_FUNC_CNT
83 STR_FUN_TEXT_COUNT, // SUBTOTAL_FUNC_CNT2
84 STR_FUN_TEXT_MAX, // SUBTOTAL_FUNC_MAX
85 STR_FUN_TEXT_MIN, // SUBTOTAL_FUNC_MIN
86 STR_FUN_TEXT_PRODUCT, // SUBTOTAL_FUNC_PROD
87 STR_FUN_TEXT_STDDEV, // SUBTOTAL_FUNC_STD
88 STR_FUN_TEXT_STDDEV, // SUBTOTAL_FUNC_STDP
89 STR_FUN_TEXT_SUM, // SUBTOTAL_FUNC_SUM
90 STR_FUN_TEXT_VAR, // SUBTOTAL_FUNC_VAR
91 STR_FUN_TEXT_VAR // SUBTOTAL_FUNC_VARP
94 // -----------------------------------------------------------------------
97 // function objects for sorting of the column and row members:
100 class ScDPRowMembersOrder
102 ScDPResultDimension& rDimension;
103 long nMeasure;
104 BOOL bAscending;
106 public:
107 ScDPRowMembersOrder( ScDPResultDimension& rDim, long nM, BOOL bAsc ) :
108 rDimension(rDim),
109 nMeasure(nM),
110 bAscending(bAsc)
112 ~ScDPRowMembersOrder() {}
114 BOOL operator()( sal_Int32 nIndex1, sal_Int32 nIndex2 ) const;
117 class ScDPColMembersOrder
119 ScDPDataDimension& rDimension;
120 long nMeasure;
121 BOOL bAscending;
123 public:
124 ScDPColMembersOrder( ScDPDataDimension& rDim, long nM, BOOL bAsc ) :
125 rDimension(rDim),
126 nMeasure(nM),
127 bAscending(bAsc)
129 ~ScDPColMembersOrder() {}
131 BOOL operator()( sal_Int32 nIndex1, sal_Int32 nIndex2 ) const;
134 static BOOL lcl_IsLess( const ScDPDataMember* pDataMember1, const ScDPDataMember* pDataMember2, long nMeasure, BOOL bAscending )
136 // members can be NULL if used for rows
138 ScDPSubTotalState aEmptyState;
139 const ScDPAggData* pAgg1 = pDataMember1 ? pDataMember1->GetConstAggData( nMeasure, aEmptyState ) : NULL;
140 const ScDPAggData* pAgg2 = pDataMember2 ? pDataMember2->GetConstAggData( nMeasure, aEmptyState ) : NULL;
142 BOOL bError1 = pAgg1 && pAgg1->HasError();
143 BOOL bError2 = pAgg2 && pAgg2->HasError();
144 if ( bError1 )
146 if ( bError2 )
147 return FALSE; // equal
148 else
149 return FALSE; // errors are always sorted at the end
151 else if ( bError2 )
152 return TRUE; // errors are always sorted at the end
153 else
155 double fVal1 = ( pAgg1 && pAgg1->HasData() ) ? pAgg1->GetResult() : 0.0; // no data is sorted as 0
156 double fVal2 = ( pAgg2 && pAgg2->HasData() ) ? pAgg2->GetResult() : 0.0;
158 // compare values
159 // don't have to check approxEqual, as this is the only sort criterion
161 return bAscending ? ( fVal1 < fVal2 ) : ( fVal1 > fVal2 );
165 static BOOL lcl_IsEqual( const ScDPDataMember* pDataMember1, const ScDPDataMember* pDataMember2, long nMeasure )
167 // members can be NULL if used for rows
169 ScDPSubTotalState aEmptyState;
170 const ScDPAggData* pAgg1 = pDataMember1 ? pDataMember1->GetConstAggData( nMeasure, aEmptyState ) : NULL;
171 const ScDPAggData* pAgg2 = pDataMember2 ? pDataMember2->GetConstAggData( nMeasure, aEmptyState ) : NULL;
173 BOOL bError1 = pAgg1 && pAgg1->HasError();
174 BOOL bError2 = pAgg2 && pAgg2->HasError();
175 if ( bError1 )
177 if ( bError2 )
178 return TRUE; // equal
179 else
180 return FALSE;
182 else if ( bError2 )
183 return FALSE;
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 // this is used to find equal data at the end of the AutoShow range, so approxEqual must be used
192 return rtl::math::approxEqual( fVal1, fVal2 );
196 BOOL ScDPRowMembersOrder::operator()( sal_Int32 nIndex1, sal_Int32 nIndex2 ) const
198 const ScDPResultMember* pMember1 = rDimension.GetMember(nIndex1);
199 const ScDPResultMember* pMember2 = rDimension.GetMember(nIndex2);
201 // GetDataRoot can be NULL if there was no data.
202 // IsVisible == FALSE can happen after AutoShow.
203 const ScDPDataMember* pDataMember1 = pMember1->IsVisible() ? pMember1->GetDataRoot() : NULL;
204 const ScDPDataMember* pDataMember2 = pMember2->IsVisible() ? pMember2->GetDataRoot() : NULL;
206 return lcl_IsLess( pDataMember1, pDataMember2, nMeasure, bAscending );
209 BOOL ScDPColMembersOrder::operator()( sal_Int32 nIndex1, sal_Int32 nIndex2 ) const
211 ScDPDataMember* pDataMember1 = rDimension.GetMember(nIndex1);
212 ScDPDataMember* pDataMember2 = rDimension.GetMember(nIndex2);
214 if ( pDataMember1 && !pDataMember1->IsVisible() ) //! IsColVisible?
215 pDataMember1 = NULL;
216 if ( pDataMember2 && !pDataMember2->IsVisible() )
217 pDataMember2 = NULL;
219 return lcl_IsLess( pDataMember1, pDataMember2, nMeasure, bAscending );
222 // -----------------------------------------------------------------------
224 ScDPInitState::ScDPInitState() :
225 nCount( 0 )
227 pIndex = new long[SC_DAPI_MAXFIELDS];
228 pData = new ScDPItemData[SC_DAPI_MAXFIELDS];
231 ScDPInitState::~ScDPInitState()
233 delete[] pIndex;
234 delete[] pData;
237 void ScDPInitState::AddMember( long nSourceIndex, const ScDPItemData& rName )
239 DBG_ASSERT( nCount < SC_DAPI_MAXFIELDS, "too many InitState members" );
240 if ( nCount < SC_DAPI_MAXFIELDS )
242 pIndex[nCount] = nSourceIndex;
243 pData[nCount] = rName;
244 ++nCount;
248 void ScDPInitState::RemoveMember()
250 DBG_ASSERT( nCount > 0, "RemoveColIndex without index" );
251 if ( nCount > 0 )
252 --nCount;
255 const ScDPItemData* ScDPInitState::GetNameForIndex( long nIndexValue ) const
257 for (long i=0; i<nCount; i++)
258 if ( pIndex[i] == nIndexValue )
259 return &pData[i];
261 return NULL; // not found
264 // -----------------------------------------------------------------------
266 void lcl_DumpRow( const String& rType, const String& rName, const ScDPAggData* pAggData,
267 ScDocument* pDoc, ScAddress& rPos )
269 SCCOL nCol = rPos.Col();
270 SCROW nRow = rPos.Row();
271 SCTAB nTab = rPos.Tab();
272 pDoc->SetString( nCol++, nRow, nTab, rType );
273 pDoc->SetString( nCol++, nRow, nTab, rName );
274 while ( pAggData )
276 pDoc->SetValue( nCol++, nRow, nTab, pAggData->GetResult() );
277 pAggData = pAggData->GetExistingChild();
279 rPos.SetRow( nRow + 1 );
282 void lcl_Indent( ScDocument* pDoc, SCROW nStartRow, const ScAddress& rPos )
284 SCCOL nCol = rPos.Col();
285 SCTAB nTab = rPos.Tab();
287 String aString;
288 for (SCROW nRow = nStartRow; nRow < rPos.Row(); nRow++)
290 pDoc->GetString( nCol, nRow, nTab, aString );
291 if ( aString.Len() )
293 aString.InsertAscii( " ", 0 );
294 pDoc->SetString( nCol, nRow, nTab, aString );
299 // -----------------------------------------------------------------------
301 ScDPRunningTotalState::ScDPRunningTotalState( ScDPResultMember* pColRoot, ScDPResultMember* pRowRoot ) :
302 pColResRoot( pColRoot ),
303 pRowResRoot( pRowRoot ),
304 nColIndexPos( 0 ),
305 nRowIndexPos( 0 )
307 pColVisible = new long[SC_DAPI_MAXFIELDS+1];
308 pColIndexes = new long[SC_DAPI_MAXFIELDS+1];
309 pRowVisible = new long[SC_DAPI_MAXFIELDS+1];
310 pRowIndexes = new long[SC_DAPI_MAXFIELDS+1];
311 pColIndexes[0] = -1;
312 pRowIndexes[0] = -1;
315 ScDPRunningTotalState::~ScDPRunningTotalState()
317 delete[] pColVisible;
318 delete[] pColIndexes;
319 delete[] pRowVisible;
320 delete[] pRowIndexes;
323 void ScDPRunningTotalState::AddColIndex( long nVisible, long nSorted )
325 DBG_ASSERT( nColIndexPos < SC_DAPI_MAXFIELDS, "too many column indexes" );
326 if ( nColIndexPos < SC_DAPI_MAXFIELDS )
328 pColVisible[nColIndexPos] = nVisible;
329 pColIndexes[nColIndexPos] = nSorted;
330 pColVisible[nColIndexPos+1] = -1;
331 pColIndexes[nColIndexPos+1] = -1;
332 ++nColIndexPos;
336 void ScDPRunningTotalState::AddRowIndex( long nVisible, long nSorted )
338 DBG_ASSERT( nRowIndexPos < SC_DAPI_MAXFIELDS, "too many row indexes" );
339 if ( nRowIndexPos < SC_DAPI_MAXFIELDS )
341 pRowVisible[nRowIndexPos] = nVisible;
342 pRowIndexes[nRowIndexPos] = nSorted;
343 pRowVisible[nRowIndexPos+1] = -1;
344 pRowIndexes[nRowIndexPos+1] = -1;
345 ++nRowIndexPos;
349 void ScDPRunningTotalState::RemoveColIndex()
351 DBG_ASSERT( nColIndexPos > 0, "RemoveColIndex without index" );
352 if ( nColIndexPos > 0 )
354 --nColIndexPos;
355 pColVisible[nColIndexPos] = -1;
356 pColIndexes[nColIndexPos] = -1;
360 void ScDPRunningTotalState::RemoveRowIndex()
362 DBG_ASSERT( nRowIndexPos > 0, "RemoveRowIndex without index" );
363 if ( nRowIndexPos > 0 )
365 --nRowIndexPos;
366 pRowVisible[nRowIndexPos] = -1;
367 pRowIndexes[nRowIndexPos] = -1;
371 // -----------------------------------------------------------------------
373 ScDPRelativePos::ScDPRelativePos( long nBase, long nDir ) :
374 nBasePos( nBase ),
375 nDirection( nDir )
379 // -----------------------------------------------------------------------
381 void ScDPAggData::Update( const ScDPValueData& rNext, ScSubTotalFunc eFunc, const ScDPSubTotalState& rSubState )
383 if (nCount<0) // error?
384 return; // nothing more...
386 if ( rNext.nType == SC_VALTYPE_EMPTY )
387 return;
389 if ( rSubState.eColForce != SUBTOTAL_FUNC_NONE && rSubState.eRowForce != SUBTOTAL_FUNC_NONE &&
390 rSubState.eColForce != rSubState.eRowForce )
391 return;
392 if ( rSubState.eColForce != SUBTOTAL_FUNC_NONE ) eFunc = rSubState.eColForce;
393 if ( rSubState.eRowForce != SUBTOTAL_FUNC_NONE ) eFunc = rSubState.eRowForce;
395 if ( eFunc == SUBTOTAL_FUNC_NONE )
396 return;
398 if ( eFunc != SUBTOTAL_FUNC_CNT2 ) // CNT2 counts everything, incl. strings and errors
400 if ( rNext.nType == SC_VALTYPE_ERROR )
402 nCount = -1; // -1 for error (not for CNT2)
403 return;
405 if ( rNext.nType == SC_VALTYPE_STRING )
406 return; // ignore
409 ++nCount; // for all functions
411 switch (eFunc)
413 case SUBTOTAL_FUNC_SUM:
414 case SUBTOTAL_FUNC_AVE:
415 if ( !SubTotal::SafePlus( fVal, rNext.fValue ) )
416 nCount = -1; // -1 for error
417 break;
418 case SUBTOTAL_FUNC_PROD:
419 if ( nCount == 1 ) // copy first value (fVal is initialized to 0)
420 fVal = rNext.fValue;
421 else if ( !SubTotal::SafeMult( fVal, rNext.fValue ) )
422 nCount = -1; // -1 for error
423 break;
424 case SUBTOTAL_FUNC_CNT:
425 case SUBTOTAL_FUNC_CNT2:
426 // nothing more than incrementing nCount
427 break;
428 case SUBTOTAL_FUNC_MAX:
429 if ( nCount == 1 || rNext.fValue > fVal )
430 fVal = rNext.fValue;
431 break;
432 case SUBTOTAL_FUNC_MIN:
433 if ( nCount == 1 || rNext.fValue < fVal )
434 fVal = rNext.fValue;
435 break;
436 case SUBTOTAL_FUNC_STD:
437 case SUBTOTAL_FUNC_STDP:
438 case SUBTOTAL_FUNC_VAR:
439 case SUBTOTAL_FUNC_VARP:
441 // fAux is used to sum up squares
442 if ( !SubTotal::SafePlus( fVal, rNext.fValue ) )
443 nCount = -1; // -1 for error
444 double fAdd = rNext.fValue;
445 if ( !SubTotal::SafeMult( fAdd, rNext.fValue ) ||
446 !SubTotal::SafePlus( fAux, fAdd ) )
447 nCount = -1; // -1 for error
449 break;
450 default:
451 DBG_ERROR("invalid function");
455 void ScDPAggData::Calculate( ScSubTotalFunc eFunc, const ScDPSubTotalState& rSubState )
457 // calculate the original result
458 // (without reference value, used as the basis for reference value calculation)
460 // called several times at the cross-section of several subtotals - don't calculate twice then
461 if ( IsCalculated() )
462 return;
464 if ( rSubState.eColForce != SUBTOTAL_FUNC_NONE ) eFunc = rSubState.eColForce;
465 if ( rSubState.eRowForce != SUBTOTAL_FUNC_NONE ) eFunc = rSubState.eRowForce;
467 if ( eFunc == SUBTOTAL_FUNC_NONE ) // this happens when there is no data dimension
469 nCount = SC_DPAGG_RESULT_EMPTY; // make sure there's a valid state for HasData etc.
470 return;
473 // check the error conditions for the selected function
475 BOOL bError = FALSE;
476 switch (eFunc)
478 case SUBTOTAL_FUNC_SUM:
479 case SUBTOTAL_FUNC_PROD:
480 case SUBTOTAL_FUNC_CNT:
481 case SUBTOTAL_FUNC_CNT2:
482 bError = ( nCount < 0 ); // only real errors
483 break;
485 case SUBTOTAL_FUNC_AVE:
486 case SUBTOTAL_FUNC_MAX:
487 case SUBTOTAL_FUNC_MIN:
488 case SUBTOTAL_FUNC_STDP:
489 case SUBTOTAL_FUNC_VARP:
490 bError = ( nCount <= 0 ); // no data is an error
491 break;
493 case SUBTOTAL_FUNC_STD:
494 case SUBTOTAL_FUNC_VAR:
495 bError = ( nCount < 2 ); // need at least 2 values
496 break;
498 default:
499 DBG_ERROR("invalid function");
502 // calculate the selected function
504 double fResult = 0.0;
505 if ( !bError )
507 switch (eFunc)
509 case SUBTOTAL_FUNC_MAX:
510 case SUBTOTAL_FUNC_MIN:
511 case SUBTOTAL_FUNC_SUM:
512 case SUBTOTAL_FUNC_PROD:
513 // different error conditions are handled above
514 fResult = fVal;
515 break;
517 case SUBTOTAL_FUNC_CNT:
518 case SUBTOTAL_FUNC_CNT2:
519 fResult = nCount;
520 break;
522 case SUBTOTAL_FUNC_AVE:
523 if ( nCount > 0 )
524 fResult = fVal / (double) nCount;
525 break;
527 //! use safe mul for fVal * fVal
529 case SUBTOTAL_FUNC_STD:
530 if ( nCount >= 2 )
531 fResult = sqrt((fAux - fVal*fVal/(double)(nCount)) / (double)(nCount-1));
532 break;
533 case SUBTOTAL_FUNC_VAR:
534 if ( nCount >= 2 )
535 fResult = (fAux - fVal*fVal/(double)(nCount)) / (double)(nCount-1);
536 break;
537 case SUBTOTAL_FUNC_STDP:
538 if ( nCount > 0 )
539 fResult = sqrt((fAux - fVal*fVal/(double)(nCount)) / (double)nCount);
540 break;
541 case SUBTOTAL_FUNC_VARP:
542 if ( nCount > 0 )
543 fResult = (fAux - fVal*fVal/(double)(nCount)) / (double)nCount;
544 break;
545 default:
546 DBG_ERROR("invalid function");
550 BOOL bEmpty = ( nCount == 0 ); // no data
552 // store the result
553 // Empty is checked first, so empty results are shown empty even for "average" etc.
554 // If these results should be treated as errors in reference value calculations,
555 // a separate state value (EMPTY_ERROR) is needed.
556 // Now, for compatibility, empty "average" results are counted as 0.
558 if ( bEmpty )
559 nCount = SC_DPAGG_RESULT_EMPTY;
560 else if ( bError )
561 nCount = SC_DPAGG_RESULT_ERROR;
562 else
563 nCount = SC_DPAGG_RESULT_VALID;
565 if ( bEmpty || bError )
566 fResult = 0.0; // default, in case the state is later modified
568 // fprintf(stdout, "ScDPAggData::Calculate: result = %g\n", fResult);fflush(stdout);
569 fVal = fResult; // used directly from now on
570 fAux = 0.0; // used for running total or original result of reference value
573 BOOL ScDPAggData::IsCalculated() const
575 return ( nCount <= SC_DPAGG_RESULT_EMPTY );
578 double ScDPAggData::GetResult() const
580 DBG_ASSERT( IsCalculated(), "ScDPAggData not calculated" );
582 return fVal; // use calculated value
585 BOOL ScDPAggData::HasError() const
587 DBG_ASSERT( IsCalculated(), "ScDPAggData not calculated" );
589 return ( nCount == SC_DPAGG_RESULT_ERROR );
592 BOOL ScDPAggData::HasData() const
594 DBG_ASSERT( IsCalculated(), "ScDPAggData not calculated" );
596 return ( nCount != SC_DPAGG_RESULT_EMPTY ); // values or error
599 void ScDPAggData::SetResult( double fNew )
601 DBG_ASSERT( IsCalculated(), "ScDPAggData not calculated" );
603 fVal = fNew; // don't reset error flag
606 void ScDPAggData::SetError()
608 DBG_ASSERT( IsCalculated(), "ScDPAggData not calculated" );
610 nCount = SC_DPAGG_RESULT_ERROR;
613 void ScDPAggData::SetEmpty( BOOL bSet )
615 DBG_ASSERT( IsCalculated(), "ScDPAggData not calculated" );
617 if ( bSet )
618 nCount = SC_DPAGG_RESULT_EMPTY;
619 else
620 nCount = SC_DPAGG_RESULT_VALID;
623 double ScDPAggData::GetAuxiliary() const
625 // after Calculate, fAux is used as auxiliary value for running totals and reference values
626 DBG_ASSERT( IsCalculated(), "ScDPAggData not calculated" );
628 return fAux;
631 void ScDPAggData::SetAuxiliary( double fNew )
633 // after Calculate, fAux is used as auxiliary value for running totals and reference values
634 DBG_ASSERT( IsCalculated(), "ScDPAggData not calculated" );
636 fAux = fNew;
639 ScDPAggData* ScDPAggData::GetChild()
641 if (!pChild)
642 pChild = new ScDPAggData;
643 return pChild;
646 void ScDPAggData::Reset()
648 fVal = 0.0;
649 fAux = 0.0;
650 nCount = SC_DPAGG_EMPTY;
651 delete pChild;
652 pChild = NULL;
655 // -----------------------------------------------------------------------
657 ScDPRowTotals::ScDPRowTotals() :
658 bIsInColRoot( FALSE )
662 ScDPRowTotals::~ScDPRowTotals()
666 ScDPAggData* lcl_GetChildTotal( ScDPAggData* pFirst, long nMeasure )
668 DBG_ASSERT( nMeasure >= 0, "GetColTotal: no measure" );
670 ScDPAggData* pAgg = pFirst;
671 long nSkip = nMeasure;
673 // subtotal settings are ignored - colum/row totals exist once per measure
675 for ( long nPos=0; nPos<nSkip; nPos++ )
676 pAgg = pAgg->GetChild(); // column total is constructed empty - children need to be created
678 if ( !pAgg->IsCalculated() )
680 // for first use, simulate an empty calculation
681 ScDPSubTotalState aEmptyState;
682 pAgg->Calculate( SUBTOTAL_FUNC_SUM, aEmptyState );
685 return pAgg;
688 ScDPAggData* ScDPRowTotals::GetRowTotal( long nMeasure )
690 return lcl_GetChildTotal( &aRowTotal, nMeasure );
693 ScDPAggData* ScDPRowTotals::GetGrandTotal( long nMeasure )
695 return lcl_GetChildTotal( &aGrandTotal, nMeasure );
698 // -----------------------------------------------------------------------
700 static ScSubTotalFunc lcl_GetForceFunc( const ScDPLevel* pLevel, long nFuncNo )
702 ScSubTotalFunc eRet = SUBTOTAL_FUNC_NONE;
703 if ( pLevel )
705 //! direct access via ScDPLevel
707 uno::Sequence<sheet::GeneralFunction> aSeq = pLevel->getSubTotals();
708 long nSequence = aSeq.getLength();
709 if ( nSequence && aSeq[0] != sheet::GeneralFunction_AUTO )
711 // For manual subtotals, "automatic" is added as first function.
712 // ScDPResultMember::GetSubTotalCount adds to the count, here NONE has to be
713 // returned as the first function then.
715 --nFuncNo; // keep NONE for first (check below), move the other entries
718 if ( nFuncNo >= 0 && nFuncNo < nSequence )
720 sheet::GeneralFunction eUser = aSeq.getConstArray()[nFuncNo];
721 if (eUser != sheet::GeneralFunction_AUTO)
722 eRet = ScDataUnoConversion::GeneralToSubTotal( eUser );
725 return eRet;
728 // -----------------------------------------------------------------------
730 ScDPResultData::ScDPResultData( ScDPSource* pSrc ) : //! Ref
731 pSource( pSrc ),
732 nMeasCount( 0 ),
733 pMeasFuncs( NULL ),
734 pMeasRefs( NULL ),
735 pMeasRefOrient( NULL ),
736 pMeasNames( NULL ),
737 bLateInit( FALSE ),
738 bDataAtCol( FALSE ),
739 bDataAtRow( FALSE )
743 ScDPResultData::~ScDPResultData()
745 delete[] pMeasFuncs;
746 delete[] pMeasRefs;
747 delete[] pMeasRefOrient;
748 delete[] pMeasNames;
751 void ScDPResultData::SetMeasureData( long nCount, const ScSubTotalFunc* pFunctions,
752 const sheet::DataPilotFieldReference* pRefs, const USHORT* pRefOrient,
753 const String* pNames )
755 delete[] pMeasFuncs;
756 delete[] pMeasRefs;
757 delete[] pMeasRefOrient;
758 delete[] pMeasNames;
759 if ( nCount )
761 nMeasCount = nCount;
762 pMeasFuncs = new ScSubTotalFunc[nCount];
763 pMeasRefs = new sheet::DataPilotFieldReference[nCount];
764 pMeasRefOrient = new USHORT[nCount];
765 pMeasNames = new String[nCount];
766 for (long i=0; i<nCount; i++)
768 pMeasFuncs[i] = pFunctions[i];
769 pMeasRefs[i] = pRefs[i];
770 pMeasRefOrient[i] = pRefOrient[i];
771 pMeasNames[i] = pNames[i];
774 else
776 // use one dummy measure
777 nMeasCount = 1;
778 pMeasFuncs = new ScSubTotalFunc[1];
779 pMeasFuncs[0] = SUBTOTAL_FUNC_NONE;
780 pMeasRefs = new sheet::DataPilotFieldReference[1]; // default ctor is ok
781 pMeasRefOrient = new USHORT[1];
782 pMeasRefOrient[0] = sheet::DataPilotFieldOrientation_HIDDEN;
783 pMeasNames = new String[1];
784 pMeasNames[0] = ScGlobal::GetRscString( STR_EMPTYDATA );
788 void ScDPResultData::SetDataLayoutOrientation( USHORT nOrient )
790 bDataAtCol = ( nOrient == sheet::DataPilotFieldOrientation_COLUMN );
791 bDataAtRow = ( nOrient == sheet::DataPilotFieldOrientation_ROW );
794 void ScDPResultData::SetLateInit( BOOL bSet )
796 bLateInit = bSet;
799 long ScDPResultData::GetColStartMeasure() const
801 if ( nMeasCount == 1 ) return 0;
802 return bDataAtCol ? SC_DPMEASURE_ALL : SC_DPMEASURE_ANY;
805 long ScDPResultData::GetRowStartMeasure() const
807 if ( nMeasCount == 1 ) return 0;
808 return bDataAtRow ? SC_DPMEASURE_ALL : SC_DPMEASURE_ANY;
811 ScSubTotalFunc ScDPResultData::GetMeasureFunction(long nMeasure) const
813 DBG_ASSERT( pMeasFuncs && nMeasure < nMeasCount, "bumm" );
814 return pMeasFuncs[nMeasure];
817 const sheet::DataPilotFieldReference& ScDPResultData::GetMeasureRefVal(long nMeasure) const
819 DBG_ASSERT( pMeasRefs && nMeasure < nMeasCount, "bumm" );
820 return pMeasRefs[nMeasure];
823 USHORT ScDPResultData::GetMeasureRefOrient(long nMeasure) const
825 DBG_ASSERT( pMeasRefOrient && nMeasure < nMeasCount, "bumm" );
826 return pMeasRefOrient[nMeasure];
829 String ScDPResultData::GetMeasureString(long nMeasure, BOOL bForce, ScSubTotalFunc eForceFunc, bool& rbTotalResult) const
831 // with bForce==TRUE, return function instead of "result" for single measure
832 // with eForceFunc != SUBTOTAL_FUNC_NONE, always use eForceFunc
833 rbTotalResult = false;
834 if ( nMeasure < 0 || ( nMeasCount == 1 && !bForce && eForceFunc == SUBTOTAL_FUNC_NONE ) )
836 // for user-specified subtotal function with all measures,
837 // display only function name
838 if ( eForceFunc != SUBTOTAL_FUNC_NONE )
839 return ScGlobal::GetRscString(nFuncStrIds[eForceFunc]);
841 rbTotalResult = true;
842 return ScGlobal::GetRscString(STR_TABLE_ERGEBNIS);
844 else
846 DBG_ASSERT( pMeasNames && nMeasure < nMeasCount, "bumm" );
847 ScDPDimension* pDataDim = pSource->GetDataDimension(nMeasure);
848 if (pDataDim)
850 const OUString* pLayoutName = pDataDim->GetLayoutName();
851 if (pLayoutName)
852 return *pLayoutName;
854 String aRet;
855 ScSubTotalFunc eFunc = ( eForceFunc == SUBTOTAL_FUNC_NONE ) ?
856 GetMeasureFunction(nMeasure) : eForceFunc;
857 USHORT nId = nFuncStrIds[eFunc];
858 if (nId)
860 aRet += ScGlobal::GetRscString(nId); // function name
861 aRet.AppendAscii(RTL_CONSTASCII_STRINGPARAM( " - " ));
863 aRet += pMeasNames[nMeasure]; // field name
865 return aRet;
869 String ScDPResultData::GetMeasureDimensionName(long nMeasure) const
871 if ( nMeasure < 0 )
873 DBG_ERROR("GetMeasureDimensionName: negative");
874 return String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM("***"));
877 return pSource->GetDataDimName( nMeasure );
880 BOOL ScDPResultData::IsBaseForGroup( long nDim ) const
882 return pSource->GetData()->IsBaseForGroup( nDim );
885 long ScDPResultData::GetGroupBase( long nGroupDim ) const
887 return pSource->GetData()->GetGroupBase( nGroupDim );
890 BOOL ScDPResultData::IsNumOrDateGroup( long nDim ) const
892 return pSource->GetData()->IsNumOrDateGroup( nDim );
895 BOOL ScDPResultData::IsInGroup( const ScDPItemData& rGroupData, long nGroupIndex,
896 const ScDPItemData& rBaseData, long nBaseIndex ) const
898 return pSource->GetData()->IsInGroup( rGroupData, nGroupIndex, rBaseData, nBaseIndex );
901 BOOL ScDPResultData::HasCommonElement( const ScDPItemData& rFirstData, long nFirstIndex,
902 const ScDPItemData& rSecondData, long nSecondIndex ) const
904 return pSource->GetData()->HasCommonElement( rFirstData, nFirstIndex, rSecondData, nSecondIndex );
907 const ScDPSource* ScDPResultData::GetSource() const
909 return pSource;
912 // -----------------------------------------------------------------------
915 ScDPResultMember::ScDPResultMember( const ScDPResultData* pData, const ScDPDimension* pDim,
916 const ScDPLevel* pLev, const ScDPMember* pDesc,
917 BOOL bForceSub ) :
918 pResultData( pData ),
919 pParentDim( pDim ),
920 pParentLevel( pLev ),
921 pMemberDesc( pDesc ),
922 pChildDimension( NULL ),
923 pDataRoot( NULL ),
924 bHasElements( FALSE ),
925 bForceSubTotal( bForceSub ),
926 bHasHiddenDetails( FALSE ),
927 bInitialized( FALSE ),
928 bAutoHidden( FALSE )
930 // pParentLevel/pMemberDesc is 0 for root members
933 ScDPResultMember::~ScDPResultMember()
935 delete pChildDimension;
936 delete pDataRoot;
939 String ScDPResultMember::GetName() const
941 if (pMemberDesc)
942 return pMemberDesc->GetNameStr();
943 else
944 return ScGlobal::GetRscString(STR_PIVOT_TOTAL); // root member
947 void ScDPResultMember::FillItemData( ScDPItemData& rData ) const
949 if (pMemberDesc)
950 pMemberDesc->FillItemData( rData );
951 else
952 rData.SetString( ScGlobal::GetRscString(STR_PIVOT_TOTAL) ); // root member
955 BOOL ScDPResultMember::IsNamedItem( const ScDPItemData& r ) const
957 //! store ScDPMember pointer instead of ScDPMember ???
959 if (pMemberDesc)
960 return ((ScDPMember*)pMemberDesc)->IsNamedItem( r );
961 return FALSE;
964 bool ScDPResultMember::IsValidEntry( const vector<ScDPItemData>& aMembers ) const
966 if ( !IsValid() )
967 return false;
969 const ScDPResultDimension* pChildDim = GetChildDimension();
970 if (pChildDim)
972 if (aMembers.size() < 2)
973 return false;
975 vector<ScDPItemData>::const_iterator itr = aMembers.begin();
976 vector<ScDPItemData> aChildMembers(++itr, aMembers.end());
977 return pChildDim->IsValidEntry(aChildMembers);
979 else
980 return true;
983 void ScDPResultMember::InitFrom( const vector<ScDPDimension*>& ppDim, const vector<ScDPLevel*>& ppLev,
984 size_t nPos, ScDPInitState& rInitState )
986 // with LateInit, initialize only those members that have data
987 if ( pResultData->IsLateInit() )
988 return;
990 bInitialized = TRUE;
992 if (nPos >= ppDim.size())
993 return;
995 // skip child dimension if details are not shown
996 if ( pMemberDesc && !pMemberDesc->getShowDetails() )
998 bHasHiddenDetails = TRUE; // only if there is a next dimension
999 return;
1002 pChildDimension = new ScDPResultDimension( pResultData );
1003 pChildDimension->InitFrom( ppDim, ppLev, nPos, rInitState );
1006 void ScDPResultMember::LateInitFrom( const vector<ScDPDimension*>& ppDim, const vector<ScDPLevel*>& ppLev,
1007 const vector<ScDPItemData>& pItemData, size_t nPos,
1008 ScDPInitState& rInitState )
1010 // without LateInit, everything has already been initialized
1011 if ( !pResultData->IsLateInit() )
1012 return;
1014 bInitialized = TRUE;
1016 if (nPos >= ppDim.size())
1017 // No next dimension. Bail out.
1018 return;
1020 // skip child dimension if details are not shown
1021 if ( pMemberDesc && !pMemberDesc->getShowDetails() )
1023 bHasHiddenDetails = TRUE; // only if there is a next dimension
1024 return;
1027 // LateInitFrom is called several times...
1028 if ( !pChildDimension )
1029 pChildDimension = new ScDPResultDimension( pResultData );
1031 pChildDimension->LateInitFrom( ppDim, ppLev, pItemData, nPos, rInitState );
1034 BOOL ScDPResultMember::IsSubTotalInTitle(long nMeasure) const
1036 BOOL bRet = FALSE;
1037 if ( pChildDimension && pParentLevel &&
1038 pParentLevel->IsOutlineLayout() && pParentLevel->IsSubtotalsAtTop() )
1040 long nUserSubStart;
1041 long nSubTotals = GetSubTotalCount( &nUserSubStart );
1042 nSubTotals -= nUserSubStart; // visible count
1043 if ( nSubTotals )
1045 if ( nMeasure == SC_DPMEASURE_ALL )
1046 nSubTotals *= pResultData->GetMeasureCount(); // number of subtotals that will be inserted
1048 // only a single subtotal row will be shown in the outline title row
1049 if ( nSubTotals == 1 )
1050 bRet = TRUE;
1053 return bRet;
1056 long ScDPResultMember::GetSize(long nMeasure) const
1058 if ( !IsVisible() )
1059 return 0;
1061 long nExtraSpace = 0;
1062 if ( pParentLevel && pParentLevel->IsAddEmpty() )
1063 ++nExtraSpace;
1065 if ( pChildDimension )
1067 // outline layout takes up an extra row for the title only if subtotals aren't shown in that row
1068 if ( pParentLevel && pParentLevel->IsOutlineLayout() && !IsSubTotalInTitle( nMeasure ) )
1069 ++nExtraSpace;
1071 long nSize = pChildDimension->GetSize(nMeasure);
1072 long nUserSubStart;
1073 long nUserSubCount = GetSubTotalCount( &nUserSubStart );
1074 nUserSubCount -= nUserSubStart; // for output size, use visible count
1075 if ( nUserSubCount )
1077 if ( nMeasure == SC_DPMEASURE_ALL )
1078 nSize += pResultData->GetMeasureCount() * nUserSubCount;
1079 else
1080 nSize += nUserSubCount;
1082 return nSize + nExtraSpace;
1084 else
1086 if ( nMeasure == SC_DPMEASURE_ALL )
1087 return pResultData->GetMeasureCount() + nExtraSpace;
1088 else
1089 return 1 + nExtraSpace;
1093 BOOL ScDPResultMember::IsVisible() const
1095 // not initialized -> shouldn't be there at all
1096 // (allocated only to preserve ordering)
1098 return ( bHasElements || ( pParentLevel && pParentLevel->getShowEmpty() ) ) && IsValid() && bInitialized;
1101 BOOL ScDPResultMember::IsValid() const
1103 // non-Valid members are left out of calculation
1105 // was member set no invisible at the DataPilotSource?
1106 if ( pMemberDesc && !pMemberDesc->getIsVisible() )
1107 return FALSE;
1109 if ( bAutoHidden )
1110 return FALSE;
1112 return TRUE;
1115 BOOL ScDPResultMember::HasHiddenDetails() const
1117 // bHasHiddenDetails is set only if the "show details" flag is off,
1118 // and there was a child dimension to skip
1120 return bHasHiddenDetails;
1123 long ScDPResultMember::GetSubTotalCount( long* pUserSubStart ) const
1125 if ( pUserSubStart )
1126 *pUserSubStart = 0; // default
1128 if ( bForceSubTotal ) // set if needed for root members
1129 return 1; // grand total is always "automatic"
1130 else if ( pParentLevel )
1132 //! direct access via ScDPLevel
1134 uno::Sequence<sheet::GeneralFunction> aSeq = pParentLevel->getSubTotals();
1135 long nSequence = aSeq.getLength();
1136 if ( nSequence && aSeq[0] != sheet::GeneralFunction_AUTO )
1138 // For manual subtotals, always add "automatic" as first function
1139 // (used for calculation, but not for display, needed for sorting, see lcl_GetForceFunc)
1141 ++nSequence;
1142 if ( pUserSubStart )
1143 *pUserSubStart = 1; // visible subtotals start at 1
1145 return nSequence;
1147 else
1148 return 0;
1151 void ScDPResultMember::ProcessData( const vector<ScDPItemData>& aChildMembers, const ScDPResultDimension* pDataDim,
1152 const vector<ScDPItemData>& aDataMembers, const vector<ScDPValueData>& aValues )
1154 SetHasElements();
1156 if (pChildDimension)
1157 pChildDimension->ProcessData( aChildMembers, pDataDim, aDataMembers, aValues );
1159 if ( !pDataRoot )
1161 pDataRoot = new ScDPDataMember( pResultData, NULL );
1162 if ( pDataDim )
1163 pDataRoot->InitFrom( pDataDim ); // recursive
1166 ScDPSubTotalState aSubState; // initial state
1168 long nUserSubCount = GetSubTotalCount();
1170 // Calculate at least automatic if no subtotals are selected,
1171 // show only own values if there's no child dimension (innermost).
1172 if ( !nUserSubCount || !pChildDimension )
1173 nUserSubCount = 1;
1175 for (long nUserPos=0; nUserPos<nUserSubCount; nUserPos++) // including hidden "automatic"
1177 // #i68338# if nUserSubCount is 1 (automatic only), don't set nRowSubTotalFunc
1178 if ( pChildDimension && nUserSubCount > 1 )
1180 aSubState.nRowSubTotalFunc = nUserPos;
1181 aSubState.eRowForce = lcl_GetForceFunc( pParentLevel, nUserPos );
1184 pDataRoot->ProcessData( aDataMembers, aValues, aSubState );
1188 /**
1189 * Parse subtotal string and replace all occurrences of '?' with the caption
1190 * string. Do ensure that escaped characters are not translated.
1192 static String lcl_parseSubtotalName(const String& rSubStr, const String& rCaption)
1194 String aNewStr;
1195 xub_StrLen n = rSubStr.Len();
1196 bool bEscaped = false;
1197 for (xub_StrLen i = 0; i < n; ++i)
1199 sal_Unicode c = rSubStr.GetChar(i);
1200 if (!bEscaped && c == sal_Unicode('\\'))
1202 bEscaped = true;
1203 continue;
1206 if (!bEscaped && c == sal_Unicode('?'))
1207 aNewStr.Append(rCaption);
1208 else
1209 aNewStr.Append(c);
1210 bEscaped = false;
1212 return aNewStr;
1215 void ScDPResultMember::FillMemberResults( uno::Sequence<sheet::MemberResult>* pSequences,
1216 long& rPos, long nMeasure, BOOL bRoot,
1217 const String* pMemberName,
1218 const String* pMemberCaption )
1220 // IsVisible() test is in ScDPResultDimension::FillMemberResults
1221 // (not on data layout dimension)
1223 long nSize = GetSize(nMeasure);
1224 sheet::MemberResult* pArray = pSequences->getArray();
1225 DBG_ASSERT( rPos+nSize <= pSequences->getLength(), "bumm" );
1227 BOOL bIsNumeric = FALSE;
1228 String aName;
1229 if ( pMemberName ) // if pMemberName != NULL, use instead of real member name
1230 aName = *pMemberName;
1231 else
1233 ScDPItemData aItemData;
1234 FillItemData( aItemData );
1235 aName = aItemData.aString;
1236 bIsNumeric = aItemData.bHasValue;
1239 if ( bIsNumeric && pParentDim && pResultData->IsNumOrDateGroup( pParentDim->GetDimension() ) )
1241 // Numeric group dimensions use numeric entries for proper sorting,
1242 // but the group titles must be output as text.
1243 bIsNumeric = FALSE;
1246 String aCaption = aName;
1247 if (pMemberDesc)
1249 const OUString* pLayoutName = pMemberDesc->GetLayoutName();
1250 if (pLayoutName)
1252 aCaption = *pLayoutName;
1253 bIsNumeric = false; // layout name is always non-numeric.
1257 if ( pMemberCaption ) // use pMemberCaption if != NULL
1258 aCaption = *pMemberCaption;
1259 if (!aCaption.Len())
1260 aCaption = ScGlobal::GetRscString(STR_EMPTYDATA);
1262 if (bIsNumeric)
1263 pArray[rPos].Flags |= sheet::MemberResultFlags::NUMERIC;
1264 else
1265 pArray[rPos].Flags &= ~sheet::MemberResultFlags::NUMERIC;
1267 if ( nSize && !bRoot ) // root is overwritten by first dimension
1269 pArray[rPos].Name = rtl::OUString(aName);
1270 pArray[rPos].Caption = rtl::OUString(aCaption);
1271 pArray[rPos].Flags |= sheet::MemberResultFlags::HASMEMBER;
1273 // set "continue" flag (removed for subtotals later)
1274 for (long i=1; i<nSize; i++)
1275 pArray[rPos+i].Flags |= sheet::MemberResultFlags::CONTINUE;
1278 long nExtraSpace = 0;
1279 if ( pParentLevel && pParentLevel->IsAddEmpty() )
1280 ++nExtraSpace;
1282 BOOL bTitleLine = FALSE;
1283 if ( pParentLevel && pParentLevel->IsOutlineLayout() )
1284 bTitleLine = TRUE;
1286 // if the subtotals are shown at the top (title row) in outline layout,
1287 // no extra row for the subtotals is needed
1288 BOOL bSubTotalInTitle = IsSubTotalInTitle( nMeasure );
1290 BOOL bHasChild = ( pChildDimension != NULL );
1291 if (bHasChild)
1293 if ( bTitleLine ) // in tabular layout the title is on a separate row
1294 ++rPos; // -> fill child dimension one row below
1296 if (bRoot) // same sequence for root member
1297 pChildDimension->FillMemberResults( pSequences, rPos, nMeasure );
1298 else
1299 pChildDimension->FillMemberResults( pSequences + 1, rPos, nMeasure );
1301 if ( bTitleLine ) // title row is included in GetSize, so the following
1302 --rPos; // positions are calculated with the normal values
1305 rPos += nSize;
1307 long nUserSubStart;
1308 long nUserSubCount = GetSubTotalCount(&nUserSubStart);
1309 if ( nUserSubCount && pChildDimension && !bSubTotalInTitle )
1311 long nMemberMeasure = nMeasure;
1312 long nSubSize = pResultData->GetCountForMeasure(nMeasure);
1314 rPos -= nSubSize * (nUserSubCount - nUserSubStart); // GetSize includes space for SubTotal
1315 rPos -= nExtraSpace; // GetSize includes the empty line
1317 for (long nUserPos=nUserSubStart; nUserPos<nUserSubCount; nUserPos++)
1319 for ( long nSubCount=0; nSubCount<nSubSize; nSubCount++ )
1321 if ( nMeasure == SC_DPMEASURE_ALL )
1322 nMemberMeasure = nSubCount;
1324 ScSubTotalFunc eForce = SUBTOTAL_FUNC_NONE;
1325 if (bHasChild)
1326 eForce = lcl_GetForceFunc( pParentLevel, nUserPos );
1328 bool bTotalResult = false;
1329 String aSubStr = aCaption;
1330 aSubStr += ' ';
1331 aSubStr += pResultData->GetMeasureString(nMemberMeasure, FALSE, eForce, bTotalResult);
1333 if (bTotalResult)
1335 if (pMemberDesc)
1337 // single data field layout.
1338 const OUString* pSubtotalName = pParentDim->GetSubtotalName();
1339 if (pSubtotalName)
1340 aSubStr = lcl_parseSubtotalName(*pSubtotalName, aCaption);
1341 pArray[rPos].Flags &= ~sheet::MemberResultFlags::GRANDTOTAL;
1343 else
1345 // root member - subtotal (grand total?) for multi-data field layout.
1346 const rtl::OUString* pGrandTotalName = pResultData->GetSource()->GetGrandTotalName();
1347 if (pGrandTotalName)
1348 aSubStr = *pGrandTotalName;
1349 pArray[rPos].Flags |= sheet::MemberResultFlags::GRANDTOTAL;
1353 pArray[rPos].Name = rtl::OUString(aName);
1354 pArray[rPos].Caption = rtl::OUString(aSubStr);
1355 pArray[rPos].Flags = ( pArray[rPos].Flags |
1356 ( sheet::MemberResultFlags::HASMEMBER | sheet::MemberResultFlags::SUBTOTAL) ) &
1357 ~sheet::MemberResultFlags::CONTINUE;
1359 if ( nMeasure == SC_DPMEASURE_ALL )
1361 // data layout dimension is (direct/indirect) child of this.
1362 // data layout dimension must have name for all entries.
1364 uno::Sequence<sheet::MemberResult>* pLayoutSeq = pSequences;
1365 if (!bRoot)
1366 ++pLayoutSeq;
1367 ScDPResultDimension* pLayoutDim = pChildDimension;
1368 while ( pLayoutDim && !pLayoutDim->IsDataLayout() )
1370 pLayoutDim = pLayoutDim->GetFirstChildDimension();
1371 ++pLayoutSeq;
1373 if ( pLayoutDim )
1375 sheet::MemberResult* pLayoutArray = pLayoutSeq->getArray();
1376 String aDataName = pResultData->GetMeasureDimensionName(nMemberMeasure);
1377 pLayoutArray[rPos].Name = rtl::OUString(aDataName);
1381 rPos += 1;
1385 rPos += nExtraSpace; // add again (subtracted above)
1389 void ScDPResultMember::FillDataResults( const ScDPResultMember* pRefMember,
1390 uno::Sequence< uno::Sequence<sheet::DataResult> >& rSequence,
1391 long& rRow, long nMeasure ) const
1393 // IsVisible() test is in ScDPResultDimension::FillDataResults
1394 // (not on data layout dimension)
1396 long nStartRow = rRow;
1398 long nExtraSpace = 0;
1399 if ( pParentLevel && pParentLevel->IsAddEmpty() )
1400 ++nExtraSpace;
1402 BOOL bTitleLine = FALSE;
1403 if ( pParentLevel && pParentLevel->IsOutlineLayout() )
1404 bTitleLine = TRUE;
1406 BOOL bSubTotalInTitle = IsSubTotalInTitle( nMeasure );
1408 BOOL bHasChild = ( pChildDimension != NULL );
1409 if (bHasChild)
1411 if ( bTitleLine ) // in tabular layout the title is on a separate row
1412 ++rRow; // -> fill child dimension one row below
1414 pChildDimension->FillDataResults( pRefMember, rSequence, rRow, nMeasure ); // doesn't modify rRow
1415 rRow += (USHORT) GetSize( nMeasure );
1417 if ( bTitleLine ) // title row is included in GetSize, so the following
1418 --rRow; // positions are calculated with the normal values
1421 long nUserSubStart;
1422 long nUserSubCount = GetSubTotalCount(&nUserSubStart);
1423 if ( nUserSubCount || !bHasChild )
1425 // Calculate at least automatic if no subtotals are selected,
1426 // show only own values if there's no child dimension (innermost).
1427 if ( !nUserSubCount || !bHasChild )
1429 nUserSubCount = 1;
1430 nUserSubStart = 0;
1433 long nMemberMeasure = nMeasure;
1434 long nSubSize = pResultData->GetCountForMeasure(nMeasure);
1435 if (bHasChild)
1437 rRow -= nSubSize * ( nUserSubCount - nUserSubStart ); // GetSize includes space for SubTotal
1438 rRow -= nExtraSpace; // GetSize includes the empty line
1441 long nMoveSubTotal = 0;
1442 if ( bSubTotalInTitle )
1444 nMoveSubTotal = rRow - nStartRow; // force to first (title) row
1445 rRow = nStartRow;
1448 if ( pDataRoot )
1450 ScDPSubTotalState aSubState; // initial state
1452 for (long nUserPos=nUserSubStart; nUserPos<nUserSubCount; nUserPos++)
1454 if ( bHasChild && nUserSubCount > 1 )
1456 aSubState.nRowSubTotalFunc = nUserPos;
1457 aSubState.eRowForce = lcl_GetForceFunc( pParentLevel, nUserPos );
1460 for ( long nSubCount=0; nSubCount<nSubSize; nSubCount++ )
1462 if ( nMeasure == SC_DPMEASURE_ALL )
1463 nMemberMeasure = nSubCount;
1464 else if ( pResultData->GetColStartMeasure() == SC_DPMEASURE_ALL )
1465 nMemberMeasure = SC_DPMEASURE_ALL;
1467 DBG_ASSERT( rRow < rSequence.getLength(), "bumm" );
1468 uno::Sequence<sheet::DataResult>& rSubSeq = rSequence.getArray()[rRow];
1469 long nSeqCol = 0;
1470 pDataRoot->FillDataRow( pRefMember, rSubSeq, nSeqCol, nMemberMeasure, bHasChild, aSubState );
1472 rRow += 1;
1476 else
1477 rRow += nSubSize * ( nUserSubCount - nUserSubStart ); // empty rows occur when ShowEmpty is true
1479 // add extra space again if subtracted from GetSize above,
1480 // add to own size if no children
1481 rRow += nExtraSpace;
1483 rRow += nMoveSubTotal;
1487 void ScDPResultMember::UpdateDataResults( const ScDPResultMember* pRefMember, long nMeasure ) const
1489 // IsVisible() test is in ScDPResultDimension::FillDataResults
1490 // (not on data layout dimension)
1492 BOOL bHasChild = ( pChildDimension != NULL );
1494 long nUserSubCount = GetSubTotalCount();
1495 // process subtotals even if not shown
1496 // if ( nUserSubCount || !bHasChild )
1498 // Calculate at least automatic if no subtotals are selected,
1499 // show only own values if there's no child dimension (innermost).
1500 if ( !nUserSubCount || !bHasChild )
1501 nUserSubCount = 1;
1503 long nMemberMeasure = nMeasure;
1504 long nSubSize = pResultData->GetCountForMeasure(nMeasure);
1506 if ( pDataRoot )
1508 ScDPSubTotalState aSubState; // initial state
1510 for (long nUserPos=0; nUserPos<nUserSubCount; nUserPos++) // including hidden "automatic"
1512 if ( bHasChild && nUserSubCount > 1 )
1514 aSubState.nRowSubTotalFunc = nUserPos;
1515 aSubState.eRowForce = lcl_GetForceFunc( pParentLevel, nUserPos );
1518 for ( long nSubCount=0; nSubCount<nSubSize; nSubCount++ )
1520 if ( nMeasure == SC_DPMEASURE_ALL )
1521 nMemberMeasure = nSubCount;
1522 else if ( pResultData->GetColStartMeasure() == SC_DPMEASURE_ALL )
1523 nMemberMeasure = SC_DPMEASURE_ALL;
1525 pDataRoot->UpdateDataRow( pRefMember, nMemberMeasure, bHasChild, aSubState );
1531 if (bHasChild) // child dimension must be processed last, so the column total is known
1533 pChildDimension->UpdateDataResults( pRefMember, nMeasure );
1537 void ScDPResultMember::SortMembers( ScDPResultMember* pRefMember )
1539 BOOL bHasChild = ( pChildDimension != NULL );
1540 if (bHasChild)
1541 pChildDimension->SortMembers( pRefMember ); // sorting is done at the dimension
1543 BOOL bIsRoot = ( pParentLevel == NULL );
1544 if ( bIsRoot && pDataRoot )
1546 // use the row root member to sort columns
1547 // sub total count is always 1
1549 pDataRoot->SortMembers( pRefMember );
1553 void ScDPResultMember::DoAutoShow( ScDPResultMember* pRefMember )
1555 BOOL bHasChild = ( pChildDimension != NULL );
1556 if (bHasChild)
1557 pChildDimension->DoAutoShow( pRefMember ); // sorting is done at the dimension
1559 BOOL bIsRoot = ( pParentLevel == NULL );
1560 if ( bIsRoot && pDataRoot )
1562 // use the row root member to sort columns
1563 // sub total count is always 1
1565 pDataRoot->DoAutoShow( pRefMember );
1569 void ScDPResultMember::ResetResults( BOOL bRoot )
1571 if (pDataRoot)
1572 pDataRoot->ResetResults();
1574 if (pChildDimension)
1575 pChildDimension->ResetResults();
1577 if (!bRoot)
1578 bHasElements = FALSE;
1581 void ScDPResultMember::UpdateRunningTotals( const ScDPResultMember* pRefMember, long nMeasure,
1582 ScDPRunningTotalState& rRunning, ScDPRowTotals& rTotals ) const
1584 // IsVisible() test is in ScDPResultDimension::FillDataResults
1585 // (not on data layout dimension)
1587 BOOL bIsRoot = ( pParentLevel == NULL );
1588 rTotals.SetInColRoot( bIsRoot );
1590 BOOL bHasChild = ( pChildDimension != NULL );
1592 long nUserSubCount = GetSubTotalCount();
1593 if ( nUserSubCount || !bHasChild )
1595 // Calculate at least automatic if no subtotals are selected,
1596 // show only own values if there's no child dimension (innermost).
1597 if ( !nUserSubCount || !bHasChild )
1598 nUserSubCount = 1;
1600 long nMemberMeasure = nMeasure;
1601 long nSubSize = pResultData->GetCountForMeasure(nMeasure);
1603 if ( pDataRoot )
1605 ScDPSubTotalState aSubState; // initial state
1607 for (long nUserPos=0; nUserPos<nUserSubCount; nUserPos++) // including hidden "automatic"
1609 if ( bHasChild && nUserSubCount > 1 )
1611 aSubState.nRowSubTotalFunc = nUserPos;
1612 aSubState.eRowForce = lcl_GetForceFunc( pParentLevel, nUserPos );
1615 for ( long nSubCount=0; nSubCount<nSubSize; nSubCount++ )
1617 if ( nMeasure == SC_DPMEASURE_ALL )
1618 nMemberMeasure = nSubCount;
1619 else if ( pResultData->GetColStartMeasure() == SC_DPMEASURE_ALL )
1620 nMemberMeasure = SC_DPMEASURE_ALL;
1622 pDataRoot->UpdateRunningTotals( pRefMember, nMemberMeasure,
1623 bHasChild, aSubState, rRunning, rTotals, *this );
1629 if (bHasChild) // child dimension must be processed last, so the column total is known
1631 pChildDimension->UpdateRunningTotals( pRefMember, nMeasure, rRunning, rTotals );
1635 void ScDPResultMember::DumpState( const ScDPResultMember* pRefMember, ScDocument* pDoc, ScAddress& rPos ) const
1637 lcl_DumpRow( String::CreateFromAscii("ScDPResultMember"), GetName(), NULL, pDoc, rPos );
1638 SCROW nStartRow = rPos.Row();
1640 if (pDataRoot)
1641 pDataRoot->DumpState( pRefMember, pDoc, rPos );
1643 if (pChildDimension)
1644 pChildDimension->DumpState( pRefMember, pDoc, rPos );
1646 lcl_Indent( pDoc, nStartRow, rPos );
1649 ScDPAggData* ScDPResultMember::GetColTotal( long nMeasure ) const
1651 return lcl_GetChildTotal( const_cast<ScDPAggData*>(&aColTotal), nMeasure );
1654 void ScDPResultMember::FillVisibilityData(ScDPResultVisibilityData& rData) const
1656 if (pChildDimension)
1657 pChildDimension->FillVisibilityData(rData);
1660 // -----------------------------------------------------------------------
1662 ScDPDataMember::ScDPDataMember( const ScDPResultData* pData, const ScDPResultMember* pRes ) :
1663 pResultData( pData ),
1664 pResultMember( pRes ),
1665 pChildDimension( NULL )
1667 // pResultMember is 0 for root members
1670 ScDPDataMember::~ScDPDataMember()
1672 delete pChildDimension;
1675 String ScDPDataMember::GetName() const
1677 if (pResultMember)
1678 return pResultMember->GetName();
1679 else
1680 return EMPTY_STRING;
1683 BOOL ScDPDataMember::IsVisible() const
1685 if (pResultMember)
1686 return pResultMember->IsVisible();
1687 else
1688 return FALSE;
1691 BOOL ScDPDataMember::IsNamedItem( const ScDPItemData& r ) const
1693 if (pResultMember)
1694 return pResultMember->IsNamedItem(r);
1695 else
1696 return FALSE;
1699 BOOL ScDPDataMember::HasHiddenDetails() const
1701 if (pResultMember)
1702 return pResultMember->HasHiddenDetails();
1703 else
1704 return FALSE;
1707 void ScDPDataMember::InitFrom( const ScDPResultDimension* pDim )
1709 if ( !pChildDimension )
1710 pChildDimension = new ScDPDataDimension(pResultData);
1711 pChildDimension->InitFrom(pDim);
1714 const long SC_SUBTOTALPOS_AUTO = -1; // default
1715 const long SC_SUBTOTALPOS_SKIP = -2; // don't use
1717 long lcl_GetSubTotalPos( const ScDPSubTotalState& rSubState )
1719 if ( rSubState.nColSubTotalFunc >= 0 && rSubState.nRowSubTotalFunc >= 0 &&
1720 rSubState.nColSubTotalFunc != rSubState.nRowSubTotalFunc )
1722 // #i68338# don't return the same index for different combinations (leading to repeated updates),
1723 // return a "don't use" value instead
1725 return SC_SUBTOTALPOS_SKIP;
1728 long nRet = SC_SUBTOTALPOS_AUTO;
1729 if ( rSubState.nColSubTotalFunc >= 0 ) nRet = rSubState.nColSubTotalFunc;
1730 if ( rSubState.nRowSubTotalFunc >= 0 ) nRet = rSubState.nRowSubTotalFunc;
1731 return nRet;
1734 void ScDPDataMember::UpdateValues( const vector<ScDPValueData>& aValues, const ScDPSubTotalState& rSubState )
1736 //! find out how many and which subtotals are used
1738 ScDPAggData* pAgg = &aAggregate;
1740 long nSubPos = lcl_GetSubTotalPos(rSubState);
1741 if (nSubPos == SC_SUBTOTALPOS_SKIP)
1742 return;
1743 if (nSubPos > 0)
1745 long nSkip = nSubPos * pResultData->GetMeasureCount();
1746 for (long i=0; i<nSkip; i++)
1747 pAgg = pAgg->GetChild(); // created if not there
1750 size_t nCount = aValues.size();
1751 for (size_t nPos = 0; nPos < nCount; ++nPos)
1753 pAgg->Update(aValues[nPos], pResultData->GetMeasureFunction(nPos), rSubState);
1754 pAgg = pAgg->GetChild();
1758 void ScDPDataMember::ProcessData( const vector<ScDPItemData>& aChildMembers, const vector<ScDPValueData>& aValues,
1759 const ScDPSubTotalState& rSubState )
1761 if ( pResultData->IsLateInit() && !pChildDimension && pResultMember && pResultMember->GetChildDimension() )
1763 // if this DataMember doesn't have a child dimension because the ResultMember's
1764 // child dimension wasn't there yet during this DataMembers's creation,
1765 // create the child dimension now
1766 InitFrom( pResultMember->GetChildDimension() );
1769 ScDPSubTotalState aLocalSubState(rSubState); // keep row state, modify column
1771 long nUserSubCount = pResultMember ? pResultMember->GetSubTotalCount() : 0;
1773 // Calculate at least automatic if no subtotals are selected,
1774 // show only own values if there's no child dimension (innermost).
1775 if ( !nUserSubCount || !pChildDimension )
1776 nUserSubCount = 1;
1778 for (long nUserPos=0; nUserPos<nUserSubCount; nUserPos++) // including hidden "automatic"
1780 if ( pChildDimension && nUserSubCount > 1 )
1782 const ScDPLevel* pForceLevel = pResultMember ? pResultMember->GetParentLevel() : NULL;
1783 aLocalSubState.nColSubTotalFunc = nUserPos;
1784 aLocalSubState.eColForce = lcl_GetForceFunc( pForceLevel, nUserPos );
1787 UpdateValues( aValues, aLocalSubState );
1790 if (pChildDimension)
1791 pChildDimension->ProcessData( aChildMembers, aValues, rSubState ); // with unmodified subtotal state
1794 BOOL ScDPDataMember::HasData( long nMeasure, const ScDPSubTotalState& rSubState ) const
1796 if ( rSubState.eColForce != SUBTOTAL_FUNC_NONE && rSubState.eRowForce != SUBTOTAL_FUNC_NONE &&
1797 rSubState.eColForce != rSubState.eRowForce )
1798 return FALSE;
1800 // #74542# HasData can be different between measures!
1802 const ScDPAggData* pAgg = GetConstAggData( nMeasure, rSubState );
1803 if (!pAgg)
1804 return FALSE; //! error?
1806 return pAgg->HasData();
1809 BOOL ScDPDataMember::HasError( long nMeasure, const ScDPSubTotalState& rSubState ) const
1811 const ScDPAggData* pAgg = GetConstAggData( nMeasure, rSubState );
1812 if (!pAgg)
1813 return TRUE;
1815 return pAgg->HasError();
1818 double ScDPDataMember::GetAggregate( long nMeasure, const ScDPSubTotalState& rSubState ) const
1820 const ScDPAggData* pAgg = GetConstAggData( nMeasure, rSubState );
1821 if (!pAgg)
1822 return DBL_MAX; //! error?
1824 return pAgg->GetResult();
1827 ScDPAggData* ScDPDataMember::GetAggData( long nMeasure, const ScDPSubTotalState& rSubState )
1829 DBG_ASSERT( nMeasure >= 0, "GetAggData: no measure" );
1831 ScDPAggData* pAgg = &aAggregate;
1832 long nSkip = nMeasure;
1833 long nSubPos = lcl_GetSubTotalPos(rSubState);
1834 if (nSubPos == SC_SUBTOTALPOS_SKIP)
1835 return NULL;
1836 if (nSubPos > 0)
1837 nSkip += nSubPos * pResultData->GetMeasureCount();
1839 for ( long nPos=0; nPos<nSkip; nPos++ )
1840 pAgg = pAgg->GetChild(); //! need to create children here?
1842 return pAgg;
1845 const ScDPAggData* ScDPDataMember::GetConstAggData( long nMeasure, const ScDPSubTotalState& rSubState ) const
1847 DBG_ASSERT( nMeasure >= 0, "GetConstAggData: no measure" );
1849 const ScDPAggData* pAgg = &aAggregate;
1850 long nSkip = nMeasure;
1851 long nSubPos = lcl_GetSubTotalPos(rSubState);
1852 if (nSubPos == SC_SUBTOTALPOS_SKIP)
1853 return NULL;
1854 if (nSubPos > 0)
1855 nSkip += nSubPos * pResultData->GetMeasureCount();
1857 for ( long nPos=0; nPos<nSkip; nPos++ )
1859 pAgg = pAgg->GetExistingChild();
1860 if (!pAgg)
1861 return NULL;
1864 return pAgg;
1867 void ScDPDataMember::FillDataRow( const ScDPResultMember* pRefMember,
1868 uno::Sequence<sheet::DataResult>& rSequence,
1869 long& rCol, long nMeasure, BOOL bIsSubTotalRow,
1870 const ScDPSubTotalState& rSubState ) const
1872 DBG_ASSERT( pRefMember == pResultMember || !pResultMember, "bla" );
1874 if ( pRefMember->IsVisible() ) //! here or in ScDPDataDimension::FillDataRow ???
1876 long nStartCol = rCol;
1878 const ScDPDataDimension* pDataChild = GetChildDimension();
1879 const ScDPResultDimension* pRefChild = pRefMember->GetChildDimension();
1881 const ScDPLevel* pRefParentLevel = const_cast<ScDPResultMember*>(pRefMember)->GetParentLevel();
1883 long nExtraSpace = 0;
1884 if ( pRefParentLevel && pRefParentLevel->IsAddEmpty() )
1885 ++nExtraSpace;
1887 BOOL bTitleLine = FALSE;
1888 if ( pRefParentLevel && pRefParentLevel->IsOutlineLayout() )
1889 bTitleLine = TRUE;
1891 BOOL bSubTotalInTitle = pRefMember->IsSubTotalInTitle( nMeasure );
1893 // leave space for children even if the DataMember hasn't been initialized
1894 // (pDataChild is null then, this happens when no values for it are in this row)
1895 BOOL bHasChild = ( pRefChild != NULL );
1897 if ( bHasChild )
1899 if ( bTitleLine ) // in tabular layout the title is on a separate column
1900 ++rCol; // -> fill child dimension one column below
1902 if ( pDataChild )
1903 pDataChild->FillDataRow( pRefChild, rSequence, rCol, nMeasure, bIsSubTotalRow, rSubState );
1904 rCol += (USHORT)pRefMember->GetSize( nMeasure );
1906 if ( bTitleLine ) // title column is included in GetSize, so the following
1907 --rCol; // positions are calculated with the normal values
1910 long nUserSubStart;
1911 long nUserSubCount = pRefMember->GetSubTotalCount(&nUserSubStart);
1912 if ( nUserSubCount || !bHasChild )
1914 // Calculate at least automatic if no subtotals are selected,
1915 // show only own values if there's no child dimension (innermost).
1916 if ( !nUserSubCount || !bHasChild )
1918 nUserSubCount = 1;
1919 nUserSubStart = 0;
1922 ScDPSubTotalState aLocalSubState(rSubState); // keep row state, modify column
1924 long nMemberMeasure = nMeasure;
1925 long nSubSize = pResultData->GetCountForMeasure(nMeasure);
1926 if (bHasChild)
1928 rCol -= nSubSize * ( nUserSubCount - nUserSubStart ); // GetSize includes space for SubTotal
1929 rCol -= nExtraSpace; // GetSize includes the empty line
1932 long nMoveSubTotal = 0;
1933 if ( bSubTotalInTitle )
1935 nMoveSubTotal = rCol - nStartCol; // force to first (title) column
1936 rCol = nStartCol;
1939 for (long nUserPos=nUserSubStart; nUserPos<nUserSubCount; nUserPos++)
1941 if ( pChildDimension && nUserSubCount > 1 )
1943 const ScDPLevel* pForceLevel = pResultMember ? pResultMember->GetParentLevel() : NULL;
1944 aLocalSubState.nColSubTotalFunc = nUserPos;
1945 aLocalSubState.eColForce = lcl_GetForceFunc( pForceLevel, nUserPos );
1948 for ( long nSubCount=0; nSubCount<nSubSize; nSubCount++ )
1950 if ( nMeasure == SC_DPMEASURE_ALL )
1951 nMemberMeasure = nSubCount;
1953 DBG_ASSERT( rCol < rSequence.getLength(), "bumm" );
1954 sheet::DataResult& rRes = rSequence.getArray()[rCol];
1956 if ( HasData( nMemberMeasure, aLocalSubState ) )
1958 if ( HasError( nMemberMeasure, aLocalSubState ) )
1960 rRes.Value = 0;
1961 rRes.Flags |= sheet::DataResultFlags::ERROR;
1963 else
1965 rRes.Value = GetAggregate( nMemberMeasure, aLocalSubState );
1966 rRes.Flags |= sheet::DataResultFlags::HASDATA;
1970 if ( bHasChild || bIsSubTotalRow )
1971 rRes.Flags |= sheet::DataResultFlags::SUBTOTAL;
1973 rCol += 1;
1977 // add extra space again if subtracted from GetSize above,
1978 // add to own size if no children
1979 rCol += nExtraSpace;
1981 rCol += nMoveSubTotal;
1986 void ScDPDataMember::UpdateDataRow( const ScDPResultMember* pRefMember,
1987 long nMeasure, BOOL bIsSubTotalRow,
1988 const ScDPSubTotalState& rSubState )
1990 DBG_ASSERT( pRefMember == pResultMember || !pResultMember, "bla" );
1992 // Calculate must be called even if not visible (for use as reference value)
1993 const ScDPDataDimension* pDataChild = GetChildDimension();
1994 const ScDPResultDimension* pRefChild = pRefMember->GetChildDimension();
1996 // leave space for children even if the DataMember hasn't been initialized
1997 // (pDataChild is null then, this happens when no values for it are in this row)
1998 BOOL bHasChild = ( pRefChild != NULL );
2000 // process subtotals even if not shown
2001 long nUserSubCount = pRefMember->GetSubTotalCount();
2003 // Calculate at least automatic if no subtotals are selected,
2004 // show only own values if there's no child dimension (innermost).
2005 if ( !nUserSubCount || !bHasChild )
2006 nUserSubCount = 1;
2008 ScDPSubTotalState aLocalSubState(rSubState); // keep row state, modify column
2010 long nMemberMeasure = nMeasure;
2011 long nSubSize = pResultData->GetCountForMeasure(nMeasure);
2013 for (long nUserPos=0; nUserPos<nUserSubCount; nUserPos++) // including hidden "automatic"
2015 if ( pChildDimension && nUserSubCount > 1 )
2017 const ScDPLevel* pForceLevel = pResultMember ? pResultMember->GetParentLevel() : NULL;
2018 aLocalSubState.nColSubTotalFunc = nUserPos;
2019 aLocalSubState.eColForce = lcl_GetForceFunc( pForceLevel, nUserPos );
2022 for ( long nSubCount=0; nSubCount<nSubSize; nSubCount++ )
2024 if ( nMeasure == SC_DPMEASURE_ALL )
2025 nMemberMeasure = nSubCount;
2027 // update data...
2028 ScDPAggData* pAggData = GetAggData( nMemberMeasure, aLocalSubState );
2029 if (pAggData)
2031 //! aLocalSubState?
2032 ScSubTotalFunc eFunc = pResultData->GetMeasureFunction( nMemberMeasure );
2033 sheet::DataPilotFieldReference aReferenceValue = pResultData->GetMeasureRefVal( nMemberMeasure );
2034 sal_Int32 eRefType = aReferenceValue.ReferenceType;
2036 // calculate the result first - for all members, regardless of reference value
2037 pAggData->Calculate( eFunc, aLocalSubState );
2039 if ( eRefType == sheet::DataPilotFieldReferenceType::ITEM_DIFFERENCE ||
2040 eRefType == sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE ||
2041 eRefType == sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE )
2043 // copy the result into auxiliary value, so differences can be
2044 // calculated in any order
2045 pAggData->SetAuxiliary( pAggData->GetResult() );
2047 // column/row percentage/index is now in UpdateRunningTotals, so it doesn't disturb sorting
2052 if ( bHasChild ) // child dimension must be processed last, so the row total is known
2054 if ( pDataChild )
2055 pDataChild->UpdateDataRow( pRefChild, nMeasure, bIsSubTotalRow, rSubState );
2059 void ScDPDataMember::SortMembers( ScDPResultMember* pRefMember )
2061 DBG_ASSERT( pRefMember == pResultMember || !pResultMember, "bla" );
2063 if ( pRefMember->IsVisible() ) //! here or in ScDPDataDimension ???
2065 ScDPDataDimension* pDataChild = GetChildDimension();
2066 ScDPResultDimension* pRefChild = pRefMember->GetChildDimension();
2067 if ( pRefChild && pDataChild )
2068 pDataChild->SortMembers( pRefChild ); // sorting is done at the dimension
2072 void ScDPDataMember::DoAutoShow( ScDPResultMember* pRefMember )
2074 DBG_ASSERT( pRefMember == pResultMember || !pResultMember, "bla" );
2076 if ( pRefMember->IsVisible() ) //! here or in ScDPDataDimension ???
2078 ScDPDataDimension* pDataChild = GetChildDimension();
2079 ScDPResultDimension* pRefChild = pRefMember->GetChildDimension();
2080 if ( pRefChild && pDataChild )
2081 pDataChild->DoAutoShow( pRefChild ); // sorting is done at the dimension
2085 void ScDPDataMember::ResetResults()
2087 aAggregate.Reset();
2089 ScDPDataDimension* pDataChild = GetChildDimension();
2090 if ( pDataChild )
2091 pDataChild->ResetResults();
2094 void ScDPDataMember::UpdateRunningTotals( const ScDPResultMember* pRefMember,
2095 long nMeasure, BOOL bIsSubTotalRow,
2096 const ScDPSubTotalState& rSubState, ScDPRunningTotalState& rRunning,
2097 ScDPRowTotals& rTotals, const ScDPResultMember& rRowParent )
2099 DBG_ASSERT( pRefMember == pResultMember || !pResultMember, "bla" );
2101 if ( pRefMember->IsVisible() ) //! here or in ScDPDataDimension::UpdateRunningTotals ???
2103 const ScDPDataDimension* pDataChild = GetChildDimension();
2104 const ScDPResultDimension* pRefChild = pRefMember->GetChildDimension();
2106 BOOL bIsRoot = ( pResultMember == NULL || pResultMember->GetParentLevel() == NULL );
2108 // leave space for children even if the DataMember hasn't been initialized
2109 // (pDataChild is null then, this happens when no values for it are in this row)
2110 BOOL bHasChild = ( pRefChild != NULL );
2112 long nUserSubCount = pRefMember->GetSubTotalCount();
2113 if ( nUserSubCount || !bHasChild )
2115 // Calculate at least automatic if no subtotals are selected,
2116 // show only own values if there's no child dimension (innermost).
2117 if ( !nUserSubCount || !bHasChild )
2118 nUserSubCount = 1;
2120 ScDPSubTotalState aLocalSubState(rSubState); // keep row state, modify column
2122 long nMemberMeasure = nMeasure;
2123 long nSubSize = pResultData->GetCountForMeasure(nMeasure);
2125 for (long nUserPos=0; nUserPos<nUserSubCount; nUserPos++) // including hidden "automatic"
2127 if ( pChildDimension && nUserSubCount > 1 )
2129 const ScDPLevel* pForceLevel = pResultMember ? pResultMember->GetParentLevel() : NULL;
2130 aLocalSubState.nColSubTotalFunc = nUserPos;
2131 aLocalSubState.eColForce = lcl_GetForceFunc( pForceLevel, nUserPos );
2134 for ( long nSubCount=0; nSubCount<nSubSize; nSubCount++ )
2136 if ( nMeasure == SC_DPMEASURE_ALL )
2137 nMemberMeasure = nSubCount;
2139 // update data...
2140 ScDPAggData* pAggData = GetAggData( nMemberMeasure, aLocalSubState );
2141 if (pAggData)
2143 //! aLocalSubState?
2144 sheet::DataPilotFieldReference aReferenceValue = pResultData->GetMeasureRefVal( nMemberMeasure );
2145 sal_Int32 eRefType = aReferenceValue.ReferenceType;
2147 if ( eRefType == sheet::DataPilotFieldReferenceType::RUNNING_TOTAL ||
2148 eRefType == sheet::DataPilotFieldReferenceType::ITEM_DIFFERENCE ||
2149 eRefType == sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE ||
2150 eRefType == sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE )
2152 BOOL bRunningTotal = ( eRefType == sheet::DataPilotFieldReferenceType::RUNNING_TOTAL );
2153 BOOL bRelative =
2154 ( aReferenceValue.ReferenceItemType != sheet::DataPilotFieldReferenceItemType::NAMED && !bRunningTotal );
2155 long nRelativeDir = bRelative ?
2156 ( ( aReferenceValue.ReferenceItemType == sheet::DataPilotFieldReferenceItemType::PREVIOUS ) ? -1 : 1 ) : 0;
2158 const long* pColVisible = rRunning.GetColVisible();
2159 const long* pColIndexes = rRunning.GetColIndexes();
2160 const long* pRowVisible = rRunning.GetRowVisible();
2161 const long* pRowIndexes = rRunning.GetRowIndexes();
2163 String aRefFieldName = aReferenceValue.ReferenceField;
2165 //! aLocalSubState?
2166 USHORT nRefOrient = pResultData->GetMeasureRefOrient( nMemberMeasure );
2167 BOOL bRefDimInCol = ( nRefOrient == sheet::DataPilotFieldOrientation_COLUMN );
2168 BOOL bRefDimInRow = ( nRefOrient == sheet::DataPilotFieldOrientation_ROW );
2170 const ScDPResultDimension* pSelectDim = NULL;
2171 long nRowPos = 0;
2172 long nColPos = 0;
2175 // find the reference field in column or row dimensions
2178 if ( bRefDimInRow ) // look in row dimensions
2180 pSelectDim = rRunning.GetRowResRoot()->GetChildDimension();
2181 while ( pSelectDim && pSelectDim->GetName() != aRefFieldName )
2183 long nIndex = pRowIndexes[nRowPos];
2184 if ( nIndex >= 0 && nIndex < pSelectDim->GetMemberCount() )
2185 pSelectDim = pSelectDim->GetMember(nIndex)->GetChildDimension();
2186 else
2187 pSelectDim = NULL;
2188 ++nRowPos;
2190 // child dimension of innermost member?
2191 if ( pSelectDim && pRowIndexes[nRowPos] < 0 )
2192 pSelectDim = NULL;
2195 if ( bRefDimInCol ) // look in column dimensions
2197 pSelectDim = rRunning.GetColResRoot()->GetChildDimension();
2198 while ( pSelectDim && pSelectDim->GetName() != aRefFieldName )
2200 long nIndex = pColIndexes[nColPos];
2201 if ( nIndex >= 0 && nIndex < pSelectDim->GetMemberCount() )
2202 pSelectDim = pSelectDim->GetMember(nIndex)->GetChildDimension();
2203 else
2204 pSelectDim = NULL;
2205 ++nColPos;
2207 // child dimension of innermost member?
2208 if ( pSelectDim && pColIndexes[nColPos] < 0 )
2209 pSelectDim = NULL;
2212 BOOL bNoDetailsInRef = FALSE;
2213 if ( pSelectDim && bRunningTotal )
2215 // Running totals:
2216 // If details are hidden for this member in the reference dimension,
2217 // don't show or sum up the value. Otherwise, for following members,
2218 // the running totals of details and subtotals wouldn't match.
2220 long nMyIndex = bRefDimInCol ? pColIndexes[nColPos] : pRowIndexes[nRowPos];
2221 if ( nMyIndex >= 0 && nMyIndex < pSelectDim->GetMemberCount() )
2223 const ScDPResultMember* pMyRefMember = pSelectDim->GetMember(nMyIndex);
2224 if ( pMyRefMember && pMyRefMember->HasHiddenDetails() )
2226 pSelectDim = NULL; // don't calculate
2227 bNoDetailsInRef = TRUE; // show error, not empty
2232 if ( bRelative )
2234 // Difference/Percentage from previous/next:
2235 // If details are hidden for this member in the innermost column/row
2236 // dimension (the orientation of the reference dimension), show an
2237 // error value.
2238 // - If the no-details dimension is the reference dimension, its
2239 // members will be skipped when finding the previous/next member,
2240 // so there must be no results for its members.
2241 // - If the no-details dimension is outside of the reference dimension,
2242 // no calculation in the reference dimension is possible.
2243 // - Otherwise, the error isn't strictly necessary, but shown for
2244 // consistency.
2246 BOOL bInnerNoDetails = bRefDimInCol ? HasHiddenDetails() :
2247 ( bRefDimInRow ? rRowParent.HasHiddenDetails() : TRUE );
2248 if ( bInnerNoDetails )
2250 pSelectDim = NULL;
2251 bNoDetailsInRef = TRUE; // show error, not empty
2255 if ( !bRefDimInCol && !bRefDimInRow ) // invalid dimension specified
2256 bNoDetailsInRef = TRUE; // pSelectDim is then already NULL
2259 // get the member for the reference item and do the calculation
2262 if ( bRunningTotal )
2264 // running total in (dimension) -> find first existing member
2266 if ( pSelectDim )
2268 ScDPDataMember* pSelectMember;
2269 if ( bRefDimInCol )
2270 pSelectMember = ScDPResultDimension::GetColReferenceMember( NULL, NULL,
2271 nColPos, rRunning );
2272 else
2274 long nSkip = nRowPos + 1; // including the reference dimension
2275 pSelectMember = pSelectDim->GetRowReferenceMember( NULL, NULL,
2276 pRowIndexes+nSkip, pColIndexes );
2279 if ( pSelectMember )
2281 // The running total is kept as the auxiliary value in
2282 // the first available member for the reference dimension.
2283 // Members are visited in final order, so each one's result
2284 // can be used and then modified.
2286 ScDPAggData* pSelectData = pSelectMember->
2287 GetAggData( nMemberMeasure, aLocalSubState );
2288 if ( pSelectData )
2290 double fTotal = pSelectData->GetAuxiliary();
2291 fTotal += pAggData->GetResult();
2292 pSelectData->SetAuxiliary( fTotal );
2293 pAggData->SetResult( fTotal );
2294 pAggData->SetEmpty(FALSE); // always display
2297 else
2298 pAggData->SetError();
2300 else if (bNoDetailsInRef)
2301 pAggData->SetError();
2302 else
2303 pAggData->SetEmpty(TRUE); // empty (dim set to 0 above)
2305 else
2307 // difference/percentage -> find specified member
2309 if ( pSelectDim )
2311 String aRefItemName = aReferenceValue.ReferenceItemName;
2312 ScDPRelativePos aRefItemPos( 0, nRelativeDir ); // nBasePos is modified later
2314 const String* pRefName = NULL;
2315 const ScDPRelativePos* pRefPos = NULL;
2316 if ( bRelative )
2317 pRefPos = &aRefItemPos;
2318 else
2319 pRefName = &aRefItemName;
2321 ScDPDataMember* pSelectMember;
2322 if ( bRefDimInCol )
2324 aRefItemPos.nBasePos = pColVisible[nColPos]; // without sort order applied
2325 pSelectMember = ScDPResultDimension::GetColReferenceMember( pRefPos, pRefName,
2326 nColPos, rRunning );
2328 else
2330 aRefItemPos.nBasePos = pRowVisible[nRowPos]; // without sort order applied
2331 long nSkip = nRowPos + 1; // including the reference dimension
2332 pSelectMember = pSelectDim->GetRowReferenceMember( pRefPos, pRefName,
2333 pRowIndexes+nSkip, pColIndexes );
2336 // difference or perc.difference is empty for the reference item itself
2337 if ( pSelectMember == this &&
2338 eRefType != sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE )
2340 pAggData->SetEmpty(TRUE);
2342 else if ( pSelectMember )
2344 const ScDPAggData* pOtherAggData = pSelectMember->
2345 GetConstAggData( nMemberMeasure, aLocalSubState );
2346 DBG_ASSERT( pOtherAggData, "no agg data" );
2347 if ( pOtherAggData )
2349 // Reference member may be visited before or after this one,
2350 // so the auxiliary value is used for the original result.
2352 double fOtherResult = pOtherAggData->GetAuxiliary();
2353 double fThisResult = pAggData->GetResult();
2354 BOOL bError = FALSE;
2355 switch ( eRefType )
2357 case sheet::DataPilotFieldReferenceType::ITEM_DIFFERENCE:
2358 fThisResult = fThisResult - fOtherResult;
2359 break;
2360 case sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE:
2361 if ( fOtherResult == 0.0 )
2362 bError = TRUE;
2363 else
2364 fThisResult = fThisResult / fOtherResult;
2365 break;
2366 case sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE:
2367 if ( fOtherResult == 0.0 )
2368 bError = TRUE;
2369 else
2370 fThisResult = ( fThisResult - fOtherResult ) / fOtherResult;
2371 break;
2372 default:
2373 DBG_ERROR("invalid calculation type");
2375 if ( bError )
2377 pAggData->SetError();
2379 else
2381 pAggData->SetResult(fThisResult);
2382 pAggData->SetEmpty(FALSE); // always display
2384 //! errors in data?
2387 else if (bRelative && !bNoDetailsInRef)
2388 pAggData->SetEmpty(TRUE); // empty
2389 else
2390 pAggData->SetError(); // error
2392 else if (bNoDetailsInRef)
2393 pAggData->SetError(); // error
2394 else
2395 pAggData->SetEmpty(TRUE); // empty
2398 else if ( eRefType == sheet::DataPilotFieldReferenceType::ROW_PERCENTAGE ||
2399 eRefType == sheet::DataPilotFieldReferenceType::COLUMN_PERCENTAGE ||
2400 eRefType == sheet::DataPilotFieldReferenceType::TOTAL_PERCENTAGE ||
2401 eRefType == sheet::DataPilotFieldReferenceType::INDEX )
2404 // set total values when they are encountered (always before their use)
2407 ScDPAggData* pColTotalData = pRefMember->GetColTotal( nMemberMeasure );
2408 ScDPAggData* pRowTotalData = rTotals.GetRowTotal( nMemberMeasure );
2409 ScDPAggData* pGrandTotalData = rTotals.GetGrandTotal( nMemberMeasure );
2411 double fTotalValue = pAggData->HasError() ? 0 : pAggData->GetResult();
2413 if ( bIsRoot && rTotals.IsInColRoot() && pGrandTotalData )
2414 pGrandTotalData->SetAuxiliary( fTotalValue );
2416 if ( bIsRoot && pRowTotalData )
2417 pRowTotalData->SetAuxiliary( fTotalValue );
2419 if ( rTotals.IsInColRoot() && pColTotalData )
2420 pColTotalData->SetAuxiliary( fTotalValue );
2423 // find relation to total values
2426 switch ( eRefType )
2428 case sheet::DataPilotFieldReferenceType::ROW_PERCENTAGE:
2429 case sheet::DataPilotFieldReferenceType::COLUMN_PERCENTAGE:
2430 case sheet::DataPilotFieldReferenceType::TOTAL_PERCENTAGE:
2432 double nTotal;
2433 if ( eRefType == sheet::DataPilotFieldReferenceType::ROW_PERCENTAGE )
2434 nTotal = pRowTotalData->GetAuxiliary();
2435 else if ( eRefType == sheet::DataPilotFieldReferenceType::COLUMN_PERCENTAGE )
2436 nTotal = pColTotalData->GetAuxiliary();
2437 else
2438 nTotal = pGrandTotalData->GetAuxiliary();
2440 if ( nTotal == 0.0 )
2441 pAggData->SetError();
2442 else
2443 pAggData->SetResult( pAggData->GetResult() / nTotal );
2445 break;
2446 case sheet::DataPilotFieldReferenceType::INDEX:
2448 double nColTotal = pColTotalData->GetAuxiliary();
2449 double nRowTotal = pRowTotalData->GetAuxiliary();
2450 double nGrandTotal = pGrandTotalData->GetAuxiliary();
2451 if ( nRowTotal == 0.0 || nColTotal == 0.0 )
2452 pAggData->SetError();
2453 else
2454 pAggData->SetResult(
2455 ( pAggData->GetResult() * nGrandTotal ) /
2456 ( nRowTotal * nColTotal ) );
2458 break;
2466 if ( bHasChild ) // child dimension must be processed last, so the row total is known
2468 if ( pDataChild )
2469 pDataChild->UpdateRunningTotals( pRefChild, nMeasure,
2470 bIsSubTotalRow, rSubState, rRunning, rTotals, rRowParent );
2475 void ScDPDataMember::DumpState( const ScDPResultMember* pRefMember, ScDocument* pDoc, ScAddress& rPos ) const
2477 lcl_DumpRow( String::CreateFromAscii("ScDPDataMember"), GetName(), &aAggregate, pDoc, rPos );
2478 SCROW nStartRow = rPos.Row();
2480 const ScDPDataDimension* pDataChild = GetChildDimension();
2481 const ScDPResultDimension* pRefChild = pRefMember->GetChildDimension();
2482 if ( pDataChild && pRefChild )
2483 pDataChild->DumpState( pRefChild, pDoc, rPos );
2485 lcl_Indent( pDoc, nStartRow, rPos );
2488 // -----------------------------------------------------------------------
2490 // Helper class to select the members to include in
2491 // ScDPResultDimension::InitFrom or LateInitFrom if groups are used
2493 class ScDPGroupCompare
2495 private:
2496 const ScDPResultData* pResultData;
2497 const ScDPInitState& rInitState;
2498 long nDimSource;
2499 BOOL bIncludeAll;
2500 BOOL bIsBase;
2501 long nGroupBase;
2502 const ScDPItemData* pBaseData;
2504 public:
2505 ScDPGroupCompare( const ScDPResultData* pData, const ScDPInitState& rState, long nDimension );
2506 ~ScDPGroupCompare() {}
2508 BOOL IsIncluded( const ScDPMember& rMember ) { return bIncludeAll || TestIncluded( rMember ); }
2509 BOOL TestIncluded( const ScDPMember& rMember );
2512 ScDPGroupCompare::ScDPGroupCompare( const ScDPResultData* pData, const ScDPInitState& rState, long nDimension ) :
2513 pResultData( pData ),
2514 rInitState( rState ),
2515 nDimSource( nDimension ),
2516 pBaseData( NULL )
2518 bIsBase = pResultData->IsBaseForGroup( nDimSource );
2519 nGroupBase = pResultData->GetGroupBase( nDimSource ); //! get together in one call?
2520 if ( nGroupBase >= 0 )
2521 pBaseData = rInitState.GetNameForIndex( nGroupBase );
2523 // if bIncludeAll is set, TestIncluded doesn't need to be called
2524 bIncludeAll = !( bIsBase || nGroupBase >= 0 );
2527 BOOL ScDPGroupCompare::TestIncluded( const ScDPMember& rMember )
2529 BOOL bInclude = TRUE;
2530 if ( pBaseData )
2532 ScDPItemData aMemberData;
2533 rMember.FillItemData( aMemberData );
2534 bInclude = pResultData->IsInGroup( aMemberData, nDimSource, *pBaseData, nGroupBase );
2536 else if ( bIsBase )
2538 // need to check all previous groups
2539 //! get array of groups (or indexes) before loop?
2540 ScDPItemData aMemberData;
2541 rMember.FillItemData( aMemberData );
2542 long nInitCount = rInitState.GetCount();
2543 const long* pInitSource = rInitState.GetSource();
2544 const ScDPItemData* pInitNames = rInitState.GetNames();
2545 for (long nInitPos=0; nInitPos<nInitCount && bInclude; nInitPos++)
2546 if ( pResultData->GetGroupBase( pInitSource[nInitPos] ) == nDimSource )
2548 bInclude = pResultData->IsInGroup( pInitNames[nInitPos], pInitSource[nInitPos],
2549 aMemberData, nDimSource );
2552 else if ( nGroupBase >= 0 )
2554 // base isn't used in preceding fields
2555 // -> look for other groups using the same base
2557 //! get array of groups (or indexes) before loop?
2558 ScDPItemData aMemberData;
2559 rMember.FillItemData( aMemberData );
2560 long nInitCount = rInitState.GetCount();
2561 const long* pInitSource = rInitState.GetSource();
2562 const ScDPItemData* pInitNames = rInitState.GetNames();
2563 for (long nInitPos=0; nInitPos<nInitCount && bInclude; nInitPos++)
2564 if ( pResultData->GetGroupBase( pInitSource[nInitPos] ) == nGroupBase )
2566 // same base (hierarchy between the two groups is irrelevant)
2567 bInclude = pResultData->HasCommonElement( pInitNames[nInitPos], pInitSource[nInitPos],
2568 aMemberData, nDimSource );
2572 return bInclude;
2575 // -----------------------------------------------------------------------
2577 ScDPResultDimension::ScDPResultDimension( const ScDPResultData* pData ) :
2578 pResultData( pData ),
2579 bInitialized( FALSE ),
2580 bIsDataLayout( FALSE ),
2581 bSortByData( FALSE ),
2582 bSortAscending( FALSE ),
2583 nSortMeasure( 0 ),
2584 bAutoShow( FALSE ),
2585 bAutoTopItems( FALSE ),
2586 nAutoMeasure( 0 ),
2587 nAutoCount( 0 )
2591 ScDPResultDimension::~ScDPResultDimension()
2593 for( int i = maMemberArray.size () ; i-- > 0 ; )
2594 delete maMemberArray[i];
2597 ScDPResultMember *ScDPResultDimension::FindMember( const ScDPItemData& rData ) const
2599 if( bIsDataLayout )
2600 return maMemberArray[0];
2602 MemberHash::const_iterator aRes = maMemberHash.find( rData );
2603 if( aRes != maMemberHash.end()) {
2604 if ( aRes->second->IsNamedItem( rData ) )
2605 return aRes->second;
2606 DBG_ERROR("problem! hash result is not the same as IsNamedItem");
2609 unsigned int i;
2610 unsigned int nCount = maMemberArray.size();
2611 ScDPResultMember* pResultMember;
2612 for( i = 0; i < nCount ; i++ )
2614 pResultMember = maMemberArray[i];
2615 if ( pResultMember->IsNamedItem( rData ) )
2616 return pResultMember;
2618 return NULL;
2621 void ScDPResultDimension::InitFrom( const vector<ScDPDimension*>& ppDim, const vector<ScDPLevel*>& ppLev,
2622 size_t nPos, ScDPInitState& rInitState )
2624 if (nPos >= ppDim.size() || nPos >= ppLev.size())
2626 bInitialized = true;
2627 return;
2630 ScDPDimension* pThisDim = ppDim[nPos];
2631 ScDPLevel* pThisLevel = ppLev[nPos];
2633 if (!pThisDim || !pThisLevel)
2635 bInitialized = true;
2636 return;
2639 bIsDataLayout = pThisDim->getIsDataLayoutDimension(); // member
2640 aDimensionName = pThisDim->getName(); // member
2642 // Check the autoshow setting. If it's enabled, store the settings.
2643 const sheet::DataPilotFieldAutoShowInfo& rAutoInfo = pThisLevel->GetAutoShow();
2644 if ( rAutoInfo.IsEnabled )
2646 bAutoShow = TRUE;
2647 bAutoTopItems = ( rAutoInfo.ShowItemsMode == sheet::DataPilotFieldShowItemsMode::FROM_TOP );
2648 nAutoMeasure = pThisLevel->GetAutoMeasure();
2649 nAutoCount = rAutoInfo.ItemCount;
2652 // Check the sort info, and store the settings if appropriate.
2653 const sheet::DataPilotFieldSortInfo& rSortInfo = pThisLevel->GetSortInfo();
2654 if ( rSortInfo.Mode == sheet::DataPilotFieldSortMode::DATA )
2656 bSortByData = TRUE;
2657 bSortAscending = rSortInfo.IsAscending;
2658 nSortMeasure = pThisLevel->GetSortMeasure();
2661 // global order is used to initialize aMembers, so it doesn't have to be looked at later
2662 const ScMemberSortOrder& rGlobalOrder = pThisLevel->GetGlobalOrder();
2664 long nDimSource = pThisDim->GetDimension(); //! check GetSourceDim?
2665 ScDPGroupCompare aCompare( pResultData, rInitState, nDimSource );
2667 // Now, go through all members and initialize them.
2668 ScDPMembers* pMembers = pThisLevel->GetMembersObject();
2669 long nMembCount = pMembers->getCount();
2670 for ( long i=0; i<nMembCount; i++ )
2672 long nSorted = rGlobalOrder.empty() ? i : rGlobalOrder[i];
2674 ScDPMember* pMember = pMembers->getByIndex(nSorted);
2675 if ( aCompare.IsIncluded( *pMember ) )
2677 ScDPResultMember* pNew = new ScDPResultMember( pResultData, pThisDim,
2678 pThisLevel, pMember, FALSE );
2679 maMemberArray.push_back( pNew );
2681 ScDPItemData aMemberData;
2682 pMember->FillItemData( aMemberData );
2684 // honour order of maMemberArray and only insert if it does not
2685 // already exist
2686 if ( maMemberHash.end() == maMemberHash.find( aMemberData ) )
2687 maMemberHash.insert( std::pair< const ScDPItemData, ScDPResultMember *>( aMemberData, pNew ) );
2689 rInitState.AddMember( nDimSource, aMemberData );
2690 pNew->InitFrom( ppDim, ppLev, nPos+1, rInitState );
2691 rInitState.RemoveMember();
2694 bInitialized = TRUE;
2697 void ScDPResultDimension::LateInitFrom( const vector<ScDPDimension*>& ppDim, const vector<ScDPLevel*>& ppLev,
2698 const vector<ScDPItemData>& pItemData, size_t nPos,
2699 ScDPInitState& rInitState )
2701 if (nPos >= ppDim.size() || nPos >= ppLev.size() || nPos >= pItemData.size())
2702 return;
2704 ScDPDimension* pThisDim = ppDim[nPos];
2705 ScDPLevel* pThisLevel = ppLev[nPos];
2706 const ScDPItemData& rThisData = pItemData[nPos];
2708 if (!pThisDim || !pThisLevel)
2709 return;
2711 long nDimSource = pThisDim->GetDimension(); //! check GetSourceDim?
2713 if ( !bInitialized )
2715 // create all members at the first call (preserve order)
2717 bIsDataLayout = pThisDim->getIsDataLayoutDimension();
2718 aDimensionName = pThisDim->getName();
2720 const sheet::DataPilotFieldAutoShowInfo& rAutoInfo = pThisLevel->GetAutoShow();
2721 if ( rAutoInfo.IsEnabled )
2723 bAutoShow = TRUE;
2724 bAutoTopItems = ( rAutoInfo.ShowItemsMode == sheet::DataPilotFieldShowItemsMode::FROM_TOP );
2725 nAutoMeasure = pThisLevel->GetAutoMeasure();
2726 nAutoCount = rAutoInfo.ItemCount;
2729 const sheet::DataPilotFieldSortInfo& rSortInfo = pThisLevel->GetSortInfo();
2730 if ( rSortInfo.Mode == sheet::DataPilotFieldSortMode::DATA )
2732 bSortByData = TRUE;
2733 bSortAscending = rSortInfo.IsAscending;
2734 nSortMeasure = pThisLevel->GetSortMeasure();
2737 // global order is used to initialize aMembers, so it doesn't have to be looked at later
2738 const ScMemberSortOrder& rGlobalOrder = pThisLevel->GetGlobalOrder();
2740 ScDPGroupCompare aCompare( pResultData, rInitState, nDimSource );
2742 ScDPMembers* pMembers = pThisLevel->GetMembersObject();
2743 long nMembCount = pMembers->getCount();
2744 for ( long i=0; i<nMembCount; i++ )
2746 long nSorted = rGlobalOrder.empty() ? i : rGlobalOrder[i];
2748 ScDPMember* pMember = pMembers->getByIndex(nSorted);
2749 if ( aCompare.IsIncluded( *pMember ) )
2751 ScDPResultMember* pNew = new ScDPResultMember( pResultData, pThisDim,
2752 pThisLevel, pMember, FALSE );
2753 maMemberArray.push_back( pNew );
2755 ScDPItemData aMemberData;
2756 pMember->FillItemData( aMemberData );
2758 // honour order of maMemberArray and only insert if it does not
2759 // already exist
2760 if ( maMemberHash.end() == maMemberHash.find( aMemberData ) )
2761 maMemberHash.insert( std::pair< const ScDPItemData, ScDPResultMember *>( aMemberData, pNew ) );
2764 bInitialized = TRUE; // don't call again, even if no members were included
2767 // initialize only specific member (or all if "show empty" flag is set)
2769 BOOL bShowEmpty = pThisLevel->getShowEmpty();
2770 if ( bIsDataLayout || bShowEmpty )
2772 long nCount = maMemberArray.size();
2773 for (long i=0; i<nCount; i++)
2775 ScDPResultMember* pResultMember = maMemberArray[i];
2776 ScDPItemData aMemberData;
2777 pResultMember->FillItemData( aMemberData );
2778 rInitState.AddMember( nDimSource, aMemberData );
2779 pResultMember->LateInitFrom( ppDim, ppLev, pItemData, nPos+1, rInitState );
2780 rInitState.RemoveMember();
2783 else
2785 ScDPResultMember* pResultMember = FindMember( rThisData );
2786 if( NULL != pResultMember )
2788 ScDPItemData aMemberData;
2789 pResultMember->FillItemData( aMemberData );
2790 rInitState.AddMember( nDimSource, aMemberData );
2791 pResultMember->LateInitFrom( ppDim, ppLev, pItemData, nPos+1, rInitState );
2792 rInitState.RemoveMember();
2797 long ScDPResultDimension::GetSize(long nMeasure) const
2799 long nTotal = 0;
2800 long nMemberCount = maMemberArray.size();
2801 if (bIsDataLayout)
2803 DBG_ASSERT(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1,
2804 "DataLayout dimension twice?");
2805 // repeat first member...
2806 nTotal = nMemberCount * maMemberArray[0]->GetSize(0); // all measures have equal size
2808 else
2810 // add all members
2811 for (long nMem=0; nMem<nMemberCount; nMem++)
2812 nTotal += maMemberArray[nMem]->GetSize(nMeasure);
2814 return nTotal;
2817 bool ScDPResultDimension::IsValidEntry( const vector<ScDPItemData>& aMembers ) const
2819 if (aMembers.empty())
2820 return false;
2822 const ScDPResultMember* pMember = FindMember( aMembers[0] );
2823 if ( NULL != pMember )
2824 return pMember->IsValidEntry( aMembers );
2826 DBG_ERROR("IsValidEntry: Member not found");
2827 return false;
2830 void ScDPResultDimension::ProcessData( const vector<ScDPItemData>& aMembers,
2831 const ScDPResultDimension* pDataDim,
2832 const vector<ScDPItemData>& aDataMembers,
2833 const vector<ScDPValueData>& aValues ) const
2835 if (aMembers.empty())
2836 return;
2838 ScDPResultMember* pMember = FindMember( aMembers[0] );
2839 if ( NULL != pMember )
2841 vector<ScDPItemData> aChildMembers;
2842 if (aMembers.size() > 1)
2844 vector<ScDPItemData>::const_iterator itr = aMembers.begin();
2845 aChildMembers.assign(++itr, aMembers.end());
2847 pMember->ProcessData( aChildMembers, pDataDim, aDataMembers, aValues );
2848 return;
2851 DBG_ERROR("ProcessData: Member not found");
2854 void ScDPResultDimension::FillMemberResults( uno::Sequence<sheet::MemberResult>* pSequences,
2855 long nStart, long nMeasure )
2857 long nPos = nStart;
2858 long nCount = maMemberArray.size();
2860 for (long i=0; i<nCount; i++)
2862 long nSorted = aMemberOrder.empty() ? i : aMemberOrder[i];
2864 ScDPResultMember* pMember = maMemberArray[nSorted];
2865 // in data layout dimension, use first member with different measures/names
2866 if ( bIsDataLayout )
2868 bool bTotalResult = false;
2869 String aMbrName = pResultData->GetMeasureDimensionName( nSorted );
2870 String aMbrCapt = pResultData->GetMeasureString( nSorted, FALSE, SUBTOTAL_FUNC_NONE, bTotalResult );
2871 maMemberArray[0]->FillMemberResults( pSequences, nPos, nSorted, FALSE, &aMbrName, &aMbrCapt );
2873 else if ( pMember->IsVisible() )
2874 pMember->FillMemberResults( pSequences, nPos, nMeasure, FALSE, NULL, NULL );
2875 // nPos is modified
2879 void ScDPResultDimension::FillDataResults( const ScDPResultMember* pRefMember,
2880 uno::Sequence< uno::Sequence<sheet::DataResult> >& rSequence,
2881 long nRow, long nMeasure ) const
2883 long nMemberRow = nRow;
2884 long nMemberMeasure = nMeasure;
2885 long nCount = maMemberArray.size();
2886 for (long i=0; i<nCount; i++)
2888 long nSorted = aMemberOrder.empty() ? i : aMemberOrder[i];
2890 const ScDPResultMember* pMember;
2891 if (bIsDataLayout)
2893 DBG_ASSERT(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1,
2894 "DataLayout dimension twice?");
2895 pMember = maMemberArray[0];
2896 nMemberMeasure = nSorted;
2898 else
2899 pMember = maMemberArray[nSorted];
2901 if ( pMember->IsVisible() )
2902 pMember->FillDataResults( pRefMember, rSequence, nMemberRow, nMemberMeasure );
2903 // nMemberRow is modified
2907 void ScDPResultDimension::UpdateDataResults( const ScDPResultMember* pRefMember, long nMeasure ) const
2909 long nMemberMeasure = nMeasure;
2910 long nCount = maMemberArray.size();
2911 for (long i=0; i<nCount; i++)
2913 const ScDPResultMember* pMember;
2914 if (bIsDataLayout)
2916 DBG_ASSERT(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1,
2917 "DataLayout dimension twice?");
2918 pMember = maMemberArray[0];
2919 nMemberMeasure = i;
2921 else
2922 pMember = maMemberArray[i];
2924 if ( pMember->IsVisible() )
2925 pMember->UpdateDataResults( pRefMember, nMemberMeasure );
2929 void ScDPResultDimension::SortMembers( ScDPResultMember* pRefMember )
2931 long nCount = maMemberArray.size();
2933 if ( bSortByData )
2935 // sort members
2937 DBG_ASSERT( aMemberOrder.empty(), "sort twice?" );
2938 aMemberOrder.resize( nCount );
2939 for (long nPos=0; nPos<nCount; nPos++)
2940 aMemberOrder[nPos] = nPos;
2942 ScDPRowMembersOrder aComp( *this, nSortMeasure, bSortAscending );
2943 ::std::sort( aMemberOrder.begin(), aMemberOrder.end(), aComp );
2946 // handle children
2948 // for data layout, call only once - sorting measure is always taken from settings
2949 long nLoopCount = bIsDataLayout ? 1 : nCount;
2950 for (long i=0; i<nLoopCount; i++)
2952 ScDPResultMember* pMember = maMemberArray[i];
2953 if ( pMember->IsVisible() )
2954 pMember->SortMembers( pRefMember );
2958 void ScDPResultDimension::DoAutoShow( ScDPResultMember* pRefMember )
2960 long nCount = maMemberArray.size();
2962 // handle children first, before changing the visible state
2964 // for data layout, call only once - sorting measure is always taken from settings
2965 long nLoopCount = bIsDataLayout ? 1 : nCount;
2966 for (long i=0; i<nLoopCount; i++)
2968 ScDPResultMember* pMember = maMemberArray[i];
2969 if ( pMember->IsVisible() )
2970 pMember->DoAutoShow( pRefMember );
2973 if ( bAutoShow && nAutoCount > 0 && nAutoCount < nCount )
2975 // establish temporary order, hide remaining members
2977 ScMemberSortOrder aAutoOrder;
2978 aAutoOrder.resize( nCount );
2979 long nPos;
2980 for (nPos=0; nPos<nCount; nPos++)
2981 aAutoOrder[nPos] = nPos;
2983 ScDPRowMembersOrder aComp( *this, nAutoMeasure, !bAutoTopItems );
2984 ::std::sort( aAutoOrder.begin(), aAutoOrder.end(), aComp );
2986 // look for equal values to the last included one
2988 long nIncluded = nAutoCount;
2989 const ScDPResultMember* pMember1 = maMemberArray[aAutoOrder[nIncluded - 1]];
2990 const ScDPDataMember* pDataMember1 = pMember1->IsVisible() ? pMember1->GetDataRoot() : NULL;
2991 BOOL bContinue = TRUE;
2992 while ( bContinue )
2994 bContinue = FALSE;
2995 if ( nIncluded < nCount )
2997 const ScDPResultMember* pMember2 = maMemberArray[aAutoOrder[nIncluded]];
2998 const ScDPDataMember* pDataMember2 = pMember2->IsVisible() ? pMember2->GetDataRoot() : NULL;
3000 if ( lcl_IsEqual( pDataMember1, pDataMember2, nAutoMeasure ) )
3002 ++nIncluded; // include more members if values are equal
3003 bContinue = TRUE;
3008 // hide the remaining members
3010 for (nPos = nIncluded; nPos < nCount; nPos++)
3012 ScDPResultMember* pMember = maMemberArray[aAutoOrder[nPos]];
3013 pMember->SetAutoHidden();
3018 void ScDPResultDimension::ResetResults()
3020 long nCount = maMemberArray.size();
3021 for (long i=0; i<nCount; i++)
3023 // sort order doesn't matter
3024 ScDPResultMember* pMember = maMemberArray[bIsDataLayout ? 0 : i];
3025 pMember->ResetResults( FALSE );
3029 long ScDPResultDimension::GetSortedIndex( long nUnsorted ) const
3031 return aMemberOrder.empty() ? nUnsorted : aMemberOrder[nUnsorted];
3034 void ScDPResultDimension::UpdateRunningTotals( const ScDPResultMember* pRefMember, long nMeasure,
3035 ScDPRunningTotalState& rRunning, ScDPRowTotals& rTotals ) const
3037 const ScDPResultMember* pMember;
3038 long nMemberMeasure = nMeasure;
3039 long nCount = maMemberArray.size();
3040 for (long i=0; i<nCount; i++)
3042 long nSorted = aMemberOrder.empty() ? i : aMemberOrder[i];
3044 if (bIsDataLayout)
3046 DBG_ASSERT(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1,
3047 "DataLayout dimension twice?");
3048 pMember = maMemberArray[0];
3049 nMemberMeasure = nSorted;
3051 else
3052 pMember = maMemberArray[nSorted];
3054 if ( pMember->IsVisible() )
3056 if ( bIsDataLayout )
3057 rRunning.AddRowIndex( 0, 0 );
3058 else
3059 rRunning.AddRowIndex( i, nSorted );
3060 pMember->UpdateRunningTotals( pRefMember, nMemberMeasure, rRunning, rTotals );
3061 rRunning.RemoveRowIndex();
3066 ScDPDataMember* ScDPResultDimension::GetRowReferenceMember( const ScDPRelativePos* pRelativePos, const String* pName,
3067 const long* pRowIndexes, const long* pColIndexes ) const
3069 // get named, previous/next, or first member of this dimension (first existing if pRelativePos and pName are NULL)
3071 DBG_ASSERT( pRelativePos == NULL || pName == NULL, "can't use position and name" );
3073 ScDPDataMember* pColMember = NULL;
3075 BOOL bFirstExisting = ( pRelativePos == NULL && pName == NULL );
3076 long nMemberCount = maMemberArray.size();
3077 long nMemberIndex = 0; // unsorted
3078 long nDirection = 1; // forward if no relative position is used
3079 if ( pRelativePos )
3081 nDirection = pRelativePos->nDirection;
3082 nMemberIndex = pRelativePos->nBasePos + nDirection; // bounds are handled below
3084 DBG_ASSERT( nDirection == 1 || nDirection == -1, "Direction must be 1 or -1" );
3086 else if ( pName )
3088 // search for named member
3090 const ScDPResultMember* pRowMember = maMemberArray[GetSortedIndex(nMemberIndex)];
3092 //! use ScDPItemData, as in ScDPDimension::IsValidPage?
3093 while ( pRowMember && pRowMember->GetName() != *pName )
3095 ++nMemberIndex;
3096 if ( nMemberIndex < nMemberCount )
3097 pRowMember = maMemberArray[GetSortedIndex(nMemberIndex)];
3098 else
3099 pRowMember = NULL;
3103 BOOL bContinue = TRUE;
3104 while ( bContinue && nMemberIndex >= 0 && nMemberIndex < nMemberCount )
3106 const ScDPResultMember* pRowMember = maMemberArray[GetSortedIndex(nMemberIndex)];
3108 // get child members by given indexes
3110 const long* pNextRowIndex = pRowIndexes;
3111 while ( *pNextRowIndex >= 0 && pRowMember )
3113 const ScDPResultDimension* pRowChild = pRowMember->GetChildDimension();
3114 if ( pRowChild && *pNextRowIndex < pRowChild->GetMemberCount() )
3115 pRowMember = pRowChild->GetMember( *pNextRowIndex );
3116 else
3117 pRowMember = NULL;
3118 ++pNextRowIndex;
3121 if ( pRowMember && pRelativePos )
3123 // Skip the member if it has hidden details
3124 // (because when looking for the details, it is skipped, too).
3125 // Also skip if the member is invisible because it has no data,
3126 // for consistent ordering.
3127 if ( pRowMember->HasHiddenDetails() || !pRowMember->IsVisible() )
3128 pRowMember = NULL;
3131 if ( pRowMember )
3133 pColMember = pRowMember->GetDataRoot();
3135 const long* pNextColIndex = pColIndexes;
3136 while ( *pNextColIndex >= 0 && pColMember )
3138 const ScDPDataDimension* pColChild = pColMember->GetChildDimension();
3139 if ( pColChild && *pNextColIndex < pColChild->GetMemberCount() )
3140 pColMember = pColChild->GetMember( *pNextColIndex );
3141 else
3142 pColMember = NULL;
3143 ++pNextColIndex;
3147 // continue searching only if looking for first existing or relative position
3148 bContinue = ( pColMember == NULL && ( bFirstExisting || pRelativePos ) );
3149 nMemberIndex += nDirection;
3152 return pColMember;
3155 // static
3156 ScDPDataMember* ScDPResultDimension::GetColReferenceMember( const ScDPRelativePos* pRelativePos, const String* pName,
3157 long nRefDimPos, const ScDPRunningTotalState& rRunning )
3159 DBG_ASSERT( pRelativePos == NULL || pName == NULL, "can't use position and name" );
3161 const long* pColIndexes = rRunning.GetColIndexes();
3162 const long* pRowIndexes = rRunning.GetRowIndexes();
3164 // get own row member using all indexes
3166 const ScDPResultMember* pRowMember = rRunning.GetRowResRoot();
3167 ScDPDataMember* pColMember = NULL;
3169 const long* pNextRowIndex = pRowIndexes;
3170 while ( *pNextRowIndex >= 0 && pRowMember )
3172 const ScDPResultDimension* pRowChild = pRowMember->GetChildDimension();
3173 if ( pRowChild && *pNextRowIndex < pRowChild->GetMemberCount() )
3174 pRowMember = pRowChild->GetMember( *pNextRowIndex );
3175 else
3176 pRowMember = NULL;
3177 ++pNextRowIndex;
3180 // get column (data) members before the reference field
3181 //! pass rRowParent from ScDPDataMember::UpdateRunningTotals instead
3183 if ( pRowMember )
3185 pColMember = pRowMember->GetDataRoot();
3187 const long* pNextColIndex = pColIndexes;
3188 long nColSkipped = 0;
3189 while ( *pNextColIndex >= 0 && pColMember && nColSkipped < nRefDimPos )
3191 const ScDPDataDimension* pColChild = pColMember->GetChildDimension();
3192 if ( pColChild && *pNextColIndex < pColChild->GetMemberCount() )
3193 pColMember = pColChild->GetMember( *pNextColIndex );
3194 else
3195 pColMember = NULL;
3196 ++pNextColIndex;
3197 ++nColSkipped;
3201 // get column member for the reference field
3203 if ( pColMember )
3205 const ScDPDataDimension* pReferenceDim = pColMember->GetChildDimension();
3206 if ( pReferenceDim )
3208 long nReferenceCount = pReferenceDim->GetMemberCount();
3210 BOOL bFirstExisting = ( pRelativePos == NULL && pName == NULL );
3211 long nMemberIndex = 0; // unsorted
3212 long nDirection = 1; // forward if no relative position is used
3213 pColMember = NULL; // don't use parent dimension's member if none found
3214 if ( pRelativePos )
3216 nDirection = pRelativePos->nDirection;
3217 nMemberIndex = pRelativePos->nBasePos + nDirection; // bounds are handled below
3219 else if ( pName )
3221 // search for named member
3223 pColMember = pReferenceDim->GetMember( pReferenceDim->GetSortedIndex( nMemberIndex ) );
3225 //! use ScDPItemData, as in ScDPDimension::IsValidPage?
3226 while ( pColMember && pColMember->GetName() != *pName )
3228 ++nMemberIndex;
3229 if ( nMemberIndex < nReferenceCount )
3230 pColMember = pReferenceDim->GetMember( pReferenceDim->GetSortedIndex( nMemberIndex ) );
3231 else
3232 pColMember = NULL;
3236 BOOL bContinue = TRUE;
3237 while ( bContinue && nMemberIndex >= 0 && nMemberIndex < nReferenceCount )
3239 pColMember = pReferenceDim->GetMember( pReferenceDim->GetSortedIndex( nMemberIndex ) );
3241 // get column members below the reference field
3243 const long* pNextColIndex = pColIndexes + nRefDimPos + 1;
3244 while ( *pNextColIndex >= 0 && pColMember )
3246 const ScDPDataDimension* pColChild = pColMember->GetChildDimension();
3247 if ( pColChild && *pNextColIndex < pColChild->GetMemberCount() )
3248 pColMember = pColChild->GetMember( *pNextColIndex );
3249 else
3250 pColMember = NULL;
3251 ++pNextColIndex;
3254 if ( pColMember && pRelativePos )
3256 // Skip the member if it has hidden details
3257 // (because when looking for the details, it is skipped, too).
3258 // Also skip if the member is invisible because it has no data,
3259 // for consistent ordering.
3260 if ( pColMember->HasHiddenDetails() || !pColMember->IsVisible() )
3261 pColMember = NULL;
3264 // continue searching only if looking for first existing or relative position
3265 bContinue = ( pColMember == NULL && ( bFirstExisting || pRelativePos ) );
3266 nMemberIndex += nDirection;
3269 else
3270 pColMember = NULL;
3273 return pColMember;
3276 void ScDPResultDimension::DumpState( const ScDPResultMember* pRefMember, ScDocument* pDoc, ScAddress& rPos ) const
3278 String aDimName = bIsDataLayout ? String::CreateFromAscii("(data layout)") : GetName();
3279 lcl_DumpRow( String::CreateFromAscii("ScDPResultDimension"), aDimName, NULL, pDoc, rPos );
3281 SCROW nStartRow = rPos.Row();
3283 long nCount = bIsDataLayout ? 1 : maMemberArray.size();
3284 for (long i=0; i<nCount; i++)
3286 const ScDPResultMember* pMember = maMemberArray[i];
3287 pMember->DumpState( pRefMember, pDoc, rPos );
3290 lcl_Indent( pDoc, nStartRow, rPos );
3293 long ScDPResultDimension::GetMemberCount() const
3295 return maMemberArray.size();
3298 const ScDPResultMember* ScDPResultDimension::GetMember(long n) const
3300 return maMemberArray[n];
3302 ScDPResultMember* ScDPResultDimension::GetMember(long n)
3304 return maMemberArray[n];
3307 ScDPResultDimension* ScDPResultDimension::GetFirstChildDimension() const
3309 if ( maMemberArray.size() > 0 )
3310 return maMemberArray[0]->GetChildDimension();
3311 else
3312 return NULL;
3315 void ScDPResultDimension::FillVisibilityData(ScDPResultVisibilityData& rData) const
3317 if (IsDataLayout())
3318 return;
3320 MemberArray::const_iterator itr = maMemberArray.begin(), itrEnd = maMemberArray.end();
3322 for (;itr != itrEnd; ++itr)
3324 ScDPResultMember* pMember = *itr;
3325 if (pMember->IsValid())
3327 ScDPItemData aItem;
3328 pMember->FillItemData(aItem);
3329 rData.addVisibleMember(GetName(), aItem);
3330 pMember->FillVisibilityData(rData);
3335 // -----------------------------------------------------------------------
3337 ScDPDataDimension::ScDPDataDimension( const ScDPResultData* pData ) :
3338 pResultData( pData ),
3339 pResultDimension( NULL ),
3340 bIsDataLayout( FALSE )
3344 ScDPDataDimension::~ScDPDataDimension()
3348 void ScDPDataDimension::InitFrom( const ScDPResultDimension* pDim )
3350 if (!pDim)
3351 return;
3353 pResultDimension = pDim;
3354 bIsDataLayout = pDim->IsDataLayout();
3356 // Go through all result members under the given result dimension, and
3357 // create a new data member instance for each result member.
3358 long nCount = pDim->GetMemberCount();
3359 for (long i=0; i<nCount; i++)
3361 const ScDPResultMember* pResMem = pDim->GetMember(i);
3363 ScDPDataMember* pNew = new ScDPDataMember( pResultData, pResMem );
3364 aMembers.Insert( pNew, aMembers.Count() );
3366 if ( !pResultData->IsLateInit() )
3368 // with LateInit, pResMem hasn't necessarily been initialized yet,
3369 // so InitFrom for the new result member is called from its ProcessData method
3371 const ScDPResultDimension* pChildDim = pResMem->GetChildDimension();
3372 if ( pChildDim )
3373 pNew->InitFrom( pChildDim );
3378 void ScDPDataDimension::ProcessData( const vector<ScDPItemData>& aDataMembers, const vector<ScDPValueData>& aValues,
3379 const ScDPSubTotalState& rSubState )
3381 // the ScDPItemData array must contain enough entries for all dimensions - this isn't checked
3383 long nCount = aMembers.Count();
3384 for (long i=0; i<nCount; i++)
3386 ScDPDataMember* pMember = aMembers[(USHORT)i];
3388 // always first member for data layout dim
3389 if ( bIsDataLayout || ( !aDataMembers.empty() && pMember->IsNamedItem(aDataMembers[0]) ) )
3391 vector<ScDPItemData> aChildDataMembers;
3392 if (aDataMembers.size() > 1)
3394 vector<ScDPItemData>::const_iterator itr = aDataMembers.begin();
3395 aChildDataMembers.assign(++itr, aDataMembers.end());
3397 pMember->ProcessData( aChildDataMembers, aValues, rSubState );
3398 return;
3402 DBG_ERROR("ProcessData: Member not found");
3405 void ScDPDataDimension::FillDataRow( const ScDPResultDimension* pRefDim,
3406 uno::Sequence<sheet::DataResult>& rSequence,
3407 long nCol, long nMeasure, BOOL bIsSubTotalRow,
3408 const ScDPSubTotalState& rSubState ) const
3410 DBG_ASSERT( pRefDim && pRefDim->GetMemberCount() == aMembers.Count(), "dimensions don't match" );
3411 DBG_ASSERT( pRefDim == pResultDimension, "wrong dim" );
3413 const ScMemberSortOrder& rMemberOrder = pRefDim->GetMemberOrder();
3415 long nMemberMeasure = nMeasure;
3416 long nMemberCol = nCol;
3417 long nCount = aMembers.Count();
3418 for (long i=0; i<nCount; i++)
3420 long nSorted = rMemberOrder.empty() ? i : rMemberOrder[i];
3422 long nMemberPos = nSorted;
3423 if (bIsDataLayout)
3425 DBG_ASSERT(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1,
3426 "DataLayout dimension twice?");
3427 nMemberPos = 0;
3428 nMemberMeasure = nSorted;
3431 const ScDPResultMember* pRefMember = pRefDim->GetMember(nMemberPos);
3432 if ( pRefMember->IsVisible() ) //! here or in ScDPDataMember::FillDataRow ???
3434 const ScDPDataMember* pDataMember = aMembers[(USHORT)nMemberPos];
3435 pDataMember->FillDataRow( pRefMember, rSequence, nMemberCol, nMemberMeasure, bIsSubTotalRow, rSubState );
3436 // nMemberCol is modified
3441 void ScDPDataDimension::UpdateDataRow( const ScDPResultDimension* pRefDim,
3442 long nMeasure, BOOL bIsSubTotalRow,
3443 const ScDPSubTotalState& rSubState ) const
3445 DBG_ASSERT( pRefDim && pRefDim->GetMemberCount() == aMembers.Count(), "dimensions don't match" );
3446 DBG_ASSERT( pRefDim == pResultDimension, "wrong dim" );
3448 long nMemberMeasure = nMeasure;
3449 long nCount = aMembers.Count();
3450 for (long i=0; i<nCount; i++)
3452 long nMemberPos = i;
3453 if (bIsDataLayout)
3455 DBG_ASSERT(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1,
3456 "DataLayout dimension twice?");
3457 nMemberPos = 0;
3458 nMemberMeasure = i;
3461 // Calculate must be called even if the member is not visible (for use as reference value)
3462 const ScDPResultMember* pRefMember = pRefDim->GetMember(nMemberPos);
3463 ScDPDataMember* pDataMember = aMembers[(USHORT)nMemberPos];
3464 pDataMember->UpdateDataRow( pRefMember, nMemberMeasure, bIsSubTotalRow, rSubState );
3468 void ScDPDataDimension::SortMembers( ScDPResultDimension* pRefDim )
3470 long nCount = aMembers.Count();
3472 if ( pRefDim->IsSortByData() )
3474 // sort members
3476 ScMemberSortOrder& rMemberOrder = pRefDim->GetMemberOrder();
3477 DBG_ASSERT( rMemberOrder.empty(), "sort twice?" );
3478 rMemberOrder.resize( nCount );
3479 for (long nPos=0; nPos<nCount; nPos++)
3480 rMemberOrder[nPos] = nPos;
3482 ScDPColMembersOrder aComp( *this, pRefDim->GetSortMeasure(), pRefDim->IsSortAscending() );
3483 ::std::sort( rMemberOrder.begin(), rMemberOrder.end(), aComp );
3486 // handle children
3488 DBG_ASSERT( pRefDim && pRefDim->GetMemberCount() == aMembers.Count(), "dimensions don't match" );
3489 DBG_ASSERT( pRefDim == pResultDimension, "wrong dim" );
3491 // for data layout, call only once - sorting measure is always taken from settings
3492 long nLoopCount = bIsDataLayout ? 1 : nCount;
3493 for (long i=0; i<nLoopCount; i++)
3495 ScDPResultMember* pRefMember = pRefDim->GetMember(i);
3496 if ( pRefMember->IsVisible() ) //! here or in ScDPDataMember ???
3498 ScDPDataMember* pDataMember = aMembers[(USHORT)i];
3499 pDataMember->SortMembers( pRefMember );
3504 void ScDPDataDimension::DoAutoShow( ScDPResultDimension* pRefDim )
3506 long nCount = aMembers.Count();
3508 // handle children first, before changing the visible state
3510 DBG_ASSERT( pRefDim && pRefDim->GetMemberCount() == aMembers.Count(), "dimensions don't match" );
3511 DBG_ASSERT( pRefDim == pResultDimension, "wrong dim" );
3513 // for data layout, call only once - sorting measure is always taken from settings
3514 long nLoopCount = bIsDataLayout ? 1 : nCount;
3515 for (long i=0; i<nLoopCount; i++)
3517 ScDPResultMember* pRefMember = pRefDim->GetMember(i);
3518 if ( pRefMember->IsVisible() ) //! here or in ScDPDataMember ???
3520 ScDPDataMember* pDataMember = aMembers[(USHORT)i];
3521 pDataMember->DoAutoShow( pRefMember );
3525 if ( pRefDim->IsAutoShow() && pRefDim->GetAutoCount() > 0 && pRefDim->GetAutoCount() < nCount )
3527 // establish temporary order, hide remaining members
3529 ScMemberSortOrder aAutoOrder;
3530 aAutoOrder.resize( nCount );
3531 long nPos;
3532 for (nPos=0; nPos<nCount; nPos++)
3533 aAutoOrder[nPos] = nPos;
3535 ScDPColMembersOrder aComp( *this, pRefDim->GetAutoMeasure(), !pRefDim->IsAutoTopItems() );
3536 ::std::sort( aAutoOrder.begin(), aAutoOrder.end(), aComp );
3538 // look for equal values to the last included one
3540 long nIncluded = pRefDim->GetAutoCount();
3541 ScDPDataMember* pDataMember1 = aMembers[(USHORT)aAutoOrder[nIncluded - 1]];
3542 if ( !pDataMember1->IsVisible() )
3543 pDataMember1 = NULL;
3544 BOOL bContinue = TRUE;
3545 while ( bContinue )
3547 bContinue = FALSE;
3548 if ( nIncluded < nCount )
3550 ScDPDataMember* pDataMember2 = aMembers[(USHORT)aAutoOrder[nIncluded]];
3551 if ( !pDataMember2->IsVisible() )
3552 pDataMember2 = NULL;
3554 if ( lcl_IsEqual( pDataMember1, pDataMember2, pRefDim->GetAutoMeasure() ) )
3556 ++nIncluded; // include more members if values are equal
3557 bContinue = TRUE;
3562 // hide the remaining members
3564 for (nPos = nIncluded; nPos < nCount; nPos++)
3566 ScDPResultMember* pMember = pRefDim->GetMember(aAutoOrder[nPos]);
3567 pMember->SetAutoHidden();
3572 void ScDPDataDimension::ResetResults()
3574 long nCount = aMembers.Count();
3575 for (long i=0; i<nCount; i++)
3577 // sort order doesn't matter
3579 long nMemberPos = bIsDataLayout ? 0 : i;
3580 ScDPDataMember* pDataMember = aMembers[(USHORT)nMemberPos];
3581 pDataMember->ResetResults();
3585 long ScDPDataDimension::GetSortedIndex( long nUnsorted ) const
3587 if (!pResultDimension)
3588 return nUnsorted;
3590 const ScMemberSortOrder& rMemberOrder = pResultDimension->GetMemberOrder();
3591 return rMemberOrder.empty() ? nUnsorted : rMemberOrder[nUnsorted];
3594 void ScDPDataDimension::UpdateRunningTotals( const ScDPResultDimension* pRefDim,
3595 long nMeasure, BOOL bIsSubTotalRow,
3596 const ScDPSubTotalState& rSubState, ScDPRunningTotalState& rRunning,
3597 ScDPRowTotals& rTotals, const ScDPResultMember& rRowParent ) const
3599 DBG_ASSERT( pRefDim && pRefDim->GetMemberCount() == aMembers.Count(), "dimensions don't match" );
3600 DBG_ASSERT( pRefDim == pResultDimension, "wrong dim" );
3602 long nMemberMeasure = nMeasure;
3603 long nCount = aMembers.Count();
3604 for (long i=0; i<nCount; i++)
3606 const ScMemberSortOrder& rMemberOrder = pRefDim->GetMemberOrder();
3607 long nSorted = rMemberOrder.empty() ? i : rMemberOrder[i];
3609 long nMemberPos = nSorted;
3610 if (bIsDataLayout)
3612 DBG_ASSERT(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1,
3613 "DataLayout dimension twice?");
3614 nMemberPos = 0;
3615 nMemberMeasure = nSorted;
3618 const ScDPResultMember* pRefMember = pRefDim->GetMember(nMemberPos);
3619 if ( pRefMember->IsVisible() ) //! here or in ScDPDataMember::UpdateRunningTotals ???
3621 if ( bIsDataLayout )
3622 rRunning.AddColIndex( 0, 0 );
3623 else
3624 rRunning.AddColIndex( i, nSorted );
3626 ScDPDataMember* pDataMember = aMembers[(USHORT)nMemberPos];
3627 pDataMember->UpdateRunningTotals( pRefMember, nMemberMeasure,
3628 bIsSubTotalRow, rSubState, rRunning, rTotals, rRowParent );
3630 rRunning.RemoveColIndex();
3635 void ScDPDataDimension::DumpState( const ScDPResultDimension* pRefDim, ScDocument* pDoc, ScAddress& rPos ) const
3637 String aDimName = String::CreateFromAscii( bIsDataLayout ? "(data layout)" : "(unknown)" );
3638 lcl_DumpRow( String::CreateFromAscii("ScDPDataDimension"), aDimName, NULL, pDoc, rPos );
3640 SCROW nStartRow = rPos.Row();
3642 long nCount = bIsDataLayout ? 1 : aMembers.Count();
3643 for (long i=0; i<nCount; i++)
3645 const ScDPResultMember* pRefMember = pRefDim->GetMember(i);
3646 const ScDPDataMember* pDataMember = aMembers[(USHORT)i];
3647 pDataMember->DumpState( pRefMember, pDoc, rPos );
3650 lcl_Indent( pDoc, nStartRow, rPos );
3653 long ScDPDataDimension::GetMemberCount() const
3655 return aMembers.Count();
3658 ScDPDataMember* ScDPDataDimension::GetMember(long n) const
3660 return aMembers[(USHORT)n];
3663 // ----------------------------------------------------------------------------
3665 ScDPResultVisibilityData::ScDPResultVisibilityData(
3666 ScSimpleSharedString& rSharedString, ScDPSource* pSource) :
3667 mrSharedString(rSharedString),
3668 mpSource(pSource)
3672 ScDPResultVisibilityData::~ScDPResultVisibilityData()
3676 void ScDPResultVisibilityData::addVisibleMember(const String& rDimName, const ScDPItemData& rMemberItem)
3678 DimMemberType::iterator itr = maDimensions.find(rDimName);
3679 if (itr == maDimensions.end())
3681 pair<DimMemberType::iterator, bool> r = maDimensions.insert(
3682 DimMemberType::value_type(rDimName, VisibleMemberType()));
3684 if (!r.second)
3685 // insertion failed.
3686 return;
3688 itr = r.first;
3690 VisibleMemberType& rMem = itr->second;
3691 VisibleMemberType::iterator itrMem = rMem.find(rMemberItem);
3692 if (itrMem == rMem.end())
3693 rMem.insert(rMemberItem);
3696 void ScDPResultVisibilityData::fillFieldFilters(vector<ScDPCacheTable::Criterion>& rFilters) const
3698 typedef hash_map<String, long, ScStringHashCode> FieldNameMapType;
3699 FieldNameMapType aFieldNames;
3700 ScDPTableData* pData = mpSource->GetData();
3701 long nColumnCount = pData->GetColumnCount();
3702 for (long i = 0; i < nColumnCount; ++i)
3704 aFieldNames.insert(
3705 FieldNameMapType::value_type(pData->getDimensionName(i), i));
3708 const ScDPDimensions* pDims = mpSource->GetDimensionsObject();
3709 for (DimMemberType::const_iterator itr = maDimensions.begin(), itrEnd = maDimensions.end();
3710 itr != itrEnd; ++itr)
3712 const String& rDimName = itr->first;
3713 ScDPCacheTable::Criterion aCri;
3714 FieldNameMapType::const_iterator itrField = aFieldNames.find(rDimName);
3715 if (itrField == aFieldNames.end())
3716 // This should never happen!
3717 continue;
3719 long nDimIndex = itrField->second;
3720 aCri.mnFieldIndex = static_cast<sal_Int32>(nDimIndex);
3721 aCri.mpFilter.reset(new ScDPCacheTable::GroupFilter(mrSharedString));
3722 ScDPCacheTable::GroupFilter* pGrpFilter =
3723 static_cast<ScDPCacheTable::GroupFilter*>(aCri.mpFilter.get());
3725 const VisibleMemberType& rMem = itr->second;
3726 for (VisibleMemberType::const_iterator itrMem = rMem.begin(), itrMemEnd = rMem.end();
3727 itrMem != itrMemEnd; ++itrMem)
3729 const ScDPItemData& rMemItem = *itrMem;
3730 pGrpFilter->addMatchItem(rMemItem.aString, rMemItem.fValue, rMemItem.bHasValue);
3733 ScDPDimension* pDim = pDims->getByIndex(nDimIndex);
3734 ScDPMembers* pMembers = pDim->GetHierarchiesObject()->getByIndex(0)->
3735 GetLevelsObject()->getByIndex(0)->GetMembersObject();
3736 if (pGrpFilter->getMatchItemCount() < static_cast<size_t>(pMembers->getCount()))
3737 rFilters.push_back(aCri);
3741 size_t ScDPResultVisibilityData::MemberHash::operator() (const ScDPItemData& r) const
3743 if (r.bHasValue)
3744 return static_cast<size_t>(::rtl::math::approxFloor(r.fValue));
3745 else
3746 return rtl_ustr_hashCode_WithLength(r.aString.GetBuffer(), r.aString.Len());