use insert function instead of for loop
[LibreOffice.git] / sc / source / filter / excel / xepivot.cxx
blob0afbe31a19b6741a06e1989d7dd88c6cc6308020
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>
32 #include <string_view>
34 #include <osl/diagnose.h>
35 #include <sot/storage.hxx>
36 #include <document.hxx>
37 #include <dpcache.hxx>
38 #include <dpgroup.hxx>
39 #include <dpobject.hxx>
40 #include <dpsave.hxx>
41 #include <dpdimsave.hxx>
42 #include <dpshttab.hxx>
43 #include <globstr.hrc>
44 #include <scresid.hxx>
45 #include <xestring.hxx>
46 #include <xelink.hxx>
47 #include <dputil.hxx>
48 #include <generalfunction.hxx>
49 #include <svl/numformat.hxx>
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::DataPilotFieldSortInfo;
57 using ::com::sun::star::sheet::DataPilotFieldAutoShowInfo;
58 using ::com::sun::star::sheet::DataPilotFieldLayoutInfo;
59 using ::com::sun::star::sheet::DataPilotFieldReference;
61 // Pivot cache
63 namespace {
65 // constants to track occurrence of specific data types
66 const sal_uInt16 EXC_PCITEM_DATA_STRING = 0x0001; /// String, empty, boolean, error.
67 const sal_uInt16 EXC_PCITEM_DATA_DOUBLE = 0x0002; /// Double with fraction.
68 const sal_uInt16 EXC_PCITEM_DATA_INTEGER = 0x0004; /// Integer, double without fraction.
69 const sal_uInt16 EXC_PCITEM_DATA_DATE = 0x0008; /// Date, time, date/time.
71 /** Maps a bitfield consisting of EXC_PCITEM_DATA_* flags above to SXFIELD data type bitfield. */
72 const sal_uInt16 spnPCItemFlags[] =
73 { // STR DBL INT DAT
74 EXC_SXFIELD_DATA_NONE,
75 EXC_SXFIELD_DATA_STR, // x
76 EXC_SXFIELD_DATA_INT, // x
77 EXC_SXFIELD_DATA_STR_INT, // x x
78 EXC_SXFIELD_DATA_DBL, // x
79 EXC_SXFIELD_DATA_STR_DBL, // x x
80 EXC_SXFIELD_DATA_INT, // x x
81 EXC_SXFIELD_DATA_STR_INT, // x x x
82 EXC_SXFIELD_DATA_DATE, // x
83 EXC_SXFIELD_DATA_DATE_STR, // x x
84 EXC_SXFIELD_DATA_DATE_NUM, // x x
85 EXC_SXFIELD_DATA_DATE_STR, // x x x
86 EXC_SXFIELD_DATA_DATE_NUM, // x x
87 EXC_SXFIELD_DATA_DATE_STR, // x x x
88 EXC_SXFIELD_DATA_DATE_NUM, // x x x
89 EXC_SXFIELD_DATA_DATE_STR // x x x x
92 } // namespace
94 XclExpPCItem::XclExpPCItem( const OUString& rText ) :
95 XclExpRecord( (!rText.isEmpty()) ? EXC_ID_SXSTRING : EXC_ID_SXEMPTY, 0 ),
96 mnTypeFlag( EXC_PCITEM_DATA_STRING )
98 if( !rText.isEmpty() )
99 SetText( rText );
100 else
101 SetEmpty();
104 XclExpPCItem::XclExpPCItem( double fValue, const OUString& rText ) :
105 XclExpRecord( EXC_ID_SXDOUBLE, 8 )
107 SetDouble( fValue, rText );
108 mnTypeFlag = (fValue - floor( fValue ) == 0.0) ?
109 EXC_PCITEM_DATA_INTEGER : EXC_PCITEM_DATA_DOUBLE;
112 XclExpPCItem::XclExpPCItem( const DateTime& rDateTime, const OUString& rText ) :
113 XclExpRecord( EXC_ID_SXDATETIME, 8 )
115 SetDateTime( rDateTime, rText );
116 mnTypeFlag = EXC_PCITEM_DATA_DATE;
119 XclExpPCItem::XclExpPCItem( sal_Int16 nValue ) :
120 XclExpRecord( EXC_ID_SXINTEGER, 2 ),
121 mnTypeFlag( EXC_PCITEM_DATA_INTEGER )
123 SetInteger( nValue );
126 XclExpPCItem::XclExpPCItem( bool bValue, const OUString& rText ) :
127 XclExpRecord( EXC_ID_SXBOOLEAN, 2 ),
128 mnTypeFlag( EXC_PCITEM_DATA_STRING )
130 SetBool( bValue, rText );
133 bool XclExpPCItem::EqualsText( std::u16string_view rText ) const
135 return rText.empty() ? IsEmpty() : (GetText() && (*GetText() == rText));
138 bool XclExpPCItem::EqualsDouble( double fValue ) const
140 return GetDouble() && (*GetDouble() == fValue);
143 bool XclExpPCItem::EqualsDateTime( const DateTime& rDateTime ) const
145 return GetDateTime() && (*GetDateTime() == rDateTime);
148 bool XclExpPCItem::EqualsBool( bool bValue ) const
150 return GetBool() && (*GetBool() == bValue);
153 void XclExpPCItem::WriteBody( XclExpStream& rStrm )
155 if( const OUString* pText = GetText() )
157 rStrm << XclExpString( *pText );
159 else if( const double* pfValue = GetDouble() )
161 rStrm << *pfValue;
163 else if( const sal_Int16* pnValue = GetInteger() )
165 rStrm << *pnValue;
167 else if( const DateTime* pDateTime = GetDateTime() )
169 sal_uInt16 nYear = static_cast< sal_uInt16 >( pDateTime->GetYear() );
170 sal_uInt16 nMonth = pDateTime->GetMonth();
171 sal_uInt8 nDay = static_cast< sal_uInt8 >( pDateTime->GetDay() );
172 sal_uInt8 nHour = static_cast< sal_uInt8 >( pDateTime->GetHour() );
173 sal_uInt8 nMin = static_cast< sal_uInt8 >( pDateTime->GetMin() );
174 sal_uInt8 nSec = static_cast< sal_uInt8 >( pDateTime->GetSec() );
175 if( nYear < 1900 ) { nYear = 1900; nMonth = 1; nDay = 0; }
176 rStrm << nYear << nMonth << nDay << nHour << nMin << nSec;
178 else if( const bool* pbValue = GetBool() )
180 rStrm << static_cast< sal_uInt16 >( *pbValue ? 1 : 0 );
182 else
184 // nothing to do for SXEMPTY
185 OSL_ENSURE( IsEmpty(), "XclExpPCItem::WriteBody - no data found" );
189 XclExpPCField::XclExpPCField(
190 const XclExpRoot& rRoot, sal_uInt16 nFieldIdx,
191 const ScDPObject& rDPObj, const ScRange& rRange ) :
192 XclExpRecord( EXC_ID_SXFIELD ),
193 XclPCField( EXC_PCFIELD_STANDARD, nFieldIdx ),
194 XclExpRoot( rRoot ),
195 mnTypeFlags( 0 )
197 // general settings for the standard field, insert all items from source range
198 InitStandardField( rRange );
200 // add special settings for inplace numeric grouping
201 if( const ScDPSaveData* pSaveData = rDPObj.GetSaveData() )
203 if( const ScDPDimensionSaveData* pSaveDimData = pSaveData->GetExistingDimensionData() )
205 if( const ScDPSaveNumGroupDimension* pNumGroupDim = pSaveDimData->GetNumGroupDim( GetFieldName() ) )
207 const ScDPNumGroupInfo& rNumInfo = pNumGroupDim->GetInfo();
208 const ScDPNumGroupInfo& rDateInfo = pNumGroupDim->GetDateInfo();
209 OSL_ENSURE( !rNumInfo.mbEnable || !rDateInfo.mbEnable,
210 "XclExpPCField::XclExpPCField - numeric and date grouping enabled" );
212 if( rNumInfo.mbEnable )
213 InitNumGroupField( rDPObj, rNumInfo );
214 else if( rDateInfo.mbEnable )
215 InitDateGroupField( rDPObj, rDateInfo, pNumGroupDim->GetDatePart() );
220 // final settings (flags, item numbers)
221 Finalize();
224 XclExpPCField::XclExpPCField(
225 const XclExpRoot& rRoot, sal_uInt16 nFieldIdx,
226 const ScDPObject& rDPObj, const ScDPSaveGroupDimension& rGroupDim, const XclExpPCField& rBaseField ) :
227 XclExpRecord( EXC_ID_SXFIELD ),
228 XclPCField( EXC_PCFIELD_STDGROUP, nFieldIdx ),
229 XclExpRoot( rRoot ),
230 mnTypeFlags( 0 )
232 // add base field info (always using first base field, not predecessor of this field) ***
233 OSL_ENSURE( rBaseField.GetFieldName() == rGroupDim.GetSourceDimName(),
234 "XclExpPCField::FillFromGroup - wrong base cache field" );
235 maFieldInfo.maName = rGroupDim.GetGroupDimName();
236 maFieldInfo.mnGroupBase = rBaseField.GetFieldIndex();
238 // add standard group info or date group info
239 const ScDPNumGroupInfo& rDateInfo = rGroupDim.GetDateInfo();
240 if( rDateInfo.mbEnable && (rGroupDim.GetDatePart() != 0) )
241 InitDateGroupField( rDPObj, rDateInfo, rGroupDim.GetDatePart() );
242 else
243 InitStdGroupField( rBaseField, rGroupDim );
245 // final settings (flags, item numbers)
246 Finalize();
249 XclExpPCField::~XclExpPCField()
253 void XclExpPCField::SetGroupChildField( const XclExpPCField& rChildField )
255 OSL_ENSURE( !::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_HASCHILD ),
256 "XclExpPCField::SetGroupChildIndex - field already has a grouping child field" );
257 ::set_flag( maFieldInfo.mnFlags, EXC_SXFIELD_HASCHILD );
258 maFieldInfo.mnGroupChild = rChildField.GetFieldIndex();
261 sal_uInt16 XclExpPCField::GetItemCount() const
263 return static_cast< sal_uInt16 >( GetVisItemList().GetSize() );
266 const XclExpPCItem* XclExpPCField::GetItem( sal_uInt16 nItemIdx ) const
268 return GetVisItemList().GetRecord( nItemIdx );
271 sal_uInt16 XclExpPCField::GetItemIndex( std::u16string_view rItemName ) const
273 const XclExpPCItemList& rItemList = GetVisItemList();
274 for( size_t nPos = 0, nSize = rItemList.GetSize(); nPos < nSize; ++nPos )
275 if( rItemList.GetRecord( nPos )->ConvertToText() == rItemName )
276 return static_cast< sal_uInt16 >( nPos );
277 return EXC_PC_NOITEM;
280 std::size_t XclExpPCField::GetIndexSize() const
282 return Has16BitIndexes() ? 2 : 1;
285 void XclExpPCField::WriteIndex( XclExpStream& rStrm, sal_uInt32 nSrcRow ) const
287 // only standard fields write item indexes
288 if( nSrcRow < maIndexVec.size() )
290 sal_uInt16 nIndex = maIndexVec[ nSrcRow ];
291 if( Has16BitIndexes() )
292 rStrm << nIndex;
293 else
294 rStrm << static_cast< sal_uInt8 >( nIndex );
298 void XclExpPCField::Save( XclExpStream& rStrm )
300 OSL_ENSURE( IsSupportedField(), "XclExpPCField::Save - unknown field type" );
301 // SXFIELD
302 XclExpRecord::Save( rStrm );
303 // SXFDBTYPE
304 XclExpUInt16Record( EXC_ID_SXFDBTYPE, EXC_SXFDBTYPE_DEFAULT ).Save( rStrm );
305 // list of grouping items
306 maGroupItemList.Save( rStrm );
307 // SXGROUPINFO
308 WriteSxgroupinfo( rStrm );
309 // SXNUMGROUP and additional grouping items (grouping limit settings)
310 WriteSxnumgroup( rStrm );
311 // list of original items
312 maOrigItemList.Save( rStrm );
315 // private --------------------------------------------------------------------
317 const XclExpPCField::XclExpPCItemList& XclExpPCField::GetVisItemList() const
319 OSL_ENSURE( IsStandardField() == maGroupItemList.IsEmpty(),
320 "XclExpPCField::GetVisItemList - unexpected additional items in standard field" );
321 return IsStandardField() ? maOrigItemList : maGroupItemList;
324 void XclExpPCField::InitStandardField( const ScRange& rRange )
326 OSL_ENSURE( IsStandardField(), "XclExpPCField::InitStandardField - only for standard fields" );
327 OSL_ENSURE( rRange.aStart.Col() == rRange.aEnd.Col(), "XclExpPCField::InitStandardField - cell range with multiple columns" );
329 ScDocument& rDoc = GetDoc();
330 SvNumberFormatter& rFormatter = GetFormatter();
332 // field name is in top cell of the range
333 ScAddress aPos( rRange.aStart );
334 maFieldInfo.maName = rDoc.GetString(aPos.Col(), aPos.Row(), aPos.Tab());
335 // #i76047# maximum field name length in pivot cache is 255
336 if (maFieldInfo.maName.getLength() > EXC_PC_MAXSTRLEN)
337 maFieldInfo.maName = maFieldInfo.maName.copy(0, EXC_PC_MAXSTRLEN);
339 // loop over all cells, create pivot cache items
340 for( aPos.IncRow(); (aPos.Row() <= rRange.aEnd.Row()) && (maOrigItemList.GetSize() < EXC_PC_MAXITEMCOUNT); aPos.IncRow() )
342 OUString aText = rDoc.GetString(aPos.Col(), aPos.Row(), aPos.Tab());
343 if( rDoc.HasValueData( aPos.Col(), aPos.Row(), aPos.Tab() ) )
345 double fValue = rDoc.GetValue( aPos );
346 SvNumFormatType nFmtType = rFormatter.GetType( rDoc.GetNumberFormat( rDoc.GetNonThreadedContext(), aPos ) );
347 if( nFmtType == SvNumFormatType::LOGICAL )
348 InsertOrigBoolItem( fValue != 0, aText );
349 else if( nFmtType & SvNumFormatType::DATETIME )
350 InsertOrigDateTimeItem( GetDateTimeFromDouble( ::std::max( fValue, 0.0 ) ), aText );
351 else
352 InsertOrigDoubleItem( fValue, aText );
354 else
356 InsertOrigTextItem( aText );
361 void XclExpPCField::InitStdGroupField( const XclExpPCField& rBaseField, const ScDPSaveGroupDimension& rGroupDim )
363 OSL_ENSURE( IsGroupField(), "XclExpPCField::InitStdGroupField - only for standard grouping fields" );
365 maFieldInfo.mnBaseItems = rBaseField.GetItemCount();
366 maGroupOrder.resize( maFieldInfo.mnBaseItems, EXC_PC_NOITEM );
368 // loop over all groups of this field
369 for( tools::Long nGroupIdx = 0, nGroupCount = rGroupDim.GetGroupCount(); nGroupIdx < nGroupCount; ++nGroupIdx )
371 const ScDPSaveGroupItem& rGroupItem = rGroupDim.GetGroupByIndex( nGroupIdx );
372 // the index of the new item containing the grouping name
373 sal_uInt16 nGroupItemIdx = EXC_PC_NOITEM;
374 // loop over all elements of one group
375 for( size_t nElemIdx = 0, nElemCount = rGroupItem.GetElementCount(); nElemIdx < nElemCount; ++nElemIdx )
377 if (const OUString* pElemName = rGroupItem.GetElementByIndex(nElemIdx))
379 // try to find the item that is part of the group in the base field
380 sal_uInt16 nBaseItemIdx = rBaseField.GetItemIndex( *pElemName );
381 if( nBaseItemIdx < maFieldInfo.mnBaseItems )
383 // add group name item only if there are any valid base items
384 if( nGroupItemIdx == EXC_PC_NOITEM )
385 nGroupItemIdx = InsertGroupItem( new XclExpPCItem( rGroupItem.GetGroupName() ) );
386 maGroupOrder[ nBaseItemIdx ] = nGroupItemIdx;
392 // add items and base item indexes of all ungrouped elements
393 for( sal_uInt16 nBaseItemIdx = 0; nBaseItemIdx < maFieldInfo.mnBaseItems; ++nBaseItemIdx )
394 // items that are not part of a group still have the EXC_PC_NOITEM entry
395 if( maGroupOrder[ nBaseItemIdx ] == EXC_PC_NOITEM )
396 // try to find the base item
397 if( const XclExpPCItem* pBaseItem = rBaseField.GetItem( nBaseItemIdx ) )
398 // create a clone of the base item, insert its index into item order list
399 maGroupOrder[ nBaseItemIdx ] = InsertGroupItem( new XclExpPCItem( *pBaseItem ) );
402 void XclExpPCField::InitNumGroupField( const ScDPObject& rDPObj, const ScDPNumGroupInfo& rNumInfo )
404 OSL_ENSURE( IsStandardField(), "XclExpPCField::InitNumGroupField - only for standard fields" );
405 OSL_ENSURE( rNumInfo.mbEnable, "XclExpPCField::InitNumGroupField - numeric grouping not enabled" );
407 // new field type, date type, limit settings (min/max/step/auto)
408 if( rNumInfo.mbDateValues )
410 // special case: group by days with step count
411 meFieldType = EXC_PCFIELD_DATEGROUP;
412 maNumGroupInfo.SetScDateType( css::sheet::DataPilotFieldGroupBy::DAYS );
413 SetDateGroupLimit( rNumInfo, true );
415 else
417 meFieldType = EXC_PCFIELD_NUMGROUP;
418 maNumGroupInfo.SetNumType();
419 SetNumGroupLimit( rNumInfo );
422 // generate visible items
423 InsertNumDateGroupItems( rDPObj, rNumInfo );
426 void XclExpPCField::InitDateGroupField( const ScDPObject& rDPObj, const ScDPNumGroupInfo& rDateInfo, sal_Int32 nDatePart )
428 OSL_ENSURE( IsStandardField() || IsStdGroupField(), "XclExpPCField::InitDateGroupField - only for standard fields" );
429 OSL_ENSURE( rDateInfo.mbEnable, "XclExpPCField::InitDateGroupField - date grouping not enabled" );
431 // new field type
432 meFieldType = IsStandardField() ? EXC_PCFIELD_DATEGROUP : EXC_PCFIELD_DATECHILD;
434 // date type, limit settings (min/max/step/auto)
435 maNumGroupInfo.SetScDateType( nDatePart );
436 SetDateGroupLimit( rDateInfo, false );
438 // generate visible items
439 InsertNumDateGroupItems( rDPObj, rDateInfo, nDatePart );
442 void XclExpPCField::InsertItemArrayIndex( size_t nListPos )
444 OSL_ENSURE( IsStandardField(), "XclExpPCField::InsertItemArrayIndex - only for standard fields" );
445 maIndexVec.push_back( static_cast< sal_uInt16 >( nListPos ) );
448 void XclExpPCField::InsertOrigItem( XclExpPCItem* pNewItem )
450 size_t nItemIdx = maOrigItemList.GetSize();
451 maOrigItemList.AppendNewRecord( pNewItem );
452 InsertItemArrayIndex( nItemIdx );
453 mnTypeFlags |= pNewItem->GetTypeFlag();
456 void XclExpPCField::InsertOrigTextItem( const OUString& aText )
458 size_t nPos = 0;
459 bool bFound = false;
460 // #i76047# maximum item text length in pivot cache is 255
461 OUString aShortText = aText.copy( 0, ::std::min(aText.getLength(), EXC_PC_MAXSTRLEN ) );
462 for( size_t nSize = maOrigItemList.GetSize(); !bFound && (nPos < nSize); ++nPos )
463 if( (bFound = maOrigItemList.GetRecord( nPos )->EqualsText( aShortText )) )
464 InsertItemArrayIndex( nPos );
465 if( !bFound )
466 InsertOrigItem( new XclExpPCItem( aShortText ) );
469 void XclExpPCField::InsertOrigDoubleItem( double fValue, const OUString& rText )
471 size_t nPos = 0;
472 bool bFound = false;
473 for( size_t nSize = maOrigItemList.GetSize(); !bFound && (nPos < nSize); ++nPos )
474 if( (bFound = maOrigItemList.GetRecord( nPos )->EqualsDouble( fValue )) )
475 InsertItemArrayIndex( nPos );
476 if( !bFound )
477 InsertOrigItem( new XclExpPCItem( fValue, rText ) );
480 void XclExpPCField::InsertOrigDateTimeItem( const DateTime& rDateTime, const OUString& rText )
482 size_t nPos = 0;
483 bool bFound = false;
484 for( size_t nSize = maOrigItemList.GetSize(); !bFound && (nPos < nSize); ++nPos )
485 if( (bFound = maOrigItemList.GetRecord( nPos )->EqualsDateTime( rDateTime )) )
486 InsertItemArrayIndex( nPos );
487 if( !bFound )
488 InsertOrigItem( new XclExpPCItem( rDateTime, rText ) );
491 void XclExpPCField::InsertOrigBoolItem( bool bValue, const OUString& rText )
493 size_t nPos = 0;
494 bool bFound = false;
495 for( size_t nSize = maOrigItemList.GetSize(); !bFound && (nPos < nSize); ++nPos )
496 if( (bFound = maOrigItemList.GetRecord( nPos )->EqualsBool( bValue )) )
497 InsertItemArrayIndex( nPos );
498 if( !bFound )
499 InsertOrigItem( new XclExpPCItem( bValue, rText ) );
502 sal_uInt16 XclExpPCField::InsertGroupItem( XclExpPCItem* pNewItem )
504 maGroupItemList.AppendNewRecord( pNewItem );
505 return static_cast< sal_uInt16 >( maGroupItemList.GetSize() - 1 );
508 void XclExpPCField::InsertNumDateGroupItems( const ScDPObject& rDPObj, const ScDPNumGroupInfo& rNumInfo, sal_Int32 nDatePart )
510 OSL_ENSURE( rDPObj.GetSheetDesc(), "XclExpPCField::InsertNumDateGroupItems - cannot generate element list" );
511 const ScSheetSourceDesc* pSrcDesc = rDPObj.GetSheetDesc();
512 if(!pSrcDesc)
513 return;
515 // get the string collection with original source elements
516 const ScDPSaveData* pSaveData = rDPObj.GetSaveData();
517 const ScDPDimensionSaveData* pDimData = nullptr;
518 if (pSaveData)
519 pDimData = pSaveData->GetExistingDimensionData();
521 const ScDPCache* pCache = pSrcDesc->CreateCache(pDimData);
522 if (!pCache)
523 return;
525 ScSheetDPData aDPData(&GetDoc(), *pSrcDesc, *pCache);
526 tools::Long nDim = GetFieldIndex();
527 // get the string collection with generated grouping elements
528 ScDPNumGroupDimension aTmpDim( rNumInfo );
529 if( nDatePart != 0 )
530 aTmpDim.SetDateDimension();
531 const std::vector<SCROW>& aMemberIds = aTmpDim.GetNumEntries(
532 static_cast<SCCOL>(nDim), pCache);
533 for (SCROW nMemberId : aMemberIds)
535 const ScDPItemData* pData = aDPData.GetMemberById(nDim, nMemberId);
536 if ( pData )
538 OUString aStr = pCache->GetFormattedString(nDim, *pData, false);
539 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 const ScSheetSourceDesc* pSrcDesc = rDPObj.GetSheetDesc();
622 if(!pSrcDesc)
623 return;
625 /* maOrigSrcRange: Range received from the DataPilot object.
626 maExpSrcRange: Range written to the DCONREF record.
627 maDocSrcRange: Range used to get source data from Calc document.
628 This range may be shorter than maExpSrcRange to improve export
629 performance (#i22541#). */
630 maOrigSrcRange = maExpSrcRange = maDocSrcRange = pSrcDesc->GetSourceRange();
631 maSrcRangeName = pSrcDesc->GetRangeName();
633 // internal sheet data only
634 SCTAB nScTab = maExpSrcRange.aStart.Tab();
635 if( !((nScTab == maExpSrcRange.aEnd.Tab()) && GetTabInfo().IsExportTab( nScTab )) )
636 return;
638 // ValidateRange() restricts source range to valid Excel limits
639 if( !GetAddressConverter().ValidateRange( maExpSrcRange, true ) )
640 return;
642 // #i22541# skip empty cell areas (performance)
643 SCCOL nDocCol1, nDocCol2;
644 SCROW nDocRow1, nDocRow2;
645 GetDoc().GetDataStart( nScTab, nDocCol1, nDocRow1 );
646 GetDoc().GetPrintArea( nScTab, nDocCol2, nDocRow2, false );
647 SCCOL nSrcCol1 = maExpSrcRange.aStart.Col();
648 SCROW nSrcRow1 = maExpSrcRange.aStart.Row();
649 SCCOL nSrcCol2 = maExpSrcRange.aEnd.Col();
650 SCROW nSrcRow2 = maExpSrcRange.aEnd.Row();
652 // #i22541# do not store index list for too big ranges
653 if( 2 * (nDocRow2 - nDocRow1) < (nSrcRow2 - nSrcRow1) )
654 ::set_flag( maPCInfo.mnFlags, EXC_SXDB_SAVEDATA, false );
656 // adjust row indexes, keep one row of empty area to surely have the empty cache item
657 if( nSrcRow1 < nDocRow1 )
658 nSrcRow1 = nDocRow1 - 1;
659 if( nSrcRow2 > nDocRow2 )
660 nSrcRow2 = nDocRow2 + 1;
662 maDocSrcRange.aStart.SetCol( ::std::max( nDocCol1, nSrcCol1 ) );
663 maDocSrcRange.aStart.SetRow( nSrcRow1 );
664 maDocSrcRange.aEnd.SetCol( ::std::min( nDocCol2, nSrcCol2 ) );
665 maDocSrcRange.aEnd.SetRow( nSrcRow2 );
667 GetDoc().GetName( nScTab, maTabName );
668 maPCInfo.mnSrcRecs = static_cast< sal_uInt32 >( maExpSrcRange.aEnd.Row() - maExpSrcRange.aStart.Row() );
669 maPCInfo.mnStrmId = nListIdx + 1;
670 maPCInfo.mnSrcType = EXC_SXDB_SRC_SHEET;
672 AddFields( rDPObj );
674 mbValid = true;
677 bool XclExpPivotCache::HasItemIndexList() const
679 return ::get_flag( maPCInfo.mnFlags, EXC_SXDB_SAVEDATA );
682 sal_uInt16 XclExpPivotCache::GetFieldCount() const
684 return static_cast< sal_uInt16 >( maFieldList.GetSize() );
687 const XclExpPCField* XclExpPivotCache::GetField( sal_uInt16 nFieldIdx ) const
689 return maFieldList.GetRecord( nFieldIdx );
692 bool XclExpPivotCache::HasAddFields() const
694 // pivot cache can be shared, if there are no additional cache fields
695 return maPCInfo.mnStdFields < maPCInfo.mnTotalFields;
698 bool XclExpPivotCache::HasEqualDataSource( const ScDPObject& rDPObj ) const
700 /* For now, only sheet sources are supported, therefore it is enough to
701 compare the ScSheetSourceDesc. Later, there should be done more complicated
702 comparisons regarding the source type of rDPObj and this cache. */
703 if( const ScSheetSourceDesc* pSrcDesc = rDPObj.GetSheetDesc() )
704 return pSrcDesc->GetSourceRange() == maOrigSrcRange;
705 return false;
708 void XclExpPivotCache::Save( XclExpStream& rStrm )
710 OSL_ENSURE( mbValid, "XclExpPivotCache::Save - invalid pivot cache" );
711 // SXIDSTM
712 XclExpUInt16Record( EXC_ID_SXIDSTM, maPCInfo.mnStrmId ).Save( rStrm );
713 // SXVS
714 XclExpUInt16Record( EXC_ID_SXVS, EXC_SXVS_SHEET ).Save( rStrm );
716 if (!maSrcRangeName.isEmpty())
717 // DCONNAME
718 WriteDConName(rStrm);
719 else
720 // DCONREF
721 WriteDconref(rStrm);
723 // create the pivot cache storage stream
724 WriteCacheStream();
727 void XclExpPivotCache::SaveXml( XclExpXmlStream& /*rStrm*/ )
731 void XclExpPivotCache::AddFields( const ScDPObject& rDPObj )
733 AddStdFields( rDPObj );
734 maPCInfo.mnStdFields = GetFieldCount();
735 AddGroupFields( rDPObj );
736 maPCInfo.mnTotalFields = GetFieldCount();
739 void XclExpPivotCache::AddStdFields( const ScDPObject& rDPObj )
741 // if item index list is not written, used shortened source range (maDocSrcRange) for performance
742 const ScRange& rRange = HasItemIndexList() ? maExpSrcRange : maDocSrcRange;
743 // create a standard pivot cache field for each source column
744 for( SCCOL nScCol = rRange.aStart.Col(), nEndScCol = rRange.aEnd.Col(); nScCol <= nEndScCol; ++nScCol )
746 ScRange aColRange( rRange );
747 aColRange.aStart.SetCol( nScCol );
748 aColRange.aEnd.SetCol( nScCol );
749 maFieldList.AppendNewRecord( new XclExpPCField(
750 GetRoot(), GetFieldCount(), rDPObj, aColRange ) );
754 void XclExpPivotCache::AddGroupFields( const ScDPObject& rDPObj )
756 const ScDPSaveData* pSaveData = rDPObj.GetSaveData();
757 if(!pSaveData)
758 return;
759 const ScDPDimensionSaveData* pSaveDimData = pSaveData->GetExistingDimensionData();
760 if( !pSaveDimData )
761 return;
763 // loop over all existing standard fields to find their group fields
764 for( sal_uInt16 nFieldIdx = 0; nFieldIdx < maPCInfo.mnStdFields; ++nFieldIdx )
766 if( XclExpPCField* pCurrStdField = maFieldList.GetRecord( nFieldIdx ) )
768 const ScDPSaveGroupDimension* pGroupDim = pSaveDimData->GetGroupDimForBase( pCurrStdField->GetFieldName() );
769 XclExpPCField* pLastGroupField = pCurrStdField;
770 while( pGroupDim )
772 // insert the new grouping field
773 XclExpPCFieldRef xNewGroupField = new XclExpPCField(
774 GetRoot(), GetFieldCount(), rDPObj, *pGroupDim, *pCurrStdField );
775 maFieldList.AppendRecord( xNewGroupField );
777 // register new grouping field at current grouping field, building a chain
778 pLastGroupField->SetGroupChildField( *xNewGroupField );
780 // next grouping dimension
781 pGroupDim = pSaveDimData->GetGroupDimForBase( pGroupDim->GetGroupDimName() );
782 pLastGroupField = xNewGroupField.get();
788 void XclExpPivotCache::WriteDconref( XclExpStream& rStrm ) const
790 XclExpString aRef( XclExpUrlHelper::EncodeUrl( GetRoot(), u"", &maTabName ) );
791 rStrm.StartRecord( EXC_ID_DCONREF, 7 + aRef.GetSize() );
792 rStrm << static_cast< sal_uInt16 >( maExpSrcRange.aStart.Row() )
793 << static_cast< sal_uInt16 >( maExpSrcRange.aEnd.Row() )
794 << static_cast< sal_uInt8 >( maExpSrcRange.aStart.Col() )
795 << static_cast< sal_uInt8 >( maExpSrcRange.aEnd.Col() )
796 << aRef
797 << sal_uInt8( 0 );
798 rStrm.EndRecord();
801 void XclExpPivotCache::WriteDConName( XclExpStream& rStrm ) const
803 XclExpString aName(maSrcRangeName);
804 rStrm.StartRecord(EXC_ID_DCONNAME, aName.GetSize() + 2);
805 rStrm << aName << sal_uInt16(0);
806 rStrm.EndRecord();
809 void XclExpPivotCache::WriteCacheStream()
811 rtl::Reference<SotStorage> xSvStrg = OpenStorage(EXC_STORAGE_PTCACHE);
812 rtl::Reference<SotStorageStream> xSvStrm = OpenStream( xSvStrg, ScfTools::GetHexStr( maPCInfo.mnStrmId ) );
813 if( !xSvStrm.is() )
814 return;
816 XclExpStream aStrm( *xSvStrm, GetRoot() );
817 // SXDB
818 WriteSxdb( aStrm );
819 // SXDBEX
820 WriteSxdbex( aStrm );
821 // field list (SXFIELD and items)
822 maFieldList.Save( aStrm );
823 // index table (list of SXINDEXLIST)
824 WriteSxindexlistList( aStrm );
825 // EOF
826 XclExpEmptyRecord( EXC_ID_EOF ).Save( aStrm );
829 void XclExpPivotCache::WriteSxdb( XclExpStream& rStrm ) const
831 rStrm.StartRecord( EXC_ID_SXDB, 21 );
832 rStrm << maPCInfo;
833 rStrm.EndRecord();
836 void XclExpPivotCache::WriteSxdbex( XclExpStream& rStrm )
838 rStrm.StartRecord( EXC_ID_SXDBEX, 12 );
839 rStrm << EXC_SXDBEX_CREATION_DATE
840 << sal_uInt32( 0 ); // number of SXFORMULA records
841 rStrm.EndRecord();
844 void XclExpPivotCache::WriteSxindexlistList( XclExpStream& rStrm ) const
846 if( !HasItemIndexList() )
847 return;
849 std::size_t nRecSize = 0;
850 size_t nPos, nSize = maFieldList.GetSize();
851 for( nPos = 0; nPos < nSize; ++nPos )
852 nRecSize += maFieldList.GetRecord( nPos )->GetIndexSize();
854 for( sal_uInt32 nSrcRow = 0; nSrcRow < maPCInfo.mnSrcRecs; ++nSrcRow )
856 rStrm.StartRecord( EXC_ID_SXINDEXLIST, nRecSize );
857 for( nPos = 0; nPos < nSize; ++nPos )
858 maFieldList.GetRecord( nPos )->WriteIndex( rStrm, nSrcRow );
859 rStrm.EndRecord();
863 // Pivot table
865 namespace {
867 /** Returns a display string for a data field containing the field name and aggregation function. */
868 OUString lclGetDataFieldCaption( std::u16string_view rFieldName, ScGeneralFunction eFunc )
870 OUString aCaption;
872 TranslateId pResIdx;
873 switch( eFunc )
875 case ScGeneralFunction::SUM: pResIdx = STR_FUN_TEXT_SUM; break;
876 case ScGeneralFunction::COUNT: pResIdx = STR_FUN_TEXT_COUNT; break;
877 case ScGeneralFunction::AVERAGE: pResIdx = STR_FUN_TEXT_AVG; break;
878 case ScGeneralFunction::MAX: pResIdx = STR_FUN_TEXT_MAX; break;
879 case ScGeneralFunction::MIN: pResIdx = STR_FUN_TEXT_MIN; break;
880 case ScGeneralFunction::PRODUCT: pResIdx = STR_FUN_TEXT_PRODUCT; break;
881 case ScGeneralFunction::COUNTNUMS: pResIdx = STR_FUN_TEXT_COUNT; break;
882 case ScGeneralFunction::STDEV: pResIdx = STR_FUN_TEXT_STDDEV; break;
883 case ScGeneralFunction::STDEVP: pResIdx = STR_FUN_TEXT_STDDEV; break;
884 case ScGeneralFunction::VAR: pResIdx = STR_FUN_TEXT_VAR; break;
885 case ScGeneralFunction::VARP: pResIdx = STR_FUN_TEXT_VAR; break;
886 default:;
888 if (pResIdx)
889 aCaption = ScResId(pResIdx) + " - ";
890 aCaption += rFieldName;
891 return aCaption;
894 } // namespace
896 XclExpPTItem::XclExpPTItem( const XclExpPCField& rCacheField, sal_uInt16 nCacheIdx ) :
897 XclExpRecord( EXC_ID_SXVI, 8 ),
898 mpCacheItem( rCacheField.GetItem( nCacheIdx ) )
900 maItemInfo.mnType = EXC_SXVI_TYPE_DATA;
901 maItemInfo.mnCacheIdx = nCacheIdx;
902 maItemInfo.maVisName.mbUseCache = mpCacheItem != nullptr;
905 XclExpPTItem::XclExpPTItem( sal_uInt16 nItemType, sal_uInt16 nCacheIdx ) :
906 XclExpRecord( EXC_ID_SXVI, 8 ),
907 mpCacheItem( nullptr )
909 maItemInfo.mnType = nItemType;
910 maItemInfo.mnCacheIdx = nCacheIdx;
911 maItemInfo.maVisName.mbUseCache = true;
914 OUString XclExpPTItem::GetItemName() const
916 return mpCacheItem ? mpCacheItem->ConvertToText() : OUString();
919 void XclExpPTItem::SetPropertiesFromMember( const ScDPSaveMember& rSaveMem )
921 // #i115659# GetIsVisible() is not valid if HasIsVisible() returns false, default is 'visible' then
922 ::set_flag( maItemInfo.mnFlags, EXC_SXVI_HIDDEN, rSaveMem.HasIsVisible() && !rSaveMem.GetIsVisible() );
923 // #i115659# GetShowDetails() is not valid if HasShowDetails() returns false, default is 'show detail' then
924 ::set_flag( maItemInfo.mnFlags, EXC_SXVI_HIDEDETAIL, rSaveMem.HasShowDetails() && !rSaveMem.GetShowDetails() );
926 // visible name
927 const std::optional<OUString> & pVisName = rSaveMem.GetLayoutName();
928 if (pVisName && *pVisName != GetItemName())
929 maItemInfo.SetVisName(*pVisName);
932 void XclExpPTItem::WriteBody( XclExpStream& rStrm )
934 rStrm << maItemInfo;
937 XclExpPTField::XclExpPTField( const XclExpPivotTable& rPTable, sal_uInt16 nCacheIdx ) :
938 mrPTable( rPTable ),
939 mpCacheField( rPTable.GetCacheField( nCacheIdx ) )
941 maFieldInfo.mnCacheIdx = nCacheIdx;
943 // create field items
944 if( mpCacheField )
945 for( sal_uInt16 nItemIdx = 0, nItemCount = mpCacheField->GetItemCount(); nItemIdx < nItemCount; ++nItemIdx )
946 maItemList.AppendNewRecord( new XclExpPTItem( *mpCacheField, nItemIdx ) );
947 maFieldInfo.mnItemCount = static_cast< sal_uInt16 >( maItemList.GetSize() );
950 // data access ----------------------------------------------------------------
952 OUString XclExpPTField::GetFieldName() const
954 return mpCacheField ? mpCacheField->GetFieldName() : OUString();
957 sal_uInt16 XclExpPTField::GetLastDataInfoIndex() const
959 OSL_ENSURE( !maDataInfoVec.empty(), "XclExpPTField::GetLastDataInfoIndex - no data info found" );
960 // will return 0xFFFF for empty vector -> ok
961 return static_cast< sal_uInt16 >( maDataInfoVec.size() - 1 );
964 sal_uInt16 XclExpPTField::GetItemIndex( std::u16string_view rName, sal_uInt16 nDefaultIdx ) const
966 for( size_t nPos = 0, nSize = maItemList.GetSize(); nPos < nSize; ++nPos )
967 if( maItemList.GetRecord( nPos )->GetItemName() == rName )
968 return static_cast< sal_uInt16 >( nPos );
969 return nDefaultIdx;
972 // fill data --------------------------------------------------------------
975 * Calc's subtotal names are escaped with backslashes ('\'), while Excel's
976 * are not escaped at all.
978 static OUString lcl_convertCalcSubtotalName(const OUString& rName)
980 OUStringBuffer aBuf;
981 const sal_Unicode* p = rName.getStr();
982 sal_Int32 n = rName.getLength();
983 bool bEscaped = false;
984 for (sal_Int32 i = 0; i < n; ++i)
986 const sal_Unicode c = p[i];
987 if (!bEscaped && c == '\\')
989 bEscaped = true;
990 continue;
993 aBuf.append(c);
994 bEscaped = false;
996 return aBuf.makeStringAndClear();
999 void XclExpPTField::SetPropertiesFromDim( const ScDPSaveDimension& rSaveDim )
1001 // orientation
1002 DataPilotFieldOrientation eOrient = rSaveDim.GetOrientation();
1003 OSL_ENSURE( eOrient != DataPilotFieldOrientation_DATA, "XclExpPTField::SetPropertiesFromDim - called for data field" );
1004 maFieldInfo.AddApiOrient( eOrient );
1006 // show empty items (#i115659# GetShowEmpty() is not valid if HasShowEmpty() returns false, default is false then)
1007 ::set_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_SHOWALL, rSaveDim.HasShowEmpty() && rSaveDim.GetShowEmpty() );
1009 // visible name
1010 const std::optional<OUString> & pLayoutName = rSaveDim.GetLayoutName();
1011 if (pLayoutName && *pLayoutName != GetFieldName())
1012 maFieldInfo.SetVisName(*pLayoutName);
1014 const std::optional<OUString> & pSubtotalName = rSaveDim.GetSubtotalName();
1015 if (pSubtotalName)
1017 OUString aSubName = lcl_convertCalcSubtotalName(*pSubtotalName);
1018 maFieldExtInfo.mpFieldTotalName = aSubName;
1021 // subtotals
1022 XclPTSubtotalVec aSubtotals;
1023 aSubtotals.reserve( static_cast< size_t >( rSaveDim.GetSubTotalsCount() ) );
1024 for( tools::Long nSubtIdx = 0, nSubtCount = rSaveDim.GetSubTotalsCount(); nSubtIdx < nSubtCount; ++nSubtIdx )
1025 aSubtotals.push_back( rSaveDim.GetSubTotalFunc( nSubtIdx ) );
1026 maFieldInfo.SetSubtotals( aSubtotals );
1028 // sorting
1029 if( const DataPilotFieldSortInfo* pSortInfo = rSaveDim.GetSortInfo() )
1031 maFieldExtInfo.SetApiSortMode( pSortInfo->Mode );
1032 if( pSortInfo->Mode == css::sheet::DataPilotFieldSortMode::DATA )
1033 maFieldExtInfo.mnSortField = mrPTable.GetDataFieldIndex( pSortInfo->Field, EXC_SXVDEX_SORT_OWN );
1034 ::set_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_SORT_ASC, pSortInfo->IsAscending );
1037 // auto show
1038 if( const DataPilotFieldAutoShowInfo* pShowInfo = rSaveDim.GetAutoShowInfo() )
1040 ::set_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_AUTOSHOW, pShowInfo->IsEnabled );
1041 maFieldExtInfo.SetApiAutoShowMode( pShowInfo->ShowItemsMode );
1042 maFieldExtInfo.SetApiAutoShowCount( pShowInfo->ItemCount );
1043 maFieldExtInfo.mnShowField = mrPTable.GetDataFieldIndex( pShowInfo->DataField, EXC_SXVDEX_SHOW_NONE );
1046 // layout
1047 if( const DataPilotFieldLayoutInfo* pLayoutInfo = rSaveDim.GetLayoutInfo() )
1049 maFieldExtInfo.SetApiLayoutMode( pLayoutInfo->LayoutMode );
1050 ::set_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_LAYOUT_BLANK, pLayoutInfo->AddEmptyLines );
1053 // special page field properties
1054 if( eOrient == DataPilotFieldOrientation_PAGE )
1056 maPageInfo.mnField = GetFieldIndex();
1057 maPageInfo.mnSelItem = EXC_SXPI_ALLITEMS;
1060 // item properties
1061 const ScDPSaveDimension::MemberList &rMembers = rSaveDim.GetMembers();
1062 for (const auto& pMember : rMembers)
1063 if( XclExpPTItem* pItem = GetItemAcc( pMember->GetName() ) )
1064 pItem->SetPropertiesFromMember( *pMember );
1067 void XclExpPTField::SetDataPropertiesFromDim( const ScDPSaveDimension& rSaveDim )
1069 maDataInfoVec.emplace_back( );
1070 XclPTDataFieldInfo& rDataInfo = maDataInfoVec.back();
1071 rDataInfo.mnField = GetFieldIndex();
1073 // orientation
1074 maFieldInfo.AddApiOrient( DataPilotFieldOrientation_DATA );
1076 // aggregation function
1077 ScGeneralFunction eFunc = rSaveDim.GetFunction();
1078 rDataInfo.SetApiAggFunc( eFunc );
1080 // visible name
1081 const std::optional<OUString> & pVisName = rSaveDim.GetLayoutName();
1082 if (pVisName)
1083 rDataInfo.SetVisName(*pVisName);
1084 else
1085 rDataInfo.SetVisName( lclGetDataFieldCaption( GetFieldName(), eFunc ) );
1087 // result field reference
1088 if( const DataPilotFieldReference* pFieldRef = rSaveDim.GetReferenceValue() )
1090 rDataInfo.SetApiRefType( pFieldRef->ReferenceType );
1091 rDataInfo.SetApiRefItemType( pFieldRef->ReferenceItemType );
1092 if( const XclExpPTField* pRefField = mrPTable.GetField( pFieldRef->ReferenceField ) )
1094 rDataInfo.mnRefField = pRefField->GetFieldIndex();
1095 if( pFieldRef->ReferenceItemType == css::sheet::DataPilotFieldReferenceItemType::NAMED )
1096 rDataInfo.mnRefItem = pRefField->GetItemIndex( pFieldRef->ReferenceItemName, 0 );
1101 void XclExpPTField::AppendSubtotalItems()
1103 if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_DEFAULT ) AppendSubtotalItem( EXC_SXVI_TYPE_DEFAULT );
1104 if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_SUM ) AppendSubtotalItem( EXC_SXVI_TYPE_SUM );
1105 if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_COUNT ) AppendSubtotalItem( EXC_SXVI_TYPE_COUNT );
1106 if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_AVERAGE ) AppendSubtotalItem( EXC_SXVI_TYPE_AVERAGE );
1107 if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_MAX ) AppendSubtotalItem( EXC_SXVI_TYPE_MAX );
1108 if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_MIN ) AppendSubtotalItem( EXC_SXVI_TYPE_MIN );
1109 if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_PROD ) AppendSubtotalItem( EXC_SXVI_TYPE_PROD );
1110 if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_COUNTNUM ) AppendSubtotalItem( EXC_SXVI_TYPE_COUNTNUM );
1111 if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_STDDEV ) AppendSubtotalItem( EXC_SXVI_TYPE_STDDEV );
1112 if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_STDDEVP ) AppendSubtotalItem( EXC_SXVI_TYPE_STDDEVP );
1113 if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_VAR ) AppendSubtotalItem( EXC_SXVI_TYPE_VAR );
1114 if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_VARP ) AppendSubtotalItem( EXC_SXVI_TYPE_VARP );
1117 // records --------------------------------------------------------------------
1119 void XclExpPTField::WriteSxpiEntry( XclExpStream& rStrm ) const
1121 rStrm << maPageInfo;
1124 void XclExpPTField::WriteSxdi( XclExpStream& rStrm, sal_uInt16 nDataInfoIdx ) const
1126 OSL_ENSURE( nDataInfoIdx < maDataInfoVec.size(), "XclExpPTField::WriteSxdi - data field not found" );
1127 if( nDataInfoIdx < maDataInfoVec.size() )
1129 rStrm.StartRecord( EXC_ID_SXDI, 12 );
1130 rStrm << maDataInfoVec[ nDataInfoIdx ];
1131 rStrm.EndRecord();
1135 void XclExpPTField::Save( XclExpStream& rStrm )
1137 // SXVD
1138 WriteSxvd( rStrm );
1139 // list of SXVI records
1140 maItemList.Save( rStrm );
1141 // SXVDEX
1142 WriteSxvdex( rStrm );
1145 // private --------------------------------------------------------------------
1147 XclExpPTItem* XclExpPTField::GetItemAcc( std::u16string_view rName )
1149 XclExpPTItem* pItem = nullptr;
1150 for( size_t nPos = 0, nSize = maItemList.GetSize(); !pItem && (nPos < nSize); ++nPos )
1151 if( maItemList.GetRecord( nPos )->GetItemName() == rName )
1152 pItem = maItemList.GetRecord( nPos );
1153 return pItem;
1156 void XclExpPTField::AppendSubtotalItem( sal_uInt16 nItemType )
1158 maItemList.AppendNewRecord( new XclExpPTItem( nItemType, EXC_SXVI_DEFAULT_CACHE ) );
1159 ++maFieldInfo.mnItemCount;
1162 void XclExpPTField::WriteSxvd( XclExpStream& rStrm ) const
1164 rStrm.StartRecord( EXC_ID_SXVD, 10 );
1165 rStrm << maFieldInfo;
1166 rStrm.EndRecord();
1169 void XclExpPTField::WriteSxvdex( XclExpStream& rStrm ) const
1171 rStrm.StartRecord( EXC_ID_SXVDEX, 20 );
1172 rStrm << maFieldExtInfo;
1173 rStrm.EndRecord();
1176 XclExpPivotTable::XclExpPivotTable( const XclExpRoot& rRoot, const ScDPObject& rDPObj, const XclExpPivotCache& rPCache ) :
1177 XclExpRoot( rRoot ),
1178 mrPCache( rPCache ),
1179 maDataOrientField( *this, EXC_SXIVD_DATA ),
1180 mnOutScTab( 0 ),
1181 mbValid( false ),
1182 mbFilterBtn( false )
1184 const ScRange& rOutScRange = rDPObj.GetOutRange();
1185 if( !GetAddressConverter().ConvertRange( maPTInfo.maOutXclRange, rOutScRange, true ) )
1186 return;
1188 // DataPilot properties -----------------------------------------------
1190 // pivot table properties from DP object
1191 mnOutScTab = rOutScRange.aStart.Tab();
1192 maPTInfo.maTableName = rDPObj.GetName();
1193 maPTInfo.mnCacheIdx = mrPCache.GetCacheIndex();
1195 maPTViewEx9Info.Init( rDPObj );
1197 const ScDPSaveData* pSaveData = rDPObj.GetSaveData();
1198 if( !pSaveData )
1199 return;
1201 // additional properties from ScDPSaveData
1202 SetPropertiesFromDP( *pSaveData );
1204 // loop over all dimensions ---------------------------------------
1206 /* 1) Default-construct all pivot table fields for all pivot cache fields. */
1207 for( sal_uInt16 nFieldIdx = 0, nFieldCount = mrPCache.GetFieldCount(); nFieldIdx < nFieldCount; ++nFieldIdx )
1208 maFieldList.AppendNewRecord( new XclExpPTField( *this, nFieldIdx ) );
1210 const ScDPSaveData::DimsType& rDimList = pSaveData->GetDimensions();
1212 /* 2) First process all data dimensions, they are needed for extended
1213 settings of row/column/page fields (sorting/auto show). */
1214 for (auto const& iter : rDimList)
1216 if (iter->GetOrientation() == DataPilotFieldOrientation_DATA)
1217 SetDataFieldPropertiesFromDim(*iter);
1220 /* 3) Row/column/page/hidden fields. */
1221 for (auto const& iter : rDimList)
1223 if (iter->GetOrientation() != DataPilotFieldOrientation_DATA)
1224 SetFieldPropertiesFromDim(*iter);
1227 // Finalize -------------------------------------------------------
1229 Finalize();
1230 mbValid = true;
1233 const XclExpPCField* XclExpPivotTable::GetCacheField( sal_uInt16 nCacheIdx ) const
1235 return mrPCache.GetField( nCacheIdx );
1238 const XclExpPTField* XclExpPivotTable::GetField( sal_uInt16 nFieldIdx ) const
1240 return (nFieldIdx == EXC_SXIVD_DATA) ? &maDataOrientField : maFieldList.GetRecord( nFieldIdx );
1243 const XclExpPTField* XclExpPivotTable::GetField( std::u16string_view rName ) const
1245 return const_cast< XclExpPivotTable* >( this )->GetFieldAcc( rName );
1248 sal_uInt16 XclExpPivotTable::GetDataFieldIndex( const OUString& rName, sal_uInt16 nDefaultIdx ) const
1250 auto aIt = std::find_if(maDataFields.begin(), maDataFields.end(),
1251 [this, &rName](const XclPTDataFieldPos& rDataField) {
1252 const XclExpPTField* pField = GetField( rDataField.first );
1253 return pField && pField->GetFieldName() == rName;
1255 if (aIt != maDataFields.end())
1256 return static_cast< sal_uInt16 >( std::distance(maDataFields.begin(), aIt) );
1257 return nDefaultIdx;
1260 void XclExpPivotTable::Save( XclExpStream& rStrm )
1262 if( !mbValid )
1263 return;
1265 // SXVIEW
1266 WriteSxview( rStrm );
1267 // pivot table fields (SXVD, SXVDEX, and item records)
1268 maFieldList.Save( rStrm );
1269 // SXIVD records for row and column fields
1270 WriteSxivd( rStrm, maRowFields );
1271 WriteSxivd( rStrm, maColFields );
1272 // SXPI
1273 WriteSxpi( rStrm );
1274 // list of SXDI records containing data field info
1275 WriteSxdiList( rStrm );
1276 // SXLI records
1277 WriteSxli( rStrm, maPTInfo.mnDataRows, maPTInfo.mnRowFields );
1278 WriteSxli( rStrm, maPTInfo.mnDataCols, maPTInfo.mnColFields );
1279 // SXEX
1280 WriteSxex( rStrm );
1281 // QSISXTAG
1282 WriteQsiSxTag( rStrm );
1283 // SXVIEWEX9
1284 WriteSxViewEx9( rStrm );
1287 XclExpPTField* XclExpPivotTable::GetFieldAcc( std::u16string_view rName )
1289 XclExpPTField* pField = nullptr;
1290 for( size_t nPos = 0, nSize = maFieldList.GetSize(); !pField && (nPos < nSize); ++nPos )
1291 if( maFieldList.GetRecord( nPos )->GetFieldName() == rName )
1292 pField = maFieldList.GetRecord( nPos );
1293 return pField;
1296 XclExpPTField* XclExpPivotTable::GetFieldAcc( const ScDPSaveDimension& rSaveDim )
1298 // data field orientation field?
1299 if( rSaveDim.IsDataLayout() )
1300 return &maDataOrientField;
1302 // a real dimension
1303 OUString aFieldName = ScDPUtil::getSourceDimensionName(rSaveDim.GetName());
1304 return aFieldName.isEmpty() ? nullptr : GetFieldAcc(aFieldName);
1307 // fill data --------------------------------------------------------------
1309 void XclExpPivotTable::SetPropertiesFromDP( const ScDPSaveData& rSaveData )
1311 ::set_flag( maPTInfo.mnFlags, EXC_SXVIEW_ROWGRAND, rSaveData.GetRowGrand() );
1312 ::set_flag( maPTInfo.mnFlags, EXC_SXVIEW_COLGRAND, rSaveData.GetColumnGrand() );
1313 ::set_flag( maPTExtInfo.mnFlags, EXC_SXEX_DRILLDOWN, rSaveData.GetDrillDown() );
1314 mbFilterBtn = rSaveData.GetFilterButton();
1315 const ScDPSaveDimension* pDim = rSaveData.GetExistingDataLayoutDimension();
1317 if (pDim && pDim->GetLayoutName())
1318 maPTInfo.maDataName = *pDim->GetLayoutName();
1319 else
1320 maPTInfo.maDataName = ScResId(STR_PIVOT_DATA);
1323 void XclExpPivotTable::SetFieldPropertiesFromDim( const ScDPSaveDimension& rSaveDim )
1325 XclExpPTField* pField = GetFieldAcc( rSaveDim );
1326 if(!pField)
1327 return;
1329 // field properties
1330 pField->SetPropertiesFromDim( rSaveDim );
1332 // update the corresponding field position list
1333 DataPilotFieldOrientation eOrient = rSaveDim.GetOrientation();
1334 sal_uInt16 nFieldIdx = pField->GetFieldIndex();
1335 bool bDataLayout = nFieldIdx == EXC_SXIVD_DATA;
1336 bool bMultiData = maDataFields.size() > 1;
1338 if( bDataLayout && !bMultiData )
1339 return;
1341 switch( eOrient )
1343 case DataPilotFieldOrientation_ROW:
1344 maRowFields.push_back( nFieldIdx );
1345 if( bDataLayout )
1346 maPTInfo.mnDataAxis = EXC_SXVD_AXIS_ROW;
1347 break;
1348 case DataPilotFieldOrientation_COLUMN:
1349 maColFields.push_back( nFieldIdx );
1350 if( bDataLayout )
1351 maPTInfo.mnDataAxis = EXC_SXVD_AXIS_COL;
1352 break;
1353 case DataPilotFieldOrientation_PAGE:
1354 maPageFields.push_back( nFieldIdx );
1355 OSL_ENSURE( !bDataLayout, "XclExpPivotTable::SetFieldPropertiesFromDim - wrong orientation for data fields" );
1356 break;
1357 case DataPilotFieldOrientation_DATA:
1358 OSL_FAIL( "XclExpPivotTable::SetFieldPropertiesFromDim - called for data field" );
1359 break;
1360 default:;
1364 void XclExpPivotTable::SetDataFieldPropertiesFromDim( const ScDPSaveDimension& rSaveDim )
1366 if( XclExpPTField* pField = GetFieldAcc( rSaveDim ) )
1368 // field properties
1369 pField->SetDataPropertiesFromDim( rSaveDim );
1370 // update the data field position list
1371 maDataFields.emplace_back( pField->GetFieldIndex(), pField->GetLastDataInfoIndex() );
1375 void XclExpPivotTable::Finalize()
1377 // field numbers
1378 maPTInfo.mnFields = static_cast< sal_uInt16 >( maFieldList.GetSize() );
1379 maPTInfo.mnRowFields = static_cast< sal_uInt16 >( maRowFields.size() );
1380 maPTInfo.mnColFields = static_cast< sal_uInt16 >( maColFields.size() );
1381 maPTInfo.mnPageFields = static_cast< sal_uInt16 >( maPageFields.size() );
1382 maPTInfo.mnDataFields = static_cast< sal_uInt16 >( maDataFields.size() );
1384 maPTExtInfo.mnPagePerRow = maPTInfo.mnPageFields;
1385 maPTExtInfo.mnPagePerCol = (maPTInfo.mnPageFields > 0) ? 1 : 0;
1387 // subtotal items
1388 for( size_t nPos = 0, nSize = maFieldList.GetSize(); nPos < nSize; ++nPos )
1389 maFieldList.GetRecord( nPos )->AppendSubtotalItems();
1391 // find data field orientation field
1392 maPTInfo.mnDataPos = EXC_SXVIEW_DATALAST;
1393 const ScfUInt16Vec* pFieldVec = nullptr;
1394 switch( maPTInfo.mnDataAxis )
1396 case EXC_SXVD_AXIS_ROW: pFieldVec = &maRowFields; break;
1397 case EXC_SXVD_AXIS_COL: pFieldVec = &maColFields; break;
1400 if( pFieldVec && !pFieldVec->empty() && (pFieldVec->back() != EXC_SXIVD_DATA) )
1402 ScfUInt16Vec::const_iterator aIt = ::std::find( pFieldVec->begin(), pFieldVec->end(), EXC_SXIVD_DATA );
1403 if( aIt != pFieldVec->end() )
1404 maPTInfo.mnDataPos = static_cast< sal_uInt16 >( std::distance(pFieldVec->begin(), aIt) );
1407 // single data field is always row oriented
1408 if( maPTInfo.mnDataAxis == EXC_SXVD_AXIS_NONE )
1409 maPTInfo.mnDataAxis = EXC_SXVD_AXIS_ROW;
1411 // update output range (initialized in ctor)
1412 sal_uInt16& rnXclCol1 = maPTInfo.maOutXclRange.maFirst.mnCol;
1413 sal_uInt32& rnXclRow1 = maPTInfo.maOutXclRange.maFirst.mnRow;
1414 sal_uInt16& rnXclCol2 = maPTInfo.maOutXclRange.maLast.mnCol;
1415 sal_uInt32& rnXclRow2 = maPTInfo.maOutXclRange.maLast.mnRow;
1416 // exclude page fields from output range
1417 rnXclRow1 = rnXclRow1 + maPTInfo.mnPageFields;
1418 // exclude filter button from output range
1419 if( mbFilterBtn )
1420 ++rnXclRow1;
1421 // exclude empty row between (filter button and/or page fields) and table
1422 if( mbFilterBtn || maPTInfo.mnPageFields )
1423 ++rnXclRow1;
1425 // data area
1426 sal_uInt16& rnDataXclCol = maPTInfo.maDataXclPos.mnCol;
1427 sal_uInt32& rnDataXclRow = maPTInfo.maDataXclPos.mnRow;
1428 rnDataXclCol = rnXclCol1 + maPTInfo.mnRowFields;
1429 rnDataXclRow = rnXclRow1 + maPTInfo.mnColFields + 1;
1430 if( maDataFields.empty() )
1431 ++rnDataXclRow;
1433 bool bExtraHeaderRow = (0 == maPTViewEx9Info.mnGridLayout && maPTInfo.mnColFields == 0);
1434 if (bExtraHeaderRow)
1435 // Insert an extra row only when there is no column field.
1436 ++rnDataXclRow;
1438 rnXclCol2 = ::std::max( rnXclCol2, rnDataXclCol );
1439 rnXclRow2 = ::std::max( rnXclRow2, rnDataXclRow );
1440 maPTInfo.mnDataCols = rnXclCol2 - rnDataXclCol + 1;
1441 maPTInfo.mnDataRows = rnXclRow2 - rnDataXclRow + 1;
1443 // first heading
1444 maPTInfo.mnFirstHeadRow = rnXclRow1 + 1;
1445 if (bExtraHeaderRow)
1446 maPTInfo.mnFirstHeadRow += 1;
1449 // records ----------------------------------------------------------------
1451 void XclExpPivotTable::WriteSxview( XclExpStream& rStrm ) const
1453 rStrm.StartRecord( EXC_ID_SXVIEW, 46 + maPTInfo.maTableName.getLength() + maPTInfo.maDataName.getLength() );
1454 rStrm << maPTInfo;
1455 rStrm.EndRecord();
1458 void XclExpPivotTable::WriteSxivd( XclExpStream& rStrm, const ScfUInt16Vec& rFields )
1460 if( !rFields.empty() )
1462 rStrm.StartRecord( EXC_ID_SXIVD, rFields.size() * 2 );
1463 for( const auto& rField : rFields )
1464 rStrm << rField;
1465 rStrm.EndRecord();
1469 void XclExpPivotTable::WriteSxpi( XclExpStream& rStrm ) const
1471 if( !maPageFields.empty() )
1473 rStrm.StartRecord( EXC_ID_SXPI, maPageFields.size() * 6 );
1474 rStrm.SetSliceSize( 6 );
1475 for( const auto& rPageField : maPageFields )
1477 XclExpPTFieldRef xField = maFieldList.GetRecord( rPageField );
1478 if( xField )
1479 xField->WriteSxpiEntry( rStrm );
1481 rStrm.EndRecord();
1485 void XclExpPivotTable::WriteSxdiList( XclExpStream& rStrm ) const
1487 for( const auto& [rFieldIdx, rDataInfoIdx] : maDataFields )
1489 XclExpPTFieldRef xField = maFieldList.GetRecord( rFieldIdx );
1490 if( xField )
1491 xField->WriteSxdi( rStrm, rDataInfoIdx );
1495 void XclExpPivotTable::WriteSxli( XclExpStream& rStrm, sal_uInt16 nLineCount, sal_uInt16 nIndexCount )
1497 if( nLineCount <= 0 )
1498 return;
1500 std::size_t nLineSize = 8 + 2 * nIndexCount;
1501 rStrm.StartRecord( EXC_ID_SXLI, nLineSize * nLineCount );
1503 /* Excel expects the records to be filled completely, do not
1504 set a segment size... */
1505 // rStrm.SetSliceSize( nLineSize );
1507 for( sal_uInt16 nLine = 0; nLine < nLineCount; ++nLine )
1509 // Excel XP needs a partly initialized SXLI record
1510 rStrm << sal_uInt16( 0 ) // number of equal index entries
1511 << EXC_SXVI_TYPE_DATA
1512 << nIndexCount
1513 << EXC_SXLI_DEFAULTFLAGS;
1514 rStrm.WriteZeroBytes( 2 * nIndexCount );
1516 rStrm.EndRecord();
1519 void XclExpPivotTable::WriteSxex( XclExpStream& rStrm ) const
1521 rStrm.StartRecord( EXC_ID_SXEX, 24 );
1522 rStrm << maPTExtInfo;
1523 rStrm.EndRecord();
1526 void XclExpPivotTable::WriteQsiSxTag( XclExpStream& rStrm ) const
1528 rStrm.StartRecord( 0x0802, 32 );
1530 sal_uInt16 const nRecordType = 0x0802;
1531 sal_uInt16 const nDummyFlags = 0x0000;
1532 sal_uInt16 const nTableType = 1; // 0 = query table : 1 = pivot table
1534 rStrm << nRecordType << nDummyFlags << nTableType;
1536 // General flags
1537 sal_uInt16 const nFlags = 0x0001;
1538 #if 0
1539 // for doc purpose
1540 sal_uInt16 nFlags = 0x0000;
1541 bool bEnableRefresh = true;
1542 bool bPCacheInvalid = false;
1543 bool bOlapPTReport = false;
1545 if (bEnableRefresh) nFlags |= 0x0001;
1546 if (bPCacheInvalid) nFlags |= 0x0002;
1547 if (bOlapPTReport) nFlags |= 0x0004;
1548 #endif
1549 rStrm << nFlags;
1551 // Feature-specific options. The value differs depending on the table
1552 // type, but we assume the table type is always pivot table.
1553 sal_uInt32 const nOptions = 0x00000000;
1554 #if 0
1555 // documentation for which bit is for what
1556 bool bNoStencil = false;
1557 bool bHideTotal = false;
1558 bool bEmptyRows = false;
1559 bool bEmptyCols = false;
1560 if (bNoStencil) nOptions |= 0x00000001;
1561 if (bHideTotal) nOptions |= 0x00000002;
1562 if (bEmptyRows) nOptions |= 0x00000008;
1563 if (bEmptyCols) nOptions |= 0x00000010;
1564 #endif
1565 rStrm << nOptions;
1567 sal_uInt8 eXclVer = 0; // Excel2000
1568 sal_uInt8 const nOffsetBytes = 16;
1569 rStrm << eXclVer // version table last refreshed
1570 << eXclVer // minimum version to refresh
1571 << nOffsetBytes
1572 << eXclVer; // first version created
1574 rStrm << XclExpString(maPTInfo.maTableName);
1575 rStrm << static_cast<sal_uInt16>(0x0001); // no idea what this is for.
1577 rStrm.EndRecord();
1580 void XclExpPivotTable::WriteSxViewEx9( XclExpStream& rStrm ) const
1582 // Until we sync the autoformat ids only export if using grid header layout
1583 // That could only have been set via xls import so far.
1584 if ( 0 == maPTViewEx9Info.mnGridLayout )
1586 rStrm.StartRecord( EXC_ID_SXVIEWEX9, 17 );
1587 rStrm << maPTViewEx9Info;
1588 rStrm.EndRecord();
1592 namespace {
1594 const SCTAB EXC_PTMGR_PIVOTCACHES = SCTAB_MAX;
1596 /** Record wrapper class to write the pivot caches or pivot tables. */
1597 class XclExpPivotRecWrapper : public XclExpRecordBase
1599 public:
1600 explicit XclExpPivotRecWrapper( XclExpPivotTableManager& rPTMgr, SCTAB nScTab );
1601 virtual void Save( XclExpStream& rStrm ) override;
1602 private:
1603 XclExpPivotTableManager& mrPTMgr;
1604 SCTAB mnScTab;
1607 XclExpPivotRecWrapper::XclExpPivotRecWrapper( XclExpPivotTableManager& rPTMgr, SCTAB nScTab ) :
1608 mrPTMgr( rPTMgr ),
1609 mnScTab( nScTab )
1613 void XclExpPivotRecWrapper::Save( XclExpStream& rStrm )
1615 if( mnScTab == EXC_PTMGR_PIVOTCACHES )
1616 mrPTMgr.WritePivotCaches( rStrm );
1617 else
1618 mrPTMgr.WritePivotTables( rStrm, mnScTab );
1621 } // namespace
1623 XclExpPivotTableManager::XclExpPivotTableManager( const XclExpRoot& rRoot ) :
1624 XclExpRoot( rRoot )
1628 void XclExpPivotTableManager::CreatePivotTables()
1630 if( ScDPCollection* pDPColl = GetDoc().GetDPCollection() )
1631 for( size_t nDPObj = 0, nCount = pDPColl->GetCount(); nDPObj < nCount; ++nDPObj )
1633 ScDPObject& rDPObj = (*pDPColl)[ nDPObj ];
1634 if( const XclExpPivotCache* pPCache = CreatePivotCache( rDPObj ) )
1635 maPTableList.AppendNewRecord( new XclExpPivotTable( GetRoot(), rDPObj, *pPCache ) );
1639 XclExpRecordRef XclExpPivotTableManager::CreatePivotCachesRecord()
1641 return new XclExpPivotRecWrapper( *this, EXC_PTMGR_PIVOTCACHES );
1644 XclExpRecordRef XclExpPivotTableManager::CreatePivotTablesRecord( SCTAB nScTab )
1646 return new XclExpPivotRecWrapper( *this, nScTab );
1649 void XclExpPivotTableManager::WritePivotCaches( XclExpStream& rStrm )
1651 maPCacheList.Save( rStrm );
1654 void XclExpPivotTableManager::WritePivotTables( XclExpStream& rStrm, SCTAB nScTab )
1656 for( size_t nPos = 0, nSize = maPTableList.GetSize(); nPos < nSize; ++nPos )
1658 XclExpPivotTableRef xPTable = maPTableList.GetRecord( nPos );
1659 if( xPTable->GetScTab() == nScTab )
1660 xPTable->Save( rStrm );
1664 const XclExpPivotCache* XclExpPivotTableManager::CreatePivotCache( const ScDPObject& rDPObj )
1666 // try to find a pivot cache with the same data source
1667 /* #i25110# In Excel, the pivot cache contains additional fields
1668 (i.e. grouping info, calculated fields). If the passed DataPilot object
1669 or the found cache contains this data, do not share the cache with
1670 multiple pivot tables. */
1671 if( const ScDPSaveData* pSaveData = rDPObj.GetSaveData() )
1673 const ScDPDimensionSaveData* pDimSaveData = pSaveData->GetExistingDimensionData();
1674 // no dimension save data at all or save data does not contain grouping info
1675 if( !pDimSaveData || !pDimSaveData->HasGroupDimensions() )
1677 // check all existing pivot caches
1678 for( size_t nPos = 0, nSize = maPCacheList.GetSize(); nPos < nSize; ++nPos )
1680 XclExpPivotCache* pPCache = maPCacheList.GetRecord( nPos );
1681 // pivot cache does not have grouping info and source data is equal
1682 if( !pPCache->HasAddFields() && pPCache->HasEqualDataSource( rDPObj ) )
1683 return pPCache;
1688 // create a new pivot cache
1689 sal_uInt16 nNewCacheIdx = static_cast< sal_uInt16 >( maPCacheList.GetSize() );
1690 XclExpPivotCacheRef xNewPCache = new XclExpPivotCache( GetRoot(), rDPObj, nNewCacheIdx );
1691 if( xNewPCache->IsValid() )
1693 maPCacheList.AppendRecord( xNewPCache.get() );
1694 return xNewPCache.get();
1697 return nullptr;
1700 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */