Gtk-WARNING gtktreestore.c:1047: Invalid column number 1 added to iter
[LibreOffice.git] / sc / source / core / data / dpsave.cxx
blob6e1d5c56f085c04014136deaa14743d638ba1cc6
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <memory>
21 #include <dpsave.hxx>
22 #include <dpdimsave.hxx>
23 #include <miscuno.hxx>
24 #include <unonames.hxx>
25 #include <dputil.hxx>
26 #include <generalfunction.hxx>
27 #include <dptabdat.hxx>
28 #include <pivot/PivotTableFormats.hxx>
30 #include <sal/types.h>
31 #include <sal/log.hxx>
32 #include <osl/diagnose.h>
33 #include <comphelper/stl_types.hxx>
34 #include <unotools/charclass.hxx>
36 #include <com/sun/star/sheet/XDimensionsSupplier.hpp>
37 #include <com/sun/star/sheet/DataPilotFieldAutoShowInfo.hpp>
38 #include <com/sun/star/sheet/DataPilotFieldLayoutInfo.hpp>
39 #include <com/sun/star/sheet/DataPilotFieldReference.hpp>
40 #include <com/sun/star/sheet/DataPilotFieldSortInfo.hpp>
41 #include <com/sun/star/sheet/DataPilotFieldSortMode.hpp>
42 #include <com/sun/star/sheet/XHierarchiesSupplier.hpp>
43 #include <com/sun/star/sheet/XLevelsSupplier.hpp>
44 #include <com/sun/star/sheet/XMembersSupplier.hpp>
45 #include <com/sun/star/container/XNamed.hpp>
46 #include <com/sun/star/util/XCloneable.hpp>
47 #include <comphelper/diagnose_ex.hxx>
49 #include <unordered_map>
50 #include <algorithm>
51 #include <utility>
53 using namespace com::sun::star;
54 using namespace com::sun::star::sheet;
55 using ::std::unique_ptr;
57 #define SC_DPSAVEMODE_DONTKNOW 2
59 static void lcl_SetBoolProperty( const uno::Reference<beans::XPropertySet>& xProp,
60 const OUString& rName, bool bValue )
62 //TODO: move to ScUnoHelpFunctions?
64 xProp->setPropertyValue( rName, uno::Any( bValue ) );
67 ScDPSaveMember::ScDPSaveMember(OUString _aName) :
68 aName(std::move( _aName )),
69 nVisibleMode( SC_DPSAVEMODE_DONTKNOW ),
70 nShowDetailsMode( SC_DPSAVEMODE_DONTKNOW )
74 ScDPSaveMember::ScDPSaveMember(const ScDPSaveMember& r) :
75 aName( r.aName ),
76 mpLayoutName( r.mpLayoutName ),
77 nVisibleMode( r.nVisibleMode ),
78 nShowDetailsMode( r.nShowDetailsMode )
82 ScDPSaveMember::~ScDPSaveMember()
86 bool ScDPSaveMember::operator== ( const ScDPSaveMember& r ) const
88 return aName == r.aName &&
89 nVisibleMode == r.nVisibleMode &&
90 nShowDetailsMode == r.nShowDetailsMode;
93 bool ScDPSaveMember::HasIsVisible() const
95 return nVisibleMode != SC_DPSAVEMODE_DONTKNOW;
98 void ScDPSaveMember::SetIsVisible(bool bSet)
100 nVisibleMode = sal_uInt16(bSet);
103 bool ScDPSaveMember::HasShowDetails() const
105 return nShowDetailsMode != SC_DPSAVEMODE_DONTKNOW;
108 void ScDPSaveMember::SetShowDetails(bool bSet)
110 nShowDetailsMode = sal_uInt16(bSet);
113 void ScDPSaveMember::SetName( const OUString& rNew )
115 // Used only if the source member was renamed (groups).
116 // For UI renaming of members, a layout name must be used.
118 aName = rNew;
121 void ScDPSaveMember::SetLayoutName( const OUString& rName )
123 mpLayoutName = rName;
126 const std::optional<OUString> & ScDPSaveMember::GetLayoutName() const
128 return mpLayoutName;
131 void ScDPSaveMember::RemoveLayoutName()
133 mpLayoutName.reset();
136 void ScDPSaveMember::WriteToSource( const uno::Reference<uno::XInterface>& xMember, sal_Int32 nPosition )
138 uno::Reference<beans::XPropertySet> xMembProp( xMember, uno::UNO_QUERY );
139 OSL_ENSURE( xMembProp.is(), "no properties at member" );
140 if ( !xMembProp.is() )
141 return;
143 // exceptions are caught at ScDPSaveData::WriteToSource
145 if ( nVisibleMode != SC_DPSAVEMODE_DONTKNOW )
146 lcl_SetBoolProperty( xMembProp,
147 SC_UNO_DP_ISVISIBLE, static_cast<bool>(nVisibleMode) );
149 if ( nShowDetailsMode != SC_DPSAVEMODE_DONTKNOW )
150 lcl_SetBoolProperty( xMembProp,
151 SC_UNO_DP_SHOWDETAILS, static_cast<bool>(nShowDetailsMode) );
153 if (mpLayoutName)
154 ScUnoHelpFunctions::SetOptionalPropertyValue(xMembProp, SC_UNO_DP_LAYOUTNAME, *mpLayoutName);
156 if ( nPosition >= 0 )
157 ScUnoHelpFunctions::SetOptionalPropertyValue(xMembProp, SC_UNO_DP_POSITION, nPosition);
160 #if DUMP_PIVOT_TABLE
162 void ScDPSaveMember::Dump(int nIndent) const
164 std::string aIndent(nIndent*4, ' ');
165 cout << aIndent << "* member name: '" << aName << "'" << endl;
167 cout << aIndent << " + layout name: ";
168 if (mpLayoutName)
169 cout << "'" << *mpLayoutName << "'";
170 else
171 cout << "(none)";
172 cout << endl;
174 cout << aIndent << " + visibility: ";
175 if (nVisibleMode == SC_DPSAVEMODE_DONTKNOW)
176 cout << "(unknown)";
177 else
178 cout << (nVisibleMode ? "visible" : "hidden");
179 cout << endl;
182 #endif
184 ScDPSaveDimension::ScDPSaveDimension(OUString _aName, bool bDataLayout) :
185 aName(std::move( _aName )),
186 bIsDataLayout( bDataLayout ),
187 bDupFlag( false ),
188 nOrientation( sheet::DataPilotFieldOrientation_HIDDEN ),
189 nFunction( ScGeneralFunction::AUTO ),
190 nUsedHierarchy( -1 ),
191 nShowEmptyMode( SC_DPSAVEMODE_DONTKNOW ),
192 bRepeatItemLabels( false ),
193 bSubTotalDefault( true )
197 ScDPSaveDimension::ScDPSaveDimension(const ScDPSaveDimension& r) :
198 aName( r.aName ),
199 mpLayoutName( r.mpLayoutName ),
200 mpSubtotalName( r.mpSubtotalName ),
201 bIsDataLayout( r.bIsDataLayout ),
202 bDupFlag( r.bDupFlag ),
203 nOrientation( r.nOrientation ),
204 nFunction( r.nFunction ),
205 nUsedHierarchy( r.nUsedHierarchy ),
206 nShowEmptyMode( r.nShowEmptyMode ),
207 bRepeatItemLabels( r.bRepeatItemLabels ),
208 bSubTotalDefault( r.bSubTotalDefault ),
209 maSubTotalFuncs( r.maSubTotalFuncs )
211 for (const ScDPSaveMember* pMem : r.maMemberList)
213 const OUString& rName = pMem->GetName();
214 std::unique_ptr<ScDPSaveMember> pNew(new ScDPSaveMember( *pMem ));
215 maMemberList.push_back( pNew.get() );
216 maMemberHash[rName] = std::move(pNew);
218 if (r.pReferenceValue)
219 pReferenceValue.reset( new sheet::DataPilotFieldReference( *(r.pReferenceValue) ) );
220 if (r.pSortInfo)
221 pSortInfo.reset( new sheet::DataPilotFieldSortInfo( *(r.pSortInfo) ) );
222 if (r.pAutoShowInfo)
223 pAutoShowInfo.reset( new sheet::DataPilotFieldAutoShowInfo( *(r.pAutoShowInfo) ) );
224 if (r.pLayoutInfo)
225 pLayoutInfo.reset(new sheet::DataPilotFieldLayoutInfo( *(r.pLayoutInfo) ));
228 ScDPSaveDimension::~ScDPSaveDimension()
230 maMemberHash.clear();
231 pReferenceValue.reset();
232 pSortInfo.reset();
233 pAutoShowInfo.reset();
234 pLayoutInfo.reset();
237 bool ScDPSaveDimension::operator== ( const ScDPSaveDimension& r ) const
239 if ( aName != r.aName ||
240 bIsDataLayout != r.bIsDataLayout ||
241 bDupFlag != r.bDupFlag ||
242 nOrientation != r.nOrientation ||
243 nFunction != r.nFunction ||
244 nUsedHierarchy != r.nUsedHierarchy ||
245 nShowEmptyMode != r.nShowEmptyMode ||
246 bRepeatItemLabels!= r.bRepeatItemLabels||
247 bSubTotalDefault != r.bSubTotalDefault ||
248 maSubTotalFuncs != r.maSubTotalFuncs )
249 return false;
251 if (maMemberHash.size() != r.maMemberHash.size() )
252 return false;
254 if (!std::equal(maMemberList.begin(), maMemberList.end(), r.maMemberList.begin(), r.maMemberList.end(),
255 [](const ScDPSaveMember* a, const ScDPSaveMember* b) { return *a == *b; }))
256 return false;
258 if( pReferenceValue && r.pReferenceValue )
260 if ( *pReferenceValue != *r.pReferenceValue )
262 return false;
265 else if ( pReferenceValue || r.pReferenceValue )
267 return false;
269 if( this->pSortInfo && r.pSortInfo )
271 if ( *this->pSortInfo != *r.pSortInfo )
273 return false;
276 else if ( this->pSortInfo || r.pSortInfo )
278 return false;
280 if( this->pAutoShowInfo && r.pAutoShowInfo )
282 if ( *this->pAutoShowInfo != *r.pAutoShowInfo )
284 return false;
287 else if ( this->pAutoShowInfo || r.pAutoShowInfo )
289 return false;
292 return true;
295 void ScDPSaveDimension::AddMember(std::unique_ptr<ScDPSaveMember> pMember)
297 const OUString & rName = pMember->GetName();
298 auto aExisting = maMemberHash.find( rName );
299 auto tmp = pMember.get();
300 if ( aExisting == maMemberHash.end() )
302 maMemberHash[rName] = std::move(pMember);
304 else
306 std::erase(maMemberList, aExisting->second.get());
307 aExisting->second = std::move(pMember);
309 maMemberList.push_back( tmp );
312 void ScDPSaveDimension::SetName( const OUString& rNew )
314 // Used only if the source dim was renamed (groups).
315 // For UI renaming of dimensions, the layout name must be used.
317 aName = rNew;
320 void ScDPSaveDimension::SetOrientation(css::sheet::DataPilotFieldOrientation nNew)
322 nOrientation = nNew;
325 void ScDPSaveDimension::SetSubTotals(std::vector<ScGeneralFunction> && rFuncs)
327 maSubTotalFuncs = std::move(rFuncs);
328 bSubTotalDefault = false;
331 bool ScDPSaveDimension::HasShowEmpty() const
333 return nShowEmptyMode != SC_DPSAVEMODE_DONTKNOW;
336 void ScDPSaveDimension::SetShowEmpty(bool bSet)
338 nShowEmptyMode = sal_uInt16(bSet);
341 void ScDPSaveDimension::SetRepeatItemLabels(bool bSet)
343 bRepeatItemLabels = bSet;
346 void ScDPSaveDimension::SetFunction(ScGeneralFunction nNew)
348 nFunction = nNew;
351 void ScDPSaveDimension::SetUsedHierarchy(tools::Long nNew)
353 nUsedHierarchy = nNew;
356 void ScDPSaveDimension::SetSubtotalName(const OUString& rName)
358 mpSubtotalName = rName;
361 const std::optional<OUString> & ScDPSaveDimension::GetSubtotalName() const
363 return mpSubtotalName;
366 void ScDPSaveDimension::RemoveSubtotalName()
368 mpSubtotalName.reset();
371 bool ScDPSaveDimension::IsMemberNameInUse(const OUString& rName) const
373 return std::any_of(maMemberList.begin(), maMemberList.end(), [&rName](const ScDPSaveMember* pMem) {
374 if (rName.equalsIgnoreAsciiCase(pMem->GetName()))
375 return true;
377 const std::optional<OUString> & pLayoutName = pMem->GetLayoutName();
378 return pLayoutName && rName.equalsIgnoreAsciiCase(*pLayoutName);
382 void ScDPSaveDimension::SetLayoutName(const OUString& rName)
384 mpLayoutName = rName;
387 const std::optional<OUString> & ScDPSaveDimension::GetLayoutName() const
389 return mpLayoutName;
392 void ScDPSaveDimension::RemoveLayoutName()
394 mpLayoutName.reset();
397 void ScDPSaveDimension::SetReferenceValue(const sheet::DataPilotFieldReference* pNew)
399 if (pNew)
400 pReferenceValue.reset( new sheet::DataPilotFieldReference(*pNew) );
401 else
402 pReferenceValue.reset();
405 void ScDPSaveDimension::SetSortInfo(const sheet::DataPilotFieldSortInfo* pNew)
407 if (pNew)
408 pSortInfo.reset( new sheet::DataPilotFieldSortInfo(*pNew) );
409 else
410 pSortInfo.reset();
413 void ScDPSaveDimension::SetAutoShowInfo(const sheet::DataPilotFieldAutoShowInfo* pNew)
415 if (pNew)
416 pAutoShowInfo.reset( new sheet::DataPilotFieldAutoShowInfo(*pNew) );
417 else
418 pAutoShowInfo.reset();
421 void ScDPSaveDimension::SetLayoutInfo(const sheet::DataPilotFieldLayoutInfo* pNew)
423 if (pNew)
424 pLayoutInfo.reset( new sheet::DataPilotFieldLayoutInfo(*pNew) );
425 else
426 pLayoutInfo.reset();
429 void ScDPSaveDimension::SetCurrentPage( const OUString* pPage )
431 // We use member's visibility attribute to filter by page dimension.
433 // pPage == nullptr -> all members visible.
434 for (ScDPSaveMember* pMem : maMemberList)
436 bool bVisible = !pPage || pMem->GetName() == *pPage;
437 pMem->SetIsVisible(bVisible);
441 OUString ScDPSaveDimension::GetCurrentPage() const
443 MemberList::const_iterator it = std::find_if(maMemberList.begin(), maMemberList.end(),
444 [](const ScDPSaveMember* pMem) { return pMem->GetIsVisible(); });
445 if (it != maMemberList.end())
446 return (*it)->GetName();
448 return OUString();
451 ScDPSaveMember* ScDPSaveDimension::GetExistingMemberByName(const OUString& rName)
453 auto res = maMemberHash.find (rName);
454 if (res != maMemberHash.end())
455 return res->second.get();
456 return nullptr;
459 ScDPSaveMember* ScDPSaveDimension::GetMemberByName(const OUString& rName)
461 ScDPSaveMember* pResult = GetExistingMemberByName(rName);
462 if (pResult)
463 return pResult;
465 pResult = new ScDPSaveMember(rName);
466 maMemberHash[rName] = std::unique_ptr<ScDPSaveMember>(pResult);
467 maMemberList.push_back(pResult);
468 return pResult;
471 void ScDPSaveDimension::SetMemberPosition( const OUString& rName, sal_Int32 nNewPos )
473 ScDPSaveMember* pMember = GetMemberByName( rName ); // make sure it exists and is in the hash
475 std::erase(maMemberList, pMember);
477 maMemberList.insert( maMemberList.begin() + nNewPos, pMember );
480 void ScDPSaveDimension::WriteToSource( const uno::Reference<uno::XInterface>& xDim )
482 uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY );
483 OSL_ENSURE( xDimProp.is(), "no properties at dimension" );
484 if ( xDimProp.is() )
486 // exceptions are caught at ScDPSaveData::WriteToSource
488 sheet::DataPilotFieldOrientation eOrient = nOrientation;
489 xDimProp->setPropertyValue( SC_UNO_DP_ORIENTATION, uno::Any(eOrient) );
491 sal_Int16 eFunc = static_cast<sal_Int16>(nFunction);
492 xDimProp->setPropertyValue( SC_UNO_DP_FUNCTION2, uno::Any(eFunc) );
494 if ( nUsedHierarchy >= 0 )
496 xDimProp->setPropertyValue( SC_UNO_DP_USEDHIERARCHY, uno::Any(static_cast<sal_Int32>(nUsedHierarchy)) );
499 if ( pReferenceValue )
502 xDimProp->setPropertyValue( SC_UNO_DP_REFVALUE, uno::Any(*pReferenceValue) );
505 if (mpLayoutName)
506 ScUnoHelpFunctions::SetOptionalPropertyValue(xDimProp, SC_UNO_DP_LAYOUTNAME, *mpLayoutName);
508 const std::optional<OUString> & pSubTotalName = GetSubtotalName();
509 if (pSubTotalName)
510 // Custom subtotal name, with '?' being replaced by the visible field name later.
511 ScUnoHelpFunctions::SetOptionalPropertyValue(xDimProp, SC_UNO_DP_FIELD_SUBTOTALNAME, *pSubTotalName);
514 // Level loop outside of maMemberList loop
515 // because SubTotals have to be set independently of known members
517 tools::Long nCount = maMemberHash.size();
519 tools::Long nHierCount = 0;
520 uno::Reference<container::XIndexAccess> xHiers;
521 uno::Reference<sheet::XHierarchiesSupplier> xHierSupp( xDim, uno::UNO_QUERY );
522 if ( xHierSupp.is() )
524 xHiers = new ScNameToIndexAccess(xHierSupp->getHierarchies());
525 nHierCount = xHiers->getCount();
528 bool bHasHiddenMember = false;
530 for (tools::Long nHier=0; nHier<nHierCount; nHier++)
532 tools::Long nLevCount = 0;
533 uno::Reference<container::XIndexAccess> xLevels;
534 uno::Reference<sheet::XLevelsSupplier> xLevSupp(xHiers->getByIndex(nHier), uno::UNO_QUERY);
535 if ( xLevSupp.is() )
537 xLevels = new ScNameToIndexAccess(xLevSupp->getLevels());
538 nLevCount = xLevels->getCount();
541 for (tools::Long nLev=0; nLev<nLevCount; nLev++)
543 uno::Reference<uno::XInterface> xLevel(xLevels->getByIndex(nLev), uno::UNO_QUERY);
544 uno::Reference<beans::XPropertySet> xLevProp( xLevel, uno::UNO_QUERY );
545 OSL_ENSURE( xLevProp.is(), "no properties at level" );
546 if ( xLevProp.is() )
548 if ( !bSubTotalDefault )
550 uno::Sequence<sal_Int16> aSeq(maSubTotalFuncs.size());
551 for(size_t i = 0; i < maSubTotalFuncs.size(); ++i)
552 aSeq.getArray()[i] = static_cast<sal_Int16>(maSubTotalFuncs[i]);
553 xLevProp->setPropertyValue( SC_UNO_DP_SUBTOTAL2, uno::Any(aSeq) );
555 if ( nShowEmptyMode != SC_DPSAVEMODE_DONTKNOW )
556 lcl_SetBoolProperty( xLevProp,
557 SC_UNO_DP_SHOWEMPTY, static_cast<bool>(nShowEmptyMode) );
559 lcl_SetBoolProperty( xLevProp,
560 SC_UNO_DP_REPEATITEMLABELS, bRepeatItemLabels );
562 if ( pSortInfo )
563 ScUnoHelpFunctions::SetOptionalPropertyValue(xLevProp, SC_UNO_DP_SORTING, *pSortInfo);
565 if ( pAutoShowInfo )
566 ScUnoHelpFunctions::SetOptionalPropertyValue(xLevProp, SC_UNO_DP_AUTOSHOW, *pAutoShowInfo);
568 if ( pLayoutInfo )
569 ScUnoHelpFunctions::SetOptionalPropertyValue(xLevProp, SC_UNO_DP_LAYOUT, *pLayoutInfo);
571 // exceptions are caught at ScDPSaveData::WriteToSource
574 if ( nCount > 0 )
576 uno::Reference<sheet::XMembersSupplier> xMembSupp( xLevel, uno::UNO_QUERY );
577 if ( xMembSupp.is() )
579 uno::Reference<sheet::XMembersAccess> xMembers = xMembSupp->getMembers();
580 if ( xMembers.is() )
582 sal_Int32 nPosition = -1; // set position only in manual mode
583 if ( !pSortInfo || pSortInfo->Mode == sheet::DataPilotFieldSortMode::MANUAL )
584 nPosition = 0;
586 for (ScDPSaveMember* pMember : maMemberList)
588 if (!pMember->GetIsVisible())
589 bHasHiddenMember = true;
590 OUString aMemberName = pMember->GetName();
591 if ( xMembers->hasByName( aMemberName ) )
593 uno::Reference<uno::XInterface> xMemberInt(
594 xMembers->getByName(aMemberName), uno::UNO_QUERY);
595 pMember->WriteToSource( xMemberInt, nPosition );
597 if ( nPosition >= 0 )
598 ++nPosition; // increase if initialized
600 // missing member is no error
608 if (xDimProp.is())
609 ScUnoHelpFunctions::SetOptionalPropertyValue(xDimProp, SC_UNO_DP_HAS_HIDDEN_MEMBER, bHasHiddenMember);
612 void ScDPSaveDimension::UpdateMemberVisibility(const std::unordered_map<OUString, bool>& rData)
614 for (ScDPSaveMember* pMem : maMemberList)
616 const OUString& rMemName = pMem->GetName();
617 auto itr = rData.find(rMemName);
618 if (itr != rData.end())
619 pMem->SetIsVisible(itr->second);
623 bool ScDPSaveDimension::HasInvisibleMember() const
625 return std::any_of(maMemberList.begin(), maMemberList.end(),
626 [](const ScDPSaveMember* pMem) { return !pMem->GetIsVisible(); });
629 void ScDPSaveDimension::RemoveObsoleteMembers(const MemberSetType& rMembers)
631 MemberList aNew;
632 for (ScDPSaveMember* pMem : maMemberList)
634 if (rMembers.count(pMem->GetName()))
636 // This member still exists.
637 aNew.push_back(pMem);
639 else
641 maMemberHash.erase(pMem->GetName());
645 maMemberList.swap(aNew);
648 #if DUMP_PIVOT_TABLE
650 void ScDPSaveDimension::Dump(int nIndent) const
652 static const char* pOrientNames[] = { "hidden", "column", "row", "page", "data" };
653 std::string aIndent(nIndent*4, ' ');
655 cout << aIndent << "* dimension name: '" << aName << "'" << endl;
657 cout << aIndent << " + orientation: ";
658 if (nOrientation <= DataPilotFieldOrientation_DATA)
659 cout << pOrientNames[static_cast<int>(nOrientation)];
660 else
661 cout << "(invalid)";
662 cout << endl;
664 cout << aIndent << " + layout name: ";
665 if (mpLayoutName)
666 cout << "'" << *mpLayoutName << "'";
667 else
668 cout << "(none)";
669 cout << endl;
671 cout << aIndent << " + subtotal name: ";
672 if (mpSubtotalName)
673 cout << "'" << *mpSubtotalName << "'";
674 else
675 cout << "(none)";
676 cout << endl;
678 cout << aIndent << " + is data layout: " << (bIsDataLayout ? "yes" : "no") << endl;
679 cout << aIndent << " + is duplicate: " << (bDupFlag ? "yes" : "no") << endl;
681 for (ScDPSaveMember* pMem : maMemberList)
683 pMem->Dump(nIndent+1);
686 cout << endl; // blank line
689 #endif
691 ScDPSaveData::ScDPSaveData()
692 : mnColumnGrandMode(SC_DPSAVEMODE_DONTKNOW)
693 , mnRowGrandMode(SC_DPSAVEMODE_DONTKNOW)
694 , mnIgnoreEmptyMode(SC_DPSAVEMODE_DONTKNOW)
695 , mnRepeatEmptyMode(SC_DPSAVEMODE_DONTKNOW)
696 , mbFilterButton(true)
697 , mbDrillDown(true)
698 , mbExpandCollapse(false)
699 , mbDimensionMembersBuilt(false)
703 ScDPSaveData::ScDPSaveData(const ScDPSaveData& rOther)
704 : mnColumnGrandMode(rOther.mnColumnGrandMode)
705 , mnRowGrandMode(rOther.mnRowGrandMode)
706 , mnIgnoreEmptyMode(rOther.mnIgnoreEmptyMode)
707 , mnRepeatEmptyMode(rOther.mnRepeatEmptyMode)
708 , mbFilterButton(rOther.mbFilterButton)
709 , mbDrillDown(rOther.mbDrillDown)
710 , mbExpandCollapse(rOther.mbExpandCollapse)
711 , mbDimensionMembersBuilt(rOther.mbDimensionMembersBuilt)
712 , mpGrandTotalName(rOther.mpGrandTotalName)
714 if (rOther.mpDimensionData)
715 mpDimensionData.reset(new ScDPDimensionSaveData(*rOther.mpDimensionData));
716 if (rOther.mpFormats)
717 mpFormats.reset(new sc::PivotTableFormats(*rOther.mpFormats));
719 for (auto const& rOtherSaveDimension : rOther.m_DimList)
721 m_DimList.push_back(std::make_unique<ScDPSaveDimension>(*rOtherSaveDimension));
725 ScDPSaveData& ScDPSaveData::operator=(const ScDPSaveData& rOther)
727 if (&rOther != this)
729 this->~ScDPSaveData();
730 new(this)ScDPSaveData(rOther);
732 return *this;
735 bool ScDPSaveData::operator== (const ScDPSaveData& rOther) const
737 if (mnColumnGrandMode != rOther.mnColumnGrandMode ||
738 mnRowGrandMode != rOther.mnRowGrandMode ||
739 mnIgnoreEmptyMode != rOther.mnIgnoreEmptyMode ||
740 mnRepeatEmptyMode != rOther.mnRepeatEmptyMode ||
741 mbFilterButton != rOther.mbFilterButton ||
742 mbDrillDown != rOther.mbDrillDown ||
743 mbDimensionMembersBuilt != rOther.mbDimensionMembersBuilt)
744 return false;
746 if (mpDimensionData || rOther.mpDimensionData)
747 if (!mpDimensionData || !rOther.mpDimensionData || !(*mpDimensionData == *rOther.mpDimensionData))
748 return false;
750 if (!(::comphelper::ContainerUniquePtrEquals(m_DimList, rOther.m_DimList)))
751 return false;
753 if (mpGrandTotalName)
755 if (!rOther.mpGrandTotalName)
756 return false;
757 if (*mpGrandTotalName != *rOther.mpGrandTotalName)
758 return false;
760 else if (rOther.mpGrandTotalName)
761 return false;
763 return true;
766 ScDPSaveData::~ScDPSaveData()
770 void ScDPSaveData::setFormats(sc::PivotTableFormats const& rPivotTableFormats)
772 mpFormats.reset(new sc::PivotTableFormats(rPivotTableFormats));
775 bool ScDPSaveData::hasFormats()
777 return bool(mpFormats);
780 sc::PivotTableFormats const& ScDPSaveData::getFormats()
782 return *mpFormats;
785 void ScDPSaveData::SetGrandTotalName(const OUString& rName)
787 mpGrandTotalName = rName;
790 const std::optional<OUString> & ScDPSaveData::GetGrandTotalName() const
792 return mpGrandTotalName;
795 namespace {
797 class DimOrderInserter
799 ScDPSaveData::DimOrderType& mrNames;
800 public:
801 explicit DimOrderInserter(ScDPSaveData::DimOrderType& rNames) : mrNames(rNames) {}
803 void operator() (const ScDPSaveDimension* pDim)
805 size_t nRank = mrNames.size();
806 mrNames.emplace(ScGlobal::getCharClass().uppercase(pDim->GetName()), nRank);
812 const ScDPSaveData::DimOrderType& ScDPSaveData::GetDimensionSortOrder() const
814 if (!mpDimOrder)
816 mpDimOrder.reset(new DimOrderType);
817 std::vector<const ScDPSaveDimension*> aRowDims, aColDims;
818 GetAllDimensionsByOrientation(sheet::DataPilotFieldOrientation_ROW, aRowDims);
819 GetAllDimensionsByOrientation(sheet::DataPilotFieldOrientation_COLUMN, aColDims);
821 std::for_each(aRowDims.begin(), aRowDims.end(), DimOrderInserter(*mpDimOrder));
822 std::for_each(aColDims.begin(), aColDims.end(), DimOrderInserter(*mpDimOrder));
824 return *mpDimOrder;
827 void ScDPSaveData::GetAllDimensionsByOrientation(
828 sheet::DataPilotFieldOrientation eOrientation, std::vector<const ScDPSaveDimension*>& rDims) const
830 std::vector<const ScDPSaveDimension*> aDims;
831 for (auto const& it : m_DimList)
833 const ScDPSaveDimension& rDim = *it;
834 if (rDim.GetOrientation() != eOrientation)
835 continue;
837 aDims.push_back(&rDim);
840 rDims.swap(aDims);
843 void ScDPSaveData::AddDimension(ScDPSaveDimension* pDim)
845 if (!pDim)
846 return;
848 CheckDuplicateName(*pDim);
849 m_DimList.push_back(std::unique_ptr<ScDPSaveDimension>(pDim));
851 DimensionsChanged();
854 ScDPSaveDimension* ScDPSaveData::GetDimensionByName(const OUString& rName)
856 for (auto const& iter : m_DimList)
858 if (iter->GetName() == rName && !iter->IsDataLayout() )
859 return &(*iter);
862 return AppendNewDimension(rName, false);
865 ScDPSaveDimension* ScDPSaveData::GetExistingDimensionByName(std::u16string_view rName) const
867 for (auto const& iter : m_DimList)
869 if (iter->GetName() == rName && !iter->IsDataLayout() )
870 return &(*iter);
872 return nullptr; // don't create new
875 ScDPSaveDimension* ScDPSaveData::GetNewDimensionByName(const OUString& rName)
877 for (auto const& iter : m_DimList)
879 if (iter->GetName() == rName && !iter->IsDataLayout() )
880 return DuplicateDimension(rName);
883 return AppendNewDimension(rName, false);
886 ScDPSaveDimension* ScDPSaveData::GetDataLayoutDimension()
888 ScDPSaveDimension* pDim = GetExistingDataLayoutDimension();
889 if (pDim)
890 return pDim;
892 return AppendNewDimension(OUString(), true);
895 ScDPSaveDimension* ScDPSaveData::GetExistingDataLayoutDimension() const
897 for (auto const& iter : m_DimList)
899 if ( iter->IsDataLayout() )
900 return &(*iter);
902 return nullptr;
905 ScDPSaveDimension* ScDPSaveData::DuplicateDimension(std::u16string_view rName)
907 // always insert new
909 ScDPSaveDimension* pOld = GetExistingDimensionByName(rName);
910 if (!pOld)
911 return nullptr;
913 ScDPSaveDimension* pNew = new ScDPSaveDimension( *pOld );
914 AddDimension(pNew);
915 return pNew;
918 void ScDPSaveData::RemoveDimensionByName(const OUString& rName)
920 auto iter = std::find_if(m_DimList.begin(), m_DimList.end(),
921 [&rName](const std::unique_ptr<ScDPSaveDimension>& rxDim) {
922 return rxDim->GetName() == rName && !rxDim->IsDataLayout(); });
923 if (iter != m_DimList.end())
925 m_DimList.erase(iter);
926 RemoveDuplicateNameCount(rName);
927 DimensionsChanged();
931 ScDPSaveDimension& ScDPSaveData::DuplicateDimension( const ScDPSaveDimension& rDim )
933 ScDPSaveDimension* pNew = new ScDPSaveDimension( rDim );
934 AddDimension(pNew);
935 return *pNew;
938 ScDPSaveDimension* ScDPSaveData::GetInnermostDimension(DataPilotFieldOrientation nOrientation)
940 // return the innermost dimension for the given orientation,
941 // excluding data layout dimension
943 auto iter = std::find_if(m_DimList.rbegin(), m_DimList.rend(),
944 [&nOrientation](const std::unique_ptr<ScDPSaveDimension>& rxDim) {
945 return rxDim->GetOrientation() == nOrientation && !rxDim->IsDataLayout(); });
946 if (iter != m_DimList.rend())
947 return iter->get();
949 return nullptr;
952 ScDPSaveDimension* ScDPSaveData::GetFirstDimension(sheet::DataPilotFieldOrientation eOrientation)
954 for (auto const& iter : m_DimList)
956 if (iter->GetOrientation() == eOrientation && !iter->IsDataLayout())
957 return &(*iter);
959 return nullptr;
962 tools::Long ScDPSaveData::GetDataDimensionCount() const
964 tools::Long nDataCount = 0;
966 for (auto const& iter : m_DimList)
968 if (iter->GetOrientation() == sheet::DataPilotFieldOrientation_DATA)
969 ++nDataCount;
972 return nDataCount;
975 void ScDPSaveData::SetPosition( ScDPSaveDimension* pDim, tools::Long nNew )
977 // position (nNew) is counted within dimensions of the same orientation
979 DataPilotFieldOrientation nOrient = pDim->GetOrientation();
981 auto it = std::find_if(m_DimList.begin(), m_DimList.end(),
982 [&pDim](const std::unique_ptr<ScDPSaveDimension>& rxDim) { return pDim == rxDim.get(); });
983 if (it != m_DimList.end())
985 // Tell vector<unique_ptr> to give up ownership of this element. Don't
986 // delete this instance as it is re-inserted into the container later.
987 // coverity[leaked_storage] - re-inserted into the container later
988 it->release();
989 m_DimList.erase(it);
992 auto iterInsert = std::find_if(m_DimList.begin(), m_DimList.end(),
993 [&nOrient, &nNew](const std::unique_ptr<ScDPSaveDimension>& rxDim) {
994 if (rxDim->GetOrientation() == nOrient )
995 --nNew;
996 return nNew <= 0;
999 m_DimList.insert(iterInsert, std::unique_ptr<ScDPSaveDimension>(pDim));
1000 DimensionsChanged();
1003 void ScDPSaveData::SetColumnGrand(bool bSet)
1005 mnColumnGrandMode = sal_uInt16(bSet);
1008 void ScDPSaveData::SetRowGrand(bool bSet)
1010 mnRowGrandMode = sal_uInt16(bSet);
1013 void ScDPSaveData::SetIgnoreEmptyRows(bool bSet)
1015 mnIgnoreEmptyMode = sal_uInt16(bSet);
1018 void ScDPSaveData::SetRepeatIfEmpty(bool bSet)
1020 mnRepeatEmptyMode = sal_uInt16(bSet);
1023 void ScDPSaveData::SetFilterButton(bool bSet)
1025 mbFilterButton = bSet;
1028 void ScDPSaveData::SetDrillDown(bool bSet)
1030 mbDrillDown = bSet;
1033 void ScDPSaveData::SetExpandCollapse(bool bSet)
1035 mbExpandCollapse = bSet;
1038 static void lcl_ResetOrient( const uno::Reference<sheet::XDimensionsSupplier>& xSource )
1040 uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions();
1041 uno::Reference<container::XIndexAccess> xIntDims = new ScNameToIndexAccess( xDimsName );
1042 tools::Long nIntCount = xIntDims->getCount();
1043 for (tools::Long nIntDim=0; nIntDim<nIntCount; nIntDim++)
1045 uno::Reference<beans::XPropertySet> xDimProp(xIntDims->getByIndex(nIntDim), uno::UNO_QUERY);
1046 if (xDimProp.is())
1048 xDimProp->setPropertyValue( SC_UNO_DP_ORIENTATION, uno::Any(sheet::DataPilotFieldOrientation_HIDDEN) );
1053 void ScDPSaveData::WriteToSource( const uno::Reference<sheet::XDimensionsSupplier>& xSource )
1055 if (!xSource.is())
1056 return;
1058 // source options must be first!
1060 uno::Reference<beans::XPropertySet> xSourceProp( xSource, uno::UNO_QUERY );
1061 SAL_WARN_IF( !xSourceProp.is(), "sc.core", "no properties at source" );
1062 if ( xSourceProp.is() )
1064 // source options are not available for external sources
1065 //TODO: use XPropertySetInfo to test for availability?
1069 if (mnIgnoreEmptyMode != SC_DPSAVEMODE_DONTKNOW)
1070 lcl_SetBoolProperty(xSourceProp, SC_UNO_DP_IGNOREEMPTY, bool(mnIgnoreEmptyMode));
1071 if (mnRepeatEmptyMode != SC_DPSAVEMODE_DONTKNOW)
1072 lcl_SetBoolProperty(xSourceProp, SC_UNO_DP_REPEATEMPTY, bool(mnRepeatEmptyMode));
1074 catch(uno::Exception&)
1076 // no error
1079 const std::optional<OUString> & pGrandTotalName = GetGrandTotalName();
1080 if (pGrandTotalName)
1081 ScUnoHelpFunctions::SetOptionalPropertyValue(xSourceProp, SC_UNO_DP_GRANDTOTAL_NAME, *pGrandTotalName);
1084 // exceptions in the other calls are errors
1087 // reset all orientations
1088 //TODO: "forgetSettings" or similar at source ?????
1089 //TODO: reset all duplicated dimensions, or reuse them below !!!
1090 SAL_INFO("sc.core", "ScDPSaveData::WriteToSource");
1092 lcl_ResetOrient( xSource );
1094 uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions();
1095 uno::Reference<container::XIndexAccess> xIntDims = new ScNameToIndexAccess( xDimsName );
1096 tools::Long nIntCount = xIntDims->getCount();
1098 for (const auto& rxDim : m_DimList)
1100 OUString aName = rxDim->GetName();
1101 OUString aCoreName = ScDPUtil::getSourceDimensionName(aName);
1103 SAL_INFO("sc.core", aName);
1105 bool bData = rxDim->IsDataLayout();
1107 //TODO: getByName for ScDPSource, including DataLayoutDimension !!!!!!!!
1109 bool bFound = false;
1110 for (tools::Long nIntDim=0; nIntDim<nIntCount && !bFound; nIntDim++)
1112 uno::Reference<uno::XInterface> xIntDim(xIntDims->getByIndex(nIntDim),
1113 uno::UNO_QUERY);
1114 if ( bData )
1116 uno::Reference<beans::XPropertySet> xDimProp( xIntDim, uno::UNO_QUERY );
1117 if ( xDimProp.is() )
1119 bFound = ScUnoHelpFunctions::GetBoolProperty( xDimProp,
1120 SC_UNO_DP_ISDATALAYOUT );
1121 //TODO: error checking -- is "IsDataLayoutDimension" property required??
1124 else
1126 uno::Reference<container::XNamed> xDimName( xIntDim, uno::UNO_QUERY );
1127 if (xDimName.is() && xDimName->getName() == aCoreName)
1128 bFound = true;
1131 if (bFound)
1133 if (rxDim->GetDupFlag())
1135 uno::Reference<util::XCloneable> xCloneable(xIntDim, uno::UNO_QUERY);
1136 SAL_WARN_IF(!xCloneable.is(), "sc.core", "cannot clone dimension");
1137 if (xCloneable.is())
1139 uno::Reference<util::XCloneable> xNew = xCloneable->createClone();
1140 uno::Reference<container::XNamed> xNewName(xNew, uno::UNO_QUERY);
1141 if (xNewName.is())
1143 xNewName->setName(aName);
1144 rxDim->WriteToSource(xNew);
1148 else
1149 rxDim->WriteToSource( xIntDim );
1152 SAL_WARN_IF(!bFound, "sc.core", "WriteToSource: Dimension not found: " + aName + ".");
1155 if ( xSourceProp.is() )
1157 if (mnColumnGrandMode != SC_DPSAVEMODE_DONTKNOW)
1158 lcl_SetBoolProperty(xSourceProp, SC_UNO_DP_COLGRAND, bool(mnColumnGrandMode));
1159 if (mnRowGrandMode != SC_DPSAVEMODE_DONTKNOW)
1160 lcl_SetBoolProperty(xSourceProp, SC_UNO_DP_ROWGRAND, bool(mnRowGrandMode));
1163 catch(uno::Exception const &)
1165 TOOLS_WARN_EXCEPTION("sc.core", "WriteToSource");
1169 bool ScDPSaveData::IsEmpty() const
1171 for (auto const& iter : m_DimList)
1173 if (iter->GetOrientation() != sheet::DataPilotFieldOrientation_HIDDEN && !iter->IsDataLayout())
1174 return false;
1176 return true; // no entries that are not hidden
1179 void ScDPSaveData::RemoveAllGroupDimensions( const OUString& rSrcDimName, std::vector<OUString>* pDeletedNames )
1181 if (!mpDimensionData)
1182 // No group dimensions exist. Nothing to do.
1183 return;
1185 // Remove numeric group dimension (exists once at most). No need to delete
1186 // anything in save data (grouping was done inplace in an existing base
1187 // dimension).
1188 mpDimensionData->RemoveNumGroupDimension(rSrcDimName);
1190 // Remove named group dimension(s). Dimensions have to be removed from
1191 // dimension save data and from save data too.
1192 const ScDPSaveGroupDimension* pExistingGroup = mpDimensionData->GetGroupDimForBase(rSrcDimName);
1193 while ( pExistingGroup )
1195 OUString aGroupDimName = pExistingGroup->GetGroupDimName();
1196 mpDimensionData->RemoveGroupDimension(aGroupDimName); // pExistingGroup is deleted
1198 // also remove SaveData settings for the dimension that no longer exists
1199 RemoveDimensionByName(aGroupDimName);
1201 if (pDeletedNames)
1202 pDeletedNames->push_back(aGroupDimName);
1204 // see if there are more group dimensions
1205 pExistingGroup = mpDimensionData->GetGroupDimForBase(rSrcDimName);
1207 if ( pExistingGroup && pExistingGroup->GetGroupDimName() == aGroupDimName )
1209 // still get the same group dimension?
1210 OSL_FAIL("couldn't remove group dimension");
1211 pExistingGroup = nullptr; // avoid endless loop
1216 ScDPDimensionSaveData* ScDPSaveData::GetDimensionData()
1218 if (!mpDimensionData)
1219 mpDimensionData.reset(new ScDPDimensionSaveData);
1220 return mpDimensionData.get();
1223 void ScDPSaveData::SetDimensionData(const ScDPDimensionSaveData* pNew)
1225 if (pNew)
1226 mpDimensionData.reset(new ScDPDimensionSaveData(*pNew));
1227 else
1228 mpDimensionData.reset();
1231 void ScDPSaveData::BuildAllDimensionMembers(ScDPTableData* pData)
1233 if (mbDimensionMembersBuilt)
1234 return;
1236 // First, build a dimension name-to-index map.
1237 typedef std::unordered_map<OUString, tools::Long> NameIndexMap;
1238 NameIndexMap aMap;
1239 tools::Long nColCount = pData->GetColumnCount();
1240 for (tools::Long i = 0; i < nColCount; ++i)
1241 aMap.emplace(pData->getDimensionName(i), i);
1243 NameIndexMap::const_iterator itrEnd = aMap.end();
1245 for (auto const& iter : m_DimList)
1247 const OUString& rDimName = iter->GetName();
1248 if (rDimName.isEmpty())
1249 // empty dimension name. It must be data layout.
1250 continue;
1252 NameIndexMap::const_iterator itr = aMap.find(rDimName);
1253 if (itr == itrEnd)
1254 // dimension name not in the data. This should never happen!
1255 continue;
1257 tools::Long nDimIndex = itr->second;
1258 const std::vector<SCROW>& rMembers = pData->GetColumnEntries(nDimIndex);
1259 size_t nMemberCount = rMembers.size();
1260 for (size_t j = 0; j < nMemberCount; ++j)
1262 const ScDPItemData* pMemberData = pData->GetMemberById( nDimIndex, rMembers[j] );
1263 OUString aMemName = pData->GetFormattedString(nDimIndex, *pMemberData, false);
1264 if (iter->GetExistingMemberByName(aMemName))
1265 // this member instance already exists. nothing to do.
1266 continue;
1268 unique_ptr<ScDPSaveMember> pNewMember(new ScDPSaveMember(aMemName));
1269 pNewMember->SetIsVisible(true);
1270 iter->AddMember(std::move(pNewMember));
1274 mbDimensionMembersBuilt = true;
1277 void ScDPSaveData::SyncAllDimensionMembers(ScDPTableData* pData)
1279 typedef std::unordered_map<OUString, tools::Long> NameIndexMap;
1281 // First, build a dimension name-to-index map.
1282 NameIndexMap aMap;
1283 tools::Long nColCount = pData->GetColumnCount();
1284 for (tools::Long i = 0; i < nColCount; ++i)
1285 aMap.emplace(pData->getDimensionName(i), i);
1287 NameIndexMap::const_iterator itMapEnd = aMap.end();
1289 for (auto const& it : m_DimList)
1291 const OUString& rDimName = it->GetName();
1292 if (rDimName.isEmpty())
1293 // empty dimension name. It must be data layout.
1294 continue;
1296 NameIndexMap::const_iterator itMap = aMap.find(rDimName);
1297 if (itMap == itMapEnd)
1298 // dimension name not in the data. This should never happen!
1299 continue;
1301 ScDPSaveDimension::MemberSetType aMemNames;
1302 tools::Long nDimIndex = itMap->second;
1303 const std::vector<SCROW>& rMembers = pData->GetColumnEntries(nDimIndex);
1304 size_t nMemberCount = rMembers.size();
1305 for (size_t j = 0; j < nMemberCount; ++j)
1307 const ScDPItemData* pMemberData = pData->GetMemberById(nDimIndex, rMembers[j]);
1308 // ScDPCache::GetItemDataById() (via
1309 // ScDPTableData::GetMemberById(),
1310 // ScDPGroupTableData::GetMemberById() through
1311 // GetCacheTable().getCache()) may return nullptr.
1312 if (pMemberData)
1314 OUString aMemName = pData->GetFormattedString(nDimIndex, *pMemberData, false);
1315 aMemNames.insert(aMemName);
1317 else
1319 SAL_WARN("sc.core", "No pMemberData for nDimIndex " << nDimIndex << ", rMembers[j] " << rMembers[j]
1320 << ", j " << j);
1324 it->RemoveObsoleteMembers(aMemNames);
1328 bool ScDPSaveData::HasInvisibleMember(std::u16string_view rDimName) const
1330 ScDPSaveDimension* pDim = GetExistingDimensionByName(rDimName);
1331 if (!pDim)
1332 return false;
1334 return pDim->HasInvisibleMember();
1337 #if DUMP_PIVOT_TABLE
1339 void ScDPSaveData::Dump() const
1341 for (auto const& itDim : m_DimList)
1343 const ScDPSaveDimension& rDim = *itDim;
1344 rDim.Dump();
1348 #endif
1350 void ScDPSaveData::CheckDuplicateName(ScDPSaveDimension& rDim)
1352 const OUString aName = ScDPUtil::getSourceDimensionName(rDim.GetName());
1353 DupNameCountType::iterator it = maDupNameCounts.find(aName);
1354 if (it != maDupNameCounts.end())
1356 rDim.SetName(ScDPUtil::createDuplicateDimensionName(aName, ++it->second));
1357 rDim.SetDupFlag(true);
1359 else
1360 // New name.
1361 maDupNameCounts.emplace(aName, 0);
1364 void ScDPSaveData::RemoveDuplicateNameCount(const OUString& rName)
1366 OUString aCoreName = rName;
1367 if (ScDPUtil::isDuplicateDimension(rName))
1368 aCoreName = ScDPUtil::getSourceDimensionName(rName);
1370 DupNameCountType::iterator it = maDupNameCounts.find(aCoreName);
1371 if (it == maDupNameCounts.end())
1372 return;
1374 if (!it->second)
1376 maDupNameCounts.erase(it);
1377 return;
1380 --it->second;
1383 ScDPSaveDimension* ScDPSaveData::AppendNewDimension(const OUString& rName, bool bDataLayout)
1385 if (ScDPUtil::isDuplicateDimension(rName))
1386 // This call is for original dimensions only.
1387 return nullptr;
1389 ScDPSaveDimension* pNew = new ScDPSaveDimension(rName, bDataLayout);
1390 m_DimList.push_back(std::unique_ptr<ScDPSaveDimension>(pNew));
1391 if (!maDupNameCounts.count(rName))
1392 maDupNameCounts.emplace(rName, 0);
1394 DimensionsChanged();
1395 return pNew;
1398 void ScDPSaveData::DimensionsChanged()
1400 mpDimOrder.reset();
1403 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */