1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: dptabres.cxx,v $
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"
45 #include "subtotal.hxx"
46 #include "globstr.hrc"
47 #include "datauno.hxx" // ScDataUnoConversion
49 #include "document.hxx" // for DumpState only!
52 #include <float.h> //! Test !!!
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
;
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
;
107 ScDPRowMembersOrder( ScDPResultDimension
& rDim
, long nM
, BOOL bAsc
) :
112 ~ScDPRowMembersOrder() {}
114 BOOL
operator()( sal_Int32 nIndex1
, sal_Int32 nIndex2
) const;
117 class ScDPColMembersOrder
119 ScDPDataDimension
& rDimension
;
124 ScDPColMembersOrder( ScDPDataDimension
& rDim
, long nM
, BOOL 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();
147 return FALSE
; // equal
149 return FALSE
; // errors are always sorted at the end
152 return TRUE
; // errors are always sorted at the end
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;
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();
178 return TRUE
; // equal
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;
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?
216 if ( pDataMember2
&& !pDataMember2
->IsVisible() )
219 return lcl_IsLess( pDataMember1
, pDataMember2
, nMeasure
, bAscending
);
222 // -----------------------------------------------------------------------
224 ScDPInitState::ScDPInitState() :
227 pIndex
= new long[SC_DAPI_MAXFIELDS
];
228 pData
= new ScDPItemData
[SC_DAPI_MAXFIELDS
];
231 ScDPInitState::~ScDPInitState()
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
;
248 void ScDPInitState::RemoveMember()
250 DBG_ASSERT( nCount
> 0, "RemoveColIndex without index" );
255 const ScDPItemData
* ScDPInitState::GetNameForIndex( long nIndexValue
) const
257 for (long i
=0; i
<nCount
; i
++)
258 if ( pIndex
[i
] == nIndexValue
)
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
);
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();
288 for (SCROW nRow
= nStartRow
; nRow
< rPos
.Row(); nRow
++)
290 pDoc
->GetString( nCol
, nRow
, nTab
, aString
);
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
),
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];
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;
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;
349 void ScDPRunningTotalState::RemoveColIndex()
351 DBG_ASSERT( nColIndexPos
> 0, "RemoveColIndex without index" );
352 if ( nColIndexPos
> 0 )
355 pColVisible
[nColIndexPos
] = -1;
356 pColIndexes
[nColIndexPos
] = -1;
360 void ScDPRunningTotalState::RemoveRowIndex()
362 DBG_ASSERT( nRowIndexPos
> 0, "RemoveRowIndex without index" );
363 if ( nRowIndexPos
> 0 )
366 pRowVisible
[nRowIndexPos
] = -1;
367 pRowIndexes
[nRowIndexPos
] = -1;
371 // -----------------------------------------------------------------------
373 ScDPRelativePos::ScDPRelativePos( long nBase
, long 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
)
389 if ( rSubState
.eColForce
!= SUBTOTAL_FUNC_NONE
&& rSubState
.eRowForce
!= SUBTOTAL_FUNC_NONE
&&
390 rSubState
.eColForce
!= rSubState
.eRowForce
)
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
)
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)
405 if ( rNext
.nType
== SC_VALTYPE_STRING
)
409 ++nCount
; // for all functions
413 case SUBTOTAL_FUNC_SUM
:
414 case SUBTOTAL_FUNC_AVE
:
415 if ( !SubTotal::SafePlus( fVal
, rNext
.fValue
) )
416 nCount
= -1; // -1 for error
418 case SUBTOTAL_FUNC_PROD
:
419 if ( nCount
== 1 ) // copy first value (fVal is initialized to 0)
421 else if ( !SubTotal::SafeMult( fVal
, rNext
.fValue
) )
422 nCount
= -1; // -1 for error
424 case SUBTOTAL_FUNC_CNT
:
425 case SUBTOTAL_FUNC_CNT2
:
426 // nothing more than incrementing nCount
428 case SUBTOTAL_FUNC_MAX
:
429 if ( nCount
== 1 || rNext
.fValue
> fVal
)
432 case SUBTOTAL_FUNC_MIN
:
433 if ( nCount
== 1 || rNext
.fValue
< fVal
)
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
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() )
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.
473 // check the error conditions for the selected function
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
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
493 case SUBTOTAL_FUNC_STD
:
494 case SUBTOTAL_FUNC_VAR
:
495 bError
= ( nCount
< 2 ); // need at least 2 values
499 DBG_ERROR("invalid function");
502 // calculate the selected function
504 double fResult
= 0.0;
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
517 case SUBTOTAL_FUNC_CNT
:
518 case SUBTOTAL_FUNC_CNT2
:
522 case SUBTOTAL_FUNC_AVE
:
524 fResult
= fVal
/ (double) nCount
;
527 //! use safe mul for fVal * fVal
529 case SUBTOTAL_FUNC_STD
:
531 fResult
= sqrt((fAux
- fVal
*fVal
/(double)(nCount
)) / (double)(nCount
-1));
533 case SUBTOTAL_FUNC_VAR
:
535 fResult
= (fAux
- fVal
*fVal
/(double)(nCount
)) / (double)(nCount
-1);
537 case SUBTOTAL_FUNC_STDP
:
539 fResult
= sqrt((fAux
- fVal
*fVal
/(double)(nCount
)) / (double)nCount
);
541 case SUBTOTAL_FUNC_VARP
:
543 fResult
= (fAux
- fVal
*fVal
/(double)(nCount
)) / (double)nCount
;
546 DBG_ERROR("invalid function");
550 BOOL bEmpty
= ( nCount
== 0 ); // no data
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.
559 nCount
= SC_DPAGG_RESULT_EMPTY
;
561 nCount
= SC_DPAGG_RESULT_ERROR
;
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" );
618 nCount
= SC_DPAGG_RESULT_EMPTY
;
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" );
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" );
639 ScDPAggData
* ScDPAggData::GetChild()
642 pChild
= new ScDPAggData
;
646 void ScDPAggData::Reset()
650 nCount
= SC_DPAGG_EMPTY
;
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
);
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
;
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
);
728 // -----------------------------------------------------------------------
730 ScDPResultData::ScDPResultData( ScDPSource
* pSrc
) : //! Ref
735 pMeasRefOrient( NULL
),
743 ScDPResultData::~ScDPResultData()
747 delete[] pMeasRefOrient
;
751 void ScDPResultData::SetMeasureData( long nCount
, const ScSubTotalFunc
* pFunctions
,
752 const sheet::DataPilotFieldReference
* pRefs
, const USHORT
* pRefOrient
,
753 const String
* pNames
)
757 delete[] pMeasRefOrient
;
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
];
776 // use one dummy measure
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
)
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
);
846 DBG_ASSERT( pMeasNames
&& nMeasure
< nMeasCount
, "bumm" );
847 ScDPDimension
* pDataDim
= pSource
->GetDataDimension(nMeasure
);
850 const OUString
* pLayoutName
= pDataDim
->GetLayoutName();
855 ScSubTotalFunc eFunc
= ( eForceFunc
== SUBTOTAL_FUNC_NONE
) ?
856 GetMeasureFunction(nMeasure
) : eForceFunc
;
857 USHORT nId
= nFuncStrIds
[eFunc
];
860 aRet
+= ScGlobal::GetRscString(nId
); // function name
861 aRet
.AppendAscii(RTL_CONSTASCII_STRINGPARAM( " - " ));
863 aRet
+= pMeasNames
[nMeasure
]; // field name
869 String
ScDPResultData::GetMeasureDimensionName(long nMeasure
) const
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
912 // -----------------------------------------------------------------------
915 ScDPResultMember::ScDPResultMember( const ScDPResultData
* pData
, const ScDPDimension
* pDim
,
916 const ScDPLevel
* pLev
, const ScDPMember
* pDesc
,
918 pResultData( pData
),
920 pParentLevel( pLev
),
921 pMemberDesc( pDesc
),
922 pChildDimension( NULL
),
924 bHasElements( FALSE
),
925 bForceSubTotal( bForceSub
),
926 bHasHiddenDetails( FALSE
),
927 bInitialized( FALSE
),
930 // pParentLevel/pMemberDesc is 0 for root members
933 ScDPResultMember::~ScDPResultMember()
935 delete pChildDimension
;
939 String
ScDPResultMember::GetName() const
942 return pMemberDesc
->GetNameStr();
944 return ScGlobal::GetRscString(STR_PIVOT_TOTAL
); // root member
947 void ScDPResultMember::FillItemData( ScDPItemData
& rData
) const
950 pMemberDesc
->FillItemData( rData
);
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 ???
960 return ((ScDPMember
*)pMemberDesc
)->IsNamedItem( r
);
964 bool ScDPResultMember::IsValidEntry( const vector
<ScDPItemData
>& aMembers
) const
969 const ScDPResultDimension
* pChildDim
= GetChildDimension();
972 if (aMembers
.size() < 2)
975 vector
<ScDPItemData
>::const_iterator itr
= aMembers
.begin();
976 vector
<ScDPItemData
> aChildMembers(++itr
, aMembers
.end());
977 return pChildDim
->IsValidEntry(aChildMembers
);
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() )
992 if (nPos
>= ppDim
.size())
995 // skip child dimension if details are not shown
996 if ( pMemberDesc
&& !pMemberDesc
->getShowDetails() )
998 bHasHiddenDetails
= TRUE
; // only if there is a next dimension
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() )
1014 bInitialized
= TRUE
;
1016 if (nPos
>= ppDim
.size())
1017 // No next dimension. Bail out.
1020 // skip child dimension if details are not shown
1021 if ( pMemberDesc
&& !pMemberDesc
->getShowDetails() )
1023 bHasHiddenDetails
= TRUE
; // only if there is a next dimension
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
1037 if ( pChildDimension
&& pParentLevel
&&
1038 pParentLevel
->IsOutlineLayout() && pParentLevel
->IsSubtotalsAtTop() )
1041 long nSubTotals
= GetSubTotalCount( &nUserSubStart
);
1042 nSubTotals
-= nUserSubStart
; // visible count
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 )
1056 long ScDPResultMember::GetSize(long nMeasure
) const
1061 long nExtraSpace
= 0;
1062 if ( pParentLevel
&& pParentLevel
->IsAddEmpty() )
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
) )
1071 long nSize
= pChildDimension
->GetSize(nMeasure
);
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
;
1080 nSize
+= nUserSubCount
;
1082 return nSize
+ nExtraSpace
;
1086 if ( nMeasure
== SC_DPMEASURE_ALL
)
1087 return pResultData
->GetMeasureCount() + nExtraSpace
;
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() )
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)
1142 if ( pUserSubStart
)
1143 *pUserSubStart
= 1; // visible subtotals start at 1
1151 void ScDPResultMember::ProcessData( const vector
<ScDPItemData
>& aChildMembers
, const ScDPResultDimension
* pDataDim
,
1152 const vector
<ScDPItemData
>& aDataMembers
, const vector
<ScDPValueData
>& aValues
)
1156 if (pChildDimension
)
1157 pChildDimension
->ProcessData( aChildMembers
, pDataDim
, aDataMembers
, aValues
);
1161 pDataRoot
= new ScDPDataMember( pResultData
, NULL
);
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
)
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
);
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
)
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('\\'))
1206 if (!bEscaped
&& c
== sal_Unicode('?'))
1207 aNewStr
.Append(rCaption
);
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
;
1229 if ( pMemberName
) // if pMemberName != NULL, use instead of real member name
1230 aName
= *pMemberName
;
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.
1246 String aCaption
= aName
;
1249 const OUString
* pLayoutName
= pMemberDesc
->GetLayoutName();
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
);
1263 pArray
[rPos
].Flags
|= sheet::MemberResultFlags::NUMERIC
;
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() )
1282 BOOL bTitleLine
= FALSE
;
1283 if ( pParentLevel
&& pParentLevel
->IsOutlineLayout() )
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
);
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
);
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
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
;
1326 eForce
= lcl_GetForceFunc( pParentLevel
, nUserPos
);
1328 bool bTotalResult
= false;
1329 String aSubStr
= aCaption
;
1331 aSubStr
+= pResultData
->GetMeasureString(nMemberMeasure
, FALSE
, eForce
, bTotalResult
);
1337 // single data field layout.
1338 const OUString
* pSubtotalName
= pParentDim
->GetSubtotalName();
1340 aSubStr
= lcl_parseSubtotalName(*pSubtotalName
, aCaption
);
1341 pArray
[rPos
].Flags
&= ~sheet::MemberResultFlags::GRANDTOTAL
;
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
;
1367 ScDPResultDimension
* pLayoutDim
= pChildDimension
;
1368 while ( pLayoutDim
&& !pLayoutDim
->IsDataLayout() )
1370 pLayoutDim
= pLayoutDim
->GetFirstChildDimension();
1375 sheet::MemberResult
* pLayoutArray
= pLayoutSeq
->getArray();
1376 String aDataName
= pResultData
->GetMeasureDimensionName(nMemberMeasure
);
1377 pLayoutArray
[rPos
].Name
= rtl::OUString(aDataName
);
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() )
1402 BOOL bTitleLine
= FALSE
;
1403 if ( pParentLevel
&& pParentLevel
->IsOutlineLayout() )
1406 BOOL bSubTotalInTitle
= IsSubTotalInTitle( nMeasure
);
1408 BOOL bHasChild
= ( pChildDimension
!= NULL
);
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
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
)
1433 long nMemberMeasure
= nMeasure
;
1434 long nSubSize
= pResultData
->GetCountForMeasure(nMeasure
);
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
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
];
1470 pDataRoot
->FillDataRow( pRefMember
, rSubSeq
, nSeqCol
, nMemberMeasure
, bHasChild
, aSubState
);
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
)
1503 long nMemberMeasure
= nMeasure
;
1504 long nSubSize
= pResultData
->GetCountForMeasure(nMeasure
);
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
);
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
);
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
)
1572 pDataRoot
->ResetResults();
1574 if (pChildDimension
)
1575 pChildDimension
->ResetResults();
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
)
1600 long nMemberMeasure
= nMeasure
;
1601 long nSubSize
= pResultData
->GetCountForMeasure(nMeasure
);
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();
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
1678 return pResultMember
->GetName();
1680 return EMPTY_STRING
;
1683 BOOL
ScDPDataMember::IsVisible() const
1686 return pResultMember
->IsVisible();
1691 BOOL
ScDPDataMember::IsNamedItem( const ScDPItemData
& r
) const
1694 return pResultMember
->IsNamedItem(r
);
1699 BOOL
ScDPDataMember::HasHiddenDetails() const
1702 return pResultMember
->HasHiddenDetails();
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
;
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
)
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
)
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
)
1800 // #74542# HasData can be different between measures!
1802 const ScDPAggData
* pAgg
= GetConstAggData( nMeasure
, rSubState
);
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
);
1815 return pAgg
->HasError();
1818 double ScDPDataMember::GetAggregate( long nMeasure
, const ScDPSubTotalState
& rSubState
) const
1820 const ScDPAggData
* pAgg
= GetConstAggData( nMeasure
, rSubState
);
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
)
1837 nSkip
+= nSubPos
* pResultData
->GetMeasureCount();
1839 for ( long nPos
=0; nPos
<nSkip
; nPos
++ )
1840 pAgg
= pAgg
->GetChild(); //! need to create children here?
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
)
1855 nSkip
+= nSubPos
* pResultData
->GetMeasureCount();
1857 for ( long nPos
=0; nPos
<nSkip
; nPos
++ )
1859 pAgg
= pAgg
->GetExistingChild();
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() )
1887 BOOL bTitleLine
= FALSE
;
1888 if ( pRefParentLevel
&& pRefParentLevel
->IsOutlineLayout() )
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
);
1899 if ( bTitleLine
) // in tabular layout the title is on a separate column
1900 ++rCol
; // -> fill child dimension one column below
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
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
)
1922 ScDPSubTotalState
aLocalSubState(rSubState
); // keep row state, modify column
1924 long nMemberMeasure
= nMeasure
;
1925 long nSubSize
= pResultData
->GetCountForMeasure(nMeasure
);
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
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
) )
1961 rRes
.Flags
|= sheet::DataResultFlags::ERROR
;
1965 rRes
.Value
= GetAggregate( nMemberMeasure
, aLocalSubState
);
1966 rRes
.Flags
|= sheet::DataResultFlags::HASDATA
;
1970 if ( bHasChild
|| bIsSubTotalRow
)
1971 rRes
.Flags
|= sheet::DataResultFlags::SUBTOTAL
;
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
)
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
;
2028 ScDPAggData
* pAggData
= GetAggData( nMemberMeasure
, 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
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()
2089 ScDPDataDimension
* pDataChild
= GetChildDimension();
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
)
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
;
2140 ScDPAggData
* pAggData
= GetAggData( nMemberMeasure
, 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
);
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
;
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
;
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();
2190 // child dimension of innermost member?
2191 if ( pSelectDim
&& pRowIndexes
[nRowPos
] < 0 )
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();
2207 // child dimension of innermost member?
2208 if ( pSelectDim
&& pColIndexes
[nColPos
] < 0 )
2212 BOOL bNoDetailsInRef
= FALSE
;
2213 if ( pSelectDim
&& bRunningTotal
)
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
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
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
2246 BOOL bInnerNoDetails
= bRefDimInCol
? HasHiddenDetails() :
2247 ( bRefDimInRow
? rRowParent
.HasHiddenDetails() : TRUE
);
2248 if ( bInnerNoDetails
)
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
2268 ScDPDataMember
* pSelectMember
;
2270 pSelectMember
= ScDPResultDimension::GetColReferenceMember( NULL
, NULL
,
2271 nColPos
, rRunning
);
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
);
2290 double fTotal
= pSelectData
->GetAuxiliary();
2291 fTotal
+= pAggData
->GetResult();
2292 pSelectData
->SetAuxiliary( fTotal
);
2293 pAggData
->SetResult( fTotal
);
2294 pAggData
->SetEmpty(FALSE
); // always display
2298 pAggData
->SetError();
2300 else if (bNoDetailsInRef
)
2301 pAggData
->SetError();
2303 pAggData
->SetEmpty(TRUE
); // empty (dim set to 0 above)
2307 // difference/percentage -> find specified member
2311 String aRefItemName
= aReferenceValue
.ReferenceItemName
;
2312 ScDPRelativePos
aRefItemPos( 0, nRelativeDir
); // nBasePos is modified later
2314 const String
* pRefName
= NULL
;
2315 const ScDPRelativePos
* pRefPos
= NULL
;
2317 pRefPos
= &aRefItemPos
;
2319 pRefName
= &aRefItemName
;
2321 ScDPDataMember
* pSelectMember
;
2324 aRefItemPos
.nBasePos
= pColVisible
[nColPos
]; // without sort order applied
2325 pSelectMember
= ScDPResultDimension::GetColReferenceMember( pRefPos
, pRefName
,
2326 nColPos
, rRunning
);
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
;
2357 case sheet::DataPilotFieldReferenceType::ITEM_DIFFERENCE
:
2358 fThisResult
= fThisResult
- fOtherResult
;
2360 case sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE
:
2361 if ( fOtherResult
== 0.0 )
2364 fThisResult
= fThisResult
/ fOtherResult
;
2366 case sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE
:
2367 if ( fOtherResult
== 0.0 )
2370 fThisResult
= ( fThisResult
- fOtherResult
) / fOtherResult
;
2373 DBG_ERROR("invalid calculation type");
2377 pAggData
->SetError();
2381 pAggData
->SetResult(fThisResult
);
2382 pAggData
->SetEmpty(FALSE
); // always display
2387 else if (bRelative
&& !bNoDetailsInRef
)
2388 pAggData
->SetEmpty(TRUE
); // empty
2390 pAggData
->SetError(); // error
2392 else if (bNoDetailsInRef
)
2393 pAggData
->SetError(); // error
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
2428 case sheet::DataPilotFieldReferenceType::ROW_PERCENTAGE
:
2429 case sheet::DataPilotFieldReferenceType::COLUMN_PERCENTAGE
:
2430 case sheet::DataPilotFieldReferenceType::TOTAL_PERCENTAGE
:
2433 if ( eRefType
== sheet::DataPilotFieldReferenceType::ROW_PERCENTAGE
)
2434 nTotal
= pRowTotalData
->GetAuxiliary();
2435 else if ( eRefType
== sheet::DataPilotFieldReferenceType::COLUMN_PERCENTAGE
)
2436 nTotal
= pColTotalData
->GetAuxiliary();
2438 nTotal
= pGrandTotalData
->GetAuxiliary();
2440 if ( nTotal
== 0.0 )
2441 pAggData
->SetError();
2443 pAggData
->SetResult( pAggData
->GetResult() / nTotal
);
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();
2454 pAggData
->SetResult(
2455 ( pAggData
->GetResult() * nGrandTotal
) /
2456 ( nRowTotal
* nColTotal
) );
2466 if ( bHasChild
) // child dimension must be processed last, so the row total is known
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
2496 const ScDPResultData
* pResultData
;
2497 const ScDPInitState
& rInitState
;
2502 const ScDPItemData
* pBaseData
;
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
),
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
;
2532 ScDPItemData aMemberData
;
2533 rMember
.FillItemData( aMemberData
);
2534 bInclude
= pResultData
->IsInGroup( aMemberData
, nDimSource
, *pBaseData
, nGroupBase
);
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
);
2575 // -----------------------------------------------------------------------
2577 ScDPResultDimension::ScDPResultDimension( const ScDPResultData
* pData
) :
2578 pResultData( pData
),
2579 bInitialized( FALSE
),
2580 bIsDataLayout( FALSE
),
2581 bSortByData( FALSE
),
2582 bSortAscending( FALSE
),
2585 bAutoTopItems( FALSE
),
2591 ScDPResultDimension::~ScDPResultDimension()
2593 for( int i
= maMemberArray
.size () ; i
-- > 0 ; )
2594 delete maMemberArray
[i
];
2597 ScDPResultMember
*ScDPResultDimension::FindMember( const ScDPItemData
& rData
) const
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");
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
;
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;
2630 ScDPDimension
* pThisDim
= ppDim
[nPos
];
2631 ScDPLevel
* pThisLevel
= ppLev
[nPos
];
2633 if (!pThisDim
|| !pThisLevel
)
2635 bInitialized
= true;
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
)
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
)
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
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())
2704 ScDPDimension
* pThisDim
= ppDim
[nPos
];
2705 ScDPLevel
* pThisLevel
= ppLev
[nPos
];
2706 const ScDPItemData
& rThisData
= pItemData
[nPos
];
2708 if (!pThisDim
|| !pThisLevel
)
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
)
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
)
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
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();
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
2800 long nMemberCount
= maMemberArray
.size();
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
2811 for (long nMem
=0; nMem
<nMemberCount
; nMem
++)
2812 nTotal
+= maMemberArray
[nMem
]->GetSize(nMeasure
);
2817 bool ScDPResultDimension::IsValidEntry( const vector
<ScDPItemData
>& aMembers
) const
2819 if (aMembers
.empty())
2822 const ScDPResultMember
* pMember
= FindMember( aMembers
[0] );
2823 if ( NULL
!= pMember
)
2824 return pMember
->IsValidEntry( aMembers
);
2826 DBG_ERROR("IsValidEntry: Member not found");
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())
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
);
2851 DBG_ERROR("ProcessData: Member not found");
2854 void ScDPResultDimension::FillMemberResults( uno::Sequence
<sheet::MemberResult
>* pSequences
,
2855 long nStart
, long nMeasure
)
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
);
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
;
2893 DBG_ASSERT(nMeasure
== SC_DPMEASURE_ALL
|| pResultData
->GetMeasureCount() == 1,
2894 "DataLayout dimension twice?");
2895 pMember
= maMemberArray
[0];
2896 nMemberMeasure
= nSorted
;
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
;
2916 DBG_ASSERT(nMeasure
== SC_DPMEASURE_ALL
|| pResultData
->GetMeasureCount() == 1,
2917 "DataLayout dimension twice?");
2918 pMember
= maMemberArray
[0];
2922 pMember
= maMemberArray
[i
];
2924 if ( pMember
->IsVisible() )
2925 pMember
->UpdateDataResults( pRefMember
, nMemberMeasure
);
2929 void ScDPResultDimension::SortMembers( ScDPResultMember
* pRefMember
)
2931 long nCount
= maMemberArray
.size();
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
);
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
);
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
;
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
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
];
3046 DBG_ASSERT(nMeasure
== SC_DPMEASURE_ALL
|| pResultData
->GetMeasureCount() == 1,
3047 "DataLayout dimension twice?");
3048 pMember
= maMemberArray
[0];
3049 nMemberMeasure
= nSorted
;
3052 pMember
= maMemberArray
[nSorted
];
3054 if ( pMember
->IsVisible() )
3056 if ( bIsDataLayout
)
3057 rRunning
.AddRowIndex( 0, 0 );
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
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" );
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
)
3096 if ( nMemberIndex
< nMemberCount
)
3097 pRowMember
= maMemberArray
[GetSortedIndex(nMemberIndex
)];
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
);
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() )
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
);
3147 // continue searching only if looking for first existing or relative position
3148 bContinue
= ( pColMember
== NULL
&& ( bFirstExisting
|| pRelativePos
) );
3149 nMemberIndex
+= nDirection
;
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
);
3180 // get column (data) members before the reference field
3181 //! pass rRowParent from ScDPDataMember::UpdateRunningTotals instead
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
);
3201 // get column member for the reference field
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
3216 nDirection
= pRelativePos
->nDirection
;
3217 nMemberIndex
= pRelativePos
->nBasePos
+ nDirection
; // bounds are handled below
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
)
3229 if ( nMemberIndex
< nReferenceCount
)
3230 pColMember
= pReferenceDim
->GetMember( pReferenceDim
->GetSortedIndex( nMemberIndex
) );
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
);
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() )
3264 // continue searching only if looking for first existing or relative position
3265 bContinue
= ( pColMember
== NULL
&& ( bFirstExisting
|| pRelativePos
) );
3266 nMemberIndex
+= nDirection
;
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();
3315 void ScDPResultDimension::FillVisibilityData(ScDPResultVisibilityData
& rData
) const
3320 MemberArray::const_iterator itr
= maMemberArray
.begin(), itrEnd
= maMemberArray
.end();
3322 for (;itr
!= itrEnd
; ++itr
)
3324 ScDPResultMember
* pMember
= *itr
;
3325 if (pMember
->IsValid())
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
)
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();
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
);
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
;
3425 DBG_ASSERT(nMeasure
== SC_DPMEASURE_ALL
|| pResultData
->GetMeasureCount() == 1,
3426 "DataLayout dimension twice?");
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
;
3455 DBG_ASSERT(nMeasure
== SC_DPMEASURE_ALL
|| pResultData
->GetMeasureCount() == 1,
3456 "DataLayout dimension twice?");
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() )
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
);
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
);
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
;
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
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
)
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
;
3612 DBG_ASSERT(nMeasure
== SC_DPMEASURE_ALL
|| pResultData
->GetMeasureCount() == 1,
3613 "DataLayout dimension twice?");
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 );
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
),
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()));
3685 // insertion failed.
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
)
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!
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
3744 return static_cast<size_t>(::rtl::math::approxFloor(r
.fValue
));
3746 return rtl_ustr_hashCode_WithLength(r
.aString
.GetBuffer(), r
.aString
.Len());