diff options
Diffstat (limited to '')
-rw-r--r-- | sc/source/ui/unoobj/dapiuno.cxx | 3356 |
1 files changed, 3356 insertions, 0 deletions
diff --git a/sc/source/ui/unoobj/dapiuno.cxx b/sc/source/ui/unoobj/dapiuno.cxx new file mode 100644 index 000000000..64111c1c3 --- /dev/null +++ b/sc/source/ui/unoobj/dapiuno.cxx @@ -0,0 +1,3356 @@ +/* -*- 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 <algorithm> +#include <cmath> + +#include <o3tl/safeint.hxx> +#include <svl/hint.hxx> +#include <vcl/svapp.hxx> +#include <sal/log.hxx> + +#include <dapiuno.hxx> +#include <datauno.hxx> +#include <miscuno.hxx> +#include <convuno.hxx> +#include <docsh.hxx> +#include <tabvwsh.hxx> +#include <rangeutl.hxx> +#include <dpobject.hxx> +#include <dpshttab.hxx> +#include <dpsdbtab.hxx> +#include <dpsave.hxx> +#include <dbdocfun.hxx> +#include <unonames.hxx> +#include <dpdimsave.hxx> +#include <hints.hxx> +#include <dputil.hxx> +#include <globstr.hrc> +#include <scresid.hxx> +#include <generalfunction.hxx> + +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/lang/NullPointerException.hpp> +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <com/sun/star/sheet/XDimensionsSupplier.hpp> +#include <com/sun/star/sheet/XLevelsSupplier.hpp> +#include <com/sun/star/sheet/XMembersAccess.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/sheet/DataImportMode.hpp> +#include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp> +#include <com/sun/star/sheet/DataPilotFieldFilter.hpp> +#include <com/sun/star/sheet/DataPilotOutputRangeType.hpp> +#include <com/sun/star/sheet/DataPilotTablePositionData.hpp> +#include <com/sun/star/sheet/GeneralFunction2.hpp> + +#include <comphelper/extract.hxx> +#include <comphelper/propertysequence.hxx> +#include <comphelper/sequence.hxx> +#include <comphelper/servicehelper.hxx> +#include <cppuhelper/exc_hlp.hxx> + +using namespace com::sun::star; +using namespace com::sun::star::sheet; + +using ::com::sun::star::uno::Any; +using ::com::sun::star::uno::Exception; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::RuntimeException; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::uno::UNO_QUERY_THROW; + +using ::com::sun::star::container::ElementExistException; +using ::com::sun::star::container::NoSuchElementException; +using ::com::sun::star::container::XEnumeration; +using ::com::sun::star::container::XIndexAccess; +using ::com::sun::star::container::XNameAccess; +using ::com::sun::star::container::XNamed; + +using ::com::sun::star::beans::UnknownPropertyException; +using ::com::sun::star::beans::XPropertyChangeListener; +using ::com::sun::star::beans::XPropertySet; +using ::com::sun::star::beans::XPropertySetInfo; +using ::com::sun::star::beans::XVetoableChangeListener; + +using ::com::sun::star::lang::IllegalArgumentException; +using ::com::sun::star::lang::IndexOutOfBoundsException; +using ::com::sun::star::lang::NullPointerException; + +using ::com::sun::star::table::CellAddress; +using ::com::sun::star::table::CellRangeAddress; + +namespace { + +const SfxItemPropertyMapEntry* lcl_GetDataPilotDescriptorBaseMap() +{ + static const SfxItemPropertyMapEntry aDataPilotDescriptorBaseMap_Impl[] = + { + { SC_UNO_DP_COLGRAND, 0, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNO_DP_DRILLDOWN, 0, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNO_DP_GRANDTOTAL_NAME,0,cppu::UnoType<OUString>::get(), beans::PropertyAttribute::MAYBEVOID, 0 }, + { SC_UNO_DP_IGNORE_EMPTYROWS, 0, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNO_DP_IMPORTDESC, 0, cppu::UnoType<uno::Sequence<beans::PropertyValue>>::get(), 0, 0 }, + { SC_UNO_DP_REPEATEMPTY, 0, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNO_DP_ROWGRAND, 0, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNO_DP_SERVICEARG, 0, cppu::UnoType<uno::Sequence<beans::PropertyValue>>::get(), 0, 0 }, + { SC_UNO_DP_SHOWFILTER, 0, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNO_DP_SOURCESERVICE, 0, cppu::UnoType<OUString>::get(), 0, 0 }, + { u"", 0, css::uno::Type(), 0, 0 } + }; + return aDataPilotDescriptorBaseMap_Impl; +} + +const SfxItemPropertyMapEntry* lcl_GetDataPilotFieldMap() +{ + using namespace ::com::sun::star::beans::PropertyAttribute; + static const SfxItemPropertyMapEntry aDataPilotFieldMap_Impl[] = + { + { SC_UNONAME_AUTOSHOW, 0, cppu::UnoType<DataPilotFieldAutoShowInfo>::get(), MAYBEVOID, 0 }, + { SC_UNONAME_FUNCTION, 0, cppu::UnoType<GeneralFunction>::get(), 0, 0 }, + { SC_UNONAME_FUNCTION2, 0, cppu::UnoType<sal_Int16>::get(), 0, 0 }, + { SC_UNONAME_GROUPINFO, 0, cppu::UnoType<DataPilotFieldGroupInfo>::get(), MAYBEVOID, 0 }, + { SC_UNONAME_HASAUTOSHOW, 0, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_HASLAYOUTINFO,0, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_HASREFERENCE, 0, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_HASSORTINFO, 0, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_ISGROUP, 0, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_LAYOUTINFO, 0, cppu::UnoType<DataPilotFieldLayoutInfo>::get(), MAYBEVOID, 0 }, + { SC_UNONAME_ORIENT, 0, cppu::UnoType<DataPilotFieldOrientation>::get(), MAYBEVOID, 0 }, + { SC_UNONAME_REFERENCE, 0, cppu::UnoType<DataPilotFieldReference>::get(), MAYBEVOID, 0 }, + { SC_UNONAME_SELPAGE, 0, cppu::UnoType<OUString>::get(), 0, 0 }, + { SC_UNONAME_SHOWEMPTY, 0, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_REPEATITEMLABELS, 0, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_SORTINFO, 0, cppu::UnoType<DataPilotFieldSortInfo>::get(), MAYBEVOID, 0 }, + { SC_UNONAME_SUBTOTALS, 0, cppu::UnoType<Sequence<GeneralFunction>>::get(), 0, 0 }, + { SC_UNONAME_SUBTOTALS2, 0, cppu::UnoType<Sequence<sal_Int16>>::get(), 0, 0 }, + { SC_UNONAME_USESELPAGE, 0, cppu::UnoType<bool>::get(), 0, 0 }, + { u"", 0, css::uno::Type(), 0, 0 } + }; + return aDataPilotFieldMap_Impl; +} + +const SfxItemPropertyMapEntry* lcl_GetDataPilotItemMap() +{ + static const SfxItemPropertyMapEntry aDataPilotItemMap_Impl[] = + { + { SC_UNONAME_ISHIDDEN, 0, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_POS, 0, cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { SC_UNONAME_SHOWDETAIL, 0, cppu::UnoType<bool>::get(), 0, 0 }, + { u"", 0, css::uno::Type(), 0, 0 } + }; + return aDataPilotItemMap_Impl; +} + +bool lclCheckValidDouble( double fValue, bool bAuto ) +{ + return bAuto || std::isfinite( fValue ); +} + +bool lclCheckMinMaxStep( const DataPilotFieldGroupInfo& rInfo ) +{ + return + lclCheckValidDouble( rInfo.Start, rInfo.HasAutoStart ) && + lclCheckValidDouble( rInfo.End, rInfo.HasAutoEnd ) && + (rInfo.HasAutoStart || rInfo.HasAutoEnd || (rInfo.Start <= rInfo.End)) && + lclCheckValidDouble( rInfo.Step, false ) && + (0.0 <= rInfo.Step); +} + +} // namespace + +SC_SIMPLE_SERVICE_INFO( ScDataPilotDescriptor, "ScDataPilotDescriptor", "stardiv::one::sheet::DataPilotDescriptor" ) +SC_SIMPLE_SERVICE_INFO( ScDataPilotFieldObj, "ScDataPilotFieldObj", "com.sun.star.sheet.DataPilotField" ) +SC_SIMPLE_SERVICE_INFO( ScDataPilotFieldsObj, "ScDataPilotFieldsObj", "com.sun.star.sheet.DataPilotFields" ) +SC_SIMPLE_SERVICE_INFO( ScDataPilotTableObj, "ScDataPilotTableObj", "com.sun.star.sheet.DataPilotTable" ) +SC_SIMPLE_SERVICE_INFO( ScDataPilotTablesObj, "ScDataPilotTablesObj", "com.sun.star.sheet.DataPilotTables" ) +SC_SIMPLE_SERVICE_INFO( ScDataPilotItemsObj, "ScDataPilotItemsObj", "com.sun.star.sheet.DataPilotItems" ) +SC_SIMPLE_SERVICE_INFO( ScDataPilotItemObj, "ScDataPilotItemObj", "com.sun.star.sheet.DataPilotItem" ) + +SC_SIMPLE_SERVICE_INFO( ScDataPilotFieldGroupsObj, "ScDataPilotFieldGroupsObj", "com.sun.star.sheet.DataPilotFieldGroups" ) +SC_SIMPLE_SERVICE_INFO( ScDataPilotFieldGroupObj, "ScDataPilotFieldGroupObj", "com.sun.star.sheet.DataPilotFieldGroup" ) +SC_SIMPLE_SERVICE_INFO( ScDataPilotFieldGroupItemObj, "ScDataPilotFieldGroupItemObj", "com.sun.star.sheet.DataPilotFieldGroupItem" ) + +// name that is used in the API for the data layout field +constexpr OUStringLiteral SC_DATALAYOUT_NAME = u"Data"; + +ScGeneralFunction ScDataPilotConversion::FirstFunc( PivotFunc nBits ) +{ + if ( nBits & PivotFunc::Sum ) return ScGeneralFunction::SUM; + if ( nBits & PivotFunc::Count ) return ScGeneralFunction::COUNT; + if ( nBits & PivotFunc::Average ) return ScGeneralFunction::AVERAGE; + if ( nBits & PivotFunc::Median ) return ScGeneralFunction::MEDIAN; + if ( nBits & PivotFunc::Max ) return ScGeneralFunction::MAX; + if ( nBits & PivotFunc::Min ) return ScGeneralFunction::MIN; + if ( nBits & PivotFunc::Product ) return ScGeneralFunction::PRODUCT; + if ( nBits & PivotFunc::CountNum ) return ScGeneralFunction::COUNTNUMS; + if ( nBits & PivotFunc::StdDev ) return ScGeneralFunction::STDEV; + if ( nBits & PivotFunc::StdDevP ) return ScGeneralFunction::STDEVP; + if ( nBits & PivotFunc::StdVar ) return ScGeneralFunction::VAR; + if ( nBits & PivotFunc::StdVarP ) return ScGeneralFunction::VARP; + if ( nBits & PivotFunc::Auto ) return ScGeneralFunction::AUTO; + return ScGeneralFunction::NONE; +} + +PivotFunc ScDataPilotConversion::FunctionBit( sal_Int16 eFunc ) +{ + PivotFunc nRet = PivotFunc::NONE; // 0 + switch (eFunc) + { + case GeneralFunction2::SUM: nRet = PivotFunc::Sum; break; + case GeneralFunction2::COUNT: nRet = PivotFunc::Count; break; + case GeneralFunction2::AVERAGE: nRet = PivotFunc::Average; break; + case GeneralFunction2::MEDIAN: nRet = PivotFunc::Median; break; + case GeneralFunction2::MAX: nRet = PivotFunc::Max; break; + case GeneralFunction2::MIN: nRet = PivotFunc::Min; break; + case GeneralFunction2::PRODUCT: nRet = PivotFunc::Product; break; + case GeneralFunction2::COUNTNUMS: nRet = PivotFunc::CountNum; break; + case GeneralFunction2::STDEV: nRet = PivotFunc::StdDev; break; + case GeneralFunction2::STDEVP: nRet = PivotFunc::StdDevP; break; + case GeneralFunction2::VAR: nRet = PivotFunc::StdVar; break; + case GeneralFunction2::VARP: nRet = PivotFunc::StdVarP; break; + case GeneralFunction2::AUTO: nRet = PivotFunc::Auto; break; + default: + { + assert(false); + } + } + return nRet; +} + +void ScDataPilotConversion::FillGroupInfo( DataPilotFieldGroupInfo& rInfo, const ScDPNumGroupInfo& rGroupInfo ) +{ + rInfo.HasDateValues = rGroupInfo.mbDateValues; + rInfo.HasAutoStart = rGroupInfo.mbAutoStart; + rInfo.Start = rGroupInfo.mfStart; + rInfo.HasAutoEnd = rGroupInfo.mbAutoEnd; + rInfo.End = rGroupInfo.mfEnd; + rInfo.Step = rGroupInfo.mfStep; +} + +static ScDPObject* lcl_GetDPObject( ScDocShell* pDocShell, SCTAB nTab, std::u16string_view rName ) +{ + if (pDocShell) + { + ScDocument& rDoc = pDocShell->GetDocument(); + ScDPCollection* pColl = rDoc.GetDPCollection(); + if ( pColl ) + { + size_t nCount = pColl->GetCount(); + for (size_t i=0; i<nCount; ++i) + { + ScDPObject& rDPObj = (*pColl)[i]; + if ( rDPObj.GetOutRange().aStart.Tab() == nTab && + rDPObj.GetName() == rName ) + return &rDPObj; + } + } + } + return nullptr; // not found +} + +static OUString lcl_CreatePivotName( ScDocShell* pDocShell ) +{ + if (pDocShell) + { + ScDocument& rDoc = pDocShell->GetDocument(); + ScDPCollection* pColl = rDoc.GetDPCollection(); + if ( pColl ) + return pColl->CreateNewName(); + } + return OUString(); // shouldn't happen +} + +static sal_Int32 lcl_GetObjectIndex( ScDPObject* pDPObj, const ScFieldIdentifier& rFieldId ) +{ + // used for items - nRepeat in identifier can be ignored + if ( pDPObj ) + { + sal_Int32 nCount = pDPObj->GetDimCount(); + for ( sal_Int32 nDim = 0; nDim < nCount; ++nDim ) + { + bool bIsDataLayout = false; + OUString aDimName( pDPObj->GetDimName( nDim, bIsDataLayout ) ); + if ( rFieldId.mbDataLayout ? bIsDataLayout : (aDimName == rFieldId.maFieldName) ) + return nDim; + } + } + return -1; // none +} + +ScDataPilotTablesObj::ScDataPilotTablesObj(ScDocShell& rDocSh, SCTAB nT) : + pDocShell( &rDocSh ), + nTab( nT ) +{ + pDocShell->GetDocument().AddUnoObject(*this); +} + +ScDataPilotTablesObj::~ScDataPilotTablesObj() +{ + SolarMutexGuard g; + + if (pDocShell) + pDocShell->GetDocument().RemoveUnoObject(*this); +} + +void ScDataPilotTablesObj::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + //! update of references + + if ( rHint.GetId() == SfxHintId::Dying ) + { + pDocShell = nullptr; // became invalid + } +} + +// XDataPilotTables + +rtl::Reference<ScDataPilotTableObj> ScDataPilotTablesObj::GetObjectByIndex_Impl( sal_Int32 nIndex ) +{ + if (pDocShell) + { + ScDocument& rDoc = pDocShell->GetDocument(); + ScDPCollection* pColl = rDoc.GetDPCollection(); + if ( pColl ) + { + // count tables on this sheet + sal_Int32 nFound = 0; + size_t nCount = pColl->GetCount(); + for (size_t i=0; i<nCount; ++i) + { + ScDPObject& rDPObj = (*pColl)[i]; + if ( rDPObj.GetOutRange().aStart.Tab() == nTab ) + { + if ( nFound == nIndex ) + { + return new ScDataPilotTableObj(*pDocShell, nTab, rDPObj.GetName()); + } + ++nFound; + } + } + } + } + return nullptr; +} + +rtl::Reference<ScDataPilotTableObj> ScDataPilotTablesObj::GetObjectByName_Impl(const OUString& rName) +{ + if (hasByName(rName)) + return new ScDataPilotTableObj(*pDocShell, nTab, rName); + return nullptr; +} + +Reference<XDataPilotDescriptor> SAL_CALL ScDataPilotTablesObj::createDataPilotDescriptor() +{ + SolarMutexGuard aGuard; + if (pDocShell) + return new ScDataPilotDescriptor(*pDocShell); + return nullptr; +} + +static bool lcl_IsDuplicated(const Reference<XPropertySet>& rDimProps) +{ + try + { + Any aAny = rDimProps->getPropertyValue( SC_UNO_DP_ORIGINAL ); + Reference< XNamed > xOriginal( aAny, UNO_QUERY ); + return xOriginal.is(); + } + catch( Exception& ) + { + } + return false; +} + +static OUString lcl_GetOriginalName(const Reference< XNamed >& rDim) +{ + Reference< XNamed > xOriginal; + + Reference< XPropertySet > xDimProps(rDim, UNO_QUERY); + if ( xDimProps.is() ) + { + try + { + Any aAny = xDimProps->getPropertyValue(SC_UNO_DP_ORIGINAL); + aAny >>= xOriginal; + } + catch( Exception& ) + { + } + } + + if ( !xOriginal.is() ) + xOriginal = rDim; + + return xOriginal->getName(); +} + +void SAL_CALL ScDataPilotTablesObj::insertNewByName( const OUString& aNewName, + const CellAddress& aOutputAddress, + const Reference<XDataPilotDescriptor>& xDescriptor ) +{ + SolarMutexGuard aGuard; + if (!xDescriptor.is()) return; + + if ( !aNewName.isEmpty() && hasByName( aNewName ) ) + throw IllegalArgumentException("Name \"" + aNewName + "\" already exists", static_cast<cppu::OWeakObject*>(this), 0); + + if (!pDocShell) + throw RuntimeException("DocShell is null", static_cast<cppu::OWeakObject*>(this)); + + auto pImp = comphelper::getFromUnoTunnel<ScDataPilotDescriptorBase>( xDescriptor ); + if (!pImp) + throw RuntimeException("Failed to get ScDataPilotDescriptor", static_cast<cppu::OWeakObject*>(this)); + + ScDPObject* pNewObj = pImp->GetDPObject(); + if (!pNewObj) + throw RuntimeException("Failed to get DPObject", static_cast<cppu::OWeakObject*>(this)); + + ScRange aOutputRange(static_cast<SCCOL>(aOutputAddress.Column), static_cast<SCROW>(aOutputAddress.Row), static_cast<SCTAB>(aOutputAddress.Sheet), + static_cast<SCCOL>(aOutputAddress.Column), static_cast<SCROW>(aOutputAddress.Row), static_cast<SCTAB>(aOutputAddress.Sheet)); + pNewObj->SetOutRange(aOutputRange); + OUString aName = aNewName; + if (aName.isEmpty()) + aName = lcl_CreatePivotName( pDocShell ); + pNewObj->SetName(aName); + OUString aTag = xDescriptor->getTag(); + pNewObj->SetTag(aTag); + + // todo: handle double fields (for more information see ScDPObject) + + ScDBDocFunc aFunc(*pDocShell); + if (!aFunc.CreatePivotTable(*pNewObj, true, true)) + throw RuntimeException("Failed to create pivot table", static_cast<cppu::OWeakObject*>(this)); +} + +void SAL_CALL ScDataPilotTablesObj::removeByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + ScDPObject* pDPObj = lcl_GetDPObject( pDocShell, nTab, aName ); + if (!pDPObj || !pDocShell) + throw RuntimeException(); // no other exceptions specified + + ScDBDocFunc aFunc(*pDocShell); + aFunc.RemovePivotTable(*pDPObj, true, true); // remove - incl. undo etc. + +} + +// XEnumerationAccess + +Reference< XEnumeration > SAL_CALL ScDataPilotTablesObj::createEnumeration() +{ + SolarMutexGuard aGuard; + return new ScIndexEnumeration(this, "com.sun.star.sheet.DataPilotTablesEnumeration"); +} + +// XIndexAccess + +sal_Int32 SAL_CALL ScDataPilotTablesObj::getCount() +{ + SolarMutexGuard aGuard; + if ( pDocShell ) + { + ScDocument& rDoc = pDocShell->GetDocument(); + ScDPCollection* pColl = rDoc.GetDPCollection(); + if ( pColl ) + { + // count tables on this sheet + + sal_uInt16 nFound = 0; + size_t nCount = pColl->GetCount(); + for (size_t i=0; i<nCount; ++i) + { + ScDPObject& rDPObj = (*pColl)[i]; + if ( rDPObj.GetOutRange().aStart.Tab() == nTab ) + ++nFound; + } + return nFound; + } + } + + return 0; +} + +Any SAL_CALL ScDataPilotTablesObj::getByIndex( sal_Int32 nIndex ) +{ + SolarMutexGuard aGuard; + Reference<XDataPilotTable2> xTable(GetObjectByIndex_Impl(nIndex)); + if (!xTable.is()) + throw IndexOutOfBoundsException(); + return Any( xTable ); +} + +uno::Type SAL_CALL ScDataPilotTablesObj::getElementType() +{ + return cppu::UnoType<XDataPilotTable2>::get(); +} + +sal_Bool SAL_CALL ScDataPilotTablesObj::hasElements() +{ + SolarMutexGuard aGuard; + return ( getCount() != 0 ); +} + +// XNameAccess + +Any SAL_CALL ScDataPilotTablesObj::getByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + Reference<XDataPilotTable2> xTable(GetObjectByName_Impl(aName)); + if (!xTable.is()) + throw NoSuchElementException(); + return Any( xTable ); +} + +Sequence<OUString> SAL_CALL ScDataPilotTablesObj::getElementNames() +{ + SolarMutexGuard aGuard; + if (pDocShell) + { + ScDocument& rDoc = pDocShell->GetDocument(); + ScDPCollection* pColl = rDoc.GetDPCollection(); + if ( pColl ) + { + // count tables on this sheet + + sal_uInt16 nFound = 0; + size_t nCount = pColl->GetCount(); + size_t i; + for (i=0; i<nCount; ++i) + { + ScDPObject& rDPObj = (*pColl)[i]; + if ( rDPObj.GetOutRange().aStart.Tab() == nTab ) + ++nFound; + } + + sal_uInt16 nPos = 0; + Sequence<OUString> aSeq(nFound); + OUString* pAry = aSeq.getArray(); + for (i=0; i<nCount; ++i) + { + ScDPObject& rDPObj = (*pColl)[i]; + if ( rDPObj.GetOutRange().aStart.Tab() == nTab ) + pAry[nPos++] = rDPObj.GetName(); + } + + return aSeq; + } + } + return {}; +} + +sal_Bool SAL_CALL ScDataPilotTablesObj::hasByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + if (pDocShell) + { + ScDocument& rDoc = pDocShell->GetDocument(); + ScDPCollection* pColl = rDoc.GetDPCollection(); + if ( pColl ) + { + size_t nCount = pColl->GetCount(); + for (size_t i=0; i<nCount; ++i) + { + ScDPObject& rDPObj = (*pColl)[i]; + if ( rDPObj.GetOutRange().aStart.Tab() == nTab && + rDPObj.GetName() == aName ) + return true; + } + } + } + return false; +} + +ScDataPilotDescriptorBase::ScDataPilotDescriptorBase(ScDocShell& rDocSh) : + maPropSet( lcl_GetDataPilotDescriptorBaseMap() ), + pDocShell( &rDocSh ) +{ + pDocShell->GetDocument().AddUnoObject(*this); +} + +ScDataPilotDescriptorBase::~ScDataPilotDescriptorBase() +{ + SolarMutexGuard g; + + if (pDocShell) + pDocShell->GetDocument().RemoveUnoObject(*this); +} + +void ScDataPilotDescriptorBase::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + //! update of references ? + + if ( rHint.GetId() == SfxHintId::Dying ) + { + pDocShell = nullptr; // became invalid + } +} + +// XDataPilotDescriptor + +CellRangeAddress SAL_CALL ScDataPilotDescriptorBase::getSourceRange() +{ + SolarMutexGuard aGuard; + + ScDPObject* pDPObject(GetDPObject()); + if (!pDPObject) + throw RuntimeException("Failed to get DPObject", static_cast<cppu::OWeakObject*>(this)); + + CellRangeAddress aRet; + if (pDPObject->IsSheetData()) + ScUnoConversion::FillApiRange( aRet, pDPObject->GetSheetDesc()->GetSourceRange() ); + return aRet; +} + +void SAL_CALL ScDataPilotDescriptorBase::setSourceRange( const CellRangeAddress& aSourceRange ) +{ + SolarMutexGuard aGuard; + + ScDPObject* pDPObject = GetDPObject(); + if (!pDPObject) + throw RuntimeException("Failed to get DPObject", static_cast<cppu::OWeakObject*>(this)); + + ScSheetSourceDesc aSheetDesc(&pDocShell->GetDocument()); + if (pDPObject->IsSheetData()) + aSheetDesc = *pDPObject->GetSheetDesc(); + + ScRange aRange; + ScUnoConversion::FillScRange(aRange, aSourceRange); + aSheetDesc.SetSourceRange(aRange); + pDPObject->SetSheetDesc( aSheetDesc ); + SetDPObject( pDPObject ); +} + +Reference<XSheetFilterDescriptor> SAL_CALL ScDataPilotDescriptorBase::getFilterDescriptor() +{ + SolarMutexGuard aGuard; + return new ScDataPilotFilterDescriptor( pDocShell, this ); +} + +Reference<XIndexAccess> SAL_CALL ScDataPilotDescriptorBase::getDataPilotFields() +{ + SolarMutexGuard aGuard; + return new ScDataPilotFieldsObj( *this ); +} + +Reference<XIndexAccess> SAL_CALL ScDataPilotDescriptorBase::getColumnFields() +{ + SolarMutexGuard aGuard; + return new ScDataPilotFieldsObj( *this, DataPilotFieldOrientation_COLUMN ); +} + +Reference<XIndexAccess> SAL_CALL ScDataPilotDescriptorBase::getRowFields() +{ + SolarMutexGuard aGuard; + return new ScDataPilotFieldsObj( *this, DataPilotFieldOrientation_ROW ); +} + +Reference<XIndexAccess> SAL_CALL ScDataPilotDescriptorBase::getPageFields() +{ + SolarMutexGuard aGuard; + return new ScDataPilotFieldsObj( *this, DataPilotFieldOrientation_PAGE ); +} + +Reference<XIndexAccess> SAL_CALL ScDataPilotDescriptorBase::getDataFields() +{ + SolarMutexGuard aGuard; + return new ScDataPilotFieldsObj( *this, DataPilotFieldOrientation_DATA ); +} + +Reference<XIndexAccess> SAL_CALL ScDataPilotDescriptorBase::getHiddenFields() +{ + SolarMutexGuard aGuard; + return new ScDataPilotFieldsObj( *this, DataPilotFieldOrientation_HIDDEN ); +} + +// XPropertySet +Reference< XPropertySetInfo > SAL_CALL ScDataPilotDescriptorBase::getPropertySetInfo( ) +{ + SolarMutexGuard aGuard; + static Reference<XPropertySetInfo> aRef = + new SfxItemPropertySetInfo( maPropSet.getPropertyMap() ); + return aRef; +} + +void SAL_CALL ScDataPilotDescriptorBase::setPropertyValue( const OUString& aPropertyName, const Any& aValue ) +{ + SolarMutexGuard aGuard; + ScDPObject* pDPObject = GetDPObject(); + if (!pDPObject) + return; + + ScDPSaveData* pOldData = pDPObject->GetSaveData(); + OSL_ENSURE(pOldData, "Here should be a SaveData"); + if ( pOldData ) + { + ScDPSaveData aNewData( *pOldData ); + + if ( aPropertyName == SC_UNO_DP_COLGRAND ) + { + aNewData.SetColumnGrand(::cppu::any2bool( aValue )); + } + else if ( aPropertyName == SC_UNO_DP_IGNORE_EMPTYROWS ) + { + aNewData.SetIgnoreEmptyRows(::cppu::any2bool( aValue )); + } + else if ( aPropertyName == SC_UNO_DP_REPEATEMPTY ) + { + aNewData.SetRepeatIfEmpty(::cppu::any2bool( aValue )); + } + else if ( aPropertyName == SC_UNO_DP_ROWGRAND ) + { + aNewData.SetRowGrand(::cppu::any2bool( aValue )); + } + else if ( aPropertyName == SC_UNO_DP_SHOWFILTER ) + { + aNewData.SetFilterButton(::cppu::any2bool( aValue )); + } + else if ( aPropertyName == SC_UNO_DP_DRILLDOWN ) + { + aNewData.SetDrillDown(::cppu::any2bool( aValue )); + } + else if ( aPropertyName == SC_UNO_DP_GRANDTOTAL_NAME ) + { + OUString aStrVal; + if ( aValue >>= aStrVal ) + aNewData.SetGrandTotalName(aStrVal); + } + else if ( aPropertyName == SC_UNO_DP_IMPORTDESC ) + { + uno::Sequence<beans::PropertyValue> aArgSeq; + if ( aValue >>= aArgSeq ) + { + ScImportSourceDesc aImportDesc(&pDocShell->GetDocument()); + + const ScImportSourceDesc* pOldDesc = pDPObject->GetImportSourceDesc(); + if (pOldDesc) + aImportDesc = *pOldDesc; + + ScImportParam aParam; + ScImportDescriptor::FillImportParam( aParam, aArgSeq ); + + sheet::DataImportMode nNewType = sheet::DataImportMode_NONE; + if ( aParam.bImport ) + { + if ( aParam.bSql ) + nNewType = sheet::DataImportMode_SQL; + else if ( aParam.nType == ScDbQuery ) + nNewType = sheet::DataImportMode_QUERY; + else + nNewType = sheet::DataImportMode_TABLE; + } + aImportDesc.nType = nNewType; + aImportDesc.aDBName = aParam.aDBName; + aImportDesc.aObject = aParam.aStatement; + aImportDesc.bNative = aParam.bNative; + + pDPObject->SetImportDesc( aImportDesc ); + } + } + else if ( aPropertyName == SC_UNO_DP_SOURCESERVICE ) + { + OUString aStrVal; + if ( aValue >>= aStrVal ) + { + ScDPServiceDesc aServiceDesc("", "", "", "", ""); + + const ScDPServiceDesc* pOldDesc = pDPObject->GetDPServiceDesc(); + if (pOldDesc) + aServiceDesc = *pOldDesc; + + aServiceDesc.aServiceName = aStrVal; + + pDPObject->SetServiceData( aServiceDesc ); + } + } + else if ( aPropertyName == SC_UNO_DP_SERVICEARG ) + { + uno::Sequence<beans::PropertyValue> aArgSeq; + if ( aValue >>= aArgSeq ) + { + ScDPServiceDesc aServiceDesc("", "", "", "", ""); + + const ScDPServiceDesc* pOldDesc = pDPObject->GetDPServiceDesc(); + if (pOldDesc) + aServiceDesc = *pOldDesc; + + OUString aStrVal; + for (const beans::PropertyValue& rProp : std::as_const(aArgSeq)) + { + OUString aPropName(rProp.Name); + + if (aPropName == SC_UNO_DP_SOURCENAME) + { + if ( rProp.Value >>= aStrVal ) + aServiceDesc.aParSource = aStrVal; + } + else if (aPropName == SC_UNO_DP_OBJECTNAME) + { + if ( rProp.Value >>= aStrVal ) + aServiceDesc.aParName = aStrVal; + } + else if (aPropName == SC_UNO_DP_USERNAME) + { + if ( rProp.Value >>= aStrVal ) + aServiceDesc.aParUser = aStrVal; + } + else if (aPropName == SC_UNO_DP_PASSWORD) + { + if ( rProp.Value >>= aStrVal ) + aServiceDesc.aParPass = aStrVal; + } + } + + pDPObject->SetServiceData( aServiceDesc ); + } + } + else + throw UnknownPropertyException(aPropertyName); + + pDPObject->SetSaveData( aNewData ); + } + + SetDPObject(pDPObject); +} + +Any SAL_CALL ScDataPilotDescriptorBase::getPropertyValue( const OUString& aPropertyName ) +{ + SolarMutexGuard aGuard; + Any aRet; + + ScDPObject* pDPObject(GetDPObject()); + if (pDPObject) + { + ScDPSaveData* pOldData = pDPObject->GetSaveData(); + OSL_ENSURE(pOldData, "Here should be a SaveData"); + if ( pOldData ) + { + ScDPSaveData aNewData( *pOldData ); + + if ( aPropertyName == SC_UNO_DP_COLGRAND ) + { + aRet <<= aNewData.GetColumnGrand(); + } + else if ( aPropertyName == SC_UNO_DP_IGNORE_EMPTYROWS ) + { + aRet <<= aNewData.GetIgnoreEmptyRows(); + } + else if ( aPropertyName == SC_UNO_DP_REPEATEMPTY ) + { + aRet <<= aNewData.GetRepeatIfEmpty(); + } + else if ( aPropertyName == SC_UNO_DP_ROWGRAND ) + { + aRet <<= aNewData.GetRowGrand(); + } + else if ( aPropertyName == SC_UNO_DP_SHOWFILTER ) + { + aRet <<= aNewData.GetFilterButton(); + } + else if ( aPropertyName == SC_UNO_DP_DRILLDOWN ) + { + aRet <<= aNewData.GetDrillDown(); + } + else if ( aPropertyName == SC_UNO_DP_GRANDTOTAL_NAME ) + { + const std::optional<OUString> & pGrandTotalName = aNewData.GetGrandTotalName(); + if (pGrandTotalName) + aRet <<= *pGrandTotalName; // same behavior as in ScDPSource + } + else if ( aPropertyName == SC_UNO_DP_IMPORTDESC ) + { + const ScImportSourceDesc* pImportDesc = pDPObject->GetImportSourceDesc(); + if ( pImportDesc ) + { + // fill ScImportParam so ScImportDescriptor::FillProperties can be used + ScImportParam aParam; + aParam.bImport = ( pImportDesc->nType != sheet::DataImportMode_NONE ); + aParam.aDBName = pImportDesc->aDBName; + aParam.aStatement = pImportDesc->aObject; + aParam.bNative = pImportDesc->bNative; + aParam.bSql = ( pImportDesc->nType == sheet::DataImportMode_SQL ); + aParam.nType = static_cast<sal_uInt8>(( pImportDesc->nType == sheet::DataImportMode_QUERY ) ? ScDbQuery : ScDbTable); + + uno::Sequence<beans::PropertyValue> aSeq( ScImportDescriptor::GetPropertyCount() ); + ScImportDescriptor::FillProperties( aSeq, aParam ); + aRet <<= aSeq; + } + else + { + // empty sequence + uno::Sequence<beans::PropertyValue> aEmpty(0); + aRet <<= aEmpty; + } + } + else if ( aPropertyName == SC_UNO_DP_SOURCESERVICE ) + { + OUString aServiceName; + const ScDPServiceDesc* pServiceDesc = pDPObject->GetDPServiceDesc(); + if (pServiceDesc) + aServiceName = pServiceDesc->aServiceName; + aRet <<= aServiceName; // empty string if no ServiceDesc set + } + else if ( aPropertyName == SC_UNO_DP_SERVICEARG ) + { + const ScDPServiceDesc* pServiceDesc = pDPObject->GetDPServiceDesc(); + if (pServiceDesc) + { + uno::Sequence<beans::PropertyValue> aSeq( comphelper::InitPropertySequence({ + { SC_UNO_DP_SOURCENAME, Any(pServiceDesc->aParSource) }, + { SC_UNO_DP_OBJECTNAME, Any(pServiceDesc->aParName) }, + { SC_UNO_DP_USERNAME, Any(pServiceDesc->aParUser) }, + { SC_UNO_DP_PASSWORD, Any(pServiceDesc->aParPass) } + })); + aRet <<= aSeq; + } + else + { + // empty sequence + uno::Sequence<beans::PropertyValue> aEmpty; + aRet <<= aEmpty; + } + } + else + throw UnknownPropertyException(aPropertyName); + } + } + + return aRet; +} + +void SAL_CALL ScDataPilotDescriptorBase::addPropertyChangeListener( + const OUString& /* aPropertyName */, const Reference<XPropertyChangeListener >& /* xListener */ ) +{ +} + +void SAL_CALL ScDataPilotDescriptorBase::removePropertyChangeListener( + const OUString& /* aPropertyName */, const Reference<XPropertyChangeListener >& /* aListener */ ) +{ +} + +void SAL_CALL ScDataPilotDescriptorBase::addVetoableChangeListener( + const OUString& /* PropertyName */, const Reference<XVetoableChangeListener >& /* aListener */ ) +{ +} + +void SAL_CALL ScDataPilotDescriptorBase::removeVetoableChangeListener( + const OUString& /* PropertyName */, const Reference<XVetoableChangeListener >& /* aListener */ ) +{ +} + +// XDataPilotDataLayoutFieldSupplier + +Reference< XDataPilotField > SAL_CALL ScDataPilotDescriptorBase::getDataLayoutField() +{ + SolarMutexGuard aGuard; + if( ScDPObject* pDPObject = GetDPObject() ) + { + if( ScDPSaveData* pSaveData = pDPObject->GetSaveData() ) + { + if( pSaveData->GetDataLayoutDimension() ) + { + ScFieldIdentifier aFieldId( SC_DATALAYOUT_NAME, true ); + return new ScDataPilotFieldObj( *this, aFieldId ); + } + } + } + return nullptr; +} + +// XUnoTunnel + +sal_Int64 SAL_CALL ScDataPilotDescriptorBase::getSomething( + const Sequence<sal_Int8 >& rId ) +{ + return comphelper::getSomethingImpl(rId, this); +} + +const Sequence<sal_Int8>& ScDataPilotDescriptorBase::getUnoTunnelId() +{ + static const comphelper::UnoIdInit theScDataPilotDescriptorBaseUnoTunnelId; + return theScDataPilotDescriptorBaseUnoTunnelId.getSeq(); +} + +ScDataPilotTableObj::ScDataPilotTableObj(ScDocShell& rDocSh, SCTAB nT, const OUString& rN) : + ScDataPilotDescriptorBase( rDocSh ), + nTab( nT ), + aName( rN ), + aModifyListeners( 0 ) +{ +} + +ScDataPilotTableObj::~ScDataPilotTableObj() +{ +} + +Any SAL_CALL ScDataPilotTableObj::queryInterface( const uno::Type& rType ) +{ + // since we manually do resolve the query for XDataPilotTable2 + // we also need to do the same for XDataPilotTable + SC_QUERYINTERFACE( XDataPilotTable ) + SC_QUERYINTERFACE( XDataPilotTable2 ) + SC_QUERYINTERFACE( XModifyBroadcaster ) + + return ScDataPilotDescriptorBase::queryInterface( rType ); +} + +void SAL_CALL ScDataPilotTableObj::acquire() noexcept +{ + ScDataPilotDescriptorBase::acquire(); +} + +void SAL_CALL ScDataPilotTableObj::release() noexcept +{ + ScDataPilotDescriptorBase::release(); +} + +Sequence< uno::Type > SAL_CALL ScDataPilotTableObj::getTypes() +{ + return comphelper::concatSequences( + ScDataPilotDescriptorBase::getTypes(), + Sequence< uno::Type > + { + cppu::UnoType<XDataPilotTable2>::get(), + cppu::UnoType<XModifyBroadcaster>::get() + } ); +} + +Sequence<sal_Int8> SAL_CALL ScDataPilotTableObj::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +ScDPObject* ScDataPilotTableObj::GetDPObject() const +{ + return lcl_GetDPObject(GetDocShell(), nTab, aName); +} + +void ScDataPilotTableObj::SetDPObject( ScDPObject* pDPObject ) +{ + ScDocShell* pDocSh = GetDocShell(); + ScDPObject* pDPObj = lcl_GetDPObject(pDocSh, nTab, aName); + if ( pDPObj && pDocSh ) + { + ScDBDocFunc aFunc(*pDocSh); + aFunc.DataPilotUpdate( pDPObj, pDPObject, true, true ); + } +} + +// "rest of XDataPilotDescriptor" + +OUString SAL_CALL ScDataPilotTableObj::getName() +{ + SolarMutexGuard aGuard; + ScDPObject* pDPObj = lcl_GetDPObject(GetDocShell(), nTab, aName); + if (pDPObj) + return pDPObj->GetName(); + return OUString(); +} + +void SAL_CALL ScDataPilotTableObj::setName( const OUString& aNewName ) +{ + SolarMutexGuard aGuard; + ScDPObject* pDPObj = lcl_GetDPObject(GetDocShell(), nTab, aName); + if (pDPObj) + { + //! test for existing names !!! + + pDPObj->SetName( aNewName ); //! Undo - DBDocFunc ??? + aName = aNewName; + + // DataPilotUpdate would do too much (output table is not changed) + GetDocShell()->SetDocumentModified(); + } +} + +OUString SAL_CALL ScDataPilotTableObj::getTag() +{ + SolarMutexGuard aGuard; + ScDPObject* pDPObj = lcl_GetDPObject(GetDocShell(), nTab, aName); + if (pDPObj) + return pDPObj->GetTag(); + return OUString(); +} + +void SAL_CALL ScDataPilotTableObj::setTag( const OUString& aNewTag ) +{ + SolarMutexGuard aGuard; + ScDPObject* pDPObj = lcl_GetDPObject(GetDocShell(), nTab, aName); + if (pDPObj) + { + pDPObj->SetTag( aNewTag ); //! Undo - DBDocFunc ??? + + // DataPilotUpdate would do too much (output table is not changed) + GetDocShell()->SetDocumentModified(); + } +} + +// XDataPilotTable + +CellRangeAddress SAL_CALL ScDataPilotTableObj::getOutputRange() +{ + SolarMutexGuard aGuard; + CellRangeAddress aRet; + ScDPObject* pDPObj = lcl_GetDPObject(GetDocShell(), nTab, aName); + if (pDPObj) + { + ScRange aRange(pDPObj->GetOutRange()); + aRet.Sheet = aRange.aStart.Tab(); + aRet.StartColumn = aRange.aStart.Col(); + aRet.StartRow = aRange.aStart.Row(); + aRet.EndColumn = aRange.aEnd.Col(); + aRet.EndRow = aRange.aEnd.Row(); + } + return aRet; +} + +void SAL_CALL ScDataPilotTableObj::refresh() +{ + SolarMutexGuard aGuard; + ScDPObject* pDPObj = lcl_GetDPObject(GetDocShell(), nTab, aName); + if (pDPObj) + { + ScDBDocFunc aFunc(*GetDocShell()); + aFunc.RefreshPivotTables(pDPObj, true); + } +} + +Sequence< Sequence<Any> > SAL_CALL ScDataPilotTableObj::getDrillDownData(const CellAddress& aAddr) +{ + SolarMutexGuard aGuard; + Sequence< Sequence<Any> > aTabData; + ScAddress aAddr2(static_cast<SCCOL>(aAddr.Column), static_cast<SCROW>(aAddr.Row), aAddr.Sheet); + ScDPObject* pObj = GetDPObject(); + if (!pObj) + throw RuntimeException("Failed to get DPObject", static_cast<cppu::OWeakObject*>(this)); + + pObj->GetDrillDownData(aAddr2, aTabData); + return aTabData; +} + +DataPilotTablePositionData SAL_CALL ScDataPilotTableObj::getPositionData(const CellAddress& aAddr) +{ + SolarMutexGuard aGuard; + DataPilotTablePositionData aPosData; + ScAddress aAddr2(static_cast<SCCOL>(aAddr.Column), static_cast<SCROW>(aAddr.Row), aAddr.Sheet); + ScDPObject* pObj = GetDPObject(); + if (!pObj) + throw RuntimeException("Failed to get DPObject", static_cast<cppu::OWeakObject*>(this)); + + pObj->GetPositionData(aAddr2, aPosData); + return aPosData; +} + +void SAL_CALL ScDataPilotTableObj::insertDrillDownSheet(const CellAddress& aAddr) +{ + SolarMutexGuard aGuard; + ScDPObject* pDPObj = GetDPObject(); + if (!pDPObj) + throw RuntimeException("Failed to get DPObject", static_cast<cppu::OWeakObject*>(this)); + ScTabViewShell* pViewSh = GetDocShell()->GetBestViewShell(); + if (!pViewSh) + throw RuntimeException("Failed to get ViewShell", static_cast<cppu::OWeakObject*>(this)); + + Sequence<DataPilotFieldFilter> aFilters; + pDPObj->GetDataFieldPositionData( + ScAddress(static_cast<SCCOL>(aAddr.Column), static_cast<SCROW>(aAddr.Row), aAddr.Sheet), aFilters); + pViewSh->ShowDataPilotSourceData(*pDPObj, aFilters); +} + +CellRangeAddress SAL_CALL ScDataPilotTableObj::getOutputRangeByType( sal_Int32 nType ) +{ + SolarMutexGuard aGuard; + if (nType < 0 || nType > DataPilotOutputRangeType::RESULT) + throw IllegalArgumentException("nType must be between 0 and " + + OUString::number(DataPilotOutputRangeType::RESULT) + ", got " + OUString::number(nType), + static_cast<cppu::OWeakObject*>(this), 0); + + CellRangeAddress aRet; + if (ScDPObject* pDPObj = lcl_GetDPObject(GetDocShell(), nTab, aName)) + ScUnoConversion::FillApiRange( aRet, pDPObj->GetOutputRangeByType( nType ) ); + return aRet; +} + +void SAL_CALL ScDataPilotTableObj::addModifyListener( const uno::Reference<util::XModifyListener>& aListener ) +{ + SolarMutexGuard aGuard; + + aModifyListeners.emplace_back( aListener ); + + if ( aModifyListeners.size() == 1 ) + { + acquire(); // don't lose this object (one ref for all listeners) + } +} + +void SAL_CALL ScDataPilotTableObj::removeModifyListener( const uno::Reference<util::XModifyListener>& aListener ) +{ + SolarMutexGuard aGuard; + + rtl::Reference<ScDataPilotTableObj> xSelfHold(this); // in case the listeners have the last ref + + sal_uInt16 nCount = aModifyListeners.size(); + for ( sal_uInt16 n=nCount; n--; ) + { + uno::Reference<util::XModifyListener>& rObj = aModifyListeners[n]; + if ( rObj == aListener ) + { + aModifyListeners.erase( aModifyListeners.begin() + n ); + + if ( aModifyListeners.empty() ) + { + release(); // release the ref for the listeners + } + + break; + } + } +} + +void ScDataPilotTableObj::Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) +{ + if ( auto pDataPilotHint = dynamic_cast<const ScDataPilotModifiedHint*>(&rHint) ) + { + if (pDataPilotHint->GetName() == aName) + Refreshed_Impl(); + } + else if ( auto pRefHint = dynamic_cast<const ScUpdateRefHint*>(&rHint) ) + { + ScRange aRange( 0, 0, nTab ); + ScRangeList aRanges( aRange ); + if ( aRanges.UpdateReference( pRefHint->GetMode(), &GetDocShell()->GetDocument(), pRefHint->GetRange(), + pRefHint->GetDx(), pRefHint->GetDy(), pRefHint->GetDz() ) && + aRanges.size() == 1 ) + { + nTab = aRanges.front().aStart.Tab(); + } + } + + ScDataPilotDescriptorBase::Notify( rBC, rHint ); +} + +void ScDataPilotTableObj::Refreshed_Impl() +{ + lang::EventObject aEvent; + aEvent.Source.set(static_cast<cppu::OWeakObject*>(this)); + + // the EventObject holds a Ref to this object until after the listener calls + + ScDocument& rDoc = GetDocShell()->GetDocument(); + for (const uno::Reference<util::XModifyListener> & xModifyListener : aModifyListeners) + rDoc.AddUnoListenerCall( xModifyListener, aEvent ); +} + +ScDataPilotDescriptor::ScDataPilotDescriptor(ScDocShell& rDocSh) : + ScDataPilotDescriptorBase( rDocSh ), + mpDPObject(new ScDPObject(&rDocSh.GetDocument())) +{ + ScDPSaveData aSaveData; + // set defaults like in ScPivotParam constructor + aSaveData.SetColumnGrand( true ); + aSaveData.SetRowGrand( true ); + aSaveData.SetIgnoreEmptyRows( false ); + aSaveData.SetRepeatIfEmpty( false ); + mpDPObject->SetSaveData(aSaveData); + ScSheetSourceDesc aSheetDesc(&rDocSh.GetDocument()); + mpDPObject->SetSheetDesc(aSheetDesc); +} + +ScDataPilotDescriptor::~ScDataPilotDescriptor() +{ +} + +ScDPObject* ScDataPilotDescriptor::GetDPObject() const +{ + return mpDPObject.get(); +} + +void ScDataPilotDescriptor::SetDPObject( ScDPObject* pDPObject ) +{ + if (mpDPObject.get() != pDPObject) + { + mpDPObject.reset( pDPObject ); + OSL_FAIL("replace DPObject should not happen"); + } +} + +// "rest of XDataPilotDescriptor" + +OUString SAL_CALL ScDataPilotDescriptor::getName() +{ + SolarMutexGuard aGuard; + return mpDPObject->GetName(); +} + +void SAL_CALL ScDataPilotDescriptor::setName( const OUString& aNewName ) +{ + SolarMutexGuard aGuard; + mpDPObject->SetName( aNewName ); +} + +OUString SAL_CALL ScDataPilotDescriptor::getTag() +{ + SolarMutexGuard aGuard; + return mpDPObject->GetTag(); +} + +void SAL_CALL ScDataPilotDescriptor::setTag( const OUString& aNewTag ) +{ + SolarMutexGuard aGuard; + mpDPObject->SetTag( aNewTag ); +} + +ScDataPilotChildObjBase::ScDataPilotChildObjBase( ScDataPilotDescriptorBase& rParent ) : + mxParent( &rParent ) +{ +} + +ScDataPilotChildObjBase::ScDataPilotChildObjBase( ScDataPilotDescriptorBase& rParent, const ScFieldIdentifier& rFieldId ) : + mxParent( &rParent ), + maFieldId( rFieldId ) +{ +} + +ScDataPilotChildObjBase::~ScDataPilotChildObjBase() +{ +} + +ScDPObject* ScDataPilotChildObjBase::GetDPObject() const +{ + return mxParent->GetDPObject(); +} + +void ScDataPilotChildObjBase::SetDPObject( ScDPObject* pDPObject ) +{ + mxParent->SetDPObject( pDPObject ); +} + +ScDPSaveDimension* ScDataPilotChildObjBase::GetDPDimension( ScDPObject** ppDPObject ) const +{ + if( ScDPObject* pDPObj = GetDPObject() ) + { + if( ppDPObject ) *ppDPObject = pDPObj; + if( ScDPSaveData* pSaveData = pDPObj->GetSaveData() ) + { + if( maFieldId.mbDataLayout ) + return pSaveData->GetDataLayoutDimension(); + + if( maFieldId.mnFieldIdx == 0 ) + return pSaveData->GetDimensionByName( maFieldId.maFieldName ); + + // find dimension with specified index (search in duplicated dimensions) + const ScDPSaveData::DimsType& rDims = pSaveData->GetDimensions(); + + sal_Int32 nFoundIdx = 0; + for (auto const& it : rDims) + { + if (it->IsDataLayout()) + continue; + + OUString aSrcName = ScDPUtil::getSourceDimensionName(it->GetName()); + if (aSrcName == maFieldId.maFieldName) + { + if( nFoundIdx == maFieldId.mnFieldIdx ) + return it.get(); + ++nFoundIdx; + } + } + } + } + return nullptr; +} + +sal_Int32 ScDataPilotChildObjBase::GetMemberCount() const +{ + sal_Int32 nRet = 0; + Reference<XNameAccess> xMembersNA = GetMembers(); + if (xMembersNA.is()) + { + Reference< XIndexAccess > xMembersIA( new ScNameToIndexAccess( xMembersNA ) ); + nRet = xMembersIA->getCount(); + } + return nRet; +} + +Reference< XMembersAccess > ScDataPilotChildObjBase::GetMembers() const +{ + Reference< XMembersAccess > xMembersNA; + if( ScDPObject* pDPObj = GetDPObject() ) + pDPObj->GetMembersNA( lcl_GetObjectIndex( pDPObj, maFieldId ), xMembersNA ); + return xMembersNA; +} + +ScDocShell* ScDataPilotChildObjBase::GetDocShell() const +{ + return mxParent->GetDocShell(); +} + +ScDataPilotFieldsObj::ScDataPilotFieldsObj( ScDataPilotDescriptorBase& rParent ) : + ScDataPilotChildObjBase( rParent ) +{ +} + +ScDataPilotFieldsObj::ScDataPilotFieldsObj( ScDataPilotDescriptorBase& rParent, DataPilotFieldOrientation eOrient ) : + ScDataPilotChildObjBase( rParent ), + maOrient( eOrient ) +{ +} + +ScDataPilotFieldsObj::~ScDataPilotFieldsObj() +{ +} + +static sal_Int32 lcl_GetFieldCount( const Reference<XDimensionsSupplier>& rSource, const Any& rOrient ) +{ + if (!rSource.is()) + throw NullPointerException(); + + sal_Int32 nRet = 0; + + Reference<XNameAccess> xDimsName(rSource->getDimensions()); + Reference<XIndexAccess> xIntDims(new ScNameToIndexAccess( xDimsName )); + sal_Int32 nIntCount = xIntDims->getCount(); + for (sal_Int32 i = 0; i < nIntCount; ++i) + { + Reference<XPropertySet> xDim(xIntDims->getByIndex(i), UNO_QUERY); + const bool bMatch = xDim + && (rOrient.hasValue() + // all fields of the specified orientation, including duplicated + ? (xDim->getPropertyValue(SC_UNO_DP_ORIENTATION) == rOrient) + // count all non-duplicated fields + : !lcl_IsDuplicated(xDim)); + if (bMatch) + ++nRet; + } + + return nRet; +} + +static bool lcl_GetFieldDataByIndex( const Reference<XDimensionsSupplier>& rSource, + const Any& rOrient, SCSIZE nIndex, ScFieldIdentifier& rFieldId ) +{ + if (!rSource.is()) + throw NullPointerException(); + + bool bOk = false; + SCSIZE nPos = 0; + sal_Int32 nDimIndex = 0; + + Reference<XNameAccess> xDimsName(rSource->getDimensions()); + Reference<XIndexAccess> xIntDims(new ScNameToIndexAccess( xDimsName )); + sal_Int32 nIntCount = xIntDims->getCount(); + Reference<XPropertySet> xDim; + for (sal_Int32 i = 0; i < nIntCount; ++i) + { + xDim.set(xIntDims->getByIndex(i), UNO_QUERY); + const bool bMatch = xDim + && (rOrient.hasValue() + ? (xDim->getPropertyValue(SC_UNO_DP_ORIENTATION) == rOrient) + : !lcl_IsDuplicated(xDim)); + if (bMatch) + { + if (nPos == nIndex) + { + bOk = true; + nDimIndex = i; + break; + } + else + ++nPos; + } + } + + if ( bOk ) + { + xDim.set( xIntDims->getByIndex(nDimIndex), UNO_QUERY ); + Reference<XNamed> xDimName( xDim, UNO_QUERY ); + if ( xDimName.is() ) + { + OUString sOriginalName( lcl_GetOriginalName( xDimName ) ); + rFieldId.maFieldName = sOriginalName; + rFieldId.mbDataLayout = ScUnoHelpFunctions::GetBoolProperty( xDim, + SC_UNO_DP_ISDATALAYOUT ); + + sal_Int32 nRepeat = 0; + if ( rOrient.hasValue() && lcl_IsDuplicated( xDim ) ) + { + // find the repeat count + // (this relies on the original dimension always being before the duplicates) + + Reference<XNamed> xPrevName; + for (sal_Int32 i = 0; i < nDimIndex; ++i) + { + xPrevName.set( xIntDims->getByIndex(i), UNO_QUERY ); + if ( xPrevName.is() && lcl_GetOriginalName( xPrevName ) == sOriginalName ) + ++nRepeat; + } + } + rFieldId.mnFieldIdx = nRepeat; + } + else + bOk = false; + } + + return bOk; +} + +static bool lcl_GetFieldDataByName( ScDPObject* pDPObj, const OUString& rFieldName, ScFieldIdentifier& rFieldId ) +{ + // "By name" is always the first match. + // The name "Data" always refers to the data layout field. + rFieldId.maFieldName = rFieldName; + rFieldId.mnFieldIdx = 0; + rFieldId.mbDataLayout = rFieldName == SC_DATALAYOUT_NAME; + + pDPObj->GetSource(); // IsDimNameInUse doesn't update source data + + // check if the named field exists (not for data layout) + return rFieldId.mbDataLayout || pDPObj->IsDimNameInUse( rFieldName ); +} + +// XDataPilotFields + +rtl::Reference<ScDataPilotFieldObj> ScDataPilotFieldsObj::GetObjectByIndex_Impl( sal_Int32 nIndex ) const +{ + if (ScDPObject* pObj = GetDPObject()) + { + ScFieldIdentifier aFieldId; + if (lcl_GetFieldDataByIndex( pObj->GetSource(), maOrient, nIndex, aFieldId )) + return new ScDataPilotFieldObj( *mxParent, aFieldId, maOrient ); + } + return nullptr; +} + +rtl::Reference<ScDataPilotFieldObj> ScDataPilotFieldsObj::GetObjectByName_Impl(const OUString& aName) const +{ + if (ScDPObject* pDPObj = GetDPObject()) + { + ScFieldIdentifier aFieldId; + if (lcl_GetFieldDataByName( pDPObj, aName, aFieldId )) + return new ScDataPilotFieldObj( *mxParent, aFieldId, maOrient ); + } + return nullptr; +} + +// XEnumerationAccess + +Reference<XEnumeration> SAL_CALL ScDataPilotFieldsObj::createEnumeration() +{ + SolarMutexGuard aGuard; + return new ScIndexEnumeration(this, "com.sun.star.sheet.DataPilotFieldsEnumeration"); +} + +// XIndexAccess + +sal_Int32 SAL_CALL ScDataPilotFieldsObj::getCount() +{ + SolarMutexGuard aGuard; + ScDPObject* pDPObj = GetDPObject(); + return pDPObj ? lcl_GetFieldCount( pDPObj->GetSource(), maOrient ) : 0; +} + +Any SAL_CALL ScDataPilotFieldsObj::getByIndex( sal_Int32 nIndex ) +{ + SolarMutexGuard aGuard; + Reference< XPropertySet > xField( GetObjectByIndex_Impl( nIndex ) ); + if (!xField.is()) + throw IndexOutOfBoundsException(); + return Any( xField ); +} + +// XElementAccess + +uno::Type SAL_CALL ScDataPilotFieldsObj::getElementType() +{ + return cppu::UnoType<XPropertySet>::get(); +} + +sal_Bool SAL_CALL ScDataPilotFieldsObj::hasElements() +{ + SolarMutexGuard aGuard; + return ( getCount() != 0 ); +} + +// XNameAccess + +Any SAL_CALL ScDataPilotFieldsObj::getByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + Reference<XPropertySet> xField(GetObjectByName_Impl(aName)); + if (!xField.is()) + throw NoSuchElementException(); + return Any( xField ); +} + +Sequence<OUString> SAL_CALL ScDataPilotFieldsObj::getElementNames() +{ + SolarMutexGuard aGuard; + if (ScDPObject* pDPObj = GetDPObject()) + { + Sequence< OUString > aSeq( lcl_GetFieldCount( pDPObj->GetSource(), maOrient ) ); + OUString* pAry = aSeq.getArray(); + + const ScDPSaveData::DimsType& rDimensions = pDPObj->GetSaveData()->GetDimensions(); + for (auto const& it : rDimensions) + { + if(maOrient.hasValue() && (it->GetOrientation() == maOrient.get< DataPilotFieldOrientation >())) + { + *pAry = it->GetName(); + ++pAry; + } + } + return aSeq; + } + return Sequence<OUString>(); +} + +sal_Bool SAL_CALL ScDataPilotFieldsObj::hasByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + + return GetObjectByName_Impl(aName) != nullptr; +} + +ScDataPilotFieldObj::ScDataPilotFieldObj( + ScDataPilotDescriptorBase& rParent, const ScFieldIdentifier& rFieldId ) : + ScDataPilotChildObjBase( rParent, rFieldId ), + maPropSet( lcl_GetDataPilotFieldMap() ) +{ +} + +ScDataPilotFieldObj::ScDataPilotFieldObj( ScDataPilotDescriptorBase& rParent, + const ScFieldIdentifier& rFieldId, const Any& rOrient ) : + ScDataPilotChildObjBase( rParent, rFieldId ), + maPropSet( lcl_GetDataPilotFieldMap() ), + maOrient( rOrient ) +{ +} + +ScDataPilotFieldObj::~ScDataPilotFieldObj() +{ +} + +// XNamed + +OUString SAL_CALL ScDataPilotFieldObj::getName() +{ + SolarMutexGuard aGuard; + OUString aName; + if( ScDPSaveDimension* pDim = GetDPDimension() ) + { + if( pDim->IsDataLayout() ) + aName = SC_DATALAYOUT_NAME; + else + { + const std::optional<OUString> & pLayoutName = pDim->GetLayoutName(); + if (pLayoutName) + aName = *pLayoutName; + else + aName = pDim->GetName(); + } + } + return aName; +} + +void SAL_CALL ScDataPilotFieldObj::setName(const OUString& rName) +{ + SolarMutexGuard aGuard; + ScDPObject* pDPObj = nullptr; + ScDPSaveDimension* pDim = GetDPDimension( &pDPObj ); + if( pDim && !pDim->IsDataLayout() ) + { + pDim->SetLayoutName(rName); + SetDPObject( pDPObj ); + } +} + +// XPropertySet + +Reference<XPropertySetInfo> SAL_CALL ScDataPilotFieldObj::getPropertySetInfo() +{ + SolarMutexGuard aGuard; + static Reference<XPropertySetInfo> aRef( + new SfxItemPropertySetInfo( maPropSet.getPropertyMap() )); + return aRef; +} + +void SAL_CALL ScDataPilotFieldObj::setPropertyValue( const OUString& aPropertyName, const Any& aValue ) +{ + SolarMutexGuard aGuard; + if ( aPropertyName == SC_UNONAME_FUNCTION ) + { + // #i109350# use GetEnumFromAny because it also allows sal_Int32 + ScGeneralFunction eFunction = static_cast<ScGeneralFunction>(ScUnoHelpFunctions::GetEnumFromAny( aValue )); + setFunction( eFunction ); + } + else if ( aPropertyName == SC_UNONAME_FUNCTION2 ) + { + ScGeneralFunction eFunction = static_cast<ScGeneralFunction>(ScUnoHelpFunctions::GetInt16FromAny( aValue )); + setFunction( eFunction ); + } + else if ( aPropertyName == SC_UNONAME_SUBTOTALS ) + { + uno::Sequence<sheet::GeneralFunction> aSeq; + if( aValue >>= aSeq) + { + std::vector< ScGeneralFunction > aSubTotals(aSeq.getLength()); + std::transform(std::cbegin(aSeq), std::cend(aSeq), aSubTotals.begin(), + [](const sheet::GeneralFunction& rValue) -> ScGeneralFunction { + const int nValAsInt = static_cast<int>(rValue); + return static_cast<ScGeneralFunction>(nValAsInt); + }); + setSubtotals( aSubTotals ); + } + } + else if ( aPropertyName == SC_UNONAME_SUBTOTALS2 ) + { + Sequence< sal_Int16 > aSeq; + if( aValue >>= aSeq ) + { + std::vector< ScGeneralFunction > aSubTotals(aSeq.getLength()); + std::transform(std::cbegin(aSeq), std::cend(aSeq), aSubTotals.begin(), + [](sal_Int16 nValue) -> ScGeneralFunction { return static_cast<ScGeneralFunction>(nValue); }); + setSubtotals( aSubTotals ); + } + } + else if ( aPropertyName == SC_UNONAME_ORIENT ) + { + //! test for correct enum type? + DataPilotFieldOrientation eOrient = static_cast<DataPilotFieldOrientation>(ScUnoHelpFunctions::GetEnumFromAny( aValue )); + setOrientation( eOrient ); + } + else if ( aPropertyName == SC_UNONAME_SELPAGE ) + { + OUString sCurrentPage; + if (aValue >>= sCurrentPage) + setCurrentPage(sCurrentPage); + } + else if ( aPropertyName == SC_UNONAME_USESELPAGE ) + { + setUseCurrentPage(cppu::any2bool(aValue)); + } + else if ( aPropertyName == SC_UNONAME_HASAUTOSHOW ) + { + if (!cppu::any2bool(aValue)) + setAutoShowInfo(nullptr); + } + else if ( aPropertyName == SC_UNONAME_AUTOSHOW ) + { + DataPilotFieldAutoShowInfo aInfo; + if (aValue >>= aInfo) + setAutoShowInfo(&aInfo); + } + else if ( aPropertyName == SC_UNONAME_HASLAYOUTINFO ) + { + if (!cppu::any2bool(aValue)) + setLayoutInfo(nullptr); + } + else if ( aPropertyName == SC_UNONAME_LAYOUTINFO ) + { + DataPilotFieldLayoutInfo aInfo; + if (aValue >>= aInfo) + setLayoutInfo(&aInfo); + } + else if ( aPropertyName == SC_UNONAME_HASREFERENCE ) + { + if (!cppu::any2bool(aValue)) + setReference(nullptr); + } + else if ( aPropertyName == SC_UNONAME_REFERENCE ) + { + DataPilotFieldReference aRef; + if (aValue >>= aRef) + setReference(&aRef); + } + else if ( aPropertyName == SC_UNONAME_HASSORTINFO ) + { + if (!cppu::any2bool(aValue)) + setSortInfo(nullptr); + } + else if ( aPropertyName == SC_UNONAME_SORTINFO ) + { + DataPilotFieldSortInfo aInfo; + if (aValue >>= aInfo) + setSortInfo(&aInfo); + } + else if ( aPropertyName == SC_UNONAME_ISGROUP ) + { + if (!cppu::any2bool(aValue)) + setGroupInfo(nullptr); + } + else if ( aPropertyName == SC_UNONAME_GROUPINFO ) + { + DataPilotFieldGroupInfo aInfo; + if (aValue >>= aInfo) + setGroupInfo(&aInfo); + } + else if ( aPropertyName == SC_UNONAME_SHOWEMPTY ) + { + setShowEmpty(cppu::any2bool(aValue)); + } + else if ( aPropertyName == SC_UNONAME_REPEATITEMLABELS ) + { + setRepeatItemLabels(cppu::any2bool(aValue)); + } + else if (aPropertyName == SC_UNONAME_NAME) + { + OUString sName; + if (aValue >>= sName) + setName(sName); + } +} + +Any SAL_CALL ScDataPilotFieldObj::getPropertyValue( const OUString& aPropertyName ) +{ + SolarMutexGuard aGuard; + Any aRet; + + if ( aPropertyName == SC_UNONAME_FUNCTION ) + { + sheet::GeneralFunction eVal; + sal_Int16 nFunction = getFunction(); + if (nFunction == sheet::GeneralFunction2::MEDIAN) + { + eVal = sheet::GeneralFunction_NONE; + } + else + { + eVal = static_cast<sheet::GeneralFunction>(nFunction); + } + aRet <<= eVal; + } + else if ( aPropertyName == SC_UNONAME_FUNCTION2 ) + aRet <<= getFunction(); + else if ( aPropertyName == SC_UNONAME_SUBTOTALS ) + { + const uno::Sequence<sal_Int16> aSeq = getSubtotals(); + uno::Sequence<sheet::GeneralFunction> aNewSeq(aSeq.getLength()); + std::transform(aSeq.begin(), aSeq.end(), aNewSeq.getArray(), + [](sal_Int16 nFunc) -> sheet::GeneralFunction { + if (nFunc == sheet::GeneralFunction2::MEDIAN) + return sheet::GeneralFunction_NONE; + return static_cast<sheet::GeneralFunction>(nFunc); + }); + aRet <<= aNewSeq; + } + else if ( aPropertyName == SC_UNONAME_SUBTOTALS2 ) + { + aRet <<= getSubtotals(); + } + else if ( aPropertyName == SC_UNONAME_ORIENT ) + aRet <<= getOrientation(); + else if ( aPropertyName == SC_UNONAME_SELPAGE ) + aRet <<= OUString(); + else if ( aPropertyName == SC_UNONAME_USESELPAGE ) + aRet <<= false; + else if ( aPropertyName == SC_UNONAME_HASAUTOSHOW ) + aRet <<= (getAutoShowInfo() != nullptr); + else if ( aPropertyName == SC_UNONAME_AUTOSHOW ) + { + const DataPilotFieldAutoShowInfo* pInfo = getAutoShowInfo(); + if (pInfo) + aRet <<= *pInfo; + } + else if ( aPropertyName == SC_UNONAME_HASLAYOUTINFO ) + aRet <<= (getLayoutInfo() != nullptr); + else if ( aPropertyName == SC_UNONAME_LAYOUTINFO ) + { + const DataPilotFieldLayoutInfo* pInfo = getLayoutInfo(); + if (pInfo) + aRet <<= *pInfo; + } + else if ( aPropertyName == SC_UNONAME_HASREFERENCE ) + aRet <<= (getReference() != nullptr); + else if ( aPropertyName == SC_UNONAME_REFERENCE ) + { + const DataPilotFieldReference* pRef = getReference(); + if (pRef) + aRet <<= *pRef; + } + else if ( aPropertyName == SC_UNONAME_HASSORTINFO ) + aRet <<= (getSortInfo() != nullptr); + else if ( aPropertyName == SC_UNONAME_SORTINFO ) + { + const DataPilotFieldSortInfo* pInfo = getSortInfo(); + if (pInfo) + aRet <<= *pInfo; + } + else if ( aPropertyName == SC_UNONAME_ISGROUP ) + aRet <<= hasGroupInfo(); + else if ( aPropertyName == SC_UNONAME_GROUPINFO ) + { + aRet <<= getGroupInfo(); + } + else if ( aPropertyName == SC_UNONAME_SHOWEMPTY ) + aRet <<= getShowEmpty(); + else if ( aPropertyName == SC_UNONAME_REPEATITEMLABELS ) + aRet <<= getRepeatItemLabels(); + else if (aPropertyName == SC_UNONAME_NAME) + aRet <<= getName(); + + return aRet; +} + +// XDatePilotField + +Reference<XIndexAccess> SAL_CALL ScDataPilotFieldObj::getItems() +{ + SolarMutexGuard aGuard; + if (!mxItems.is()) + mxItems.set( new ScDataPilotItemsObj( *mxParent, maFieldId ) ); + return mxItems; +} + +SC_IMPL_DUMMY_PROPERTY_LISTENER( ScDataPilotFieldObj ) + +DataPilotFieldOrientation ScDataPilotFieldObj::getOrientation() const +{ + SolarMutexGuard aGuard; + ScDPSaveDimension* pDim = GetDPDimension(); + return pDim ? pDim->GetOrientation() : DataPilotFieldOrientation_HIDDEN; +} + +void ScDataPilotFieldObj::setOrientation(DataPilotFieldOrientation eNew) +{ + SolarMutexGuard aGuard; + if (maOrient.hasValue() && (eNew == maOrient.get< DataPilotFieldOrientation >())) + return; + + ScDPObject* pDPObj = nullptr; + ScDPSaveDimension* pDim = GetDPDimension( &pDPObj ); + if(!pDim) + return; + + ScDPSaveData* pSaveData = pDPObj->GetSaveData(); + + /* If the field was taken from getDataPilotFields(), don't reset the + orientation for an existing use, but create a duplicated field + instead (for "Data" orientation only). */ + if ( !maOrient.hasValue() && !maFieldId.mbDataLayout && + (pDim->GetOrientation() != DataPilotFieldOrientation_HIDDEN) && + (eNew == DataPilotFieldOrientation_DATA) ) + { + + ScDPSaveDimension* pNewDim = nullptr; + + // look for existing duplicate with orientation "hidden" + + sal_Int32 nFound = 0; + const ScDPSaveData::DimsType& rDimensions = pSaveData->GetDimensions(); + for (auto const& it : rDimensions) + { + if ( !it->IsDataLayout() && (it->GetName() == maFieldId.maFieldName) ) + { + if ( it->GetOrientation() == DataPilotFieldOrientation_HIDDEN ) + { + pNewDim = it.get(); // use this one + break; + } + else + ++nFound; // count existing non-hidden occurrences + } + } + + if ( !pNewDim ) // if none found, create a new duplicated dimension + pNewDim = &pSaveData->DuplicateDimension( *pDim ); + + maFieldId.mnFieldIdx = nFound; // keep accessing the new one + pDim = pNewDim; + } + + pDim->SetOrientation(eNew); + + // move changed field behind all other fields (make it the last field in dimension) + pSaveData->SetPosition( pDim, pSaveData->GetDimensions().size() ); + + SetDPObject( pDPObj ); + + maOrient <<= eNew; // modifying the same object's orientation again doesn't create another duplicate +} + +sal_Int16 ScDataPilotFieldObj::getFunction() const +{ + SolarMutexGuard aGuard; + sal_Int16 eRet = GeneralFunction2::NONE; + if( ScDPSaveDimension* pDim = GetDPDimension() ) + { + if( pDim->GetOrientation() != DataPilotFieldOrientation_DATA ) + { + // for non-data fields, property Function is the subtotals + tools::Long nSubCount = pDim->GetSubTotalsCount(); + if ( nSubCount > 0 ) + eRet = static_cast<sal_Int16>(pDim->GetSubTotalFunc(0)); // always use the first one + // else keep NONE + } + else + eRet = static_cast<sal_Int16>(pDim->GetFunction()); + } + return eRet; +} + +void ScDataPilotFieldObj::setFunction(ScGeneralFunction eNewFunc) +{ + SolarMutexGuard aGuard; + ScDPObject* pDPObj = nullptr; + ScDPSaveDimension* pDim = GetDPDimension( &pDPObj ); + if(!pDim) + return; + + if( pDim->GetOrientation() != DataPilotFieldOrientation_DATA ) + { + // for non-data fields, property Function is the subtotals + std::vector<ScGeneralFunction> nSubTotalFuncs; + if ( eNewFunc != ScGeneralFunction::NONE ) + { + nSubTotalFuncs.push_back( eNewFunc ); + } + pDim->SetSubTotals( std::move(nSubTotalFuncs) ); + } + else + pDim->SetFunction( eNewFunc ); + SetDPObject( pDPObj ); +} + +Sequence< sal_Int16 > ScDataPilotFieldObj::getSubtotals() const +{ + SolarMutexGuard aGuard; + Sequence< sal_Int16 > aRet; + if( ScDPSaveDimension* pDim = GetDPDimension() ) + { + if( pDim->GetOrientation() != DataPilotFieldOrientation_DATA ) + { + // for non-data fields, property Functions is the sequence of subtotals + sal_Int32 nCount = static_cast< sal_Int32 >( pDim->GetSubTotalsCount() ); + if ( nCount > 0 ) + { + aRet.realloc( nCount ); + auto pRet = aRet.getArray(); + for( sal_Int32 nIdx = 0; nIdx < nCount; ++nIdx ) + pRet[ nIdx ] = static_cast<sal_Int16>(pDim->GetSubTotalFunc( nIdx )); + } + } + } + return aRet; +} + +void ScDataPilotFieldObj::setSubtotals( const std::vector< ScGeneralFunction >& rSubtotals ) +{ + SolarMutexGuard aGuard; + ScDPObject* pDPObj = nullptr; + ScDPSaveDimension* pDim = GetDPDimension( &pDPObj ); + if(!pDim) + return; + + if( pDim->GetOrientation() != DataPilotFieldOrientation_DATA ) + { + sal_Int32 nCount = rSubtotals.size(); + if( nCount == 1 ) + { + // count 1: all values are allowed (including NONE and AUTO) + std::vector<ScGeneralFunction> nTmpFuncs; + if( rSubtotals[ 0 ] != ScGeneralFunction::NONE ) + { + nTmpFuncs.push_back( rSubtotals[ 0 ] ); + } + pDim->SetSubTotals( std::move(nTmpFuncs) ); + } + else if( nCount > 1 ) + { + // set multiple functions, ignore NONE and AUTO in this case + ::std::vector< ScGeneralFunction > aSubt; + for( sal_Int32 nIdx = 0; nIdx < nCount; ++nIdx ) + { + ScGeneralFunction eFunc = rSubtotals[ nIdx ]; + if( (eFunc != ScGeneralFunction::NONE) && (eFunc != ScGeneralFunction::AUTO) ) + { + // do not insert functions twice + if( ::std::find( aSubt.begin(), aSubt.end(), eFunc ) == aSubt.end() ) + aSubt.push_back( eFunc ); + } + } + // set values from vector to ScDPSaveDimension + pDim->SetSubTotals( std::move(aSubt) ); + } + } + SetDPObject( pDPObj ); +} + +void ScDataPilotFieldObj::setCurrentPage( const OUString& rPage ) +{ + SolarMutexGuard aGuard; + ScDPObject* pDPObj = nullptr; + if( ScDPSaveDimension* pDim = GetDPDimension( &pDPObj ) ) + { + pDim->SetCurrentPage( &rPage ); + SetDPObject( pDPObj ); + } +} + +void ScDataPilotFieldObj::setUseCurrentPage( bool bUse ) +{ + SolarMutexGuard aGuard; + ScDPObject* pDPObj = nullptr; + ScDPSaveDimension* pDim = GetDPDimension( &pDPObj ); + if(!pDim) + return; + + if( bUse ) + { + /* It is somehow useless to set the property "HasSelectedPage" to + true, because it is still needed to set an explicit page name. */ + const OUString aPage; + pDim->SetCurrentPage( &aPage ); + } + else + pDim->SetCurrentPage( nullptr ); + SetDPObject( pDPObj ); +} + +const DataPilotFieldAutoShowInfo* ScDataPilotFieldObj::getAutoShowInfo() const +{ + SolarMutexGuard aGuard; + ScDPSaveDimension* pDim = GetDPDimension(); + return pDim ? pDim->GetAutoShowInfo() : nullptr; +} + +void ScDataPilotFieldObj::setAutoShowInfo( const DataPilotFieldAutoShowInfo* pInfo ) +{ + SolarMutexGuard aGuard; + ScDPObject* pDPObj = nullptr; + if( ScDPSaveDimension* pDim = GetDPDimension( &pDPObj ) ) + { + pDim->SetAutoShowInfo( pInfo ); + SetDPObject( pDPObj ); + } +} + +const DataPilotFieldLayoutInfo* ScDataPilotFieldObj::getLayoutInfo() const +{ + SolarMutexGuard aGuard; + ScDPSaveDimension* pDim = GetDPDimension(); + return pDim ? pDim->GetLayoutInfo() : nullptr; +} + +void ScDataPilotFieldObj::setLayoutInfo( const DataPilotFieldLayoutInfo* pInfo ) +{ + SolarMutexGuard aGuard; + ScDPObject* pDPObj = nullptr; + if( ScDPSaveDimension* pDim = GetDPDimension( &pDPObj ) ) + { + pDim->SetLayoutInfo( pInfo ); + SetDPObject( pDPObj ); + } +} + +const DataPilotFieldReference* ScDataPilotFieldObj::getReference() const +{ + SolarMutexGuard aGuard; + ScDPSaveDimension* pDim = GetDPDimension(); + return pDim ? pDim->GetReferenceValue() : nullptr; +} + +void ScDataPilotFieldObj::setReference( const DataPilotFieldReference* pInfo ) +{ + SolarMutexGuard aGuard; + ScDPObject* pDPObj = nullptr; + if( ScDPSaveDimension* pDim = GetDPDimension( &pDPObj ) ) + { + pDim->SetReferenceValue( pInfo ); + SetDPObject( pDPObj ); + } +} + +const DataPilotFieldSortInfo* ScDataPilotFieldObj::getSortInfo() const +{ + SolarMutexGuard aGuard; + ScDPSaveDimension* pDim = GetDPDimension(); + return pDim ? pDim->GetSortInfo() : nullptr; +} + +void ScDataPilotFieldObj::setSortInfo( const DataPilotFieldSortInfo* pInfo ) +{ + SolarMutexGuard aGuard; + ScDPObject* pDPObj = nullptr; + if( ScDPSaveDimension* pDim = GetDPDimension( &pDPObj ) ) + { + pDim->SetSortInfo( pInfo ); + SetDPObject( pDPObj ); + } +} + +bool ScDataPilotFieldObj::getShowEmpty() const +{ + SolarMutexGuard aGuard; + ScDPSaveDimension* pDim = GetDPDimension(); + return pDim && pDim->GetShowEmpty(); +} + +void ScDataPilotFieldObj::setShowEmpty( bool bShow ) +{ + SolarMutexGuard aGuard; + ScDPObject* pDPObj = nullptr; + if( ScDPSaveDimension* pDim = GetDPDimension( &pDPObj ) ) + { + pDim->SetShowEmpty( bShow ); + SetDPObject( pDPObj ); + } +} + +bool ScDataPilotFieldObj::getRepeatItemLabels() const +{ + SolarMutexGuard aGuard; + ScDPSaveDimension* pDim = GetDPDimension(); + return pDim && pDim->GetRepeatItemLabels(); +} + +void ScDataPilotFieldObj::setRepeatItemLabels( bool bShow ) +{ + SolarMutexGuard aGuard; + ScDPObject* pDPObj = nullptr; + if( ScDPSaveDimension* pDim = GetDPDimension( &pDPObj ) ) + { + pDim->SetRepeatItemLabels( bShow ); + SetDPObject( pDPObj ); + } +} + +bool ScDataPilotFieldObj::hasGroupInfo() const +{ + SolarMutexGuard aGuard; + ScDPObject* pDPObj = nullptr; + if( ScDPSaveDimension* pDim = GetDPDimension( &pDPObj ) ) + if( const ScDPDimensionSaveData* pDimData = pDPObj->GetSaveData()->GetExistingDimensionData() ) + return pDimData->GetNamedGroupDim( pDim->GetName() ) || pDimData->GetNumGroupDim( pDim->GetName() ); + return false; +} + +DataPilotFieldGroupInfo ScDataPilotFieldObj::getGroupInfo() +{ + SolarMutexGuard aGuard; + DataPilotFieldGroupInfo aInfo; + ScDPObject* pDPObj = nullptr; + if( ScDPSaveDimension* pDim = GetDPDimension( &pDPObj ) ) + { + if( const ScDPDimensionSaveData* pDimData = pDPObj->GetSaveData()->GetExistingDimensionData() ) + { + if( const ScDPSaveGroupDimension* pGroupDim = pDimData->GetNamedGroupDim( pDim->GetName() ) ) + { + // grouped by ... + aInfo.GroupBy = pGroupDim->GetDatePart(); + + // find source field + try + { + Reference< XNameAccess > xFields( mxParent->getDataPilotFields(), UNO_QUERY_THROW ); + aInfo.SourceField.set( xFields->getByName( pGroupDim->GetSourceDimName() ), UNO_QUERY ); + } + catch( Exception& ) + { + } + + ScDataPilotConversion::FillGroupInfo( aInfo, pGroupDim->GetDateInfo() ); + if( pGroupDim->GetDatePart() == 0 ) + { + // fill vector of group and group member information + ScFieldGroups aGroups; + for( sal_Int32 nIdx = 0, nCount = pGroupDim->GetGroupCount(); nIdx < nCount; ++nIdx ) + { + const ScDPSaveGroupItem& rGroup = pGroupDim->GetGroupByIndex( nIdx ); + ScFieldGroup aGroup; + aGroup.maName = rGroup.GetGroupName(); + for( sal_Int32 nMemIdx = 0, nMemCount = rGroup.GetElementCount(); nMemIdx < nMemCount; ++nMemIdx ) + if (const OUString* pMem = rGroup.GetElementByIndex(nMemIdx)) + aGroup.maMembers.push_back( *pMem ); + aGroups.push_back( aGroup ); + } + aInfo.Groups = new ScDataPilotFieldGroupsObj( std::move(aGroups) ); + } + } + else if( const ScDPSaveNumGroupDimension* pNumGroupDim = pDimData->GetNumGroupDim( pDim->GetName() ) ) + { + if (pNumGroupDim->GetDatePart()) + { + ScDataPilotConversion::FillGroupInfo( aInfo, pNumGroupDim->GetDateInfo() ); + aInfo.GroupBy = pNumGroupDim->GetDatePart(); + } + else + { + ScDataPilotConversion::FillGroupInfo( aInfo, pNumGroupDim->GetInfo() ); + } + } + } + } + return aInfo; +} + +void ScDataPilotFieldObj::setGroupInfo( const DataPilotFieldGroupInfo* pInfo ) +{ + SolarMutexGuard aGuard; + ScDPObject* pDPObj = nullptr; + if( /*ScDPSaveDimension* pDim =*/ !GetDPDimension( &pDPObj ) ) + return; + + ScDPSaveData* pSaveData = pDPObj->GetSaveData(); + if( pInfo && lclCheckMinMaxStep( *pInfo ) ) + { + ScDPNumGroupInfo aInfo; + aInfo.mbEnable = true; + aInfo.mbDateValues = pInfo->HasDateValues; + aInfo.mbAutoStart = pInfo->HasAutoStart; + aInfo.mbAutoEnd = pInfo->HasAutoEnd; + aInfo.mfStart = pInfo->Start; + aInfo.mfEnd = pInfo->End; + aInfo.mfStep = pInfo->Step; + Reference< XNamed > xNamed( pInfo->SourceField, UNO_QUERY ); + if( xNamed.is() ) + { + ScDPSaveGroupDimension aGroupDim( xNamed->getName(), getName() ); + if( pInfo->GroupBy ) + aGroupDim.SetDateInfo(aInfo, pInfo->GroupBy); + else + { + Reference<XIndexAccess> xIndex(pInfo->Groups, UNO_QUERY); + if (xIndex.is()) + { + sal_Int32 nCount(xIndex->getCount()); + for(sal_Int32 i = 0; i < nCount; i++) + { + Reference<XNamed> xGroupNamed(xIndex->getByIndex(i), UNO_QUERY); + if (xGroupNamed.is()) + { + ScDPSaveGroupItem aItem(xGroupNamed->getName()); + Reference<XIndexAccess> xGroupIndex(xGroupNamed, UNO_QUERY); + if (xGroupIndex.is()) + { + sal_Int32 nItemCount(xGroupIndex->getCount()); + for (sal_Int32 j = 0; j < nItemCount; ++j) + { + Reference<XNamed> xItemNamed(xGroupIndex->getByIndex(j), UNO_QUERY); + if (xItemNamed.is()) + aItem.AddElement(xItemNamed->getName()); + } + } + aGroupDim.AddGroupItem(aItem); + } + } + } + } + + // get dimension savedata or create new if none + ScDPDimensionSaveData& rDimSaveData = *pSaveData->GetDimensionData(); + rDimSaveData.ReplaceGroupDimension( aGroupDim ); + } + else // no source field in group info -> numeric group + { + ScDPDimensionSaveData* pDimData = pSaveData->GetDimensionData(); // created if not there + + ScDPSaveNumGroupDimension* pExisting = pDimData->GetNumGroupDimAcc( getName() ); + if ( pExisting ) + { + if (pInfo->GroupBy) + pExisting->SetDateInfo(aInfo, pInfo->GroupBy); + // modify existing group dimension + pExisting->SetGroupInfo( aInfo ); + } + else if (pInfo->GroupBy) + { + // create new group dimension + ScDPSaveNumGroupDimension aNumGroupDim( getName(), aInfo, pInfo->GroupBy ); + pDimData->AddNumGroupDimension( aNumGroupDim ); + } + else + { + // create new group dimension + ScDPSaveNumGroupDimension aNumGroupDim( getName(), aInfo ); + pDimData->AddNumGroupDimension( aNumGroupDim ); + } + } + } + else // null passed as argument + { + pSaveData->SetDimensionData( nullptr ); + } + + pDPObj->SetSaveData( *pSaveData ); + SetDPObject( pDPObj ); +} + +// XDataPilotFieldGrouping +Reference< XDataPilotField > SAL_CALL ScDataPilotFieldObj::createNameGroup( const Sequence< OUString >& rItems ) +{ + SolarMutexGuard aGuard; + + if( !rItems.hasElements() ) + throw IllegalArgumentException("rItems is empty", static_cast<cppu::OWeakObject*>(this), 0); + + Reference< XMembersAccess > xMembers = GetMembers(); + if (!xMembers.is()) + { + SAL_WARN("sc.ui", "Cannot access members of the field object."); + throw RuntimeException("Cannot access members of the field object", static_cast<cppu::OWeakObject*>(this)); + } + + for (const OUString& aEntryName : rItems) + { + if (!xMembers->hasByName(aEntryName)) + { + SAL_WARN("sc.ui", "There is no member with that name: " + aEntryName + "."); + throw IllegalArgumentException("There is no member with name \"" + aEntryName + "\"", static_cast<cppu::OWeakObject*>(this), 0); + } + } + + Reference< XDataPilotField > xRet; + OUString sNewDim; + ScDPObject* pDPObj = nullptr; + if( ScDPSaveDimension* pDim = GetDPDimension( &pDPObj ) ) + { + const OUString& aDimName = pDim->GetName(); + + ScDPSaveData aSaveData = *pDPObj->GetSaveData(); + ScDPDimensionSaveData* pDimData = aSaveData.GetDimensionData(); // created if not there + + // find original base + OUString aBaseDimName( aDimName ); + const ScDPSaveGroupDimension* pBaseGroupDim = pDimData->GetNamedGroupDim( aDimName ); + if ( pBaseGroupDim ) + { + // any entry's SourceDimName is the original base + aBaseDimName = pBaseGroupDim->GetSourceDimName(); + } + + // find existing group dimension + // (using the selected dim, can be intermediate group dim) + ScDPSaveGroupDimension* pGroupDimension = pDimData->GetGroupDimAccForBase( aDimName ); + + // remove the selected items from their groups + // (empty groups are removed, too) + if ( pGroupDimension ) + { + for (const OUString& aEntryName : rItems) + { + if ( pBaseGroupDim ) + { + // for each selected (intermediate) group, remove all its items + // (same logic as for adding, below) + const ScDPSaveGroupItem* pBaseGroup = pBaseGroupDim->GetNamedGroup( aEntryName ); + if ( pBaseGroup ) + pBaseGroup->RemoveElementsFromGroups( *pGroupDimension ); // remove all elements + else + pGroupDimension->RemoveFromGroups( aEntryName ); + } + else + pGroupDimension->RemoveFromGroups( aEntryName ); + } + } + + std::unique_ptr<ScDPSaveGroupDimension> pNewGroupDim; + if ( !pGroupDimension ) + { + // create a new group dimension + sNewDim = pDimData->CreateGroupDimName( aBaseDimName, *pDPObj, false, nullptr ); + pNewGroupDim.reset(new ScDPSaveGroupDimension( aBaseDimName, sNewDim )); + + pGroupDimension = pNewGroupDim.get(); // make changes to the new dim if none existed + + if ( pBaseGroupDim ) + { + // If it's a higher-order group dimension, pre-allocate groups for all + // non-selected original groups, so the individual base members aren't + // used for automatic groups (this would make the original groups hard + // to find). + //! Also do this when removing groups? + //! Handle this case dynamically with automatic groups? + + tools::Long nGroupCount = pBaseGroupDim->GetGroupCount(); + for ( tools::Long nGroup = 0; nGroup < nGroupCount; nGroup++ ) + { + const ScDPSaveGroupItem& rBaseGroup = pBaseGroupDim->GetGroupByIndex( nGroup ); + + if (comphelper::findValue(rItems, rBaseGroup.GetGroupName()) == -1) //! ignore case? + { + // add an additional group for each item that is not in the selection + ScDPSaveGroupItem aGroup( rBaseGroup.GetGroupName() ); + aGroup.AddElementsFromGroup( rBaseGroup ); + pGroupDimension->AddGroupItem( aGroup ); + } + } + } + } + OUString aGroupDimName = pGroupDimension->GetGroupDimName(); + + OUString aGroupName = pGroupDimension->CreateGroupName( ScResId(STR_PIVOT_GROUP) ); + ScDPSaveGroupItem aGroup( aGroupName ); + for (const OUString& aEntryName : rItems) + { + if ( pBaseGroupDim ) + { + // for each selected (intermediate) group, add all its items + const ScDPSaveGroupItem* pBaseGroup = pBaseGroupDim->GetNamedGroup( aEntryName ); + if ( pBaseGroup ) + aGroup.AddElementsFromGroup( *pBaseGroup ); + else + aGroup.AddElement( aEntryName ); // no group found -> automatic group, add the item itself + } + else + aGroup.AddElement( aEntryName ); // no group dimension, add all items directly + } + + pGroupDimension->AddGroupItem( aGroup ); + + if ( pNewGroupDim ) + { + pDimData->AddGroupDimension( *pNewGroupDim ); + pNewGroupDim.reset(); // AddGroupDimension copies the object + // don't access pGroupDimension after here + } + pGroupDimension = nullptr; + + // set orientation + ScDPSaveDimension* pSaveDimension = aSaveData.GetDimensionByName( aGroupDimName ); + if ( pSaveDimension->GetOrientation() == DataPilotFieldOrientation_HIDDEN ) + { + ScDPSaveDimension* pOldDimension = aSaveData.GetDimensionByName( aDimName ); + pSaveDimension->SetOrientation( pOldDimension->GetOrientation() ); + aSaveData.SetPosition( pSaveDimension, 0 ); //! before (immediate) base + } + + // apply changes + pDPObj->SetSaveData( aSaveData ); + ScDBDocFunc(*GetDocShell()).RefreshPivotTableGroups(pDPObj); + } + + // if new grouping field has been created (on first group), return it + if( !sNewDim.isEmpty() ) + { + Reference< XNameAccess > xFields(mxParent->getDataPilotFields(), UNO_QUERY); + if (xFields.is()) + { + try + { + xRet.set(xFields->getByName(sNewDim), UNO_QUERY); + SAL_WARN_IF(!xRet.is(), "sc.ui", "there is a name, so there should be also a field"); + } + catch (const container::NoSuchElementException&) + { + css::uno::Any anyEx = cppu::getCaughtException(); + SAL_WARN("sc.ui", "Cannot find field with that name: " + sNewDim + "."); + // Avoid throwing exception that's not specified in the method signature. + throw css::lang::WrappedTargetRuntimeException( + "Cannot find field with name \"" + sNewDim + "\"", + static_cast<cppu::OWeakObject*>(this), anyEx ); + } + } + } + return xRet; +} + +Reference < XDataPilotField > SAL_CALL ScDataPilotFieldObj::createDateGroup( const DataPilotFieldGroupInfo& rInfo ) +{ + SolarMutexGuard aGuard; + using namespace ::com::sun::star::sheet::DataPilotFieldGroupBy; + + if( !rInfo.HasDateValues ) + throw IllegalArgumentException("HasDateValues is not set", static_cast<cppu::OWeakObject*>(this), 0); + if( !lclCheckMinMaxStep( rInfo ) ) + throw IllegalArgumentException("min/max/step", static_cast<cppu::OWeakObject*>(this), 0); + + // only a single date flag is allowed + if( (rInfo.GroupBy == 0) || (rInfo.GroupBy > YEARS) || ((rInfo.GroupBy & (rInfo.GroupBy - 1)) != 0) ) + throw IllegalArgumentException("Invalid GroupBy value: " + OUString::number(rInfo.GroupBy), static_cast<cppu::OWeakObject*>(this), 0); + + // step must be zero, if something else than DAYS is specified + if( rInfo.Step >= ((rInfo.GroupBy == DAYS) ? 32768.0 : 1.0) ) + throw IllegalArgumentException("Invalid step value: " + OUString::number(rInfo.Step), static_cast<cppu::OWeakObject*>(this), 0); + + OUString aGroupDimName; + ScDPObject* pDPObj = nullptr; + if( ScDPSaveDimension* pDim = GetDPDimension( &pDPObj ) ) + { + ScDPNumGroupInfo aInfo; + aInfo.mbEnable = true; + aInfo.mbDateValues = (rInfo.GroupBy == DAYS) && (rInfo.Step >= 1.0); + aInfo.mbAutoStart = rInfo.HasAutoStart; + aInfo.mbAutoEnd = rInfo.HasAutoEnd; + aInfo.mfStart = rInfo.Start; + aInfo.mfEnd = rInfo.End; + aInfo.mfStep = std::trunc( rInfo.Step ); + + // create a local copy of the entire save data (will be written back below) + ScDPSaveData aSaveData = *pDPObj->GetSaveData(); + // get or create dimension save data + ScDPDimensionSaveData& rDimData = *aSaveData.GetDimensionData(); + + // find source dimension name + const OUString& rDimName = pDim->GetName(); + const ScDPSaveGroupDimension* pGroupDim = rDimData.GetNamedGroupDim( rDimName ); + OUString aSrcDimName = pGroupDim ? pGroupDim->GetSourceDimName() : rDimName; + + // find a group dimension for the base field, or get numeric grouping + pGroupDim = rDimData.GetFirstNamedGroupDim( aSrcDimName ); + const ScDPSaveNumGroupDimension* pNumGroupDim = rDimData.GetNumGroupDim( aSrcDimName ); + + // do not group by dates, if named groups or numeric grouping is present + bool bHasNamedGrouping = pGroupDim && !pGroupDim->GetDateInfo().mbEnable; + bool bHasNumGrouping = pNumGroupDim && pNumGroupDim->GetInfo().mbEnable && !pNumGroupDim->GetInfo().mbDateValues && !pNumGroupDim->GetDateInfo().mbEnable; + if( bHasNamedGrouping || bHasNumGrouping ) + throw IllegalArgumentException(); + + if( aInfo.mbDateValues ) // create day ranges grouping + { + // first remove all named group dimensions + while( pGroupDim ) + { + OUString aGroupDimName2 = pGroupDim->GetGroupDimName(); + // find next group dimension before deleting this group + pGroupDim = rDimData.GetNextNamedGroupDim( aGroupDimName2 ); + // remove from dimension save data + rDimData.RemoveGroupDimension( aGroupDimName2 ); + // also remove save data settings for the dimension that no longer exists + aSaveData.RemoveDimensionByName( aGroupDimName2 ); + } + // create or replace the number grouping dimension + ScDPSaveNumGroupDimension aNumGroupDim( aSrcDimName, aInfo ); + rDimData.ReplaceNumGroupDimension( aNumGroupDim ); + } + else // create date grouping + { + // collect all existing date flags + sal_Int32 nDateParts = rDimData.CollectDateParts( aSrcDimName ); + if( nDateParts == 0 ) + { + // insert numeric group dimension, if no date groups exist yet (or replace day range grouping) + ScDPSaveNumGroupDimension aNumGroupDim( aSrcDimName, aInfo, rInfo.GroupBy ); + rDimData.ReplaceNumGroupDimension( aNumGroupDim ); + } + else if( (nDateParts & rInfo.GroupBy) == 0 ) // do nothing if date field exists already + { + // create new named group dimension for additional date groups + aGroupDimName = rDimData.CreateDateGroupDimName( rInfo.GroupBy, *pDPObj, true, nullptr ); + ScDPSaveGroupDimension aGroupDim( aSrcDimName, aGroupDimName, aInfo, rInfo.GroupBy ); + rDimData.AddGroupDimension( aGroupDim ); + + // set orientation of new named group dimension + ScDPSaveDimension& rSaveDim = *aSaveData.GetDimensionByName( aGroupDimName ); + if( rSaveDim.GetOrientation() == DataPilotFieldOrientation_HIDDEN ) + { + ScDPSaveDimension& rOldDim = *aSaveData.GetDimensionByName( aSrcDimName ); + rSaveDim.SetOrientation( rOldDim.GetOrientation() ); + aSaveData.SetPosition( &rSaveDim, 0 ); //! before (immediate) base + } + } + } + + // apply changes + pDPObj->SetSaveData( aSaveData ); + ScDBDocFunc(*GetDocShell()).RefreshPivotTableGroups(pDPObj); + } + + // return the UNO object of the new dimension, after writing back saved data + Reference< XDataPilotField > xRet; + if( !aGroupDimName.isEmpty() ) + try + { + Reference< XNameAccess > xFields( mxParent->getDataPilotFields(), UNO_QUERY_THROW ); + xRet.set( xFields->getByName( aGroupDimName ), UNO_QUERY ); + } + catch( Exception& ) + { + } + return xRet; +} + +namespace { + +bool lclExtractGroupMembers( ScFieldGroupMembers& rMembers, const Any& rElement ) +{ + // allow empty value to create a new group + if( !rElement.hasValue() ) + return true; + + // try to extract a simple sequence of strings + Sequence< OUString > aSeq; + if( rElement >>= aSeq ) + { + if( aSeq.hasElements() ) + rMembers.insert( rMembers.end(), std::cbegin(aSeq), std::cend(aSeq) ); + return true; + } + + // try to use XIndexAccess providing objects that support XNamed + Reference< XIndexAccess > xItemsIA( rElement, UNO_QUERY ); + if( xItemsIA.is() ) + { + for( sal_Int32 nIdx = 0, nCount = xItemsIA->getCount(); nIdx < nCount; ++nIdx ) + { + try // getByIndex() should not throw, but we cannot be sure + { + Reference< XNamed > xItemName( xItemsIA->getByIndex( nIdx ), UNO_QUERY_THROW ); + rMembers.push_back( xItemName->getName() ); + } + catch( Exception& ) + { + // ignore exceptions, go ahead with next element in the array + } + } + return true; + } + + // nothing valid inside the Any -> return false + return false; +} + +} // namespace + +ScDataPilotFieldGroupsObj::ScDataPilotFieldGroupsObj( ScFieldGroups&& rGroups ) : + maGroups( std::move(rGroups) ) +{ +} + +ScDataPilotFieldGroupsObj::~ScDataPilotFieldGroupsObj() +{ +} + +// XNameAccess + +Any SAL_CALL ScDataPilotFieldGroupsObj::getByName( const OUString& rName ) +{ + SolarMutexGuard aGuard; + if( implFindByName( rName ) == maGroups.end() ) + throw NoSuchElementException(); + return Any( Reference< XNameAccess >( new ScDataPilotFieldGroupObj( *this, rName ) ) ); +} + +Sequence< OUString > SAL_CALL ScDataPilotFieldGroupsObj::getElementNames() +{ + SolarMutexGuard aGuard; + Sequence< OUString > aSeq; + if( !maGroups.empty() ) + { + aSeq.realloc( static_cast< sal_Int32 >( maGroups.size() ) ); + OUString* pName = aSeq.getArray(); + for( const auto& rGroup : maGroups ) + { + *pName = rGroup.maName; + ++pName; + } + } + return aSeq; +} + +sal_Bool SAL_CALL ScDataPilotFieldGroupsObj::hasByName( const OUString& rName ) +{ + SolarMutexGuard aGuard; + return implFindByName( rName ) != maGroups.end(); +} + +// XNameReplace + +void SAL_CALL ScDataPilotFieldGroupsObj::replaceByName( const OUString& rName, const Any& rElement ) +{ + SolarMutexGuard aGuard; + + if( rName.isEmpty() ) + throw IllegalArgumentException("Name is empty", static_cast<cppu::OWeakObject*>(this), 0); + + ScFieldGroups::iterator aIt = implFindByName( rName ); + if( aIt == maGroups.end() ) + throw NoSuchElementException("Name \"" + rName + "\" not found", static_cast<cppu::OWeakObject*>(this)); + + // read all item names provided by the passed object + ScFieldGroupMembers aMembers; + if( !lclExtractGroupMembers( aMembers, rElement ) ) + throw IllegalArgumentException("Invalid element object", static_cast<cppu::OWeakObject*>(this), 0); + + // copy and forget, faster than vector assignment + aIt->maMembers.swap( aMembers ); +} + +// XNameContainer + +void SAL_CALL ScDataPilotFieldGroupsObj::insertByName( const OUString& rName, const Any& rElement ) +{ + SolarMutexGuard aGuard; + + if( rName.isEmpty() ) + throw IllegalArgumentException("Name is empty", static_cast<cppu::OWeakObject*>(this), 0); + + ScFieldGroups::iterator aIt = implFindByName( rName ); + if( aIt != maGroups.end() ) + throw ElementExistException("Name \"" + rName + "\" already exists", static_cast<cppu::OWeakObject*>(this)); + + // read all item names provided by the passed object + ScFieldGroupMembers aMembers; + if( !lclExtractGroupMembers( aMembers, rElement ) ) + throw IllegalArgumentException("Invalid element object", static_cast<cppu::OWeakObject*>(this), 0); + + // create the new entry if no error has been occurred + maGroups.emplace_back(); + ScFieldGroup& rGroup = maGroups.back(); + rGroup.maName = rName; + rGroup.maMembers.swap( aMembers ); +} + +void SAL_CALL ScDataPilotFieldGroupsObj::removeByName( const OUString& rName ) +{ + SolarMutexGuard aGuard; + + if( rName.isEmpty() ) + throw IllegalArgumentException("Name is empty", static_cast<cppu::OWeakObject*>(this), 0); + + ScFieldGroups::iterator aIt = implFindByName( rName ); + if( aIt == maGroups.end() ) + throw NoSuchElementException("Name \"" + rName + "\" not found", static_cast<cppu::OWeakObject*>(this)); + + maGroups.erase( aIt ); +} + +// XIndexAccess + +sal_Int32 SAL_CALL ScDataPilotFieldGroupsObj::getCount() +{ + SolarMutexGuard aGuard; + return static_cast< sal_Int32 >( maGroups.size() ); +} + +Any SAL_CALL ScDataPilotFieldGroupsObj::getByIndex( sal_Int32 nIndex ) +{ + SolarMutexGuard aGuard; + if ((nIndex < 0) || (o3tl::make_unsigned(nIndex) >= maGroups.size())) + throw IndexOutOfBoundsException(); + return Any( Reference< XNameAccess >( new ScDataPilotFieldGroupObj( *this, maGroups[ nIndex ].maName ) ) ); +} + +// XEnumerationAccess + +Reference<XEnumeration> SAL_CALL ScDataPilotFieldGroupsObj::createEnumeration() +{ + SolarMutexGuard aGuard; + return new ScIndexEnumeration( this, "com.sun.star.sheet.DataPilotFieldGroupsEnumeration" ); +} + +// XElementAccess + +uno::Type SAL_CALL ScDataPilotFieldGroupsObj::getElementType() +{ + return cppu::UnoType<XNameAccess>::get(); +} + +sal_Bool SAL_CALL ScDataPilotFieldGroupsObj::hasElements() +{ + SolarMutexGuard aGuard; + return !maGroups.empty(); +} + +// implementation + +ScFieldGroup& ScDataPilotFieldGroupsObj::getFieldGroup( const OUString& rName ) +{ + SolarMutexGuard aGuard; + ScFieldGroups::iterator aIt = implFindByName( rName ); + if( aIt == maGroups.end() ) + throw RuntimeException("Field Group with name \"" + rName + "\" not found", static_cast<cppu::OWeakObject*>(this)); + return *aIt; +} + +void ScDataPilotFieldGroupsObj::renameFieldGroup( const OUString& rOldName, const OUString& rNewName ) +{ + SolarMutexGuard aGuard; + ScFieldGroups::iterator aOldIt = implFindByName( rOldName ); + ScFieldGroups::iterator aNewIt = implFindByName( rNewName ); + if( aOldIt == maGroups.end() ) + throw RuntimeException("Field Group with name \"" + rOldName + "\" not found", static_cast<cppu::OWeakObject*>(this)); + // new name must not exist yet + if( (aNewIt != maGroups.end()) && (aNewIt != aOldIt) ) + throw RuntimeException("Field Group with name \"" + rOldName + "\" already exists", static_cast<cppu::OWeakObject*>(this)); + aOldIt->maName = rNewName; +} + +ScFieldGroups::iterator ScDataPilotFieldGroupsObj::implFindByName( const OUString& rName ) +{ + return std::find_if(maGroups.begin(), maGroups.end(), + [&rName](const ScFieldGroup& rGroup) { return rGroup.maName == rName; }); +} + +namespace { + +OUString lclExtractMember( const Any& rElement ) +{ + if( rElement.has< OUString >() ) + return rElement.get< OUString >(); + + Reference< XNamed > xNamed( rElement, UNO_QUERY ); + if( xNamed.is() ) + return xNamed->getName(); + + return OUString(); +} + +} // namespace + +ScDataPilotFieldGroupObj::ScDataPilotFieldGroupObj( ScDataPilotFieldGroupsObj& rParent, const OUString& rGroupName ) : + mxParent( &rParent ), + maGroupName( rGroupName ) +{ +} + +ScDataPilotFieldGroupObj::~ScDataPilotFieldGroupObj() +{ +} + +// XNameAccess + +Any SAL_CALL ScDataPilotFieldGroupObj::getByName( const OUString& rName ) +{ + SolarMutexGuard aGuard; + ScFieldGroupMembers& rMembers = mxParent->getFieldGroup( maGroupName ).maMembers; + ScFieldGroupMembers::iterator aIt = ::std::find( rMembers.begin(), rMembers.end(), rName ); + if( aIt == rMembers.end() ) + throw NoSuchElementException("Name \"" + rName + "\" not found", static_cast<cppu::OWeakObject*>(this)); + return Any( Reference< XNamed >( new ScDataPilotFieldGroupItemObj( *this, *aIt ) ) ); +} + +Sequence< OUString > SAL_CALL ScDataPilotFieldGroupObj::getElementNames() +{ + SolarMutexGuard aGuard; + return ::comphelper::containerToSequence( mxParent->getFieldGroup( maGroupName ).maMembers ); +} + +sal_Bool SAL_CALL ScDataPilotFieldGroupObj::hasByName( const OUString& rName ) +{ + SolarMutexGuard aGuard; + ScFieldGroupMembers& rMembers = mxParent->getFieldGroup( maGroupName ).maMembers; + return ::std::find( rMembers.begin(), rMembers.end(), rName ) != rMembers.end(); +} + +// XNameReplace + +void SAL_CALL ScDataPilotFieldGroupObj::replaceByName( const OUString& rName, const Any& rElement ) +{ + SolarMutexGuard aGuard; + + // it should be possible to quickly rename an item -> accept string or XNamed + OUString aNewName = lclExtractMember( rElement ); + if( rName.isEmpty() || aNewName.isEmpty() ) + throw IllegalArgumentException("Name is empty", static_cast<cppu::OWeakObject*>(this), 0); + if( rName == aNewName ) + return; + + ScFieldGroupMembers& rMembers = mxParent->getFieldGroup( maGroupName ).maMembers; + ScFieldGroupMembers::iterator aOldIt = ::std::find( rMembers.begin(), rMembers.end(), rName ); + ScFieldGroupMembers::iterator aNewIt = ::std::find( rMembers.begin(), rMembers.end(), aNewName ); + if( aOldIt == rMembers.end() ) + throw NoSuchElementException("Name \"" + rName + "\" not found", static_cast<cppu::OWeakObject*>(this)); + if( aNewIt != rMembers.end() ) + throw IllegalArgumentException("Name \"" + rName + "\" already exists", static_cast<cppu::OWeakObject*>(this), 0); + *aOldIt = aNewName; +} + +// XNameContainer + +void SAL_CALL ScDataPilotFieldGroupObj::insertByName( const OUString& rName, const Any& /*rElement*/ ) +{ + SolarMutexGuard aGuard; + + // we will ignore the passed element and just try to insert the name + if( rName.isEmpty() ) + throw IllegalArgumentException("Name is empty", static_cast<cppu::OWeakObject*>(this), 0); + + ScFieldGroupMembers& rMembers = mxParent->getFieldGroup( maGroupName ).maMembers; + ScFieldGroupMembers::iterator aIt = ::std::find( rMembers.begin(), rMembers.end(), rName ); + if( aIt != rMembers.end() ) + throw IllegalArgumentException("Name \"" + rName + "\" already exists", static_cast<cppu::OWeakObject*>(this), 0); + rMembers.push_back( rName ); +} + +void SAL_CALL ScDataPilotFieldGroupObj::removeByName( const OUString& rName ) +{ + SolarMutexGuard aGuard; + + if( rName.isEmpty() ) + throw IllegalArgumentException("Name is empty", static_cast<cppu::OWeakObject*>(this), 0); + ScFieldGroupMembers& rMembers = mxParent->getFieldGroup( maGroupName ).maMembers; + ScFieldGroupMembers::iterator aIt = ::std::find( rMembers.begin(), rMembers.end(), rName ); + if( aIt == rMembers.end() ) + throw NoSuchElementException("Name \"" + rName + "\" not found", static_cast<cppu::OWeakObject*>(this)); + rMembers.erase( aIt ); +} + +// XIndexAccess + +sal_Int32 SAL_CALL ScDataPilotFieldGroupObj::getCount() +{ + SolarMutexGuard aGuard; + return static_cast< sal_Int32 >( mxParent->getFieldGroup( maGroupName ).maMembers.size() ); +} + +Any SAL_CALL ScDataPilotFieldGroupObj::getByIndex( sal_Int32 nIndex ) +{ + SolarMutexGuard aGuard; + ScFieldGroupMembers& rMembers = mxParent->getFieldGroup( maGroupName ).maMembers; + if ((nIndex < 0) || (o3tl::make_unsigned(nIndex) >= rMembers.size())) + throw IndexOutOfBoundsException(); + return Any( Reference< XNamed >( new ScDataPilotFieldGroupItemObj( *this, rMembers[ nIndex ] ) ) ); +} + +// XEnumerationAccess + +Reference< XEnumeration > SAL_CALL ScDataPilotFieldGroupObj::createEnumeration() +{ + SolarMutexGuard aGuard; + return new ScIndexEnumeration( this, "com.sun.star.sheet.DataPilotFieldGroupEnumeration" ); +} + +// XElementAccess + +uno::Type SAL_CALL ScDataPilotFieldGroupObj::getElementType() +{ + return cppu::UnoType<XNamed>::get(); +} + +sal_Bool SAL_CALL ScDataPilotFieldGroupObj::hasElements() +{ + SolarMutexGuard aGuard; + return !mxParent->getFieldGroup( maGroupName ).maMembers.empty(); +} + +// XNamed + +OUString SAL_CALL ScDataPilotFieldGroupObj::getName() +{ + SolarMutexGuard aGuard; + return maGroupName; +} + +void SAL_CALL ScDataPilotFieldGroupObj::setName( const OUString& rName ) +{ + SolarMutexGuard aGuard; + mxParent->renameFieldGroup( maGroupName, rName ); + // if call to renameFieldGroup() did not throw, remember the new name + maGroupName = rName; +} + +ScDataPilotFieldGroupItemObj::ScDataPilotFieldGroupItemObj( ScDataPilotFieldGroupObj& rParent, const OUString& rName ) : + mxParent( &rParent ), + maName( rName ) +{ +} + +ScDataPilotFieldGroupItemObj::~ScDataPilotFieldGroupItemObj() +{ +} + +// XNamed + +OUString SAL_CALL ScDataPilotFieldGroupItemObj::getName() +{ + SolarMutexGuard aGuard; + return maName; +} + +void SAL_CALL ScDataPilotFieldGroupItemObj::setName( const OUString& rName ) +{ + SolarMutexGuard aGuard; + mxParent->replaceByName( maName, Any( rName ) ); + // if call to replaceByName() did not throw, remember the new name + maName = rName; +} + +ScDataPilotItemsObj::ScDataPilotItemsObj( ScDataPilotDescriptorBase& rParent, const ScFieldIdentifier& rFieldId ) : + ScDataPilotChildObjBase( rParent, rFieldId ) +{ +} + +ScDataPilotItemsObj::~ScDataPilotItemsObj() +{ +} + +// XDataPilotItems + +ScDataPilotItemObj* ScDataPilotItemsObj::GetObjectByIndex_Impl( sal_Int32 nIndex ) const +{ + return ((0 <= nIndex) && (nIndex < GetMemberCount())) ? + new ScDataPilotItemObj( *mxParent, maFieldId, nIndex ) : nullptr; +} + +// XNameAccess + +Any SAL_CALL ScDataPilotItemsObj::getByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + Reference<XNameAccess> xMembers = GetMembers(); + if (xMembers.is()) + { + Reference<XIndexAccess> xMembersIndex(new ScNameToIndexAccess( xMembers )); + sal_Int32 nCount = xMembersIndex->getCount(); + sal_Int32 nItem = 0; + while (nItem < nCount) + { + Reference<XNamed> xMember(xMembersIndex->getByIndex(nItem), UNO_QUERY); + if (xMember.is() && (aName == xMember->getName())) + { + return Any( Reference< XPropertySet >( GetObjectByIndex_Impl( nItem ) ) ); + } + ++nItem; + } + throw NoSuchElementException("Name \"" + aName + "\" not found", static_cast<cppu::OWeakObject*>(this)); + } + return Any(); +} + +Sequence<OUString> SAL_CALL ScDataPilotItemsObj::getElementNames() +{ + SolarMutexGuard aGuard; + Sequence< OUString > aSeq; + if( ScDPObject* pDPObj = GetDPObject() ) + pDPObj->GetMemberNames( lcl_GetObjectIndex( pDPObj, maFieldId ), aSeq ); + return aSeq; +} + +sal_Bool SAL_CALL ScDataPilotItemsObj::hasByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + bool bFound = false; + Reference<XNameAccess> xMembers = GetMembers(); + if (xMembers.is()) + { + Reference<XIndexAccess> xMembersIndex(new ScNameToIndexAccess( xMembers )); + sal_Int32 nCount = xMembersIndex->getCount(); + sal_Int32 nItem = 0; + while (nItem < nCount && !bFound ) + { + Reference<XNamed> xMember(xMembersIndex->getByIndex(nItem), UNO_QUERY); + if (xMember.is() && aName == xMember->getName()) + bFound = true; + else + nItem++; + } + } + return bFound; +} + +// XEnumerationAccess + +Reference<XEnumeration> SAL_CALL ScDataPilotItemsObj::createEnumeration() +{ + SolarMutexGuard aGuard; + return new ScIndexEnumeration(this, "com.sun.star.sheet.DataPilotItemsEnumeration"); +} + +// XIndexAccess + +sal_Int32 SAL_CALL ScDataPilotItemsObj::getCount() +{ + SolarMutexGuard aGuard; + return GetMemberCount(); +} + +Any SAL_CALL ScDataPilotItemsObj::getByIndex( sal_Int32 nIndex ) +{ + SolarMutexGuard aGuard; + Reference< XPropertySet > xItem( GetObjectByIndex_Impl( nIndex ) ); + if (!xItem.is()) + throw IndexOutOfBoundsException(); + return Any( xItem ); +} + +uno::Type SAL_CALL ScDataPilotItemsObj::getElementType() +{ + return cppu::UnoType<XPropertySet>::get(); +} + +sal_Bool SAL_CALL ScDataPilotItemsObj::hasElements() +{ + SolarMutexGuard aGuard; + return ( getCount() != 0 ); +} + +ScDataPilotItemObj::ScDataPilotItemObj( ScDataPilotDescriptorBase& rParent, const ScFieldIdentifier& rFieldId, sal_Int32 nIndex ) : + ScDataPilotChildObjBase( rParent, rFieldId ), + maPropSet( lcl_GetDataPilotItemMap() ), + mnIndex( nIndex ) +{ +} + +ScDataPilotItemObj::~ScDataPilotItemObj() +{ +} + + // XNamed +OUString SAL_CALL ScDataPilotItemObj::getName() +{ + SolarMutexGuard aGuard; + OUString sRet; + Reference<XNameAccess> xMembers = GetMembers(); + if (xMembers.is()) + { + Reference<XIndexAccess> xMembersIndex(new ScNameToIndexAccess( xMembers )); + sal_Int32 nCount = xMembersIndex->getCount(); + if (mnIndex < nCount) + { + Reference<XNamed> xMember(xMembersIndex->getByIndex(mnIndex), UNO_QUERY); + sRet = xMember->getName(); + } + } + return sRet; +} + +void SAL_CALL ScDataPilotItemObj::setName( const OUString& /* aName */ ) +{ +} + + // XPropertySet +Reference< XPropertySetInfo > + SAL_CALL ScDataPilotItemObj::getPropertySetInfo( ) +{ + SolarMutexGuard aGuard; + static Reference<XPropertySetInfo> aRef = + new SfxItemPropertySetInfo( maPropSet.getPropertyMap() ); + return aRef; +} + +void SAL_CALL ScDataPilotItemObj::setPropertyValue( const OUString& aPropertyName, const Any& aValue ) +{ + SolarMutexGuard aGuard; + ScDPObject* pDPObj = nullptr; + ScDPSaveDimension* pDim = GetDPDimension( &pDPObj ); + if(!pDim) + return; + + Reference<XNameAccess> xMembers = GetMembers(); + if( !xMembers.is() ) + return; + + Reference<XIndexAccess> xMembersIndex( new ScNameToIndexAccess( xMembers ) ); + sal_Int32 nCount = xMembersIndex->getCount(); + if( mnIndex >= nCount ) + return; + + Reference<XNamed> xMember(xMembersIndex->getByIndex(mnIndex), UNO_QUERY); + OUString sName(xMember->getName()); + ScDPSaveMember* pMember = pDim->GetMemberByName(sName); + if (!pMember) + return; + + bool bGetNewIndex = false; + if ( aPropertyName == SC_UNONAME_SHOWDETAIL ) + pMember->SetShowDetails(cppu::any2bool(aValue)); + else if ( aPropertyName == SC_UNONAME_ISHIDDEN ) + pMember->SetIsVisible(!cppu::any2bool(aValue)); + else if ( aPropertyName == SC_UNONAME_POS ) + { + sal_Int32 nNewPos = 0; + if ( !( aValue >>= nNewPos ) || nNewPos < 0 || nNewPos >= nCount ) + throw IllegalArgumentException(); + + pDim->SetMemberPosition( sName, nNewPos ); + // get new effective index (depends on sorting mode, which isn't modified) + bGetNewIndex = true; + + } + SetDPObject( pDPObj ); + + if ( bGetNewIndex ) // after SetDPObject, get the new index + { + Sequence< OUString > aItemNames = xMembers->getElementNames(); + sal_Int32 nItemCount = aItemNames.getLength(); + for (sal_Int32 nItem=0; nItem<nItemCount; ++nItem) + if (aItemNames[nItem] == sName) + mnIndex = nItem; + } +} + +Any SAL_CALL ScDataPilotItemObj::getPropertyValue( const OUString& aPropertyName ) +{ + SolarMutexGuard aGuard; + Any aRet; + if( ScDPSaveDimension* pDim = GetDPDimension() ) + { + Reference< XNameAccess > xMembers = GetMembers(); + if( xMembers.is() ) + { + Reference< XIndexAccess > xMembersIndex( new ScNameToIndexAccess( xMembers ) ); + sal_Int32 nCount = xMembersIndex->getCount(); + if( mnIndex < nCount ) + { + Reference< XNamed > xMember( xMembersIndex->getByIndex( mnIndex ), UNO_QUERY ); + OUString sName( xMember->getName() ); + ScDPSaveMember* pMember = pDim->GetExistingMemberByName( sName ); + if ( aPropertyName == SC_UNONAME_SHOWDETAIL ) + { + if (pMember && pMember->HasShowDetails()) + { + aRet <<= pMember->GetShowDetails(); + } + else + { + Reference< XPropertySet > xMemberProps( xMember, UNO_QUERY ); + if( xMemberProps.is() ) + aRet = xMemberProps->getPropertyValue( SC_UNO_DP_SHOWDETAILS ); + else + aRet <<= true; + } + } + else if ( aPropertyName == SC_UNONAME_ISHIDDEN ) + { + if (pMember && pMember->HasIsVisible()) + { + aRet <<= !pMember->GetIsVisible(); + } + else + { + Reference< XPropertySet > xMemberProps( xMember, UNO_QUERY ); + if( xMemberProps.is() ) + aRet <<= !cppu::any2bool( xMemberProps->getPropertyValue( SC_UNO_DP_ISVISIBLE ) ); + else + aRet <<= false; + } + } + else if ( aPropertyName == SC_UNONAME_POS ) + { + aRet <<= mnIndex; + } + } + } + } + return aRet; +} + +void SAL_CALL ScDataPilotItemObj::addPropertyChangeListener( + const OUString& /* aPropertyName */, const Reference< XPropertyChangeListener >& /* xListener */ ) +{ +} + +void SAL_CALL ScDataPilotItemObj::removePropertyChangeListener( + const OUString& /* aPropertyName */, const Reference< XPropertyChangeListener >& /* aListener */ ) +{ +} + +void SAL_CALL ScDataPilotItemObj::addVetoableChangeListener( + const OUString& /* PropertyName */, const Reference< XVetoableChangeListener >& /* aListener */ ) +{ +} + +void SAL_CALL ScDataPilotItemObj::removeVetoableChangeListener( + const OUString& /* PropertyName */, const Reference< XVetoableChangeListener >& /* aListener */ ) +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |