Version 6.4.0.3, tag libreoffice-6.4.0.3
[LibreOffice.git] / sc / source / filter / excel / xepivot.cxx
blob6334e0d961f2ff5d7c8be02d4ebd10118305cd49
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <xepivot.hxx>
21 #include <xehelper.hxx>
22 #include <com/sun/star/sheet/DataPilotFieldSortInfo.hpp>
23 #include <com/sun/star/sheet/DataPilotFieldAutoShowInfo.hpp>
24 #include <com/sun/star/sheet/DataPilotFieldLayoutInfo.hpp>
25 #include <com/sun/star/sheet/DataPilotFieldReference.hpp>
26 #include <com/sun/star/sheet/DataPilotFieldReferenceItemType.hpp>
27 #include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
28 #include <com/sun/star/sheet/DataPilotFieldSortMode.hpp>
30 #include <algorithm>
31 #include <math.h>
33 #include <osl/diagnose.h>
34 #include <sot/storage.hxx>
35 #include <document.hxx>
36 #include <dpcache.hxx>
37 #include <dpgroup.hxx>
38 #include <dpobject.hxx>
39 #include <dpsave.hxx>
40 #include <dpdimsave.hxx>
41 #include <dpshttab.hxx>
42 #include <globstr.hrc>
43 #include <scresid.hxx>
44 #include <xestring.hxx>
45 #include <xelink.hxx>
46 #include <dputil.hxx>
47 #include <generalfunction.hxx>
49 using namespace ::oox;
51 using ::com::sun::star::sheet::DataPilotFieldOrientation;
52 using ::com::sun::star::sheet::DataPilotFieldOrientation_ROW;
53 using ::com::sun::star::sheet::DataPilotFieldOrientation_COLUMN;
54 using ::com::sun::star::sheet::DataPilotFieldOrientation_PAGE;
55 using ::com::sun::star::sheet::DataPilotFieldOrientation_DATA;
56 using ::com::sun::star::sheet::GeneralFunction;
57 using ::com::sun::star::sheet::DataPilotFieldSortInfo;
58 using ::com::sun::star::sheet::DataPilotFieldAutoShowInfo;
59 using ::com::sun::star::sheet::DataPilotFieldLayoutInfo;
60 using ::com::sun::star::sheet::DataPilotFieldReference;
62 // Pivot cache
64 namespace {
66 // constants to track occurrence of specific data types
67 const sal_uInt16 EXC_PCITEM_DATA_STRING = 0x0001; /// String, empty, boolean, error.
68 const sal_uInt16 EXC_PCITEM_DATA_DOUBLE = 0x0002; /// Double with fraction.
69 const sal_uInt16 EXC_PCITEM_DATA_INTEGER = 0x0004; /// Integer, double without fraction.
70 const sal_uInt16 EXC_PCITEM_DATA_DATE = 0x0008; /// Date, time, date/time.
72 /** Maps a bitfield consisting of EXC_PCITEM_DATA_* flags above to SXFIELD data type bitfield. */
73 static const sal_uInt16 spnPCItemFlags[] =
74 { // STR DBL INT DAT
75 EXC_SXFIELD_DATA_NONE,
76 EXC_SXFIELD_DATA_STR, // x
77 EXC_SXFIELD_DATA_INT, // x
78 EXC_SXFIELD_DATA_STR_INT, // x x
79 EXC_SXFIELD_DATA_DBL, // x
80 EXC_SXFIELD_DATA_STR_DBL, // x x
81 EXC_SXFIELD_DATA_INT, // x x
82 EXC_SXFIELD_DATA_STR_INT, // x x x
83 EXC_SXFIELD_DATA_DATE, // x
84 EXC_SXFIELD_DATA_DATE_STR, // x x
85 EXC_SXFIELD_DATA_DATE_NUM, // x x
86 EXC_SXFIELD_DATA_DATE_STR, // x x x
87 EXC_SXFIELD_DATA_DATE_NUM, // x x
88 EXC_SXFIELD_DATA_DATE_STR, // x x x
89 EXC_SXFIELD_DATA_DATE_NUM, // x x x
90 EXC_SXFIELD_DATA_DATE_STR // x x x x
93 } // namespace
95 XclExpPCItem::XclExpPCItem( const OUString& rText ) :
96 XclExpRecord( (!rText.isEmpty()) ? EXC_ID_SXSTRING : EXC_ID_SXEMPTY, 0 ),
97 mnTypeFlag( EXC_PCITEM_DATA_STRING )
99 if( !rText.isEmpty() )
100 SetText( rText );
101 else
102 SetEmpty();
105 XclExpPCItem::XclExpPCItem( double fValue, const OUString& rText ) :
106 XclExpRecord( EXC_ID_SXDOUBLE, 8 )
108 SetDouble( fValue, rText );
109 mnTypeFlag = (fValue - floor( fValue ) == 0.0) ?
110 EXC_PCITEM_DATA_INTEGER : EXC_PCITEM_DATA_DOUBLE;
113 XclExpPCItem::XclExpPCItem( const DateTime& rDateTime, const OUString& rText ) :
114 XclExpRecord( EXC_ID_SXDATETIME, 8 )
116 SetDateTime( rDateTime, rText );
117 mnTypeFlag = EXC_PCITEM_DATA_DATE;
120 XclExpPCItem::XclExpPCItem( sal_Int16 nValue ) :
121 XclExpRecord( EXC_ID_SXINTEGER, 2 ),
122 mnTypeFlag( EXC_PCITEM_DATA_INTEGER )
124 SetInteger( nValue );
127 XclExpPCItem::XclExpPCItem( bool bValue, const OUString& rText ) :
128 XclExpRecord( EXC_ID_SXBOOLEAN, 2 ),
129 mnTypeFlag( EXC_PCITEM_DATA_STRING )
131 SetBool( bValue, rText );
134 bool XclExpPCItem::EqualsText( const OUString& rText ) const
136 return rText.isEmpty() ? IsEmpty() : (GetText() && (*GetText() == rText));
139 bool XclExpPCItem::EqualsDouble( double fValue ) const
141 return GetDouble() && (*GetDouble() == fValue);
144 bool XclExpPCItem::EqualsDateTime( const DateTime& rDateTime ) const
146 return GetDateTime() && (*GetDateTime() == rDateTime);
149 bool XclExpPCItem::EqualsBool( bool bValue ) const
151 return GetBool() && (*GetBool() == bValue);
154 void XclExpPCItem::WriteBody( XclExpStream& rStrm )
156 if( const OUString* pText = GetText() )
158 rStrm << XclExpString( *pText );
160 else if( const double* pfValue = GetDouble() )
162 rStrm << *pfValue;
164 else if( const sal_Int16* pnValue = GetInteger() )
166 rStrm << *pnValue;
168 else if( const DateTime* pDateTime = GetDateTime() )
170 sal_uInt16 nYear = static_cast< sal_uInt16 >( pDateTime->GetYear() );
171 sal_uInt16 nMonth = pDateTime->GetMonth();
172 sal_uInt8 nDay = static_cast< sal_uInt8 >( pDateTime->GetDay() );
173 sal_uInt8 nHour = static_cast< sal_uInt8 >( pDateTime->GetHour() );
174 sal_uInt8 nMin = static_cast< sal_uInt8 >( pDateTime->GetMin() );
175 sal_uInt8 nSec = static_cast< sal_uInt8 >( pDateTime->GetSec() );
176 if( nYear < 1900 ) { nYear = 1900; nMonth = 1; nDay = 0; }
177 rStrm << nYear << nMonth << nDay << nHour << nMin << nSec;
179 else if( const bool* pbValue = GetBool() )
181 rStrm << static_cast< sal_uInt16 >( *pbValue ? 1 : 0 );
183 else
185 // nothing to do for SXEMPTY
186 OSL_ENSURE( IsEmpty(), "XclExpPCItem::WriteBody - no data found" );
190 XclExpPCField::XclExpPCField(
191 const XclExpRoot& rRoot, sal_uInt16 nFieldIdx,
192 const ScDPObject& rDPObj, const ScRange& rRange ) :
193 XclExpRecord( EXC_ID_SXFIELD ),
194 XclPCField( EXC_PCFIELD_STANDARD, nFieldIdx ),
195 XclExpRoot( rRoot ),
196 mnTypeFlags( 0 )
198 // general settings for the standard field, insert all items from source range
199 InitStandardField( rRange );
201 // add special settings for inplace numeric grouping
202 if( const ScDPSaveData* pSaveData = rDPObj.GetSaveData() )
204 if( const ScDPDimensionSaveData* pSaveDimData = pSaveData->GetExistingDimensionData() )
206 if( const ScDPSaveNumGroupDimension* pNumGroupDim = pSaveDimData->GetNumGroupDim( GetFieldName() ) )
208 const ScDPNumGroupInfo& rNumInfo = pNumGroupDim->GetInfo();
209 const ScDPNumGroupInfo& rDateInfo = pNumGroupDim->GetDateInfo();
210 OSL_ENSURE( !rNumInfo.mbEnable || !rDateInfo.mbEnable,
211 "XclExpPCField::XclExpPCField - numeric and date grouping enabled" );
213 if( rNumInfo.mbEnable )
214 InitNumGroupField( rDPObj, rNumInfo );
215 else if( rDateInfo.mbEnable )
216 InitDateGroupField( rDPObj, rDateInfo, pNumGroupDim->GetDatePart() );
221 // final settings (flags, item numbers)
222 Finalize();
225 XclExpPCField::XclExpPCField(
226 const XclExpRoot& rRoot, sal_uInt16 nFieldIdx,
227 const ScDPObject& rDPObj, const ScDPSaveGroupDimension& rGroupDim, const XclExpPCField& rBaseField ) :
228 XclExpRecord( EXC_ID_SXFIELD ),
229 XclPCField( EXC_PCFIELD_STDGROUP, nFieldIdx ),
230 XclExpRoot( rRoot ),
231 mnTypeFlags( 0 )
233 // add base field info (always using first base field, not predecessor of this field) ***
234 OSL_ENSURE( rBaseField.GetFieldName() == rGroupDim.GetSourceDimName(),
235 "XclExpPCField::FillFromGroup - wrong base cache field" );
236 maFieldInfo.maName = rGroupDim.GetGroupDimName();
237 maFieldInfo.mnGroupBase = rBaseField.GetFieldIndex();
239 // add standard group info or date group info
240 const ScDPNumGroupInfo& rDateInfo = rGroupDim.GetDateInfo();
241 if( rDateInfo.mbEnable && (rGroupDim.GetDatePart() != 0) )
242 InitDateGroupField( rDPObj, rDateInfo, rGroupDim.GetDatePart() );
243 else
244 InitStdGroupField( rBaseField, rGroupDim );
246 // final settings (flags, item numbers)
247 Finalize();
250 XclExpPCField::~XclExpPCField()
254 void XclExpPCField::SetGroupChildField( const XclExpPCField& rChildField )
256 OSL_ENSURE( !::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_HASCHILD ),
257 "XclExpPCField::SetGroupChildIndex - field already has a grouping child field" );
258 ::set_flag( maFieldInfo.mnFlags, EXC_SXFIELD_HASCHILD );
259 maFieldInfo.mnGroupChild = rChildField.GetFieldIndex();
262 sal_uInt16 XclExpPCField::GetItemCount() const
264 return static_cast< sal_uInt16 >( GetVisItemList().GetSize() );
267 const XclExpPCItem* XclExpPCField::GetItem( sal_uInt16 nItemIdx ) const
269 return GetVisItemList().GetRecord( nItemIdx ).get();
272 sal_uInt16 XclExpPCField::GetItemIndex( const OUString& rItemName ) const
274 const XclExpPCItemList& rItemList = GetVisItemList();
275 for( size_t nPos = 0, nSize = rItemList.GetSize(); nPos < nSize; ++nPos )
276 if( rItemList.GetRecord( nPos )->ConvertToText() == rItemName )
277 return static_cast< sal_uInt16 >( nPos );
278 return EXC_PC_NOITEM;
281 std::size_t XclExpPCField::GetIndexSize() const
283 return Has16BitIndexes() ? 2 : 1;
286 void XclExpPCField::WriteIndex( XclExpStream& rStrm, sal_uInt32 nSrcRow ) const
288 // only standard fields write item indexes
289 if( nSrcRow < maIndexVec.size() )
291 sal_uInt16 nIndex = maIndexVec[ nSrcRow ];
292 if( Has16BitIndexes() )
293 rStrm << nIndex;
294 else
295 rStrm << static_cast< sal_uInt8 >( nIndex );
299 void XclExpPCField::Save( XclExpStream& rStrm )
301 OSL_ENSURE( IsSupportedField(), "XclExpPCField::Save - unknown field type" );
302 // SXFIELD
303 XclExpRecord::Save( rStrm );
304 // SXFDBTYPE
305 XclExpUInt16Record( EXC_ID_SXFDBTYPE, EXC_SXFDBTYPE_DEFAULT ).Save( rStrm );
306 // list of grouping items
307 maGroupItemList.Save( rStrm );
308 // SXGROUPINFO
309 WriteSxgroupinfo( rStrm );
310 // SXNUMGROUP and additional grouping items (grouping limit settings)
311 WriteSxnumgroup( rStrm );
312 // list of original items
313 maOrigItemList.Save( rStrm );
316 // private --------------------------------------------------------------------
318 const XclExpPCField::XclExpPCItemList& XclExpPCField::GetVisItemList() const
320 OSL_ENSURE( IsStandardField() == maGroupItemList.IsEmpty(),
321 "XclExpPCField::GetVisItemList - unexpected additional items in standard field" );
322 return IsStandardField() ? maOrigItemList : maGroupItemList;
325 void XclExpPCField::InitStandardField( const ScRange& rRange )
327 OSL_ENSURE( IsStandardField(), "XclExpPCField::InitStandardField - only for standard fields" );
328 OSL_ENSURE( rRange.aStart.Col() == rRange.aEnd.Col(), "XclExpPCField::InitStandardField - cell range with multiple columns" );
330 ScDocument& rDoc = GetDoc();
331 SvNumberFormatter& rFormatter = GetFormatter();
333 // field name is in top cell of the range
334 ScAddress aPos( rRange.aStart );
335 maFieldInfo.maName = rDoc.GetString(aPos.Col(), aPos.Row(), aPos.Tab());
336 // #i76047# maximum field name length in pivot cache is 255
337 if (maFieldInfo.maName.getLength() > EXC_PC_MAXSTRLEN)
338 maFieldInfo.maName = maFieldInfo.maName.copy(0, EXC_PC_MAXSTRLEN);
340 // loop over all cells, create pivot cache items
341 for( aPos.IncRow(); (aPos.Row() <= rRange.aEnd.Row()) && (maOrigItemList.GetSize() < EXC_PC_MAXITEMCOUNT); aPos.IncRow() )
343 OUString aText = rDoc.GetString(aPos.Col(), aPos.Row(), aPos.Tab());
344 if( rDoc.HasValueData( aPos.Col(), aPos.Row(), aPos.Tab() ) )
346 double fValue = rDoc.GetValue( aPos );
347 SvNumFormatType nFmtType = rFormatter.GetType( rDoc.GetNumberFormat( rDoc.GetNonThreadedContext(), aPos ) );
348 if( nFmtType == SvNumFormatType::LOGICAL )
349 InsertOrigBoolItem( fValue != 0, aText );
350 else if( nFmtType & SvNumFormatType::DATETIME )
351 InsertOrigDateTimeItem( GetDateTimeFromDouble( ::std::max( fValue, 0.0 ) ), aText );
352 else
353 InsertOrigDoubleItem( fValue, aText );
355 else
357 InsertOrigTextItem( aText );
362 void XclExpPCField::InitStdGroupField( const XclExpPCField& rBaseField, const ScDPSaveGroupDimension& rGroupDim )
364 OSL_ENSURE( IsGroupField(), "XclExpPCField::InitStdGroupField - only for standard grouping fields" );
366 maFieldInfo.mnBaseItems = rBaseField.GetItemCount();
367 maGroupOrder.resize( maFieldInfo.mnBaseItems, EXC_PC_NOITEM );
369 // loop over all groups of this field
370 for( long nGroupIdx = 0, nGroupCount = rGroupDim.GetGroupCount(); nGroupIdx < nGroupCount; ++nGroupIdx )
372 const ScDPSaveGroupItem& rGroupItem = rGroupDim.GetGroupByIndex( nGroupIdx );
373 // the index of the new item containing the grouping name
374 sal_uInt16 nGroupItemIdx = EXC_PC_NOITEM;
375 // loop over all elements of one group
376 for( size_t nElemIdx = 0, nElemCount = rGroupItem.GetElementCount(); nElemIdx < nElemCount; ++nElemIdx )
378 if (const OUString* pElemName = rGroupItem.GetElementByIndex(nElemIdx))
380 // try to find the item that is part of the group in the base field
381 sal_uInt16 nBaseItemIdx = rBaseField.GetItemIndex( *pElemName );
382 if( nBaseItemIdx < maFieldInfo.mnBaseItems )
384 // add group name item only if there are any valid base items
385 if( nGroupItemIdx == EXC_PC_NOITEM )
386 nGroupItemIdx = InsertGroupItem( new XclExpPCItem( rGroupItem.GetGroupName() ) );
387 maGroupOrder[ nBaseItemIdx ] = nGroupItemIdx;
393 // add items and base item indexes of all ungrouped elements
394 for( sal_uInt16 nBaseItemIdx = 0; nBaseItemIdx < maFieldInfo.mnBaseItems; ++nBaseItemIdx )
395 // items that are not part of a group still have the EXC_PC_NOITEM entry
396 if( maGroupOrder[ nBaseItemIdx ] == EXC_PC_NOITEM )
397 // try to find the base item
398 if( const XclExpPCItem* pBaseItem = rBaseField.GetItem( nBaseItemIdx ) )
399 // create a clone of the base item, insert its index into item order list
400 maGroupOrder[ nBaseItemIdx ] = InsertGroupItem( new XclExpPCItem( *pBaseItem ) );
403 void XclExpPCField::InitNumGroupField( const ScDPObject& rDPObj, const ScDPNumGroupInfo& rNumInfo )
405 OSL_ENSURE( IsStandardField(), "XclExpPCField::InitNumGroupField - only for standard fields" );
406 OSL_ENSURE( rNumInfo.mbEnable, "XclExpPCField::InitNumGroupField - numeric grouping not enabled" );
408 // new field type, date type, limit settings (min/max/step/auto)
409 if( rNumInfo.mbDateValues )
411 // special case: group by days with step count
412 meFieldType = EXC_PCFIELD_DATEGROUP;
413 maNumGroupInfo.SetScDateType( css::sheet::DataPilotFieldGroupBy::DAYS );
414 SetDateGroupLimit( rNumInfo, true );
416 else
418 meFieldType = EXC_PCFIELD_NUMGROUP;
419 maNumGroupInfo.SetNumType();
420 SetNumGroupLimit( rNumInfo );
423 // generate visible items
424 InsertNumDateGroupItems( rDPObj, rNumInfo );
427 void XclExpPCField::InitDateGroupField( const ScDPObject& rDPObj, const ScDPNumGroupInfo& rDateInfo, sal_Int32 nDatePart )
429 OSL_ENSURE( IsStandardField() || IsStdGroupField(), "XclExpPCField::InitDateGroupField - only for standard fields" );
430 OSL_ENSURE( rDateInfo.mbEnable, "XclExpPCField::InitDateGroupField - date grouping not enabled" );
432 // new field type
433 meFieldType = IsStandardField() ? EXC_PCFIELD_DATEGROUP : EXC_PCFIELD_DATECHILD;
435 // date type, limit settings (min/max/step/auto)
436 maNumGroupInfo.SetScDateType( nDatePart );
437 SetDateGroupLimit( rDateInfo, false );
439 // generate visible items
440 InsertNumDateGroupItems( rDPObj, rDateInfo, nDatePart );
443 void XclExpPCField::InsertItemArrayIndex( size_t nListPos )
445 OSL_ENSURE( IsStandardField(), "XclExpPCField::InsertItemArrayIndex - only for standard fields" );
446 maIndexVec.push_back( static_cast< sal_uInt16 >( nListPos ) );
449 void XclExpPCField::InsertOrigItem( XclExpPCItem* pNewItem )
451 size_t nItemIdx = maOrigItemList.GetSize();
452 maOrigItemList.AppendNewRecord( pNewItem );
453 InsertItemArrayIndex( nItemIdx );
454 mnTypeFlags |= pNewItem->GetTypeFlag();
457 void XclExpPCField::InsertOrigTextItem( const OUString& rText )
459 size_t nPos = 0;
460 bool bFound = false;
461 // #i76047# maximum item text length in pivot cache is 255
462 OUString aShortText = rText.copy( 0, ::std::min(rText.getLength(), EXC_PC_MAXSTRLEN ) );
463 for( size_t nSize = maOrigItemList.GetSize(); !bFound && (nPos < nSize); ++nPos )
464 if( (bFound = maOrigItemList.GetRecord( nPos )->EqualsText( aShortText )) )
465 InsertItemArrayIndex( nPos );
466 if( !bFound )
467 InsertOrigItem( new XclExpPCItem( aShortText ) );
470 void XclExpPCField::InsertOrigDoubleItem( double fValue, const OUString& rText )
472 size_t nPos = 0;
473 bool bFound = false;
474 for( size_t nSize = maOrigItemList.GetSize(); !bFound && (nPos < nSize); ++nPos )
475 if( (bFound = maOrigItemList.GetRecord( nPos )->EqualsDouble( fValue )) )
476 InsertItemArrayIndex( nPos );
477 if( !bFound )
478 InsertOrigItem( new XclExpPCItem( fValue, rText ) );
481 void XclExpPCField::InsertOrigDateTimeItem( const DateTime& rDateTime, const OUString& rText )
483 size_t nPos = 0;
484 bool bFound = false;
485 for( size_t nSize = maOrigItemList.GetSize(); !bFound && (nPos < nSize); ++nPos )
486 if( (bFound = maOrigItemList.GetRecord( nPos )->EqualsDateTime( rDateTime )) )
487 InsertItemArrayIndex( nPos );
488 if( !bFound )
489 InsertOrigItem( new XclExpPCItem( rDateTime, rText ) );
492 void XclExpPCField::InsertOrigBoolItem( bool bValue, const OUString& rText )
494 size_t nPos = 0;
495 bool bFound = false;
496 for( size_t nSize = maOrigItemList.GetSize(); !bFound && (nPos < nSize); ++nPos )
497 if( (bFound = maOrigItemList.GetRecord( nPos )->EqualsBool( bValue )) )
498 InsertItemArrayIndex( nPos );
499 if( !bFound )
500 InsertOrigItem( new XclExpPCItem( bValue, rText ) );
503 sal_uInt16 XclExpPCField::InsertGroupItem( XclExpPCItem* pNewItem )
505 maGroupItemList.AppendNewRecord( pNewItem );
506 return static_cast< sal_uInt16 >( maGroupItemList.GetSize() - 1 );
509 void XclExpPCField::InsertNumDateGroupItems( const ScDPObject& rDPObj, const ScDPNumGroupInfo& rNumInfo, sal_Int32 nDatePart )
511 OSL_ENSURE( rDPObj.GetSheetDesc(), "XclExpPCField::InsertNumDateGroupItems - cannot generate element list" );
512 if( const ScSheetSourceDesc* pSrcDesc = rDPObj.GetSheetDesc() )
514 // get the string collection with original source elements
515 const ScDPSaveData* pSaveData = rDPObj.GetSaveData();
516 const ScDPDimensionSaveData* pDimData = nullptr;
517 if (pSaveData)
518 pDimData = pSaveData->GetExistingDimensionData();
520 const ScDPCache* pCache = pSrcDesc->CreateCache(pDimData);
521 if (!pCache)
522 return;
524 ScSheetDPData aDPData(&GetDoc(), *pSrcDesc, *pCache);
525 long nDim = GetFieldIndex();
526 // get the string collection with generated grouping elements
527 ScDPNumGroupDimension aTmpDim( rNumInfo );
528 if( nDatePart != 0 )
529 aTmpDim.SetDateDimension();
530 const std::vector<SCROW>& aMemberIds = aTmpDim.GetNumEntries(
531 static_cast<SCCOL>(nDim), pCache);
532 for (SCROW nMemberId : aMemberIds)
534 const ScDPItemData* pData = aDPData.GetMemberById(nDim, nMemberId);
535 if ( pData )
537 OUString aStr = pCache->GetFormattedString(nDim, *pData, false);
538 InsertGroupItem(new XclExpPCItem(aStr));
544 void XclExpPCField::SetNumGroupLimit( const ScDPNumGroupInfo& rNumInfo )
546 ::set_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMIN, rNumInfo.mbAutoStart );
547 ::set_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMAX, rNumInfo.mbAutoEnd );
548 maNumGroupLimits.AppendNewRecord( new XclExpPCItem( rNumInfo.mfStart ) );
549 maNumGroupLimits.AppendNewRecord( new XclExpPCItem( rNumInfo.mfEnd ) );
550 maNumGroupLimits.AppendNewRecord( new XclExpPCItem( rNumInfo.mfStep ) );
553 void XclExpPCField::SetDateGroupLimit( const ScDPNumGroupInfo& rDateInfo, bool bUseStep )
555 ::set_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMIN, rDateInfo.mbAutoStart );
556 ::set_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMAX, rDateInfo.mbAutoEnd );
557 maNumGroupLimits.AppendNewRecord( new XclExpPCItem( GetDateTimeFromDouble( rDateInfo.mfStart ) ) );
558 maNumGroupLimits.AppendNewRecord( new XclExpPCItem( GetDateTimeFromDouble( rDateInfo.mfEnd ) ) );
559 sal_Int16 nStep = bUseStep ? limit_cast< sal_Int16 >( rDateInfo.mfStep, 1, SAL_MAX_INT16 ) : 1;
560 maNumGroupLimits.AppendNewRecord( new XclExpPCItem( nStep ) );
563 void XclExpPCField::Finalize()
565 // flags
566 ::set_flag( maFieldInfo.mnFlags, EXC_SXFIELD_HASITEMS, !GetVisItemList().IsEmpty() );
567 // Excel writes long indexes even for 0x0100 items (indexes from 0x00 to 0xFF)
568 ::set_flag( maFieldInfo.mnFlags, EXC_SXFIELD_16BIT, maOrigItemList.GetSize() >= 0x0100 );
569 ::set_flag( maFieldInfo.mnFlags, EXC_SXFIELD_NUMGROUP, IsNumGroupField() || IsDateGroupField() );
570 /* mnTypeFlags is updated in all Insert***Item() functions. Now the flags
571 for the current combination of item types is added to the flags. */
572 ::set_flag( maFieldInfo.mnFlags, spnPCItemFlags[ mnTypeFlags ] );
574 // item count fields
575 maFieldInfo.mnVisItems = static_cast< sal_uInt16 >( GetVisItemList().GetSize() );
576 maFieldInfo.mnGroupItems = static_cast< sal_uInt16 >( maGroupItemList.GetSize() );
577 // maFieldInfo.mnBaseItems set in InitStdGroupField()
578 maFieldInfo.mnOrigItems = static_cast< sal_uInt16 >( maOrigItemList.GetSize() );
581 void XclExpPCField::WriteSxnumgroup( XclExpStream& rStrm )
583 if( IsNumGroupField() || IsDateGroupField() )
585 // SXNUMGROUP record
586 rStrm.StartRecord( EXC_ID_SXNUMGROUP, 2 );
587 rStrm << maNumGroupInfo;
588 rStrm.EndRecord();
590 // limits (min/max/step) for numeric grouping
591 OSL_ENSURE( maNumGroupLimits.GetSize() == 3,
592 "XclExpPCField::WriteSxnumgroup - missing numeric grouping limits" );
593 maNumGroupLimits.Save( rStrm );
597 void XclExpPCField::WriteSxgroupinfo( XclExpStream& rStrm )
599 OSL_ENSURE( IsStdGroupField() != maGroupOrder.empty(),
600 "XclExpPCField::WriteSxgroupinfo - missing grouping info" );
601 if( IsStdGroupField() && !maGroupOrder.empty() )
603 rStrm.StartRecord( EXC_ID_SXGROUPINFO, 2 * maGroupOrder.size() );
604 for( const auto& rItem : maGroupOrder )
605 rStrm << rItem;
606 rStrm.EndRecord();
610 void XclExpPCField::WriteBody( XclExpStream& rStrm )
612 rStrm << maFieldInfo;
615 XclExpPivotCache::XclExpPivotCache( const XclExpRoot& rRoot, const ScDPObject& rDPObj, sal_uInt16 nListIdx ) :
616 XclExpRoot( rRoot ),
617 mnListIdx( nListIdx ),
618 mbValid( false )
620 // source from sheet only
621 if( const ScSheetSourceDesc* pSrcDesc = rDPObj.GetSheetDesc() )
623 /* maOrigSrcRange: Range received from the DataPilot object.
624 maExpSrcRange: Range written to the DCONREF record.
625 maDocSrcRange: Range used to get source data from Calc document.
626 This range may be shorter than maExpSrcRange to improve export
627 performance (#i22541#). */
628 maOrigSrcRange = maExpSrcRange = maDocSrcRange = pSrcDesc->GetSourceRange();
629 maSrcRangeName = pSrcDesc->GetRangeName();
631 // internal sheet data only
632 SCTAB nScTab = maExpSrcRange.aStart.Tab();
633 if( (nScTab == maExpSrcRange.aEnd.Tab()) && GetTabInfo().IsExportTab( nScTab ) )
635 // ValidateRange() restricts source range to valid Excel limits
636 if( GetAddressConverter().ValidateRange( maExpSrcRange, true ) )
638 // #i22541# skip empty cell areas (performance)
639 SCCOL nDocCol1, nDocCol2;
640 SCROW nDocRow1, nDocRow2;
641 GetDoc().GetDataStart( nScTab, nDocCol1, nDocRow1 );
642 GetDoc().GetPrintArea( nScTab, nDocCol2, nDocRow2, false );
643 SCCOL nSrcCol1 = maExpSrcRange.aStart.Col();
644 SCROW nSrcRow1 = maExpSrcRange.aStart.Row();
645 SCCOL nSrcCol2 = maExpSrcRange.aEnd.Col();
646 SCROW nSrcRow2 = maExpSrcRange.aEnd.Row();
648 // #i22541# do not store index list for too big ranges
649 if( 2 * (nDocRow2 - nDocRow1) < (nSrcRow2 - nSrcRow1) )
650 ::set_flag( maPCInfo.mnFlags, EXC_SXDB_SAVEDATA, false );
652 // adjust row indexes, keep one row of empty area to surely have the empty cache item
653 if( nSrcRow1 < nDocRow1 )
654 nSrcRow1 = nDocRow1 - 1;
655 if( nSrcRow2 > nDocRow2 )
656 nSrcRow2 = nDocRow2 + 1;
658 maDocSrcRange.aStart.SetCol( ::std::max( nDocCol1, nSrcCol1 ) );
659 maDocSrcRange.aStart.SetRow( nSrcRow1 );
660 maDocSrcRange.aEnd.SetCol( ::std::min( nDocCol2, nSrcCol2 ) );
661 maDocSrcRange.aEnd.SetRow( nSrcRow2 );
663 GetDoc().GetName( nScTab, maTabName );
664 maPCInfo.mnSrcRecs = static_cast< sal_uInt32 >( maExpSrcRange.aEnd.Row() - maExpSrcRange.aStart.Row() );
665 maPCInfo.mnStrmId = nListIdx + 1;
666 maPCInfo.mnSrcType = EXC_SXDB_SRC_SHEET;
668 AddFields( rDPObj );
670 mbValid = true;
676 bool XclExpPivotCache::HasItemIndexList() const
678 return ::get_flag( maPCInfo.mnFlags, EXC_SXDB_SAVEDATA );
681 sal_uInt16 XclExpPivotCache::GetFieldCount() const
683 return static_cast< sal_uInt16 >( maFieldList.GetSize() );
686 const XclExpPCField* XclExpPivotCache::GetField( sal_uInt16 nFieldIdx ) const
688 return maFieldList.GetRecord( nFieldIdx ).get();
691 bool XclExpPivotCache::HasAddFields() const
693 // pivot cache can be shared, if there are no additional cache fields
694 return maPCInfo.mnStdFields < maPCInfo.mnTotalFields;
697 bool XclExpPivotCache::HasEqualDataSource( const ScDPObject& rDPObj ) const
699 /* For now, only sheet sources are supported, therefore it is enough to
700 compare the ScSheetSourceDesc. Later, there should be done more complicated
701 comparisons regarding the source type of rDPObj and this cache. */
702 if( const ScSheetSourceDesc* pSrcDesc = rDPObj.GetSheetDesc() )
703 return pSrcDesc->GetSourceRange() == maOrigSrcRange;
704 return false;
707 void XclExpPivotCache::Save( XclExpStream& rStrm )
709 OSL_ENSURE( mbValid, "XclExpPivotCache::Save - invalid pivot cache" );
710 // SXIDSTM
711 XclExpUInt16Record( EXC_ID_SXIDSTM, maPCInfo.mnStrmId ).Save( rStrm );
712 // SXVS
713 XclExpUInt16Record( EXC_ID_SXVS, EXC_SXVS_SHEET ).Save( rStrm );
715 if (!maSrcRangeName.isEmpty())
716 // DCONNAME
717 WriteDConName(rStrm);
718 else
719 // DCONREF
720 WriteDconref(rStrm);
722 // create the pivot cache storage stream
723 WriteCacheStream();
726 void XclExpPivotCache::SaveXml( XclExpXmlStream& /*rStrm*/ )
730 void XclExpPivotCache::AddFields( const ScDPObject& rDPObj )
732 AddStdFields( rDPObj );
733 maPCInfo.mnStdFields = GetFieldCount();
734 AddGroupFields( rDPObj );
735 maPCInfo.mnTotalFields = GetFieldCount();
738 void XclExpPivotCache::AddStdFields( const ScDPObject& rDPObj )
740 // if item index list is not written, used shortened source range (maDocSrcRange) for performance
741 const ScRange& rRange = HasItemIndexList() ? maExpSrcRange : maDocSrcRange;
742 // create a standard pivot cache field for each source column
743 for( SCCOL nScCol = rRange.aStart.Col(), nEndScCol = rRange.aEnd.Col(); nScCol <= nEndScCol; ++nScCol )
745 ScRange aColRange( rRange );
746 aColRange.aStart.SetCol( nScCol );
747 aColRange.aEnd.SetCol( nScCol );
748 maFieldList.AppendNewRecord( new XclExpPCField(
749 GetRoot(), GetFieldCount(), rDPObj, aColRange ) );
753 void XclExpPivotCache::AddGroupFields( const ScDPObject& rDPObj )
755 if( const ScDPSaveData* pSaveData = rDPObj.GetSaveData() )
757 if( const ScDPDimensionSaveData* pSaveDimData = pSaveData->GetExistingDimensionData() )
759 // loop over all existing standard fields to find their group fields
760 for( sal_uInt16 nFieldIdx = 0; nFieldIdx < maPCInfo.mnStdFields; ++nFieldIdx )
762 if( XclExpPCField* pCurrStdField = maFieldList.GetRecord( nFieldIdx ).get() )
764 const ScDPSaveGroupDimension* pGroupDim = pSaveDimData->GetGroupDimForBase( pCurrStdField->GetFieldName() );
765 XclExpPCField* pLastGroupField = pCurrStdField;
766 while( pGroupDim )
768 // insert the new grouping field
769 XclExpPCFieldRef xNewGroupField( new XclExpPCField(
770 GetRoot(), GetFieldCount(), rDPObj, *pGroupDim, *pCurrStdField ) );
771 maFieldList.AppendRecord( xNewGroupField );
773 // register new grouping field at current grouping field, building a chain
774 pLastGroupField->SetGroupChildField( *xNewGroupField );
776 // next grouping dimension
777 pGroupDim = pSaveDimData->GetGroupDimForBase( pGroupDim->GetGroupDimName() );
778 pLastGroupField = xNewGroupField.get();
786 void XclExpPivotCache::WriteDconref( XclExpStream& rStrm ) const
788 XclExpString aRef( XclExpUrlHelper::EncodeUrl( GetRoot(), EMPTY_OUSTRING, &maTabName ) );
789 rStrm.StartRecord( EXC_ID_DCONREF, 7 + aRef.GetSize() );
790 rStrm << static_cast< sal_uInt16 >( maExpSrcRange.aStart.Row() )
791 << static_cast< sal_uInt16 >( maExpSrcRange.aEnd.Row() )
792 << static_cast< sal_uInt8 >( maExpSrcRange.aStart.Col() )
793 << static_cast< sal_uInt8 >( maExpSrcRange.aEnd.Col() )
794 << aRef
795 << sal_uInt8( 0 );
796 rStrm.EndRecord();
799 void XclExpPivotCache::WriteDConName( XclExpStream& rStrm ) const
801 XclExpString aName(maSrcRangeName);
802 rStrm.StartRecord(EXC_ID_DCONNAME, aName.GetSize() + 2);
803 rStrm << aName << sal_uInt16(0);
804 rStrm.EndRecord();
807 void XclExpPivotCache::WriteCacheStream()
809 tools::SvRef<SotStorage> xSvStrg = OpenStorage( EXC_STORAGE_PTCACHE );
810 tools::SvRef<SotStorageStream> xSvStrm = OpenStream( xSvStrg, ScfTools::GetHexStr( maPCInfo.mnStrmId ) );
811 if( xSvStrm.is() )
813 XclExpStream aStrm( *xSvStrm, GetRoot() );
814 // SXDB
815 WriteSxdb( aStrm );
816 // SXDBEX
817 WriteSxdbex( aStrm );
818 // field list (SXFIELD and items)
819 maFieldList.Save( aStrm );
820 // index table (list of SXINDEXLIST)
821 WriteSxindexlistList( aStrm );
822 // EOF
823 XclExpEmptyRecord( EXC_ID_EOF ).Save( aStrm );
827 void XclExpPivotCache::WriteSxdb( XclExpStream& rStrm ) const
829 rStrm.StartRecord( EXC_ID_SXDB, 21 );
830 rStrm << maPCInfo;
831 rStrm.EndRecord();
834 void XclExpPivotCache::WriteSxdbex( XclExpStream& rStrm )
836 rStrm.StartRecord( EXC_ID_SXDBEX, 12 );
837 rStrm << EXC_SXDBEX_CREATION_DATE
838 << sal_uInt32( 0 ); // number of SXFORMULA records
839 rStrm.EndRecord();
842 void XclExpPivotCache::WriteSxindexlistList( XclExpStream& rStrm ) const
844 if( HasItemIndexList() )
846 std::size_t nRecSize = 0;
847 size_t nPos, nSize = maFieldList.GetSize();
848 for( nPos = 0; nPos < nSize; ++nPos )
849 nRecSize += maFieldList.GetRecord( nPos )->GetIndexSize();
851 for( sal_uInt32 nSrcRow = 0; nSrcRow < maPCInfo.mnSrcRecs; ++nSrcRow )
853 rStrm.StartRecord( EXC_ID_SXINDEXLIST, nRecSize );
854 for( nPos = 0; nPos < nSize; ++nPos )
855 maFieldList.GetRecord( nPos )->WriteIndex( rStrm, nSrcRow );
856 rStrm.EndRecord();
861 // Pivot table
863 namespace {
865 /** Returns a display string for a data field containing the field name and aggregation function. */
866 OUString lclGetDataFieldCaption( const OUString& rFieldName, ScGeneralFunction eFunc )
868 OUString aCaption;
870 const char* pResIdx = nullptr;
871 switch( eFunc )
873 case ScGeneralFunction::SUM: pResIdx = STR_FUN_TEXT_SUM; break;
874 case ScGeneralFunction::COUNT: pResIdx = STR_FUN_TEXT_COUNT; break;
875 case ScGeneralFunction::AVERAGE: pResIdx = STR_FUN_TEXT_AVG; break;
876 case ScGeneralFunction::MAX: pResIdx = STR_FUN_TEXT_MAX; break;
877 case ScGeneralFunction::MIN: pResIdx = STR_FUN_TEXT_MIN; break;
878 case ScGeneralFunction::PRODUCT: pResIdx = STR_FUN_TEXT_PRODUCT; break;
879 case ScGeneralFunction::COUNTNUMS: pResIdx = STR_FUN_TEXT_COUNT; break;
880 case ScGeneralFunction::STDEV: pResIdx = STR_FUN_TEXT_STDDEV; break;
881 case ScGeneralFunction::STDEVP: pResIdx = STR_FUN_TEXT_STDDEV; break;
882 case ScGeneralFunction::VAR: pResIdx = STR_FUN_TEXT_VAR; break;
883 case ScGeneralFunction::VARP: pResIdx = STR_FUN_TEXT_VAR; break;
884 default:;
886 if (pResIdx)
887 aCaption = ScResId(pResIdx) + " - ";
888 aCaption += rFieldName;
889 return aCaption;
892 } // namespace
894 XclExpPTItem::XclExpPTItem( const XclExpPCField& rCacheField, sal_uInt16 nCacheIdx ) :
895 XclExpRecord( EXC_ID_SXVI, 8 ),
896 mpCacheItem( rCacheField.GetItem( nCacheIdx ) )
898 maItemInfo.mnType = EXC_SXVI_TYPE_DATA;
899 maItemInfo.mnCacheIdx = nCacheIdx;
900 maItemInfo.maVisName.mbUseCache = mpCacheItem != nullptr;
903 XclExpPTItem::XclExpPTItem( sal_uInt16 nItemType, sal_uInt16 nCacheIdx ) :
904 XclExpRecord( EXC_ID_SXVI, 8 ),
905 mpCacheItem( nullptr )
907 maItemInfo.mnType = nItemType;
908 maItemInfo.mnCacheIdx = nCacheIdx;
909 maItemInfo.maVisName.mbUseCache = true;
912 OUString XclExpPTItem::GetItemName() const
914 return mpCacheItem ? mpCacheItem->ConvertToText() : OUString();
917 void XclExpPTItem::SetPropertiesFromMember( const ScDPSaveMember& rSaveMem )
919 // #i115659# GetIsVisible() is not valid if HasIsVisible() returns false, default is 'visible' then
920 ::set_flag( maItemInfo.mnFlags, EXC_SXVI_HIDDEN, rSaveMem.HasIsVisible() && !rSaveMem.GetIsVisible() );
921 // #i115659# GetShowDetails() is not valid if HasShowDetails() returns false, default is 'show detail' then
922 ::set_flag( maItemInfo.mnFlags, EXC_SXVI_HIDEDETAIL, rSaveMem.HasShowDetails() && !rSaveMem.GetShowDetails() );
924 // visible name
925 const boost::optional<OUString> & pVisName = rSaveMem.GetLayoutName();
926 if (pVisName && *pVisName != GetItemName())
927 maItemInfo.SetVisName(*pVisName);
930 void XclExpPTItem::WriteBody( XclExpStream& rStrm )
932 rStrm << maItemInfo;
935 XclExpPTField::XclExpPTField( const XclExpPivotTable& rPTable, sal_uInt16 nCacheIdx ) :
936 mrPTable( rPTable ),
937 mpCacheField( rPTable.GetCacheField( nCacheIdx ) )
939 maFieldInfo.mnCacheIdx = nCacheIdx;
941 // create field items
942 if( mpCacheField )
943 for( sal_uInt16 nItemIdx = 0, nItemCount = mpCacheField->GetItemCount(); nItemIdx < nItemCount; ++nItemIdx )
944 maItemList.AppendNewRecord( new XclExpPTItem( *mpCacheField, nItemIdx ) );
945 maFieldInfo.mnItemCount = static_cast< sal_uInt16 >( maItemList.GetSize() );
948 // data access ----------------------------------------------------------------
950 OUString XclExpPTField::GetFieldName() const
952 return mpCacheField ? mpCacheField->GetFieldName() : OUString();
955 sal_uInt16 XclExpPTField::GetLastDataInfoIndex() const
957 OSL_ENSURE( !maDataInfoVec.empty(), "XclExpPTField::GetLastDataInfoIndex - no data info found" );
958 // will return 0xFFFF for empty vector -> ok
959 return static_cast< sal_uInt16 >( maDataInfoVec.size() - 1 );
962 sal_uInt16 XclExpPTField::GetItemIndex( const OUString& rName, sal_uInt16 nDefaultIdx ) const
964 for( size_t nPos = 0, nSize = maItemList.GetSize(); nPos < nSize; ++nPos )
965 if( maItemList.GetRecord( nPos )->GetItemName() == rName )
966 return static_cast< sal_uInt16 >( nPos );
967 return nDefaultIdx;
970 // fill data --------------------------------------------------------------
973 * Calc's subtotal names are escaped with backslashes ('\'), while Excel's
974 * are not escaped at all.
976 static OUString lcl_convertCalcSubtotalName(const OUString& rName)
978 OUStringBuffer aBuf;
979 const sal_Unicode* p = rName.getStr();
980 sal_Int32 n = rName.getLength();
981 bool bEscaped = false;
982 for (sal_Int32 i = 0; i < n; ++i)
984 const sal_Unicode c = p[i];
985 if (!bEscaped && c == '\\')
987 bEscaped = true;
988 continue;
991 aBuf.append(c);
992 bEscaped = false;
994 return aBuf.makeStringAndClear();
997 void XclExpPTField::SetPropertiesFromDim( const ScDPSaveDimension& rSaveDim )
999 // orientation
1000 DataPilotFieldOrientation eOrient = rSaveDim.GetOrientation();
1001 OSL_ENSURE( eOrient != DataPilotFieldOrientation_DATA, "XclExpPTField::SetPropertiesFromDim - called for data field" );
1002 maFieldInfo.AddApiOrient( eOrient );
1004 // show empty items (#i115659# GetShowEmpty() is not valid if HasShowEmpty() returns false, default is false then)
1005 ::set_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_SHOWALL, rSaveDim.HasShowEmpty() && rSaveDim.GetShowEmpty() );
1007 // visible name
1008 const boost::optional<OUString> & pLayoutName = rSaveDim.GetLayoutName();
1009 if (pLayoutName && *pLayoutName != GetFieldName())
1010 maFieldInfo.SetVisName(*pLayoutName);
1012 const boost::optional<OUString> & pSubtotalName = rSaveDim.GetSubtotalName();
1013 if (pSubtotalName)
1015 OUString aSubName = lcl_convertCalcSubtotalName(*pSubtotalName);
1016 maFieldExtInfo.mpFieldTotalName = aSubName;
1019 // subtotals
1020 XclPTSubtotalVec aSubtotals;
1021 aSubtotals.reserve( static_cast< size_t >( rSaveDim.GetSubTotalsCount() ) );
1022 for( long nSubtIdx = 0, nSubtCount = rSaveDim.GetSubTotalsCount(); nSubtIdx < nSubtCount; ++nSubtIdx )
1023 aSubtotals.push_back( rSaveDim.GetSubTotalFunc( nSubtIdx ) );
1024 maFieldInfo.SetSubtotals( aSubtotals );
1026 // sorting
1027 if( const DataPilotFieldSortInfo* pSortInfo = rSaveDim.GetSortInfo() )
1029 maFieldExtInfo.SetApiSortMode( pSortInfo->Mode );
1030 if( pSortInfo->Mode == css::sheet::DataPilotFieldSortMode::DATA )
1031 maFieldExtInfo.mnSortField = mrPTable.GetDataFieldIndex( pSortInfo->Field, EXC_SXVDEX_SORT_OWN );
1032 ::set_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_SORT_ASC, pSortInfo->IsAscending );
1035 // auto show
1036 if( const DataPilotFieldAutoShowInfo* pShowInfo = rSaveDim.GetAutoShowInfo() )
1038 ::set_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_AUTOSHOW, pShowInfo->IsEnabled );
1039 maFieldExtInfo.SetApiAutoShowMode( pShowInfo->ShowItemsMode );
1040 maFieldExtInfo.SetApiAutoShowCount( pShowInfo->ItemCount );
1041 maFieldExtInfo.mnShowField = mrPTable.GetDataFieldIndex( pShowInfo->DataField, EXC_SXVDEX_SHOW_NONE );
1044 // layout
1045 if( const DataPilotFieldLayoutInfo* pLayoutInfo = rSaveDim.GetLayoutInfo() )
1047 maFieldExtInfo.SetApiLayoutMode( pLayoutInfo->LayoutMode );
1048 ::set_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_LAYOUT_BLANK, pLayoutInfo->AddEmptyLines );
1051 // special page field properties
1052 if( eOrient == DataPilotFieldOrientation_PAGE )
1054 maPageInfo.mnField = GetFieldIndex();
1055 maPageInfo.mnSelItem = EXC_SXPI_ALLITEMS;
1058 // item properties
1059 const ScDPSaveDimension::MemberList &rMembers = rSaveDim.GetMembers();
1060 for (const auto& pMember : rMembers)
1061 if( XclExpPTItem* pItem = GetItemAcc( pMember->GetName() ) )
1062 pItem->SetPropertiesFromMember( *pMember );
1065 void XclExpPTField::SetDataPropertiesFromDim( const ScDPSaveDimension& rSaveDim )
1067 maDataInfoVec.emplace_back( );
1068 XclPTDataFieldInfo& rDataInfo = maDataInfoVec.back();
1069 rDataInfo.mnField = GetFieldIndex();
1071 // orientation
1072 maFieldInfo.AddApiOrient( DataPilotFieldOrientation_DATA );
1074 // aggregation function
1075 ScGeneralFunction eFunc = rSaveDim.GetFunction();
1076 rDataInfo.SetApiAggFunc( eFunc );
1078 // visible name
1079 const boost::optional<OUString> & pVisName = rSaveDim.GetLayoutName();
1080 if (pVisName)
1081 rDataInfo.SetVisName(*pVisName);
1082 else
1083 rDataInfo.SetVisName( lclGetDataFieldCaption( GetFieldName(), eFunc ) );
1085 // result field reference
1086 if( const DataPilotFieldReference* pFieldRef = rSaveDim.GetReferenceValue() )
1088 rDataInfo.SetApiRefType( pFieldRef->ReferenceType );
1089 rDataInfo.SetApiRefItemType( pFieldRef->ReferenceItemType );
1090 if( const XclExpPTField* pRefField = mrPTable.GetField( pFieldRef->ReferenceField ) )
1092 rDataInfo.mnRefField = pRefField->GetFieldIndex();
1093 if( pFieldRef->ReferenceItemType == css::sheet::DataPilotFieldReferenceItemType::NAMED )
1094 rDataInfo.mnRefItem = pRefField->GetItemIndex( pFieldRef->ReferenceItemName, 0 );
1099 void XclExpPTField::AppendSubtotalItems()
1101 if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_DEFAULT ) AppendSubtotalItem( EXC_SXVI_TYPE_DEFAULT );
1102 if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_SUM ) AppendSubtotalItem( EXC_SXVI_TYPE_SUM );
1103 if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_COUNT ) AppendSubtotalItem( EXC_SXVI_TYPE_COUNT );
1104 if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_AVERAGE ) AppendSubtotalItem( EXC_SXVI_TYPE_AVERAGE );
1105 if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_MAX ) AppendSubtotalItem( EXC_SXVI_TYPE_MAX );
1106 if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_MIN ) AppendSubtotalItem( EXC_SXVI_TYPE_MIN );
1107 if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_PROD ) AppendSubtotalItem( EXC_SXVI_TYPE_PROD );
1108 if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_COUNTNUM ) AppendSubtotalItem( EXC_SXVI_TYPE_COUNTNUM );
1109 if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_STDDEV ) AppendSubtotalItem( EXC_SXVI_TYPE_STDDEV );
1110 if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_STDDEVP ) AppendSubtotalItem( EXC_SXVI_TYPE_STDDEVP );
1111 if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_VAR ) AppendSubtotalItem( EXC_SXVI_TYPE_VAR );
1112 if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_VARP ) AppendSubtotalItem( EXC_SXVI_TYPE_VARP );
1115 // records --------------------------------------------------------------------
1117 void XclExpPTField::WriteSxpiEntry( XclExpStream& rStrm ) const
1119 rStrm << maPageInfo;
1122 void XclExpPTField::WriteSxdi( XclExpStream& rStrm, sal_uInt16 nDataInfoIdx ) const
1124 OSL_ENSURE( nDataInfoIdx < maDataInfoVec.size(), "XclExpPTField::WriteSxdi - data field not found" );
1125 if( nDataInfoIdx < maDataInfoVec.size() )
1127 rStrm.StartRecord( EXC_ID_SXDI, 12 );
1128 rStrm << maDataInfoVec[ nDataInfoIdx ];
1129 rStrm.EndRecord();
1133 void XclExpPTField::Save( XclExpStream& rStrm )
1135 // SXVD
1136 WriteSxvd( rStrm );
1137 // list of SXVI records
1138 maItemList.Save( rStrm );
1139 // SXVDEX
1140 WriteSxvdex( rStrm );
1143 // private --------------------------------------------------------------------
1145 XclExpPTItem* XclExpPTField::GetItemAcc( const OUString& rName )
1147 XclExpPTItem* pItem = nullptr;
1148 for( size_t nPos = 0, nSize = maItemList.GetSize(); !pItem && (nPos < nSize); ++nPos )
1149 if( maItemList.GetRecord( nPos )->GetItemName() == rName )
1150 pItem = maItemList.GetRecord( nPos ).get();
1151 return pItem;
1154 void XclExpPTField::AppendSubtotalItem( sal_uInt16 nItemType )
1156 maItemList.AppendNewRecord( new XclExpPTItem( nItemType, EXC_SXVI_DEFAULT_CACHE ) );
1157 ++maFieldInfo.mnItemCount;
1160 void XclExpPTField::WriteSxvd( XclExpStream& rStrm ) const
1162 rStrm.StartRecord( EXC_ID_SXVD, 10 );
1163 rStrm << maFieldInfo;
1164 rStrm.EndRecord();
1167 void XclExpPTField::WriteSxvdex( XclExpStream& rStrm ) const
1169 rStrm.StartRecord( EXC_ID_SXVDEX, 20 );
1170 rStrm << maFieldExtInfo;
1171 rStrm.EndRecord();
1174 XclExpPivotTable::XclExpPivotTable( const XclExpRoot& rRoot, const ScDPObject& rDPObj, const XclExpPivotCache& rPCache ) :
1175 XclExpRoot( rRoot ),
1176 mrPCache( rPCache ),
1177 maDataOrientField( *this, EXC_SXIVD_DATA ),
1178 mnOutScTab( 0 ),
1179 mbValid( false ),
1180 mbFilterBtn( false )
1182 const ScRange& rOutScRange = rDPObj.GetOutRange();
1183 if( GetAddressConverter().ConvertRange( maPTInfo.maOutXclRange, rOutScRange, true ) )
1185 // DataPilot properties -----------------------------------------------
1187 // pivot table properties from DP object
1188 mnOutScTab = rOutScRange.aStart.Tab();
1189 maPTInfo.maTableName = rDPObj.GetName();
1190 maPTInfo.mnCacheIdx = mrPCache.GetCacheIndex();
1192 maPTViewEx9Info.Init( rDPObj );
1194 if( const ScDPSaveData* pSaveData = rDPObj.GetSaveData() )
1196 // additional properties from ScDPSaveData
1197 SetPropertiesFromDP( *pSaveData );
1199 // loop over all dimensions ---------------------------------------
1201 /* 1) Default-construct all pivot table fields for all pivot cache fields. */
1202 for( sal_uInt16 nFieldIdx = 0, nFieldCount = mrPCache.GetFieldCount(); nFieldIdx < nFieldCount; ++nFieldIdx )
1203 maFieldList.AppendNewRecord( new XclExpPTField( *this, nFieldIdx ) );
1205 const ScDPSaveData::DimsType& rDimList = pSaveData->GetDimensions();
1207 /* 2) First process all data dimensions, they are needed for extended
1208 settings of row/column/page fields (sorting/auto show). */
1209 for (auto const& iter : rDimList)
1211 if (iter->GetOrientation() == DataPilotFieldOrientation_DATA)
1212 SetDataFieldPropertiesFromDim(*iter);
1215 /* 3) Row/column/page/hidden fields. */
1216 for (auto const& iter : rDimList)
1218 if (iter->GetOrientation() != DataPilotFieldOrientation_DATA)
1219 SetFieldPropertiesFromDim(*iter);
1222 // Finalize -------------------------------------------------------
1224 Finalize();
1225 mbValid = true;
1230 const XclExpPCField* XclExpPivotTable::GetCacheField( sal_uInt16 nCacheIdx ) const
1232 return mrPCache.GetField( nCacheIdx );
1235 const XclExpPTField* XclExpPivotTable::GetField( sal_uInt16 nFieldIdx ) const
1237 return (nFieldIdx == EXC_SXIVD_DATA) ? &maDataOrientField : maFieldList.GetRecord( nFieldIdx ).get();
1240 const XclExpPTField* XclExpPivotTable::GetField( const OUString& rName ) const
1242 return const_cast< XclExpPivotTable* >( this )->GetFieldAcc( rName );
1245 sal_uInt16 XclExpPivotTable::GetDataFieldIndex( const OUString& rName, sal_uInt16 nDefaultIdx ) const
1247 auto aIt = std::find_if(maDataFields.begin(), maDataFields.end(),
1248 [this, &rName](const XclPTDataFieldPos& rDataField) {
1249 const XclExpPTField* pField = GetField( rDataField.first );
1250 return pField && pField->GetFieldName() == rName;
1252 if (aIt != maDataFields.end())
1253 return static_cast< sal_uInt16 >( std::distance(maDataFields.begin(), aIt) );
1254 return nDefaultIdx;
1257 void XclExpPivotTable::Save( XclExpStream& rStrm )
1259 if( mbValid )
1261 // SXVIEW
1262 WriteSxview( rStrm );
1263 // pivot table fields (SXVD, SXVDEX, and item records)
1264 maFieldList.Save( rStrm );
1265 // SXIVD records for row and column fields
1266 WriteSxivd( rStrm, maRowFields );
1267 WriteSxivd( rStrm, maColFields );
1268 // SXPI
1269 WriteSxpi( rStrm );
1270 // list of SXDI records containing data field info
1271 WriteSxdiList( rStrm );
1272 // SXLI records
1273 WriteSxli( rStrm, maPTInfo.mnDataRows, maPTInfo.mnRowFields );
1274 WriteSxli( rStrm, maPTInfo.mnDataCols, maPTInfo.mnColFields );
1275 // SXEX
1276 WriteSxex( rStrm );
1277 // QSISXTAG
1278 WriteQsiSxTag( rStrm );
1279 // SXVIEWEX9
1280 WriteSxViewEx9( rStrm );
1284 XclExpPTField* XclExpPivotTable::GetFieldAcc( const OUString& rName )
1286 XclExpPTField* pField = nullptr;
1287 for( size_t nPos = 0, nSize = maFieldList.GetSize(); !pField && (nPos < nSize); ++nPos )
1288 if( maFieldList.GetRecord( nPos )->GetFieldName() == rName )
1289 pField = maFieldList.GetRecord( nPos ).get();
1290 return pField;
1293 XclExpPTField* XclExpPivotTable::GetFieldAcc( const ScDPSaveDimension& rSaveDim )
1295 // data field orientation field?
1296 if( rSaveDim.IsDataLayout() )
1297 return &maDataOrientField;
1299 // a real dimension
1300 OUString aFieldName = ScDPUtil::getSourceDimensionName(rSaveDim.GetName());
1301 return aFieldName.isEmpty() ? nullptr : GetFieldAcc(aFieldName);
1304 // fill data --------------------------------------------------------------
1306 void XclExpPivotTable::SetPropertiesFromDP( const ScDPSaveData& rSaveData )
1308 ::set_flag( maPTInfo.mnFlags, EXC_SXVIEW_ROWGRAND, rSaveData.GetRowGrand() );
1309 ::set_flag( maPTInfo.mnFlags, EXC_SXVIEW_COLGRAND, rSaveData.GetColumnGrand() );
1310 ::set_flag( maPTExtInfo.mnFlags, EXC_SXEX_DRILLDOWN, rSaveData.GetDrillDown() );
1311 mbFilterBtn = rSaveData.GetFilterButton();
1312 const ScDPSaveDimension* pDim = rSaveData.GetExistingDataLayoutDimension();
1314 if (pDim && pDim->GetLayoutName())
1315 maPTInfo.maDataName = *pDim->GetLayoutName();
1316 else
1317 maPTInfo.maDataName = ScResId(STR_PIVOT_DATA);
1320 void XclExpPivotTable::SetFieldPropertiesFromDim( const ScDPSaveDimension& rSaveDim )
1322 if( XclExpPTField* pField = GetFieldAcc( rSaveDim ) )
1324 // field properties
1325 pField->SetPropertiesFromDim( rSaveDim );
1327 // update the corresponding field position list
1328 DataPilotFieldOrientation eOrient = rSaveDim.GetOrientation();
1329 sal_uInt16 nFieldIdx = pField->GetFieldIndex();
1330 bool bDataLayout = nFieldIdx == EXC_SXIVD_DATA;
1331 bool bMultiData = maDataFields.size() > 1;
1333 if( !bDataLayout || bMultiData ) switch( eOrient )
1335 case DataPilotFieldOrientation_ROW:
1336 maRowFields.push_back( nFieldIdx );
1337 if( bDataLayout )
1338 maPTInfo.mnDataAxis = EXC_SXVD_AXIS_ROW;
1339 break;
1340 case DataPilotFieldOrientation_COLUMN:
1341 maColFields.push_back( nFieldIdx );
1342 if( bDataLayout )
1343 maPTInfo.mnDataAxis = EXC_SXVD_AXIS_COL;
1344 break;
1345 case DataPilotFieldOrientation_PAGE:
1346 maPageFields.push_back( nFieldIdx );
1347 OSL_ENSURE( !bDataLayout, "XclExpPivotTable::SetFieldPropertiesFromDim - wrong orientation for data fields" );
1348 break;
1349 case DataPilotFieldOrientation_DATA:
1350 OSL_FAIL( "XclExpPivotTable::SetFieldPropertiesFromDim - called for data field" );
1351 break;
1352 default:;
1357 void XclExpPivotTable::SetDataFieldPropertiesFromDim( const ScDPSaveDimension& rSaveDim )
1359 if( XclExpPTField* pField = GetFieldAcc( rSaveDim ) )
1361 // field properties
1362 pField->SetDataPropertiesFromDim( rSaveDim );
1363 // update the data field position list
1364 maDataFields.emplace_back( pField->GetFieldIndex(), pField->GetLastDataInfoIndex() );
1368 void XclExpPivotTable::Finalize()
1370 // field numbers
1371 maPTInfo.mnFields = static_cast< sal_uInt16 >( maFieldList.GetSize() );
1372 maPTInfo.mnRowFields = static_cast< sal_uInt16 >( maRowFields.size() );
1373 maPTInfo.mnColFields = static_cast< sal_uInt16 >( maColFields.size() );
1374 maPTInfo.mnPageFields = static_cast< sal_uInt16 >( maPageFields.size() );
1375 maPTInfo.mnDataFields = static_cast< sal_uInt16 >( maDataFields.size() );
1377 maPTExtInfo.mnPagePerRow = maPTInfo.mnPageFields;
1378 maPTExtInfo.mnPagePerCol = (maPTInfo.mnPageFields > 0) ? 1 : 0;
1380 // subtotal items
1381 for( size_t nPos = 0, nSize = maFieldList.GetSize(); nPos < nSize; ++nPos )
1382 maFieldList.GetRecord( nPos )->AppendSubtotalItems();
1384 // find data field orientation field
1385 maPTInfo.mnDataPos = EXC_SXVIEW_DATALAST;
1386 const ScfUInt16Vec* pFieldVec = nullptr;
1387 switch( maPTInfo.mnDataAxis )
1389 case EXC_SXVD_AXIS_ROW: pFieldVec = &maRowFields; break;
1390 case EXC_SXVD_AXIS_COL: pFieldVec = &maColFields; break;
1393 if( pFieldVec && !pFieldVec->empty() && (pFieldVec->back() != EXC_SXIVD_DATA) )
1395 ScfUInt16Vec::const_iterator aIt = ::std::find( pFieldVec->begin(), pFieldVec->end(), EXC_SXIVD_DATA );
1396 if( aIt != pFieldVec->end() )
1397 maPTInfo.mnDataPos = static_cast< sal_uInt16 >( std::distance(pFieldVec->begin(), aIt) );
1400 // single data field is always row oriented
1401 if( maPTInfo.mnDataAxis == EXC_SXVD_AXIS_NONE )
1402 maPTInfo.mnDataAxis = EXC_SXVD_AXIS_ROW;
1404 // update output range (initialized in ctor)
1405 sal_uInt16& rnXclCol1 = maPTInfo.maOutXclRange.maFirst.mnCol;
1406 sal_uInt32& rnXclRow1 = maPTInfo.maOutXclRange.maFirst.mnRow;
1407 sal_uInt16& rnXclCol2 = maPTInfo.maOutXclRange.maLast.mnCol;
1408 sal_uInt32& rnXclRow2 = maPTInfo.maOutXclRange.maLast.mnRow;
1409 // exclude page fields from output range
1410 rnXclRow1 = rnXclRow1 + maPTInfo.mnPageFields;
1411 // exclude filter button from output range
1412 if( mbFilterBtn )
1413 ++rnXclRow1;
1414 // exclude empty row between (filter button and/or page fields) and table
1415 if( mbFilterBtn || maPTInfo.mnPageFields )
1416 ++rnXclRow1;
1418 // data area
1419 sal_uInt16& rnDataXclCol = maPTInfo.maDataXclPos.mnCol;
1420 sal_uInt32& rnDataXclRow = maPTInfo.maDataXclPos.mnRow;
1421 rnDataXclCol = rnXclCol1 + maPTInfo.mnRowFields;
1422 rnDataXclRow = rnXclRow1 + maPTInfo.mnColFields + 1;
1423 if( maDataFields.empty() )
1424 ++rnDataXclRow;
1426 bool bExtraHeaderRow = (0 == maPTViewEx9Info.mnGridLayout && maPTInfo.mnColFields == 0);
1427 if (bExtraHeaderRow)
1428 // Insert an extra row only when there is no column field.
1429 ++rnDataXclRow;
1431 rnXclCol2 = ::std::max( rnXclCol2, rnDataXclCol );
1432 rnXclRow2 = ::std::max( rnXclRow2, rnDataXclRow );
1433 maPTInfo.mnDataCols = rnXclCol2 - rnDataXclCol + 1;
1434 maPTInfo.mnDataRows = rnXclRow2 - rnDataXclRow + 1;
1436 // first heading
1437 maPTInfo.mnFirstHeadRow = rnXclRow1 + 1;
1438 if (bExtraHeaderRow)
1439 maPTInfo.mnFirstHeadRow += 1;
1442 // records ----------------------------------------------------------------
1444 void XclExpPivotTable::WriteSxview( XclExpStream& rStrm ) const
1446 rStrm.StartRecord( EXC_ID_SXVIEW, 46 + maPTInfo.maTableName.getLength() + maPTInfo.maDataName.getLength() );
1447 rStrm << maPTInfo;
1448 rStrm.EndRecord();
1451 void XclExpPivotTable::WriteSxivd( XclExpStream& rStrm, const ScfUInt16Vec& rFields )
1453 if( !rFields.empty() )
1455 rStrm.StartRecord( EXC_ID_SXIVD, rFields.size() * 2 );
1456 for( const auto& rField : rFields )
1457 rStrm << rField;
1458 rStrm.EndRecord();
1462 void XclExpPivotTable::WriteSxpi( XclExpStream& rStrm ) const
1464 if( !maPageFields.empty() )
1466 rStrm.StartRecord( EXC_ID_SXPI, maPageFields.size() * 6 );
1467 rStrm.SetSliceSize( 6 );
1468 for( const auto& rPageField : maPageFields )
1470 XclExpPTFieldRef xField = maFieldList.GetRecord( rPageField );
1471 if( xField )
1472 xField->WriteSxpiEntry( rStrm );
1474 rStrm.EndRecord();
1478 void XclExpPivotTable::WriteSxdiList( XclExpStream& rStrm ) const
1480 for( const auto& [rFieldIdx, rDataInfoIdx] : maDataFields )
1482 XclExpPTFieldRef xField = maFieldList.GetRecord( rFieldIdx );
1483 if( xField )
1484 xField->WriteSxdi( rStrm, rDataInfoIdx );
1488 void XclExpPivotTable::WriteSxli( XclExpStream& rStrm, sal_uInt16 nLineCount, sal_uInt16 nIndexCount )
1490 if( nLineCount > 0 )
1492 std::size_t nLineSize = 8 + 2 * nIndexCount;
1493 rStrm.StartRecord( EXC_ID_SXLI, nLineSize * nLineCount );
1495 /* Excel expects the records to be filled completely, do not
1496 set a segment size... */
1497 // rStrm.SetSliceSize( nLineSize );
1499 for( sal_uInt16 nLine = 0; nLine < nLineCount; ++nLine )
1501 // Excel XP needs a partly initialized SXLI record
1502 rStrm << sal_uInt16( 0 ) // number of equal index entries
1503 << EXC_SXVI_TYPE_DATA
1504 << nIndexCount
1505 << EXC_SXLI_DEFAULTFLAGS;
1506 rStrm.WriteZeroBytes( 2 * nIndexCount );
1508 rStrm.EndRecord();
1512 void XclExpPivotTable::WriteSxex( XclExpStream& rStrm ) const
1514 rStrm.StartRecord( EXC_ID_SXEX, 24 );
1515 rStrm << maPTExtInfo;
1516 rStrm.EndRecord();
1519 void XclExpPivotTable::WriteQsiSxTag( XclExpStream& rStrm ) const
1521 rStrm.StartRecord( 0x0802, 32 );
1523 sal_uInt16 const nRecordType = 0x0802;
1524 sal_uInt16 const nDummyFlags = 0x0000;
1525 sal_uInt16 const nTableType = 1; // 0 = query table : 1 = pivot table
1527 rStrm << nRecordType << nDummyFlags << nTableType;
1529 // General flags
1530 sal_uInt16 const nFlags = 0x0001;
1531 #if 0
1532 // for doc purpose
1533 sal_uInt16 nFlags = 0x0000;
1534 bool bEnableRefresh = true;
1535 bool bPCacheInvalid = false;
1536 bool bOlapPTReport = false;
1538 if (bEnableRefresh) nFlags |= 0x0001;
1539 if (bPCacheInvalid) nFlags |= 0x0002;
1540 if (bOlapPTReport) nFlags |= 0x0004;
1541 #endif
1542 rStrm << nFlags;
1544 // Feature-specific options. The value differs depending on the table
1545 // type, but we assume the table type is always pivot table.
1546 sal_uInt32 const nOptions = 0x00000000;
1547 #if 0
1548 // documentation for which bit is for what
1549 bool bNoStencil = false;
1550 bool bHideTotal = false;
1551 bool bEmptyRows = false;
1552 bool bEmptyCols = false;
1553 if (bNoStencil) nOptions |= 0x00000001;
1554 if (bHideTotal) nOptions |= 0x00000002;
1555 if (bEmptyRows) nOptions |= 0x00000008;
1556 if (bEmptyCols) nOptions |= 0x00000010;
1557 #endif
1558 rStrm << nOptions;
1560 sal_uInt8 eXclVer = 0; // Excel2000
1561 sal_uInt8 const nOffsetBytes = 16;
1562 rStrm << eXclVer // version table last refreshed
1563 << eXclVer // minimum version to refresh
1564 << nOffsetBytes
1565 << eXclVer; // first version created
1567 rStrm << XclExpString(maPTInfo.maTableName);
1568 rStrm << static_cast<sal_uInt16>(0x0001); // no idea what this is for.
1570 rStrm.EndRecord();
1573 void XclExpPivotTable::WriteSxViewEx9( XclExpStream& rStrm ) const
1575 // Until we sync the autoformat ids only export if using grid header layout
1576 // That could only have been set via xls import so far.
1577 if ( 0 == maPTViewEx9Info.mnGridLayout )
1579 rStrm.StartRecord( EXC_ID_SXVIEWEX9, 17 );
1580 rStrm << maPTViewEx9Info;
1581 rStrm.EndRecord();
1585 namespace {
1587 const SCTAB EXC_PTMGR_PIVOTCACHES = SCTAB_MAX;
1589 /** Record wrapper class to write the pivot caches or pivot tables. */
1590 class XclExpPivotRecWrapper : public XclExpRecordBase
1592 public:
1593 explicit XclExpPivotRecWrapper( XclExpPivotTableManager& rPTMgr, SCTAB nScTab );
1594 virtual void Save( XclExpStream& rStrm ) override;
1595 private:
1596 XclExpPivotTableManager& mrPTMgr;
1597 SCTAB const mnScTab;
1600 XclExpPivotRecWrapper::XclExpPivotRecWrapper( XclExpPivotTableManager& rPTMgr, SCTAB nScTab ) :
1601 mrPTMgr( rPTMgr ),
1602 mnScTab( nScTab )
1606 void XclExpPivotRecWrapper::Save( XclExpStream& rStrm )
1608 if( mnScTab == EXC_PTMGR_PIVOTCACHES )
1609 mrPTMgr.WritePivotCaches( rStrm );
1610 else
1611 mrPTMgr.WritePivotTables( rStrm, mnScTab );
1614 } // namespace
1616 XclExpPivotTableManager::XclExpPivotTableManager( const XclExpRoot& rRoot ) :
1617 XclExpRoot( rRoot )
1621 void XclExpPivotTableManager::CreatePivotTables()
1623 if( ScDPCollection* pDPColl = GetDoc().GetDPCollection() )
1624 for( size_t nDPObj = 0, nCount = pDPColl->GetCount(); nDPObj < nCount; ++nDPObj )
1626 ScDPObject& rDPObj = (*pDPColl)[ nDPObj ];
1627 if( const XclExpPivotCache* pPCache = CreatePivotCache( rDPObj ) )
1628 maPTableList.AppendNewRecord( new XclExpPivotTable( GetRoot(), rDPObj, *pPCache ) );
1632 XclExpRecordRef XclExpPivotTableManager::CreatePivotCachesRecord()
1634 return XclExpRecordRef( new XclExpPivotRecWrapper( *this, EXC_PTMGR_PIVOTCACHES ) );
1637 XclExpRecordRef XclExpPivotTableManager::CreatePivotTablesRecord( SCTAB nScTab )
1639 return XclExpRecordRef( new XclExpPivotRecWrapper( *this, nScTab ) );
1642 void XclExpPivotTableManager::WritePivotCaches( XclExpStream& rStrm )
1644 maPCacheList.Save( rStrm );
1647 void XclExpPivotTableManager::WritePivotTables( XclExpStream& rStrm, SCTAB nScTab )
1649 for( size_t nPos = 0, nSize = maPTableList.GetSize(); nPos < nSize; ++nPos )
1651 XclExpPivotTableRef xPTable = maPTableList.GetRecord( nPos );
1652 if( xPTable->GetScTab() == nScTab )
1653 xPTable->Save( rStrm );
1657 const XclExpPivotCache* XclExpPivotTableManager::CreatePivotCache( const ScDPObject& rDPObj )
1659 // try to find a pivot cache with the same data source
1660 /* #i25110# In Excel, the pivot cache contains additional fields
1661 (i.e. grouping info, calculated fields). If the passed DataPilot object
1662 or the found cache contains this data, do not share the cache with
1663 multiple pivot tables. */
1664 if( const ScDPSaveData* pSaveData = rDPObj.GetSaveData() )
1666 const ScDPDimensionSaveData* pDimSaveData = pSaveData->GetExistingDimensionData();
1667 // no dimension save data at all or save data does not contain grouping info
1668 if( !pDimSaveData || !pDimSaveData->HasGroupDimensions() )
1670 // check all existing pivot caches
1671 for( size_t nPos = 0, nSize = maPCacheList.GetSize(); nPos < nSize; ++nPos )
1673 XclExpPivotCacheRef xPCache = maPCacheList.GetRecord( nPos );
1674 // pivot cache does not have grouping info and source data is equal
1675 if( !xPCache->HasAddFields() && xPCache->HasEqualDataSource( rDPObj ) )
1676 return xPCache.get();
1681 // create a new pivot cache
1682 sal_uInt16 nNewCacheIdx = static_cast< sal_uInt16 >( maPCacheList.GetSize() );
1683 XclExpPivotCacheRef xNewPCache( new XclExpPivotCache( GetRoot(), rDPObj, nNewCacheIdx ) );
1684 if( xNewPCache->IsValid() )
1686 maPCacheList.AppendRecord( xNewPCache );
1687 return xNewPCache.get();
1690 return nullptr;
1693 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */