diff options
Diffstat (limited to 'sc/source/ui/unoobj')
56 files changed, 49389 insertions, 0 deletions
diff --git a/sc/source/ui/unoobj/ChartRangeSelectionListener.cxx b/sc/source/ui/unoobj/ChartRangeSelectionListener.cxx new file mode 100644 index 0000000000..eae58aee6f --- /dev/null +++ b/sc/source/ui/unoobj/ChartRangeSelectionListener.cxx @@ -0,0 +1,71 @@ +/* -*- 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 <ChartRangeSelectionListener.hxx> + +#include <com/sun/star/chart2/data/XRangeHighlighter.hpp> + +#include <tabvwsh.hxx> +#include <unonames.hxx> +#include <miscuno.hxx> + +using namespace ::com::sun::star; + +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; + +SC_SIMPLE_SERVICE_INFO( ScChartRangeSelectionListener, "ScChartRangeSelectionListener", + SC_SERVICENAME_CHRANGEHILIGHT ) + +ScChartRangeSelectionListener::ScChartRangeSelectionListener( ScTabViewShell * pViewShell ) : + m_pViewShell( pViewShell ) +{} + +ScChartRangeSelectionListener::~ScChartRangeSelectionListener() +{} + +// ____ XModifyListener ____ +void SAL_CALL ScChartRangeSelectionListener::selectionChanged( const lang::EventObject& aEvent ) +{ + Reference< chart2::data::XRangeHighlighter > xRangeHighlighter( aEvent.Source, uno::UNO_QUERY ); + if( xRangeHighlighter.is()) + { + Sequence< chart2::data::HighlightedRange > aRanges( xRangeHighlighter->getSelectedRanges()); + + // search the view on which the chart is active + + if( m_pViewShell ) + { + m_pViewShell->DoChartSelection( aRanges ); + } + } +} + +// ____ XEventListener ____ +void SAL_CALL ScChartRangeSelectionListener::disposing( const lang::EventObject& /*Source*/ ) +{ +} + +// ____ WeakComponentImplHelperBase ____ +void ScChartRangeSelectionListener::disposing(std::unique_lock<std::mutex>&) +{ + m_pViewShell = nullptr; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/unoobj/ChartTools.cxx b/sc/source/ui/unoobj/ChartTools.cxx new file mode 100644 index 0000000000..e9fdc996a3 --- /dev/null +++ b/sc/source/ui/unoobj/ChartTools.cxx @@ -0,0 +1,174 @@ +/* -*- 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 <ChartTools.hxx> +#include <docsh.hxx> +#include <drwlayer.hxx> + +#include <com/sun/star/chart2/data/XPivotTableDataProvider.hpp> +#include <com/sun/star/chart2/XChartDocument.hpp> +#include <com/sun/star/embed/XEmbeddedObject.hpp> +#include <svx/svditer.hxx> +#include <svx/svdoole2.hxx> +#include <svx/svdpage.hxx> + +using namespace css; + +namespace sc::tools { + +namespace { + +uno::Reference<chart2::data::XPivotTableDataProvider> +getPivotTableDataProvider(const SdrOle2Obj* pOleObject) +{ + uno::Reference<chart2::data::XPivotTableDataProvider> xPivotTableDataProvider; + + const uno::Reference<embed::XEmbeddedObject>& xObject = pOleObject->GetObjRef(); + if (xObject.is()) + { + uno::Reference<chart2::XChartDocument> xChartDoc(xObject->getComponent(), uno::UNO_QUERY); + if (xChartDoc.is()) + { + xPivotTableDataProvider.set(uno::Reference<chart2::data::XPivotTableDataProvider>( + xChartDoc->getDataProvider(), uno::UNO_QUERY)); + } + } + return xPivotTableDataProvider; +} + +OUString getAssociatedPivotTableName(const SdrOle2Obj* pOleObject) +{ + OUString aPivotTableName; + uno::Reference<chart2::data::XPivotTableDataProvider> xPivotTableDataProvider; + xPivotTableDataProvider.set(getPivotTableDataProvider(pOleObject)); + if (xPivotTableDataProvider.is()) + aPivotTableName = xPivotTableDataProvider->getPivotTableName(); + return aPivotTableName; +} + +} // end anonymous namespace + +ChartIterator::ChartIterator(ScDocShell* pDocShell, SCTAB nTab, ChartSourceType eChartSourceType) + : m_eChartSourceType(eChartSourceType) +{ + if (!pDocShell) + return; + ScDocument& rDoc = pDocShell->GetDocument(); + ScDrawLayer* pDrawLayer = rDoc.GetDrawLayer(); + if (!pDrawLayer) + return; + SdrPage* pPage = pDrawLayer->GetPage(sal_uInt16(nTab)); + if (!pPage) + return; + m_oIterator.emplace(pPage, SdrIterMode::DeepNoGroups); +} + +SdrOle2Obj* ChartIterator::next() +{ + if (!m_oIterator) + return nullptr; + + SdrObject* pObject = m_oIterator->Next(); + while (pObject) + { + if (pObject->GetObjIdentifier() == SdrObjKind::OLE2 && ScDocument::IsChart(pObject)) + { + SdrOle2Obj* pOleObject = static_cast<SdrOle2Obj*>(pObject); + + uno::Reference<chart2::data::XPivotTableDataProvider> xPivotTableDataProvider; + xPivotTableDataProvider.set(getPivotTableDataProvider(pOleObject)); + + if (xPivotTableDataProvider.is() && m_eChartSourceType == ChartSourceType::PIVOT_TABLE) + return pOleObject; + else if (!xPivotTableDataProvider.is() && m_eChartSourceType == ChartSourceType::CELL_RANGE) + return pOleObject; + } + pObject = m_oIterator->Next(); + } + return nullptr; +} + +SdrOle2Obj* findChartsByName(ScDocShell* pDocShell, SCTAB nTab, std::u16string_view rName, ChartSourceType eChartSourceType) +{ + if (!pDocShell) + return nullptr; + + ChartIterator aIterator(pDocShell, nTab, eChartSourceType); + + SdrOle2Obj* pObject = aIterator.next(); + while (pObject) + { + uno::Reference<embed::XEmbeddedObject> xObject = pObject->GetObjRef(); + if (xObject.is()) + { + OUString aObjectName = pDocShell->GetEmbeddedObjectContainer().GetEmbeddedObjectName(xObject); + if (aObjectName == rName) + return pObject; + } + pObject = aIterator.next(); + } + return nullptr; +} + +SdrOle2Obj* getChartByIndex(ScDocShell* pDocShell, SCTAB nTab, ::tools::Long nIndex, ChartSourceType eChartSourceType) +{ + if (!pDocShell) + return nullptr; + + ChartIterator aIterator(pDocShell, nTab, eChartSourceType); + + SdrOle2Obj* pObject = aIterator.next(); + ::tools::Long i = 0; + while (pObject) + { + if (i == nIndex) + { + return pObject; + } + + i++; + pObject = aIterator.next(); + } + return nullptr; +} + +std::vector<SdrOle2Obj*> getAllPivotChartsConnectedTo(std::u16string_view sPivotTableName, ScDocShell* pDocShell) +{ + std::vector<SdrOle2Obj*> aObjects; + + ScDocument& rDocument = pDocShell->GetDocument(); + ScDrawLayer* pModel = rDocument.GetDrawLayer(); + if (!pModel) + return aObjects; + + sal_uInt16 nPageCount = pModel->GetPageCount(); + for (sal_uInt16 nPageNo = 0; nPageNo < nPageCount; nPageNo++) + { + SdrPage* pPage = pModel->GetPage(nPageNo); + if (!pPage) + continue; + + sc::tools::ChartIterator aIterator(pDocShell, nPageNo, ChartSourceType::PIVOT_TABLE); + SdrOle2Obj* pObject = aIterator.next(); + while (pObject) + { + if (sPivotTableName == getAssociatedPivotTableName(pObject)) + { + aObjects.push_back(pObject); + } + pObject = aIterator.next(); + } + } + return aObjects; +} + +} // end sc::tools + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/unoobj/PivotTableDataProvider.cxx b/sc/source/ui/unoobj/PivotTableDataProvider.cxx new file mode 100644 index 0000000000..e3c800128b --- /dev/null +++ b/sc/source/ui/unoobj/PivotTableDataProvider.cxx @@ -0,0 +1,906 @@ +/* -*- 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 <docsh.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"); + +std::span<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 }, + }; + return aDataProviderPropertyMap_Impl; +} + +uno::Reference<frame::XModel> lcl_GetXModel(const ScDocument * pDoc) +{ + uno::Reference<frame::XModel> xModel; + ScDocShell* 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(getXWeak(), + 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) + { + assert(pItem && "bHasContinueFlag must be false on this or some preceding element"); + + 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: */ diff --git a/sc/source/ui/unoobj/PivotTableDataSequence.cxx b/sc/source/ui/unoobj/PivotTableDataSequence.cxx new file mode 100644 index 0000000000..dc81d03978 --- /dev/null +++ b/sc/source/ui/unoobj/PivotTableDataSequence.cxx @@ -0,0 +1,278 @@ +/* -*- 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 <memory> +#include <PivotTableDataSequence.hxx> + +#include <sal/config.h> +#include <sal/log.hxx> +#include <o3tl/safeint.hxx> +#include <osl/diagnose.h> +#include <utility> +#include <vcl/svapp.hxx> + +#include <miscuno.hxx> +#include <document.hxx> +#include <unonames.hxx> + +using namespace css; + +namespace sc +{ + +SC_SIMPLE_SERVICE_INFO( PivotTableDataSequence, "PivotTableDataSequence", "com.sun.star.chart2.data.DataSequence") + +static std::span<const SfxItemPropertyMapEntry> lcl_GetDataSequencePropertyMap() +{ + static const SfxItemPropertyMapEntry aDataSequencePropertyMap_Impl[] = + { + { SC_UNONAME_HIDDENVALUES, 0, cppu::UnoType<uno::Sequence<sal_Int32>>::get(), 0, 0 }, + { SC_UNONAME_ROLE, 0, cppu::UnoType<css::chart2::data::DataSequenceRole>::get(), 0, 0 }, + { SC_UNONAME_INCLUDEHIDDENCELLS, 0, cppu::UnoType<bool>::get(), 0, 0 }, + }; + return aDataSequencePropertyMap_Impl; +} + +PivotTableDataSequence::PivotTableDataSequence(ScDocument* pDocument, OUString sID, + std::vector<ValueAndFormat>&& rData) + : m_pDocument(pDocument) + , m_aID(std::move(sID)) + , m_aData(std::move(rData)) + , m_aPropSet(lcl_GetDataSequencePropertyMap()) +{ + if (m_pDocument) + m_pDocument->AddUnoObject(*this); +} + +PivotTableDataSequence::~PivotTableDataSequence() +{ + SolarMutexGuard g; + + if (m_pDocument) + m_pDocument->RemoveUnoObject(*this); +} + +void PivotTableDataSequence::Notify(SfxBroadcaster& /*rBC*/, const SfxHint& rHint) +{ + if (rHint.GetId() == SfxHintId::Dying) + { + m_pDocument = nullptr; + } +} + +uno::Sequence<uno::Any> SAL_CALL PivotTableDataSequence::getData() +{ + SolarMutexGuard aGuard; + + if (!m_pDocument) + throw uno::RuntimeException(); + + uno::Sequence<uno::Any> aSeq(m_aData.size()); + auto pSeq = aSeq.getArray(); + + size_t i = 0; + for (ValueAndFormat const & rItem : m_aData) + { + if (rItem.m_eType == ValueType::Numeric) + pSeq[i] <<= double(rItem.m_fValue); + else if (rItem.m_eType == ValueType::String) + pSeq[i] <<= rItem.m_aString; + + i++; + } + return aSeq; +} + +// XNumericalDataSequence -------------------------------------------------- + +uno::Sequence<double> SAL_CALL PivotTableDataSequence::getNumericalData() +{ + SolarMutexGuard aGuard; + if (!m_pDocument) + throw uno::RuntimeException(); + + uno::Sequence<double> aSeq(m_aData.size()); + auto pSeq = aSeq.getArray(); + + size_t i = 0; + for (ValueAndFormat const & rItem : m_aData) + { + pSeq[i] = rItem.m_fValue; + i++; + } + return aSeq; +} + +// XTextualDataSequence -------------------------------------------------- + +uno::Sequence<OUString> SAL_CALL PivotTableDataSequence::getTextualData() +{ + SolarMutexGuard aGuard; + if (!m_pDocument) + throw uno::RuntimeException(); + + uno::Sequence<OUString> aSeq(m_aData.size()); + auto pSeq = aSeq.getArray(); + + size_t i = 0; + for (ValueAndFormat const & rItem : m_aData) + { + if (rItem.m_eType == ValueType::String) + pSeq[i] = rItem.m_aString; + i++; + } + return aSeq; +} + +OUString SAL_CALL PivotTableDataSequence::getSourceRangeRepresentation() +{ + return m_aID; +} + +uno::Sequence<OUString> SAL_CALL PivotTableDataSequence::generateLabel(chart2::data::LabelOrigin /*eOrigin*/) +{ + SolarMutexGuard aGuard; + if (!m_pDocument) + throw uno::RuntimeException(); + + uno::Sequence<OUString> aSeq; + return aSeq; +} + +sal_Int32 SAL_CALL PivotTableDataSequence::getNumberFormatKeyByIndex(sal_Int32 nIndex) +{ + SolarMutexGuard aGuard; + if (nIndex == -1 && !m_aData.empty()) + { + return m_aData[0].m_nNumberFormat; + } + else if (nIndex < 0 && o3tl::make_unsigned(nIndex) >= m_aData.size()) + { + SAL_WARN("sc.ui", "Passed invalid index to getNumberFormatKeyByIndex(). Will return default value '0'."); + return 0; + } + return m_aData[size_t(nIndex)].m_nNumberFormat; +} + +// XCloneable ================================================================ + +uno::Reference<util::XCloneable> SAL_CALL PivotTableDataSequence::createClone() +{ + SolarMutexGuard aGuard; + + rtl::Reference<PivotTableDataSequence> pClone(new PivotTableDataSequence(m_pDocument, m_aID, std::vector(m_aData))); + pClone->setRole(m_aRole); + + return pClone; +} + +// XModifyBroadcaster ======================================================== + +void SAL_CALL PivotTableDataSequence::addModifyListener(const uno::Reference<util::XModifyListener>& aListener) +{ + SolarMutexGuard aGuard; + m_aValueListeners.emplace_back(aListener); +} + +void SAL_CALL PivotTableDataSequence::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>& rObj = m_aValueListeners[n]; + if (rObj == aListener) + { + m_aValueListeners.erase(m_aValueListeners.begin() + n); + } + } +} + +// DataSequence XPropertySet ------------------------------------------------- + +uno::Reference< beans::XPropertySetInfo> SAL_CALL PivotTableDataSequence::getPropertySetInfo() +{ + SolarMutexGuard aGuard; + static uno::Reference<beans::XPropertySetInfo> aRef = new SfxItemPropertySetInfo(m_aPropSet.getPropertyMap()); + return aRef; +} + +void SAL_CALL PivotTableDataSequence::setPropertyValue(const OUString& rPropertyName, const uno::Any& rValue) +{ + if (rPropertyName == SC_UNONAME_ROLE) + { + if (!(rValue >>= m_aRole)) + throw lang::IllegalArgumentException(); + } + else if (rPropertyName == SC_UNONAME_INCLUDEHIDDENCELLS + || rPropertyName == SC_UNONAME_HIDDENVALUES + || rPropertyName == SC_UNONAME_TIME_BASED + || rPropertyName == SC_UNONAME_HAS_STRING_LABEL) + {} + else + throw beans::UnknownPropertyException(rPropertyName); +} + +uno::Any SAL_CALL PivotTableDataSequence::getPropertyValue(const OUString& rPropertyName) +{ + uno::Any aReturn; + if (rPropertyName == SC_UNONAME_ROLE) + aReturn <<= m_aRole; + else if (rPropertyName == SC_UNONAME_INCLUDEHIDDENCELLS) + aReturn <<= false; + else if (rPropertyName == SC_UNONAME_HIDDENVALUES) + { + css::uno::Sequence<sal_Int32> aHiddenValues; + aReturn <<= aHiddenValues; + } + else if (rPropertyName == SC_UNONAME_TIME_BASED) + { + aReturn <<= false; + } + else if (rPropertyName == SC_UNONAME_HAS_STRING_LABEL) + { + aReturn <<= false; + } + else + throw beans::UnknownPropertyException(rPropertyName); + return aReturn; +} + +void SAL_CALL PivotTableDataSequence::addPropertyChangeListener( + const OUString& /*rPropertyName*/, + const uno::Reference< beans::XPropertyChangeListener>& /*xListener*/) +{ + OSL_FAIL("Not yet implemented"); +} + +void SAL_CALL PivotTableDataSequence::removePropertyChangeListener( + const OUString& /*rPropertyName*/, + const uno::Reference< beans::XPropertyChangeListener>& /*rListener*/) +{ + OSL_FAIL("Not yet implemented"); +} + +void SAL_CALL PivotTableDataSequence::addVetoableChangeListener( + const OUString& /*rPropertyName*/, + const uno::Reference< beans::XVetoableChangeListener>& /*rListener*/) +{ + OSL_FAIL("Not yet implemented"); +} + +void SAL_CALL PivotTableDataSequence::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: */ diff --git a/sc/source/ui/unoobj/PivotTableDataSource.cxx b/sc/source/ui/unoobj/PivotTableDataSource.cxx new file mode 100644 index 0000000000..5c47f0d127 --- /dev/null +++ b/sc/source/ui/unoobj/PivotTableDataSource.cxx @@ -0,0 +1,49 @@ +/* -*- 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 <PivotTableDataSource.hxx> + +#include <sal/config.h> + +#include <miscuno.hxx> + +#include <comphelper/sequence.hxx> +#include <vcl/svapp.hxx> + +using namespace css; + +namespace sc +{ + +SC_SIMPLE_SERVICE_INFO(PivotTableDataSource, "PivotTableDataSource", "com.sun.star.chart2.data.DataSource") + +PivotTableDataSource::PivotTableDataSource(std::vector<css::uno::Reference<css::chart2::data::XLabeledDataSequence>>&& xLabeledSequence) + : m_xLabeledSequence(std::move(xLabeledSequence)) +{ +} + +PivotTableDataSource::~PivotTableDataSource() +{ +} + +void PivotTableDataSource::Notify(SfxBroadcaster& /*rBroadcaster*/, const SfxHint& /*rHint*/) +{ +} + +uno::Sequence<uno::Reference<chart2::data::XLabeledDataSequence>> SAL_CALL + PivotTableDataSource::getDataSequences() +{ + SolarMutexGuard aGuard; + + return comphelper::containerToSequence(m_xLabeledSequence); +} + +} // end sc namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/unoobj/TablePivotChart.cxx b/sc/source/ui/unoobj/TablePivotChart.cxx new file mode 100644 index 0000000000..107d6d5baa --- /dev/null +++ b/sc/source/ui/unoobj/TablePivotChart.cxx @@ -0,0 +1,104 @@ +/* -*- 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 <com/sun/star/chart2/data/XPivotTableDataProvider.hpp> +#include <com/sun/star/chart2/XChartDocument.hpp> +#include <com/sun/star/embed/XEmbeddedObject.hpp> +#include <svx/svdoole2.hxx> +#include <svtools/embedhlp.hxx> +#include <utility> +#include <vcl/svapp.hxx> + +#include <miscuno.hxx> +#include <docsh.hxx> + +#include <TablePivotChart.hxx> +#include <ChartTools.hxx> + +using namespace css; + +namespace sc +{ + +SC_SIMPLE_SERVICE_INFO(TablePivotChart, "TablePivotChart", "com.sun.star.table.TablePivotChart") + +TablePivotChart::TablePivotChart(ScDocShell* pDocShell, SCTAB nTab, OUString aName) + : m_pDocShell(pDocShell) + , m_nTab(nTab) + , m_aChartName(std::move(aName)) +{ + if (m_pDocShell) + m_pDocShell->GetDocument().AddUnoObject(*this); +} + +TablePivotChart::~TablePivotChart() +{ + SolarMutexGuard aGuard; + + if (m_pDocShell) + m_pDocShell->GetDocument().RemoveUnoObject(*this); +} + +void TablePivotChart::Notify(SfxBroadcaster&, const SfxHint& rHint) +{ + if (rHint.GetId() == SfxHintId::Dying) + m_pDocShell = nullptr; +} + +// XEmbeddedObjectSupplier + +uno::Reference<lang::XComponent> SAL_CALL TablePivotChart::getEmbeddedObject() +{ + SolarMutexGuard aGuard; + SdrOle2Obj* pObject = sc::tools::findChartsByName(m_pDocShell, m_nTab, m_aChartName, sc::tools::ChartSourceType::PIVOT_TABLE); + if (pObject && svt::EmbeddedObjectRef::TryRunningState(pObject->GetObjRef())) + return uno::Reference<lang::XComponent>(pObject->GetObjRef()->getComponent(), uno::UNO_QUERY); + return nullptr; +} + +// XNamed + +OUString SAL_CALL TablePivotChart::getName() +{ + return m_aChartName; +} + +void SAL_CALL TablePivotChart::setName(OUString const & /* aName */) +{ + throw uno::RuntimeException(); // name cannot be changed +} + +// XTablePivotChart + +OUString SAL_CALL TablePivotChart::getPivotTableName() +{ + SolarMutexGuard aGuard; + + SdrOle2Obj* pObject = sc::tools::findChartsByName(m_pDocShell, m_nTab, m_aChartName, sc::tools::ChartSourceType::PIVOT_TABLE); + if (!pObject) + return OUString(); + + uno::Reference<embed::XEmbeddedObject> xObject = pObject->GetObjRef(); + if (!xObject.is()) + return OUString(); + + uno::Reference<chart2::XChartDocument> xChartDoc(xObject->getComponent(), uno::UNO_QUERY); + if (!xChartDoc.is()) + return OUString(); + + uno::Reference<chart2::data::XPivotTableDataProvider> xPivotTableDataProvider(xChartDoc->getDataProvider(), uno::UNO_QUERY); + if (!xPivotTableDataProvider.is()) + return OUString(); + + return xPivotTableDataProvider->getPivotTableName(); +} + +} // end sc namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/unoobj/TablePivotCharts.cxx b/sc/source/ui/unoobj/TablePivotCharts.cxx new file mode 100644 index 0000000000..f69a8298c3 --- /dev/null +++ b/sc/source/ui/unoobj/TablePivotCharts.cxx @@ -0,0 +1,279 @@ +/* -*- 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 <memory> +#include <com/sun/star/embed/Aspects.hpp> +#include <com/sun/star/embed/XEmbeddedObject.hpp> +#include <com/sun/star/awt/Size.hpp> +#include <com/sun/star/chart/ChartDataRowSource.hpp> +#include <com/sun/star/chart2/data/XDataReceiver.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> + +#include <tools/gen.hxx> +#include <svx/svdoole2.hxx> +#include <svx/svdpage.hxx> +#include <svx/svdundo.hxx> +#include <unotools/moduleoptions.hxx> +#include <comphelper/propertysequence.hxx> +#include <comphelper/classids.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <tools/globname.hxx> +#include <svtools/embedhlp.hxx> +#include <comphelper/sequence.hxx> +#include <vcl/svapp.hxx> + +#include <TablePivotChart.hxx> +#include <TablePivotCharts.hxx> +#include <PivotTableDataProvider.hxx> +#include <ChartTools.hxx> + +#include <miscuno.hxx> +#include <docsh.hxx> +#include <drwlayer.hxx> + +using namespace css; + +namespace sc +{ + +SC_SIMPLE_SERVICE_INFO(TablePivotCharts, "TablePivotCharts", "com.sun.star.table.TablePivotCharts") + +TablePivotCharts::TablePivotCharts(ScDocShell* pDocShell, SCTAB nTab) + : m_pDocShell(pDocShell) + , m_nTab(nTab) +{ + m_pDocShell->GetDocument().AddUnoObject(*this); +} + +TablePivotCharts::~TablePivotCharts() +{ + SolarMutexGuard aGuard; + + if (m_pDocShell) + m_pDocShell->GetDocument().RemoveUnoObject(*this); +} + +void TablePivotCharts::Notify(SfxBroadcaster& /*rBroadcaster*/, const SfxHint& rHint) +{ + if (rHint.GetId() == SfxHintId::Dying) + m_pDocShell = nullptr; +} + +// XTablePivotCharts +void SAL_CALL TablePivotCharts::addNewByName(OUString const & rName, + const awt::Rectangle& aRect, + OUString const & rDataPilotName) +{ + SolarMutexGuard aGuard; + + if (!m_pDocShell) + return; + + ScDocument& rDoc = m_pDocShell->GetDocument(); + ScDrawLayer* pModel = m_pDocShell->MakeDrawLayer(); + SdrPage* pPage = pModel->GetPage(sal_uInt16(m_nTab)); + if (!pPage) + return; + + // chart can't be inserted if any ole object with that name exists on any table + // (empty string: generate valid name) + + OUString aName = rName; + SCTAB nDummy; + if (!aName.isEmpty() && pModel->GetNamedObject(aName, SdrObjKind::OLE2, nDummy)) + { + // object exists - only RuntimeException is specified + throw uno::RuntimeException(); + } + + uno::Reference<embed::XEmbeddedObject> xObject; + + if (SvtModuleOptions().IsChart()) + xObject = m_pDocShell->GetEmbeddedObjectContainer().CreateEmbeddedObject(SvGlobalName(SO3_SCH_CLASSID).GetByteSequence(), aName); + + if (!xObject.is()) + return; + + Point aRectPos(aRect.X, aRect.Y); + bool bLayoutRTL = rDoc.IsLayoutRTL(m_nTab); + if ((aRectPos.X() < 0 && !bLayoutRTL) || (aRectPos.X() > 0 && bLayoutRTL)) + aRectPos.setX( 0 ); + + if (aRectPos.Y() < 0) + aRectPos.setY( 0 ); + + Size aRectSize(aRect.Width, aRect.Height); + if (aRectSize.Width() <= 0) + aRectSize.setWidth( 5000 ); // default size + + if (aRectSize.Height() <= 0) + aRectSize.setHeight( 5000 ); + + ::tools::Rectangle aInsRect(aRectPos, aRectSize); + + sal_Int64 nAspect(embed::Aspects::MSOLE_CONTENT); + MapUnit aMapUnit(VCLUnoHelper::UnoEmbed2VCLMapUnit(xObject->getMapUnit(nAspect))); + Size aSize(aInsRect.GetSize()); + aSize = OutputDevice::LogicToLogic(aSize, MapMode(MapUnit::Map100thMM), MapMode(aMapUnit)); + awt::Size aAwtSize; + aAwtSize.Width = aSize.Width(); + aAwtSize.Height = aSize.Height(); + + rtl::Reference<sc::PivotTableDataProvider> pPivotTableDataProvider(new sc::PivotTableDataProvider(rDoc)); + pPivotTableDataProvider->setPivotTableName(rDataPilotName); + + uno::Reference<chart2::data::XDataProvider> xDataProvider(pPivotTableDataProvider); + + uno::Reference<chart2::data::XDataReceiver> xReceiver; + + if (xObject.is()) + xReceiver.set(xObject->getComponent(), uno::UNO_QUERY); + + if (xReceiver.is()) + { + xReceiver->attachDataProvider(xDataProvider); + + uno::Reference<util::XNumberFormatsSupplier> xNumberFormatsSupplier(cppu::getXWeak(m_pDocShell->GetModel()), uno::UNO_QUERY); + xReceiver->attachNumberFormatsSupplier(xNumberFormatsSupplier); + + uno::Sequence<beans::PropertyValue> aArgs( comphelper::InitPropertySequence({ + { "CellRangeRepresentation", uno::Any(rDataPilotName) }, + { "HasCategories", uno::Any(true) }, + { "DataRowSource", uno::Any(chart::ChartDataRowSource_COLUMNS) } + })); + xReceiver->setArguments(aArgs); + } + + rtl::Reference<SdrOle2Obj> pObject = new SdrOle2Obj( + *pModel, + svt::EmbeddedObjectRef(xObject, embed::Aspects::MSOLE_CONTENT), + aName, + aInsRect); + + if (xObject.is()) + xObject->setVisualAreaSize(nAspect, aAwtSize); + + pPage->InsertObject(pObject.get()); + pModel->AddUndo(std::make_unique<SdrUndoInsertObj>(*pObject)); +} + +void SAL_CALL TablePivotCharts::removeByName(const OUString& rName) +{ + SolarMutexGuard aGuard; + SdrOle2Obj* pObject = sc::tools::findChartsByName(m_pDocShell, m_nTab, rName, sc::tools::ChartSourceType::PIVOT_TABLE); + if (pObject) + { + ScDocument& rDoc = m_pDocShell->GetDocument(); + ScDrawLayer* pModel = rDoc.GetDrawLayer(); + SdrPage* pPage = pModel->GetPage(sal_uInt16(m_nTab)); + pModel->AddUndo(std::make_unique<SdrUndoDelObj>(*pObject)); + pPage->RemoveObject(pObject->GetOrdNum()); + } +} + +// XIndexAccess +sal_Int32 SAL_CALL TablePivotCharts::getCount() +{ + SolarMutexGuard aGuard; + sal_Int32 nCount = 0; + + if (!m_pDocShell) + return nCount; + + sc::tools::ChartIterator aIterator(m_pDocShell, m_nTab, sc::tools::ChartSourceType::PIVOT_TABLE); + + SdrOle2Obj* pOleObject = aIterator.next(); + while (pOleObject) + { + if (pOleObject->GetObjRef().is()) + nCount++; + pOleObject = aIterator.next(); + } + return nCount; +} + +uno::Any SAL_CALL TablePivotCharts::getByIndex(sal_Int32 nIndex) +{ + SolarMutexGuard aGuard; + SdrOle2Obj* pObject = sc::tools::getChartByIndex(m_pDocShell, m_nTab, nIndex, + sc::tools::ChartSourceType::PIVOT_TABLE); + if (!pObject) + throw lang::IndexOutOfBoundsException(); + + OUString aName; + uno::Reference<embed::XEmbeddedObject> xObject = pObject->GetObjRef(); + if (xObject.is()) + aName = m_pDocShell->GetEmbeddedObjectContainer().GetEmbeddedObjectName(xObject); + + if (aName.isEmpty()) + throw lang::IndexOutOfBoundsException(); + + uno::Reference<table::XTablePivotChart> xChart(new TablePivotChart(m_pDocShell, m_nTab, aName)); + if (!xChart.is()) + throw lang::IndexOutOfBoundsException(); + + return uno::Any(xChart); +} + +uno::Type SAL_CALL TablePivotCharts::getElementType() +{ + return cppu::UnoType<table::XTablePivotChart>::get(); +} + +sal_Bool SAL_CALL TablePivotCharts::hasElements() +{ + SolarMutexGuard aGuard; + return getCount() != 0; +} + +uno::Any SAL_CALL TablePivotCharts::getByName(OUString const & rName) +{ + SolarMutexGuard aGuard; + + if (!sc::tools::findChartsByName(m_pDocShell, m_nTab, rName, sc::tools::ChartSourceType::PIVOT_TABLE)) + throw container::NoSuchElementException(); + + uno::Reference<table::XTablePivotChart> xChart(new TablePivotChart(m_pDocShell, m_nTab, rName)); + if (!xChart.is()) + throw container::NoSuchElementException(); + + return uno::Any(xChart); +} + +uno::Sequence<OUString> SAL_CALL TablePivotCharts::getElementNames() +{ + SolarMutexGuard aGuard; + + std::vector<OUString> aElements; + sc::tools::ChartIterator aIterator(m_pDocShell, m_nTab, sc::tools::ChartSourceType::PIVOT_TABLE); + + SdrOle2Obj* pOleObject = aIterator.next(); + while (pOleObject) + { + uno::Reference<embed::XEmbeddedObject> xObject = pOleObject->GetObjRef(); + if (xObject.is()) + { + OUString aName = m_pDocShell->GetEmbeddedObjectContainer().GetEmbeddedObjectName(xObject); + aElements.push_back(aName); + } + pOleObject = aIterator.next(); + } + return comphelper::containerToSequence(aElements); +} + +sal_Bool SAL_CALL TablePivotCharts::hasByName(OUString const & rName) +{ + SolarMutexGuard aGuard; + + return sc::tools::findChartsByName(m_pDocShell, m_nTab, rName, sc::tools::ChartSourceType::PIVOT_TABLE) != nullptr; +} + +} // end sc namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/unoobj/addruno.cxx b/sc/source/ui/unoobj/addruno.cxx new file mode 100644 index 0000000000..7a1c2b8b80 --- /dev/null +++ b/sc/source/ui/unoobj/addruno.cxx @@ -0,0 +1,298 @@ +/* -*- 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 <com/sun/star/table/CellAddress.hpp> +#include <com/sun/star/table/CellRangeAddress.hpp> +#include <cppuhelper/supportsservice.hxx> + +#include <svl/itemprop.hxx> +#include <vcl/svapp.hxx> + +#include <docsh.hxx> +#include <unonames.hxx> +#include <miscuno.hxx> +#include <convuno.hxx> +#include <addruno.hxx> + +using namespace com::sun::star; + +ScAddressConversionObj::ScAddressConversionObj(ScDocShell* pDocSh, bool _bIsRange) : + pDocShell( pDocSh ), + nRefSheet( 0 ), + bIsRange( _bIsRange ) +{ + pDocShell->GetDocument().AddUnoObject(*this); +} + +ScAddressConversionObj::~ScAddressConversionObj() +{ + SolarMutexGuard g; + + if (pDocShell) + pDocShell->GetDocument().RemoveUnoObject(*this); +} + +void ScAddressConversionObj::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + if ( rHint.GetId() == SfxHintId::Dying ) + { + pDocShell = nullptr; // invalid + } +} + +bool ScAddressConversionObj::ParseUIString( const OUString& rUIString, ::formula::FormulaGrammar::AddressConvention eConv ) +{ + if (!pDocShell) + return false; + + ScDocument& rDoc = pDocShell->GetDocument(); + bool bSuccess = false; + if ( bIsRange ) + { + ScRefFlags nResult = aRange.ParseAny( rUIString, rDoc, eConv ); + if ( nResult & ScRefFlags::VALID ) + { + if ( ( nResult & ScRefFlags::TAB_3D ) == ScRefFlags::ZERO ) + aRange.aStart.SetTab( static_cast<SCTAB>(nRefSheet) ); + if ( ( nResult & ScRefFlags::TAB2_3D ) == ScRefFlags::ZERO ) + aRange.aEnd.SetTab( aRange.aStart.Tab() ); + // different sheets are not supported in CellRangeAddress + if ( aRange.aStart.Tab() == aRange.aEnd.Tab() ) + bSuccess = true; + } + } + else + { + ScRefFlags nResult = aRange.aStart.Parse( rUIString, rDoc, eConv ); + if ( nResult & ScRefFlags::VALID ) + { + if ( ( nResult & ScRefFlags::TAB_3D ) == ScRefFlags::ZERO ) + aRange.aStart.SetTab( static_cast<SCTAB>(nRefSheet) ); + bSuccess = true; + } + } + return bSuccess; +} + +// XPropertySet + +uno::Reference<beans::XPropertySetInfo> SAL_CALL ScAddressConversionObj::getPropertySetInfo() +{ + SolarMutexGuard aGuard; + + if ( bIsRange ) + { + static const SfxItemPropertyMapEntry aPropertyMap[] = + { + { SC_UNONAME_ADDRESS, 0, cppu::UnoType<table::CellRangeAddress>::get(), 0, 0 }, + { SC_UNONAME_PERSREPR, 0, cppu::UnoType<OUString>::get(), 0, 0 }, + { SC_UNONAME_XLA1REPR, 0, cppu::UnoType<OUString>::get(), 0, 0 }, + { SC_UNONAME_REFSHEET, 0, cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { SC_UNONAME_UIREPR, 0, cppu::UnoType<OUString>::get(), 0, 0 }, + { SC_UNONAME_XLA1REPR, 0, cppu::UnoType<OUString>::get(), 0, 0 }, + }; + static uno::Reference<beans::XPropertySetInfo> aRef(new SfxItemPropertySetInfo( aPropertyMap )); + return aRef; + } + else + { + static const SfxItemPropertyMapEntry aPropertyMap[] = + { + { SC_UNONAME_ADDRESS, 0, cppu::UnoType<table::CellAddress>::get(), 0, 0 }, + { SC_UNONAME_PERSREPR, 0, cppu::UnoType<OUString>::get(), 0, 0 }, + { SC_UNONAME_XLA1REPR, 0, cppu::UnoType<OUString>::get(), 0, 0 }, + { SC_UNONAME_REFSHEET, 0, cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { SC_UNONAME_UIREPR, 0, cppu::UnoType<OUString>::get(), 0, 0 }, + { SC_UNONAME_XLA1REPR, 0, cppu::UnoType<OUString>::get(), 0, 0 }, + }; + static uno::Reference<beans::XPropertySetInfo> aRef(new SfxItemPropertySetInfo( aPropertyMap )); + return aRef; + } +} + +void SAL_CALL ScAddressConversionObj::setPropertyValue( const OUString& aPropertyName, const uno::Any& aValue ) +{ + if ( !pDocShell ) + throw uno::RuntimeException(); + + bool bSuccess = false; + if ( aPropertyName == SC_UNONAME_ADDRESS ) + { + // read the cell/range address from API struct + if ( bIsRange ) + { + table::CellRangeAddress aRangeAddress; + if ( aValue >>= aRangeAddress ) + { + ScUnoConversion::FillScRange( aRange, aRangeAddress ); + bSuccess = true; + } + } + else + { + table::CellAddress aCellAddress; + if ( aValue >>= aCellAddress ) + { + ScUnoConversion::FillScAddress( aRange.aStart, aCellAddress ); + bSuccess = true; + } + } + } + else if ( aPropertyName == SC_UNONAME_REFSHEET ) + { + // set the reference sheet + sal_Int32 nIntVal = 0; + if ( aValue >>= nIntVal ) + { + nRefSheet = nIntVal; + bSuccess = true; + } + } + else if ( aPropertyName == SC_UNONAME_UIREPR ) + { + // parse the UI representation string + OUString sRepresentation; + if (aValue >>= sRepresentation) + { + bSuccess = ParseUIString( sRepresentation ); + } + } + else if ( aPropertyName == SC_UNONAME_PERSREPR || aPropertyName == SC_UNONAME_XLA1REPR ) + { + ::formula::FormulaGrammar::AddressConvention eConv = aPropertyName == SC_UNONAME_XLA1REPR ? + ::formula::FormulaGrammar::CONV_XL_A1 : ::formula::FormulaGrammar::CONV_OOO; + + // parse the file format string + OUString sRepresentation; + if (aValue >>= sRepresentation) + { + OUString aUIString(sRepresentation); + + // cell or range: strip a single "." at the start + if ( aUIString[0]== '.' ) + aUIString = aUIString.copy( 1 ); + + if ( bIsRange ) + { + // range: also strip a "." after the last colon + sal_Int32 nColon = aUIString.lastIndexOf( ':' ); + if ( nColon >= 0 && nColon < aUIString.getLength() - 1 && + aUIString[nColon+1] == '.' ) + aUIString = aUIString.replaceAt( nColon+1, 1, u"" ); + } + + // parse the rest like a UI string + bSuccess = ParseUIString( aUIString, eConv ); + } + } + else + throw beans::UnknownPropertyException(aPropertyName); + + if ( !bSuccess ) + throw lang::IllegalArgumentException(); +} + +uno::Any SAL_CALL ScAddressConversionObj::getPropertyValue( const OUString& aPropertyName ) +{ + if ( !pDocShell ) + throw uno::RuntimeException(); + + ScDocument& rDoc = pDocShell->GetDocument(); + uno::Any aRet; + + if ( aPropertyName == SC_UNONAME_ADDRESS ) + { + if ( bIsRange ) + { + table::CellRangeAddress aRangeAddress; + ScUnoConversion::FillApiRange( aRangeAddress, aRange ); + aRet <<= aRangeAddress; + } + else + { + table::CellAddress aCellAddress; + ScUnoConversion::FillApiAddress( aCellAddress, aRange.aStart ); + aRet <<= aCellAddress; + } + } + else if ( aPropertyName == SC_UNONAME_REFSHEET ) + { + aRet <<= nRefSheet; + } + else if ( aPropertyName == SC_UNONAME_UIREPR ) + { + // generate UI representation string - include sheet only if different from ref sheet + OUString aFormatStr; + ScRefFlags nFlags = ScRefFlags::VALID; + if ( aRange.aStart.Tab() != nRefSheet ) + nFlags |= ScRefFlags::TAB_3D; + if ( bIsRange ) + aFormatStr = aRange.Format(rDoc, nFlags); + else + aFormatStr = aRange.aStart.Format(nFlags, &rDoc); + aRet <<= aFormatStr; + } + else if ( aPropertyName == SC_UNONAME_PERSREPR || aPropertyName == SC_UNONAME_XLA1REPR ) + { + ::formula::FormulaGrammar::AddressConvention eConv = aPropertyName == SC_UNONAME_XLA1REPR ? + ::formula::FormulaGrammar::CONV_XL_A1 : ::formula::FormulaGrammar::CONV_OOO; + + // generate file format string - always include sheet + OUString aFormatStr(aRange.aStart.Format(ScRefFlags::VALID | ScRefFlags::TAB_3D, &rDoc, eConv)); + if ( bIsRange ) + { + // manually concatenate range so both parts always have the sheet name + aFormatStr += ":"; + ScRefFlags nFlags = ScRefFlags::VALID; + if( eConv != ::formula::FormulaGrammar::CONV_XL_A1 ) + nFlags |= ScRefFlags::TAB_3D; + OUString aSecond(aRange.aEnd.Format(nFlags, &rDoc, eConv)); + aFormatStr += aSecond ; + } + aRet <<= aFormatStr; + } + else + throw beans::UnknownPropertyException(aPropertyName); + + return aRet; +} + +SC_IMPL_DUMMY_PROPERTY_LISTENER( ScAddressConversionObj ) + +// lang::XServiceInfo + +OUString SAL_CALL ScAddressConversionObj::getImplementationName() +{ + return "ScAddressConversionObj"; +} + +sal_Bool SAL_CALL ScAddressConversionObj::supportsService( const OUString& rServiceName ) +{ + return cppu::supportsService(this, rServiceName); +} + +uno::Sequence<OUString> SAL_CALL ScAddressConversionObj::getSupportedServiceNames() +{ + if (bIsRange) + return {SC_SERVICENAME_RANGEADDRESS}; + else + return {SC_SERVICENAME_CELLADDRESS}; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/unoobj/afmtuno.cxx b/sc/source/ui/unoobj/afmtuno.cxx new file mode 100644 index 0000000000..fce07d2b96 --- /dev/null +++ b/sc/source/ui/unoobj/afmtuno.cxx @@ -0,0 +1,718 @@ +/* -*- 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 <scitems.hxx> +#include <editeng/memberids.h> +#include <osl/diagnose.h> +#include <svl/poolitem.hxx> +#include <vcl/svapp.hxx> +#include <svx/algitem.hxx> +#include <editeng/boxitem.hxx> +#include <svx/unomid.hxx> +#include <unowids.hxx> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/table/TableBorder.hpp> +#include <com/sun/star/table/CellHoriJustify.hpp> +#include <com/sun/star/table/CellOrientation.hpp> +#include <com/sun/star/table/TableBorder2.hpp> +#include <com/sun/star/awt/FontSlant.hpp> + +#include <attrib.hxx> +#include <afmtuno.hxx> +#include <miscuno.hxx> +#include <autoform.hxx> +#include <scdll.hxx> +#include <unonames.hxx> +#include <cellsuno.hxx> + +using namespace ::com::sun::star; + +// an AutoFormat has always 16 entries +#define SC_AF_FIELD_COUNT 16 + +// AutoFormat map only for PropertySetInfo without Which-IDs + +static std::span<const SfxItemPropertyMapEntry> lcl_GetAutoFormatMap() +{ + static const SfxItemPropertyMapEntry aAutoFormatMap_Impl[] = + { + { SC_UNONAME_INCBACK, 0, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_INCBORD, 0, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_INCFONT, 0, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_INCJUST, 0, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_INCNUM, 0, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_INCWIDTH, 0, cppu::UnoType<bool>::get(), 0, 0 }, + }; + return aAutoFormatMap_Impl; +} + +//! number format (String/Language) ??? (in XNumberFormat only ReadOnly) +//! table::TableBorder ??!? + +static std::span<const SfxItemPropertyMapEntry> lcl_GetAutoFieldMap() +{ + static const SfxItemPropertyMapEntry aAutoFieldMap_Impl[] = + { + { SC_UNONAME_CELLBACK, ATTR_BACKGROUND, ::cppu::UnoType<sal_Int32>::get(), 0, MID_BACK_COLOR }, + { SC_UNONAME_CCOLOR, ATTR_FONT_COLOR, ::cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { SC_UNONAME_COUTL, ATTR_FONT_CONTOUR, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_CCROSS, ATTR_FONT_CROSSEDOUT, cppu::UnoType<bool>::get(), 0, MID_CROSSED_OUT }, + { SC_UNONAME_CFONT, ATTR_FONT, ::cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY }, + { SC_UNONAME_CFCHARS, ATTR_FONT, ::cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_CHAR_SET }, + { SC_UNO_CJK_CFCHARS, ATTR_CJK_FONT, ::cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_CHAR_SET }, + { SC_UNO_CTL_CFCHARS, ATTR_CTL_FONT, ::cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_CHAR_SET }, + { SC_UNONAME_CFFAMIL, ATTR_FONT, ::cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY }, + { SC_UNO_CJK_CFFAMIL, ATTR_CJK_FONT, ::cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY }, + { SC_UNO_CTL_CFFAMIL, ATTR_CTL_FONT, ::cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY }, + { SC_UNONAME_CFNAME, ATTR_FONT, ::cppu::UnoType<OUString>::get(), 0, MID_FONT_FAMILY_NAME }, + { SC_UNO_CJK_CFNAME, ATTR_CJK_FONT, ::cppu::UnoType<OUString>::get(), 0, MID_FONT_FAMILY_NAME }, + { SC_UNO_CTL_CFNAME, ATTR_CTL_FONT, ::cppu::UnoType<OUString>::get(), 0, MID_FONT_FAMILY_NAME }, + { SC_UNONAME_CFPITCH, ATTR_FONT, ::cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_PITCH }, + { SC_UNO_CJK_CFPITCH, ATTR_CJK_FONT, ::cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_PITCH }, + { SC_UNO_CTL_CFPITCH, ATTR_CTL_FONT, ::cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_PITCH }, + { SC_UNONAME_CFSTYLE, ATTR_FONT, ::cppu::UnoType<OUString>::get(), 0, MID_FONT_STYLE_NAME }, + { SC_UNO_CJK_CFSTYLE, ATTR_CJK_FONT, ::cppu::UnoType<OUString>::get(), 0, MID_FONT_STYLE_NAME }, + { SC_UNO_CTL_CFSTYLE, ATTR_CTL_FONT, ::cppu::UnoType<OUString>::get(), 0, MID_FONT_STYLE_NAME }, + { SC_UNONAME_CHEIGHT, ATTR_FONT_HEIGHT, ::cppu::UnoType<float>::get(), 0, MID_FONTHEIGHT | CONVERT_TWIPS }, + { SC_UNO_CJK_CHEIGHT, ATTR_CJK_FONT_HEIGHT, ::cppu::UnoType<float>::get(), 0, MID_FONTHEIGHT | CONVERT_TWIPS }, + { SC_UNO_CTL_CHEIGHT, ATTR_CTL_FONT_HEIGHT, ::cppu::UnoType<float>::get(), 0, MID_FONTHEIGHT | CONVERT_TWIPS }, + { SC_UNONAME_COVER, ATTR_FONT_OVERLINE, ::cppu::UnoType<sal_Int16>::get(), 0, MID_TL_STYLE }, + { SC_UNONAME_CPOST, ATTR_FONT_POSTURE, ::cppu::UnoType<awt::FontSlant>::get(), 0, MID_POSTURE }, + { SC_UNO_CJK_CPOST, ATTR_CJK_FONT_POSTURE, ::cppu::UnoType<awt::FontSlant>::get(), 0, MID_POSTURE }, + { SC_UNO_CTL_CPOST, ATTR_CTL_FONT_POSTURE, ::cppu::UnoType<awt::FontSlant>::get(), 0, MID_POSTURE }, + { SC_UNONAME_CSHADD, ATTR_FONT_SHADOWED, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_TBLBORD, SC_WID_UNO_TBLBORD, ::cppu::UnoType<table::TableBorder>::get(), 0, 0 | CONVERT_TWIPS }, + { SC_UNONAME_TBLBORD2, SC_WID_UNO_TBLBORD2, ::cppu::UnoType<table::TableBorder2>::get(), 0, 0 | CONVERT_TWIPS }, + { SC_UNONAME_CUNDER, ATTR_FONT_UNDERLINE, ::cppu::UnoType<sal_Int16>::get(), 0, MID_TL_STYLE }, + { SC_UNONAME_CWEIGHT, ATTR_FONT_WEIGHT, ::cppu::UnoType<float>::get(), 0, MID_WEIGHT }, + { SC_UNO_CJK_CWEIGHT, ATTR_CJK_FONT_WEIGHT, ::cppu::UnoType<float>::get(), 0, MID_WEIGHT }, + { SC_UNO_CTL_CWEIGHT, ATTR_CTL_FONT_WEIGHT, ::cppu::UnoType<float>::get(), 0, MID_WEIGHT }, + { SC_UNONAME_CELLHJUS, ATTR_HOR_JUSTIFY, ::cppu::UnoType<table::CellHoriJustify>::get(), 0, 0 }, + { SC_UNONAME_CELLHJUS_METHOD, ATTR_HOR_JUSTIFY_METHOD, ::cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { SC_UNONAME_CELLTRAN, ATTR_BACKGROUND, cppu::UnoType<bool>::get(), 0, MID_GRAPHIC_TRANSPARENT }, + { SC_UNONAME_WRAP, ATTR_LINEBREAK, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_CELLORI, ATTR_STACKED, ::cppu::UnoType<table::CellOrientation>::get(), 0, 0 }, + { SC_UNONAME_PBMARGIN, ATTR_MARGIN, ::cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_LO_MARGIN | CONVERT_TWIPS }, + { SC_UNONAME_PLMARGIN, ATTR_MARGIN, ::cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_L_MARGIN | CONVERT_TWIPS }, + { SC_UNONAME_PRMARGIN, ATTR_MARGIN, ::cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_R_MARGIN | CONVERT_TWIPS }, + { SC_UNONAME_PTMARGIN, ATTR_MARGIN, ::cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_UP_MARGIN | CONVERT_TWIPS }, + { SC_UNONAME_ROTANG, ATTR_ROTATE_VALUE, ::cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { SC_UNONAME_ROTREF, ATTR_ROTATE_MODE, ::cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { SC_UNONAME_CELLVJUS, ATTR_VER_JUSTIFY, ::cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { SC_UNONAME_CELLVJUS_METHOD, ATTR_VER_JUSTIFY_METHOD, ::cppu::UnoType<sal_Int32>::get(), 0, 0 }, + }; + return aAutoFieldMap_Impl; +} + +constexpr OUString SCAUTOFORMATSOBJ_SERVICE = u"com.sun.star.sheet.TableAutoFormats"_ustr; + +SC_SIMPLE_SERVICE_INFO( ScAutoFormatFieldObj, "ScAutoFormatFieldObj", "com.sun.star.sheet.TableAutoFormatField" ) +SC_SIMPLE_SERVICE_INFO( ScAutoFormatObj, "ScAutoFormatObj", "com.sun.star.sheet.TableAutoFormat" ) +SC_SIMPLE_SERVICE_INFO( ScAutoFormatsObj, "stardiv.StarCalc.ScAutoFormatsObj", SCAUTOFORMATSOBJ_SERVICE ) + +static bool lcl_FindAutoFormatIndex( const ScAutoFormat& rFormats, std::u16string_view rName, sal_uInt16& rOutIndex ) +{ + ScAutoFormat::const_iterator itBeg = rFormats.begin(), itEnd = rFormats.end(); + for (ScAutoFormat::const_iterator it = itBeg; it != itEnd; ++it) + { + const ScAutoFormatData *const pEntry = it->second.get(); + const OUString& aEntryName = pEntry->GetName(); + if ( aEntryName == rName ) + { + size_t nPos = std::distance(itBeg, it); + rOutIndex = nPos; + return true; + } + } + return false; +} + +ScAutoFormatsObj::ScAutoFormatsObj() +{ + //! This object should only exist once and it must be known to Auto-Format-Data, + //! so that changes can be broadcasted +} + +ScAutoFormatsObj::~ScAutoFormatsObj() +{ +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +ScAutoFormatsObj_get_implementation(css::uno::XComponentContext*, css::uno::Sequence<css::uno::Any> const &) +{ + SolarMutexGuard aGuard; + ScDLL::Init(); + return cppu::acquire(new ScAutoFormatsObj); +} + +// XTableAutoFormats + +rtl::Reference<ScAutoFormatObj> ScAutoFormatsObj::GetObjectByIndex_Impl(sal_uInt16 nIndex) +{ + if (nIndex < ScGlobal::GetOrCreateAutoFormat()->size()) + return new ScAutoFormatObj(nIndex); + + return nullptr; // wrong index +} + +rtl::Reference<ScAutoFormatObj> ScAutoFormatsObj::GetObjectByName_Impl(std::u16string_view aName) +{ + sal_uInt16 nIndex; + if (lcl_FindAutoFormatIndex( + *ScGlobal::GetOrCreateAutoFormat(), aName, nIndex )) + return GetObjectByIndex_Impl(nIndex); + return nullptr; +} + +// container::XNameContainer + +void SAL_CALL ScAutoFormatsObj::insertByName( const OUString& aName, const uno::Any& aElement ) +{ + SolarMutexGuard aGuard; + bool bDone = false; + // Reflection need not be uno::XInterface, can be any interface... + uno::Reference< uno::XInterface > xInterface(aElement, uno::UNO_QUERY); + if ( xInterface.is() ) + { + ScAutoFormatObj* pFormatObj = dynamic_cast<ScAutoFormatObj*>( xInterface.get() ); + if ( pFormatObj && !pFormatObj->IsInserted() ) + { + ScAutoFormat* pFormats = ScGlobal::GetOrCreateAutoFormat(); + + sal_uInt16 nDummy; + if (lcl_FindAutoFormatIndex( *pFormats, aName, nDummy )) + { + throw container::ElementExistException(); + } + + std::unique_ptr<ScAutoFormatData> pNew(new ScAutoFormatData()); + pNew->SetName( aName ); + + if (pFormats->insert(std::move(pNew)) != pFormats->end()) + { + //! notify to other objects + pFormats->Save(); + + sal_uInt16 nNewIndex; + if (lcl_FindAutoFormatIndex( *pFormats, aName, nNewIndex )) + { + pFormatObj->InitFormat( nNewIndex ); // can be used now + bDone = true; + } + } + else + { + OSL_FAIL("AutoFormat could not be inserted"); + throw uno::RuntimeException(); + } + } + } + + if (!bDone) + { + // other errors are handled above + throw lang::IllegalArgumentException(); + } +} + +void SAL_CALL ScAutoFormatsObj::replaceByName( const OUString& aName, const uno::Any& aElement ) +{ + SolarMutexGuard aGuard; + //! combine? + removeByName( aName ); + insertByName( aName, aElement ); +} + +void SAL_CALL ScAutoFormatsObj::removeByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + ScAutoFormat* pFormats = ScGlobal::GetOrCreateAutoFormat(); + + ScAutoFormat::iterator it = pFormats->find(aName); + if (it == pFormats->end()) + { + throw container::NoSuchElementException(); + } + pFormats->erase(it); + + //! notify to other objects + pFormats->Save(); // save immediately + +} + +// container::XEnumerationAccess + +uno::Reference<container::XEnumeration> SAL_CALL ScAutoFormatsObj::createEnumeration() +{ + SolarMutexGuard aGuard; + return new ScIndexEnumeration(this, "com.sun.star.sheet.TableAutoFormatEnumeration"); +} + +// container::XIndexAccess + +sal_Int32 SAL_CALL ScAutoFormatsObj::getCount() +{ + SolarMutexGuard aGuard; + return ScGlobal::GetOrCreateAutoFormat()->size(); +} + +uno::Any SAL_CALL ScAutoFormatsObj::getByIndex( sal_Int32 nIndex ) +{ + SolarMutexGuard aGuard; + uno::Reference< container::XNamed > xFormat(GetObjectByIndex_Impl(static_cast<sal_uInt16>(nIndex))); + if (!xFormat.is()) + throw lang::IndexOutOfBoundsException(); + return uno::Any(xFormat); +} + +uno::Type SAL_CALL ScAutoFormatsObj::getElementType() +{ + return cppu::UnoType<container::XNamed>::get(); // must match getByIndex +} + +sal_Bool SAL_CALL ScAutoFormatsObj::hasElements() +{ + SolarMutexGuard aGuard; + return ( getCount() != 0 ); +} + +// container::XNameAccess + +uno::Any SAL_CALL ScAutoFormatsObj::getByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + uno::Reference< container::XNamed > xFormat(GetObjectByName_Impl(aName)); + if (!xFormat.is()) + throw container::NoSuchElementException(); + return uno::Any(xFormat); +} + +uno::Sequence<OUString> SAL_CALL ScAutoFormatsObj::getElementNames() +{ + SolarMutexGuard aGuard; + ScAutoFormat* pFormats = ScGlobal::GetOrCreateAutoFormat(); + uno::Sequence<OUString> aSeq(pFormats->size()); + OUString* pAry = aSeq.getArray(); + size_t i = 0; + for (const auto& rEntry : *pFormats) + { + pAry[i] = rEntry.second->GetName(); + ++i; + } + return aSeq; +} + +sal_Bool SAL_CALL ScAutoFormatsObj::hasByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + sal_uInt16 nDummy; + return lcl_FindAutoFormatIndex( + *ScGlobal::GetOrCreateAutoFormat(), aName, nDummy ); +} + +ScAutoFormatObj::ScAutoFormatObj(sal_uInt16 nIndex) : + aPropSet( lcl_GetAutoFormatMap() ), + nFormatIndex( nIndex ) +{ +} + +ScAutoFormatObj::~ScAutoFormatObj() +{ + // If an AutoFormat object is released, then eventually changes are saved + // so that they become visible in e.g Writer + + if (IsInserted()) + { + ScAutoFormat* pFormats = ScGlobal::GetAutoFormat(); + if ( pFormats && pFormats->IsSaveLater() ) + pFormats->Save(); + + // Save() resets flag SaveLater + } +} + +void ScAutoFormatObj::InitFormat( sal_uInt16 nNewIndex ) +{ + OSL_ENSURE( nFormatIndex == SC_AFMTOBJ_INVALID, "ScAutoFormatObj::InitFormat is multiple" ); + nFormatIndex = nNewIndex; +} + +// XTableAutoFormat + +rtl::Reference<ScAutoFormatFieldObj> ScAutoFormatObj::GetObjectByIndex_Impl(sal_uInt16 nIndex) +{ + if ( IsInserted() && nIndex < SC_AF_FIELD_COUNT ) + return new ScAutoFormatFieldObj( nFormatIndex, nIndex ); + + return nullptr; +} + +// container::XEnumerationAccess + +uno::Reference<container::XEnumeration> SAL_CALL ScAutoFormatObj::createEnumeration() +{ + SolarMutexGuard aGuard; + return new ScIndexEnumeration(this, "com.sun.star.sheet.TableAutoFormatEnumeration"); +} + +// container::XIndexAccess + +sal_Int32 SAL_CALL ScAutoFormatObj::getCount() +{ + SolarMutexGuard aGuard; + if (IsInserted()) + return SC_AF_FIELD_COUNT; // always 16 elements + else + return 0; +} + +uno::Any SAL_CALL ScAutoFormatObj::getByIndex( sal_Int32 nIndex ) +{ + SolarMutexGuard aGuard; + + if ( nIndex < 0 || nIndex >= getCount() ) + throw lang::IndexOutOfBoundsException(); + + if (IsInserted()) + return uno::Any(uno::Reference< beans::XPropertySet >(GetObjectByIndex_Impl(static_cast<sal_uInt16>(nIndex)))); + return uno::Any(); +} + +uno::Type SAL_CALL ScAutoFormatObj::getElementType() +{ + return cppu::UnoType<beans::XPropertySet>::get(); // must match getByIndex +} + +sal_Bool SAL_CALL ScAutoFormatObj::hasElements() +{ + SolarMutexGuard aGuard; + return ( getCount() != 0 ); +} + +// container::XNamed + +OUString SAL_CALL ScAutoFormatObj::getName() +{ + SolarMutexGuard aGuard; + ScAutoFormat* pFormats = ScGlobal::GetOrCreateAutoFormat(); + if (IsInserted() && nFormatIndex < pFormats->size()) + return pFormats->findByIndex(nFormatIndex)->GetName(); + + return OUString(); +} + +void SAL_CALL ScAutoFormatObj::setName( const OUString& aNewName ) +{ + SolarMutexGuard aGuard; + ScAutoFormat* pFormats = ScGlobal::GetOrCreateAutoFormat(); + + sal_uInt16 nDummy; + if (!IsInserted() || nFormatIndex >= pFormats->size() || + lcl_FindAutoFormatIndex( *pFormats, aNewName, nDummy )) + { + // not inserted or name exists + throw uno::RuntimeException(); + } + + ScAutoFormat::iterator it = pFormats->begin(); + std::advance(it, nFormatIndex); + ScAutoFormatData *const pData = it->second.get(); + OSL_ENSURE(pData,"AutoFormat data not available"); + + std::unique_ptr<ScAutoFormatData> pNew(new ScAutoFormatData(*pData)); + pNew->SetName( aNewName ); + + pFormats->erase(it); + it = pFormats->insert(std::move(pNew)); + if (it != pFormats->end()) + { + ScAutoFormat::iterator itBeg = pFormats->begin(); + nFormatIndex = std::distance(itBeg, it); + + //! notify to other objects + pFormats->SetSaveLater(true); + } + else + { + OSL_FAIL("AutoFormat could not be inserted"); + nFormatIndex = 0; //! old index invalid + } +} + +// beans::XPropertySet + +uno::Reference<beans::XPropertySetInfo> SAL_CALL ScAutoFormatObj::getPropertySetInfo() +{ + SolarMutexGuard aGuard; + static uno::Reference< beans::XPropertySetInfo > aRef(new SfxItemPropertySetInfo( aPropSet.getPropertyMap() )); + return aRef; +} + +void SAL_CALL ScAutoFormatObj::setPropertyValue( + const OUString& aPropertyName, const uno::Any& aValue ) +{ + SolarMutexGuard aGuard; + ScAutoFormat* pFormats = ScGlobal::GetOrCreateAutoFormat(); + if (!(IsInserted() && nFormatIndex < pFormats->size())) + return; + + ScAutoFormatData* pData = pFormats->findByIndex(nFormatIndex); + OSL_ENSURE(pData,"AutoFormat data not available"); + + bool bBool = false; + if (aPropertyName == SC_UNONAME_INCBACK && (aValue >>= bBool)) + pData->SetIncludeBackground( bBool ); + else if (aPropertyName == SC_UNONAME_INCBORD && (aValue >>= bBool)) + pData->SetIncludeFrame( bBool ); + else if (aPropertyName == SC_UNONAME_INCFONT && (aValue >>= bBool)) + pData->SetIncludeFont( bBool ); + else if (aPropertyName == SC_UNONAME_INCJUST && (aValue >>= bBool)) + pData->SetIncludeJustify( bBool ); + else if (aPropertyName == SC_UNONAME_INCNUM && (aValue >>= bBool)) + pData->SetIncludeValueFormat( bBool ); + else if (aPropertyName == SC_UNONAME_INCWIDTH && (aValue >>= bBool)) + pData->SetIncludeWidthHeight( bBool ); + + // else error + + //! notify to other objects + pFormats->SetSaveLater(true); +} + +uno::Any SAL_CALL ScAutoFormatObj::getPropertyValue( const OUString& aPropertyName ) +{ + SolarMutexGuard aGuard; + uno::Any aAny; + + ScAutoFormat* pFormats = ScGlobal::GetOrCreateAutoFormat(); + if (IsInserted() && nFormatIndex < pFormats->size()) + { + ScAutoFormatData* pData = pFormats->findByIndex(nFormatIndex); + OSL_ENSURE(pData,"AutoFormat data not available"); + + bool bValue; + bool bError = false; + + if (aPropertyName == SC_UNONAME_INCBACK) + bValue = pData->GetIncludeBackground(); + else if (aPropertyName == SC_UNONAME_INCBORD) + bValue = pData->GetIncludeFrame(); + else if (aPropertyName == SC_UNONAME_INCFONT) + bValue = pData->GetIncludeFont(); + else if (aPropertyName == SC_UNONAME_INCJUST) + bValue = pData->GetIncludeJustify(); + else if (aPropertyName == SC_UNONAME_INCNUM) + bValue = pData->GetIncludeValueFormat(); + else if (aPropertyName == SC_UNONAME_INCWIDTH) + bValue = pData->GetIncludeWidthHeight(); + else + bError = true; // unknown property + + if (!bError) + aAny <<= bValue; + } + + return aAny; +} + +SC_IMPL_DUMMY_PROPERTY_LISTENER( ScAutoFormatObj ) + +ScAutoFormatFieldObj::ScAutoFormatFieldObj(sal_uInt16 nFormat, sal_uInt16 nField) : + aPropSet( lcl_GetAutoFieldMap() ), + nFormatIndex( nFormat ), + nFieldIndex( nField ) +{ +} + +ScAutoFormatFieldObj::~ScAutoFormatFieldObj() +{ +} + +// beans::XPropertySet + +uno::Reference<beans::XPropertySetInfo> SAL_CALL ScAutoFormatFieldObj::getPropertySetInfo() +{ + SolarMutexGuard aGuard; + static uno::Reference< beans::XPropertySetInfo > aRef(new SfxItemPropertySetInfo( aPropSet.getPropertyMap() )); + return aRef; +} + +void SAL_CALL ScAutoFormatFieldObj::setPropertyValue( + const OUString& aPropertyName, const uno::Any& aValue ) +{ + SolarMutexGuard aGuard; + ScAutoFormat* pFormats = ScGlobal::GetOrCreateAutoFormat(); + const SfxItemPropertyMapEntry* pEntry = + aPropSet.getPropertyMap().getByName( aPropertyName ); + + if ( !(pEntry && pEntry->nWID && nFormatIndex < pFormats->size()) ) + return; + + ScAutoFormatData* pData = pFormats->findByIndex(nFormatIndex); + + if ( IsScItemWid( pEntry->nWID ) ) + { + if( const SfxPoolItem* pItem = pData->GetItem( nFieldIndex, pEntry->nWID ) ) + { + bool bDone = false; + + switch( pEntry->nWID ) + { + case ATTR_STACKED: + { + table::CellOrientation eOrient; + if( aValue >>= eOrient ) + { + switch( eOrient ) + { + case table::CellOrientation_STANDARD: + pData->PutItem( nFieldIndex, ScVerticalStackCell( false ) ); + break; + case table::CellOrientation_TOPBOTTOM: + pData->PutItem( nFieldIndex, ScVerticalStackCell( false ) ); + pData->PutItem( nFieldIndex, ScRotateValueItem( 27000_deg100 ) ); + break; + case table::CellOrientation_BOTTOMTOP: + pData->PutItem( nFieldIndex, ScVerticalStackCell( false ) ); + pData->PutItem( nFieldIndex, ScRotateValueItem( 9000_deg100 ) ); + break; + case table::CellOrientation_STACKED: + pData->PutItem( nFieldIndex, ScVerticalStackCell( true ) ); + break; + default: + { + // added to avoid warnings + } + } + bDone = true; + } + } + break; + default: + std::unique_ptr<SfxPoolItem> pNewItem(pItem->Clone()); + bDone = pNewItem->PutValue( aValue, pEntry->nMemberId ); + if (bDone) + pData->PutItem( nFieldIndex, *pNewItem ); + } + + if (bDone) + //! Notify to other objects? + pFormats->SetSaveLater(true); + } + } + else + { + switch (pEntry->nWID) + { + case SC_WID_UNO_TBLBORD: + { + table::TableBorder aBorder; + if ( aValue >>= aBorder ) // empty = nothing to do + { + SvxBoxItem aOuter(ATTR_BORDER); + SvxBoxInfoItem aInner(ATTR_BORDER_INNER); + ScHelperFunctions::FillBoxItems( aOuter, aInner, aBorder ); + pData->PutItem( nFieldIndex, aOuter ); + + //! Notify for other objects? + pFormats->SetSaveLater(true); + } + } + break; + case SC_WID_UNO_TBLBORD2: + { + table::TableBorder2 aBorder2; + if ( aValue >>= aBorder2 ) // empty = nothing to do + { + SvxBoxItem aOuter(ATTR_BORDER); + SvxBoxInfoItem aInner(ATTR_BORDER_INNER); + ScHelperFunctions::FillBoxItems( aOuter, aInner, aBorder2 ); + pData->PutItem( nFieldIndex, aOuter ); + + //! Notify for other objects? + pFormats->SetSaveLater(true); + } + } + break; + } + } +} + +uno::Any SAL_CALL ScAutoFormatFieldObj::getPropertyValue( const OUString& aPropertyName ) +{ + SolarMutexGuard aGuard; + uno::Any aVal; + + ScAutoFormat* pFormats = ScGlobal::GetOrCreateAutoFormat(); + const SfxItemPropertyMapEntry* pEntry = + aPropSet.getPropertyMap().getByName( aPropertyName ); + + if ( pEntry && pEntry->nWID && nFormatIndex < pFormats->size() ) + { + const ScAutoFormatData* pData = pFormats->findByIndex(nFormatIndex); + + if ( IsScItemWid( pEntry->nWID ) ) + { + if( const SfxPoolItem* pItem = pData->GetItem( nFieldIndex, pEntry->nWID ) ) + { + switch( pEntry->nWID ) + { + case ATTR_STACKED: + { + const ScRotateValueItem* pRotItem = pData->GetItem( nFieldIndex, ATTR_ROTATE_VALUE ); + Degree100 nRot = pRotItem ? pRotItem->GetValue() : 0_deg100; + bool bStacked = static_cast<const ScVerticalStackCell*>(pItem)->GetValue(); + SvxOrientationItem( nRot, bStacked, TypedWhichId<SvxOrientationItem>(0) ).QueryValue( aVal ); + } + break; + default: + pItem->QueryValue( aVal, pEntry->nMemberId ); + } + } + } + else + { + switch (pEntry->nWID) + { + case SC_WID_UNO_TBLBORD: + case SC_WID_UNO_TBLBORD2: + { + const SfxPoolItem* pItem = pData->GetItem(nFieldIndex, ATTR_BORDER); + if (pItem) + { + SvxBoxItem aOuter(*static_cast<const SvxBoxItem*>(pItem)); + SvxBoxInfoItem aInner(ATTR_BORDER_INNER); + + if (pEntry->nWID == SC_WID_UNO_TBLBORD2) + ScHelperFunctions::AssignTableBorder2ToAny( aVal, aOuter, aInner); + else + ScHelperFunctions::AssignTableBorderToAny( aVal, aOuter, aInner); + } + } + break; + } + } + } + + return aVal; +} + +SC_IMPL_DUMMY_PROPERTY_LISTENER( ScAutoFormatFieldObj ) + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/unoobj/appluno.cxx b/sc/source/ui/unoobj/appluno.cxx new file mode 100644 index 0000000000..3b12dc9da7 --- /dev/null +++ b/sc/source/ui/unoobj/appluno.cxx @@ -0,0 +1,619 @@ +/* -*- 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 <appluno.hxx> +#include <sal/types.h> +#include <o3tl/safeint.hxx> +#include <osl/diagnose.h> +#include <formula/funcvarargs.h> + +#include <vcl/svapp.hxx> +#include <sfx2/app.hxx> +#include <sfx2/sfxmodelfactory.hxx> +#include <miscuno.hxx> +#include <scmod.hxx> +#include <appoptio.hxx> +#include <inputopt.hxx> +#include <printopt.hxx> +#include <userlist.hxx> +#include <scdll.hxx> +#include <unonames.hxx> +#include <funcdesc.hxx> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/sheet/FunctionArgument.hpp> +#include <memory> + +using namespace com::sun::star; + +// Special value for zoom +//! somewhere central +#define SC_ZOOMVAL_OPTIMAL (-1) +#define SC_ZOOMVAL_WHOLEPAGE (-2) +#define SC_ZOOMVAL_PAGEWIDTH (-3) + +// Number of PropertyValues in a function description +#define SC_FUNCDESC_PROPCOUNT 5 + +// everything without Which-ID, map only for PropertySetInfo + +static std::span<const SfxItemPropertyMapEntry> lcl_GetSettingsPropertyMap() +{ + static const SfxItemPropertyMapEntry aSettingsPropertyMap_Impl[] = + { + { SC_UNONAME_DOAUTOCP, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNONAME_ENTERED, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNONAME_EXPREF, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNONAME_EXTFMT, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNONAME_LINKUPD, 0, cppu::UnoType<sal_Int16>::get(), 0, 0}, + { SC_UNONAME_MARKHDR, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNONAME_METRIC, 0, cppu::UnoType<sal_Int16>::get(), 0, 0}, + { SC_UNONAME_MOVEDIR, 0, cppu::UnoType<sal_Int16>::get(), 0, 0}, + { SC_UNONAME_MOVESEL, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNONAME_PRALLSH, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNONAME_PREMPTY, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNONAME_RANGEFIN, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNONAME_SCALE, 0, cppu::UnoType<sal_Int16>::get(), 0, 0}, + { SC_UNONAME_STBFUNC, 0, cppu::UnoType<sal_Int16>::get(), 0, 0}, + { SC_UNONAME_ULISTS, 0, cppu::UnoType<uno::Sequence<OUString>>::get(), 0, 0}, + { SC_UNONAME_PRMETRICS,0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNONAME_USETABCOL,0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNONAME_REPLWARN, 0, cppu::UnoType<bool>::get(), 0, 0}, + }; + return aSettingsPropertyMap_Impl; +} + +constexpr OUString SCFUNCTIONLISTOBJ_SERVICE = u"com.sun.star.sheet.FunctionDescriptions"_ustr; +constexpr OUString SCRECENTFUNCTIONSOBJ_SERVICE = u"com.sun.star.sheet.RecentFunctions"_ustr; +constexpr OUString SCSPREADSHEETSETTINGS_SERVICE = u"com.sun.star.sheet.GlobalSheetSettings"_ustr; + +SC_SIMPLE_SERVICE_INFO( ScFunctionListObj, "stardiv.StarCalc.ScFunctionListObj", SCFUNCTIONLISTOBJ_SERVICE ) +SC_SIMPLE_SERVICE_INFO( ScRecentFunctionsObj, "stardiv.StarCalc.ScRecentFunctionsObj", SCRECENTFUNCTIONSOBJ_SERVICE ) +SC_SIMPLE_SERVICE_INFO( ScSpreadsheetSettings, "stardiv.StarCalc.ScSpreadsheetSettings", SCSPREADSHEETSETTINGS_SERVICE ) + + +ScSpreadsheetSettings::ScSpreadsheetSettings() : + aPropSet( lcl_GetSettingsPropertyMap() ) +{ +} + +ScSpreadsheetSettings::~ScSpreadsheetSettings() +{ +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +Calc_ScSpreadsheetSettings_get_implementation( + css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&) +{ + SolarMutexGuard aGuard; + ScDLL::Init(); + return cppu::acquire(new ScSpreadsheetSettings()); +} + + +bool ScSpreadsheetSettings::getPropertyBool(const OUString& aPropertyName) +{ + uno::Any any = getPropertyValue(aPropertyName); + bool b = false; + any >>= b; + return b; +} + +sal_Int16 ScSpreadsheetSettings::getPropertyInt16(const OUString& aPropertyName) +{ + uno::Any any = getPropertyValue(aPropertyName); + sal_Int16 b = 0; + any >>= b; + return b; +} + +// XPropertySet + +uno::Reference<beans::XPropertySetInfo> SAL_CALL ScSpreadsheetSettings::getPropertySetInfo() +{ + SolarMutexGuard aGuard; + static uno::Reference<beans::XPropertySetInfo> aRef( + new SfxItemPropertySetInfo( aPropSet.getPropertyMap() )); + return aRef; +} + +void SAL_CALL ScSpreadsheetSettings::setPropertyValue( + const OUString& aPropertyName, const uno::Any& aValue ) +{ + SolarMutexGuard aGuard; + + ScModule* pScMod = SC_MOD(); + ScAppOptions aAppOpt(pScMod->GetAppOptions()); + ScInputOptions aInpOpt(pScMod->GetInputOptions()); + bool bSaveApp = false; + bool bSaveInp = false; + // print options aren't loaded until needed + + if (aPropertyName == SC_UNONAME_DOAUTOCP) + { + aAppOpt.SetAutoComplete( ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + bSaveApp = true; + } + else if (aPropertyName == SC_UNONAME_ENTERED) + { + aInpOpt.SetEnterEdit( ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + bSaveInp = true; + } + else if (aPropertyName == SC_UNONAME_EXPREF) + { + aInpOpt.SetExpandRefs( ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + bSaveInp = true; + } + else if (aPropertyName == SC_UNONAME_EXTFMT) + { + aInpOpt.SetExtendFormat( ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + bSaveInp = true; + } + else if (aPropertyName == SC_UNONAME_LINKUPD) + { + // XXX NOTE: this is not css::document::Settings property + // LinkUpdateMode but css::sheet::XGlobalSheetSettings attribute + // LinkUpdateMode. + sal_Int16 n; + if (!(aValue >>= n) || n < 0 || n >= ScLkUpdMode::LM_UNKNOWN) + { + throw css::lang::IllegalArgumentException( + ("LinkUpdateMode property value must be a SHORT with a value in the range of 0--2" + " as documented for css::sheet::XGlobalSheetSettings attribute LinkUpdateMode"), + css::uno::Reference<css::uno::XInterface>(), -1); + } + aAppOpt.SetLinkMode( static_cast<ScLkUpdMode>(n) ); + bSaveApp = true; + } + else if (aPropertyName == SC_UNONAME_MARKHDR) + { + aInpOpt.SetMarkHeader( ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + bSaveInp = true; + } + else if (aPropertyName == SC_UNONAME_MOVESEL) + { + aInpOpt.SetMoveSelection( ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + bSaveInp = true; + } + else if (aPropertyName == SC_UNONAME_RANGEFIN) + { + aInpOpt.SetRangeFinder( ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + bSaveInp = true; + } + else if (aPropertyName == SC_UNONAME_USETABCOL) + { + aInpOpt.SetUseTabCol( ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + bSaveInp = true; + } + else if (aPropertyName == SC_UNONAME_PRMETRICS) + { + aInpOpt.SetTextWysiwyg( ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + bSaveInp = true; + } + else if (aPropertyName == SC_UNONAME_REPLWARN) + { + aInpOpt.SetReplaceCellsWarn( ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + bSaveInp = true; + } + else if (aPropertyName == SC_UNONAME_METRIC) + { + aAppOpt.SetAppMetric( static_cast<FieldUnit>(ScUnoHelpFunctions::GetInt16FromAny( aValue )) ); + bSaveApp = true; + } + else if (aPropertyName == SC_UNONAME_MOVEDIR) + { + aInpOpt.SetMoveDir( ScUnoHelpFunctions::GetInt16FromAny( aValue ) ); + bSaveInp = true; + } + else if (aPropertyName == SC_UNONAME_SCALE) + { + short nVal = ScUnoHelpFunctions::GetInt16FromAny( aValue ); + if ( nVal < 0 ) + { + SvxZoomType eType = SvxZoomType::PERCENT; + switch (nVal) + { + case SC_ZOOMVAL_OPTIMAL: eType = SvxZoomType::OPTIMAL; break; + case SC_ZOOMVAL_WHOLEPAGE: eType = SvxZoomType::WHOLEPAGE; break; + case SC_ZOOMVAL_PAGEWIDTH: eType = SvxZoomType::PAGEWIDTH; break; + } + aAppOpt.SetZoomType( eType ); + } + else if ( nVal >= MINZOOM && nVal <= MAXZOOM ) + { + aAppOpt.SetZoom( nVal ); + aAppOpt.SetZoomType( SvxZoomType::PERCENT ); + } + bSaveApp = true; + } + else if (aPropertyName == SC_UNONAME_STBFUNC) + { + aAppOpt.SetStatusFunc( ScUnoHelpFunctions::GetInt16FromAny( aValue ) ); + bSaveApp = true; + } + else if (aPropertyName == SC_UNONAME_ULISTS) + { + ScUserList* pUserList = ScGlobal::GetUserList(); + uno::Sequence<OUString> aSeq; + if ( pUserList && ( aValue >>= aSeq ) ) + { + // directly change the active list + // ScGlobal::SetUseTabCol does not do much else + + pUserList->clear(); + for (const OUString& aEntry : std::as_const(aSeq)) + { + pUserList->emplace_back(aEntry); + } + bSaveApp = true; // List with App-Options are saved + } + } + else if (aPropertyName == SC_UNONAME_PRALLSH) + { + ScPrintOptions aPrintOpt(pScMod->GetPrintOptions()); + aPrintOpt.SetAllSheets( ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + pScMod->SetPrintOptions( aPrintOpt ); + } + else if (aPropertyName == SC_UNONAME_PREMPTY) + { + ScPrintOptions aPrintOpt(pScMod->GetPrintOptions()); + aPrintOpt.SetSkipEmpty( !ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); // reversed + pScMod->SetPrintOptions( aPrintOpt ); + SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScPrintOptions ) ); // update previews + } + + if ( bSaveApp ) + pScMod->SetAppOptions( aAppOpt ); + if ( bSaveInp ) + pScMod->SetInputOptions( aInpOpt ); +} + +uno::Any SAL_CALL ScSpreadsheetSettings::getPropertyValue( const OUString& aPropertyName ) +{ + SolarMutexGuard aGuard; + uno::Any aRet; + + ScModule* pScMod = SC_MOD(); + ScAppOptions aAppOpt = pScMod->GetAppOptions(); + const ScInputOptions& aInpOpt = pScMod->GetInputOptions(); + // print options aren't loaded until needed + + if (aPropertyName == SC_UNONAME_DOAUTOCP) aRet <<= aAppOpt.GetAutoComplete(); + else if (aPropertyName == SC_UNONAME_ENTERED ) aRet <<= aInpOpt.GetEnterEdit(); + else if (aPropertyName == SC_UNONAME_EXPREF ) aRet <<= aInpOpt.GetExpandRefs(); + else if (aPropertyName == SC_UNONAME_EXTFMT ) aRet <<= aInpOpt.GetExtendFormat(); + else if (aPropertyName == SC_UNONAME_LINKUPD ) aRet <<= static_cast<sal_Int16>(aAppOpt.GetLinkMode()); + else if (aPropertyName == SC_UNONAME_MARKHDR ) aRet <<= aInpOpt.GetMarkHeader(); + else if (aPropertyName == SC_UNONAME_MOVESEL ) aRet <<= aInpOpt.GetMoveSelection(); + else if (aPropertyName == SC_UNONAME_RANGEFIN ) aRet <<= aInpOpt.GetRangeFinder(); + else if (aPropertyName == SC_UNONAME_USETABCOL ) aRet <<= aInpOpt.GetUseTabCol(); + else if (aPropertyName == SC_UNONAME_PRMETRICS ) aRet <<= aInpOpt.GetTextWysiwyg(); + else if (aPropertyName == SC_UNONAME_REPLWARN ) aRet <<= aInpOpt.GetReplaceCellsWarn(); + else if (aPropertyName == SC_UNONAME_METRIC ) aRet <<= static_cast<sal_Int16>(aAppOpt.GetAppMetric()); + else if (aPropertyName == SC_UNONAME_MOVEDIR ) aRet <<= static_cast<sal_Int16>(aInpOpt.GetMoveDir()); + else if (aPropertyName == SC_UNONAME_STBFUNC ) aRet <<= static_cast<sal_Int16>(aAppOpt.GetStatusFunc()); + else if (aPropertyName == SC_UNONAME_SCALE ) + { + sal_Int16 nZoomVal = 0; + switch ( aAppOpt.GetZoomType() ) + { + case SvxZoomType::PERCENT: nZoomVal = aAppOpt.GetZoom(); break; + case SvxZoomType::OPTIMAL: nZoomVal = SC_ZOOMVAL_OPTIMAL; break; + case SvxZoomType::WHOLEPAGE: nZoomVal = SC_ZOOMVAL_WHOLEPAGE; break; + case SvxZoomType::PAGEWIDTH: nZoomVal = SC_ZOOMVAL_PAGEWIDTH; break; + default: + { + // added to avoid warnings + } + } + aRet <<= nZoomVal; + } + else if (aPropertyName == SC_UNONAME_ULISTS ) + { + ScUserList* pUserList = ScGlobal::GetUserList(); + if (pUserList) + { + size_t nCount = pUserList->size(); + uno::Sequence<OUString> aSeq(nCount); + OUString* pAry = aSeq.getArray(); + for (size_t i=0; i<nCount; ++i) + { + OUString aEntry((*pUserList)[i].GetString()); + pAry[i] = aEntry; + } + aRet <<= aSeq; + } + } + else if (aPropertyName == SC_UNONAME_PRALLSH ) + aRet <<= pScMod->GetPrintOptions().GetAllSheets(); + else if (aPropertyName == SC_UNONAME_PREMPTY ) + aRet <<= !pScMod->GetPrintOptions().GetSkipEmpty(); // reversed + + return aRet; +} + +SC_IMPL_DUMMY_PROPERTY_LISTENER( ScSpreadsheetSettings ) + +ScRecentFunctionsObj::ScRecentFunctionsObj() +{ +} + +ScRecentFunctionsObj::~ScRecentFunctionsObj() +{ +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +ScRecentFunctionsObj_get_implementation(css::uno::XComponentContext*, css::uno::Sequence<css::uno::Any> const &) +{ + SolarMutexGuard aGuard; + ScDLL::Init(); + return cppu::acquire(new ScRecentFunctionsObj()); +} + +// XRecentFunctions + +uno::Sequence<sal_Int32> SAL_CALL ScRecentFunctionsObj::getRecentFunctionIds() +{ + SolarMutexGuard aGuard; + const ScAppOptions& rOpt = SC_MOD()->GetAppOptions(); + sal_uInt16 nCount = rOpt.GetLRUFuncListCount(); + const sal_uInt16* pFuncs = rOpt.GetLRUFuncList(); + if (pFuncs) + { + uno::Sequence<sal_Int32> aSeq(nCount); + sal_Int32* pAry = aSeq.getArray(); + for (sal_uInt16 i=0; i<nCount; i++) + pAry[i] = pFuncs[i]; + return aSeq; + } + return {}; +} + +void SAL_CALL ScRecentFunctionsObj::setRecentFunctionIds( + const uno::Sequence<sal_Int32>& aRecentFunctionIds ) +{ + SolarMutexGuard aGuard; + sal_uInt16 nCount = static_cast<sal_uInt16>(std::min( aRecentFunctionIds.getLength(), sal_Int32(LRU_MAX) )); + const sal_Int32* pAry = aRecentFunctionIds.getConstArray(); + + std::unique_ptr<sal_uInt16[]> pFuncs(nCount ? new sal_uInt16[nCount] : nullptr); + for (sal_uInt16 i=0; i<nCount; i++) + pFuncs[i] = static_cast<sal_uInt16>(pAry[i]); //! check for valid values? + + ScModule* pScMod = SC_MOD(); + ScAppOptions aNewOpts(pScMod->GetAppOptions()); + aNewOpts.SetLRUFuncList(pFuncs.get(), nCount); + pScMod->SetAppOptions(aNewOpts); +} + +sal_Int32 SAL_CALL ScRecentFunctionsObj::getMaxRecentFunctions() +{ + return LRU_MAX; +} + +ScFunctionListObj::ScFunctionListObj() +{ +} + +ScFunctionListObj::~ScFunctionListObj() +{ +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +ScFunctionListObj_get_implementation(css::uno::XComponentContext*, css::uno::Sequence<css::uno::Any> const &) +{ + SolarMutexGuard aGuard; + ScDLL::Init(); + return cppu::acquire(new ScFunctionListObj()); +} + +static void lcl_FillSequence( uno::Sequence<beans::PropertyValue>& rSequence, const ScFuncDesc& rDesc ) +{ + rDesc.initArgumentInfo(); // full argument info is needed + + OSL_ENSURE( rSequence.getLength() == SC_FUNCDESC_PROPCOUNT, "Wrong count" ); + + beans::PropertyValue* pArray = rSequence.getArray(); + + pArray[0].Name = SC_UNONAME_ID; + pArray[0].Value <<= static_cast<sal_Int32>(rDesc.nFIndex); + + pArray[1].Name = SC_UNONAME_CATEGORY; + pArray[1].Value <<= static_cast<sal_Int32>(rDesc.nCategory); + + pArray[2].Name = SC_UNONAME_NAME; + if (rDesc.mxFuncName) + pArray[2].Value <<= *rDesc.mxFuncName; + + pArray[3].Name = SC_UNONAME_DESCRIPTION; + if (rDesc.mxFuncDesc) + pArray[3].Value <<= *rDesc.mxFuncDesc; + + pArray[4].Name = SC_UNONAME_ARGUMENTS; + if (rDesc.maDefArgNames.empty() || rDesc.maDefArgDescs.empty() || !rDesc.pDefArgFlags) + return; + + sal_uInt16 nCount = rDesc.nArgCount; + if (nCount >= PAIRED_VAR_ARGS) + nCount -= PAIRED_VAR_ARGS - 2; + else if (nCount >= VAR_ARGS) + nCount -= VAR_ARGS - 1; + sal_uInt16 nSeqCount = rDesc.GetSuppressedArgCount(); + if (nSeqCount >= PAIRED_VAR_ARGS) + nSeqCount -= PAIRED_VAR_ARGS - 2; + else if (nSeqCount >= VAR_ARGS) + nSeqCount -= VAR_ARGS - 1; + + if (!nSeqCount) + return; + + uno::Sequence<sheet::FunctionArgument> aArgSeq(nSeqCount); + sheet::FunctionArgument* pArgAry = aArgSeq.getArray(); + for (sal_uInt16 i=0, j=0; i<nCount; i++) + { + sheet::FunctionArgument aArgument; + aArgument.Name = rDesc.maDefArgNames[i]; + aArgument.Description = rDesc.maDefArgDescs[i]; + aArgument.IsOptional = rDesc.pDefArgFlags[i].bOptional; + pArgAry[j++] = aArgument; + } + pArray[4].Value <<= aArgSeq; +} + +// XFunctionDescriptions + +uno::Sequence<beans::PropertyValue> SAL_CALL ScFunctionListObj::getById( sal_Int32 nId ) +{ + SolarMutexGuard aGuard; + const ScFunctionList* pFuncList = ScGlobal::GetStarCalcFunctionList(); + if ( !pFuncList ) + throw uno::RuntimeException(); // should not happen + + sal_uInt16 nCount = static_cast<sal_uInt16>(pFuncList->GetCount()); + for (sal_uInt16 nIndex=0; nIndex<nCount; nIndex++) + { + const ScFuncDesc* pDesc = pFuncList->GetFunction(nIndex); + if ( pDesc && pDesc->nFIndex == nId ) + { + uno::Sequence<beans::PropertyValue> aSeq( SC_FUNCDESC_PROPCOUNT ); + lcl_FillSequence( aSeq, *pDesc ); + return aSeq; + } + } + + throw lang::IllegalArgumentException(); // not found +} + +// XNameAccess + +uno::Any SAL_CALL ScFunctionListObj::getByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + const ScFunctionList* pFuncList = ScGlobal::GetStarCalcFunctionList(); + if ( !pFuncList ) + throw uno::RuntimeException(); // should not happen + + sal_uInt16 nCount = static_cast<sal_uInt16>(pFuncList->GetCount()); + for (sal_uInt16 nIndex=0; nIndex<nCount; nIndex++) + { + const ScFuncDesc* pDesc = pFuncList->GetFunction(nIndex); + //! Case-insensitive??? + if ( pDesc && pDesc->mxFuncName && aName == *pDesc->mxFuncName ) + { + uno::Sequence<beans::PropertyValue> aSeq( SC_FUNCDESC_PROPCOUNT ); + lcl_FillSequence( aSeq, *pDesc ); + return uno::Any(aSeq); + } + } + + throw container::NoSuchElementException(); // not found +} + +// XIndexAccess + +sal_Int32 SAL_CALL ScFunctionListObj::getCount() +{ + SolarMutexGuard aGuard; + sal_Int32 nCount = 0; + const ScFunctionList* pFuncList = ScGlobal::GetStarCalcFunctionList(); + if ( pFuncList ) + nCount = static_cast<sal_Int32>(pFuncList->GetCount()); + return nCount; +} + +uno::Any SAL_CALL ScFunctionListObj::getByIndex( sal_Int32 nIndex ) +{ + SolarMutexGuard aGuard; + const ScFunctionList* pFuncList = ScGlobal::GetStarCalcFunctionList(); + if ( !pFuncList ) + throw uno::RuntimeException(); // should not happen + + if ( nIndex >= 0 && o3tl::make_unsigned(nIndex) < pFuncList->GetCount() ) + { + const ScFuncDesc* pDesc = pFuncList->GetFunction(nIndex); + if ( pDesc ) + { + uno::Sequence<beans::PropertyValue> aSeq( SC_FUNCDESC_PROPCOUNT ); + lcl_FillSequence( aSeq, *pDesc ); + return uno::Any(aSeq); + } + } + + throw lang::IndexOutOfBoundsException(); // illegal index +} + +// XEnumerationAccess + +uno::Reference<container::XEnumeration> SAL_CALL ScFunctionListObj::createEnumeration() +{ + SolarMutexGuard aGuard; + return new ScIndexEnumeration(this, "com.sun.star.sheet.FunctionDescriptionEnumeration"); +} + +// XElementAccess + +uno::Type SAL_CALL ScFunctionListObj::getElementType() +{ + return cppu::UnoType<uno::Sequence<beans::PropertyValue>>::get(); +} + +sal_Bool SAL_CALL ScFunctionListObj::hasElements() +{ + SolarMutexGuard aGuard; + return ( getCount() > 0 ); +} + +uno::Sequence<OUString> SAL_CALL ScFunctionListObj::getElementNames() +{ + SolarMutexGuard aGuard; + const ScFunctionList* pFuncList = ScGlobal::GetStarCalcFunctionList(); + if ( pFuncList ) + { + sal_uInt32 nCount = pFuncList->GetCount(); + uno::Sequence<OUString> aSeq(nCount); + OUString* pAry = aSeq.getArray(); + for (sal_uInt32 nIndex=0; nIndex<nCount; ++nIndex) + { + const ScFuncDesc* pDesc = pFuncList->GetFunction(nIndex); + if ( pDesc && pDesc->mxFuncName ) + pAry[nIndex] = *pDesc->mxFuncName; + } + return aSeq; + } + return {}; +} + +sal_Bool SAL_CALL ScFunctionListObj::hasByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + const ScFunctionList* pFuncList = ScGlobal::GetStarCalcFunctionList(); + if ( pFuncList ) + { + sal_uInt32 nCount = pFuncList->GetCount(); + for (sal_uInt32 nIndex=0; nIndex<nCount; ++nIndex) + { + const ScFuncDesc* pDesc = pFuncList->GetFunction(nIndex); + //! Case-insensitive??? + if ( pDesc && pDesc->mxFuncName && aName == *pDesc->mxFuncName ) + return true; + } + } + return false; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/unoobj/celllistsource.cxx b/sc/source/ui/unoobj/celllistsource.cxx new file mode 100644 index 0000000000..bca1e0a695 --- /dev/null +++ b/sc/source/ui/unoobj/celllistsource.cxx @@ -0,0 +1,433 @@ +/* -*- 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 "celllistsource.hxx" +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/lang/NotInitializedException.hpp> +#include <com/sun/star/lang/NullPointerException.hpp> +#include <com/sun/star/table/XCellRange.hpp> +#include <com/sun/star/text/XTextRange.hpp> +#include <com/sun/star/sheet/XCellRangeAddressable.hpp> +#include <com/sun/star/sheet/FormulaResult.hpp> +#include <com/sun/star/sheet/XSpreadsheetDocument.hpp> +#include <com/sun/star/util/XModifyBroadcaster.hpp> +#include <com/sun/star/container/XIndexAccess.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/beans/NamedValue.hpp> +#include <cppuhelper/supportsservice.hxx> +#include <comphelper/diagnose_ex.hxx> + +namespace calc +{ + +#define PROP_HANDLE_RANGE_ADDRESS 1 + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::table; + using namespace ::com::sun::star::text; + using namespace ::com::sun::star::sheet; + using namespace ::com::sun::star::container; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::util; + using namespace ::com::sun::star::form::binding; + + OCellListSource::OCellListSource( const Reference< XSpreadsheetDocument >& _rxDocument ) + :OCellListSource_Base( m_aMutex ) + ,OCellListSource_PBase( OCellListSource_Base::rBHelper ) + ,m_xDocument( _rxDocument ) + ,m_aListEntryListeners( m_aMutex ) + ,m_bInitialized( false ) + { + OSL_PRECOND( m_xDocument.is(), "OCellListSource::OCellListSource: invalid document!" ); + + // register our property at the base class + registerPropertyNoMember( + "CellRange", + PROP_HANDLE_RANGE_ADDRESS, + PropertyAttribute::BOUND | PropertyAttribute::READONLY, + cppu::UnoType<CellRangeAddress>::get(), + css::uno::Any(CellRangeAddress()) + ); + } + + OCellListSource::~OCellListSource( ) + { + if ( !OCellListSource_Base::rBHelper.bDisposed ) + { + acquire(); // prevent duplicate dtor + dispose(); + } + } + + IMPLEMENT_FORWARD_XINTERFACE2( OCellListSource, OCellListSource_Base, OCellListSource_PBase ) + + IMPLEMENT_FORWARD_XTYPEPROVIDER2( OCellListSource, OCellListSource_Base, OCellListSource_PBase ) + + void SAL_CALL OCellListSource::disposing() + { + ::osl::MutexGuard aGuard( m_aMutex ); + + Reference<XModifyBroadcaster> xBroadcaster( m_xRange, UNO_QUERY ); + if ( xBroadcaster.is() ) + { + xBroadcaster->removeModifyListener( this ); + } + + EventObject aDisposeEvent( *this ); + m_aListEntryListeners.disposeAndClear( aDisposeEvent ); + + WeakComponentImplHelperBase::disposing(); + + // TODO: clean up here whatever you need to clean up (e.g. revoking listeners etc.) + } + + Reference< XPropertySetInfo > SAL_CALL OCellListSource::getPropertySetInfo( ) + { + return createPropertySetInfo( getInfoHelper() ) ; + } + + ::cppu::IPropertyArrayHelper& SAL_CALL OCellListSource::getInfoHelper() + { + return *OCellListSource_PABase::getArrayHelper(); + } + + ::cppu::IPropertyArrayHelper* OCellListSource::createArrayHelper( ) const + { + Sequence< Property > aProps; + describeProperties( aProps ); + return new ::cppu::OPropertyArrayHelper(aProps); + } + + void SAL_CALL OCellListSource::getFastPropertyValue( Any& _rValue, sal_Int32 _nHandle ) const + { + OSL_ENSURE( _nHandle == PROP_HANDLE_RANGE_ADDRESS, "OCellListSource::getFastPropertyValue: invalid handle!" ); + // we only have this one property... + + _rValue <<= getRangeAddress( ); + } + + void OCellListSource::checkDisposed( ) const + { + if ( OCellListSource_Base::rBHelper.bInDispose || OCellListSource_Base::rBHelper.bDisposed ) + throw DisposedException(); + // TODO: is it worth having an error message here? + } + + void OCellListSource::checkInitialized() + { + if ( !m_bInitialized ) + throw NotInitializedException("CellListSource is not initialized", getXWeak()); + } + + OUString SAL_CALL OCellListSource::getImplementationName( ) + { + return "com.sun.star.comp.sheet.OCellListSource"; + } + + sal_Bool SAL_CALL OCellListSource::supportsService( const OUString& _rServiceName ) + { + return cppu::supportsService(this, _rServiceName); + } + + Sequence< OUString > SAL_CALL OCellListSource::getSupportedServiceNames( ) + { + return {"com.sun.star.table.CellRangeListSource", + "com.sun.star.form.binding.ListEntrySource"}; + } + + CellRangeAddress OCellListSource::getRangeAddress( ) const + { + OSL_PRECOND( m_xRange.is(), "OCellListSource::getRangeAddress: invalid range!" ); + + CellRangeAddress aAddress; + Reference< XCellRangeAddressable > xRangeAddress( m_xRange, UNO_QUERY ); + if ( xRangeAddress.is() ) + aAddress = xRangeAddress->getRangeAddress( ); + return aAddress; + } + + OUString OCellListSource::getCellTextContent_noCheck( sal_Int32 _nRangeRelativeRow, css::uno::Any* pAny ) + { + OUString sText; + + OSL_PRECOND( m_xRange.is(), "OCellListSource::getRangeAddress: invalid range!" ); + + if (!m_xRange.is()) + return sText; + + Reference< XCell > xCell( m_xRange->getCellByPosition( 0, _nRangeRelativeRow )); + if (!xCell.is()) + { + if (pAny) + *pAny <<= sText; + return sText; + } + + Reference< XTextRange > xCellText; + xCellText.set( xCell, UNO_QUERY); + + if (xCellText.is()) + sText = xCellText->getString(); // formatted output string + + if (pAny) + { + switch (xCell->getType()) + { + case CellContentType_VALUE: + *pAny <<= xCell->getValue(); + break; + case CellContentType_TEXT: + *pAny <<= sText; + break; + case CellContentType_FORMULA: + if (xCell->getError()) + *pAny <<= sText; // Err:... or #...! + else + { + Reference< XPropertySet > xProp( xCell, UNO_QUERY); + if (xProp.is()) + { + sal_Int32 nResultType; + if ((xProp->getPropertyValue("FormulaResultType2") >>= nResultType) && + nResultType == FormulaResult::VALUE) + *pAny <<= xCell->getValue(); + else + *pAny <<= sText; + } + } + break; + case CellContentType_EMPTY: + *pAny <<= OUString(); + break; + default: + ; // nothing, if actually occurred it would result in #N/A being displayed if selected + } + } + + return sText; + } + + sal_Int32 SAL_CALL OCellListSource::getListEntryCount( ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(); + checkInitialized(); + + CellRangeAddress aAddress( getRangeAddress( ) ); + return aAddress.EndRow - aAddress.StartRow + 1; + } + + OUString SAL_CALL OCellListSource::getListEntry( sal_Int32 _nPosition ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(); + checkInitialized(); + + if ( _nPosition >= getListEntryCount() ) + throw IndexOutOfBoundsException(); + + return getCellTextContent_noCheck( _nPosition, nullptr ); + } + + Sequence< OUString > SAL_CALL OCellListSource::getAllListEntries( ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(); + checkInitialized(); + + Sequence< OUString > aAllEntries( getListEntryCount() ); + OUString* pAllEntries = aAllEntries.getArray(); + for ( sal_Int32 i = 0; i < aAllEntries.getLength(); ++i ) + { + *pAllEntries++ = getCellTextContent_noCheck( i, nullptr ); + } + + return aAllEntries; + } + + Sequence< OUString > SAL_CALL OCellListSource::getAllListEntriesTyped( Sequence< Any >& rDataValues ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(); + checkInitialized(); + + const sal_Int32 nCount = getListEntryCount(); + Sequence< OUString > aAllEntries( nCount ); + rDataValues = Sequence< Any >( nCount ); + OUString* pAllEntries = aAllEntries.getArray(); + Any* pDataValues = rDataValues.getArray(); + for ( sal_Int32 i = 0; i < nCount; ++i ) + { + *pAllEntries++ = getCellTextContent_noCheck( i, pDataValues++ ); + } + + return aAllEntries; + } + + void SAL_CALL OCellListSource::addListEntryListener( const Reference< XListEntryListener >& _rxListener ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(); + checkInitialized(); + + if ( !_rxListener.is() ) + throw NullPointerException(); + + m_aListEntryListeners.addInterface( _rxListener ); + } + + void SAL_CALL OCellListSource::removeListEntryListener( const Reference< XListEntryListener >& _rxListener ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + checkDisposed(); + checkInitialized(); + + if ( !_rxListener.is() ) + throw NullPointerException(); + + m_aListEntryListeners.removeInterface( _rxListener ); + } + + void SAL_CALL OCellListSource::modified( const EventObject& /* aEvent */ ) + { + notifyModified(); + } + + void OCellListSource::notifyModified() + { + EventObject aEvent; + aEvent.Source.set(*this); + + ::comphelper::OInterfaceIteratorHelper3 aIter( m_aListEntryListeners ); + while ( aIter.hasMoreElements() ) + { + try + { + aIter.next()->allEntriesChanged( aEvent ); + } + catch( const RuntimeException& ) + { + // silent this + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "sc", "OCellListSource::notifyModified: caught a (non-runtime) exception!" ); + } + } + + } + + void SAL_CALL OCellListSource::disposing( const EventObject& aEvent ) + { + Reference<XInterface> xRangeInt( m_xRange, UNO_QUERY ); + if ( xRangeInt == aEvent.Source ) + { + // release references to range object + m_xRange.clear(); + } + } + + void SAL_CALL OCellListSource::initialize( const Sequence< Any >& _rArguments ) + { + if ( m_bInitialized ) + throw RuntimeException("CellListSource is already initialized", getXWeak()); + + // get the cell address + CellRangeAddress aRangeAddress; + bool bFoundAddress = false; + + for ( const Any& rArg : _rArguments ) + { + NamedValue aValue; + if ( rArg >>= aValue ) + { + if ( aValue.Name == "CellRange" ) + { + if ( aValue.Value >>= aRangeAddress ) + { + bFoundAddress = true; + break; + } + } + } + } + + if ( !bFoundAddress ) + throw RuntimeException("Cell not found", getXWeak()); + + // determine the range we're bound to + try + { + if ( m_xDocument.is() ) + { + // first the sheets collection + Reference< XIndexAccess > xSheets(m_xDocument->getSheets( ), UNO_QUERY); + OSL_ENSURE( xSheets.is(), "OCellListSource::initialize: could not retrieve the sheets!" ); + + if ( xSheets.is() ) + { + // the concrete sheet + Reference< XCellRange > xSheet(xSheets->getByIndex( aRangeAddress.Sheet ), UNO_QUERY); + OSL_ENSURE( xSheet.is(), "OCellListSource::initialize: NULL sheet, but no exception!" ); + + // the concrete cell + if ( xSheet.is() ) + { + m_xRange.set(xSheet->getCellRangeByPosition( + aRangeAddress.StartColumn, aRangeAddress.StartRow, + aRangeAddress.EndColumn, aRangeAddress.EndRow)); + OSL_ENSURE( Reference< XCellRangeAddressable >( m_xRange, UNO_QUERY ).is(), "OCellListSource::initialize: either NULL range, or cell without address access!" ); + } + } + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "sc", "OCellListSource::initialize: caught an exception while retrieving the cell object!" ); + } + + if ( !m_xRange.is() ) + throw RuntimeException("Failed to retrieve cell range", getXWeak()); + + Reference<XModifyBroadcaster> xBroadcaster( m_xRange, UNO_QUERY ); + if ( xBroadcaster.is() ) + { + xBroadcaster->addModifyListener( this ); + } + + // TODO: add as XEventListener to the cell range, so we get notified when it dies, + // and can dispose ourself then + + // TODO: somehow add as listener so we get notified when the address of the cell range changes + // We need to forward this as change in our CellRange property to our property change listeners + + // TODO: somehow add as listener to the cells in the range, so that we get notified + // when their content changes. We need to forward this to our list entry listeners then + + // TODO: somehow add as listener so that we get notified of insertions and removals of rows in our + // range. In this case, we need to fire a change in our CellRange property, and additionally + // notify our XListEntryListeners + + m_bInitialized = true; + } + +} // namespace calc + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/unoobj/celllistsource.hxx b/sc/source/ui/unoobj/celllistsource.hxx new file mode 100644 index 0000000000..c919c18ec2 --- /dev/null +++ b/sc/source/ui/unoobj/celllistsource.hxx @@ -0,0 +1,155 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/form/binding/XListEntryTypedSource.hpp> +#include <cppuhelper/compbase.hxx> +#include <cppuhelper/basemutex.hxx> +#include <comphelper/interfacecontainer3.hxx> +#include <comphelper/propertycontainer.hxx> +#include <comphelper/uno3.hxx> +#include <comphelper/proparrhlp.hxx> +#include <com/sun/star/table/CellRangeAddress.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/util/XModifyListener.hpp> + +namespace com::sun::star::sheet { class XSpreadsheetDocument; } +namespace com::sun::star::table { class XCellRange; } + +namespace calc +{ + + //= OCellListSource + + class OCellListSource; + // the base for our interfaces + typedef ::cppu::WeakComponentImplHelper < css::form::binding::XListEntryTypedSource + , css::util::XModifyListener + , css::lang::XServiceInfo + , css::lang::XInitialization + > OCellListSource_Base; + // the base for the property handling + typedef ::comphelper::OPropertyContainer OCellListSource_PBase; + // the second base for property handling + typedef ::comphelper::OPropertyArrayUsageHelper< OCellListSource > + OCellListSource_PABase; + + class OCellListSource :public ::cppu::BaseMutex + ,public OCellListSource_Base // order matters! before OCellListSource_PBase, so rBHelper gets initialized + ,public OCellListSource_PBase + ,public OCellListSource_PABase + { + private: + css::uno::Reference< css::sheet::XSpreadsheetDocument > + m_xDocument; /// the document where our cell lives + css::uno::Reference< css::table::XCellRange > + m_xRange; /// the range of cells we're bound to + ::comphelper::OInterfaceContainerHelper3<css::form::binding::XListEntryListener> + m_aListEntryListeners; /// our listeners + bool m_bInitialized; /// has XInitialization::initialize been called? + + public: + explicit OCellListSource( + const css::uno::Reference< css::sheet::XSpreadsheetDocument >& _rxDocument + ); + + using OCellListSource_PBase::getFastPropertyValue; + + protected: + virtual ~OCellListSource( ) override; + + protected: + // XInterface + DECLARE_XINTERFACE() + + // XTypeProvider + DECLARE_XTYPEPROVIDER() + + // XListEntrySource + virtual sal_Int32 SAL_CALL getListEntryCount( ) override; + virtual OUString SAL_CALL getListEntry( sal_Int32 Position ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getAllListEntries( ) override; + virtual void SAL_CALL addListEntryListener( const css::uno::Reference< css::form::binding::XListEntryListener >& Listener ) override; + virtual void SAL_CALL removeListEntryListener( const css::uno::Reference< css::form::binding::XListEntryListener >& Listener ) override; + + // XListEntryTypedSource + virtual css::uno::Sequence< OUString > SAL_CALL getAllListEntriesTyped( css::uno::Sequence< css::uno::Any >& rDataValues ) override; + + // OComponentHelper/XComponent + virtual void SAL_CALL disposing() override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + + // XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + + // OPropertySetHelper + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + virtual void SAL_CALL getFastPropertyValue( css::uno::Any& _rValue, sal_Int32 _nHandle ) const override; + + // ::comphelper::OPropertyArrayUsageHelper + virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const override; + + // XModifyListener + virtual void SAL_CALL modified( const css::lang::EventObject& aEvent ) override; + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + // XInitialization + virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override; + + private: + void checkDisposed( ) const; + void checkInitialized(); + + /** retrieves the actual address of our cell range + @precond + our m_xRange is not <NULL/> + */ + css::table::CellRangeAddress + getRangeAddress( ) const; + + /** retrieves the text of a cell within our range + @param _nRangeRelativeRow + the relative row index of the cell within our range + @param pAny + if not <NULL/> then the underlying data value is returned in the Any + @precond + our m_xRange is not <NULL/> + */ + OUString + getCellTextContent_noCheck( + sal_Int32 _nRangeRelativeRow, + css::uno::Any* pAny + ); + + void notifyModified(); + + private: + OCellListSource( const OCellListSource& ) = delete; + OCellListSource& operator=( const OCellListSource& ) = delete; + }; + +} // namespace calc + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/unoobj/cellsuno.cxx b/sc/source/ui/unoobj/cellsuno.cxx new file mode 100644 index 0000000000..3325b0bbcf --- /dev/null +++ b/sc/source/ui/unoobj/cellsuno.cxx @@ -0,0 +1,9266 @@ +/* -*- 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 <scitems.hxx> +#include <editeng/eeitem.hxx> +#include <o3tl/safeint.hxx> +#include <svx/svdpool.hxx> + +#include <utility> +#include <vcl/svapp.hxx> +#include <svx/algitem.hxx> +#include <editeng/borderline.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/editeng.hxx> +#include <editeng/flditem.hxx> +#include <editeng/editobj.hxx> +#include <editeng/unoipset.hxx> +#include <editeng/langitem.hxx> +#include <sfx2/linkmgr.hxx> +#include <svl/numformat.hxx> +#include <svl/srchitem.hxx> +#include <svl/sharedstringpool.hxx> +#include <svx/unomid.hxx> +#include <editeng/unoprnms.hxx> +#include <editeng/unotext.hxx> +#include <svx/svdpage.hxx> +#include <sfx2/bindings.hxx> +#include <svl/zforlist.hxx> +#include <svl/zformat.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <float.h> +#include <cppuhelper/queryinterface.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <tools/UnitConversion.hxx> + +#include <com/sun/star/awt/XBitmap.hpp> +#include <com/sun/star/util/CellProtection.hpp> +#include <com/sun/star/table/CellHoriJustify.hpp> +#include <com/sun/star/table/CellOrientation.hpp> +#include <com/sun/star/table/ShadowFormat.hpp> +#include <com/sun/star/table/TableBorder.hpp> +#include <com/sun/star/table/TableBorder2.hpp> +#include <com/sun/star/sheet/CellFlags.hpp> +#include <com/sun/star/sheet/FormulaResult.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/lang/Locale.hpp> +#include <com/sun/star/beans/TolerantPropertySetResultType.hpp> +#include <com/sun/star/beans/SetPropertyTolerantFailed.hpp> +#include <com/sun/star/text/WritingMode2.hpp> +#include <com/sun/star/text/textfield/Type.hpp> +#include <com/sun/star/sheet/XConditionalFormats.hpp> +#include <com/sun/star/util/XComplexColor.hpp> + +#include <autoform.hxx> +#include <cellvalue.hxx> +#include <cellmergeoption.hxx> +#include <cellsuno.hxx> +#include <cursuno.hxx> +#include <textuno.hxx> +#include <editsrc.hxx> +#include <notesuno.hxx> +#include <fielduno.hxx> +#include <docuno.hxx> +#include <datauno.hxx> +#include <dapiuno.hxx> +#include <chartuno.hxx> +#include <fmtuno.hxx> +#include <miscuno.hxx> +#include <convuno.hxx> +#include <srchuno.hxx> +#include <nameuno.hxx> +#include <targuno.hxx> +#include <tokenuno.hxx> +#include <eventuno.hxx> +#include <docsh.hxx> +#include <markdata.hxx> +#include <patattr.hxx> +#include <docpool.hxx> +#include <docfunc.hxx> +#include <dbdocfun.hxx> +#include <olinefun.hxx> +#include <hints.hxx> +#include <formulacell.hxx> +#include <undotab.hxx> +#include <undoblk.hxx> +#include <stlsheet.hxx> +#include <dbdata.hxx> +#include <attrib.hxx> +#include <chartarr.hxx> +#include <chartlis.hxx> +#include <drwlayer.hxx> +#include <printfun.hxx> +#include <prnsave.hxx> +#include <tablink.hxx> +#include <dociter.hxx> +#include <rangeutl.hxx> +#include <conditio.hxx> +#include <validat.hxx> +#include <sc.hrc> +#include <cellform.hxx> +#include <globstr.hrc> +#include <scresid.hxx> +#include <unonames.hxx> +#include <styleuno.hxx> +#include <rangeseq.hxx> +#include <unowids.hxx> +#include <paramisc.hxx> +#include <queryentry.hxx> +#include <formula/errorcodes.hxx> +#include <unoreflist.hxx> +#include <formula/grammar.hxx> +#include <editeng/escapementitem.hxx> +#include <stringutil.hxx> +#include <formulaiter.hxx> +#include <tokenarray.hxx> +#include <stylehelper.hxx> +#include <dputil.hxx> +#include <sortparam.hxx> +#include <condformatuno.hxx> +#include <TablePivotCharts.hxx> +#include <table.hxx> +#include <refundo.hxx> +#include <columnspanset.hxx> +#include <CommonProperties.hxx> + +#include <memory> + +using namespace com::sun::star; + +// The names in the maps must be sorted according to strcmp! +//! Instead of Which-ID 0 use special IDs and do not compare via names! + +// Left/Right/Top/BottomBorder are mapped directly to the core items, +// not collected/applied to the borders of a range -> ATTR_BORDER can be used directly + +static const SfxItemPropertySet* lcl_GetCellsPropertySet() +{ + static const SfxItemPropertyMapEntry aCellsPropertyMap_Impl[] = + { + { SC_UNONAME_ABSNAME, SC_WID_UNO_ABSNAME, cppu::UnoType<OUString>::get(), 0 | beans::PropertyAttribute::READONLY, 0 }, + { SC_UNONAME_ASIANVERT,ATTR_VERTICAL_ASIAN,cppu::UnoType<bool>::get(), 0, 0 }, + CELL_BORDER_PROPERTIES + CELL_BACKGROUND_COLOR_PROPERTIES + { SC_UNONAME_CELLPRO, ATTR_PROTECTION, cppu::UnoType<util::CellProtection>::get(), 0, 0 }, + { SC_UNONAME_CELLSTYL, SC_WID_UNO_CELLSTYL,cppu::UnoType<OUString>::get(), 0, 0 }, + CHAR_COLOR_PROPERTIES + { SC_UNONAME_COUTL, ATTR_FONT_CONTOUR, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_CCROSS, ATTR_FONT_CROSSEDOUT,cppu::UnoType<bool>::get(), 0, MID_CROSSED_OUT }, + { SC_UNONAME_CEMPHAS, ATTR_FONT_EMPHASISMARK,cppu::UnoType<sal_Int16>::get(), 0, MID_EMPHASIS }, + { SC_UNONAME_CFONT, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY }, + { SC_UNONAME_CFCHARS, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_CHAR_SET }, + { SC_UNO_CJK_CFCHARS, ATTR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_CHAR_SET }, + { SC_UNO_CTL_CFCHARS, ATTR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_CHAR_SET }, + { SC_UNONAME_CFFAMIL, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY }, + { SC_UNO_CJK_CFFAMIL, ATTR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY }, + { SC_UNO_CTL_CFFAMIL, ATTR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY }, + { SC_UNONAME_CFNAME, ATTR_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_FAMILY_NAME }, + { SC_UNO_CJK_CFNAME, ATTR_CJK_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_FAMILY_NAME }, + { SC_UNO_CTL_CFNAME, ATTR_CTL_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_FAMILY_NAME }, + { SC_UNONAME_CFPITCH, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_PITCH }, + { SC_UNO_CJK_CFPITCH, ATTR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_PITCH }, + { SC_UNO_CTL_CFPITCH, ATTR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_PITCH }, + { SC_UNONAME_CFSTYLE, ATTR_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_STYLE_NAME }, + { SC_UNO_CJK_CFSTYLE, ATTR_CJK_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_STYLE_NAME }, + { SC_UNO_CTL_CFSTYLE, ATTR_CTL_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_STYLE_NAME }, + { SC_UNONAME_CHEIGHT, ATTR_FONT_HEIGHT, cppu::UnoType<float>::get(), 0, MID_FONTHEIGHT | CONVERT_TWIPS }, + { SC_UNO_CJK_CHEIGHT, ATTR_CJK_FONT_HEIGHT,cppu::UnoType<float>::get(), 0, MID_FONTHEIGHT | CONVERT_TWIPS }, + { SC_UNO_CTL_CHEIGHT, ATTR_CTL_FONT_HEIGHT,cppu::UnoType<float>::get(), 0, MID_FONTHEIGHT | CONVERT_TWIPS }, + { SC_UNONAME_CLOCAL, ATTR_FONT_LANGUAGE, cppu::UnoType<lang::Locale>::get(), 0, MID_LANG_LOCALE }, + { SC_UNO_CJK_CLOCAL, ATTR_CJK_FONT_LANGUAGE,cppu::UnoType<lang::Locale>::get(), 0, MID_LANG_LOCALE }, + { SC_UNO_CTL_CLOCAL, ATTR_CTL_FONT_LANGUAGE,cppu::UnoType<lang::Locale>::get(), 0, MID_LANG_LOCALE }, + { SC_UNONAME_COVER, ATTR_FONT_OVERLINE, cppu::UnoType<sal_Int16>::get(), 0, MID_TL_STYLE }, + { SC_UNONAME_COVRLCOL, ATTR_FONT_OVERLINE, cppu::UnoType<sal_Int32>::get(), 0, MID_TL_COLOR }, + { SC_UNONAME_COVRLHAS, ATTR_FONT_OVERLINE, cppu::UnoType<bool>::get(), 0, MID_TL_HASCOLOR }, + { SC_UNONAME_CPOST, ATTR_FONT_POSTURE, cppu::UnoType<awt::FontSlant>::get(), 0, MID_POSTURE }, + { SC_UNO_CJK_CPOST, ATTR_CJK_FONT_POSTURE,cppu::UnoType<awt::FontSlant>::get(), 0, MID_POSTURE }, + { SC_UNO_CTL_CPOST, ATTR_CTL_FONT_POSTURE,cppu::UnoType<awt::FontSlant>::get(), 0, MID_POSTURE }, + { SC_UNONAME_CRELIEF, ATTR_FONT_RELIEF, cppu::UnoType<sal_Int16>::get(), 0, MID_RELIEF }, + { SC_UNONAME_CSHADD, ATTR_FONT_SHADOWED, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_CSTRIKE, ATTR_FONT_CROSSEDOUT,cppu::UnoType<sal_Int16>::get(), 0, MID_CROSS_OUT }, + { SC_UNONAME_CUNDER, ATTR_FONT_UNDERLINE,cppu::UnoType<sal_Int16>::get(), 0, MID_TL_STYLE }, + { SC_UNONAME_CUNDLCOL, ATTR_FONT_UNDERLINE,cppu::UnoType<sal_Int32>::get(), 0, MID_TL_COLOR }, + { SC_UNONAME_CUNDLHAS, ATTR_FONT_UNDERLINE,cppu::UnoType<bool>::get(), 0, MID_TL_HASCOLOR }, + { SC_UNONAME_CWEIGHT, ATTR_FONT_WEIGHT, cppu::UnoType<float>::get(), 0, MID_WEIGHT }, + { SC_UNO_CJK_CWEIGHT, ATTR_CJK_FONT_WEIGHT,cppu::UnoType<float>::get(), 0, MID_WEIGHT }, + { SC_UNO_CTL_CWEIGHT, ATTR_CTL_FONT_WEIGHT,cppu::UnoType<float>::get(), 0, MID_WEIGHT }, + { SC_UNONAME_CWORDMOD, ATTR_FONT_WORDLINE, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_CHCOLHDR, SC_WID_UNO_CHCOLHDR,cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_CHROWHDR, SC_WID_UNO_CHROWHDR,cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_CONDFMT, SC_WID_UNO_CONDFMT, cppu::UnoType<sheet::XSheetConditionalEntries>::get(), 0, 0 }, + { SC_UNONAME_CONDLOC, SC_WID_UNO_CONDLOC, cppu::UnoType<sheet::XSheetConditionalEntries>::get(), 0, 0 }, + { SC_UNONAME_CONDXML, SC_WID_UNO_CONDXML, cppu::UnoType<sheet::XSheetConditionalEntries>::get(), 0, 0 }, + { SC_UNONAME_DIAGONAL_BLTR, ATTR_BORDER_BLTR, ::cppu::UnoType<table::BorderLine>::get(), 0, 0 | CONVERT_TWIPS }, + { SC_UNONAME_DIAGONAL_BLTR2, ATTR_BORDER_BLTR, ::cppu::UnoType<table::BorderLine2>::get(), 0, 0 | CONVERT_TWIPS }, + { SC_UNONAME_DIAGONAL_TLBR, ATTR_BORDER_TLBR, ::cppu::UnoType<table::BorderLine>::get(), 0, 0 | CONVERT_TWIPS }, + { SC_UNONAME_DIAGONAL_TLBR2, ATTR_BORDER_TLBR, ::cppu::UnoType<table::BorderLine2>::get(), 0, 0 | CONVERT_TWIPS }, + { SC_UNONAME_CELLHJUS, ATTR_HOR_JUSTIFY, cppu::UnoType<table::CellHoriJustify>::get(), 0, MID_HORJUST_HORJUST }, + { SC_UNONAME_CELLHJUS_METHOD, ATTR_HOR_JUSTIFY_METHOD, ::cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { SC_UNONAME_CELLTRAN, ATTR_BACKGROUND, cppu::UnoType<bool>::get(), 0, MID_GRAPHIC_TRANSPARENT }, + { SC_UNONAME_WRAP, ATTR_LINEBREAK, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_NUMFMT, ATTR_VALUE_FORMAT, cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { SC_UNONAME_NUMRULES, SC_WID_UNO_NUMRULES,cppu::UnoType<container::XIndexReplace>::get(), 0, 0 }, + { SC_UNONAME_CELLORI, ATTR_STACKED, cppu::UnoType<table::CellOrientation>::get(), 0, 0 }, + { SC_UNONAME_PADJUST, ATTR_HOR_JUSTIFY, ::cppu::UnoType<sal_Int16>::get(), 0, MID_HORJUST_ADJUST }, + { SC_UNONAME_PBMARGIN, ATTR_MARGIN, cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_LO_MARGIN | CONVERT_TWIPS }, + { SC_UNONAME_PINDENT, ATTR_INDENT, cppu::UnoType<sal_Int16>::get(), 0, 0 }, //! CONVERT_TWIPS + { SC_UNONAME_PISCHDIST,ATTR_SCRIPTSPACE, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_PISFORBID,ATTR_FORBIDDEN_RULES,cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_PISHANG, ATTR_HANGPUNCTUATION,cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_PISHYPHEN,ATTR_HYPHENATE, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_PLASTADJ, ATTR_HOR_JUSTIFY, ::cppu::UnoType<sal_Int16>::get(), 0, MID_HORJUST_ADJUST }, + { SC_UNONAME_PLMARGIN, ATTR_MARGIN, cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_L_MARGIN | CONVERT_TWIPS }, + { SC_UNONAME_PRMARGIN, ATTR_MARGIN, cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_R_MARGIN | CONVERT_TWIPS }, + { SC_UNONAME_PTMARGIN, ATTR_MARGIN, cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_UP_MARGIN | CONVERT_TWIPS }, + { SC_UNONAME_ROTANG, ATTR_ROTATE_VALUE, cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { SC_UNONAME_ROTREF, ATTR_ROTATE_MODE, cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { SC_UNONAME_SHADOW, ATTR_SHADOW, cppu::UnoType<table::ShadowFormat>::get(), 0, 0 | CONVERT_TWIPS }, + { SC_UNONAME_SHRINK_TO_FIT, ATTR_SHRINKTOFIT, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_TBLBORD, SC_WID_UNO_TBLBORD, cppu::UnoType<table::TableBorder>::get(), 0, 0 | CONVERT_TWIPS }, + { SC_UNONAME_TBLBORD2, SC_WID_UNO_TBLBORD2, cppu::UnoType<table::TableBorder2>::get(), 0, 0 | CONVERT_TWIPS }, + { SC_UNONAME_USERDEF, ATTR_USERDEF, cppu::UnoType<container::XNameContainer>::get(), 0, 0 }, + { SC_UNONAME_VALIDAT, SC_WID_UNO_VALIDAT, cppu::UnoType<beans::XPropertySet>::get(), 0, 0 }, + { SC_UNONAME_VALILOC, SC_WID_UNO_VALILOC, cppu::UnoType<beans::XPropertySet>::get(), 0, 0 }, + { SC_UNONAME_VALIXML, SC_WID_UNO_VALIXML, cppu::UnoType<beans::XPropertySet>::get(), 0, 0 }, + { SC_UNONAME_CELLVJUS, ATTR_VER_JUSTIFY, cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { SC_UNONAME_CELLVJUS_METHOD, ATTR_VER_JUSTIFY_METHOD, ::cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { SC_UNONAME_WRITING, ATTR_WRITINGDIR, cppu::UnoType<sal_Int16>::get(), 0, 0 }, + { SC_UNONAME_HYPERLINK, ATTR_HYPERLINK, cppu::UnoType<OUString>::get(), 0, 0 }, + { SC_UNONAME_FORMATID, SC_WID_UNO_FORMATID, cppu::UnoType<sal_uInt64>::get(), 0, 0 }, + }; + static SfxItemPropertySet aCellsPropertySet( aCellsPropertyMap_Impl ); + return &aCellsPropertySet; +} + +// CellRange contains all entries from Cells, plus its own entries +// with Which-ID 0 (those are needed only for getPropertySetInfo). + +static const SfxItemPropertySet* lcl_GetRangePropertySet() +{ + static const SfxItemPropertyMapEntry aRangePropertyMap_Impl[] = + { + { SC_UNONAME_ABSNAME, SC_WID_UNO_ABSNAME, cppu::UnoType<OUString>::get(), 0 | beans::PropertyAttribute::READONLY, 0 }, + { SC_UNONAME_ASIANVERT,ATTR_VERTICAL_ASIAN,cppu::UnoType<bool>::get(), 0, 0 }, + CELL_BORDER_PROPERTIES + CELL_BACKGROUND_COLOR_PROPERTIES + { SC_UNONAME_CELLPRO, ATTR_PROTECTION, cppu::UnoType<util::CellProtection>::get(), 0, 0 }, + { SC_UNONAME_CELLSTYL, SC_WID_UNO_CELLSTYL,cppu::UnoType<OUString>::get(), 0, 0 }, + CHAR_COLOR_PROPERTIES + { SC_UNONAME_COUTL, ATTR_FONT_CONTOUR, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_CCROSS, ATTR_FONT_CROSSEDOUT,cppu::UnoType<bool>::get(), 0, MID_CROSSED_OUT }, + { SC_UNONAME_CEMPHAS, ATTR_FONT_EMPHASISMARK,cppu::UnoType<sal_Int16>::get(), 0, MID_EMPHASIS }, + { SC_UNONAME_CFONT, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY }, + { SC_UNONAME_CFCHARS, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_CHAR_SET }, + { SC_UNO_CJK_CFCHARS, ATTR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_CHAR_SET }, + { SC_UNO_CTL_CFCHARS, ATTR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_CHAR_SET }, + { SC_UNONAME_CFFAMIL, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY }, + { SC_UNO_CJK_CFFAMIL, ATTR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY }, + { SC_UNO_CTL_CFFAMIL, ATTR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY }, + { SC_UNONAME_CFNAME, ATTR_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_FAMILY_NAME }, + { SC_UNO_CJK_CFNAME, ATTR_CJK_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_FAMILY_NAME }, + { SC_UNO_CTL_CFNAME, ATTR_CTL_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_FAMILY_NAME }, + { SC_UNONAME_CFPITCH, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_PITCH }, + { SC_UNO_CJK_CFPITCH, ATTR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_PITCH }, + { SC_UNO_CTL_CFPITCH, ATTR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_PITCH }, + { SC_UNONAME_CFSTYLE, ATTR_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_STYLE_NAME }, + { SC_UNO_CJK_CFSTYLE, ATTR_CJK_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_STYLE_NAME }, + { SC_UNO_CTL_CFSTYLE, ATTR_CTL_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_STYLE_NAME }, + { SC_UNONAME_CHEIGHT, ATTR_FONT_HEIGHT, cppu::UnoType<float>::get(), 0, MID_FONTHEIGHT | CONVERT_TWIPS }, + { SC_UNO_CJK_CHEIGHT, ATTR_CJK_FONT_HEIGHT,cppu::UnoType<float>::get(), 0, MID_FONTHEIGHT | CONVERT_TWIPS }, + { SC_UNO_CTL_CHEIGHT, ATTR_CTL_FONT_HEIGHT,cppu::UnoType<float>::get(), 0, MID_FONTHEIGHT | CONVERT_TWIPS }, + { SC_UNONAME_CLOCAL, ATTR_FONT_LANGUAGE, cppu::UnoType<lang::Locale>::get(), 0, MID_LANG_LOCALE }, + { SC_UNO_CJK_CLOCAL, ATTR_CJK_FONT_LANGUAGE,cppu::UnoType<lang::Locale>::get(), 0, MID_LANG_LOCALE }, + { SC_UNO_CTL_CLOCAL, ATTR_CTL_FONT_LANGUAGE,cppu::UnoType<lang::Locale>::get(), 0, MID_LANG_LOCALE }, + { SC_UNONAME_COVER, ATTR_FONT_OVERLINE, cppu::UnoType<sal_Int16>::get(), 0, MID_TL_STYLE }, + { SC_UNONAME_COVRLCOL, ATTR_FONT_OVERLINE, cppu::UnoType<sal_Int32>::get(), 0, MID_TL_COLOR }, + { SC_UNONAME_COVRLHAS, ATTR_FONT_OVERLINE, cppu::UnoType<bool>::get(), 0, MID_TL_HASCOLOR }, + { SC_UNONAME_CPOST, ATTR_FONT_POSTURE, cppu::UnoType<awt::FontSlant>::get(), 0, MID_POSTURE }, + { SC_UNO_CJK_CPOST, ATTR_CJK_FONT_POSTURE,cppu::UnoType<awt::FontSlant>::get(), 0, MID_POSTURE }, + { SC_UNO_CTL_CPOST, ATTR_CTL_FONT_POSTURE,cppu::UnoType<awt::FontSlant>::get(), 0, MID_POSTURE }, + { SC_UNONAME_CRELIEF, ATTR_FONT_RELIEF, cppu::UnoType<sal_Int16>::get(), 0, MID_RELIEF }, + { SC_UNONAME_CSHADD, ATTR_FONT_SHADOWED, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_CSTRIKE, ATTR_FONT_CROSSEDOUT,cppu::UnoType<sal_Int16>::get(), 0, MID_CROSS_OUT }, + { SC_UNONAME_CUNDER, ATTR_FONT_UNDERLINE,cppu::UnoType<sal_Int16>::get(), 0, MID_TL_STYLE }, + { SC_UNONAME_CUNDLCOL, ATTR_FONT_UNDERLINE,cppu::UnoType<sal_Int32>::get(), 0, MID_TL_COLOR }, + { SC_UNONAME_CUNDLHAS, ATTR_FONT_UNDERLINE,cppu::UnoType<bool>::get(), 0, MID_TL_HASCOLOR }, + { SC_UNONAME_CWEIGHT, ATTR_FONT_WEIGHT, cppu::UnoType<float>::get(), 0, MID_WEIGHT }, + { SC_UNO_CJK_CWEIGHT, ATTR_CJK_FONT_WEIGHT,cppu::UnoType<float>::get(), 0, MID_WEIGHT }, + { SC_UNO_CTL_CWEIGHT, ATTR_CTL_FONT_WEIGHT,cppu::UnoType<float>::get(), 0, MID_WEIGHT }, + { SC_UNONAME_CWORDMOD, ATTR_FONT_WORDLINE, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_CHCOLHDR, SC_WID_UNO_CHCOLHDR,cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_CHROWHDR, SC_WID_UNO_CHROWHDR,cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_CONDFMT, SC_WID_UNO_CONDFMT, cppu::UnoType<sheet::XSheetConditionalEntries>::get(), 0, 0 }, + { SC_UNONAME_CONDLOC, SC_WID_UNO_CONDLOC, cppu::UnoType<sheet::XSheetConditionalEntries>::get(), 0, 0 }, + { SC_UNONAME_CONDXML, SC_WID_UNO_CONDXML, cppu::UnoType<sheet::XSheetConditionalEntries>::get(), 0, 0 }, + { SC_UNONAME_DIAGONAL_BLTR, ATTR_BORDER_BLTR, ::cppu::UnoType<table::BorderLine>::get(), 0, 0 | CONVERT_TWIPS }, + { SC_UNONAME_DIAGONAL_BLTR2, ATTR_BORDER_BLTR, ::cppu::UnoType<table::BorderLine2>::get(), 0, 0 | CONVERT_TWIPS }, + { SC_UNONAME_DIAGONAL_TLBR, ATTR_BORDER_TLBR, ::cppu::UnoType<table::BorderLine>::get(), 0, 0 | CONVERT_TWIPS }, + { SC_UNONAME_DIAGONAL_TLBR2, ATTR_BORDER_TLBR, ::cppu::UnoType<table::BorderLine2>::get(), 0, 0 | CONVERT_TWIPS }, + { SC_UNONAME_CELLHJUS, ATTR_HOR_JUSTIFY, cppu::UnoType<table::CellHoriJustify>::get(), 0, MID_HORJUST_HORJUST }, + { SC_UNONAME_CELLHJUS_METHOD, ATTR_HOR_JUSTIFY_METHOD, ::cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { SC_UNONAME_CELLTRAN, ATTR_BACKGROUND, cppu::UnoType<bool>::get(), 0, MID_GRAPHIC_TRANSPARENT }, + { SC_UNONAME_WRAP, ATTR_LINEBREAK, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_NUMFMT, ATTR_VALUE_FORMAT, cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { SC_UNONAME_NUMRULES, SC_WID_UNO_NUMRULES,cppu::UnoType<container::XIndexReplace>::get(), 0, 0 }, + { SC_UNONAME_CELLORI, ATTR_STACKED, cppu::UnoType<table::CellOrientation>::get(), 0, 0 }, + { SC_UNONAME_PADJUST, ATTR_HOR_JUSTIFY, ::cppu::UnoType<sal_Int16>::get(), 0, MID_HORJUST_ADJUST }, + { SC_UNONAME_PBMARGIN, ATTR_MARGIN, cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_LO_MARGIN | CONVERT_TWIPS }, + { SC_UNONAME_PINDENT, ATTR_INDENT, cppu::UnoType<sal_Int16>::get(), 0, 0 }, //! CONVERT_TWIPS + { SC_UNONAME_PISCHDIST,ATTR_SCRIPTSPACE, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_PISFORBID,ATTR_FORBIDDEN_RULES,cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_PISHANG, ATTR_HANGPUNCTUATION,cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_PISHYPHEN,ATTR_HYPHENATE, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_PLASTADJ, ATTR_HOR_JUSTIFY, ::cppu::UnoType<sal_Int16>::get(), 0, MID_HORJUST_ADJUST }, + { SC_UNONAME_PLMARGIN, ATTR_MARGIN, cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_L_MARGIN | CONVERT_TWIPS }, + { SC_UNONAME_PRMARGIN, ATTR_MARGIN, cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_R_MARGIN | CONVERT_TWIPS }, + { SC_UNONAME_PTMARGIN, ATTR_MARGIN, cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_UP_MARGIN | CONVERT_TWIPS }, + { SC_UNONAME_POS, SC_WID_UNO_POS, cppu::UnoType<awt::Point>::get(), 0 | beans::PropertyAttribute::READONLY, 0 }, + { SC_UNONAME_ROTANG, ATTR_ROTATE_VALUE, cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { SC_UNONAME_ROTREF, ATTR_ROTATE_MODE, cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { SC_UNONAME_SHADOW, ATTR_SHADOW, cppu::UnoType<table::ShadowFormat>::get(), 0, 0 | CONVERT_TWIPS }, + { SC_UNONAME_SHRINK_TO_FIT, ATTR_SHRINKTOFIT, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_SIZE, SC_WID_UNO_SIZE, cppu::UnoType<awt::Size>::get(), 0 | beans::PropertyAttribute::READONLY, 0 }, + { SC_UNONAME_TBLBORD, SC_WID_UNO_TBLBORD, cppu::UnoType<table::TableBorder>::get(), 0, 0 | CONVERT_TWIPS }, + { SC_UNONAME_TBLBORD2, SC_WID_UNO_TBLBORD2, cppu::UnoType<table::TableBorder2>::get(), 0, 0 | CONVERT_TWIPS }, + { SC_UNONAME_USERDEF, ATTR_USERDEF, cppu::UnoType<container::XNameContainer>::get(), 0, 0 }, + { SC_UNONAME_VALIDAT, SC_WID_UNO_VALIDAT, cppu::UnoType<beans::XPropertySet>::get(), 0, 0 }, + { SC_UNONAME_VALILOC, SC_WID_UNO_VALILOC, cppu::UnoType<beans::XPropertySet>::get(), 0, 0 }, + { SC_UNONAME_VALIXML, SC_WID_UNO_VALIXML, cppu::UnoType<beans::XPropertySet>::get(), 0, 0 }, + { SC_UNONAME_CELLVJUS, ATTR_VER_JUSTIFY, cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { SC_UNONAME_CELLVJUS_METHOD, ATTR_VER_JUSTIFY_METHOD, ::cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { SC_UNONAME_WRITING, ATTR_WRITINGDIR, cppu::UnoType<sal_Int16>::get(), 0, 0 }, + { SC_UNONAME_FORMATID, SC_WID_UNO_FORMATID, cppu::UnoType<sal_uInt64>::get(), 0, 0 }, + }; + static SfxItemPropertySet aRangePropertySet( aRangePropertyMap_Impl ); + return &aRangePropertySet; +} + +// Cell contains entries from CellRange, plus its own entries +// with Which-ID 0 (those are needed only for getPropertySetInfo). + +static const SfxItemPropertySet* lcl_GetCellPropertySet() +{ + static const SfxItemPropertyMapEntry aCellPropertyMap_Impl[] = + { + { SC_UNONAME_ABSNAME, SC_WID_UNO_ABSNAME, cppu::UnoType<OUString>::get(), 0 | beans::PropertyAttribute::READONLY, 0 }, + { SC_UNONAME_ASIANVERT,ATTR_VERTICAL_ASIAN,cppu::UnoType<bool>::get(), 0, 0 }, + CELL_BORDER_PROPERTIES + CELL_BACKGROUND_COLOR_PROPERTIES + { SC_UNONAME_CELLPRO, ATTR_PROTECTION, cppu::UnoType<util::CellProtection>::get(), 0, 0 }, + { SC_UNONAME_CELLSTYL, SC_WID_UNO_CELLSTYL,cppu::UnoType<OUString>::get(), 0, 0 }, + CHAR_COLOR_PROPERTIES + { SC_UNONAME_COUTL, ATTR_FONT_CONTOUR, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_CCROSS, ATTR_FONT_CROSSEDOUT,cppu::UnoType<bool>::get(), 0, MID_CROSSED_OUT }, + { SC_UNONAME_CEMPHAS, ATTR_FONT_EMPHASISMARK,cppu::UnoType<sal_Int16>::get(), 0, MID_EMPHASIS }, + { SC_UNONAME_CFONT, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY }, + { SC_UNONAME_CFCHARS, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_CHAR_SET }, + { SC_UNO_CJK_CFCHARS, ATTR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_CHAR_SET }, + { SC_UNO_CTL_CFCHARS, ATTR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_CHAR_SET }, + { SC_UNONAME_CFFAMIL, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY }, + { SC_UNO_CJK_CFFAMIL, ATTR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY }, + { SC_UNO_CTL_CFFAMIL, ATTR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY }, + { SC_UNONAME_CFNAME, ATTR_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_FAMILY_NAME }, + { SC_UNO_CJK_CFNAME, ATTR_CJK_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_FAMILY_NAME }, + { SC_UNO_CTL_CFNAME, ATTR_CTL_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_FAMILY_NAME }, + { SC_UNONAME_CFPITCH, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_PITCH }, + { SC_UNO_CJK_CFPITCH, ATTR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_PITCH }, + { SC_UNO_CTL_CFPITCH, ATTR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_PITCH }, + { SC_UNONAME_CFSTYLE, ATTR_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_STYLE_NAME }, + { SC_UNO_CJK_CFSTYLE, ATTR_CJK_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_STYLE_NAME }, + { SC_UNO_CTL_CFSTYLE, ATTR_CTL_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_STYLE_NAME }, + { SC_UNONAME_CHEIGHT, ATTR_FONT_HEIGHT, cppu::UnoType<float>::get(), 0, MID_FONTHEIGHT | CONVERT_TWIPS }, + { SC_UNO_CJK_CHEIGHT, ATTR_CJK_FONT_HEIGHT,cppu::UnoType<float>::get(), 0, MID_FONTHEIGHT | CONVERT_TWIPS }, + { SC_UNO_CTL_CHEIGHT, ATTR_CTL_FONT_HEIGHT,cppu::UnoType<float>::get(), 0, MID_FONTHEIGHT | CONVERT_TWIPS }, + { SC_UNONAME_CLOCAL, ATTR_FONT_LANGUAGE, cppu::UnoType<lang::Locale>::get(), 0, MID_LANG_LOCALE }, + { SC_UNO_CJK_CLOCAL, ATTR_CJK_FONT_LANGUAGE,cppu::UnoType<lang::Locale>::get(), 0, MID_LANG_LOCALE }, + { SC_UNO_CTL_CLOCAL, ATTR_CTL_FONT_LANGUAGE,cppu::UnoType<lang::Locale>::get(), 0, MID_LANG_LOCALE }, + { SC_UNONAME_COVER, ATTR_FONT_OVERLINE, cppu::UnoType<sal_Int16>::get(), 0, MID_TL_STYLE }, + { SC_UNONAME_COVRLCOL, ATTR_FONT_OVERLINE, cppu::UnoType<sal_Int32>::get(), 0, MID_TL_COLOR }, + { SC_UNONAME_COVRLHAS, ATTR_FONT_OVERLINE, cppu::UnoType<bool>::get(), 0, MID_TL_HASCOLOR }, + { SC_UNONAME_CPOST, ATTR_FONT_POSTURE, cppu::UnoType<awt::FontSlant>::get(), 0, MID_POSTURE }, + { SC_UNO_CJK_CPOST, ATTR_CJK_FONT_POSTURE,cppu::UnoType<awt::FontSlant>::get(), 0, MID_POSTURE }, + { SC_UNO_CTL_CPOST, ATTR_CTL_FONT_POSTURE,cppu::UnoType<awt::FontSlant>::get(), 0, MID_POSTURE }, + { SC_UNONAME_CRELIEF, ATTR_FONT_RELIEF, cppu::UnoType<sal_Int16>::get(), 0, MID_RELIEF }, + { SC_UNONAME_CSHADD, ATTR_FONT_SHADOWED, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_CSTRIKE, ATTR_FONT_CROSSEDOUT,cppu::UnoType<sal_Int16>::get(), 0, MID_CROSS_OUT }, + { SC_UNONAME_CUNDER, ATTR_FONT_UNDERLINE,cppu::UnoType<sal_Int16>::get(), 0, MID_TL_STYLE }, + { SC_UNONAME_CUNDLCOL, ATTR_FONT_UNDERLINE,cppu::UnoType<sal_Int32>::get(), 0, MID_TL_COLOR }, + { SC_UNONAME_CUNDLHAS, ATTR_FONT_UNDERLINE,cppu::UnoType<bool>::get(), 0, MID_TL_HASCOLOR }, + { SC_UNONAME_CWEIGHT, ATTR_FONT_WEIGHT, cppu::UnoType<float>::get(), 0, MID_WEIGHT }, + { SC_UNO_CJK_CWEIGHT, ATTR_CJK_FONT_WEIGHT,cppu::UnoType<float>::get(), 0, MID_WEIGHT }, + { SC_UNO_CTL_CWEIGHT, ATTR_CTL_FONT_WEIGHT,cppu::UnoType<float>::get(), 0, MID_WEIGHT }, + { SC_UNONAME_CWORDMOD, ATTR_FONT_WORDLINE, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_CHCOLHDR, SC_WID_UNO_CHCOLHDR,cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_CHROWHDR, SC_WID_UNO_CHROWHDR,cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_CONDFMT, SC_WID_UNO_CONDFMT, cppu::UnoType<sheet::XSheetConditionalEntries>::get(), 0, 0 }, + { SC_UNONAME_CONDLOC, SC_WID_UNO_CONDLOC, cppu::UnoType<sheet::XSheetConditionalEntries>::get(), 0, 0 }, + { SC_UNONAME_CONDXML, SC_WID_UNO_CONDXML, cppu::UnoType<sheet::XSheetConditionalEntries>::get(), 0, 0 }, + { SC_UNONAME_DIAGONAL_BLTR, ATTR_BORDER_BLTR, ::cppu::UnoType<table::BorderLine>::get(), 0, 0 | CONVERT_TWIPS }, + { SC_UNONAME_DIAGONAL_BLTR2, ATTR_BORDER_BLTR, ::cppu::UnoType<table::BorderLine2>::get(), 0, 0 | CONVERT_TWIPS }, + { SC_UNONAME_DIAGONAL_TLBR, ATTR_BORDER_TLBR, ::cppu::UnoType<table::BorderLine>::get(), 0, 0 | CONVERT_TWIPS }, + { SC_UNONAME_DIAGONAL_TLBR2, ATTR_BORDER_TLBR, ::cppu::UnoType<table::BorderLine2>::get(), 0, 0 | CONVERT_TWIPS }, + { SC_UNONAME_FORMLOC, SC_WID_UNO_FORMLOC, cppu::UnoType<OUString>::get(), 0, 0 }, + { SC_UNONAME_FORMRT, SC_WID_UNO_FORMRT, cppu::UnoType<table::CellContentType>::get(), 0 | beans::PropertyAttribute::READONLY, 0 }, + { SC_UNONAME_CELLCONTENTTYPE, SC_WID_UNO_CELLCONTENTTYPE, cppu::UnoType<table::CellContentType>::get(), 0 | beans::PropertyAttribute::READONLY, 0 }, + { SC_UNONAME_FORMRT2, SC_WID_UNO_FORMRT2, cppu::UnoType<sal_Int32>::get(), 0 | beans::PropertyAttribute::READONLY, 0 }, + { SC_UNONAME_CELLHJUS, ATTR_HOR_JUSTIFY, cppu::UnoType<table::CellHoriJustify>::get(), 0, MID_HORJUST_HORJUST }, + { SC_UNONAME_CELLHJUS_METHOD, ATTR_HOR_JUSTIFY_METHOD, ::cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { SC_UNONAME_CELLTRAN, ATTR_BACKGROUND, cppu::UnoType<bool>::get(), 0, MID_GRAPHIC_TRANSPARENT }, + { SC_UNONAME_WRAP, ATTR_LINEBREAK, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_NUMFMT, ATTR_VALUE_FORMAT, cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { SC_UNONAME_NUMRULES, SC_WID_UNO_NUMRULES,cppu::UnoType<container::XIndexReplace>::get(), 0, 0 }, + { SC_UNONAME_CELLORI, ATTR_STACKED, cppu::UnoType<table::CellOrientation>::get(), 0, 0 }, + { SC_UNONAME_PADJUST, ATTR_HOR_JUSTIFY, ::cppu::UnoType<sal_Int16>::get(), 0, MID_HORJUST_ADJUST }, + { SC_UNONAME_PBMARGIN, ATTR_MARGIN, cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_LO_MARGIN | CONVERT_TWIPS }, + { SC_UNONAME_PINDENT, ATTR_INDENT, cppu::UnoType<sal_Int16>::get(), 0, 0 }, //! CONVERT_TWIPS + { SC_UNONAME_PISCHDIST,ATTR_SCRIPTSPACE, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_PISFORBID,ATTR_FORBIDDEN_RULES,cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_PISHANG, ATTR_HANGPUNCTUATION,cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_PISHYPHEN,ATTR_HYPHENATE, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_PLASTADJ, ATTR_HOR_JUSTIFY, ::cppu::UnoType<sal_Int16>::get(), 0, MID_HORJUST_ADJUST }, + { SC_UNONAME_PLMARGIN, ATTR_MARGIN, cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_L_MARGIN | CONVERT_TWIPS }, + { SC_UNONAME_PRMARGIN, ATTR_MARGIN, cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_R_MARGIN | CONVERT_TWIPS }, + { SC_UNONAME_PTMARGIN, ATTR_MARGIN, cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_UP_MARGIN | CONVERT_TWIPS }, + { SC_UNONAME_POS, SC_WID_UNO_POS, cppu::UnoType<awt::Point>::get(), 0 | beans::PropertyAttribute::READONLY, 0 }, + { SC_UNONAME_ROTANG, ATTR_ROTATE_VALUE, cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { SC_UNONAME_ROTREF, ATTR_ROTATE_MODE, cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { SC_UNONAME_SHADOW, ATTR_SHADOW, cppu::UnoType<table::ShadowFormat>::get(), 0, 0 | CONVERT_TWIPS }, + { SC_UNONAME_SHRINK_TO_FIT, ATTR_SHRINKTOFIT, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_SIZE, SC_WID_UNO_SIZE, cppu::UnoType<awt::Size>::get(), 0 | beans::PropertyAttribute::READONLY, 0 }, + { SC_UNONAME_TBLBORD, SC_WID_UNO_TBLBORD, cppu::UnoType<table::TableBorder>::get(), 0, 0 | CONVERT_TWIPS }, + { SC_UNONAME_TBLBORD2, SC_WID_UNO_TBLBORD2, cppu::UnoType<table::TableBorder2>::get(), 0, 0 | CONVERT_TWIPS }, + { SC_UNONAME_USERDEF, ATTR_USERDEF, cppu::UnoType<container::XNameContainer>::get(), 0, 0 }, + { SC_UNONAME_VALIDAT, SC_WID_UNO_VALIDAT, cppu::UnoType<beans::XPropertySet>::get(), 0, 0 }, + { SC_UNONAME_VALILOC, SC_WID_UNO_VALILOC, cppu::UnoType<beans::XPropertySet>::get(), 0, 0 }, + { SC_UNONAME_VALIXML, SC_WID_UNO_VALIXML, cppu::UnoType<beans::XPropertySet>::get(), 0, 0 }, + { SC_UNONAME_CELLVJUS, ATTR_VER_JUSTIFY, cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { SC_UNONAME_CELLVJUS_METHOD, ATTR_VER_JUSTIFY_METHOD, ::cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { SC_UNONAME_WRITING, ATTR_WRITINGDIR, cppu::UnoType<sal_Int16>::get(), 0, 0 }, + { UNO_NAME_EDIT_CHAR_ESCAPEMENT, EE_CHAR_ESCAPEMENT, cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { SC_UNONAME_HYPERLINK, ATTR_HYPERLINK, cppu::UnoType<OUString>::get(), 0, 0 }, + { SC_UNONAME_FORMATID, SC_WID_UNO_FORMATID, cppu::UnoType<sal_uInt64>::get(), 0, 0 }, + }; + static SfxItemPropertySet aCellPropertySet( aCellPropertyMap_Impl ); + return &aCellPropertySet; +} + +// Column and Row contain all entries from CellRange, plus its own entries +// with Which-ID 0 (those are needed only for getPropertySetInfo). + +static const SfxItemPropertySet* lcl_GetColumnPropertySet() +{ + static const SfxItemPropertyMapEntry aColumnPropertyMap_Impl[] = + { + { SC_UNONAME_ABSNAME, SC_WID_UNO_ABSNAME, cppu::UnoType<OUString>::get(), 0 | beans::PropertyAttribute::READONLY, 0 }, + { SC_UNONAME_ASIANVERT,ATTR_VERTICAL_ASIAN,cppu::UnoType<bool>::get(), 0, 0 }, + CELL_BORDER_PROPERTIES + CELL_BACKGROUND_COLOR_PROPERTIES + { SC_UNONAME_CELLPRO, ATTR_PROTECTION, cppu::UnoType<util::CellProtection>::get(), 0, 0 }, + { SC_UNONAME_CELLSTYL, SC_WID_UNO_CELLSTYL,cppu::UnoType<OUString>::get(), 0, 0 }, + CHAR_COLOR_PROPERTIES + { SC_UNONAME_COUTL, ATTR_FONT_CONTOUR, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_CCROSS, ATTR_FONT_CROSSEDOUT,cppu::UnoType<bool>::get(), 0, MID_CROSSED_OUT }, + { SC_UNONAME_CEMPHAS, ATTR_FONT_EMPHASISMARK,cppu::UnoType<sal_Int16>::get(), 0, MID_EMPHASIS }, + { SC_UNONAME_CFONT, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY }, + { SC_UNONAME_CFCHARS, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_CHAR_SET }, + { SC_UNO_CJK_CFCHARS, ATTR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_CHAR_SET }, + { SC_UNO_CTL_CFCHARS, ATTR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_CHAR_SET }, + { SC_UNONAME_CFFAMIL, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY }, + { SC_UNO_CJK_CFFAMIL, ATTR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY }, + { SC_UNO_CTL_CFFAMIL, ATTR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY }, + { SC_UNONAME_CFNAME, ATTR_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_FAMILY_NAME }, + { SC_UNO_CJK_CFNAME, ATTR_CJK_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_FAMILY_NAME }, + { SC_UNO_CTL_CFNAME, ATTR_CTL_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_FAMILY_NAME }, + { SC_UNONAME_CFPITCH, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_PITCH }, + { SC_UNO_CJK_CFPITCH, ATTR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_PITCH }, + { SC_UNO_CTL_CFPITCH, ATTR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_PITCH }, + { SC_UNONAME_CFSTYLE, ATTR_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_STYLE_NAME }, + { SC_UNO_CJK_CFSTYLE, ATTR_CJK_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_STYLE_NAME }, + { SC_UNO_CTL_CFSTYLE, ATTR_CTL_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_STYLE_NAME }, + { SC_UNONAME_CHEIGHT, ATTR_FONT_HEIGHT, cppu::UnoType<float>::get(), 0, MID_FONTHEIGHT | CONVERT_TWIPS }, + { SC_UNO_CJK_CHEIGHT, ATTR_CJK_FONT_HEIGHT,cppu::UnoType<float>::get(), 0, MID_FONTHEIGHT | CONVERT_TWIPS }, + { SC_UNO_CTL_CHEIGHT, ATTR_CTL_FONT_HEIGHT,cppu::UnoType<float>::get(), 0, MID_FONTHEIGHT | CONVERT_TWIPS }, + { SC_UNONAME_CLOCAL, ATTR_FONT_LANGUAGE, cppu::UnoType<lang::Locale>::get(), 0, MID_LANG_LOCALE }, + { SC_UNO_CJK_CLOCAL, ATTR_CJK_FONT_LANGUAGE,cppu::UnoType<lang::Locale>::get(), 0, MID_LANG_LOCALE }, + { SC_UNO_CTL_CLOCAL, ATTR_CTL_FONT_LANGUAGE,cppu::UnoType<lang::Locale>::get(), 0, MID_LANG_LOCALE }, + { SC_UNONAME_COVER, ATTR_FONT_OVERLINE, cppu::UnoType<sal_Int16>::get(), 0, MID_TL_STYLE }, + { SC_UNONAME_COVRLCOL, ATTR_FONT_OVERLINE, cppu::UnoType<sal_Int32>::get(), 0, MID_TL_COLOR }, + { SC_UNONAME_COVRLHAS, ATTR_FONT_OVERLINE, cppu::UnoType<bool>::get(), 0, MID_TL_HASCOLOR }, + { SC_UNONAME_CPOST, ATTR_FONT_POSTURE, cppu::UnoType<awt::FontSlant>::get(), 0, MID_POSTURE }, + { SC_UNO_CJK_CPOST, ATTR_CJK_FONT_POSTURE,cppu::UnoType<awt::FontSlant>::get(), 0, MID_POSTURE }, + { SC_UNO_CTL_CPOST, ATTR_CTL_FONT_POSTURE,cppu::UnoType<awt::FontSlant>::get(), 0, MID_POSTURE }, + { SC_UNONAME_CRELIEF, ATTR_FONT_RELIEF, cppu::UnoType<sal_Int16>::get(), 0, MID_RELIEF }, + { SC_UNONAME_CSHADD, ATTR_FONT_SHADOWED, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_CSTRIKE, ATTR_FONT_CROSSEDOUT,cppu::UnoType<sal_Int16>::get(), 0, MID_CROSS_OUT }, + { SC_UNONAME_CUNDER, ATTR_FONT_UNDERLINE,cppu::UnoType<sal_Int16>::get(), 0, MID_TL_STYLE }, + { SC_UNONAME_CUNDLCOL, ATTR_FONT_UNDERLINE,cppu::UnoType<sal_Int32>::get(), 0, MID_TL_COLOR }, + { SC_UNONAME_CUNDLHAS, ATTR_FONT_UNDERLINE,cppu::UnoType<bool>::get(), 0, MID_TL_HASCOLOR }, + { SC_UNONAME_CWEIGHT, ATTR_FONT_WEIGHT, cppu::UnoType<float>::get(), 0, MID_WEIGHT }, + { SC_UNO_CJK_CWEIGHT, ATTR_CJK_FONT_WEIGHT,cppu::UnoType<float>::get(), 0, MID_WEIGHT }, + { SC_UNO_CTL_CWEIGHT, ATTR_CTL_FONT_WEIGHT,cppu::UnoType<float>::get(), 0, MID_WEIGHT }, + { SC_UNONAME_CWORDMOD, ATTR_FONT_WORDLINE, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_CHCOLHDR, SC_WID_UNO_CHCOLHDR,cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_CHROWHDR, SC_WID_UNO_CHROWHDR,cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_CONDFMT, SC_WID_UNO_CONDFMT, cppu::UnoType<sheet::XSheetConditionalEntries>::get(), 0, 0 }, + { SC_UNONAME_CONDLOC, SC_WID_UNO_CONDLOC, cppu::UnoType<sheet::XSheetConditionalEntries>::get(), 0, 0 }, + { SC_UNONAME_CONDXML, SC_WID_UNO_CONDXML, cppu::UnoType<sheet::XSheetConditionalEntries>::get(), 0, 0 }, + { SC_UNONAME_DIAGONAL_BLTR, ATTR_BORDER_BLTR, ::cppu::UnoType<table::BorderLine>::get(), 0, 0 | CONVERT_TWIPS }, + { SC_UNONAME_DIAGONAL_BLTR2, ATTR_BORDER_BLTR, ::cppu::UnoType<table::BorderLine2>::get(), 0, 0 | CONVERT_TWIPS }, + { SC_UNONAME_DIAGONAL_TLBR, ATTR_BORDER_TLBR, ::cppu::UnoType<table::BorderLine>::get(), 0, 0 | CONVERT_TWIPS }, + { SC_UNONAME_DIAGONAL_TLBR2, ATTR_BORDER_TLBR, ::cppu::UnoType<table::BorderLine2>::get(), 0, 0 | CONVERT_TWIPS }, + { SC_UNONAME_CELLHJUS, ATTR_HOR_JUSTIFY, cppu::UnoType<table::CellHoriJustify>::get(), 0, MID_HORJUST_HORJUST }, + { SC_UNONAME_CELLHJUS_METHOD, ATTR_HOR_JUSTIFY_METHOD, ::cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { SC_UNONAME_CELLTRAN, ATTR_BACKGROUND, cppu::UnoType<bool>::get(), 0, MID_GRAPHIC_TRANSPARENT }, + { SC_UNONAME_MANPAGE, SC_WID_UNO_MANPAGE, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_NEWPAGE, SC_WID_UNO_NEWPAGE, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_WRAP, ATTR_LINEBREAK, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_CELLVIS, SC_WID_UNO_CELLVIS, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_NUMFMT, ATTR_VALUE_FORMAT, cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { SC_UNONAME_NUMRULES, SC_WID_UNO_NUMRULES,cppu::UnoType<container::XIndexReplace>::get(), 0, 0 }, + { SC_UNONAME_OWIDTH, SC_WID_UNO_OWIDTH, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_CELLORI, ATTR_STACKED, cppu::UnoType<table::CellOrientation>::get(), 0, 0 }, + { SC_UNONAME_PADJUST, ATTR_HOR_JUSTIFY, ::cppu::UnoType<sal_Int16>::get(), 0, MID_HORJUST_ADJUST }, + { SC_UNONAME_PBMARGIN, ATTR_MARGIN, cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_LO_MARGIN | CONVERT_TWIPS }, + { SC_UNONAME_PINDENT, ATTR_INDENT, cppu::UnoType<sal_Int16>::get(), 0, 0 }, //! CONVERT_TWIPS + { SC_UNONAME_PISCHDIST,ATTR_SCRIPTSPACE, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_PISFORBID,ATTR_FORBIDDEN_RULES,cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_PISHANG, ATTR_HANGPUNCTUATION,cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_PISHYPHEN,ATTR_HYPHENATE, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_PLASTADJ, ATTR_HOR_JUSTIFY, ::cppu::UnoType<sal_Int16>::get(), 0, MID_HORJUST_ADJUST }, + { SC_UNONAME_PLMARGIN, ATTR_MARGIN, cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_L_MARGIN | CONVERT_TWIPS }, + { SC_UNONAME_PRMARGIN, ATTR_MARGIN, cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_R_MARGIN | CONVERT_TWIPS }, + { SC_UNONAME_PTMARGIN, ATTR_MARGIN, cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_UP_MARGIN | CONVERT_TWIPS }, + { SC_UNONAME_POS, SC_WID_UNO_POS, cppu::UnoType<awt::Point>::get(), 0 | beans::PropertyAttribute::READONLY, 0 }, + { SC_UNONAME_ROTANG, ATTR_ROTATE_VALUE, cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { SC_UNONAME_ROTREF, ATTR_ROTATE_MODE, cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { SC_UNONAME_SHADOW, ATTR_SHADOW, cppu::UnoType<table::ShadowFormat>::get(), 0, 0 | CONVERT_TWIPS }, + { SC_UNONAME_SHRINK_TO_FIT, ATTR_SHRINKTOFIT, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_SIZE, SC_WID_UNO_SIZE, cppu::UnoType<awt::Size>::get(), 0 | beans::PropertyAttribute::READONLY, 0 }, + { SC_UNONAME_TBLBORD, SC_WID_UNO_TBLBORD, cppu::UnoType<table::TableBorder>::get(), 0, 0 | CONVERT_TWIPS }, + { SC_UNONAME_TBLBORD2, SC_WID_UNO_TBLBORD2, cppu::UnoType<table::TableBorder2>::get(), 0, 0 | CONVERT_TWIPS }, + { SC_UNONAME_USERDEF, ATTR_USERDEF, cppu::UnoType<container::XNameContainer>::get(), 0, 0 }, + { SC_UNONAME_VALIDAT, SC_WID_UNO_VALIDAT, cppu::UnoType<beans::XPropertySet>::get(), 0, 0 }, + { SC_UNONAME_VALILOC, SC_WID_UNO_VALILOC, cppu::UnoType<beans::XPropertySet>::get(), 0, 0 }, + { SC_UNONAME_VALIXML, SC_WID_UNO_VALIXML, cppu::UnoType<beans::XPropertySet>::get(), 0, 0 }, + { SC_UNONAME_CELLVJUS, ATTR_VER_JUSTIFY, cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { SC_UNONAME_CELLVJUS_METHOD, ATTR_VER_JUSTIFY_METHOD, ::cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { SC_UNONAME_CELLWID, SC_WID_UNO_CELLWID, cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { SC_UNONAME_WRITING, ATTR_WRITINGDIR, cppu::UnoType<sal_Int16>::get(), 0, 0 }, + }; + static SfxItemPropertySet aColumnPropertySet( aColumnPropertyMap_Impl ); + return &aColumnPropertySet; +} + +static const SfxItemPropertySet* lcl_GetRowPropertySet() +{ + static const SfxItemPropertyMapEntry aRowPropertyMap_Impl[] = + { + { SC_UNONAME_ABSNAME, SC_WID_UNO_ABSNAME, cppu::UnoType<OUString>::get(), 0 | beans::PropertyAttribute::READONLY, 0 }, + { SC_UNONAME_ASIANVERT,ATTR_VERTICAL_ASIAN,cppu::UnoType<bool>::get(), 0, 0 }, + CELL_BORDER_PROPERTIES + CELL_BACKGROUND_COLOR_PROPERTIES + { SC_UNONAME_CELLPRO, ATTR_PROTECTION, cppu::UnoType<util::CellProtection>::get(), 0, 0 }, + { SC_UNONAME_CELLSTYL, SC_WID_UNO_CELLSTYL,cppu::UnoType<OUString>::get(), 0, 0 }, + CHAR_COLOR_PROPERTIES + { SC_UNONAME_COUTL, ATTR_FONT_CONTOUR, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_CCROSS, ATTR_FONT_CROSSEDOUT,cppu::UnoType<bool>::get(), 0, MID_CROSSED_OUT }, + { SC_UNONAME_CEMPHAS, ATTR_FONT_EMPHASISMARK,cppu::UnoType<sal_Int16>::get(), 0, MID_EMPHASIS }, + { SC_UNONAME_CFONT, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY }, + { SC_UNONAME_CFCHARS, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_CHAR_SET }, + { SC_UNO_CJK_CFCHARS, ATTR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_CHAR_SET }, + { SC_UNO_CTL_CFCHARS, ATTR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_CHAR_SET }, + { SC_UNONAME_CFFAMIL, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY }, + { SC_UNO_CJK_CFFAMIL, ATTR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY }, + { SC_UNO_CTL_CFFAMIL, ATTR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY }, + { SC_UNONAME_CFNAME, ATTR_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_FAMILY_NAME }, + { SC_UNO_CJK_CFNAME, ATTR_CJK_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_FAMILY_NAME }, + { SC_UNO_CTL_CFNAME, ATTR_CTL_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_FAMILY_NAME }, + { SC_UNONAME_CFPITCH, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_PITCH }, + { SC_UNO_CJK_CFPITCH, ATTR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_PITCH }, + { SC_UNO_CTL_CFPITCH, ATTR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_PITCH }, + { SC_UNONAME_CFSTYLE, ATTR_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_STYLE_NAME }, + { SC_UNO_CJK_CFSTYLE, ATTR_CJK_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_STYLE_NAME }, + { SC_UNO_CTL_CFSTYLE, ATTR_CTL_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_STYLE_NAME }, + { SC_UNONAME_CHEIGHT, ATTR_FONT_HEIGHT, cppu::UnoType<float>::get(), 0, MID_FONTHEIGHT | CONVERT_TWIPS }, + { SC_UNO_CJK_CHEIGHT, ATTR_CJK_FONT_HEIGHT,cppu::UnoType<float>::get(), 0, MID_FONTHEIGHT | CONVERT_TWIPS }, + { SC_UNO_CTL_CHEIGHT, ATTR_CTL_FONT_HEIGHT,cppu::UnoType<float>::get(), 0, MID_FONTHEIGHT | CONVERT_TWIPS }, + { SC_UNONAME_CLOCAL, ATTR_FONT_LANGUAGE, cppu::UnoType<lang::Locale>::get(), 0, MID_LANG_LOCALE }, + { SC_UNO_CJK_CLOCAL, ATTR_CJK_FONT_LANGUAGE,cppu::UnoType<lang::Locale>::get(), 0, MID_LANG_LOCALE }, + { SC_UNO_CTL_CLOCAL, ATTR_CTL_FONT_LANGUAGE,cppu::UnoType<lang::Locale>::get(), 0, MID_LANG_LOCALE }, + { SC_UNONAME_COVER, ATTR_FONT_OVERLINE, cppu::UnoType<sal_Int16>::get(), 0, MID_TL_STYLE }, + { SC_UNONAME_COVRLCOL, ATTR_FONT_OVERLINE, cppu::UnoType<sal_Int32>::get(), 0, MID_TL_COLOR }, + { SC_UNONAME_COVRLHAS, ATTR_FONT_OVERLINE, cppu::UnoType<bool>::get(), 0, MID_TL_HASCOLOR }, + { SC_UNONAME_CPOST, ATTR_FONT_POSTURE, cppu::UnoType<awt::FontSlant>::get(), 0, MID_POSTURE }, + { SC_UNO_CJK_CPOST, ATTR_CJK_FONT_POSTURE,cppu::UnoType<awt::FontSlant>::get(), 0, MID_POSTURE }, + { SC_UNO_CTL_CPOST, ATTR_CTL_FONT_POSTURE,cppu::UnoType<awt::FontSlant>::get(), 0, MID_POSTURE }, + { SC_UNONAME_CRELIEF, ATTR_FONT_RELIEF, cppu::UnoType<sal_Int16>::get(), 0, MID_RELIEF }, + { SC_UNONAME_CSHADD, ATTR_FONT_SHADOWED, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_CSTRIKE, ATTR_FONT_CROSSEDOUT,cppu::UnoType<sal_Int16>::get(), 0, MID_CROSS_OUT }, + { SC_UNONAME_CUNDER, ATTR_FONT_UNDERLINE,cppu::UnoType<sal_Int16>::get(), 0, MID_TL_STYLE }, + { SC_UNONAME_CUNDLCOL, ATTR_FONT_UNDERLINE,cppu::UnoType<sal_Int32>::get(), 0, MID_TL_COLOR }, + { SC_UNONAME_CUNDLHAS, ATTR_FONT_UNDERLINE,cppu::UnoType<bool>::get(), 0, MID_TL_HASCOLOR }, + { SC_UNONAME_CWEIGHT, ATTR_FONT_WEIGHT, cppu::UnoType<float>::get(), 0, MID_WEIGHT }, + { SC_UNO_CJK_CWEIGHT, ATTR_CJK_FONT_WEIGHT,cppu::UnoType<float>::get(), 0, MID_WEIGHT }, + { SC_UNO_CTL_CWEIGHT, ATTR_CTL_FONT_WEIGHT,cppu::UnoType<float>::get(), 0, MID_WEIGHT }, + { SC_UNONAME_CWORDMOD, ATTR_FONT_WORDLINE, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_CHCOLHDR, SC_WID_UNO_CHCOLHDR,cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_CHROWHDR, SC_WID_UNO_CHROWHDR,cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_CONDFMT, SC_WID_UNO_CONDFMT, cppu::UnoType<sheet::XSheetConditionalEntries>::get(), 0, 0 }, + { SC_UNONAME_CONDLOC, SC_WID_UNO_CONDLOC, cppu::UnoType<sheet::XSheetConditionalEntries>::get(), 0, 0 }, + { SC_UNONAME_CONDXML, SC_WID_UNO_CONDXML, cppu::UnoType<sheet::XSheetConditionalEntries>::get(), 0, 0 }, + { SC_UNONAME_DIAGONAL_BLTR, ATTR_BORDER_BLTR, ::cppu::UnoType<table::BorderLine>::get(), 0, 0 | CONVERT_TWIPS }, + { SC_UNONAME_DIAGONAL_BLTR2, ATTR_BORDER_BLTR, ::cppu::UnoType<table::BorderLine2>::get(), 0, 0 | CONVERT_TWIPS }, + { SC_UNONAME_DIAGONAL_TLBR, ATTR_BORDER_TLBR, ::cppu::UnoType<table::BorderLine>::get(), 0, 0 | CONVERT_TWIPS }, + { SC_UNONAME_DIAGONAL_TLBR2, ATTR_BORDER_TLBR, ::cppu::UnoType<table::BorderLine2>::get(), 0, 0 | CONVERT_TWIPS }, + { SC_UNONAME_CELLHGT, SC_WID_UNO_CELLHGT, cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { SC_UNONAME_CELLHJUS, ATTR_HOR_JUSTIFY, cppu::UnoType<table::CellHoriJustify>::get(), 0, MID_HORJUST_HORJUST }, + { SC_UNONAME_CELLHJUS_METHOD, ATTR_HOR_JUSTIFY_METHOD, ::cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { SC_UNONAME_CELLTRAN, ATTR_BACKGROUND, cppu::UnoType<bool>::get(), 0, MID_GRAPHIC_TRANSPARENT }, + { SC_UNONAME_CELLFILT, SC_WID_UNO_CELLFILT,cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_MANPAGE, SC_WID_UNO_MANPAGE, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_NEWPAGE, SC_WID_UNO_NEWPAGE, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_WRAP, ATTR_LINEBREAK, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_CELLVIS, SC_WID_UNO_CELLVIS, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_NUMFMT, ATTR_VALUE_FORMAT, cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { SC_UNONAME_NUMRULES, SC_WID_UNO_NUMRULES,cppu::UnoType<container::XIndexReplace>::get(), 0, 0 }, + { SC_UNONAME_OHEIGHT, SC_WID_UNO_OHEIGHT, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_CELLORI, ATTR_STACKED, cppu::UnoType<table::CellOrientation>::get(), 0, 0 }, + { SC_UNONAME_PADJUST, ATTR_HOR_JUSTIFY, ::cppu::UnoType<sal_Int16>::get(), 0, MID_HORJUST_ADJUST }, + { SC_UNONAME_PBMARGIN, ATTR_MARGIN, cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_LO_MARGIN | CONVERT_TWIPS }, + { SC_UNONAME_PINDENT, ATTR_INDENT, cppu::UnoType<sal_Int16>::get(), 0, 0 }, //! CONVERT_TWIPS + { SC_UNONAME_PISCHDIST,ATTR_SCRIPTSPACE, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_PISFORBID,ATTR_FORBIDDEN_RULES,cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_PISHANG, ATTR_HANGPUNCTUATION,cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_PISHYPHEN,ATTR_HYPHENATE, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_PLASTADJ, ATTR_HOR_JUSTIFY, ::cppu::UnoType<sal_Int16>::get(), 0, MID_HORJUST_ADJUST }, + { SC_UNONAME_PLMARGIN, ATTR_MARGIN, cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_L_MARGIN | CONVERT_TWIPS }, + { SC_UNONAME_PRMARGIN, ATTR_MARGIN, cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_R_MARGIN | CONVERT_TWIPS }, + { SC_UNONAME_PTMARGIN, ATTR_MARGIN, cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_UP_MARGIN | CONVERT_TWIPS }, + { SC_UNONAME_POS, SC_WID_UNO_POS, cppu::UnoType<awt::Point>::get(), 0 | beans::PropertyAttribute::READONLY, 0 }, + { SC_UNONAME_ROTANG, ATTR_ROTATE_VALUE, cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { SC_UNONAME_ROTREF, ATTR_ROTATE_MODE, cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { SC_UNONAME_SHADOW, ATTR_SHADOW, cppu::UnoType<table::ShadowFormat>::get(), 0, 0 | CONVERT_TWIPS }, + { SC_UNONAME_SHRINK_TO_FIT, ATTR_SHRINKTOFIT, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_SIZE, SC_WID_UNO_SIZE, cppu::UnoType<awt::Size>::get(), 0 | beans::PropertyAttribute::READONLY, 0 }, + { SC_UNONAME_TBLBORD, SC_WID_UNO_TBLBORD, cppu::UnoType<table::TableBorder>::get(), 0, 0 | CONVERT_TWIPS }, + { SC_UNONAME_TBLBORD2, SC_WID_UNO_TBLBORD2, cppu::UnoType<table::TableBorder2>::get(), 0, 0 | CONVERT_TWIPS }, + { SC_UNONAME_USERDEF, ATTR_USERDEF, cppu::UnoType<container::XNameContainer>::get(), 0, 0 }, + { SC_UNONAME_VALIDAT, SC_WID_UNO_VALIDAT, cppu::UnoType<beans::XPropertySet>::get(), 0, 0 }, + { SC_UNONAME_VALILOC, SC_WID_UNO_VALILOC, cppu::UnoType<beans::XPropertySet>::get(), 0, 0 }, + { SC_UNONAME_VALIXML, SC_WID_UNO_VALIXML, cppu::UnoType<beans::XPropertySet>::get(), 0, 0 }, + { SC_UNONAME_CELLVJUS, ATTR_VER_JUSTIFY, cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { SC_UNONAME_CELLVJUS_METHOD, ATTR_VER_JUSTIFY_METHOD, ::cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { SC_UNONAME_WRITING, ATTR_WRITINGDIR, cppu::UnoType<sal_Int16>::get(), 0, 0 }, + }; + static SfxItemPropertySet aRowPropertySet( aRowPropertyMap_Impl ); + return &aRowPropertySet; +} + +static const SfxItemPropertySet* lcl_GetSheetPropertySet() +{ + static const SfxItemPropertyMapEntry aSheetPropertyMap_Impl[] = + { + { SC_UNONAME_ABSNAME, SC_WID_UNO_ABSNAME, cppu::UnoType<OUString>::get(), 0 | beans::PropertyAttribute::READONLY, 0 }, + { SC_UNONAME_ASIANVERT,ATTR_VERTICAL_ASIAN,cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_AUTOPRINT,SC_WID_UNO_AUTOPRINT,cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_BORDCOL, SC_WID_UNO_BORDCOL, cppu::UnoType<sal_Int32>::get(), 0, 0 }, + CELL_BORDER_PROPERTIES + CELL_BACKGROUND_COLOR_PROPERTIES + { SC_UNONAME_CELLPRO, ATTR_PROTECTION, cppu::UnoType<util::CellProtection>::get(), 0, 0 }, + { SC_UNONAME_CELLSTYL, SC_WID_UNO_CELLSTYL,cppu::UnoType<OUString>::get(), 0, 0 }, + CHAR_COLOR_PROPERTIES + { SC_UNONAME_COUTL, ATTR_FONT_CONTOUR, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_CCROSS, ATTR_FONT_CROSSEDOUT,cppu::UnoType<bool>::get(), 0, MID_CROSSED_OUT }, + { SC_UNONAME_CEMPHAS, ATTR_FONT_EMPHASISMARK,cppu::UnoType<sal_Int16>::get(), 0, MID_EMPHASIS }, + { SC_UNONAME_CFONT, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY }, + { SC_UNONAME_CFCHARS, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_CHAR_SET }, + { SC_UNO_CJK_CFCHARS, ATTR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_CHAR_SET }, + { SC_UNO_CTL_CFCHARS, ATTR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_CHAR_SET }, + { SC_UNONAME_CFFAMIL, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY }, + { SC_UNO_CJK_CFFAMIL, ATTR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY }, + { SC_UNO_CTL_CFFAMIL, ATTR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY }, + { SC_UNONAME_CFNAME, ATTR_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_FAMILY_NAME }, + { SC_UNO_CJK_CFNAME, ATTR_CJK_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_FAMILY_NAME }, + { SC_UNO_CTL_CFNAME, ATTR_CTL_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_FAMILY_NAME }, + { SC_UNONAME_CFPITCH, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_PITCH }, + { SC_UNO_CJK_CFPITCH, ATTR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_PITCH }, + { SC_UNO_CTL_CFPITCH, ATTR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_PITCH }, + { SC_UNONAME_CFSTYLE, ATTR_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_STYLE_NAME }, + { SC_UNO_CJK_CFSTYLE, ATTR_CJK_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_STYLE_NAME }, + { SC_UNO_CTL_CFSTYLE, ATTR_CTL_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_STYLE_NAME }, + { SC_UNONAME_CHEIGHT, ATTR_FONT_HEIGHT, cppu::UnoType<float>::get(), 0, MID_FONTHEIGHT | CONVERT_TWIPS }, + { SC_UNO_CJK_CHEIGHT, ATTR_CJK_FONT_HEIGHT,cppu::UnoType<float>::get(), 0, MID_FONTHEIGHT | CONVERT_TWIPS }, + { SC_UNO_CTL_CHEIGHT, ATTR_CTL_FONT_HEIGHT,cppu::UnoType<float>::get(), 0, MID_FONTHEIGHT | CONVERT_TWIPS }, + { SC_UNONAME_CLOCAL, ATTR_FONT_LANGUAGE, cppu::UnoType<lang::Locale>::get(), 0, MID_LANG_LOCALE }, + { SC_UNO_CJK_CLOCAL, ATTR_CJK_FONT_LANGUAGE,cppu::UnoType<lang::Locale>::get(), 0, MID_LANG_LOCALE }, + { SC_UNO_CTL_CLOCAL, ATTR_CTL_FONT_LANGUAGE,cppu::UnoType<lang::Locale>::get(), 0, MID_LANG_LOCALE }, + { SC_UNONAME_COVER, ATTR_FONT_OVERLINE, cppu::UnoType<sal_Int16>::get(), 0, MID_TL_STYLE }, + { SC_UNONAME_COVRLCOL, ATTR_FONT_OVERLINE, cppu::UnoType<sal_Int32>::get(), 0, MID_TL_COLOR }, + { SC_UNONAME_COVRLHAS, ATTR_FONT_OVERLINE, cppu::UnoType<bool>::get(), 0, MID_TL_HASCOLOR }, + { SC_UNONAME_CPOST, ATTR_FONT_POSTURE, cppu::UnoType<awt::FontSlant>::get(), 0, MID_POSTURE }, + { SC_UNO_CJK_CPOST, ATTR_CJK_FONT_POSTURE,cppu::UnoType<awt::FontSlant>::get(), 0, MID_POSTURE }, + { SC_UNO_CTL_CPOST, ATTR_CTL_FONT_POSTURE,cppu::UnoType<awt::FontSlant>::get(), 0, MID_POSTURE }, + { SC_UNONAME_CRELIEF, ATTR_FONT_RELIEF, cppu::UnoType<sal_Int16>::get(), 0, MID_RELIEF }, + { SC_UNONAME_CSHADD, ATTR_FONT_SHADOWED, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_CSTRIKE, ATTR_FONT_CROSSEDOUT,cppu::UnoType<sal_Int16>::get(), 0, MID_CROSS_OUT }, + { SC_UNONAME_CUNDER, ATTR_FONT_UNDERLINE,cppu::UnoType<sal_Int16>::get(), 0, MID_TL_STYLE }, + { SC_UNONAME_CUNDLCOL, ATTR_FONT_UNDERLINE,cppu::UnoType<sal_Int32>::get(), 0, MID_TL_COLOR }, + { SC_UNONAME_CUNDLHAS, ATTR_FONT_UNDERLINE,cppu::UnoType<bool>::get(), 0, MID_TL_HASCOLOR }, + { SC_UNONAME_CWEIGHT, ATTR_FONT_WEIGHT, cppu::UnoType<float>::get(), 0, MID_WEIGHT }, + { SC_UNO_CJK_CWEIGHT, ATTR_CJK_FONT_WEIGHT,cppu::UnoType<float>::get(), 0, MID_WEIGHT }, + { SC_UNO_CTL_CWEIGHT, ATTR_CTL_FONT_WEIGHT,cppu::UnoType<float>::get(), 0, MID_WEIGHT }, + { SC_UNONAME_CWORDMOD, ATTR_FONT_WORDLINE, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_CHCOLHDR, SC_WID_UNO_CHCOLHDR,cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_CHROWHDR, SC_WID_UNO_CHROWHDR,cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_CONDFMT, SC_WID_UNO_CONDFMT, cppu::UnoType<sheet::XSheetConditionalEntries>::get(), 0, 0 }, + { SC_UNONAME_CONDLOC, SC_WID_UNO_CONDLOC, cppu::UnoType<sheet::XSheetConditionalEntries>::get(), 0, 0 }, + { SC_UNONAME_CONDXML, SC_WID_UNO_CONDXML, cppu::UnoType<sheet::XSheetConditionalEntries>::get(), 0, 0 }, + { SC_UNONAME_COPYBACK, SC_WID_UNO_COPYBACK,cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_COPYFORM, SC_WID_UNO_COPYFORM,cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_COPYSTYL, SC_WID_UNO_COPYSTYL,cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_DIAGONAL_BLTR, ATTR_BORDER_BLTR, ::cppu::UnoType<table::BorderLine>::get(), 0, 0 | CONVERT_TWIPS }, + { SC_UNONAME_DIAGONAL_BLTR2, ATTR_BORDER_BLTR, ::cppu::UnoType<table::BorderLine2>::get(), 0, 0 | CONVERT_TWIPS }, + { SC_UNONAME_DIAGONAL_TLBR, ATTR_BORDER_TLBR, ::cppu::UnoType<table::BorderLine>::get(), 0, 0 | CONVERT_TWIPS }, + { SC_UNONAME_DIAGONAL_TLBR2, ATTR_BORDER_TLBR, ::cppu::UnoType<table::BorderLine2>::get(), 0, 0 | CONVERT_TWIPS }, + { SC_UNONAME_CELLHJUS, ATTR_HOR_JUSTIFY, cppu::UnoType<table::CellHoriJustify>::get(), 0, MID_HORJUST_HORJUST }, + { SC_UNONAME_CELLHJUS_METHOD, ATTR_HOR_JUSTIFY_METHOD, ::cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { SC_UNONAME_ISACTIVE, SC_WID_UNO_ISACTIVE,cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_CELLTRAN, ATTR_BACKGROUND, cppu::UnoType<bool>::get(), 0, MID_GRAPHIC_TRANSPARENT }, + { SC_UNONAME_WRAP, ATTR_LINEBREAK, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_CELLVIS, SC_WID_UNO_CELLVIS, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNO_LINKDISPBIT, SC_WID_UNO_LINKDISPBIT,cppu::UnoType<awt::XBitmap>::get(), 0 | beans::PropertyAttribute::READONLY, 0 }, + { SC_UNO_LINKDISPNAME, SC_WID_UNO_LINKDISPNAME,cppu::UnoType<OUString>::get(), 0 | beans::PropertyAttribute::READONLY, 0 }, + { SC_UNONAME_NUMFMT, ATTR_VALUE_FORMAT, cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { SC_UNONAME_NUMRULES, SC_WID_UNO_NUMRULES,cppu::UnoType<container::XIndexReplace>::get(), 0, 0 }, + { SC_UNONAME_CELLORI, ATTR_STACKED, cppu::UnoType<table::CellOrientation>::get(), 0, 0 }, + { SC_UNONAME_PAGESTL, SC_WID_UNO_PAGESTL, cppu::UnoType<OUString>::get(), 0, 0 }, + { SC_UNONAME_PADJUST, ATTR_HOR_JUSTIFY, ::cppu::UnoType<sal_Int16>::get(), 0, MID_HORJUST_ADJUST }, + { SC_UNONAME_PBMARGIN, ATTR_MARGIN, cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_LO_MARGIN | CONVERT_TWIPS }, + { SC_UNONAME_PINDENT, ATTR_INDENT, cppu::UnoType<sal_Int16>::get(), 0, 0 }, //! CONVERT_TWIPS + { SC_UNONAME_PISCHDIST,ATTR_SCRIPTSPACE, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_PISFORBID,ATTR_FORBIDDEN_RULES,cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_PISHANG, ATTR_HANGPUNCTUATION,cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_PISHYPHEN,ATTR_HYPHENATE, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_PLASTADJ, ATTR_HOR_JUSTIFY, ::cppu::UnoType<sal_Int16>::get(), 0, MID_HORJUST_ADJUST }, + { SC_UNONAME_PLMARGIN, ATTR_MARGIN, cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_L_MARGIN | CONVERT_TWIPS }, + { SC_UNONAME_PRMARGIN, ATTR_MARGIN, cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_R_MARGIN | CONVERT_TWIPS }, + { SC_UNONAME_PTMARGIN, ATTR_MARGIN, cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_UP_MARGIN | CONVERT_TWIPS }, + { SC_UNONAME_POS, SC_WID_UNO_POS, cppu::UnoType<awt::Point>::get(), 0 | beans::PropertyAttribute::READONLY, 0 }, + { SC_UNONAME_PRINTBORD,SC_WID_UNO_PRINTBORD,cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_PROTECT, SC_WID_UNO_PROTECT, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_ROTANG, ATTR_ROTATE_VALUE, cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { SC_UNONAME_ROTREF, ATTR_ROTATE_MODE, cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { SC_UNONAME_SHADOW, ATTR_SHADOW, cppu::UnoType<table::ShadowFormat>::get(), 0, 0 | CONVERT_TWIPS }, + { SC_UNONAME_SHOWBORD, SC_WID_UNO_SHOWBORD,cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_SHRINK_TO_FIT, ATTR_SHRINKTOFIT, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_SIZE, SC_WID_UNO_SIZE, cppu::UnoType<awt::Size>::get(), 0 | beans::PropertyAttribute::READONLY, 0 }, + { SC_UNONAME_TBLBORD, SC_WID_UNO_TBLBORD, cppu::UnoType<table::TableBorder>::get(), 0, 0 | CONVERT_TWIPS }, + { SC_UNONAME_TBLBORD2, SC_WID_UNO_TBLBORD2, cppu::UnoType<table::TableBorder2>::get(), 0, 0 | CONVERT_TWIPS }, + { SC_UNONAME_TABLAYOUT,SC_WID_UNO_TABLAYOUT,cppu::UnoType<sal_Int16>::get(), 0, 0 }, + { SC_UNONAME_CONDFORMAT, SC_WID_UNO_CONDFORMAT, cppu::UnoType<sheet::XConditionalFormats>::get(), 0, 0}, + { SC_UNONAME_USERDEF, ATTR_USERDEF, cppu::UnoType<container::XNameContainer>::get(), 0, 0 }, + { SC_UNONAME_VALIDAT, SC_WID_UNO_VALIDAT, cppu::UnoType<beans::XPropertySet>::get(), 0, 0 }, + { SC_UNONAME_VALILOC, SC_WID_UNO_VALILOC, cppu::UnoType<beans::XPropertySet>::get(), 0, 0 }, + { SC_UNONAME_VALIXML, SC_WID_UNO_VALIXML, cppu::UnoType<beans::XPropertySet>::get(), 0, 0 }, + { SC_UNONAME_CELLVJUS, ATTR_VER_JUSTIFY, cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { SC_UNONAME_CELLVJUS_METHOD, ATTR_VER_JUSTIFY_METHOD, ::cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { SC_UNONAME_WRITING, ATTR_WRITINGDIR, cppu::UnoType<sal_Int16>::get(), 0, 0 }, + { SC_UNONAME_TABCOLOR, SC_WID_UNO_TABCOLOR, cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { SC_UNO_CODENAME, SC_WID_UNO_CODENAME, cppu::UnoType<OUString>::get(), 0, 0}, + { SC_UNO_NAMEDRANGES, SC_WID_UNO_NAMES, cppu::UnoType<sheet::XNamedRanges>::get(), 0, 0 }, + }; + static SfxItemPropertySet aSheetPropertySet( aSheetPropertyMap_Impl ); + return &aSheetPropertySet; +} + +static std::span<const SfxItemPropertyMapEntry> lcl_GetEditPropertyMap() +{ + static const SfxItemPropertyMapEntry aEditPropertyMap_Impl[] = + { + SVX_UNOEDIT_CHAR_PROPERTIES, + SVX_UNOEDIT_FONT_PROPERTIES, + SVX_UNOEDIT_PARA_PROPERTIES, + SVX_UNOEDIT_NUMBERING_PROPERTY, // for completeness of service ParagraphProperties + { SC_UNONAME_TEXTUSER, EE_CHAR_XMLATTRIBS, cppu::UnoType<container::XNameContainer>::get(), 0, 0}, + { SC_UNONAME_USERDEF, EE_PARA_XMLATTRIBS, cppu::UnoType<container::XNameContainer>::get(), 0, 0}, + }; + return aEditPropertyMap_Impl; +} +static const SvxItemPropertySet* lcl_GetEditPropertySet() +{ + static SvxItemPropertySet aEditPropertySet( lcl_GetEditPropertyMap(), SdrObject::GetGlobalDrawObjectItemPool() ); + return &aEditPropertySet; +} + +constexpr OUString SCCHARPROPERTIES_SERVICE = u"com.sun.star.style.CharacterProperties"_ustr; +constexpr OUString SCPARAPROPERTIES_SERVICE = u"com.sun.star.style.ParagraphProperties"_ustr; +constexpr OUString SCCELLPROPERTIES_SERVICE = u"com.sun.star.table.CellProperties"_ustr; +constexpr OUString SCCELLRANGE_SERVICE = u"com.sun.star.table.CellRange"_ustr; +constexpr OUString SCCELL_SERVICE = u"com.sun.star.table.Cell"_ustr; +constexpr OUString SCSHEETCELLRANGES_SERVICE = u"com.sun.star.sheet.SheetCellRanges"_ustr; +constexpr OUString SCSHEETCELLRANGE_SERVICE = u"com.sun.star.sheet.SheetCellRange"_ustr; +constexpr OUString SCSPREADSHEET_SERVICE = u"com.sun.star.sheet.Spreadsheet"_ustr; +constexpr OUString SCSHEETCELL_SERVICE = u"com.sun.star.sheet.SheetCell"_ustr; + +SC_SIMPLE_SERVICE_INFO( ScCellFormatsEnumeration, "ScCellFormatsEnumeration", "com.sun.star.sheet.CellFormatRangesEnumeration" ) +SC_SIMPLE_SERVICE_INFO( ScCellFormatsObj, "ScCellFormatsObj", "com.sun.star.sheet.CellFormatRanges" ) +SC_SIMPLE_SERVICE_INFO( ScUniqueCellFormatsEnumeration, "ScUniqueCellFormatsEnumeration", "com.sun.star.sheet.UniqueCellFormatRangesEnumeration" ) +SC_SIMPLE_SERVICE_INFO( ScUniqueCellFormatsObj, "ScUniqueCellFormatsObj", "com.sun.star.sheet.UniqueCellFormatRanges" ) +SC_SIMPLE_SERVICE_INFO( ScCellRangesBase, "ScCellRangesBase", "stardiv.unknown" ) +SC_SIMPLE_SERVICE_INFO( ScCellsEnumeration, "ScCellsEnumeration", "com.sun.star.sheet.CellsEnumeration" ) +SC_SIMPLE_SERVICE_INFO( ScCellsObj, "ScCellsObj", "com.sun.star.sheet.Cells" ) +SC_SIMPLE_SERVICE_INFO( ScTableColumnObj, "ScTableColumnObj", "com.sun.star.table.TableColumn" ) +SC_SIMPLE_SERVICE_INFO( ScTableRowObj, "ScTableRowObj", "com.sun.star.table.TableRow" ) + +//! move ScLinkListener into another file !!! + +ScLinkListener::~ScLinkListener() +{ +} + +void ScLinkListener::Notify( const SfxHint& rHint ) +{ + aLink.Call( rHint ); +} + +static void lcl_CopyProperties( beans::XPropertySet& rDest, beans::XPropertySet& rSource ) +{ + uno::Reference<beans::XPropertySetInfo> xInfo(rSource.getPropertySetInfo()); + if (xInfo.is()) + { + const uno::Sequence<beans::Property> aSeq(xInfo->getProperties()); + for (const beans::Property& rProp : aSeq) + { + OUString aName(rProp.Name); + rDest.setPropertyValue( aName, rSource.getPropertyValue( aName ) ); + } + } +} + +static SCTAB lcl_FirstTab( const ScRangeList& rRanges ) +{ + if (rRanges.empty()) + throw std::out_of_range("empty range"); + const ScRange & rFirst = rRanges[0]; + return rFirst.aStart.Tab(); +} + +static bool lcl_WholeSheet( const ScDocument& rDoc, const ScRangeList& rRanges ) +{ + if ( rRanges.size() == 1 ) + { + const ScRange & rRange = rRanges[0]; + if ( rRange.aStart.Col() == 0 && rRange.aEnd.Col() == rDoc.MaxCol() && + rRange.aStart.Row() == 0 && rRange.aEnd.Row() == rDoc.MaxRow() ) + return true; + } + return false; +} + +namespace { +template<typename BorderLineType> +const ::editeng::SvxBorderLine* lcl_getBorderLine( + ::editeng::SvxBorderLine& rLine, const BorderLineType& rStruct ) +{ + // Convert from 1/100mm to Twips. + if (!SvxBoxItem::LineToSvxLine( rStruct, rLine, true)) + return nullptr; + + if ( rLine.GetOutWidth() || rLine.GetInWidth() || rLine.GetDistance() ) + return &rLine; + else + return nullptr; +} +} + +const ::editeng::SvxBorderLine* ScHelperFunctions::GetBorderLine( + ::editeng::SvxBorderLine& rLine, const table::BorderLine& rStruct ) +{ + return lcl_getBorderLine( rLine, rStruct); +} + +const ::editeng::SvxBorderLine* ScHelperFunctions::GetBorderLine( + ::editeng::SvxBorderLine& rLine, const table::BorderLine2& rStruct ) +{ + return lcl_getBorderLine( rLine, rStruct); +} + +namespace { +template<typename TableBorderType> +void lcl_fillBoxItems( SvxBoxItem& rOuter, SvxBoxInfoItem& rInner, const TableBorderType& rBorder ) +{ + ::editeng::SvxBorderLine aLine; + rOuter.SetAllDistances(o3tl::toTwips(rBorder.Distance, o3tl::Length::mm100)); + rOuter.SetLine( ScHelperFunctions::GetBorderLine( aLine, rBorder.TopLine ), SvxBoxItemLine::TOP ); + rOuter.SetLine( ScHelperFunctions::GetBorderLine( aLine, rBorder.BottomLine ), SvxBoxItemLine::BOTTOM ); + rOuter.SetLine( ScHelperFunctions::GetBorderLine( aLine, rBorder.LeftLine ), SvxBoxItemLine::LEFT ); + rOuter.SetLine( ScHelperFunctions::GetBorderLine( aLine, rBorder.RightLine ), SvxBoxItemLine::RIGHT ); + rInner.SetLine( ScHelperFunctions::GetBorderLine( aLine, rBorder.HorizontalLine ), SvxBoxInfoItemLine::HORI ); + rInner.SetLine( ScHelperFunctions::GetBorderLine( aLine, rBorder.VerticalLine ), SvxBoxInfoItemLine::VERT ); + rInner.SetValid( SvxBoxInfoItemValidFlags::TOP, rBorder.IsTopLineValid ); + rInner.SetValid( SvxBoxInfoItemValidFlags::BOTTOM, rBorder.IsBottomLineValid ); + rInner.SetValid( SvxBoxInfoItemValidFlags::LEFT, rBorder.IsLeftLineValid ); + rInner.SetValid( SvxBoxInfoItemValidFlags::RIGHT, rBorder.IsRightLineValid ); + rInner.SetValid( SvxBoxInfoItemValidFlags::HORI, rBorder.IsHorizontalLineValid ); + rInner.SetValid( SvxBoxInfoItemValidFlags::VERT, rBorder.IsVerticalLineValid ); + rInner.SetValid( SvxBoxInfoItemValidFlags::DISTANCE, rBorder.IsDistanceValid ); + rInner.SetTable( true ); +} +} + +void ScHelperFunctions::FillBoxItems( SvxBoxItem& rOuter, SvxBoxInfoItem& rInner, const table::TableBorder& rBorder ) +{ + lcl_fillBoxItems( rOuter, rInner, rBorder); +} + +void ScHelperFunctions::FillBoxItems( SvxBoxItem& rOuter, SvxBoxInfoItem& rInner, const table::TableBorder2& rBorder ) +{ + lcl_fillBoxItems( rOuter, rInner, rBorder); +} + +void ScHelperFunctions::FillBorderLine( table::BorderLine& rStruct, const ::editeng::SvxBorderLine* pLine ) +{ + // Convert from Twips to 1/100mm. + rStruct = SvxBoxItem::SvxLineToLine( pLine, true); +} + +void ScHelperFunctions::FillBorderLine( table::BorderLine2& rStruct, const ::editeng::SvxBorderLine* pLine ) +{ + rStruct = SvxBoxItem::SvxLineToLine( pLine, true); +} + +namespace { +template<typename TableBorderItem> +void lcl_fillTableBorder( TableBorderItem& rBorder, const SvxBoxItem& rOuter, const SvxBoxInfoItem& rInner, + bool bInvalidateHorVerDist ) +{ + ScHelperFunctions::FillBorderLine( rBorder.TopLine, rOuter.GetTop() ); + ScHelperFunctions::FillBorderLine( rBorder.BottomLine, rOuter.GetBottom() ); + ScHelperFunctions::FillBorderLine( rBorder.LeftLine, rOuter.GetLeft() ); + ScHelperFunctions::FillBorderLine( rBorder.RightLine, rOuter.GetRight() ); + ScHelperFunctions::FillBorderLine( rBorder.HorizontalLine, rInner.GetHori() ); + ScHelperFunctions::FillBorderLine( rBorder.VerticalLine, rInner.GetVert() ); + + rBorder.Distance = rOuter.GetSmallestDistance(); + rBorder.IsTopLineValid = rInner.IsValid(SvxBoxInfoItemValidFlags::TOP); + rBorder.IsBottomLineValid = rInner.IsValid(SvxBoxInfoItemValidFlags::BOTTOM); + rBorder.IsLeftLineValid = rInner.IsValid(SvxBoxInfoItemValidFlags::LEFT); + rBorder.IsRightLineValid = rInner.IsValid(SvxBoxInfoItemValidFlags::RIGHT); + rBorder.IsHorizontalLineValid = !bInvalidateHorVerDist && rInner.IsValid(SvxBoxInfoItemValidFlags::HORI); + rBorder.IsVerticalLineValid = !bInvalidateHorVerDist && rInner.IsValid(SvxBoxInfoItemValidFlags::VERT); + rBorder.IsDistanceValid = !bInvalidateHorVerDist && rInner.IsValid(SvxBoxInfoItemValidFlags::DISTANCE); +} +} + +void ScHelperFunctions::AssignTableBorderToAny( uno::Any& rAny, + const SvxBoxItem& rOuter, const SvxBoxInfoItem& rInner, bool bInvalidateHorVerDist ) +{ + table::TableBorder aBorder; + lcl_fillTableBorder( aBorder, rOuter, rInner, bInvalidateHorVerDist); + rAny <<= aBorder; +} + +void ScHelperFunctions::AssignTableBorder2ToAny( uno::Any& rAny, + const SvxBoxItem& rOuter, const SvxBoxInfoItem& rInner, bool bInvalidateHorVerDist ) +{ + table::TableBorder2 aBorder; + lcl_fillTableBorder( aBorder, rOuter, rInner, bInvalidateHorVerDist); + rAny <<= aBorder; +} + +//! move lcl_ApplyBorder to docfunc ! + +void ScHelperFunctions::ApplyBorder( ScDocShell* pDocShell, const ScRangeList& rRanges, + const SvxBoxItem& rOuter, const SvxBoxInfoItem& rInner ) +{ + ScDocument& rDoc = pDocShell->GetDocument(); + bool bUndo(rDoc.IsUndoEnabled()); + ScDocumentUniquePtr pUndoDoc; + if (bUndo) + pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO )); + size_t nCount = rRanges.size(); + for (size_t i = 0; i < nCount; ++i) + { + ScRange const & rRange = rRanges[ i ]; + SCTAB nTab = rRange.aStart.Tab(); + + if (bUndo) + { + if ( i==0 ) + pUndoDoc->InitUndo( rDoc, nTab, nTab ); + else + pUndoDoc->AddUndoTab( nTab, nTab ); + rDoc.CopyToDocument(rRange, InsertDeleteFlags::ATTRIB, false, *pUndoDoc); + } + + ScMarkData aMark(rDoc.GetSheetLimits()); + aMark.SetMarkArea( rRange ); + aMark.SelectTable( nTab, true ); + + rDoc.ApplySelectionFrame(aMark, rOuter, &rInner); + // don't need RowHeight if there is only a border + } + + if (bUndo) + { + pDocShell->GetUndoManager()->AddUndoAction( + std::make_unique<ScUndoBorder>( pDocShell, rRanges, std::move(pUndoDoc), rOuter, rInner ) ); + } + + for (size_t i = 0; i < nCount; ++i ) + pDocShell->PostPaint( rRanges[ i ], PaintPartFlags::Grid, SC_PF_LINES | SC_PF_TESTMERGE ); + + pDocShell->SetDocumentModified(); +} + +//! move lcl_PutDataArray to docfunc? +//! merge loop with ScFunctionAccess::callFunction + +static bool lcl_PutDataArray( ScDocShell& rDocShell, const ScRange& rRange, + const uno::Sequence< uno::Sequence<uno::Any> >& aData ) +{ + ScDocument& rDoc = rDocShell.GetDocument(); + ScFieldEditEngine& rEngine = rDoc.GetEditEngine(); + SCTAB nTab = rRange.aStart.Tab(); + SCCOL nStartCol = rRange.aStart.Col(); + SCROW nStartRow = rRange.aStart.Row(); + SCCOL nEndCol = rRange.aEnd.Col(); + SCROW nEndRow = rRange.aEnd.Row(); + bool bUndo(rDoc.IsUndoEnabled()); + + if ( !rDoc.IsBlockEditable( nTab, nStartCol,nStartRow, nEndCol,nEndRow ) ) + { + //! error message + return false; + } + + sal_Int32 nCols = 0; + sal_Int32 nRows = aData.getLength(); + if ( nRows ) + nCols = aData[0].getLength(); + + if ( nCols != nEndCol-nStartCol+1 || nRows != nEndRow-nStartRow+1 ) + { + //! error message? + return false; + } + + ScDocumentUniquePtr pUndoDoc; + if ( bUndo ) + { + pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO )); + pUndoDoc->InitUndo( rDoc, nTab, nTab ); + rDoc.CopyToDocument(rRange, InsertDeleteFlags::CONTENTS|InsertDeleteFlags::NOCAPTIONS, false, *pUndoDoc); + } + + rDoc.DeleteAreaTab( nStartCol, nStartRow, nEndCol, nEndRow, nTab, InsertDeleteFlags::CONTENTS ); + + bool bError = false; + SCROW nDocRow = nStartRow; + for (const uno::Sequence<uno::Any>& rColSeq : aData) + { + if ( rColSeq.getLength() == nCols ) + { + SCCOL nDocCol = nStartCol; + for (const uno::Any& rElement : rColSeq) + { + ScAddress aPos(nDocCol, nDocRow, nTab); + + switch( rElement.getValueTypeClass() ) + { + case uno::TypeClass_VOID: + { + // void = "no value" + rDoc.SetError( nDocCol, nDocRow, nTab, FormulaError::NotAvailable ); + } + break; + + // #87871# accept integer types because Basic passes a floating point + // variable as byte, short or long if it's an integer number. + case uno::TypeClass_BYTE: + case uno::TypeClass_SHORT: + case uno::TypeClass_UNSIGNED_SHORT: + case uno::TypeClass_LONG: + case uno::TypeClass_UNSIGNED_LONG: + case uno::TypeClass_FLOAT: + case uno::TypeClass_DOUBLE: + { + double fVal(0.0); + rElement >>= fVal; + rDoc.SetValue(aPos, fVal); + } + break; + + case uno::TypeClass_STRING: + { + OUString aUStr; + rElement >>= aUStr; + if ( !aUStr.isEmpty() ) + { + // tdf#146454 - check for a multiline string since setting an edit + // or string cell is in magnitudes slower than setting a plain string + if (ScStringUtil::isMultiline(aUStr)) + { + rEngine.SetTextCurrentDefaults(aUStr); + rDoc.SetEditText(aPos, rEngine.CreateTextObject()); + } + else + { + ScSetStringParam aParam; + aParam.setTextInput(); + rDoc.SetString(aPos, aUStr, &aParam); + } + } + } + break; + + // accept Sequence<FormulaToken> for formula cells + case uno::TypeClass_SEQUENCE: + { + uno::Sequence< sheet::FormulaToken > aTokens; + if ( rElement >>= aTokens ) + { + ScTokenArray aTokenArray(rDoc); + ScTokenConversion::ConvertToTokenArray( rDoc, aTokenArray, aTokens ); + rDoc.SetFormula(aPos, aTokenArray); + } + else + bError = true; + } + break; + + default: + bError = true; // invalid type + } + ++nDocCol; + } + } + else + bError = true; // wrong size + + ++nDocRow; + } + + bool bHeight = rDocShell.AdjustRowHeight( nStartRow, nEndRow, nTab ); + + if ( pUndoDoc ) + { + ScMarkData aDestMark(rDoc.GetSheetLimits()); + aDestMark.SelectOneTable( nTab ); + rDocShell.GetUndoManager()->AddUndoAction( + std::make_unique<ScUndoPaste>( + &rDocShell, ScRange(nStartCol, nStartRow, nTab, nEndCol, nEndRow, nTab), + aDestMark, std::move(pUndoDoc), nullptr, InsertDeleteFlags::CONTENTS, nullptr, false)); + } + + if (!bHeight) + rDocShell.PostPaint( rRange, PaintPartFlags::Grid ); // AdjustRowHeight may have painted already + + rDocShell.SetDocumentModified(); + + return !bError; +} + +static bool lcl_PutFormulaArray( ScDocShell& rDocShell, const ScRange& rRange, + const uno::Sequence< uno::Sequence<OUString> >& aData, + const formula::FormulaGrammar::Grammar eGrammar ) +{ + ScDocument& rDoc = rDocShell.GetDocument(); + SCTAB nTab = rRange.aStart.Tab(); + SCCOL nStartCol = rRange.aStart.Col(); + SCROW nStartRow = rRange.aStart.Row(); + SCCOL nEndCol = rRange.aEnd.Col(); + SCROW nEndRow = rRange.aEnd.Row(); + bool bUndo(rDoc.IsUndoEnabled()); + + if ( !rDoc.IsBlockEditable( nTab, nStartCol,nStartRow, nEndCol,nEndRow ) ) + { + //! error message + return false; + } + + sal_Int32 nCols = 0; + sal_Int32 nRows = aData.getLength(); + if ( nRows ) + nCols = aData[0].getLength(); + + if ( nCols != nEndCol-nStartCol+1 || nRows != nEndRow-nStartRow+1 ) + { + //! error message? + return false; + } + + ScDocumentUniquePtr pUndoDoc; + if ( bUndo ) + { + pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO )); + pUndoDoc->InitUndo( rDoc, nTab, nTab ); + rDoc.CopyToDocument(rRange, InsertDeleteFlags::CONTENTS, false, *pUndoDoc); + } + + rDoc.DeleteAreaTab( nStartCol, nStartRow, nEndCol, nEndRow, nTab, InsertDeleteFlags::CONTENTS ); + + bool bError = false; + SCROW nDocRow = nStartRow; + for (const uno::Sequence<OUString>& rColSeq : aData) + { + if ( rColSeq.getLength() == nCols ) + { + SCCOL nDocCol = nStartCol; + for (const OUString& aText : rColSeq) + { + ScAddress aPos( nDocCol, nDocRow, nTab ); + + ScInputStringType aRes = + ScStringUtil::parseInputString( + *rDoc.GetFormatTable(), aText, LANGUAGE_ENGLISH_US); + switch (aRes.meType) + { + case ScInputStringType::Formula: + rDoc.SetFormula(aPos, aRes.maText, eGrammar); + break; + case ScInputStringType::Number: + rDoc.SetValue(aPos, aRes.mfValue); + break; + case ScInputStringType::Text: + rDoc.SetTextCell(aPos, aRes.maText); + break; + default: + ; + } + + ++nDocCol; + } + } + else + bError = true; // wrong size + + ++nDocRow; + } + + bool bHeight = rDocShell.AdjustRowHeight( nStartRow, nEndRow, nTab ); + + if ( pUndoDoc ) + { + ScMarkData aDestMark(rDoc.GetSheetLimits()); + aDestMark.SelectOneTable( nTab ); + rDocShell.GetUndoManager()->AddUndoAction( + std::make_unique<ScUndoPaste>( &rDocShell, + ScRange(nStartCol, nStartRow, nTab, nEndCol, nEndRow, nTab), aDestMark, + std::move(pUndoDoc), nullptr, InsertDeleteFlags::CONTENTS, nullptr, false)); + } + + if (!bHeight) + rDocShell.PostPaint( rRange, PaintPartFlags::Grid ); // AdjustRowHeight may have painted already + + rDocShell.SetDocumentModified(); + + return !bError; +} + +// used in ScCellRangeObj::getFormulaArray and ScCellObj::GetInputString_Impl +static OUString lcl_GetInputString( ScDocument& rDoc, const ScAddress& rPos, bool bEnglish ) +{ + ScRefCellValue aCell(rDoc, rPos); + if (aCell.isEmpty()) + return OUString(); + + OUString aVal; + + CellType eType = aCell.getType(); + if (eType == CELLTYPE_FORMULA) + { + ScFormulaCell* pForm = aCell.getFormula(); + return pForm->GetFormula( formula::FormulaGrammar::mapAPItoGrammar( bEnglish, false)); + } + + SvNumberFormatter* pFormatter = bEnglish ? ScGlobal::GetEnglishFormatter() : + rDoc.GetFormatTable(); + // Since the English formatter was constructed with + // LANGUAGE_ENGLISH_US the "General" format has index key 0, + // we don't have to query. + sal_uInt32 nNumFmt = bEnglish ? 0 : rDoc.GetNumberFormat(rPos); + + if (eType == CELLTYPE_EDIT) + { + // GetString on EditCell turns breaks into spaces, + // but we need the breaks here + const EditTextObject* pData = aCell.getEditText(); + if (pData) + { + EditEngine& rEngine = rDoc.GetEditEngine(); + rEngine.SetText(*pData); + aVal = rEngine.GetText(); + } + } + else + aVal = ScCellFormat::GetInputString(aCell, nNumFmt, *pFormatter, rDoc); + + // if applicable, prepend ' like in ScTabViewShell::UpdateInputHandler + if ( eType == CELLTYPE_STRING || eType == CELLTYPE_EDIT ) + { + double fDummy; + OUString aTempString = aVal; + bool bIsNumberFormat(pFormatter->IsNumberFormat(aTempString, nNumFmt, fDummy)); + if ( bIsNumberFormat ) + aTempString = "'" + aTempString; + else if ( aTempString.startsWith("'") ) + { + // if the string starts with a "'", add another one because setFormula + // strips one (like text input, except for "text" number formats) + if ( bEnglish || ( pFormatter->GetType(nNumFmt) != SvNumFormatType::TEXT ) ) + aTempString = "'" + aTempString; + } + aVal = aTempString; + } + return aVal; +} + +ScCellRangesBase::ScCellRangesBase(ScDocShell* pDocSh, const ScRange& rR) : + pPropSet(lcl_GetCellsPropertySet()), + pDocShell( pDocSh ), + nObjectId( 0 ), + bChartColAsHdr( false ), + bChartRowAsHdr( false ), + bCursorOnly( false ), + bGotDataChangedHint( false ), + aValueListeners( 0 ) +{ + ScRange aCellRange(rR); + aCellRange.PutInOrder(); + aRanges.push_back( aCellRange ); + + if (pDocShell) // Null if created with createInstance + { + ScDocument& rDoc = pDocShell->GetDocument(); + rDoc.AddUnoObject(*this); + nObjectId = rDoc.GetNewUnoId(); + } +} + +ScCellRangesBase::ScCellRangesBase(ScDocShell* pDocSh, ScRangeList aR) : + pPropSet(lcl_GetCellsPropertySet()), + pDocShell( pDocSh ), + aRanges(std::move( aR )), + nObjectId( 0 ), + bChartColAsHdr( false ), + bChartRowAsHdr( false ), + bCursorOnly( false ), + bGotDataChangedHint( false ), + aValueListeners( 0 ) +{ + if (pDocShell) // Null if created with createInstance + { + ScDocument& rDoc = pDocShell->GetDocument(); + rDoc.AddUnoObject(*this); + nObjectId = rDoc.GetNewUnoId(); + } +} + +ScCellRangesBase::~ScCellRangesBase() +{ + SolarMutexGuard g; + + // call RemoveUnoObject first, so no notification can happen + // during ForgetCurrentAttrs + + if (pDocShell) + pDocShell->GetDocument().RemoveUnoObject(*this); + + ForgetCurrentAttrs(); + ForgetMarkData(); + + pValueListener.reset(); + + //! unregister XChartDataChangeEventListener ?? + //! (ChartCollection will then hold this object as well!) +} + +void ScCellRangesBase::ForgetCurrentAttrs() +{ + pCurrentFlat.reset(); + pCurrentDeep.reset(); + moCurrentDataSet.reset(); + moNoDfltCurrentDataSet.reset(); + + // #i62483# pMarkData can remain unchanged, is deleted only if the range changes (RefChanged) +} + +void ScCellRangesBase::ForgetMarkData() +{ + pMarkData.reset(); +} + +const ScPatternAttr* ScCellRangesBase::GetCurrentAttrsFlat() +{ + // get and cache direct cell attributes for this object's range + + if ( !pCurrentFlat && pDocShell ) + { + ScDocument& rDoc = pDocShell->GetDocument(); + pCurrentFlat = rDoc.CreateSelectionPattern( *GetMarkData(), false ); + } + return pCurrentFlat.get(); +} + +const ScPatternAttr* ScCellRangesBase::GetCurrentAttrsDeep() +{ + // get and cache cell attributes (incl. styles) for this object's range + + if ( !pCurrentDeep && pDocShell ) + { + ScDocument& rDoc = pDocShell->GetDocument(); + pCurrentDeep = rDoc.CreateSelectionPattern( *GetMarkData() ); + } + return pCurrentDeep.get(); +} + +SfxItemSet* ScCellRangesBase::GetCurrentDataSet(bool bNoDflt) +{ + if(!moCurrentDataSet) + { + const ScPatternAttr* pPattern = GetCurrentAttrsDeep(); + if ( pPattern ) + { + // replace Dontcare with Default, so that we always have a reflection + moCurrentDataSet.emplace( pPattern->GetItemSet() ); + moNoDfltCurrentDataSet.emplace( pPattern->GetItemSet() ); + moCurrentDataSet->ClearInvalidItems(); + } + } + if (bNoDflt) + { + if (moNoDfltCurrentDataSet) + return &*moNoDfltCurrentDataSet; + } + else + { + if (moCurrentDataSet) + return &*moCurrentDataSet; + } + return nullptr; +} + +const ScMarkData* ScCellRangesBase::GetMarkData() +{ + if (!pMarkData) + { + pMarkData.reset( new ScMarkData(GetDocument()->GetSheetLimits(), aRanges) ); + } + return pMarkData.get(); +} + +void ScCellRangesBase::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + const SfxHintId nId = rHint.GetId(); + if ( nId == SfxHintId::Dying ) + { + // if the document dies, must reset to avoid crash in dtor! + ForgetCurrentAttrs(); + pDocShell = nullptr; // invalid + + // fdo#72695: if UNO object is already dead, don't revive it with event + if ( m_refCount > 0 && !aValueListeners.empty() ) + { + // dispose listeners + + lang::EventObject aEvent; + aEvent.Source = getXWeak(); + for (uno::Reference<util::XModifyListener> & xValueListener : aValueListeners) + xValueListener->disposing( aEvent ); + + aValueListeners.clear(); + + // The listeners can't have the last ref to this, as it's still held + // by the DocShell. + } + } + else if ( nId == SfxHintId::DataChanged ) + { + // document content changed -> forget cached attributes + ForgetCurrentAttrs(); + + if ( bGotDataChangedHint && pDocShell ) + { + // This object was notified of content changes, so one call + // for each listener is generated now. + // The calls can't be executed directly because the document's + // UNO broadcaster list must not be modified. + // Instead, add to the document's list of listener calls, + // which will be executed directly after the broadcast of + // SfxHintId::DataChanged. + + lang::EventObject aEvent; + aEvent.Source = getXWeak(); + + // the EventObject holds a Ref to this object until after the listener calls + + ScDocument& rDoc = pDocShell->GetDocument(); + for (const uno::Reference<util::XModifyListener> & xValueListener : aValueListeners) + rDoc.AddUnoListenerCall( xValueListener, aEvent ); + + bGotDataChangedHint = false; + } + } + else if ( nId == SfxHintId::ScCalcAll ) + { + // broadcast from DoHardRecalc - set bGotDataChangedHint + // (SfxHintId::DataChanged follows separately) + + if ( !aValueListeners.empty() ) + bGotDataChangedHint = true; + } + else if ( auto pRefHint = dynamic_cast<const ScUpdateRefHint*>(&rHint) ) + { + ScDocument& rDoc = pDocShell->GetDocument(); + std::unique_ptr<ScRangeList> pUndoRanges; + if ( rDoc.HasUnoRefUndo() ) + pUndoRanges.reset(new ScRangeList( aRanges )); + + if ( aRanges.UpdateReference( pRefHint->GetMode(), &rDoc, pRefHint->GetRange(), + pRefHint->GetDx(), pRefHint->GetDy(), pRefHint->GetDz() ) ) + { + if ( pRefHint->GetMode() == URM_INSDEL + && aRanges.size() == 1 + && dynamic_cast<ScTableSheetObj*>(this) + ) + { + // #101755#; the range size of a sheet does not change + ScRange & rR = aRanges.front(); + rR.aStart.SetCol(0); + rR.aStart.SetRow(0); + rR.aEnd.SetCol(rDoc.MaxCol()); + rR.aEnd.SetRow(rDoc.MaxRow()); + } + RefChanged(); + + // any change of the range address is broadcast to value (modify) listeners + if ( !aValueListeners.empty() ) + bGotDataChangedHint = true; + + if ( pUndoRanges ) + rDoc.AddUnoRefChange( nObjectId, *pUndoRanges ); + } + } + else if ( auto pUndoHint = dynamic_cast<const ScUnoRefUndoHint*>(&rHint) ) + { + if ( pUndoHint->GetObjectId() == nObjectId ) + { + // restore ranges from hint + + aRanges = pUndoHint->GetRanges(); + + RefChanged(); + if ( !aValueListeners.empty() ) + bGotDataChangedHint = true; // need to broadcast the undo, too + } + } +} + +void ScCellRangesBase::RefChanged() +{ + //! adjust XChartDataChangeEventListener + + if ( pValueListener && !aValueListeners.empty() ) + { + pValueListener->EndListeningAll(); + + ScDocument& rDoc = pDocShell->GetDocument(); + for ( size_t i = 0, nCount = aRanges.size(); i < nCount; ++i ) + rDoc.StartListeningArea( aRanges[ i ], false, pValueListener.get() ); + } + + ForgetCurrentAttrs(); + ForgetMarkData(); +} + +ScDocument* ScCellRangesBase::GetDocument() const +{ + if (pDocShell) + return &pDocShell->GetDocument(); + else + return nullptr; +} + +void ScCellRangesBase::InitInsertRange(ScDocShell* pDocSh, const ScRange& rR) +{ + if ( pDocShell || !pDocSh ) + return; + + pDocShell = pDocSh; + + ScRange aCellRange(rR); + aCellRange.PutInOrder(); + aRanges.RemoveAll(); + aRanges.push_back( aCellRange ); + + pDocShell->GetDocument().AddUnoObject(*this); + + RefChanged(); // adjust range in range object +} + +void ScCellRangesBase::AddRange(const ScRange& rRange, const bool bMergeRanges) +{ + if (bMergeRanges) + aRanges.Join(rRange); + else + aRanges.push_back(rRange); + RefChanged(); +} + +void ScCellRangesBase::SetNewRange(const ScRange& rNew) +{ + ScRange aCellRange(rNew); + aCellRange.PutInOrder(); + + aRanges.RemoveAll(); + aRanges.push_back( aCellRange ); + RefChanged(); +} + +void ScCellRangesBase::SetNewRanges(const ScRangeList& rNew) +{ + aRanges = rNew; + RefChanged(); +} + +void ScCellRangesBase::SetCursorOnly( bool bSet ) +{ + // set for a selection object that is created from the cursor position + // without anything selected (may contain several sheets) + + bCursorOnly = bSet; +} + +void ScCellRangesBase::PaintGridRanges_Impl( ) +{ + for (size_t i = 0, nCount = aRanges.size(); i < nCount; ++i) + pDocShell->PostPaint( aRanges[ i ], PaintPartFlags::Grid ); +} + +// XSheetOperation + +double SAL_CALL ScCellRangesBase::computeFunction( sheet::GeneralFunction nFunction ) +{ + SolarMutexGuard aGuard; + ScMarkData aMark(*GetMarkData()); + aMark.MarkToSimple(); + if (!aMark.IsMarked()) + aMark.SetMarkNegative(true); // so we can enter dummy position + + ScAddress aDummy; // if not marked, ignored if it is negative + double fVal; + ScSubTotalFunc eFunc = ScDPUtil::toSubTotalFunc(static_cast<ScGeneralFunction>(nFunction)); + ScDocument& rDoc = pDocShell->GetDocument(); + if ( !rDoc.GetSelectionFunction( eFunc, aDummy, aMark, fVal ) ) + { + throw uno::RuntimeException(); //! own exception? + } + + return fVal; +} + +void SAL_CALL ScCellRangesBase::clearContents( sal_Int32 nContentFlags ) +{ + SolarMutexGuard aGuard; + if ( !aRanges.empty() ) + { + // only for clearContents: EDITATTR is only used if no contents are deleted + InsertDeleteFlags nDelFlags = static_cast<InsertDeleteFlags>(nContentFlags) & InsertDeleteFlags::ALL; + if ( ( nDelFlags & InsertDeleteFlags::EDITATTR ) && ( nDelFlags & InsertDeleteFlags::CONTENTS ) == InsertDeleteFlags::NONE ) + nDelFlags |= InsertDeleteFlags::EDITATTR; + + pDocShell->GetDocFunc().DeleteContents( *GetMarkData(), nDelFlags, true, true ); + } + // otherwise nothing to do +} + +// XPropertyState + +const SfxItemPropertyMap& ScCellRangesBase::GetItemPropertyMap() +{ + return pPropSet->getPropertyMap(); +} + +static void lcl_GetPropertyWhich( const SfxItemPropertyMapEntry* pEntry, + sal_uInt16& rItemWhich ) +{ + // Which-ID of the affected items also when the item can't handle + // the property by itself + if ( !pEntry ) + return; + + if ( IsScItemWid( pEntry->nWID ) ) + rItemWhich = pEntry->nWID; + else + switch ( pEntry->nWID ) + { + case SC_WID_UNO_TBLBORD: + case SC_WID_UNO_TBLBORD2: + rItemWhich = ATTR_BORDER; + break; + case SC_WID_UNO_CONDFMT: + case SC_WID_UNO_CONDLOC: + case SC_WID_UNO_CONDXML: + rItemWhich = ATTR_CONDITIONAL; + break; + case SC_WID_UNO_VALIDAT: + case SC_WID_UNO_VALILOC: + case SC_WID_UNO_VALIXML: + rItemWhich = ATTR_VALIDDATA; + break; + } + +} + +beans::PropertyState ScCellRangesBase::GetOnePropertyState( sal_uInt16 nItemWhich, const SfxItemPropertyMapEntry* pEntry ) +{ + beans::PropertyState eRet = beans::PropertyState_DIRECT_VALUE; + if ( nItemWhich ) // item wid (from map or special case) + { + // For items that contain several properties (like background), + // "ambiguous" is returned too often here + + // for PropertyState, don't look at styles + const ScPatternAttr* pPattern = GetCurrentAttrsFlat(); + if ( pPattern ) + { + SfxItemState eState = pPattern->GetItemSet().GetItemState( nItemWhich, false ); + + if ( nItemWhich == ATTR_VALUE_FORMAT && eState == SfxItemState::DEFAULT ) + eState = pPattern->GetItemSet().GetItemState( ATTR_LANGUAGE_FORMAT, false ); + + if ( eState == SfxItemState::SET ) + eRet = beans::PropertyState_DIRECT_VALUE; + else if ( eState == SfxItemState::DEFAULT ) + eRet = beans::PropertyState_DEFAULT_VALUE; + else if ( eState == SfxItemState::DONTCARE ) + eRet = beans::PropertyState_AMBIGUOUS_VALUE; + else + { + OSL_FAIL("unknown ItemState"); + } + } + } + else if ( pEntry ) + { + if ( pEntry->nWID == SC_WID_UNO_CHCOLHDR || pEntry->nWID == SC_WID_UNO_CHROWHDR || pEntry->nWID == SC_WID_UNO_ABSNAME ) + eRet = beans::PropertyState_DIRECT_VALUE; + else if ( pEntry->nWID == SC_WID_UNO_CELLSTYL ) + { + // a style is always set, there's no default state + const ScStyleSheet* pStyle = pDocShell->GetDocument().GetSelectionStyle(*GetMarkData()); + if (pStyle) + eRet = beans::PropertyState_DIRECT_VALUE; + else + eRet = beans::PropertyState_AMBIGUOUS_VALUE; + } + else if ( pEntry->nWID == SC_WID_UNO_NUMRULES ) + eRet = beans::PropertyState_DEFAULT_VALUE; // numbering rules are always default + } + return eRet; +} + +beans::PropertyState SAL_CALL ScCellRangesBase::getPropertyState( const OUString& aPropertyName ) +{ + SolarMutexGuard aGuard; + if ( aRanges.empty() ) + throw uno::RuntimeException(); + + const SfxItemPropertyMap& rMap = GetItemPropertyMap(); // from derived class + sal_uInt16 nItemWhich = 0; + const SfxItemPropertyMapEntry* pEntry = rMap.getByName( aPropertyName ); + lcl_GetPropertyWhich( pEntry, nItemWhich ); + return GetOnePropertyState( nItemWhich, pEntry ); +} + +uno::Sequence<beans::PropertyState> SAL_CALL ScCellRangesBase::getPropertyStates( + const uno::Sequence<OUString>& aPropertyNames ) +{ + SolarMutexGuard aGuard; + + const SfxItemPropertyMap& rPropertyMap = GetItemPropertyMap(); // from derived class + + uno::Sequence<beans::PropertyState> aRet(aPropertyNames.getLength()); + std::transform(aPropertyNames.begin(), aPropertyNames.end(), aRet.getArray(), + [this, &rPropertyMap](const auto& rName) -> beans::PropertyState { + sal_uInt16 nItemWhich = 0; + const SfxItemPropertyMapEntry* pEntry = rPropertyMap.getByName( rName ); + lcl_GetPropertyWhich( pEntry, nItemWhich ); + return GetOnePropertyState(nItemWhich, pEntry); + }); + return aRet; +} + +void SAL_CALL ScCellRangesBase::setPropertyToDefault( const OUString& aPropertyName ) +{ + SolarMutexGuard aGuard; + if ( !pDocShell ) + return; + + const SfxItemPropertyMap& rPropertyMap = GetItemPropertyMap(); // from derived class + sal_uInt16 nItemWhich = 0; + const SfxItemPropertyMapEntry* pEntry = rPropertyMap.getByName( aPropertyName ); + lcl_GetPropertyWhich( pEntry, nItemWhich ); + if ( nItemWhich ) // item wid (from map or special case) + { + if ( !aRanges.empty() ) // empty = nothing to do + { + //! for items that have multiple properties (e.g. background) + //! too much will be reset + //! for ATTR_ROTATE_VALUE, reset ATTR_ORIENTATION as well? + + sal_uInt16 aWIDs[3]; + aWIDs[0] = nItemWhich; + if ( nItemWhich == ATTR_VALUE_FORMAT ) + { + aWIDs[1] = ATTR_LANGUAGE_FORMAT; // language for number formats + aWIDs[2] = 0; + } + else + aWIDs[1] = 0; + pDocShell->GetDocFunc().ClearItems( *GetMarkData(), aWIDs, true ); + } + } + else if ( pEntry ) + { + if ( pEntry->nWID == SC_WID_UNO_CHCOLHDR ) + bChartColAsHdr = false; + else if ( pEntry->nWID == SC_WID_UNO_CHROWHDR ) + bChartRowAsHdr = false; + else if ( pEntry->nWID == SC_WID_UNO_CELLSTYL ) + { + OUString aStyleName( ScResId( STR_STYLENAME_STANDARD ) ); + pDocShell->GetDocFunc().ApplyStyle( *GetMarkData(), aStyleName, true ); + } + } +} + +uno::Any SAL_CALL ScCellRangesBase::getPropertyDefault( const OUString& aPropertyName ) +{ + //! bundle with getPropertyValue + + SolarMutexGuard aGuard; + uno::Any aAny; + + if ( pDocShell ) + { + ScDocument& rDoc = pDocShell->GetDocument(); + const SfxItemPropertyMap& rPropertyMap = GetItemPropertyMap(); // from derived class + const SfxItemPropertyMapEntry* pEntry = rPropertyMap.getByName( aPropertyName ); + if ( pEntry ) + { + if ( IsScItemWid( pEntry->nWID ) ) + { + const ScPatternAttr* pPattern = rDoc.GetDefPattern(); + if ( pPattern ) + { + const SfxItemSet& rSet = pPattern->GetItemSet(); + + switch ( pEntry->nWID ) // for item-specific handling + { + case ATTR_VALUE_FORMAT: + // default has no language set + aAny <<= static_cast<sal_Int32>( static_cast<const SfxUInt32Item&>(rSet.Get(pEntry->nWID)).GetValue() ); + break; + case ATTR_INDENT: + aAny <<= static_cast<sal_Int16>( convertTwipToMm100(static_cast<const ScIndentItem&>( + rSet.Get(pEntry->nWID)).GetValue()) ); + break; + default: + pPropSet->getPropertyValue(aPropertyName, rSet, aAny); + } + } + } + else + switch ( pEntry->nWID ) + { + case SC_WID_UNO_CHCOLHDR: + case SC_WID_UNO_CHROWHDR: + aAny <<= false; + break; + case SC_WID_UNO_CELLSTYL: + aAny <<= ScStyleNameConversion::DisplayToProgrammaticName( + ScResId(STR_STYLENAME_STANDARD), SfxStyleFamily::Para ); + break; + case SC_WID_UNO_TBLBORD: + case SC_WID_UNO_TBLBORD2: + { + const ScPatternAttr* pPattern = rDoc.GetDefPattern(); + if ( pPattern ) + { + if (pEntry->nWID == SC_WID_UNO_TBLBORD2) + ScHelperFunctions::AssignTableBorder2ToAny( aAny, + pPattern->GetItem(ATTR_BORDER), + pPattern->GetItem(ATTR_BORDER_INNER) ); + else + ScHelperFunctions::AssignTableBorderToAny( aAny, + pPattern->GetItem(ATTR_BORDER), + pPattern->GetItem(ATTR_BORDER_INNER) ); + } + } + break; + case SC_WID_UNO_CONDFMT: + case SC_WID_UNO_CONDLOC: + case SC_WID_UNO_CONDXML: + { + bool bEnglish = ( pEntry->nWID != SC_WID_UNO_CONDLOC ); + bool bXML = ( pEntry->nWID == SC_WID_UNO_CONDXML ); + formula::FormulaGrammar::Grammar eGrammar = (bXML ? + rDoc.GetStorageGrammar() : + formula::FormulaGrammar::mapAPItoGrammar( bEnglish, bXML)); + + aAny <<= uno::Reference<sheet::XSheetConditionalEntries>( + new ScTableConditionalFormat( &rDoc, 0, aRanges[0].aStart.Tab(), eGrammar )); + } + break; + case SC_WID_UNO_VALIDAT: + case SC_WID_UNO_VALILOC: + case SC_WID_UNO_VALIXML: + { + bool bEnglish = ( pEntry->nWID != SC_WID_UNO_VALILOC ); + bool bXML = ( pEntry->nWID == SC_WID_UNO_VALIXML ); + formula::FormulaGrammar::Grammar eGrammar = (bXML ? + rDoc.GetStorageGrammar() : + formula::FormulaGrammar::mapAPItoGrammar( bEnglish, bXML)); + + aAny <<= uno::Reference<beans::XPropertySet>( + new ScTableValidationObj( rDoc, 0, eGrammar )); + } + break; + case SC_WID_UNO_NUMRULES: + { + aAny <<= ScStyleObj::CreateEmptyNumberingRules(); + } + break; + } + } + } + + return aAny; +} + +// XPropertySet + +uno::Reference<beans::XPropertySetInfo> SAL_CALL ScCellRangesBase::getPropertySetInfo() +{ + SolarMutexGuard aGuard; + static uno::Reference<beans::XPropertySetInfo> aRef( + new SfxItemPropertySetInfo( pPropSet->getPropertyMap() )); + return aRef; +} + +static void lcl_SetCellProperty( const SfxItemPropertyMapEntry& rEntry, const uno::Any& rValue, + ScPatternAttr& rPattern, const ScDocument &rDoc, + sal_uInt16& rFirstItemId, sal_uInt16& rSecondItemId ) +{ + rFirstItemId = rEntry.nWID; + rSecondItemId = 0; + + SfxItemSet& rSet = rPattern.GetItemSet(); + switch ( rEntry.nWID ) + { + case ATTR_VALUE_FORMAT: + { + // language for number formats + SvNumberFormatter* pFormatter = rDoc.GetFormatTable(); + sal_uLong nOldFormat = rSet.Get( ATTR_VALUE_FORMAT ).GetValue(); + LanguageType eOldLang = rSet.Get( ATTR_LANGUAGE_FORMAT ).GetLanguage(); + nOldFormat = pFormatter->GetFormatForLanguageIfBuiltIn( nOldFormat, eOldLang ); + + sal_Int32 nIntVal = 0; + if ( !(rValue >>= nIntVal) ) + throw lang::IllegalArgumentException(); + + sal_uLong nNewFormat = static_cast<sal_uLong>(nIntVal); + rSet.Put( SfxUInt32Item( ATTR_VALUE_FORMAT, nNewFormat ) ); + + const SvNumberformat* pNewEntry = pFormatter->GetEntry( nNewFormat ); + LanguageType eNewLang = + pNewEntry ? pNewEntry->GetLanguage() : LANGUAGE_DONTKNOW; + if ( eNewLang != eOldLang && eNewLang != LANGUAGE_DONTKNOW ) + { + rSet.Put( SvxLanguageItem( eNewLang, ATTR_LANGUAGE_FORMAT ) ); + + // if only language is changed, + // don't touch number format attribute + sal_uLong nNewMod = nNewFormat % SV_COUNTRY_LANGUAGE_OFFSET; + if ( nNewMod == ( nOldFormat % SV_COUNTRY_LANGUAGE_OFFSET ) && + nNewMod <= SV_MAX_COUNT_STANDARD_FORMATS ) + { + rFirstItemId = 0; // don't use ATTR_VALUE_FORMAT value + } + + rSecondItemId = ATTR_LANGUAGE_FORMAT; + } + + } + break; + case ATTR_INDENT: + { + sal_Int16 nIntVal = 0; + if ( !(rValue >>= nIntVal) ) + throw lang::IllegalArgumentException(); + + rSet.Put(ScIndentItem(o3tl::toTwips(nIntVal, o3tl::Length::mm100))); + + } + break; + case ATTR_ROTATE_VALUE: + { + sal_Int32 nRotVal = 0; + if ( !(rValue >>= nRotVal) ) + throw lang::IllegalArgumentException(); + + // stored value is always between 0 and 360 deg. + nRotVal %= 36000; + if ( nRotVal < 0 ) + nRotVal += 36000; + + rSet.Put( ScRotateValueItem( Degree100(nRotVal) ) ); + + } + break; + case ATTR_STACKED: + { + table::CellOrientation eOrient; + if( rValue >>= eOrient ) + { + switch( eOrient ) + { + case table::CellOrientation_STANDARD: + rSet.Put( ScVerticalStackCell( false ) ); + break; + case table::CellOrientation_TOPBOTTOM: + rSet.Put( ScVerticalStackCell( false ) ); + rSet.Put( ScRotateValueItem( 27000_deg100 ) ); + rSecondItemId = ATTR_ROTATE_VALUE; + break; + case table::CellOrientation_BOTTOMTOP: + rSet.Put( ScVerticalStackCell( false ) ); + rSet.Put( ScRotateValueItem( 9000_deg100 ) ); + rSecondItemId = ATTR_ROTATE_VALUE; + break; + case table::CellOrientation_STACKED: + rSet.Put( ScVerticalStackCell( true ) ); + break; + default: + { + // added to avoid warnings + } + } + } + } + break; + default: + { + lcl_GetCellsPropertySet()->setPropertyValue(rEntry, rValue, rSet); + } + } +} + +void SAL_CALL ScCellRangesBase::setPropertyValue( + const OUString& aPropertyName, const uno::Any& aValue ) +{ + SolarMutexGuard aGuard; + + if ( !pDocShell || aRanges.empty() ) + throw uno::RuntimeException(); + + const SfxItemPropertyMap& rPropertyMap = GetItemPropertyMap(); // from derived class + const SfxItemPropertyMapEntry* pEntry = rPropertyMap.getByName( aPropertyName ); + if ( !pEntry ) + throw beans::UnknownPropertyException(aPropertyName); + + SetOnePropertyValue( pEntry, aValue ); +} + +void ScCellRangesBase::SetOnePropertyValue( const SfxItemPropertyMapEntry* pEntry, const uno::Any& aValue ) +{ + if ( !pEntry ) + return; + + if ( IsScItemWid( pEntry->nWID ) ) + { + if ( !aRanges.empty() ) // empty = nothing to do + { + ScDocument& rDoc = pDocShell->GetDocument(); + + // For parts of compound items with multiple properties (e.g. background) + // the old item has to be first fetched from the document. + //! But we can't recognize this case here + //! -> an extra flag in PropertyMap entry, or something like that??? + //! fetch the item directly from its position in the range? + // ClearInvalidItems, so that in any case we have an item with the correct type + + ScPatternAttr aPattern( *GetCurrentAttrsDeep() ); + SfxItemSet& rSet = aPattern.GetItemSet(); + rSet.ClearInvalidItems(); + + sal_uInt16 nFirstItem, nSecondItem; + lcl_SetCellProperty( *pEntry, aValue, aPattern, rDoc, nFirstItem, nSecondItem ); + + for (sal_uInt16 nWhich = ATTR_PATTERN_START; nWhich <= ATTR_PATTERN_END; nWhich++) + if ( nWhich != nFirstItem && nWhich != nSecondItem ) + rSet.ClearItem(nWhich); + + pDocShell->GetDocFunc().ApplyAttributes( *GetMarkData(), aPattern, true ); + } + } + else // implemented here + switch ( pEntry->nWID ) + { + case EE_CHAR_ESCAPEMENT: // Specifically for xlsx import + { + sal_Int32 nValue = 0; + aValue >>= nValue; + if (nValue) + { + for (size_t i = 0, n = aRanges.size(); i < n; ++i) + { + ScRange const & rRange = aRanges[i]; + + /* TODO: Iterate through the range */ + ScAddress aAddr = rRange.aStart; + ScDocument& rDoc = pDocShell->GetDocument(); + ScRefCellValue aCell(rDoc, aAddr); + + OUString aStr = aCell.getString(&rDoc); + EditEngine aEngine( rDoc.GetEnginePool() ); + aEngine.SetEditTextObjectPool(rDoc.GetEditPool()); + + /* EE_CHAR_ESCAPEMENT seems to be set on the cell _only_ when + * there are no other attribs for the cell. + * So, it is safe to overwrite the complete attribute set. + * If there is a need - getting CellType and processing + * the attributes could be considered. + */ + SfxItemSet aAttr = aEngine.GetEmptyItemSet(); + aEngine.SetText(aStr); + if( nValue < 0 ) // Subscript + aAttr.Put( SvxEscapementItem( SvxEscapement::Subscript, EE_CHAR_ESCAPEMENT ) ); + else // Superscript + aAttr.Put( SvxEscapementItem( SvxEscapement::Superscript, EE_CHAR_ESCAPEMENT ) ); + aEngine.QuickSetAttribs(aAttr, ESelection(0, 0, 0, aStr.getLength())); + + // The cell will own the text object instance. + rDoc.SetEditText(aRanges[0].aStart, aEngine.CreateTextObject()); + } + } + } + break; + case SC_WID_UNO_CHCOLHDR: + // chart header flags are set for this object, not stored with document + bChartColAsHdr = ScUnoHelpFunctions::GetBoolFromAny( aValue ); + break; + case SC_WID_UNO_CHROWHDR: + bChartRowAsHdr = ScUnoHelpFunctions::GetBoolFromAny( aValue ); + break; + case SC_WID_UNO_CELLSTYL: + { + OUString aStrVal; + aValue >>= aStrVal; + OUString aString(ScStyleNameConversion::ProgrammaticToDisplayName( + aStrVal, SfxStyleFamily::Para )); + pDocShell->GetDocFunc().ApplyStyle( *GetMarkData(), aString, true ); + } + break; + case SC_WID_UNO_TBLBORD: + { + table::TableBorder aBorder; + if ( !aRanges.empty() && ( aValue >>= aBorder ) ) // empty = nothing to do + { + SvxBoxItem aOuter(ATTR_BORDER); + SvxBoxInfoItem aInner(ATTR_BORDER_INNER); + ScHelperFunctions::FillBoxItems( aOuter, aInner, aBorder ); + + ScHelperFunctions::ApplyBorder( pDocShell, aRanges, aOuter, aInner ); //! docfunc + } + } + break; + case SC_WID_UNO_TBLBORD2: + { + table::TableBorder2 aBorder2; + if ( !aRanges.empty() && ( aValue >>= aBorder2 ) ) // empty = nothing to do + { + SvxBoxItem aOuter(ATTR_BORDER); + SvxBoxInfoItem aInner(ATTR_BORDER_INNER); + ScHelperFunctions::FillBoxItems( aOuter, aInner, aBorder2 ); + + ScHelperFunctions::ApplyBorder( pDocShell, aRanges, aOuter, aInner ); //! docfunc + } + } + break; + case SC_WID_UNO_CONDFMT: + case SC_WID_UNO_CONDLOC: + case SC_WID_UNO_CONDXML: + { + uno::Reference<sheet::XSheetConditionalEntries> xInterface(aValue, uno::UNO_QUERY); + if ( !aRanges.empty() && xInterface.is() ) // empty = nothing to do + { + ScTableConditionalFormat* pFormat = + dynamic_cast<ScTableConditionalFormat*>( xInterface.get() ); + if (pFormat) + { + ScDocument& rDoc = pDocShell->GetDocument(); + bool bEnglish = ( pEntry->nWID != SC_WID_UNO_CONDLOC ); + bool bXML = ( pEntry->nWID == SC_WID_UNO_CONDXML ); + formula::FormulaGrammar::Grammar eGrammar = (bXML ? + formula::FormulaGrammar::GRAM_UNSPECIFIED : + formula::FormulaGrammar::mapAPItoGrammar( bEnglish, bXML)); + + SCTAB nTab = aRanges.front().aStart.Tab(); + // To remove conditional formats for all cells in aRanges we need to: + // Remove conditional format data from cells' attributes + rDoc.RemoveCondFormatData( aRanges, nTab, 0 ); + // And also remove ranges from conditional formats list + for (size_t i = 0; i < aRanges.size(); ++i) + { + rDoc.GetCondFormList( aRanges[i].aStart.Tab() )->DeleteArea( + aRanges[i].aStart.Col(), aRanges[i].aStart.Row(), + aRanges[i].aEnd.Col(), aRanges[i].aEnd.Row() ); + } + + // Then we can apply new conditional format if there is one + if (pFormat->getCount()) + { + auto pNew = std::make_unique<ScConditionalFormat>( 0, &rDoc ); // Index will be set on inserting + pFormat->FillFormat( *pNew, rDoc, eGrammar ); + pNew->SetRange( aRanges ); + pDocShell->GetDocFunc().ReplaceConditionalFormat( 0, std::move(pNew), nTab, aRanges ); + } + + // and repaint + for (size_t i = 0; i < aRanges.size(); ++i) + pDocShell->PostPaint(aRanges[i], PaintPartFlags::Grid); + pDocShell->SetDocumentModified(); + } + } + } + break; + case SC_WID_UNO_VALIDAT: + case SC_WID_UNO_VALILOC: + case SC_WID_UNO_VALIXML: + { + uno::Reference<beans::XPropertySet> xInterface(aValue, uno::UNO_QUERY); + if ( !aRanges.empty() && xInterface.is() ) // empty = nothing to do + { + ScTableValidationObj* pValidObj = + dynamic_cast<ScTableValidationObj*>( xInterface.get() ); + if (pValidObj) + { + ScDocument& rDoc = pDocShell->GetDocument(); + bool bEnglish = ( pEntry->nWID != SC_WID_UNO_VALILOC ); + bool bXML = ( pEntry->nWID == SC_WID_UNO_VALIXML ); + formula::FormulaGrammar::Grammar eGrammar = (bXML ? + formula::FormulaGrammar::GRAM_UNSPECIFIED : + formula::FormulaGrammar::mapAPItoGrammar( bEnglish, bXML)); + + std::unique_ptr<ScValidationData> pNewData( + pValidObj->CreateValidationData( rDoc, eGrammar )); + sal_uInt32 nIndex = rDoc.AddValidationEntry( *pNewData ); + pNewData.reset(); + + ScPatternAttr aPattern( rDoc.GetPool() ); + aPattern.GetItemSet().Put( SfxUInt32Item( ATTR_VALIDDATA, nIndex ) ); + pDocShell->GetDocFunc().ApplyAttributes( *GetMarkData(), aPattern, true ); + } + } + } + break; + // SC_WID_UNO_NUMRULES is ignored... + } +} + +uno::Any SAL_CALL ScCellRangesBase::getPropertyValue( const OUString& aPropertyName ) +{ + SolarMutexGuard aGuard; + + if ( !pDocShell || aRanges.empty() ) + throw uno::RuntimeException(); + + const SfxItemPropertyMap& rPropertyMap = GetItemPropertyMap(); // from derived class + const SfxItemPropertyMapEntry* pEntry = rPropertyMap.getByName( aPropertyName ); + if ( !pEntry ) + throw beans::UnknownPropertyException(aPropertyName); + + uno::Any aAny; + GetOnePropertyValue( pEntry, aAny ); + return aAny; +} + +void ScCellRangesBase::GetOnePropertyValue( const SfxItemPropertyMapEntry* pEntry, uno::Any& rAny ) +{ + if ( !pEntry ) + return; + + if ( IsScItemWid( pEntry->nWID ) ) + { + SfxItemSet* pDataSet = GetCurrentDataSet(); + if ( pDataSet ) + { + switch ( pEntry->nWID ) // for special handling of items + { + case ATTR_VALUE_FORMAT: + { + ScDocument& rDoc = pDocShell->GetDocument(); + + sal_uLong nOldFormat = + pDataSet->Get( ATTR_VALUE_FORMAT ).GetValue(); + LanguageType eOldLang = + pDataSet->Get( ATTR_LANGUAGE_FORMAT ).GetLanguage(); + nOldFormat = rDoc.GetFormatTable()-> + GetFormatForLanguageIfBuiltIn( nOldFormat, eOldLang ); + rAny <<= static_cast<sal_Int32>(nOldFormat); + } + break; + case ATTR_INDENT: + rAny <<= static_cast<sal_Int16>( convertTwipToMm100(static_cast<const ScIndentItem&>( + pDataSet->Get(pEntry->nWID)).GetValue()) ); + break; + case ATTR_STACKED: + { + Degree100 nRot = pDataSet->Get(ATTR_ROTATE_VALUE).GetValue(); + bool bStacked = static_cast<const ScVerticalStackCell&>(pDataSet->Get(pEntry->nWID)).GetValue(); + SvxOrientationItem( nRot, bStacked, TypedWhichId<SvxOrientationItem>(0) ).QueryValue( rAny ); + } + break; + default: + pPropSet->getPropertyValue(*pEntry, *pDataSet, rAny); + } + } + } + else // implemented here + switch ( pEntry->nWID ) + { + case SC_WID_UNO_CHCOLHDR: + rAny <<= bChartColAsHdr; + break; + case SC_WID_UNO_CHROWHDR: + rAny <<= bChartRowAsHdr; + break; + case SC_WID_UNO_CELLSTYL: + { + OUString aStyleName; + const ScStyleSheet* pStyle = pDocShell->GetDocument().GetSelectionStyle(*GetMarkData()); + if (pStyle) + aStyleName = pStyle->GetName(); + rAny <<= ScStyleNameConversion::DisplayToProgrammaticName( + aStyleName, SfxStyleFamily::Para ); + } + break; + case SC_WID_UNO_TBLBORD: + case SC_WID_UNO_TBLBORD2: + { + //! loop through all ranges + if ( !aRanges.empty() ) + { + const ScRange & rFirst = aRanges[ 0 ]; + SvxBoxItem aOuter(ATTR_BORDER); + SvxBoxInfoItem aInner(ATTR_BORDER_INNER); + + ScDocument& rDoc = pDocShell->GetDocument(); + ScMarkData aMark(rDoc.GetSheetLimits()); + aMark.SetMarkArea( rFirst ); + aMark.SelectTable( rFirst.aStart.Tab(), true ); + rDoc.GetSelectionFrame( aMark, aOuter, aInner ); + + if (pEntry->nWID == SC_WID_UNO_TBLBORD2) + ScHelperFunctions::AssignTableBorder2ToAny( rAny, aOuter, aInner); + else + ScHelperFunctions::AssignTableBorderToAny( rAny, aOuter, aInner); + } + } + break; + case SC_WID_UNO_CONDFMT: + case SC_WID_UNO_CONDLOC: + case SC_WID_UNO_CONDXML: + { + const ScPatternAttr* pPattern = GetCurrentAttrsDeep(); + if ( pPattern ) + { + ScDocument& rDoc = pDocShell->GetDocument(); + bool bEnglish = ( pEntry->nWID != SC_WID_UNO_CONDLOC ); + bool bXML = ( pEntry->nWID == SC_WID_UNO_CONDXML ); + formula::FormulaGrammar::Grammar eGrammar = (bXML ? + rDoc.GetStorageGrammar() : + formula::FormulaGrammar::mapAPItoGrammar( bEnglish, bXML)); + const ScCondFormatIndexes& rIndex = + pPattern->GetItem(ATTR_CONDITIONAL).GetCondFormatData(); + sal_uLong nIndex = 0; + if(!rIndex.empty()) + nIndex = rIndex[0]; + rAny <<= uno::Reference<sheet::XSheetConditionalEntries>( + new ScTableConditionalFormat( &rDoc, nIndex, aRanges.front().aStart.Tab(), eGrammar )); + } + } + break; + case SC_WID_UNO_VALIDAT: + case SC_WID_UNO_VALILOC: + case SC_WID_UNO_VALIXML: + { + const ScPatternAttr* pPattern = GetCurrentAttrsDeep(); + if ( pPattern ) + { + ScDocument& rDoc = pDocShell->GetDocument(); + bool bEnglish = ( pEntry->nWID != SC_WID_UNO_VALILOC ); + bool bXML = ( pEntry->nWID == SC_WID_UNO_VALIXML ); + formula::FormulaGrammar::Grammar eGrammar = (bXML ? + rDoc.GetStorageGrammar() : + formula::FormulaGrammar::mapAPItoGrammar( bEnglish, bXML)); + sal_uLong nIndex = + pPattern->GetItem(ATTR_VALIDDATA).GetValue(); + rAny <<= uno::Reference<beans::XPropertySet>( + new ScTableValidationObj( rDoc, nIndex, eGrammar )); + } + } + break; + case SC_WID_UNO_NUMRULES: + { + // always return empty numbering rules object + rAny <<= ScStyleObj::CreateEmptyNumberingRules(); + } + break; + case SC_WID_UNO_ABSNAME: + { + OUString sRet; + aRanges.Format(sRet, ScRefFlags::RANGE_ABS_3D, pDocShell->GetDocument()); + rAny <<= sRet; + } + break; + case SC_WID_UNO_FORMATID: + { + const ScPatternAttr* pPattern = GetCurrentAttrsFlat(); + rAny <<= pPattern->GetPAKey(); + } + break; + } +} + +void SAL_CALL ScCellRangesBase::addPropertyChangeListener( const OUString& /* aPropertyName */, + const uno::Reference<beans::XPropertyChangeListener>& /* aListener */) +{ + SolarMutexGuard aGuard; + if ( aRanges.empty() ) + throw uno::RuntimeException(); + + OSL_FAIL("not implemented"); +} + +void SAL_CALL ScCellRangesBase::removePropertyChangeListener( const OUString& /* aPropertyName */, + const uno::Reference<beans::XPropertyChangeListener>& /* aListener */) +{ + SolarMutexGuard aGuard; + if ( aRanges.empty() ) + throw uno::RuntimeException(); + + OSL_FAIL("not implemented"); +} + +void SAL_CALL ScCellRangesBase::addVetoableChangeListener( const OUString&, + const uno::Reference<beans::XVetoableChangeListener>&) +{ + OSL_FAIL("not implemented"); +} + +void SAL_CALL ScCellRangesBase::removeVetoableChangeListener( const OUString&, + const uno::Reference<beans::XVetoableChangeListener>&) +{ + OSL_FAIL("not implemented"); +} + +// XMultiPropertySet + +void SAL_CALL ScCellRangesBase::setPropertyValues( const uno::Sequence< OUString >& aPropertyNames, + const uno::Sequence< uno::Any >& aValues ) +{ + SolarMutexGuard aGuard; + + sal_Int32 nCount(aPropertyNames.getLength()); + sal_Int32 nValues(aValues.getLength()); + if (nCount != nValues) + throw lang::IllegalArgumentException(); + + if ( !(pDocShell && nCount) ) + return; + + const SfxItemPropertyMap& rPropertyMap = GetItemPropertyMap(); // from derived class + const OUString* pNames = aPropertyNames.getConstArray(); + const uno::Any* pValues = aValues.getConstArray(); + + std::unique_ptr<const SfxItemPropertyMapEntry*[]> pEntryArray(new const SfxItemPropertyMapEntry*[nCount]); + + sal_Int32 i; + for(i = 0; i < nCount; i++) + { + // first loop: find all properties in map, but handle only CellStyle + // (CellStyle must be set before any other cell properties) + + const SfxItemPropertyMapEntry* pEntry = rPropertyMap.getByName( pNames[i] ); + pEntryArray[i] = pEntry; + if (pEntry) + { + if ( pEntry->nWID == SC_WID_UNO_CELLSTYL ) + { + try + { + SetOnePropertyValue( pEntry, pValues[i] ); + } + catch ( lang::IllegalArgumentException& ) + { + TOOLS_WARN_EXCEPTION( "sc", "exception when setting cell style"); // not supposed to happen + } + } + } + } + + ScDocument& rDoc = pDocShell->GetDocument(); + std::unique_ptr<ScPatternAttr> pOldPattern; + std::unique_ptr<ScPatternAttr> pNewPattern; + + for(i = 0; i < nCount; i++) + { + // second loop: handle other properties + + const SfxItemPropertyMapEntry* pEntry = pEntryArray[i]; + if ( pEntry ) + { + if ( IsScItemWid( pEntry->nWID ) ) // can be handled by SfxItemPropertySet + { + if ( !pOldPattern ) + { + pOldPattern.reset(new ScPatternAttr( *GetCurrentAttrsDeep() )); + pOldPattern->GetItemSet().ClearInvalidItems(); + pNewPattern.reset(new ScPatternAttr( rDoc.GetPool() )); + } + + // collect items in pNewPattern, apply with one call after the loop + + sal_uInt16 nFirstItem, nSecondItem; + lcl_SetCellProperty( *pEntry, pValues[i], *pOldPattern, rDoc, nFirstItem, nSecondItem ); + + // put only affected items into new set + if ( nFirstItem ) + pNewPattern->GetItemSet().Put( pOldPattern->GetItemSet().Get( nFirstItem ) ); + if ( nSecondItem ) + pNewPattern->GetItemSet().Put( pOldPattern->GetItemSet().Get( nSecondItem ) ); + } + else if ( pEntry->nWID != SC_WID_UNO_CELLSTYL ) // CellStyle is handled above + { + // call virtual method to set a single property + SetOnePropertyValue( pEntry, pValues[i] ); + } + } + } + + if ( pNewPattern && !aRanges.empty() ) + pDocShell->GetDocFunc().ApplyAttributes( *GetMarkData(), *pNewPattern, true ); +} + +uno::Sequence<uno::Any> SAL_CALL ScCellRangesBase::getPropertyValues( + const uno::Sequence< OUString >& aPropertyNames ) +{ + SolarMutexGuard aGuard; + + const SfxItemPropertyMap& rPropertyMap = GetItemPropertyMap(); // from derived class + + uno::Sequence<uno::Any> aRet(aPropertyNames.getLength()); + uno::Any* pProperties = aRet.getArray(); + for(sal_Int32 i = 0; i < aPropertyNames.getLength(); i++) + { + const SfxItemPropertyMapEntry* pEntry = rPropertyMap.getByName( aPropertyNames[i] ); + GetOnePropertyValue( pEntry, pProperties[i] ); + } + return aRet; +} + +void SAL_CALL ScCellRangesBase::addPropertiesChangeListener( const uno::Sequence< OUString >& /* aPropertyNames */, + const uno::Reference< beans::XPropertiesChangeListener >& /* xListener */ ) +{ + OSL_FAIL("not implemented"); +} + +void SAL_CALL ScCellRangesBase::removePropertiesChangeListener( const uno::Reference< beans::XPropertiesChangeListener >& /* xListener */ ) +{ + OSL_FAIL("not implemented"); +} + +void SAL_CALL ScCellRangesBase::firePropertiesChangeEvent( const uno::Sequence< OUString >& /* aPropertyNames */, + const uno::Reference< beans::XPropertiesChangeListener >& /* xListener */ ) +{ + OSL_FAIL("not implemented"); +} + +IMPL_LINK( ScCellRangesBase, ValueListenerHdl, const SfxHint&, rHint, void ) +{ + if ( pDocShell && (rHint.GetId() == SfxHintId::ScDataChanged)) + { + // This may be called several times for a single change, if several formulas + // in the range are notified. So only a flag is set that is checked when + // SfxHintId::DataChanged is received. + + bGotDataChangedHint = true; + } +} + +// XTolerantMultiPropertySet +uno::Sequence< beans::SetPropertyTolerantFailed > SAL_CALL ScCellRangesBase::setPropertyValuesTolerant( const uno::Sequence< OUString >& aPropertyNames, + const uno::Sequence< uno::Any >& aValues ) +{ + SolarMutexGuard aGuard; + + sal_Int32 nCount(aPropertyNames.getLength()); + sal_Int32 nValues(aValues.getLength()); + if (nCount != nValues) + throw lang::IllegalArgumentException(); + + if ( pDocShell && nCount ) + { + uno::Sequence < beans::SetPropertyTolerantFailed > aReturns(nCount); + beans::SetPropertyTolerantFailed* pReturns = aReturns.getArray(); + + const SfxItemPropertyMap& rPropertyMap = GetItemPropertyMap(); // from derived class + const OUString* pNames = aPropertyNames.getConstArray(); + const uno::Any* pValues = aValues.getConstArray(); + + std::unique_ptr<const SfxItemPropertyMapEntry*[]> pMapArray(new const SfxItemPropertyMapEntry*[nCount]); + + sal_Int32 i; + for(i = 0; i < nCount; i++) + { + // first loop: find all properties in map, but handle only CellStyle + // (CellStyle must be set before any other cell properties) + + const SfxItemPropertyMapEntry* pEntry = rPropertyMap.getByName( pNames[i] ); + pMapArray[i] = pEntry; + if (pEntry) + { + if ( pEntry->nWID == SC_WID_UNO_CELLSTYL ) + { + try + { + SetOnePropertyValue( pEntry, pValues[i] ); + } + catch ( lang::IllegalArgumentException& ) + { + TOOLS_WARN_EXCEPTION( "sc", "exception when setting cell style"); // not supposed to happen + } + } + } + } + + ScDocument& rDoc = pDocShell->GetDocument(); + std::unique_ptr<ScPatternAttr> pOldPattern; + std::unique_ptr<ScPatternAttr> pNewPattern; + + sal_Int32 nFailed(0); + for(i = 0; i < nCount; i++) + { + // second loop: handle other properties + + const SfxItemPropertyMapEntry* pEntry = pMapArray[i]; + if ( pEntry && ((pEntry->nFlags & beans::PropertyAttribute::READONLY) == 0)) + { + if ( IsScItemWid( pEntry->nWID ) ) // can be handled by SfxItemPropertySet + { + if ( !pOldPattern ) + { + pOldPattern.reset(new ScPatternAttr( *GetCurrentAttrsDeep() )); + pOldPattern->GetItemSet().ClearInvalidItems(); + pNewPattern.reset(new ScPatternAttr( rDoc.GetPool() )); + } + + // collect items in pNewPattern, apply with one call after the loop + try + { + sal_uInt16 nFirstItem, nSecondItem; + lcl_SetCellProperty( *pEntry, pValues[i], *pOldPattern, rDoc, nFirstItem, nSecondItem ); + + // put only affected items into new set + if ( nFirstItem ) + pNewPattern->GetItemSet().Put( pOldPattern->GetItemSet().Get( nFirstItem ) ); + if ( nSecondItem ) + pNewPattern->GetItemSet().Put( pOldPattern->GetItemSet().Get( nSecondItem ) ); + } + catch ( lang::IllegalArgumentException& ) + { + pReturns[nFailed].Name = pNames[i]; + pReturns[nFailed++].Result = beans::TolerantPropertySetResultType::ILLEGAL_ARGUMENT; + } + } + else if ( pEntry->nWID != SC_WID_UNO_CELLSTYL ) // CellStyle is handled above + { + // call virtual method to set a single property + try + { + SetOnePropertyValue( pEntry, pValues[i] ); + } + catch ( lang::IllegalArgumentException& ) + { + pReturns[nFailed].Name = pNames[i]; + pReturns[nFailed++].Result = beans::TolerantPropertySetResultType::ILLEGAL_ARGUMENT; + } + } + } + else + { + pReturns[nFailed].Name = pNames[i]; + if (pEntry) + pReturns[nFailed++].Result = beans::TolerantPropertySetResultType::PROPERTY_VETO; + else + pReturns[nFailed++].Result = beans::TolerantPropertySetResultType::UNKNOWN_PROPERTY; + } + } + + if ( pNewPattern && !aRanges.empty() ) + pDocShell->GetDocFunc().ApplyAttributes( *GetMarkData(), *pNewPattern, true ); + + aReturns.realloc(nFailed); + + return aReturns; + } + return uno::Sequence < beans::SetPropertyTolerantFailed >(); +} + +uno::Sequence< beans::GetPropertyTolerantResult > SAL_CALL ScCellRangesBase::getPropertyValuesTolerant( const uno::Sequence< OUString >& aPropertyNames ) +{ + SolarMutexGuard aGuard; + + sal_Int32 nCount(aPropertyNames.getLength()); + uno::Sequence < beans::GetPropertyTolerantResult > aReturns(nCount); + beans::GetPropertyTolerantResult* pReturns = aReturns.getArray(); + + const SfxItemPropertyMap& rPropertyMap = GetItemPropertyMap(); // from derived class + + for(sal_Int32 i = 0; i < nCount; i++) + { + const SfxItemPropertyMapEntry* pEntry = rPropertyMap.getByName( aPropertyNames[i] ); + if (!pEntry) + { + pReturns[i].Result = beans::TolerantPropertySetResultType::UNKNOWN_PROPERTY; + } + else + { + sal_uInt16 nItemWhich = 0; + lcl_GetPropertyWhich( pEntry, nItemWhich ); + pReturns[i].State = GetOnePropertyState( nItemWhich, pEntry ); + GetOnePropertyValue( pEntry, pReturns[i].Value ); + pReturns[i].Result = beans::TolerantPropertySetResultType::SUCCESS; + } + } + return aReturns; +} + +uno::Sequence< beans::GetDirectPropertyTolerantResult > SAL_CALL ScCellRangesBase::getDirectPropertyValuesTolerant( const uno::Sequence< OUString >& aPropertyNames ) +{ + SolarMutexGuard aGuard; + + sal_Int32 nCount(aPropertyNames.getLength()); + uno::Sequence < beans::GetDirectPropertyTolerantResult > aReturns(nCount); + beans::GetDirectPropertyTolerantResult* pReturns = aReturns.getArray(); + + const SfxItemPropertyMap& rPropertyMap = GetItemPropertyMap(); // from derived class + + sal_Int32 j = 0; + for(sal_Int32 i = 0; i < nCount; i++) + { + const SfxItemPropertyMapEntry* pEntry = rPropertyMap.getByName( aPropertyNames[i] ); + if (!pEntry) + { + pReturns[i].Result = beans::TolerantPropertySetResultType::UNKNOWN_PROPERTY; + } + else + { + sal_uInt16 nItemWhich = 0; + lcl_GetPropertyWhich( pEntry, nItemWhich ); + pReturns[j].State = GetOnePropertyState( nItemWhich, pEntry ); + if (pReturns[j].State == beans::PropertyState_DIRECT_VALUE) + { + GetOnePropertyValue( pEntry, pReturns[j].Value ); + pReturns[j].Result = beans::TolerantPropertySetResultType::SUCCESS; + pReturns[j].Name = aPropertyNames[i]; + ++j; + } + } + } + if (j < nCount) + aReturns.realloc(j); + return aReturns; +} + +// XIndent + +void SAL_CALL ScCellRangesBase::decrementIndent() +{ + SolarMutexGuard aGuard; + if ( pDocShell && !aRanges.empty() ) + { + //#97041#; put only MultiMarked ScMarkData in ChangeIndent + ScMarkData aMarkData(*GetMarkData()); + aMarkData.MarkToMulti(); + pDocShell->GetDocFunc().ChangeIndent( aMarkData, false, true ); + } +} + +void SAL_CALL ScCellRangesBase::incrementIndent() +{ + SolarMutexGuard aGuard; + if ( pDocShell && !aRanges.empty() ) + { + //#97041#; put only MultiMarked ScMarkData in ChangeIndent + ScMarkData aMarkData(*GetMarkData()); + aMarkData.MarkToMulti(); + pDocShell->GetDocFunc().ChangeIndent( aMarkData, true, true ); + } +} + +// XChartData + +std::unique_ptr<ScMemChart> ScCellRangesBase::CreateMemChart_Impl() const +{ + if ( pDocShell && !aRanges.empty() ) + { + ScRangeListRef xChartRanges; + if ( aRanges.size() == 1 ) + { + // set useful table limit (only occupied data area) + // (only here - Listeners are registered for the whole area) + //! check immediately if a ScTableSheetObj? + + const ScDocument & rDoc = pDocShell->GetDocument(); + const ScRange & rRange = aRanges[0]; + if ( rRange.aStart.Col() == 0 && rRange.aEnd.Col() == rDoc.MaxCol() && + rRange.aStart.Row() == 0 && rRange.aEnd.Row() == rDoc.MaxRow() ) + { + SCTAB nTab = rRange.aStart.Tab(); + + SCCOL nStartX; + SCROW nStartY; // Get start + if (!pDocShell->GetDocument().GetDataStart( nTab, nStartX, nStartY )) + { + nStartX = 0; + nStartY = 0; + } + + SCCOL nEndX; + SCROW nEndY; // Get end + if (!pDocShell->GetDocument().GetTableArea( nTab, nEndX, nEndY )) + { + nEndX = 0; + nEndY = 0; + } + + xChartRanges = new ScRangeList( ScRange( nStartX, nStartY, nTab, nEndX, nEndY, nTab ) ); + } + } + if (!xChartRanges.is()) // otherwise take Ranges directly + xChartRanges = new ScRangeList(aRanges); + ScChartArray aArr( pDocShell->GetDocument(), xChartRanges ); + + // RowAsHdr = ColHeaders and vice versa + aArr.SetHeaders( bChartRowAsHdr, bChartColAsHdr ); + + return aArr.CreateMemChart(); + } + return nullptr; +} + +uno::Sequence< uno::Sequence<double> > SAL_CALL ScCellRangesBase::getData() +{ + SolarMutexGuard aGuard; + std::unique_ptr<ScMemChart> pMemChart(CreateMemChart_Impl()); + if ( pMemChart ) + { + sal_Int32 nColCount = pMemChart->GetColCount(); + sal_Int32 nRowCount = static_cast<sal_Int32>(pMemChart->GetRowCount()); + + uno::Sequence< uno::Sequence<double> > aRowSeq( nRowCount ); + uno::Sequence<double>* pRowAry = aRowSeq.getArray(); + for (sal_Int32 nRow = 0; nRow < nRowCount; nRow++) + { + uno::Sequence<double> aColSeq( nColCount ); + double* pColAry = aColSeq.getArray(); + for (sal_Int32 nCol = 0; nCol < nColCount; nCol++) + pColAry[nCol] = pMemChart->GetData( nCol, nRow ); + + pRowAry[nRow] = aColSeq; + } + + return aRowSeq; + } + + return {}; +} + +ScRangeListRef ScCellRangesBase::GetLimitedChartRanges_Impl( sal_Int32 nDataColumns, sal_Int32 nDataRows ) const +{ + if ( aRanges.size() == 1 ) + { + const ScDocument & rDoc = pDocShell->GetDocument(); + const ScRange & rRange = aRanges[0]; + if ( rRange.aStart.Col() == 0 && rRange.aEnd.Col() == rDoc.MaxCol() && + rRange.aStart.Row() == 0 && rRange.aEnd.Row() == rDoc.MaxRow() ) + { + // if aRanges is a complete sheet, limit to given size + + SCTAB nTab = rRange.aStart.Tab(); + + sal_Int32 nEndColumn = nDataColumns - 1 + ( bChartColAsHdr ? 1 : 0 ); + if ( nEndColumn < 0 ) + nEndColumn = 0; + if ( nEndColumn > rDoc.MaxCol() ) + nEndColumn = rDoc.MaxCol(); + + sal_Int32 nEndRow = nDataRows - 1 + ( bChartRowAsHdr ? 1 : 0 ); + if ( nEndRow < 0 ) + nEndRow = 0; + if ( nEndRow > rDoc.MaxRow() ) + nEndRow = rDoc.MaxRow(); + + ScRangeListRef xChartRanges = new ScRangeList( ScRange( 0, 0, nTab, static_cast<SCCOL>(nEndColumn), static_cast<SCROW>(nEndRow), nTab ) ); + return xChartRanges; + } + } + + return new ScRangeList(aRanges); // as-is +} + +void SAL_CALL ScCellRangesBase::setData( const uno::Sequence< uno::Sequence<double> >& aData ) +{ + SolarMutexGuard aGuard; + bool bDone = false; + sal_Int32 nRowCount = aData.getLength(); + sal_Int32 nColCount = nRowCount ? aData[0].getLength() : 0; + ScRangeListRef xChartRanges = GetLimitedChartRanges_Impl( nColCount, nRowCount ); + if ( pDocShell && xChartRanges.is() ) + { + ScDocument& rDoc = pDocShell->GetDocument(); + ScChartArray aArr( rDoc, xChartRanges ); + aArr.SetHeaders( bChartRowAsHdr, bChartColAsHdr ); // RowAsHdr = ColHeaders + const ScChartPositionMap* pPosMap = aArr.GetPositionMap(); + if (pPosMap) + { + if ( pPosMap->GetColCount() == static_cast<SCCOL>(nColCount) && + pPosMap->GetRowCount() == static_cast<SCROW>(nRowCount) ) + { + for (sal_Int32 nRow=0; nRow<nRowCount; nRow++) + { + const uno::Sequence<double>& rRowSeq = aData[nRow]; + const double* pArray = rRowSeq.getConstArray(); + nColCount = rRowSeq.getLength(); + for (sal_Int32 nCol=0; nCol<nColCount; nCol++) + { + const ScAddress* pPos = pPosMap->GetPosition( + sal::static_int_cast<SCCOL>(nCol), + sal::static_int_cast<SCROW>(nRow) ); + if (pPos) + { + double fVal = pArray[nCol]; + if ( fVal == DBL_MIN ) + rDoc.SetEmptyCell(*pPos); + else + rDoc.SetValue(*pPos, pArray[nCol]); + } + } + } + + //! undo + PaintGridRanges_Impl(); + pDocShell->SetDocumentModified(); + ForceChartListener_Impl(); // call listeners for this object synchronously + bDone = true; + } + } + } + + if (!bDone) + throw uno::RuntimeException(); +} + +uno::Sequence<OUString> SAL_CALL ScCellRangesBase::getRowDescriptions() +{ + SolarMutexGuard aGuard; + std::unique_ptr<ScMemChart> pMemChart(CreateMemChart_Impl()); + if ( pMemChart ) + { + sal_Int32 nRowCount = static_cast<sal_Int32>(pMemChart->GetRowCount()); + uno::Sequence<OUString> aSeq( nRowCount ); + OUString* pAry = aSeq.getArray(); + for (sal_Int32 nRow = 0; nRow < nRowCount; nRow++) + pAry[nRow] = pMemChart->GetRowText(nRow); + + return aSeq; + } + return {}; +} + +void SAL_CALL ScCellRangesBase::setRowDescriptions( + const uno::Sequence<OUString>& aRowDescriptions ) +{ + SolarMutexGuard aGuard; + bool bDone = false; + if ( bChartColAsHdr ) + { + sal_Int32 nRowCount = aRowDescriptions.getLength(); + ScRangeListRef xChartRanges = GetLimitedChartRanges_Impl( 1, nRowCount ); + if ( pDocShell && xChartRanges.is() ) + { + ScDocument& rDoc = pDocShell->GetDocument(); + ScChartArray aArr( rDoc, xChartRanges ); + aArr.SetHeaders( bChartRowAsHdr, bChartColAsHdr ); // RowAsHdr = ColHeaders + const ScChartPositionMap* pPosMap = aArr.GetPositionMap(); + if (pPosMap) + { + if ( pPosMap->GetRowCount() == static_cast<SCROW>(nRowCount) ) + { + const OUString* pArray = aRowDescriptions.getConstArray(); + for (sal_Int32 nRow=0; nRow<nRowCount; nRow++) + { + const ScAddress* pPos = pPosMap->GetRowHeaderPosition( + static_cast<SCSIZE>(nRow) ); + if (pPos) + { + const OUString& aStr = pArray[nRow]; + if (aStr.isEmpty()) + rDoc.SetEmptyCell(*pPos); + else + { + ScSetStringParam aParam; + aParam.setTextInput(); + rDoc.SetString(*pPos, aStr, &aParam); + } + } + } + + //! undo + PaintGridRanges_Impl(); + pDocShell->SetDocumentModified(); + ForceChartListener_Impl(); // call listeners for this object synchronously + bDone = true; + } + } + } + } + + if (!bDone) + throw uno::RuntimeException(); +} + +uno::Sequence<OUString> SAL_CALL ScCellRangesBase::getColumnDescriptions() +{ + SolarMutexGuard aGuard; + std::unique_ptr<ScMemChart> pMemChart(CreateMemChart_Impl()); + if ( pMemChart ) + { + sal_Int32 nColCount = pMemChart->GetColCount(); + uno::Sequence<OUString> aSeq( nColCount ); + OUString* pAry = aSeq.getArray(); + for (sal_Int32 nCol = 0; nCol < nColCount; nCol++) + pAry[nCol] = pMemChart->GetColText(nCol); + + return aSeq; + } + return {}; +} + +void SAL_CALL ScCellRangesBase::setColumnDescriptions( + const uno::Sequence<OUString>& aColumnDescriptions ) +{ + SolarMutexGuard aGuard; + bool bDone = false; + if ( bChartRowAsHdr ) + { + sal_Int32 nColCount = aColumnDescriptions.getLength(); + ScRangeListRef xChartRanges = GetLimitedChartRanges_Impl( nColCount, 1 ); + if ( pDocShell && xChartRanges.is() ) + { + ScDocument& rDoc = pDocShell->GetDocument(); + ScChartArray aArr( rDoc, xChartRanges ); + aArr.SetHeaders( bChartRowAsHdr, bChartColAsHdr ); // RowAsHdr = ColHeaders + const ScChartPositionMap* pPosMap = aArr.GetPositionMap(); + if (pPosMap) + { + if ( pPosMap->GetColCount() == static_cast<SCCOL>(nColCount) ) + { + const OUString* pArray = aColumnDescriptions.getConstArray(); + for (sal_Int32 nCol=0; nCol<nColCount; nCol++) + { + const ScAddress* pPos = pPosMap->GetColHeaderPosition( + sal::static_int_cast<SCCOL>(nCol) ); + if (pPos) + { + const OUString& aStr = pArray[nCol]; + if (aStr.isEmpty()) + rDoc.SetEmptyCell(*pPos); + else + { + ScSetStringParam aParam; + aParam.setTextInput(); + rDoc.SetString(*pPos, aStr, &aParam); + } + } + } + + //! undo + PaintGridRanges_Impl(); + pDocShell->SetDocumentModified(); + ForceChartListener_Impl(); // call listeners for this object synchronously + bDone = true; + } + } + } + } + + if (!bDone) + throw uno::RuntimeException(); +} + +void ScCellRangesBase::ForceChartListener_Impl() +{ + // call Update immediately so the caller to setData etc. can + // recognize the listener call + + if (!pDocShell) + return; + + ScChartListenerCollection* pColl = pDocShell->GetDocument().GetChartListenerCollection(); + if (!pColl) + return; + + ScChartListenerCollection::ListenersType& rListeners = pColl->getListeners(); + for (auto const& it : rListeners) + { + ScChartListener *const p = it.second.get(); + assert(p); + if (p->GetUnoSource() == static_cast<chart::XChartData*>(this) && p->IsDirty()) + p->Update(); + } +} + +void SAL_CALL ScCellRangesBase::addChartDataChangeEventListener( const uno::Reference< + chart::XChartDataChangeEventListener >& aListener ) +{ + SolarMutexGuard aGuard; + if ( !pDocShell || aRanges.empty() ) + return; + + //! test for duplicates ? + + ScDocument& rDoc = pDocShell->GetDocument(); + ScRangeListRef aRangesRef( new ScRangeList(aRanges) ); + ScChartListenerCollection* pColl = rDoc.GetChartListenerCollection(); + OUString aName = pColl->getUniqueName(u"__Uno"); + if (aName.isEmpty()) + // failed to create unique name. + return; + + ScChartListener* pListener = new ScChartListener( aName, rDoc, aRangesRef ); + pListener->SetUno( aListener, this ); + pColl->insert( pListener ); + pListener->StartListeningTo(); +} + +void SAL_CALL ScCellRangesBase::removeChartDataChangeEventListener( const uno::Reference< + chart::XChartDataChangeEventListener >& aListener ) +{ + SolarMutexGuard aGuard; + if ( pDocShell && !aRanges.empty() ) + { + ScDocument& rDoc = pDocShell->GetDocument(); + ScChartListenerCollection* pColl = rDoc.GetChartListenerCollection(); + pColl->FreeUno( aListener, this ); + } +} + +double SAL_CALL ScCellRangesBase::getNotANumber() +{ + // use DBL_MIN in ScChartArray, because Chart wants it so + return DBL_MIN; +} + +sal_Bool SAL_CALL ScCellRangesBase::isNotANumber( double nNumber ) +{ + // use DBL_MIN in ScChartArray, because Chart wants it so + return (nNumber == DBL_MIN); +} + +// XModifyBroadcaster + +void SAL_CALL ScCellRangesBase::addModifyListener(const uno::Reference<util::XModifyListener>& aListener) +{ + SolarMutexGuard aGuard; + if ( aRanges.empty() ) + throw uno::RuntimeException(); + + aValueListeners.emplace_back( aListener ); + + if ( aValueListeners.size() == 1 ) + { + if (!pValueListener) + pValueListener.reset( new ScLinkListener( LINK( this, ScCellRangesBase, ValueListenerHdl ) ) ); + + ScDocument& rDoc = pDocShell->GetDocument(); + for ( size_t i = 0, nCount = aRanges.size(); i < nCount; i++) + rDoc.StartListeningArea( aRanges[ i ], false, pValueListener.get() ); + + acquire(); // don't lose this object (one ref for all listeners) + } +} + +void SAL_CALL ScCellRangesBase::removeModifyListener( const uno::Reference<util::XModifyListener>& aListener ) +{ + + SolarMutexGuard aGuard; + if ( aRanges.empty() ) + throw uno::RuntimeException(); + + rtl::Reference<ScCellRangesBase> xSelfHold(this); // in case the listeners have the last ref + + sal_uInt16 nCount = aValueListeners.size(); + for ( sal_uInt16 n=nCount; n--; ) + { + uno::Reference<util::XModifyListener>& rObj = aValueListeners[n]; + if ( rObj == aListener ) + { + aValueListeners.erase( aValueListeners.begin() + n ); + + if ( aValueListeners.empty() ) + { + if (pValueListener) + pValueListener->EndListeningAll(); + + release(); // release the ref for the listeners + } + + break; + } + } +} + +// XCellRangesQuery + +uno::Reference<sheet::XSheetCellRanges> SAL_CALL ScCellRangesBase::queryVisibleCells() +{ + SolarMutexGuard aGuard; + if (pDocShell) + { + //! Separate for all tables, if markings separated per table + SCTAB nTab = lcl_FirstTab(aRanges); + + ScMarkData aMarkData(*GetMarkData()); + + ScDocument& rDoc = pDocShell->GetDocument(); + SCCOL nCol = 0, nLastCol; + while (nCol <= rDoc.MaxCol()) + { + if (rDoc.ColHidden(nCol, nTab, nullptr, &nLastCol)) + // hidden columns. Deselect them. + aMarkData.SetMultiMarkArea(ScRange(nCol, 0, nTab, nLastCol, rDoc.MaxRow(), nTab), false); + + nCol = nLastCol + 1; + } + + SCROW nRow = 0, nLastRow; + while (nRow <= rDoc.MaxRow()) + { + if (rDoc.RowHidden(nRow, nTab, nullptr, &nLastRow)) + // These rows are hidden. Deselect them. + aMarkData.SetMultiMarkArea(ScRange(0, nRow, nTab, rDoc.MaxCol(), nLastRow, nTab), false); + + nRow = nLastRow + 1; + } + + ScRangeList aNewRanges; + aMarkData.FillRangeListWithMarks( &aNewRanges, false ); + return new ScCellRangesObj( pDocShell, aNewRanges ); + } + + return nullptr; +} + +uno::Reference<sheet::XSheetCellRanges> SAL_CALL ScCellRangesBase::queryEmptyCells() +{ + SolarMutexGuard aGuard; + if (pDocShell) + { + ScDocument& rDoc = pDocShell->GetDocument(); + + ScMarkData aMarkData(*GetMarkData()); + + // mark occupied cells + for (size_t i = 0, nCount = aRanges.size(); i < nCount; ++i) + { + ScRange const & rRange = aRanges[ i ]; + + ScCellIterator aIter(rDoc, rRange); + for (bool bHasCell = aIter.first(); bHasCell; bHasCell = aIter.next()) + { + // notes count as non-empty + if (!aIter.isEmpty()) + aMarkData.SetMultiMarkArea(aIter.GetPos(), false); + } + } + + ScRangeList aNewRanges; + // IsMultiMarked is not enough (will not be reset during deselecting) + //if (aMarkData.HasAnyMultiMarks()) // #i20044# should be set for all empty range + aMarkData.FillRangeListWithMarks( &aNewRanges, false ); + + return new ScCellRangesObj( pDocShell, aNewRanges ); // aNewRanges can be empty + } + + return nullptr; +} + +uno::Reference<sheet::XSheetCellRanges> SAL_CALL ScCellRangesBase::queryContentCells( + sal_Int16 nContentFlags ) +{ + SolarMutexGuard aGuard; + if (pDocShell) + { + ScDocument& rDoc = pDocShell->GetDocument(); + + ScMarkData aMarkData(rDoc.GetSheetLimits()); + + // select matching cells + for ( size_t i = 0, nCount = aRanges.size(); i < nCount; ++i ) + { + ScRange const & rRange = aRanges[ i ]; + + ScCellIterator aIter(rDoc, rRange); + for (bool bHasCell = aIter.first(); bHasCell; bHasCell = aIter.next()) + { + bool bAdd = false; + switch (aIter.getType()) + { + case CELLTYPE_STRING: + if ( nContentFlags & sheet::CellFlags::STRING ) + bAdd = true; + break; + case CELLTYPE_EDIT: + if ( (nContentFlags & sheet::CellFlags::STRING) || (nContentFlags & sheet::CellFlags::FORMATTED) ) + bAdd = true; + break; + case CELLTYPE_FORMULA: + if ( nContentFlags & sheet::CellFlags::FORMULA ) + bAdd = true; + break; + case CELLTYPE_VALUE: + if ( (nContentFlags & (sheet::CellFlags::VALUE|sheet::CellFlags::DATETIME)) + == (sheet::CellFlags::VALUE|sheet::CellFlags::DATETIME) ) + bAdd = true; + else + { + // date/time identification + + sal_uLong nIndex = static_cast<sal_uLong>(rDoc.GetAttr( + aIter.GetPos(), ATTR_VALUE_FORMAT)->GetValue()); + SvNumFormatType nTyp = rDoc.GetFormatTable()->GetType(nIndex); + if ((nTyp == SvNumFormatType::DATE) || (nTyp == SvNumFormatType::TIME) || + (nTyp == SvNumFormatType::DATETIME)) + { + if ( nContentFlags & sheet::CellFlags::DATETIME ) + bAdd = true; + } + else + { + if ( nContentFlags & sheet::CellFlags::VALUE ) + bAdd = true; + } + } + break; + default: + { + // added to avoid warnings + } + } + + if (bAdd) + aMarkData.SetMultiMarkArea(aIter.GetPos()); + } + } + + if (nContentFlags & sheet::CellFlags::ANNOTATION) + { + std::vector<sc::NoteEntry> aNotes; + rDoc.GetNotesInRange(aRanges, aNotes); + + for (const auto& i : aNotes) + { + aMarkData.SetMultiMarkArea(i.maPos); + } + } + + ScRangeList aNewRanges; + if (aMarkData.IsMultiMarked()) + aMarkData.FillRangeListWithMarks( &aNewRanges, false ); + + return new ScCellRangesObj( pDocShell, aNewRanges ); // aNewRanges can be empty + } + + return nullptr; +} + +uno::Reference<sheet::XSheetCellRanges> SAL_CALL ScCellRangesBase::queryFormulaCells( + sal_Int32 nResultFlags ) +{ + SolarMutexGuard aGuard; + if (pDocShell) + { + ScDocument& rDoc = pDocShell->GetDocument(); + + ScMarkData aMarkData(rDoc.GetSheetLimits()); + + // select matching cells + for ( size_t i = 0, nCount = aRanges.size(); i < nCount; ++i ) + { + ScRange const & rRange = aRanges[ i ]; + + ScCellIterator aIter(rDoc, rRange); + for (bool bHasCell = aIter.first(); bHasCell; bHasCell = aIter.next()) + { + if (aIter.getType() == CELLTYPE_FORMULA) + { + ScFormulaCell* pFCell = aIter.getFormulaCell(); + bool bAdd = false; + if (pFCell->GetErrCode() != FormulaError::NONE) + { + if ( nResultFlags & sheet::FormulaResult::ERROR ) + bAdd = true; + } + else if (pFCell->IsValue()) + { + if ( nResultFlags & sheet::FormulaResult::VALUE ) + bAdd = true; + } + else // String + { + if ( nResultFlags & sheet::FormulaResult::STRING ) + bAdd = true; + } + + if (bAdd) + aMarkData.SetMultiMarkArea(aIter.GetPos()); + } + } + } + + ScRangeList aNewRanges; + if (aMarkData.IsMultiMarked()) + aMarkData.FillRangeListWithMarks( &aNewRanges, false ); + + return new ScCellRangesObj( pDocShell, aNewRanges ); // aNewRanges can be empty + } + + return nullptr; +} + +uno::Reference<sheet::XSheetCellRanges> ScCellRangesBase::QueryDifferences_Impl( + const table::CellAddress& aCompare, bool bColumnDiff) +{ + if (pDocShell) + { + size_t nRangeCount = aRanges.size(); + size_t i; + ScDocument& rDoc = pDocShell->GetDocument(); + ScMarkData aMarkData(rDoc.GetSheetLimits()); + + SCCOLROW nCmpPos = bColumnDiff ? static_cast<SCCOLROW>(aCompare.Row) : static_cast<SCCOLROW>(aCompare.Column); + + // first select everything, where at all something is in the comparison column + // (in the second step the selection is cancelled for equal cells) + + SCTAB nTab = lcl_FirstTab(aRanges); //! for all tables, if markings per table + ScRange aCmpRange, aCellRange; + if (bColumnDiff) + aCmpRange = ScRange( 0,nCmpPos,nTab, rDoc.MaxCol(),nCmpPos,nTab ); + else + aCmpRange = ScRange( static_cast<SCCOL>(nCmpPos),0,nTab, static_cast<SCCOL>(nCmpPos),rDoc.MaxRow(),nTab ); + ScCellIterator aCmpIter(rDoc, aCmpRange); + for (bool bHasCell = aCmpIter.first(); bHasCell; bHasCell = aCmpIter.next()) + { + SCCOLROW nCellPos = bColumnDiff ? static_cast<SCCOLROW>(aCmpIter.GetPos().Col()) : static_cast<SCCOLROW>(aCmpIter.GetPos().Row()); + if (bColumnDiff) + aCellRange = ScRange( static_cast<SCCOL>(nCellPos),0,nTab, + static_cast<SCCOL>(nCellPos),rDoc.MaxRow(),nTab ); + else + aCellRange = ScRange( 0,nCellPos,nTab, rDoc.MaxCol(),nCellPos,nTab ); + + for (i=0; i<nRangeCount; i++) + { + ScRange aRange( aRanges[ i ] ); + if ( aRange.Intersects( aCellRange ) ) + { + if (bColumnDiff) + { + aRange.aStart.SetCol(static_cast<SCCOL>(nCellPos)); + aRange.aEnd.SetCol(static_cast<SCCOL>(nCellPos)); + } + else + { + aRange.aStart.SetRow(nCellPos); + aRange.aEnd.SetRow(nCellPos); + } + aMarkData.SetMultiMarkArea( aRange ); + } + } + } + + // compare all not empty cells with the comparison column and accordingly + // select or cancel + + ScAddress aCmpAddr; + for (i=0; i<nRangeCount; i++) + { + ScRange const & rRange = aRanges[ i ]; + + ScCellIterator aIter( rDoc, rRange ); + for (bool bHasCell = aIter.first(); bHasCell; bHasCell = aIter.next()) + { + if (bColumnDiff) + aCmpAddr = ScAddress( aIter.GetPos().Col(), nCmpPos, aIter.GetPos().Tab() ); + else + aCmpAddr = ScAddress( static_cast<SCCOL>(nCmpPos), aIter.GetPos().Row(), aIter.GetPos().Tab() ); + + ScRange aOneRange(aIter.GetPos()); + if (!aIter.equalsWithoutFormat(aCmpAddr)) + aMarkData.SetMultiMarkArea( aOneRange ); + else + aMarkData.SetMultiMarkArea( aOneRange, false ); // deselect + } + } + + ScRangeList aNewRanges; + if (aMarkData.IsMultiMarked()) + aMarkData.FillRangeListWithMarks( &aNewRanges, false ); + + return new ScCellRangesObj( pDocShell, aNewRanges ); // aNewRanges can be empty + } + return nullptr; +} + +uno::Reference<sheet::XSheetCellRanges > SAL_CALL ScCellRangesBase::queryColumnDifferences( + const table::CellAddress& aCompare ) +{ + SolarMutexGuard aGuard; + return QueryDifferences_Impl( aCompare, true ); +} + +uno::Reference<sheet::XSheetCellRanges> SAL_CALL ScCellRangesBase::queryRowDifferences( + const table::CellAddress& aCompare ) +{ + SolarMutexGuard aGuard; + return QueryDifferences_Impl( aCompare, false ); +} + +uno::Reference<sheet::XSheetCellRanges> SAL_CALL ScCellRangesBase::queryIntersection( + const table::CellRangeAddress& aRange ) +{ + SolarMutexGuard aGuard; + ScRange aMask( static_cast<SCCOL>(aRange.StartColumn), static_cast<SCROW>(aRange.StartRow), aRange.Sheet, + static_cast<SCCOL>(aRange.EndColumn), static_cast<SCROW>(aRange.EndRow), aRange.Sheet ); + + ScRangeList aNew; + for ( size_t i = 0, nCount = aRanges.size(); i < nCount; ++i ) + { + ScRange aTemp( aRanges[ i ] ); + if ( aTemp.Intersects( aMask ) ) + aNew.Join( ScRange( std::max( aTemp.aStart.Col(), aMask.aStart.Col() ), + std::max( aTemp.aStart.Row(), aMask.aStart.Row() ), + std::max( aTemp.aStart.Tab(), aMask.aStart.Tab() ), + std::min( aTemp.aEnd.Col(), aMask.aEnd.Col() ), + std::min( aTemp.aEnd.Row(), aMask.aEnd.Row() ), + std::min( aTemp.aEnd.Tab(), aMask.aEnd.Tab() ) ) ); + } + + return new ScCellRangesObj( pDocShell, aNew ); // can be empty +} + +// XFormulaQuery + +uno::Reference<sheet::XSheetCellRanges> SAL_CALL ScCellRangesBase::queryPrecedents( + sal_Bool bRecursive ) +{ + SolarMutexGuard aGuard; + if ( pDocShell ) + { + ScDocument& rDoc = pDocShell->GetDocument(); + + ScRangeList aNewRanges(aRanges); + bool bFound; + do + { + bFound = false; + + // aMarkData uses aNewRanges, not aRanges, so GetMarkData can't be used + ScMarkData aMarkData(rDoc.GetSheetLimits()); + aMarkData.MarkFromRangeList( aNewRanges, false ); + + for (size_t nR = 0, nCount = aNewRanges.size(); nR<nCount; ++nR) + { + ScRange const & rRange = aNewRanges[ nR]; + ScCellIterator aIter(rDoc, rRange); + for (bool bHasCell = aIter.first(); bHasCell; bHasCell = aIter.next()) + { + if (aIter.getType() != CELLTYPE_FORMULA) + continue; + + ScDetectiveRefIter aRefIter(rDoc, aIter.getFormulaCell()); + ScRange aRefRange; + while ( aRefIter.GetNextRef( aRefRange) ) + { + if ( bRecursive && !bFound && !aMarkData.IsAllMarked( aRefRange ) ) + bFound = true; + aMarkData.SetMultiMarkArea(aRefRange); + } + } + } + + aMarkData.FillRangeListWithMarks( &aNewRanges, true ); + } + while ( bRecursive && bFound ); + + return new ScCellRangesObj( pDocShell, aNewRanges ); + } + + return nullptr; +} + +uno::Reference<sheet::XSheetCellRanges> SAL_CALL ScCellRangesBase::queryDependents( + sal_Bool bRecursive ) +{ + SolarMutexGuard aGuard; + if ( pDocShell ) + { + ScDocument& rDoc = pDocShell->GetDocument(); + + ScRangeList aNewRanges(aRanges); + bool bFound; + do + { + bFound = false; + + // aMarkData uses aNewRanges, not aRanges, so GetMarkData can't be used + ScMarkData aMarkData(rDoc.GetSheetLimits()); + aMarkData.MarkFromRangeList( aNewRanges, false ); + + SCTAB nTab = lcl_FirstTab(aNewRanges); //! all tables + + ScCellIterator aCellIter( rDoc, ScRange(0, 0, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab) ); + for (bool bHasCell = aCellIter.first(); bHasCell; bHasCell = aCellIter.next()) + { + if (aCellIter.getType() != CELLTYPE_FORMULA) + continue; + + bool bMark = false; + ScDetectiveRefIter aIter(rDoc, aCellIter.getFormulaCell()); + ScRange aRefRange; + while ( aIter.GetNextRef( aRefRange) && !bMark ) + { + size_t nRangesCount = aNewRanges.size(); + for (size_t nR = 0; nR < nRangesCount; ++nR) + { + ScRange const & rRange = aNewRanges[ nR ]; + if (rRange.Intersects(aRefRange)) + { + bMark = true; // depending on part of Range + break; + } + } + } + if (bMark) + { + ScRange aCellRange(aCellIter.GetPos()); + if ( bRecursive && !bFound && !aMarkData.IsAllMarked( aCellRange ) ) + bFound = true; + aMarkData.SetMultiMarkArea(aCellRange); + } + } + + aMarkData.FillRangeListWithMarks( &aNewRanges, true ); + } + while ( bRecursive && bFound ); + + return new ScCellRangesObj( pDocShell, aNewRanges ); + } + + return nullptr; +} + +// XSearchable + +uno::Reference<util::XSearchDescriptor> SAL_CALL ScCellRangesBase::createSearchDescriptor() +{ + return new ScCellSearchObj; +} + +uno::Reference<container::XIndexAccess> SAL_CALL ScCellRangesBase::findAll( + const uno::Reference<util::XSearchDescriptor>& xDesc ) +{ + SolarMutexGuard aGuard; + // should we return Null if nothing is found(?) + uno::Reference<container::XIndexAccess> xRet; + if ( pDocShell && xDesc.is() ) + { + ScCellSearchObj* pSearch = dynamic_cast<ScCellSearchObj*>( xDesc.get() ); + if (pSearch) + { + SvxSearchItem* pSearchItem = pSearch->GetSearchItem(); + if (pSearchItem) + { + ScDocument& rDoc = pDocShell->GetDocument(); + pSearchItem->SetCommand( SvxSearchCmd::FIND_ALL ); + // always only within this object + pSearchItem->SetSelection( !lcl_WholeSheet(rDoc, aRanges) ); + + ScMarkData aMark(*GetMarkData()); + + OUString aDummyUndo; + ScRangeList aMatchedRanges; + SCCOL nCol = 0; + SCROW nRow = 0; + SCTAB nTab = 0; + bool bMatchedRangesWereClamped = false; + bool bFound = rDoc.SearchAndReplace( + *pSearchItem, nCol, nRow, nTab, aMark, aMatchedRanges, aDummyUndo, nullptr, bMatchedRangesWereClamped); + if (bFound) + { + // on findAll always CellRanges no matter how much has been found + xRet.set(new ScCellRangesObj( pDocShell, aMatchedRanges )); + } + } + } + } + return xRet; +} + +uno::Reference<uno::XInterface> ScCellRangesBase::Find_Impl( + const uno::Reference<util::XSearchDescriptor>& xDesc, + const ScAddress* pLastPos ) +{ + uno::Reference<uno::XInterface> xRet; + if ( pDocShell && xDesc.is() ) + { + ScCellSearchObj* pSearch = dynamic_cast<ScCellSearchObj*>( xDesc.get() ); + if (pSearch) + { + SvxSearchItem* pSearchItem = pSearch->GetSearchItem(); + if (pSearchItem) + { + ScDocument& rDoc = pDocShell->GetDocument(); + pSearchItem->SetCommand( SvxSearchCmd::FIND ); + // only always in this object + pSearchItem->SetSelection( !lcl_WholeSheet(rDoc, aRanges) ); + + ScMarkData aMark(*GetMarkData()); + + SCCOL nCol; + SCROW nRow; + SCTAB nTab; + if (pLastPos) + pLastPos->GetVars( nCol, nRow, nTab ); + else + { + nTab = lcl_FirstTab(aRanges); //! multiple sheets? + rDoc.GetSearchAndReplaceStart( *pSearchItem, nCol, nRow ); + } + + OUString aDummyUndo; + ScRangeList aMatchedRanges; + bool bMatchedRangesWereClamped; + bool bFound = rDoc.SearchAndReplace( + *pSearchItem, nCol, nRow, nTab, aMark, aMatchedRanges, aDummyUndo, nullptr, bMatchedRangesWereClamped); + if (bFound) + { + ScAddress aFoundPos( nCol, nRow, nTab ); + xRet.set(cppu::getXWeak(new ScCellObj( pDocShell, aFoundPos ))); + } + } + } + } + return xRet; +} + +uno::Reference<uno::XInterface> SAL_CALL ScCellRangesBase::findFirst( + const uno::Reference<util::XSearchDescriptor>& xDesc ) +{ + SolarMutexGuard aGuard; + return Find_Impl( xDesc, nullptr ); +} + +uno::Reference<uno::XInterface> SAL_CALL ScCellRangesBase::findNext( + const uno::Reference<uno::XInterface>& xStartAt, + const uno::Reference<util::XSearchDescriptor >& xDesc ) +{ + SolarMutexGuard aGuard; + if ( xStartAt.is() ) + { + ScCellRangesBase* pRangesImp = dynamic_cast<ScCellRangesBase*>( xStartAt.get() ); + if ( pRangesImp && pRangesImp->GetDocShell() == pDocShell ) + { + const ScRangeList& rStartRanges = pRangesImp->GetRangeList(); + if ( rStartRanges.size() == 1 ) + { + ScAddress aStartPos = rStartRanges[ 0 ].aStart; + return Find_Impl( xDesc, &aStartPos ); + } + } + } + return nullptr; +} + +// XReplaceable + +uno::Reference<util::XReplaceDescriptor> SAL_CALL ScCellRangesBase::createReplaceDescriptor() +{ + return new ScCellSearchObj; +} + +sal_Int32 SAL_CALL ScCellRangesBase::replaceAll( const uno::Reference<util::XSearchDescriptor>& xDesc ) +{ + SolarMutexGuard aGuard; + sal_uInt64 nReplaced = 0; + if ( pDocShell && xDesc.is() ) + { + ScCellSearchObj* pSearch = dynamic_cast<ScCellSearchObj*>( xDesc.get() ); + if (pSearch) + { + SvxSearchItem* pSearchItem = pSearch->GetSearchItem(); + if (pSearchItem) + { + ScDocument& rDoc = pDocShell->GetDocument(); + bool bUndo(rDoc.IsUndoEnabled()); + pSearchItem->SetCommand( SvxSearchCmd::REPLACE_ALL ); + // only always in this object + pSearchItem->SetSelection( !lcl_WholeSheet(rDoc, aRanges) ); + + ScMarkData aMark(*GetMarkData()); + + SCTAB nTabCount = rDoc.GetTableCount(); + bool bProtected = !pDocShell->IsEditable(); + for (const auto& rTab : aMark) + { + if (rTab >= nTabCount) + break; + if ( rDoc.IsTabProtected(rTab) ) + bProtected = true; + } + if (bProtected) + { + //! Exception, or what? + } + else + { + SCTAB nTab = aMark.GetFirstSelected(); // do not use if SearchAndReplace + SCCOL nCol = 0; + SCROW nRow = 0; + + OUString aUndoStr; + ScDocumentUniquePtr pUndoDoc; + if (bUndo) + { + pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO )); + pUndoDoc->InitUndo( rDoc, nTab, nTab ); + } + for (const auto& rTab : aMark) + { + if (rTab >= nTabCount) + break; + if (rTab != nTab && bUndo) + pUndoDoc->AddUndoTab( rTab, rTab ); + } + std::unique_ptr<ScMarkData> pUndoMark; + if (bUndo) + pUndoMark.reset(new ScMarkData(aMark)); + + bool bFound = false; + if (bUndo) + { + ScRangeList aMatchedRanges; + bool bMatchedRangesWereClamped; + bFound = rDoc.SearchAndReplace( + *pSearchItem, nCol, nRow, nTab, aMark, aMatchedRanges, aUndoStr, pUndoDoc.get(), bMatchedRangesWereClamped ); + } + if (bFound) + { + nReplaced = pUndoDoc->GetCellCount(); + + pDocShell->GetUndoManager()->AddUndoAction( + std::make_unique<ScUndoReplace>( pDocShell, *pUndoMark, nCol, nRow, nTab, + aUndoStr, std::move(pUndoDoc), pSearchItem ) ); + + pDocShell->PostPaintGridAll(); + pDocShell->SetDocumentModified(); + } + } + } + } + } + return nReplaced; +} + +ScCellRangesObj::ScCellRangesObj(ScDocShell* pDocSh, const ScRangeList& rR) + : ScCellRangesBase(pDocSh, rR) +{ +} + +ScCellRangesObj::~ScCellRangesObj() +{ +} + +void ScCellRangesObj::RefChanged() +{ + ScCellRangesBase::RefChanged(); +} + +uno::Any SAL_CALL ScCellRangesObj::queryInterface( const uno::Type& rType ) +{ + uno::Any aReturn = ::cppu::queryInterface(rType, + static_cast<sheet::XSheetCellRangeContainer*>(this), + static_cast<sheet::XSheetCellRanges*>(this), + static_cast<container::XIndexAccess*>(this), + static_cast<container::XElementAccess*>(static_cast<container::XIndexAccess*>(this)), + static_cast<container::XEnumerationAccess*>(this), + static_cast<container::XNameContainer*>(this), + static_cast<container::XNameReplace*>(this), + static_cast<container::XNameAccess*>(this)); + if ( aReturn.hasValue() ) + return aReturn; + + return ScCellRangesBase::queryInterface( rType ); +} + +void SAL_CALL ScCellRangesObj::acquire() noexcept +{ + ScCellRangesBase::acquire(); +} + +void SAL_CALL ScCellRangesObj::release() noexcept +{ + ScCellRangesBase::release(); +} + +uno::Sequence<uno::Type> SAL_CALL ScCellRangesObj::getTypes() +{ + static const uno::Sequence<uno::Type> aTypes = comphelper::concatSequences( + ScCellRangesBase::getTypes(), + uno::Sequence<uno::Type> + { + cppu::UnoType<sheet::XSheetCellRangeContainer>::get(), + cppu::UnoType<container::XNameContainer>::get(), + cppu::UnoType<container::XEnumerationAccess>::get() + } ); + return aTypes; +} + +uno::Sequence<sal_Int8> SAL_CALL ScCellRangesObj::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +// XCellRanges + +rtl::Reference<ScCellRangeObj> ScCellRangesObj::GetObjectByIndex_Impl(sal_Int32 nIndex) const +{ + ScDocShell* pDocSh = GetDocShell(); + const ScRangeList& rRanges = GetRangeList(); + if ( pDocSh && nIndex >= 0 && nIndex < sal::static_int_cast<sal_Int32>(rRanges.size()) ) + { + ScRange const & rRange = rRanges[ nIndex ]; + if ( rRange.aStart == rRange.aEnd ) + return new ScCellObj( pDocSh, rRange.aStart ); + else + return new ScCellRangeObj( pDocSh, rRange ); + } + + return nullptr; // no DocShell or wrong index +} + +uno::Sequence<table::CellRangeAddress> SAL_CALL ScCellRangesObj::getRangeAddresses() +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + const ScRangeList& rRanges = GetRangeList(); + size_t nCount = rRanges.size(); + if ( pDocSh && nCount ) + { + table::CellRangeAddress aRangeAddress; + uno::Sequence<table::CellRangeAddress> aSeq(nCount); + table::CellRangeAddress* pAry = aSeq.getArray(); + for ( size_t i=0; i < nCount; i++) + { + ScUnoConversion::FillApiRange( aRangeAddress, rRanges[ i ] ); + pAry[i] = aRangeAddress; + } + return aSeq; + } + + return {}; // can be empty +} + +uno::Reference<container::XEnumerationAccess> SAL_CALL ScCellRangesObj::getCells() +{ + SolarMutexGuard aGuard; + + // getCells with empty range list is possible (no exception), + // the resulting enumeration just has no elements + // (same behaviour as a valid range with no cells) + // This is handled in ScCellsEnumeration ctor. + + const ScRangeList& rRanges = GetRangeList(); + ScDocShell* pDocSh = GetDocShell(); + if (pDocSh) + return new ScCellsObj( pDocSh, rRanges ); + return nullptr; +} + +OUString SAL_CALL ScCellRangesObj::getRangeAddressesAsString() +{ + SolarMutexGuard aGuard; + OUString aString; + ScDocShell* pDocSh = GetDocShell(); + const ScRangeList& rRanges = GetRangeList(); + if (pDocSh) + rRanges.Format( aString, ScRefFlags::VALID | ScRefFlags::TAB_3D, pDocSh->GetDocument() ); + return aString; +} + +// XSheetCellRangeContainer + +void SAL_CALL ScCellRangesObj::addRangeAddress( const table::CellRangeAddress& rRange, + sal_Bool bMergeRanges ) +{ + SolarMutexGuard aGuard; + ScRange aRange(static_cast<SCCOL>(rRange.StartColumn), + static_cast<SCROW>(rRange.StartRow), + static_cast<SCTAB>(rRange.Sheet), + static_cast<SCCOL>(rRange.EndColumn), + static_cast<SCROW>(rRange.EndRow), + static_cast<SCTAB>(rRange.Sheet)); + AddRange(aRange, bMergeRanges); +} + +static void lcl_RemoveNamedEntry( std::vector<ScCellRangesObj::ScNamedEntry>& rNamedEntries, const ScRange& rRange ) +{ + sal_uInt16 nCount = rNamedEntries.size(); + for ( sal_uInt16 n=nCount; n--; ) + if ( rNamedEntries[n].GetRange() == rRange ) + rNamedEntries.erase( rNamedEntries.begin() + n ); +} + +void SAL_CALL ScCellRangesObj::removeRangeAddress( const table::CellRangeAddress& rRange ) +{ + SolarMutexGuard aGuard; + const ScRangeList& rRanges = GetRangeList(); + + ScRangeList aSheetRanges; + ScRangeList aNotSheetRanges; + for (size_t i = 0; i < rRanges.size(); ++i) + { + if (rRanges[ i].aStart.Tab() == rRange.Sheet) + { + aSheetRanges.push_back( rRanges[ i ] ); + } + else + { + aNotSheetRanges.push_back( rRanges[ i ] ); + } + } + ScMarkData aMarkData(GetDocument()->GetSheetLimits()); + aMarkData.MarkFromRangeList( aSheetRanges, false ); + ScRange aRange(static_cast<SCCOL>(rRange.StartColumn), + static_cast<SCROW>(rRange.StartRow), + static_cast<SCTAB>(rRange.Sheet), + static_cast<SCCOL>(rRange.EndColumn), + static_cast<SCROW>(rRange.EndRow), + static_cast<SCTAB>(rRange.Sheet)); + if (aMarkData.GetTableSelect( aRange.aStart.Tab() )) + { + aMarkData.MarkToMulti(); + if (!aMarkData.IsAllMarked( aRange ) ) + throw container::NoSuchElementException(); + + aMarkData.SetMultiMarkArea( aRange, false ); + lcl_RemoveNamedEntry(m_aNamedEntries, aRange); + + } + SetNewRanges(aNotSheetRanges); + ScRangeList aNew; + aMarkData.FillRangeListWithMarks( &aNew, false ); + for ( size_t j = 0; j < aNew.size(); ++j) + { + AddRange(aNew[ j ], false); + } +} + +void SAL_CALL ScCellRangesObj::addRangeAddresses( const uno::Sequence<table::CellRangeAddress >& rRanges, + sal_Bool bMergeRanges ) +{ + SolarMutexGuard aGuard; + for (const table::CellRangeAddress& rRange : rRanges) + { + ScRange aRange(static_cast<SCCOL>(rRange.StartColumn), + static_cast<SCROW>(rRange.StartRow), + static_cast<SCTAB>(rRange.Sheet), + static_cast<SCCOL>(rRange.EndColumn), + static_cast<SCROW>(rRange.EndRow), + static_cast<SCTAB>(rRange.Sheet)); + AddRange(aRange, bMergeRanges); + } +} + +void SAL_CALL ScCellRangesObj::removeRangeAddresses( const uno::Sequence<table::CellRangeAddress >& rRangeSeq ) +{ + // use sometimes a better/faster implementation + for (const table::CellRangeAddress& rRange : rRangeSeq) + { + removeRangeAddress(rRange); + } +} + +// XNameContainer + +static void lcl_RemoveNamedEntry( std::vector<ScCellRangesObj::ScNamedEntry>& rNamedEntries, std::u16string_view rName ) +{ + sal_uInt16 nCount = rNamedEntries.size(); + for ( sal_uInt16 n=nCount; n--; ) + if ( rNamedEntries[n].GetName() == rName ) + rNamedEntries.erase( rNamedEntries.begin() + n ); +} + +void SAL_CALL ScCellRangesObj::insertByName( const OUString& aName, const uno::Any& aElement ) +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + bool bDone = false; + + //! Type of aElement can be some specific interface instead of XInterface + + uno::Reference<uno::XInterface> xInterface(aElement, uno::UNO_QUERY); + if ( pDocSh && xInterface.is() ) + { + ScCellRangesBase* pRangesImp = dynamic_cast<ScCellRangesBase*>( xInterface.get() ); + if ( pRangesImp && pRangesImp->GetDocShell() == pDocSh ) + { + // if explicit name is given and already existing, throw exception + + if ( !aName.isEmpty() ) + { + size_t nNamedCount = m_aNamedEntries.size(); + for (size_t n = 0; n < nNamedCount; n++) + { + if (m_aNamedEntries[n].GetName() == aName) + throw container::ElementExistException(); + } + } + + ScRangeList aNew(GetRangeList()); + const ScRangeList& rAddRanges = pRangesImp->GetRangeList(); + size_t nAddCount = rAddRanges.size(); + for ( size_t i = 0; i < nAddCount; i++ ) + aNew.Join( rAddRanges[ i ] ); + SetNewRanges(aNew); + bDone = true; + + if ( !aName.isEmpty() && nAddCount == 1 ) + { + // if a name is given, also insert into list of named entries + // (only possible for a single range) + // name is not in m_aNamedEntries (tested above) + m_aNamedEntries.emplace_back( ScNamedEntry{aName, rAddRanges[ 0 ]} ); + } + } + } + + if (!bDone) + { + // invalid element - double names are handled above + throw lang::IllegalArgumentException(); + } +} + +static bool lcl_FindRangeByName( const ScRangeList& rRanges, ScDocShell* pDocSh, + std::u16string_view rName, size_t& rIndex ) +{ + if (pDocSh) + { + OUString aRangeStr; + ScDocument& rDoc = pDocSh->GetDocument(); + for ( size_t i = 0, nCount = rRanges.size(); i < nCount; i++ ) + { + aRangeStr = rRanges[ i ].Format(rDoc, ScRefFlags::VALID | ScRefFlags::TAB_3D); + if ( aRangeStr == rName ) + { + rIndex = i; + return true; + } + } + } + return false; +} + +static bool lcl_FindRangeOrEntry( const std::vector<ScCellRangesObj::ScNamedEntry>& rNamedEntries, + const ScRangeList& rRanges, ScDocShell* pDocSh, + const OUString& rName, ScRange& rFound ) +{ + // exact range in list? + + size_t nIndex = 0; + if ( lcl_FindRangeByName( rRanges, pDocSh, rName, nIndex ) ) + { + rFound = rRanges[ nIndex ]; + return true; + } + + // range contained in selection? (sheet must be specified) + + ScRange aCellRange; + ScRefFlags nParse = aCellRange.ParseAny( rName, pDocSh->GetDocument() ); + if ( (nParse & ( ScRefFlags::VALID | ScRefFlags::TAB_3D )) + == ( ScRefFlags::VALID | ScRefFlags::TAB_3D )) + { + ScMarkData aMarkData(pDocSh->GetDocument().GetSheetLimits()); + aMarkData.MarkFromRangeList( rRanges, false ); + if ( aMarkData.IsAllMarked( aCellRange ) ) + { + rFound = aCellRange; + return true; + } + } + + // named entry in this object? + + for (const auto & rNamedEntry : rNamedEntries) + if ( rNamedEntry.GetName() == rName ) + { + // test if named entry is contained in rRanges + + const ScRange& rComp = rNamedEntry.GetRange(); + ScMarkData aMarkData(pDocSh->GetDocument().GetSheetLimits()); + aMarkData.MarkFromRangeList( rRanges, false ); + if ( aMarkData.IsAllMarked( rComp ) ) + { + rFound = rComp; + return true; + } + } + + return false; // not found +} + +void SAL_CALL ScCellRangesObj::removeByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + bool bDone = false; + ScDocShell* pDocSh = GetDocShell(); + const ScRangeList& rRanges = GetRangeList(); + size_t nIndex = 0; + if ( lcl_FindRangeByName( rRanges, pDocSh, aName, nIndex ) ) + { + // skip a single range + ScRangeList aNew; + for ( size_t i = 0, nCount = rRanges.size(); i < nCount; i++ ) + if (i != nIndex) + aNew.push_back( rRanges[ i ] ); + SetNewRanges(aNew); + bDone = true; + } + else if (pDocSh) + { + // deselect any ranges (parsed or named entry) + ScRangeList aDiff; + bool bValid = ( aDiff.Parse( aName, pDocSh->GetDocument() ) & ScRefFlags::VALID ) + == ScRefFlags::VALID; + if (!bValid) + { + sal_uInt16 nCount = m_aNamedEntries.size(); + for (sal_uInt16 n=0; n<nCount && !bValid; n++) + if (m_aNamedEntries[n].GetName() == aName) + { + aDiff.RemoveAll(); + aDiff.push_back(m_aNamedEntries[n].GetRange()); + bValid = true; + } + } + if ( bValid ) + { + ScMarkData aMarkData(GetDocument()->GetSheetLimits()); + aMarkData.MarkFromRangeList( rRanges, false ); + + for ( size_t i = 0, nDiffCount = aDiff.size(); i < nDiffCount; i++ ) + { + ScRange const & rDiffRange = aDiff[ i ]; + if (aMarkData.GetTableSelect( rDiffRange.aStart.Tab() )) + aMarkData.SetMultiMarkArea( rDiffRange, false ); + } + + ScRangeList aNew; + aMarkData.FillRangeListWithMarks( &aNew, false ); + SetNewRanges(aNew); + + bDone = true; //! error if range was not selected before? + } + } + + if (!m_aNamedEntries.empty()) + lcl_RemoveNamedEntry(m_aNamedEntries, aName); + + if (!bDone) + throw container::NoSuchElementException(); // not found +} + +// XNameReplace + +void SAL_CALL ScCellRangesObj::replaceByName( const OUString& aName, const uno::Any& aElement ) +{ + SolarMutexGuard aGuard; + //! combine? + removeByName( aName ); + insertByName( aName, aElement ); +} + +// XNameAccess + +uno::Any SAL_CALL ScCellRangesObj::getByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + uno::Any aRet; + + ScDocShell* pDocSh = GetDocShell(); + const ScRangeList& rRanges = GetRangeList(); + ScRange aRange; + if (!lcl_FindRangeOrEntry(m_aNamedEntries, rRanges, + pDocSh, aName, aRange)) + throw container::NoSuchElementException(); + + uno::Reference<table::XCellRange> xRange; + if ( aRange.aStart == aRange.aEnd ) + xRange.set(new ScCellObj( pDocSh, aRange.aStart )); + else + xRange.set(new ScCellRangeObj( pDocSh, aRange )); + aRet <<= xRange; + + return aRet; +} + +static bool lcl_FindEntryName( const std::vector<ScCellRangesObj::ScNamedEntry>& rNamedEntries, + const ScRange& rRange, OUString& rName ) +{ + sal_uInt16 nCount = rNamedEntries.size(); + for (sal_uInt16 i=0; i<nCount; i++) + if (rNamedEntries[i].GetRange() == rRange) + { + rName = rNamedEntries[i].GetName(); + return true; + } + return false; +} + +uno::Sequence<OUString> SAL_CALL ScCellRangesObj::getElementNames() +{ + SolarMutexGuard aGuard; + + ScDocShell* pDocSh = GetDocShell(); + const ScRangeList& rRanges = GetRangeList(); + if (pDocSh) + { + OUString aRangeStr; + ScDocument& rDoc = pDocSh->GetDocument(); + size_t nCount = rRanges.size(); + + uno::Sequence<OUString> aSeq(nCount); + OUString* pAry = aSeq.getArray(); + for (size_t i=0; i < nCount; i++) + { + // use given name if for exactly this range, otherwise just format + ScRange const & rRange = rRanges[ i ]; + if (m_aNamedEntries.empty() || + !lcl_FindEntryName(m_aNamedEntries, rRange, aRangeStr)) + { + aRangeStr = rRange.Format(rDoc, ScRefFlags::VALID | ScRefFlags::TAB_3D); + } + pAry[i] = aRangeStr; + } + return aSeq; + } + return {}; +} + +sal_Bool SAL_CALL ScCellRangesObj::hasByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + const ScRangeList& rRanges = GetRangeList(); + ScRange aRange; + return lcl_FindRangeOrEntry(m_aNamedEntries, rRanges, pDocSh, + aName, aRange); +} + +// XEnumerationAccess + +uno::Reference<container::XEnumeration> SAL_CALL ScCellRangesObj::createEnumeration() +{ + SolarMutexGuard aGuard; + return new ScIndexEnumeration(this, "com.sun.star.sheet.SheetCellRangesEnumeration"); +} + +// XIndexAccess + +sal_Int32 SAL_CALL ScCellRangesObj::getCount() +{ + SolarMutexGuard aGuard; + const ScRangeList& rRanges = GetRangeList(); + return rRanges.size(); +} + +uno::Any SAL_CALL ScCellRangesObj::getByIndex( sal_Int32 nIndex ) +{ + SolarMutexGuard aGuard; + uno::Reference<table::XCellRange> xRange(GetObjectByIndex_Impl(nIndex)); + if (!xRange.is()) + throw lang::IndexOutOfBoundsException(); + + return uno::Any(xRange); + +} + +uno::Type SAL_CALL ScCellRangesObj::getElementType() +{ + return cppu::UnoType<table::XCellRange>::get(); +} + +sal_Bool SAL_CALL ScCellRangesObj::hasElements() +{ + SolarMutexGuard aGuard; + const ScRangeList& rRanges = GetRangeList(); + return !rRanges.empty(); +} + +// XServiceInfo +OUString SAL_CALL ScCellRangesObj::getImplementationName() +{ + return "ScCellRangesObj"; +} + +sal_Bool SAL_CALL ScCellRangesObj::supportsService( const OUString& rServiceName ) +{ + return cppu::supportsService(this, rServiceName); +} + +uno::Sequence<OUString> SAL_CALL ScCellRangesObj::getSupportedServiceNames() +{ + return {SCSHEETCELLRANGES_SERVICE, + SCCELLPROPERTIES_SERVICE, + SCCHARPROPERTIES_SERVICE, + SCPARAPROPERTIES_SERVICE}; +} + +uno::Reference<table::XCellRange> ScCellRangeObj::CreateRangeFromDoc( const ScDocument& rDoc, const ScRange& rR ) +{ + if ( ScDocShell* pDocShell = rDoc.GetDocumentShell() ) + return new ScCellRangeObj( pDocShell, rR ); + return nullptr; +} + +ScCellRangeObj::ScCellRangeObj(ScDocShell* pDocSh, const ScRange& rR) : + ScCellRangesBase( pDocSh, rR ), + pRangePropSet( lcl_GetRangePropertySet() ), + aRange( rR ) +{ + aRange.PutInOrder(); // beginning / end correct +} + +ScCellRangeObj::~ScCellRangeObj() +{ +} + +void ScCellRangeObj::RefChanged() +{ + ScCellRangesBase::RefChanged(); + + const ScRangeList& rRanges = GetRangeList(); + OSL_ENSURE(rRanges.size() == 1, "What ranges ?!?!"); + if ( !rRanges.empty() ) + { + const ScRange & rFirst = rRanges[0]; + aRange = rFirst; + aRange.PutInOrder(); + } +} + +uno::Any SAL_CALL ScCellRangeObj::queryInterface( const uno::Type& rType ) +{ + uno::Any aReturn = ::cppu::queryInterface(rType, + static_cast<sheet::XCellRangeAddressable*>(this), + static_cast<table::XCellRange*>(this), + static_cast<sheet::XSheetCellRange*>(this), + static_cast<sheet::XArrayFormulaRange*>(this), + static_cast<sheet::XArrayFormulaTokens*>(this), + static_cast<sheet::XCellRangeData*>(this), + static_cast<sheet::XCellRangeFormula*>(this), + static_cast<sheet::XMultipleOperation*>(this), + static_cast<util::XMergeable*>(this), + static_cast<sheet::XCellSeries*>(this), + static_cast<table::XAutoFormattable*>(this), + static_cast<util::XSortable*>(this), + static_cast<sheet::XSheetFilterableEx*>(this), + static_cast<sheet::XSheetFilterable*>(this), + static_cast<sheet::XSubTotalCalculatable*>(this), + static_cast<table::XColumnRowRange*>(this), + static_cast<util::XImportable*>(this), + static_cast<sheet::XCellFormatRangesSupplier*>(this), + static_cast<sheet::XUniqueCellFormatRangesSupplier*>(this)); + if ( aReturn.hasValue() ) + return aReturn; + + return ScCellRangesBase::queryInterface( rType ); +} + +void SAL_CALL ScCellRangeObj::acquire() noexcept +{ + ScCellRangesBase::acquire(); +} + +void SAL_CALL ScCellRangeObj::release() noexcept +{ + ScCellRangesBase::release(); +} + +uno::Sequence<uno::Type> SAL_CALL ScCellRangeObj::getTypes() +{ + static const uno::Sequence<uno::Type> aTypes = comphelper::concatSequences( + ScCellRangesBase::getTypes(), + uno::Sequence<uno::Type> + { + cppu::UnoType<sheet::XCellRangeAddressable>::get(), + cppu::UnoType<sheet::XSheetCellRange>::get(), + cppu::UnoType<sheet::XArrayFormulaRange>::get(), + cppu::UnoType<sheet::XArrayFormulaTokens>::get(), + cppu::UnoType<sheet::XCellRangeData>::get(), + cppu::UnoType<sheet::XCellRangeFormula>::get(), + cppu::UnoType<sheet::XMultipleOperation>::get(), + cppu::UnoType<util::XMergeable>::get(), + cppu::UnoType<sheet::XCellSeries>::get(), + cppu::UnoType<table::XAutoFormattable>::get(), + cppu::UnoType<util::XSortable>::get(), + cppu::UnoType<sheet::XSheetFilterableEx>::get(), + cppu::UnoType<sheet::XSubTotalCalculatable>::get(), + cppu::UnoType<table::XColumnRowRange>::get(), + cppu::UnoType<util::XImportable>::get(), + cppu::UnoType<sheet::XCellFormatRangesSupplier>::get(), + cppu::UnoType<sheet::XUniqueCellFormatRangesSupplier>::get() + } ); + return aTypes; +} + +uno::Sequence<sal_Int8> SAL_CALL ScCellRangeObj::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +// XCellRange + +// ColumnCount / RowCount vanished +//! are used in Writer for tables ??? + +uno::Reference<table::XCell> ScCellRangeObj::GetCellByPosition_Impl( + sal_Int32 nColumn, sal_Int32 nRow ) +{ + ScDocShell* pDocSh = GetDocShell(); + if (!pDocSh) + throw uno::RuntimeException(); + + if ( nColumn >= 0 && nRow >= 0 ) + { + sal_Int32 nPosX = aRange.aStart.Col() + nColumn; + sal_Int32 nPosY = aRange.aStart.Row() + nRow; + + if ( nPosX <= aRange.aEnd.Col() && nPosY <= aRange.aEnd.Row() ) + { + ScAddress aNew( static_cast<SCCOL>(nPosX), static_cast<SCROW>(nPosY), aRange.aStart.Tab() ); + return new ScCellObj( pDocSh, aNew ); + } + } + + throw lang::IndexOutOfBoundsException(); +} + +uno::Reference<table::XCell> SAL_CALL ScCellRangeObj::getCellByPosition( + sal_Int32 nColumn, sal_Int32 nRow ) +{ + SolarMutexGuard aGuard; + + return GetCellByPosition_Impl(nColumn, nRow); +} + +uno::Reference<table::XCellRange> SAL_CALL ScCellRangeObj::getCellRangeByPosition( + sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom ) +{ + SolarMutexGuard aGuard; + + ScDocShell* pDocSh = GetDocShell(); + if (!pDocSh) + throw uno::RuntimeException(); + + if ( nLeft >= 0 && nTop >= 0 && nRight >= 0 && nBottom >= 0 ) + { + sal_Int32 nStartX = aRange.aStart.Col() + nLeft; + sal_Int32 nStartY = aRange.aStart.Row() + nTop; + sal_Int32 nEndX = aRange.aStart.Col() + nRight; + sal_Int32 nEndY = aRange.aStart.Row() + nBottom; + + if ( nStartX <= nEndX && nEndX <= aRange.aEnd.Col() && + nStartY <= nEndY && nEndY <= aRange.aEnd.Row() ) + { + ScRange aNew( static_cast<SCCOL>(nStartX), static_cast<SCROW>(nStartY), aRange.aStart.Tab(), + static_cast<SCCOL>(nEndX), static_cast<SCROW>(nEndY), aRange.aEnd.Tab() ); + return new ScCellRangeObj( pDocSh, aNew ); + } + } + + throw lang::IndexOutOfBoundsException(); +} + +uno::Reference<table::XCellRange> SAL_CALL ScCellRangeObj::getCellRangeByName( + const OUString& aName ) +{ + return getCellRangeByName( aName, ScAddress::detailsOOOa1 ); +} + +uno::Reference<table::XCellRange> ScCellRangeObj::getCellRangeByName( + const OUString& aName, const ScAddress::Details& rDetails ) +{ + // name refers to the whole document (with the range's table as default), + // valid only if the range is within this range + + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + { + ScDocument& rDoc = pDocSh->GetDocument(); + SCTAB nTab = aRange.aStart.Tab(); + + ScRange aCellRange; + bool bFound = false; + ScRefFlags nParse = aCellRange.ParseAny( aName, rDoc, rDetails ); + if ( nParse & ScRefFlags::VALID ) + { + if ( !(nParse & ScRefFlags::TAB_3D) ) // no sheet specified -> this sheet + { + aCellRange.aStart.SetTab(nTab); + aCellRange.aEnd.SetTab(nTab); + } + bFound = true; + } + else + { + if ( ScRangeUtil::MakeRangeFromName( aName, rDoc, nTab, aCellRange, RUTL_NAMES, rDetails) || + ScRangeUtil::MakeRangeFromName( aName, rDoc, nTab, aCellRange, RUTL_DBASE, rDetails)) + bFound = true; + } + + if (bFound) // valid only if within this object's range + { + if (!aRange.Contains(aCellRange)) + bFound = false; + } + + if (bFound) + { + if ( aCellRange.aStart == aCellRange.aEnd ) + return new ScCellObj( pDocSh, aCellRange.aStart ); + else + return new ScCellRangeObj( pDocSh, aCellRange ); + } + } + + throw uno::RuntimeException(); +} + +// XColumnRowRange + +uno::Reference<table::XTableColumns> SAL_CALL ScCellRangeObj::getColumns() +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if (pDocSh) + return new ScTableColumnsObj( pDocSh, aRange.aStart.Tab(), + aRange.aStart.Col(), aRange.aEnd.Col() ); + + OSL_FAIL("Document invalid"); + return nullptr; +} + +uno::Reference<table::XTableRows> SAL_CALL ScCellRangeObj::getRows() +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if (pDocSh) + return new ScTableRowsObj( pDocSh, aRange.aStart.Tab(), + aRange.aStart.Row(), aRange.aEnd.Row() ); + + OSL_FAIL("Document invalid"); + return nullptr; +} + +// XAddressableCellRange + +table::CellRangeAddress SAL_CALL ScCellRangeObj::getRangeAddress() +{ + SolarMutexGuard aGuard; + table::CellRangeAddress aRet; + ScUnoConversion::FillApiRange( aRet, aRange ); + return aRet; +} + +// XSheetCellRange + +uno::Reference<sheet::XSpreadsheet> SAL_CALL ScCellRangeObj::getSpreadsheet() +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if (pDocSh) + return new ScTableSheetObj( pDocSh, aRange.aStart.Tab() ); + + OSL_FAIL("Document invalid"); + return nullptr; +} + +// XArrayFormulaRange + +OUString SAL_CALL ScCellRangeObj::getArrayFormula() +{ + SolarMutexGuard aGuard; + + // Matrix formula if clearly part of a matrix (so when start and end of + // the block belong to the same matrix) else empty string. + + ScDocShell* pDocSh = GetDocShell(); + if (!pDocSh) + return OUString(); + + ScDocument& rDoc = pDocSh->GetDocument(); + ScRefCellValue aCell1(rDoc, aRange.aStart); + ScRefCellValue aCell2(rDoc, aRange.aEnd); + if (aCell1.getType() == CELLTYPE_FORMULA && aCell2.getType() == CELLTYPE_FORMULA) + { + const ScFormulaCell* pFCell1 = aCell1.getFormula(); + const ScFormulaCell* pFCell2 = aCell2.getFormula(); + ScAddress aStart1; + ScAddress aStart2; + if (pFCell1->GetMatrixOrigin(rDoc, aStart1) && pFCell2->GetMatrixOrigin(rDoc, aStart2)) + { + if (aStart1 == aStart2) // both the same matrix + return pFCell1->GetFormula(); // it doesn't matter from which cell + } + } + return OUString(); +} + +void ScCellRangeObj::SetArrayFormula_Impl(const OUString& rFormula, + const formula::FormulaGrammar::Grammar eGrammar) +{ + ScDocShell* pDocSh = GetDocShell(); + if (!pDocSh) + return; + + if ( !rFormula.isEmpty() ) + { + if ( dynamic_cast<ScTableSheetObj*>( this ) ) + { + // don't set array formula for sheet object + throw uno::RuntimeException(); + } + + pDocSh->GetDocFunc().EnterMatrix( aRange, nullptr, nullptr, rFormula, true, true, OUString()/*rFormulaNmsp*/, eGrammar ); + } + else + { + // empty string -> erase array formula + ScMarkData aMark(GetDocument()->GetSheetLimits()); + aMark.SetMarkArea( aRange ); + aMark.SelectTable( aRange.aStart.Tab(), true ); + pDocSh->GetDocFunc().DeleteContents( aMark, InsertDeleteFlags::CONTENTS, true, true ); + } +} + +void SAL_CALL ScCellRangeObj::setArrayFormula( const OUString& aFormula ) +{ + SolarMutexGuard aGuard; + // GRAM_API for API compatibility. + SetArrayFormula_Impl( aFormula, formula::FormulaGrammar::GRAM_API); +} + +// XArrayFormulaTokens +uno::Sequence<sheet::FormulaToken> SAL_CALL ScCellRangeObj::getArrayTokens() +{ + SolarMutexGuard aGuard; + + // same cell logic as in getArrayFormula + + uno::Sequence<sheet::FormulaToken> aSequence; + ScDocShell* pDocSh = GetDocShell(); + if (!pDocSh) + return aSequence; + + ScDocument& rDoc = pDocSh->GetDocument(); + ScRefCellValue aCell1(rDoc, aRange.aStart); + ScRefCellValue aCell2(rDoc, aRange.aEnd); + if (aCell1.getType() == CELLTYPE_FORMULA && aCell2.getType() == CELLTYPE_FORMULA) + { + const ScFormulaCell* pFCell1 = aCell1.getFormula(); + const ScFormulaCell* pFCell2 = aCell2.getFormula(); + ScAddress aStart1; + ScAddress aStart2; + if (pFCell1->GetMatrixOrigin(rDoc, aStart1) && pFCell2->GetMatrixOrigin(rDoc, aStart2)) + { + if (aStart1 == aStart2) + { + const ScTokenArray* pTokenArray = pFCell1->GetCode(); + if (pTokenArray) + ScTokenConversion::ConvertToTokenSequence(rDoc, aSequence, *pTokenArray); + } + } + } + + return aSequence; +} + +void SAL_CALL ScCellRangeObj::setArrayTokens( const uno::Sequence<sheet::FormulaToken>& rTokens ) +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( !pDocSh ) + return; + + if ( rTokens.hasElements() ) + { + if ( dynamic_cast<ScTableSheetObj*>( this ) ) + { + throw uno::RuntimeException(); + } + + ScDocument& rDoc = pDocSh->GetDocument(); + ScTokenArray aTokenArray(rDoc); + (void)ScTokenConversion::ConvertToTokenArray( rDoc, aTokenArray, rTokens ); + + // Actually GRAM_API is a don't-care here because of the token + // array being set, it fits with other API compatibility grammars + // though. + pDocSh->GetDocFunc().EnterMatrix( aRange, nullptr, &aTokenArray, OUString(), true, true, OUString(), formula::FormulaGrammar::GRAM_API ); + } + else + { + // empty sequence -> erase array formula + ScMarkData aMark(pDocSh->GetDocument().GetSheetLimits()); + aMark.SetMarkArea( aRange ); + aMark.SelectTable( aRange.aStart.Tab(), true ); + pDocSh->GetDocFunc().DeleteContents( aMark, InsertDeleteFlags::CONTENTS, true, true ); + } +} + +// XCellRangeData + +uno::Sequence< uno::Sequence<uno::Any> > SAL_CALL ScCellRangeObj::getDataArray() +{ + SolarMutexGuard aGuard; + + if ( dynamic_cast<ScTableSheetObj*>( this ) ) + { + // don't create a data array for the sheet + throw uno::RuntimeException(); + } + + ScDocShell* pDocSh = GetDocShell(); + if (pDocSh) + { + uno::Any aAny; + // bAllowNV = TRUE: errors as void + if ( ScRangeToSequence::FillMixedArray( aAny, pDocSh->GetDocument(), aRange, true ) ) + { + uno::Sequence< uno::Sequence<uno::Any> > aSeq; + if ( aAny >>= aSeq ) + return aSeq; // success + } + } + + throw uno::RuntimeException(); // no other exceptions specified +} + +void SAL_CALL ScCellRangeObj::setDataArray( + const uno::Sequence< uno::Sequence<uno::Any> >& aArray ) +{ + SolarMutexGuard aGuard; + + bool bDone = false; + ScDocShell* pDocSh = GetDocShell(); + if (pDocSh) + { + //! move lcl_PutDataArray to docfunc? + bDone = lcl_PutDataArray( *pDocSh, aRange, aArray ); + } + + if (!bDone) + throw uno::RuntimeException(); // no other exceptions specified +} + +// XCellRangeFormula + +uno::Sequence< uno::Sequence<OUString> > SAL_CALL ScCellRangeObj::getFormulaArray() +{ + SolarMutexGuard aGuard; + + if ( dynamic_cast<ScTableSheetObj*>( this ) ) + { + // don't create a data array for the sheet + throw uno::RuntimeException(); + } + + ScDocShell* pDocSh = GetDocShell(); + if (pDocSh) + { + SCCOL nStartCol = aRange.aStart.Col(); + SCROW nStartRow = aRange.aStart.Row(); + SCCOL nEndCol = aRange.aEnd.Col(); + SCROW nEndRow = aRange.aEnd.Row(); + SCCOL nColCount = nEndCol + 1 - nStartCol; + SCROW nRowCount = nEndRow + 1 - nStartRow; + SCTAB nTab = aRange.aStart.Tab(); + + uno::Sequence< uno::Sequence<OUString> > aRowSeq( nRowCount ); + uno::Sequence<OUString>* pRowAry = aRowSeq.getArray(); + for (SCROW nRowIndex = 0; nRowIndex < nRowCount; nRowIndex++) + { + uno::Sequence<OUString> aColSeq( nColCount ); + OUString* pColAry = aColSeq.getArray(); + for (SCCOL nColIndex = 0; nColIndex < nColCount; nColIndex++) + pColAry[nColIndex] = lcl_GetInputString( pDocSh->GetDocument(), + ScAddress( nStartCol+nColIndex, nStartRow+nRowIndex, nTab ), true ); + + pRowAry[nRowIndex] = aColSeq; + } + + return aRowSeq; + } + + throw uno::RuntimeException(); // no other exceptions specified +} + +void SAL_CALL ScCellRangeObj::setFormulaArray( + const uno::Sequence< uno::Sequence<OUString> >& aArray ) +{ + SolarMutexGuard aGuard; + + bool bDone = false; + ScDocShell* pDocSh = GetDocShell(); + if (pDocSh) + { + ScExternalRefManager::ApiGuard aExtRefGuard(pDocSh->GetDocument()); + + // GRAM_API for API compatibility. + bDone = lcl_PutFormulaArray( *pDocSh, aRange, aArray, formula::FormulaGrammar::GRAM_API ); + } + + if (!bDone) + throw uno::RuntimeException(); // no other exceptions specified +} + +// XMultipleOperation + +void SAL_CALL ScCellRangeObj::setTableOperation( const table::CellRangeAddress& aFormulaRange, + sheet::TableOperationMode nMode, + const table::CellAddress& aColumnCell, + const table::CellAddress& aRowCell ) +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if (!pDocSh) + return; + + bool bError = false; + ScTabOpParam aParam; + aParam.aRefFormulaCell = ScRefAddress( static_cast<SCCOL>(aFormulaRange.StartColumn), + static_cast<SCROW>(aFormulaRange.StartRow), aFormulaRange.Sheet ); + aParam.aRefFormulaEnd = ScRefAddress( static_cast<SCCOL>(aFormulaRange.EndColumn), + static_cast<SCROW>(aFormulaRange.EndRow), aFormulaRange.Sheet ); + aParam.aRefRowCell = ScRefAddress( static_cast<SCCOL>(aRowCell.Column), + static_cast<SCROW>(aRowCell.Row), aRowCell.Sheet ); + aParam.aRefColCell = ScRefAddress( static_cast<SCCOL>(aColumnCell.Column), + static_cast<SCROW>(aColumnCell.Row), aColumnCell.Sheet ); + + switch (nMode) + { + case sheet::TableOperationMode_COLUMN: + aParam.meMode = ScTabOpParam::Column; + break; + case sheet::TableOperationMode_ROW: + aParam.meMode = ScTabOpParam::Row; + break; + case sheet::TableOperationMode_BOTH: + aParam.meMode = ScTabOpParam::Both; + break; + default: + bError = true; + } + + if (!bError) + pDocSh->GetDocFunc().TabOp( aRange, nullptr, aParam, true, true ); +} + +// XMergeable + +void SAL_CALL ScCellRangeObj::merge( sal_Bool bMerge ) +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( !pDocSh ) + return; + + ScCellMergeOption aMergeOption( + aRange.aStart.Col(), aRange.aStart.Row(), + aRange.aEnd.Col(), aRange.aEnd.Row(), false); + aMergeOption.maTabs.insert(aRange.aStart.Tab()); + if ( bMerge ) + pDocSh->GetDocFunc().MergeCells( aMergeOption, false, true, true ); + else + pDocSh->GetDocFunc().UnmergeCells( aMergeOption, true, nullptr ); + + //! Catch error? +} + +sal_Bool SAL_CALL ScCellRangeObj::getIsMerged() +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + return pDocSh && pDocSh->GetDocument().HasAttrib( aRange, HasAttrFlags::Merged ); +} + +// XCellSeries + +void SAL_CALL ScCellRangeObj::fillSeries( sheet::FillDirection nFillDirection, + sheet::FillMode nFillMode, sheet::FillDateMode nFillDateMode, + double fStep, double fEndValue ) +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( !pDocSh ) + return; + + bool bError = false; + + FillDir eDir = FILL_TO_BOTTOM; + switch (nFillDirection) + { + case sheet::FillDirection_TO_BOTTOM: + eDir = FILL_TO_BOTTOM; + break; + case sheet::FillDirection_TO_RIGHT: + eDir = FILL_TO_RIGHT; + break; + case sheet::FillDirection_TO_TOP: + eDir = FILL_TO_TOP; + break; + case sheet::FillDirection_TO_LEFT: + eDir = FILL_TO_LEFT; + break; + default: + bError = true; + } + + FillCmd eCmd = FILL_SIMPLE; + switch ( nFillMode ) + { + case sheet::FillMode_SIMPLE: + eCmd = FILL_SIMPLE; + break; + case sheet::FillMode_LINEAR: + eCmd = FILL_LINEAR; + break; + case sheet::FillMode_GROWTH: + eCmd = FILL_GROWTH; + break; + case sheet::FillMode_DATE: + eCmd = FILL_DATE; + break; + case sheet::FillMode_AUTO: + eCmd = FILL_AUTO; + break; + default: + bError = true; + } + + FillDateCmd eDateCmd = FILL_DAY; + switch ( nFillDateMode ) + { + case sheet::FillDateMode_FILL_DATE_DAY: + eDateCmd = FILL_DAY; + break; + case sheet::FillDateMode_FILL_DATE_WEEKDAY: + eDateCmd = FILL_WEEKDAY; + break; + case sheet::FillDateMode_FILL_DATE_MONTH: + eDateCmd = FILL_MONTH; + break; + case sheet::FillDateMode_FILL_DATE_YEAR: + eDateCmd = FILL_YEAR; + break; + default: + bError = true; + } + + if (!bError) + pDocSh->GetDocFunc().FillSeries( aRange, nullptr, eDir, eCmd, eDateCmd, + MAXDOUBLE, fStep, fEndValue, true ); +} + +void SAL_CALL ScCellRangeObj::fillAuto( sheet::FillDirection nFillDirection, + sal_Int32 nSourceCount ) +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( !(pDocSh && nSourceCount) ) + return; + + ScRange aSourceRange(aRange); + SCCOLROW nCount = 0; // "Dest-Count" + FillDir eDir = FILL_TO_BOTTOM; + bool bError = false; + switch (nFillDirection) + { + case sheet::FillDirection_TO_BOTTOM: + aSourceRange.aEnd.SetRow( static_cast<SCROW>( aSourceRange.aStart.Row() + nSourceCount - 1 ) ); + nCount = aRange.aEnd.Row() - aSourceRange.aEnd.Row(); + eDir = FILL_TO_BOTTOM; + break; + case sheet::FillDirection_TO_RIGHT: + aSourceRange.aEnd.SetCol( static_cast<SCCOL>( aSourceRange.aStart.Col() + nSourceCount - 1 ) ); + nCount = aRange.aEnd.Col() - aSourceRange.aEnd.Col(); + eDir = FILL_TO_RIGHT; + break; + case sheet::FillDirection_TO_TOP: + aSourceRange.aStart.SetRow( static_cast<SCROW>( aSourceRange.aEnd.Row() - nSourceCount + 1 ) ); + nCount = aSourceRange.aStart.Row() - aRange.aStart.Row(); + eDir = FILL_TO_TOP; + break; + case sheet::FillDirection_TO_LEFT: + aSourceRange.aStart.SetCol( static_cast<SCCOL>( aSourceRange.aEnd.Col() - nSourceCount + 1 ) ); + nCount = aSourceRange.aStart.Col() - aRange.aStart.Col(); + eDir = FILL_TO_LEFT; + break; + default: + bError = true; + } + const ScDocument& rDoc = pDocSh->GetDocument(); + if (nCount < 0 || nCount > rDoc.MaxRow()) // overflow + bError = true; + + if (!bError) + pDocSh->GetDocFunc().FillAuto( aSourceRange, nullptr, eDir, nCount, true ); +} + +// XAutoFormattable + +void SAL_CALL ScCellRangeObj::autoFormat( const OUString& aName ) +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + { + ScAutoFormat* pAutoFormat = ScGlobal::GetOrCreateAutoFormat(); + ScAutoFormat::const_iterator it = pAutoFormat->find(aName); + if (it == pAutoFormat->end()) + throw lang::IllegalArgumentException(); + + ScAutoFormat::const_iterator itBeg = pAutoFormat->begin(); + size_t nIndex = std::distance(itBeg, it); + pDocSh->GetDocFunc().AutoFormat(aRange, nullptr, nIndex, true); + + } +} + +// XSortable + +uno::Sequence<beans::PropertyValue> SAL_CALL ScCellRangeObj::createSortDescriptor() +{ + SolarMutexGuard aGuard; + ScSortParam aParam; + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + { + // create DB-Area only during execution; API always the exact area + ScDBData* pData = pDocSh->GetDBData( aRange, SC_DB_OLD, ScGetDBSelection::ForceMark ); + if (pData) + { + pData->GetSortParam(aParam); + + // SortDescriptor contains the counted fields inside the area + ScRange aDBRange; + pData->GetArea(aDBRange); + SCCOLROW nFieldStart = aParam.bByRow ? + static_cast<SCCOLROW>(aDBRange.aStart.Col()) : + static_cast<SCCOLROW>(aDBRange.aStart.Row()); + for (sal_uInt16 i=0; i<aParam.GetSortKeyCount(); i++) + if ( aParam.maKeyState[i].bDoSort && aParam.maKeyState[i].nField >= nFieldStart ) + aParam.maKeyState[i].nField -= nFieldStart; + } + } + + uno::Sequence<beans::PropertyValue> aSeq( ScSortDescriptor::GetPropertyCount() ); + ScSortDescriptor::FillProperties( aSeq, aParam ); + return aSeq; +} + +void SAL_CALL ScCellRangeObj::sort( const uno::Sequence<beans::PropertyValue>& aDescriptor ) +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if (!pDocSh) + return; + + sal_uInt16 i; + ScSortParam aParam; + ScDBData* pData = pDocSh->GetDBData( aRange, SC_DB_MAKE, ScGetDBSelection::ForceMark ); // if needed create area + if (pData) + { + // get old settings if not everything is set anew + pData->GetSortParam(aParam); + SCCOLROW nOldStart = aParam.bByRow ? + static_cast<SCCOLROW>(aRange.aStart.Col()) : + static_cast<SCCOLROW>(aRange.aStart.Row()); + for (i=0; i<aParam.GetSortKeyCount(); i++) + if ( aParam.maKeyState[i].bDoSort && aParam.maKeyState[i].nField >= nOldStart ) + aParam.maKeyState[i].nField -= nOldStart; + } + + ScSortDescriptor::FillSortParam( aParam, aDescriptor ); + + // SortDescriptor contains the counted fields inside the area + // ByRow can be changed during execution of FillSortParam + SCCOLROW nFieldStart = aParam.bByRow ? + static_cast<SCCOLROW>(aRange.aStart.Col()) : + static_cast<SCCOLROW>(aRange.aStart.Row()); + SCCOLROW nFieldEnd = aParam.bByRow ? + static_cast<SCCOLROW>(aRange.aEnd.Col()) : + static_cast<SCCOLROW>(aRange.aEnd.Row()); + for (i=0; i<aParam.GetSortKeyCount(); i++) + { + aParam.maKeyState[i].nField += nFieldStart; + // tdf#103632 - sanity check poorly behaved macros. + if (aParam.maKeyState[i].nField > nFieldEnd) + aParam.maKeyState[i].nField = nFieldEnd; + } + + SCTAB nTab = aRange.aStart.Tab(); + aParam.nCol1 = aRange.aStart.Col(); + aParam.nRow1 = aRange.aStart.Row(); + aParam.nCol2 = aRange.aEnd.Col(); + aParam.nRow2 = aRange.aEnd.Row(); + + pDocSh->GetDBData( aRange, SC_DB_MAKE, ScGetDBSelection::ForceMark ); // if needed create area + + ScDBDocFunc aFunc(*pDocSh); // area must be created + (void)aFunc.Sort( nTab, aParam, true, true, true ); +} + +// XFilterable + +uno::Reference<sheet::XSheetFilterDescriptor> SAL_CALL ScCellRangeObj::createFilterDescriptor( + sal_Bool bEmpty ) +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + rtl::Reference<ScFilterDescriptor> pNew = new ScFilterDescriptor(pDocSh); + if ( !bEmpty && pDocSh ) + { + // create DB-Area only during execution; API always the exact area + ScDBData* pData = pDocSh->GetDBData( aRange, SC_DB_OLD, ScGetDBSelection::ForceMark ); + if (pData) + { + ScQueryParam aParam; + pData->GetQueryParam(aParam); + // FilterDescriptor contains the counted fields inside the area + ScRange aDBRange; + pData->GetArea(aDBRange); + SCCOLROW nFieldStart = aParam.bByRow ? + static_cast<SCCOLROW>(aDBRange.aStart.Col()) : + static_cast<SCCOLROW>(aDBRange.aStart.Row()); + SCSIZE nCount = aParam.GetEntryCount(); + for (SCSIZE i=0; i<nCount; i++) + { + ScQueryEntry& rEntry = aParam.GetEntry(i); + if (rEntry.bDoQuery && rEntry.nField >= nFieldStart) + rEntry.nField -= nFieldStart; + } + pNew->SetParam(aParam); + } + } + return pNew; +} + +void SAL_CALL ScCellRangeObj::filter( const uno::Reference<sheet::XSheetFilterDescriptor>& xDescriptor ) +{ + SolarMutexGuard aGuard; + + if (!xDescriptor.is()) return; + + // This could be theoretically an unknown object, so only use the + // public XSheetFilterDescriptor interface to copy the data into a + // ScFilterDescriptor object: + //! if it already a ScFilterDescriptor is, direct via getImplementation? + + ScDocShell* pDocSh = GetDocShell(); + rtl::Reference<ScFilterDescriptor> xImpl(new ScFilterDescriptor(pDocSh)); + uno::Reference< sheet::XSheetFilterDescriptor2 > xDescriptor2( xDescriptor, uno::UNO_QUERY ); + if ( xDescriptor2.is() ) + { + xImpl->setFilterFields2( xDescriptor2->getFilterFields2() ); + } + else + { + xImpl->setFilterFields( xDescriptor->getFilterFields() ); + } + // the rest are now properties... + + uno::Reference<beans::XPropertySet> xPropSet( xDescriptor, uno::UNO_QUERY ); + if (xPropSet.is()) + lcl_CopyProperties(*xImpl, *xPropSet); + + if (!pDocSh) + return; + + ScQueryParam aParam = xImpl->GetParam(); + // FilterDescriptor contains the counted fields inside the area + SCCOLROW nFieldStart = aParam.bByRow ? + static_cast<SCCOLROW>(aRange.aStart.Col()) : + static_cast<SCCOLROW>(aRange.aStart.Row()); + SCSIZE nCount = aParam.GetEntryCount(); + svl::SharedStringPool& rPool = pDocSh->GetDocument().GetSharedStringPool(); + for (SCSIZE i=0; i<nCount; i++) + { + ScQueryEntry& rEntry = aParam.GetEntry(i); + if (rEntry.bDoQuery) + { + rEntry.nField += nFieldStart; + // dialog always shows the string -> must match the value + ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems(); + rItems.resize(1); + ScQueryEntry::Item& rItem = rItems.front(); + if (rItem.meType != ScQueryEntry::ByString) + { + OUString aStr; + pDocSh->GetDocument().GetFormatTable()->GetInputLineString(rItem.mfVal, 0, aStr); + rItem.maString = rPool.intern(aStr); + } + } + } + + SCTAB nTab = aRange.aStart.Tab(); + aParam.nCol1 = aRange.aStart.Col(); + aParam.nRow1 = aRange.aStart.Row(); + aParam.nCol2 = aRange.aEnd.Col(); + aParam.nRow2 = aRange.aEnd.Row(); + + pDocSh->GetDBData( aRange, SC_DB_MAKE, ScGetDBSelection::ForceMark ); // if needed create area + + //! keep source range in filter descriptor + //! if created by createFilterDescriptorByObject ??? + + ScDBDocFunc aFunc(*pDocSh); + aFunc.Query( nTab, aParam, nullptr, true, true ); // area must be created +} + +//! get/setAutoFilter as properties!!! + +// XAdvancedFilterSource + +uno::Reference<sheet::XSheetFilterDescriptor> SAL_CALL ScCellRangeObj::createFilterDescriptorByObject( + const uno::Reference<sheet::XSheetFilterable>& xObject ) +{ + SolarMutexGuard aGuard; + + // this here is not the area, which will be filtered, instead the area + // with the query + + uno::Reference<sheet::XCellRangeAddressable> xAddr( xObject, uno::UNO_QUERY ); + + ScDocShell* pDocSh = GetDocShell(); + if ( !pDocSh || !xAddr.is() ) + { + OSL_FAIL("no document or no area"); + return nullptr; + } + + //! check if xObject is in the same document + + rtl::Reference<ScFilterDescriptor> pNew(new ScFilterDescriptor(pDocSh)); //! instead from object? + + ScQueryParam aParam = pNew->GetParam(); + aParam.bHasHeader = true; + + table::CellRangeAddress aDataAddress(xAddr->getRangeAddress()); + aParam.nCol1 = static_cast<SCCOL>(aDataAddress.StartColumn); + aParam.nRow1 = static_cast<SCROW>(aDataAddress.StartRow); + aParam.nCol2 = static_cast<SCCOL>(aDataAddress.EndColumn); + aParam.nRow2 = static_cast<SCROW>(aDataAddress.EndRow); + aParam.nTab = aDataAddress.Sheet; + + ScDocument& rDoc = pDocSh->GetDocument(); + if (!rDoc.CreateQueryParam(aRange, aParam)) + return nullptr; + + // FilterDescriptor contains the counted fields inside the area + SCCOLROW nFieldStart = aParam.bByRow ? + static_cast<SCCOLROW>(aDataAddress.StartColumn) : + static_cast<SCCOLROW>(aDataAddress.StartRow); + SCSIZE nCount = aParam.GetEntryCount(); + for (SCSIZE i=0; i<nCount; i++) + { + ScQueryEntry& rEntry = aParam.GetEntry(i); + if (rEntry.bDoQuery && rEntry.nField >= nFieldStart) + rEntry.nField -= nFieldStart; + } + + pNew->SetParam( aParam ); + return pNew; +} + +// XSubTotalSource + +uno::Reference<sheet::XSubTotalDescriptor> SAL_CALL ScCellRangeObj::createSubTotalDescriptor( + sal_Bool bEmpty ) +{ + SolarMutexGuard aGuard; + rtl::Reference<ScSubTotalDescriptor> pNew = new ScSubTotalDescriptor; + ScDocShell* pDocSh = GetDocShell(); + if ( !bEmpty && pDocSh ) + { + // create DB-Area only during execution; API always the exact area + ScDBData* pData = pDocSh->GetDBData( aRange, SC_DB_OLD, ScGetDBSelection::ForceMark ); + if (pData) + { + ScSubTotalParam aParam; + pData->GetSubTotalParam(aParam); + // SubTotalDescriptor contains the counted fields inside the area + ScRange aDBRange; + pData->GetArea(aDBRange); + SCCOL nFieldStart = aDBRange.aStart.Col(); + for (sal_uInt16 i=0; i<MAXSUBTOTAL; i++) + { + if ( aParam.bGroupActive[i] ) + { + if ( aParam.nField[i] >= nFieldStart ) + aParam.nField[i] = sal::static_int_cast<SCCOL>( aParam.nField[i] - nFieldStart ); + for (SCCOL j=0; j<aParam.nSubTotals[i]; j++) + if ( aParam.pSubTotals[i][j] >= nFieldStart ) + aParam.pSubTotals[i][j] = sal::static_int_cast<SCCOL>( aParam.pSubTotals[i][j] - nFieldStart ); + } + } + pNew->SetParam(aParam); + } + } + return pNew; +} + +void SAL_CALL ScCellRangeObj::applySubTotals( + const uno::Reference<sheet::XSubTotalDescriptor>& xDescriptor, + sal_Bool bReplace) +{ + SolarMutexGuard aGuard; + + if (!xDescriptor.is()) return; + + ScDocShell* pDocSh = GetDocShell(); + ScSubTotalDescriptorBase* pImp = + dynamic_cast<ScSubTotalDescriptorBase*>( xDescriptor.get() ); + + if (!(pDocSh && pImp)) + return; + + ScSubTotalParam aParam; + pImp->GetData(aParam); // virtual method of base class + + // SubTotalDescriptor contains the counted fields inside the area + SCCOL nFieldStart = aRange.aStart.Col(); + for (sal_uInt16 i=0; i<MAXSUBTOTAL; i++) + { + if ( aParam.bGroupActive[i] ) + { + aParam.nField[i] = sal::static_int_cast<SCCOL>( aParam.nField[i] + nFieldStart ); + for (SCCOL j=0; j<aParam.nSubTotals[i]; j++) + aParam.pSubTotals[i][j] = sal::static_int_cast<SCCOL>( aParam.pSubTotals[i][j] + nFieldStart ); + } + } + + aParam.bReplace = bReplace; + + SCTAB nTab = aRange.aStart.Tab(); + aParam.nCol1 = aRange.aStart.Col(); + aParam.nRow1 = aRange.aStart.Row(); + aParam.nCol2 = aRange.aEnd.Col(); + aParam.nRow2 = aRange.aEnd.Row(); + + pDocSh->GetDBData( aRange, SC_DB_MAKE, ScGetDBSelection::ForceMark ); // if needed create area + + ScDBDocFunc aFunc(*pDocSh); + aFunc.DoSubTotals( nTab, aParam, true, true ); // area must be created +} + +void SAL_CALL ScCellRangeObj::removeSubTotals() +{ + SolarMutexGuard aGuard; + + ScDocShell* pDocSh = GetDocShell(); + if (!pDocSh) + return; + + ScSubTotalParam aParam; + ScDBData* pData = pDocSh->GetDBData( aRange, SC_DB_OLD, ScGetDBSelection::ForceMark ); + if (pData) + pData->GetSubTotalParam(aParam); // also keep field entries during remove + + aParam.bRemoveOnly = true; + + SCTAB nTab = aRange.aStart.Tab(); + aParam.nCol1 = aRange.aStart.Col(); + aParam.nRow1 = aRange.aStart.Row(); + aParam.nCol2 = aRange.aEnd.Col(); + aParam.nRow2 = aRange.aEnd.Row(); + + pDocSh->GetDBData( aRange, SC_DB_MAKE, ScGetDBSelection::ForceMark ); // if needed create area + + ScDBDocFunc aFunc(*pDocSh); + aFunc.DoSubTotals( nTab, aParam, true, true ); // are must be created +} + +uno::Sequence<beans::PropertyValue> SAL_CALL ScCellRangeObj::createImportDescriptor( sal_Bool bEmpty ) +{ + SolarMutexGuard aGuard; + ScImportParam aParam; + ScDocShell* pDocSh = GetDocShell(); + if ( !bEmpty && pDocSh ) + { + // create DB-Area only during execution; API always the exact area + ScDBData* pData = pDocSh->GetDBData( aRange, SC_DB_OLD, ScGetDBSelection::ForceMark ); + if (pData) + pData->GetImportParam(aParam); + } + + uno::Sequence<beans::PropertyValue> aSeq( ScImportDescriptor::GetPropertyCount() ); + ScImportDescriptor::FillProperties( aSeq, aParam ); + return aSeq; +} + +void SAL_CALL ScCellRangeObj::doImport( const uno::Sequence<beans::PropertyValue>& aDescriptor ) +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if (!pDocSh) + return; + + ScImportParam aParam; + ScImportDescriptor::FillImportParam( aParam, aDescriptor ); + + SCTAB nTab = aRange.aStart.Tab(); + aParam.nCol1 = aRange.aStart.Col(); + aParam.nRow1 = aRange.aStart.Row(); + aParam.nCol2 = aRange.aEnd.Col(); + aParam.nRow2 = aRange.aEnd.Row(); + + //! TODO: could we get passed a valid result set by any means? + + pDocSh->GetDBData( aRange, SC_DB_MAKE, ScGetDBSelection::ForceMark ); // if needed create area + + ScDBDocFunc aFunc(*pDocSh); // are must be created + aFunc.DoImport( nTab, aParam, nullptr ); //! Api-Flag as parameter +} + +// XCellFormatRangesSupplier + +uno::Reference<container::XIndexAccess> SAL_CALL ScCellRangeObj::getCellFormatRanges() +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + return new ScCellFormatsObj( pDocSh, aRange ); + return nullptr; +} + +// XUniqueCellFormatRangesSupplier + +uno::Reference<container::XIndexAccess> SAL_CALL ScCellRangeObj::getUniqueCellFormatRanges() +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + return new ScUniqueCellFormatsObj( pDocSh, aRange ); + return nullptr; +} + +// XPropertySet extended for Range-Properties + +uno::Reference<beans::XPropertySetInfo> SAL_CALL ScCellRangeObj::getPropertySetInfo() +{ + SolarMutexGuard aGuard; + static uno::Reference<beans::XPropertySetInfo> aRef( + new SfxItemPropertySetInfo( pRangePropSet->getPropertyMap() )); + return aRef; +} + +void ScCellRangeObj::SetOnePropertyValue( const SfxItemPropertyMapEntry* pEntry, const uno::Any& aValue ) +{ + // Range has only Position and Size in addition to ScCellRangesBase, both are ReadOnly + // -> nothing to do here + + ScCellRangesBase::SetOnePropertyValue( pEntry, aValue ); +} + +void ScCellRangeObj::GetOnePropertyValue( const SfxItemPropertyMapEntry* pEntry, uno::Any& rAny ) +{ + if ( !pEntry ) + return; + + if ( pEntry->nWID == SC_WID_UNO_POS ) + { + ScDocShell* pDocSh = GetDocShell(); + if (pDocSh) + { + // GetMMRect converts using HMM_PER_TWIPS, like the DrawingLayer + tools::Rectangle aMMRect(pDocSh->GetDocument().GetMMRect( + aRange.aStart.Col(), aRange.aStart.Row(), + aRange.aEnd.Col(), aRange.aEnd.Row(), aRange.aStart.Tab() )); + awt::Point aPos( aMMRect.Left(), aMMRect.Top() ); + rAny <<= aPos; + } + } + else if ( pEntry->nWID == SC_WID_UNO_SIZE ) + { + ScDocShell* pDocSh = GetDocShell(); + if (pDocSh) + { + // GetMMRect converts using HMM_PER_TWIPS, like the DrawingLayer + tools::Rectangle aMMRect = pDocSh->GetDocument().GetMMRect( + aRange.aStart.Col(), aRange.aStart.Row(), + aRange.aEnd.Col(), aRange.aEnd.Row(), aRange.aStart.Tab() ); + Size aSize(aMMRect.GetSize()); + awt::Size aAwtSize( aSize.Width(), aSize.Height() ); + rAny <<= aAwtSize; + } + } + else + ScCellRangesBase::GetOnePropertyValue( pEntry, rAny ); +} + +const SfxItemPropertyMap& ScCellRangeObj::GetItemPropertyMap() +{ + return pRangePropSet->getPropertyMap(); +} + +// XServiceInfo + +OUString SAL_CALL ScCellRangeObj::getImplementationName() +{ + return "ScCellRangeObj"; +} + +sal_Bool SAL_CALL ScCellRangeObj::supportsService( const OUString& rServiceName ) +{ + return cppu::supportsService(this, rServiceName); +} + +uno::Sequence<OUString> SAL_CALL ScCellRangeObj::getSupportedServiceNames() +{ + return {SCSHEETCELLRANGE_SERVICE, + SCCELLRANGE_SERVICE, + SCCELLPROPERTIES_SERVICE, + SCCHARPROPERTIES_SERVICE, + SCPARAPROPERTIES_SERVICE}; +} + +const SvxItemPropertySet* ScCellObj::GetEditPropertySet() +{ + return lcl_GetEditPropertySet(); +} + +const SfxItemPropertyMap& ScCellObj::GetCellPropertyMap() +{ + return lcl_GetCellPropertySet()->getPropertyMap(); +} + +ScCellObj::ScCellObj(ScDocShell* pDocSh, const ScAddress& rP) : + ScCellRangeObj( pDocSh, ScRange(rP,rP) ), + pCellPropSet( lcl_GetCellPropertySet() ), + aCellPos( rP ), + nActionLockCount( 0 ) +{ + // pUnoText is allocated on demand (GetUnoText) + // can't be aggregated because getString/setString is handled here +} + +SvxUnoText& ScCellObj::GetUnoText() +{ + if (!mxUnoText.is()) + { + mxUnoText.set(new ScCellTextObj(GetDocShell(), aCellPos)); + if (nActionLockCount) + { + ScCellEditSource* pEditSource = + static_cast<ScCellEditSource*> (mxUnoText->GetEditSource()); + if (pEditSource) + pEditSource->SetDoUpdateData(false); + } + } + return *mxUnoText; +} + +ScCellObj::~ScCellObj() +{ +} + +void ScCellObj::RefChanged() +{ + ScCellRangeObj::RefChanged(); + + const ScRangeList& rRanges = GetRangeList(); + OSL_ENSURE(rRanges.size() == 1, "What ranges ?!?!"); + if ( !rRanges.empty() ) + { + aCellPos = rRanges[ 0 ].aStart; + } +} + +uno::Any SAL_CALL ScCellObj::queryInterface( const uno::Type& rType ) +{ + uno::Any aReturn = ::cppu::queryInterface(rType, + static_cast<table::XCell*>(this), + static_cast<table::XCell2*>(this), + static_cast<sheet::XFormulaTokens*>(this), + static_cast<sheet::XCellAddressable*>(this), + static_cast<text::XText*>(this), + static_cast<text::XSimpleText*>(this), + static_cast<text::XTextRange*>(this), + static_cast<container::XEnumerationAccess*>(this), + static_cast<container::XElementAccess*>(this), + static_cast<sheet::XSheetAnnotationAnchor*>(this), + static_cast<text::XTextFieldsSupplier*>(this), + static_cast<document::XActionLockable*>(this)); + if ( aReturn.hasValue() ) + return aReturn; + + return ScCellRangeObj::queryInterface( rType ); +} + +void SAL_CALL ScCellObj::acquire() noexcept +{ + ScCellRangeObj::acquire(); +} + +void SAL_CALL ScCellObj::release() noexcept +{ + ScCellRangeObj::release(); +} + +uno::Sequence<uno::Type> SAL_CALL ScCellObj::getTypes() +{ + static const uno::Sequence<uno::Type> aTypes = comphelper::concatSequences( + ScCellRangeObj::getTypes(), + uno::Sequence<uno::Type> + { + cppu::UnoType<table::XCell>::get(), + cppu::UnoType<sheet::XCellAddressable>::get(), + cppu::UnoType<text::XText>::get(), + cppu::UnoType<container::XEnumerationAccess>::get(), + cppu::UnoType<sheet::XSheetAnnotationAnchor>::get(), + cppu::UnoType<text::XTextFieldsSupplier>::get(), + cppu::UnoType<document::XActionLockable>::get(), + cppu::UnoType<sheet::XFormulaTokens>::get(), + cppu::UnoType<table::XCell2>::get() + } ); + return aTypes; +} + +uno::Sequence<sal_Int8> SAL_CALL ScCellObj::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +// helper methods + +OUString ScCellObj::GetInputString_Impl(bool bEnglish) const // for getFormula / FormulaLocal +{ + if (GetDocShell()) + return lcl_GetInputString( GetDocShell()->GetDocument(), aCellPos, bEnglish ); + return OUString(); +} + +OUString ScCellObj::GetOutputString_Impl() const +{ + ScDocShell* pDocSh = GetDocShell(); + OUString aVal; + if ( pDocSh ) + { + ScDocument& rDoc = pDocSh->GetDocument(); + ScRefCellValue aCell(rDoc, aCellPos); + + aVal = ScCellFormat::GetOutputString(rDoc, aCellPos, aCell); + } + return aVal; +} + +void ScCellObj::SetString_Impl(const OUString& rString, bool bInterpret, bool bEnglish) +{ + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + { + // GRAM_API for API compatibility. + (void)pDocSh->GetDocFunc().SetCellText( + aCellPos, rString, bInterpret, bEnglish, true, formula::FormulaGrammar::GRAM_API ); + } +} + +double ScCellObj::GetValue_Impl() const +{ + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + return pDocSh->GetDocument().GetValue( aCellPos ); + + return 0.0; +} + +void ScCellObj::SetValue_Impl(double fValue) +{ + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + pDocSh->GetDocFunc().SetValueCell(aCellPos, fValue, false); +} + +// only for XML import + +void ScCellObj::InputEnglishString( const OUString& rText ) +{ + // This is like a mixture of setFormula and property FormulaLocal: + // The cell's number format is checked for "text", a new cell format may be set, + // but all parsing is in English. + + ScDocShell* pDocSh = GetDocShell(); + if (!pDocSh) + return; + + ScDocument& rDoc = pDocSh->GetDocument(); + SvNumberFormatter* pFormatter = rDoc.GetFormatTable(); + sal_uInt32 nOldFormat = rDoc.GetNumberFormat( aCellPos ); + if (pFormatter->GetType(nOldFormat) == SvNumFormatType::TEXT) + { + SetString_Impl(rText, false, false); // text cell + return; + } + + ScDocFunc &rFunc = pDocSh->GetDocFunc(); + + ScInputStringType aRes = + ScStringUtil::parseInputString(*pFormatter, rText, LANGUAGE_ENGLISH_US); + + if (aRes.meType != ScInputStringType::Unknown) + { + if ((nOldFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0 && aRes.mnFormatType != SvNumFormatType::ALL) + { + // apply a format for the recognized type and the old format's language + sal_uInt32 nNewFormat = ScGlobal::GetStandardFormat(*pFormatter, nOldFormat, aRes.mnFormatType); + if (nNewFormat != nOldFormat) + { + ScPatternAttr aPattern( rDoc.GetPool() ); + aPattern.GetItemSet().Put( SfxUInt32Item( ATTR_VALUE_FORMAT, nNewFormat ) ); + // ATTR_LANGUAGE_FORMAT remains unchanged + rFunc.ApplyAttributes( *GetMarkData(), aPattern, true ); + } + } + } + switch (aRes.meType) + { + case ScInputStringType::Formula: + rFunc.SetFormulaCell( + aCellPos, + new ScFormulaCell(rDoc, aCellPos, aRes.maText, formula::FormulaGrammar::GRAM_API), + false); + break; + case ScInputStringType::Number: + rFunc.SetValueCell(aCellPos, aRes.mfValue, false); + break; + case ScInputStringType::Text: + rFunc.SetStringOrEditCell(aCellPos, aRes.maText, false); + break; + default: + SetString_Impl(rText, false, false); // probably empty string + } +} + +// XText + +uno::Reference<text::XTextCursor> SAL_CALL ScCellObj::createTextCursor() +{ + SolarMutexGuard aGuard; + return new ScCellTextCursor( *this ); +} + +uno::Reference<text::XTextCursor> SAL_CALL ScCellObj::createTextCursorByRange( + const uno::Reference<text::XTextRange>& aTextPosition ) +{ + SolarMutexGuard aGuard; + rtl::Reference<SvxUnoTextCursor> pCursor = new ScCellTextCursor( *this ); + + SvxUnoTextRangeBase* pRange = comphelper::getFromUnoTunnel<SvxUnoTextRangeBase>( aTextPosition ); + if(pRange) + pCursor->SetSelection( pRange->GetSelection() ); + else + { + ScCellTextCursor* pOther = comphelper::getFromUnoTunnel<ScCellTextCursor>( aTextPosition ); + if(!pOther) + throw uno::RuntimeException(); + + pCursor->SetSelection( pOther->GetSelection() ); + + } + + return pCursor; +} + +OUString SAL_CALL ScCellObj::getString() +{ + SolarMutexGuard aGuard; + return GetOutputString_Impl(); +} + +void SAL_CALL ScCellObj::setString( const OUString& aText ) +{ + SolarMutexGuard aGuard; + SetString_Impl(aText, false, false); // always text + + // don't create pUnoText here if not there + if (mxUnoText.is()) + mxUnoText->SetSelection(ESelection( 0,0, 0,aText.getLength() )); +} + +void SAL_CALL ScCellObj::insertString( const uno::Reference<text::XTextRange>& xRange, + const OUString& aString, sal_Bool bAbsorb ) +{ + // special handling for ScCellTextCursor is no longer needed, + // SvxUnoText::insertString checks for SvxUnoTextRangeBase instead of SvxUnoTextRange + + SolarMutexGuard aGuard; + GetUnoText().insertString(xRange, aString, bAbsorb); +} + +void SAL_CALL ScCellObj::insertControlCharacter( const uno::Reference<text::XTextRange>& xRange, + sal_Int16 nControlCharacter, sal_Bool bAbsorb ) +{ + SolarMutexGuard aGuard; + GetUnoText().insertControlCharacter(xRange, nControlCharacter, bAbsorb); +} + +void SAL_CALL ScCellObj::insertTextContent( const uno::Reference<text::XTextRange >& xRange, + const uno::Reference<text::XTextContent >& xContent, + sal_Bool bAbsorb ) +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh && xContent.is() ) + { + ScEditFieldObj* pCellField = dynamic_cast<ScEditFieldObj*>(xContent.get()); + SvxUnoTextRangeBase* pTextRange = comphelper::getFromUnoTunnel<ScCellTextCursor>( xRange ); + + if ( pCellField && !pCellField->IsInserted() && pTextRange ) + { + SvxEditSource* pEditSource = pTextRange->GetEditSource(); + ESelection aSelection(pTextRange->GetSelection()); + + if (!bAbsorb) + { + // do not replace -> append + aSelection.Adjust(); + aSelection.nStartPara = aSelection.nEndPara; + aSelection.nStartPos = aSelection.nEndPos; + } + + if (pCellField->GetFieldType() == text::textfield::Type::TABLE) + pCellField->setPropertyValue(SC_UNONAME_TABLEPOS, uno::Any(sal_Int32(aCellPos.Tab()))); + + SvxFieldItem aItem = pCellField->CreateFieldItem(); + SvxTextForwarder* pForwarder = pEditSource->GetTextForwarder(); + pForwarder->QuickInsertField( aItem, aSelection ); + pEditSource->UpdateData(); + + // new selection: a digit + aSelection.Adjust(); + aSelection.nEndPara = aSelection.nStartPara; + aSelection.nEndPos = aSelection.nStartPos + 1; + uno::Reference<text::XTextRange> xParent(this); + pCellField->InitDoc( + xParent, std::make_unique<ScCellEditSource>(pDocSh, aCellPos), aSelection); + + // for bAbsorb=FALSE, the new selection must be behind the inserted content + // (the xml filter relies on this) + if (!bAbsorb) + aSelection.nStartPos = aSelection.nEndPos; + + pTextRange->SetSelection( aSelection ); + + return; + } + } + GetUnoText().insertTextContent(xRange, xContent, bAbsorb); +} + +void SAL_CALL ScCellObj::removeTextContent( const uno::Reference<text::XTextContent>& xContent ) +{ + SolarMutexGuard aGuard; + if ( xContent.is() ) + { + ScEditFieldObj* pCellField = dynamic_cast<ScEditFieldObj*>(xContent.get()); + if ( pCellField && pCellField->IsInserted() ) + { + //! Check if field is in this cell + pCellField->DeleteField(); + return; + } + } + GetUnoText().removeTextContent(xContent); +} + +uno::Reference<text::XText> SAL_CALL ScCellObj::getText() +{ + return this; +} + +uno::Reference<text::XTextRange> SAL_CALL ScCellObj::getStart() +{ + SolarMutexGuard aGuard; + return GetUnoText().getStart(); +} + +uno::Reference<text::XTextRange> SAL_CALL ScCellObj::getEnd() +{ + SolarMutexGuard aGuard; + return GetUnoText().getEnd(); +} + +uno::Reference<container::XEnumeration> SAL_CALL ScCellObj::createEnumeration() +{ + SolarMutexGuard aGuard; + return GetUnoText().createEnumeration(); +} + +uno::Type SAL_CALL ScCellObj::getElementType() +{ + SolarMutexGuard aGuard; + return GetUnoText().getElementType(); +} + +sal_Bool SAL_CALL ScCellObj::hasElements() +{ + SolarMutexGuard aGuard; + return GetUnoText().hasElements(); +} + +// XCell + +OUString SAL_CALL ScCellObj::getFormula() +{ + SolarMutexGuard aGuard; + return GetInputString_Impl( true /* English */ ); +} + +void SAL_CALL ScCellObj::setFormula( const OUString& aFormula ) +{ + SolarMutexGuard aGuard; + SetString_Impl(aFormula, true, true); // Interpret as English +} + +double SAL_CALL ScCellObj::getValue() +{ + SolarMutexGuard aGuard; + return GetValue_Impl(); +} + +void SAL_CALL ScCellObj::setValue( double nValue ) +{ + SolarMutexGuard aGuard; + SetValue_Impl(nValue); +} + +void SAL_CALL ScCellObj::setFormulaString( const OUString& aFormula) +{ + SolarMutexGuard aGuard; + ScDocShell *pDocSh = GetDocShell(); + if( pDocSh ) + { + ScFormulaCell* pCell = new ScFormulaCell( pDocSh->GetDocument(), aCellPos ); + pCell->SetHybridFormula( aFormula, formula::FormulaGrammar::GRAM_NATIVE ); + pDocSh->GetDocFunc().SetFormulaCell(aCellPos, pCell, false); + } +} +void SAL_CALL ScCellObj::setFormulaResult( double nValue ) +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if (pDocSh) + { + ScRefCellValue aCell(pDocSh->GetDocument(), aCellPos); + if (aCell.getType() == CELLTYPE_FORMULA) + { + ScFormulaCell* pCell = aCell.getFormula(); + pCell->SetHybridDouble( nValue ); + pCell->ResetDirty(); + pCell->SetChanged(false); + } + } +} + +table::CellContentType SAL_CALL ScCellObj::getType() +{ + SolarMutexGuard aGuard; + table::CellContentType eRet = table::CellContentType_EMPTY; + ScDocShell* pDocSh = GetDocShell(); + if (pDocSh) + { + CellType eCalcType = pDocSh->GetDocument().GetCellType( aCellPos ); + switch (eCalcType) + { + case CELLTYPE_VALUE: + eRet = table::CellContentType_VALUE; + break; + case CELLTYPE_STRING: + case CELLTYPE_EDIT: + eRet = table::CellContentType_TEXT; + break; + case CELLTYPE_FORMULA: + eRet = table::CellContentType_FORMULA; + break; + default: + eRet = table::CellContentType_EMPTY; + } + } + else + { + OSL_FAIL("no DocShell"); //! Exception or so? + } + + return eRet; +} + +sal_Int32 ScCellObj::GetResultType_Impl() const +{ + SolarMutexGuard aGuard; + sal_Int32 eRet = sheet::FormulaResult::STRING; + ScDocShell* pDocSh = GetDocShell(); + if (pDocSh) + { + if (pDocSh->GetDocument().GetCellType(aCellPos) == CELLTYPE_FORMULA) + { + ScFormulaCell* pFCell = pDocSh->GetDocument().GetFormulaCell(aCellPos); + if (!pFCell) + { + // should throw instead of default string? + } + else if (pFCell->GetErrCode() != FormulaError::NONE ) + { + eRet = sheet::FormulaResult::ERROR; + } + else if (pFCell->IsValue()) + { + eRet = sheet::FormulaResult::VALUE; + } + else + { + eRet = sheet::FormulaResult::STRING; + } + } + } + else + { + OSL_FAIL("no DocShell"); + } + + return eRet; +} + +table::CellContentType ScCellObj::GetContentType_Impl() +{ + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + { + ScRefCellValue aCell(pDocSh->GetDocument(), aCellPos); + if (aCell.getType() == CELLTYPE_FORMULA) + { + bool bValue = aCell.getFormula()->IsValue(); + return bValue ? table::CellContentType_VALUE : table::CellContentType_TEXT; + } + } + return getType(); +} + +sal_Int32 SAL_CALL ScCellObj::getError() +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if (!pDocSh) + { + OSL_FAIL("no DocShell"); //! Exception or so? + return 0; + } + + FormulaError nError = FormulaError::NONE; + ScRefCellValue aCell(pDocSh->GetDocument(), aCellPos); + if (aCell.getType() == CELLTYPE_FORMULA) + nError = aCell.getFormula()->GetErrCode(); + + return static_cast<sal_Int32>(nError); +} + +// XFormulaTokens + +uno::Sequence<sheet::FormulaToken> SAL_CALL ScCellObj::getTokens() +{ + SolarMutexGuard aGuard; + uno::Sequence<sheet::FormulaToken> aSequence; + ScDocShell* pDocSh = GetDocShell(); + if (!pDocSh) + return aSequence; + + ScDocument& rDoc = pDocSh->GetDocument(); + ScRefCellValue aCell(rDoc, aCellPos); + if (aCell.getType() == CELLTYPE_FORMULA) + { + ScTokenArray* pTokenArray = aCell.getFormula()->GetCode(); + if (pTokenArray) + ScTokenConversion::ConvertToTokenSequence(rDoc, aSequence, *pTokenArray); + } + return aSequence; +} + +void SAL_CALL ScCellObj::setTokens( const uno::Sequence<sheet::FormulaToken>& rTokens ) +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + { + ScDocument& rDoc = pDocSh->GetDocument(); + ScTokenArray aTokenArray(rDoc); + (void)ScTokenConversion::ConvertToTokenArray( rDoc, aTokenArray, rTokens ); + + ScFormulaCell* pNewCell = new ScFormulaCell(rDoc, aCellPos, aTokenArray); + (void)pDocSh->GetDocFunc().SetFormulaCell(aCellPos, pNewCell, false); + } +} + +// XCellAddressable + +table::CellAddress SAL_CALL ScCellObj::getCellAddress() +{ + SolarMutexGuard aGuard; + table::CellAddress aAdr; + aAdr.Sheet = aCellPos.Tab(); + aAdr.Column = aCellPos.Col(); + aAdr.Row = aCellPos.Row(); + return aAdr; +} + +// XSheetAnnotationAnchor + +uno::Reference<sheet::XSheetAnnotation> SAL_CALL ScCellObj::getAnnotation() +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + return new ScAnnotationObj( pDocSh, aCellPos ); + + OSL_FAIL("getAnnotation without DocShell"); + return nullptr; +} + +// XFieldTypesSupplier + +uno::Reference<container::XEnumerationAccess> SAL_CALL ScCellObj::getTextFields() +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + { + uno::Reference<text::XTextRange> xContent(this); + return new ScCellFieldsObj(xContent, pDocSh, aCellPos); + } + + return nullptr; +} + +uno::Reference<container::XNameAccess> SAL_CALL ScCellObj::getTextFieldMasters() +{ + // there is no such thing in Calc (?) + return nullptr; +} + +// XPropertySet extended for Cell-Properties + +uno::Reference<beans::XPropertySetInfo> SAL_CALL ScCellObj::getPropertySetInfo() +{ + SolarMutexGuard aGuard; + static uno::Reference<beans::XPropertySetInfo> aRef( + new SfxItemPropertySetInfo( pCellPropSet->getPropertyMap() )); + return aRef; +} + +void ScCellObj::SetOnePropertyValue( const SfxItemPropertyMapEntry* pEntry, const uno::Any& aValue ) +{ + if ( !pEntry ) + return; + + if ( pEntry->nWID == SC_WID_UNO_FORMLOC ) + { + OUString aStrVal; + aValue >>= aStrVal; + SetString_Impl(aStrVal, true, false); // interpret locally + } + else if ( pEntry->nWID == SC_WID_UNO_FORMRT || pEntry->nWID == SC_WID_UNO_FORMRT2 + || pEntry->nWID == SC_WID_UNO_CELLCONTENTTYPE ) + { + // Read-Only + //! Exception or so... + } + else + ScCellRangeObj::SetOnePropertyValue( pEntry, aValue ); +} + +void ScCellObj::GetOnePropertyValue( const SfxItemPropertyMapEntry* pEntry, uno::Any& rAny ) +{ + if ( !pEntry ) + return; + + if ( pEntry->nWID == SC_WID_UNO_FORMLOC ) + { + // sal_False = local + rAny <<= GetInputString_Impl(false); + } + else if ( pEntry->nWID == SC_WID_UNO_FORMRT2 ) + { + sal_Int32 eType = GetResultType_Impl(); + rAny <<= eType; + } + else if ( pEntry->nWID == SC_WID_UNO_CELLCONTENTTYPE || pEntry->nWID == SC_WID_UNO_FORMRT ) + { + table::CellContentType eType = GetContentType_Impl(); + rAny <<= eType; + } + else + ScCellRangeObj::GetOnePropertyValue(pEntry, rAny); +} + +const SfxItemPropertyMap& ScCellObj::GetItemPropertyMap() +{ + return pCellPropSet->getPropertyMap(); +} + +// XServiceInfo + +OUString SAL_CALL ScCellObj::getImplementationName() +{ + return "ScCellObj"; +} + +sal_Bool SAL_CALL ScCellObj::supportsService( const OUString& rServiceName ) +{ + return cppu::supportsService(this, rServiceName); +} + +uno::Sequence<OUString> SAL_CALL ScCellObj::getSupportedServiceNames() +{ + return {SCSHEETCELL_SERVICE, + SCCELL_SERVICE, + SCCELLPROPERTIES_SERVICE, + SCCHARPROPERTIES_SERVICE, + SCPARAPROPERTIES_SERVICE, + SCSHEETCELLRANGE_SERVICE, + SCCELLRANGE_SERVICE}; +} + +// XActionLockable + +sal_Bool SAL_CALL ScCellObj::isActionLocked() +{ + SolarMutexGuard aGuard; + return nActionLockCount != 0; +} + +void SAL_CALL ScCellObj::addActionLock() +{ + SolarMutexGuard aGuard; + if (!nActionLockCount) + { + if (mxUnoText.is()) + { + ScCellEditSource* pEditSource = + static_cast<ScCellEditSource*> (mxUnoText->GetEditSource()); + if (pEditSource) + pEditSource->SetDoUpdateData(false); + } + } + nActionLockCount++; +} + +void SAL_CALL ScCellObj::removeActionLock() +{ + SolarMutexGuard aGuard; + if (nActionLockCount <= 0) + return; + + nActionLockCount--; + if (nActionLockCount) + return; + + if (mxUnoText.is()) + { + ScCellEditSource* pEditSource = + static_cast<ScCellEditSource*> (mxUnoText->GetEditSource()); + if (pEditSource) + { + pEditSource->SetDoUpdateData(true); + if (pEditSource->IsDirty()) + pEditSource->UpdateData(); + } + } +} + +void SAL_CALL ScCellObj::setActionLocks( sal_Int16 nLock ) +{ + SolarMutexGuard aGuard; + if (mxUnoText.is()) + { + ScCellEditSource* pEditSource = + static_cast<ScCellEditSource*> (mxUnoText->GetEditSource()); + if (pEditSource) + { + pEditSource->SetDoUpdateData(nLock == 0); + if ((nActionLockCount > 0) && (nLock == 0) && pEditSource->IsDirty()) + pEditSource->UpdateData(); + } + } + nActionLockCount = nLock; +} + +sal_Int16 SAL_CALL ScCellObj::resetActionLocks() +{ + SolarMutexGuard aGuard; + sal_uInt16 nRet(nActionLockCount); + if (mxUnoText.is()) + { + ScCellEditSource* pEditSource = + static_cast<ScCellEditSource*> (mxUnoText->GetEditSource()); + if (pEditSource) + { + pEditSource->SetDoUpdateData(true); + if (pEditSource->IsDirty()) + pEditSource->UpdateData(); + } + } + nActionLockCount = 0; + return nRet; +} + +static ScRange MaxDocRange(ScDocShell* pDocSh, SCTAB nTab) +{ + const SCCOL nMaxcol = pDocSh ? pDocSh->GetDocument().MaxCol() : MAXCOL; + const SCROW nMaxRow = pDocSh ? pDocSh->GetDocument().MaxRow() : MAXROW; + return ScRange(0, 0, nTab, nMaxcol, nMaxRow, nTab); +} + +ScTableSheetObj::ScTableSheetObj( ScDocShell* pDocSh, SCTAB nTab ) : + ScCellRangeObj( pDocSh, MaxDocRange(pDocSh, nTab) ), + pSheetPropSet(lcl_GetSheetPropertySet()) +{ +} + +ScTableSheetObj::~ScTableSheetObj() +{ +} + +void ScTableSheetObj::InitInsertSheet(ScDocShell* pDocSh, SCTAB nTab) +{ + ScDocument& rDoc = pDocSh->GetDocument(); + InitInsertRange( pDocSh, ScRange(0,0,nTab, rDoc.MaxCol(),rDoc.MaxRow(),nTab) ); +} + +uno::Any SAL_CALL ScTableSheetObj::queryInterface( const uno::Type& rType ) +{ + uno::Any aReturn = ::cppu::queryInterface(rType, + static_cast<sheet::XSpreadsheet*>(this), + static_cast<container::XNamed*>(this), + static_cast<sheet::XSheetPageBreak*>(this), + static_cast<sheet::XCellRangeMovement*>(this), + static_cast<table::XTableChartsSupplier*>(this), + static_cast<sheet::XDataPilotTablesSupplier*>(this), + static_cast<sheet::XScenariosSupplier*>(this), + static_cast<sheet::XSheetAnnotationsSupplier*>(this), + static_cast<drawing::XDrawPageSupplier*>(this), + static_cast<sheet::XPrintAreas*>(this), + static_cast<sheet::XSheetAuditing*>(this), + static_cast<sheet::XSheetOutline*>(this), + static_cast<util::XProtectable*>(this), + static_cast<sheet::XScenario*>(this), + static_cast<sheet::XScenarioEnhanced*>(this), + static_cast<sheet::XSheetLinkable*>(this), + static_cast<sheet::XExternalSheetName*>(this), + static_cast<document::XEventsSupplier*>(this), + static_cast<table::XTablePivotChartsSupplier*>(this)); + if ( aReturn.hasValue() ) + return aReturn; + + return ScCellRangeObj::queryInterface( rType ); +} + +void SAL_CALL ScTableSheetObj::acquire() noexcept +{ + ScCellRangeObj::acquire(); +} + +void SAL_CALL ScTableSheetObj::release() noexcept +{ + ScCellRangeObj::release(); +} + +uno::Sequence<uno::Type> SAL_CALL ScTableSheetObj::getTypes() +{ + static const uno::Sequence<uno::Type> aTypes = comphelper::concatSequences( + ScCellRangeObj::getTypes(), + uno::Sequence<uno::Type> + { + cppu::UnoType<sheet::XSpreadsheet>::get(), + cppu::UnoType<container::XNamed>::get(), + cppu::UnoType<sheet::XSheetPageBreak>::get(), + cppu::UnoType<sheet::XCellRangeMovement>::get(), + cppu::UnoType<table::XTableChartsSupplier>::get(), + cppu::UnoType<sheet::XDataPilotTablesSupplier>::get(), + cppu::UnoType<sheet::XScenariosSupplier>::get(), + cppu::UnoType<sheet::XSheetAnnotationsSupplier>::get(), + cppu::UnoType<drawing::XDrawPageSupplier>::get(), + cppu::UnoType<sheet::XPrintAreas>::get(), + cppu::UnoType<sheet::XSheetAuditing>::get(), + cppu::UnoType<sheet::XSheetOutline>::get(), + cppu::UnoType<util::XProtectable>::get(), + cppu::UnoType<sheet::XScenario>::get(), + cppu::UnoType<sheet::XScenarioEnhanced>::get(), + cppu::UnoType<sheet::XSheetLinkable>::get(), + cppu::UnoType<sheet::XExternalSheetName>::get(), + cppu::UnoType<document::XEventsSupplier>::get(), + cppu::UnoType<table::XTablePivotChartsSupplier>::get() + } ); + return aTypes; +} + +uno::Sequence<sal_Int8> SAL_CALL ScTableSheetObj::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +// Helper functions + +SCTAB ScTableSheetObj::GetTab_Impl() const +{ + const ScRangeList& rRanges = GetRangeList(); + OSL_ENSURE(rRanges.size() == 1, "What ranges ?!?!"); + if ( !rRanges.empty() ) + { + return rRanges[ 0 ].aStart.Tab(); + } + return 0; +} + +// former XSheet + +uno::Reference<table::XTableCharts> SAL_CALL ScTableSheetObj::getCharts() +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + return new ScChartsObj( pDocSh, GetTab_Impl() ); + + OSL_FAIL("no document"); + return nullptr; +} + +uno::Reference<table::XTablePivotCharts> SAL_CALL ScTableSheetObj::getPivotCharts() +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if (pDocSh) + return new sc::TablePivotCharts(pDocSh, GetTab_Impl()); + + OSL_FAIL("no document"); + return nullptr; +} + +uno::Reference<sheet::XDataPilotTables> SAL_CALL ScTableSheetObj::getDataPilotTables() +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + return new ScDataPilotTablesObj(*pDocSh, GetTab_Impl()); + + OSL_FAIL("no document"); + return nullptr; +} + +uno::Reference<sheet::XScenarios> SAL_CALL ScTableSheetObj::getScenarios() +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + + if ( pDocSh ) + return new ScScenariosObj( pDocSh, GetTab_Impl() ); + + OSL_FAIL("no document"); + return nullptr; +} + +uno::Reference<sheet::XSheetAnnotations> SAL_CALL ScTableSheetObj::getAnnotations() +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + + if ( pDocSh ) + return new ScAnnotationsObj( pDocSh, GetTab_Impl() ); + + OSL_FAIL("no document"); + return nullptr; +} + +uno::Reference<table::XCellRange> SAL_CALL ScTableSheetObj::getCellRangeByName( + const OUString& rRange ) +{ + SolarMutexGuard aGuard; + return ScCellRangeObj::getCellRangeByName( rRange ); +} + +uno::Reference<sheet::XSheetCellCursor> SAL_CALL ScTableSheetObj::createCursor() +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + { + //! single cell or whole table?????? + const ScDocument& rDoc = pDocSh->GetDocument(); + SCTAB nTab = GetTab_Impl(); + return new ScCellCursorObj( pDocSh, ScRange( 0,0,nTab, rDoc.MaxCol(),rDoc.MaxRow(),nTab ) ); + } + return nullptr; +} + +uno::Reference<sheet::XSheetCellCursor> SAL_CALL ScTableSheetObj::createCursorByRange( + const uno::Reference<sheet::XSheetCellRange>& xCellRange ) +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh && xCellRange.is() ) + { + ScCellRangesBase* pRangesImp = dynamic_cast<ScCellRangesBase*>( xCellRange.get() ); + if (pRangesImp) + { + const ScRangeList& rRanges = pRangesImp->GetRangeList(); + SAL_WARN_IF( rRanges.size() != 1, "sc", "ScTableSheetObj::createCursorByRange: Range? Ranges?"); + if (rRanges.empty()) + return nullptr; + return new ScCellCursorObj( pDocSh, rRanges[ 0 ] ); + } + } + return nullptr; +} + +// XSheetCellRange + +uno::Reference<sheet::XSpreadsheet> SAL_CALL ScTableSheetObj::getSpreadsheet() +{ + return this; //!??? +} + +// XCellRange + +uno::Reference<table::XCell> SAL_CALL ScTableSheetObj::getCellByPosition( + sal_Int32 nColumn, sal_Int32 nRow ) +{ + SolarMutexGuard aGuard; + return ScCellRangeObj::GetCellByPosition_Impl(nColumn, nRow); +} + +uno::Reference<table::XCellRange> SAL_CALL ScTableSheetObj::getCellRangeByPosition( + sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom ) +{ + SolarMutexGuard aGuard; + return ScCellRangeObj::getCellRangeByPosition(nLeft,nTop,nRight,nBottom); +} + +uno::Sequence<sheet::TablePageBreakData> SAL_CALL ScTableSheetObj::getColumnPageBreaks() +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + { + ScDocument& rDoc = pDocSh->GetDocument(); + SCTAB nTab = GetTab_Impl(); + + Size aSize(rDoc.GetPageSize( nTab )); + if (aSize.Width() && aSize.Height()) // effective size already set? + rDoc.UpdatePageBreaks( nTab ); + else + { + // update breaks like in ScDocShell::PageStyleModified: + ScPrintFunc aPrintFunc( pDocSh, pDocSh->GetPrinter(), nTab ); + aPrintFunc.UpdatePages(); + } + + SCCOL nCount = 0; + for (SCCOL nCol : rDoc.GetColumnsRange(nTab, 0, rDoc.MaxCol())) + if (rDoc.HasColBreak(nCol, nTab) != ScBreakType::NONE) + ++nCount; + + sheet::TablePageBreakData aData; + uno::Sequence<sheet::TablePageBreakData> aSeq(nCount); + sheet::TablePageBreakData* pAry = aSeq.getArray(); + sal_uInt16 nPos = 0; + for (SCCOL nCol : rDoc.GetColumnsRange(nTab, 0, rDoc.MaxCol())) + { + ScBreakType nBreak = rDoc.HasColBreak(nCol, nTab); + if (nBreak != ScBreakType::NONE) + { + aData.Position = nCol; + aData.ManualBreak = bool(nBreak & ScBreakType::Manual); + pAry[nPos] = aData; + ++nPos; + } + } + return aSeq; + } + return {}; +} + +uno::Sequence<sheet::TablePageBreakData> SAL_CALL ScTableSheetObj::getRowPageBreaks() +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + { + ScDocument& rDoc = pDocSh->GetDocument(); + SCTAB nTab = GetTab_Impl(); + + Size aSize(rDoc.GetPageSize( nTab )); + if (aSize.Width() && aSize.Height()) // effective size already set? + rDoc.UpdatePageBreaks( nTab ); + else + { + // update breaks like in ScDocShell::PageStyleModified: + ScPrintFunc aPrintFunc( pDocSh, pDocSh->GetPrinter(), nTab ); + aPrintFunc.UpdatePages(); + } + return rDoc.GetRowBreakData(nTab); + } + return {}; +} + +void SAL_CALL ScTableSheetObj::removeAllManualPageBreaks() +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( !pDocSh ) + return; + + //! DocFunc function, also for ScViewFunc::RemoveManualBreaks + + ScDocument& rDoc = pDocSh->GetDocument(); + bool bUndo (rDoc.IsUndoEnabled()); + SCTAB nTab = GetTab_Impl(); + + if (bUndo) + { + ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO )); + pUndoDoc->InitUndo( rDoc, nTab, nTab, true, true ); + rDoc.CopyToDocument(0,0,nTab, rDoc.MaxCol(),rDoc.MaxRow(),nTab, InsertDeleteFlags::NONE, false, *pUndoDoc); + pDocSh->GetUndoManager()->AddUndoAction( + std::make_unique<ScUndoRemoveBreaks>( pDocSh, nTab, std::move(pUndoDoc) ) ); + } + + rDoc.RemoveManualBreaks(nTab); + rDoc.UpdatePageBreaks(nTab); + + //? UpdatePageBreakData( sal_True ); + pDocSh->SetDocumentModified(); + pDocSh->PostPaint(ScRange(0, 0, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab), PaintPartFlags::Grid); +} + +// XNamed + +OUString SAL_CALL ScTableSheetObj::getName() +{ + SolarMutexGuard aGuard; + OUString aName; + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + pDocSh->GetDocument().GetName( GetTab_Impl(), aName ); + return aName; +} + +void SAL_CALL ScTableSheetObj::setName( const OUString& aNewName ) +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + { + pDocSh->GetDocFunc().RenameTable( GetTab_Impl(), aNewName, true, true ); + } +} + +// XDrawPageSupplier + +uno::Reference<drawing::XDrawPage> SAL_CALL ScTableSheetObj::getDrawPage() +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + { + ScDrawLayer* pDrawLayer = pDocSh->MakeDrawLayer(); + OSL_ENSURE(pDrawLayer,"Cannot create Draw-Layer"); + + SCTAB nTab = GetTab_Impl(); + SdrPage* pPage = pDrawLayer->GetPage(static_cast<sal_uInt16>(nTab)); + OSL_ENSURE(pPage,"Draw-Page not found"); + if (pPage) + return uno::Reference<drawing::XDrawPage> (pPage->getUnoPage(), uno::UNO_QUERY); + + // The DrawPage object will register itself as a Listener at SdrModel + // and should receive all action from there + } + return nullptr; +} + +// XCellMovement + +void SAL_CALL ScTableSheetObj::insertCells( const table::CellRangeAddress& rRangeAddress, + sheet::CellInsertMode nMode ) +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( !pDocSh ) + return; + + bool bDo = true; + InsCellCmd eCmd = INS_NONE; + switch (nMode) + { + case sheet::CellInsertMode_NONE: bDo = false; break; + case sheet::CellInsertMode_DOWN: eCmd = INS_CELLSDOWN; break; + case sheet::CellInsertMode_RIGHT: eCmd = INS_CELLSRIGHT; break; + case sheet::CellInsertMode_ROWS: eCmd = INS_INSROWS_BEFORE; break; + case sheet::CellInsertMode_COLUMNS: eCmd = INS_INSCOLS_BEFORE; break; + default: + OSL_FAIL("insertCells: wrong mode"); + bDo = false; + } + + if (bDo) + { + OSL_ENSURE( rRangeAddress.Sheet == GetTab_Impl(), "wrong table in CellRangeAddress" ); + ScRange aScRange; + ScUnoConversion::FillScRange( aScRange, rRangeAddress ); + (void)pDocSh->GetDocFunc().InsertCells( aScRange, nullptr, eCmd, true, true ); + } +} + +void SAL_CALL ScTableSheetObj::removeRange( const table::CellRangeAddress& rRangeAddress, + sheet::CellDeleteMode nMode ) +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( !pDocSh ) + return; + + bool bDo = true; + DelCellCmd eCmd = DelCellCmd::NONE; + switch (nMode) + { + case sheet::CellDeleteMode_NONE: bDo = false; break; + case sheet::CellDeleteMode_UP: eCmd = DelCellCmd::CellsUp; break; + case sheet::CellDeleteMode_LEFT: eCmd = DelCellCmd::CellsLeft; break; + case sheet::CellDeleteMode_ROWS: eCmd = DelCellCmd::Rows; break; + case sheet::CellDeleteMode_COLUMNS: eCmd = DelCellCmd::Cols; break; + default: + OSL_FAIL("deleteCells: wrong mode"); + bDo = false; + } + + if (bDo) + { + OSL_ENSURE( rRangeAddress.Sheet == GetTab_Impl(), "wrong table in CellRangeAddress" ); + ScRange aScRange; + ScUnoConversion::FillScRange( aScRange, rRangeAddress ); + (void)pDocSh->GetDocFunc().DeleteCells( aScRange, nullptr, eCmd, true ); + } +} + +void SAL_CALL ScTableSheetObj::moveRange( const table::CellAddress& aDestination, + const table::CellRangeAddress& aSource ) +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + { + OSL_ENSURE( aSource.Sheet == GetTab_Impl(), "wrong table in CellRangeAddress" ); + ScRange aSourceRange; + ScUnoConversion::FillScRange( aSourceRange, aSource ); + ScAddress aDestPos( static_cast<SCCOL>(aDestination.Column), static_cast<SCROW>(aDestination.Row), aDestination.Sheet ); + (void)pDocSh->GetDocFunc().MoveBlock( aSourceRange, aDestPos, true, true, true, true ); + } +} + +void SAL_CALL ScTableSheetObj::copyRange( const table::CellAddress& aDestination, + const table::CellRangeAddress& aSource ) +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + { + OSL_ENSURE( aSource.Sheet == GetTab_Impl(), "wrong table in CellRangeAddress" ); + ScRange aSourceRange; + ScUnoConversion::FillScRange( aSourceRange, aSource ); + ScAddress aDestPos( static_cast<SCCOL>(aDestination.Column), static_cast<SCROW>(aDestination.Row), aDestination.Sheet ); + (void)pDocSh->GetDocFunc().MoveBlock( aSourceRange, aDestPos, false, true, true, true ); + } +} + +// XPrintAreas + +void ScTableSheetObj::PrintAreaUndo_Impl( std::unique_ptr<ScPrintRangeSaver> pOldRanges ) +{ + // page break and undo + ScDocShell* pDocSh = GetDocShell(); + + if(!pDocSh) + return; + + ScDocument& rDoc = pDocSh->GetDocument(); + const bool bUndo(rDoc.IsUndoEnabled()); + const SCTAB nTab(GetTab_Impl()); + + if(bUndo) + { + pDocSh->GetUndoManager()->AddUndoAction( + std::make_unique<ScUndoPrintRange>( + pDocSh, + nTab, + std::move(pOldRanges), + rDoc.CreatePrintRangeSaver())); // create new ranges + } + + ScPrintFunc(pDocSh, pDocSh->GetPrinter(), nTab).UpdatePages(); + SfxBindings* pBindings = pDocSh->GetViewBindings(); + + if(pBindings) + { + pBindings->Invalidate(SID_DELETE_PRINTAREA); + } + + pDocSh->SetDocumentModified(); +} + +uno::Sequence<table::CellRangeAddress> SAL_CALL ScTableSheetObj::getPrintAreas() +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + { + ScDocument& rDoc = pDocSh->GetDocument(); + SCTAB nTab = GetTab_Impl(); + sal_uInt16 nCount = rDoc.GetPrintRangeCount( nTab ); + + table::CellRangeAddress aRangeAddress; + uno::Sequence<table::CellRangeAddress> aSeq(nCount); + table::CellRangeAddress* pAry = aSeq.getArray(); + for (sal_uInt16 i=0; i<nCount; i++) + { + const ScRange* pRange = rDoc.GetPrintRange( nTab, i ); + OSL_ENSURE(pRange,"where is the printing area"); + if (pRange) + { + ScUnoConversion::FillApiRange( aRangeAddress, *pRange ); + aRangeAddress.Sheet = nTab; // core does not care about sheet index + pAry[i] = aRangeAddress; + } + } + return aSeq; + } + return uno::Sequence<table::CellRangeAddress>(); +} + +void SAL_CALL ScTableSheetObj::setPrintAreas( + const uno::Sequence<table::CellRangeAddress>& aPrintAreas ) +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( !pDocSh ) + return; + + std::unique_ptr<ScPrintRangeSaver> pOldRanges; + ScDocument& rDoc = pDocSh->GetDocument(); + SCTAB nTab = GetTab_Impl(); + + if ( rDoc.IsUndoEnabled() ) + pOldRanges = rDoc.CreatePrintRangeSaver(); + + sal_uInt16 nCount = static_cast<sal_uInt16>(aPrintAreas.getLength()); + rDoc.ClearPrintRanges( nTab ); + if (nCount) + { + ScRange aPrintRange; + for (const table::CellRangeAddress& rPrintArea : aPrintAreas) + { + ScUnoConversion::FillScRange( aPrintRange, rPrintArea ); + rDoc.AddPrintRange( nTab, aPrintRange ); + } + } + + if ( rDoc.IsUndoEnabled() ) + PrintAreaUndo_Impl( std::move(pOldRanges) ); // Undo, Page Breaks, Modified etc. +} + +sal_Bool SAL_CALL ScTableSheetObj::getPrintTitleColumns() +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + { + ScDocument& rDoc = pDocSh->GetDocument(); + SCTAB nTab = GetTab_Impl(); + return rDoc.GetRepeatColRange(nTab).has_value(); + } + return false; +} + +void SAL_CALL ScTableSheetObj::setPrintTitleColumns( sal_Bool bPrintTitleColumns ) +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( !pDocSh ) + return; + + ScDocument& rDoc = pDocSh->GetDocument(); + SCTAB nTab = GetTab_Impl(); + + std::unique_ptr<ScPrintRangeSaver> pOldRanges = rDoc.CreatePrintRangeSaver(); + + if ( bPrintTitleColumns ) + { + if ( !rDoc.GetRepeatColRange( nTab ) ) // do not change existing area + { + rDoc.SetRepeatColRange( nTab, ScRange( 0, 0, nTab, 0, 0, nTab ) ); // enable + } + } + else + rDoc.SetRepeatColRange( nTab, std::nullopt ); // disable + + PrintAreaUndo_Impl( std::move(pOldRanges) ); // undo, page break, modified etc. + + //! save last set area during switch off and recreate during switch on ??? +} + +table::CellRangeAddress SAL_CALL ScTableSheetObj::getTitleColumns() +{ + SolarMutexGuard aGuard; + table::CellRangeAddress aRet; + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + { + ScDocument& rDoc = pDocSh->GetDocument(); + SCTAB nTab = GetTab_Impl(); + std::optional<ScRange> oRange = rDoc.GetRepeatColRange(nTab); + if (oRange) + { + ScUnoConversion::FillApiRange( aRet, *oRange ); + aRet.Sheet = nTab; // core does not care about sheet index + } + } + return aRet; +} + +void SAL_CALL ScTableSheetObj::setTitleColumns( const table::CellRangeAddress& aTitleColumns ) +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( !pDocSh ) + return; + + ScDocument& rDoc = pDocSh->GetDocument(); + SCTAB nTab = GetTab_Impl(); + + std::unique_ptr<ScPrintRangeSaver> pOldRanges = rDoc.CreatePrintRangeSaver(); + + ScRange aNew; + ScUnoConversion::FillScRange( aNew, aTitleColumns ); + rDoc.SetRepeatColRange( nTab, std::move(aNew) ); // also always enable + + PrintAreaUndo_Impl( std::move(pOldRanges) ); // undo, page breaks, modified etc. +} + +sal_Bool SAL_CALL ScTableSheetObj::getPrintTitleRows() +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + { + ScDocument& rDoc = pDocSh->GetDocument(); + SCTAB nTab = GetTab_Impl(); + return rDoc.GetRepeatRowRange(nTab).has_value(); + } + return false; +} + +void SAL_CALL ScTableSheetObj::setPrintTitleRows( sal_Bool bPrintTitleRows ) +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( !pDocSh ) + return; + + ScDocument& rDoc = pDocSh->GetDocument(); + SCTAB nTab = GetTab_Impl(); + + std::unique_ptr<ScPrintRangeSaver> pOldRanges = rDoc.CreatePrintRangeSaver(); + + if ( bPrintTitleRows ) + { + if ( !rDoc.GetRepeatRowRange( nTab ) ) // do not change existing area + { + rDoc.SetRepeatRowRange( nTab, ScRange(0, 0, nTab, 0, 0, nTab) ); // enable + } + } + else + rDoc.SetRepeatRowRange( nTab, std::nullopt ); // disable + + PrintAreaUndo_Impl( std::move(pOldRanges) ); // undo, page breaks, modified etc. + + //! save last set area during switch off and recreate during switch on ??? +} + +table::CellRangeAddress SAL_CALL ScTableSheetObj::getTitleRows() +{ + SolarMutexGuard aGuard; + table::CellRangeAddress aRet; + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + { + ScDocument& rDoc = pDocSh->GetDocument(); + SCTAB nTab = GetTab_Impl(); + std::optional<ScRange> oRange = rDoc.GetRepeatRowRange(nTab); + if (oRange) + { + ScUnoConversion::FillApiRange( aRet, *oRange ); + aRet.Sheet = nTab; // core does not care about sheet index + } + } + return aRet; +} + +void SAL_CALL ScTableSheetObj::setTitleRows( const table::CellRangeAddress& aTitleRows ) +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( !pDocSh ) + return; + + ScDocument& rDoc = pDocSh->GetDocument(); + SCTAB nTab = GetTab_Impl(); + + std::unique_ptr<ScPrintRangeSaver> pOldRanges = rDoc.CreatePrintRangeSaver(); + + ScRange aNew; + ScUnoConversion::FillScRange( aNew, aTitleRows ); + rDoc.SetRepeatRowRange( nTab, std::move(aNew) ); // also always enable + + PrintAreaUndo_Impl( std::move(pOldRanges) ); // Undo, page breaks, modified etc. +} + +// XSheetLinkable + +sheet::SheetLinkMode SAL_CALL ScTableSheetObj::getLinkMode() +{ + SolarMutexGuard aGuard; + sheet::SheetLinkMode eRet = sheet::SheetLinkMode_NONE; + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + { + ScLinkMode nMode = pDocSh->GetDocument().GetLinkMode( GetTab_Impl() ); + if ( nMode == ScLinkMode::NORMAL ) + eRet = sheet::SheetLinkMode_NORMAL; + else if ( nMode == ScLinkMode::VALUE ) + eRet = sheet::SheetLinkMode_VALUE; + } + return eRet; +} + +void SAL_CALL ScTableSheetObj::setLinkMode( sheet::SheetLinkMode nLinkMode ) +{ + SolarMutexGuard aGuard; + + //! search for filter and options in old link + + OUString aUrl(getLinkUrl()); + OUString aSheet(getLinkSheetName()); + + link( aUrl, aSheet, "", "", nLinkMode ); +} + +OUString SAL_CALL ScTableSheetObj::getLinkUrl() +{ + SolarMutexGuard aGuard; + OUString aFile; + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + aFile = pDocSh->GetDocument().GetLinkDoc( GetTab_Impl() ); + return aFile; +} + +void SAL_CALL ScTableSheetObj::setLinkUrl( const OUString& aLinkUrl ) +{ + SolarMutexGuard aGuard; + + //! search for filter and options in old link + + sheet::SheetLinkMode eMode = getLinkMode(); + OUString aSheet(getLinkSheetName()); + + link( aLinkUrl, aSheet, "", "", eMode ); +} + +OUString SAL_CALL ScTableSheetObj::getLinkSheetName() +{ + SolarMutexGuard aGuard; + OUString aSheet; + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + aSheet = pDocSh->GetDocument().GetLinkTab( GetTab_Impl() ); + return aSheet; +} + +void SAL_CALL ScTableSheetObj::setLinkSheetName( const OUString& aLinkSheetName ) +{ + SolarMutexGuard aGuard; + + //! search for filter and options in old link + + sheet::SheetLinkMode eMode = getLinkMode(); + OUString aUrl(getLinkUrl()); + + link( aUrl, aLinkSheetName, "", "", eMode ); +} + +void SAL_CALL ScTableSheetObj::link( const OUString& aUrl, const OUString& aSheetName, + const OUString& aFilterName, const OUString& aFilterOptions, + sheet::SheetLinkMode nMode ) +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( !pDocSh ) + return; + + ScDocument& rDoc = pDocSh->GetDocument(); + SCTAB nTab = GetTab_Impl(); + + OUString aFileString = aUrl; + OUString aFilterString = aFilterName; + OUString aOptString = aFilterOptions; + + aFileString = ScGlobal::GetAbsDocName( aFileString, pDocSh ); + if (aFilterString.isEmpty()) + ScDocumentLoader::GetFilterName( aFileString, aFilterString, aOptString, true, false ); + + // remove application prefix from filter name here, so the filter options + // aren't reset when the filter name is changed in ScTableLink::DataChanged + ScDocumentLoader::RemoveAppPrefix( aFilterString ); + + ScLinkMode nLinkMode = ScLinkMode::NONE; + if ( nMode == sheet::SheetLinkMode_NORMAL ) + nLinkMode = ScLinkMode::NORMAL; + else if ( nMode == sheet::SheetLinkMode_VALUE ) + nLinkMode = ScLinkMode::VALUE; + + rDoc.SetLink( nTab, nLinkMode, aFileString, aFilterString, aOptString, aSheetName, 0/*nRefresh*/ ); + + pDocSh->UpdateLinks(); // if needed add or delete link + SfxBindings* pBindings = pDocSh->GetViewBindings(); + if (pBindings) + pBindings->Invalidate(SID_LINKS); + + //! undo of link data on the table + + if ( !(nLinkMode != ScLinkMode::NONE && rDoc.IsExecuteLinkEnabled()) ) // update link + return; + + // Always update link also if already exists + //! update only on the affected table??? + + sfx2::LinkManager* pLinkManager = rDoc.GetLinkManager(); + sal_uInt16 nCount = pLinkManager->GetLinks().size(); + for ( sal_uInt16 i=0; i<nCount; i++ ) + { + ::sfx2::SvBaseLink* pBase = pLinkManager->GetLinks()[i].get(); + if (auto pTabLink = dynamic_cast<ScTableLink*>( pBase)) + { + if ( aFileString == pTabLink->GetFileName() ) + pTabLink->Update(); // include Paint&Undo + + //! The file name should only exists once (?) + } + } + + //! notify ScSheetLinkObj objects!!! +} + +// XSheetAuditing + +sal_Bool SAL_CALL ScTableSheetObj::hideDependents( const table::CellAddress& aPosition ) +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + { + SCTAB nTab = GetTab_Impl(); + OSL_ENSURE( aPosition.Sheet == nTab, "wrong table in CellAddress" ); + ScAddress aPos( static_cast<SCCOL>(aPosition.Column), static_cast<SCROW>(aPosition.Row), nTab ); + return pDocSh->GetDocFunc().DetectiveDelSucc( aPos ); + } + return false; +} + +sal_Bool SAL_CALL ScTableSheetObj::hidePrecedents( const table::CellAddress& aPosition ) +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + { + SCTAB nTab = GetTab_Impl(); + OSL_ENSURE( aPosition.Sheet == nTab, "wrong table in CellAddress" ); + ScAddress aPos( static_cast<SCCOL>(aPosition.Column), static_cast<SCROW>(aPosition.Row), nTab ); + return pDocSh->GetDocFunc().DetectiveDelPred( aPos ); + } + return false; +} + +sal_Bool SAL_CALL ScTableSheetObj::showDependents( const table::CellAddress& aPosition ) +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + { + SCTAB nTab = GetTab_Impl(); + OSL_ENSURE( aPosition.Sheet == nTab, "wrong table in CellAddress" ); + ScAddress aPos( static_cast<SCCOL>(aPosition.Column), static_cast<SCROW>(aPosition.Row), nTab ); + return pDocSh->GetDocFunc().DetectiveAddSucc( aPos ); + } + return false; +} + +sal_Bool SAL_CALL ScTableSheetObj::showPrecedents( const table::CellAddress& aPosition ) +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + { + SCTAB nTab = GetTab_Impl(); + OSL_ENSURE( aPosition.Sheet == nTab, "wrong table in CellAddress" ); + ScAddress aPos( static_cast<SCCOL>(aPosition.Column), static_cast<SCROW>(aPosition.Row), nTab ); + return pDocSh->GetDocFunc().DetectiveAddPred( aPos ); + } + return false; +} + +sal_Bool SAL_CALL ScTableSheetObj::showErrors( const table::CellAddress& aPosition ) +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + { + SCTAB nTab = GetTab_Impl(); + OSL_ENSURE( aPosition.Sheet == nTab, "wrong table in CellAddress" ); + ScAddress aPos( static_cast<SCCOL>(aPosition.Column), static_cast<SCROW>(aPosition.Row), nTab ); + return pDocSh->GetDocFunc().DetectiveAddError( aPos ); + } + return false; +} + +sal_Bool SAL_CALL ScTableSheetObj::showInvalid() +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + return pDocSh->GetDocFunc().DetectiveMarkInvalid( GetTab_Impl() ); + return false; +} + +void SAL_CALL ScTableSheetObj::clearArrows() +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + pDocSh->GetDocFunc().DetectiveDelAll( GetTab_Impl() ); +} + +// XSheetOutline + +void SAL_CALL ScTableSheetObj::group( const table::CellRangeAddress& rGroupRange, + table::TableOrientation nOrientation ) +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + { + bool bColumns = ( nOrientation == table::TableOrientation_COLUMNS ); + ScRange aGroupRange; + ScUnoConversion::FillScRange( aGroupRange, rGroupRange ); + ScOutlineDocFunc aFunc(*pDocSh); + aFunc.MakeOutline( aGroupRange, bColumns, true, true ); + } +} + +void SAL_CALL ScTableSheetObj::ungroup( const table::CellRangeAddress& rGroupRange, + table::TableOrientation nOrientation ) +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + { + bool bColumns = ( nOrientation == table::TableOrientation_COLUMNS ); + ScRange aGroupRange; + ScUnoConversion::FillScRange( aGroupRange, rGroupRange ); + ScOutlineDocFunc aFunc(*pDocSh); + aFunc.RemoveOutline( aGroupRange, bColumns, true, true ); + } +} + +void SAL_CALL ScTableSheetObj::autoOutline( const table::CellRangeAddress& rCellRange ) +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + { + ScRange aFormulaRange; + ScUnoConversion::FillScRange( aFormulaRange, rCellRange ); + ScOutlineDocFunc aFunc(*pDocSh); + aFunc.AutoOutline( aFormulaRange, true ); + } +} + +void SAL_CALL ScTableSheetObj::clearOutline() +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + { + SCTAB nTab = GetTab_Impl(); + ScOutlineDocFunc aFunc(*pDocSh); + aFunc.RemoveAllOutlines( nTab, true ); + } +} + +void SAL_CALL ScTableSheetObj::hideDetail( const table::CellRangeAddress& rCellRange ) +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + { + ScRange aMarkRange; + ScUnoConversion::FillScRange( aMarkRange, rCellRange ); + ScOutlineDocFunc aFunc(*pDocSh); + aFunc.HideMarkedOutlines( aMarkRange, true ); + } +} + +void SAL_CALL ScTableSheetObj::showDetail( const table::CellRangeAddress& rCellRange ) +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + { + ScRange aMarkRange; + ScUnoConversion::FillScRange( aMarkRange, rCellRange ); + ScOutlineDocFunc aFunc(*pDocSh); + aFunc.ShowMarkedOutlines( aMarkRange, true ); + } +} + +void SAL_CALL ScTableSheetObj::showLevel( sal_Int16 nLevel, table::TableOrientation nOrientation ) +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + { + bool bColumns = ( nOrientation == table::TableOrientation_COLUMNS ); + SCTAB nTab = GetTab_Impl(); + ScOutlineDocFunc aFunc(*pDocSh); + aFunc.SelectLevel( nTab, bColumns, nLevel, true, true ); + } +} + +// XProtectable + +void SAL_CALL ScTableSheetObj::protect( const OUString& aPassword ) +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + // #i108245# if already protected, don't change anything + if ( pDocSh && !pDocSh->GetDocument().IsTabProtected( GetTab_Impl() ) ) + { + pDocSh->GetDocFunc().Protect( GetTab_Impl(), aPassword ); + } +} + +void SAL_CALL ScTableSheetObj::unprotect( const OUString& aPassword ) +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + { + bool bDone = pDocSh->GetDocFunc().Unprotect( GetTab_Impl(), aPassword, true ); + if (!bDone) + throw lang::IllegalArgumentException(); + } +} + +sal_Bool SAL_CALL ScTableSheetObj::isProtected() +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + return pDocSh->GetDocument().IsTabProtected( GetTab_Impl() ); + + OSL_FAIL("no DocShell"); //! Exception or so? + return false; +} + +// XScenario + +sal_Bool SAL_CALL ScTableSheetObj::getIsScenario() +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + return pDocSh->GetDocument().IsScenario( GetTab_Impl() ); + + return false; +} + +OUString SAL_CALL ScTableSheetObj::getScenarioComment() +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + { + OUString aComment; + Color aColor; + ScScenarioFlags nFlags; + pDocSh->GetDocument().GetScenarioData( GetTab_Impl(), aComment, aColor, nFlags ); + return aComment; + } + return OUString(); +} + +void SAL_CALL ScTableSheetObj::setScenarioComment( const OUString& aScenarioComment ) +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( !pDocSh ) + return; + + ScDocument& rDoc = pDocSh->GetDocument(); + SCTAB nTab = GetTab_Impl(); + + OUString aName; + OUString aComment; + Color aColor; + ScScenarioFlags nFlags; + rDoc.GetName( nTab, aName ); + rDoc.GetScenarioData( nTab, aComment, aColor, nFlags ); + + aComment = aScenarioComment; + + pDocSh->ModifyScenario( nTab, aName, aComment, aColor, nFlags ); +} + +void SAL_CALL ScTableSheetObj::addRanges( const uno::Sequence<table::CellRangeAddress>& rScenRanges ) +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( !pDocSh ) + return; + + ScDocument& rDoc = pDocSh->GetDocument(); + SCTAB nTab = GetTab_Impl(); + + if (!rDoc.IsScenario(nTab)) + return; + + ScMarkData aMarkData(rDoc.GetSheetLimits()); + aMarkData.SelectTable( nTab, true ); + + for (const table::CellRangeAddress& rRange : rScenRanges) + { + OSL_ENSURE( rRange.Sheet == nTab, "addRanges with wrong Tab" ); + ScRange aOneRange( static_cast<SCCOL>(rRange.StartColumn), static_cast<SCROW>(rRange.StartRow), nTab, + static_cast<SCCOL>(rRange.EndColumn), static_cast<SCROW>(rRange.EndRow), nTab ); + + aMarkData.SetMultiMarkArea( aOneRange ); + } + + // Scenario ranges are tagged with attribute + ScPatternAttr aPattern( rDoc.GetPool() ); + aPattern.GetItemSet().Put( ScMergeFlagAttr( ScMF::Scenario ) ); + aPattern.GetItemSet().Put( ScProtectionAttr( true ) ); + pDocSh->GetDocFunc().ApplyAttributes( aMarkData, aPattern, true ); +} + +void SAL_CALL ScTableSheetObj::apply() +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( !pDocSh ) + return; + + ScDocument& rDoc = pDocSh->GetDocument(); + SCTAB nTab = GetTab_Impl(); + OUString aName; + rDoc.GetName( nTab, aName ); // scenario name + + SCTAB nDestTab = nTab; + while ( nDestTab > 0 && rDoc.IsScenario(nDestTab) ) + --nDestTab; + + if ( !rDoc.IsScenario(nDestTab) ) + pDocSh->UseScenario( nDestTab, aName ); + + //! otherwise error or so +} + +// XScenarioEnhanced + +uno::Sequence< table::CellRangeAddress > SAL_CALL ScTableSheetObj::getRanges( ) +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + { + ScDocument& rDoc = pDocSh->GetDocument(); + SCTAB nTab = GetTab_Impl(); + const ScRangeList* pRangeList = rDoc.GetScenarioRanges(nTab); + if (pRangeList) + { + size_t nCount = pRangeList->size(); + uno::Sequence< table::CellRangeAddress > aRetRanges( nCount ); + table::CellRangeAddress* pAry = aRetRanges.getArray(); + for( size_t nIndex = 0; nIndex < nCount; nIndex++ ) + { + const ScRange & rRange = (*pRangeList)[nIndex]; + pAry->StartColumn = rRange.aStart.Col(); + pAry->StartRow = rRange.aStart.Row(); + pAry->EndColumn = rRange.aEnd.Col(); + pAry->EndRow = rRange.aEnd.Row(); + pAry->Sheet = rRange.aStart.Tab(); + ++pAry; + } + return aRetRanges; + } + } + return uno::Sequence< table::CellRangeAddress > (); +} + +// XExternalSheetName + +void ScTableSheetObj::setExternalName( const OUString& aUrl, const OUString& aSheetName ) +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + { + ScDocument& rDoc = pDocSh->GetDocument(); + const SCTAB nTab = GetTab_Impl(); + const OUString aAbsDocName( ScGlobal::GetAbsDocName( aUrl, pDocSh ) ); + const OUString aDocTabName( ScGlobal::GetDocTabName( aAbsDocName, aSheetName ) ); + if ( !rDoc.RenameTab( nTab, aDocTabName, true /*bExternalDocument*/ ) ) + { + throw container::ElementExistException( OUString(), *this ); + } + } +} + +// XEventsSupplier + +uno::Reference<container::XNameReplace> SAL_CALL ScTableSheetObj::getEvents() +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + return new ScSheetEventsObj( pDocSh, GetTab_Impl() ); + + return nullptr; +} + +// XPropertySet extended for Sheet-Properties + +uno::Reference<beans::XPropertySetInfo> SAL_CALL ScTableSheetObj::getPropertySetInfo() +{ + SolarMutexGuard aGuard; + static uno::Reference<beans::XPropertySetInfo> aRef( + new SfxItemPropertySetInfo( pSheetPropSet->getPropertyMap() )); + return aRef; +} + +void ScTableSheetObj::SetOnePropertyValue( const SfxItemPropertyMapEntry* pEntry, const uno::Any& aValue ) +{ + if ( !pEntry ) + return; + + if ( IsScItemWid( pEntry->nWID ) ) + { + // for Item WIDs, call ScCellRangesBase directly + ScCellRangesBase::SetOnePropertyValue(pEntry, aValue); + return; + } + + // own properties + + ScDocShell* pDocSh = GetDocShell(); + if (!pDocSh) + return; //! Exception or so? + ScDocument& rDoc = pDocSh->GetDocument(); + SCTAB nTab = GetTab_Impl(); + ScDocFunc &rFunc = pDocSh->GetDocFunc(); + + if ( pEntry->nWID == SC_WID_UNO_PAGESTL ) + { + OUString aStrVal; + aValue >>= aStrVal; + OUString aNewStr(ScStyleNameConversion::ProgrammaticToDisplayName( + aStrVal, SfxStyleFamily::Page )); + + //! Undo? (also if SID_STYLE_APPLY on View) + + if ( rDoc.GetPageStyle( nTab ) != aNewStr ) + { + rDoc.SetPageStyle( nTab, aNewStr ); + if (!rDoc.IsImportingXML()) + { + ScPrintFunc( pDocSh, pDocSh->GetPrinter(), nTab ).UpdatePages(); + + SfxBindings* pBindings = pDocSh->GetViewBindings(); + if (pBindings) + { + pBindings->Invalidate( SID_STYLE_FAMILY4 ); + pBindings->Invalidate( SID_STATUS_PAGESTYLE ); + pBindings->Invalidate( FID_RESET_PRINTZOOM ); + pBindings->Invalidate( SID_ATTR_PARA_LEFT_TO_RIGHT ); + pBindings->Invalidate( SID_ATTR_PARA_RIGHT_TO_LEFT ); + } + } + pDocSh->SetDocumentModified(); + } + } + else if ( pEntry->nWID == SC_WID_UNO_CELLVIS ) + { + bool bVis = ScUnoHelpFunctions::GetBoolFromAny( aValue ); + rFunc.SetTableVisible( nTab, bVis, true ); + } + else if ( pEntry->nWID == SC_WID_UNO_ISACTIVE ) + { + if (rDoc.IsScenario(nTab)) + rDoc.SetActiveScenario( nTab, ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + } + else if ( pEntry->nWID == SC_WID_UNO_BORDCOL ) + { + if (rDoc.IsScenario(nTab)) + { + Color aColor; + if (aValue >>= aColor) + { + OUString aName; + OUString aComment; + ScScenarioFlags nFlags; + Color aTmp; + rDoc.GetName( nTab, aName ); + rDoc.GetScenarioData( nTab, aComment, aTmp, nFlags ); + + pDocSh->ModifyScenario( nTab, aName, aComment, aColor, nFlags ); + } + } + } + else if ( pEntry->nWID == SC_WID_UNO_PROTECT ) + { + if (rDoc.IsScenario(nTab)) + { + OUString aName; + OUString aComment; + Color aColor; + ScScenarioFlags nFlags; + rDoc.GetName( nTab, aName ); + rDoc.GetScenarioData( nTab, aComment, aColor, nFlags ); + bool bModify(false); + + if (ScUnoHelpFunctions::GetBoolFromAny( aValue )) + { + if (!(nFlags & ScScenarioFlags::Protected)) + { + nFlags |= ScScenarioFlags::Protected; + bModify = true; + } + } + else + { + if (nFlags & ScScenarioFlags::Protected) + { + nFlags &= ~ScScenarioFlags::Protected; + bModify = true; + } + } + + if (bModify) + pDocSh->ModifyScenario( nTab, aName, aComment, aColor, nFlags ); + } + } + else if ( pEntry->nWID == SC_WID_UNO_SHOWBORD ) + { + if (rDoc.IsScenario(nTab)) + { + OUString aName; + OUString aComment; + Color aColor; + ScScenarioFlags nFlags; + rDoc.GetName( nTab, aName ); + rDoc.GetScenarioData( nTab, aComment, aColor, nFlags ); + bool bModify(false); + + if (ScUnoHelpFunctions::GetBoolFromAny( aValue )) + { + if (!(nFlags & ScScenarioFlags::ShowFrame)) + { + nFlags |= ScScenarioFlags::ShowFrame; + bModify = true; + } + } + else + { + if (nFlags & ScScenarioFlags::ShowFrame) + { + nFlags &= ~ScScenarioFlags::ShowFrame; + bModify = true; + } + } + + if (bModify) + pDocSh->ModifyScenario( nTab, aName, aComment, aColor, nFlags ); + } + } + else if ( pEntry->nWID == SC_WID_UNO_PRINTBORD ) + { + if (rDoc.IsScenario(nTab)) + { + OUString aName; + OUString aComment; + Color aColor; + ScScenarioFlags nFlags; + rDoc.GetName( nTab, aName ); + rDoc.GetScenarioData( nTab, aComment, aColor, nFlags ); + bool bModify(false); + + if (ScUnoHelpFunctions::GetBoolFromAny( aValue )) + { + if (!(nFlags & ScScenarioFlags::PrintFrame)) + { + nFlags |= ScScenarioFlags::PrintFrame; + bModify = true; + } + } + else + { + if (nFlags & ScScenarioFlags::PrintFrame) + { + nFlags &= ~ScScenarioFlags::PrintFrame; + bModify = true; + } + } + + if (bModify) + pDocSh->ModifyScenario( nTab, aName, aComment, aColor, nFlags ); + } + } + else if ( pEntry->nWID == SC_WID_UNO_COPYBACK ) + { + if (rDoc.IsScenario(nTab)) + { + OUString aName; + OUString aComment; + Color aColor; + ScScenarioFlags nFlags; + rDoc.GetName( nTab, aName ); + rDoc.GetScenarioData( nTab, aComment, aColor, nFlags ); + bool bModify(false); + + if (ScUnoHelpFunctions::GetBoolFromAny( aValue )) + { + if (!(nFlags & ScScenarioFlags::TwoWay)) + { + nFlags |= ScScenarioFlags::TwoWay; + bModify = true; + } + } + else + { + if (nFlags & ScScenarioFlags::TwoWay) + { + nFlags &= ~ScScenarioFlags::TwoWay; + bModify = true; + } + } + + if (bModify) + pDocSh->ModifyScenario( nTab, aName, aComment, aColor, nFlags ); + } + } + else if ( pEntry->nWID == SC_WID_UNO_COPYSTYL ) + { + if (rDoc.IsScenario(nTab)) + { + OUString aName; + OUString aComment; + Color aColor; + ScScenarioFlags nFlags; + rDoc.GetName( nTab, aName ); + rDoc.GetScenarioData( nTab, aComment, aColor, nFlags ); + bool bModify(false); + + if (ScUnoHelpFunctions::GetBoolFromAny( aValue )) + { + if (!(nFlags & ScScenarioFlags::Attrib)) + { + nFlags |= ScScenarioFlags::Attrib; + bModify = true; + } + } + else + { + if (nFlags & ScScenarioFlags::Attrib) + { + nFlags &= ~ScScenarioFlags::Attrib; + bModify = true; + } + } + + if (bModify) + pDocSh->ModifyScenario( nTab, aName, aComment, aColor, nFlags ); + } + } + else if ( pEntry->nWID == SC_WID_UNO_COPYFORM ) + { + if (rDoc.IsScenario(nTab)) + { + OUString aName; + OUString aComment; + Color aColor; + ScScenarioFlags nFlags; + rDoc.GetName( nTab, aName ); + rDoc.GetScenarioData( nTab, aComment, aColor, nFlags ); + bool bModify(false); + + if (ScUnoHelpFunctions::GetBoolFromAny( aValue )) + { + if (nFlags & ScScenarioFlags::Value) + { + nFlags &= ~ScScenarioFlags::Value; + bModify = true; + } + } + else + { + if (!(nFlags & ScScenarioFlags::Value)) + { + nFlags |= ScScenarioFlags::Value; + bModify = true; + } + } + + if (bModify) + pDocSh->ModifyScenario( nTab, aName, aComment, aColor, nFlags ); + } + } + else if ( pEntry->nWID == SC_WID_UNO_TABLAYOUT ) + { + sal_Int16 nValue = 0; + if (aValue >>= nValue) + { + if (nValue == css::text::WritingMode2::RL_TB) + rFunc.SetLayoutRTL(nTab, true); + else + rFunc.SetLayoutRTL(nTab, false); + } + } + else if ( pEntry->nWID == SC_WID_UNO_AUTOPRINT ) + { + bool bAutoPrint = ScUnoHelpFunctions::GetBoolFromAny( aValue ); + if (bAutoPrint) + rDoc.SetPrintEntireSheet( nTab ); // clears all print ranges + else + { + if (rDoc.IsPrintEntireSheet( nTab )) + rDoc.ClearPrintRanges( nTab ); // if this flag is true, there are no PrintRanges, so Clear clears only the flag. + } + } + else if ( pEntry->nWID == SC_WID_UNO_TABCOLOR ) + { + Color aColor = COL_AUTO; + if ( aValue >>= aColor ) + { + if ( rDoc.GetTabBgColor( nTab ) != aColor ) + rFunc.SetTabBgColor( nTab, aColor, true, true ); + } + } + else if ( pEntry->nWID == SC_WID_UNO_CODENAME ) + { + OUString aCodeName; + if (aValue >>= aCodeName) + { + pDocSh->GetDocument().SetCodeName( GetTab_Impl(), aCodeName ); + } + } + else if (pEntry->nWID == SC_WID_UNO_CONDFORMAT) + { + uno::Reference<sheet::XConditionalFormats> xCondFormat; + if (aValue >>= xCondFormat) + { + // how to set the format correctly + } + } + else + ScCellRangeObj::SetOnePropertyValue(pEntry, aValue); // base class, no Item WID +} + +void ScTableSheetObj::GetOnePropertyValue( const SfxItemPropertyMapEntry* pEntry, + uno::Any& rAny ) +{ + if ( !pEntry ) + return; + + ScDocShell* pDocSh = GetDocShell(); + if (!pDocSh) + throw uno::RuntimeException(); + ScDocument& rDoc = pDocSh->GetDocument(); + SCTAB nTab = GetTab_Impl(); + + if ( pEntry->nWID == SC_WID_UNO_NAMES ) + { + rAny <<= uno::Reference<sheet::XNamedRanges>(new ScLocalNamedRangesObj(pDocSh, this)); + } + else if ( pEntry->nWID == SC_WID_UNO_PAGESTL ) + { + rAny <<= ScStyleNameConversion::DisplayToProgrammaticName( + rDoc.GetPageStyle( nTab ), SfxStyleFamily::Page ); + } + else if ( pEntry->nWID == SC_WID_UNO_CELLVIS ) + { + bool bVis = rDoc.IsVisible( nTab ); + rAny <<= bVis; + } + else if ( pEntry->nWID == SC_WID_UNO_LINKDISPBIT ) + { + // no target bitmaps for individual entries (would be all equal) + // ScLinkTargetTypeObj::SetLinkTargetBitmap( aAny, SC_LINKTARGETTYPE_SHEET ); + } + else if ( pEntry->nWID == SC_WID_UNO_LINKDISPNAME ) + { + // LinkDisplayName for hyperlink dialog + rAny <<= getName(); // sheet name + } + else if ( pEntry->nWID == SC_WID_UNO_ISACTIVE ) + { + if (rDoc.IsScenario(nTab)) + rAny <<= rDoc.IsActiveScenario( nTab ); + } + else if ( pEntry->nWID == SC_WID_UNO_BORDCOL ) + { + if (rDoc.IsScenario(nTab)) + { + OUString aComment; + Color aColor; + ScScenarioFlags nFlags; + rDoc.GetScenarioData( nTab, aComment, aColor, nFlags ); + + rAny <<= aColor; + } + } + else if ( pEntry->nWID == SC_WID_UNO_PROTECT ) + { + if (rDoc.IsScenario(nTab)) + { + ScScenarioFlags nFlags; + rDoc.GetScenarioFlags(nTab, nFlags); + + rAny <<= ((nFlags & ScScenarioFlags::Protected) != ScScenarioFlags::NONE); + } + } + else if ( pEntry->nWID == SC_WID_UNO_SHOWBORD ) + { + if (rDoc.IsScenario(nTab)) + { + ScScenarioFlags nFlags; + rDoc.GetScenarioFlags(nTab, nFlags); + + rAny <<= ((nFlags & ScScenarioFlags::ShowFrame) != ScScenarioFlags::NONE); + } + } + else if ( pEntry->nWID == SC_WID_UNO_PRINTBORD ) + { + if (rDoc.IsScenario(nTab)) + { + ScScenarioFlags nFlags; + rDoc.GetScenarioFlags(nTab, nFlags); + + rAny <<= ((nFlags & ScScenarioFlags::PrintFrame) != ScScenarioFlags::NONE); + } + } + else if ( pEntry->nWID == SC_WID_UNO_COPYBACK ) + { + if (rDoc.IsScenario(nTab)) + { + ScScenarioFlags nFlags; + rDoc.GetScenarioFlags(nTab, nFlags); + + rAny <<= ((nFlags & ScScenarioFlags::TwoWay) != ScScenarioFlags::NONE); + } + } + else if ( pEntry->nWID == SC_WID_UNO_COPYSTYL ) + { + if (rDoc.IsScenario(nTab)) + { + ScScenarioFlags nFlags; + rDoc.GetScenarioFlags(nTab, nFlags); + + rAny <<= ((nFlags & ScScenarioFlags::Attrib) != ScScenarioFlags::NONE); + } + } + else if ( pEntry->nWID == SC_WID_UNO_COPYFORM ) + { + if (rDoc.IsScenario(nTab)) + { + ScScenarioFlags nFlags; + rDoc.GetScenarioFlags(nTab, nFlags); + + rAny <<= !(nFlags & ScScenarioFlags::Value); + } + } + else if ( pEntry->nWID == SC_WID_UNO_TABLAYOUT ) + { + if (rDoc.IsLayoutRTL(nTab)) + rAny <<= sal_Int16(css::text::WritingMode2::RL_TB); + else + rAny <<= sal_Int16(css::text::WritingMode2::LR_TB); + } + else if ( pEntry->nWID == SC_WID_UNO_AUTOPRINT ) + { + bool bAutoPrint = rDoc.IsPrintEntireSheet( nTab ); + rAny <<= bAutoPrint; + } + else if ( pEntry->nWID == SC_WID_UNO_TABCOLOR ) + { + rAny <<= rDoc.GetTabBgColor(nTab); + } + else if ( pEntry->nWID == SC_WID_UNO_CODENAME ) + { + OUString aCodeName; + pDocSh->GetDocument().GetCodeName(GetTab_Impl(), aCodeName); + rAny <<= aCodeName; + } + else if (pEntry->nWID == SC_WID_UNO_CONDFORMAT) + { + rAny <<= uno::Reference<sheet::XConditionalFormats>(new ScCondFormatsObj(pDocSh, nTab)); + } + else + ScCellRangeObj::GetOnePropertyValue(pEntry, rAny); +} + +const SfxItemPropertyMap& ScTableSheetObj::GetItemPropertyMap() +{ + return pSheetPropSet->getPropertyMap(); +} + +// XServiceInfo + +OUString SAL_CALL ScTableSheetObj::getImplementationName() +{ + return "ScTableSheetObj"; +} + +sal_Bool SAL_CALL ScTableSheetObj::supportsService( const OUString& rServiceName ) +{ + return cppu::supportsService(this, rServiceName); +} + +uno::Sequence<OUString> SAL_CALL ScTableSheetObj::getSupportedServiceNames() +{ + return {SCSPREADSHEET_SERVICE, + SCSHEETCELLRANGE_SERVICE, + SCCELLRANGE_SERVICE, + SCCELLPROPERTIES_SERVICE, + SCCHARPROPERTIES_SERVICE, + SCPARAPROPERTIES_SERVICE, + SCLINKTARGET_SERVICE}; +} + +ScTableColumnObj::ScTableColumnObj( ScDocShell* pDocSh, SCCOL nCol, SCTAB nTab ) : + ScCellRangeObj( pDocSh, ScRange(nCol,0,nTab, nCol, pDocSh->GetDocument().MaxRow(),nTab) ), + pColPropSet(lcl_GetColumnPropertySet()) +{ +} + +ScTableColumnObj::~ScTableColumnObj() +{ +} + +uno::Any SAL_CALL ScTableColumnObj::queryInterface( const uno::Type& rType ) +{ + uno::Any aReturn = ::cppu::queryInterface(rType, + static_cast<container::XNamed*>(this)); + if ( aReturn.hasValue() ) + return aReturn; + + return ScCellRangeObj::queryInterface( rType ); +} + +void SAL_CALL ScTableColumnObj::acquire() noexcept +{ + ScCellRangeObj::acquire(); +} + +void SAL_CALL ScTableColumnObj::release() noexcept +{ + ScCellRangeObj::release(); +} + +uno::Sequence<uno::Type> SAL_CALL ScTableColumnObj::getTypes() +{ + return comphelper::concatSequences( + ScCellRangeObj::getTypes(), + uno::Sequence<uno::Type> { cppu::UnoType<container::XNamed>::get() } ); +} + +uno::Sequence<sal_Int8> SAL_CALL ScTableColumnObj::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +// XNamed + +OUString SAL_CALL ScTableColumnObj::getName() +{ + SolarMutexGuard aGuard; + + const ScRange& rRange = GetRange(); + OSL_ENSURE(rRange.aStart.Col() == rRange.aEnd.Col(), "too many columns"); + SCCOL nCol = rRange.aStart.Col(); + + return ScColToAlpha( nCol ); // from global.hxx +} + +void SAL_CALL ScTableColumnObj::setName( const OUString& /* aNewName */ ) +{ + throw uno::RuntimeException(); // read-only +} + +// XPropertySet extended for Column-Properties + +uno::Reference<beans::XPropertySetInfo> SAL_CALL ScTableColumnObj::getPropertySetInfo() +{ + SolarMutexGuard aGuard; + static uno::Reference<beans::XPropertySetInfo> aRef( + new SfxItemPropertySetInfo( pColPropSet->getPropertyMap() )); + return aRef; +} + +void ScTableColumnObj::SetOnePropertyValue(const SfxItemPropertyMapEntry* pEntry, const uno::Any& aValue) +{ + if ( !pEntry ) + return; + + if ( IsScItemWid( pEntry->nWID ) ) + { + // for Item WIDs, call ScCellRangesBase directly + ScCellRangesBase::SetOnePropertyValue(pEntry, aValue); + return; + } + + // own properties + + ScDocShell* pDocSh = GetDocShell(); + if (!pDocSh) + return; //! Exception or so? + const ScRange& rRange = GetRange(); + OSL_ENSURE(rRange.aStart.Col() == rRange.aEnd.Col(), "Too many columns"); + SCCOL nCol = rRange.aStart.Col(); + SCTAB nTab = rRange.aStart.Tab(); + ScDocFunc &rFunc = pDocSh->GetDocFunc(); + + std::vector<sc::ColRowSpan> aColArr(1, sc::ColRowSpan(nCol,nCol)); + + if ( pEntry->nWID == SC_WID_UNO_CELLWID ) + { + sal_Int32 nNewWidth = 0; + if ( aValue >>= nNewWidth ) + { + // property is 1/100mm, column width is twips + nNewWidth = o3tl::toTwips(nNewWidth, o3tl::Length::mm100); + rFunc.SetWidthOrHeight( + true, aColArr, nTab, SC_SIZE_ORIGINAL, nNewWidth, true, true); + } + } + else if ( pEntry->nWID == SC_WID_UNO_CELLVIS ) + { + bool bVis = ScUnoHelpFunctions::GetBoolFromAny( aValue ); + ScSizeMode eMode = bVis ? SC_SIZE_SHOW : SC_SIZE_DIRECT; + rFunc.SetWidthOrHeight(true, aColArr, nTab, eMode, 0, true, true); + // SC_SIZE_DIRECT with size 0 will hide + } + else if ( pEntry->nWID == SC_WID_UNO_OWIDTH ) + { + bool bOpt = ScUnoHelpFunctions::GetBoolFromAny( aValue ); + if (bOpt) + rFunc.SetWidthOrHeight( + true, aColArr, nTab, SC_SIZE_OPTIMAL, STD_EXTRA_WIDTH, true, true); + // sal_False on columns currently without effect + } + else if ( pEntry->nWID == SC_WID_UNO_NEWPAGE || pEntry->nWID == SC_WID_UNO_MANPAGE ) + { + bool bSet = ScUnoHelpFunctions::GetBoolFromAny( aValue ); + if (bSet) + rFunc.InsertPageBreak( true, rRange.aStart, true, true ); + else + rFunc.RemovePageBreak( true, rRange.aStart, true, true ); + } + else + ScCellRangeObj::SetOnePropertyValue(pEntry, aValue); // base class, no Item WID +} + +void ScTableColumnObj::GetOnePropertyValue( const SfxItemPropertyMapEntry* pEntry, uno::Any& rAny ) +{ + if ( !pEntry ) + return; + + ScDocShell* pDocSh = GetDocShell(); + if (!pDocSh) + throw uno::RuntimeException(); + + ScDocument& rDoc = pDocSh->GetDocument(); + const ScRange& rRange = GetRange(); + OSL_ENSURE(rRange.aStart.Col() == rRange.aEnd.Col(), "too many columns"); + SCCOL nCol = rRange.aStart.Col(); + SCTAB nTab = rRange.aStart.Tab(); + + if ( pEntry->nWID == SC_WID_UNO_CELLWID ) + { + // for hidden column, return original height + sal_uInt16 nWidth = rDoc.GetOriginalWidth( nCol, nTab ); + // property is 1/100mm, column width is twips + nWidth = static_cast<sal_uInt16>(convertTwipToMm100(nWidth)); + rAny <<= static_cast<sal_Int32>(nWidth); + } + else if ( pEntry->nWID == SC_WID_UNO_CELLVIS ) + { + bool bHidden = rDoc.ColHidden(nCol, nTab); + rAny <<= !bHidden; + } + else if ( pEntry->nWID == SC_WID_UNO_OWIDTH ) + { + //! at the moment always set ??!?! + bool bOpt = !(rDoc.GetColFlags( nCol, nTab ) & CRFlags::ManualSize); + rAny <<= bOpt; + } + else if ( pEntry->nWID == SC_WID_UNO_NEWPAGE ) + { + ScBreakType nBreak = rDoc.HasColBreak(nCol, nTab); + rAny <<= nBreak != ScBreakType::NONE; + } + else if ( pEntry->nWID == SC_WID_UNO_MANPAGE ) + { + ScBreakType nBreak = rDoc.HasColBreak(nCol, nTab); + rAny <<= bool(nBreak & ScBreakType::Manual); + } + else + ScCellRangeObj::GetOnePropertyValue(pEntry, rAny); +} + +const SfxItemPropertyMap& ScTableColumnObj::GetItemPropertyMap() +{ + return pColPropSet->getPropertyMap(); +} + +ScTableRowObj::ScTableRowObj(ScDocShell* pDocSh, SCROW nRow, SCTAB nTab) : + ScCellRangeObj( pDocSh, ScRange(0,nRow,nTab, pDocSh->GetDocument().MaxCol(),nRow,nTab) ), + pRowPropSet(lcl_GetRowPropertySet()) +{ +} + +ScTableRowObj::~ScTableRowObj() +{ +} + +// XPropertySet extended for Row-Properties + +uno::Reference<beans::XPropertySetInfo> SAL_CALL ScTableRowObj::getPropertySetInfo() +{ + SolarMutexGuard aGuard; + static uno::Reference<beans::XPropertySetInfo> aRef( + new SfxItemPropertySetInfo( pRowPropSet->getPropertyMap() )); + return aRef; +} + +void ScTableRowObj::SetOnePropertyValue( const SfxItemPropertyMapEntry* pEntry, const uno::Any& aValue ) +{ + if ( !pEntry ) + return; + + if ( IsScItemWid( pEntry->nWID ) ) + { + // for Item WIDs, call ScCellRangesBase directly + ScCellRangesBase::SetOnePropertyValue(pEntry, aValue); + return; + } + + // own properties + + ScDocShell* pDocSh = GetDocShell(); + if (!pDocSh) + return; //! Exception or so? + ScDocument& rDoc = pDocSh->GetDocument(); + const ScRange& rRange = GetRange(); + OSL_ENSURE(rRange.aStart.Row() == rRange.aEnd.Row(), "too many rows"); + SCROW nRow = rRange.aStart.Row(); + SCTAB nTab = rRange.aStart.Tab(); + ScDocFunc &rFunc = pDocSh->GetDocFunc(); + + std::vector<sc::ColRowSpan> aRowArr(1, sc::ColRowSpan(nRow,nRow)); + + if ( pEntry->nWID == SC_WID_UNO_CELLHGT ) + { + sal_Int32 nNewHeight = 0; + if ( aValue >>= nNewHeight ) + { + // property is 1/100mm, row height is twips + nNewHeight = o3tl::toTwips(nNewHeight, o3tl::Length::mm100); + rFunc.SetWidthOrHeight( + false, aRowArr, nTab, SC_SIZE_ORIGINAL, nNewHeight, true, true); + } + } + else if ( pEntry->nWID == SC_WID_UNO_CELLVIS ) + { + bool bVis = ScUnoHelpFunctions::GetBoolFromAny( aValue ); + ScSizeMode eMode = bVis ? SC_SIZE_SHOW : SC_SIZE_DIRECT; + rFunc.SetWidthOrHeight(false, aRowArr, nTab, eMode, 0, true, true); + // SC_SIZE_DIRECT with size zero will hide + } + else if ( pEntry->nWID == SC_WID_UNO_CELLFILT ) + { + bool bFil = ScUnoHelpFunctions::GetBoolFromAny( aValue ); + // SC_SIZE_DIRECT with size zero will hide + rDoc.SetRowFiltered(nRow, nRow, nTab, bFil); + } + else if ( pEntry->nWID == SC_WID_UNO_OHEIGHT ) + { + bool bOpt = ScUnoHelpFunctions::GetBoolFromAny( aValue ); + if (bOpt) + rFunc.SetWidthOrHeight(false, aRowArr, nTab, SC_SIZE_OPTIMAL, 0, true, true); + else + { + // set current height again manually + sal_uInt16 nHeight = rDoc.GetOriginalHeight( nRow, nTab ); + rFunc.SetWidthOrHeight(false, aRowArr, nTab, SC_SIZE_ORIGINAL, nHeight, true, true); + } + } + else if ( pEntry->nWID == SC_WID_UNO_NEWPAGE || pEntry->nWID == SC_WID_UNO_MANPAGE ) + { + bool bSet = ScUnoHelpFunctions::GetBoolFromAny( aValue ); + if (bSet) + rFunc.InsertPageBreak( false, rRange.aStart, true, true ); + else + rFunc.RemovePageBreak( false, rRange.aStart, true, true ); + } + else + ScCellRangeObj::SetOnePropertyValue(pEntry, aValue); // base class, no Item WID +} + +void ScTableRowObj::GetOnePropertyValue( const SfxItemPropertyMapEntry* pEntry, uno::Any& rAny ) +{ + if ( !pEntry ) + return; + + ScDocShell* pDocSh = GetDocShell(); + if (!pDocSh) + throw uno::RuntimeException(); + ScDocument& rDoc = pDocSh->GetDocument(); + const ScRange& rRange = GetRange(); + OSL_ENSURE(rRange.aStart.Row() == rRange.aEnd.Row(), "too many rows"); + SCROW nRow = rRange.aStart.Row(); + SCTAB nTab = rRange.aStart.Tab(); + + if ( pEntry->nWID == SC_WID_UNO_CELLHGT ) + { + // for hidden row, return original height + sal_uInt16 nHeight = rDoc.GetOriginalHeight( nRow, nTab ); + // property is 1/100mm, row height is twips + nHeight = static_cast<sal_uInt16>(convertTwipToMm100(nHeight)); + rAny <<= static_cast<sal_Int32>(nHeight); + } + else if ( pEntry->nWID == SC_WID_UNO_CELLVIS ) + { + bool bHidden = rDoc.RowHidden(nRow, nTab); + rAny <<= !bHidden; + } + else if ( pEntry->nWID == SC_WID_UNO_CELLFILT ) + { + bool bVis = rDoc.RowFiltered(nRow, nTab); + rAny <<= bVis; + } + else if ( pEntry->nWID == SC_WID_UNO_OHEIGHT ) + { + bool bOpt = !(rDoc.GetRowFlags( nRow, nTab ) & CRFlags::ManualSize); + rAny <<= bOpt; + } + else if ( pEntry->nWID == SC_WID_UNO_NEWPAGE ) + { + ScBreakType nBreak = rDoc.HasRowBreak(nRow, nTab); + rAny <<= (nBreak != ScBreakType::NONE); + } + else if ( pEntry->nWID == SC_WID_UNO_MANPAGE ) + { + bool bBreak(rDoc.HasRowBreak(nRow, nTab) & ScBreakType::Manual); + rAny <<= bBreak; + } + else + ScCellRangeObj::GetOnePropertyValue(pEntry, rAny); +} + +const SfxItemPropertyMap& ScTableRowObj::GetItemPropertyMap() +{ + return pRowPropSet->getPropertyMap(); +} + +ScCellsObj::ScCellsObj(ScDocShell* pDocSh, ScRangeList aR) : + pDocShell( pDocSh ), + aRanges(std::move( aR )) +{ + pDocShell->GetDocument().AddUnoObject(*this); +} + +ScCellsObj::~ScCellsObj() +{ + SolarMutexGuard g; + + if (pDocShell) + pDocShell->GetDocument().RemoveUnoObject(*this); +} + +void ScCellsObj::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + if ( auto pRefHint = dynamic_cast<const ScUpdateRefHint*>(&rHint) ) + { + aRanges.UpdateReference( pRefHint->GetMode(), &pDocShell->GetDocument(), pRefHint->GetRange(), + pRefHint->GetDx(), pRefHint->GetDy(), pRefHint->GetDz() ); + } + else if ( rHint.GetId() == SfxHintId::Dying ) + { + pDocShell = nullptr; + } +} + +// XEnumerationAccess + +uno::Reference<container::XEnumeration> SAL_CALL ScCellsObj::createEnumeration() +{ + SolarMutexGuard aGuard; + if (pDocShell) + return new ScCellsEnumeration( pDocShell, aRanges ); + return nullptr; +} + +uno::Type SAL_CALL ScCellsObj::getElementType() +{ + return cppu::UnoType<table::XCell>::get(); +} + +sal_Bool SAL_CALL ScCellsObj::hasElements() +{ + SolarMutexGuard aGuard; + bool bHas = false; + if ( pDocShell ) + { + //! faster if test ourself? + + uno::Reference<container::XEnumeration> xEnum(new ScCellsEnumeration( pDocShell, aRanges )); + bHas = xEnum->hasMoreElements(); + } + return bHas; +} + +ScCellsEnumeration::ScCellsEnumeration(ScDocShell* pDocSh, ScRangeList aR) : + pDocShell( pDocSh ), + aRanges(std::move( aR )), + bAtEnd( false ) +{ + ScDocument& rDoc = pDocShell->GetDocument(); + rDoc.AddUnoObject(*this); + + if ( aRanges.empty() ) + bAtEnd = true; + else + { + SCTAB nTab = aRanges[ 0 ].aStart.Tab(); + aPos = ScAddress(0,0,nTab); + CheckPos_Impl(); // set aPos on first matching cell + } +} + +void ScCellsEnumeration::CheckPos_Impl() +{ + if (!pDocShell) + return; + + bool bFound = false; + ScDocument& rDoc = pDocShell->GetDocument(); + ScRefCellValue aCell(rDoc, aPos); + if (!aCell.isEmpty()) + { + if (!pMark) + { + pMark.reset( new ScMarkData(rDoc.GetSheetLimits()) ); + pMark->MarkFromRangeList(aRanges, false); + pMark->MarkToMulti(); // needed for GetNextMarkedCell + } + bFound = pMark->IsCellMarked(aPos.Col(), aPos.Row()); + } + if (!bFound) + Advance_Impl(); +} + +ScCellsEnumeration::~ScCellsEnumeration() +{ + SolarMutexGuard g; + + if (pDocShell) + pDocShell->GetDocument().RemoveUnoObject(*this); + pMark.reset(); +} + +void ScCellsEnumeration::Advance_Impl() +{ + OSL_ENSURE(!bAtEnd,"too much Advance_Impl"); + if (!pMark) + { + pMark.reset( new ScMarkData(pDocShell->GetDocument().GetSheetLimits()) ); + pMark->MarkFromRangeList( aRanges, false ); + pMark->MarkToMulti(); // needed for GetNextMarkedCell + } + + SCCOL nCol = aPos.Col(); + SCROW nRow = aPos.Row(); + SCTAB nTab = aPos.Tab(); + bool bFound = pDocShell->GetDocument().GetNextMarkedCell( nCol, nRow, nTab, *pMark ); + if (bFound) + aPos.Set( nCol, nRow, nTab ); + else + bAtEnd = true; // nothing will follow +} + +void ScCellsEnumeration::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + const ScUpdateRefHint* pRefHint = dynamic_cast<const ScUpdateRefHint*>(&rHint); + if ( pRefHint ) + { + if (pDocShell) + { + aRanges.UpdateReference( pRefHint->GetMode(), &pDocShell->GetDocument(), pRefHint->GetRange(), + pRefHint->GetDx(), pRefHint->GetDy(), pRefHint->GetDz() ); + + pMark.reset(); // recreate from moved area + + if (!bAtEnd) // adjust aPos + { + ScRangeList aNew { ScRange(aPos) }; + aNew.UpdateReference( pRefHint->GetMode(), &pDocShell->GetDocument(), pRefHint->GetRange(), + pRefHint->GetDx(), pRefHint->GetDy(), pRefHint->GetDz() ); + if (aNew.size()==1) + { + aPos = aNew[ 0 ].aStart; + CheckPos_Impl(); + } + } + } + } + else if ( rHint.GetId() == SfxHintId::Dying ) + { + pDocShell = nullptr; + } +} + +// XEnumeration + +sal_Bool SAL_CALL ScCellsEnumeration::hasMoreElements() +{ + SolarMutexGuard aGuard; + return !bAtEnd; +} + +uno::Any SAL_CALL ScCellsEnumeration::nextElement() +{ + SolarMutexGuard aGuard; + if (pDocShell && !bAtEnd) + { + // interface must match ScCellsObj::getElementType + + ScAddress aTempPos(aPos); + Advance_Impl(); + return uno::Any(uno::Reference<table::XCell>(new ScCellObj( pDocShell, aTempPos ))); + } + + throw container::NoSuchElementException(); // no more elements +} + +ScCellFormatsObj::ScCellFormatsObj(ScDocShell* pDocSh, const ScRange& rRange) : + pDocShell( pDocSh ), + aTotalRange( rRange ) +{ + ScDocument& rDoc = pDocShell->GetDocument(); + rDoc.AddUnoObject(*this); + + OSL_ENSURE( aTotalRange.aStart.Tab() == aTotalRange.aEnd.Tab(), "different tables" ); +} + +ScCellFormatsObj::~ScCellFormatsObj() +{ + SolarMutexGuard g; + + if (pDocShell) + pDocShell->GetDocument().RemoveUnoObject(*this); +} + +void ScCellFormatsObj::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + if ( dynamic_cast<const ScUpdateRefHint*>(&rHint) ) + { + //! aTotalRange... + } + else if ( rHint.GetId() == SfxHintId::Dying ) + { + pDocShell = nullptr; + } +} + +rtl::Reference<ScCellRangeObj> ScCellFormatsObj::GetObjectByIndex_Impl(tools::Long nIndex) const +{ + //! access the AttrArrays directly !!!! + + if (pDocShell) + { + ScDocument& rDoc = pDocShell->GetDocument(); + tools::Long nPos = 0; + ScAttrRectIterator aIter( rDoc, aTotalRange.aStart.Tab(), + aTotalRange.aStart.Col(), aTotalRange.aStart.Row(), + aTotalRange.aEnd.Col(), aTotalRange.aEnd.Row() ); + SCCOL nCol1, nCol2; + SCROW nRow1, nRow2; + while ( aIter.GetNext( nCol1, nCol2, nRow1, nRow2 ) ) + { + if ( nPos == nIndex ) + { + SCTAB nTab = aTotalRange.aStart.Tab(); + ScRange aNext( nCol1, nRow1, nTab, nCol2, nRow2, nTab ); + + if ( aNext.aStart == aNext.aEnd ) + return new ScCellObj( pDocShell, aNext.aStart ); + else + return new ScCellRangeObj( pDocShell, aNext ); + } + ++nPos; + } + } + return {}; +} + +// XIndexAccess + +sal_Int32 SAL_CALL ScCellFormatsObj::getCount() +{ + SolarMutexGuard aGuard; + + //! access the AttrArrays directly !!!! + + tools::Long nCount = 0; + if (pDocShell) + { + ScDocument& rDoc = pDocShell->GetDocument(); + ScAttrRectIterator aIter( rDoc, aTotalRange.aStart.Tab(), + aTotalRange.aStart.Col(), aTotalRange.aStart.Row(), + aTotalRange.aEnd.Col(), aTotalRange.aEnd.Row() ); + SCCOL nCol1, nCol2; + SCROW nRow1, nRow2; + while ( aIter.GetNext( nCol1, nCol2, nRow1, nRow2 ) ) + ++nCount; + } + return nCount; +} + +uno::Any SAL_CALL ScCellFormatsObj::getByIndex( sal_Int32 nIndex ) +{ + SolarMutexGuard aGuard; + + uno::Reference<table::XCellRange> xRange(GetObjectByIndex_Impl(nIndex)); + if (!xRange.is()) + throw lang::IndexOutOfBoundsException(); + + return uno::Any(xRange); + +} + +uno::Type SAL_CALL ScCellFormatsObj::getElementType() +{ + return cppu::UnoType<table::XCellRange>::get(); +} + +sal_Bool SAL_CALL ScCellFormatsObj::hasElements() +{ + SolarMutexGuard aGuard; + return ( getCount() != 0 ); //! always greater then zero ?? +} + +// XEnumerationAccess + +uno::Reference<container::XEnumeration> SAL_CALL ScCellFormatsObj::createEnumeration() +{ + SolarMutexGuard aGuard; + if (pDocShell) + return new ScCellFormatsEnumeration( pDocShell, aTotalRange ); + return nullptr; +} + +ScCellFormatsEnumeration::ScCellFormatsEnumeration(ScDocShell* pDocSh, const ScRange& rRange) : + pDocShell( pDocSh ), + nTab( rRange.aStart.Tab() ), + bAtEnd( false ), + bDirty( false ) +{ + ScDocument& rDoc = pDocShell->GetDocument(); + rDoc.AddUnoObject(*this); + + OSL_ENSURE( rRange.aStart.Tab() == rRange.aEnd.Tab(), + "CellFormatsEnumeration: different tables" ); + + pIter.reset( new ScAttrRectIterator( rDoc, nTab, + rRange.aStart.Col(), rRange.aStart.Row(), + rRange.aEnd.Col(), rRange.aEnd.Row() ) ); + Advance_Impl(); +} + +ScCellFormatsEnumeration::~ScCellFormatsEnumeration() +{ + SolarMutexGuard g; + + if (pDocShell) + pDocShell->GetDocument().RemoveUnoObject(*this); +} + +void ScCellFormatsEnumeration::Advance_Impl() +{ + OSL_ENSURE(!bAtEnd,"too many Advance_Impl"); + + if ( pIter ) + { + if ( bDirty ) + { + pIter->DataChanged(); // new search for AttrArray-Index + bDirty = false; + } + + SCCOL nCol1, nCol2; + SCROW nRow1, nRow2; + if ( pIter->GetNext( nCol1, nCol2, nRow1, nRow2 ) ) + aNext = ScRange( nCol1, nRow1, nTab, nCol2, nRow2, nTab ); + else + bAtEnd = true; + } + else + bAtEnd = true; // document vanished or so +} + +rtl::Reference<ScCellRangeObj> ScCellFormatsEnumeration::NextObject_Impl() +{ + rtl::Reference<ScCellRangeObj> pRet; + if (pDocShell && !bAtEnd) + { + if ( aNext.aStart == aNext.aEnd ) + pRet = new ScCellObj( pDocShell, aNext.aStart ); + else + pRet = new ScCellRangeObj( pDocShell, aNext ); + Advance_Impl(); + } + return pRet; +} + +void ScCellFormatsEnumeration::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + if ( dynamic_cast<const ScUpdateRefHint*>(&rHint) ) + { + //! and now??? + } + else + { + const SfxHintId nId = rHint.GetId(); + if ( nId == SfxHintId::Dying ) + { + pDocShell = nullptr; + pIter.reset(); + } + else if ( nId == SfxHintId::DataChanged ) + { + bDirty = true; // AttrArray-Index possibly invalid + } + } +} + +// XEnumeration + +sal_Bool SAL_CALL ScCellFormatsEnumeration::hasMoreElements() +{ + SolarMutexGuard aGuard; + return !bAtEnd; +} + +uno::Any SAL_CALL ScCellFormatsEnumeration::nextElement() +{ + SolarMutexGuard aGuard; + + if ( bAtEnd || !pDocShell ) + throw container::NoSuchElementException(); // no more elements + + // interface must match ScCellFormatsObj::getElementType + + return uno::Any(uno::Reference<table::XCellRange> (NextObject_Impl())); +} + +ScUniqueCellFormatsObj::~ScUniqueCellFormatsObj() +{ + SolarMutexGuard g; + + if (pDocShell) + pDocShell->GetDocument().RemoveUnoObject(*this); +} + +void ScUniqueCellFormatsObj::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + if ( dynamic_cast<const ScUpdateRefHint*>(&rHint) ) + { + //! aTotalRange... + } + else + { + if ( rHint.GetId() == SfxHintId::Dying ) + pDocShell = nullptr; + } +} + +// Fill the list of formats from the document + +namespace { + +// hash code to access the range lists by ScPatternAttr pointer +struct ScPatternHashCode +{ + size_t operator()( const ScPatternAttr* pPattern ) const + { + return reinterpret_cast<size_t>(pPattern); + } +}; + +} + +// Hash map to find a range by its start row +typedef std::unordered_map< SCROW, ScRange > ScRowRangeHashMap; + +namespace { + +// Hash map entry. +// The Join method depends on the column-wise order of ScAttrRectIterator +class ScUniqueFormatsEntry +{ + enum EntryState { STATE_EMPTY, STATE_SINGLE, STATE_COMPLEX }; + + EntryState eState; + ScRange aSingleRange; + ScRowRangeHashMap aJoinedRanges; // "active" ranges to be merged + std::vector<ScRange> aCompletedRanges; // ranges that will no longer be touched + ScRangeListRef aReturnRanges; // result as ScRangeList for further use + +public: + ScUniqueFormatsEntry() : eState( STATE_EMPTY ) {} + + void Join( const ScRange& rNewRange ); + const ScRangeList& GetRanges(); + void Clear() { aReturnRanges.clear(); } // aJoinedRanges and aCompletedRanges are cleared in GetRanges +}; + +} + +void ScUniqueFormatsEntry::Join( const ScRange& rNewRange ) +{ + // Special-case handling for single range + + if ( eState == STATE_EMPTY ) + { + aSingleRange = rNewRange; + eState = STATE_SINGLE; + return; + } + if ( eState == STATE_SINGLE ) + { + if ( aSingleRange.aStart.Row() == rNewRange.aStart.Row() && + aSingleRange.aEnd.Row() == rNewRange.aEnd.Row() && + aSingleRange.aEnd.Col() + 1 == rNewRange.aStart.Col() ) + { + aSingleRange.aEnd.SetCol( rNewRange.aEnd.Col() ); + return; // still a single range + } + + SCROW nSingleRow = aSingleRange.aStart.Row(); + aJoinedRanges.emplace( nSingleRow, aSingleRange ); + eState = STATE_COMPLEX; + // continue normally + } + + // This is called in the order of ScAttrRectIterator results. + // rNewRange can only be joined with an existing entry if it's the same rows, starting in the next column. + // If the old entry for the start row extends to a different end row, or ends in a different column, it + // can be moved to aCompletedRanges because it can't be joined with following iterator results. + // Everything happens within one sheet, so Tab can be ignored. + + SCROW nStartRow = rNewRange.aStart.Row(); + ScRowRangeHashMap::iterator aIter( aJoinedRanges.find( nStartRow ) ); // find the active entry for the start row + if ( aIter != aJoinedRanges.end() ) + { + ScRange& rOldRange = aIter->second; + if ( rOldRange.aEnd.Row() == rNewRange.aEnd.Row() && + rOldRange.aEnd.Col() + 1 == rNewRange.aStart.Col() ) + { + // extend existing range + rOldRange.aEnd.SetCol( rNewRange.aEnd.Col() ); + } + else + { + // move old range to aCompletedRanges, keep rNewRange for joining + aCompletedRanges.push_back( rOldRange ); + rOldRange = rNewRange; // replace in hash map + } + } + else + { + // keep rNewRange for joining + aJoinedRanges.emplace( nStartRow, rNewRange ); + } +} + +const ScRangeList& ScUniqueFormatsEntry::GetRanges() +{ + if ( eState == STATE_SINGLE ) + { + aReturnRanges = new ScRangeList( aSingleRange ); + return *aReturnRanges; + } + + // move remaining entries from aJoinedRanges to aCompletedRanges + + for ( const auto& rEntry : aJoinedRanges ) + aCompletedRanges.push_back( rEntry.second ); + aJoinedRanges.clear(); + + // sort all ranges for a predictable API result + + std::sort( aCompletedRanges.begin(), aCompletedRanges.end() ); + + // fill and return ScRangeList + + aReturnRanges = new ScRangeList; + aReturnRanges->insert( aReturnRanges->end(), aCompletedRanges.begin(), aCompletedRanges.end() ); + aCompletedRanges.clear(); + + return *aReturnRanges; +} + +namespace { + +// function object to sort the range lists by start of first range +struct ScUniqueFormatsOrder +{ + bool operator()( const ScRangeList& rList1, const ScRangeList& rList2 ) const + { + // all range lists have at least one entry + OSL_ENSURE( !rList1.empty() && !rList2.empty(), "ScUniqueFormatsOrder: empty list" ); + + // compare start positions using ScAddress comparison operator + return ( rList1[ 0 ].aStart < rList2[ 0 ].aStart ); + } +}; + +} + +ScUniqueCellFormatsObj::ScUniqueCellFormatsObj(ScDocShell* pDocSh, const ScRange& rTotalRange) : + pDocShell( pDocSh ) +{ + pDocShell->GetDocument().AddUnoObject(*this); + + OSL_ENSURE( rTotalRange.aStart.Tab() == rTotalRange.aEnd.Tab(), "different tables" ); + + ScDocument& rDoc = pDocShell->GetDocument(); + SCTAB nTab = rTotalRange.aStart.Tab(); + ScAttrRectIterator aIter( rDoc, nTab, + rTotalRange.aStart.Col(), rTotalRange.aStart.Row(), + rTotalRange.aEnd.Col(), rTotalRange.aEnd.Row() ); + SCCOL nCol1, nCol2; + SCROW nRow1, nRow2; + + // Collect the ranges for each format in a hash map, to avoid nested loops + + std::unordered_map< const ScPatternAttr*, ScUniqueFormatsEntry, ScPatternHashCode > aHashMap; + while (aIter.GetNext( nCol1, nCol2, nRow1, nRow2 ) ) + { + ScRange aRange( nCol1, nRow1, nTab, nCol2, nRow2, nTab ); + const ScPatternAttr* pPattern = rDoc.GetPattern(nCol1, nRow1, nTab); + aHashMap[pPattern].Join( aRange ); + } + + // Fill the vector aRangeLists with the range lists from the hash map + + aRangeLists.reserve( aHashMap.size() ); + for ( auto& rMapEntry : aHashMap ) + { + ScUniqueFormatsEntry& rEntry = rMapEntry.second; + const ScRangeList& rRanges = rEntry.GetRanges(); + aRangeLists.push_back( rRanges ); // copy ScRangeList + rEntry.Clear(); // free memory, don't hold both copies of all ranges + } + + // Sort the vector by first range's start position, to avoid random shuffling + // due to using the ScPatterAttr pointers + + ::std::sort( aRangeLists.begin(), aRangeLists.end(), ScUniqueFormatsOrder() ); +} + + +// XIndexAccess + +sal_Int32 SAL_CALL ScUniqueCellFormatsObj::getCount() +{ + SolarMutexGuard aGuard; + + return aRangeLists.size(); +} + +uno::Any SAL_CALL ScUniqueCellFormatsObj::getByIndex( sal_Int32 nIndex ) +{ + SolarMutexGuard aGuard; + + if(o3tl::make_unsigned(nIndex) >= aRangeLists.size()) + throw lang::IndexOutOfBoundsException(); + + return uno::Any(uno::Reference<sheet::XSheetCellRangeContainer>(new ScCellRangesObj(pDocShell, aRangeLists[nIndex]))); + +} + +uno::Type SAL_CALL ScUniqueCellFormatsObj::getElementType() +{ + return cppu::UnoType<sheet::XSheetCellRangeContainer>::get(); +} + +sal_Bool SAL_CALL ScUniqueCellFormatsObj::hasElements() +{ + SolarMutexGuard aGuard; + return ( !aRangeLists.empty() ); +} + +// XEnumerationAccess + +uno::Reference<container::XEnumeration> SAL_CALL ScUniqueCellFormatsObj::createEnumeration() +{ + SolarMutexGuard aGuard; + if (pDocShell) + return new ScUniqueCellFormatsEnumeration( pDocShell, std::vector(aRangeLists) ); + return nullptr; +} + +ScUniqueCellFormatsEnumeration::ScUniqueCellFormatsEnumeration(ScDocShell* pDocSh, std::vector<ScRangeList>&& rRangeLists) : + aRangeLists(std::move(rRangeLists)), + pDocShell( pDocSh ), + nCurrentPosition(0) +{ + pDocShell->GetDocument().AddUnoObject(*this); +} + +ScUniqueCellFormatsEnumeration::~ScUniqueCellFormatsEnumeration() +{ + SolarMutexGuard g; + + if (pDocShell) + pDocShell->GetDocument().RemoveUnoObject(*this); +} + +void ScUniqueCellFormatsEnumeration::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + if ( dynamic_cast<const ScUpdateRefHint*>(&rHint) ) + { + //! and now ??? + } + else + { + if ( rHint.GetId() == SfxHintId::Dying ) + pDocShell = nullptr; + } +} + +// XEnumeration + +sal_Bool SAL_CALL ScUniqueCellFormatsEnumeration::hasMoreElements() +{ + SolarMutexGuard aGuard; + return o3tl::make_unsigned(nCurrentPosition) < aRangeLists.size(); +} + +uno::Any SAL_CALL ScUniqueCellFormatsEnumeration::nextElement() +{ + SolarMutexGuard aGuard; + + if ( !hasMoreElements() || !pDocShell ) + throw container::NoSuchElementException(); // no more elements + + // interface type must match ScCellFormatsObj::getElementType + + return uno::Any(uno::Reference<sheet::XSheetCellRangeContainer>(new ScCellRangesObj(pDocShell, aRangeLists[nCurrentPosition++]))); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/unoobj/cellvaluebinding.cxx b/sc/source/ui/unoobj/cellvaluebinding.cxx new file mode 100644 index 0000000000..fd8b43f957 --- /dev/null +++ b/sc/source/ui/unoobj/cellvaluebinding.cxx @@ -0,0 +1,576 @@ +/* -*- 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 "cellvaluebinding.hxx" +#include <rtl/math.hxx> +#include <com/sun/star/form/binding/IncompatibleTypesException.hpp> +#include <com/sun/star/lang/NotInitializedException.hpp> +#include <com/sun/star/text/XTextRange.hpp> +#include <com/sun/star/table/XCellRange.hpp> +#include <com/sun/star/sheet/FormulaResult.hpp> +#include <com/sun/star/sheet/XCellAddressable.hpp> +#include <com/sun/star/sheet/XCellRangeData.hpp> +#include <com/sun/star/sheet/XSpreadsheetDocument.hpp> +#include <com/sun/star/container/XIndexAccess.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/util/XNumberFormatsSupplier.hpp> +#include <com/sun/star/util/XNumberFormatTypes.hpp> +#include <com/sun/star/util/NumberFormat.hpp> +#include <cppuhelper/supportsservice.hxx> +#include <comphelper/types.hxx> +#include <comphelper/diagnose_ex.hxx> + +namespace calc +{ + +#define PROP_HANDLE_BOUND_CELL 1 + + namespace lang = ::com::sun::star::lang; + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::table; + using namespace ::com::sun::star::text; + using namespace ::com::sun::star::sheet; + using namespace ::com::sun::star::container; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::util; + using namespace ::com::sun::star::form::binding; + + OCellValueBinding::OCellValueBinding( const Reference< XSpreadsheetDocument >& _rxDocument, bool _bListPos ) + :OCellValueBinding_Base( m_aMutex ) + ,OCellValueBinding_PBase( OCellValueBinding_Base::rBHelper ) + ,m_xDocument( _rxDocument ) + ,m_aModifyListeners( m_aMutex ) + ,m_bInitialized( false ) + ,m_bListPos( _bListPos ) + { + // register our property at the base class + registerPropertyNoMember( + "BoundCell", + PROP_HANDLE_BOUND_CELL, + PropertyAttribute::BOUND | PropertyAttribute::READONLY, + cppu::UnoType<CellAddress>::get(), + css::uno::Any(CellAddress()) + ); + + // TODO: implement a ReadOnly property as required by the service, + // which probably maps to the cell being locked + } + + OCellValueBinding::~OCellValueBinding( ) + { + if ( !OCellValueBinding_Base::rBHelper.bDisposed ) + { + acquire(); // prevent duplicate dtor + dispose(); + } + } + + IMPLEMENT_FORWARD_XINTERFACE2( OCellValueBinding, OCellValueBinding_Base, OCellValueBinding_PBase ) + + IMPLEMENT_FORWARD_XTYPEPROVIDER2( OCellValueBinding, OCellValueBinding_Base, OCellValueBinding_PBase ) + + void SAL_CALL OCellValueBinding::disposing() + { + Reference<XModifyBroadcaster> xBroadcaster( m_xCell, UNO_QUERY ); + if ( xBroadcaster.is() ) + { + xBroadcaster->removeModifyListener( this ); + } + + WeakComponentImplHelperBase::disposing(); + + // TODO: clean up here whatever you need to clean up (e.g. deregister as XEventListener + // for the cell) + } + + Reference< XPropertySetInfo > SAL_CALL OCellValueBinding::getPropertySetInfo( ) + { + return createPropertySetInfo( getInfoHelper() ) ; + } + + ::cppu::IPropertyArrayHelper& SAL_CALL OCellValueBinding::getInfoHelper() + { + return *OCellValueBinding_PABase::getArrayHelper(); + } + + ::cppu::IPropertyArrayHelper* OCellValueBinding::createArrayHelper( ) const + { + Sequence< Property > aProps; + describeProperties( aProps ); + return new ::cppu::OPropertyArrayHelper(aProps); + } + + void SAL_CALL OCellValueBinding::getFastPropertyValue( Any& _rValue, sal_Int32 _nHandle ) const + { + OSL_ENSURE( _nHandle == PROP_HANDLE_BOUND_CELL, "OCellValueBinding::getFastPropertyValue: invalid handle!" ); + // we only have this one property... + + _rValue.clear(); + Reference< XCellAddressable > xCellAddress( m_xCell, UNO_QUERY ); + if ( xCellAddress.is() ) + _rValue <<= xCellAddress->getCellAddress( ); + } + + Sequence< Type > SAL_CALL OCellValueBinding::getSupportedValueTypes( ) + { + checkDisposed( ); + checkInitialized( ); + + sal_Int32 nCount = m_xCellText.is() ? 3 : m_xCell.is() ? 1 : 0; + if ( m_bListPos ) + ++nCount; + + Sequence< Type > aTypes( nCount ); + if ( m_xCell.is() ) + { + auto pTypes = aTypes.getArray(); + + // an XCell can be used to set/get "double" values + pTypes[0] = ::cppu::UnoType<double>::get(); + if ( m_xCellText.is() ) + { + // an XTextRange can be used to set/get "string" values + pTypes[1] = ::cppu::UnoType<OUString>::get(); + // and additionally, we use it to handle booleans + pTypes[2] = ::cppu::UnoType<sal_Bool>::get(); + } + + // add sal_Int32 only if constructed as ListPositionCellBinding + if ( m_bListPos ) + pTypes[nCount-1] = cppu::UnoType<sal_Int32>::get(); + } + + return aTypes; + } + + sal_Bool SAL_CALL OCellValueBinding::supportsType( const Type& aType ) + { + checkDisposed( ); + checkInitialized( ); + + // look up in our sequence + const Sequence< Type > aSupportedTypes( getSupportedValueTypes() ); + for ( auto const & i : aSupportedTypes ) + if ( aType == i ) + return true; + + return false; + } + + Any SAL_CALL OCellValueBinding::getValue( const Type& aType ) + { + checkDisposed( ); + checkInitialized( ); + checkValueType( aType ); + + Any aReturn; + switch ( aType.getTypeClass() ) + { + case TypeClass_STRING: + OSL_ENSURE( m_xCellText.is(), "OCellValueBinding::getValue: don't have a text!" ); + if ( m_xCellText.is() ) + aReturn <<= m_xCellText->getString(); + else + aReturn <<= OUString(); + break; + + case TypeClass_BOOLEAN: + OSL_ENSURE( m_xCell.is(), "OCellValueBinding::getValue: don't have a double value supplier!" ); + if ( m_xCell.is() ) + { + // check if the cell has a numeric value (this might go into a helper function): + + bool bHasValue = false; + CellContentType eCellType = m_xCell->getType(); + if ( eCellType == CellContentType_VALUE ) + bHasValue = true; + else if ( eCellType == CellContentType_FORMULA ) + { + // check if the formula result is a value + if ( m_xCell->getError() == 0 ) + { + Reference<XPropertySet> xProp( m_xCell, UNO_QUERY ); + if ( xProp.is() ) + { + sal_Int32 nResultType; + if ( (xProp->getPropertyValue("FormulaResultType2") >>= nResultType) + && nResultType == FormulaResult::VALUE ) + bHasValue = true; + } + } + } + + if ( bHasValue ) + { + // 0 is "unchecked", any other value is "checked", regardless of number format + double nCellValue = m_xCell->getValue(); + bool bBoolValue = ( nCellValue != 0.0 ); + aReturn <<= bBoolValue; + } + // empty cells, text cells and text or error formula results: leave return value empty + } + break; + + case TypeClass_DOUBLE: + OSL_ENSURE( m_xCell.is(), "OCellValueBinding::getValue: don't have a double value supplier!" ); + if ( m_xCell.is() ) + aReturn <<= m_xCell->getValue(); + else + aReturn <<= double(0); + break; + + case TypeClass_LONG: + OSL_ENSURE( m_xCell.is(), "OCellValueBinding::getValue: don't have a double value supplier!" ); + if ( m_xCell.is() ) + { + // The list position value in the cell is 1-based. + // We subtract 1 from any cell value (no special handling for 0 or negative values). + + sal_Int32 nValue = static_cast<sal_Int32>(rtl::math::approxFloor( m_xCell->getValue() )); + --nValue; + + aReturn <<= nValue; + } + else + aReturn <<= sal_Int32(0); + break; + + default: + OSL_FAIL( "OCellValueBinding::getValue: unreachable code!" ); + // a type other than double and string should never have survived the checkValueType + // above + } + return aReturn; + } + + void SAL_CALL OCellValueBinding::setValue( const Any& aValue ) + { + checkDisposed( ); + checkInitialized( ); + if ( aValue.hasValue() ) + checkValueType( aValue.getValueType() ); + + switch ( aValue.getValueType().getTypeClass() ) + { + case TypeClass_STRING: + { + OSL_ENSURE( m_xCellText.is(), "OCellValueBinding::setValue: don't have a text!" ); + + OUString sText; + aValue >>= sText; + if ( m_xCellText.is() ) + m_xCellText->setString( sText ); + } + break; + + case TypeClass_BOOLEAN: + { + OSL_ENSURE( m_xCell.is(), "OCellValueBinding::setValue: don't have a double value supplier!" ); + + // boolean is stored as values 0 or 1 + // TODO: set the number format to boolean if no format is set? + + bool bValue( false ); + aValue >>= bValue; + double nCellValue = bValue ? 1.0 : 0.0; + + if ( m_xCell.is() ) + m_xCell->setValue( nCellValue ); + + setBooleanFormat(); + } + break; + + case TypeClass_DOUBLE: + { + OSL_ENSURE( m_xCell.is(), "OCellValueBinding::setValue: don't have a double value supplier!" ); + + double nValue = 0; + aValue >>= nValue; + if ( m_xCell.is() ) + m_xCell->setValue( nValue ); + } + break; + + case TypeClass_LONG: + { + OSL_ENSURE( m_xCell.is(), "OCellValueBinding::setValue: don't have a double value supplier!" ); + + sal_Int32 nValue = 0; + aValue >>= nValue; // list index from control layer (0-based) + ++nValue; // the list position value in the cell is 1-based + if ( m_xCell.is() ) + m_xCell->setValue( nValue ); + } + break; + + case TypeClass_VOID: + { + // #N/A error value can only be set using XCellRangeData + + Reference<XCellRangeData> xData( m_xCell, UNO_QUERY ); + OSL_ENSURE( xData.is(), "OCellValueBinding::setValue: don't have XCellRangeData!" ); + if ( xData.is() ) + { + Sequence<Any> aInner(1); // one empty element + Sequence< Sequence<Any> > aOuter( &aInner, 1 ); // one row + xData->setDataArray( aOuter ); + } + } + break; + + default: + OSL_FAIL( "OCellValueBinding::setValue: unreachable code!" ); + // a type other than double and string should never have survived the checkValueType + // above + } + } + + void OCellValueBinding::setBooleanFormat() + { + // set boolean number format if not already set + + OUString sPropName( "NumberFormat" ); + Reference<XPropertySet> xCellProp( m_xCell, UNO_QUERY ); + Reference<XNumberFormatsSupplier> xSupplier( m_xDocument, UNO_QUERY ); + if ( !(xSupplier.is() && xCellProp.is()) ) + return; + + Reference<XNumberFormats> xFormats(xSupplier->getNumberFormats()); + Reference<XNumberFormatTypes> xTypes( xFormats, UNO_QUERY ); + if ( !xTypes.is() ) + return; + + lang::Locale aLocale; + bool bWasBoolean = false; + + sal_Int32 nOldIndex = ::comphelper::getINT32( xCellProp->getPropertyValue( sPropName ) ); + Reference<XPropertySet> xOldFormat; + try + { + xOldFormat.set(xFormats->getByKey( nOldIndex )); + } + catch ( Exception& ) + { + // non-existing format - can happen, use defaults + } + if ( xOldFormat.is() ) + { + // use the locale of the existing format + xOldFormat->getPropertyValue("Locale") >>= aLocale; + + sal_Int16 nOldType = ::comphelper::getINT16( + xOldFormat->getPropertyValue("Type") ); + if ( nOldType & NumberFormat::LOGICAL ) + bWasBoolean = true; + } + + if ( !bWasBoolean ) + { + sal_Int32 nNewIndex = xTypes->getStandardFormat( NumberFormat::LOGICAL, aLocale ); + xCellProp->setPropertyValue( sPropName, Any( nNewIndex ) ); + } + } + + void OCellValueBinding::checkDisposed( ) const + { + if ( OCellValueBinding_Base::rBHelper.bInDispose || OCellValueBinding_Base::rBHelper.bDisposed ) + throw DisposedException(); + // TODO: is it worth having an error message here? + } + + void OCellValueBinding::checkInitialized() + { + if ( !m_bInitialized ) + throw NotInitializedException("CellValueBinding is not initialized", getXWeak()); + } + + void OCellValueBinding::checkValueType( const Type& _rType ) const + { + OCellValueBinding* pNonConstThis = const_cast< OCellValueBinding* >( this ); + if ( !pNonConstThis->supportsType( _rType ) ) + { + OUString sMessage = "The given type (" + + _rType.getTypeName() + + ") is not supported by this binding."; + // TODO: localize this error message + + throw IncompatibleTypesException( sMessage, *pNonConstThis ); + // TODO: alternatively use a type converter service for this? + } + } + + OUString SAL_CALL OCellValueBinding::getImplementationName( ) + { + return "com.sun.star.comp.sheet.OCellValueBinding"; + } + + sal_Bool SAL_CALL OCellValueBinding::supportsService( const OUString& _rServiceName ) + { + return cppu::supportsService(this, _rServiceName); + } + + Sequence< OUString > SAL_CALL OCellValueBinding::getSupportedServiceNames( ) + { + Sequence< OUString > aServices( m_bListPos ? 3 : 2 ); + auto pServices = aServices.getArray(); + pServices[ 0 ] = "com.sun.star.table.CellValueBinding"; + pServices[ 1 ] = "com.sun.star.form.binding.ValueBinding"; + if ( m_bListPos ) + pServices[ 2 ] = "com.sun.star.table.ListPositionCellBinding"; + return aServices; + } + + void SAL_CALL OCellValueBinding::addModifyListener( const Reference< XModifyListener >& _rxListener ) + { + if ( _rxListener.is() ) + m_aModifyListeners.addInterface( _rxListener ); + } + + void SAL_CALL OCellValueBinding::removeModifyListener( const Reference< XModifyListener >& _rxListener ) + { + if ( _rxListener.is() ) + m_aModifyListeners.removeInterface( _rxListener ); + } + + void OCellValueBinding::notifyModified() + { + EventObject aEvent; + aEvent.Source.set(*this); + + ::comphelper::OInterfaceIteratorHelper3 aIter( m_aModifyListeners ); + while ( aIter.hasMoreElements() ) + { + try + { + aIter.next()->modified( aEvent ); + } + catch( const RuntimeException& ) + { + // silent this + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "sc", "OCellValueBinding::notifyModified: caught a (non-runtime) exception!" ); + } + } + } + + void SAL_CALL OCellValueBinding::modified( const EventObject& /* aEvent */ ) + { + notifyModified(); + } + + void SAL_CALL OCellValueBinding::disposing( const EventObject& aEvent ) + { + Reference<XInterface> xCellInt( m_xCell, UNO_QUERY ); + if ( xCellInt == aEvent.Source ) + { + // release references to cell object + m_xCell.clear(); + m_xCellText.clear(); + } + } + + void SAL_CALL OCellValueBinding::initialize( const Sequence< Any >& _rArguments ) + { + if ( m_bInitialized ) + throw RuntimeException("CellValueBinding is already initialized", getXWeak()); + + // get the cell address + CellAddress aAddress; + bool bFoundAddress = false; + + for ( const Any& rArg : _rArguments ) + { + NamedValue aValue; + if ( rArg >>= aValue ) + { + if ( aValue.Name == "BoundCell" ) + { + if ( aValue.Value >>= aAddress ) + { + bFoundAddress = true; + break; + } + } + } + } + + if ( !bFoundAddress ) + throw RuntimeException("Cell not found", getXWeak()); + + // get the cell object + try + { + // first the sheets collection + Reference< XIndexAccess > xSheets; + if ( m_xDocument.is() ) + xSheets.set(m_xDocument->getSheets( ), css::uno::UNO_QUERY); + OSL_ENSURE( xSheets.is(), "OCellValueBinding::initialize: could not retrieve the sheets!" ); + + if ( xSheets.is() ) + { + // the concrete sheet + Reference< XCellRange > xSheet(xSheets->getByIndex( aAddress.Sheet ), UNO_QUERY); + OSL_ENSURE( xSheet.is(), "OCellValueBinding::initialize: NULL sheet, but no exception!" ); + + // the concrete cell + if ( xSheet.is() ) + { + m_xCell.set(xSheet->getCellByPosition( aAddress.Column, aAddress.Row )); + Reference< XCellAddressable > xAddressAccess( m_xCell, UNO_QUERY ); + OSL_ENSURE( xAddressAccess.is(), "OCellValueBinding::initialize: either NULL cell, or cell without address access!" ); + } + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "sc", "OCellValueBinding::initialize: caught an exception while retrieving the cell object!" ); + } + + if ( !m_xCell.is() ) + throw RuntimeException("Failed to retrieve cell object", getXWeak()); + + m_xCellText.set(m_xCell, css::uno::UNO_QUERY); + + Reference<XModifyBroadcaster> xBroadcaster( m_xCell, UNO_QUERY ); + if ( xBroadcaster.is() ) + { + xBroadcaster->addModifyListener( this ); + } + + // TODO: add as XEventListener to the cell, so we get notified when it dies, + // and can dispose ourself then + + // TODO: somehow add as listener so we get notified when the address of the cell changes + // We need to forward this as change in our BoundCell property to our property change listeners + + // TODO: be an XModifyBroadcaster, so that changes in our cell can be notified + // to the BindableValue which is/will be bound to this instance. + + m_bInitialized = true; + // TODO: place your code here + } + +} // namespace calc + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/unoobj/cellvaluebinding.hxx b/sc/source/ui/unoobj/cellvaluebinding.hxx new file mode 100644 index 0000000000..511303f7ed --- /dev/null +++ b/sc/source/ui/unoobj/cellvaluebinding.hxx @@ -0,0 +1,146 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/form/binding/XValueBinding.hpp> +#include <com/sun/star/util/XModifyBroadcaster.hpp> +#include <cppuhelper/compbase.hxx> +#include <cppuhelper/basemutex.hxx> +#include <comphelper/interfacecontainer3.hxx> +#include <comphelper/propertycontainer.hxx> +#include <comphelper/uno3.hxx> +#include <comphelper/proparrhlp.hxx> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XInitialization.hpp> + +namespace com::sun::star::table { class XCell; } +namespace com::sun::star::sheet { class XSpreadsheetDocument; } +namespace com::sun::star::text { class XTextRange; } + +namespace calc +{ + + //= OCellValueBinding + + class OCellValueBinding; + // the base for our interfaces + typedef ::cppu::WeakComponentImplHelper < css::form::binding::XValueBinding + , css::lang::XServiceInfo + , css::util::XModifyBroadcaster + , css::util::XModifyListener + , css::lang::XInitialization + > OCellValueBinding_Base; + // the base for the property handling + typedef ::comphelper::OPropertyContainer OCellValueBinding_PBase; + // the second base for property handling + typedef ::comphelper::OPropertyArrayUsageHelper< OCellValueBinding > + OCellValueBinding_PABase; + + class OCellValueBinding :public ::cppu::BaseMutex + ,public OCellValueBinding_Base // order matters! before OCellValueBinding_PBase, so rBHelper gets initialized + ,public OCellValueBinding_PBase + ,public OCellValueBinding_PABase + { + private: + css::uno::Reference< css::sheet::XSpreadsheetDocument > + m_xDocument; /// the document where our cell lives + css::uno::Reference< css::table::XCell > + m_xCell; /// the cell we're bound to, for double value access + css::uno::Reference< css::text::XTextRange > + m_xCellText; /// the cell we're bound to, for text access + ::comphelper::OInterfaceContainerHelper3<css::util::XModifyListener> + m_aModifyListeners; /// our modify listeners + bool m_bInitialized; /// has XInitialization::initialize been called? + bool m_bListPos; /// constructed as ListPositionCellBinding? + + public: + OCellValueBinding( + const css::uno::Reference< css::sheet::XSpreadsheetDocument >& _rxDocument, + bool _bListPos + ); + + using OCellValueBinding_PBase::getFastPropertyValue; + + protected: + virtual ~OCellValueBinding( ) override; + + protected: + // XInterface + DECLARE_XINTERFACE() + + // XTypeProvider + DECLARE_XTYPEPROVIDER() + + // XValueBinding + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getSupportedValueTypes( ) override; + virtual sal_Bool SAL_CALL supportsType( const css::uno::Type& aType ) override; + virtual css::uno::Any SAL_CALL getValue( const css::uno::Type& aType ) override; + virtual void SAL_CALL setValue( const css::uno::Any& aValue ) override; + + // OComponentHelper/XComponent + virtual void SAL_CALL disposing() override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + + // XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + + // OPropertySetHelper + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + virtual void SAL_CALL getFastPropertyValue( css::uno::Any& _rValue, sal_Int32 _nHandle ) const override; + + // ::comphelper::OPropertyArrayUsageHelper + virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const override; + + // XModifyBroadcaster + virtual void SAL_CALL addModifyListener( const css::uno::Reference< css::util::XModifyListener >& aListener ) override; + virtual void SAL_CALL removeModifyListener( const css::uno::Reference< css::util::XModifyListener >& aListener ) override; + + // XModifyListener + virtual void SAL_CALL modified( const css::lang::EventObject& aEvent ) override; + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + // XInitialization + virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override; + + private: + void checkDisposed( ) const; + void checkValueType( const css::uno::Type& _rType ) const; + void checkInitialized(); + + /** notifies our modify listeners + @precond + our mutex is <em>not</em> locked + */ + void notifyModified(); + + void setBooleanFormat(); + + private: + OCellValueBinding( const OCellValueBinding& ) = delete; + OCellValueBinding& operator=( const OCellValueBinding& ) = delete; + }; + +} // namespace calc + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/unoobj/chart2uno.cxx b/sc/source/ui/unoobj/chart2uno.cxx new file mode 100644 index 0000000000..e20a473f06 --- /dev/null +++ b/sc/source/ui/unoobj/chart2uno.cxx @@ -0,0 +1,3517 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <memory> +#include <sal/config.h> +#include <sal/log.hxx> + +#include <algorithm> +#include <utility> + +#include <chart2uno.hxx> +#include <miscuno.hxx> +#include <document.hxx> +#include <docsh.hxx> +#include <formulacell.hxx> +#include <unonames.hxx> +#include <globstr.hrc> +#include <scresid.hxx> +#include <rangeutl.hxx> +#include <hints.hxx> +#include <unoreflist.hxx> +#include <compiler.hxx> +#include <reftokenhelper.hxx> +#include <chartlis.hxx> +#include <tokenuno.hxx> +#include <cellvalue.hxx> +#include <tokenarray.hxx> +#include <scmatrix.hxx> +#include <brdcst.hxx> +#include <mtvelements.hxx> + +#include <formula/opcode.hxx> +#include <o3tl/safeint.hxx> +#include <svl/numformat.hxx> +#include <svl/sharedstring.hxx> + +#include <sfx2/objsh.hxx> +#include <vcl/svapp.hxx> + +#include <com/sun/star/beans/UnknownPropertyException.hpp> +#include <com/sun/star/chart/ChartDataRowSource.hpp> +#include <com/sun/star/chart2/data/LabeledDataSequence.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <comphelper/extract.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/sequence.hxx> + +#include <limits> + +SC_SIMPLE_SERVICE_INFO( ScChart2DataProvider, "ScChart2DataProvider", + "com.sun.star.chart2.data.DataProvider") +SC_SIMPLE_SERVICE_INFO( ScChart2DataSource, "ScChart2DataSource", + "com.sun.star.chart2.data.DataSource") +SC_SIMPLE_SERVICE_INFO( ScChart2DataSequence, "ScChart2DataSequence", + "com.sun.star.chart2.data.DataSequence") + +using namespace ::com::sun::star; +using namespace ::formula; +using ::com::sun::star::uno::Sequence; +using ::std::unique_ptr; +using ::std::vector; +using ::std::distance; +using ::std::shared_ptr; + +namespace +{ +std::span<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 }, + }; + return aDataProviderPropertyMap_Impl; +} + +std::span<const SfxItemPropertyMapEntry> lcl_GetDataSequencePropertyMap() +{ + static const SfxItemPropertyMapEntry aDataSequencePropertyMap_Impl[] = + { + { SC_UNONAME_HIDDENVALUES, 0, cppu::UnoType<uno::Sequence<sal_Int32>>::get(), 0, 0 }, + { SC_UNONAME_ROLE, 0, cppu::UnoType<css::chart2::data::DataSequenceRole>::get(), 0, 0 }, + { SC_UNONAME_INCLUDEHIDDENCELLS, 0, cppu::UnoType<bool>::get(), 0, 0 }, + }; + return aDataSequencePropertyMap_Impl; +} + +struct lcl_appendTableNumber +{ + explicit lcl_appendTableNumber( OUStringBuffer & rBuffer ) : + m_rBuffer( rBuffer ) + {} + void operator() ( SCTAB nTab ) + { + // there is no append with SCTAB or sal_Int16 + m_rBuffer.append( static_cast< sal_Int32 >( nTab )); + m_rBuffer.append( ' ' ); + } +private: + OUStringBuffer & m_rBuffer; +}; + +OUString lcl_createTableNumberList( const ::std::vector< SCTAB > & rTableVector ) +{ + OUStringBuffer aBuffer; + ::std::for_each( rTableVector.begin(), rTableVector.end(), lcl_appendTableNumber( aBuffer )); + // remove last trailing ' ' + if( !aBuffer.isEmpty() ) + aBuffer.setLength( aBuffer.getLength() - 1 ); + return aBuffer.makeStringAndClear(); +} + +uno::Reference< frame::XModel > lcl_GetXModel( const ScDocument * pDoc ) +{ + uno::Reference< frame::XModel > xModel; + ScDocShell * pObjSh( pDoc ? pDoc->GetDocumentShell() : nullptr ); + if( pObjSh ) + xModel.set( pObjSh->GetModel()); + return xModel; +} + +struct TokenTable +{ + SCROW mnRowCount; + SCCOL mnColCount; + vector<std::unique_ptr<FormulaToken>> maTokens; + + // noncopyable + TokenTable(const TokenTable&) = delete; + const TokenTable& operator=(const TokenTable&) = delete; + + TokenTable() + : mnRowCount(0) + , mnColCount(0) + { + } + + void init( SCCOL nColCount, SCROW nRowCount ) + { + mnColCount = nColCount; + mnRowCount = nRowCount; + maTokens.reserve(mnColCount*mnRowCount); + } + void clear() + { + for (auto & rToken : maTokens) + rToken.reset(); + } + + void push_back( std::unique_ptr<FormulaToken> pToken ) + { + maTokens.push_back( std::move(pToken) ); + OSL_ENSURE( maTokens.size()<= o3tl::make_unsigned( mnColCount*mnRowCount ), "too many tokens" ); + } + + sal_uInt32 getIndex(SCCOL nCol, SCROW nRow) const + { + OSL_ENSURE( nCol<mnColCount, "wrong column index" ); + OSL_ENSURE( nRow<mnRowCount, "wrong row index" ); + sal_uInt32 nRet = static_cast<sal_uInt32>(nCol*mnRowCount + nRow); + OSL_ENSURE( maTokens.size()>= o3tl::make_unsigned( mnColCount*mnRowCount ), "too few tokens" ); + return nRet; + } + + vector<ScTokenRef> getColRanges(const ScDocument* pDoc, SCCOL nCol) const; + vector<ScTokenRef> getRowRanges(const ScDocument* pDoc, SCROW nRow) const; + vector<ScTokenRef> getAllRanges(const ScDocument* pDoc) const; +}; + +vector<ScTokenRef> TokenTable::getColRanges(const ScDocument* pDoc, SCCOL nCol) const +{ + if (nCol >= mnColCount) + return vector<ScTokenRef>(); + if( mnRowCount<=0 ) + return vector<ScTokenRef>(); + + vector<ScTokenRef> aTokens; + sal_uInt32 nLast = getIndex(nCol, mnRowCount-1); + for (sal_uInt32 i = getIndex(nCol, 0); i <= nLast; ++i) + { + FormulaToken* p = maTokens[i].get(); + if (!p) + continue; + + ScTokenRef pCopy(p->Clone()); + ScRefTokenHelper::join(pDoc, aTokens, pCopy, ScAddress()); + } + return aTokens; +} + +vector<ScTokenRef> TokenTable::getRowRanges(const ScDocument* pDoc, SCROW nRow) const +{ + if (nRow >= mnRowCount) + return vector<ScTokenRef>(); + if( mnColCount<=0 ) + return vector<ScTokenRef>(); + + vector<ScTokenRef> aTokens; + sal_uInt32 nLast = getIndex(mnColCount-1, nRow); + for (sal_uInt32 i = getIndex(0, nRow); i <= nLast; i += mnRowCount) + { + FormulaToken* p = maTokens[i].get(); + if (!p) + continue; + + ScTokenRef p2(p->Clone()); + ScRefTokenHelper::join(pDoc, aTokens, p2, ScAddress()); + } + return aTokens; +} + +vector<ScTokenRef> TokenTable::getAllRanges(const ScDocument* pDoc) const +{ + vector<ScTokenRef> aTokens; + sal_uInt32 nStop = mnColCount*mnRowCount; + for (sal_uInt32 i = 0; i < nStop; i++) + { + FormulaToken* p = maTokens[i].get(); + if (!p) + continue; + + ScTokenRef p2(p->Clone()); + ScRefTokenHelper::join(pDoc, aTokens, p2, ScAddress()); + } + return aTokens; +} + +typedef std::map<SCROW, std::unique_ptr<FormulaToken>> FormulaTokenMap; +typedef std::map<sal_uInt32, FormulaTokenMap> FormulaTokenMapMap; + +class Chart2PositionMap +{ +public: + Chart2PositionMap(SCCOL nColCount, SCROW nRowCount, + bool bFillRowHeader, bool bFillColumnHeader, FormulaTokenMapMap& rCols, + ScDocument* pDoc ); + ~Chart2PositionMap(); + + SCCOL getDataColCount() const { return mnDataColCount; } + SCROW getDataRowCount() const { return mnDataRowCount; } + + vector<ScTokenRef> getLeftUpperCornerRanges() const; + vector<ScTokenRef> getAllColHeaderRanges() const; + vector<ScTokenRef> getAllRowHeaderRanges() const; + + vector<ScTokenRef> getColHeaderRanges(SCCOL nChartCol) const; + vector<ScTokenRef> getRowHeaderRanges(SCROW nChartRow) const; + + vector<ScTokenRef> getDataColRanges(SCCOL nCol) const; + vector<ScTokenRef> getDataRowRanges(SCROW nRow) const; + +private: + const ScDocument* mpDoc; + SCCOL mnDataColCount; + SCROW mnDataRowCount; + + TokenTable maLeftUpperCorner; //nHeaderColCount*nHeaderRowCount + TokenTable maColHeaders; //mnDataColCount*nHeaderRowCount + TokenTable maRowHeaders; //nHeaderColCount*mnDataRowCount + TokenTable maData;//mnDataColCount*mnDataRowCount +}; + +Chart2PositionMap::Chart2PositionMap(SCCOL nAllColCount, SCROW nAllRowCount, + bool bFillRowHeader, bool bFillColumnHeader, FormulaTokenMapMap& rCols, ScDocument* pDoc) +{ + mpDoc = pDoc; + // if bFillRowHeader is true, at least the first column serves as a row header. + // If more than one column is pure text all the first pure text columns are used as header. + // Likewise, if bFillColumnHeader is true, at least the first row serves as a column header. + // If more than one row is pure text all the first pure text rows are used as header. + + SCROW nHeaderRowCount = (bFillColumnHeader && nAllColCount && nAllRowCount) ? 1 : 0; + SCCOL nHeaderColCount = (bFillRowHeader && nAllColCount && nAllRowCount) ? 1 : 0; + + if( pDoc && (nHeaderColCount || nHeaderRowCount ) ) + { + //check whether there is more than one text column or row that should be added to the headers + SCROW nMaxHeaderRow = nAllRowCount; + SCCOL nCol = 0; + for (auto it = rCols.begin(); it != rCols.end(); ++it, ++nCol) + { + // Skip header columns + if (nCol < nHeaderColCount) + continue; + + const auto& rCol = *it; + + bool bFoundValuesInCol = false; + bool bFoundAnythingInCol = false; + SCROW nRow = 0; + for (auto it2 = rCol.second.begin(); it2 != rCol.second.end(); ++it2, ++nRow) + { + const auto& rCell = *it2; + + // Skip header rows + if (nRow < nHeaderRowCount || !rCell.second) + continue; + + ScRange aRange; + bool bExternal = false; + StackVar eType = rCell.second->GetType(); + if( eType==svExternal || eType==svExternalSingleRef || eType==svExternalDoubleRef || eType==svExternalName ) + bExternal = true;//lllll todo correct? + ScTokenRef pSharedToken(rCell.second->Clone()); + ScRefTokenHelper::getRangeFromToken(pDoc, aRange, pSharedToken, ScAddress(), bExternal); + SCCOL nCol1=0, nCol2=0; + SCROW nRow1=0, nRow2=0; + SCTAB nTab1=0, nTab2=0; + aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ); + if ( pDoc->HasValueData( nCol1, nRow1, nTab1 ) ) + { + // Found some numeric data + bFoundValuesInCol = true; + nMaxHeaderRow = std::min(nMaxHeaderRow, nRow); + break; + } + if ( pDoc->HasData( nCol1, nRow1, nTab1 ) ) + { + // Found some other data (non-numeric) + bFoundAnythingInCol = true; + } + else + { + // If cell is empty, it belongs to data + nMaxHeaderRow = std::min(nMaxHeaderRow, nRow); + } + } + + if (nHeaderColCount && !bFoundValuesInCol && bFoundAnythingInCol && nCol == nHeaderColCount) + { + // There is no values in row, but some data. And this column is next to header + // So lets put it to header + nHeaderColCount++; + } + } + + if (nHeaderRowCount) + { + nHeaderRowCount = nMaxHeaderRow; + } + } + + mnDataColCount = nAllColCount - nHeaderColCount; + mnDataRowCount = nAllRowCount - nHeaderRowCount; + + maLeftUpperCorner.init(nHeaderColCount,nHeaderRowCount); + maColHeaders.init(mnDataColCount,nHeaderRowCount); + maRowHeaders.init(nHeaderColCount,mnDataRowCount); + maData.init(mnDataColCount,mnDataRowCount); + + FormulaTokenMapMap::iterator it1 = rCols.begin(); + for (SCCOL nCol = 0; nCol < nAllColCount; ++nCol) + { + if (it1 != rCols.end()) + { + FormulaTokenMap& rCol = it1->second; + FormulaTokenMap::iterator it2 = rCol.begin(); + for (SCROW nRow = 0; nRow < nAllRowCount; ++nRow) + { + std::unique_ptr<FormulaToken> pToken; + if (it2 != rCol.end()) + { + pToken = std::move(it2->second); + ++it2; + } + + if( nCol < nHeaderColCount ) + { + if( nRow < nHeaderRowCount ) + maLeftUpperCorner.push_back(std::move(pToken)); + else + maRowHeaders.push_back(std::move(pToken)); + } + else if( nRow < nHeaderRowCount ) + maColHeaders.push_back(std::move(pToken)); + else + maData.push_back(std::move(pToken)); + } + ++it1; + } + } +} + +Chart2PositionMap::~Chart2PositionMap() +{ + maLeftUpperCorner.clear(); + maColHeaders.clear(); + maRowHeaders.clear(); + maData.clear(); +} + +vector<ScTokenRef> Chart2PositionMap::getLeftUpperCornerRanges() const +{ + return maLeftUpperCorner.getAllRanges(mpDoc); +} +vector<ScTokenRef> Chart2PositionMap::getAllColHeaderRanges() const +{ + return maColHeaders.getAllRanges(mpDoc); +} +vector<ScTokenRef> Chart2PositionMap::getAllRowHeaderRanges() const +{ + return maRowHeaders.getAllRanges(mpDoc); +} +vector<ScTokenRef> Chart2PositionMap::getColHeaderRanges(SCCOL nCol) const +{ + return maColHeaders.getColRanges(mpDoc, nCol); +} +vector<ScTokenRef> Chart2PositionMap::getRowHeaderRanges(SCROW nRow) const +{ + return maRowHeaders.getRowRanges(mpDoc, nRow); +} + +vector<ScTokenRef> Chart2PositionMap::getDataColRanges(SCCOL nCol) const +{ + return maData.getColRanges(mpDoc, nCol); +} + +vector<ScTokenRef> Chart2PositionMap::getDataRowRanges(SCROW nRow) const +{ + return maData.getRowRanges(mpDoc, nRow); +} + +/** + * Designed to be a drop-in replacement for ScChartPositioner, in order to + * handle external references. + */ +class Chart2Positioner +{ + enum GlueType + { + GLUETYPE_NA, + GLUETYPE_NONE, + GLUETYPE_COLS, + GLUETYPE_ROWS, + GLUETYPE_BOTH + }; + +public: + Chart2Positioner(const Chart2Positioner&) = delete; + const Chart2Positioner& operator=(const Chart2Positioner&) = delete; + + Chart2Positioner(ScDocument* pDoc, const vector<ScTokenRef>& rRefTokens) : + mrRefTokens(rRefTokens), + meGlue(GLUETYPE_NA), + mnStartCol(0), + mnStartRow(0), + mpDoc(pDoc), + mbColHeaders(false), + mbRowHeaders(false), + mbDummyUpperLeft(false) + { + } + + void setHeaders(bool bColHeaders, bool bRowHeaders) + { + mbColHeaders = bColHeaders; + mbRowHeaders = bRowHeaders; + } + + Chart2PositionMap* getPositionMap() + { + createPositionMap(); + return mpPositionMap.get(); + } + +private: + void invalidateGlue(); + void glueState(); + void calcGlueState(SCCOL nCols, SCROW nRows); + void createPositionMap(); + +private: + const vector<ScTokenRef>& mrRefTokens; + std::unique_ptr<Chart2PositionMap> mpPositionMap; + GlueType meGlue; + SCCOL mnStartCol; + SCROW mnStartRow; + ScDocument* mpDoc; + bool mbColHeaders:1; + bool mbRowHeaders:1; + bool mbDummyUpperLeft:1; +}; + +void Chart2Positioner::invalidateGlue() +{ + meGlue = GLUETYPE_NA; + mpPositionMap.reset(); +} + +void Chart2Positioner::glueState() +{ + if (meGlue != GLUETYPE_NA) + return; + + mbDummyUpperLeft = false; + if (mrRefTokens.size() <= 1) + { + // Source data consists of only one data range. + const ScTokenRef& p = mrRefTokens.front(); + ScComplexRefData aData; + if (ScRefTokenHelper::getDoubleRefDataFromToken(aData, p)) + { + if (aData.Ref1.Tab() == aData.Ref2.Tab()) + meGlue = GLUETYPE_NONE; + else + meGlue = GLUETYPE_COLS; + mnStartCol = aData.Ref1.Col(); + mnStartRow = aData.Ref1.Row(); + } + else + { + invalidateGlue(); + mnStartCol = 0; + mnStartRow = 0; + } + return; + } + + ScComplexRefData aData; + if (!ScRefTokenHelper::getDoubleRefDataFromToken(aData, mrRefTokens.front())) + { + SAL_WARN("sc", "Chart2Positioner::glueState getDoubleRefDataFromToken failed"); + invalidateGlue(); + mnStartCol = 0; + mnStartRow = 0; + return; + } + mnStartCol = aData.Ref1.Col(); + mnStartRow = aData.Ref1.Row(); + + SCCOL nEndCol = 0; + SCROW nEndRow = 0; + for (const auto& rxToken : mrRefTokens) + { + ScRefTokenHelper::getDoubleRefDataFromToken(aData, rxToken); + SCCOLROW n1 = aData.Ref1.Col(); + SCCOLROW n2 = aData.Ref2.Col(); + if (n1 > mpDoc->MaxCol()) + n1 = mpDoc->MaxCol(); + if (n2 > mpDoc->MaxCol()) + n2 = mpDoc->MaxCol(); + if (n1 < mnStartCol) + mnStartCol = static_cast<SCCOL>(n1); + if (n2 > nEndCol) + nEndCol = static_cast<SCCOL>(n2); + + n1 = aData.Ref1.Row(); + n2 = aData.Ref2.Row(); + if (n1 > mpDoc->MaxRow()) + n1 = mpDoc->MaxRow(); + if (n2 > mpDoc->MaxRow()) + n2 = mpDoc->MaxRow(); + + if (n1 < mnStartRow) + mnStartRow = static_cast<SCROW>(n1); + if (n2 > nEndRow) + nEndRow = static_cast<SCROW>(n2); + } + + if (mnStartCol == nEndCol) + { + // All source data is in a single column. + meGlue = GLUETYPE_ROWS; + return; + } + + if (mnStartRow == nEndRow) + { + // All source data is in a single row. + meGlue = GLUETYPE_COLS; + return; + } + + // total column size + SCCOL nC = nEndCol - mnStartCol + 1; + + // total row size + SCROW nR = nEndRow - mnStartRow + 1; + + // #i103540# prevent invalid vector size + if ((nC <= 0) || (nR <= 0)) + { + invalidateGlue(); + mnStartCol = 0; + mnStartRow = 0; + return; + } + + calcGlueState(nC, nR); +} + +enum State { Hole = 0, Occupied = 1, Free = 2, Glue = 3 }; + +void Chart2Positioner::calcGlueState(SCCOL nColSize, SCROW nRowSize) +{ + // TODO: This code can use some space optimization. Using an array to + // store individual cell's states is terribly inefficient esp for large + // data ranges; let's use flat_segment_tree to reduce memory usage here. + + sal_uInt32 nCR = static_cast<sal_uInt32>(nColSize*nRowSize); + + vector<State> aCellStates(nCR, Hole); + + // Mark all referenced cells "occupied". + for (const auto& rxToken : mrRefTokens) + { + ScComplexRefData aData; + ScRefTokenHelper::getDoubleRefDataFromToken(aData, rxToken); + SCCOL nCol1 = aData.Ref1.Col() - mnStartCol; + SCCOL nCol2 = aData.Ref2.Col() - mnStartCol; + SCROW nRow1 = aData.Ref1.Row() - mnStartRow; + SCROW nRow2 = aData.Ref2.Row() - mnStartRow; + for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol) + for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow) + { + size_t i = nCol*nRowSize + nRow; + aCellStates[i] = Occupied; + } + } + + // If at least one cell in either the first column or first row is empty, + // we don't glue at all unless the whole column or row is empty; we expect + // all cells in the first column / row to be fully populated. If we have + // empty column or row, then we do glue by the column or row, + // respectively. + + bool bGlue = true; + bool bGlueCols = false; + for (SCCOL nCol = 0; bGlue && nCol < nColSize; ++nCol) + { + for (SCROW nRow = 0; bGlue && nRow < nRowSize; ++nRow) + { + size_t i = nCol*nRowSize + nRow; + if (aCellStates[i] == Occupied) + { + if (nCol == 0 || nRow == 0) + break; + + bGlue = false; + } + else + aCellStates[i] = Free; + } + size_t nLast = (nCol+1)*nRowSize - 1; // index for the last cell in the column. + if (bGlue && aCellStates[nLast] == Free) + { + // Whole column is empty. + aCellStates[nLast] = Glue; + bGlueCols = true; + } + } + + bool bGlueRows = false; + for (SCROW nRow = 0; bGlue && nRow < nRowSize; ++nRow) + { + size_t i = nRow; + for (SCCOL nCol = 0; bGlue && nCol < nColSize; ++nCol, i += nRowSize) + { + if (aCellStates[i] == Occupied) + { + if (nCol == 0 || nRow == 0) + break; + + bGlue = false; + } + else + aCellStates[i] = Free; + } + i = (nColSize-1)*nRowSize + nRow; // index for the row position in the last column. + if (bGlue && aCellStates[i] == Free) + { + // Whole row is empty. + aCellStates[i] = Glue; + bGlueRows = true; + } + } + + size_t i = 1; + for (sal_uInt32 n = 1; bGlue && n < nCR; ++n, ++i) + if (aCellStates[i] == Hole) + bGlue = false; + + if (bGlue) + { + if (bGlueCols && bGlueRows) + meGlue = GLUETYPE_BOTH; + else if (bGlueRows) + meGlue = GLUETYPE_ROWS; + else + meGlue = GLUETYPE_COLS; + if (aCellStates.front() != Occupied) + mbDummyUpperLeft = true; + } + else + meGlue = GLUETYPE_NONE; +} + +void Chart2Positioner::createPositionMap() +{ + if (meGlue == GLUETYPE_NA && mpPositionMap) + mpPositionMap.reset(); + + if (mpPositionMap) + return; + + glueState(); + + bool bNoGlue = (meGlue == GLUETYPE_NONE); + FormulaTokenMapMap aCols; + SCROW nNoGlueRow = 0; + for (const ScTokenRef& pToken : mrRefTokens) + { + bool bExternal = ScRefTokenHelper::isExternalRef(pToken); + sal_uInt16 nFileId = bExternal ? pToken->GetIndex() : 0; + svl::SharedString aTabName = svl::SharedString::getEmptyString(); + if (bExternal) + aTabName = pToken->GetString(); + + ScComplexRefData aData; + if( !ScRefTokenHelper::getDoubleRefDataFromToken(aData, pToken) ) + break; + const ScSingleRefData& s = aData.Ref1; + const ScSingleRefData& e = aData.Ref2; + SCCOL nCol1 = s.Col(), nCol2 = e.Col(); + SCROW nRow1 = s.Row(), nRow2 = e.Row(); + SCTAB nTab1 = s.Tab(), nTab2 = e.Tab(); + + for (SCTAB nTab = nTab1; nTab <= nTab2; ++nTab) + { + // columns on secondary sheets are appended; we treat them as if + // all columns are on the same sheet. TODO: We can't assume that + // the column range is 16-bit; remove that restriction. + sal_uInt32 nInsCol = (static_cast<sal_uInt32>(nTab) << 16) | + (bNoGlue ? 0 : static_cast<sal_uInt32>(nCol1)); + + for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol, ++nInsCol) + { + FormulaTokenMap& rCol = aCols[nInsCol]; + + auto nInsRow = bNoGlue ? nNoGlueRow : nRow1; + for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow, ++nInsRow) + { + ScSingleRefData aCellData; + aCellData.InitFlags(); + aCellData.SetFlag3D(true); + aCellData.SetColRel(false); + aCellData.SetRowRel(false); + aCellData.SetTabRel(false); + aCellData.SetAbsCol(nCol); + aCellData.SetAbsRow(nRow); + aCellData.SetAbsTab(nTab); + + auto& rCell = rCol[nInsRow]; + if (!rCell) + { + if (bExternal) + rCell.reset(new ScExternalSingleRefToken(nFileId, aTabName, aCellData)); + else + rCell.reset(new ScSingleRefToken(mpDoc->GetSheetLimits(), aCellData)); + } + } + } + } + nNoGlueRow += nRow2 - nRow1 + 1; + } + + bool bFillRowHeader = mbRowHeaders; + bool bFillColumnHeader = mbColHeaders; + + SCSIZE nAllColCount = static_cast<SCSIZE>(aCols.size()); + SCSIZE nAllRowCount = 0; + if (!aCols.empty()) + { + FormulaTokenMap& rCol = aCols.begin()->second; + if (mbDummyUpperLeft) + rCol.try_emplace( 0, nullptr ); // dummy for labeling + nAllRowCount = static_cast<SCSIZE>(rCol.size()); + } + + if( nAllColCount!=0 && nAllRowCount!=0 ) + { + if (bNoGlue) + { + FormulaTokenMap& rFirstCol = aCols.begin()->second; + for (const auto& rFirstColEntry : rFirstCol) + { + SCROW nKey = rFirstColEntry.first; + for (auto& rEntry : aCols) + { + FormulaTokenMap& rCol = rEntry.second; + rCol.try_emplace( nKey, nullptr ); + } + } + } + } + mpPositionMap.reset( + new Chart2PositionMap( + static_cast<SCCOL>(nAllColCount), static_cast<SCROW>(nAllRowCount), + bFillRowHeader, bFillColumnHeader, aCols, mpDoc)); +} + +/** + * Function object to create a range string from a token list. + */ +class Tokens2RangeString +{ +public: + Tokens2RangeString(ScDocument& rDoc, FormulaGrammar::Grammar eGram, sal_Unicode cRangeSep) : + mpRangeStr(std::make_shared<OUStringBuffer>()), + mpDoc(&rDoc), + meGrammar(eGram), + mcRangeSep(cRangeSep), + mbFirst(true) + { + } + + void operator() (const ScTokenRef& rToken) + { + ScCompiler aCompiler(*mpDoc, ScAddress(0,0,0), meGrammar); + OUString aStr; + aCompiler.CreateStringFromToken(aStr, rToken.get()); + if (mbFirst) + mbFirst = false; + else + mpRangeStr->append(mcRangeSep); + mpRangeStr->append(aStr); + } + + void getString(OUString& rStr) + { + rStr = mpRangeStr->makeStringAndClear(); + } + +private: + shared_ptr<OUStringBuffer> mpRangeStr; + ScDocument* mpDoc; + FormulaGrammar::Grammar meGrammar; + sal_Unicode mcRangeSep; + bool mbFirst; +}; + +/** + * Function object to convert a list of tokens into a string form suitable + * for ODF export. In ODF, a range is expressed as + * + * (start cell address):(end cell address) + * + * and each address doesn't include any '$' symbols. + */ +class Tokens2RangeStringXML +{ +public: + explicit Tokens2RangeStringXML(ScDocument& rDoc) : + mpRangeStr(std::make_shared<OUStringBuffer>()), + mpDoc(&rDoc), + mbFirst(true) + { + } + + void operator() (const ScTokenRef& rToken) + { + if (mbFirst) + mbFirst = false; + else + mpRangeStr->append(mcRangeSep); + + ScTokenRef aStart, aEnd; + bool bValidToken = splitRangeToken(*mpDoc, rToken, aStart, aEnd); + // Check there is a valid reference in named range + if (!bValidToken && rToken->GetType() == svIndex && rToken->GetOpCode() == ocName) + { + ScRangeData* pNameRange = mpDoc->FindRangeNameBySheetAndIndex(rToken->GetSheet(), rToken->GetIndex()); + if (pNameRange->HasReferences()) + { + const ScTokenRef aTempToken = pNameRange->GetCode()->FirstToken(); + bValidToken = splitRangeToken(*mpDoc, aTempToken, aStart, aEnd); + } + } + + OSL_ENSURE(bValidToken, "invalid token"); + if (!bValidToken) + return; + + ScCompiler aCompiler(*mpDoc, ScAddress(0,0,0), FormulaGrammar::GRAM_ENGLISH); + { + OUString aStr; + aCompiler.CreateStringFromToken(aStr, aStart.get()); + mpRangeStr->append(aStr); + } + mpRangeStr->append(mcAddrSep); + { + OUString aStr; + aCompiler.CreateStringFromToken(aStr, aEnd.get()); + mpRangeStr->append(aStr); + } + } + + void getString(OUString& rStr) + { + rStr = mpRangeStr->makeStringAndClear(); + } + +private: + static bool splitRangeToken(const ScDocument& rDoc, const ScTokenRef& pToken, ScTokenRef& rStart, ScTokenRef& rEnd) + { + ScComplexRefData aData; + bool bIsRefToken = ScRefTokenHelper::getDoubleRefDataFromToken(aData, pToken); + OSL_ENSURE(bIsRefToken, "invalid token"); + if (!bIsRefToken) + return false; + bool bExternal = ScRefTokenHelper::isExternalRef(pToken); + sal_uInt16 nFileId = bExternal ? pToken->GetIndex() : 0; + svl::SharedString aTabName = svl::SharedString::getEmptyString(); + if (bExternal) + aTabName = pToken->GetString(); + + // In saving to XML, we don't prepend address with '$'. + setRelative(aData.Ref1); + setRelative(aData.Ref2); + + // In XML, the range must explicitly specify sheet name. + aData.Ref1.SetFlag3D(true); + aData.Ref2.SetFlag3D(true); + + if (bExternal) + rStart.reset(new ScExternalSingleRefToken(nFileId, aTabName, aData.Ref1)); + else + rStart.reset(new ScSingleRefToken(rDoc.GetSheetLimits(), aData.Ref1)); + + if (bExternal) + rEnd.reset(new ScExternalSingleRefToken(nFileId, aTabName, aData.Ref2)); + else + rEnd.reset(new ScSingleRefToken(rDoc.GetSheetLimits(), aData.Ref2)); + return true; + } + + static void setRelative(ScSingleRefData& rData) + { + rData.SetColRel(true); + rData.SetRowRel(true); + rData.SetTabRel(true); + } + +private: + shared_ptr<OUStringBuffer> mpRangeStr; + ScDocument* mpDoc; + static const sal_Unicode mcRangeSep = ' '; + static const sal_Unicode mcAddrSep = ':'; + bool mbFirst; +}; + +void lcl_convertTokensToString(OUString& rStr, const vector<ScTokenRef>& rTokens, ScDocument& rDoc) +{ + const sal_Unicode cRangeSep = ScCompiler::GetNativeSymbolChar(ocSep); + FormulaGrammar::Grammar eGrammar = rDoc.GetGrammar(); + Tokens2RangeString func(rDoc, eGrammar, cRangeSep); + func = ::std::for_each(rTokens.begin(), rTokens.end(), func); + func.getString(rStr); +} + +} // anonymous namespace + +// DataProvider ============================================================== + +ScChart2DataProvider::ScChart2DataProvider( ScDocument* pDoc ) + : m_pDocument( pDoc) + , m_aPropSet(lcl_GetDataProviderPropertyMap()) + , m_bIncludeHiddenCells( true) +{ + if ( m_pDocument ) + m_pDocument->AddUnoObject( *this); +} + +ScChart2DataProvider::~ScChart2DataProvider() +{ + SolarMutexGuard g; + + if ( m_pDocument ) + m_pDocument->RemoveUnoObject( *this); +} + +void ScChart2DataProvider::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint) +{ + if ( rHint.GetId() == SfxHintId::Dying ) + { + m_pDocument = nullptr; + } +} + +sal_Bool SAL_CALL ScChart2DataProvider::createDataSourcePossible( const uno::Sequence< beans::PropertyValue >& aArguments ) +{ + SolarMutexGuard aGuard; + if( ! m_pDocument ) + return false; + + OUString aRangeRepresentation; + for(const auto& rArgument : aArguments) + { + if ( rArgument.Name == "CellRangeRepresentation" ) + { + rArgument.Value >>= aRangeRepresentation; + } + } + + vector<ScTokenRef> aTokens; + const sal_Unicode cSep = ScCompiler::GetNativeSymbolChar(ocSep); + ScRefTokenHelper::compileRangeRepresentation( + aTokens, aRangeRepresentation, *m_pDocument, cSep, m_pDocument->GetGrammar(), true); + return !aTokens.empty(); +} + +namespace +{ + +uno::Reference< chart2::data::XLabeledDataSequence > lcl_createLabeledDataSequenceFromTokens( + vector< ScTokenRef > && aValueTokens, vector< ScTokenRef > && aLabelTokens, + ScDocument* pDoc, bool bIncludeHiddenCells ) +{ + uno::Reference< chart2::data::XLabeledDataSequence > xResult; + bool bHasValues = !aValueTokens.empty(); + bool bHasLabel = !aLabelTokens.empty(); + if( bHasValues || bHasLabel ) + { + try + { + uno::Reference< uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() ); + if ( xContext.is() ) + { + xResult.set( chart2::data::LabeledDataSequence::create(xContext), uno::UNO_QUERY_THROW ); + } + if ( bHasValues ) + { + uno::Reference< chart2::data::XDataSequence > xSeq( new ScChart2DataSequence( pDoc, std::move(aValueTokens), bIncludeHiddenCells ) ); + xResult->setValues( xSeq ); + } + if ( bHasLabel ) + { + //Labels should always include hidden cells, regardless of the bIncludeHiddenCells setting + uno::Reference< chart2::data::XDataSequence > xLabelSeq( new ScChart2DataSequence( pDoc, std::move(aLabelTokens), true ) ); + xResult->setLabel( xLabelSeq ); + } + } + catch( const uno::Exception& ) + { + } + } + return xResult; +} + +/** + * Check the current list of reference tokens, and add the upper left + * corner of the minimum range that encloses all ranges if certain + * conditions are met. + * + * @param rRefTokens list of reference tokens + * + * @return true if the corner was added, false otherwise. + */ +bool lcl_addUpperLeftCornerIfMissing(const ScDocument* pDoc, vector<ScTokenRef>& rRefTokens, + SCROW nCornerRowCount, SCCOL nCornerColumnCount) +{ + using ::std::max; + using ::std::min; + + if (rRefTokens.empty()) + return false; + + SCCOL nMinCol = pDoc->GetSheetLimits().GetMaxColCount(); + SCROW nMinRow = pDoc->GetSheetLimits().GetMaxRowCount(); + SCCOL nMaxCol = 0; + SCROW nMaxRow = 0; + SCTAB nTab = 0; + + sal_uInt16 nFileId = 0; + svl::SharedString aExtTabName; + bool bExternal = false; + + vector<ScTokenRef>::const_iterator itr = rRefTokens.begin(), itrEnd = rRefTokens.end(); + + // Get the first ref token. + ScTokenRef pToken = *itr; + switch (pToken->GetType()) + { + case svSingleRef: + { + const ScSingleRefData& rData = *pToken->GetSingleRef(); + nMinCol = rData.Col(); + nMinRow = rData.Row(); + nMaxCol = rData.Col(); + nMaxRow = rData.Row(); + nTab = rData.Tab(); + } + break; + case svDoubleRef: + { + const ScComplexRefData& rData = *pToken->GetDoubleRef(); + nMinCol = min(rData.Ref1.Col(), rData.Ref2.Col()); + nMinRow = min(rData.Ref1.Row(), rData.Ref2.Row()); + nMaxCol = max(rData.Ref1.Col(), rData.Ref2.Col()); + nMaxRow = max(rData.Ref1.Row(), rData.Ref2.Row()); + nTab = rData.Ref1.Tab(); + } + break; + case svExternalSingleRef: + { + const ScSingleRefData& rData = *pToken->GetSingleRef(); + nMinCol = rData.Col(); + nMinRow = rData.Row(); + nMaxCol = rData.Col(); + nMaxRow = rData.Row(); + nTab = rData.Tab(); + nFileId = pToken->GetIndex(); + aExtTabName = pToken->GetString(); + bExternal = true; + } + break; + case svExternalDoubleRef: + { + const ScComplexRefData& rData = *pToken->GetDoubleRef(); + nMinCol = min(rData.Ref1.Col(), rData.Ref2.Col()); + nMinRow = min(rData.Ref1.Row(), rData.Ref2.Row()); + nMaxCol = max(rData.Ref1.Col(), rData.Ref2.Col()); + nMaxRow = max(rData.Ref1.Row(), rData.Ref2.Row()); + nTab = rData.Ref1.Tab(); + nFileId = pToken->GetIndex(); + aExtTabName = pToken->GetString(); + bExternal = true; + } + break; + default: + ; + } + + // Determine the minimum range enclosing all data ranges. Also make sure + // that they are all on the same table. + + for (++itr; itr != itrEnd; ++itr) + { + pToken = *itr; + switch (pToken->GetType()) + { + case svSingleRef: + { + const ScSingleRefData& rData = *pToken->GetSingleRef(); + + nMinCol = min(nMinCol, rData.Col()); + nMinRow = min(nMinRow, rData.Row()); + nMaxCol = max(nMaxCol, rData.Col()); + nMaxRow = max(nMaxRow, rData.Row()); + if (nTab != rData.Tab() || bExternal) + return false; + } + break; + case svDoubleRef: + { + const ScComplexRefData& rData = *pToken->GetDoubleRef(); + + nMinCol = min(nMinCol, rData.Ref1.Col()); + nMinCol = min(nMinCol, rData.Ref2.Col()); + nMinRow = min(nMinRow, rData.Ref1.Row()); + nMinRow = min(nMinRow, rData.Ref2.Row()); + + nMaxCol = max(nMaxCol, rData.Ref1.Col()); + nMaxCol = max(nMaxCol, rData.Ref2.Col()); + nMaxRow = max(nMaxRow, rData.Ref1.Row()); + nMaxRow = max(nMaxRow, rData.Ref2.Row()); + + if (nTab != rData.Ref1.Tab() || bExternal) + return false; + } + break; + case svExternalSingleRef: + { + if (!bExternal) + return false; + + if (nFileId != pToken->GetIndex() || aExtTabName != pToken->GetString()) + return false; + + const ScSingleRefData& rData = *pToken->GetSingleRef(); + + nMinCol = min(nMinCol, rData.Col()); + nMinRow = min(nMinRow, rData.Row()); + nMaxCol = max(nMaxCol, rData.Col()); + nMaxRow = max(nMaxRow, rData.Row()); + } + break; + case svExternalDoubleRef: + { + if (!bExternal) + return false; + + if (nFileId != pToken->GetIndex() || aExtTabName != pToken->GetString()) + return false; + + const ScComplexRefData& rData = *pToken->GetDoubleRef(); + + nMinCol = min(nMinCol, rData.Ref1.Col()); + nMinCol = min(nMinCol, rData.Ref2.Col()); + nMinRow = min(nMinRow, rData.Ref1.Row()); + nMinRow = min(nMinRow, rData.Ref2.Row()); + + nMaxCol = max(nMaxCol, rData.Ref1.Col()); + nMaxCol = max(nMaxCol, rData.Ref2.Col()); + nMaxRow = max(nMaxRow, rData.Ref1.Row()); + nMaxRow = max(nMaxRow, rData.Ref2.Row()); + } + break; + default: + ; + } + } + + const auto & rSheetLimits = pDoc->GetSheetLimits(); + if (nMinRow >= nMaxRow || nMinCol >= nMaxCol || + nMinRow >= rSheetLimits.GetMaxRowCount() || nMinCol >= rSheetLimits.GetMaxColCount() || + nMaxRow >= rSheetLimits.GetMaxRowCount() || nMaxCol >= rSheetLimits.GetMaxColCount()) + { + // Invalid range. Bail out. + return false; + } + + // Check if the following conditions are met: + + // 1) The upper-left corner cell is not included. + // 2) The three adjacent cells of that corner cell are included. + + bool bRight = false, bBottom = false, bDiagonal = false; + for (const auto& rxToken : rRefTokens) + { + switch (rxToken->GetType()) + { + case svSingleRef: + case svExternalSingleRef: + { + const ScSingleRefData& rData = *rxToken->GetSingleRef(); + if (rData.Col() == nMinCol && rData.Row() == nMinRow) + // The corner cell is contained. + return false; + + if (rData.Col() == nMinCol+nCornerColumnCount && rData.Row() == nMinRow) + bRight = true; + + if (rData.Col() == nMinCol && rData.Row() == nMinRow+nCornerRowCount) + bBottom = true; + + if (rData.Col() == nMinCol+nCornerColumnCount && rData.Row() == nMinRow+nCornerRowCount) + bDiagonal = true; + } + break; + case svDoubleRef: + case svExternalDoubleRef: + { + const ScComplexRefData& rData = *rxToken->GetDoubleRef(); + const ScSingleRefData& r1 = rData.Ref1; + const ScSingleRefData& r2 = rData.Ref2; + if (r1.Col() <= nMinCol && nMinCol <= r2.Col() && + r1.Row() <= nMinRow && nMinRow <= r2.Row()) + // The corner cell is contained. + return false; + + if (r1.Col() <= nMinCol+nCornerColumnCount && nMinCol+nCornerColumnCount <= r2.Col() && + r1.Row() <= nMinRow && nMinRow <= r2.Row()) + bRight = true; + + if (r1.Col() <= nMinCol && nMinCol <= r2.Col() && + r1.Row() <= nMinRow+nCornerRowCount && nMinRow+nCornerRowCount <= r2.Row()) + bBottom = true; + + if (r1.Col() <= nMinCol+nCornerColumnCount && nMinCol+nCornerColumnCount <= r2.Col() && + r1.Row() <= nMinRow+nCornerRowCount && nMinRow+nCornerRowCount <= r2.Row()) + bDiagonal = true; + } + break; + default: + ; + } + } + + if (!bRight || !bBottom || !bDiagonal) + // Not all the adjacent cells are included. Bail out. + return false; + + ScSingleRefData aData; + aData.InitFlags(); + aData.SetFlag3D(true); + aData.SetAbsCol(nMinCol); + aData.SetAbsRow(nMinRow); + aData.SetAbsTab(nTab); + + if( nCornerRowCount==1 && nCornerColumnCount==1 ) + { + if (bExternal) + { + ScTokenRef pCorner( + new ScExternalSingleRefToken(nFileId, aExtTabName, aData)); + ScRefTokenHelper::join(pDoc, rRefTokens, pCorner, ScAddress()); + } + else + { + ScTokenRef pCorner(new ScSingleRefToken(pDoc->GetSheetLimits(), aData)); + ScRefTokenHelper::join(pDoc, rRefTokens, pCorner, ScAddress()); + } + } + else + { + ScSingleRefData aDataEnd(aData); + aDataEnd.IncCol(nCornerColumnCount-1); + aDataEnd.IncRow(nCornerRowCount-1); + ScComplexRefData r; + r.Ref1=aData; + r.Ref2=aDataEnd; + if (bExternal) + { + ScTokenRef pCorner( + new ScExternalDoubleRefToken(nFileId, aExtTabName, r)); + ScRefTokenHelper::join(pDoc, rRefTokens, pCorner, ScAddress()); + } + else + { + ScTokenRef pCorner(new ScDoubleRefToken(pDoc->GetSheetLimits(), r)); + ScRefTokenHelper::join(pDoc, rRefTokens, pCorner, ScAddress()); + } + } + + return true; +} + +#define SHRINK_RANGE_THRESHOLD 10000 + +class ShrinkRefTokenToDataRange +{ + ScDocument* mpDoc; +public: + explicit ShrinkRefTokenToDataRange(ScDocument* pDoc) : mpDoc(pDoc) {} + void operator() (const ScTokenRef& rRef) + { + if (ScRefTokenHelper::isExternalRef(rRef)) + return; + + // Don't assume an ScDoubleRefToken if it isn't. It can be at least an + // ScSingleRefToken, then there isn't anything to shrink. + if (rRef->GetType() != svDoubleRef) + return; + + ScComplexRefData& rData = *rRef->GetDoubleRef(); + ScSingleRefData& s = rData.Ref1; + ScSingleRefData& e = rData.Ref2; + + if(abs((e.Col()-s.Col())*(e.Row()-s.Row())) < SHRINK_RANGE_THRESHOLD) + return; + + SCCOL nMinCol = mpDoc->MaxCol(), nMaxCol = 0; + SCROW nMinRow = mpDoc->MaxRow(), nMaxRow = 0; + + // Determine the smallest range that encompasses the data ranges of all sheets. + SCTAB nTab1 = s.Tab(), nTab2 = e.Tab(); + for (SCTAB nTab = nTab1; nTab <= nTab2; ++nTab) + { + SCCOL nCol1 = 0, nCol2 = mpDoc->MaxCol(); + SCROW nRow1 = 0, nRow2 = mpDoc->MaxRow(); + mpDoc->ShrinkToDataArea(nTab, nCol1, nRow1, nCol2, nRow2); + nMinCol = std::min(nMinCol, nCol1); + nMinRow = std::min(nMinRow, nRow1); + nMaxCol = std::max(nMaxCol, nCol2); + nMaxRow = std::max(nMaxRow, nRow2); + } + + // Shrink range to the data range if applicable. + if (s.Col() < nMinCol) + s.SetAbsCol(nMinCol); + if (s.Row() < nMinRow) + s.SetAbsRow(nMinRow); + if (e.Col() > nMaxCol) + e.SetAbsCol(nMaxCol); + if (e.Row() > nMaxRow) + e.SetAbsRow(nMaxRow); + } +}; + +void shrinkToDataRange(ScDocument* pDoc, vector<ScTokenRef>& rRefTokens) +{ + std::for_each(rRefTokens.begin(), rRefTokens.end(), ShrinkRefTokenToDataRange(pDoc)); +} + +} + +uno::Reference< chart2::data::XDataSource> SAL_CALL +ScChart2DataProvider::createDataSource( + const uno::Sequence< beans::PropertyValue >& aArguments ) +{ + SolarMutexGuard aGuard; + if ( ! m_pDocument ) + throw uno::RuntimeException(); + + uno::Reference< chart2::data::XDataSource> xResult; + bool bLabel = true; + bool bCategories = false; + bool bOrientCol = true; + OUString aRangeRepresentation; + uno::Sequence< sal_Int32 > aSequenceMapping; + bool bTimeBased = false; + for(const auto& rArgument : aArguments) + { + if ( rArgument.Name == "DataRowSource" ) + { + chart::ChartDataRowSource eSource = chart::ChartDataRowSource_COLUMNS; + if( ! (rArgument.Value >>= eSource)) + { + sal_Int32 nSource(0); + if( rArgument.Value >>= nSource ) + eSource = static_cast< chart::ChartDataRowSource >( nSource ); + } + bOrientCol = (eSource == chart::ChartDataRowSource_COLUMNS); + } + else if ( rArgument.Name == "FirstCellAsLabel" ) + { + bLabel = ::cppu::any2bool(rArgument.Value); + } + else if ( rArgument.Name == "HasCategories" ) + { + bCategories = ::cppu::any2bool(rArgument.Value); + } + else if ( rArgument.Name == "CellRangeRepresentation" ) + { + rArgument.Value >>= aRangeRepresentation; + } + else if ( rArgument.Name == "SequenceMapping" ) + { + rArgument.Value >>= aSequenceMapping; + } + else if ( rArgument.Name == "TimeBased" ) + { + rArgument.Value >>= bTimeBased; + } + } + + vector<ScTokenRef> aRefTokens; + const sal_Unicode cSep = ScCompiler::GetNativeSymbolChar(ocSep); + ScRefTokenHelper::compileRangeRepresentation( + aRefTokens, aRangeRepresentation, *m_pDocument, cSep, m_pDocument->GetGrammar(), true); + if (aRefTokens.empty()) + // Invalid range representation. Bail out. + throw lang::IllegalArgumentException(); + + SCTAB nTimeBasedStart = MAXTAB; + SCTAB nTimeBasedEnd = 0; + if(bTimeBased) + { + // limit to first sheet + for(const auto& rxToken : aRefTokens) + { + if (rxToken->GetType() != svDoubleRef) + continue; + + ScComplexRefData& rData = *rxToken->GetDoubleRef(); + ScSingleRefData& s = rData.Ref1; + ScSingleRefData& e = rData.Ref2; + + nTimeBasedStart = std::min(nTimeBasedStart, s.Tab()); + nTimeBasedEnd = std::min(nTimeBasedEnd, e.Tab()); + + if(s.Tab() != e.Tab()) + e.SetAbsTab(s.Tab()); + } + } + + if(!bTimeBased) + shrinkToDataRange(m_pDocument, aRefTokens); + + if (bLabel) + lcl_addUpperLeftCornerIfMissing(m_pDocument, aRefTokens, 1, 1); //#i90669# + + bool bColHeaders = (bOrientCol ? bLabel : bCategories ); + bool bRowHeaders = (bOrientCol ? bCategories : bLabel ); + + Chart2Positioner aChPositioner(m_pDocument, aRefTokens); + aChPositioner.setHeaders(bColHeaders, bRowHeaders); + + const Chart2PositionMap* pChartMap = aChPositioner.getPositionMap(); + if (!pChartMap) + // No chart position map instance. Bail out. + return xResult; + + rtl::Reference<ScChart2DataSource> pDS; + ::std::vector< uno::Reference< chart2::data::XLabeledDataSequence > > aSeqs; + + // Fill Categories + if( bCategories ) + { + vector<ScTokenRef> aValueTokens; + if (bOrientCol) + aValueTokens = pChartMap->getAllRowHeaderRanges(); + else + aValueTokens = pChartMap->getAllColHeaderRanges(); + + vector<ScTokenRef> aLabelTokens( + pChartMap->getLeftUpperCornerRanges()); + + uno::Reference< chart2::data::XLabeledDataSequence > xCategories = lcl_createLabeledDataSequenceFromTokens( + std::move(aValueTokens), std::move(aLabelTokens), m_pDocument, m_bIncludeHiddenCells ); //ownership of pointers is transferred! + if ( xCategories.is() ) + { + aSeqs.push_back( xCategories ); + } + } + + // Fill Series (values and label) + sal_Int32 nCount = bOrientCol ? pChartMap->getDataColCount() : pChartMap->getDataRowCount(); + for (sal_Int32 i = 0; i < nCount; ++i) + { + vector<ScTokenRef> aValueTokens; + vector<ScTokenRef> aLabelTokens; + if (bOrientCol) + { + aValueTokens = pChartMap->getDataColRanges(static_cast<SCCOL>(i)); + aLabelTokens = pChartMap->getColHeaderRanges(static_cast<SCCOL>(i)); + } + else + { + aValueTokens = pChartMap->getDataRowRanges(static_cast<SCROW>(i)); + aLabelTokens = pChartMap->getRowHeaderRanges(static_cast<SCROW>(i)); + } + uno::Reference< chart2::data::XLabeledDataSequence > xChartSeries = lcl_createLabeledDataSequenceFromTokens( + std::move(aValueTokens), std::move(aLabelTokens), m_pDocument, m_bIncludeHiddenCells ); //ownership of pointers is transferred! + if ( xChartSeries.is() && xChartSeries->getValues().is() && xChartSeries->getValues()->getData().hasElements() ) + { + aSeqs.push_back( xChartSeries ); + } + } + + pDS = new ScChart2DataSource(m_pDocument); + + //reorder labeled sequences according to aSequenceMapping + ::std::vector< uno::Reference< chart2::data::XLabeledDataSequence > > aSeqVector; + aSeqVector.reserve(aSeqs.size()); + for (auto const& aSeq : aSeqs) + { + aSeqVector.push_back(aSeq); + } + + for( const sal_Int32 nNewIndex : std::as_const(aSequenceMapping) ) + { + // note: assuming that the values in the sequence mapping are always non-negative + ::std::vector< uno::Reference< chart2::data::XLabeledDataSequence > >::size_type nOldIndex( static_cast< sal_uInt32 >( nNewIndex ) ); + if( nOldIndex < aSeqVector.size() ) + { + pDS->AddLabeledSequence( aSeqVector[nOldIndex] ); + aSeqVector[nOldIndex] = nullptr; + } + } + + for(const uno::Reference< chart2::data::XLabeledDataSequence >& xSeq : aSeqVector) + { + if ( xSeq.is() ) + { + pDS->AddLabeledSequence( xSeq ); + } + } + + xResult.set( pDS ); + return xResult; +} + +namespace +{ + +/** + * Function object to create a list of table numbers from a token list. + */ +class InsertTabNumber +{ +public: + InsertTabNumber() : + mpTabNumVector(std::make_shared<vector<SCTAB>>()) + { + } + + void operator() (const ScTokenRef& pToken) const + { + if (!ScRefTokenHelper::isRef(pToken)) + return; + + const ScSingleRefData& r = *pToken->GetSingleRef(); + mpTabNumVector->push_back(r.Tab()); + } + + void getVector(vector<SCTAB>& rVector) + { + mpTabNumVector->swap(rVector); + } +private: + shared_ptr< vector<SCTAB> > mpTabNumVector; +}; + +class RangeAnalyzer +{ +public: + RangeAnalyzer(); + void initRangeAnalyzer( const ScDocument* pDoc, const vector<ScTokenRef>& rTokens ); + void analyzeRange( sal_Int32& rnDataInRows, sal_Int32& rnDataInCols, + bool& rbRowSourceAmbiguous ) const; + bool inSameSingleRow( const RangeAnalyzer& rOther ); + bool inSameSingleColumn( const RangeAnalyzer& rOther ); + SCROW getRowCount() const { return mnRowCount; } + SCCOL getColumnCount() const { return mnColumnCount; } + +private: + bool mbEmpty; + bool mbAmbiguous; + SCROW mnRowCount; + SCCOL mnColumnCount; + + SCCOL mnStartColumn; + SCROW mnStartRow; +}; + +RangeAnalyzer::RangeAnalyzer() + : mbEmpty(true) + , mbAmbiguous(false) + , mnRowCount(0) + , mnColumnCount(0) + , mnStartColumn(-1) + , mnStartRow(-1) +{ +} + +void RangeAnalyzer::initRangeAnalyzer( const ScDocument* pDoc, const vector<ScTokenRef>& rTokens ) +{ + mnRowCount=0; + mnColumnCount=0; + mnStartColumn = -1; + mnStartRow = -1; + mbAmbiguous=false; + if( rTokens.empty() ) + { + mbEmpty=true; + return; + } + mbEmpty=false; + + for (const ScTokenRef& aRefToken : rTokens) + { + StackVar eVar = aRefToken->GetType(); + if (eVar == svDoubleRef || eVar == svExternalDoubleRef) + { + const ScComplexRefData& r = *aRefToken->GetDoubleRef(); + if (r.Ref1.Tab() == r.Ref2.Tab()) + { + mnColumnCount = std::max<SCCOL>(mnColumnCount, static_cast<SCCOL>(abs(r.Ref2.Col() - r.Ref1.Col())+1)); + mnRowCount = std::max<SCROW>(mnRowCount, static_cast<SCROW>(abs(r.Ref2.Row() - r.Ref1.Row())+1)); + if( mnStartColumn == -1 ) + { + mnStartColumn = r.Ref1.Col(); + mnStartRow = r.Ref1.Row(); + } + else + { + if (mnStartColumn != r.Ref1.Col() && mnStartRow != r.Ref1.Row()) + mbAmbiguous=true; + } + } + else + mbAmbiguous=true; + } + else if (eVar == svSingleRef || eVar == svExternalSingleRef) + { + const ScSingleRefData& r = *aRefToken->GetSingleRef(); + mnColumnCount = std::max<SCCOL>( mnColumnCount, 1); + mnRowCount = std::max<SCROW>( mnRowCount, 1); + if( mnStartColumn == -1 ) + { + mnStartColumn = r.Col(); + mnStartRow = r.Row(); + } + else + { + if (mnStartColumn != r.Col() && mnStartRow != r.Row()) + mbAmbiguous=true; + } + } + else if (eVar == svIndex && aRefToken->GetOpCode() == ocName) + { + ScRangeData* pNameRange = pDoc->FindRangeNameBySheetAndIndex(aRefToken->GetSheet(), aRefToken->GetIndex()); + ScRange aRange; + if (pNameRange->IsReference(aRange)) + { + mnColumnCount = std::max<SCCOL>(mnColumnCount, static_cast<SCCOL>(abs(aRange.aEnd.Col() - aRange.aStart.Col()) + 1)); + mnRowCount = std::max<SCROW>(mnRowCount, static_cast<SCROW>(abs(aRange.aEnd.Row() - aRange.aStart.Row()) + 1)); + if (mnStartColumn == -1) + { + mnStartColumn = aRange.aStart.Col(); + mnStartRow = aRange.aStart.Row(); + } + else + { + if (mnStartColumn != aRange.aStart.Col() && mnStartRow != aRange.aStart.Row()) + mbAmbiguous = true; + } + } + else + mbAmbiguous = true; + } + else + mbAmbiguous=true; + } +} + +void RangeAnalyzer::analyzeRange( sal_Int32& rnDataInRows, + sal_Int32& rnDataInCols, + bool& rbRowSourceAmbiguous ) const +{ + if(!mbEmpty && !mbAmbiguous) + { + if( mnRowCount==1 && mnColumnCount>1 ) + ++rnDataInRows; + else if( mnColumnCount==1 && mnRowCount>1 ) + ++rnDataInCols; + else if( mnRowCount>1 && mnColumnCount>1 ) + rbRowSourceAmbiguous = true; + } + else if( !mbEmpty ) + rbRowSourceAmbiguous = true; +} + +bool RangeAnalyzer::inSameSingleRow( const RangeAnalyzer& rOther ) +{ + return mnStartRow==rOther.mnStartRow && + mnRowCount==1 && rOther.mnRowCount==1; +} + +bool RangeAnalyzer::inSameSingleColumn( const RangeAnalyzer& rOther ) +{ + return mnStartColumn==rOther.mnStartColumn && + mnColumnCount==1 && rOther.mnColumnCount==1; +} + +std::pair<OUString, OUString> constructKey(const uno::Reference< chart2::data::XLabeledDataSequence>& xNew) +{ + std::pair<OUString, OUString> aKey; + if( xNew->getLabel().is() ) + aKey.first = xNew->getLabel()->getSourceRangeRepresentation(); + if( xNew->getValues().is() ) + aKey.second = xNew->getValues()->getSourceRangeRepresentation(); + return aKey; +} + + +} //end anonymous namespace + +uno::Sequence< beans::PropertyValue > SAL_CALL ScChart2DataProvider::detectArguments( + const uno::Reference< chart2::data::XDataSource >& xDataSource ) +{ + ::std::vector< beans::PropertyValue > aResult; + bool bRowSourceDetected = false; + bool bFirstCellAsLabel = false; + bool bHasCategories = false; + OUString sRangeRep; + + bool bHasCategoriesLabels = false; + vector<ScTokenRef> aAllCategoriesValuesTokens; + vector<ScTokenRef> aAllSeriesLabelTokens; + + chart::ChartDataRowSource eRowSource = chart::ChartDataRowSource_COLUMNS; + + vector<ScTokenRef> aAllTokens; + + // parse given data source and collect infos + { + SolarMutexGuard aGuard; + OSL_ENSURE( m_pDocument, "No Document -> no detectArguments" ); + if(!m_pDocument ||!xDataSource.is()) + return comphelper::containerToSequence( aResult ); + + sal_Int32 nDataInRows = 0; + sal_Int32 nDataInCols = 0; + bool bRowSourceAmbiguous = false; + + const Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > aSequences( xDataSource->getDataSequences()); + const sal_Int32 nCount( aSequences.getLength()); + RangeAnalyzer aPrevLabel,aPrevValues; + for( const uno::Reference< chart2::data::XLabeledDataSequence >& xLS : aSequences ) + { + if( xLS.is() ) + { + bool bThisIsCategories = false; + if(!bHasCategories) + { + uno::Reference< beans::XPropertySet > xSeqProp( xLS->getValues(), uno::UNO_QUERY ); + OUString aRole; + if( xSeqProp.is() && (xSeqProp->getPropertyValue("Role") >>= aRole) && + aRole == "categories" ) + bThisIsCategories = bHasCategories = true; + } + + RangeAnalyzer aLabel,aValues; + // label + uno::Reference< chart2::data::XDataSequence > xLabel( xLS->getLabel()); + if( xLabel.is()) + { + bFirstCellAsLabel = true; + vector<ScTokenRef> aTokens; + const sal_Unicode cSep = ScCompiler::GetNativeSymbolChar(ocSep); + ScRefTokenHelper::compileRangeRepresentation( + aTokens, xLabel->getSourceRangeRepresentation(), *m_pDocument, cSep, m_pDocument->GetGrammar(), true); + aLabel.initRangeAnalyzer(m_pDocument, aTokens); + for (const auto& rxToken : aTokens) + { + if (rxToken->GetType() == svIndex && rxToken->GetOpCode() == ocName) + { + ScRangeData* pNameRange = m_pDocument->FindRangeNameBySheetAndIndex(rxToken->GetSheet(), rxToken->GetIndex()); + if (pNameRange->HasReferences()) + { + const ScTokenRef aTempToken = pNameRange->GetCode()->FirstToken(); + ScRefTokenHelper::join(m_pDocument, aAllTokens, aTempToken, ScAddress()); + } + else + ScRefTokenHelper::join(m_pDocument, aAllTokens, rxToken, ScAddress()); + } + else + ScRefTokenHelper::join(m_pDocument, aAllTokens, rxToken, ScAddress()); + if(!bThisIsCategories) + ScRefTokenHelper::join(m_pDocument, aAllSeriesLabelTokens, rxToken, ScAddress()); + } + if(bThisIsCategories) + bHasCategoriesLabels=true; + } + // values + uno::Reference< chart2::data::XDataSequence > xValues( xLS->getValues()); + if( xValues.is()) + { + vector<ScTokenRef> aTokens; + const sal_Unicode cSep = ScCompiler::GetNativeSymbolChar(ocSep); + ScRefTokenHelper::compileRangeRepresentation( + aTokens, xValues->getSourceRangeRepresentation(), *m_pDocument, cSep, m_pDocument->GetGrammar(), true); + aValues.initRangeAnalyzer(m_pDocument, aTokens); + for (const auto& rxToken : aTokens) + { + if (rxToken->GetType() == svIndex && rxToken->GetOpCode() == ocName) + { + ScRangeData* pNameRange = m_pDocument->FindRangeNameBySheetAndIndex(rxToken->GetSheet(), rxToken->GetIndex()); + if (pNameRange->HasReferences()) + { + const ScTokenRef aTempToken = pNameRange->GetCode()->FirstToken(); + ScRefTokenHelper::join(m_pDocument, aAllTokens, aTempToken, ScAddress()); + } + else + ScRefTokenHelper::join(m_pDocument, aAllTokens, rxToken, ScAddress()); + } + else + ScRefTokenHelper::join(m_pDocument, aAllTokens, rxToken, ScAddress()); + if(bThisIsCategories) + ScRefTokenHelper::join(m_pDocument, aAllCategoriesValuesTokens, rxToken, ScAddress()); + } + } + //detect row source + if(!bThisIsCategories || nCount==1) //categories might span multiple rows *and* columns, so they should be used for detection only if nothing else is available + { + if (!bRowSourceAmbiguous) + { + aValues.analyzeRange(nDataInRows,nDataInCols,bRowSourceAmbiguous); + aLabel.analyzeRange(nDataInRows,nDataInCols,bRowSourceAmbiguous); + if (nDataInRows > 1 && nDataInCols > 1) + bRowSourceAmbiguous = true; + else if( !bRowSourceAmbiguous && !nDataInRows && !nDataInCols ) + { + if( aValues.inSameSingleColumn( aLabel ) ) + nDataInCols++; + else if( aValues.inSameSingleRow( aLabel ) ) + nDataInRows++; + else + { + //#i86188# also detect a single column split into rows correctly + if( aValues.inSameSingleColumn( aPrevValues ) ) + nDataInRows++; + else if( aValues.inSameSingleRow( aPrevValues ) ) + nDataInCols++; + else if( aLabel.inSameSingleColumn( aPrevLabel ) ) + nDataInRows++; + else if( aLabel.inSameSingleRow( aPrevLabel ) ) + nDataInCols++; + } + } + } + } + aPrevValues=aValues; + aPrevLabel=aLabel; + } + } + + if (!bRowSourceAmbiguous) + { + bRowSourceDetected = true; + eRowSource = ( nDataInCols > 0 + ? chart::ChartDataRowSource_COLUMNS + : chart::ChartDataRowSource_ROWS ); + } + else + { + // set DataRowSource to the better of the two ambiguities + eRowSource = ( nDataInRows > nDataInCols + ? chart::ChartDataRowSource_ROWS + : chart::ChartDataRowSource_COLUMNS ); + } + + } + + // TableNumberList + { + vector<SCTAB> aTableNumVector; + InsertTabNumber func; + func = ::std::for_each(aAllTokens.begin(), aAllTokens.end(), func); + func.getVector(aTableNumVector); + aResult.emplace_back( "TableNumberList", -1, + uno::Any( lcl_createTableNumberList( aTableNumVector ) ), + beans::PropertyState_DIRECT_VALUE ); + } + + if( bRowSourceDetected ) + { + // DataRowSource (calculated before) + aResult.emplace_back( "DataRowSource", -1, + uno::Any( eRowSource ), beans::PropertyState_DIRECT_VALUE ); + // HasCategories + aResult.emplace_back( "HasCategories", -1, + uno::Any( bHasCategories ), beans::PropertyState_DIRECT_VALUE ); + // FirstCellAsLabel + aResult.emplace_back( "FirstCellAsLabel", -1, + uno::Any( bFirstCellAsLabel ), beans::PropertyState_DIRECT_VALUE ); + } + + // Add the left upper corner to the range if it is missing. + if (bRowSourceDetected && bFirstCellAsLabel && bHasCategories && !bHasCategoriesLabels ) + { + RangeAnalyzer aTop,aLeft; + if( eRowSource==chart::ChartDataRowSource_COLUMNS ) + { + aTop.initRangeAnalyzer(m_pDocument, aAllSeriesLabelTokens); + aLeft.initRangeAnalyzer(m_pDocument, aAllCategoriesValuesTokens); + } + else + { + aTop.initRangeAnalyzer(m_pDocument, aAllCategoriesValuesTokens); + aLeft.initRangeAnalyzer(m_pDocument, aAllSeriesLabelTokens); + } + lcl_addUpperLeftCornerIfMissing(m_pDocument, aAllTokens, aTop.getRowCount(), aLeft.getColumnCount());//e.g. #i91212# + } + + // Get range string. + lcl_convertTokensToString(sRangeRep, aAllTokens, *m_pDocument); + + // add cell range property + aResult.emplace_back( "CellRangeRepresentation", -1, + uno::Any( sRangeRep ), beans::PropertyState_DIRECT_VALUE ); + + //Sequence Mapping + bool const bSequencesReordered = true;//todo detect this above or detect this sequence mapping cheaper ... + if( bSequencesReordered && bRowSourceDetected ) + { + bool bDifferentIndexes = false; + + std::vector< sal_Int32 > aSequenceMappingVector; + + uno::Reference< chart2::data::XDataSource > xCompareDataSource; + try + { + xCompareDataSource.set( createDataSource( comphelper::containerToSequence( aResult ) ) ); + } + catch( const lang::IllegalArgumentException & ) + { + // creation of data source to compare didn't work, so we cannot + // create a sequence mapping + } + + if( xDataSource.is() && xCompareDataSource.is() ) + { + const uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence> >& aOldSequences = + xCompareDataSource->getDataSequences(); + const uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence> >& aNewSequences = + xDataSource->getDataSequences(); + + std::map<std::pair<OUString, OUString>,sal_Int32> aOldEntryToIndex; + for( sal_Int32 nIndex = 0, n = aOldSequences.getLength(); nIndex < n; nIndex++ ) + { + const uno::Reference< chart2::data::XLabeledDataSequence>& xOld( aOldSequences[nIndex] ); + if( xOld.is() ) + { + std::pair<OUString, OUString> aKey = constructKey(xOld); + aOldEntryToIndex[aKey] = nIndex; + } + } + + for( sal_Int32 nNewIndex = 0, n = aNewSequences.getLength(); nNewIndex < n; nNewIndex++ ) + { + const uno::Reference< chart2::data::XLabeledDataSequence>& xNew( aNewSequences[nNewIndex] ); + if( !xNew.is() ) + continue; + + std::pair<OUString, OUString> aKey = constructKey(xNew); + if (aOldEntryToIndex.find(aKey) == aOldEntryToIndex.end()) + continue; + + sal_Int32 nOldIndex = aOldEntryToIndex[aKey]; + if( nOldIndex != nNewIndex ) + bDifferentIndexes = true; + + aSequenceMappingVector.push_back(nOldIndex); + } + } + + if( bDifferentIndexes && !aSequenceMappingVector.empty() ) + { + aResult.emplace_back( "SequenceMapping", -1, + uno::Any( comphelper::containerToSequence(aSequenceMappingVector) ) + , beans::PropertyState_DIRECT_VALUE ); + } + } + + return comphelper::containerToSequence( aResult ); +} + +sal_Bool SAL_CALL ScChart2DataProvider::createDataSequenceByRangeRepresentationPossible( const OUString& aRangeRepresentation ) +{ + SolarMutexGuard aGuard; + if( ! m_pDocument ) + return false; + + vector<ScTokenRef> aTokens; + const sal_Unicode cSep = ScCompiler::GetNativeSymbolChar(ocSep); + ScRefTokenHelper::compileRangeRepresentation( + aTokens, aRangeRepresentation, *m_pDocument, cSep, m_pDocument->GetGrammar(), true); + return !aTokens.empty(); +} + +uno::Reference< chart2::data::XDataSequence > SAL_CALL + ScChart2DataProvider::createDataSequenceByRangeRepresentation( + const OUString& aRangeRepresentation ) +{ + SolarMutexGuard aGuard; + uno::Reference< chart2::data::XDataSequence > xResult; + + OSL_ENSURE( m_pDocument, "No Document -> no createDataSequenceByRangeRepresentation" ); + if(!m_pDocument || aRangeRepresentation.isEmpty()) + return xResult; + + vector<ScTokenRef> aRefTokens; + const sal_Unicode cSep = ScCompiler::GetNativeSymbolChar(ocSep); + ScRefTokenHelper::compileRangeRepresentation( + aRefTokens, aRangeRepresentation, *m_pDocument, cSep, m_pDocument->GetGrammar(), true); + if (aRefTokens.empty()) + return xResult; + + shrinkToDataRange(m_pDocument, aRefTokens); + + xResult.set(new ScChart2DataSequence(m_pDocument, std::move(aRefTokens), m_bIncludeHiddenCells)); + + return xResult; +} + +uno::Reference<chart2::data::XDataSequence> SAL_CALL +ScChart2DataProvider::createDataSequenceByValueArray( + const OUString& /*aRole*/, const OUString& /*aRangeRepresentation*/, + const OUString& /*aRoleQualifier*/ ) +{ + return uno::Reference<chart2::data::XDataSequence>(); +} + +uno::Reference< sheet::XRangeSelection > SAL_CALL ScChart2DataProvider::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; +} + +sal_Bool SAL_CALL ScChart2DataProvider::createDataSequenceByFormulaTokensPossible( + const Sequence<sheet::FormulaToken>& aTokens ) +{ + if (!aTokens.hasElements()) + return false; + + ScTokenArray aCode(*m_pDocument); + if (!ScTokenConversion::ConvertToTokenArray(*m_pDocument, aCode, aTokens)) + return false; + + sal_uInt16 n = aCode.GetLen(); + if (!n) + return false; + + formula::FormulaTokenArrayPlainIterator aIter(aCode); + const formula::FormulaToken* pFirst = aIter.First(); + const formula::FormulaToken* pLast = aCode.GetArray()[n-1]; + for (const formula::FormulaToken* p = aIter.First(); p; p = aIter.Next()) + { + switch (p->GetType()) + { + case svSep: + { + switch (p->GetOpCode()) + { + case ocSep: + // separators are allowed. + break; + case ocOpen: + if (p != pFirst) + // open paran is allowed only as the first token. + return false; + break; + case ocClose: + if (p != pLast) + // close paren is allowed only as the last token. + return false; + break; + default: + return false; + } + } + break; + case svSingleRef: + case svDoubleRef: + case svExternalSingleRef: + case svExternalDoubleRef: + break; + default: + return false; + } + } + + return true; +} + +uno::Reference<chart2::data::XDataSequence> SAL_CALL +ScChart2DataProvider::createDataSequenceByFormulaTokens( + const Sequence<sheet::FormulaToken>& aTokens ) +{ + uno::Reference<chart2::data::XDataSequence> xResult; + if (!aTokens.hasElements()) + return xResult; + + ScTokenArray aCode(*m_pDocument); + if (!ScTokenConversion::ConvertToTokenArray(*m_pDocument, aCode, aTokens)) + return xResult; + + sal_uInt16 n = aCode.GetLen(); + if (!n) + return xResult; + + vector<ScTokenRef> aRefTokens; + formula::FormulaTokenArrayPlainIterator aIter(aCode); + const formula::FormulaToken* pFirst = aIter.First(); + const formula::FormulaToken* pLast = aCode.GetArray()[n-1]; + for (const formula::FormulaToken* p = aIter.First(); p; p = aIter.Next()) + { + switch (p->GetType()) + { + case svSep: + { + switch (p->GetOpCode()) + { + case ocSep: + // separators are allowed. + break; + case ocOpen: + if (p != pFirst) + // open paran is allowed only as the first token. + throw lang::IllegalArgumentException(); + break; + case ocClose: + if (p != pLast) + // close paren is allowed only as the last token. + throw lang::IllegalArgumentException(); + break; + default: + throw lang::IllegalArgumentException(); + } + } + break; + case svIndex: + case svString: + case svSingleRef: + case svDoubleRef: + case svExternalSingleRef: + case svExternalDoubleRef: + { + ScTokenRef pNew(p->Clone()); + aRefTokens.push_back(pNew); + } + break; + default: + throw lang::IllegalArgumentException(); + } + } + + if (aRefTokens.empty()) + return xResult; + + shrinkToDataRange(m_pDocument, aRefTokens); + + xResult.set(new ScChart2DataSequence(m_pDocument, std::move(aRefTokens), m_bIncludeHiddenCells)); + return xResult; +} + +// XRangeXMLConversion --------------------------------------------------- + +OUString SAL_CALL ScChart2DataProvider::convertRangeToXML( const OUString& sRangeRepresentation ) +{ + OUString aRet; + if (!m_pDocument) + return aRet; + + if (sRangeRepresentation.isEmpty()) + // Empty data range is allowed. + return aRet; + + vector<ScTokenRef> aRefTokens; + const sal_Unicode cSep = ScCompiler::GetNativeSymbolChar(ocSep); + ScRefTokenHelper::compileRangeRepresentation( + aRefTokens, sRangeRepresentation, *m_pDocument, cSep, m_pDocument->GetGrammar(), true); + if (aRefTokens.empty()) + { + SAL_WARN("sc", "convertRangeToXML throw IllegalArgumentException from input of: " << sRangeRepresentation); + throw lang::IllegalArgumentException(); + } + + Tokens2RangeStringXML converter(*m_pDocument); + converter = ::std::for_each(aRefTokens.begin(), aRefTokens.end(), converter); + converter.getString(aRet); + + return aRet; +} + +OUString SAL_CALL ScChart2DataProvider::convertRangeFromXML( const OUString& sXMLRange ) +{ + if (!m_pDocument) + { + // #i74062# When loading flat XML, this is called before the referenced sheets are in the document, + // so the conversion has to take place directly with the strings, without looking up the sheets. + + OUStringBuffer sRet; + sal_Int32 nOffset = 0; + while( nOffset >= 0 ) + { + OUString sToken; + ScRangeStringConverter::GetTokenByOffset( sToken, sXMLRange, nOffset ); + if( nOffset >= 0 ) + { + // convert one address (remove dots) + + OUString aUIString(sToken); + + sal_Int32 nIndex = ScRangeStringConverter::IndexOf( sToken, ':', 0 ); + if ( nIndex >= 0 && nIndex < aUIString.getLength() - 1 && + aUIString[nIndex + 1] == '.' ) + aUIString = aUIString.replaceAt( nIndex + 1, 1, u"" ); + + if ( aUIString[0] == '.' ) + aUIString = aUIString.copy( 1 ); + + if( !sRet.isEmpty() ) + sRet.append( ';' ); + sRet.append( aUIString ); + } + } + + return sRet.makeStringAndClear(); + } + + OUString aRet; + ScRangeStringConverter::GetStringFromXMLRangeString(aRet, sXMLRange, *m_pDocument); + return aRet; +} + +// DataProvider XPropertySet ------------------------------------------------- + +uno::Reference< beans::XPropertySetInfo> SAL_CALL +ScChart2DataProvider::getPropertySetInfo() +{ + SolarMutexGuard aGuard; + static uno::Reference<beans::XPropertySetInfo> aRef = + new SfxItemPropertySetInfo( m_aPropSet.getPropertyMap() ); + return aRef; +} + +void SAL_CALL ScChart2DataProvider::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 ScChart2DataProvider::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 ScChart2DataProvider::addPropertyChangeListener( + const OUString& /*rPropertyName*/, + const uno::Reference< beans::XPropertyChangeListener>& /*xListener*/) +{ + OSL_FAIL( "Not yet implemented" ); +} + +void SAL_CALL ScChart2DataProvider::removePropertyChangeListener( + const OUString& /*rPropertyName*/, + const uno::Reference< beans::XPropertyChangeListener>& /*rListener*/) +{ + OSL_FAIL( "Not yet implemented" ); +} + +void SAL_CALL ScChart2DataProvider::addVetoableChangeListener( + const OUString& /*rPropertyName*/, + const uno::Reference< beans::XVetoableChangeListener>& /*rListener*/) +{ + OSL_FAIL( "Not yet implemented" ); +} + +void SAL_CALL ScChart2DataProvider::removeVetoableChangeListener( + const OUString& /*rPropertyName*/, + const uno::Reference< beans::XVetoableChangeListener>& /*rListener*/ ) +{ + OSL_FAIL( "Not yet implemented" ); +} + +// DataSource ================================================================ + +ScChart2DataSource::ScChart2DataSource( ScDocument* pDoc) + : m_pDocument( pDoc) +{ + if ( m_pDocument ) + m_pDocument->AddUnoObject( *this); +} + +ScChart2DataSource::~ScChart2DataSource() +{ + SolarMutexGuard g; + + if ( m_pDocument ) + m_pDocument->RemoveUnoObject( *this); +} + +void ScChart2DataSource::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint) +{ + if ( rHint.GetId() == SfxHintId::Dying ) + { + m_pDocument = nullptr; + } +} + +uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence> > SAL_CALL +ScChart2DataSource::getDataSequences() +{ + SolarMutexGuard aGuard; + return comphelper::containerToSequence(m_aLabeledSequences); +} + +void ScChart2DataSource::AddLabeledSequence(const uno::Reference < chart2::data::XLabeledDataSequence >& xNew) +{ + m_aLabeledSequences.push_back(xNew); +} + +// DataSequence ============================================================== + +ScChart2DataSequence::Item::Item() + : mfValue(std::numeric_limits<double>::quiet_NaN()) + , mbIsValue(false) +{ +} + +ScChart2DataSequence::HiddenRangeListener::HiddenRangeListener(ScChart2DataSequence& rParent) : + mrParent(rParent) +{ +} + +ScChart2DataSequence::HiddenRangeListener::~HiddenRangeListener() +{ +} + +void ScChart2DataSequence::HiddenRangeListener::notify() +{ + mrParent.setDataChangedHint(true); +} + +ScChart2DataSequence::ScChart2DataSequence( ScDocument* pDoc, + vector<ScTokenRef>&& rTokens, + bool bIncludeHiddenCells ) + : m_xDataArray(new std::vector<Item>) + , m_bIncludeHiddenCells( bIncludeHiddenCells) + , m_nObjectId( 0 ) + , m_pDocument( pDoc) + , m_aTokens(std::move(rTokens)) + , m_aPropSet(lcl_GetDataSequencePropertyMap()) + , m_bGotDataChangedHint(false) + , m_bExtDataRebuildQueued(false) + , mbTimeBased(false) + , mnTimeBasedStart(0) + , mnTimeBasedEnd(0) + , mnCurrentTab(0) +{ + if ( m_pDocument ) + { + m_pDocument->AddUnoObject( *this); + m_nObjectId = m_pDocument->GetNewUnoId(); + } + // FIXME: real implementation of identifier and it's mapping to ranges. + // Reuse ScChartListener? + + // BM: don't use names of named ranges but the UI range strings +// String aStr; +// rRangeList->Format( aStr, ScRefFlags::RANGE_ABS_3D, m_pDocument ); +// m_aIdentifier = aStr; + +// m_aIdentifier = "ID_"; +// static sal_Int32 nID = 0; +// m_aIdentifier += OUString::valueOf( ++nID); +} + +/** called from Clone() */ +ScChart2DataSequence::ScChart2DataSequence(ScDocument* pDoc, const ScChart2DataSequence& r) + : m_xDataArray(r.m_xDataArray) + , m_aHiddenValues(r.m_aHiddenValues) + , m_aRole(r.m_aRole) + , m_bIncludeHiddenCells( r.m_bIncludeHiddenCells) + , m_nObjectId( 0 ) + , m_pDocument( pDoc) + , m_aPropSet(lcl_GetDataSequencePropertyMap()) + , m_bGotDataChangedHint(false) + , m_bExtDataRebuildQueued(false) + , mbTimeBased(false) + , mnTimeBasedStart(0) + , mnTimeBasedEnd(0) + , mnCurrentTab(0) +{ + assert(pDoc); + + // Clone tokens. + m_aTokens.reserve(r.m_aTokens.size()); + for (const auto& rxToken : r.m_aTokens) + { + ScTokenRef p(rxToken->Clone()); + m_aTokens.push_back(p); + } + + m_pDocument->AddUnoObject( *this); + m_nObjectId = m_pDocument->GetNewUnoId(); + + if (r.m_oRangeIndices) + m_oRangeIndices = *r.m_oRangeIndices; + + if (!r.m_pExtRefListener) + return; + + // Re-register all external files that the old instance was + // listening to. + + ScExternalRefManager* pRefMgr = m_pDocument->GetExternalRefManager(); + m_pExtRefListener.reset(new ExternalRefListener(*this, m_pDocument)); + const std::unordered_set<sal_uInt16>& rFileIds = r.m_pExtRefListener->getAllFileIds(); + for (const auto& rFileId : rFileIds) + { + pRefMgr->addLinkListener(rFileId, m_pExtRefListener.get()); + m_pExtRefListener->addFileId(rFileId); + } +} + +ScChart2DataSequence::~ScChart2DataSequence() +{ + SolarMutexGuard g; + + if ( m_pDocument ) + { + m_pDocument->RemoveUnoObject( *this); + if (m_pHiddenListener) + { + ScChartListenerCollection* pCLC = m_pDocument->GetChartListenerCollection(); + if (pCLC) + pCLC->EndListeningHiddenRange(m_pHiddenListener.get()); + } + StopListeningToAllExternalRefs(); + } + + m_pValueListener.reset(); +} + +void ScChart2DataSequence::RefChanged() +{ + if( !m_pValueListener || m_aValueListeners.empty() ) + return; + + m_pValueListener->EndListeningAll(); + + if( !m_pDocument ) + return; + + ScChartListenerCollection* pCLC = nullptr; + if (m_pHiddenListener) + { + pCLC = m_pDocument->GetChartListenerCollection(); + if (pCLC) + pCLC->EndListeningHiddenRange(m_pHiddenListener.get()); + } + + for (const auto& rxToken : m_aTokens) + { + ScRange aRange; + if (!ScRefTokenHelper::getRangeFromToken(m_pDocument, aRange, rxToken, ScAddress())) + continue; + + m_pDocument->StartListeningArea(aRange, false, m_pValueListener.get()); + if (pCLC) + pCLC->StartListeningHiddenRange(aRange, m_pHiddenListener.get()); + } +} + +void ScChart2DataSequence::BuildDataCache() +{ + m_bExtDataRebuildQueued = false; + + if (!m_xDataArray->empty()) + return; + + StopListeningToAllExternalRefs(); + + ::std::vector<sal_Int32> aHiddenValues; + sal_Int32 nDataCount = 0; + + for (const auto& rxToken : m_aTokens) + { + if (ScRefTokenHelper::isExternalRef(rxToken)) + { + nDataCount += FillCacheFromExternalRef(rxToken); + } + else + { + ScRange aRange; + if (!ScRefTokenHelper::getRangeFromToken(m_pDocument, aRange, rxToken, ScAddress())) + continue; + + SCCOL nLastCol = -1; + SCROW nLastRow = -1; + for (SCTAB nTab = aRange.aStart.Tab(); nTab <= aRange.aEnd.Tab(); ++nTab) + { + for (SCCOL nCol = aRange.aStart.Col(); nCol <= aRange.aEnd.Col(); ++nCol) + { + sc::ColumnBlockPosition hint; + m_pDocument->InitColumnBlockPosition( hint, nTab, nCol ); + for (SCROW nRow = aRange.aStart.Row(); nRow <= aRange.aEnd.Row(); ++nRow) + { + if (nRow == aRange.aEnd.Row()) + { + // Excel behavior: if the last row is the totals row, the data + // is not added to the chart. If it's not the last row, the data + // is added like normal. + const auto* pData = m_pDocument->GetDBAtCursor( + nCol, nRow, nTab, + ScDBDataPortion::AREA + ); + if (pData && pData->HasTotals()) + { + ScRange aTempRange; + pData->GetArea(aTempRange); + if (aTempRange.aEnd.Row() == nRow) + { + // Current row is totals row, skip + break; + } + } + } + bool bColHidden = m_pDocument->ColHidden(nCol, nTab, nullptr, &nLastCol); + bool bRowHidden = m_pDocument->RowHidden(nRow, nTab, nullptr, &nLastRow); + + if (bColHidden || bRowHidden) + { + // hidden cell + aHiddenValues.push_back(nDataCount-1); + + if( !m_bIncludeHiddenCells ) + continue; + } + + Item aItem; + + ScAddress aAdr(nCol, nRow, nTab); + aItem.maString = m_pDocument->GetString(aAdr); + + ScRefCellValue aCell(*m_pDocument, aAdr, hint); + switch (aCell.getType()) + { + case CELLTYPE_VALUE: + aItem.mfValue = aCell.getValue(); + aItem.mbIsValue = true; + break; + case CELLTYPE_FORMULA: + { + ScFormulaCell* pFCell = aCell.getFormula(); + FormulaError nErr = pFCell->GetErrCode(); + if (nErr != FormulaError::NONE) + break; + + if (pFCell->IsValue()) + { + aItem.mfValue = pFCell->GetValue(); + aItem.mbIsValue = true; + } + } + break; + case CELLTYPE_EDIT: + case CELLTYPE_NONE: + case CELLTYPE_STRING: + default: + ; // do nothing + } + + aItem.mAddress = ScAddress(nCol, nRow, nTab); + + m_xDataArray->push_back(std::move(aItem)); + ++nDataCount; + } + } + } + } + } + + // convert the hidden cell list to sequence. + m_aHiddenValues.realloc(aHiddenValues.size()); + std::copy( + aHiddenValues.begin(), aHiddenValues.end(), m_aHiddenValues.getArray()); + + // Clear the data series cache when the array is re-built. + m_aMixedDataCache.realloc(0); +} + +void ScChart2DataSequence::RebuildDataCache() +{ + if (!m_bExtDataRebuildQueued) + { + m_xDataArray.reset(new std::vector<Item>); + m_pDocument->BroadcastUno(ScHint(SfxHintId::ScDataChanged, ScAddress())); + m_bExtDataRebuildQueued = true; + m_bGotDataChangedHint = true; + } +} + +sal_Int32 ScChart2DataSequence::FillCacheFromExternalRef(const ScTokenRef& pToken) +{ + ScExternalRefManager* pRefMgr = m_pDocument->GetExternalRefManager(); + ScRange aRange; + if (!ScRefTokenHelper::getRangeFromToken(m_pDocument, aRange, pToken, ScAddress(), true)) + return 0; + + sal_uInt16 nFileId = pToken->GetIndex(); + OUString aTabName = pToken->GetString().getString(); + ScExternalRefCache::TokenArrayRef pArray = pRefMgr->getDoubleRefTokens(nFileId, aTabName, aRange, nullptr); + if (!pArray) + // no external data exists for this range. + return 0; + + // Start listening for this external document. + ExternalRefListener* pExtRefListener = GetExtRefListener(); + pRefMgr->addLinkListener(nFileId, pExtRefListener); + pExtRefListener->addFileId(nFileId); + + m_xDataArray.reset(new std::vector<Item>); + ScExternalRefCache::TableTypeRef pTable = pRefMgr->getCacheTable(nFileId, aTabName, false); + sal_Int32 nDataCount = 0; + FormulaTokenArrayPlainIterator aIter(*pArray); + for (FormulaToken* p = aIter.First(); p; p = aIter.Next()) + { + // Cached external range is always represented as a single + // matrix token, although that might change in the future when + // we introduce a new token type to store multi-table range + // data. + + if (p->GetType() != svMatrix) + { + OSL_FAIL("Cached array is not a matrix token."); + continue; + } + + const ScMatrix* pMat = p->GetMatrix(); + SCSIZE nCSize, nRSize; + pMat->GetDimensions(nCSize, nRSize); + for (SCSIZE nC = 0; nC < nCSize; ++nC) + { + for (SCSIZE nR = 0; nR < nRSize; ++nR) + { + if (pMat->IsValue(nC, nR) || pMat->IsBoolean(nC, nR)) + { + Item aItem; + + aItem.mbIsValue = true; + aItem.mfValue = pMat->GetDouble(nC, nR); + + SvNumberFormatter* pFormatter = m_pDocument->GetFormatTable(); + if (pFormatter) + { + const double fVal = aItem.mfValue; + const Color* pColor = nullptr; + sal_uInt32 nFmt = 0; + if (pTable) + { + // Get the correct format index from the cache. + SCCOL nCol = aRange.aStart.Col() + static_cast<SCCOL>(nC); + SCROW nRow = aRange.aStart.Row() + static_cast<SCROW>(nR); + pTable->getCell(nCol, nRow, &nFmt); + } + pFormatter->GetOutputString(fVal, nFmt, aItem.maString, &pColor); + } + + m_xDataArray->push_back(std::move(aItem)); + ++nDataCount; + } + else if (pMat->IsStringOrEmpty(nC, nR)) + { + Item aItem; + + aItem.mbIsValue = false; + aItem.maString = pMat->GetString(nC, nR).getString(); + + m_xDataArray->push_back(std::move(aItem)); + ++nDataCount; + } + } + } + } + return nDataCount; +} + +void ScChart2DataSequence::UpdateTokensFromRanges(const ScRangeList& rRanges) +{ + if (!m_oRangeIndices) + return; + + for ( size_t i = 0, nCount = rRanges.size(); i < nCount; ++i ) + { + ScTokenRef pToken; + const ScRange & rRange = rRanges[i]; + + ScRefTokenHelper::getTokenFromRange(m_pDocument, pToken, rRange); + sal_uInt32 nOrigPos = (*m_oRangeIndices)[i]; + m_aTokens[nOrigPos] = pToken; + } + + RefChanged(); + + // any change of the range address is broadcast to value (modify) listeners + if ( !m_aValueListeners.empty() ) + m_bGotDataChangedHint = true; +} + +ScChart2DataSequence::ExternalRefListener* ScChart2DataSequence::GetExtRefListener() +{ + if (!m_pExtRefListener) + m_pExtRefListener.reset(new ExternalRefListener(*this, m_pDocument)); + + return m_pExtRefListener.get(); +} + +void ScChart2DataSequence::StopListeningToAllExternalRefs() +{ + if (!m_pExtRefListener) + return; + + const std::unordered_set<sal_uInt16>& rFileIds = m_pExtRefListener->getAllFileIds(); + ScExternalRefManager* pRefMgr = m_pDocument->GetExternalRefManager(); + for (const auto& rFileId : rFileIds) + pRefMgr->removeLinkListener(rFileId, m_pExtRefListener.get()); + + m_pExtRefListener.reset(); +} + + +void ScChart2DataSequence::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint) +{ + if ( dynamic_cast<const ScUpdateRefHint*>(&rHint) ) + { + // Create a range list from the token list, have the range list + // updated, and bring the change back to the token list. + + ScRangeList aRanges; + m_oRangeIndices.emplace(); + vector<ScTokenRef>::const_iterator itrBeg = m_aTokens.begin(), itrEnd = m_aTokens.end(); + for (vector<ScTokenRef>::const_iterator itr = itrBeg ;itr != itrEnd; ++itr) + { + if (!ScRefTokenHelper::isExternalRef(*itr)) + { + ScRange aRange; + ScRefTokenHelper::getRangeFromToken(m_pDocument, aRange, *itr, ScAddress()); + aRanges.push_back(aRange); + sal_uInt32 nPos = distance(itrBeg, itr); + m_oRangeIndices->push_back(nPos); + } + } + + assert(m_oRangeIndices->size() == aRanges.size() && + "range list and range index list have different sizes."); + + unique_ptr<ScRangeList> pUndoRanges; + if ( m_pDocument->HasUnoRefUndo() ) + pUndoRanges.reset(new ScRangeList(aRanges)); + + const ScUpdateRefHint& rRef = static_cast<const ScUpdateRefHint&>(rHint); + bool bChanged = aRanges.UpdateReference( + rRef.GetMode(), m_pDocument, rRef.GetRange(), rRef.GetDx(), rRef.GetDy(), rRef.GetDz()); + + if (bChanged) + { + // TODO: This should be an assert, but tdf#144537 triggers it. + SAL_WARN_IF(m_oRangeIndices->size() == aRanges.size(), + "sc.ui", "range list and range index list have different sizes after the reference update."); + + // Bring the change back from the range list to the token list. + UpdateTokensFromRanges(aRanges); + + if (pUndoRanges) + m_pDocument->AddUnoRefChange(m_nObjectId, *pUndoRanges); + } + } + else if ( auto pUndoHint = dynamic_cast<const ScUnoRefUndoHint*>(&rHint) ) + { + do + { + if (pUndoHint->GetObjectId() != m_nObjectId) + break; + + // The hint object provides the old ranges. Restore the old state + // from these ranges. + + if (!m_oRangeIndices || m_oRangeIndices->empty()) + { + assert(false && " faulty range indices"); + break; + } + + const ScRangeList& rRanges = pUndoHint->GetRanges(); + + size_t nCount = rRanges.size(); + if (nCount != m_oRangeIndices->size()) + { + assert(false && "range count and range index count differ."); + break; + } + + UpdateTokensFromRanges(rRanges); + } + while (false); + } + else + { + const SfxHintId nId = rHint.GetId(); + if ( nId ==SfxHintId::Dying ) + { + m_pDocument = nullptr; + } + else if ( nId == SfxHintId::DataChanged ) + { + // delayed broadcast as in ScCellRangesBase + + if ( m_bGotDataChangedHint && m_pDocument ) + { + m_xDataArray.reset(new std::vector<Item>); + lang::EventObject aEvent; + aEvent.Source = getXWeak(); + + if( m_pDocument ) + { + for (const uno::Reference<util::XModifyListener> & xListener: m_aValueListeners) + m_pDocument->AddUnoListenerCall( xListener, aEvent ); + } + + m_bGotDataChangedHint = false; + } + } + else if ( nId == SfxHintId::ScCalcAll ) + { + // broadcast from DoHardRecalc - set m_bGotDataChangedHint + // (SfxHintId::DataChanged follows separately) + + if ( !m_aValueListeners.empty() ) + m_bGotDataChangedHint = true; + } + else if (nId == SfxHintId::ScClearCache) + { + // necessary after import + m_xDataArray.reset(new std::vector<Item>); + } + } +} + +IMPL_LINK( ScChart2DataSequence, ValueListenerHdl, const SfxHint&, rHint, void ) +{ + if ( m_pDocument && (rHint.GetId() == SfxHintId::ScDataChanged) ) + { + // This may be called several times for a single change, if several formulas + // in the range are notified. So only a flag is set that is checked when + // SfxHintId::DataChanged is received. + + setDataChangedHint(true); + } +} + +ScChart2DataSequence::ExternalRefListener::ExternalRefListener( + ScChart2DataSequence& rParent, ScDocument* pDoc) : + mrParent(rParent), + mpDoc(pDoc) +{ +} + +ScChart2DataSequence::ExternalRefListener::~ExternalRefListener() +{ + if (!mpDoc || mpDoc->IsInDtorClear()) + // The document is being destroyed. Do nothing. + return; + + // Make sure to remove all pointers to this object. + mpDoc->GetExternalRefManager()->removeLinkListener(this); +} + +void ScChart2DataSequence::ExternalRefListener::notify(sal_uInt16 nFileId, ScExternalRefManager::LinkUpdateType eType) +{ + switch (eType) + { + case ScExternalRefManager::LINK_MODIFIED: + { + if (maFileIds.count(nFileId)) + // We are listening to this external document. + mrParent.RebuildDataCache(); + } + break; + case ScExternalRefManager::LINK_BROKEN: + maFileIds.erase(nFileId); + break; + case ScExternalRefManager::OH_NO_WE_ARE_GOING_TO_DIE: + mpDoc = nullptr; + break; + } +} + +void ScChart2DataSequence::ExternalRefListener::addFileId(sal_uInt16 nFileId) +{ + maFileIds.insert(nFileId); +} + +uno::Sequence< uno::Any> SAL_CALL ScChart2DataSequence::getData() +{ + SolarMutexGuard aGuard; + if ( !m_pDocument) + throw uno::RuntimeException(); + + BuildDataCache(); + + if (!m_aMixedDataCache.hasElements()) + { + // Build a cache for the 1st time... + + sal_Int32 nCount = m_xDataArray->size(); + m_aMixedDataCache.realloc(nCount); + uno::Any* pArr = m_aMixedDataCache.getArray(); + for (const Item &rItem : *m_xDataArray) + { + if (rItem.mbIsValue) + *pArr <<= rItem.mfValue; + else if (rItem.maString.isEmpty()) + { + ScRefCellValue aCell(*m_pDocument, rItem.mAddress); + if (aCell.isEmpty()) + *pArr = uno::Any(); + else + *pArr <<= rItem.maString; + } + else + *pArr <<= rItem.maString; + ++pArr; + } + } + return m_aMixedDataCache; +} + +// XNumericalDataSequence -------------------------------------------------- + +uno::Sequence< double > SAL_CALL ScChart2DataSequence::getNumericalData() +{ + SolarMutexGuard aGuard; + if ( !m_pDocument) + throw uno::RuntimeException(); + + BuildDataCache(); + + sal_Int32 nCount = m_xDataArray->size(); + uno::Sequence<double> aSeq(nCount); + double* pArr = aSeq.getArray(); + for (const Item& rItem : *m_xDataArray) + { + *pArr = rItem.mbIsValue ? rItem.mfValue : std::numeric_limits<double>::quiet_NaN(); + ++pArr; + } + + return aSeq; +} + +// XTextualDataSequence -------------------------------------------------- + +uno::Sequence< OUString > SAL_CALL ScChart2DataSequence::getTextualData() +{ + SolarMutexGuard aGuard; + uno::Sequence<OUString> aSeq; + if ( !m_pDocument ) + throw uno::RuntimeException(); + + BuildDataCache(); + + sal_Int32 nCount = m_xDataArray->size(); + if ( nCount > 0 ) + { + aSeq = uno::Sequence<OUString>(nCount); + OUString* pArr = aSeq.getArray(); + for (const Item& rItem : *m_xDataArray) + { + *pArr = rItem.maString; + ++pArr; + } + } + else if ( m_aTokens.front() ) + { + if( m_aTokens.front()->GetType() == svString ) + { + aSeq = uno::Sequence<OUString> { m_aTokens.front()->GetString().getString() }; + } + } + + return aSeq; +} + +OUString SAL_CALL ScChart2DataSequence::getSourceRangeRepresentation() +{ + SolarMutexGuard aGuard; + OUString aStr; + OSL_ENSURE( m_pDocument, "No Document -> no SourceRangeRepresentation" ); + if (m_pDocument) + lcl_convertTokensToString(aStr, m_aTokens, *m_pDocument); + + return aStr; +} + +namespace { + +/** + * This function object is used to accumulatively count the numbers of + * columns and rows in all reference tokens. + */ +class AccumulateRangeSize +{ +public: + AccumulateRangeSize(const ScDocument* pDoc) : + mpDoc(pDoc), mnCols(0), mnRows(0) {} + + void operator() (const ScTokenRef& pToken) + { + ScRange r; + bool bExternal = ScRefTokenHelper::isExternalRef(pToken); + ScRefTokenHelper::getRangeFromToken(mpDoc, r, pToken, ScAddress(), bExternal); + r.PutInOrder(); + mnCols += r.aEnd.Col() - r.aStart.Col() + 1; + mnRows += r.aEnd.Row() - r.aStart.Row() + 1; + } + + SCCOL getCols() const { return mnCols; } + SCROW getRows() const { return mnRows; } +private: + const ScDocument* mpDoc; + SCCOL mnCols; + SCROW mnRows; +}; + +/** + * This function object is used to generate label strings from a list of + * reference tokens. + */ +class GenerateLabelStrings +{ +public: + GenerateLabelStrings(const ScDocument* pDoc, sal_Int32 nSize, chart2::data::LabelOrigin eOrigin, bool bColumn) : + mpDoc(pDoc), + mpLabels(std::make_shared<Sequence<OUString>>(nSize)), + meOrigin(eOrigin), + mnCount(0), + mbColumn(bColumn) {} + + void operator() (const ScTokenRef& pToken) + { + bool bExternal = ScRefTokenHelper::isExternalRef(pToken); + ScRange aRange; + ScRefTokenHelper::getRangeFromToken(mpDoc, aRange, pToken, ScAddress(), bExternal); + OUString* pArr = mpLabels->getArray(); + if (mbColumn) + { + for (SCCOL nCol = aRange.aStart.Col(); nCol <= aRange.aEnd.Col(); ++nCol) + { + if ( meOrigin != chart2::data::LabelOrigin_LONG_SIDE) + { + OUString aString = ScResId(STR_COLUMN) + " "; + ScAddress aPos( nCol, 0, 0 ); + OUString aColStr(aPos.Format(ScRefFlags::COL_VALID)); + aString += aColStr; + pArr[mnCount] = aString; + } + else //only indices for categories + pArr[mnCount] = OUString::number( mnCount+1 ); + ++mnCount; + } + } + else + { + for (sal_Int32 nRow = aRange.aStart.Row(); nRow <= aRange.aEnd.Row(); ++nRow) + { + if (meOrigin != chart2::data::LabelOrigin_LONG_SIDE) + { + OUString aString = ScResId(STR_ROW) + + " " + OUString::number( nRow+1 ); + pArr[mnCount] = aString; + } + else //only indices for categories + pArr[mnCount] = OUString::number( mnCount+1 ); + ++mnCount; + } + } + } + + const Sequence<OUString>& getLabels() const { return *mpLabels; } + +private: + const ScDocument* mpDoc; + shared_ptr< Sequence<OUString> > mpLabels; + chart2::data::LabelOrigin meOrigin; + sal_Int32 mnCount; + bool mbColumn; +}; + +} + +uno::Sequence< OUString > SAL_CALL ScChart2DataSequence::generateLabel(chart2::data::LabelOrigin eOrigin) +{ + SolarMutexGuard aGuard; + if ( !m_pDocument) + throw uno::RuntimeException(); + + // Determine the total size of all ranges. + AccumulateRangeSize func(m_pDocument); + func = ::std::for_each(m_aTokens.begin(), m_aTokens.end(), func); + SCCOL nCols = func.getCols(); + SCROW nRows = func.getRows(); + + // Determine whether this is column-major or row-major. + bool bColumn = true; + if ((eOrigin == chart2::data::LabelOrigin_SHORT_SIDE) || + (eOrigin == chart2::data::LabelOrigin_LONG_SIDE)) + { + if (nRows > nCols) + { + bColumn = eOrigin == chart2::data::LabelOrigin_SHORT_SIDE; + } + else if (nCols > nRows) + { + bColumn = eOrigin != chart2::data::LabelOrigin_SHORT_SIDE; + } + else + return Sequence<OUString>(); + } + + // Generate label strings based on the info so far. + sal_Int32 nCount = bColumn ? nCols : nRows; + GenerateLabelStrings genLabels(m_pDocument, nCount, eOrigin, bColumn); + genLabels = ::std::for_each(m_aTokens.begin(), m_aTokens.end(), genLabels); + Sequence<OUString> aSeq = genLabels.getLabels(); + + return aSeq; +} + +namespace { + +sal_uInt32 getDisplayNumberFormat(const ScDocument* pDoc, const ScAddress& rPos) +{ + sal_uInt32 nFormat = pDoc->GetNumberFormat(rPos); // original format from cell. + return nFormat; +} + +} + +::sal_Int32 SAL_CALL ScChart2DataSequence::getNumberFormatKeyByIndex( ::sal_Int32 nIndex ) +{ + SolarMutexGuard aGuard; + BuildDataCache(); + + if (nIndex == -1) + { + // return format of first non-empty cell + // TODO: use nicer heuristic + for (const Item& rItem : *m_xDataArray) + { + ScRefCellValue aCell(*m_pDocument, rItem.mAddress); + if (!aCell.isEmpty() && aCell.hasNumeric()) + { + return static_cast<sal_Int32>(getDisplayNumberFormat(m_pDocument, rItem.mAddress)); + } + } + + // we could not find a non-empty cell + return 0; + } + + if (nIndex < 0 || o3tl::make_unsigned(nIndex) >= m_xDataArray->size()) + { + SAL_WARN("sc.ui", "Passed invalid index to getNumberFormatKeyByIndex(). Will return default value '0'."); + return 0; + } + + return static_cast<sal_Int32>(getDisplayNumberFormat(m_pDocument, m_xDataArray->at(nIndex).mAddress)); +} + +// XCloneable ================================================================ + +uno::Reference< util::XCloneable > SAL_CALL ScChart2DataSequence::createClone() +{ + SolarMutexGuard aGuard; + + rtl::Reference<ScChart2DataSequence> p(new ScChart2DataSequence(m_pDocument, *this)); + return p; +} + +// XModifyBroadcaster ======================================================== + +void SAL_CALL ScChart2DataSequence::addModifyListener( const uno::Reference< util::XModifyListener >& aListener ) +{ + // like ScCellRangesBase::addModifyListener + SolarMutexGuard aGuard; + if (m_aTokens.empty()) + return; + + ScRangeList aRanges; + ScRefTokenHelper::getRangeListFromTokens(m_pDocument, aRanges, m_aTokens, ScAddress()); + m_aValueListeners.emplace_back( aListener ); + + if ( m_aValueListeners.size() != 1 ) + return; + + if (!m_pValueListener) + m_pValueListener.reset(new ScLinkListener( LINK( this, ScChart2DataSequence, ValueListenerHdl ) )); + + if (!m_pHiddenListener) + m_pHiddenListener.reset(new HiddenRangeListener(*this)); + + if( m_pDocument ) + { + ScChartListenerCollection* pCLC = m_pDocument->GetChartListenerCollection(); + for (const auto& rxToken : m_aTokens) + { + ScRange aRange; + if (!ScRefTokenHelper::getRangeFromToken(m_pDocument, aRange, rxToken, ScAddress())) + continue; + + m_pDocument->StartListeningArea( aRange, false, m_pValueListener.get() ); + if (pCLC) + pCLC->StartListeningHiddenRange(aRange, m_pHiddenListener.get()); + } + } + + acquire(); // don't lose this object (one ref for all listeners) +} + +void SAL_CALL ScChart2DataSequence::removeModifyListener( const uno::Reference< util::XModifyListener >& aListener ) +{ + // like ScCellRangesBase::removeModifyListener + + SolarMutexGuard aGuard; + if (m_aTokens.empty()) + return; + + rtl::Reference<ScChart2DataSequence> xSelfHold(this); // in case the listeners have the last ref + + sal_uInt16 nCount = m_aValueListeners.size(); + for ( sal_uInt16 n=nCount; n--; ) + { + uno::Reference<util::XModifyListener>& rObj = m_aValueListeners[n]; + if ( rObj == aListener ) + { + m_aValueListeners.erase( m_aValueListeners.begin() + n ); + + if ( m_aValueListeners.empty() ) + { + if (m_pValueListener) + m_pValueListener->EndListeningAll(); + + if (m_pHiddenListener && m_pDocument) + { + ScChartListenerCollection* pCLC = m_pDocument->GetChartListenerCollection(); + if (pCLC) + pCLC->EndListeningHiddenRange(m_pHiddenListener.get()); + } + + release(); // release the ref for the listeners + } + + break; + } + } +} + +// DataSequence XPropertySet ------------------------------------------------- + +uno::Reference< beans::XPropertySetInfo> SAL_CALL +ScChart2DataSequence::getPropertySetInfo() +{ + SolarMutexGuard aGuard; + static uno::Reference<beans::XPropertySetInfo> aRef = + new SfxItemPropertySetInfo( m_aPropSet.getPropertyMap() ); + return aRef; +} + +void SAL_CALL ScChart2DataSequence::setPropertyValue( + const OUString& rPropertyName, const uno::Any& rValue) +{ + if ( rPropertyName == SC_UNONAME_ROLE ) + { + if ( !(rValue >>= m_aRole)) + throw lang::IllegalArgumentException(); + } + else if ( rPropertyName == SC_UNONAME_INCLUDEHIDDENCELLS ) + { + bool bOldValue = m_bIncludeHiddenCells; + if ( !(rValue >>= m_bIncludeHiddenCells)) + throw lang::IllegalArgumentException(); + if( bOldValue != m_bIncludeHiddenCells ) + m_xDataArray.reset(new std::vector<Item>);//data array is dirty now + } + else if( rPropertyName == "TimeBased" ) + { + bool bTimeBased = mbTimeBased; + rValue>>= bTimeBased; + mbTimeBased = bTimeBased; + } + else + throw beans::UnknownPropertyException(rPropertyName); + // TODO: support optional properties +} + +uno::Any SAL_CALL ScChart2DataSequence::getPropertyValue(const OUString& rPropertyName) +{ + uno::Any aRet; + if ( rPropertyName == SC_UNONAME_ROLE ) + aRet <<= m_aRole; + else if ( rPropertyName == SC_UNONAME_INCLUDEHIDDENCELLS ) + aRet <<= m_bIncludeHiddenCells; + else if ( rPropertyName == SC_UNONAME_HIDDENVALUES ) + { + // This property is read-only thus cannot be set externally via + // setPropertyValue(...). + BuildDataCache(); + aRet <<= m_aHiddenValues; + } + else if (rPropertyName == SC_UNONAME_TIME_BASED) + { + aRet <<= mbTimeBased; + } + else if (rPropertyName == SC_UNONAME_HAS_STRING_LABEL) + { + // Read-only property. It returns whether or not the label value is a + // direct user input, rather than an indirect reference. + bool bHasStringLabel = false; + if (m_aTokens.size() == 1) + { + const formula::FormulaToken& rToken = *m_aTokens[0]; + bHasStringLabel = rToken.GetType() == formula::svString; + } + aRet <<= bHasStringLabel; + } + else + throw beans::UnknownPropertyException(rPropertyName); + // TODO: support optional properties + return aRet; +} + +void SAL_CALL ScChart2DataSequence::addPropertyChangeListener( + const OUString& /*rPropertyName*/, + const uno::Reference< beans::XPropertyChangeListener>& /*xListener*/) +{ + // FIXME: real implementation + OSL_FAIL( "Not yet implemented" ); +} + +void SAL_CALL ScChart2DataSequence::removePropertyChangeListener( + const OUString& /*rPropertyName*/, + const uno::Reference< beans::XPropertyChangeListener>& /*rListener*/) +{ + // FIXME: real implementation + OSL_FAIL( "Not yet implemented" ); +} + +void SAL_CALL ScChart2DataSequence::addVetoableChangeListener( + const OUString& /*rPropertyName*/, + const uno::Reference< beans::XVetoableChangeListener>& /*rListener*/) +{ + // FIXME: real implementation + OSL_FAIL( "Not yet implemented" ); +} + +void SAL_CALL ScChart2DataSequence::removeVetoableChangeListener( + const OUString& /*rPropertyName*/, + const uno::Reference< beans::XVetoableChangeListener>& /*rListener*/) +{ + // FIXME: real implementation + OSL_FAIL( "Not yet implemented" ); +} + +void ScChart2DataSequence::setDataChangedHint(bool b) +{ + m_bGotDataChangedHint = b; +} + +sal_Bool ScChart2DataSequence::switchToNext(sal_Bool bWrap) +{ + if(!mbTimeBased) + return true; + + if(mnCurrentTab >= mnTimeBasedEnd) + { + if(bWrap) + setToPointInTime(0); + return false; + } + + for(const auto& rxToken : m_aTokens) + { + if (rxToken->GetType() != svDoubleRef) + continue; + + ScComplexRefData& rData = *rxToken->GetDoubleRef(); + ScSingleRefData& s = rData.Ref1; + ScSingleRefData& e = rData.Ref2; + + s.IncTab(1); + e.IncTab(1); + } + + ++mnCurrentTab; + + RebuildDataCache(); + + return true; +} + +void ScChart2DataSequence::setRange(sal_Int32 nStart, sal_Int32 nEnd) +{ + mnTimeBasedStart = nStart; + mnTimeBasedEnd = nEnd; + mnCurrentTab = mnTimeBasedStart; +} + +sal_Bool ScChart2DataSequence::setToPointInTime(sal_Int32 nPoint) +{ + if(nPoint > mnTimeBasedEnd - mnTimeBasedStart) + return false; + + SCTAB nTab = mnTimeBasedStart + nPoint; + for(const auto& rxToken : m_aTokens) + { + if (rxToken->GetType() != svDoubleRef) + continue; + + ScComplexRefData& rData = *rxToken->GetDoubleRef(); + ScSingleRefData& s = rData.Ref1; + ScSingleRefData& e = rData.Ref2; + + s.SetAbsTab(nTab); + e.SetAbsTab(nTab); + } + + mnCurrentTab = nTab; + + RebuildDataCache(); + + return true; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/unoobj/chartuno.cxx b/sc/source/ui/unoobj/chartuno.cxx new file mode 100644 index 0000000000..aa0554b27f --- /dev/null +++ b/sc/source/ui/unoobj/chartuno.cxx @@ -0,0 +1,739 @@ +/* -*- 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 <com/sun/star/embed/Aspects.hpp> +#include <com/sun/star/embed/XEmbeddedObject.hpp> +#include <com/sun/star/awt/Size.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/chart2/data/XDataReceiver.hpp> +#include <com/sun/star/chart/ChartDataRowSource.hpp> +#include <com/sun/star/chart2/XChartDocument.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/table/CellRangeAddress.hpp> + +#include <osl/diagnose.h> +#include <svx/svditer.hxx> +#include <svx/svdoole2.hxx> +#include <svx/svdpage.hxx> +#include <svx/svdundo.hxx> +#include <unotools/moduleoptions.hxx> +#include <comphelper/classids.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <tools/globname.hxx> +#include <svtools/embedhlp.hxx> +#include <utility> +#include <vcl/svapp.hxx> + +#include <ChartTools.hxx> +#include <chartuno.hxx> +#include <miscuno.hxx> +#include <docsh.hxx> +#include <drwlayer.hxx> +#include <undodat.hxx> +#include <chartlis.hxx> +#include <chart2uno.hxx> +#include <convuno.hxx> + +using namespace css; + +#define PROP_HANDLE_RELATED_CELLRANGES 1 + +SC_SIMPLE_SERVICE_INFO( ScChartObj, "ScChartObj", "com.sun.star.table.TableChart" ) +SC_SIMPLE_SERVICE_INFO( ScChartsObj, "ScChartsObj", "com.sun.star.table.TableCharts" ) + +ScChartsObj::ScChartsObj(ScDocShell* pDocSh, SCTAB nT) : + pDocShell( pDocSh ), + nTab( nT ) +{ + pDocShell->GetDocument().AddUnoObject(*this); +} + +ScChartsObj::~ScChartsObj() +{ + SolarMutexGuard g; + + if (pDocShell) + pDocShell->GetDocument().RemoveUnoObject(*this); +} + +void ScChartsObj::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + //! update reference + + if ( rHint.GetId() == SfxHintId::Dying ) + { + pDocShell = nullptr; + } +} + +rtl::Reference<ScChartObj> ScChartsObj::GetObjectByIndex_Impl(tools::Long nIndex) const +{ + if ( pDocShell ) + { + OUString aName; + + ScDocument& rDoc = pDocShell->GetDocument(); + ScDrawLayer* pDrawLayer = rDoc.GetDrawLayer(); + if (pDrawLayer) + { + SdrPage* pPage = pDrawLayer->GetPage(static_cast<sal_uInt16>(nTab)); + OSL_ENSURE(pPage, "Page not found"); + if (pPage) + { + tools::Long nPos = 0; + SdrObjListIter aIter( pPage, SdrIterMode::DeepNoGroups ); + SdrObject* pObject = aIter.Next(); + while (pObject) + { + if ( pObject->GetObjIdentifier() == SdrObjKind::OLE2 && ScDocument::IsChart(pObject) ) + { + if ( nPos == nIndex ) + { + uno::Reference < embed::XEmbeddedObject > xObj = static_cast<SdrOle2Obj*>(pObject)->GetObjRef(); + if ( xObj.is() ) + aName = pDocShell->GetEmbeddedObjectContainer().GetEmbeddedObjectName( xObj ); + break; // stop searching + } + ++nPos; + } + pObject = aIter.Next(); + } + } + } + + if (!aName.isEmpty()) + return new ScChartObj( pDocShell, nTab, aName ); + } + + return nullptr; +} + +rtl::Reference<ScChartObj> ScChartsObj::GetObjectByName_Impl(const OUString& aName) const +{ + if (sc::tools::findChartsByName(pDocShell, nTab, aName, sc::tools::ChartSourceType::CELL_RANGE)) + return new ScChartObj( pDocShell, nTab, aName ); + if (sc::tools::findChartsByName(pDocShell, nTab, aName, sc::tools::ChartSourceType::PIVOT_TABLE)) + return new ScChartObj( pDocShell, nTab, aName ); + return nullptr; +} + +// XTableCharts + +void SAL_CALL ScChartsObj::addNewByName( const OUString& rName, + const awt::Rectangle& aRect, + const uno::Sequence<table::CellRangeAddress>& aRanges, + sal_Bool bColumnHeaders, sal_Bool bRowHeaders ) +{ + SolarMutexGuard aGuard; + if (!pDocShell) + return; + + ScDocument& rDoc = pDocShell->GetDocument(); + ScDrawLayer* pModel = pDocShell->MakeDrawLayer(); + SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab)); + OSL_ENSURE(pPage,"addChart: no Page"); + if (!pPage) + return; + + // chart can't be inserted if any ole object with that name exists on any table + // (empty string: generate valid name) + + OUString aName = rName; + SCTAB nDummy; + if ( !aName.isEmpty() && pModel->GetNamedObject( aName, SdrObjKind::OLE2, nDummy ) ) + { + // object exists - only RuntimeException is specified + throw uno::RuntimeException(); + } + + ScRangeList* pList = new ScRangeList; + for (const table::CellRangeAddress& rRange : aRanges) + { + ScRange aRange( static_cast<SCCOL>(rRange.StartColumn), rRange.StartRow, rRange.Sheet, + static_cast<SCCOL>(rRange.EndColumn), rRange.EndRow, rRange.Sheet ); + pList->push_back( aRange ); + } + ScRangeListRef xNewRanges( pList ); + + uno::Reference < embed::XEmbeddedObject > xObj; + if ( SvtModuleOptions().IsChart() ) + xObj = pDocShell->GetEmbeddedObjectContainer().CreateEmbeddedObject( SvGlobalName( SO3_SCH_CLASSID ).GetByteSequence(), aName ); + if ( !xObj.is() ) + return; + + // adjust rectangle + //! error/exception, if empty/invalid ??? + Point aRectPos( aRect.X, aRect.Y ); + bool bLayoutRTL = rDoc.IsLayoutRTL( nTab ); + if ( ( aRectPos.X() < 0 && !bLayoutRTL ) || ( aRectPos.X() > 0 && bLayoutRTL ) ) + aRectPos.setX( 0 ); + + if (aRectPos.Y() < 0) + aRectPos.setY( 0 ); + + Size aRectSize( aRect.Width, aRect.Height ); + if (aRectSize.Width() <= 0) + aRectSize.setWidth( 5000 ); // default size + + if (aRectSize.Height() <= 0) + aRectSize.setHeight( 5000 ); + tools::Rectangle aInsRect( aRectPos, aRectSize ); + + sal_Int64 nAspect(embed::Aspects::MSOLE_CONTENT); + MapUnit aMapUnit(VCLUnoHelper::UnoEmbed2VCLMapUnit( xObj->getMapUnit( nAspect ) )); + Size aSize(aInsRect.GetSize()); + aSize = OutputDevice::LogicToLogic( aSize, MapMode( MapUnit::Map100thMM ), MapMode( aMapUnit ) ); + awt::Size aSz; + aSz.Width = aSize.Width(); + aSz.Height = aSize.Height(); + + // Calc -> DataProvider + uno::Reference< chart2::data::XDataProvider > xDataProvider = new + ScChart2DataProvider( &rDoc ); + // Chart -> DataReceiver + uno::Reference< chart2::data::XDataReceiver > xReceiver; + if( xObj.is()) + xReceiver.set( xObj->getComponent(), uno::UNO_QUERY ); + if( xReceiver.is()) + { + // Range in UI representation. + OUString sRangeStr; + xNewRanges->Format(sRangeStr, ScRefFlags::RANGE_ABS_3D, rDoc, rDoc.GetAddressConvention()); + + // connect + if( !sRangeStr.isEmpty() ) + xReceiver->attachDataProvider( xDataProvider ); + else + sRangeStr = "all"; + + uno::Reference< util::XNumberFormatsSupplier > xNumberFormatsSupplier( cppu::getXWeak(pDocShell->GetModel()), uno::UNO_QUERY ); + xReceiver->attachNumberFormatsSupplier( xNumberFormatsSupplier ); + + // set arguments + uno::Sequence< beans::PropertyValue > aArgs{ + beans::PropertyValue( + "CellRangeRepresentation", -1, + uno::Any( sRangeStr ), beans::PropertyState_DIRECT_VALUE ), + beans::PropertyValue( + "HasCategories", -1, + uno::Any( bRowHeaders ), beans::PropertyState_DIRECT_VALUE ), + beans::PropertyValue( + "FirstCellAsLabel", -1, + uno::Any( bColumnHeaders ), beans::PropertyState_DIRECT_VALUE ), + beans::PropertyValue( + "DataRowSource", -1, + uno::Any( chart::ChartDataRowSource_COLUMNS ), beans::PropertyState_DIRECT_VALUE ) + }; + xReceiver->setArguments( aArgs ); + } + + ScChartListener* pChartListener = + new ScChartListener( aName, rDoc, xNewRanges ); + rDoc.GetChartListenerCollection()->insert( pChartListener ); + pChartListener->StartListeningTo(); + + rtl::Reference<SdrOle2Obj> pObj = new SdrOle2Obj( + *pModel, + ::svt::EmbeddedObjectRef(xObj, embed::Aspects::MSOLE_CONTENT), + aName, + aInsRect); + + // set VisArea + if( xObj.is()) + xObj->setVisualAreaSize( nAspect, aSz ); + + // #i121334# This call will change the chart's default background fill from white to transparent. + // Add here again if this is wanted (see task description for details) + // ChartHelper::AdaptDefaultsForChart( xObj ); + + pPage->InsertObject( pObj.get() ); + pModel->AddUndo( std::make_unique<SdrUndoInsertObj>( *pObj ) ); +} + +void SAL_CALL ScChartsObj::removeByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + SdrOle2Obj* pObj = sc::tools::findChartsByName(pDocShell, nTab, aName, sc::tools::ChartSourceType::CELL_RANGE); + if (pObj) + { + ScDocument& rDoc = pDocShell->GetDocument(); + rDoc.GetChartListenerCollection()->removeByName(aName); + ScDrawLayer* pModel = rDoc.GetDrawLayer(); // is not zero + SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab)); // is not zero + + pModel->AddUndo( std::make_unique<SdrUndoDelObj>( *pObj ) ); + pPage->RemoveObject( pObj->GetOrdNum() ); + + //! Notify etc.??? + } +} + +// XEnumerationAccess + +uno::Reference<container::XEnumeration> SAL_CALL ScChartsObj::createEnumeration() +{ + SolarMutexGuard aGuard; + return new ScIndexEnumeration(this, "com.sun.star.table.TableChartsEnumeration"); +} + +// XIndexAccess + +sal_Int32 SAL_CALL ScChartsObj::getCount() +{ + SolarMutexGuard aGuard; + sal_Int32 nCount = 0; + if ( pDocShell ) + { + ScDocument& rDoc = pDocShell->GetDocument(); + ScDrawLayer* pDrawLayer = rDoc.GetDrawLayer(); + if (pDrawLayer) + { + SdrPage* pPage = pDrawLayer->GetPage(static_cast<sal_uInt16>(nTab)); + OSL_ENSURE(pPage, "Page not found"); + if (pPage) + { + SdrObjListIter aIter( pPage, SdrIterMode::DeepNoGroups ); + SdrObject* pObject = aIter.Next(); + while (pObject) + { + if ( pObject->GetObjIdentifier() == SdrObjKind::OLE2 && ScDocument::IsChart(pObject) ) + ++nCount; + pObject = aIter.Next(); + } + } + } + } + return nCount; +} + +uno::Any SAL_CALL ScChartsObj::getByIndex( sal_Int32 nIndex ) +{ + SolarMutexGuard aGuard; + uno::Reference<table::XTableChart> xChart(GetObjectByIndex_Impl(nIndex)); + if (!xChart.is()) + throw lang::IndexOutOfBoundsException(); + + return uno::Any(xChart); +} + +uno::Type SAL_CALL ScChartsObj::getElementType() +{ + return cppu::UnoType<table::XTableChart>::get(); +} + +sal_Bool SAL_CALL ScChartsObj::hasElements() +{ + SolarMutexGuard aGuard; + return getCount() != 0; +} + +uno::Any SAL_CALL ScChartsObj::getByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + uno::Reference<table::XTableChart> xChart(GetObjectByName_Impl(aName)); + if (!xChart.is()) + throw container::NoSuchElementException(); + + return uno::Any(xChart); +} + +uno::Sequence<OUString> SAL_CALL ScChartsObj::getElementNames() +{ + SolarMutexGuard aGuard; + if (pDocShell) + { + ScDocument& rDoc = pDocShell->GetDocument(); + + tools::Long nCount = getCount(); + uno::Sequence<OUString> aSeq(nCount); + OUString* pAry = aSeq.getArray(); + + tools::Long nPos = 0; + ScDrawLayer* pDrawLayer = rDoc.GetDrawLayer(); + if (pDrawLayer) + { + SdrPage* pPage = pDrawLayer->GetPage(static_cast<sal_uInt16>(nTab)); + OSL_ENSURE(pPage, "Page not found"); + if (pPage) + { + SdrObjListIter aIter( pPage, SdrIterMode::DeepNoGroups ); + SdrObject* pObject = aIter.Next(); + while (pObject) + { + if ( pObject->GetObjIdentifier() == SdrObjKind::OLE2 && ScDocument::IsChart(pObject) ) + { + OUString aName; + uno::Reference < embed::XEmbeddedObject > xObj = static_cast<SdrOle2Obj*>(pObject)->GetObjRef(); + if ( xObj.is() ) + aName = pDocShell->GetEmbeddedObjectContainer().GetEmbeddedObjectName( xObj ); + + OSL_ENSURE(nPos<nCount, "oops, miscounted?"); + pAry[nPos++] = aName; + } + pObject = aIter.Next(); + } + } + } + OSL_ENSURE(nPos==nCount, "hey, miscounted?"); + + return aSeq; + } + return {}; +} + +sal_Bool SAL_CALL ScChartsObj::hasByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + SdrOle2Obj* aOle2Obj = sc::tools::findChartsByName(pDocShell, nTab, aName, + sc::tools::ChartSourceType::CELL_RANGE); + return aOle2Obj != nullptr; +} + +ScChartObj::ScChartObj(ScDocShell* pDocSh, SCTAB nT, OUString aN) + :ScChartObj_Base( m_aMutex ) + ,ScChartObj_PBase( ScChartObj_Base::rBHelper ) + ,pDocShell( pDocSh ) + ,nTab( nT ) + ,aChartName(std::move( aN )) +{ + pDocShell->GetDocument().AddUnoObject(*this); + + registerPropertyNoMember( "RelatedCellRanges", + PROP_HANDLE_RELATED_CELLRANGES, beans::PropertyAttribute::MAYBEVOID, + cppu::UnoType<uno::Sequence<table::CellRangeAddress>>::get(), + css::uno::Any(uno::Sequence<table::CellRangeAddress>()) ); +} + +ScChartObj::~ScChartObj() +{ + SolarMutexGuard g; + + if (pDocShell) + pDocShell->GetDocument().RemoveUnoObject(*this); +} + +void ScChartObj::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + //! update reference + + if ( rHint.GetId() == SfxHintId::Dying ) + { + pDocShell = nullptr; + } +} + +void ScChartObj::GetData_Impl( ScRangeListRef& rRanges, bool& rColHeaders, bool& rRowHeaders ) const +{ + bool bFound = false; + + if( pDocShell ) + { + ScDocument& rDoc = pDocShell->GetDocument(); + uno::Reference< chart2::XChartDocument > xChartDoc( rDoc.GetChartByName( aChartName ) ); + if( xChartDoc.is() ) + { + uno::Reference< chart2::data::XDataReceiver > xReceiver( xChartDoc, uno::UNO_QUERY ); + uno::Reference< chart2::data::XDataProvider > xProvider = xChartDoc->getDataProvider(); + if( xReceiver.is() && xProvider.is() ) + { + const uno::Sequence< beans::PropertyValue > aArgs( xProvider->detectArguments( xReceiver->getUsedData() ) ); + + OUString aRanges; + chart::ChartDataRowSource eDataRowSource = chart::ChartDataRowSource_COLUMNS; + bool bHasCategories=false; + bool bFirstCellAsLabel=false; + for (const beans::PropertyValue& rProp : aArgs) + { + OUString aPropName(rProp.Name); + + if (aPropName == "CellRangeRepresentation") + rProp.Value >>= aRanges; + else if (aPropName == "DataRowSource") + eDataRowSource = static_cast<chart::ChartDataRowSource>(ScUnoHelpFunctions::GetEnumFromAny( rProp.Value )); + else if (aPropName == "HasCategories") + bHasCategories = ScUnoHelpFunctions::GetBoolFromAny( rProp.Value ); + else if (aPropName == "FirstCellAsLabel") + bFirstCellAsLabel = ScUnoHelpFunctions::GetBoolFromAny( rProp.Value ); + } + + if( chart::ChartDataRowSource_COLUMNS == eDataRowSource ) + { + rColHeaders=bFirstCellAsLabel; + rRowHeaders=bHasCategories; + } + else + { + rColHeaders=bHasCategories; + rRowHeaders=bFirstCellAsLabel; + } + // Range in UI representation. + rRanges->Parse( aRanges, rDoc, rDoc.GetAddressConvention()); + } + bFound = true; + } + } + if( !bFound ) + { + rRanges = nullptr; + rColHeaders = false; + rRowHeaders = false; + } +} + +void ScChartObj::Update_Impl( const ScRangeListRef& rRanges, bool bColHeaders, bool bRowHeaders ) +{ + if (pDocShell) + { + ScDocument& rDoc = pDocShell->GetDocument(); + bool bUndo(rDoc.IsUndoEnabled()); + + if (bUndo) + { + pDocShell->GetUndoManager()->AddUndoAction( + std::make_unique<ScUndoChartData>( pDocShell, aChartName, rRanges, bColHeaders, bRowHeaders, false ) ); + } + rDoc.UpdateChartArea( aChartName, rRanges, bColHeaders, bRowHeaders, false ); + } +} + +// ::comphelper::OPropertySetHelper + +::cppu::IPropertyArrayHelper& ScChartObj::getInfoHelper() +{ + return *ScChartObj_PABase::getArrayHelper(); +} + +void ScChartObj::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const uno::Any& rValue ) +{ + switch ( nHandle ) + { + case PROP_HANDLE_RELATED_CELLRANGES: + { + uno::Sequence< table::CellRangeAddress > aCellRanges; + if ( rValue >>= aCellRanges ) + { + ScRangeListRef rRangeList = new ScRangeList(); + for ( table::CellRangeAddress const & aCellRange : std::as_const(aCellRanges) ) + { + ScRange aRange; + ScUnoConversion::FillScRange( aRange, aCellRange ); + rRangeList->push_back( aRange ); + } + if ( pDocShell ) + { + ScChartListenerCollection* pCollection = pDocShell->GetDocument().GetChartListenerCollection(); + if ( pCollection ) + { + pCollection->ChangeListening( aChartName, rRangeList ); + } + } + } + } + break; + default: + break; + } +} + +void ScChartObj::getFastPropertyValue( uno::Any& rValue, sal_Int32 nHandle ) const +{ + switch ( nHandle ) + { + case PROP_HANDLE_RELATED_CELLRANGES: + { + if (!pDocShell) + break; + ScDocument& rDoc = pDocShell->GetDocument(); + + ScChartListenerCollection* pCollection = rDoc.GetChartListenerCollection(); + if (!pCollection) + break; + + ScChartListener* pListener = pCollection->findByName(aChartName); + if (!pListener) + break; + + const ScRangeListRef& rRangeList = pListener->GetRangeList(); + if (!rRangeList.is()) + break; + + size_t nCount = rRangeList->size(); + uno::Sequence<table::CellRangeAddress> aCellRanges(nCount); + table::CellRangeAddress* pCellRanges = aCellRanges.getArray(); + for (size_t i = 0; i < nCount; ++i) + { + ScRange const & rRange = (*rRangeList)[i]; + table::CellRangeAddress aCellRange; + ScUnoConversion::FillApiRange(aCellRange, rRange); + pCellRanges[i] = aCellRange; + } + rValue <<= aCellRanges; + } + break; + default: + ; + } +} + +// ::comphelper::OPropertyArrayUsageHelper + +::cppu::IPropertyArrayHelper* ScChartObj::createArrayHelper() const +{ + uno::Sequence< beans::Property > aProps; + describeProperties( aProps ); + return new ::cppu::OPropertyArrayHelper( aProps ); +} + +// XInterface + +IMPLEMENT_FORWARD_XINTERFACE2( ScChartObj, ScChartObj_Base, ScChartObj_PBase ) + +// XTypeProvider + +IMPLEMENT_FORWARD_XTYPEPROVIDER2( ScChartObj, ScChartObj_Base, ScChartObj_PBase ) + +// XTableChart + +sal_Bool SAL_CALL ScChartObj::getHasColumnHeaders() +{ + SolarMutexGuard aGuard; + ScRangeListRef xRanges = new ScRangeList; + bool bColHeaders, bRowHeaders; + GetData_Impl( xRanges, bColHeaders, bRowHeaders ); + return bColHeaders; +} + +void SAL_CALL ScChartObj::setHasColumnHeaders( sal_Bool bHasColumnHeaders ) +{ + SolarMutexGuard aGuard; + ScRangeListRef xRanges = new ScRangeList; + bool bOldColHeaders, bOldRowHeaders; + GetData_Impl( xRanges, bOldColHeaders, bOldRowHeaders ); + if ( bOldColHeaders != bool(bHasColumnHeaders) ) + Update_Impl( xRanges, bHasColumnHeaders, bOldRowHeaders ); +} + +sal_Bool SAL_CALL ScChartObj::getHasRowHeaders() +{ + SolarMutexGuard aGuard; + ScRangeListRef xRanges = new ScRangeList; + bool bColHeaders, bRowHeaders; + GetData_Impl( xRanges, bColHeaders, bRowHeaders ); + return bRowHeaders; +} + +void SAL_CALL ScChartObj::setHasRowHeaders( sal_Bool bHasRowHeaders ) +{ + SolarMutexGuard aGuard; + ScRangeListRef xRanges = new ScRangeList; + bool bOldColHeaders, bOldRowHeaders; + GetData_Impl( xRanges, bOldColHeaders, bOldRowHeaders ); + if ( bOldRowHeaders != bool(bHasRowHeaders) ) + Update_Impl( xRanges, bOldColHeaders, bHasRowHeaders ); +} + +uno::Sequence<table::CellRangeAddress> SAL_CALL ScChartObj::getRanges() +{ + SolarMutexGuard aGuard; + ScRangeListRef xRanges = new ScRangeList; + bool bColHeaders, bRowHeaders; + GetData_Impl( xRanges, bColHeaders, bRowHeaders ); + if ( xRanges.is() ) + { + size_t nCount = xRanges->size(); + + table::CellRangeAddress aRangeAddress; + uno::Sequence<table::CellRangeAddress> aSeq(nCount); + table::CellRangeAddress* pAry = aSeq.getArray(); + for (size_t i = 0; i < nCount; i++) + { + ScRange const & rRange = (*xRanges)[i]; + + aRangeAddress.Sheet = rRange.aStart.Tab(); + aRangeAddress.StartColumn = rRange.aStart.Col(); + aRangeAddress.StartRow = rRange.aStart.Row(); + aRangeAddress.EndColumn = rRange.aEnd.Col(); + aRangeAddress.EndRow = rRange.aEnd.Row(); + + pAry[i] = aRangeAddress; + } + return aSeq; + } + + OSL_FAIL("ScChartObj::getRanges: no Ranges"); + return uno::Sequence<table::CellRangeAddress>(); +} + +void SAL_CALL ScChartObj::setRanges( const uno::Sequence<table::CellRangeAddress>& aRanges ) +{ + SolarMutexGuard aGuard; + ScRangeListRef xOldRanges = new ScRangeList; + bool bColHeaders, bRowHeaders; + GetData_Impl( xOldRanges, bColHeaders, bRowHeaders ); + + ScRangeList* pList = new ScRangeList; + for (const table::CellRangeAddress& rRange : aRanges) + { + ScRange aRange( static_cast<SCCOL>(rRange.StartColumn), rRange.StartRow, rRange.Sheet, + static_cast<SCCOL>(rRange.EndColumn), rRange.EndRow, rRange.Sheet ); + pList->push_back( aRange ); + } + ScRangeListRef xNewRanges( pList ); + + if ( !xOldRanges.is() || *xOldRanges != *xNewRanges ) + Update_Impl( xNewRanges, bColHeaders, bRowHeaders ); +} + +// XEmbeddedObjectSupplier + +uno::Reference<lang::XComponent> SAL_CALL ScChartObj::getEmbeddedObject() +{ + SolarMutexGuard aGuard; + SdrOle2Obj* pObject = sc::tools::findChartsByName(pDocShell, nTab, aChartName, + sc::tools::ChartSourceType::CELL_RANGE); + if ( pObject && svt::EmbeddedObjectRef::TryRunningState( pObject->GetObjRef() ) ) + { + //TODO/LATER: is it OK that something is returned for *all* objects, not only own objects? + return uno::Reference < lang::XComponent > ( pObject->GetObjRef()->getComponent(), uno::UNO_QUERY ); + } + + return nullptr; +} + +// XNamed + +OUString SAL_CALL ScChartObj::getName() +{ + SolarMutexGuard aGuard; + return aChartName; +} + +void SAL_CALL ScChartObj::setName( const OUString& /* aName */ ) +{ + throw uno::RuntimeException(); // name cannot be changed +} + +// XPropertySet + +uno::Reference< beans::XPropertySetInfo > ScChartObj::getPropertySetInfo() +{ + return createPropertySetInfo( getInfoHelper() ) ; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/unoobj/condformatuno.cxx b/sc/source/ui/unoobj/condformatuno.cxx new file mode 100644 index 0000000000..945752a3c5 --- /dev/null +++ b/sc/source/ui/unoobj/condformatuno.cxx @@ -0,0 +1,1899 @@ +/* -*- 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 <sal/config.h> + +#include <algorithm> +#include <memory> +#include <condformatuno.hxx> + +#include <document.hxx> +#include <conditio.hxx> +#include <colorscale.hxx> +#include <docsh.hxx> +#include <compiler.hxx> +#include <tokenarray.hxx> + +#include <cellsuno.hxx> +#include <convuno.hxx> + +#include <o3tl/safeint.hxx> +#include <utility> +#include <vcl/svapp.hxx> +#include <rtl/ustring.hxx> +#include <sal/log.hxx> + +#include <com/sun/star/sheet/DataBarAxis.hpp> +#include <com/sun/star/sheet/IconSetType.hpp> +#include <com/sun/star/sheet/ConditionFormatOperator.hpp> +#include <com/sun/star/sheet/DataBarEntryType.hpp> +#include <com/sun/star/sheet/ColorScaleEntryType.hpp> +#include <com/sun/star/sheet/IconSetFormatEntry.hpp> +#include <com/sun/star/sheet/ConditionEntryType.hpp> +#include <com/sun/star/sheet/DateType.hpp> + +namespace { + +enum CondFormatProperties +{ + ID, + CondFormat_Range +}; + +std::span<const SfxItemPropertyMapEntry> getCondFormatPropset() +{ + static const SfxItemPropertyMapEntry aCondFormatPropertyMap_Impl[] = + { + {u"ID"_ustr, ID, cppu::UnoType<sal_Int32>::get(), 0, 0}, + {u"Range"_ustr, CondFormat_Range, cppu::UnoType<sheet::XSheetCellRanges>::get(), 0, 0}, + }; + return aCondFormatPropertyMap_Impl; +} + +enum ConditionEntryProperties +{ + StyleName, + Formula1, + Formula2, + Operator +}; + +std::span<const SfxItemPropertyMapEntry> getConditionEntryrPropSet() +{ + static const SfxItemPropertyMapEntry aConditionEntryPropertyMap_Impl[] = + { + {u"StyleName"_ustr, StyleName, cppu::UnoType<OUString>::get(), 0, 0}, + {u"Formula1"_ustr, Formula1, cppu::UnoType<OUString>::get(), 0, 0}, + {u"Formula2"_ustr, Formula2, cppu::UnoType<OUString>::get(), 0, 0}, + {u"Operator"_ustr, Operator, cppu::UnoType<decltype(sheet::ConditionFormatOperator::EQUAL)>::get(), 0, 0 }, + }; + return aConditionEntryPropertyMap_Impl; +} + +struct ConditionEntryApiMap +{ + ScConditionMode eMode; + sal_Int32 nApiMode; +}; + +ConditionEntryApiMap const aConditionEntryMap[] = +{ + {ScConditionMode::Equal, sheet::ConditionFormatOperator::EQUAL}, + {ScConditionMode::Less, sheet::ConditionFormatOperator::LESS}, + {ScConditionMode::Greater, sheet::ConditionFormatOperator::GREATER}, + {ScConditionMode::EqLess, sheet::ConditionFormatOperator::LESS_EQUAL}, + {ScConditionMode::EqGreater, sheet::ConditionFormatOperator::GREATER_EQUAL}, + {ScConditionMode::NotEqual, sheet::ConditionFormatOperator::NOT_EQUAL}, + {ScConditionMode::Between, sheet::ConditionFormatOperator::BETWEEN}, + {ScConditionMode::NotBetween, sheet::ConditionFormatOperator::NOT_BETWEEN}, + {ScConditionMode::Duplicate, sheet::ConditionFormatOperator::DUPLICATE}, + {ScConditionMode::NotDuplicate, sheet::ConditionFormatOperator::UNIQUE}, + {ScConditionMode::Direct, sheet::ConditionFormatOperator::EXPRESSION}, + {ScConditionMode::Top10, sheet::ConditionFormatOperator::TOP_N_ELEMENTS}, + {ScConditionMode::Bottom10, sheet::ConditionFormatOperator::BOTTOM_N_ELEMENTS}, + {ScConditionMode::TopPercent, sheet::ConditionFormatOperator::TOP_N_PERCENT}, + {ScConditionMode::BottomPercent, sheet::ConditionFormatOperator::BOTTOM_N_PERCENT}, + {ScConditionMode::AboveAverage, sheet::ConditionFormatOperator::ABOVE_AVERAGE}, + {ScConditionMode::BelowAverage, sheet::ConditionFormatOperator::BELOW_AVERAGE}, + {ScConditionMode::AboveEqualAverage, sheet::ConditionFormatOperator::ABOVE_EQUAL_AVERAGE}, + {ScConditionMode::BelowEqualAverage, sheet::ConditionFormatOperator::BELOW_EQUAL_AVERAGE}, + {ScConditionMode::Error, sheet::ConditionFormatOperator::ERROR}, + {ScConditionMode::NoError, sheet::ConditionFormatOperator::NO_ERROR}, + {ScConditionMode::BeginsWith, sheet::ConditionFormatOperator::BEGINS_WITH}, + {ScConditionMode::EndsWith, sheet::ConditionFormatOperator::ENDS_WITH}, + {ScConditionMode::ContainsText, sheet::ConditionFormatOperator::CONTAINS}, + {ScConditionMode::NotContainsText, sheet::ConditionFormatOperator::NOT_CONTAINS}, + {ScConditionMode::NONE, sheet::ConditionFormatOperator::EQUAL}, +}; + +enum ColorScaleProperties +{ + ColorScaleEntries +}; + +std::span<const SfxItemPropertyMapEntry> getColorScalePropSet() +{ + static const SfxItemPropertyMapEntry aColorScalePropertyMap_Impl[] = + { + {u"ColorScaleEntries"_ustr, ColorScaleEntries, cppu::UnoType<uno::Sequence< sheet::XColorScaleEntry >>::get(), 0, 0 }, + }; + return aColorScalePropertyMap_Impl; +} + +struct ColorScaleEntryTypeApiMap +{ + ScColorScaleEntryType eType; + sal_Int32 nApiType; +}; + +ColorScaleEntryTypeApiMap const aColorScaleEntryTypeMap[] = +{ + { COLORSCALE_MIN, sheet::ColorScaleEntryType::COLORSCALE_MIN }, + { COLORSCALE_MAX, sheet::ColorScaleEntryType::COLORSCALE_MAX }, + { COLORSCALE_VALUE, sheet::ColorScaleEntryType::COLORSCALE_VALUE }, + { COLORSCALE_FORMULA, sheet::ColorScaleEntryType::COLORSCALE_FORMULA }, + { COLORSCALE_PERCENT, sheet::ColorScaleEntryType::COLORSCALE_PERCENT }, + { COLORSCALE_PERCENTILE, sheet::ColorScaleEntryType::COLORSCALE_PERCENTILE } +}; + +enum DataBarProperties +{ + AxisPosition, + UseGradient, + UseNegativeColor, + DataBar_ShowValue, + DataBar_Color, + AxisColor, + NegativeColor, + DataBarEntries, + MinimumLength, + MaximumLength +}; + +std::span<const SfxItemPropertyMapEntry> getDataBarPropSet() +{ + static const SfxItemPropertyMapEntry aDataBarPropertyMap_Impl[] = + { + {u"AxisPosition"_ustr, AxisPosition, cppu::UnoType<decltype(sheet::DataBarAxis::AXIS_AUTOMATIC)>::get(), 0, 0 }, + {u"UseGradient"_ustr, UseGradient, cppu::UnoType<bool>::get(), 0, 0 }, + {u"UseNegativeColor"_ustr, UseNegativeColor, cppu::UnoType<bool>::get(), 0, 0 }, + {u"ShowValue"_ustr, DataBar_ShowValue, cppu::UnoType<bool>::get(), 0, 0 }, + {u"Color"_ustr, DataBar_Color, cppu::UnoType<sal_Int32>::get(), 0, 0}, + {u"AxisColor"_ustr, AxisColor, cppu::UnoType<sal_Int32>::get(), 0, 0}, + {u"NegativeColor"_ustr, NegativeColor, cppu::UnoType<sal_Int32>::get(), 0, 0}, + {u"DataBarEntries"_ustr, DataBarEntries, cppu::UnoType<uno::Sequence< sheet::XDataBarEntry >>::get(), 0, 0 }, + {u"MinimumLength"_ustr, MinimumLength, cppu::UnoType<double>::get(), 0, 0 }, + {u"MaximumLength"_ustr, MaximumLength, cppu::UnoType<double>::get(), 0, 0 }, + }; + return aDataBarPropertyMap_Impl; +} + +struct DataBarAxisApiMap +{ + databar::ScAxisPosition ePos; + sal_Int32 nApiPos; +}; + +DataBarAxisApiMap const aDataBarAxisMap[] = +{ + { databar::NONE, sheet::DataBarAxis::AXIS_NONE }, + { databar::AUTOMATIC, sheet::DataBarAxis::AXIS_AUTOMATIC }, + { databar::MIDDLE, sheet::DataBarAxis::AXIS_MIDDLE } +}; + +struct DataBarEntryTypeApiMap +{ + ScColorScaleEntryType eType; + sal_Int32 nApiType; +}; + +DataBarEntryTypeApiMap const aDataBarEntryTypeMap[] = +{ + { COLORSCALE_AUTO, sheet::DataBarEntryType::DATABAR_AUTO }, + { COLORSCALE_MIN, sheet::DataBarEntryType::DATABAR_MIN }, + { COLORSCALE_MAX, sheet::DataBarEntryType::DATABAR_MAX }, + { COLORSCALE_VALUE, sheet::DataBarEntryType::DATABAR_VALUE }, + { COLORSCALE_FORMULA, sheet::DataBarEntryType::DATABAR_FORMULA }, + { COLORSCALE_PERCENT, sheet::DataBarEntryType::DATABAR_PERCENT }, + { COLORSCALE_PERCENTILE, sheet::DataBarEntryType::DATABAR_PERCENTILE } +}; + +enum IconSetProperties +{ + Icons, + Reverse, + ShowValue, + IconSetEntries +}; + +std::span<const SfxItemPropertyMapEntry> getIconSetPropSet() +{ + static const SfxItemPropertyMapEntry aIconSetPropertyMap_Impl[] = + { + {u"Icons"_ustr, Icons, cppu::UnoType<decltype(sheet::IconSetType::ICONSET_3SYMBOLS)>::get(), 0, 0 }, + {u"Reverse"_ustr, Reverse, cppu::UnoType<bool>::get(), 0, 0 }, + {u"ShowValue"_ustr, ShowValue, cppu::UnoType<bool>::get(), 0, 0 }, + {u"IconSetEntries"_ustr, IconSetEntries, cppu::UnoType<uno::Sequence< sheet::XIconSetEntry >>::get(), 0, 0 }, + }; + return aIconSetPropertyMap_Impl; +} + +struct IconSetTypeApiMap +{ + ScIconSetType eType; + sal_Int32 nApiType; +}; + +const IconSetTypeApiMap aIconSetApiMap[] = +{ + { IconSet_3Arrows, sheet::IconSetType::ICONSET_3ARROWS }, + { IconSet_3ArrowsGray, sheet::IconSetType::ICONSET_3ARROWS_GRAY }, + { IconSet_3Flags, sheet::IconSetType::ICONSET_3FLAGS }, + { IconSet_3TrafficLights1, sheet::IconSetType::ICONSET_3TRAFFICLIGHTS1 }, + { IconSet_3TrafficLights2, sheet::IconSetType::ICONSET_3TRAFFICLIGHTS2 }, + { IconSet_3Signs, sheet::IconSetType::ICONSET_3SIGNS }, + { IconSet_3Symbols, sheet::IconSetType::ICONSET_3SYMBOLS }, + { IconSet_3Symbols2, sheet::IconSetType::ICONSET_3SYMBOLS2 }, + { IconSet_3Smilies, sheet::IconSetType::ICONSET_3SMILIES }, + { IconSet_3ColorSmilies, sheet::IconSetType::ICONSET_3COLOR_SIMILIES }, + { IconSet_4Arrows, sheet::IconSetType::ICONSET_4ARROWS }, + { IconSet_4ArrowsGray, sheet::IconSetType::ICONSET_4ARROWS_GRAY }, + { IconSet_4Rating, sheet::IconSetType::ICONSET_4RATING }, + { IconSet_4RedToBlack, sheet::IconSetType::ICONSET_4RED_TO_BLACK }, + { IconSet_4TrafficLights, sheet::IconSetType::ICONSET_4TRAFFICLIGHTS }, + { IconSet_5Arrows, sheet::IconSetType::ICONSET_5ARROWS }, + { IconSet_5ArrowsGray, sheet::IconSetType::ICONSET_4ARROWS_GRAY }, + { IconSet_5Ratings, sheet::IconSetType::ICONSET_5RATINGS }, + { IconSet_5Quarters, sheet::IconSetType::ICONSET_5QUARTERS }, +}; + +struct IconSetEntryTypeApiMap +{ + ScColorScaleEntryType eType; + sal_Int32 nApiType; +}; + +IconSetEntryTypeApiMap const aIconSetEntryTypeMap[] = +{ + { COLORSCALE_MIN, sheet::IconSetFormatEntry::ICONSET_MIN }, + { COLORSCALE_VALUE, sheet::IconSetFormatEntry::ICONSET_VALUE }, + { COLORSCALE_FORMULA, sheet::IconSetFormatEntry::ICONSET_FORMULA }, + { COLORSCALE_PERCENT, sheet::IconSetFormatEntry::ICONSET_PERCENT }, + { COLORSCALE_PERCENTILE, sheet::IconSetFormatEntry::ICONSET_PERCENTILE } +}; + +enum DateProperties +{ + Date_StyleName, + DateType +}; + +std::span<const SfxItemPropertyMapEntry> getCondDatePropSet() +{ + static const SfxItemPropertyMapEntry aCondDatePropertyMap_Impl[] = + { + {u"StyleName"_ustr, StyleName, cppu::UnoType<OUString>::get(), 0, 0}, + {u"DateType"_ustr, Icons, cppu::UnoType<decltype(sheet::DateType::TODAY)>::get(), 0, 0 }, + }; + return aCondDatePropertyMap_Impl; +} + +struct DateTypeApiMap +{ + condformat::ScCondFormatDateType eType; + sal_Int32 nApiType; +}; + +DateTypeApiMap const aDateTypeApiMap[] = +{ + { condformat::TODAY, sheet::DateType::TODAY }, + { condformat::YESTERDAY, sheet::DateType::YESTERDAY }, + { condformat::TOMORROW, sheet::DateType::TOMORROW }, + { condformat::LAST7DAYS, sheet::DateType::LAST7DAYS }, + { condformat::THISWEEK, sheet::DateType::THISWEEK }, + { condformat::LASTWEEK, sheet::DateType::LASTWEEK }, + { condformat::NEXTWEEK, sheet::DateType::NEXTWEEK }, + { condformat::THISMONTH, sheet::DateType::THISMONTH }, + { condformat::LASTMONTH, sheet::DateType::LASTMONTH }, + { condformat::NEXTMONTH, sheet::DateType::NEXTMONTH }, + { condformat::THISYEAR, sheet::DateType::THISYEAR }, + { condformat::LASTYEAR, sheet::DateType::LASTYEAR }, + { condformat::NEXTYEAR, sheet::DateType::NEXTYEAR } +}; + +} + +ScCondFormatsObj::ScCondFormatsObj(ScDocShell* pDocShell, SCTAB nTab): + mnTab(nTab), + mpDocShell(pDocShell) +{ + pDocShell->GetDocument().AddUnoObject(*this); +} + +ScCondFormatsObj::~ScCondFormatsObj() +{ + if (mpDocShell) + mpDocShell->GetDocument().RemoveUnoObject(*this); +} + +void ScCondFormatsObj::Notify(SfxBroadcaster& /*rBC*/, const SfxHint& rHint) +{ + if ( rHint.GetId() == SfxHintId::Dying ) + { + mpDocShell = nullptr; + } +} + +sal_Int32 ScCondFormatsObj::createByRange(const uno::Reference< sheet::XSheetCellRanges >& xRanges) +{ + SolarMutexGuard aGuard; + if (!mpDocShell) + throw lang::IllegalArgumentException(); + + if (!xRanges.is()) + throw lang::IllegalArgumentException(); + + const uno::Sequence<table::CellRangeAddress> aRanges = + xRanges->getRangeAddresses(); + + ScRangeList aCoreRange; + for (const auto& rRange : aRanges) + { + ScRange aRange; + ScUnoConversion::FillScRange(aRange, rRange); + aCoreRange.Join(aRange); + } + + if (aCoreRange.empty()) + throw lang::IllegalArgumentException(); + + SCTAB nTab = aCoreRange[0].aStart.Tab(); + + auto pNewFormat = std::make_unique<ScConditionalFormat>(0, &mpDocShell->GetDocument()); + pNewFormat->SetRange(aCoreRange); + return mpDocShell->GetDocument().AddCondFormat(std::move(pNewFormat), nTab); +} + +void ScCondFormatsObj::removeByID(const sal_Int32 nID) +{ + SolarMutexGuard aGuard; + ScConditionalFormatList* pFormatList = getCoreObject(); + pFormatList->erase(nID); +} + +uno::Sequence<uno::Reference<sheet::XConditionalFormat> > ScCondFormatsObj::getConditionalFormats() +{ + SolarMutexGuard aGuard; + ScConditionalFormatList* pFormatList = getCoreObject(); + size_t n = pFormatList->size(); + uno::Sequence<uno::Reference<sheet::XConditionalFormat> > aCondFormats(n); + std::transform(pFormatList->begin(), pFormatList->end(), aCondFormats.getArray(), + [this](const auto& rFormat) + { return uno::Reference(new ScCondFormatObj(mpDocShell, this, rFormat->GetKey())); }); + + return aCondFormats; +} + +sal_Int32 ScCondFormatsObj::getLength() +{ + SolarMutexGuard aGuard; + ScConditionalFormatList* pFormatList = getCoreObject(); + return pFormatList->size(); +} + +ScConditionalFormatList* ScCondFormatsObj::getCoreObject() +{ + if (!mpDocShell) + throw uno::RuntimeException(); + + ScConditionalFormatList* pList = mpDocShell->GetDocument().GetCondFormList(mnTab); + if (!pList) + throw uno::RuntimeException(); + + return pList; +} + +namespace { + +uno::Reference<beans::XPropertySet> createConditionEntry(const ScFormatEntry* pEntry, + rtl::Reference<ScCondFormatObj> const & xParent) +{ + switch (pEntry->GetType()) + { + case ScFormatEntry::Type::Condition: + case ScFormatEntry::Type::ExtCondition: + return new ScConditionEntryObj(xParent, + static_cast<const ScCondFormatEntry*>(pEntry)); + break; + case ScFormatEntry::Type::Colorscale: + return new ScColorScaleFormatObj(xParent, + static_cast<const ScColorScaleFormat*>(pEntry)); + break; + case ScFormatEntry::Type::Databar: + return new ScDataBarFormatObj(xParent, + static_cast<const ScDataBarFormat*>(pEntry)); + break; + case ScFormatEntry::Type::Iconset: + return new ScIconSetFormatObj(xParent, + static_cast<const ScIconSetFormat*>(pEntry)); + break; + case ScFormatEntry::Type::Date: + return new ScCondDateFormatObj(xParent, + static_cast<const ScCondDateFormatEntry*>(pEntry)); + break; + default: + break; + } + return uno::Reference<beans::XPropertySet>(); +} + +} + +ScCondFormatObj::ScCondFormatObj(ScDocShell* pDocShell, rtl::Reference<ScCondFormatsObj> xCondFormats, + sal_Int32 nKey): + mxCondFormatList(std::move(xCondFormats)), + mpDocShell(pDocShell), + maPropSet(getCondFormatPropset()), + mnKey(nKey) +{ +} + +ScCondFormatObj::~ScCondFormatObj() +{ +} + +ScConditionalFormat* ScCondFormatObj::getCoreObject() +{ + ScConditionalFormatList* pList = mxCondFormatList->getCoreObject(); + ScConditionalFormat* pFormat = pList->GetFormat(mnKey); + if (!pFormat) + throw uno::RuntimeException(); + + return pFormat; +} + +ScDocShell* ScCondFormatObj::getDocShell() +{ + return mpDocShell; +} + +void ScCondFormatObj::createEntry(const sal_Int32 nType, const sal_Int32 nPos) +{ + SolarMutexGuard aGuard; + ScConditionalFormat* pFormat = getCoreObject(); + if (nPos > sal_Int32(pFormat->size())) + throw lang::IllegalArgumentException(); + + ScFormatEntry* pNewEntry = nullptr; + ScDocument& rDoc = mpDocShell->GetDocument(); + switch (nType) + { + case sheet::ConditionEntryType::CONDITION: + pNewEntry = new ScCondFormatEntry(ScConditionMode::Equal, "", "", + rDoc, pFormat->GetRange().GetTopLeftCorner(), ""); + break; + case sheet::ConditionEntryType::COLORSCALE: + pNewEntry = new ScColorScaleFormat(&rDoc); + static_cast<ScColorScaleFormat*>(pNewEntry)->EnsureSize(); + break; + case sheet::ConditionEntryType::DATABAR: + pNewEntry = new ScDataBarFormat(&rDoc); + static_cast<ScDataBarFormat*>(pNewEntry)->EnsureSize(); + break; + case sheet::ConditionEntryType::ICONSET: + pNewEntry = new ScIconSetFormat(&rDoc); + static_cast<ScIconSetFormat*>(pNewEntry)->EnsureSize(); + break; + case sheet::ConditionEntryType::DATE: + pNewEntry = new ScCondDateFormatEntry(&rDoc); + break; + default: + SAL_WARN("sc", "unknown conditional format type"); + throw lang::IllegalArgumentException(); + } + + pFormat->AddEntry(pNewEntry); +} + +void ScCondFormatObj::removeByIndex(const sal_Int32 nIndex) +{ + SolarMutexGuard aGuard; + if (getCoreObject()->size() >= o3tl::make_unsigned(nIndex)) + throw lang::IllegalArgumentException(); + + getCoreObject()->RemoveEntry(nIndex); +} + +uno::Type ScCondFormatObj::getElementType() +{ + return cppu::UnoType<beans::XPropertySet>::get(); +} + +sal_Bool ScCondFormatObj::hasElements() +{ + SolarMutexGuard aGuard; + ScConditionalFormat* pFormat = getCoreObject(); + return !pFormat->IsEmpty(); +} + +sal_Int32 ScCondFormatObj::getCount() +{ + SolarMutexGuard aGuard; + ScConditionalFormat* pFormat = getCoreObject(); + + return pFormat->size(); +} + +uno::Any ScCondFormatObj::getByIndex(sal_Int32 nIndex) +{ + SolarMutexGuard aGuard; + if (getCoreObject()->size() <= o3tl::make_unsigned(nIndex)) + throw lang::IllegalArgumentException(); + + const ScFormatEntry* pEntry = getCoreObject()->GetEntry(nIndex); + uno::Reference<beans::XPropertySet> xCondEntry = + createConditionEntry(pEntry, this); + return uno::Any(xCondEntry); +} + +uno::Reference<beans::XPropertySetInfo> SAL_CALL ScCondFormatObj::getPropertySetInfo() +{ + SolarMutexGuard aGuard; + static uno::Reference<beans::XPropertySetInfo> aRef( + new SfxItemPropertySetInfo( maPropSet.getPropertyMap())); + return aRef; +} + +void SAL_CALL ScCondFormatObj::setPropertyValue( + const OUString& aPropertyName, const uno::Any& aValue ) +{ + SolarMutexGuard aGuard; + + const SfxItemPropertyMap& rPropertyMap = maPropSet.getPropertyMap(); // from derived class + const SfxItemPropertyMapEntry* pEntry = rPropertyMap.getByName( aPropertyName ); + if ( !pEntry ) + throw beans::UnknownPropertyException(aPropertyName); + + switch(pEntry->nWID) + { + case ID: + throw lang::IllegalArgumentException(); + break; + case CondFormat_Range: + { + uno::Reference<sheet::XSheetCellRanges> xRange; + if (aValue >>= xRange) + { + ScConditionalFormat* pFormat = getCoreObject(); + const uno::Sequence<table::CellRangeAddress> aRanges = + xRange->getRangeAddresses(); + ScRangeList aTargetRange; + for (const auto& rRange : aRanges) + { + ScRange aRange; + ScUnoConversion::FillScRange(aRange, rRange); + aTargetRange.Join(aRange); + } + pFormat->SetRange(aTargetRange); + } + } + break; + default: + SAL_WARN("sc", "unknown property"); + } +} + +uno::Any SAL_CALL ScCondFormatObj::getPropertyValue( const OUString& aPropertyName ) +{ + SolarMutexGuard aGuard; + + const SfxItemPropertyMap& rPropertyMap = maPropSet.getPropertyMap(); // from derived class + const SfxItemPropertyMapEntry* pEntry = rPropertyMap.getByName( aPropertyName ); + if ( !pEntry ) + throw beans::UnknownPropertyException(aPropertyName); + + uno::Any aAny; + switch(pEntry->nWID) + { + case ID: + aAny <<= sal_Int32(getCoreObject()->GetKey()); + break; + case CondFormat_Range: + { + const ScRangeList& rRange = getCoreObject()->GetRange(); + uno::Reference<sheet::XSheetCellRanges> xRange; + xRange.set(new ScCellRangesObj(mpDocShell, rRange)); + aAny <<= xRange; + } + break; + default: + SAL_WARN("sc", "unknown property"); + } + return aAny; +} + +void SAL_CALL ScCondFormatObj::addPropertyChangeListener( const OUString& /* aPropertyName */, + const uno::Reference<beans::XPropertyChangeListener>& /* aListener */) +{ + SAL_WARN("sc", "not implemented"); +} + +void SAL_CALL ScCondFormatObj::removePropertyChangeListener( const OUString& /* aPropertyName */, + const uno::Reference<beans::XPropertyChangeListener>& /* aListener */) +{ + SAL_WARN("sc", "not implemented"); +} + +void SAL_CALL ScCondFormatObj::addVetoableChangeListener( const OUString&, + const uno::Reference<beans::XVetoableChangeListener>&) +{ + SAL_WARN("sc", "not implemented"); +} + +void SAL_CALL ScCondFormatObj::removeVetoableChangeListener( const OUString&, + const uno::Reference<beans::XVetoableChangeListener>&) +{ + SAL_WARN("sc", "not implemented"); +} + +namespace { + +bool isObjectStillAlive(const ScConditionalFormat* pFormat, const ScFormatEntry* pEntry) +{ + for(size_t i = 0, n= pFormat->size(); i < n; ++i) + { + if (pFormat->GetEntry(i) == pEntry) + return true; + } + return false; +} + +} + +ScConditionEntryObj::ScConditionEntryObj(rtl::Reference<ScCondFormatObj> const & xParent, + const ScCondFormatEntry* pFormat): + mpDocShell(xParent->getDocShell()), + mxParent(xParent), + maPropSet(getConditionEntryrPropSet()), + mpFormat(pFormat) +{ +} + +ScConditionEntryObj::~ScConditionEntryObj() +{ +} + +ScCondFormatEntry* ScConditionEntryObj::getCoreObject() +{ + ScConditionalFormat* pFormat = mxParent->getCoreObject(); + if (isObjectStillAlive(pFormat, mpFormat)) + return const_cast<ScCondFormatEntry*>(mpFormat); + + throw lang::IllegalArgumentException(); +} + +sal_Int32 ScConditionEntryObj::getType() +{ + return sheet::ConditionEntryType::CONDITION; +} + +uno::Reference<beans::XPropertySetInfo> SAL_CALL ScConditionEntryObj::getPropertySetInfo() +{ + static uno::Reference<beans::XPropertySetInfo> aRef( + new SfxItemPropertySetInfo( maPropSet.getPropertyMap() )); + return aRef; +} + +void SAL_CALL ScConditionEntryObj::setPropertyValue( + const OUString& aPropertyName, const uno::Any& aValue ) +{ + SolarMutexGuard aGuard; + + const SfxItemPropertyMap& rPropertyMap = maPropSet.getPropertyMap(); // from derived class + const SfxItemPropertyMapEntry* pEntry = rPropertyMap.getByName( aPropertyName ); + if ( !pEntry ) + throw beans::UnknownPropertyException(aPropertyName); + + switch(pEntry->nWID) + { + case StyleName: + { + OUString aStyleName; + if ((aValue >>= aStyleName) && !aStyleName.isEmpty()) + getCoreObject()->UpdateStyleName(aStyleName); + } + break; + case Formula1: + { + OUString aFormula; + if ((aValue >>= aFormula) && !aFormula.isEmpty()) + { + ScCompiler aComp(mpDocShell->GetDocument(), getCoreObject()->GetSrcPos()); + aComp.SetGrammar(mpDocShell->GetDocument().GetGrammar()); + std::unique_ptr<ScTokenArray> pArr(aComp.CompileString(aFormula)); + getCoreObject()->SetFormula1(*pArr); + } + } + break; + case Formula2: + { + OUString aFormula; + if ((aValue >>= aFormula) && !aFormula.isEmpty()) + { + ScCompiler aComp(mpDocShell->GetDocument(), getCoreObject()->GetSrcPos()); + aComp.SetGrammar(mpDocShell->GetDocument().GetGrammar()); + std::unique_ptr<ScTokenArray> pArr(aComp.CompileString(aFormula)); + getCoreObject()->SetFormula2(*pArr); + } + } + break; + case Operator: + { + sal_Int32 nVal; + if (aValue >>= nVal) + { + for (ConditionEntryApiMap const & rEntry : aConditionEntryMap) + { + if (rEntry.nApiMode == nVal) + { + getCoreObject()->SetOperation(rEntry.eMode); + break; + } + } + } + } + break; + default: + SAL_WARN("sc", "unsupported property"); + } +} + +uno::Any SAL_CALL ScConditionEntryObj::getPropertyValue( const OUString& aPropertyName ) +{ + SolarMutexGuard aGuard; + + const SfxItemPropertyMap& rPropertyMap = maPropSet.getPropertyMap(); // from derived class + const SfxItemPropertyMapEntry* pEntry = rPropertyMap.getByName( aPropertyName ); + if ( !pEntry ) + throw beans::UnknownPropertyException(aPropertyName); + + uno::Any aAny; + switch(pEntry->nWID) + { + case StyleName: + aAny <<= getCoreObject()->GetStyle(); + break; + case Formula1: + { + ScAddress aCursor = getCoreObject()->GetSrcPos(); + OUString aFormula = getCoreObject()->GetExpression(aCursor, 0); + aAny <<= aFormula; + } + break; + case Formula2: + { + ScAddress aCursor = getCoreObject()->GetSrcPos(); + OUString aFormula = getCoreObject()->GetExpression(aCursor, 1); + aAny <<= aFormula; + } + break; + case Operator: + { + ScConditionMode eMode = getCoreObject()->GetOperation(); + for (ConditionEntryApiMap const & rEntry : aConditionEntryMap) + { + if (rEntry.eMode == eMode) + { + aAny <<= rEntry.nApiMode; + break; + } + } + } + break; + default: + SAL_WARN("sc", "unsupported property"); + } + return aAny; +} + +void SAL_CALL ScConditionEntryObj::addPropertyChangeListener( const OUString& /* aPropertyName */, + const uno::Reference<beans::XPropertyChangeListener>& /* aListener */) +{ + SAL_WARN("sc", "not implemented"); +} + +void SAL_CALL ScConditionEntryObj::removePropertyChangeListener( const OUString& /* aPropertyName */, + const uno::Reference<beans::XPropertyChangeListener>& /* aListener */) +{ + SAL_WARN("sc", "not implemented"); +} + +void SAL_CALL ScConditionEntryObj::addVetoableChangeListener( const OUString&, + const uno::Reference<beans::XVetoableChangeListener>&) +{ + SAL_WARN("sc", "not implemented"); +} + +void SAL_CALL ScConditionEntryObj::removeVetoableChangeListener( const OUString&, + const uno::Reference<beans::XVetoableChangeListener>&) +{ + SAL_WARN("sc", "not implemented"); +} + +ScColorScaleFormatObj::ScColorScaleFormatObj(rtl::Reference<ScCondFormatObj> xParent, + const ScColorScaleFormat* pFormat): + mxParent(std::move(xParent)), + maPropSet(getColorScalePropSet()), + mpFormat(pFormat) +{ +} + +ScColorScaleFormatObj::~ScColorScaleFormatObj() +{ +} + +ScColorScaleFormat* ScColorScaleFormatObj::getCoreObject() +{ + ScConditionalFormat* pFormat = mxParent->getCoreObject(); + if (isObjectStillAlive(pFormat, mpFormat)) + return const_cast<ScColorScaleFormat*>(mpFormat); + + throw lang::IllegalArgumentException(); +} + +sal_Int32 ScColorScaleFormatObj::getType() +{ + return sheet::ConditionEntryType::COLORSCALE; +} + +uno::Reference<beans::XPropertySetInfo> SAL_CALL ScColorScaleFormatObj::getPropertySetInfo() +{ + static uno::Reference<beans::XPropertySetInfo> aRef( + new SfxItemPropertySetInfo( maPropSet.getPropertyMap() )); + return aRef; +} + +namespace { + +void setColorScaleEntry(ScColorScaleEntry* pEntry, uno::Reference<sheet::XColorScaleEntry> const & xEntry) +{ + ScColorScaleEntryType eType = ScColorScaleEntryType(); + sal_Int32 nApiType = xEntry->getType(); + bool bFound = false; + for (ColorScaleEntryTypeApiMap const & rEntry : aColorScaleEntryTypeMap) + { + if (rEntry.nApiType == nApiType) + { + eType = rEntry.eType; + bFound = true; + break; + } + } + + if (!bFound) + throw lang::IllegalArgumentException(); + + pEntry->SetType(eType); + pEntry->SetColor(Color(ColorTransparency, xEntry->getColor())); + switch (eType) + { + case COLORSCALE_FORMULA: + // TODO: Implement + break; + default: + { + double nVal = xEntry->getFormula().toDouble(); + pEntry->SetValue(nVal); + } + break; + } +} + +} + +void SAL_CALL ScColorScaleFormatObj::setPropertyValue( + const OUString& aPropertyName, const uno::Any& aValue ) +{ + SolarMutexGuard aGuard; + + const SfxItemPropertyMap& rPropertyMap = maPropSet.getPropertyMap(); // from derived class + const SfxItemPropertyMapEntry* pEntry = rPropertyMap.getByName( aPropertyName ); + if ( !pEntry ) + throw beans::UnknownPropertyException(aPropertyName); + + switch(pEntry->nWID) + { + case ColorScaleEntries: + { + uno::Sequence<uno::Reference<sheet::XColorScaleEntry> > aEntries; + if (!(aValue >>= aEntries)) + throw lang::IllegalArgumentException(); + + if (aEntries.getLength() < 2) + throw lang::IllegalArgumentException(); + + // TODO: we need to make sure that there are enough entries + size_t n = size_t(aEntries.getLength()); + for (size_t i = 0; i < n; ++i) + { + setColorScaleEntry(getCoreObject()->GetEntry(i), aEntries[i]); + } + + } + break; + default: + SAL_WARN("sc", "unknown property"); + } +} + +uno::Any SAL_CALL ScColorScaleFormatObj::getPropertyValue( const OUString& aPropertyName ) +{ + SolarMutexGuard aGuard; + + const SfxItemPropertyMap& rPropertyMap = maPropSet.getPropertyMap(); // from derived class + const SfxItemPropertyMapEntry* pEntry = rPropertyMap.getByName( aPropertyName ); + if ( !pEntry ) + throw beans::UnknownPropertyException(aPropertyName); + + uno::Any aAny; + + switch(pEntry->nWID) + { + case ColorScaleEntries: + { + uno::Sequence<uno::Reference<sheet::XColorScaleEntry> > aEntries(getCoreObject()->size()); + auto aEntriesRange = asNonConstRange(aEntries); + for (size_t i = 0; i < getCoreObject()->size(); ++i) + { + aEntriesRange[i] = new ScColorScaleEntryObj(this, i); + } + aAny <<= aEntries; + } + break; + default: + SAL_WARN("sc", "unknown property"); + } + + return aAny; +} + +void SAL_CALL ScColorScaleFormatObj::addPropertyChangeListener( const OUString& /* aPropertyName */, + const uno::Reference<beans::XPropertyChangeListener>& /* aListener */) +{ + SAL_WARN("sc", "not implemented"); +} + +void SAL_CALL ScColorScaleFormatObj::removePropertyChangeListener( const OUString& /* aPropertyName */, + const uno::Reference<beans::XPropertyChangeListener>& /* aListener */) +{ + SAL_WARN("sc", "not implemented"); +} + +void SAL_CALL ScColorScaleFormatObj::addVetoableChangeListener( const OUString&, + const uno::Reference<beans::XVetoableChangeListener>&) +{ + SAL_WARN("sc", "not implemented"); +} + +void SAL_CALL ScColorScaleFormatObj::removeVetoableChangeListener( const OUString&, + const uno::Reference<beans::XVetoableChangeListener>&) +{ + SAL_WARN("sc", "not implemented"); +} + +ScColorScaleEntryObj::ScColorScaleEntryObj(rtl::Reference<ScColorScaleFormatObj> xParent, + size_t nPos): + mxParent(std::move(xParent)), + mnPos(nPos) +{ +} + +ScColorScaleEntryObj::~ScColorScaleEntryObj() +{ +} + +ScColorScaleEntry* ScColorScaleEntryObj::getCoreObject() +{ + ScColorScaleFormat* pFormat = mxParent->getCoreObject(); + if (pFormat->size() <= mnPos) + throw lang::IllegalArgumentException(); + + return pFormat->GetEntry(mnPos); +} + +sal_Int32 ScColorScaleEntryObj::getColor() +{ + Color aColor = getCoreObject()->GetColor(); + return sal_Int32(aColor); +} + +void ScColorScaleEntryObj::setColor(sal_Int32 aColor) +{ + getCoreObject()->SetColor(Color(ColorTransparency, aColor)); +} + +sal_Int32 ScColorScaleEntryObj::getType() +{ + ScColorScaleEntry* pEntry = getCoreObject(); + for (ColorScaleEntryTypeApiMap const & rEntry : aColorScaleEntryTypeMap) + { + if (rEntry.eType == pEntry->GetType()) + { + return rEntry.nApiType; + } + } + + throw lang::IllegalArgumentException(); +} + +void ScColorScaleEntryObj::setType(sal_Int32 nType) +{ + ScColorScaleEntry* pEntry = getCoreObject(); + for (ColorScaleEntryTypeApiMap const & rEntry : aColorScaleEntryTypeMap) + { + if (rEntry.nApiType == nType) + { + pEntry->SetType(rEntry.eType); + return; + } + } + throw lang::IllegalArgumentException(); +} + +OUString ScColorScaleEntryObj::getFormula() +{ + ScColorScaleEntry* pEntry = getCoreObject(); + switch (pEntry->GetType()) + { + case COLORSCALE_FORMULA: + // TODO: Implement + break; + default: + return OUString::number(pEntry->GetValue()); + } + + return OUString(); +} + +void ScColorScaleEntryObj::setFormula(const OUString& rFormula) +{ + ScColorScaleEntry* pEntry = getCoreObject(); + switch (pEntry->GetType()) + { + case COLORSCALE_FORMULA: + // TODO: Implement + // pEntry->SetFormula(rFormula); + break; + default: + pEntry->SetValue(rFormula.toDouble()); + break; + } +} + + +ScDataBarFormatObj::ScDataBarFormatObj(rtl::Reference<ScCondFormatObj> xParent, + const ScDataBarFormat* pFormat): + mxParent(std::move(xParent)), + maPropSet(getDataBarPropSet()), + mpFormat(pFormat) +{ +} + +ScDataBarFormatObj::~ScDataBarFormatObj() +{ +} + +ScDataBarFormat* ScDataBarFormatObj::getCoreObject() +{ + ScConditionalFormat* pFormat = mxParent->getCoreObject(); + if (isObjectStillAlive(pFormat, mpFormat)) + return const_cast<ScDataBarFormat*>(mpFormat); + + throw lang::IllegalArgumentException(); +} + +sal_Int32 ScDataBarFormatObj::getType() +{ + return sheet::ConditionEntryType::DATABAR; +} + +uno::Reference<beans::XPropertySetInfo> SAL_CALL ScDataBarFormatObj::getPropertySetInfo() +{ + SolarMutexGuard aGuard; + static uno::Reference<beans::XPropertySetInfo> aRef( + new SfxItemPropertySetInfo( maPropSet.getPropertyMap() )); + return aRef; +} + +namespace { + +void setDataBarEntry(ScColorScaleEntry* pEntry, uno::Reference<sheet::XDataBarEntry> const & xEntry) +{ + ScColorScaleEntryType eType = ScColorScaleEntryType(); + sal_Int32 nApiType = xEntry->getType(); + bool bFound = false; + for (DataBarEntryTypeApiMap const & rEntry : aDataBarEntryTypeMap) + { + if (rEntry.nApiType == nApiType) + { + eType = rEntry.eType; + bFound = true; + break; + } + } + + if (!bFound) + throw lang::IllegalArgumentException(); + + pEntry->SetType(eType); + switch (eType) + { + case COLORSCALE_FORMULA: + // TODO: Implement + break; + default: + { + double nVal = xEntry->getFormula().toDouble(); + pEntry->SetValue(nVal); + } + break; + } +} + +} + +void SAL_CALL ScDataBarFormatObj::setPropertyValue( + const OUString& aPropertyName, const uno::Any& aValue ) +{ + SolarMutexGuard aGuard; + + const SfxItemPropertyMap& rPropertyMap = maPropSet.getPropertyMap(); // from derived class + const SfxItemPropertyMapEntry* pEntry = rPropertyMap.getByName( aPropertyName ); + if ( !pEntry ) + throw beans::UnknownPropertyException(aPropertyName); + + switch(pEntry->nWID) + { + case AxisPosition: + { + sal_Int32 nVal; + if (aValue >>= nVal) + { + for (DataBarAxisApiMap const & rEntry : aDataBarAxisMap) + { + if (rEntry.nApiPos == nVal) + { + getCoreObject()->GetDataBarData()->meAxisPosition = + rEntry.ePos; + break; + } + } + } + } + break; + case UseGradient: + { + bool bUseGradient = true; + if (aValue >>= bUseGradient) + { + getCoreObject()->GetDataBarData()->mbGradient = bUseGradient; + } + } + break; + case UseNegativeColor: + { + bool bUseNegativeColor = false; + if (aValue >>= bUseNegativeColor) + { + getCoreObject()->GetDataBarData()->mbNeg = bUseNegativeColor; + if (bUseNegativeColor && !getCoreObject()->GetDataBarData()->mxNegativeColor) + { + getCoreObject()->GetDataBarData()->mxNegativeColor = COL_AUTO; + } + } + } + break; + case DataBar_ShowValue: + { + bool bShowValue = true; + if (aValue >>= bShowValue) + { + getCoreObject()->GetDataBarData()->mbOnlyBar = !bShowValue; + } + } + break; + case DataBar_Color: + { + Color nColor = COL_AUTO; + if (aValue >>= nColor) + { + getCoreObject()->GetDataBarData()->maPositiveColor = nColor; + } + } + break; + case AxisColor: + { + Color nAxisColor = COL_AUTO; + if (aValue >>= nAxisColor) + { + getCoreObject()->GetDataBarData()->maAxisColor = nAxisColor; + } + } + break; + case NegativeColor: + { + Color nNegativeColor = COL_AUTO; + if (!(aValue >>= nNegativeColor) || !getCoreObject()->GetDataBarData()->mbNeg) + throw lang::IllegalArgumentException(); + + getCoreObject()->GetDataBarData()->mxNegativeColor = nNegativeColor; + + } + break; + case DataBarEntries: + { + uno::Sequence<uno::Reference<sheet::XDataBarEntry> > aEntries; + if (!(aValue >>= aEntries)) + throw lang::IllegalArgumentException(); + + if (aEntries.getLength() != 2) + throw lang::IllegalArgumentException(); + + setDataBarEntry(getCoreObject()->GetDataBarData()->mpLowerLimit.get(), + aEntries[0]); + setDataBarEntry(getCoreObject()->GetDataBarData()->mpUpperLimit.get(), + aEntries[1]); + + } + break; + case MinimumLength: + { + double nLength = 0; + if (!(aValue >>= nLength) || nLength >= 100 || nLength < 0) + throw lang::IllegalArgumentException(); + getCoreObject()->GetDataBarData()->mnMinLength = nLength; + + } + break; + case MaximumLength: + { + double nLength = 0; + if (!(aValue >>= nLength) || nLength > 100 || nLength <= 0) + throw lang::IllegalArgumentException(); + getCoreObject()->GetDataBarData()->mnMaxLength = nLength; + + } + break; + } +} + +uno::Any SAL_CALL ScDataBarFormatObj::getPropertyValue( const OUString& aPropertyName ) +{ + SolarMutexGuard aGuard; + + const SfxItemPropertyMap& rPropertyMap = maPropSet.getPropertyMap(); // from derived class + const SfxItemPropertyMapEntry* pEntry = rPropertyMap.getByName( aPropertyName ); + if ( !pEntry ) + throw beans::UnknownPropertyException(aPropertyName); + + uno::Any aAny; + switch(pEntry->nWID) + { + case AxisPosition: + { + databar::ScAxisPosition ePos = getCoreObject()->GetDataBarData()->meAxisPosition; + sal_Int32 nApiPos = sheet::DataBarAxis::AXIS_NONE; + for (DataBarAxisApiMap const & rEntry : aDataBarAxisMap) + { + if (rEntry.ePos == ePos) + { + nApiPos = rEntry.nApiPos; + } + } + + aAny <<= nApiPos; + } + break; + case UseGradient: + { + aAny <<= getCoreObject()->GetDataBarData()->mbGradient; + } + break; + case UseNegativeColor: + { + aAny <<= getCoreObject()->GetDataBarData()->mbNeg; + } + break; + case DataBar_ShowValue: + { + aAny <<= !getCoreObject()->GetDataBarData()->mbOnlyBar; + } + break; + case DataBar_Color: + { + aAny <<= getCoreObject()->GetDataBarData()->maPositiveColor; + } + break; + case AxisColor: + { + aAny <<= getCoreObject()->GetDataBarData()->maAxisColor; + } + break; + case NegativeColor: + { + if (getCoreObject()->GetDataBarData()->mbNeg && getCoreObject()->GetDataBarData()->mxNegativeColor) + { + aAny <<= *getCoreObject()->GetDataBarData()->mxNegativeColor; + } + } + break; + case DataBarEntries: + { + uno::Sequence<uno::Reference<sheet::XDataBarEntry> > aEntries + { + new ScDataBarEntryObj(this, 0), + new ScDataBarEntryObj(this, 1) + }; + aAny <<= aEntries; + } + break; + } + return aAny; +} + +void SAL_CALL ScDataBarFormatObj::addPropertyChangeListener( const OUString& /* aPropertyName */, + const uno::Reference<beans::XPropertyChangeListener>& /* aListener */) +{ + SAL_WARN("sc", "not implemented"); +} + +void SAL_CALL ScDataBarFormatObj::removePropertyChangeListener( const OUString& /* aPropertyName */, + const uno::Reference<beans::XPropertyChangeListener>& /* aListener */) +{ + SAL_WARN("sc", "not implemented"); +} + +void SAL_CALL ScDataBarFormatObj::addVetoableChangeListener( const OUString&, + const uno::Reference<beans::XVetoableChangeListener>&) +{ + SAL_WARN("sc", "not implemented"); +} + +void SAL_CALL ScDataBarFormatObj::removeVetoableChangeListener( const OUString&, + const uno::Reference<beans::XVetoableChangeListener>&) +{ + SAL_WARN("sc", "not implemented"); +} + +ScDataBarEntryObj::ScDataBarEntryObj(rtl::Reference<ScDataBarFormatObj> xParent, + size_t nPos): + mxParent(std::move(xParent)), + mnPos(nPos) +{ +} + +ScDataBarEntryObj::~ScDataBarEntryObj() +{ +} + +ScColorScaleEntry* ScDataBarEntryObj::getCoreObject() +{ + ScDataBarFormat* pFormat = mxParent->getCoreObject(); + ScColorScaleEntry* pEntry; + if (mnPos == 0) + pEntry = pFormat->GetDataBarData()->mpLowerLimit.get(); + else + pEntry = pFormat->GetDataBarData()->mpUpperLimit.get(); + + return pEntry; +} + +sal_Int32 ScDataBarEntryObj::getType() +{ + ScColorScaleEntry* pEntry = getCoreObject(); + for (DataBarEntryTypeApiMap const & rEntry : aDataBarEntryTypeMap) + { + if (rEntry.eType == pEntry->GetType()) + { + return rEntry.nApiType; + } + } + + throw lang::IllegalArgumentException(); +} + +void ScDataBarEntryObj::setType(sal_Int32 nType) +{ + ScColorScaleEntry* pEntry = getCoreObject(); + for (DataBarEntryTypeApiMap const & rEntry : aDataBarEntryTypeMap) + { + if (rEntry.nApiType == nType) + { + pEntry->SetType(rEntry.eType); + return; + } + } + throw lang::IllegalArgumentException(); +} + +OUString ScDataBarEntryObj::getFormula() +{ + ScColorScaleEntry* pEntry = getCoreObject(); + switch (pEntry->GetType()) + { + case COLORSCALE_FORMULA: + // TODO: Implement + break; + default: + return OUString::number(pEntry->GetValue()); + } + + return OUString(); +} + +void ScDataBarEntryObj::setFormula(const OUString& rFormula) +{ + ScColorScaleEntry* pEntry = getCoreObject(); + switch (pEntry->GetType()) + { + case COLORSCALE_FORMULA: + // TODO: Implement + // pEntry->SetFormula(rFormula); + break; + default: + pEntry->SetValue(rFormula.toDouble()); + break; + } +} + + +ScIconSetFormatObj::ScIconSetFormatObj(rtl::Reference<ScCondFormatObj> xParent, + const ScIconSetFormat* pFormat): + mxParent(std::move(xParent)), + maPropSet(getIconSetPropSet()), + mpFormat(pFormat) +{ +} + +ScIconSetFormatObj::~ScIconSetFormatObj() +{ +} + +ScIconSetFormat* ScIconSetFormatObj::getCoreObject() +{ + ScConditionalFormat* pFormat = mxParent->getCoreObject(); + if (isObjectStillAlive(pFormat, mpFormat)) + return const_cast<ScIconSetFormat*>(mpFormat); + + throw lang::IllegalArgumentException(); +} + +sal_Int32 ScIconSetFormatObj::getType() +{ + return sheet::ConditionEntryType::ICONSET; +} + +uno::Reference<beans::XPropertySetInfo> SAL_CALL ScIconSetFormatObj::getPropertySetInfo() +{ + SolarMutexGuard aGuard; + static uno::Reference<beans::XPropertySetInfo> aRef( + new SfxItemPropertySetInfo( maPropSet.getPropertyMap() )); + return aRef; +} + +namespace { + +void setIconSetEntry(ScIconSetFormat* pFormat, uno::Reference<sheet::XIconSetEntry> const & xEntry, size_t nPos) +{ + ScIconSetFormatData* pData = pFormat->GetIconSetData(); + ScColorScaleEntryType eType = ScColorScaleEntryType(); + sal_Int32 nApiType = xEntry->getType(); + bool bFound = false; + for (IconSetEntryTypeApiMap const & rEntry : aIconSetEntryTypeMap) + { + if (rEntry.nApiType == nApiType) + { + eType = rEntry.eType; + bFound = true; + break; + } + } + + if (!bFound) + throw lang::IllegalArgumentException(); + + pData->m_Entries[nPos]->SetType(eType); + switch (eType) + { + case COLORSCALE_FORMULA: + // TODO: Implement + break; + default: + { + double nVal = xEntry->getFormula().toDouble(); + pData->m_Entries[nPos]->SetValue(nVal); + } + break; + } +} + +} + +void SAL_CALL ScIconSetFormatObj::setPropertyValue( + const OUString& aPropertyName, const uno::Any& aValue ) +{ + SolarMutexGuard aGuard; + + const SfxItemPropertyMap& rPropertyMap = maPropSet.getPropertyMap(); // from derived class + const SfxItemPropertyMapEntry* pEntry = rPropertyMap.getByName( aPropertyName ); + if ( !pEntry ) + throw beans::UnknownPropertyException(aPropertyName); + + switch(pEntry->nWID) + { + case ShowValue: + { + bool bShowValue = true; + aValue >>= bShowValue; + getCoreObject()->GetIconSetData()->mbShowValue = bShowValue; + } + break; + case Reverse: + { + bool bReverse = false; + aValue >>= bReverse; + getCoreObject()->GetIconSetData()->mbReverse = bReverse; + } + break; + case Icons: + { + sal_Int32 nApiType = -1; + aValue >>= nApiType; + ScIconSetType eType = IconSet_3Arrows; + bool bFound = false; + for (const IconSetTypeApiMap & rEntry : aIconSetApiMap) + { + if (rEntry.nApiType == nApiType) + { + eType = rEntry.eType; + bFound = true; + break; + } + } + + if (!bFound) + { + throw lang::IllegalArgumentException(); + } + + // TODO: we need to make sure that there are enough entries + getCoreObject()->GetIconSetData()->eIconSetType = eType; + } + break; + case IconSetEntries: + { + uno::Sequence<uno::Reference<sheet::XIconSetEntry> > aEntries; + if (!(aValue >>= aEntries)) + throw lang::IllegalArgumentException(); + + // TODO: we need to check that the number of entries + // corresponds to the icon type + sal_Int32 nLength = aEntries.getLength(); + for (size_t i = 0; i < o3tl::make_unsigned(nLength); ++i) + { + setIconSetEntry(getCoreObject(), aEntries[i], i); + } + + } + break; + default: + break; + } +} + +uno::Any SAL_CALL ScIconSetFormatObj::getPropertyValue( const OUString& aPropertyName ) +{ + SolarMutexGuard aGuard; + + const SfxItemPropertyMap& rPropertyMap = maPropSet.getPropertyMap(); // from derived class + const SfxItemPropertyMapEntry* pEntry = rPropertyMap.getByName( aPropertyName ); + if ( !pEntry ) + throw beans::UnknownPropertyException(aPropertyName); + + uno::Any aAny; + + switch(pEntry->nWID) + { + case ShowValue: + aAny <<= getCoreObject()->GetIconSetData()->mbShowValue; + break; + case Reverse: + aAny <<= getCoreObject()->GetIconSetData()->mbReverse; + break; + case Icons: + { + ScIconSetType eType = getCoreObject()->GetIconSetData()->eIconSetType; + for (const IconSetTypeApiMap & rEntry : aIconSetApiMap) + { + if (rEntry.eType == eType) + { + aAny <<= rEntry.nApiType; + break; + } + } + } + break; + case IconSetEntries: + { + size_t nSize = getCoreObject()->size(); + uno::Sequence<uno::Reference<sheet::XIconSetEntry> > aEntries(nSize); + auto aEntriesRange = asNonConstRange(aEntries); + for (size_t i = 0; i < nSize; ++i) + { + aEntriesRange[i] = new ScIconSetEntryObj(this, i); + } + aAny <<= aEntries; + } + break; + default: + SAL_WARN("sc", "unknown property"); + } + return aAny; +} + +void SAL_CALL ScIconSetFormatObj::addPropertyChangeListener( const OUString& /* aPropertyName */, + const uno::Reference<beans::XPropertyChangeListener>& /* aListener */) +{ + SAL_WARN("sc", "not implemented"); +} + +void SAL_CALL ScIconSetFormatObj::removePropertyChangeListener( const OUString& /* aPropertyName */, + const uno::Reference<beans::XPropertyChangeListener>& /* aListener */) +{ + SAL_WARN("sc", "not implemented"); +} + +void SAL_CALL ScIconSetFormatObj::addVetoableChangeListener( const OUString&, + const uno::Reference<beans::XVetoableChangeListener>&) +{ + SAL_WARN("sc", "not implemented"); +} + +void SAL_CALL ScIconSetFormatObj::removeVetoableChangeListener( const OUString&, + const uno::Reference<beans::XVetoableChangeListener>&) +{ + SAL_WARN("sc", "not implemented"); +} + +ScIconSetEntryObj::ScIconSetEntryObj(rtl::Reference<ScIconSetFormatObj> xParent, + size_t nPos): + mxParent(std::move(xParent)), + mnPos(nPos) +{ +} + +ScIconSetEntryObj::~ScIconSetEntryObj() +{ +} + +ScColorScaleEntry* ScIconSetEntryObj::getCoreObject() +{ + ScIconSetFormat* pFormat = mxParent->getCoreObject(); + if (pFormat->GetIconSetData()->m_Entries.size() <= mnPos) + throw lang::IllegalArgumentException(); + + return pFormat->GetIconSetData()->m_Entries[mnPos].get(); +} + +sal_Int32 ScIconSetEntryObj::getType() +{ + ScColorScaleEntry* pEntry = getCoreObject(); + // the first entry always is minimum + if (mnPos == 0) + return sheet::IconSetFormatEntry::ICONSET_MIN; + + for (IconSetEntryTypeApiMap const & rEntry : aIconSetEntryTypeMap) + { + if (rEntry.eType == pEntry->GetType()) + { + return rEntry.nApiType; + } + } + + throw lang::IllegalArgumentException(); +} + +void ScIconSetEntryObj::setType(sal_Int32 nType) +{ + // first entry is always MIN + if (mnPos == 0) + return; + + ScColorScaleEntry* pEntry = getCoreObject(); + for (IconSetEntryTypeApiMap const & rEntry : aIconSetEntryTypeMap) + { + if (rEntry.nApiType == nType) + { + pEntry->SetType(rEntry.eType); + return; + } + } + throw lang::IllegalArgumentException(); +} + +OUString ScIconSetEntryObj::getFormula() +{ + ScColorScaleEntry* pEntry = getCoreObject(); + switch (pEntry->GetType()) + { + case COLORSCALE_FORMULA: + // TODO: Implement + break; + default: + return OUString::number(pEntry->GetValue()); + } + + return OUString(); +} + +void ScIconSetEntryObj::setFormula(const OUString& rFormula) +{ + ScColorScaleEntry* pEntry = getCoreObject(); + switch (pEntry->GetType()) + { + case COLORSCALE_FORMULA: + // TODO: Implement + // pEntry->SetFormula(rFormula); + break; + default: + pEntry->SetValue(rFormula.toDouble()); + break; + } +} + +ScCondDateFormatObj::ScCondDateFormatObj(rtl::Reference<ScCondFormatObj> xParent, + const ScCondDateFormatEntry* pFormat): + mxParent(std::move(xParent)), + maPropSet(getCondDatePropSet()), + mpFormat(pFormat) +{ +} + +ScCondDateFormatObj::~ScCondDateFormatObj() +{ +} + +ScCondDateFormatEntry* ScCondDateFormatObj::getCoreObject() +{ + ScConditionalFormat* pFormat = mxParent->getCoreObject(); + if (isObjectStillAlive(pFormat, mpFormat)) + return const_cast<ScCondDateFormatEntry*>(mpFormat); + + throw lang::IllegalArgumentException(); +} + +sal_Int32 ScCondDateFormatObj::getType() +{ + return sheet::ConditionEntryType::DATE; +} + +uno::Reference<beans::XPropertySetInfo> SAL_CALL ScCondDateFormatObj::getPropertySetInfo() +{ + SolarMutexGuard aGuard; + static uno::Reference<beans::XPropertySetInfo> aRef( + new SfxItemPropertySetInfo( maPropSet.getPropertyMap() )); + return aRef; +} + +void SAL_CALL ScCondDateFormatObj::setPropertyValue( + const OUString& aPropertyName, const uno::Any& aValue ) +{ + SolarMutexGuard aGuard; + + const SfxItemPropertyMap& rPropertyMap = maPropSet.getPropertyMap(); // from derived class + const SfxItemPropertyMapEntry* pEntry = rPropertyMap.getByName( aPropertyName ); + if ( !pEntry ) + throw beans::UnknownPropertyException(aPropertyName); + + switch(pEntry->nWID) + { + case Date_StyleName: + { + OUString aStyleName; + if (!(aValue >>= aStyleName)) + throw lang::IllegalArgumentException(); + + getCoreObject()->SetStyleName(aStyleName); + + } + break; + case DateType: + { + sal_Int32 nApiType = -1; + if (!(aValue >>= nApiType)) + throw lang::IllegalArgumentException(); + + for (DateTypeApiMap const & rEntry : aDateTypeApiMap) + { + if (rEntry.nApiType == nApiType) + { + getCoreObject()->SetDateType(rEntry.eType); + break; + } + } + } + break; + default: + break; + } +} + +uno::Any SAL_CALL ScCondDateFormatObj::getPropertyValue( const OUString& aPropertyName ) +{ + SolarMutexGuard aGuard; + + const SfxItemPropertyMap& rPropertyMap = maPropSet.getPropertyMap(); // from derived class + const SfxItemPropertyMapEntry* pEntry = rPropertyMap.getByName( aPropertyName ); + if ( !pEntry ) + throw beans::UnknownPropertyException(aPropertyName); + + uno::Any aAny; + + switch(pEntry->nWID) + { + case Date_StyleName: + { + OUString aStyleName = getCoreObject()->GetStyleName(); + aAny <<= aStyleName; + } + break; + case DateType: + { + condformat::ScCondFormatDateType eType = getCoreObject()->GetDateType(); + for (DateTypeApiMap const & rEntry : aDateTypeApiMap) + { + if (rEntry.eType == eType) + { + aAny <<= rEntry.nApiType; + break; + } + } + } + break; + default: + SAL_WARN("sc", "unknown property"); + } + return aAny; +} + +void SAL_CALL ScCondDateFormatObj::addPropertyChangeListener( const OUString& /* aPropertyName */, + const uno::Reference<beans::XPropertyChangeListener>& /* aListener */) +{ + SAL_WARN("sc", "not implemented"); +} + +void SAL_CALL ScCondDateFormatObj::removePropertyChangeListener( const OUString& /* aPropertyName */, + const uno::Reference<beans::XPropertyChangeListener>& /* aListener */) +{ + SAL_WARN("sc", "not implemented"); +} + +void SAL_CALL ScCondDateFormatObj::addVetoableChangeListener( const OUString&, + const uno::Reference<beans::XVetoableChangeListener>&) +{ + SAL_WARN("sc", "not implemented"); +} + +void SAL_CALL ScCondDateFormatObj::removeVetoableChangeListener( const OUString&, + const uno::Reference<beans::XVetoableChangeListener>&) +{ + SAL_WARN("sc", "not implemented"); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/unoobj/confuno.cxx b/sc/source/ui/unoobj/confuno.cxx new file mode 100644 index 0000000000..4c69e65645 --- /dev/null +++ b/sc/source/ui/unoobj/confuno.cxx @@ -0,0 +1,661 @@ +/* -*- 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 <sal/config.h> + +#include <utility> + +#include <config_features.h> + +#include <confuno.hxx> +#include <unonames.hxx> +#include <docsh.hxx> +#include <miscuno.hxx> +#include <forbiuno.hxx> +#include <appoptio.hxx> +#include <viewopti.hxx> +#include <docpool.hxx> +#include <sc.hrc> +#include <scmod.hxx> + +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/document/LinkUpdateModes.hpp> +#include <cppuhelper/supportsservice.hxx> +#include <formula/grammar.hxx> +#include <sfx2/printer.hxx> +#include <svl/itemset.hxx> +#include <vcl/svapp.hxx> +#include <tools/stream.hxx> + +using namespace com::sun::star; + +constexpr OUString SCSAVEVERSION = u"SaveVersionOnClose"_ustr; + +static std::span<const SfxItemPropertyMapEntry> lcl_GetConfigPropertyMap() +{ + static const SfxItemPropertyMapEntry aConfigPropertyMap_Impl[] = + { + { SC_UNO_SHOWZERO, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_SHOWNOTES, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_SHOWFORMULASMARKS, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_SHOWGRID, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_GRIDCOLOR, 0, cppu::UnoType<sal_Int32>::get(), 0, 0}, + { SC_UNO_SHOWPAGEBR, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNONAME_LINKUPD, 0, cppu::UnoType<sal_Int16>::get(), 0, 0}, + { SC_UNO_COLROWHDR, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_SHEETTABS, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_OUTLSYMB, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_SNAPTORASTER, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_RASTERVIS, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_RASTERRESX, 0, cppu::UnoType<sal_Int32>::get(), 0, 0}, + { SC_UNO_RASTERRESY, 0, cppu::UnoType<sal_Int32>::get(), 0, 0}, + { SC_UNO_RASTERSUBX, 0, cppu::UnoType<sal_Int32>::get(), 0, 0}, + { SC_UNO_RASTERSUBY, 0, cppu::UnoType<sal_Int32>::get(), 0, 0}, + { SC_UNO_RASTERSYNC, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_AUTOCALC, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_PRINTERNAME, 0, cppu::UnoType<OUString>::get(), 0, 0}, + { SC_UNO_PRINTERSETUP, 0, cppu::UnoType<uno::Sequence<sal_Int8>>::get(), 0, 0}, + { SC_UNO_PRINTERPAPER, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_APPLYDOCINF, 0, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNO_SAVE_THUMBNAIL, 0, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNO_FORBIDDEN, 0, cppu::UnoType<i18n::XForbiddenCharacters>::get(), beans::PropertyAttribute::READONLY, 0}, + { SC_UNO_CHARCOMP, 0, cppu::UnoType<sal_Int16>::get(), 0, 0}, + { SC_UNO_ASIANKERN, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SCSAVEVERSION, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_UPDTEMPL, 0, cppu::UnoType<bool>::get(), 0, 0}, + /*Stampit enable/disable print cancel */ + { SC_UNO_ALLOWPRINTJOBCANCEL, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_LOADREADONLY, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_SHAREDOC, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_MODIFYPASSWORDINFO, 0, cppu::UnoType<uno::Sequence< beans::PropertyValue >>::get(), 0, 0}, + { SC_UNO_MODIFYPASSWORDHASH, 0, cppu::UnoType<sal_Int32>::get(), 0, 0}, + { SC_UNO_EMBED_FONTS, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_EMBED_ONLY_USED_FONTS, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_EMBED_FONT_SCRIPT_LATIN, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_EMBED_FONT_SCRIPT_ASIAN, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_EMBED_FONT_SCRIPT_COMPLEX, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_IMAGE_PREFERRED_DPI, 0, cppu::UnoType<sal_Int32>::get(), 0, 0}, + { SC_UNO_SYNTAXSTRINGREF, 0, cppu::UnoType<sal_Int16>::get(), 0, 0}, + }; + return aConfigPropertyMap_Impl; +} + +ScDocumentConfiguration::ScDocumentConfiguration(ScDocShell* pDocSh) + : pDocShell(pDocSh) , + aPropSet ( lcl_GetConfigPropertyMap() ) +{ + pDocShell->GetDocument().AddUnoObject(*this); +} + +ScDocumentConfiguration::~ScDocumentConfiguration() +{ + SolarMutexGuard g; + + if (pDocShell) + pDocShell->GetDocument().RemoveUnoObject(*this); +} + +void ScDocumentConfiguration::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + // reference update does not matter here + + if ( rHint.GetId() == SfxHintId::Dying ) + { + pDocShell = nullptr; + } +} + +// XPropertySet + +uno::Reference<beans::XPropertySetInfo> SAL_CALL ScDocumentConfiguration::getPropertySetInfo() +{ + SolarMutexGuard aGuard; + static uno::Reference<beans::XPropertySetInfo> aRef( + new SfxItemPropertySetInfo( aPropSet.getPropertyMap() )); + return aRef; +} + +void SAL_CALL ScDocumentConfiguration::setPropertyValue( + const OUString& aPropertyName, const uno::Any& aValue ) +{ + SolarMutexGuard aGuard; + + if(!pDocShell) + throw uno::RuntimeException(); + + ScDocument& rDoc = pDocShell->GetDocument(); + bool bUpdateHeights = false; + + ScViewOptions aViewOpt(rDoc.GetViewOptions()); + + /*Stampit enable/disable print cancel */ + if ( aPropertyName == SC_UNO_ALLOWPRINTJOBCANCEL ) + pDocShell->Stamp_SetPrintCancelState( ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + /*Stampit enable/disable print cancel */ + + else if ( aPropertyName == SC_UNO_SHOWZERO ) + aViewOpt.SetOption(VOPT_NULLVALS, ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + else if ( aPropertyName == SC_UNO_SHOWNOTES ) + aViewOpt.SetOption(VOPT_NOTES, ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + else if ( aPropertyName == SC_UNO_SHOWFORMULASMARKS ) + aViewOpt.SetOption(VOPT_FORMULAS_MARKS, ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + else if ( aPropertyName == SC_UNO_SHOWGRID ) + aViewOpt.SetOption(VOPT_GRID, ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + else if ( aPropertyName == SC_UNO_GRIDCOLOR ) + { + Color aColor; + if (aValue >>= aColor) + aViewOpt.SetGridColor(aColor, OUString()); + } + else if ( aPropertyName == SC_UNO_SHOWPAGEBR ) + aViewOpt.SetOption(VOPT_PAGEBREAKS, ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + else if ( aPropertyName == SC_UNONAME_LINKUPD ) + { + // XXX NOTE: this is the css::document::Settings property + // LinkUpdateMode, not the css::sheet::XGlobalSheetSettings + // attribute LinkUpdateMode. + sal_Int16 n; + if (!(aValue >>= n) || n < css::document::LinkUpdateModes::NEVER || + n > css::document::LinkUpdateModes::GLOBAL_SETTING) + { + throw css::lang::IllegalArgumentException( + ("LinkUpdateMode property value must be a SHORT with a value in" + " the range of the css::document::LinkUpdateModes constants"), + css::uno::Reference<css::uno::XInterface>(), -1); + } + ScLkUpdMode eMode; + switch (n) + { + case css::document::LinkUpdateModes::NEVER: + eMode = LM_NEVER; + break; + case css::document::LinkUpdateModes::MANUAL: + eMode = LM_ON_DEMAND; + break; + case css::document::LinkUpdateModes::AUTO: + eMode = LM_ALWAYS; + break; + case css::document::LinkUpdateModes::GLOBAL_SETTING: + default: + eMode = SC_MOD()->GetAppOptions().GetLinkMode(); + break; + } + rDoc.SetLinkMode( eMode ); + } + else if ( aPropertyName == SC_UNO_COLROWHDR ) + aViewOpt.SetOption(VOPT_HEADER, ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + else if ( aPropertyName == SC_UNO_SHEETTABS ) + aViewOpt.SetOption(VOPT_TABCONTROLS, ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + else if ( aPropertyName == SC_UNO_OUTLSYMB ) + aViewOpt.SetOption(VOPT_OUTLINER, ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + else if ( aPropertyName == SC_UNO_AUTOCALC ) + rDoc.SetAutoCalc( ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + else if ( aPropertyName == SC_UNO_PRINTERNAME ) + { + OUString sPrinterName; + if ( !(aValue >>= sPrinterName) ) + throw lang::IllegalArgumentException(); + + // #i75610# if the name is empty, do nothing (don't create any printer) + if ( !sPrinterName.isEmpty() && pDocShell->GetCreateMode() != SfxObjectCreateMode::EMBEDDED ) + { + SfxPrinter* pPrinter = pDocShell->GetPrinter(); + if (!pPrinter) + throw uno::RuntimeException(); + + if (pPrinter->GetName() != sPrinterName) + { + VclPtrInstance<SfxPrinter> pNewPrinter( pPrinter->GetOptions().Clone(), sPrinterName ); + if (pNewPrinter->IsKnown()) + pDocShell->SetPrinter( pNewPrinter, SfxPrinterChangeFlags::PRINTER ); + else + pNewPrinter.disposeAndClear(); + } + + } + + } + else if ( aPropertyName == SC_UNO_PRINTERSETUP ) + { + uno::Sequence<sal_Int8> aSequence; + if ( aValue >>= aSequence ) + { + sal_uInt32 nSize = aSequence.getLength(); + // #i75610# if the sequence is empty, do nothing (don't create any printer) + if ( nSize != 0 ) + { + SvMemoryStream aStream (aSequence.getArray(), nSize, StreamMode::READ ); + aStream.Seek ( STREAM_SEEK_TO_BEGIN ); + auto pSet = std::make_unique<SfxItemSetFixed + <SID_PRINTER_NOTFOUND_WARN, SID_PRINTER_NOTFOUND_WARN, + SID_PRINTER_CHANGESTODOC, SID_PRINTER_CHANGESTODOC, + SID_PRINT_SELECTEDSHEET, SID_PRINT_SELECTEDSHEET, + SID_SCPRINTOPTIONS, SID_SCPRINTOPTIONS>>( *rDoc.GetPool()); + + SfxPrinter* pPrinter = pDocShell->GetPrinter(); + bool bPreferPrinterPapersize = false; + if ( pPrinter ) + bPreferPrinterPapersize = pPrinter->GetPrinterSettingsPreferred(); + + VclPtr<SfxPrinter> pTempPrinter = SfxPrinter::Create( aStream, std::move(pSet) ); + pTempPrinter->SetPrinterSettingsPreferred( bPreferPrinterPapersize ); + pDocShell->SetPrinter( pTempPrinter ); + } + } + } + else if ( aPropertyName == SC_UNO_PRINTERPAPER ) + { + bool bPreferPrinterPapersize; + if( aValue >>= bPreferPrinterPapersize ) + { + if( pDocShell->GetCreateMode() != SfxObjectCreateMode::EMBEDDED ) + { + SfxPrinter *pTempPrinter = pDocShell->GetPrinter( true ); + if (pTempPrinter) + pTempPrinter->SetPrinterSettingsPreferred( bPreferPrinterPapersize ); + } + } + } + else if ( aPropertyName == SC_UNO_APPLYDOCINF ) + { + bool bTmp=true; + if ( aValue >>= bTmp ) + pDocShell->SetUseUserData( bTmp ); + } + else if ( aPropertyName == SC_UNO_SAVE_THUMBNAIL) + { + bool bTmp = true; + if (aValue >>= bTmp) + pDocShell->SetUseThumbnailSave( bTmp ); + } + else if ( aPropertyName == SC_UNO_FORBIDDEN ) + { + // read-only - should not be set + } + else if ( aPropertyName == SC_UNO_CHARCOMP ) + { + // Int16 contains CharacterCompressionType values + sal_Int16 nUno = ScUnoHelpFunctions::GetInt16FromAny( aValue ); + rDoc.SetAsianCompression( static_cast<CharCompressType>(nUno) ); + bUpdateHeights = true; + } + else if ( aPropertyName == SC_UNO_ASIANKERN ) + { + rDoc.SetAsianKerning( ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + bUpdateHeights = true; + } + else if ( aPropertyName == SCSAVEVERSION ) + { + bool bTmp=false; + if ( aValue >>= bTmp ) + pDocShell->SetSaveVersionOnClose( bTmp ); + } + else if ( aPropertyName == SC_UNO_UPDTEMPL ) + { + bool bTmp=true; + if ( aValue >>= bTmp ) + pDocShell->SetQueryLoadTemplate( bTmp ); + } + else if ( aPropertyName == SC_UNO_LOADREADONLY ) + { + bool bTmp=false; + if ( aValue >>= bTmp ) + pDocShell->SetLoadReadonly( bTmp ); + } + else if ( aPropertyName == SC_UNO_SHAREDOC ) + { +#if HAVE_FEATURE_MULTIUSER_ENVIRONMENT + bool bDocShared = false; + if ( aValue >>= bDocShared ) + { + pDocShell->SetSharedXMLFlag( bDocShared ); + } +#endif + } + else if ( aPropertyName == SC_UNO_MODIFYPASSWORDINFO ) + { + uno::Sequence< beans::PropertyValue > aInfo; + if ( !( aValue >>= aInfo ) ) + throw lang::IllegalArgumentException( + "Value of type Sequence<PropertyValue> expected!", + uno::Reference< uno::XInterface >(), + 2 ); + + if ( !pDocShell->SetModifyPasswordInfo( aInfo ) ) + throw beans::PropertyVetoException( + "The hash is not allowed to be changed now!" ); + } + else if (aPropertyName == SC_UNO_MODIFYPASSWORDHASH) + { + sal_Int32 nHash; + if (!(aValue >>= nHash)) + throw lang::IllegalArgumentException("Value of type sal_Int32 expected!", + uno::Reference<uno::XInterface>(), 2); + + if (!pDocShell->SetModifyPasswordHash(nHash)) + throw beans::PropertyVetoException("The hash is not allowed to be changed now!"); + } + else if (aPropertyName == SC_UNO_EMBED_FONTS) + { + bool bVal = aValue.has<bool>() && aValue.get<bool>(); + rDoc.SetEmbedFonts(bVal); + } + else if (aPropertyName == SC_UNO_EMBED_ONLY_USED_FONTS) + { + bool bVal = aValue.has<bool>() && aValue.get<bool>(); + rDoc.SetEmbedUsedFontsOnly(bVal); + } + else if (aPropertyName == SC_UNO_EMBED_FONT_SCRIPT_LATIN) + { + bool bVal = aValue.has<bool>() && aValue.get<bool>(); + rDoc.SetEmbedFontScriptLatin(bVal); + } + else if (aPropertyName == SC_UNO_EMBED_FONT_SCRIPT_ASIAN) + { + bool bVal = aValue.has<bool>() && aValue.get<bool>(); + rDoc.SetEmbedFontScriptAsian(bVal); + } + else if (aPropertyName == SC_UNO_EMBED_FONT_SCRIPT_COMPLEX) + { + bool bVal = aValue.has<bool>() && aValue.get<bool>(); + rDoc.SetEmbedFontScriptComplex(bVal); + } + else if ( aPropertyName == SC_UNO_SYNTAXSTRINGREF ) + { + ScCalcConfig aCalcConfig = rDoc.GetCalcConfig(); + sal_Int16 nUno = 0; + + if( aValue >>= nUno ) + { + switch (nUno) + { + case 0: // CONV_OOO + case 2: // CONV_XL_A1 + case 3: // CONV_XL_R1C1 + case 7: // CONV_A1_XL_A1 + aCalcConfig.SetStringRefSyntax( static_cast<formula::FormulaGrammar::AddressConvention>( nUno ) ); + break; + default: + aCalcConfig.SetStringRefSyntax( formula::FormulaGrammar::CONV_UNSPECIFIED ); + break; + + } + rDoc.SetCalcConfig( aCalcConfig ); + } + } + else if (aPropertyName == SC_UNO_IMAGE_PREFERRED_DPI) + { + if (aValue.has<sal_Int32>()) + { + rDoc.SetImagePreferredDPI(aValue.get<sal_Int32>()); + } + } + else + { + ScGridOptions aGridOpt(aViewOpt.GetGridOptions()); + if ( aPropertyName == SC_UNO_SNAPTORASTER ) + aGridOpt.SetUseGridSnap( ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + else if ( aPropertyName == SC_UNO_RASTERVIS ) + aGridOpt.SetGridVisible( ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + else if ( aPropertyName == SC_UNO_RASTERRESX ) + aGridOpt.SetFieldDrawX( static_cast <sal_uInt32> ( ScUnoHelpFunctions::GetInt32FromAny( aValue ) ) ); + else if ( aPropertyName == SC_UNO_RASTERRESY ) + aGridOpt.SetFieldDrawY( static_cast <sal_uInt32> ( ScUnoHelpFunctions::GetInt32FromAny( aValue ) ) ); + else if ( aPropertyName == SC_UNO_RASTERSUBX ) + aGridOpt.SetFieldDivisionX( static_cast <sal_uInt32> ( ScUnoHelpFunctions::GetInt32FromAny( aValue ) ) ); + else if ( aPropertyName == SC_UNO_RASTERSUBY ) + aGridOpt.SetFieldDivisionY( static_cast <sal_uInt32> ( ScUnoHelpFunctions::GetInt32FromAny( aValue ) ) ); + else if ( aPropertyName == SC_UNO_RASTERSYNC ) + aGridOpt.SetSynchronize( ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + else + throw beans::UnknownPropertyException(aPropertyName); + aViewOpt.SetGridOptions(aGridOpt); + } + rDoc.SetViewOptions(aViewOpt); + + if ( bUpdateHeights && !rDoc.IsImportingXML() ) + { + // update automatic row heights and repaint + SCTAB nTabCount = rDoc.GetTableCount(); + for (SCTAB nTab=0; nTab<nTabCount; nTab++) + if ( !pDocShell->AdjustRowHeight( 0, rDoc.MaxRow(), nTab ) ) + pDocShell->PostPaint(ScRange(0, 0, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab), PaintPartFlags::Grid); + pDocShell->SetDocumentModified(); + } + +} + +uno::Any SAL_CALL ScDocumentConfiguration::getPropertyValue( const OUString& aPropertyName ) +{ + SolarMutexGuard aGuard; + uno::Any aRet; + + if(!pDocShell) + throw uno::RuntimeException(); + + ScDocument& rDoc = pDocShell->GetDocument(); + const ScViewOptions& aViewOpt = rDoc.GetViewOptions(); + + /*Stampit enable/disable print cancel */ + if ( aPropertyName == SC_UNO_ALLOWPRINTJOBCANCEL ) + aRet <<= pDocShell->Stamp_GetPrintCancelState(); + /*Stampit enable/disable print cancel */ + + else if ( aPropertyName == SC_UNO_SHOWZERO ) + aRet <<= aViewOpt.GetOption( VOPT_NULLVALS ); + else if ( aPropertyName == SC_UNO_SHOWNOTES ) + aRet <<= aViewOpt.GetOption( VOPT_NOTES ); + else if ( aPropertyName == SC_UNO_SHOWFORMULASMARKS ) + aRet <<= aViewOpt.GetOption( VOPT_FORMULAS_MARKS ); + else if ( aPropertyName == SC_UNO_SHOWGRID ) + aRet <<= aViewOpt.GetOption( VOPT_GRID ); + else if ( aPropertyName == SC_UNO_GRIDCOLOR ) + { + OUString aColorName; + Color aColor = aViewOpt.GetGridColor(&aColorName); + aRet <<= aColor; + } + else if ( aPropertyName == SC_UNO_SHOWPAGEBR ) + aRet <<= aViewOpt.GetOption( VOPT_PAGEBREAKS ); + else if ( aPropertyName == SC_UNONAME_LINKUPD ) + { + sal_Int16 nLUM; + switch (rDoc.GetLinkMode()) + { + case LM_ALWAYS: + nLUM = css::document::LinkUpdateModes::AUTO; + break; + case LM_NEVER: + nLUM = css::document::LinkUpdateModes::NEVER; + break; + case LM_ON_DEMAND: + nLUM = css::document::LinkUpdateModes::MANUAL; + break; + case LM_UNKNOWN: + default: + nLUM = css::document::LinkUpdateModes::GLOBAL_SETTING; + break; + } + aRet <<= nLUM; + } + else if ( aPropertyName == SC_UNO_COLROWHDR ) + aRet <<= aViewOpt.GetOption( VOPT_HEADER ); + else if ( aPropertyName == SC_UNO_SHEETTABS ) + aRet <<= aViewOpt.GetOption( VOPT_TABCONTROLS ); + else if ( aPropertyName == SC_UNO_OUTLSYMB ) + aRet <<= aViewOpt.GetOption( VOPT_OUTLINER ); + else if ( aPropertyName == SC_UNO_AUTOCALC ) + aRet <<= rDoc.GetAutoCalc(); + else if ( aPropertyName == SC_UNO_PRINTERNAME ) + { + // #i75610# don't create the printer, return empty string if no printer created yet + // (as in SwXDocumentSettings) + SfxPrinter* pPrinter = rDoc.GetPrinter( false ); + if (pPrinter) + aRet <<= pPrinter->GetName(); + else + aRet <<= OUString(); + } + else if ( aPropertyName == SC_UNO_PRINTERSETUP ) + { + // #i75610# don't create the printer, return empty sequence if no printer created yet + // (as in SwXDocumentSettings) + SfxPrinter* pPrinter = rDoc.GetPrinter( false ); + if (pPrinter) + { + SvMemoryStream aStream; + pPrinter->Store( aStream ); + aRet <<= uno::Sequence< sal_Int8 >( static_cast< const sal_Int8* >( aStream.GetData() ), + aStream.TellEnd() ); + } + else + aRet <<= uno::Sequence<sal_Int8>(); + } + else if ( aPropertyName == SC_UNO_PRINTERPAPER) + { + SfxPrinter *pTempPrinter = pDocShell->GetPrinter( false ); + aRet <<= pTempPrinter && pTempPrinter->GetPrinterSettingsPreferred(); + + } + else if ( aPropertyName == SC_UNO_APPLYDOCINF ) + aRet <<= pDocShell->IsUseUserData(); + else if ( aPropertyName == SC_UNO_SAVE_THUMBNAIL ) + aRet <<= pDocShell->IsUseThumbnailSave(); + else if ( aPropertyName == SC_UNO_FORBIDDEN ) + { + aRet <<= uno::Reference<i18n::XForbiddenCharacters>(new ScForbiddenCharsObj( pDocShell )); + } + else if ( aPropertyName == SC_UNO_CHARCOMP ) + aRet <<= static_cast<sal_Int16> ( rDoc.GetAsianCompression() ); + else if ( aPropertyName == SC_UNO_ASIANKERN ) + aRet <<= rDoc.GetAsianKerning(); + else if ( aPropertyName == SCSAVEVERSION ) + aRet <<= pDocShell->IsSaveVersionOnClose(); + else if ( aPropertyName == SC_UNO_UPDTEMPL ) + aRet <<= pDocShell->IsQueryLoadTemplate(); + else if ( aPropertyName == SC_UNO_LOADREADONLY ) + aRet <<= pDocShell->IsLoadReadonly(); + else if ( aPropertyName == SC_UNO_SHAREDOC ) + { +#if HAVE_FEATURE_MULTIUSER_ENVIRONMENT + aRet <<= pDocShell->HasSharedXMLFlagSet(); +#endif + } + else if ( aPropertyName == SC_UNO_MODIFYPASSWORDINFO ) + aRet <<= pDocShell->GetModifyPasswordInfo(); + else if (aPropertyName == SC_UNO_MODIFYPASSWORDHASH) + aRet <<= pDocShell->GetModifyPasswordHash(); + else if (aPropertyName == SC_UNO_EMBED_FONTS) + aRet <<= rDoc.IsEmbedFonts(); + else if (aPropertyName == SC_UNO_EMBED_ONLY_USED_FONTS) + aRet <<= rDoc.IsEmbedUsedFontsOnly(); + else if (aPropertyName == SC_UNO_EMBED_FONT_SCRIPT_LATIN) + aRet <<= rDoc.IsEmbedFontScriptLatin(); + else if (aPropertyName == SC_UNO_EMBED_FONT_SCRIPT_ASIAN) + aRet <<= rDoc.IsEmbedFontScriptAsian(); + else if (aPropertyName == SC_UNO_EMBED_FONT_SCRIPT_COMPLEX) + aRet <<= rDoc.IsEmbedFontScriptComplex(); + else if ( aPropertyName == SC_UNO_SYNTAXSTRINGREF ) + { + ScCalcConfig aCalcConfig = rDoc.GetCalcConfig(); + formula::FormulaGrammar::AddressConvention eConv = aCalcConfig.meStringRefAddressSyntax; + + // don't save "unspecified" string ref syntax ... query formula grammar + // and save that instead + if( eConv == formula::FormulaGrammar::CONV_UNSPECIFIED) + { + eConv = rDoc.GetAddressConvention(); + } + + // write if it has been read|imported or explicitly changed + // or if ref syntax isn't what would be native for our file format + // i.e. CalcA1 in this case + if ( aCalcConfig.mbHasStringRefSyntax || + (eConv != formula::FormulaGrammar::CONV_OOO) ) + { + switch (eConv) + { + case formula::FormulaGrammar::CONV_OOO: + case formula::FormulaGrammar::CONV_XL_A1: + case formula::FormulaGrammar::CONV_XL_R1C1: + case formula::FormulaGrammar::CONV_A1_XL_A1: + aRet <<= static_cast<sal_Int16>( eConv ); + break; + + case formula::FormulaGrammar::CONV_UNSPECIFIED: + case formula::FormulaGrammar::CONV_ODF: + case formula::FormulaGrammar::CONV_XL_OOX: + case formula::FormulaGrammar::CONV_LOTUS_A1: + case formula::FormulaGrammar::CONV_LAST: + { + aRet <<= sal_Int16(9999); + break; + } + } + } + } + else if (aPropertyName == SC_UNO_IMAGE_PREFERRED_DPI) + { + aRet <<= rDoc.GetImagePreferredDPI(); + } + else + { + const ScGridOptions& aGridOpt = aViewOpt.GetGridOptions(); + if ( aPropertyName == SC_UNO_SNAPTORASTER ) + aRet <<= aGridOpt.GetUseGridSnap(); + else if ( aPropertyName == SC_UNO_RASTERVIS ) + aRet <<= aGridOpt.GetGridVisible(); + else if ( aPropertyName == SC_UNO_RASTERRESX ) + aRet <<= static_cast<sal_Int32> ( aGridOpt.GetFieldDrawX() ); + else if ( aPropertyName == SC_UNO_RASTERRESY ) + aRet <<= static_cast<sal_Int32> ( aGridOpt.GetFieldDrawY() ); + else if ( aPropertyName == SC_UNO_RASTERSUBX ) + aRet <<= static_cast<sal_Int32> ( aGridOpt.GetFieldDivisionX() ); + else if ( aPropertyName == SC_UNO_RASTERSUBY ) + aRet <<= static_cast<sal_Int32> ( aGridOpt.GetFieldDivisionY() ); + else if ( aPropertyName == SC_UNO_RASTERSYNC ) + aRet <<= aGridOpt.GetSynchronize(); + else + throw beans::UnknownPropertyException(aPropertyName); + } + + + return aRet; +} + +SC_IMPL_DUMMY_PROPERTY_LISTENER( ScDocumentConfiguration ) + +// XServiceInfo +OUString SAL_CALL ScDocumentConfiguration::getImplementationName() +{ + return "ScDocumentConfiguration"; +} + +sal_Bool SAL_CALL ScDocumentConfiguration::supportsService( const OUString& rServiceName ) +{ + return cppu::supportsService(this, rServiceName); +} + +uno::Sequence<OUString> SAL_CALL ScDocumentConfiguration::getSupportedServiceNames() +{ + return {"com.sun.star.comp.SpreadsheetSettings", + "com.sun.star.document.Settings"}; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/unoobj/convuno.cxx b/sc/source/ui/unoobj/convuno.cxx new file mode 100644 index 0000000000..d6ce8d9e1f --- /dev/null +++ b/sc/source/ui/unoobj/convuno.cxx @@ -0,0 +1,46 @@ +/* -*- 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 <i18nlangtag/languagetag.hxx> + +#include <convuno.hxx> + +using namespace com::sun::star; + +// everything is static... + +LanguageType ScUnoConversion::GetLanguage(const lang::Locale& rLocale) +{ + // empty language -> LANGUAGE_SYSTEM + if (rLocale.Language.isEmpty()) + return LANGUAGE_SYSTEM; + + LanguageType eRet = LanguageTag::convertToLanguageType(rLocale, false); + if (eRet == LANGUAGE_NONE) + eRet = LANGUAGE_SYSTEM; //! or throw an exception? + + return eRet; +} + +void ScUnoConversion::FillLocale(lang::Locale& rLocale, LanguageType eLang) +{ + rLocale = LanguageTag::convertToLocale(eLang); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/unoobj/cursuno.cxx b/sc/source/ui/unoobj/cursuno.cxx new file mode 100644 index 0000000000..f2b9142f37 --- /dev/null +++ b/sc/source/ui/unoobj/cursuno.cxx @@ -0,0 +1,453 @@ +/* -*- 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 <vcl/svapp.hxx> +#include <comphelper/sequence.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <cursuno.hxx> +#include <cellsuno.hxx> +#include <docsh.hxx> +#include <markdata.hxx> +#include <miscuno.hxx> + +using namespace com::sun::star; + +constexpr OUString SCSHEETCELLCURSOR_SERVICE = u"com.sun.star.sheet.SheetCellCursor"_ustr; +constexpr OUString SCCELLCURSOR_SERVICE = u"com.sun.star.table.CellCursor"_ustr; + +ScCellCursorObj::ScCellCursorObj(ScDocShell* pDocSh, const ScRange& rR) : + ScCellRangeObj( pDocSh, rR ) +{ +} + +ScCellCursorObj::~ScCellCursorObj() +{ +} + +uno::Any SAL_CALL ScCellCursorObj::queryInterface( const uno::Type& rType ) +{ + uno::Any aReturn = ::cppu::queryInterface(rType, + static_cast<sheet::XSheetCellCursor*>(this), + static_cast<sheet::XUsedAreaCursor*>(this), + static_cast<table::XCellCursor*>(this)); + if ( aReturn.hasValue() ) + return aReturn; + + return ScCellRangeObj::queryInterface( rType ); +} + +void SAL_CALL ScCellCursorObj::acquire() noexcept +{ + ScCellRangeObj::acquire(); +} + +void SAL_CALL ScCellCursorObj::release() noexcept +{ + ScCellRangeObj::release(); +} + +uno::Sequence<uno::Type> SAL_CALL ScCellCursorObj::getTypes() +{ + return comphelper::concatSequences( + ScCellRangeObj::getTypes(), + uno::Sequence<uno::Type> + { + cppu::UnoType<sheet::XSheetCellCursor>::get(), + cppu::UnoType<sheet::XUsedAreaCursor>::get(), + cppu::UnoType<table::XCellCursor>::get() + } ); +} + +uno::Sequence<sal_Int8> SAL_CALL ScCellCursorObj::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +// XSheetCellCursor + +void SAL_CALL ScCellCursorObj::collapseToCurrentRegion() +{ + SolarMutexGuard aGuard; + const ScRangeList& rRanges = GetRangeList(); + OSL_ENSURE( rRanges.size() == 1, "Range? Ranges?" ); + ScRange aOneRange( rRanges[ 0 ] ); + + aOneRange.PutInOrder(); + ScDocShell* pDocSh = GetDocShell(); + if ( !pDocSh ) + return; + + SCCOL nStartCol = aOneRange.aStart.Col(); + SCROW nStartRow = aOneRange.aStart.Row(); + SCCOL nEndCol = aOneRange.aEnd.Col(); + SCROW nEndRow = aOneRange.aEnd.Row(); + SCTAB nTab = aOneRange.aStart.Tab(); + + pDocSh->GetDocument().GetDataArea( + nTab, nStartCol, nStartRow, nEndCol, nEndRow, true, false ); + + ScRange aNew( nStartCol, nStartRow, nTab, nEndCol, nEndRow, nTab ); + SetNewRange( aNew ); +} + +void SAL_CALL ScCellCursorObj::collapseToCurrentArray() +{ + SolarMutexGuard aGuard; + const ScRangeList& rRanges = GetRangeList(); + OSL_ENSURE( rRanges.size() == 1, "Range? Ranges?" ); + ScRange aOneRange( rRanges[ 0 ] ); + + aOneRange.PutInOrder(); + ScAddress aCursor(aOneRange.aStart); // use the start address of the range + + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + { + ScDocument& rDoc = pDocSh->GetDocument(); + ScRange aMatrix; + + // finding the matrix range is now in GetMatrixFormulaRange in the document + if ( rDoc.GetMatrixFormulaRange( aCursor, aMatrix ) ) + { + SetNewRange( aMatrix ); + } + } + // that's a Bug, that this assertion comes; the API Reference says, that + // if there is no Matrix, the Range is left unchanged; they say nothing + // about an exception + /*if (!bFound) + { + OSL_FAIL("no matrix"); + //! Exception, or what? + }*/ +} + +void SAL_CALL ScCellCursorObj::collapseToMergedArea() +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + { + const ScRangeList& rRanges = GetRangeList(); + OSL_ENSURE( rRanges.size() == 1, "Range? Ranges?" ); + ScRange aNewRange( rRanges[ 0 ] ); + + ScDocument& rDoc = pDocSh->GetDocument(); + rDoc.ExtendOverlapped( aNewRange ); + rDoc.ExtendMerge( aNewRange ); // after ExtendOverlapped! + + SetNewRange( aNewRange ); + } +} + +void SAL_CALL ScCellCursorObj::expandToEntireColumns() +{ + SolarMutexGuard aGuard; + const ScRangeList& rRanges = GetRangeList(); + OSL_ENSURE( rRanges.size() == 1, "Range? Ranges?" ); + ScRange aNewRange( rRanges[ 0 ] ); + + aNewRange.aStart.SetRow( 0 ); + aNewRange.aEnd.SetRow( GetDocShell()->GetDocument().MaxRow() ); + + SetNewRange( aNewRange ); +} + +void SAL_CALL ScCellCursorObj::expandToEntireRows() +{ + SolarMutexGuard aGuard; + const ScRangeList& rRanges = GetRangeList(); + OSL_ENSURE( rRanges.size() == 1, "Range? Ranges?" ); + ScRange aNewRange( rRanges[ 0 ] ); + + aNewRange.aStart.SetCol( 0 ); + aNewRange.aEnd.SetCol( GetDocShell()->GetDocument().MaxCol() ); + + SetNewRange( aNewRange ); +} + +void SAL_CALL ScCellCursorObj::collapseToSize( sal_Int32 nColumns, sal_Int32 nRows ) +{ + SolarMutexGuard aGuard; + if ( nColumns <= 0 || nRows <= 0 ) + { + OSL_FAIL("Empty range not allowed"); + //! and then? + } + else + { + const ScRangeList& rRanges = GetRangeList(); + OSL_ENSURE( rRanges.size() == 1, "Range? Ranges?" ); + ScRange aNewRange( rRanges[ 0 ] ); + + aNewRange.PutInOrder(); //! really? + + const auto & rDoc = GetDocShell()->GetDocument(); + tools::Long nEndX = aNewRange.aStart.Col() + nColumns - 1; + tools::Long nEndY = aNewRange.aStart.Row() + nRows - 1; + if ( nEndX < 0 ) nEndX = 0; + if ( nEndX > rDoc.MaxCol() ) nEndX = rDoc.MaxCol(); + if ( nEndY < 0 ) nEndY = 0; + if ( nEndY > rDoc.MaxRow() ) nEndY = rDoc.MaxRow(); + //! error/exception or so, if too big/small + + aNewRange.aEnd.SetCol(static_cast<SCCOL>(nEndX)); + aNewRange.aEnd.SetRow(static_cast<SCROW>(nEndY)); + + aNewRange.PutInOrder(); //! really? + + SetNewRange( aNewRange ); + } +} + +// XUsedAreaCursor + +void SAL_CALL ScCellCursorObj::gotoStartOfUsedArea(sal_Bool bExpand) +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( !pDocSh ) + return; + + const ScRangeList& rRanges = GetRangeList(); + OSL_ENSURE( rRanges.size() == 1, "Range? Ranges?" ); + ScRange aNewRange( rRanges[0] ); + SCTAB nTab = aNewRange.aStart.Tab(); + + SCCOL nUsedX = 0; // fetch the beginning + SCROW nUsedY = 0; + if (!pDocSh->GetDocument().GetDataStart( nTab, nUsedX, nUsedY )) + { + nUsedX = 0; + nUsedY = 0; + } + + aNewRange.aStart.SetCol( nUsedX ); + aNewRange.aStart.SetRow( nUsedY ); + if (!bExpand) + aNewRange.aEnd = aNewRange.aStart; + SetNewRange( aNewRange ); +} + +void SAL_CALL ScCellCursorObj::gotoEndOfUsedArea( sal_Bool bExpand ) +{ + SolarMutexGuard aGuard; + ScDocShell* pDocSh = GetDocShell(); + if ( !pDocSh ) + return; + + const ScRangeList& rRanges = GetRangeList(); + OSL_ENSURE( rRanges.size() == 1, "Range? Ranges?" ); + ScRange aNewRange( rRanges[ 0 ]); + SCTAB nTab = aNewRange.aStart.Tab(); + + SCCOL nUsedX = 0; // fetch the end + SCROW nUsedY = 0; + if (!pDocSh->GetDocument().GetTableArea( nTab, nUsedX, nUsedY, true )) + { + nUsedX = 0; + nUsedY = 0; + } + + aNewRange.aEnd.SetCol( nUsedX ); + aNewRange.aEnd.SetRow( nUsedY ); + if (!bExpand) + aNewRange.aStart = aNewRange.aEnd; + SetNewRange( aNewRange ); +} + +// XCellCursor + +void SAL_CALL ScCellCursorObj::gotoStart() +{ + // this is similar to collapseToCurrentRegion + //! something like gotoEdge with 4 possible directions is needed + + SolarMutexGuard aGuard; + const ScRangeList& rRanges = GetRangeList(); + OSL_ENSURE( rRanges.size() == 1, "Range? Ranges?" ); + ScRange aOneRange( rRanges[ 0 ]); + + aOneRange.PutInOrder(); + ScDocShell* pDocSh = GetDocShell(); + if ( !pDocSh ) + return; + + SCCOL nStartCol = aOneRange.aStart.Col(); + SCROW nStartRow = aOneRange.aStart.Row(); + SCCOL nEndCol = aOneRange.aEnd.Col(); + SCROW nEndRow = aOneRange.aEnd.Row(); + SCTAB nTab = aOneRange.aStart.Tab(); + + pDocSh->GetDocument().GetDataArea( + nTab, nStartCol, nStartRow, nEndCol, nEndRow, false, false ); + + ScRange aNew( nStartCol, nStartRow, nTab ); + SetNewRange( aNew ); +} + +void SAL_CALL ScCellCursorObj::gotoEnd() +{ + // this is similar to collapseToCurrentRegion + //! something like gotoEdge with 4 possible directions is needed + + SolarMutexGuard aGuard; + const ScRangeList& rRanges = GetRangeList(); + OSL_ENSURE( rRanges.size() == 1, "Range? Ranges?" ); + ScRange aOneRange( rRanges[ 0 ] ); + + aOneRange.PutInOrder(); + ScDocShell* pDocSh = GetDocShell(); + if ( !pDocSh ) + return; + + SCCOL nStartCol = aOneRange.aStart.Col(); + SCROW nStartRow = aOneRange.aStart.Row(); + SCCOL nEndCol = aOneRange.aEnd.Col(); + SCROW nEndRow = aOneRange.aEnd.Row(); + SCTAB nTab = aOneRange.aStart.Tab(); + + pDocSh->GetDocument().GetDataArea( + nTab, nStartCol, nStartRow, nEndCol, nEndRow, false, false ); + + ScRange aNew( nEndCol, nEndRow, nTab ); + SetNewRange( aNew ); +} + +void SAL_CALL ScCellCursorObj::gotoNext() +{ + SolarMutexGuard aGuard; + const ScRangeList& rRanges = GetRangeList(); + OSL_ENSURE( rRanges.size() == 1, "Range? Ranges?" ); + ScRange aOneRange( rRanges[ 0 ] ); + + aOneRange.PutInOrder(); + ScAddress aCursor(aOneRange.aStart); // always use start of block + + ScMarkData aMark(GetDocument()->GetSheetLimits()); // not used with bMarked=FALSE + SCCOL nNewX = aCursor.Col(); + SCROW nNewY = aCursor.Row(); + SCTAB nTab = aCursor.Tab(); + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + pDocSh->GetDocument().GetNextPos( nNewX,nNewY, nTab, 1,0, false,true, aMark ); + //! otherwise exception or so + + SetNewRange( ScRange( nNewX, nNewY, nTab ) ); +} + +void SAL_CALL ScCellCursorObj::gotoPrevious() +{ + SolarMutexGuard aGuard; + const ScRangeList& rRanges = GetRangeList(); + OSL_ENSURE( rRanges.size() == 1, "Range? Ranges?" ); + ScRange aOneRange( rRanges[ 0 ] ); + + aOneRange.PutInOrder(); + ScAddress aCursor(aOneRange.aStart); // always use start of block + + ScMarkData aMark(GetDocument()->GetSheetLimits()); // not used with bMarked=FALSE + SCCOL nNewX = aCursor.Col(); + SCROW nNewY = aCursor.Row(); + SCTAB nTab = aCursor.Tab(); + ScDocShell* pDocSh = GetDocShell(); + if ( pDocSh ) + pDocSh->GetDocument().GetNextPos( nNewX,nNewY, nTab, -1,0, false,true, aMark ); + //! otherwise exception or so + + SetNewRange( ScRange( nNewX, nNewY, nTab ) ); +} + +void SAL_CALL ScCellCursorObj::gotoOffset( sal_Int32 nColumnOffset, sal_Int32 nRowOffset ) +{ + SolarMutexGuard aGuard; + const ScRangeList& rRanges = GetRangeList(); + OSL_ENSURE( rRanges.size() == 1, "Range? Ranges?" ); + ScRange aOneRange( rRanges[ 0 ] ); + aOneRange.PutInOrder(); + + const auto & rDoc = GetDocShell()->GetDocument(); + if ( aOneRange.aStart.Col() + nColumnOffset >= 0 && + aOneRange.aEnd.Col() + nColumnOffset <= rDoc.MaxCol() && + aOneRange.aStart.Row() + nRowOffset >= 0 && + aOneRange.aEnd.Row() + nRowOffset <= rDoc.MaxRow() ) + { + ScRange aNew( static_cast<SCCOL>(aOneRange.aStart.Col() + nColumnOffset), + static_cast<SCROW>(aOneRange.aStart.Row() + nRowOffset), + aOneRange.aStart.Tab(), + static_cast<SCCOL>(aOneRange.aEnd.Col() + nColumnOffset), + static_cast<SCROW>(aOneRange.aEnd.Row() + nRowOffset), + aOneRange.aEnd.Tab() ); + SetNewRange( aNew ); + } +} + +// XSheetCellRange + +uno::Reference<sheet::XSpreadsheet> SAL_CALL ScCellCursorObj::getSpreadsheet() +{ + SolarMutexGuard aGuard; + return ScCellRangeObj::getSpreadsheet(); +} + +// XCellRange + +uno::Reference<table::XCell> SAL_CALL ScCellCursorObj::getCellByPosition( + sal_Int32 nColumn, sal_Int32 nRow ) +{ + SolarMutexGuard aGuard; + return ScCellRangeObj::getCellByPosition(nColumn,nRow); +} + +uno::Reference<table::XCellRange> SAL_CALL ScCellCursorObj::getCellRangeByPosition( + sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom ) +{ + SolarMutexGuard aGuard; + return ScCellRangeObj::getCellRangeByPosition(nLeft,nTop,nRight,nBottom); +} + +uno::Reference<table::XCellRange> SAL_CALL ScCellCursorObj::getCellRangeByName( + const OUString& rRange ) +{ + SolarMutexGuard aGuard; + return ScCellRangeObj::getCellRangeByName(rRange); +} + +// XServiceInfo + +OUString SAL_CALL ScCellCursorObj::getImplementationName() +{ + return "ScCellCursorObj"; +} + +sal_Bool SAL_CALL ScCellCursorObj::supportsService( const OUString& rServiceName ) +{ + return cppu::supportsService(this, rServiceName); +} + +uno::Sequence<OUString> SAL_CALL ScCellCursorObj::getSupportedServiceNames() +{ + // SheetCellCursor should be first (?) + return comphelper::concatSequences<OUString>( + { SCSHEETCELLCURSOR_SERVICE, SCCELLCURSOR_SERVICE }, + ScCellRangeObj::getSupportedServiceNames()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/unoobj/dapiuno.cxx b/sc/source/ui/unoobj/dapiuno.cxx new file mode 100644 index 0000000000..f172716c11 --- /dev/null +++ b/sc/source/ui/unoobj/dapiuno.cxx @@ -0,0 +1,3344 @@ +/* -*- 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 <utility> +#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 <cppuhelper/queryinterface.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 { + +std::span<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 }, + }; + return aDataPilotDescriptorBaseMap_Impl; +} + +std::span<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 }, + }; + return aDataPilotFieldMap_Impl; +} + +std::span<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 }, + }; + 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 OUString SC_DATALAYOUT_NAME = u"Data"_ustr; + +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", getXWeak(), 0); + + if (!pDocShell) + throw RuntimeException("DocShell is null", getXWeak()); + + auto pImp = dynamic_cast<ScDataPilotDescriptorBase*>( xDescriptor.get() ); + if (!pImp) + throw RuntimeException("Failed to get ScDataPilotDescriptor", getXWeak()); + + ScDPObject* pNewObj = pImp->GetDPObject(); + if (!pNewObj) + throw RuntimeException("Failed to get DPObject", getXWeak()); + + 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", getXWeak()); +} + +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", getXWeak()); + + 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", getXWeak()); + + 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; +} + +ScDataPilotTableObj::ScDataPilotTableObj(ScDocShell& rDocSh, SCTAB nT, OUString aN) : + ScDataPilotDescriptorBase( rDocSh ), + nTab( nT ), + aName(std::move( aN )), + 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 + uno::Any aReturn = ::cppu::queryInterface(rType, + static_cast<XDataPilotTable*>(this), + static_cast<XDataPilotTable2*>(this), + static_cast<XModifyBroadcaster*>(this)); + if ( aReturn.hasValue() ) + return aReturn; + + 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", getXWeak()); + + 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", getXWeak()); + + 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", getXWeak()); + ScTabViewShell* pViewSh = GetDocShell()->GetBestViewShell(); + if (!pViewSh) + throw RuntimeException("Failed to get ViewShell", getXWeak()); + + 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), + getXWeak(), 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 = getXWeak(); + + // 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, ScFieldIdentifier aFieldId ) : + mxParent( &rParent ), + maFieldId(std::move( aFieldId )) +{ +} + +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, Any aOrient ) : + ScDataPilotChildObjBase( rParent, rFieldId ), + maPropSet( lcl_GetDataPilotFieldMap() ), + maOrient(std::move( aOrient )) +{ +} + +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", getXWeak(), 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", getXWeak()); + } + + 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 + "\"", getXWeak(), 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 + "\"", + getXWeak(), 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", getXWeak(), 0); + if( !lclCheckMinMaxStep( rInfo ) ) + throw IllegalArgumentException("min/max/step", getXWeak(), 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), getXWeak(), 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), getXWeak(), 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", getXWeak(), 0); + + ScFieldGroups::iterator aIt = implFindByName( rName ); + if( aIt == maGroups.end() ) + throw NoSuchElementException("Name \"" + rName + "\" not found", getXWeak()); + + // read all item names provided by the passed object + ScFieldGroupMembers aMembers; + if( !lclExtractGroupMembers( aMembers, rElement ) ) + throw IllegalArgumentException("Invalid element object", getXWeak(), 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", getXWeak(), 0); + + ScFieldGroups::iterator aIt = implFindByName( rName ); + if( aIt != maGroups.end() ) + throw ElementExistException("Name \"" + rName + "\" already exists", getXWeak()); + + // read all item names provided by the passed object + ScFieldGroupMembers aMembers; + if( !lclExtractGroupMembers( aMembers, rElement ) ) + throw IllegalArgumentException("Invalid element object", getXWeak(), 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", getXWeak(), 0); + + ScFieldGroups::iterator aIt = implFindByName( rName ); + if( aIt == maGroups.end() ) + throw NoSuchElementException("Name \"" + rName + "\" not found", getXWeak()); + + 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", getXWeak()); + 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", getXWeak()); + // new name must not exist yet + if( (aNewIt != maGroups.end()) && (aNewIt != aOldIt) ) + throw RuntimeException("Field Group with name \"" + rOldName + "\" already exists", getXWeak()); + 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, OUString aGroupName ) : + mxParent( &rParent ), + maGroupName(std::move( aGroupName )) +{ +} + +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", getXWeak()); + 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", getXWeak(), 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", getXWeak()); + if( aNewIt != rMembers.end() ) + throw IllegalArgumentException("Name \"" + rName + "\" already exists", getXWeak(), 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", getXWeak(), 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", getXWeak(), 0); + rMembers.push_back( rName ); +} + +void SAL_CALL ScDataPilotFieldGroupObj::removeByName( const OUString& rName ) +{ + SolarMutexGuard aGuard; + + if( rName.isEmpty() ) + throw IllegalArgumentException("Name is empty", getXWeak(), 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", getXWeak()); + 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, OUString aName ) : + mxParent( &rParent ), + maName(std::move( aName )) +{ +} + +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", getXWeak()); + } + 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: */ diff --git a/sc/source/ui/unoobj/datauno.cxx b/sc/source/ui/unoobj/datauno.cxx new file mode 100644 index 0000000000..1bbd661a4f --- /dev/null +++ b/sc/source/ui/unoobj/datauno.cxx @@ -0,0 +1,2384 @@ +/* -*- 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 <datauno.hxx> + +#include <svl/hint.hxx> +#include <svl/numformat.hxx> +#include <svl/sharedstringpool.hxx> +#include <utility> +#include <vcl/svapp.hxx> +#include <unotools/charclass.hxx> +#include <osl/diagnose.h> + +#include <com/sun/star/awt/XBitmap.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/util/SortField.hpp> +#include <com/sun/star/table/TableSortField.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/table/TableOrientation.hpp> +#include <com/sun/star/table/CellRangeAddress.hpp> +#include <com/sun/star/sheet/DataImportMode.hpp> +#include <com/sun/star/sheet/FilterFieldType.hpp> +#include <com/sun/star/sheet/FilterOperator2.hpp> +#include <com/sun/star/sheet/TableFilterField2.hpp> + +#include <dapiuno.hxx> +#include <cellsuno.hxx> +#include <miscuno.hxx> +#include <targuno.hxx> +#include <rangeutl.hxx> +#include <dbdata.hxx> +#include <docsh.hxx> +#include <dbdocfun.hxx> +#include <unonames.hxx> +#include <globalnames.hxx> +#include <convuno.hxx> +#include <hints.hxx> +#include <attrib.hxx> +#include <dpshttab.hxx> +#include <queryentry.hxx> +#include <dputil.hxx> +#include <sortparam.hxx> +#include <dpobject.hxx> +#include <filterentries.hxx> + +#include <comphelper/extract.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <svx/dataaccessdescriptor.hxx> + +#include <memory> + +using namespace com::sun::star; +using namespace css::sheet; + +// everything without Which-ID, map only for PropertySetInfo + +static std::span<const SfxItemPropertyMapEntry> lcl_GetSubTotalPropertyMap() +{ + // some old property names are for 5.2 compatibility + + static const SfxItemPropertyMapEntry aSubTotalPropertyMap_Impl[] = + { + { SC_UNONAME_BINDFMT, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNONAME_CASE, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNONAME_ENABSORT, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNONAME_ENUSLIST, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNONAME_FORMATS, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNONAME_INSBRK, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNONAME_ISCASE, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNONAME_MAXFLD, 0, cppu::UnoType<sal_Int32>::get(), beans::PropertyAttribute::READONLY, 0}, + { SC_UNONAME_SORTASC, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNONAME_ULIST, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNONAME_UINDEX, 0, cppu::UnoType<sal_Int32>::get(), 0, 0}, + { SC_UNONAME_USINDEX, 0, cppu::UnoType<sal_Int32>::get(), 0, 0}, + }; + return aSubTotalPropertyMap_Impl; +} + +static std::span<const SfxItemPropertyMapEntry> lcl_GetFilterPropertyMap() +{ + static const SfxItemPropertyMapEntry aFilterPropertyMap_Impl[] = + { + { SC_UNONAME_CONTHDR, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNONAME_COPYOUT, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNONAME_ISCASE, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNONAME_MAXFLD, 0, cppu::UnoType<sal_Int32>::get(), beans::PropertyAttribute::READONLY, 0}, + { SC_UNONAME_ORIENT, 0, cppu::UnoType<table::TableOrientation>::get(), 0, 0}, + { SC_UNONAME_OUTPOS, 0, cppu::UnoType<table::CellAddress>::get(), 0, 0}, + { SC_UNONAME_SAVEOUT, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNONAME_SKIPDUP, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNONAME_USEREGEX, 0, cppu::UnoType<bool>::get(), 0, 0}, + }; + return aFilterPropertyMap_Impl; +} + +static std::span<const SfxItemPropertyMapEntry> lcl_GetDBRangePropertyMap() +{ + static const SfxItemPropertyMapEntry aDBRangePropertyMap_Impl[] = + { + { SC_UNONAME_AUTOFLT, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNONAME_FLTCRT, 0, cppu::UnoType<table::CellRangeAddress>::get(), 0, 0}, + { SC_UNONAME_FROMSELECT,0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNONAME_ISUSER, 0, cppu::UnoType<bool>::get(), beans::PropertyAttribute::READONLY, 0 }, + { SC_UNONAME_KEEPFORM, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_LINKDISPBIT, 0, cppu::UnoType<awt::XBitmap>::get(), beans::PropertyAttribute::READONLY, 0 }, + { SC_UNO_LINKDISPNAME, 0, cppu::UnoType<OUString>::get(), beans::PropertyAttribute::READONLY, 0 }, + { SC_UNONAME_MOVCELLS, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNONAME_REFPERIOD, 0, cppu::UnoType<sal_Int32>::get(), 0, 0}, + { SC_UNONAME_STRIPDAT, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNONAME_TOKENINDEX,0, cppu::UnoType<sal_Int32>::get(), beans::PropertyAttribute::READONLY, 0 }, + { SC_UNONAME_USEFLTCRT,0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNONAME_TOTALSROW,0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNONAME_CONTHDR ,0, cppu::UnoType<bool>::get(), 0, 0}, + }; + return aDBRangePropertyMap_Impl; +} + +SC_SIMPLE_SERVICE_INFO( ScConsolidationDescriptor, "ScConsolidationDescriptor", "com.sun.star.sheet.ConsolidationDescriptor" ) +SC_SIMPLE_SERVICE_INFO( ScDatabaseRangesObj, "ScDatabaseRangesObj", "com.sun.star.sheet.DatabaseRanges" ) +SC_SIMPLE_SERVICE_INFO( ScFilterDescriptorBase, "ScFilterDescriptorBase", "com.sun.star.sheet.SheetFilterDescriptor" ) +SC_SIMPLE_SERVICE_INFO( ScSubTotalDescriptorBase, "ScSubTotalDescriptorBase", "com.sun.star.sheet.SubTotalDescriptor" ) +SC_SIMPLE_SERVICE_INFO( ScSubTotalFieldObj, "ScSubTotalFieldObj", "com.sun.star.sheet.SubTotalField" ) + +sheet::GeneralFunction ScDataUnoConversion::SubTotalToGeneral( ScSubTotalFunc eSubTotal ) +{ + sheet::GeneralFunction eGeneral; + switch (eSubTotal) + { + case SUBTOTAL_FUNC_NONE: eGeneral = sheet::GeneralFunction_NONE; break; + case SUBTOTAL_FUNC_AVE: eGeneral = sheet::GeneralFunction_AVERAGE; break; + case SUBTOTAL_FUNC_CNT: eGeneral = sheet::GeneralFunction_COUNTNUMS; break; + case SUBTOTAL_FUNC_CNT2: eGeneral = sheet::GeneralFunction_COUNT; break; + case SUBTOTAL_FUNC_MAX: eGeneral = sheet::GeneralFunction_MAX; break; + case SUBTOTAL_FUNC_MIN: eGeneral = sheet::GeneralFunction_MIN; break; + case SUBTOTAL_FUNC_PROD: eGeneral = sheet::GeneralFunction_PRODUCT; break; + case SUBTOTAL_FUNC_STD: eGeneral = sheet::GeneralFunction_STDEV; break; + case SUBTOTAL_FUNC_STDP: eGeneral = sheet::GeneralFunction_STDEVP; break; + case SUBTOTAL_FUNC_SUM: eGeneral = sheet::GeneralFunction_SUM; break; + case SUBTOTAL_FUNC_VAR: eGeneral = sheet::GeneralFunction_VAR; break; + case SUBTOTAL_FUNC_VARP: eGeneral = sheet::GeneralFunction_VARP; break; + default: + OSL_FAIL("SubTotalToGeneral: wrong enum"); + eGeneral = sheet::GeneralFunction_NONE; + break; + } + return eGeneral; +} + +void ScImportDescriptor::FillProperties( uno::Sequence<beans::PropertyValue>& rSeq, const ScImportParam& rParam ) +{ + OSL_ENSURE( rSeq.getLength() == GetPropertyCount(), "wrong Count" ); + + beans::PropertyValue* pArray = rSeq.getArray(); + + sheet::DataImportMode eMode = sheet::DataImportMode_NONE; + if ( rParam.bImport ) + { + if ( rParam.bSql ) + eMode = sheet::DataImportMode_SQL; + else if ( rParam.nType == ScDbQuery ) + eMode = sheet::DataImportMode_QUERY; + else + eMode = sheet::DataImportMode_TABLE; // type always ScDbQuery or ScDbTable + } + + svx::ODataAccessDescriptor aDescriptor; + aDescriptor.setDataSource(rParam.aDBName); + if (aDescriptor.has( svx::DataAccessDescriptorProperty::DataSource )) + { + pArray[0].Name = SC_UNONAME_DBNAME; + pArray[0].Value <<= rParam.aDBName; + } + else if (aDescriptor.has( svx::DataAccessDescriptorProperty::ConnectionResource )) + { + pArray[0].Name = SC_UNONAME_CONRES; + pArray[0].Value <<= rParam.aDBName; + } + + pArray[1].Name = SC_UNONAME_SRCTYPE; + pArray[1].Value <<= eMode; + + pArray[2].Name = SC_UNONAME_SRCOBJ; + pArray[2].Value <<= rParam.aStatement; + + pArray[3].Name = SC_UNONAME_ISNATIVE; + pArray[3].Value <<= rParam.bNative; +} + +void ScImportDescriptor::FillImportParam( ScImportParam& rParam, const uno::Sequence<beans::PropertyValue>& rSeq ) +{ + OUString aStrVal; + for (const beans::PropertyValue& rProp : rSeq) + { + OUString aPropName(rProp.Name); + + if (aPropName == SC_UNONAME_ISNATIVE) + rParam.bNative = ScUnoHelpFunctions::GetBoolFromAny( rProp.Value ); + else if (aPropName == SC_UNONAME_DBNAME) + { + if ( rProp.Value >>= aStrVal ) + rParam.aDBName = aStrVal; + } + else if (aPropName == SC_UNONAME_CONRES) + { + if ( rProp.Value >>= aStrVal ) + rParam.aDBName = aStrVal; + } + else if (aPropName == SC_UNONAME_SRCOBJ) + { + if ( rProp.Value >>= aStrVal ) + rParam.aStatement = aStrVal; + } + else if (aPropName == SC_UNONAME_SRCTYPE) + { + //! test for correct enum type? + sheet::DataImportMode eMode = static_cast<sheet::DataImportMode>(ScUnoHelpFunctions::GetEnumFromAny( rProp.Value )); + switch (eMode) + { + case sheet::DataImportMode_NONE: + rParam.bImport = false; + break; + case sheet::DataImportMode_SQL: + rParam.bImport = true; + rParam.bSql = true; + break; + case sheet::DataImportMode_TABLE: + rParam.bImport = true; + rParam.bSql = false; + rParam.nType = ScDbTable; + break; + case sheet::DataImportMode_QUERY: + rParam.bImport = true; + rParam.bSql = false; + rParam.nType = ScDbQuery; + break; + default: + OSL_FAIL("wrong mode"); + rParam.bImport = false; + } + } + } +} + +void ScSortDescriptor::FillProperties( uno::Sequence<beans::PropertyValue>& rSeq, const ScSortParam& rParam ) +{ + OSL_ENSURE( rSeq.getLength() == GetPropertyCount(), "wrong count" ); + + beans::PropertyValue* pArray = rSeq.getArray(); + + // gather Uno values together + + table::CellAddress aOutPos; + aOutPos.Sheet = rParam.nDestTab; + aOutPos.Column = rParam.nDestCol; + aOutPos.Row = rParam.nDestRow; + + sal_uInt16 nSortCount = 0; + while ( nSortCount < rParam.GetSortKeyCount() && rParam.maKeyState[nSortCount].bDoSort ) + ++nSortCount; + + uno::Sequence<table::TableSortField> aFields(nSortCount); + if (nSortCount) + { + table::TableSortField* pFieldArray = aFields.getArray(); + for (sal_uInt16 i=0; i<nSortCount; i++) + { + pFieldArray[i].Field = rParam.maKeyState[i].nField; + pFieldArray[i].IsAscending = rParam.maKeyState[i].bAscending; + pFieldArray[i].FieldType = table::TableSortFieldType_AUTOMATIC; // always automatic + pFieldArray[i].IsCaseSensitive = rParam.bCaseSens; + pFieldArray[i].CollatorLocale = rParam.aCollatorLocale; + pFieldArray[i].CollatorAlgorithm = rParam.aCollatorAlgorithm; + } + } + + // fill the sequence + + pArray[0].Name = SC_UNONAME_ISSORTCOLUMNS; + pArray[0].Value <<= !rParam.bByRow; + + pArray[1].Name = SC_UNONAME_CONTHDR; + pArray[1].Value <<= rParam.bHasHeader; + + pArray[2].Name = SC_UNONAME_MAXFLD; + pArray[2].Value <<= static_cast<sal_Int32>( rParam.GetSortKeyCount() ); + + pArray[3].Name = SC_UNONAME_SORTFLD; + pArray[3].Value <<= aFields; + + pArray[4].Name = SC_UNONAME_BINDFMT; + pArray[4].Value <<= rParam.aDataAreaExtras.mbCellFormats; + + pArray[5].Name = SC_UNONAME_COPYOUT; + pArray[5].Value <<= !rParam.bInplace; + + pArray[6].Name = SC_UNONAME_OUTPOS; + pArray[6].Value <<= aOutPos; + + pArray[7].Name = SC_UNONAME_ISULIST; + pArray[7].Value <<= rParam.bUserDef; + + pArray[8].Name = SC_UNONAME_UINDEX; + pArray[8].Value <<= static_cast<sal_Int32>( rParam.nUserIndex ); +} + +void ScSortDescriptor::FillSortParam( ScSortParam& rParam, const uno::Sequence<beans::PropertyValue>& rSeq ) +{ + sal_Int32 nSortSize = static_cast<sal_Int32>(rParam.GetSortKeyCount()); + + for (const beans::PropertyValue& rProp : rSeq) + { + OUString aPropName(rProp.Name); + + if (aPropName == SC_UNONAME_ORIENT) + { + //! test for correct enum type? + table::TableOrientation eOrient = static_cast<table::TableOrientation>(ScUnoHelpFunctions::GetEnumFromAny( rProp.Value )); + rParam.bByRow = ( eOrient != table::TableOrientation_COLUMNS ); + } + else if (aPropName == SC_UNONAME_ISSORTCOLUMNS) + { + rParam.bByRow = !::cppu::any2bool(rProp.Value); + } + else if (aPropName == SC_UNONAME_CONTHDR) + rParam.bHasHeader = ScUnoHelpFunctions::GetBoolFromAny( rProp.Value ); + else if (aPropName == SC_UNONAME_MAXFLD) + { + sal_Int32 nVal; + if ( (rProp.Value >>= nVal) && nVal > nSortSize ) + { + //! specify exceptions + //! throw lang::IllegalArgumentException(); + } + } + else if (aPropName == SC_UNONAME_SORTFLD) + { + uno::Sequence<util::SortField> aSeq; + uno::Sequence<table::TableSortField> aNewSeq; + if ( rProp.Value >>= aSeq ) + { + sal_Int32 nCount = aSeq.getLength(); + sal_Int32 i; + if ( nCount > static_cast<sal_Int32>( rParam.GetSortKeyCount() ) ) + { + // tdf#105301 - increase the size of the sorting keys + nSortSize = nCount; + rParam.maKeyState.resize(nCount); + } + const util::SortField* pFieldArray = aSeq.getConstArray(); + for (i=0; i<nCount; i++) + { + rParam.maKeyState[i].nField = static_cast<SCCOLROW>( pFieldArray[i].Field ); + rParam.maKeyState[i].bAscending = pFieldArray[i].SortAscending; + + // FieldType is ignored + rParam.maKeyState[i].bDoSort = true; + } + for (i=nCount; i<nSortSize; i++) + rParam.maKeyState[i].bDoSort = false; + } + else if ( rProp.Value >>= aNewSeq ) + { + sal_Int32 nCount = aNewSeq.getLength(); + sal_Int32 i; + if ( nCount > nSortSize ) + { + nCount = nSortSize; + rParam.maKeyState.resize(nCount); + } + const table::TableSortField* pFieldArray = aNewSeq.getConstArray(); + for (i=0; i<nCount; i++) + { + rParam.maKeyState[i].nField = static_cast<SCCOLROW>( pFieldArray[i].Field ); + rParam.maKeyState[i].bAscending = pFieldArray[i].IsAscending; + + // only one is possible, sometime we should make it possible to have different for every entry + rParam.bCaseSens = pFieldArray[i].IsCaseSensitive; + rParam.aCollatorLocale = pFieldArray[i].CollatorLocale; + rParam.aCollatorAlgorithm = pFieldArray[i].CollatorAlgorithm; + + // FieldType is ignored + rParam.maKeyState[i].bDoSort = true; + } + for (i=nCount; i<nSortSize; i++) + rParam.maKeyState[i].bDoSort = false; + } + } + else if (aPropName == SC_UNONAME_ISCASE) + { + rParam.bCaseSens = ScUnoHelpFunctions::GetBoolFromAny( rProp.Value ); + } + else if (aPropName == SC_UNONAME_BINDFMT) + rParam.aDataAreaExtras.mbCellFormats = ScUnoHelpFunctions::GetBoolFromAny( rProp.Value ); + else if (aPropName == SC_UNONAME_COPYOUT) + rParam.bInplace = !ScUnoHelpFunctions::GetBoolFromAny( rProp.Value ); + else if (aPropName == SC_UNONAME_OUTPOS) + { + table::CellAddress aAddress; + if ( rProp.Value >>= aAddress ) + { + rParam.nDestTab = aAddress.Sheet; + rParam.nDestCol = static_cast<SCCOL>(aAddress.Column); + rParam.nDestRow = static_cast<SCROW>(aAddress.Row); + } + } + else if (aPropName == SC_UNONAME_ISULIST) + rParam.bUserDef = ScUnoHelpFunctions::GetBoolFromAny( rProp.Value ); + else if (aPropName == SC_UNONAME_UINDEX) + { + sal_Int32 nVal = 0; + if ( rProp.Value >>= nVal ) + rParam.nUserIndex = static_cast<sal_uInt16>(nVal); + } + else if (aPropName == SC_UNONAME_COLLLOC) + { + rProp.Value >>= rParam.aCollatorLocale; + } + else if (aPropName == SC_UNONAME_COLLALG) + { + OUString sStr; + if ( rProp.Value >>= sStr ) + rParam.aCollatorAlgorithm = sStr; + } + } +} + +ScSubTotalFieldObj::ScSubTotalFieldObj( ScSubTotalDescriptorBase* pDesc, sal_uInt16 nP ) : + xParent( pDesc ), + nPos( nP ) +{ + OSL_ENSURE(pDesc, "ScSubTotalFieldObj: Parent is 0"); +} + +ScSubTotalFieldObj::~ScSubTotalFieldObj() +{ +} + +// XSubTotalField + +sal_Int32 SAL_CALL ScSubTotalFieldObj::getGroupColumn() +{ + SolarMutexGuard aGuard; + ScSubTotalParam aParam; + xParent->GetData(aParam); + + return aParam.nField[nPos]; +} + +void SAL_CALL ScSubTotalFieldObj::setGroupColumn( sal_Int32 nGroupColumn ) +{ + SolarMutexGuard aGuard; + ScSubTotalParam aParam; + xParent->GetData(aParam); + + aParam.nField[nPos] = static_cast<SCCOL>(nGroupColumn); + + xParent->PutData(aParam); +} + +uno::Sequence<sheet::SubTotalColumn> SAL_CALL ScSubTotalFieldObj::getSubTotalColumns() +{ + SolarMutexGuard aGuard; + ScSubTotalParam aParam; + xParent->GetData(aParam); + + SCCOL nCount = aParam.nSubTotals[nPos]; + uno::Sequence<sheet::SubTotalColumn> aSeq(nCount); + sheet::SubTotalColumn* pAry = aSeq.getArray(); + for (SCCOL i=0; i<nCount; i++) + { + pAry[i].Column = aParam.pSubTotals[nPos][i]; + pAry[i].Function = ScDataUnoConversion::SubTotalToGeneral( + aParam.pFunctions[nPos][i] ); + } + return aSeq; +} + +void SAL_CALL ScSubTotalFieldObj::setSubTotalColumns( + const uno::Sequence<sheet::SubTotalColumn>& aSubTotalColumns ) +{ + SolarMutexGuard aGuard; + ScSubTotalParam aParam; + xParent->GetData(aParam); + + sal_uInt32 nColCount = aSubTotalColumns.getLength(); + if ( nColCount <= sal::static_int_cast<sal_uInt32>(SCCOL_MAX) ) + { + SCCOL nCount = static_cast<SCCOL>(nColCount); + aParam.nSubTotals[nPos] = nCount; + if (nCount != 0) + { + aParam.pSubTotals[nPos].reset(new SCCOL[nCount]); + aParam.pFunctions[nPos].reset(new ScSubTotalFunc[nCount]); + + const sheet::SubTotalColumn* pAry = aSubTotalColumns.getConstArray(); + for (SCCOL i=0; i<nCount; i++) + { + aParam.pSubTotals[nPos][i] = static_cast<SCCOL>(pAry[i].Column); + aParam.pFunctions[nPos][i] = ScDPUtil::toSubTotalFunc(static_cast<ScGeneralFunction>(pAry[i].Function)); + } + } + else + { + aParam.pSubTotals[nPos].reset(); + aParam.pFunctions[nPos].reset(); + } + } + //! otherwise exception or so? (too many columns) + + xParent->PutData(aParam); +} + +ScSubTotalDescriptorBase::ScSubTotalDescriptorBase() : + aPropSet( lcl_GetSubTotalPropertyMap() ) +{ +} + +ScSubTotalDescriptorBase::~ScSubTotalDescriptorBase() +{ +} + +// XSubTotalDescriptor + +rtl::Reference<ScSubTotalFieldObj> ScSubTotalDescriptorBase::GetObjectByIndex_Impl(sal_uInt16 nIndex) +{ + if ( nIndex < getCount() ) + return new ScSubTotalFieldObj( this, nIndex ); + return nullptr; +} + +void SAL_CALL ScSubTotalDescriptorBase::clear() +{ + SolarMutexGuard aGuard; + ScSubTotalParam aParam; + GetData(aParam); + + for (bool & rn : aParam.bGroupActive) + rn = false; + + //! notify the field objects??? + + PutData(aParam); +} + +void SAL_CALL ScSubTotalDescriptorBase::addNew( + const uno::Sequence<sheet::SubTotalColumn>& aSubTotalColumns, + sal_Int32 nGroupColumn ) +{ + SolarMutexGuard aGuard; + ScSubTotalParam aParam; + GetData(aParam); + + sal_uInt16 nPos = 0; + while ( nPos < MAXSUBTOTAL && aParam.bGroupActive[nPos] ) + ++nPos; + + sal_uInt32 nColCount = aSubTotalColumns.getLength(); + + if ( nPos >= MAXSUBTOTAL || nColCount > sal::static_int_cast<sal_uInt32>(SCCOL_MAX) ) + // too many fields / columns + throw uno::RuntimeException(); // no other exceptions specified + + aParam.bGroupActive[nPos] = true; + aParam.nField[nPos] = static_cast<SCCOL>(nGroupColumn); + + aParam.pSubTotals[nPos].reset(); + aParam.pFunctions[nPos].reset(); + + SCCOL nCount = static_cast<SCCOL>(nColCount); + aParam.nSubTotals[nPos] = nCount; + if (nCount != 0) + { + aParam.pSubTotals[nPos].reset(new SCCOL[nCount]); + aParam.pFunctions[nPos].reset(new ScSubTotalFunc[nCount]); + + const sheet::SubTotalColumn* pAry = aSubTotalColumns.getConstArray(); + for (SCCOL i=0; i<nCount; i++) + { + aParam.pSubTotals[nPos][i] = static_cast<SCCOL>(pAry[i].Column); + aParam.pFunctions[nPos][i] = ScDPUtil::toSubTotalFunc(static_cast<ScGeneralFunction>(pAry[i].Function)); + } + } + else + { + aParam.pSubTotals[nPos].reset(); + aParam.pFunctions[nPos].reset(); + } + + PutData(aParam); +} + +// flags/settings as properties + +// XEnumerationAccess + +uno::Reference<container::XEnumeration> SAL_CALL ScSubTotalDescriptorBase::createEnumeration() +{ + SolarMutexGuard aGuard; + return new ScIndexEnumeration(this, "com.sun.star.sheet.SubTotalFieldsEnumeration"); +} + +// XIndexAccess + +sal_Int32 SAL_CALL ScSubTotalDescriptorBase::getCount() +{ + SolarMutexGuard aGuard; + ScSubTotalParam aParam; + GetData(aParam); + + sal_uInt16 nCount = 0; + while ( nCount < MAXSUBTOTAL && aParam.bGroupActive[nCount] ) + ++nCount; + return nCount; +} + +uno::Any SAL_CALL ScSubTotalDescriptorBase::getByIndex( sal_Int32 nIndex ) +{ + SolarMutexGuard aGuard; + uno::Reference<sheet::XSubTotalField> xField(GetObjectByIndex_Impl(static_cast<sal_uInt16>(nIndex))); + if (!xField.is()) + throw lang::IndexOutOfBoundsException(); + + return uno::Any(xField); +} + +uno::Type SAL_CALL ScSubTotalDescriptorBase::getElementType() +{ + return cppu::UnoType<sheet::XSubTotalField>::get(); +} + +sal_Bool SAL_CALL ScSubTotalDescriptorBase::hasElements() +{ + SolarMutexGuard aGuard; + return ( getCount() != 0 ); +} + +// XPropertySet + +uno::Reference<beans::XPropertySetInfo> SAL_CALL ScSubTotalDescriptorBase::getPropertySetInfo() +{ + SolarMutexGuard aGuard; + static uno::Reference<beans::XPropertySetInfo> aRef( + new SfxItemPropertySetInfo( aPropSet.getPropertyMap() )); + return aRef; +} + +void SAL_CALL ScSubTotalDescriptorBase::setPropertyValue( + const OUString& aPropertyName, const uno::Any& aValue ) +{ + SolarMutexGuard aGuard; + ScSubTotalParam aParam; + GetData(aParam); + + // some old property names are for 5.2 compatibility + + if (aPropertyName == SC_UNONAME_CASE || aPropertyName == SC_UNONAME_ISCASE ) + aParam.bCaseSens = ScUnoHelpFunctions::GetBoolFromAny( aValue ); + else if (aPropertyName == SC_UNONAME_FORMATS || aPropertyName == SC_UNONAME_BINDFMT ) + aParam.bIncludePattern = ScUnoHelpFunctions::GetBoolFromAny( aValue ); + else if (aPropertyName == SC_UNONAME_ENABSORT ) + aParam.bDoSort = ScUnoHelpFunctions::GetBoolFromAny( aValue ); + else if (aPropertyName == SC_UNONAME_SORTASC ) + aParam.bAscending = ScUnoHelpFunctions::GetBoolFromAny( aValue ); + else if (aPropertyName == SC_UNONAME_INSBRK ) + aParam.bPagebreak = ScUnoHelpFunctions::GetBoolFromAny( aValue ); + else if (aPropertyName == SC_UNONAME_ULIST || aPropertyName == SC_UNONAME_ENUSLIST ) + aParam.bUserDef = ScUnoHelpFunctions::GetBoolFromAny( aValue ); + else if (aPropertyName == SC_UNONAME_UINDEX || aPropertyName == SC_UNONAME_USINDEX ) + { + sal_Int32 nVal = 0; + if ( aValue >>= nVal ) + aParam.nUserIndex = static_cast<sal_uInt16>(nVal); + } + else if (aPropertyName == SC_UNONAME_MAXFLD ) + { + sal_Int32 nVal = 0; + if ( (aValue >>= nVal) && nVal > sal::static_int_cast<sal_Int32>(MAXSUBTOTAL) ) + { + throw lang::IllegalArgumentException(); + } + } + + PutData(aParam); +} + +uno::Any SAL_CALL ScSubTotalDescriptorBase::getPropertyValue( const OUString& aPropertyName ) +{ + SolarMutexGuard aGuard; + ScSubTotalParam aParam; + GetData(aParam); + + uno::Any aRet; + + // some old property names are for 5.2 compatibility + + if (aPropertyName == SC_UNONAME_CASE || aPropertyName == SC_UNONAME_ISCASE ) + aRet <<= aParam.bCaseSens; + else if (aPropertyName == SC_UNONAME_FORMATS || aPropertyName == SC_UNONAME_BINDFMT ) + aRet <<= aParam.bIncludePattern; + else if (aPropertyName == SC_UNONAME_ENABSORT ) + aRet <<= aParam.bDoSort; + else if (aPropertyName == SC_UNONAME_SORTASC ) + aRet <<= aParam.bAscending; + else if (aPropertyName == SC_UNONAME_INSBRK ) + aRet <<= aParam.bPagebreak; + else if (aPropertyName == SC_UNONAME_ULIST || aPropertyName == SC_UNONAME_ENUSLIST ) + aRet <<= aParam.bUserDef; + else if (aPropertyName == SC_UNONAME_UINDEX || aPropertyName == SC_UNONAME_USINDEX ) + aRet <<= static_cast<sal_Int32>(aParam.nUserIndex); + else if (aPropertyName == SC_UNONAME_MAXFLD ) + aRet <<= sal_Int32(MAXSUBTOTAL); + + return aRet; +} + +SC_IMPL_DUMMY_PROPERTY_LISTENER( ScSubTotalDescriptorBase ) + +ScSubTotalDescriptor::ScSubTotalDescriptor() +{ +} + +ScSubTotalDescriptor::~ScSubTotalDescriptor() +{ +} + +void ScSubTotalDescriptor::GetData( ScSubTotalParam& rParam ) const +{ + rParam = aStoredParam; // query for interface +} + +void ScSubTotalDescriptor::PutData( const ScSubTotalParam& rParam ) +{ + aStoredParam = rParam; // set by the interface +} + +void ScSubTotalDescriptor::SetParam( const ScSubTotalParam& rNew ) +{ + aStoredParam = rNew; // set from outside +} + +ScRangeSubTotalDescriptor::ScRangeSubTotalDescriptor(ScDatabaseRangeObj* pPar) : + mxParent(pPar) +{ +} + +ScRangeSubTotalDescriptor::~ScRangeSubTotalDescriptor() +{ +} + +void ScRangeSubTotalDescriptor::GetData( ScSubTotalParam& rParam ) const +{ + if (mxParent.is()) + mxParent->GetSubTotalParam( rParam ); +} + +void ScRangeSubTotalDescriptor::PutData( const ScSubTotalParam& rParam ) +{ + if (mxParent.is()) + mxParent->SetSubTotalParam( rParam ); +} + +ScConsolidationDescriptor::ScConsolidationDescriptor() +{ +} + +ScConsolidationDescriptor::~ScConsolidationDescriptor() +{ +} + +void ScConsolidationDescriptor::SetParam( const ScConsolidateParam& rNew ) +{ + aParam = rNew; +} + +// XConsolidationDescriptor + +sheet::GeneralFunction SAL_CALL ScConsolidationDescriptor::getFunction() +{ + SolarMutexGuard aGuard; + return ScDataUnoConversion::SubTotalToGeneral(aParam.eFunction); +} + +void SAL_CALL ScConsolidationDescriptor::setFunction( sheet::GeneralFunction nFunction ) +{ + SolarMutexGuard aGuard; + aParam.eFunction = ScDPUtil::toSubTotalFunc(static_cast<ScGeneralFunction>(nFunction)); +} + +uno::Sequence<table::CellRangeAddress> SAL_CALL ScConsolidationDescriptor::getSources() +{ + SolarMutexGuard aGuard; + sal_uInt16 nCount = aParam.nDataAreaCount; + if (!aParam.pDataAreas) + nCount = 0; + table::CellRangeAddress aRange; + uno::Sequence<table::CellRangeAddress> aSeq(nCount); + table::CellRangeAddress* pAry = aSeq.getArray(); + for (sal_uInt16 i=0; i<nCount; i++) + { + ScArea const & rArea = aParam.pDataAreas[i]; + aRange.Sheet = rArea.nTab; + aRange.StartColumn = rArea.nColStart; + aRange.StartRow = rArea.nRowStart; + aRange.EndColumn = rArea.nColEnd; + aRange.EndRow = rArea.nRowEnd; + pAry[i] = aRange; + } + return aSeq; +} + +void SAL_CALL ScConsolidationDescriptor::setSources( + const uno::Sequence<table::CellRangeAddress>& aSources ) +{ + SolarMutexGuard aGuard; + sal_uInt16 nCount = static_cast<sal_uInt16>(aSources.getLength()); + if (nCount) + { + const table::CellRangeAddress* pAry = aSources.getConstArray(); + std::unique_ptr<ScArea[]> pNew(new ScArea[nCount]); + sal_uInt16 i; + for (i=0; i<nCount; i++) + pNew[i] = ScArea( pAry[i].Sheet, + static_cast<SCCOL>(pAry[i].StartColumn), pAry[i].StartRow, + static_cast<SCCOL>(pAry[i].EndColumn), pAry[i].EndRow ); + + aParam.SetAreas( std::move(pNew), nCount ); // copy everything + } + else + aParam.ClearDataAreas(); +} + +table::CellAddress SAL_CALL ScConsolidationDescriptor::getStartOutputPosition() +{ + SolarMutexGuard aGuard; + table::CellAddress aPos; + aPos.Column = aParam.nCol; + aPos.Row = aParam.nRow; + aPos.Sheet = aParam.nTab; + return aPos; +} + +void SAL_CALL ScConsolidationDescriptor::setStartOutputPosition( + const table::CellAddress& aStartOutputPosition ) +{ + SolarMutexGuard aGuard; + aParam.nCol = static_cast<SCCOL>(aStartOutputPosition.Column); + aParam.nRow = static_cast<SCROW>(aStartOutputPosition.Row); + aParam.nTab = aStartOutputPosition.Sheet; +} + +sal_Bool SAL_CALL ScConsolidationDescriptor::getUseColumnHeaders() +{ + SolarMutexGuard aGuard; + return aParam.bByCol; +} + +void SAL_CALL ScConsolidationDescriptor::setUseColumnHeaders( sal_Bool bUseColumnHeaders ) +{ + SolarMutexGuard aGuard; + aParam.bByCol = bUseColumnHeaders; +} + +sal_Bool SAL_CALL ScConsolidationDescriptor::getUseRowHeaders() +{ + SolarMutexGuard aGuard; + return aParam.bByRow; +} + +void SAL_CALL ScConsolidationDescriptor::setUseRowHeaders( sal_Bool bUseRowHeaders ) +{ + SolarMutexGuard aGuard; + aParam.bByRow = bUseRowHeaders; +} + +sal_Bool SAL_CALL ScConsolidationDescriptor::getInsertLinks() +{ + SolarMutexGuard aGuard; + return aParam.bReferenceData; +} + +void SAL_CALL ScConsolidationDescriptor::setInsertLinks( sal_Bool bInsertLinks ) +{ + SolarMutexGuard aGuard; + aParam.bReferenceData = bInsertLinks; +} + +ScFilterDescriptorBase::ScFilterDescriptorBase(ScDocShell* pDocShell) : + aPropSet( lcl_GetFilterPropertyMap() ), + pDocSh(pDocShell) +{ + if (pDocSh) + pDocSh->GetDocument().AddUnoObject(*this); +} + +ScFilterDescriptorBase::~ScFilterDescriptorBase() +{ + SolarMutexGuard g; + + if (pDocSh) + pDocSh->GetDocument().RemoveUnoObject(*this); +} + +void ScFilterDescriptorBase::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + if ( rHint.GetId() == SfxHintId::Dying ) + { + pDocSh = nullptr; // invalid + } +} + +// XSheetFilterDescriptor and XSheetFilterDescriptor2 + +uno::Sequence<sheet::TableFilterField> SAL_CALL ScFilterDescriptorBase::getFilterFields() +{ + SolarMutexGuard aGuard; + ScQueryParam aParam; + GetData(aParam); + + SCSIZE nEntries = aParam.GetEntryCount(); // allocated entries in Param + SCSIZE nCount = 0; // active + while ( nCount < nEntries && + aParam.GetEntry(nCount).bDoQuery ) + ++nCount; + + sheet::TableFilterField aField; + uno::Sequence<sheet::TableFilterField> aSeq(static_cast<sal_Int32>(nCount)); + sheet::TableFilterField* pAry = aSeq.getArray(); + for (SCSIZE i=0; i<nCount; i++) + { + const ScQueryEntry& rEntry = aParam.GetEntry(i); + if (rEntry.GetQueryItems().empty()) + continue; + + const ScQueryEntry::Item& rItem = rEntry.GetQueryItems().front(); + + aField.Connection = (rEntry.eConnect == SC_AND) ? sheet::FilterConnection_AND : + sheet::FilterConnection_OR; + aField.Field = rEntry.nField; + aField.IsNumeric = rItem.meType != ScQueryEntry::ByString; + aField.StringValue = rItem.maString.getString(); + aField.NumericValue = rItem.mfVal; + + switch (rEntry.eOp) // ScQueryOp + { + case SC_EQUAL: + { + aField.Operator = sheet::FilterOperator_EQUAL; + if (rEntry.IsQueryByEmpty()) + { + aField.Operator = sheet::FilterOperator_EMPTY; + aField.NumericValue = 0; + } + else if (rEntry.IsQueryByNonEmpty()) + { + aField.Operator = sheet::FilterOperator_NOT_EMPTY; + aField.NumericValue = 0; + } + } + break; + case SC_LESS: aField.Operator = sheet::FilterOperator_LESS; break; + case SC_GREATER: aField.Operator = sheet::FilterOperator_GREATER; break; + case SC_LESS_EQUAL: aField.Operator = sheet::FilterOperator_LESS_EQUAL; break; + case SC_GREATER_EQUAL: aField.Operator = sheet::FilterOperator_GREATER_EQUAL; break; + case SC_NOT_EQUAL: aField.Operator = sheet::FilterOperator_NOT_EQUAL; break; + case SC_TOPVAL: aField.Operator = sheet::FilterOperator_TOP_VALUES; break; + case SC_BOTVAL: aField.Operator = sheet::FilterOperator_BOTTOM_VALUES; break; + case SC_TOPPERC: aField.Operator = sheet::FilterOperator_TOP_PERCENT; break; + case SC_BOTPERC: aField.Operator = sheet::FilterOperator_BOTTOM_PERCENT; break; + default: + OSL_FAIL("wrong filter enum"); + aField.Operator = sheet::FilterOperator_EMPTY; + } + pAry[i] = aField; + } + return aSeq; +} + +namespace { + +template<typename T> +void convertQueryEntryToUno(const ScQueryEntry& rEntry, T& rField) +{ + rField.Connection = (rEntry.eConnect == SC_AND) ? sheet::FilterConnection_AND : sheet::FilterConnection_OR; + rField.Field = rEntry.nField; + + switch (rEntry.eOp) // ScQueryOp + { + case SC_EQUAL: rField.Operator = sheet::FilterOperator2::EQUAL; break; + case SC_LESS: rField.Operator = sheet::FilterOperator2::LESS; break; + case SC_GREATER: rField.Operator = sheet::FilterOperator2::GREATER; break; + case SC_LESS_EQUAL: rField.Operator = sheet::FilterOperator2::LESS_EQUAL; break; + case SC_GREATER_EQUAL: rField.Operator = sheet::FilterOperator2::GREATER_EQUAL; break; + case SC_NOT_EQUAL: rField.Operator = sheet::FilterOperator2::NOT_EQUAL; break; + case SC_TOPVAL: rField.Operator = sheet::FilterOperator2::TOP_VALUES; break; + case SC_BOTVAL: rField.Operator = sheet::FilterOperator2::BOTTOM_VALUES; break; + case SC_TOPPERC: rField.Operator = sheet::FilterOperator2::TOP_PERCENT; break; + case SC_BOTPERC: rField.Operator = sheet::FilterOperator2::BOTTOM_PERCENT; break; + case SC_CONTAINS: rField.Operator = sheet::FilterOperator2::CONTAINS; break; + case SC_DOES_NOT_CONTAIN: rField.Operator = sheet::FilterOperator2::DOES_NOT_CONTAIN; break; + case SC_BEGINS_WITH: rField.Operator = sheet::FilterOperator2::BEGINS_WITH; break; + case SC_DOES_NOT_BEGIN_WITH: rField.Operator = sheet::FilterOperator2::DOES_NOT_BEGIN_WITH; break; + case SC_ENDS_WITH: rField.Operator = sheet::FilterOperator2::ENDS_WITH; break; + case SC_DOES_NOT_END_WITH: rField.Operator = sheet::FilterOperator2::DOES_NOT_END_WITH; break; + default: + OSL_FAIL("Unknown filter operator value."); + rField.Operator = sheet::FilterOperator2::EMPTY; + } +} + +template<typename T> +void convertUnoToQueryEntry(const T& rField, ScQueryEntry& rEntry) +{ + rEntry.bDoQuery = true; + rEntry.eConnect = (rField.Connection == sheet::FilterConnection_AND) ? SC_AND : SC_OR; + rEntry.nField = rField.Field; + + switch (rField.Operator) // FilterOperator + { + case sheet::FilterOperator2::EQUAL: rEntry.eOp = SC_EQUAL; break; + case sheet::FilterOperator2::LESS: rEntry.eOp = SC_LESS; break; + case sheet::FilterOperator2::GREATER: rEntry.eOp = SC_GREATER; break; + case sheet::FilterOperator2::LESS_EQUAL: rEntry.eOp = SC_LESS_EQUAL; break; + case sheet::FilterOperator2::GREATER_EQUAL: rEntry.eOp = SC_GREATER_EQUAL; break; + case sheet::FilterOperator2::NOT_EQUAL: rEntry.eOp = SC_NOT_EQUAL; break; + case sheet::FilterOperator2::TOP_VALUES: rEntry.eOp = SC_TOPVAL; break; + case sheet::FilterOperator2::BOTTOM_VALUES: rEntry.eOp = SC_BOTVAL; break; + case sheet::FilterOperator2::TOP_PERCENT: rEntry.eOp = SC_TOPPERC; break; + case sheet::FilterOperator2::BOTTOM_PERCENT: rEntry.eOp = SC_BOTPERC; break; + case sheet::FilterOperator2::CONTAINS: rEntry.eOp = SC_CONTAINS; break; + case sheet::FilterOperator2::DOES_NOT_CONTAIN: rEntry.eOp = SC_DOES_NOT_CONTAIN; break; + case sheet::FilterOperator2::BEGINS_WITH: rEntry.eOp = SC_BEGINS_WITH; break; + case sheet::FilterOperator2::DOES_NOT_BEGIN_WITH: rEntry.eOp = SC_DOES_NOT_BEGIN_WITH;break; + case sheet::FilterOperator2::ENDS_WITH: rEntry.eOp = SC_ENDS_WITH; break; + case sheet::FilterOperator2::DOES_NOT_END_WITH: rEntry.eOp = SC_DOES_NOT_END_WITH; break; + case sheet::FilterOperator2::EMPTY: + rEntry.SetQueryByEmpty(); + break; + case sheet::FilterOperator2::NOT_EMPTY: + rEntry.SetQueryByNonEmpty(); + break; + default: + OSL_FAIL("Unknown filter operator type."); + rEntry.eOp = SC_EQUAL; + } +} + +void fillQueryParam( + ScQueryParam& rParam, ScDocument* pDoc, + const uno::Sequence<sheet::TableFilterField2>& aFilterFields) +{ + size_t nCount = static_cast<size_t>(aFilterFields.getLength()); + rParam.Resize(nCount); + + const sheet::TableFilterField2* pAry = aFilterFields.getConstArray(); + svl::SharedStringPool& rPool = pDoc->GetSharedStringPool(); + for (size_t i = 0; i < nCount; ++i) + { + ScQueryEntry& rEntry = rParam.GetEntry(i); + convertUnoToQueryEntry(pAry[i], rEntry); + + if (pAry[i].Operator != sheet::FilterOperator2::EMPTY && pAry[i].Operator != sheet::FilterOperator2::NOT_EMPTY) + { + ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems(); + rItems.resize(1); + ScQueryEntry::Item& rItem = rItems.front(); + rItem.meType = pAry[i].IsNumeric ? ScQueryEntry::ByValue : ScQueryEntry::ByString; + rItem.mfVal = pAry[i].NumericValue; + rItem.maString = rPool.intern(pAry[i].StringValue); + + if (rItem.meType == ScQueryEntry::ByValue) + { + OUString aStr; + pDoc->GetFormatTable()->GetInputLineString(rItem.mfVal, 0, aStr); + rItem.maString = rPool.intern(aStr); + } + } + } + + size_t nParamCount = rParam.GetEntryCount(); // if below eight Param isn't resized + for (size_t i = nCount; i < nParamCount; ++i) + rParam.GetEntry(i).bDoQuery = false; // reset surplus fields +} + +void fillQueryParam( + ScQueryParam& rParam, ScDocument* pDoc, + const uno::Sequence<sheet::TableFilterField3>& aFilterFields) +{ + size_t nCount = static_cast<size_t>(aFilterFields.getLength()); + rParam.Resize(nCount); + + svl::SharedStringPool& rPool = pDoc->GetSharedStringPool(); + const sheet::TableFilterField3* pAry = aFilterFields.getConstArray(); + for (size_t i = 0; i < nCount; ++i) + { + ScQueryEntry& rEntry = rParam.GetEntry(i); + convertUnoToQueryEntry(pAry[i], rEntry); + + if (pAry[i].Operator != sheet::FilterOperator2::EMPTY && pAry[i].Operator != sheet::FilterOperator2::NOT_EMPTY) + { + ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems(); + rItems.clear(); + const uno::Sequence<sheet::FilterFieldValue>& rVals = pAry[i].Values; + for (const auto& rVal : rVals) + { + ScQueryEntry::Item aItem; + switch (rVal.FilterType) + { + case FilterFieldType::NUMERIC: + aItem.meType = ScQueryEntry::ByValue; + break; + case FilterFieldType::STRING: + aItem.meType = ScQueryEntry::ByString; + break; + case FilterFieldType::DATE: + aItem.meType = ScQueryEntry::ByDate; + break; + case FilterFieldType::TEXT_COLOR: + aItem.meType = ScQueryEntry::ByTextColor; + break; + case FilterFieldType::BACKGROUND_COLOR: + aItem.meType = ScQueryEntry::ByBackgroundColor; + break; + } + aItem.mfVal = rVal.NumericValue; + aItem.maString = rPool.intern(rVal.StringValue); + + if (aItem.meType == ScQueryEntry::ByValue) + { + OUString aStr; + pDoc->GetFormatTable()->GetInputLineString(aItem.mfVal, 0, aStr); + aItem.maString = rPool.intern(aStr); + } + else if (aItem.meType == ScQueryEntry::ByTextColor + || aItem.meType == ScQueryEntry::ByBackgroundColor) + { + aItem.maColor = Color(ColorTransparency, rVal.ColorValue); + } + + // filter all dates starting with the given date filter YYYY or YYYY-MM and filter all datetimes + // starting with the given datetime filter YYYY-MM-DD, YYYY-MM-DD HH, or YYYY-MM-DD HH:MM + if( aItem.meType == ScQueryEntry::ByDate && aItem.maString.getLength() < 19 ) + { + ScFilterEntries aFilterEntries; + pDoc->GetFilterEntries(rEntry.nField, rParam.nRow1, rParam.nTab, aFilterEntries); + for( const auto& rFilter : aFilterEntries ) + { + if( rFilter.GetString().startsWith(rVal.StringValue) ) + { + aItem.maString = rPool.intern(rFilter.GetString()); + rItems.push_back(aItem); + } + } + } + else + { + rItems.push_back(aItem); + } + } + } + } + + size_t nParamCount = rParam.GetEntryCount(); // if below eight Param isn't resized + for (size_t i = nCount; i < nParamCount; ++i) + rParam.GetEntry(i).bDoQuery = false; // reset surplus fields +} + +} + +uno::Sequence<sheet::TableFilterField2> SAL_CALL ScFilterDescriptorBase::getFilterFields2() +{ + SolarMutexGuard aGuard; + ScQueryParam aParam; + GetData(aParam); + + SCSIZE nEntries = aParam.GetEntryCount(); // allocated entries in Param + SCSIZE nCount = 0; // active + while ( nCount < nEntries && + aParam.GetEntry(nCount).bDoQuery ) + ++nCount; + + sheet::TableFilterField2 aField; + uno::Sequence<sheet::TableFilterField2> aSeq(static_cast<sal_Int32>(nCount)); + sheet::TableFilterField2* pAry = aSeq.getArray(); + for (SCSIZE i=0; i<nCount; i++) + { + const ScQueryEntry& rEntry = aParam.GetEntry(i); + convertQueryEntryToUno(rEntry, aField); + + bool bByEmpty = false; + if (aField.Operator == sheet::FilterOperator2::EQUAL) + { + if (rEntry.IsQueryByEmpty()) + { + aField.Operator = sheet::FilterOperator2::EMPTY; + aField.NumericValue = 0; + bByEmpty = true; + } + else if (rEntry.IsQueryByNonEmpty()) + { + aField.Operator = sheet::FilterOperator2::NOT_EMPTY; + aField.NumericValue = 0; + bByEmpty = true; + } + } + + if (!bByEmpty && !rEntry.GetQueryItems().empty()) + { + const ScQueryEntry::Item& rItem = rEntry.GetQueryItems().front(); + aField.IsNumeric = rItem.meType != ScQueryEntry::ByString; + aField.StringValue = rItem.maString.getString(); + aField.NumericValue = rItem.mfVal; + } + + pAry[i] = aField; + } + return aSeq; +} + +uno::Sequence<sheet::TableFilterField3> SAL_CALL ScFilterDescriptorBase::getFilterFields3() +{ + SolarMutexGuard aGuard; + ScQueryParam aParam; + GetData(aParam); + + SCSIZE nEntries = aParam.GetEntryCount(); // allocated entries in Param + SCSIZE nCount = 0; // active + while ( nCount < nEntries && + aParam.GetEntry(nCount).bDoQuery ) + ++nCount; + + sheet::TableFilterField3 aField; + uno::Sequence<sheet::TableFilterField3> aSeq(static_cast<sal_Int32>(nCount)); + sheet::TableFilterField3* pAry = aSeq.getArray(); + for (SCSIZE i = 0; i < nCount; ++i) + { + const ScQueryEntry& rEntry = aParam.GetEntry(i); + convertQueryEntryToUno(rEntry, aField); + + bool bByEmpty = false; + if (aField.Operator == sheet::FilterOperator2::EQUAL) + { + if (rEntry.IsQueryByEmpty()) + { + aField.Operator = sheet::FilterOperator2::EMPTY; + aField.Values.realloc(1); + aField.Values.getArray()[0].NumericValue = 0; + bByEmpty = true; + } + else if (rEntry.IsQueryByNonEmpty()) + { + aField.Operator = sheet::FilterOperator2::NOT_EMPTY; + aField.Values.realloc(1); + aField.Values.getArray()[0].NumericValue = 0; + bByEmpty = true; + } + } + + if (!bByEmpty) + { + const ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems(); + size_t nItemCount = rItems.size(); + aField.Values.realloc(nItemCount); + auto pValues = aField.Values.getArray(); + size_t j = 0; + for (const auto& rItem : rItems) + { + pValues[j].IsNumeric = rItem.meType != ScQueryEntry::ByString; + pValues[j].StringValue = rItem.maString.getString(); + pValues[j].NumericValue = rItem.mfVal; + ++j; + } + } + + pAry[i] = aField; + } + return aSeq; +} + +void SAL_CALL ScFilterDescriptorBase::setFilterFields( + const uno::Sequence<sheet::TableFilterField>& aFilterFields ) +{ + SolarMutexGuard aGuard; + ScQueryParam aParam; + GetData(aParam); + + SCSIZE nCount = static_cast<SCSIZE>(aFilterFields.getLength()); + aParam.Resize( nCount ); + + ScDocument& rDoc = pDocSh->GetDocument(); + svl::SharedStringPool& rPool = rDoc.GetSharedStringPool(); + const sheet::TableFilterField* pAry = aFilterFields.getConstArray(); + SCSIZE i; + for (i=0; i<nCount; i++) + { + ScQueryEntry& rEntry = aParam.GetEntry(i); + ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems(); + rItems.resize(1); + ScQueryEntry::Item& rItem = rItems.front(); + rEntry.bDoQuery = true; + rEntry.eConnect = (pAry[i].Connection == sheet::FilterConnection_AND) ? SC_AND : SC_OR; + rEntry.nField = pAry[i].Field; + rItem.meType = pAry[i].IsNumeric ? ScQueryEntry::ByValue : ScQueryEntry::ByString; + rItem.mfVal = pAry[i].NumericValue; + rItem.maString = rPool.intern(pAry[i].StringValue); + + if (rItem.meType != ScQueryEntry::ByString) + { + OUString aStr; + rDoc.GetFormatTable()->GetInputLineString(rItem.mfVal, 0, aStr); + rItem.maString = rPool.intern(aStr); + } + + switch (pAry[i].Operator) // FilterOperator + { + case sheet::FilterOperator_EQUAL: rEntry.eOp = SC_EQUAL; break; + case sheet::FilterOperator_LESS: rEntry.eOp = SC_LESS; break; + case sheet::FilterOperator_GREATER: rEntry.eOp = SC_GREATER; break; + case sheet::FilterOperator_LESS_EQUAL: rEntry.eOp = SC_LESS_EQUAL; break; + case sheet::FilterOperator_GREATER_EQUAL: rEntry.eOp = SC_GREATER_EQUAL; break; + case sheet::FilterOperator_NOT_EQUAL: rEntry.eOp = SC_NOT_EQUAL; break; + case sheet::FilterOperator_TOP_VALUES: rEntry.eOp = SC_TOPVAL; break; + case sheet::FilterOperator_BOTTOM_VALUES: rEntry.eOp = SC_BOTVAL; break; + case sheet::FilterOperator_TOP_PERCENT: rEntry.eOp = SC_TOPPERC; break; + case sheet::FilterOperator_BOTTOM_PERCENT: rEntry.eOp = SC_BOTPERC; break; + case sheet::FilterOperator_EMPTY: + rEntry.SetQueryByEmpty(); + break; + case sheet::FilterOperator_NOT_EMPTY: + rEntry.SetQueryByNonEmpty(); + break; + default: + OSL_FAIL("Wrong query enum"); + rEntry.eOp = SC_EQUAL; + } + } + + SCSIZE nParamCount = aParam.GetEntryCount(); // if below eight Param isn't resized + for (i=nCount; i<nParamCount; i++) + aParam.GetEntry(i).bDoQuery = false; // reset surplus fields + + PutData(aParam); +} + +void SAL_CALL ScFilterDescriptorBase::setFilterFields2( + const uno::Sequence<sheet::TableFilterField2>& aFilterFields ) +{ + SolarMutexGuard aGuard; + ScQueryParam aParam; + GetData(aParam); + fillQueryParam(aParam, &pDocSh->GetDocument(), aFilterFields); + PutData(aParam); +} + +void SAL_CALL ScFilterDescriptorBase::setFilterFields3( + const uno::Sequence<sheet::TableFilterField3>& aFilterFields ) +{ + SolarMutexGuard aGuard; + ScQueryParam aParam; + GetData(aParam); + fillQueryParam(aParam, &pDocSh->GetDocument(), aFilterFields); + PutData(aParam); +} + +// Rest sind Properties + +// XPropertySet + +uno::Reference<beans::XPropertySetInfo> SAL_CALL ScFilterDescriptorBase::getPropertySetInfo() +{ + SolarMutexGuard aGuard; + static uno::Reference<beans::XPropertySetInfo> aRef( + new SfxItemPropertySetInfo( aPropSet.getPropertyMap() )); + return aRef; +} + +void SAL_CALL ScFilterDescriptorBase::setPropertyValue( + const OUString& aPropertyName, const uno::Any& aValue ) +{ + SolarMutexGuard aGuard; + ScQueryParam aParam; + GetData(aParam); + + if (aPropertyName == SC_UNONAME_CONTHDR) + aParam.bHasHeader = ScUnoHelpFunctions::GetBoolFromAny( aValue ); + else if (aPropertyName == SC_UNONAME_COPYOUT) + aParam.bInplace = !(ScUnoHelpFunctions::GetBoolFromAny( aValue )); + else if (aPropertyName == SC_UNONAME_ISCASE) + aParam.bCaseSens = ScUnoHelpFunctions::GetBoolFromAny( aValue ); + else if (aPropertyName == SC_UNONAME_MAXFLD) + { + // silently ignored + } + else if (aPropertyName == SC_UNONAME_ORIENT) + { + //! test for correct enum type? + table::TableOrientation eOrient = static_cast<table::TableOrientation>(ScUnoHelpFunctions::GetEnumFromAny( aValue )); + aParam.bByRow = ( eOrient != table::TableOrientation_COLUMNS ); + } + else if (aPropertyName == SC_UNONAME_OUTPOS) + { + table::CellAddress aAddress; + if ( aValue >>= aAddress ) + { + aParam.nDestTab = aAddress.Sheet; + aParam.nDestCol = static_cast<SCCOL>(aAddress.Column); + aParam.nDestRow = static_cast<SCROW>(aAddress.Row); + } + } + else if (aPropertyName == SC_UNONAME_SAVEOUT) + aParam.bDestPers = ScUnoHelpFunctions::GetBoolFromAny( aValue ); + else if (aPropertyName == SC_UNONAME_SKIPDUP) + aParam.bDuplicate = !(ScUnoHelpFunctions::GetBoolFromAny( aValue )); + else if (aPropertyName == SC_UNONAME_USEREGEX) + aParam.eSearchType = ScUnoHelpFunctions::GetBoolFromAny( aValue ) ? utl::SearchParam::SearchType::Regexp : + utl::SearchParam::SearchType::Normal; + + PutData(aParam); +} + +uno::Any SAL_CALL ScFilterDescriptorBase::getPropertyValue( const OUString& aPropertyName ) +{ + SolarMutexGuard aGuard; + ScQueryParam aParam; + GetData(aParam); + + uno::Any aRet; + + if (aPropertyName == SC_UNONAME_CONTHDR ) + aRet <<= aParam.bHasHeader; + else if (aPropertyName == SC_UNONAME_COPYOUT ) + aRet <<= !(aParam.bInplace); + else if (aPropertyName == SC_UNONAME_ISCASE ) + aRet <<= aParam.bCaseSens; + else if (aPropertyName == SC_UNONAME_MAXFLD ) + aRet <<= static_cast<sal_Int32>(aParam.GetEntryCount()); + else if (aPropertyName == SC_UNONAME_ORIENT ) + { + table::TableOrientation eOrient = aParam.bByRow ? table::TableOrientation_ROWS : + table::TableOrientation_COLUMNS; + aRet <<= eOrient; + } + else if (aPropertyName == SC_UNONAME_OUTPOS ) + { + table::CellAddress aOutPos; + aOutPos.Sheet = aParam.nDestTab; + aOutPos.Column = aParam.nDestCol; + aOutPos.Row = aParam.nDestRow; + aRet <<= aOutPos; + } + else if (aPropertyName == SC_UNONAME_SAVEOUT ) + aRet <<= aParam.bDestPers; + else if (aPropertyName == SC_UNONAME_SKIPDUP ) + aRet <<= !(aParam.bDuplicate); + else if (aPropertyName == SC_UNONAME_USEREGEX ) + aRet <<= (aParam.eSearchType == utl::SearchParam::SearchType::Regexp); + + return aRet; +} + +SC_IMPL_DUMMY_PROPERTY_LISTENER( ScFilterDescriptorBase ) + +ScFilterDescriptor::ScFilterDescriptor(ScDocShell* pDocShell) + : + ScFilterDescriptorBase(pDocShell) +{ +} + +ScFilterDescriptor::~ScFilterDescriptor() +{ +} + +void ScFilterDescriptor::GetData( ScQueryParam& rParam ) const +{ + rParam = aStoredParam; // query for interface +} + +void ScFilterDescriptor::PutData( const ScQueryParam& rParam ) +{ + aStoredParam = rParam; // set by the interface +} + +void ScFilterDescriptor::SetParam( const ScQueryParam& rNew ) +{ + aStoredParam = rNew; // set from outside +} + +ScRangeFilterDescriptor::ScRangeFilterDescriptor(ScDocShell* pDocShell, ScDatabaseRangeObj* pPar) : + ScFilterDescriptorBase(pDocShell), + mxParent(pPar) +{ +} + +ScRangeFilterDescriptor::~ScRangeFilterDescriptor() +{ +} + +void ScRangeFilterDescriptor::GetData( ScQueryParam& rParam ) const +{ + if (mxParent.is()) + mxParent->GetQueryParam( rParam ); +} + +void ScRangeFilterDescriptor::PutData( const ScQueryParam& rParam ) +{ + if (mxParent.is()) + mxParent->SetQueryParam( rParam ); +} + +ScDataPilotFilterDescriptor::ScDataPilotFilterDescriptor(ScDocShell* pDocShell, ScDataPilotDescriptorBase* pPar) : + ScFilterDescriptorBase(pDocShell), + mxParent(pPar) +{ +} + +ScDataPilotFilterDescriptor::~ScDataPilotFilterDescriptor() +{ +} + +void ScDataPilotFilterDescriptor::GetData( ScQueryParam& rParam ) const +{ + if (mxParent.is()) + { + ScDPObject* pDPObj = mxParent->GetDPObject(); + if (pDPObj && pDPObj->IsSheetData()) + rParam = pDPObj->GetSheetDesc()->GetQueryParam(); + } +} + +void ScDataPilotFilterDescriptor::PutData( const ScQueryParam& rParam ) +{ + if (!mxParent.is()) + return; + + ScDPObject* pDPObj = mxParent->GetDPObject(); + if (pDPObj) + { + ScSheetSourceDesc aSheetDesc(&mxParent->GetDocShell()->GetDocument()); + if (pDPObj->IsSheetData()) + aSheetDesc = *pDPObj->GetSheetDesc(); + aSheetDesc.SetQueryParam(rParam); + pDPObj->SetSheetDesc(aSheetDesc); + mxParent->SetDPObject(pDPObj); + } +} + +ScDatabaseRangeObj::ScDatabaseRangeObj(ScDocShell* pDocSh, OUString aNm) : + pDocShell( pDocSh ), + aName(std::move( aNm )), + aPropSet( lcl_GetDBRangePropertyMap() ), + bIsUnnamed(false), + aTab( 0 ) +{ + pDocShell->GetDocument().AddUnoObject(*this); +} + +ScDatabaseRangeObj::ScDatabaseRangeObj(ScDocShell* pDocSh, const SCTAB nTab) : + pDocShell( pDocSh ), + aName(STR_DB_LOCAL_NONAME), + aPropSet( lcl_GetDBRangePropertyMap() ), + bIsUnnamed(true), + aTab( nTab ) +{ + pDocShell->GetDocument().AddUnoObject(*this); +} + +ScDatabaseRangeObj::~ScDatabaseRangeObj() +{ + SolarMutexGuard g; + + if (pDocShell) + pDocShell->GetDocument().RemoveUnoObject(*this); +} + +void ScDatabaseRangeObj::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + + if ( rHint.GetId() == SfxHintId::Dying ) + pDocShell = nullptr; + else if ( auto pRefreshHint = dynamic_cast<const ScDBRangeRefreshedHint*>(&rHint) ) + { + ScDBData* pDBData = GetDBData_Impl(); + ScImportParam aParam; + pDBData->GetImportParam(aParam); + if (aParam == pRefreshHint->GetImportParam()) + Refreshed_Impl(); + } +} + +// Help functions + +ScDBData* ScDatabaseRangeObj::GetDBData_Impl() const +{ + ScDBData* pRet = nullptr; + if (pDocShell) + { + if (bIsUnnamed) + { + pRet = pDocShell->GetDocument().GetAnonymousDBData(aTab); + } + else + { + ScDBCollection* pNames = pDocShell->GetDocument().GetDBCollection(); + if (pNames) + { + ScDBData* p = pNames->getNamedDBs().findByUpperName(ScGlobal::getCharClass().uppercase(aName)); + if (p) + pRet = p; + } + } + } + return pRet; +} + +// XNamed + +OUString SAL_CALL ScDatabaseRangeObj::getName() +{ + SolarMutexGuard aGuard; + return aName; +} + +void SAL_CALL ScDatabaseRangeObj::setName( const OUString& aNewName ) +{ + SolarMutexGuard aGuard; + if (pDocShell) + { + ScDBDocFunc aFunc(*pDocShell); + bool bOk = aFunc.RenameDBRange( aName, aNewName ); + if (bOk) + aName = aNewName; + } +} + +// XDatabaseRange + +table::CellRangeAddress SAL_CALL ScDatabaseRangeObj::getDataArea() +{ + SolarMutexGuard aGuard; + table::CellRangeAddress aAddress; + ScDBData* pData = GetDBData_Impl(); + if (pData) + { + ScRange aRange; + pData->GetArea(aRange); + aAddress.Sheet = aRange.aStart.Tab(); + aAddress.StartColumn = aRange.aStart.Col(); + aAddress.StartRow = aRange.aStart.Row(); + aAddress.EndColumn = aRange.aEnd.Col(); + aAddress.EndRow = aRange.aEnd.Row(); + } + return aAddress; +} + +void SAL_CALL ScDatabaseRangeObj::setDataArea( const table::CellRangeAddress& aDataArea ) +{ + SolarMutexGuard aGuard; + ScDBData* pData = GetDBData_Impl(); + if ( pDocShell && pData ) + { + ScDBData aNewData( *pData ); + //! MoveTo ??? + aNewData.SetArea( aDataArea.Sheet, static_cast<SCCOL>(aDataArea.StartColumn), static_cast<SCROW>(aDataArea.StartRow), + static_cast<SCCOL>(aDataArea.EndColumn), static_cast<SCROW>(aDataArea.EndRow) ); + ScDBDocFunc aFunc(*pDocShell); + aFunc.ModifyDBData(aNewData); + } +} + +uno::Sequence<beans::PropertyValue> SAL_CALL ScDatabaseRangeObj::getSortDescriptor() +{ + SolarMutexGuard aGuard; + ScSortParam aParam; + const ScDBData* pData = GetDBData_Impl(); + if (pData) + { + pData->GetSortParam(aParam); + + // SortDescriptor contains the counted fields inside the area + ScRange aDBRange; + pData->GetArea(aDBRange); + SCCOLROW nFieldStart = aParam.bByRow ? static_cast<SCCOLROW>(aDBRange.aStart.Col()) : static_cast<SCCOLROW>(aDBRange.aStart.Row()); + for (sal_uInt16 i=0; i<aParam.GetSortKeyCount(); i++) + if ( aParam.maKeyState[i].bDoSort && aParam.maKeyState[i].nField >= nFieldStart ) + aParam.maKeyState[i].nField -= nFieldStart; + } + + uno::Sequence<beans::PropertyValue> aSeq( ScSortDescriptor::GetPropertyCount() ); + ScSortDescriptor::FillProperties( aSeq, aParam ); + return aSeq; +} + +void ScDatabaseRangeObj::GetQueryParam(ScQueryParam& rQueryParam) const +{ + const ScDBData* pData = GetDBData_Impl(); + if (!pData) + return; + + pData->GetQueryParam(rQueryParam); + + // FilterDescriptor contains the counted fields inside the area + ScRange aDBRange; + pData->GetArea(aDBRange); + SCCOLROW nFieldStart = rQueryParam.bByRow ? static_cast<SCCOLROW>(aDBRange.aStart.Col()) : static_cast<SCCOLROW>(aDBRange.aStart.Row()); + SCSIZE nCount = rQueryParam.GetEntryCount(); + for (SCSIZE i=0; i<nCount; i++) + { + ScQueryEntry& rEntry = rQueryParam.GetEntry(i); + if (rEntry.bDoQuery && rEntry.nField >= nFieldStart) + rEntry.nField -= nFieldStart; + } +} + +void ScDatabaseRangeObj::SetQueryParam(const ScQueryParam& rQueryParam) +{ + const ScDBData* pData = GetDBData_Impl(); + if (!pData) + return; + + // FilterDescriptor contains the counted fields inside the area + ScQueryParam aParam(rQueryParam); + ScRange aDBRange; + pData->GetArea(aDBRange); + SCCOLROW nFieldStart = aParam.bByRow ? static_cast<SCCOLROW>(aDBRange.aStart.Col()) : static_cast<SCCOLROW>(aDBRange.aStart.Row()); + + SCSIZE nCount = aParam.GetEntryCount(); + for (SCSIZE i=0; i<nCount; i++) + { + ScQueryEntry& rEntry = aParam.GetEntry(i); + if (rEntry.bDoQuery) + rEntry.nField += nFieldStart; + } + + ScDBData aNewData( *pData ); + aNewData.SetQueryParam(aParam); + aNewData.SetHeader(aParam.bHasHeader); // not in ScDBData::SetQueryParam + ScDBDocFunc aFunc(*pDocShell); + aFunc.ModifyDBData(aNewData); +} + +uno::Reference<sheet::XSheetFilterDescriptor> SAL_CALL ScDatabaseRangeObj::getFilterDescriptor() +{ + SolarMutexGuard aGuard; + return new ScRangeFilterDescriptor(pDocShell, this); +} + +void ScDatabaseRangeObj::GetSubTotalParam(ScSubTotalParam& rSubTotalParam) const +{ + const ScDBData* pData = GetDBData_Impl(); + if (!pData) + return; + + pData->GetSubTotalParam(rSubTotalParam); + + // FilterDescriptor contains the counted fields inside the area + ScRange aDBRange; + pData->GetArea(aDBRange); + SCCOL nFieldStart = aDBRange.aStart.Col(); + for (sal_uInt16 i=0; i<MAXSUBTOTAL; i++) + { + if ( rSubTotalParam.bGroupActive[i] ) + { + if ( rSubTotalParam.nField[i] >= nFieldStart ) + rSubTotalParam.nField[i] = sal::static_int_cast<SCCOL>( rSubTotalParam.nField[i] - nFieldStart ); + for (SCCOL j=0; j<rSubTotalParam.nSubTotals[i]; j++) + if ( rSubTotalParam.pSubTotals[i][j] >= nFieldStart ) + rSubTotalParam.pSubTotals[i][j] = + sal::static_int_cast<SCCOL>( rSubTotalParam.pSubTotals[i][j] - nFieldStart ); + } + } +} + +void ScDatabaseRangeObj::SetSubTotalParam(const ScSubTotalParam& rSubTotalParam) +{ + const ScDBData* pData = GetDBData_Impl(); + if (!pData) + return; + + // FilterDescriptor contains the counted fields inside the area + ScSubTotalParam aParam(rSubTotalParam); + ScRange aDBRange; + pData->GetArea(aDBRange); + SCCOL nFieldStart = aDBRange.aStart.Col(); + for (sal_uInt16 i=0; i<MAXSUBTOTAL; i++) + { + if ( aParam.bGroupActive[i] ) + { + aParam.nField[i] = sal::static_int_cast<SCCOL>( aParam.nField[i] + nFieldStart ); + for (SCCOL j=0; j<aParam.nSubTotals[i]; j++) + aParam.pSubTotals[i][j] = sal::static_int_cast<SCCOL>( aParam.pSubTotals[i][j] + nFieldStart ); + } + } + + ScDBData aNewData( *pData ); + aNewData.SetSubTotalParam(aParam); + ScDBDocFunc aFunc(*pDocShell); + aFunc.ModifyDBData(aNewData); +} + +uno::Reference<sheet::XSubTotalDescriptor> SAL_CALL ScDatabaseRangeObj::getSubTotalDescriptor() +{ + SolarMutexGuard aGuard; + return new ScRangeSubTotalDescriptor(this); +} + +uno::Sequence<beans::PropertyValue> SAL_CALL ScDatabaseRangeObj::getImportDescriptor() +{ + SolarMutexGuard aGuard; + ScImportParam aParam; + const ScDBData* pData = GetDBData_Impl(); + if (pData) + pData->GetImportParam(aParam); + + uno::Sequence<beans::PropertyValue> aSeq( ScImportDescriptor::GetPropertyCount() ); + ScImportDescriptor::FillProperties( aSeq, aParam ); + return aSeq; +} + +// XRefreshable + +void SAL_CALL ScDatabaseRangeObj::refresh() +{ + SolarMutexGuard aGuard; + ScDBData* pData = GetDBData_Impl(); + if ( !(pDocShell && pData) ) + return; + + ScDBDocFunc aFunc(*pDocShell); + + // repeat import? + bool bContinue = true; + ScImportParam aImportParam; + pData->GetImportParam( aImportParam ); + if (aImportParam.bImport && !pData->HasImportSelection()) + { + SCTAB nTab; + SCCOL nDummyCol; + SCROW nDummyRow; + pData->GetArea( nTab, nDummyCol,nDummyRow,nDummyCol,nDummyRow ); + bContinue = aFunc.DoImport( nTab, aImportParam, nullptr ); //! Api-Flag as parameter + } + + // if no error then internal operations (sort, query, subtotal) + if (bContinue) + aFunc.RepeatDB( pData->GetName(), true, bIsUnnamed, aTab ); +} + +void SAL_CALL ScDatabaseRangeObj::addRefreshListener( + const uno::Reference<util::XRefreshListener >& xListener ) +{ + SolarMutexGuard aGuard; + aRefreshListeners.emplace_back( xListener ); + + // hold one additional ref to keep this object alive as long as there are listeners + if ( aRefreshListeners.size() == 1 ) + acquire(); +} + +void SAL_CALL ScDatabaseRangeObj::removeRefreshListener( + const uno::Reference<util::XRefreshListener >& xListener ) +{ + SolarMutexGuard aGuard; + sal_uInt16 nCount = aRefreshListeners.size(); + for ( sal_uInt16 n=nCount; n--; ) + { + uno::Reference<util::XRefreshListener>& rObj = aRefreshListeners[n]; + if ( rObj == xListener ) + { + aRefreshListeners.erase( aRefreshListeners.begin() + n ); + if ( aRefreshListeners.empty() ) + release(); // release ref for listeners + break; + } + } +} + +void ScDatabaseRangeObj::Refreshed_Impl() +{ + lang::EventObject aEvent; + aEvent.Source = getXWeak(); + for (uno::Reference<util::XRefreshListener> & xRefreshListener : aRefreshListeners) + xRefreshListener->refreshed( aEvent ); +} + +// XCellRangeSource + +uno::Reference<table::XCellRange> SAL_CALL ScDatabaseRangeObj::getReferredCells() +{ + SolarMutexGuard aGuard; + ScDBData* pData = GetDBData_Impl(); + if ( pData ) + { + //! static function to create ScCellObj/ScCellRange on ScCellRangeObj ??? + ScRange aRange; + + pData->GetArea(aRange); + if ( aRange.aStart == aRange.aEnd ) + return new ScCellObj( pDocShell, aRange.aStart ); + else + return new ScCellRangeObj( pDocShell, aRange ); + } + return nullptr; +} + +// XPropertySet + +uno::Reference<beans::XPropertySetInfo> SAL_CALL ScDatabaseRangeObj::getPropertySetInfo() +{ + SolarMutexGuard aGuard; + static uno::Reference<beans::XPropertySetInfo> aRef( + new SfxItemPropertySetInfo( aPropSet.getPropertyMap() )); + return aRef; +} + +void SAL_CALL ScDatabaseRangeObj::setPropertyValue( + const OUString& aPropertyName, const uno::Any& aValue ) +{ + SolarMutexGuard aGuard; + ScDBData* pData = GetDBData_Impl(); + if ( !(pDocShell && pData) ) + return; + + ScDBData aNewData( *pData ); + bool bDo = true; + + if ( aPropertyName == SC_UNONAME_KEEPFORM ) + aNewData.SetKeepFmt( ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + else if ( aPropertyName == SC_UNONAME_MOVCELLS ) + aNewData.SetDoSize( ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + else if ( aPropertyName == SC_UNONAME_STRIPDAT ) + aNewData.SetStripData( ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + else if (aPropertyName == SC_UNONAME_AUTOFLT ) + { + bool bAutoFilter(ScUnoHelpFunctions::GetBoolFromAny( aValue )); + aNewData.SetAutoFilter(bAutoFilter); + ScRange aRange; + aNewData.GetArea(aRange); + ScDocument& rDoc = pDocShell->GetDocument(); + if (bAutoFilter) + rDoc.ApplyFlagsTab( aRange.aStart.Col(), aRange.aStart.Row(), + aRange.aEnd.Col(), aRange.aStart.Row(), + aRange.aStart.Tab(), ScMF::Auto ); + else if (!bAutoFilter) + rDoc.RemoveFlagsTab(aRange.aStart.Col(), aRange.aStart.Row(), + aRange.aEnd.Col(), aRange.aStart.Row(), + aRange.aStart.Tab(), ScMF::Auto ); + ScRange aPaintRange(aRange.aStart, aRange.aEnd); + aPaintRange.aEnd.SetRow(aPaintRange.aStart.Row()); + pDocShell->PostPaint(aPaintRange, PaintPartFlags::Grid); + } + else if (aPropertyName == SC_UNONAME_USEFLTCRT ) + { + if (ScUnoHelpFunctions::GetBoolFromAny( aValue )) + { + // only here to set bIsAdvanced in ScDBData + ScRange aRange; + (void)aNewData.GetAdvancedQuerySource(aRange); + aNewData.SetAdvancedQuerySource(&aRange); + } + else + aNewData.SetAdvancedQuerySource(nullptr); + } + else if (aPropertyName == SC_UNONAME_FLTCRT ) + { + table::CellRangeAddress aRange; + if (aValue >>= aRange) + { + ScRange aCoreRange; + ScUnoConversion::FillScRange(aCoreRange, aRange); + + aNewData.SetAdvancedQuerySource(&aCoreRange); + } + } + else if (aPropertyName == SC_UNONAME_FROMSELECT ) + { + aNewData.SetImportSelection(::cppu::any2bool(aValue)); + } + else if (aPropertyName == SC_UNONAME_REFPERIOD ) + { + sal_Int32 nRefresh = 0; + if (aValue >>= nRefresh) + { + ScDocument& rDoc = pDocShell->GetDocument(); + aNewData.SetRefreshDelay(nRefresh); + if (rDoc.GetDBCollection()) + { + aNewData.SetRefreshHandler( rDoc.GetDBCollection()->GetRefreshHandler() ); + aNewData.SetRefreshControl( &rDoc.GetRefreshTimerControlAddress() ); + } + } + } + else if (aPropertyName == SC_UNONAME_CONRES ) + { + } + else if ( aPropertyName == SC_UNONAME_TOTALSROW ) + aNewData.SetTotals( ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + else if ( aPropertyName == SC_UNONAME_CONTHDR ) + aNewData.SetHeader( ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + else + bDo = false; + + if (bDo) + { + ScDBDocFunc aFunc(*pDocShell); + aFunc.ModifyDBData(aNewData); + } +} + +uno::Any SAL_CALL ScDatabaseRangeObj::getPropertyValue( const OUString& aPropertyName ) +{ + SolarMutexGuard aGuard; + uno::Any aRet; + ScDBData* pData = GetDBData_Impl(); + if ( pData ) + { + if ( aPropertyName == SC_UNONAME_KEEPFORM ) + aRet <<= pData->IsKeepFmt(); + else if ( aPropertyName == SC_UNONAME_MOVCELLS ) + aRet <<= pData->IsDoSize(); + else if ( aPropertyName == SC_UNONAME_STRIPDAT ) + aRet <<= pData->IsStripData(); + else if ( aPropertyName == SC_UNONAME_ISUSER ) + { + // all database ranges except "unnamed" are user defined + aRet <<= (pData->GetName() != STR_DB_LOCAL_NONAME); + } + else if ( aPropertyName == SC_UNO_LINKDISPBIT ) + { + // no target bitmaps for individual entries (would be all equal) + // ScLinkTargetTypeObj::SetLinkTargetBitmap( aRet, SC_LINKTARGETTYPE_DBAREA ); + } + else if ( aPropertyName == SC_UNO_LINKDISPNAME ) + aRet <<= aName; + else if (aPropertyName == SC_UNONAME_AUTOFLT ) + { + bool bAutoFilter(GetDBData_Impl()->HasAutoFilter()); + + aRet <<= bAutoFilter; + } + else if (aPropertyName == SC_UNONAME_USEFLTCRT ) + { + ScRange aRange; + bool bIsAdvancedSource(GetDBData_Impl()->GetAdvancedQuerySource(aRange)); + + aRet <<= bIsAdvancedSource; + } + else if (aPropertyName == SC_UNONAME_FLTCRT ) + { + table::CellRangeAddress aRange; + ScRange aCoreRange; + if (GetDBData_Impl()->GetAdvancedQuerySource(aCoreRange)) + ScUnoConversion::FillApiRange(aRange, aCoreRange); + + aRet <<= aRange; + } + else if (aPropertyName == SC_UNONAME_FROMSELECT ) + { + aRet <<= GetDBData_Impl()->HasImportSelection(); + } + else if (aPropertyName == SC_UNONAME_REFPERIOD ) + { + sal_Int32 nRefresh(GetDBData_Impl()->GetRefreshDelaySeconds()); + aRet <<= nRefresh; + } + else if (aPropertyName == SC_UNONAME_CONRES ) + { + } + else if (aPropertyName == SC_UNONAME_TOKENINDEX ) + { + // get index for use in formula tokens (read-only) + aRet <<= static_cast<sal_Int32>(GetDBData_Impl()->GetIndex()); + } + else if (aPropertyName == SC_UNONAME_TOTALSROW ) + { + bool bTotals(GetDBData_Impl()->HasTotals()); + + aRet <<= bTotals; + } + else if (aPropertyName == SC_UNONAME_CONTHDR ) + { + bool bHeader(GetDBData_Impl()->HasHeader()); + + aRet <<= bHeader; + } + } + return aRet; +} + +SC_IMPL_DUMMY_PROPERTY_LISTENER( ScDatabaseRangeObj ) + +// XServiceInfo +OUString SAL_CALL ScDatabaseRangeObj::getImplementationName() +{ + return "ScDatabaseRangeObj"; +} + +sal_Bool SAL_CALL ScDatabaseRangeObj::supportsService( const OUString& rServiceName ) +{ + return cppu::supportsService(this, rServiceName); +} + +uno::Sequence<OUString> SAL_CALL ScDatabaseRangeObj::getSupportedServiceNames() +{ + return {"com.sun.star.sheet.DatabaseRange", + SCLINKTARGET_SERVICE}; +} + +ScDatabaseRangesObj::ScDatabaseRangesObj(ScDocShell* pDocSh) : + pDocShell( pDocSh ) +{ + pDocShell->GetDocument().AddUnoObject(*this); +} + +ScDatabaseRangesObj::~ScDatabaseRangesObj() +{ + SolarMutexGuard g; + + if (pDocShell) + pDocShell->GetDocument().RemoveUnoObject(*this); +} + +void ScDatabaseRangesObj::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + // reference update does not matter here + + if ( rHint.GetId() == SfxHintId::Dying ) + { + pDocShell = nullptr; + } +} + +// XDatabaseRanges + +rtl::Reference<ScDatabaseRangeObj> ScDatabaseRangesObj::GetObjectByIndex_Impl(size_t nIndex) +{ + if (!pDocShell) + return nullptr; + + ScDBCollection* pNames = pDocShell->GetDocument().GetDBCollection(); + if (!pNames) + return nullptr; + + const ScDBCollection::NamedDBs& rDBs = pNames->getNamedDBs(); + if (rDBs.empty() || nIndex >= rDBs.size()) + return nullptr; + + ScDBCollection::NamedDBs::const_iterator itr = rDBs.begin(); + ::std::advance(itr, nIndex); // boundary check is done above. + return new ScDatabaseRangeObj(pDocShell, (*itr)->GetName()); +} + +rtl::Reference<ScDatabaseRangeObj> ScDatabaseRangesObj::GetObjectByName_Impl(const OUString& aName) +{ + if ( pDocShell && hasByName(aName) ) + { + return new ScDatabaseRangeObj( pDocShell, aName ); + } + return nullptr; +} + +void SAL_CALL ScDatabaseRangesObj::addNewByName( const OUString& aName, + const table::CellRangeAddress& aRange ) +{ + SolarMutexGuard aGuard; + bool bDone = false; + if (pDocShell) + { + ScDBDocFunc aFunc(*pDocShell); + + ScRange aNameRange( static_cast<SCCOL>(aRange.StartColumn), static_cast<SCROW>(aRange.StartRow), aRange.Sheet, + static_cast<SCCOL>(aRange.EndColumn), static_cast<SCROW>(aRange.EndRow), aRange.Sheet ); + bDone = aFunc.AddDBRange( aName, aNameRange ); + } + if (!bDone) + throw uno::RuntimeException(); // no other exceptions specified +} + +void SAL_CALL ScDatabaseRangesObj::removeByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + bool bDone = false; + if (pDocShell) + { + ScDBDocFunc aFunc(*pDocShell); + bDone = aFunc.DeleteDBRange( aName ); + } + if (!bDone) + throw uno::RuntimeException(); // no other exceptions specified +} + +// XEnumerationAccess + +uno::Reference<container::XEnumeration> SAL_CALL ScDatabaseRangesObj::createEnumeration() +{ + SolarMutexGuard aGuard; + return new ScIndexEnumeration(this, "com.sun.star.sheet.DatabaseRangesEnumeration"); +} + +// XIndexAccess + +sal_Int32 SAL_CALL ScDatabaseRangesObj::getCount() +{ + SolarMutexGuard aGuard; + + //! need to omit "unnamed"? + + if (pDocShell) + { + ScDBCollection* pNames = pDocShell->GetDocument().GetDBCollection(); + if (pNames) + return static_cast<sal_Int32>(pNames->getNamedDBs().size()); + } + return 0; +} + +uno::Any SAL_CALL ScDatabaseRangesObj::getByIndex( sal_Int32 nIndex ) +{ + SolarMutexGuard aGuard; + if (nIndex < 0) + throw lang::IndexOutOfBoundsException(); + + uno::Reference<sheet::XDatabaseRange> xRange(GetObjectByIndex_Impl(static_cast<size_t>(nIndex))); + if (!xRange.is()) + throw lang::IndexOutOfBoundsException(); + + return uno::Any(xRange); +} + +uno::Type SAL_CALL ScDatabaseRangesObj::getElementType() +{ + return cppu::UnoType<sheet::XDatabaseRange>::get(); +} + +sal_Bool SAL_CALL ScDatabaseRangesObj::hasElements() +{ + SolarMutexGuard aGuard; + return ( getCount() != 0 ); +} + +// XNameAccess + +uno::Any SAL_CALL ScDatabaseRangesObj::getByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + uno::Reference<sheet::XDatabaseRange> xRange(GetObjectByName_Impl(aName)); + if (!xRange.is()) + throw container::NoSuchElementException(); + + return uno::Any(xRange); +} + +uno::Sequence<OUString> SAL_CALL ScDatabaseRangesObj::getElementNames() +{ + SolarMutexGuard aGuard; + + //! need to omit "unnamed"? + + if (pDocShell) + { + ScDBCollection* pNames = pDocShell->GetDocument().GetDBCollection(); + if (pNames) + { + const ScDBCollection::NamedDBs& rDBs = pNames->getNamedDBs(); + uno::Sequence<OUString> aSeq(rDBs.size()); + auto aSeqRange = asNonConstRange(aSeq); + size_t i = 0; + for (const auto& rDB : rDBs) + { + aSeqRange[i] = rDB->GetName(); + ++i; + } + + return aSeq; + } + } + return {}; +} + +sal_Bool SAL_CALL ScDatabaseRangesObj::hasByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + + //! need to omit "unnamed"? + + if (pDocShell) + { + ScDBCollection* pNames = pDocShell->GetDocument().GetDBCollection(); + if (pNames) + return pNames->getNamedDBs().findByUpperName(ScGlobal::getCharClass().uppercase(aName)) != nullptr; + } + return false; +} + +ScUnnamedDatabaseRangesObj::ScUnnamedDatabaseRangesObj(ScDocShell* pDocSh) : + pDocShell( pDocSh ) +{ + pDocShell->GetDocument().AddUnoObject(*this); +} + +ScUnnamedDatabaseRangesObj::~ScUnnamedDatabaseRangesObj() +{ + SolarMutexGuard g; + + if (pDocShell) + pDocShell->GetDocument().RemoveUnoObject(*this); +} + +void ScUnnamedDatabaseRangesObj::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + // reference update does not matter here + + if ( rHint.GetId() == SfxHintId::Dying ) + { + pDocShell = nullptr; + } +} + +// XUnnamedDatabaseRanges + +void ScUnnamedDatabaseRangesObj::setByTable( const table::CellRangeAddress& aRange ) +{ + SolarMutexGuard aGuard; + bool bDone = false; + if (pDocShell) + { + if ( pDocShell->GetDocument().GetTableCount() <= aRange.Sheet ) + throw lang::IndexOutOfBoundsException(); + + ScDBDocFunc aFunc(*pDocShell); + ScRange aUnnamedRange( static_cast<SCCOL>(aRange.StartColumn), static_cast<SCROW>(aRange.StartRow), aRange.Sheet, + static_cast<SCCOL>(aRange.EndColumn), static_cast<SCROW>(aRange.EndRow), aRange.Sheet ); + bDone = aFunc.AddDBRange( STR_DB_LOCAL_NONAME, aUnnamedRange ); + } + if (!bDone) + throw uno::RuntimeException(); // no other exceptions specified +} + +uno::Any ScUnnamedDatabaseRangesObj::getByTable( sal_Int32 nTab ) +{ + SolarMutexGuard aGuard; + if (!pDocShell) + throw uno::RuntimeException(); + + if ( pDocShell->GetDocument().GetTableCount() <= nTab ) + throw lang::IndexOutOfBoundsException(); + uno::Reference<sheet::XDatabaseRange> xRange( + new ScDatabaseRangeObj(pDocShell, static_cast<SCTAB>(nTab))); + if (!xRange.is()) + throw container::NoSuchElementException(); + + return uno::Any(xRange); +} + +sal_Bool ScUnnamedDatabaseRangesObj::hasByTable( sal_Int32 nTab ) +{ + SolarMutexGuard aGuard; + if (pDocShell) + { + if (pDocShell->GetDocument().GetTableCount() <= nTab) + throw lang::IndexOutOfBoundsException(); + if (pDocShell->GetDocument().GetAnonymousDBData(static_cast<SCTAB>(nTab))) + return true; + return false; + } + else + return false; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/unoobj/defltuno.cxx b/sc/source/ui/unoobj/defltuno.cxx new file mode 100644 index 0000000000..516fc8c6c7 --- /dev/null +++ b/sc/source/ui/unoobj/defltuno.cxx @@ -0,0 +1,334 @@ +/* -*- 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 <editeng/memberids.h> +#include <svl/hint.hxx> +#include <svl/itemprop.hxx> +#include <vcl/svapp.hxx> +#include <i18nlangtag/languagetag.hxx> + +#include <scitems.hxx> +#include <defltuno.hxx> +#include <miscuno.hxx> +#include <docsh.hxx> +#include <docpool.hxx> +#include <unonames.hxx> +#include <docoptio.hxx> + +#include <limits> + +class SvxFontItem; +using namespace ::com::sun::star; + +static std::span<const SfxItemPropertyMapEntry> lcl_GetDocDefaultsMap() +{ + static const SfxItemPropertyMapEntry aDocDefaultsMap_Impl[] = + { + { SC_UNONAME_CFCHARS, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_CHAR_SET }, + { SC_UNO_CJK_CFCHARS, ATTR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_CHAR_SET }, + { SC_UNO_CTL_CFCHARS, ATTR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_CHAR_SET }, + { SC_UNONAME_CFFAMIL, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY }, + { SC_UNO_CJK_CFFAMIL, ATTR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY }, + { SC_UNO_CTL_CFFAMIL, ATTR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY }, + { SC_UNONAME_CFNAME, ATTR_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_FAMILY_NAME }, + { SC_UNO_CJK_CFNAME, ATTR_CJK_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_FAMILY_NAME }, + { SC_UNO_CTL_CFNAME, ATTR_CTL_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_FAMILY_NAME }, + { SC_UNONAME_CFPITCH, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_PITCH }, + { SC_UNO_CJK_CFPITCH, ATTR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_PITCH }, + { SC_UNO_CTL_CFPITCH, ATTR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_PITCH }, + { SC_UNONAME_CFSTYLE, ATTR_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_STYLE_NAME }, + { SC_UNO_CJK_CFSTYLE, ATTR_CJK_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_STYLE_NAME }, + { SC_UNO_CTL_CFSTYLE, ATTR_CTL_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_STYLE_NAME }, + { SC_UNONAME_CLOCAL, ATTR_FONT_LANGUAGE, cppu::UnoType<lang::Locale>::get(), 0, MID_LANG_LOCALE }, + { SC_UNO_CJK_CLOCAL, ATTR_CJK_FONT_LANGUAGE, cppu::UnoType<lang::Locale>::get(), 0, MID_LANG_LOCALE }, + { SC_UNO_CTL_CLOCAL, ATTR_CTL_FONT_LANGUAGE, cppu::UnoType<lang::Locale>::get(), 0, MID_LANG_LOCALE }, + { SC_UNONAME_CHEIGHT, ATTR_FONT_HEIGHT, cppu::UnoType<float>::get(), 0, MID_FONTHEIGHT | CONVERT_TWIPS }, + { SC_UNO_CJK_CHEIGHT, ATTR_CJK_FONT_HEIGHT, cppu::UnoType<float>::get(), 0, MID_FONTHEIGHT | CONVERT_TWIPS }, + { SC_UNO_CTL_CHEIGHT, ATTR_CTL_FONT_HEIGHT, cppu::UnoType<float>::get(), 0, MID_FONTHEIGHT | CONVERT_TWIPS }, + { SC_UNO_STANDARDDEC, 0, cppu::UnoType<sal_Int16>::get(), 0, 0 }, + { SC_UNO_TABSTOPDIS, 0, cppu::UnoType<sal_Int32>::get(), 0, 0 }, + }; + return aDocDefaultsMap_Impl; +} + +using sc::TwipsToEvenHMM; + +SC_SIMPLE_SERVICE_INFO( ScDocDefaultsObj, "ScDocDefaultsObj", "com.sun.star.sheet.Defaults" ) + +ScDocDefaultsObj::ScDocDefaultsObj(ScDocShell* pDocSh) : + pDocShell( pDocSh ), + aPropertyMap(lcl_GetDocDefaultsMap()) +{ + pDocShell->GetDocument().AddUnoObject(*this); +} + +ScDocDefaultsObj::~ScDocDefaultsObj() +{ + SolarMutexGuard g; + + if (pDocShell) + pDocShell->GetDocument().RemoveUnoObject(*this); +} + +void ScDocDefaultsObj::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + if ( rHint.GetId() == SfxHintId::Dying ) + { + pDocShell = nullptr; // document gone + } +} + +void ScDocDefaultsObj::ItemsChanged() +{ + if (pDocShell) + { + //! if not in XML import, adjust row heights + const auto & rDoc = pDocShell->GetDocument(); + pDocShell->PostPaint(ScRange(0, 0, 0, rDoc.MaxCol(), rDoc.MaxRow(), MAXTAB), PaintPartFlags::Grid); + } +} + +// XPropertySet + +uno::Reference<beans::XPropertySetInfo> SAL_CALL ScDocDefaultsObj::getPropertySetInfo() +{ + SolarMutexGuard aGuard; + static uno::Reference<beans::XPropertySetInfo> aRef = new SfxItemPropertySetInfo( + aPropertyMap ); + return aRef; +} + +void SAL_CALL ScDocDefaultsObj::setPropertyValue( + const OUString& aPropertyName, const uno::Any& aValue ) +{ + SolarMutexGuard aGuard; + + if ( !pDocShell ) + throw uno::RuntimeException(); + + const SfxItemPropertyMapEntry* pEntry = aPropertyMap.getByName( aPropertyName ); + if ( !pEntry ) + throw beans::UnknownPropertyException(aPropertyName); + if(!pEntry->nWID) + { + if(aPropertyName ==SC_UNO_STANDARDDEC) + { + ScDocument& rDoc = pDocShell->GetDocument(); + ScDocOptions aDocOpt(rDoc.GetDocOptions()); + sal_Int16 nValue = 0; + if (aValue >>= nValue) + { + aDocOpt.SetStdPrecision(static_cast<sal_uInt16> (nValue)); + rDoc.SetDocOptions(aDocOpt); + } + } + else if (aPropertyName == SC_UNO_TABSTOPDIS) + { + ScDocument& rDoc = pDocShell->GetDocument(); + ScDocOptions aDocOpt(rDoc.GetDocOptions()); + sal_Int32 nValue = 0; + if (aValue >>= nValue) + { + aDocOpt.SetTabDistance(o3tl::toTwips(nValue, o3tl::Length::mm100)); + rDoc.SetDocOptions(aDocOpt); + } + } + } + else if ( pEntry->nWID == ATTR_FONT_LANGUAGE || + pEntry->nWID == ATTR_CJK_FONT_LANGUAGE || + pEntry->nWID == ATTR_CTL_FONT_LANGUAGE ) + { + // for getPropertyValue the PoolDefaults are sufficient, + // but setPropertyValue has to be handled differently + + lang::Locale aLocale; + if ( aValue >>= aLocale ) + { + LanguageType eNew; + if (!aLocale.Language.isEmpty() || !aLocale.Country.isEmpty()) + eNew = LanguageTag::convertToLanguageType( aLocale, false); + else + eNew = LANGUAGE_NONE; + + ScDocument& rDoc = pDocShell->GetDocument(); + LanguageType eLatin, eCjk, eCtl; + rDoc.GetLanguage( eLatin, eCjk, eCtl ); + + if ( pEntry->nWID == ATTR_CJK_FONT_LANGUAGE ) + eCjk = eNew; + else if ( pEntry->nWID == ATTR_CTL_FONT_LANGUAGE ) + eCtl = eNew; + else + eLatin = eNew; + + rDoc.SetLanguage( eLatin, eCjk, eCtl ); + } + } + else + { + ScDocumentPool* pPool = pDocShell->GetDocument().GetPool(); + std::unique_ptr<SfxPoolItem> pNewItem(pPool->GetDefaultItem(pEntry->nWID).Clone()); + + if( !pNewItem->PutValue( aValue, pEntry->nMemberId ) ) + throw lang::IllegalArgumentException(); + + pPool->SetPoolDefaultItem( *pNewItem ); + + ItemsChanged(); + } +} + +uno::Any SAL_CALL ScDocDefaultsObj::getPropertyValue( const OUString& aPropertyName ) +{ + // use pool default if set + + SolarMutexGuard aGuard; + + if ( !pDocShell ) + throw uno::RuntimeException(); + + uno::Any aRet; + const SfxItemPropertyMapEntry* pEntry = aPropertyMap.getByName( aPropertyName ); + if ( !pEntry ) + throw beans::UnknownPropertyException(aPropertyName); + + if (!pEntry->nWID) + { + if(aPropertyName == SC_UNO_STANDARDDEC) + { + ScDocument& rDoc = pDocShell->GetDocument(); + const ScDocOptions& aDocOpt = rDoc.GetDocOptions(); + sal_uInt16 nPrec = aDocOpt.GetStdPrecision(); + // the max value of unsigned 16-bit integer is used as the flag + // value for unlimited precision, c.f. + // SvNumberFormatter::UNLIMITED_PRECISION. + if (nPrec <= ::std::numeric_limits<sal_Int16>::max()) + aRet <<= static_cast<sal_Int16> (nPrec); + } + else if (aPropertyName == SC_UNO_TABSTOPDIS) + { + ScDocument& rDoc = pDocShell->GetDocument(); + const ScDocOptions& aDocOpt = rDoc.GetDocOptions(); + sal_Int32 nValue (TwipsToEvenHMM(aDocOpt.GetTabDistance())); + aRet <<= nValue; + } + } + else + { + ScDocumentPool* pPool = pDocShell->GetDocument().GetPool(); + const SfxPoolItem& rItem = pPool->GetDefaultItem( pEntry->nWID ); + rItem.QueryValue( aRet, pEntry->nMemberId ); + } + return aRet; +} + +SC_IMPL_DUMMY_PROPERTY_LISTENER( ScDocDefaultsObj ) + +// XPropertyState + +beans::PropertyState SAL_CALL ScDocDefaultsObj::getPropertyState( const OUString& aPropertyName ) +{ + SolarMutexGuard aGuard; + + if ( !pDocShell ) + throw uno::RuntimeException(); + + const SfxItemPropertyMapEntry* pEntry = aPropertyMap.getByName( aPropertyName ); + if ( !pEntry ) + throw beans::UnknownPropertyException(aPropertyName); + + beans::PropertyState eRet = beans::PropertyState_DEFAULT_VALUE; + + sal_uInt16 nWID = pEntry->nWID; + if ( nWID == ATTR_FONT || nWID == ATTR_CJK_FONT || nWID == ATTR_CTL_FONT || !nWID ) + { + // static default for font is system-dependent, + // so font default is always treated as "direct value". + + eRet = beans::PropertyState_DIRECT_VALUE; + } + else + { + // check if pool default is set + + ScDocumentPool* pPool = pDocShell->GetDocument().GetPool(); + if ( pPool->GetPoolDefaultItem( nWID ) != nullptr ) + eRet = beans::PropertyState_DIRECT_VALUE; + } + + return eRet; +} + +uno::Sequence<beans::PropertyState> SAL_CALL ScDocDefaultsObj::getPropertyStates( + const uno::Sequence<OUString>& aPropertyNames ) +{ + // the simple way: call getPropertyState + + SolarMutexGuard aGuard; + uno::Sequence<beans::PropertyState> aRet(aPropertyNames.getLength()); + std::transform(aPropertyNames.begin(), aPropertyNames.end(), aRet.getArray(), + [this](const OUString& rName) -> beans::PropertyState { return getPropertyState(rName); }); + return aRet; +} + +void SAL_CALL ScDocDefaultsObj::setPropertyToDefault( const OUString& aPropertyName ) +{ + SolarMutexGuard aGuard; + + if ( !pDocShell ) + throw uno::RuntimeException(); + + const SfxItemPropertyMapEntry* pEntry = aPropertyMap.getByName( aPropertyName ); + if ( !pEntry ) + throw beans::UnknownPropertyException(aPropertyName); + + if (pEntry->nWID) + { + ScDocumentPool* pPool = pDocShell->GetDocument().GetPool(); + pPool->ResetPoolDefaultItem( pEntry->nWID ); + + ItemsChanged(); + } +} + +uno::Any SAL_CALL ScDocDefaultsObj::getPropertyDefault( const OUString& aPropertyName ) +{ + // always use static default + + SolarMutexGuard aGuard; + + if ( !pDocShell ) + throw uno::RuntimeException(); + + const SfxItemPropertyMapEntry* pEntry = aPropertyMap.getByName( aPropertyName ); + if ( !pEntry ) + throw beans::UnknownPropertyException(aPropertyName); + + uno::Any aRet; + if (pEntry->nWID) + { + ScDocumentPool* pPool = pDocShell->GetDocument().GetPool(); + const SfxPoolItem* pItem = pPool->GetItem2Default( pEntry->nWID ); + if (pItem) + pItem->QueryValue( aRet, pEntry->nMemberId ); + } + return aRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/unoobj/dispuno.cxx b/sc/source/ui/unoobj/dispuno.cxx new file mode 100644 index 0000000000..536d271d64 --- /dev/null +++ b/sc/source/ui/unoobj/dispuno.cxx @@ -0,0 +1,366 @@ +/* -*- 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 <sfx2/viewfrm.hxx> +#include <svx/dataaccessdescriptor.hxx> +#include <svl/hint.hxx> +#include <vcl/svapp.hxx> + +#include <com/sun/star/frame/XDispatchProviderInterception.hpp> +#include <com/sun/star/view/XSelectionSupplier.hpp> +#include <com/sun/star/sdb/CommandType.hpp> + +#include <dispuno.hxx> +#include <tabvwsh.hxx> +#include <dbdocfun.hxx> +#include <dbdata.hxx> + +using namespace com::sun::star; + +const char cURLInsertColumns[] = ".uno:DataSourceBrowser/InsertColumns"; //data into text +constexpr OUString cURLDocDataSource = u".uno:DataSourceBrowser/DocumentDataSource"_ustr; + +static uno::Reference<view::XSelectionSupplier> lcl_GetSelectionSupplier( const SfxViewShell* pViewShell ) +{ + if ( pViewShell ) + { + SfxViewFrame& rViewFrame = pViewShell->GetViewFrame(); + return uno::Reference<view::XSelectionSupplier>( rViewFrame.GetFrame().GetController(), uno::UNO_QUERY ); + } + return uno::Reference<view::XSelectionSupplier>(); +} + +ScDispatchProviderInterceptor::ScDispatchProviderInterceptor(ScTabViewShell* pViewSh) : + pViewShell( pViewSh ) +{ + if ( !pViewShell ) + return; + + m_xIntercepted.set(uno::Reference<frame::XDispatchProviderInterception>(pViewShell->GetViewFrame().GetFrame().GetFrameInterface(), uno::UNO_QUERY)); + if (m_xIntercepted.is()) + { + osl_atomic_increment( &m_refCount ); + + m_xIntercepted->registerDispatchProviderInterceptor( + static_cast<frame::XDispatchProviderInterceptor*>(this)); + // this should make us the top-level dispatch-provider for the component, via a call to our + // setDispatchProvider we should have got a fallback for requests we (i.e. our master) cannot fulfill + uno::Reference<lang::XComponent> xInterceptedComponent(m_xIntercepted, uno::UNO_QUERY); + if (xInterceptedComponent.is()) + xInterceptedComponent->addEventListener(static_cast<lang::XEventListener*>(this)); + + osl_atomic_decrement( &m_refCount ); + } + + StartListening(*pViewShell); +} + +ScDispatchProviderInterceptor::~ScDispatchProviderInterceptor() +{ + if (pViewShell) + EndListening(*pViewShell); +} + +void ScDispatchProviderInterceptor::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + if ( rHint.GetId() == SfxHintId::Dying ) + pViewShell = nullptr; +} + +// XDispatchProvider + +uno::Reference<frame::XDispatch> SAL_CALL ScDispatchProviderInterceptor::queryDispatch( + const util::URL& aURL, const OUString& aTargetFrameName, + sal_Int32 nSearchFlags ) +{ + SolarMutexGuard aGuard; + + uno::Reference<frame::XDispatch> xResult; + // create some dispatch ... + if ( pViewShell && ( + aURL.Complete == cURLInsertColumns || + aURL.Complete == cURLDocDataSource ) ) + { + if (!m_xMyDispatch.is()) + m_xMyDispatch = new ScDispatch( pViewShell ); + xResult = m_xMyDispatch; + } + + // ask our slave provider + if (!xResult.is() && m_xSlaveDispatcher.is()) + xResult = m_xSlaveDispatcher->queryDispatch(aURL, aTargetFrameName, nSearchFlags); + + return xResult; +} + +uno::Sequence< uno::Reference<frame::XDispatch> > SAL_CALL + ScDispatchProviderInterceptor::queryDispatches( + const uno::Sequence<frame::DispatchDescriptor>& aDescripts ) +{ + SolarMutexGuard aGuard; + + uno::Sequence< uno::Reference< frame::XDispatch> > aReturn(aDescripts.getLength()); + std::transform(aDescripts.begin(), aDescripts.end(), aReturn.getArray(), + [this](const frame::DispatchDescriptor& rDescr) -> uno::Reference<frame::XDispatch> { + return queryDispatch(rDescr.FeatureURL, rDescr.FrameName, rDescr.SearchFlags); }); + return aReturn; +} + +// XDispatchProviderInterceptor + +uno::Reference<frame::XDispatchProvider> SAL_CALL + ScDispatchProviderInterceptor::getSlaveDispatchProvider() +{ + SolarMutexGuard aGuard; + return m_xSlaveDispatcher; +} + +void SAL_CALL ScDispatchProviderInterceptor::setSlaveDispatchProvider( + const uno::Reference<frame::XDispatchProvider>& xNewDispatchProvider ) +{ + SolarMutexGuard aGuard; + m_xSlaveDispatcher.set(xNewDispatchProvider); +} + +uno::Reference<frame::XDispatchProvider> SAL_CALL + ScDispatchProviderInterceptor::getMasterDispatchProvider() +{ + SolarMutexGuard aGuard; + return m_xMasterDispatcher; +} + +void SAL_CALL ScDispatchProviderInterceptor::setMasterDispatchProvider( + const uno::Reference<frame::XDispatchProvider>& xNewSupplier ) +{ + SolarMutexGuard aGuard; + m_xMasterDispatcher.set(xNewSupplier); +} + +// XEventListener + +void SAL_CALL ScDispatchProviderInterceptor::disposing( const lang::EventObject& /* Source */ ) +{ + SolarMutexGuard aGuard; + + if (m_xIntercepted.is()) + { + m_xIntercepted->releaseDispatchProviderInterceptor( + static_cast<frame::XDispatchProviderInterceptor*>(this)); + uno::Reference<lang::XComponent> xInterceptedComponent(m_xIntercepted, uno::UNO_QUERY); + if (xInterceptedComponent.is()) + xInterceptedComponent->removeEventListener(static_cast<lang::XEventListener*>(this)); + + m_xMyDispatch = nullptr; + } + m_xIntercepted = nullptr; +} + +ScDispatch::ScDispatch(ScTabViewShell* pViewSh) : + pViewShell( pViewSh ), + bListeningToView( false ) +{ + if (pViewShell) + StartListening(*pViewShell); +} + +ScDispatch::~ScDispatch() +{ + if (pViewShell) + EndListening(*pViewShell); + + if (bListeningToView && pViewShell) + { + uno::Reference<view::XSelectionSupplier> xSupplier(lcl_GetSelectionSupplier( pViewShell )); + if ( xSupplier.is() ) + xSupplier->removeSelectionChangeListener(this); + } +} + +void ScDispatch::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + if ( rHint.GetId() == SfxHintId::Dying ) + pViewShell = nullptr; +} + +// XDispatch + +void SAL_CALL ScDispatch::dispatch( const util::URL& aURL, + const uno::Sequence<beans::PropertyValue>& aArgs ) +{ + SolarMutexGuard aGuard; + + bool bDone = false; + if ( pViewShell && aURL.Complete == cURLInsertColumns ) + { + ScViewData& rViewData = pViewShell->GetViewData(); + ScAddress aPos( rViewData.GetCurX(), rViewData.GetCurY(), rViewData.GetTabNo() ); + + ScDBDocFunc aFunc( *rViewData.GetDocShell() ); + aFunc.DoImportUno( aPos, aArgs ); + bDone = true; + } + // cURLDocDataSource is never dispatched + + if (!bDone) + throw uno::RuntimeException(); +} + +static void lcl_FillDataSource( frame::FeatureStateEvent& rEvent, const ScImportParam& rParam ) +{ + rEvent.IsEnabled = rParam.bImport; + + svx::ODataAccessDescriptor aDescriptor; + if ( rParam.bImport ) + { + sal_Int32 nType = rParam.bSql ? sdb::CommandType::COMMAND : + ( (rParam.nType == ScDbQuery) ? sdb::CommandType::QUERY : + sdb::CommandType::TABLE ); + + aDescriptor.setDataSource(rParam.aDBName); + aDescriptor[svx::DataAccessDescriptorProperty::Command] <<= rParam.aStatement; + aDescriptor[svx::DataAccessDescriptorProperty::CommandType] <<= nType; + } + else + { + // descriptor has to be complete anyway + + aDescriptor[svx::DataAccessDescriptorProperty::DataSource] <<= OUString(); + aDescriptor[svx::DataAccessDescriptorProperty::Command] <<= OUString(); + aDescriptor[svx::DataAccessDescriptorProperty::CommandType] <<= sal_Int32(sdb::CommandType::TABLE); + } + rEvent.State <<= aDescriptor.createPropertyValueSequence(); +} + +void SAL_CALL ScDispatch::addStatusListener( + const uno::Reference<frame::XStatusListener>& xListener, + const util::URL& aURL) +{ + SolarMutexGuard aGuard; + + if (!pViewShell) + throw uno::RuntimeException(); + + // initial state + frame::FeatureStateEvent aEvent; + aEvent.IsEnabled = true; + aEvent.Source = getXWeak(); + aEvent.FeatureURL = aURL; + + if ( aURL.Complete == cURLDocDataSource ) + { + aDataSourceListeners.emplace_back( xListener ); + + if (!bListeningToView) + { + uno::Reference<view::XSelectionSupplier> xSupplier(lcl_GetSelectionSupplier( pViewShell )); + if ( xSupplier.is() ) + xSupplier->addSelectionChangeListener(this); + bListeningToView = true; + } + + ScDBData* pDBData = pViewShell->GetDBData(false,SC_DB_OLD); + if ( pDBData ) + pDBData->GetImportParam( aLastImport ); + lcl_FillDataSource( aEvent, aLastImport ); // modifies State, IsEnabled + } + //! else add to listener for "enabled" changes? + + xListener->statusChanged( aEvent ); +} + +void SAL_CALL ScDispatch::removeStatusListener( + const uno::Reference<frame::XStatusListener>& xListener, + const util::URL& aURL ) +{ + SolarMutexGuard aGuard; + + if ( aURL.Complete != cURLDocDataSource ) + return; + + sal_uInt16 nCount = aDataSourceListeners.size(); + for ( sal_uInt16 n=nCount; n--; ) + { + uno::Reference<frame::XStatusListener>& rObj = aDataSourceListeners[n]; + if ( rObj == xListener ) + { + aDataSourceListeners.erase( aDataSourceListeners.begin() + n ); + break; + } + } + + if ( aDataSourceListeners.empty() && pViewShell ) + { + uno::Reference<view::XSelectionSupplier> xSupplier(lcl_GetSelectionSupplier( pViewShell )); + if ( xSupplier.is() ) + xSupplier->removeSelectionChangeListener(this); + bListeningToView = false; + } +} + +// XSelectionChangeListener + +void SAL_CALL ScDispatch::selectionChanged( const css::lang::EventObject& /* aEvent */ ) +{ + // currently only called for URL cURLDocDataSource + + if ( !pViewShell ) + return; + + ScImportParam aNewImport; + ScDBData* pDBData = pViewShell->GetDBData(false,SC_DB_OLD); + if ( pDBData ) + pDBData->GetImportParam( aNewImport ); + + // notify listeners only if data source has changed + if ( !(aNewImport.bImport != aLastImport.bImport || + aNewImport.aDBName != aLastImport.aDBName || + aNewImport.aStatement != aLastImport.aStatement || + aNewImport.bSql != aLastImport.bSql || + aNewImport.nType != aLastImport.nType) ) + return; + + frame::FeatureStateEvent aEvent; + aEvent.Source = getXWeak(); + aEvent.FeatureURL.Complete = cURLDocDataSource; + + lcl_FillDataSource( aEvent, aNewImport ); // modifies State, IsEnabled + + for (uno::Reference<frame::XStatusListener> & xDataSourceListener : aDataSourceListeners) + xDataSourceListener->statusChanged( aEvent ); + + aLastImport = aNewImport; +} + +// XEventListener + +void SAL_CALL ScDispatch::disposing( const css::lang::EventObject& rSource ) +{ + uno::Reference<view::XSelectionSupplier> xSupplier(rSource.Source, uno::UNO_QUERY); + xSupplier->removeSelectionChangeListener(this); + bListeningToView = false; + + lang::EventObject aEvent; + aEvent.Source = getXWeak(); + for (uno::Reference<frame::XStatusListener> & xDataSourceListener : aDataSourceListeners) + xDataSourceListener->disposing( aEvent ); + + pViewShell = nullptr; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/unoobj/docuno.cxx b/sc/source/ui/unoobj/docuno.cxx new file mode 100644 index 0000000000..c906f39336 --- /dev/null +++ b/sc/source/ui/unoobj/docuno.cxx @@ -0,0 +1,4984 @@ +/* -*- 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 <config_feature_opencl.h> + +#include <scitems.hxx> + +#include <comphelper/propertyvalue.hxx> +#include <comphelper/sequence.hxx> +#include <editeng/brushitem.hxx> +#include <editeng/editview.hxx> +#include <editeng/memberids.h> +#include <editeng/outliner.hxx> +#include <o3tl/any.hxx> +#include <o3tl/safeint.hxx> +#include <svx/fmview.hxx> +#include <svx/svditer.hxx> +#include <svx/svdpage.hxx> +#include <svx/svxids.hrc> + +#include <LibreOfficeKit/LibreOfficeKitEnums.h> +#include <comphelper/propertysequence.hxx> +#include <officecfg/Office/Common.hxx> +#include <officecfg/Office/Calc.hxx> +#include <svl/numuno.hxx> +#include <svl/hint.hxx> +#include <unotools/moduleoptions.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/viewfrm.hxx> +#include <svx/unopage.hxx> +#include <vcl/pdfextoutdevdata.hxx> +#include <vcl/print.hxx> +#include <vcl/svapp.hxx> +#include <tools/json_writer.hxx> +#include <tools/multisel.hxx> +#include <tools/UnitConversion.hxx> +#include <toolkit/awt/vclxdevice.hxx> + +#include <float.h> + +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/util/Date.hpp> +#include <com/sun/star/util/XTheme.hpp> +#include <com/sun/star/sheet/XNamedRanges.hpp> +#include <com/sun/star/sheet/XLabelRanges.hpp> +#include <com/sun/star/sheet/XSelectedSheetsSupplier.hpp> +#include <com/sun/star/sheet/XUnnamedDatabaseRanges.hpp> +#include <com/sun/star/i18n/XForbiddenCharacters.hpp> +#include <com/sun/star/script/XLibraryContainer.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/lang/ServiceNotRegisteredException.hpp> +#include <com/sun/star/document/XDocumentEventBroadcaster.hpp> +#include <com/sun/star/script/XInvocation.hpp> +#include <com/sun/star/script/vba/XVBAEventProcessor.hpp> +#include <com/sun/star/beans/XFastPropertySet.hpp> +#include <comphelper/indexedpropertyvalues.hxx> +#include <comphelper/lok.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/profilezone.hxx> +#include <comphelper/servicehelper.hxx> +#include <comphelper/string.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <cppuhelper/supportsservice.hxx> +#if HAVE_FEATURE_OPENCL +#include <opencl/platforminfo.hxx> +#endif +#include <sfx2/lokhelper.hxx> +#include <sfx2/lokcomponenthelpers.hxx> +#include <sfx2/LokControlHandler.hxx> +#include <docmodel/uno/UnoTheme.hxx> +#include <docmodel/theme/Theme.hxx> + +#include <cellsuno.hxx> +#include <columnspanset.hxx> +#include <convuno.hxx> +#include <datauno.hxx> +#include <docfunc.hxx> +#include <docoptio.hxx> +#include <docsh.hxx> +#include <docuno.hxx> +#include <drwlayer.hxx> +#include <forbiuno.hxx> +#include <formulagroup.hxx> +#include <gridwin.hxx> +#include <hints.hxx> +#include <inputhdl.hxx> +#include <inputopt.hxx> +#include <interpre.hxx> +#include <linkuno.hxx> +#include <markdata.hxx> +#include <miscuno.hxx> +#include <nameuno.hxx> +#include <notesuno.hxx> +#include <optuno.hxx> +#include <pfuncache.hxx> +#include <postit.hxx> +#include <printfun.hxx> +#include <rangeutl.hxx> +#include <scmod.hxx> +#include <scresid.hxx> +#include <servuno.hxx> +#include <shapeuno.hxx> +#include <sheetevents.hxx> +#include <styleuno.hxx> +#include <tabvwsh.hxx> +#include <targuno.hxx> +#include <unonames.hxx> +#include <ViewSettingsSequenceDefines.hxx> +#include <editsh.hxx> +#include <drawsh.hxx> +#include <drtxtob.hxx> +#include <transobj.hxx> +#include <chgtrack.hxx> +#include <table.hxx> +#include <appoptio.hxx> +#include <formulaopt.hxx> + +#include <strings.hrc> + +using namespace com::sun::star; + +// #i111553# provides the name of the VBA constant for this document type (e.g. 'ThisExcelDoc' for Calc) +constexpr OUString SC_UNO_VBAGLOBNAME = u"VBAGlobalConstantName"_ustr; + +// no Which-ID here, map only for PropertySetInfo + +//! rename this, those are no longer only options +static std::span<const SfxItemPropertyMapEntry> lcl_GetDocOptPropertyMap() +{ + static const SfxItemPropertyMapEntry aDocOptPropertyMap_Impl[] = + { + { SC_UNO_APPLYFMDES, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_AREALINKS, 0, cppu::UnoType<sheet::XAreaLinks>::get(), 0, 0}, + { SC_UNO_AUTOCONTFOC, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_BASICLIBRARIES, 0, cppu::UnoType<script::XLibraryContainer>::get(), beans::PropertyAttribute::READONLY, 0}, + { SC_UNO_DIALOGLIBRARIES, 0, cppu::UnoType<script::XLibraryContainer>::get(), beans::PropertyAttribute::READONLY, 0}, + { SC_UNO_VBAGLOBNAME, 0, cppu::UnoType<OUString>::get(), beans::PropertyAttribute::READONLY, 0}, + { SC_UNO_CALCASSHOWN, PROP_UNO_CALCASSHOWN, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNONAME_CLOCAL, 0, cppu::UnoType<lang::Locale>::get(), 0, 0}, + { SC_UNO_CJK_CLOCAL, 0, cppu::UnoType<lang::Locale>::get(), 0, 0}, + { SC_UNO_CTL_CLOCAL, 0, cppu::UnoType<lang::Locale>::get(), 0, 0}, + { SC_UNO_COLLABELRNG, 0, cppu::UnoType<sheet::XLabelRanges>::get(), 0, 0}, + { SC_UNO_DDELINKS, 0, cppu::UnoType<container::XNameAccess>::get(), 0, 0}, + { SC_UNO_DEFTABSTOP, PROP_UNO_DEFTABSTOP, cppu::UnoType<sal_Int16>::get(), 0, 0}, + { SC_UNO_EXTERNALDOCLINKS, 0, cppu::UnoType<sheet::XExternalDocLinks>::get(), 0, 0}, + { SC_UNO_FORBIDDEN, 0, cppu::UnoType<i18n::XForbiddenCharacters>::get(), beans::PropertyAttribute::READONLY, 0}, + { SC_UNO_HASDRAWPAGES, 0, cppu::UnoType<bool>::get(), beans::PropertyAttribute::READONLY, 0}, + { SC_UNO_IGNORECASE, PROP_UNO_IGNORECASE, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_ITERENABLED, PROP_UNO_ITERENABLED, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_ITERCOUNT, PROP_UNO_ITERCOUNT, cppu::UnoType<sal_Int32>::get(), 0, 0}, + { SC_UNO_ITEREPSILON, PROP_UNO_ITEREPSILON, cppu::UnoType<double>::get(), 0, 0}, + { SC_UNO_LOOKUPLABELS, PROP_UNO_LOOKUPLABELS, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_MATCHWHOLE, PROP_UNO_MATCHWHOLE, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_NAMEDRANGES, 0, cppu::UnoType<sheet::XNamedRanges>::get(), 0, 0}, + { SC_UNO_THEME, 0, cppu::UnoType<util::XTheme>::get(), 0, 0}, + { SC_UNO_DATABASERNG, 0, cppu::UnoType<sheet::XDatabaseRanges>::get(), 0, 0}, + { SC_UNO_NULLDATE, PROP_UNO_NULLDATE, cppu::UnoType<util::Date>::get(), 0, 0}, + { SC_UNO_ROWLABELRNG, 0, cppu::UnoType<sheet::XLabelRanges>::get(), 0, 0}, + { SC_UNO_SHEETLINKS, 0, cppu::UnoType<container::XNameAccess>::get(), 0, 0}, + { SC_UNO_SPELLONLINE, PROP_UNO_SPELLONLINE, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_STANDARDDEC, PROP_UNO_STANDARDDEC, cppu::UnoType<sal_Int16>::get(), 0, 0}, + { SC_UNO_REGEXENABLED, PROP_UNO_REGEXENABLED, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_WILDCARDSENABLED, PROP_UNO_WILDCARDSENABLED, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_RUNTIMEUID, 0, cppu::UnoType<OUString>::get(), beans::PropertyAttribute::READONLY, 0}, + { SC_UNO_HASVALIDSIGNATURES, 0, cppu::UnoType<bool>::get(), beans::PropertyAttribute::READONLY, 0}, + { SC_UNO_ISLOADED, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_ISUNDOENABLED, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_RECORDCHANGES, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_ISRECORDCHANGESPROTECTED,0, cppu::UnoType<bool>::get(), beans::PropertyAttribute::READONLY, 0}, + { SC_UNO_ISADJUSTHEIGHTENABLED, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_ISEXECUTELINKENABLED, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_ISCHANGEREADONLYENABLED, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_REFERENCEDEVICE, 0, cppu::UnoType<awt::XDevice>::get(), beans::PropertyAttribute::READONLY, 0}, + {u"BuildId"_ustr, 0, ::cppu::UnoType<OUString>::get(), 0, 0}, + { SC_UNO_CODENAME, 0, cppu::UnoType<OUString>::get(), 0, 0}, + { SC_UNO_INTEROPGRABBAG, 0, cppu::UnoType<uno::Sequence< beans::PropertyValue >>::get(), 0, 0}, + }; + return aDocOptPropertyMap_Impl; +} + +//! StandardDecimals as property and from NumberFormatter ???????? + +static std::span<const SfxItemPropertyMapEntry> lcl_GetColumnsPropertyMap() +{ + static const SfxItemPropertyMapEntry aColumnsPropertyMap_Impl[] = + { + { SC_UNONAME_MANPAGE, 0, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_NEWPAGE, 0, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_CELLVIS, 0, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_OWIDTH, 0, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_CELLWID, 0, cppu::UnoType<sal_Int32>::get(), 0, 0 }, + }; + return aColumnsPropertyMap_Impl; +} + +static std::span<const SfxItemPropertyMapEntry> lcl_GetRowsPropertyMap() +{ + static const SfxItemPropertyMapEntry aRowsPropertyMap_Impl[] = + { + { SC_UNONAME_CELLHGT, 0, cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { SC_UNONAME_CELLFILT, 0, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_OHEIGHT, 0, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_MANPAGE, 0, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_NEWPAGE, 0, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_CELLVIS, 0, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_CELLBACK, ATTR_BACKGROUND, ::cppu::UnoType<sal_Int32>::get(), 0, MID_BACK_COLOR }, + { SC_UNONAME_CELLTRAN, ATTR_BACKGROUND, cppu::UnoType<bool>::get(), 0, MID_GRAPHIC_TRANSPARENT }, + // not sorted, not used with SfxItemPropertyMapEntry::GetByName + }; + return aRowsPropertyMap_Impl; +} + +constexpr OUString SCMODELOBJ_SERVICE = u"com.sun.star.sheet.SpreadsheetDocument"_ustr; +constexpr OUString SCDOCSETTINGS_SERVICE = u"com.sun.star.sheet.SpreadsheetDocumentSettings"_ustr; +constexpr OUString SCDOC_SERVICE = u"com.sun.star.document.OfficeDocument"_ustr; + +SC_SIMPLE_SERVICE_INFO( ScAnnotationsObj, "ScAnnotationsObj", "com.sun.star.sheet.CellAnnotations" ) +SC_SIMPLE_SERVICE_INFO( ScDrawPagesObj, "ScDrawPagesObj", "com.sun.star.drawing.DrawPages" ) +SC_SIMPLE_SERVICE_INFO( ScScenariosObj, "ScScenariosObj", "com.sun.star.sheet.Scenarios" ) +SC_SIMPLE_SERVICE_INFO( ScSpreadsheetSettingsObj, "ScSpreadsheetSettingsObj", "com.sun.star.sheet.SpreadsheetDocumentSettings" ) +SC_SIMPLE_SERVICE_INFO( ScTableColumnsObj, "ScTableColumnsObj", "com.sun.star.table.TableColumns" ) +SC_SIMPLE_SERVICE_INFO( ScTableRowsObj, "ScTableRowsObj", "com.sun.star.table.TableRows" ) +SC_SIMPLE_SERVICE_INFO( ScTableSheetsObj, "ScTableSheetsObj", "com.sun.star.sheet.Spreadsheets" ) + +class ScPrintUIOptions : public vcl::PrinterOptionsHelper +{ +public: + ScPrintUIOptions(); + void SetDefaults(); +}; + +ScPrintUIOptions::ScPrintUIOptions() +{ + const ScPrintOptions& rPrintOpt = SC_MOD()->GetPrintOptions(); + sal_Int32 nContent = rPrintOpt.GetAllSheets() ? 0 : 1; + bool bSuppress = rPrintOpt.GetSkipEmpty(); + + sal_Int32 nNumProps= 10, nIdx = 0; + + m_aUIProperties.resize(nNumProps); + + // load the writer PrinterOptions into the custom tab + m_aUIProperties[nIdx].Name = "OptionsUIFile"; + m_aUIProperties[nIdx++].Value <<= OUString("modules/scalc/ui/printeroptions.ui"); + + // create Section for spreadsheet (results in an extra tab page in dialog) + SvtModuleOptions aOpt; + OUString aAppGroupname( ScResId( SCSTR_PRINTOPT_PRODNAME ) ); + aAppGroupname = aAppGroupname.replaceFirst( "%s", aOpt.GetModuleName( SvtModuleOptions::EModule::CALC ) ); + m_aUIProperties[nIdx++].Value = setGroupControlOpt("tabcontrol-page2", aAppGroupname, OUString()); + + // show subgroup for pages + m_aUIProperties[nIdx++].Value = setSubgroupControlOpt("pages", ScResId( SCSTR_PRINTOPT_PAGES ), OUString()); + + // create a bool option for empty pages + m_aUIProperties[nIdx++].Value = setBoolControlOpt("suppressemptypages", ScResId( SCSTR_PRINTOPT_SUPPRESSEMPTY ), + ".HelpID:vcl:PrintDialog:IsSuppressEmptyPages:CheckBox", + "IsSuppressEmptyPages", + bSuppress); + // show Subgroup for print content + vcl::PrinterOptionsHelper::UIControlOptions aPrintRangeOpt; + aPrintRangeOpt.maGroupHint = "PrintRange"; + m_aUIProperties[nIdx++].Value = setSubgroupControlOpt("printrange", ScResId( SCSTR_PRINTOPT_PAGES ), + OUString(), + aPrintRangeOpt); + + // create a choice for the content to create + uno::Sequence< OUString > aChoices{ + ScResId( SCSTR_PRINTOPT_ALLSHEETS ), + ScResId( SCSTR_PRINTOPT_SELECTEDSHEETS ), + ScResId( SCSTR_PRINTOPT_SELECTEDCELLS )}; + uno::Sequence< OUString > aHelpIds{ + ".HelpID:vcl:PrintDialog:PrintContent:ListBox"}; + m_aUIProperties[nIdx++].Value = setChoiceListControlOpt( "printextrabox", OUString(), + aHelpIds, "PrintContent", + aChoices, nContent ); + + // show Subgroup for print range + aPrintRangeOpt.mbInternalOnly = true; + m_aUIProperties[nIdx++].Value = setSubgroupControlOpt("fromwhich", ScResId( SCSTR_PRINTOPT_FROMWHICH ), + OUString(), + aPrintRangeOpt); + + // create a choice for the range to print + OUString aPrintRangeName( "PrintRange" ); + aChoices = { ScResId( SCSTR_PRINTOPT_PRINTALLPAGES ), ScResId( SCSTR_PRINTOPT_PRINTPAGES ) }; + aHelpIds = { ".HelpID:vcl:PrintDialog:PrintRange:RadioButton:0", + ".HelpID:vcl:PrintDialog:PrintRange:RadioButton:1" }; + uno::Sequence< OUString > aWidgetIds{ "rbAllPages", "rbRangePages" }; + m_aUIProperties[nIdx++].Value = setChoiceRadiosControlOpt(aWidgetIds, OUString(), + aHelpIds, + aPrintRangeName, + aChoices, + 0 ); + + // create an Edit dependent on "Pages" selected + vcl::PrinterOptionsHelper::UIControlOptions aPageRangeOpt( aPrintRangeName, 1, true ); + m_aUIProperties[nIdx++].Value = setEditControlOpt("pagerange", OUString(), + ".HelpID:vcl:PrintDialog:PageRange:Edit", + "PageRange", OUString(), aPageRangeOpt); + + vcl::PrinterOptionsHelper::UIControlOptions aEvenOddOpt(aPrintRangeName, 0, true); + m_aUIProperties[ nIdx++ ].Value = setChoiceListControlOpt("evenoddbox", + OUString(), + uno::Sequence<OUString>(), + "EvenOdd", + uno::Sequence<OUString>(), + 0, + uno::Sequence< sal_Bool >(), + aEvenOddOpt); + + assert(nIdx == nNumProps); +} + +void ScPrintUIOptions::SetDefaults() +{ + // re-initialize the default values from print options + + const ScPrintOptions& rPrintOpt = SC_MOD()->GetPrintOptions(); + sal_Int32 nContent = rPrintOpt.GetAllSheets() ? 0 : 1; + bool bSuppress = rPrintOpt.GetSkipEmpty(); + + for (beans::PropertyValue & rPropValue : m_aUIProperties) + { + uno::Sequence<beans::PropertyValue> aUIProp; + if ( rPropValue.Value >>= aUIProp ) + { + for (auto& rProp : asNonConstRange(aUIProp)) + { + OUString aName = rProp.Name; + if ( aName == "Property" ) + { + beans::PropertyValue aPropertyValue; + if ( rProp.Value >>= aPropertyValue ) + { + if ( aPropertyValue.Name == "PrintContent" ) + { + aPropertyValue.Value <<= nContent; + rProp.Value <<= aPropertyValue; + } + else if ( aPropertyValue.Name == "IsSuppressEmptyPages" ) + { + aPropertyValue.Value <<= bSuppress; + rProp.Value <<= aPropertyValue; + } + } + } + } + rPropValue.Value <<= aUIProp; + } + } +} + +void ScModelObj::CreateAndSet(ScDocShell* pDocSh) +{ + if (pDocSh) + pDocSh->SetBaseModel( new ScModelObj(pDocSh) ); +} + +SdrModel& ScModelObj::getSdrModelFromUnoModel() const +{ + ScDocument& rDoc(pDocShell->GetDocument()); + + if(!rDoc.GetDrawLayer()) + { + rDoc.InitDrawLayer(); + } + + return *rDoc.GetDrawLayer(); // TTTT should be reference +} + +ScModelObj::ScModelObj( ScDocShell* pDocSh ) : + SfxBaseModel( pDocSh ), + aPropSet( lcl_GetDocOptPropertyMap() ), + pDocShell( pDocSh ), + maChangesListeners( m_aMutex ) +{ + // pDocShell may be NULL if this is the base of a ScDocOptionsObj + if ( pDocShell ) + { + pDocShell->GetDocument().AddUnoObject(*this); // SfxModel is derived from SfxListener + } +} + +ScModelObj::~ScModelObj() +{ + SolarMutexGuard g; + + if (pDocShell) + pDocShell->GetDocument().RemoveUnoObject(*this); + + if (xNumberAgg.is()) + xNumberAgg->setDelegator(uno::Reference<uno::XInterface>()); + + pPrintFuncCache.reset(); + pPrinterOptions.reset(); +} + +uno::Reference< uno::XAggregation> const & ScModelObj::GetFormatter() +{ + // pDocShell may be NULL if this is the base of a ScDocOptionsObj + if ( !xNumberAgg.is() && pDocShell ) + { + // setDelegator changes RefCount, so we'd better hold the reference ourselves + // (directly in m_refCount, so we don't delete ourselves with release()) + osl_atomic_increment( &m_refCount ); + // we need a reference to SvNumberFormatsSupplierObj during queryInterface, + // otherwise it'll be deleted + uno::Reference<util::XNumberFormatsSupplier> xFormatter( + new SvNumberFormatsSupplierObj(pDocShell->GetDocument().GetThreadedContext().GetFormatTable() )); + { + xNumberAgg.set(uno::Reference<uno::XAggregation>( xFormatter, uno::UNO_QUERY )); + // extra block to force deletion of the temporary before setDelegator + } + + // during setDelegator no additional reference should exist + xFormatter = nullptr; + + if (xNumberAgg.is()) + xNumberAgg->setDelegator( getXWeak() ); + osl_atomic_decrement( &m_refCount ); + } // if ( !xNumberAgg.is() ) + return xNumberAgg; +} + +ScDocument* ScModelObj::GetDocument() const +{ + if (pDocShell) + return &pDocShell->GetDocument(); + return nullptr; +} + +SfxObjectShell* ScModelObj::GetEmbeddedObject() const +{ + return pDocShell; +} + +void ScModelObj::UpdateAllRowHeights() +{ + if (pDocShell) + pDocShell->UpdateAllRowHeights(); +} + +void ScModelObj::BeforeXMLLoading() +{ + if (pDocShell) + pDocShell->BeforeXMLLoading(); +} + +void ScModelObj::AfterXMLLoading() +{ + if (pDocShell) + pDocShell->AfterXMLLoading(true); +} + +ScSheetSaveData* ScModelObj::GetSheetSaveData() +{ + if (pDocShell) + return pDocShell->GetSheetSaveData(); + return nullptr; +} + +ScFormatSaveData* ScModelObj::GetFormatSaveData() +{ + if (pDocShell) + return pDocShell->GetFormatSaveData(); + return nullptr; +} + +void ScModelObj::RepaintRange( const ScRange& rRange ) +{ + if (pDocShell) + pDocShell->PostPaint( rRange, PaintPartFlags::Grid ); +} + +void ScModelObj::RepaintRange( const ScRangeList& rRange ) +{ + if (pDocShell) + pDocShell->PostPaint(rRange, PaintPartFlags::Grid, SC_PF_TESTMERGE); +} + +static ScViewData* lcl_getViewMatchingDocZoomTab(const Fraction& rZoomX, + const Fraction& rZoomY, + const SCTAB nTab, + const ViewShellDocId& rDocId) +{ + constexpr size_t nMaxIter = 5; + size_t nIter = 0; + for (SfxViewShell* pViewShell = SfxViewShell::GetFirst(); + pViewShell && nIter < nMaxIter; + (pViewShell = SfxViewShell::GetNext(*pViewShell)), ++nIter) + { + if (pViewShell->GetDocId() != rDocId) + continue; + + ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(pViewShell); + if (!pTabViewShell) + continue; + + ScViewData& rData = pTabViewShell->GetViewData(); + if (rData.GetTabNo() == nTab && rData.GetZoomX() == rZoomX && rData.GetZoomY() == rZoomY) + return &rData; + } + + return nullptr; +} + +void ScModelObj::paintTile( VirtualDevice& rDevice, + int nOutputWidth, int nOutputHeight, + int nTilePosX, int nTilePosY, + tools::Long nTileWidth, tools::Long nTileHeight ) +{ + ScTabViewShell* pViewShell = pDocShell->GetBestViewShell(false); + + // FIXME: Can this happen? What should we do? + if (!pViewShell) + return; + + ScViewData* pActiveViewData = &pViewShell->GetViewData(); + Fraction aFracX(o3tl::toTwips(nOutputWidth, o3tl::Length::px), nTileWidth); + Fraction aFracY(o3tl::toTwips(nOutputHeight, o3tl::Length::px), nTileHeight); + + // Try to find a view that matches the tile-zoom requested by iterating over + // first few shells. This is to avoid switching of zooms in ScGridWindow::PaintTile + // and hence avoid grid-offset recomputation on all shapes which is not cheap. + ScViewData* pViewData = lcl_getViewMatchingDocZoomTab(aFracX, aFracY, + pActiveViewData->GetTabNo(), pViewShell->GetDocId()); + if (!pViewData) + pViewData = pActiveViewData; + + ScGridWindow* pGridWindow = pViewData->GetActiveWin(); + + // update the size of the area we are painting + // FIXME we want to use only the minimal necessary size, like the + // following; but for the moment there is too many problems with that and + // interaction with editeng used for the cell editing + //Size aTileSize(nOutputWidth, nOutputHeight); + //if (pGridWindow->GetOutputSizePixel() != aTileSize) + // pGridWindow->SetOutputSizePixel(Size(nOutputWidth, nOutputHeight)); + // so instead for now, set the viewport size to document size + + // Fetch the document size and the tiled rendering area together, + // because the tiled rendering area is not cheap to compute, and we want + // to pass it down to ScGridWindow::PaintFile to avoid computing twice. + SCCOL nTiledRenderingAreaEndCol = 0; + SCROW nTiledRenderingAreaEndRow = 0; + Size aDocSize = getDocumentSize(nTiledRenderingAreaEndCol, nTiledRenderingAreaEndRow); + + pGridWindow->SetOutputSizePixel(Size(aDocSize.Width() * pViewData->GetPPTX(), aDocSize.Height() * pViewData->GetPPTY())); + + pGridWindow->PaintTile( rDevice, nOutputWidth, nOutputHeight, + nTilePosX, nTilePosY, nTileWidth, nTileHeight, + nTiledRenderingAreaEndCol, nTiledRenderingAreaEndRow ); + + // Draw Form controls + ScDrawLayer* pDrawLayer = pDocShell->GetDocument().GetDrawLayer(); + SdrPage* pPage = pDrawLayer->GetPage(sal_uInt16(pViewData->GetTabNo())); + SdrView* pDrawView = pViewData->GetViewShell()->GetScDrawView(); + tools::Rectangle aTileRect(Point(nTilePosX, nTilePosY), Size(nTileWidth, nTileHeight)); + Size aOutputSize(nOutputWidth, nOutputHeight); + LokControlHandler::paintControlTile(pPage, pDrawView, *pGridWindow, rDevice, aOutputSize, aTileRect); +} + +void ScModelObj::setPart( int nPart, bool /*bAllowChangeFocus*/ ) +{ + ScViewData* pViewData = ScDocShell::GetViewData(); + if (!pViewData) + return; + + ScTabView* pTabView = pViewData->GetView(); + if (!pTabView) + return; + + if (SdrView* pDrawView = pViewData->GetViewShell()->GetScDrawView()) + pDrawView->SetNegativeX(comphelper::LibreOfficeKit::isActive() && + pViewData->GetDocument().IsLayoutRTL(nPart)); + + pTabView->SelectTabPage(nPart + 1); +} + +int ScModelObj::getParts() +{ + ScDocument& rDoc = pDocShell->GetDocument(); + return rDoc.GetTableCount(); +} + +int ScModelObj::getPart() +{ + ScViewData* pViewData = ScDocShell::GetViewData(); + return pViewData ? pViewData->GetViewShell()->getPart() : 0; +} + +OUString ScModelObj::getPartInfo( int nPart ) +{ + ScViewData* pViewData = ScDocShell::GetViewData(); + if (!pViewData) + return OUString(); + + const bool bIsVisible = pViewData->GetDocument().IsVisible(nPart); + //FIXME: Implement IsSelected(). + const bool bIsSelected = false; //pViewData->GetDocument()->IsSelected(nPart); + const bool bIsRTLLayout = pViewData->GetDocument().IsLayoutRTL(nPart); + + OUString aPartInfo = "{ \"visible\": \"" + + OUString::number(static_cast<unsigned int>(bIsVisible)) + + "\", \"selected\": \"" + + OUString::number(static_cast<unsigned int>(bIsSelected)) + + "\", \"rtllayout\": \"" + + OUString::number(static_cast<unsigned int>(bIsRTLLayout)) + + "\" }"; + return aPartInfo; +} + +OUString ScModelObj::getPartName( int nPart ) +{ + ScViewData* pViewData = ScDocShell::GetViewData(); + if (!pViewData) + return OUString(); + + OUString sTabName; + pViewData->GetDocument().GetName(nPart, sTabName); + return sTabName; +} + +OUString ScModelObj::getPartHash( int nPart ) +{ + ScViewData* pViewData = ScDocShell::GetViewData(); + if (!pViewData) + return OUString(); + + sal_Int64 nHashCode; + return (pViewData->GetDocument().GetHashCode(nPart, nHashCode) ? OUString::number(nHashCode) : OUString()); +} + +VclPtr<vcl::Window> ScModelObj::getDocWindow() +{ + SolarMutexGuard aGuard; + + ScTabViewShell* pViewShell = pDocShell->GetBestViewShell(false); + + // FIXME: Can this happen? What should we do? + if (!pViewShell) + return VclPtr<vcl::Window>(); + + if (VclPtr<vcl::Window> pWindow = SfxLokHelper::getInPlaceDocWindow(pViewShell)) + return pWindow; + + return pViewShell->GetViewData().GetActiveWin(); +} + +Size ScModelObj::getDocumentSize() +{ + SCCOL nTiledRenderingAreaEndCol = 0; + SCROW nTiledRenderingAreaEndRow = 0; + return getDocumentSize(nTiledRenderingAreaEndCol, nTiledRenderingAreaEndRow); +} + +Size ScModelObj::getDocumentSize(SCCOL& rnTiledRenderingAreaEndCol, SCROW& rnTiledRenderingAreaEndRow) +{ + Size aSize(10, 10); // minimum size + + ScViewData* pViewData = ScDocShell::GetViewData(); + if (!pViewData) + return aSize; + + SCTAB nTab = pViewData->GetTabNo(); + rnTiledRenderingAreaEndCol = 0; + rnTiledRenderingAreaEndRow = 0; + const ScDocument& rDoc = pDocShell->GetDocument(); + + rDoc.GetTiledRenderingArea(nTab, rnTiledRenderingAreaEndCol, rnTiledRenderingAreaEndRow); + + const ScDocument* pThisDoc = &rDoc; + const double fPPTX = pViewData->GetPPTX(); + const double fPPTY = pViewData->GetPPTY(); + + auto GetColWidthPx = [pThisDoc, fPPTX, nTab](SCCOL nCol) { + const sal_uInt16 nSize = pThisDoc->GetColWidth(nCol, nTab); + return ScViewData::ToPixel(nSize, fPPTX); + }; + + tools::Long nDocWidthPixel = pViewData->GetLOKWidthHelper().computePosition(rnTiledRenderingAreaEndCol, GetColWidthPx); + tools::Long nDocHeightPixel = pThisDoc->GetScaledRowHeight(0, rnTiledRenderingAreaEndRow, nTab, fPPTY); + + if (nDocWidthPixel > 0 && nDocHeightPixel > 0) + { + // convert to twips + aSize.setWidth(nDocWidthPixel / fPPTX); + aSize.setHeight(nDocHeightPixel / fPPTY); + } + else + { + // convert to twips + aSize.setWidth(rDoc.GetColWidth(0, rnTiledRenderingAreaEndCol, nTab)); + aSize.setHeight(rDoc.GetRowHeight(0, rnTiledRenderingAreaEndRow, nTab)); + } + + return aSize; +} + +Size ScModelObj::getDataArea(long nPart) +{ + Size aSize(1, 1); + + ScViewData* pViewData = ScDocShell::GetViewData(); + if (!pViewData || !pDocShell) + return aSize; + + SCTAB nTab = nPart; + SCCOL nEndCol = 0; + SCROW nEndRow = 0; + ScDocument& rDoc = pDocShell->GetDocument(); + + ScTable* pTab = rDoc.FetchTable(nTab); + if (!pTab) + return aSize; + + pTab->GetCellArea(nEndCol, nEndRow); + aSize = Size(nEndCol, nEndRow); + + return aSize; +} + +void ScModelObj::postKeyEvent(int nType, int nCharCode, int nKeyCode) +{ + SolarMutexGuard aGuard; + SfxLokHelper::postKeyEventAsync(getDocWindow(), nType, nCharCode, nKeyCode); +} + +void ScModelObj::postMouseEvent(int nType, int nX, int nY, int nCount, int nButtons, int nModifier) +{ + SolarMutexGuard aGuard; + + ScTabViewShell* pViewShell = pDocShell->GetBestViewShell(false); + + // FIXME: Can this happen? What should we do? + if (!pViewShell) + return; + + ScViewData* pViewData = &pViewShell->GetViewData(); + + ScGridWindow* pGridWindow = pViewData->GetActiveWin(); + + if (!pGridWindow) + return; + + SCTAB nTab = pViewData->GetTabNo(); + const ScDocument& rDoc = pDocShell->GetDocument(); + bool bDrawNegativeX = rDoc.IsNegativePage(nTab); + if (SfxLokHelper::testInPlaceComponentMouseEventHit(pViewShell, nType, nX, nY, nCount, + nButtons, nModifier, pViewData->GetPPTX(), + pViewData->GetPPTY(), bDrawNegativeX)) + return; + + Point aPointTwip(nX, nY); + + // Check if a control is hit + Point aPointHMM = o3tl::convert(aPointTwip, o3tl::Length::twip, o3tl::Length::mm100); + Point aPointHMMDraw(bDrawNegativeX ? -aPointHMM.X() : aPointHMM.X(), aPointHMM.Y()); + ScDrawLayer* pDrawLayer = pDocShell->GetDocument().GetDrawLayer(); + SdrPage* pPage = pDrawLayer->GetPage(sal_uInt16(nTab)); + SdrView* pDrawView = pViewData->GetViewShell()->GetScDrawView(); + if (LokControlHandler::postMouseEvent(pPage, pDrawView, *pGridWindow, nType, aPointHMMDraw, nCount, nButtons, nModifier)) + return; + + if (!pGridWindow->HasChildPathFocus(true)) + pGridWindow->GrabFocus(); + + // Calc operates in pixels... + const Point aPosition(nX * pViewData->GetPPTX() + pGridWindow->GetOutOffXPixel(), + nY * pViewData->GetPPTY() + pGridWindow->GetOutOffYPixel()); + + VclEventId aEvent = VclEventId::NONE; + MouseEvent aData(aPosition, nCount, MouseEventModifiers::SIMPLECLICK, nButtons, nModifier); + aData.setLogicPosition(aPointHMM); + switch (nType) + { + case LOK_MOUSEEVENT_MOUSEBUTTONDOWN: + aEvent = VclEventId::WindowMouseButtonDown; + break; + case LOK_MOUSEEVENT_MOUSEBUTTONUP: + aEvent = VclEventId::WindowMouseButtonUp; + break; + case LOK_MOUSEEVENT_MOUSEMOVE: + aEvent = VclEventId::WindowMouseMove; + break; + default: + break; + } + + Application::LOKHandleMouseEvent(aEvent, pGridWindow, &aData); +} + +void ScModelObj::setTextSelection(int nType, int nX, int nY) +{ + SolarMutexGuard aGuard; + + ScViewData* pViewData = ScDocShell::GetViewData(); + if (!pViewData) + return; + + ScTabViewShell* pViewShell = pViewData->GetViewShell(); + + LokChartHelper aChartHelper(pViewShell); + if (aChartHelper.setTextSelection(nType, nX, nY)) + return; + + ScInputHandler* pInputHandler = SC_MOD()->GetInputHdl(pViewShell); + ScDrawView* pDrawView = pViewData->GetScDrawView(); + + bool bHandled = false; + + if (pInputHandler && pInputHandler->IsInputMode()) + { + // forwarding to editeng - we are editing the cell content + EditView* pTableView = pInputHandler->GetTableView(); + assert(pTableView); + + Point aPoint(convertTwipToMm100(nX), convertTwipToMm100(nY)); + + if (pTableView->GetOutputArea().Contains(aPoint)) + { + switch (nType) + { + case LOK_SETTEXTSELECTION_START: + pTableView->SetCursorLogicPosition(aPoint, /*bPoint=*/false, /*bClearMark=*/false); + break; + case LOK_SETTEXTSELECTION_END: + pTableView->SetCursorLogicPosition(aPoint, /*bPoint=*/true, /*bClearMark=*/false); + break; + case LOK_SETTEXTSELECTION_RESET: + pTableView->SetCursorLogicPosition(aPoint, /*bPoint=*/true, /*bClearMark=*/true); + break; + default: + assert(false); + break; + } + bHandled = true; + } + } + else if (pDrawView && pDrawView->IsTextEdit()) + { + // forwarding to editeng - we are editing the text in shape + OutlinerView* pOutlinerView = pDrawView->GetTextEditOutlinerView(); + EditView& rEditView = pOutlinerView->GetEditView(); + + Point aPoint(convertTwipToMm100(nX), convertTwipToMm100(nY)); + switch (nType) + { + case LOK_SETTEXTSELECTION_START: + rEditView.SetCursorLogicPosition(aPoint, /*bPoint=*/false, /*bClearMark=*/false); + break; + case LOK_SETTEXTSELECTION_END: + rEditView.SetCursorLogicPosition(aPoint, /*bPoint=*/true, /*bClearMark=*/false); + break; + case LOK_SETTEXTSELECTION_RESET: + rEditView.SetCursorLogicPosition(aPoint, /*bPoint=*/true, /*bClearMark=*/true); + break; + default: + assert(false); + break; + } + bHandled = true; + } + + if (!bHandled) + { + // just update the cell selection + ScGridWindow* pGridWindow = pViewData->GetActiveWin(); + if (!pGridWindow) + return; + + // move the cell selection handles + pGridWindow->SetCellSelectionPixel(nType, nX * pViewData->GetPPTX(), nY * pViewData->GetPPTY()); + } +} + +uno::Reference<datatransfer::XTransferable> ScModelObj::getSelection() +{ + SolarMutexGuard aGuard; + + TransferableDataHelper aDataHelper; + uno::Reference<datatransfer::XTransferable> xTransferable; + + if (ScViewData* pViewData = ScDocShell::GetViewData()) + { + if ( ScEditShell * pShell = dynamic_cast<ScEditShell*>( pViewData->GetViewShell()->GetViewFrame().GetDispatcher()->GetShell(0) ) ) + xTransferable = pShell->GetEditView()->GetTransferable(); + else if ( nullptr != dynamic_cast<ScDrawTextObjectBar*>( pViewData->GetViewShell()->GetViewFrame().GetDispatcher()->GetShell(0) )) + { + ScDrawView* pView = pViewData->GetScDrawView(); + OutlinerView* pOutView = pView->GetTextEditOutlinerView(); + if (pOutView) + xTransferable = pOutView->GetEditView().GetTransferable(); + } + else if ( ScDrawShell * pDrawShell = dynamic_cast<ScDrawShell*>( pViewData->GetViewShell()->GetViewFrame().GetDispatcher()->GetShell(0) ) ) + xTransferable = pDrawShell->GetDrawView()->CopyToTransferable(); + else + xTransferable = pViewData->GetViewShell()->CopyToTransferable(); + } + + if (!xTransferable.is()) + xTransferable.set( aDataHelper.GetTransferable() ); + + return xTransferable; +} + +void ScModelObj::setGraphicSelection(int nType, int nX, int nY) +{ + SolarMutexGuard aGuard; + + ScTabViewShell* pViewShell = pDocShell->GetBestViewShell(false); + + // FIXME: Can this happen? What should we do? + if (!pViewShell) + return; + + ScViewData* pViewData = &pViewShell->GetViewData(); + + ScGridWindow* pGridWindow = pViewData->GetActiveWin(); + + double fPPTX = pViewData->GetPPTX(); + double fPPTY = pViewData->GetPPTY(); + + pViewShell = pViewData->GetViewShell(); + LokChartHelper aChartHelper(pViewShell); + if (aChartHelper.setGraphicSelection(nType, nX, nY, fPPTX, fPPTY)) + return; + + int nPixelX = nX * fPPTX; + int nPixelY = nY * fPPTY; + + switch (nType) + { + case LOK_SETGRAPHICSELECTION_START: + { + MouseEvent aClickEvent(Point(nPixelX, nPixelY), 1, MouseEventModifiers::SIMPLECLICK, MOUSE_LEFT); + pGridWindow->MouseButtonDown(aClickEvent); + MouseEvent aMoveEvent(Point(nPixelX, nPixelY), 0, MouseEventModifiers::SIMPLEMOVE, MOUSE_LEFT); + pGridWindow->MouseMove(aMoveEvent); + } + break; + case LOK_SETGRAPHICSELECTION_END: + { + MouseEvent aMoveEvent(Point(nPixelX, nPixelY), 0, MouseEventModifiers::SIMPLEMOVE, MOUSE_LEFT); + pGridWindow->MouseMove(aMoveEvent); + MouseEvent aClickEvent(Point(nPixelX, nPixelY), 1, MouseEventModifiers::SIMPLECLICK, MOUSE_LEFT); + pGridWindow->MouseButtonUp(aClickEvent); + } + break; + default: + assert(false); + break; + } +} + +void ScModelObj::resetSelection() +{ + SolarMutexGuard aGuard; + + ScViewData* pViewData = ScDocShell::GetViewData(); + if (!pViewData) + return; + + ScTabViewShell* pViewShell = pViewData->GetViewShell(); + + // deselect the shapes & texts + ScDrawView* pDrawView = pViewShell->GetScDrawView(); + if (pDrawView) + { + pDrawView->ScEndTextEdit(); + pDrawView->UnmarkAll(); + } + else + pViewShell->Unmark(); + + // and hide the cell and text selection + pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, ""_ostr); + SfxLokHelper::notifyOtherViews(pViewShell, LOK_CALLBACK_TEXT_VIEW_SELECTION, "selection", ""_ostr); +} + +void ScModelObj::setClipboard(const uno::Reference<datatransfer::clipboard::XClipboard>& xClipboard) +{ + SolarMutexGuard aGuard; + + ScViewData* pViewData = ScDocShell::GetViewData(); + if (!pViewData) + return; + + pViewData->GetActiveWin()->SetClipboard(xClipboard); +} + +bool ScModelObj::isMimeTypeSupported() +{ + SolarMutexGuard aGuard; + + ScViewData* pViewData = ScDocShell::GetViewData(); + if (!pViewData) + return false; + + + TransferableDataHelper aDataHelper(TransferableDataHelper::CreateFromSystemClipboard(pViewData->GetActiveWin())); + return EditEngine::HasValidData(aDataHelper.GetTransferable()); +} + +static void lcl_sendLOKDocumentBackground(const ScViewData* pViewData) +{ + ScDocShell* pDocSh = pViewData->GetDocShell(); + ScDocument& rDoc = pDocSh->GetDocument(); + const ScPatternAttr *pAttr = rDoc.GetDefPattern(); + const SfxPoolItem& rItem = pAttr->GetItem(ATTR_BACKGROUND); + const SvxBrushItem& rBackground = static_cast<const SvxBrushItem&>(rItem); + const Color& rColor = rBackground.GetColor(); + + ScTabViewShell* pViewShell = pViewData->GetViewShell(); + pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_DOCUMENT_BACKGROUND_COLOR, rColor.AsRGBHexString().toUtf8()); +} + +void ScModelObj::setClientZoom(int nTilePixelWidth_, int nTilePixelHeight_, int nTileTwipWidth_, int nTileTwipHeight_) +{ + ScViewData* pViewData = ScDocShell::GetViewData(); + if (!pViewData) + return; + + // Currently in LOK clients the doc background cannot be changed, so send this sparingly as possible but for every view. + // FIXME: Find a better place to trigger this callback where it would be called just once per view creation. + // Doing this in ScTabViewShell init code does not work because callbacks do not work at that point for the first view. + lcl_sendLOKDocumentBackground(pViewData); + + const Fraction newZoomX(o3tl::toTwips(nTilePixelWidth_, o3tl::Length::px), nTileTwipWidth_); + const Fraction newZoomY(o3tl::toTwips(nTilePixelHeight_, o3tl::Length::px), nTileTwipHeight_); + + double fDeltaPPTX = std::abs(ScGlobal::nScreenPPTX * static_cast<double>(newZoomX) - pViewData->GetPPTX()); + double fDeltaPPTY = std::abs(ScGlobal::nScreenPPTY * static_cast<double>(newZoomY) - pViewData->GetPPTY()); + constexpr double fEps = 1E-08; + + if (pViewData->GetZoomX() == newZoomX && pViewData->GetZoomY() == newZoomY && fDeltaPPTX < fEps && fDeltaPPTY < fEps) + return; + + pViewData->SetZoom(newZoomX, newZoomY, true); + + // refresh our view's take on other view's cursors & selections + pViewData->GetActiveWin()->updateKitOtherCursors(); + pViewData->GetActiveWin()->updateOtherKitSelections(); + + if (ScDrawView* pDrawView = pViewData->GetScDrawView()) + pDrawView->resetGridOffsetsForAllSdrPageViews(); +} + +void ScModelObj::getRowColumnHeaders(const tools::Rectangle& rRectangle, tools::JsonWriter& rJsonWriter) +{ + ScViewData* pViewData = ScDocShell::GetViewData(); + if (!pViewData) + return; + + ScTabView* pTabView = pViewData->GetView(); + if (!pTabView) + return; + + pTabView->getRowColumnHeaders(rRectangle, rJsonWriter); +} + +OString ScModelObj::getSheetGeometryData(bool bColumns, bool bRows, bool bSizes, bool bHidden, + bool bFiltered, bool bGroups) +{ + ScViewData* pViewData = ScDocShell::GetViewData(); + if (!pViewData) + return ""_ostr; + + ScTabView* pTabView = pViewData->GetView(); + if (!pTabView) + return ""_ostr; + + return pTabView->getSheetGeometryData(bColumns, bRows, bSizes, bHidden, bFiltered, bGroups); +} + +void ScModelObj::getCellCursor(tools::JsonWriter& rJsonWriter) +{ + SolarMutexGuard aGuard; + + ScViewData* pViewData = ScDocShell::GetViewData(); + if (!pViewData) + return; + + ScGridWindow* pGridWindow = pViewData->GetActiveWin(); + if (!pGridWindow) + return; + + rJsonWriter.put("commandName", ".uno:CellCursor"); + rJsonWriter.put("commandValues", pGridWindow->getCellCursor()); +} + +PointerStyle ScModelObj::getPointer() +{ + SolarMutexGuard aGuard; + + ScViewData* pViewData = ScDocShell::GetViewData(); + if (!pViewData) + return PointerStyle::Arrow; + + ScGridWindow* pGridWindow = pViewData->GetActiveWin(); + if (!pGridWindow) + return PointerStyle::Arrow; + + return pGridWindow->GetPointer(); +} + +void ScModelObj::getTrackedChanges(tools::JsonWriter& rJson) +{ + if (pDocShell) + { + if (ScChangeTrack* pChangeTrack = pDocShell->GetDocument().GetChangeTrack()) + pChangeTrack->GetChangeTrackInfo(rJson); + } +} + +void ScModelObj::setClientVisibleArea(const tools::Rectangle& rRectangle) +{ + ScViewData* pViewData = ScDocShell::GetViewData(); + if (!pViewData) + return; + + // set the PgUp/PgDown offset + pViewData->ForcePageUpDownOffset(rRectangle.GetHeight()); + + // Store the visible area so that we can use at places like shape insertion + pViewData->setLOKVisibleArea(rRectangle); + + if (comphelper::LibreOfficeKit::isCompatFlagSet( + comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs)) + { + ScTabView* pTabView = pViewData->GetView(); + if (pTabView) + pTabView->extendTiledAreaIfNeeded(); + } +} + +void ScModelObj::setOutlineState(bool bColumn, int nLevel, int nIndex, bool bHidden) +{ + ScViewData* pViewData = ScDocShell::GetViewData(); + if (!pViewData) + return; + + ScDBFunc* pFunc = pViewData->GetView(); + + if (pFunc) + pFunc->SetOutlineState(bColumn, nLevel, nIndex, bHidden); +} + +void ScModelObj::getPostIts(tools::JsonWriter& rJsonWriter) +{ + if (!pDocShell) + return; + + ScDocument& rDoc = pDocShell->GetDocument(); + std::vector<sc::NoteEntry> aNotes; + rDoc.GetAllNoteEntries(aNotes); + + auto commentsNode = rJsonWriter.startArray("comments"); + for (const sc::NoteEntry& aNote : aNotes) + { + auto commentNode = rJsonWriter.startStruct(); + + rJsonWriter.put("id", aNote.mpNote->GetId()); + rJsonWriter.put("tab", aNote.maPos.Tab()); + rJsonWriter.put("author", aNote.mpNote->GetAuthor()); + rJsonWriter.put("dateTime", aNote.mpNote->GetDate()); + rJsonWriter.put("text", aNote.mpNote->GetText()); + + // Calculating the cell cursor position + if (ScViewData* pViewData = ScDocShell::GetViewData()) + { + ScGridWindow* pGridWindow = pViewData->GetActiveWin(); + if (pGridWindow) + { + rJsonWriter.put("cellRange", ScPostIt::NoteRangeToJsonString(rDoc, aNote.maPos)); + } + } + } +} + +OString ScPostIt::NoteRangeToJsonString(const ScDocument& rDoc, const ScAddress& rPos) +{ + SCCOL nX(rPos.Col()); + SCROW nY(rPos.Row()); + OString aStartCellAddress(OString::number(nX) + " " + OString::number(nY)); + const ScPatternAttr* pMarkPattern = rDoc.GetPattern(nX, nY, rPos.Tab()); + if (pMarkPattern && pMarkPattern->GetItemSet().GetItemState(ATTR_MERGE, false) == SfxItemState::SET) + { + SCCOL nCol = pMarkPattern->GetItem(ATTR_MERGE).GetColMerge(); + if (nCol > 1) + nX += nCol - 1; + SCROW nRow = pMarkPattern->GetItem(ATTR_MERGE).GetRowMerge(); + if (nRow > 1) + nY += nRow - 1; + } + OString aEndCellAddress(OString::number(nX) + " " + OString::number(nY)); + return aStartCellAddress + " " + aEndCellAddress; +} + +void ScModelObj::getPostItsPos(tools::JsonWriter& rJsonWriter) +{ + if (!pDocShell) + return; + + ScDocument& rDoc = pDocShell->GetDocument(); + std::vector<sc::NoteEntry> aNotes; + rDoc.GetAllNoteEntries(aNotes); + + auto commentsNode = rJsonWriter.startArray("commentsPos"); + for (const sc::NoteEntry& aNote : aNotes) + { + auto commentNode = rJsonWriter.startStruct(); + + rJsonWriter.put("id", aNote.mpNote->GetId()); + rJsonWriter.put("tab", aNote.maPos.Tab()); + + // Calculating the cell cursor position + if (ScViewData* pViewData = ScDocShell::GetViewData()) + { + ScGridWindow* pGridWindow = pViewData->GetActiveWin(); + if (pGridWindow) + { + rJsonWriter.put("cellRange", ScPostIt::NoteRangeToJsonString(rDoc, aNote.maPos)); + } + } + } +} + +void ScModelObj::completeFunction(const OUString& rFunctionName) +{ + ScInputHandler* pHdl = SC_MOD()->GetInputHdl(); + if (pHdl) + { + assert(!rFunctionName.isEmpty()); + pHdl->LOKPasteFunctionData(rFunctionName); + } +} + +OString ScModelObj::getViewRenderState(SfxViewShell* pViewShell) +{ + OStringBuffer aState; + ScViewData* pViewData = nullptr; + + if (pViewShell) + { + ScTabViewShell* pTabViewShell = dynamic_cast< ScTabViewShell*>(pViewShell); + if (pTabViewShell) + pViewData = &pTabViewShell->GetViewData(); + } + else + { + pViewData = ScDocShell::GetViewData(); + } + + if (pViewData) + { + aState.append(';'); + + const ScViewOptions& aViewOptions = pViewData->GetOptions(); + OString aThemeName = OUStringToOString(aViewOptions.GetColorSchemeName(), RTL_TEXTENCODING_UTF8); + aState.append(aThemeName); + } + + return aState.makeStringAndClear(); +} + +void ScModelObj::initializeForTiledRendering(const css::uno::Sequence<css::beans::PropertyValue>& rArguments) +{ + SolarMutexGuard aGuard; + + // enable word autocompletion + ScAppOptions aAppOptions(SC_MOD()->GetAppOptions()); + aAppOptions.SetAutoComplete(true); + SC_MOD()->SetAppOptions(aAppOptions); + + for (const beans::PropertyValue& rValue : rArguments) + { + if (rValue.Name == ".uno:SpellOnline" && rValue.Value.has<bool>()) + { + ScDocOptions options = GetDocument()->GetDocOptions(); + options.SetAutoSpell(rValue.Value.get<bool>()); + GetDocument()->SetDocOptions(options); + } + } + + // show us the text exactly + ScInputOptions aInputOptions(SC_MOD()->GetInputOptions()); + aInputOptions.SetTextWysiwyg(true); + aInputOptions.SetReplaceCellsWarn(false); + SC_MOD()->SetInputOptions(aInputOptions); + pDocShell->CalcOutputFactor(); + + // when the "This document may contain formatting or content that cannot + // be saved..." dialog appears, it is auto-cancelled with tiled rendering, + // causing 'Save' being disabled; so let's always save to the original + // format + auto xChanges = comphelper::ConfigurationChanges::create(); + officecfg::Office::Common::Save::Document::WarnAlienFormat::set(false, xChanges); + xChanges->commit(); +} + +uno::Any SAL_CALL ScModelObj::queryInterface( const uno::Type& rType ) +{ + uno::Any aReturn = ::cppu::queryInterface(rType, + static_cast< sheet::XSpreadsheetDocument *>(this), + static_cast< document::XActionLockable *>(this), + static_cast< sheet::XCalculatable *>(this), + static_cast< util::XProtectable *>(this), + static_cast< drawing::XDrawPagesSupplier *>(this), + static_cast< sheet::XGoalSeek *>(this), + static_cast< sheet::XConsolidatable *>(this), + static_cast< sheet::XDocumentAuditing *>(this), + static_cast< style::XStyleFamiliesSupplier *>(this), + static_cast< view::XRenderable *>(this), + static_cast< document::XLinkTargetSupplier *>(this), + static_cast< beans::XPropertySet *>(this), + static_cast< lang::XMultiServiceFactory *>(this), + static_cast< lang::XServiceInfo *>(this), + static_cast< util::XChangesNotifier *>(this), + static_cast< sheet::opencl::XOpenCLSelection *>(this), + static_cast< chart2::XDataProviderAccess *>(this)); + if ( aReturn.hasValue() ) + return aReturn; + + uno::Any aRet(SfxBaseModel::queryInterface( rType )); + if ( !aRet.hasValue() + && rType != cppu::UnoType<css::document::XDocumentEventBroadcaster>::get() + && rType != cppu::UnoType<css::frame::XController>::get() + && rType != cppu::UnoType<css::frame::XFrame>::get() + && rType != cppu::UnoType<css::script::XInvocation>::get() + && rType != cppu::UnoType<css::beans::XFastPropertySet>::get() + && rType != cppu::UnoType<css::awt::XWindow>::get()) + { + GetFormatter(); + if ( xNumberAgg.is() ) + aRet = xNumberAgg->queryAggregation( rType ); + } + + return aRet; +} + +void SAL_CALL ScModelObj::acquire() noexcept +{ + SfxBaseModel::acquire(); +} + +void SAL_CALL ScModelObj::release() noexcept +{ + SfxBaseModel::release(); +} + +uno::Sequence<uno::Type> SAL_CALL ScModelObj::getTypes() +{ + static const uno::Sequence<uno::Type> aTypes = [&]() + { + uno::Sequence<uno::Type> aAggTypes; + if ( GetFormatter().is() ) + { + const uno::Type& rProvType = cppu::UnoType<lang::XTypeProvider>::get(); + uno::Any aNumProv(xNumberAgg->queryAggregation(rProvType)); + if(auto xNumProv + = o3tl::tryAccess<uno::Reference<lang::XTypeProvider>>(aNumProv)) + { + aAggTypes = (*xNumProv)->getTypes(); + } + } + return comphelper::concatSequences( + SfxBaseModel::getTypes(), + aAggTypes, + uno::Sequence<uno::Type> + { + cppu::UnoType<sheet::XSpreadsheetDocument>::get(), + cppu::UnoType<document::XActionLockable>::get(), + cppu::UnoType<sheet::XCalculatable>::get(), + cppu::UnoType<util::XProtectable>::get(), + cppu::UnoType<drawing::XDrawPagesSupplier>::get(), + cppu::UnoType<sheet::XGoalSeek>::get(), + cppu::UnoType<sheet::XConsolidatable>::get(), + cppu::UnoType<sheet::XDocumentAuditing>::get(), + cppu::UnoType<style::XStyleFamiliesSupplier>::get(), + cppu::UnoType<view::XRenderable>::get(), + cppu::UnoType<document::XLinkTargetSupplier>::get(), + cppu::UnoType<beans::XPropertySet>::get(), + cppu::UnoType<lang::XMultiServiceFactory>::get(), + cppu::UnoType<lang::XServiceInfo>::get(), + cppu::UnoType<util::XChangesNotifier>::get(), + cppu::UnoType<sheet::opencl::XOpenCLSelection>::get(), + } ); + }(); + return aTypes; +} + +uno::Sequence<sal_Int8> SAL_CALL ScModelObj::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +void ScModelObj::Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) +{ + // Not interested in reference update hints here + + const SfxHintId nId = rHint.GetId(); + if ( nId == SfxHintId::Dying ) + { + pDocShell = nullptr; // has become invalid + if (xNumberAgg.is()) + { + SvNumberFormatsSupplierObj* pNumFmt = + comphelper::getFromUnoTunnel<SvNumberFormatsSupplierObj>( + uno::Reference<util::XNumberFormatsSupplier>(xNumberAgg, uno::UNO_QUERY) ); + if ( pNumFmt ) + pNumFmt->SetNumberFormatter( nullptr ); + } + + pPrintFuncCache.reset(); // must be deleted because it has a pointer to the DocShell + m_pPrintState.reset(); + } + else if ( nId == SfxHintId::DataChanged ) + { + // cached data for rendering become invalid when contents change + // (if a broadcast is added to SetDrawModified, is has to be tested here, too) + + pPrintFuncCache.reset(); + m_pPrintState.reset(); + + // handle "OnCalculate" sheet events (search also for VBA event handlers) + if ( pDocShell ) + { + ScDocument& rDoc = pDocShell->GetDocument(); + if ( rDoc.GetVbaEventProcessor().is() ) + { + // If the VBA event processor is set, HasAnyCalcNotification is much faster than HasAnySheetEventScript + if ( rDoc.HasAnyCalcNotification() && rDoc.HasAnySheetEventScript( ScSheetEventId::CALCULATE, true ) ) + HandleCalculateEvents(); + } + else + { + if ( rDoc.HasAnySheetEventScript( ScSheetEventId::CALCULATE ) ) + HandleCalculateEvents(); + } + } + } + + // always call parent - SfxBaseModel might need to handle the same hints again + SfxBaseModel::Notify( rBC, rHint ); // SfxBaseModel is derived from SfxListener +} + +// XSpreadsheetDocument + +uno::Reference<sheet::XSpreadsheets> SAL_CALL ScModelObj::getSheets() +{ + SolarMutexGuard aGuard; + if (pDocShell) + return new ScTableSheetsObj(pDocShell); + return nullptr; +} + +css::uno::Reference< ::css::chart2::data::XDataProvider > SAL_CALL ScModelObj::createDataProvider() +{ + if (pDocShell) + { + return css::uno::Reference< ::css::chart2::data::XDataProvider > ( + ScServiceProvider::MakeInstance(ScServiceProvider::Type::CHDATAPROV, pDocShell), uno::UNO_QUERY); + } + return nullptr; +} + +// XStyleFamiliesSupplier + +uno::Reference<container::XNameAccess> SAL_CALL ScModelObj::getStyleFamilies() +{ + SolarMutexGuard aGuard; + if (pDocShell) + return new ScStyleFamiliesObj(pDocShell); + return nullptr; +} + +// XRenderable + +static OutputDevice* lcl_GetRenderDevice( const uno::Sequence<beans::PropertyValue>& rOptions ) +{ + OutputDevice* pRet = nullptr; + for (const beans::PropertyValue& rProp : rOptions) + { + const OUString & rPropName = rProp.Name; + + if (rPropName == SC_UNONAME_RENDERDEV) + { + uno::Reference<awt::XDevice> xRenderDevice(rProp.Value, uno::UNO_QUERY); + if ( xRenderDevice.is() ) + { + VCLXDevice* pDevice = dynamic_cast<VCLXDevice*>( xRenderDevice.get() ); + if ( pDevice ) + { + pRet = pDevice->GetOutputDevice().get(); + pRet->SetDigitLanguage( ScModule::GetOptDigitLanguage() ); + } + } + } + } + return pRet; +} + +static bool lcl_ParseTarget( const OUString& rTarget, ScRange& rTargetRange, tools::Rectangle& rTargetRect, + bool& rIsSheet, ScDocument& rDoc, SCTAB nSourceTab ) +{ + // test in same order as in SID_CURRENTCELL execute + + ScAddress aAddress; + SCTAB nNameTab; + sal_Int32 nNumeric = 0; + + bool bRangeValid = false; + bool bRectValid = false; + + if ( rTargetRange.Parse( rTarget, rDoc ) & ScRefFlags::VALID ) + { + bRangeValid = true; // range reference + } + else if ( aAddress.Parse( rTarget, rDoc ) & ScRefFlags::VALID ) + { + rTargetRange = aAddress; + bRangeValid = true; // cell reference + } + else if ( ScRangeUtil::MakeRangeFromName( rTarget, rDoc, nSourceTab, rTargetRange ) || + ScRangeUtil::MakeRangeFromName( rTarget, rDoc, nSourceTab, rTargetRange, RUTL_DBASE ) ) + { + bRangeValid = true; // named range or database range + } + else if ( comphelper::string::isdigitAsciiString(rTarget) && + ( nNumeric = rTarget.toInt32() ) > 0 && nNumeric <= rDoc.MaxRow()+1 ) + { + // row number is always mapped to cell A(row) on the same sheet + rTargetRange = ScAddress( 0, static_cast<SCROW>(nNumeric-1), nSourceTab ); // target row number is 1-based + bRangeValid = true; // row number + } + else if ( rDoc.GetTable( rTarget, nNameTab ) ) + { + rTargetRange = ScAddress(0,0,nNameTab); + bRangeValid = true; // sheet name + rIsSheet = true; // needs special handling (first page of the sheet) + } + else + { + // look for named drawing object + + ScDrawLayer* pDrawLayer = rDoc.GetDrawLayer(); + if ( pDrawLayer ) + { + SCTAB nTabCount = rDoc.GetTableCount(); + for (SCTAB i=0; i<nTabCount && !bRangeValid; i++) + { + SdrPage* pPage = pDrawLayer->GetPage(static_cast<sal_uInt16>(i)); + OSL_ENSURE(pPage,"Page ?"); + if (pPage) + { + SdrObjListIter aIter( pPage, SdrIterMode::DeepWithGroups ); + SdrObject* pObject = aIter.Next(); + while (pObject && !bRangeValid) + { + if ( ScDrawLayer::GetVisibleName( pObject ) == rTarget ) + { + rTargetRect = pObject->GetLogicRect(); // 1/100th mm + rTargetRange = rDoc.GetRange( i, rTargetRect ); // underlying cells + bRangeValid = bRectValid = true; // rectangle is valid + } + pObject = aIter.Next(); + } + } + } + } + } + if ( bRangeValid && !bRectValid ) + { + // get rectangle for cell range + rTargetRect = rDoc.GetMMRect( rTargetRange.aStart.Col(), rTargetRange.aStart.Row(), + rTargetRange.aEnd.Col(), rTargetRange.aEnd.Row(), + rTargetRange.aStart.Tab() ); + } + + return bRangeValid; +} + +bool ScModelObj::FillRenderMarkData( const uno::Any& aSelection, + const uno::Sequence< beans::PropertyValue >& rOptions, + ScMarkData& rMark, + ScPrintSelectionStatus& rStatus, OUString& rPagesStr, + bool& rbRenderToGraphic ) const +{ + OSL_ENSURE( !rMark.IsMarked() && !rMark.IsMultiMarked(), "FillRenderMarkData: MarkData must be empty" ); + OSL_ENSURE( pDocShell, "FillRenderMarkData: DocShell must be set" ); + + bool bDone = false; + + uno::Reference<frame::XController> xView; + + // defaults when no options are passed: all sheets, include empty pages + bool bSelectedSheetsOnly = false; + bool bSuppressEmptyPages = true; + + bool bHasPrintContent = false; + sal_Int32 nPrintContent = 0; // all sheets / selected sheets / selected cells + sal_Int32 nPrintRange = 0; // all pages / pages + sal_Int32 nEOContent = 0; // even pages / odd pages + OUString aPageRange; // "pages" edit value + + for( const auto& rOption : rOptions ) + { + if ( rOption.Name == "IsOnlySelectedSheets" ) + { + rOption.Value >>= bSelectedSheetsOnly; + } + else if ( rOption.Name == "IsSuppressEmptyPages" ) + { + rOption.Value >>= bSuppressEmptyPages; + } + else if ( rOption.Name == "PageRange" ) + { + rOption.Value >>= aPageRange; + } + else if ( rOption.Name == "PrintRange" ) + { + rOption.Value >>= nPrintRange; + } + else if ( rOption.Name == "EvenOdd" ) + { + rOption.Value >>= nEOContent; + } + else if ( rOption.Name == "PrintContent" ) + { + bHasPrintContent = true; + rOption.Value >>= nPrintContent; + } + else if ( rOption.Name == "View" ) + { + rOption.Value >>= xView; + } + else if ( rOption.Name == "RenderToGraphic" ) + { + rOption.Value >>= rbRenderToGraphic; + } + } + + // "Print Content" selection wins over "Selected Sheets" option + if ( bHasPrintContent ) + bSelectedSheetsOnly = ( nPrintContent != 0 ); + + uno::Reference<uno::XInterface> xInterface(aSelection, uno::UNO_QUERY); + if ( xInterface.is() ) + { + ScCellRangesBase* pSelObj = dynamic_cast<ScCellRangesBase*>( xInterface.get() ); + uno::Reference< drawing::XShapes > xShapes( xInterface, uno::UNO_QUERY ); + if ( pSelObj && pSelObj->GetDocShell() == pDocShell ) + { + bool bSheet = ( dynamic_cast<ScTableSheetObj*>( pSelObj ) != nullptr ); + bool bCursor = pSelObj->IsCursorOnly(); + const ScRangeList& rRanges = pSelObj->GetRangeList(); + + rMark.MarkFromRangeList( rRanges, false ); + rMark.MarkToSimple(); + + if ( rMark.IsMultiMarked() ) + { + // #i115266# copy behavior of old printing: + // treat multiple selection like a single selection with the enclosing range + const ScRange& aMultiMarkArea = rMark.GetMultiMarkArea(); + rMark.ResetMark(); + rMark.SetMarkArea( aMultiMarkArea ); + } + + if ( rMark.IsMarked() && !rMark.IsMultiMarked() ) + { + // a sheet object is treated like an empty selection: print the used area of the sheet + + if ( bCursor || bSheet ) // nothing selected -> use whole tables + { + rMark.ResetMark(); // doesn't change table selection + rStatus.SetMode( ScPrintSelectionMode::Cursor ); + } + else + rStatus.SetMode( ScPrintSelectionMode::Range ); + + rStatus.SetRanges( rRanges ); + bDone = true; + } + // multi selection isn't supported + } + else if( xShapes.is() ) + { + //print a selected ole object + // multi selection isn't supported yet + uno::Reference< drawing::XShape > xShape( xShapes->getByIndex(0), uno::UNO_QUERY ); + SdrObject* pSdrObj = SdrObject::getSdrObjectFromXShape( xShape ); + if( pSdrObj && pDocShell ) + { + ScDocument& rDoc = pDocShell->GetDocument(); + tools::Rectangle aObjRect = pSdrObj->GetCurrentBoundRect(); + SCTAB nCurrentTab = ScDocShell::GetCurTab(); + ScRange aRange = rDoc.GetRange( nCurrentTab, aObjRect ); + rMark.SetMarkArea( aRange ); + + if( rMark.IsMarked() && !rMark.IsMultiMarked() ) + { + rStatus.SetMode( ScPrintSelectionMode::RangeExclusivelyOleAndDrawObjects ); + bDone = true; + } + } + } + else if ( comphelper::getFromUnoTunnel<ScModelObj>( xInterface ) == this ) + { + // render the whole document + // -> no selection, all sheets + + SCTAB nTabCount = pDocShell->GetDocument().GetTableCount(); + for (SCTAB nTab = 0; nTab < nTabCount; nTab++) + rMark.SelectTable( nTab, true ); + rStatus.SetMode( ScPrintSelectionMode::Document ); + bDone = true; + } + // other selection types aren't supported + } + + // restrict to selected sheets if a view is available + uno::Reference<sheet::XSelectedSheetsSupplier> xSelectedSheets(xView, uno::UNO_QUERY); + if (bSelectedSheetsOnly && pDocShell && xSelectedSheets.is()) + { + const uno::Sequence<sal_Int32> aSelected = xSelectedSheets->getSelectedSheets(); + ScMarkData::MarkedTabsType aSelectedTabs; + SCTAB nMaxTab = pDocShell->GetDocument().GetTableCount() -1; + for (const auto& rSelected : aSelected) + { + SCTAB nSelected = static_cast<SCTAB>(rSelected); + if (ValidTab(nSelected, nMaxTab)) + aSelectedTabs.insert(nSelected); + } + rMark.SetSelectedTabs(aSelectedTabs); + } + + ScPrintOptions aNewOptions; + aNewOptions.SetSkipEmpty( bSuppressEmptyPages ); + aNewOptions.SetAllSheets( !bSelectedSheetsOnly ); + rStatus.SetOptions( aNewOptions ); + + // "PrintRange" enables (1) or disables (0) the "PageRange" edit + if ( nPrintRange == 1 ) + rPagesStr = aPageRange; + else + rPagesStr.clear(); + + return bDone; +} + +sal_Int32 SAL_CALL ScModelObj::getRendererCount(const uno::Any& aSelection, + const uno::Sequence<beans::PropertyValue>& rOptions) +{ + SolarMutexGuard aGuard; + if (!pDocShell) + { + throw lang::DisposedException( OUString(), + static_cast< sheet::XSpreadsheetDocument* >(this) ); + } + + ScMarkData aMark(GetDocument()->GetSheetLimits()); + ScPrintSelectionStatus aStatus; + OUString aPagesStr; + bool bRenderToGraphic = false; + if ( !FillRenderMarkData( aSelection, rOptions, aMark, aStatus, aPagesStr, bRenderToGraphic ) ) + return 0; + + // The same ScPrintFuncCache object in pPrintFuncCache is used as long as + // the same selection is used (aStatus) and the document isn't changed + // (pPrintFuncCache is cleared in Notify handler) + + if ( !pPrintFuncCache || !pPrintFuncCache->IsSameSelection( aStatus ) ) + { + pPrintFuncCache.reset(new ScPrintFuncCache( pDocShell, aMark, aStatus )); + } + sal_Int32 nPages = pPrintFuncCache->GetPageCount(); + + m_pPrintState.reset(); + maValidPages.clear(); + + sal_Int32 nContent = 0; + sal_Int32 nEOContent = 0; + bool bSinglePageSheets = false; + for ( const auto& rValue : rOptions) + { + if ( rValue.Name == "PrintRange" ) + { + rValue.Value >>= nContent; + } + else if ( rValue.Name == "SinglePageSheets" ) + { + rValue.Value >>= bSinglePageSheets; + } + else if ( rValue.Name == "EvenOdd" ) + { + rValue.Value >>= nEOContent; + } + } + + if (bSinglePageSheets) + { + return pDocShell->GetDocument().GetTableCount(); + } + + bool bIsPrintEvenPages = (nEOContent != 1 && nContent == 0) || nContent != 0; + bool bIsPrintOddPages = (nEOContent != 2 && nContent == 0) || nContent != 0; + + for ( sal_Int32 nPage = 1; nPage <= nPages; nPage++ ) + { + if ( (bIsPrintEvenPages && IsOnEvenPage( nPage )) || (bIsPrintOddPages && !IsOnEvenPage( nPage )) ) + maValidPages.push_back( nPage ); + } + + sal_Int32 nSelectCount = static_cast<sal_Int32>( maValidPages.size() ); + + if ( nEOContent == 1 || nEOContent == 2 ) // even pages / odd pages + return nSelectCount; + + if ( !aPagesStr.isEmpty() ) + { + StringRangeEnumerator aRangeEnum( aPagesStr, 0, nPages-1 ); + nSelectCount = aRangeEnum.size(); + } + return (nSelectCount > 0) ? nSelectCount : 1; +} + +static sal_Int32 lcl_GetRendererNum( sal_Int32 nSelRenderer, std::u16string_view rPagesStr, sal_Int32 nTotalPages ) +{ + if ( rPagesStr.empty() ) + return nSelRenderer; + + StringRangeEnumerator aRangeEnum( rPagesStr, 0, nTotalPages-1 ); + StringRangeEnumerator::Iterator aIter = aRangeEnum.begin(); + StringRangeEnumerator::Iterator aEnd = aRangeEnum.end(); + for ( ; nSelRenderer > 0 && aIter != aEnd; --nSelRenderer ) + ++aIter; + + return *aIter; // returns -1 if reached the end +} + +static bool lcl_renderSelectionToGraphic( bool bRenderToGraphic, const ScPrintSelectionStatus& rStatus ) +{ + return bRenderToGraphic && rStatus.GetMode() == ScPrintSelectionMode::Range; +} + +uno::Sequence<beans::PropertyValue> SAL_CALL ScModelObj::getRenderer( sal_Int32 nSelRenderer, + const uno::Any& aSelection, const uno::Sequence<beans::PropertyValue>& rOptions ) +{ + SolarMutexGuard aGuard; + if (!pDocShell) + { + throw lang::DisposedException( OUString(), + static_cast< sheet::XSpreadsheetDocument* >(this) ); + } + + ScMarkData aMark(pDocShell->GetDocument().GetSheetLimits()); + ScPrintSelectionStatus aStatus; + OUString aPagesStr; + // #i115266# if FillRenderMarkData fails, keep nTotalPages at 0, but still handle getRenderer(0) below + tools::Long nTotalPages = 0; + bool bRenderToGraphic = false; + bool bSinglePageSheets = false; + if ( FillRenderMarkData( aSelection, rOptions, aMark, aStatus, aPagesStr, bRenderToGraphic ) ) + { + if ( !pPrintFuncCache || !pPrintFuncCache->IsSameSelection( aStatus ) ) + { + pPrintFuncCache.reset(new ScPrintFuncCache( pDocShell, aMark, aStatus )); + } + nTotalPages = pPrintFuncCache->GetPageCount(); + } + + for ( const auto& rValue : rOptions) + { + if ( rValue.Name == "SinglePageSheets" ) + { + rValue.Value >>= bSinglePageSheets; + break; + } + } + + if (bSinglePageSheets) + nTotalPages = pDocShell->GetDocument().GetTableCount(); + + sal_Int32 nRenderer = lcl_GetRendererNum( nSelRenderer, aPagesStr, nTotalPages ); + + if ( nRenderer < 0 ) + { + if ( nSelRenderer != 0 ) + throw lang::IllegalArgumentException(); + + // getRenderer(0) is used to query the settings, so it must always return something + + awt::Size aPageSize; + if (lcl_renderSelectionToGraphic( bRenderToGraphic, aStatus)) + { + assert( aMark.IsMarked()); + const ScRange& aRange = aMark.GetMarkArea(); + tools::Rectangle aMMRect( pDocShell->GetDocument().GetMMRect( + aRange.aStart.Col(), aRange.aStart.Row(), + aRange.aEnd.Col(), aRange.aEnd.Row(), aRange.aStart.Tab())); + aPageSize.Width = aMMRect.GetWidth(); + aPageSize.Height = aMMRect.GetHeight(); + } + else + { + SCTAB const nCurTab = 0; //! use current sheet from view? + ScPrintFunc aDefaultFunc( pDocShell, pDocShell->GetPrinter(), nCurTab ); + Size aTwips = aDefaultFunc.GetPageSize(); + aPageSize.Width = convertTwipToMm100(aTwips.Width()); + aPageSize.Height = convertTwipToMm100(aTwips.Height()); + } + + uno::Sequence<beans::PropertyValue> aSequence( comphelper::InitPropertySequence({ + { SC_UNONAME_PAGESIZE, uno::Any(aPageSize) } + })); + + if( ! pPrinterOptions ) + pPrinterOptions.reset(new ScPrintUIOptions); + else + pPrinterOptions->SetDefaults(); + pPrinterOptions->appendPrintUIOptions( aSequence ); + return aSequence; + + } + + // printer is used as device (just for page layout), draw view is not needed + + SCTAB nTab; + if (bSinglePageSheets) + nTab = nSelRenderer; + else if ( !maValidPages.empty() ) + nTab = pPrintFuncCache->GetTabForPage( maValidPages.at( nRenderer )-1 ); + else + nTab = pPrintFuncCache->GetTabForPage( nRenderer ); + + + ScRange aRange; + const ScRange* pSelRange = nullptr; + if ( bSinglePageSheets ) + { + SCCOL nStartCol; + SCROW nStartRow; + const ScDocument* pDocument = &pDocShell->GetDocument(); + pDocument->GetDataStart( nTab, nStartCol, nStartRow ); + SCCOL nEndCol; + SCROW nEndRow; + pDocument->GetPrintArea( nTab, nEndCol, nEndRow ); + + aRange.aStart = ScAddress(nStartCol, nStartRow, nTab); + aRange.aEnd = ScAddress(nEndCol, nEndRow, nTab); + + table::CellRangeAddress aRangeAddress( nTab, + aRange.aStart.Col(), aRange.aStart.Row(), + aRange.aEnd.Col(), aRange.aEnd.Row() ); + tools::Rectangle aMMRect( pDocShell->GetDocument().GetMMRect( + aRange.aStart.Col(), aRange.aStart.Row(), + aRange.aEnd.Col(), aRange.aEnd.Row(), aRange.aStart.Tab())); + + const awt::Size aPageSize(aMMRect.GetWidth(), aMMRect.GetHeight()); + const awt::Point aCalcPagePos(aMMRect.Left(), aMMRect.Top()); + + uno::Sequence<beans::PropertyValue> aSequence + { + comphelper::makePropertyValue(SC_UNONAME_PAGESIZE, aPageSize), + // #i111158# all positions are relative to the whole page, including non-printable area + comphelper::makePropertyValue(SC_UNONAME_INC_NP_AREA, true), + comphelper::makePropertyValue(SC_UNONAME_SOURCERANGE, aRangeAddress), + comphelper::makePropertyValue(SC_UNONAME_CALCPAGESIZE, aPageSize), // TODO aPageSize too ? + comphelper::makePropertyValue(SC_UNONAME_CALCPAGEPOS, aCalcPagePos) + }; + + if( ! pPrinterOptions ) + pPrinterOptions.reset(new ScPrintUIOptions); + else + pPrinterOptions->SetDefaults(); + pPrinterOptions->appendPrintUIOptions( aSequence ); + return aSequence; + } + else if ( aMark.IsMarked() ) + { + aRange = aMark.GetMarkArea(); + pSelRange = &aRange; + } + + awt::Size aPageSize; + bool bWasCellRange = false; + ScRange aCellRange; + if (lcl_renderSelectionToGraphic( bRenderToGraphic, aStatus)) + { + bWasCellRange = true; + aCellRange = aRange; + tools::Rectangle aMMRect( pDocShell->GetDocument().GetMMRect( + aRange.aStart.Col(), aRange.aStart.Row(), + aRange.aEnd.Col(), aRange.aEnd.Row(), aRange.aStart.Tab())); + aPageSize.Width = aMMRect.GetWidth(); + aPageSize.Height = aMMRect.GetHeight(); + } + else + { + std::unique_ptr<ScPrintFunc, o3tl::default_delete<ScPrintFunc>> pPrintFunc; + if (m_pPrintState && m_pPrintState->nPrintTab == nTab) + pPrintFunc.reset(new ScPrintFunc(pDocShell, pDocShell->GetPrinter(), *m_pPrintState, &aStatus.GetOptions())); + else + pPrintFunc.reset(new ScPrintFunc(pDocShell, pDocShell->GetPrinter(), nTab, + pPrintFuncCache->GetFirstAttr(nTab), nTotalPages, pSelRange, &aStatus.GetOptions())); + pPrintFunc->SetRenderFlag( true ); + + sal_Int32 nContent = 0; + sal_Int32 nEOContent = 0; + for ( const auto& rValue : rOptions) + { + if ( rValue.Name == "PrintRange" ) + { + rValue.Value >>= nContent; + } + else if ( rValue.Name == "EvenOdd" ) + { + rValue.Value >>= nEOContent; + } + } + + MultiSelection aPage; + aPage.SetTotalRange( Range(0,RANGE_MAX) ); + + bool bOddOrEven = (nContent == 0 && nEOContent == 1) || (nContent == 1 && nEOContent == 2); // even pages or odd pages + // tdf#127682 when odd/even allow nRenderer of 0 even when maValidPages is empty + // to allow PrinterController::abortJob to spool an empty page as part of + // its abort procedure + if (bOddOrEven && !maValidPages.empty()) + aPage.Select( maValidPages.at(nRenderer) ); + else + aPage.Select( nRenderer+1 ); + + tools::Long nDisplayStart = pPrintFuncCache->GetDisplayStart( nTab ); + tools::Long nTabStart = pPrintFuncCache->GetTabStart( nTab ); + + (void)pPrintFunc->DoPrint( aPage, nTabStart, nDisplayStart, false, nullptr ); + + bWasCellRange = pPrintFunc->GetLastSourceRange( aCellRange ); + Size aTwips = pPrintFunc->GetPageSize(); + + if (!m_pPrintState) + { + m_pPrintState.reset(new ScPrintState()); + pPrintFunc->GetPrintState(*m_pPrintState, true); + } + + aPageSize.Width = convertTwipToMm100(aTwips.Width()); + aPageSize.Height = convertTwipToMm100(aTwips.Height()); + } + + tools::Long nPropCount = bWasCellRange ? 5 : 4; + uno::Sequence<beans::PropertyValue> aSequence(nPropCount); + beans::PropertyValue* pArray = aSequence.getArray(); + pArray[0].Name = SC_UNONAME_PAGESIZE; + pArray[0].Value <<= aPageSize; + // #i111158# all positions are relative to the whole page, including non-printable area + pArray[1].Name = SC_UNONAME_INC_NP_AREA; + pArray[1].Value <<= true; + if ( bWasCellRange ) + { + table::CellRangeAddress aRangeAddress( nTab, + aCellRange.aStart.Col(), aCellRange.aStart.Row(), + aCellRange.aEnd.Col(), aCellRange.aEnd.Row() ); + tools::Rectangle aMMRect( pDocShell->GetDocument().GetMMRect( + aCellRange.aStart.Col(), aCellRange.aStart.Row(), + aCellRange.aEnd.Col(), aCellRange.aEnd.Row(), aCellRange.aStart.Tab())); + + const awt::Size aCalcPageSize(aMMRect.GetWidth(), aMMRect.GetHeight()); + const awt::Point aCalcPagePos(aMMRect.Left(), aMMRect.Top()); + + pArray[2].Name = SC_UNONAME_SOURCERANGE; + pArray[2].Value <<= aRangeAddress; + pArray[3].Name = SC_UNONAME_CALCPAGESIZE; + pArray[3].Value <<= aCalcPageSize; + pArray[4].Name = SC_UNONAME_CALCPAGEPOS; + pArray[4].Value <<= aCalcPagePos; + } + + if( ! pPrinterOptions ) + pPrinterOptions.reset(new ScPrintUIOptions); + else + pPrinterOptions->SetDefaults(); + pPrinterOptions->appendPrintUIOptions( aSequence ); + return aSequence; +} + +void SAL_CALL ScModelObj::render( sal_Int32 nSelRenderer, const uno::Any& aSelection, + const uno::Sequence<beans::PropertyValue>& rOptions ) +{ + SolarMutexGuard aGuard; + if (!pDocShell) + { + throw lang::DisposedException( OUString(), + static_cast< sheet::XSpreadsheetDocument* >(this) ); + } + + ScMarkData aMark(pDocShell->GetDocument().GetSheetLimits()); + ScPrintSelectionStatus aStatus; + OUString aPagesStr; + bool bRenderToGraphic = false; + bool bSinglePageSheets = false; + if ( !FillRenderMarkData( aSelection, rOptions, aMark, aStatus, aPagesStr, bRenderToGraphic ) ) + throw lang::IllegalArgumentException(); + + if ( !pPrintFuncCache || !pPrintFuncCache->IsSameSelection( aStatus ) ) + { + pPrintFuncCache.reset(new ScPrintFuncCache( pDocShell, aMark, aStatus )); + } + tools::Long nTotalPages = pPrintFuncCache->GetPageCount(); + + for ( const auto& rValue : rOptions) + { + if ( rValue.Name == "SinglePageSheets" ) + { + rValue.Value >>= bSinglePageSheets; + break; + } + } + + if (bSinglePageSheets) + nTotalPages = pDocShell->GetDocument().GetTableCount(); + + sal_Int32 nRenderer = lcl_GetRendererNum( nSelRenderer, aPagesStr, nTotalPages ); + if ( nRenderer < 0 ) + throw lang::IllegalArgumentException(); + + OutputDevice* pDev = lcl_GetRenderDevice( rOptions ); + if ( !pDev ) + throw lang::IllegalArgumentException(); + + ScDocument& rDoc = pDocShell->GetDocument(); + + ScRange aRange; + const ScRange* pSelRange = nullptr; + if ( bSinglePageSheets ) + { + awt::Size aPageSize; + SCCOL nStartCol; + SCROW nStartRow; + rDoc.GetDataStart( nSelRenderer, nStartCol, nStartRow ); + SCCOL nEndCol; + SCROW nEndRow; + rDoc.GetPrintArea( nSelRenderer, nEndCol, nEndRow ); + + aRange.aStart = ScAddress(nStartCol, nStartRow, nSelRenderer); + aRange.aEnd = ScAddress(nEndCol, nEndRow, nSelRenderer); + + tools::Rectangle aMMRect( pDocShell->GetDocument().GetMMRect( + aRange.aStart.Col(), aRange.aStart.Row(), + aRange.aEnd.Col(), aRange.aEnd.Row(), aRange.aStart.Tab())); + + aPageSize.Width = aMMRect.GetWidth(); + aPageSize.Height = aMMRect.GetHeight(); + + //Set visible tab + SCTAB nVisTab = rDoc.GetVisibleTab(); + if (nVisTab != nSelRenderer) + { + nVisTab = nSelRenderer; + rDoc.SetVisibleTab(nVisTab); + } + + pDocShell->DoDraw(pDev, Point(0,0), Size(aPageSize.Width, aPageSize.Height), JobSetup()); + + return; + } + else if ( aMark.IsMarked() ) + { + aRange = aMark.GetMarkArea(); + pSelRange = &aRange; + } + + if (lcl_renderSelectionToGraphic( bRenderToGraphic, aStatus)) + { + // Similar to as in and when calling ScTransferObj::PaintToDev() + + tools::Rectangle aBound( Point(), pDev->GetOutputSize()); + + ScViewData aViewData(rDoc); + + aViewData.SetTabNo( aRange.aStart.Tab() ); + aViewData.SetScreen( aRange.aStart.Col(), aRange.aStart.Row(), aRange.aEnd.Col(), aRange.aEnd.Row() ); + + const double nPrintFactor = 1.0; /* XXX: currently (2017-08-28) is not evaluated */ + // The bMetaFile argument maybe could be + // pDev->GetConnectMetaFile() != nullptr + // but for some yet unknown reason does not draw cell content if true. + ScPrintFunc::DrawToDev( rDoc, pDev, nPrintFactor, aBound, &aViewData, false /*bMetaFile*/ ); + + return; + } + + struct DrawViewKeeper + { + std::unique_ptr<FmFormView> mpDrawView; + DrawViewKeeper() {} + ~DrawViewKeeper() + { + if (mpDrawView) + { + mpDrawView->HideSdrPage(); + mpDrawView.reset(); + } + } + } aDrawViewKeeper; + + SCTAB nTab; + if ( !maValidPages.empty() ) + nTab = pPrintFuncCache->GetTabForPage( maValidPages.at( nRenderer )-1 ); + else + nTab = pPrintFuncCache->GetTabForPage( nRenderer ); + + ScDrawLayer* pModel = rDoc.GetDrawLayer(); + + if( pModel ) + { + aDrawViewKeeper.mpDrawView.reset( new FmFormView( + *pModel, + pDev) ); + aDrawViewKeeper.mpDrawView->ShowSdrPage(aDrawViewKeeper.mpDrawView->GetModel().GetPage(nTab)); + aDrawViewKeeper.mpDrawView->SetPrintPreview(); + } + + // to increase performance, ScPrintState might be used here for subsequent + // pages of the same sheet + + + std::unique_ptr<ScPrintFunc, o3tl::default_delete<ScPrintFunc>> pPrintFunc; + if (m_pPrintState && m_pPrintState->nPrintTab == nTab + && ! pSelRange) // tdf#120161 use selection to set required printed area + pPrintFunc.reset(new ScPrintFunc(pDev, pDocShell, *m_pPrintState, &aStatus.GetOptions())); + else + pPrintFunc.reset(new ScPrintFunc(pDev, pDocShell, nTab, pPrintFuncCache->GetFirstAttr(nTab), nTotalPages, pSelRange, &aStatus.GetOptions())); + + pPrintFunc->SetDrawView( aDrawViewKeeper.mpDrawView.get() ); + pPrintFunc->SetRenderFlag( true ); + if( aStatus.GetMode() == ScPrintSelectionMode::RangeExclusivelyOleAndDrawObjects ) + pPrintFunc->SetExclusivelyDrawOleAndDrawObjects(); + + sal_Int32 nContent = 0; + sal_Int32 nEOContent = 0; + for ( const auto& rValue : rOptions) + { + if ( rValue.Name == "PrintRange" ) + { + rValue.Value >>= nContent; + } + else if ( rValue.Name == "EvenOdd" ) + { + rValue.Value >>= nEOContent; + } + } + + MultiSelection aPage; + aPage.SetTotalRange( Range(0,RANGE_MAX) ); + + bool bOddOrEven = (nContent == 0 && nEOContent == 1) || (nContent == 0 && nEOContent == 2); // even pages or odd pages + // tdf#127682 when odd/even allow nRenderer of 0 even when maValidPages is empty + // to allow PrinterController::abortJob to spool an empty page as part of + // its abort procedure + if (bOddOrEven && !maValidPages.empty()) + aPage.Select( maValidPages.at( nRenderer ) ); + else + aPage.Select( nRenderer+1 ); + + tools::Long nDisplayStart = pPrintFuncCache->GetDisplayStart( nTab ); + tools::Long nTabStart = pPrintFuncCache->GetTabStart( nTab ); + + vcl::PDFExtOutDevData* pPDFData = dynamic_cast< vcl::PDFExtOutDevData* >(pDev->GetExtOutDevData() ); + if ( nRenderer == nTabStart ) + { + if (pPDFData) + { + css::lang::Locale const docLocale(Application::GetSettings().GetLanguageTag().getLocale()); + pPDFData->SetDocumentLocale(docLocale); + } + + // first page of a sheet: add outline item for the sheet name + + if ( pPDFData && pPDFData->GetIsExportBookmarks() ) + { + // the sheet starts at the top of the page + tools::Rectangle aArea( pDev->PixelToLogic( tools::Rectangle( 0,0,0,0 ) ) ); + sal_Int32 nDestID = pPDFData->CreateDest( aArea ); + OUString aTabName; + rDoc.GetName( nTab, aTabName ); + // top-level + pPDFData->CreateOutlineItem( -1/*nParent*/, aTabName, nDestID ); + } + // #i56629# add the named destination stuff + if( pPDFData && pPDFData->GetIsExportNamedDestinations() ) + { + tools::Rectangle aArea( pDev->PixelToLogic( tools::Rectangle( 0,0,0,0 ) ) ); + OUString aTabName; + rDoc.GetName( nTab, aTabName ); + //need the PDF page number here + pPDFData->CreateNamedDest( aTabName, aArea ); + } + } + + (void)pPrintFunc->DoPrint( aPage, nTabStart, nDisplayStart, true, nullptr ); + + if (!m_pPrintState) + { + m_pPrintState.reset(new ScPrintState()); + pPrintFunc->GetPrintState(*m_pPrintState, true); + } + + // resolve the hyperlinks for PDF export + + if ( !pPDFData || pPDFData->GetBookmarks().empty() ) + return; + + // iterate over the hyperlinks that were output for this page + + std::vector< vcl::PDFExtOutDevBookmarkEntry >& rBookmarks = pPDFData->GetBookmarks(); + for ( const auto& rBookmark : rBookmarks ) + { + OUString aBookmark = rBookmark.aBookmark; + if ( aBookmark.toChar() == '#' ) + { + // try to resolve internal link + + OUString aTarget( aBookmark.copy( 1 ) ); + + ScRange aTargetRange; + tools::Rectangle aTargetRect; // 1/100th mm + bool bIsSheet = false; + bool bValid = lcl_ParseTarget( aTarget, aTargetRange, aTargetRect, bIsSheet, rDoc, nTab ); + + if ( bValid ) + { + sal_Int32 nPage = -1; + tools::Rectangle aArea; + if ( bIsSheet ) + { + // Get first page for sheet (if nothing from that sheet is printed, + // this page can show a different sheet) + nPage = pPrintFuncCache->GetTabStart( aTargetRange.aStart.Tab() ); + aArea = pDev->PixelToLogic( tools::Rectangle( 0,0,0,0 ) ); + } + else + { + pPrintFuncCache->InitLocations( aMark, pDev ); // does nothing if already initialized + + ScPrintPageLocation aLocation; + if ( pPrintFuncCache->FindLocation( aTargetRange.aStart, aLocation ) ) + { + nPage = aLocation.nPage; + + // get the rectangle of the page's cell range in 1/100th mm + ScRange aLocRange = aLocation.aCellRange; + tools::Rectangle aLocationMM = rDoc.GetMMRect( + aLocRange.aStart.Col(), aLocRange.aStart.Row(), + aLocRange.aEnd.Col(), aLocRange.aEnd.Row(), + aLocRange.aStart.Tab() ); + tools::Rectangle aLocationPixel = aLocation.aRectangle; + + // Scale and move the target rectangle from aLocationMM to aLocationPixel, + // to get the target rectangle in pixels. + assert(aLocationPixel.GetWidth() != 0 && aLocationPixel.GetHeight() != 0); + + Fraction aScaleX( aLocationPixel.GetWidth(), aLocationMM.GetWidth() ); + Fraction aScaleY( aLocationPixel.GetHeight(), aLocationMM.GetHeight() ); + + tools::Long nX1 = aLocationPixel.Left() + static_cast<tools::Long>( Fraction( aTargetRect.Left() - aLocationMM.Left(), 1 ) * aScaleX ); + tools::Long nX2 = aLocationPixel.Left() + static_cast<tools::Long>( Fraction( aTargetRect.Right() - aLocationMM.Left(), 1 ) * aScaleX ); + tools::Long nY1 = aLocationPixel.Top() + static_cast<tools::Long>( Fraction( aTargetRect.Top() - aLocationMM.Top(), 1 ) * aScaleY ); + tools::Long nY2 = aLocationPixel.Top() + static_cast<tools::Long>( Fraction( aTargetRect.Bottom() - aLocationMM.Top(), 1 ) * aScaleY ); + + if ( nX1 > aLocationPixel.Right() ) nX1 = aLocationPixel.Right(); + if ( nX2 > aLocationPixel.Right() ) nX2 = aLocationPixel.Right(); + if ( nY1 > aLocationPixel.Bottom() ) nY1 = aLocationPixel.Bottom(); + if ( nY2 > aLocationPixel.Bottom() ) nY2 = aLocationPixel.Bottom(); + + // The link target area is interpreted using the device's MapMode at + // the time of the CreateDest call, so PixelToLogic can be used here, + // regardless of the MapMode that is actually selected. + aArea = pDev->PixelToLogic( tools::Rectangle( nX1, nY1, nX2, nY2 ) ); + } + } + + if ( nPage >= 0 ) + pPDFData->SetLinkDest( rBookmark.nLinkId, pPDFData->CreateDest( aArea, nPage ) ); + } + } + else + { + // external link, use as-is + pPDFData->SetLinkURL( rBookmark.nLinkId, aBookmark ); + } + } + rBookmarks.clear(); +} + +// XLinkTargetSupplier + +uno::Reference<container::XNameAccess> SAL_CALL ScModelObj::getLinks() +{ + SolarMutexGuard aGuard; + if (pDocShell) + return new ScLinkTargetTypesObj(pDocShell); + return nullptr; +} + +// XActionLockable + +sal_Bool SAL_CALL ScModelObj::isActionLocked() +{ + SolarMutexGuard aGuard; + bool bLocked = false; + if (pDocShell) + bLocked = ( pDocShell->GetLockCount() != 0 ); + return bLocked; +} + +void SAL_CALL ScModelObj::addActionLock() +{ + SolarMutexGuard aGuard; + if (pDocShell) + pDocShell->LockDocument(); +} + +void SAL_CALL ScModelObj::removeActionLock() +{ + SolarMutexGuard aGuard; + if (pDocShell) + pDocShell->UnlockDocument(); +} + +void SAL_CALL ScModelObj::setActionLocks( sal_Int16 nLock ) +{ + SolarMutexGuard aGuard; + if (pDocShell) + pDocShell->SetLockCount(nLock); +} + +sal_Int16 SAL_CALL ScModelObj::resetActionLocks() +{ + SolarMutexGuard aGuard; + sal_uInt16 nRet = 0; + if (pDocShell) + { + nRet = pDocShell->GetLockCount(); + pDocShell->SetLockCount(0); + } + return nRet; +} + +void SAL_CALL ScModelObj::lockControllers() +{ + SolarMutexGuard aGuard; + SfxBaseModel::lockControllers(); + if (pDocShell) + pDocShell->LockPaint(); +} + +void SAL_CALL ScModelObj::unlockControllers() +{ + SolarMutexGuard aGuard; + if (hasControllersLocked()) + { + SfxBaseModel::unlockControllers(); + if (pDocShell) + pDocShell->UnlockPaint(); + } +} + +// XCalculate + +void SAL_CALL ScModelObj::calculate() +{ + SolarMutexGuard aGuard; + if (pDocShell) + { + comphelper::ProfileZone aZone("calculate"); + pDocShell->DoRecalc(true); + } + else + { + OSL_FAIL("no DocShell"); //! throw exception? + } +} + +void SAL_CALL ScModelObj::calculateAll() +{ + SolarMutexGuard aGuard; + if (pDocShell) + { + comphelper::ProfileZone aZone("calculateAll"); + pDocShell->DoHardRecalc(); + } + else + { + OSL_FAIL("no DocShell"); //! throw exception? + } +} + +sal_Bool SAL_CALL ScModelObj::isAutomaticCalculationEnabled() +{ + SolarMutexGuard aGuard; + if (pDocShell) + return pDocShell->GetDocument().GetAutoCalc(); + + OSL_FAIL("no DocShell"); //! throw exception? + return false; +} + +void SAL_CALL ScModelObj::enableAutomaticCalculation( sal_Bool bEnabledIn ) +{ + bool bEnabled(bEnabledIn); + SolarMutexGuard aGuard; + if (pDocShell) + { + ScDocument& rDoc = pDocShell->GetDocument(); + if ( rDoc.GetAutoCalc() != bEnabled ) + { + rDoc.SetAutoCalc( bEnabled ); + pDocShell->SetDocumentModified(); + } + } + else + { + OSL_FAIL("no DocShell"); //! throw exception? + } +} + +// XProtectable + +void SAL_CALL ScModelObj::protect( const OUString& aPassword ) +{ + SolarMutexGuard aGuard; + // #i108245# if already protected, don't change anything + if ( pDocShell && !pDocShell->GetDocument().IsDocProtected() ) + { + pDocShell->GetDocFunc().Protect( TABLEID_DOC, aPassword ); + } +} + +void SAL_CALL ScModelObj::unprotect( const OUString& aPassword ) +{ + SolarMutexGuard aGuard; + if (pDocShell) + { + bool bDone = pDocShell->GetDocFunc().Unprotect( TABLEID_DOC, aPassword, true ); + if (!bDone) + throw lang::IllegalArgumentException(); + } +} + +sal_Bool SAL_CALL ScModelObj::isProtected() +{ + SolarMutexGuard aGuard; + if (pDocShell) + return pDocShell->GetDocument().IsDocProtected(); + + OSL_FAIL("no DocShell"); //! throw exception? + return false; +} + +// XDrawPagesSupplier + +uno::Reference<drawing::XDrawPages> SAL_CALL ScModelObj::getDrawPages() +{ + SolarMutexGuard aGuard; + if (pDocShell) + return new ScDrawPagesObj(pDocShell); + + OSL_FAIL("no DocShell"); //! throw exception? + return nullptr; +} + +// XGoalSeek + +sheet::GoalResult SAL_CALL ScModelObj::seekGoal( + const table::CellAddress& aFormulaPosition, + const table::CellAddress& aVariablePosition, + const OUString& aGoalValue ) +{ + SolarMutexGuard aGuard; + sheet::GoalResult aResult; + aResult.Divergence = DBL_MAX; // not found + if (pDocShell) + { + weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() ); + ScDocument& rDoc = pDocShell->GetDocument(); + double fValue = 0.0; + bool bFound = rDoc.Solver( + static_cast<SCCOL>(aFormulaPosition.Column), static_cast<SCROW>(aFormulaPosition.Row), aFormulaPosition.Sheet, + static_cast<SCCOL>(aVariablePosition.Column), static_cast<SCROW>(aVariablePosition.Row), aVariablePosition.Sheet, + aGoalValue, fValue ); + aResult.Result = fValue; + if (bFound) + aResult.Divergence = 0.0; //! this is a lie + } + return aResult; +} + +// XConsolidatable + +uno::Reference<sheet::XConsolidationDescriptor> SAL_CALL ScModelObj::createConsolidationDescriptor( + sal_Bool bEmpty ) +{ + SolarMutexGuard aGuard; + rtl::Reference<ScConsolidationDescriptor> pNew = new ScConsolidationDescriptor; + if ( pDocShell && !bEmpty ) + { + ScDocument& rDoc = pDocShell->GetDocument(); + const ScConsolidateParam* pParam = rDoc.GetConsolidateDlgData(); + if (pParam) + pNew->SetParam( *pParam ); + } + return pNew; +} + +void SAL_CALL ScModelObj::consolidate( + const uno::Reference<sheet::XConsolidationDescriptor>& xDescriptor ) +{ + SolarMutexGuard aGuard; + // in theory, this could also be a different object, so use only + // public XConsolidationDescriptor interface to copy the data into + // ScConsolidationDescriptor object + //! but if this already is ScConsolidationDescriptor, do it directly via getImplementation? + + rtl::Reference< ScConsolidationDescriptor > xImpl(new ScConsolidationDescriptor); + xImpl->setFunction( xDescriptor->getFunction() ); + xImpl->setSources( xDescriptor->getSources() ); + xImpl->setStartOutputPosition( xDescriptor->getStartOutputPosition() ); + xImpl->setUseColumnHeaders( xDescriptor->getUseColumnHeaders() ); + xImpl->setUseRowHeaders( xDescriptor->getUseRowHeaders() ); + xImpl->setInsertLinks( xDescriptor->getInsertLinks() ); + + if (pDocShell) + { + const ScConsolidateParam& rParam = xImpl->GetParam(); + pDocShell->DoConsolidate( rParam ); + pDocShell->GetDocument().SetConsolidateDlgData( std::unique_ptr<ScConsolidateParam>(new ScConsolidateParam(rParam)) ); + } +} + +// XDocumentAuditing + +void SAL_CALL ScModelObj::refreshArrows() +{ + SolarMutexGuard aGuard; + if (pDocShell) + pDocShell->GetDocFunc().DetectiveRefresh(); +} + +// XViewDataSupplier +uno::Reference< container::XIndexAccess > SAL_CALL ScModelObj::getViewData( ) +{ + uno::Reference < container::XIndexAccess > xRet( SfxBaseModel::getViewData() ); + + if( !xRet.is() ) + { + SolarMutexGuard aGuard; + if (pDocShell && pDocShell->GetCreateMode() == SfxObjectCreateMode::EMBEDDED) + { + rtl::Reference< comphelper::IndexedPropertyValuesContainer > xCont = new comphelper::IndexedPropertyValuesContainer(); + xRet = xCont; + + OUString sName; + pDocShell->GetDocument().GetName( pDocShell->GetDocument().GetVisibleTab(), sName ); + SCCOL nPosLeft = pDocShell->GetDocument().GetPosLeft(); + SCROW nPosTop = pDocShell->GetDocument().GetPosTop(); + uno::Sequence< beans::PropertyValue > aSeq{ + comphelper::makePropertyValue(SC_ACTIVETABLE, sName), + comphelper::makePropertyValue(SC_POSITIONLEFT, nPosLeft), + comphelper::makePropertyValue(SC_POSITIONTOP, nPosTop) + }; + xCont->insertByIndex( 0, uno::Any( aSeq ) ); + } + } + + return xRet; +} + +// XPropertySet (Doc-Options) +//! provide them also to the application? + +uno::Reference<beans::XPropertySetInfo> SAL_CALL ScModelObj::getPropertySetInfo() +{ + SolarMutexGuard aGuard; + static uno::Reference<beans::XPropertySetInfo> aRef( + new SfxItemPropertySetInfo( aPropSet.getPropertyMap() )); + return aRef; +} + +void SAL_CALL ScModelObj::setPropertyValue( + const OUString& aPropertyName, const uno::Any& aValue ) +{ + SolarMutexGuard aGuard; + + if (!pDocShell) + return; + + ScDocument& rDoc = pDocShell->GetDocument(); + const ScDocOptions& rOldOpt = rDoc.GetDocOptions(); + ScDocOptions aNewOpt = rOldOpt; + // Don't recalculate while loading XML, when the formula text is stored + // Recalculation after loading is handled separately. + bool bHardRecalc = !rDoc.IsImportingXML(); + + bool bOpt = ScDocOptionsHelper::setPropertyValue( aNewOpt, aPropSet.getPropertyMap(), aPropertyName, aValue ); + if (bOpt) + { + // done... + if ( aPropertyName == SC_UNO_IGNORECASE || + aPropertyName == SC_UNONAME_REGEXP || + aPropertyName == SC_UNONAME_WILDCARDS || + aPropertyName == SC_UNO_LOOKUPLABELS ) + bHardRecalc = false; + } + else if ( aPropertyName == SC_UNONAME_CLOCAL ) + { + lang::Locale aLocale; + if ( aValue >>= aLocale ) + { + LanguageType eLatin, eCjk, eCtl; + rDoc.GetLanguage( eLatin, eCjk, eCtl ); + eLatin = ScUnoConversion::GetLanguage(aLocale); + rDoc.SetLanguage( eLatin, eCjk, eCtl ); + } + } + else if ( aPropertyName == SC_UNO_CODENAME ) + { + OUString sCodeName; + if ( aValue >>= sCodeName ) + rDoc.SetCodeName( sCodeName ); + } + else if ( aPropertyName == SC_UNO_CJK_CLOCAL ) + { + lang::Locale aLocale; + if ( aValue >>= aLocale ) + { + LanguageType eLatin, eCjk, eCtl; + rDoc.GetLanguage( eLatin, eCjk, eCtl ); + eCjk = ScUnoConversion::GetLanguage(aLocale); + rDoc.SetLanguage( eLatin, eCjk, eCtl ); + } + } + else if ( aPropertyName == SC_UNO_CTL_CLOCAL ) + { + lang::Locale aLocale; + if ( aValue >>= aLocale ) + { + LanguageType eLatin, eCjk, eCtl; + rDoc.GetLanguage( eLatin, eCjk, eCtl ); + eCtl = ScUnoConversion::GetLanguage(aLocale); + rDoc.SetLanguage( eLatin, eCjk, eCtl ); + } + } + else if ( aPropertyName == SC_UNO_APPLYFMDES ) + { + // model is created if not there + ScDrawLayer* pModel = pDocShell->MakeDrawLayer(); + pModel->SetOpenInDesignMode( ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + + SfxBindings* pBindings = pDocShell->GetViewBindings(); + if (pBindings) + pBindings->Invalidate( SID_FM_OPEN_READONLY ); + } + else if ( aPropertyName == SC_UNO_AUTOCONTFOC ) + { + // model is created if not there + ScDrawLayer* pModel = pDocShell->MakeDrawLayer(); + pModel->SetAutoControlFocus( ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + + SfxBindings* pBindings = pDocShell->GetViewBindings(); + if (pBindings) + pBindings->Invalidate( SID_FM_AUTOCONTROLFOCUS ); + } + else if ( aPropertyName == SC_UNO_ISLOADED ) + { + pDocShell->SetEmpty( !ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + } + else if ( aPropertyName == SC_UNO_ISUNDOENABLED ) + { + bool bUndoEnabled = ScUnoHelpFunctions::GetBoolFromAny( aValue ); + rDoc.EnableUndo( bUndoEnabled ); + pDocShell->GetUndoManager()->SetMaxUndoActionCount( + bUndoEnabled + ? officecfg::Office::Common::Undo::Steps::get() : 0); + } + else if ( aPropertyName == SC_UNO_RECORDCHANGES ) + { + bool bRecordChangesEnabled = ScUnoHelpFunctions::GetBoolFromAny( aValue ); + + bool bChangeAllowed = true; + if (!bRecordChangesEnabled) + bChangeAllowed = !pDocShell->HasChangeRecordProtection(); + + if (bChangeAllowed) + pDocShell->SetChangeRecording(bRecordChangesEnabled); + } + else if ( aPropertyName == SC_UNO_ISADJUSTHEIGHTENABLED ) + { + if( ScUnoHelpFunctions::GetBoolFromAny( aValue ) ) + rDoc.UnlockAdjustHeight(); + else + rDoc.LockAdjustHeight(); + } + else if ( aPropertyName == SC_UNO_ISEXECUTELINKENABLED ) + { + rDoc.EnableExecuteLink( ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + } + else if ( aPropertyName == SC_UNO_ISCHANGEREADONLYENABLED ) + { + rDoc.EnableChangeReadOnly( ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + } + else if ( aPropertyName == "BuildId" ) + { + aValue >>= maBuildId; + } + else if ( aPropertyName == "SavedObject" ) // set from chart after saving + { + OUString aObjName; + aValue >>= aObjName; + if ( !aObjName.isEmpty() ) + rDoc.RestoreChartListener( aObjName ); + } + else if ( aPropertyName == SC_UNO_INTEROPGRABBAG ) + { + setGrabBagItem(aValue); + } + else if (aPropertyName == SC_UNO_THEME) + { + SdrModel& rSdrModel = getSdrModelFromUnoModel(); + uno::Reference<util::XTheme> xTheme; + if (aValue >>= xTheme) + { + auto& rUnoTheme = dynamic_cast<UnoTheme&>(*xTheme); + rSdrModel.setTheme(rUnoTheme.getTheme()); + } + } + + if ( aNewOpt != rOldOpt ) + { + rDoc.SetDocOptions( aNewOpt ); + //! Recalc only for options that need it? + if ( bHardRecalc ) + pDocShell->DoHardRecalc(); + pDocShell->SetDocumentModified(); + } +} + +uno::Any SAL_CALL ScModelObj::getPropertyValue( const OUString& aPropertyName ) +{ + SolarMutexGuard aGuard; + uno::Any aRet; + + if (pDocShell) + { + ScDocument& rDoc = pDocShell->GetDocument(); + const ScDocOptions& rOpt = rDoc.GetDocOptions(); + aRet = ScDocOptionsHelper::getPropertyValue( rOpt, aPropSet.getPropertyMap(), aPropertyName ); + if ( aRet.hasValue() ) + { + // done... + } + else if ( aPropertyName == SC_UNONAME_CLOCAL ) + { + LanguageType eLatin, eCjk, eCtl; + rDoc.GetLanguage( eLatin, eCjk, eCtl ); + + lang::Locale aLocale; + ScUnoConversion::FillLocale( aLocale, eLatin ); + aRet <<= aLocale; + } + else if ( aPropertyName == SC_UNO_CODENAME ) + { + aRet <<= rDoc.GetCodeName(); + } + + else if ( aPropertyName == SC_UNO_CJK_CLOCAL ) + { + LanguageType eLatin, eCjk, eCtl; + rDoc.GetLanguage( eLatin, eCjk, eCtl ); + + lang::Locale aLocale; + ScUnoConversion::FillLocale( aLocale, eCjk ); + aRet <<= aLocale; + } + else if ( aPropertyName == SC_UNO_CTL_CLOCAL ) + { + LanguageType eLatin, eCjk, eCtl; + rDoc.GetLanguage( eLatin, eCjk, eCtl ); + + lang::Locale aLocale; + ScUnoConversion::FillLocale( aLocale, eCtl ); + aRet <<= aLocale; + } + else if ( aPropertyName == SC_UNO_NAMEDRANGES ) + { + aRet <<= uno::Reference<sheet::XNamedRanges>(new ScGlobalNamedRangesObj( pDocShell )); + } + else if ( aPropertyName == SC_UNO_DATABASERNG ) + { + aRet <<= uno::Reference<sheet::XDatabaseRanges>(new ScDatabaseRangesObj( pDocShell )); + } + else if ( aPropertyName == SC_UNO_UNNAMEDDBRNG ) + { + aRet <<= uno::Reference<sheet::XUnnamedDatabaseRanges>(new ScUnnamedDatabaseRangesObj(pDocShell)); + } + else if ( aPropertyName == SC_UNO_COLLABELRNG ) + { + aRet <<= uno::Reference<sheet::XLabelRanges>(new ScLabelRangesObj( pDocShell, true )); + } + else if ( aPropertyName == SC_UNO_ROWLABELRNG ) + { + aRet <<= uno::Reference<sheet::XLabelRanges>(new ScLabelRangesObj( pDocShell, false )); + } + else if ( aPropertyName == SC_UNO_AREALINKS ) + { + aRet <<= uno::Reference<sheet::XAreaLinks>(new ScAreaLinksObj( pDocShell )); + } + else if ( aPropertyName == SC_UNO_DDELINKS ) + { + aRet <<= uno::Reference<container::XNameAccess>(new ScDDELinksObj( pDocShell )); + } + else if ( aPropertyName == SC_UNO_EXTERNALDOCLINKS ) + { + aRet <<= uno::Reference<sheet::XExternalDocLinks>(new ScExternalDocLinksObj(pDocShell)); + } + else if ( aPropertyName == SC_UNO_SHEETLINKS ) + { + aRet <<= uno::Reference<container::XNameAccess>(new ScSheetLinksObj( pDocShell )); + } + else if ( aPropertyName == SC_UNO_APPLYFMDES ) + { + // default for no model is TRUE + ScDrawLayer* pModel = rDoc.GetDrawLayer(); + bool bOpenInDesign = pModel == nullptr || pModel->GetOpenInDesignMode(); + aRet <<= bOpenInDesign; + } + else if ( aPropertyName == SC_UNO_AUTOCONTFOC ) + { + // default for no model is FALSE + ScDrawLayer* pModel = rDoc.GetDrawLayer(); + bool bAutoControlFocus = pModel && pModel->GetAutoControlFocus(); + aRet <<= bAutoControlFocus; + } + else if ( aPropertyName == SC_UNO_FORBIDDEN ) + { + aRet <<= uno::Reference<i18n::XForbiddenCharacters>(new ScForbiddenCharsObj( pDocShell )); + } + else if ( aPropertyName == SC_UNO_HASDRAWPAGES ) + { + aRet <<= (pDocShell->GetDocument().GetDrawLayer() != nullptr); + } + else if ( aPropertyName == SC_UNO_BASICLIBRARIES ) + { + aRet <<= pDocShell->GetBasicContainer(); + } + else if ( aPropertyName == SC_UNO_DIALOGLIBRARIES ) + { + aRet <<= pDocShell->GetDialogContainer(); + } + else if ( aPropertyName == SC_UNO_VBAGLOBNAME ) + { + /* #i111553# This property provides the name of the constant that + will be used to store this model in the global Basic manager. + That constant will be equivalent to 'ThisComponent' but for + each application, so e.g. a 'ThisExcelDoc' and a 'ThisWordDoc' + constant can co-exist, as required by VBA. */ + aRet <<= OUString( "ThisExcelDoc" ); + } + else if ( aPropertyName == SC_UNO_RUNTIMEUID ) + { + aRet <<= getRuntimeUID(); + } + else if ( aPropertyName == SC_UNO_HASVALIDSIGNATURES ) + { + aRet <<= hasValidSignatures(); + } + else if ( aPropertyName == SC_UNO_ISLOADED ) + { + aRet <<= !pDocShell->IsEmpty(); + } + else if ( aPropertyName == SC_UNO_ISUNDOENABLED ) + { + aRet <<= rDoc.IsUndoEnabled(); + } + else if ( aPropertyName == SC_UNO_RECORDCHANGES ) + { + aRet <<= pDocShell->IsChangeRecording(); + } + else if ( aPropertyName == SC_UNO_ISRECORDCHANGESPROTECTED ) + { + aRet <<= pDocShell->HasChangeRecordProtection(); + } + else if ( aPropertyName == SC_UNO_ISADJUSTHEIGHTENABLED ) + { + aRet <<= !( rDoc.IsAdjustHeightLocked() ); + } + else if ( aPropertyName == SC_UNO_ISEXECUTELINKENABLED ) + { + aRet <<= rDoc.IsExecuteLinkEnabled(); + } + else if ( aPropertyName == SC_UNO_ISCHANGEREADONLYENABLED ) + { + aRet <<= rDoc.IsChangeReadOnlyEnabled(); + } + else if ( aPropertyName == SC_UNO_REFERENCEDEVICE ) + { + rtl::Reference<VCLXDevice> pXDev = new VCLXDevice(); + pXDev->SetOutputDevice( rDoc.GetRefDevice() ); + aRet <<= uno::Reference< awt::XDevice >( pXDev ); + } + else if ( aPropertyName == "BuildId" ) + { + aRet <<= maBuildId; + } + else if ( aPropertyName == "InternalDocument" ) + { + aRet <<= (pDocShell->GetCreateMode() == SfxObjectCreateMode::INTERNAL); + } + else if ( aPropertyName == SC_UNO_INTEROPGRABBAG ) + { + getGrabBagItem(aRet); + } + else if (aPropertyName == SC_UNO_THEME) + { + SdrModel& rSdrModel = getSdrModelFromUnoModel(); + css::uno::Reference<css::util::XTheme> xTheme; + auto pTheme = rSdrModel.getTheme(); + if (pTheme) + xTheme = model::theme::createXTheme(pTheme); + aRet <<= xTheme; + } + } + + return aRet; +} + +SC_IMPL_DUMMY_PROPERTY_LISTENER( ScModelObj ) + +// XMultiServiceFactory + +css::uno::Reference<css::uno::XInterface> ScModelObj::create( + OUString const & aServiceSpecifier, + css::uno::Sequence<css::uno::Any> const * arguments) +{ + using ServiceType = ScServiceProvider::Type; + + uno::Reference<uno::XInterface> xRet; + ServiceType nType = ScServiceProvider::GetProviderType(aServiceSpecifier); + if ( nType != ServiceType::INVALID ) + { + // drawing layer tables must be kept as long as the model is alive + // return stored instance if already set + switch ( nType ) + { + case ServiceType::GRADTAB: xRet.set(xDrawGradTab); break; + case ServiceType::HATCHTAB: xRet.set(xDrawHatchTab); break; + case ServiceType::BITMAPTAB: xRet.set(xDrawBitmapTab); break; + case ServiceType::TRGRADTAB: xRet.set(xDrawTrGradTab); break; + case ServiceType::MARKERTAB: xRet.set(xDrawMarkerTab); break; + case ServiceType::DASHTAB: xRet.set(xDrawDashTab); break; + case ServiceType::CHDATAPROV: xRet.set(xChartDataProv); break; + case ServiceType::VBAOBJECTPROVIDER: xRet.set(xObjProvider); break; + default: break; + } + + // #i64497# If a chart is in a temporary document during clipboard paste, + // there should be no data provider, so that own data is used + bool bCreate = + ( nType != ServiceType::CHDATAPROV || + ( pDocShell->GetCreateMode() != SfxObjectCreateMode::INTERNAL )); + // this should never happen, i.e. the temporary document should never be + // loaded, because this unlinks the data + assert(bCreate); + + if ( !xRet.is() && bCreate ) + { + xRet.set(ScServiceProvider::MakeInstance( nType, pDocShell )); + + // store created instance + switch ( nType ) + { + case ServiceType::GRADTAB: xDrawGradTab.set(xRet); break; + case ServiceType::HATCHTAB: xDrawHatchTab.set(xRet); break; + case ServiceType::BITMAPTAB: xDrawBitmapTab.set(xRet); break; + case ServiceType::TRGRADTAB: xDrawTrGradTab.set(xRet); break; + case ServiceType::MARKERTAB: xDrawMarkerTab.set(xRet); break; + case ServiceType::DASHTAB: xDrawDashTab.set(xRet); break; + case ServiceType::CHDATAPROV: xChartDataProv.set(xRet); break; + case ServiceType::VBAOBJECTPROVIDER: xObjProvider.set(xRet); break; + default: break; + } + } + } + else + { + // we offload everything we don't know to SvxFmMSFactory, + // it'll throw exception if this isn't okay ... + + try + { + xRet = arguments == nullptr + ? SvxFmMSFactory::createInstance(aServiceSpecifier) + : SvxFmMSFactory::createInstanceWithArguments( + aServiceSpecifier, *arguments); + // extra block to force deletion of the temporary before ScShapeObj ctor (setDelegator) + } + catch ( lang::ServiceNotRegisteredException & ) + { + } + + // if the drawing factory created a shape, a ScShapeObj has to be used + // to support own properties like ImageMap: + + uno::Reference<drawing::XShape> xShape( xRet, uno::UNO_QUERY ); + if ( xShape.is() ) + { + xRet.clear(); // for aggregation, xShape must be the object's only ref + new ScShapeObj( xShape ); // aggregates object and modifies xShape + xRet.set(xShape); + } + } + return xRet; +} + +uno::Reference<uno::XInterface> SAL_CALL ScModelObj::createInstance( + const OUString& aServiceSpecifier ) +{ + SolarMutexGuard aGuard; + return create(aServiceSpecifier, nullptr); +} + +uno::Reference<uno::XInterface> SAL_CALL ScModelObj::createInstanceWithArguments( + const OUString& ServiceSpecifier, + const uno::Sequence<uno::Any>& aArgs ) +{ + //! distinguish between own services and those of drawing layer? + + SolarMutexGuard aGuard; + uno::Reference<uno::XInterface> xInt(create(ServiceSpecifier, &aArgs)); + + if ( aArgs.hasElements() ) + { + // used only for cell value binding so far - it can be initialized after creating + + uno::Reference<lang::XInitialization> xInit( xInt, uno::UNO_QUERY ); + if ( xInit.is() ) + xInit->initialize( aArgs ); + } + + return xInt; +} + +uno::Sequence<OUString> SAL_CALL ScModelObj::getAvailableServiceNames() +{ + SolarMutexGuard aGuard; + + return comphelper::concatSequences( ScServiceProvider::GetAllServiceNames(), + SvxFmMSFactory::getAvailableServiceNames() ); +} + +// XServiceInfo +OUString SAL_CALL ScModelObj::getImplementationName() +{ + return "ScModelObj"; + /* // Matching the .component information: + return OUString( "com.sun.star.comp.Calc.SpreadsheetDocument" ); + */ +} + +sal_Bool SAL_CALL ScModelObj::supportsService( const OUString& rServiceName ) +{ + return cppu::supportsService(this, rServiceName); +} + +uno::Sequence<OUString> SAL_CALL ScModelObj::getSupportedServiceNames() +{ + return {SCMODELOBJ_SERVICE, SCDOCSETTINGS_SERVICE, SCDOC_SERVICE}; +} + +// XUnoTunnel + +sal_Int64 SAL_CALL ScModelObj::getSomething( + const uno::Sequence<sal_Int8 >& rId ) +{ + if ( comphelper::isUnoTunnelId<ScModelObj>(rId) ) + { + return comphelper::getSomething_cast(this); + } + + if ( comphelper::isUnoTunnelId<SfxObjectShell>(rId) ) + { + return comphelper::getSomething_cast(pDocShell); + } + + // aggregated number formats supplier has XUnoTunnel, too + // interface from aggregated object must be obtained via queryAggregation + + sal_Int64 nRet = SfxBaseModel::getSomething( rId ); + if ( nRet ) + return nRet; + + if ( GetFormatter().is() ) + { + const uno::Type& rTunnelType = cppu::UnoType<lang::XUnoTunnel>::get(); + uno::Any aNumTunnel(xNumberAgg->queryAggregation(rTunnelType)); + if(auto xTunnelAgg = o3tl::tryAccess<uno::Reference<lang::XUnoTunnel>>( + aNumTunnel)) + { + return (*xTunnelAgg)->getSomething( rId ); + } + } + + return 0; +} + +const uno::Sequence<sal_Int8>& ScModelObj::getUnoTunnelId() +{ + static const comphelper::UnoIdInit theScModelObjUnoTunnelId; + return theScModelObjUnoTunnelId.getSeq(); +} + +// XChangesNotifier + +void ScModelObj::addChangesListener( const uno::Reference< util::XChangesListener >& aListener ) +{ + SolarMutexGuard aGuard; + maChangesListeners.addInterface( aListener ); +} + +void ScModelObj::removeChangesListener( const uno::Reference< util::XChangesListener >& aListener ) +{ + SolarMutexGuard aGuard; + maChangesListeners.removeInterface( aListener ); +} + +bool ScModelObj::HasChangesListeners() const +{ + if ( maChangesListeners.getLength() > 0 ) + return true; + + // "change" event set in any sheet? + return pDocShell && pDocShell->GetDocument().HasAnySheetEventScript(ScSheetEventId::CHANGE); +} + +namespace +{ + +void lcl_dataAreaInvalidation(ScModelObj* pModel, + const ScRangeList& rRanges, + bool bInvalidateDataArea, bool bExtendDataArea) +{ + size_t nRangeCount = rRanges.size(); + + for ( size_t nIndex = 0; nIndex < nRangeCount; ++nIndex ) + { + ScRange const & rRange = rRanges[ nIndex ]; + ScAddress const & rEnd = rRange.aEnd; + SCTAB nTab = rEnd.Tab(); + + bool bAreaExtended = false; + + if (bExtendDataArea) + { + const Size aCurrentDataArea = pModel->getDataArea( nTab ); + + SCCOL nLastCol = aCurrentDataArea.Width(); + SCROW nLastRow = aCurrentDataArea.Height(); + + bAreaExtended = rEnd.Col() > nLastCol || rEnd.Row() > nLastRow; + } + + bool bInvalidate = bAreaExtended || bInvalidateDataArea; + if ( bInvalidate ) + { + if ( comphelper::LibreOfficeKit::isActive() ) + SfxLokHelper::notifyPartSizeChangedAllViews( pModel, nTab ); + } + } +} + +}; + +void ScModelObj::NotifyChanges( const OUString& rOperation, const ScRangeList& rRanges, + const uno::Sequence< beans::PropertyValue >& rProperties ) +{ + OUString aOperation = rOperation; + bool bIsDataAreaInvalidateType = aOperation == "data-area-invalidate"; + bool bIsDataAreaExtendType = aOperation == "data-area-extend"; + + bool bInvalidateDataArea = bIsDataAreaInvalidateType + || HelperNotifyChanges::isDataAreaInvalidateType(aOperation); + bool bExtendDataArea = bIsDataAreaExtendType || aOperation == "cell-change"; + + if ( pDocShell ) + { + lcl_dataAreaInvalidation(this, rRanges, bInvalidateDataArea, bExtendDataArea); + + // check if we were called only to update data area + if (bIsDataAreaInvalidateType || bIsDataAreaExtendType) + return; + + // backward-compatibility Operation conversion + // FIXME: make sure it can be passed + if (rOperation == "delete-content" || rOperation == "undo" + || rOperation == "redo" || rOperation == "paste") + aOperation = "cell-change"; + } + + if ( pDocShell && HasChangesListeners() ) + { + util::ChangesEvent aEvent; + aEvent.Source.set(getXWeak()); + aEvent.Base <<= aEvent.Source; + + size_t nRangeCount = rRanges.size(); + aEvent.Changes.realloc( static_cast< sal_Int32 >( nRangeCount ) ); + auto pChanges = aEvent.Changes.getArray(); + for ( size_t nIndex = 0; nIndex < nRangeCount; ++nIndex ) + { + uno::Reference< table::XCellRange > xRangeObj; + + ScRange const & rRange = rRanges[ nIndex ]; + if ( rRange.aStart == rRange.aEnd ) + { + xRangeObj.set( new ScCellObj( pDocShell, rRange.aStart ) ); + } + else + { + xRangeObj.set( new ScCellRangeObj( pDocShell, rRange ) ); + } + + util::ElementChange& rChange = pChanges[ static_cast< sal_Int32 >( nIndex ) ]; + rChange.Accessor <<= aOperation; + rChange.Element <<= rProperties; + rChange.ReplacedElement <<= xRangeObj; + } + + ::comphelper::OInterfaceIteratorHelper3 aIter( maChangesListeners ); + while ( aIter.hasMoreElements() ) + { + try + { + aIter.next()->changesOccurred( aEvent ); + } + catch( uno::Exception& ) + { + } + } + } + + // handle sheet events + //! separate method with ScMarkData? Then change HasChangesListeners back. + if ( !(aOperation == "cell-change" && pDocShell) ) + return; + + ScMarkData aMarkData(pDocShell->GetDocument().GetSheetLimits()); + aMarkData.MarkFromRangeList( rRanges, false ); + ScDocument& rDoc = pDocShell->GetDocument(); + SCTAB nTabCount = rDoc.GetTableCount(); + for (const SCTAB& nTab : aMarkData) + { + if (nTab >= nTabCount) + break; + const ScSheetEvents* pEvents = rDoc.GetSheetEvents(nTab); + if (pEvents) + { + const OUString* pScript = pEvents->GetScript(ScSheetEventId::CHANGE); + if (pScript) + { + ScRangeList aTabRanges; // collect ranges on this sheet + size_t nRangeCount = rRanges.size(); + for ( size_t nIndex = 0; nIndex < nRangeCount; ++nIndex ) + { + ScRange const & rRange = rRanges[ nIndex ]; + if ( rRange.aStart.Tab() == nTab ) + aTabRanges.push_back( rRange ); + } + size_t nTabRangeCount = aTabRanges.size(); + if ( nTabRangeCount > 0 ) + { + uno::Reference<uno::XInterface> xTarget; + if ( nTabRangeCount == 1 ) + { + ScRange const & rRange = aTabRanges[ 0 ]; + if ( rRange.aStart == rRange.aEnd ) + xTarget.set( cppu::getXWeak( new ScCellObj( pDocShell, rRange.aStart ) ) ); + else + xTarget.set( cppu::getXWeak( new ScCellRangeObj( pDocShell, rRange ) ) ); + } + else + xTarget.set( cppu::getXWeak( new ScCellRangesObj( pDocShell, aTabRanges ) ) ); + + uno::Sequence<uno::Any> aParams{ uno::Any(xTarget) }; + + uno::Any aRet; + uno::Sequence<sal_Int16> aOutArgsIndex; + uno::Sequence<uno::Any> aOutArgs; + + /*ErrCode eRet =*/ pDocShell->CallXScript( *pScript, aParams, aRet, aOutArgsIndex, aOutArgs ); + } + } + } + } +} + +void ScModelObj::HandleCalculateEvents() +{ + if (!pDocShell) + return; + + ScDocument& rDoc = pDocShell->GetDocument(); + // don't call events before the document is visible + // (might also set a flag on SfxEventHintId::LoadFinished and only disable while loading) + if ( rDoc.IsDocVisible() ) + { + SCTAB nTabCount = rDoc.GetTableCount(); + for (SCTAB nTab = 0; nTab < nTabCount; nTab++) + { + if (rDoc.HasCalcNotification(nTab)) + { + if (const ScSheetEvents* pEvents = rDoc.GetSheetEvents( nTab )) + { + if (const OUString* pScript = pEvents->GetScript(ScSheetEventId::CALCULATE)) + { + uno::Any aRet; + uno::Sequence<uno::Any> aParams; + uno::Sequence<sal_Int16> aOutArgsIndex; + uno::Sequence<uno::Any> aOutArgs; + pDocShell->CallXScript( *pScript, aParams, aRet, aOutArgsIndex, aOutArgs ); + } + } + + try + { + uno::Reference< script::vba::XVBAEventProcessor > xVbaEvents( rDoc.GetVbaEventProcessor(), uno::UNO_SET_THROW ); + uno::Sequence< uno::Any > aArgs{ uno::Any(nTab) }; + xVbaEvents->processVbaEvent( ScSheetEvents::GetVbaSheetEventId( ScSheetEventId::CALCULATE ), aArgs ); + } + catch( uno::Exception& ) + { + } + } + } + } + rDoc.ResetCalcNotifications(); +} + +// XOpenCLSelection + +sal_Bool ScModelObj::isOpenCLEnabled() +{ + return ScCalcConfig::isOpenCLEnabled(); +} + +void ScModelObj::enableOpenCL(sal_Bool bEnable) +{ + if (ScCalcConfig::isOpenCLEnabled() == static_cast<bool>(bEnable)) + return; + if (ScCalcConfig::getForceCalculationType() != ForceCalculationNone) + return; + + std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create()); + officecfg::Office::Common::Misc::UseOpenCL::set(bEnable, batch); + batch->commit(); + + ScCalcConfig aConfig = ScInterpreter::GetGlobalConfig(); + if (bEnable) + aConfig.setOpenCLConfigToDefault(); + ScInterpreter::SetGlobalConfig(aConfig); + +#if HAVE_FEATURE_OPENCL + sc::FormulaGroupInterpreter::switchOpenCLDevice(u"", true); +#endif + + ScDocument* pDoc = GetDocument(); + pDoc->CheckVectorizationState(); + +} + +void ScModelObj::enableAutomaticDeviceSelection(sal_Bool bForce) +{ + ScCalcConfig aConfig = ScInterpreter::GetGlobalConfig(); + aConfig.mbOpenCLAutoSelect = true; + ScInterpreter::SetGlobalConfig(aConfig); + ScFormulaOptions aOptions = SC_MOD()->GetFormulaOptions(); + aOptions.SetCalcConfig(aConfig); + SC_MOD()->SetFormulaOptions(aOptions); +#if !HAVE_FEATURE_OPENCL + (void) bForce; +#else + sc::FormulaGroupInterpreter::switchOpenCLDevice(u"", true, bForce); +#endif +} + +void ScModelObj::disableAutomaticDeviceSelection() +{ + ScCalcConfig aConfig = ScInterpreter::GetGlobalConfig(); + aConfig.mbOpenCLAutoSelect = false; + ScInterpreter::SetGlobalConfig(aConfig); + ScFormulaOptions aOptions = SC_MOD()->GetFormulaOptions(); + aOptions.SetCalcConfig(aConfig); + SC_MOD()->SetFormulaOptions(aOptions); +} + +void ScModelObj::selectOpenCLDevice( sal_Int32 nPlatform, sal_Int32 nDevice ) +{ + if(nPlatform < 0 || nDevice < 0) + throw uno::RuntimeException(); + +#if !HAVE_FEATURE_OPENCL + throw uno::RuntimeException(); +#else + std::vector<OpenCLPlatformInfo> aPlatformInfo; + sc::FormulaGroupInterpreter::fillOpenCLInfo(aPlatformInfo); + if(o3tl::make_unsigned(nPlatform) >= aPlatformInfo.size()) + throw uno::RuntimeException(); + + if(o3tl::make_unsigned(nDevice) >= aPlatformInfo[nPlatform].maDevices.size()) + throw uno::RuntimeException(); + + OUString aDeviceString = aPlatformInfo[nPlatform].maVendor + " " + aPlatformInfo[nPlatform].maDevices[nDevice].maName; + sc::FormulaGroupInterpreter::switchOpenCLDevice(aDeviceString, false); +#endif +} + +sal_Int32 ScModelObj::getPlatformID() +{ +#if !HAVE_FEATURE_OPENCL + return -1; +#else + sal_Int32 nPlatformId; + sal_Int32 nDeviceId; + sc::FormulaGroupInterpreter::getOpenCLDeviceInfo(nDeviceId, nPlatformId); + return nPlatformId; +#endif +} + +sal_Int32 ScModelObj::getDeviceID() +{ +#if !HAVE_FEATURE_OPENCL + return -1; +#else + sal_Int32 nPlatformId; + sal_Int32 nDeviceId; + sc::FormulaGroupInterpreter::getOpenCLDeviceInfo(nDeviceId, nPlatformId); + return nDeviceId; +#endif +} + +uno::Sequence< sheet::opencl::OpenCLPlatform > ScModelObj::getOpenCLPlatforms() +{ +#if !HAVE_FEATURE_OPENCL + return uno::Sequence<sheet::opencl::OpenCLPlatform>(); +#else + std::vector<OpenCLPlatformInfo> aPlatformInfo; + sc::FormulaGroupInterpreter::fillOpenCLInfo(aPlatformInfo); + + uno::Sequence<sheet::opencl::OpenCLPlatform> aRet(aPlatformInfo.size()); + auto aRetRange = asNonConstRange(aRet); + for(size_t i = 0; i < aPlatformInfo.size(); ++i) + { + aRetRange[i].Name = aPlatformInfo[i].maName; + aRetRange[i].Vendor = aPlatformInfo[i].maVendor; + + aRetRange[i].Devices.realloc(aPlatformInfo[i].maDevices.size()); + auto pDevices = aRetRange[i].Devices.getArray(); + for(size_t j = 0; j < aPlatformInfo[i].maDevices.size(); ++j) + { + const OpenCLDeviceInfo& rDevice = aPlatformInfo[i].maDevices[j]; + pDevices[j].Name = rDevice.maName; + pDevices[j].Vendor = rDevice.maVendor; + pDevices[j].Driver = rDevice.maDriver; + } + } + + return aRet; +#endif +} + +namespace { + +/// @throws css::uno::RuntimeException +void setOpcodeSubsetTest(bool bFlag) +{ + std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create()); + officecfg::Office::Calc::Formula::Calculation::OpenCLSubsetOnly::set(bFlag, batch); + batch->commit(); +} + +} + +void ScModelObj::enableOpcodeSubsetTest() +{ + setOpcodeSubsetTest(true); +} + +void ScModelObj::disableOpcodeSubsetTest() +{ + setOpcodeSubsetTest(false); +} + +sal_Bool ScModelObj::isOpcodeSubsetTested() +{ + return officecfg::Office::Calc::Formula::Calculation::OpenCLSubsetOnly::get(); +} + +void ScModelObj::setFormulaCellNumberLimit( sal_Int32 number ) +{ + std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create()); + officecfg::Office::Calc::Formula::Calculation::OpenCLMinimumDataSize::set(number, batch); + batch->commit(); +} + +sal_Int32 ScModelObj::getFormulaCellNumberLimit() +{ + return officecfg::Office::Calc::Formula::Calculation::OpenCLMinimumDataSize::get(); +} + +ScDrawPagesObj::ScDrawPagesObj(ScDocShell* pDocSh) : + pDocShell( pDocSh ) +{ + pDocShell->GetDocument().AddUnoObject(*this); +} + +ScDrawPagesObj::~ScDrawPagesObj() +{ + SolarMutexGuard g; + + if (pDocShell) + pDocShell->GetDocument().RemoveUnoObject(*this); +} + +void ScDrawPagesObj::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + // we don't care about update of references here + + if ( rHint.GetId() == SfxHintId::Dying ) + { + pDocShell = nullptr; // became invalid + } +} + +uno::Reference<drawing::XDrawPage> ScDrawPagesObj::GetObjectByIndex_Impl(sal_Int32 nIndex) const +{ + if (pDocShell) + { + ScDrawLayer* pDrawLayer = pDocShell->MakeDrawLayer(); + OSL_ENSURE(pDrawLayer,"Cannot create Draw-Layer"); + if ( pDrawLayer && nIndex >= 0 && nIndex < pDocShell->GetDocument().GetTableCount() ) + { + SdrPage* pPage = pDrawLayer->GetPage(static_cast<sal_uInt16>(nIndex)); + OSL_ENSURE(pPage,"Draw-Page not found"); + if (pPage) + { + return uno::Reference<drawing::XDrawPage> (pPage->getUnoPage(), uno::UNO_QUERY); + } + } + } + return nullptr; +} + +// XDrawPages + +uno::Reference<drawing::XDrawPage> SAL_CALL ScDrawPagesObj::insertNewByIndex( sal_Int32 nPos ) +{ + SolarMutexGuard aGuard; + uno::Reference<drawing::XDrawPage> xRet; + if (pDocShell) + { + OUString aNewName; + pDocShell->GetDocument().CreateValidTabName(aNewName); + if ( pDocShell->GetDocFunc().InsertTable( static_cast<SCTAB>(nPos), + aNewName, true, true ) ) + xRet.set(GetObjectByIndex_Impl( nPos )); + } + return xRet; +} + +void SAL_CALL ScDrawPagesObj::remove( const uno::Reference<drawing::XDrawPage>& xPage ) +{ + SolarMutexGuard aGuard; + SvxDrawPage* pImp = comphelper::getFromUnoTunnel<SvxDrawPage>( xPage ); + if ( pDocShell && pImp ) + { + SdrPage* pPage = pImp->GetSdrPage(); + if (pPage) + { + SCTAB nPageNum = static_cast<SCTAB>(pPage->GetPageNum()); + pDocShell->GetDocFunc().DeleteTable( nPageNum, true ); + } + } +} + +// XIndexAccess + +sal_Int32 SAL_CALL ScDrawPagesObj::getCount() +{ + SolarMutexGuard aGuard; + if (pDocShell) + return pDocShell->GetDocument().GetTableCount(); + return 0; +} + +uno::Any SAL_CALL ScDrawPagesObj::getByIndex( sal_Int32 nIndex ) +{ + SolarMutexGuard aGuard; + uno::Reference<drawing::XDrawPage> xPage(GetObjectByIndex_Impl(nIndex)); + if (!xPage.is()) + throw lang::IndexOutOfBoundsException(); + + return uno::Any(xPage); +} + +uno::Type SAL_CALL ScDrawPagesObj::getElementType() +{ + return cppu::UnoType<drawing::XDrawPage>::get(); +} + +sal_Bool SAL_CALL ScDrawPagesObj::hasElements() +{ + SolarMutexGuard aGuard; + return ( getCount() != 0 ); +} + +ScTableSheetsObj::ScTableSheetsObj(ScDocShell* pDocSh) : + pDocShell( pDocSh ) +{ + pDocShell->GetDocument().AddUnoObject(*this); +} + +ScTableSheetsObj::~ScTableSheetsObj() +{ + SolarMutexGuard g; + + if (pDocShell) + pDocShell->GetDocument().RemoveUnoObject(*this); +} + +void ScTableSheetsObj::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + // we don't care about update of references here + + if ( rHint.GetId() == SfxHintId::Dying ) + { + pDocShell = nullptr; // became invalid + } +} + +// XSpreadsheets + +rtl::Reference<ScTableSheetObj> ScTableSheetsObj::GetObjectByIndex_Impl(sal_Int32 nIndex) const +{ + if ( pDocShell && nIndex >= 0 && nIndex < pDocShell->GetDocument().GetTableCount() ) + return new ScTableSheetObj( pDocShell, static_cast<SCTAB>(nIndex) ); + + return nullptr; +} + +rtl::Reference<ScTableSheetObj> ScTableSheetsObj::GetObjectByName_Impl(const OUString& aName) const +{ + if (pDocShell) + { + SCTAB nIndex; + if ( pDocShell->GetDocument().GetTable( aName, nIndex ) ) + return new ScTableSheetObj( pDocShell, nIndex ); + } + return nullptr; +} + +void SAL_CALL ScTableSheetsObj::insertNewByName( const OUString& aName, sal_Int16 nPosition ) +{ + SolarMutexGuard aGuard; + bool bDone = false; + if (pDocShell) + { + bDone = pDocShell->GetDocFunc().InsertTable( nPosition, aName, true, true ); + } + if (!bDone) + throw uno::RuntimeException("ScTableSheetsObj::insertNewByName(): Illegal object name or bad index. Duplicate name?"); // no other exceptions specified +} + +void SAL_CALL ScTableSheetsObj::moveByName( const OUString& aName, sal_Int16 nDestination ) +{ + SolarMutexGuard aGuard; + bool bDone = false; + if (pDocShell) + { + SCTAB nSource; + if ( pDocShell->GetDocument().GetTable( aName, nSource ) ) + bDone = pDocShell->MoveTable( nSource, nDestination, false, true ); + } + if (!bDone) + throw uno::RuntimeException(); // no other exceptions specified +} + +void SAL_CALL ScTableSheetsObj::copyByName( const OUString& aName, + const OUString& aCopy, sal_Int16 nDestination ) +{ + SolarMutexGuard aGuard; + bool bDone = false; + if (pDocShell) + { + SCTAB nSource; + if ( pDocShell->GetDocument().GetTable( aName, nSource ) ) + { + bDone = pDocShell->MoveTable( nSource, nDestination, true, true ); + if (bDone) + { + // #i92477# any index past the last sheet means "append" in MoveTable + SCTAB nResultTab = static_cast<SCTAB>(nDestination); + SCTAB nTabCount = pDocShell->GetDocument().GetTableCount(); // count after copying + if (nResultTab >= nTabCount) + nResultTab = nTabCount - 1; + + bDone = pDocShell->GetDocFunc().RenameTable( nResultTab, aCopy, + true, true ); + } + } + } + if (!bDone) + throw uno::RuntimeException("ScTableSheetsObj::copyByName(): Illegal object name or bad index. Duplicate name?"); // no other exceptions specified +} + +void SAL_CALL ScTableSheetsObj::insertByName( const OUString& aName, const uno::Any& aElement ) +{ + SolarMutexGuard aGuard; + bool bDone = false; + bool bIllArg = false; + + //! Type of aElement can be some specific interface instead of XInterface + + if ( pDocShell ) + { + uno::Reference<uno::XInterface> xInterface(aElement, uno::UNO_QUERY); + if ( xInterface.is() ) + { + ScTableSheetObj* pSheetObj = dynamic_cast<ScTableSheetObj*>( xInterface.get() ); + if ( pSheetObj && !pSheetObj->GetDocShell() ) // not inserted yet? + { + ScDocument& rDoc = pDocShell->GetDocument(); + SCTAB nDummy; + if ( rDoc.GetTable( aName, nDummy ) ) + { + // name already exists + throw container::ElementExistException(); + } + SCTAB nPosition = rDoc.GetTableCount(); + bDone = pDocShell->GetDocFunc().InsertTable( nPosition, aName, + true, true ); + if (bDone) + pSheetObj->InitInsertSheet( pDocShell, nPosition ); + // set document and new range in the object + } + else + bIllArg = true; + } + else + bIllArg = true; + } + + if (!bDone) + { + if (bIllArg) + throw lang::IllegalArgumentException(); + else + throw uno::RuntimeException(); // ElementExistException is handled above + } +} + +void SAL_CALL ScTableSheetsObj::replaceByName( const OUString& aName, const uno::Any& aElement ) +{ + SolarMutexGuard aGuard; + bool bDone = false; + bool bIllArg = false; + + //! Type of aElement can be some specific interface instead of XInterface + + if ( pDocShell ) + { + uno::Reference<uno::XInterface> xInterface(aElement, uno::UNO_QUERY); + if ( xInterface.is() ) + { + ScTableSheetObj* pSheetObj = dynamic_cast<ScTableSheetObj*>( xInterface.get() ); + if ( pSheetObj && !pSheetObj->GetDocShell() ) // not inserted yet? + { + SCTAB nPosition; + if ( !pDocShell->GetDocument().GetTable( aName, nPosition ) ) + { + // not found + throw container::NoSuchElementException(); + } + + if ( pDocShell->GetDocFunc().DeleteTable( nPosition, true ) ) + { + // InsertTable can't really go wrong now + bDone = pDocShell->GetDocFunc().InsertTable( nPosition, aName, true, true ); + if (bDone) + pSheetObj->InitInsertSheet( pDocShell, nPosition ); + } + + } + else + bIllArg = true; + } + else + bIllArg = true; + } + + if (!bDone) + { + if (bIllArg) + throw lang::IllegalArgumentException(); + else + throw uno::RuntimeException(); // NoSuchElementException is handled above + } +} + +void SAL_CALL ScTableSheetsObj::removeByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + bool bDone = false; + if (pDocShell) + { + SCTAB nIndex; + if ( !pDocShell->GetDocument().GetTable( aName, nIndex ) ) + throw container::NoSuchElementException(); // not found + bDone = pDocShell->GetDocFunc().DeleteTable( nIndex, true ); + } + + if (!bDone) + throw uno::RuntimeException(); // NoSuchElementException is handled above +} + +sal_Int32 ScTableSheetsObj::importSheet( + const uno::Reference < sheet::XSpreadsheetDocument > & xDocSrc, + const OUString& srcName, const sal_Int32 nDestPosition ) +{ + //pDocShell is the destination + ScDocument& rDocDest = pDocShell->GetDocument(); + + // Source document docShell + if ( !xDocSrc.is() ) + throw uno::RuntimeException(); + ScModelObj* pObj = comphelper::getFromUnoTunnel<ScModelObj>(xDocSrc); + ScDocShell* pDocShellSrc = static_cast<ScDocShell*>(pObj->GetEmbeddedObject()); + + // SourceSheet Position and does srcName exists ? + SCTAB nIndexSrc; + if ( !pDocShellSrc->GetDocument().GetTable( srcName, nIndexSrc ) ) + throw lang::IllegalArgumentException(); + + // Check the validity of destination index. + SCTAB nCount = rDocDest.GetTableCount(); + SCTAB nIndexDest = static_cast<SCTAB>(nDestPosition); + if (nIndexDest > nCount || nIndexDest < 0) + throw lang::IndexOutOfBoundsException(); + + // Transfer Tab + pDocShell->TransferTab( + *pDocShellSrc, nIndexSrc, nIndexDest, true/*bInsertNew*/, true/*bNotifyAndPaint*/ ); + + return nIndexDest; +} + +// XCellRangesAccess + +uno::Reference< table::XCell > SAL_CALL ScTableSheetsObj::getCellByPosition( sal_Int32 nColumn, sal_Int32 nRow, sal_Int32 nSheet ) +{ + SolarMutexGuard aGuard; + rtl::Reference<ScTableSheetObj> xSheet = GetObjectByIndex_Impl(static_cast<sal_uInt16>(nSheet)); + if (! xSheet.is()) + throw lang::IndexOutOfBoundsException(); + + return xSheet->getCellByPosition(nColumn, nRow); +} + +uno::Reference< table::XCellRange > SAL_CALL ScTableSheetsObj::getCellRangeByPosition( sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom, sal_Int32 nSheet ) +{ + SolarMutexGuard aGuard; + rtl::Reference<ScTableSheetObj> xSheet = GetObjectByIndex_Impl(static_cast<sal_uInt16>(nSheet)); + if (! xSheet.is()) + throw lang::IndexOutOfBoundsException(); + + return xSheet->getCellRangeByPosition(nLeft, nTop, nRight, nBottom); +} + +uno::Sequence < uno::Reference< table::XCellRange > > SAL_CALL ScTableSheetsObj::getCellRangesByName( const OUString& aRange ) +{ + SolarMutexGuard aGuard; + uno::Sequence < uno::Reference < table::XCellRange > > xRet; + + ScRangeList aRangeList; + ScDocument& rDoc = pDocShell->GetDocument(); + if (!ScRangeStringConverter::GetRangeListFromString( aRangeList, aRange, rDoc, ::formula::FormulaGrammar::CONV_OOO, ';' )) + throw lang::IllegalArgumentException(); + + size_t nCount = aRangeList.size(); + if (!nCount) + throw lang::IllegalArgumentException(); + + xRet.realloc(nCount); + auto pRet = xRet.getArray(); + for( size_t nIndex = 0; nIndex < nCount; nIndex++ ) + { + const ScRange & rRange = aRangeList[ nIndex ]; + pRet[nIndex] = new ScCellRangeObj(pDocShell, rRange); + } + + return xRet; +} + +// XEnumerationAccess + +uno::Reference<container::XEnumeration> SAL_CALL ScTableSheetsObj::createEnumeration() +{ + SolarMutexGuard aGuard; + return new ScIndexEnumeration(this, "com.sun.star.sheet.SpreadsheetsEnumeration"); +} + +// XIndexAccess + +sal_Int32 SAL_CALL ScTableSheetsObj::getCount() +{ + SolarMutexGuard aGuard; + if (pDocShell) + return pDocShell->GetDocument().GetTableCount(); + return 0; +} + +uno::Any SAL_CALL ScTableSheetsObj::getByIndex( sal_Int32 nIndex ) +{ + SolarMutexGuard aGuard; + uno::Reference<sheet::XSpreadsheet> xSheet(GetObjectByIndex_Impl(nIndex)); + if (!xSheet.is()) + throw lang::IndexOutOfBoundsException(); + + return uno::Any(xSheet); + +// return uno::Any(); +} + +uno::Type SAL_CALL ScTableSheetsObj::getElementType() +{ + return cppu::UnoType<sheet::XSpreadsheet>::get(); +} + +sal_Bool SAL_CALL ScTableSheetsObj::hasElements() +{ + SolarMutexGuard aGuard; + return ( getCount() != 0 ); +} + +// XNameAccess + +uno::Any SAL_CALL ScTableSheetsObj::getByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + uno::Reference<sheet::XSpreadsheet> xSheet(GetObjectByName_Impl(aName)); + if (!xSheet.is()) + throw container::NoSuchElementException(); + + return uno::Any(xSheet); +} + +uno::Sequence<OUString> SAL_CALL ScTableSheetsObj::getElementNames() +{ + SolarMutexGuard aGuard; + if (pDocShell) + { + ScDocument& rDoc = pDocShell->GetDocument(); + SCTAB nCount = rDoc.GetTableCount(); + OUString aName; + uno::Sequence<OUString> aSeq(nCount); + OUString* pAry = aSeq.getArray(); + for (SCTAB i=0; i<nCount; i++) + { + rDoc.GetName( i, aName ); + pAry[i] = aName; + } + return aSeq; + } + return uno::Sequence<OUString>(); +} + +sal_Bool SAL_CALL ScTableSheetsObj::hasByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + if (pDocShell) + { + SCTAB nIndex; + if ( pDocShell->GetDocument().GetTable( aName, nIndex ) ) + return true; + } + return false; +} + +ScTableColumnsObj::ScTableColumnsObj(ScDocShell* pDocSh, SCTAB nT, SCCOL nSC, SCCOL nEC) : + pDocShell( pDocSh ), + nTab ( nT ), + nStartCol( nSC ), + nEndCol ( nEC ) +{ + pDocShell->GetDocument().AddUnoObject(*this); +} + +ScTableColumnsObj::~ScTableColumnsObj() +{ + SolarMutexGuard g; + + if (pDocShell) + pDocShell->GetDocument().RemoveUnoObject(*this); +} + +void ScTableColumnsObj::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + if ( dynamic_cast<const ScUpdateRefHint*>(&rHint) ) + { + //! update of references for sheet and its start/end + } + else if ( rHint.GetId() == SfxHintId::Dying ) + { + pDocShell = nullptr; // became invalid + } +} + +// XTableColumns + +rtl::Reference<ScTableColumnObj> ScTableColumnsObj::GetObjectByIndex_Impl(sal_Int32 nIndex) const +{ + SCCOL nCol = static_cast<SCCOL>(nIndex) + nStartCol; + if ( pDocShell && nCol <= nEndCol ) + return new ScTableColumnObj( pDocShell, nCol, nTab ); + + return nullptr; // wrong index +} + +rtl::Reference<ScTableColumnObj> ScTableColumnsObj::GetObjectByName_Impl(std::u16string_view aName) const +{ + SCCOL nCol = 0; + if (pDocShell && ::AlphaToCol(pDocShell->GetDocument(), nCol, aName)) + if (nCol >= nStartCol && nCol <= nEndCol) + return new ScTableColumnObj( pDocShell, nCol, nTab ); + + return nullptr; +} + +void SAL_CALL ScTableColumnsObj::insertByIndex( sal_Int32 nPosition, sal_Int32 nCount ) +{ + SolarMutexGuard aGuard; + bool bDone = false; + if ( pDocShell ) + { + const ScDocument& rDoc = pDocShell->GetDocument(); + if ( nCount > 0 && nPosition >= 0 && nStartCol+nPosition <= nEndCol && + nStartCol+nPosition+nCount-1 <= rDoc.MaxCol() ) + { + ScRange aRange( static_cast<SCCOL>(nStartCol+nPosition), 0, nTab, + static_cast<SCCOL>(nStartCol+nPosition+nCount-1), rDoc.MaxRow(), nTab ); + bDone = pDocShell->GetDocFunc().InsertCells( aRange, nullptr, INS_INSCOLS_BEFORE, true, true ); + } + } + if (!bDone) + throw uno::RuntimeException(); // no other exceptions specified +} + +void SAL_CALL ScTableColumnsObj::removeByIndex( sal_Int32 nIndex, sal_Int32 nCount ) +{ + SolarMutexGuard aGuard; + bool bDone = false; + // the range to be deleted has to lie within the object + if ( pDocShell ) + { + const ScDocument& rDoc = pDocShell->GetDocument(); + if ( nCount > 0 && nIndex >= 0 && nStartCol+nIndex+nCount-1 <= nEndCol ) + { + ScRange aRange( static_cast<SCCOL>(nStartCol+nIndex), 0, nTab, + static_cast<SCCOL>(nStartCol+nIndex+nCount-1), rDoc.MaxRow(), nTab ); + bDone = pDocShell->GetDocFunc().DeleteCells( aRange, nullptr, DelCellCmd::Cols, true ); + } + } + if (!bDone) + throw uno::RuntimeException(); // no other exceptions specified +} + +// XEnumerationAccess + +uno::Reference<container::XEnumeration> SAL_CALL ScTableColumnsObj::createEnumeration() +{ + SolarMutexGuard aGuard; + return new ScIndexEnumeration(this, "com.sun.star.table.TableColumnsEnumeration"); +} + +// XIndexAccess + +sal_Int32 SAL_CALL ScTableColumnsObj::getCount() +{ + SolarMutexGuard aGuard; + return nEndCol - nStartCol + 1; +} + +uno::Any SAL_CALL ScTableColumnsObj::getByIndex( sal_Int32 nIndex ) +{ + SolarMutexGuard aGuard; + uno::Reference<table::XCellRange> xColumn(GetObjectByIndex_Impl(nIndex)); + if (!xColumn.is()) + throw lang::IndexOutOfBoundsException(); + + return uno::Any(xColumn); + +} + +uno::Type SAL_CALL ScTableColumnsObj::getElementType() +{ + return cppu::UnoType<table::XCellRange>::get(); +} + +sal_Bool SAL_CALL ScTableColumnsObj::hasElements() +{ + SolarMutexGuard aGuard; + return ( getCount() != 0 ); +} + +uno::Any SAL_CALL ScTableColumnsObj::getByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + uno::Reference<table::XCellRange> xColumn(GetObjectByName_Impl(aName)); + if (!xColumn.is()) + throw container::NoSuchElementException(); + + return uno::Any(xColumn); +} + +uno::Sequence<OUString> SAL_CALL ScTableColumnsObj::getElementNames() +{ + SolarMutexGuard aGuard; + SCCOL nCount = nEndCol - nStartCol + 1; + uno::Sequence<OUString> aSeq(nCount); + OUString* pAry = aSeq.getArray(); + for (SCCOL i=0; i<nCount; i++) + pAry[i] = ::ScColToAlpha( nStartCol + i ); + + return aSeq; +} + +sal_Bool SAL_CALL ScTableColumnsObj::hasByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + SCCOL nCol = 0; + if (pDocShell && ::AlphaToCol(pDocShell->GetDocument(), nCol, aName)) + if (nCol >= nStartCol && nCol <= nEndCol) + return true; + + return false; // not found +} + +// XPropertySet + +uno::Reference<beans::XPropertySetInfo> SAL_CALL ScTableColumnsObj::getPropertySetInfo() +{ + static uno::Reference<beans::XPropertySetInfo> aRef( + new SfxItemPropertySetInfo( lcl_GetColumnsPropertyMap() )); + return aRef; +} + +void SAL_CALL ScTableColumnsObj::setPropertyValue( + const OUString& aPropertyName, const uno::Any& aValue ) +{ + SolarMutexGuard aGuard; + if (!pDocShell) + throw uno::RuntimeException(); + + std::vector<sc::ColRowSpan> aColArr(1, sc::ColRowSpan(nStartCol,nEndCol)); + ScDocFunc& rFunc = pDocShell->GetDocFunc(); + + if ( aPropertyName == SC_UNONAME_CELLWID ) + { + sal_Int32 nNewWidth = 0; + if ( aValue >>= nNewWidth ) + rFunc.SetWidthOrHeight( + true, aColArr, nTab, SC_SIZE_ORIGINAL, o3tl::toTwips(nNewWidth, o3tl::Length::mm100), true, true); + } + else if ( aPropertyName == SC_UNONAME_CELLVIS ) + { + bool bVis = ScUnoHelpFunctions::GetBoolFromAny( aValue ); + ScSizeMode eMode = bVis ? SC_SIZE_SHOW : SC_SIZE_DIRECT; + rFunc.SetWidthOrHeight(true, aColArr, nTab, eMode, 0, true, true); + // SC_SIZE_DIRECT with size 0: hide + } + else if ( aPropertyName == SC_UNONAME_OWIDTH ) + { + bool bOpt = ScUnoHelpFunctions::GetBoolFromAny( aValue ); + if (bOpt) + rFunc.SetWidthOrHeight( + true, aColArr, nTab, SC_SIZE_OPTIMAL, STD_EXTRA_WIDTH, true, true); + // sal_False for columns currently has no effect + } + else if ( aPropertyName == SC_UNONAME_NEWPAGE || aPropertyName == SC_UNONAME_MANPAGE ) + { + //! single function to set/remove all breaks? + bool bSet = ScUnoHelpFunctions::GetBoolFromAny( aValue ); + for (SCCOL nCol=nStartCol; nCol<=nEndCol; nCol++) + if (bSet) + rFunc.InsertPageBreak( true, ScAddress(nCol,0,nTab), true, true ); + else + rFunc.RemovePageBreak( true, ScAddress(nCol,0,nTab), true, true ); + } +} + +uno::Any SAL_CALL ScTableColumnsObj::getPropertyValue( const OUString& aPropertyName ) +{ + SolarMutexGuard aGuard; + if (!pDocShell) + throw uno::RuntimeException(); + + ScDocument& rDoc = pDocShell->GetDocument(); + uno::Any aAny; + + //! loop over all columns for current state? + + if ( aPropertyName == SC_UNONAME_CELLWID ) + { + // for hidden column, return original height + sal_uInt16 nWidth = rDoc.GetOriginalWidth( nStartCol, nTab ); + aAny <<= static_cast<sal_Int32>(convertTwipToMm100(nWidth)); + } + else if ( aPropertyName == SC_UNONAME_CELLVIS ) + { + bool bVis = !rDoc.ColHidden(nStartCol, nTab); + aAny <<= bVis; + } + else if ( aPropertyName == SC_UNONAME_OWIDTH ) + { + bool bOpt = !(rDoc.GetColFlags( nStartCol, nTab ) & CRFlags::ManualSize); + aAny <<= bOpt; + } + else if ( aPropertyName == SC_UNONAME_NEWPAGE ) + { + ScBreakType nBreak = rDoc.HasColBreak(nStartCol, nTab); + aAny <<= (nBreak != ScBreakType::NONE); + } + else if ( aPropertyName == SC_UNONAME_MANPAGE ) + { + ScBreakType nBreak = rDoc.HasColBreak(nStartCol, nTab); + aAny <<= bool(nBreak & ScBreakType::Manual); + } + + return aAny; +} + +SC_IMPL_DUMMY_PROPERTY_LISTENER( ScTableColumnsObj ) + +ScTableRowsObj::ScTableRowsObj(ScDocShell* pDocSh, SCTAB nT, SCROW nSR, SCROW nER) : + pDocShell( pDocSh ), + nTab ( nT ), + nStartRow( nSR ), + nEndRow ( nER ) +{ + pDocShell->GetDocument().AddUnoObject(*this); +} + +ScTableRowsObj::~ScTableRowsObj() +{ + SolarMutexGuard g; + + if (pDocShell) + pDocShell->GetDocument().RemoveUnoObject(*this); +} + +void ScTableRowsObj::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + if ( dynamic_cast<const ScUpdateRefHint*>(&rHint) ) + { + //! update of references for sheet and its start/end + } + else if ( rHint.GetId() == SfxHintId::Dying ) + { + pDocShell = nullptr; // became invalid + } +} + +// XTableRows + +rtl::Reference<ScTableRowObj> ScTableRowsObj::GetObjectByIndex_Impl(sal_Int32 nIndex) const +{ + SCROW nRow = static_cast<SCROW>(nIndex) + nStartRow; + if ( pDocShell && nRow <= nEndRow ) + return new ScTableRowObj( pDocShell, nRow, nTab ); + + return nullptr; // wrong index +} + +void SAL_CALL ScTableRowsObj::insertByIndex( sal_Int32 nPosition, sal_Int32 nCount ) +{ + SolarMutexGuard aGuard; + bool bDone = false; + if ( pDocShell ) + { + const ScDocument& rDoc = pDocShell->GetDocument(); + if ( nCount > 0 && nPosition >= 0 && nStartRow+nPosition <= nEndRow && + nStartRow+nPosition+nCount-1 <= rDoc.MaxRow() ) + { + ScRange aRange( 0, static_cast<SCROW>(nStartRow+nPosition), nTab, + rDoc.MaxCol(), static_cast<SCROW>(nStartRow+nPosition+nCount-1), nTab ); + bDone = pDocShell->GetDocFunc().InsertCells( aRange, nullptr, INS_INSROWS_BEFORE, true, true ); + } + } + if (!bDone) + throw uno::RuntimeException(); // no other exceptions specified +} + +void SAL_CALL ScTableRowsObj::removeByIndex( sal_Int32 nIndex, sal_Int32 nCount ) +{ + SolarMutexGuard aGuard; + bool bDone = false; + // the range to be deleted has to lie within the object + if ( pDocShell && nCount > 0 && nIndex >= 0 && nStartRow+nIndex+nCount-1 <= nEndRow ) + { + const ScDocument& rDoc = pDocShell->GetDocument(); + ScRange aRange( 0, static_cast<SCROW>(nStartRow+nIndex), nTab, + rDoc.MaxCol(), static_cast<SCROW>(nStartRow+nIndex+nCount-1), nTab ); + bDone = pDocShell->GetDocFunc().DeleteCells( aRange, nullptr, DelCellCmd::Rows, true ); + } + if (!bDone) + throw uno::RuntimeException(); // no other exceptions specified +} + +// XEnumerationAccess + +uno::Reference<container::XEnumeration> SAL_CALL ScTableRowsObj::createEnumeration() +{ + SolarMutexGuard aGuard; + return new ScIndexEnumeration(this, "com.sun.star.table.TableRowsEnumeration"); +} + +// XIndexAccess + +sal_Int32 SAL_CALL ScTableRowsObj::getCount() +{ + SolarMutexGuard aGuard; + return nEndRow - nStartRow + 1; +} + +uno::Any SAL_CALL ScTableRowsObj::getByIndex( sal_Int32 nIndex ) +{ + SolarMutexGuard aGuard; + uno::Reference<table::XCellRange> xRow(GetObjectByIndex_Impl(nIndex)); + if (!xRow.is()) + throw lang::IndexOutOfBoundsException(); + + return uno::Any(xRow); +} + +uno::Type SAL_CALL ScTableRowsObj::getElementType() +{ + return cppu::UnoType<table::XCellRange>::get(); +} + +sal_Bool SAL_CALL ScTableRowsObj::hasElements() +{ + SolarMutexGuard aGuard; + return ( getCount() != 0 ); +} + +// XPropertySet + +uno::Reference<beans::XPropertySetInfo> SAL_CALL ScTableRowsObj::getPropertySetInfo() +{ + static uno::Reference<beans::XPropertySetInfo> aRef( + new SfxItemPropertySetInfo( lcl_GetRowsPropertyMap() )); + return aRef; +} + +void SAL_CALL ScTableRowsObj::setPropertyValue( + const OUString& aPropertyName, const uno::Any& aValue ) +{ + SolarMutexGuard aGuard; + if (!pDocShell) + throw uno::RuntimeException(); + + ScDocFunc& rFunc = pDocShell->GetDocFunc(); + ScDocument& rDoc = pDocShell->GetDocument(); + std::vector<sc::ColRowSpan> aRowArr(1, sc::ColRowSpan(nStartRow,nEndRow)); + + if ( aPropertyName == SC_UNONAME_OHEIGHT ) + { + sal_Int32 nNewHeight = 0; + if ( rDoc.IsImportingXML() && ( aValue >>= nNewHeight ) ) + { + // used to set the stored row height for rows with optimal height when loading. + + // TODO: It's probably cleaner to use a different property name + // for this. + rDoc.SetRowHeightOnly( nStartRow, nEndRow, nTab, o3tl::toTwips(nNewHeight, o3tl::Length::mm100) ); + } + else + { + bool bOpt = ScUnoHelpFunctions::GetBoolFromAny( aValue ); + if (bOpt) + rFunc.SetWidthOrHeight(false, aRowArr, nTab, SC_SIZE_OPTIMAL, 0, true, true); + else + { + //! manually set old heights again? + } + } + } + else if ( aPropertyName == SC_UNONAME_CELLHGT ) + { + sal_Int32 nNewHeight = 0; + if ( aValue >>= nNewHeight ) + { + if (rDoc.IsImportingXML()) + { + // TODO: This is a band-aid fix. Eventually we need to + // re-work ods' style import to get it to set styles to + // ScDocument directly. + rDoc.SetRowHeightOnly( nStartRow, nEndRow, nTab, o3tl::toTwips(nNewHeight, o3tl::Length::mm100) ); + rDoc.SetManualHeight( nStartRow, nEndRow, nTab, true ); + } + else + rFunc.SetWidthOrHeight( + false, aRowArr, nTab, SC_SIZE_ORIGINAL, o3tl::toTwips(nNewHeight, o3tl::Length::mm100), true, true); + } + } + else if ( aPropertyName == SC_UNONAME_CELLVIS ) + { + bool bVis = ScUnoHelpFunctions::GetBoolFromAny( aValue ); + ScSizeMode eMode = bVis ? SC_SIZE_SHOW : SC_SIZE_DIRECT; + rFunc.SetWidthOrHeight(false, aRowArr, nTab, eMode, 0, true, true); + // SC_SIZE_DIRECT with size 0: hide + } + else if ( aPropertyName == SC_UNONAME_VISFLAG ) + { + // #i116460# Shortcut to only set the flag, without drawing layer update etc. + // Should only be used from import filters. + rDoc.SetRowHidden(nStartRow, nEndRow, nTab, !ScUnoHelpFunctions::GetBoolFromAny( aValue )); + } + else if ( aPropertyName == SC_UNONAME_CELLFILT ) + { + //! undo etc. + if (ScUnoHelpFunctions::GetBoolFromAny( aValue )) + rDoc.SetRowFiltered(nStartRow, nEndRow, nTab, true); + else + rDoc.SetRowFiltered(nStartRow, nEndRow, nTab, false); + } + else if ( aPropertyName == SC_UNONAME_NEWPAGE || aPropertyName == SC_UNONAME_MANPAGE ) + { + //! single function to set/remove all breaks? + bool bSet = ScUnoHelpFunctions::GetBoolFromAny( aValue ); + for (SCROW nRow=nStartRow; nRow<=nEndRow; nRow++) + if (bSet) + rFunc.InsertPageBreak( false, ScAddress(0,nRow,nTab), true, true ); + else + rFunc.RemovePageBreak( false, ScAddress(0,nRow,nTab), true, true ); + } + else if ( aPropertyName == SC_UNONAME_CELLBACK || aPropertyName == SC_UNONAME_CELLTRAN ) + { + // #i57867# Background color is specified for row styles in the file format, + // so it has to be supported along with the row properties (import only). + + // Use ScCellRangeObj to set the property for all cells in the rows + // (this means, the "row attribute" must be set before individual cell attributes). + + ScRange aRange( 0, nStartRow, nTab, rDoc.MaxCol(), nEndRow, nTab ); + uno::Reference<beans::XPropertySet> xRangeObj = new ScCellRangeObj( pDocShell, aRange ); + xRangeObj->setPropertyValue( aPropertyName, aValue ); + } +} + +uno::Any SAL_CALL ScTableRowsObj::getPropertyValue( const OUString& aPropertyName ) +{ + SolarMutexGuard aGuard; + if (!pDocShell) + throw uno::RuntimeException(); + + ScDocument& rDoc = pDocShell->GetDocument(); + uno::Any aAny; + + //! loop over all rows for current state? + + if ( aPropertyName == SC_UNONAME_CELLHGT ) + { + // for hidden row, return original height + sal_uInt16 nHeight = rDoc.GetOriginalHeight( nStartRow, nTab ); + aAny <<= static_cast<sal_Int32>(convertTwipToMm100(nHeight)); + } + else if ( aPropertyName == SC_UNONAME_CELLVIS ) + { + SCROW nLastRow; + bool bVis = !rDoc.RowHidden(nStartRow, nTab, nullptr, &nLastRow); + aAny <<= bVis; + } + else if ( aPropertyName == SC_UNONAME_CELLFILT ) + { + bool bVis = rDoc.RowFiltered(nStartRow, nTab); + aAny <<= bVis; + } + else if ( aPropertyName == SC_UNONAME_OHEIGHT ) + { + bool bOpt = !(rDoc.GetRowFlags( nStartRow, nTab ) & CRFlags::ManualSize); + aAny <<= bOpt; + } + else if ( aPropertyName == SC_UNONAME_NEWPAGE ) + { + ScBreakType nBreak = rDoc.HasRowBreak(nStartRow, nTab); + aAny <<= (nBreak != ScBreakType::NONE); + } + else if ( aPropertyName == SC_UNONAME_MANPAGE ) + { + ScBreakType nBreak = rDoc.HasRowBreak(nStartRow, nTab); + aAny <<= bool(nBreak & ScBreakType::Manual); + } + else if ( aPropertyName == SC_UNONAME_CELLBACK || aPropertyName == SC_UNONAME_CELLTRAN ) + { + // Use ScCellRangeObj to get the property from the cell range + // (for completeness only, this is not used by the XML filter). + + ScRange aRange( 0, nStartRow, nTab, rDoc.MaxCol(), nEndRow, nTab ); + uno::Reference<beans::XPropertySet> xRangeObj = new ScCellRangeObj( pDocShell, aRange ); + aAny = xRangeObj->getPropertyValue( aPropertyName ); + } + + return aAny; +} + +SC_IMPL_DUMMY_PROPERTY_LISTENER( ScTableRowsObj ) + +ScSpreadsheetSettingsObj::~ScSpreadsheetSettingsObj() +{ +} + +// XPropertySet + +uno::Reference<beans::XPropertySetInfo> SAL_CALL ScSpreadsheetSettingsObj::getPropertySetInfo() +{ + return nullptr; +} + +void SAL_CALL ScSpreadsheetSettingsObj::setPropertyValue( + const OUString& /* aPropertyName */, const uno::Any& /* aValue */ ) +{ +} + +uno::Any SAL_CALL ScSpreadsheetSettingsObj::getPropertyValue( const OUString& /* aPropertyName */ ) +{ + return uno::Any(); +} + +SC_IMPL_DUMMY_PROPERTY_LISTENER( ScSpreadsheetSettingsObj ) + +ScAnnotationsObj::ScAnnotationsObj(ScDocShell* pDocSh, SCTAB nT) : + pDocShell( pDocSh ), + nTab( nT ) +{ + pDocShell->GetDocument().AddUnoObject(*this); +} + +ScAnnotationsObj::~ScAnnotationsObj() +{ + SolarMutexGuard g; + + if (pDocShell) + pDocShell->GetDocument().RemoveUnoObject(*this); +} + +void ScAnnotationsObj::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + //! adjust nTab when updating references!!! + + if ( rHint.GetId() == SfxHintId::Dying ) + { + pDocShell = nullptr; // became invalid + } +} + +bool ScAnnotationsObj::GetAddressByIndex_Impl( sal_Int32 nIndex, ScAddress& rPos ) const +{ + if (!pDocShell) + return false; + + ScDocument& rDoc = pDocShell->GetDocument(); + rPos = rDoc.GetNotePosition(nIndex, nTab); + return rPos.IsValid(); +} + +rtl::Reference<ScAnnotationObj> ScAnnotationsObj::GetObjectByIndex_Impl( sal_Int32 nIndex ) const +{ + if (pDocShell) + { + ScAddress aPos; + if ( GetAddressByIndex_Impl( nIndex, aPos ) ) + return new ScAnnotationObj( pDocShell, aPos ); + } + return nullptr; +} + +// XSheetAnnotations + +void SAL_CALL ScAnnotationsObj::insertNew( + const table::CellAddress& aPosition, const OUString& rText ) +{ + SolarMutexGuard aGuard; + if (pDocShell) + { + OSL_ENSURE( aPosition.Sheet == nTab, "addAnnotation with a wrong Sheet" ); + ScAddress aPos( static_cast<SCCOL>(aPosition.Column), static_cast<SCROW>(aPosition.Row), nTab ); + pDocShell->GetDocFunc().ReplaceNote( aPos, rText, nullptr, nullptr, true ); + } +} + +void SAL_CALL ScAnnotationsObj::removeByIndex( sal_Int32 nIndex ) +{ + SolarMutexGuard aGuard; + if (pDocShell) + { + ScAddress aPos; + if ( GetAddressByIndex_Impl( nIndex, aPos ) ) + { + ScMarkData aMarkData(pDocShell->GetDocument().GetSheetLimits()); + aMarkData.SelectTable( aPos.Tab(), true ); + aMarkData.SetMultiMarkArea( ScRange(aPos) ); + + pDocShell->GetDocFunc().DeleteContents( aMarkData, InsertDeleteFlags::NOTE, true, true ); + } + } +} + +// XEnumerationAccess + +uno::Reference<container::XEnumeration> SAL_CALL ScAnnotationsObj::createEnumeration() +{ + //! iterate directly (more efficiently)? + + SolarMutexGuard aGuard; + return new ScIndexEnumeration(this, "com.sun.star.sheet.CellAnnotationsEnumeration"); +} + +// XIndexAccess + +sal_Int32 SAL_CALL ScAnnotationsObj::getCount() +{ + SolarMutexGuard aGuard; + sal_Int32 nCount = 0; + if (pDocShell) + { + const ScDocument& rDoc = pDocShell->GetDocument(); + for (SCCOL nCol : rDoc.GetAllocatedColumnsRange(nTab, 0, rDoc.MaxCol())) + nCount += rDoc.GetNoteCount(nTab, nCol); + } + return nCount; +} + +uno::Any SAL_CALL ScAnnotationsObj::getByIndex( sal_Int32 nIndex ) +{ + SolarMutexGuard aGuard; + uno::Reference<sheet::XSheetAnnotation> xAnnotation(GetObjectByIndex_Impl(nIndex)); + if (!xAnnotation.is()) + throw lang::IndexOutOfBoundsException(); + + return uno::Any(xAnnotation); +} + +uno::Type SAL_CALL ScAnnotationsObj::getElementType() +{ + return cppu::UnoType<sheet::XSheetAnnotation>::get(); +} + +sal_Bool SAL_CALL ScAnnotationsObj::hasElements() +{ + SolarMutexGuard aGuard; + return ( getCount() != 0 ); +} + +ScScenariosObj::ScScenariosObj(ScDocShell* pDocSh, SCTAB nT) : + pDocShell( pDocSh ), + nTab ( nT ) +{ + pDocShell->GetDocument().AddUnoObject(*this); +} + +ScScenariosObj::~ScScenariosObj() +{ + SolarMutexGuard g; + + if (pDocShell) + pDocShell->GetDocument().RemoveUnoObject(*this); +} + +void ScScenariosObj::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + if ( dynamic_cast<const ScUpdateRefHint*>(&rHint) ) + { + //! update of references for sheet and its start/end + } + else if ( rHint.GetId() == SfxHintId::Dying ) + { + pDocShell = nullptr; // became invalid + } +} + +// XScenarios + +bool ScScenariosObj::GetScenarioIndex_Impl( std::u16string_view rName, SCTAB& rIndex ) +{ + //! Case-insensitive ???? + + if ( pDocShell ) + { + OUString aTabName; + ScDocument& rDoc = pDocShell->GetDocument(); + SCTAB nCount = static_cast<SCTAB>(getCount()); + for (SCTAB i=0; i<nCount; i++) + if (rDoc.GetName( nTab+i+1, aTabName )) + if (aTabName == rName) + { + rIndex = i; + return true; + } + } + + return false; +} + +rtl::Reference<ScTableSheetObj> ScScenariosObj::GetObjectByIndex_Impl(sal_Int32 nIndex) +{ + sal_uInt16 nCount = static_cast<sal_uInt16>(getCount()); + if ( pDocShell && nIndex >= 0 && nIndex < nCount ) + return new ScTableSheetObj( pDocShell, nTab+static_cast<SCTAB>(nIndex)+1 ); + + return nullptr; // no document or wrong index +} + +rtl::Reference<ScTableSheetObj> ScScenariosObj::GetObjectByName_Impl(std::u16string_view aName) +{ + SCTAB nIndex; + if ( pDocShell && GetScenarioIndex_Impl( aName, nIndex ) ) + return new ScTableSheetObj( pDocShell, nTab+nIndex+1 ); + + return nullptr; // not found +} + +void SAL_CALL ScScenariosObj::addNewByName( const OUString& aName, + const uno::Sequence<table::CellRangeAddress>& aRanges, + const OUString& aComment ) +{ + SolarMutexGuard aGuard; + if ( !pDocShell ) + return; + + ScMarkData aMarkData(pDocShell->GetDocument().GetSheetLimits()); + aMarkData.SelectTable( nTab, true ); + + for (const table::CellRangeAddress& rRange : aRanges) + { + OSL_ENSURE( rRange.Sheet == nTab, "addScenario with a wrong Tab" ); + ScRange aRange( static_cast<SCCOL>(rRange.StartColumn), static_cast<SCROW>(rRange.StartRow), nTab, + static_cast<SCCOL>(rRange.EndColumn), static_cast<SCROW>(rRange.EndRow), nTab ); + + aMarkData.SetMultiMarkArea( aRange ); + } + + ScScenarioFlags const nFlags = ScScenarioFlags::ShowFrame | ScScenarioFlags::PrintFrame + | ScScenarioFlags::TwoWay | ScScenarioFlags::Protected; + + pDocShell->MakeScenario( nTab, aName, aComment, COL_LIGHTGRAY, nFlags, aMarkData ); +} + +void SAL_CALL ScScenariosObj::removeByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + SCTAB nIndex; + if ( pDocShell && GetScenarioIndex_Impl( aName, nIndex ) ) + pDocShell->GetDocFunc().DeleteTable( nTab+nIndex+1, true ); +} + +// XEnumerationAccess + +uno::Reference<container::XEnumeration> SAL_CALL ScScenariosObj::createEnumeration() +{ + SolarMutexGuard aGuard; + return new ScIndexEnumeration(this, "com.sun.star.sheet.ScenariosEnumeration"); +} + +// XIndexAccess + +sal_Int32 SAL_CALL ScScenariosObj::getCount() +{ + SolarMutexGuard aGuard; + SCTAB nCount = 0; + if ( pDocShell ) + { + ScDocument& rDoc = pDocShell->GetDocument(); + if (!rDoc.IsScenario(nTab)) + { + SCTAB nTabCount = rDoc.GetTableCount(); + SCTAB nNext = nTab + 1; + while (nNext < nTabCount && rDoc.IsScenario(nNext)) + { + ++nCount; + ++nNext; + } + } + } + return nCount; +} + +uno::Any SAL_CALL ScScenariosObj::getByIndex( sal_Int32 nIndex ) +{ + SolarMutexGuard aGuard; + uno::Reference<sheet::XScenario> xScen(GetObjectByIndex_Impl(nIndex)); + if (!xScen.is()) + throw lang::IndexOutOfBoundsException(); + + return uno::Any(xScen); +} + +uno::Type SAL_CALL ScScenariosObj::getElementType() +{ + return cppu::UnoType<sheet::XScenario>::get(); +} + +sal_Bool SAL_CALL ScScenariosObj::hasElements() +{ + SolarMutexGuard aGuard; + return ( getCount() != 0 ); +} + +uno::Any SAL_CALL ScScenariosObj::getByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + uno::Reference<sheet::XScenario> xScen(GetObjectByName_Impl(aName)); + if (!xScen.is()) + throw container::NoSuchElementException(); + + return uno::Any(xScen); +} + +uno::Sequence<OUString> SAL_CALL ScScenariosObj::getElementNames() +{ + SolarMutexGuard aGuard; + SCTAB nCount = static_cast<SCTAB>(getCount()); + uno::Sequence<OUString> aSeq(nCount); + + if ( pDocShell ) // otherwise Count = 0 + { + OUString aTabName; + ScDocument& rDoc = pDocShell->GetDocument(); + OUString* pAry = aSeq.getArray(); + for (SCTAB i=0; i<nCount; i++) + if (rDoc.GetName( nTab+i+1, aTabName )) + pAry[i] = aTabName; + } + + return aSeq; +} + +sal_Bool SAL_CALL ScScenariosObj::hasByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + SCTAB nIndex; + return GetScenarioIndex_Impl( aName, nIndex ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/unoobj/drdefuno.cxx b/sc/source/ui/unoobj/drdefuno.cxx new file mode 100644 index 0000000000..fa2387d28b --- /dev/null +++ b/sc/source/ui/unoobj/drdefuno.cxx @@ -0,0 +1,80 @@ +/* -*- 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 <drdefuno.hxx> +#include <docsh.hxx> +#include <drwlayer.hxx> + +#include <svx/unoprov.hxx> +#include <vcl/svapp.hxx> + +using namespace ::com::sun::star; + +ScDrawDefaultsObj::ScDrawDefaultsObj(ScDocShell* pDocSh) : + SvxUnoDrawPool( nullptr, SvxPropertySetInfoPool::getDrawingDefaults() ), + pDocShell( pDocSh ) +{ + // SvxUnoDrawPool is initialized without model, + // draw layer is created on demand in getModelPool + + pDocShell->GetDocument().AddUnoObject(*this); +} + +ScDrawDefaultsObj::~ScDrawDefaultsObj() noexcept +{ + SolarMutexGuard g; + + if (pDocShell) + pDocShell->GetDocument().RemoveUnoObject(*this); +} + +void ScDrawDefaultsObj::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + if ( rHint.GetId() == SfxHintId::Dying ) + { + pDocShell = nullptr; // document gone + } +} + +SfxItemPool* ScDrawDefaultsObj::getModelPool( bool bReadOnly ) noexcept +{ + SfxItemPool* pRet = nullptr; + + try + { + if ( pDocShell ) + { + ScDrawLayer* pModel = bReadOnly ? + pDocShell->GetDocument().GetDrawLayer() : + pDocShell->MakeDrawLayer(); + if ( pModel ) + pRet = &pModel->GetItemPool(); + } + } + catch (...) + { + } + + if ( !pRet ) + pRet = SvxUnoDrawPool::getModelPool( bReadOnly ); // uses default pool + + return pRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/unoobj/editsrc.cxx b/sc/source/ui/unoobj/editsrc.cxx new file mode 100644 index 0000000000..f96f9c0201 --- /dev/null +++ b/sc/source/ui/unoobj/editsrc.cxx @@ -0,0 +1,272 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <memory> +#include <sal/config.h> + +#include <utility> + +#include <editsrc.hxx> + +#include <editeng/unofored.hxx> +#include <vcl/svapp.hxx> +#include <svx/svdocapt.hxx> +#include <editeng/outlobj.hxx> +#include <editeng/editobj.hxx> +#include <editeng/outliner.hxx> +#include <textuno.hxx> +#include <editutil.hxx> +#include <docsh.hxx> +#include <hints.hxx> +#include <postit.hxx> +#include <AccessibleText.hxx> + +ScHeaderFooterEditSource::ScHeaderFooterEditSource(ScHeaderFooterTextData& rData) : + mrTextData(rData) {} + +ScHeaderFooterEditSource::~ScHeaderFooterEditSource() {} + +ScEditEngineDefaulter* ScHeaderFooterEditSource::GetEditEngine() +{ + return mrTextData.GetEditEngine(); +} + +std::unique_ptr<SvxEditSource> ScHeaderFooterEditSource::Clone() const +{ + return std::unique_ptr<SvxEditSource>(new ScHeaderFooterEditSource(mrTextData)); +} + +SvxTextForwarder* ScHeaderFooterEditSource::GetTextForwarder() +{ + return mrTextData.GetTextForwarder(); +} + +void ScHeaderFooterEditSource::UpdateData() +{ + mrTextData.UpdateData(); +} + +ScCellEditSource::ScCellEditSource(ScDocShell* pDocSh, const ScAddress& rP) : + pCellTextData(new ScCellTextData(pDocSh, rP)) {} + +ScCellEditSource::~ScCellEditSource() +{ +} + +std::unique_ptr<SvxEditSource> ScCellEditSource::Clone() const +{ + return std::unique_ptr<SvxEditSource>(new ScCellEditSource(pCellTextData->GetDocShell(), pCellTextData->GetCellPos())); +} + +SvxTextForwarder* ScCellEditSource::GetTextForwarder() +{ + return pCellTextData->GetTextForwarder(); +} + +void ScCellEditSource::UpdateData() +{ + pCellTextData->UpdateData(); +} + +void ScCellEditSource::SetDoUpdateData(bool bValue) +{ + pCellTextData->SetDoUpdate(bValue); +} + +bool ScCellEditSource::IsDirty() const +{ + return pCellTextData->IsDirty(); +} + +ScEditEngineDefaulter* ScCellEditSource::GetEditEngine() +{ + return pCellTextData->GetEditEngine(); +} + +ScAnnotationEditSource::ScAnnotationEditSource(ScDocShell* pDocSh, const ScAddress& rP) : + pDocShell( pDocSh ), + aCellPos( rP ), + bDataValid( false ) +{ + if (pDocShell) + pDocShell->GetDocument().AddUnoObject(*this); +} + +ScAnnotationEditSource::~ScAnnotationEditSource() +{ + SolarMutexGuard aGuard; // needed for EditEngine dtor + + if (pDocShell) + pDocShell->GetDocument().RemoveUnoObject(*this); + + pForwarder.reset(); + pEditEngine.reset(); +} + +std::unique_ptr<SvxEditSource> ScAnnotationEditSource::Clone() const +{ + return std::unique_ptr<SvxEditSource>(new ScAnnotationEditSource( pDocShell, aCellPos )); +} + +SdrObject* ScAnnotationEditSource::GetCaptionObj() +{ + ScPostIt* pNote = pDocShell->GetDocument().GetNote(aCellPos); + return pNote ? pNote->GetOrCreateCaption( aCellPos ) : nullptr; +} + +SvxTextForwarder* ScAnnotationEditSource::GetTextForwarder() +{ + if (!pEditEngine) + { + // notes don't have fields + if ( pDocShell ) + { + pEditEngine.reset( new ScNoteEditEngine( pDocShell->GetDocument().GetNoteEngine() ) ); + } + else + { + rtl::Reference<SfxItemPool> pEnginePool = EditEngine::CreatePool(); + pEnginePool->FreezeIdRanges(); + pEditEngine.reset( new ScEditEngineDefaulter( pEnginePool.get(), true ) ); + } + pForwarder.reset( new SvxEditEngineForwarder(*pEditEngine) ); + } + + if (bDataValid) + return pForwarder.get(); + + if ( pDocShell ) + if ( ScPostIt* pNote = pDocShell->GetDocument().GetNote(aCellPos) ) + if ( const EditTextObject* pEditObj = pNote->GetEditTextObject() ) + pEditEngine->SetTextCurrentDefaults( *pEditObj ); // incl. breaks (line, etc.) + + bDataValid = true; + return pForwarder.get(); +} + +void ScAnnotationEditSource::UpdateData() +{ + if ( !(pDocShell && pEditEngine) ) + return; + + ScDocShellModificator aModificator( *pDocShell ); + + if( SdrObject* pObj = GetCaptionObj() ) + { + OutlinerParaObject aOPO( pEditEngine->CreateTextObject() ); + aOPO.SetOutlinerMode( OutlinerMode::TextObject ); + pObj->NbcSetOutlinerParaObject( std::move(aOPO) ); + pObj->ActionChanged(); + } + + //! Undo !!! + + aModificator.SetDocumentModified(); + + // SetDocumentModified will reset bDataValid +} + +void ScAnnotationEditSource::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + if ( dynamic_cast<const ScUpdateRefHint*>(&rHint) ) + { + //! reference update + } + else + { + const SfxHintId nId = rHint.GetId(); + if ( nId == SfxHintId::Dying ) + { + pDocShell = nullptr; + + pForwarder.reset(); + pEditEngine.reset(); // EditEngine uses document's pool + } + else if ( nId == SfxHintId::DataChanged ) + bDataValid = false; // text must be retrieved again + } +} + +ScSimpleEditSource::ScSimpleEditSource( SvxTextForwarder* pForw ) : + pForwarder( pForw ) +{ + // The same forwarder (and EditEngine) is shared by all children of the same Text object. + // Text range and cursor keep a reference to their parent text, so the text object is + // always alive and the forwarder is valid as long as there are children. +} + +ScSimpleEditSource::~ScSimpleEditSource() +{ +} + +std::unique_ptr<SvxEditSource> ScSimpleEditSource::Clone() const +{ + return std::unique_ptr<SvxEditSource>(new ScSimpleEditSource( pForwarder )); +} + +SvxTextForwarder* ScSimpleEditSource::GetTextForwarder() +{ + return pForwarder; +} + +void ScSimpleEditSource::UpdateData() +{ + // nothing +} + +ScAccessibilityEditSource::ScAccessibilityEditSource( ::std::unique_ptr < ScAccessibleTextData > && pAccessibleCellTextData ) + : mpAccessibleTextData(std::move(pAccessibleCellTextData)) +{ +} + +ScAccessibilityEditSource::~ScAccessibilityEditSource() +{ +} + +std::unique_ptr<SvxEditSource> ScAccessibilityEditSource::Clone() const +{ + return std::unique_ptr<SvxEditSource>(new ScAccessibilityEditSource(::std::unique_ptr < ScAccessibleTextData > (mpAccessibleTextData->Clone()))); +} + +SvxTextForwarder* ScAccessibilityEditSource::GetTextForwarder() +{ + return mpAccessibleTextData->GetTextForwarder(); +} + +SvxViewForwarder* ScAccessibilityEditSource::GetViewForwarder() +{ + return mpAccessibleTextData->GetViewForwarder(); +} + +SvxEditViewForwarder* ScAccessibilityEditSource::GetEditViewForwarder( bool bCreate ) +{ + return mpAccessibleTextData->GetEditViewForwarder(bCreate); +} + +void ScAccessibilityEditSource::UpdateData() +{ + mpAccessibleTextData->UpdateData(); +} + +SfxBroadcaster& ScAccessibilityEditSource::GetBroadcaster() const +{ + return mpAccessibleTextData->GetBroadcaster(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/unoobj/eventuno.cxx b/sc/source/ui/unoobj/eventuno.cxx new file mode 100644 index 0000000000..88a3876378 --- /dev/null +++ b/sc/source/ui/unoobj/eventuno.cxx @@ -0,0 +1,174 @@ +/* -*- 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 <eventuno.hxx> +#include <miscuno.hxx> +#include <docsh.hxx> +#include <sheetevents.hxx> +#include <unonames.hxx> +#include <comphelper/propertysequence.hxx> +#include <vcl/svapp.hxx> + +using namespace ::com::sun::star; + +SC_SIMPLE_SERVICE_INFO( ScSheetEventsObj, "ScSheetEventsObj", "com.sun.star.document.Events" ) + +ScSheetEventsObj::ScSheetEventsObj(ScDocShell* pDocSh, SCTAB nT) : + mpDocShell( pDocSh ), + mnTab( nT ) +{ + mpDocShell->GetDocument().AddUnoObject(*this); +} + +ScSheetEventsObj::~ScSheetEventsObj() +{ + SolarMutexGuard g; + + if (mpDocShell) + mpDocShell->GetDocument().RemoveUnoObject(*this); +} + +void ScSheetEventsObj::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + //! reference update + if ( rHint.GetId() == SfxHintId::Dying ) + { + mpDocShell = nullptr; + } +} + +static ScSheetEventId lcl_GetEventFromName( std::u16string_view aName ) +{ + for (sal_Int32 nEvent=0; nEvent<static_cast<sal_Int32>(ScSheetEventId::COUNT); ++nEvent) + if ( aName == ScSheetEvents::GetEventName(static_cast<ScSheetEventId>(nEvent)) ) + return static_cast<ScSheetEventId>(nEvent); + + return ScSheetEventId::NOTFOUND; // not found +} + +// XNameReplace + +void SAL_CALL ScSheetEventsObj::replaceByName( const OUString& aName, const uno::Any& aElement ) +{ + SolarMutexGuard aGuard; + if (!mpDocShell) + throw uno::RuntimeException(); + + ScSheetEventId nEvent = lcl_GetEventFromName(aName); + if (nEvent == ScSheetEventId::NOTFOUND) + throw container::NoSuchElementException(); + + std::unique_ptr<ScSheetEvents> pNewEvents(new ScSheetEvents); + const ScSheetEvents* pOldEvents = mpDocShell->GetDocument().GetSheetEvents(mnTab); + if (pOldEvents) + *pNewEvents = *pOldEvents; + + OUString aScript; + if ( aElement.hasValue() ) // empty Any -> reset event + { + uno::Sequence<beans::PropertyValue> aPropSeq; + if ( aElement >>= aPropSeq ) + { + for (const beans::PropertyValue& rProp : std::as_const(aPropSeq)) + { + if ( rProp.Name == SC_UNO_EVENTTYPE ) + { + OUString aEventType; + if ( rProp.Value >>= aEventType ) + { + // only "Script" is supported + if ( aEventType != SC_UNO_SCRIPT ) + throw lang::IllegalArgumentException(); + } + } + else if ( rProp.Name == SC_UNO_SCRIPT ) + rProp.Value >>= aScript; + } + } + } + if (!aScript.isEmpty()) + pNewEvents->SetScript( nEvent, &aScript ); + else + pNewEvents->SetScript( nEvent, nullptr ); // reset + + mpDocShell->GetDocument().SetSheetEvents( mnTab, std::move(pNewEvents) ); + mpDocShell->SetDocumentModified(); +} + +// XNameAccess + +uno::Any SAL_CALL ScSheetEventsObj::getByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + ScSheetEventId nEvent = lcl_GetEventFromName(aName); + if (nEvent == ScSheetEventId::NOTFOUND) + throw container::NoSuchElementException(); + + const OUString* pScript = nullptr; + if (mpDocShell) + { + const ScSheetEvents* pEvents = mpDocShell->GetDocument().GetSheetEvents(mnTab); + if (pEvents) + pScript = pEvents->GetScript(nEvent); + } + + uno::Any aRet; + if (pScript) + { + uno::Sequence<beans::PropertyValue> aPropSeq( comphelper::InitPropertySequence({ + { "EventType", uno::Any( OUString("Script") ) }, + { "Script", uno::Any( *pScript ) } + })); + aRet <<= aPropSeq; + } + // empty Any if nothing was set + return aRet; +} + +uno::Sequence<OUString> SAL_CALL ScSheetEventsObj::getElementNames() +{ + auto aNames = uno::Sequence<OUString>(int(ScSheetEventId::COUNT)); + auto pNames = aNames.getArray(); + for (sal_Int32 nEvent=0; nEvent<int(ScSheetEventId::COUNT); ++nEvent) + pNames[nEvent] = ScSheetEvents::GetEventName(static_cast<ScSheetEventId>(nEvent)); + return aNames; +} + +sal_Bool SAL_CALL ScSheetEventsObj::hasByName( const OUString& aName ) +{ + ScSheetEventId nEvent = lcl_GetEventFromName(aName); + return (nEvent != ScSheetEventId::NOTFOUND); +} + +// XElementAccess + +uno::Type SAL_CALL ScSheetEventsObj::getElementType() +{ + return cppu::UnoType<uno::Sequence<beans::PropertyValue>>::get(); +} + +sal_Bool SAL_CALL ScSheetEventsObj::hasElements() +{ + SolarMutexGuard aGuard; + if (mpDocShell) + return true; + return false; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/unoobj/exceldetect.cxx b/sc/source/ui/unoobj/exceldetect.cxx new file mode 100644 index 0000000000..0c4373356a --- /dev/null +++ b/sc/source/ui/unoobj/exceldetect.cxx @@ -0,0 +1,198 @@ +/* -*- 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 "exceldetect.hxx" + +#include <com/sun/star/io/XInputStream.hpp> +#include <com/sun/star/ucb/ContentCreationException.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <cppuhelper/supportsservice.hxx> + +#include <sfx2/docfile.hxx> +#include <unotools/mediadescriptor.hxx> +#include <sot/storage.hxx> +#include <comphelper/diagnose_ex.hxx> + +using namespace com::sun::star; +using utl::MediaDescriptor; + +ScExcelBiffDetect::ScExcelBiffDetect() {} +ScExcelBiffDetect::~ScExcelBiffDetect() {} + +OUString ScExcelBiffDetect::getImplementationName() +{ + return "com.sun.star.comp.calc.ExcelBiffFormatDetector"; +} + +sal_Bool ScExcelBiffDetect::supportsService( const OUString& aName ) +{ + return cppu::supportsService(this, aName); +} + +uno::Sequence<OUString> ScExcelBiffDetect::getSupportedServiceNames() +{ + return { "com.sun.star.frame.ExtendedTypeDetection" }; +} + +namespace { + +bool hasStream(const uno::Reference<io::XInputStream>& xInStream, const OUString& rName) +{ + SfxMedium aMedium; + aMedium.UseInteractionHandler(false); + aMedium.setStreamToLoadFrom(xInStream, true); + SvStream* pStream = aMedium.GetInStream(); + if (!pStream) + return false; + + sal_uInt64 const nSize = pStream->TellEnd(); + pStream->Seek(0); + + if (!nSize) + { + // 0-size stream. Failed. + return false; + } + + try + { + tools::SvRef<SotStorage> xStorage = new SotStorage(pStream, false); + if (!xStorage.is() || xStorage->GetError()) + return false; + return xStorage->IsStream(rName); + } + catch (const css::ucb::ContentCreationException &) + { + TOOLS_WARN_EXCEPTION("sc", "hasStream"); + } + + return false; +} + +/** + * We detect BIFF 2, 3 and 4 file types together since the only thing that + * set them apart is the BOF ID. + */ +bool isExcel40(const uno::Reference<io::XInputStream>& xInStream) +{ + SfxMedium aMedium; + aMedium.UseInteractionHandler(false); + aMedium.setStreamToLoadFrom(xInStream, true); + SvStream* pStream = aMedium.GetInStream(); + if (!pStream) + return false; + + sal_uInt64 const nSize = pStream->TellEnd(); + pStream->Seek(0); + + if (nSize < 4) + return false; + + sal_uInt16 nBofId, nBofSize; + pStream->ReadUInt16( nBofId ).ReadUInt16( nBofSize ); + + switch (nBofId) + { + case 0x0009: // Excel 2.1 worksheet (BIFF 2) + case 0x0209: // Excel 3.0 worksheet (BIFF 3) + case 0x0409: // Excel 4.0 worksheet (BIFF 4) + case 0x0809: // Excel 5.0 worksheet (BIFF 5), some apps create such files (fdo#70100) + break; + default: + return false; + } + + if (nBofSize < 4 || 16 < nBofSize) + // BOF record must be sized between 4 and 16 for BIFF 2, 3 and 4. + return false; + + sal_uInt64 const nPos = pStream->Tell(); + if (nSize - nPos < nBofSize) + // BOF record doesn't have required bytes. + return false; + + return true; +} + +bool isTemplate(std::u16string_view rType) +{ + return rType.find(u"_VorlageTemplate") != std::u16string_view::npos; +} + +} + +OUString ScExcelBiffDetect::detect( uno::Sequence<beans::PropertyValue>& lDescriptor ) +{ + MediaDescriptor aMediaDesc(lDescriptor); + OUString aType; + aMediaDesc[MediaDescriptor::PROP_TYPENAME] >>= aType; + if (aType.isEmpty()) + // Type is not given. We can't proceed. + return OUString(); + + aMediaDesc.addInputStream(); + uno::Reference<io::XInputStream> xInStream(aMediaDesc[MediaDescriptor::PROP_INPUTSTREAM], uno::UNO_QUERY); + if (!xInStream.is()) + // No input stream. + return OUString(); + + if (aType == "calc_MS_Excel_97" || aType == "calc_MS_Excel_97_VorlageTemplate") + { + // See if this stream is an Excel 97/XP/2003 (BIFF8) stream. + if (!hasStream(xInStream, "Workbook")) + // BIFF8 is expected to contain a stream named "Workbook". + return OUString(); + + aMediaDesc[MediaDescriptor::PROP_FILTERNAME] <<= isTemplate(aType) ? OUString("MS Excel 97 Vorlage/Template") : OUString("MS Excel 97"); + } + + else if (aType == "calc_MS_Excel_95" || aType == "calc_MS_Excel_95_VorlageTemplate") + { + // See if this stream is an Excel 95 (BIFF5) stream. + if (!hasStream(xInStream, "Book")) + return OUString(); + + aMediaDesc[MediaDescriptor::PROP_FILTERNAME] <<= isTemplate(aType) ? OUString("MS Excel 95 Vorlage/Template") : OUString("MS Excel 95"); + } + + else if (aType == "calc_MS_Excel_5095" || aType == "calc_MS_Excel_5095_VorlageTemplate") + { + // See if this stream is an Excel 5.0/95 stream. + if (!hasStream(xInStream, "Book")) + return OUString(); + + aMediaDesc[MediaDescriptor::PROP_FILTERNAME] <<= isTemplate(aType) ? OUString("MS Excel 5.0/95 Vorlage/Template") : OUString("MS Excel 5.0/95"); + } + + else if (aType == "calc_MS_Excel_40" || aType == "calc_MS_Excel_40_VorlageTemplate") + { + // See if this stream is an Excel 4.0 stream. + if (!isExcel40(xInStream)) + return OUString(); + + aMediaDesc[MediaDescriptor::PROP_FILTERNAME] <<= isTemplate(aType) ? OUString("MS Excel 4.0 Vorlage/Template") : OUString("MS Excel 4.0"); + } + + else + // Nothing to detect. + return OUString(); + + aMediaDesc >> lDescriptor; + return aType; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_calc_ExcelBiffFormatDetector_get_implementation(css::uno::XComponentContext* /*context*/, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new ScExcelBiffDetect); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/unoobj/exceldetect.hxx b/sc/source/ui/unoobj/exceldetect.hxx new file mode 100644 index 0000000000..7189637526 --- /dev/null +++ b/sc/source/ui/unoobj/exceldetect.hxx @@ -0,0 +1,34 @@ +/* -*- 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/. + */ + +#pragma once + +#include <cppuhelper/implbase.hxx> + +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/document/XExtendedFilterDetection.hpp> + +class ScExcelBiffDetect + : public cppu::WeakImplHelper<css::document::XExtendedFilterDetection, css::lang::XServiceInfo> +{ +public: + explicit ScExcelBiffDetect(); + virtual ~ScExcelBiffDetect() override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService(const OUString& aName) override; + virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; + + // XExtendedFilterDetection + virtual OUString SAL_CALL + detect(css::uno::Sequence<css::beans::PropertyValue>& lDescriptor) override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/unoobj/fielduno.cxx b/sc/source/ui/unoobj/fielduno.cxx new file mode 100644 index 0000000000..bdd3271a8b --- /dev/null +++ b/sc/source/ui/unoobj/fielduno.cxx @@ -0,0 +1,1289 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <memory> +#include <fielduno.hxx> +#include <textuno.hxx> +#include <miscuno.hxx> +#include <docsh.hxx> +#include <hints.hxx> +#include <editsrc.hxx> +#include <unonames.hxx> +#include <editutil.hxx> + +#include <svl/hint.hxx> +#include <utility> +#include <vcl/svapp.hxx> + +#include <editeng/eeitem.hxx> + +#include <editeng/editeng.hxx> +#include <editeng/editobj.hxx> +#include <editeng/flditem.hxx> +#include <comphelper/sequence.hxx> +#include <cppuhelper/supportsservice.hxx> + +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/text/TextContentAnchorType.hpp> +#include <com/sun/star/text/WrapTextMode.hpp> +#include <com/sun/star/text/FilenameDisplayFormat.hpp> +#include <com/sun/star/text/textfield/Type.hpp> + +using namespace com::sun::star; + +namespace { + +// no Which-ID here, map only for PropertySetInfo + +const SfxItemPropertySet* getDateTimePropertySet() +{ + static const SfxItemPropertyMapEntry aMapContent[] = + { + { SC_UNONAME_DATETIME, 0, cppu::UnoType<util::DateTime>::get(), 0, 0 }, + { SC_UNONAME_ISFIXED, 0, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_ISDATE, 0, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNONAME_NUMFMT, 0, cppu::UnoType<sal_Int32>::get(), 0, 0 }, + }; + static SfxItemPropertySet aMap(aMapContent); + return &aMap; +} + +const SfxItemPropertySet* getEmptyPropertySet() +{ + static SfxItemPropertySet aMap({}); + return &aMap; +} + +const SfxItemPropertySet* lcl_GetURLPropertySet() +{ + static const SfxItemPropertyMapEntry aURLPropertyMap_Impl[] = + { + { SC_UNONAME_ANCTYPE, 0, cppu::UnoType<text::TextContentAnchorType>::get(), beans::PropertyAttribute::READONLY, 0 }, + { SC_UNONAME_ANCTYPES, 0, cppu::UnoType<uno::Sequence<text::TextContentAnchorType>>::get(), beans::PropertyAttribute::READONLY, 0 }, + { SC_UNONAME_REPR, 0, cppu::UnoType<OUString>::get(), 0, 0}, + { SC_UNONAME_TARGET, 0, cppu::UnoType<OUString>::get(), 0, 0}, + { SC_UNONAME_TEXTWRAP, 0, cppu::UnoType<text::WrapTextMode>::get(), beans::PropertyAttribute::READONLY, 0 }, + { SC_UNONAME_URL, 0, cppu::UnoType<OUString>::get(), 0, 0}, + }; + static SfxItemPropertySet aURLPropertySet_Impl( aURLPropertyMap_Impl ); + return &aURLPropertySet_Impl; +} + +const SfxItemPropertySet* lcl_GetHeaderFieldPropertySet() +{ + static const SfxItemPropertyMapEntry aHeaderFieldPropertyMap_Impl[] = + { + { SC_UNONAME_ANCTYPE, 0, cppu::UnoType<text::TextContentAnchorType>::get(), beans::PropertyAttribute::READONLY, 0 }, + { SC_UNONAME_ANCTYPES, 0, cppu::UnoType<uno::Sequence<text::TextContentAnchorType>>::get(), beans::PropertyAttribute::READONLY, 0 }, + { SC_UNONAME_TEXTWRAP, 0, cppu::UnoType<text::WrapTextMode>::get(), beans::PropertyAttribute::READONLY, 0 }, + }; + static SfxItemPropertySet aHeaderFieldPropertySet_Impl( aHeaderFieldPropertyMap_Impl ); + return &aHeaderFieldPropertySet_Impl; +} + +const SfxItemPropertySet* lcl_GetFileFieldPropertySet() +{ + static const SfxItemPropertyMapEntry aFileFieldPropertyMap_Impl[] = + { + { SC_UNONAME_ANCTYPE, 0, cppu::UnoType<text::TextContentAnchorType>::get(), beans::PropertyAttribute::READONLY, 0 }, + { SC_UNONAME_ANCTYPES, 0, cppu::UnoType<uno::Sequence<text::TextContentAnchorType>>::get(), beans::PropertyAttribute::READONLY, 0 }, + { SC_UNONAME_FILEFORM, 0, cppu::UnoType<sal_Int16>::get(), 0, 0 }, + { SC_UNONAME_TEXTWRAP, 0, cppu::UnoType<text::WrapTextMode>::get(), beans::PropertyAttribute::READONLY, 0 }, + }; + static SfxItemPropertySet aFileFieldPropertySet_Impl( aFileFieldPropertyMap_Impl ); + return &aFileFieldPropertySet_Impl; +} + +SvxFileFormat lcl_UnoToSvxFileFormat( sal_Int16 nUnoValue ) +{ + switch( nUnoValue ) + { + case text::FilenameDisplayFormat::FULL: return SvxFileFormat::PathFull; + case text::FilenameDisplayFormat::PATH: return SvxFileFormat::PathOnly; + case text::FilenameDisplayFormat::NAME: return SvxFileFormat::NameOnly; + default: + return SvxFileFormat::NameAndExt; + } +} + +sal_Int16 lcl_SvxToUnoFileFormat( SvxFileFormat nSvxValue ) +{ + switch( nSvxValue ) + { + case SvxFileFormat::NameAndExt: return text::FilenameDisplayFormat::NAME_AND_EXT; + case SvxFileFormat::PathFull: return text::FilenameDisplayFormat::FULL; + case SvxFileFormat::PathOnly: return text::FilenameDisplayFormat::PATH; + default: + return text::FilenameDisplayFormat::NAME; + } +} + +} + +SC_SIMPLE_SERVICE_INFO( ScCellFieldsObj, "ScCellFieldsObj", "com.sun.star.text.TextFields" ) +SC_SIMPLE_SERVICE_INFO( ScHeaderFieldsObj, "ScHeaderFieldsObj", "com.sun.star.text.TextFields" ) + +namespace { + +enum ScUnoCollectMode +{ + SC_UNO_COLLECT_NONE, + SC_UNO_COLLECT_COUNT, + SC_UNO_COLLECT_FINDINDEX, + SC_UNO_COLLECT_FINDPOS +}; + +/** + * This class exists solely to allow searching through field items. TODO: + * Look into providing the same functionality directly in EditEngine, to + * avoid having this class altogether. + */ +class ScUnoEditEngine : public ScEditEngineDefaulter +{ + ScUnoCollectMode eMode; + sal_uInt16 nFieldCount; + sal_Int32 mnFieldType; + std::unique_ptr<SvxFieldData> + pFound; // local copy + sal_Int32 nFieldPar; + sal_Int32 nFieldPos; + sal_uInt16 nFieldIndex; + +public: + explicit ScUnoEditEngine(ScEditEngineDefaulter* pSource); + + virtual OUString CalcFieldValue( const SvxFieldItem& rField, sal_Int32 nPara, sal_Int32 nPos, + std::optional<Color>& rTxtColor, std::optional<Color>& rFldColor, std::optional<FontLineStyle>& rFldLineStyle ) override; + + sal_uInt16 CountFields(); + SvxFieldData* FindByIndex(sal_uInt16 nIndex); + SvxFieldData* FindByPos(sal_Int32 nPar, sal_Int32 nPos, sal_Int32 nType); + + sal_Int32 GetFieldPar() const { return nFieldPar; } + sal_Int32 GetFieldPos() const { return nFieldPos; } +}; + +} + +ScUnoEditEngine::ScUnoEditEngine(ScEditEngineDefaulter* pSource) + : ScEditEngineDefaulter(*pSource) + , eMode(SC_UNO_COLLECT_NONE) + , nFieldCount(0) + , mnFieldType(text::textfield::Type::UNSPECIFIED) + , nFieldPar(0) + , nFieldPos(0) + , nFieldIndex(0) +{ + std::unique_ptr<EditTextObject> pData = pSource->CreateTextObject(); + SetTextCurrentDefaults( *pData ); +} + +OUString ScUnoEditEngine::CalcFieldValue( const SvxFieldItem& rField, + sal_Int32 nPara, sal_Int32 nPos, std::optional<Color>& rTxtColor, std::optional<Color>& rFldColor, std::optional<FontLineStyle>& rFldLineStyle ) +{ + OUString aRet(EditEngine::CalcFieldValue( rField, nPara, nPos, rTxtColor, rFldColor, rFldLineStyle )); + if (eMode != SC_UNO_COLLECT_NONE) + { + const SvxFieldData* pFieldData = rField.GetField(); + if ( pFieldData ) + { + if (mnFieldType == text::textfield::Type::UNSPECIFIED || pFieldData->GetClassId() == mnFieldType) + { + if ( eMode == SC_UNO_COLLECT_FINDINDEX && !pFound && nFieldCount == nFieldIndex ) + { + pFound = pFieldData->Clone(); + nFieldPar = nPara; + nFieldPos = nPos; + } + if ( eMode == SC_UNO_COLLECT_FINDPOS && !pFound && + nPara == nFieldPar && nPos == nFieldPos ) + { + pFound = pFieldData->Clone(); + nFieldIndex = nFieldCount; + } + ++nFieldCount; + } + } + } + return aRet; +} + +sal_uInt16 ScUnoEditEngine::CountFields() +{ + eMode = SC_UNO_COLLECT_COUNT; + mnFieldType = text::textfield::Type::UNSPECIFIED; + nFieldCount = 0; + UpdateFields(); + eMode = SC_UNO_COLLECT_NONE; + + return nFieldCount; +} + +SvxFieldData* ScUnoEditEngine::FindByIndex(sal_uInt16 nIndex) +{ + eMode = SC_UNO_COLLECT_FINDINDEX; + nFieldIndex = nIndex; + mnFieldType = text::textfield::Type::UNSPECIFIED; + nFieldCount = 0; + UpdateFields(); + eMode = SC_UNO_COLLECT_NONE; + + return pFound.get(); +} + +SvxFieldData* ScUnoEditEngine::FindByPos(sal_Int32 nPar, sal_Int32 nPos, sal_Int32 nType) +{ + eMode = SC_UNO_COLLECT_FINDPOS; + nFieldPar = nPar; + nFieldPos = nPos; + mnFieldType = nType; + nFieldCount = 0; + UpdateFields(); + mnFieldType = text::textfield::Type::UNSPECIFIED; + eMode = SC_UNO_COLLECT_NONE; + + return pFound.get(); +} + +ScCellFieldsObj::ScCellFieldsObj( + uno::Reference<text::XTextRange> xContent, + ScDocShell* pDocSh, const ScAddress& rPos) : + mxContent(std::move(xContent)), + pDocShell( pDocSh ), + aCellPos( rPos ) +{ + pDocShell->GetDocument().AddUnoObject(*this); + + mpEditSource.reset( new ScCellEditSource( pDocShell, aCellPos ) ); +} + +ScCellFieldsObj::~ScCellFieldsObj() +{ + { + SolarMutexGuard g; + + if (pDocShell) + pDocShell->GetDocument().RemoveUnoObject(*this); + + mpEditSource.reset(); + } + + // increment refcount to prevent double call off dtor + osl_atomic_increment( &m_refCount ); + + std::unique_lock g(aMutex); + if (maRefreshListeners.getLength(g)) + { + lang::EventObject aEvent; + aEvent.Source.set(getXWeak()); + maRefreshListeners.disposeAndClear(g, aEvent); + } +} + +void ScCellFieldsObj::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + if ( dynamic_cast<const ScUpdateRefHint*>(&rHint) ) + { + //! update of references + } + else if ( rHint.GetId() == SfxHintId::Dying ) + { + pDocShell = nullptr; // became invalid + } + + // EditSource registered itself as a listener +} + +// XIndexAccess (via XTextFields) + +uno::Reference<text::XTextField> ScCellFieldsObj::GetObjectByIndex_Impl(sal_Int32 Index) const +{ + //! Field functions have to be passed to the forwarder !!! + ScEditEngineDefaulter* pEditEngine = mpEditSource->GetEditEngine(); + ScUnoEditEngine aTempEngine(pEditEngine); + SvxFieldData* pData = aTempEngine.FindByIndex(static_cast<sal_uInt16>(Index)); + if (!pData) + return uno::Reference<text::XTextField>(); + + sal_Int32 nPar = aTempEngine.GetFieldPar(); + sal_Int32 nPos = aTempEngine.GetFieldPos(); + ESelection aSelection( nPar, nPos, nPar, nPos+1 ); // Field size is 1 character + + sal_Int32 eType = pData->GetClassId(); + uno::Reference<text::XTextField> xRet( + new ScEditFieldObj(mxContent, std::make_unique<ScCellEditSource>(pDocShell, aCellPos), eType, aSelection)); + return xRet; +} + +sal_Int32 SAL_CALL ScCellFieldsObj::getCount() +{ + SolarMutexGuard aGuard; + + //! Field functions have to be passed to the forwarder !!! + ScEditEngineDefaulter* pEditEngine = mpEditSource->GetEditEngine(); + ScUnoEditEngine aTempEngine(pEditEngine); + + return aTempEngine.CountFields(); // count the fields, we don't care about their type in the cell +} + +uno::Any SAL_CALL ScCellFieldsObj::getByIndex( sal_Int32 nIndex ) +{ + SolarMutexGuard aGuard; + uno::Reference<text::XTextField> xField(GetObjectByIndex_Impl(nIndex)); + if (!xField.is()) + throw lang::IndexOutOfBoundsException(); + + return uno::Any(xField); +} + +uno::Type SAL_CALL ScCellFieldsObj::getElementType() +{ + return cppu::UnoType<text::XTextField>::get(); +} + +sal_Bool SAL_CALL ScCellFieldsObj::hasElements() +{ + SolarMutexGuard aGuard; + return ( getCount() != 0 ); +} + +uno::Reference<container::XEnumeration> SAL_CALL ScCellFieldsObj::createEnumeration() +{ + SolarMutexGuard aGuard; + return new ScIndexEnumeration(this, "com.sun.star.text.TextFieldEnumeration"); +} + +void SAL_CALL ScCellFieldsObj::addContainerListener( + const uno::Reference<container::XContainerListener>& /* xListener */ ) +{ + OSL_FAIL("not implemented"); +} + +void SAL_CALL ScCellFieldsObj::removeContainerListener( + const uno::Reference<container::XContainerListener>& /* xListener */ ) +{ + OSL_FAIL("not implemented"); +} + +// XRefreshable +void SAL_CALL ScCellFieldsObj::refresh( ) +{ + std::unique_lock g(aMutex); + if (maRefreshListeners.getLength(g)) + { + // Call all listeners. + lang::EventObject aEvent; + aEvent.Source.set(uno::Reference< util::XRefreshable >(this)); + maRefreshListeners.notifyEach( g, &util::XRefreshListener::refreshed, aEvent ); + } +} + +void SAL_CALL ScCellFieldsObj::addRefreshListener( const uno::Reference< util::XRefreshListener >& xListener ) +{ + if (xListener.is()) + { + std::unique_lock g(aMutex); + maRefreshListeners.addInterface(g, xListener); + } +} + +void SAL_CALL ScCellFieldsObj::removeRefreshListener( const uno::Reference<util::XRefreshListener >& xListener ) +{ + if (xListener.is()) + { + std::unique_lock g(aMutex); + maRefreshListeners.removeInterface(g, xListener); + } +} + +ScHeaderFieldsObj::ScHeaderFieldsObj(ScHeaderFooterTextData& rData) : + mrData(rData) +{ + mpEditSource.reset( new ScHeaderFooterEditSource(rData) ); +} + +ScHeaderFieldsObj::~ScHeaderFieldsObj() +{ + mpEditSource.reset(); + + // increment refcount to prevent double call of dtor + osl_atomic_increment( &m_refCount ); + + std::unique_lock g(aMutex); + if (maRefreshListeners.getLength(g)) + { + lang::EventObject aEvent; + aEvent.Source = getXWeak(); + maRefreshListeners.disposeAndClear(g, aEvent); + } +} + +// XIndexAccess (via XTextFields) + +uno::Reference<text::XTextField> ScHeaderFieldsObj::GetObjectByIndex_Impl(sal_Int32 Index) const +{ + //! Field functions have to be passed to the forwarder !!! + ScEditEngineDefaulter* pEditEngine = mpEditSource->GetEditEngine(); + ScUnoEditEngine aTempEngine(pEditEngine); + + SvxFieldData* pData = aTempEngine.FindByIndex(static_cast<sal_uInt16>(Index)); + if (!pData) + return nullptr; + + // Get the parent text range instance. + uno::Reference<text::XTextRange> xTextRange; + uno::Reference<sheet::XHeaderFooterContent> xContentObj = mrData.GetContentObj(); + if (!xContentObj.is()) + throw uno::RuntimeException(""); + + rtl::Reference<ScHeaderFooterContentObj> pContentObj = ScHeaderFooterContentObj::getImplementation(xContentObj); + uno::Reference<text::XText> xText; + + switch ( mrData.GetPart() ) + { + case ScHeaderFooterPart::LEFT: + xText = pContentObj->getLeftText(); + break; + case ScHeaderFooterPart::CENTER: + xText = pContentObj->getCenterText(); + break; + case ScHeaderFooterPart::RIGHT: + xText = pContentObj->getRightText(); + break; + } + + xTextRange = xText; + + sal_Int32 nPar = aTempEngine.GetFieldPar(); + sal_Int32 nPos = aTempEngine.GetFieldPos(); + ESelection aSelection( nPar, nPos, nPar, nPos+1 ); // Field size is 1 character + + sal_Int32 eRealType = pData->GetClassId(); + uno::Reference<text::XTextField> xRet( + new ScEditFieldObj(xTextRange, std::make_unique<ScHeaderFooterEditSource>(mrData), eRealType, aSelection)); + return xRet; +} + +sal_Int32 SAL_CALL ScHeaderFieldsObj::getCount() +{ + SolarMutexGuard aGuard; + + //! Field functions have to be passed to the forwarder !!! + ScEditEngineDefaulter* pEditEngine = mpEditSource->GetEditEngine(); + ScUnoEditEngine aTempEngine(pEditEngine); + return aTempEngine.CountFields(); +} + +uno::Any SAL_CALL ScHeaderFieldsObj::getByIndex( sal_Int32 nIndex ) +{ + SolarMutexGuard aGuard; + uno::Reference<text::XTextField> xField(GetObjectByIndex_Impl(nIndex)); + if (!xField.is()) + throw lang::IndexOutOfBoundsException(); + + return uno::Any(xField); +} + +uno::Type SAL_CALL ScHeaderFieldsObj::getElementType() +{ + return cppu::UnoType<text::XTextField>::get(); +} + +sal_Bool SAL_CALL ScHeaderFieldsObj::hasElements() +{ + SolarMutexGuard aGuard; + return ( getCount() != 0 ); +} + +uno::Reference<container::XEnumeration> SAL_CALL ScHeaderFieldsObj::createEnumeration() +{ + SolarMutexGuard aGuard; + return new ScIndexEnumeration(this, "com.sun.star.text.TextFieldEnumeration"); +} + +void SAL_CALL ScHeaderFieldsObj::addContainerListener( + const uno::Reference<container::XContainerListener>& /* xListener */ ) +{ + OSL_FAIL("not implemented"); +} + +void SAL_CALL ScHeaderFieldsObj::removeContainerListener( + const uno::Reference<container::XContainerListener>& /* xListener */ ) +{ + OSL_FAIL("not implemented"); +} + +// XRefreshable +void SAL_CALL ScHeaderFieldsObj::refresh( ) +{ + std::unique_lock g(aMutex); + if (maRefreshListeners.getLength(g)) + { + // Call all listeners. + lang::EventObject aEvent; + aEvent.Source.set(uno::Reference< util::XRefreshable >(this)); + maRefreshListeners.notifyEach( g, &util::XRefreshListener::refreshed, aEvent); + } +} + +void SAL_CALL ScHeaderFieldsObj::addRefreshListener( const uno::Reference< util::XRefreshListener >& xListener ) +{ + if (xListener.is()) + { + std::unique_lock g(aMutex); + maRefreshListeners.addInterface(g, xListener); + } +} + +void SAL_CALL ScHeaderFieldsObj::removeRefreshListener( const uno::Reference<util::XRefreshListener >& xListener ) +{ + if (xListener.is()) + { + std::unique_lock g(aMutex); + maRefreshListeners.removeInterface(g, xListener); + } +} + +SvxFieldData& ScEditFieldObj::getData() +{ + if (!mpData) + { + switch (meType) + { + case text::textfield::Type::DATE: + mpData.reset(new SvxDateField); + break; + case text::textfield::Type::EXTENDED_FILE: + mpData.reset( + new SvxExtFileField(OUString(), SvxFileType::Var, SvxFileFormat::NameAndExt)); + break; + case text::textfield::Type::PAGE: + mpData.reset(new SvxPageField); + break; + case text::textfield::Type::PAGES: + mpData.reset(new SvxPagesField); + break; + case text::textfield::Type::TABLE: + mpData.reset(new SvxTableField); + break; + case text::textfield::Type::TIME: + mpData.reset(new SvxTimeField); + break; + case text::textfield::Type::EXTENDED_TIME: + { + if (mbIsDate) + mpData.reset(new SvxDateField); + else + mpData.reset(new SvxExtTimeField); + } + break; + case text::textfield::Type::DOCINFO_TITLE: + mpData.reset(new SvxFileField); + break; + case text::textfield::Type::URL: + mpData.reset( + new SvxURLField(OUString(), OUString(), SvxURLFormat::AppDefault)); + break; + default: + mpData.reset(new SvxFieldData); + } + } + return *mpData; +} + +void ScEditFieldObj::setPropertyValueURL(const OUString& rName, const css::uno::Any& rVal) +{ + OUString aStrVal; + if (mpEditSource) + { + // Edit engine instance already exists for this field item. Use it. + ScEditEngineDefaulter* pEditEngine = mpEditSource->GetEditEngine(); + ScUnoEditEngine aTempEngine(pEditEngine); + + // don't care about the type (only URLs can be found in the cells) + SvxFieldData* pField = aTempEngine.FindByPos( + aSelection.nStartPara, aSelection.nStartPos, text::textfield::Type::UNSPECIFIED); + OSL_ENSURE(pField,"setPropertyValue: Field not found"); + if (!pField) + return; + + if (pField->GetClassId() != text::textfield::Type::URL) + // Make sure this is indeed a URL field. + return; + + SvxURLField* pURL = static_cast<SvxURLField*>(pField); + + if (rName == SC_UNONAME_URL) + { + if (rVal >>= aStrVal) + pURL->SetURL(aStrVal); + } + else if (rName == SC_UNONAME_REPR) + { + if (rVal >>= aStrVal) + pURL->SetRepresentation(aStrVal); + } + else if (rName == SC_UNONAME_TARGET) + { + if (rVal >>= aStrVal) + pURL->SetTargetFrame(aStrVal); + } + else + throw beans::UnknownPropertyException(rName); + + pEditEngine->QuickInsertField( SvxFieldItem(*pField, EE_FEATURE_FIELD), aSelection ); + mpEditSource->UpdateData(); + return; + } + + // Edit engine instance not yet present. Store the item data for later use. + SvxURLField& rData = static_cast<SvxURLField&>(getData()); + if (rName == SC_UNONAME_URL) + { + if (rVal >>= aStrVal) + rData.SetURL(aStrVal); + } + else if (rName == SC_UNONAME_REPR) + { + if (rVal >>= aStrVal) + rData.SetRepresentation(aStrVal); + } + else if (rName == SC_UNONAME_TARGET) + { + if (rVal >>= aStrVal) + rData.SetTargetFrame(aStrVal); + } + else + throw beans::UnknownPropertyException(rName); +} + +uno::Any ScEditFieldObj::getPropertyValueURL(const OUString& rName) +{ + uno::Any aRet; + + // anchor type is always "as character", text wrap always "none" + + if (mpEditSource) + { + //! Field functions have to be passed to the forwarder !!! + ScEditEngineDefaulter* pEditEngine = mpEditSource->GetEditEngine(); + ScUnoEditEngine aTempEngine(pEditEngine); + + // don't care about the type (only URLs can be found in the cells) + const SvxFieldData* pField = aTempEngine.FindByPos( + aSelection.nStartPara, aSelection.nStartPos, text::textfield::Type::UNSPECIFIED); + OSL_ENSURE(pField,"getPropertyValue: Field not found"); + if (!pField) + throw uno::RuntimeException(); + + if (pField->GetClassId() != text::textfield::Type::URL) + throw uno::RuntimeException(); + + const SvxURLField* pURL = static_cast<const SvxURLField*>(pField); + + if (rName == SC_UNONAME_URL) + aRet <<= pURL->GetURL(); + else if (rName == SC_UNONAME_REPR) + aRet <<= pURL->GetRepresentation(); + else if (rName == SC_UNONAME_TARGET) + aRet <<= pURL->GetTargetFrame(); + else + throw beans::UnknownPropertyException(rName); + } + else // not inserted yet + { + const SvxURLField& rURL = static_cast<const SvxURLField&>(getData()); + + if (rName == SC_UNONAME_URL) + aRet <<= rURL.GetURL(); + else if (rName == SC_UNONAME_REPR) + aRet <<= rURL.GetRepresentation(); + else if (rName == SC_UNONAME_TARGET) + aRet <<= rURL.GetTargetFrame(); + else + throw beans::UnknownPropertyException(rName); + } + return aRet; +} + +void ScEditFieldObj::setPropertyValueFile(const OUString& rName, const uno::Any& rVal) +{ + if (rName != SC_UNONAME_FILEFORM) + throw beans::UnknownPropertyException(rName); + + sal_Int16 nIntVal = 0; + if (!(rVal >>= nIntVal)) + return; + + SvxFileFormat eFormat = lcl_UnoToSvxFileFormat(nIntVal); + if (mpEditSource) + { + ScEditEngineDefaulter* pEditEngine = mpEditSource->GetEditEngine(); + ScUnoEditEngine aTempEngine(pEditEngine); + SvxFieldData* pField = aTempEngine.FindByPos( + aSelection.nStartPara, aSelection.nStartPos, text::textfield::Type::EXTENDED_FILE); + OSL_ENSURE(pField, "setPropertyValueFile: Field not found"); + if (pField) + { + SvxExtFileField* pExtFile = static_cast<SvxExtFileField*>(pField); // local to the ScUnoEditEngine + pExtFile->SetFormat(eFormat); + pEditEngine->QuickInsertField(SvxFieldItem(*pField, EE_FEATURE_FIELD), aSelection); + mpEditSource->UpdateData(); + } + } + else + { + SvxExtFileField& rExtFile = static_cast<SvxExtFileField&>(getData()); + rExtFile.SetFormat(eFormat); + } + +} + +uno::Any ScEditFieldObj::getPropertyValueFile(const OUString& rName) +{ + uno::Any aRet; + if (rName != SC_UNONAME_FILEFORM) + throw beans::UnknownPropertyException(rName); + + SvxFileFormat eFormat = SvxFileFormat::NameAndExt; + const SvxFieldData* pField = nullptr; + if (mpEditSource) + { + ScEditEngineDefaulter* pEditEngine = mpEditSource->GetEditEngine(); + ScUnoEditEngine aTempEngine(pEditEngine); + pField = aTempEngine.FindByPos( + aSelection.nStartPara, aSelection.nStartPos, text::textfield::Type::EXTENDED_FILE); + } + else + pField = &getData(); + + OSL_ENSURE(pField, "setPropertyValueFile: Field not found"); + if (!pField) + throw uno::RuntimeException(); + + const SvxExtFileField* pExtFile = static_cast<const SvxExtFileField*>(pField); + eFormat = pExtFile->GetFormat(); + sal_Int16 nIntVal = lcl_SvxToUnoFileFormat(eFormat); + aRet <<= nIntVal; + + return aRet; +} + +void ScEditFieldObj::setPropertyValueDateTime(const OUString& rName, const uno::Any& rVal) +{ + if (mpEditSource) + { + // Field already inserted. + ScEditEngineDefaulter* pEditEngine = mpEditSource->GetEditEngine(); + ScUnoEditEngine aTempEngine(pEditEngine); + SvxFieldData* pField = aTempEngine.FindByPos(aSelection.nStartPara, aSelection.nStartPos, meType); + if (!pField) + return; + + switch (meType) + { + case text::textfield::Type::DATE: + { + SvxDateField* p = static_cast<SvxDateField*>(pField); + if (rName == SC_UNONAME_ISDATE) + { + // Do nothing for now. + } + else if (rName == SC_UNONAME_ISFIXED) + { + SvxDateType eType = rVal.get<bool>() ? SvxDateType::Fix : SvxDateType::Var; + p->SetType(eType); + } + else if (rName == SC_UNONAME_DATETIME) + { + maDateTime = rVal.get<util::DateTime>(); + Date aDate(maDateTime.Day, maDateTime.Month, maDateTime.Year); + p->SetFixDate(aDate); + } + else if (rName == SC_UNONAME_NUMFMT) + { + mnNumFormat = rVal.get<sal_Int32>(); + p->SetFormat(static_cast<SvxDateFormat>(mnNumFormat)); + } + else + throw beans::UnknownPropertyException(rName); + } + break; + case text::textfield::Type::TIME: + { + // SvxTimeField doesn't have any attributes. + if (rName != SC_UNONAME_ISDATE && rName != SC_UNONAME_ISFIXED && + rName != SC_UNONAME_DATETIME && rName != SC_UNONAME_NUMFMT) + throw beans::UnknownPropertyException(rName); + } + break; + case text::textfield::Type::EXTENDED_TIME: + { + SvxExtTimeField* p = static_cast<SvxExtTimeField*>(pField); + if (rName == SC_UNONAME_ISDATE) + { + // Do nothing for now. + } + else if (rName == SC_UNONAME_ISFIXED) + { + SvxTimeType eType = rVal.get<bool>() ? SvxTimeType::Fix : SvxTimeType::Var; + p->SetType(eType); + } + else if (rName == SC_UNONAME_DATETIME) + { + maDateTime = rVal.get<util::DateTime>(); + tools::Time aTime(maDateTime); + p->SetFixTime(aTime); + } + else if (rName == SC_UNONAME_NUMFMT) + { + mnNumFormat = rVal.get<sal_Int32>(); + p->SetFormat(static_cast<SvxTimeFormat>(mnNumFormat)); + } + else + throw beans::UnknownPropertyException(rName); + } + break; + default: + throw beans::UnknownPropertyException(rName); + } + } + else + { + if (rName == SC_UNONAME_ISDATE) + mbIsDate = rVal.get<bool>(); + else if (rName == SC_UNONAME_ISFIXED) + mbIsFixed = rVal.get<bool>(); + else if (rName == SC_UNONAME_DATETIME) + maDateTime = rVal.get<util::DateTime>(); + else if (rName == SC_UNONAME_NUMFMT) + mnNumFormat = rVal.get<sal_Int32>(); + else + throw beans::UnknownPropertyException(rName); + } +} + +uno::Any ScEditFieldObj::getPropertyValueDateTime(const OUString& rName) +{ + if (mpEditSource) + { + // Field already inserted. + ScEditEngineDefaulter* pEditEngine = mpEditSource->GetEditEngine(); + ScUnoEditEngine aTempEngine(pEditEngine); + SvxFieldData* pField = aTempEngine.FindByPos(aSelection.nStartPara, aSelection.nStartPos, meType); + if (!pField) + throw uno::RuntimeException(); + + switch (meType) + { + case text::textfield::Type::DATE: + { + SvxDateField* p = static_cast<SvxDateField*>(pField); + if (rName == SC_UNONAME_ISDATE) + return uno::Any(true); + + if (rName == SC_UNONAME_ISFIXED) + return uno::Any(p->GetType() == SvxDateType::Fix); + + if (rName == SC_UNONAME_DATETIME) + { + Date aD(p->GetFixDate()); + maDateTime.Year = aD.GetYear(); + maDateTime.Month = aD.GetMonth(); + maDateTime.Day = aD.GetDay(); + maDateTime.Hours = 0; + maDateTime.Minutes = 0; + maDateTime.Seconds = 0; + maDateTime.NanoSeconds = 0; + return uno::Any(maDateTime); + } + + if (rName == SC_UNONAME_NUMFMT) + return uno::Any(static_cast<sal_Int32>(p->GetFormat())); + } + break; + case text::textfield::Type::TIME: + { + // SvxTimeField doesn't have any attributes. + if (rName == SC_UNONAME_ISDATE) + return uno::Any(false); + + if (rName == SC_UNONAME_ISFIXED) + return uno::Any(false); + + if (rName == SC_UNONAME_DATETIME) + // This is the best we can do. + return uno::Any(maDateTime); + + if (rName == SC_UNONAME_NUMFMT) + // Same as above. + return uno::Any(sal_Int32(0)); + } + break; + case text::textfield::Type::EXTENDED_TIME: + { + SvxExtTimeField* p = static_cast<SvxExtTimeField*>(pField); + if (rName == SC_UNONAME_ISDATE) + return uno::Any(false); + + if (rName == SC_UNONAME_ISFIXED) + return uno::Any(p->GetType() == SvxTimeType::Fix); + + if (rName == SC_UNONAME_DATETIME) + { + tools::Time aT(p->GetFixTime()); + maDateTime.Year = 0; + maDateTime.Month = 0; + maDateTime.Day = 0; + maDateTime.Hours = aT.GetHour(); + maDateTime.Minutes = aT.GetMin(); + maDateTime.Seconds = aT.GetSec(); + maDateTime.NanoSeconds = aT.GetNanoSec(); + return uno::Any(maDateTime); + } + + if (rName == SC_UNONAME_NUMFMT) + return uno::Any(static_cast<sal_Int32>(p->GetFormat())); + } + break; + default: + ; + } + } + else + { + if (rName == SC_UNONAME_ISDATE) + return uno::Any(mbIsDate); + + if (rName == SC_UNONAME_ISFIXED) + return uno::Any(mbIsFixed); + + if (rName == SC_UNONAME_DATETIME) + return uno::Any(maDateTime); + + if (rName == SC_UNONAME_NUMFMT) + return uno::Any(mnNumFormat); + } + + throw beans::UnknownPropertyException(rName); +} + +void ScEditFieldObj::setPropertyValueSheet(const OUString& rName, const uno::Any& rVal) +{ + if (mpEditSource) + { + // Edit engine instance already exists for this field item. Use it. + ScEditEngineDefaulter* pEditEngine = mpEditSource->GetEditEngine(); + ScUnoEditEngine aTempEngine(pEditEngine); + + // don't care about the type (only URLs can be found in the cells) + SvxFieldData* pField = aTempEngine.FindByPos( + aSelection.nStartPara, aSelection.nStartPos, text::textfield::Type::UNSPECIFIED); + OSL_ENSURE(pField,"setPropertyValue: Field not found"); + if (!pField) + return; + + if (pField->GetClassId() != text::textfield::Type::TABLE) + // Make sure this is indeed a URL field. + return; + + SvxTableField* p = static_cast<SvxTableField*>(pField); + + if (rName != SC_UNONAME_TABLEPOS) + throw beans::UnknownPropertyException(rName); + + sal_Int32 nTab = rVal.get<sal_Int32>(); + p->SetTab(nTab); + + + pEditEngine->QuickInsertField(SvxFieldItem(*pField, EE_FEATURE_FIELD), aSelection); + mpEditSource->UpdateData(); + return; + } + + // Edit engine instance not yet present. Store the item data for later use. + SvxTableField& r = static_cast<SvxTableField&>(getData()); + if (rName != SC_UNONAME_TABLEPOS) + throw beans::UnknownPropertyException(rName); + + sal_Int32 nTab = rVal.get<sal_Int32>(); + r.SetTab(nTab); +} + +ScEditFieldObj::ScEditFieldObj( + uno::Reference<text::XTextRange> xContent, + std::unique_ptr<ScEditSource> pEditSrc, sal_Int32 eType, const ESelection& rSel) : + pPropSet(nullptr), + mpEditSource(std::move(pEditSrc)), + aSelection(rSel), + meType(eType), mpContent(std::move(xContent)), mnNumFormat(0), mbIsDate(false), mbIsFixed(false) +{ + switch (meType) + { + case text::textfield::Type::DOCINFO_TITLE: + pPropSet = getEmptyPropertySet(); + break; + case text::textfield::Type::EXTENDED_FILE: + pPropSet = lcl_GetFileFieldPropertySet(); + break; + case text::textfield::Type::URL: + pPropSet = lcl_GetURLPropertySet(); + break; + case text::textfield::Type::DATE: + case text::textfield::Type::TIME: + case text::textfield::Type::EXTENDED_TIME: + pPropSet = getDateTimePropertySet(); + break; + default: + pPropSet = lcl_GetHeaderFieldPropertySet(); + } + + if (meType == text::textfield::Type::DATE) + mbIsDate = true; +} + +void ScEditFieldObj::InitDoc( + const uno::Reference<text::XTextRange>& rContent, std::unique_ptr<ScEditSource> pEditSrc, const ESelection& rSel) +{ + if (!mpEditSource) + { + mpContent = rContent; + mpData.reset(); + + aSelection = rSel; + mpEditSource = std::move( pEditSrc ); + } +} + +ScEditFieldObj::~ScEditFieldObj() +{ +} + +SvxFieldItem ScEditFieldObj::CreateFieldItem() +{ + OSL_ENSURE( !mpEditSource, "CreateFieldItem with inserted field" ); + return SvxFieldItem(getData(), EE_FEATURE_FIELD); +} + +void ScEditFieldObj::DeleteField() +{ + if (mpEditSource) + { + SvxTextForwarder* pForwarder = mpEditSource->GetTextForwarder(); + pForwarder->QuickInsertText( OUString(), aSelection ); + mpEditSource->UpdateData(); + + aSelection.nEndPara = aSelection.nStartPara; + aSelection.nEndPos = aSelection.nStartPos; + + //! Broadcast in order to adjust selection in other objects + //! (also for other actions) + } +} + +bool ScEditFieldObj::IsInserted() const +{ + return mpEditSource != nullptr; +} + +// XTextField + +OUString SAL_CALL ScEditFieldObj::getPresentation( sal_Bool bShowCommand ) +{ + SolarMutexGuard aGuard; + + if (!mpEditSource) + return OUString(); + + //! Field functions have to be passed to the forwarder !!! + ScEditEngineDefaulter* pEditEngine = mpEditSource->GetEditEngine(); + ScUnoEditEngine aTempEngine(pEditEngine); + + // don't care about the type (only URLs can be found in the cells) + const SvxFieldData* pField = aTempEngine.FindByPos( + aSelection.nStartPara, aSelection.nStartPos, text::textfield::Type::UNSPECIFIED); + OSL_ENSURE(pField,"getPresentation: Field not found"); + if (!pField) + return OUString(); + + switch (meType) + { + case text::textfield::Type::URL: + { + if (pField->GetClassId() != text::textfield::Type::URL) + // Not a URL field, but URL is expected. + throw uno::RuntimeException(); + + const SvxURLField* pURL = static_cast<const SvxURLField*>(pField); + return bShowCommand ? pURL->GetURL() : pURL->GetRepresentation(); + } + break; + default: + ; + } + return OUString(); +} + +// XTextContent + +void SAL_CALL ScEditFieldObj::attach( const uno::Reference<text::XTextRange>& xTextRange ) +{ + SolarMutexGuard aGuard; + if (xTextRange.is()) + { + uno::Reference<text::XText> xText(xTextRange->getText()); + if (xText.is()) + { + xText->insertTextContent( xTextRange, this, true ); + } + } +} + +uno::Reference<text::XTextRange> SAL_CALL ScEditFieldObj::getAnchor() +{ + SolarMutexGuard aGuard; + return mpContent; +} + +// XPropertySet + +uno::Reference<beans::XPropertySetInfo> SAL_CALL ScEditFieldObj::getPropertySetInfo() +{ + SolarMutexGuard aGuard; + uno::Reference<beans::XPropertySetInfo> aRef = pPropSet->getPropertySetInfo(); + return aRef; +} + +void SAL_CALL ScEditFieldObj::setPropertyValue( + const OUString& aPropertyName, const uno::Any& aValue ) +{ + SolarMutexGuard aGuard; + if (aPropertyName == SC_UNONAME_ANCHOR) + { + aValue >>= mpContent; + return; + } + + switch (meType) + { + case text::textfield::Type::URL: + setPropertyValueURL(aPropertyName, aValue); + break; + case text::textfield::Type::EXTENDED_FILE: + setPropertyValueFile(aPropertyName, aValue); + break; + case text::textfield::Type::DATE: + case text::textfield::Type::TIME: + case text::textfield::Type::EXTENDED_TIME: + setPropertyValueDateTime(aPropertyName, aValue); + break; + case text::textfield::Type::TABLE: + setPropertyValueSheet(aPropertyName, aValue); + break; + case text::textfield::Type::DOCINFO_TITLE: + default: + throw beans::UnknownPropertyException(OUString::number(meType)); + } +} + +uno::Any SAL_CALL ScEditFieldObj::getPropertyValue( const OUString& aPropertyName ) +{ + SolarMutexGuard aGuard; + if (aPropertyName == SC_UNONAME_TEXTFIELD_TYPE) + return uno::Any(meType); + + if (aPropertyName == SC_UNONAME_ANCHOR) + return uno::Any(mpContent); + + if (aPropertyName == SC_UNONAME_ANCTYPE) + { + uno::Any aRet; + aRet <<= text::TextContentAnchorType_AS_CHARACTER; + return aRet; + } + if (aPropertyName == SC_UNONAME_ANCTYPES) + { + uno::Any aRet; + uno::Sequence<text::TextContentAnchorType> aSeq { text::TextContentAnchorType_AS_CHARACTER }; + aRet <<= aSeq; + return aRet; + } + if (aPropertyName == SC_UNONAME_TEXTWRAP) + { + uno::Any aRet; + aRet <<= text::WrapTextMode_NONE; + return aRet; + } + + switch (meType) + { + case text::textfield::Type::URL: + return getPropertyValueURL(aPropertyName); + case text::textfield::Type::EXTENDED_FILE: + return getPropertyValueFile(aPropertyName); + case text::textfield::Type::DATE: + case text::textfield::Type::TIME: + case text::textfield::Type::EXTENDED_TIME: + return getPropertyValueDateTime(aPropertyName); + case text::textfield::Type::DOCINFO_TITLE: + default: + throw beans::UnknownPropertyException(OUString::number(meType)); + } +} + +SC_IMPL_DUMMY_PROPERTY_LISTENER( ScEditFieldObj ) + +// XServiceInfo + +OUString SAL_CALL ScEditFieldObj::getImplementationName() +{ + return "ScEditFieldObj"; +} + +sal_Bool SAL_CALL ScEditFieldObj::supportsService( const OUString& rServiceName ) +{ + return cppu::supportsService(this, rServiceName); +} + +uno::Sequence<OUString> SAL_CALL ScEditFieldObj::getSupportedServiceNames() +{ + return {"com.sun.star.text.TextField", + "com.sun.star.text.TextContent"}; +} + +uno::Sequence<uno::Type> SAL_CALL ScEditFieldObj::getTypes() +{ + return comphelper::concatSequences( + ScEditFieldObj_Base::getTypes(), + uno::Sequence<uno::Type> + { + cppu::UnoType<text::XTextField>::get(), + cppu::UnoType<beans::XPropertySet>::get(), + cppu::UnoType<lang::XUnoTunnel>::get(), + cppu::UnoType<lang::XServiceInfo>::get() + } ); +} + +uno::Sequence<sal_Int8> SAL_CALL ScEditFieldObj::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/unoobj/filtuno.cxx b/sc/source/ui/unoobj/filtuno.cxx new file mode 100644 index 0000000000..fa520e23ae --- /dev/null +++ b/sc/source/ui/unoobj/filtuno.cxx @@ -0,0 +1,371 @@ +/* -*- 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 <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp> +#include <tools/urlobj.hxx> +#include <vcl/svapp.hxx> +#include <unotools/ucbstreamhelper.hxx> +#include <connectivity/dbtools.hxx> +#include <osl/diagnose.h> + +#include <filtuno.hxx> +#include <miscuno.hxx> +#include <scdll.hxx> +#include <imoptdlg.hxx> +#include <asciiopt.hxx> +#include <docsh.hxx> +#include <globstr.hrc> +#include <scresid.hxx> + +#include <scabstdlg.hxx> +#include <i18nlangtag/lang.h> + +#include <optutil.hxx> +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/uno/Sequence.hxx> +#include <comphelper/namedvaluecollection.hxx> +#include <comphelper/propertysequence.hxx> +#include <memory> + +using namespace com::sun::star; +using namespace com::sun::star::uno; +using namespace connectivity::dbase; + +constexpr OUString SCFILTEROPTIONSOBJ_SERVICE = u"com.sun.star.ui.dialogs.FilterOptionsDialog"_ustr; +constexpr OUStringLiteral SCFILTEROPTIONSOBJ_IMPLNAME = u"com.sun.star.comp.Calc.FilterOptionsDialog"; + +SC_SIMPLE_SERVICE_INFO( ScFilterOptionsObj, SCFILTEROPTIONSOBJ_IMPLNAME, SCFILTEROPTIONSOBJ_SERVICE ) + +constexpr OUStringLiteral SC_UNONAME_FILENAME = u"URL"; +constexpr OUStringLiteral SC_UNONAME_FILTERNAME = u"FilterName"; +constexpr OUString SC_UNONAME_FILTEROPTIONS = u"FilterOptions"_ustr; +constexpr OUStringLiteral SC_UNONAME_INPUTSTREAM = u"InputStream"; + +constexpr OUString DBF_CHAR_SET = u"CharSet"_ustr; +constexpr OUString DBF_SEP_PATH_IMPORT = u"Office.Calc/Dialogs/DBFImport"_ustr; +constexpr OUString DBF_SEP_PATH_EXPORT = u"Office.Calc/Dialogs/DBFExport"_ustr; + +namespace +{ + + enum class charsetSource + { + charset_from_file, + charset_from_user_setting, + charset_default + }; + + charsetSource load_CharSet(rtl_TextEncoding &nCharSet, bool bExport, SvStream* dbf_Stream) + { + if (dbf_Stream && dbfReadCharset(nCharSet, dbf_Stream)) + { + return charsetSource::charset_from_file; + } + + Sequence<Any> aValues; + const Any *pProperties; + Sequence<OUString> aNames { DBF_CHAR_SET }; + ScLinkConfigItem aItem( bExport ? DBF_SEP_PATH_EXPORT : DBF_SEP_PATH_IMPORT ); + + aValues = aItem.GetProperties( aNames ); + pProperties = aValues.getConstArray(); + + if( pProperties[0].hasValue() ) + { + sal_Int32 nChar = 0; + pProperties[0] >>= nChar; + if( nChar >= 0) + { + nCharSet = static_cast<rtl_TextEncoding>(nChar); + return charsetSource::charset_from_user_setting; + } + } + + // Default choice + nCharSet = RTL_TEXTENCODING_IBM_850; + return charsetSource::charset_default; + } + + void save_CharSet( rtl_TextEncoding nCharSet, bool bExport ) + { + Sequence<Any> aValues; + Any *pProperties; + Sequence<OUString> aNames { DBF_CHAR_SET }; + ScLinkConfigItem aItem( bExport ? DBF_SEP_PATH_EXPORT : DBF_SEP_PATH_IMPORT ); + + aValues = aItem.GetProperties( aNames ); + pProperties = aValues.getArray(); + pProperties[0] <<= static_cast<sal_Int32>(nCharSet); + + aItem.PutProperties(aNames, aValues); + } +} + +ScFilterOptionsObj::ScFilterOptionsObj() : + bExport( false ) +{ +} + +ScFilterOptionsObj::~ScFilterOptionsObj() +{ +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +Calc_FilterOptionsDialog_get_implementation(css::uno::XComponentContext*, css::uno::Sequence<css::uno::Any> const &) +{ + SolarMutexGuard aGuard; + ScDLL::Init(); + return cppu::acquire(new ScFilterOptionsObj); +} + +// XPropertyAccess + +uno::Sequence<beans::PropertyValue> SAL_CALL ScFilterOptionsObj::getPropertyValues() +{ + return comphelper::InitPropertySequence({ + { SC_UNONAME_FILTEROPTIONS, Any(aFilterOptions) } + }); +} + +void SAL_CALL ScFilterOptionsObj::setPropertyValues( const uno::Sequence<beans::PropertyValue>& aProps ) +{ + for (const beans::PropertyValue& rProp : aProps) + { + OUString aPropName(rProp.Name); + + if ( aPropName == SC_UNONAME_FILENAME ) + rProp.Value >>= aFileName; + else if ( aPropName == SC_UNONAME_FILTERNAME ) + rProp.Value >>= aFilterName; + else if ( aPropName == SC_UNONAME_FILTEROPTIONS ) + rProp.Value >>= aFilterOptions; + else if ( aPropName == SC_UNONAME_INPUTSTREAM ) + rProp.Value >>= xInputStream; + } +} + +// XExecutableDialog + +void SAL_CALL ScFilterOptionsObj::setTitle( const OUString& /* aTitle */ ) +{ + // not used +} + +sal_Int16 SAL_CALL ScFilterOptionsObj::execute() +{ + sal_Int16 nRet = ui::dialogs::ExecutableDialogResults::CANCEL; + + OUString aFilterString( aFilterName ); + + ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create(); + + if ( !bExport && aFilterString == ScDocShell::GetAsciiFilterName() ) + { + // ascii import is special... + + INetURLObject aURL( aFileName ); + // tdf#132421 - don't URL encode filename for the import ASCII dialog title + OUString aPrivDatName(aURL.GetLastName(INetURLObject::DecodeMechanism::Unambiguous)); + std::unique_ptr<SvStream> pInStream; + if ( xInputStream.is() ) + pInStream = utl::UcbStreamHelper::CreateStream( xInputStream ); + + ScopedVclPtr<AbstractScImportAsciiDlg> pDlg(pFact->CreateScImportAsciiDlg(Application::GetFrameWeld(xDialogParent), aPrivDatName, + pInStream.get(), SC_IMPORTFILE)); + if ( pDlg->Execute() == RET_OK ) + { + ScAsciiOptions aOptions; + pDlg->GetOptions( aOptions ); + pDlg->SaveParameters(); + aFilterOptions = aOptions.WriteToString(); + nRet = ui::dialogs::ExecutableDialogResults::OK; + } + } + else if ( aFilterString == ScDocShell::GetWebQueryFilterName() || aFilterString == ScDocShell::GetHtmlFilterName() ) + { + if (bExport) + nRet = ui::dialogs::ExecutableDialogResults::OK; // export HTML without dialog + else + { + // HTML import. + ScopedVclPtr<AbstractScTextImportOptionsDlg> pDlg( + pFact->CreateScTextImportOptionsDlg(Application::GetFrameWeld(xDialogParent))); + + if (pDlg->Execute() == RET_OK) + { + LanguageType eLang = pDlg->GetLanguageType(); + OUStringBuffer aBuf; + + aBuf.append(static_cast<sal_Int32>(static_cast<sal_uInt16>(eLang))); + aBuf.append(' '); + aBuf.append(pDlg->IsDateConversionSet() ? u'1' : u'0'); + aBuf.append(' '); + aBuf.append(pDlg->IsScientificConversionSet() ? u'1' : u'0'); + aFilterOptions = aBuf.makeStringAndClear(); + nRet = ui::dialogs::ExecutableDialogResults::OK; + } + } + } + else + { + bool bDBEnc = false; + bool bAscii = false; + bool skipDialog = false; + + sal_Unicode const cStrDel = '"'; + sal_Unicode cAsciiDel = ';'; + rtl_TextEncoding eEncoding = RTL_TEXTENCODING_DONTKNOW; + + OUString aTitle; + bool bIncludeBOM = false; + + if ( aFilterString == ScDocShell::GetAsciiFilterName() ) + { + // ascii export (import is handled above) + + INetURLObject aURL( aFileName ); + OUString aExt(aURL.getExtension()); + if (aExt.equalsIgnoreAsciiCase("CSV")) + cAsciiDel = ','; + else + cAsciiDel = '\t'; + + aTitle = ScResId( STR_EXPORT_ASCII ); + bAscii = true; + + ScAsciiOptions aOptions; + aOptions.ReadFromString(aFilterOptions); + bIncludeBOM = aOptions.GetIncludeBOM(); + } + else if ( aFilterString == ScDocShell::GetLotusFilterName() ) + { + // lotus is only imported + OSL_ENSURE( !bExport, "Filter Options for Lotus Export is not implemented" ); + + aTitle = ScResId( STR_IMPORT_LOTUS ); + eEncoding = RTL_TEXTENCODING_IBM_437; + } + else if ( aFilterString == ScDocShell::GetDBaseFilterName() ) + { + if ( bExport ) + { + // dBase export + aTitle = ScResId( STR_EXPORT_DBF ); + } + else + { + // dBase import + aTitle = ScResId( STR_IMPORT_DBF ); + } + + std::unique_ptr<SvStream> pInStream; + if ( xInputStream.is() ) + pInStream = utl::UcbStreamHelper::CreateStream( xInputStream ); + switch(load_CharSet( eEncoding, bExport, pInStream.get())) + { + case charsetSource::charset_from_file: + skipDialog = true; + break; + case charsetSource::charset_from_user_setting: + case charsetSource::charset_default: + break; + } + bDBEnc = true; + // pInStream goes out of scope, the stream is automatically closed + } + else if ( aFilterString == ScDocShell::GetDifFilterName() ) + { + if ( bExport ) + { + // DIF export + aTitle = ScResId( STR_EXPORT_DIF ); + } + else + { + // DIF import + aTitle = ScResId( STR_IMPORT_DIF ); + } + // common for DIF import/export + eEncoding = RTL_TEXTENCODING_MS_1252; + } + + ScImportOptions aOptions( cAsciiDel, cStrDel, eEncoding); + aOptions.bIncludeBOM = bIncludeBOM; + if(skipDialog) + { + // TODO: check we are not missing some of the stuff that ScImportOptionsDlg::GetImportOptions + // (file sc/source/ui/dbgui/scuiimoptdlg.cxx) does + // that is, if the dialog sets options that are not selected by the user (!) + // then we are missing them here. + // Then we may need to rip them out of the dialog. + // Or we actually change the dialog to not display if skipDialog==true + // in that case, add an argument skipDialog to CreateScImportOptionsDlg + nRet = ui::dialogs::ExecutableDialogResults::OK; + } + else + { + ScopedVclPtr<AbstractScImportOptionsDlg> pDlg(pFact->CreateScImportOptionsDlg(Application::GetFrameWeld(xDialogParent), + bAscii, &aOptions, &aTitle, + bDBEnc, !bExport)); + if ( pDlg->Execute() == RET_OK ) + { + pDlg->SaveImportOptions(); + pDlg->GetImportOptions( aOptions ); + save_CharSet( aOptions.eCharSet, bExport ); + nRet = ui::dialogs::ExecutableDialogResults::OK; + } + } + if (nRet == ui::dialogs::ExecutableDialogResults::OK) + { + if ( bAscii ) + aFilterOptions = aOptions.BuildString(); + else + aFilterOptions = aOptions.aStrFont; + } + } + + xInputStream.clear(); // don't hold the stream longer than necessary + + return nRet; +} + +// XImporter + +void SAL_CALL ScFilterOptionsObj::setTargetDocument( const uno::Reference<lang::XComponent>& /* xDoc */ ) +{ + bExport = false; +} + +// XExporter + +void SAL_CALL ScFilterOptionsObj::setSourceDocument( const uno::Reference<lang::XComponent>& /* xDoc */ ) +{ + bExport = true; +} + +// XInitialization + +void SAL_CALL ScFilterOptionsObj::initialize(const uno::Sequence<uno::Any>& rArguments) +{ + ::comphelper::NamedValueCollection aProperties(rArguments); + if (aProperties.has("ParentWindow")) + aProperties.get("ParentWindow") >>= xDialogParent; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/unoobj/fmtuno.cxx b/sc/source/ui/unoobj/fmtuno.cxx new file mode 100644 index 0000000000..1b91855917 --- /dev/null +++ b/sc/source/ui/unoobj/fmtuno.cxx @@ -0,0 +1,913 @@ +/* -*- 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 <sal/config.h> + +#include <o3tl/safeint.hxx> +#include <osl/diagnose.h> +#include <svl/style.hxx> +#include <utility> +#include <vcl/svapp.hxx> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/sheet/ConditionOperator2.hpp> +#include <com/sun/star/sheet/ValidationAlertStyle.hpp> +#include <com/sun/star/sheet/ValidationType.hpp> +#include <com/sun/star/sheet/TableValidationVisibility.hpp> + +#include <fmtuno.hxx> +#include <miscuno.hxx> +#include <validat.hxx> +#include <document.hxx> +#include <unonames.hxx> +#include <tokenarray.hxx> +#include <tokenuno.hxx> +#include <stylehelper.hxx> + +using namespace ::com::sun::star; +using namespace ::formula; + +// map only for PropertySetInfo + +static std::span<const SfxItemPropertyMapEntry> lcl_GetValidatePropertyMap() +{ + static const SfxItemPropertyMapEntry aValidatePropertyMap_Impl[] = + { + { SC_UNONAME_ERRALSTY, 0, cppu::UnoType<sheet::ValidationAlertStyle>::get(), 0, 0}, + { SC_UNONAME_ERRMESS, 0, cppu::UnoType<OUString>::get(), 0, 0}, + { SC_UNONAME_ERRTITLE, 0, cppu::UnoType<OUString>::get(), 0, 0}, + { SC_UNONAME_IGNOREBL, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNONAME_INPMESS, 0, cppu::UnoType<OUString>::get(), 0, 0}, + { SC_UNONAME_INPTITLE, 0, cppu::UnoType<OUString>::get(), 0, 0}, + { SC_UNONAME_SHOWERR, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNONAME_SHOWINP, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNONAME_SHOWLIST, 0, cppu::UnoType<sal_Int16>::get(), 0, 0}, + { SC_UNONAME_TYPE, 0, cppu::UnoType<sheet::ValidationType>::get(), 0, 0}, + }; + return aValidatePropertyMap_Impl; +} + +SC_SIMPLE_SERVICE_INFO( ScTableConditionalEntry, "ScTableConditionalEntry", "com.sun.star.sheet.TableConditionalEntry" ) +SC_SIMPLE_SERVICE_INFO( ScTableConditionalFormat, "ScTableConditionalFormat", "com.sun.star.sheet.TableConditionalFormat" ) +SC_SIMPLE_SERVICE_INFO( ScTableValidationObj, "ScTableValidationObj", "com.sun.star.sheet.TableValidation" ) + +static sal_Int32 lcl_ConditionModeToOperatorNew( ScConditionMode eMode ) +{ + sal_Int32 eOper = sheet::ConditionOperator2::NONE; + switch (eMode) + { + case ScConditionMode::Equal: eOper = sheet::ConditionOperator2::EQUAL; break; + case ScConditionMode::Less: eOper = sheet::ConditionOperator2::LESS; break; + case ScConditionMode::Greater: eOper = sheet::ConditionOperator2::GREATER; break; + case ScConditionMode::EqLess: eOper = sheet::ConditionOperator2::LESS_EQUAL; break; + case ScConditionMode::EqGreater: eOper = sheet::ConditionOperator2::GREATER_EQUAL; break; + case ScConditionMode::NotEqual: eOper = sheet::ConditionOperator2::NOT_EQUAL; break; + case ScConditionMode::Between: eOper = sheet::ConditionOperator2::BETWEEN; break; + case ScConditionMode::NotBetween: eOper = sheet::ConditionOperator2::NOT_BETWEEN; break; + case ScConditionMode::Direct: eOper = sheet::ConditionOperator2::FORMULA; break; + case ScConditionMode::Duplicate: eOper = sheet::ConditionOperator2::DUPLICATE; break; + default: + { + // added to avoid warnings + } + } + return eOper; +} + +static sheet::ConditionOperator lcl_ConditionModeToOperator( ScConditionMode eMode ) +{ + sheet::ConditionOperator eOper = sheet::ConditionOperator_NONE; + switch (eMode) + { + case ScConditionMode::Equal: eOper = sheet::ConditionOperator_EQUAL; break; + case ScConditionMode::Less: eOper = sheet::ConditionOperator_LESS; break; + case ScConditionMode::Greater: eOper = sheet::ConditionOperator_GREATER; break; + case ScConditionMode::EqLess: eOper = sheet::ConditionOperator_LESS_EQUAL; break; + case ScConditionMode::EqGreater: eOper = sheet::ConditionOperator_GREATER_EQUAL; break; + case ScConditionMode::NotEqual: eOper = sheet::ConditionOperator_NOT_EQUAL; break; + case ScConditionMode::Between: eOper = sheet::ConditionOperator_BETWEEN; break; + case ScConditionMode::NotBetween: eOper = sheet::ConditionOperator_NOT_BETWEEN; break; + case ScConditionMode::Direct: eOper = sheet::ConditionOperator_FORMULA; break; + default: + { + // added to avoid warnings + } + } + return eOper; +} + +static ScConditionMode lcl_ConditionOperatorToMode( sheet::ConditionOperator eOper ) +{ + ScConditionMode eMode = ScConditionMode::NONE; + switch (eOper) + { + case sheet::ConditionOperator_EQUAL: eMode = ScConditionMode::Equal; break; + case sheet::ConditionOperator_LESS: eMode = ScConditionMode::Less; break; + case sheet::ConditionOperator_GREATER: eMode = ScConditionMode::Greater; break; + case sheet::ConditionOperator_LESS_EQUAL: eMode = ScConditionMode::EqLess; break; + case sheet::ConditionOperator_GREATER_EQUAL: eMode = ScConditionMode::EqGreater; break; + case sheet::ConditionOperator_NOT_EQUAL: eMode = ScConditionMode::NotEqual; break; + case sheet::ConditionOperator_BETWEEN: eMode = ScConditionMode::Between; break; + case sheet::ConditionOperator_NOT_BETWEEN: eMode = ScConditionMode::NotBetween; break; + case sheet::ConditionOperator_FORMULA: eMode = ScConditionMode::Direct; break; + default: + { + // added to avoid warnings + } + } + return eMode; +} + +ScCondFormatEntryItem::ScCondFormatEntryItem() : + meGrammar1( FormulaGrammar::GRAM_UNSPECIFIED ), + meGrammar2( FormulaGrammar::GRAM_UNSPECIFIED ), + meMode( ScConditionMode::NONE ) +{ +} + +ScTableConditionalFormat::ScTableConditionalFormat( + const ScDocument* pDoc, sal_uLong nKey, SCTAB nTab, FormulaGrammar::Grammar eGrammar) +{ + // read the entry from the document... + + if ( !(pDoc && nKey) ) + return; + + ScConditionalFormatList* pList = pDoc->GetCondFormList(nTab); + if (!pList) + return; + + const ScConditionalFormat* pFormat = pList->GetFormat( nKey ); + if (!pFormat) + return; + + // During save to XML. + if (pDoc->IsInExternalReferenceMarking()) + pFormat->MarkUsedExternalReferences(); + + size_t nEntryCount = pFormat->size(); + for (size_t i=0; i<nEntryCount; i++) + { + ScCondFormatEntryItem aItem; + const ScFormatEntry* pFrmtEntry = pFormat->GetEntry(i); + if(pFrmtEntry->GetType() != ScFormatEntry::Type::Condition && + pFrmtEntry->GetType() != ScFormatEntry::Type::ExtCondition) + continue; + + const ScCondFormatEntry* pFormatEntry = static_cast<const ScCondFormatEntry*>(pFrmtEntry); + aItem.meMode = pFormatEntry->GetOperation(); + aItem.maPos = pFormatEntry->GetValidSrcPos(); + aItem.maExpr1 = pFormatEntry->GetExpression(aItem.maPos, 0, 0, eGrammar); + aItem.maExpr2 = pFormatEntry->GetExpression(aItem.maPos, 1, 0, eGrammar); + aItem.meGrammar1 = aItem.meGrammar2 = eGrammar; + aItem.maStyle = pFormatEntry->GetStyle(); + + AddEntry_Impl(aItem); + } +} + +namespace { + +FormulaGrammar::Grammar lclResolveGrammar( FormulaGrammar::Grammar eExtGrammar, FormulaGrammar::Grammar eIntGrammar ) +{ + if( eExtGrammar != FormulaGrammar::GRAM_UNSPECIFIED ) + return eExtGrammar; + OSL_ENSURE( eIntGrammar != FormulaGrammar::GRAM_UNSPECIFIED, "lclResolveGrammar - unspecified grammar, using GRAM_API" ); + return (eIntGrammar == FormulaGrammar::GRAM_UNSPECIFIED) ? FormulaGrammar::GRAM_API : eIntGrammar; +} + +} // namespace + +void ScTableConditionalFormat::FillFormat( ScConditionalFormat& rFormat, + ScDocument& rDoc, FormulaGrammar::Grammar eGrammar) const +{ + // ScConditionalFormat = Core-Struktur, has to be empty + + OSL_ENSURE( rFormat.IsEmpty(), "FillFormat: format not empty" ); + + for (const auto & i : maEntries) + { + ScCondFormatEntryItem aData; + i->GetData(aData); + + FormulaGrammar::Grammar eGrammar1 = lclResolveGrammar( eGrammar, aData.meGrammar1 ); + FormulaGrammar::Grammar eGrammar2 = lclResolveGrammar( eGrammar, aData.meGrammar2 ); + + ScCondFormatEntry* pCoreEntry = new ScCondFormatEntry( aData.meMode, aData.maExpr1, aData.maExpr2, + rDoc, aData.maPos, aData.maStyle, aData.maExprNmsp1, aData.maExprNmsp2, eGrammar1, eGrammar2 ); + + if ( !aData.maPosStr.isEmpty() ) + pCoreEntry->SetSrcString( aData.maPosStr ); + + if ( aData.maTokens1.hasElements() ) + { + ScTokenArray aTokenArray(rDoc); + if ( ScTokenConversion::ConvertToTokenArray(rDoc, aTokenArray, aData.maTokens1) ) + pCoreEntry->SetFormula1(aTokenArray); + } + + if ( aData.maTokens2.hasElements() ) + { + ScTokenArray aTokenArray(rDoc); + if ( ScTokenConversion::ConvertToTokenArray(rDoc, aTokenArray, aData.maTokens2) ) + pCoreEntry->SetFormula2(aTokenArray); + } + rFormat.AddEntry( pCoreEntry ); + } +} + +ScTableConditionalFormat::~ScTableConditionalFormat() +{ +} + +void ScTableConditionalFormat::AddEntry_Impl(const ScCondFormatEntryItem& aEntry) +{ + rtl::Reference<ScTableConditionalEntry> pNew = new ScTableConditionalEntry(aEntry); + maEntries.emplace_back(pNew); +} + +// XSheetConditionalFormat + +ScTableConditionalEntry* ScTableConditionalFormat::GetObjectByIndex_Impl(sal_uInt16 nIndex) const +{ + return nIndex < maEntries.size() ? maEntries[nIndex].get() : nullptr; +} + +void SAL_CALL ScTableConditionalFormat::addNew( + const uno::Sequence<beans::PropertyValue >& aConditionalEntry ) +{ + SolarMutexGuard aGuard; + ScCondFormatEntryItem aEntry; + aEntry.meMode = ScConditionMode::NONE; + + for (const beans::PropertyValue& rProp : aConditionalEntry) + { + if ( rProp.Name == SC_UNONAME_OPERATOR ) + { + sal_Int32 eOper = ScUnoHelpFunctions::GetEnumFromAny( rProp.Value ); + aEntry.meMode = ScConditionEntry::GetModeFromApi( static_cast<sheet::ConditionOperator>(eOper) ); + } + else if ( rProp.Name == SC_UNONAME_FORMULA1 ) + { + OUString aStrVal; + uno::Sequence<sheet::FormulaToken> aTokens; + if ( rProp.Value >>= aStrVal ) + aEntry.maExpr1 = aStrVal; + else if ( rProp.Value >>= aTokens ) + { + aEntry.maExpr1.clear(); + aEntry.maTokens1 = aTokens; + } + } + else if ( rProp.Name == SC_UNONAME_FORMULA2 ) + { + OUString aStrVal; + uno::Sequence<sheet::FormulaToken> aTokens; + if ( rProp.Value >>= aStrVal ) + aEntry.maExpr2 = aStrVal; + else if ( rProp.Value >>= aTokens ) + { + aEntry.maExpr2.clear(); + aEntry.maTokens2 = aTokens; + } + } + else if ( rProp.Name == SC_UNONAME_SOURCEPOS ) + { + table::CellAddress aAddress; + if ( rProp.Value >>= aAddress ) + aEntry.maPos = ScAddress( static_cast<SCCOL>(aAddress.Column), static_cast<SCROW>(aAddress.Row), aAddress.Sheet ); + } + else if ( rProp.Name == SC_UNONAME_SOURCESTR ) + { + OUString aStrVal; + if ( rProp.Value >>= aStrVal ) + aEntry.maPosStr = aStrVal; + } + else if ( rProp.Name == SC_UNONAME_STYLENAME ) + { + OUString aStrVal; + if ( rProp.Value >>= aStrVal ) + aEntry.maStyle = ScStyleNameConversion::ProgrammaticToDisplayName( + aStrVal, SfxStyleFamily::Para ); + } + else if ( rProp.Name == SC_UNONAME_FORMULANMSP1 ) + { + OUString aStrVal; + if ( rProp.Value >>= aStrVal ) + aEntry.maExprNmsp1 = aStrVal; + } + else if ( rProp.Name == SC_UNONAME_FORMULANMSP2 ) + { + OUString aStrVal; + if ( rProp.Value >>= aStrVal ) + aEntry.maExprNmsp2 = aStrVal; + } + else if ( rProp.Name == SC_UNONAME_GRAMMAR1 ) + { + sal_Int32 nVal = 0; + if ( rProp.Value >>= nVal ) + aEntry.meGrammar1 = static_cast< FormulaGrammar::Grammar >( nVal ); + } + else if ( rProp.Name == SC_UNONAME_GRAMMAR2 ) + { + sal_Int32 nVal = 0; + if ( rProp.Value >>= nVal ) + aEntry.meGrammar2 = static_cast< FormulaGrammar::Grammar >( nVal ); + } + else + { + OSL_FAIL("wrong property"); + //! Exception... + } + } + + AddEntry_Impl(aEntry); +} + +void SAL_CALL ScTableConditionalFormat::removeByIndex( sal_Int32 nIndex ) +{ + SolarMutexGuard aGuard; + + if (nIndex >= 0 && o3tl::make_unsigned(nIndex) < maEntries.size()) + { + maEntries.erase(maEntries.begin()+nIndex); + } +} + +void SAL_CALL ScTableConditionalFormat::clear() +{ + SolarMutexGuard aGuard; + maEntries.clear(); +} + +// XEnumerationAccess + +uno::Reference<container::XEnumeration> SAL_CALL ScTableConditionalFormat::createEnumeration() +{ + SolarMutexGuard aGuard; + return new ScIndexEnumeration(this, "com.sun.star.sheet.TableConditionalEntryEnumeration"); +} + +// XIndexAccess + +sal_Int32 SAL_CALL ScTableConditionalFormat::getCount() +{ + SolarMutexGuard aGuard; + return maEntries.size(); +} + +uno::Any SAL_CALL ScTableConditionalFormat::getByIndex( sal_Int32 nIndex ) +{ + SolarMutexGuard aGuard; + uno::Reference<sheet::XSheetConditionalEntry> xEntry(GetObjectByIndex_Impl(static_cast<sal_uInt16>(nIndex))); + if (!xEntry.is()) + throw lang::IndexOutOfBoundsException(); + + return uno::Any(xEntry); +} + +uno::Type SAL_CALL ScTableConditionalFormat::getElementType() +{ + return cppu::UnoType<sheet::XSheetConditionalEntry>::get(); +} + +sal_Bool SAL_CALL ScTableConditionalFormat::hasElements() +{ + SolarMutexGuard aGuard; + return ( getCount() != 0 ); +} + +// conditional format entries have no real names +// -> generate name from index + +static OUString lcl_GetEntryNameFromIndex( sal_Int32 nIndex ) +{ + OUString aRet = "Entry" + OUString::number( nIndex ); + return aRet; +} + +uno::Any SAL_CALL ScTableConditionalFormat::getByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + + uno::Reference<sheet::XSheetConditionalEntry> xEntry; + tools::Long nCount = maEntries.size(); + for (tools::Long i=0; i<nCount; i++) + if ( aName == lcl_GetEntryNameFromIndex(i) ) + { + xEntry.set(GetObjectByIndex_Impl(static_cast<sal_uInt16>(i))); + break; + } + + if (!xEntry.is()) + throw container::NoSuchElementException(); + + return uno::Any(xEntry); +} + +uno::Sequence<OUString> SAL_CALL ScTableConditionalFormat::getElementNames() +{ + SolarMutexGuard aGuard; + + tools::Long nCount = maEntries.size(); + uno::Sequence<OUString> aNames(nCount); + OUString* pArray = aNames.getArray(); + for (tools::Long i=0; i<nCount; i++) + pArray[i] = lcl_GetEntryNameFromIndex(i); + + return aNames; +} + +sal_Bool SAL_CALL ScTableConditionalFormat::hasByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + + tools::Long nCount = maEntries.size(); + for (tools::Long i=0; i<nCount; i++) + if ( aName == lcl_GetEntryNameFromIndex(i) ) + return true; + + return false; +} + +ScTableConditionalEntry::ScTableConditionalEntry(ScCondFormatEntryItem aItem) : + aData(std::move( aItem )) +{ + // #i113668# only store the settings, keep no reference to parent object +} + +ScTableConditionalEntry::~ScTableConditionalEntry() +{ +} + +void ScTableConditionalEntry::GetData(ScCondFormatEntryItem& rData) const +{ + rData = aData; +} + +// XSheetCondition + +sheet::ConditionOperator SAL_CALL ScTableConditionalEntry::getOperator() +{ + SolarMutexGuard aGuard; + return lcl_ConditionModeToOperator( aData.meMode ); +} + +void SAL_CALL ScTableConditionalEntry::setOperator( sheet::ConditionOperator nOperator ) +{ + SolarMutexGuard aGuard; + aData.meMode = lcl_ConditionOperatorToMode( nOperator ); +} + +sal_Int32 SAL_CALL ScTableConditionalEntry::getConditionOperator() +{ + SolarMutexGuard aGuard; + return lcl_ConditionModeToOperatorNew( aData.meMode ); +} + +void SAL_CALL ScTableConditionalEntry::setConditionOperator( sal_Int32 nOperator ) +{ + SolarMutexGuard aGuard; + aData.meMode = ScConditionEntry::GetModeFromApi( static_cast<sheet::ConditionOperator>(nOperator) ); +} + +OUString SAL_CALL ScTableConditionalEntry::getFormula1() +{ + SolarMutexGuard aGuard; + return aData.maExpr1; +} + +void SAL_CALL ScTableConditionalEntry::setFormula1( const OUString& aFormula1 ) +{ + SolarMutexGuard aGuard; + aData.maExpr1 = aFormula1; +} + +OUString SAL_CALL ScTableConditionalEntry::getFormula2() +{ + SolarMutexGuard aGuard; + return aData.maExpr2; +} + +void SAL_CALL ScTableConditionalEntry::setFormula2( const OUString& aFormula2 ) +{ + SolarMutexGuard aGuard; + aData.maExpr2 = aFormula2; +} + +table::CellAddress SAL_CALL ScTableConditionalEntry::getSourcePosition() +{ + SolarMutexGuard aGuard; + table::CellAddress aRet; + aRet.Column = aData.maPos.Col(); + aRet.Row = aData.maPos.Row(); + aRet.Sheet = aData.maPos.Tab(); + return aRet; +} + +void SAL_CALL ScTableConditionalEntry::setSourcePosition( const table::CellAddress& aSourcePosition ) +{ + SolarMutexGuard aGuard; + aData.maPos.Set( static_cast<SCCOL>(aSourcePosition.Column), static_cast<SCROW>(aSourcePosition.Row), aSourcePosition.Sheet ); +} + +// XSheetConditionalEntry + +OUString SAL_CALL ScTableConditionalEntry::getStyleName() +{ + SolarMutexGuard aGuard; + return ScStyleNameConversion::DisplayToProgrammaticName( aData.maStyle, SfxStyleFamily::Para ); +} + +void SAL_CALL ScTableConditionalEntry::setStyleName( const OUString& aStyleName ) +{ + SolarMutexGuard aGuard; + aData.maStyle = ScStyleNameConversion::ProgrammaticToDisplayName( aStyleName, SfxStyleFamily::Para ); +} + +ScTableValidationObj::ScTableValidationObj(const ScDocument& rDoc, sal_uInt32 nKey, + const formula::FormulaGrammar::Grammar eGrammar) : + aPropSet( lcl_GetValidatePropertyMap() ) +{ + // read the entry from the document... + + bool bFound = false; + if (nKey) + { + const ScValidationData* pData = rDoc.GetValidationEntry( nKey ); + if (pData) + { + nMode = pData->GetOperation(); + aSrcPos = pData->GetValidSrcPos(); // valid pos for expressions + aExpr1 = pData->GetExpression( aSrcPos, 0, 0, eGrammar ); + aExpr2 = pData->GetExpression( aSrcPos, 1, 0, eGrammar ); + meGrammar1 = meGrammar2 = eGrammar; + nValMode = sal::static_int_cast<sal_uInt16>( pData->GetDataMode() ); + bIgnoreBlank = pData->IsIgnoreBlank(); + nShowList = pData->GetListType(); + bShowInput = pData->GetInput( aInputTitle, aInputMessage ); + ScValidErrorStyle eStyle; + bShowError = pData->GetErrMsg( aErrorTitle, aErrorMessage, eStyle ); + nErrorStyle = sal::static_int_cast<sal_uInt16>( eStyle ); + + // During save to XML, sheet::ValidationType_ANY formulas are not + // saved, even if in the list, see + // ScMyValidationsContainer::GetCondition(), so shall not mark + // anything in use. + if (nValMode != SC_VALID_ANY && rDoc.IsInExternalReferenceMarking()) + pData->MarkUsedExternalReferences(); + + bFound = true; + } + } + if (!bFound) + ClearData_Impl(); // Defaults +} + +ScValidationData* ScTableValidationObj::CreateValidationData( ScDocument& rDoc, + formula::FormulaGrammar::Grammar eGrammar ) const +{ + // ScValidationData = Core-Struktur + + FormulaGrammar::Grammar eGrammar1 = lclResolveGrammar( eGrammar, meGrammar1 ); + FormulaGrammar::Grammar eGrammar2 = lclResolveGrammar( eGrammar, meGrammar2 ); + + ScValidationData* pRet = new ScValidationData( static_cast<ScValidationMode>(nValMode), + nMode, + aExpr1, aExpr2, rDoc, aSrcPos, + maExprNmsp1, maExprNmsp2, + eGrammar1, eGrammar2 ); + pRet->SetIgnoreBlank(bIgnoreBlank); + pRet->SetListType(nShowList); + + if ( aTokens1.hasElements() ) + { + ScTokenArray aTokenArray(rDoc); + if ( ScTokenConversion::ConvertToTokenArray(rDoc, aTokenArray, aTokens1) ) + pRet->SetFormula1(aTokenArray); + } + + if ( aTokens2.hasElements() ) + { + ScTokenArray aTokenArray(rDoc); + if ( ScTokenConversion::ConvertToTokenArray(rDoc, aTokenArray, aTokens2) ) + pRet->SetFormula2(aTokenArray); + } + + // set strings for error / input even if disabled (and disable afterwards) + pRet->SetInput( aInputTitle, aInputMessage ); + if (!bShowInput) + pRet->ResetInput(); + pRet->SetError( aErrorTitle, aErrorMessage, static_cast<ScValidErrorStyle>(nErrorStyle) ); + if (!bShowError) + pRet->ResetError(); + + if ( !aPosString.isEmpty() ) + pRet->SetSrcString( aPosString ); + + return pRet; +} + +void ScTableValidationObj::ClearData_Impl() +{ + nMode = ScConditionMode::NONE; + nValMode = SC_VALID_ANY; + bIgnoreBlank = true; + nShowList = sheet::TableValidationVisibility::UNSORTED; + bShowInput = false; + bShowError = false; + nErrorStyle = SC_VALERR_STOP; + aSrcPos.Set(0,0,0); + aExpr1.clear(); + aExpr2.clear(); + maExprNmsp1.clear(); + maExprNmsp2.clear(); + meGrammar1 = meGrammar2 = FormulaGrammar::GRAM_UNSPECIFIED; // will be overridden when needed + aInputTitle.clear(); + aInputMessage.clear(); + aErrorTitle.clear(); + aErrorMessage.clear(); +} + +ScTableValidationObj::~ScTableValidationObj() +{ +} + +// XSheetCondition + +sheet::ConditionOperator SAL_CALL ScTableValidationObj::getOperator() +{ + SolarMutexGuard aGuard; + return lcl_ConditionModeToOperator( nMode ); +} + +void SAL_CALL ScTableValidationObj::setOperator( sheet::ConditionOperator nOperator ) +{ + SolarMutexGuard aGuard; + nMode = lcl_ConditionOperatorToMode( nOperator ); +} + +sal_Int32 SAL_CALL ScTableValidationObj::getConditionOperator() +{ + SolarMutexGuard aGuard; + return lcl_ConditionModeToOperatorNew( nMode ); +} + +void SAL_CALL ScTableValidationObj::setConditionOperator( sal_Int32 nOperator ) +{ + SolarMutexGuard aGuard; + nMode = ScConditionEntry::GetModeFromApi( static_cast<css::sheet::ConditionOperator>(nOperator) ); +} + +OUString SAL_CALL ScTableValidationObj::getFormula1() +{ + SolarMutexGuard aGuard; + return aExpr1; +} + +void SAL_CALL ScTableValidationObj::setFormula1( const OUString& aFormula1 ) +{ + SolarMutexGuard aGuard; + aExpr1 = aFormula1; +} + +OUString SAL_CALL ScTableValidationObj::getFormula2() +{ + SolarMutexGuard aGuard; + return aExpr2; +} + +void SAL_CALL ScTableValidationObj::setFormula2( const OUString& aFormula2 ) +{ + SolarMutexGuard aGuard; + aExpr2 = aFormula2; +} + +table::CellAddress SAL_CALL ScTableValidationObj::getSourcePosition() +{ + SolarMutexGuard aGuard; + table::CellAddress aRet; + aRet.Column = aSrcPos.Col(); + aRet.Row = aSrcPos.Row(); + aRet.Sheet = aSrcPos.Tab(); + return aRet; +} + +void SAL_CALL ScTableValidationObj::setSourcePosition( const table::CellAddress& aSourcePosition ) +{ + SolarMutexGuard aGuard; + aSrcPos.Set( static_cast<SCCOL>(aSourcePosition.Column), static_cast<SCROW>(aSourcePosition.Row), aSourcePosition.Sheet ); +} + +uno::Sequence<sheet::FormulaToken> SAL_CALL ScTableValidationObj::getTokens( sal_Int32 nIndex ) +{ + SolarMutexGuard aGuard; + if (nIndex >= 2 || nIndex < 0) + throw lang::IndexOutOfBoundsException(); + + return nIndex == 0 ? aTokens1 : aTokens2; +} + +void SAL_CALL ScTableValidationObj::setTokens( sal_Int32 nIndex, const uno::Sequence<sheet::FormulaToken>& aTokens ) +{ + SolarMutexGuard aGuard; + if (nIndex >= 2 || nIndex < 0) + throw lang::IndexOutOfBoundsException(); + + if (nIndex == 0) + { + aTokens1 = aTokens; + aExpr1.clear(); + } + else if (nIndex == 1) + { + aTokens2 = aTokens; + aExpr2.clear(); + } +} + +sal_Int32 SAL_CALL ScTableValidationObj::getCount() +{ + return 2; +} + +uno::Reference<beans::XPropertySetInfo> SAL_CALL ScTableValidationObj::getPropertySetInfo() +{ + SolarMutexGuard aGuard; + static uno::Reference<beans::XPropertySetInfo> aRef( + new SfxItemPropertySetInfo( aPropSet.getPropertyMap() )); + return aRef; +} + +void SAL_CALL ScTableValidationObj::setPropertyValue( + const OUString& aPropertyName, const uno::Any& aValue ) +{ + SolarMutexGuard aGuard; + + if ( aPropertyName == SC_UNONAME_SHOWINP ) bShowInput = ScUnoHelpFunctions::GetBoolFromAny( aValue ); + else if ( aPropertyName == SC_UNONAME_SHOWERR ) bShowError = ScUnoHelpFunctions::GetBoolFromAny( aValue ); + else if ( aPropertyName == SC_UNONAME_IGNOREBL ) bIgnoreBlank = ScUnoHelpFunctions::GetBoolFromAny( aValue ); + else if ( aPropertyName == SC_UNONAME_SHOWLIST ) aValue >>= nShowList; + else if ( aPropertyName == SC_UNONAME_INPTITLE ) + { + OUString aStrVal; + if ( aValue >>= aStrVal ) + aInputTitle = aStrVal; + } + else if ( aPropertyName == SC_UNONAME_INPMESS ) + { + OUString aStrVal; + if ( aValue >>= aStrVal ) + aInputMessage = aStrVal; + } + else if ( aPropertyName == SC_UNONAME_ERRTITLE ) + { + OUString aStrVal; + if ( aValue >>= aStrVal ) + aErrorTitle = aStrVal; + } + else if ( aPropertyName == SC_UNONAME_ERRMESS ) + { + OUString aStrVal; + if ( aValue >>= aStrVal ) + aErrorMessage = aStrVal; + } + else if ( aPropertyName == SC_UNONAME_TYPE ) + { + sheet::ValidationType eType = static_cast<sheet::ValidationType>(ScUnoHelpFunctions::GetEnumFromAny( aValue )); + switch (eType) + { + case sheet::ValidationType_ANY: nValMode = SC_VALID_ANY; break; + case sheet::ValidationType_WHOLE: nValMode = SC_VALID_WHOLE; break; + case sheet::ValidationType_DECIMAL: nValMode = SC_VALID_DECIMAL; break; + case sheet::ValidationType_DATE: nValMode = SC_VALID_DATE; break; + case sheet::ValidationType_TIME: nValMode = SC_VALID_TIME; break; + case sheet::ValidationType_TEXT_LEN: nValMode = SC_VALID_TEXTLEN; break; + case sheet::ValidationType_LIST: nValMode = SC_VALID_LIST; break; + case sheet::ValidationType_CUSTOM: nValMode = SC_VALID_CUSTOM; break; + default: + { + // added to avoid warnings + } + } + } + else if ( aPropertyName == SC_UNONAME_ERRALSTY ) + { + sheet::ValidationAlertStyle eStyle = static_cast<sheet::ValidationAlertStyle>(ScUnoHelpFunctions::GetEnumFromAny( aValue )); + switch (eStyle) + { + case sheet::ValidationAlertStyle_STOP: nErrorStyle = SC_VALERR_STOP; break; + case sheet::ValidationAlertStyle_WARNING: nErrorStyle = SC_VALERR_WARNING; break; + case sheet::ValidationAlertStyle_INFO: nErrorStyle = SC_VALERR_INFO; break; + case sheet::ValidationAlertStyle_MACRO: nErrorStyle = SC_VALERR_MACRO; break; + default: + { + // added to avoid warnings + } + } + } + else if ( aPropertyName == SC_UNONAME_SOURCESTR ) + { + // internal - only for XML filter, not in PropertySetInfo, only set + + OUString aStrVal; + if ( aValue >>= aStrVal ) + aPosString = aStrVal; + } + else if ( aPropertyName == SC_UNONAME_FORMULANMSP1 ) + { + // internal - only for XML filter, not in PropertySetInfo, only set + + OUString aStrVal; + if ( aValue >>= aStrVal ) + maExprNmsp1 = aStrVal; + } + else if ( aPropertyName == SC_UNONAME_FORMULANMSP2 ) + { + // internal - only for XML filter, not in PropertySetInfo, only set + + OUString aStrVal; + if ( aValue >>= aStrVal ) + maExprNmsp2 = aStrVal; + } + else if ( aPropertyName == SC_UNONAME_GRAMMAR1 ) + { + // internal - only for XML filter, not in PropertySetInfo, only set + + sal_Int32 nVal = 0; + if ( aValue >>= nVal ) + meGrammar1 = static_cast< FormulaGrammar::Grammar >(nVal); + } + else if ( aPropertyName == SC_UNONAME_GRAMMAR2 ) + { + // internal - only for XML filter, not in PropertySetInfo, only set + + sal_Int32 nVal = 0; + if ( aValue >>= nVal ) + meGrammar2 = static_cast< FormulaGrammar::Grammar >(nVal); + } +} + +uno::Any SAL_CALL ScTableValidationObj::getPropertyValue( const OUString& aPropertyName ) +{ + SolarMutexGuard aGuard; + uno::Any aRet; + + if ( aPropertyName == SC_UNONAME_SHOWINP ) aRet <<= bShowInput; + else if ( aPropertyName == SC_UNONAME_SHOWERR ) aRet <<= bShowError; + else if ( aPropertyName == SC_UNONAME_IGNOREBL ) aRet <<= bIgnoreBlank; + else if ( aPropertyName == SC_UNONAME_SHOWLIST ) aRet <<= nShowList; + else if ( aPropertyName == SC_UNONAME_INPTITLE ) aRet <<= aInputTitle; + else if ( aPropertyName == SC_UNONAME_INPMESS ) aRet <<= aInputMessage; + else if ( aPropertyName == SC_UNONAME_ERRTITLE ) aRet <<= aErrorTitle; + else if ( aPropertyName == SC_UNONAME_ERRMESS ) aRet <<= aErrorMessage; + else if ( aPropertyName == SC_UNONAME_TYPE ) + { + sheet::ValidationType eType = sheet::ValidationType_ANY; + switch (nValMode) + { + case SC_VALID_ANY: eType = sheet::ValidationType_ANY; break; + case SC_VALID_WHOLE: eType = sheet::ValidationType_WHOLE; break; + case SC_VALID_DECIMAL: eType = sheet::ValidationType_DECIMAL; break; + case SC_VALID_DATE: eType = sheet::ValidationType_DATE; break; + case SC_VALID_TIME: eType = sheet::ValidationType_TIME; break; + case SC_VALID_TEXTLEN: eType = sheet::ValidationType_TEXT_LEN; break; + case SC_VALID_LIST: eType = sheet::ValidationType_LIST; break; + case SC_VALID_CUSTOM: eType = sheet::ValidationType_CUSTOM; break; + } + aRet <<= eType; + } + else if ( aPropertyName == SC_UNONAME_ERRALSTY ) + { + sheet::ValidationAlertStyle eStyle = sheet::ValidationAlertStyle_STOP; + switch (nErrorStyle) + { + case SC_VALERR_STOP: eStyle = sheet::ValidationAlertStyle_STOP; break; + case SC_VALERR_WARNING: eStyle = sheet::ValidationAlertStyle_WARNING; break; + case SC_VALERR_INFO: eStyle = sheet::ValidationAlertStyle_INFO; break; + case SC_VALERR_MACRO: eStyle = sheet::ValidationAlertStyle_MACRO; break; + } + aRet <<= eStyle; + } + + return aRet; +} + +SC_IMPL_DUMMY_PROPERTY_LISTENER( ScTableValidationObj ) + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/unoobj/forbiuno.cxx b/sc/source/ui/unoobj/forbiuno.cxx new file mode 100644 index 0000000000..0928170c55 --- /dev/null +++ b/sc/source/ui/unoobj/forbiuno.cxx @@ -0,0 +1,81 @@ +/* -*- 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 <editeng/forbiddencharacterstable.hxx> +#include <comphelper/processfactory.hxx> +#include <vcl/svapp.hxx> + +#include <forbiuno.hxx> +#include <docsh.hxx> + +using namespace ::com::sun::star; + +static std::shared_ptr<SvxForbiddenCharactersTable> lcl_GetForbidden( ScDocShell* pDocSh ) +{ + std::shared_ptr<SvxForbiddenCharactersTable> xRet; + if ( pDocSh ) + { + ScDocument& rDoc = pDocSh->GetDocument(); + xRet = rDoc.GetForbiddenCharacters(); + if (!xRet) + { + // create an empty SvxForbiddenCharactersTable for SvxUnoForbiddenCharsTable, + // so changes can be stored. + xRet = SvxForbiddenCharactersTable::makeForbiddenCharactersTable(comphelper::getProcessComponentContext()); + rDoc.SetForbiddenCharacters( xRet ); + } + } + return xRet; +} + +ScForbiddenCharsObj::ScForbiddenCharsObj( ScDocShell* pDocSh ) : + SvxUnoForbiddenCharsTable( lcl_GetForbidden( pDocSh ) ), + pDocShell( pDocSh ) +{ + if (pDocShell) + pDocShell->GetDocument().AddUnoObject(*this); +} + +ScForbiddenCharsObj::~ScForbiddenCharsObj() +{ + SolarMutexGuard g; + + if (pDocShell) + pDocShell->GetDocument().RemoveUnoObject(*this); +} + +void ScForbiddenCharsObj::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + if ( rHint.GetId() == SfxHintId::Dying ) + { + pDocShell = nullptr; // document gone + } +} + +void ScForbiddenCharsObj::onChange() +{ + if (pDocShell) + { + pDocShell->GetDocument().SetForbiddenCharacters( mxForbiddenChars ); + pDocShell->PostPaintGridAll(); + pDocShell->SetDocumentModified(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/unoobj/funcuno.cxx b/sc/source/ui/unoobj/funcuno.cxx new file mode 100644 index 0000000000..6f5226452b --- /dev/null +++ b/sc/source/ui/unoobj/funcuno.cxx @@ -0,0 +1,653 @@ +/* -*- 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 <cppuhelper/supportsservice.hxx> +#include <sfx2/app.hxx> +#include <svl/itemprop.hxx> +#include <svl/sharedstringpool.hxx> +#include <unotools/charclass.hxx> +#include <osl/diagnose.h> +#include <vcl/svapp.hxx> + +#include <funcuno.hxx> +#include <miscuno.hxx> +#include <cellsuno.hxx> +#include <scdll.hxx> +#include <document.hxx> +#include <compiler.hxx> +#include <formula/errorcodes.hxx> +#include <callform.hxx> +#include <addincol.hxx> +#include <rangeseq.hxx> +#include <formulacell.hxx> +#include <docoptio.hxx> +#include <optuno.hxx> +#include <markdata.hxx> +#include <patattr.hxx> +#include <docpool.hxx> +#include <attrib.hxx> +#include <clipparam.hxx> +#include <stringutil.hxx> +#include <tokenarray.hxx> +#include <memory> + +using namespace com::sun::star; + +// registered as implementation for service FunctionAccess, +// also supports service SpreadsheetDocumentSettings (to set null date etc.) + +constexpr OUString SCFUNCTIONACCESS_SERVICE = u"com.sun.star.sheet.FunctionAccess"_ustr; +constexpr OUString SCDOCSETTINGS_SERVICE = u"com.sun.star.sheet.SpreadsheetDocumentSettings"_ustr; + +// helper to use cached document if not in use, temporary document otherwise + +namespace { + +class ScTempDocSource +{ +private: + ScTempDocCache& rCache; + ScDocumentUniquePtr pTempDoc; + + static ScDocument* CreateDocument(); // create and initialize doc + +public: + explicit ScTempDocSource( ScTempDocCache& rDocCache ); + ~ScTempDocSource() COVERITY_NOEXCEPT_FALSE; + + ScDocument* GetDocument(); +}; + +} + +ScDocument* ScTempDocSource::CreateDocument() +{ + ScDocument* pDoc = new ScDocument( SCDOCMODE_FUNCTIONACCESS ); + pDoc->MakeTable( 0 ); + return pDoc; +} + +ScTempDocSource::ScTempDocSource( ScTempDocCache& rDocCache ) : + rCache( rDocCache ) +{ + if ( rCache.IsInUse() ) + pTempDoc.reset(CreateDocument()); + else + { + rCache.SetInUse( true ); + if ( !rCache.GetDocument() ) + rCache.SetDocument( CreateDocument() ); + } +} + +ScTempDocSource::~ScTempDocSource() COVERITY_NOEXCEPT_FALSE +{ + if ( !pTempDoc ) + rCache.SetInUse( false ); +} + +ScDocument* ScTempDocSource::GetDocument() +{ + if ( pTempDoc ) + return pTempDoc.get(); + else + return rCache.GetDocument(); +} + +ScTempDocCache::ScTempDocCache() + : bInUse(false) +{ +} + +void ScTempDocCache::SetDocument( ScDocument* pNew ) +{ + OSL_ENSURE(!xDoc, "ScTempDocCache::SetDocument: already set"); + xDoc.reset(pNew); +} + +void ScTempDocCache::Clear() +{ + OSL_ENSURE( !bInUse, "ScTempDocCache::Clear: bInUse" ); + xDoc.reset(); +} + +// copy results from one document into another +//! merge this with ScAreaLink::Refresh +//! copy directly without a clipboard document? + +static bool lcl_CopyData( ScDocument* pSrcDoc, const ScRange& rSrcRange, + ScDocument* pDestDoc, const ScAddress& rDestPos ) +{ + SCTAB nSrcTab = rSrcRange.aStart.Tab(); + SCTAB nDestTab = rDestPos.Tab(); + + ScRange aNewRange( rDestPos, ScAddress( + rSrcRange.aEnd.Col() - rSrcRange.aStart.Col() + rDestPos.Col(), + rSrcRange.aEnd.Row() - rSrcRange.aStart.Row() + rDestPos.Row(), + nDestTab ) ); + + ScDocumentUniquePtr pClipDoc(new ScDocument( SCDOCMODE_CLIP )); + ScMarkData aSourceMark(pSrcDoc->GetSheetLimits()); + aSourceMark.SelectOneTable( nSrcTab ); // for CopyToClip + aSourceMark.SetMarkArea( rSrcRange ); + ScClipParam aClipParam(rSrcRange, false); + pSrcDoc->CopyToClip(aClipParam, pClipDoc.get(), &aSourceMark, false, false); + + if ( pClipDoc->HasAttrib( 0,0,nSrcTab, pClipDoc->MaxCol(), pClipDoc->MaxRow(),nSrcTab, + HasAttrFlags::Merged | HasAttrFlags::Overlapped ) ) + { + ScPatternAttr aPattern( pSrcDoc->GetPool() ); + aPattern.GetItemSet().Put( ScMergeAttr() ); // Defaults + aPattern.GetItemSet().Put( ScMergeFlagAttr() ); + pClipDoc->ApplyPatternAreaTab( 0,0, pClipDoc->MaxCol(), pClipDoc->MaxRow(), nSrcTab, aPattern ); + } + + ScMarkData aDestMark(pDestDoc->GetSheetLimits()); + aDestMark.SelectOneTable( nDestTab ); + aDestMark.SetMarkArea( aNewRange ); + pDestDoc->CopyFromClip( aNewRange, aDestMark, InsertDeleteFlags::ALL & ~InsertDeleteFlags::FORMULA, nullptr, pClipDoc.get(), false ); + + return true; +} + +ScFunctionAccess::ScFunctionAccess() : + aPropertyMap( ScDocOptionsHelper::GetPropertyMap() ), + mbArray( true ), // default according to behaviour of older Office versions + mbValid( true ) +{ + StartListening( *SfxGetpApp() ); // for SfxHintId::Deinitializing +} + +ScFunctionAccess::~ScFunctionAccess() +{ + pOptions.reset(); + { + // SfxBroadcaster::RemoveListener checks DBG_TESTSOLARMUTEX(): + SolarMutexGuard g; + EndListeningAll(); + } +} + +void ScFunctionAccess::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + if ( rHint.GetId() == SfxHintId::Deinitializing ) + { + // document must not be used anymore + aDocCache.Clear(); + mbValid = false; + } +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +ScFunctionAccess_get_implementation(css::uno::XComponentContext*, css::uno::Sequence<css::uno::Any> const &) +{ + SolarMutexGuard aGuard; + ScDLL::Init(); + return cppu::acquire(new ScFunctionAccess); +} + +// XServiceInfo +OUString SAL_CALL ScFunctionAccess::getImplementationName() +{ + return "stardiv.StarCalc.ScFunctionAccess"; +} + +sal_Bool SAL_CALL ScFunctionAccess::supportsService( const OUString& rServiceName ) +{ + return cppu::supportsService(this, rServiceName); +} + +uno::Sequence<OUString> SAL_CALL ScFunctionAccess::getSupportedServiceNames() +{ + return {SCFUNCTIONACCESS_SERVICE, SCDOCSETTINGS_SERVICE}; +} + +// XPropertySet (document settings) + +uno::Reference<beans::XPropertySetInfo> SAL_CALL ScFunctionAccess::getPropertySetInfo() +{ + SolarMutexGuard aGuard; + static uno::Reference<beans::XPropertySetInfo> aRef( + new SfxItemPropertySetInfo( aPropertyMap )); + return aRef; +} + +void SAL_CALL ScFunctionAccess::setPropertyValue( + const OUString& aPropertyName, const uno::Any& aValue ) +{ + SolarMutexGuard aGuard; + + if ( aPropertyName == "IsArrayFunction" ) + { + if( !(aValue >>= mbArray) ) + throw lang::IllegalArgumentException(); + } + else + { + if ( !pOptions ) + pOptions.reset( new ScDocOptions() ); + + // options aren't initialized from configuration - always get the same default behaviour + + bool bDone = ScDocOptionsHelper::setPropertyValue( *pOptions, aPropertyMap, aPropertyName, aValue ); + if (!bDone) + throw beans::UnknownPropertyException(aPropertyName); + } +} + +uno::Any SAL_CALL ScFunctionAccess::getPropertyValue( const OUString& aPropertyName ) +{ + SolarMutexGuard aGuard; + + if ( aPropertyName == "IsArrayFunction" ) + return uno::Any( mbArray ); + + if ( !pOptions ) + pOptions.reset( new ScDocOptions() ); + + // options aren't initialized from configuration - always get the same default behaviour + + return ScDocOptionsHelper::getPropertyValue( *pOptions, aPropertyMap, aPropertyName ); +} + +SC_IMPL_DUMMY_PROPERTY_LISTENER( ScFunctionAccess ) + +// XFunctionAccess + +static bool lcl_AddFunctionToken( ScTokenArray& rArray, const OUString& rName,const ScCompiler& rCompiler ) +{ + // function names are always case-insensitive + OUString aUpper = ScGlobal::getCharClass().uppercase(rName); + + // same options as in ScCompiler::IsOpCode: + // 1. built-in function name + + OpCode eOp = rCompiler.GetEnglishOpCode( aUpper ); + if ( eOp != ocNone ) + { + rArray.AddOpCode( eOp ); + return true; + } + + // 2. old add in functions + + if (ScGlobal::GetLegacyFuncCollection()->findByName(aUpper)) + { + rArray.AddExternal(aUpper.getStr()); + return true; + } + + // 3. new (uno) add in functions + + OUString aIntName = + ScGlobal::GetAddInCollection()->FindFunction(aUpper, false); + if (!aIntName.isEmpty()) + { + rArray.AddExternal(aIntName.getStr()); // international name + return true; + } + + return false; // no valid function name +} + +static void lcl_AddRef( ScTokenArray& rArray, sal_Int32 nStartRow, sal_Int32 nColCount, sal_Int32 nRowCount ) +{ + ScComplexRefData aRef; + aRef.InitRange(ScRange(0,nStartRow,0,nColCount-1,nStartRow+nRowCount-1,0)); + rArray.AddDoubleReference(aRef); +} + +namespace { + +class SimpleVisitor +{ +protected: + bool mbArgError; + ScDocument* mpDoc; +public: + explicit SimpleVisitor( ScDocument* pDoc ) : mbArgError( false ), mpDoc( pDoc ) {} + // could possibly just get away with JUST the following overload + // 1) virtual void visitElem( long& nCol, long& nRow, const double& elem ) + // 2) virtual void visitElem( long& nCol, long& nRow, const OUString& elem ) + // 3) virtual void visitElem( long& nCol, long& nRow, const uno::Any& elem ) + // the other types methods are here just to reflect the orig code and for + // completeness. + + void visitElem( sal_Int32 nCol, sal_Int32 nRow, sal_Int16 elem ) + { + mpDoc->SetValue( static_cast<SCCOL>(nCol), static_cast<SCROW>(nRow), 0, elem ); + } + void visitElem( sal_Int32 nCol, sal_Int32 nRow, sal_Int32 elem ) + { + mpDoc->SetValue( static_cast<SCCOL>(nCol), static_cast<SCROW>(nRow), 0, elem ); + } + void visitElem( sal_Int32 nCol, sal_Int32 nRow, const double& elem ) + { + mpDoc->SetValue( static_cast<SCCOL>(nCol), static_cast<SCROW>(nRow), 0, elem ); + } + void visitElem( sal_Int32 nCol, sal_Int32 nRow, const OUString& elem ) + { + if (!elem.isEmpty()) + { + ScSetStringParam aParam; + aParam.setTextInput(); + mpDoc->SetString(ScAddress(nCol,nRow,0), elem, &aParam); + } + } + void visitElem( sal_Int32 nCol, sal_Int32 nRow, const uno::Any& rElement ) + { + uno::TypeClass eElemClass = rElement.getValueTypeClass(); + if ( eElemClass == uno::TypeClass_VOID ) + { + // leave empty + } + else if ( eElemClass == uno::TypeClass_BYTE || + eElemClass == uno::TypeClass_SHORT || + eElemClass == uno::TypeClass_UNSIGNED_SHORT || + eElemClass == uno::TypeClass_LONG || + eElemClass == uno::TypeClass_UNSIGNED_LONG || + eElemClass == uno::TypeClass_FLOAT || + eElemClass == uno::TypeClass_DOUBLE ) + { + // accept integer types because Basic passes a floating point + // variable as byte, short or long if it's an integer number. + double fVal(0.0); + rElement >>= fVal; + visitElem( nCol, nRow, fVal ); + } + else if ( eElemClass == uno::TypeClass_STRING ) + { + OUString aUStr; + rElement >>= aUStr; + visitElem( nCol, nRow, aUStr ); + } + else + mbArgError = true; + } + bool hasArgError() const { return mbArgError; } +}; + +template< class seq > +class SequencesContainer +{ + uno::Sequence< uno::Sequence< seq > > maSeq; + + sal_Int32& mrDocRow; + bool mbOverflow; + bool mbArgError; + ScDocument* mpDoc; + ScTokenArray& mrTokenArr; + +public: + SequencesContainer( const uno::Any& rArg, ScTokenArray& rTokenArr, sal_Int32& rDocRow, ScDocument* pDoc ) : + mrDocRow( rDocRow ), mbOverflow(false), mbArgError(false), mpDoc( pDoc ), mrTokenArr( rTokenArr ) + { + rArg >>= maSeq; + } + + void process() + { + SimpleVisitor aVisitor(mpDoc); + sal_Int32 nStartRow = mrDocRow; + sal_Int32 nRowCount = maSeq.getLength(); + sal_Int32 nMaxColCount = 0; + for ( const uno::Sequence< seq >& rRow : std::as_const(maSeq) ) + { + sal_Int32 nColCount = rRow.getLength(); + if ( nColCount > nMaxColCount ) + nMaxColCount = nColCount; + for (sal_Int32 nCol=0; nCol<nColCount; nCol++) + if ( nCol <= mpDoc->MaxCol() && mrDocRow <= mpDoc->MaxRow() ) + aVisitor.visitElem( nCol, mrDocRow, rRow[ nCol ] ); + else + mbOverflow=true; + mrDocRow++; + } + mbArgError = aVisitor.hasArgError(); + if (!mbOverflow) + { + if (nRowCount && nMaxColCount) + lcl_AddRef( mrTokenArr, nStartRow, nMaxColCount, nRowCount ); + else if (nRowCount == 1 && !nMaxColCount) + // Empty Sequence<Sequence<Any>> is omitted argument. + mrTokenArr.AddOpCode( ocMissing); + } + } + bool getOverflow() const { return mbOverflow; } + bool getArgError() const { return mbArgError; } +}; + +template <class T> +class ArrayOfArrayProc +{ +public: +static void processSequences( ScDocument* pDoc, const uno::Any& rArg, ScTokenArray& rTokenArr, + sal_Int32& rDocRow, bool& rArgErr, bool& rOverflow ) +{ + SequencesContainer< T > aContainer( rArg, rTokenArr, rDocRow, pDoc ); + aContainer.process(); + rArgErr = aContainer.getArgError(); + rOverflow = aContainer.getOverflow(); +} +}; + +} + +uno::Any SAL_CALL ScFunctionAccess::callFunction( const OUString& aName, + const uno::Sequence<uno::Any>& aArguments ) +{ + SolarMutexGuard aGuard; + + if (!mbValid) + throw uno::RuntimeException(); + + // use cached document if not in use, temporary document otherwise + // (deleted in ScTempDocSource dtor) + ScTempDocSource aSource( aDocCache ); + ScDocument* pDoc = aSource.GetDocument(); + const static SCTAB nTempSheet = 1; + // Create an extra tab to contain the Function Cell + // this will allow full rows to be used. + if ( !pDoc->HasTable( nTempSheet ) ) + pDoc->MakeTable( nTempSheet ); + + /// TODO: check + ScAddress aAdr; + ScCompiler aCompiler(*pDoc, aAdr, pDoc->GetGrammar()); + + // find function + + ScTokenArray aTokenArr(*pDoc); + if ( !lcl_AddFunctionToken( aTokenArr, aName,aCompiler ) ) + { + // function not found + throw container::NoSuchElementException(); + } + + // set options (null date, etc.) + + if ( pOptions ) + pDoc->SetDocOptions( *pOptions ); + + // add arguments to token array + + bool bArgErr = false; + bool bOverflow = false; + sal_Int32 nDocRow = 0; + tools::Long nArgCount = aArguments.getLength(); + const uno::Any* pArgArr = aArguments.getConstArray(); + + svl::SharedStringPool& rSPool = pDoc->GetSharedStringPool(); + aTokenArr.AddOpCode(ocOpen); + for (tools::Long nPos=0; nPos<nArgCount; nPos++) + { + if ( nPos > 0 ) + aTokenArr.AddOpCode(ocSep); + + const uno::Any& rArg = pArgArr[nPos]; + + uno::TypeClass eClass = rArg.getValueTypeClass(); + const uno::Type& aType = rArg.getValueType(); + if ( eClass == uno::TypeClass_BYTE || + eClass == uno::TypeClass_BOOLEAN || + eClass == uno::TypeClass_SHORT || + eClass == uno::TypeClass_UNSIGNED_SHORT || + eClass == uno::TypeClass_LONG || + eClass == uno::TypeClass_UNSIGNED_LONG || + eClass == uno::TypeClass_FLOAT || + eClass == uno::TypeClass_DOUBLE ) + { + // accept integer types because Basic passes a floating point + // variable as byte, short or long if it's an integer number. + double fVal = 0; + rArg >>= fVal; + aTokenArr.AddDouble( fVal ); + } + else if ( eClass == uno::TypeClass_STRING ) + { + OUString aUStr; + rArg >>= aUStr; + aTokenArr.AddString(rSPool.intern(aUStr)); + } + else if ( aType.equals( cppu::UnoType<uno::Sequence< uno::Sequence<sal_Int16> >>::get() ) ) + { + ArrayOfArrayProc<sal_Int16>::processSequences( pDoc, rArg, aTokenArr, nDocRow, bArgErr, bOverflow ); + } + else if ( aType.equals( cppu::UnoType<uno::Sequence< uno::Sequence<sal_Int32> >>::get() ) ) + { + ArrayOfArrayProc<sal_Int32>::processSequences( pDoc, rArg, aTokenArr, nDocRow, bArgErr, bOverflow ); + } + else if ( aType.equals( cppu::UnoType<uno::Sequence< uno::Sequence<double> >>::get() ) ) + { + ArrayOfArrayProc<double>::processSequences( pDoc, rArg, aTokenArr, nDocRow, bArgErr, bOverflow ); + } + else if ( aType.equals( cppu::UnoType<uno::Sequence< uno::Sequence<OUString> >>::get() ) ) + { + ArrayOfArrayProc<OUString>::processSequences( pDoc, rArg, aTokenArr, nDocRow, bArgErr, bOverflow ); + } + else if ( aType.equals( cppu::UnoType<uno::Sequence< uno::Sequence<uno::Any> >>::get() ) ) + { + ArrayOfArrayProc<uno::Any>::processSequences( pDoc, rArg, aTokenArr, nDocRow, bArgErr, bOverflow ); + } + else if ( aType.equals( cppu::UnoType<table::XCellRange>::get()) ) + { + // currently, only our own cell ranges are supported + + uno::Reference<table::XCellRange> xRange(rArg, uno::UNO_QUERY); + ScCellRangesBase* pImpl = dynamic_cast<ScCellRangesBase*>( xRange.get() ); + if ( pImpl ) + { + ScDocument* pSrcDoc = pImpl->GetDocument(); + const ScRangeList& rRanges = pImpl->GetRangeList(); + if ( pSrcDoc && rRanges.size() == 1 ) + { + ScRange const & rSrcRange = rRanges[ 0 ]; + + sal_Int32 nStartRow = nDocRow; + sal_Int32 nColCount = rSrcRange.aEnd.Col() - rSrcRange.aStart.Col() + 1; + sal_Int32 nRowCount = rSrcRange.aEnd.Row() - rSrcRange.aStart.Row() + 1; + + if ( nStartRow + nRowCount > pDoc->GetSheetLimits().GetMaxRowCount() ) + bOverflow = true; + else + { + // copy data + if ( !lcl_CopyData( pSrcDoc, rSrcRange, pDoc, ScAddress( 0, static_cast<SCROW>(nDocRow), 0 ) ) ) + bOverflow = true; + } + + nDocRow += nRowCount; + if ( !bOverflow ) + lcl_AddRef( aTokenArr, nStartRow, nColCount, nRowCount ); + } + else + bArgErr = true; + } + else + bArgErr = true; + } + else + bArgErr = true; // invalid type + } + aTokenArr.AddOpCode(ocClose); + aTokenArr.AddOpCode(ocStop); + + // execute formula + + uno::Any aRet; + if ( !bArgErr && !bOverflow && nDocRow <= pDoc->GetSheetLimits().GetMaxRowCount() ) + { + ScAddress aFormulaPos( 0, 0, nTempSheet ); + // GRAM_API doesn't really matter for the token array but fits with + // other API compatibility grammars. + ScFormulaCell* pFormula = new ScFormulaCell( + *pDoc, aFormulaPos, aTokenArr, formula::FormulaGrammar::GRAM_API, + mbArray ? ScMatrixMode::Formula : ScMatrixMode::NONE ); + pFormula = pDoc->SetFormulaCell(aFormulaPos, pFormula); + if (mbArray && pFormula) + pFormula->SetMatColsRows(1,1); // the cell dimensions (only one cell) + + // call GetMatrix before GetErrCode because GetMatrix always recalculates + // if there is no matrix result + + const ScMatrix* pMat = (mbArray && pFormula) ? pFormula->GetMatrix() : nullptr; + FormulaError nErrCode = pFormula ? pFormula->GetErrCode() : FormulaError::IllegalArgument; + if ( nErrCode == FormulaError::NONE ) + { + if ( pMat ) + { + // array result + ScRangeToSequence::FillMixedArray( aRet, pMat ); + } + else if ( pFormula->IsValue() ) + { + // numeric value + aRet <<= pFormula->GetValue(); + } + else + { + // string result + OUString aStrVal = pFormula->GetString().getString(); + aRet <<= aStrVal; + } + } + else if ( nErrCode == FormulaError::NotAvailable ) + { + // #N/A: leave result empty, no exception + } + else + { + // any other error: IllegalArgumentException + bArgErr = true; + } + + pDoc->DeleteAreaTab( 0, 0, pDoc->MaxCol(), pDoc->MaxRow(), 0, InsertDeleteFlags::ALL ); + pDoc->DeleteAreaTab( 0, 0, 0, 0, nTempSheet, InsertDeleteFlags::ALL ); + } + + if (bOverflow) + throw uno::RuntimeException(); + + if (bArgErr) + throw lang::IllegalArgumentException(); + + return aRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/unoobj/linkuno.cxx b/sc/source/ui/unoobj/linkuno.cxx new file mode 100644 index 0000000000..b8c6a9c7e3 --- /dev/null +++ b/sc/source/ui/unoobj/linkuno.cxx @@ -0,0 +1,1688 @@ +/* -*- 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 <sal/config.h> + +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <comphelper/sequence.hxx> +#include <formula/token.hxx> +#include <svl/hint.hxx> +#include <sfx2/linkmgr.hxx> +#include <utility> +#include <vcl/svapp.hxx> +#include <svl/sharedstringpool.hxx> + +#include <linkuno.hxx> +#include <miscuno.hxx> +#include <convuno.hxx> +#include <docsh.hxx> +#include <docfunc.hxx> +#include <tablink.hxx> +#include <arealink.hxx> +#include <hints.hxx> +#include <unonames.hxx> +#include <rangeseq.hxx> +#include <scmatrix.hxx> +#include <documentlinkmgr.hxx> + +#include <string_view> +#include <vector> + +using namespace com::sun::star; +using namespace formula; +using ::com::sun::star::uno::Any; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::lang::IllegalArgumentException; +using ::com::sun::star::uno::RuntimeException; +using ::std::vector; + +// used for sheet- and area link: +static std::span<const SfxItemPropertyMapEntry> lcl_GetSheetLinkMap() +{ + static const SfxItemPropertyMapEntry aSheetLinkMap_Impl[] = + { + { SC_UNONAME_FILTER, 0, cppu::UnoType<OUString>::get(), 0, 0 }, + { SC_UNONAME_FILTOPT, 0, cppu::UnoType<OUString>::get(), 0, 0 }, + { SC_UNONAME_LINKURL, 0, cppu::UnoType<OUString>::get(), 0, 0 }, + { SC_UNONAME_REFDELAY, 0, cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { SC_UNONAME_REFPERIOD, 0, cppu::UnoType<sal_Int32>::get(), 0, 0 }, + }; + return aSheetLinkMap_Impl; +} + +SC_SIMPLE_SERVICE_INFO( ScAreaLinkObj, "ScAreaLinkObj", "com.sun.star.sheet.CellAreaLink" ) +SC_SIMPLE_SERVICE_INFO( ScAreaLinksObj, "ScAreaLinksObj", "com.sun.star.sheet.CellAreaLinks" ) +SC_SIMPLE_SERVICE_INFO( ScDDELinkObj, "ScDDELinkObj", "com.sun.star.sheet.DDELink" ) +SC_SIMPLE_SERVICE_INFO( ScDDELinksObj, "ScDDELinksObj", "com.sun.star.sheet.DDELinks" ) +SC_SIMPLE_SERVICE_INFO( ScSheetLinkObj, "ScSheetLinkObj", "com.sun.star.sheet.SheetLink" ) +SC_SIMPLE_SERVICE_INFO( ScSheetLinksObj, "ScSheetLinksObj", "com.sun.star.sheet.SheetLinks" ) + +ScSheetLinkObj::ScSheetLinkObj(ScDocShell* pDocSh, OUString aName) : + aPropSet( lcl_GetSheetLinkMap() ), + pDocShell( pDocSh ), + aFileName(std::move( aName )) +{ + pDocShell->GetDocument().AddUnoObject(*this); +} + +ScSheetLinkObj::~ScSheetLinkObj() +{ + SolarMutexGuard g; + + if (pDocShell) + pDocShell->GetDocument().RemoveUnoObject(*this); +} + +void ScSheetLinkObj::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + //! notify if links in document are changed + // UpdateRef is not needed here + + if ( auto pRefreshHint = dynamic_cast<const ScLinkRefreshedHint*>(&rHint) ) + { + if ( pRefreshHint->GetLinkType() == ScLinkRefType::SHEET && pRefreshHint->GetUrl() == aFileName ) + Refreshed_Impl(); + } + else + { + if ( rHint.GetId() == SfxHintId::Dying ) + pDocShell = nullptr; // pointer is invalid + } +} + +ScTableLink* ScSheetLinkObj::GetLink_Impl() const +{ + if (pDocShell) + { + sfx2::LinkManager* pLinkManager = pDocShell->GetDocument().GetLinkManager(); + size_t nCount = pLinkManager->GetLinks().size(); + for (size_t i=0; i<nCount; i++) + { + ::sfx2::SvBaseLink* pBase = pLinkManager->GetLinks()[i].get(); + if (auto pTabLink = dynamic_cast<ScTableLink*>( pBase)) + { + if ( pTabLink->GetFileName() == aFileName ) + return pTabLink; + } + } + } + return nullptr; // not found +} + +// XNamed + +OUString SAL_CALL ScSheetLinkObj::getName() +{ + SolarMutexGuard aGuard; + return getFileName(); // Name is the same as filename (URL) +} + +void SAL_CALL ScSheetLinkObj::setName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + setFileName(aName); // Name is the same as filename (URL) +} + +// XRefreshable + +void SAL_CALL ScSheetLinkObj::refresh() +{ + SolarMutexGuard aGuard; + ScTableLink* pLink = GetLink_Impl(); + if (pLink) + pLink->Refresh( pLink->GetFileName(), pLink->GetFilterName(), nullptr, pLink->GetRefreshDelaySeconds() ); +} + +void SAL_CALL ScSheetLinkObj::addRefreshListener( + const uno::Reference<util::XRefreshListener >& xListener ) +{ + SolarMutexGuard aGuard; + aRefreshListeners.push_back( xListener ); + + // hold one additional ref to keep this object alive as long as there are listeners + if ( aRefreshListeners.size() == 1 ) + acquire(); +} + +void SAL_CALL ScSheetLinkObj::removeRefreshListener( + const uno::Reference<util::XRefreshListener >& xListener ) +{ + SolarMutexGuard aGuard; + size_t nCount = aRefreshListeners.size(); + for ( size_t n=nCount; n--; ) + { + uno::Reference<util::XRefreshListener>& rObj = aRefreshListeners[n]; + if ( rObj == xListener ) + { + aRefreshListeners.erase( aRefreshListeners.begin() + n ); + if ( aRefreshListeners.empty() ) + release(); // release ref for listeners + break; + } + } +} + +void ScSheetLinkObj::Refreshed_Impl() +{ + lang::EventObject aEvent; + aEvent.Source.set(getXWeak()); + for (uno::Reference<util::XRefreshListener> & xRefreshListener : aRefreshListeners) + xRefreshListener->refreshed( aEvent ); +} + +void ScSheetLinkObj::ModifyRefreshDelay_Impl( sal_Int32 nRefresh ) +{ + ScTableLink* pLink = GetLink_Impl(); + if( pLink ) + pLink->SetRefreshDelay( static_cast<sal_uLong>(nRefresh) ); +} + +// XPropertySet + +uno::Reference<beans::XPropertySetInfo> SAL_CALL ScSheetLinkObj::getPropertySetInfo() +{ + SolarMutexGuard aGuard; + static uno::Reference<beans::XPropertySetInfo> aRef( + new SfxItemPropertySetInfo( aPropSet.getPropertyMap() )); + return aRef; +} + +void SAL_CALL ScSheetLinkObj::setPropertyValue( + const OUString& aPropertyName, const uno::Any& aValue ) +{ + SolarMutexGuard aGuard; + OUString aValStr; + if ( aPropertyName == SC_UNONAME_LINKURL ) + { + if ( aValue >>= aValStr ) + setFileName( aValStr ); + } + else if ( aPropertyName == SC_UNONAME_FILTER ) + { + if ( aValue >>= aValStr ) + setFilter( aValStr ); + } + else if ( aPropertyName == SC_UNONAME_FILTOPT ) + { + if ( aValue >>= aValStr ) + setFilterOptions( aValStr ); + } + else if ( aPropertyName == SC_UNONAME_REFPERIOD ) + { + sal_Int32 nRefresh = 0; + if ( aValue >>= nRefresh ) + setRefreshDelay( nRefresh ); + } + else if ( aPropertyName == SC_UNONAME_REFDELAY ) + { + sal_Int32 nRefresh = 0; + if ( aValue >>= nRefresh ) + setRefreshDelay( nRefresh ); + } +} + +uno::Any SAL_CALL ScSheetLinkObj::getPropertyValue( const OUString& aPropertyName ) +{ + SolarMutexGuard aGuard; + uno::Any aRet; + if ( aPropertyName == SC_UNONAME_LINKURL ) + aRet <<= getFileName(); + else if ( aPropertyName == SC_UNONAME_FILTER ) + aRet <<= getFilter(); + else if ( aPropertyName == SC_UNONAME_FILTOPT ) + aRet <<= getFilterOptions(); + else if ( aPropertyName == SC_UNONAME_REFPERIOD ) + aRet <<= getRefreshDelay(); + else if ( aPropertyName == SC_UNONAME_REFDELAY ) + aRet <<= getRefreshDelay(); + return aRet; +} + +SC_IMPL_DUMMY_PROPERTY_LISTENER( ScSheetLinkObj ) + +// internal: + +OUString ScSheetLinkObj::getFileName() const +{ + SolarMutexGuard aGuard; + return aFileName; +} + +void ScSheetLinkObj::setFileName(const OUString& rNewName) +{ + SolarMutexGuard aGuard; + ScTableLink* pLink = GetLink_Impl(); + if (!pLink) + return; + + // pLink->Refresh with a new file name confuses sfx2::LinkManager + // therefore we transplant the sheets manually and create new links with UpdateLinks + + OUString aNewStr(ScGlobal::GetAbsDocName( rNewName, pDocShell )); + + // first transplant the sheets + + ScDocument& rDoc = pDocShell->GetDocument(); + SCTAB nTabCount = rDoc.GetTableCount(); + for (SCTAB nTab=0; nTab<nTabCount; nTab++) + if ( rDoc.IsLinked(nTab) && rDoc.GetLinkDoc(nTab) == aFileName ) // old file + rDoc.SetLink( nTab, rDoc.GetLinkMode(nTab), aNewStr, + rDoc.GetLinkFlt(nTab), rDoc.GetLinkOpt(nTab), + rDoc.GetLinkTab(nTab), + rDoc.GetLinkRefreshDelay(nTab) ); // only change the file + + // update links + //! Undo !!! + + pDocShell->UpdateLinks(); // remove old links, possibly set up new ones + + // copy data + + aFileName = aNewStr; + pLink = GetLink_Impl(); // new link with new name + if (pLink) + pLink->Update(); // incl. paint & undo for data +} + +OUString ScSheetLinkObj::getFilter() const +{ + SolarMutexGuard aGuard; + OUString aRet; + ScTableLink* pLink = GetLink_Impl(); + if (pLink) + aRet = pLink->GetFilterName(); + return aRet; +} + +void ScSheetLinkObj::setFilter(const OUString& rFilter) +{ + SolarMutexGuard aGuard; + ScTableLink* pLink = GetLink_Impl(); + if (pLink) + { + pLink->Refresh( aFileName, rFilter, nullptr, pLink->GetRefreshDelaySeconds() ); + } +} + +OUString ScSheetLinkObj::getFilterOptions() const +{ + SolarMutexGuard aGuard; + OUString aRet; + ScTableLink* pLink = GetLink_Impl(); + if (pLink) + aRet = pLink->GetOptions(); + return aRet; +} + +void ScSheetLinkObj::setFilterOptions(const OUString& FilterOptions) +{ + SolarMutexGuard aGuard; + ScTableLink* pLink = GetLink_Impl(); + if (pLink) + { + OUString aOptStr(FilterOptions); + pLink->Refresh( aFileName, pLink->GetFilterName(), &aOptStr, pLink->GetRefreshDelaySeconds() ); + } +} + +sal_Int32 ScSheetLinkObj::getRefreshDelay() const +{ + SolarMutexGuard aGuard; + sal_Int32 nRet = 0; + ScTableLink* pLink = GetLink_Impl(); + if (pLink) + nRet = pLink->GetRefreshDelaySeconds(); + return nRet; +} + +void ScSheetLinkObj::setRefreshDelay(sal_Int32 nRefreshDelay) +{ + SolarMutexGuard aGuard; + ModifyRefreshDelay_Impl( nRefreshDelay ); +} + +ScSheetLinksObj::ScSheetLinksObj(ScDocShell* pDocSh) : + pDocShell( pDocSh ) +{ + pDocShell->GetDocument().AddUnoObject(*this); +} + +ScSheetLinksObj::~ScSheetLinksObj() +{ + SolarMutexGuard g; + + if (pDocShell) + pDocShell->GetDocument().RemoveUnoObject(*this); +} + +void ScSheetLinksObj::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + // we don't care about update of references here + + if ( rHint.GetId() == SfxHintId::Dying ) + { + pDocShell = nullptr; // became invalid + } +} + +// XSheetLinks + +rtl::Reference<ScSheetLinkObj> ScSheetLinksObj::GetObjectByIndex_Impl(sal_Int32 nIndex) +{ + if (!pDocShell) + return nullptr; + + typedef std::unordered_set<OUString> StrSetType; + StrSetType aNames; + ScDocument& rDoc = pDocShell->GetDocument(); + SCTAB nTabCount = rDoc.GetTableCount(); + sal_Int32 nCount = 0; + for (SCTAB nTab = 0; nTab < nTabCount; ++nTab) + { + if (!rDoc.IsLinked(nTab)) + continue; + + OUString aLinkDoc = rDoc.GetLinkDoc(nTab); + if (aNames.insert(aLinkDoc).second) + { + // unique document name. + if (nCount == nIndex) + return new ScSheetLinkObj( pDocShell, aLinkDoc ); + ++nCount; + } + } + + return nullptr; // no document or index too large +} + +rtl::Reference<ScSheetLinkObj> ScSheetLinksObj::GetObjectByName_Impl(const OUString& aName) +{ + // Name is the same as file name + + if (pDocShell) + { + ScDocument& rDoc = pDocShell->GetDocument(); + SCTAB nTabCount = rDoc.GetTableCount(); + for (SCTAB nTab=0; nTab<nTabCount; nTab++) + if (rDoc.IsLinked(nTab)) + { + //! case-insensitive ??? + OUString aLinkDoc = rDoc.GetLinkDoc( nTab ); + if ( aLinkDoc == aName ) + return new ScSheetLinkObj( pDocShell, aName ); + } + } + + return nullptr; +} + +// XEnumerationAccess +uno::Reference<container::XEnumeration> SAL_CALL ScSheetLinksObj::createEnumeration() +{ + SolarMutexGuard aGuard; + return new ScIndexEnumeration(this, "com.sun.star.sheet.SheetLinksEnumeration"); +} + +// XIndexAccess +sal_Int32 SAL_CALL ScSheetLinksObj::getCount() +{ + typedef std::unordered_set<OUString> StrSetType; + + SolarMutexGuard aGuard; + if (!pDocShell) + return 0; + + sal_Int32 nCount = 0; + + StrSetType aNames; + ScDocument& rDoc = pDocShell->GetDocument(); + SCTAB nTabCount = rDoc.GetTableCount(); + for (SCTAB nTab = 0; nTab < nTabCount; ++nTab) + { + if (!rDoc.IsLinked(nTab)) + continue; + + OUString aLinkDoc = rDoc.GetLinkDoc(nTab); + if (aNames.insert(aLinkDoc).second) + ++nCount; + } + return nCount; +} + +uno::Any SAL_CALL ScSheetLinksObj::getByIndex( sal_Int32 nIndex ) +{ + SolarMutexGuard aGuard; + uno::Reference<beans::XPropertySet> xLink(GetObjectByIndex_Impl(nIndex)); + if (!xLink.is()) + throw lang::IndexOutOfBoundsException(); + + return uno::Any(xLink); +} + +uno::Type SAL_CALL ScSheetLinksObj::getElementType() +{ + return cppu::UnoType<beans::XPropertySet>::get(); +} + +sal_Bool SAL_CALL ScSheetLinksObj::hasElements() +{ + SolarMutexGuard aGuard; + return ( getCount() != 0 ); +} + +uno::Any SAL_CALL ScSheetLinksObj::getByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + uno::Reference<beans::XPropertySet> xLink(GetObjectByName_Impl(aName)); + if (!xLink.is()) + throw container::NoSuchElementException(); + + return uno::Any(xLink); +} + +sal_Bool SAL_CALL ScSheetLinksObj::hasByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + // Name is the same as file name + + if (pDocShell) + { + ScDocument& rDoc = pDocShell->GetDocument(); + SCTAB nTabCount = rDoc.GetTableCount(); + for (SCTAB nTab=0; nTab<nTabCount; nTab++) + if (rDoc.IsLinked(nTab)) + { + //! case-insensitive ??? + OUString aLinkDoc(rDoc.GetLinkDoc( nTab )); + if ( aLinkDoc == aName ) + return true; + } + } + return false; +} + +uno::Sequence<OUString> SAL_CALL ScSheetLinksObj::getElementNames() +{ + typedef std::unordered_set<OUString> StrSetType; + + SolarMutexGuard aGuard; + // Name is the same as file name + + if (!pDocShell) + return uno::Sequence<OUString>(); + + StrSetType aNames; + ScDocument& rDoc = pDocShell->GetDocument(); + SCTAB nTabCount = rDoc.GetTableCount(); + + sal_Int32 nLinkCount = getCount(); + uno::Sequence<OUString> aSeq(nLinkCount); + OUString* pAry = aSeq.getArray(); + size_t nPos = 0; + for (SCTAB nTab = 0; nTab < nTabCount; ++nTab) + { + if (!rDoc.IsLinked(nTab)) + continue; + + OUString aLinkDoc = rDoc.GetLinkDoc(nTab); + if (aNames.insert(aLinkDoc).second) + pAry[nPos++] = aLinkDoc; + } + OSL_ENSURE( nPos==static_cast<size_t>(nLinkCount), "verzaehlt" ); + return aSeq; +} + +static ScAreaLink* lcl_GetAreaLink( ScDocShell* pDocShell, size_t nPos ) +{ + if (pDocShell) + { + sfx2::LinkManager* pLinkManager = pDocShell->GetDocument().GetLinkManager(); + size_t nTotalCount = pLinkManager->GetLinks().size(); + size_t nAreaCount = 0; + for (size_t i=0; i<nTotalCount; i++) + { + ::sfx2::SvBaseLink* pBase = pLinkManager->GetLinks()[i].get(); + if (auto pAreaLink = dynamic_cast<ScAreaLink*>( pBase)) + { + if ( nAreaCount == nPos ) + return pAreaLink; + ++nAreaCount; + } + } + } + return nullptr; // not found +} + +ScAreaLinkObj::ScAreaLinkObj(ScDocShell* pDocSh, size_t nP) : + aPropSet( lcl_GetSheetLinkMap() ), + pDocShell( pDocSh ), + nPos( nP ) +{ + pDocShell->GetDocument().AddUnoObject(*this); +} + +ScAreaLinkObj::~ScAreaLinkObj() +{ + SolarMutexGuard g; + + if (pDocShell) + pDocShell->GetDocument().RemoveUnoObject(*this); +} + +void ScAreaLinkObj::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + //! notify if links in document are changed + // UpdateRef is not needed here + + if ( auto pRefreshedHint = dynamic_cast<const ScLinkRefreshedHint*>(&rHint) ) + { + if ( pRefreshedHint->GetLinkType() == ScLinkRefType::AREA ) + { + // get this link to compare dest position + ScAreaLink* pLink = lcl_GetAreaLink(pDocShell, nPos); + if ( pLink && pLink->GetDestArea().aStart == pRefreshedHint->GetDestPos() ) + Refreshed_Impl(); + } + } + else + { + if ( rHint.GetId() == SfxHintId::Dying ) + pDocShell = nullptr; // pointer is invalid + } +} + +// XFileLink + +void ScAreaLinkObj::Modify_Impl( const OUString* pNewFile, const OUString* pNewFilter, + const OUString* pNewOptions, const OUString* pNewSource, + const table::CellRangeAddress* pNewDest ) +{ + ScAreaLink* pLink = lcl_GetAreaLink(pDocShell, nPos); + if (!pLink) + return; + + OUString aFile (pLink->GetFile()); + OUString aFilter (pLink->GetFilter()); + OUString aOptions (pLink->GetOptions()); + OUString aSource (pLink->GetSource()); + ScRange aDest (pLink->GetDestArea()); + sal_Int32 nRefreshDelaySeconds = pLink->GetRefreshDelaySeconds(); + + //! Undo delete + //! Undo merge + + sfx2::LinkManager* pLinkManager = pDocShell->GetDocument().GetLinkManager(); + pLinkManager->Remove( pLink ); + pLink = nullptr; // deleted along with remove + + bool bFitBlock = true; // move, if the size changes with update + if (pNewFile) + { + aFile = ScGlobal::GetAbsDocName( *pNewFile, pDocShell ); //! in InsertAreaLink? + } + if (pNewFilter) + aFilter = *pNewFilter; + if (pNewOptions) + aOptions = *pNewOptions; + if (pNewSource) + aSource = *pNewSource; + if (pNewDest) + { + ScUnoConversion::FillScRange( aDest, *pNewDest ); + bFitBlock = false; // new range was specified -> do not move the content + } + pDocShell->GetDocFunc().InsertAreaLink( aFile, aFilter, aOptions, aSource, + aDest, nRefreshDelaySeconds, bFitBlock, true ); +} + +void ScAreaLinkObj::ModifyRefreshDelay_Impl( sal_Int32 nRefreshDelaySeconds ) +{ + ScAreaLink* pLink = lcl_GetAreaLink( pDocShell, nPos ); + if( pLink ) + pLink->SetRefreshDelay( nRefreshDelaySeconds ); +} + +// XRefreshable + +void SAL_CALL ScAreaLinkObj::refresh() +{ + SolarMutexGuard aGuard; + ScAreaLink* pLink = lcl_GetAreaLink(pDocShell, nPos); + if (pLink) + pLink->Refresh( pLink->GetFile(), pLink->GetFilter(), pLink->GetSource(), pLink->GetRefreshDelaySeconds() ); +} + +void SAL_CALL ScAreaLinkObj::addRefreshListener( + const uno::Reference<util::XRefreshListener >& xListener ) +{ + SolarMutexGuard aGuard; + aRefreshListeners.push_back( xListener ); + + // hold one additional ref to keep this object alive as long as there are listeners + if ( aRefreshListeners.size() == 1 ) + acquire(); +} + +void SAL_CALL ScAreaLinkObj::removeRefreshListener( + const uno::Reference<util::XRefreshListener >& xListener ) +{ + SolarMutexGuard aGuard; + size_t nCount = aRefreshListeners.size(); + for ( size_t n=nCount; n--; ) + { + uno::Reference<util::XRefreshListener>& rObj = aRefreshListeners[n]; + if ( rObj == xListener ) + { + aRefreshListeners.erase( aRefreshListeners.begin() + n ); + if ( aRefreshListeners.empty() ) + release(); // release ref for listeners + break; + } + + if(n == 0) + break; + } +} + +void ScAreaLinkObj::Refreshed_Impl() +{ + lang::EventObject aEvent; + aEvent.Source.set(getXWeak()); + for (uno::Reference<util::XRefreshListener> & xRefreshListener : aRefreshListeners) + xRefreshListener->refreshed( aEvent ); +} + +// XPropertySet + +uno::Reference<beans::XPropertySetInfo> SAL_CALL ScAreaLinkObj::getPropertySetInfo() +{ + SolarMutexGuard aGuard; + static uno::Reference<beans::XPropertySetInfo> aRef( + new SfxItemPropertySetInfo( aPropSet.getPropertyMap() )); + return aRef; +} + +void SAL_CALL ScAreaLinkObj::setPropertyValue( + const OUString& aPropertyName, const uno::Any& aValue ) +{ + SolarMutexGuard aGuard; + OUString aValStr; + if ( aPropertyName == SC_UNONAME_LINKURL ) + { + if ( aValue >>= aValStr ) + setFileName( aValStr ); + } + else if ( aPropertyName == SC_UNONAME_FILTER ) + { + if ( aValue >>= aValStr ) + setFilter( aValStr ); + } + else if ( aPropertyName == SC_UNONAME_FILTOPT ) + { + if ( aValue >>= aValStr ) + setFilterOptions( aValStr ); + } + else if ( aPropertyName == SC_UNONAME_REFPERIOD ) + { + sal_Int32 nRefresh = 0; + if ( aValue >>= nRefresh ) + setRefreshDelay( nRefresh ); + } + else if ( aPropertyName == SC_UNONAME_REFDELAY ) + { + sal_Int32 nRefresh = 0; + if ( aValue >>= nRefresh ) + setRefreshDelay( nRefresh ); + } +} + +uno::Any SAL_CALL ScAreaLinkObj::getPropertyValue( const OUString& aPropertyName ) +{ + SolarMutexGuard aGuard; + uno::Any aRet; + if ( aPropertyName == SC_UNONAME_LINKURL ) + aRet <<= getFileName(); + else if ( aPropertyName == SC_UNONAME_FILTER ) + aRet <<= getFilter(); + else if ( aPropertyName == SC_UNONAME_FILTOPT ) + aRet <<= getFilterOptions(); + else if ( aPropertyName == SC_UNONAME_REFPERIOD ) + aRet <<= getRefreshDelay(); + else if ( aPropertyName == SC_UNONAME_REFDELAY ) + aRet <<= getRefreshDelay(); + return aRet; +} + +SC_IMPL_DUMMY_PROPERTY_LISTENER( ScAreaLinkObj ) + +// internal: + +OUString ScAreaLinkObj::getFileName() const +{ + SolarMutexGuard aGuard; + OUString aRet; + ScAreaLink* pLink = lcl_GetAreaLink(pDocShell, nPos); + if (pLink) + aRet = pLink->GetFile(); + return aRet; +} + +void ScAreaLinkObj::setFileName(const OUString& rNewName) +{ + SolarMutexGuard aGuard; + Modify_Impl( &rNewName, nullptr, nullptr, nullptr, nullptr ); +} + +OUString ScAreaLinkObj::getFilter() const +{ + SolarMutexGuard aGuard; + OUString aRet; + ScAreaLink* pLink = lcl_GetAreaLink(pDocShell, nPos); + if (pLink) + aRet = pLink->GetFilter(); + return aRet; +} + +void ScAreaLinkObj::setFilter(const OUString& Filter) +{ + SolarMutexGuard aGuard; + Modify_Impl( nullptr, &Filter, nullptr, nullptr, nullptr ); +} + +OUString ScAreaLinkObj::getFilterOptions() const +{ + SolarMutexGuard aGuard; + OUString aRet; + ScAreaLink* pLink = lcl_GetAreaLink(pDocShell, nPos); + if (pLink) + aRet = pLink->GetOptions(); + return aRet; +} + +void ScAreaLinkObj::setFilterOptions(const OUString& FilterOptions) +{ + SolarMutexGuard aGuard; + Modify_Impl( nullptr, nullptr, &FilterOptions, nullptr, nullptr ); +} + +sal_Int32 ScAreaLinkObj::getRefreshDelay() const +{ + SolarMutexGuard aGuard; + sal_Int32 nRet = 0; + ScAreaLink* pLink = lcl_GetAreaLink(pDocShell, nPos); + if (pLink) + nRet = pLink->GetRefreshDelaySeconds(); + return nRet; +} + +void ScAreaLinkObj::setRefreshDelay(sal_Int32 nRefreshDelay) +{ + SolarMutexGuard aGuard; + ModifyRefreshDelay_Impl( nRefreshDelay ); +} + +// XAreaLink + +OUString SAL_CALL ScAreaLinkObj::getSourceArea() +{ + SolarMutexGuard aGuard; + OUString aRet; + ScAreaLink* pLink = lcl_GetAreaLink(pDocShell, nPos); + if (pLink) + aRet = pLink->GetSource(); + return aRet; +} + +void SAL_CALL ScAreaLinkObj::setSourceArea( const OUString& aSourceArea ) +{ + SolarMutexGuard aGuard; + Modify_Impl( nullptr, nullptr, nullptr, &aSourceArea, nullptr ); +} + +table::CellRangeAddress SAL_CALL ScAreaLinkObj::getDestArea() +{ + SolarMutexGuard aGuard; + table::CellRangeAddress aRet; + ScAreaLink* pLink = lcl_GetAreaLink(pDocShell, nPos); + if (pLink) + ScUnoConversion::FillApiRange( aRet, pLink->GetDestArea() ); + return aRet; +} + +void SAL_CALL ScAreaLinkObj::setDestArea( const table::CellRangeAddress& aDestArea ) +{ + SolarMutexGuard aGuard; + Modify_Impl( nullptr, nullptr, nullptr, nullptr, &aDestArea ); +} + +ScAreaLinksObj::ScAreaLinksObj(ScDocShell* pDocSh) : + pDocShell( pDocSh ) +{ + pDocShell->GetDocument().AddUnoObject(*this); +} + +ScAreaLinksObj::~ScAreaLinksObj() +{ + SolarMutexGuard g; + + if (pDocShell) + pDocShell->GetDocument().RemoveUnoObject(*this); +} + +void ScAreaLinksObj::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + // we don't care about update of references here + + if ( rHint.GetId() == SfxHintId::Dying ) + { + pDocShell = nullptr; // became invalid + } +} + +// XAreaLinks + +rtl::Reference<ScAreaLinkObj> ScAreaLinksObj::GetObjectByIndex_Impl(sal_Int32 nIndex) +{ + if ( pDocShell && nIndex >= 0 && nIndex < getCount() ) + return new ScAreaLinkObj( pDocShell, static_cast<size_t>(nIndex) ); + + return nullptr; // not found +} + +void SAL_CALL ScAreaLinksObj::insertAtPosition( const table::CellAddress& aDestPos, + const OUString& aFileName, + const OUString& aSourceArea, + const OUString& aFilter, + const OUString& aFilterOptions ) +{ + SolarMutexGuard aGuard; + if (pDocShell) + { + OUString aFileStr (aFileName); + ScAddress aDestAddr( static_cast<SCCOL>(aDestPos.Column), static_cast<SCROW>(aDestPos.Row), aDestPos.Sheet ); + + aFileStr = ScGlobal::GetAbsDocName( aFileStr, pDocShell ); //! in InsertAreaLink ??? + pDocShell->GetDocFunc().InsertAreaLink( aFileStr, aFilter, aFilterOptions, + aSourceArea, ScRange(aDestAddr), + /*nRefreshDelaySeconds*/0, false, true ); // don't move contents + } +} + +void SAL_CALL ScAreaLinksObj::removeByIndex( sal_Int32 nIndex ) +{ + SolarMutexGuard aGuard; + ScAreaLink* pLink = lcl_GetAreaLink(pDocShell, static_cast<size_t>(nIndex)); + if (pLink) + { + //! SetAddUndo or what + + sfx2::LinkManager* pLinkManager = pDocShell->GetDocument().GetLinkManager(); + pLinkManager->Remove( pLink ); + } +} + +// XEnumerationAccess + +uno::Reference<container::XEnumeration> SAL_CALL ScAreaLinksObj::createEnumeration() +{ + SolarMutexGuard aGuard; + return new ScIndexEnumeration(this, "com.sun.star.sheet.CellAreaLinksEnumeration"); +} + +// XIndexAccess + +sal_Int32 SAL_CALL ScAreaLinksObj::getCount() +{ + SolarMutexGuard aGuard; + sal_Int32 nAreaCount = 0; + if (pDocShell) + { + sfx2::LinkManager* pLinkManager = pDocShell->GetDocument().GetLinkManager(); + size_t nTotalCount = pLinkManager->GetLinks().size(); + for (size_t i=0; i<nTotalCount; i++) + { + ::sfx2::SvBaseLink* pBase = pLinkManager->GetLinks()[i].get(); + if (dynamic_cast<const ScAreaLink*>( pBase) != nullptr) + ++nAreaCount; + } + } + return nAreaCount; +} + +uno::Any SAL_CALL ScAreaLinksObj::getByIndex( sal_Int32 nIndex ) +{ + SolarMutexGuard aGuard; + uno::Reference<sheet::XAreaLink> xLink(GetObjectByIndex_Impl(nIndex)); + if (!xLink.is()) + throw lang::IndexOutOfBoundsException(); + + return uno::Any(xLink); + +} + +uno::Type SAL_CALL ScAreaLinksObj::getElementType() +{ + return cppu::UnoType<sheet::XAreaLink>::get(); +} + +sal_Bool SAL_CALL ScAreaLinksObj::hasElements() +{ + SolarMutexGuard aGuard; + return ( getCount() != 0 ); +} + +ScDDELinkObj::ScDDELinkObj(ScDocShell* pDocSh, OUString aA, + OUString aT, OUString aI) : + pDocShell( pDocSh ), + aAppl(std::move( aA )), + aTopic(std::move( aT )), + aItem(std::move( aI )) +{ + pDocShell->GetDocument().AddUnoObject(*this); +} + +ScDDELinkObj::~ScDDELinkObj() +{ + SolarMutexGuard g; + + if (pDocShell) + pDocShell->GetDocument().RemoveUnoObject(*this); +} + +void ScDDELinkObj::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + //! notify if links in document are changed + // UpdateRef is not needed here + + if ( auto pRefreshedHint = dynamic_cast<const ScLinkRefreshedHint*>(&rHint) ) + { + if ( pRefreshedHint->GetLinkType() == ScLinkRefType::DDE && + pRefreshedHint->GetDdeAppl() == aAppl && + pRefreshedHint->GetDdeTopic() == aTopic && + pRefreshedHint->GetDdeItem() == aItem ) //! mode is ignored + Refreshed_Impl(); + } + else + { + if ( rHint.GetId() == SfxHintId::Dying ) + pDocShell = nullptr; // pointer is invalid + } +} + +// XNamed + +static OUString lcl_BuildDDEName( std::u16string_view rAppl, std::u16string_view rTopic, std::u16string_view rItem ) +{ + // Appl|Topic!Item (like Excel) + OUString aRet = OUString::Concat(rAppl) + "|" + rTopic + "!" + rItem; + return aRet; +} + +OUString SAL_CALL ScDDELinkObj::getName() +{ + SolarMutexGuard aGuard; + return lcl_BuildDDEName( aAppl, aTopic, aItem ); +} + +void SAL_CALL ScDDELinkObj::setName( const OUString& /* aName */ ) +{ + // name can't be changed (formulas wouldn't find the link) + throw uno::RuntimeException(); +} + +// XDDELink + +OUString SAL_CALL ScDDELinkObj::getApplication() +{ + //! Test if the link is still in the document? + + return aAppl; +} + +OUString SAL_CALL ScDDELinkObj::getTopic() +{ + //! Test if the link is still in the document? + + return aTopic; +} + +OUString SAL_CALL ScDDELinkObj::getItem() +{ + //! Test if the link is still in the document? + + return aItem; +} + +// XRefreshable + +void SAL_CALL ScDDELinkObj::refresh() +{ + SolarMutexGuard aGuard; + if (pDocShell) + { + sc::DocumentLinkManager& rMgr = pDocShell->GetDocument().GetDocLinkManager(); + rMgr.updateDdeLink(aAppl, aTopic, aItem); + } +} + +void SAL_CALL ScDDELinkObj::addRefreshListener( + const uno::Reference<util::XRefreshListener >& xListener ) +{ + SolarMutexGuard aGuard; + aRefreshListeners.push_back( xListener ); + + // hold one additional ref to keep this object alive as long as there are listeners + if ( aRefreshListeners.size() == 1 ) + acquire(); +} + +void SAL_CALL ScDDELinkObj::removeRefreshListener( + const uno::Reference<util::XRefreshListener >& xListener ) +{ + SolarMutexGuard aGuard; + size_t nCount = aRefreshListeners.size(); + for ( size_t n=nCount; n--; ) + { + uno::Reference<util::XRefreshListener>& rObj = aRefreshListeners[n]; + if ( rObj == xListener ) + { + aRefreshListeners.erase( aRefreshListeners.begin() + n ); + if ( aRefreshListeners.empty() ) + release(); // release ref for listeners + break; + } + } +} + +// XDDELinkResults + +uno::Sequence< uno::Sequence< uno::Any > > ScDDELinkObj::getResults( ) +{ + SolarMutexGuard aGuard; + uno::Sequence< uno::Sequence< uno::Any > > aReturn; + bool bSuccess = false; + + if ( pDocShell ) + { + ScDocument& rDoc = pDocShell->GetDocument(); + size_t nPos = 0; + if ( rDoc.FindDdeLink( aAppl, aTopic, aItem, SC_DDE_IGNOREMODE, nPos ) ) + { + const ScMatrix* pMatrix = rDoc.GetDdeLinkResultMatrix( nPos ); + if ( pMatrix ) + { + uno::Any aAny; + if ( ScRangeToSequence::FillMixedArray( aAny, pMatrix, true ) ) + { + aAny >>= aReturn; + } + } + bSuccess = true; + } + } + + if ( !bSuccess ) + { + throw uno::RuntimeException( + "ScDDELinkObj::getResults: failed to get results!" ); + } + + return aReturn; +} + +void ScDDELinkObj::setResults( const uno::Sequence< uno::Sequence< uno::Any > >& aResults ) +{ + SolarMutexGuard aGuard; + bool bSuccess = false; + + if ( pDocShell ) + { + ScDocument& rDoc = pDocShell->GetDocument(); + size_t nPos = 0; + if ( rDoc.FindDdeLink( aAppl, aTopic, aItem, SC_DDE_IGNOREMODE, nPos ) ) + { + ScMatrixRef xMatrix = ScSequenceToMatrix::CreateMixedMatrix( Any(aResults) ); + bSuccess = rDoc.SetDdeLinkResultMatrix( nPos, xMatrix ); + } + } + + if ( !bSuccess ) + { + throw uno::RuntimeException( + "ScDDELinkObj::setResults: failed to set results!" ); + } +} + +void ScDDELinkObj::Refreshed_Impl() +{ + lang::EventObject aEvent; + aEvent.Source.set(getXWeak()); + for (uno::Reference<util::XRefreshListener> & xRefreshListener : aRefreshListeners) + xRefreshListener->refreshed( aEvent ); +} + +ScDDELinksObj::ScDDELinksObj(ScDocShell* pDocSh) : + pDocShell( pDocSh ) +{ + pDocShell->GetDocument().AddUnoObject(*this); +} + +ScDDELinksObj::~ScDDELinksObj() +{ + SolarMutexGuard g; + + if (pDocShell) + pDocShell->GetDocument().RemoveUnoObject(*this); +} + +void ScDDELinksObj::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + // we don't care about update of references here + + if ( rHint.GetId() == SfxHintId::Dying ) + { + pDocShell = nullptr; // became invalid + } +} + +// XDDELinks + +rtl::Reference<ScDDELinkObj> ScDDELinksObj::GetObjectByIndex_Impl(sal_Int32 nIndex) +{ + if (pDocShell) + { + OUString aAppl, aTopic, aItem; + if ( pDocShell->GetDocument().GetDdeLinkData( static_cast<size_t>(nIndex), aAppl, aTopic, aItem ) ) + return new ScDDELinkObj( pDocShell, aAppl, aTopic, aItem ); + } + return nullptr; +} + +rtl::Reference<ScDDELinkObj> ScDDELinksObj::GetObjectByName_Impl(std::u16string_view aName) +{ + if (pDocShell) + { + OUString aAppl, aTopic, aItem; + + ScDocument& rDoc = pDocShell->GetDocument(); + size_t nCount = rDoc.GetDocLinkManager().getDdeLinkCount(); + for (size_t i=0; i<nCount; i++) + { + rDoc.GetDdeLinkData( i, aAppl, aTopic, aItem ); + if ( lcl_BuildDDEName(aAppl, aTopic, aItem) == aName ) + return new ScDDELinkObj( pDocShell, aAppl, aTopic, aItem ); + } + } + return nullptr; +} + +// XEnumerationAccess + +uno::Reference<container::XEnumeration> SAL_CALL ScDDELinksObj::createEnumeration() +{ + SolarMutexGuard aGuard; + return new ScIndexEnumeration(this, "com.sun.star.sheet.DDELinksEnumeration"); +} + +// XIndexAccess + +sal_Int32 SAL_CALL ScDDELinksObj::getCount() +{ + SolarMutexGuard aGuard; + sal_Int32 nAreaCount = 0; + if (pDocShell) + nAreaCount = pDocShell->GetDocument().GetDocLinkManager().getDdeLinkCount(); + return nAreaCount; +} + +uno::Any SAL_CALL ScDDELinksObj::getByIndex( sal_Int32 nIndex ) +{ + SolarMutexGuard aGuard; + uno::Reference<sheet::XDDELink> xLink(GetObjectByIndex_Impl(nIndex)); + if (!xLink.is()) + throw lang::IndexOutOfBoundsException(); + + return uno::Any(xLink); +} + +uno::Type SAL_CALL ScDDELinksObj::getElementType() +{ + return cppu::UnoType<sheet::XDDELink>::get(); +} + +sal_Bool SAL_CALL ScDDELinksObj::hasElements() +{ + SolarMutexGuard aGuard; + return ( getCount() != 0 ); +} + +uno::Any SAL_CALL ScDDELinksObj::getByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + uno::Reference<sheet::XDDELink> xLink(GetObjectByName_Impl(aName)); + if (!xLink.is()) + throw container::NoSuchElementException(); + + return uno::Any(xLink); +} + +uno::Sequence<OUString> SAL_CALL ScDDELinksObj::getElementNames() +{ + SolarMutexGuard aGuard; + if (pDocShell) + { + OUString aAppl, aTopic, aItem; + + ScDocument& rDoc = pDocShell->GetDocument(); + size_t nCount = pDocShell->GetDocument().GetDocLinkManager().getDdeLinkCount(); + uno::Sequence<OUString> aSeq(nCount); + OUString* pAry = aSeq.getArray(); + + for (size_t i=0; i<nCount; i++) + { + rDoc.GetDdeLinkData( i, aAppl, aTopic, aItem ); + pAry[i] = lcl_BuildDDEName(aAppl, aTopic, aItem); + } + return aSeq; + } + return uno::Sequence<OUString>(); +} + +sal_Bool SAL_CALL ScDDELinksObj::hasByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + if (pDocShell) + { + OUString aAppl, aTopic, aItem; + + ScDocument& rDoc = pDocShell->GetDocument(); + size_t nCount = pDocShell->GetDocument().GetDocLinkManager().getDdeLinkCount(); + for (size_t i=0; i<nCount; i++) + { + rDoc.GetDdeLinkData( i, aAppl, aTopic, aItem ); + if ( lcl_BuildDDEName(aAppl, aTopic, aItem) == aName ) + return true; + } + } + return false; +} + +// XDDELinks + +uno::Reference< sheet::XDDELink > ScDDELinksObj::addDDELink( + const OUString& aApplication, const OUString& aTopic, + const OUString& aItem, css::sheet::DDELinkMode nMode ) +{ + SolarMutexGuard aGuard; + uno::Reference< sheet::XDDELink > xLink; + + if ( pDocShell ) + { + ScDocument& rDoc = pDocShell->GetDocument(); + sal_uInt8 nMod = SC_DDE_DEFAULT; + switch ( nMode ) + { + case sheet::DDELinkMode_DEFAULT: + { + nMod = SC_DDE_DEFAULT; + } + break; + case sheet::DDELinkMode_ENGLISH: + { + nMod = SC_DDE_ENGLISH; + } + break; + case sheet::DDELinkMode_TEXT: + { + nMod = SC_DDE_TEXT; + } + break; + default: + { + } + break; + } + + if ( rDoc.CreateDdeLink( aApplication, aTopic, aItem, nMod, ScMatrixRef() ) ) + { + const OUString aName( lcl_BuildDDEName( aApplication, aTopic, aItem ) ); + xLink.set( GetObjectByName_Impl( aName ) ); + } + } + + if ( !xLink.is() ) + { + throw uno::RuntimeException( + "ScDDELinksObj::addDDELink: cannot add DDE link!" ); + } + + return xLink; +} + +ScExternalSheetCacheObj::ScExternalSheetCacheObj(ScDocShell* pDocShell, ScExternalRefCache::TableTypeRef pTable, size_t nIndex) : + mpDocShell(pDocShell), + mpTable(std::move(pTable)), + mnIndex(nIndex) +{ +} + +ScExternalSheetCacheObj::~ScExternalSheetCacheObj() +{ +} + +void SAL_CALL ScExternalSheetCacheObj::setCellValue(sal_Int32 nCol, sal_Int32 nRow, const Any& rValue) +{ + SolarMutexGuard aGuard; + if (nRow < 0 || nCol < 0) + throw IllegalArgumentException(); + + ScExternalRefCache::TokenRef pToken; + double fVal = 0.0; + OUString aVal; + if (rValue >>= fVal) + pToken.reset(new FormulaDoubleToken(fVal)); + else if (rValue >>= aVal) + { + svl::SharedStringPool& rPool = mpDocShell->GetDocument().GetSharedStringPool(); + svl::SharedString aSS = rPool.intern(aVal); + pToken.reset(new FormulaStringToken(std::move(aSS))); + } + else + // unidentified value type. + return; + + mpTable->setCell(static_cast<SCCOL>(nCol), static_cast<SCROW>(nRow), pToken); +} + +Any SAL_CALL ScExternalSheetCacheObj::getCellValue(sal_Int32 nCol, sal_Int32 nRow) +{ + SolarMutexGuard aGuard; + if (nRow < 0 || nCol < 0) + throw IllegalArgumentException(); + + FormulaToken* pToken = mpTable->getCell(static_cast<SCCOL>(nCol), static_cast<SCROW>(nRow)).get(); + if (!pToken) + throw IllegalArgumentException(); + + Any aValue; + switch (pToken->GetType()) + { + case svDouble: + { + double fVal = pToken->GetDouble(); + aValue <<= fVal; + } + break; + case svString: + { + OUString aVal = pToken->GetString().getString(); + aValue <<= aVal; + } + break; + default: + throw IllegalArgumentException(); + } + return aValue; +} + +Sequence< sal_Int32 > SAL_CALL ScExternalSheetCacheObj::getAllRows() +{ + SolarMutexGuard aGuard; + vector<SCROW> aRows; + mpTable->getAllRows(aRows); + size_t nSize = aRows.size(); + Sequence<sal_Int32> aRowsSeq(nSize); + auto aRowsSeqRange = asNonConstRange(aRowsSeq); + for (size_t i = 0; i < nSize; ++i) + aRowsSeqRange[i] = aRows[i]; + + return aRowsSeq; +} + +Sequence< sal_Int32 > SAL_CALL ScExternalSheetCacheObj::getAllColumns(sal_Int32 nRow) +{ + SolarMutexGuard aGuard; + if (nRow < 0) + throw IllegalArgumentException(); + + vector<SCCOL> aCols; + mpTable->getAllCols(static_cast<SCROW>(nRow), aCols); + size_t nSize = aCols.size(); + Sequence<sal_Int32> aColsSeq(nSize); + auto aColsSeqRange = asNonConstRange(aColsSeq); + for (size_t i = 0; i < nSize; ++i) + aColsSeqRange[i] = aCols[i]; + + return aColsSeq; +} + +sal_Int32 SAL_CALL ScExternalSheetCacheObj::getTokenIndex() +{ + return static_cast< sal_Int32 >( mnIndex ); +} + +ScExternalDocLinkObj::ScExternalDocLinkObj(ScDocShell* pDocShell, ScExternalRefManager* pRefMgr, sal_uInt16 nFileId) : + mpDocShell(pDocShell), mpRefMgr(pRefMgr), mnFileId(nFileId) +{ +} + +ScExternalDocLinkObj::~ScExternalDocLinkObj() +{ +} + +uno::Reference< sheet::XExternalSheetCache > SAL_CALL ScExternalDocLinkObj::addSheetCache( + const OUString& aSheetName, sal_Bool bDynamicCache ) +{ + SolarMutexGuard aGuard; + size_t nIndex = 0; + ScExternalRefCache::TableTypeRef pTable = mpRefMgr->getCacheTable(mnFileId, aSheetName, true, &nIndex); + if (!bDynamicCache) + // Set the whole table cached to prevent access to the source document. + pTable->setWholeTableCached(); + + uno::Reference< sheet::XExternalSheetCache > aSheetCache(new ScExternalSheetCacheObj(mpDocShell, pTable, nIndex)); + return aSheetCache; +} + +Any SAL_CALL ScExternalDocLinkObj::getByName(const OUString &aName) +{ + SolarMutexGuard aGuard; + size_t nIndex = 0; + ScExternalRefCache::TableTypeRef pTable = mpRefMgr->getCacheTable(mnFileId, aName, false, &nIndex); + if (!pTable) + throw container::NoSuchElementException(); + + uno::Reference< sheet::XExternalSheetCache > aSheetCache(new ScExternalSheetCacheObj(mpDocShell, pTable, nIndex)); + + return Any(aSheetCache); +} + +Sequence< OUString > SAL_CALL ScExternalDocLinkObj::getElementNames() +{ + SolarMutexGuard aGuard; + vector<OUString> aTabNames; + mpRefMgr->getAllCachedTableNames(mnFileId, aTabNames); + + // #i116940# be consistent with getByName: include only table names which have a cache already + vector<OUString> aValidNames; + std::copy_if(aTabNames.begin(), aTabNames.end(), std::back_inserter(aValidNames), + [&](const OUString& rTabName) { return mpRefMgr->getCacheTable(mnFileId, rTabName, false); }); + + Sequence<OUString> aSeq(comphelper::containerToSequence(aValidNames)); + return aSeq; +} + +sal_Bool SAL_CALL ScExternalDocLinkObj::hasByName(const OUString &aName) +{ + SolarMutexGuard aGuard; + + // #i116940# be consistent with getByName: allow only table names which have a cache already + ScExternalRefCache::TableTypeRef pTable = mpRefMgr->getCacheTable(mnFileId, aName, false); + return bool(pTable); +} + +sal_Int32 SAL_CALL ScExternalDocLinkObj::getCount() +{ + SolarMutexGuard aGuard; + + // #i116940# be consistent with getByName: count only table names which have a cache already + return getElementNames().getLength(); +} + +Any SAL_CALL ScExternalDocLinkObj::getByIndex(sal_Int32 nApiIndex) +{ + SolarMutexGuard aGuard; + + // #i116940# Can't use nApiIndex as index for the ref manager, because the API counts only + // the entries which have a cache already. Quick solution: Use getElementNames. + Sequence< OUString > aNames( getElementNames() ); + if (nApiIndex < 0 || nApiIndex >= aNames.getLength()) + throw lang::IndexOutOfBoundsException(); + + size_t nIndex = 0; + ScExternalRefCache::TableTypeRef pTable = mpRefMgr->getCacheTable(mnFileId, aNames[nApiIndex], false, &nIndex); + if (!pTable) + throw lang::IndexOutOfBoundsException(); + + uno::Reference< sheet::XExternalSheetCache > aSheetCache(new ScExternalSheetCacheObj(mpDocShell, pTable, nIndex)); + + return Any(aSheetCache); +} + +uno::Reference< container::XEnumeration > SAL_CALL ScExternalDocLinkObj::createEnumeration() +{ + SolarMutexGuard aGuard; + uno::Reference< container::XEnumeration > aRef( + new ScIndexEnumeration(this, "com.sun.star.sheet.ExternalDocLink")); + return aRef; +} + +uno::Type SAL_CALL ScExternalDocLinkObj::getElementType() +{ + return cppu::UnoType<sheet::XExternalDocLink>::get(); +} + +sal_Bool SAL_CALL ScExternalDocLinkObj::hasElements() +{ + SolarMutexGuard aGuard; + + // #i116940# be consistent with getByName: count only table names which have a cache already + return getElementNames().hasElements(); +} + +sal_Int32 SAL_CALL ScExternalDocLinkObj::getTokenIndex() +{ + return static_cast<sal_Int32>(mnFileId); +} + +ScExternalDocLinksObj::ScExternalDocLinksObj(ScDocShell* pDocShell) : + mpDocShell(pDocShell), + mpRefMgr(pDocShell->GetDocument().GetExternalRefManager()) +{ +} + +ScExternalDocLinksObj::~ScExternalDocLinksObj() +{ +} + +uno::Reference< sheet::XExternalDocLink > SAL_CALL ScExternalDocLinksObj::addDocLink( + const OUString& aDocName ) +{ + SolarMutexGuard aGuard; + OUString aDocUrl( ScGlobal::GetAbsDocName( aDocName, mpDocShell)); + sal_uInt16 nFileId = mpRefMgr->getExternalFileId(aDocUrl); + uno::Reference< sheet::XExternalDocLink > aDocLink(new ScExternalDocLinkObj(mpDocShell, mpRefMgr, nFileId)); + return aDocLink; +} + +Any SAL_CALL ScExternalDocLinksObj::getByName(const OUString &aName) +{ + SolarMutexGuard aGuard; + OUString aDocUrl( ScGlobal::GetAbsDocName( aName, mpDocShell)); + if (!mpRefMgr->hasExternalFile(aDocUrl)) + throw container::NoSuchElementException(); + + sal_uInt16 nFileId = mpRefMgr->getExternalFileId(aDocUrl); + uno::Reference< sheet::XExternalDocLink > aDocLink(new ScExternalDocLinkObj(mpDocShell, mpRefMgr, nFileId)); + + return Any(aDocLink); +} + +Sequence< OUString > SAL_CALL ScExternalDocLinksObj::getElementNames() +{ + SolarMutexGuard aGuard; + sal_uInt16 n = mpRefMgr->getExternalFileCount(); + Sequence<OUString> aSeq(n); + auto aSeqRange = asNonConstRange(aSeq); + for (sal_uInt16 i = 0; i < n; ++i) + { + const OUString* pName = mpRefMgr->getExternalFileName(i); + aSeqRange[i] = pName ? *pName : OUString(); + } + + return aSeq; +} + +sal_Bool SAL_CALL ScExternalDocLinksObj::hasByName(const OUString &aName) +{ + SolarMutexGuard aGuard; + return mpRefMgr->hasExternalFile(aName); +} + +sal_Int32 SAL_CALL ScExternalDocLinksObj::getCount() +{ + SolarMutexGuard aGuard; + return mpRefMgr->getExternalFileCount(); +} + +Any SAL_CALL ScExternalDocLinksObj::getByIndex(sal_Int32 nIndex) +{ + SolarMutexGuard aGuard; + if (nIndex > ::std::numeric_limits<sal_uInt16>::max() || nIndex < ::std::numeric_limits<sal_uInt16>::min()) + throw lang::IndexOutOfBoundsException(); + + sal_uInt16 nFileId = static_cast<sal_uInt16>(nIndex); + + if (!mpRefMgr->hasExternalFile(nFileId)) + throw lang::IndexOutOfBoundsException(); + + uno::Reference< sheet::XExternalDocLink > aDocLink(new ScExternalDocLinkObj(mpDocShell, mpRefMgr, nFileId)); + return Any(aDocLink); +} + +uno::Reference< container::XEnumeration > SAL_CALL ScExternalDocLinksObj::createEnumeration() +{ + SolarMutexGuard aGuard; + uno::Reference< container::XEnumeration > aRef( + new ScIndexEnumeration(this, "com.sun.star.sheet.ExternalDocLinks")); + return aRef; +} + +uno::Type SAL_CALL ScExternalDocLinksObj::getElementType() +{ + return cppu::UnoType<sheet::XExternalDocLinks>::get(); +} + +sal_Bool SAL_CALL ScExternalDocLinksObj::hasElements() +{ + SolarMutexGuard aGuard; + return mpRefMgr->getExternalFileCount() > 0; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/unoobj/listenercalls.cxx b/sc/source/ui/unoobj/listenercalls.cxx new file mode 100644 index 0000000000..7ff7c7df09 --- /dev/null +++ b/sc/source/ui/unoobj/listenercalls.cxx @@ -0,0 +1,69 @@ +/* -*- 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 <com/sun/star/util/XModifyListener.hpp> +#include <osl/diagnose.h> + +#include <listenercalls.hxx> + +using namespace com::sun::star; + +ScUnoListenerCalls::ScUnoListenerCalls() {} + +ScUnoListenerCalls::~ScUnoListenerCalls() +{ + OSL_ENSURE(aEntries.empty(), "unhandled listener calls remaining"); +} + +void ScUnoListenerCalls::Add(const uno::Reference<util::XModifyListener>& rListener, + const lang::EventObject& rEvent) +{ + if (rListener.is()) + aEntries.emplace_back(rListener, rEvent); +} + +void ScUnoListenerCalls::ExecuteAndClear() +{ + // Execute all stored calls and remove them from the list. + // During each modified() call, Add may be called again. + // These new calls are executed here, too. + + std::list<ScUnoListenerEntry>::iterator aItr(aEntries.begin()); + while (aItr != aEntries.end()) + { + ScUnoListenerEntry aEntry = *aItr; + try + { + aEntry.xListener->modified(aEntry.aEvent); + } + catch (const uno::RuntimeException&) + { + // the listener is an external object and may throw a RuntimeException + // for reasons we don't know + } + + // New calls that are added during the modified() call are appended to the end + // of aEntries, so the loop will catch them, too (as long as erase happens + // after modified). + + aItr = aEntries.erase(aItr); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/unoobj/miscuno.cxx b/sc/source/ui/unoobj/miscuno.cxx new file mode 100644 index 0000000000..5c875bc0b7 --- /dev/null +++ b/sc/source/ui/unoobj/miscuno.cxx @@ -0,0 +1,285 @@ +/* -*- 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 <sal/config.h> + +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <cppuhelper/supportsservice.hxx> +#include <o3tl/any.hxx> +#include <utility> +#include <vcl/svapp.hxx> + +#include <miscuno.hxx> + +using namespace com::sun::star; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Any; + +SC_SIMPLE_SERVICE_INFO( ScNameToIndexAccess, "ScNameToIndexAccess", "stardiv.unknown" ) + +bool ScUnoHelpFunctions::GetBoolProperty( const uno::Reference<beans::XPropertySet>& xProp, + const OUString& rName, bool bDefault ) +{ + bool bRet = bDefault; + if ( xProp.is() ) + { + try + { + xProp->getPropertyValue( rName ) >>= bRet; + } + catch(uno::Exception&) + { + // keep default + } + } + return bRet; +} + +sal_Int16 ScUnoHelpFunctions::GetShortProperty( const css::uno::Reference< css::beans::XPropertySet>& xProp, + const OUString& rName, sal_Int16 nDefault ) +{ + sal_Int16 nRet = nDefault; + if ( xProp.is() ) + { + try + { + xProp->getPropertyValue( rName ) >>= nRet; + } + catch(uno::Exception&) + { + // keep default + } + } + return nRet; +} + +sal_Int32 ScUnoHelpFunctions::GetLongProperty( const uno::Reference<beans::XPropertySet>& xProp, + const OUString& rName ) +{ + sal_Int32 nRet = 0; + if ( xProp.is() ) + { + try + { + //! type conversion??? + xProp->getPropertyValue( rName ) >>= nRet; + } + catch(uno::Exception&) + { + // keep default + } + } + return nRet; +} + +sal_Int32 ScUnoHelpFunctions::GetEnumPropertyImpl( const uno::Reference<beans::XPropertySet>& xProp, + const OUString& rName, sal_Int32 nDefault ) +{ + sal_Int32 nRet = nDefault; + if ( xProp.is() ) + { + try + { + uno::Any aAny(xProp->getPropertyValue( rName )); + + if ( aAny.getValueTypeClass() == uno::TypeClass_ENUM ) + { + //! get enum value from any??? + nRet = *static_cast<sal_Int32 const *>(aAny.getValue()); + } + else + { + //! type conversion??? + aAny >>= nRet; + } + } + catch(uno::Exception&) + { + // keep default + } + } + return nRet; +} + +OUString ScUnoHelpFunctions::GetStringProperty( + const Reference<beans::XPropertySet>& xProp, const OUString& rName, const OUString& rDefault ) +{ + OUString aRet = rDefault; + if (!xProp.is()) + return aRet; + + try + { + Any any = xProp->getPropertyValue(rName); + any >>= aRet; + } + catch (const uno::Exception&) + { + } + + return aRet; +} + +bool ScUnoHelpFunctions::GetBoolFromAny( const uno::Any& aAny ) +{ + auto b = o3tl::tryAccess<bool>(aAny); + return b.has_value() && *b; +} + +sal_Int16 ScUnoHelpFunctions::GetInt16FromAny( const uno::Any& aAny ) +{ + sal_Int16 nRet = 0; + if ( aAny >>= nRet ) + return nRet; + return 0; +} + +sal_Int32 ScUnoHelpFunctions::GetInt32FromAny( const uno::Any& aAny ) +{ + sal_Int32 nRet = 0; + if ( aAny >>= nRet ) + return nRet; + return 0; +} + +sal_Int32 ScUnoHelpFunctions::GetEnumFromAny( const uno::Any& aAny ) +{ + sal_Int32 nRet = 0; + if ( aAny.getValueTypeClass() == uno::TypeClass_ENUM ) + nRet = *static_cast<sal_Int32 const *>(aAny.getValue()); + else + aAny >>= nRet; + return nRet; +} + +void ScUnoHelpFunctions::SetOptionalPropertyValue( + const Reference<beans::XPropertySet>& rPropSet, const char* pPropName, const Any& rVal ) +{ + SetOptionalPropertyValue(rPropSet, OUString::createFromAscii(pPropName), rVal); +} + +void ScUnoHelpFunctions::SetOptionalPropertyValue( + const Reference<beans::XPropertySet>& rPropSet, const OUString& sPropName, const Any& rVal ) +{ + try + { + rPropSet->setPropertyValue(sPropName, rVal); + } + catch (const beans::UnknownPropertyException&) + { + // ignored - not supported. + } +} + +ScIndexEnumeration::ScIndexEnumeration(uno::Reference<container::XIndexAccess> xInd, + OUString aServiceName) : + xIndex(std::move( xInd )), + sServiceName(std::move(aServiceName)), + nPos( 0 ) +{ +} + +ScIndexEnumeration::~ScIndexEnumeration() +{ +} + +// XEnumeration + +sal_Bool SAL_CALL ScIndexEnumeration::hasMoreElements() +{ + SolarMutexGuard aGuard; + return ( nPos < xIndex->getCount() ); +} + +uno::Any SAL_CALL ScIndexEnumeration::nextElement() +{ + SolarMutexGuard aGuard; + uno::Any aReturn; + try + { + aReturn = xIndex->getByIndex(nPos++); + } + catch (lang::IndexOutOfBoundsException&) + { + throw container::NoSuchElementException(); + } + return aReturn; +} + +OUString SAL_CALL ScIndexEnumeration::getImplementationName() +{ + return "ScIndexEnumeration"; +} + +sal_Bool SAL_CALL ScIndexEnumeration::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +css::uno::Sequence< OUString > + SAL_CALL ScIndexEnumeration::getSupportedServiceNames() +{ + return { sServiceName }; +} + +ScNameToIndexAccess::ScNameToIndexAccess( css::uno::Reference< + css::container::XNameAccess> xNameObj ) : + xNameAccess(std::move( xNameObj )) +{ + //! test for XIndexAccess interface at rNameObj, use that instead! + + if ( xNameAccess.is() ) + aNames = xNameAccess->getElementNames(); +} + +ScNameToIndexAccess::~ScNameToIndexAccess() +{ +} + +// XIndexAccess + +sal_Int32 SAL_CALL ScNameToIndexAccess::getCount( ) +{ + return aNames.getLength(); +} + +css::uno::Any SAL_CALL ScNameToIndexAccess::getByIndex( sal_Int32 nIndex ) +{ + if ( xNameAccess.is() && nIndex >= 0 && nIndex < aNames.getLength() ) + return xNameAccess->getByName( aNames.getConstArray()[nIndex] ); + + throw lang::IndexOutOfBoundsException(); +} + +// XElementAccess + +css::uno::Type SAL_CALL ScNameToIndexAccess::getElementType( ) +{ + if ( xNameAccess.is() ) + return xNameAccess->getElementType(); + else + return uno::Type(); +} + +sal_Bool SAL_CALL ScNameToIndexAccess::hasElements( ) +{ + return getCount() > 0; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/unoobj/nameuno.cxx b/sc/source/ui/unoobj/nameuno.cxx new file mode 100644 index 0000000000..6a43a4c05f --- /dev/null +++ b/sc/source/ui/unoobj/nameuno.cxx @@ -0,0 +1,1147 @@ +/* -*- 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 <o3tl/safeint.hxx> +#include <svl/hint.hxx> +#include <utility> +#include <vcl/svapp.hxx> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/sheet/NamedRangeFlag.hpp> +#include <com/sun/star/awt/XBitmap.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> + +#include <comphelper/servicehelper.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <unotools/charclass.hxx> + +#include <nameuno.hxx> +#include <miscuno.hxx> +#include <cellsuno.hxx> +#include <convuno.hxx> +#include <targuno.hxx> +#include <tokenuno.hxx> +#include <tokenarray.hxx> +#include <docsh.hxx> +#include <docfunc.hxx> +#include <rangenam.hxx> +#include <unonames.hxx> + +#include <scui_def.hxx> + +using namespace ::com::sun::star; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Any; + +static std::span<const SfxItemPropertyMapEntry> lcl_GetNamedRangeMap() +{ + static const SfxItemPropertyMapEntry aNamedRangeMap_Impl[] = + { + { SC_UNO_LINKDISPBIT, 0, cppu::UnoType<awt::XBitmap>::get(), beans::PropertyAttribute::READONLY, 0 }, + { SC_UNO_LINKDISPNAME, 0, cppu::UnoType<OUString>::get(), beans::PropertyAttribute::READONLY, 0 }, + { SC_UNONAME_TOKENINDEX, 0, cppu::UnoType<sal_Int32>::get(), beans::PropertyAttribute::READONLY, 0 }, + { SC_UNONAME_ISSHAREDFMLA, 0, cppu::UnoType<bool>::get(), 0, 0 }, + }; + return aNamedRangeMap_Impl; +} + +static std::span<const SfxItemPropertyMapEntry> lcl_GetNamedRangesMap() +{ + static const SfxItemPropertyMapEntry aNamedRangesMap_Impl[] = + { + { SC_UNO_MODIFY_BROADCAST, 0, cppu::UnoType<bool>::get(), 0, 0 }, + }; + return aNamedRangesMap_Impl; +} + +constexpr OUString SCNAMEDRANGEOBJ_SERVICE = u"com.sun.star.sheet.NamedRange"_ustr; + +SC_SIMPLE_SERVICE_INFO( ScLabelRangeObj, "ScLabelRangeObj", "com.sun.star.sheet.LabelRange" ) +SC_SIMPLE_SERVICE_INFO( ScLabelRangesObj, "ScLabelRangesObj", "com.sun.star.sheet.LabelRanges" ) +SC_SIMPLE_SERVICE_INFO( ScNamedRangesObj, "ScNamedRangesObj", "com.sun.star.sheet.NamedRanges" ) + +// Database named ranges are not considered by getCount, hasByName, removeByName and getElementNames +// Note that hidden named ranges are considered by these methods +static bool lcl_UserVisibleName(const ScRangeData& rData) +{ + //! as method to ScRangeData + return !rData.HasType(ScRangeData::Type::Database); +} + +ScNamedRangeObj::ScNamedRangeObj( rtl::Reference< ScNamedRangesObj > xParent, ScDocShell* pDocSh, OUString aNm, Reference<container::XNamed> const & xSheet): + mxParent(std::move(xParent)), + pDocShell( pDocSh ), + aName(std::move( aNm )), + mxSheet( xSheet ) +{ + pDocShell->GetDocument().AddUnoObject(*this); +} + +ScNamedRangeObj::~ScNamedRangeObj() +{ + SolarMutexGuard g; + + if (pDocShell) + pDocShell->GetDocument().RemoveUnoObject(*this); +} + +void ScNamedRangeObj::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + // reference update is of no interest + + if ( rHint.GetId() == SfxHintId::Dying ) + pDocShell = nullptr; // became invalid +} + +// Helper functions + +ScRangeData* ScNamedRangeObj::GetRangeData_Impl() +{ + ScRangeData* pRet = nullptr; + if (pDocShell) + { + ScRangeName* pNames; + SCTAB nTab = GetTab_Impl(); + if (nTab >= 0) + pNames = pDocShell->GetDocument().GetRangeName(nTab); + else + pNames = pDocShell->GetDocument().GetRangeName(); + if (pNames) + { + pRet = pNames->findByUpperName(ScGlobal::getCharClass().uppercase(aName)); + if (pRet) + pRet->ValidateTabRefs(); // adjust relative tab refs to valid tables + } + } + return pRet; +} + +SCTAB ScNamedRangeObj::GetTab_Impl() +{ + if (mxSheet.is()) + { + if (!pDocShell) + return -2; + ScDocument& rDoc = pDocShell->GetDocument(); + SCTAB nTab; + OUString sName = mxSheet->getName(); + bool bFound = rDoc.GetTable(sName, nTab); + assert(bFound); (void)bFound; // fouled up? + return nTab; + } + else + return -1;//global range name +} + +// sheet::XNamedRange + +void ScNamedRangeObj::Modify_Impl( const OUString* pNewName, const ScTokenArray* pNewTokens, const OUString* pNewContent, + const ScAddress* pNewPos, const ScRangeData::Type* pNewType, + const formula::FormulaGrammar::Grammar eGrammar ) +{ + if (!pDocShell) + return; + + ScDocument& rDoc = pDocShell->GetDocument(); + ScRangeName* pNames; + SCTAB nTab = GetTab_Impl(); + if (nTab >= 0) + pNames = rDoc.GetRangeName(nTab); + else + pNames = rDoc.GetRangeName(); + if (!pNames) + return; + + const ScRangeData* pOld = pNames->findByUpperName(ScGlobal::getCharClass().uppercase(aName)); + if (!pOld) + return; + + std::unique_ptr<ScRangeName> pNewRanges(new ScRangeName(*pNames)); + + OUString aInsName = pOld->GetName(); + if (pNewName) + aInsName = *pNewName; + + // Content string based => no problems with changed positions and such. + OUString aContent = pOld->GetSymbol(eGrammar); + if (pNewContent) + aContent = *pNewContent; + + ScAddress aPos = pOld->GetPos(); + if (pNewPos) + aPos = *pNewPos; + + ScRangeData::Type nType = pOld->GetType(); + if (pNewType) + nType = *pNewType; + + ScRangeData* pNew = nullptr; + if (pNewTokens) + pNew = new ScRangeData( rDoc, aInsName, *pNewTokens, aPos, nType ); + else + pNew = new ScRangeData( rDoc, aInsName, aContent, aPos, nType, eGrammar ); + + pNew->SetIndex( pOld->GetIndex() ); + + pNewRanges->erase(*pOld); + if (pNewRanges->insert(pNew)) + { + pDocShell->GetDocFunc().SetNewRangeNames(std::move(pNewRanges), mxParent->IsModifyAndBroadcast(), nTab); + + aName = aInsName; //! broadcast? + } + else + { + pNew = nullptr; //! uno::Exception/Error or something + } +} + +OUString SAL_CALL ScNamedRangeObj::getName() +{ + SolarMutexGuard aGuard; + return aName; +} + +void SAL_CALL ScNamedRangeObj::setName( const OUString& aNewName ) +{ + SolarMutexGuard aGuard; + //! adapt formulas ????? + + OUString aNewStr(aNewName); + // GRAM_API for API compatibility. + Modify_Impl( &aNewStr, nullptr, nullptr, nullptr, nullptr,formula::FormulaGrammar::GRAM_API ); + + if ( aName != aNewStr ) // some error occurred... + throw uno::RuntimeException(); // no other exceptions specified +} + +OUString SAL_CALL ScNamedRangeObj::getContent() +{ + SolarMutexGuard aGuard; + OUString aContent; + ScRangeData* pData = GetRangeData_Impl(); + if (pData) + // GRAM_API for API compatibility. + aContent = pData->GetSymbol(formula::FormulaGrammar::GRAM_API); + return aContent; +} + +void SAL_CALL ScNamedRangeObj::setContent( const OUString& aContent ) +{ + SolarMutexGuard aGuard; + OUString aContStr(aContent); + // GRAM_API for API compatibility. + Modify_Impl( nullptr, nullptr, &aContStr, nullptr, nullptr,formula::FormulaGrammar::GRAM_API ); +} + +table::CellAddress SAL_CALL ScNamedRangeObj::getReferencePosition() +{ + SolarMutexGuard aGuard; + ScAddress aPos; + ScRangeData* pData = GetRangeData_Impl(); + if (pData) + aPos = pData->GetPos(); + table::CellAddress aAddress; + aAddress.Column = aPos.Col(); + aAddress.Row = aPos.Row(); + aAddress.Sheet = aPos.Tab(); + if (pDocShell) + { + SCTAB nDocTabs = pDocShell->GetDocument().GetTableCount(); + if ( aAddress.Sheet >= nDocTabs && nDocTabs > 0 ) + { + // Even after ValidateTabRefs, the position can be invalid if + // the content points to preceding tables. The resulting string + // is invalid in any case, so the position is just shifted. + aAddress.Sheet = nDocTabs - 1; + } + } + return aAddress; +} + +void SAL_CALL ScNamedRangeObj::setReferencePosition( const table::CellAddress& aReferencePosition ) +{ + SolarMutexGuard aGuard; + ScAddress aPos( static_cast<SCCOL>(aReferencePosition.Column), static_cast<SCROW>(aReferencePosition.Row), aReferencePosition.Sheet ); + // GRAM_API for API compatibility. + Modify_Impl( nullptr, nullptr, nullptr, &aPos, nullptr,formula::FormulaGrammar::GRAM_API ); +} + +sal_Int32 SAL_CALL ScNamedRangeObj::getType() +{ + SolarMutexGuard aGuard; + sal_Int32 nType=0; + ScRangeData* pData = GetRangeData_Impl(); + if (pData) + { + // do not return internal ScRangeData::Type flags + if ( pData->HasType(ScRangeData::Type::Criteria) ) nType |= sheet::NamedRangeFlag::FILTER_CRITERIA; + if ( pData->HasType(ScRangeData::Type::PrintArea) ) nType |= sheet::NamedRangeFlag::PRINT_AREA; + if ( pData->HasType(ScRangeData::Type::ColHeader) ) nType |= sheet::NamedRangeFlag::COLUMN_HEADER; + if ( pData->HasType(ScRangeData::Type::RowHeader) ) nType |= sheet::NamedRangeFlag::ROW_HEADER; + if ( pData->HasType(ScRangeData::Type::Hidden) ) nType |= sheet::NamedRangeFlag::HIDDEN; + } + return nType; +} + +void SAL_CALL ScNamedRangeObj::setType( sal_Int32 nUnoType ) +{ + SolarMutexGuard aGuard; + ScRangeData::Type nNewType = ScRangeData::Type::Name; + if ( nUnoType & sheet::NamedRangeFlag::FILTER_CRITERIA ) nNewType |= ScRangeData::Type::Criteria; + if ( nUnoType & sheet::NamedRangeFlag::PRINT_AREA ) nNewType |= ScRangeData::Type::PrintArea; + if ( nUnoType & sheet::NamedRangeFlag::COLUMN_HEADER ) nNewType |= ScRangeData::Type::ColHeader; + if ( nUnoType & sheet::NamedRangeFlag::ROW_HEADER ) nNewType |= ScRangeData::Type::RowHeader; + if ( nUnoType & sheet::NamedRangeFlag::HIDDEN ) nNewType |= ScRangeData::Type::Hidden; + + // GRAM_API for API compatibility. + Modify_Impl( nullptr, nullptr, nullptr, nullptr, &nNewType,formula::FormulaGrammar::GRAM_API ); +} + +// XFormulaTokens + +uno::Sequence<sheet::FormulaToken> SAL_CALL ScNamedRangeObj::getTokens() +{ + SolarMutexGuard aGuard; + uno::Sequence<sheet::FormulaToken> aSequence; + ScRangeData* pData = GetRangeData_Impl(); + if (pData && pDocShell) + { + ScTokenArray* pTokenArray = pData->GetCode(); + if ( pTokenArray ) + ScTokenConversion::ConvertToTokenSequence( pDocShell->GetDocument(), aSequence, *pTokenArray ); + } + return aSequence; +} + +void SAL_CALL ScNamedRangeObj::setTokens( const uno::Sequence<sheet::FormulaToken>& rTokens ) +{ + SolarMutexGuard aGuard; + if( pDocShell ) + { + ScTokenArray aTokenArray(pDocShell->GetDocument()); + (void)ScTokenConversion::ConvertToTokenArray( pDocShell->GetDocument(), aTokenArray, rTokens ); + // GRAM_API for API compatibility. + Modify_Impl( nullptr, &aTokenArray, nullptr, nullptr, nullptr, formula::FormulaGrammar::GRAM_API ); + } +} + +// XCellRangeSource + +uno::Reference<table::XCellRange> SAL_CALL ScNamedRangeObj::getReferredCells() +{ + SolarMutexGuard aGuard; + ScRange aRange; + ScRangeData* pData = GetRangeData_Impl(); + if ( pData && pData->IsValidReference( aRange ) ) + { + //! static function to create ScCellObj/ScCellRangeObj at ScCellRangeObj ??? + + if ( aRange.aStart == aRange.aEnd ) + return new ScCellObj( pDocShell, aRange.aStart ); + else + return new ScCellRangeObj( pDocShell, aRange ); + } + return nullptr; +} + +// beans::XPropertySet + +uno::Reference<beans::XPropertySetInfo> SAL_CALL ScNamedRangeObj::getPropertySetInfo() +{ + static uno::Reference< beans::XPropertySetInfo > aRef(new SfxItemPropertySetInfo( lcl_GetNamedRangeMap() )); + return aRef; +} + +void SAL_CALL ScNamedRangeObj::setPropertyValue( + const OUString& rPropertyName, const uno::Any& /*aValue*/ ) +{ + if ( rPropertyName == SC_UNONAME_ISSHAREDFMLA ) + { + // Ignore this. + } +} + +uno::Any SAL_CALL ScNamedRangeObj::getPropertyValue( const OUString& rPropertyName ) +{ + SolarMutexGuard aGuard; + uno::Any aRet; + if ( rPropertyName == SC_UNO_LINKDISPBIT ) + { + // no target bitmaps for individual entries (would be all equal) + // ScLinkTargetTypeObj::SetLinkTargetBitmap( aRet, SC_LINKTARGETTYPE_RANGENAME ); + } + else if ( rPropertyName == SC_UNO_LINKDISPNAME ) + aRet <<= aName; + else if ( rPropertyName == SC_UNONAME_TOKENINDEX ) + { + // get index for use in formula tokens (read-only) + ScRangeData* pData = GetRangeData_Impl(); + if (pData) + aRet <<= static_cast<sal_Int32>(pData->GetIndex()); + } + else if ( rPropertyName == SC_UNONAME_ISSHAREDFMLA ) + { + if (GetRangeData_Impl()) + aRet <<= false; + } + return aRet; +} + +SC_IMPL_DUMMY_PROPERTY_LISTENER( ScNamedRangeObj ) + +// lang::XServiceInfo + +OUString SAL_CALL ScNamedRangeObj::getImplementationName() +{ + return "ScNamedRangeObj"; +} + +sal_Bool SAL_CALL ScNamedRangeObj::supportsService( const OUString& rServiceName ) +{ + return cppu::supportsService(this, rServiceName); +} + +uno::Sequence<OUString> SAL_CALL ScNamedRangeObj::getSupportedServiceNames() +{ + return {SCNAMEDRANGEOBJ_SERVICE, SCLINKTARGET_SERVICE}; +} + +ScNamedRangesObj::ScNamedRangesObj(ScDocShell* pDocSh) : + mbModifyAndBroadcast(true), + pDocShell( pDocSh ) +{ + pDocShell->GetDocument().AddUnoObject(*this); +} + +ScNamedRangesObj::~ScNamedRangesObj() +{ + SolarMutexGuard g; + + if (pDocShell) + pDocShell->GetDocument().RemoveUnoObject(*this); +} + +void ScNamedRangesObj::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + // reference update is of no interest + + if ( rHint.GetId() == SfxHintId::Dying ) + { + pDocShell = nullptr; // became invalid + } +} + +// sheet::XNamedRanges + +void SAL_CALL ScNamedRangesObj::addNewByName( const OUString& aName, + const OUString& aContent, const table::CellAddress& aPosition, + sal_Int32 nUnoType ) +{ + SolarMutexGuard aGuard; + ScAddress aPos( static_cast<SCCOL>(aPosition.Column), static_cast<SCROW>(aPosition.Row), aPosition.Sheet ); + + ScRangeData::Type nNewType = ScRangeData::Type::Name; + if ( nUnoType & sheet::NamedRangeFlag::FILTER_CRITERIA ) nNewType |= ScRangeData::Type::Criteria; + if ( nUnoType & sheet::NamedRangeFlag::PRINT_AREA ) nNewType |= ScRangeData::Type::PrintArea; + if ( nUnoType & sheet::NamedRangeFlag::COLUMN_HEADER ) nNewType |= ScRangeData::Type::ColHeader; + if ( nUnoType & sheet::NamedRangeFlag::ROW_HEADER ) nNewType |= ScRangeData::Type::RowHeader; + if ( nUnoType & sheet::NamedRangeFlag::HIDDEN ) nNewType |= ScRangeData::Type::Hidden; + + bool bDone = false; + if (pDocShell) + { + ScDocument& rDoc = pDocShell->GetDocument(); + // tdf#119457 - check for a valid range name and cell reference + switch (ScRangeData::IsNameValid(aName, rDoc)) + { + case ScRangeData::IsNameValidType::NAME_INVALID_CELL_REF: + throw uno::RuntimeException( + "Invalid name. Reference to a cell, or a range of cells not allowed", + getXWeak()); + break; + case ScRangeData::IsNameValidType::NAME_INVALID_BAD_STRING: + throw uno::RuntimeException( + "Invalid name. Start with a letter, use only letters, numbers and underscore", + getXWeak()); + break; + case ScRangeData::IsNameValidType::NAME_VALID: + if (ScRangeName* pNames = GetRangeName_Impl(); + pNames + && !pNames->findByUpperName(ScGlobal::getCharClass().uppercase(aName))) + { + std::unique_ptr<ScRangeName> pNewRanges(new ScRangeName( *pNames )); + // GRAM_API for API compatibility. + ScRangeData* pNew = new ScRangeData( rDoc, aName, aContent, + aPos, nNewType,formula::FormulaGrammar::GRAM_API ); + if ( pNewRanges->insert(pNew) ) + { + pDocShell->GetDocFunc().SetNewRangeNames(std::move(pNewRanges), mbModifyAndBroadcast, GetTab_Impl()); + bDone = true; + } + else + { + pNew = nullptr; + } + } + } + } + + if (!bDone) + throw uno::RuntimeException(); // no other exceptions specified +} + +void SAL_CALL ScNamedRangesObj::addNewFromTitles( const table::CellRangeAddress& aSource, + sheet::Border aBorder ) +{ + SolarMutexGuard aGuard; + //! this cannot be an enum, because multiple bits can be set !!! + + bool bTop = ( aBorder == sheet::Border_TOP ); + bool bLeft = ( aBorder == sheet::Border_LEFT ); + bool bBottom = ( aBorder == sheet::Border_BOTTOM ); + bool bRight = ( aBorder == sheet::Border_RIGHT ); + + ScRange aRange; + ScUnoConversion::FillScRange( aRange, aSource ); + + CreateNameFlags nFlags = CreateNameFlags::NONE; + if (bTop) nFlags |= CreateNameFlags::Top; + if (bLeft) nFlags |= CreateNameFlags::Left; + if (bBottom) nFlags |= CreateNameFlags::Bottom; + if (bRight) nFlags |= CreateNameFlags::Right; + + if (nFlags != CreateNameFlags::NONE) + pDocShell->GetDocFunc().CreateNames( aRange, nFlags, true, GetTab_Impl() ); +} + +void SAL_CALL ScNamedRangesObj::removeByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + bool bDone = false; + if (pDocShell) + { + ScRangeName* pNames = GetRangeName_Impl(); + if (pNames) + { + const ScRangeData* pData = pNames->findByUpperName(ScGlobal::getCharClass().uppercase(aName)); + if (pData && lcl_UserVisibleName(*pData)) + { + std::unique_ptr<ScRangeName> pNewRanges(new ScRangeName(*pNames)); + pNewRanges->erase(*pData); + pDocShell->GetDocFunc().SetNewRangeNames( std::move(pNewRanges), mbModifyAndBroadcast, GetTab_Impl()); + bDone = true; + } + } + } + + if (!bDone) + throw uno::RuntimeException(); // no other exceptions specified +} + +void SAL_CALL ScNamedRangesObj::outputList( const table::CellAddress& aOutputPosition ) +{ + SolarMutexGuard aGuard; + ScAddress aPos( static_cast<SCCOL>(aOutputPosition.Column), static_cast<SCROW>(aOutputPosition.Row), aOutputPosition.Sheet ); + if (pDocShell) + pDocShell->GetDocFunc().InsertNameList( aPos, true ); +} + +// container::XEnumerationAccess + +uno::Reference<container::XEnumeration> SAL_CALL ScNamedRangesObj::createEnumeration() +{ + SolarMutexGuard aGuard; + return new ScIndexEnumeration(this, "com.sun.star.sheet.NamedRangesEnumeration"); +} + +// container::XIndexAccess + +sal_Int32 SAL_CALL ScNamedRangesObj::getCount() +{ + SolarMutexGuard aGuard; + tools::Long nRet = 0; + if (pDocShell) + { + ScRangeName* pNames = GetRangeName_Impl(); + if (pNames) + { + for (const auto& rName : *pNames) + if (lcl_UserVisibleName(*rName.second)) + ++nRet; + } + } + return nRet; +} + +uno::Any SAL_CALL ScNamedRangesObj::getByIndex( sal_Int32 nIndex ) +{ + SolarMutexGuard aGuard; + uno::Reference< sheet::XNamedRange > xRange(GetObjectByIndex_Impl(static_cast<sal_uInt16>(nIndex))); + if ( !xRange.is() ) + throw lang::IndexOutOfBoundsException(); + + return uno::Any(xRange); +} + +uno::Type SAL_CALL ScNamedRangesObj::getElementType() +{ + return cppu::UnoType<sheet::XNamedRange>::get(); // must be suitable for getByIndex +} + +sal_Bool SAL_CALL ScNamedRangesObj::hasElements() +{ + SolarMutexGuard aGuard; + return ( getCount() != 0 ); +} + +Reference<beans::XPropertySetInfo> SAL_CALL ScNamedRangesObj::getPropertySetInfo() +{ + static Reference<beans::XPropertySetInfo> aRef( + new SfxItemPropertySetInfo(lcl_GetNamedRangesMap())); + return aRef; +} + +void SAL_CALL ScNamedRangesObj::setPropertyValue( + const OUString& rPropertyName, const uno::Any& aValue ) +{ + if ( rPropertyName == SC_UNO_MODIFY_BROADCAST ) + { + aValue >>= mbModifyAndBroadcast; + } +} + +Any SAL_CALL ScNamedRangesObj::getPropertyValue( const OUString& rPropertyName ) +{ + Any aRet; + if ( rPropertyName == SC_UNO_MODIFY_BROADCAST ) + { + aRet <<= mbModifyAndBroadcast; + } + + return aRet; +} + +SC_IMPL_DUMMY_PROPERTY_LISTENER( ScNamedRangesObj ) + +uno::Any SAL_CALL ScNamedRangesObj::getByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + uno::Reference< sheet::XNamedRange > xRange(GetObjectByName_Impl(aName)); + if ( !xRange.is() ) + throw container::NoSuchElementException(); + + return uno::Any(xRange); +} + +uno::Sequence<OUString> SAL_CALL ScNamedRangesObj::getElementNames() +{ + SolarMutexGuard aGuard; + if (pDocShell) + { + ScRangeName* pNames = GetRangeName_Impl(); + if (pNames) + { + tools::Long nVisCount = getCount(); // names with lcl_UserVisibleName + uno::Sequence<OUString> aSeq(nVisCount); + OUString* pAry = aSeq.getArray(); + sal_uInt16 nVisPos = 0; + for (const auto& rName : *pNames) + { + if (lcl_UserVisibleName(*rName.second)) + pAry[nVisPos++] = rName.second->GetName(); + } + return aSeq; + } + } + return {}; +} + +sal_Bool SAL_CALL ScNamedRangesObj::hasByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + if (pDocShell) + { + ScRangeName* pNames = GetRangeName_Impl(); + if (pNames) + { + const ScRangeData* pData = pNames->findByUpperName(ScGlobal::getCharClass().uppercase(aName)); + if (pData && lcl_UserVisibleName(*pData)) + return true; + } + } + return false; +} + +/** called from the XActionLockable interface methods on initial locking */ +void ScNamedRangesObj::lock() +{ + pDocShell->GetDocument().PreprocessRangeNameUpdate(); +} + +/** called from the XActionLockable interface methods on final unlock */ +void ScNamedRangesObj::unlock() +{ + pDocShell->GetDocument().CompileHybridFormula(); +} + +// document::XActionLockable + +sal_Bool ScNamedRangesObj::isActionLocked() +{ + SolarMutexGuard aGuard; + return pDocShell->GetDocument().GetNamedRangesLockCount() != 0; +} + +void ScNamedRangesObj::addActionLock() +{ + SolarMutexGuard aGuard; + ScDocument& rDoc = pDocShell->GetDocument(); + sal_Int16 nLockCount = rDoc.GetNamedRangesLockCount(); + ++nLockCount; + if ( nLockCount == 1 ) + { + lock(); + } + rDoc.SetNamedRangesLockCount( nLockCount ); +} + +void ScNamedRangesObj::removeActionLock() +{ + SolarMutexGuard aGuard; + ScDocument& rDoc = pDocShell->GetDocument(); + sal_Int16 nLockCount = rDoc.GetNamedRangesLockCount(); + if ( nLockCount > 0 ) + { + --nLockCount; + if ( nLockCount == 0 ) + { + unlock(); + } + rDoc.SetNamedRangesLockCount( nLockCount ); + } +} + +void ScNamedRangesObj::setActionLocks( sal_Int16 nLock ) +{ + SolarMutexGuard aGuard; + if ( nLock < 0 ) + return; + + ScDocument& rDoc = pDocShell->GetDocument(); + sal_Int16 nLockCount = rDoc.GetNamedRangesLockCount(); + if ( nLock == 0 && nLockCount > 0 ) + { + unlock(); + } + if ( nLock > 0 && nLockCount == 0 ) + { + lock(); + } + rDoc.SetNamedRangesLockCount( nLock ); +} + +sal_Int16 ScNamedRangesObj::resetActionLocks() +{ + SolarMutexGuard aGuard; + ScDocument& rDoc = pDocShell->GetDocument(); + sal_Int16 nLockCount = rDoc.GetNamedRangesLockCount(); + if ( nLockCount > 0 ) + { + unlock(); + } + rDoc.SetNamedRangesLockCount( 0 ); + return nLockCount; +} + +ScGlobalNamedRangesObj::ScGlobalNamedRangesObj(ScDocShell* pDocSh) + : ScNamedRangesObj(pDocSh) +{ + +} + +ScGlobalNamedRangesObj::~ScGlobalNamedRangesObj() +{ + +} + +rtl::Reference<ScNamedRangeObj> ScGlobalNamedRangesObj::GetObjectByIndex_Impl(sal_uInt16 nIndex) +{ + if (!pDocShell) + return nullptr; + + ScRangeName* pNames = pDocShell->GetDocument().GetRangeName(); + if (!pNames) + return nullptr; + + sal_uInt16 nPos = 0; + for (const auto& rName : *pNames) + { + if (lcl_UserVisibleName(*rName.second)) + { + if (nPos == nIndex) + return new ScNamedRangeObj(this, pDocShell, rName.second->GetName()); + } + ++nPos; + } + return nullptr; +} + +rtl::Reference<ScNamedRangeObj> ScGlobalNamedRangesObj::GetObjectByName_Impl(const OUString& aName) +{ + if ( pDocShell && hasByName(aName) ) + return new ScNamedRangeObj(this, pDocShell, aName); + return nullptr; +} + +ScRangeName* ScGlobalNamedRangesObj::GetRangeName_Impl() +{ + return pDocShell->GetDocument().GetRangeName(); +} + +SCTAB ScGlobalNamedRangesObj::GetTab_Impl() +{ + return -1; +} + +ScLocalNamedRangesObj::ScLocalNamedRangesObj( ScDocShell* pDocSh, uno::Reference<container::XNamed> xSheet ) + : ScNamedRangesObj(pDocSh), + mxSheet(std::move(xSheet)) +{ + +} + +ScLocalNamedRangesObj::~ScLocalNamedRangesObj() +{ + +} + +rtl::Reference<ScNamedRangeObj> ScLocalNamedRangesObj::GetObjectByName_Impl(const OUString& aName) +{ + if ( pDocShell && hasByName( aName ) ) + return new ScNamedRangeObj( this, pDocShell, aName, mxSheet); + return nullptr; + +} + +rtl::Reference<ScNamedRangeObj> ScLocalNamedRangesObj::GetObjectByIndex_Impl( sal_uInt16 nIndex ) +{ + if (!pDocShell) + return nullptr; + + OUString aName = mxSheet->getName(); + ScDocument& rDoc = pDocShell->GetDocument(); + SCTAB nTab; + if (!rDoc.GetTable(aName, nTab)) + return nullptr; + + ScRangeName* pNames = rDoc.GetRangeName( nTab ); + if (!pNames) + return nullptr; + + sal_uInt16 nPos = 0; + for (const auto& rName : *pNames) + { + if (lcl_UserVisibleName(*rName.second)) + { + if (nPos == nIndex) + return new ScNamedRangeObj(this, pDocShell, rName.second->GetName(), mxSheet); + } + ++nPos; + } + return nullptr; +} + +ScRangeName* ScLocalNamedRangesObj::GetRangeName_Impl() +{ + SCTAB nTab = GetTab_Impl(); + return pDocShell->GetDocument().GetRangeName( nTab ); +} + +SCTAB ScLocalNamedRangesObj::GetTab_Impl() +{ + SCTAB nTab; + (void)pDocShell->GetDocument().GetTable(mxSheet->getName(), nTab); + return nTab; +} + +ScLabelRangeObj::ScLabelRangeObj(ScDocShell* pDocSh, bool bCol, const ScRange& rR) : + pDocShell( pDocSh ), + bColumn( bCol ), + aRange( rR ) +{ + pDocShell->GetDocument().AddUnoObject(*this); +} + +ScLabelRangeObj::~ScLabelRangeObj() +{ + SolarMutexGuard g; + + if (pDocShell) + pDocShell->GetDocument().RemoveUnoObject(*this); +} + +void ScLabelRangeObj::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + //! Ref-Update !!! + + if ( rHint.GetId() == SfxHintId::Dying ) + pDocShell = nullptr; // became invalid +} + +// Helper functions + +ScRangePair* ScLabelRangeObj::GetData_Impl() +{ + ScRangePair* pRet = nullptr; + if (pDocShell) + { + ScDocument& rDoc = pDocShell->GetDocument(); + ScRangePairList* pList = bColumn ? rDoc.GetColNameRanges() : rDoc.GetRowNameRanges(); + if (pList) + pRet = pList->Find( aRange ); + } + return pRet; +} + +void ScLabelRangeObj::Modify_Impl( const ScRange* pLabel, const ScRange* pData ) +{ + if (!pDocShell) + return; + + ScDocument& rDoc = pDocShell->GetDocument(); + ScRangePairList* pOldList = bColumn ? rDoc.GetColNameRanges() : rDoc.GetRowNameRanges(); + if (!pOldList) + return; + + ScRangePairListRef xNewList(pOldList->Clone()); + ScRangePair* pEntry = xNewList->Find( aRange ); + if (!pEntry) + return; + + if ( pLabel ) + pEntry->GetRange(0) = *pLabel; + if ( pData ) + pEntry->GetRange(1) = *pData; + + xNewList->Join( *pEntry, true ); + + if (bColumn) + rDoc.GetColNameRangesRef() = xNewList; + else + rDoc.GetRowNameRangesRef() = xNewList; + + rDoc.CompileColRowNameFormula(); + pDocShell->PostPaint( 0,0,0, rDoc.MaxCol(),rDoc.MaxRow(),MAXTAB, PaintPartFlags::Grid ); + pDocShell->SetDocumentModified(); + + //! Undo ?!?! (here and from dialog) + + if ( pLabel ) + aRange = *pLabel; // adapt object to find range again +} + +// sheet::XLabelRange + +table::CellRangeAddress SAL_CALL ScLabelRangeObj::getLabelArea() +{ + SolarMutexGuard aGuard; + table::CellRangeAddress aRet; + ScRangePair* pData = GetData_Impl(); + if (pData) + ScUnoConversion::FillApiRange( aRet, pData->GetRange(0) ); + return aRet; +} + +void SAL_CALL ScLabelRangeObj::setLabelArea( const table::CellRangeAddress& aLabelArea ) +{ + SolarMutexGuard aGuard; + ScRange aLabelRange; + ScUnoConversion::FillScRange( aLabelRange, aLabelArea ); + Modify_Impl( &aLabelRange, nullptr ); +} + +table::CellRangeAddress SAL_CALL ScLabelRangeObj::getDataArea() +{ + SolarMutexGuard aGuard; + table::CellRangeAddress aRet; + ScRangePair* pData = GetData_Impl(); + if (pData) + ScUnoConversion::FillApiRange( aRet, pData->GetRange(1) ); + return aRet; +} + +void SAL_CALL ScLabelRangeObj::setDataArea( const table::CellRangeAddress& aDataArea ) +{ + SolarMutexGuard aGuard; + ScRange aDataRange; + ScUnoConversion::FillScRange( aDataRange, aDataArea ); + Modify_Impl( nullptr, &aDataRange ); +} + +ScLabelRangesObj::ScLabelRangesObj(ScDocShell* pDocSh, bool bCol) : + pDocShell( pDocSh ), + bColumn( bCol ) +{ + pDocShell->GetDocument().AddUnoObject(*this); +} + +ScLabelRangesObj::~ScLabelRangesObj() +{ + SolarMutexGuard g; + + if (pDocShell) + pDocShell->GetDocument().RemoveUnoObject(*this); +} + +void ScLabelRangesObj::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + // reference update is of no interest + + if ( rHint.GetId() == SfxHintId::Dying ) + { + pDocShell = nullptr; // became invalid + } +} + +// sheet::XLabelRanges + +rtl::Reference<ScLabelRangeObj> ScLabelRangesObj::GetObjectByIndex_Impl(size_t nIndex) +{ + if (pDocShell) + { + ScDocument& rDoc = pDocShell->GetDocument(); + ScRangePairList* pList = bColumn ? rDoc.GetColNameRanges() : rDoc.GetRowNameRanges(); + if ( pList && nIndex < pList->size() ) + { + ScRangePair & rData = (*pList)[nIndex]; + return new ScLabelRangeObj( pDocShell, bColumn, rData.GetRange(0) ); + } + } + return nullptr; +} + +void SAL_CALL ScLabelRangesObj::addNew( const table::CellRangeAddress& aLabelArea, + const table::CellRangeAddress& aDataArea ) +{ + SolarMutexGuard aGuard; + if (!pDocShell) + return; + + ScDocument& rDoc = pDocShell->GetDocument(); + ScRangePairList* pOldList = bColumn ? rDoc.GetColNameRanges() : rDoc.GetRowNameRanges(); + if (!pOldList) + return; + + ScRangePairListRef xNewList(pOldList->Clone()); + + ScRange aLabelRange; + ScRange aDataRange; + ScUnoConversion::FillScRange( aLabelRange, aLabelArea ); + ScUnoConversion::FillScRange( aDataRange, aDataArea ); + xNewList->Join( ScRangePair( aLabelRange, aDataRange ) ); + + if (bColumn) + rDoc.GetColNameRangesRef() = xNewList; + else + rDoc.GetRowNameRangesRef() = xNewList; + + rDoc.CompileColRowNameFormula(); + pDocShell->PostPaint( 0,0,0, rDoc.MaxCol(),rDoc.MaxRow(),MAXTAB, PaintPartFlags::Grid ); + pDocShell->SetDocumentModified(); + + //! Undo ?!?! (here and from dialog) +} + +void SAL_CALL ScLabelRangesObj::removeByIndex( sal_Int32 nIndex ) +{ + SolarMutexGuard aGuard; + bool bDone = false; + if (pDocShell) + { + ScDocument& rDoc = pDocShell->GetDocument(); + ScRangePairList* pOldList = bColumn ? rDoc.GetColNameRanges() : rDoc.GetRowNameRanges(); + + if ( pOldList && nIndex >= 0 && o3tl::make_unsigned(nIndex) < pOldList->size() ) + { + ScRangePairListRef xNewList(pOldList->Clone()); + + xNewList->Remove( nIndex ); + + if (bColumn) + rDoc.GetColNameRangesRef() = xNewList; + else + rDoc.GetRowNameRangesRef() = xNewList; + + rDoc.CompileColRowNameFormula(); + pDocShell->PostPaint( 0,0,0, rDoc.MaxCol(),rDoc.MaxRow(),MAXTAB, PaintPartFlags::Grid ); + pDocShell->SetDocumentModified(); + bDone = true; + + //! Undo ?!?! (here and from dialog) + } + } + if (!bDone) + throw uno::RuntimeException(); // no other exceptions specified +} + +// container::XEnumerationAccess + +uno::Reference<container::XEnumeration> SAL_CALL ScLabelRangesObj::createEnumeration() +{ + SolarMutexGuard aGuard; + return new ScIndexEnumeration(this, "com.sun.star.sheet.LabelRangesEnumeration"); +} + +// container::XIndexAccess + +sal_Int32 SAL_CALL ScLabelRangesObj::getCount() +{ + SolarMutexGuard aGuard; + if (pDocShell) + { + ScDocument& rDoc = pDocShell->GetDocument(); + ScRangePairList* pList = bColumn ? rDoc.GetColNameRanges() : rDoc.GetRowNameRanges(); + if (pList) + return pList->size(); + } + return 0; +} + +uno::Any SAL_CALL ScLabelRangesObj::getByIndex( sal_Int32 nIndex ) +{ + SolarMutexGuard aGuard; + uno::Reference< sheet::XLabelRange > xRange(GetObjectByIndex_Impl(static_cast<sal_uInt16>(nIndex))); + if ( !xRange.is() ) + throw lang::IndexOutOfBoundsException(); + + return uno::Any(xRange); +} + +uno::Type SAL_CALL ScLabelRangesObj::getElementType() +{ + return cppu::UnoType<sheet::XLabelRange>::get(); // must be suitable for getByIndex +} + +sal_Bool SAL_CALL ScLabelRangesObj::hasElements() +{ + SolarMutexGuard aGuard; + return ( getCount() != 0 ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/unoobj/notesuno.cxx b/sc/source/ui/unoobj/notesuno.cxx new file mode 100644 index 0000000000..4e99431dd4 --- /dev/null +++ b/sc/source/ui/unoobj/notesuno.cxx @@ -0,0 +1,231 @@ +/* -*- 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 <notesuno.hxx> + +#include <vcl/svapp.hxx> +#include <svl/hint.hxx> +#include <editeng/unoipset.hxx> +#include <editeng/unotext.hxx> +#include <editeng/unoprnms.hxx> +#include <svx/svdpool.hxx> +#include <svx/svdobj.hxx> +#include <svx/svdocapt.hxx> + +#include <postit.hxx> +#include <cellsuno.hxx> +#include <docsh.hxx> +#include <docfunc.hxx> +#include <editsrc.hxx> +#include <miscuno.hxx> + +using namespace com::sun::star; + +static const SvxItemPropertySet* lcl_GetAnnotationPropertySet() +{ + static const SfxItemPropertyMapEntry aAnnotationPropertyMap_Impl[] = + { + SVX_UNOEDIT_CHAR_PROPERTIES, + SVX_UNOEDIT_FONT_PROPERTIES, + SVX_UNOEDIT_PARA_PROPERTIES, + SVX_UNOEDIT_NUMBERING_PROPERTY, // for completeness of service ParagraphProperties + }; + static SvxItemPropertySet aAnnotationPropertySet_Impl( aAnnotationPropertyMap_Impl, SdrObject::GetGlobalDrawObjectItemPool() ); + return &aAnnotationPropertySet_Impl; +} + +SC_SIMPLE_SERVICE_INFO( ScAnnotationObj, "ScAnnotationObj", "com.sun.star.sheet.CellAnnotation" ) +//SC_SIMPLE_SERVICE_INFO( ScAnnotationShapeObj, "ScAnnotationShapeObj", "com.sun.star.sheet.CellAnnotationShape" ) + +ScAnnotationObj::ScAnnotationObj(ScDocShell* pDocSh, const ScAddress& rPos) : + pDocShell( pDocSh ), + aCellPos( rPos ) +{ + pDocShell->GetDocument().AddUnoObject(*this); + + // pUnoText is allocated on demand (GetUnoText) + // can't be aggregated because getString/setString is handled here +} + +ScAnnotationObj::~ScAnnotationObj() +{ + SolarMutexGuard g; + + if (pDocShell) + pDocShell->GetDocument().RemoveUnoObject(*this); +} + +void ScAnnotationObj::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + if ( rHint.GetId() == SfxHintId::Dying ) + { + pDocShell = nullptr; // became invalid + } +} + +// XChild + +uno::Reference<uno::XInterface> SAL_CALL ScAnnotationObj::getParent() +{ + SolarMutexGuard aGuard; + + // parent of note is the related cell + //! find and reset existing object ??? + + if (pDocShell) + return cppu::getXWeak(new ScCellObj( pDocShell, aCellPos )); + + return nullptr; +} + +void SAL_CALL ScAnnotationObj::setParent( const uno::Reference<uno::XInterface>& /* Parent */ ) +{ + // ain't there + //! exception or what ??! +} + +// XSimpleText + +uno::Reference<text::XTextCursor> SAL_CALL ScAnnotationObj::createTextCursor() +{ + SolarMutexGuard aGuard; + // notes does not need special treatment + return GetUnoText().createTextCursor(); +} + +uno::Reference<text::XTextCursor> SAL_CALL ScAnnotationObj::createTextCursorByRange( + const uno::Reference<text::XTextRange>& aTextPosition ) +{ + SolarMutexGuard aGuard; + // notes does not need special treatment + return GetUnoText().createTextCursorByRange(aTextPosition); +} + +OUString SAL_CALL ScAnnotationObj::getString() +{ + SolarMutexGuard aGuard; + return GetUnoText().getString(); +} + +void SAL_CALL ScAnnotationObj::setString( const OUString& aText ) +{ + SolarMutexGuard aGuard; + GetUnoText().setString(aText); +} + +void SAL_CALL ScAnnotationObj::insertString( const uno::Reference<text::XTextRange>& xRange, + const OUString& aString, sal_Bool bAbsorb ) +{ + SolarMutexGuard aGuard; + GetUnoText().insertString( xRange, aString, bAbsorb ); +} + +void SAL_CALL ScAnnotationObj::insertControlCharacter( const uno::Reference<text::XTextRange>& xRange, + sal_Int16 nControlCharacter, sal_Bool bAbsorb ) +{ + SolarMutexGuard aGuard; + GetUnoText().insertControlCharacter( xRange, nControlCharacter, bAbsorb ); +} + +uno::Reference<text::XText> SAL_CALL ScAnnotationObj::getText() +{ + SolarMutexGuard aGuard; + return GetUnoText().getText(); +} + +uno::Reference<text::XTextRange> SAL_CALL ScAnnotationObj::getStart() +{ + SolarMutexGuard aGuard; + return GetUnoText().getStart(); +} + +uno::Reference<text::XTextRange> SAL_CALL ScAnnotationObj::getEnd() +{ + SolarMutexGuard aGuard; + return GetUnoText().getEnd(); +} + +// XSheetAnnotation + +table::CellAddress SAL_CALL ScAnnotationObj::getPosition() +{ + SolarMutexGuard aGuard; + table::CellAddress aAdr; + aAdr.Sheet = aCellPos.Tab(); + aAdr.Column = aCellPos.Col(); + aAdr.Row = aCellPos.Row(); + return aAdr; +} + +OUString SAL_CALL ScAnnotationObj::getAuthor() +{ + SolarMutexGuard aGuard; + const ScPostIt* pNote = ImplGetNote(); + return pNote ? pNote->GetAuthor() : OUString(); +} + +OUString SAL_CALL ScAnnotationObj::getDate() +{ + SolarMutexGuard aGuard; + const ScPostIt* pNote = ImplGetNote(); + return pNote ? pNote->GetDate() : OUString(); +} + +sal_Bool SAL_CALL ScAnnotationObj::getIsVisible() +{ + SolarMutexGuard aGuard; + const ScPostIt* pNote = ImplGetNote(); + return pNote && pNote->IsCaptionShown(); +} + +void SAL_CALL ScAnnotationObj::setIsVisible( sal_Bool bIsVisible ) +{ + SolarMutexGuard aGuard; + // show/hide note with undo action + if( pDocShell ) + pDocShell->GetDocFunc().ShowNote( aCellPos, bIsVisible ); +} + +// XSheetAnnotationShapeSupplier +uno::Reference < drawing::XShape > SAL_CALL ScAnnotationObj::getAnnotationShape() +{ + SolarMutexGuard aGuard; + uno::Reference < drawing::XShape > xShape; + if( const ScPostIt* pNote = ImplGetNote() ) + if( SdrObject* pCaption = pNote->GetOrCreateCaption( aCellPos ) ) + xShape.set( pCaption->getUnoShape(), uno::UNO_QUERY ); + return xShape; +} + +SvxUnoText& ScAnnotationObj::GetUnoText() +{ + if (!pUnoText.is()) + { + ScAnnotationEditSource aEditSource( pDocShell, aCellPos ); + pUnoText = new SvxUnoText( &aEditSource, lcl_GetAnnotationPropertySet(), + uno::Reference<text::XText>() ); + } + return *pUnoText; +} + +const ScPostIt* ScAnnotationObj::ImplGetNote() const +{ + return pDocShell ? pDocShell->GetDocument().GetNote(aCellPos) : nullptr; +} +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/unoobj/optuno.cxx b/sc/source/ui/unoobj/optuno.cxx new file mode 100644 index 0000000000..09e17add09 --- /dev/null +++ b/sc/source/ui/unoobj/optuno.cxx @@ -0,0 +1,221 @@ +/* -*- 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 <svl/itemprop.hxx> +#include <vcl/svapp.hxx> + +#include <com/sun/star/util/Date.hpp> + +#include <optuno.hxx> +#include <miscuno.hxx> +#include <unonames.hxx> +#include <docoptio.hxx> + +using namespace com::sun::star; + +std::span<const SfxItemPropertyMapEntry> ScDocOptionsHelper::GetPropertyMap() +{ + static const SfxItemPropertyMapEntry aMap[] = + { + { SC_UNO_CALCASSHOWN, PROP_UNO_CALCASSHOWN , cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_DEFTABSTOP, PROP_UNO_DEFTABSTOP , cppu::UnoType<sal_Int16>::get(), 0, 0}, + { SC_UNO_IGNORECASE, PROP_UNO_IGNORECASE , cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_ITERENABLED, PROP_UNO_ITERENABLED , cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_ITERCOUNT, PROP_UNO_ITERCOUNT , cppu::UnoType<sal_Int32>::get(), 0, 0}, + { SC_UNO_ITEREPSILON, PROP_UNO_ITEREPSILON , cppu::UnoType<double>::get(), 0, 0}, + { SC_UNO_LOOKUPLABELS, PROP_UNO_LOOKUPLABELS, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_MATCHWHOLE, PROP_UNO_MATCHWHOLE , cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_NULLDATE, PROP_UNO_NULLDATE , cppu::UnoType<util::Date>::get(), 0, 0}, + { SC_UNO_SPELLONLINE, PROP_UNO_SPELLONLINE , cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_STANDARDDEC, PROP_UNO_STANDARDDEC , cppu::UnoType<sal_Int16>::get(), 0, 0}, + { SC_UNO_REGEXENABLED, PROP_UNO_REGEXENABLED, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_WILDCARDSENABLED, PROP_UNO_WILDCARDSENABLED, cppu::UnoType<bool>::get(), 0, 0}, + }; + return aMap; +} + +bool ScDocOptionsHelper::setPropertyValue( ScDocOptions& rOptions, + const SfxItemPropertyMap& rPropMap, + std::u16string_view aPropertyName, const uno::Any& aValue ) +{ + //! use map (with new identifiers) + + const SfxItemPropertyMapEntry* pEntry = rPropMap.getByName(aPropertyName ); + if( !pEntry || !pEntry->nWID ) + return false; + switch( pEntry->nWID ) + { + case PROP_UNO_CALCASSHOWN : + rOptions.SetCalcAsShown( ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + break; + case PROP_UNO_DEFTABSTOP : + { + sal_Int16 nIntVal = 0; + if ( aValue >>= nIntVal ) + rOptions.SetTabDistance( nIntVal ); + } + break; + case PROP_UNO_IGNORECASE : + rOptions.SetIgnoreCase( ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + break; + case PROP_UNO_ITERENABLED: + rOptions.SetIter( ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + break; + case PROP_UNO_ITERCOUNT : + { + sal_Int32 nIntVal = 0; + if ( aValue >>= nIntVal ) + rOptions.SetIterCount( static_cast<sal_uInt16>(nIntVal) ); + } + break; + case PROP_UNO_ITEREPSILON : + { + double fDoubleVal = 0; + if ( aValue >>= fDoubleVal ) + rOptions.SetIterEps( fDoubleVal ); + } + break; + case PROP_UNO_LOOKUPLABELS : + rOptions.SetLookUpColRowNames( ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + break; + case PROP_UNO_MATCHWHOLE : + rOptions.SetMatchWholeCell( ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + break; + case PROP_UNO_NULLDATE: + { + util::Date aDate; + if ( aValue >>= aDate ) + rOptions.SetDate( aDate.Day, aDate.Month, aDate.Year ); + } + break; + case PROP_UNO_SPELLONLINE: + rOptions.SetAutoSpell( ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + break; + case PROP_UNO_STANDARDDEC: + { + sal_Int16 nIntVal = 0; + if ( aValue >>= nIntVal ) + rOptions.SetStdPrecision( nIntVal ); + } + break; + case PROP_UNO_REGEXENABLED: + rOptions.SetFormulaRegexEnabled( ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + break; + case PROP_UNO_WILDCARDSENABLED: + rOptions.SetFormulaWildcardsEnabled( ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + break; + default:; + } + return true; +} + +uno::Any ScDocOptionsHelper::getPropertyValue( + const ScDocOptions& rOptions, + const SfxItemPropertyMap& rPropMap, + std::u16string_view aPropertyName ) +{ + uno::Any aRet; + const SfxItemPropertyMapEntry* pEntry = rPropMap.getByName( aPropertyName ); + if( !pEntry || !pEntry->nWID ) + return aRet; + switch( pEntry->nWID ) + { + case PROP_UNO_CALCASSHOWN : + aRet <<= rOptions.IsCalcAsShown(); + break; + case PROP_UNO_DEFTABSTOP : + aRet <<= static_cast<sal_Int16>( rOptions.GetTabDistance() ); + break; + case PROP_UNO_IGNORECASE : + aRet <<= rOptions.IsIgnoreCase(); + break; + case PROP_UNO_ITERENABLED: + aRet <<= rOptions.IsIter(); + break; + case PROP_UNO_ITERCOUNT: + aRet <<= static_cast<sal_Int32>( rOptions.GetIterCount() ); + break; + case PROP_UNO_ITEREPSILON: + aRet <<= rOptions.GetIterEps(); + break; + case PROP_UNO_LOOKUPLABELS: + aRet <<= rOptions.IsLookUpColRowNames(); + break; + case PROP_UNO_MATCHWHOLE: + aRet <<= rOptions.IsMatchWholeCell(); + break; + case PROP_UNO_NULLDATE: + { + sal_uInt16 nD, nM; + sal_Int16 nY; + rOptions.GetDate( nD, nM, nY ); + util::Date aDate( nD, nM, nY ); + aRet <<= aDate; + } + break; + case PROP_UNO_SPELLONLINE: + aRet <<= rOptions.IsAutoSpell(); + break; + case PROP_UNO_STANDARDDEC : + aRet <<= static_cast<sal_Int16>( rOptions.GetStdPrecision() ); + break; + case PROP_UNO_REGEXENABLED: + aRet <<= rOptions.IsFormulaRegexEnabled(); + break; + case PROP_UNO_WILDCARDSENABLED: + aRet <<= rOptions.IsFormulaWildcardsEnabled(); + break; + default:; + } + return aRet; +} + +ScDocOptionsObj::ScDocOptionsObj( const ScDocOptions& rOpt ) : + ScModelObj( nullptr ), + aOptions( rOpt ) +{ +} + +ScDocOptionsObj::~ScDocOptionsObj() +{ +} + +void SAL_CALL ScDocOptionsObj::setPropertyValue( + const OUString& aPropertyName, const uno::Any& aValue ) +{ + SolarMutexGuard aGuard; + + bool bDone = ScDocOptionsHelper::setPropertyValue( aOptions, GetPropertySet().getPropertyMap(), aPropertyName, aValue ); + + if (!bDone) + ScModelObj::setPropertyValue( aPropertyName, aValue ); +} + +uno::Any SAL_CALL ScDocOptionsObj::getPropertyValue( const OUString& aPropertyName ) +{ + SolarMutexGuard aGuard; + + uno::Any aRet(ScDocOptionsHelper::getPropertyValue( aOptions, GetPropertySet().getPropertyMap(), aPropertyName )); + if ( !aRet.hasValue() ) + aRet = ScModelObj::getPropertyValue( aPropertyName ); + + return aRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/unoobj/pageuno.cxx b/sc/source/ui/unoobj/pageuno.cxx new file mode 100644 index 0000000000..3ca903e63e --- /dev/null +++ b/sc/source/ui/unoobj/pageuno.cxx @@ -0,0 +1,60 @@ +/* -*- 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 <cppuhelper/supportsservice.hxx> +#include <rtl/ustring.hxx> +#include <pageuno.hxx> +#include <shapeuno.hxx> + +using namespace ::com::sun::star; + +ScPageObj::ScPageObj( SdrPage* pPage ) : + SvxDrawPage( pPage ) +{ +} + +ScPageObj::~ScPageObj() noexcept +{ +} + +uno::Reference<drawing::XShape > ScPageObj::CreateShape( SdrObject *pObj ) const +{ + uno::Reference<drawing::XShape> xShape(SvxDrawPage::CreateShape( pObj )); + + new ScShapeObj( xShape ); // aggregates object and modifies xShape + + return xShape; +} + +OUString SAL_CALL ScPageObj::getImplementationName() +{ + return "ScPageObj"; +} + +sal_Bool SAL_CALL ScPageObj::supportsService( const OUString& rServiceName ) +{ + return cppu::supportsService(this, rServiceName); +} + +uno::Sequence<OUString> SAL_CALL ScPageObj::getSupportedServiceNames() +{ + return { "com.sun.star.sheet.SpreadsheetDrawPage" }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/unoobj/scdetect.cxx b/sc/source/ui/unoobj/scdetect.cxx new file mode 100644 index 0000000000..e5fc5848e1 --- /dev/null +++ b/sc/source/ui/unoobj/scdetect.cxx @@ -0,0 +1,353 @@ +/* -*- 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 "scdetect.hxx" + +#include <sal/macros.h> + +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <cppuhelper/supportsservice.hxx> +#include <com/sun/star/io/XInputStream.hpp> +#include <unotools/mediadescriptor.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/docfilt.hxx> +#include <sfx2/fcontnr.hxx> + +using namespace ::com::sun::star; +using utl::MediaDescriptor; + +namespace { + +// table with search pattern +// meaning of the sequences +// 0x00??: the exact byte 0x?? must be at that place +// 0x0100: read over a byte (don't care) +// 0x02nn: a byte of 0xnn variations follows +// 0x8000: recognition finished + +#define M_DC 0x0100 +#define M_ALT(CNT) (0x0200+(CNT)) +#define M_END 0x8000 + +const sal_uInt16 pLotus[] = // Lotus 1/1A/2 + { 0x0000, 0x0000, 0x0002, 0x0000, + M_ALT(2), 0x0004, 0x0006, + 0x0004, M_END }; + +const sal_uInt16 pLotusNew[] = // Lotus >= 9.7 + { 0x0000, 0x0000, M_DC, 0x0000, // Rec# + Len (0x1a) + M_ALT(3), 0x0003, 0x0004, 0x0005, // File Revision Code 97->ME + 0x0010, 0x0004, 0x0000, 0x0000, + M_END }; + +const sal_uInt16 pLotus2[] = // Lotus >3 + { 0x0000, 0x0000, 0x001A, 0x0000, // Rec# + Len (26) + M_ALT(2), 0x0000, 0x0002, // File Revision Code + 0x0010, + 0x0004, 0x0000, // File Revision Subcode + M_END }; + +const sal_uInt16 pQPro[] = + { 0x0000, 0x0000, 0x0002, 0x0000, + M_ALT(4), 0x0001, 0x0002, // WB1, WB2 + 0x0006, 0x0007, // QPro 6/7 (?) + 0x0010, + M_END }; + +const sal_uInt16 pDIF1[] = // DIF with CR-LF + { + 'T', 'A', 'B', 'L', 'E', + M_DC, M_DC, + '0', ',', '1', + M_DC, M_DC, + '\"', + M_END }; + +const sal_uInt16 pDIF2[] = // DIF with CR or LF + { + 'T', 'A', 'B', 'L', 'E', + M_DC, + '0', ',', '1', + M_DC, + '\"', + M_END }; + +const sal_uInt16 pSylk[] = // Sylk + { + 'I', 'D', ';', + M_ALT(3), 'P', 'N', 'E', // 'P' plus undocumented Excel extensions 'N' and 'E' + M_END }; + +bool detectThisFormat(SvStream& rStr, const sal_uInt16* pSearch) +{ + sal_uInt8 nByte; + rStr.Seek( 0 ); // in the beginning everything was bad... + rStr.ReadUChar( nByte ); + bool bSync = true; + while( !rStr.eof() && bSync ) + { + sal_uInt16 nMuster = *pSearch; + + if( nMuster < 0x0100 ) + { // compare bytes + if( static_cast<sal_uInt8>(nMuster) != nByte ) + bSync = false; + } + else if( nMuster & M_DC ) + { // don't care + } + else if( nMuster & M_ALT(0) ) + { // alternative Bytes + sal_uInt8 nCntAlt = static_cast<sal_uInt8>(nMuster); + bSync = false; // first unsynchron + while( nCntAlt > 0 ) + { + pSearch++; + if( static_cast<sal_uInt8>(*pSearch) == nByte ) + bSync = true; // only now synchronization + nCntAlt--; + } + } + else if( nMuster & M_END ) + { // Format detected + return true; + } + + pSearch++; + rStr.ReadUChar( nByte ); + } + + return false; +} + +} + +ScFilterDetect::ScFilterDetect() +{ +} + +ScFilterDetect::~ScFilterDetect() +{ +} + +#if 0 +// This method is no longer used, but I do want to keep this for now to see +// if we could transfer this check to the now centralized ascii detection +// code in the filter module. +static sal_Bool lcl_MayBeAscii( SvStream& rStream ) +{ + // ASCII/CSV is considered possible if there are no null bytes, or a Byte + // Order Mark is present, or if, for Unicode UCS2/UTF-16, all null bytes + // are on either even or uneven byte positions. + + rStream.Seek(STREAM_SEEK_TO_BEGIN); + + const size_t nBufSize = 2048; + sal_uInt16 aBuffer[ nBufSize ]; + sal_uInt8* pByte = reinterpret_cast<sal_uInt8*>(aBuffer); + sal_uLong nBytesRead = rStream.Read( pByte, nBufSize*2); + + if ( nBytesRead >= 2 && (aBuffer[0] == 0xfffe || aBuffer[0] == 0xfeff) ) + { + // Unicode BOM file may contain null bytes. + return sal_True; + } + + const sal_uInt16* p = aBuffer; + sal_uInt16 nMask = 0xffff; + nBytesRead /= 2; + while( nBytesRead-- && nMask ) + { + sal_uInt16 nVal = *p++ & nMask; + if (!(nVal & 0x00ff)) + nMask &= 0xff00; + if (!(nVal & 0xff00)) + nMask &= 0x00ff; + } + + return nMask != 0; +} +#endif + +static bool lcl_MayBeDBase( SvStream& rStream ) +{ + // Look for dbf marker, see connectivity/source/inc/dbase/DTable.hxx + // DBFType for values. + const sal_uInt8 nValidMarks[] = { + 0x03, 0x04, 0x05, 0x30, 0x31, 0x43, 0xB3, 0x83, 0x8b, 0x8e, 0xf5 }; + sal_uInt8 nMark; + rStream.Seek(STREAM_SEEK_TO_BEGIN); + rStream.ReadUChar( nMark ); + bool bValidMark = false; + for (size_t i=0; i < SAL_N_ELEMENTS(nValidMarks) && !bValidMark; ++i) + { + if (nValidMarks[i] == nMark) + bValidMark = true; + } + if ( !bValidMark ) + return false; + + const size_t nHeaderBlockSize = 32; + // Empty dbf is >= 32*2+1 bytes in size. + const size_t nEmptyDbf = nHeaderBlockSize * 2 + 1; + + sal_uInt64 nSize = rStream.TellEnd(); + if ( nSize < nEmptyDbf ) + return false; + + // count of records at 4 + rStream.Seek(4); + sal_uInt32 nRecords(0); + rStream.ReadUInt32(nRecords); + + // length of header starts at 8 + rStream.Seek(8); + sal_uInt16 nHeaderLen; + rStream.ReadUInt16( nHeaderLen ); + + // size of record at 10 + sal_uInt16 nRecordSize(0); + rStream.ReadUInt16(nRecordSize); + + if ( nHeaderLen < nEmptyDbf || nSize < nHeaderLen ) + return false; + + // see DTable.cxx ODbaseTable::readHeader() + if (0 == nRecordSize) + return false; + + // see DTable.cxx ODbaseTable::construct() line 546 + if (0 == nRecords) + { + nRecords = (nSize - nHeaderLen) / nRecordSize; + } + + // tdf#84834 sanity check of size + // tdf#106423: a dbf file can have 0 record, so no need to check nRecords + if (nSize < nHeaderLen + nRecords * sal_uInt64(nRecordSize)) + return false; + + // Last byte of header must be 0x0d, this is how it's specified. + // #i9581#,#i26407# but some applications don't follow the specification + // and pad the header with one byte 0x00 to reach an + // even boundary. Some (#i88577# ) even pad more or pad using a 0x1a ^Z + // control character (#i8857#). This results in: + // Last byte of header must be 0x0d on 32 bytes boundary. + sal_uInt16 nBlocks = (nHeaderLen - 1) / nHeaderBlockSize; + sal_uInt8 nEndFlag = 0; + while ( nBlocks > 1 && nEndFlag != 0x0d ) { + rStream.Seek( nBlocks-- * nHeaderBlockSize ); + rStream.ReadUChar( nEndFlag ); + } + + return ( 0x0d == nEndFlag ); +} + +OUString SAL_CALL ScFilterDetect::detect( uno::Sequence<beans::PropertyValue>& lDescriptor ) +{ + MediaDescriptor aMediaDesc( lDescriptor ); + OUString aTypeName = aMediaDesc.getUnpackedValueOrDefault( MediaDescriptor::PROP_TYPENAME, OUString() ); + uno::Reference< io::XInputStream > xStream ( aMediaDesc[MediaDescriptor::PROP_INPUTSTREAM], uno::UNO_QUERY ); + if ( !xStream.is() ) + return OUString(); + + SfxMedium aMedium; + aMedium.UseInteractionHandler( false ); + aMedium.setStreamToLoadFrom( xStream, true ); + + SvStream* pStream = aMedium.GetInStream(); + if ( !pStream || pStream->GetError() ) + // No stream, no detection. + return OUString(); + + const char* pSearchFilterName = nullptr; + if (aTypeName == "calc_Lotus") + { + if (!detectThisFormat(*pStream, pLotus) && !detectThisFormat(*pStream, pLotusNew) && !detectThisFormat(*pStream, pLotus2)) + return OUString(); + + pSearchFilterName = "Lotus"; + } + else if (aTypeName == "calc_QPro") + { + if (!detectThisFormat(*pStream, pQPro)) + return OUString(); + + pSearchFilterName = "Quattro Pro 6.0"; + } + else if (aTypeName == "calc_SYLK") + { + if (!detectThisFormat(*pStream, pSylk)) + return OUString(); + + pSearchFilterName = "SYLK"; + } + else if (aTypeName == "calc_DIF") + { + if (!detectThisFormat(*pStream, pDIF1) && !detectThisFormat(*pStream, pDIF2)) + return OUString(); + + pSearchFilterName = "DIF"; + } + else if (aTypeName == "calc_dBase") + { + if (!lcl_MayBeDBase(*pStream)) + return OUString(); + + pSearchFilterName = "dBase"; + } + else + return OUString(); + + SfxFilterMatcher aMatcher("scalc"); + std::shared_ptr<const SfxFilter> pFilter = aMatcher.GetFilter4FilterName(OUString::createFromAscii(pSearchFilterName)); + + if (!pFilter) + return OUString(); + + aMediaDesc[MediaDescriptor::PROP_FILTERNAME] <<= pFilter->GetName(); + aMediaDesc >> lDescriptor; + return aTypeName; +} + +OUString SAL_CALL ScFilterDetect::getImplementationName() +{ + return "com.sun.star.comp.calc.FormatDetector"; +} + +sal_Bool ScFilterDetect::supportsService( const OUString& sServiceName ) +{ + return cppu::supportsService(this, sServiceName); +} + +css::uno::Sequence<OUString> ScFilterDetect::getSupportedServiceNames() +{ + return { "com.sun.star.frame.ExtendedTypeDetection" }; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_calc_FormatDetector_get_implementation(css::uno::XComponentContext* /*context*/, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new ScFilterDetect); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/unoobj/scdetect.hxx b/sc/source/ui/unoobj/scdetect.hxx new file mode 100644 index 0000000000..697bd8d58e --- /dev/null +++ b/sc/source/ui/unoobj/scdetect.hxx @@ -0,0 +1,49 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/document/XExtendedFilterDetection.hpp> +#include <cppuhelper/implbase.hxx> + +#include <com/sun/star/lang/XServiceInfo.hpp> + +#include <rtl/ustring.hxx> + +namespace com::sun::star { + namespace beans { struct PropertyValue; } +} + +class ScFilterDetect : public ::cppu::WeakImplHelper< css::document::XExtendedFilterDetection, css::lang::XServiceInfo > +{ +public: + explicit ScFilterDetect(); + virtual ~ScFilterDetect() override; + + /* XServiceInfo */ + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& sServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XExtendedFilterDetect + + virtual OUString SAL_CALL detect( css::uno::Sequence<css::beans::PropertyValue>& lDescriptor ) override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/unoobj/servuno.cxx b/sc/source/ui/unoobj/servuno.cxx new file mode 100644 index 0000000000..4326817027 --- /dev/null +++ b/sc/source/ui/unoobj/servuno.cxx @@ -0,0 +1,625 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <memory> +#include <config_features.h> + +#include <sal/macros.h> +#include <svtools/unoimap.hxx> +#include <svx/unofill.hxx> +#include <vcl/svapp.hxx> +#include <com/sun/star/sheet/XSpreadsheetDocument.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/text/textfield/Type.hpp> + +#include <editsrc.hxx> +#include <servuno.hxx> +#include <unonames.hxx> +#include <appluno.hxx> +#include <cellsuno.hxx> +#include <fielduno.hxx> +#include <styleuno.hxx> +#include <afmtuno.hxx> +#include <defltuno.hxx> +#include <drdefuno.hxx> +#include <docsh.hxx> +#include <drwlayer.hxx> +#include <confuno.hxx> +#include <shapeuno.hxx> +#include "cellvaluebinding.hxx" +#include "celllistsource.hxx" +#include <addruno.hxx> +#include <chart2uno.hxx> +#include <tokenuno.hxx> +#include <PivotTableDataProvider.hxx> + +// Support creation of GraphicStorageHandler and EmbeddedObjectResolver +#include <svx/xmleohlp.hxx> +#include <svx/xmlgrhlp.hxx> +#include <com/sun/star/script/vba/XVBAEventProcessor.hpp> +#include <com/sun/star/document/XCodeNameQuery.hpp> +#include <com/sun/star/drawing/XDrawPagesSupplier.hpp> +#include <com/sun/star/form/XFormsSupplier.hpp> +#include <svx/unomod.hxx> +#include <vbahelper/vbaaccesshelper.hxx> + +#include <comphelper/processfactory.hxx> +#include <basic/basmgr.hxx> +#include <sfx2/app.hxx> + +#include <cppuhelper/implbase.hxx> +#include <com/sun/star/script/vba/XVBACompatibility.hpp> + +using namespace ::com::sun::star; + +#if HAVE_FEATURE_SCRIPTING + +static bool isInVBAMode( ScDocShell& rDocSh ) +{ + uno::Reference<script::XLibraryContainer> xLibContainer = rDocSh.GetBasicContainer(); + uno::Reference<script::vba::XVBACompatibility> xVBACompat( xLibContainer, uno::UNO_QUERY ); + if ( xVBACompat.is() ) + return xVBACompat->getVBACompatibilityMode(); + return false; +} + +#endif + +namespace { + +#if HAVE_FEATURE_SCRIPTING +class ScVbaObjectForCodeNameProvider : public ::cppu::WeakImplHelper< container::XNameAccess > +{ + uno::Any maWorkbook; + uno::Any maCachedObject; + ScDocShell* mpDocShell; +public: + explicit ScVbaObjectForCodeNameProvider( ScDocShell* pDocShell ) : mpDocShell( pDocShell ) + { + uno::Sequence< uno::Any > aArgs{ + // access the application object ( parent for workbook ) + uno::Any(ooo::vba::createVBAUnoAPIServiceWithArgs( mpDocShell, "ooo.vba.Application", {} )), + uno::Any(uno::Reference(static_cast<css::sheet::XSpreadsheetDocument*>(mpDocShell->GetModel()))) + }; + maWorkbook <<= ooo::vba::createVBAUnoAPIServiceWithArgs( mpDocShell, "ooo.vba.excel.Workbook", aArgs ); + } + + virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override + { + SolarMutexGuard aGuard; + maCachedObject = uno::Any(); // clear cached object + + ScDocument& rDoc = mpDocShell->GetDocument(); + // aName is generated from the stream name which can be different ( case-wise ) + // from the code name + if( aName.equalsIgnoreAsciiCase( rDoc.GetCodeName() ) ) + maCachedObject = maWorkbook; + else + { + OUString sCodeName; + SCTAB nCount = rDoc.GetTableCount(); + for( SCTAB i = 0; i < nCount; i++ ) + { + rDoc.GetCodeName( i, sCodeName ); + // aName is generated from the stream name which can be different ( case-wise ) + // from the code name + if( sCodeName.equalsIgnoreAsciiCase( aName ) ) + { + OUString sSheetName; + if( rDoc.GetName( i, sSheetName ) ) + { + rtl::Reference< ScModelObj > xSpreadDoc( mpDocShell->GetModel() ); + uno::Reference<sheet::XSpreadsheets > xSheets( xSpreadDoc->getSheets(), uno::UNO_SET_THROW ); + uno::Reference< container::XIndexAccess > xIndexAccess( xSheets, uno::UNO_QUERY_THROW ); + uno::Reference< sheet::XSpreadsheet > xSheet( xIndexAccess->getByIndex( i ), uno::UNO_QUERY_THROW ); + uno::Sequence< uno::Any > aArgs{ maWorkbook, uno::Any(uno::Reference< frame::XModel >(xSpreadDoc)), uno::Any(sSheetName) }; + // use the convenience function + maCachedObject <<= ooo::vba::createVBAUnoAPIServiceWithArgs( mpDocShell, "ooo.vba.excel.Worksheet", aArgs ); + break; + } + } + } + } + return maCachedObject.hasValue(); + + } + css::uno::Any SAL_CALL getByName( const OUString& aName ) override + { + SolarMutexGuard aGuard; + if ( !hasByName( aName ) ) + throw css::container::NoSuchElementException(); + return maCachedObject; + } + virtual css::uno::Sequence< OUString > SAL_CALL getElementNames( ) override + { + SolarMutexGuard aGuard; + ScDocument& rDoc = mpDocShell->GetDocument(); + SCTAB nCount = rDoc.GetTableCount(); + uno::Sequence< OUString > aNames( nCount + 1 ); + auto pNames = aNames.getArray(); + SCTAB index = 0; + OUString sCodeName; + for( ; index < nCount; ++index ) + { + rDoc.GetCodeName( index, sCodeName ); + pNames[ index ] = sCodeName; + } + pNames[ index ] = rDoc.GetCodeName(); + return aNames; + } + // XElemenAccess + virtual css::uno::Type SAL_CALL getElementType( ) override { return uno::Type(); } + virtual sal_Bool SAL_CALL hasElements( ) override { return true; } + +}; + +class ScVbaCodeNameProvider : public ::cppu::WeakImplHelper< document::XCodeNameQuery > +{ + ScDocShell& mrDocShell; +public: + explicit ScVbaCodeNameProvider( ScDocShell& rDocShell ) : mrDocShell(rDocShell) {} + // XCodeNameQuery + OUString SAL_CALL getCodeNameForObject( const uno::Reference< uno::XInterface >& xIf ) override + { + SolarMutexGuard aGuard; + OUString sCodeName; + + // need to find the page ( and index ) for this control + uno::Reference< container::XIndexAccess > xIndex( mrDocShell.GetModel()->getDrawPages(), uno::UNO_QUERY_THROW ); + sal_Int32 nLen = xIndex->getCount(); + bool bMatched = false; + for ( sal_Int32 index = 0; index < nLen; ++index ) + { + try + { + uno::Reference< form::XFormsSupplier > xFormSupplier( xIndex->getByIndex( index ), uno::UNO_QUERY_THROW ); + uno::Reference< container::XIndexAccess > xFormIndex( xFormSupplier->getForms(), uno::UNO_QUERY_THROW ); + // get the www-standard container + uno::Reference< container::XIndexAccess > xFormControls( xFormIndex->getByIndex(0), uno::UNO_QUERY_THROW ); + sal_Int32 nCntrls = xFormControls->getCount(); + for( sal_Int32 cIndex = 0; cIndex < nCntrls; ++cIndex ) + { + uno::Reference< uno::XInterface > xControl( xFormControls->getByIndex( cIndex ), uno::UNO_QUERY_THROW ); + bMatched = ( xControl == xIf ); + if ( bMatched ) + { + OUString sName; + mrDocShell.GetDocument().GetCodeName( static_cast<SCTAB>( index ), sName ); + sCodeName = sName; + } + } + } + catch( uno::Exception& ) {} + if ( bMatched ) + break; + } + // Probably should throw here ( if !bMatched ) + return sCodeName; + } + + OUString SAL_CALL getCodeNameForContainer( const uno::Reference<uno::XInterface>& xContainer ) override + { + SolarMutexGuard aGuard; + uno::Reference<container::XIndexAccess> xIndex(mrDocShell.GetModel()->getDrawPages(), uno::UNO_QUERY_THROW); + + for (sal_Int32 i = 0, n = xIndex->getCount(); i < n; ++i) + { + try + { + uno::Reference<form::XFormsSupplier> xFormSupplier(xIndex->getByIndex(i), uno::UNO_QUERY_THROW); + uno::Reference<container::XIndexAccess> xFormIndex(xFormSupplier->getForms(), uno::UNO_QUERY_THROW); + // get the www-standard container + uno::Reference<container::XIndexAccess> xFormControls(xFormIndex->getByIndex(0), uno::UNO_QUERY_THROW); + if (xFormControls == xContainer) + { + OUString aName; + if (mrDocShell.GetDocument().GetCodeName(static_cast<SCTAB>(i), aName)) + return aName; + } + } + catch( uno::Exception& ) {} + } + return OUString(); + } +}; + +#endif + +using Type = ScServiceProvider::Type; + +struct ProvNamesId_Type +{ + OUString pName; + ScServiceProvider::Type nType; +}; + +const ProvNamesId_Type aProvNamesId[] = +{ + { "com.sun.star.sheet.Spreadsheet", Type::SHEET }, + { "com.sun.star.text.TextField.URL", Type::URLFIELD }, + { "com.sun.star.text.TextField.PageNumber", Type::PAGEFIELD }, + { "com.sun.star.text.TextField.PageCount", Type::PAGESFIELD }, + { "com.sun.star.text.TextField.Date", Type::DATEFIELD }, + { "com.sun.star.text.TextField.Time", Type::TIMEFIELD }, + { "com.sun.star.text.TextField.DateTime", Type::EXT_TIMEFIELD }, + { "com.sun.star.text.TextField.DocInfo.Title", Type::TITLEFIELD }, + { "com.sun.star.text.TextField.FileName", Type::FILEFIELD }, + { "com.sun.star.text.TextField.SheetName", Type::SHEETFIELD }, + { "com.sun.star.style.CellStyle", Type::CELLSTYLE }, + { "com.sun.star.style.PageStyle", Type::PAGESTYLE }, + { "com.sun.star.style.GraphicStyle", Type::GRAPHICSTYLE }, + { "com.sun.star.sheet.TableAutoFormat", Type::AUTOFORMAT }, + { "com.sun.star.sheet.TableAutoFormats", Type::AUTOFORMATS }, + { "com.sun.star.sheet.SheetCellRanges", Type::CELLRANGES }, + { "com.sun.star.sheet.FunctionDescriptions", Type::FUNCTIONDESCRIPTIONS }, + { "com.sun.star.sheet.GlobalSheetSettings", Type::GLOBALSHEETSETTINGS }, + { "com.sun.star.sheet.RecentFunctions", Type::RECENTFUNCTIONS }, + { "com.sun.star.drawing.GradientTable", Type::GRADTAB }, + { "com.sun.star.drawing.HatchTable", Type::HATCHTAB }, + { "com.sun.star.drawing.BitmapTable", Type::BITMAPTAB }, + { "com.sun.star.drawing.TransparencyGradientTable", Type::TRGRADTAB }, + { "com.sun.star.drawing.MarkerTable", Type::MARKERTAB }, + { "com.sun.star.drawing.DashTable", Type::DASHTAB }, + { "com.sun.star.text.NumberingRules", Type::NUMRULES }, + { "com.sun.star.sheet.Defaults", Type::DOCDEFLTS }, + { "com.sun.star.drawing.Defaults", Type::DRAWDEFLTS }, + { "com.sun.star.comp.SpreadsheetSettings", Type::DOCSPRSETT }, + { "com.sun.star.document.Settings", Type::DOCCONF }, + { "com.sun.star.image.ImageMapRectangleObject", Type::IMAP_RECT }, + { "com.sun.star.image.ImageMapCircleObject", Type::IMAP_CIRC }, + { "com.sun.star.image.ImageMapPolygonObject", Type::IMAP_POLY }, + + // Support creation of GraphicStorageHandler and EmbeddedObjectResolver + { "com.sun.star.document.ExportGraphicStorageHandler", Type::EXPORT_GRAPHIC_STORAGE_HANDLER }, + { "com.sun.star.document.ImportGraphicStorageHandler", Type::IMPORT_GRAPHIC_STORAGE_HANDLER }, + { "com.sun.star.document.ExportEmbeddedObjectResolver", Type::EXPORT_EOR }, + { "com.sun.star.document.ImportEmbeddedObjectResolver", Type::IMPORT_EOR }, + + { SC_SERVICENAME_VALBIND, Type::VALBIND }, + { SC_SERVICENAME_LISTCELLBIND, Type::LISTCELLBIND }, + { SC_SERVICENAME_LISTSOURCE, Type::LISTSOURCE }, + { SC_SERVICENAME_CELLADDRESS, Type::CELLADDRESS }, + { SC_SERVICENAME_RANGEADDRESS, Type::RANGEADDRESS }, + + { "com.sun.star.sheet.DocumentSettings",Type::SHEETDOCSET }, + + { SC_SERVICENAME_CHDATAPROV, Type::CHDATAPROV }, + { SC_SERVICENAME_CHART_PIVOTTABLE_DATAPROVIDER, Type::CHART_PIVOTTABLE_DATAPROVIDER }, + { SC_SERVICENAME_FORMULAPARS, Type::FORMULAPARS }, + { SC_SERVICENAME_OPCODEMAPPER, Type::OPCODEMAPPER }, + { "ooo.vba.VBAObjectModuleObjectProvider", Type::VBAOBJECTPROVIDER }, + { "ooo.vba.VBACodeNameProvider", Type::VBACODENAMEPROVIDER }, + { "ooo.vba.VBAGlobals", Type::VBAGLOBALS }, + + // case-correct versions of the service names (#i102468#) + { "com.sun.star.text.textfield.URL", Type::URLFIELD }, + { "com.sun.star.text.textfield.PageNumber", Type::PAGEFIELD }, + { "com.sun.star.text.textfield.PageCount", Type::PAGESFIELD }, + { "com.sun.star.text.textfield.Date", Type::DATEFIELD }, + { "com.sun.star.text.textfield.Time", Type::TIMEFIELD }, + { "com.sun.star.text.textfield.DateTime", Type::EXT_TIMEFIELD }, + { "com.sun.star.text.textfield.docinfo.Title", Type::TITLEFIELD }, + { "com.sun.star.text.textfield.FileName", Type::FILEFIELD }, + { "com.sun.star.text.textfield.SheetName", Type::SHEETFIELD }, + { "ooo.vba.VBAGlobals", Type::VBAGLOBALS }, +}; + +// old service names that were in 567 still work in createInstance, +// in case some macro is still using them +const ProvNamesId_Type aOldNames[] = +{ + { "stardiv.one.text.TextField.URL", Type::URLFIELD }, + { "stardiv.one.text.TextField.PageNumber", Type::PAGEFIELD }, + { "stardiv.one.text.TextField.PageCount", Type::PAGESFIELD }, + { "stardiv.one.text.TextField.Date", Type::DATEFIELD }, + { "stardiv.one.text.TextField.Time", Type::TIMEFIELD }, + { "stardiv.one.text.TextField.DocumentTitle", Type::TITLEFIELD }, + { "stardiv.one.text.TextField.FileName", Type::FILEFIELD }, + { "stardiv.one.text.TextField.SheetName", Type::SHEETFIELD }, + { "stardiv.one.style.CellStyle", Type::CELLSTYLE }, + { "stardiv.one.style.PageStyle", Type::PAGESTYLE }, +}; + +sal_Int32 getFieldType(ScServiceProvider::Type nOldType) +{ + switch (nOldType) + { + case Type::URLFIELD: + return text::textfield::Type::URL; + case Type::PAGEFIELD: + return text::textfield::Type::PAGE; + case Type::PAGESFIELD: + return text::textfield::Type::PAGES; + case Type::DATEFIELD: + return text::textfield::Type::DATE; + case Type::TIMEFIELD: + return text::textfield::Type::TIME; + case Type::EXT_TIMEFIELD: + return text::textfield::Type::EXTENDED_TIME; + case Type::TITLEFIELD: + return text::textfield::Type::DOCINFO_TITLE; + case Type::FILEFIELD: + return text::textfield::Type::EXTENDED_FILE; + case Type::SHEETFIELD: + return text::textfield::Type::TABLE; + default: + ; + } + + return text::textfield::Type::URL; // default to URL for no reason whatsoever. +} + +} // namespace + + +ScServiceProvider::Type ScServiceProvider::GetProviderType(std::u16string_view rServiceName) +{ + if (!rServiceName.empty()) + { + for (const ProvNamesId_Type & i : aProvNamesId) + { + if (rServiceName == i.pName) + { + return i.nType; + } + } + + for (const ProvNamesId_Type & rOldName : aOldNames) + { + if (rServiceName == rOldName.pName) + { + OSL_FAIL("old service name used"); + return rOldName.nType; + } + } + } + return Type::INVALID; +} + +uno::Reference<uno::XInterface> ScServiceProvider::MakeInstance( + Type nType, ScDocShell* pDocShell ) +{ + uno::Reference<uno::XInterface> xRet; + + switch (nType) + { + case Type::SHEET: + // not inserted yet - DocShell=Null + xRet.set(static_cast<sheet::XSpreadsheet*>(new ScTableSheetObj(nullptr,0))); + break; + case Type::URLFIELD: + case Type::PAGEFIELD: + case Type::PAGESFIELD: + case Type::DATEFIELD: + case Type::TIMEFIELD: + case Type::EXT_TIMEFIELD: + case Type::TITLEFIELD: + case Type::FILEFIELD: + case Type::SHEETFIELD: + { + uno::Reference<text::XTextRange> xNullContent; + xRet.set(static_cast<text::XTextField*>( + new ScEditFieldObj(xNullContent, nullptr, getFieldType(nType), ESelection()))); + } break; + case Type::CELLSTYLE: + xRet.set(static_cast<style::XStyle*>(new ScStyleObj( nullptr, SfxStyleFamily::Para, OUString() ))); + break; + case Type::PAGESTYLE: + xRet.set(static_cast<style::XStyle*>(new ScStyleObj( nullptr, SfxStyleFamily::Page, OUString() ))); + break; + case Type::GRAPHICSTYLE: + if (pDocShell) + { + pDocShell->MakeDrawLayer(); + xRet.set(static_cast<style::XStyle*>(new ScStyleObj( nullptr, SfxStyleFamily::Frame, OUString() ))); + } + break; + case Type::AUTOFORMAT: + xRet.set(static_cast<container::XIndexAccess*>(new ScAutoFormatObj( SC_AFMTOBJ_INVALID ))); + break; + case Type::AUTOFORMATS: + xRet.set(static_cast<container::XIndexAccess*>(new ScAutoFormatsObj())); + break; + case Type::CELLRANGES: + // isn't inserted, rather filled + // -> DocShell must be set, but empty ranges + if (pDocShell) + xRet.set(static_cast<sheet::XSheetCellRanges*>(new ScCellRangesObj( pDocShell, ScRangeList() ))); + break; + case Type::FUNCTIONDESCRIPTIONS: + xRet.set(static_cast<sheet::XFunctionDescriptions*>(new ScFunctionListObj())); + break; + case Type::GLOBALSHEETSETTINGS: + xRet.set(static_cast<sheet::XGlobalSheetSettings*>(new ScSpreadsheetSettings())); + break; + case Type::RECENTFUNCTIONS: + xRet.set(static_cast<sheet::XRecentFunctions*>(new ScRecentFunctionsObj())); + break; + case Type::DOCDEFLTS: + if (pDocShell) + xRet.set(static_cast<beans::XPropertySet*>(new ScDocDefaultsObj( pDocShell ))); + break; + case Type::DRAWDEFLTS: + if (pDocShell) + xRet.set(static_cast<beans::XPropertySet*>(new ScDrawDefaultsObj( pDocShell ))); + break; + + // Drawing layer tables are not in SvxUnoDrawMSFactory, + // because SvxUnoDrawMSFactory doesn't have a SdrModel pointer. + // Drawing layer is always allocated if not there (MakeDrawLayer). + + case Type::GRADTAB: + if (pDocShell) + xRet.set(SvxUnoGradientTable_createInstance( pDocShell->MakeDrawLayer() )); + break; + case Type::HATCHTAB: + if (pDocShell) + xRet.set(SvxUnoHatchTable_createInstance( pDocShell->MakeDrawLayer() )); + break; + case Type::BITMAPTAB: + if (pDocShell) + xRet.set(SvxUnoBitmapTable_createInstance( pDocShell->MakeDrawLayer() )); + break; + case Type::TRGRADTAB: + if (pDocShell) + xRet.set(SvxUnoTransGradientTable_createInstance( pDocShell->MakeDrawLayer() )); + break; + case Type::MARKERTAB: + if (pDocShell) + xRet.set(SvxUnoMarkerTable_createInstance( pDocShell->MakeDrawLayer() )); + break; + case Type::DASHTAB: + if (pDocShell) + xRet.set(SvxUnoDashTable_createInstance( pDocShell->MakeDrawLayer() )); + break; + case Type::NUMRULES: + if (pDocShell) + xRet.set(SvxCreateNumRule( pDocShell->MakeDrawLayer() )); + break; + case Type::DOCSPRSETT: + case Type::SHEETDOCSET: + case Type::DOCCONF: + if (pDocShell) + xRet.set(static_cast<beans::XPropertySet*>(new ScDocumentConfiguration(pDocShell))); + break; + case Type::IMAP_RECT: + xRet.set(SvUnoImageMapRectangleObject_createInstance( ScShapeObj::GetSupportedMacroItems() )); + break; + case Type::IMAP_CIRC: + xRet.set(SvUnoImageMapCircleObject_createInstance( ScShapeObj::GetSupportedMacroItems() )); + break; + case Type::IMAP_POLY: + xRet.set(SvUnoImageMapPolygonObject_createInstance( ScShapeObj::GetSupportedMacroItems() )); + break; + + // Support creation of GraphicStorageHandler and EmbeddedObjectResolver + case Type::EXPORT_GRAPHIC_STORAGE_HANDLER: + xRet.set(getXWeak(new SvXMLGraphicHelper( SvXMLGraphicHelperMode::Write ))); + break; + case Type::IMPORT_GRAPHIC_STORAGE_HANDLER: + xRet.set(getXWeak(new SvXMLGraphicHelper( SvXMLGraphicHelperMode::Read ))); + break; + case Type::EXPORT_EOR: + if (pDocShell) + xRet.set(getXWeak(new SvXMLEmbeddedObjectHelper( *pDocShell, SvXMLEmbeddedObjectHelperMode::Write ))); + break; + case Type::IMPORT_EOR: + if (pDocShell) + xRet.set(getXWeak(new SvXMLEmbeddedObjectHelper( *pDocShell, SvXMLEmbeddedObjectHelperMode::Read ))); + break; + case Type::VALBIND: + case Type::LISTCELLBIND: + if (pDocShell) + { + bool bListPos = ( nType == Type::LISTCELLBIND ); + uno::Reference<sheet::XSpreadsheetDocument> xDoc( pDocShell->GetBaseModel(), uno::UNO_QUERY ); + xRet.set(*new calc::OCellValueBinding( xDoc, bListPos )); + } + break; + case Type::LISTSOURCE: + if (pDocShell) + { + uno::Reference<sheet::XSpreadsheetDocument> xDoc( pDocShell->GetBaseModel(), uno::UNO_QUERY ); + xRet.set(*new calc::OCellListSource( xDoc )); + } + break; + case Type::CELLADDRESS: + case Type::RANGEADDRESS: + if (pDocShell) + { + bool bIsRange = ( nType == Type::RANGEADDRESS ); + xRet.set(*new ScAddressConversionObj( pDocShell, bIsRange )); + } + break; + case Type::CHDATAPROV: + if (pDocShell) + xRet = *new ScChart2DataProvider( &pDocShell->GetDocument() ); + break; + case Type::CHART_PIVOTTABLE_DATAPROVIDER: + if (pDocShell) + xRet = *new sc::PivotTableDataProvider(pDocShell->GetDocument()); + break; + case Type::FORMULAPARS: + if (pDocShell) + xRet.set(static_cast<sheet::XFormulaParser*>(new ScFormulaParserObj( pDocShell ))); + break; + case Type::OPCODEMAPPER: + if (pDocShell) + { + ScDocument& rDoc = pDocShell->GetDocument(); + ScAddress aAddress; + ScCompiler* pComp = new ScCompiler(rDoc, aAddress, rDoc.GetGrammar()); + xRet.set(static_cast<sheet::XFormulaOpCodeMapper*>(new ScFormulaOpCodeMapperObj(::std::unique_ptr<formula::FormulaCompiler> (pComp)))); + break; + } + break; +#if HAVE_FEATURE_SCRIPTING + case Type::VBAOBJECTPROVIDER: + if (pDocShell && pDocShell->GetDocument().IsInVBAMode()) + { + xRet.set(static_cast<container::XNameAccess*>(new ScVbaObjectForCodeNameProvider( pDocShell ))); + } + break; + case Type::VBACODENAMEPROVIDER: + if ( pDocShell && isInVBAMode( *pDocShell ) ) + { + xRet.set(static_cast<document::XCodeNameQuery*>(new ScVbaCodeNameProvider(*pDocShell))); + } + break; + case Type::VBAGLOBALS: + if (pDocShell) + { + uno::Any aGlobs; + if ( !pDocShell->GetBasicManager()->GetGlobalUNOConstant( "VBAGlobals", aGlobs ) ) + { + uno::Sequence< uno::Any > aArgs{ uno::Any(uno::Reference(static_cast<css::sheet::XSpreadsheetDocument*>(pDocShell->GetModel()))) }; + xRet = ::comphelper::getProcessServiceFactory()->createInstanceWithArguments( "ooo.vba.excel.Globals", aArgs ); + pDocShell->GetBasicManager()->SetGlobalUNOConstant( "VBAGlobals", uno::Any( xRet ) ); + BasicManager* pAppMgr = SfxApplication::GetBasicManager(); + if ( pAppMgr ) + pAppMgr->SetGlobalUNOConstant( "ThisExcelDoc", aArgs[ 0 ] ); + + // create the VBA document event processor + uno::Reference< script::vba::XVBAEventProcessor > xVbaEvents( + ::ooo::vba::createVBAUnoAPIServiceWithArgs( pDocShell, "com.sun.star.script.vba.VBASpreadsheetEventProcessor", aArgs ), uno::UNO_QUERY ); + pDocShell->GetDocument().SetVbaEventProcessor( xVbaEvents ); + } + } + break; +#endif + default: + break; + } + + return xRet; +} + +uno::Sequence<OUString> ScServiceProvider::GetAllServiceNames() +{ + const sal_uInt16 nEntries = SAL_N_ELEMENTS(aProvNamesId); + uno::Sequence<OUString> aRet(nEntries); + OUString* pArray = aRet.getArray(); + for (sal_uInt16 i = 0; i < nEntries; i++) + { + pArray[i] = aProvNamesId[i].pName; + } + return aRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/unoobj/shapeuno.cxx b/sc/source/ui/unoobj/shapeuno.cxx new file mode 100644 index 0000000000..5d1ecadb08 --- /dev/null +++ b/sc/source/ui/unoobj/shapeuno.cxx @@ -0,0 +1,1469 @@ +/* -*- 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 <sal/config.h> + +#include <comphelper/propertyvalue.hxx> +#include <comphelper/sequence.hxx> +#include <svtools/unoevent.hxx> +#include <svtools/unoimap.hxx> +#include <svx/svdobj.hxx> +#include <svx/ImageMapInfo.hxx> +#include <vcl/svapp.hxx> +#include <sfx2/event.hxx> +#include <editeng/unofield.hxx> +#include <toolkit/helper/convert.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <comphelper/diagnose_ex.hxx> + +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/drawing/XShape.hpp> +#include <com/sun/star/lang/NoSupportException.hpp> + +#include <shapeuno.hxx> +#include <cellsuno.hxx> +#include <textuno.hxx> +#include <fielduno.hxx> +#include <docsh.hxx> +#include <drwlayer.hxx> +#include <userdat.hxx> +#include <unonames.hxx> +#include <styleuno.hxx> + +using namespace ::com::sun::star; + +static std::span<const SfxItemPropertyMapEntry> lcl_GetShapeMap() +{ + static const SfxItemPropertyMapEntry aShapeMap_Impl[] = + { + { SC_UNONAME_ANCHOR, 0, cppu::UnoType<uno::XInterface>::get(), 0, 0 }, + { SC_UNONAME_RESIZE_WITH_CELL, 0, cppu::UnoType<sal_Bool>::get(), 0, 0 }, + { SC_UNONAME_HORIPOS, 0, cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { SC_UNONAME_IMAGEMAP, 0, cppu::UnoType<container::XIndexContainer>::get(), 0, 0 }, + { SC_UNONAME_VERTPOS, 0, cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { SC_UNONAME_MOVEPROTECT, 0, cppu::UnoType<sal_Bool>::get(), 0, 0 }, + { SC_UNONAME_HYPERLINK, 0, cppu::UnoType<OUString>::get(), 0, 0 }, + { SC_UNONAME_URL, 0, cppu::UnoType<OUString>::get(), 0, 0 }, + { SC_UNONAME_STYLE, 0, cppu::UnoType<style::XStyle>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 }, + }; + return aShapeMap_Impl; +} + +const SvEventDescription* ScShapeObj::GetSupportedMacroItems() +{ + static const SvEventDescription aMacroDescriptionsImpl[] = + { + { SvMacroItemId::NONE, nullptr } + }; + return aMacroDescriptionsImpl; +} +ScMacroInfo* ScShapeObj_getShapeHyperMacroInfo( const ScShapeObj* pShape, bool bCreate = false ) +{ + if( pShape ) + if( SdrObject* pObj = pShape->GetSdrObject() ) + return ScDrawLayer::GetMacroInfo( pObj, bCreate ); + return nullptr; +} + +ScShapeObj::ScShapeObj( uno::Reference<drawing::XShape>& xShape ) : + pShapePropertySet(nullptr), + pShapePropertyState(nullptr), + bIsTextShape(false), + bIsNoteCaption(false) +{ + osl_atomic_increment( &m_refCount ); + + { + mxShapeAgg.set( xShape, uno::UNO_QUERY ); + // extra block to force deletion of the temporary before setDelegator + } + + if (mxShapeAgg.is()) + { + xShape = nullptr; // during setDelegator, mxShapeAgg must be the only ref + + mxShapeAgg->setDelegator( getXWeak() ); + + xShape.set(uno::Reference<drawing::XShape>( mxShapeAgg, uno::UNO_QUERY )); + + bIsTextShape = ( comphelper::getFromUnoTunnel<SvxUnoTextBase>( mxShapeAgg ) != nullptr ); + } + + { + SdrObject* pObj = GetSdrObject(); + if ( pObj ) + { + bIsNoteCaption = ScDrawLayer::IsNoteCaption( pObj ); + } + } + + osl_atomic_decrement( &m_refCount ); +} + +ScShapeObj::~ScShapeObj() +{ +// if (mxShapeAgg.is()) +// mxShapeAgg->setDelegator(uno::Reference<uno::XInterface>()); +} + +// XInterface + +uno::Any SAL_CALL ScShapeObj::queryInterface( const uno::Type& rType ) +{ + uno::Any aRet = ScShapeObj_Base::queryInterface( rType ); + + if ( !aRet.hasValue() && bIsTextShape ) + aRet = ScShapeObj_TextBase::queryInterface( rType ); + + if ( !aRet.hasValue() && bIsNoteCaption ) + aRet = ScShapeObj_ChildBase::queryInterface( rType ); + + if ( !aRet.hasValue() && mxShapeAgg.is() ) + aRet = mxShapeAgg->queryAggregation( rType ); + + return aRet; +} + +void SAL_CALL ScShapeObj::acquire() noexcept +{ + OWeakObject::acquire(); +} + +void SAL_CALL ScShapeObj::release() noexcept +{ + OWeakObject::release(); +} + +void ScShapeObj::GetShapePropertySet() +{ + // #i61908# Store the result of queryAggregation in a member. + // The reference in mxShapeAgg is kept for this object's lifetime, so the pointer is always valid. + + if (!pShapePropertySet) + { + uno::Reference<beans::XPropertySet> xProp; + if ( mxShapeAgg.is() ) + mxShapeAgg->queryAggregation( cppu::UnoType<beans::XPropertySet>::get()) >>= xProp; + pShapePropertySet = xProp.get(); + } +} + +void ScShapeObj::GetShapePropertyState() +{ + // #i61908# Store the result of queryAggregation in a member. + // The reference in mxShapeAgg is kept for this object's lifetime, so the pointer is always valid. + + if (!pShapePropertyState) + { + uno::Reference<beans::XPropertyState> xState; + if ( mxShapeAgg.is() ) + mxShapeAgg->queryAggregation( cppu::UnoType<beans::XPropertyState>::get()) >>= xState; + pShapePropertyState = xState.get(); + } +} + +static uno::Reference<lang::XComponent> lcl_GetComponent( const uno::Reference<uno::XAggregation>& xAgg ) +{ + uno::Reference<lang::XComponent> xRet; + if ( xAgg.is() ) + xAgg->queryAggregation( cppu::UnoType<lang::XComponent>::get()) >>= xRet; + return xRet; +} + +static uno::Reference<text::XText> lcl_GetText( const uno::Reference<uno::XAggregation>& xAgg ) +{ + uno::Reference<text::XText> xRet; + if ( xAgg.is() ) + xAgg->queryAggregation( cppu::UnoType<text::XText>::get()) >>= xRet; + return xRet; +} + +static uno::Reference<text::XSimpleText> lcl_GetSimpleText( const uno::Reference<uno::XAggregation>& xAgg ) +{ + uno::Reference<text::XSimpleText> xRet; + if ( xAgg.is() ) + xAgg->queryAggregation( cppu::UnoType<text::XSimpleText>::get()) >>= xRet; + return xRet; +} + +static uno::Reference<text::XTextRange> lcl_GetTextRange( const uno::Reference<uno::XAggregation>& xAgg ) +{ + uno::Reference<text::XTextRange> xRet; + if ( xAgg.is() ) + xAgg->queryAggregation( cppu::UnoType<text::XTextRange>::get()) >>= xRet; + return xRet; +} + +// XPropertySet + +uno::Reference<beans::XPropertySetInfo> SAL_CALL ScShapeObj::getPropertySetInfo() +{ + SolarMutexGuard aGuard; + + // #i61527# cache property set info for this object + if ( !mxPropSetInfo.is() ) + { + // mix own and aggregated properties: + GetShapePropertySet(); + if (pShapePropertySet) + { + uno::Reference<beans::XPropertySetInfo> xAggInfo(pShapePropertySet->getPropertySetInfo()); + const uno::Sequence<beans::Property> aPropSeq(xAggInfo->getProperties()); + mxPropSetInfo.set(new SfxExtItemPropertySetInfo( lcl_GetShapeMap(), aPropSeq )); + } + } + return mxPropSetInfo; +} + +static bool lcl_GetPageNum( const SdrPage* pPage, SdrModel& rModel, SCTAB& rNum ) +{ + sal_uInt16 nCount = rModel.GetPageCount(); + for (sal_uInt16 i=0; i<nCount; i++) + if ( rModel.GetPage(i) == pPage ) + { + rNum = static_cast<SCTAB>(i); + return true; + } + + return false; +} + +static bool lcl_GetCaptionPoint( const uno::Reference< drawing::XShape >& xShape, awt::Point& rCaptionPoint ) +{ + bool bReturn = false; + OUString sType(xShape->getShapeType()); + bool bCaptionShape( sType == "com.sun.star.drawing.CaptionShape" ); + if (bCaptionShape) + { + uno::Reference < beans::XPropertySet > xShapeProp (xShape, uno::UNO_QUERY); + if (xShapeProp.is()) + { + xShapeProp->getPropertyValue("CaptionPoint") >>= rCaptionPoint; + bReturn = true; + } + } + return bReturn; +} + +static ScRange lcl_GetAnchorCell( const uno::Reference< drawing::XShape >& xShape, const ScDocument* pDoc, SCTAB nTab, + awt::Point& rUnoPoint, awt::Size& rUnoSize, awt::Point& rCaptionPoint ) +{ + ScRange aReturn; + rUnoPoint = xShape->getPosition(); + bool bCaptionShape(lcl_GetCaptionPoint(xShape, rCaptionPoint)); + if (pDoc->IsNegativePage(nTab)) + { + rUnoSize = xShape->getSize(); + rUnoPoint.X += rUnoSize.Width; // the right top point is base + if (bCaptionShape) + { + if (rCaptionPoint.X > 0 && rCaptionPoint.X > rUnoSize.Width) + rUnoPoint.X += rCaptionPoint.X - rUnoSize.Width; + if (rCaptionPoint.Y < 0) + rUnoPoint.Y += rCaptionPoint.Y; + } + aReturn = pDoc->GetRange( nTab, tools::Rectangle( VCLPoint(rUnoPoint), VCLPoint(rUnoPoint) )); + } + else + { + if (bCaptionShape) + { + if (rCaptionPoint.X < 0) + rUnoPoint.X += rCaptionPoint.X; + if (rCaptionPoint.Y < 0) + rUnoPoint.Y += rCaptionPoint.Y; + } + aReturn = pDoc->GetRange( nTab, tools::Rectangle( VCLPoint(rUnoPoint), VCLPoint(rUnoPoint) )); + } + + return aReturn; +} + +static awt::Point lcl_GetRelativePos( const uno::Reference< drawing::XShape >& xShape, const ScDocument* pDoc, SCTAB nTab, ScRange& rRange, + awt::Size& rUnoSize, awt::Point& rCaptionPoint) +{ + awt::Point aUnoPoint; + rRange = lcl_GetAnchorCell(xShape, pDoc, nTab, aUnoPoint, rUnoSize, rCaptionPoint); + tools::Rectangle aRect(pDoc->GetMMRect( rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row(), rRange.aStart.Tab() )); + Point aPoint = pDoc->IsNegativePage(nTab) ? aRect.TopRight() : aRect.TopLeft(); + aUnoPoint.X -= aPoint.X(); + aUnoPoint.Y -= aPoint.Y(); + return aUnoPoint; +} + +void SAL_CALL ScShapeObj::setPropertyValue(const OUString& aPropertyName, const uno::Any& aValue) +{ + SolarMutexGuard aGuard; + + if ( aPropertyName == SC_UNONAME_ANCHOR ) + { + uno::Reference<sheet::XCellRangeAddressable> xRangeAdd(aValue, uno::UNO_QUERY); + if (!xRangeAdd.is()) + throw lang::IllegalArgumentException("only XCell or XSpreadsheet objects allowed", getXWeak(), 0); + + SdrObject *pObj = GetSdrObject(); + if (pObj) + { + ScDrawLayer& rModel(static_cast< ScDrawLayer& >(pObj->getSdrModelFromSdrObject())); + SdrPage* pPage(pObj->getSdrPageFromSdrObject()); + + if ( pPage ) + { + ScDocument* pDoc(rModel.GetDocument()); + + if ( pDoc ) + { + if ( ScDocShell* pDocSh = pDoc->GetDocumentShell() ) + { + SCTAB nTab = 0; + if ( lcl_GetPageNum( pPage, rModel, nTab ) ) + { + table::CellRangeAddress aAddress = xRangeAdd->getRangeAddress(); + if (nTab == aAddress.Sheet) + { + tools::Rectangle aRect(pDoc->GetMMRect( static_cast<SCCOL>(aAddress.StartColumn), static_cast<SCROW>(aAddress.StartRow), + static_cast<SCCOL>(aAddress.EndColumn), static_cast<SCROW>(aAddress.EndRow), aAddress.Sheet )); + awt::Point aRelPoint; + uno::Reference<drawing::XShape> xShape( mxShapeAgg, uno::UNO_QUERY ); + if (xShape.is()) + { + Point aPoint; + Point aEndPoint; + if (pDoc->IsNegativePage(nTab)) + { + aPoint = aRect.TopRight(); + aEndPoint = aRect.BottomLeft(); + } + else + { + aPoint = aRect.TopLeft(); + aEndPoint = aRect.BottomRight(); + } + awt::Size aUnoSize; + awt::Point aCaptionPoint; + ScRange aRange; + aRelPoint = lcl_GetRelativePos( xShape, pDoc, nTab, aRange, aUnoSize, aCaptionPoint ); + awt::Point aUnoPoint(aRelPoint); + + aUnoPoint.X += aPoint.X(); + aUnoPoint.Y += aPoint.Y(); + + if ( aUnoPoint.Y > aEndPoint.Y() ) + aUnoPoint.Y = aEndPoint.Y() - 2; + if (pDoc->IsNegativePage(nTab)) + { + if ( aUnoPoint.X < aEndPoint.X() ) + aUnoPoint.X = aEndPoint.X() + 2; + aUnoPoint.X -= aUnoSize.Width; + // remove difference to caption point + if (aCaptionPoint.X > 0 && aCaptionPoint.X > aUnoSize.Width) + aUnoPoint.X -= aCaptionPoint.X - aUnoSize.Width; + } + else + { + if ( aUnoPoint.X > aEndPoint.X() ) + aUnoPoint.X = aEndPoint.X() - 2; + if (aCaptionPoint.X < 0) + aUnoPoint.X -= aCaptionPoint.X; + } + if (aCaptionPoint.Y < 0) + aUnoPoint.Y -= aCaptionPoint.Y; + + xShape->setPosition(aUnoPoint); + pDocSh->SetModified(); + } + + if (aAddress.StartRow != aAddress.EndRow) //should be a Spreadsheet + { + OSL_ENSURE(aAddress.StartRow == 0 && aAddress.EndRow == pDoc->MaxRow() && + aAddress.StartColumn == 0 && aAddress.EndColumn == pDoc->MaxCol(), "here should be a XSpreadsheet"); + ScDrawLayer::SetPageAnchored(*pObj); + } + else + { + OSL_ENSURE(aAddress.StartRow == aAddress.EndRow && + aAddress.StartColumn == aAddress.EndColumn, "here should be a XCell"); + ScDrawObjData aAnchor; + aAnchor.maStart = ScAddress(aAddress.StartColumn, aAddress.StartRow, aAddress.Sheet); + aAnchor.maStartOffset = Point(aRelPoint.X, aRelPoint.Y); + ScDrawObjData* pDrawObjData = ScDrawLayer::GetObjData(pObj); + if (pDrawObjData) + aAnchor.mbResizeWithCell = pDrawObjData->mbResizeWithCell; + //Uno sets the Anchor in terms of the unrotated shape, not much we can do + //about that since uno also displays the shape geometry in terms of the unrotated + //shape. #TODO think about changing the anchoring behaviour here too + //Currently we've only got a start anchor, not an end-anchor, so generate that now + ScDrawLayer::UpdateCellAnchorFromPositionEnd(*pObj, aAnchor, *pDoc, aAddress.Sheet); + ScDrawLayer::SetCellAnchored(*pObj, aAnchor); + } + } + } + } + } + } + } + + } + else if ( aPropertyName == SC_UNONAME_RESIZE_WITH_CELL ) + { + SdrObject* pObj = GetSdrObject(); + if (!pObj) + return; + ScAnchorType aAnchorType = ScDrawLayer::GetAnchorType(*pObj); + + // Nothing to do if anchored to page + if (aAnchorType == SCA_PAGE) + return; + + ScDrawObjData* pDrawObjData = ScDrawLayer::GetObjData(pObj); + if (!pDrawObjData) + return; + + aValue >>= pDrawObjData->mbResizeWithCell; + ScDrawLayer::SetCellAnchored(*pObj, *pDrawObjData); + } + else if ( aPropertyName == SC_UNONAME_IMAGEMAP ) + { + SdrObject* pObj = GetSdrObject(); + if ( pObj ) + { + ImageMap aImageMap; + uno::Reference< uno::XInterface > xImageMapInt(aValue, uno::UNO_QUERY); + + if( !xImageMapInt.is() || !SvUnoImageMap_fillImageMap( xImageMapInt, aImageMap ) ) + throw lang::IllegalArgumentException(); + + SvxIMapInfo* pIMapInfo = SvxIMapInfo::GetIMapInfo(pObj); + if( pIMapInfo ) + { + // replace existing image map + pIMapInfo->SetImageMap( aImageMap ); + } + else + { + // insert new user data with image map + pObj->AppendUserData(std::unique_ptr<SdrObjUserData>(new SvxIMapInfo(aImageMap) )); + } + } + } + else if ( aPropertyName == SC_UNONAME_HORIPOS ) + { + sal_Int32 nPos = 0; + if (aValue >>= nPos) + { + SdrObject *pObj = GetSdrObject(); + if (pObj) + { + ScDrawLayer& rModel(static_cast< ScDrawLayer& >(pObj->getSdrModelFromSdrObject())); + SdrPage* pPage(pObj->getSdrPageFromSdrObject()); + + if ( pPage ) + { + SCTAB nTab = 0; + if ( lcl_GetPageNum( pPage, rModel, nTab ) ) + { + ScDocument* pDoc = rModel.GetDocument(); + if ( pDoc ) + { + if ( ScDocShell* pDocSh = pDoc->GetDocumentShell() ) + { + uno::Reference<drawing::XShape> xShape( mxShapeAgg, uno::UNO_QUERY ); + if (xShape.is()) + { + if (ScDrawLayer::GetAnchorType(*pObj) == SCA_PAGE) + { + awt::Point aPoint(xShape->getPosition()); + awt::Size aSize(xShape->getSize()); + awt::Point aCaptionPoint; + if (pDoc->IsNegativePage(nTab)) + { + nPos *= -1; + nPos -= aSize.Width; + } + if (lcl_GetCaptionPoint(xShape, aCaptionPoint)) + { + if (pDoc->IsNegativePage(nTab)) + { + if (aCaptionPoint.X > 0 && aCaptionPoint.X > aSize.Width) + nPos -= aCaptionPoint.X - aSize.Width; + } + else + { + if (aCaptionPoint.X < 0) + nPos -= aCaptionPoint.X; + } + } + aPoint.X = nPos; + xShape->setPosition(aPoint); + pDocSh->SetModified(); + } + else if (ScDrawLayer::GetAnchorType(*pObj) == SCA_CELL + || ScDrawLayer::GetAnchorType(*pObj) + == SCA_CELL_RESIZE) + { + awt::Size aUnoSize; + awt::Point aCaptionPoint; + ScRange aRange; + awt::Point aUnoPoint(lcl_GetRelativePos( xShape, pDoc, nTab, aRange, aUnoSize, aCaptionPoint )); + tools::Rectangle aRect(pDoc->GetMMRect( aRange.aStart.Col(), aRange.aStart.Row(), aRange.aEnd.Col(), aRange.aEnd.Row(), aRange.aStart.Tab() )); + if (pDoc->IsNegativePage(nTab)) + { + aUnoPoint.X = -nPos; + Point aPoint(aRect.TopRight()); + Point aEndPoint(aRect.BottomLeft()); + aUnoPoint.X += aPoint.X(); + if (aUnoPoint.X < aEndPoint.X()) + aUnoPoint.X = aEndPoint.X() + 2; + aUnoPoint.X -= aUnoSize.Width; + if (aCaptionPoint.X > 0 && aCaptionPoint.X > aUnoSize.Width) + aUnoPoint.X -= aCaptionPoint.X - aUnoSize.Width; + } + else + { + aUnoPoint.X = nPos; + Point aPoint(aRect.TopLeft()); + Point aEndPoint(aRect.BottomRight()); + aUnoPoint.X += aPoint.X(); + if (aUnoPoint.X > aEndPoint.X()) + aUnoPoint.X = aEndPoint.X() - 2; + if (aCaptionPoint.X < 0) + aUnoPoint.X -= aCaptionPoint.X; + } + aUnoPoint.Y = xShape->getPosition().Y; + xShape->setPosition(aUnoPoint); + pDocSh->SetModified(); + } + else + { + OSL_FAIL("unknown anchor type"); + } + } + } + } + } + } + } + } + } + else if ( aPropertyName == SC_UNONAME_VERTPOS ) + { + sal_Int32 nPos = 0; + if (aValue >>= nPos) + { + SdrObject *pObj = GetSdrObject(); + if (pObj) + { + ScDrawLayer& rModel(static_cast< ScDrawLayer& >(pObj->getSdrModelFromSdrObject())); + SdrPage* pPage(pObj->getSdrPageFromSdrObject()); + + if ( pPage ) + { + SCTAB nTab = 0; + if ( lcl_GetPageNum( pPage, rModel, nTab ) ) + { + ScDocument* pDoc = rModel.GetDocument(); + if ( pDoc ) + { + if ( ScDocShell* pDocSh = pDoc->GetDocumentShell() ) + { + uno::Reference<drawing::XShape> xShape( mxShapeAgg, uno::UNO_QUERY ); + if (xShape.is()) + { + if (ScDrawLayer::GetAnchorType(*pObj) == SCA_PAGE) + { + awt::Point aPoint = xShape->getPosition(); + awt::Point aCaptionPoint; + if (lcl_GetCaptionPoint(xShape, aCaptionPoint)) + { + if (aCaptionPoint.Y < 0) + nPos -= aCaptionPoint.Y; + } + aPoint.Y = nPos; + xShape->setPosition(aPoint); + pDocSh->SetModified(); + } + else if (ScDrawLayer::GetAnchorType(*pObj) == SCA_CELL + || ScDrawLayer::GetAnchorType(*pObj) + == SCA_CELL_RESIZE) + { + awt::Size aUnoSize; + awt::Point aCaptionPoint; + ScRange aRange; + awt::Point aUnoPoint(lcl_GetRelativePos( xShape, pDoc, nTab, aRange, aUnoSize, aCaptionPoint )); + tools::Rectangle aRect(pDoc->GetMMRect( aRange.aStart.Col(), aRange.aStart.Row(), aRange.aEnd.Col(), aRange.aEnd.Row(), aRange.aStart.Tab() )); + Point aPoint(aRect.TopRight()); + Point aEndPoint(aRect.BottomLeft()); + aUnoPoint.Y = nPos; + aUnoPoint.Y += aPoint.Y(); + if (aUnoPoint.Y > aEndPoint.Y()) + aUnoPoint.Y = aEndPoint.Y() - 2; + if (aCaptionPoint.Y < 0) + aUnoPoint.Y -= aCaptionPoint.Y; + aUnoPoint.X = xShape->getPosition().X; + xShape->setPosition(aUnoPoint); + pDocSh->SetModified(); + } + else + { + OSL_FAIL("unknown anchor type"); + } + } + } + } + } + } + } + } + } + else if ( aPropertyName == SC_UNONAME_HYPERLINK || + aPropertyName == SC_UNONAME_URL ) + { + OUString sHyperlink; + SdrObject* pObj = GetSdrObject(); + if (pObj && (aValue >>= sHyperlink)) + pObj->setHyperlink(sHyperlink); + } + else if ( aPropertyName == SC_UNONAME_MOVEPROTECT ) + { + if( SdrObject* pObj = GetSdrObject() ) + { + bool aProt = false; + if( aValue >>= aProt ) + pObj->SetMoveProtect( aProt ); + } + } + else if ( aPropertyName == SC_UNONAME_STYLE ) + { + if (SdrObject* pObj = GetSdrObject()) + { + uno::Reference<style::XStyle> xStyle(aValue, uno::UNO_QUERY); + auto pStyleSheetObj = dynamic_cast<ScStyleObj*>(xStyle.get()); + if (!pStyleSheetObj) + throw lang::IllegalArgumentException(); + + auto pStyleSheet = pStyleSheetObj->GetStyle_Impl(); + auto pOldStyleSheet = pObj->GetStyleSheet(); + + if (pStyleSheet != pOldStyleSheet) + pObj->SetStyleSheet(static_cast<SfxStyleSheet*>(pStyleSheet), false); + } + } + else + { + GetShapePropertySet(); + if (pShapePropertySet) + pShapePropertySet->setPropertyValue( aPropertyName, aValue ); + } +} + +uno::Any SAL_CALL ScShapeObj::getPropertyValue( const OUString& aPropertyName ) +{ + SolarMutexGuard aGuard; + + uno::Any aAny; + if ( aPropertyName == SC_UNONAME_ANCHOR ) + { + SdrObject *pObj = GetSdrObject(); + if (pObj) + { + ScDrawLayer& rModel(static_cast< ScDrawLayer& >(pObj->getSdrModelFromSdrObject())); + SdrPage* pPage(pObj->getSdrPageFromSdrObject()); + + if ( pPage ) + { + ScDocument* pDoc = rModel.GetDocument(); + if ( pDoc ) + { + SCTAB nTab = 0; + if ( lcl_GetPageNum( pPage, rModel, nTab ) ) + { + if ( ScDocShell* pDocSh = pDoc->GetDocumentShell() ) + { + uno::Reference< uno::XInterface > xAnchor; + if (ScDrawObjData *pAnchor = ScDrawLayer::GetObjDataTab(pObj, nTab)) + xAnchor.set(cppu::getXWeak(new ScCellObj( pDocSh, pAnchor->maStart))); + else + xAnchor.set(cppu::getXWeak(new ScTableSheetObj( pDocSh, nTab ))); + aAny <<= xAnchor; + } + } + } + } + } + } + else if (aPropertyName == SC_UNONAME_RESIZE_WITH_CELL) + { + bool bIsResizeWithCell = false; + SdrObject* pObj = GetSdrObject(); + if (pObj) + { + ScAnchorType anchorType = ScDrawLayer::GetAnchorType(*pObj); + bIsResizeWithCell = (anchorType == SCA_CELL_RESIZE); + } + aAny <<= bIsResizeWithCell; + } + else if ( aPropertyName == SC_UNONAME_IMAGEMAP ) + { + uno::Reference< uno::XInterface > xImageMap; + SdrObject* pObj = GetSdrObject(); + if ( pObj ) + { + SvxIMapInfo* pIMapInfo = SvxIMapInfo::GetIMapInfo(GetSdrObject()); + if( pIMapInfo ) + { + const ImageMap& rIMap = pIMapInfo->GetImageMap(); + xImageMap.set(SvUnoImageMap_createInstance( rIMap, GetSupportedMacroItems() )); + } + else + xImageMap = SvUnoImageMap_createInstance(); + } + aAny <<= uno::Reference< container::XIndexContainer >::query( xImageMap ); + } + else if ( aPropertyName == SC_UNONAME_HORIPOS ) + { + SdrObject *pObj = GetSdrObject(); + if (pObj) + { + ScDrawLayer& rModel(static_cast< ScDrawLayer& >(pObj->getSdrModelFromSdrObject())); + SdrPage* pPage(pObj->getSdrPageFromSdrObject()); + + if ( pPage ) + { + ScDocument* pDoc = rModel.GetDocument(); + if ( pDoc ) + { + SCTAB nTab = 0; + if ( lcl_GetPageNum( pPage, rModel, nTab ) ) + { + uno::Reference<drawing::XShape> xShape( mxShapeAgg, uno::UNO_QUERY ); + if (xShape.is()) + { + if (ScDrawLayer::GetAnchorType(*pObj) == SCA_CELL + || ScDrawLayer::GetAnchorType(*pObj) == SCA_CELL_RESIZE) + { + awt::Size aUnoSize; + awt::Point aCaptionPoint; + ScRange aRange; + awt::Point aUnoPoint(lcl_GetRelativePos( xShape, pDoc, nTab, aRange, aUnoSize, aCaptionPoint )); + if (pDoc->IsNegativePage(nTab)) + aUnoPoint.X *= -1; + aAny <<= aUnoPoint.X; + } + else + { + awt::Point aCaptionPoint; + awt::Point aUnoPoint(xShape->getPosition()); + awt::Size aUnoSize(xShape->getSize()); + if (pDoc->IsNegativePage(nTab)) + { + aUnoPoint.X *= -1; + aUnoPoint.X -= aUnoSize.Width; + } + if (lcl_GetCaptionPoint(xShape, aCaptionPoint)) + { + if (pDoc->IsNegativePage(nTab)) + { + if (aCaptionPoint.X > 0 && aCaptionPoint.X > aUnoSize.Width) + aUnoPoint.X -= aCaptionPoint.X - aUnoSize.Width; + } + else + { + if (aCaptionPoint.X < 0) + aUnoPoint.X += aCaptionPoint.X; + } + } + aAny <<= aUnoPoint.X; + } + } + } + } + } + } + } + else if ( aPropertyName == SC_UNONAME_VERTPOS ) + { + SdrObject *pObj = GetSdrObject(); + if (pObj) + { + ScDrawLayer& rModel(static_cast< ScDrawLayer& >(pObj->getSdrModelFromSdrObject())); + SdrPage* pPage(pObj->getSdrPageFromSdrObject()); + + if ( pPage ) + { + ScDocument* pDoc = rModel.GetDocument(); + if ( pDoc ) + { + SCTAB nTab = 0; + if ( lcl_GetPageNum( pPage, rModel, nTab ) ) + { + uno::Reference<drawing::XShape> xShape( mxShapeAgg, uno::UNO_QUERY ); + if (xShape.is()) + { + if (ScDrawLayer::GetAnchorType(*pObj) == SCA_CELL + || ScDrawLayer::GetAnchorType(*pObj) == SCA_CELL_RESIZE) + { + awt::Size aUnoSize; + awt::Point aCaptionPoint; + ScRange aRange; + awt::Point aUnoPoint(lcl_GetRelativePos( xShape, pDoc, nTab, aRange, aUnoSize, aCaptionPoint )); + + aAny <<= aUnoPoint.Y; + } + else + { + awt::Point aUnoPoint(xShape->getPosition()); + awt::Point aCaptionPoint; + if (lcl_GetCaptionPoint(xShape, aCaptionPoint)) + { + if (aCaptionPoint.Y < 0) + aUnoPoint.Y += aCaptionPoint.Y; + } + aAny <<= aUnoPoint.Y; + } + } + } + } + } + } + } + else if ( aPropertyName == SC_UNONAME_HYPERLINK || + aPropertyName == SC_UNONAME_URL ) + { + OUString sHlink; + if (SdrObject* pObj = GetSdrObject()) + sHlink = pObj->getHyperlink(); + aAny <<= sHlink; + } + else if ( aPropertyName == SC_UNONAME_MOVEPROTECT ) + { + bool aProt = false; + if ( SdrObject* pObj = GetSdrObject() ) + aProt = pObj->IsMoveProtect(); + aAny <<= aProt; + } + else if ( aPropertyName == SC_UNONAME_STYLE ) + { + if (SdrObject* pObj = GetSdrObject()) + { + if (auto pStyleSheet = pObj->GetStyleSheet()) + { + ScDrawLayer& rModel(static_cast< ScDrawLayer& >(pObj->getSdrModelFromSdrObject())); + ScDocument* pDoc = rModel.GetDocument(); + aAny <<= uno::Reference<style::XStyle>(new ScStyleObj( + pDoc ? pDoc->GetDocumentShell() : nullptr, + SfxStyleFamily::Frame, pStyleSheet->GetName())); + } + } + } + else + { + if(!pShapePropertySet) //performance consideration + GetShapePropertySet(); + if (pShapePropertySet) + aAny = pShapePropertySet->getPropertyValue( aPropertyName ); + } + + return aAny; +} + +void SAL_CALL ScShapeObj::addPropertyChangeListener( const OUString& aPropertyName, + const uno::Reference<beans::XPropertyChangeListener>& aListener) +{ + SolarMutexGuard aGuard; + + GetShapePropertySet(); + if (pShapePropertySet) + pShapePropertySet->addPropertyChangeListener( aPropertyName, aListener ); +} + +void SAL_CALL ScShapeObj::removePropertyChangeListener( const OUString& aPropertyName, + const uno::Reference<beans::XPropertyChangeListener>& aListener) +{ + SolarMutexGuard aGuard; + + GetShapePropertySet(); + if (pShapePropertySet) + pShapePropertySet->removePropertyChangeListener( aPropertyName, aListener ); +} + +void SAL_CALL ScShapeObj::addVetoableChangeListener( const OUString& aPropertyName, + const uno::Reference<beans::XVetoableChangeListener>& aListener) +{ + SolarMutexGuard aGuard; + + GetShapePropertySet(); + if (pShapePropertySet) + pShapePropertySet->addVetoableChangeListener( aPropertyName, aListener ); +} + +void SAL_CALL ScShapeObj::removeVetoableChangeListener( const OUString& aPropertyName, + const uno::Reference<beans::XVetoableChangeListener>& aListener) +{ + SolarMutexGuard aGuard; + + GetShapePropertySet(); + if (pShapePropertySet) + pShapePropertySet->removeVetoableChangeListener( aPropertyName, aListener ); +} + +// XPropertyState + +beans::PropertyState SAL_CALL ScShapeObj::getPropertyState( const OUString& aPropertyName ) +{ + SolarMutexGuard aGuard; + + beans::PropertyState eRet = beans::PropertyState_DIRECT_VALUE; + if ( aPropertyName == SC_UNONAME_IMAGEMAP ) + { + // ImageMap is always "direct" + } + else if ( aPropertyName == SC_UNONAME_ANCHOR ) + { + // Anchor is always "direct" + } + else if ( aPropertyName == SC_UNONAME_HORIPOS ) + { + // HoriPos is always "direct" + } + else if ( aPropertyName == SC_UNONAME_VERTPOS ) + { + // VertPos is always "direct" + } + else + { + GetShapePropertyState(); + if (pShapePropertyState) + eRet = pShapePropertyState->getPropertyState( aPropertyName ); + } + + return eRet; +} + +uno::Sequence<beans::PropertyState> SAL_CALL ScShapeObj::getPropertyStates( + const uno::Sequence<OUString>& aPropertyNames ) +{ + SolarMutexGuard aGuard; + + // simple loop to get own and aggregated states + + uno::Sequence<beans::PropertyState> aRet(aPropertyNames.getLength()); + std::transform(aPropertyNames.begin(), aPropertyNames.end(), aRet.getArray(), + [this](const OUString& rName) -> beans::PropertyState { return getPropertyState(rName); }); + return aRet; +} + +void SAL_CALL ScShapeObj::setPropertyToDefault( const OUString& aPropertyName ) +{ + SolarMutexGuard aGuard; + + if ( aPropertyName == SC_UNONAME_IMAGEMAP ) + { + SdrObject* pObj = GetSdrObject(); + if ( pObj ) + { + SvxIMapInfo* pIMapInfo = SvxIMapInfo::GetIMapInfo(pObj); + if( pIMapInfo ) + { + ImageMap aEmpty; + pIMapInfo->SetImageMap( aEmpty ); // replace with empty image map + } + else + { + // nothing to do (no need to insert user data for an empty map) + } + } + } + else + { + GetShapePropertyState(); + if (pShapePropertyState) + pShapePropertyState->setPropertyToDefault( aPropertyName ); + } +} + +uno::Any SAL_CALL ScShapeObj::getPropertyDefault( const OUString& aPropertyName ) +{ + SolarMutexGuard aGuard; + + uno::Any aAny; + if ( aPropertyName == SC_UNONAME_IMAGEMAP ) + { + // default: empty ImageMap + uno::Reference< uno::XInterface > xImageMap(SvUnoImageMap_createInstance()); + aAny <<= uno::Reference< container::XIndexContainer >::query( xImageMap ); + } + else + { + GetShapePropertyState(); + if (pShapePropertyState) + aAny = pShapePropertyState->getPropertyDefault( aPropertyName ); + } + + return aAny; +} + +// XTextContent + +void SAL_CALL ScShapeObj::attach( const uno::Reference<text::XTextRange>& /* xTextRange */ ) +{ + throw lang::IllegalArgumentException(); // anchor cannot be changed +} + +uno::Reference<text::XTextRange> SAL_CALL ScShapeObj::getAnchor() +{ + SolarMutexGuard aGuard; + + uno::Reference<text::XTextRange> xRet; + + SdrObject* pObj = GetSdrObject(); + if( pObj ) + { + ScDrawLayer& rModel(static_cast< ScDrawLayer& >(pObj->getSdrModelFromSdrObject())); + SdrPage* pPage(pObj->getSdrPageFromSdrObject()); + ScDocument* pDoc = rModel.GetDocument(); + + if ( pPage && pDoc ) + { + if ( ScDocShell* pDocSh = pDoc->GetDocumentShell() ) + { + SCTAB nTab = 0; + if ( lcl_GetPageNum( pPage, rModel, nTab ) ) + { + Point aPos(pObj->GetCurrentBoundRect().TopLeft()); + ScRange aRange(pDoc->GetRange( nTab, tools::Rectangle( aPos, aPos ) )); + + // anchor is always the cell + + xRet.set(new ScCellObj( pDocSh, aRange.aStart )); + } + } + } + } + + return xRet; +} + +// XComponent + +void SAL_CALL ScShapeObj::dispose() +{ + SolarMutexGuard aGuard; + + uno::Reference<lang::XComponent> xAggComp(lcl_GetComponent(mxShapeAgg)); + if ( xAggComp.is() ) + xAggComp->dispose(); +} + +void SAL_CALL ScShapeObj::addEventListener( + const uno::Reference<lang::XEventListener>& xListener ) +{ + SolarMutexGuard aGuard; + + uno::Reference<lang::XComponent> xAggComp(lcl_GetComponent(mxShapeAgg)); + if ( xAggComp.is() ) + xAggComp->addEventListener(xListener); +} + +void SAL_CALL ScShapeObj::removeEventListener( + const uno::Reference<lang::XEventListener>& xListener ) +{ + SolarMutexGuard aGuard; + + uno::Reference<lang::XComponent> xAggComp(lcl_GetComponent(mxShapeAgg)); + if ( xAggComp.is() ) + xAggComp->removeEventListener(xListener); +} + +// XText +// (special handling for ScCellFieldObj) + +static void lcl_CopyOneProperty( beans::XPropertySet& rDest, beans::XPropertySet& rSource, const OUString& aNameStr ) +{ + try + { + rDest.setPropertyValue( aNameStr, rSource.getPropertyValue( aNameStr ) ); + } + catch (uno::Exception&) + { + TOOLS_WARN_EXCEPTION( "sc", "Exception in text field"); + } +} + +void SAL_CALL ScShapeObj::insertTextContent( const uno::Reference<text::XTextRange>& xRange, + const uno::Reference<text::XTextContent>& xContent, + sal_Bool bAbsorb ) +{ + SolarMutexGuard aGuard; + + uno::Reference<text::XTextContent> xEffContent; + + ScEditFieldObj* pCellField = dynamic_cast<ScEditFieldObj*>( xContent.get() ); + if ( pCellField ) + { + // createInstance("TextField.URL") from the document creates a ScCellFieldObj. + // To insert it into drawing text, a SvxUnoTextField is needed instead. + // The ScCellFieldObj object is left in non-inserted state. + + rtl::Reference<SvxUnoTextField> pDrawField = new SvxUnoTextField( text::textfield::Type::URL ); + xEffContent.set(pDrawField); + lcl_CopyOneProperty( *pDrawField, *pCellField, SC_UNONAME_URL ); + lcl_CopyOneProperty( *pDrawField, *pCellField, SC_UNONAME_REPR ); + lcl_CopyOneProperty( *pDrawField, *pCellField, SC_UNONAME_TARGET ); + } + else + xEffContent.set(xContent); + + uno::Reference<text::XText> xAggText(lcl_GetText(mxShapeAgg)); + if ( xAggText.is() ) + xAggText->insertTextContent( xRange, xEffContent, bAbsorb ); +} + +void SAL_CALL ScShapeObj::removeTextContent( const uno::Reference<text::XTextContent>& xContent ) +{ + SolarMutexGuard aGuard; + + // ScCellFieldObj can't be used here. + + uno::Reference<text::XText> xAggText(lcl_GetText(mxShapeAgg)); + if ( xAggText.is() ) + xAggText->removeTextContent( xContent ); +} + +// XSimpleText (parent of XText) +// Use own SvxUnoTextCursor subclass - everything is just passed to aggregated object + +uno::Reference<text::XTextCursor> SAL_CALL ScShapeObj::createTextCursor() +{ + SolarMutexGuard aGuard; + + if ( mxShapeAgg.is() ) + { + // ScDrawTextCursor must be used to ensure the ScShapeObj is returned by getText + + SvxUnoTextBase* pText = comphelper::getFromUnoTunnel<SvxUnoTextBase>( mxShapeAgg ); + if (pText) + return new ScDrawTextCursor( this, *pText ); + } + + return uno::Reference<text::XTextCursor>(); +} + +uno::Reference<text::XTextCursor> SAL_CALL ScShapeObj::createTextCursorByRange( + const uno::Reference<text::XTextRange>& aTextPosition ) +{ + SolarMutexGuard aGuard; + + if ( mxShapeAgg.is() && aTextPosition.is() ) + { + // ScDrawTextCursor must be used to ensure the ScShapeObj is returned by getText + + SvxUnoTextBase* pText = comphelper::getFromUnoTunnel<SvxUnoTextBase>( mxShapeAgg ); + SvxUnoTextRangeBase* pRange = comphelper::getFromUnoTunnel<SvxUnoTextRangeBase>( aTextPosition ); + if ( pText && pRange ) + { + rtl::Reference<SvxUnoTextCursor> pCursor = new ScDrawTextCursor( this, *pText ); + pCursor->SetSelection( pRange->GetSelection() ); + return pCursor; + } + } + + return uno::Reference<text::XTextCursor>(); +} + +void SAL_CALL ScShapeObj::insertString( const uno::Reference<text::XTextRange>& xRange, + const OUString& aString, sal_Bool bAbsorb ) +{ + SolarMutexGuard aGuard; + + uno::Reference<text::XSimpleText> xAggSimpleText(lcl_GetSimpleText(mxShapeAgg)); + if ( !xAggSimpleText.is() ) + throw uno::RuntimeException(); + + xAggSimpleText->insertString( xRange, aString, bAbsorb ); +} + +void SAL_CALL ScShapeObj::insertControlCharacter( const uno::Reference<text::XTextRange>& xRange, + sal_Int16 nControlCharacter, sal_Bool bAbsorb ) +{ + SolarMutexGuard aGuard; + + uno::Reference<text::XSimpleText> xAggSimpleText(lcl_GetSimpleText(mxShapeAgg)); + if ( !xAggSimpleText.is() ) + throw uno::RuntimeException(); + + xAggSimpleText->insertControlCharacter( xRange, nControlCharacter, bAbsorb ); +} + +// XTextRange +// (parent of XSimpleText) + +uno::Reference<text::XText> SAL_CALL ScShapeObj::getText() +{ + return this; +} + +uno::Reference<text::XTextRange> SAL_CALL ScShapeObj::getStart() +{ + SolarMutexGuard aGuard; + + uno::Reference<text::XTextRange> xAggTextRange(lcl_GetTextRange(mxShapeAgg)); + if ( !xAggTextRange.is() ) + throw uno::RuntimeException(); + + return xAggTextRange->getStart(); +} + +uno::Reference<text::XTextRange> SAL_CALL ScShapeObj::getEnd() +{ + SolarMutexGuard aGuard; + + uno::Reference<text::XTextRange> xAggTextRange(lcl_GetTextRange(mxShapeAgg)); + if ( !xAggTextRange.is() ) + throw uno::RuntimeException(); + + return xAggTextRange->getEnd(); +} + +OUString SAL_CALL ScShapeObj::getString() +{ + SolarMutexGuard aGuard; + + uno::Reference<text::XTextRange> xAggTextRange(lcl_GetTextRange(mxShapeAgg)); + if ( !xAggTextRange.is() ) + throw uno::RuntimeException(); + + return xAggTextRange->getString(); +} + +void SAL_CALL ScShapeObj::setString( const OUString& aText ) +{ + SolarMutexGuard aGuard; + + uno::Reference<text::XTextRange> xAggTextRange(lcl_GetTextRange(mxShapeAgg)); + if ( !xAggTextRange.is() ) + throw uno::RuntimeException(); + + xAggTextRange->setString( aText ); +} + +// XChild + +uno::Reference< uno::XInterface > SAL_CALL ScShapeObj::getParent() +{ + SolarMutexGuard aGuard; + + // receive cell position from caption object (parent of a note caption is the note cell) + SdrObject* pObj = GetSdrObject(); + if( pObj ) + { + ScDrawLayer& rModel(static_cast< ScDrawLayer& >(pObj->getSdrModelFromSdrObject())); + SdrPage* pPage(pObj->getSdrPageFromSdrObject()); + ScDocument* pDoc = rModel.GetDocument(); + + if ( pPage && pDoc ) + { + if ( ScDocShell* pDocSh = pDoc->GetDocumentShell() ) + { + SCTAB nTab = 0; + if ( lcl_GetPageNum( pPage, rModel, nTab ) ) + { + const ScDrawObjData* pCaptData = ScDrawLayer::GetNoteCaptionData( pObj, nTab ); + if( pCaptData ) + return cppu::getXWeak( new ScCellObj( pDocSh, pCaptData->maStart ) ); + } + } + } + } + + return nullptr; +} + +void SAL_CALL ScShapeObj::setParent( const uno::Reference< uno::XInterface >& ) +{ + throw lang::NoSupportException(); +} + +// XTypeProvider + +uno::Sequence<uno::Type> SAL_CALL ScShapeObj::getTypes() +{ + uno::Sequence< uno::Type > aBaseTypes( ScShapeObj_Base::getTypes() ); + + uno::Sequence< uno::Type > aTextTypes; + if ( bIsTextShape ) + aTextTypes = ScShapeObj_TextBase::getTypes(); + + uno::Reference<lang::XTypeProvider> xBaseProvider; + if ( mxShapeAgg.is() ) + mxShapeAgg->queryAggregation( cppu::UnoType<lang::XTypeProvider>::get()) >>= xBaseProvider; + OSL_ENSURE( xBaseProvider.is(), "ScShapeObj: No XTypeProvider from aggregated shape!" ); + + uno::Sequence< uno::Type > aAggTypes; + if( xBaseProvider.is() ) + aAggTypes = xBaseProvider->getTypes(); + + return ::comphelper::concatSequences( aBaseTypes, aTextTypes, aAggTypes ); +} + +uno::Sequence<sal_Int8> SAL_CALL ScShapeObj::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +SdrObject* ScShapeObj::GetSdrObject() const noexcept +{ + if(mxShapeAgg.is()) + return SdrObject::getSdrObjectFromXShape( mxShapeAgg ); + return nullptr; +} + +constexpr OUString SC_EVENTACC_ONCLICK = u"OnClick"_ustr; +constexpr OUString SC_EVENTACC_SCRIPT = u"Script"_ustr; +constexpr OUString SC_EVENTACC_EVENTTYPE = u"EventType"_ustr; + +class ShapeUnoEventAccessImpl : public ::cppu::WeakImplHelper< container::XNameReplace > +{ +private: + ScShapeObj* mpShape; + + ScMacroInfo* getInfo( bool bCreate ) + { + return ScShapeObj_getShapeHyperMacroInfo( mpShape, bCreate ); + } + +public: + explicit ShapeUnoEventAccessImpl( ScShapeObj* pShape ): mpShape( pShape ) + { + } + + // XNameReplace + virtual void SAL_CALL replaceByName( const OUString& aName, const uno::Any& aElement ) override + { + if ( !hasByName( aName ) ) + throw container::NoSuchElementException(); + uno::Sequence< beans::PropertyValue > aProperties; + aElement >>= aProperties; + bool isEventType = false; + for( const beans::PropertyValue& rProperty : std::as_const(aProperties) ) + { + if ( rProperty.Name == SC_EVENTACC_EVENTTYPE ) + { + isEventType = true; + continue; + } + if ( isEventType && (rProperty.Name == SC_EVENTACC_SCRIPT) ) + { + OUString sValue; + if ( rProperty.Value >>= sValue ) + { + ScMacroInfo* pInfo = getInfo( true ); + OSL_ENSURE( pInfo, "shape macro info could not be created!" ); + if ( !pInfo ) + break; + pInfo->SetMacro( sValue ); + } + } + } + } + + // XNameAccess + virtual uno::Any SAL_CALL getByName( const OUString& aName ) override + { + uno::Sequence< beans::PropertyValue > aProperties; + ScMacroInfo* pInfo = getInfo(false); + + if ( aName != SC_EVENTACC_ONCLICK ) + { + throw container::NoSuchElementException(); + } + + if ( pInfo && !pInfo->GetMacro().isEmpty() ) + { + aProperties = { comphelper::makePropertyValue(SC_EVENTACC_EVENTTYPE, + SC_EVENTACC_SCRIPT), + comphelper::makePropertyValue(SC_EVENTACC_SCRIPT, pInfo->GetMacro()) }; + } + + return uno::Any( aProperties ); + } + + virtual uno::Sequence< OUString > SAL_CALL getElementNames() override + { + uno::Sequence<OUString> aSeq { SC_EVENTACC_ONCLICK }; + return aSeq; + } + + virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override + { + return aName == SC_EVENTACC_ONCLICK; + } + + // XElementAccess + virtual uno::Type SAL_CALL getElementType() override + { + return cppu::UnoType<uno::Sequence< beans::PropertyValue >>::get(); + } + + virtual sal_Bool SAL_CALL hasElements() override + { + // elements are always present (but contained property sequences may be empty) + return true; + } +}; + +::uno::Reference< container::XNameReplace > SAL_CALL +ScShapeObj::getEvents( ) +{ + return new ShapeUnoEventAccessImpl( this ); +} + +OUString SAL_CALL ScShapeObj::getImplementationName( ) +{ + return "com.sun.star.comp.sc.ScShapeObj"; +} + +sal_Bool SAL_CALL ScShapeObj::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +uno::Sequence< OUString > SAL_CALL ScShapeObj::getSupportedServiceNames( ) +{ + uno::Reference<lang::XServiceInfo> xSI; + if ( mxShapeAgg.is() ) + mxShapeAgg->queryAggregation( cppu::UnoType<lang::XServiceInfo>::get() ) >>= xSI; + + uno::Sequence< OUString > aSupported; + if ( xSI.is() ) + aSupported = xSI->getSupportedServiceNames(); + + aSupported.realloc( aSupported.getLength() + 1 ); + aSupported.getArray()[ aSupported.getLength() - 1 ] = "com.sun.star.sheet.Shape"; + + if( bIsNoteCaption ) + { + aSupported.realloc( aSupported.getLength() + 1 ); + aSupported.getArray()[ aSupported.getLength() - 1 ] = "com.sun.star.sheet.CellAnnotationShape"; + } + + return aSupported; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/unoobj/srchuno.cxx b/sc/source/ui/unoobj/srchuno.cxx new file mode 100644 index 0000000000..a5b59a330c --- /dev/null +++ b/sc/source/ui/unoobj/srchuno.cxx @@ -0,0 +1,195 @@ +/* -*- 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 <scitems.hxx> +#include <svl/srchitem.hxx> +#include <vcl/svapp.hxx> +#include <cppuhelper/supportsservice.hxx> + +#include <srchuno.hxx> +#include <miscuno.hxx> +#include <unonames.hxx> + +using namespace com::sun::star; + +//! SearchWords searches in whole cells - rename it ??? + +// SfxItemPropertyMapEntry only for GetPropertySetInfo + +static std::span<const SfxItemPropertyMapEntry> lcl_GetSearchPropertyMap() +{ + static const SfxItemPropertyMapEntry aSearchPropertyMap_Impl[] = + { + { SC_UNO_SRCHBACK, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_SRCHBYROW, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_SRCHCASE, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_SRCHREGEXP, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_SRCHWILDCARD, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_SRCHSIM, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_SRCHSIMADD, 0, cppu::UnoType<sal_Int16>::get(), 0, 0}, + { SC_UNO_SRCHSIMEX, 0, cppu::UnoType<sal_Int16>::get(), 0, 0}, + { SC_UNO_SRCHSIMREL, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_SRCHSIMREM, 0, cppu::UnoType<sal_Int16>::get(), 0, 0}, + { SC_UNO_SRCHSTYLES, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_SRCHTYPE, 0, cppu::UnoType<sal_Int16>::get(), 0, 0}, // enum TableSearch is gone + { SC_UNO_SRCHWORDS, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_SRCHWCESCCHAR, 0, cppu::UnoType<sal_Int32>::get(), 0, 0 }, + }; + return aSearchPropertyMap_Impl; +} + +constexpr OUString SCSEARCHDESCRIPTOR_SERVICE = u"com.sun.star.util.SearchDescriptor"_ustr; +constexpr OUString SCREPLACEDESCRIPTOR_SERVICE = u"com.sun.star.util.ReplaceDescriptor"_ustr; + +ScCellSearchObj::ScCellSearchObj() : + aPropSet(lcl_GetSearchPropertyMap()), + pSearchItem( new SvxSearchItem( SCITEM_SEARCHDATA ) ) +{ + // Defaults: + pSearchItem->SetWordOnly(false); + pSearchItem->SetExact(false); + pSearchItem->SetMatchFullHalfWidthForms(false); + pSearchItem->SetUseAsianOptions(false); // or all asian bits would have to be handled + pSearchItem->SetBackward(false); + pSearchItem->SetSelection(false); + pSearchItem->SetRegExp(false); + pSearchItem->SetWildcard(false); + pSearchItem->SetPattern(false); + pSearchItem->SetLevenshtein(false); + pSearchItem->SetLEVRelaxed(false); + pSearchItem->SetLEVOther(2); + pSearchItem->SetLEVShorter(2); + pSearchItem->SetLEVLonger(2); + // Calc-Flags + pSearchItem->SetRowDirection(false); + pSearchItem->SetCellType(SvxSearchCellType::FORMULA); + + // Selection-Flag will be set when this is called +} + +ScCellSearchObj::~ScCellSearchObj() +{ +} + +// XSearchDescriptor + +OUString SAL_CALL ScCellSearchObj::getSearchString() +{ + SolarMutexGuard aGuard; + return pSearchItem->GetSearchString(); +} + +void SAL_CALL ScCellSearchObj::setSearchString( const OUString& aString ) +{ + SolarMutexGuard aGuard; + pSearchItem->SetSearchString( aString ); +} + +// XReplaceDescriptor + +OUString SAL_CALL ScCellSearchObj::getReplaceString() +{ + SolarMutexGuard aGuard; + return pSearchItem->GetReplaceString(); +} + +void SAL_CALL ScCellSearchObj::setReplaceString( const OUString& aReplaceString ) +{ + SolarMutexGuard aGuard; + pSearchItem->SetReplaceString( aReplaceString ); +} + +// XPropertySet + +uno::Reference<beans::XPropertySetInfo> SAL_CALL ScCellSearchObj::getPropertySetInfo() +{ + SolarMutexGuard aGuard; + static uno::Reference<beans::XPropertySetInfo> aRef( + new SfxItemPropertySetInfo( aPropSet.getPropertyMap() )); + return aRef; +} + +void SAL_CALL ScCellSearchObj::setPropertyValue( + const OUString& aPropertyName, const uno::Any& aValue ) +{ + SolarMutexGuard aGuard; + + if (aPropertyName == SC_UNO_SRCHBACK) pSearchItem->SetBackward( ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + else if (aPropertyName == SC_UNO_SRCHBYROW) pSearchItem->SetRowDirection( ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + else if (aPropertyName == SC_UNO_SRCHCASE) pSearchItem->SetExact( ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + else if (aPropertyName == SC_UNO_SRCHREGEXP) pSearchItem->SetRegExp( ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + else if (aPropertyName == SC_UNO_SRCHWILDCARD) pSearchItem->SetWildcard( ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + else if (aPropertyName == SC_UNO_SRCHSIM) pSearchItem->SetLevenshtein( ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + else if (aPropertyName == SC_UNO_SRCHSIMREL) pSearchItem->SetLEVRelaxed( ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + else if (aPropertyName == SC_UNO_SRCHSTYLES) pSearchItem->SetPattern( ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + else if (aPropertyName == SC_UNO_SRCHWORDS) pSearchItem->SetWordOnly( ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + else if (aPropertyName == SC_UNO_SRCHSIMADD) pSearchItem->SetLEVLonger( ScUnoHelpFunctions::GetInt16FromAny( aValue ) ); + else if (aPropertyName == SC_UNO_SRCHSIMEX) pSearchItem->SetLEVOther( ScUnoHelpFunctions::GetInt16FromAny( aValue ) ); + else if (aPropertyName == SC_UNO_SRCHSIMREM) pSearchItem->SetLEVShorter( ScUnoHelpFunctions::GetInt16FromAny( aValue ) ); + else if (aPropertyName == SC_UNO_SRCHTYPE) pSearchItem->SetCellType( static_cast<SvxSearchCellType>(ScUnoHelpFunctions::GetInt16FromAny( aValue )) ); + else if (aPropertyName == SC_UNO_SRCHFILTERED) pSearchItem->SetSearchFiltered( ScUnoHelpFunctions::GetBoolFromAny(aValue) ); + else if (aPropertyName == SC_UNO_SRCHFORMATTED) pSearchItem->SetSearchFormatted( ScUnoHelpFunctions::GetBoolFromAny(aValue) ); + else if (aPropertyName == SC_UNO_SRCHWCESCCHAR) pSearchItem->SetWildcardEscapeCharacter( ScUnoHelpFunctions::GetInt32FromAny(aValue) ); +} + +uno::Any SAL_CALL ScCellSearchObj::getPropertyValue( const OUString& aPropertyName ) +{ + SolarMutexGuard aGuard; + uno::Any aRet; + + if (aPropertyName == SC_UNO_SRCHBACK) aRet <<= pSearchItem->GetBackward(); + else if (aPropertyName == SC_UNO_SRCHBYROW) aRet <<= pSearchItem->GetRowDirection(); + else if (aPropertyName == SC_UNO_SRCHCASE) aRet <<= pSearchItem->GetExact(); + else if (aPropertyName == SC_UNO_SRCHREGEXP) aRet <<= pSearchItem->GetRegExp(); + else if (aPropertyName == SC_UNO_SRCHWILDCARD) aRet <<= pSearchItem->GetWildcard(); + else if (aPropertyName == SC_UNO_SRCHSIM) aRet <<= pSearchItem->IsLevenshtein(); + else if (aPropertyName == SC_UNO_SRCHSIMREL) aRet <<= pSearchItem->IsLEVRelaxed(); + else if (aPropertyName == SC_UNO_SRCHSTYLES) aRet <<= pSearchItem->GetPattern(); + else if (aPropertyName == SC_UNO_SRCHWORDS) aRet <<= pSearchItem->GetWordOnly(); + else if (aPropertyName == SC_UNO_SRCHSIMADD) aRet <<= static_cast<sal_Int16>(pSearchItem->GetLEVLonger()); + else if (aPropertyName == SC_UNO_SRCHSIMEX) aRet <<= static_cast<sal_Int16>(pSearchItem->GetLEVOther()); + else if (aPropertyName == SC_UNO_SRCHSIMREM) aRet <<= static_cast<sal_Int16>(pSearchItem->GetLEVShorter()); + else if (aPropertyName == SC_UNO_SRCHTYPE) aRet <<= static_cast<sal_Int16>(pSearchItem->GetCellType()); + else if (aPropertyName == SC_UNO_SRCHFILTERED) aRet <<= pSearchItem->IsSearchFiltered(); + else if (aPropertyName == SC_UNO_SRCHFORMATTED) aRet <<= pSearchItem->IsSearchFormatted(); + else if (aPropertyName == SC_UNO_SRCHWCESCCHAR) aRet <<= pSearchItem->GetWildcardEscapeCharacter(); + + return aRet; +} + +SC_IMPL_DUMMY_PROPERTY_LISTENER( ScCellSearchObj ) + +// XServiceInfo + +OUString SAL_CALL ScCellSearchObj::getImplementationName() +{ + return "ScCellSearchObj"; +} + +sal_Bool SAL_CALL ScCellSearchObj::supportsService( const OUString& rServiceName ) +{ + return cppu::supportsService(this, rServiceName); +} + +uno::Sequence<OUString> SAL_CALL ScCellSearchObj::getSupportedServiceNames() +{ + return {SCSEARCHDESCRIPTOR_SERVICE, SCREPLACEDESCRIPTOR_SERVICE}; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/unoobj/styleuno.cxx b/sc/source/ui/unoobj/styleuno.cxx new file mode 100644 index 0000000000..8dd6fc58f8 --- /dev/null +++ b/sc/source/ui/unoobj/styleuno.cxx @@ -0,0 +1,2069 @@ +/* -*- 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 <scitems.hxx> +#include <editeng/memberids.h> +#include <svx/algitem.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/langitem.hxx> +#include <editeng/numitem.hxx> +#include <svx/pageitem.hxx> +#include <editeng/pbinitem.hxx> +#include <svx/unomid.hxx> +#include <svx/unoshape.hxx> +#include <svx/unoshprp.hxx> +#include <svx/xflbstit.hxx> +#include <svx/xflbmtit.hxx> +#include <editeng/unonrule.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/printer.hxx> +#include <sfx2/sfxsids.hrc> +#include <utility> +#include <vcl/virdev.hxx> +#include <vcl/svapp.hxx> +#include <svl/itempool.hxx> +#include <svl/itemset.hxx> +#include <svl/numformat.hxx> +#include <svl/intitem.hxx> +#include <svl/zformat.hxx> +#include <tools/fract.hxx> +#include <tools/UnitConversion.hxx> +#include <osl/diagnose.h> + +#include <com/sun/star/drawing/BitmapMode.hpp> +#include <com/sun/star/table/BorderLine.hpp> +#include <com/sun/star/table/TableBorder.hpp> +#include <com/sun/star/table/TableBorder2.hpp> +#include <com/sun/star/table/ShadowFormat.hpp> +#include <com/sun/star/table/CellHoriJustify.hpp> +#include <com/sun/star/table/CellOrientation.hpp> +#include <com/sun/star/style/PageStyleLayout.hpp> +#include <com/sun/star/style/GraphicLocation.hpp> +#include <com/sun/star/sheet/XHeaderFooterContent.hpp> +#include <com/sun/star/util/CellProtection.hpp> +#include <com/sun/star/awt/FontSlant.hpp> +#include <com/sun/star/awt/Size.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/lang/Locale.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/graphic/XGraphic.hpp> +#include <comphelper/propertysequence.hxx> +#include <cppuhelper/supportsservice.hxx> + +#include <CommonProperties.hxx> +#include <styleuno.hxx> +#include <docsh.hxx> +#include <attrib.hxx> +#include <stlpool.hxx> +#include <docpool.hxx> +#include <miscuno.hxx> +#include <tablink.hxx> +#include <unonames.hxx> +#include <unowids.hxx> +#include <globstr.hrc> +#include <scresid.hxx> +#include <cellsuno.hxx> +#include <stylehelper.hxx> + +using namespace ::com::sun::star; + +static const SfxItemPropertySet* lcl_GetGraphicStyleSet() +{ + static const SfxItemPropertyMapEntry aGraphicStyleMap_Impl[] = + { + SVX_UNOEDIT_NUMBERING_PROPERTY, + SHADOW_PROPERTIES + LINE_PROPERTIES + LINE_PROPERTIES_START_END + FILL_PROPERTIES + EDGERADIUS_PROPERTIES + TEXT_PROPERTIES_DEFAULTS + CONNECTOR_PROPERTIES + SPECIAL_DIMENSIONING_PROPERTIES_DEFAULTS + {SC_UNONAME_HIDDEN, ATTR_HIDDEN, cppu::UnoType<sal_Bool>::get(), 0, 0 }, + {SC_UNONAME_DISPNAME, SC_WID_UNO_DISPNAME, cppu::UnoType<OUString>::get(), beans::PropertyAttribute::READONLY, 0 }, + }; + static SfxItemPropertySet aGraphicStyleSet_Impl( aGraphicStyleMap_Impl ); + return &aGraphicStyleSet_Impl; +} + +static const SfxItemPropertySet* lcl_GetCellStyleSet() +{ + static const SfxItemPropertyMapEntry aCellStyleMap_Impl[] = + { + {SC_UNONAME_ASIANVERT,ATTR_VERTICAL_ASIAN,cppu::UnoType<bool>::get(), 0, 0 }, + CELL_BORDER_PROPERTIES + CELL_BACKGROUND_COLOR_PROPERTIES + {SC_UNONAME_CELLPRO, ATTR_PROTECTION, ::cppu::UnoType<util::CellProtection>::get(), 0, 0 }, + CHAR_COLOR_PROPERTIES + {SC_UNONAME_COUTL, ATTR_FONT_CONTOUR, cppu::UnoType<bool>::get(), 0, 0 }, + {SC_UNONAME_CCROSS, ATTR_FONT_CROSSEDOUT,cppu::UnoType<bool>::get(), 0, MID_CROSSED_OUT }, + {SC_UNONAME_CEMPHAS, ATTR_FONT_EMPHASISMARK,cppu::UnoType<sal_Int16>::get(), 0, MID_EMPHASIS }, + {SC_UNONAME_CFONT, ATTR_FONT, ::cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY }, + {SC_UNONAME_CFCHARS, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_CHAR_SET }, + {SC_UNO_CJK_CFCHARS, ATTR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_CHAR_SET }, + {SC_UNO_CTL_CFCHARS, ATTR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_CHAR_SET }, + {SC_UNONAME_CFFAMIL, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY }, + {SC_UNO_CJK_CFFAMIL, ATTR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY }, + {SC_UNO_CTL_CFFAMIL, ATTR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY }, + {SC_UNONAME_CFNAME, ATTR_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_FAMILY_NAME }, + {SC_UNO_CJK_CFNAME, ATTR_CJK_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_FAMILY_NAME }, + {SC_UNO_CTL_CFNAME, ATTR_CTL_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_FAMILY_NAME }, + {SC_UNONAME_CFPITCH, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_PITCH }, + {SC_UNO_CJK_CFPITCH, ATTR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_PITCH }, + {SC_UNO_CTL_CFPITCH, ATTR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_PITCH }, + {SC_UNONAME_CFSTYLE, ATTR_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_STYLE_NAME }, + {SC_UNO_CJK_CFSTYLE, ATTR_CJK_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_STYLE_NAME }, + {SC_UNO_CTL_CFSTYLE, ATTR_CTL_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_STYLE_NAME }, + {SC_UNONAME_CHEIGHT, ATTR_FONT_HEIGHT, ::cppu::UnoType<float>::get(), 0, MID_FONTHEIGHT | CONVERT_TWIPS }, + {SC_UNO_CJK_CHEIGHT, ATTR_CJK_FONT_HEIGHT,::cppu::UnoType<float>::get(), 0, MID_FONTHEIGHT | CONVERT_TWIPS }, + {SC_UNO_CTL_CHEIGHT, ATTR_CTL_FONT_HEIGHT,::cppu::UnoType<float>::get(), 0, MID_FONTHEIGHT | CONVERT_TWIPS }, + {SC_UNONAME_CLOCAL, ATTR_FONT_LANGUAGE, ::cppu::UnoType<lang::Locale>::get(), 0, MID_LANG_LOCALE }, + {SC_UNO_CJK_CLOCAL, ATTR_CJK_FONT_LANGUAGE,::cppu::UnoType<lang::Locale>::get(), 0, MID_LANG_LOCALE }, + {SC_UNO_CTL_CLOCAL, ATTR_CTL_FONT_LANGUAGE,::cppu::UnoType<lang::Locale>::get(), 0, MID_LANG_LOCALE }, + {SC_UNONAME_COVER, ATTR_FONT_OVERLINE, ::cppu::UnoType<sal_Int16>::get(), 0, MID_TL_STYLE }, + {SC_UNONAME_COVRLCOL, ATTR_FONT_OVERLINE, cppu::UnoType<sal_Int32>::get(), 0, MID_TL_COLOR }, + {SC_UNONAME_COVRLHAS, ATTR_FONT_OVERLINE, cppu::UnoType<bool>::get(), 0, MID_TL_HASCOLOR }, + {SC_UNONAME_CPOST, ATTR_FONT_POSTURE, ::cppu::UnoType<awt::FontSlant>::get(), 0, MID_POSTURE }, + {SC_UNO_CJK_CPOST, ATTR_CJK_FONT_POSTURE,::cppu::UnoType<awt::FontSlant>::get(), 0, MID_POSTURE }, + {SC_UNO_CTL_CPOST, ATTR_CTL_FONT_POSTURE,::cppu::UnoType<awt::FontSlant>::get(), 0, MID_POSTURE }, + {SC_UNONAME_CRELIEF, ATTR_FONT_RELIEF, cppu::UnoType<sal_Int16>::get(), 0, MID_RELIEF }, + {SC_UNONAME_CSHADD, ATTR_FONT_SHADOWED, cppu::UnoType<bool>::get(), 0, 0 }, + {SC_UNONAME_CSTRIKE, ATTR_FONT_CROSSEDOUT,cppu::UnoType<sal_Int16>::get(), 0, MID_CROSS_OUT }, + {SC_UNONAME_CUNDER, ATTR_FONT_UNDERLINE,::cppu::UnoType<sal_Int16>::get(), 0, MID_TL_STYLE }, + {SC_UNONAME_CUNDLCOL, ATTR_FONT_UNDERLINE,cppu::UnoType<sal_Int32>::get(), 0, MID_TL_COLOR }, + {SC_UNONAME_CUNDLHAS, ATTR_FONT_UNDERLINE,cppu::UnoType<bool>::get(), 0, MID_TL_HASCOLOR }, + {SC_UNONAME_CWEIGHT, ATTR_FONT_WEIGHT, ::cppu::UnoType<float>::get(), 0, MID_WEIGHT }, + {SC_UNO_CJK_CWEIGHT, ATTR_CJK_FONT_WEIGHT,::cppu::UnoType<float>::get(), 0, MID_WEIGHT }, + {SC_UNO_CTL_CWEIGHT, ATTR_CTL_FONT_WEIGHT,::cppu::UnoType<float>::get(), 0, MID_WEIGHT }, + {SC_UNONAME_CWORDMOD, ATTR_FONT_WORDLINE, cppu::UnoType<bool>::get(), 0, 0 }, + {SC_UNONAME_DIAGONAL_BLTR, ATTR_BORDER_BLTR, ::cppu::UnoType<table::BorderLine>::get(), 0, 0 | CONVERT_TWIPS }, + {SC_UNONAME_DIAGONAL_BLTR2, ATTR_BORDER_BLTR, ::cppu::UnoType<table::BorderLine2>::get(), 0, 0 | CONVERT_TWIPS }, + {SC_UNONAME_DIAGONAL_TLBR, ATTR_BORDER_TLBR, ::cppu::UnoType<table::BorderLine>::get(), 0, 0 | CONVERT_TWIPS }, + {SC_UNONAME_DIAGONAL_TLBR2, ATTR_BORDER_TLBR, ::cppu::UnoType<table::BorderLine2>::get(), 0, 0 | CONVERT_TWIPS }, + {SC_UNONAME_DISPNAME, SC_WID_UNO_DISPNAME,::cppu::UnoType<OUString>::get(), beans::PropertyAttribute::READONLY, 0 }, + {SC_UNONAME_CELLHJUS, ATTR_HOR_JUSTIFY, ::cppu::UnoType<table::CellHoriJustify>::get(), 0, MID_HORJUST_HORJUST }, + {SC_UNONAME_CELLHJUS_METHOD, ATTR_HOR_JUSTIFY_METHOD, ::cppu::UnoType<sal_Int32>::get(), 0, 0 }, + {SC_UNONAME_CELLTRAN, ATTR_BACKGROUND, cppu::UnoType<bool>::get(), 0, MID_GRAPHIC_TRANSPARENT }, + {SC_UNONAME_WRAP, ATTR_LINEBREAK, cppu::UnoType<bool>::get(), 0, 0 }, + {SC_UNONAME_NUMFMT, ATTR_VALUE_FORMAT, ::cppu::UnoType<sal_Int32>::get(), 0, 0 }, +// {SC_UNONAME_NUMRULES, SC_WID_UNO_NUMRULES,cppu::UnoType<container::XIndexReplace>::get(), 0, 0 }, + {SC_UNONAME_CELLORI, ATTR_STACKED, ::cppu::UnoType<table::CellOrientation>::get(), 0, 0 }, + {SC_UNONAME_PADJUST, ATTR_HOR_JUSTIFY, ::cppu::UnoType<sal_Int16>::get(), 0, MID_HORJUST_ADJUST }, + {SC_UNONAME_PBMARGIN, ATTR_MARGIN, ::cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_LO_MARGIN | CONVERT_TWIPS }, + {SC_UNONAME_PINDENT, ATTR_INDENT, ::cppu::UnoType<sal_Int16>::get(), 0, 0 }, //! CONVERT_TWIPS + {SC_UNONAME_PISCHDIST,ATTR_SCRIPTSPACE, cppu::UnoType<bool>::get(), 0, 0 }, + {SC_UNONAME_PISFORBID,ATTR_FORBIDDEN_RULES,cppu::UnoType<bool>::get(), 0, 0 }, + {SC_UNONAME_PISHANG, ATTR_HANGPUNCTUATION,cppu::UnoType<bool>::get(), 0, 0 }, + {SC_UNONAME_PISHYPHEN,ATTR_HYPHENATE, cppu::UnoType<bool>::get(), 0, 0 }, + {SC_UNONAME_PLASTADJ, ATTR_HOR_JUSTIFY, ::cppu::UnoType<sal_Int16>::get(), 0, MID_HORJUST_ADJUST }, + {SC_UNONAME_PLMARGIN, ATTR_MARGIN, ::cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_L_MARGIN | CONVERT_TWIPS }, + {SC_UNONAME_PRMARGIN, ATTR_MARGIN, ::cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_R_MARGIN | CONVERT_TWIPS }, + {SC_UNONAME_PTMARGIN, ATTR_MARGIN, ::cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_UP_MARGIN | CONVERT_TWIPS }, + {SC_UNONAME_ROTANG, ATTR_ROTATE_VALUE, ::cppu::UnoType<sal_Int32>::get(), 0, 0 }, + {SC_UNONAME_ROTREF, ATTR_ROTATE_MODE, ::cppu::UnoType<sal_Int32>::get(), 0, 0 }, + {SC_UNONAME_SHADOW, ATTR_SHADOW, ::cppu::UnoType<table::ShadowFormat>::get(), 0, 0 | CONVERT_TWIPS }, + {SC_UNONAME_SHRINK_TO_FIT, ATTR_SHRINKTOFIT, cppu::UnoType<bool>::get(), 0, 0 }, + {SC_UNONAME_TBLBORD, SC_WID_UNO_TBLBORD, ::cppu::UnoType<table::TableBorder>::get(), 0, 0 | CONVERT_TWIPS }, + {SC_UNONAME_TBLBORD, SC_WID_UNO_TBLBORD2, ::cppu::UnoType<table::TableBorder2>::get(), 0, 0 | CONVERT_TWIPS }, + {SC_UNONAME_USERDEF, ATTR_USERDEF, cppu::UnoType<container::XNameContainer>::get(), 0, 0 }, + {SC_UNONAME_CELLVJUS, ATTR_VER_JUSTIFY, ::cppu::UnoType<sal_Int32>::get(), 0, 0 }, + {SC_UNONAME_CELLVJUS_METHOD, ATTR_VER_JUSTIFY_METHOD, ::cppu::UnoType<sal_Int32>::get(), 0, 0 }, + {SC_UNONAME_WRITING, ATTR_WRITINGDIR, cppu::UnoType<sal_Int16>::get(), 0, 0 }, + {SC_UNONAME_HIDDEN, ATTR_HIDDEN, cppu::UnoType<sal_Bool>::get(), 0, 0 }, + {SC_UNONAME_HYPERLINK, ATTR_HYPERLINK, cppu::UnoType<OUString>::get(), 0, 0 }, + }; + static SfxItemPropertySet aCellStyleSet_Impl( aCellStyleMap_Impl ); + return &aCellStyleSet_Impl; +} + +// map with all site attributes including header and footer attributes + +static const SfxItemPropertySet * lcl_GetPageStyleSet() +{ + static const SfxItemPropertyMapEntry aPageStyleMap_Impl[] = + { + {SC_UNO_PAGE_BACKCOLOR, ATTR_BACKGROUND, ::cppu::UnoType<sal_Int32>::get(), 0, MID_BACK_COLOR }, + {SC_UNO_PAGE_GRAPHICFILT, ATTR_BACKGROUND, ::cppu::UnoType<OUString>::get(), 0, MID_GRAPHIC_FILTER }, + {SC_UNO_PAGE_GRAPHICLOC, ATTR_BACKGROUND, ::cppu::UnoType<style::GraphicLocation>::get(), 0, MID_GRAPHIC_POSITION }, + {SC_UNO_PAGE_GRAPHICURL, ATTR_BACKGROUND, ::cppu::UnoType<OUString>::get(), 0, MID_GRAPHIC_URL }, + {SC_UNO_PAGE_GRAPHIC, ATTR_BACKGROUND, ::cppu::UnoType<graphic::XGraphic>::get(), 0, MID_GRAPHIC }, + {SC_UNO_PAGE_BACKTRANS, ATTR_BACKGROUND, cppu::UnoType<bool>::get(), 0, MID_GRAPHIC_TRANSPARENT }, + {OLD_UNO_PAGE_BACKCOLOR, ATTR_BACKGROUND, ::cppu::UnoType<sal_Int32>::get(), 0, MID_BACK_COLOR }, + {SC_UNO_PAGE_BORDERDIST, ATTR_BORDER, ::cppu::UnoType<sal_Int32>::get(), 0, BORDER_DISTANCE | CONVERT_TWIPS }, + {SC_UNO_PAGE_BOTTBORDER, ATTR_BORDER, ::cppu::UnoType<table::BorderLine>::get(), 0, BOTTOM_BORDER | CONVERT_TWIPS }, + {SC_UNO_PAGE_BOTTBRDDIST, ATTR_BORDER, ::cppu::UnoType<sal_Int32>::get(), 0, BOTTOM_BORDER_DISTANCE | CONVERT_TWIPS }, + {SC_UNO_PAGE_BOTTMARGIN, ATTR_ULSPACE, ::cppu::UnoType<sal_Int32>::get(), 0, MID_LO_MARGIN | CONVERT_TWIPS }, + {SC_UNO_PAGE_CENTERHOR, ATTR_PAGE_HORCENTER,cppu::UnoType<bool>::get(), 0, 0 }, + {SC_UNO_PAGE_CENTERVER, ATTR_PAGE_VERCENTER,cppu::UnoType<bool>::get(), 0, 0 }, + {SC_UNONAME_DISPNAME, SC_WID_UNO_DISPNAME,::cppu::UnoType<OUString>::get(), beans::PropertyAttribute::READONLY, 0 }, + {SC_UNO_PAGE_FIRSTPAGE, ATTR_PAGE_FIRSTPAGENO,::cppu::UnoType<sal_Int16>::get(), 0, 0 }, + {SC_UNO_PAGE_FIRSTFTRSHARED, SC_WID_UNO_FOOTERSET,cppu::UnoType<bool>::get(), 0, 0 }, + {SC_UNO_PAGE_FIRSTHDRSHARED, SC_WID_UNO_HEADERSET,cppu::UnoType<bool>::get(), 0, 0 }, + {SC_UNO_PAGE_FIRSTFTRCONT, ATTR_PAGE_FOOTERFIRST,cppu::UnoType<sheet::XHeaderFooterContent>::get(), 0, 0 }, + {SC_UNO_PAGE_FIRSTHDRCONT, ATTR_PAGE_HEADERFIRST,cppu::UnoType<sheet::XHeaderFooterContent>::get(), 0, 0 }, + + {SC_UNO_PAGE_FTRBACKCOL, SC_WID_UNO_FOOTERSET,::cppu::UnoType<sal_Int32>::get(), 0, 0 }, + {SC_UNO_PAGE_FTRGRFFILT, SC_WID_UNO_FOOTERSET,::cppu::UnoType<OUString>::get(), 0, 0 }, + {SC_UNO_PAGE_FTRGRFLOC, SC_WID_UNO_FOOTERSET,::cppu::UnoType<style::GraphicLocation>::get(), 0, 0 }, + {SC_UNO_PAGE_FTRGRFURL, SC_WID_UNO_FOOTERSET,::cppu::UnoType<OUString>::get(), 0, 0 }, + {SC_UNO_PAGE_FTRGRF, SC_WID_UNO_FOOTERSET,::cppu::UnoType<graphic::XGraphic>::get(), 0, 0 }, + {SC_UNO_PAGE_FTRBACKTRAN, SC_WID_UNO_FOOTERSET,cppu::UnoType<bool>::get(), 0, 0 }, + {OLD_UNO_PAGE_FTRBACKCOL, SC_WID_UNO_FOOTERSET,::cppu::UnoType<sal_Int32>::get(), 0, 0 }, + {SC_UNO_PAGE_FTRBODYDIST, SC_WID_UNO_FOOTERSET,::cppu::UnoType<sal_Int32>::get(), 0, 0 }, + {SC_UNO_PAGE_FTRBRDDIST, SC_WID_UNO_FOOTERSET,::cppu::UnoType<sal_Int32>::get(), 0, 0 }, + {SC_UNO_PAGE_FTRBOTTBOR, SC_WID_UNO_FOOTERSET,::cppu::UnoType<table::BorderLine>::get(), 0, 0 }, + {SC_UNO_PAGE_FTRBOTTBDIS, SC_WID_UNO_FOOTERSET,::cppu::UnoType<sal_Int32>::get(), 0, 0 }, + {OLD_UNO_PAGE_FTRDYNAMIC, SC_WID_UNO_FOOTERSET,cppu::UnoType<bool>::get(), 0, 0 }, + {SC_UNO_PAGE_FTRHEIGHT, SC_WID_UNO_FOOTERSET,::cppu::UnoType<sal_Int32>::get(), 0, 0 }, + {SC_UNO_PAGE_FTRDYNAMIC, SC_WID_UNO_FOOTERSET,cppu::UnoType<bool>::get(), 0, 0 }, + {SC_UNO_PAGE_FTRON, SC_WID_UNO_FOOTERSET,cppu::UnoType<bool>::get(), 0, 0 }, + {SC_UNO_PAGE_FTRSHARED, SC_WID_UNO_FOOTERSET,cppu::UnoType<bool>::get(), 0, 0 }, + {SC_UNO_PAGE_FTRLEFTBOR, SC_WID_UNO_FOOTERSET,::cppu::UnoType<table::BorderLine>::get(), 0, 0 }, + {SC_UNO_PAGE_FTRLEFTBDIS, SC_WID_UNO_FOOTERSET,::cppu::UnoType<sal_Int32>::get(), 0, 0 }, + {SC_UNO_PAGE_FTRLEFTMAR, SC_WID_UNO_FOOTERSET,::cppu::UnoType<sal_Int32>::get(), 0, 0 }, + {OLD_UNO_PAGE_FTRON, SC_WID_UNO_FOOTERSET,cppu::UnoType<bool>::get(), 0, 0 }, + {SC_UNO_PAGE_FTRRIGHTBOR, SC_WID_UNO_FOOTERSET,::cppu::UnoType<table::BorderLine>::get(), 0, 0 }, + {SC_UNO_PAGE_FTRRIGHTBDIS,SC_WID_UNO_FOOTERSET,::cppu::UnoType<sal_Int32>::get(), 0, 0 }, + {SC_UNO_PAGE_FTRRIGHTMAR, SC_WID_UNO_FOOTERSET,::cppu::UnoType<sal_Int32>::get(), 0, 0 }, + {SC_UNO_PAGE_FTRSHADOW, SC_WID_UNO_FOOTERSET,::cppu::UnoType<table::ShadowFormat>::get(), 0, 0 }, + {OLD_UNO_PAGE_FTRSHARED, SC_WID_UNO_FOOTERSET,cppu::UnoType<bool>::get(), 0, 0 }, + {SC_UNO_PAGE_FTRTOPBOR, SC_WID_UNO_FOOTERSET,::cppu::UnoType<table::BorderLine>::get(), 0, 0 }, + {SC_UNO_PAGE_FTRTOPBDIS, SC_WID_UNO_FOOTERSET,::cppu::UnoType<sal_Int32>::get(), 0, 0 }, + + {SC_UNO_PAGE_HDRBACKCOL, SC_WID_UNO_HEADERSET,::cppu::UnoType<sal_Int32>::get(), 0, 0 }, + {SC_UNO_PAGE_HDRGRFFILT, SC_WID_UNO_HEADERSET,::cppu::UnoType<OUString>::get(), 0, 0 }, + {SC_UNO_PAGE_HDRGRFLOC, SC_WID_UNO_HEADERSET,::cppu::UnoType<style::GraphicLocation>::get(), 0, 0 }, + {SC_UNO_PAGE_HDRGRFURL, SC_WID_UNO_HEADERSET,::cppu::UnoType<OUString>::get(), 0, 0 }, + {SC_UNO_PAGE_HDRGRF, SC_WID_UNO_HEADERSET,::cppu::UnoType<graphic::XGraphic>::get(), 0, 0 }, + {SC_UNO_PAGE_HDRBACKTRAN, SC_WID_UNO_HEADERSET,cppu::UnoType<bool>::get(), 0, 0 }, + {OLD_UNO_PAGE_HDRBACKCOL, SC_WID_UNO_HEADERSET,::cppu::UnoType<sal_Int32>::get(), 0, 0 }, + {SC_UNO_PAGE_HDRBODYDIST, SC_WID_UNO_HEADERSET,::cppu::UnoType<sal_Int32>::get(), 0, 0 }, + {SC_UNO_PAGE_HDRBRDDIST, SC_WID_UNO_HEADERSET,::cppu::UnoType<sal_Int32>::get(), 0, 0 }, + {SC_UNO_PAGE_HDRBOTTBOR, SC_WID_UNO_HEADERSET,::cppu::UnoType<table::BorderLine>::get(), 0, 0 }, + {SC_UNO_PAGE_HDRBOTTBDIS, SC_WID_UNO_HEADERSET,::cppu::UnoType<sal_Int32>::get(), 0, 0 }, + {OLD_UNO_PAGE_HDRDYNAMIC, SC_WID_UNO_HEADERSET,cppu::UnoType<bool>::get(), 0, 0 }, + {SC_UNO_PAGE_HDRHEIGHT, SC_WID_UNO_HEADERSET,::cppu::UnoType<sal_Int32>::get(), 0, 0 }, + {SC_UNO_PAGE_HDRDYNAMIC, SC_WID_UNO_HEADERSET,cppu::UnoType<bool>::get(), 0, 0 }, + {SC_UNO_PAGE_HDRON, SC_WID_UNO_HEADERSET,cppu::UnoType<bool>::get(), 0, 0 }, + {SC_UNO_PAGE_HDRSHARED, SC_WID_UNO_HEADERSET,cppu::UnoType<bool>::get(), 0, 0 }, + {SC_UNO_PAGE_HDRLEFTBOR, SC_WID_UNO_HEADERSET,::cppu::UnoType<table::BorderLine>::get(), 0, 0 }, + {SC_UNO_PAGE_HDRLEFTBDIS, SC_WID_UNO_HEADERSET,::cppu::UnoType<sal_Int32>::get(), 0, 0 }, + {SC_UNO_PAGE_HDRLEFTMAR, SC_WID_UNO_HEADERSET,::cppu::UnoType<sal_Int32>::get(), 0, 0 }, + {OLD_UNO_PAGE_HDRON, SC_WID_UNO_HEADERSET,cppu::UnoType<bool>::get(), 0, 0 }, + {SC_UNO_PAGE_HDRRIGHTBOR, SC_WID_UNO_HEADERSET,::cppu::UnoType<table::BorderLine>::get(), 0, 0 }, + {SC_UNO_PAGE_HDRRIGHTBDIS,SC_WID_UNO_HEADERSET,::cppu::UnoType<sal_Int32>::get(), 0, 0 }, + {SC_UNO_PAGE_HDRRIGHTMAR, SC_WID_UNO_HEADERSET,::cppu::UnoType<sal_Int32>::get(), 0, 0 }, + {SC_UNO_PAGE_HDRSHADOW, SC_WID_UNO_HEADERSET,::cppu::UnoType<table::ShadowFormat>::get(), 0, 0 }, + {OLD_UNO_PAGE_HDRSHARED, SC_WID_UNO_HEADERSET,cppu::UnoType<bool>::get(), 0, 0 }, + {SC_UNO_PAGE_HDRTOPBOR, SC_WID_UNO_HEADERSET,::cppu::UnoType<table::BorderLine>::get(), 0, 0 }, + {SC_UNO_PAGE_HDRTOPBDIS, SC_WID_UNO_HEADERSET,::cppu::UnoType<sal_Int32>::get(), 0, 0 }, + + {SC_UNO_PAGE_HEIGHT, ATTR_PAGE_SIZE, ::cppu::UnoType<sal_Int32>::get(), 0, MID_SIZE_HEIGHT | CONVERT_TWIPS }, + {OLD_UNO_PAGE_BACKTRANS, ATTR_BACKGROUND, cppu::UnoType<bool>::get(), 0, MID_GRAPHIC_TRANSPARENT }, + {SC_UNO_PAGE_LANDSCAPE, ATTR_PAGE, cppu::UnoType<bool>::get(), 0, MID_PAGE_ORIENTATION }, + {SC_UNO_PAGE_LEFTBORDER, ATTR_BORDER, ::cppu::UnoType<table::BorderLine>::get(), 0, LEFT_BORDER | CONVERT_TWIPS }, + {SC_UNO_PAGE_LEFTBRDDIST, ATTR_BORDER, ::cppu::UnoType<sal_Int32>::get(), 0, LEFT_BORDER_DISTANCE | CONVERT_TWIPS }, + {SC_UNO_PAGE_LEFTMARGIN, ATTR_LRSPACE, ::cppu::UnoType<sal_Int32>::get(), 0, MID_L_MARGIN | CONVERT_TWIPS }, + {SC_UNO_PAGE_LEFTFTRCONT, ATTR_PAGE_FOOTERLEFT,cppu::UnoType<sheet::XHeaderFooterContent>::get(), 0, 0 }, + {SC_UNO_PAGE_LEFTHDRCONT, ATTR_PAGE_HEADERLEFT,cppu::UnoType<sheet::XHeaderFooterContent>::get(), 0, 0 }, + {SC_UNO_PAGE_NUMBERTYPE, ATTR_PAGE, ::cppu::UnoType<sal_Int16>::get(), 0, MID_PAGE_NUMTYPE }, + {SC_UNO_PAGE_SCALEVAL, ATTR_PAGE_SCALE, ::cppu::UnoType<sal_Int16>::get(), 0, 0 }, + {SC_UNO_PAGE_SYTLELAYOUT, ATTR_PAGE, ::cppu::UnoType<style::PageStyleLayout>::get(), 0, MID_PAGE_LAYOUT }, + {SC_UNO_PAGE_PRINTANNOT, ATTR_PAGE_NOTES, cppu::UnoType<bool>::get(), 0, 0 }, + {SC_UNO_PAGE_PRINTCHARTS, ATTR_PAGE_CHARTS, cppu::UnoType<bool>::get(), 0, 0 }, + {SC_UNO_PAGE_PRINTDOWN, ATTR_PAGE_TOPDOWN, cppu::UnoType<bool>::get(), 0, 0 }, + {SC_UNO_PAGE_PRINTDRAW, ATTR_PAGE_DRAWINGS, cppu::UnoType<bool>::get(), 0, 0 }, + {SC_UNO_PAGE_PRINTFORMUL, ATTR_PAGE_FORMULAS, cppu::UnoType<bool>::get(), 0, 0 }, + {SC_UNO_PAGE_PRINTGRID, ATTR_PAGE_GRID, cppu::UnoType<bool>::get(), 0, 0 }, + {SC_UNO_PAGE_PRINTHEADER, ATTR_PAGE_HEADERS, cppu::UnoType<bool>::get(), 0, 0 }, + {SC_UNO_PAGE_PRINTOBJS, ATTR_PAGE_OBJECTS, cppu::UnoType<bool>::get(), 0, 0 }, + {SC_UNO_PAGE_PRINTZERO, ATTR_PAGE_NULLVALS, cppu::UnoType<bool>::get(), 0, 0 }, + {SC_UNO_PAGE_PAPERTRAY, ATTR_PAGE_PAPERBIN, ::cppu::UnoType<OUString>::get(), 0, 0 }, + {SC_UNO_PAGE_RIGHTBORDER, ATTR_BORDER, ::cppu::UnoType<table::BorderLine>::get(), 0, RIGHT_BORDER | CONVERT_TWIPS }, + {SC_UNO_PAGE_RIGHTBRDDIST,ATTR_BORDER, ::cppu::UnoType<sal_Int32>::get(), 0, RIGHT_BORDER_DISTANCE | CONVERT_TWIPS }, + {SC_UNO_PAGE_RIGHTMARGIN, ATTR_LRSPACE, ::cppu::UnoType<sal_Int32>::get(), 0, MID_R_MARGIN | CONVERT_TWIPS }, + {SC_UNO_PAGE_RIGHTFTRCON, ATTR_PAGE_FOOTERRIGHT,cppu::UnoType<sheet::XHeaderFooterContent>::get(), 0, 0 }, + {SC_UNO_PAGE_RIGHTHDRCON, ATTR_PAGE_HEADERRIGHT,cppu::UnoType<sheet::XHeaderFooterContent>::get(), 0, 0 }, + {SC_UNO_PAGE_SCALETOPAG, ATTR_PAGE_SCALETOPAGES,::cppu::UnoType<sal_Int16>::get(), 0, 0 }, + {SC_UNO_PAGE_SCALETOX, ATTR_PAGE_SCALETO, ::cppu::UnoType<sal_Int16>::get(), 0, 0 }, + {SC_UNO_PAGE_SCALETOY, ATTR_PAGE_SCALETO, ::cppu::UnoType<sal_Int16>::get(), 0, 0 }, + {SC_UNO_PAGE_SHADOWFORM, ATTR_SHADOW, ::cppu::UnoType<table::ShadowFormat>::get(), 0, 0 | CONVERT_TWIPS }, + {SC_UNO_PAGE_SIZE, ATTR_PAGE_SIZE, ::cppu::UnoType<awt::Size>::get(), 0, MID_SIZE_SIZE | CONVERT_TWIPS }, + {SC_UNO_PAGE_TOPBORDER, ATTR_BORDER, ::cppu::UnoType<table::BorderLine>::get(), 0, TOP_BORDER | CONVERT_TWIPS }, + {SC_UNO_PAGE_TOPBRDDIST, ATTR_BORDER, ::cppu::UnoType<sal_Int32>::get(), 0, TOP_BORDER_DISTANCE | CONVERT_TWIPS }, + {SC_UNO_PAGE_TOPMARGIN, ATTR_ULSPACE, ::cppu::UnoType<sal_Int32>::get(), 0, MID_UP_MARGIN | CONVERT_TWIPS }, + {OLD_UNO_PAGE_FTRBACKTRAN,SC_WID_UNO_FOOTERSET,cppu::UnoType<bool>::get(), 0, 0 }, + {OLD_UNO_PAGE_HDRBACKTRAN,SC_WID_UNO_HEADERSET,cppu::UnoType<bool>::get(), 0, 0 }, + {SC_UNONAME_USERDEF, ATTR_USERDEF, cppu::UnoType<container::XNameContainer>::get(), 0, 0 }, + {SC_UNO_PAGE_WIDTH, ATTR_PAGE_SIZE, ::cppu::UnoType<sal_Int32>::get(), 0, MID_SIZE_WIDTH | CONVERT_TWIPS }, + {SC_UNONAME_WRITING, ATTR_WRITINGDIR, cppu::UnoType<sal_Int16>::get(), 0, 0 }, + {SC_UNONAME_HIDDEN, ATTR_HIDDEN, cppu::UnoType<sal_Bool>::get(), 0, 0 }, + }; + static SfxItemPropertySet aPageStyleSet_Impl( aPageStyleMap_Impl ); + return &aPageStyleSet_Impl; +} + +// map with content of the Header-Item-Sets + +static const SfxItemPropertyMap* lcl_GetHeaderStyleMap() +{ + static const SfxItemPropertyMapEntry aHeaderStyleMap_Impl[] = + { + {SC_UNO_PAGE_HDRBACKCOL, ATTR_BACKGROUND, ::cppu::UnoType<sal_Int32>::get(), 0, MID_BACK_COLOR }, + {SC_UNO_PAGE_HDRGRFFILT, ATTR_BACKGROUND, ::cppu::UnoType<OUString>::get(), 0, MID_GRAPHIC_FILTER }, + {SC_UNO_PAGE_HDRGRFLOC, ATTR_BACKGROUND, ::cppu::UnoType<style::GraphicLocation>::get(), 0, MID_GRAPHIC_POSITION }, + {SC_UNO_PAGE_HDRGRFURL, ATTR_BACKGROUND, ::cppu::UnoType<OUString>::get(), 0, MID_GRAPHIC_URL }, + {SC_UNO_PAGE_HDRGRF, ATTR_BACKGROUND, ::cppu::UnoType<graphic::XGraphic>::get(), 0, MID_GRAPHIC }, + {SC_UNO_PAGE_HDRBACKTRAN, ATTR_BACKGROUND, cppu::UnoType<bool>::get(), 0, MID_GRAPHIC_TRANSPARENT }, + {OLD_UNO_PAGE_HDRBACKCOL, ATTR_BACKGROUND, ::cppu::UnoType<sal_Int32>::get(), 0, MID_BACK_COLOR }, + {SC_UNO_PAGE_HDRBODYDIST, ATTR_ULSPACE, ::cppu::UnoType<sal_Int32>::get(), 0, MID_LO_MARGIN | CONVERT_TWIPS }, + {SC_UNO_PAGE_HDRBRDDIST, ATTR_BORDER, ::cppu::UnoType<sal_Int32>::get(), 0, BORDER_DISTANCE | CONVERT_TWIPS }, + {SC_UNO_PAGE_HDRBOTTBOR, ATTR_BORDER, ::cppu::UnoType<table::BorderLine>::get(), 0, BOTTOM_BORDER | CONVERT_TWIPS }, + {SC_UNO_PAGE_HDRBOTTBDIS, ATTR_BORDER, ::cppu::UnoType<sal_Int32>::get(), 0, BOTTOM_BORDER_DISTANCE | CONVERT_TWIPS }, + {OLD_UNO_PAGE_HDRDYNAMIC, ATTR_PAGE_DYNAMIC, cppu::UnoType<bool>::get(), 0, 0 }, + {SC_UNO_PAGE_HDRHEIGHT, ATTR_PAGE_SIZE, ::cppu::UnoType<sal_Int32>::get(), 0, MID_SIZE_HEIGHT | CONVERT_TWIPS }, + {SC_UNO_PAGE_HDRDYNAMIC, ATTR_PAGE_DYNAMIC, cppu::UnoType<bool>::get(), 0, 0 }, + {SC_UNO_PAGE_HDRON, ATTR_PAGE_ON, cppu::UnoType<bool>::get(), 0, 0 }, + {SC_UNO_PAGE_HDRSHARED, ATTR_PAGE_SHARED, cppu::UnoType<bool>::get(), 0, 0 }, + {SC_UNO_PAGE_FIRSTHDRSHARED, ATTR_PAGE_SHARED_FIRST, cppu::UnoType<bool>::get(), 0, 0 }, + {SC_UNO_PAGE_HDRLEFTBOR, ATTR_BORDER, ::cppu::UnoType<table::BorderLine>::get(), 0, LEFT_BORDER | CONVERT_TWIPS }, + {SC_UNO_PAGE_HDRLEFTBDIS, ATTR_BORDER, ::cppu::UnoType<sal_Int32>::get(), 0, LEFT_BORDER_DISTANCE | CONVERT_TWIPS }, + {SC_UNO_PAGE_HDRLEFTMAR, ATTR_LRSPACE, ::cppu::UnoType<sal_Int32>::get(), 0, MID_L_MARGIN | CONVERT_TWIPS }, + {OLD_UNO_PAGE_HDRON, ATTR_PAGE_ON, cppu::UnoType<bool>::get(), 0, 0 }, + {SC_UNO_PAGE_HDRRIGHTBOR, ATTR_BORDER, ::cppu::UnoType<table::BorderLine>::get(), 0, RIGHT_BORDER | CONVERT_TWIPS }, + {SC_UNO_PAGE_HDRRIGHTBDIS,ATTR_BORDER, ::cppu::UnoType<sal_Int32>::get(), 0, RIGHT_BORDER_DISTANCE | CONVERT_TWIPS }, + {SC_UNO_PAGE_HDRRIGHTMAR, ATTR_LRSPACE, ::cppu::UnoType<sal_Int32>::get(), 0, MID_R_MARGIN | CONVERT_TWIPS }, + {SC_UNO_PAGE_HDRSHADOW, ATTR_SHADOW, ::cppu::UnoType<table::ShadowFormat>::get(), 0, 0 | CONVERT_TWIPS }, + {OLD_UNO_PAGE_HDRSHARED, ATTR_PAGE_SHARED, cppu::UnoType<bool>::get(), 0, 0 }, + {SC_UNO_PAGE_HDRTOPBOR, ATTR_BORDER, ::cppu::UnoType<table::BorderLine>::get(), 0, TOP_BORDER | CONVERT_TWIPS }, + {SC_UNO_PAGE_HDRTOPBDIS, ATTR_BORDER, ::cppu::UnoType<sal_Int32>::get(), 0, TOP_BORDER_DISTANCE | CONVERT_TWIPS }, + {OLD_UNO_PAGE_HDRBACKTRAN,ATTR_BACKGROUND, cppu::UnoType<bool>::get(), 0, MID_GRAPHIC_TRANSPARENT }, + }; + static SfxItemPropertyMap aHeaderStyleMap( aHeaderStyleMap_Impl ); + return &aHeaderStyleMap; +} + +// map with content of the Footer-Item-Sets + +static const SfxItemPropertyMap* lcl_GetFooterStyleMap() +{ + static const SfxItemPropertyMapEntry aFooterStyleMap_Impl[] = + { + {SC_UNO_PAGE_FTRBACKCOL, ATTR_BACKGROUND, ::cppu::UnoType<sal_Int32>::get(), 0, MID_BACK_COLOR }, + {SC_UNO_PAGE_FTRGRFFILT, ATTR_BACKGROUND, ::cppu::UnoType<OUString>::get(), 0, MID_GRAPHIC_FILTER }, + {SC_UNO_PAGE_FTRGRFLOC, ATTR_BACKGROUND, ::cppu::UnoType<style::GraphicLocation>::get(), 0, MID_GRAPHIC_POSITION }, + {SC_UNO_PAGE_FTRGRFURL, ATTR_BACKGROUND, ::cppu::UnoType<OUString>::get(), 0, MID_GRAPHIC_URL }, + {SC_UNO_PAGE_FTRGRF, ATTR_BACKGROUND, ::cppu::UnoType<graphic::XGraphic>::get(), 0, MID_GRAPHIC }, + {SC_UNO_PAGE_FTRBACKTRAN, ATTR_BACKGROUND, cppu::UnoType<bool>::get(), 0, MID_GRAPHIC_TRANSPARENT }, + {OLD_UNO_PAGE_FTRBACKCOL, ATTR_BACKGROUND, ::cppu::UnoType<sal_Int32>::get(), 0, MID_BACK_COLOR }, + {SC_UNO_PAGE_FTRBODYDIST, ATTR_ULSPACE, ::cppu::UnoType<sal_Int32>::get(), 0, MID_UP_MARGIN | CONVERT_TWIPS }, + {SC_UNO_PAGE_FTRBRDDIST, ATTR_BORDER, ::cppu::UnoType<sal_Int32>::get(), 0, BORDER_DISTANCE | CONVERT_TWIPS }, + {SC_UNO_PAGE_FTRBOTTBOR, ATTR_BORDER, ::cppu::UnoType<table::BorderLine>::get(), 0, BOTTOM_BORDER | CONVERT_TWIPS }, + {SC_UNO_PAGE_FTRBOTTBDIS, ATTR_BORDER, ::cppu::UnoType<sal_Int32>::get(), 0, BOTTOM_BORDER_DISTANCE | CONVERT_TWIPS }, + {OLD_UNO_PAGE_FTRDYNAMIC, ATTR_PAGE_DYNAMIC, cppu::UnoType<bool>::get(), 0, 0 }, + {SC_UNO_PAGE_FTRHEIGHT, ATTR_PAGE_SIZE, ::cppu::UnoType<sal_Int32>::get(), 0, MID_SIZE_HEIGHT | CONVERT_TWIPS }, + {SC_UNO_PAGE_FTRDYNAMIC, ATTR_PAGE_DYNAMIC, cppu::UnoType<bool>::get(), 0, 0 }, + {SC_UNO_PAGE_FTRON, ATTR_PAGE_ON, cppu::UnoType<bool>::get(), 0, 0 }, + {SC_UNO_PAGE_FTRSHARED, ATTR_PAGE_SHARED, cppu::UnoType<bool>::get(), 0, 0 }, + {SC_UNO_PAGE_FIRSTFTRSHARED, ATTR_PAGE_SHARED_FIRST, cppu::UnoType<bool>::get(), 0, 0 }, + {SC_UNO_PAGE_FTRLEFTBOR, ATTR_BORDER, ::cppu::UnoType<table::BorderLine>::get(), 0, LEFT_BORDER | CONVERT_TWIPS }, + {SC_UNO_PAGE_FTRLEFTBDIS, ATTR_BORDER, ::cppu::UnoType<sal_Int32>::get(), 0, LEFT_BORDER_DISTANCE | CONVERT_TWIPS }, + {SC_UNO_PAGE_FTRLEFTMAR, ATTR_LRSPACE, ::cppu::UnoType<sal_Int32>::get(), 0, MID_L_MARGIN | CONVERT_TWIPS }, + {OLD_UNO_PAGE_FTRON, ATTR_PAGE_ON, cppu::UnoType<bool>::get(), 0, 0 }, + {SC_UNO_PAGE_FTRRIGHTBOR, ATTR_BORDER, ::cppu::UnoType<table::BorderLine>::get(), 0, RIGHT_BORDER | CONVERT_TWIPS }, + {SC_UNO_PAGE_FTRRIGHTBDIS,ATTR_BORDER, ::cppu::UnoType<sal_Int32>::get(), 0, RIGHT_BORDER_DISTANCE | CONVERT_TWIPS }, + {SC_UNO_PAGE_FTRRIGHTMAR, ATTR_LRSPACE, ::cppu::UnoType<sal_Int32>::get(), 0, MID_R_MARGIN | CONVERT_TWIPS }, + {SC_UNO_PAGE_FTRSHADOW, ATTR_SHADOW, ::cppu::UnoType<table::ShadowFormat>::get(), 0, 0 | CONVERT_TWIPS }, + {OLD_UNO_PAGE_FTRSHARED, ATTR_PAGE_SHARED, cppu::UnoType<bool>::get(), 0, 0 }, + {SC_UNO_PAGE_FTRTOPBOR, ATTR_BORDER, ::cppu::UnoType<table::BorderLine>::get(), 0, TOP_BORDER | CONVERT_TWIPS }, + {SC_UNO_PAGE_FTRTOPBDIS, ATTR_BORDER, ::cppu::UnoType<sal_Int32>::get(), 0, TOP_BORDER_DISTANCE | CONVERT_TWIPS }, + {OLD_UNO_PAGE_FTRBACKTRAN,ATTR_BACKGROUND, cppu::UnoType<bool>::get(), 0, MID_GRAPHIC_TRANSPARENT }, + }; + static SfxItemPropertyMap aFooterStyleMap( aFooterStyleMap_Impl ); + return &aFooterStyleMap; +} + +// access index on the style types: 0 = Cell, 1 = Page, 2 = Drawing + +#define SC_STYLE_FAMILY_COUNT 3 + +constexpr OUString SC_FAMILYNAME_CELL = u"CellStyles"_ustr; +constexpr OUString SC_FAMILYNAME_PAGE = u"PageStyles"_ustr; +constexpr OUString SC_FAMILYNAME_GRAPHIC = u"GraphicStyles"_ustr; + +const SfxStyleFamily aStyleFamilyTypes[SC_STYLE_FAMILY_COUNT] = { SfxStyleFamily::Para, SfxStyleFamily::Page, SfxStyleFamily::Frame }; + +constexpr OUString SCSTYLE_SERVICE = u"com.sun.star.style.Style"_ustr; +constexpr OUString SCCELLSTYLE_SERVICE = u"com.sun.star.style.CellStyle"_ustr; +constexpr OUString SCPAGESTYLE_SERVICE = u"com.sun.star.style.PageStyle"_ustr; +constexpr OUString SCGRAPHICSTYLE_SERVICE = u"com.sun.star.style.GraphicStyle"_ustr; + +SC_SIMPLE_SERVICE_INFO( ScStyleFamiliesObj, "ScStyleFamiliesObj", "com.sun.star.style.StyleFamilies" ) +SC_SIMPLE_SERVICE_INFO( ScStyleFamilyObj, "ScStyleFamilyObj", "com.sun.star.style.StyleFamily" ) + +constexpr OUString SC_PAPERBIN_DEFAULTNAME = u"[From printer settings]"_ustr; + +static bool lcl_AnyTabProtected( const ScDocument& rDoc ) +{ + SCTAB nTabCount = rDoc.GetTableCount(); + for (SCTAB i=0; i<nTabCount; i++) + if (rDoc.IsTabProtected(i)) + return true; + return false; +} + +ScStyleFamiliesObj::ScStyleFamiliesObj(ScDocShell* pDocSh) : + pDocShell( pDocSh ) +{ + pDocShell->GetDocument().AddUnoObject(*this); +} + +ScStyleFamiliesObj::~ScStyleFamiliesObj() +{ + SolarMutexGuard g; + + if (pDocShell) + pDocShell->GetDocument().RemoveUnoObject(*this); +} + +void ScStyleFamiliesObj::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + // reference update does not matter here + + if ( rHint.GetId() == SfxHintId::Dying ) + { + pDocShell = nullptr; + } +} + +// XStyleFamilies + +rtl::Reference<ScStyleFamilyObj> ScStyleFamiliesObj::GetObjectByType_Impl(SfxStyleFamily nType) const +{ + if ( pDocShell ) + { + if ( nType == SfxStyleFamily::Para ) + return new ScStyleFamilyObj( pDocShell, SfxStyleFamily::Para ); + else if ( nType == SfxStyleFamily::Page ) + return new ScStyleFamilyObj( pDocShell, SfxStyleFamily::Page ); + else if ( nType == SfxStyleFamily::Frame ) + return new ScStyleFamilyObj( pDocShell, SfxStyleFamily::Frame ); + } + OSL_FAIL("getStyleFamilyByType: no DocShell or wrong SfxStyleFamily"); + return nullptr; +} + +rtl::Reference<ScStyleFamilyObj> ScStyleFamiliesObj::GetObjectByIndex_Impl(sal_uInt32 nIndex) const +{ + if ( nIndex < SC_STYLE_FAMILY_COUNT ) + return GetObjectByType_Impl(aStyleFamilyTypes[nIndex]); + + return nullptr; // invalid index +} + +rtl::Reference<ScStyleFamilyObj> ScStyleFamiliesObj::GetObjectByName_Impl(std::u16string_view aName) const +{ + if ( pDocShell ) + { + if ( aName == SC_FAMILYNAME_CELL ) + return new ScStyleFamilyObj( pDocShell, SfxStyleFamily::Para ); + else if ( aName == SC_FAMILYNAME_PAGE ) + return new ScStyleFamilyObj( pDocShell, SfxStyleFamily::Page ); + else if ( aName == SC_FAMILYNAME_GRAPHIC ) + return new ScStyleFamilyObj( pDocShell, SfxStyleFamily::Frame ); + } + // no assertion - called directly from getByName + return nullptr; +} + +// container::XIndexAccess + +sal_Int32 SAL_CALL ScStyleFamiliesObj::getCount() +{ + return SC_STYLE_FAMILY_COUNT; +} + +uno::Any SAL_CALL ScStyleFamiliesObj::getByIndex( sal_Int32 nIndex ) +{ + SolarMutexGuard aGuard; + uno::Reference< container::XNameContainer > xFamily(GetObjectByIndex_Impl(nIndex)); + if (!xFamily.is()) + throw lang::IndexOutOfBoundsException(); + + return uno::Any(xFamily); +} + +uno::Type SAL_CALL ScStyleFamiliesObj::getElementType() +{ + return cppu::UnoType<container::XNameContainer>::get(); // has to fit to getByIndex +} + +sal_Bool SAL_CALL ScStyleFamiliesObj::hasElements() +{ + SolarMutexGuard aGuard; + return ( getCount() != 0 ); +} + +// container::XNameAccess + +uno::Any SAL_CALL ScStyleFamiliesObj::getByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + uno::Reference< container::XNameContainer > xFamily(GetObjectByName_Impl(aName)); + if (!xFamily.is()) + throw container::NoSuchElementException(); + + return uno::Any(xFamily); +} + +uno::Sequence<OUString> SAL_CALL ScStyleFamiliesObj::getElementNames() +{ + return {SC_FAMILYNAME_CELL, SC_FAMILYNAME_PAGE, SC_FAMILYNAME_GRAPHIC}; +} + +sal_Bool SAL_CALL ScStyleFamiliesObj::hasByName( const OUString& aName ) +{ + return aName == SC_FAMILYNAME_CELL || aName == SC_FAMILYNAME_PAGE || aName == SC_FAMILYNAME_GRAPHIC; +} + +// style::XStyleLoader + +void SAL_CALL ScStyleFamiliesObj::loadStylesFromURL( const OUString& aURL, + const uno::Sequence<beans::PropertyValue>& aOptions ) +{ + //! use aOptions (like Writer) + //! set flag to disable filter option dialogs when importing + + OUString aFilter; // empty - detect + OUString aFiltOpt; + uno::Reference<io::XInputStream> xInputStream; + if (aURL == "private:stream") + { + for (const auto& rProp : aOptions) + { + if (rProp.Name == "InputStream") + { + rProp.Value >>= xInputStream; + if (!xInputStream.is()) + { + throw lang::IllegalArgumentException( + "Parameter 'InputStream' could not be converted " + "to type 'com::sun::star::io::XInputStream'", + nullptr, 0); + } + break; + } + } + } + + ScDocumentLoader aLoader( aURL, aFilter, aFiltOpt, 0, nullptr, xInputStream ); + + ScDocShell* pSource = aLoader.GetDocShell(); + + loadStylesFromDocShell(pSource, aOptions); +} + +uno::Sequence<beans::PropertyValue> SAL_CALL ScStyleFamiliesObj::getStyleLoaderOptions() +{ + // return defaults for options (?) + return comphelper::InitPropertySequence({ + { SC_UNONAME_OVERWSTL, uno::Any(true) }, + { SC_UNONAME_LOADCELL, uno::Any(true) }, + { SC_UNONAME_LOADPAGE, uno::Any(true) } + }); +} + +// style::XStyleLoader2 + +void SAL_CALL ScStyleFamiliesObj::loadStylesFromDocument( const uno::Reference < lang::XComponent > & aSourceComponent, + const uno::Sequence<beans::PropertyValue>& aOptions ) +{ + // Source document docShell + if ( !aSourceComponent.is() ) + throw uno::RuntimeException(); + + ScDocShell* pDocShellSrc = dynamic_cast<ScDocShell*> (SfxObjectShell::GetShellFromComponent(aSourceComponent)); + + loadStylesFromDocShell(pDocShellSrc, aOptions); +} + +// private + +void ScStyleFamiliesObj::loadStylesFromDocShell( ScDocShell* pSource, + const uno::Sequence<beans::PropertyValue>& aOptions ) +{ + + if ( !(pSource && pDocShell) ) + return; + + // collect options + + bool bLoadReplace = true; // defaults + bool bLoadCellStyles = true; + bool bLoadPageStyles = true; + + for (const beans::PropertyValue& rProp : aOptions) + { + OUString aPropName(rProp.Name); + + if (aPropName == SC_UNONAME_OVERWSTL) + bLoadReplace = ScUnoHelpFunctions::GetBoolFromAny( rProp.Value ); + else if (aPropName == SC_UNONAME_LOADCELL) + bLoadCellStyles = ScUnoHelpFunctions::GetBoolFromAny( rProp.Value ); + else if (aPropName == SC_UNONAME_LOADPAGE) + bLoadPageStyles = ScUnoHelpFunctions::GetBoolFromAny( rProp.Value ); + } + + pDocShell->LoadStylesArgs( *pSource, bLoadReplace, bLoadCellStyles, bLoadPageStyles ); + pDocShell->SetDocumentModified(); // paint is inside LoadStyles +} + +ScStyleFamilyObj::ScStyleFamilyObj(ScDocShell* pDocSh, SfxStyleFamily eFam) : + pDocShell( pDocSh ), + eFamily( eFam ) +{ + pDocShell->GetDocument().AddUnoObject(*this); +} + +ScStyleFamilyObj::~ScStyleFamilyObj() +{ + SolarMutexGuard g; + + if (pDocShell) + pDocShell->GetDocument().RemoveUnoObject(*this); +} + +void ScStyleFamilyObj::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + // reference update does not matter here + + if ( rHint.GetId() == SfxHintId::Dying ) + { + pDocShell = nullptr; // has become invalid + } +} + +// XStyleFamily + +rtl::Reference<ScStyleObj> ScStyleFamilyObj::GetObjectByIndex_Impl(sal_Int32 nIndex) +{ + if ( pDocShell ) + { + ScDocument& rDoc = pDocShell->GetDocument(); + ScStyleSheetPool* pStylePool = rDoc.GetStyleSheetPool(); + + SfxStyleSheetIterator aIter( pStylePool, eFamily ); + if ( nIndex < aIter.Count() ) + { + SfxStyleSheetBase* pStyle = aIter[nIndex]; + if ( pStyle ) + { + return new ScStyleObj( pDocShell, eFamily, pStyle->GetName() ); + } + } + } + return nullptr; +} + +rtl::Reference<ScStyleObj> ScStyleFamilyObj::GetObjectByName_Impl(const OUString& aName) +{ + if ( pDocShell ) + { + ScDocument& rDoc = pDocShell->GetDocument(); + ScStyleSheetPool* pStylePool = rDoc.GetStyleSheetPool(); + if ( pStylePool->Find( aName, eFamily ) ) + return new ScStyleObj( pDocShell, eFamily, aName ); + } + return nullptr; +} + +void SAL_CALL ScStyleFamilyObj::insertByName( const OUString& aName, const uno::Any& aElement ) +{ + SolarMutexGuard aGuard; + bool bDone = false; + // reflection does not need to be uno::XInterface, can be any interface... + uno::Reference< uno::XInterface > xInterface(aElement, uno::UNO_QUERY); + if ( xInterface.is() ) + { + ScStyleObj* pStyleObj = dynamic_cast<ScStyleObj*>( xInterface.get() ); + if ( pStyleObj && pStyleObj->GetFamily() == eFamily && + !pStyleObj->IsInserted() ) // not yet inserted? + { + OUString aNameStr(ScStyleNameConversion::ProgrammaticToDisplayName( aName, eFamily )); + + ScDocument& rDoc = pDocShell->GetDocument(); + ScStyleSheetPool* pStylePool = rDoc.GetStyleSheetPool(); + + //! DocFunc function ??? + //! Undo ????????????? + + if ( pStylePool->Find( aNameStr, eFamily ) ) // not available yet + throw container::ElementExistException(); + + (void)pStylePool->Make( aNameStr, eFamily, SfxStyleSearchBits::UserDefined ); + + if ( eFamily == SfxStyleFamily::Para && !rDoc.IsImportingXML() ) + rDoc.GetPool()->CellStyleCreated( aNameStr, rDoc ); + + pStyleObj->InitDoc( pDocShell, aNameStr ); // object can be used + + if (!rDoc.IsImportingXML()) + pDocShell->SetDocumentModified(); // new style not used yet + bDone = true; + + } + } + + if (!bDone) + { + // other errors are handled above + throw lang::IllegalArgumentException(); + } +} + +void SAL_CALL ScStyleFamilyObj::replaceByName( const OUString& aName, const uno::Any& aElement ) +{ + SolarMutexGuard aGuard; + //! combine? + removeByName( aName ); + insertByName( aName, aElement ); +} + +void SAL_CALL ScStyleFamilyObj::removeByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + bool bFound = false; + if ( pDocShell ) + { + OUString aString(ScStyleNameConversion::ProgrammaticToDisplayName( aName, eFamily )); + + ScDocument& rDoc = pDocShell->GetDocument(); + ScStyleSheetPool* pStylePool = rDoc.GetStyleSheetPool(); + + //! DocFunc function?? + //! Undo ????????????? + + SfxStyleSheetBase* pStyle = pStylePool->Find( aString, eFamily ); + if (pStyle) + { + bFound = true; + if ( eFamily == SfxStyleFamily::Para ) + { + // like ScViewFunc::RemoveStyleSheetInUse + ScopedVclPtrInstance< VirtualDevice > pVDev; + Point aLogic = pVDev->LogicToPixel(Point(1000,1000), MapMode(MapUnit::MapTwip)); + double nPPTX = aLogic.X() / 1000.0; + double nPPTY = aLogic.Y() / 1000.0; + Fraction aZoom(1,1); + rDoc.StyleSheetChanged( pStyle, false, pVDev, nPPTX, nPPTY, aZoom, aZoom ); + pDocShell->PostPaint( 0,0,0, rDoc.MaxCol(),rDoc.MaxRow(),MAXTAB, PaintPartFlags::Grid|PaintPartFlags::Left ); + pDocShell->SetDocumentModified(); + + pStylePool->Remove( pStyle ); + + //! InvalidateAttribs(); // Bindings-Invalidate + } + else if ( eFamily == SfxStyleFamily::Page ) + { + if ( rDoc.RemovePageStyleInUse( aString ) ) + pDocShell->PageStyleModified( ScResId(STR_STYLENAME_STANDARD), true ); + + pStylePool->Remove( pStyle ); + + SfxBindings* pBindings = pDocShell->GetViewBindings(); + if (pBindings) + pBindings->Invalidate( SID_STYLE_FAMILY4 ); + pDocShell->SetDocumentModified(); + } + else + { + pStylePool->Remove( pStyle ); + + SfxBindings* pBindings = pDocShell->GetViewBindings(); + if (pBindings) + pBindings->Invalidate( SID_STYLE_FAMILY3 ); + pDocShell->SetDocumentModified(); + } + } + } + + if (!bFound) + throw container::NoSuchElementException(); +} + +// container::XIndexAccess + +sal_Int32 SAL_CALL ScStyleFamilyObj::getCount() +{ + SolarMutexGuard aGuard; + if ( pDocShell ) + { + ScDocument& rDoc = pDocShell->GetDocument(); + ScStyleSheetPool* pStylePool = rDoc.GetStyleSheetPool(); + + SfxStyleSheetIterator aIter( pStylePool, eFamily ); + return aIter.Count(); + } + return 0; +} + +uno::Any SAL_CALL ScStyleFamilyObj::getByIndex( sal_Int32 nIndex ) +{ + SolarMutexGuard aGuard; + uno::Reference< style::XStyle > xObj(GetObjectByIndex_Impl(nIndex)); + if (!xObj.is()) + throw lang::IndexOutOfBoundsException(); + + return uno::Any(xObj); +} + +uno::Type SAL_CALL ScStyleFamilyObj::getElementType() +{ + return cppu::UnoType<style::XStyle>::get(); // has to fit to getByIndex +} + +sal_Bool SAL_CALL ScStyleFamilyObj::hasElements() +{ + SolarMutexGuard aGuard; + return ( getCount() != 0 ); +} + +// container::XNameAccess + +uno::Any SAL_CALL ScStyleFamilyObj::getByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + uno::Reference< style::XStyle > xObj( + GetObjectByName_Impl( ScStyleNameConversion::ProgrammaticToDisplayName( aName, eFamily ) )); + if (!xObj.is()) + throw container::NoSuchElementException(); + + return uno::Any(xObj); +} + +uno::Sequence<OUString> SAL_CALL ScStyleFamilyObj::getElementNames() +{ + SolarMutexGuard aGuard; + if ( pDocShell ) + { + ScDocument& rDoc = pDocShell->GetDocument(); + ScStyleSheetPool* pStylePool = rDoc.GetStyleSheetPool(); + + SfxStyleSheetIterator aIter( pStylePool, eFamily ); + sal_uInt16 nCount = aIter.Count(); + + uno::Sequence<OUString> aSeq(nCount); + OUString* pAry = aSeq.getArray(); + SfxStyleSheetBase* pStyle = aIter.First(); + sal_uInt16 nPos = 0; + while (pStyle) + { + OSL_ENSURE( nPos < nCount, "Count is wrong" ); + if (nPos < nCount) + pAry[nPos++] = ScStyleNameConversion::DisplayToProgrammaticName( + pStyle->GetName(), eFamily ); + pStyle = aIter.Next(); + } + return aSeq; + } + return uno::Sequence<OUString>(); +} + +sal_Bool SAL_CALL ScStyleFamilyObj::hasByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + if ( pDocShell ) + { + OUString aString(ScStyleNameConversion::ProgrammaticToDisplayName( aName, eFamily )); + + ScDocument& rDoc = pDocShell->GetDocument(); + ScStyleSheetPool* pStylePool = rDoc.GetStyleSheetPool(); + if ( pStylePool->Find( aString, eFamily ) ) + return true; + } + return false; +} + +// XPropertySet + +uno::Reference< beans::XPropertySetInfo > SAL_CALL ScStyleFamilyObj::getPropertySetInfo( ) +{ + OSL_FAIL( "###unexpected!" ); + return uno::Reference< beans::XPropertySetInfo >(); +} + +void SAL_CALL ScStyleFamilyObj::setPropertyValue( const OUString&, const uno::Any& ) +{ + OSL_FAIL( "###unexpected!" ); +} + +uno::Any SAL_CALL ScStyleFamilyObj::getPropertyValue( const OUString& sPropertyName ) +{ + uno::Any aRet; + + if ( sPropertyName != "DisplayName" ) + { + throw beans::UnknownPropertyException( "unknown property: " + sPropertyName, getXWeak() ); + } + + SolarMutexGuard aGuard; + TranslateId pResId; + switch ( eFamily ) + { + case SfxStyleFamily::Para: + pResId = STR_STYLE_FAMILY_CELL; break; + case SfxStyleFamily::Page: + pResId = STR_STYLE_FAMILY_PAGE; break; + case SfxStyleFamily::Frame: + pResId = STR_STYLE_FAMILY_GRAPHICS; break; + default: + OSL_FAIL( "ScStyleFamilyObj::getPropertyValue(): invalid family" ); + } + if (pResId) + { + OUString sDisplayName(ScResId(pResId)); + aRet <<= sDisplayName; + } + + return aRet; +} + +void SAL_CALL ScStyleFamilyObj::addPropertyChangeListener( const OUString&, const uno::Reference< beans::XPropertyChangeListener >& ) +{ + OSL_FAIL( "###unexpected!" ); +} + +void SAL_CALL ScStyleFamilyObj::removePropertyChangeListener( const OUString&, const uno::Reference< beans::XPropertyChangeListener >& ) +{ + OSL_FAIL( "###unexpected!" ); +} + +void SAL_CALL ScStyleFamilyObj::addVetoableChangeListener( const OUString&, const uno::Reference< beans::XVetoableChangeListener >& ) +{ + OSL_FAIL( "###unexpected!" ); +} + +void SAL_CALL ScStyleFamilyObj::removeVetoableChangeListener( const OUString&, const uno::Reference< beans::XVetoableChangeListener >& ) +{ + OSL_FAIL( "###unexpected!" ); +} + +// default ctor is needed for reflection + +ScStyleObj::ScStyleObj(ScDocShell* pDocSh, SfxStyleFamily eFam, OUString aName) + : pDocShell(pDocSh) + , eFamily(eFam) + , aStyleName(std::move(aName)) + , pStyle_cached(nullptr) +{ + if (eFam == SfxStyleFamily::Para) + pPropSet = lcl_GetCellStyleSet(); + else if (eFam == SfxStyleFamily::Page) + pPropSet = lcl_GetPageStyleSet(); + else + pPropSet = lcl_GetGraphicStyleSet(); + + // if create by ServiceProvider then pDocShell is NULL + + if (pDocShell) + pDocShell->GetDocument().AddUnoObject(*this); +} + +void ScStyleObj::InitDoc( ScDocShell* pNewDocSh, const OUString& rNewName ) +{ + if ( pNewDocSh && !pDocShell ) + { + aStyleName = rNewName; + pDocShell = pNewDocSh; + pDocShell->GetDocument().AddUnoObject(*this); + } +} + +ScStyleObj::~ScStyleObj() +{ + SolarMutexGuard g; + + if (pDocShell) + pDocShell->GetDocument().RemoveUnoObject(*this); +} + +void ScStyleObj::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + // reference update does not matter here + + if ( rHint.GetId() == SfxHintId::Dying ) + { + pDocShell = nullptr; // has become invalid + } +} + +SfxStyleSheetBase* ScStyleObj::GetStyle_Impl( bool bUseCachedValue ) +{ + if ( bUseCachedValue ) + return pStyle_cached; + + pStyle_cached = nullptr; + if ( pDocShell ) + { + ScDocument& rDoc = pDocShell->GetDocument(); + ScStyleSheetPool* pStylePool = rDoc.GetStyleSheetPool(); + pStyle_cached = pStylePool->Find( aStyleName, eFamily ); + } + return pStyle_cached; +} + +// style::XStyle + +sal_Bool SAL_CALL ScStyleObj::isUserDefined() +{ + SolarMutexGuard aGuard; + SfxStyleSheetBase* pStyle = GetStyle_Impl(); + if (pStyle) + return pStyle->IsUserDefined(); + return false; +} + +sal_Bool SAL_CALL ScStyleObj::isInUse() +{ + SolarMutexGuard aGuard; + SfxStyleSheetBase* pStyle = GetStyle_Impl(); + if (pStyle) + return pStyle->IsUsed(); + return false; +} + +OUString SAL_CALL ScStyleObj::getParentStyle() +{ + SolarMutexGuard aGuard; + SfxStyleSheetBase* pStyle = GetStyle_Impl(); + if (pStyle) + return ScStyleNameConversion::DisplayToProgrammaticName( pStyle->GetParent(), eFamily ); + return OUString(); +} + +void SAL_CALL ScStyleObj::setParentStyle( const OUString& rParentStyle ) +{ + SolarMutexGuard aGuard; + SfxStyleSheetBase* pStyle = GetStyle_Impl(); + if (!pStyle) + return; + + // cell styles cannot be modified if any sheet is protected + if ( eFamily == SfxStyleFamily::Para && lcl_AnyTabProtected( pDocShell->GetDocument() ) ) + return; //! exception? + + //! DocFunc function?? + //! Undo ????????????? + + OUString aString(ScStyleNameConversion::ProgrammaticToDisplayName( rParentStyle, eFamily )); + bool bOk = pStyle->SetParent( aString ); + if (!bOk) + return; + + // as by setPropertyValue + + ScDocument& rDoc = pDocShell->GetDocument(); + if ( eFamily == SfxStyleFamily::Para ) + { + // update line height + + ScopedVclPtrInstance< VirtualDevice > pVDev; + Point aLogic = pVDev->LogicToPixel( Point(1000,1000), MapMode(MapUnit::MapTwip)); + double nPPTX = aLogic.X() / 1000.0; + double nPPTY = aLogic.Y() / 1000.0; + Fraction aZoom(1,1); + rDoc.StyleSheetChanged( pStyle, false, pVDev, nPPTX, nPPTY, aZoom, aZoom ); + + if (!rDoc.IsImportingXML()) + { + pDocShell->PostPaint( 0,0,0, rDoc.MaxCol(),rDoc.MaxRow(),MAXTAB, PaintPartFlags::Grid|PaintPartFlags::Left ); + pDocShell->SetDocumentModified(); + } + } + else if ( eFamily == SfxStyleFamily::Page ) + { + //! ModifyStyleSheet on document (save old values) + + pDocShell->PageStyleModified( aStyleName, true ); + } + else + static_cast<SfxStyleSheet*>(GetStyle_Impl())->Broadcast(SfxHint(SfxHintId::DataChanged)); +} + +// container::XNamed + +OUString SAL_CALL ScStyleObj::getName() +{ + SolarMutexGuard aGuard; + SfxStyleSheetBase* pStyle = GetStyle_Impl(); + if (pStyle) + return ScStyleNameConversion::DisplayToProgrammaticName( pStyle->GetName(), eFamily ); + return OUString(); +} + +void SAL_CALL ScStyleObj::setName( const OUString& aNewName ) +{ + SolarMutexGuard aGuard; + SfxStyleSheetBase* pStyle = GetStyle_Impl(); + if (!pStyle) + return; + + // cell styles cannot be renamed if any sheet is protected + if ( eFamily == SfxStyleFamily::Para && lcl_AnyTabProtected( pDocShell->GetDocument() ) ) + return; //! exception? + + //! DocFunc function?? + //! Undo ????????????? + + bool bOk = pStyle->SetName( aNewName ); + if (!bOk) + return; + + aStyleName = aNewName; //! notify other objects for this style? + + ScDocument& rDoc = pDocShell->GetDocument(); + if ( eFamily == SfxStyleFamily::Para && !rDoc.IsImportingXML() ) + rDoc.GetPool()->CellStyleCreated( aNewName, rDoc ); + + // cell styles = 2, drawing styles = 3, page styles = 4 + sal_uInt16 nId = eFamily == SfxStyleFamily::Para ? SID_STYLE_FAMILY2 : + (eFamily == SfxStyleFamily::Page ? SID_STYLE_FAMILY4 : SID_STYLE_FAMILY3); + SfxBindings* pBindings = pDocShell->GetViewBindings(); + if (pBindings) + { + pBindings->Invalidate( nId ); + pBindings->Invalidate( SID_STYLE_APPLY ); + } +} + +uno::Reference<container::XIndexReplace> ScStyleObj::CreateEmptyNumberingRules() +{ + SvxNumRule aRule( SvxNumRuleFlags::NONE, 0, true ); // nothing supported + return SvxCreateNumRule( aRule ); +} + +// beans::XPropertyState + +const SfxItemSet* ScStyleObj::GetStyleItemSet_Impl( std::u16string_view rPropName, + const SfxItemPropertyMapEntry*& rpResultEntry ) +{ + SfxStyleSheetBase* pStyle = GetStyle_Impl( true ); + if ( pStyle ) + { + const SfxItemPropertyMapEntry* pEntry = nullptr; + if ( eFamily == SfxStyleFamily::Page ) + { + pEntry = lcl_GetHeaderStyleMap()->getByName( rPropName ); + if ( pEntry ) // only item-WIDs in header/footer map + { + rpResultEntry = pEntry; + return &pStyle->GetItemSet().Get(ATTR_PAGE_HEADERSET).GetItemSet(); + } + pEntry = lcl_GetFooterStyleMap()->getByName( rPropName ); + if ( pEntry ) // only item-WIDs in header/footer map + { + rpResultEntry = pEntry; + return &pStyle->GetItemSet().Get(ATTR_PAGE_FOOTERSET).GetItemSet(); + } + } + pEntry = pPropSet->getPropertyMap().getByName( rPropName ); + if ( pEntry ) + { + rpResultEntry = pEntry; + return &pStyle->GetItemSet(); + } + } + + rpResultEntry = nullptr; + return nullptr; +} + +beans::PropertyState ScStyleObj::getPropertyState_Impl( std::u16string_view aPropertyName ) +{ + beans::PropertyState eRet = beans::PropertyState_DIRECT_VALUE; + + const SfxItemPropertyMapEntry* pResultEntry = nullptr; + const SfxItemSet* pItemSet = GetStyleItemSet_Impl( aPropertyName, pResultEntry ); + + if ( pItemSet && pResultEntry ) + { + sal_uInt16 nWhich = pResultEntry->nWID; + if ( nWhich == SC_WID_UNO_TBLBORD || nWhich == SC_WID_UNO_TBLBORD2 ) + { + nWhich = ATTR_BORDER; + } + if ( nWhich == OWN_ATTR_FILLBMP_MODE ) + { + if ( pItemSet->GetItemState( XATTR_FILLBMP_STRETCH, false ) == SfxItemState::SET || + pItemSet->GetItemState( XATTR_FILLBMP_TILE, false ) == SfxItemState::SET ) + { + eRet = beans::PropertyState_DIRECT_VALUE; + } + else + { + eRet = beans::PropertyState_AMBIGUOUS_VALUE; + } + } + else if ( nWhich == SDRATTR_TEXTDIRECTION ) + { + eRet = beans::PropertyState_DEFAULT_VALUE; + } + else if ( IsScItemWid( nWhich ) || eFamily == SfxStyleFamily::Frame ) + { + SfxItemState eState = pItemSet->GetItemState( nWhich, false ); + +// // if no rotate value is set, look at orientation +// //! also for a fixed value of 0 (in case orientation is ambiguous)? +// if ( nWhich == ATTR_ROTATE_VALUE && eState == SfxItemState::DEFAULT ) +// eState = pItemSet->GetItemState( ATTR_ORIENTATION, sal_False ); + + if ( eState == SfxItemState::SET ) + eRet = beans::PropertyState_DIRECT_VALUE; + else if ( eState == SfxItemState::DEFAULT ) + eRet = beans::PropertyState_DEFAULT_VALUE; + else + { + assert(eFamily == SfxStyleFamily::Frame); + eRet = beans::PropertyState_AMBIGUOUS_VALUE; + } + } + } + return eRet; +} + +beans::PropertyState SAL_CALL ScStyleObj::getPropertyState( const OUString& aPropertyName ) +{ + SolarMutexGuard aGuard; + GetStyle_Impl(); + + return getPropertyState_Impl( aPropertyName ); +} + +uno::Sequence<beans::PropertyState> SAL_CALL ScStyleObj::getPropertyStates( const uno::Sequence<OUString>& aPropertyNames ) +{ + SolarMutexGuard aGuard; + GetStyle_Impl(); + + uno::Sequence<beans::PropertyState> aRet( aPropertyNames.getLength() ); + std::transform(aPropertyNames.begin(), aPropertyNames.end(), aRet.getArray(), + [this](const OUString& rName) -> beans::PropertyState { return getPropertyState_Impl(rName); }); + return aRet; +} + +void SAL_CALL ScStyleObj::setPropertyToDefault( const OUString& aPropertyName ) +{ + SolarMutexGuard aGuard; + GetStyle_Impl(); + + const SfxItemPropertyMapEntry* pEntry = pPropSet->getPropertyMap().getByName( aPropertyName ); + if ( !pEntry ) + throw beans::UnknownPropertyException(aPropertyName); + + setPropertyValue_Impl( aPropertyName, pEntry, nullptr ); +} + +uno::Any ScStyleObj::getPropertyDefault_Impl( std::u16string_view aPropertyName ) +{ + uno::Any aAny; + + const SfxItemPropertyMapEntry* pResultEntry = nullptr; + const SfxItemSet* pStyleSet = GetStyleItemSet_Impl( aPropertyName, pResultEntry ); + + if ( pStyleSet && pResultEntry ) + { + sal_uInt16 nWhich = pResultEntry->nWID; + + if ( IsScItemWid( nWhich ) ) + { + // Default is default from ItemPool, not from Standard-Style, + // so it is the same as in setPropertyToDefault + SfxItemSet aEmptySet( *pStyleSet->GetPool(), pStyleSet->GetRanges() ); + // default items with wrong Slot-ID are not functional in SfxItemPropertySet3 + //! change Slot-IDs... + if ( aEmptySet.GetPool()->GetSlotId(nWhich) == nWhich && + aEmptySet.GetItemState(nWhich, false) == SfxItemState::DEFAULT ) + { + aEmptySet.Put( aEmptySet.Get( nWhich ) ); + } + const SfxItemSet* pItemSet = &aEmptySet; + + switch ( nWhich ) // special item handling + { + case ATTR_VALUE_FORMAT: + // default has no language set + aAny <<= sal_Int32( static_cast<const SfxUInt32Item&>(pItemSet->Get(nWhich)).GetValue() ); + break; + case ATTR_INDENT: + aAny <<= sal_Int16( convertTwipToMm100(static_cast<const ScIndentItem&>( + pItemSet->Get(nWhich)).GetValue()) ); + break; + case ATTR_PAGE_SCALE: + case ATTR_PAGE_SCALETOPAGES: + case ATTR_PAGE_FIRSTPAGENO: + aAny <<= sal_Int16( static_cast<const SfxUInt16Item&>(pItemSet->Get(nWhich)).GetValue() ); + break; + case ATTR_PAGE_CHARTS: + case ATTR_PAGE_OBJECTS: + case ATTR_PAGE_DRAWINGS: + //! define sal_Bool-MID for ScViewObjectModeItem? + aAny <<= static_cast<const ScViewObjectModeItem&>(pItemSet->Get(nWhich)).GetValue() == VOBJ_MODE_SHOW; + break; + case ATTR_PAGE_SCALETO: + { + const ScPageScaleToItem aItem(static_cast<const ScPageScaleToItem&>(pItemSet->Get(nWhich))); + if ( aPropertyName == SC_UNO_PAGE_SCALETOX ) + aAny <<= static_cast<sal_Int16>(aItem.GetWidth()); + else + aAny <<= static_cast<sal_Int16>(aItem.GetHeight()); + } + break; + default: + pPropSet->getPropertyValue( *pResultEntry, *pItemSet, aAny ); + } + } + else if ( IsScUnoWid( nWhich ) ) + { + SfxItemSet aEmptySet( *pStyleSet->GetPool(), pStyleSet->GetRanges() ); + const SfxItemSet* pItemSet = &aEmptySet; + switch ( nWhich ) + { + case SC_WID_UNO_TBLBORD: + case SC_WID_UNO_TBLBORD2: + { + const SfxPoolItem& rItem = pItemSet->Get(ATTR_BORDER); + SvxBoxItem aOuter(static_cast<const SvxBoxItem&>(rItem)); + SvxBoxInfoItem aInner(ATTR_BORDER_INNER); + if (nWhich == SC_WID_UNO_TBLBORD2) + ScHelperFunctions::AssignTableBorder2ToAny(aAny, aOuter, aInner, true); + else + ScHelperFunctions::AssignTableBorderToAny(aAny, aOuter, aInner, true); + } + break; + } + } + else if ( nWhich == SDRATTR_TEXTDIRECTION ) + { + aAny <<= false; + } + else if ( nWhich == OWN_ATTR_FILLBMP_MODE ) + { + aAny <<= css::drawing::BitmapMode_REPEAT; + } + else if ( nWhich != OWN_ATTR_TEXTCOLUMNS ) + { + SfxItemSet aItemSet(*pStyleSet->GetPool(), pStyleSet->GetRanges()); + aAny = SvxItemPropertySet_getPropertyValue(pResultEntry, aItemSet); + } + } + return aAny; +} + +uno::Any SAL_CALL ScStyleObj::getPropertyDefault( const OUString& aPropertyName ) +{ + SolarMutexGuard aGuard; + GetStyle_Impl(); + + return getPropertyDefault_Impl( aPropertyName ); +} + +uno::Sequence<uno::Any> SAL_CALL ScStyleObj::getPropertyDefaults( const uno::Sequence<OUString>& aPropertyNames ) +{ + SolarMutexGuard aGuard; + GetStyle_Impl(); + + uno::Sequence<uno::Any> aSequence( aPropertyNames.getLength() ); + std::transform(aPropertyNames.begin(), aPropertyNames.end(), aSequence.getArray(), + [this](const OUString& rName) -> uno::Any { return getPropertyDefault_Impl(rName); }); + return aSequence; +} + +// XMultiPropertySet + +void SAL_CALL ScStyleObj::setPropertyValues( const uno::Sequence< OUString >& aPropertyNames, + const uno::Sequence< uno::Any >& aValues ) +{ + SolarMutexGuard aGuard; + GetStyle_Impl(); + + if ( aValues.getLength() != aPropertyNames.getLength() ) + throw lang::IllegalArgumentException(); + + const OUString* pNames = aPropertyNames.getConstArray(); + const uno::Any* pValues = aValues.getConstArray(); + const SfxItemPropertyMap& rPropertyMap = pPropSet->getPropertyMap(); + for ( sal_Int32 i = 0; i < aPropertyNames.getLength(); i++ ) + { + const SfxItemPropertyMapEntry* pEntry = rPropertyMap.getByName( pNames[i] ); + setPropertyValue_Impl( pNames[i], pEntry, &pValues[i] ); + } +} + +uno::Sequence<uno::Any> SAL_CALL ScStyleObj::getPropertyValues( const uno::Sequence< OUString >& aPropertyNames ) +{ + SolarMutexGuard aGuard; + GetStyle_Impl(); + + uno::Sequence<uno::Any> aSequence( aPropertyNames.getLength() ); + std::transform(aPropertyNames.begin(), aPropertyNames.end(), aSequence.getArray(), + [this](const OUString& rName) -> uno::Any { return getPropertyValue_Impl(rName); }); + return aSequence; +} + +void SAL_CALL ScStyleObj::addPropertiesChangeListener( const uno::Sequence<OUString>& /* aPropertyNames */, + const uno::Reference<beans::XPropertiesChangeListener>& /* xListener */ ) +{ + // no bound properties +} + +void SAL_CALL ScStyleObj::removePropertiesChangeListener( + const uno::Reference<beans::XPropertiesChangeListener>& /* xListener */ ) +{ + // no bound properties +} + +void SAL_CALL ScStyleObj::firePropertiesChangeEvent( const uno::Sequence<OUString>& /* aPropertyNames */, + const uno::Reference<beans::XPropertiesChangeListener>& /* xListener */ ) +{ + // no bound properties +} + +// XMultiPropertyStates +// getPropertyStates already defined for XPropertyState + +void SAL_CALL ScStyleObj::setAllPropertiesToDefault() +{ + SolarMutexGuard aGuard; + + SfxStyleSheetBase* pStyle = GetStyle_Impl(); + if ( !pStyle ) + return; + + // cell styles cannot be modified if any sheet is protected + if ( eFamily == SfxStyleFamily::Para && lcl_AnyTabProtected( pDocShell->GetDocument() ) ) + throw uno::RuntimeException(); + + SfxItemSet& rSet = pStyle->GetItemSet(); + rSet.ClearItem(); // set all items to default + + //! merge with SetOneProperty + + ScDocument& rDoc = pDocShell->GetDocument(); + if ( eFamily == SfxStyleFamily::Para ) + { + // row heights + + ScopedVclPtrInstance< VirtualDevice > pVDev; + Point aLogic = pVDev->LogicToPixel(Point(1000,1000), MapMode(MapUnit::MapTwip)); + double nPPTX = aLogic.X() / 1000.0; + double nPPTY = aLogic.Y() / 1000.0; + Fraction aZoom(1,1); + rDoc.StyleSheetChanged( pStyle, false, pVDev, nPPTX, nPPTY, aZoom, aZoom ); + + if (!rDoc.IsImportingXML()) + { + pDocShell->PostPaint( 0,0,0, rDoc.MaxCol(),rDoc.MaxRow(),MAXTAB, PaintPartFlags::Grid|PaintPartFlags::Left ); + pDocShell->SetDocumentModified(); + } + } + else if ( eFamily == SfxStyleFamily::Page ) + { + // #i22448# apply the default BoxInfoItem for page styles again + // (same content as in ScStyleSheet::GetItemSet, to control the dialog) + SvxBoxInfoItem aBoxInfoItem( ATTR_BORDER_INNER ); + aBoxInfoItem.SetTable( false ); + aBoxInfoItem.SetDist( true ); + aBoxInfoItem.SetValid( SvxBoxInfoItemValidFlags::DISTANCE ); + rSet.Put( aBoxInfoItem ); + + pDocShell->PageStyleModified( aStyleName, true ); + } + else + static_cast<SfxStyleSheet*>(GetStyle_Impl())->Broadcast(SfxHint(SfxHintId::DataChanged)); +} + +void SAL_CALL ScStyleObj::setPropertiesToDefault( const uno::Sequence<OUString>& aPropertyNames ) +{ + SolarMutexGuard aGuard; + GetStyle_Impl(); + + const SfxItemPropertyMap& rPropertyMap = pPropSet->getPropertyMap(); + for ( const OUString& rName : aPropertyNames ) + { + const SfxItemPropertyMapEntry* pEntry = rPropertyMap.getByName( rName ); + setPropertyValue_Impl( rName, pEntry, nullptr ); + } +} + +// beans::XPropertySet + +uno::Reference<beans::XPropertySetInfo> SAL_CALL ScStyleObj::getPropertySetInfo() +{ + SolarMutexGuard aGuard; + return pPropSet->getPropertySetInfo(); +} + +void SAL_CALL ScStyleObj::setPropertyValue( const OUString& aPropertyName, const uno::Any& aValue ) +{ + SolarMutexGuard aGuard; + GetStyle_Impl(); + + const SfxItemPropertyMapEntry* pEntry = pPropSet->getPropertyMap().getByName( aPropertyName ); + if ( !pEntry ) + throw beans::UnknownPropertyException(aPropertyName); + + setPropertyValue_Impl( aPropertyName, pEntry, &aValue ); +} + +void ScStyleObj::setPropertyValue_Impl( std::u16string_view rPropertyName, const SfxItemPropertyMapEntry* pEntry, const uno::Any* pValue ) +{ + SfxStyleSheetBase* pStyle = GetStyle_Impl( true ); + if ( !(pStyle && pEntry) ) + return; + + // cell styles cannot be modified if any sheet is protected + if ( eFamily == SfxStyleFamily::Para && lcl_AnyTabProtected( pDocShell->GetDocument() ) ) + throw uno::RuntimeException(); + + SfxItemSet& rSet = pStyle->GetItemSet(); // change directly in active Style + bool bDone = false; + if ( eFamily == SfxStyleFamily::Page ) + { + if(pEntry->nWID == SC_WID_UNO_HEADERSET) + { + const SfxItemPropertyMapEntry* pHeaderEntry = lcl_GetHeaderStyleMap()->getByName( rPropertyName ); + if ( pHeaderEntry ) // only item-WIDs in header/footer map + { + SvxSetItem aNewHeader( rSet.Get(ATTR_PAGE_HEADERSET) ); + if (pValue) + pPropSet->setPropertyValue( *pHeaderEntry, *pValue, aNewHeader.GetItemSet() ); + else + aNewHeader.GetItemSet().ClearItem( pHeaderEntry->nWID ); + rSet.Put( aNewHeader ); + bDone = true; + } + } + else if(pEntry->nWID == SC_WID_UNO_FOOTERSET) + { + const SfxItemPropertyMapEntry* pFooterEntry = lcl_GetFooterStyleMap()->getByName( rPropertyName ); + if ( pFooterEntry ) // only item-WIDs in header/footer map + { + SvxSetItem aNewFooter( rSet.Get(ATTR_PAGE_FOOTERSET) ); + if (pValue) + pPropSet->setPropertyValue( *pFooterEntry, *pValue, aNewFooter.GetItemSet() ); + else + aNewFooter.GetItemSet().ClearItem( pFooterEntry->nWID ); + rSet.Put( aNewFooter ); + bDone = true; + } + } + } + if (!bDone) + { + if (IsScItemWid(pEntry->nWID)) + { + if (pValue) + { + switch (pEntry->nWID) // special item handling + { + case ATTR_VALUE_FORMAT: + { + // language for number formats + SvNumberFormatter* pFormatter + = pDocShell->GetDocument().GetFormatTable(); + sal_uInt32 nOldFormat = rSet.Get(ATTR_VALUE_FORMAT).GetValue(); + LanguageType eOldLang + = rSet.Get(ATTR_LANGUAGE_FORMAT).GetLanguage(); + pFormatter->GetFormatForLanguageIfBuiltIn(nOldFormat, eOldLang); + + sal_uInt32 nNewFormat = 0; + *pValue >>= nNewFormat; + rSet.Put(SfxUInt32Item(ATTR_VALUE_FORMAT, nNewFormat)); + + const SvNumberformat* pNewEntry = pFormatter->GetEntry(nNewFormat); + LanguageType eNewLang + = pNewEntry ? pNewEntry->GetLanguage() : LANGUAGE_DONTKNOW; + if (eNewLang != eOldLang && eNewLang != LANGUAGE_DONTKNOW) + rSet.Put(SvxLanguageItem(eNewLang, ATTR_LANGUAGE_FORMAT)); + + //! keep default state of number format if only language changed? + } + break; + case ATTR_INDENT: + { + sal_Int16 nVal = 0; + *pValue >>= nVal; + rSet.Put(ScIndentItem(o3tl::toTwips(nVal, o3tl::Length::mm100))); + } + break; + case ATTR_ROTATE_VALUE: + { + sal_Int32 nRotVal = 0; + if (*pValue >>= nRotVal) + { + // stored value is always between 0 and 360 deg. + nRotVal %= 36000; + if (nRotVal < 0) + nRotVal += 36000; + rSet.Put(ScRotateValueItem(Degree100(nRotVal))); + } + } + break; + case ATTR_STACKED: + { + table::CellOrientation eOrient; + if (*pValue >>= eOrient) + { + switch (eOrient) + { + case table::CellOrientation_STANDARD: + rSet.Put(ScVerticalStackCell(false)); + break; + case table::CellOrientation_TOPBOTTOM: + rSet.Put(ScVerticalStackCell(false)); + rSet.Put(ScRotateValueItem(27000_deg100)); + break; + case table::CellOrientation_BOTTOMTOP: + rSet.Put(ScVerticalStackCell(false)); + rSet.Put(ScRotateValueItem(9000_deg100)); + break; + case table::CellOrientation_STACKED: + rSet.Put(ScVerticalStackCell(true)); + break; + default: + { + // added to avoid warnings + } + } + } + } + break; + case ATTR_PAGE_SCALE: + case ATTR_PAGE_SCALETOPAGES: + { + rSet.ClearItem(ATTR_PAGE_SCALETOPAGES); + rSet.ClearItem(ATTR_PAGE_SCALE); + rSet.ClearItem(ATTR_PAGE_SCALETO); + sal_Int16 nVal = 0; + *pValue >>= nVal; + rSet.Put(SfxUInt16Item(pEntry->nWID, nVal)); + } + break; + case ATTR_PAGE_FIRSTPAGENO: + { + sal_Int16 nVal = 0; + *pValue >>= nVal; + rSet.Put(SfxUInt16Item(ATTR_PAGE_FIRSTPAGENO, nVal)); + } + break; + case ATTR_PAGE_CHARTS: + case ATTR_PAGE_OBJECTS: + case ATTR_PAGE_DRAWINGS: + { + bool bBool = false; + *pValue >>= bBool; + //! need to define sal_Bool-MID for ScViewObjectModeItem? + rSet.Put(ScViewObjectModeItem( + pEntry->nWID, bBool ? VOBJ_MODE_SHOW : VOBJ_MODE_HIDE)); + } + break; + case ATTR_PAGE_PAPERBIN: + { + sal_uInt8 nTray = PAPERBIN_PRINTER_SETTINGS; + bool bFound = false; + + OUString aName; + if (*pValue >>= aName) + { + if (aName == SC_PAPERBIN_DEFAULTNAME) + bFound = true; + else + { + Printer* pPrinter = pDocShell->GetPrinter(); + if (pPrinter) + { + const sal_uInt16 nCount = pPrinter->GetPaperBinCount(); + for (sal_uInt16 i = 0; i < nCount; i++) + if (aName == pPrinter->GetPaperBinName(i)) + { + nTray = static_cast<sal_uInt8>(i); + bFound = true; + break; + } + } + } + } + if (!bFound) + throw lang::IllegalArgumentException(); + + rSet.Put(SvxPaperBinItem(ATTR_PAGE_PAPERBIN, nTray)); + + } + break; + case ATTR_PAGE_SCALETO: + { + sal_Int16 nPages = 0; + if (*pValue >>= nPages) + { + ScPageScaleToItem aItem = rSet.Get(ATTR_PAGE_SCALETO); + if (rPropertyName == SC_UNO_PAGE_SCALETOX) + aItem.SetWidth(static_cast<sal_uInt16>(nPages)); + else + aItem.SetHeight(static_cast<sal_uInt16>(nPages)); + rSet.Put(aItem); + rSet.ClearItem(ATTR_PAGE_SCALETOPAGES); + rSet.ClearItem(ATTR_PAGE_SCALE); + } + } + break; + case ATTR_HIDDEN: + { + bool bHidden = false; + if (*pValue >>= bHidden) + pStyle->SetHidden(bHidden); + } + break; + default: + // default items with wrong Slot-ID are not working in SfxItemPropertySet3 + //! change Slot-IDs... + if (rSet.GetPool()->GetSlotId(pEntry->nWID) == pEntry->nWID + && rSet.GetItemState(pEntry->nWID, false) == SfxItemState::DEFAULT) + { + rSet.Put(rSet.Get(pEntry->nWID)); + } + pPropSet->setPropertyValue(*pEntry, *pValue, rSet); + } + } + else + { + rSet.ClearItem(pEntry->nWID); + // language for number formats + if (pEntry->nWID == ATTR_VALUE_FORMAT) + rSet.ClearItem(ATTR_LANGUAGE_FORMAT); + + //! for ATTR_ROTATE_VALUE, also reset ATTR_ORIENTATION? + } + } + else if (IsScUnoWid(pEntry->nWID)) + { + switch (pEntry->nWID) + { + case SC_WID_UNO_TBLBORD: + { + if (pValue) + { + table::TableBorder aBorder; + if (*pValue >>= aBorder) + { + SvxBoxItem aOuter(ATTR_BORDER); + SvxBoxInfoItem aInner(ATTR_BORDER_INNER); + ScHelperFunctions::FillBoxItems(aOuter, aInner, aBorder); + rSet.Put(aOuter); + } + } + else + { + rSet.ClearItem(ATTR_BORDER); + } + } + break; + case SC_WID_UNO_TBLBORD2: + { + if (pValue) + { + table::TableBorder2 aBorder2; + if (*pValue >>= aBorder2) + { + SvxBoxItem aOuter(ATTR_BORDER); + SvxBoxInfoItem aInner(ATTR_BORDER_INNER); + ScHelperFunctions::FillBoxItems(aOuter, aInner, aBorder2); + rSet.Put(aOuter); + } + } + else + { + rSet.ClearItem(ATTR_BORDER); + } + } + break; + } + } + else if (pEntry->nWID == OWN_ATTR_FILLBMP_MODE) + { + css::drawing::BitmapMode eMode; + if (!pValue) + { + rSet.ClearItem(XATTR_FILLBMP_STRETCH); + rSet.ClearItem(XATTR_FILLBMP_TILE); + } + else if (*pValue >>= eMode) + { + rSet.Put(XFillBmpStretchItem(eMode == css::drawing::BitmapMode_STRETCH)); + rSet.Put(XFillBmpTileItem(eMode == css::drawing::BitmapMode_REPEAT)); + } + } + else if(pEntry->nMemberId == MID_NAME && + (pEntry->nWID == XATTR_FILLBITMAP || pEntry->nWID == XATTR_FILLGRADIENT || + pEntry->nWID == XATTR_FILLHATCH || pEntry->nWID == XATTR_FILLFLOATTRANSPARENCE || + pEntry->nWID == XATTR_LINESTART || pEntry->nWID == XATTR_LINEEND || pEntry->nWID == XATTR_LINEDASH)) + { + OUString aTempName; + if (*pValue >>= aTempName) + SvxShape::SetFillAttribute(pEntry->nWID, aTempName, rSet); + } + else if(pEntry->nWID == SDRATTR_TEXTDIRECTION) + { + return; // not yet implemented for styles + } + else if(!SvxUnoTextRangeBase::SetPropertyValueHelper(pEntry, *pValue, rSet)) + { + SvxItemPropertySet_setPropertyValue(pEntry, *pValue, rSet); + } + } + + //! DocFunc-?? + //! Undo ?? + + if ( eFamily == SfxStyleFamily::Para ) + { + // If we are loading, we can delay line height calculation, because we are going to re-calc all of those + // after load. + if (pDocShell && !pDocShell->IsLoading()) + { + // update line height + ScopedVclPtrInstance< VirtualDevice > pVDev; + Point aLogic = pVDev->LogicToPixel(Point(1000,1000), MapMode(MapUnit::MapTwip)); + double nPPTX = aLogic.X() / 1000.0; + double nPPTY = aLogic.Y() / 1000.0; + Fraction aZoom(1,1); + ScDocument& rDoc = pDocShell->GetDocument(); + rDoc.StyleSheetChanged( pStyle, false, pVDev, nPPTX, nPPTY, aZoom, aZoom ); + + if (!rDoc.IsImportingXML()) + { + pDocShell->PostPaint( 0,0,0, rDoc.MaxCol(),rDoc.MaxRow(),MAXTAB, PaintPartFlags::Grid|PaintPartFlags::Left ); + pDocShell->SetDocumentModified(); + } + } + } + else if ( eFamily == SfxStyleFamily::Page ) + { + //! ModifyStyleSheet on document (save old values) + + pDocShell->PageStyleModified( aStyleName, true ); + } + else + static_cast<SfxStyleSheet*>(GetStyle_Impl())->Broadcast(SfxHint(SfxHintId::DataChanged)); +} + +uno::Any ScStyleObj::getPropertyValue_Impl( std::u16string_view aPropertyName ) +{ + uno::Any aAny; + SfxStyleSheetBase* pStyle = GetStyle_Impl( true ); + + if ( aPropertyName == SC_UNONAME_DISPNAME ) // read-only + { + // core always has the display name + if ( pStyle ) + aAny <<= pStyle->GetName(); + } + else + { + const SfxItemPropertyMapEntry* pResultEntry = nullptr; + const SfxItemSet* pItemSet = GetStyleItemSet_Impl( aPropertyName, pResultEntry ); + + if ( pItemSet && pResultEntry ) + { + sal_uInt16 nWhich = pResultEntry->nWID; + + if ( IsScItemWid( nWhich ) ) + { + switch ( nWhich ) // for special item handling + { + case ATTR_VALUE_FORMAT: + if ( pDocShell ) + { + sal_uInt32 nOldFormat = + pItemSet->Get( ATTR_VALUE_FORMAT ).GetValue(); + LanguageType eOldLang = + pItemSet->Get( ATTR_LANGUAGE_FORMAT ).GetLanguage(); + nOldFormat = pDocShell->GetDocument().GetFormatTable()-> + GetFormatForLanguageIfBuiltIn( nOldFormat, eOldLang ); + aAny <<= nOldFormat; + } + break; + case ATTR_INDENT: + aAny <<= sal_Int16( convertTwipToMm100(static_cast<const ScIndentItem&>( + pItemSet->Get(nWhich)).GetValue()) ); + break; + case ATTR_STACKED: + { + Degree100 nRot = pItemSet->Get(ATTR_ROTATE_VALUE).GetValue(); + bool bStacked = static_cast<const ScVerticalStackCell&>(pItemSet->Get(nWhich)).GetValue(); + SvxOrientationItem( nRot, bStacked, TypedWhichId<SvxOrientationItem>(0) ).QueryValue( aAny ); + } + break; + case ATTR_PAGE_SCALE: + case ATTR_PAGE_SCALETOPAGES: + case ATTR_PAGE_FIRSTPAGENO: + aAny <<= sal_Int16( static_cast<const SfxUInt16Item&>(pItemSet->Get(nWhich)).GetValue() ); + break; + case ATTR_PAGE_CHARTS: + case ATTR_PAGE_OBJECTS: + case ATTR_PAGE_DRAWINGS: + //! define sal_Bool-MID for ScViewObjectModeItem? + aAny <<= static_cast<const ScViewObjectModeItem&>(pItemSet->Get(nWhich)).GetValue() == VOBJ_MODE_SHOW; + break; + case ATTR_PAGE_PAPERBIN: + { + // property PrinterPaperTray is the name of the tray + + sal_uInt8 nValue = static_cast<const SvxPaperBinItem&>(pItemSet->Get(nWhich)).GetValue(); + OUString aName; + if ( nValue == PAPERBIN_PRINTER_SETTINGS ) + aName = SC_PAPERBIN_DEFAULTNAME; + else + { + Printer* pPrinter = pDocShell->GetPrinter(); + if (pPrinter) + aName = pPrinter->GetPaperBinName( nValue ); + } + aAny <<= aName; + } + break; + case ATTR_PAGE_SCALETO: + { + const ScPageScaleToItem& aItem(pItemSet->Get(ATTR_PAGE_SCALETO)); + if ( aPropertyName == SC_UNO_PAGE_SCALETOX ) + aAny <<= static_cast<sal_Int16>(aItem.GetWidth()); + else + aAny <<= static_cast<sal_Int16>(aItem.GetHeight()); + } + break; + case ATTR_HIDDEN: + { + bool bHidden = pStyle && pStyle->IsHidden(); + aAny <<= bHidden; + } + break; + default: + // Default-Items with wrong Slot-ID don't work in SfxItemPropertySet3 + //! change Slot-IDs... + if ( pItemSet->GetPool()->GetSlotId(nWhich) == nWhich && + pItemSet->GetItemState(nWhich, false) == SfxItemState::DEFAULT ) + { + SfxItemSet aNoEmptySet( *pItemSet ); + aNoEmptySet.Put( aNoEmptySet.Get( nWhich ) ); + pPropSet->getPropertyValue( *pResultEntry, aNoEmptySet, aAny ); + } + else + pPropSet->getPropertyValue( *pResultEntry, *pItemSet, aAny ); + } + } + else if ( IsScUnoWid( nWhich ) ) + { + switch ( nWhich ) + { + case SC_WID_UNO_TBLBORD: + case SC_WID_UNO_TBLBORD2: + { + const SfxPoolItem& rItem = pItemSet->Get(ATTR_BORDER); + SvxBoxItem aOuter(static_cast<const SvxBoxItem&>(rItem)); + SvxBoxInfoItem aInner(ATTR_BORDER_INNER); + if (nWhich == SC_WID_UNO_TBLBORD2) + ScHelperFunctions::AssignTableBorder2ToAny(aAny, aOuter, aInner, + true); + else + ScHelperFunctions::AssignTableBorderToAny(aAny, aOuter, aInner, + true); + } + break; + } + } + else if ( nWhich == SDRATTR_TEXTDIRECTION ) + { + aAny <<= false; + } + else if ( nWhich == OWN_ATTR_FILLBMP_MODE ) + { + const XFillBmpStretchItem* pStretchItem = pItemSet->GetItem<XFillBmpStretchItem>(XATTR_FILLBMP_STRETCH); + const XFillBmpTileItem* pTileItem = pItemSet->GetItem<XFillBmpTileItem>(XATTR_FILLBMP_TILE); + + if ( pStretchItem && pTileItem ) + { + if ( pTileItem->GetValue() ) + aAny <<= css::drawing::BitmapMode_REPEAT; + else if ( pStretchItem->GetValue() ) + aAny <<= css::drawing::BitmapMode_STRETCH; + else + aAny <<= css::drawing::BitmapMode_NO_REPEAT; + } + } + else if ( nWhich != OWN_ATTR_TEXTCOLUMNS ) + { + if (!SvxUnoTextRangeBase::GetPropertyValueHelper(*pItemSet, pResultEntry, aAny)) + aAny = SvxItemPropertySet_getPropertyValue(pResultEntry, *pItemSet); + + // since the sfx uint16 item now exports a sal_Int32, we may have to fix this here + if (pResultEntry->aType == ::cppu::UnoType<sal_Int16>::get() && + aAny.getValueType() == ::cppu::UnoType<sal_Int32>::get()) + { + aAny <<= static_cast<sal_Int16>(aAny.get<sal_Int32>()); + } + } + } + } + + return aAny; +} + +uno::Any SAL_CALL ScStyleObj::getPropertyValue( const OUString& aPropertyName ) +{ + SolarMutexGuard aGuard; + GetStyle_Impl(); + + return getPropertyValue_Impl( aPropertyName ); +} + +SC_IMPL_DUMMY_PROPERTY_LISTENER( ScStyleObj ) + +// lang::XServiceInfo + +OUString SAL_CALL ScStyleObj::getImplementationName() +{ + return "ScStyleObj"; +} + +sal_Bool SAL_CALL ScStyleObj::supportsService( const OUString& rServiceName ) +{ + return cppu::supportsService(this, rServiceName); +} + +uno::Sequence<OUString> SAL_CALL ScStyleObj::getSupportedServiceNames() +{ + if (eFamily == SfxStyleFamily::Page) + return {SCSTYLE_SERVICE, SCPAGESTYLE_SERVICE}; + + if (eFamily == SfxStyleFamily::Frame) + return {SCSTYLE_SERVICE, SCGRAPHICSTYLE_SERVICE}; + + return {SCSTYLE_SERVICE, SCCELLSTYLE_SERVICE}; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/unoobj/targuno.cxx b/sc/source/ui/unoobj/targuno.cxx new file mode 100644 index 0000000000..635c918f36 --- /dev/null +++ b/sc/source/ui/unoobj/targuno.cxx @@ -0,0 +1,293 @@ +/* -*- 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 <sal/config.h> + +#include <toolkit/helper/vclunohelper.hxx> +#include <svl/itemprop.hxx> +#include <svl/hint.hxx> +#include <utility> +#include <vcl/svapp.hxx> +#include <osl/diagnose.h> +#include <com/sun/star/awt/XBitmap.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> + +#include <targuno.hxx> +#include <miscuno.hxx> +#include <docuno.hxx> +#include <datauno.hxx> +#include <nameuno.hxx> +#include <docsh.hxx> +#include <content.hxx> +#include <scresid.hxx> +#include <strings.hrc> +#include <bitmaps.hlst> +#include <unonames.hxx> + +using namespace ::com::sun::star; + +const TranslateId aTypeResIds[SC_LINKTARGETTYPE_COUNT] = +{ + SCSTR_CONTENT_TABLE, // SC_LINKTARGETTYPE_SHEET + SCSTR_CONTENT_RANGENAME, // SC_LINKTARGETTYPE_RANGENAME + SCSTR_CONTENT_DBAREA // SC_LINKTARGETTYPE_DBAREA +}; + +static std::span<const SfxItemPropertyMapEntry> lcl_GetLinkTargetMap() +{ + static const SfxItemPropertyMapEntry aLinkTargetMap_Impl[] = + { + { SC_UNO_LINKDISPBIT, 0, cppu::UnoType<awt::XBitmap>::get(), beans::PropertyAttribute::READONLY, 0 }, + { SC_UNO_LINKDISPNAME, 0, cppu::UnoType<OUString>::get(), beans::PropertyAttribute::READONLY, 0 }, + }; + return aLinkTargetMap_Impl; +} + +// service for ScLinkTargetTypeObj is not defined +// must not support document::LinkTarget because the target type cannot be used as a target + +SC_SIMPLE_SERVICE_INFO( ScLinkTargetTypesObj, "ScLinkTargetTypesObj", "com.sun.star.document.LinkTargets" ) +SC_SIMPLE_SERVICE_INFO( ScLinkTargetTypeObj, "ScLinkTargetTypeObj", "com.sun.star.document.LinkTargetSupplier" ) +SC_SIMPLE_SERVICE_INFO( ScLinkTargetsObj, "ScLinkTargetsObj", "com.sun.star.document.LinkTargets" ) + +ScLinkTargetTypesObj::ScLinkTargetTypesObj(ScDocShell* pDocSh) : + pDocShell( pDocSh ) +{ + pDocShell->GetDocument().AddUnoObject(*this); + + for (sal_uInt16 i=0; i<SC_LINKTARGETTYPE_COUNT; i++) + aNames[i] = ScResId(aTypeResIds[i]); +} + +ScLinkTargetTypesObj::~ScLinkTargetTypesObj() +{ + SolarMutexGuard g; + + if (pDocShell) + pDocShell->GetDocument().RemoveUnoObject(*this); +} + +void ScLinkTargetTypesObj::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + if ( rHint.GetId() == SfxHintId::Dying ) + pDocShell = nullptr; // document gone +} + +// container::XNameAccess + +uno::Any SAL_CALL ScLinkTargetTypesObj::getByName(const OUString& aName) +{ + if (pDocShell) + { + for (sal_uInt16 i=0; i<SC_LINKTARGETTYPE_COUNT; i++) + if ( aNames[i] == aName ) + return uno::Any(uno::Reference< beans::XPropertySet >(new ScLinkTargetTypeObj( pDocShell, i ))); + } + + throw container::NoSuchElementException(); +} + +uno::Sequence<OUString> SAL_CALL ScLinkTargetTypesObj::getElementNames() +{ + uno::Sequence<OUString> aRet(SC_LINKTARGETTYPE_COUNT); + OUString* pArray = aRet.getArray(); + for (sal_uInt16 i=0; i<SC_LINKTARGETTYPE_COUNT; i++) + pArray[i] = aNames[i]; + return aRet; +} + +sal_Bool SAL_CALL ScLinkTargetTypesObj::hasByName(const OUString& aName) +{ + for (const auto & i : aNames) + if ( i == aName ) + return true; + return false; +} + +// container::XElementAccess + +uno::Type SAL_CALL ScLinkTargetTypesObj::getElementType() +{ + return cppu::UnoType<beans::XPropertySet>::get(); +} + +sal_Bool SAL_CALL ScLinkTargetTypesObj::hasElements() +{ + return true; +} + +ScLinkTargetTypeObj::ScLinkTargetTypeObj(ScDocShell* pDocSh, sal_uInt16 nT) : + pDocShell( pDocSh ), + nType( nT ) +{ + pDocShell->GetDocument().AddUnoObject(*this); + aName = ScResId(aTypeResIds[nType]); //! on demand? +} + +ScLinkTargetTypeObj::~ScLinkTargetTypeObj() +{ + SolarMutexGuard g; + + if (pDocShell) + pDocShell->GetDocument().RemoveUnoObject(*this); +} + +void ScLinkTargetTypeObj::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + if ( rHint.GetId() == SfxHintId::Dying ) + pDocShell = nullptr; // document gone +} + +// document::XLinkTargetSupplier + +uno::Reference< container::XNameAccess > SAL_CALL ScLinkTargetTypeObj::getLinks() +{ + uno::Reference< container::XNameAccess > xCollection; + + if ( pDocShell ) + { + switch ( nType ) + { + case SC_LINKTARGETTYPE_SHEET: + xCollection.set(new ScTableSheetsObj(pDocShell)); + break; + case SC_LINKTARGETTYPE_RANGENAME: + xCollection.set(new ScGlobalNamedRangesObj(pDocShell)); + break; + case SC_LINKTARGETTYPE_DBAREA: + xCollection.set(new ScDatabaseRangesObj(pDocShell)); + break; + default: + OSL_FAIL("invalid type"); + } + } + + // wrap collection in ScLinkTargetsObj because service document::LinkTargets requires + // beans::XPropertySet as ElementType in container::XNameAccess. + if ( xCollection.is() ) + return new ScLinkTargetsObj( xCollection ); + return nullptr; +} + +// beans::XPropertySet + +uno::Reference< beans::XPropertySetInfo > SAL_CALL ScLinkTargetTypeObj::getPropertySetInfo() +{ + static uno::Reference< beans::XPropertySetInfo > aRef(new SfxItemPropertySetInfo( lcl_GetLinkTargetMap() )); + return aRef; +} + +void SAL_CALL ScLinkTargetTypeObj::setPropertyValue(const OUString& /* aPropertyName */, + const uno::Any& /* aValue */) +{ + // everything is read-only + //! exception? +} + +constexpr OUString aContentBmps[]= +{ + RID_BMP_CONTENT_TABLE, + RID_BMP_CONTENT_RANGENAME, + RID_BMP_CONTENT_DBAREA, + RID_BMP_CONTENT_GRAPHIC, + RID_BMP_CONTENT_OLEOBJECT, + RID_BMP_CONTENT_NOTE, + RID_BMP_CONTENT_AREALINK, + RID_BMP_CONTENT_DRAWING +}; + +void ScLinkTargetTypeObj::SetLinkTargetBitmap( uno::Any& rRet, sal_uInt16 nType ) +{ + ScContentId nImgId = ScContentId::ROOT; + switch ( nType ) + { + case SC_LINKTARGETTYPE_SHEET: + nImgId = ScContentId::TABLE; + break; + case SC_LINKTARGETTYPE_RANGENAME: + nImgId = ScContentId::RANGENAME; + break; + case SC_LINKTARGETTYPE_DBAREA: + nImgId = ScContentId::DBAREA; + break; + } + if (nImgId != ScContentId::ROOT) + { + BitmapEx aBitmapEx { aContentBmps[static_cast<int>(nImgId) -1 ] }; + rRet <<= VCLUnoHelper::CreateBitmap(aBitmapEx); + } +} + +uno::Any SAL_CALL ScLinkTargetTypeObj::getPropertyValue(const OUString& PropertyName) +{ + uno::Any aRet; + if ( PropertyName == SC_UNO_LINKDISPBIT ) + SetLinkTargetBitmap( aRet, nType ); + else if ( PropertyName == SC_UNO_LINKDISPNAME ) + aRet <<= aName; + + return aRet; +} + +SC_IMPL_DUMMY_PROPERTY_LISTENER( ScLinkTargetTypeObj ) + +ScLinkTargetsObj::ScLinkTargetsObj( uno::Reference< container::XNameAccess > xColl ) : + xCollection(std::move( xColl )) +{ + OSL_ENSURE( xCollection.is(), "ScLinkTargetsObj: NULL" ); +} + +ScLinkTargetsObj::~ScLinkTargetsObj() +{ +} + +// container::XNameAccess + +uno::Any SAL_CALL ScLinkTargetsObj::getByName(const OUString& aName) +{ + uno::Reference<beans::XPropertySet> xProp(xCollection->getByName(aName), uno::UNO_QUERY); + if (xProp.is()) + return uno::Any(xProp); + + throw container::NoSuchElementException(); +} + +uno::Sequence<OUString> SAL_CALL ScLinkTargetsObj::getElementNames() +{ + return xCollection->getElementNames(); +} + +sal_Bool SAL_CALL ScLinkTargetsObj::hasByName(const OUString& aName) +{ + return xCollection->hasByName(aName); +} + +// container::XElementAccess + +uno::Type SAL_CALL ScLinkTargetsObj::getElementType() +{ + return cppu::UnoType<beans::XPropertySet>::get(); +} + +sal_Bool SAL_CALL ScLinkTargetsObj::hasElements() +{ + return xCollection->hasElements(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/unoobj/textuno.cxx b/sc/source/ui/unoobj/textuno.cxx new file mode 100644 index 0000000000..6102cdb9fa --- /dev/null +++ b/sc/source/ui/unoobj/textuno.cxx @@ -0,0 +1,869 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <memory> +#include <sal/config.h> +#include <sal/log.hxx> + +#include <scitems.hxx> +#include <editeng/eeitem.hxx> +#include <svx/svdpool.hxx> +#include <svx/svdobj.hxx> +#include <editeng/editeng.hxx> +#include <editeng/editobj.hxx> +#include <editeng/flditem.hxx> +#include <editeng/fhgtitem.hxx> +#include <editeng/unoprnms.hxx> +#include <editeng/unofored.hxx> +#include <utility> +#include <vcl/svapp.hxx> + +#include <editeng/unoipset.hxx> +#include <textuno.hxx> +#include <fielduno.hxx> +#include <editsrc.hxx> +#include <docsh.hxx> +#include <editutil.hxx> +#include <miscuno.hxx> +#include <cellsuno.hxx> +#include <cellvalue.hxx> +#include <cellform.hxx> +#include <patattr.hxx> +#include <docfunc.hxx> +#include <scmod.hxx> + +using namespace com::sun::star; + +static const SvxItemPropertySet * lcl_GetHdFtPropertySet() +{ + static const SvxItemPropertySet aHdFtPropertySet_Impl = [] { + static SfxItemPropertyMapEntry aHdFtPropertyMap_Impl[] = + { + SVX_UNOEDIT_CHAR_PROPERTIES, + SVX_UNOEDIT_FONT_PROPERTIES, + SVX_UNOEDIT_PARA_PROPERTIES, + SVX_UNOEDIT_NUMBERING_PROPERTY, // for completeness of service ParagraphProperties + }; + + // modify PropertyMap to include CONVERT_TWIPS flag for font height + // (headers/footers are in twips) + + for (auto & rEntry : aHdFtPropertyMap_Impl) + { + if ( ( rEntry.nWID == EE_CHAR_FONTHEIGHT || + rEntry.nWID == EE_CHAR_FONTHEIGHT_CJK || + rEntry.nWID == EE_CHAR_FONTHEIGHT_CTL ) && + rEntry.nMemberId == MID_FONTHEIGHT ) + { + rEntry.nMemberId |= CONVERT_TWIPS; + } + } + + return SvxItemPropertySet(aHdFtPropertyMap_Impl, SdrObject::GetGlobalDrawObjectItemPool()); + }(); + return &aHdFtPropertySet_Impl; +} + +SC_SIMPLE_SERVICE_INFO( ScHeaderFooterContentObj, "ScHeaderFooterContentObj", "com.sun.star.sheet.HeaderFooterContent" ) +SC_SIMPLE_SERVICE_INFO( ScHeaderFooterTextObj, "ScHeaderFooterTextObj", "stardiv.one.Text.Text" ) + +ScHeaderFooterContentObj::ScHeaderFooterContentObj() +{ +} + +ScHeaderFooterContentObj::~ScHeaderFooterContentObj() {} + +const EditTextObject* ScHeaderFooterContentObj::GetLeftEditObject() const +{ + return mxLeftText->GetTextObject(); +} + +const EditTextObject* ScHeaderFooterContentObj::GetCenterEditObject() const +{ + return mxCenterText->GetTextObject(); +} + +const EditTextObject* ScHeaderFooterContentObj::GetRightEditObject() const +{ + return mxRightText->GetTextObject(); +} + +// XHeaderFooterContent + +uno::Reference<text::XText> SAL_CALL ScHeaderFooterContentObj::getLeftText() +{ + SolarMutexGuard aGuard; + uno::Reference<text::XText> xInt(*mxLeftText, uno::UNO_QUERY); + return xInt; +} + +uno::Reference<text::XText> SAL_CALL ScHeaderFooterContentObj::getCenterText() +{ + SolarMutexGuard aGuard; + uno::Reference<text::XText> xInt(*mxCenterText, uno::UNO_QUERY); + return xInt; +} + +uno::Reference<text::XText> SAL_CALL ScHeaderFooterContentObj::getRightText() +{ + SolarMutexGuard aGuard; + uno::Reference<text::XText> xInt(*mxRightText, uno::UNO_QUERY); + return xInt; +} + +rtl::Reference<ScHeaderFooterContentObj> ScHeaderFooterContentObj::getImplementation( + const uno::Reference<sheet::XHeaderFooterContent>& rObj) +{ + return dynamic_cast<ScHeaderFooterContentObj*>(rObj.get()); +} + +void ScHeaderFooterContentObj::Init( const EditTextObject* pLeft, + const EditTextObject* pCenter, + const EditTextObject* pRight ) +{ + uno::Reference<css::sheet::XHeaderFooterContent> xThis(this); + mxLeftText = rtl::Reference<ScHeaderFooterTextObj>(new ScHeaderFooterTextObj(xThis, ScHeaderFooterPart::LEFT, pLeft)); + mxCenterText = rtl::Reference<ScHeaderFooterTextObj>(new ScHeaderFooterTextObj(xThis, ScHeaderFooterPart::CENTER, pCenter)); + mxRightText = rtl::Reference<ScHeaderFooterTextObj>(new ScHeaderFooterTextObj(xThis, ScHeaderFooterPart::RIGHT, pRight)); +} + +ScHeaderFooterTextData::ScHeaderFooterTextData( + uno::WeakReference<sheet::XHeaderFooterContent> xContent, ScHeaderFooterPart nP, const EditTextObject* pTextObj) : + mpTextObj(pTextObj ? pTextObj->Clone() : nullptr), + xContentObj(std::move( xContent )), + nPart( nP ), + bDataValid(false) +{ +} + +ScHeaderFooterTextData::~ScHeaderFooterTextData() +{ + SolarMutexGuard aGuard; // needed for EditEngine dtor + + pForwarder.reset(); + pEditEngine.reset(); +} + +SvxTextForwarder* ScHeaderFooterTextData::GetTextForwarder() +{ + if (!pEditEngine) + { + rtl::Reference<SfxItemPool> pEnginePool = EditEngine::CreatePool(); + pEnginePool->FreezeIdRanges(); + std::unique_ptr<ScHeaderEditEngine> pHdrEngine(new ScHeaderEditEngine( pEnginePool.get() )); + + pHdrEngine->EnableUndo( false ); + pHdrEngine->SetRefMapMode(MapMode(MapUnit::MapTwip)); + + // default font must be set, independently of document + // -> use global pool from module + + SfxItemSet aDefaults( pHdrEngine->GetEmptyItemSet() ); + const ScPatternAttr& rPattern = SC_MOD()->GetPool().GetDefaultItem(ATTR_PATTERN); + rPattern.FillEditItemSet( &aDefaults ); + // FillEditItemSet adjusts font height to 1/100th mm, + // but for header/footer twips is needed, as in the PatternAttr: + aDefaults.Put( rPattern.GetItem(ATTR_FONT_HEIGHT).CloneSetWhich(EE_CHAR_FONTHEIGHT) ); + aDefaults.Put( rPattern.GetItem(ATTR_CJK_FONT_HEIGHT).CloneSetWhich(EE_CHAR_FONTHEIGHT_CJK) ) ; + aDefaults.Put( rPattern.GetItem(ATTR_CTL_FONT_HEIGHT).CloneSetWhich(EE_CHAR_FONTHEIGHT_CTL) ); + pHdrEngine->SetDefaults( aDefaults ); + + ScHeaderFieldData aData; + ScHeaderFooterTextObj::FillDummyFieldData( aData ); + pHdrEngine->SetData( aData ); + + pEditEngine = std::move(pHdrEngine); + pForwarder.reset( new SvxEditEngineForwarder(*pEditEngine) ); + } + + if (bDataValid) + return pForwarder.get(); + + if (mpTextObj) + pEditEngine->SetTextCurrentDefaults(*mpTextObj); + + bDataValid = true; + return pForwarder.get(); +} + +void ScHeaderFooterTextData::UpdateData() +{ + if (pEditEngine) + { + mpTextObj = pEditEngine->CreateTextObject(); + } +} + +void ScHeaderFooterTextData::UpdateData(EditEngine& rEditEngine) +{ + mpTextObj = rEditEngine.CreateTextObject(); + bDataValid = false; +} + +ScHeaderFooterTextObj::ScHeaderFooterTextObj( + const uno::WeakReference<sheet::XHeaderFooterContent>& xContent, ScHeaderFooterPart nP, const EditTextObject* pTextObj) : + aTextData(xContent, nP, pTextObj) +{ + // ScHeaderFooterTextData acquires rContent + // pUnoText is created on demand (getString/setString work without it) +} + +void ScHeaderFooterTextObj::CreateUnoText_Impl() +{ + if (!mxUnoText.is()) + { + // can't be aggregated because getString/setString is handled here + ScHeaderFooterEditSource aEditSrc(aTextData); + mxUnoText.set(new SvxUnoText(&aEditSrc, lcl_GetHdFtPropertySet(), uno::Reference<text::XText>())); + } +} + +ScHeaderFooterTextObj::~ScHeaderFooterTextObj() {} + +const EditTextObject* ScHeaderFooterTextObj::GetTextObject() const +{ + return aTextData.GetTextObject(); +} + +const SvxUnoText& ScHeaderFooterTextObj::GetUnoText() +{ + if (!mxUnoText.is()) + CreateUnoText_Impl(); + return *mxUnoText; +} + +// XText + +uno::Reference<text::XTextCursor> SAL_CALL ScHeaderFooterTextObj::createTextCursor() +{ + SolarMutexGuard aGuard; + return new ScHeaderFooterTextCursor( this ); +} + +uno::Reference<text::XTextCursor> SAL_CALL ScHeaderFooterTextObj::createTextCursorByRange( + const uno::Reference<text::XTextRange>& aTextPosition ) +{ + SolarMutexGuard aGuard; + if (!mxUnoText.is()) + CreateUnoText_Impl(); + return mxUnoText->createTextCursorByRange(aTextPosition); + //! like ScCellObj::createTextCursorByRange, if SvxUnoTextRange_getReflection available +} + +void ScHeaderFooterTextObj::FillDummyFieldData( ScHeaderFieldData& rData ) +{ + OUString aDummy("???"); + rData.aTitle = aDummy; + rData.aLongDocName = aDummy; + rData.aShortDocName = aDummy; + rData.aTabName = aDummy; + rData.nPageNo = 1; + rData.nTotalPages = 99; +} + +OUString SAL_CALL ScHeaderFooterTextObj::getString() +{ + SolarMutexGuard aGuard; + OUString aRet; + const EditTextObject* pData; + + uno::Reference<css::sheet::XHeaderFooterContent> xContentObj = aTextData.GetContentObj(); + if (!xContentObj.is()) + throw css::uno::RuntimeException( + "ScHeaderFooterTextObj::getString: no ContentObj"); + + rtl::Reference<ScHeaderFooterContentObj> pObj = ScHeaderFooterContentObj::getImplementation(xContentObj); + + switch ( aTextData.GetPart() ) + { + case ScHeaderFooterPart::LEFT: + pData = pObj->GetLeftEditObject(); + break; + case ScHeaderFooterPart::CENTER: + pData = pObj->GetCenterEditObject(); + break; + case ScHeaderFooterPart::RIGHT: + pData = pObj->GetRightEditObject(); + break; + default: + SAL_WARN("sc.ui","unexpected enum value of ScHeaderFooterPart"); + pData = nullptr; + } + + if (pData) + { + // for pure text, no font info is needed in pool defaults + ScHeaderEditEngine aEditEngine( EditEngine::CreatePool().get() ); + + ScHeaderFieldData aData; + FillDummyFieldData( aData ); + aEditEngine.SetData( aData ); + + aEditEngine.SetTextCurrentDefaults(*pData); + aRet = ScEditUtil::GetSpaceDelimitedString( aEditEngine ); + } + return aRet; +} + +void SAL_CALL ScHeaderFooterTextObj::setString( const OUString& aText ) +{ + SolarMutexGuard aGuard; + + // for pure text, no font info is needed in pool defaults + ScHeaderEditEngine aEditEngine(EditEngine::CreatePool().get()); + aEditEngine.SetTextCurrentDefaults( aText ); + aTextData.UpdateData(aEditEngine); +} + +void SAL_CALL ScHeaderFooterTextObj::insertString( const uno::Reference<text::XTextRange>& xRange, + const OUString& aString, sal_Bool bAbsorb ) +{ + SolarMutexGuard aGuard; + if (!mxUnoText.is()) + CreateUnoText_Impl(); + mxUnoText->insertString( xRange, aString, bAbsorb ); +} + +void SAL_CALL ScHeaderFooterTextObj::insertControlCharacter( + const uno::Reference<text::XTextRange>& xRange, + sal_Int16 nControlCharacter, sal_Bool bAbsorb ) +{ + SolarMutexGuard aGuard; + if (!mxUnoText.is()) + CreateUnoText_Impl(); + mxUnoText->insertControlCharacter( xRange, nControlCharacter, bAbsorb ); +} + +void SAL_CALL ScHeaderFooterTextObj::insertTextContent( + const uno::Reference<text::XTextRange >& xRange, + const uno::Reference<text::XTextContent >& xContent, + sal_Bool bAbsorb ) +{ + SolarMutexGuard aGuard; + if ( xContent.is() && xRange.is() ) + { + ScEditFieldObj* pHeaderField = dynamic_cast<ScEditFieldObj*>( xContent.get() ); + + SvxUnoTextRangeBase* pTextRange = + comphelper::getFromUnoTunnel<ScHeaderFooterTextCursor>( xRange ); + + if ( pHeaderField && !pHeaderField->IsInserted() && pTextRange ) + { + SvxEditSource* pEditSource = pTextRange->GetEditSource(); + ESelection aSelection(pTextRange->GetSelection()); + + if (!bAbsorb) + { + // don't replace -> append at end + aSelection.Adjust(); + aSelection.nStartPara = aSelection.nEndPara; + aSelection.nStartPos = aSelection.nEndPos; + } + + SvxFieldItem aItem(pHeaderField->CreateFieldItem()); + + SvxTextForwarder* pForwarder = pEditSource->GetTextForwarder(); + pForwarder->QuickInsertField( aItem, aSelection ); + pEditSource->UpdateData(); + + // new selection: a digit + aSelection.Adjust(); + aSelection.nEndPara = aSelection.nStartPara; + aSelection.nEndPos = aSelection.nStartPos + 1; + + uno::Reference<text::XTextRange> xTextRange; + switch ( aTextData.GetPart() ) + { + case ScHeaderFooterPart::LEFT: + xTextRange = aTextData.GetContentObj()->getLeftText(); + break; + case ScHeaderFooterPart::CENTER: + xTextRange = aTextData.GetContentObj()->getCenterText(); + break; + case ScHeaderFooterPart::RIGHT: + xTextRange = aTextData.GetContentObj()->getRightText(); + break; + } + + pHeaderField->InitDoc(xTextRange, std::make_unique<ScHeaderFooterEditSource>(aTextData), aSelection); + + // for bAbsorb=FALSE, the new selection must be behind the inserted content + // (the xml filter relies on this) + if (!bAbsorb) + aSelection.nStartPos = aSelection.nEndPos; + + pTextRange->SetSelection( aSelection ); + + return; + } + } + + if (!mxUnoText.is()) + CreateUnoText_Impl(); + mxUnoText->insertTextContent( xRange, xContent, bAbsorb ); +} + +void SAL_CALL ScHeaderFooterTextObj::removeTextContent( + const uno::Reference<text::XTextContent>& xContent ) +{ + SolarMutexGuard aGuard; + if ( xContent.is() ) + { + ScEditFieldObj* pHeaderField = dynamic_cast<ScEditFieldObj*>(xContent.get()); + if ( pHeaderField && pHeaderField->IsInserted() ) + { + //! check if the field is in this cell + pHeaderField->DeleteField(); + return; + } + } + if (!mxUnoText.is()) + CreateUnoText_Impl(); + mxUnoText->removeTextContent( xContent ); +} + +uno::Reference<text::XText> SAL_CALL ScHeaderFooterTextObj::getText() +{ + SolarMutexGuard aGuard; + if (!mxUnoText.is()) + CreateUnoText_Impl(); + return mxUnoText->getText(); +} + +uno::Reference<text::XTextRange> SAL_CALL ScHeaderFooterTextObj::getStart() +{ + SolarMutexGuard aGuard; + if (!mxUnoText.is()) + CreateUnoText_Impl(); + return mxUnoText->getStart(); +} + +uno::Reference<text::XTextRange> SAL_CALL ScHeaderFooterTextObj::getEnd() +{ + SolarMutexGuard aGuard; + if (!mxUnoText.is()) + CreateUnoText_Impl(); + return mxUnoText->getEnd(); +} + +// XTextFieldsSupplier + +uno::Reference<container::XEnumerationAccess> SAL_CALL ScHeaderFooterTextObj::getTextFields() +{ + SolarMutexGuard aGuard; + // all fields + return new ScHeaderFieldsObj(aTextData); +} + +uno::Reference<container::XNameAccess> SAL_CALL ScHeaderFooterTextObj::getTextFieldMasters() +{ + // this does not exists in Calc (?) + return nullptr; +} + +// XTextRangeMover + +void SAL_CALL ScHeaderFooterTextObj::moveTextRange( + const uno::Reference<text::XTextRange>& xRange, + sal_Int16 nParagraphs ) +{ + SolarMutexGuard aGuard; + if (!mxUnoText.is()) + CreateUnoText_Impl(); + mxUnoText->moveTextRange( xRange, nParagraphs ); +} + +// XEnumerationAccess + +uno::Reference<container::XEnumeration> SAL_CALL ScHeaderFooterTextObj::createEnumeration() +{ + SolarMutexGuard aGuard; + if (!mxUnoText.is()) + CreateUnoText_Impl(); + return mxUnoText->createEnumeration(); +} + +// XElementAccess + +uno::Type SAL_CALL ScHeaderFooterTextObj::getElementType() +{ + SolarMutexGuard aGuard; + if (!mxUnoText.is()) + CreateUnoText_Impl(); + return mxUnoText->getElementType(); +} + +sal_Bool SAL_CALL ScHeaderFooterTextObj::hasElements() +{ + SolarMutexGuard aGuard; + if (!mxUnoText.is()) + CreateUnoText_Impl(); + return mxUnoText->hasElements(); +} + +ScCellTextCursor::ScCellTextCursor(ScCellObj& rText) : + SvxUnoTextCursor( rText.GetUnoText() ), + mxTextObj( &rText ) +{ +} + +ScCellTextCursor::~ScCellTextCursor() noexcept +{ +} + +// SvxUnoTextCursor methods reimplemented here to return the right objects: + +uno::Reference<text::XText> SAL_CALL ScCellTextCursor::getText() +{ + return mxTextObj; +} + +uno::Reference<text::XTextRange> SAL_CALL ScCellTextCursor::getStart() +{ + SolarMutexGuard aGuard; + + //! use other object for range than cursor? + + rtl::Reference<ScCellTextCursor> pNew = new ScCellTextCursor( *this ); + + ESelection aNewSel(GetSelection()); + aNewSel.nEndPara = aNewSel.nStartPara; + aNewSel.nEndPos = aNewSel.nStartPos; + pNew->SetSelection( aNewSel ); + + return static_cast<SvxUnoTextRangeBase*>(pNew.get()); +} + +uno::Reference<text::XTextRange> SAL_CALL ScCellTextCursor::getEnd() +{ + SolarMutexGuard aGuard; + + //! use other object for range than cursor? + + rtl::Reference<ScCellTextCursor> pNew = new ScCellTextCursor( *this ); + + ESelection aNewSel(GetSelection()); + aNewSel.nStartPara = aNewSel.nEndPara; + aNewSel.nStartPos = aNewSel.nEndPos; + pNew->SetSelection( aNewSel ); + + return static_cast<SvxUnoTextRangeBase*>(pNew.get()); +} + +// XUnoTunnel + +UNO3_GETIMPLEMENTATION2_IMPL(ScCellTextCursor, SvxUnoTextCursor); + +ScHeaderFooterTextCursor::ScHeaderFooterTextCursor(rtl::Reference<ScHeaderFooterTextObj> const & rText) : + SvxUnoTextCursor( rText->GetUnoText() ), + rTextObj( rText ) +{} + +ScHeaderFooterTextCursor::~ScHeaderFooterTextCursor() noexcept {}; + +// SvxUnoTextCursor methods reimplemented here to return the right objects: + +uno::Reference<text::XText> SAL_CALL ScHeaderFooterTextCursor::getText() +{ + SolarMutexGuard aGuard; + return rTextObj; +} + +uno::Reference<text::XTextRange> SAL_CALL ScHeaderFooterTextCursor::getStart() +{ + SolarMutexGuard aGuard; + + //! use other object for range than cursor? + + rtl::Reference<ScHeaderFooterTextCursor> pNew = new ScHeaderFooterTextCursor( *this ); + + ESelection aNewSel(GetSelection()); + aNewSel.nEndPara = aNewSel.nStartPara; + aNewSel.nEndPos = aNewSel.nStartPos; + pNew->SetSelection( aNewSel ); + + return static_cast<SvxUnoTextRangeBase*>(pNew.get()); +} + +uno::Reference<text::XTextRange> SAL_CALL ScHeaderFooterTextCursor::getEnd() +{ + SolarMutexGuard aGuard; + + //! use other object for range than cursor? + + rtl::Reference<ScHeaderFooterTextCursor> pNew = new ScHeaderFooterTextCursor( *this ); + + ESelection aNewSel(GetSelection()); + aNewSel.nStartPara = aNewSel.nEndPara; + aNewSel.nStartPos = aNewSel.nEndPos; + pNew->SetSelection( aNewSel ); + + return static_cast<SvxUnoTextRangeBase*>(pNew.get()); +} + +// XUnoTunnel + +UNO3_GETIMPLEMENTATION2_IMPL(ScHeaderFooterTextCursor, SvxUnoTextCursor); + +ScDrawTextCursor::ScDrawTextCursor( uno::Reference<text::XText> xParent, + const SvxUnoTextBase& rText ) : + SvxUnoTextCursor( rText ), + xParentText(std::move( xParent )) + +{ +} + +ScDrawTextCursor::~ScDrawTextCursor() noexcept +{ +} + +// SvxUnoTextCursor methods reimplemented here to return the right objects: + +uno::Reference<text::XText> SAL_CALL ScDrawTextCursor::getText() +{ + SolarMutexGuard aGuard; + return xParentText; +} + +uno::Reference<text::XTextRange> SAL_CALL ScDrawTextCursor::getStart() +{ + SolarMutexGuard aGuard; + + //! use other object for range than cursor? + + rtl::Reference<ScDrawTextCursor> pNew = new ScDrawTextCursor( *this ); + + ESelection aNewSel(GetSelection()); + aNewSel.nEndPara = aNewSel.nStartPara; + aNewSel.nEndPos = aNewSel.nStartPos; + pNew->SetSelection( aNewSel ); + + return static_cast<SvxUnoTextRangeBase*>(pNew.get()); +} + +uno::Reference<text::XTextRange> SAL_CALL ScDrawTextCursor::getEnd() +{ + SolarMutexGuard aGuard; + + //! use other object for range than cursor? + + rtl::Reference<ScDrawTextCursor> pNew = new ScDrawTextCursor( *this ); + + ESelection aNewSel(GetSelection()); + aNewSel.nStartPara = aNewSel.nEndPara; + aNewSel.nStartPos = aNewSel.nEndPos; + pNew->SetSelection( aNewSel ); + + return static_cast<SvxUnoTextRangeBase*>(pNew.get()); +} + +// XUnoTunnel + +UNO3_GETIMPLEMENTATION2_IMPL(ScDrawTextCursor, SvxUnoTextCursor); + +ScSimpleEditSourceHelper::ScSimpleEditSourceHelper() +{ + rtl::Reference<SfxItemPool> pEnginePool = EditEngine::CreatePool(); + pEnginePool->SetDefaultMetric( MapUnit::Map100thMM ); + pEnginePool->FreezeIdRanges(); + + pEditEngine.reset( new ScFieldEditEngine(nullptr, pEnginePool.get(), nullptr, true) ); // TRUE: become owner of pool + pForwarder.reset( new SvxEditEngineForwarder( *pEditEngine ) ); + pOriginalSource.reset( new ScSimpleEditSource( pForwarder.get() ) ); +} + +ScSimpleEditSourceHelper::~ScSimpleEditSourceHelper() +{ + SolarMutexGuard aGuard; // needed for EditEngine dtor + + pOriginalSource.reset(); + pForwarder.reset(); + pEditEngine.reset(); +} + +ScEditEngineTextObj::ScEditEngineTextObj() : + SvxUnoText( GetOriginalSource(), ScCellObj::GetEditPropertySet(), uno::Reference<text::XText>() ) +{ +} + +ScEditEngineTextObj::~ScEditEngineTextObj() noexcept +{ +} + +void ScEditEngineTextObj::SetText( const EditTextObject& rTextObject ) +{ + GetEditEngine()->SetTextCurrentDefaults( rTextObject ); + + ESelection aSel; + ::GetSelection( aSel, GetEditSource()->GetTextForwarder() ); + SetSelection( aSel ); +} + +std::unique_ptr<EditTextObject> ScEditEngineTextObj::CreateTextObject() +{ + return GetEditEngine()->CreateTextObject(); +} + +ScCellTextData::ScCellTextData(ScDocShell* pDocSh, const ScAddress& rP) : + pDocShell( pDocSh ), + aCellPos( rP ), + bDataValid( false ), + bInUpdate( false ), + bDirty( false ), + bDoUpdate( true ) +{ + if (pDocShell) + pDocShell->GetDocument().AddUnoObject(*this); +} + +ScCellTextData::~ScCellTextData() +{ + SolarMutexGuard aGuard; // needed for EditEngine dtor + + if (pDocShell) + { + pDocShell->GetDocument().RemoveUnoObject(*this); + pDocShell->GetDocument().DisposeFieldEditEngine(pEditEngine); + } + else + pEditEngine.reset(); + + pForwarder.reset(); + + pOriginalSource.reset(); +} + +ScCellEditSource* ScCellTextData::GetOriginalSource() +{ + if (!pOriginalSource) + pOriginalSource.reset( new ScCellEditSource(pDocShell, aCellPos) ); + return pOriginalSource.get(); +} + +SvxTextForwarder* ScCellTextData::GetTextForwarder() +{ + if (!pEditEngine) + { + if ( pDocShell ) + { + ScDocument& rDoc = pDocShell->GetDocument(); + pEditEngine = rDoc.CreateFieldEditEngine(); + } + else + { + rtl::Reference<SfxItemPool> pEnginePool = EditEngine::CreatePool(); + pEnginePool->FreezeIdRanges(); + pEditEngine.reset( new ScFieldEditEngine(nullptr, pEnginePool.get(), nullptr, true) ); + } + // currently, GetPortions doesn't work if UpdateMode is sal_False, + // this will be fixed (in EditEngine) by src600 +// pEditEngine->SetUpdateMode( sal_False ); + pEditEngine->EnableUndo( false ); + if (pDocShell) + pEditEngine->SetRefDevice(pDocShell->GetRefDevice()); + else + pEditEngine->SetRefMapMode(MapMode(MapUnit::Map100thMM)); + pForwarder.reset( new SvxEditEngineForwarder(*pEditEngine) ); + } + + if (bDataValid) + return pForwarder.get(); + + if (pDocShell) + { + ScDocument& rDoc = pDocShell->GetDocument(); + + SfxItemSet aDefaults( pEditEngine->GetEmptyItemSet() ); + if( const ScPatternAttr* pPattern = + rDoc.GetPattern( aCellPos.Col(), aCellPos.Row(), aCellPos.Tab() ) ) + { + pPattern->FillEditItemSet( &aDefaults ); + pPattern->FillEditParaItems( &aDefaults ); // including alignment etc. (for reading) + } + + ScRefCellValue aCell(rDoc, aCellPos); + if (aCell.getType() == CELLTYPE_EDIT) + { + const EditTextObject* pObj = aCell.getEditText(); + pEditEngine->SetTextNewDefaults(*pObj, aDefaults); + } + else + { + sal_uInt32 nFormat = rDoc.GetNumberFormat(aCellPos); + OUString aText = ScCellFormat::GetInputString(aCell, nFormat, *rDoc.GetFormatTable(), rDoc); + if (!aText.isEmpty()) + pEditEngine->SetTextNewDefaults(aText, aDefaults); + else + pEditEngine->SetDefaults(aDefaults); + } + } + + bDataValid = true; + return pForwarder.get(); +} + +void ScCellTextData::UpdateData() +{ + if ( bDoUpdate ) + { + OSL_ENSURE(pEditEngine != nullptr, "no EditEngine for UpdateData()"); + if ( pDocShell && pEditEngine ) + { + // during the own UpdateData call, bDataValid must not be reset, + // or things like attributes after the text would be lost + // (are not stored in the cell) + bInUpdate = true; // prevents bDataValid from being reset + pDocShell->GetDocFunc().PutData(aCellPos, *pEditEngine, true); // always as text + + bInUpdate = false; + bDirty = false; + } + } + else + bDirty = true; +} + +void ScCellTextData::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + const SfxHintId nId = rHint.GetId(); + if ( nId == SfxHintId::Dying ) + { + pDocShell = nullptr; // invalid now + + pForwarder.reset(); + pEditEngine.reset(); // EditEngine uses document's pool + } + else if ( nId == SfxHintId::DataChanged ) + { + if (!bInUpdate) // not for own UpdateData calls + bDataValid = false; // text has to be read from the cell again + } +} + +ScCellTextObj::ScCellTextObj(ScDocShell* pDocSh, const ScAddress& rP) : + ScCellTextData( pDocSh, rP ), + SvxUnoText( GetOriginalSource(), ScCellObj::GetEditPropertySet(), uno::Reference<text::XText>() ) +{ +} + +ScCellTextObj::~ScCellTextObj() COVERITY_NOEXCEPT_FALSE +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/unoobj/tokenuno.cxx b/sc/source/ui/unoobj/tokenuno.cxx new file mode 100644 index 0000000000..90b0a87e30 --- /dev/null +++ b/sc/source/ui/unoobj/tokenuno.cxx @@ -0,0 +1,520 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <memory> +#include <tokenuno.hxx> + +#include <sal/macros.h> +#include <sal/log.hxx> + +#include <com/sun/star/sheet/ComplexReference.hpp> +#include <com/sun/star/sheet/ExternalReference.hpp> +#include <com/sun/star/sheet/ReferenceFlags.hpp> +#include <com/sun/star/sheet/AddressConvention.hpp> +#include <com/sun/star/sheet/NameToken.hpp> +#include <com/sun/star/table/CellAddress.hpp> + +#include <svl/itemprop.hxx> +#include <vcl/svapp.hxx> +#include <comphelper/string.hxx> + +#include <miscuno.hxx> +#include <convuno.hxx> +#include <unonames.hxx> +#include <compiler.hxx> +#include <tokenarray.hxx> +#include <docsh.hxx> +#include <rangeseq.hxx> +#include <externalrefmgr.hxx> + +using namespace ::formula; +using namespace ::com::sun::star; + +static std::span<const SfxItemPropertyMapEntry> lcl_GetFormulaParserMap() +{ + static const SfxItemPropertyMapEntry aFormulaParserMap_Impl[] = + { + { SC_UNO_COMPILEFAP, 0, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNO_COMPILEENGLISH, 0, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNO_IGNORELEADING, 0, cppu::UnoType<bool>::get(), 0, 0 }, + { SC_UNO_FORMULACONVENTION, 0, cppu::UnoType<decltype(sheet::AddressConvention::UNSPECIFIED)>::get(), 0, 0 }, + { SC_UNO_OPCODEMAP, 0, cppu::UnoType<uno::Sequence< sheet::FormulaOpCodeMapEntry >>::get(), 0, 0 }, + }; + return aFormulaParserMap_Impl; +} + +const formula::FormulaGrammar::AddressConvention aConvMap[] = { + formula::FormulaGrammar::CONV_OOO, // <- AddressConvention::OOO + formula::FormulaGrammar::CONV_XL_A1, // <- AddressConvention::XL_A1 + formula::FormulaGrammar::CONV_XL_R1C1, // <- AddressConvention::XL_R1C1 + formula::FormulaGrammar::CONV_XL_OOX, // <- AddressConvention::XL_OOX + formula::FormulaGrammar::CONV_LOTUS_A1 // <- AddressConvention::LOTUS_A1 +}; +// sal_Int16 because of comparison of integer expressions below. +constexpr sal_Int16 nConvMapCount = SAL_N_ELEMENTS(aConvMap); + + +SC_SIMPLE_SERVICE_INFO( ScFormulaParserObj, "ScFormulaParserObj", SC_SERVICENAME_FORMULAPARS ) + +ScFormulaParserObj::ScFormulaParserObj(ScDocShell* pDocSh) : + mpDocShell( pDocSh ), + mnConv( sheet::AddressConvention::UNSPECIFIED ), + mbEnglish( false ), + mbIgnoreSpaces( true ), + mbCompileFAP( false ), + mbRefConventionChartOOXML( false ) +{ + mpDocShell->GetDocument().AddUnoObject(*this); +} + +ScFormulaParserObj::~ScFormulaParserObj() +{ + SolarMutexGuard g; + + if (mpDocShell) + mpDocShell->GetDocument().RemoveUnoObject(*this); +} + +void ScFormulaParserObj::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + if ( rHint.GetId() == SfxHintId::Dying ) + mpDocShell = nullptr; +} + +// XFormulaParser + +void ScFormulaParserObj::SetCompilerFlags( ScCompiler& rCompiler ) const +{ + formula::FormulaGrammar::AddressConvention eConv = formula::FormulaGrammar::CONV_UNSPECIFIED; + if (mnConv >= 0 && mnConv < nConvMapCount) + eConv = aConvMap[mnConv]; + + // If mxOpCodeMap is not empty it overrides mbEnglish, and vice versa. We + // don't need to initialize things twice. + if (mxOpCodeMap) + rCompiler.SetFormulaLanguage( mxOpCodeMap ); + else + { + const sal_Int32 nFormulaLanguage = (eConv == formula::FormulaGrammar::CONV_XL_OOX ? + sheet::FormulaLanguage::OOXML : + (mbEnglish ? sheet::FormulaLanguage::ENGLISH : sheet::FormulaLanguage::NATIVE)); + ScCompiler::OpCodeMapPtr xMap = rCompiler.GetOpCodeMap( nFormulaLanguage); + rCompiler.SetFormulaLanguage( xMap); + } + + rCompiler.SetRefConvention( eConv ); + rCompiler.EnableJumpCommandReorder(!mbCompileFAP); + rCompiler.EnableStopOnError(!mbCompileFAP); + + rCompiler.SetExternalLinks(maExternalLinks); + rCompiler.SetRefConventionChartOOXML(mbRefConventionChartOOXML); +} + +uno::Sequence<sheet::FormulaToken> SAL_CALL ScFormulaParserObj::parseFormula( + const OUString& aFormula, const table::CellAddress& rReferencePos ) +{ + SolarMutexGuard aGuard; + uno::Sequence<sheet::FormulaToken> aRet; + + if (mpDocShell) + { + ScDocument& rDoc = mpDocShell->GetDocument(); + ScExternalRefManager::ApiGuard aExtRefGuard(rDoc); + + ScAddress aRefPos( ScAddress::UNINITIALIZED ); + ScUnoConversion::FillScAddress( aRefPos, rReferencePos ); + ScCompiler aCompiler( rDoc, aRefPos, rDoc.GetGrammar()); + SetCompilerFlags( aCompiler ); + + std::unique_ptr<ScTokenArray> pCode = aCompiler.CompileString( aFormula ); + ScTokenConversion::ConvertToTokenSequence( rDoc, aRet, *pCode ); + } + + return aRet; +} + +OUString SAL_CALL ScFormulaParserObj::printFormula( + const uno::Sequence<sheet::FormulaToken>& aTokens, const table::CellAddress& rReferencePos ) +{ + SolarMutexGuard aGuard; + OUString aRet; + + if (mpDocShell) + { + ScDocument& rDoc = mpDocShell->GetDocument(); + ScTokenArray aCode(rDoc); + (void)ScTokenConversion::ConvertToTokenArray( rDoc, aCode, aTokens ); + ScAddress aRefPos( ScAddress::UNINITIALIZED ); + ScUnoConversion::FillScAddress( aRefPos, rReferencePos ); + ScCompiler aCompiler(rDoc, aRefPos, aCode, rDoc.GetGrammar()); + SetCompilerFlags( aCompiler ); + + OUStringBuffer aBuffer; + aCompiler.CreateStringFromTokenArray( aBuffer ); + aRet = aBuffer.makeStringAndClear(); + } + + return aRet; +} + +// XPropertySet + +uno::Reference<beans::XPropertySetInfo> SAL_CALL ScFormulaParserObj::getPropertySetInfo() +{ + static uno::Reference< beans::XPropertySetInfo > aRef(new SfxItemPropertySetInfo( lcl_GetFormulaParserMap() )); + return aRef; +} + +void SAL_CALL ScFormulaParserObj::setPropertyValue( + const OUString& aPropertyName, const uno::Any& aValue ) +{ + SolarMutexGuard aGuard; + if ( aPropertyName == SC_UNO_COMPILEFAP ) + { + aValue >>= mbCompileFAP; + } + else if ( aPropertyName == SC_UNO_COMPILEENGLISH ) + { + bool bOldEnglish = mbEnglish; + if (!(aValue >>= mbEnglish)) + throw lang::IllegalArgumentException(); + + // Need to recreate the symbol map to change English property + // because the map is const. So for performance reasons set + // CompileEnglish _before_ OpCodeMap! + if (mxOpCodeMap && mbEnglish != bOldEnglish) + { + ScDocument& rDoc = mpDocShell->GetDocument(); + ScCompiler aCompiler( rDoc, ScAddress(), rDoc.GetGrammar()); + mxOpCodeMap = formula::FormulaCompiler::CreateOpCodeMap( maOpCodeMapping, mbEnglish); + } + + } + else if ( aPropertyName == SC_UNO_FORMULACONVENTION ) + { + aValue >>= mnConv; + + bool bOldEnglish = mbEnglish; + if (mnConv >= 0 && mnConv < nConvMapCount + && aConvMap[mnConv] == formula::FormulaGrammar::CONV_XL_OOX) + mbEnglish = true; + + // Same as for SC_UNO_COMPILEENGLISH, though an OpCodeMap should not + // had been set for CONV_XL_OOX. + if (mxOpCodeMap && mbEnglish != bOldEnglish) + { + ScDocument& rDoc = mpDocShell->GetDocument(); + ScCompiler aCompiler( rDoc, ScAddress(), rDoc.GetGrammar()); + mxOpCodeMap = formula::FormulaCompiler::CreateOpCodeMap( maOpCodeMapping, mbEnglish); + } + } + else if ( aPropertyName == SC_UNO_IGNORELEADING ) + { + aValue >>= mbIgnoreSpaces; + } + else if ( aPropertyName == SC_UNO_OPCODEMAP ) + { + if (!(aValue >>= maOpCodeMapping)) + throw lang::IllegalArgumentException(); + + ScDocument& rDoc = mpDocShell->GetDocument(); + ScCompiler aCompiler(rDoc, ScAddress(), rDoc.GetGrammar()); + mxOpCodeMap = formula::FormulaCompiler::CreateOpCodeMap( maOpCodeMapping, mbEnglish); + + } + else if ( aPropertyName == SC_UNO_EXTERNALLINKS ) + { + if (!(aValue >>= maExternalLinks)) + throw lang::IllegalArgumentException(); + } + else if ( aPropertyName == SC_UNO_REF_CONV_CHARTOOXML ) + { + if (!(aValue >>= mbRefConventionChartOOXML)) + throw lang::IllegalArgumentException(); + } + else + throw beans::UnknownPropertyException(aPropertyName); +} + +uno::Any SAL_CALL ScFormulaParserObj::getPropertyValue( const OUString& aPropertyName ) +{ + SolarMutexGuard aGuard; + uno::Any aRet; + if ( aPropertyName == SC_UNO_COMPILEFAP ) + { + aRet <<= mbCompileFAP; + } + else if ( aPropertyName == SC_UNO_COMPILEENGLISH ) + { + aRet <<= mbEnglish; + } + else if ( aPropertyName == SC_UNO_FORMULACONVENTION ) + { + aRet <<= mnConv; + } + else if ( aPropertyName == SC_UNO_IGNORELEADING ) + { + aRet <<= mbIgnoreSpaces; + } + else if ( aPropertyName == SC_UNO_OPCODEMAP ) + { + aRet <<= maOpCodeMapping; + } + else if ( aPropertyName == SC_UNO_EXTERNALLINKS ) + { + aRet <<= maExternalLinks; + } + else if ( aPropertyName == SC_UNO_REF_CONV_CHARTOOXML ) + { + aRet <<= mbRefConventionChartOOXML; + } + else + throw beans::UnknownPropertyException(aPropertyName); + return aRet; +} + +SC_IMPL_DUMMY_PROPERTY_LISTENER( ScFormulaParserObj ) + +static void lcl_ExternalRefToApi( sheet::SingleReference& rAPI, const ScSingleRefData& rRef ) +{ + rAPI.Column = 0; + rAPI.Row = 0; + rAPI.Sheet = 0; + rAPI.RelativeColumn = 0; + rAPI.RelativeRow = 0; + rAPI.RelativeSheet = 0; + + sal_Int32 nFlags = 0; + if ( rRef.IsColRel() ) + { + nFlags |= sheet::ReferenceFlags::COLUMN_RELATIVE; + rAPI.RelativeColumn = rRef.Col(); + } + else + rAPI.Column = rRef.Col(); + + if ( rRef.IsRowRel() ) + { + nFlags |= sheet::ReferenceFlags::ROW_RELATIVE; + rAPI.RelativeRow = rRef.Row(); + } + else + rAPI.Row = rRef.Row(); + + if ( rRef.IsColDeleted() ) nFlags |= sheet::ReferenceFlags::COLUMN_DELETED; + if ( rRef.IsRowDeleted() ) nFlags |= sheet::ReferenceFlags::ROW_DELETED; + if ( rRef.IsFlag3D() ) nFlags |= sheet::ReferenceFlags::SHEET_3D; + if ( rRef.IsRelName() ) nFlags |= sheet::ReferenceFlags::RELATIVE_NAME; + rAPI.Flags = nFlags; +} + +static void lcl_SingleRefToApi( sheet::SingleReference& rAPI, const ScSingleRefData& rRef ) +{ + sal_Int32 nFlags = 0; + if ( rRef.IsColRel() ) + { + nFlags |= sheet::ReferenceFlags::COLUMN_RELATIVE; + rAPI.RelativeColumn = rRef.Col(); + rAPI.Column = 0; + } + else + { + rAPI.RelativeColumn = 0; + rAPI.Column = rRef.Col(); + } + + if ( rRef.IsRowRel() ) + { + nFlags |= sheet::ReferenceFlags::ROW_RELATIVE; + rAPI.RelativeRow = rRef.Row(); + rAPI.Row = 0; + } + else + { + rAPI.RelativeRow = 0; + rAPI.Row = rRef.Row(); + } + + if ( rRef.IsTabRel() ) + { + nFlags |= sheet::ReferenceFlags::SHEET_RELATIVE; + rAPI.RelativeSheet = rRef.Tab(); + rAPI.Sheet = 0; + } + else + { + rAPI.RelativeSheet = 0; + rAPI.Sheet = rRef.Tab(); + } + + if ( rRef.IsColDeleted() ) nFlags |= sheet::ReferenceFlags::COLUMN_DELETED; + if ( rRef.IsRowDeleted() ) nFlags |= sheet::ReferenceFlags::ROW_DELETED; + if ( rRef.IsTabDeleted() ) nFlags |= sheet::ReferenceFlags::SHEET_DELETED; + if ( rRef.IsFlag3D() ) nFlags |= sheet::ReferenceFlags::SHEET_3D; + if ( rRef.IsRelName() ) nFlags |= sheet::ReferenceFlags::RELATIVE_NAME; + rAPI.Flags = nFlags; +} + +bool ScTokenConversion::ConvertToTokenArray( ScDocument& rDoc, + ScTokenArray& rTokenArray, const uno::Sequence<sheet::FormulaToken>& rSequence ) +{ + return !rTokenArray.Fill(rSequence, rDoc.GetSharedStringPool(), rDoc.GetExternalRefManager()); +} + +void ScTokenConversion::ConvertToTokenSequence( const ScDocument& rDoc, + uno::Sequence<sheet::FormulaToken>& rSequence, const ScTokenArray& rTokenArray ) +{ + sal_Int32 nLen = static_cast<sal_Int32>(rTokenArray.GetLen()); + formula::FormulaToken** pTokens = rTokenArray.GetArray(); + if ( pTokens ) + { + rSequence.realloc(nLen); + auto pSequence = rSequence.getArray(); + for (sal_Int32 nPos=0; nPos<nLen; nPos++) + { + const formula::FormulaToken& rToken = *pTokens[nPos]; + sheet::FormulaToken& rAPI = pSequence[nPos]; + + OpCode eOpCode = rToken.GetOpCode(); + // eOpCode may be changed in the following switch/case + switch ( rToken.GetType() ) + { + case svByte: + // Only the count of spaces is stored as "long". Parameter count is ignored. + if ( eOpCode == ocSpaces ) + rAPI.Data <<= static_cast<sal_Int32>(rToken.GetByte()); + else if (eOpCode == ocWhitespace) + { + // Convention is one character repeated. + if (rToken.GetByte() == 1) + rAPI.Data <<= OUString( rToken.GetChar()); + else + { + OUStringBuffer aBuf( rToken.GetByte()); + comphelper::string::padToLength( aBuf, rToken.GetByte(), rToken.GetChar()); + rAPI.Data <<= aBuf.makeStringAndClear(); + } + } + else + rAPI.Data.clear(); // no data + break; + case formula::svDouble: + rAPI.Data <<= rToken.GetDouble(); + break; + case formula::svString: + rAPI.Data <<= rToken.GetString().getString(); + break; + case svExternal: + // Function name is stored as string. + // Byte (parameter count) is ignored. + rAPI.Data <<= rToken.GetExternal(); + break; + case svSingleRef: + { + sheet::SingleReference aSingleRef; + lcl_SingleRefToApi( aSingleRef, *rToken.GetSingleRef() ); + rAPI.Data <<= aSingleRef; + } + break; + case formula::svDoubleRef: + { + sheet::ComplexReference aCompRef; + lcl_SingleRefToApi( aCompRef.Reference1, *rToken.GetSingleRef() ); + lcl_SingleRefToApi( aCompRef.Reference2, *rToken.GetSingleRef2() ); + rAPI.Data <<= aCompRef; + } + break; + case svIndex: + { + sheet::NameToken aNameToken; + aNameToken.Index = static_cast<sal_Int32>( rToken.GetIndex() ); + aNameToken.Sheet = rToken.GetSheet(); + rAPI.Data <<= aNameToken; + } + break; + case svMatrix: + if (!ScRangeToSequence::FillMixedArray( rAPI.Data, rToken.GetMatrix(), true)) + rAPI.Data.clear(); + break; + case svExternalSingleRef: + { + sheet::SingleReference aSingleRef; + lcl_ExternalRefToApi( aSingleRef, *rToken.GetSingleRef() ); + size_t nCacheId; + rDoc.GetExternalRefManager()->getCacheTable( + rToken.GetIndex(), rToken.GetString().getString(), false, &nCacheId); + aSingleRef.Sheet = static_cast< sal_Int32 >( nCacheId ); + sheet::ExternalReference aExtRef; + aExtRef.Index = rToken.GetIndex(); + aExtRef.Reference <<= aSingleRef; + rAPI.Data <<= aExtRef; + eOpCode = ocPush; + } + break; + case svExternalDoubleRef: + { + sheet::ComplexReference aComplRef; + lcl_ExternalRefToApi( aComplRef.Reference1, *rToken.GetSingleRef() ); + lcl_ExternalRefToApi( aComplRef.Reference2, *rToken.GetSingleRef2() ); + size_t nCacheId; + rDoc.GetExternalRefManager()->getCacheTable( + rToken.GetIndex(), rToken.GetString().getString(), false, &nCacheId); + aComplRef.Reference1.Sheet = static_cast< sal_Int32 >( nCacheId ); + // NOTE: This assumes that cached sheets are in consecutive order! + aComplRef.Reference2.Sheet = + aComplRef.Reference1.Sheet + + (rToken.GetSingleRef2()->Tab() - rToken.GetSingleRef()->Tab()); + sheet::ExternalReference aExtRef; + aExtRef.Index = rToken.GetIndex(); + aExtRef.Reference <<= aComplRef; + rAPI.Data <<= aExtRef; + eOpCode = ocPush; + } + break; + case svExternalName: + { + sheet::ExternalReference aExtRef; + aExtRef.Index = rToken.GetIndex(); + aExtRef.Reference <<= rToken.GetString().getString(); + rAPI.Data <<= aExtRef; + eOpCode = ocPush; + } + break; + default: + SAL_WARN("sc", "ScTokenConversion::ConvertToTokenSequence: unhandled token type " << StackVarEnumToString(rToken.GetType())); + [[fallthrough]]; + case svJump: // occurs with ocIf, ocChoose + case svError: // seems to be fairly common, and probably not exceptional and not worth a warning? + case svMissing: // occurs with ocMissing + case svSep: // occurs with ocSep, ocOpen, ocClose, ocArray* + rAPI.Data.clear(); // no data + } + rAPI.OpCode = static_cast<sal_Int32>(eOpCode); //! assuming equal values for the moment + } + } + else + rSequence.realloc(0); +} + +ScFormulaOpCodeMapperObj::ScFormulaOpCodeMapperObj(::std::unique_ptr<formula::FormulaCompiler> && _pCompiler) +: formula::FormulaOpCodeMapperObj(std::move(_pCompiler)) +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/unoobj/unodoc.cxx b/sc/source/ui/unoobj/unodoc.cxx new file mode 100644 index 0000000000..bed297df7c --- /dev/null +++ b/sc/source/ui/unoobj/unodoc.cxx @@ -0,0 +1,45 @@ +/* -*- 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 <sfx2/sfxmodelfactory.hxx> + +#include <scdll.hxx> +#include <vcl/svapp.hxx> + +#include <docsh.hxx> + +using namespace ::com::sun::star; + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +Calc_SpreadsheetDocument_get_implementation( + css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const& args) +{ + SolarMutexGuard aGuard; + ScDLL::Init(); + css::uno::Reference<css::uno::XInterface> xInterface = sfx2::createSfxModelInstance(args, + [](SfxModelFlags _nCreationFlags) + { + SfxObjectShell* pShell = new ScDocShell( _nCreationFlags ); + return uno::Reference< uno::XInterface >( pShell->GetModel() ); + }); + xInterface->acquire(); + return xInterface.get(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/unoobj/unoreflist.cxx b/sc/source/ui/unoobj/unoreflist.cxx new file mode 100644 index 0000000000..fff4d0fd9d --- /dev/null +++ b/sc/source/ui/unoobj/unoreflist.cxx @@ -0,0 +1,55 @@ +/* -*- 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 <unoreflist.hxx> +#include <document.hxx> +#include <utility> + +ScUnoRefList::ScUnoRefList() +{ +} + +ScUnoRefList::~ScUnoRefList() +{ +} + +void ScUnoRefList::Add( sal_Int64 nId, const ScRangeList& rOldRanges ) +{ + aEntries.emplace_back( nId, rOldRanges ); +} + +void ScUnoRefList::Undo( ScDocument* pDoc ) +{ + for (const auto & entry: aEntries) + { + ScUnoRefUndoHint aHint(entry); + pDoc->BroadcastUno( aHint ); + } +} + +ScUnoRefUndoHint::ScUnoRefUndoHint( ScUnoRefEntry aRefEntry ) : + aEntry(std::move( aRefEntry )) +{ +} + +ScUnoRefUndoHint::~ScUnoRefUndoHint() +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/unoobj/viewuno.cxx b/sc/source/ui/unoobj/viewuno.cxx new file mode 100644 index 0000000000..2d066bcfc2 --- /dev/null +++ b/sc/source/ui/unoobj/viewuno.cxx @@ -0,0 +1,2220 @@ +/* -*- 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 <com/sun/star/awt/MouseButton.hpp> +#include <com/sun/star/drawing/ShapeCollection.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/script/vba/XVBAEventProcessor.hpp> +#include <com/sun/star/util/VetoException.hpp> +#include <com/sun/star/view/DocumentZoomType.hpp> + +#include <editeng/outliner.hxx> +#include <svx/svditer.hxx> +#include <svx/svdmark.hxx> +#include <svx/svdpage.hxx> +#include <svx/svdpagv.hxx> +#include <svx/svdview.hxx> +#include <svx/unoshape.hxx> +#include <svx/fmshell.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/request.hxx> +#include <sfx2/viewfrm.hxx> +#include <comphelper/profilezone.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/sequence.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <toolkit/helper/convert.hxx> +#include <vcl/svapp.hxx> + +#include <drawsh.hxx> +#include <drtxtob.hxx> +#include <transobj.hxx> +#include <editsh.hxx> +#include <viewuno.hxx> +#include <cellsuno.hxx> +#include <miscuno.hxx> +#include <tabvwsh.hxx> +#include <prevwsh.hxx> +#include <docsh.hxx> +#include <drwlayer.hxx> +#include <attrib.hxx> +#include <drawview.hxx> +#include <fupoor.hxx> +#include <sc.hrc> +#include <unonames.hxx> +#include <scmod.hxx> +#include <appoptio.hxx> +#include <gridwin.hxx> +#include <sheetevents.hxx> +#include <markdata.hxx> +#include <scextopt.hxx> +#include <preview.hxx> +#include <inputhdl.hxx> +#include <inputwin.hxx> +#include <svx/sdrhittesthelper.hxx> +#include <formatsh.hxx> +#include <sfx2/app.hxx> +#include <scitems.hxx> + +using namespace com::sun::star; + +//! Clipping Marks + +// no Which-ID here, Map only for PropertySetInfo + +static std::span<const SfxItemPropertyMapEntry> lcl_GetViewOptPropertyMap() +{ + static const SfxItemPropertyMapEntry aViewOptPropertyMap_Impl[] = + { + { OLD_UNO_COLROWHDR, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_GRIDCOLOR, 0, cppu::UnoType<sal_Int32>::get(), 0, 0}, + { SC_UNO_COLROWHDR, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_HORSCROLL, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_SHEETTABS, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_VERTSCROLL, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_HIDESPELL, 0, cppu::UnoType<bool>::get(), 0, 0}, /* deprecated #i91949 */ + { OLD_UNO_HORSCROLL, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_OUTLSYMB, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_VALUEHIGH, 0, cppu::UnoType<bool>::get(), 0, 0}, + { OLD_UNO_OUTLSYMB, 0, cppu::UnoType<bool>::get(), 0, 0}, + { OLD_UNO_SHEETTABS, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_SHOWANCHOR, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_SHOWCHARTS, 0, cppu::UnoType<sal_Int16>::get(), 0, 0}, + { SC_UNO_SHOWDRAW, 0, cppu::UnoType<sal_Int16>::get(), 0, 0}, + { SC_UNO_SHOWFORM, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_SHOWGRID, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_SHOWHELP, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_SHOWNOTES, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_SHOWFORMULASMARKS, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_SHOWOBJ, 0, cppu::UnoType<sal_Int16>::get(), 0, 0}, + { SC_UNO_SHOWPAGEBR, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_SHOWZERO, 0, cppu::UnoType<bool>::get(), 0, 0}, + { OLD_UNO_VALUEHIGH, 0, cppu::UnoType<bool>::get(), 0, 0}, + { OLD_UNO_VERTSCROLL, 0, cppu::UnoType<bool>::get(), 0, 0}, + { SC_UNO_VISAREA, 0, cppu::UnoType<awt::Rectangle>::get(), 0, 0}, + { SC_UNO_ZOOMTYPE, 0, cppu::UnoType<sal_Int16>::get(), 0, 0}, + { SC_UNO_ZOOMVALUE, 0, cppu::UnoType<sal_Int16>::get(), 0, 0}, + { SC_UNO_VISAREASCREEN,0, cppu::UnoType<awt::Rectangle>::get(), 0, 0}, + { SC_UNO_FORMULABARHEIGHT,0,cppu::UnoType<sal_Int16>::get(), 0, 0}, + }; + return aViewOptPropertyMap_Impl; +} + +constexpr OUString SCTABVIEWOBJ_SERVICE = u"com.sun.star.sheet.SpreadsheetView"_ustr; +constexpr OUString SCVIEWSETTINGS_SERVICE = u"com.sun.star.sheet.SpreadsheetViewSettings"_ustr; + +SC_SIMPLE_SERVICE_INFO( ScViewPaneBase, "ScViewPaneObj", "com.sun.star.sheet.SpreadsheetViewPane" ) + +ScViewPaneBase::ScViewPaneBase(ScTabViewShell* pViewSh, sal_uInt16 nP) : + pViewShell( pViewSh ), + nPane( nP ) +{ + if (pViewShell) + StartListening(*pViewShell); +} + +ScViewPaneBase::~ScViewPaneBase() +{ + SolarMutexGuard g; + + if (pViewShell) + EndListening(*pViewShell); +} + +void ScViewPaneBase::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + if ( rHint.GetId() == SfxHintId::Dying ) + pViewShell = nullptr; +} + +uno::Any SAL_CALL ScViewPaneBase::queryInterface( const uno::Type& rType ) +{ + uno::Any aReturn = ::cppu::queryInterface(rType, + static_cast<sheet::XViewPane*>(this), + static_cast<sheet::XCellRangeReferrer*>(this), + static_cast<view::XFormLayerAccess*>(this), + static_cast<view::XControlAccess*>(this), + static_cast<lang::XServiceInfo*>(this), + static_cast<lang::XTypeProvider*>(this)); + if ( aReturn.hasValue() ) + return aReturn; + + return uno::Any(); // OWeakObject is in derived objects +} + +uno::Sequence<uno::Type> SAL_CALL ScViewPaneBase::getTypes() +{ + static const uno::Sequence<uno::Type> aTypes + { + cppu::UnoType<sheet::XViewPane>::get(), + cppu::UnoType<sheet::XCellRangeReferrer>::get(), + cppu::UnoType<view::XFormLayerAccess>::get(), + cppu::UnoType<lang::XServiceInfo>::get(), + cppu::UnoType<lang::XTypeProvider>::get(), + }; + return aTypes; +} + +uno::Sequence<sal_Int8> SAL_CALL ScViewPaneBase::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +// XViewPane + +sal_Int32 SAL_CALL ScViewPaneBase::getFirstVisibleColumn() +{ + SolarMutexGuard aGuard; + if (pViewShell) + { + ScViewData& rViewData = pViewShell->GetViewData(); + ScSplitPos eWhich = ( nPane == SC_VIEWPANE_ACTIVE ) ? + rViewData.GetActivePart() : + static_cast<ScSplitPos>(nPane); + ScHSplitPos eWhichH = WhichH( eWhich ); + + return rViewData.GetPosX( eWhichH ); + } + OSL_FAIL("no View ?!?"); //! Exception? + return 0; +} + +void SAL_CALL ScViewPaneBase::setFirstVisibleColumn(sal_Int32 nFirstVisibleColumn) +{ + SolarMutexGuard aGuard; + if (pViewShell) + { + ScViewData& rViewData = pViewShell->GetViewData(); + ScSplitPos eWhich = ( nPane == SC_VIEWPANE_ACTIVE ) ? + rViewData.GetActivePart() : + static_cast<ScSplitPos>(nPane); + ScHSplitPos eWhichH = WhichH( eWhich ); + + tools::Long nDeltaX = static_cast<tools::Long>(nFirstVisibleColumn) - rViewData.GetPosX( eWhichH ); + pViewShell->ScrollX( nDeltaX, eWhichH ); + } +} + +sal_Int32 SAL_CALL ScViewPaneBase::getFirstVisibleRow() +{ + SolarMutexGuard aGuard; + if (pViewShell) + { + ScViewData& rViewData = pViewShell->GetViewData(); + ScSplitPos eWhich = ( nPane == SC_VIEWPANE_ACTIVE ) ? + rViewData.GetActivePart() : + static_cast<ScSplitPos>(nPane); + ScVSplitPos eWhichV = WhichV( eWhich ); + + return rViewData.GetPosY( eWhichV ); + } + OSL_FAIL("no View ?!?"); //! Exception? + return 0; +} + +void SAL_CALL ScViewPaneBase::setFirstVisibleRow( sal_Int32 nFirstVisibleRow ) +{ + SolarMutexGuard aGuard; + if (pViewShell) + { + ScViewData& rViewData = pViewShell->GetViewData(); + ScSplitPos eWhich = ( nPane == SC_VIEWPANE_ACTIVE ) ? + rViewData.GetActivePart() : + static_cast<ScSplitPos>(nPane); + ScVSplitPos eWhichV = WhichV( eWhich ); + + tools::Long nDeltaY = static_cast<tools::Long>(nFirstVisibleRow) - rViewData.GetPosY( eWhichV ); + pViewShell->ScrollY( nDeltaY, eWhichV ); + } +} + +table::CellRangeAddress SAL_CALL ScViewPaneBase::getVisibleRange() +{ + SolarMutexGuard aGuard; + table::CellRangeAddress aAdr; + if (pViewShell) + { + ScViewData& rViewData = pViewShell->GetViewData(); + ScSplitPos eWhich = ( nPane == SC_VIEWPANE_ACTIVE ) ? + rViewData.GetActivePart() : + static_cast<ScSplitPos>(nPane); + ScHSplitPos eWhichH = WhichH( eWhich ); + ScVSplitPos eWhichV = WhichV( eWhich ); + + // VisibleCellsX returns only completely visible cells + // VisibleRange in Excel also partially visible ones + //! do the same ??? + + SCCOL nVisX = rViewData.VisibleCellsX( eWhichH ); + SCROW nVisY = rViewData.VisibleCellsY( eWhichV ); + if (!nVisX) nVisX = 1; // there has to be something in the range + if (!nVisY) nVisY = 1; + aAdr.Sheet = rViewData.GetTabNo(); + aAdr.StartColumn = rViewData.GetPosX( eWhichH ); + aAdr.StartRow = rViewData.GetPosY( eWhichV ); + aAdr.EndColumn = aAdr.StartColumn + nVisX - 1; + aAdr.EndRow = aAdr.StartRow + nVisY - 1; + } + return aAdr; +} + +// XCellRangeSource + +uno::Reference<table::XCellRange> SAL_CALL ScViewPaneBase::getReferredCells() +{ + SolarMutexGuard aGuard; + if (pViewShell) + { + ScDocShell* pDocSh = pViewShell->GetViewData().GetDocShell(); + + table::CellRangeAddress aAdr(getVisibleRange()); //! helper function with ScRange? + ScRange aRange( static_cast<SCCOL>(aAdr.StartColumn), static_cast<SCROW>(aAdr.StartRow), aAdr.Sheet, + static_cast<SCCOL>(aAdr.EndColumn), static_cast<SCROW>(aAdr.EndRow), aAdr.Sheet ); + if ( aRange.aStart == aRange.aEnd ) + return new ScCellObj( pDocSh, aRange.aStart ); + else + return new ScCellRangeObj( pDocSh, aRange ); + } + + return nullptr; +} + +namespace +{ + bool lcl_prepareFormShellCall( ScTabViewShell* _pViewShell, sal_uInt16 _nPane, FmFormShell*& _rpFormShell, vcl::Window*& _rpWindow, SdrView*& _rpSdrView ) + { + if ( !_pViewShell ) + return false; + + ScViewData& rViewData = _pViewShell->GetViewData(); + ScSplitPos eWhich = ( _nPane == SC_VIEWPANE_ACTIVE ) ? + rViewData.GetActivePart() : + static_cast<ScSplitPos>(_nPane); + _rpWindow = _pViewShell->GetWindowByPos( eWhich ); + _rpSdrView = _pViewShell->GetScDrawView(); + _rpFormShell = _pViewShell->GetFormShell(); + return ( _rpFormShell != nullptr ) && ( _rpSdrView != nullptr )&& ( _rpWindow != nullptr ); + } +} + +// XFormLayerAccess +uno::Reference< form::runtime::XFormController > SAL_CALL ScViewPaneBase::getFormController( const uno::Reference< form::XForm >& Form ) +{ + SolarMutexGuard aGuard; + + uno::Reference< form::runtime::XFormController > xController; + + vcl::Window* pWindow( nullptr ); + SdrView* pSdrView( nullptr ); + FmFormShell* pFormShell( nullptr ); + if ( lcl_prepareFormShellCall( pViewShell, nPane, pFormShell, pWindow, pSdrView ) ) + xController = FmFormShell::GetFormController( Form, *pSdrView, *pWindow->GetOutDev() ); + + return xController; +} + +sal_Bool SAL_CALL ScViewPaneBase::isFormDesignMode( ) +{ + SolarMutexGuard aGuard; + + bool bIsFormDesignMode( true ); + + FmFormShell* pFormShell( pViewShell ? pViewShell->GetFormShell() : nullptr ); + if ( pFormShell ) + bIsFormDesignMode = pFormShell->IsDesignMode(); + + return bIsFormDesignMode; +} + +void SAL_CALL ScViewPaneBase::setFormDesignMode( sal_Bool DesignMode ) +{ + SolarMutexGuard aGuard; + + vcl::Window* pWindow( nullptr ); + SdrView* pSdrView( nullptr ); + FmFormShell* pFormShell( nullptr ); + if ( lcl_prepareFormShellCall( pViewShell, nPane, pFormShell, pWindow, pSdrView ) ) + pFormShell->SetDesignMode( DesignMode ); +} + +// XControlAccess + +uno::Reference<awt::XControl> SAL_CALL ScViewPaneBase::getControl( + const uno::Reference<awt::XControlModel>& xModel ) +{ + SolarMutexGuard aGuard; + + uno::Reference<awt::XControl> xRet; + + vcl::Window* pWindow( nullptr ); + SdrView* pSdrView( nullptr ); + FmFormShell* pFormShell( nullptr ); + if ( lcl_prepareFormShellCall( pViewShell, nPane, pFormShell, pWindow, pSdrView ) ) + pFormShell->GetFormControl( xModel, *pSdrView, *pWindow->GetOutDev(), xRet ); + + if ( !xRet.is() ) + throw container::NoSuchElementException(); // no control found + + return xRet; +} + +awt::Rectangle ScViewPaneBase::GetVisArea() const +{ + awt::Rectangle aVisArea; + if (pViewShell) + { + ScSplitPos eWhich = ( nPane == SC_VIEWPANE_ACTIVE ) ? + pViewShell->GetViewData().GetActivePart() : + static_cast<ScSplitPos>(nPane); + ScGridWindow* pWindow = static_cast<ScGridWindow*>(pViewShell->GetWindowByPos(eWhich)); + ScDocument& rDoc = pViewShell->GetViewData().GetDocument(); + if (pWindow) + { + ScHSplitPos eWhichH = ((eWhich == SC_SPLIT_TOPLEFT) || (eWhich == SC_SPLIT_BOTTOMLEFT)) ? + SC_SPLIT_LEFT : SC_SPLIT_RIGHT; + ScVSplitPos eWhichV = ((eWhich == SC_SPLIT_TOPLEFT) || (eWhich == SC_SPLIT_TOPRIGHT)) ? + SC_SPLIT_TOP : SC_SPLIT_BOTTOM; + ScAddress aCell(pViewShell->GetViewData().GetPosX(eWhichH), + pViewShell->GetViewData().GetPosY(eWhichV), + pViewShell->GetViewData().GetTabNo()); + tools::Rectangle aCellRect( rDoc.GetMMRect( aCell.Col(), aCell.Row(), aCell.Col(), aCell.Row(), aCell.Tab() ) ); + Size aVisSize( pWindow->PixelToLogic( pWindow->GetSizePixel(), pWindow->GetDrawMapMode( true ) ) ); + Point aVisPos( aCellRect.TopLeft() ); + if ( rDoc.IsLayoutRTL( aCell.Tab() ) ) + { + aVisPos = aCellRect.TopRight(); + aVisPos.AdjustX( -(aVisSize.Width()) ); + } + tools::Rectangle aVisRect( aVisPos, aVisSize ); + aVisArea = AWTRectangle(aVisRect); + } + } + return aVisArea; +} + +ScViewPaneObj::ScViewPaneObj(ScTabViewShell* pViewSh, sal_uInt16 nP) : + ScViewPaneBase( pViewSh, nP ) +{ +} + +ScViewPaneObj::~ScViewPaneObj() +{ +} + +uno::Any SAL_CALL ScViewPaneObj::queryInterface( const uno::Type& rType ) +{ + // ScViewPaneBase has everything except OWeakObject + + uno::Any aRet(ScViewPaneBase::queryInterface( rType )); + if (!aRet.hasValue()) + aRet = OWeakObject::queryInterface( rType ); + return aRet; +} + +void SAL_CALL ScViewPaneObj::acquire() noexcept +{ + OWeakObject::acquire(); +} + +void SAL_CALL ScViewPaneObj::release() noexcept +{ + OWeakObject::release(); +} + +// We need default ctor for SMART_REFLECTION_IMPLEMENTATION + +ScTabViewObj::ScTabViewObj( ScTabViewShell* pViewSh ) : + ScViewPaneBase( pViewSh, SC_VIEWPANE_ACTIVE ), + SfxBaseController( pViewSh ), + aPropSet( lcl_GetViewOptPropertyMap() ), + aMouseClickHandlers( 0 ), + aActivationListeners( 0 ), + nPreviousTab( 0 ), + bDrawSelModeSet(false), + bFilteredRangeSelection(false), + mbLeftMousePressed(false) +{ + if (pViewSh) + nPreviousTab = pViewSh->GetViewData().GetTabNo(); +} + +ScTabViewObj::~ScTabViewObj() +{ + //! Listening or something along that line + if (!aMouseClickHandlers.empty()) + { + acquire(); + EndMouseListening(); + } + if (!aActivationListeners.empty()) + { + acquire(); + EndActivationListening(); + } +} + +uno::Any SAL_CALL ScTabViewObj::queryInterface( const uno::Type& rType ) +{ + uno::Any aReturn = ::cppu::queryInterface(rType, + static_cast<sheet::XSpreadsheetView*>(this), + static_cast<sheet::XEnhancedMouseClickBroadcaster*>(this), + static_cast<sheet::XActivationBroadcaster*>(this), + static_cast<container::XEnumerationAccess*>(this), + static_cast<container::XIndexAccess*>(this), + static_cast<container::XElementAccess*>(static_cast<container::XIndexAccess*>(this)), + static_cast<view::XSelectionSupplier*>(this), + static_cast<beans::XPropertySet*>(this), + static_cast<sheet::XViewSplitable*>(this), + static_cast<sheet::XViewFreezable*>(this), + static_cast<sheet::XRangeSelection*>(this), + static_cast<sheet::XSelectedSheetsSupplier*>(this), + static_cast<datatransfer::XTransferableSupplier*>(this)); + if ( aReturn.hasValue() ) + return aReturn; + + uno::Any aRet(ScViewPaneBase::queryInterface( rType )); + if (!aRet.hasValue()) + aRet = SfxBaseController::queryInterface( rType ); + return aRet; +} + +void SAL_CALL ScTabViewObj::acquire() noexcept +{ + SfxBaseController::acquire(); +} + +void SAL_CALL ScTabViewObj::release() noexcept +{ + SfxBaseController::release(); +} + +static void lcl_CallActivate( ScDocShell* pDocSh, SCTAB nTab, ScSheetEventId nEvent ) +{ + ScDocument& rDoc = pDocSh->GetDocument(); + // when deleting a sheet, nPreviousTab can be invalid + // (could be handled with reference updates) + if (!rDoc.HasTable(nTab)) + return; + + const ScSheetEvents* pEvents = rDoc.GetSheetEvents(nTab); + if (pEvents) + { + const OUString* pScript = pEvents->GetScript(nEvent); + if (pScript) + { + uno::Any aRet; + uno::Sequence<uno::Any> aParams; + uno::Sequence<sal_Int16> aOutArgsIndex; + uno::Sequence<uno::Any> aOutArgs; + /*ErrCode eRet =*/ pDocSh->CallXScript( *pScript, aParams, aRet, aOutArgsIndex, aOutArgs ); + } + } + + // execute VBA event handlers + try + { + uno::Reference< script::vba::XVBAEventProcessor > xVbaEvents( rDoc.GetVbaEventProcessor(), uno::UNO_SET_THROW ); + // the parameter is the clicked object, as in the mousePressed call above + uno::Sequence< uno::Any > aArgs{ uno::Any(nTab) }; + xVbaEvents->processVbaEvent( ScSheetEvents::GetVbaSheetEventId( nEvent ), aArgs ); + } + catch( uno::Exception& ) + { + } +} + +void ScTabViewObj::SheetChanged( bool bSameTabButMoved ) +{ + if ( !GetViewShell() ) + return; + + ScViewData& rViewData = GetViewShell()->GetViewData(); + ScDocShell* pDocSh = rViewData.GetDocShell(); + if (!aActivationListeners.empty()) + { + sheet::ActivationEvent aEvent; + uno::Reference< sheet::XSpreadsheetView > xView(this); + aEvent.Source.set(xView, uno::UNO_QUERY); + aEvent.ActiveSheet = new ScTableSheetObj(pDocSh, rViewData.GetTabNo()); + // Listener's handler may remove it from the listeners list + for (size_t i = aActivationListeners.size(); i > 0; --i) + { + try + { + aActivationListeners[i - 1]->activeSpreadsheetChanged( aEvent ); + } + catch( uno::Exception& ) + { + aActivationListeners.erase(aActivationListeners.begin() + (i - 1)); + } + } + } + + /* Handle sheet events, but do not trigger event handlers, if the old + active sheet gets re-activated after inserting/deleting/moving a sheet. */ + SCTAB nNewTab = rViewData.GetTabNo(); + if ( !bSameTabButMoved && (nNewTab != nPreviousTab) ) + { + lcl_CallActivate( pDocSh, nPreviousTab, ScSheetEventId::UNFOCUS ); + lcl_CallActivate( pDocSh, nNewTab, ScSheetEventId::FOCUS ); + } + nPreviousTab = nNewTab; +} + +uno::Sequence<uno::Type> SAL_CALL ScTabViewObj::getTypes() +{ + return comphelper::concatSequences( + ScViewPaneBase::getTypes(), + SfxBaseController::getTypes(), + uno::Sequence<uno::Type> + { + cppu::UnoType<sheet::XSpreadsheetView>::get(), + cppu::UnoType<container::XEnumerationAccess>::get(), + cppu::UnoType<container::XIndexAccess>::get(), + cppu::UnoType<view::XSelectionSupplier>::get(), + cppu::UnoType<beans::XPropertySet>::get(), + cppu::UnoType<sheet::XViewSplitable>::get(), + cppu::UnoType<sheet::XViewFreezable>::get(), + cppu::UnoType<sheet::XRangeSelection>::get(), + cppu::UnoType<lang::XUnoTunnel>::get(), + cppu::UnoType<sheet::XEnhancedMouseClickBroadcaster>::get(), + cppu::UnoType<sheet::XActivationBroadcaster>::get(), + cppu::UnoType<datatransfer::XTransferableSupplier>::get() + } ); +} + +uno::Sequence<sal_Int8> SAL_CALL ScTabViewObj::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +// XDocumentView + +static bool lcl_TabInRanges( SCTAB nTab, const ScRangeList& rRanges ) +{ + for (size_t i = 0, nCount = rRanges.size(); i < nCount; ++i) + { + const ScRange & rRange = rRanges[ i ]; + if ( nTab >= rRange.aStart.Tab() && nTab <= rRange.aEnd.Tab() ) + return true; + } + return false; +} + +static void lcl_ShowObject( ScTabViewShell& rViewSh, const ScDrawView& rDrawView, const SdrObject* pSelObj ) +{ + bool bFound = false; + SCTAB nObjectTab = 0; + + SdrModel& rModel = rDrawView.GetModel(); + sal_uInt16 nPageCount = rModel.GetPageCount(); + for (sal_uInt16 i=0; i<nPageCount && !bFound; i++) + { + SdrPage* pPage = rModel.GetPage(i); + if (pPage) + { + SdrObjListIter aIter( pPage, SdrIterMode::DeepWithGroups ); + SdrObject* pObject = aIter.Next(); + while (pObject && !bFound) + { + if ( pObject == pSelObj ) + { + bFound = true; + nObjectTab = static_cast<SCTAB>(i); + } + pObject = aIter.Next(); + } + } + } + + if (bFound) + { + rViewSh.SetTabNo( nObjectTab ); + rViewSh.ScrollToObject( pSelObj ); + } +} + +sal_Bool SAL_CALL ScTabViewObj::select( const uno::Any& aSelection ) +{ + SolarMutexGuard aGuard; + ScTabViewShell* pViewSh = GetViewShell(); + + if ( !pViewSh ) + return false; + + //! Type of aSelection can be some specific interface instead of XInterface + + bool bRet = false; + uno::Reference<uno::XInterface> xInterface(aSelection, uno::UNO_QUERY); + if ( !xInterface.is() ) //clear all selections + { + ScDrawView* pDrawView = pViewSh->GetScDrawView(); + if (pDrawView) + { + pDrawView->ScEndTextEdit(); + pDrawView->UnmarkAll(); + } + else //#102232#; if there is no DrawView remove range selection + pViewSh->Unmark(); + bRet = true; + } + + if (bDrawSelModeSet) // remove DrawSelMode if set by API; if necessary it will be set again later + { + pViewSh->SetDrawSelMode(false); + pViewSh->UpdateLayerLocks(); + bDrawSelModeSet = false; + } + + if (bRet) + return bRet; + + ScCellRangesBase* pRangesImp = dynamic_cast<ScCellRangesBase*>( xInterface.get() ); + uno::Reference<drawing::XShapes> xShapeColl( xInterface, uno::UNO_QUERY ); + uno::Reference<drawing::XShape> xShapeSel( xInterface, uno::UNO_QUERY ); + SvxShape* pShapeImp = comphelper::getFromUnoTunnel<SvxShape>( xShapeSel ); + + if (pRangesImp) // Cell ranges + { + ScViewData& rViewData = pViewSh->GetViewData(); + if ( rViewData.GetDocShell() == pRangesImp->GetDocShell() ) + { + // perhaps remove drawing selection first + // (MarkListHasChanged removes sheet selection) + + ScDrawView* pDrawView = pViewSh->GetScDrawView(); + if (pDrawView) + { + pDrawView->ScEndTextEdit(); + pDrawView->UnmarkAll(); + } + FuPoor* pFunc = pViewSh->GetDrawFuncPtr(); + if ( pFunc && pFunc->GetSlotID() != SID_OBJECT_SELECT ) + { + // execute the slot of drawing function again -> switch off + SfxDispatcher* pDisp = pViewSh->GetDispatcher(); + if (pDisp) + pDisp->Execute( pFunc->GetSlotID(), SfxCallMode::SYNCHRON ); + } + pViewSh->SetDrawShell(false); + pViewSh->SetDrawSelMode(false); // after Dispatcher-Execute + + // select ranges + + const ScRangeList& rRanges = pRangesImp->GetRangeList(); + size_t nRangeCount = rRanges.size(); + // for empty range list, remove selection (cursor remains where it was) + if ( nRangeCount == 0 ) + pViewSh->Unmark(); + else if ( nRangeCount == 1 ) + pViewSh->MarkRange( rRanges[ 0 ] ); + else + { + // multiselection + + const ScRange & rFirst = rRanges[ 0 ]; + if ( !lcl_TabInRanges( rViewData.GetTabNo(), rRanges ) ) + pViewSh->SetTabNo( rFirst.aStart.Tab() ); + pViewSh->DoneBlockMode(); + pViewSh->InitOwnBlockMode( rFirst ); /* TODO: or even the overall range? */ + rViewData.GetMarkData().MarkFromRangeList( rRanges, true ); + pViewSh->MarkDataChanged(); + rViewData.GetDocShell()->PostPaintGridAll(); // Marks (old&new) + pViewSh->AlignToCursor( rFirst.aStart.Col(), rFirst.aStart.Row(), + SC_FOLLOW_JUMP ); + pViewSh->SetCursor( rFirst.aStart.Col(), rFirst.aStart.Row() ); + + //! method of the view to select RangeList + } + bRet = true; + } + } + else if ( pShapeImp || xShapeColl.is() ) // Drawing-Layer + { + ScDrawView* pDrawView = pViewSh->GetScDrawView(); + if (pDrawView) + { + pDrawView->ScEndTextEdit(); + pDrawView->UnmarkAll(); + + if (pShapeImp) // single shape + { + SdrObject *pObj = pShapeImp->GetSdrObject(); + if (pObj) + { + lcl_ShowObject( *pViewSh, *pDrawView, pObj ); + SdrPageView* pPV = pDrawView->GetSdrPageView(); + if ( pPV && pObj->getSdrPageFromSdrObject() == pPV->GetPage() ) + { + pDrawView->MarkObj( pObj, pPV ); + bRet = true; + } + } + } + else // Shape-Collection (xShapeColl is not 0) + { + // We'll switch to the sheet where the first object is + // and select all objects on that sheet + //!?throw exception when objects are on different sheets? + + tools::Long nCount = xShapeColl->getCount(); + if (nCount) + { + SdrPageView* pPV = nullptr; + bool bAllMarked(true); + for ( tools::Long i = 0; i < nCount; i++ ) + { + uno::Reference<drawing::XShape> xShapeInt(xShapeColl->getByIndex(i), uno::UNO_QUERY); + if (xShapeInt.is()) + { + SdrObject* pObj = SdrObject::getSdrObjectFromXShape( xShapeInt ); + if (pObj) + { + if (!bDrawSelModeSet && (pObj->GetLayer() == SC_LAYER_BACK)) + { + pViewSh->SetDrawSelMode(true); + pViewSh->UpdateLayerLocks(); + bDrawSelModeSet = true; + } + if (!pPV) // first object + { + lcl_ShowObject( *pViewSh, *pDrawView, pObj ); + pPV = pDrawView->GetSdrPageView(); + } + if ( pPV && pObj->getSdrPageFromSdrObject() == pPV->GetPage() ) + { + if (pDrawView->IsObjMarkable( pObj, pPV )) + pDrawView->MarkObj( pObj, pPV ); + else + bAllMarked = false; + } + } + } + } + if (bAllMarked) + bRet = true; + } + else + bRet = true; // empty XShapes (all shapes are deselected) + } + + if (bRet) + pViewSh->SetDrawShell(true); + } + } + + if (!bRet) + throw lang::IllegalArgumentException(); + + return bRet; +} + +uno::Reference<drawing::XShapes> ScTabViewShell::getSelectedXShapes() +{ + uno::Reference<drawing::XShapes> xShapes; + SdrView* pSdrView = GetScDrawView(); + if (pSdrView) + { + const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList(); + const size_t nMarkCount = rMarkList.GetMarkCount(); + if (nMarkCount) + { + // generate ShapeCollection (like in SdXImpressView::getSelection in Draw) + // XInterfaceRef will be returned and it has to be UsrObject-XInterface + xShapes = drawing::ShapeCollection::create(comphelper::getProcessComponentContext()); + + for (size_t i = 0; i < nMarkCount; ++i) + { + SdrObject* pDrawObj = rMarkList.GetMark(i)->GetMarkedSdrObj(); + if (pDrawObj) + { + uno::Reference<drawing::XShape> xShape( pDrawObj->getUnoShape(), uno::UNO_QUERY ); + if (xShape.is()) + xShapes->add(xShape); + } + } + } + } + return xShapes; +} + +uno::Any SAL_CALL ScTabViewObj::getSelection() +{ + SolarMutexGuard aGuard; + ScTabViewShell* pViewSh = GetViewShell(); + rtl::Reference<ScCellRangesBase> pObj; + if (pViewSh) + { + // is something selected in drawing layer? + uno::Reference<uno::XInterface> xRet(pViewSh->getSelectedXShapes()); + if (xRet.is()) + return uno::Any(xRet); + + // otherwise sheet (cell) selection + + ScViewData& rViewData = pViewSh->GetViewData(); + ScDocShell* pDocSh = rViewData.GetDocShell(); + + const ScMarkData& rMark = rViewData.GetMarkData(); + SCTAB nTabs = rMark.GetSelectCount(); + + ScRange aRange; + ScMarkType eMarkType = rViewData.GetSimpleArea(aRange); + if ( nTabs == 1 && (eMarkType == SC_MARK_SIMPLE) ) + { + // tdf#154803 - check if range is entirely merged + ScDocument& rDoc = pDocSh->GetDocument(); + const ScMergeAttr* pMergeAttr = rDoc.GetAttr(aRange.aStart, ATTR_MERGE); + SCCOL nColSpan = 1; + SCROW nRowSpan = 1; + if (pMergeAttr && pMergeAttr->IsMerged()) + { + nColSpan = pMergeAttr->GetColMerge(); + nRowSpan = pMergeAttr->GetRowMerge(); + } + // tdf#147122 - return cell object when a simple selection is entirely merged + if (aRange.aStart == aRange.aEnd + || (aRange.aEnd.Col() - aRange.aStart.Col() == nColSpan - 1 + && aRange.aEnd.Row() - aRange.aStart.Row() == nRowSpan - 1)) + pObj = new ScCellObj( pDocSh, aRange.aStart ); + else + pObj = new ScCellRangeObj( pDocSh, aRange ); + } + else if ( nTabs == 1 && (eMarkType == SC_MARK_SIMPLE_FILTERED) ) + { + ScMarkData aFilteredMark( rMark ); + ScViewUtil::UnmarkFiltered( aFilteredMark, pDocSh->GetDocument()); + ScRangeList aRangeList; + aFilteredMark.FillRangeListWithMarks( &aRangeList, false); + // Theoretically a selection may start and end on a filtered row. + switch ( aRangeList.size() ) + { + case 0: + // No unfiltered row, we have to return some object, so + // here is one with no ranges. + pObj = new ScCellRangesObj( pDocSh, aRangeList ); + break; + case 1: + { + const ScRange& rRange = aRangeList[ 0 ]; + if (rRange.aStart == rRange.aEnd) + pObj = new ScCellObj( pDocSh, rRange.aStart ); + else + pObj = new ScCellRangeObj( pDocSh, rRange ); + } + break; + default: + pObj = new ScCellRangesObj( pDocSh, aRangeList ); + } + } + else // multiselection + { + ScRangeListRef xRanges; + rViewData.GetMultiArea( xRanges ); + + // if there are more sheets, copy ranges + //! should this happen in ScMarkData::FillRangeListWithMarks already? + if ( nTabs > 1 ) + rMark.ExtendRangeListTables( xRanges.get() ); + + pObj = new ScCellRangesObj( pDocSh, *xRanges ); + } + + if ( !rMark.IsMarked() && !rMark.IsMultiMarked() ) + { + // remember if the selection was from the cursor position without anything selected + // (used when rendering the selection) + + pObj->SetCursorOnly( true ); + } + } + + return uno::Any(uno::Reference(cppu::getXWeak(pObj.get()))); +} + +// XEnumerationAccess + +uno::Reference<container::XEnumeration> SAL_CALL ScTabViewObj::createEnumeration() +{ + SolarMutexGuard aGuard; + return new ScIndexEnumeration(this, "com.sun.star.sheet.SpreadsheetViewPanesEnumeration"); +} + +// XIndexAccess + +sal_Int32 SAL_CALL ScTabViewObj::getCount() +{ + SolarMutexGuard aGuard; + ScTabViewShell* pViewSh = GetViewShell(); + sal_uInt16 nPanes = 0; + if (pViewSh) + { + nPanes = 1; + ScViewData& rViewData = pViewSh->GetViewData(); + if ( rViewData.GetHSplitMode() != SC_SPLIT_NONE ) + nPanes *= 2; + if ( rViewData.GetVSplitMode() != SC_SPLIT_NONE ) + nPanes *= 2; + } + return nPanes; +} + +uno::Any SAL_CALL ScTabViewObj::getByIndex( sal_Int32 nIndex ) +{ + SolarMutexGuard aGuard; + uno::Reference<sheet::XViewPane> xPane(GetObjectByIndex_Impl(static_cast<sal_uInt16>(nIndex))); + if (!xPane.is()) + throw lang::IndexOutOfBoundsException(); + + return uno::Any(xPane); +} + +uno::Type SAL_CALL ScTabViewObj::getElementType() +{ + return cppu::UnoType<sheet::XViewPane>::get(); +} + +sal_Bool SAL_CALL ScTabViewObj::hasElements() +{ + SolarMutexGuard aGuard; + return ( getCount() != 0 ); +} + +// XSpreadsheetView + +rtl::Reference<ScViewPaneObj> ScTabViewObj::GetObjectByIndex_Impl(sal_uInt16 nIndex) const +{ + static const ScSplitPos ePosHV[4] = + { SC_SPLIT_TOPLEFT, SC_SPLIT_BOTTOMLEFT, SC_SPLIT_TOPRIGHT, SC_SPLIT_BOTTOMRIGHT }; + + ScTabViewShell* pViewSh = GetViewShell(); + if (pViewSh) + { + ScSplitPos eWhich = SC_SPLIT_BOTTOMLEFT; // default position + bool bError = false; + ScViewData& rViewData = pViewSh->GetViewData(); + bool bHor = ( rViewData.GetHSplitMode() != SC_SPLIT_NONE ); + bool bVer = ( rViewData.GetVSplitMode() != SC_SPLIT_NONE ); + if ( bHor && bVer ) + { + // bottom left, bottom right, top left, top right - like in Excel + if ( nIndex < 4 ) + eWhich = ePosHV[nIndex]; + else + bError = true; + } + else if ( bHor ) + { + if ( nIndex > 1 ) + bError = true; + else if ( nIndex == 1 ) + eWhich = SC_SPLIT_BOTTOMRIGHT; + // otherwise SC_SPLIT_BOTTOMLEFT + } + else if ( bVer ) + { + if ( nIndex > 1 ) + bError = true; + else if ( nIndex == 0 ) + eWhich = SC_SPLIT_TOPLEFT; + // otherwise SC_SPLIT_BOTTOMLEFT + } + else if ( nIndex > 0 ) + bError = true; // not split: only 0 is valid + + if (!bError) + return new ScViewPaneObj( pViewSh, sal::static_int_cast<sal_uInt16>(eWhich) ); + } + + return nullptr; +} + +uno::Reference<sheet::XSpreadsheet> SAL_CALL ScTabViewObj::getActiveSheet() +{ + SolarMutexGuard aGuard; + ScTabViewShell* pViewSh = GetViewShell(); + if (pViewSh) + { + ScViewData& rViewData = pViewSh->GetViewData(); + SCTAB nTab = rViewData.GetTabNo(); + return new ScTableSheetObj( rViewData.GetDocShell(), nTab ); + } + return nullptr; +} + +// support expand (but not replace) the active sheet +void SAL_CALL ScTabViewObj::setActiveSheet( const uno::Reference<sheet::XSpreadsheet>& xActiveSheet ) +{ + SolarMutexGuard aGuard; + comphelper::ProfileZone aZone("setActiveSheet"); + + ScTabViewShell* pViewSh = GetViewShell(); + if ( !(pViewSh && xActiveSheet.is()) ) + return; + + // XSpreadsheet and ScCellRangesBase -> has to be the same sheet + + ScCellRangesBase* pRangesImp = dynamic_cast<ScCellRangesBase*>( xActiveSheet.get() ); + if ( pRangesImp && pViewSh->GetViewData().GetDocShell() == pRangesImp->GetDocShell() ) + { + const ScRangeList& rRanges = pRangesImp->GetRangeList(); + if ( rRanges.size() == 1 ) + { + SCTAB nNewTab = rRanges[ 0 ].aStart.Tab(); + if ( pViewSh->GetViewData().GetDocument().HasTable(nNewTab) ) + pViewSh->SetTabNo( nNewTab ); + } + } +} + +uno::Reference< uno::XInterface > ScTabViewObj::GetClickedObject(const Point& rPoint) const +{ + uno::Reference< uno::XInterface > xTarget; + if (GetViewShell()) + { + SCCOL nX; + SCROW nY; + ScViewData& rData = GetViewShell()->GetViewData(); + ScSplitPos eSplitMode = rData.GetActivePart(); + SCTAB nTab(rData.GetTabNo()); + rData.GetPosFromPixel( rPoint.X(), rPoint.Y(), eSplitMode, nX, nY); + + ScAddress aCellPos (nX, nY, nTab); + rtl::Reference<ScCellObj> pCellObj = new ScCellObj(rData.GetDocShell(), aCellPos); + + xTarget.set(uno::Reference<table::XCell>(pCellObj), uno::UNO_QUERY); + + ScDocument& rDoc = rData.GetDocument(); + if (ScDrawLayer* pDrawLayer = rDoc.GetDrawLayer()) + { + SdrPage* pDrawPage = nullptr; + if (pDrawLayer->HasObjects() && (pDrawLayer->GetPageCount() > nTab)) + pDrawPage = pDrawLayer->GetPage(static_cast<sal_uInt16>(nTab)); + + SdrView* pDrawView = GetViewShell()->GetScDrawView(); + + if (pDrawPage && pDrawView && pDrawView->GetSdrPageView()) + { + vcl::Window* pActiveWin = rData.GetActiveWin(); + Point aPos = pActiveWin->PixelToLogic(rPoint); + + double fHitLog = pActiveWin->PixelToLogic(Size(pDrawView->GetHitTolerancePixel(),0)).Width(); + + for (const rtl::Reference<SdrObject>& pObj : *pDrawPage) + { + if (SdrObjectPrimitiveHit(*pObj, aPos, {fHitLog, fHitLog}, *pDrawView->GetSdrPageView(), nullptr, false)) + { + xTarget.set(pObj->getUnoShape(), uno::UNO_QUERY); + break; + } + } + } + } + } + return xTarget; +} + +bool ScTabViewObj::IsMouseListening() const +{ + if ( !aMouseClickHandlers.empty() ) + return true; + + // also include sheet events, because MousePressed must be called for them + ScViewData& rViewData = GetViewShell()->GetViewData(); + ScDocument& rDoc = rViewData.GetDocument(); + SCTAB nTab = rViewData.GetTabNo(); + return + rDoc.HasSheetEventScript( nTab, ScSheetEventId::RIGHTCLICK, true ) || + rDoc.HasSheetEventScript( nTab, ScSheetEventId::DOUBLECLICK, true ) || + rDoc.HasSheetEventScript( nTab, ScSheetEventId::SELECT, true ); + +} + +bool ScTabViewObj::MousePressed( const awt::MouseEvent& e ) +{ + bool bReturn(false); + if ( e.Buttons == css::awt::MouseButton::LEFT ) + mbLeftMousePressed = true; + + uno::Reference< uno::XInterface > xTarget = GetClickedObject(Point(e.X, e.Y)); + if (!aMouseClickHandlers.empty() && xTarget.is()) + { + awt::EnhancedMouseEvent aMouseEvent; + + aMouseEvent.Buttons = e.Buttons; + aMouseEvent.X = e.X; + aMouseEvent.Y = e.Y; + aMouseEvent.ClickCount = e.ClickCount; + aMouseEvent.PopupTrigger = e.PopupTrigger; + aMouseEvent.Target = xTarget; + aMouseEvent.Modifiers = e.Modifiers; + + // Listener's handler may remove it from the listeners list + for (size_t i = aMouseClickHandlers.size(); i > 0; --i) + { + try + { + if (!aMouseClickHandlers[i - 1]->mousePressed(aMouseEvent)) + bReturn = true; + } + catch ( uno::Exception& ) + { + aMouseClickHandlers.erase(aMouseClickHandlers.begin() + (i - 1)); + } + } + } + + // handle sheet events + bool bDoubleClick = ( e.Buttons == awt::MouseButton::LEFT && e.ClickCount == 2 ); + bool bRightClick = ( e.Buttons == awt::MouseButton::RIGHT && e.ClickCount == 1 ); + if ( ( bDoubleClick || bRightClick ) && !bReturn && xTarget.is()) + { + ScSheetEventId nEvent = bDoubleClick ? ScSheetEventId::DOUBLECLICK : ScSheetEventId::RIGHTCLICK; + + ScTabViewShell* pViewSh = GetViewShell(); + ScViewData& rViewData = pViewSh->GetViewData(); + ScDocShell* pDocSh = rViewData.GetDocShell(); + ScDocument& rDoc = pDocSh->GetDocument(); + SCTAB nTab = rViewData.GetTabNo(); + const ScSheetEvents* pEvents = rDoc.GetSheetEvents(nTab); + if (pEvents) + { + const OUString* pScript = pEvents->GetScript(nEvent); + if (pScript) + { + // the macro parameter is the clicked object, as in the mousePressed call above + uno::Sequence<uno::Any> aParams{ uno::Any(xTarget) }; + + uno::Any aRet; + uno::Sequence<sal_Int16> aOutArgsIndex; + uno::Sequence<uno::Any> aOutArgs; + + /*ErrCode eRet =*/ pDocSh->CallXScript( *pScript, aParams, aRet, aOutArgsIndex, aOutArgs ); + + // look for a boolean return value of true + bool bRetValue = false; + if ((aRet >>= bRetValue) && bRetValue) + bReturn = true; + } + } + + // execute VBA event handler + if (!bReturn && xTarget.is()) try + { + uno::Reference< script::vba::XVBAEventProcessor > xVbaEvents( rDoc.GetVbaEventProcessor(), uno::UNO_SET_THROW ); + // the parameter is the clicked object, as in the mousePressed call above + uno::Sequence< uno::Any > aArgs{ uno::Any(xTarget) }; + xVbaEvents->processVbaEvent( ScSheetEvents::GetVbaSheetEventId( nEvent ), aArgs ); + } + catch( util::VetoException& ) + { + bReturn = true; + } + catch( uno::Exception& ) + { + } + } + + return bReturn; +} + +bool ScTabViewObj::MouseReleased( const awt::MouseEvent& e ) +{ + if ( e.Buttons == css::awt::MouseButton::LEFT ) + { + try + { + ScTabViewShell* pViewSh = GetViewShell(); + ScViewData& rViewData = pViewSh->GetViewData(); + ScDocShell* pDocSh = rViewData.GetDocShell(); + ScDocument& rDoc = pDocSh->GetDocument(); + uno::Reference< script::vba::XVBAEventProcessor > xVbaEvents( rDoc.GetVbaEventProcessor(), uno::UNO_SET_THROW ); + uno::Sequence< uno::Any > aArgs{ getSelection() }; + xVbaEvents->processVbaEvent( ScSheetEvents::GetVbaSheetEventId( ScSheetEventId::SELECT ), aArgs ); + } + catch( uno::Exception& ) + { + } + mbLeftMousePressed = false; + } + + bool bReturn(false); + + if (!aMouseClickHandlers.empty()) + { + uno::Reference< uno::XInterface > xTarget = GetClickedObject(Point(e.X, e.Y)); + + if (xTarget.is()) + { + awt::EnhancedMouseEvent aMouseEvent; + + aMouseEvent.Buttons = e.Buttons; + aMouseEvent.X = e.X; + aMouseEvent.Y = e.Y; + aMouseEvent.ClickCount = e.ClickCount; + aMouseEvent.PopupTrigger = e.PopupTrigger; + aMouseEvent.Target = xTarget; + aMouseEvent.Modifiers = e.Modifiers; + + // Listener's handler may remove it from the listeners list + for (size_t i = aMouseClickHandlers.size(); i > 0; --i) + { + try + { + if (!aMouseClickHandlers[i - 1]->mouseReleased( aMouseEvent )) + bReturn = true; + } + catch ( uno::Exception& ) + { + aMouseClickHandlers.erase(aMouseClickHandlers.begin() + (i - 1)); + } + } + } + } + return bReturn; +} + +// XEnhancedMouseClickBroadcaster + +void ScTabViewObj::EndMouseListening() +{ + lang::EventObject aEvent; + aEvent.Source = getXWeak(); + for (const auto& rListener : aMouseClickHandlers) + { + try + { + rListener->disposing(aEvent); + } + catch ( uno::Exception& ) + { + } + } + aMouseClickHandlers.clear(); +} + +void ScTabViewObj::EndActivationListening() +{ + lang::EventObject aEvent; + aEvent.Source = getXWeak(); + for (const auto& rListener : aActivationListeners) + { + try + { + rListener->disposing(aEvent); + } + catch ( uno::Exception& ) + { + } + } + aActivationListeners.clear(); +} + +void SAL_CALL ScTabViewObj::addEnhancedMouseClickHandler( const uno::Reference< awt::XEnhancedMouseClickHandler >& aListener ) +{ + SolarMutexGuard aGuard; + + if (aListener.is()) + { + aMouseClickHandlers.push_back( aListener ); + } +} + +void SAL_CALL ScTabViewObj::removeEnhancedMouseClickHandler( const uno::Reference< awt::XEnhancedMouseClickHandler >& aListener ) +{ + SolarMutexGuard aGuard; + sal_uInt16 nCount = aMouseClickHandlers.size(); + std::erase(aMouseClickHandlers, aListener); + if (aMouseClickHandlers.empty() && (nCount > 0)) // only if last listener removed + EndMouseListening(); +} + +// XActivationBroadcaster + +void SAL_CALL ScTabViewObj::addActivationEventListener( const uno::Reference< sheet::XActivationEventListener >& aListener ) +{ + SolarMutexGuard aGuard; + + if (aListener.is()) + { + aActivationListeners.push_back( aListener ); + } +} + +void SAL_CALL ScTabViewObj::removeActivationEventListener( const uno::Reference< sheet::XActivationEventListener >& aListener ) +{ + SolarMutexGuard aGuard; + sal_uInt16 nCount = aActivationListeners.size(); + std::erase(aActivationListeners, aListener); + if (aActivationListeners.empty() && (nCount > 0)) // only if last listener removed + EndActivationListening(); +} + +sal_Int16 ScTabViewObj::GetZoom() const +{ + ScTabViewShell* pViewSh = GetViewShell(); + if (pViewSh) + { + const Fraction& rZoomY = pViewSh->GetViewData().GetZoomY(); // Y will be shown + return static_cast<sal_Int16>(tools::Long( rZoomY * 100 )); + } + return 0; +} + +void ScTabViewObj::SetZoom(sal_Int16 nZoom) +{ + ScTabViewShell* pViewSh = GetViewShell(); + if (!pViewSh) + return; + + if ( nZoom != GetZoom() && nZoom != 0 ) + { + if (!pViewSh->GetViewData().IsPagebreakMode()) + { + ScModule* pScMod = SC_MOD(); + ScAppOptions aNewOpt(pScMod->GetAppOptions()); + aNewOpt.SetZoom( nZoom ); + aNewOpt.SetZoomType( pViewSh->GetViewData().GetView()->GetZoomType() ); + pScMod->SetAppOptions( aNewOpt ); + } + } + Fraction aFract( nZoom, 100 ); + pViewSh->SetZoom( aFract, aFract, true ); + pViewSh->PaintGrid(); + pViewSh->PaintTop(); + pViewSh->PaintLeft(); + pViewSh->GetViewFrame().GetBindings().Invalidate( SID_ATTR_ZOOM ); + pViewSh->GetViewFrame().GetBindings().Invalidate( SID_ATTR_ZOOMSLIDER ); + pViewSh->GetViewFrame().GetBindings().Invalidate(SID_ZOOM_IN); + pViewSh->GetViewFrame().GetBindings().Invalidate(SID_ZOOM_OUT); +} + +sal_Int16 ScTabViewObj::GetZoomType() const +{ + sal_Int16 aZoomType = view::DocumentZoomType::OPTIMAL; + ScTabViewShell* pViewSh = GetViewShell(); + if (pViewSh) + { + SvxZoomType eZoomType = pViewSh->GetViewData().GetView()->GetZoomType(); + switch (eZoomType) + { + case SvxZoomType::PERCENT: + aZoomType = view::DocumentZoomType::BY_VALUE; + break; + case SvxZoomType::OPTIMAL: + aZoomType = view::DocumentZoomType::OPTIMAL; + break; + case SvxZoomType::WHOLEPAGE: + aZoomType = view::DocumentZoomType::ENTIRE_PAGE; + break; + case SvxZoomType::PAGEWIDTH: + aZoomType = view::DocumentZoomType::PAGE_WIDTH; + break; + case SvxZoomType::PAGEWIDTH_NOBORDER: + aZoomType = view::DocumentZoomType::PAGE_WIDTH_EXACT; + break; + } + } + return aZoomType; +} + +void ScTabViewObj::SetZoomType(sal_Int16 aZoomType) +{ + ScTabViewShell* pViewSh = GetViewShell(); + if (!pViewSh) + return; + + ScDBFunc* pView = pViewSh->GetViewData().GetView(); + if (!pView) + return; + + SvxZoomType eZoomType; + switch (aZoomType) + { + case view::DocumentZoomType::BY_VALUE: + eZoomType = SvxZoomType::PERCENT; + break; + case view::DocumentZoomType::OPTIMAL: + eZoomType = SvxZoomType::OPTIMAL; + break; + case view::DocumentZoomType::ENTIRE_PAGE: + eZoomType = SvxZoomType::WHOLEPAGE; + break; + case view::DocumentZoomType::PAGE_WIDTH: + eZoomType = SvxZoomType::PAGEWIDTH; + break; + case view::DocumentZoomType::PAGE_WIDTH_EXACT: + eZoomType = SvxZoomType::PAGEWIDTH_NOBORDER; + break; + default: + eZoomType = SvxZoomType::OPTIMAL; + } + sal_Int16 nZoom(GetZoom()); + sal_Int16 nOldZoom(nZoom); + if ( eZoomType == SvxZoomType::PERCENT ) + { + if ( nZoom < MINZOOM ) nZoom = MINZOOM; + if ( nZoom > MAXZOOM ) nZoom = MAXZOOM; + } + else + nZoom = pView->CalcZoom( eZoomType, nOldZoom ); + + switch ( eZoomType ) + { + case SvxZoomType::WHOLEPAGE: + case SvxZoomType::PAGEWIDTH: + pView->SetZoomType( eZoomType, true ); + break; + + default: + pView->SetZoomType( SvxZoomType::PERCENT, true ); + } + SetZoom( nZoom ); +} + +sal_Bool SAL_CALL ScTabViewObj::getIsWindowSplit() +{ + SolarMutexGuard aGuard; + // what menu slot SID_WINDOW_SPLIT does + + ScTabViewShell* pViewSh = GetViewShell(); + if (pViewSh) + { + ScViewData& rViewData = pViewSh->GetViewData(); + return ( rViewData.GetHSplitMode() == SC_SPLIT_NORMAL || + rViewData.GetVSplitMode() == SC_SPLIT_NORMAL ); + } + + return false; +} + +sal_Bool SAL_CALL ScTabViewObj::hasFrozenPanes() +{ + SolarMutexGuard aGuard; + // what menu slot SID_WINDOW_FIX does + + ScTabViewShell* pViewSh = GetViewShell(); + if (pViewSh) + { + ScViewData& rViewData = pViewSh->GetViewData(); + return ( rViewData.GetHSplitMode() == SC_SPLIT_FIX || + rViewData.GetVSplitMode() == SC_SPLIT_FIX ); + } + + return false; +} + +sal_Int32 SAL_CALL ScTabViewObj::getSplitHorizontal() +{ + SolarMutexGuard aGuard; + ScTabViewShell* pViewSh = GetViewShell(); + if (pViewSh) + { + ScViewData& rViewData = pViewSh->GetViewData(); + if ( rViewData.GetHSplitMode() != SC_SPLIT_NONE ) + return rViewData.GetHSplitPos(); + } + return 0; +} + +sal_Int32 SAL_CALL ScTabViewObj::getSplitVertical() +{ + SolarMutexGuard aGuard; + ScTabViewShell* pViewSh = GetViewShell(); + if (pViewSh) + { + ScViewData& rViewData = pViewSh->GetViewData(); + if ( rViewData.GetVSplitMode() != SC_SPLIT_NONE ) + return rViewData.GetVSplitPos(); + } + return 0; +} + +sal_Int32 SAL_CALL ScTabViewObj::getSplitColumn() +{ + SolarMutexGuard aGuard; + ScTabViewShell* pViewSh = GetViewShell(); + if (pViewSh) + { + ScViewData& rViewData = pViewSh->GetViewData(); + if ( rViewData.GetHSplitMode() != SC_SPLIT_NONE ) + { + tools::Long nSplit = rViewData.GetHSplitPos(); + + ScSplitPos ePos = SC_SPLIT_BOTTOMLEFT; + if ( rViewData.GetVSplitMode() != SC_SPLIT_NONE ) + ePos = SC_SPLIT_TOPLEFT; + + SCCOL nCol; + SCROW nRow; + rViewData.GetPosFromPixel( nSplit, 0, ePos, nCol, nRow, false ); + if ( nCol > 0 ) + return nCol; + } + } + return 0; +} + +sal_Int32 SAL_CALL ScTabViewObj::getSplitRow() +{ + SolarMutexGuard aGuard; + ScTabViewShell* pViewSh = GetViewShell(); + if (pViewSh) + { + ScViewData& rViewData = pViewSh->GetViewData(); + if ( rViewData.GetVSplitMode() != SC_SPLIT_NONE ) + { + tools::Long nSplit = rViewData.GetVSplitPos(); + + // split vertically + SCCOL nCol; + SCROW nRow; + rViewData.GetPosFromPixel( 0, nSplit, SC_SPLIT_TOPLEFT, nCol, nRow, false ); + if ( nRow > 0 ) + return nRow; + } + } + return 0; +} + +void SAL_CALL ScTabViewObj::splitAtPosition( sal_Int32 nPixelX, sal_Int32 nPixelY ) +{ + SolarMutexGuard aGuard; + ScTabViewShell* pViewSh = GetViewShell(); + if (pViewSh) + { + pViewSh->SplitAtPixel( Point( nPixelX, nPixelY ) ); + pViewSh->FreezeSplitters( false ); + pViewSh->InvalidateSplit(); + } +} + +void SAL_CALL ScTabViewObj::freezeAtPosition( sal_Int32 nColumns, sal_Int32 nRows ) +{ + SolarMutexGuard aGuard; + ScTabViewShell* pViewSh = GetViewShell(); + if (!pViewSh) + return; + + // first, remove them all -> no stress with scrolling in the meantime + + pViewSh->RemoveSplit(); + + Point aWinStart; + vcl::Window* pWin = pViewSh->GetWindowByPos( SC_SPLIT_BOTTOMLEFT ); + if (pWin) + aWinStart = pWin->GetPosPixel(); + + ScViewData& rViewData = pViewSh->GetViewData(); + Point aSplit(rViewData.GetScrPos( static_cast<SCCOL>(nColumns), static_cast<SCROW>(nRows), SC_SPLIT_BOTTOMLEFT, true )); + aSplit += aWinStart; + + pViewSh->SplitAtPixel( aSplit ); + pViewSh->FreezeSplitters( true ); + pViewSh->InvalidateSplit(); +} + +void SAL_CALL ScTabViewObj::addSelectionChangeListener( + const uno::Reference<view::XSelectionChangeListener>& xListener ) +{ + SolarMutexGuard aGuard; + aSelectionChgListeners.push_back( xListener ); +} + +void SAL_CALL ScTabViewObj::removeSelectionChangeListener( + const uno::Reference< view::XSelectionChangeListener >& xListener ) +{ + SolarMutexGuard aGuard; + auto it = std::find(aSelectionChgListeners.begin(), aSelectionChgListeners.end(), xListener); //! why the hassle with queryInterface? + if (it != aSelectionChgListeners.end()) + aSelectionChgListeners.erase(it); +} + +void ScTabViewObj::SelectionChanged() +{ + // Selection changed so end any style preview + // Note: executing this slot through the dispatcher + // will cause the style dialog to be raised so we go + // direct here + ScFormatShell aShell( GetViewShell()->GetViewData() ); + SfxAllItemSet reqList( SfxGetpApp()->GetPool() ); + SfxRequest aReq( SID_STYLE_END_PREVIEW, SfxCallMode::SLOT, reqList ); + aShell.ExecuteStyle( aReq ); + lang::EventObject aEvent; + aEvent.Source.set(getXWeak()); + for (const auto& rListener : aSelectionChgListeners) + rListener->selectionChanged( aEvent ); + + // handle sheet events + ScTabViewShell* pViewSh = GetViewShell(); + ScViewData& rViewData = pViewSh->GetViewData(); + ScDocShell* pDocSh = rViewData.GetDocShell(); + ScDocument& rDoc = pDocSh->GetDocument(); + SCTAB nTab = rViewData.GetTabNo(); + const ScSheetEvents* pEvents = rDoc.GetSheetEvents(nTab); + if (pEvents) + { + const OUString* pScript = pEvents->GetScript(ScSheetEventId::SELECT); + if (pScript) + { + // the macro parameter is the selection as returned by getSelection + uno::Sequence<uno::Any> aParams{ getSelection() }; + uno::Any aRet; + uno::Sequence<sal_Int16> aOutArgsIndex; + uno::Sequence<uno::Any> aOutArgs; + /*ErrCode eRet =*/ pDocSh->CallXScript( *pScript, aParams, aRet, aOutArgsIndex, aOutArgs ); + } + } + + SfxApplication::Get()->Broadcast( SfxHint( SfxHintId::ScSelectionChanged ) ); + + if ( mbLeftMousePressed ) // selection still in progress + return; + + try + { + uno::Reference< script::vba::XVBAEventProcessor > xVbaEvents( rDoc.GetVbaEventProcessor(), uno::UNO_SET_THROW ); + uno::Sequence< uno::Any > aArgs{ getSelection() }; + xVbaEvents->processVbaEvent( ScSheetEvents::GetVbaSheetEventId( ScSheetEventId::SELECT ), aArgs ); + } + catch( uno::Exception& ) + { + } +} + +// XPropertySet (view options) +//! provide those also in application? + +uno::Reference<beans::XPropertySetInfo> SAL_CALL ScTabViewObj::getPropertySetInfo() +{ + SolarMutexGuard aGuard; + static uno::Reference<beans::XPropertySetInfo> aRef( + new SfxItemPropertySetInfo( aPropSet.getPropertyMap() )); + return aRef; +} + +void SAL_CALL ScTabViewObj::setPropertyValue( + const OUString& aPropertyName, const uno::Any& aValue ) +{ + SolarMutexGuard aGuard; + + if ( aPropertyName == SC_UNO_FILTERED_RANGE_SELECTION ) + { + bFilteredRangeSelection = ScUnoHelpFunctions::GetBoolFromAny(aValue); + return; + } + + ScTabViewShell* pViewSh = GetViewShell(); + if (!pViewSh) + return; + + ScViewData& rViewData = pViewSh->GetViewData(); + const ScViewOptions& rOldOpt = pViewSh->GetViewData().GetOptions(); + ScViewOptions aNewOpt(rOldOpt); + + if ( aPropertyName == SC_UNO_COLROWHDR || aPropertyName == OLD_UNO_COLROWHDR ) + aNewOpt.SetOption( VOPT_HEADER, ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + else if ( aPropertyName == SC_UNO_HORSCROLL || aPropertyName == OLD_UNO_HORSCROLL ) + aNewOpt.SetOption( VOPT_HSCROLL, ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + else if ( aPropertyName == SC_UNO_OUTLSYMB || aPropertyName == OLD_UNO_OUTLSYMB ) + aNewOpt.SetOption( VOPT_OUTLINER, ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + else if ( aPropertyName == SC_UNO_SHEETTABS || aPropertyName == OLD_UNO_SHEETTABS ) + aNewOpt.SetOption( VOPT_TABCONTROLS, ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + else if ( aPropertyName == SC_UNO_SHOWANCHOR ) + aNewOpt.SetOption( VOPT_ANCHOR, ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + else if ( aPropertyName == SC_UNO_SHOWFORM ) + aNewOpt.SetOption( VOPT_FORMULAS, ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + else if ( aPropertyName == SC_UNO_SHOWGRID ) + aNewOpt.SetOption( VOPT_GRID, ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + else if ( aPropertyName == SC_UNO_SHOWHELP ) + aNewOpt.SetOption( VOPT_HELPLINES, ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + else if ( aPropertyName == SC_UNO_SHOWNOTES ) + aNewOpt.SetOption( VOPT_NOTES, ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + else if ( aPropertyName == SC_UNO_SHOWFORMULASMARKS ) + aNewOpt.SetOption( VOPT_FORMULAS_MARKS, ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + else if ( aPropertyName == SC_UNO_SHOWPAGEBR ) + aNewOpt.SetOption( VOPT_PAGEBREAKS, ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + else if ( aPropertyName == SC_UNO_SHOWZERO ) + aNewOpt.SetOption( VOPT_NULLVALS, ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + else if ( aPropertyName == SC_UNO_VALUEHIGH || aPropertyName == OLD_UNO_VALUEHIGH ) + aNewOpt.SetOption( VOPT_SYNTAX, ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + else if ( aPropertyName == SC_UNO_VERTSCROLL || aPropertyName == OLD_UNO_VERTSCROLL ) + aNewOpt.SetOption( VOPT_VSCROLL, ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); + else if ( aPropertyName == SC_UNO_SHOWOBJ ) + { + sal_Int16 nIntVal = 0; + if ( aValue >>= nIntVal ) + { + //#i80528# adapt to new range eventually + if(sal_Int16(VOBJ_MODE_HIDE) < nIntVal) nIntVal = sal_Int16(VOBJ_MODE_SHOW); + + aNewOpt.SetObjMode( VOBJ_TYPE_OLE, static_cast<ScVObjMode>(nIntVal)); + } + } + else if ( aPropertyName == SC_UNO_SHOWCHARTS ) + { + sal_Int16 nIntVal = 0; + if ( aValue >>= nIntVal ) + { + //#i80528# adapt to new range eventually + if(sal_Int16(VOBJ_MODE_HIDE) < nIntVal) nIntVal = sal_Int16(VOBJ_MODE_SHOW); + + aNewOpt.SetObjMode( VOBJ_TYPE_CHART, static_cast<ScVObjMode>(nIntVal)); + } + } + else if ( aPropertyName == SC_UNO_SHOWDRAW ) + { + sal_Int16 nIntVal = 0; + if ( aValue >>= nIntVal ) + { + //#i80528# adapt to new range eventually + if(sal_Int16(VOBJ_MODE_HIDE) < nIntVal) nIntVal = sal_Int16(VOBJ_MODE_SHOW); + + aNewOpt.SetObjMode( VOBJ_TYPE_DRAW, static_cast<ScVObjMode>(nIntVal)); + } + } + else if ( aPropertyName == SC_UNO_GRIDCOLOR ) + { + Color nIntVal; + if ( aValue >>= nIntVal ) + aNewOpt.SetGridColor( nIntVal, OUString() ); + } + else if ( aPropertyName == SC_UNO_ZOOMTYPE ) + { + sal_Int16 nIntVal = 0; + if ( aValue >>= nIntVal ) + SetZoomType(nIntVal); + } + else if ( aPropertyName == SC_UNO_ZOOMVALUE ) + { + sal_Int16 nIntVal = 0; + if ( aValue >>= nIntVal ) + SetZoom(nIntVal); + } + else if ( aPropertyName == SC_UNO_FORMULABARHEIGHT ) + { + sal_Int16 nIntVal = ScUnoHelpFunctions::GetInt16FromAny(aValue); + if (nIntVal > 0) + { + rViewData.SetFormulaBarLines(nIntVal); + // Notify formula bar about changed lines + ScInputHandler* pInputHdl = SC_MOD()->GetInputHdl(); + if (pInputHdl) + { + ScInputWindow* pInputWin = pInputHdl->GetInputWindow(); + if (pInputWin) + pInputWin->NumLinesChanged(); + } + } + } + + // Options are set on the view and document (for new views), + // so that they remain during saving. + //! In the app (module) we need an extra options to tune that + //! (for new documents) + + if ( aNewOpt == rOldOpt ) + return; + + rViewData.SetOptions( aNewOpt ); + rViewData.GetDocument().SetViewOptions( aNewOpt ); + rViewData.GetDocShell()->SetDocumentModified(); //! really? + + pViewSh->UpdateFixPos(); + pViewSh->PaintGrid(); + pViewSh->PaintTop(); + pViewSh->PaintLeft(); + pViewSh->PaintExtras(); + pViewSh->InvalidateBorder(); + + SfxBindings& rBindings = pViewSh->GetViewFrame().GetBindings(); + rBindings.Invalidate( FID_TOGGLEHEADERS ); // -> check in menu + rBindings.Invalidate( FID_TOGGLESYNTAX ); +} + +uno::Any SAL_CALL ScTabViewObj::getPropertyValue( const OUString& aPropertyName ) +{ + SolarMutexGuard aGuard; + uno::Any aRet; + + if ( aPropertyName == SC_UNO_FILTERED_RANGE_SELECTION ) + { + aRet <<= bFilteredRangeSelection; + return aRet; + } + + ScTabViewShell* pViewSh = GetViewShell(); + if (pViewSh) + { + ScViewData& rViewData = pViewSh->GetViewData(); + const ScViewOptions& rOpt = rViewData.GetOptions(); + + if ( aPropertyName == SC_UNO_COLROWHDR || aPropertyName == OLD_UNO_COLROWHDR ) + aRet <<= rOpt.GetOption( VOPT_HEADER ); + else if ( aPropertyName == SC_UNO_HORSCROLL || aPropertyName == OLD_UNO_HORSCROLL ) + aRet <<= rOpt.GetOption( VOPT_HSCROLL ); + else if ( aPropertyName == SC_UNO_OUTLSYMB || aPropertyName == OLD_UNO_OUTLSYMB ) + aRet <<= rOpt.GetOption( VOPT_OUTLINER ); + else if ( aPropertyName == SC_UNO_SHEETTABS || aPropertyName == OLD_UNO_SHEETTABS ) + aRet <<= rOpt.GetOption( VOPT_TABCONTROLS ); + else if ( aPropertyName == SC_UNO_SHOWANCHOR ) aRet <<= rOpt.GetOption( VOPT_ANCHOR ); + else if ( aPropertyName == SC_UNO_SHOWFORM ) aRet <<= rOpt.GetOption( VOPT_FORMULAS ); + else if ( aPropertyName == SC_UNO_SHOWGRID ) aRet <<= rOpt.GetOption( VOPT_GRID ); + else if ( aPropertyName == SC_UNO_SHOWHELP ) aRet <<= rOpt.GetOption( VOPT_HELPLINES ); + else if ( aPropertyName == SC_UNO_SHOWNOTES ) aRet <<= rOpt.GetOption( VOPT_NOTES ); + else if ( aPropertyName == SC_UNO_SHOWFORMULASMARKS ) aRet <<= rOpt.GetOption( VOPT_FORMULAS_MARKS ); + else if ( aPropertyName == SC_UNO_SHOWPAGEBR ) aRet <<= rOpt.GetOption( VOPT_PAGEBREAKS ); + else if ( aPropertyName == SC_UNO_SHOWZERO ) aRet <<= rOpt.GetOption( VOPT_NULLVALS ); + else if ( aPropertyName == SC_UNO_VALUEHIGH || aPropertyName == OLD_UNO_VALUEHIGH ) + aRet <<= rOpt.GetOption( VOPT_SYNTAX ); + else if ( aPropertyName == SC_UNO_VERTSCROLL || aPropertyName == OLD_UNO_VERTSCROLL ) + aRet <<= rOpt.GetOption( VOPT_VSCROLL ); + else if ( aPropertyName == SC_UNO_SHOWOBJ ) aRet <<= static_cast<sal_Int16>( rOpt.GetObjMode( VOBJ_TYPE_OLE ) ); + else if ( aPropertyName == SC_UNO_SHOWCHARTS ) aRet <<= static_cast<sal_Int16>( rOpt.GetObjMode( VOBJ_TYPE_CHART ) ); + else if ( aPropertyName == SC_UNO_SHOWDRAW ) aRet <<= static_cast<sal_Int16>( rOpt.GetObjMode( VOBJ_TYPE_DRAW ) ); + else if ( aPropertyName == SC_UNO_GRIDCOLOR ) aRet <<= rOpt.GetGridColor(); + else if ( aPropertyName == SC_UNO_VISAREA ) aRet <<= GetVisArea(); + else if ( aPropertyName == SC_UNO_ZOOMTYPE ) aRet <<= GetZoomType(); + else if ( aPropertyName == SC_UNO_ZOOMVALUE ) aRet <<= GetZoom(); + else if ( aPropertyName == SC_UNO_FORMULABARHEIGHT ) aRet <<= rViewData.GetFormulaBarLines(); + else if ( aPropertyName == SC_UNO_VISAREASCREEN ) + { + vcl::Window* pActiveWin = rViewData.GetActiveWin(); + if ( pActiveWin ) + { + AbsoluteScreenPixelRectangle aRect = pActiveWin->GetWindowExtentsAbsolute(); + aRet <<= AWTRectangle( aRect ); + } + } + } + + return aRet; +} + +void SAL_CALL ScTabViewObj::addPropertyChangeListener( const OUString& /* aPropertyName */, + const uno::Reference<beans::XPropertyChangeListener >& xListener ) +{ + SolarMutexGuard aGuard; + aPropertyChgListeners.push_back( xListener ); +} + +void SAL_CALL ScTabViewObj::removePropertyChangeListener( const OUString& /* aPropertyName */, + const uno::Reference<beans::XPropertyChangeListener >& xListener ) +{ + SolarMutexGuard aGuard; + auto it = std::find(aPropertyChgListeners.begin(), aPropertyChgListeners.end(), xListener); //! Why the nonsense with queryInterface? + if (it != aPropertyChgListeners.end()) + aPropertyChgListeners.erase(it); +} + +void SAL_CALL ScTabViewObj::addVetoableChangeListener( const OUString& /* PropertyName */, + const uno::Reference<beans::XVetoableChangeListener >& /* aListener */ ) +{ +} + +void SAL_CALL ScTabViewObj::removeVetoableChangeListener( const OUString& /* PropertyName */, + const uno::Reference<beans::XVetoableChangeListener >& /* aListener */ ) +{ +} + +void ScTabViewObj::VisAreaChanged() +{ + beans::PropertyChangeEvent aEvent; + aEvent.Source.set(getXWeak()); + for (const auto& rListener : aPropertyChgListeners) + rListener->propertyChange( aEvent ); +} + +// XRangeSelection + +void SAL_CALL ScTabViewObj::startRangeSelection( + const uno::Sequence<beans::PropertyValue>& aArguments ) +{ + SolarMutexGuard aGuard; + ScTabViewShell* pViewSh = GetViewShell(); + if (!pViewSh) + return; + + OUString aInitVal, aTitle; + bool bCloseOnButtonUp = false; + bool bSingleCell = false; + bool bMultiSelection = false; + + OUString aStrVal; + for (const beans::PropertyValue& rProp : aArguments) + { + OUString aPropName(rProp.Name); + + if (aPropName == SC_UNONAME_CLOSEONUP ) + bCloseOnButtonUp = ScUnoHelpFunctions::GetBoolFromAny( rProp.Value ); + else if (aPropName == SC_UNONAME_TITLE ) + { + if ( rProp.Value >>= aStrVal ) + aTitle = aStrVal; + } + else if (aPropName == SC_UNONAME_INITVAL ) + { + if ( rProp.Value >>= aStrVal ) + aInitVal = aStrVal; + } + else if (aPropName == SC_UNONAME_SINGLECELL ) + bSingleCell = ScUnoHelpFunctions::GetBoolFromAny( rProp.Value ); + else if (aPropName == SC_UNONAME_MULTISEL ) + bMultiSelection = ScUnoHelpFunctions::GetBoolFromAny( rProp.Value ); + } + + pViewSh->StartSimpleRefDialog( aTitle, aInitVal, bCloseOnButtonUp, bSingleCell, bMultiSelection ); +} + +void SAL_CALL ScTabViewObj::abortRangeSelection() +{ + SolarMutexGuard aGuard; + ScTabViewShell* pViewSh = GetViewShell(); + if (pViewSh) + pViewSh->StopSimpleRefDialog(); +} + +void SAL_CALL ScTabViewObj::addRangeSelectionListener( + const uno::Reference<sheet::XRangeSelectionListener>& xListener ) +{ + SolarMutexGuard aGuard; + aRangeSelListeners.push_back( xListener ); +} + +void SAL_CALL ScTabViewObj::removeRangeSelectionListener( + const uno::Reference<sheet::XRangeSelectionListener>& xListener ) +{ + SolarMutexGuard aGuard; + auto it = std::find(aRangeSelListeners.begin(), aRangeSelListeners.end(), xListener); + if (it != aRangeSelListeners.end()) + aRangeSelListeners.erase(it); +} + +void SAL_CALL ScTabViewObj::addRangeSelectionChangeListener( + const uno::Reference<sheet::XRangeSelectionChangeListener>& xListener ) +{ + SolarMutexGuard aGuard; + aRangeChgListeners.push_back( xListener ); +} + +void SAL_CALL ScTabViewObj::removeRangeSelectionChangeListener( + const uno::Reference<sheet::XRangeSelectionChangeListener>& xListener ) +{ + SolarMutexGuard aGuard; + auto it = std::find(aRangeChgListeners.begin(), aRangeChgListeners.end(), xListener); + if (it != aRangeChgListeners.end()) + aRangeChgListeners.erase(it); +} + +void ScTabViewObj::RangeSelDone( const OUString& rText ) +{ + sheet::RangeSelectionEvent aEvent; + aEvent.Source.set(getXWeak()); + aEvent.RangeDescriptor = rText; + + // copy on the stack because listener could remove itself + auto const listeners(aRangeSelListeners); + + for (const auto& rListener : listeners) + rListener->done( aEvent ); +} + +void ScTabViewObj::RangeSelAborted( const OUString& rText ) +{ + sheet::RangeSelectionEvent aEvent; + aEvent.Source.set(getXWeak()); + aEvent.RangeDescriptor = rText; + + // copy on the stack because listener could remove itself + auto const listeners(aRangeSelListeners); + + for (const auto& rListener : listeners) + rListener->aborted( aEvent ); +} + +void ScTabViewObj::RangeSelChanged( const OUString& rText ) +{ + sheet::RangeSelectionEvent aEvent; + aEvent.Source.set(getXWeak()); + aEvent.RangeDescriptor = rText; + + // copy on the stack because listener could remove itself + auto const listener(aRangeChgListeners); + + for (const auto& rListener : listener) + rListener->descriptorChanged( aEvent ); +} + +// XServiceInfo +OUString SAL_CALL ScTabViewObj::getImplementationName() +{ + return "ScTabViewObj"; +} + +sal_Bool SAL_CALL ScTabViewObj::supportsService( const OUString& rServiceName ) +{ + return cppu::supportsService(this, rServiceName); +} + +uno::Sequence<OUString> SAL_CALL ScTabViewObj::getSupportedServiceNames() +{ + return {SCTABVIEWOBJ_SERVICE, SCVIEWSETTINGS_SERVICE}; +} + +// XUnoTunnel + +css::uno::Reference< css::datatransfer::XTransferable > SAL_CALL ScTabViewObj::getTransferable() +{ + SolarMutexGuard aGuard; + ScEditShell* pShell = dynamic_cast<ScEditShell*>( GetViewShell()->GetViewFrame().GetDispatcher()->GetShell(0) ); + if (pShell) + return pShell->GetEditView()->GetTransferable(); + + ScDrawTextObjectBar* pTextShell = dynamic_cast<ScDrawTextObjectBar*>( GetViewShell()->GetViewFrame().GetDispatcher()->GetShell(0) ); + if (pTextShell) + { + ScViewData& rViewData = GetViewShell()->GetViewData(); + ScDrawView* pView = rViewData.GetScDrawView(); + OutlinerView* pOutView = pView->GetTextEditOutlinerView(); + if (pOutView) + return pOutView->GetEditView().GetTransferable(); + } + + ScDrawShell* pDrawShell = dynamic_cast<ScDrawShell*>( GetViewShell()->GetViewFrame().GetDispatcher()->GetShell(0) ); + if (pDrawShell) + return pDrawShell->GetDrawView()->CopyToTransferable(); + + return GetViewShell()->CopyToTransferable(); +} + +void SAL_CALL ScTabViewObj::insertTransferable( const css::uno::Reference< css::datatransfer::XTransferable >& xTrans ) +{ + SolarMutexGuard aGuard; + ScEditShell* pShell = dynamic_cast<ScEditShell*>( GetViewShell()->GetViewFrame().GetDispatcher()->GetShell(0) ); + if (pShell) + pShell->GetEditView()->InsertText( xTrans, OUString(), false ); + else + { + ScDrawTextObjectBar* pTextShell = dynamic_cast<ScDrawTextObjectBar*>( GetViewShell()->GetViewFrame().GetDispatcher()->GetShell(0) ); + if (pTextShell) + { + ScViewData& rViewData = GetViewShell()->GetViewData(); + ScDrawView* pView = rViewData.GetScDrawView(); + OutlinerView* pOutView = pView->GetTextEditOutlinerView(); + if ( pOutView ) + { + pOutView->GetEditView().InsertText( xTrans, OUString(), false ); + return; + } + } + + GetViewShell()->PasteFromTransferable( xTrans ); + } +} + +namespace { + +uno::Sequence<sal_Int32> toSequence(const ScMarkData::MarkedTabsType& rSelected) +{ + uno::Sequence<sal_Int32> aRet(rSelected.size()); + auto aRetRange = asNonConstRange(aRet); + size_t i = 0; + for (const auto& rTab : rSelected) + { + aRetRange[i] = static_cast<sal_Int32>(rTab); + ++i; + } + + return aRet; +} + +} + +uno::Sequence<sal_Int32> ScTabViewObj::getSelectedSheets() +{ + ScTabViewShell* pViewSh = GetViewShell(); + if (!pViewSh) + return uno::Sequence<sal_Int32>(); + + ScViewData& rViewData = pViewSh->GetViewData(); + + // #i95280# when printing from the shell, the view is never activated, + // so Excel view settings must also be evaluated here. + ScExtDocOptions* pExtOpt = rViewData.GetDocument().GetExtDocOptions(); + if (pExtOpt && pExtOpt->IsChanged()) + { + pViewSh->GetViewData().ReadExtOptions(*pExtOpt); // Excel view settings + pViewSh->SetTabNo(pViewSh->GetViewData().GetTabNo(), true); + pExtOpt->SetChanged(false); + } + + return toSequence(rViewData.GetMarkData().GetSelectedTabs()); +} + +ScPreviewObj::ScPreviewObj(ScPreviewShell* pViewSh) : + SfxBaseController(pViewSh), + mpViewShell(pViewSh) +{ + if (mpViewShell) + StartListening(*mpViewShell); +} + +ScPreviewObj::~ScPreviewObj() +{ + if (mpViewShell) + EndListening(*mpViewShell); +} + +uno::Any ScPreviewObj::queryInterface(const uno::Type& rType) +{ + uno::Any aReturn = ::cppu::queryInterface(rType, + static_cast<sheet::XSelectedSheetsSupplier*>(this)); + if ( aReturn.hasValue() ) + return aReturn; + return SfxBaseController::queryInterface(rType); +} + +void ScPreviewObj::acquire() noexcept +{ + SfxBaseController::acquire(); +} + +void ScPreviewObj::release() noexcept +{ + SfxBaseController::release(); +} + +void ScPreviewObj::Notify(SfxBroadcaster&, const SfxHint& rHint) +{ + if (rHint.GetId() == SfxHintId::Dying) + mpViewShell = nullptr; +} + +uno::Sequence<sal_Int32> ScPreviewObj::getSelectedSheets() +{ + ScPreview* p = mpViewShell ? mpViewShell->GetPreview() : nullptr; + if (!p) + return uno::Sequence<sal_Int32>(); + + return toSequence(p->GetSelectedTabs()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/unoobj/warnpassword.cxx b/sc/source/ui/unoobj/warnpassword.cxx new file mode 100644 index 0000000000..b8ccbffa2e --- /dev/null +++ b/sc/source/ui/unoobj/warnpassword.cxx @@ -0,0 +1,62 @@ +/* -*- 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 <warnpassword.hxx> +#include <com/sun/star/task/XInteractionHandler.hpp> +#include <sfx2/docfile.hxx> +#include <ucbhelper/simpleinteractionrequest.hxx> +#include <com/sun/star/task/InteractionClassification.hpp> +#include <com/sun/star/ucb/InteractiveAppException.hpp> +#include <svx/svxerr.hxx> +#include <rtl/ref.hxx> + +using ::com::sun::star::uno::Any; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::XInterface; +using ::com::sun::star::task::InteractionClassification_QUERY; +using ::com::sun::star::task::XInteractionHandler; +using ::com::sun::star::ucb::InteractiveAppException; + +bool ScWarnPassword::WarningOnPassword( SfxMedium& rMedium ) +{ + bool bReturn = true; + Reference< XInteractionHandler > xHandler( rMedium.GetInteractionHandler()); + if( xHandler.is() ) + { + Any aException( InteractiveAppException("", + Reference <XInterface> (), + InteractionClassification_QUERY, + sal_uInt32(ERRCODE_SVX_EXPORT_FILTER_CRYPT))); + + rtl::Reference< ucbhelper::SimpleInteractionRequest > xRequest + = new ucbhelper::SimpleInteractionRequest( + aException, + ContinuationFlags::Approve | ContinuationFlags::Disapprove ); + + xHandler->handle( xRequest ); + + const ContinuationFlags nResp = xRequest->getResponse(); + + if ( nResp == ContinuationFlags::Disapprove ) + bReturn = false; + } + return bReturn; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |