diff options
Diffstat (limited to 'sc/source/ui/unoobj/PivotTableDataProvider.cxx')
-rw-r--r-- | sc/source/ui/unoobj/PivotTableDataProvider.cxx | 904 |
1 files changed, 904 insertions, 0 deletions
diff --git a/sc/source/ui/unoobj/PivotTableDataProvider.cxx b/sc/source/ui/unoobj/PivotTableDataProvider.cxx new file mode 100644 index 000000000..f0a9cdb8e --- /dev/null +++ b/sc/source/ui/unoobj/PivotTableDataProvider.cxx @@ -0,0 +1,904 @@ +/* -*- 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/. + */ + +#include <limits> +#include <memory> +#include <sal/config.h> + +#include <PivotTableDataProvider.hxx> +#include <PivotTableDataSource.hxx> +#include <PivotTableDataSequence.hxx> + +#include <miscuno.hxx> +#include <document.hxx> +#include <unonames.hxx> +#include <scresid.hxx> +#include <globstr.hrc> +#include <strings.hrc> +#include <dpobject.hxx> +#include <hints.hxx> + +#include <o3tl/safeint.hxx> +#include <vcl/svapp.hxx> +#include <sfx2/objsh.hxx> +#include <comphelper/propertysequence.hxx> +#include <comphelper/sequence.hxx> +#include <comphelper/processfactory.hxx> + +#include <com/sun/star/chart2/data/LabeledDataSequence.hpp> +#include <com/sun/star/chart/ChartDataRowSource.hpp> +#include <com/sun/star/frame/XModel.hpp> + +#include <com/sun/star/sheet/XDataPilotResults.hpp> +#include <com/sun/star/sheet/DataResultFlags.hpp> + +#include <com/sun/star/sheet/XDimensionsSupplier.hpp> +#include <com/sun/star/sheet/XHierarchiesSupplier.hpp> +#include <com/sun/star/sheet/XLevelsSupplier.hpp> +#include <com/sun/star/sheet/XDataPilotMemberResults.hpp> +#include <com/sun/star/sheet/MemberResultFlags.hpp> +#include <com/sun/star/sheet/XMembersSupplier.hpp> + +#include <com/sun/star/chart/ChartDataChangeEvent.hpp> +#include <com/sun/star/container/XNamed.hpp> + +#include <unordered_map> + +using namespace css; + +namespace sc +{ +namespace +{ +constexpr OUStringLiteral constIdCategories(u"categories"); +constexpr OUStringLiteral constIdLabel(u"label"); +constexpr OUStringLiteral constIdData(u"data"); + +const SfxItemPropertyMapEntry* lcl_GetDataProviderPropertyMap() +{ + static const SfxItemPropertyMapEntry aDataProviderPropertyMap_Impl[] = + { + { SC_UNONAME_INCLUDEHIDDENCELLS, 0, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_USE_INTERNAL_DATA_PROVIDER, 0, cppu::UnoType<bool>::get(), 0, 0 }, + { u"", 0, css::uno::Type(), 0, 0 } + }; + return aDataProviderPropertyMap_Impl; +} + +uno::Reference<frame::XModel> lcl_GetXModel(const ScDocument * pDoc) +{ + uno::Reference<frame::XModel> xModel; + SfxObjectShell* pObjSh(pDoc ? pDoc->GetDocumentShell() : nullptr); + if (pObjSh) + xModel.set(pObjSh->GetModel()); + return xModel; +} + +OUString lcl_identifierForData(sal_Int32 index) +{ + return "PT@" + constIdData + " " + OUString::number(index); +} + +OUString lcl_identifierForLabel(sal_Int32 index) +{ + return "PT@" + constIdLabel + " " + OUString::number(index); +} + +OUString lcl_identifierForCategories() +{ + return "PT@" + constIdCategories; +} + +std::vector<OUString> lcl_getVisiblePageMembers(const uno::Reference<uno::XInterface> & xLevel) +{ + std::vector<OUString> aResult; + if (!xLevel.is()) + return aResult; + + uno::Reference<sheet::XMembersSupplier> xMembersSupplier(xLevel, uno::UNO_QUERY); + if (!xMembersSupplier.is()) + return aResult; + + uno::Reference<sheet::XMembersAccess> xMembersAccess = xMembersSupplier->getMembers(); + if (!xMembersAccess.is()) + return aResult; + + const css::uno::Sequence<OUString> aMembersNames = xMembersAccess->getElementNames(); + for (OUString const & rMemberNames : aMembersNames) + { + uno::Reference<beans::XPropertySet> xProperties(xMembersAccess->getByName(rMemberNames), uno::UNO_QUERY); + if (!xProperties.is()) + continue; + + OUString aCaption = ScUnoHelpFunctions::GetStringProperty(xProperties, SC_UNO_DP_LAYOUTNAME, OUString()); + if (aCaption.isEmpty()) + aCaption = rMemberNames; + + bool bVisible = ScUnoHelpFunctions::GetBoolProperty(xProperties, SC_UNO_DP_ISVISIBLE); + + if (bVisible) + aResult.push_back(aCaption); + } + + return aResult; +} + +} // end anonymous namespace + +SC_SIMPLE_SERVICE_INFO(PivotTableDataProvider, "PivotTableDataProvider", SC_SERVICENAME_CHART_PIVOTTABLE_DATAPROVIDER) + +// DataProvider ============================================================== + +PivotTableDataProvider::PivotTableDataProvider(ScDocument& rDoc) + : m_pDocument(&rDoc) + , m_aPropSet(lcl_GetDataProviderPropertyMap()) + , m_bIncludeHiddenCells(true) + , m_bNeedsUpdate(true) + , m_xContext(comphelper::getProcessComponentContext()) +{ + rDoc.AddUnoObject(*this); +} + +PivotTableDataProvider::~PivotTableDataProvider() +{ + SolarMutexGuard g; + + if (m_pDocument) + m_pDocument->RemoveUnoObject( *this); +} + +void PivotTableDataProvider::Notify(SfxBroadcaster& /*rBroadcaster*/, const SfxHint& rHint) +{ + if (rHint.GetId() == SfxHintId::Dying) + { + m_pDocument = nullptr; + } + else if (m_pDocument) + { + if (auto pDataPilotHint = dynamic_cast<const ScDataPilotModifiedHint*>(&rHint)) + { + if (pDataPilotHint->GetName() == m_sPivotTableName) + { + m_bNeedsUpdate = true; + for (uno::Reference<util::XModifyListener> const & xListener : m_aValueListeners) + { + css::chart::ChartDataChangeEvent aEvent(static_cast<cppu::OWeakObject*>(this), + css::chart::ChartDataChangeType_ALL, + 0, 0, 0, 0); + xListener->modified(aEvent); + } + } + } + } +} + +sal_Bool SAL_CALL PivotTableDataProvider::createDataSourcePossible(const uno::Sequence<beans::PropertyValue>& /*aArguments*/) +{ + SolarMutexGuard aGuard; + if (!m_pDocument) + return false; + + if (m_sPivotTableName.isEmpty()) + return false; + + ScDPCollection* pDPCollection = m_pDocument->GetDPCollection(); + return bool(pDPCollection->GetByName(m_sPivotTableName)); +} + +uno::Reference<chart2::data::XDataSource> SAL_CALL + PivotTableDataProvider::createDataSource(const uno::Sequence<beans::PropertyValue>& aArguments) +{ + SolarMutexGuard aGuard; + + if (!m_pDocument) + throw uno::RuntimeException(); + + bool bOrientCol = true; + OUString aRangeRepresentation; + + for (beans::PropertyValue const & rProperty : aArguments) + { + if (rProperty.Name == "DataRowSource") + { + chart::ChartDataRowSource eSource = chart::ChartDataRowSource_COLUMNS; + if (!(rProperty.Value >>= eSource)) + { + sal_Int32 nSource(0); + if (rProperty.Value >>= nSource) + eSource = chart::ChartDataRowSource(nSource); + } + bOrientCol = (eSource == chart::ChartDataRowSource_COLUMNS); + } + else if (rProperty.Name == "CellRangeRepresentation") + rProperty.Value >>= aRangeRepresentation; + } + + uno::Reference<chart2::data::XDataSource> xResult; + + if (aRangeRepresentation == lcl_identifierForCategories()) + xResult = createCategoriesDataSource(bOrientCol); + else + xResult = createValuesDataSource(); + + return xResult; +} + +uno::Reference<chart2::data::XLabeledDataSequence> + PivotTableDataProvider::newLabeledDataSequence() +{ + uno::Reference<chart2::data::XLabeledDataSequence> xResult; + if (!m_xContext.is()) + return xResult; + xResult.set(chart2::data::LabeledDataSequence::create(m_xContext), uno::UNO_QUERY_THROW); + return xResult; +} + +uno::Reference<chart2::data::XDataSource> +PivotTableDataProvider::createCategoriesDataSource(bool bOrientationIsColumn) +{ + if (m_bNeedsUpdate) + collectPivotTableData(); + + uno::Reference<chart2::data::XDataSource> xDataSource; + std::vector<uno::Reference<chart2::data::XLabeledDataSequence>> aLabeledSequences; + + std::vector<std::vector<ValueAndFormat>> const & rCategoriesVector = bOrientationIsColumn ? m_aCategoriesColumnOrientation + : m_aCategoriesRowOrientation; + + for (std::vector<ValueAndFormat> const & rCategories : rCategoriesVector) + { + uno::Reference<chart2::data::XLabeledDataSequence> xResult = newLabeledDataSequence(); + rtl::Reference<PivotTableDataSequence> pSequence(new PivotTableDataSequence(m_pDocument, + lcl_identifierForCategories(), std::vector(rCategories))); + pSequence->setRole("categories"); + xResult->setValues(uno::Reference<chart2::data::XDataSequence>(pSequence)); + + aLabeledSequences.push_back(xResult); + } + + xDataSource.set(new PivotTableDataSource(std::move(aLabeledSequences))); + return xDataSource; +} + +void PivotTableDataProvider::collectPivotTableData() +{ + ScDPCollection* pDPCollection = m_pDocument->GetDPCollection(); + ScDPObject* pDPObject = pDPCollection->GetByName(m_sPivotTableName); + if (!pDPObject) + return; + + m_aCategoriesColumnOrientation.clear(); + m_aCategoriesRowOrientation.clear(); + m_aLabels.clear(); + m_aDataRowVector.clear(); + m_aColumnFields.clear(); + m_aRowFields.clear(); + m_aPageFields.clear(); + m_aDataFields.clear(); + m_aFieldOutputDescriptionMap.clear(); + + uno::Reference<sheet::XDataPilotResults> xDPResults(pDPObject->GetSource(), uno::UNO_QUERY); + if (!xDPResults.is()) + return; + const uno::Sequence<uno::Sequence<sheet::DataResult>> xDataResultsSequence = xDPResults->getResults(); + + std::unordered_set<size_t> aValidRowIndex; + + size_t nRowIndex = 0; + for (uno::Sequence<sheet::DataResult> const & xDataResults : xDataResultsSequence) + { + std::vector<ValueAndFormat> aRow; + bool bRowEmpty = true; + // First pass - collect a row of valid data and track if the row is empty + for (sheet::DataResult const & rDataResult : xDataResults) + { + if (rDataResult.Flags & css::sheet::DataResultFlags::SUBTOTAL) + continue; + if (rDataResult.Flags == 0 || rDataResult.Flags & css::sheet::DataResultFlags::HASDATA) + { + aRow.emplace_back(rDataResult.Flags ? rDataResult.Value + : std::numeric_limits<double>::quiet_NaN(), 0); + if (rDataResult.Flags != 0) // set as valid only if we have data + { + bRowEmpty = false; + // We need to remember all valid (non-empty) row indices + aValidRowIndex.insert(nRowIndex); + } + } + } + // Second pass: add to collection only non-empty rows + if (!bRowEmpty) + { + size_t nColumnIndex = 0; + for (ValueAndFormat const & aValue : aRow) + { + if (nColumnIndex >= m_aDataRowVector.size()) + m_aDataRowVector.resize(nColumnIndex + 1); + m_aDataRowVector[nColumnIndex].push_back(aValue); + nColumnIndex++; + } + } + nRowIndex++; + } + + uno::Reference<sheet::XDimensionsSupplier> xDimensionsSupplier(pDPObject->GetSource()); + uno::Reference<container::XIndexAccess> xDims = new ScNameToIndexAccess(xDimensionsSupplier->getDimensions()); + + std::unordered_map<OUString, sal_Int32> aDataFieldNumberFormatMap; + std::vector<OUString> aDataFieldNamesVectors; + + std::unordered_map<OUString, OUString> aDataFieldCaptionNames; + + sheet::DataPilotFieldOrientation eDataFieldOrientation = sheet::DataPilotFieldOrientation_HIDDEN; + + for (sal_Int32 nDim = 0; nDim < xDims->getCount(); nDim++) + { + uno::Reference<uno::XInterface> xDim(xDims->getByIndex(nDim), uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xDimProp(xDim, uno::UNO_QUERY); + uno::Reference<sheet::XHierarchiesSupplier> xDimSupp(xDim, uno::UNO_QUERY); + + if (!xDimProp.is() || !xDimSupp.is()) + continue; + + sheet::DataPilotFieldOrientation eDimOrient = + ScUnoHelpFunctions::GetEnumProperty(xDimProp, SC_UNO_DP_ORIENTATION, + sheet::DataPilotFieldOrientation_HIDDEN); + + if (eDimOrient == sheet::DataPilotFieldOrientation_HIDDEN) + continue; + + uno::Reference<container::XIndexAccess> xHierarchies = new ScNameToIndexAccess(xDimSupp->getHierarchies()); + sal_Int32 nHierarchy = ScUnoHelpFunctions::GetLongProperty(xDimProp, SC_UNO_DP_USEDHIERARCHY); + if (nHierarchy >= xHierarchies->getCount()) + nHierarchy = 0; + + uno::Reference<sheet::XLevelsSupplier> xLevelsSupplier(xHierarchies->getByIndex(nHierarchy), + uno::UNO_QUERY); + + if (!xLevelsSupplier.is()) + continue; + + uno::Reference<container::XIndexAccess> xLevels = new ScNameToIndexAccess(xLevelsSupplier->getLevels()); + + for (tools::Long nLevel = 0; nLevel < xLevels->getCount(); nLevel++) + { + uno::Reference<uno::XInterface> xLevel(xLevels->getByIndex(nLevel), uno::UNO_QUERY); + uno::Reference<container::XNamed> xLevelName(xLevel, uno::UNO_QUERY); + uno::Reference<sheet::XDataPilotMemberResults> xLevelResult(xLevel, uno::UNO_QUERY ); + + if (xLevelName.is() && xLevelResult.is()) + { + bool bIsDataLayout = ScUnoHelpFunctions::GetBoolProperty(xDimProp, SC_UNO_DP_ISDATALAYOUT); + sal_Int32 nDimPos = ScUnoHelpFunctions::GetLongProperty(xDimProp, SC_UNO_DP_POSITION); + sal_Int32 nNumberFormat = ScUnoHelpFunctions::GetLongProperty(xDimProp, SC_UNO_DP_NUMBERFO); + bool bHasHiddenMember = ScUnoHelpFunctions::GetBoolProperty(xDimProp, SC_UNO_DP_HAS_HIDDEN_MEMBER); + + switch (eDimOrient) + { + case sheet::DataPilotFieldOrientation_COLUMN: + { + m_aColumnFields.emplace_back(xLevelName->getName(), nDim, nDimPos, bHasHiddenMember); + + const uno::Sequence<sheet::MemberResult> aSequence = xLevelResult->getResults(); + size_t i = 0; + OUString sCaption; + OUString sName; + for (sheet::MemberResult const & rMember : aSequence) + { + // Skip grandtotals and subtotals + if (rMember.Flags & sheet::MemberResultFlags::SUBTOTAL || + rMember.Flags & sheet::MemberResultFlags::GRANDTOTAL) + continue; + if (rMember.Flags & sheet::MemberResultFlags::HASMEMBER || + rMember.Flags & sheet::MemberResultFlags::CONTINUE) + { + if (!(rMember.Flags & sheet::MemberResultFlags::CONTINUE)) + { + sCaption = rMember.Caption; + sName = rMember.Name; + } + + if (i >= m_aLabels.size()) + m_aLabels.resize(i + 1); + + if (o3tl::make_unsigned(nDimPos) >= m_aLabels[i].size()) + m_aLabels[i].resize(nDimPos + 1); + m_aLabels[i][nDimPos] = ValueAndFormat(sCaption); + + if (bIsDataLayout) + { + // Remember data fields to determine the number format of data + aDataFieldNamesVectors.push_back(sName); + eDataFieldOrientation = sheet::DataPilotFieldOrientation_COLUMN; + // Remember the caption name + aDataFieldCaptionNames[rMember.Name] = rMember.Caption; + } + i++; + } + } + } + break; + + case sheet::DataPilotFieldOrientation_ROW: + { + m_aRowFields.emplace_back(xLevelName->getName(), nDim, nDimPos, bHasHiddenMember); + + const uno::Sequence<sheet::MemberResult> aSequence = xLevelResult->getResults(); + + size_t i = 0; + size_t nEachIndex = 0; + std::unique_ptr<ValueAndFormat> pItem; + + for (sheet::MemberResult const & rMember : aSequence) + { + bool bFound = aValidRowIndex.find(nEachIndex) != aValidRowIndex.end(); + + nEachIndex++; + + bool bHasContinueFlag = rMember.Flags & sheet::MemberResultFlags::CONTINUE; + + if (rMember.Flags & sheet::MemberResultFlags::HASMEMBER || bHasContinueFlag) + { + if (!bHasContinueFlag) + { + // Chart2 does not use number format for labels, so use the display string. + pItem.reset(new ValueAndFormat(rMember.Caption)); + } + + if (bFound) + { + if (i >= m_aCategoriesRowOrientation.size()) + m_aCategoriesRowOrientation.resize(i + 1); + + if (o3tl::make_unsigned(nDimPos) >= m_aCategoriesColumnOrientation.size()) + m_aCategoriesColumnOrientation.resize(nDimPos + 1); + m_aCategoriesColumnOrientation[nDimPos].push_back(*pItem); + + if (o3tl::make_unsigned(nDimPos) >= m_aCategoriesRowOrientation[i].size()) + m_aCategoriesRowOrientation[i].resize(nDimPos + 1); + m_aCategoriesRowOrientation[i][nDimPos] = *pItem; + + if (bIsDataLayout) + { + // Remember data fields to determine the number format of data + aDataFieldNamesVectors.push_back(rMember.Name); + eDataFieldOrientation = sheet::DataPilotFieldOrientation_ROW; + + // Remember the caption name + aDataFieldCaptionNames[rMember.Name] = rMember.Caption; + } + + // Set to empty so the sub categories are set to empty when they continue + pItem.reset(new ValueAndFormat); + i++; + } + } + } + } + break; + + case sheet::DataPilotFieldOrientation_PAGE: + { + m_aPageFields.emplace_back(xLevelName->getName(), nDim, nDimPos, bHasHiddenMember); + + // Resolve filtering + OUString aFieldOutputDescription; + if (bHasHiddenMember) + { + std::vector<OUString> aMembers = lcl_getVisiblePageMembers(xLevel); + + if (aMembers.size() == 1) + aFieldOutputDescription = aMembers[0]; + else + aFieldOutputDescription = ScResId(SCSTR_MULTIPLE); + } + else + { + aFieldOutputDescription = ScResId(SCSTR_ALL); + } + m_aFieldOutputDescriptionMap[nDim] = aFieldOutputDescription; + } + break; + + case sheet::DataPilotFieldOrientation_DATA: + { + aDataFieldNumberFormatMap[xLevelName->getName()] = nNumberFormat; + m_aDataFields.emplace_back(xLevelName->getName(), nDim, nDimPos, bHasHiddenMember); + } + break; + + default: + break; + } + } + } + } + + // Transform the name of data fields + for (chart2::data::PivotTableFieldEntry& rDataFields : m_aDataFields) + { + rDataFields.Name = aDataFieldCaptionNames[rDataFields.Name]; + } + + // Apply number format to the data + if (eDataFieldOrientation == sheet::DataPilotFieldOrientation_ROW) + { + for (std::vector<ValueAndFormat> & rDataRow : m_aDataRowVector) + { + size_t i = 0; + for (ValueAndFormat & rItem : rDataRow) + { + OUString sName = aDataFieldNamesVectors[i]; + sal_Int32 nNumberFormat = aDataFieldNumberFormatMap[sName]; + rItem.m_nNumberFormat = nNumberFormat; + i++; + } + } + } + else if (eDataFieldOrientation == sheet::DataPilotFieldOrientation_COLUMN) + { + size_t i = 0; + for (std::vector<ValueAndFormat> & rDataRow : m_aDataRowVector) + { + OUString sName = aDataFieldNamesVectors[i]; + sal_Int32 nNumberFormat = aDataFieldNumberFormatMap[sName]; + for (ValueAndFormat & rItem : rDataRow) + { + rItem.m_nNumberFormat = nNumberFormat; + } + i++; + } + } + + // Sort fields so it respects the order of how it is represented in the pivot table + + auto funcDimensionPositionSortCompare = [] (chart2::data::PivotTableFieldEntry const & entry1, + chart2::data::PivotTableFieldEntry const & entry2) + { + return entry1.DimensionPositionIndex < entry2.DimensionPositionIndex; + }; + + std::sort(m_aColumnFields.begin(), m_aColumnFields.end(), funcDimensionPositionSortCompare); + std::sort(m_aRowFields.begin(), m_aRowFields.end(), funcDimensionPositionSortCompare); + std::sort(m_aPageFields.begin(), m_aPageFields.end(), funcDimensionPositionSortCompare); + std::sort(m_aDataFields.begin(), m_aDataFields.end(), funcDimensionPositionSortCompare); + + // Mark that we updated the data + m_bNeedsUpdate = false; +} + +uno::Reference<chart2::data::XDataSequence> +PivotTableDataProvider::assignValuesToDataSequence(size_t nIndex) +{ + uno::Reference<chart2::data::XDataSequence> xDataSequence; + if (nIndex >= m_aDataRowVector.size()) + return xDataSequence; + + OUString sDataID = lcl_identifierForData(nIndex); + + std::vector<ValueAndFormat> const & rRowOfData = m_aDataRowVector[nIndex]; + rtl::Reference<PivotTableDataSequence> pSequence(new PivotTableDataSequence(m_pDocument, sDataID, std::vector(rRowOfData))); + pSequence->setRole("values-y"); + xDataSequence = pSequence; + return xDataSequence; +} + +uno::Reference<chart2::data::XDataSequence> +PivotTableDataProvider::assignLabelsToDataSequence(size_t nIndex) +{ + uno::Reference<chart2::data::XDataSequence> xDataSequence; + + OUString sLabelID = lcl_identifierForLabel(nIndex); + + OUStringBuffer aLabel; + bool bFirst = true; + + if (m_aLabels.empty()) + { + aLabel = ScResId(STR_PIVOT_TOTAL); + } + else if (nIndex < m_aLabels.size()) + { + for (ValueAndFormat const & rItem : m_aLabels[nIndex]) + { + if (bFirst) + { + aLabel.append(rItem.m_aString); + bFirst = false; + } + else + { + aLabel.append(" - " + rItem.m_aString); + } + } + } + + std::vector<ValueAndFormat> aLabelVector { ValueAndFormat(aLabel.makeStringAndClear()) }; + + rtl::Reference<PivotTableDataSequence> pSequence(new PivotTableDataSequence(m_pDocument, + sLabelID, std::move(aLabelVector))); + pSequence->setRole("values-y"); + xDataSequence = pSequence; + return xDataSequence; +} + +css::uno::Reference<css::chart2::data::XDataSequence> + PivotTableDataProvider::assignFirstCategoriesToDataSequence() +{ + uno::Reference<chart2::data::XDataSequence> xDataSequence; + + if (m_aCategoriesColumnOrientation.empty()) + return xDataSequence; + + std::vector<ValueAndFormat> const & rCategories = m_aCategoriesColumnOrientation.back(); + + rtl::Reference<PivotTableDataSequence> pSequence(new PivotTableDataSequence(m_pDocument, + lcl_identifierForCategories(), std::vector(rCategories))); + pSequence->setRole("categories"); + xDataSequence = pSequence; + + return xDataSequence; +} + +uno::Reference<chart2::data::XDataSource> + PivotTableDataProvider::createValuesDataSource() +{ + if (m_bNeedsUpdate) + collectPivotTableData(); + + uno::Reference<chart2::data::XDataSource> xDataSource; + std::vector<uno::Reference<chart2::data::XLabeledDataSequence>> aLabeledSequences; + + // Fill first sequence of categories + { + uno::Reference<chart2::data::XLabeledDataSequence> xResult = newLabeledDataSequence(); + xResult->setValues(assignFirstCategoriesToDataSequence()); + aLabeledSequences.push_back(xResult); + } + + // Fill values and labels + { + for (size_t i = 0; i < m_aDataRowVector.size(); ++i) + { + uno::Reference<chart2::data::XLabeledDataSequence> xResult = newLabeledDataSequence(); + xResult->setValues(assignValuesToDataSequence(i)); + xResult->setLabel(assignLabelsToDataSequence(i)); + aLabeledSequences.push_back(xResult); + } + } + + xDataSource.set(new PivotTableDataSource(std::move(aLabeledSequences))); + return xDataSource; +} + + +uno::Sequence<beans::PropertyValue> SAL_CALL PivotTableDataProvider::detectArguments( + const uno::Reference<chart2::data::XDataSource> & xDataSource) +{ + if (!m_pDocument ||!xDataSource.is()) + return uno::Sequence<beans::PropertyValue>(); + + return comphelper::InitPropertySequence({ + { "CellRangeRepresentation", uno::Any(OUString("PivotChart")) }, + { "DataRowSource", uno::Any(chart::ChartDataRowSource_COLUMNS) }, + { "FirstCellAsLabel", uno::Any(false) }, + { "HasCategories", uno::Any(true) } + }); +} + +sal_Bool SAL_CALL PivotTableDataProvider::createDataSequenceByRangeRepresentationPossible(const OUString& /*aRangeRepresentation*/) +{ + return false; +} + +uno::Reference<chart2::data::XDataSequence> SAL_CALL + PivotTableDataProvider::createDataSequenceByRangeRepresentation(const OUString& /*rRangeRepresentation*/) +{ + uno::Reference<chart2::data::XDataSequence> xDataSequence; + return xDataSequence; +} + +uno::Reference<chart2::data::XDataSequence> SAL_CALL + PivotTableDataProvider::createDataSequenceByValueArray(const OUString& /*aRole*/, + const OUString& /*aRangeRepresentation*/, + const OUString& /*aRoleQualifier*/) +{ + return uno::Reference<chart2::data::XDataSequence>(); +} + +uno::Reference<sheet::XRangeSelection> SAL_CALL PivotTableDataProvider::getRangeSelection() +{ + uno::Reference<sheet::XRangeSelection> xResult; + + uno::Reference<frame::XModel> xModel(lcl_GetXModel(m_pDocument)); + if (xModel.is()) + xResult.set(xModel->getCurrentController(), uno::UNO_QUERY); + + return xResult; +} + +// XPivotTableDataProvider ======================================================== + +uno::Sequence<chart2::data::PivotTableFieldEntry> PivotTableDataProvider::getColumnFields() +{ + return comphelper::containerToSequence(m_aColumnFields); +} + +uno::Sequence<chart2::data::PivotTableFieldEntry> PivotTableDataProvider::getRowFields() +{ + return comphelper::containerToSequence(m_aRowFields); +} + +uno::Sequence<chart2::data::PivotTableFieldEntry> PivotTableDataProvider::getPageFields() +{ + return comphelper::containerToSequence(m_aPageFields); +} + +uno::Sequence<chart2::data::PivotTableFieldEntry> PivotTableDataProvider::getDataFields() +{ + return comphelper::containerToSequence(m_aDataFields); +} + +OUString PivotTableDataProvider::getPivotTableName() +{ + return m_sPivotTableName; +} + +void PivotTableDataProvider::setPivotTableName(const OUString& sPivotTableName) +{ + ScDPCollection* pDPCollection = m_pDocument->GetDPCollection(); + ScDPObject* pDPObject = pDPCollection->GetByName(sPivotTableName); + if (pDPObject) + m_sPivotTableName = sPivotTableName; +} + +sal_Bool PivotTableDataProvider::hasPivotTable() +{ + if (m_sPivotTableName.isEmpty()) + return false; + + ScDPCollection* pDPCollection = m_pDocument->GetDPCollection(); + ScDPObject* pDPObject = pDPCollection->GetByName(m_sPivotTableName); + + if (pDPObject) + return true; + + return false; +} + +uno::Reference<chart2::data::XDataSequence> + PivotTableDataProvider::createDataSequenceOfValuesByIndex(sal_Int32 nIndex) +{ + SolarMutexGuard aGuard; + + if (m_bNeedsUpdate) + collectPivotTableData(); + + return assignValuesToDataSequence(size_t(nIndex)); +} + +uno::Reference<css::chart2::data::XDataSequence> + PivotTableDataProvider::createDataSequenceOfLabelsByIndex(sal_Int32 nIndex) +{ + SolarMutexGuard aGuard; + + if (m_bNeedsUpdate) + collectPivotTableData(); + + return assignLabelsToDataSequence(size_t(nIndex)); +} + +uno::Reference<css::chart2::data::XDataSequence> + PivotTableDataProvider::createDataSequenceOfCategories() +{ + SolarMutexGuard aGuard; + + if (m_bNeedsUpdate) + collectPivotTableData(); + + return assignFirstCategoriesToDataSequence(); +} + +OUString PivotTableDataProvider::getFieldOutputDescription(sal_Int32 nDimensionIndex) +{ + if (nDimensionIndex < 0) + return OUString(); + return m_aFieldOutputDescriptionMap[size_t(nDimensionIndex)]; +} + +// XModifyBroadcaster ======================================================== + +void SAL_CALL PivotTableDataProvider::addModifyListener(const uno::Reference< util::XModifyListener>& aListener) +{ + SolarMutexGuard aGuard; + + m_aValueListeners.emplace_back(aListener); +} + +void SAL_CALL PivotTableDataProvider::removeModifyListener(const uno::Reference<util::XModifyListener>& aListener ) +{ + SolarMutexGuard aGuard; + + sal_uInt16 nCount = m_aValueListeners.size(); + for (sal_uInt16 n = nCount; n--;) + { + uno::Reference<util::XModifyListener>& rObject = m_aValueListeners[n]; + if (rObject == aListener) + { + m_aValueListeners.erase(m_aValueListeners.begin() + n); + } + } +} + +// DataProvider XPropertySet ======================================================== + +uno::Reference< beans::XPropertySetInfo> SAL_CALL + PivotTableDataProvider::getPropertySetInfo() +{ + SolarMutexGuard aGuard; + static uno::Reference<beans::XPropertySetInfo> aRef = + new SfxItemPropertySetInfo( m_aPropSet.getPropertyMap() ); + return aRef; +} + +void SAL_CALL PivotTableDataProvider::setPropertyValue(const OUString& rPropertyName, const uno::Any& rValue) +{ + if (rPropertyName != SC_UNONAME_INCLUDEHIDDENCELLS) + throw beans::UnknownPropertyException(rPropertyName); + + if (!(rValue >>= m_bIncludeHiddenCells)) + throw lang::IllegalArgumentException(); +} + +uno::Any SAL_CALL PivotTableDataProvider::getPropertyValue(const OUString& rPropertyName) +{ + uno::Any aRet; + if (rPropertyName == SC_UNONAME_INCLUDEHIDDENCELLS) + aRet <<= m_bIncludeHiddenCells; + else if (rPropertyName == SC_UNONAME_USE_INTERNAL_DATA_PROVIDER) + { + // This is a read-only property. + aRet <<= m_pDocument->PastingDrawFromOtherDoc(); + } + else + throw beans::UnknownPropertyException(rPropertyName); + return aRet; +} + +void SAL_CALL PivotTableDataProvider::addPropertyChangeListener( + const OUString& /*rPropertyName*/, + const uno::Reference<beans::XPropertyChangeListener>& /*xListener*/) +{ + OSL_FAIL("Not yet implemented"); +} + +void SAL_CALL PivotTableDataProvider::removePropertyChangeListener( + const OUString& /*rPropertyName*/, + const uno::Reference<beans::XPropertyChangeListener>& /*rListener*/) +{ + OSL_FAIL("Not yet implemented"); +} + +void SAL_CALL PivotTableDataProvider::addVetoableChangeListener( + const OUString& /*rPropertyName*/, + const uno::Reference<beans::XVetoableChangeListener>& /*rListener*/) +{ + OSL_FAIL("Not yet implemented"); +} + +void SAL_CALL PivotTableDataProvider::removeVetoableChangeListener( + const OUString& /*rPropertyName*/, + const uno::Reference<beans::XVetoableChangeListener>& /*rListener*/ ) +{ + OSL_FAIL("Not yet implemented"); +} + +} // end sc namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |