1403 lines
43 KiB
C++
1403 lines
43 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* This file incorporates work covered by the following license notice:
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed
|
|
* with this work for additional information regarding copyright
|
|
* ownership. The ASF licenses this file to you under the Apache
|
|
* License, Version 2.0 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
|
|
*/
|
|
|
|
#include <memory>
|
|
#include <dpsave.hxx>
|
|
#include <dpdimsave.hxx>
|
|
#include <miscuno.hxx>
|
|
#include <unonames.hxx>
|
|
#include <dputil.hxx>
|
|
#include <generalfunction.hxx>
|
|
#include <dptabdat.hxx>
|
|
#include <pivot/PivotTableFormats.hxx>
|
|
|
|
#include <sal/types.h>
|
|
#include <sal/log.hxx>
|
|
#include <osl/diagnose.h>
|
|
#include <comphelper/stl_types.hxx>
|
|
#include <unotools/charclass.hxx>
|
|
|
|
#include <com/sun/star/sheet/XDimensionsSupplier.hpp>
|
|
#include <com/sun/star/sheet/DataPilotFieldAutoShowInfo.hpp>
|
|
#include <com/sun/star/sheet/DataPilotFieldLayoutInfo.hpp>
|
|
#include <com/sun/star/sheet/DataPilotFieldReference.hpp>
|
|
#include <com/sun/star/sheet/DataPilotFieldSortInfo.hpp>
|
|
#include <com/sun/star/sheet/DataPilotFieldSortMode.hpp>
|
|
#include <com/sun/star/sheet/XHierarchiesSupplier.hpp>
|
|
#include <com/sun/star/sheet/XLevelsSupplier.hpp>
|
|
#include <com/sun/star/sheet/XMembersSupplier.hpp>
|
|
#include <com/sun/star/container/XNamed.hpp>
|
|
#include <com/sun/star/util/XCloneable.hpp>
|
|
#include <comphelper/diagnose_ex.hxx>
|
|
|
|
#include <unordered_map>
|
|
#include <algorithm>
|
|
#include <utility>
|
|
|
|
using namespace com::sun::star;
|
|
using namespace com::sun::star::sheet;
|
|
using ::std::unique_ptr;
|
|
|
|
#define SC_DPSAVEMODE_DONTKNOW 2
|
|
|
|
static void lcl_SetBoolProperty( const uno::Reference<beans::XPropertySet>& xProp,
|
|
const OUString& rName, bool bValue )
|
|
{
|
|
//TODO: move to ScUnoHelpFunctions?
|
|
|
|
xProp->setPropertyValue( rName, uno::Any( bValue ) );
|
|
}
|
|
|
|
ScDPSaveMember::ScDPSaveMember(OUString _aName) :
|
|
aName(std::move( _aName )),
|
|
nVisibleMode( SC_DPSAVEMODE_DONTKNOW ),
|
|
nShowDetailsMode( SC_DPSAVEMODE_DONTKNOW )
|
|
{
|
|
}
|
|
|
|
ScDPSaveMember::ScDPSaveMember(const ScDPSaveMember& r) :
|
|
aName( r.aName ),
|
|
mpLayoutName( r.mpLayoutName ),
|
|
nVisibleMode( r.nVisibleMode ),
|
|
nShowDetailsMode( r.nShowDetailsMode )
|
|
{
|
|
}
|
|
|
|
ScDPSaveMember::~ScDPSaveMember()
|
|
{
|
|
}
|
|
|
|
bool ScDPSaveMember::operator== ( const ScDPSaveMember& r ) const
|
|
{
|
|
return aName == r.aName &&
|
|
nVisibleMode == r.nVisibleMode &&
|
|
nShowDetailsMode == r.nShowDetailsMode;
|
|
}
|
|
|
|
bool ScDPSaveMember::HasIsVisible() const
|
|
{
|
|
return nVisibleMode != SC_DPSAVEMODE_DONTKNOW;
|
|
}
|
|
|
|
void ScDPSaveMember::SetIsVisible(bool bSet)
|
|
{
|
|
nVisibleMode = sal_uInt16(bSet);
|
|
}
|
|
|
|
bool ScDPSaveMember::HasShowDetails() const
|
|
{
|
|
return nShowDetailsMode != SC_DPSAVEMODE_DONTKNOW;
|
|
}
|
|
|
|
void ScDPSaveMember::SetShowDetails(bool bSet)
|
|
{
|
|
nShowDetailsMode = sal_uInt16(bSet);
|
|
}
|
|
|
|
void ScDPSaveMember::SetName( const OUString& rNew )
|
|
{
|
|
// Used only if the source member was renamed (groups).
|
|
// For UI renaming of members, a layout name must be used.
|
|
|
|
aName = rNew;
|
|
}
|
|
|
|
void ScDPSaveMember::SetLayoutName( const OUString& rName )
|
|
{
|
|
mpLayoutName = rName;
|
|
}
|
|
|
|
const std::optional<OUString> & ScDPSaveMember::GetLayoutName() const
|
|
{
|
|
return mpLayoutName;
|
|
}
|
|
|
|
void ScDPSaveMember::RemoveLayoutName()
|
|
{
|
|
mpLayoutName.reset();
|
|
}
|
|
|
|
void ScDPSaveMember::WriteToSource( const uno::Reference<uno::XInterface>& xMember, sal_Int32 nPosition )
|
|
{
|
|
uno::Reference<beans::XPropertySet> xMembProp( xMember, uno::UNO_QUERY );
|
|
OSL_ENSURE( xMembProp.is(), "no properties at member" );
|
|
if ( !xMembProp.is() )
|
|
return;
|
|
|
|
// exceptions are caught at ScDPSaveData::WriteToSource
|
|
|
|
if ( nVisibleMode != SC_DPSAVEMODE_DONTKNOW )
|
|
lcl_SetBoolProperty( xMembProp,
|
|
SC_UNO_DP_ISVISIBLE, static_cast<bool>(nVisibleMode) );
|
|
|
|
if ( nShowDetailsMode != SC_DPSAVEMODE_DONTKNOW )
|
|
lcl_SetBoolProperty( xMembProp,
|
|
SC_UNO_DP_SHOWDETAILS, static_cast<bool>(nShowDetailsMode) );
|
|
|
|
if (mpLayoutName)
|
|
ScUnoHelpFunctions::SetOptionalPropertyValue(xMembProp, SC_UNO_DP_LAYOUTNAME, *mpLayoutName);
|
|
|
|
if ( nPosition >= 0 )
|
|
ScUnoHelpFunctions::SetOptionalPropertyValue(xMembProp, SC_UNO_DP_POSITION, nPosition);
|
|
}
|
|
|
|
#if DUMP_PIVOT_TABLE
|
|
|
|
void ScDPSaveMember::Dump(int nIndent) const
|
|
{
|
|
std::string aIndent(nIndent*4, ' ');
|
|
cout << aIndent << "* member name: '" << aName << "'" << endl;
|
|
|
|
cout << aIndent << " + layout name: ";
|
|
if (mpLayoutName)
|
|
cout << "'" << *mpLayoutName << "'";
|
|
else
|
|
cout << "(none)";
|
|
cout << endl;
|
|
|
|
cout << aIndent << " + visibility: ";
|
|
if (nVisibleMode == SC_DPSAVEMODE_DONTKNOW)
|
|
cout << "(unknown)";
|
|
else
|
|
cout << (nVisibleMode ? "visible" : "hidden");
|
|
cout << endl;
|
|
}
|
|
|
|
#endif
|
|
|
|
ScDPSaveDimension::ScDPSaveDimension(OUString _aName, bool bDataLayout) :
|
|
aName(std::move( _aName )),
|
|
bIsDataLayout( bDataLayout ),
|
|
bDupFlag( false ),
|
|
nOrientation( sheet::DataPilotFieldOrientation_HIDDEN ),
|
|
nFunction( ScGeneralFunction::AUTO ),
|
|
nUsedHierarchy( -1 ),
|
|
nShowEmptyMode( SC_DPSAVEMODE_DONTKNOW ),
|
|
bRepeatItemLabels( false ),
|
|
bSubTotalDefault( true )
|
|
{
|
|
}
|
|
|
|
ScDPSaveDimension::ScDPSaveDimension(const ScDPSaveDimension& r) :
|
|
aName( r.aName ),
|
|
mpLayoutName( r.mpLayoutName ),
|
|
mpSubtotalName( r.mpSubtotalName ),
|
|
bIsDataLayout( r.bIsDataLayout ),
|
|
bDupFlag( r.bDupFlag ),
|
|
nOrientation( r.nOrientation ),
|
|
nFunction( r.nFunction ),
|
|
nUsedHierarchy( r.nUsedHierarchy ),
|
|
nShowEmptyMode( r.nShowEmptyMode ),
|
|
bRepeatItemLabels( r.bRepeatItemLabels ),
|
|
bSubTotalDefault( r.bSubTotalDefault ),
|
|
maSubTotalFuncs( r.maSubTotalFuncs )
|
|
{
|
|
for (const ScDPSaveMember* pMem : r.maMemberList)
|
|
{
|
|
const OUString& rName = pMem->GetName();
|
|
std::unique_ptr<ScDPSaveMember> pNew(new ScDPSaveMember( *pMem ));
|
|
maMemberList.push_back( pNew.get() );
|
|
maMemberHash[rName] = std::move(pNew);
|
|
}
|
|
if (r.pReferenceValue)
|
|
pReferenceValue.reset( new sheet::DataPilotFieldReference( *(r.pReferenceValue) ) );
|
|
if (r.pSortInfo)
|
|
pSortInfo.reset( new sheet::DataPilotFieldSortInfo( *(r.pSortInfo) ) );
|
|
if (r.pAutoShowInfo)
|
|
pAutoShowInfo.reset( new sheet::DataPilotFieldAutoShowInfo( *(r.pAutoShowInfo) ) );
|
|
if (r.pLayoutInfo)
|
|
pLayoutInfo.reset(new sheet::DataPilotFieldLayoutInfo( *(r.pLayoutInfo) ));
|
|
}
|
|
|
|
ScDPSaveDimension::~ScDPSaveDimension()
|
|
{
|
|
maMemberHash.clear();
|
|
pReferenceValue.reset();
|
|
pSortInfo.reset();
|
|
pAutoShowInfo.reset();
|
|
pLayoutInfo.reset();
|
|
}
|
|
|
|
bool ScDPSaveDimension::operator== ( const ScDPSaveDimension& r ) const
|
|
{
|
|
if ( aName != r.aName ||
|
|
bIsDataLayout != r.bIsDataLayout ||
|
|
bDupFlag != r.bDupFlag ||
|
|
nOrientation != r.nOrientation ||
|
|
nFunction != r.nFunction ||
|
|
nUsedHierarchy != r.nUsedHierarchy ||
|
|
nShowEmptyMode != r.nShowEmptyMode ||
|
|
bRepeatItemLabels!= r.bRepeatItemLabels||
|
|
bSubTotalDefault != r.bSubTotalDefault ||
|
|
maSubTotalFuncs != r.maSubTotalFuncs )
|
|
return false;
|
|
|
|
if (maMemberHash.size() != r.maMemberHash.size() )
|
|
return false;
|
|
|
|
if (!std::equal(maMemberList.begin(), maMemberList.end(), r.maMemberList.begin(), r.maMemberList.end(),
|
|
[](const ScDPSaveMember* a, const ScDPSaveMember* b) { return *a == *b; }))
|
|
return false;
|
|
|
|
if( pReferenceValue && r.pReferenceValue )
|
|
{
|
|
if ( *pReferenceValue != *r.pReferenceValue )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else if ( pReferenceValue || r.pReferenceValue )
|
|
{
|
|
return false;
|
|
}
|
|
if( this->pSortInfo && r.pSortInfo )
|
|
{
|
|
if ( *this->pSortInfo != *r.pSortInfo )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else if ( this->pSortInfo || r.pSortInfo )
|
|
{
|
|
return false;
|
|
}
|
|
if( this->pAutoShowInfo && r.pAutoShowInfo )
|
|
{
|
|
if ( *this->pAutoShowInfo != *r.pAutoShowInfo )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else if ( this->pAutoShowInfo || r.pAutoShowInfo )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void ScDPSaveDimension::AddMember(std::unique_ptr<ScDPSaveMember> pMember)
|
|
{
|
|
const OUString & rName = pMember->GetName();
|
|
auto aExisting = maMemberHash.find( rName );
|
|
auto tmp = pMember.get();
|
|
if ( aExisting == maMemberHash.end() )
|
|
{
|
|
maMemberHash[rName] = std::move(pMember);
|
|
}
|
|
else
|
|
{
|
|
std::erase(maMemberList, aExisting->second.get());
|
|
aExisting->second = std::move(pMember);
|
|
}
|
|
maMemberList.push_back( tmp );
|
|
}
|
|
|
|
void ScDPSaveDimension::SetName( const OUString& rNew )
|
|
{
|
|
// Used only if the source dim was renamed (groups).
|
|
// For UI renaming of dimensions, the layout name must be used.
|
|
|
|
aName = rNew;
|
|
}
|
|
|
|
void ScDPSaveDimension::SetOrientation(css::sheet::DataPilotFieldOrientation nNew)
|
|
{
|
|
nOrientation = nNew;
|
|
}
|
|
|
|
void ScDPSaveDimension::SetSubTotals(std::vector<ScGeneralFunction> && rFuncs)
|
|
{
|
|
maSubTotalFuncs = std::move(rFuncs);
|
|
bSubTotalDefault = false;
|
|
}
|
|
|
|
bool ScDPSaveDimension::HasShowEmpty() const
|
|
{
|
|
return nShowEmptyMode != SC_DPSAVEMODE_DONTKNOW;
|
|
}
|
|
|
|
void ScDPSaveDimension::SetShowEmpty(bool bSet)
|
|
{
|
|
nShowEmptyMode = sal_uInt16(bSet);
|
|
}
|
|
|
|
void ScDPSaveDimension::SetRepeatItemLabels(bool bSet)
|
|
{
|
|
bRepeatItemLabels = bSet;
|
|
}
|
|
|
|
void ScDPSaveDimension::SetFunction(ScGeneralFunction nNew)
|
|
{
|
|
nFunction = nNew;
|
|
}
|
|
|
|
void ScDPSaveDimension::SetUsedHierarchy(tools::Long nNew)
|
|
{
|
|
nUsedHierarchy = nNew;
|
|
}
|
|
|
|
void ScDPSaveDimension::SetSubtotalName(const OUString& rName)
|
|
{
|
|
mpSubtotalName = rName;
|
|
}
|
|
|
|
const std::optional<OUString> & ScDPSaveDimension::GetSubtotalName() const
|
|
{
|
|
return mpSubtotalName;
|
|
}
|
|
|
|
void ScDPSaveDimension::RemoveSubtotalName()
|
|
{
|
|
mpSubtotalName.reset();
|
|
}
|
|
|
|
bool ScDPSaveDimension::IsMemberNameInUse(const OUString& rName) const
|
|
{
|
|
return std::any_of(maMemberList.begin(), maMemberList.end(), [&rName](const ScDPSaveMember* pMem) {
|
|
if (rName.equalsIgnoreAsciiCase(pMem->GetName()))
|
|
return true;
|
|
|
|
const std::optional<OUString> & pLayoutName = pMem->GetLayoutName();
|
|
return pLayoutName && rName.equalsIgnoreAsciiCase(*pLayoutName);
|
|
});
|
|
}
|
|
|
|
void ScDPSaveDimension::SetLayoutName(const OUString& rName)
|
|
{
|
|
mpLayoutName = rName;
|
|
}
|
|
|
|
const std::optional<OUString> & ScDPSaveDimension::GetLayoutName() const
|
|
{
|
|
return mpLayoutName;
|
|
}
|
|
|
|
void ScDPSaveDimension::RemoveLayoutName()
|
|
{
|
|
mpLayoutName.reset();
|
|
}
|
|
|
|
void ScDPSaveDimension::SetReferenceValue(const sheet::DataPilotFieldReference* pNew)
|
|
{
|
|
if (pNew)
|
|
pReferenceValue.reset( new sheet::DataPilotFieldReference(*pNew) );
|
|
else
|
|
pReferenceValue.reset();
|
|
}
|
|
|
|
void ScDPSaveDimension::SetSortInfo(const sheet::DataPilotFieldSortInfo* pNew)
|
|
{
|
|
if (pNew)
|
|
pSortInfo.reset( new sheet::DataPilotFieldSortInfo(*pNew) );
|
|
else
|
|
pSortInfo.reset();
|
|
}
|
|
|
|
void ScDPSaveDimension::SetAutoShowInfo(const sheet::DataPilotFieldAutoShowInfo* pNew)
|
|
{
|
|
if (pNew)
|
|
pAutoShowInfo.reset( new sheet::DataPilotFieldAutoShowInfo(*pNew) );
|
|
else
|
|
pAutoShowInfo.reset();
|
|
}
|
|
|
|
void ScDPSaveDimension::SetLayoutInfo(const sheet::DataPilotFieldLayoutInfo* pNew)
|
|
{
|
|
if (pNew)
|
|
pLayoutInfo.reset( new sheet::DataPilotFieldLayoutInfo(*pNew) );
|
|
else
|
|
pLayoutInfo.reset();
|
|
}
|
|
|
|
void ScDPSaveDimension::SetCurrentPage( const OUString* pPage )
|
|
{
|
|
// We use member's visibility attribute to filter by page dimension.
|
|
|
|
// pPage == nullptr -> all members visible.
|
|
for (ScDPSaveMember* pMem : maMemberList)
|
|
{
|
|
bool bVisible = !pPage || pMem->GetName() == *pPage;
|
|
pMem->SetIsVisible(bVisible);
|
|
}
|
|
}
|
|
|
|
OUString ScDPSaveDimension::GetCurrentPage() const
|
|
{
|
|
MemberList::const_iterator it = std::find_if(maMemberList.begin(), maMemberList.end(),
|
|
[](const ScDPSaveMember* pMem) { return pMem->GetIsVisible(); });
|
|
if (it != maMemberList.end())
|
|
return (*it)->GetName();
|
|
|
|
return OUString();
|
|
}
|
|
|
|
ScDPSaveMember* ScDPSaveDimension::GetExistingMemberByName(const OUString& rName)
|
|
{
|
|
auto res = maMemberHash.find (rName);
|
|
if (res != maMemberHash.end())
|
|
return res->second.get();
|
|
return nullptr;
|
|
}
|
|
|
|
ScDPSaveMember* ScDPSaveDimension::GetMemberByName(const OUString& rName)
|
|
{
|
|
ScDPSaveMember* pResult = GetExistingMemberByName(rName);
|
|
if (pResult)
|
|
return pResult;
|
|
|
|
pResult = new ScDPSaveMember(rName);
|
|
maMemberHash[rName] = std::unique_ptr<ScDPSaveMember>(pResult);
|
|
maMemberList.push_back(pResult);
|
|
return pResult;
|
|
}
|
|
|
|
void ScDPSaveDimension::SetMemberPosition( const OUString& rName, sal_Int32 nNewPos )
|
|
{
|
|
ScDPSaveMember* pMember = GetMemberByName( rName ); // make sure it exists and is in the hash
|
|
|
|
std::erase(maMemberList, pMember);
|
|
|
|
maMemberList.insert( maMemberList.begin() + nNewPos, pMember );
|
|
}
|
|
|
|
void ScDPSaveDimension::WriteToSource( const uno::Reference<uno::XInterface>& xDim )
|
|
{
|
|
uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY );
|
|
OSL_ENSURE( xDimProp.is(), "no properties at dimension" );
|
|
if ( xDimProp.is() )
|
|
{
|
|
// exceptions are caught at ScDPSaveData::WriteToSource
|
|
|
|
sheet::DataPilotFieldOrientation eOrient = nOrientation;
|
|
xDimProp->setPropertyValue( SC_UNO_DP_ORIENTATION, uno::Any(eOrient) );
|
|
|
|
sal_Int16 eFunc = static_cast<sal_Int16>(nFunction);
|
|
xDimProp->setPropertyValue( SC_UNO_DP_FUNCTION2, uno::Any(eFunc) );
|
|
|
|
if ( nUsedHierarchy >= 0 )
|
|
{
|
|
xDimProp->setPropertyValue( SC_UNO_DP_USEDHIERARCHY, uno::Any(static_cast<sal_Int32>(nUsedHierarchy)) );
|
|
}
|
|
|
|
if ( pReferenceValue )
|
|
{
|
|
;
|
|
xDimProp->setPropertyValue( SC_UNO_DP_REFVALUE, uno::Any(*pReferenceValue) );
|
|
}
|
|
|
|
if (mpLayoutName)
|
|
ScUnoHelpFunctions::SetOptionalPropertyValue(xDimProp, SC_UNO_DP_LAYOUTNAME, *mpLayoutName);
|
|
|
|
const std::optional<OUString> & pSubTotalName = GetSubtotalName();
|
|
if (pSubTotalName)
|
|
// Custom subtotal name, with '?' being replaced by the visible field name later.
|
|
ScUnoHelpFunctions::SetOptionalPropertyValue(xDimProp, SC_UNO_DP_FIELD_SUBTOTALNAME, *pSubTotalName);
|
|
}
|
|
|
|
// Level loop outside of maMemberList loop
|
|
// because SubTotals have to be set independently of known members
|
|
|
|
tools::Long nCount = maMemberHash.size();
|
|
|
|
tools::Long nHierCount = 0;
|
|
uno::Reference<container::XIndexAccess> xHiers;
|
|
uno::Reference<sheet::XHierarchiesSupplier> xHierSupp( xDim, uno::UNO_QUERY );
|
|
if ( xHierSupp.is() )
|
|
{
|
|
xHiers = new ScNameToIndexAccess(xHierSupp->getHierarchies());
|
|
nHierCount = xHiers->getCount();
|
|
}
|
|
|
|
bool bHasHiddenMember = false;
|
|
|
|
for (tools::Long nHier=0; nHier<nHierCount; nHier++)
|
|
{
|
|
tools::Long nLevCount = 0;
|
|
uno::Reference<container::XIndexAccess> xLevels;
|
|
uno::Reference<sheet::XLevelsSupplier> xLevSupp(xHiers->getByIndex(nHier), uno::UNO_QUERY);
|
|
if ( xLevSupp.is() )
|
|
{
|
|
xLevels = new ScNameToIndexAccess(xLevSupp->getLevels());
|
|
nLevCount = xLevels->getCount();
|
|
}
|
|
|
|
for (tools::Long nLev=0; nLev<nLevCount; nLev++)
|
|
{
|
|
uno::Reference<uno::XInterface> xLevel(xLevels->getByIndex(nLev), uno::UNO_QUERY);
|
|
uno::Reference<beans::XPropertySet> xLevProp( xLevel, uno::UNO_QUERY );
|
|
OSL_ENSURE( xLevProp.is(), "no properties at level" );
|
|
if ( xLevProp.is() )
|
|
{
|
|
if ( !bSubTotalDefault )
|
|
{
|
|
uno::Sequence<sal_Int16> aSeq(maSubTotalFuncs.size());
|
|
for(size_t i = 0; i < maSubTotalFuncs.size(); ++i)
|
|
aSeq.getArray()[i] = static_cast<sal_Int16>(maSubTotalFuncs[i]);
|
|
xLevProp->setPropertyValue( SC_UNO_DP_SUBTOTAL2, uno::Any(aSeq) );
|
|
}
|
|
if ( nShowEmptyMode != SC_DPSAVEMODE_DONTKNOW )
|
|
lcl_SetBoolProperty( xLevProp,
|
|
SC_UNO_DP_SHOWEMPTY, static_cast<bool>(nShowEmptyMode) );
|
|
|
|
lcl_SetBoolProperty( xLevProp,
|
|
SC_UNO_DP_REPEATITEMLABELS, bRepeatItemLabels );
|
|
|
|
if ( pSortInfo )
|
|
ScUnoHelpFunctions::SetOptionalPropertyValue(xLevProp, SC_UNO_DP_SORTING, *pSortInfo);
|
|
|
|
if ( pAutoShowInfo )
|
|
ScUnoHelpFunctions::SetOptionalPropertyValue(xLevProp, SC_UNO_DP_AUTOSHOW, *pAutoShowInfo);
|
|
|
|
if ( pLayoutInfo )
|
|
ScUnoHelpFunctions::SetOptionalPropertyValue(xLevProp, SC_UNO_DP_LAYOUT, *pLayoutInfo);
|
|
|
|
// exceptions are caught at ScDPSaveData::WriteToSource
|
|
}
|
|
|
|
if ( nCount > 0 )
|
|
{
|
|
uno::Reference<sheet::XMembersSupplier> xMembSupp( xLevel, uno::UNO_QUERY );
|
|
if ( xMembSupp.is() )
|
|
{
|
|
uno::Reference<sheet::XMembersAccess> xMembers = xMembSupp->getMembers();
|
|
if ( xMembers.is() )
|
|
{
|
|
sal_Int32 nPosition = -1; // set position only in manual mode
|
|
if ( !pSortInfo || pSortInfo->Mode == sheet::DataPilotFieldSortMode::MANUAL )
|
|
nPosition = 0;
|
|
|
|
for (ScDPSaveMember* pMember : maMemberList)
|
|
{
|
|
if (!pMember->GetIsVisible())
|
|
bHasHiddenMember = true;
|
|
OUString aMemberName = pMember->GetName();
|
|
if ( xMembers->hasByName( aMemberName ) )
|
|
{
|
|
uno::Reference<uno::XInterface> xMemberInt(
|
|
xMembers->getByName(aMemberName), uno::UNO_QUERY);
|
|
pMember->WriteToSource( xMemberInt, nPosition );
|
|
|
|
if ( nPosition >= 0 )
|
|
++nPosition; // increase if initialized
|
|
}
|
|
// missing member is no error
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (xDimProp.is())
|
|
ScUnoHelpFunctions::SetOptionalPropertyValue(xDimProp, SC_UNO_DP_HAS_HIDDEN_MEMBER, bHasHiddenMember);
|
|
}
|
|
|
|
void ScDPSaveDimension::UpdateMemberVisibility(const std::unordered_map<OUString, bool>& rData)
|
|
{
|
|
for (ScDPSaveMember* pMem : maMemberList)
|
|
{
|
|
const OUString& rMemName = pMem->GetName();
|
|
auto itr = rData.find(rMemName);
|
|
if (itr != rData.end())
|
|
pMem->SetIsVisible(itr->second);
|
|
}
|
|
}
|
|
|
|
bool ScDPSaveDimension::HasInvisibleMember() const
|
|
{
|
|
return std::any_of(maMemberList.begin(), maMemberList.end(),
|
|
[](const ScDPSaveMember* pMem) { return !pMem->GetIsVisible(); });
|
|
}
|
|
|
|
void ScDPSaveDimension::RemoveObsoleteMembers(const MemberSetType& rMembers)
|
|
{
|
|
MemberList aNew;
|
|
for (ScDPSaveMember* pMem : maMemberList)
|
|
{
|
|
if (rMembers.count(pMem->GetName()))
|
|
{
|
|
// This member still exists.
|
|
aNew.push_back(pMem);
|
|
}
|
|
else
|
|
{
|
|
maMemberHash.erase(pMem->GetName());
|
|
}
|
|
}
|
|
|
|
maMemberList.swap(aNew);
|
|
}
|
|
|
|
#if DUMP_PIVOT_TABLE
|
|
|
|
void ScDPSaveDimension::Dump(int nIndent) const
|
|
{
|
|
static const char* pOrientNames[] = { "hidden", "column", "row", "page", "data" };
|
|
std::string aIndent(nIndent*4, ' ');
|
|
|
|
cout << aIndent << "* dimension name: '" << aName << "'" << endl;
|
|
|
|
cout << aIndent << " + orientation: ";
|
|
if (nOrientation <= DataPilotFieldOrientation_DATA)
|
|
cout << pOrientNames[static_cast<int>(nOrientation)];
|
|
else
|
|
cout << "(invalid)";
|
|
cout << endl;
|
|
|
|
cout << aIndent << " + layout name: ";
|
|
if (mpLayoutName)
|
|
cout << "'" << *mpLayoutName << "'";
|
|
else
|
|
cout << "(none)";
|
|
cout << endl;
|
|
|
|
cout << aIndent << " + subtotal name: ";
|
|
if (mpSubtotalName)
|
|
cout << "'" << *mpSubtotalName << "'";
|
|
else
|
|
cout << "(none)";
|
|
cout << endl;
|
|
|
|
cout << aIndent << " + is data layout: " << (bIsDataLayout ? "yes" : "no") << endl;
|
|
cout << aIndent << " + is duplicate: " << (bDupFlag ? "yes" : "no") << endl;
|
|
|
|
for (ScDPSaveMember* pMem : maMemberList)
|
|
{
|
|
pMem->Dump(nIndent+1);
|
|
}
|
|
|
|
cout << endl; // blank line
|
|
}
|
|
|
|
#endif
|
|
|
|
ScDPSaveData::ScDPSaveData()
|
|
: mnColumnGrandMode(SC_DPSAVEMODE_DONTKNOW)
|
|
, mnRowGrandMode(SC_DPSAVEMODE_DONTKNOW)
|
|
, mnIgnoreEmptyMode(SC_DPSAVEMODE_DONTKNOW)
|
|
, mnRepeatEmptyMode(SC_DPSAVEMODE_DONTKNOW)
|
|
, mbFilterButton(true)
|
|
, mbDrillDown(true)
|
|
, mbExpandCollapse(false)
|
|
, mbDimensionMembersBuilt(false)
|
|
{
|
|
}
|
|
|
|
ScDPSaveData::ScDPSaveData(const ScDPSaveData& rOther)
|
|
: mnColumnGrandMode(rOther.mnColumnGrandMode)
|
|
, mnRowGrandMode(rOther.mnRowGrandMode)
|
|
, mnIgnoreEmptyMode(rOther.mnIgnoreEmptyMode)
|
|
, mnRepeatEmptyMode(rOther.mnRepeatEmptyMode)
|
|
, mbFilterButton(rOther.mbFilterButton)
|
|
, mbDrillDown(rOther.mbDrillDown)
|
|
, mbExpandCollapse(rOther.mbExpandCollapse)
|
|
, mbDimensionMembersBuilt(rOther.mbDimensionMembersBuilt)
|
|
, mpGrandTotalName(rOther.mpGrandTotalName)
|
|
{
|
|
if (rOther.mpDimensionData)
|
|
mpDimensionData.reset(new ScDPDimensionSaveData(*rOther.mpDimensionData));
|
|
if (rOther.mpFormats)
|
|
mpFormats.reset(new sc::PivotTableFormats(*rOther.mpFormats));
|
|
|
|
for (auto const& rOtherSaveDimension : rOther.m_DimList)
|
|
{
|
|
m_DimList.push_back(std::make_unique<ScDPSaveDimension>(*rOtherSaveDimension));
|
|
}
|
|
}
|
|
|
|
ScDPSaveData& ScDPSaveData::operator=(const ScDPSaveData& rOther)
|
|
{
|
|
if (&rOther != this)
|
|
{
|
|
this->~ScDPSaveData();
|
|
new(this)ScDPSaveData(rOther);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
bool ScDPSaveData::operator== (const ScDPSaveData& rOther) const
|
|
{
|
|
if (mnColumnGrandMode != rOther.mnColumnGrandMode ||
|
|
mnRowGrandMode != rOther.mnRowGrandMode ||
|
|
mnIgnoreEmptyMode != rOther.mnIgnoreEmptyMode ||
|
|
mnRepeatEmptyMode != rOther.mnRepeatEmptyMode ||
|
|
mbFilterButton != rOther.mbFilterButton ||
|
|
mbDrillDown != rOther.mbDrillDown ||
|
|
mbDimensionMembersBuilt != rOther.mbDimensionMembersBuilt)
|
|
return false;
|
|
|
|
if (mpDimensionData || rOther.mpDimensionData)
|
|
if (!mpDimensionData || !rOther.mpDimensionData || !(*mpDimensionData == *rOther.mpDimensionData))
|
|
return false;
|
|
|
|
if (!(::comphelper::ContainerUniquePtrEquals(m_DimList, rOther.m_DimList)))
|
|
return false;
|
|
|
|
if (mpGrandTotalName)
|
|
{
|
|
if (!rOther.mpGrandTotalName)
|
|
return false;
|
|
if (*mpGrandTotalName != *rOther.mpGrandTotalName)
|
|
return false;
|
|
}
|
|
else if (rOther.mpGrandTotalName)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
ScDPSaveData::~ScDPSaveData()
|
|
{
|
|
}
|
|
|
|
void ScDPSaveData::setFormats(sc::PivotTableFormats const& rPivotTableFormats)
|
|
{
|
|
mpFormats.reset(new sc::PivotTableFormats(rPivotTableFormats));
|
|
}
|
|
|
|
bool ScDPSaveData::hasFormats()
|
|
{
|
|
return bool(mpFormats);
|
|
}
|
|
|
|
sc::PivotTableFormats const& ScDPSaveData::getFormats()
|
|
{
|
|
return *mpFormats;
|
|
}
|
|
|
|
void ScDPSaveData::SetGrandTotalName(const OUString& rName)
|
|
{
|
|
mpGrandTotalName = rName;
|
|
}
|
|
|
|
const std::optional<OUString> & ScDPSaveData::GetGrandTotalName() const
|
|
{
|
|
return mpGrandTotalName;
|
|
}
|
|
|
|
namespace {
|
|
|
|
class DimOrderInserter
|
|
{
|
|
ScDPSaveData::DimOrderType& mrNames;
|
|
public:
|
|
explicit DimOrderInserter(ScDPSaveData::DimOrderType& rNames) : mrNames(rNames) {}
|
|
|
|
void operator() (const ScDPSaveDimension* pDim)
|
|
{
|
|
size_t nRank = mrNames.size();
|
|
mrNames.emplace(ScGlobal::getCharClass().uppercase(pDim->GetName()), nRank);
|
|
}
|
|
};
|
|
|
|
}
|
|
|
|
const ScDPSaveData::DimOrderType& ScDPSaveData::GetDimensionSortOrder() const
|
|
{
|
|
if (!mpDimOrder)
|
|
{
|
|
mpDimOrder.reset(new DimOrderType);
|
|
std::vector<const ScDPSaveDimension*> aRowDims, aColDims;
|
|
GetAllDimensionsByOrientation(sheet::DataPilotFieldOrientation_ROW, aRowDims);
|
|
GetAllDimensionsByOrientation(sheet::DataPilotFieldOrientation_COLUMN, aColDims);
|
|
|
|
std::for_each(aRowDims.begin(), aRowDims.end(), DimOrderInserter(*mpDimOrder));
|
|
std::for_each(aColDims.begin(), aColDims.end(), DimOrderInserter(*mpDimOrder));
|
|
}
|
|
return *mpDimOrder;
|
|
}
|
|
|
|
void ScDPSaveData::GetAllDimensionsByOrientation(
|
|
sheet::DataPilotFieldOrientation eOrientation, std::vector<const ScDPSaveDimension*>& rDims) const
|
|
{
|
|
std::vector<const ScDPSaveDimension*> aDims;
|
|
for (auto const& it : m_DimList)
|
|
{
|
|
const ScDPSaveDimension& rDim = *it;
|
|
if (rDim.GetOrientation() != eOrientation)
|
|
continue;
|
|
|
|
aDims.push_back(&rDim);
|
|
}
|
|
|
|
rDims.swap(aDims);
|
|
}
|
|
|
|
void ScDPSaveData::AddDimension(ScDPSaveDimension* pDim)
|
|
{
|
|
if (!pDim)
|
|
return;
|
|
|
|
CheckDuplicateName(*pDim);
|
|
m_DimList.push_back(std::unique_ptr<ScDPSaveDimension>(pDim));
|
|
|
|
DimensionsChanged();
|
|
}
|
|
|
|
ScDPSaveDimension* ScDPSaveData::GetDimensionByName(const OUString& rName)
|
|
{
|
|
for (auto const& iter : m_DimList)
|
|
{
|
|
if (iter->GetName() == rName && !iter->IsDataLayout() )
|
|
return &(*iter);
|
|
}
|
|
|
|
return AppendNewDimension(rName, false);
|
|
}
|
|
|
|
ScDPSaveDimension* ScDPSaveData::GetExistingDimensionByName(std::u16string_view rName) const
|
|
{
|
|
for (auto const& iter : m_DimList)
|
|
{
|
|
if (iter->GetName() == rName && !iter->IsDataLayout() )
|
|
return &(*iter);
|
|
}
|
|
return nullptr; // don't create new
|
|
}
|
|
|
|
ScDPSaveDimension* ScDPSaveData::GetNewDimensionByName(const OUString& rName)
|
|
{
|
|
for (auto const& iter : m_DimList)
|
|
{
|
|
if (iter->GetName() == rName && !iter->IsDataLayout() )
|
|
return DuplicateDimension(rName);
|
|
}
|
|
|
|
return AppendNewDimension(rName, false);
|
|
}
|
|
|
|
ScDPSaveDimension* ScDPSaveData::GetDataLayoutDimension()
|
|
{
|
|
ScDPSaveDimension* pDim = GetExistingDataLayoutDimension();
|
|
if (pDim)
|
|
return pDim;
|
|
|
|
return AppendNewDimension(OUString(), true);
|
|
}
|
|
|
|
ScDPSaveDimension* ScDPSaveData::GetExistingDataLayoutDimension() const
|
|
{
|
|
for (auto const& iter : m_DimList)
|
|
{
|
|
if ( iter->IsDataLayout() )
|
|
return &(*iter);
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
ScDPSaveDimension* ScDPSaveData::DuplicateDimension(std::u16string_view rName)
|
|
{
|
|
// always insert new
|
|
|
|
ScDPSaveDimension* pOld = GetExistingDimensionByName(rName);
|
|
if (!pOld)
|
|
return nullptr;
|
|
|
|
ScDPSaveDimension* pNew = new ScDPSaveDimension( *pOld );
|
|
AddDimension(pNew);
|
|
return pNew;
|
|
}
|
|
|
|
void ScDPSaveData::RemoveDimensionByName(const OUString& rName)
|
|
{
|
|
auto iter = std::find_if(m_DimList.begin(), m_DimList.end(),
|
|
[&rName](const std::unique_ptr<ScDPSaveDimension>& rxDim) {
|
|
return rxDim->GetName() == rName && !rxDim->IsDataLayout(); });
|
|
if (iter != m_DimList.end())
|
|
{
|
|
m_DimList.erase(iter);
|
|
RemoveDuplicateNameCount(rName);
|
|
DimensionsChanged();
|
|
}
|
|
}
|
|
|
|
ScDPSaveDimension& ScDPSaveData::DuplicateDimension( const ScDPSaveDimension& rDim )
|
|
{
|
|
ScDPSaveDimension* pNew = new ScDPSaveDimension( rDim );
|
|
AddDimension(pNew);
|
|
return *pNew;
|
|
}
|
|
|
|
ScDPSaveDimension* ScDPSaveData::GetInnermostDimension(DataPilotFieldOrientation nOrientation)
|
|
{
|
|
// return the innermost dimension for the given orientation,
|
|
// excluding data layout dimension
|
|
|
|
auto iter = std::find_if(m_DimList.rbegin(), m_DimList.rend(),
|
|
[&nOrientation](const std::unique_ptr<ScDPSaveDimension>& rxDim) {
|
|
return rxDim->GetOrientation() == nOrientation && !rxDim->IsDataLayout(); });
|
|
if (iter != m_DimList.rend())
|
|
return iter->get();
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
ScDPSaveDimension* ScDPSaveData::GetFirstDimension(sheet::DataPilotFieldOrientation eOrientation)
|
|
{
|
|
for (auto const& iter : m_DimList)
|
|
{
|
|
if (iter->GetOrientation() == eOrientation && !iter->IsDataLayout())
|
|
return &(*iter);
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
tools::Long ScDPSaveData::GetDataDimensionCount() const
|
|
{
|
|
tools::Long nDataCount = 0;
|
|
|
|
for (auto const& iter : m_DimList)
|
|
{
|
|
if (iter->GetOrientation() == sheet::DataPilotFieldOrientation_DATA)
|
|
++nDataCount;
|
|
}
|
|
|
|
return nDataCount;
|
|
}
|
|
|
|
void ScDPSaveData::SetPosition( ScDPSaveDimension* pDim, tools::Long nNew )
|
|
{
|
|
// position (nNew) is counted within dimensions of the same orientation
|
|
|
|
DataPilotFieldOrientation nOrient = pDim->GetOrientation();
|
|
|
|
auto it = std::find_if(m_DimList.begin(), m_DimList.end(),
|
|
[&pDim](const std::unique_ptr<ScDPSaveDimension>& rxDim) { return pDim == rxDim.get(); });
|
|
if (it != m_DimList.end())
|
|
{
|
|
// Tell vector<unique_ptr> to give up ownership of this element. Don't
|
|
// delete this instance as it is re-inserted into the container later.
|
|
// coverity[leaked_storage] - re-inserted into the container later
|
|
it->release();
|
|
m_DimList.erase(it);
|
|
}
|
|
|
|
auto iterInsert = std::find_if(m_DimList.begin(), m_DimList.end(),
|
|
[&nOrient, &nNew](const std::unique_ptr<ScDPSaveDimension>& rxDim) {
|
|
if (rxDim->GetOrientation() == nOrient )
|
|
--nNew;
|
|
return nNew <= 0;
|
|
});
|
|
|
|
m_DimList.insert(iterInsert, std::unique_ptr<ScDPSaveDimension>(pDim));
|
|
DimensionsChanged();
|
|
}
|
|
|
|
void ScDPSaveData::SetColumnGrand(bool bSet)
|
|
{
|
|
mnColumnGrandMode = sal_uInt16(bSet);
|
|
}
|
|
|
|
void ScDPSaveData::SetRowGrand(bool bSet)
|
|
{
|
|
mnRowGrandMode = sal_uInt16(bSet);
|
|
}
|
|
|
|
void ScDPSaveData::SetIgnoreEmptyRows(bool bSet)
|
|
{
|
|
mnIgnoreEmptyMode = sal_uInt16(bSet);
|
|
}
|
|
|
|
void ScDPSaveData::SetRepeatIfEmpty(bool bSet)
|
|
{
|
|
mnRepeatEmptyMode = sal_uInt16(bSet);
|
|
}
|
|
|
|
void ScDPSaveData::SetFilterButton(bool bSet)
|
|
{
|
|
mbFilterButton = bSet;
|
|
}
|
|
|
|
void ScDPSaveData::SetDrillDown(bool bSet)
|
|
{
|
|
mbDrillDown = bSet;
|
|
}
|
|
|
|
void ScDPSaveData::SetExpandCollapse(bool bSet)
|
|
{
|
|
mbExpandCollapse = bSet;
|
|
}
|
|
|
|
static void lcl_ResetOrient( const uno::Reference<sheet::XDimensionsSupplier>& xSource )
|
|
{
|
|
uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions();
|
|
uno::Reference<container::XIndexAccess> xIntDims = new ScNameToIndexAccess( xDimsName );
|
|
tools::Long nIntCount = xIntDims->getCount();
|
|
for (tools::Long nIntDim=0; nIntDim<nIntCount; nIntDim++)
|
|
{
|
|
uno::Reference<beans::XPropertySet> xDimProp(xIntDims->getByIndex(nIntDim), uno::UNO_QUERY);
|
|
if (xDimProp.is())
|
|
{
|
|
xDimProp->setPropertyValue( SC_UNO_DP_ORIENTATION, uno::Any(sheet::DataPilotFieldOrientation_HIDDEN) );
|
|
}
|
|
}
|
|
}
|
|
|
|
void ScDPSaveData::WriteToSource( const uno::Reference<sheet::XDimensionsSupplier>& xSource )
|
|
{
|
|
if (!xSource.is())
|
|
return;
|
|
|
|
// source options must be first!
|
|
|
|
uno::Reference<beans::XPropertySet> xSourceProp( xSource, uno::UNO_QUERY );
|
|
SAL_WARN_IF( !xSourceProp.is(), "sc.core", "no properties at source" );
|
|
if ( xSourceProp.is() )
|
|
{
|
|
// source options are not available for external sources
|
|
//TODO: use XPropertySetInfo to test for availability?
|
|
|
|
try
|
|
{
|
|
if (mnIgnoreEmptyMode != SC_DPSAVEMODE_DONTKNOW)
|
|
lcl_SetBoolProperty(xSourceProp, SC_UNO_DP_IGNOREEMPTY, bool(mnIgnoreEmptyMode));
|
|
if (mnRepeatEmptyMode != SC_DPSAVEMODE_DONTKNOW)
|
|
lcl_SetBoolProperty(xSourceProp, SC_UNO_DP_REPEATEMPTY, bool(mnRepeatEmptyMode));
|
|
}
|
|
catch(uno::Exception&)
|
|
{
|
|
// no error
|
|
}
|
|
|
|
const std::optional<OUString> & pGrandTotalName = GetGrandTotalName();
|
|
if (pGrandTotalName)
|
|
ScUnoHelpFunctions::SetOptionalPropertyValue(xSourceProp, SC_UNO_DP_GRANDTOTAL_NAME, *pGrandTotalName);
|
|
}
|
|
|
|
// exceptions in the other calls are errors
|
|
try
|
|
{
|
|
// reset all orientations
|
|
//TODO: "forgetSettings" or similar at source ?????
|
|
//TODO: reset all duplicated dimensions, or reuse them below !!!
|
|
SAL_INFO("sc.core", "ScDPSaveData::WriteToSource");
|
|
|
|
lcl_ResetOrient( xSource );
|
|
|
|
uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions();
|
|
uno::Reference<container::XIndexAccess> xIntDims = new ScNameToIndexAccess( xDimsName );
|
|
tools::Long nIntCount = xIntDims->getCount();
|
|
|
|
for (const auto& rxDim : m_DimList)
|
|
{
|
|
OUString aName = rxDim->GetName();
|
|
OUString aCoreName = ScDPUtil::getSourceDimensionName(aName);
|
|
|
|
SAL_INFO("sc.core", aName);
|
|
|
|
bool bData = rxDim->IsDataLayout();
|
|
|
|
//TODO: getByName for ScDPSource, including DataLayoutDimension !!!!!!!!
|
|
|
|
bool bFound = false;
|
|
for (tools::Long nIntDim=0; nIntDim<nIntCount && !bFound; nIntDim++)
|
|
{
|
|
uno::Reference<uno::XInterface> xIntDim(xIntDims->getByIndex(nIntDim),
|
|
uno::UNO_QUERY);
|
|
if ( bData )
|
|
{
|
|
uno::Reference<beans::XPropertySet> xDimProp( xIntDim, uno::UNO_QUERY );
|
|
if ( xDimProp.is() )
|
|
{
|
|
bFound = ScUnoHelpFunctions::GetBoolProperty( xDimProp,
|
|
SC_UNO_DP_ISDATALAYOUT );
|
|
//TODO: error checking -- is "IsDataLayoutDimension" property required??
|
|
}
|
|
}
|
|
else
|
|
{
|
|
uno::Reference<container::XNamed> xDimName( xIntDim, uno::UNO_QUERY );
|
|
if (xDimName.is() && xDimName->getName() == aCoreName)
|
|
bFound = true;
|
|
}
|
|
|
|
if (bFound)
|
|
{
|
|
if (rxDim->GetDupFlag())
|
|
{
|
|
uno::Reference<util::XCloneable> xCloneable(xIntDim, uno::UNO_QUERY);
|
|
SAL_WARN_IF(!xCloneable.is(), "sc.core", "cannot clone dimension");
|
|
if (xCloneable.is())
|
|
{
|
|
uno::Reference<util::XCloneable> xNew = xCloneable->createClone();
|
|
uno::Reference<container::XNamed> xNewName(xNew, uno::UNO_QUERY);
|
|
if (xNewName.is())
|
|
{
|
|
xNewName->setName(aName);
|
|
rxDim->WriteToSource(xNew);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
rxDim->WriteToSource( xIntDim );
|
|
}
|
|
}
|
|
SAL_WARN_IF(!bFound, "sc.core", "WriteToSource: Dimension not found: " + aName + ".");
|
|
}
|
|
|
|
if ( xSourceProp.is() )
|
|
{
|
|
if (mnColumnGrandMode != SC_DPSAVEMODE_DONTKNOW)
|
|
lcl_SetBoolProperty(xSourceProp, SC_UNO_DP_COLGRAND, bool(mnColumnGrandMode));
|
|
if (mnRowGrandMode != SC_DPSAVEMODE_DONTKNOW)
|
|
lcl_SetBoolProperty(xSourceProp, SC_UNO_DP_ROWGRAND, bool(mnRowGrandMode));
|
|
}
|
|
}
|
|
catch(uno::Exception const &)
|
|
{
|
|
TOOLS_WARN_EXCEPTION("sc.core", "WriteToSource");
|
|
}
|
|
}
|
|
|
|
bool ScDPSaveData::IsEmpty() const
|
|
{
|
|
for (auto const& iter : m_DimList)
|
|
{
|
|
if (iter->GetOrientation() != sheet::DataPilotFieldOrientation_HIDDEN && !iter->IsDataLayout())
|
|
return false;
|
|
}
|
|
return true; // no entries that are not hidden
|
|
}
|
|
|
|
void ScDPSaveData::RemoveAllGroupDimensions( const OUString& rSrcDimName, std::vector<OUString>* pDeletedNames )
|
|
{
|
|
if (!mpDimensionData)
|
|
// No group dimensions exist. Nothing to do.
|
|
return;
|
|
|
|
// Remove numeric group dimension (exists once at most). No need to delete
|
|
// anything in save data (grouping was done inplace in an existing base
|
|
// dimension).
|
|
mpDimensionData->RemoveNumGroupDimension(rSrcDimName);
|
|
|
|
// Remove named group dimension(s). Dimensions have to be removed from
|
|
// dimension save data and from save data too.
|
|
const ScDPSaveGroupDimension* pExistingGroup = mpDimensionData->GetGroupDimForBase(rSrcDimName);
|
|
while ( pExistingGroup )
|
|
{
|
|
OUString aGroupDimName = pExistingGroup->GetGroupDimName();
|
|
mpDimensionData->RemoveGroupDimension(aGroupDimName); // pExistingGroup is deleted
|
|
|
|
// also remove SaveData settings for the dimension that no longer exists
|
|
RemoveDimensionByName(aGroupDimName);
|
|
|
|
if (pDeletedNames)
|
|
pDeletedNames->push_back(aGroupDimName);
|
|
|
|
// see if there are more group dimensions
|
|
pExistingGroup = mpDimensionData->GetGroupDimForBase(rSrcDimName);
|
|
|
|
if ( pExistingGroup && pExistingGroup->GetGroupDimName() == aGroupDimName )
|
|
{
|
|
// still get the same group dimension?
|
|
OSL_FAIL("couldn't remove group dimension");
|
|
pExistingGroup = nullptr; // avoid endless loop
|
|
}
|
|
}
|
|
}
|
|
|
|
ScDPDimensionSaveData* ScDPSaveData::GetDimensionData()
|
|
{
|
|
if (!mpDimensionData)
|
|
mpDimensionData.reset(new ScDPDimensionSaveData);
|
|
return mpDimensionData.get();
|
|
}
|
|
|
|
void ScDPSaveData::SetDimensionData(const ScDPDimensionSaveData* pNew)
|
|
{
|
|
if (pNew)
|
|
mpDimensionData.reset(new ScDPDimensionSaveData(*pNew));
|
|
else
|
|
mpDimensionData.reset();
|
|
}
|
|
|
|
void ScDPSaveData::BuildAllDimensionMembers(ScDPTableData* pData)
|
|
{
|
|
if (mbDimensionMembersBuilt)
|
|
return;
|
|
|
|
// First, build a dimension name-to-index map.
|
|
typedef std::unordered_map<OUString, tools::Long> NameIndexMap;
|
|
NameIndexMap aMap;
|
|
tools::Long nColCount = pData->GetColumnCount();
|
|
for (tools::Long i = 0; i < nColCount; ++i)
|
|
aMap.emplace(pData->getDimensionName(i), i);
|
|
|
|
NameIndexMap::const_iterator itrEnd = aMap.end();
|
|
|
|
for (auto const& iter : m_DimList)
|
|
{
|
|
const OUString& rDimName = iter->GetName();
|
|
if (rDimName.isEmpty())
|
|
// empty dimension name. It must be data layout.
|
|
continue;
|
|
|
|
NameIndexMap::const_iterator itr = aMap.find(rDimName);
|
|
if (itr == itrEnd)
|
|
// dimension name not in the data. This should never happen!
|
|
continue;
|
|
|
|
tools::Long nDimIndex = itr->second;
|
|
const std::vector<SCROW>& rMembers = pData->GetColumnEntries(nDimIndex);
|
|
size_t nMemberCount = rMembers.size();
|
|
for (size_t j = 0; j < nMemberCount; ++j)
|
|
{
|
|
const ScDPItemData* pMemberData = pData->GetMemberById( nDimIndex, rMembers[j] );
|
|
OUString aMemName = pData->GetFormattedString(nDimIndex, *pMemberData, false);
|
|
if (iter->GetExistingMemberByName(aMemName))
|
|
// this member instance already exists. nothing to do.
|
|
continue;
|
|
|
|
unique_ptr<ScDPSaveMember> pNewMember(new ScDPSaveMember(aMemName));
|
|
pNewMember->SetIsVisible(true);
|
|
iter->AddMember(std::move(pNewMember));
|
|
}
|
|
}
|
|
|
|
mbDimensionMembersBuilt = true;
|
|
}
|
|
|
|
void ScDPSaveData::SyncAllDimensionMembers(ScDPTableData* pData)
|
|
{
|
|
typedef std::unordered_map<OUString, tools::Long> NameIndexMap;
|
|
|
|
// First, build a dimension name-to-index map.
|
|
NameIndexMap aMap;
|
|
tools::Long nColCount = pData->GetColumnCount();
|
|
for (tools::Long i = 0; i < nColCount; ++i)
|
|
aMap.emplace(pData->getDimensionName(i), i);
|
|
|
|
NameIndexMap::const_iterator itMapEnd = aMap.end();
|
|
|
|
for (auto const& it : m_DimList)
|
|
{
|
|
const OUString& rDimName = it->GetName();
|
|
if (rDimName.isEmpty())
|
|
// empty dimension name. It must be data layout.
|
|
continue;
|
|
|
|
NameIndexMap::const_iterator itMap = aMap.find(rDimName);
|
|
if (itMap == itMapEnd)
|
|
// dimension name not in the data. This should never happen!
|
|
continue;
|
|
|
|
ScDPSaveDimension::MemberSetType aMemNames;
|
|
tools::Long nDimIndex = itMap->second;
|
|
const std::vector<SCROW>& rMembers = pData->GetColumnEntries(nDimIndex);
|
|
size_t nMemberCount = rMembers.size();
|
|
for (size_t j = 0; j < nMemberCount; ++j)
|
|
{
|
|
const ScDPItemData* pMemberData = pData->GetMemberById(nDimIndex, rMembers[j]);
|
|
// ScDPCache::GetItemDataById() (via
|
|
// ScDPTableData::GetMemberById(),
|
|
// ScDPGroupTableData::GetMemberById() through
|
|
// GetCacheTable().getCache()) may return nullptr.
|
|
if (pMemberData)
|
|
{
|
|
OUString aMemName = pData->GetFormattedString(nDimIndex, *pMemberData, false);
|
|
aMemNames.insert(aMemName);
|
|
}
|
|
else
|
|
{
|
|
SAL_WARN("sc.core", "No pMemberData for nDimIndex " << nDimIndex << ", rMembers[j] " << rMembers[j]
|
|
<< ", j " << j);
|
|
}
|
|
}
|
|
|
|
it->RemoveObsoleteMembers(aMemNames);
|
|
}
|
|
}
|
|
|
|
bool ScDPSaveData::HasInvisibleMember(std::u16string_view rDimName) const
|
|
{
|
|
ScDPSaveDimension* pDim = GetExistingDimensionByName(rDimName);
|
|
if (!pDim)
|
|
return false;
|
|
|
|
return pDim->HasInvisibleMember();
|
|
}
|
|
|
|
#if DUMP_PIVOT_TABLE
|
|
|
|
void ScDPSaveData::Dump() const
|
|
{
|
|
for (auto const& itDim : m_DimList)
|
|
{
|
|
const ScDPSaveDimension& rDim = *itDim;
|
|
rDim.Dump();
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
void ScDPSaveData::CheckDuplicateName(ScDPSaveDimension& rDim)
|
|
{
|
|
const OUString aName = ScDPUtil::getSourceDimensionName(rDim.GetName());
|
|
DupNameCountType::iterator it = maDupNameCounts.find(aName);
|
|
if (it != maDupNameCounts.end())
|
|
{
|
|
rDim.SetName(ScDPUtil::createDuplicateDimensionName(aName, ++it->second));
|
|
rDim.SetDupFlag(true);
|
|
}
|
|
else
|
|
// New name.
|
|
maDupNameCounts.emplace(aName, 0);
|
|
}
|
|
|
|
void ScDPSaveData::RemoveDuplicateNameCount(const OUString& rName)
|
|
{
|
|
OUString aCoreName = rName;
|
|
if (ScDPUtil::isDuplicateDimension(rName))
|
|
aCoreName = ScDPUtil::getSourceDimensionName(rName);
|
|
|
|
DupNameCountType::iterator it = maDupNameCounts.find(aCoreName);
|
|
if (it == maDupNameCounts.end())
|
|
return;
|
|
|
|
if (!it->second)
|
|
{
|
|
maDupNameCounts.erase(it);
|
|
return;
|
|
}
|
|
|
|
--it->second;
|
|
}
|
|
|
|
ScDPSaveDimension* ScDPSaveData::AppendNewDimension(const OUString& rName, bool bDataLayout)
|
|
{
|
|
if (ScDPUtil::isDuplicateDimension(rName))
|
|
// This call is for original dimensions only.
|
|
return nullptr;
|
|
|
|
ScDPSaveDimension* pNew = new ScDPSaveDimension(rName, bDataLayout);
|
|
m_DimList.push_back(std::unique_ptr<ScDPSaveDimension>(pNew));
|
|
if (!maDupNameCounts.count(rName))
|
|
maDupNameCounts.emplace(rName, 0);
|
|
|
|
DimensionsChanged();
|
|
return pNew;
|
|
}
|
|
|
|
void ScDPSaveData::DimensionsChanged()
|
|
{
|
|
mpDimOrder.reset();
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|