1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 <xipivot.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>
28 #include <tools/datetime.hxx>
29 #include <svl/intitem.hxx>
30 #include <svl/numformat.hxx>
31 #include <sal/log.hxx>
32 #include <sot/storage.hxx>
33 #include <comphelper/configuration.hxx>
35 #include <document.hxx>
36 #include <formulacell.hxx>
38 #include <dpdimsave.hxx>
39 #include <dpobject.hxx>
40 #include <dpshttab.hxx>
41 #include <dpoutputgeometry.hxx>
42 #include <scitems.hxx>
45 #include <xltracer.hxx>
46 #include <xistream.hxx>
47 #include <xihelper.hxx>
49 #include <xiescher.hxx>
51 //TODO ExcelToSc usage
52 #include <excform.hxx>
53 #include <documentimport.hxx>
57 using namespace com::sun::star
;
59 using ::com::sun::star::sheet::DataPilotFieldOrientation_DATA
;
60 using ::com::sun::star::sheet::DataPilotFieldSortInfo
;
61 using ::com::sun::star::sheet::DataPilotFieldAutoShowInfo
;
62 using ::com::sun::star::sheet::DataPilotFieldLayoutInfo
;
63 using ::com::sun::star::sheet::DataPilotFieldReference
;
68 XclImpPCItem::XclImpPCItem( XclImpStream
& rStrm
)
70 switch( rStrm
.GetRecId() )
72 case EXC_ID_SXDOUBLE
: ReadSxdouble( rStrm
); break;
73 case EXC_ID_SXBOOLEAN
: ReadSxboolean( rStrm
); break;
74 case EXC_ID_SXERROR
: ReadSxerror( rStrm
); break;
75 case EXC_ID_SXINTEGER
: ReadSxinteger( rStrm
); break;
76 case EXC_ID_SXSTRING
: ReadSxstring( rStrm
); break;
77 case EXC_ID_SXDATETIME
: ReadSxdatetime( rStrm
); break;
78 case EXC_ID_SXEMPTY
: ReadSxempty( rStrm
); break;
79 default: OSL_FAIL( "XclImpPCItem::XclImpPCItem - unknown record id" );
85 void lclSetValue( XclImpRoot
& rRoot
, const ScAddress
& rScPos
, double fValue
, SvNumFormatType nFormatType
)
87 ScDocumentImport
& rDoc
= rRoot
.GetDocImport();
88 rDoc
.setNumericCell(rScPos
, fValue
);
89 sal_uInt32 nScNumFmt
= rRoot
.GetFormatter().GetStandardFormat( nFormatType
, rRoot
.GetDocLanguage() );
90 rDoc
.getDoc().ApplyAttr(
91 rScPos
.Col(), rScPos
.Row(), rScPos
.Tab(), SfxUInt32Item(ATTR_VALUE_FORMAT
, nScNumFmt
));
96 void XclImpPCItem::WriteToSource( XclImpRoot
& rRoot
, const ScAddress
& rScPos
) const
98 ScDocumentImport
& rDoc
= rRoot
.GetDocImport();
99 if( const OUString
* pText
= GetText() )
100 rDoc
.setStringCell(rScPos
, *pText
);
101 else if( const double* pfValue
= GetDouble() )
102 rDoc
.setNumericCell(rScPos
, *pfValue
);
103 else if( const sal_Int16
* pnValue
= GetInteger() )
104 rDoc
.setNumericCell(rScPos
, *pnValue
);
105 else if( const bool* pbValue
= GetBool() )
106 lclSetValue( rRoot
, rScPos
, *pbValue
? 1.0 : 0.0, SvNumFormatType::LOGICAL
);
107 else if( const DateTime
* pDateTime
= GetDateTime() )
109 // set number format date, time, or date/time, depending on the value
110 double fValue
= rRoot
.GetDoubleFromDateTime( *pDateTime
);
112 double fFrac
= modf( fValue
, &fInt
);
113 SvNumFormatType nFormatType
= ((fFrac
== 0.0) && (fInt
!= 0.0)) ? SvNumFormatType::DATE
:
114 ((fInt
== 0.0) ? SvNumFormatType::TIME
: SvNumFormatType::DATETIME
);
115 lclSetValue( rRoot
, rScPos
, fValue
, nFormatType
);
117 else if( const sal_uInt16
* pnError
= GetError() )
120 sal_uInt8 nErrCode
= static_cast< sal_uInt8
>( *pnError
);
121 std::unique_ptr
<ScTokenArray
> pScTokArr
= rRoot
.GetOldFmlaConverter().GetBoolErr(
122 XclTools::ErrorToEnum( fValue
, true, nErrCode
) );
123 ScFormulaCell
* pCell
= pScTokArr
124 ? new ScFormulaCell(rDoc
.getDoc(), rScPos
, std::move(pScTokArr
))
125 : new ScFormulaCell(rDoc
.getDoc(), rScPos
);
126 pCell
->SetHybridDouble( fValue
);
127 rDoc
.setFormulaCell(rScPos
, pCell
);
131 void XclImpPCItem::ReadSxdouble( XclImpStream
& rStrm
)
133 OSL_ENSURE( rStrm
.GetRecSize() == 8, "XclImpPCItem::ReadSxdouble - wrong record size" );
134 SetDouble( rStrm
.ReadDouble() );
137 void XclImpPCItem::ReadSxboolean( XclImpStream
& rStrm
)
139 OSL_ENSURE( rStrm
.GetRecSize() == 2, "XclImpPCItem::ReadSxboolean - wrong record size" );
140 SetBool( rStrm
.ReaduInt16() != 0 );
143 void XclImpPCItem::ReadSxerror( XclImpStream
& rStrm
)
145 OSL_ENSURE( rStrm
.GetRecSize() == 2, "XclImpPCItem::ReadSxerror - wrong record size" );
146 SetError( rStrm
.ReaduInt16() );
149 void XclImpPCItem::ReadSxinteger( XclImpStream
& rStrm
)
151 OSL_ENSURE( rStrm
.GetRecSize() == 2, "XclImpPCItem::ReadSxinteger - wrong record size" );
152 SetInteger( rStrm
.ReadInt16() );
155 void XclImpPCItem::ReadSxstring( XclImpStream
& rStrm
)
157 OSL_ENSURE( rStrm
.GetRecSize() >= 3, "XclImpPCItem::ReadSxstring - wrong record size" );
158 SetText( rStrm
.ReadUniString() );
161 void XclImpPCItem::ReadSxdatetime( XclImpStream
& rStrm
)
163 OSL_ENSURE( rStrm
.GetRecSize() == 8, "XclImpPCItem::ReadSxdatetime - wrong record size" );
164 sal_uInt16 nYear
, nMonth
;
165 sal_uInt8 nDay
, nHour
, nMin
, nSec
;
166 nYear
= rStrm
.ReaduInt16();
167 nMonth
= rStrm
.ReaduInt16();
168 nDay
= rStrm
.ReaduInt8();
169 nHour
= rStrm
.ReaduInt8();
170 nMin
= rStrm
.ReaduInt8();
171 nSec
= rStrm
.ReaduInt8();
172 SetDateTime( DateTime( Date( nDay
, nMonth
, nYear
), tools::Time( nHour
, nMin
, nSec
) ) );
175 void XclImpPCItem::ReadSxempty( XclImpStream
& rStrm
)
177 OSL_ENSURE( rStrm
.GetRecSize() == 0, "XclImpPCItem::ReadSxempty - wrong record size" );
181 XclImpPCField::XclImpPCField( const XclImpRoot
& rRoot
, XclImpPivotCache
& rPCache
, sal_uInt16 nFieldIdx
) :
182 XclPCField( EXC_PCFIELD_UNKNOWN
, nFieldIdx
),
186 mbNumGroupInfoRead( false )
190 XclImpPCField::~XclImpPCField()
194 // general field/item access --------------------------------------------------
196 const OUString
& XclImpPCField::GetFieldName( const ScfStringVec
& rVisNames
) const
198 if( IsGroupChildField() && (mnFieldIdx
< rVisNames
.size()) )
200 const OUString
& rVisName
= rVisNames
[ mnFieldIdx
];
201 if (!rVisName
.isEmpty())
204 return maFieldInfo
.maName
;
207 const XclImpPCField
* XclImpPCField::GetGroupBaseField() const
209 OSL_ENSURE( IsGroupChildField(), "XclImpPCField::GetGroupBaseField - this field type does not have a base field" );
210 return IsGroupChildField() ? mrPCache
.GetField( maFieldInfo
.mnGroupBase
) : nullptr;
213 const XclImpPCItem
* XclImpPCField::GetItem( sal_uInt16 nItemIdx
) const
215 return (nItemIdx
< maItems
.size()) ? maItems
[ nItemIdx
].get() : nullptr;
218 const XclImpPCItem
* XclImpPCField::GetLimitItem( sal_uInt16 nItemIdx
) const
220 OSL_ENSURE( nItemIdx
< 3, "XclImpPCField::GetLimitItem - invalid item index" );
221 OSL_ENSURE( nItemIdx
< maNumGroupItems
.size(), "XclImpPCField::GetLimitItem - no item found" );
222 return (nItemIdx
< maNumGroupItems
.size()) ? maNumGroupItems
[ nItemIdx
].get() : nullptr;
225 void XclImpPCField::WriteFieldNameToSource( SCCOL nScCol
, SCTAB nScTab
)
227 OSL_ENSURE( HasOrigItems(), "XclImpPCField::WriteFieldNameToSource - only for standard fields" );
228 GetDocImport().setStringCell(ScAddress(nScCol
, 0, nScTab
), maFieldInfo
.maName
);
229 mnSourceScCol
= nScCol
;
232 void XclImpPCField::WriteOrigItemToSource( SCROW nScRow
, SCTAB nScTab
, sal_uInt16 nItemIdx
)
234 if( nItemIdx
< maOrigItems
.size() )
235 maOrigItems
[ nItemIdx
]->WriteToSource( GetRoot(), ScAddress( mnSourceScCol
, nScRow
, nScTab
) );
238 void XclImpPCField::WriteLastOrigItemToSource( SCROW nScRow
, SCTAB nScTab
)
240 if( !maOrigItems
.empty() )
241 maOrigItems
.back()->WriteToSource( GetRoot(), ScAddress( mnSourceScCol
, nScRow
, nScTab
) );
244 // records --------------------------------------------------------------------
246 void XclImpPCField::ReadSxfield( XclImpStream
& rStrm
)
248 rStrm
>> maFieldInfo
;
250 /* Detect the type of this field. This is done very restrictive to detect
251 any unexpected state. */
252 meFieldType
= EXC_PCFIELD_UNKNOWN
;
254 bool bItems
= ::get_flag( maFieldInfo
.mnFlags
, EXC_SXFIELD_HASITEMS
);
255 bool bPostp
= ::get_flag( maFieldInfo
.mnFlags
, EXC_SXFIELD_POSTPONE
);
256 bool bCalced
= ::get_flag( maFieldInfo
.mnFlags
, EXC_SXFIELD_CALCED
);
257 bool bChild
= ::get_flag( maFieldInfo
.mnFlags
, EXC_SXFIELD_HASCHILD
);
258 bool bNum
= ::get_flag( maFieldInfo
.mnFlags
, EXC_SXFIELD_NUMGROUP
);
260 sal_uInt16 nVisC
= maFieldInfo
.mnVisItems
;
261 sal_uInt16 nGroupC
= maFieldInfo
.mnGroupItems
;
262 sal_uInt16 nBaseC
= maFieldInfo
.mnBaseItems
;
263 sal_uInt16 nOrigC
= maFieldInfo
.mnOrigItems
;
264 OSL_ENSURE( nVisC
> 0, "XclImpPCField::ReadSxfield - field without visible items" );
266 sal_uInt16 nType
= maFieldInfo
.mnFlags
& EXC_SXFIELD_DATA_MASK
;
268 (nType
== EXC_SXFIELD_DATA_STR
) ||
269 (nType
== EXC_SXFIELD_DATA_INT
) ||
270 (nType
== EXC_SXFIELD_DATA_DBL
) ||
271 (nType
== EXC_SXFIELD_DATA_STR_INT
) ||
272 (nType
== EXC_SXFIELD_DATA_STR_DBL
) ||
273 (nType
== EXC_SXFIELD_DATA_DATE
) ||
274 (nType
== EXC_SXFIELD_DATA_DATE_EMP
) ||
275 (nType
== EXC_SXFIELD_DATA_DATE_NUM
) ||
276 (nType
== EXC_SXFIELD_DATA_DATE_STR
);
278 (nType
== EXC_SXFIELD_DATA_NONE
);
279 // for now, ignore data type of calculated fields
280 OSL_ENSURE( bCalced
|| bType
|| bTypeNone
, "XclImpPCField::ReadSxfield - unknown item data type" );
282 if( !(nVisC
> 0 || bPostp
) )
285 if( bItems
&& !bPostp
)
289 // 1) standard fields and standard grouping fields
292 // 1a) standard field without grouping
293 if( bType
&& (nGroupC
== 0) && (nBaseC
== 0) && (nOrigC
== nVisC
) )
294 meFieldType
= EXC_PCFIELD_STANDARD
;
296 // 1b) standard grouping field
297 else if( bTypeNone
&& (nGroupC
== nVisC
) && (nBaseC
> 0) && (nOrigC
== 0) )
298 meFieldType
= EXC_PCFIELD_STDGROUP
;
300 // 2) numerical grouping fields
301 else if( (nGroupC
== nVisC
) && (nBaseC
== 0) )
303 // 2a) single num/date grouping field without child grouping field
304 if( !bChild
&& bType
&& (nOrigC
> 0) )
308 case EXC_SXFIELD_DATA_INT
:
309 case EXC_SXFIELD_DATA_DBL
: meFieldType
= EXC_PCFIELD_NUMGROUP
; break;
310 case EXC_SXFIELD_DATA_DATE
: meFieldType
= EXC_PCFIELD_DATEGROUP
; break;
311 default: OSL_FAIL( "XclImpPCField::ReadSxfield - numeric group with wrong data type" );
315 // 2b) first date grouping field with child grouping field
316 else if( bChild
&& (nType
== EXC_SXFIELD_DATA_DATE
) && (nOrigC
> 0) )
317 meFieldType
= EXC_PCFIELD_DATEGROUP
;
319 // 2c) additional date grouping field
320 else if( bTypeNone
&& (nOrigC
== 0) )
321 meFieldType
= EXC_PCFIELD_DATECHILD
;
323 OSL_ENSURE( meFieldType
!= EXC_PCFIELD_UNKNOWN
, "XclImpPCField::ReadSxfield - invalid standard or grouped field" );
326 // 3) calculated field
329 if( !bChild
&& !bNum
&& (nGroupC
== 0) && (nBaseC
== 0) && (nOrigC
== 0) )
330 meFieldType
= EXC_PCFIELD_CALCED
;
331 OSL_ENSURE( meFieldType
== EXC_PCFIELD_CALCED
, "XclImpPCField::ReadSxfield - invalid calculated field" );
335 else if( !bItems
&& bPostp
)
337 // 4) standard field with postponed items
338 if( !bCalced
&& !bChild
&& !bNum
&& bType
&& (nGroupC
== 0) && (nBaseC
== 0) && (nOrigC
== 0) )
339 meFieldType
= EXC_PCFIELD_STANDARD
;
340 OSL_ENSURE( meFieldType
== EXC_PCFIELD_STANDARD
, "XclImpPCField::ReadSxfield - invalid postponed field" );
344 void XclImpPCField::ReadItem( XclImpStream
& rStrm
)
346 OSL_ENSURE( HasInlineItems() || HasPostponedItems(), "XclImpPCField::ReadItem - field does not expect items" );
349 XclImpPCItemRef xItem
= std::make_shared
<XclImpPCItem
>( rStrm
);
351 // try to insert into an item list
352 if( mbNumGroupInfoRead
)
354 // there are 3 items after SXNUMGROUP that contain grouping limits and step count
355 if( maNumGroupItems
.size() < 3 )
356 maNumGroupItems
.push_back( xItem
);
358 maOrigItems
.push_back( xItem
);
360 else if( HasInlineItems() || HasPostponedItems() )
362 maItems
.push_back( xItem
);
363 // visible item is original item in standard fields
364 if( IsStandardField() )
365 maOrigItems
.push_back( xItem
);
369 void XclImpPCField::ReadSxnumgroup( XclImpStream
& rStrm
)
371 OSL_ENSURE( IsNumGroupField() || IsDateGroupField(), "XclImpPCField::ReadSxnumgroup - SXNUMGROUP outside numeric grouping field" );
372 OSL_ENSURE( !mbNumGroupInfoRead
, "XclImpPCField::ReadSxnumgroup - multiple SXNUMGROUP records" );
373 OSL_ENSURE( maItems
.size() == maFieldInfo
.mnGroupItems
, "XclImpPCField::ReadSxnumgroup - SXNUMGROUP out of record order" );
374 rStrm
>> maNumGroupInfo
;
375 mbNumGroupInfoRead
= IsNumGroupField() || IsDateGroupField();
378 void XclImpPCField::ReadSxgroupinfo( XclImpStream
& rStrm
)
380 OSL_ENSURE( IsStdGroupField(), "XclImpPCField::ReadSxgroupinfo - SXGROUPINFO outside grouping field" );
381 OSL_ENSURE( maGroupOrder
.empty(), "XclImpPCField::ReadSxgroupinfo - multiple SXGROUPINFO records" );
382 OSL_ENSURE( maItems
.size() == maFieldInfo
.mnGroupItems
, "XclImpPCField::ReadSxgroupinfo - SXGROUPINFO out of record order" );
383 OSL_ENSURE( (rStrm
.GetRecLeft() / 2) == maFieldInfo
.mnBaseItems
, "XclImpPCField::ReadSxgroupinfo - wrong SXGROUPINFO size" );
384 maGroupOrder
.clear();
385 size_t nSize
= rStrm
.GetRecLeft() / 2;
386 maGroupOrder
.resize( nSize
, 0 );
387 for( size_t nIdx
= 0; nIdx
< nSize
; ++nIdx
)
388 maGroupOrder
[ nIdx
] = rStrm
.ReaduInt16();
391 // grouping -------------------------------------------------------------------
393 void XclImpPCField::ConvertGroupField( ScDPSaveData
& rSaveData
, const ScfStringVec
& rVisNames
) const
395 if (!GetFieldName(rVisNames
).isEmpty())
397 if( IsStdGroupField() )
398 ConvertStdGroupField( rSaveData
, rVisNames
);
399 else if( IsNumGroupField() )
400 ConvertNumGroupField( rSaveData
, rVisNames
);
401 else if( IsDateGroupField() )
402 ConvertDateGroupField( rSaveData
, rVisNames
);
406 // private --------------------------------------------------------------------
408 void XclImpPCField::ConvertStdGroupField( ScDPSaveData
& rSaveData
, const ScfStringVec
& rVisNames
) const
410 const XclImpPCField
* pBaseField
= GetGroupBaseField();
414 const OUString
& rBaseFieldName
= pBaseField
->GetFieldName( rVisNames
);
415 if( rBaseFieldName
.isEmpty() )
418 // *** create a ScDPSaveGroupItem for each own item, they collect base item names ***
419 ScDPSaveGroupItemVec aGroupItems
;
420 aGroupItems
.reserve( maItems
.size() );
421 // initialize with own item names
422 for( const auto& rxItem
: maItems
)
423 aGroupItems
.emplace_back( rxItem
->ConvertToText() );
425 // *** iterate over all base items, set their names at corresponding own items ***
426 for( sal_uInt16 nItemIdx
= 0, nItemCount
= static_cast< sal_uInt16
>( maGroupOrder
.size() ); nItemIdx
< nItemCount
; ++nItemIdx
)
427 if( maGroupOrder
[ nItemIdx
] < aGroupItems
.size() )
428 if( const XclImpPCItem
* pBaseItem
= pBaseField
->GetItem( nItemIdx
) )
429 if( const XclImpPCItem
* pGroupItem
= GetItem( maGroupOrder
[ nItemIdx
] ) )
430 if( *pBaseItem
!= *pGroupItem
)
431 aGroupItems
[ maGroupOrder
[ nItemIdx
] ].AddElement( pBaseItem
->ConvertToText() );
433 // *** create the ScDPSaveGroupDimension object, fill with grouping info ***
434 ScDPSaveGroupDimension
aGroupDim( rBaseFieldName
, GetFieldName( rVisNames
) );
435 for( const auto& rGroupItem
: aGroupItems
)
436 if( !rGroupItem
.IsEmpty() )
437 aGroupDim
.AddGroupItem( rGroupItem
);
438 rSaveData
.GetDimensionData()->AddGroupDimension( aGroupDim
);
441 void XclImpPCField::ConvertNumGroupField( ScDPSaveData
& rSaveData
, const ScfStringVec
& rVisNames
) const
443 ScDPNumGroupInfo
aNumInfo( GetScNumGroupInfo() );
444 ScDPSaveNumGroupDimension
aNumGroupDim( GetFieldName( rVisNames
), aNumInfo
);
445 rSaveData
.GetDimensionData()->AddNumGroupDimension( aNumGroupDim
);
448 void XclImpPCField::ConvertDateGroupField( ScDPSaveData
& rSaveData
, const ScfStringVec
& rVisNames
) const
450 ScDPNumGroupInfo
aDateInfo( GetScDateGroupInfo() );
451 sal_Int32 nScDateType
= maNumGroupInfo
.GetScDateType();
453 switch( meFieldType
)
455 case EXC_PCFIELD_DATEGROUP
:
457 if( aDateInfo
.mbDateValues
)
459 // special case for days only with step value - create numeric grouping
460 ScDPSaveNumGroupDimension
aNumGroupDim( GetFieldName( rVisNames
), aDateInfo
);
461 rSaveData
.GetDimensionData()->AddNumGroupDimension( aNumGroupDim
);
465 ScDPSaveNumGroupDimension
aNumGroupDim( GetFieldName( rVisNames
), ScDPNumGroupInfo() );
466 aNumGroupDim
.SetDateInfo( aDateInfo
, nScDateType
);
467 rSaveData
.GetDimensionData()->AddNumGroupDimension( aNumGroupDim
);
472 case EXC_PCFIELD_DATECHILD
:
474 if( const XclImpPCField
* pBaseField
= GetGroupBaseField() )
476 const OUString
& rBaseFieldName
= pBaseField
->GetFieldName( rVisNames
);
477 if( !rBaseFieldName
.isEmpty() )
479 ScDPSaveGroupDimension
aGroupDim( rBaseFieldName
, GetFieldName( rVisNames
) );
480 aGroupDim
.SetDateInfo( aDateInfo
, nScDateType
);
481 rSaveData
.GetDimensionData()->AddGroupDimension( aGroupDim
);
488 OSL_FAIL( "XclImpPCField::ConvertDateGroupField - unknown date field type" );
492 ScDPNumGroupInfo
XclImpPCField::GetScNumGroupInfo() const
494 ScDPNumGroupInfo aNumInfo
;
495 aNumInfo
.mbEnable
= true;
496 aNumInfo
.mbDateValues
= false;
497 aNumInfo
.mbAutoStart
= true;
498 aNumInfo
.mbAutoEnd
= true;
500 if( const double* pfMinValue
= GetNumGroupLimit( EXC_SXFIELD_INDEX_MIN
) )
502 aNumInfo
.mfStart
= *pfMinValue
;
503 aNumInfo
.mbAutoStart
= ::get_flag( maNumGroupInfo
.mnFlags
, EXC_SXNUMGROUP_AUTOMIN
);
505 if( const double* pfMaxValue
= GetNumGroupLimit( EXC_SXFIELD_INDEX_MAX
) )
507 aNumInfo
.mfEnd
= *pfMaxValue
;
508 aNumInfo
.mbAutoEnd
= ::get_flag( maNumGroupInfo
.mnFlags
, EXC_SXNUMGROUP_AUTOMAX
);
510 if( const double* pfStepValue
= GetNumGroupLimit( EXC_SXFIELD_INDEX_STEP
) )
511 aNumInfo
.mfStep
= *pfStepValue
;
516 ScDPNumGroupInfo
XclImpPCField::GetScDateGroupInfo() const
518 ScDPNumGroupInfo aDateInfo
;
519 aDateInfo
.mbEnable
= true;
520 aDateInfo
.mbDateValues
= false;
521 aDateInfo
.mbAutoStart
= true;
522 aDateInfo
.mbAutoEnd
= true;
524 if( const DateTime
* pMinDate
= GetDateGroupLimit( EXC_SXFIELD_INDEX_MIN
) )
526 aDateInfo
.mfStart
= GetDoubleFromDateTime( *pMinDate
);
527 aDateInfo
.mbAutoStart
= ::get_flag( maNumGroupInfo
.mnFlags
, EXC_SXNUMGROUP_AUTOMIN
);
529 if( const DateTime
* pMaxDate
= GetDateGroupLimit( EXC_SXFIELD_INDEX_MAX
) )
531 aDateInfo
.mfEnd
= GetDoubleFromDateTime( *pMaxDate
);
532 aDateInfo
.mbAutoEnd
= ::get_flag( maNumGroupInfo
.mnFlags
, EXC_SXNUMGROUP_AUTOMAX
);
534 // GetDateGroupStep() returns a value for date type "day" in single date groups only
535 if( const sal_Int16
* pnStepValue
= GetDateGroupStep() )
537 aDateInfo
.mfStep
= *pnStepValue
;
538 aDateInfo
.mbDateValues
= true;
544 const double* XclImpPCField::GetNumGroupLimit( sal_uInt16 nLimitIdx
) const
546 OSL_ENSURE( IsNumGroupField(), "XclImpPCField::GetNumGroupLimit - only for numeric grouping fields" );
547 if( const XclImpPCItem
* pItem
= GetLimitItem( nLimitIdx
) )
549 OSL_ENSURE( pItem
->GetDouble(), "XclImpPCField::GetNumGroupLimit - SXDOUBLE item expected" );
550 return pItem
->GetDouble();
555 const DateTime
* XclImpPCField::GetDateGroupLimit( sal_uInt16 nLimitIdx
) const
557 OSL_ENSURE( IsDateGroupField(), "XclImpPCField::GetDateGroupLimit - only for date grouping fields" );
558 if( const XclImpPCItem
* pItem
= GetLimitItem( nLimitIdx
) )
560 OSL_ENSURE( pItem
->GetDateTime(), "XclImpPCField::GetDateGroupLimit - SXDATETIME item expected" );
561 return pItem
->GetDateTime();
566 const sal_Int16
* XclImpPCField::GetDateGroupStep() const
568 // only for single date grouping fields, not for grouping chains
569 if( !IsGroupBaseField() && !IsGroupChildField() )
571 // only days may have a step value, return 0 for all other date types
572 if( maNumGroupInfo
.GetXclDataType() == EXC_SXNUMGROUP_TYPE_DAY
)
574 if( const XclImpPCItem
* pItem
= GetLimitItem( EXC_SXFIELD_INDEX_STEP
) )
576 OSL_ENSURE( pItem
->GetInteger(), "XclImpPCField::GetDateGroupStep - SXINTEGER item expected" );
577 if( const sal_Int16
* pnStep
= pItem
->GetInteger() )
579 OSL_ENSURE( *pnStep
> 0, "XclImpPCField::GetDateGroupStep - invalid step count" );
580 // return nothing for step count 1 - this is also a standard date group in Excel
581 return (*pnStep
> 1) ? pnStep
: nullptr;
589 XclImpPivotCache::XclImpPivotCache( const XclImpRoot
& rRoot
) :
591 maSrcRange( ScAddress::INITIALIZE_INVALID
),
593 mnSrcType( EXC_SXVS_UNKNOWN
),
598 XclImpPivotCache::~XclImpPivotCache()
602 // data access ----------------------------------------------------------------
604 const XclImpPCField
* XclImpPivotCache::GetField( sal_uInt16 nFieldIdx
) const
606 return (nFieldIdx
< maFields
.size()) ? maFields
[ nFieldIdx
].get() : nullptr;
609 // records --------------------------------------------------------------------
611 void XclImpPivotCache::ReadSxidstm( XclImpStream
& rStrm
)
613 mnStrmId
= rStrm
.ReaduInt16();
616 void XclImpPivotCache::ReadSxvs( XclImpStream
& rStrm
)
618 mnSrcType
= rStrm
.ReaduInt16();
619 GetTracer().TracePivotDataSource( mnSrcType
!= EXC_SXVS_SHEET
);
622 void XclImpPivotCache::ReadDconref( XclImpStream
& rStrm
)
624 /* Read DCONREF only once (by checking maTabName), there may be other
625 DCONREF records in another context. Read reference only if a leading
626 SXVS record is present (by checking mnSrcType). */
627 if( !maTabName
.isEmpty() || (mnSrcType
!= EXC_SXVS_SHEET
) )
630 XclRange
aXclRange( ScAddress::UNINITIALIZED
);
631 aXclRange
.Read( rStrm
, false );
632 OUString aEncUrl
= rStrm
.ReadUniString();
634 XclImpUrlHelper::DecodeUrl( maUrl
, maTabName
, mbSelfRef
, GetRoot(), aEncUrl
);
636 /* Do not convert maTabName to Calc sheet name -> original name is used to
637 find the sheet in the document. Sheet index of source range will be
638 found later in XclImpPivotCache::ReadPivotCacheStream(), because sheet
639 may not exist yet. */
641 GetAddressConverter().ConvertRange( maSrcRange
, aXclRange
, 0, 0, true );
644 void XclImpPivotCache::ReadDConName( XclImpStream
& rStrm
)
646 maSrcRangeName
= rStrm
.ReadUniString();
648 // This 2-byte value equals the length of string that follows, or if 0 it
649 // indicates that the name has a workbook scope. For now, we only support
650 // internal defined name with a workbook scope.
652 nFlag
= rStrm
.ReaduInt16();
653 mbSelfRef
= (nFlag
== 0);
656 // External name is not supported yet.
657 maSrcRangeName
.clear();
660 void XclImpPivotCache::ReadPivotCacheStream( const XclImpStream
& rStrm
)
662 if( (mnSrcType
!= EXC_SXVS_SHEET
) && (mnSrcType
!= EXC_SXVS_EXTERN
) )
665 ScDocument
& rDoc
= GetDoc();
666 SCCOL nFieldScCol
= 0; // column index of source data for next field
667 SCROW nItemScRow
= 0; // row index of source data for current items
668 SCTAB nScTab
= 0; // sheet index of source data
669 bool bGenerateSource
= false; // true = write source data from cache to dummy table
673 if (maSrcRangeName
.isEmpty())
675 // try to find internal sheet containing the source data
676 nScTab
= GetTabInfo().GetScTabFromXclName( maTabName
);
677 if( rDoc
.HasTable( nScTab
) )
679 // set sheet index to source range
680 maSrcRange
.aStart
.SetTab( nScTab
);
681 maSrcRange
.aEnd
.SetTab( nScTab
);
685 // create dummy sheet for deleted internal sheet
686 bGenerateSource
= true;
692 // create dummy sheet for external sheet
693 bGenerateSource
= true;
696 // create dummy sheet for source data from external or deleted sheet
697 if( bGenerateSource
)
699 if( rDoc
.GetTableCount() >= MAXTABCOUNT
)
700 // cannot create more sheets -> exit
703 nScTab
= rDoc
.GetTableCount();
704 rDoc
.MakeTable( nScTab
);
705 OUStringBuffer
aDummyName("DPCache");
706 if( maTabName
.getLength() > 0 )
707 aDummyName
.append( "_" + maTabName
);
708 OUString aName
= aDummyName
.makeStringAndClear();
709 rDoc
.CreateValidTabName( aName
);
710 rDoc
.RenameTab( nScTab
, aName
);
711 // set sheet index to source range
712 maSrcRange
.aStart
.SetTab( nScTab
);
713 maSrcRange
.aEnd
.SetTab( nScTab
);
716 // open pivot cache storage stream
717 rtl::Reference
<SotStorage
> xSvStrg
= OpenStorage(EXC_STORAGE_PTCACHE
);
718 rtl::Reference
<SotStorageStream
> xSvStrm
= OpenStream(xSvStrg
, ScfTools::GetHexStr(mnStrmId
));
722 // create Excel record stream object
723 XclImpStream
aPCStrm( *xSvStrm
, GetRoot() );
724 aPCStrm
.CopyDecrypterFrom( rStrm
); // pivot cache streams are encrypted
726 XclImpPCFieldRef xCurrField
; // current field for new items
727 XclImpPCFieldVec aOrigFields
; // all standard fields with inline original items
728 XclImpPCFieldVec aPostpFields
; // all standard fields with postponed original items
729 size_t nPostpIdx
= 0; // index to current field with postponed items
730 bool bLoop
= true; // true = continue loop
732 while( bLoop
&& aPCStrm
.StartNextRecord() )
734 switch( aPCStrm
.GetRecId() )
747 sal_uInt16 nNewFieldIdx
= static_cast< sal_uInt16
>( maFields
.size() );
748 if( nNewFieldIdx
< EXC_PC_MAXFIELDCOUNT
)
750 xCurrField
= std::make_shared
<XclImpPCField
>( GetRoot(), *this, nNewFieldIdx
);
751 maFields
.push_back( xCurrField
);
752 xCurrField
->ReadSxfield( aPCStrm
);
753 if( xCurrField
->HasOrigItems() )
755 if( xCurrField
->HasPostponedItems() )
756 aPostpFields
.push_back( xCurrField
);
758 aOrigFields
.push_back( xCurrField
);
759 // insert field name into generated source data, field remembers its column index
760 if( bGenerateSource
&& (nFieldScCol
<= rDoc
.MaxCol()) )
761 xCurrField
->WriteFieldNameToSource( nFieldScCol
++, nScTab
);
763 // do not read items into invalid/postponed fields
764 if( !xCurrField
->HasInlineItems() )
770 case EXC_ID_SXINDEXLIST
:
771 // read index list and insert all items into generated source data
772 if( bGenerateSource
&& (nItemScRow
<= rDoc
.MaxRow()) && (++nItemScRow
<= rDoc
.MaxRow()) )
774 for( const auto& rxOrigField
: aOrigFields
)
776 sal_uInt16 nItemIdx
= rxOrigField
->Has16BitIndexes() ? aPCStrm
.ReaduInt16() : aPCStrm
.ReaduInt8();
777 rxOrigField
->WriteOrigItemToSource( nItemScRow
, nScTab
, nItemIdx
);
783 case EXC_ID_SXDOUBLE
:
784 case EXC_ID_SXBOOLEAN
:
786 case EXC_ID_SXINTEGER
:
787 case EXC_ID_SXSTRING
:
788 case EXC_ID_SXDATETIME
:
790 if( xCurrField
) // inline items
792 xCurrField
->ReadItem( aPCStrm
);
794 else if( !aPostpFields
.empty() ) // postponed items
796 // read postponed item
797 aPostpFields
[ nPostpIdx
]->ReadItem( aPCStrm
);
798 // write item to source
799 if( bGenerateSource
&& (nItemScRow
<= rDoc
.MaxRow()) )
801 // start new row, if there are only postponed fields
802 if( aOrigFields
.empty() && (nPostpIdx
== 0) )
804 if( nItemScRow
<= rDoc
.MaxRow() )
805 aPostpFields
[ nPostpIdx
]->WriteLastOrigItemToSource( nItemScRow
, nScTab
);
807 // get index of next postponed field
809 if( nPostpIdx
>= aPostpFields
.size() )
814 case EXC_ID_SXNUMGROUP
:
816 xCurrField
->ReadSxnumgroup( aPCStrm
);
819 case EXC_ID_SXGROUPINFO
:
821 xCurrField
->ReadSxgroupinfo( aPCStrm
);
824 // known but ignored records
831 case EXC_ID_SXFORMULA
:
833 case EXC_ID_SXFDBTYPE
:
837 SAL_WARN("sc.filter", "XclImpPivotCache::ReadPivotCacheStream - unknown record 0x" << std::hex
<< aPCStrm
.GetRecId() );
841 OSL_ENSURE( maPCInfo
.mnTotalFields
== maFields
.size(),
842 "XclImpPivotCache::ReadPivotCacheStream - field count mismatch" );
844 if (static_cast<bool>(maPCInfo
.mnFlags
& EXC_SXDB_SAVEDATA
))
846 SCROW nNewEnd
= maSrcRange
.aStart
.Row() + maPCInfo
.mnSrcRecs
;
847 maSrcRange
.aEnd
.SetRow(nNewEnd
);
850 // set source range for external source data
851 if( bGenerateSource
&& (nFieldScCol
> 0) )
853 maSrcRange
.aStart
.SetCol( 0 );
854 maSrcRange
.aStart
.SetRow( 0 );
855 // nFieldScCol points to first unused column
856 maSrcRange
.aEnd
.SetCol( nFieldScCol
- 1 );
857 // nItemScRow points to last used row
858 maSrcRange
.aEnd
.SetRow( nItemScRow
);
862 bool XclImpPivotCache::IsRefreshOnLoad() const
864 return static_cast<bool>(maPCInfo
.mnFlags
& EXC_SXDB_REFRESH_LOAD
);
867 bool XclImpPivotCache::IsValid() const
869 if (!maSrcRangeName
.isEmpty())
872 return maSrcRange
.IsValid();
877 XclImpPTItem::XclImpPTItem( const XclImpPCField
* pCacheField
) :
878 mpCacheField( pCacheField
)
882 const OUString
* XclImpPTItem::GetItemName() const
885 if( const XclImpPCItem
* pCacheItem
= mpCacheField
->GetItem( maItemInfo
.mnCacheIdx
) )
886 //TODO: use XclImpPCItem::ConvertToText(), if all conversions are available
887 return pCacheItem
->IsEmpty() ? nullptr : pCacheItem
->GetText();
891 std::pair
<bool, OUString
> XclImpPTItem::GetItemName(const ScDPSaveDimension
& rSaveDim
, ScDPObject
* pObj
, const XclImpRoot
& rRoot
) const
894 return std::pair
<bool, OUString
>(false, OUString());
896 const XclImpPCItem
* pCacheItem
= mpCacheField
->GetItem( maItemInfo
.mnCacheIdx
);
898 return std::pair
<bool, OUString
>(false, OUString());
901 if(pCacheItem
->GetType() == EXC_PCITEM_TEXT
|| pCacheItem
->GetType() == EXC_PCITEM_ERROR
)
903 const OUString
* pItemName
= pCacheItem
->GetText();
905 return std::pair
<bool, OUString
>(false, OUString());
906 sItemName
= *pItemName
;
908 else if (pCacheItem
->GetType() == EXC_PCITEM_DOUBLE
)
910 sItemName
= pObj
->GetFormattedString(rSaveDim
.GetName(), *pCacheItem
->GetDouble());
912 else if (pCacheItem
->GetType() == EXC_PCITEM_INTEGER
)
914 sItemName
= pObj
->GetFormattedString(rSaveDim
.GetName(), static_cast<double>(*pCacheItem
->GetInteger()));
916 else if (pCacheItem
->GetType() == EXC_PCITEM_BOOL
)
918 sItemName
= pObj
->GetFormattedString(rSaveDim
.GetName(), static_cast<double>(*pCacheItem
->GetBool()));
920 else if (pCacheItem
->GetType() == EXC_PCITEM_DATETIME
)
922 sItemName
= pObj
->GetFormattedString(rSaveDim
.GetName(), rRoot
.GetDoubleFromDateTime(*pCacheItem
->GetDateTime()));
924 else if (pCacheItem
->GetType() == EXC_PCITEM_EMPTY
)
926 // sItemName is an empty string
928 else // EXC_PCITEM_INVALID
929 return std::pair
<bool, OUString
>(false, OUString());
931 return std::pair
<bool, OUString
>(true, sItemName
);
934 void XclImpPTItem::ReadSxvi( XclImpStream
& rStrm
)
939 void XclImpPTItem::ConvertItem( ScDPSaveDimension
& rSaveDim
, ScDPObject
* pObj
, const XclImpRoot
& rRoot
) const
941 // Find member and set properties
942 std::pair
<bool, OUString
> aReturnedName
= GetItemName(rSaveDim
, pObj
, rRoot
);
943 if(aReturnedName
.first
)
945 ScDPSaveMember
* pMember
= rSaveDim
.GetExistingMemberByName(aReturnedName
.second
);
948 pMember
->SetIsVisible( !::get_flag( maItemInfo
.mnFlags
, EXC_SXVI_HIDDEN
) );
949 pMember
->SetShowDetails( !::get_flag( maItemInfo
.mnFlags
, EXC_SXVI_HIDEDETAIL
) );
950 if (maItemInfo
.HasVisName())
951 pMember
->SetLayoutName(*maItemInfo
.GetVisName());
956 XclImpPTField::XclImpPTField( const XclImpPivotTable
& rPTable
, sal_uInt16 nCacheIdx
) :
959 maFieldInfo
.mnCacheIdx
= nCacheIdx
;
962 // general field/item access --------------------------------------------------
964 const XclImpPCField
* XclImpPTField::GetCacheField() const
966 XclImpPivotCacheRef xPCache
= mrPTable
.GetPivotCache();
967 return xPCache
? xPCache
->GetField( maFieldInfo
.mnCacheIdx
) : nullptr;
970 OUString
XclImpPTField::GetFieldName() const
972 const XclImpPCField
* pField
= GetCacheField();
973 return pField
? pField
->GetFieldName( mrPTable
.GetVisFieldNames() ) : OUString();
976 OUString
XclImpPTField::GetVisFieldName() const
978 const OUString
* pVisName
= maFieldInfo
.GetVisName();
979 return pVisName
? *pVisName
: OUString();
982 const XclImpPTItem
* XclImpPTField::GetItem( sal_uInt16 nItemIdx
) const
984 return (nItemIdx
< maItems
.size()) ? maItems
[ nItemIdx
].get() : nullptr;
987 const OUString
* XclImpPTField::GetItemName( sal_uInt16 nItemIdx
) const
989 const XclImpPTItem
* pItem
= GetItem( nItemIdx
);
990 return pItem
? pItem
->GetItemName() : nullptr;
993 // records --------------------------------------------------------------------
995 void XclImpPTField::ReadSxvd( XclImpStream
& rStrm
)
997 rStrm
>> maFieldInfo
;
1000 void XclImpPTField::ReadSxvdex( XclImpStream
& rStrm
)
1002 rStrm
>> maFieldExtInfo
;
1005 void XclImpPTField::ReadSxvi( XclImpStream
& rStrm
)
1007 XclImpPTItemRef xItem
= std::make_shared
<XclImpPTItem
>( GetCacheField() );
1008 maItems
.push_back( xItem
);
1009 xItem
->ReadSxvi( rStrm
);
1012 // row/column fields ----------------------------------------------------------
1014 void XclImpPTField::ConvertRowColField( ScDPSaveData
& rSaveData
) const
1016 OSL_ENSURE( maFieldInfo
.mnAxes
& EXC_SXVD_AXIS_ROWCOL
, "XclImpPTField::ConvertRowColField - no row/column field" );
1017 // special data orientation field?
1018 if( maFieldInfo
.mnCacheIdx
== EXC_SXIVD_DATA
)
1019 rSaveData
.GetDataLayoutDimension()->SetOrientation( maFieldInfo
.GetApiOrient( EXC_SXVD_AXIS_ROWCOL
) );
1021 ConvertRCPField( rSaveData
);
1024 // page fields ----------------------------------------------------------------
1026 void XclImpPTField::SetPageFieldInfo( const XclPTPageFieldInfo
& rPageInfo
)
1028 maPageInfo
= rPageInfo
;
1031 void XclImpPTField::ConvertPageField( ScDPSaveData
& rSaveData
) const
1033 OSL_ENSURE( maFieldInfo
.mnAxes
& EXC_SXVD_AXIS_PAGE
, "XclImpPTField::ConvertPageField - no page field" );
1034 ConvertRCPField( rSaveData
);
1037 // hidden fields --------------------------------------------------------------
1039 void XclImpPTField::ConvertHiddenField( ScDPSaveData
& rSaveData
) const
1041 OSL_ENSURE( (maFieldInfo
.mnAxes
& EXC_SXVD_AXIS_ROWCOLPAGE
) == 0, "XclImpPTField::ConvertHiddenField - field not hidden" );
1042 ConvertRCPField( rSaveData
);
1045 // data fields ----------------------------------------------------------------
1047 bool XclImpPTField::HasDataFieldInfo() const
1049 return !maDataInfoVector
.empty();
1052 void XclImpPTField::AddDataFieldInfo( const XclPTDataFieldInfo
& rDataInfo
)
1054 OSL_ENSURE( maFieldInfo
.mnAxes
& EXC_SXVD_AXIS_DATA
, "XclImpPTField::AddDataFieldInfo - no data field" );
1055 maDataInfoVector
.push_back( rDataInfo
);
1058 void XclImpPTField::ConvertDataField( ScDPSaveData
& rSaveData
) const
1060 OSL_ENSURE( maFieldInfo
.mnAxes
& EXC_SXVD_AXIS_DATA
, "XclImpPTField::ConvertDataField - no data field" );
1061 OSL_ENSURE( !maDataInfoVector
.empty(), "XclImpPTField::ConvertDataField - no data field info" );
1062 if (maDataInfoVector
.empty())
1065 OUString aFieldName
= GetFieldName();
1066 if (aFieldName
.isEmpty())
1069 ScDPSaveDimension
* pSaveDim
= rSaveData
.GetNewDimensionByName(aFieldName
);
1072 SAL_WARN("sc.filter","XclImpPTField::ConvertDataField - field name not found: " << aFieldName
);
1076 auto aIt
= maDataInfoVector
.begin(), aEnd
= maDataInfoVector
.end();
1078 ConvertDataField( *pSaveDim
, *aIt
);
1080 // multiple data fields -> clone dimension
1081 for( ++aIt
; aIt
!= aEnd
; ++aIt
)
1083 ScDPSaveDimension
& rDupDim
= rSaveData
.DuplicateDimension( *pSaveDim
);
1084 ConvertDataFieldInfo( rDupDim
, *aIt
);
1088 // private --------------------------------------------------------------------
1091 * Convert Excel-encoded subtotal name to a Calc-encoded one.
1093 static OUString
lcl_convertExcelSubtotalName(const OUString
& rName
)
1095 OUStringBuffer aBuf
;
1096 const sal_Unicode
* p
= rName
.getStr();
1097 sal_Int32 n
= rName
.getLength();
1098 for (sal_Int32 i
= 0; i
< n
; ++i
)
1100 const sal_Unicode c
= p
[i
];
1103 aBuf
.append(OUStringChar(c
) + OUStringChar(c
));
1108 return aBuf
.makeStringAndClear();
1111 void XclImpPTField::ConvertRCPField( ScDPSaveData
& rSaveData
) const
1113 const OUString aFieldName
= GetFieldName();
1114 if( aFieldName
.isEmpty() )
1117 const XclImpPCField
* pCacheField
= GetCacheField();
1118 if( !pCacheField
|| !pCacheField
->IsSupportedField() )
1121 ScDPSaveDimension
* pTest
= rSaveData
.GetNewDimensionByName(aFieldName
);
1125 ScDPSaveDimension
& rSaveDim
= *pTest
;
1128 rSaveDim
.SetOrientation( maFieldInfo
.GetApiOrient( EXC_SXVD_AXIS_ROWCOLPAGE
) );
1131 if (const OUString
* pVisName
= maFieldInfo
.GetVisName())
1132 if (!pVisName
->isEmpty())
1133 rSaveDim
.SetLayoutName( *pVisName
);
1135 // subtotal function(s)
1136 XclPTSubtotalVec aSubtotalVec
;
1137 maFieldInfo
.GetSubtotals( aSubtotalVec
);
1138 if( !aSubtotalVec
.empty() )
1139 rSaveDim
.SetSubTotals( std::move(aSubtotalVec
) );
1142 DataPilotFieldSortInfo aSortInfo
;
1143 aSortInfo
.Field
= mrPTable
.GetDataFieldName( maFieldExtInfo
.mnSortField
);
1144 aSortInfo
.IsAscending
= ::get_flag( maFieldExtInfo
.mnFlags
, EXC_SXVDEX_SORT_ASC
);
1145 aSortInfo
.Mode
= maFieldExtInfo
.GetApiSortMode();
1146 rSaveDim
.SetSortInfo( &aSortInfo
);
1149 DataPilotFieldAutoShowInfo aShowInfo
;
1150 aShowInfo
.IsEnabled
= ::get_flag( maFieldExtInfo
.mnFlags
, EXC_SXVDEX_AUTOSHOW
);
1151 aShowInfo
.ShowItemsMode
= maFieldExtInfo
.GetApiAutoShowMode();
1152 aShowInfo
.ItemCount
= maFieldExtInfo
.GetApiAutoShowCount();
1153 aShowInfo
.DataField
= mrPTable
.GetDataFieldName( maFieldExtInfo
.mnShowField
);
1154 rSaveDim
.SetAutoShowInfo( &aShowInfo
);
1157 DataPilotFieldLayoutInfo aLayoutInfo
;
1158 aLayoutInfo
.LayoutMode
= maFieldExtInfo
.GetApiLayoutMode();
1159 aLayoutInfo
.AddEmptyLines
= ::get_flag( maFieldExtInfo
.mnFlags
, EXC_SXVDEX_LAYOUT_BLANK
);
1160 rSaveDim
.SetLayoutInfo( &aLayoutInfo
);
1163 pCacheField
->ConvertGroupField( rSaveData
, mrPTable
.GetVisFieldNames() );
1165 // custom subtotal name
1166 if (maFieldExtInfo
.mpFieldTotalName
)
1168 OUString aSubName
= lcl_convertExcelSubtotalName(*maFieldExtInfo
.mpFieldTotalName
);
1169 rSaveDim
.SetSubtotalName(aSubName
);
1173 void XclImpPTField::ConvertFieldInfo( const ScDPSaveData
& rSaveData
, ScDPObject
* pObj
, const XclImpRoot
& rRoot
, bool bPageField
) const
1175 const OUString aFieldName
= GetFieldName();
1176 if( aFieldName
.isEmpty() )
1179 const XclImpPCField
* pCacheField
= GetCacheField();
1180 if( !pCacheField
|| !pCacheField
->IsSupportedField() )
1183 ScDPSaveDimension
* pSaveDim
= rSaveData
.GetExistingDimensionByName(aFieldName
);
1187 pSaveDim
->SetShowEmpty( ::get_flag( maFieldExtInfo
.mnFlags
, EXC_SXVDEX_SHOWALL
) );
1188 for( const auto& rxItem
: maItems
)
1189 rxItem
->ConvertItem( *pSaveDim
, pObj
, rRoot
);
1191 if(bPageField
&& maPageInfo
.mnSelItem
!= EXC_SXPI_ALLITEMS
)
1193 const XclImpPTItem
* pItem
= GetItem( maPageInfo
.mnSelItem
);
1196 std::pair
<bool, OUString
> aReturnedName
= pItem
->GetItemName(*pSaveDim
, pObj
, rRoot
);
1197 if(aReturnedName
.first
)
1198 pSaveDim
->SetCurrentPage(&aReturnedName
.second
);
1203 void XclImpPTField::ConvertDataField( ScDPSaveDimension
& rSaveDim
, const XclPTDataFieldInfo
& rDataInfo
) const
1206 rSaveDim
.SetOrientation( DataPilotFieldOrientation_DATA
);
1207 // extended data field info
1208 ConvertDataFieldInfo( rSaveDim
, rDataInfo
);
1211 void XclImpPTField::ConvertDataFieldInfo( ScDPSaveDimension
& rSaveDim
, const XclPTDataFieldInfo
& rDataInfo
) const
1214 const OUString
* pVisName
= rDataInfo
.GetVisName();
1215 if (pVisName
&& !pVisName
->isEmpty())
1216 rSaveDim
.SetLayoutName(*pVisName
);
1218 // aggregation function
1219 rSaveDim
.SetFunction( rDataInfo
.GetApiAggFunc() );
1221 // result field reference
1222 sal_Int32 nRefType
= rDataInfo
.GetApiRefType();
1223 DataPilotFieldReference aFieldRef
;
1224 aFieldRef
.ReferenceType
= nRefType
;
1225 const XclImpPTField
* pRefField
= mrPTable
.GetField(rDataInfo
.mnRefField
);
1228 aFieldRef
.ReferenceField
= pRefField
->GetFieldName();
1229 aFieldRef
.ReferenceItemType
= rDataInfo
.GetApiRefItemType();
1230 if (aFieldRef
.ReferenceItemType
== sheet::DataPilotFieldReferenceItemType::NAMED
)
1232 const OUString
* pRefItemName
= pRefField
->GetItemName(rDataInfo
.mnRefItem
);
1234 aFieldRef
.ReferenceItemName
= *pRefItemName
;
1238 rSaveDim
.SetReferenceValue(&aFieldRef
);
1241 XclImpPivotTable::XclImpPivotTable( const XclImpRoot
& rRoot
) :
1242 XclImpRoot( rRoot
),
1243 maDataOrientField( *this, EXC_SXIVD_DATA
),
1248 XclImpPivotTable::~XclImpPivotTable()
1252 // cache/field access, misc. --------------------------------------------------
1254 sal_uInt16
XclImpPivotTable::GetFieldCount() const
1256 return static_cast< sal_uInt16
>( maFields
.size() );
1259 const XclImpPTField
* XclImpPivotTable::GetField( sal_uInt16 nFieldIdx
) const
1261 return (nFieldIdx
== EXC_SXIVD_DATA
) ? &maDataOrientField
:
1262 ((nFieldIdx
< maFields
.size()) ? maFields
[ nFieldIdx
].get() : nullptr);
1265 XclImpPTField
* XclImpPivotTable::GetFieldAcc( sal_uInt16 nFieldIdx
)
1267 // do not return maDataOrientField
1268 return (nFieldIdx
< maFields
.size()) ? maFields
[ nFieldIdx
].get() : nullptr;
1271 const XclImpPTField
* XclImpPivotTable::GetDataField( sal_uInt16 nDataFieldIdx
) const
1273 if( nDataFieldIdx
< maOrigDataFields
.size() )
1274 return GetField( maOrigDataFields
[ nDataFieldIdx
] );
1278 OUString
XclImpPivotTable::GetDataFieldName( sal_uInt16 nDataFieldIdx
) const
1280 if( const XclImpPTField
* pField
= GetDataField( nDataFieldIdx
) )
1281 return pField
->GetFieldName();
1285 // records --------------------------------------------------------------------
1287 void XclImpPivotTable::ReadSxview( XclImpStream
& rStrm
)
1291 GetAddressConverter().ConvertRange(
1292 maOutScRange
, maPTInfo
.maOutXclRange
, GetCurrScTab(), GetCurrScTab(), true );
1294 mxPCache
= GetPivotTableManager().GetPivotCache( maPTInfo
.mnCacheIdx
);
1295 mxCurrField
.reset();
1298 void XclImpPivotTable::ReadSxvd( XclImpStream
& rStrm
)
1300 sal_uInt16 nFieldCount
= GetFieldCount();
1301 if( nFieldCount
< EXC_PT_MAXFIELDCOUNT
)
1303 // cache index for the field is equal to the SXVD record index
1304 mxCurrField
= std::make_shared
<XclImpPTField
>( *this, nFieldCount
);
1305 maFields
.push_back( mxCurrField
);
1306 mxCurrField
->ReadSxvd( rStrm
);
1307 // add visible name of new field to list of visible names
1308 maVisFieldNames
.push_back( mxCurrField
->GetVisFieldName() );
1309 OSL_ENSURE( maFields
.size() == maVisFieldNames
.size(),
1310 "XclImpPivotTable::ReadSxvd - wrong size of visible name array" );
1313 mxCurrField
.reset();
1316 void XclImpPivotTable::ReadSxvi( XclImpStream
& rStrm
)
1319 mxCurrField
->ReadSxvi( rStrm
);
1322 void XclImpPivotTable::ReadSxvdex( XclImpStream
& rStrm
)
1325 mxCurrField
->ReadSxvdex( rStrm
);
1328 void XclImpPivotTable::ReadSxivd( XclImpStream
& rStrm
)
1330 mxCurrField
.reset();
1332 // find the index vector to fill (row SXIVD doesn't exist without row fields)
1333 ScfUInt16Vec
* pFieldVec
= nullptr;
1334 if( maRowFields
.empty() && (maPTInfo
.mnRowFields
> 0) )
1335 pFieldVec
= &maRowFields
;
1336 else if( maColFields
.empty() && (maPTInfo
.mnColFields
> 0) )
1337 pFieldVec
= &maColFields
;
1339 // fill the vector from record data
1343 sal_uInt16 nSize
= ulimit_cast
< sal_uInt16
>( rStrm
.GetRecSize() / 2, EXC_PT_MAXROWCOLCOUNT
);
1344 pFieldVec
->reserve( nSize
);
1345 for( sal_uInt16 nIdx
= 0; nIdx
< nSize
; ++nIdx
)
1347 sal_uInt16 nFieldIdx
;
1348 nFieldIdx
= rStrm
.ReaduInt16();
1349 pFieldVec
->push_back( nFieldIdx
);
1351 // set orientation at special data orientation field
1352 if( nFieldIdx
== EXC_SXIVD_DATA
)
1354 sal_uInt16 nAxis
= (pFieldVec
== &maRowFields
) ? EXC_SXVD_AXIS_ROW
: EXC_SXVD_AXIS_COL
;
1355 maDataOrientField
.SetAxes( nAxis
);
1360 void XclImpPivotTable::ReadSxpi( XclImpStream
& rStrm
)
1362 mxCurrField
.reset();
1364 sal_uInt16 nSize
= ulimit_cast
< sal_uInt16
>( rStrm
.GetRecSize() / 6 );
1365 for( sal_uInt16 nEntry
= 0; nEntry
< nSize
; ++nEntry
)
1367 XclPTPageFieldInfo aPageInfo
;
1369 if( XclImpPTField
* pField
= GetFieldAcc( aPageInfo
.mnField
) )
1371 maPageFields
.push_back( aPageInfo
.mnField
);
1372 pField
->SetPageFieldInfo( aPageInfo
);
1374 GetCurrSheetDrawing().SetSkipObj( aPageInfo
.mnObjId
);
1378 void XclImpPivotTable::ReadSxdi( XclImpStream
& rStrm
)
1380 mxCurrField
.reset();
1382 XclPTDataFieldInfo aDataInfo
;
1384 if( XclImpPTField
* pField
= GetFieldAcc( aDataInfo
.mnField
) )
1386 maOrigDataFields
.push_back( aDataInfo
.mnField
);
1387 // DataPilot does not support double data fields -> add first appearance to index list only
1388 if( !pField
->HasDataFieldInfo() )
1389 maFiltDataFields
.push_back( aDataInfo
.mnField
);
1390 pField
->AddDataFieldInfo( aDataInfo
);
1394 void XclImpPivotTable::ReadSxex( XclImpStream
& rStrm
)
1396 rStrm
>> maPTExtInfo
;
1399 void XclImpPivotTable::ReadSxViewEx9( XclImpStream
& rStrm
)
1401 rStrm
>> maPTViewEx9Info
;
1404 void XclImpPivotTable::ReadSxAddl( XclImpStream
& rStrm
)
1406 rStrm
>> maPTAddlInfo
;
1409 void XclImpPivotTable::Convert()
1411 if( !mxPCache
|| !mxPCache
->IsValid() )
1414 if (comphelper::IsFuzzing()) //just too slow
1417 ScDPSaveData aSaveData
;
1419 // *** global settings ***
1421 aSaveData
.SetRowGrand( ::get_flag( maPTInfo
.mnFlags
, EXC_SXVIEW_ROWGRAND
) );
1422 aSaveData
.SetColumnGrand( ::get_flag( maPTInfo
.mnFlags
, EXC_SXVIEW_COLGRAND
) );
1423 aSaveData
.SetFilterButton( false );
1424 aSaveData
.SetDrillDown( ::get_flag( maPTExtInfo
.mnFlags
, EXC_SXEX_DRILLDOWN
) );
1425 aSaveData
.SetIgnoreEmptyRows( false );
1426 aSaveData
.SetRepeatIfEmpty( false );
1431 for( const auto& rRowField
: maRowFields
)
1432 if( const XclImpPTField
* pField
= GetField( rRowField
) )
1433 pField
->ConvertRowColField( aSaveData
);
1436 for( const auto& rColField
: maColFields
)
1437 if( const XclImpPTField
* pField
= GetField( rColField
) )
1438 pField
->ConvertRowColField( aSaveData
);
1441 for( const auto& rPageField
: maPageFields
)
1442 if( const XclImpPTField
* pField
= GetField( rPageField
) )
1443 pField
->ConvertPageField( aSaveData
);
1445 // We need to import hidden fields because hidden fields may contain
1446 // special settings for subtotals (aggregation function, filters, custom
1447 // name etc.) and members (hidden, custom name etc.).
1450 for( sal_uInt16 nField
= 0, nCount
= GetFieldCount(); nField
< nCount
; ++nField
)
1451 if( const XclImpPTField
* pField
= GetField( nField
) )
1452 if (!pField
->GetAxes())
1453 pField
->ConvertHiddenField( aSaveData
);
1456 for( const auto& rFiltDataField
: maFiltDataFields
)
1457 if( const XclImpPTField
* pField
= GetField( rFiltDataField
) )
1458 pField
->ConvertDataField( aSaveData
);
1460 // *** insert into Calc document ***
1462 // create source descriptor
1463 ScSheetSourceDesc
aDesc(&GetDoc());
1464 const OUString
& rSrcName
= mxPCache
->GetSourceRangeName();
1465 if (!rSrcName
.isEmpty())
1466 // Range name is the data source.
1467 aDesc
.SetRangeName(rSrcName
);
1469 // Normal cell range.
1470 aDesc
.SetSourceRange(mxPCache
->GetSourceRange());
1472 // adjust output range to include the page fields
1473 ScRange
aOutRange( maOutScRange
);
1474 if( !maPageFields
.empty() )
1476 SCROW nDecRows
= ::std::min
< SCROW
>( aOutRange
.aStart
.Row(), maPageFields
.size() + 1 );
1477 aOutRange
.aStart
.IncRow( -nDecRows
);
1480 // create the DataPilot
1481 std::unique_ptr
<ScDPObject
> pDPObj(new ScDPObject( &GetDoc() ));
1482 pDPObj
->SetName( maPTInfo
.maTableName
);
1483 if (!maPTInfo
.maDataName
.isEmpty())
1484 aSaveData
.GetDataLayoutDimension()->SetLayoutName(maPTInfo
.maDataName
);
1486 if (!maPTViewEx9Info
.maGrandTotalName
.isEmpty())
1487 aSaveData
.SetGrandTotalName(maPTViewEx9Info
.maGrandTotalName
);
1489 pDPObj
->SetSaveData( aSaveData
);
1490 pDPObj
->SetSheetDesc( aDesc
);
1491 pDPObj
->SetOutRange( aOutRange
);
1492 pDPObj
->SetHeaderLayout( maPTViewEx9Info
.mnGridLayout
== 0 );
1494 mpDPObj
= GetDoc().GetDPCollection()->InsertNewTable(std::move(pDPObj
));
1497 ApplyMergeFlags(aOutRange
, aSaveData
);
1500 void XclImpPivotTable::MaybeRefresh()
1502 if (mpDPObj
&& mxPCache
->IsRefreshOnLoad())
1504 // 'refresh table on load' flag is set. Refresh the table now. Some
1505 // Excel files contain partial table output when this flag is set.
1506 ScRange aOutRange
= mpDPObj
->GetOutRange();
1507 mpDPObj
->Output(aOutRange
.aStart
);
1511 void XclImpPivotTable::ApplyMergeFlags(const ScRange
& rOutRange
, const ScDPSaveData
& rSaveData
)
1513 // Apply merge flags for various datapilot controls.
1515 ScDPOutputGeometry
aGeometry(rOutRange
, false);
1516 aGeometry
.setColumnFieldCount(maPTInfo
.mnColFields
);
1517 aGeometry
.setPageFieldCount(maPTInfo
.mnPageFields
);
1518 aGeometry
.setDataFieldCount(maPTInfo
.mnDataFields
);
1519 aGeometry
.setRowFieldCount(maPTInfo
.mnRowFields
);
1521 // Make sure we set headerlayout when input file has additional raw header
1522 if(maPTInfo
.mnColFields
== 0)
1524 mpDPObj
->SetHeaderLayout( maPTInfo
.mnFirstHeadRow
- 2 == static_cast<sal_uInt16
>(aGeometry
.getRowFieldHeaderRow()) );
1526 aGeometry
.setHeaderLayout(mpDPObj
->GetHeaderLayout());
1527 aGeometry
.setCompactMode(maPTAddlInfo
.mbCompactMode
);
1529 ScDocument
& rDoc
= GetDoc();
1531 vector
<const ScDPSaveDimension
*> aFieldDims
;
1532 vector
<ScAddress
> aFieldBtns
;
1534 aGeometry
.getPageFieldPositions(aFieldBtns
);
1535 for (const auto& rFieldBtn
: aFieldBtns
)
1537 rDoc
.ApplyFlagsTab(rFieldBtn
.Col(), rFieldBtn
.Row(), rFieldBtn
.Col(), rFieldBtn
.Row(), rFieldBtn
.Tab(), ScMF::Button
);
1539 ScMF nMFlag
= ScMF::ButtonPopup
;
1540 OUString aName
= rDoc
.GetString(rFieldBtn
.Col(), rFieldBtn
.Row(), rFieldBtn
.Tab());
1541 if (rSaveData
.HasInvisibleMember(aName
))
1542 nMFlag
|= ScMF::HiddenMember
;
1544 rDoc
.ApplyFlagsTab(rFieldBtn
.Col()+1, rFieldBtn
.Row(), rFieldBtn
.Col()+1, rFieldBtn
.Row(), rFieldBtn
.Tab(), nMFlag
);
1547 aGeometry
.getColumnFieldPositions(aFieldBtns
);
1548 rSaveData
.GetAllDimensionsByOrientation(sheet::DataPilotFieldOrientation_COLUMN
, aFieldDims
);
1549 if (aFieldBtns
.size() == aFieldDims
.size())
1551 vector
<const ScDPSaveDimension
*>::const_iterator itDim
= aFieldDims
.begin();
1552 for (const auto& rFieldBtn
: aFieldBtns
)
1554 ScMF nMFlag
= ScMF::Button
;
1555 const ScDPSaveDimension
* pDim
= *itDim
;
1556 if (pDim
->HasInvisibleMember())
1557 nMFlag
|= ScMF::HiddenMember
;
1558 if (!pDim
->IsDataLayout())
1559 nMFlag
|= ScMF::ButtonPopup
;
1560 rDoc
.ApplyFlagsTab(rFieldBtn
.Col(), rFieldBtn
.Row(), rFieldBtn
.Col(), rFieldBtn
.Row(), rFieldBtn
.Tab(), nMFlag
);
1565 aGeometry
.getRowFieldPositions(aFieldBtns
);
1566 rSaveData
.GetAllDimensionsByOrientation(sheet::DataPilotFieldOrientation_ROW
, aFieldDims
);
1567 if (!((aFieldBtns
.size() == aFieldDims
.size()) || (maPTAddlInfo
.mbCompactMode
&& aFieldBtns
.size() == 1)))
1570 vector
<const ScDPSaveDimension
*>::const_iterator itDim
= aFieldDims
.begin();
1571 for (const auto& rFieldBtn
: aFieldBtns
)
1573 ScMF nMFlag
= ScMF::Button
;
1574 const ScDPSaveDimension
* pDim
= itDim
!= aFieldDims
.end() ? *itDim
++ : nullptr;
1575 if (pDim
&& pDim
->HasInvisibleMember())
1576 nMFlag
|= ScMF::HiddenMember
;
1577 if (!pDim
|| !pDim
->IsDataLayout())
1578 nMFlag
|= ScMF::ButtonPopup
;
1579 rDoc
.ApplyFlagsTab(rFieldBtn
.Col(), rFieldBtn
.Row(), rFieldBtn
.Col(), rFieldBtn
.Row(), rFieldBtn
.Tab(), nMFlag
);
1584 void XclImpPivotTable::ApplyFieldInfo()
1586 mpDPObj
->BuildAllDimensionMembers();
1587 ScDPSaveData
& rSaveData
= *mpDPObj
->GetSaveData();
1590 for( const auto& rRowField
: maRowFields
)
1591 if( const XclImpPTField
* pField
= GetField( rRowField
) )
1592 pField
->ConvertFieldInfo( rSaveData
, mpDPObj
, *this );
1595 for( const auto& rColField
: maColFields
)
1596 if( const XclImpPTField
* pField
= GetField( rColField
) )
1597 pField
->ConvertFieldInfo( rSaveData
, mpDPObj
, *this );
1600 for( const auto& rPageField
: maPageFields
)
1601 if( const XclImpPTField
* pField
= GetField( rPageField
) )
1602 pField
->ConvertFieldInfo( rSaveData
, mpDPObj
, *this, true );
1605 for( sal_uInt16 nField
= 0, nCount
= GetFieldCount(); nField
< nCount
; ++nField
)
1606 if( const XclImpPTField
* pField
= GetField( nField
) )
1607 if (!pField
->GetAxes())
1608 pField
->ConvertFieldInfo( rSaveData
, mpDPObj
, *this );
1611 XclImpPivotTableManager::XclImpPivotTableManager( const XclImpRoot
& rRoot
) :
1616 XclImpPivotTableManager::~XclImpPivotTableManager()
1620 // pivot cache records --------------------------------------------------------
1622 XclImpPivotCacheRef
XclImpPivotTableManager::GetPivotCache( sal_uInt16 nCacheIdx
)
1624 XclImpPivotCacheRef xPCache
;
1625 if( nCacheIdx
< maPCaches
.size() )
1626 xPCache
= maPCaches
[ nCacheIdx
];
1630 void XclImpPivotTableManager::ReadSxidstm( XclImpStream
& rStrm
)
1632 XclImpPivotCacheRef xPCache
= std::make_shared
<XclImpPivotCache
>( GetRoot() );
1633 maPCaches
.push_back( xPCache
);
1634 xPCache
->ReadSxidstm( rStrm
);
1637 void XclImpPivotTableManager::ReadSxvs( XclImpStream
& rStrm
)
1639 if( !maPCaches
.empty() )
1640 maPCaches
.back()->ReadSxvs( rStrm
);
1643 void XclImpPivotTableManager::ReadDconref( XclImpStream
& rStrm
)
1645 if( !maPCaches
.empty() )
1646 maPCaches
.back()->ReadDconref( rStrm
);
1649 void XclImpPivotTableManager::ReadDConName( XclImpStream
& rStrm
)
1651 if( !maPCaches
.empty() )
1652 maPCaches
.back()->ReadDConName( rStrm
);
1655 // pivot table records --------------------------------------------------------
1657 void XclImpPivotTableManager::ReadSxview( XclImpStream
& rStrm
)
1659 XclImpPivotTableRef xPTable
= std::make_shared
<XclImpPivotTable
>( GetRoot() );
1660 maPTables
.push_back( xPTable
);
1661 xPTable
->ReadSxview( rStrm
);
1664 void XclImpPivotTableManager::ReadSxvd( XclImpStream
& rStrm
)
1666 if( !maPTables
.empty() )
1667 maPTables
.back()->ReadSxvd( rStrm
);
1670 void XclImpPivotTableManager::ReadSxvdex( XclImpStream
& rStrm
)
1672 if( !maPTables
.empty() )
1673 maPTables
.back()->ReadSxvdex( rStrm
);
1676 void XclImpPivotTableManager::ReadSxivd( XclImpStream
& rStrm
)
1678 if( !maPTables
.empty() )
1679 maPTables
.back()->ReadSxivd( rStrm
);
1682 void XclImpPivotTableManager::ReadSxpi( XclImpStream
& rStrm
)
1684 if( !maPTables
.empty() )
1685 maPTables
.back()->ReadSxpi( rStrm
);
1688 void XclImpPivotTableManager::ReadSxdi( XclImpStream
& rStrm
)
1690 if( !maPTables
.empty() )
1691 maPTables
.back()->ReadSxdi( rStrm
);
1694 void XclImpPivotTableManager::ReadSxvi( XclImpStream
& rStrm
)
1696 if( !maPTables
.empty() )
1697 maPTables
.back()->ReadSxvi( rStrm
);
1700 void XclImpPivotTableManager::ReadSxex( XclImpStream
& rStrm
)
1702 if( !maPTables
.empty() )
1703 maPTables
.back()->ReadSxex( rStrm
);
1706 void XclImpPivotTableManager::ReadSxViewEx9( XclImpStream
& rStrm
)
1708 if( !maPTables
.empty() )
1709 maPTables
.back()->ReadSxViewEx9( rStrm
);
1712 void XclImpPivotTableManager::ReadSxAddl( XclImpStream
& rStrm
)
1714 if( !maPTables
.empty() )
1715 maPTables
.back()->ReadSxAddl( rStrm
);
1718 void XclImpPivotTableManager::ReadPivotCaches( const XclImpStream
& rStrm
)
1720 for( auto& rxPCache
: maPCaches
)
1721 rxPCache
->ReadPivotCacheStream( rStrm
);
1724 void XclImpPivotTableManager::ConvertPivotTables()
1726 for( auto& rxPTable
: maPTables
)
1727 rxPTable
->Convert();
1730 void XclImpPivotTableManager::MaybeRefreshPivotTables()
1732 for( auto& rxPTable
: maPTables
)
1733 rxPTable
->MaybeRefresh();
1736 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */