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 <sal/log.hxx>
31 #include <sot/storage.hxx>
32 #include <unotools/configmgr.hxx>
34 #include <document.hxx>
35 #include <formulacell.hxx>
37 #include <dpdimsave.hxx>
38 #include <dpobject.hxx>
39 #include <dpshttab.hxx>
40 #include <dpoutputgeometry.hxx>
41 #include <scitems.hxx>
44 #include <xltracer.hxx>
45 #include <xistream.hxx>
46 #include <xihelper.hxx>
48 #include <xiescher.hxx>
50 //TODO ExcelToSc usage
51 #include <excform.hxx>
52 #include <documentimport.hxx>
56 using namespace com::sun::star
;
58 using ::com::sun::star::sheet::DataPilotFieldOrientation_DATA
;
59 using ::com::sun::star::sheet::DataPilotFieldSortInfo
;
60 using ::com::sun::star::sheet::DataPilotFieldAutoShowInfo
;
61 using ::com::sun::star::sheet::DataPilotFieldLayoutInfo
;
62 using ::com::sun::star::sheet::DataPilotFieldReference
;
67 XclImpPCItem::XclImpPCItem( XclImpStream
& rStrm
)
69 switch( rStrm
.GetRecId() )
71 case EXC_ID_SXDOUBLE
: ReadSxdouble( rStrm
); break;
72 case EXC_ID_SXBOOLEAN
: ReadSxboolean( rStrm
); break;
73 case EXC_ID_SXERROR
: ReadSxerror( rStrm
); break;
74 case EXC_ID_SXINTEGER
: ReadSxinteger( rStrm
); break;
75 case EXC_ID_SXSTRING
: ReadSxstring( rStrm
); break;
76 case EXC_ID_SXDATETIME
: ReadSxdatetime( rStrm
); break;
77 case EXC_ID_SXEMPTY
: ReadSxempty( rStrm
); break;
78 default: OSL_FAIL( "XclImpPCItem::XclImpPCItem - unknown record id" );
84 void lclSetValue( XclImpRoot
& rRoot
, const ScAddress
& rScPos
, double fValue
, SvNumFormatType nFormatType
)
86 ScDocumentImport
& rDoc
= rRoot
.GetDocImport();
87 rDoc
.setNumericCell(rScPos
, fValue
);
88 sal_uInt32 nScNumFmt
= rRoot
.GetFormatter().GetStandardFormat( nFormatType
, rRoot
.GetDocLanguage() );
89 rDoc
.getDoc().ApplyAttr(
90 rScPos
.Col(), rScPos
.Row(), rScPos
.Tab(), SfxUInt32Item(ATTR_VALUE_FORMAT
, nScNumFmt
));
95 void XclImpPCItem::WriteToSource( XclImpRoot
& rRoot
, const ScAddress
& rScPos
) const
97 ScDocumentImport
& rDoc
= rRoot
.GetDocImport();
98 if( const OUString
* pText
= GetText() )
99 rDoc
.setStringCell(rScPos
, *pText
);
100 else if( const double* pfValue
= GetDouble() )
101 rDoc
.setNumericCell(rScPos
, *pfValue
);
102 else if( const sal_Int16
* pnValue
= GetInteger() )
103 rDoc
.setNumericCell(rScPos
, *pnValue
);
104 else if( const bool* pbValue
= GetBool() )
105 lclSetValue( rRoot
, rScPos
, *pbValue
? 1.0 : 0.0, SvNumFormatType::LOGICAL
);
106 else if( const DateTime
* pDateTime
= GetDateTime() )
108 // set number format date, time, or date/time, depending on the value
109 double fValue
= rRoot
.GetDoubleFromDateTime( *pDateTime
);
111 double fFrac
= modf( fValue
, &fInt
);
112 SvNumFormatType nFormatType
= ((fFrac
== 0.0) && (fInt
!= 0.0)) ? SvNumFormatType::DATE
:
113 ((fInt
== 0.0) ? SvNumFormatType::TIME
: SvNumFormatType::DATETIME
);
114 lclSetValue( rRoot
, rScPos
, fValue
, nFormatType
);
116 else if( const sal_uInt16
* pnError
= GetError() )
119 sal_uInt8 nErrCode
= static_cast< sal_uInt8
>( *pnError
);
120 std::unique_ptr
<ScTokenArray
> pScTokArr
= rRoot
.GetOldFmlaConverter().GetBoolErr(
121 XclTools::ErrorToEnum( fValue
, true, nErrCode
) );
122 ScFormulaCell
* pCell
= pScTokArr
123 ? new ScFormulaCell(&rDoc
.getDoc(), rScPos
, std::move(pScTokArr
))
124 : new ScFormulaCell(&rDoc
.getDoc(), rScPos
);
125 pCell
->SetHybridDouble( fValue
);
126 rDoc
.setFormulaCell(rScPos
, pCell
);
130 void XclImpPCItem::ReadSxdouble( XclImpStream
& rStrm
)
132 OSL_ENSURE( rStrm
.GetRecSize() == 8, "XclImpPCItem::ReadSxdouble - wrong record size" );
133 SetDouble( rStrm
.ReadDouble() );
136 void XclImpPCItem::ReadSxboolean( XclImpStream
& rStrm
)
138 OSL_ENSURE( rStrm
.GetRecSize() == 2, "XclImpPCItem::ReadSxboolean - wrong record size" );
139 SetBool( rStrm
.ReaduInt16() != 0 );
142 void XclImpPCItem::ReadSxerror( XclImpStream
& rStrm
)
144 OSL_ENSURE( rStrm
.GetRecSize() == 2, "XclImpPCItem::ReadSxerror - wrong record size" );
145 SetError( rStrm
.ReaduInt16() );
148 void XclImpPCItem::ReadSxinteger( XclImpStream
& rStrm
)
150 OSL_ENSURE( rStrm
.GetRecSize() == 2, "XclImpPCItem::ReadSxinteger - wrong record size" );
151 SetInteger( rStrm
.ReadInt16() );
154 void XclImpPCItem::ReadSxstring( XclImpStream
& rStrm
)
156 OSL_ENSURE( rStrm
.GetRecSize() >= 3, "XclImpPCItem::ReadSxstring - wrong record size" );
157 SetText( rStrm
.ReadUniString() );
160 void XclImpPCItem::ReadSxdatetime( XclImpStream
& rStrm
)
162 OSL_ENSURE( rStrm
.GetRecSize() == 8, "XclImpPCItem::ReadSxdatetime - wrong record size" );
163 sal_uInt16 nYear
, nMonth
;
164 sal_uInt8 nDay
, nHour
, nMin
, nSec
;
165 nYear
= rStrm
.ReaduInt16();
166 nMonth
= rStrm
.ReaduInt16();
167 nDay
= rStrm
.ReaduInt8();
168 nHour
= rStrm
.ReaduInt8();
169 nMin
= rStrm
.ReaduInt8();
170 nSec
= rStrm
.ReaduInt8();
171 SetDateTime( DateTime( Date( nDay
, nMonth
, nYear
), tools::Time( nHour
, nMin
, nSec
) ) );
174 void XclImpPCItem::ReadSxempty( XclImpStream
& rStrm
)
176 OSL_ENSURE( rStrm
.GetRecSize() == 0, "XclImpPCItem::ReadSxempty - wrong record size" );
180 XclImpPCField::XclImpPCField( const XclImpRoot
& rRoot
, XclImpPivotCache
& rPCache
, sal_uInt16 nFieldIdx
) :
181 XclPCField( EXC_PCFIELD_UNKNOWN
, nFieldIdx
),
185 mbNumGroupInfoRead( false )
189 XclImpPCField::~XclImpPCField()
193 // general field/item access --------------------------------------------------
195 const OUString
& XclImpPCField::GetFieldName( const ScfStringVec
& rVisNames
) const
197 if( IsGroupChildField() && (mnFieldIdx
< rVisNames
.size()) )
199 const OUString
& rVisName
= rVisNames
[ mnFieldIdx
];
200 if (!rVisName
.isEmpty())
203 return maFieldInfo
.maName
;
206 const XclImpPCField
* XclImpPCField::GetGroupBaseField() const
208 OSL_ENSURE( IsGroupChildField(), "XclImpPCField::GetGroupBaseField - this field type does not have a base field" );
209 return IsGroupChildField() ? mrPCache
.GetField( maFieldInfo
.mnGroupBase
) : nullptr;
212 const XclImpPCItem
* XclImpPCField::GetItem( sal_uInt16 nItemIdx
) const
214 return (nItemIdx
< maItems
.size()) ? maItems
[ nItemIdx
].get() : nullptr;
217 const XclImpPCItem
* XclImpPCField::GetLimitItem( sal_uInt16 nItemIdx
) const
219 OSL_ENSURE( nItemIdx
< 3, "XclImpPCField::GetLimitItem - invalid item index" );
220 OSL_ENSURE( nItemIdx
< maNumGroupItems
.size(), "XclImpPCField::GetLimitItem - no item found" );
221 return (nItemIdx
< maNumGroupItems
.size()) ? maNumGroupItems
[ nItemIdx
].get() : nullptr;
224 void XclImpPCField::WriteFieldNameToSource( SCCOL nScCol
, SCTAB nScTab
)
226 OSL_ENSURE( HasOrigItems(), "XclImpPCField::WriteFieldNameToSource - only for standard fields" );
227 GetDocImport().setStringCell(ScAddress(nScCol
, 0, nScTab
), maFieldInfo
.maName
);
228 mnSourceScCol
= nScCol
;
231 void XclImpPCField::WriteOrigItemToSource( SCROW nScRow
, SCTAB nScTab
, sal_uInt16 nItemIdx
)
233 if( nItemIdx
< maOrigItems
.size() )
234 maOrigItems
[ nItemIdx
]->WriteToSource( GetRoot(), ScAddress( mnSourceScCol
, nScRow
, nScTab
) );
237 void XclImpPCField::WriteLastOrigItemToSource( SCROW nScRow
, SCTAB nScTab
)
239 if( !maOrigItems
.empty() )
240 maOrigItems
.back()->WriteToSource( GetRoot(), ScAddress( mnSourceScCol
, nScRow
, nScTab
) );
243 // records --------------------------------------------------------------------
245 void XclImpPCField::ReadSxfield( XclImpStream
& rStrm
)
247 rStrm
>> maFieldInfo
;
249 /* Detect the type of this field. This is done very restrictive to detect
250 any unexpected state. */
251 meFieldType
= EXC_PCFIELD_UNKNOWN
;
253 bool bItems
= ::get_flag( maFieldInfo
.mnFlags
, EXC_SXFIELD_HASITEMS
);
254 bool bPostp
= ::get_flag( maFieldInfo
.mnFlags
, EXC_SXFIELD_POSTPONE
);
255 bool bCalced
= ::get_flag( maFieldInfo
.mnFlags
, EXC_SXFIELD_CALCED
);
256 bool bChild
= ::get_flag( maFieldInfo
.mnFlags
, EXC_SXFIELD_HASCHILD
);
257 bool bNum
= ::get_flag( maFieldInfo
.mnFlags
, EXC_SXFIELD_NUMGROUP
);
259 sal_uInt16 nVisC
= maFieldInfo
.mnVisItems
;
260 sal_uInt16 nGroupC
= maFieldInfo
.mnGroupItems
;
261 sal_uInt16 nBaseC
= maFieldInfo
.mnBaseItems
;
262 sal_uInt16 nOrigC
= maFieldInfo
.mnOrigItems
;
263 OSL_ENSURE( nVisC
> 0, "XclImpPCField::ReadSxfield - field without visible items" );
265 sal_uInt16 nType
= maFieldInfo
.mnFlags
& EXC_SXFIELD_DATA_MASK
;
267 (nType
== EXC_SXFIELD_DATA_STR
) ||
268 (nType
== EXC_SXFIELD_DATA_INT
) ||
269 (nType
== EXC_SXFIELD_DATA_DBL
) ||
270 (nType
== EXC_SXFIELD_DATA_STR_INT
) ||
271 (nType
== EXC_SXFIELD_DATA_STR_DBL
) ||
272 (nType
== EXC_SXFIELD_DATA_DATE
) ||
273 (nType
== EXC_SXFIELD_DATA_DATE_EMP
) ||
274 (nType
== EXC_SXFIELD_DATA_DATE_NUM
) ||
275 (nType
== EXC_SXFIELD_DATA_DATE_STR
);
277 (nType
== EXC_SXFIELD_DATA_NONE
);
278 // for now, ignore data type of calculated fields
279 OSL_ENSURE( bCalced
|| bType
|| bTypeNone
, "XclImpPCField::ReadSxfield - unknown item data type" );
281 if( nVisC
> 0 || bPostp
)
283 if( bItems
&& !bPostp
)
287 // 1) standard fields and standard grouping fields
290 // 1a) standard field without grouping
291 if( bType
&& (nGroupC
== 0) && (nBaseC
== 0) && (nOrigC
== nVisC
) )
292 meFieldType
= EXC_PCFIELD_STANDARD
;
294 // 1b) standard grouping field
295 else if( bTypeNone
&& (nGroupC
== nVisC
) && (nBaseC
> 0) && (nOrigC
== 0) )
296 meFieldType
= EXC_PCFIELD_STDGROUP
;
298 // 2) numerical grouping fields
299 else if( (nGroupC
== nVisC
) && (nBaseC
== 0) )
301 // 2a) single num/date grouping field without child grouping field
302 if( !bChild
&& bType
&& (nOrigC
> 0) )
306 case EXC_SXFIELD_DATA_INT
:
307 case EXC_SXFIELD_DATA_DBL
: meFieldType
= EXC_PCFIELD_NUMGROUP
; break;
308 case EXC_SXFIELD_DATA_DATE
: meFieldType
= EXC_PCFIELD_DATEGROUP
; break;
309 default: OSL_FAIL( "XclImpPCField::ReadSxfield - numeric group with wrong data type" );
313 // 2b) first date grouping field with child grouping field
314 else if( bChild
&& (nType
== EXC_SXFIELD_DATA_DATE
) && (nOrigC
> 0) )
315 meFieldType
= EXC_PCFIELD_DATEGROUP
;
317 // 2c) additional date grouping field
318 else if( bTypeNone
&& (nOrigC
== 0) )
319 meFieldType
= EXC_PCFIELD_DATECHILD
;
321 OSL_ENSURE( meFieldType
!= EXC_PCFIELD_UNKNOWN
, "XclImpPCField::ReadSxfield - invalid standard or grouped field" );
324 // 3) calculated field
327 if( !bChild
&& !bNum
&& (nGroupC
== 0) && (nBaseC
== 0) && (nOrigC
== 0) )
328 meFieldType
= EXC_PCFIELD_CALCED
;
329 OSL_ENSURE( meFieldType
== EXC_PCFIELD_CALCED
, "XclImpPCField::ReadSxfield - invalid calculated field" );
333 else if( !bItems
&& bPostp
)
335 // 4) standard field with postponed items
336 if( !bCalced
&& !bChild
&& !bNum
&& bType
&& (nGroupC
== 0) && (nBaseC
== 0) && (nOrigC
== 0) )
337 meFieldType
= EXC_PCFIELD_STANDARD
;
338 OSL_ENSURE( meFieldType
== EXC_PCFIELD_STANDARD
, "XclImpPCField::ReadSxfield - invalid postponed field" );
343 void XclImpPCField::ReadItem( XclImpStream
& rStrm
)
345 OSL_ENSURE( HasInlineItems() || HasPostponedItems(), "XclImpPCField::ReadItem - field does not expect items" );
348 XclImpPCItemRef
xItem( new XclImpPCItem( rStrm
) );
350 // try to insert into an item list
351 if( mbNumGroupInfoRead
)
353 // there are 3 items after SXNUMGROUP that contain grouping limits and step count
354 if( maNumGroupItems
.size() < 3 )
355 maNumGroupItems
.push_back( xItem
);
357 maOrigItems
.push_back( xItem
);
359 else if( HasInlineItems() || HasPostponedItems() )
361 maItems
.push_back( xItem
);
362 // visible item is original item in standard fields
363 if( IsStandardField() )
364 maOrigItems
.push_back( xItem
);
368 void XclImpPCField::ReadSxnumgroup( XclImpStream
& rStrm
)
370 OSL_ENSURE( IsNumGroupField() || IsDateGroupField(), "XclImpPCField::ReadSxnumgroup - SXNUMGROUP outside numeric grouping field" );
371 OSL_ENSURE( !mbNumGroupInfoRead
, "XclImpPCField::ReadSxnumgroup - multiple SXNUMGROUP records" );
372 OSL_ENSURE( maItems
.size() == maFieldInfo
.mnGroupItems
, "XclImpPCField::ReadSxnumgroup - SXNUMGROUP out of record order" );
373 rStrm
>> maNumGroupInfo
;
374 mbNumGroupInfoRead
= IsNumGroupField() || IsDateGroupField();
377 void XclImpPCField::ReadSxgroupinfo( XclImpStream
& rStrm
)
379 OSL_ENSURE( IsStdGroupField(), "XclImpPCField::ReadSxgroupinfo - SXGROUPINFO outside grouping field" );
380 OSL_ENSURE( maGroupOrder
.empty(), "XclImpPCField::ReadSxgroupinfo - multiple SXGROUPINFO records" );
381 OSL_ENSURE( maItems
.size() == maFieldInfo
.mnGroupItems
, "XclImpPCField::ReadSxgroupinfo - SXGROUPINFO out of record order" );
382 OSL_ENSURE( (rStrm
.GetRecLeft() / 2) == maFieldInfo
.mnBaseItems
, "XclImpPCField::ReadSxgroupinfo - wrong SXGROUPINFO size" );
383 maGroupOrder
.clear();
384 size_t nSize
= rStrm
.GetRecLeft() / 2;
385 maGroupOrder
.resize( nSize
, 0 );
386 for( size_t nIdx
= 0; nIdx
< nSize
; ++nIdx
)
387 maGroupOrder
[ nIdx
] = rStrm
.ReaduInt16();
390 // grouping -------------------------------------------------------------------
392 void XclImpPCField::ConvertGroupField( ScDPSaveData
& rSaveData
, const ScfStringVec
& rVisNames
) const
394 if (!GetFieldName(rVisNames
).isEmpty())
396 if( IsStdGroupField() )
397 ConvertStdGroupField( rSaveData
, rVisNames
);
398 else if( IsNumGroupField() )
399 ConvertNumGroupField( rSaveData
, rVisNames
);
400 else if( IsDateGroupField() )
401 ConvertDateGroupField( rSaveData
, rVisNames
);
405 // private --------------------------------------------------------------------
407 void XclImpPCField::ConvertStdGroupField( ScDPSaveData
& rSaveData
, const ScfStringVec
& rVisNames
) const
409 if( const XclImpPCField
* pBaseField
= GetGroupBaseField() )
411 const OUString
& rBaseFieldName
= pBaseField
->GetFieldName( rVisNames
);
412 if( !rBaseFieldName
.isEmpty() )
414 // *** create a ScDPSaveGroupItem for each own item, they collect base item names ***
415 ScDPSaveGroupItemVec aGroupItems
;
416 aGroupItems
.reserve( maItems
.size() );
417 // initialize with own item names
418 for( const auto& rxItem
: maItems
)
419 aGroupItems
.emplace_back( rxItem
->ConvertToText() );
421 // *** iterate over all base items, set their names at corresponding own items ***
422 for( sal_uInt16 nItemIdx
= 0, nItemCount
= static_cast< sal_uInt16
>( maGroupOrder
.size() ); nItemIdx
< nItemCount
; ++nItemIdx
)
423 if( maGroupOrder
[ nItemIdx
] < aGroupItems
.size() )
424 if( const XclImpPCItem
* pBaseItem
= pBaseField
->GetItem( nItemIdx
) )
425 if( const XclImpPCItem
* pGroupItem
= GetItem( maGroupOrder
[ nItemIdx
] ) )
426 if( *pBaseItem
!= *pGroupItem
)
427 aGroupItems
[ maGroupOrder
[ nItemIdx
] ].AddElement( pBaseItem
->ConvertToText() );
429 // *** create the ScDPSaveGroupDimension object, fill with grouping info ***
430 ScDPSaveGroupDimension
aGroupDim( rBaseFieldName
, GetFieldName( rVisNames
) );
431 for( const auto& rGroupItem
: aGroupItems
)
432 if( !rGroupItem
.IsEmpty() )
433 aGroupDim
.AddGroupItem( rGroupItem
);
434 rSaveData
.GetDimensionData()->AddGroupDimension( aGroupDim
);
439 void XclImpPCField::ConvertNumGroupField( ScDPSaveData
& rSaveData
, const ScfStringVec
& rVisNames
) const
441 ScDPNumGroupInfo
aNumInfo( GetScNumGroupInfo() );
442 ScDPSaveNumGroupDimension
aNumGroupDim( GetFieldName( rVisNames
), aNumInfo
);
443 rSaveData
.GetDimensionData()->AddNumGroupDimension( aNumGroupDim
);
446 void XclImpPCField::ConvertDateGroupField( ScDPSaveData
& rSaveData
, const ScfStringVec
& rVisNames
) const
448 ScDPNumGroupInfo
aDateInfo( GetScDateGroupInfo() );
449 sal_Int32 nScDateType
= maNumGroupInfo
.GetScDateType();
451 switch( meFieldType
)
453 case EXC_PCFIELD_DATEGROUP
:
455 if( aDateInfo
.mbDateValues
)
457 // special case for days only with step value - create numeric grouping
458 ScDPSaveNumGroupDimension
aNumGroupDim( GetFieldName( rVisNames
), aDateInfo
);
459 rSaveData
.GetDimensionData()->AddNumGroupDimension( aNumGroupDim
);
463 ScDPSaveNumGroupDimension
aNumGroupDim( GetFieldName( rVisNames
), ScDPNumGroupInfo() );
464 aNumGroupDim
.SetDateInfo( aDateInfo
, nScDateType
);
465 rSaveData
.GetDimensionData()->AddNumGroupDimension( aNumGroupDim
);
470 case EXC_PCFIELD_DATECHILD
:
472 if( const XclImpPCField
* pBaseField
= GetGroupBaseField() )
474 const OUString
& rBaseFieldName
= pBaseField
->GetFieldName( rVisNames
);
475 if( !rBaseFieldName
.isEmpty() )
477 ScDPSaveGroupDimension
aGroupDim( rBaseFieldName
, GetFieldName( rVisNames
) );
478 aGroupDim
.SetDateInfo( aDateInfo
, nScDateType
);
479 rSaveData
.GetDimensionData()->AddGroupDimension( aGroupDim
);
486 OSL_FAIL( "XclImpPCField::ConvertDateGroupField - unknown date field type" );
490 ScDPNumGroupInfo
XclImpPCField::GetScNumGroupInfo() const
492 ScDPNumGroupInfo aNumInfo
;
493 aNumInfo
.mbEnable
= true;
494 aNumInfo
.mbDateValues
= false;
495 aNumInfo
.mbAutoStart
= true;
496 aNumInfo
.mbAutoEnd
= true;
498 if( const double* pfMinValue
= GetNumGroupLimit( EXC_SXFIELD_INDEX_MIN
) )
500 aNumInfo
.mfStart
= *pfMinValue
;
501 aNumInfo
.mbAutoStart
= ::get_flag( maNumGroupInfo
.mnFlags
, EXC_SXNUMGROUP_AUTOMIN
);
503 if( const double* pfMaxValue
= GetNumGroupLimit( EXC_SXFIELD_INDEX_MAX
) )
505 aNumInfo
.mfEnd
= *pfMaxValue
;
506 aNumInfo
.mbAutoEnd
= ::get_flag( maNumGroupInfo
.mnFlags
, EXC_SXNUMGROUP_AUTOMAX
);
508 if( const double* pfStepValue
= GetNumGroupLimit( EXC_SXFIELD_INDEX_STEP
) )
509 aNumInfo
.mfStep
= *pfStepValue
;
514 ScDPNumGroupInfo
XclImpPCField::GetScDateGroupInfo() const
516 ScDPNumGroupInfo aDateInfo
;
517 aDateInfo
.mbEnable
= true;
518 aDateInfo
.mbDateValues
= false;
519 aDateInfo
.mbAutoStart
= true;
520 aDateInfo
.mbAutoEnd
= true;
522 if( const DateTime
* pMinDate
= GetDateGroupLimit( EXC_SXFIELD_INDEX_MIN
) )
524 aDateInfo
.mfStart
= GetDoubleFromDateTime( *pMinDate
);
525 aDateInfo
.mbAutoStart
= ::get_flag( maNumGroupInfo
.mnFlags
, EXC_SXNUMGROUP_AUTOMIN
);
527 if( const DateTime
* pMaxDate
= GetDateGroupLimit( EXC_SXFIELD_INDEX_MAX
) )
529 aDateInfo
.mfEnd
= GetDoubleFromDateTime( *pMaxDate
);
530 aDateInfo
.mbAutoEnd
= ::get_flag( maNumGroupInfo
.mnFlags
, EXC_SXNUMGROUP_AUTOMAX
);
532 // GetDateGroupStep() returns a value for date type "day" in single date groups only
533 if( const sal_Int16
* pnStepValue
= GetDateGroupStep() )
535 aDateInfo
.mfStep
= *pnStepValue
;
536 aDateInfo
.mbDateValues
= true;
542 const double* XclImpPCField::GetNumGroupLimit( sal_uInt16 nLimitIdx
) const
544 OSL_ENSURE( IsNumGroupField(), "XclImpPCField::GetNumGroupLimit - only for numeric grouping fields" );
545 if( const XclImpPCItem
* pItem
= GetLimitItem( nLimitIdx
) )
547 OSL_ENSURE( pItem
->GetDouble(), "XclImpPCField::GetNumGroupLimit - SXDOUBLE item expected" );
548 return pItem
->GetDouble();
553 const DateTime
* XclImpPCField::GetDateGroupLimit( sal_uInt16 nLimitIdx
) const
555 OSL_ENSURE( IsDateGroupField(), "XclImpPCField::GetDateGroupLimit - only for date grouping fields" );
556 if( const XclImpPCItem
* pItem
= GetLimitItem( nLimitIdx
) )
558 OSL_ENSURE( pItem
->GetDateTime(), "XclImpPCField::GetDateGroupLimit - SXDATETIME item expected" );
559 return pItem
->GetDateTime();
564 const sal_Int16
* XclImpPCField::GetDateGroupStep() const
566 // only for single date grouping fields, not for grouping chains
567 if( !IsGroupBaseField() && !IsGroupChildField() )
569 // only days may have a step value, return 0 for all other date types
570 if( maNumGroupInfo
.GetXclDataType() == EXC_SXNUMGROUP_TYPE_DAY
)
572 if( const XclImpPCItem
* pItem
= GetLimitItem( EXC_SXFIELD_INDEX_STEP
) )
574 OSL_ENSURE( pItem
->GetInteger(), "XclImpPCField::GetDateGroupStep - SXINTEGER item expected" );
575 if( const sal_Int16
* pnStep
= pItem
->GetInteger() )
577 OSL_ENSURE( *pnStep
> 0, "XclImpPCField::GetDateGroupStep - invalid step count" );
578 // return nothing for step count 1 - this is also a standard date group in Excel
579 return (*pnStep
> 1) ? pnStep
: nullptr;
587 XclImpPivotCache::XclImpPivotCache( const XclImpRoot
& rRoot
) :
589 maSrcRange( ScAddress::INITIALIZE_INVALID
),
591 mnSrcType( EXC_SXVS_UNKNOWN
),
596 XclImpPivotCache::~XclImpPivotCache()
600 // data access ----------------------------------------------------------------
602 const XclImpPCField
* XclImpPivotCache::GetField( sal_uInt16 nFieldIdx
) const
604 return (nFieldIdx
< maFields
.size()) ? maFields
[ nFieldIdx
].get() : nullptr;
607 // records --------------------------------------------------------------------
609 void XclImpPivotCache::ReadSxidstm( XclImpStream
& rStrm
)
611 mnStrmId
= rStrm
.ReaduInt16();
614 void XclImpPivotCache::ReadSxvs( XclImpStream
& rStrm
)
616 mnSrcType
= rStrm
.ReaduInt16();
617 GetTracer().TracePivotDataSource( mnSrcType
!= EXC_SXVS_SHEET
);
620 void XclImpPivotCache::ReadDconref( XclImpStream
& rStrm
)
622 /* Read DCONREF only once (by checking maTabName), there may be other
623 DCONREF records in another context. Read reference only if a leading
624 SXVS record is present (by checking mnSrcType). */
625 if( !maTabName
.isEmpty() || (mnSrcType
!= EXC_SXVS_SHEET
) )
628 XclRange
aXclRange( ScAddress::UNINITIALIZED
);
629 aXclRange
.Read( rStrm
, false );
630 OUString aEncUrl
= rStrm
.ReadUniString();
632 XclImpUrlHelper::DecodeUrl( maUrl
, maTabName
, mbSelfRef
, GetRoot(), aEncUrl
);
634 /* Do not convert maTabName to Calc sheet name -> original name is used to
635 find the sheet in the document. Sheet index of source range will be
636 found later in XclImpPivotCache::ReadPivotCacheStream(), because sheet
637 may not exist yet. */
639 GetAddressConverter().ConvertRange( maSrcRange
, aXclRange
, 0, 0, true );
642 void XclImpPivotCache::ReadDConName( XclImpStream
& rStrm
)
644 maSrcRangeName
= rStrm
.ReadUniString();
646 // This 2-byte value equals the length of string that follows, or if 0 it
647 // indicates that the name has a workbook scope. For now, we only support
648 // internal defined name with a workbook scope.
650 nFlag
= rStrm
.ReaduInt16();
651 mbSelfRef
= (nFlag
== 0);
654 // External name is not supported yet.
655 maSrcRangeName
.clear();
658 void XclImpPivotCache::ReadPivotCacheStream( const XclImpStream
& rStrm
)
660 if( (mnSrcType
!= EXC_SXVS_SHEET
) && (mnSrcType
!= EXC_SXVS_EXTERN
) )
663 ScDocument
& rDoc
= GetDoc();
664 SCCOL nFieldScCol
= 0; // column index of source data for next field
665 SCROW nItemScRow
= 0; // row index of source data for current items
666 SCTAB nScTab
= 0; // sheet index of source data
667 bool bGenerateSource
= false; // true = write source data from cache to dummy table
671 if (maSrcRangeName
.isEmpty())
673 // try to find internal sheet containing the source data
674 nScTab
= GetTabInfo().GetScTabFromXclName( maTabName
);
675 if( rDoc
.HasTable( nScTab
) )
677 // set sheet index to source range
678 maSrcRange
.aStart
.SetTab( nScTab
);
679 maSrcRange
.aEnd
.SetTab( nScTab
);
683 // create dummy sheet for deleted internal sheet
684 bGenerateSource
= true;
690 // create dummy sheet for external sheet
691 bGenerateSource
= true;
694 // create dummy sheet for source data from external or deleted sheet
695 if( bGenerateSource
)
697 if( rDoc
.GetTableCount() >= MAXTABCOUNT
)
698 // cannot create more sheets -> exit
701 nScTab
= rDoc
.GetTableCount();
702 rDoc
.MakeTable( nScTab
);
703 OUStringBuffer
aDummyName("DPCache");
704 if( maTabName
.getLength() > 0 )
705 aDummyName
.append( '_' ).append( maTabName
);
706 OUString aName
= aDummyName
.makeStringAndClear();
707 rDoc
.CreateValidTabName( aName
);
708 rDoc
.RenameTab( nScTab
, aName
);
709 // set sheet index to source range
710 maSrcRange
.aStart
.SetTab( nScTab
);
711 maSrcRange
.aEnd
.SetTab( nScTab
);
714 // open pivot cache storage stream
715 tools::SvRef
<SotStorage
> xSvStrg
= OpenStorage( EXC_STORAGE_PTCACHE
);
716 tools::SvRef
<SotStorageStream
> xSvStrm
= OpenStream( xSvStrg
, ScfTools::GetHexStr( mnStrmId
) );
720 // create Excel record stream object
721 XclImpStream
aPCStrm( *xSvStrm
, GetRoot() );
722 aPCStrm
.CopyDecrypterFrom( rStrm
); // pivot cache streams are encrypted
724 XclImpPCFieldRef xCurrField
; // current field for new items
725 XclImpPCFieldVec aOrigFields
; // all standard fields with inline original items
726 XclImpPCFieldVec aPostpFields
; // all standard fields with postponed original items
727 size_t nPostpIdx
= 0; // index to current field with postponed items
728 bool bLoop
= true; // true = continue loop
730 while( bLoop
&& aPCStrm
.StartNextRecord() )
732 switch( aPCStrm
.GetRecId() )
745 sal_uInt16 nNewFieldIdx
= static_cast< sal_uInt16
>( maFields
.size() );
746 if( nNewFieldIdx
< EXC_PC_MAXFIELDCOUNT
)
748 xCurrField
.reset( new XclImpPCField( GetRoot(), *this, nNewFieldIdx
) );
749 maFields
.push_back( xCurrField
);
750 xCurrField
->ReadSxfield( aPCStrm
);
751 if( xCurrField
->HasOrigItems() )
753 if( xCurrField
->HasPostponedItems() )
754 aPostpFields
.push_back( xCurrField
);
756 aOrigFields
.push_back( xCurrField
);
757 // insert field name into generated source data, field remembers its column index
758 if( bGenerateSource
&& (nFieldScCol
<= rDoc
.MaxCol()) )
759 xCurrField
->WriteFieldNameToSource( nFieldScCol
++, nScTab
);
761 // do not read items into invalid/postponed fields
762 if( !xCurrField
->HasInlineItems() )
768 case EXC_ID_SXINDEXLIST
:
769 // read index list and insert all items into generated source data
770 if( bGenerateSource
&& (nItemScRow
<= rDoc
.MaxRow()) && (++nItemScRow
<= rDoc
.MaxRow()) )
772 for( const auto& rxOrigField
: aOrigFields
)
774 sal_uInt16 nItemIdx
= rxOrigField
->Has16BitIndexes() ? aPCStrm
.ReaduInt16() : aPCStrm
.ReaduInt8();
775 rxOrigField
->WriteOrigItemToSource( nItemScRow
, nScTab
, nItemIdx
);
781 case EXC_ID_SXDOUBLE
:
782 case EXC_ID_SXBOOLEAN
:
784 case EXC_ID_SXINTEGER
:
785 case EXC_ID_SXSTRING
:
786 case EXC_ID_SXDATETIME
:
788 if( xCurrField
) // inline items
790 xCurrField
->ReadItem( aPCStrm
);
792 else if( !aPostpFields
.empty() ) // postponed items
794 // read postponed item
795 aPostpFields
[ nPostpIdx
]->ReadItem( aPCStrm
);
796 // write item to source
797 if( bGenerateSource
&& (nItemScRow
<= rDoc
.MaxRow()) )
799 // start new row, if there are only postponed fields
800 if( aOrigFields
.empty() && (nPostpIdx
== 0) )
802 if( nItemScRow
<= rDoc
.MaxRow() )
803 aPostpFields
[ nPostpIdx
]->WriteLastOrigItemToSource( nItemScRow
, nScTab
);
805 // get index of next postponed field
807 if( nPostpIdx
>= aPostpFields
.size() )
812 case EXC_ID_SXNUMGROUP
:
814 xCurrField
->ReadSxnumgroup( aPCStrm
);
817 case EXC_ID_SXGROUPINFO
:
819 xCurrField
->ReadSxgroupinfo( aPCStrm
);
822 // known but ignored records
829 case EXC_ID_SXFORMULA
:
831 case EXC_ID_SXFDBTYPE
:
835 SAL_WARN("sc.filter", "XclImpPivotCache::ReadPivotCacheStream - unknown record 0x" << std::hex
<< aPCStrm
.GetRecId() );
839 OSL_ENSURE( maPCInfo
.mnTotalFields
== maFields
.size(),
840 "XclImpPivotCache::ReadPivotCacheStream - field count mismatch" );
842 if (static_cast<bool>(maPCInfo
.mnFlags
& EXC_SXDB_SAVEDATA
))
844 SCROW nNewEnd
= maSrcRange
.aStart
.Row() + maPCInfo
.mnSrcRecs
;
845 maSrcRange
.aEnd
.SetRow(nNewEnd
);
848 // set source range for external source data
849 if( bGenerateSource
&& (nFieldScCol
> 0) )
851 maSrcRange
.aStart
.SetCol( 0 );
852 maSrcRange
.aStart
.SetRow( 0 );
853 // nFieldScCol points to first unused column
854 maSrcRange
.aEnd
.SetCol( nFieldScCol
- 1 );
855 // nItemScRow points to last used row
856 maSrcRange
.aEnd
.SetRow( nItemScRow
);
860 bool XclImpPivotCache::IsRefreshOnLoad() const
862 return static_cast<bool>(maPCInfo
.mnFlags
& EXC_SXDB_REFRESH_LOAD
);
865 bool XclImpPivotCache::IsValid() const
867 if (!maSrcRangeName
.isEmpty())
870 return maSrcRange
.IsValid();
875 XclImpPTItem::XclImpPTItem( const XclImpPCField
* pCacheField
) :
876 mpCacheField( pCacheField
)
880 const OUString
* XclImpPTItem::GetItemName() const
883 if( const XclImpPCItem
* pCacheItem
= mpCacheField
->GetItem( maItemInfo
.mnCacheIdx
) )
884 //TODO: use XclImpPCItem::ConvertToText(), if all conversions are available
885 return pCacheItem
->IsEmpty() ? nullptr : pCacheItem
->GetText();
889 std::pair
<bool, OUString
> XclImpPTItem::GetItemName(const ScDPSaveDimension
& rSaveDim
, ScDPObject
* pObj
, const XclImpRoot
& rRoot
) const
892 return std::pair
<bool, OUString
>(false, OUString());
894 const XclImpPCItem
* pCacheItem
= mpCacheField
->GetItem( maItemInfo
.mnCacheIdx
);
896 return std::pair
<bool, OUString
>(false, OUString());
899 if(pCacheItem
->GetType() == EXC_PCITEM_TEXT
|| pCacheItem
->GetType() == EXC_PCITEM_ERROR
)
901 const OUString
* pItemName
= pCacheItem
->GetText();
903 return std::pair
<bool, OUString
>(false, OUString());
904 sItemName
= *pItemName
;
906 else if (pCacheItem
->GetType() == EXC_PCITEM_DOUBLE
)
908 sItemName
= pObj
->GetFormattedString(rSaveDim
.GetName(), *pCacheItem
->GetDouble());
910 else if (pCacheItem
->GetType() == EXC_PCITEM_INTEGER
)
912 sItemName
= pObj
->GetFormattedString(rSaveDim
.GetName(), static_cast<double>(*pCacheItem
->GetInteger()));
914 else if (pCacheItem
->GetType() == EXC_PCITEM_BOOL
)
916 sItemName
= pObj
->GetFormattedString(rSaveDim
.GetName(), static_cast<double>(*pCacheItem
->GetBool()));
918 else if (pCacheItem
->GetType() == EXC_PCITEM_DATETIME
)
920 sItemName
= pObj
->GetFormattedString(rSaveDim
.GetName(), rRoot
.GetDoubleFromDateTime(*pCacheItem
->GetDateTime()));
922 else if (pCacheItem
->GetType() == EXC_PCITEM_EMPTY
)
924 // sItemName is an empty string
926 else // EXC_PCITEM_INVALID
927 return std::pair
<bool, OUString
>(false, OUString());
929 return std::pair
<bool, OUString
>(true, sItemName
);
932 void XclImpPTItem::ReadSxvi( XclImpStream
& rStrm
)
937 void XclImpPTItem::ConvertItem( ScDPSaveDimension
& rSaveDim
, ScDPObject
* pObj
, const XclImpRoot
& rRoot
) const
939 // Find member and set properties
940 std::pair
<bool, OUString
> aReturnedName
= GetItemName(rSaveDim
, pObj
, rRoot
);
941 if(aReturnedName
.first
)
943 ScDPSaveMember
* pMember
= rSaveDim
.GetExistingMemberByName(aReturnedName
.second
);
946 pMember
->SetIsVisible( !::get_flag( maItemInfo
.mnFlags
, EXC_SXVI_HIDDEN
) );
947 pMember
->SetShowDetails( !::get_flag( maItemInfo
.mnFlags
, EXC_SXVI_HIDEDETAIL
) );
948 if (maItemInfo
.HasVisName())
949 pMember
->SetLayoutName(*maItemInfo
.GetVisName());
954 XclImpPTField::XclImpPTField( const XclImpPivotTable
& rPTable
, sal_uInt16 nCacheIdx
) :
957 maFieldInfo
.mnCacheIdx
= nCacheIdx
;
960 // general field/item access --------------------------------------------------
962 const XclImpPCField
* XclImpPTField::GetCacheField() const
964 XclImpPivotCacheRef xPCache
= mrPTable
.GetPivotCache();
965 return xPCache
? xPCache
->GetField( maFieldInfo
.mnCacheIdx
) : nullptr;
968 OUString
XclImpPTField::GetFieldName() const
970 const XclImpPCField
* pField
= GetCacheField();
971 return pField
? pField
->GetFieldName( mrPTable
.GetVisFieldNames() ) : OUString();
974 OUString
XclImpPTField::GetVisFieldName() const
976 const OUString
* pVisName
= maFieldInfo
.GetVisName();
977 return pVisName
? *pVisName
: OUString();
980 const XclImpPTItem
* XclImpPTField::GetItem( sal_uInt16 nItemIdx
) const
982 return (nItemIdx
< maItems
.size()) ? maItems
[ nItemIdx
].get() : nullptr;
985 const OUString
* XclImpPTField::GetItemName( sal_uInt16 nItemIdx
) const
987 const XclImpPTItem
* pItem
= GetItem( nItemIdx
);
988 return pItem
? pItem
->GetItemName() : nullptr;
991 // records --------------------------------------------------------------------
993 void XclImpPTField::ReadSxvd( XclImpStream
& rStrm
)
995 rStrm
>> maFieldInfo
;
998 void XclImpPTField::ReadSxvdex( XclImpStream
& rStrm
)
1000 rStrm
>> maFieldExtInfo
;
1003 void XclImpPTField::ReadSxvi( XclImpStream
& rStrm
)
1005 XclImpPTItemRef
xItem( new XclImpPTItem( GetCacheField() ) );
1006 maItems
.push_back( xItem
);
1007 xItem
->ReadSxvi( rStrm
);
1010 // row/column fields ----------------------------------------------------------
1012 void XclImpPTField::ConvertRowColField( ScDPSaveData
& rSaveData
) const
1014 OSL_ENSURE( maFieldInfo
.mnAxes
& EXC_SXVD_AXIS_ROWCOL
, "XclImpPTField::ConvertRowColField - no row/column field" );
1015 // special data orientation field?
1016 if( maFieldInfo
.mnCacheIdx
== EXC_SXIVD_DATA
)
1017 rSaveData
.GetDataLayoutDimension()->SetOrientation( maFieldInfo
.GetApiOrient( EXC_SXVD_AXIS_ROWCOL
) );
1019 ConvertRCPField( rSaveData
);
1022 // page fields ----------------------------------------------------------------
1024 void XclImpPTField::SetPageFieldInfo( const XclPTPageFieldInfo
& rPageInfo
)
1026 maPageInfo
= rPageInfo
;
1029 void XclImpPTField::ConvertPageField( ScDPSaveData
& rSaveData
) const
1031 OSL_ENSURE( maFieldInfo
.mnAxes
& EXC_SXVD_AXIS_PAGE
, "XclImpPTField::ConvertPageField - no page field" );
1032 ConvertRCPField( rSaveData
);
1035 // hidden fields --------------------------------------------------------------
1037 void XclImpPTField::ConvertHiddenField( ScDPSaveData
& rSaveData
) const
1039 OSL_ENSURE( (maFieldInfo
.mnAxes
& EXC_SXVD_AXIS_ROWCOLPAGE
) == 0, "XclImpPTField::ConvertHiddenField - field not hidden" );
1040 ConvertRCPField( rSaveData
);
1043 // data fields ----------------------------------------------------------------
1045 bool XclImpPTField::HasDataFieldInfo() const
1047 return !maDataInfoVector
.empty();
1050 void XclImpPTField::AddDataFieldInfo( const XclPTDataFieldInfo
& rDataInfo
)
1052 OSL_ENSURE( maFieldInfo
.mnAxes
& EXC_SXVD_AXIS_DATA
, "XclImpPTField::AddDataFieldInfo - no data field" );
1053 maDataInfoVector
.push_back( rDataInfo
);
1056 void XclImpPTField::ConvertDataField( ScDPSaveData
& rSaveData
) const
1058 OSL_ENSURE( maFieldInfo
.mnAxes
& EXC_SXVD_AXIS_DATA
, "XclImpPTField::ConvertDataField - no data field" );
1059 OSL_ENSURE( !maDataInfoVector
.empty(), "XclImpPTField::ConvertDataField - no data field info" );
1060 if (maDataInfoVector
.empty())
1063 OUString aFieldName
= GetFieldName();
1064 if (aFieldName
.isEmpty())
1067 ScDPSaveDimension
* pSaveDim
= rSaveData
.GetNewDimensionByName(aFieldName
);
1070 SAL_WARN("sc.filter","XclImpPTField::ConvertDataField - field name not found: " << aFieldName
);
1074 auto aIt
= maDataInfoVector
.begin(), aEnd
= maDataInfoVector
.end();
1076 ConvertDataField( *pSaveDim
, *aIt
);
1078 // multiple data fields -> clone dimension
1079 for( ++aIt
; aIt
!= aEnd
; ++aIt
)
1081 ScDPSaveDimension
& rDupDim
= rSaveData
.DuplicateDimension( *pSaveDim
);
1082 ConvertDataFieldInfo( rDupDim
, *aIt
);
1086 // private --------------------------------------------------------------------
1089 * Convert Excel-encoded subtotal name to a Calc-encoded one.
1091 static OUString
lcl_convertExcelSubtotalName(const OUString
& rName
)
1093 OUStringBuffer aBuf
;
1094 const sal_Unicode
* p
= rName
.getStr();
1095 sal_Int32 n
= rName
.getLength();
1096 for (sal_Int32 i
= 0; i
< n
; ++i
)
1098 const sal_Unicode c
= p
[i
];
1107 return aBuf
.makeStringAndClear();
1110 void XclImpPTField::ConvertRCPField( ScDPSaveData
& rSaveData
) const
1112 const OUString
& rFieldName
= GetFieldName();
1113 if( rFieldName
.isEmpty() )
1116 const XclImpPCField
* pCacheField
= GetCacheField();
1117 if( !pCacheField
|| !pCacheField
->IsSupportedField() )
1120 ScDPSaveDimension
* pTest
= rSaveData
.GetNewDimensionByName(rFieldName
);
1124 ScDPSaveDimension
& rSaveDim
= *pTest
;
1127 rSaveDim
.SetOrientation( maFieldInfo
.GetApiOrient( EXC_SXVD_AXIS_ROWCOLPAGE
) );
1130 if (const OUString
* pVisName
= maFieldInfo
.GetVisName())
1131 if (!pVisName
->isEmpty())
1132 rSaveDim
.SetLayoutName( *pVisName
);
1134 // subtotal function(s)
1135 XclPTSubtotalVec aSubtotalVec
;
1136 maFieldInfo
.GetSubtotals( aSubtotalVec
);
1137 if( !aSubtotalVec
.empty() )
1138 rSaveDim
.SetSubTotals( aSubtotalVec
);
1141 DataPilotFieldSortInfo aSortInfo
;
1142 aSortInfo
.Field
= mrPTable
.GetDataFieldName( maFieldExtInfo
.mnSortField
);
1143 aSortInfo
.IsAscending
= ::get_flag( maFieldExtInfo
.mnFlags
, EXC_SXVDEX_SORT_ASC
);
1144 aSortInfo
.Mode
= maFieldExtInfo
.GetApiSortMode();
1145 rSaveDim
.SetSortInfo( &aSortInfo
);
1148 DataPilotFieldAutoShowInfo aShowInfo
;
1149 aShowInfo
.IsEnabled
= ::get_flag( maFieldExtInfo
.mnFlags
, EXC_SXVDEX_AUTOSHOW
);
1150 aShowInfo
.ShowItemsMode
= maFieldExtInfo
.GetApiAutoShowMode();
1151 aShowInfo
.ItemCount
= maFieldExtInfo
.GetApiAutoShowCount();
1152 aShowInfo
.DataField
= mrPTable
.GetDataFieldName( maFieldExtInfo
.mnShowField
);
1153 rSaveDim
.SetAutoShowInfo( &aShowInfo
);
1156 DataPilotFieldLayoutInfo aLayoutInfo
;
1157 aLayoutInfo
.LayoutMode
= maFieldExtInfo
.GetApiLayoutMode();
1158 aLayoutInfo
.AddEmptyLines
= ::get_flag( maFieldExtInfo
.mnFlags
, EXC_SXVDEX_LAYOUT_BLANK
);
1159 rSaveDim
.SetLayoutInfo( &aLayoutInfo
);
1162 pCacheField
->ConvertGroupField( rSaveData
, mrPTable
.GetVisFieldNames() );
1164 // custom subtotal name
1165 if (maFieldExtInfo
.mpFieldTotalName
)
1167 OUString aSubName
= lcl_convertExcelSubtotalName(*maFieldExtInfo
.mpFieldTotalName
);
1168 rSaveDim
.SetSubtotalName(aSubName
);
1172 void XclImpPTField::ConvertFieldInfo( const ScDPSaveData
& rSaveData
, ScDPObject
* pObj
, const XclImpRoot
& rRoot
, bool bPageField
) const
1174 const OUString
& rFieldName
= GetFieldName();
1175 if( rFieldName
.isEmpty() )
1178 const XclImpPCField
* pCacheField
= GetCacheField();
1179 if( !pCacheField
|| !pCacheField
->IsSupportedField() )
1182 ScDPSaveDimension
* pSaveDim
= rSaveData
.GetExistingDimensionByName(rFieldName
);
1186 pSaveDim
->SetShowEmpty( ::get_flag( maFieldExtInfo
.mnFlags
, EXC_SXVDEX_SHOWALL
) );
1187 for( const auto& rxItem
: maItems
)
1188 rxItem
->ConvertItem( *pSaveDim
, pObj
, rRoot
);
1190 if(bPageField
&& maPageInfo
.mnSelItem
!= EXC_SXPI_ALLITEMS
)
1192 const XclImpPTItem
* pItem
= GetItem( maPageInfo
.mnSelItem
);
1195 std::pair
<bool, OUString
> aReturnedName
= pItem
->GetItemName(*pSaveDim
, pObj
, rRoot
);
1196 if(aReturnedName
.first
)
1197 pSaveDim
->SetCurrentPage(&aReturnedName
.second
);
1202 void XclImpPTField::ConvertDataField( ScDPSaveDimension
& rSaveDim
, const XclPTDataFieldInfo
& rDataInfo
) const
1205 rSaveDim
.SetOrientation( DataPilotFieldOrientation_DATA
);
1206 // extended data field info
1207 ConvertDataFieldInfo( rSaveDim
, rDataInfo
);
1210 void XclImpPTField::ConvertDataFieldInfo( ScDPSaveDimension
& rSaveDim
, const XclPTDataFieldInfo
& rDataInfo
) const
1213 const OUString
* pVisName
= rDataInfo
.GetVisName();
1214 if (pVisName
&& !pVisName
->isEmpty())
1215 rSaveDim
.SetLayoutName(*pVisName
);
1217 // aggregation function
1218 rSaveDim
.SetFunction( rDataInfo
.GetApiAggFunc() );
1220 // result field reference
1221 sal_Int32 nRefType
= rDataInfo
.GetApiRefType();
1222 DataPilotFieldReference aFieldRef
;
1223 aFieldRef
.ReferenceType
= nRefType
;
1224 const XclImpPTField
* pRefField
= mrPTable
.GetField(rDataInfo
.mnRefField
);
1227 aFieldRef
.ReferenceField
= pRefField
->GetFieldName();
1228 aFieldRef
.ReferenceItemType
= rDataInfo
.GetApiRefItemType();
1229 if (aFieldRef
.ReferenceItemType
== sheet::DataPilotFieldReferenceItemType::NAMED
)
1231 const OUString
* pRefItemName
= pRefField
->GetItemName(rDataInfo
.mnRefItem
);
1233 aFieldRef
.ReferenceItemName
= *pRefItemName
;
1237 rSaveDim
.SetReferenceValue(&aFieldRef
);
1240 XclImpPivotTable::XclImpPivotTable( const XclImpRoot
& rRoot
) :
1241 XclImpRoot( rRoot
),
1242 maDataOrientField( *this, EXC_SXIVD_DATA
),
1247 XclImpPivotTable::~XclImpPivotTable()
1251 // cache/field access, misc. --------------------------------------------------
1253 sal_uInt16
XclImpPivotTable::GetFieldCount() const
1255 return static_cast< sal_uInt16
>( maFields
.size() );
1258 const XclImpPTField
* XclImpPivotTable::GetField( sal_uInt16 nFieldIdx
) const
1260 return (nFieldIdx
== EXC_SXIVD_DATA
) ? &maDataOrientField
:
1261 ((nFieldIdx
< maFields
.size()) ? maFields
[ nFieldIdx
].get() : nullptr);
1264 XclImpPTField
* XclImpPivotTable::GetFieldAcc( sal_uInt16 nFieldIdx
)
1266 // do not return maDataOrientField
1267 return (nFieldIdx
< maFields
.size()) ? maFields
[ nFieldIdx
].get() : nullptr;
1270 const XclImpPTField
* XclImpPivotTable::GetDataField( sal_uInt16 nDataFieldIdx
) const
1272 if( nDataFieldIdx
< maOrigDataFields
.size() )
1273 return GetField( maOrigDataFields
[ nDataFieldIdx
] );
1277 OUString
XclImpPivotTable::GetDataFieldName( sal_uInt16 nDataFieldIdx
) const
1279 if( const XclImpPTField
* pField
= GetDataField( nDataFieldIdx
) )
1280 return pField
->GetFieldName();
1284 // records --------------------------------------------------------------------
1286 void XclImpPivotTable::ReadSxview( XclImpStream
& rStrm
)
1290 GetAddressConverter().ConvertRange(
1291 maOutScRange
, maPTInfo
.maOutXclRange
, GetCurrScTab(), GetCurrScTab(), true );
1293 mxPCache
= GetPivotTableManager().GetPivotCache( maPTInfo
.mnCacheIdx
);
1294 mxCurrField
.reset();
1297 void XclImpPivotTable::ReadSxvd( XclImpStream
& rStrm
)
1299 sal_uInt16 nFieldCount
= GetFieldCount();
1300 if( nFieldCount
< EXC_PT_MAXFIELDCOUNT
)
1302 // cache index for the field is equal to the SXVD record index
1303 mxCurrField
.reset( new XclImpPTField( *this, nFieldCount
) );
1304 maFields
.push_back( mxCurrField
);
1305 mxCurrField
->ReadSxvd( rStrm
);
1306 // add visible name of new field to list of visible names
1307 maVisFieldNames
.push_back( mxCurrField
->GetVisFieldName() );
1308 OSL_ENSURE( maFields
.size() == maVisFieldNames
.size(),
1309 "XclImpPivotTable::ReadSxvd - wrong size of visible name array" );
1312 mxCurrField
.reset();
1315 void XclImpPivotTable::ReadSxvi( XclImpStream
& rStrm
)
1318 mxCurrField
->ReadSxvi( rStrm
);
1321 void XclImpPivotTable::ReadSxvdex( XclImpStream
& rStrm
)
1324 mxCurrField
->ReadSxvdex( rStrm
);
1327 void XclImpPivotTable::ReadSxivd( XclImpStream
& rStrm
)
1329 mxCurrField
.reset();
1331 // find the index vector to fill (row SXIVD doesn't exist without row fields)
1332 ScfUInt16Vec
* pFieldVec
= nullptr;
1333 if( maRowFields
.empty() && (maPTInfo
.mnRowFields
> 0) )
1334 pFieldVec
= &maRowFields
;
1335 else if( maColFields
.empty() && (maPTInfo
.mnColFields
> 0) )
1336 pFieldVec
= &maColFields
;
1338 // fill the vector from record data
1341 sal_uInt16 nSize
= ulimit_cast
< sal_uInt16
>( rStrm
.GetRecSize() / 2, EXC_PT_MAXROWCOLCOUNT
);
1342 pFieldVec
->reserve( nSize
);
1343 for( sal_uInt16 nIdx
= 0; nIdx
< nSize
; ++nIdx
)
1345 sal_uInt16 nFieldIdx
;
1346 nFieldIdx
= rStrm
.ReaduInt16();
1347 pFieldVec
->push_back( nFieldIdx
);
1349 // set orientation at special data orientation field
1350 if( nFieldIdx
== EXC_SXIVD_DATA
)
1352 sal_uInt16 nAxis
= (pFieldVec
== &maRowFields
) ? EXC_SXVD_AXIS_ROW
: EXC_SXVD_AXIS_COL
;
1353 maDataOrientField
.SetAxes( nAxis
);
1359 void XclImpPivotTable::ReadSxpi( XclImpStream
& rStrm
)
1361 mxCurrField
.reset();
1363 sal_uInt16 nSize
= ulimit_cast
< sal_uInt16
>( rStrm
.GetRecSize() / 6 );
1364 for( sal_uInt16 nEntry
= 0; nEntry
< nSize
; ++nEntry
)
1366 XclPTPageFieldInfo aPageInfo
;
1368 if( XclImpPTField
* pField
= GetFieldAcc( aPageInfo
.mnField
) )
1370 maPageFields
.push_back( aPageInfo
.mnField
);
1371 pField
->SetPageFieldInfo( aPageInfo
);
1373 GetCurrSheetDrawing().SetSkipObj( aPageInfo
.mnObjId
);
1377 void XclImpPivotTable::ReadSxdi( XclImpStream
& rStrm
)
1379 mxCurrField
.reset();
1381 XclPTDataFieldInfo aDataInfo
;
1383 if( XclImpPTField
* pField
= GetFieldAcc( aDataInfo
.mnField
) )
1385 maOrigDataFields
.push_back( aDataInfo
.mnField
);
1386 // DataPilot does not support double data fields -> add first appearance to index list only
1387 if( !pField
->HasDataFieldInfo() )
1388 maFiltDataFields
.push_back( aDataInfo
.mnField
);
1389 pField
->AddDataFieldInfo( aDataInfo
);
1393 void XclImpPivotTable::ReadSxex( XclImpStream
& rStrm
)
1395 rStrm
>> maPTExtInfo
;
1398 void XclImpPivotTable::ReadSxViewEx9( XclImpStream
& rStrm
)
1400 rStrm
>> maPTViewEx9Info
;
1403 void XclImpPivotTable::ReadSxAddl( XclImpStream
& rStrm
)
1405 rStrm
>> maPTAddlInfo
;
1408 void XclImpPivotTable::Convert()
1410 if( !mxPCache
|| !mxPCache
->IsValid() )
1413 if (utl::ConfigManager::IsFuzzing()) //just too slow
1416 ScDPSaveData aSaveData
;
1418 // *** global settings ***
1420 aSaveData
.SetRowGrand( ::get_flag( maPTInfo
.mnFlags
, EXC_SXVIEW_ROWGRAND
) );
1421 aSaveData
.SetColumnGrand( ::get_flag( maPTInfo
.mnFlags
, EXC_SXVIEW_COLGRAND
) );
1422 aSaveData
.SetFilterButton( false );
1423 aSaveData
.SetDrillDown( ::get_flag( maPTExtInfo
.mnFlags
, EXC_SXEX_DRILLDOWN
) );
1424 aSaveData
.SetIgnoreEmptyRows( false );
1425 aSaveData
.SetRepeatIfEmpty( false );
1430 for( const auto& rRowField
: maRowFields
)
1431 if( const XclImpPTField
* pField
= GetField( rRowField
) )
1432 pField
->ConvertRowColField( aSaveData
);
1435 for( const auto& rColField
: maColFields
)
1436 if( const XclImpPTField
* pField
= GetField( rColField
) )
1437 pField
->ConvertRowColField( aSaveData
);
1440 for( const auto& rPageField
: maPageFields
)
1441 if( const XclImpPTField
* pField
= GetField( rPageField
) )
1442 pField
->ConvertPageField( aSaveData
);
1444 // We need to import hidden fields because hidden fields may contain
1445 // special settings for subtotals (aggregation function, filters, custom
1446 // name etc.) and members (hidden, custom name etc.).
1449 for( sal_uInt16 nField
= 0, nCount
= GetFieldCount(); nField
< nCount
; ++nField
)
1450 if( const XclImpPTField
* pField
= GetField( nField
) )
1451 if (!pField
->GetAxes())
1452 pField
->ConvertHiddenField( aSaveData
);
1455 for( const auto& rFiltDataField
: maFiltDataFields
)
1456 if( const XclImpPTField
* pField
= GetField( rFiltDataField
) )
1457 pField
->ConvertDataField( aSaveData
);
1459 // *** insert into Calc document ***
1461 // create source descriptor
1462 ScSheetSourceDesc
aDesc(&GetDoc());
1463 const OUString
& rSrcName
= mxPCache
->GetSourceRangeName();
1464 if (!rSrcName
.isEmpty())
1465 // Range name is the data source.
1466 aDesc
.SetRangeName(rSrcName
);
1468 // Normal cell range.
1469 aDesc
.SetSourceRange(mxPCache
->GetSourceRange());
1471 // adjust output range to include the page fields
1472 ScRange
aOutRange( maOutScRange
);
1473 if( !maPageFields
.empty() )
1475 SCROW nDecRows
= ::std::min
< SCROW
>( aOutRange
.aStart
.Row(), maPageFields
.size() + 1 );
1476 aOutRange
.aStart
.IncRow( -nDecRows
);
1479 // create the DataPilot
1480 std::unique_ptr
<ScDPObject
> pDPObj(new ScDPObject( &GetDoc() ));
1481 pDPObj
->SetName( maPTInfo
.maTableName
);
1482 if (!maPTInfo
.maDataName
.isEmpty())
1483 aSaveData
.GetDataLayoutDimension()->SetLayoutName(maPTInfo
.maDataName
);
1485 if (!maPTViewEx9Info
.maGrandTotalName
.isEmpty())
1486 aSaveData
.SetGrandTotalName(maPTViewEx9Info
.maGrandTotalName
);
1488 pDPObj
->SetSaveData( aSaveData
);
1489 pDPObj
->SetSheetDesc( aDesc
);
1490 pDPObj
->SetOutRange( aOutRange
);
1491 pDPObj
->SetHeaderLayout( maPTViewEx9Info
.mnGridLayout
== 0 );
1493 mpDPObj
= GetDoc().GetDPCollection()->InsertNewTable(std::move(pDPObj
));
1496 ApplyMergeFlags(aOutRange
, aSaveData
);
1499 void XclImpPivotTable::MaybeRefresh()
1501 if (mpDPObj
&& mxPCache
->IsRefreshOnLoad())
1503 // 'refresh table on load' flag is set. Refresh the table now. Some
1504 // Excel files contain partial table output when this flag is set.
1505 ScRange aOutRange
= mpDPObj
->GetOutRange();
1506 mpDPObj
->Output(aOutRange
.aStart
);
1510 void XclImpPivotTable::ApplyMergeFlags(const ScRange
& rOutRange
, const ScDPSaveData
& rSaveData
)
1512 // Apply merge flags for various datapilot controls.
1514 ScDPOutputGeometry
aGeometry(rOutRange
, false);
1515 aGeometry
.setColumnFieldCount(maPTInfo
.mnColFields
);
1516 aGeometry
.setPageFieldCount(maPTInfo
.mnPageFields
);
1517 aGeometry
.setDataFieldCount(maPTInfo
.mnDataFields
);
1518 aGeometry
.setRowFieldCount(maPTInfo
.mnRowFields
);
1520 // Make sure we set headerlayout when input file has additional raw header
1521 if(maPTInfo
.mnColFields
== 0)
1523 mpDPObj
->SetHeaderLayout( maPTInfo
.mnFirstHeadRow
- 2 == static_cast<sal_uInt16
>(aGeometry
.getRowFieldHeaderRow()) );
1525 aGeometry
.setHeaderLayout(mpDPObj
->GetHeaderLayout());
1526 aGeometry
.setCompactMode(maPTAddlInfo
.mbCompactMode
);
1528 ScDocument
& rDoc
= GetDoc();
1530 vector
<const ScDPSaveDimension
*> aFieldDims
;
1531 vector
<ScAddress
> aFieldBtns
;
1533 aGeometry
.getPageFieldPositions(aFieldBtns
);
1534 for (const auto& rFieldBtn
: aFieldBtns
)
1536 rDoc
.ApplyFlagsTab(rFieldBtn
.Col(), rFieldBtn
.Row(), rFieldBtn
.Col(), rFieldBtn
.Row(), rFieldBtn
.Tab(), ScMF::Button
);
1538 ScMF nMFlag
= ScMF::ButtonPopup
;
1539 OUString aName
= rDoc
.GetString(rFieldBtn
.Col(), rFieldBtn
.Row(), rFieldBtn
.Tab());
1540 if (rSaveData
.HasInvisibleMember(aName
))
1541 nMFlag
|= ScMF::HiddenMember
;
1543 rDoc
.ApplyFlagsTab(rFieldBtn
.Col()+1, rFieldBtn
.Row(), rFieldBtn
.Col()+1, rFieldBtn
.Row(), rFieldBtn
.Tab(), nMFlag
);
1546 aGeometry
.getColumnFieldPositions(aFieldBtns
);
1547 rSaveData
.GetAllDimensionsByOrientation(sheet::DataPilotFieldOrientation_COLUMN
, aFieldDims
);
1548 if (aFieldBtns
.size() == aFieldDims
.size())
1550 vector
<const ScDPSaveDimension
*>::const_iterator itDim
= aFieldDims
.begin();
1551 for (const auto& rFieldBtn
: aFieldBtns
)
1553 ScMF nMFlag
= ScMF::Button
;
1554 const ScDPSaveDimension
* pDim
= *itDim
;
1555 if (pDim
->HasInvisibleMember())
1556 nMFlag
|= ScMF::HiddenMember
;
1557 if (!pDim
->IsDataLayout())
1558 nMFlag
|= ScMF::ButtonPopup
;
1559 rDoc
.ApplyFlagsTab(rFieldBtn
.Col(), rFieldBtn
.Row(), rFieldBtn
.Col(), rFieldBtn
.Row(), rFieldBtn
.Tab(), nMFlag
);
1564 aGeometry
.getRowFieldPositions(aFieldBtns
);
1565 rSaveData
.GetAllDimensionsByOrientation(sheet::DataPilotFieldOrientation_ROW
, aFieldDims
);
1566 if ((aFieldBtns
.size() == aFieldDims
.size()) || (maPTAddlInfo
.mbCompactMode
&& aFieldBtns
.size() == 1))
1568 vector
<const ScDPSaveDimension
*>::const_iterator itDim
= aFieldDims
.begin();
1569 for (const auto& rFieldBtn
: aFieldBtns
)
1571 ScMF nMFlag
= ScMF::Button
;
1572 const ScDPSaveDimension
* pDim
= itDim
!= aFieldDims
.end() ? *itDim
++ : nullptr;
1573 if (pDim
&& pDim
->HasInvisibleMember())
1574 nMFlag
|= ScMF::HiddenMember
;
1575 if (!pDim
|| !pDim
->IsDataLayout())
1576 nMFlag
|= ScMF::ButtonPopup
;
1577 rDoc
.ApplyFlagsTab(rFieldBtn
.Col(), rFieldBtn
.Row(), rFieldBtn
.Col(), rFieldBtn
.Row(), rFieldBtn
.Tab(), nMFlag
);
1583 void XclImpPivotTable::ApplyFieldInfo()
1585 mpDPObj
->BuildAllDimensionMembers();
1586 ScDPSaveData
& rSaveData
= *mpDPObj
->GetSaveData();
1589 for( const auto& rRowField
: maRowFields
)
1590 if( const XclImpPTField
* pField
= GetField( rRowField
) )
1591 pField
->ConvertFieldInfo( rSaveData
, mpDPObj
, *this );
1594 for( const auto& rColField
: maColFields
)
1595 if( const XclImpPTField
* pField
= GetField( rColField
) )
1596 pField
->ConvertFieldInfo( rSaveData
, mpDPObj
, *this );
1599 for( const auto& rPageField
: maPageFields
)
1600 if( const XclImpPTField
* pField
= GetField( rPageField
) )
1601 pField
->ConvertFieldInfo( rSaveData
, mpDPObj
, *this, true );
1604 for( sal_uInt16 nField
= 0, nCount
= GetFieldCount(); nField
< nCount
; ++nField
)
1605 if( const XclImpPTField
* pField
= GetField( nField
) )
1606 if (!pField
->GetAxes())
1607 pField
->ConvertFieldInfo( rSaveData
, mpDPObj
, *this );
1610 XclImpPivotTableManager::XclImpPivotTableManager( const XclImpRoot
& rRoot
) :
1615 XclImpPivotTableManager::~XclImpPivotTableManager()
1619 // pivot cache records --------------------------------------------------------
1621 XclImpPivotCacheRef
XclImpPivotTableManager::GetPivotCache( sal_uInt16 nCacheIdx
)
1623 XclImpPivotCacheRef xPCache
;
1624 if( nCacheIdx
< maPCaches
.size() )
1625 xPCache
= maPCaches
[ nCacheIdx
];
1629 void XclImpPivotTableManager::ReadSxidstm( XclImpStream
& rStrm
)
1631 XclImpPivotCacheRef
xPCache( new XclImpPivotCache( GetRoot() ) );
1632 maPCaches
.push_back( xPCache
);
1633 xPCache
->ReadSxidstm( rStrm
);
1636 void XclImpPivotTableManager::ReadSxvs( XclImpStream
& rStrm
)
1638 if( !maPCaches
.empty() )
1639 maPCaches
.back()->ReadSxvs( rStrm
);
1642 void XclImpPivotTableManager::ReadDconref( XclImpStream
& rStrm
)
1644 if( !maPCaches
.empty() )
1645 maPCaches
.back()->ReadDconref( rStrm
);
1648 void XclImpPivotTableManager::ReadDConName( XclImpStream
& rStrm
)
1650 if( !maPCaches
.empty() )
1651 maPCaches
.back()->ReadDConName( rStrm
);
1654 // pivot table records --------------------------------------------------------
1656 void XclImpPivotTableManager::ReadSxview( XclImpStream
& rStrm
)
1658 XclImpPivotTableRef
xPTable( new XclImpPivotTable( GetRoot() ) );
1659 maPTables
.push_back( xPTable
);
1660 xPTable
->ReadSxview( rStrm
);
1663 void XclImpPivotTableManager::ReadSxvd( XclImpStream
& rStrm
)
1665 if( !maPTables
.empty() )
1666 maPTables
.back()->ReadSxvd( rStrm
);
1669 void XclImpPivotTableManager::ReadSxvdex( XclImpStream
& rStrm
)
1671 if( !maPTables
.empty() )
1672 maPTables
.back()->ReadSxvdex( rStrm
);
1675 void XclImpPivotTableManager::ReadSxivd( XclImpStream
& rStrm
)
1677 if( !maPTables
.empty() )
1678 maPTables
.back()->ReadSxivd( rStrm
);
1681 void XclImpPivotTableManager::ReadSxpi( XclImpStream
& rStrm
)
1683 if( !maPTables
.empty() )
1684 maPTables
.back()->ReadSxpi( rStrm
);
1687 void XclImpPivotTableManager::ReadSxdi( XclImpStream
& rStrm
)
1689 if( !maPTables
.empty() )
1690 maPTables
.back()->ReadSxdi( rStrm
);
1693 void XclImpPivotTableManager::ReadSxvi( XclImpStream
& rStrm
)
1695 if( !maPTables
.empty() )
1696 maPTables
.back()->ReadSxvi( rStrm
);
1699 void XclImpPivotTableManager::ReadSxex( XclImpStream
& rStrm
)
1701 if( !maPTables
.empty() )
1702 maPTables
.back()->ReadSxex( rStrm
);
1705 void XclImpPivotTableManager::ReadSxViewEx9( XclImpStream
& rStrm
)
1707 if( !maPTables
.empty() )
1708 maPTables
.back()->ReadSxViewEx9( rStrm
);
1711 void XclImpPivotTableManager::ReadSxAddl( XclImpStream
& rStrm
)
1713 if( !maPTables
.empty() )
1714 maPTables
.back()->ReadSxAddl( rStrm
);
1717 void XclImpPivotTableManager::ReadPivotCaches( const XclImpStream
& rStrm
)
1719 for( auto& rxPCache
: maPCaches
)
1720 rxPCache
->ReadPivotCacheStream( rStrm
);
1723 void XclImpPivotTableManager::ConvertPivotTables()
1725 for( auto& rxPTable
: maPTables
)
1726 rxPTable
->Convert();
1729 void XclImpPivotTableManager::MaybeRefreshPivotTables()
1731 for( auto& rxPTable
: maPTables
)
1732 rxPTable
->MaybeRefresh();
1735 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */